summaryrefslogtreecommitdiff
path: root/dmake/msdos/spawn.c
blob: bc6bb4c64783a72d1206e91f25c6936a2e745342 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
/* RCS  $Id: spawn.c,v 1.3 2007-10-15 15:43:28 ihi 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

iz81252 changed the parameters for Pack_argv() but this file did not get fixed!
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 );
      Packed_shell = TRUE; /* Previous call implies shell = TRUE. */

      /* 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) );
}