From: cgf AT cygnus DOT com (Christopher Faylor) Subject: Re: relative path script 21 Jan 1999 14:25:24 -0800 Message-ID: <19990121171200.A19658.cygnus.cygwin32.developers@cygnus.com> References: Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii To: Kazuhiro Fujieda , cygwin32-developers AT cygnus DOT com Thanks for noticing these inconsistencies and for the patch. Your patch didn't quite do it for me. This one below did, however. This is against the last snapshot. Sorry but I forgot to save my previous version before making this change. This should incorporate the majority of your changes, however. -chris On Thu, Jan 21, 1999 at 07:18:42PM +0900, Kazuhiro Fujieda wrote: >>>> On Wed, 20 Jan 1999 14:46:59 -0500 >>>> Christopher Faylor said: > >> I've actually been checking this out in my spare time. I've >> rewritten the section of code that deals with this. It's a major >> change, so I could have broken something, but here it is. >(snip) > >There is a bug in this patch. The argument parser can't convert >arguments rightly. For example, it convert `foo "bar" foo' to >`"foo """bar" foo"'. This should be `"foo ""bar"" foo"'. > >Then, It should check not only ' ' but also '\n', '\r' and '\t' >in order to be consistent with build_argv() in dcrt0.cc. >[snip] Index: spawn.cc =================================================================== RCS file: /cvs/cvsfiles/devo/winsup/spawn.cc,v retrieving revision 1.99 diff -u -p -r1.99 spawn.cc --- spawn.cc 1999/01/17 06:08:43 1.99 +++ spawn.cc 1999/01/21 22:10:42 @@ -167,12 +167,56 @@ iscmd (const char *argv0, const char *wh (n == 0 || isdirsep (argv0[n - 1])); } +class linebuf +{ + size_t alloc; +public: + size_t ix; + char *buf; + linebuf () + { + ix = 0; + alloc = 0; + buf = NULL; + } + ~linebuf () {if (buf) free (buf);} + void add (const char *what, int len); + void add (const char *what) {add (what, strlen (what));} + void prepend (const char *what, int len); +}; + +void +linebuf::add (const char *what, int len) +{ + size_t newix; + if ((newix = ix + len) >= alloc) + { + alloc += MAX_PATH * 2; + buf = (char *) realloc (buf, alloc + 1); + } + memcpy (buf + ix, what, len); + ix = newix; +} + +void +linebuf::prepend (const char *what, int len) +{ + size_t newix; + if ((newix = ix + len) >= alloc) + { + alloc += MAX_PATH * 2; + buf = (char *) realloc (buf, alloc + 1); + } + memmove (buf + len, buf, strlen (buf)); + memcpy (buf, what, len); + ix += len; +} + int spawn_guts (HANDLE hToken, const char * prog_arg, const char *const *argv, const char *const envp[], pinfo *child, int mode) { int i; - char *copy; BOOL rc; int argc; @@ -196,36 +240,23 @@ spawn_guts (HANDLE hToken, const char * /* CreateProcess takes one long string that is the command line (sigh). We need to quote any argument that has whitespace or embedded "'s. */ - int clen, last_clen; for (argc = 0; argv[argc]; argc++) -debug_printf ("argv[%d] = '%s'", argc, argv[argc]); /* nothing */; - /* See how much space we need. Note that "'s are doubled. */ - clen = 0; - if (argc == 0) - { - /* Hmm - nasty - no prog name, - fill one up for us */ - /* FIXME: what about argv[1]??? */ - small_printf ("spawn_guts: NO ARG0"); - char **tmp; - tmp = (char **) &argv[0]; - *tmp = (char *) prog_arg; - } - char *real_path; char real_path_buf[MAX_PATH]; + + sig_protect (starting_here, 0); + linebuf one_line; + if (argc == 3 && argv[1][0] == '/' && argv[1][1] == 'c' && (iscmd (argv[0], "command.com") || iscmd (argv[0], "cmd.exe"))) { - copy = (char *) alloca (strlen (argv[0]) + strlen (argv[1]) + - strlen (argv[2]) + 3); - strcpy (copy, argv[0]); - strcat (copy, " "); - strcat (copy, argv[1]); - strcat (copy, " "); - strcat (copy, argv[2]); + one_line.add (argv[0]); + one_line.add (" "); + one_line.add (argv[1]); + one_line.add (" "); real_path = NULL; strcpy (real_path_buf, argv[0]); goto skip_arg_parsing; @@ -233,83 +264,21 @@ debug_printf ("argv[%d] = '%s'", argc, a real_path = real_path_buf; - for (argc = 0; argv[argc]; argc++) - { - int needquote = 0; - const char *str; - /* Zero length args need quotes. */ - if (!argv[argc][0]) - needquote = 1; - - for (str = argv[argc]; *str; str++) - { - if (*str == '"') - clen++; - if (issep (*str) || *str == '"') - needquote = 1; - clen++; - } - if (needquote) - clen += 2; /* space for quotes */ - clen++; /* space for separating space */ - } - - if ((copy = (char *) alloca (clen + 1)) == NULL) - { - syscall_printf ("couldn't allocate %d characters", clen + 1); - set_errno (ENAMETOOLONG); - return -1; - } - syscall_printf ("total arg length %d, copy %p", clen, copy); - last_clen = clen + 1; - clen = 0; - - for (i = 0, argc--; i <= argc; i++) - { - int needquote = 0; - const char *str; - - /* Extra space after `in' intentional. */ - syscall_printf ("argv[%d] in `%s'", i, argv[i]); - - if (!argv[i][0]) - needquote = 1; - - for (str = argv[i]; *str; str++) - if (issep (*str) || *str == '"') - needquote = 1; - - if (needquote) - copy[clen++] = '"'; - - for (str = argv[i]; *str; str++) - { - if (*str=='"') - copy[clen++] = '"'; - copy[clen++] = *str; - } - - if (needquote) - copy[clen++] = '"'; - copy[clen++] = (i == argc) ? '\0' : ' '; - syscall_printf ("argv[%d] out`%s'", i, argv[i]); - } - if (clen > last_clen) - system_printf ("Hey! clen %d, last_clen %d", clen, last_clen); - const char *saved_prog_arg; + const char *newargv0, **firstarg; const char *ext; - saved_prog_arg = prog_arg; if ((ext = perhaps_suffix (prog_arg, real_path)) == NULL) { set_errno (ENOENT); return -1; } - /* Check if it's a script. */ + saved_prog_arg = prog_arg; + newargv0 = argv[0]; + firstarg = &newargv0; - /* if the file name ends in either .exe, .com, .bat, or .cmd we assume + /* If the file name ends in either .exe, .com, .bat, or .cmd we assume that it is NOT a script file */ while (*ext == '\0') { @@ -355,46 +324,42 @@ debug_printf ("argv[%d] = '%s'", argc, a else { pgm = buf + 2; - pgm += strspn(pgm, " \t"); + pgm += strspn (pgm, " \t"); for (ptr = pgm, arg1 = NULL; *ptr && *ptr != '\r' && *ptr != '\n'; ptr++) if (!arg1 && (*ptr == ' ' || *ptr == '\t')) { - *ptr = '\0'; - do - arg1 = ++ptr; - while (*ptr && (*ptr == ' ' || *ptr == '\t')); - ptr--; + arg1 = ptr; + *arg1++ = '\0'; + ptr = (arg1 += strspn (arg1, " \t")); } *ptr = '\0'; - if (arg1 == NULL) - arg1 = ptr; } - char prog_arg1[MAX_PATH]; + char buf2[MAX_PATH + 1]; /* pointers: * pgm interpreter name * arg1 optional string * ptr end of string */ - find_exec (pgm, (char *) real_path, "PATH=", 0, &ext); - cygwin_conv_to_posix_path (real_path, prog_arg1); - char *f = (char *) alloca (strlen (copy) + strlen (prog_arg1) + - (ptr - arg1) + 8 + strlen (saved_prog_arg)); - strcpy (f, prog_arg1); - if (ptr == arg1) - strcat (f, " "); + + if (!arg1) + one_line.prepend (" ", 1); else { - strcat (f, " \""); - strcat (f, arg1); - strcat (f, "\" "); + one_line.prepend ("\" ", 1); + one_line.prepend (arg1, strlen (arg1)); + one_line.prepend (" \"", 2); } + find_exec (pgm, real_path, "PATH=", 0, &ext); + cygwin_conv_to_posix_path (real_path, buf2); + one_line.prepend (buf2, strlen (buf2)); + /* If script had absolute path, add it to script name now! * This is necessary if script has been found via PATH. * For example, /usr/local/bin/tkman started as "tkman": @@ -404,19 +369,43 @@ debug_printf ("argv[%d] = '%s'", argc, a * but not /usr/local/bin/wish -f tkman! * We don't modify anything, if script has qulified path. */ - if (strpbrk (saved_prog_arg, "\\/") && !isabspath (copy)) + if (firstarg) + *firstarg = saved_prog_arg; + + debug_printf ("prog_arg '%s', copy '%s'", prog_arg, one_line.buf); + firstarg = NULL; + } + + for (; *argv; argv++) + { + char *p = NULL; + const char *a = newargv0 ?: *argv; + + newargv0 = NULL; + int len = strlen (a); + if (len != 0 && !(p = strpbrk (a, " \t\n\r\""))) + one_line.add (a, len); + else { - debug_printf ("getting path of %s, copy %s", saved_prog_arg, buf); - cygwin_split_path (saved_prog_arg, strchr(f, '\0'), buf); - strcat (f, "/"); + one_line.add ("\"", 1); + for (; p; a = p, p = strchr (p, '"')) + { + one_line.add (a, ++p - a); + if (p[-1] == '"') + one_line.add ("\"", 1); + } + if (*a) + one_line.add (a); + one_line.add ("\"", 1); } - - strcat (f, copy); - copy = f; - - debug_printf ("prog_arg '%s', copy '%s'", prog_arg, copy); + one_line.add (" ", 1); } + if (one_line.ix) + one_line.buf[one_line.ix - 1] = '\0'; + else + one_line.add ("", 1); + skip_arg_parsing: PROCESS_INFORMATION pi = {0}; @@ -472,7 +461,7 @@ skip_arg_parsing: /* We print the translated program and arguments here so the user can see what was done to it. */ - syscall_printf ("spawn_guts (%s, %.132s)", real_path, copy); + syscall_printf ("spawn_guts (%s, %.132s)", real_path, one_line.buf); int flags = CREATE_DEFAULT_ERROR_MODE | CREATE_SUSPENDED | GetPriorityClass (GetCurrentProcess ()); @@ -494,8 +483,6 @@ skip_arg_parsing: flags |= DETACHED_PROCESS; } - sig_protect (starting_here, 0); - /* Build windows style environment list */ char *envblock = winenv (envp); @@ -521,7 +508,7 @@ skip_arg_parsing: myself->uid = USHRT_MAX; rc = CreateProcessAsUser (hToken, real_path, /* image name - with full path */ - copy, /* what was passed to exec */ + one_line.buf, /* what was passed to exec */ &sec_all_nih, /* process security attrs */ &sec_all_nih, /* thread security attrs */ TRUE, /* inherit handles from parent */ @@ -533,7 +520,7 @@ skip_arg_parsing: } else rc = CreateProcessA (real_path, /* image name - with full path */ - copy, /* what was passed to exec */ + one_line.buf, /* what was passed to exec */ &sec_all_nih, /* process security attrs */ &sec_all_nih, /* thread security attrs */ TRUE, /* inherit handles from parent */ @@ -557,7 +544,7 @@ skip_arg_parsing: /* We print the original program name here so the user can see that too. */ syscall_printf ("%d = spawn_guts (%s, %.132s)", rc ? pi.dwProcessId : (unsigned int) -1, - prog_arg, copy); + prog_arg, one_line.buf); if (!rc) {