415 lines
12 KiB
C
415 lines
12 KiB
C
|
/* 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) );
|
||
|
}
|