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
= NULL
;
42 static HKEY currentKeyHandle
= NULL
;
43 static BOOL bTheKeyIsOpen
= FALSE
;
45 /* Delimitors used to parse the "value"="data" pair for setValue*/
46 #define SET_VALUE_MAX_ARGS 2
47 /* Delimitors 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(80000000):", REG_NONE
},
75 {"hex(80000001):", REG_SZ
},
76 {"hex(80000002):", REG_EXPAND_SZ
},
77 {"hex(80000003):", REG_BINARY
},
78 {"hex(80000004):", REG_DWORD
},
79 {"hex(80000005):", REG_DWORD_BIG_ENDIAN
},
80 {"hex(80000006):", REG_LINK
},
81 {"hex(80000007):", REG_MULTI_SZ
},
82 {"hex(80000008):", REG_RESOURCE_LIST
},
83 {"hex(80000009):", REG_FULL_RESOURCE_DESCRIPTOR
},
84 {"hex(8000000a):", REG_BINARY
}, /* REG_RESOURCE_REQUIREMENTS_LIST}, !Exist */
85 {"hex(8000000A):", REG_BINARY
}, /* REG_RESOURCE_REQUIREMENTS_LIST}, !Exist */
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 suuported 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 displyed 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 resgistry. 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 chanel, 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 funtion 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 ( strncmp( *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
= NULL
; /* 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
= NULL
; /* 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] = NULL
;
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 coma 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 coma */
400 str
[strlen(str
)-1] = NULL
;
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 coma */
432 /******************************************************************************
433 * Converts a hex coma 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 coma, "
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 CHAR lpsCurrentValue
[KEY_MAX_LEN
];
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
;
493 * Default registry values are encoded in the input stream as '@' but as
494 * blank in the wine registry.
496 if( (keyValue
[0] == '@') && (strlen(keyValue
) == 1) )
499 /* Get the data type stored into the value field */
500 dwDataType
= getDataType(&keyData
);
502 memset(lpsCurrentValue
, 0, KEY_MAX_LEN
);
503 hRes
= RegQueryValueExA(
508 (LPBYTE
)lpsCurrentValue
,
511 if( ( strlen(lpsCurrentValue
) == 0 ) || /* The value is not existing */
512 ( bForce
)) /* -force option */
515 BYTE convert
[KEY_MAX_LEN
];
518 if ( dwDataType
== REG_SZ
) /* no convertion for string */
520 dwLen
= strlen(keyData
);
523 else if (dwDataType
== REG_DWORD
) /* Convert the dword types */
525 dwLen
= convertHexToDWord(keyData
, convert
);
528 else /* Convert the hexadecimal types */
530 dwLen
= convertHexCSVToHex(keyData
, convert
, KEY_MAX_LEN
);
534 hRes
= RegSetValueEx(
544 /* return the current value data into argv[1] */
547 HeapFree(GetProcessHeap(), 0, argv
[1]);
548 argv
[1] = HeapAlloc(GetProcessHeap(), 0, dwSize
+1);
550 if ( argv
[1] != NULL
)
551 strncpy(argv
[1], lpsCurrentValue
, dwSize
);
554 return KEY_VALUE_ALREADY_SET
;
560 /******************************************************************************
563 static HRESULT
openKey( LPSTR stdInput
)
569 if (stdInput
== NULL
)
570 return ERROR_INVALID_PARAMETER
;
572 /* Get the registry class */
573 currentKeyClass
= getRegClass(stdInput
); /* Sets global variable */
574 if (currentKeyClass
== ERROR_INVALID_PARAMETER
)
575 return ERROR_INVALID_PARAMETER
;
577 /* Get the key name */
578 currentKeyName
= getRegKeyName(stdInput
); /* Sets global variable */
579 if (currentKeyName
== NULL
)
580 return ERROR_INVALID_PARAMETER
;
582 hRes
= RegCreateKeyEx(
583 currentKeyClass
, /* Class */
584 currentKeyName
, /* Sub Key */
586 NULL
, /* object type */
587 REG_OPTION_NON_VOLATILE
, /* option, REG_OPTION_NON_VOLATILE ... */
588 KEY_ALL_ACCESS
, /* access mask, KEY_ALL_ACCESS */
589 NULL
, /* security attribute */
590 ¤tKeyHandle
, /* result */
591 &dwDisp
); /* disposition, REG_CREATED_NEW_KEY or
592 REG_OPENED_EXISTING_KEY */
594 if (hRes
== ERROR_SUCCESS
)
595 bTheKeyIsOpen
= TRUE
;
600 /******************************************************************************
601 * This function is a wrapper arround the setValue function. It prepares the
602 * land and clean the area once completed.
604 static void processSetValue(LPSTR cmdline
)
606 LPSTR argv
[SET_VALUE_MAX_ARGS
]; /* args storage */
608 LPSTR token
= NULL
; /* current token analized */
609 ULONG argCounter
= 0; /* counter of args */
614 * Init storage and parse the line
616 for (counter
=0; counter
<SET_VALUE_MAX_ARGS
; counter
++)
619 while( (token
= strsep(&cmdline
, setValueDelim
[argCounter
])) != NULL
)
621 argv
[argCounter
++] = getArg(token
);
623 if (argCounter
== SET_VALUE_MAX_ARGS
)
624 break; /* Stop processing args no matter what */
627 hRes
= setValue(argv
);
628 if ( hRes
== ERROR_SUCCESS
)
630 "regapi: Value \"%s\" has been set to \"%s\" in key [%s]\n",
635 else if ( hRes
== KEY_VALUE_ALREADY_SET
)
637 "regapi: Value \"%s\" already set to \"%s\" in key [%s]\n",
643 printf("regapi: ERROR Key %s not created. Value: %s, Data: %s\n",
651 for (counter
=0; counter
<argCounter
; counter
++)
652 if (argv
[counter
] != NULL
)
653 HeapFree(GetProcessHeap(), 0, argv
[counter
]);
656 /******************************************************************************
657 * This function is a wrapper arround the queryValue function. It prepares the
658 * land and clean the area once completed.
660 static void processQueryValue(LPSTR cmdline
)
662 LPSTR argv
[QUERY_VALUE_MAX_ARGS
];/* args storage */
663 LPSTR token
= NULL
; /* current token analized */
664 ULONG argCounter
= 0; /* counter of args */
667 LPSTR keyValue
= NULL
;
671 * Init storage and parse the line
673 for (counter
=0; counter
<QUERY_VALUE_MAX_ARGS
; counter
++)
676 while( (token
= strsep(&cmdline
, queryValueDelim
[argCounter
])) != NULL
)
678 argv
[argCounter
++] = getArg(token
);
680 if (argCounter
== QUERY_VALUE_MAX_ARGS
)
681 break; /* Stop processing args no matter what */
684 /* The value we look for is the first token on the line */
685 if ( argv
[0] == NULL
)
686 return; /* SHOULD NOT OCCURS */
690 if( (keyValue
[0] == '@') && (strlen(keyValue
) == 1) )
692 LONG lLen
= KEY_MAX_LEN
;
693 CHAR lpsData
[KEY_MAX_LEN
];
695 * We need to query the key default value
697 hRes
= RegQueryValue(
703 if (hRes
== ERROR_SUCCESS
)
705 lpsRes
= HeapAlloc( GetProcessHeap(), 0, lLen
);
706 strncpy(lpsRes
, lpsData
, lLen
);
711 DWORD dwLen
= KEY_MAX_LEN
;
712 BYTE lpbData
[KEY_MAX_LEN
];
715 * We need to query a specific value for the key
717 hRes
= RegQueryValueEx(
725 if (hRes
== ERROR_SUCCESS
)
728 * Convert the returned data to a displayable format
734 lpsRes
= HeapAlloc( GetProcessHeap(), 0, dwLen
);
735 strncpy(lpsRes
, lpbData
, dwLen
);
740 lpsRes
= convertHexToDWORDStr(lpbData
, dwLen
);
745 lpsRes
= convertHexToHexCSV(lpbData
, dwLen
);
753 if ( hRes
== ERROR_SUCCESS
)
755 "regapi: Value \"%s\" = \"%s\" in key [%s]\n",
761 printf("regapi: ERROR Value \"%s\" not found. for key \"%s\"\n",
768 for (counter
=0; counter
<argCounter
; counter
++)
769 if (argv
[counter
] != NULL
)
770 HeapFree(GetProcessHeap(), 0, argv
[counter
]);
773 HeapFree(GetProcessHeap(), 0, lpsRes
);
777 /******************************************************************************
778 * Close the currently opened key.
780 static void closeKey()
782 RegCloseKey(currentKeyHandle
);
784 HeapFree(GetProcessHeap(), 0, currentKeyName
); /* Allocated by getKeyName */
786 bTheKeyIsOpen
= FALSE
;
788 currentKeyName
= NULL
;
789 currentKeyClass
= NULL
;
790 currentKeyHandle
= NULL
;
793 /******************************************************************************
794 * This funtion is the main entry point to the setValue type of action. It
795 * receives the currently read line and dispatch the work depending on the
798 static void doSetValue(LPSTR stdInput
)
801 * We encoutered the end of the file, make sure we
802 * close the opened key and exit
804 if (stdInput
== NULL
)
806 if (bTheKeyIsOpen
!= FALSE
)
812 if ( stdInput
[0] == '[') /* We are reading a new key */
814 if ( bTheKeyIsOpen
!= FALSE
)
815 closeKey(); /* Close the previous key before */
817 if ( openKey(stdInput
) != ERROR_SUCCESS
)
818 printf ("regapi: doSetValue failed to open key %s\n", stdInput
);
820 else if( ( bTheKeyIsOpen
) &&
821 (( stdInput
[0] == '@') || /* reading a default @=data pair */
822 ( stdInput
[0] == '\"'))) /* reading a new value=data pair */
824 processSetValue(stdInput
);
826 else /* since we are assuming that the */
827 { /* file format is valid we must */
828 if ( bTheKeyIsOpen
) /* be reading a blank line which */
829 closeKey(); /* indicate end of this key processing */
833 /******************************************************************************
834 * This funtion is the main entry point to the queryValue type of action. It
835 * receives the currently read line and dispatch the work depending on the
838 static void doQueryValue(LPSTR stdInput
) {
840 * We encoutered the end of the file, make sure we
841 * close the opened key and exit
843 if (stdInput
== NULL
)
845 if (bTheKeyIsOpen
!= FALSE
)
851 if ( stdInput
[0] == '[') /* We are reading a new key */
853 if ( bTheKeyIsOpen
!= FALSE
)
854 closeKey(); /* Close the previous key before */
856 if ( openKey(stdInput
) != ERROR_SUCCESS
)
857 printf ("regapi: doSetValue failed to open key %s\n", stdInput
);
859 else if( ( bTheKeyIsOpen
) &&
860 (( stdInput
[0] == '@') || /* reading a default @=data pair */
861 ( stdInput
[0] == '\"'))) /* reading a new value=data pair */
863 processQueryValue(stdInput
);
865 else /* since we are assuming that the */
866 { /* file format is valid we must */
867 if ( bTheKeyIsOpen
) /* be reading a blank line which */
868 closeKey(); /* indicate end of this key processing */
872 /******************************************************************************
873 * This funtion is the main entry point to the deletetValue type of action. It
874 * receives the currently read line and dispatch the work depending on the
877 static void doDeleteValue(LPSTR line
) {
878 printf ("regapi: deleteValue not yet implemented\n");
880 /******************************************************************************
881 * This funtion is the main entry point to the deleteKey type of action. It
882 * receives the currently read line and dispatch the work depending on the
885 static void doDeleteKey(LPSTR line
) {
886 printf ("regapi: deleteKey not yet implemented\n");
888 /******************************************************************************
889 * This funtion is the main entry point to the createKey type of action. It
890 * receives the currently read line and dispatch the work depending on the
893 static void doCreateKey(LPSTR line
) {
894 printf ("regapi: createKey not yet implemented\n");
897 /******************************************************************************
898 * MAIN - The main simply validate the first parameter (command to perform)
899 * It then read the STDIN lines by lines forwarding their processing
900 * to the appropriate method.
902 int PASCAL
WinMain (HANDLE inst
, HANDLE prev
, LPSTR cmdline
, int show
)
904 LPSTR token
= NULL
; /* current token analized */
905 LPSTR stdInput
= NULL
; /* line read from stdin */
906 INT cmdIndex
= -1; /* index of the command in array */
908 stdInput
= HeapAlloc(GetProcessHeap(), 0, STDIN_MAX_LEN
);
909 if (stdInput
== NULL
)
910 return NOT_ENOUGH_MEMORY
;
913 * get the command, should be the first arg (modify cmdLine)
915 token
= strsep(&cmdline
, " ");
918 cmdIndex
= getCommand(token
);
919 if (cmdIndex
== COMMAND_NOT_FOUND
)
921 printf("regapi: Command \"%s\" is not supported.\n", token
);
923 return COMMAND_NOT_SUPPORTED
;
929 "regapi: The first item on the command line must be the command name.\n");
931 return COMMAND_NOT_SUPPORTED
;
935 * check to see weather we force the action
936 * (meaning differ depending on the command performed)
938 if ( cmdline
!= NULL
) /* will be NULL if '-force' is not provided */
939 if ( strstr(cmdline
, "-force") != NULL
)
942 printf("Processing stdin...\n");
949 stdInput
= fgets(stdInput
, STDIN_MAX_LEN
, stdin
);
952 * Make some handy generic stuff here...
954 if ( stdInput
!= NULL
)
956 stdInput
[strlen(stdInput
) -1] = NULL
; /* get rid of new line */
958 if( stdInput
[0] == '#' ) /* this is a comment, skip */
963 * We process every lines even the NULL (last) line, to indicate the
964 * end of the processing to the specific process.
966 commandAPIs
[cmdIndex
](stdInput
);
968 if (stdInput
== NULL
) /* EOF encountered */
973 * Save the registry only if it was modified
975 if ( commandSaveRegistry
[cmdIndex
] != FALSE
)
976 SHELL_SaveRegistry();
978 HeapFree(GetProcessHeap(), 0, stdInput
);