dosexec.c
来自「UNIX下SH的实现源码」· C语言 代码 · 共 1,146 行 · 第 1/3 页
C
1,146 行
/* 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 <libc/stubs.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <errno.h>#include <signal.h>#include <limits.h>#include <fcntl.h>#include <unistd.h>#include <process.h>#include <go32.h>#include <dpmi.h>#include <ctype.h>#include <sys/system.h>#include <sys/movedata.h>#include <libc/dosexec.h>#include <libc/unconst.h>#include <libc/dosio.h>#include <libc/farptrgs.h>/* FIXME: this is not LFN-clean. Win95 has a way to pass long command lines, but we don't support it here. */#define CMDLEN_LIMIT 125extern char **environ;int __dosexec_in_system = 0;typedef struct { unsigned short eseg; unsigned short argoff; unsigned short argseg; unsigned short fcb1_off; unsigned short fcb1_seg; unsigned short fcb2_off; unsigned short fcb2_seg;} Execp;static Execp parm;static unsigned long tbuf_ptr;static unsigned long tbuf_beg;static unsigned long tbuf_end;static unsigned long tbuf_len;#if 0static int tbuf_selector;#endifstatic int script_exec(const char *, char **, char **);/* Allocate AMT bytes off the transfer buffer. */static unsigned long talloc(size_t amt){ unsigned long rv = tbuf_ptr; tbuf_ptr += amt; return rv;}/* Make sure we can allocate AMT bytes off the transfer buffer without overflowing it. Return non-zero if we can, zero otherwise. WARNING: This can relocate the data already in the transfer buffer, so all linear addresses which use it should be relative to TBUF_BEG! */static int check_talloc(size_t amt){ int retval = 1; if (tbuf_ptr + amt > tbuf_end) {#if 0 /* Code that reallocs the transfer buffer; currently disabled. */ unsigned long new_tb; unsigned long min_len = tbuf_len + amt; unsigned long max_len = 0x10000; /* 64KB */ int old_selector = tbuf_selector; int max_avail; int e = errno; errno = E2BIG; /* Try to allocate new, larger DOS buffer, upto 64KB. */ if (min_len > max_len) { retval = 0; goto done; } while (tbuf_len <= max_len && tbuf_len < min_len) tbuf_len *= 2; if (tbuf_len < min_len) { retval = 0; goto done; } tbuf_len = (tbuf_len + 15) & 0xffff0; /* round to nearest paragraph */ if ((new_tb = __dpmi_allocate_dos_memory(tbuf_len/16, &max_avail)) == -1) { if (max_avail*16 < min_len || (new_tb = __dpmi_allocate_dos_memory(max_avail, &tbuf_selector)) == -1) { retval = 0; goto done; } tbuf_len = max_avail*16; } else tbuf_selector = max_avail; new_tb *= 16; /* convert to linear address */ movedata (_dos_ds, tbuf_beg, _dos_ds, new_tb, tbuf_ptr - tbuf_beg); tbuf_ptr = new_tb + tbuf_ptr - tbuf_beg; tbuf_beg = new_tb; tbuf_end = tbuf_beg + tbuf_len - 1; errno = e; done: /* Assume caller will return immediately in case of failure to reallocate, so they won't need the old data. */ if (!retval) tbuf_selector = 0; if (old_selector) __dpmi_free_dos_memory(old_selector);#else errno = E2BIG; retval = 0;#endif } return retval;}extern char __PROXY[]; /* defined on crt0/crt1.c */extern size_t __PROXY_LEN;/* Functions that call `direct_exec_tail' after they've put some data into the transfer buffer, should set LFN parameter to either 0 (no LFN support) or 1 (LFN supported), but NOT 2! if LFN is 2, there is a possiblity that the contents of the transfer buffer will be overrun! */static intdirect_exec_tail(const char *program, const char *args, char * const envp[], const char *proxy, int lfn){ __dpmi_regs r; unsigned long program_la; unsigned long arg_la; unsigned long parm_la; unsigned long env_la, env_e_la; size_t proxy_len = proxy ? strlen(proxy)+1 : 0; int seen_proxy = 0; char arg_header[3]; char short_name[FILENAME_MAX]; const char *progname; unsigned proglen; int i; unsigned long fcb1_la, fcb2_la, fname_la; /* This used to just call sync(). But `sync' flushes the disk cache nowadays, and that can slow down the child tremendously, since some caches (e.g. SmartDrv) invalidate all of their buffers when `_flush_disk_cache' is called. */ for (i = 0; i < 255; i++) fsync(i); if (lfn == 2) /* don't know yet */ lfn = _USE_LFN; /* The pathname of the executable to run. */ proglen = strlen(program)+1; if (!check_talloc(proglen)) return -1; /* Make sure any magic names, like /dev/c/foo, are converted to the usual DOS form, and, under LFN, to the short 8+3 alias. */ _put_path2(program, tbuf_beg == __tb ? tbuf_ptr - tbuf_beg : 0); if(lfn) { r.x.ax = 0x7160; /* Truename */ r.x.cx = 1; /* Get short name */ r.x.ds = r.x.es = tbuf_ptr / 16; r.x.si = r.x.di = tbuf_ptr & 15; __dpmi_int(0x21, &r); if (r.x.flags & 1) { errno = __doserr_to_errno(r.x.ax); return -1; } } dosmemget(tbuf_beg == __tb ? tbuf_ptr : __tb, FILENAME_MAX, short_name); progname = short_name; proglen = strlen(short_name)+1; if (!check_talloc(proglen + strlen(args) + 3 + sizeof(Execp) + 48)) return -1; program_la = talloc(proglen); arg_la = talloc(strlen(args)+3); parm_la = talloc(sizeof(Execp)); 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. */ fcb1_la = talloc(16); /* allocate space for 1st FCB */ fname_la = arg_la + 1; /* first character of command tail */ r.x.ax = 0x2901; /* AL = 1 means skip leading separators */ r.x.ds = fname_la / 16; /* pointer to 1st cmd argument */ r.x.si = fname_la & 15; r.x.es = fcb1_la / 16; /* pointer to FCB buffer */ r.x.di = fcb1_la & 15; __dpmi_int (0x21, &r); /* We cannot be sure that Int 21h/AX=2901h parsed the entire first command-line argument (it might not be a filename at all!). We need to get to the next command-line arg before calling 2901 again. 2901 returns the pointer to first unparsed character in DS:SI. Note that, in case there is no second command-line argument, the following loop is terminated by the trailing CR which ends the command-line tail. */ for (_farsetsel(_dos_ds), fname_la = ((unsigned)r.x.ds) * 16 + r.x.si; !isspace(_farnspeekb(fname_la)); fname_la++) ; fcb2_la = talloc(16); r.x.ax = 0x2901; r.x.ds = fname_la / 16; /* begin parsing 2nd arg from here */ r.x.si = fname_la & 15; r.x.es = fcb2_la / 16; r.x.di = fcb2_la & 15; __dpmi_int (0x21, &r); /* The environment must be on a segment boundary, so get to the first location in the transfer buffer whose linear address is divisable by 16. */ do { env_la = talloc(1); } while (env_la & 15); talloc(-1);#if 0 /* Convert to relative, since `check_talloc' may relocate. */ arg_la -= tbuf_beg; env_la -= tbuf_beg; fcb1_la -= tbuf_beg; fcb2_la -= tbuf_beg; parm_la -= tbuf_beg; program_la -= tbuf_beg;#endif /* The environment. Replace the !proxy variable, if there is one (for nested programs) if we are called from `system', or skip it, if we are called from `spawnXX'. */ for (i=0; envp[i]; i++) { const char *ep = envp[i]; size_t env_len = strlen(ep)+1; if (strncmp(ep, __PROXY, __PROXY_LEN) == 0 && ep[__PROXY_LEN] == '=') { seen_proxy = 1; if (proxy) { ep = proxy; env_len = proxy_len; } else continue; } if (!check_talloc(env_len)) return -1; env_e_la = talloc(env_len); dosmemput(ep, env_len, env_e_la); } /* If no !proxy variable was found, create one. */ if (proxy && !seen_proxy) { if (!check_talloc(proxy_len)) return -1; env_e_la = talloc(proxy_len); dosmemput(proxy, proxy_len, env_e_la); } /* Terminate by an extra NULL char. */ arg_header[0] = 0; /* The name of the program that owns the environment. */ arg_header[1] = 1; /* the number of strings (1, little-endian) */ arg_header[2] = 0; if (!check_talloc(3 + proglen)) return -1; dosmemput(arg_header, 3, talloc(3)); env_e_la = talloc(proglen); dosmemput(progname, proglen, env_e_la); /* Prepare the parameter block and call Int 21h/AX=4B00h. */#if 0 arg_la += tbuf_beg; env_la += tbuf_beg; fcb1_la += tbuf_beg; fcb2_la += tbuf_beg; parm_la += tbuf_beg; program_la += tbuf_beg;#endif parm.eseg = env_la / 16; parm.argseg = arg_la / 16; parm.argoff = arg_la & 15; parm.fcb1_seg = fcb1_la / 16; parm.fcb1_off = fcb1_la & 15; parm.fcb2_seg = fcb2_la / 16; parm.fcb2_off = fcb2_la & 15; dosmemput(&parm, sizeof(parm), parm_la); r.x.ax = 0x4b00; r.x.ds = program_la / 16; r.x.dx = program_la & 15; r.x.es = parm_la / 16; r.x.bx = parm_la & 15; __dpmi_int(0x21, &r);#if 0 if (tbuf_selector) __dpmi_free_dos_memory (tbuf_selector); tbuf_selector = 0;#endif if (r.x.flags & 1) { errno = __doserr_to_errno(r.x.ax); return -1; } r.h.ah = 0x4d; __dpmi_int(0x21, &r); if (r.x.flags & 1) { errno = __doserr_to_errno(r.x.ax); return -1; } /* AH holds the ``system exit code'' which is non-zero if the child was aborted by Ctrl-C, or Critical Device error (also if the child installs itself as a TSR). */ if (r.h.ah && r.h.ah != 3) /* 3 means it exited as TSR (is it ``normal''?) */ { errno = EINTR; /* what else can we put in `errno'? */ return ( ((r.h.ah == 1 ? SIGINT : SIGABRT) << 8) | r.h.al ); } return r.h.al; /* AL holds the child exit code */}int_dos_exec(const char *program, const char *args, char * const envp[]){ 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 char GO32_V2_STRING[] = "go32-v2.exe";static char GO32_STRING[] = "go32.exe";/* A list of known shells which require we DON'T quote command lines that are passed to them with the /c or -c switch. */static const char *shell_brokets[] = {
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?