--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/sdkcreationmw/sdkruntimes/NMIT/instreg/instreg.c Mon Mar 08 12:09:11 2010 +0530
@@ -0,0 +1,545 @@
+/*
+* Copyright (c) 2003-2005 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:
+*
+*/
+
+
+#include "s_lib.h"
+#include "instreg.h"
+
+/* Exit codes */
+#define EXITCODE_SUCCESS 0 /* successful completion */
+#define EXITCODE_HELP 1 /* help printed, nothing done */
+#define EXITCODE_CMDLINE 2 /* command line parse error */
+#define EXITCODE_IOERR 3 /* cannot write installation registry */
+#define EXITCODE_NOMEM 4 /* out of memory (very unlikely) */
+
+/* Data structures */
+struct _MemContext {
+ const MemHook * next;
+};
+
+typedef struct _AppContext {
+ Bool unregister; /* true if we are unregistering the product */
+ StrBuf sb; /* temporary string buffer */
+ DOMNode* root; /* root node of the DOM tree */
+ Str name; /* product name */
+ Str ver; /* product version, NULL if unknown */
+ Str loc; /* product location */
+ Str exe; /* product executable */
+ Str type; /* product type */
+ Str nameEsc; /* XML-escaped product name */
+ Str typeEsc; /* XML-escaped product type */
+ Str verEsc; /* XML-escaped version */
+ Str exeEsc; /* XML-escaped path */
+ Str locEsc; /* XML-escaped location */
+} AppContext;
+
+/* Tags in the installation registry file */
+#define REGISTRY_TAG "registry"
+#define PRODUCT_TAG "product"
+#define NAME_TAG "name"
+#define VERSION_TAG "version"
+#define EXECUTABLE_TAG "executable"
+#define LOCATION_TAG "location"
+#define TYPE_ATTR "type"
+#define EMULATOR_TYPE "emulator"
+
+#define ROOT_TAG REGISTRY_TAG
+
+/*
+ * Some default values. Note that we are making an assumption that the
+ * default values don't need to be XML-escaped.
+ */
+#define DEFAULT_TYPE EMULATOR_TYPE
+#define DEFAULT_VERSION "1.0"
+
+/* Desctiption of the command line parameters */
+#define CMDLINE_PARAM "PRODUCT"
+
+/* Modifiable global data */
+static Char* pname = "instreg";
+static Char regfile[] = "C:\\Nokia\\Registry\\installationRegistry.xml";
+
+/* Constants */
+static const Str commonHeader = "\
+<?xml version=\"1.0\" standalone=\"yes\"?>\n\
+\n\
+<!DOCTYPE "REGISTRY_TAG" [\n\
+<!ELEMENT "REGISTRY_TAG" ("PRODUCT_TAG")*>\n\
+<!ELEMENT "PRODUCT_TAG" ("NAME_TAG", "VERSION_TAG", "EXECUTABLE_TAG", "\
+LOCATION_TAG")>\n\
+<!ELEMENT "NAME_TAG" (#PCDATA)>\n\
+<!ELEMENT "VERSION_TAG" (#PCDATA)>\n\
+<!ELEMENT "EXECUTABLE_TAG" (#PCDATA)>\n\
+<!ELEMENT "LOCATION_TAG" (#PCDATA)>\n\
+<!ATTLIST "PRODUCT_TAG" "TYPE_ATTR" CDATA #IMPLIED>\n\
+]>\n\
+\n\
+";
+
+/* This template is used when we are creating a new file */
+static const Str newTemplate = "%s\
+<"REGISTRY_TAG">\n\
+ <"PRODUCT_TAG" "TYPE_ATTR"=\"%s\">\n\
+ <"NAME_TAG">\n\
+ %s\n\
+ </"NAME_TAG">\n\
+ <"VERSION_TAG">\n\
+ %s\n\
+ </"VERSION_TAG">\n\
+ <"EXECUTABLE_TAG">\n\
+ %s\n\
+ </"EXECUTABLE_TAG">\n\
+ <"LOCATION_TAG">\n\
+ %s\n\
+ </"LOCATION_TAG">\n\
+ </"PRODUCT_TAG">\n\
+</"REGISTRY_TAG">\n\
+";
+
+/* This template is used when we are updating the existing entry */
+static const Str updateTemplate = "\
+<"PRODUCT_TAG" "TYPE_ATTR"=\"%s\">\n\
+ <"NAME_TAG">\n\
+ %s\n\
+ </"NAME_TAG">\n\
+ <"VERSION_TAG">\n\
+ %s\n\
+ </"VERSION_TAG">\n\
+ <"EXECUTABLE_TAG">\n\
+ %s\n\
+ </"EXECUTABLE_TAG">\n\
+ <"LOCATION_TAG">\n\
+ %s\n\
+ </"LOCATION_TAG">\n\
+ </"PRODUCT_TAG">\
+";
+
+/* Template for appending a new entry to the existing file */
+static const Str appendTemplate = "\
+ <"PRODUCT_TAG" "TYPE_ATTR"=\"%s\">\n\
+ <"NAME_TAG">\n\
+ %s\n\
+ </"NAME_TAG">\n\
+ <"VERSION_TAG">\n\
+ %s\n\
+ </"VERSION_TAG">\n\
+ <"EXECUTABLE_TAG">\n\
+ %s\n\
+ </"EXECUTABLE_TAG">\n\
+ <"LOCATION_TAG">\n\
+ %s\n\
+ </"LOCATION_TAG">\n\
+ </"PRODUCT_TAG">\n\
+";
+
+/**
+ * Memory hooks. If memory allocation fails, we exit the program with
+ * EXITCODE_NOMEM status. Therefore, we don't need for check for memory
+ * allocation failures in the code.
+ */
+static Bool APP_MemInit(MemContext* ctx, const MemHook* next)
+{
+ ctx->next = next;
+ return True;
+}
+
+static void* APP_MemAlloc(MemContext* ctx, size_t size)
+{
+ void* ptr = MEM_Alloc2(ctx->next, size);
+ if (!ptr) {
+ TRACE_Error("Out of memory!\n");
+ exit(EXITCODE_NOMEM);
+ }
+ return ptr;
+}
+
+static Bool APP_PrintTag(File* out, Str tag, const XMLAttr* a, StrBuf* sb)
+{
+ if (FILE_Puts(out, TEXT("<")) && FILE_Puts(out, tag)) {
+ int i;
+ int n = XML_AttrCount(a);
+ for (i=0; i<n; i++) {
+ Str name = XML_AttrNameAt(a, i);
+ Str value = XML_AttrValueAt(a, i);
+ Str esc = XML_Escape(NULL, value);
+ if (!esc) esc = XML_Escape(sb, value);
+ if (FILE_Printf(out, TEXT(" %s=\"%s\""), name, esc) < 0) {
+ return False;
+ }
+ }
+ return (FILE_Puts(out, TEXT(">")) > 0);
+ }
+ return False;
+}
+
+/**
+ * Prints the DOM node to the specified stream.
+ */
+static Bool APP_PrintDOM(File* out, DOMNode* node, StrBuf* sb)
+{
+ if (APP_PrintTag(out, DOM_TagName(node),DOM_Attr(node), sb)) {
+ if (DOM_ChunkCount(node) == 0) {
+ Str charData = DOM_CharData(node);
+ if (charData && FILE_Printf(out, TEXT("%s"), charData) < 0) {
+ return False;
+ }
+ } else {
+ DOMChunk* chunk = DOM_FirstChunk(node);
+ while (chunk) {
+ DOMNode* child = DOM_ChunkNode(chunk);
+ Str text = DOM_ChunkText(chunk);
+ ASSERT(text || child);
+ if ((text && !FILE_Puts(out, text)) ||
+ (child && !APP_PrintDOM(out, child, sb))) {
+ return False;
+ }
+ chunk=DOM_NextChunk(chunk);
+ }
+ }
+ return (FILE_Printf(out, TEXT("</%s>"),DOM_TagName(node)) > 0);
+ }
+ return False;
+}
+
+/**
+ * Matches the child tag of the product node against the expected value
+ */
+static Bool APP_MatchInfo(DOMNode* p, Str tag, Str expect, StrBuf* sb)
+{
+ if (expect) {
+ DOMNode* node = DOM_FindFirst(p, tag);
+ if (node) {
+ Str data = DOM_CharData(node);
+ if (data && StrStr(data, expect)) {
+ STRBUF_Copy(sb, data);
+ STRBUF_Trim(sb);
+ return STRBUF_EqualsTo(sb, expect);
+ }
+ }
+ /* no match */
+ return False;
+ } else {
+ /* nothing is expected, anything will do */
+ return True;
+ }
+}
+
+/**
+ * Checks whether the node matches the product criteria specified on
+ * the command line. This serves dual purpose. When registering the
+ * product, this is how we find the existing entry to update. The
+ * first entry that matches the criteria will be updated, all others
+ * ignored. When unregistering the product, all matching entries are
+ * removed.
+ */
+static Bool APP_MatchProduct(DOMNode* product, AppContext* app)
+{
+ Bool match = False;
+ if (product && StrCmp(DOM_TagName(product),PRODUCT_TAG) == 0) {
+ Str type = XML_AttrValue(DOM_Attr(product),TYPE_ATTR);
+ if (!app->type || (type && StrCmp(type,app->type) == 0)) {
+ /* compare product information */
+ if (APP_MatchInfo(product, NAME_TAG, app->name, &app->sb) &&
+ APP_MatchInfo(product, VERSION_TAG, app->ver, &app->sb) &&
+ APP_MatchInfo(product, LOCATION_TAG, app->loc, &app->sb) &&
+ APP_MatchInfo(product, EXECUTABLE_TAG, app->exe, &app->sb)) {
+ match = True;
+ }
+ }
+ }
+ return match;
+}
+
+/**
+ * FILE_Save callback. If invoked from FILE_Save, this callback is actually
+ * writing to a temporary file. The temporary file then gets renamed into the
+ * actual output file. This guarantees that we either successfully update the
+ * file, or don't change it at all. Note that we also invoke this callback
+ * directly, to write to stdout.
+ */
+static Bool APP_FileSaveCB(File* out, Str fname, void * ctx)
+{
+ AppContext* app = ctx;
+ Str tag = DOM_TagName(app->root);
+ if (FILE_Puts(out, commonHeader) &&
+ APP_PrintTag(out, tag, DOM_Attr(app->root), &app->sb)) {
+ Bool updated = False;
+ DOMChunk* chunk = DOM_FirstChunk(app->root);
+ while (chunk) {
+ DOMNode* child = DOM_ChunkNode(chunk);
+ Str text = DOM_ChunkText(chunk);
+ if (child) {
+ if (APP_MatchProduct(child, app)) {
+
+ /*
+ * If unregistering, skip this entry. Also, when
+ * we find more that one matching entry when we are
+ * registering the product, we update the first one
+ * and remove the others.
+ */
+ if (!app->unregister && !updated) {
+ /* update the existing entry */
+ if (!FILE_Puts(out, text) ||
+ FILE_Printf(out, updateTemplate,
+ app->typeEsc, app->nameEsc,
+ app->verEsc, app->exeEsc,
+ app->locEsc) > 0) {
+ updated = True;
+ } else {
+ return False;
+ }
+ }
+ } else if (!FILE_Puts(out, text) ||
+ !APP_PrintDOM(out, child, &app->sb)) {
+ return False;
+ }
+ } else if (text) {
+ if (!FILE_Puts(out, text)) {
+ return False;
+ }
+ }
+ chunk = DOM_NextChunk(chunk);
+ }
+ if (!updated) {
+ if (DOM_ChunkCount(app->root) == 0) {
+ Str charData = DOM_CharData(app->root);
+ if (charData && FILE_Printf(out, TEXT("%s"), charData) < 0) {
+ return False;
+ }
+ }
+ if (!app->unregister) {
+ /* append a new entry */
+ if (FILE_Printf(out,appendTemplate, app->typeEsc,
+ app->nameEsc, app->verEsc, app->exeEsc,
+ app->locEsc) < 0) {
+ return False;
+ }
+ }
+ }
+ return (FILE_Printf(out, TEXT("</%s>\n"),tag) > 0);
+ }
+
+ /* some kind of I/O error */
+ return False;
+}
+
+/**
+ * The program entry point.
+ */
+int main(int argc, char* argv[])
+{
+ AppContext app;
+ Bool toStdout = False;
+ Bool help = False;
+ Vector params;
+ CmdLine* c;
+ Str prog;
+ int status;
+ Char sysDirBuf[MAX_PATH +1];
+
+ MemProc mp;
+ MemContext mc;
+
+ /* install memory hook so that we don't have to check for NULL */
+ MEM_InitModule();
+ memset(&mc, 0, sizeof(mc));
+ memset(&mp, 0, sizeof(mp));
+ mp.memInit = APP_MemInit;
+ mp.memAlloc = APP_MemAlloc;
+ MEM_Hook(&mp, &mc);
+ RANDOM_InitModule();
+
+ /* figure out the program name */
+ prog = FILE_FilePart(argv[0]);
+#ifdef _WIN32
+ if (STRING_EndsWith(prog, ".exe")) {
+ int n = StrLen(prog)-4;
+ pname = MEM_NewArray(Char, n+1);
+ StrnCpy(pname, prog, n);
+ pname[n] = 0;
+ } else
+#endif /* _WIN32 */
+ pname = STRING_Dup(prog);
+
+ /*
+ * determine the drive where Windows is installed. That's the drive
+ * where installationRegistry.xml file is located.
+ */
+#ifdef _WIN32
+ if (GetSystemDirectory(sysDirBuf, COUNT(sysDirBuf))) {
+ TRACE1("system drive is %c:\n",sysDirBuf[0]);
+ regfile[0] = sysDirBuf[0];
+ }
+#endif /* _WIN32 */
+
+ memset(&app, 0, sizeof(app));
+ c = CMDLINE_Create(pname);
+
+#ifdef _CONSOLE
+ CMDLINE_AddTrueOpt(c,'h',"help","print this help and exit",&help);
+ CMDLINE_AddTrueOpt(c,'c',"stdout","write updated installation registry "
+ "to stdout",&toStdout);
+#endif /* _CONSOLE */
+
+ CMDLINE_SetParamName(CMDLINE_AddStrOpt(c,'e',"executable",
+ "path to the executable (required to register)",&app.exe), "FILE");
+ CMDLINE_SetParamName(CMDLINE_AddStrOpt(c,'v',"version",
+ "product version (default is "DEFAULT_VERSION")",&app.ver),"VERSION");
+ CMDLINE_SetParamName(CMDLINE_AddStrOpt(c,'l',"location",
+ "product location (default is the executable folder)",&app.loc),"DIR");
+ CMDLINE_SetParamName(CMDLINE_AddStrOpt(c,'t',"type",
+ "product type (default is "DEFAULT_TYPE")",&app.type),"TYPE");
+ CMDLINE_AddTrueOpt(c,'u',"unregister","unregister the product "
+ "(default is to register)",&app.unregister);
+
+ VECTOR_Init(¶ms, 0, vectorEqualsString, NULL);
+ if (!CMDLINE_Parse(c,argv+1,argc-1,PARSE_NO_DUP,¶ms) ||
+ VECTOR_IsEmpty(¶ms) || help) {
+ TRACE_Output(PRODUCT_NAME " Version %d.%d.%d\nCopyright (C) "
+ PRODUCT_COPYRIGHT ". All rights reserved.\n\n",
+ PRODUCT_VERSION_MAJOR, PRODUCT_VERSION_MINOR,
+ PRODUCT_VERSION_MICRO);
+ CMDLINE_Usage(c, CMDLINE_PARAM, 0);
+ status = (help ? EXITCODE_HELP : EXITCODE_CMDLINE);
+ } else if (VECTOR_Size(¶ms) > 1) {
+ TRACE_Error("%s: unexpected command line parameters\n", pname);
+ CMDLINE_Usage(c, CMDLINE_PARAM, 0);
+ status = EXITCODE_CMDLINE;
+ } else if (!app.exe && !app.unregister) {
+ TRACE_Error("%s: product executable is required\n", pname);
+ CMDLINE_Usage(c, CMDLINE_PARAM, 0);
+ status = EXITCODE_CMDLINE;
+ } else {
+
+ StrBuf typeBuf;
+ StrBuf nameBuf;
+ StrBuf verBuf;
+ StrBuf exeBuf;
+ StrBuf locBuf;
+ Char* dir = NULL;
+
+ if (app.exe && !app.loc) {
+ /* default location */
+ if (FILE_FilePart(app.exe) == app.exe) {
+ int n = GetCurrentDirectory(0,NULL)+1;
+ dir = MEM_NewArray(Char,n);
+ dir[GetCurrentDirectory(n,dir)] = 0;
+ } else {
+ dir = FILE_DirName(app.exe, 0);
+ }
+ if (STRING_EndsWith(dir, FILE_SEPARATOR)) {
+ dir[StrLen(dir)-1] = 0;
+ }
+ app.loc = dir;
+ }
+
+ status = EXITCODE_SUCCESS;
+
+ /* XML escape the strings */
+ STRBUF_Init(&typeBuf);
+ STRBUF_Init(&nameBuf);
+ STRBUF_Init(&verBuf);
+ STRBUF_Init(&exeBuf);
+ STRBUF_Init(&locBuf);
+ STRBUF_Init(&app.sb);
+ app.name = VECTOR_Get(¶ms, 0);
+ app.nameEsc = XML_Escape(&nameBuf, app.name);
+ if (app.exe) app.exeEsc = XML_Escape(&exeBuf, app.exe);
+ if (app.loc) app.locEsc = XML_Escape(&locBuf, app.loc);
+
+ if (app.type) {
+ app.typeEsc = XML_Escape(&typeBuf, app.type);
+ } else {
+ /* we know there's nothing to escape here */
+ app.typeEsc = DEFAULT_TYPE;
+ }
+
+ if (app.ver) {
+ app.verEsc = XML_Escape(&verBuf, app.ver);
+ } else {
+ /* we know there's nothing to escape here */
+ app.verEsc = DEFAULT_VERSION;
+ }
+
+ app.root = DOM_Load(regfile);
+ if (app.root && StrCmp(DOM_TagName(app.root),ROOT_TAG) == 0) {
+ if (toStdout) {
+ File* out = FILE_AttachToFile(stdout,"stdout");
+ APP_FileSaveCB(out, FILE_Name(out), &app);
+ FILE_Close(out);
+ } else {
+ if (!FILE_Save(regfile, APP_FileSaveCB, &app, NULL)) {
+ status = EXITCODE_IOERR;
+ }
+ }
+ } else if (!app.unregister) {
+
+ /*
+ * either the file does not exist, or else it exists but
+ * contains some garbage (root tag does not match the
+ * expectation). Create a new file, unless the user has
+ * given us -u (unregister) option in which case we have
+ * nothing to do.
+ */
+ if (toStdout) {
+ printf(newTemplate, commonHeader, app.typeEsc, app.nameEsc,
+ app.verEsc, app.exeEsc, app.locEsc);
+ } else {
+
+ /* create a new file */
+ File* f = FILE_Open(regfile, WRITE_TEXT_MODE, NULL);
+ if (!f) {
+ /* perhaps, the directory does not exist? */
+ Char* regdir = FILE_DirName(regfile, 0);
+ FILE_MkDir(regdir);
+ MEM_Free(regdir);
+ }
+
+ /* try again */
+ f = FILE_Open(regfile, WRITE_TEXT_MODE, NULL);
+ if (f) {
+ if (FILE_Printf(f, newTemplate, commonHeader,
+ app.typeEsc, app.nameEsc,
+ app.verEsc, app.exeEsc,
+ app.locEsc) < 0) {
+ status = EXITCODE_IOERR;
+ }
+ FILE_Close(f);
+ } else {
+ status = EXITCODE_IOERR;
+ }
+ }
+ }
+
+ STRBUF_Destroy(&app.sb);
+ DOM_Delete(app.root);
+ STRBUF_Destroy(&typeBuf);
+ STRBUF_Destroy(&nameBuf);
+ STRBUF_Destroy(&verBuf);
+ STRBUF_Destroy(&exeBuf);
+ STRBUF_Destroy(&locBuf);
+ MEM_Free(dir);
+ }
+
+ /* Cleanup */
+ MEM_Free(pname);
+ VECTOR_Destroy(¶ms);
+ CMDLINE_Delete(c);
+ RANDOM_Shutdown();
+ MEM_Shutdown();
+ return status;
+}