/* Copyright (C) 2015 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 2002 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 2001 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 2000 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1998 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1996 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */ #include #include #include #include #include #include #include #include #include #include #include #include #define ds _my_ds() static void * c1xmalloc(size_t s) { void *q = malloc(s); if (q == 0) { #define err(x) _write(STDERR_FILENO, x, sizeof(x)-1) err("No memory to gather arguments\r\n"); _exit(1); } return q; } static int far_strlen(int selector, int linear_addr) { int save=linear_addr; _farsetsel(selector); while (_farnspeekb(linear_addr)) linear_addr++; return linear_addr - save; } static int atohex(char *s) { int rv = 0; while (*s) { int v = *s - '0'; if (v > 9) v -= 7; v &= 15; /* in case it's lower case */ rv = rv*16 + v; s++; } return rv; } typedef struct Arg { char *arg; char **arg_globbed; struct ArgList *arg_file; struct Arg *next; int was_quoted; } Arg; typedef struct ArgList { int argc; Arg **argv; } ArgList; static Arg *new_arg(void) { Arg *a = (Arg *)c1xmalloc(sizeof(Arg)); memset(a, 0, sizeof(Arg)); return a; } static void delete_arglist(ArgList *al); static void delete_arg(Arg *a) { if (a->arg) free(a->arg); if (a->arg_globbed) { int i; for (i=0; a->arg_globbed[i]; i++) free(a->arg_globbed[i]); free(a->arg_globbed); } if (a->arg_file) delete_arglist(a->arg_file); free(a); } static ArgList * new_arglist(int count) { ArgList *al = (ArgList *)c1xmalloc(sizeof(ArgList)); al->argc = count; al->argv = (Arg **)c1xmalloc((count+1)*sizeof(Arg *)); memset(al->argv, 0, (count+1)*sizeof(Arg *)); return al; } static void delete_arglist(ArgList *al) { int i; for (i=0; iargc; i++) delete_arg(al->argv[i]); free(al->argv); free(al); } static char * parse_arg(char *bp, char *last, int unquote, size_t *len, int *was_quoted) { char *ep = bp, *epp = bp; int quote=0; while ((quote || !isspace(*(unsigned char *)ep)) && ep < last) { if (quote && *ep == quote) { quote = 0; if (!unquote) *epp++ = *ep; ep++; } else if (!quote && (*ep == '\'' || *ep == '"')) { quote = *ep++; if (!unquote) *epp++ = quote; } else if (*ep == '\\' && strchr("'\"", ep[1]) && ep < last-1) { if (!unquote) *epp++ = *ep; ep++; *epp++ = *ep++; /* *was_quoted = 1; - This makes no sense. */ } else { if ((quote && (strchr("[?*", *ep) || strncmp(ep, "...", 3) == 0)) && unquote) *was_quoted = 1; *epp++ = *ep++; } } *len = epp - bp; return ep; } static ArgList * parse_bytes(char *bytes, int length, int unquote) { int largc, i; Arg *a, **anext, *afirst; ArgList *al; char *bp=bytes, *ep, *last=bytes+length; anext = &afirst; largc = 0; while (bpwas_quoted)); anext = &(a->next); largc++; a->arg = (char *)c1xmalloc(arg_len+1); memcpy(a->arg, bp, arg_len); a->arg[arg_len] = 0; bp = ep+1; } al = new_arglist(largc); for (i=0, a=afirst; inext) al->argv[i] = a; return al; } /* parse the output from 'find -print0' */ static ArgList * parse_print0(char *bytes, int length) { int largc, i; Arg *a, **anext, *afirst; ArgList *al; char *bp=bytes, *ep, *last=bytes+length; anext = &afirst; largc = 0; while (bpwas_quoted = 1; anext = &(a->next); largc++; a->arg = (char *)c1xmalloc(arg_len+1); memcpy(a->arg, ep, arg_len); a->arg[arg_len] = 0; } al = new_arglist(largc); for (i=0, a=afirst; inext) al->argv[i] = a; return al; } static int count_args(ArgList *al) { int i, r=0; for (i=0; iargc; i++) { int j; if (al->argv[i]->arg_globbed) { for (j=0; al->argv[i]->arg_globbed[j]; j++); r += j; } else if (al->argv[i]->arg_file) { r += count_args(al->argv[i]->arg_file); } else { r++; } } return r; } static char ** fill_args(char **largv, ArgList *al) { int i; for (i=0; iargc; i++) { int j; if (al->argv[i]->arg_globbed) { for (j=0; al->argv[i]->arg_globbed[j]; j++) { *largv++ = al->argv[i]->arg_globbed[j]; al->argv[i]->arg_globbed[j] = 0; } } else if (al->argv[i]->arg_file) { largv = fill_args(largv, al->argv[i]->arg_file); } else { *largv++ = al->argv[i]->arg; al->argv[i]->arg = 0; } } return largv; } static void expand_response_files(ArgList *al) { int i, f; for (i=0; iargc; i++) { if (! al->argv[i]->was_quoted && al->argv[i]->arg[0] == '@') if ((f = _open(al->argv[i]->arg+1, O_RDONLY)) >= 0) { char *bytes; int len, st_size; st_size = lseek(f, 0L, SEEK_END); lseek(f, 0L, SEEK_SET); if (st_size < 0) st_size = 0; bytes = (char *)c1xmalloc(st_size+1); len = _read(f, bytes, st_size); if (len < 0) len = 0; _close(f); /* if the last character is ^Z, remove it */ if (len > 0 && bytes[len-1] == 0x1a) len--; /* assume 'find -print0' if the last char is a '\0' */ if (len > 0 && bytes[len-1] == '\0') al->argv[i]->arg_file = parse_print0(bytes, len); else al->argv[i]->arg_file = parse_bytes(bytes, len, (_crt0_startup_flags & _CRT0_FLAG_KEEP_QUOTES) == 0); expand_response_files(al->argv[i]->arg_file); free(bytes); } } } static void expand_wildcards(ArgList *al) { int i; for (i=0; iargc; i++) { if (al->argv[i]->arg_file) expand_wildcards(al->argv[i]->arg_file); else if (!(al->argv[i]->was_quoted) && (strchr(al->argv[i]->arg, '*') || strchr(al->argv[i]->arg, '?'))) { al->argv[i]->arg_globbed = __crt0_glob_function(al->argv[i]->arg); } } } extern char __PROXY[]; /* defined on crt0/crt1.c */ extern size_t __PROXY_LEN; void __crt0_setup_arguments(void) { ArgList *arglist; char *argv0; int prepend_argv0 = 1; int should_expand_wildcards = 1; char *proxy_v = 0; /* ** first, figure out what to pass for argv[0] */ { int i; char *ap, *ls, *fc; /* char newbase[14]; */ if (_crt0_startup_flags & _CRT0_FLAG_DROP_DRIVE_SPECIFIER) if (__dos_argv0[1] == ':') __dos_argv0 += 2; ls = __dos_argv0; for (ap=__dos_argv0; *ap; ap++) if (*ap == ':' || *ap == '\\' || *ap == '/') ls = ap + 1; fc = ls; #if 0 /* We never do this! Only the stub uses this field */ if (_stubinfo->basename[0]) { for (i=0; i<8 && _stubinfo->basename[i]; i++) newbase[i] = _stubinfo->basename[i]; newbase[i++] = '.'; newbase[i++] = 'E'; newbase[i++] = 'X'; newbase[i++] = 'E'; newbase[i++] = 0; fc = newbase; } #endif if (_stubinfo->argv0[0]) { fc = _stubinfo->argv0; } argv0 = (char *)malloc(ls-__dos_argv0+strlen(fc)+1); if (ls == __dos_argv0) strlcpy(argv0, fc, 16+1); else { memcpy(argv0, __dos_argv0, ls-__dos_argv0); argv0[ls-__dos_argv0] = '\0'; strncat(argv0, fc, 16); } for (i=0; (fc == _stubinfo->argv0)?(ipsp_selector, 128), 128); if (doscmd[0] != 127 || ((cmdline = getenv("CMDLINE")) == NULL)) arglist = parse_bytes(doscmd + 1, doscmd[0] & 0x7f, (_crt0_startup_flags & _CRT0_FLAG_KEEP_QUOTES)==0); else { /* Command line is in the environment variable CMDLINE. */ char stop_token; /* Skip over the name of the program. */ if ((*cmdline == '\"') || (*cmdline == '\'')) stop_token = *cmdline; else stop_token = ' '; /* FIXME: what about the case of 'foo\'bar'? */ while (*cmdline != stop_token) ++cmdline; ++cmdline; /* Skip over the stop token. */ arglist = parse_bytes(cmdline, strlen(cmdline), (_crt0_startup_flags & _CRT0_FLAG_KEEP_QUOTES) == 0); } } /* ** Check for !proxy. ** ** If there is " !proxy" (note the leading blank!) in the environ, ** use it instead of what DOS command line tells. This is the method ** v2.01 and later uses to pass long command lines from `system'; ** these should be passed through wildcard expansion unless quoted. */ if ((proxy_v = getenv(__PROXY))) { char proxy_line[50]; /* need only 34 */ size_t plen = strlen(proxy_v); strncpy(proxy_line, __PROXY, __PROXY_LEN + 1); /* copy " !proxy" */ proxy_line[__PROXY_LEN] = ' '; strncpy(proxy_line + __PROXY_LEN+1, proxy_v, plen+1); /* copy value */ delete_arglist(arglist); arglist = parse_bytes(proxy_line, plen+__PROXY_LEN+1, 0); } if (arglist->argc > 3 && strcmp(arglist->argv[0]->arg, __PROXY+1) == 0) { int argv_seg, argv_ofs, i; unsigned short *rm_argv; __crt0_argc = atohex(arglist->argv[1]->arg); argv_seg = atohex(arglist->argv[2]->arg); argv_ofs = atohex(arglist->argv[3]->arg); delete_arglist(arglist); rm_argv = (unsigned short *)alloca(__crt0_argc*sizeof(unsigned short)); fmemcpy2(rm_argv, DP(_dos_ds, argv_seg*16+argv_ofs), __crt0_argc*sizeof(unsigned short)); arglist = new_arglist(__crt0_argc); if (proxy_v) should_expand_wildcards = 1; else should_expand_wildcards = 0; for (i=0; i<__crt0_argc; i++) { int al = far_strlen(_dos_ds, argv_seg*16 + rm_argv[i]); arglist->argv[i] = new_arg(); arglist->argv[i]->arg = (char *)c1xmalloc(al+1); fmemcpy2(arglist->argv[i]->arg, DP(_dos_ds, argv_seg*16 + rm_argv[i]), al+1); if (proxy_v && !(_crt0_startup_flags & _CRT0_FLAG_KEEP_QUOTES)) { size_t ln; char *lastc = arglist->argv[i]->arg + al; parse_arg(arglist->argv[i]->arg, lastc, 1, &ln, &(arglist->argv[i]->was_quoted)); arglist->argv[i]->arg[ln] = '\0'; } } prepend_argv0 = 0; } #if 0 /* This method will break under DPMI 1.0, because descriptor tables are private to a process there. Disabled. */ else if (arglist->argc > 3 && strcmp(arglist->argv[0]->arg, "!proxy2") == 0) { int argv_sel, argv_ofs, i; unsigned long *pm_argv; __crt0_argc = atohex(arglist->argv[1]->arg); argv_sel = atohex(arglist->argv[2]->arg); argv_ofs = atohex(arglist->argv[3]->arg); delete_arglist(arglist); pm_argv = (unsigned long *)alloca(__crt0_argc*sizeof(unsigned long)); movedata(argv_sel, argv_ofs, ds, (int)pm_argv, __crt0_argc*sizeof(unsigned long)); arglist = new_arglist(__crt0_argc); for (i=0; i<__crt0_argc; i++) { int al = far_strlen(argv_sel, pm_argv[i]); arglist->argv[i] = new_arg(); arglist->argv[i]->arg = (char *)c1xmalloc(al+1); movedata(argv_sel, pm_argv[i], ds, (int)(arglist->argv[i]->arg), al+1); } prepend_argv0 = 0; should_expand_wildcards = 0; } #endif /* ** Now, expand response files */ if (!(_crt0_startup_flags & _CRT0_FLAG_DISALLOW_RESPONSE_FILES)) expand_response_files(arglist); /* ** Now, expand wildcards */ if (should_expand_wildcards) expand_wildcards(arglist); __crt0_argc = prepend_argv0 + count_args(arglist); __crt0_argv = (char **)c1xmalloc((__crt0_argc+1) * sizeof(char *)); if (prepend_argv0) __crt0_argv[0] = argv0; *fill_args(__crt0_argv+prepend_argv0, arglist) = 0; }