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