Added winebuild support for generating a .dbg.c file containing the
[wine] / programs / wcmd / batch.c
1 /*
2  * WCMD - Wine-compatible command line interface - batch interface.
3  *
4  * Copyright (C) 1999 D A Pickles
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "wcmd.h"
22
23 void WCMD_batch_command (char *line);
24
25 extern char nyi[];
26 extern char newline[];
27 extern char version_string[];
28 extern int echo_mode;
29 extern char quals[MAX_PATH], param1[MAX_PATH], param2[MAX_PATH];
30 extern BATCH_CONTEXT *context;
31 extern DWORD errorlevel;
32
33
34 /****************************************************************************
35  * WCMD_batch
36  *
37  * Open and execute a batch file.
38  * On entry *command includes the complete command line beginning with the name
39  * of the batch file (if a CALL command was entered the CALL has been removed).
40  * *file is the name of the file, which might not exist and may not have the
41  * .BAT suffix on. Called is 1 for a CALL, 0 otherwise.
42  *
43  * We need to handle recursion correctly, since one batch program might call another.
44  * So parameters for this batch file are held in a BATCH_CONTEXT structure.
45  */
46
47 void WCMD_batch (char *file, char *command, int called) {
48
49 HANDLE h;
50 char string[MAX_PATH];
51 BATCH_CONTEXT *prev_context;
52
53   strcpy (string, file);
54   CharLower (string);
55   if (strstr (string, ".bat") == NULL) strcat (string, ".bat");
56   h = CreateFile (string, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
57   if (h == INVALID_HANDLE_VALUE) {
58     SetLastError (ERROR_FILE_NOT_FOUND);
59     WCMD_print_error ();
60     return;
61   }
62
63 /*
64  *      Create a context structure for this batch file.
65  */
66
67   prev_context = context;
68   context = (BATCH_CONTEXT *)LocalAlloc (LMEM_FIXED, sizeof (BATCH_CONTEXT));
69   context -> h = h;
70   context -> command = command;
71   context -> shift_count = 0;
72   context -> prev_context = prev_context;
73
74 /*
75  *      Work through the file line by line. Specific batch commands are processed here,
76  *      the rest are handled by the main command processor.
77  */
78
79   while (WCMD_fgets (string, sizeof(string), h)) {
80     if (string[0] != ':') {                      /* Skip over labels */
81       WCMD_batch_command (string);
82     }
83   }
84   CloseHandle (h);
85
86 /*
87  *      If invoked by a CALL, we return to the context of our caller. Otherwise return
88  *      to the caller's caller.
89  */
90
91   LocalFree ((HANDLE)context);
92   if ((prev_context != NULL) && (!called)) {
93     CloseHandle (prev_context -> h);
94     context = prev_context -> prev_context;
95     LocalFree ((HANDLE)prev_context);
96   }
97   else {
98     context = prev_context;
99   }
100 }
101
102 /****************************************************************************
103  * WCMD_batch_command
104  *
105  * Execute one line from a batch file, expanding parameters.
106  */
107
108 void WCMD_batch_command (char *line) {
109
110 DWORD status;
111 char cmd1[1024],cmd2[1024];
112 char *p, *s, *t;
113 int i;
114
115   /* Get working version of command line */                  
116   strcpy(cmd1, line);                                    
117
118   /* Expand environment variables in a batch file %{0-9} first  */
119   /*   Then env vars, and if any left (ie use of undefined vars,*/
120   /*   replace with spaces                                      */
121   /* FIXME: Winnt would replace %1%fred%1 with first parm, then */
122   /*   contents of fred, then the digit 1. Would need to remove */
123   /*   ExpandEnvStrings to achieve this                         */
124
125   /* Replace use of %0...%9 */                                
126   p = cmd1;                                                   
127   while ((p = strchr(p, '%'))) {
128     i = *(p+1) - '0';
129     if ((i >= 0) && (i <= 9)) {
130       s = strdup (p+2);
131       t = WCMD_parameter (context -> command, i + context -> shift_count, NULL);
132       strcpy (p, t);
133       strcat (p, s);
134       free (s);
135     } else {                                                  
136       p++;                                                    
137     }
138   }
139
140   /* Now replace environment variables */
141   status = ExpandEnvironmentStrings(cmd1, cmd2, sizeof(cmd2));
142   if (!status) {
143     WCMD_print_error ();
144     return;
145   }
146
147   /* In a batch program, unknown variables are replace by nothing */
148   /* so remove any remaining %var%                                */
149   p = cmd2;                                                   
150   while ((p = strchr(p, '%'))) {                              
151     s = strchr(p+1, '%');                                     
152     if (!s) {                                                 
153       *p=0x00;                                                
154     } else {                                                  
155       t = strdup(s+1);                                        
156       strcpy(p, t);                                           
157       free(t);                                                
158     }                                                         
159   }                                                           
160
161   /* Show prompt before batch line IF echo is on */
162   if (echo_mode && (line[0] != '@')) {                        
163     WCMD_show_prompt();                                       
164     WCMD_output ("%s\n", cmd2);                               
165   }                                                           
166
167   WCMD_process_command (cmd2);                             
168 }
169
170 /*******************************************************************
171  * WCMD_parameter - extract a parameter from a command line.
172  *
173  *      Returns the 'n'th space-delimited parameter on the command line (zero-based).
174  *      Parameter is in static storage overwritten on the next call.
175  *      Parameters in quotes (and brackets) are handled.
176  *      Also returns a pointer to the location of the parameter in the command line.
177  */
178
179 char *WCMD_parameter (char *s, int n, char **where) {
180
181 int i = 0;
182 static char param[MAX_PATH];
183 char *p;
184
185   p = param;
186   while (TRUE) {
187     switch (*s) {
188       case ' ':
189         s++;
190         break;
191       case '"':
192         if (where != NULL) *where = s;
193         s++;
194         while ((*s != '\0') && (*s != '"')) {
195           *p++ = *s++;
196         }
197         if (i == n) {
198           *p = '\0';
199           return param;
200         }
201         if (*s == '"') s++;
202           param[0] = '\0';
203           i++;
204         p = param;
205         break;
206       case '(':
207         if (where != NULL) *where = s;
208         s++;
209         while ((*s != '\0') && (*s != ')')) {
210           *p++ = *s++;
211         }
212         if (i == n) {
213           *p = '\0';
214           return param;
215         }
216         if (*s == ')') s++;
217           param[0] = '\0';
218           i++;
219         p = param;
220         break;
221       case '\0':
222         return param;
223       default:
224         if (where != NULL) *where = s;
225         while ((*s != '\0') && (*s != ' ')) {
226           *p++ = *s++;
227         }
228         if (i == n) {
229           *p = '\0';
230           return param;
231         }
232           param[0] = '\0';
233           i++;
234         p = param;
235     }
236   }
237 }
238
239 /****************************************************************************
240  * WCMD_fgets
241  *
242  * Get one line from a batch file. We can't use the native f* functions because
243  * of the filename syntax differences between DOS and Unix. Also need to lose
244  * the LF (or CRLF) from the line.
245  */
246
247 char *WCMD_fgets (char *s, int n, HANDLE h) {
248
249 DWORD bytes;
250 BOOL status;
251 char *p;
252
253   p = s;
254   do {
255     status = ReadFile (h, s, 1, &bytes, NULL);
256     if ((status == 0) || ((bytes == 0) && (s == p))) return NULL;
257     if (*s == '\n') bytes = 0;
258     else if (*s != '\r') {
259       s++;
260       n--;
261     }
262     *s = '\0';
263   } while ((bytes == 1) && (n > 1));
264   return p;
265 }