Vidalia 0.3.1
wixtool.cpp
Go to the documentation of this file.
1/*
2** $Id$
3**
4** Copyright (C) 2009 The Tor Project, Inc.
5** See LICENSE file for terms; may be used according
6** Vidalia or Tor license constraints. (dual license)
7*/
8
9#include <QFile>
10#include <QDomDocument>
11#include <QTextStream>
12#include <QTextCodec>
13#include <QStringList>
14#include <stdlib.h>
15
16#define WIX_ATTR_ID "Id"
17#define WIX_ATTR_DIRACTION "uninstall"
18#define WIX_ATTR_REGACTION "createAndRemoveOnUninstall"
19#define WIX_ATTR_VALUE "Value"
20#define WIX_ATTR_KEY "KeyPath"
21#define WIX_ATTR_GUID "Guid"
22#define WIX_ATTR_NAME "Name"
23#define WIX_ATTR_REG_TYPE "Type"
24#define WIX_ATTR_REG_NAME "Name"
25#define WIX_ATTR_REG_ROOT "Root"
26#define WIX_ATTR_REG_KEYPATH "Key"
27#define WIX_ATTR_REG_ACTION "Action"
28#define WIX_REG_KEY_TYPE "integer"
29#define WIX_TAG_FILE "File"
30#define WIX_TAG_DIR "Directory"
31#define WIX_TAG_FEATURE "Feature"
32#define WIX_TAG_COMPONENT "Component"
33#define WIX_TAG_COMPONENT_REF "ComponentRef"
34#define WIX_TAG_CREATEDIR "CreateFolder"
35#define WIX_TAG_REMOVEDIR "RemoveFolder"
36#define WIX_TAG_REGKEY "RegistryKey"
37#define WIX_TAG_REGVAL "RegistryValue"
38
39typedef void (*TraverseCallback)(void *cbdata, QDomElement e);
40
41/* Splice command takes an element or sub tree from one
42 * document and inserts it into another. This is useful for
43 * expanding placeholder elements with their desired content
44 * for example.
45 * If an element name is not unique the conventional WiX Id
46 * attribute can be used to identify the specific element.
47 */
48typedef struct s_SpliceData {
49 QString dtag;
50 QString did;
51 QDomElement splice;
53
54/* Replace operates on tags by name or Id like Splice but
55 * only makes modifications to individual elements. Replace
56 * can also remove elements. (replace with null)
57 */
58typedef struct s_ReplaceData {
59 QString dtag;
60 QString did;
61 QString dprop;
62 QString newtag;
63 QString newprop;
64 QString newpropval;
66
67/* Add operates on tags by name or Id as usual.
68 */
69typedef struct s_AddData {
70 QString dtag;
71 QString did;
72 QString newtag;
73 QString newprop;
74 QString newpropval;
76
77/* In order to support local per user installation some basic
78 * constrains must apply to every component included in a
79 * package. This includes using a key path for each component
80 * via registry keys and placing all application data under the
81 * local user profile folder.
82 * This utility will navigate the components and convert any
83 * keys to registry key paths and create folders in the deployment
84 * hierarchy as required.
85 */
86typedef struct s_UserLocalData {
87 QString keypath;
88 QString featureid;
89 QStringList newcomps;
91
92
93/* Note that we must walk the tree ourselves as locate by ID
94 * nor suitable select by classification is available in the
95 * Qt API.
96 */
97bool
98do_walkdoc(QDomNode n,
100 void * cbdata,
101 QString *errorMessage)
102{
103 QTextStream error(stderr);
104 if ( !n.isNull() ) {
105 if ( n.isElement() ) {
106 QDomElement e = n.toElement();
107 (*cb)(cbdata, e);
108 }
109 if ( n.hasChildNodes() ) {
110 QDomNodeList subnodes = n.childNodes();
111 int i = 0;
112 while (i < subnodes.count()) {
113 do_walkdoc(subnodes.item(i++), cb, cbdata, errorMessage);
114 }
115 }
116 }
117 return true;
118}
119
120bool
121walkdoc(QDomDocument *doc,
123 void * cbdata,
124 QString *errorMessage)
125{
126 QTextStream error(stderr);
127 QDomNode n = doc->documentElement();
128 do_walkdoc(n, cb, cbdata, errorMessage);
129 return true;
130}
131
132void
133splicefunc(void *cbdata,
134 QDomElement e)
135{
136 SpliceData *d = reinterpret_cast<SpliceData *>(cbdata);
137 QString eid = e.attribute(WIX_ATTR_ID);
138
139 if (e.tagName().compare(d->dtag) == 0) {
140 /* if a specific Id is set, verify it too. */
141 if (d->did.isEmpty() ||
142 (eid.size() && !eid.compare(d->did)) ) {
143
144 /* expected behavior is to graft children of the splice under target.
145 * if we're only given a single element graft it instead.
146 */
147 if (d->splice.hasChildNodes()) {
148 QDomNodeList subnodes = d->splice.childNodes();
149 int i = 0;
150 while (i < subnodes.count()) {
151 e.appendChild(e.ownerDocument().importNode(subnodes.item(i++), true));
152 }
153 }
154 else {
155 e.appendChild(e.ownerDocument().importNode(d->splice, true));
156 }
157 }
158 }
159}
160
161/** Make modifications to requested documents.
162 * returns false on error and <b>errorMessage</b> will be set.
163 */
164bool
165docsplice(QDomDocument *doc,
166 QString arguments,
167 QString *errorMessage)
168{
169 Q_ASSERT(doc);
170 Q_ASSERT(errorMessage);
171 SpliceData cbdata;
172
173 QStringList spliceinfo = arguments.split("=");
174 if (spliceinfo.count() != 2) {
175 *errorMessage = "Invalid argument for splice command: " + arguments;
176 return false;
177 }
178 if (spliceinfo[0].contains(':')) {
179 /* Id syntax */
180 QStringList destinfo = spliceinfo[0].split(":");
181 cbdata.dtag = destinfo[0];
182 cbdata.did = destinfo[1];
183 }
184 else {
185 cbdata.dtag = spliceinfo[0];
186 }
187
188 QStringList srcinfo = spliceinfo[1].split(":");
189 if (srcinfo.count() < 2) {
190 *errorMessage = "Invalid source argument for splice command: " + arguments;
191 return false;
192 }
193 QFile spliceFile(srcinfo[0]);
194 if (!spliceFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
195 *errorMessage = QString("Unable to open '%1' for reading: %2\n")
196 .arg(srcinfo[0]).arg(spliceFile.errorString());
197 return false;
198 }
199 QTextStream sfiletxt(&spliceFile);
200 QDomDocument sdoc;
201 QString parseError;
202 int badline, badcol;
203 if (!sdoc.setContent (sfiletxt.readAll(), false, &parseError, &badline, &badcol)) {
204 *errorMessage = QString("Error parsing splice document '%1' at line %2 and column %3: %4")
205 .arg(srcinfo[0]).arg(badline).arg(badcol).arg(parseError);
206 return false;
207 }
208
209 QDomNodeList elist = sdoc.elementsByTagName(srcinfo[1]);
210 if (elist.count() == 0) {
211 *errorMessage = QString("Unable to locate splice element '%1' in document.\n").arg(srcinfo[1]);
212 return false;
213 }
214 if (srcinfo.count() == 3) {
215 /* Id syntax for source elem */
216 for (int i=0; i < elist.count(); i++) {
217 QString eid = elist.item(i).toElement().attribute(WIX_ATTR_ID);
218 if (eid.size() && !eid.compare(srcinfo[2])) {
219 cbdata.splice = elist.item(i).toElement();
220 }
221 }
222 }
223 else {
224 /* without an Id the tag name should be unique. */
225 cbdata.splice = elist.item(0).toElement();
226 }
227 return walkdoc(doc, &splicefunc, &cbdata, errorMessage);
228}
229
230void
231replacefunc(void *cbdata,
232 QDomElement e)
233{
234 ReplaceData *d = reinterpret_cast<ReplaceData *>(cbdata);
235 QString eid = e.attribute(WIX_ATTR_ID);
236
237 if (e.tagName().compare(d->dtag) == 0) {
238 /* if a specific Id is set, verify it too. */
239 if (d->did.isEmpty() ||
240 (eid.size() && !eid.compare(d->did)) ) {
241
242 /* no destination means remove node from tree */
243 if (d->newtag.isNull()) {
244 QDomNode parent = e.parentNode();
245 parent.removeChild(e);
246 }
247 else {
248 if (d->newtag.compare(e.tagName())) {
249 e.setTagName (d->newtag);
250 }
251 if (d->newprop.isNull()) {
252 /* clear all attributes (except Id if present) */
253 QDomNamedNodeMap attrs = e.attributes();
254 for (int i = 0; i < attrs.count(); i++) {
255 if (attrs.item(i).nodeName().compare(WIX_ATTR_ID)) {
256 e.removeAttribute(attrs.item(i).nodeName());
257 }
258 }
259 }
260 else {
261 /* only modify / clear a specific property */
262 QDomNode prop = e.attributeNode(d->newprop);
263 if (!prop.isNull()) {
264 e.setAttribute(d->newprop, d->newpropval);
265 }
266 }
267 }
268 }
269 }
270}
271
272/** Make modifications to requested documents.
273 * returns false on error and <b>errorMessage</b> will be set.
274 */
275bool
276docreplace(QDomDocument *doc,
277 QString arguments,
278 QString *errorMessage)
279{
280 Q_ASSERT(doc);
281 Q_ASSERT(errorMessage);
282 ReplaceData cbdata;
283
284 QStringList replaceinfo = arguments.split("=");
285 if (replaceinfo.count() < 1) {
286 *errorMessage = "Invalid argument for replace command: " + arguments;
287 return false;
288 }
289 if (replaceinfo[0].contains(':')) {
290 /* Id syntax */
291 QStringList destinfo = replaceinfo[0].split(":");
292 cbdata.dtag = destinfo[0];
293 cbdata.did = destinfo[1];
294 if (destinfo.count() >= 3) {
295 cbdata.dprop = destinfo[2];
296 }
297 }
298 else {
299 cbdata.dtag = replaceinfo[0];
300 }
301 if (replaceinfo.count() > 1) {
302 QStringList srcinfo = replaceinfo[1].split(":");
303 if (srcinfo.count() < 1) {
304 *errorMessage = "Invalid target argument for replace command: " + arguments;
305 return false;
306 }
307 if (srcinfo.count() >= 1) {
308 if (srcinfo[0].length()) cbdata.newtag = srcinfo[0];
309 }
310 if (srcinfo.count() >= 2) {
311 if (srcinfo[1].length()) cbdata.newprop = srcinfo[1];
312 }
313 if (srcinfo.count() >= 3) {
314 if (srcinfo[2].length()) cbdata.newpropval = srcinfo[2];
315 }
316 }
317 return walkdoc(doc, &replacefunc, &cbdata, errorMessage);
318}
319
320void
321addfunc(void *cbdata,
322 QDomElement e)
323{
324 AddData *d = reinterpret_cast<AddData *>(cbdata);
325 QString eid = e.attribute(WIX_ATTR_ID);
326
327 if (e.tagName().compare(d->dtag) == 0) {
328 /* if a specific Id is set, verify it too. */
329 if (d->did.isEmpty() ||
330 (eid.size() && !eid.compare(d->did)) ) {
331 if (d->newtag.compare(d->dtag)) {
332 QDomElement ne = e.ownerDocument().createElement(d->newtag);
333 if (!d->newprop.isNull()) {
334 ne.setAttribute(d->newprop, d->newpropval);
335 }
336 e.appendChild(ne);
337 }
338 else {
339 e.setAttribute(d->newprop, d->newpropval);
340 }
341 }
342 }
343}
344
345/** Make modifications to requested documents.
346 * returns false on error and <b>errorMessage</b> will be set.
347 */
348bool
349docadd(QDomDocument *doc,
350 QString arguments,
351 QString *errorMessage)
352{
353 Q_ASSERT(doc);
354 Q_ASSERT(errorMessage);
355 AddData cbdata;
356
357 QStringList addinfo = arguments.split("=");
358 if (addinfo.count() < 1) {
359 *errorMessage = "Invalid argument for add command: " + arguments;
360 return false;
361 }
362 if (addinfo[0].contains(':')) {
363 /* Id syntax */
364 QStringList destinfo = addinfo[0].split(":");
365 cbdata.dtag = destinfo[0];
366 cbdata.did = destinfo[1];
367 }
368 else {
369 cbdata.dtag = addinfo[0];
370 }
371 if (addinfo.count() > 1) {
372 QStringList srcinfo = addinfo[1].split(":");
373 if (srcinfo.count() < 1) {
374 *errorMessage = "Invalid target argument for add command: " + arguments;
375 return false;
376 }
377 if (srcinfo.count() >= 1) {
378 if (srcinfo[0].length()) cbdata.newtag = srcinfo[0];
379 }
380 if (srcinfo.count() >= 2) {
381 if (srcinfo[1].length()) cbdata.newprop = srcinfo[1];
382 }
383 if (srcinfo.count() >= 3) {
384 if (srcinfo[2].length()) cbdata.newpropval = srcinfo[2];
385 }
386 }
387 return walkdoc(doc, &addfunc, &cbdata, errorMessage);
388}
389
390void
392 QString dirName,
393 QString keyPath)
394{
395 QDomElement nrk = e.ownerDocument().createElement(WIX_TAG_REGKEY);
396 QDomElement nrv = e.ownerDocument().createElement(WIX_TAG_REGVAL);
397 nrk.setAttribute(WIX_ATTR_REG_ROOT, "HKCU");
398 nrk.setAttribute(WIX_ATTR_REG_ACTION, "createAndRemoveOnUninstall");
399 nrk.setAttribute(WIX_ATTR_REG_KEYPATH, keyPath);
400 nrv.setAttribute(WIX_ATTR_REG_TYPE, WIX_REG_KEY_TYPE);
401 nrv.setAttribute(WIX_ATTR_REG_NAME, dirName);
402 nrv.setAttribute(WIX_ATTR_VALUE, "1");
403 nrv.setAttribute(WIX_ATTR_KEY, "yes");
404 nrk.appendChild(nrv);
405 e.appendChild(nrk);
406}
407
408void
410 QString dirName)
411{
412 QDomElement nce;
413 /* An empty dir might produce a createdir, so only add if not present. */
414 if (e.elementsByTagName(WIX_TAG_CREATEDIR).count() == 0) {
415 nce = e.ownerDocument().createElement(WIX_TAG_CREATEDIR);
416 e.appendChild(nce);
417 }
418 nce = e.ownerDocument().createElement(WIX_TAG_REMOVEDIR);
419 nce.setAttribute("On", WIX_ATTR_DIRACTION);
420 nce.setAttribute(WIX_ATTR_ID, QString("Remove").append(dirName));
421 e.appendChild(nce);
422}
423
424void
425userlocalfunc(void *cbdata,
426 QDomElement e)
427{
428 UserLocalData *ulinfo = reinterpret_cast<UserLocalData *>(cbdata);
429 QString eid = e.attribute(WIX_ATTR_ID);
430
431 if (e.tagName().compare(WIX_TAG_FILE) == 0) {
432 e.removeAttribute(WIX_ATTR_KEY);
433 }
434 else if (e.tagName().compare(WIX_TAG_COMPONENT) == 0) {
435 /* If the WiX tools get confused we need to remove KeyPath attrs
436 * on any component elements after creation or merging.
437 * Empty directories with a CreateFolder and nothing else will do this.
438 */
439 e.removeAttribute(WIX_ATTR_KEY);
440 }
441 else if (e.tagName().compare(WIX_TAG_FEATURE) == 0) {
442 /* be sure to remove any default feature names; changed added above. */
443 QDomNodeList cnl = e.elementsByTagName(WIX_TAG_COMPONENT_REF);
444 for (int i = 0; i < cnl.count(); i++) {
445 QDomElement cre = cnl.item(i).toElement();
446 if (cre.attribute(WIX_ATTR_ID).compare(WIX_TAG_COMPONENT) == 0) {
447 e.removeChild(cre);
448 }
449 }
450 if (ulinfo->featureid.compare(e.attribute(WIX_ATTR_ID)) == 0) {
451 /* this is the target feature element for the new components, if any. */
452 QDomElement ne;
453 for (int i = 0; i < ulinfo->newcomps.count(); i++) {
454 QString currid = ulinfo->newcomps[i];
455 ne = e.ownerDocument().createElement(WIX_TAG_COMPONENT_REF);
456 ne.setAttribute(WIX_ATTR_ID, currid);
457 e.appendChild(ne);
458 }
459 }
460 }
461 else if (e.tagName().compare(WIX_TAG_DIR) == 0) {
462 QString dirName = e.attribute(WIX_ATTR_NAME);
463 QString dirId = e.attribute(WIX_ATTR_ID);
464 /* find all child components for this dir and see if it contains:
465 * create/remove folder elements, a registry element
466 */
467 if ( e.hasChildNodes() ) {
468 QDomElement fc;
469 bool hasComponent = false;
470 bool hasRegKey;
471 QDomNodeList subnodes = e.childNodes();
472 for (int i = 0; i < subnodes.count(); i++) {
473 hasRegKey = false;
474 if (subnodes.item(i).isElement()) {
475 QDomElement ce = subnodes.item(i).toElement();
476 if (ce.tagName().compare(WIX_TAG_COMPONENT) == 0) {
477 if (!hasComponent) {
478 hasComponent = true;
479 fc = ce;
480 if (ce.attribute(WIX_ATTR_ID).compare(WIX_TAG_COMPONENT) == 0) {
481 /* Fix default named components before adding registry elements. */
482 ce.setAttribute(WIX_ATTR_ID, QString("DCOMP").append(dirName));
483 ulinfo->newcomps.append(ce.attribute(WIX_ATTR_ID));
484 }
485 if (ce.elementsByTagName(WIX_TAG_REMOVEDIR).count() == 0) {
486 createDirMgmtComponent(ce, ce.attribute(WIX_ATTR_ID));
487 }
488 }
489 QDomNodeList compnodes = ce.childNodes();
490 for (int j = 0; j < compnodes.count(); j++) {
491 if (compnodes.item(j).isElement()) {
492 QDomElement compe = compnodes.item(j).toElement();
493 if (compe.tagName().compare(WIX_TAG_REGKEY) == 0) {
494 hasRegKey = true;
495 }
496 }
497 }
498 if (!hasRegKey) {
499 createRegLocalComponent(ce, QString("RK").append(ce.attribute(WIX_ATTR_ID)), ulinfo->keypath);
500 }
501 }
502 }
503 }
504 if (!hasComponent) {
505 /* Certain system directories must be ignored; we don't manage them. */
506 if (dirId.compare("LocalAppDataFolder") &&
507 dirId.compare("AppDataFolder") &&
508 dirId.compare("CommonAppDataFolder") &&
509 dirId.compare("CommonFilesFolder") &&
510 dirId.compare("DesktopFolder") &&
511 dirId.compare("PersonalFolder") &&
512 dirId.compare("ProgramFilesFolder") &&
513 dirId.compare("ProgramMenuFolder") &&
514 dirId.compare("StartMenuFolder") &&
515 dirId.compare("StartupFolder") &&
516 dirId.compare("SystemFolder") &&
517 dirId.compare("TempFolder") &&
518 dirId.compare("WindowsFolder") ) {
519 /* if there is no component under this dir parent then we
520 * must create a component for the sole purpose of dir
521 * creation with the requisite registry key path.
522 */
523 QDomElement ne = e.ownerDocument().createElement(WIX_TAG_COMPONENT);
524 QString compId = QString("ULDirComp_").append(dirName);
525 ne.setAttribute(WIX_ATTR_GUID, "*");
526 ne.setAttribute(WIX_ATTR_ID, compId);
527 e.appendChild(ne);
528 createDirMgmtComponent(ne, dirName);
529 createRegLocalComponent(ne, QString("DRK").append(dirName), ulinfo->keypath);
530 ulinfo->newcomps.append(compId);
531 }
532 }
533 }
534 }
535}
536
537/** Make modifications to requested documents.
538 * returns false on error and <b>errorMessage</b> will be set.
539 */
540bool
541docuserlocal(QDomDocument *doc,
542 QString argument,
543 QString *errorMessage)
544{
545 Q_ASSERT(doc);
546 Q_ASSERT(errorMessage);
547 UserLocalData cbdata;
548
549 QStringList ulinfo = argument.split(":");
550 if (ulinfo.count() < 2) {
551 *errorMessage = "Invalid argument for userlocal command: " + argument;
552 return false;
553 }
554 cbdata.keypath = ulinfo[0];
555 cbdata.featureid = ulinfo[1];
556 return walkdoc(doc, &userlocalfunc, &cbdata, errorMessage);
557}
558
559/** Display application usage and exit. */
560void
562{
563 QTextStream error(stderr);
564 error << "usage: wixtool <command> [-q] -i <infile> -o <outfile> <Arg0> [... <ArgN>]" << endl;
565 error << " command one of: " << endl;
566 error << " splice Splice children from one document into another." << endl;
567 error << " replace Replace elements or attributes in a document." << endl;
568 error << " add Add elements or attributes into a document." << endl;
569 error << " userlocal Convert File elements into per-user local elements." << endl;
570 error << " -i <infile> Input or template file" << endl;
571 error << " -o <outfile> Output file" << endl;
572 error << endl;
573 error << " splice args: desttagname[:Id]=file:basetag[:Id]" << endl;
574 error << " Splice children of basetag in file under desttagname" << endl;
575 error << endl;
576 error << " replace args: tagname[:Id]:property=newtagname[:Id]:property:value" << endl;
577 error << " If newtagname is empty the element is deleted" << endl;
578 error << " If newproperty is empty the property is deleted" << endl;
579 error << endl;
580 error << " add args: desttagname[:Id]=newtagname[:Id]:property:value" << endl;
581 error << " Add properties or child elements to target" << endl;
582 error << " If newtagname is empty only properties added to dest" << endl;
583 error << endl;
584 error << " userlocal arg: <registry key path>:<dest feature id>" << endl;
585 error << " Convert KeyPath File elements into the per user local idiom" << endl;
586 error << " with corresponding Create/RemoveDir and RegistryKey elements." << endl;
587 error << endl;
588 error << " NOTE: text content within an element is not accessible." << endl;
589 error << " Use the Value= attribute syntax if necessary." << endl;
590 error << " The optional :Id syntax restricts matching to elements with" << endl;
591 error << " the Id attribute set to the value indicated." << endl;
592 error.flush();
593 exit(1);
594}
595
596int
597main(int argc, char *argv[])
598{
599 QTextStream error(stderr);
600 QString command, errorMessage;
601 char *infile = 0, *outfile = 0;
602 QTextCodec *codec = QTextCodec::codecForName("utf-8");
603 bool quiet = false;
604 QStringList commandargs;
605
606 /* Check for the correct number of input parameters. */
607 if (argc < 6)
609
610 /* Verify command is supported. */
611 command = argv[1];
612 if ( command.compare("splice", Qt::CaseInsensitive) &&
613 command.compare("replace", Qt::CaseInsensitive) &&
614 command.compare("add", Qt::CaseInsensitive) &&
615 command.compare("userlocal", Qt::CaseInsensitive) ) {
617 }
618
619 /* Gather remaining arguments. */
620 for (int i = 2; i < argc; i++) {
621 QString arg(argv[i]);
622 if (!arg.compare("-q", Qt::CaseInsensitive))
623 quiet = true;
624 else if (!arg.compare("-i", Qt::CaseInsensitive) && ++i < argc)
625 infile = argv[i];
626 else if (!arg.compare("-o", Qt::CaseInsensitive) && ++i < argc)
627 outfile = argv[i];
628 else if (infile && outfile) {
629 commandargs.append(arg);
630 }
631 }
632 if ( !infile || !outfile || !commandargs.count() ) {
634 }
635
636 /* Open the source document for reading. */
637 QFile srcFile(infile);
638 QTextStream sfiletxt(&srcFile);
639 sfiletxt.setCodec(codec);
640 if (!srcFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
641 error << QString("Unable to open '%1' for reading: %2\n").arg(infile)
642 .arg(srcFile.errorString());
643 return 2;
644 }
645
646 /* Make sure the outfile does not exist before we use it. */
647 if (QFile::exists(outfile)) {
648 if (!QFile::remove(outfile)) {
649 error << QString("Unable to truncate outfile '%1'\n").arg(outfile);
650 return 2;
651 }
652 }
653
654 QDomDocument doc;
655 QString parseError;
656 int badline, badcol;
657 if (!doc.setContent (sfiletxt.readAll(), false, &parseError, &badline, &badcol)) {
658 error << QString("Error parsing source document '%1' at line %2 and column %3: %4")
659 .arg(infile).arg(badline).arg(badcol).arg(parseError);
660 return 3;
661 }
662
663 if (!command.compare("userlocal", Qt::CaseInsensitive)) {
664 if (!docuserlocal(&doc, commandargs[0], &errorMessage)) {
665 error << QString("Unable to convert document components to user local: %1\n")
666 .arg(errorMessage);
667 return 4;
668 }
669 }
670 else {
671 for (int i = 0; i < commandargs.count(); i++) {
672 if (!command.compare("splice", Qt::CaseInsensitive)) {
673 if (!docsplice(&doc, commandargs[i], &errorMessage)) {
674 error << QString("Unable to process splice command '%1': %2\n")
675 .arg(commandargs[i]).arg(errorMessage);
676 return 4;
677 }
678 }
679 else if (!command.compare("replace", Qt::CaseInsensitive)) {
680 if (!docreplace(&doc, commandargs[i], &errorMessage)) {
681 error << QString("Unable to process replace command '%1': %2\n")
682 .arg(commandargs[i]).arg(errorMessage);
683 return 4;
684 }
685 }
686 else if (!command.compare("add", Qt::CaseInsensitive)) {
687 if (!docadd(&doc, commandargs[i], &errorMessage)) {
688 error << QString("Unable to process add command '%1': %2\n")
689 .arg(commandargs[i]).arg(errorMessage);
690 return 4;
691 }
692 }
693 }
694 }
695
696 /* Open the output file for writing. */
697 QFile docFile(outfile);
698 if (!docFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
699 error << QString("Unable to open '%1' for writing: %2\n").arg(outfile)
700 .arg(docFile.errorString());
701 return 5;
702 }
703
704 /* Write the .wxl output. */
705 QTextStream out(&docFile);
706 out << doc.toString(4);
707
708 return 0;
709}
710
stop errmsg void
DebugMessage arg(const QString &a)
Definition: tcglobal.h:48
QString i(QString str)
Definition: html.cpp:32
DebugMessage error(const QString &fmt)
Definition: tcglobal.cpp:40
QString newprop
Definition: wixtool.cpp:73
QString newpropval
Definition: wixtool.cpp:74
QString newtag
Definition: wixtool.cpp:72
QString dtag
Definition: wixtool.cpp:70
QString did
Definition: wixtool.cpp:71
QString newtag
Definition: wixtool.cpp:62
QString dtag
Definition: wixtool.cpp:59
QString newpropval
Definition: wixtool.cpp:64
QString dprop
Definition: wixtool.cpp:61
QString did
Definition: wixtool.cpp:60
QString newprop
Definition: wixtool.cpp:63
QString did
Definition: wixtool.cpp:50
QString dtag
Definition: wixtool.cpp:49
QDomElement splice
Definition: wixtool.cpp:51
QString featureid
Definition: wixtool.cpp:88
QStringList newcomps
Definition: wixtool.cpp:89
QString keypath
Definition: wixtool.cpp:87
void print_usage_and_exit()
Definition: wixtool.cpp:561
struct s_ReplaceData ReplaceData
#define WIX_TAG_DIR
Definition: wixtool.cpp:30
bool do_walkdoc(QDomNode n, TraverseCallback cb, void *cbdata, QString *errorMessage)
Definition: wixtool.cpp:98
int main(int argc, char *argv[])
Definition: wixtool.cpp:597
#define WIX_TAG_FEATURE
Definition: wixtool.cpp:31
#define WIX_TAG_REMOVEDIR
Definition: wixtool.cpp:35
#define WIX_ATTR_VALUE
Definition: wixtool.cpp:19
void createDirMgmtComponent(QDomElement e, QString dirName)
Definition: wixtool.cpp:409
#define WIX_TAG_REGKEY
Definition: wixtool.cpp:36
void(* TraverseCallback)(void *cbdata, QDomElement e)
Definition: wixtool.cpp:39
#define WIX_ATTR_DIRACTION
Definition: wixtool.cpp:17
#define WIX_ATTR_NAME
Definition: wixtool.cpp:22
#define WIX_TAG_FILE
Definition: wixtool.cpp:29
#define WIX_TAG_COMPONENT
Definition: wixtool.cpp:32
#define WIX_ATTR_REG_NAME
Definition: wixtool.cpp:24
bool docreplace(QDomDocument *doc, QString arguments, QString *errorMessage)
Definition: wixtool.cpp:276
bool docuserlocal(QDomDocument *doc, QString argument, QString *errorMessage)
Definition: wixtool.cpp:541
void addfunc(void *cbdata, QDomElement e)
Definition: wixtool.cpp:321
#define WIX_ATTR_REG_ACTION
Definition: wixtool.cpp:27
#define WIX_REG_KEY_TYPE
Definition: wixtool.cpp:28
void userlocalfunc(void *cbdata, QDomElement e)
Definition: wixtool.cpp:425
#define WIX_TAG_CREATEDIR
Definition: wixtool.cpp:34
struct s_AddData AddData
#define WIX_ATTR_KEY
Definition: wixtool.cpp:20
#define WIX_ATTR_ID
Definition: wixtool.cpp:16
void createRegLocalComponent(QDomElement e, QString dirName, QString keyPath)
Definition: wixtool.cpp:391
void replacefunc(void *cbdata, QDomElement e)
Definition: wixtool.cpp:231
#define WIX_ATTR_REG_ROOT
Definition: wixtool.cpp:25
#define WIX_TAG_REGVAL
Definition: wixtool.cpp:37
bool walkdoc(QDomDocument *doc, TraverseCallback cb, void *cbdata, QString *errorMessage)
Definition: wixtool.cpp:121
#define WIX_TAG_COMPONENT_REF
Definition: wixtool.cpp:33
bool docadd(QDomDocument *doc, QString arguments, QString *errorMessage)
Definition: wixtool.cpp:349
struct s_UserLocalData UserLocalData
bool docsplice(QDomDocument *doc, QString arguments, QString *errorMessage)
Definition: wixtool.cpp:165
#define WIX_ATTR_GUID
Definition: wixtool.cpp:21
#define WIX_ATTR_REG_TYPE
Definition: wixtool.cpp:23
void splicefunc(void *cbdata, QDomElement e)
Definition: wixtool.cpp:133
#define WIX_ATTR_REG_KEYPATH
Definition: wixtool.cpp:26
struct s_SpliceData SpliceData