0
|
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(¶ms, 0, vectorEqualsString, NULL);
|
|
410 |
if (!CMDLINE_Parse(c,argv+1,argc-1,PARSE_NO_DUP,¶ms) ||
|
|
411 |
VECTOR_IsEmpty(¶ms) || 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(¶ms) > 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(¶ms, 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(¶ms);
|
|
541 |
CMDLINE_Delete(c);
|
|
542 |
RANDOM_Shutdown();
|
|
543 |
MEM_Shutdown();
|
|
544 |
return status;
|
|
545 |
}
|