home *** CD-ROM | disk | FTP | other *** search
/ Black Box 4 / BlackBox.cdr / proglang / dmake38s.arj / SPAWN.C < prev    next >
C/C++ Source or Header  |  1992-01-23  |  13KB  |  417 lines

  1. /* RCS      -- $Header: /u2/dvadura/src/generic/dmake/src/msdos/spawn.c,v 1.1 1992/01/24 03:27:31 dvadura Exp $
  2. -- SYNOPSIS -- spawnvpe code to emulate spawnvpe call common to DOS compilers.
  3. -- 
  4. -- DESCRIPTION
  5. --    This implementation is further integrated into dmake in that it
  6. --    determines the program to execute and if it's extension is either
  7. --    .bat or .ksh it executes it using the appropriate shell based on the
  8. --    setting of .MKSARGS.  If .MKSARGS is set then in addition
  9. --    to the command tail getting built the arguments are also passed in the
  10. --    environment pursuant to the published MKS argument passing conventions.
  11. --    If the variable Swap_on_exec is set and the DOS OS supports it
  12. --    then the dmake executable image is swapped to secondary storage prior
  13. --    to running the child process.  This is requested by setting the
  14. --    appropriate flag in the call to exec.
  15. --
  16. --    This and the exec.asm routine are derived from work that was supplied
  17. --    to me by Kent Williams (williams@umaxc.weeg.uiowa.edu) and by
  18. --      Len Reed, (..!gatech!holos0!lbr or holos0!lbr@gatech.edu., Holos
  19. --    Software, Inc., Tucker, Ga.).  I sincerely acknowledge their help since
  20. --    their Turbo C, and MSC 6.0 code lead directly to this combined
  21. --    swapping exec that hopefully works with either compiler in all memory
  22. --    models.
  23. --
  24. -- AUTHOR
  25. --      Dennis Vadura, dvadura@watdragon.uwaterloo.ca
  26. --      CS DEPT, University of Waterloo, Waterloo, Ont., Canada
  27. --
  28. -- COPYRIGHT
  29. --      Copyright (c) 1990 by Dennis Vadura.  All rights reserved.
  30. -- 
  31. --      This program is free software; you can redistribute it and/or
  32. --      modify it under the terms of the GNU General Public License
  33. --      (version 1), as published by the Free Software Foundation, and
  34. --      found in the file 'LICENSE' included with this distribution.
  35. -- 
  36. --      This program is distributed in the hope that it will be useful,
  37. --      but WITHOUT ANY WARRANTY; without even the implied warrant of
  38. --      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  39. --      GNU General Public License for more details.
  40. -- 
  41. --      You should have received a copy of the GNU General Public License
  42. --      along with this program;  if not, write to the Free Software
  43. --      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  44. --
  45. -- LOG
  46. --     $Log: spawn.c,v $
  47.  * Revision 1.1  1992/01/24  03:27:31  dvadura
  48.  * dmake Version 3.8, Initial revision
  49.  *
  50. */
  51.  
  52. #include <stdio.h>
  53. #include <stdlib.h>
  54.  
  55. #if defined(_MSC_VER) && _MSC_VER >= 600
  56.     /* Ignore the MSC 6.0 library's "const"-riddled prototype
  57.        for spawnvpe.
  58.     */
  59. # define spawnvpe _ignore_msc_spawnvpe
  60. # include <process.h>
  61. # undef spawnvpe
  62.   int spawnvpe(int, char *, char **, char **);
  63. #else
  64. # include <process.h>
  65. #endif
  66.  
  67. #include <dos.h>
  68. #include <errno.h>
  69. #include <string.h>
  70. #include <alloc.h>
  71. #include <fcntl.h>
  72. #include "extern.h"
  73. #include "dirlib.h"
  74. #include "exec.h"
  75. #include "sysintf.h"
  76.  
  77. extern int Interrupted;
  78.  
  79. /* variables and functions local to this file */
  80. static char     *_findexec ANSI((char *, int *));
  81. static char    **_getpath ANSI(());
  82. static char far *_dos_alloc ANSI((uint16));
  83.  
  84. static uint16 _swap_mask;
  85. static int    _mks_args;
  86. static char   dot_com[] = ".COM",
  87.           dot_exe[] = ".EXE",
  88.               dot_bat[] = ".BAT",
  89.           dot_ksh[] = ".KSH";
  90.  
  91. /* Kinds of executables */
  92. #define SCR 1
  93. #define COM 2
  94. #define EXE 4
  95. #define ALL (SCR|COM|EXE)
  96.  
  97. /* How to make a long pointer */
  98. #define CF(x) (char far *)x
  99.  
  100. /* Make sure we know how to get a segment out of a long pointer */
  101. #ifndef FP_SEG
  102. #define FP_SEG(fp)    ((unsigned)((unsigned long)(fp) >> 16))
  103. #endif
  104.  
  105.  
  106. PUBLIC int
  107. spawnvpe(mode, program, av, ep)/*
  108. =================================
  109.    Spawn a process using an environment and a vector of arguments.
  110.    The code computes a new environment, puts the MKS arguments into
  111.    it if need be, and calls the appropriate routines to search the
  112.    path and to invoke the process. */
  113. int  mode;
  114. char *program;
  115. char **av;
  116. char **ep;
  117. {
  118.    char **envp = ep;        /* Cause we are going to mess with it. */
  119.    char **argv = av;        /* Same with this one.               */
  120.    char cmdtail[129];
  121.    char far *environment;
  122.    char *tail;
  123.    char *swptmp;
  124.    unsigned int envsize;
  125.    unsigned int cmdsize;
  126.    int  cmdtailen;
  127.    int  i;
  128.    int  doswap;
  129.  
  130.    /* First check to see if we can find the program to execute this way we
  131.     * don't alloc the environment and other such stuff prior to figuring out
  132.     * we don't know how to run the program. */
  133. find_program:
  134.    if((program = _findexec(program, &i)) == NIL(char)) {
  135.       errno = ENOENT;
  136.       return( -1 );
  137.    }
  138.  
  139.    /* i is set to TRUE in _findexec if the exec is a shell
  140.     * script (either .BAT or .KSH file), returns FALSE for all others. */
  141.    if( i && !Packed_shell ) {
  142.       /* Restore the spaces into the command line that were erased by
  143.        * the previous call to Pack_argv.  This enables us to repack the
  144.        * command as a shell command using Pack_argv again. */
  145.       for( i=0; argv[i] != NIL(char); i++ ) {
  146.          int x = strlen(argv[i]);
  147.          if( argv[i+1] != NIL(char) ) argv[i][x] = ' ';
  148.       }
  149.  
  150.       argv = Pack_argv( FALSE, TRUE, *argv );
  151.  
  152.       /* Go and find the program again, I hate goto's but it seems silly to
  153.        * use tail recursion here just for aesthetic purity. */
  154.       program = *argv;
  155.       goto find_program;
  156.    }
  157.  
  158.    /* Compute size of *argv vector for passing as MKS style arguments */
  159.    cmdsize = strlen(*argv)+2;
  160.  
  161.    /* So we have decided on a program to run, therefore pack the command tail
  162.     * and build the environment to pass to the exec code.  This loop packs the
  163.     * DOS command tail, and computes the size of all arguments for the MKS
  164.     * argument passing convention.  Note that we reserve one less byte in the
  165.     * command tail if we are not using MKS style argument passing.
  166.     *
  167.     * Make sure the command tail contains at leat a space.  Some commands fail
  168.     * to work if the command tail is only a \r, STUPID DOS! */
  169.    cmdtailen = (_mks_args = ((Glob_attr & A_MKSARGS) != 0))?3:2;
  170.    tail      = cmdtail+1;
  171.  
  172.    if( argv[1] != NIL(char) )
  173.       for( i=1; argv[i] != NIL(char); i++ ) {
  174.      int arglen = strlen(argv[i]);
  175.  
  176.      cmdsize += arglen+2;        /* Compute all args size for MKS */
  177.  
  178.      if( (cmdtailen += arglen+1) <= 128 ) {
  179.         register char *p = argv[i];
  180.         tail[-1] = ' ';        /* put in the space */
  181.         while( *tail++ = *p++ );    /* put in the arg   */
  182.      }
  183.      else if( !_mks_args ) {
  184.         errno = E2BIG;        /* unless its MKS exit if arglist */
  185.         return(-1);            /* is too long.              */
  186.      }
  187.       }
  188.    else
  189.       *tail++ = ' ';
  190.  
  191.    /* Finish the command tail set up, placing the length in the first byte,
  192.     * and the \r \n \0 at the end for DOS, MKS and us respectively. */
  193.    *cmdtail = tail-cmdtail-2;
  194.    tail[-1] = '\r';
  195.    if( _mks_args ) *tail++ = '\n';
  196.    *tail = '\0';
  197.  
  198.    /* Compute size of environment, skipping any MKS arguments passed in our
  199.     * environment */
  200.    for(; *envp && **envp == '~'; envp++ );
  201.    for(i=0, envsize=_mks_args?cmdsize:1; envp[i] != NIL(char); i++ )
  202.       envsize += strlen(envp[i]) + 1;
  203.  
  204.    /* Check the DOS version number here.  If it is < 3.0 then we don't
  205.     * even want to think about executing the swapping code.   Permanently
  206.     * set swap to 0. */
  207.    doswap = (_osmajor < 3) ? 0 : Swap_on_exec;
  208.  
  209.    /* Set up temporary file for swapping */
  210.    swptmp = doswap?tempnam(NIL(char),"mk"):""; 
  211.  
  212.    /* Allocate an appropriate sized environment block and align it on a
  213.     * paragraph boundary.  It will later get copied to an appropriately low
  214.     * place in the executable image so that when we swap out the environment
  215.     * is still present.  Use
  216.     *    _dos_alloc
  217.     * to allocate the environment segment.  The segment is freed by the call
  218.     * to exec. */
  219.    environment = _dos_alloc( envsize = ((envsize+16)>>4) );
  220.  
  221.    /* First copy the arguments preceeded by ~ character if we are using
  222.     * MKS style argument passing */
  223.    if( _mks_args )
  224.       for(; *argv; argv++) {
  225.          register char *p = *argv;
  226.  
  227.      *environment++ = '~';
  228.      while( *environment++ = *p++ );    /* Far dest, poss near ptr */
  229.       }
  230.  
  231.    /* Now stick in the current evironment vectors. */
  232.    for(; *envp; envp++) {
  233.       register char *p = *envp;
  234.       while( *environment++ = *p++ );        /* Far dest, poss near ptr */
  235.    }
  236.    *environment = '\0';
  237.  
  238.    /* Clear the interrupted flag, and exec  */
  239.    Interrupted = 0;
  240.    i = exec(doswap,CF(program),CF(cmdtail),FP_SEG(environment),CF(swptmp));
  241.  
  242.    /* Now free the temporary file name */
  243.    if( doswap ) FREE(swptmp);
  244.  
  245.    /* If swap was interrupted then quit properly from dmake. */
  246.    if( Interrupted ) Quit();
  247.  
  248.    return(i);
  249. }
  250.  
  251.  
  252. PUBLIC void
  253. Hook_std_writes( file )
  254. char *file;
  255. {
  256.    if( file!= NIL(char) ) {
  257.       int mode = O_BINARY | O_WRONLY | O_CREAT | O_TRUNC;
  258.       int handle;
  259.  
  260.       if (*file == '+') {
  261.           ++file;             /* -F +file means append to file */
  262.           mode = O_BINARY | O_WRONLY | O_CREAT | O_APPEND;
  263.       }
  264.       handle = open(file, mode, S_IREAD | S_IWRITE);
  265.       if (handle < 0) {
  266.           Fatal( "Could not open -F file");
  267.       }
  268.       (void) lseek(handle, 0L, SEEK_END);
  269.       do_hook_std_writes(handle);
  270.    }
  271.    else
  272.       do_unhook_std_writes();
  273. }
  274.  
  275.  
  276. /*
  277. ** _findexec finds executables on the path.
  278. ** Note that it is pretty simple to add support for other executable types
  279. ** (shell scripts, etc.
  280. **
  281. ** This follows the command.com behavior very closely.
  282. */
  283. static char *
  284. _findexec( s, is_shell )/*
  285. ==========================
  286.    Cloned closely from code provided by Kent Williams.  Stripped his down to
  287.    a reduced search since dmake doesn't need to recompute the PATH vector
  288.    each time it does the search since it cannot alter the path vector once
  289.    it begins to make recipes.  Also modified it to use findfirst and findnext
  290.    as provided for dirlib package that I got off the net. */
  291. char *s;
  292. int  *is_shell;
  293. {
  294.    unsigned found_flags;
  295.    char     **pathv = NIL(char *);
  296.    char     *ext    = NIL(char);
  297.    char     *buf    = NIL(char);
  298.    char     *p[2];
  299.    char     *dot_scr;
  300.    char        *dot;
  301.  
  302.    p[0] = ""; p[1] = NIL(char);
  303.    if( strchr("./\\", *s) || s[1] == ':' )
  304.       pathv = p;
  305.    else if( (pathv = _getpath()) == NIL(char *) )
  306.       return( NIL(char) );
  307.  
  308.    /* Compute the extension we need if any. */
  309.    if( (dot = strrchr(s,'.')) != NIL(char) &&
  310.         dot > strrchr(s,'/') && dot > strrchr(s,'\\') )
  311.       ext = dot+1;
  312.  
  313.    dot_scr   = _mks_args ? dot_ksh : dot_bat;
  314.    *is_shell = FALSE;
  315.  
  316.    for( found_flags = 0; *pathv && !found_flags; pathv++ ) {
  317.       DTA dta;
  318.  
  319.       if( !ext ) {
  320.      char *name;
  321.      buf = Build_path( *pathv, name=_strjoin(s, ".???", -1, FALSE) );
  322.      FREE(name);
  323.       }
  324.       else
  325.      buf = Build_path( *pathv, s );
  326.  
  327.       if( findfirst((char *)strupr(buf), &dta) != NIL(DTA) ) {
  328.      if( !ext ) {
  329.         char *dot;
  330.  
  331.         /* search order is .com .exe (.ksh || .bat)
  332.          * there has to be a '.' */
  333.         do {
  334.            dot = strrchr(dta.name,'.');
  335.            if(0 == strcmp(dot,dot_com))
  336.           found_flags |= COM;
  337.            else if(0 == strcmp(dot,dot_exe))
  338.           found_flags |= EXE;
  339.            else if( 0 == strcmp(dot,dot_scr) )
  340.           found_flags |= SCR;
  341.         } while( found_flags != ALL && findnext(&dta) != NIL(DTA) );
  342.  
  343.         if(found_flags & COM)      ext = dot_com;
  344.         else if(found_flags & EXE) ext = dot_exe;
  345.         else if(found_flags & SCR) {
  346.            ext = dot_scr;
  347.            *is_shell = TRUE;
  348.         }
  349.  
  350.         if( found_flags ) {
  351.            char *name;
  352.            buf = Build_path( *pathv, name=_strjoin(s,ext,-1,FALSE) );
  353.            FREE(name);
  354.            strupr(buf);
  355.         }
  356.      }
  357.      else
  358.         found_flags++;
  359.       }
  360.    }
  361.  
  362.    return( found_flags ? buf : NIL(char) );
  363. }
  364.  
  365.  
  366. /*
  367. ** getpath turns the DOS path into a char *vector, It is gotten and
  368. ** transformed only once since dmake can't modify the value of PATH while
  369. ** it is making targets.
  370. */
  371. static char **
  372. _getpath()
  373. {
  374.    static   char **dir = NIL(char *);
  375.    register char *p;
  376.  
  377.    if( !dir ) {
  378.       register char *t;
  379.       int           i;
  380.       char          *semi = NIL(char);
  381.  
  382.       if( (p = getenv("PATH")) == NIL(char) ) p = "";
  383.       for( i=1, t=p; *t; t++ ) if( *t == ';' ) i++;
  384.  
  385.       TALLOC(dir, i+1, char *);
  386.       p   = _strdup(p);
  387.  
  388.       for( i=0; p; p = semi ? (semi+1):NIL(char),i++ ){
  389.      if( (semi = strchr(p,';')) != NIL(char) ) *semi = '\0';
  390.      dir[i] = p;
  391.       }
  392.       dir[i]=NIL(char);
  393.    }
  394.  
  395.    return( dir );
  396. }
  397.  
  398.  
  399. static char far *
  400. _dos_alloc( size )/*
  401. ====================
  402.    This routine allocates size paragraphs from DOS.  It changes the memory
  403.    allocation strategy to allocate from the tail and then changes it back.
  404.    to using first fit. */
  405. uint16 size;
  406. {
  407.    union REGS r;
  408.  
  409.    r.h.ah = 0x48;
  410.    r.x.bx = size;
  411.  
  412.    intdos( &r, &r );
  413.    if( r.x.cflag ) No_ram();
  414.    
  415.    return( (char far *) MK_FP(r.x.ax, 0) );
  416. }
  417.