1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452
|
/*
* CMD - Wine-compatible command line interface.
*
* Copyright (C) 1999 D A Pickles
* Copyright (C) 2007 J Edmeades
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#define IDI_ICON1 1
#include <windows.h>
#include <windef.h>
#include <winternl.h>
#ifndef RC_INVOKED
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <ctype.h>
#include <wchar.h>
/* msdn specified max for Win XP */
#define MAXSTRING 8192
/* Data structure to express a redirection */
typedef struct _CMD_REDIRECTION
{
enum CMD_REDIRECTION_KIND {REDIR_READ_FROM, REDIR_WRITE_TO, REDIR_WRITE_APPEND, REDIR_WRITE_CLONE} kind;
unsigned short fd;
struct _CMD_REDIRECTION *next;
union
{
unsigned short clone; /* only if kind is REDIR_WRITE_CLONE */
WCHAR file[1]; /* only if kind is READ_FROM, WRITE or WRITE_APPEND */
};
} CMD_REDIRECTION;
/* Data structure to hold commands delimiters/separators */
typedef enum _CMD_OPERATOR
{
CMD_SINGLE, /* single command */
CMD_CONCAT, /* & */
CMD_ONFAILURE, /* || */
CMD_ONSUCCESS, /* && */
CMD_PIPE, /* Single | */
CMD_IF, /* IF command */
CMD_FOR, /* FOR command */
} CMD_OPERATOR;
/* Data structure to hold commands to be processed */
enum cond_operator {CMD_IF_ERRORLEVEL, CMD_IF_EXIST, CMD_IF_DEFINED,
CMD_IF_BINOP_EQUAL /* == */, CMD_IF_BINOP_LSS, CMD_IF_BINOP_LEQ, CMD_IF_BINOP_EQU,
CMD_IF_BINOP_NEQ, CMD_IF_BINOP_GEQ, CMD_IF_BINOP_GTR};
typedef struct _CMD_IF_CONDITION
{
unsigned case_insensitive : 1,
negated : 1,
op;
union
{
/* CMD_IF_ERRORLEVEL, CMD_IF_EXIST, CMD_IF_DEFINED */
const WCHAR *operand;
/* CMD_BINOP_EQUAL, CMD_BINOP_LSS, CMD_BINOP_LEQ, CMD_BINOP_EQU, CMD_BINOP_NEQ, CMD_BINOP_GEQ, CMD_BINOP_GTR */
struct
{
const WCHAR *left;
const WCHAR *right;
};
};
} CMD_IF_CONDITION;
#define CMD_FOR_FLAG_TREE_RECURSE (1u << 0)
#define CMD_FOR_FLAG_TREE_INCLUDE_FILES (1u << 1)
#define CMD_FOR_FLAG_TREE_INCLUDE_DIRECTORIES (1u << 2)
typedef struct _CMD_FOR_CONTROL
{
enum for_control_operator {CMD_FOR_FILETREE, CMD_FOR_FILE_SET /* /F */,
CMD_FOR_NUMBERS /* /L */} operator;
unsigned flags; /* |-ed CMD_FOR_FLAG_* */
unsigned variable_index;
const WCHAR *set;
union
{
const WCHAR *root_dir; /* for CMD_FOR_FILETREE */
struct /* for CMD_FOR_FILE_SET */
{
WCHAR eol;
BOOL use_backq;
int num_lines_to_skip;
const WCHAR *delims;
const WCHAR *tokens;
};
};
} CMD_FOR_CONTROL;
typedef struct _CMD_NODE
{
CMD_OPERATOR op; /* operator */
CMD_REDIRECTION *redirects; /* Redirections */
union
{
WCHAR *command; /* CMD_SINGLE */
struct /* binary operator (CMD_CONCAT, ONFAILURE, ONSUCCESS, PIPE) */
{
struct _CMD_NODE *left;
struct _CMD_NODE *right;
};
struct /* CMD_IF */
{
CMD_IF_CONDITION condition;
struct _CMD_NODE *then_block;
struct _CMD_NODE *else_block;
};
struct /* CMD_FOR */
{
CMD_FOR_CONTROL for_ctrl;
struct _CMD_NODE *do_block;
};
};
} CMD_NODE;
struct _DIRECTORY_STACK;
void WCMD_add_dirstowalk(struct _DIRECTORY_STACK *dirsToWalk);
struct _DIRECTORY_STACK *WCMD_dir_stack_create(const WCHAR *dir, const WCHAR *file);
struct _DIRECTORY_STACK *WCMD_dir_stack_free(struct _DIRECTORY_STACK *dir);
/* The return code:
* - some of them are directly mapped to kernel32's errors
* - some others are cmd.exe specific
* - ABORTED if used to break out of FOR/IF blocks (to handle GOTO, EXIT commands)
*/
typedef int RETURN_CODE;
#define RETURN_CODE_SYNTAX_ERROR 255
#define RETURN_CODE_CANT_LAUNCH 9009
#define RETURN_CODE_ABORTED (-999999)
BOOL WCMD_print_volume_information(const WCHAR *);
RETURN_CODE WCMD_assoc(const WCHAR *, BOOL);
RETURN_CODE WCMD_call(WCHAR *command);
RETURN_CODE WCMD_choice(WCHAR *);
RETURN_CODE WCMD_clear_screen(void);
RETURN_CODE WCMD_color(void);
RETURN_CODE WCMD_copy(WCHAR *);
RETURN_CODE WCMD_create_dir(WCHAR *);
RETURN_CODE WCMD_delete(WCHAR *);
RETURN_CODE WCMD_directory(WCHAR *);
RETURN_CODE WCMD_echo(const WCHAR *);
RETURN_CODE WCMD_endlocal(void);
void WCMD_enter_paged_mode(const WCHAR *);
RETURN_CODE WCMD_exit(void);
BOOL WCMD_get_fullpath(const WCHAR *, SIZE_T, WCHAR *, WCHAR **);
RETURN_CODE WCMD_give_help(WCHAR *args);
RETURN_CODE WCMD_goto(void);
RETURN_CODE WCMD_label(void);
void WCMD_leave_paged_mode(void);
RETURN_CODE WCMD_more(WCHAR *);
RETURN_CODE WCMD_move (void);
WCHAR* WINAPIV WCMD_format_string (const WCHAR *format, ...);
void WINAPIV WCMD_output (const WCHAR *format, ...);
void WINAPIV WCMD_output_stderr (const WCHAR *format, ...);
void WCMD_output_asis (const WCHAR *message);
void WCMD_output_asis_stderr (const WCHAR *message);
RETURN_CODE WCMD_pause(void);
RETURN_CODE WCMD_popd(void);
void WCMD_print_error (void);
RETURN_CODE WCMD_pushd(const WCHAR *args);
RETURN_CODE WCMD_remove_dir(WCHAR *command);
RETURN_CODE WCMD_rename(void);
RETURN_CODE WCMD_setlocal(WCHAR *args);
RETURN_CODE WCMD_setshow_date(void);
RETURN_CODE WCMD_setshow_default(const WCHAR *args);
RETURN_CODE WCMD_setshow_env(WCHAR *command);
RETURN_CODE WCMD_setshow_path(const WCHAR *args);
RETURN_CODE WCMD_setshow_prompt(void);
RETURN_CODE WCMD_setshow_time(void);
RETURN_CODE WCMD_shift(const WCHAR *args);
RETURN_CODE WCMD_start(WCHAR *args);
RETURN_CODE WCMD_title(const WCHAR *);
RETURN_CODE WCMD_type(WCHAR *);
RETURN_CODE WCMD_verify(void);
RETURN_CODE WCMD_version(void);
RETURN_CODE WCMD_volume(void);
RETURN_CODE WCMD_mklink(WCHAR *args);
RETURN_CODE WCMD_change_drive(WCHAR drive);
WCHAR *WCMD_fgets (WCHAR *buf, DWORD n, HANDLE stream);
WCHAR *WCMD_parameter (WCHAR *s, int n, WCHAR **start, BOOL raw, BOOL wholecmdline);
WCHAR *WCMD_parameter_with_delims (WCHAR *s, int n, WCHAR **start, BOOL raw,
BOOL wholecmdline, const WCHAR *delims);
WCHAR *WCMD_skip_leading_spaces (WCHAR *string);
BOOL WCMD_keyword_ws_found(const WCHAR *keyword, const WCHAR *ptr);
void WCMD_HandleTildeModifiers(WCHAR **start, BOOL atExecute);
WCHAR *WCMD_strip_quotes(WCHAR *cmd);
WCHAR *WCMD_LoadMessage(UINT id);
WCHAR *WCMD_strsubstW(WCHAR *start, const WCHAR* next, const WCHAR* insert, int len);
BOOL WCMD_ReadFile(const HANDLE hIn, WCHAR *intoBuf, const DWORD maxChars, LPDWORD charsRead);
enum read_parse_line {RPL_SUCCESS, RPL_EOF, RPL_SYNTAXERROR};
enum read_parse_line WCMD_ReadAndParseLine(const WCHAR *initialcmd, CMD_NODE **output);
void node_dispose_tree(CMD_NODE *cmds);
RETURN_CODE node_execute(CMD_NODE *node);
RETURN_CODE WCMD_call_batch(const WCHAR *, WCHAR *);
RETURN_CODE WCMD_call_command(WCHAR *command);
RETURN_CODE WCMD_run_builtin_command(int cmd_index, WCHAR *cmd);
BOOL WCMD_find_label(HANDLE h, const WCHAR*, LARGE_INTEGER *pos);
void WCMD_set_label_end(WCHAR *string);
void *xrealloc(void *, size_t) __WINE_ALLOC_SIZE(2) __WINE_DEALLOC(free);
static inline void *xalloc(size_t sz) __WINE_MALLOC;
static inline void *xalloc(size_t sz)
{
return xrealloc(NULL, sz);
}
static inline WCHAR *xstrdupW(const WCHAR *str)
{
WCHAR *ret = NULL;
if(str) {
size_t size;
size = (lstrlenW(str)+1)*sizeof(WCHAR);
ret = xalloc(size);
memcpy(ret, str, size);
}
return ret;
}
static inline BOOL ends_with_backslash( const WCHAR *path )
{
return path[0] && path[lstrlenW(path) - 1] == '\\';
}
int evaluate_if_condition(WCHAR *p, WCHAR **command, int *test, int *negate);
/* Data structure to hold context when executing batch files */
typedef struct _BATCH_CONTEXT
{
WCHAR *command; /* The command which invoked the batch file */
LARGE_INTEGER file_position;
WCHAR *batchfileW; /* Name of same */
int shift_count[10]; /* Offset in terms of shifts for %0 - %9 */
struct _BATCH_CONTEXT *prev_context; /* Pointer to the previous context block */
BOOL skip_rest; /* Skip the rest of the batch program and exit */
} BATCH_CONTEXT;
/* Data structure to handle building lists during recursive calls */
struct env_stack
{
BATCH_CONTEXT *context;
struct env_stack *next;
union
{
int stackdepth; /* Only used for pushd and popd */
WCHAR cwd; /* Only used for set/endlocal */
} u;
WCHAR *strings;
BOOL delayedsubst; /* Is delayed substitution in effect */
};
/* Data structure to save setlocal and pushd information */
typedef struct _DIRECTORY_STACK
{
struct _DIRECTORY_STACK *next;
WCHAR *dirName;
WCHAR *fileName;
} DIRECTORY_STACK;
static inline const char *debugstr_for_var(WCHAR ch)
{
static char tmp[16];
if (iswprint(ch))
sprintf(tmp, "%%%lc", ch);
else
sprintf(tmp, "%%[%x]", ch);
return tmp;
}
typedef struct _FOR_CONTEXT
{
struct _FOR_CONTEXT *previous;
WCHAR *variable[128];
} FOR_CONTEXT;
extern FOR_CONTEXT *forloopcontext;
static inline BOOL for_var_is_valid(WCHAR ch) {return ch && ch < ARRAY_SIZE(forloopcontext->variable);}
void WCMD_save_for_loop_context(BOOL reset);
void WCMD_restore_for_loop_context(void);
void WCMD_set_for_loop_variable(unsigned varidx, const WCHAR *value);
/*
* Global variables quals, param1, param2 contain the current qualifiers
* (uppercased and concatenated) and parameters entered, with environment
* variables and batch parameters substitution already done.
*/
extern WCHAR quals[MAXSTRING], param1[MAXSTRING], param2[MAXSTRING];
extern int errorlevel;
extern BATCH_CONTEXT *context;
extern BOOL delayedsubst;
static inline BOOL WCMD_is_in_context(const WCHAR *ext)
{
size_t c_len, e_len;
if (!context) return FALSE;
if (!ext) return TRUE;
c_len = wcslen(context->batchfileW);
e_len = wcslen(ext);
return (c_len > e_len) && !wcsicmp(&context->batchfileW[c_len - e_len], ext);
}
#endif /* !RC_INVOKED */
/*
* Serial nos of builtin commands. These constants must be in step with
* the list of strings defined in wcmd.rc, and WCMD_EXIT *must* always be
* the last one.
*
* Yes it *would* be nice to use an enumeration here, but the Resource
* Compiler won't accept resource IDs from enumerations :-(
*/
#define WCMD_CALL 0
#define WCMD_CD 1
#define WCMD_CHDIR 2
#define WCMD_CLS 3
#define WCMD_COPY 4
/* no longer used slot */
#define WCMD_DATE 6
#define WCMD_DEL 7
#define WCMD_DIR 8
#define WCMD_ECHO 9
#define WCMD_ERASE 10
#define WCMD_FOR 11
#define WCMD_GOTO 12
#define WCMD_HELP 13
#define WCMD_IF 14
#define WCMD_LABEL 15
#define WCMD_MD 16
#define WCMD_MKDIR 17
#define WCMD_MOVE 18
#define WCMD_PATH 19
#define WCMD_PAUSE 20
#define WCMD_PROMPT 21
#define WCMD_REM 22
#define WCMD_REN 23
#define WCMD_RENAME 24
#define WCMD_RD 25
#define WCMD_RMDIR 26
#define WCMD_SET 27
#define WCMD_SHIFT 28
#define WCMD_START 29
#define WCMD_TIME 30
#define WCMD_TITLE 31
#define WCMD_TYPE 32
#define WCMD_VERIFY 33
#define WCMD_VER 34
#define WCMD_VOL 35
#define WCMD_ENDLOCAL 36
#define WCMD_SETLOCAL 37
#define WCMD_PUSHD 38
#define WCMD_POPD 39
#define WCMD_ASSOC 40
#define WCMD_COLOR 41
#define WCMD_FTYPE 42
#define WCMD_MORE 43
#define WCMD_CHOICE 44
#define WCMD_MKLINK 45
#define WCMD_CHGDRIVE 46
/* Must be last in list */
#define WCMD_EXIT 47
/* Some standard messages */
extern WCHAR anykey[];
extern WCHAR version_string[];
/* Translated messages */
#define WCMD_ALLHELP 1000
#define WCMD_CONFIRM 1001
#define WCMD_YES 1002
#define WCMD_NO 1003
#define WCMD_NOASSOC 1004
#define WCMD_NOFTYPE 1005
#define WCMD_OVERWRITE 1006
#define WCMD_MORESTR 1007
#define WCMD_TRUNCATEDLINE 1008
#define WCMD_NYI 1009
#define WCMD_NOARG 1010
#define WCMD_SYNTAXERR 1011
#define WCMD_FILENOTFOUND 1012
#define WCMD_NOCMDHELP 1013
#define WCMD_NOTARGET 1014
#define WCMD_CURRENTDATE 1015
#define WCMD_CURRENTTIME 1016
#define WCMD_NEWDATE 1017
#define WCMD_NEWTIME 1018
#define WCMD_MISSINGENV 1019
#define WCMD_READFAIL 1020
#define WCMD_CALLINSCRIPT 1021
#define WCMD_ALL 1022
#define WCMD_DELPROMPT 1023
#define WCMD_ECHOPROMPT 1024
#define WCMD_VERIFYPROMPT 1025
#define WCMD_VERIFYERR 1026
#define WCMD_ARGERR 1027
#define WCMD_VOLUMESERIALNO 1028
#define WCMD_VOLUMEPROMPT 1029
#define WCMD_ANYKEY 1031
#define WCMD_CONSTITLE 1032
#define WCMD_VERSION 1033
#define WCMD_MOREPROMPT 1034
#define WCMD_LINETOOLONG 1035
#define WCMD_VOLUMELABEL 1036
#define WCMD_VOLUMENOLABEL 1037
#define WCMD_YESNO 1038
#define WCMD_YESNOALL 1039
#define WCMD_NO_COMMAND_FOUND 1040
#define WCMD_DIVIDEBYZERO 1041
#define WCMD_NOOPERAND 1042
#define WCMD_NOOPERATOR 1043
#define WCMD_BADPAREN 1044
#define WCMD_BADHEXOCT 1045
#define WCMD_FILENAMETOOLONG 1046
#define WCMD_BADTOKEN 1047
#define WCMD_ENDOFLINE 1048
#define WCMD_ENDOFFILE 1049
|