Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/boost_1_33_1/tools/build/jam_src/execnt.c @ 12

Last change on this file since 12 was 12, checked in by landauf, 17 years ago

added boost

File size: 23.5 KB
Line 
1/*
2 * Copyright 1993, 1995 Christopher Seiwald.
3 *
4 * This file is part of Jam - see jam.c for Copyright information.
5 */
6
7/*  This file is ALSO:
8 *  Copyright 2001-2004 David Abrahams.
9 *  Distributed under the Boost Software License, Version 1.0.
10 *  (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
11 */
12
13# include "jam.h"
14# include "lists.h"
15# include "execcmd.h"
16# include <errno.h>
17# include <assert.h>
18# include <ctype.h>
19# include <time.h>
20
21# ifdef USE_EXECNT
22
23# define WIN32_LEAN_AND_MEAN
24# include <windows.h>           /* do the ugly deed */
25# include <process.h>
26
27# if !defined( __BORLANDC__ ) && !defined( OS_OS2 )
28# define wait my_wait
29static int my_wait( int *status );
30# endif
31
32/*
33 * execnt.c - execute a shell command on Windows NT and Windows 95/98
34 *
35 * If $(JAMSHELL) is defined, uses that to formulate execvp()/spawnvp().
36 * The default is:
37 *
38 *      /bin/sh -c %            [ on UNIX/AmigaOS ]
39 *      cmd.exe /c %            [ on Windows NT ]
40 *
41 * Each word must be an individual element in a jam variable value.
42 *
43 * In $(JAMSHELL), % expands to the command string and ! expands to
44 * the slot number (starting at 1) for multiprocess (-j) invocations.
45 * If $(JAMSHELL) doesn't include a %, it is tacked on as the last
46 * argument.
47 *
48 * Don't just set JAMSHELL to /bin/sh or cmd.exe - it won't work!
49 *
50 * External routines:
51 *      execcmd() - launch an async command execution
52 *      execwait() - wait and drive at most one execution completion
53 *
54 * Internal routines:
55 *      onintr() - bump intr to note command interruption
56 *
57 * 04/08/94 (seiwald) - Coherent/386 support added.
58 * 05/04/94 (seiwald) - async multiprocess interface
59 * 01/22/95 (seiwald) - $(JAMSHELL) support
60 * 06/02/97 (gsar)    - full async multiprocess support for Win32
61 */
62
63static int intr = 0;
64static int cmdsrunning = 0;
65static void (*istat)( int );
66
67static int  is_nt_351        = 0;
68static int  is_win95         = 1;
69static int  is_win95_defined = 0;
70
71
72static struct
73{
74        int     pid; /* on win32, a real process handle */
75        void    (*func)( void *closure, int status, timing_info* );
76        void    *closure;
77        char    *tempfile;
78
79} cmdtab[ MAXJOBS ] = {{0}};
80
81
82static void
83set_is_win95( void )
84{
85  OSVERSIONINFO  os_info;
86
87  os_info.dwOSVersionInfoSize = sizeof(os_info);
88  os_info.dwPlatformId        = VER_PLATFORM_WIN32_WINDOWS;
89  GetVersionEx( &os_info );
90 
91  is_win95         = (os_info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS);
92  is_win95_defined = 1;
93 
94  /* now, test wether we're running Windows 3.51                */
95  /* this is later used to limit the system call command length */
96  if (os_info.dwPlatformId ==  VER_PLATFORM_WIN32_NT)
97    is_nt_351 = os_info.dwMajorVersion == 3;
98}
99
100int maxline()
101{
102    if (!is_win95_defined)
103        set_is_win95();
104   
105    /* Set the maximum command line length according to the OS */
106    return is_nt_351 ? 996
107        : is_win95 ? 1023
108        : 2047;
109}
110
111static void
112free_argv( char** args )
113{
114  free( args[0] );
115  free( args );
116}
117
118/* Convert a command string into arguments for spawnvp.  The original
119 * code, inherited from ftjam, tried to break up every argument on the
120 * command-line, dealing with quotes, but that's really a waste of
121 * time on Win32, at least.  It turns out that all you need to do is
122 * get the raw path to the executable in the first argument to
123 * spawnvp, and you can pass all the rest of the command-line
124 * arguments to spawnvp in one, un-processed string.
125 *
126 * New strategy: break the string in at most one place.
127 */
128static char**
129string_to_args( const char*  string )
130{
131    int src_len;
132    int in_quote;
133    char* line;
134    char const* src;
135    char* dst;
136    char** argv;
137
138    /* drop leading and trailing whitespace if any */
139    while (isspace(*string))
140        ++string;
141 
142    src_len = strlen( string );
143    while ( src_len > 0 && isspace( string[src_len - 1] ) )
144        --src_len;
145
146    /* Copy the input string into a buffer we can modify
147     */
148    line = (char*)malloc( src_len+1 );
149    if (!line)
150        return 0;
151
152    /* allocate the argv array.
153     *   element 0: stores the path to the executable
154     *   element 1: stores the command-line arguments to the executable
155     *   element 2: NULL terminator
156     */
157    argv = (char**)malloc( 3 * sizeof(char*) );
158    if (!argv)
159    {
160        free( line );
161        return 0;
162    }
163   
164    /* Strip quotes from the first command-line argument and find
165     * where it ends.  Quotes are illegal in Win32 pathnames, so we
166     * don't need to worry about preserving escaped quotes here.
167     * Spaces can't be escaped in Win32, only enclosed in quotes, so
168     * removing backslash escapes is also a non-issue.
169     */
170    in_quote = 0;
171    for ( src = string, dst = line ; *src; src++ )
172    {
173        if (*src == '"')
174            in_quote = !in_quote;
175        else if (!in_quote && isspace(*src))
176            break;
177        else
178            *dst++ = *src;
179    }
180    *dst++ = 0;
181    argv[0] = line;
182
183    /* skip whitespace in src */
184    while (isspace(*src))
185        ++src;
186
187    argv[1] = dst;
188
189        /* Copy the rest of the arguments verbatim */
190   
191    src_len -= src - string;
192
193    /* Use strncat because it appends a trailing nul */
194    *dst = 0;
195    strncat(dst, src, src_len);
196
197    argv[2] = 0;
198   
199    return argv;
200}
201
202
203
204/* process a "del" or "erase" command under Windows 95/98 */
205static int
206process_del( char*  command )
207{
208  char** arg;
209  char*  p = command, *q;
210  int    wildcard = 0, result = 0;
211
212  /* first of all, skip the command itself */
213  if ( p[0] == 'd' )
214    p += 3; /* assumes "del..;" */
215  else if ( p[0] == 'e' )
216    p += 5; /* assumes "erase.." */
217  else
218    return 1; /* invalid command */
219
220  /* process all targets independently */
221  for (;;)
222  {
223    /* skip leading spaces */
224    while ( *p && isspace(*p) )
225      p++;
226     
227    /* exit if we encounter an end of string */
228    if (!*p)
229      return 0;
230     
231    /* ignore toggles/flags */
232    while (*p == '/')
233    {
234      p++;
235      while ( *p && isalnum(*p) )
236          p++;
237      while (*p && isspace(*p) )
238          ++p;
239    }
240
241   
242    {
243      int  in_quote = 0;
244      int  wildcard = 0;
245      int  go_on    = 1;
246     
247      q = p;
248      while (go_on)
249      {
250        switch (*p)
251        {
252          case '"':
253            in_quote = !in_quote;
254            break;
255         
256          case '?':
257          case '*':
258            if (!in_quote)
259              wildcard = 1;
260            break;
261           
262          case '\0':
263            if (in_quote)
264              return 1;
265            /* fall-through */
266             
267          case ' ':
268          case '\t':
269            if (!in_quote)
270            {
271              int    len = p - q;
272              int    result;
273              char*  line;
274             
275              /* q..p-1 contains the delete argument */
276              if ( len <= 0 )
277                return 1;
278 
279              line = (char*)malloc( len+4+1 );
280              if (!line)
281                return 1;
282               
283              strncpy( line, "del ", 4 );
284              strncpy( line+4, q, len );
285              line[len+4] = '\0';
286             
287              if ( wildcard )
288                result = system( line );
289              else
290                result = !DeleteFile( line+4 );
291 
292              free( line );
293              if (result)
294                return 1;
295               
296              go_on = 0;
297            }
298           
299          default:
300            ;
301        }
302        p++;
303      } /* while (go_on) */
304    }
305  }
306}
307
308
309/*
310 * onintr() - bump intr to note command interruption
311 */
312
313void
314onintr( int disp )
315{
316        intr++;
317        printf( "...interrupted\n" );
318}
319
320/*
321 * can_spawn() - If the command is suitable for execution via spawnvp,
322 * return a number >= the number of characters it would occupy on the
323 * command-line.  Otherwise, return zero.
324 */
325long can_spawn(char* command)
326{
327    char *p;
328   
329    char inquote = 0;
330
331    /* Move to the first non-whitespace */
332    command += strspn( command, " \t" );
333
334    p = command;
335   
336    /* Look for newlines and unquoted i/o redirection */
337    do
338    {
339        p += strcspn( p, "'\n\"<>|" );
340
341        switch (*p)
342        {
343        case '\n':
344            /* skip over any following spaces */
345            while( isspace( *p ) )
346                ++p;
347            /* Must use a .bat file if there is anything significant
348             * following the newline
349             */
350            if (*p)
351                return 0;
352            break;
353           
354        case '"':
355        case '\'':
356            if (p > command && p[-1] != '\\')
357            {
358                if (inquote == *p)
359                    inquote = 0;
360                else if (inquote == 0)
361                    inquote = *p;
362            }
363               
364            ++p;
365            break;
366           
367        case '<':
368        case '>':
369        case '|':
370            if (!inquote)
371                return 0;
372            ++p;
373            break;
374        }
375    }
376    while (*p);
377
378    /* Return the number of characters the command will occupy
379     */
380    return p - command;
381}
382
383void execnt_unit_test()
384{
385#if !defined(NDEBUG)       
386    /* vc6 preprocessor is broken, so assert with these strings gets
387     * confused. Use a table instead.
388     */
389    typedef struct test { char* command; int result; } test;
390    test tests[] = {
391        { "x", 0 },
392        { "x\n ", 0 },
393        { "x\ny", 1 },
394        { "x\n\n y", 1 },
395        { "echo x > foo.bar", 1 },
396        { "echo x < foo.bar", 1 },
397        { "echo x \">\" foo.bar", 0 },
398        { "echo x \"<\" foo.bar", 0 },
399        { "echo x \\\">\\\" foo.bar", 1 },
400        { "echo x \\\"<\\\" foo.bar", 1 }
401    };
402    int i;
403    for ( i = 0; i < sizeof(tests)/sizeof(*tests); ++i)
404    {
405        assert( !can_spawn( tests[i].command ) == tests[i].result );
406    }
407
408    {
409        char* long_command = malloc(MAXLINE + 10);
410        assert( long_command != 0 );
411        memset( long_command, 'x', MAXLINE + 9 );
412        long_command[MAXLINE + 9] = 0;
413        assert( can_spawn( long_command ) == MAXLINE + 9);
414        free( long_command );
415    }
416
417    {
418        /* Work around vc6 bug; it doesn't like escaped string
419         * literals inside assert
420         */
421        char** argv = string_to_args("\"g++\" -c -I\"Foobar\"");
422        char const expected[] = "-c -I\"Foobar\""; 
423       
424        assert(!strcmp(argv[0], "g++"));
425        assert(!strcmp(argv[1], expected));
426        free_argv(argv);
427    }
428#endif
429}
430
431/* SVA - handle temp dirs with spaces in the path */
432static const char *getTempDir(void)
433{
434    static char tempPath[_MAX_PATH];
435    static char *pTempPath=NULL;
436
437    if(pTempPath == NULL)
438    {
439        char *p;
440
441        p = getenv("TEMP");
442        if(p == NULL)
443        {
444            p = getenv("TMP");
445        }
446        if(p == NULL)
447        {
448            pTempPath = "\\temp";
449        }
450        else
451        {
452            GetShortPathName(p, tempPath, _MAX_PATH);
453            pTempPath = tempPath;
454        }
455    }
456    return pTempPath;
457}
458
459/* 64-bit arithmetic helpers */
460
461/* Compute the carry bit from the addition of two 32-bit unsigned numbers */
462#define add_carry_bit(a, b) ( (((a) | (b)) >> 31) & (~((a) + (b)) >> 31) & 0x1 )
463
464/* Compute the high 32 bits of the addition of two 64-bit unsigned numbers, h1l1 and h2l2 */
465#define add_64_hi(h1, l1, h2, l2) ((h1) + (h2) + add_carry_bit(l1, l2))
466
467/* Add two 64-bit unsigned numbers, h1l1 and h2l2 */
468static FILETIME add_64(
469    unsigned long h1, unsigned long l1,
470    unsigned long h2, unsigned long l2)
471{
472    FILETIME result;
473    result.dwLowDateTime = l1 + l2;
474    result.dwHighDateTime = add_64_hi(h1, l1, h2, l2);
475
476    return result;
477}
478
479static FILETIME add_FILETIME(FILETIME t1, FILETIME t2)
480{
481    return add_64(
482        t1.dwHighDateTime, t1.dwLowDateTime
483      , t2.dwHighDateTime, t2.dwLowDateTime);
484}
485static FILETIME negate_FILETIME(FILETIME t)
486{
487    /* 2s complement negation */
488    return add_64(~t.dwHighDateTime, ~t.dwLowDateTime, 0, 1);
489}
490
491/* COnvert a FILETIME to a number of seconds */
492static double filetime_seconds(FILETIME t)
493{
494    return t.dwHighDateTime * (double)(1UL << 31) * 2 + t.dwLowDateTime * 1.0e-7;
495}
496
497static void
498record_times(int pid, timing_info* time)
499{
500    FILETIME creation, exit, kernel, user;
501    if (GetProcessTimes((HANDLE)pid, &creation, &exit, &kernel, &user))
502    {
503        /* Compute the elapsed time */
504#if 0 /* We don't know how to get this number this on Unix */
505        time->elapsed = filetime_seconds(
506            add_FILETIME( exit, negate_FILETIME(creation) )
507        );
508#endif
509
510        time->system = filetime_seconds(kernel);
511        time->user = filetime_seconds(user);           
512    }
513       
514    CloseHandle((HANDLE)pid);
515}
516   
517
518/*
519 * execcmd() - launch an async command execution
520 */
521
522void
523execcmd( 
524        char *string,
525        void (*func)( void *closure, int status, timing_info* ),
526        void *closure,
527        LIST *shell )
528{
529    int pid;
530    int slot;
531    int raw_cmd = 0 ;
532    char *argv_static[ MAXARGC + 1 ];   /* +1 for NULL */
533    char **argv = argv_static;
534    char *p;
535
536    /* Check to see if we need to hack around the line-length limitation. */
537    /* Look for a JAMSHELL setting of "%", indicating that the command
538     * should be invoked directly */
539    if ( shell && !strcmp(shell->string,"%") && !list_next(shell) )
540    {
541        raw_cmd = 1;
542        shell = 0;
543    }
544
545    if ( !is_win95_defined )
546        set_is_win95();
547         
548    /* Find a slot in the running commands table for this one. */
549    if ( is_win95 )
550    {
551        /* only synchronous spans are supported on Windows 95/98 */
552        slot = 0;
553    }
554    else
555    {
556        for( slot = 0; slot < MAXJOBS; slot++ )
557            if( !cmdtab[ slot ].pid )
558                break;
559    }
560    if( slot == MAXJOBS )
561    {
562        printf( "no slots for child!\n" );
563        exit( EXITBAD );
564    }
565 
566    if( !cmdtab[ slot ].tempfile )
567    {
568        const char *tempdir;
569        DWORD procID;
570
571        tempdir = getTempDir();
572 
573        /* SVA - allocate 64 other just to be safe */
574        cmdtab[ slot ].tempfile = malloc( strlen( tempdir ) + 64 );
575 
576        procID = GetCurrentProcessId();
577 
578        sprintf( cmdtab[ slot ].tempfile, "%s\\jam%d-%02d.bat", 
579                 tempdir, procID, slot );               
580    }
581
582    /* Trim leading, ending white space */
583
584    while( isspace( *string ) )
585        ++string;
586
587    /* Write to .BAT file unless the line would be too long and it
588     * meets the other spawnability criteria.
589     */
590    if( raw_cmd && can_spawn( string ) >= MAXLINE )
591    {
592        if( DEBUG_EXECCMD )
593            printf("Executing raw command directly\n");       
594    }
595    else
596    {
597        FILE *f;
598        raw_cmd = 0;
599       
600        /* Write command to bat file. */
601        f = fopen( cmdtab[ slot ].tempfile, "w" );
602        if (!f)
603        {
604            printf( "failed to write command file!\n" );
605            exit( EXITBAD );
606        }
607        fputs( string, f );
608        fclose( f );
609
610        string = cmdtab[ slot ].tempfile;
611       
612        if( DEBUG_EXECCMD )
613        {
614            if (shell)
615                printf("using user-specified shell: %s", shell->string);
616            else
617                printf("Executing through .bat file\n");
618        }
619    }
620
621    /* Forumulate argv */
622    /* If shell was defined, be prepared for % and ! subs. */
623    /* Otherwise, use stock /bin/sh (on unix) or cmd.exe (on NT). */
624
625    if( shell )
626    {
627        int i;
628        char jobno[4];
629        int gotpercent = 0;
630
631        sprintf( jobno, "%d", slot + 1 );
632
633        for( i = 0; shell && i < MAXARGC; i++, shell = list_next( shell ) )
634        {
635            switch( shell->string[0] )
636            {
637            case '%':   argv[i] = string; gotpercent++; break;
638            case '!':   argv[i] = jobno; break;
639            default:    argv[i] = shell->string;
640            }
641            if( DEBUG_EXECCMD )
642                printf( "argv[%d] = '%s'\n", i, argv[i] );
643        }
644
645        if( !gotpercent )
646            argv[i++] = string;
647
648        argv[i] = 0;
649    }
650    else if (raw_cmd)
651    {
652        argv = string_to_args(string);
653    }
654    else
655    {
656        /* don't worry, this is ignored on Win95/98, see later.. */
657        argv[0] = "cmd.exe";
658        argv[1] = "/Q/C";               /* anything more is non-portable */
659        argv[2] = string;
660        argv[3] = 0;
661    }
662
663    /* Catch interrupts whenever commands are running. */
664
665    if( !cmdsrunning++ )
666        istat = signal( SIGINT, onintr );
667
668    /* Start the command */
669
670    /* on Win95, we only do a synchronous call */
671    if ( is_win95 )
672    {
673        static const char* hard_coded[] =
674            {
675                "del", "erase", "copy", "mkdir", "rmdir", "cls", "dir",
676                "ren", "rename", "move", 0
677            };
678         
679        const char**  keyword;
680        int           len, spawn = 1;
681        int           result;
682        timing_info time = {0,0};
683         
684        for ( keyword = hard_coded; keyword[0]; keyword++ )
685        {
686            len = strlen( keyword[0] );
687            if ( strnicmp( string, keyword[0], len ) == 0 &&
688                 !isalnum(string[len]) )
689            {
690                /* this is one of the hard coded symbols, use 'system' to run */
691                /* them.. except for "del"/"erase"                            */
692                if ( keyword - hard_coded < 2 )
693                    result = process_del( string );
694                else
695                    result = system( string );
696
697                spawn  = 0;
698                break;
699            }
700        }
701         
702        if (spawn)
703        {
704            char**  args;
705           
706            /* convert the string into an array of arguments */
707            /* we need to take care of double quotes !!      */
708            args = string_to_args( string );
709            if ( args )
710            {
711#if 0
712                char** arg;
713                fprintf( stderr, "%s: ", args[0] );
714                arg = args+1;
715                while ( arg[0] )
716                {
717                    fprintf( stderr, " {%s}", arg[0] );
718                    arg++;
719                }
720                fprintf( stderr, "\n" );
721#endif             
722                result = spawnvp( P_WAIT, args[0], args );
723                record_times(result, &time);
724                free_argv( args );
725            }
726            else
727                result = 1;
728        }
729        func( closure, result ? EXEC_CMD_FAIL : EXEC_CMD_OK, &time );
730        return;
731    }
732
733    if( DEBUG_EXECCMD )
734    {
735        char **argp = argv;
736
737        printf("Executing command");
738        while(*argp != 0)
739        {
740            printf(" [%s]", *argp);
741            argp++;
742        }
743        printf("\n");
744    }
745
746    /* the rest is for Windows NT only */
747    /* spawn doesn't like quotes around the command name */
748    if ( argv[0][0] == '"')
749    {
750        int l = strlen(argv[0]);
751
752        /* Clobber any closing quote, shortening the string by one
753         * element */
754        if (argv[0][l-1] == '"')
755            argv[0][l-1] = '\0';
756       
757        /* Move everything *including* the original terminating zero
758         * back one place in memory, covering up the opening quote */
759        memmove(argv[0],argv[0]+1,l);
760    }
761    if( ( pid = spawnvp( P_NOWAIT, argv[0], argv ) ) == -1 )
762    {
763        perror( "spawn" );
764        exit( EXITBAD );
765    }
766    /* Save the operation for execwait() to find. */
767
768    cmdtab[ slot ].pid = pid;
769    cmdtab[ slot ].func = func;
770    cmdtab[ slot ].closure = closure;
771
772    /* Wait until we're under the limit of concurrent commands. */
773    /* Don't trust globs.jobs alone.                            */
774
775    while( cmdsrunning >= MAXJOBS || cmdsrunning >= globs.jobs )
776        if( !execwait() )
777            break;
778   
779    if (argv != argv_static)
780    {
781        free_argv(argv);
782    }
783}
784
785/*
786 * execwait() - wait and drive at most one execution completion
787 */
788
789int
790execwait()
791{
792        int i;
793        int status, w;
794        int rstat;
795    timing_info time;
796
797        /* Handle naive make1() which doesn't know if cmds are running. */
798
799        if( !cmdsrunning )
800            return 0;
801
802    if ( is_win95 )
803        return 0;
804         
805        /* Pick up process pid and status */
806   
807    while( ( w = wait( &status ) ) == -1 && errno == EINTR )
808        ;
809
810        if( w == -1 )
811        {
812            printf( "child process(es) lost!\n" );
813            perror("wait");
814            exit( EXITBAD );
815        }
816
817        /* Find the process in the cmdtab. */
818
819        for( i = 0; i < MAXJOBS; i++ )
820            if( w == cmdtab[ i ].pid )
821                break;
822
823        if( i == MAXJOBS )
824        {
825            printf( "waif child found!\n" );
826            exit( EXITBAD );
827        }
828
829    record_times(cmdtab[i].pid, &time);
830   
831        /* Clear the temp file */
832    if ( cmdtab[i].tempfile )
833        unlink( cmdtab[ i ].tempfile );
834
835        /* Drive the completion */
836
837        if( !--cmdsrunning )
838            signal( SIGINT, istat );
839
840        if( intr )
841            rstat = EXEC_CMD_INTR;
842        else if( w == -1 || status != 0 )
843            rstat = EXEC_CMD_FAIL;
844        else
845            rstat = EXEC_CMD_OK;
846
847        cmdtab[ i ].pid = 0;
848        /* SVA don't leak temp files */
849        if(cmdtab[i].tempfile != NULL)
850        {
851            free(cmdtab[i].tempfile);
852            cmdtab[i].tempfile = NULL;
853        }
854        (*cmdtab[ i ].func)( cmdtab[ i ].closure, rstat, &time );
855
856        return 1;
857}
858
859# if !defined( __BORLANDC__ )
860
861/* The possible result codes from check_process_exit, below */
862typedef enum { process_error, process_active, process_finished } process_state;
863
864/* Helper for my_wait() below.  Checks to see whether the process has
865 * exited and if so, records timing information.
866 */
867static process_state
868check_process_exit(
869    HANDLE process         /* The process we're looking at */
870   
871  , int* status            /* Storage for the finished process' exit
872                            * code.  If the process is still active
873                            * this location is left untouched. */
874   
875  , HANDLE* active_handles /* Storage for the process handle if it is
876                            * found to be still active, or NULL.  The
877                            * process is treated as though it is
878                            * complete.  */
879   
880  , int* num_active        /* The current length of active_handles */
881)
882{
883    DWORD exitcode;
884    process_state result;
885
886    /* Try to get the process exit code */
887    if (!GetExitCodeProcess(process, &exitcode))
888    {
889        result = process_error; /* signal an error */
890    }
891    else if (
892        exitcode == STILL_ACTIVE     /* If the process is still active */
893        && active_handles != 0       /* and we've been passed a place to buffer it */
894    )
895    {
896        active_handles[(*num_active)++] = process; /* push it onto the active stack */
897        result = process_active;
898    }
899    else
900    {
901        *status = (int)((exitcode & 0xff) << 8);
902        result = process_finished;
903    }
904   
905    return result;
906}
907
908static int
909my_wait( int *status )
910{
911        int i, num_active = 0;
912        DWORD exitcode, waitcode;
913        HANDLE active_handles[MAXJOBS];
914
915        /* first see if any non-waited-for processes are dead,
916         * and return if so.
917         */
918        for ( i = 0; i < globs.jobs; i++ )
919    {
920        int pid = cmdtab[i].pid;
921       
922            if ( pid )
923        {
924            process_state state
925                = check_process_exit((HANDLE)pid, status, active_handles, &num_active);
926           
927            if ( state == process_error )
928                goto FAILED;
929            else if ( state == process_finished )
930                return pid;
931            }
932        }
933
934        /* if a child exists, wait for it to die */
935        if ( !num_active )
936    {
937            errno = ECHILD;
938            return -1;
939        }
940   
941        waitcode = WaitForMultipleObjects( num_active,
942                                       active_handles,
943                                       FALSE,
944                                       INFINITE );
945        if ( waitcode != WAIT_FAILED )
946    {
947            if ( waitcode >= WAIT_ABANDONED_0
948             && waitcode < WAIT_ABANDONED_0 + num_active )
949            i = waitcode - WAIT_ABANDONED_0;
950            else
951            i = waitcode - WAIT_OBJECT_0;
952       
953        if ( check_process_exit(active_handles[i], status, 0, 0) == process_finished )
954            return (int)active_handles[i];
955        }
956
957FAILED:
958        errno = GetLastError();
959        return -1;
960   
961}
962
963# endif /* !__BORLANDC__ */
964
965# endif /* USE_EXECNT */
Note: See TracBrowser for help on using the repository browser.