Message-Id: <199909090114.BAA150418@out2.ibm.net> From: "Mark E." To: djgpp-workers AT delorie DOT com Date: Wed, 8 Sep 1999 21:14:55 -0400 MIME-Version: 1.0 Content-type: text/plain; charset=US-ASCII Content-transfer-encoding: 7BIT Subject: long command line patch v2 X-mailer: Pegasus Mail for Win32 (v3.12a) Reply-To: djgpp-workers AT delorie DOT com This is the second version of the patch that enables use of long command lines with Win32 and 4DOS. This version detects PE-COFF Win32 executables, and the 4DOS shell and it tries to detect a long cmdline capable of command.com. *** src/libc/crt0/c1args.c.orig Tue Jun 29 11:49:58 1999 --- src/libc/crt0/c1args.c Sun Sep 5 16:16:44 1999 *************** __crt0_setup_arguments(void) *** 398,408 **** */ { char doscmd[128]; movedata(_stubinfo->psp_selector, 128, ds, (int)doscmd, 128); ! arglist = parse_bytes(doscmd+1, doscmd[0] & 0x7f, ! (_crt0_startup_flags & _CRT0_FLAG_KEEP_QUOTES) == 0); } ! /* ** Check for !proxy. ** --- 398,430 ---- */ { char doscmd[128]; + char *cmdline; + movedata(_stubinfo->psp_selector, 128, ds, (int)doscmd, 128); ! if ((doscmd[0] & 0x7f) != 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 = ' '; ! ! 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. ** *** src/libc/dos/process/chkv2prg.c.orig Thu Jul 22 12:59:50 1999 --- src/libc/dos/process/chkv2prg.c Wed Sep 8 13:55:54 1999 *************** const _v2_prog_type *_check_v2_prog_inte *** 77,83 **** --- 77,107 ---- char go32stub[9]; unsigned long coff_start = (unsigned long)header[2]*512L; unsigned long exe_start; + unsigned long real_exe_start; type.exec_format = _V2_EXEC_FORMAT_EXE; + + if (lseek(pf, 0x3c, SEEK_SET) != 0x3c) + return NULL; + if (read(pf, &real_exe_start, 4) != 4) + return NULL; + + /* Detect if EXE is really a PE-COFF EXE. */ + if (real_exe_start != 0) + { + unsigned long magic; + + if (lseek(pf, real_exe_start, SEEK_SET) != real_exe_start) + return NULL; + if (read(pf, &magic, 4) != 4) + return NULL; + + if (magic == 0x00004550) /* PE */ + { + type.object_format = _V2_OBJECT_FORMAT_PE_COFF; + return &type; + } + } + if (header[1]) coff_start += (long)header[1] - 512L; exe_start = (unsigned long)header[4]*16L; *** src/libc/dos/process/dosexec.c.orig Tue Jun 29 11:50:10 1999 --- src/libc/dos/process/dosexec.c Wed Sep 8 19:34:42 1999 *************** *** 15,20 **** --- 15,21 ---- #include #include #include + #include #include #include #include *************** direct_exec_tail(const char *program, co *** 163,168 **** --- 164,170 ---- unsigned proglen; int i; unsigned long fcb1_la, fcb2_la, fname_la; + int arg_len; /* This used to just call sync(). But `sync' flushes the disk cache nowadays, and that can slow down the child tremendously, *************** direct_exec_tail(const char *program, co *** 206,215 **** dosmemput(progname, proglen, program_la); /* The command-line tail. */ ! arg_header[0] = strlen(args); arg_header[1] = '\r'; dosmemput(arg_header, 1, arg_la); ! dosmemput(args, strlen(args), arg_la+1); dosmemput(arg_header+1, 1, arg_la+1+strlen(args)); /* The 2 FCBs. Some programs (like XCOPY from DOS 6.x) need them. */ --- 208,222 ---- dosmemput(progname, proglen, program_la); /* The command-line tail. */ ! arg_len = strlen(args); ! arg_header[0] = arg_len; arg_header[1] = '\r'; + + if (arg_len > 126) + arg_len = 126; + dosmemput(arg_header, 1, arg_la); ! dosmemput(args, arg_len, arg_la+1); dosmemput(arg_header+1, 1, arg_la+1+strlen(args)); /* The 2 FCBs. Some programs (like XCOPY from DOS 6.x) need them. */ *************** static int direct_exec(const char *progr *** 458,466 **** for (i=1; argv[i]; i++) arglen += 2*strlen(argv[i]) + 1 + 2; ! args = (char *)alloca(arglen+1); argp = args; ! for (i=1; argv[i]; i++) { int quoted = 0; const char *p = argv[i]; --- 465,477 ---- for (i=1; argv[i]; i++) arglen += 2*strlen(argv[i]) + 1 + 2; ! #if 0 ! args = (char *)alloca(arglen + 1); ! #else ! args = (char *)alloca((arglen <= CMDLEN_LIMIT) ? (arglen + 1) : (CMDLEN_LIMIT + 2)); ! #endif argp = args; ! for (i = 1; argv[i]; i++) { int quoted = 0; const char *p = argv[i]; *************** static int direct_exec(const char *progr *** 511,516 **** --- 522,617 ---- return direct_exec_tail(program, args, envp, 0, 2); } + static int direct_pe_exec(const char *program, char **argv, char **envp) + { + int i, arglen; + char *args, *argp, *varp; + int need_quote = !__dosexec_in_system; + int unescape_quote = __dosexec_in_system; + const char cmdline_str[] = "CMDLINE="; + const unsigned int cmdline_str_len = sizeof(cmdline_str) - 1; + + /* PROGRAM can be a shell which expects a single argument + (beyond the /c or -c switch) that is the entire command + line. With some shells, we must NOT quote that command + line, because that will confuse the shell. + + The hard problem is to know when PROGRAM names a shell + that doesn't like its command line quoted... */ + + if (need_quote + && argv[1] && !strcmp (argv[1], "/c") + && argv[2] && !argv[3] + && _is_dos_shell (program)) + need_quote = 0; + + if (unescape_quote && _is_unixy_shell (program)) + unescape_quote = 0; + + arglen = 0; + for (i=1; argv[i]; i++) + arglen += 2*strlen(argv[i]) + 1 + 2; + + varp = (char *)alloca(cmdline_str_len + arglen + 1); + strcpy(varp, cmdline_str); + args = varp + cmdline_str_len; + + argp = args; + for (i = 1; argv[i]; i++) + { + int quoted = 0; + const char *p = argv[i]; + + *argp++ = ' '; + /* If invoked by `spawnXX' or `execXX' functions, we need to + quote arguments which include whitespace, so they end up + as a single argument on the child side. + We will invoke PROGRAM directly by DOS Exec function (not + through COMMAND.COM), therefore no need to quote characters + special only to COMMAND.COM. + We also assume that DJGPP programs aren't invoked through + here, so a single quote `\'' is also not special. The only + programs other than DJGPP that treat a single quote specially + are Unix-like shells, but whoever uses them should know to + escape the quotes himself. */ + if (need_quote && strpbrk(p, " \t") != 0) + { + *argp++ = '"'; + quoted = 1; + } + while (*p) + { + if (*p == '"' && (quoted || need_quote)) + *argp++ = '\\'; + /* Most non-DJGPP programs don't treat `\'' specially, + but our `system' requires we always escape it, so + we should undo the quoting here. */ + else if (*p == '\\' && p[1] == '\'' && unescape_quote) + p++; + *argp++ = *p++; + } + if (quoted) + *argp++ = '"'; + } + *argp = 0; + + if (argp - args > CMDLEN_LIMIT) + { + /* The command line is too long to pass directly. Put the entire + contents of the command line into the CMDLINE variable and + set the command line length to 127. direct_exec_tail will + take care of the final details. */ + + putenv(varp); + args[127] = 0; + } + + tbuf_beg = tbuf_ptr = __tb; + tbuf_len = _go32_info_block.size_of_transfer_buffer; + tbuf_end = tbuf_beg + tbuf_len - 1; + return direct_exec_tail(program, args, envp, 0, 2); + } + static int go32_exec(const char *program, char **argv, char **envp) { const _v2_prog_type * type; *************** static int go32_exec(const char *program *** 545,552 **** --- 646,658 ---- if (!is_coff) { if (type->exec_format == _V2_EXEC_FORMAT_EXE) + { + if (type->object_format != _V2_OBJECT_FORMAT_PE_COFF) return direct_exec(program, argv, envp); else + return direct_pe_exec(program, argv, envp); + } + else return __dosexec_command_exec (program, argv, envp); } *************** __dosexec_command_exec(const char *progr *** 709,714 **** --- 815,821 ---- int cmdlen; int i; int was_quoted = 0; /* was the program name quoted? */ + int cmdline_len; /* Add spare space for possible quote characters. */ cmdlen = strlen(program) + 4 + 2; *************** __dosexec_command_exec(const char *progr *** 795,804 **** comspec = "c:\\command.com"; /* FIXME: 126-char limit below isn't LFN-clean. */ ! if (strlen(cmdline) > CMDLEN_LIMIT + 1) { ! cmdline[CMDLEN_LIMIT+1] = '\0'; errno = E2BIG; } tbuf_beg = tbuf_ptr = __tb; --- 902,957 ---- comspec = "c:\\command.com"; /* FIXME: 126-char limit below isn't LFN-clean. */ ! if ((cmdline_len = strlen(cmdline)) > CMDLEN_LIMIT + 1) { ! static const char *comspec_cache = NULL; ! static unsigned int cmdline_limit = 126; ! ! if (comspec != comspec_cache) ! { ! char *base; ! comspec_cache = comspec; ! ! base = basename(comspec_cache); ! ! if ((stricmp (base, "command.com") == 0) && (_osmajor >= 7)) ! cmdline_limit = (unsigned int)(-1); /* No limit for MS-DOS 7. */ ! else if ((stricmp (base, "4dos.com") == 0) ! || (stricmp (base, "ndos.com") == 0)) ! cmdline_limit = 255; /* 255 char limit for [4N]DOS. */ ! else ! cmdline_limit = 126; /* 126 char limit otherwise. */ ! } ! ! if (cmdline_len > cmdline_limit) ! { ! cmdline[CMDLEN_LIMIT + 1] = '\0'; errno = E2BIG; + } + else + { + int comspec_len = strlen(comspec); + char *cmdline_var = (char *)alloca(comspec_len + 1 + cmdline_len + 1); + char *ptr = cmdline_var; + + /* Dump into CMDLINE the name of the shell to execute and the + entire command line. */ + strcpy(cmdline_var, "CMDLINE="); + ptr += sizeof("CMDLINE=") - 1; + + strcpy(ptr, comspec); + ptr += comspec_len; + + *ptr = ' '; + ++ptr; + + strcpy (ptr, cmdline); + + putenv (cmdline_var); + /* Set the command line to 127 characters long to signal + direct_exec_tail that it has a long command line. */ + cmdline[CMDLEN_LIMIT + 2] = '\0'; + } } tbuf_beg = tbuf_ptr = __tb; *** src/libc/ansi/stdlib/system.c.orig Tue Jun 29 11:49:46 1999 --- src/libc/ansi/stdlib/system.c Wed Sep 8 14:23:46 1999 *************** *** 15,20 **** --- 15,21 ---- #include #include #include + #include #include #include #include /* for fileno() */ *************** _shell_command (const char *prog, const *** 92,101 **** } else if (_is_dos_shell (shell)) { ! char *cmd_tail = (char *)alloca (3 + strlen (prog) + 1 ! + strlen (cmdline) + 1); const char *s = prog; char *d = cmd_tail + 3; strcpy (cmd_tail, "/c "); while ((*d = *s++) != 0) --- 93,103 ---- } else if (_is_dos_shell (shell)) { ! int cmd_tail_alloc = 3 + strlen(prog) + 1 + strlen (cmdline) + 1; ! char *cmd_tail = (char *)alloca(cmd_tail_alloc); const char *s = prog; char *d = cmd_tail + 3; + int cmd_tail_len; strcpy (cmd_tail, "/c "); while ((*d = *s++) != 0) *************** _shell_command (const char *prog, const *** 113,126 **** strcpy (d, cmdline); } ! /* [4N]DOS.COM can support upto 255 chars per command line. ! They lose that feature here, because there's no simple ! way to pass long command lines to DOS function 4Bh (Exec) ! which `_dos_exec' summons. */ ! if (strlen (cmd_tail) > 126) { ! errno = E2BIG; ! return emiterror ("Command line too long.", 0); } else return _dos_exec (shell, cmd_tail, environ); --- 115,174 ---- strcpy (d, cmdline); } ! /* [4N]DOS.COM can support up to 255 chars per command line. ! Win32 supports a much longer command line. */ ! if ((cmd_tail_len = strlen (cmd_tail)) > 126) { ! static unsigned int cmd_tail_limit = 0; ! static char *shell_ptr = NULL; ! ! if (shell != shell_ptr) ! { ! char *base; ! ! shell_ptr = shell; ! base = basename(shell_ptr); ! ! if ((stricmp(base, "command.com") == 0) && (_osmajor >= 7) ! && (stricmp(_os_flavor, "ms-dos") == 0)) ! cmd_tail_limit = (unsigned int)(-1); /* No limit for MSDOS 7. */ ! else if ((stricmp(base, "4dos.com") == 0) ! || (stricmp(base, "ndos.com") == 0)) ! cmd_tail_limit = 255; /* 255 char limit for [4N]DOS. */ ! else ! cmd_tail_limit = 126; /* 126 char limit otherwise. */ ! } ! ! if (cmd_tail_len > cmd_tail_limit) ! { ! errno = E2BIG; ! return emiterror ("Command line too long.", 0); ! } ! else ! { ! int shell_len = strlen (shell); ! char *cmdline_var = (char *)alloca (shell_len + 1 + cmd_tail_len + 8); ! char *ptr = cmdline_var; ! ! /* Dump into CMDLINE the shell to execute and the ! entire command line. */ ! strcpy(cmdline_var, "CMDLINE="); ! ptr += sizeof("CMDLINE=") - 1; ! ! strcpy(ptr, shell); ! ptr += shell_len; ! ! *ptr = ' '; ! ++ptr; ! ! strcpy (ptr, cmd_tail); ! ! putenv (cmdline_var); ! /* Set the command line to 127 characters long to signal ! direct_exec_tail that it has a long command line. */ ! cmd_tail[127] = '\0'; ! return _dos_exec (shell, cmd_tail, environ); ! } } else return _dos_exec (shell, cmd_tail, environ); --- Mark Elbrecht, snowball3 AT bigfoot DOT com http://snowball.frogspace.net/