10 #include <QDomDocument>
11 #include <QTextStream>
13 #include <QStringList>
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"
101 QString *errorMessage)
103 QTextStream
error(stderr);
105 if ( n.isElement() ) {
106 QDomElement e = n.toElement();
109 if ( n.hasChildNodes() ) {
110 QDomNodeList subnodes = n.childNodes();
112 while (i < subnodes.count()) {
113 do_walkdoc(subnodes.item(i++), cb, cbdata, errorMessage);
124 QString *errorMessage)
126 QTextStream
error(stderr);
127 QDomNode n = doc->documentElement();
139 if (e.tagName().compare(d->
dtag) == 0) {
141 if (d->
did.isEmpty() ||
142 (eid.size() && !eid.compare(d->
did)) ) {
147 if (d->
splice.hasChildNodes()) {
148 QDomNodeList subnodes = d->
splice.childNodes();
150 while (i < subnodes.count()) {
151 e.appendChild(e.ownerDocument().importNode(subnodes.item(i++),
true));
155 e.appendChild(e.ownerDocument().importNode(d->
splice,
true));
167 QString *errorMessage)
170 Q_ASSERT(errorMessage);
173 QStringList spliceinfo = arguments.split(
"=");
174 if (spliceinfo.count() != 2) {
175 *errorMessage =
"Invalid argument for splice command: " + arguments;
178 if (spliceinfo[0].contains(
':')) {
180 QStringList destinfo = spliceinfo[0].split(
":");
181 cbdata.
dtag = destinfo[0];
182 cbdata.
did = destinfo[1];
185 cbdata.
dtag = spliceinfo[0];
188 QStringList srcinfo = spliceinfo[1].split(
":");
189 if (srcinfo.count() < 2) {
190 *errorMessage =
"Invalid source argument for splice command: " + arguments;
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());
199 QTextStream sfiletxt(&spliceFile);
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);
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]);
214 if (srcinfo.count() == 3) {
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();
225 cbdata.
splice = elist.item(0).toElement();
237 if (e.tagName().compare(d->
dtag) == 0) {
239 if (d->
did.isEmpty() ||
240 (eid.size() && !eid.compare(d->
did)) ) {
244 QDomNode parent = e.parentNode();
245 parent.removeChild(e);
248 if (d->
newtag.compare(e.tagName())) {
253 QDomNamedNodeMap attrs = e.attributes();
254 for (
int i = 0;
i < attrs.count();
i++) {
256 e.removeAttribute(attrs.item(
i).nodeName());
262 QDomNode prop = e.attributeNode(d->
newprop);
263 if (!prop.isNull()) {
278 QString *errorMessage)
281 Q_ASSERT(errorMessage);
284 QStringList replaceinfo = arguments.split(
"=");
285 if (replaceinfo.count() < 1) {
286 *errorMessage =
"Invalid argument for replace command: " + arguments;
289 if (replaceinfo[0].contains(
':')) {
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];
299 cbdata.
dtag = replaceinfo[0];
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;
307 if (srcinfo.count() >= 1) {
308 if (srcinfo[0].length()) cbdata.
newtag = srcinfo[0];
310 if (srcinfo.count() >= 2) {
311 if (srcinfo[1].length()) cbdata.
newprop = srcinfo[1];
313 if (srcinfo.count() >= 3) {
314 if (srcinfo[2].length()) cbdata.
newpropval = srcinfo[2];
327 if (e.tagName().compare(d->
dtag) == 0) {
329 if (d->
did.isEmpty() ||
330 (eid.size() && !eid.compare(d->
did)) ) {
332 QDomElement ne = e.ownerDocument().createElement(d->
newtag);
351 QString *errorMessage)
354 Q_ASSERT(errorMessage);
357 QStringList addinfo = arguments.split(
"=");
358 if (addinfo.count() < 1) {
359 *errorMessage =
"Invalid argument for add command: " + arguments;
362 if (addinfo[0].contains(
':')) {
364 QStringList destinfo = addinfo[0].split(
":");
365 cbdata.
dtag = destinfo[0];
366 cbdata.
did = destinfo[1];
369 cbdata.
dtag = addinfo[0];
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;
377 if (srcinfo.count() >= 1) {
378 if (srcinfo[0].length()) cbdata.
newtag = srcinfo[0];
380 if (srcinfo.count() >= 2) {
381 if (srcinfo[1].length()) cbdata.
newprop = srcinfo[1];
383 if (srcinfo.count() >= 3) {
384 if (srcinfo[2].length()) cbdata.
newpropval = srcinfo[2];
395 QDomElement nrk = e.ownerDocument().createElement(
WIX_TAG_REGKEY);
396 QDomElement nrv = e.ownerDocument().createElement(
WIX_TAG_REGVAL);
404 nrk.appendChild(nrv);
420 nce.setAttribute(
WIX_ATTR_ID, QString(
"Remove").append(dirName));
444 for (
int i = 0;
i < cnl.count();
i++) {
445 QDomElement cre = cnl.item(
i).toElement();
453 for (
int i = 0;
i < ulinfo->
newcomps.count();
i++) {
467 if ( e.hasChildNodes() ) {
469 bool hasComponent =
false;
471 QDomNodeList subnodes = e.childNodes();
472 for (
int i = 0;
i < subnodes.count();
i++) {
474 if (subnodes.item(
i).isElement()) {
475 QDomElement ce = subnodes.item(
i).toElement();
482 ce.setAttribute(
WIX_ATTR_ID, QString(
"DCOMP").append(dirName));
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();
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") ) {
524 QString compId = QString(
"ULDirComp_").append(dirName);
543 QString *errorMessage)
546 Q_ASSERT(errorMessage);
549 QStringList ulinfo = argument.split(
":");
550 if (ulinfo.count() < 2) {
551 *errorMessage =
"Invalid argument for userlocal command: " + argument;
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;
573 error <<
" splice args: desttagname[:Id]=file:basetag[:Id]" << endl;
574 error <<
" Splice children of basetag in file under desttagname" << 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;
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;
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;
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;
599 QTextStream
error(stderr);
600 QString command, errorMessage;
601 char *infile = 0, *outfile = 0;
602 QTextCodec *codec = QTextCodec::codecForName(
"utf-8");
604 QStringList commandargs;
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) ) {
620 for (
int i = 2;
i < argc;
i++) {
621 QString arg(argv[
i]);
622 if (!arg.compare(
"-q", Qt::CaseInsensitive))
624 else if (!arg.compare(
"-i", Qt::CaseInsensitive) && ++i < argc)
626 else if (!arg.compare(
"-o", Qt::CaseInsensitive) && ++i < argc)
628 else if (infile && outfile) {
629 commandargs.append(arg);
632 if ( !infile || !outfile || !commandargs.count() ) {
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());
647 if (QFile::exists(outfile)) {
648 if (!QFile::remove(outfile)) {
649 error << QString(
"Unable to truncate outfile '%1'\n").arg(outfile);
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);
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")
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);
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);
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);
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());
705 QTextStream out(&docFile);
706 out << doc.toString(4);
DebugMessage error(const QString &fmt)