batch.c 7.79 KB
Newer Older
1 2 3
/*
 * WCMD - Wine-compatible command line interface - batch interface.
 *
4
 * Copyright (C) 1999 D A Pickles
5
 *
6 7 8 9 10 11 12 13 14 15 16 17 18
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 20 21 22
 */

#include "wcmd.h"

23
void WCMD_batch_command (char *line);
24 25 26

extern int echo_mode;
extern char quals[MAX_PATH], param1[MAX_PATH], param2[MAX_PATH];
27
extern BATCH_CONTEXT *context;
28
extern DWORD errorlevel;
29

30 31
/* msdn specified max for Win XP */
#define MAXSTRING 8192
32 33 34 35 36 37 38 39

/****************************************************************************
 * WCMD_batch
 *
 * Open and execute a batch file.
 * On entry *command includes the complete command line beginning with the name
 * of the batch file (if a CALL command was entered the CALL has been removed).
 * *file is the name of the file, which might not exist and may not have the
40
 * .BAT suffix on. Called is 1 for a CALL, 0 otherwise.
41 42
 *
 * We need to handle recursion correctly, since one batch program might call another.
43
 * So parameters for this batch file are held in a BATCH_CONTEXT structure.
44 45
 */

46
void WCMD_batch (char *file, char *command, int called) {
47

48 49 50
#define WCMD_BATCH_EXT_SIZE 5

HANDLE h = INVALID_HANDLE_VALUE;
51
char string[MAXSTRING];
52 53
char extension_batch[][WCMD_BATCH_EXT_SIZE] = {".bat",".cmd"};
char extension_exe[WCMD_BATCH_EXT_SIZE] = ".exe";
54
unsigned int  i;
55
BATCH_CONTEXT *prev_context;
56

57
  for(i=0; (i<(sizeof(extension_batch)/WCMD_BATCH_EXT_SIZE)) && 
58
           (h == INVALID_HANDLE_VALUE); i++) {
59 60
  strcpy (string, file);
  CharLower (string);
61
    if (strstr (string, extension_batch[i]) == NULL) strcat (string, extension_batch[i]);
62 63
  h = CreateFile (string, GENERIC_READ, FILE_SHARE_READ,
                  NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
64
  }
65
  if (h == INVALID_HANDLE_VALUE) {
66 67 68 69 70 71 72 73 74 75 76
    strcpy (string, file);
    CharLower (string);
    if (strstr (string, extension_exe) == NULL) strcat (string, extension_exe);
    h = CreateFile (string, GENERIC_READ, FILE_SHARE_READ,
                    NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (h != INVALID_HANDLE_VALUE) {
      WCMD_run_program (command);
    } else {
      SetLastError (ERROR_FILE_NOT_FOUND);
      WCMD_print_error ();
    }
77 78 79
    return;
  }

80 81 82 83 84 85 86 87 88 89 90
/*
 *	Create a context structure for this batch file.
 */

  prev_context = context;
  context = (BATCH_CONTEXT *)LocalAlloc (LMEM_FIXED, sizeof (BATCH_CONTEXT));
  context -> h = h;
  context -> command = command;
  context -> shift_count = 0;
  context -> prev_context = prev_context;

91 92 93 94 95 96
/*
 * 	Work through the file line by line. Specific batch commands are processed here,
 * 	the rest are handled by the main command processor.
 */

  while (WCMD_fgets (string, sizeof(string), h)) {
97 98 99 100 101 102 103 104
      if (strlen(string) == MAXSTRING -1) {
          WCMD_output_asis( "Line in Batch processing possibly truncated. Using:\n");
          WCMD_output_asis( string);
          WCMD_output_asis( "\n");
      }
      if (string[0] != ':') {                      /* Skip over labels */
          WCMD_batch_command (string);
      }
105 106
  }
  CloseHandle (h);
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121

/*
 *	If invoked by a CALL, we return to the context of our caller. Otherwise return
 *	to the caller's caller.
 */

  LocalFree ((HANDLE)context);
  if ((prev_context != NULL) && (!called)) {
    CloseHandle (prev_context -> h);
    context = prev_context -> prev_context;
    LocalFree ((HANDLE)prev_context);
  }
  else {
    context = prev_context;
  }
122 123 124 125 126
}

/****************************************************************************
 * WCMD_batch_command
 *
127
 * Execute one line from a batch file, expanding parameters.
128 129
 */

130
void WCMD_batch_command (char *line) {
131 132

DWORD status;
133
char cmd1[MAXSTRING],cmd2[MAXSTRING];
134 135
char *p, *s, *t;
int i;
136

137 138
  /* Get working version of command line */
  strcpy(cmd1, line);
139

140 141 142 143 144 145 146
  /* Expand environment variables in a batch file %{0-9} first  */
  /*   Then env vars, and if any left (ie use of undefined vars,*/
  /*   replace with spaces                                      */
  /* FIXME: Winnt would replace %1%fred%1 with first parm, then */
  /*   contents of fred, then the digit 1. Would need to remove */
  /*   ExpandEnvStrings to achieve this                         */

147 148
  /* Replace use of %0...%9 */
  p = cmd1;
149 150 151 152
  while ((p = strchr(p, '%'))) {
    i = *(p+1) - '0';
    if ((i >= 0) && (i <= 9)) {
      s = strdup (p+2);
153
      t = WCMD_parameter (context -> command, i + context -> shift_count, NULL);
154 155 156
      strcpy (p, t);
      strcat (p, s);
      free (s);
157 158
    } else {
      p++;
159
    }
160
  }
161 162 163 164 165 166 167 168 169 170

  /* Now replace environment variables */
  status = ExpandEnvironmentStrings(cmd1, cmd2, sizeof(cmd2));
  if (!status) {
    WCMD_print_error ();
    return;
  }

  /* In a batch program, unknown variables are replace by nothing */
  /* so remove any remaining %var%                                */
171 172 173 174 175 176 177 178 179 180 181
  p = cmd2;
  while ((p = strchr(p, '%'))) {
    s = strchr(p+1, '%');
    if (!s) {
      *p=0x00;
    } else {
      t = strdup(s+1);
      strcpy(p, t);
      free(t);
    }
  }
182 183

  /* Show prompt before batch line IF echo is on */
184 185
  if (echo_mode && (line[0] != '@')) {
    WCMD_show_prompt();
186 187
    WCMD_output_asis ( cmd2);
    WCMD_output_asis ( "\n");
188
  }
189

190
  WCMD_process_command (cmd2);
191 192 193 194 195
}

/*******************************************************************
 * WCMD_parameter - extract a parameter from a command line.
 *
196
 *	Returns the 'n'th space-delimited parameter on the command line (zero-based).
197
 *	Parameter is in static storage overwritten on the next call.
198
 *	Parameters in quotes (and brackets) are handled.
199
 *	Also returns a pointer to the location of the parameter in the command line.
200 201
 */

202
char *WCMD_parameter (char *s, int n, char **where) {
203

204
int i = 0;
205 206 207 208 209 210 211 212 213 214
static char param[MAX_PATH];
char *p;

  p = param;
  while (TRUE) {
    switch (*s) {
      case ' ':
	s++;
	break;
      case '"':
215
        if (where != NULL) *where = s;
216 217 218 219 220 221 222 223
	s++;
	while ((*s != '\0') && (*s != '"')) {
	  *p++ = *s++;
	}
        if (i == n) {
          *p = '\0';
          return param;
        }
224
	if (*s == '"') s++;
225 226
          param[0] = '\0';
          i++;
227
        p = param;
228
	break;
229
      case '(':
230
        if (where != NULL) *where = s;
231 232 233 234 235 236 237 238
	s++;
	while ((*s != '\0') && (*s != ')')) {
	  *p++ = *s++;
	}
        if (i == n) {
          *p = '\0';
          return param;
        }
239
	if (*s == ')') s++;
240 241
          param[0] = '\0';
          i++;
242
        p = param;
243
	break;
244 245 246
      case '\0':
        return param;
      default:
247
        if (where != NULL) *where = s;
248 249 250 251 252 253 254 255 256
	while ((*s != '\0') && (*s != ' ')) {
	  *p++ = *s++;
	}
        if (i == n) {
          *p = '\0';
          return param;
        }
          param[0] = '\0';
          i++;
257
        p = param;
258 259 260 261
    }
  }
}

262
/****************************************************************************
263 264 265
 * WCMD_fgets
 *
 * Get one line from a batch file. We can't use the native f* functions because
266 267
 * of the filename syntax differences between DOS and Unix. Also need to lose
 * the LF (or CRLF) from the line.
268 269 270 271 272 273 274 275 276 277 278
 */

char *WCMD_fgets (char *s, int n, HANDLE h) {

DWORD bytes;
BOOL status;
char *p;

  p = s;
  do {
    status = ReadFile (h, s, 1, &bytes, NULL);
279
    if ((status == 0) || ((bytes == 0) && (s == p))) return NULL;
280
    if (*s == '\n') bytes = 0;
281 282
    else if (*s != '\r') {
      s++;
283
      n--;
284 285
    }
    *s = '\0';
286 287 288
  } while ((bytes == 1) && (n > 1));
  return p;
}