From: cgf AT cygnus DOT com (Christopher Faylor) Subject: Re: relative path script 20 Jan 1999 11:54:54 -0800 Message-ID: <19990120144659.A13057.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 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. If this breaks things for people then I'll just check in Kazuhiro's change while I play with my changes. I'd appreciate it if people could check this out. -chris On Wed, Jan 20, 1999 at 01:20:33PM +0900, Kazuhiro Fujieda wrote: >>>> On 18 Jan 1999 17:06:54 +0900 >>>> Kazuhiro Fujieda said: > >> For example, >> $ echo '#!/bin/sh' > foo >> $ mkdir bar >> $ cd bar >> $ ../foo >> ../../foo: Can't open ../../foo >(snip) > >The following patch fixes this problem. Either script found >through a relative search path or directly specified by a >relative path works fine. >[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/20 19:41:20 @@ -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,44 @@ 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, " \""))) + 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, '"')) + { + if (*p++ == '"') + one_line.add ("\"", 1); + one_line.add (a, p - a); + a = p; + } + if (*a) + one_line.add (a, strlen (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 +462,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 +484,6 @@ skip_arg_parsing: flags |= DETACHED_PROCESS; } - sig_protect (starting_here, 0); - /* Build windows style environment list */ char *envblock = winenv (envp); @@ -521,7 +509,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 +521,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 +545,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) {