sdkcreationmw/sdkruntimes/NMIT/instreg/instreg.c
changeset 0 b26acd06ea60
equal deleted inserted replaced
-1:000000000000 0:b26acd06ea60
       
     1 /*
       
     2 * Copyright (c) 2003-2005 Nokia Corporation and/or its subsidiary(-ies).
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available
       
     5 * under the terms of "Eclipse Public License v1.0"
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - initial contribution.
       
    11 *
       
    12 * Contributors:
       
    13 *
       
    14 * Description: 
       
    15 *
       
    16 */
       
    17 
       
    18 
       
    19 #include "s_lib.h"
       
    20 #include "instreg.h"
       
    21 
       
    22 /* Exit codes */
       
    23 #define EXITCODE_SUCCESS    0   /* successful completion */
       
    24 #define EXITCODE_HELP       1   /* help printed, nothing done */
       
    25 #define EXITCODE_CMDLINE    2   /* command line parse error */
       
    26 #define EXITCODE_IOERR      3   /* cannot write installation registry */
       
    27 #define EXITCODE_NOMEM      4   /* out of memory (very unlikely) */
       
    28 
       
    29 /* Data structures */
       
    30 struct _MemContext {
       
    31     const MemHook * next;
       
    32 };
       
    33 
       
    34 typedef struct _AppContext {
       
    35     Bool unregister;            /* true if we are unregistering the product */
       
    36     StrBuf sb;                  /* temporary string buffer */
       
    37     DOMNode* root;              /* root node of the DOM tree */
       
    38     Str name;                   /* product name */
       
    39     Str ver;                    /* product version, NULL if unknown */
       
    40     Str loc;                    /* product location */
       
    41     Str exe;                    /* product executable */
       
    42     Str type;                   /* product type */
       
    43     Str nameEsc;                /* XML-escaped product name */
       
    44     Str typeEsc;                /* XML-escaped product type */
       
    45     Str verEsc;                 /* XML-escaped version */
       
    46     Str exeEsc;                 /* XML-escaped path */
       
    47     Str locEsc;                 /* XML-escaped location */
       
    48 } AppContext;
       
    49 
       
    50 /* Tags in the installation registry file */
       
    51 #define REGISTRY_TAG    "registry"
       
    52 #define PRODUCT_TAG     "product"
       
    53 #define NAME_TAG        "name"
       
    54 #define VERSION_TAG     "version"
       
    55 #define EXECUTABLE_TAG  "executable"
       
    56 #define LOCATION_TAG    "location"
       
    57 #define TYPE_ATTR       "type"
       
    58 #define EMULATOR_TYPE   "emulator"
       
    59 
       
    60 #define ROOT_TAG        REGISTRY_TAG
       
    61 
       
    62 /*
       
    63  * Some default values. Note that we are making an assumption that the
       
    64  * default values don't need to be XML-escaped.
       
    65  */
       
    66 #define DEFAULT_TYPE    EMULATOR_TYPE
       
    67 #define DEFAULT_VERSION "1.0"
       
    68 
       
    69 /* Desctiption of the command line parameters */
       
    70 #define CMDLINE_PARAM   "PRODUCT"
       
    71 
       
    72 /* Modifiable global data */
       
    73 static Char* pname = "instreg";
       
    74 static Char regfile[] = "C:\\Nokia\\Registry\\installationRegistry.xml";
       
    75 
       
    76 /* Constants */
       
    77 static const Str commonHeader = "\
       
    78 <?xml version=\"1.0\" standalone=\"yes\"?>\n\
       
    79 \n\
       
    80 <!DOCTYPE "REGISTRY_TAG" [\n\
       
    81 <!ELEMENT "REGISTRY_TAG" ("PRODUCT_TAG")*>\n\
       
    82 <!ELEMENT "PRODUCT_TAG" ("NAME_TAG", "VERSION_TAG", "EXECUTABLE_TAG", "\
       
    83 LOCATION_TAG")>\n\
       
    84 <!ELEMENT "NAME_TAG" (#PCDATA)>\n\
       
    85 <!ELEMENT "VERSION_TAG" (#PCDATA)>\n\
       
    86 <!ELEMENT "EXECUTABLE_TAG" (#PCDATA)>\n\
       
    87 <!ELEMENT "LOCATION_TAG" (#PCDATA)>\n\
       
    88 <!ATTLIST "PRODUCT_TAG" "TYPE_ATTR" CDATA #IMPLIED>\n\
       
    89 ]>\n\
       
    90 \n\
       
    91 ";
       
    92 
       
    93 /* This template is used when we are creating a new file */
       
    94 static const Str newTemplate = "%s\
       
    95 <"REGISTRY_TAG">\n\
       
    96     <"PRODUCT_TAG" "TYPE_ATTR"=\"%s\">\n\
       
    97         <"NAME_TAG">\n\
       
    98             %s\n\
       
    99         </"NAME_TAG">\n\
       
   100         <"VERSION_TAG">\n\
       
   101             %s\n\
       
   102         </"VERSION_TAG">\n\
       
   103         <"EXECUTABLE_TAG">\n\
       
   104             %s\n\
       
   105         </"EXECUTABLE_TAG">\n\
       
   106         <"LOCATION_TAG">\n\
       
   107             %s\n\
       
   108         </"LOCATION_TAG">\n\
       
   109     </"PRODUCT_TAG">\n\
       
   110 </"REGISTRY_TAG">\n\
       
   111 ";
       
   112 
       
   113 /* This template is used when we are updating the existing entry */
       
   114 static const Str updateTemplate = "\
       
   115 <"PRODUCT_TAG" "TYPE_ATTR"=\"%s\">\n\
       
   116         <"NAME_TAG">\n\
       
   117             %s\n\
       
   118         </"NAME_TAG">\n\
       
   119         <"VERSION_TAG">\n\
       
   120             %s\n\
       
   121         </"VERSION_TAG">\n\
       
   122         <"EXECUTABLE_TAG">\n\
       
   123             %s\n\
       
   124         </"EXECUTABLE_TAG">\n\
       
   125         <"LOCATION_TAG">\n\
       
   126             %s\n\
       
   127         </"LOCATION_TAG">\n\
       
   128     </"PRODUCT_TAG">\
       
   129 ";
       
   130 
       
   131 /* Template for appending a new entry to the existing file */
       
   132 static const Str appendTemplate = "\
       
   133     <"PRODUCT_TAG" "TYPE_ATTR"=\"%s\">\n\
       
   134         <"NAME_TAG">\n\
       
   135             %s\n\
       
   136         </"NAME_TAG">\n\
       
   137         <"VERSION_TAG">\n\
       
   138             %s\n\
       
   139         </"VERSION_TAG">\n\
       
   140         <"EXECUTABLE_TAG">\n\
       
   141             %s\n\
       
   142         </"EXECUTABLE_TAG">\n\
       
   143         <"LOCATION_TAG">\n\
       
   144             %s\n\
       
   145         </"LOCATION_TAG">\n\
       
   146     </"PRODUCT_TAG">\n\
       
   147 ";
       
   148 
       
   149 /**
       
   150  * Memory hooks. If memory allocation fails, we exit the program with
       
   151  * EXITCODE_NOMEM status. Therefore, we don't need for check for memory
       
   152  * allocation failures in the code.
       
   153  */
       
   154 static Bool APP_MemInit(MemContext* ctx, const MemHook* next)
       
   155 {
       
   156     ctx->next = next;
       
   157     return True;
       
   158 }
       
   159 
       
   160 static void* APP_MemAlloc(MemContext* ctx, size_t size)
       
   161 {
       
   162     void* ptr = MEM_Alloc2(ctx->next, size);
       
   163     if (!ptr) {
       
   164         TRACE_Error("Out of memory!\n");
       
   165         exit(EXITCODE_NOMEM);
       
   166     }
       
   167     return ptr;
       
   168 }
       
   169 
       
   170 static Bool APP_PrintTag(File* out, Str tag, const XMLAttr* a, StrBuf* sb)
       
   171 {
       
   172     if (FILE_Puts(out, TEXT("<")) && FILE_Puts(out, tag)) {
       
   173         int i;
       
   174         int n = XML_AttrCount(a);
       
   175         for (i=0; i<n; i++) {
       
   176             Str name = XML_AttrNameAt(a, i);
       
   177             Str value = XML_AttrValueAt(a, i);
       
   178             Str esc = XML_Escape(NULL, value);
       
   179             if (!esc) esc = XML_Escape(sb, value);
       
   180             if (FILE_Printf(out, TEXT(" %s=\"%s\""), name, esc) < 0) {
       
   181                 return False;
       
   182             }
       
   183         }
       
   184         return (FILE_Puts(out, TEXT(">")) > 0);
       
   185     }
       
   186     return False;
       
   187 }
       
   188 
       
   189 /**
       
   190  * Prints the DOM node to the specified stream.
       
   191  */
       
   192 static Bool APP_PrintDOM(File* out, DOMNode* node, StrBuf* sb)
       
   193 {
       
   194     if (APP_PrintTag(out, DOM_TagName(node),DOM_Attr(node), sb)) {
       
   195         if (DOM_ChunkCount(node) == 0) {
       
   196             Str charData = DOM_CharData(node);
       
   197             if (charData && FILE_Printf(out, TEXT("%s"), charData) < 0) {
       
   198                 return False;
       
   199             }
       
   200         } else {
       
   201             DOMChunk* chunk = DOM_FirstChunk(node);
       
   202             while (chunk) {
       
   203                 DOMNode* child = DOM_ChunkNode(chunk);
       
   204                 Str text = DOM_ChunkText(chunk);
       
   205                 ASSERT(text || child);
       
   206                 if ((text && !FILE_Puts(out, text)) ||
       
   207                     (child && !APP_PrintDOM(out, child, sb))) {
       
   208                     return False;
       
   209                 }
       
   210                 chunk=DOM_NextChunk(chunk);
       
   211             }
       
   212         }
       
   213         return (FILE_Printf(out, TEXT("</%s>"),DOM_TagName(node)) > 0);
       
   214     }
       
   215     return False;
       
   216 }
       
   217 
       
   218 /**
       
   219  * Matches the child tag of the product node against the expected value
       
   220  */
       
   221 static Bool APP_MatchInfo(DOMNode* p, Str tag, Str expect, StrBuf* sb)
       
   222 {
       
   223     if (expect) {
       
   224         DOMNode* node = DOM_FindFirst(p, tag);
       
   225         if (node) {
       
   226             Str data = DOM_CharData(node);
       
   227             if (data && StrStr(data, expect)) {
       
   228                 STRBUF_Copy(sb, data);
       
   229                 STRBUF_Trim(sb);
       
   230                 return STRBUF_EqualsTo(sb, expect);
       
   231             }
       
   232         }
       
   233         /* no match */
       
   234         return False;
       
   235     } else {
       
   236         /* nothing is expected, anything will do */
       
   237         return True;
       
   238     }
       
   239 }
       
   240 
       
   241 /**
       
   242  * Checks whether the node matches the product criteria specified on
       
   243  * the command line. This serves dual purpose. When registering the
       
   244  * product, this is how we find the existing entry to update. The
       
   245  * first entry that matches the criteria will be updated, all others
       
   246  * ignored. When unregistering the product, all matching entries are
       
   247  * removed.
       
   248  */
       
   249 static Bool APP_MatchProduct(DOMNode* product, AppContext* app)
       
   250 {
       
   251     Bool match = False;
       
   252     if (product && StrCmp(DOM_TagName(product),PRODUCT_TAG) == 0) {
       
   253         Str type = XML_AttrValue(DOM_Attr(product),TYPE_ATTR);
       
   254         if (!app->type || (type && StrCmp(type,app->type) == 0)) {
       
   255             /* compare product information */
       
   256             if (APP_MatchInfo(product, NAME_TAG,  app->name, &app->sb) &&
       
   257                 APP_MatchInfo(product, VERSION_TAG,  app->ver, &app->sb) &&
       
   258                 APP_MatchInfo(product, LOCATION_TAG,  app->loc, &app->sb) &&
       
   259                 APP_MatchInfo(product, EXECUTABLE_TAG,  app->exe, &app->sb)) {
       
   260                 match = True;
       
   261             }
       
   262         }
       
   263     }
       
   264     return match;
       
   265 }
       
   266 
       
   267 /**
       
   268  * FILE_Save callback. If invoked from FILE_Save, this callback is actually
       
   269  * writing to a temporary file. The temporary file then gets renamed into the
       
   270  * actual output file. This guarantees that we either successfully update the
       
   271  * file, or don't change it at all. Note that we also invoke this callback 
       
   272  * directly, to write to stdout.
       
   273  */
       
   274 static Bool APP_FileSaveCB(File* out, Str fname, void * ctx)
       
   275 {
       
   276     AppContext* app = ctx;
       
   277     Str tag = DOM_TagName(app->root);
       
   278     if (FILE_Puts(out, commonHeader) &&
       
   279         APP_PrintTag(out, tag, DOM_Attr(app->root), &app->sb)) {
       
   280         Bool updated = False;
       
   281         DOMChunk* chunk = DOM_FirstChunk(app->root);
       
   282         while (chunk) {
       
   283             DOMNode* child = DOM_ChunkNode(chunk);
       
   284             Str text = DOM_ChunkText(chunk);
       
   285             if (child) {
       
   286                 if (APP_MatchProduct(child, app)) {
       
   287 
       
   288                     /* 
       
   289                      * If unregistering, skip this entry. Also, when 
       
   290                      * we find more that one matching entry when we are
       
   291                      * registering the product, we update the first one
       
   292                      * and remove the others.
       
   293                      */
       
   294                     if (!app->unregister && !updated) {
       
   295                         /* update the existing entry */
       
   296                         if (!FILE_Puts(out, text) ||
       
   297                              FILE_Printf(out, updateTemplate, 
       
   298                                         app->typeEsc, app->nameEsc, 
       
   299                                         app->verEsc, app->exeEsc, 
       
   300                                         app->locEsc) > 0) {
       
   301                             updated = True;
       
   302                         } else {
       
   303                             return False;
       
   304                         }
       
   305                     }
       
   306                 } else if (!FILE_Puts(out, text) ||
       
   307                            !APP_PrintDOM(out, child, &app->sb)) {
       
   308                     return False;
       
   309                 }
       
   310             } else if (text) {
       
   311                 if (!FILE_Puts(out, text)) {
       
   312                     return False;
       
   313                 }
       
   314             }
       
   315             chunk = DOM_NextChunk(chunk);
       
   316         }
       
   317         if (!updated) {
       
   318             if (DOM_ChunkCount(app->root) == 0) {
       
   319                 Str charData = DOM_CharData(app->root);
       
   320                 if (charData && FILE_Printf(out, TEXT("%s"), charData) < 0) {
       
   321                     return False;
       
   322                 }
       
   323             }
       
   324             if (!app->unregister) {
       
   325                 /* append a new entry */
       
   326                 if (FILE_Printf(out,appendTemplate, app->typeEsc, 
       
   327                                 app->nameEsc, app->verEsc, app->exeEsc,
       
   328                                 app->locEsc) < 0) {
       
   329                     return False;
       
   330                 }
       
   331             }
       
   332         }
       
   333         return (FILE_Printf(out, TEXT("</%s>\n"),tag) > 0);
       
   334     }
       
   335 
       
   336     /* some kind of I/O error */
       
   337     return False;
       
   338 }
       
   339 
       
   340 /**
       
   341  * The program entry point.
       
   342  */
       
   343 int main(int argc, char* argv[]) 
       
   344 {
       
   345     AppContext app;
       
   346     Bool toStdout = False;
       
   347     Bool help = False;
       
   348     Vector params;
       
   349     CmdLine* c;
       
   350     Str prog;
       
   351     int status;
       
   352     Char sysDirBuf[MAX_PATH +1];
       
   353 
       
   354     MemProc mp;
       
   355     MemContext mc;
       
   356 
       
   357     /* install memory hook so that we don't have to check for NULL */
       
   358     MEM_InitModule();    
       
   359     memset(&mc, 0, sizeof(mc));
       
   360     memset(&mp, 0, sizeof(mp));
       
   361     mp.memInit = APP_MemInit;
       
   362     mp.memAlloc = APP_MemAlloc;
       
   363     MEM_Hook(&mp, &mc);
       
   364     RANDOM_InitModule();    
       
   365 
       
   366     /* figure out the program name */
       
   367     prog = FILE_FilePart(argv[0]);
       
   368 #ifdef _WIN32
       
   369     if (STRING_EndsWith(prog, ".exe")) {
       
   370         int n = StrLen(prog)-4;
       
   371         pname = MEM_NewArray(Char, n+1);
       
   372         StrnCpy(pname, prog, n);
       
   373         pname[n] = 0;
       
   374     } else 
       
   375 #endif /* _WIN32 */
       
   376     pname = STRING_Dup(prog);
       
   377 
       
   378     /* 
       
   379      * determine the drive where Windows is installed. That's the drive
       
   380      * where installationRegistry.xml file is located.
       
   381      */
       
   382 #ifdef _WIN32
       
   383     if (GetSystemDirectory(sysDirBuf, COUNT(sysDirBuf))) {
       
   384         TRACE1("system drive is %c:\n",sysDirBuf[0]);
       
   385         regfile[0] = sysDirBuf[0];
       
   386     }
       
   387 #endif /* _WIN32 */
       
   388 
       
   389     memset(&app, 0, sizeof(app));
       
   390     c = CMDLINE_Create(pname);
       
   391 
       
   392 #ifdef _CONSOLE
       
   393     CMDLINE_AddTrueOpt(c,'h',"help","print this help and exit",&help);
       
   394     CMDLINE_AddTrueOpt(c,'c',"stdout","write updated installation registry "
       
   395         "to stdout",&toStdout);
       
   396 #endif /* _CONSOLE */
       
   397 
       
   398     CMDLINE_SetParamName(CMDLINE_AddStrOpt(c,'e',"executable",
       
   399         "path to the executable (required to register)",&app.exe), "FILE");
       
   400     CMDLINE_SetParamName(CMDLINE_AddStrOpt(c,'v',"version",
       
   401         "product version (default is "DEFAULT_VERSION")",&app.ver),"VERSION");
       
   402     CMDLINE_SetParamName(CMDLINE_AddStrOpt(c,'l',"location",
       
   403         "product location (default is the executable folder)",&app.loc),"DIR");
       
   404     CMDLINE_SetParamName(CMDLINE_AddStrOpt(c,'t',"type",
       
   405         "product type (default is "DEFAULT_TYPE")",&app.type),"TYPE");
       
   406     CMDLINE_AddTrueOpt(c,'u',"unregister","unregister the product "
       
   407         "(default is to register)",&app.unregister);
       
   408 
       
   409     VECTOR_Init(&params, 0, vectorEqualsString, NULL);
       
   410     if (!CMDLINE_Parse(c,argv+1,argc-1,PARSE_NO_DUP,&params) || 
       
   411         VECTOR_IsEmpty(&params) || help) {
       
   412         TRACE_Output(PRODUCT_NAME " Version %d.%d.%d\nCopyright (C) "
       
   413             PRODUCT_COPYRIGHT ". All rights reserved.\n\n", 
       
   414             PRODUCT_VERSION_MAJOR, PRODUCT_VERSION_MINOR,
       
   415             PRODUCT_VERSION_MICRO);
       
   416         CMDLINE_Usage(c, CMDLINE_PARAM, 0);
       
   417         status = (help ? EXITCODE_HELP : EXITCODE_CMDLINE);
       
   418     } else if (VECTOR_Size(&params) > 1) {
       
   419         TRACE_Error("%s: unexpected command line parameters\n", pname);
       
   420         CMDLINE_Usage(c, CMDLINE_PARAM, 0);
       
   421         status = EXITCODE_CMDLINE;
       
   422     } else if (!app.exe && !app.unregister) {
       
   423         TRACE_Error("%s: product executable is required\n", pname);
       
   424         CMDLINE_Usage(c, CMDLINE_PARAM, 0);
       
   425         status = EXITCODE_CMDLINE;
       
   426     } else {
       
   427 
       
   428         StrBuf typeBuf;
       
   429         StrBuf nameBuf;
       
   430         StrBuf verBuf;
       
   431         StrBuf exeBuf;
       
   432         StrBuf locBuf;
       
   433         Char* dir = NULL;
       
   434 
       
   435         if (app.exe && !app.loc) {
       
   436             /* default location */
       
   437             if (FILE_FilePart(app.exe) == app.exe) {
       
   438                 int n = GetCurrentDirectory(0,NULL)+1;
       
   439                 dir = MEM_NewArray(Char,n);
       
   440                 dir[GetCurrentDirectory(n,dir)] = 0;
       
   441             } else {
       
   442                 dir = FILE_DirName(app.exe, 0);
       
   443             }
       
   444             if (STRING_EndsWith(dir, FILE_SEPARATOR)) {
       
   445                 dir[StrLen(dir)-1] = 0;
       
   446             }
       
   447             app.loc = dir;
       
   448         }
       
   449 
       
   450         status = EXITCODE_SUCCESS;
       
   451 
       
   452         /* XML escape the strings */
       
   453         STRBUF_Init(&typeBuf);
       
   454         STRBUF_Init(&nameBuf);
       
   455         STRBUF_Init(&verBuf);
       
   456         STRBUF_Init(&exeBuf);
       
   457         STRBUF_Init(&locBuf);
       
   458         STRBUF_Init(&app.sb);
       
   459         app.name = VECTOR_Get(&params, 0);
       
   460         app.nameEsc = XML_Escape(&nameBuf, app.name);
       
   461         if (app.exe) app.exeEsc = XML_Escape(&exeBuf, app.exe);
       
   462         if (app.loc) app.locEsc = XML_Escape(&locBuf, app.loc);
       
   463         
       
   464         if (app.type) {
       
   465             app.typeEsc = XML_Escape(&typeBuf, app.type);
       
   466         } else {
       
   467             /* we know there's nothing to escape here */
       
   468             app.typeEsc = DEFAULT_TYPE;
       
   469         }
       
   470 
       
   471         if (app.ver) {
       
   472             app.verEsc = XML_Escape(&verBuf, app.ver);
       
   473         } else {
       
   474             /* we know there's nothing to escape here */
       
   475             app.verEsc = DEFAULT_VERSION;
       
   476         }
       
   477 
       
   478         app.root = DOM_Load(regfile);
       
   479         if (app.root && StrCmp(DOM_TagName(app.root),ROOT_TAG) == 0) {
       
   480             if (toStdout) {
       
   481                 File* out = FILE_AttachToFile(stdout,"stdout");
       
   482                 APP_FileSaveCB(out, FILE_Name(out), &app);
       
   483                 FILE_Close(out);
       
   484             } else {
       
   485                 if (!FILE_Save(regfile, APP_FileSaveCB, &app, NULL)) {
       
   486                     status = EXITCODE_IOERR;
       
   487                 }
       
   488             }
       
   489         } else if (!app.unregister) {
       
   490 
       
   491             /*
       
   492              * either the file does not exist, or else it exists but 
       
   493              * contains some garbage (root tag does not match the 
       
   494              * expectation). Create a new file, unless the user has
       
   495              * given us -u (unregister) option in which case we have
       
   496              * nothing to do.
       
   497              */
       
   498             if (toStdout) {
       
   499                 printf(newTemplate, commonHeader, app.typeEsc, app.nameEsc,
       
   500                     app.verEsc, app.exeEsc, app.locEsc);
       
   501             } else {
       
   502 
       
   503                 /* create a new file */
       
   504                 File* f = FILE_Open(regfile, WRITE_TEXT_MODE, NULL);
       
   505                 if (!f) {
       
   506                     /* perhaps, the directory does not exist? */
       
   507                     Char* regdir = FILE_DirName(regfile, 0);
       
   508                     FILE_MkDir(regdir);
       
   509                     MEM_Free(regdir);
       
   510                 }
       
   511 
       
   512                 /* try again */
       
   513                 f = FILE_Open(regfile, WRITE_TEXT_MODE, NULL);
       
   514                 if (f) {
       
   515                     if (FILE_Printf(f, newTemplate, commonHeader, 
       
   516                                     app.typeEsc, app.nameEsc, 
       
   517                                     app.verEsc, app.exeEsc, 
       
   518                                     app.locEsc) < 0) {
       
   519                         status = EXITCODE_IOERR;
       
   520                     }
       
   521                     FILE_Close(f);
       
   522                 } else {
       
   523                     status = EXITCODE_IOERR;
       
   524                 }
       
   525             }
       
   526         }
       
   527 
       
   528         STRBUF_Destroy(&app.sb);
       
   529         DOM_Delete(app.root);
       
   530         STRBUF_Destroy(&typeBuf);
       
   531         STRBUF_Destroy(&nameBuf);
       
   532         STRBUF_Destroy(&verBuf);
       
   533         STRBUF_Destroy(&exeBuf);
       
   534         STRBUF_Destroy(&locBuf);
       
   535         MEM_Free(dir);
       
   536     }
       
   537 
       
   538     /* Cleanup */
       
   539     MEM_Free(pname);
       
   540     VECTOR_Destroy(&params);
       
   541     CMDLINE_Delete(c);
       
   542     RANDOM_Shutdown();
       
   543     MEM_Shutdown();
       
   544     return status;
       
   545 }