Fixed color definition (bg and fg were swapped).
[wine] / programs / regapi / regapi.c
1 /*
2  * Command line Registry implementation
3  *
4  * Copyright 1999 Sylvain St-Germain
5  *
6  * Note: Please consult the README file for more information.
7  *
8  */
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <windows.h>
13 #include <winreg.h>
14 #include <winerror.h>
15 #include <winnt.h>
16 #include <string.h>
17
18 /******************************************************************************
19  * Defines and consts
20  */
21 #define IDENTICAL             0 
22 #define COMMAND_COUNT         7
23
24 #define KEY_MAX_LEN           1024 
25 #define STDIN_MAX_LEN         2048
26
27 /* Return values */
28 #define COMMAND_NOT_FOUND     -1
29 #define SUCCESS               0
30 #define NOT_ENOUGH_MEMORY     1
31 #define KEY_VALUE_ALREADY_SET 2
32 #define COMMAND_NOT_SUPPORTED 3
33
34 /* Generic global */
35 static BOOL bForce            = FALSE; /* Is set to TRUE when -force is
36                                           passed on the command line */
37
38 /* Globals used by the api setValue, queryValue */
39 static LPSTR currentKeyName   = NULL;
40 static HKEY  currentKeyClass  = 0;
41 static HKEY  currentKeyHandle = 0;
42 static BOOL  bTheKeyIsOpen    = FALSE;
43
44 /* Delimiters used to parse the "value"="data" pair for setValue*/
45 #define SET_VALUE_MAX_ARGS    2 
46 /* Delimiters used to parse the "value" to query queryValue*/
47 #define QUERY_VALUE_MAX_ARGS  1 
48
49 static const char *setValueDelim[SET_VALUE_MAX_ARGS]   = {"=", ""};
50 static const char *queryValueDelim[QUERY_VALUE_MAX_ARGS]   = {""};
51
52
53 /* 
54  * Forward declaration 
55  */
56 typedef void (*commandAPI)(LPSTR lpsLine);
57
58 static void doSetValue(LPSTR lpsLine);
59 static void doDeleteValue(LPSTR lpsLine);
60 static void doCreateKey(LPSTR lpsLine);
61 static void doDeleteKey(LPSTR lpsLine);
62 static void doQueryValue(LPSTR lpsLine);
63 static void doRegisterDLL(LPSTR lpsLine);
64 static void doUnregisterDLL(LPSTR lpsLine);
65
66 /*
67  * Currently supported api
68  */
69 static const char* commandNames[COMMAND_COUNT] = {
70   "setValue", 
71   "deleteValue", 
72   "createKey",
73   "deleteKey",
74   "queryValue",
75   "registerDLL",
76   "unregisterDLL"
77 };
78
79 /*
80  * Pointers to processing entry points
81  */
82 static const commandAPI commandAPIs[COMMAND_COUNT] = {
83   doSetValue, 
84   doDeleteValue, 
85   doCreateKey,
86   doDeleteKey,
87   doQueryValue,
88   doRegisterDLL,
89   doUnregisterDLL
90 };
91
92 /* 
93  * This array controls the registry saving needs at the end of the process 
94  */
95 static const BOOL commandSaveRegistry[COMMAND_COUNT] = {
96   TRUE,
97   TRUE,
98   TRUE,
99   TRUE,
100   FALSE,
101   TRUE,
102   TRUE
103 };
104
105 /* 
106  * Generic prototypes
107  */
108 static DWORD   getDataType(LPSTR *lpValue, DWORD* parse_type);
109 static LPSTR   getRegKeyName(LPSTR lpLine);
110 static HKEY    getRegClass(LPSTR lpLine);
111 static LPSTR   getArg(LPSTR arg);
112 static INT     getCommand(LPSTR commandName);
113 static DWORD   convertHexToDWord(char *str, BYTE *buf);
114 static DWORD   convertHexCSVToHex(char *str, BYTE *buf, ULONG bufLen);
115 static LPSTR   convertHexToHexCSV( BYTE *buf, ULONG len);
116 static LPSTR   convertHexToDWORDStr( BYTE *buf, ULONG len);
117 static HRESULT openKey(LPSTR stdInput);
118 static void    closeKey();
119
120 /* 
121  * api setValue prototypes
122  */
123 static void    processSetValue(LPSTR cmdline);
124 static HRESULT setValue(LPSTR *argv);
125
126 /* 
127  * api queryValue prototypes
128  */
129 static void    processQueryValue(LPSTR cmdline);
130
131 /*
132  * Help Text displayed when invalid parameters are provided
133  */
134 static char helpText[] =
135 "NAME\n"
136 "          regapi - perform certain actions on the wine registry.\n"
137 "\n"
138 "SYNOPSIS\n"
139 "          regapi commandName [-force] < file\n"
140 "\n"
141 "DESCRIPTION\n"
142 "          regapi modifies settings in the wine registry.  It processes\n"
143 "          the given commandName for every line in the stdin data stream.\n"
144 "          Input data format may vary depending on the commandName\n"
145 "          (see INPUT FILE FORMAT).\n"
146 "\n"
147 "OPTIONS\n"
148 "          commandName\n"
149 "              Instruct regapi about what action to perform on the data stream.\n"
150 "              Currently, only setValue and queryValue are supported and\n"
151 "              implemented.\n"
152 "\n"
153 "          -force\n"
154 "              When provided the action will be performed anyway.  This may\n"
155 "              have a different meaning depending on the context.  For example,\n"
156 "              when providing -force to setValue, the value is set even if it\n"
157 "              was previously set to another value.\n"
158 "\n"
159 "          < file\n"
160 "              STDIN channel, provide a file name with line of the appropriate\n"
161 "              format.\n"
162 "\n"
163 "INPUT FILE FORMAT\n"
164 "\n"
165 "          setValue\n"
166 "              The input file format required by the setValue command is similar\n"
167 "              to the one obtained from regedit.exe export option.  The only\n"
168 "              difference is that multi line values are not supported, the\n"
169 "              value data must be on a single line.\n"
170 "\n"
171 "              [KEY_CLASS\\Some\\Path\\For\\A\\Key]\n"
172 "              \"Value1\"=\"Data1\"\n"
173 "              \"Value2\"=\"Data2\"\n"
174 "              \"Valuen\"=\"Datan\"\n"
175 "              ...\n"
176 "\n"
177 "          queryValue\n"
178 "              The input file format required by the queryValue command is\n"
179 "              similar to the one required by setValue.  The only\n"
180 "              difference is that you only provide the value name.\n"
181 "\n"
182 "              [KEY_CLASS\\Some\\Path\\For\\A\\Key]\n"
183 "              \"Value1\"\n"
184 "              \"Value2\"\n"
185 "              \"Valuen\"\n"
186 "              ...\n"
187 "           registerDLL\n"
188 "               The input file format is a list of DLLs to register\n"
189 "\n"
190 "           unregisterDLL\n"
191 "               The input file format is a list of DLLs to unregister\n"
192 "                                February 1999.\n"
193 ;
194
195
196 /******************************************************************************
197  * This function returns the HKEY associated with the data type encoded in the 
198  * value.  It modifies the input parameter (key value) in order to skip this 
199  * "now useless" data type information.
200  *
201  * Note: Updated based on the algorithm used in 'server/registry.c'
202  */
203 DWORD getDataType(LPSTR *lpValue, DWORD* parse_type)
204 {
205     struct data_type { const char *tag; int len; int type; int parse_type; };
206
207     static const struct data_type data_types[] =
208     {                   /* actual type */  /* type to assume for parsing */
209         { "\"",        1,   REG_SZ,              REG_SZ },
210         { "str:\"",    5,   REG_SZ,              REG_SZ },
211         { "str(2):\"", 8,   REG_EXPAND_SZ,       REG_SZ },
212         { "str(7):\"", 8,   REG_MULTI_SZ,        REG_SZ },
213         { "hex:",      4,   REG_BINARY,          REG_BINARY },
214         { "dword:",    6,   REG_DWORD,           REG_DWORD },
215         { "hex(",      4,   -1,                  REG_BINARY },
216         { NULL,        0,    0,                  0 }
217     };
218
219     const struct data_type *ptr;
220     int type;
221
222     for (ptr = data_types; ptr->tag; ptr++)
223     {
224         if (memcmp( ptr->tag, *lpValue, ptr->len ))
225             continue;
226
227         /* Found! */
228         *parse_type = ptr->parse_type;
229         type=ptr->type;
230         *lpValue+=ptr->len;
231         if (type == -1) {
232             char* end;
233             /* "hex(xx):" is special */
234             *lpValue += 4;
235             type = (int)strtoul( *lpValue , &end, 16 );
236             if (**lpValue=='\0' || *end!=')' || *(end+1)!=':') {
237                 type=REG_NONE;
238             } else {
239                 *lpValue=end+2;
240             }
241         }
242         return type;
243     }
244     return (**lpValue=='\0'?REG_SZ:REG_NONE);
245 }
246
247 /******************************************************************************
248  * Extracts from a [HKEY\some\key\path] type of line the key name (what starts 
249  * after the first '\' and end before the ']'
250  */
251 LPSTR getRegKeyName(LPSTR lpLine) 
252 {
253   LPSTR keyNameBeg = NULL;
254   LPSTR keyNameEnd = NULL;
255   char  lpLineCopy[KEY_MAX_LEN];
256
257   if (lpLine == NULL)
258     return NULL;
259
260   strcpy(lpLineCopy, lpLine);
261
262   keyNameBeg  = strstr(lpLineCopy, "\\");  /* The key name start by '\' */
263   keyNameBeg++;                            /* but is not part of the key name */
264   keyNameEnd  = strstr(lpLineCopy, "]");   /* The key name end by ']' */
265   *keyNameEnd = '\0';                      /* Isolate the key name */
266  
267   currentKeyName = HeapAlloc(GetProcessHeap(), 0, strlen(keyNameBeg)+1);
268   if (currentKeyName != NULL)
269     strcpy(currentKeyName, keyNameBeg);
270  
271   return currentKeyName;
272 }
273
274 /******************************************************************************
275  * Extracts from a [HKEY/some/key/path] type of line the key class (what 
276  * starts after the '[' and ends before the first '\'
277  */
278 static HKEY getRegClass(LPSTR lpClass) 
279 {
280   LPSTR classNameEnd;
281   LPSTR classNameBeg;
282
283   char  lpClassCopy[KEY_MAX_LEN];
284   
285   if (lpClass == NULL)
286     return (HKEY)ERROR_INVALID_PARAMETER;
287
288   strcpy(lpClassCopy, lpClass);
289
290   classNameEnd  = strstr(lpClassCopy, "\\");  /* The class name end by '\' */
291   *classNameEnd = '\0';                       /* Isolate the class name */
292   classNameBeg  = &lpClassCopy[1];            /* Skip the '[' */
293   
294   if      (strcmp( classNameBeg, "HKEY_LOCAL_MACHINE") == IDENTICAL )
295     return  HKEY_LOCAL_MACHINE;
296   else if (strcmp( classNameBeg, "HKEY_USERS") == IDENTICAL )
297     return  HKEY_USERS;
298   else if (strcmp( classNameBeg, "HKEY_CLASSES_ROOT") == IDENTICAL )
299     return  HKEY_CLASSES_ROOT;
300   else if (strcmp( classNameBeg, "HKEY_CURRENT_CONFIG") == IDENTICAL )
301     return  HKEY_CURRENT_CONFIG;
302   else if (strcmp( classNameBeg, "HKEY_CURRENT_USER") == IDENTICAL )
303     return  HKEY_CURRENT_USER;
304   else
305     return (HKEY)ERROR_INVALID_PARAMETER;
306 }
307
308 /******************************************************************************
309  * This is a replacement for strsep which is not portable (missing on Solaris).
310  */
311 static char* getToken(char** str, const char* delims)
312 {
313     char* token;
314
315     if (*str==NULL) {
316         /* No more tokens */
317         return NULL;
318     }
319
320     token=*str;
321     while (**str!='\0') {
322         if (strchr(delims,**str)!=NULL) {
323             **str='\0';
324             (*str)++;
325             return token;
326         }
327         (*str)++;
328     }
329     /* There is no other token */
330     *str=NULL;
331     return token;
332 }
333
334 /******************************************************************************
335  * Returns an allocated buffer with a cleaned copy (removed the surrounding 
336  * dbl quotes) of the passed value.
337  */
338 static LPSTR getArg( LPSTR arg)
339 {
340   LPSTR tmp = NULL;
341   ULONG len;
342
343   if (arg == NULL)
344     return NULL;
345
346   /*
347    * Get rid of surrounding quotes
348    */
349   len = strlen(arg); 
350
351   if( arg[len-1] == '\"' ) arg[len-1] = '\0';
352   if( arg[0]     == '\"' ) arg++;
353
354   tmp = HeapAlloc(GetProcessHeap(), 0, strlen(arg)+1);
355   strcpy(tmp, arg);
356
357   return tmp;
358 }
359
360 /******************************************************************************
361  * Returns the index in the commands array of the command to process.
362  */
363 static INT getCommand(LPSTR commandName)
364 {
365   INT count;
366   for (count=0; count < COMMAND_COUNT; count++)
367     if ( strcmp(commandName, commandNames[count]) == IDENTICAL) 
368       return count;
369
370   return COMMAND_NOT_FOUND;
371 }
372
373 /******************************************************************************
374  * Converts a hex representation of a DWORD into a DWORD.
375  */
376 static DWORD convertHexToDWord(char *str, BYTE *buf)
377 {
378   DWORD dw;
379   char xbuf[9];
380
381   memcpy(xbuf,str,8);
382   xbuf[8]='\0';
383   sscanf(xbuf,"%08lx",&dw);
384   memcpy(buf,&dw,sizeof(DWORD));
385   return sizeof(DWORD);
386 }
387
388 /******************************************************************************
389  * Converts a hex buffer into a hex comma separated values 
390  */
391 static char* convertHexToHexCSV(BYTE *buf, ULONG bufLen)
392 {
393   char* str;
394   char* ptrStr;
395   BYTE* ptrBuf;
396
397   ULONG current = 0;
398
399   str    = HeapAlloc(GetProcessHeap(), 0, (bufLen+1)*2);
400   memset(str, 0, (bufLen+1)*2);
401   ptrStr = str;  /* Pointer to result  */
402   ptrBuf = buf;  /* Pointer to current */
403
404   while (current < bufLen)
405   {
406     BYTE bCur = ptrBuf[current++];
407     char res[3];
408
409     sprintf(res, "%02x", (unsigned int)*&bCur);
410     strcat(str, res);
411     strcat(str, ",");
412   }                                   
413
414   /* Get rid of the last comma */
415   str[strlen(str)-1] = '\0';
416   return str;
417 }
418
419 /******************************************************************************
420  * Converts a hex buffer into a DWORD string
421  */
422 static char* convertHexToDWORDStr(BYTE *buf, ULONG bufLen)
423 {
424   char* str;
425   DWORD dw;
426
427   if ( bufLen != sizeof(DWORD) ) return NULL;
428
429   str = HeapAlloc(GetProcessHeap(), 0, (bufLen*2)+1);
430
431   memcpy(&dw,buf,sizeof(DWORD));
432   sprintf(str, "%08lx", dw);
433
434   /* Get rid of the last comma */
435   return str;
436 }
437 /******************************************************************************
438  * Converts a hex comma separated values list into a hex list.
439  */
440 static DWORD convertHexCSVToHex(char *str, BYTE *buf, ULONG bufLen)
441 {
442   char *s = str;  /* Pointer to current */
443   char *b = buf;  /* Pointer to result  */
444
445   ULONG strLen    = strlen(str);
446   ULONG strPos    = 0;
447   DWORD byteCount = 0;
448
449   memset(buf, 0, bufLen);
450
451   /*
452    * warn the user if we are here with a string longer than 2 bytes that does
453    * not contains ",".  It is more likely because the data is invalid.
454    */
455   if ( ( strlen(str) > 2) && ( strstr(str, ",") == NULL) )
456     printf("regapi: WARNING converting CSV hex stream with no comma, "
457            "input data seems invalid.\n");
458
459   while (strPos < strLen)
460   {
461     char xbuf[3];
462     char wc;
463
464     memcpy(xbuf,s,2); xbuf[3]='\0';
465     sscanf(xbuf,"%02x",(UINT*)&wc);
466     *b++ =(unsigned char)wc;
467
468     s+=3;
469     strPos+=3;
470     byteCount++;
471   }                                   
472
473   return byteCount;
474 }
475
476
477 /******************************************************************************
478  * Sets the value in argv[0] to the data in argv[1] for the currently 
479  * opened key.
480  */
481 static HRESULT setValue(LPSTR *argv)
482 {
483   HRESULT hRes;
484   DWORD   dwSize          = KEY_MAX_LEN;
485   DWORD   dwType          = 0;
486   DWORD   dwDataType,dwParseType;
487
488   LPSTR   lpsCurrentValue;
489
490   LPSTR   keyValue = getArg(argv[0]);
491   LPSTR   keyData  = argv[1];
492
493   /* Make some checks */
494   if ( (keyValue == NULL) || (keyData == NULL) )
495     return ERROR_INVALID_PARAMETER;
496
497   lpsCurrentValue=HeapAlloc(GetProcessHeap(), 0,KEY_MAX_LEN);
498   /*
499    * Default registry values are encoded in the input stream as '@' but as 
500    * blank in the wine registry.
501    */
502   if( (keyValue[0] == '@') && (strlen(keyValue) == 1) )
503     keyValue[0] = '\0';
504
505   /* Get the data type stored into the value field */
506   dwDataType = getDataType(&keyData,&dwParseType);
507
508   memset(lpsCurrentValue, 0, KEY_MAX_LEN);
509   hRes = RegQueryValueExA(
510           currentKeyHandle, 
511           keyValue, 
512           NULL, 
513           &dwType, 
514           (LPBYTE)lpsCurrentValue, 
515           &dwSize);
516
517   while(hRes==ERROR_MORE_DATA){
518       dwSize+=KEY_MAX_LEN;
519       lpsCurrentValue=HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,lpsCurrentValue,dwSize);
520       hRes = RegQueryValueExA(currentKeyHandle,keyValue,NULL,&dwType,(LPBYTE)lpsCurrentValue,&dwSize);
521   }
522
523   if( ( strlen(lpsCurrentValue) == 0 ) ||  /* The value is not existing */
524       ( bForce ))         /* -force option */ 
525   { 
526     LPBYTE lpbData;
527     BYTE   convert[KEY_MAX_LEN];
528     DWORD  dwLen;
529
530     if ( dwParseType == REG_SZ)        /* no conversion for string */
531     {
532       dwLen   = strlen(keyData);
533       if (dwLen>0 && keyData[dwLen-1]=='"')
534       {
535         dwLen--;
536         keyData[dwLen]='\0';
537       }
538       lpbData = keyData;
539     } 
540     else if (dwParseType == REG_DWORD)  /* Convert the dword types */
541     {
542       dwLen   = convertHexToDWord(keyData, convert);
543       lpbData = convert;
544     }
545     else                               /* Convert the hexadecimal types */
546     {
547       dwLen   = convertHexCSVToHex(keyData, convert, KEY_MAX_LEN);
548       lpbData = convert;
549     }
550
551     hRes = RegSetValueEx(
552             currentKeyHandle, 
553             keyValue,
554             0,                  /* Reserved */
555             dwDataType,
556             lpbData,
557             dwLen);
558   }
559   else
560   {
561     /* return the current value data into argv[1] */
562     if (argv[1] != NULL)
563     {
564       HeapFree(GetProcessHeap(), 0, argv[1]);
565       argv[1] = HeapAlloc(GetProcessHeap(), 0, dwSize+1);
566
567       if ( argv[1] != NULL ) {
568         strncpy(argv[1], lpsCurrentValue, dwSize);
569         argv[1][dwSize]='\0';
570       }
571     }
572
573     hRes=KEY_VALUE_ALREADY_SET;
574   }
575   if (keyValue != NULL)
576       HeapFree(GetProcessHeap(), 0, keyValue);
577   return hRes;
578 }
579
580
581 /******************************************************************************
582  * Open the key
583  */
584 static HRESULT openKey( LPSTR stdInput)
585 {
586   DWORD   dwDisp;  
587   HRESULT hRes;
588
589   /* Sanity checks */
590   if (stdInput == NULL) 
591     return ERROR_INVALID_PARAMETER;
592
593   /* Get the registry class */
594   currentKeyClass = getRegClass(stdInput); /* Sets global variable */
595   if (currentKeyClass == (HKEY)ERROR_INVALID_PARAMETER)
596     return (HRESULT)ERROR_INVALID_PARAMETER;
597
598   /* Get the key name */
599   currentKeyName = getRegKeyName(stdInput); /* Sets global variable */
600   if (currentKeyName == NULL)
601     return ERROR_INVALID_PARAMETER;
602     
603   hRes = RegCreateKeyEx( 
604           currentKeyClass,          /* Class     */
605           currentKeyName,           /* Sub Key   */
606           0,                        /* MUST BE 0 */
607           NULL,                     /* object type */
608           REG_OPTION_NON_VOLATILE,  /* option, REG_OPTION_NON_VOLATILE ... */
609           KEY_ALL_ACCESS,           /* access mask, KEY_ALL_ACCESS */
610           NULL,                     /* security attribute */
611           &currentKeyHandle,        /* result */
612           &dwDisp);                 /* disposition, REG_CREATED_NEW_KEY or
613                                                     REG_OPENED_EXISTING_KEY */
614
615   if (hRes == ERROR_SUCCESS)
616     bTheKeyIsOpen = TRUE;
617
618   return hRes;
619
620 }
621 /******************************************************************************
622  * This function is a wrapper for the setValue function.  It prepares the 
623  * land and clean the area once completed.
624  */
625 static void processSetValue(LPSTR cmdline)
626 {
627   LPSTR argv[SET_VALUE_MAX_ARGS];  /* args storage    */
628
629   LPSTR token         = NULL;      /* current token analized */
630   ULONG argCounter    = 0;         /* counter of args */
631   INT   counter;
632   HRESULT hRes = 0;
633
634   /*
635    * Init storage and parse the line
636    */
637   for (counter=0; counter<SET_VALUE_MAX_ARGS; counter++)
638     argv[counter]=NULL;
639
640   while( (token = getToken(&cmdline, setValueDelim[argCounter])) != NULL ) 
641   {
642     argv[argCounter++] = token;
643
644     if (argCounter == SET_VALUE_MAX_ARGS)
645       break;  /* Stop processing args no matter what */
646   }
647
648   hRes = setValue(argv);
649   if ( hRes == ERROR_SUCCESS ) 
650     printf(
651       "regapi: Value \"%s\" has been set to \"%s\" in key [%s]\n", 
652       argv[0], 
653       argv[1],
654       currentKeyName);
655
656   else if ( hRes == KEY_VALUE_ALREADY_SET ) 
657     printf(
658       "regapi: Value \"%s\" already set to \"%s\" in key [%s]\n", 
659       argv[0], 
660       argv[1], 
661       currentKeyName);
662
663   else
664     printf("regapi: ERROR Key %s not created. Value: %s, Data: %s\n",
665       currentKeyName,
666       argv[0], 
667       argv[1]);
668 }
669
670 /******************************************************************************
671  * This function is a wrapper for the queryValue function.  It prepares the 
672  * land and clean the area once completed.
673  */
674 static void processQueryValue(LPSTR cmdline)
675 {
676   LPSTR   argv[QUERY_VALUE_MAX_ARGS];/* args storage    */
677   LPSTR   token      = NULL;         /* current token analized */
678   ULONG   argCounter = 0;            /* counter of args */
679   INT     counter;
680   HRESULT hRes       = 0;
681   LPSTR   keyValue   = NULL;
682   LPSTR   lpsRes     = NULL;
683
684   /*
685    * Init storage and parse the line
686    */
687   for (counter=0; counter<QUERY_VALUE_MAX_ARGS; counter++)
688     argv[counter]=NULL;
689
690   while( (token = getToken(&cmdline, queryValueDelim[argCounter])) != NULL ) 
691   {
692     argv[argCounter++] = getArg(token);
693
694     if (argCounter == QUERY_VALUE_MAX_ARGS)
695       break;  /* Stop processing args no matter what */
696   }
697
698   /* The value we look for is the first token on the line */
699   if ( argv[0] == NULL )
700     return; /* SHOULD NOT HAPPEN */
701   else
702     keyValue = argv[0]; 
703
704   if( (keyValue[0] == '@') && (strlen(keyValue) == 1) ) 
705   {
706     LONG  lLen  = KEY_MAX_LEN;
707     CHAR*  lpsData=HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,KEY_MAX_LEN);
708     /* 
709      * We need to query the key default value
710      */
711     hRes = RegQueryValue(
712              currentKeyHandle, 
713              currentKeyName, 
714              (LPBYTE)lpsData,
715              &lLen);
716
717     if (hRes==ERROR_MORE_DATA) {
718         lpsData=HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,lpsData,lLen);
719         hRes = RegQueryValue(currentKeyHandle,currentKeyName,(LPBYTE)lpsData,&lLen);
720     }
721
722     if (hRes == ERROR_SUCCESS)
723     {
724       lpsRes = HeapAlloc( GetProcessHeap(), 0, lLen);
725       strncpy(lpsRes, lpsData, lLen);
726       lpsRes[lLen-1]='\0';
727     }
728   }
729   else 
730   {
731     DWORD  dwLen  = KEY_MAX_LEN;
732     BYTE*  lpbData=HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,KEY_MAX_LEN);
733     DWORD  dwType;
734     /* 
735      * We need to query a specific value for the key
736      */
737     hRes = RegQueryValueEx(
738              currentKeyHandle, 
739              keyValue, 
740              0, 
741              &dwType, 
742              (LPBYTE)lpbData, 
743              &dwLen);
744
745     if (hRes==ERROR_MORE_DATA) {
746         lpbData=HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,lpbData,dwLen);
747         hRes = RegQueryValueEx(currentKeyHandle,keyValue,NULL,&dwType,(LPBYTE)lpbData,&dwLen);
748     }
749
750     if (hRes == ERROR_SUCCESS)
751     {
752       /* 
753        * Convert the returned data to a displayable format
754        */
755       switch ( dwType )
756       {
757         case REG_SZ:
758         case REG_EXPAND_SZ:
759         {
760           lpsRes = HeapAlloc( GetProcessHeap(), 0, dwLen);
761           strncpy(lpsRes, lpbData, dwLen);
762           lpsRes[dwLen-1]='\0';
763           break;
764         }
765         case REG_DWORD:
766         {
767           lpsRes = convertHexToDWORDStr(lpbData, dwLen);
768           break;
769         }
770         default:
771         {
772           lpsRes = convertHexToHexCSV(lpbData, dwLen);
773           break;
774         }
775       } 
776     }
777
778     HeapFree(GetProcessHeap(), 0, lpbData);
779   }
780  
781  
782   if ( hRes == ERROR_SUCCESS ) 
783     printf(
784       "regapi: Value \"%s\" = \"%s\" in key [%s]\n", 
785       keyValue, 
786       lpsRes,
787       currentKeyName);
788
789   else
790     printf("regapi: ERROR Value \"%s\" not found. for key \"%s\"\n",
791       keyValue, 
792       currentKeyName);
793     
794   /*
795    * Do some cleanup
796    */
797   for (counter=0; counter<argCounter; counter++)
798     if (argv[counter] != NULL)
799       HeapFree(GetProcessHeap(), 0, argv[counter]);
800
801   if (lpsRes != NULL)
802     HeapFree(GetProcessHeap(), 0, lpsRes);
803
804 }
805
806 /******************************************************************************
807  * Close the currently opened key.
808  */
809 static void closeKey()
810 {
811   RegCloseKey(currentKeyHandle); 
812
813   HeapFree(GetProcessHeap(), 0, currentKeyName); /* Allocated by getKeyName */
814
815   bTheKeyIsOpen    = FALSE;
816
817   currentKeyName   = NULL;
818   currentKeyClass  = 0;
819   currentKeyHandle = 0;
820 }
821
822 /******************************************************************************
823  * This funtion is the main entry point to the setValue type of action.  It 
824  * receives the currently read line and dispatch the work depending on the 
825  * context.
826  */
827 static void doSetValue(LPSTR stdInput)
828 {
829   /* 
830    * We encoutered the end of the file, make sure we 
831    * close the opened key and exit
832    */ 
833   if (stdInput == NULL)             
834   {
835     if (bTheKeyIsOpen != FALSE) 
836       closeKey();                    
837
838     return;
839   }
840     
841   if      ( stdInput[0] == '[')      /* We are reading a new key */
842   {
843     if ( bTheKeyIsOpen != FALSE )      
844       closeKey();                    /* Close the previous key before */
845
846     if ( openKey(stdInput) != ERROR_SUCCESS )
847       printf ("regapi: doSetValue failed to open key %s\n", stdInput);
848   }
849   else if( ( bTheKeyIsOpen ) && 
850            (( stdInput[0] == '@') || /* reading a default @=data pair */
851             ( stdInput[0] == '\"'))) /* reading a new value=data pair */
852   {
853     processSetValue(stdInput);
854   }
855   else                               /* since we are assuming that the */
856   {                                  /* file format is valid we must   */ 
857     if ( bTheKeyIsOpen )             /* be reading a blank line which  */
858       closeKey();                    /* indicate end of this key processing */ 
859   }
860 }
861
862 /******************************************************************************
863  * This funtion is the main entry point to the queryValue type of action.  It 
864  * receives the currently read line and dispatch the work depending on the 
865  * context.
866  */
867 static void doQueryValue(LPSTR stdInput) { 
868   /* 
869    * We encoutered the end of the file, make sure we 
870    * close the opened key and exit
871    */ 
872   if (stdInput == NULL)             
873   {
874     if (bTheKeyIsOpen != FALSE) 
875       closeKey();                    
876
877     return;
878   }
879     
880   if      ( stdInput[0] == '[')      /* We are reading a new key */
881   {
882     if ( bTheKeyIsOpen != FALSE )      
883       closeKey();                    /* Close the previous key before */
884
885     if ( openKey(stdInput) != ERROR_SUCCESS )
886       printf ("regapi: doSetValue failed to open key %s\n", stdInput);
887   }
888   else if( ( bTheKeyIsOpen ) && 
889            (( stdInput[0] == '@') || /* reading a default @=data pair */
890             ( stdInput[0] == '\"'))) /* reading a new value=data pair */
891   {
892     processQueryValue(stdInput);
893   }
894   else                               /* since we are assuming that the */
895   {                                  /* file format is valid we must   */ 
896     if ( bTheKeyIsOpen )             /* be reading a blank line which  */
897       closeKey();                    /* indicate end of this key processing */ 
898   }
899 }
900
901 /******************************************************************************
902  * This funtion is the main entry point to the deletetValue type of action.  It 
903  * receives the currently read line and dispatch the work depending on the 
904  * context.
905  */
906 static void doDeleteValue(LPSTR line) { 
907   printf ("regapi: deleteValue not yet implemented\n");
908 }
909 /******************************************************************************
910  * This funtion is the main entry point to the deleteKey type of action.  It 
911  * receives the currently read line and dispatch the work depending on the 
912  * context.
913  */
914 static void doDeleteKey(LPSTR line)   { 
915   printf ("regapi: deleteKey not yet implemented\n");
916 }
917 /******************************************************************************
918  * This funtion is the main entry point to the createKey type of action.  It 
919  * receives the currently read line and dispatch the work depending on the 
920  * context.
921  */
922 static void doCreateKey(LPSTR line)   { 
923   printf ("regapi: createKey not yet implemented\n");
924 }
925
926 /******************************************************************************
927  * This funtion is the main entry point to the registerDLL action.  It 
928  * receives the currently read line, then loads and registers the requested DLLs
929  */
930 static void doRegisterDLL(LPSTR stdInput) { 
931   HMODULE theLib = 0;
932   UINT retVal = 0;
933
934   /* Check for valid input */
935   if (stdInput == NULL)             
936     return;
937
938   /* Load and register the library, then free it */
939   theLib = LoadLibrary(stdInput);
940   if (theLib)
941   {
942     FARPROC lpfnDLLRegProc = GetProcAddress(theLib, "DllRegisterServer");
943     if (lpfnDLLRegProc)
944       retVal = (*lpfnDLLRegProc)();
945     else
946       printf("regapi: Couldn't find DllRegisterServer proc in '%s'.\n", stdInput);
947
948     if (retVal != S_OK)
949       printf("regapi: DLLRegisterServer error 0x%x in '%s'.\n", retVal, stdInput);      
950
951     FreeLibrary(theLib);
952   }
953   else
954   {
955     printf("regapi: Could not load DLL '%s'.\n", stdInput); 
956   }
957 }
958
959 /******************************************************************************
960  * This funtion is the main entry point to the unregisterDLL action.  It 
961  * receives the currently read line, then loads and unregisters the requested DLLs
962  */
963 static void doUnregisterDLL(LPSTR stdInput) { 
964   HMODULE theLib = 0;
965   UINT retVal = 0;
966
967   /* Check for valid input */
968   if (stdInput == NULL)             
969     return;
970
971   /* Load and unregister the library, then free it */
972   theLib = LoadLibrary(stdInput);
973   if (theLib)
974   {
975     FARPROC lpfnDLLRegProc = GetProcAddress(theLib, "DllUnregisterServer");
976     if (lpfnDLLRegProc)
977       retVal = (*lpfnDLLRegProc)();
978     else
979       printf("regapi: Couldn't find DllUnregisterServer proc in '%s'.\n", stdInput);
980
981     if (retVal != S_OK)
982       printf("regapi: DLLUnregisterServer error 0x%x in '%s'.\n", retVal, stdInput);      
983
984     FreeLibrary(theLib);
985   }
986   else
987   {
988     printf("regapi: Could not load DLL '%s'.\n", stdInput); 
989   }
990 }
991
992 /******************************************************************************
993  * MAIN - WinMain simply validates the first parameter (command to perform)
994  *        It then reads the STDIN line by line forwarding their processing
995  *        to the appropriate method.
996  */
997 int PASCAL WinMain (HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
998 {
999   LPSTR  token          = NULL;  /* current token analized */
1000   LPSTR  stdInput       = NULL;  /* line read from stdin */
1001   INT    cmdIndex       = -1;    /* index of the command in array */
1002   LPSTR nextLine        = NULL;
1003   ULONG  currentSize    = STDIN_MAX_LEN;
1004
1005   stdInput = HeapAlloc(GetProcessHeap(), 0, STDIN_MAX_LEN); 
1006   nextLine = HeapAlloc(GetProcessHeap(), 0, STDIN_MAX_LEN);
1007
1008   if (stdInput == NULL || nextLine== NULL)
1009     return NOT_ENOUGH_MEMORY;
1010
1011   /*
1012    * get the command, should be the first arg (modify cmdLine)
1013    */ 
1014   token = getToken(&cmdline, " "); 
1015   if (token != NULL) 
1016   {
1017     cmdIndex = getCommand(token);
1018     if (cmdIndex == COMMAND_NOT_FOUND)
1019     {
1020       printf("regapi: Command \"%s\" is not supported.\n", token);
1021       printf(helpText);
1022       return COMMAND_NOT_SUPPORTED;
1023     }
1024   }    
1025   else
1026   {
1027     printf(
1028       "regapi: The first item on the command line must be the command name.\n");
1029     printf(helpText);
1030     return COMMAND_NOT_SUPPORTED;
1031   }
1032
1033   /* 
1034    * check to see wether we force the action 
1035    * (meaning differs depending on the command performed)
1036    */
1037   if ( cmdline != NULL ) /* will be NULL if '-force' is not provided */
1038     if ( strstr(cmdline, "-force") != NULL )
1039       bForce = TRUE;
1040
1041   printf("Processing stdin...\n");
1042
1043   while ( TRUE )
1044   {
1045     /* 
1046      * read a line
1047      */
1048       ULONG curSize=STDIN_MAX_LEN;
1049       char* s=NULL;
1050    
1051       while((NULL!=(stdInput=fgets(stdInput,curSize,stdin))) && (NULL==(s=strchr(stdInput,'\n'))) ){
1052           fseek(stdin,-curSize,SEEK_CUR+1);
1053           stdInput=HeapReAlloc(GetProcessHeap(), 0,stdInput,curSize+=STDIN_MAX_LEN);
1054       }
1055     /* 
1056      * Make some handy generic stuff here... 
1057      */
1058     if ( stdInput != NULL )
1059     {
1060       stdInput[strlen(stdInput) -1] = '\0'; /* get rid of new line */
1061
1062       if( stdInput[0] == '#' )              /* this is a comment, skip */
1063         continue;
1064
1065       while( stdInput[strlen(stdInput) -1] == '\\' ){ /* a '\' char in the end of the current line means  */
1066                                                       /* that this line is not complete and we have to get */
1067           stdInput[strlen(stdInput) -1]= '\0';        /* the rest in the next lines */
1068
1069           nextLine = fgets(nextLine, STDIN_MAX_LEN, stdin);
1070
1071           nextLine[strlen(nextLine)-1] = '\0';
1072
1073           if ( (strlen(stdInput)+strlen(nextLine)) > currentSize){
1074
1075               stdInput=HeapReAlloc(GetProcessHeap(),0,stdInput,strlen(stdInput)+STDIN_MAX_LEN);
1076
1077               currentSize+=STDIN_MAX_LEN;
1078     }
1079
1080           strcat(stdInput,nextLine+2);
1081       }
1082     }
1083
1084     /* 
1085      * We process every lines even the NULL (last) line, to indicate the 
1086      * end of the processing to the specific process.
1087      */
1088     commandAPIs[cmdIndex](stdInput);       
1089
1090     if (stdInput == NULL)  /* EOF encountered */
1091       break;
1092   }
1093
1094 #if 0 
1095   /* 
1096    * Save the registry only if it was modified 
1097    */
1098  if ( commandSaveRegistry[cmdIndex] != FALSE )
1099        SHELL_SaveRegistry();
1100 #endif
1101   HeapFree(GetProcessHeap(), 0, nextLine);
1102
1103   HeapFree(GetProcessHeap(), 0, stdInput);
1104
1105   return SUCCESS;
1106 }