1 /*
2  *  The Regina Rexx Interpreter
3  *  Copyright (C) 1992-1994  Anders Christensen <anders@pvv.unit.no>
4  *
5  *  This library is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Library General Public
7  *  License as published by the Free Software Foundation; either
8  *  version 2 of the License, or (at your option) any later version.
9  *
10  *  This library is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *  Library General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Library General Public
16  *  License along with this library; if not, write to the Free
17  *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19 
20 #include "regina_c.h"
21 
22 #if defined(WIN32)
23 # ifdef _MSC_VER
24 #  if _MSC_VER >= 1100
25 /* Stupid MSC can't compile own headers without warning at least in VC 5.0 */
26 #   pragma warning(disable: 4115 4201 4214 4514)
27 #  endif
28 # endif
29 # include <windows.h>
30 # ifdef _MSC_VER
31 #  if _MSC_VER >= 1100
32 #   pragma warning(default: 4115 4201 4214)
33 #  endif
34 # endif
35 #endif
36 
37 #include "rexx.h"
38 #include <stdio.h>
39 
40 #include <string.h>
41 #include <signal.h>
42 #include <errno.h>
43 #ifdef HAVE_ASSERT_H
44 # include <assert.h>
45 #endif
46 #ifdef HAVE_UNISTD_H
47 # include <unistd.h>
48 #endif
49 
50 #if defined(VMS)
51 # define fork() vfork()
52 
53 #if 0
54 /* lets try and see if VMS is smart enough now to do this posixly */
55 # ifdef  posix_do_command
56 #  undef posix_do_command
57 # endif
58 # define posix_do_command __regina_vms_do_command
59 #endif
60 #endif
61 
62 #define STD_IO     0x00
63 #define QUEUE      0x01
64 #define LIFO       0x02
65 #define FIFO       0x04
66 #define STREAM     0x08
67 #define STEM       0x10
68 #define STRING     0x20
69 #define LIFOappend 0x80
70 #define FIFOappend 0x100
71 
72 #if defined(_POSIX_PIPE_BUF) && !defined(PIPE_BUF)
73 # define PIPE_BUF _POSIX_PIPE_BUF
74 #endif
75 
76 typedef struct { /* shl_tsd: static variables of this module (thread-safe) */
77    char         *cbuff ;
78    int           child ;
79    int           status ;
80    int           running ;
81    void         *AsyncInfo ;
82    unsigned char IObuf[REGINA_MAX_BUFFER_SIZE]; /* write cache */
83    unsigned      IOBused;
84 } shl_tsd_t; /* thread-specific but only needed by this module. see
85               * init_shell
86               */
87 
88 /* init_shell initializes the module.
89  * Currently, we set up the thread specific data.
90  * The function returns 1 on success, 0 if memory is short.
91  */
init_shell(tsd_t * TSD)92 int init_shell( tsd_t *TSD )
93 {
94    shl_tsd_t *st;
95 
96    if ( TSD->shl_tsd != NULL )
97       return(1);
98 
99    if ( ( TSD->shl_tsd = MallocTSD( sizeof(shl_tsd_t) ) ) == NULL )
100       return(0);
101    st = (shl_tsd_t *)TSD->shl_tsd;
102    memset( st, 0, sizeof(shl_tsd_t) );  /* correct for all values */
103    return(1);
104 }
105 
stem_access(tsd_t * TSD,environpart * e,int pos,streng * value)106 static const streng *stem_access( tsd_t *TSD, environpart *e, int pos,
107                                   streng *value )
108 /* appends "."+itoa(pos) to e->currname and accesses this variable.
109  * value is NULL to access the current value or non-NULL to set the new value.
110  * The return value is NULL if a new value is set or the old one.
111  */
112 {
113    int leaflen ;
114 
115    leaflen = sprintf( e->currname->value + e->currnamelen, "%d", pos ) ;
116 
117    e->currname->len = e->currnamelen + leaflen ;
118 
119    /*
120     * FGC: Changed back from get_it_anyway_compound to getvalue.
121     *      This will raise a NOVALUE condition if we try to read
122     *      unexpected data.
123     *      Furthermore we allow the user to use extended stems
124     *      like "A.B." if we ever want to allow it. We do so by
125     *      changing from [sg]etdirvalue_compound to [sg]etvalue.
126     */
127    if (value == NULL)
128       return( getvalue( TSD, e->currname, -1 ) ) ;
129 
130    setvalue( TSD, e->currname, value, -1 ) ;
131    return( NULL ) ;
132 }
133 
set_currname(tsd_t * TSD,environpart * e)134 static void set_currname( tsd_t *TSD, environpart *e )
135 /* Sets the initial currname of the environpart from its name. e->currname
136  * will be freed at first if not set to NULL.
137  */
138 {
139    const streng *source = e->name;
140 
141    if (e->flags.ant == antSIMSYMBOL)
142    {
143       /*
144        * Just in case of a stem we access the variable directly. Otherwise
145        * we have to resolve the symbol.
146        */
147       if ( (source->len > 0) && ( e->flags.awt != awtSTEM ) )
148       {
149          source = getvalue( TSD, source, -1 ) ;
150       }
151    }
152    else
153    {
154       assert( ( e->flags.ant == antSTRING ) || ( source == NULL ) );
155    }
156 
157    if (e->currname != NULL)
158       Free_stringTSD( e->currname ) ;
159 
160    /*
161     * We need space for "." and the maximal number, but first check if we
162     * have a "default" value.
163     */
164    if ( source == NULL )
165    {
166       e->currname = NULL ;
167       return ;
168    }
169    e->currnamelen = Str_len( source ) ;
170    e->currname = Str_makeTSD( e->currnamelen + 3*sizeof(int) ) ;
171    memcpy( e->currname->value, source->value, e->currnamelen ) ;
172    e->currname->len = e->currnamelen ; /* pro forma, will be recomputed */
173 }
174 
prepare_env_io(environpart * e)175 static void prepare_env_io( environpart *e )
176 /*
177  * Prepares the WITH-IO-redirection from envpart.
178  */
179 {
180    e->SameAsOutput = 0;
181    e->FileRedirected = 0;
182    e->tempname = NULL; /* none as default, might become char* RedirTempFile */
183    e->queue = NULL;
184    e->tmp_queue = NULL;
185    e->type = STD_IO;
186    e->hdls[0] = e->hdls[1] = e->hdls[2] = -1;
187 }
188 
open_env_io(tsd_t * TSD,environpart * e,unsigned overwrite,int isString)189 static void open_env_io( tsd_t *TSD, environpart *e, unsigned overwrite, int isString )
190 /* Opens the WITH-IO-redirection from envpart and sets *flag to either
191  * STREAM or STEM. Nothing happens if there isn't a redirection.
192  * The protect-fields are used by the
193  */
194 {
195    const streng *h ;
196    int error ;
197    unsigned awt ;
198    char code ;
199 
200    if ( ( e->name == NULL ) && !overwrite && ( e->flags.awt == awtUNKNOWN ) )
201       return ;
202 
203    set_currname(TSD, e);
204    if ( overwrite == awtUNKNOWN )
205       awt = e->flags.awt ;
206    else
207       awt = overwrite ;
208    switch (awt)
209    {
210       case awtSTREAM:
211          /*
212           * For a STREAM input/output redirection, set the file reopen
213           * flag, and reopen the file.
214           */
215          e->type = STREAM;
216          if ( e->flags.isinput )
217             code = 'r';
218          else if ( e->flags.append )
219             code = 'A';
220          else /* REPLACE */
221             code = 'R';
222          if ( e->flags.isinput || !e->SameAsOutput )
223             e->file = addr_reopen_file( TSD, e->currname, code,
224                                         e->flags.iserror );
225          break;
226 
227       case awtSTEM:
228          /*
229           * For a STEM input/output redirection, check that existing state of
230           * the stem if appropriate and initialise the stem
231           */
232          e->type = STEM ;
233 
234          if (e->flags.isinput || e->flags.append)
235          {
236             /*
237              * For a STEM input redirection, the stem must already exist and be
238              * a valid Rexx "array". This happens to an existing output stem in
239              * the append mode, too.
240              */
241             h = stem_access( TSD, e, 0, NULL ) ;
242             /* h must be a whole positive number */
243             e->maxnum = streng_to_int( TSD, h, &error ) ;
244             if (error || (e->maxnum < 0))
245                exiterror( ERR_INVALID_STEM_OPTION, /* needs stem.0 and      */
246                           1,                       /* (stem.0) as arguments */
247                           tmpstr_of( TSD, e->currname ),
248                           tmpstr_of( TSD, h ) )  ;
249             e->currnum = (e->flags.isinput) ? 1 : e->maxnum + 1;
250          }
251          else /* must be REPLACE */
252          {
253             e->maxnum = 0 ;
254             e->currnum = 1 ;
255             e->base->value[0] = '0' ;
256             e->base->len = 1 ;
257             stem_access( TSD, e, 0, Str_dupTSD( e->base ) ) ;
258          }
259          break;
260 
261       case awtLIFO:
262          if ( overwrite != awtUNKNOWN )
263          {
264             if ( e->flags.isinput ) /* "LIFO> cmd ... " */
265             {
266                e->type = QUEUE;
267                e->queue = addr_reopen_queue( TSD, NULL, 'r' ) ; /* current */
268             }
269             else
270             {
271                if ( isString )
272                   e->type = STRING ;
273                else
274                {
275                   e->type = LIFOappend ;
276                   e->queue = addr_reopen_queue( TSD, NULL, 'A' ) ; /* current */
277                }
278             }
279          }
280          else /* "normal" use of WITH ??? LIFO ??? */
281          {
282             if ( e->flags.isinput ) /* "LIFO> cmd ... " */
283             {
284                e->type = QUEUE;
285                e->queue = addr_reopen_queue( TSD, e->currname, 'r' ) ;
286                if ( e->queue == NULL )
287                   exiterror( ERR_EXTERNAL_QUEUE, 109, tmpstr_of( TSD, e->currname ) ) ;
288             }
289             else
290             {
291                e->type = LIFO;
292                if (e->flags.append)
293                {
294                   e->type = LIFOappend;
295                   code = 'A' ;
296                }
297                else /* REPLACE */
298                {
299                   e->type = LIFO;
300                   code = 'R' ;
301                }
302                e->queue = addr_reopen_queue( TSD, e->currname, code ) ;
303                if ( e->queue == NULL )
304                   exiterror( ERR_EXTERNAL_QUEUE, 109, tmpstr_of( TSD, e->currname ) ) ;
305             }
306          }
307          break;
308 
309       case awtFIFO:
310          if ( overwrite != awtUNKNOWN )
311          {
312             if ( e->flags.isinput ) /* "FIFO> cmd ... " */
313             {
314                e->type = QUEUE;
315                e->queue = addr_reopen_queue( TSD, NULL, 'r' ) ; /* current */
316             }
317             else
318             {
319                if ( isString )
320                   e->type = STRING ;
321                else
322                {
323                   e->type = FIFOappend ;
324                   e->queue = addr_reopen_queue( TSD, NULL, 'A' ) ; /* current */
325                }
326             }
327          }
328          else /* "normal" use of WITH ??? FIFO ??? */
329          {
330             if ( e->flags.isinput ) /* "FIFO> cmd ... " */
331             {
332                e->type = QUEUE;
333                e->queue = addr_reopen_queue( TSD, e->currname, 'r' ) ;
334                if ( e->queue == NULL )
335                   exiterror( ERR_EXTERNAL_QUEUE, 109, tmpstr_of( TSD, e->currname ) ) ;
336             }
337             else
338             {
339                if (e->flags.append)
340                {
341                   e->type = FIFOappend;
342                   code = 'A' ;
343                }
344                else /* REPLACE */
345                {
346                   e->type = FIFO;
347                   code = 'R' ;
348                }
349                e->queue = addr_reopen_queue( TSD, e->currname, code ) ;
350                if ( e->queue == NULL )
351                   exiterror( ERR_EXTERNAL_QUEUE, 109, tmpstr_of( TSD, e->currname ) ) ;
352             }
353          }
354          break;
355 
356       default:
357          exiterror( ERR_INTERPRETER_FAILURE, 1, __FILE__, __LINE__, "Illegal address code in open_env_io()" )  ;
358    }
359 }
360 
put_stem(tsd_t * TSD,environpart * e,streng * str)361 void put_stem( tsd_t *TSD, environpart *e, streng *str )
362 /* Adds one line to the stem specified in e. */
363 {
364    e->maxnum = e->currnum ;
365    e->currnum++ ;
366    e->base->len = sprintf( e->base->value, "%d", e->maxnum ) ;
367    stem_access( TSD, e, 0, Str_dupTSD( e->base ) ) ;
368    stem_access( TSD, e, e->maxnum, str ) ;
369 }
370 
write_buffered(const tsd_t * TSD,int hdl,const void * buf,unsigned size,void * async_info)371 static int write_buffered(const tsd_t *TSD, int hdl, const void *buf,
372                                                unsigned size, void *async_info)
373 {
374    int rc, done;
375    unsigned todo;
376    shl_tsd_t *st = (shl_tsd_t *)TSD->shl_tsd;
377 
378    if ((buf == NULL) || (size == 0)) /* force flush buffers */
379    {
380       do
381       {
382          if (st->IOBused)
383          {
384             /* the following call will return -EAGAIN if not completed */
385             rc = TSD->OS->write(hdl, st->IObuf, st->IOBused, async_info);
386          }
387          else
388             rc = 0;
389          if (rc >= 0)
390          {
391             if (rc == (int) st->IOBused)
392             {
393                st->IOBused = 0;
394                rc = TSD->OS->write(hdl, NULL, 0, async_info);
395             }
396             else
397             {
398                memmove(st->IObuf, st->IObuf + rc, st->IOBused - rc);
399                st->IOBused -= rc;
400             }
401          }
402          else
403             TSD->OS->write(hdl, NULL, 0, async_info);
404          /*
405           * continue until everything written or an error like -EAGAIN
406           * is available. -EAGAIN will let the caller come back to this
407           * function when writing is possible again, so everything
408           * will be written after a while.
409           */
410       } while (st->IOBused && (rc >= 0));
411 
412       return(rc);
413    }
414 
415    done = 0;
416    while (size) {
417       /* step 1: fill buffer up to the maximum */
418       todo = size;
419       if (todo > sizeof(st->IObuf) - st->IOBused)
420          todo = sizeof(st->IObuf) - st->IOBused;
421       if (todo > 0)
422       {
423          memcpy(st->IObuf + st->IOBused, buf, todo);
424          st->IOBused += todo;
425       }
426       done += todo; /* dropped to the buffer --> done for upper layer */
427 
428       /* step 2: flush buffer, if buffer filled */
429       if (st->IOBused < sizeof(st->IObuf))
430          return(done);
431 
432       /* step 3: buffer full, giving optimal performance (I hope!) */
433       rc = TSD->OS->write(hdl, st->IObuf, st->IOBused, async_info);
434       if (rc <= 0)
435       {
436          if (done)
437             break; /* something done sucessfully */
438          return(rc);
439       }
440       if (rc == (int) st->IOBused)
441          st->IOBused = 0;
442       else
443       {
444          memmove(st->IObuf, st->IObuf + rc, st->IOBused - rc);
445          st->IOBused -= rc;
446       }
447 
448       /* just try another chunk of the input buffer */
449       buf = (const char *) buf + todo;
450       size -= todo;
451    }
452 
453    return(done); /* something done sucessfully */
454 }
455 
feed(const tsd_t * TSD,streng ** string,int hdl,void * async_info)456 static int feed( const tsd_t *TSD, streng **string, int hdl, void *async_info )
457 /* Writes the content of *string into the file associated with hdl. The
458  * string is shortened and, after the final write, deleted and *string set
459  * to NULL.
460  * async_info is both a structure and a flag. If set, asynchronous IO shall
461  * be used, otherwise blocked IO has to be used.
462  * feed returns 0 on success or an errno value on error.
463  * A return value of EAGAIN is set if we have to wait.
464  */
465 {
466    unsigned total ;
467    int done ;
468 
469    if ((string == NULL) || (*string == NULL))
470       return( 0 ) ;
471 
472    total = Str_len( *string ) ;
473    if (total == 0)
474       return( 0 ) ;
475 
476    done = write_buffered(TSD, hdl, (*string)->value, total, async_info);
477    if (done <= 0)
478    {
479       if (done == 0)      /* no error set? */
480          done = ENOSPC ;  /* good assumption */
481       else
482          done = -done;
483       if ((done != EAGAIN) && (done != EPIPE))
484          exiterror( ERR_INTERPRETER_FAILURE, 1, __FILE__, __LINE__, strerror(done) )  ;
485       return( done ) ; /* error */
486    }
487 
488    if ((unsigned) done < total)
489    {
490       (*string)->len -= done ;
491       memmove((*string)->value, (*string)->value + done, (*string)->len);
492    }
493    else
494    {
495       assert((unsigned)done==total);
496       Free_stringTSD(*string);
497       *string = NULL;
498    }
499    return(0);
500 }
501 
reap(const tsd_t * TSD,streng ** string,int hdl,void * async_info)502 static int reap( const tsd_t *TSD, streng **string, int hdl, void *async_info )
503 /* Reads data from the file associated with hdl and returns it in *string.
504  * *string may be NULL or valid, in which case it is expanded.
505  * reap returns 0 on success or an errno value on error. The value -1 indicates
506  * EOF.
507  * async_info is both a structure and a flag. If set, asynchronous IO shall
508  * be used, otherwise blocked IO has to be used.
509  * A maximum chunk of REGINA_MAX_BUFFER_SIZE bytes is read in one operation.
510  * A return value of EAGAIN is set if we have to wait.
511  */
512 {
513    char buf[REGINA_MAX_BUFFER_SIZE] ;
514    unsigned len, total ;
515    streng *s ;
516    int done ;
517 
518    if (string == NULL)
519       return( 0 ) ;
520 
521    done = TSD->OS->read( hdl, buf, sizeof(buf), async_info ) ;
522    if (done <= 0)
523    {
524       if (done == 0)
525          return( -1 ); /* EOF */
526       else
527          done = -done;
528       /* FGC, FIXME: Not sure, this is the right processing. Setting RS, etc? */
529       if ( done != EAGAIN )
530          exiterror( ERR_INTERPRETER_FAILURE, 1, __FILE__, __LINE__, strerror(done) )  ;
531       return( done ) ; /* error */
532    }
533 
534    if (( s = *string ) == NULL)
535    {
536       len = 0 ;
537       s = Str_makeTSD( done ) ;
538    }
539    else
540    {
541       len = Str_len( s ) ;
542       total = Str_max( s ) ;
543       if (len + done > total)
544       {
545          s = Str_makeTSD( len + done ) ;
546          s->len = len ;
547          memcpy( s->value, (*string)->value, len ) ;
548          Free_stringTSD( *string ) ;
549       }
550    }
551    memcpy( s->value + len, buf, done ) ;
552    s->len += done ;
553    *string = s ;
554    return( 0 ) ;
555 }
556 
cleanup_envirpart(const tsd_t * TSD,environpart * ep)557 void cleanup_envirpart(const tsd_t *TSD, environpart *ep)
558 /* Closes the associated file handles of ep and deletes a temporary file
559  * if necessary.
560  */
561 {
562    shl_tsd_t *st = (shl_tsd_t *)TSD->shl_tsd;
563 
564    if (ep->hdls[0] != -1)
565    {
566       TSD->OS->close(ep->hdls[0], (ep->FileRedirected) ? NULL : st->AsyncInfo);
567       ep->hdls[0] = -1;
568    }
569    if (ep->hdls[1] != -1)
570    {
571       TSD->OS->close(ep->hdls[1], (ep->FileRedirected) ? NULL : st->AsyncInfo);
572       ep->hdls[1] = -1;
573    }
574    if (ep->hdls[2] != -1)
575    {
576       TSD->OS->close_special(ep->hdls[2]);
577       ep->hdls[2] = -1;
578    }
579    if (ep->tempname)
580    {
581       unlink(ep->tempname);
582       FreeTSD(ep->tempname);
583       ep->tempname = NULL;
584    }
585    if ( ep->tmp_queue != NULL )
586    {
587       addr_close_queue( TSD, ep->tmp_queue ) ;
588       ep->tmp_queue = NULL ;
589    }
590    if ( ep->queue != NULL )
591    {
592       addr_close_queue( TSD, ep->queue ) ;
593       ep->queue = NULL ;
594    }
595 }
596 
cleanup(tsd_t * TSD,environment * env)597 static void cleanup( tsd_t *TSD, environment *env )
598 /* Closes all open handles in env and deletes temporary files. Already closed
599  * handles are marked by a value of -1.
600  * -1 is set to each handle after closing.
601  */
602 {
603    shl_tsd_t *st = (shl_tsd_t *)TSD->shl_tsd;
604 
605    cleanup_envirpart(TSD, &env->input);
606    cleanup_envirpart(TSD, &env->output);
607    cleanup_envirpart(TSD, &env->error);
608 
609    if (st->AsyncInfo)
610       TSD->OS->delete_async_info(st->AsyncInfo);
611    st->AsyncInfo = NULL;
612    st->IOBused = 0;
613 }
614 
615 /*
616  * CheckAndDealWithSameStems shall be used after opening all IO channels.
617  * It checks whether some of the channels address the same stem. This function
618  * takes care of this circumstances and prevents race conditions. In fact,
619  * in may produce a copy of the input to prevent overwriting.
620  */
CheckAndDealWithSameStems(tsd_t * TSD,environment * env)621 static void CheckAndDealWithSameStems( tsd_t *TSD, environment *env )
622 {
623    if ( ( env->output.type == STEM ) && ( env->error.type == STEM ) )
624    {
625       /* env->output.name and env->error.name must exist here
626        *
627        * We have to take special care if output and error are
628        * redirected to the same stem. We neither want to overwrite
629        * stem values twice nor want to read "stem.0" for every
630        * stem on every access to prevent it.
631        */
632       if ( Str_ccmp( env->output.currname, env->error.currname ) == 0 )
633       {
634          env->error.SameAsOutput = 1;
635          if ( env->error.maxnum == 0 )
636          {
637             /* error may has the REPLACE option while output has not.
638              * Force a silent replace in this case.
639              */
640             env->output.maxnum = 0;
641             env->output.currnum = 1;
642          }
643       }
644    }
645 
646    if ( env->input.type == STEM )
647    {
648       /* Same procedure. To prevent overwriting variables while
649        * outputting to a stem wherefrom we have to read, buffer
650        * the input stem if the names do overlap.
651        */
652 
653       if ( ( env->output.type == STEM ) &&
654            ( Str_ccmp( env->input.currname, env->output.currname ) == 0 ) )
655          env->input.SameAsOutput |= 1;
656 
657       if ( ( env->error.type == STEM ) &&
658            ( Str_ccmp( env->input.currname, env->error.currname ) == 0 ) )
659          env->input.SameAsOutput |= 2;
660 
661       if ( env->input.SameAsOutput )
662       {
663          /*
664           * Fixes bug 609017
665           */
666          env->input.currname->len = env->input.currnamelen;
667          env->input.tmp_queue =
668            fill_input_queue_stem( TSD, env->input.currname, env->input.maxnum);
669       }
670    }
671 }
672 
673 /*
674  * CheckSameStreams shall be used before opening all IO channels.
675  * It checks whether some of the channels address the same file. This function
676  * takes detects it and prepares the system to reduce file opening.
677  */
CheckSameStreams(tsd_t * TSD,int io_flags,environment * env)678 static void CheckSameStreams( tsd_t *TSD, int io_flags, environment *env )
679 {
680    environpart *e;
681    int i, isFile[3], mask;
682    const streng *name[3];
683    streng *full[3];
684 
685    memset( isFile, 0, sizeof( isFile ) );
686    memset( (void *)name, 0, sizeof( name ) );
687 
688    for ( i = 0; i < 3; i++ )
689    {
690       switch ( i )
691       {
692          case 0:
693             mask = REDIR_INPUT;
694             e = &env->input;
695             break;
696 
697          case 1:
698             mask = REDIR_OUTLIFO | REDIR_OUTFIFO | REDIR_OUTSTRING;
699             e = &env->output;
700             break;
701 
702          default:
703             mask = 0;
704             e = &env->error;
705             break;
706 
707       }
708 
709       e->SameAsOutput = 0;
710       full[i] = NULL;
711       if ( ( io_flags & mask ) ||
712            ( e->flags.awt != awtSTREAM ) )
713          continue;
714 
715       name[i] = e->name;
716       if ( ( name[i] != NULL ) && ( name[i]->len == 0 ) )
717          name[i] = NULL;
718 
719       if ( ( e->flags.ant == antSIMSYMBOL ) && ( name[i] != NULL ) )
720       {
721          name[i] = getvalue( TSD, name[i], -1 ) ;
722       }
723       else
724       {
725          assert( ( e->flags.ant == antSTRING ) || ( name[i] == NULL ) );
726       }
727 
728       isFile[i] = 1;
729       /*
730        * Delay the computation of the fully qualified filename until we
731        * know that it is useful.
732        */
733    }
734    if ( isFile[0] + isFile[1] + isFile[2] > 1 )
735    {
736       for ( i = 0; i < 3; i++ )
737       {
738          full[i] = addr_file_info( TSD, name[i], i );
739       }
740    }
741    if ( ( full[0] != NULL ) && ( full[1] != NULL ) )
742       env->input.SameAsOutput |= filename_cmp( full[0], full[1] ) ? 0 : 1;
743    if ( ( full[0] != NULL ) && ( full[2] != NULL ) )
744       env->input.SameAsOutput |= filename_cmp( full[0], full[2] ) ? 0 : 2;
745    if ( ( full[1] != NULL ) && ( full[2] != NULL ) )
746       env->error.SameAsOutput |= filename_cmp( full[1], full[2] ) ? 0 : 1;
747 
748    if ( env->error.SameAsOutput && ( env->error.flags.append == 0 ) )
749       env->output.flags.append = 0;
750    for ( i = 0; i < 3; i++ )
751    {
752       if ( full[i] )
753       {
754          Free_stringTSD( full[i] );
755       }
756    }
757 }
758 
759 /*
760  * CheckAndDealWithSameQueues shall be used after opening all IO channels.
761  * It checks whether some of the channels address the same queue. This function
762  * takes care of this circumstances and prevents race conditions. In fact,
763  * in may produce a copy of the input to prevent overwriting.
764  */
CheckAndDealWithSameQueues(tsd_t * TSD,environment * env)765 static void CheckAndDealWithSameQueues( tsd_t *TSD, environment *env )
766 {
767    if ( ( env->output.type & ( LIFO | FIFO | LIFOappend | FIFOappend ) )
768      && ( env->error.type  & ( LIFO | FIFO | LIFOappend | FIFOappend ) ) )
769    {
770       /*
771        * We have to take special care if output and error are
772        * redirected to the same queue. We neither want to push
773        * values twice nor want to read our own output for every
774        * stem on every access to prevent it.
775        */
776       if ( addr_same_queue( TSD, env->output.queue, env->error.queue ) )
777       {
778          env->error.SameAsOutput = 1;
779          if ( env->error.type  & ( LIFO | FIFO ) )
780          {
781             /* error may has the REPLACE option while output has not.
782              * Force a silent replace in this case.
783              */
784             if ( env->output.type == LIFOappend )
785                env->output.type = LIFO;
786             if ( env->output.type == FIFOappend )
787                env->output.type = FIFO;
788          }
789       }
790    }
791 
792    if ( env->input.type == QUEUE )
793    {
794       /* Same procedure. To prevent overwriting values while
795        * outputting to a queue wherefrom we have to read, buffer
796        * the input queue if the queue are same.
797        */
798 
799       if ( get_options_flag( TSD->currlevel, EXT_FLUSHSTACK ) == 0 )
800       {
801          if ( ( env->output.type & ( LIFO | FIFO | LIFOappend | FIFOappend ) )
802            && addr_same_queue( TSD, env->input.queue, env->output.queue ) )
803             env->input.SameAsOutput |= 1;
804 
805          if ( ( env->error.type & ( LIFO | FIFO | LIFOappend | FIFOappend ) )
806            && addr_same_queue( TSD, env->input.queue, env->error.queue ) )
807             env->input.SameAsOutput |= 2;
808 
809          if (env->input.SameAsOutput)
810             env->input.tmp_queue = addr_redir_queue( TSD, env->input.queue );
811       }
812    }
813 
814    /* Final stages for queues: if not "append", do a replace by purging */
815    if ( ( env->output.type == FIFO )
816      || ( env->output.type == LIFO ) )
817       addr_purge_queue( TSD, env->output.queue );
818    if ( ( ( env->error.type == FIFO )
819        || ( env->error.type == LIFO ) )
820      && !env->error.SameAsOutput )
821       addr_purge_queue( TSD, env->error.queue );
822    /* reduce used names */
823    if ( env->output.type == FIFOappend )
824       env->output.type = FIFO;
825    if ( env->output.type == LIFOappend )
826       env->output.type = LIFO;
827    if ( env->error.type == FIFOappend )
828       env->error.type = FIFO;
829    if ( env->error.type == LIFOappend )
830       env->error.type = LIFO;
831 }
832 
setup_io(tsd_t * TSD,int io_flags,environment * env)833 static int setup_io( tsd_t *TSD, int io_flags, environment *env )
834 /* Sets up the IO-redirections based on the values in io_flags and env.
835  * Each environpart (env->input, env->output, env->error) is set up as follows:
836  * a) The enviroment-based streams and stems are set up if used or not.
837  *    env->input.type (or output or error) is set to STREAM, STEM or STD_IO.
838  * b) The io_flags overwrite the different settings and may have
839  *    values QUEUE, simLIFO, simFIFO, STRING.
840  * c) If a redirection takes place (type != STD_IO) a pipe() or temporary
841  *    file is opened and used.
842  * This function returns 1 on success, 0 on error, in which case an error is
843  * already reported..
844  */
845 {
846    shl_tsd_t *st = (shl_tsd_t *)TSD->shl_tsd;
847    int overwrite;
848 
849    cleanup( TSD, env ); /* Useful in case of an undetected previous error */
850 
851    prepare_env_io( &env->input );
852    prepare_env_io( &env->output );
853    prepare_env_io( &env->error );
854 
855    CheckSameStreams( TSD, io_flags, env );
856    /*
857     * Determine which ANSI redirections are in effect
858     * Use the special io_flags for redirection to overwrite the standard
859     * rules of the environment.
860     */
861    overwrite = ( io_flags & REDIR_INPUT ) ? awtFIFO : awtUNKNOWN;
862    open_env_io( TSD, &env->input, overwrite, 0 );
863    if ( env->input.SameAsOutput )
864    {
865       /*
866        * It must be a file since we don't have checked for stems and queues.
867        * We read the input into a temporary queue, then we can proceed in the
868        * usual way.
869        */
870       env->input.tmp_queue = fill_input_queue_stream( TSD, env->input.file );
871       addr_reset_file( TSD, env->input.file );
872       env->input.file = NULL;
873    }
874 
875    if ( io_flags & REDIR_OUTLIFO )
876       overwrite = awtLIFO;
877    else if ( io_flags & REDIR_OUTFIFO )
878       overwrite = awtFIFO;
879    else if ( io_flags & REDIR_OUTSTRING )
880       overwrite = awtFIFO;
881    else
882       overwrite = awtUNKNOWN;
883    open_env_io( TSD, &env->output, overwrite, io_flags & REDIR_OUTSTRING );
884 
885    if ( env->error.SameAsOutput )
886    {
887       /*
888        * It must be a file since we don't have checked for stems and queues.
889        * We read the input into a temporary queue or a temporary file.
890        * Then we can proceed in the usual way.
891        */
892       env->error.type = STREAM;
893    }
894    else
895       open_env_io( TSD, &env->error, awtUNKNOWN, 0 );
896 
897    CheckAndDealWithSameStems( TSD, env );
898    CheckAndDealWithSameQueues( TSD, env );
899 
900    if ( env->input.type != STD_IO )
901    {
902       if ( TSD->OS->open_subprocess_connection( TSD, &env->input ) != 0 )
903       {
904          cleanup( TSD, env );
905          exiterror( ERR_SYSTEM_FAILURE, 920, "creating redirection", "for input", strerror(errno) );
906          return 0;
907       }
908    }
909    if ( env->output.type != STD_IO )
910    {
911       if ( TSD->OS->open_subprocess_connection( TSD, &env->output ) != 0 )
912       {
913          cleanup( TSD, env );
914          exiterror( ERR_SYSTEM_FAILURE, 920, "creating redirection", "for output", strerror(errno) );
915          return 0;
916       }
917    }
918    else
919       fflush( stdout );
920    if ( env->error.type != STD_IO )
921    {
922       if ( TSD->OS->open_subprocess_connection( TSD, &env->error ) != 0 )
923       {
924          cleanup( TSD, env );
925          exiterror( ERR_SYSTEM_FAILURE, 920, "creating redirection", "for error", strerror(errno) );
926          return 0;
927       }
928    }
929    else
930       fflush( stderr );
931    st->AsyncInfo = TSD->OS->create_async_info( TSD );
932    return 1;
933 }
934 
fetch_food(tsd_t * TSD,environment * env)935 static streng *fetch_food( tsd_t *TSD, environment *env )
936 /* returns one streng fetched either from a queue (env->input.type == QUEUE) or
937  * from a stem or stream.
938  * Returns NULL if there is no more input to feed the child process.
939  */
940 {
941    const streng *c ;
942    streng *retval ;
943    int delflag = 0 ;
944 
945    switch (env->input.type)
946    {
947       case QUEUE:
948          delflag = 1 ;
949          if ( env->input.tmp_queue )
950             c = addr_io_queue( TSD, env->input.tmp_queue, NULL, 0 ) ;
951          else
952             c = addr_io_queue( TSD, env->input.queue, NULL, 0 ) ;
953          if ( c == NULL )
954             return NULL ;
955          break;
956 
957       case STREAM:
958          delflag = 1;
959 
960          if ( env->input.tmp_queue )
961          {
962             c = addr_io_queue( TSD, env->input.tmp_queue, NULL, 0 );
963             if ( c == NULL )
964                return NULL;
965             break;
966          }
967          if (env->input.file == NULL)
968             return NULL;
969          c = addr_io_file( TSD, env->input.file, NULL );
970          if ( !c )
971             return NULL;
972          if ( c->len == 0 )
973          {
974             Free_stringTSD( (streng *) c );
975             return NULL;
976          }
977          break;
978 
979       case STEM:
980          if (!env->input.SameAsOutput)
981          {
982             if (env->input.currnum > env->input.maxnum)
983                return( NULL ) ;
984             c = stem_access( TSD, &env->input, env->input.currnum++, NULL ) ;
985          }
986          else
987          {
988             delflag = 1 ;
989             c = addr_io_queue( TSD, env->input.tmp_queue, NULL, 0 ) ;
990          }
991          if (!c)
992             return( NULL ) ;
993          break;
994 
995       default:
996          exiterror( ERR_INTERPRETER_FAILURE, 1, __FILE__, __LINE__, "Illegal feeder in fetch_food()" )  ;
997          return( NULL ) ;
998          break ;
999    }
1000 
1001    if ( env->input.type != STREAM )
1002    {
1003       if ( env->input.flags.noeol )
1004       {
1005          /* ADDRESS SYSTEM ... WITH INPUT NOEOL ... */
1006          assert( delflag ) ;
1007          retval = (streng *) c ; /* will be destroyed */
1008       }
1009       else
1010       {
1011          /* Append a newline to the end of the line before returning */
1012 #if defined(DOS) || defined(OS2) || defined(WIN32)
1013          retval = Str_makeTSD( c->len + 2 ) ;
1014          memcpy(retval->value, c->value, c->len);
1015          retval->value[c->len] = REGINA_CR;
1016          retval->value[c->len + 1] = REGINA_EOL;
1017          retval->len = c->len + 2;
1018 #else
1019          retval = Str_makeTSD( c->len + 1 ) ;
1020          memcpy(retval->value, c->value, c->len);
1021          retval->value[c->len] = REGINA_EOL;
1022          retval->len = c->len + 1;
1023 #endif
1024          if (delflag)
1025             Free_stringTSD( (streng *) c ) ;
1026       }
1027    }
1028    else
1029    {
1030       /* STREAM mode reads blocks of buffers, usually 4KB ignoring any
1031        * line structure
1032        */
1033       assert( delflag ) ;
1034       retval = (streng *) c ; /* will be destroyed */
1035    }
1036    return( retval ) ;
1037 }
1038 
drop_crop_line(tsd_t * TSD,environment * env,const char * data,unsigned length,int is_error)1039 static void drop_crop_line( tsd_t *TSD, environment *env, const char *data,
1040                                                 unsigned length, int is_error )
1041 /* Called while reading the output of the child. The output is in data and
1042  * contains length bytes without the line terminator.
1043  * which may be empty or not yet completed. The exact destination is determined
1044  * by env->x.type, where x is either output or error depending on is_error.
1045  * type may have one of the values simLIFO, simFIFO, STRING, STREAM or STEM.
1046  * is_error is set if the error redirection should happen.
1047  */
1048 {
1049    streng *string ;
1050    int type;
1051 
1052    string = Str_makeTSD( length + 1 ) ; /* We need a terminating 0 in some */
1053                                         /* cases                           */
1054    memcpy( string->value, data, length ) ;
1055    string->len = length ;
1056    string->value[length] = '\0' ;
1057 
1058    if (is_error)
1059       type = env->error.type;
1060    else
1061       type = env->output.type;
1062 
1063    switch (type)
1064    {
1065       case LIFO:
1066          if ( is_error && !env->error.SameAsOutput )
1067          {
1068             if ( env->error.tmp_queue != NULL )
1069                addr_io_queue( TSD, env->error.tmp_queue, string, 0 ) ;
1070             else
1071                addr_io_queue( TSD, env->error.queue, string, 0 ) ;
1072          }
1073          else
1074          {
1075             if ( env->output.tmp_queue != NULL )
1076                addr_io_queue( TSD, env->output.tmp_queue, string, 0 ) ;
1077             else
1078                addr_io_queue( TSD, env->output.queue, string, 0 ) ;
1079          }
1080          return;  /* consumes the new string */
1081 
1082       case FIFO:
1083       case STRING:
1084          if ( is_error && !env->error.SameAsOutput )
1085          {
1086             if ( env->error.tmp_queue != NULL )
1087                addr_io_queue( TSD, env->error.tmp_queue, string, 1 ) ;
1088             else
1089                addr_io_queue( TSD, env->error.queue, string, 1 ) ;
1090          }
1091          else
1092          {
1093             if ( env->output.tmp_queue != NULL )
1094                addr_io_queue( TSD, env->output.tmp_queue, string, 1 ) ;
1095             else
1096                addr_io_queue( TSD, env->output.queue, string, 1 ) ;
1097          }
1098          return;  /* consumes the new string */
1099 
1100       case STREAM:
1101          exiterror( ERR_INTERPRETER_FAILURE, 1, __FILE__, __LINE__, "Illegal STREAM in drop_crop_line()" )  ;
1102          break;
1103 
1104       case STEM:
1105          if (is_error && !env->error.SameAsOutput)
1106             put_stem( TSD, &env->error, string ) ;
1107          else
1108             put_stem( TSD, &env->output, string ) ;
1109          return;  /* consumes the new string */
1110 
1111       default:
1112          exiterror( ERR_INTERPRETER_FAILURE, 1, __FILE__, __LINE__, "Illegal crop in drop_crop_line()" )  ;
1113          break ;
1114    }
1115 
1116    Free_stringTSD( string ) ;
1117 }
1118 
1119 /* line_length tries to find ANY line terminator. This is either \r,
1120  * \n, \r\n or \n\r.
1121  * The lookup happens in line (not 0-terminated) with a length of len.
1122  * The return value is either -1 (not found) or the length of the line
1123  * content. *termlen is set to the number of characters of the line
1124  * terminator, either 1 or 2.
1125  * EOFreached is boolean and indicated a final line if set.
1126  */
line_length(const char * line,int len,int * termlen,int EOFreached)1127 static int line_length(const char *line, int len, int *termlen, int EOFreached)
1128 {
1129    char *ccr;
1130    char *clf;
1131    int h;
1132 
1133    ccr = (char *)memchr( line, '\r', len ) ;
1134    clf = (char *)memchr( line, '\n', len ) ;
1135    if ( ccr != NULL )
1136    {
1137       if ( clf != NULL )
1138       {
1139          if ( clf < ccr )
1140          {
1141             *termlen = ( ccr == clf + 1 ) ? 2 : 1 ;
1142             return (int) ( clf - line ) ;
1143          }
1144          else
1145          {
1146             *termlen = ( clf == ccr + 1 ) ? 2 : 1 ;
1147             return (int) ( ccr - line ) ;
1148          }
1149       }
1150       /* else '\r' found, but we must know if it terminates */
1151       h = (int) ( ccr - line ) ;
1152       if ( ( h + 1 < len ) || EOFreached )
1153       {
1154          *termlen = 1 ;
1155          return h ;
1156       }
1157    }
1158    else if ( clf != NULL ) /* simple line feed */
1159    {
1160       h = (int) ( clf - line ) ;
1161       if ( ( h + 1 < len ) || EOFreached )
1162       {
1163          *termlen = 1 ;
1164          return h ;
1165       }
1166    }
1167    return -1 ;
1168 }
1169 
drop_crop(tsd_t * TSD,environment * env,streng ** string,int EOFreached,int is_error)1170 static void drop_crop( tsd_t *TSD, environment *env, streng **string,
1171                                                   int EOFreached, int is_error)
1172 /* Called while reading the output of the child. The output is in *string,
1173  * which may be empty or not yet completed. The exact destination is determined
1174  * by env->x.type, where x is either output or error depending on is_error.
1175  * type may have one of the values simLIFO, simFIFO, STRING, STREAM or STEM.
1176  * If EOFreached is set and some data is in *string, this data is interpreted
1177  * as a completed line.
1178  * Completed lines are cut of the string. The string itself isn't deleted.
1179  * is_error is set if the error redirection should happen.
1180  */
1181 {
1182    streng *s ;
1183    char *ptr ;
1184    void *fptr = NULL ;
1185    int max, found, termlen, isStream ;
1186 
1187 
1188    s = *string;
1189    if (s == NULL) /* might happen on a first call */
1190       return;
1191 
1192    if (is_error)
1193    {
1194       isStream = ( env->error.type == STREAM ) ;
1195       fptr = ( env->error.SameAsOutput ) ? env->output.file : env->error.file;
1196    }
1197    else
1198    {
1199       isStream = ( env->output.type == STREAM ) ;
1200       fptr = env->output.file ;
1201    }
1202 
1203    if ( isStream )
1204    {
1205       /* very fast bypass, we can simply drop the whole thing */
1206       if ( fptr )
1207          addr_io_file( TSD, fptr, s ) ;
1208 
1209       s->len = 0 ;
1210    }
1211    else
1212    {
1213       ptr = s->value ;
1214       max = Str_len( s ) ;
1215 
1216       while ( max > 0 )
1217       {
1218          found = line_length( ptr, max, &termlen, EOFreached ) ;
1219 
1220          if ( ( found == -1 ) && EOFreached )
1221          {
1222             found = max ;
1223             termlen = 0 ;
1224          }
1225          if ( found < 0 )
1226             break;
1227 
1228          drop_crop_line( TSD, env, ptr, (unsigned) found, is_error ) ;
1229          found += termlen ;
1230          max -= found ;
1231          ptr += found ;
1232       }
1233       if (max > 0)
1234       {
1235          if (ptr == s->value)
1236          {
1237             if (max != Str_len(s))
1238             {
1239                exiterror( ERR_INTERPRETER_FAILURE, 1, __FILE__, __LINE__, "Illegal memory access");
1240             }
1241          }
1242          else
1243             memmove( s->value, ptr, max ) ;
1244       }
1245       s->len = max ;
1246    }
1247    *string = s ;
1248 }
1249 
posix_do_command(tsd_t * TSD,const streng * command,int io_flags,environment * env,Queue * redir)1250 int posix_do_command( tsd_t *TSD, const streng *command, int io_flags, environment *env, Queue *redir )
1251 {
1252    int child, rc ;
1253    int in, out, err;
1254    streng *istring = NULL, *ostring = NULL, *estring = NULL ;
1255    char *cmdline ;
1256    shl_tsd_t *st = (shl_tsd_t *)TSD->shl_tsd;
1257 #ifdef SIGPIPE
1258    signal_handler prev_sig;
1259 #endif
1260 
1261    fflush( stdout ) ;
1262    fflush( stderr ) ;
1263 
1264    CloseOpenFiles( TSD, fpdRETAIN );
1265 
1266    /*
1267     * Fixes bug 615822
1268     */
1269    if (!setup_io(TSD, io_flags, env))
1270       exiterror( ERR_SYSTEM_FAILURE, 0 )  ;
1271 
1272    if (env->input.FileRedirected)
1273    {
1274       /* fill up the input file without closing the stream. */
1275 
1276       while ((istring = fetch_food(TSD, env)) != NULL)
1277       {
1278          if (feed(TSD, &istring, env->input.hdls[1], NULL) != 0)
1279             break; /* shall not happen! */
1280       }
1281       rc = write_buffered(TSD, env->input.hdls[1], NULL, 0, NULL);
1282       if (rc < 0)
1283       {
1284          errno = -rc;
1285          exiterror( ERR_SYSTEM_FAILURE, 920, "feeding redirection file", "for input", strerror(errno) );
1286       }
1287       /* seek positions of both fdin may have been destroyed */
1288       TSD->OS->restart_file(env->input.hdls[0]);
1289       TSD->OS->close(env->input.hdls[1], NULL);
1290       env->input.hdls[1] = -1;
1291    }
1292 
1293    if ( env->output.type == STRING )
1294       env->output.tmp_queue = redir ;
1295 
1296    cmdline = str_ofTSD( command ) ;
1297    child = TSD->OS->fork_exec( TSD, env, cmdline, &rc );
1298    FreeTSD( cmdline ) ;
1299    if ( ( child == -1 ) || ( child == 0 ) )
1300    {
1301       err = errno;
1302       cleanup( TSD, env ) ;
1303       if ( child == -1 )
1304          exiterror( ERR_SYSTEM_FAILURE, 1, strerror( err ) );
1305       return ( rc > 0 ) ? -rc : ( rc == 0 ) ? -1000 : rc;
1306    }
1307 
1308    /* Close the child part of the handles */
1309    if (env->input.hdls[0]  != -1) TSD->OS->close(env->input.hdls[0], NULL) ;
1310    if (env->output.hdls[1] != -1) TSD->OS->close(env->output.hdls[1], NULL) ;
1311    if (env->error.hdls[1]  != -1) TSD->OS->close(env->error.hdls[1], NULL) ;
1312    env->input.hdls[0] = env->output.hdls[1] = env->error.hdls[1] = -1;
1313 
1314    /* Force our own handles to become nonblocked */
1315    if (!env->input.FileRedirected && ((in = env->input.hdls[1]) != -1))
1316    {
1317       TSD->OS->unblock_handle( &in, st->AsyncInfo ) ;
1318    }
1319    else
1320       in = -1;
1321    if (!env->output.FileRedirected && ((out = env->output.hdls[0]) != -1))
1322    {
1323       TSD->OS->unblock_handle( &out, st->AsyncInfo ) ;
1324    }
1325    else
1326       out = -1;
1327    if (!env->error.FileRedirected && ((err = env->error.hdls[0]) != -1))
1328    {
1329       TSD->OS->unblock_handle( &err, st->AsyncInfo ) ;
1330    }
1331    else
1332       err = -1;
1333 
1334 #ifdef SIGPIPE
1335     prev_sig = regina_signal( SIGPIPE, SIG_IGN ) ;
1336 #endif
1337 
1338    while ((in != -1) || (out != -1) || (err != -1))
1339    {
1340       TSD->OS->reset_async_info(st->AsyncInfo);
1341       if (in != -1)
1342       {
1343          do {
1344             if (!istring)
1345                istring = fetch_food( TSD, env ) ;
1346             if (!istring)
1347             {
1348                rc = write_buffered(TSD, in, NULL, 0, st->AsyncInfo);
1349                if (rc == -EAGAIN)
1350                   TSD->OS->add_async_waiter(st->AsyncInfo, in, 0);
1351                else
1352                {
1353                   if ((rc < 0) && (-rc != EPIPE)) /* fixes bug 945218 */
1354                   {
1355                      errno = -rc;
1356                      exiterror( ERR_SYSTEM_FAILURE, 920, "writing to", "input redirection", strerror(errno) );
1357                   }
1358                   if (TSD->OS->close(in, st->AsyncInfo))
1359                      exiterror( ERR_SYSTEM_FAILURE, 920, "closing redirection", "for input", strerror(errno) );
1360                   env->input.hdls[1] = in = -1 ;
1361                   rc = -1 ; /* indicate a closed stream */
1362                }
1363             }
1364             else  /* nothing left in string, but more in the stack */
1365             {
1366                rc = feed( TSD, &istring, in, st->AsyncInfo ) ;
1367                if (rc)
1368                {
1369                   if (rc == EAGAIN)
1370                      TSD->OS->add_async_waiter(st->AsyncInfo, in, 0);
1371                   else
1372                   {
1373                      TSD->OS->close(in, st->AsyncInfo) ;
1374                      env->input.hdls[1] = in = -1 ;
1375                   }
1376                }
1377                else if (istring != NULL)
1378                {
1379                   /* hasn't written all at once, therefore is blocked.
1380                    * do a little performance boost and don't try to write
1381                    * once more, perform the wait instead.
1382                    */
1383                   rc = -1;
1384                   TSD->OS->add_async_waiter(st->AsyncInfo, in, 0);
1385                }
1386             }
1387          } while (rc == 0); /* It is best for performance and no penalty for */
1388                             /* security to write as much as possible         */
1389 
1390       } /* if (in != -1) */
1391 
1392       if (out != -1)
1393       {
1394          do {
1395             rc = reap( TSD, &ostring, out, st->AsyncInfo );
1396             if (rc)
1397             {
1398                if (rc == EAGAIN)
1399                   TSD->OS->add_async_waiter(st->AsyncInfo, out, 1);
1400                else
1401                {
1402                   TSD->OS->close(out, st->AsyncInfo) ;
1403                   env->output.hdls[0] = out = -1 ;
1404                }
1405             }
1406             else if (ostring != NULL)
1407                drop_crop( TSD, env, &ostring, 0, 0 ) ;
1408          } while (rc == 0); /* It is best for performance and no penalty for */
1409                             /* security to write as much as possible         */
1410       } /* if (out != -1) */
1411 
1412       if (err != -1)
1413       {
1414          do {
1415             rc = reap( TSD, &estring, err, st->AsyncInfo );
1416             if (rc)
1417             {
1418                if (rc == EAGAIN)
1419                   TSD->OS->add_async_waiter(st->AsyncInfo, err, 1);
1420                else
1421                {
1422                   TSD->OS->close(err, st->AsyncInfo) ;
1423                   env->error.hdls[0] = err = -1 ;
1424                }
1425             }
1426             else if (estring != NULL)
1427                drop_crop( TSD, env, &estring, 0, 1 ) ;
1428          } while (rc == 0); /* It is best for performance and no penalty for */
1429                             /* security to write as much as possible         */
1430       } /* if (err != -1) */
1431 
1432       TSD->OS->wait_async_info(st->AsyncInfo); /* wait for any more IO */
1433    } /* end of IO */
1434 
1435    if (istring)
1436       Free_stringTSD( istring );
1437 
1438    if (ostring)
1439    {
1440       if ( Str_len( ostring ) )
1441          drop_crop( TSD, env, &ostring, 1, 0 );
1442       Free_stringTSD( ostring );
1443    }
1444 
1445    if (estring)
1446    {
1447       if ( Str_len( estring ) )
1448          drop_crop( TSD, env, &estring, 1, 1 );
1449       Free_stringTSD( estring );
1450    }
1451 
1452    if ( ( env->input.type == QUEUE ) && ( env->input.tmp_queue == NULL ) )
1453       addr_purge_queue( TSD, env->input.queue ) ;
1454 
1455    rc = TSD->OS->wait(child);
1456 
1457 #ifdef SIGPIPE
1458    regina_signal( SIGPIPE, prev_sig ) ;
1459 //   regina_signal( SIGPIPE, SIG_DFL ) ;
1460 #endif
1461 
1462    if (env->output.FileRedirected)
1463    {
1464       /* The file position is usually at the end: */
1465       TSD->OS->restart_file(env->output.hdls[0]);
1466       while (reap( TSD, &ostring, env->output.hdls[0], NULL ) == 0)
1467       {
1468          if (ostring != NULL)
1469             drop_crop( TSD, env, &ostring, 0, 0 ) ;
1470       }
1471       if (ostring != NULL)
1472          drop_crop( TSD, env, &ostring, 1, 0 ) ;
1473 
1474       /* use the automatted closing feature of cleanup */
1475    }
1476    if (env->error.FileRedirected)
1477    {
1478       /* The file position is usually at the end: */
1479       TSD->OS->restart_file(env->error.hdls[0]);
1480       while (reap( TSD, &estring, env->error.hdls[0], NULL ) == 0) {
1481          if (estring != NULL)
1482             drop_crop( TSD, env, &estring, 0, 1 ) ;
1483       }
1484       if (estring != NULL)
1485          drop_crop( TSD, env, &estring, 1, 1 ) ;
1486       /* use the automatted closing feature of cleanup */
1487    }
1488 
1489    if ( env->output.type & ( LIFO | FIFO ) ) /* never use STRING here */
1490       flush_stack( TSD, env->output.tmp_queue, env->output.queue, env->output.type == FIFO ) ;
1491    if ( env->error.type & ( LIFO | FIFO ) )
1492       flush_stack( TSD, env->error.tmp_queue, env->error.queue, env->output.type == FIFO ) ;
1493 
1494    if ( ( env->input.type == STREAM ) && ( env->input.file != NULL ) )
1495       addr_reset_file( TSD, env->input.file );
1496    if ( env->output.type == STREAM )
1497       addr_reset_file( TSD, env->output.file );
1498    if ( ( env->error.type == STREAM ) && !env->error.SameAsOutput )
1499       addr_reset_file( TSD, env->error.file );
1500    if ( env->output.type == STRING )
1501       env->output.tmp_queue = NULL ;
1502 
1503    cleanup( TSD, env ) ;
1504 
1505    return rc ;
1506 }
1507