2 * Command line Registry implementation
4 * Copyright 1999 Sylvain St-Germain
6 * Note: Please consult the README file for more information.
19 /******************************************************************************
23 #define COMMAND_COUNT 5
25 #define KEY_MAX_LEN 1024
26 #define STDIN_MAX_LEN 2048
29 #define COMMAND_NOT_FOUND -1
31 #define NOT_ENOUGH_MEMORY 1
32 #define KEY_VALUE_ALREADY_SET 2
33 #define COMMAND_NOT_SUPPORTED 3
36 static BOOL bForce = FALSE; /* Is set to TRUE when -force is
37 passed on the command line */
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;
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
50 static const char *setValueDelim[SET_VALUE_MAX_ARGS] = {"=", ""};
51 static const char *queryValueDelim[QUERY_VALUE_MAX_ARGS] = {""};
53 /* Array used to extract the data type from a string in getDataType. */
54 typedef struct tagDataTypeMap
60 static const dataTypeMap typeMap[] =
62 {"hex:", REG_BINARY},/* could be REG_NONE (?) */
63 {"dword:", REG_DWORD},
64 {"hex(0):", REG_NONE},
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}
87 const static int LAST_TYPE_MAP = sizeof(typeMap)/sizeof(dataTypeMap);
93 typedef void (*commandAPI)(LPSTR lpsLine);
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);
102 * current supported api
104 static const char* commandNames[COMMAND_COUNT] = {
113 * Pointers to processing entry points
115 static const commandAPI commandAPIs[COMMAND_COUNT] = {
124 * This array controls the registry saving needs at the end of the process
126 static const BOOL commandSaveRegistry[COMMAND_COUNT] = {
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();
150 * api setValue prototypes
152 static void processSetValue(LPSTR cmdline);
153 static HRESULT setValue(LPSTR *argv);
156 * api queryValue prototypes
158 static void processQueryValue(LPSTR cmdline);
161 * Help Text displayed when invalid parameters are provided
163 static char helpText[] = "
165 regapi - provide a command line interface to the wine registry.
168 regapi commandName [-force] < file
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.
177 Instruct regapi about what action to perform on the data stream.
178 Currently, only setValue and queryValue are supported and
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.
188 STDIN channel, provide a file name with line of the appropriate
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.
199 [KEY_CLASS\\Some\\Path\\For\\A\\Key]
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.
210 [KEY_CLASS\\Some\\Path\\For\\A\\Key]
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.
224 HKEY getDataType(LPSTR *lpValue)
227 DWORD dwReturn = REG_SZ;
229 for (; counter < LAST_TYPE_MAP; counter++)
231 LONG len = strlen(typeMap[counter].mask);
232 if ( strncasecmp( *lpValue, typeMap[counter].mask, len) == IDENTICAL)
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.
239 dwReturn = typeMap[counter].dataType;
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 ']'
250 LPSTR getRegKeyName(LPSTR lpLine)
252 LPSTR keyNameBeg = NULL;
253 LPSTR keyNameEnd = NULL;
254 char lpLineCopy[KEY_MAX_LEN];
259 strcpy(lpLineCopy, lpLine);
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 */
266 currentKeyName = HeapAlloc(GetProcessHeap(), 0, strlen(keyNameBeg)+1);
267 if (currentKeyName != NULL)
268 strcpy(currentKeyName, keyNameBeg);
270 return currentKeyName;
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 '\'
277 static HKEY getRegClass(LPSTR lpClass)
282 char lpClassCopy[KEY_MAX_LEN];
285 return ERROR_INVALID_PARAMETER;
287 strcpy(lpClassCopy, lpClass);
289 classNameEnd = strstr(lpClassCopy, "\\"); /* The class name end by '\' */
290 *classNameEnd = '\0'; /* Isolate the class name */
291 classNameBeg = &lpClassCopy[1]; /* Skip the '[' */
293 if (strcmp( classNameBeg, "HKEY_LOCAL_MACHINE") == IDENTICAL )
294 return HKEY_LOCAL_MACHINE;
295 else if (strcmp( classNameBeg, "HKEY_USERS") == IDENTICAL )
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;
304 return ERROR_INVALID_PARAMETER;
307 /******************************************************************************
308 * Returns an allocated buffer with a cleaned copy (removed the surrounding
309 * dbl quotes) of the passed value.
311 static LPSTR getArg( LPSTR arg)
320 * Get rid of surrounding quotes
324 if( arg[len-1] == '\"' ) arg[len-1] = '\0';
325 if( arg[0] == '\"' ) arg++;
327 tmp = HeapAlloc(GetProcessHeap(), 0, strlen(arg)+1);
333 /******************************************************************************
334 * Returns the index in the commands array of the command to process.
336 static INT getCommand(LPSTR commandName)
339 for (count=0; count < COMMAND_COUNT; count++)
340 if ( strcmp(commandName, commandNames[count]) == IDENTICAL)
343 return COMMAND_NOT_FOUND;
346 /******************************************************************************
347 * Converts a hex representation of a DWORD into a DWORD.
349 static DWORD convertHexToDWord(char *str, BYTE *buf)
351 char *s = str; /* Pointer to current */
352 char *b = buf; /* Pointer to result */
357 while (strPos < 4) /* 8 byte in a DWORD */
362 memcpy(xbuf,s,2); xbuf[2]='\0';
363 sscanf(xbuf,"%02x",(UINT*)&wc);
364 *b++ =(unsigned char)wc;
370 return 4; /* always 4 byte for the word */
373 /******************************************************************************
374 * Converts a hex buffer into a hex comma separated values
376 static char* convertHexToHexCSV(BYTE *buf, ULONG bufLen)
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 */
389 while (current < bufLen)
391 BYTE bCur = ptrBuf[current++];
394 sprintf(res, "%02x", (unsigned int)*&bCur);
399 /* Get rid of the last comma */
400 str[strlen(str)-1] = '\0';
404 /******************************************************************************
405 * Converts a hex buffer into a DWORD string
407 static char* convertHexToDWORDStr(BYTE *buf, ULONG bufLen)
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 */
420 while (current < bufLen)
422 BYTE bCur = ptrBuf[current++];
425 sprintf(res, "%02x", (unsigned int)*&bCur);
429 /* Get rid of the last comma */
432 /******************************************************************************
433 * Converts a hex comma separated values list into a hex list.
435 static DWORD convertHexCSVToHex(char *str, BYTE *buf, ULONG bufLen)
437 char *s = str; /* Pointer to current */
438 char *b = buf; /* Pointer to result */
440 ULONG strLen = strlen(str);
444 memset(buf, 0, bufLen);
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.
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");
454 while (strPos < strLen)
459 memcpy(xbuf,s,2); xbuf[3]='\0';
460 sscanf(xbuf,"%02x",(UINT*)&wc);
461 *b++ =(unsigned char)wc;
472 /******************************************************************************
473 * Sets the value in argv[0] to the data in argv[1] for the currently
476 static HRESULT setValue(LPSTR *argv)
479 DWORD dwSize = KEY_MAX_LEN;
483 LPSTR lpsCurrentValue;
485 LPSTR keyValue = argv[0];
486 LPSTR keyData = argv[1];
488 /* Make some checks */
489 if ( (keyValue == NULL) || (keyData == NULL) )
490 return ERROR_INVALID_PARAMETER;
492 lpsCurrentValue=HeapAlloc(GetProcessHeap(), 0,KEY_MAX_LEN);
494 * Default registry values are encoded in the input stream as '@' but as
495 * blank in the wine registry.
497 if( (keyValue[0] == '@') && (strlen(keyValue) == 1) )
500 /* Get the data type stored into the value field */
501 dwDataType = getDataType(&keyData);
503 memset(lpsCurrentValue, 0, KEY_MAX_LEN);
504 hRes = RegQueryValueExA(
509 (LPBYTE)lpsCurrentValue,
512 while(hRes==ERROR_MORE_DATA){
514 lpsCurrentValue=HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,lpsCurrentValue,dwSize);
515 hRes = RegQueryValueExA(currentKeyHandle,keyValue,NULL,&dwType,(LPBYTE)lpsCurrentValue,&dwSize);
518 if( ( strlen(lpsCurrentValue) == 0 ) || /* The value is not existing */
519 ( bForce )) /* -force option */
522 BYTE convert[KEY_MAX_LEN];
525 if ( dwDataType == REG_SZ ) /* no convertion for string */
527 dwLen = strlen(keyData);
530 else if (dwDataType == REG_DWORD) /* Convert the dword types */
532 dwLen = convertHexToDWord(keyData, convert);
535 else /* Convert the hexadecimal types */
537 dwLen = convertHexCSVToHex(keyData, convert, KEY_MAX_LEN);
541 hRes = RegSetValueEx(
551 /* return the current value data into argv[1] */
554 HeapFree(GetProcessHeap(), 0, argv[1]);
555 argv[1] = HeapAlloc(GetProcessHeap(), 0, dwSize+1);
557 if ( argv[1] != NULL ) {
558 strncpy(argv[1], lpsCurrentValue, dwSize);
559 argv[1][dwSize]='\0';
563 return KEY_VALUE_ALREADY_SET;
569 /******************************************************************************
572 static HRESULT openKey( LPSTR stdInput)
578 if (stdInput == NULL)
579 return ERROR_INVALID_PARAMETER;
581 /* Get the registry class */
582 currentKeyClass = getRegClass(stdInput); /* Sets global variable */
583 if (currentKeyClass == ERROR_INVALID_PARAMETER)
584 return ERROR_INVALID_PARAMETER;
586 /* Get the key name */
587 currentKeyName = getRegKeyName(stdInput); /* Sets global variable */
588 if (currentKeyName == NULL)
589 return ERROR_INVALID_PARAMETER;
591 hRes = RegCreateKeyEx(
592 currentKeyClass, /* Class */
593 currentKeyName, /* Sub Key */
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 ¤tKeyHandle, /* result */
600 &dwDisp); /* disposition, REG_CREATED_NEW_KEY or
601 REG_OPENED_EXISTING_KEY */
603 if (hRes == ERROR_SUCCESS)
604 bTheKeyIsOpen = TRUE;
609 /******************************************************************************
610 * This function is a wrapper arround the setValue function. It prepares the
611 * land and clean the area once completed.
613 static void processSetValue(LPSTR cmdline)
615 LPSTR argv[SET_VALUE_MAX_ARGS]; /* args storage */
617 LPSTR token = NULL; /* current token analized */
618 ULONG argCounter = 0; /* counter of args */
623 * Init storage and parse the line
625 for (counter=0; counter<SET_VALUE_MAX_ARGS; counter++)
628 while( (token = strsep(&cmdline, setValueDelim[argCounter])) != NULL )
630 argv[argCounter++] = getArg(token);
632 if (argCounter == SET_VALUE_MAX_ARGS)
633 break; /* Stop processing args no matter what */
636 hRes = setValue(argv);
637 if ( hRes == ERROR_SUCCESS )
639 "regapi: Value \"%s\" has been set to \"%s\" in key [%s]\n",
644 else if ( hRes == KEY_VALUE_ALREADY_SET )
646 "regapi: Value \"%s\" already set to \"%s\" in key [%s]\n",
652 printf("regapi: ERROR Key %s not created. Value: %s, Data: %s\n",
660 for (counter=0; counter<argCounter; counter++)
661 if (argv[counter] != NULL)
662 HeapFree(GetProcessHeap(), 0, argv[counter]);
665 /******************************************************************************
666 * This function is a wrapper arround the queryValue function. It prepares the
667 * land and clean the area once completed.
669 static void processQueryValue(LPSTR cmdline)
671 LPSTR argv[QUERY_VALUE_MAX_ARGS];/* args storage */
672 LPSTR token = NULL; /* current token analized */
673 ULONG argCounter = 0; /* counter of args */
676 LPSTR keyValue = NULL;
680 * Init storage and parse the line
682 for (counter=0; counter<QUERY_VALUE_MAX_ARGS; counter++)
685 while( (token = strsep(&cmdline, queryValueDelim[argCounter])) != NULL )
687 argv[argCounter++] = getArg(token);
689 if (argCounter == QUERY_VALUE_MAX_ARGS)
690 break; /* Stop processing args no matter what */
693 /* The value we look for is the first token on the line */
694 if ( argv[0] == NULL )
695 return; /* SHOULD NOT OCCURS */
699 if( (keyValue[0] == '@') && (strlen(keyValue) == 1) )
701 LONG lLen = KEY_MAX_LEN;
702 CHAR* lpsData=HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,KEY_MAX_LEN);
704 * We need to query the key default value
706 hRes = RegQueryValue(
712 if (hRes==ERROR_MORE_DATA) {
713 lpsData=HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,lpsData,lLen);
714 hRes = RegQueryValue(currentKeyHandle,currentKeyName,(LPBYTE)lpsData,&lLen);
717 if (hRes == ERROR_SUCCESS)
719 lpsRes = HeapAlloc( GetProcessHeap(), 0, lLen);
720 strncpy(lpsRes, lpsData, lLen);
726 DWORD dwLen = KEY_MAX_LEN;
727 BYTE* lpbData=HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,KEY_MAX_LEN);
730 * We need to query a specific value for the key
732 hRes = RegQueryValueEx(
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);
745 if (hRes == ERROR_SUCCESS)
748 * Convert the returned data to a displayable format
755 lpsRes = HeapAlloc( GetProcessHeap(), 0, dwLen);
756 strncpy(lpsRes, lpbData, dwLen);
757 lpsRes[dwLen-1]='\0';
762 lpsRes = convertHexToDWORDStr(lpbData, dwLen);
767 lpsRes = convertHexToHexCSV(lpbData, dwLen);
773 HeapFree(GetProcessHeap(), 0, lpbData);
777 if ( hRes == ERROR_SUCCESS )
779 "regapi: Value \"%s\" = \"%s\" in key [%s]\n",
785 printf("regapi: ERROR Value \"%s\" not found. for key \"%s\"\n",
792 for (counter=0; counter<argCounter; counter++)
793 if (argv[counter] != NULL)
794 HeapFree(GetProcessHeap(), 0, argv[counter]);
797 HeapFree(GetProcessHeap(), 0, lpsRes);
801 /******************************************************************************
802 * Close the currently opened key.
804 static void closeKey()
806 RegCloseKey(currentKeyHandle);
808 HeapFree(GetProcessHeap(), 0, currentKeyName); /* Allocated by getKeyName */
810 bTheKeyIsOpen = FALSE;
812 currentKeyName = NULL;
814 currentKeyHandle = 0;
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
822 static void doSetValue(LPSTR stdInput)
825 * We encoutered the end of the file, make sure we
826 * close the opened key and exit
828 if (stdInput == NULL)
830 if (bTheKeyIsOpen != FALSE)
836 if ( stdInput[0] == '[') /* We are reading a new key */
838 if ( bTheKeyIsOpen != FALSE )
839 closeKey(); /* Close the previous key before */
841 if ( openKey(stdInput) != ERROR_SUCCESS )
842 printf ("regapi: doSetValue failed to open key %s\n", stdInput);
844 else if( ( bTheKeyIsOpen ) &&
845 (( stdInput[0] == '@') || /* reading a default @=data pair */
846 ( stdInput[0] == '\"'))) /* reading a new value=data pair */
848 processSetValue(stdInput);
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 */
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
862 static void doQueryValue(LPSTR stdInput) {
864 * We encoutered the end of the file, make sure we
865 * close the opened key and exit
867 if (stdInput == NULL)
869 if (bTheKeyIsOpen != FALSE)
875 if ( stdInput[0] == '[') /* We are reading a new key */
877 if ( bTheKeyIsOpen != FALSE )
878 closeKey(); /* Close the previous key before */
880 if ( openKey(stdInput) != ERROR_SUCCESS )
881 printf ("regapi: doSetValue failed to open key %s\n", stdInput);
883 else if( ( bTheKeyIsOpen ) &&
884 (( stdInput[0] == '@') || /* reading a default @=data pair */
885 ( stdInput[0] == '\"'))) /* reading a new value=data pair */
887 processQueryValue(stdInput);
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 */
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
901 static void doDeleteValue(LPSTR line) {
902 printf ("regapi: deleteValue not yet implemented\n");
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
909 static void doDeleteKey(LPSTR line) {
910 printf ("regapi: deleteKey not yet implemented\n");
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
917 static void doCreateKey(LPSTR line) {
918 printf ("regapi: createKey not yet implemented\n");
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.
926 int PASCAL WinMain (HANDLE inst, HANDLE prev, LPSTR cmdline, int show)
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;
934 stdInput = HeapAlloc(GetProcessHeap(), 0, STDIN_MAX_LEN);
935 nextLine = HeapAlloc(GetProcessHeap(), 0, STDIN_MAX_LEN);
937 if (stdInput == NULL || nextLine== NULL)
938 return NOT_ENOUGH_MEMORY;
941 * get the command, should be the first arg (modify cmdLine)
943 token = strsep(&cmdline, " ");
946 cmdIndex = getCommand(token);
947 if (cmdIndex == COMMAND_NOT_FOUND)
949 printf("regapi: Command \"%s\" is not supported.\n", token);
951 return COMMAND_NOT_SUPPORTED;
957 "regapi: The first item on the command line must be the command name.\n");
959 return COMMAND_NOT_SUPPORTED;
963 * check to see weather we force the action
964 * (meaning differ depending on the command performed)
966 if ( cmdline != NULL ) /* will be NULL if '-force' is not provided */
967 if ( strstr(cmdline, "-force") != NULL )
970 printf("Processing stdin...\n");
977 ULONG curSize=STDIN_MAX_LEN;
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);
985 * Make some handy generic stuff here...
987 if ( stdInput != NULL )
989 stdInput[strlen(stdInput) -1] = '\0'; /* get rid of new line */
991 if( stdInput[0] == '#' ) /* this is a comment, skip */
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 */
998 nextLine = fgets(nextLine, STDIN_MAX_LEN, stdin);
1000 nextLine[strlen(nextLine)-1] = '\0';
1002 if ( (strlen(stdInput)+strlen(nextLine)) > currentSize){
1004 stdInput=HeapReAlloc(GetProcessHeap(),0,stdInput,strlen(stdInput)+STDIN_MAX_LEN);
1006 currentSize+=STDIN_MAX_LEN;
1009 strcat(stdInput,nextLine+2);
1014 * We process every lines even the NULL (last) line, to indicate the
1015 * end of the processing to the specific process.
1017 commandAPIs[cmdIndex](stdInput);
1019 if (stdInput == NULL) /* EOF encountered */
1025 * Save the registry only if it was modified
1027 if ( commandSaveRegistry[cmdIndex] != FALSE )
1028 SHELL_SaveRegistry();
1030 HeapFree(GetProcessHeap(), 0, nextLine);
1032 HeapFree(GetProcessHeap(), 0, stdInput);