Move the RT_XXX macros to winuser.h.
[wine/wine64.git] / programs / wcmd / batch.c
blob15929a957189b56ede6f1829f0c050573af344e2
1 /*
2 * WCMD - Wine-compatible command line interface - batch interface.
4 * (C) 1999 D A Pickles
6 */
9 #include "wcmd.h"
11 void WCMD_batch_command (char *line);
13 extern char nyi[];
14 extern char newline[];
15 extern char version_string[];
16 extern int echo_mode;
17 extern char quals[MAX_PATH], param1[MAX_PATH], param2[MAX_PATH];
18 extern BATCH_CONTEXT *context;
19 extern DWORD errorlevel;
22 /****************************************************************************
23 * WCMD_batch
25 * Open and execute a batch file.
26 * On entry *command includes the complete command line beginning with the name
27 * of the batch file (if a CALL command was entered the CALL has been removed).
28 * *file is the name of the file, which might not exist and may not have the
29 * .BAT suffix on. Called is 1 for a CALL, 0 otherwise.
31 * We need to handle recursion correctly, since one batch program might call another.
32 * So parameters for this batch file are held in a BATCH_CONTEXT structure.
35 void WCMD_batch (char *file, char *command, int called) {
37 HANDLE h;
38 char string[MAX_PATH];
39 BATCH_CONTEXT *prev_context;
41 strcpy (string, file);
42 CharLower (string);
43 if (strstr (string, ".bat") == NULL) strcat (string, ".bat");
44 h = CreateFile (string, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
45 if (h == INVALID_HANDLE_VALUE) {
46 SetLastError (ERROR_FILE_NOT_FOUND);
47 WCMD_print_error ();
48 return;
52 * Create a context structure for this batch file.
55 prev_context = context;
56 context = (BATCH_CONTEXT *)LocalAlloc (LMEM_FIXED, sizeof (BATCH_CONTEXT));
57 context -> h = h;
58 context -> command = command;
59 context -> shift_count = 0;
60 context -> prev_context = prev_context;
63 * Work through the file line by line. Specific batch commands are processed here,
64 * the rest are handled by the main command processor.
67 while (WCMD_fgets (string, sizeof(string), h)) {
68 if (string[0] != ':') { /* Skip over labels */
69 WCMD_batch_command (string);
72 CloseHandle (h);
75 * If invoked by a CALL, we return to the context of our caller. Otherwise return
76 * to the caller's caller.
79 LocalFree ((HANDLE)context);
80 if ((prev_context != NULL) && (!called)) {
81 CloseHandle (prev_context -> h);
82 context = prev_context -> prev_context;
83 LocalFree ((HANDLE)prev_context);
85 else {
86 context = prev_context;
90 /****************************************************************************
91 * WCMD_batch_command
93 * Execute one line from a batch file, expanding parameters.
96 void WCMD_batch_command (char *line) {
98 DWORD status;
99 char cmd1[1024],cmd2[1024];
100 char *p, *s, *t;
101 int i;
103 /* Get working version of command line */
104 strcpy(cmd1, line);
106 /* Expand environment variables in a batch file %{0-9} first */
107 /* Then env vars, and if any left (ie use of undefined vars,*/
108 /* replace with spaces */
109 /* FIXME: Winnt would replace %1%fred%1 with first parm, then */
110 /* contents of fred, then the digit 1. Would need to remove */
111 /* ExpandEnvStrings to achieve this */
113 /* Replace use of %0...%9 */
114 p = cmd1;
115 while ((p = strchr(p, '%'))) {
116 i = *(p+1) - '0';
117 if ((i >= 0) && (i <= 9)) {
118 s = strdup (p+2);
119 t = WCMD_parameter (context -> command, i + context -> shift_count, NULL);
120 strcpy (p, t);
121 strcat (p, s);
122 free (s);
123 } else {
124 p++;
128 /* Now replace environment variables */
129 status = ExpandEnvironmentStrings(cmd1, cmd2, sizeof(cmd2));
130 if (!status) {
131 WCMD_print_error ();
132 return;
135 /* In a batch program, unknown variables are replace by nothing */
136 /* so remove any remaining %var% */
137 p = cmd2;
138 while ((p = strchr(p, '%'))) {
139 s = strchr(p+1, '%');
140 if (!s) {
141 *p=0x00;
142 } else {
143 t = strdup(s+1);
144 strcpy(p, t);
145 free(t);
149 /* Show prompt before batch line IF echo is on */
150 if (echo_mode && (line[0] != '@')) {
151 WCMD_show_prompt();
152 WCMD_output ("%s\n", cmd2);
155 WCMD_process_command (cmd2);
158 /*******************************************************************
159 * WCMD_parameter - extract a parameter from a command line.
161 * Returns the 'n'th space-delimited parameter on the command line (zero-based).
162 * Parameter is in static storage overwritten on the next call.
163 * Parameters in quotes (and brackets) are handled.
164 * Also returns a pointer to the location of the parameter in the command line.
167 char *WCMD_parameter (char *s, int n, char **where) {
169 int i = 0;
170 static char param[MAX_PATH];
171 char *p;
173 p = param;
174 while (TRUE) {
175 switch (*s) {
176 case ' ':
177 s++;
178 break;
179 case '"':
180 if (where != NULL) *where = s;
181 s++;
182 while ((*s != '\0') && (*s != '"')) {
183 *p++ = *s++;
185 if (i == n) {
186 *p = '\0';
187 return param;
189 if (*s == '"') s++;
190 param[0] = '\0';
191 i++;
192 p = param;
193 break;
194 case '(':
195 if (where != NULL) *where = s;
196 s++;
197 while ((*s != '\0') && (*s != ')')) {
198 *p++ = *s++;
200 if (i == n) {
201 *p = '\0';
202 return param;
204 if (*s == ')') s++;
205 param[0] = '\0';
206 i++;
207 p = param;
208 break;
209 case '\0':
210 return param;
211 default:
212 if (where != NULL) *where = s;
213 while ((*s != '\0') && (*s != ' ')) {
214 *p++ = *s++;
216 if (i == n) {
217 *p = '\0';
218 return param;
220 param[0] = '\0';
221 i++;
222 p = param;
227 /****************************************************************************
228 * WCMD_fgets
230 * Get one line from a batch file. We can't use the native f* functions because
231 * of the filename syntax differences between DOS and Unix. Also need to lose
232 * the LF (or CRLF) from the line.
235 char *WCMD_fgets (char *s, int n, HANDLE h) {
237 DWORD bytes;
238 BOOL status;
239 char *p;
241 p = s;
242 do {
243 status = ReadFile (h, s, 1, &bytes, NULL);
244 if ((status == 0) || ((bytes == 0) && (s == p))) return NULL;
245 if (*s == '\n') bytes = 0;
246 else if (*s != '\r') {
247 s++;
248 n--;
250 *s = '\0';
251 } while ((bytes == 1) && (n > 1));
252 return p;