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