home *** CD-ROM | disk | FTP | other *** search
- /* RCS -- $Header: /u2/dvadura/src/generic/dmake/src/msdos/RCS/spawn.c,v 1.1 91/05/06 15:25:33 dvadura 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@watdragon.uwaterloo.ca
- -- CS DEPT, University of Waterloo, Waterloo, Ont., Canada
- --
- -- COPYRIGHT
- -- Copyright (c) 1990 by Dennis Vadura. All rights reserved.
- --
- -- This program is free software; you can redistribute it and/or
- -- modify it under the terms of the GNU General Public License
- -- (version 1), as published by the Free Software Foundation, and
- -- found in the file 'LICENSE' included with this distribution.
- --
- -- This program is distributed in the hope that it will be useful,
- -- but WITHOUT ANY WARRANTY; without even the implied warrant of
- -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- -- GNU General Public License for more details.
- --
- -- You should have received a copy of the GNU General Public License
- -- along with this program; if not, write to the Free Software
- -- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- --
- -- LOG
- -- $Log: spawn.c,v $
- * Revision 1.1 91/05/06 15:25:33 dvadura
- * dmake Release Version 3.7
- *
- */
-
- #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 "dirlib.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 **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))?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) );
-
- /* 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 */
- }
- *environment = '\0';
-
- /* Clear the interrupted flag, and exec */
- Interrupted = 0;
- i = exec(doswap,CF(program),CF(cmdtail),FP_SEG(environment),CF(swptmp));
-
- /* 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=_strjoin(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=_strjoin(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 = _strdup(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) );
- }
-