diff options
Diffstat (limited to 'dmake/msdos/spawn.c')
-rw-r--r-- | dmake/msdos/spawn.c | 414 |
1 files changed, 414 insertions, 0 deletions
diff --git a/dmake/msdos/spawn.c b/dmake/msdos/spawn.c new file mode 100644 index 000000000000..e62d936ee567 --- /dev/null +++ b/dmake/msdos/spawn.c @@ -0,0 +1,414 @@ +/* RCS $Id: spawn.c,v 1.1.1.1 2000-09-22 15:33:27 hr Exp $ +-- +-- SYNOPSIS +-- Spawnvpe code to emulate spawnvpe call common to DOS compilers. +-- +-- DESCRIPTION +-- This implementation is further integrated into dmake in that it +-- determines the program to execute and if it's extension is either +-- .bat or .ksh it executes it using the appropriate shell based on the +-- setting of .MKSARGS. If .MKSARGS is set then in addition +-- to the command tail getting built the arguments are also passed in the +-- environment pursuant to the published MKS argument passing conventions. +-- If the variable Swap_on_exec is set and the DOS OS supports it +-- then the dmake executable image is swapped to secondary storage prior +-- to running the child process. This is requested by setting the +-- appropriate flag in the call to exec. +-- +-- This and the exec.asm routine are derived from work that was supplied +-- to me by Kent Williams (williams@umaxc.weeg.uiowa.edu) and by +-- Len Reed, (..!gatech!holos0!lbr or holos0!lbr@gatech.edu., Holos +-- Software, Inc., Tucker, Ga.). I sincerely acknowledge their help since +-- their Turbo C, and MSC 6.0 code lead directly to this combined +-- swapping exec that hopefully works with either compiler in all memory +-- models. +-- +-- AUTHOR +-- Dennis Vadura, dvadura@dmake.wticorp.com +-- +-- WWW +-- http://dmake.wticorp.com/ +-- +-- COPYRIGHT +-- Copyright (c) 1996,1997 by WTI Corp. All rights reserved. +-- +-- This program is NOT free software; you can redistribute it and/or +-- modify it under the terms of the Software License Agreement Provided +-- in the file <distribution-root>/readme/license.txt. +-- +-- LOG +-- Use cvs log to obtain detailed change logs. +*/ + +#include <stdio.h> +#include <stdlib.h> + +#if defined(_MSC_VER) && _MSC_VER >= 600 + /* Ignore the MSC 6.0 library's "const"-riddled prototype + for spawnvpe. + */ +# define spawnvpe _ignore_msc_spawnvpe +# include <process.h> +# undef spawnvpe + int spawnvpe(int, char *, char **, char **); +#else +# include <process.h> +#endif + +#include <dos.h> +#include <errno.h> +#include <string.h> +#include <alloc.h> +#include <fcntl.h> +#include "extern.h" +#include "dosdta.h" +#include "exec.h" +#include "sysintf.h" + +extern int Interrupted; + +/* variables and functions local to this file */ +static char *_findexec ANSI((char *, int *)); +static char **_getpath ANSI(()); +static char far *_dos_alloc ANSI((uint16)); + +static uint16 _swap_mask; +static int _mks_args; +static char dot_com[] = ".COM", + dot_exe[] = ".EXE", + dot_bat[] = ".BAT", + dot_ksh[] = ".KSH"; + +/* Kinds of executables */ +#define SCR 1 +#define COM 2 +#define EXE 4 +#define ALL (SCR|COM|EXE) + +/* How to make a long pointer */ +#define CF(x) (char far *)x + +/* Make sure we know how to get a segment out of a long pointer */ +#ifndef FP_SEG +#define FP_SEG(fp) ((unsigned)((unsigned long)(fp) >> 16)) +#endif + + +PUBLIC int +spawnvpe(mode, program, av, ep)/* +================================= + Spawn a process using an environment and a vector of arguments. + The code computes a new environment, puts the MKS arguments into + it if need be, and calls the appropriate routines to search the + path and to invoke the process. */ +int mode; +char *program; +char **av; +char **ep; +{ + char pwd[PATH_MAX+1]; + char **envp = ep; /* Cause we are going to mess with it. */ + char **argv = av; /* Same with this one. */ + char cmdtail[129]; + char far *environment; + char *tail; + char *swptmp; + unsigned int envsize; + unsigned int cmdsize; + int cmdtailen; + int i; + int doswap; + + /* First check to see if we can find the program to execute this way we + * don't alloc the environment and other such stuff prior to figuring out + * we don't know how to run the program. */ +find_program: + if((program = _findexec(program, &i)) == NIL(char)) { + errno = ENOENT; + return( -1 ); + } + + /* i is set to TRUE in _findexec if the exec is a shell + * script (either .BAT or .KSH file), returns FALSE for all others. */ + if( i && !Packed_shell ) { + /* Restore the spaces into the command line that were erased by + * the previous call to Pack_argv. This enables us to repack the + * command as a shell command using Pack_argv again. */ + for( i=0; argv[i] != NIL(char); i++ ) { + int x = strlen(argv[i]); + if( argv[i+1] != NIL(char) ) argv[i][x] = ' '; + } + + argv = Pack_argv( FALSE, TRUE, *argv ); + + /* Go and find the program again, I hate goto's but it seems silly to + * use tail recursion here just for aesthetic purity. */ + program = *argv; + goto find_program; + } + + /* Compute size of *argv vector for passing as MKS style arguments */ + cmdsize = strlen(*argv)+2; + + /* So we have decided on a program to run, therefore pack the command tail + * and build the environment to pass to the exec code. This loop packs the + * DOS command tail, and computes the size of all arguments for the MKS + * argument passing convention. Note that we reserve one less byte in the + * command tail if we are not using MKS style argument passing. + * + * Make sure the command tail contains at leat a space. Some commands fail + * to work if the command tail is only a \r, STUPID DOS! */ + cmdtailen = ((_mks_args = ((Glob_attr & A_MKSARGS) != 0)) != 0)?3:2; + tail = cmdtail+1; + + if( argv[1] != NIL(char) ) + for( i=1; argv[i] != NIL(char); i++ ) { + int arglen = strlen(argv[i]); + + cmdsize += arglen+2; /* Compute all args size for MKS */ + + if( (cmdtailen += arglen+1) <= 128 ) { + register char *p = argv[i]; + tail[-1] = ' '; /* put in the space */ + while( *tail++ = *p++ ); /* put in the arg */ + } + else if( !_mks_args ) { + errno = E2BIG; /* unless its MKS exit if arglist */ + return(-1); /* is too long. */ + } + } + else + *tail++ = ' '; + + /* Finish the command tail set up, placing the length in the first byte, + * and the \r \n \0 at the end for DOS, MKS and us respectively. */ + *cmdtail = tail-cmdtail-2; + tail[-1] = '\r'; + if( _mks_args ) *tail++ = '\n'; + *tail = '\0'; + + /* Compute size of environment, skipping any MKS arguments passed in our + * environment */ + for(; *envp && **envp == '~'; envp++ ); + for(i=0, envsize=_mks_args?cmdsize:1; envp[i] != NIL(char); i++ ) + envsize += strlen(envp[i]) + 1; + + /* Check the DOS version number here. If it is < 3.0 then we don't + * even want to think about executing the swapping code. Permanently + * set swap to 0. */ + doswap = (_osmajor < 3) ? 0 : Swap_on_exec; + + /* Set up temporary file for swapping */ + swptmp = doswap?tempnam(NIL(char),"mk"):""; + + /* Allocate an appropriate sized environment block and align it on a + * paragraph boundary. It will later get copied to an appropriately low + * place in the executable image so that when we swap out the environment + * is still present. Use + * _dos_alloc + * to allocate the environment segment. The segment is freed by the call + * to exec. */ + environment = _dos_alloc( envsize = ((envsize+16)>>4) ); + *environment = '\0'; + + /* First copy the arguments preceeded by ~ character if we are using + * MKS style argument passing */ + if( _mks_args ) + for(; *argv; argv++) { + register char *p = *argv; + + *environment++ = '~'; + while( *environment++ = *p++ ); /* Far dest, poss near ptr */ + } + + /* Now stick in the current evironment vectors. */ + for(; *envp; envp++) { + register char *p = *envp; + while( *environment++ = *p++ ); /* Far dest, poss near ptr */ + } + + /* Clear the interrupted flag, and exec */ + Interrupted = 0; + + /* Preserve the current working directory accross a spawn call + * DOS is brain dead about this. This way we have some hope of cleaning + * up the swapping tempfiles after we return. */ + strcpy(pwd,Get_current_dir()); + i = exec(doswap,CF(program),CF(cmdtail),FP_SEG(environment),CF(swptmp)); + Set_dir(pwd); + + /* Now free the temporary file name */ + if( doswap ) FREE(swptmp); + + /* If swap was interrupted then quit properly from dmake. */ + if( Interrupted ) Quit(); + + return(i); +} + + +PUBLIC void +Hook_std_writes( file ) +char *file; +{ + if( file!= NIL(char) ) { + int mode = O_BINARY | O_WRONLY | O_CREAT | O_TRUNC; + int handle; + + if (*file == '+') { + ++file; /* -F +file means append to file */ + mode = O_BINARY | O_WRONLY | O_CREAT | O_APPEND; + } + handle = open(file, mode, S_IREAD | S_IWRITE); + if (handle < 0) { + Fatal( "Could not open -F file"); + } + (void) lseek(handle, 0L, SEEK_END); + do_hook_std_writes(handle); + } + else + do_unhook_std_writes(); +} + + +/* +** _findexec finds executables on the path. +** Note that it is pretty simple to add support for other executable types +** shell scripts, etc. +** +** This follows the command.com behavior very closely. +*/ +static char * +_findexec( s, is_shell )/* +========================== + Cloned closely from code provided by Kent Williams. Stripped his down to + a reduced search since dmake doesn't need to recompute the PATH vector + each time it does the search since it cannot alter the path vector once + it begins to make recipes. Also modified it to use findfirst and findnext + as provided for dirlib package that I got off the net. */ +char *s; +int *is_shell; +{ + unsigned found_flags; + char **pathv = NIL(char *); + char *ext = NIL(char); + char *buf = NIL(char); + char *p[2]; + char *dot_scr; + char *dot; + + p[0] = ""; p[1] = NIL(char); + if( strchr("./\\", *s) || s[1] == ':' ) + pathv = p; + else if( (pathv = _getpath()) == NIL(char *) ) + return( NIL(char) ); + + /* Compute the extension we need if any. */ + if( (dot = strrchr(s,'.')) != NIL(char) && + dot > strrchr(s,'/') && dot > strrchr(s,'\\') ) + ext = dot+1; + + dot_scr = _mks_args ? dot_ksh : dot_bat; + *is_shell = FALSE; + + for( found_flags = 0; *pathv && !found_flags; pathv++ ) { + DTA dta; + + if( !ext ) { + char *name; + buf = Build_path( *pathv, name=DmStrJoin(s, ".???", -1, FALSE) ); + FREE(name); + } + else + buf = Build_path( *pathv, s ); + + if( findfirst((char *)strupr(buf), &dta) != NIL(DTA) ) { + if( !ext ) { + char *dot; + + /* search order is .com .exe (.ksh || .bat) + * there has to be a '.' */ + do { + dot = strrchr(dta.name,'.'); + if(0 == strcmp(dot,dot_com)) + found_flags |= COM; + else if(0 == strcmp(dot,dot_exe)) + found_flags |= EXE; + else if( 0 == strcmp(dot,dot_scr) ) + found_flags |= SCR; + } while( found_flags != ALL && findnext(&dta) != NIL(DTA) ); + + if(found_flags & COM) ext = dot_com; + else if(found_flags & EXE) ext = dot_exe; + else if(found_flags & SCR) { + ext = dot_scr; + *is_shell = TRUE; + } + + if( found_flags ) { + char *name; + buf = Build_path( *pathv, name=DmStrJoin(s,ext,-1,FALSE) ); + FREE(name); + strupr(buf); + } + } + else + found_flags++; + } + } + + return( found_flags ? buf : NIL(char) ); +} + + +/* +** getpath turns the DOS path into a char *vector, It is gotten and +** transformed only once since dmake can't modify the value of PATH while +** it is making targets. +*/ +static char ** +_getpath() +{ + static char **dir = NIL(char *); + register char *p; + + if( !dir ) { + register char *t; + int i; + char *semi = NIL(char); + + if( (p = getenv("PATH")) == NIL(char) ) p = ""; + for( i=1, t=p; *t; t++ ) if( *t == ';' ) i++; + + TALLOC(dir, i+1, char *); + p = DmStrDup(p); + + for( i=0; p; p = semi ? (semi+1):NIL(char),i++ ){ + if( (semi = strchr(p,';')) != NIL(char) ) *semi = '\0'; + dir[i] = p; + } + dir[i]=NIL(char); + } + + return( dir ); +} + + +static char far * +_dos_alloc( size )/* +==================== + This routine allocates size paragraphs from DOS. It changes the memory + allocation strategy to allocate from the tail and then changes it back. + to using first fit. */ +uint16 size; +{ + union REGS r; + + r.h.ah = 0x48; + r.x.bx = size; + + intdos( &r, &r ); + if( r.x.cflag ) No_ram(); + + return( (char far *) MK_FP(r.x.ax, 0) ); +} |