Vidalia 0.2.12
|
00001 /* 00002 ** $Id$ 00003 ** 00004 ** Copyright (C) 2009 The Tor Project, Inc. 00005 ** See LICENSE file for terms; may be used according 00006 ** Vidalia or Tor license constraints. (dual license) 00007 */ 00008 00009 #include <QFile> 00010 #include <QDomDocument> 00011 #include <QTextStream> 00012 #include <QTextCodec> 00013 #include <QStringList> 00014 #include <stdlib.h> 00015 00016 #define WIX_ATTR_ID "Id" 00017 #define WIX_ATTR_DIRACTION "uninstall" 00018 #define WIX_ATTR_REGACTION "createAndRemoveOnUninstall" 00019 #define WIX_ATTR_VALUE "Value" 00020 #define WIX_ATTR_KEY "KeyPath" 00021 #define WIX_ATTR_GUID "Guid" 00022 #define WIX_ATTR_NAME "Name" 00023 #define WIX_ATTR_REG_TYPE "Type" 00024 #define WIX_ATTR_REG_NAME "Name" 00025 #define WIX_ATTR_REG_ROOT "Root" 00026 #define WIX_ATTR_REG_KEYPATH "Key" 00027 #define WIX_ATTR_REG_ACTION "Action" 00028 #define WIX_REG_KEY_TYPE "integer" 00029 #define WIX_TAG_FILE "File" 00030 #define WIX_TAG_DIR "Directory" 00031 #define WIX_TAG_FEATURE "Feature" 00032 #define WIX_TAG_COMPONENT "Component" 00033 #define WIX_TAG_COMPONENT_REF "ComponentRef" 00034 #define WIX_TAG_CREATEDIR "CreateFolder" 00035 #define WIX_TAG_REMOVEDIR "RemoveFolder" 00036 #define WIX_TAG_REGKEY "RegistryKey" 00037 #define WIX_TAG_REGVAL "RegistryValue" 00038 00039 typedef void (*TraverseCallback)(void *cbdata, QDomElement e); 00040 00041 /* Splice command takes an element or sub tree from one 00042 * document and inserts it into another. This is useful for 00043 * expanding placeholder elements with their desired content 00044 * for example. 00045 * If an element name is not unique the conventional WiX Id 00046 * attribute can be used to identify the specific element. 00047 */ 00048 typedef struct s_SpliceData { 00049 QString dtag; 00050 QString did; 00051 QDomElement splice; 00052 } SpliceData; 00053 00054 /* Replace operates on tags by name or Id like Splice but 00055 * only makes modifications to individual elements. Replace 00056 * can also remove elements. (replace with null) 00057 */ 00058 typedef struct s_ReplaceData { 00059 QString dtag; 00060 QString did; 00061 QString dprop; 00062 QString newtag; 00063 QString newprop; 00064 QString newpropval; 00065 } ReplaceData; 00066 00067 /* Add operates on tags by name or Id as usual. 00068 */ 00069 typedef struct s_AddData { 00070 QString dtag; 00071 QString did; 00072 QString newtag; 00073 QString newprop; 00074 QString newpropval; 00075 } AddData; 00076 00077 /* In order to support local per user installation some basic 00078 * constrains must apply to every component included in a 00079 * package. This includes using a key path for each component 00080 * via registry keys and placing all application data under the 00081 * local user profile folder. 00082 * This utility will navigate the components and convert any 00083 * keys to registry key paths and create folders in the deployment 00084 * hierarchy as required. 00085 */ 00086 typedef struct s_UserLocalData { 00087 QString keypath; 00088 QString featureid; 00089 QStringList newcomps; 00090 } UserLocalData; 00091 00092 00093 /* Note that we must walk the tree ourselves as locate by ID 00094 * nor suitable select by classification is available in the 00095 * Qt API. 00096 */ 00097 bool 00098 do_walkdoc(QDomNode n, 00099 TraverseCallback cb, 00100 void * cbdata, 00101 QString *errorMessage) 00102 { 00103 QTextStream error(stderr); 00104 if ( !n.isNull() ) { 00105 if ( n.isElement() ) { 00106 QDomElement e = n.toElement(); 00107 (*cb)(cbdata, e); 00108 } 00109 if ( n.hasChildNodes() ) { 00110 QDomNodeList subnodes = n.childNodes(); 00111 int i = 0; 00112 while (i < subnodes.count()) { 00113 do_walkdoc(subnodes.item(i++), cb, cbdata, errorMessage); 00114 } 00115 } 00116 } 00117 return true; 00118 } 00119 00120 bool 00121 walkdoc(QDomDocument *doc, 00122 TraverseCallback cb, 00123 void * cbdata, 00124 QString *errorMessage) 00125 { 00126 QTextStream error(stderr); 00127 QDomNode n = doc->documentElement(); 00128 do_walkdoc(n, cb, cbdata, errorMessage); 00129 return true; 00130 } 00131 00132 void 00133 splicefunc(void *cbdata, 00134 QDomElement e) 00135 { 00136 SpliceData *d = reinterpret_cast<SpliceData *>(cbdata); 00137 QString eid = e.attribute(WIX_ATTR_ID); 00138 00139 if (e.tagName().compare(d->dtag) == 0) { 00140 /* if a specific Id is set, verify it too. */ 00141 if (d->did.isEmpty() || 00142 (eid.size() && !eid.compare(d->did)) ) { 00143 00144 /* expected behavior is to graft children of the splice under target. 00145 * if we're only given a single element graft it instead. 00146 */ 00147 if (d->splice.hasChildNodes()) { 00148 QDomNodeList subnodes = d->splice.childNodes(); 00149 int i = 0; 00150 while (i < subnodes.count()) { 00151 e.appendChild(e.ownerDocument().importNode(subnodes.item(i++), true)); 00152 } 00153 } 00154 else { 00155 e.appendChild(e.ownerDocument().importNode(d->splice, true)); 00156 } 00157 } 00158 } 00159 } 00160 00161 /** Make modifications to requested documents. 00162 * returns false on error and <b>errorMessage</b> will be set. 00163 */ 00164 bool 00165 docsplice(QDomDocument *doc, 00166 QString arguments, 00167 QString *errorMessage) 00168 { 00169 Q_ASSERT(doc); 00170 Q_ASSERT(errorMessage); 00171 SpliceData cbdata; 00172 00173 QStringList spliceinfo = arguments.split("="); 00174 if (spliceinfo.count() != 2) { 00175 *errorMessage = "Invalid argument for splice command: " + arguments; 00176 return false; 00177 } 00178 if (spliceinfo[0].contains(':')) { 00179 /* Id syntax */ 00180 QStringList destinfo = spliceinfo[0].split(":"); 00181 cbdata.dtag = destinfo[0]; 00182 cbdata.did = destinfo[1]; 00183 } 00184 else { 00185 cbdata.dtag = spliceinfo[0]; 00186 } 00187 00188 QStringList srcinfo = spliceinfo[1].split(":"); 00189 if (srcinfo.count() < 2) { 00190 *errorMessage = "Invalid source argument for splice command: " + arguments; 00191 return false; 00192 } 00193 QFile spliceFile(srcinfo[0]); 00194 if (!spliceFile.open(QIODevice::ReadOnly | QIODevice::Text)) { 00195 *errorMessage = QString("Unable to open '%1' for reading: %2\n") 00196 .arg(srcinfo[0]).arg(spliceFile.errorString()); 00197 return false; 00198 } 00199 QTextStream sfiletxt(&spliceFile); 00200 QDomDocument sdoc; 00201 QString parseError; 00202 int badline, badcol; 00203 if (!sdoc.setContent (sfiletxt.readAll(), false, &parseError, &badline, &badcol)) { 00204 *errorMessage = QString("Error parsing splice document '%1' at line %2 and column %3: %4") 00205 .arg(srcinfo[0]).arg(badline).arg(badcol).arg(parseError); 00206 return false; 00207 } 00208 00209 QDomNodeList elist = sdoc.elementsByTagName(srcinfo[1]); 00210 if (elist.count() == 0) { 00211 *errorMessage = QString("Unable to locate splice element '%1' in document.\n").arg(srcinfo[1]); 00212 return false; 00213 } 00214 if (srcinfo.count() == 3) { 00215 /* Id syntax for source elem */ 00216 for (int i=0; i < elist.count(); i++) { 00217 QString eid = elist.item(i).toElement().attribute(WIX_ATTR_ID); 00218 if (eid.size() && !eid.compare(srcinfo[2])) { 00219 cbdata.splice = elist.item(i).toElement(); 00220 } 00221 } 00222 } 00223 else { 00224 /* without an Id the tag name should be unique. */ 00225 cbdata.splice = elist.item(0).toElement(); 00226 } 00227 return walkdoc(doc, &splicefunc, &cbdata, errorMessage); 00228 } 00229 00230 void 00231 replacefunc(void *cbdata, 00232 QDomElement e) 00233 { 00234 ReplaceData *d = reinterpret_cast<ReplaceData *>(cbdata); 00235 QString eid = e.attribute(WIX_ATTR_ID); 00236 00237 if (e.tagName().compare(d->dtag) == 0) { 00238 /* if a specific Id is set, verify it too. */ 00239 if (d->did.isEmpty() || 00240 (eid.size() && !eid.compare(d->did)) ) { 00241 00242 /* no destination means remove node from tree */ 00243 if (d->newtag.isNull()) { 00244 QDomNode parent = e.parentNode(); 00245 parent.removeChild(e); 00246 } 00247 else { 00248 if (d->newtag.compare(e.tagName())) { 00249 e.setTagName (d->newtag); 00250 } 00251 if (d->newprop.isNull()) { 00252 /* clear all attributes (except Id if present) */ 00253 QDomNamedNodeMap attrs = e.attributes(); 00254 for (int i = 0; i < attrs.count(); i++) { 00255 if (attrs.item(i).nodeName().compare(WIX_ATTR_ID)) { 00256 e.removeAttribute(attrs.item(i).nodeName()); 00257 } 00258 } 00259 } 00260 else { 00261 /* only modify / clear a specific property */ 00262 QDomNode prop = e.attributeNode(d->newprop); 00263 if (!prop.isNull()) { 00264 e.setAttribute(d->newprop, d->newpropval); 00265 } 00266 } 00267 } 00268 } 00269 } 00270 } 00271 00272 /** Make modifications to requested documents. 00273 * returns false on error and <b>errorMessage</b> will be set. 00274 */ 00275 bool 00276 docreplace(QDomDocument *doc, 00277 QString arguments, 00278 QString *errorMessage) 00279 { 00280 Q_ASSERT(doc); 00281 Q_ASSERT(errorMessage); 00282 ReplaceData cbdata; 00283 00284 QStringList replaceinfo = arguments.split("="); 00285 if (replaceinfo.count() < 1) { 00286 *errorMessage = "Invalid argument for replace command: " + arguments; 00287 return false; 00288 } 00289 if (replaceinfo[0].contains(':')) { 00290 /* Id syntax */ 00291 QStringList destinfo = replaceinfo[0].split(":"); 00292 cbdata.dtag = destinfo[0]; 00293 cbdata.did = destinfo[1]; 00294 if (destinfo.count() >= 3) { 00295 cbdata.dprop = destinfo[2]; 00296 } 00297 } 00298 else { 00299 cbdata.dtag = replaceinfo[0]; 00300 } 00301 if (replaceinfo.count() > 1) { 00302 QStringList srcinfo = replaceinfo[1].split(":"); 00303 if (srcinfo.count() < 1) { 00304 *errorMessage = "Invalid target argument for replace command: " + arguments; 00305 return false; 00306 } 00307 if (srcinfo.count() >= 1) { 00308 if (srcinfo[0].length()) cbdata.newtag = srcinfo[0]; 00309 } 00310 if (srcinfo.count() >= 2) { 00311 if (srcinfo[1].length()) cbdata.newprop = srcinfo[1]; 00312 } 00313 if (srcinfo.count() >= 3) { 00314 if (srcinfo[2].length()) cbdata.newpropval = srcinfo[2]; 00315 } 00316 } 00317 return walkdoc(doc, &replacefunc, &cbdata, errorMessage); 00318 } 00319 00320 void 00321 addfunc(void *cbdata, 00322 QDomElement e) 00323 { 00324 AddData *d = reinterpret_cast<AddData *>(cbdata); 00325 QString eid = e.attribute(WIX_ATTR_ID); 00326 00327 if (e.tagName().compare(d->dtag) == 0) { 00328 /* if a specific Id is set, verify it too. */ 00329 if (d->did.isEmpty() || 00330 (eid.size() && !eid.compare(d->did)) ) { 00331 if (d->newtag.compare(d->dtag)) { 00332 QDomElement ne = e.ownerDocument().createElement(d->newtag); 00333 if (!d->newprop.isNull()) { 00334 ne.setAttribute(d->newprop, d->newpropval); 00335 } 00336 e.appendChild(ne); 00337 } 00338 else { 00339 e.setAttribute(d->newprop, d->newpropval); 00340 } 00341 } 00342 } 00343 } 00344 00345 /** Make modifications to requested documents. 00346 * returns false on error and <b>errorMessage</b> will be set. 00347 */ 00348 bool 00349 docadd(QDomDocument *doc, 00350 QString arguments, 00351 QString *errorMessage) 00352 { 00353 Q_ASSERT(doc); 00354 Q_ASSERT(errorMessage); 00355 AddData cbdata; 00356 00357 QStringList addinfo = arguments.split("="); 00358 if (addinfo.count() < 1) { 00359 *errorMessage = "Invalid argument for add command: " + arguments; 00360 return false; 00361 } 00362 if (addinfo[0].contains(':')) { 00363 /* Id syntax */ 00364 QStringList destinfo = addinfo[0].split(":"); 00365 cbdata.dtag = destinfo[0]; 00366 cbdata.did = destinfo[1]; 00367 } 00368 else { 00369 cbdata.dtag = addinfo[0]; 00370 } 00371 if (addinfo.count() > 1) { 00372 QStringList srcinfo = addinfo[1].split(":"); 00373 if (srcinfo.count() < 1) { 00374 *errorMessage = "Invalid target argument for add command: " + arguments; 00375 return false; 00376 } 00377 if (srcinfo.count() >= 1) { 00378 if (srcinfo[0].length()) cbdata.newtag = srcinfo[0]; 00379 } 00380 if (srcinfo.count() >= 2) { 00381 if (srcinfo[1].length()) cbdata.newprop = srcinfo[1]; 00382 } 00383 if (srcinfo.count() >= 3) { 00384 if (srcinfo[2].length()) cbdata.newpropval = srcinfo[2]; 00385 } 00386 } 00387 return walkdoc(doc, &addfunc, &cbdata, errorMessage); 00388 } 00389 00390 void 00391 createRegLocalComponent(QDomElement e, 00392 QString dirName, 00393 QString keyPath) 00394 { 00395 QDomElement nrk = e.ownerDocument().createElement(WIX_TAG_REGKEY); 00396 QDomElement nrv = e.ownerDocument().createElement(WIX_TAG_REGVAL); 00397 nrk.setAttribute(WIX_ATTR_REG_ROOT, "HKCU"); 00398 nrk.setAttribute(WIX_ATTR_REG_ACTION, "createAndRemoveOnUninstall"); 00399 nrk.setAttribute(WIX_ATTR_REG_KEYPATH, keyPath); 00400 nrv.setAttribute(WIX_ATTR_REG_TYPE, WIX_REG_KEY_TYPE); 00401 nrv.setAttribute(WIX_ATTR_REG_NAME, dirName); 00402 nrv.setAttribute(WIX_ATTR_VALUE, "1"); 00403 nrv.setAttribute(WIX_ATTR_KEY, "yes"); 00404 nrk.appendChild(nrv); 00405 e.appendChild(nrk); 00406 } 00407 00408 void 00409 createDirMgmtComponent(QDomElement e, 00410 QString dirName) 00411 { 00412 QDomElement nce; 00413 /* An empty dir might produce a createdir, so only add if not present. */ 00414 if (e.elementsByTagName(WIX_TAG_CREATEDIR).count() == 0) { 00415 nce = e.ownerDocument().createElement(WIX_TAG_CREATEDIR); 00416 e.appendChild(nce); 00417 } 00418 nce = e.ownerDocument().createElement(WIX_TAG_REMOVEDIR); 00419 nce.setAttribute("On", WIX_ATTR_DIRACTION); 00420 nce.setAttribute(WIX_ATTR_ID, QString("Remove").append(dirName)); 00421 e.appendChild(nce); 00422 } 00423 00424 void 00425 userlocalfunc(void *cbdata, 00426 QDomElement e) 00427 { 00428 UserLocalData *ulinfo = reinterpret_cast<UserLocalData *>(cbdata); 00429 QString eid = e.attribute(WIX_ATTR_ID); 00430 00431 if (e.tagName().compare(WIX_TAG_FILE) == 0) { 00432 e.removeAttribute(WIX_ATTR_KEY); 00433 } 00434 else if (e.tagName().compare(WIX_TAG_COMPONENT) == 0) { 00435 /* If the WiX tools get confused we need to remove KeyPath attrs 00436 * on any component elements after creation or merging. 00437 * Empty directories with a CreateFolder and nothing else will do this. 00438 */ 00439 e.removeAttribute(WIX_ATTR_KEY); 00440 } 00441 else if (e.tagName().compare(WIX_TAG_FEATURE) == 0) { 00442 /* be sure to remove any default feature names; changed added above. */ 00443 QDomNodeList cnl = e.elementsByTagName(WIX_TAG_COMPONENT_REF); 00444 for (int i = 0; i < cnl.count(); i++) { 00445 QDomElement cre = cnl.item(i).toElement(); 00446 if (cre.attribute(WIX_ATTR_ID).compare(WIX_TAG_COMPONENT) == 0) { 00447 e.removeChild(cre); 00448 } 00449 } 00450 if (ulinfo->featureid.compare(e.attribute(WIX_ATTR_ID)) == 0) { 00451 /* this is the target feature element for the new components, if any. */ 00452 QDomElement ne; 00453 for (int i = 0; i < ulinfo->newcomps.count(); i++) { 00454 QString currid = ulinfo->newcomps[i]; 00455 ne = e.ownerDocument().createElement(WIX_TAG_COMPONENT_REF); 00456 ne.setAttribute(WIX_ATTR_ID, currid); 00457 e.appendChild(ne); 00458 } 00459 } 00460 } 00461 else if (e.tagName().compare(WIX_TAG_DIR) == 0) { 00462 QString dirName = e.attribute(WIX_ATTR_NAME); 00463 QString dirId = e.attribute(WIX_ATTR_ID); 00464 /* find all child components for this dir and see if it contains: 00465 * create/remove folder elements, a registry element 00466 */ 00467 if ( e.hasChildNodes() ) { 00468 QDomElement fc; 00469 bool hasComponent = false; 00470 bool hasRegKey; 00471 QDomNodeList subnodes = e.childNodes(); 00472 for (int i = 0; i < subnodes.count(); i++) { 00473 hasRegKey = false; 00474 if (subnodes.item(i).isElement()) { 00475 QDomElement ce = subnodes.item(i).toElement(); 00476 if (ce.tagName().compare(WIX_TAG_COMPONENT) == 0) { 00477 if (!hasComponent) { 00478 hasComponent = true; 00479 fc = ce; 00480 if (ce.attribute(WIX_ATTR_ID).compare(WIX_TAG_COMPONENT) == 0) { 00481 /* Fix default named components before adding registry elements. */ 00482 ce.setAttribute(WIX_ATTR_ID, QString("DCOMP").append(dirName)); 00483 ulinfo->newcomps.append(ce.attribute(WIX_ATTR_ID)); 00484 } 00485 if (ce.elementsByTagName(WIX_TAG_REMOVEDIR).count() == 0) { 00486 createDirMgmtComponent(ce, ce.attribute(WIX_ATTR_ID)); 00487 } 00488 } 00489 QDomNodeList compnodes = ce.childNodes(); 00490 for (int j = 0; j < compnodes.count(); j++) { 00491 if (compnodes.item(j).isElement()) { 00492 QDomElement compe = compnodes.item(j).toElement(); 00493 if (compe.tagName().compare(WIX_TAG_REGKEY) == 0) { 00494 hasRegKey = true; 00495 } 00496 } 00497 } 00498 if (!hasRegKey) { 00499 createRegLocalComponent(ce, QString("RK").append(ce.attribute(WIX_ATTR_ID)), ulinfo->keypath); 00500 } 00501 } 00502 } 00503 } 00504 if (!hasComponent) { 00505 /* Certain system directories must be ignored; we don't manage them. */ 00506 if (dirId.compare("LocalAppDataFolder") && 00507 dirId.compare("AppDataFolder") && 00508 dirId.compare("CommonAppDataFolder") && 00509 dirId.compare("CommonFilesFolder") && 00510 dirId.compare("DesktopFolder") && 00511 dirId.compare("PersonalFolder") && 00512 dirId.compare("ProgramFilesFolder") && 00513 dirId.compare("ProgramMenuFolder") && 00514 dirId.compare("StartMenuFolder") && 00515 dirId.compare("StartupFolder") && 00516 dirId.compare("SystemFolder") && 00517 dirId.compare("TempFolder") && 00518 dirId.compare("WindowsFolder") ) { 00519 /* if there is no component under this dir parent then we 00520 * must create a component for the sole purpose of dir 00521 * creation with the requisite registry key path. 00522 */ 00523 QDomElement ne = e.ownerDocument().createElement(WIX_TAG_COMPONENT); 00524 QString compId = QString("ULDirComp_").append(dirName); 00525 ne.setAttribute(WIX_ATTR_GUID, "*"); 00526 ne.setAttribute(WIX_ATTR_ID, compId); 00527 e.appendChild(ne); 00528 createDirMgmtComponent(ne, dirName); 00529 createRegLocalComponent(ne, QString("DRK").append(dirName), ulinfo->keypath); 00530 ulinfo->newcomps.append(compId); 00531 } 00532 } 00533 } 00534 } 00535 } 00536 00537 /** Make modifications to requested documents. 00538 * returns false on error and <b>errorMessage</b> will be set. 00539 */ 00540 bool 00541 docuserlocal(QDomDocument *doc, 00542 QString argument, 00543 QString *errorMessage) 00544 { 00545 Q_ASSERT(doc); 00546 Q_ASSERT(errorMessage); 00547 UserLocalData cbdata; 00548 00549 QStringList ulinfo = argument.split(":"); 00550 if (ulinfo.count() < 2) { 00551 *errorMessage = "Invalid argument for userlocal command: " + argument; 00552 return false; 00553 } 00554 cbdata.keypath = ulinfo[0]; 00555 cbdata.featureid = ulinfo[1]; 00556 return walkdoc(doc, &userlocalfunc, &cbdata, errorMessage); 00557 } 00558 00559 /** Display application usage and exit. */ 00560 void 00561 print_usage_and_exit() 00562 { 00563 QTextStream error(stderr); 00564 error << "usage: wixtool <command> [-q] -i <infile> -o <outfile> <Arg0> [... <ArgN>]" << endl; 00565 error << " command one of: " << endl; 00566 error << " splice Splice children from one document into another." << endl; 00567 error << " replace Replace elements or attributes in a document." << endl; 00568 error << " add Add elements or attributes into a document." << endl; 00569 error << " userlocal Convert File elements into per-user local elements." << endl; 00570 error << " -i <infile> Input or template file" << endl; 00571 error << " -o <outfile> Output file" << endl; 00572 error << endl; 00573 error << " splice args: desttagname[:Id]=file:basetag[:Id]" << endl; 00574 error << " Splice children of basetag in file under desttagname" << endl; 00575 error << endl; 00576 error << " replace args: tagname[:Id]:property=newtagname[:Id]:property:value" << endl; 00577 error << " If newtagname is empty the element is deleted" << endl; 00578 error << " If newproperty is empty the property is deleted" << endl; 00579 error << endl; 00580 error << " add args: desttagname[:Id]=newtagname[:Id]:property:value" << endl; 00581 error << " Add properties or child elements to target" << endl; 00582 error << " If newtagname is empty only properties added to dest" << endl; 00583 error << endl; 00584 error << " userlocal arg: <registry key path>:<dest feature id>" << endl; 00585 error << " Convert KeyPath File elements into the per user local idiom" << endl; 00586 error << " with corresponding Create/RemoveDir and RegistryKey elements." << endl; 00587 error << endl; 00588 error << " NOTE: text content within an element is not accessible." << endl; 00589 error << " Use the Value= attribute syntax if necessary." << endl; 00590 error << " The optional :Id syntax restricts matching to elements with" << endl; 00591 error << " the Id attribute set to the value indicated." << endl; 00592 error.flush(); 00593 exit(1); 00594 } 00595 00596 int 00597 main(int argc, char *argv[]) 00598 { 00599 QTextStream error(stderr); 00600 QString command, errorMessage; 00601 char *infile = 0, *outfile = 0; 00602 QTextCodec *codec = QTextCodec::codecForName("utf-8"); 00603 bool quiet = false; 00604 QStringList commandargs; 00605 00606 /* Check for the correct number of input parameters. */ 00607 if (argc < 6) 00608 print_usage_and_exit(); 00609 00610 /* Verify command is supported. */ 00611 command = argv[1]; 00612 if ( command.compare("splice", Qt::CaseInsensitive) && 00613 command.compare("replace", Qt::CaseInsensitive) && 00614 command.compare("add", Qt::CaseInsensitive) && 00615 command.compare("userlocal", Qt::CaseInsensitive) ) { 00616 print_usage_and_exit(); 00617 } 00618 00619 /* Gather remaining arguments. */ 00620 for (int i = 2; i < argc; i++) { 00621 QString arg(argv[i]); 00622 if (!arg.compare("-q", Qt::CaseInsensitive)) 00623 quiet = true; 00624 else if (!arg.compare("-i", Qt::CaseInsensitive) && ++i < argc) 00625 infile = argv[i]; 00626 else if (!arg.compare("-o", Qt::CaseInsensitive) && ++i < argc) 00627 outfile = argv[i]; 00628 else if (infile && outfile) { 00629 commandargs.append(arg); 00630 } 00631 } 00632 if ( !infile || !outfile || !commandargs.count() ) { 00633 print_usage_and_exit(); 00634 } 00635 00636 /* Open the source document for reading. */ 00637 QFile srcFile(infile); 00638 QTextStream sfiletxt(&srcFile); 00639 sfiletxt.setCodec(codec); 00640 if (!srcFile.open(QIODevice::ReadOnly | QIODevice::Text)) { 00641 error << QString("Unable to open '%1' for reading: %2\n").arg(infile) 00642 .arg(srcFile.errorString()); 00643 return 2; 00644 } 00645 00646 /* Make sure the outfile does not exist before we use it. */ 00647 if (QFile::exists(outfile)) { 00648 if (!QFile::remove(outfile)) { 00649 error << QString("Unable to truncate outfile '%1'\n").arg(outfile); 00650 return 2; 00651 } 00652 } 00653 00654 QDomDocument doc; 00655 QString parseError; 00656 int badline, badcol; 00657 if (!doc.setContent (sfiletxt.readAll(), false, &parseError, &badline, &badcol)) { 00658 error << QString("Error parsing source document '%1' at line %2 and column %3: %4") 00659 .arg(infile).arg(badline).arg(badcol).arg(parseError); 00660 return 3; 00661 } 00662 00663 if (!command.compare("userlocal", Qt::CaseInsensitive)) { 00664 if (!docuserlocal(&doc, commandargs[0], &errorMessage)) { 00665 error << QString("Unable to convert document components to user local: %1\n") 00666 .arg(errorMessage); 00667 return 4; 00668 } 00669 } 00670 else { 00671 for (int i = 0; i < commandargs.count(); i++) { 00672 if (!command.compare("splice", Qt::CaseInsensitive)) { 00673 if (!docsplice(&doc, commandargs[i], &errorMessage)) { 00674 error << QString("Unable to process splice command '%1': %2\n") 00675 .arg(commandargs[i]).arg(errorMessage); 00676 return 4; 00677 } 00678 } 00679 else if (!command.compare("replace", Qt::CaseInsensitive)) { 00680 if (!docreplace(&doc, commandargs[i], &errorMessage)) { 00681 error << QString("Unable to process replace command '%1': %2\n") 00682 .arg(commandargs[i]).arg(errorMessage); 00683 return 4; 00684 } 00685 } 00686 else if (!command.compare("add", Qt::CaseInsensitive)) { 00687 if (!docadd(&doc, commandargs[i], &errorMessage)) { 00688 error << QString("Unable to process add command '%1': %2\n") 00689 .arg(commandargs[i]).arg(errorMessage); 00690 return 4; 00691 } 00692 } 00693 } 00694 } 00695 00696 /* Open the output file for writing. */ 00697 QFile docFile(outfile); 00698 if (!docFile.open(QIODevice::WriteOnly | QIODevice::Text)) { 00699 error << QString("Unable to open '%1' for writing: %2\n").arg(outfile) 00700 .arg(docFile.errorString()); 00701 return 5; 00702 } 00703 00704 /* Write the .wxl output. */ 00705 QTextStream out(&docFile); 00706 out << doc.toString(4); 00707 00708 return 0; 00709 } 00710