2 * The Regina Rexx Interpreter
3 * Copyright (C) 1992-1994 Anders Christensen <anders@pvv.unit.no>
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.
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.
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.
25 /* Stupid MSC can't compile own headers without warning at least in VC 5.0 */
26 # pragma warning(disable: 4115 4201 4214 4514)
32 # pragma warning(default: 4115 4201 4214)
51 # define fork() vfork()
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
58 # define posix_do_command __regina_vms_do_command
62 #if defined(__WINS__) || defined(__EPOC32__)
63 # define REGINA_MAX_BUFFER_LENGTH 256
65 # define REGINA_MAX_BUFFER_LENGTH 4096
75 #define LIFOappend 0x80
76 #define FIFOappend 0x100
78 #if defined(_POSIX_PIPE_BUF) && !defined(PIPE_BUF)
79 # define PIPE_BUF _POSIX_PIPE_BUF
82 typedef struct { /* shl_tsd: static variables of this module (thread-safe) */
88 unsigned char IObuf
[4096]; /* write cache */
90 } shl_tsd_t
; /* thread-specific but only needed by this module. see
94 /* init_shell initializes the module.
95 * Currently, we set up the thread specific data.
96 * The function returns 1 on success, 0 if memory is short.
98 int init_shell( tsd_t
*TSD
)
102 if ( TSD
->shl_tsd
!= NULL
)
105 if ( ( TSD
->shl_tsd
= MallocTSD( sizeof(shl_tsd_t
) ) ) == NULL
)
107 st
= (shl_tsd_t
*)TSD
->shl_tsd
;
108 memset( st
, 0, sizeof(shl_tsd_t
) ); /* correct for all values */
112 static const streng
*stem_access( tsd_t
*TSD
, environpart
*e
, int pos
,
114 /* appends "."+itoa(pos) to e->currname and accesses this variable.
115 * value is NULL to access the current value or non-NULL to set the new value.
116 * The return value is NULL if a new value is set or the old one.
121 leaflen
= sprintf( e
->currname
->value
+ e
->currnamelen
, "%d", pos
) ;
123 e
->currname
->len
= e
->currnamelen
+ leaflen
;
126 * FGC: Changed back from get_it_anyway_compound to getvalue.
127 * This will raise a NOVALUE condition if we try to read
129 * Furthermore we allow the user to use extended stems
130 * like "A.B." if we ever want to allow it. We do so by
131 * changing from [sg]etdirvalue_compound to [sg]etvalue.
134 return( getvalue( TSD
, e
->currname
, -1 ) ) ;
136 setvalue( TSD
, e
->currname
, value
, -1 ) ;
140 static void set_currname( tsd_t
*TSD
, environpart
*e
)
141 /* Sets the initial currname of the environpart from its name. e->currname
142 * will be freed at first if not set to NULL.
145 const streng
*source
= e
->name
;
147 if (e
->flags
.ant
== antSIMSYMBOL
)
150 * Just in case of a stem we access the variable directly. Otherwise
151 * we have to resolve the symbol.
153 if ( (source
->len
> 0) && ( e
->flags
.awt
!= awtSTEM
) )
155 source
= getvalue( TSD
, source
, -1 ) ;
160 assert( ( e
->flags
.ant
== antSTRING
) || ( source
== NULL
) );
163 if (e
->currname
!= NULL
)
164 Free_stringTSD( e
->currname
) ;
167 * We need space for "." and the maximal number, but first check if we
168 * have a "default" value.
170 if ( source
== NULL
)
175 e
->currnamelen
= Str_len( source
) ;
176 e
->currname
= Str_makeTSD( e
->currnamelen
+ 3*sizeof(int) ) ;
177 memcpy( e
->currname
->value
, source
->value
, e
->currnamelen
) ;
178 e
->currname
->len
= e
->currnamelen
; /* pro forma, will be recomputed */
181 static void prepare_env_io( environpart
*e
)
183 * Prepares the WITH-IO-redirection from envpart.
187 e
->FileRedirected
= 0;
188 e
->tempname
= NULL
; /* none as default, might become char* RedirTempFile */
192 e
->hdls
[0] = e
->hdls
[1] = e
->hdls
[2] = -1;
195 static void open_env_io( tsd_t
*TSD
, environpart
*e
, unsigned overwrite
, int isString
)
196 /* Opens the WITH-IO-redirection from envpart and sets *flag to either
197 * STREAM or STEM. Nothing happens if there isn't a redirection.
198 * The protect-fields are used by the
206 if ( ( e
->name
== NULL
) && !overwrite
&& ( e
->flags
.awt
== awtUNKNOWN
) )
209 set_currname(TSD
, e
);
210 if ( overwrite
== awtUNKNOWN
)
218 * For a STREAM input/output redirection, set the file reopen
219 * flag, and reopen the file.
222 if ( e
->flags
.isinput
)
224 else if ( e
->flags
.append
)
228 if ( e
->flags
.isinput
|| !e
->SameAsOutput
)
229 e
->file
= addr_reopen_file( TSD
, e
->currname
, code
,
235 * For a STEM input/output redirection, check that existing state of
236 * the stem if appropriate and initialise the stem
240 if (e
->flags
.isinput
|| e
->flags
.append
)
243 * For a STEM input redirection, the stem must already exist and be
244 * a valid Rexx "array". This happens to an existing output stem in
245 * the append mode, too.
247 h
= stem_access( TSD
, e
, 0, NULL
) ;
248 /* h must be a whole positive number */
249 e
->maxnum
= streng_to_int( TSD
, h
, &error
) ;
250 if (error
|| (e
->maxnum
< 0))
251 exiterror( ERR_INVALID_STEM_OPTION
, /* needs stem.0 and */
252 1, /* (stem.0) as arguments */
253 tmpstr_of( TSD
, e
->currname
),
254 tmpstr_of( TSD
, h
) ) ;
255 e
->currnum
= (e
->flags
.isinput
) ? 1 : e
->maxnum
+ 1;
257 else /* must be REPLACE */
261 e
->base
->value
[0] = '0' ;
263 stem_access( TSD
, e
, 0, Str_dupTSD( e
->base
) ) ;
268 if ( overwrite
!= awtUNKNOWN
)
270 if ( e
->flags
.isinput
) /* "LIFO> cmd ... " */
273 e
->queue
= addr_reopen_queue( TSD
, NULL
, 'r' ) ; /* current */
281 e
->type
= LIFOappend
;
282 e
->queue
= addr_reopen_queue( TSD
, NULL
, 'A' ) ; /* current */
286 else /* "normal" use of WITH ??? LIFO ??? */
288 if ( e
->flags
.isinput
) /* "LIFO> cmd ... " */
291 e
->queue
= addr_reopen_queue( TSD
, e
->currname
, 'r' ) ;
292 if ( e
->queue
== NULL
)
293 exiterror( ERR_EXTERNAL_QUEUE
, 109, tmpstr_of( TSD
, e
->currname
) ) ;
300 e
->type
= LIFOappend
;
308 e
->queue
= addr_reopen_queue( TSD
, e
->currname
, code
) ;
309 if ( e
->queue
== NULL
)
310 exiterror( ERR_EXTERNAL_QUEUE
, 109, tmpstr_of( TSD
, e
->currname
) ) ;
316 if ( overwrite
!= awtUNKNOWN
)
318 if ( e
->flags
.isinput
) /* "FIFO> cmd ... " */
321 e
->queue
= addr_reopen_queue( TSD
, NULL
, 'r' ) ; /* current */
329 e
->type
= FIFOappend
;
330 e
->queue
= addr_reopen_queue( TSD
, NULL
, 'A' ) ; /* current */
334 else /* "normal" use of WITH ??? FIFO ??? */
336 if ( e
->flags
.isinput
) /* "FIFO> cmd ... " */
339 e
->queue
= addr_reopen_queue( TSD
, e
->currname
, 'r' ) ;
340 if ( e
->queue
== NULL
)
341 exiterror( ERR_EXTERNAL_QUEUE
, 109, tmpstr_of( TSD
, e
->currname
) ) ;
347 e
->type
= FIFOappend
;
355 e
->queue
= addr_reopen_queue( TSD
, e
->currname
, code
) ;
356 if ( e
->queue
== NULL
)
357 exiterror( ERR_EXTERNAL_QUEUE
, 109, tmpstr_of( TSD
, e
->currname
) ) ;
363 exiterror( ERR_INTERPRETER_FAILURE
, 1, __FILE__
, __LINE__
, "Illegal address code in open_env_io()" ) ;
367 void put_stem( tsd_t
*TSD
, environpart
*e
, streng
*str
)
368 /* Adds one line to the stem specified in e. */
370 e
->maxnum
= e
->currnum
;
372 e
->base
->len
= sprintf( e
->base
->value
, "%d", e
->maxnum
) ;
373 stem_access( TSD
, e
, 0, Str_dupTSD( e
->base
) ) ;
374 stem_access( TSD
, e
, e
->maxnum
, str
) ;
377 static int write_buffered(const tsd_t
*TSD
, int hdl
, const void *buf
,
378 unsigned size
, void *async_info
)
382 shl_tsd_t
*st
= (shl_tsd_t
*)TSD
->shl_tsd
;
384 if ((buf
== NULL
) || (size
== 0)) /* force flush buffers */
387 rc
= TSD
->OS
->write(hdl
, st
->IObuf
, st
->IOBused
, async_info
);
392 if (rc
== (int) st
->IOBused
)
396 memmove(st
->IObuf
, st
->IObuf
+ rc
, st
->IOBused
- rc
);
399 rc
= TSD
->OS
->write(hdl
, NULL
, 0, async_info
);
402 TSD
->OS
->write(hdl
, NULL
, 0, async_info
);
408 /* step 1: fill buffer up to the maximum */
410 if (todo
> sizeof(st
->IObuf
) - st
->IOBused
)
411 todo
= sizeof(st
->IObuf
) - st
->IOBused
;
414 memcpy(st
->IObuf
+ st
->IOBused
, buf
, todo
);
417 done
+= todo
; /* dropped to the buffer --> done for upper layer */
419 /* step 2: flush buffer, if buffer filled */
420 if (st
->IOBused
< sizeof(st
->IObuf
))
423 /* step 3: buffer full, giving optimal performance (I hope!) */
424 rc
= TSD
->OS
->write(hdl
, st
->IObuf
, st
->IOBused
, async_info
);
428 break; /* something done sucessfully */
431 if (rc
== (int) st
->IOBused
)
435 memmove(st
->IObuf
, st
->IObuf
+ rc
, st
->IOBused
- rc
);
439 /* just try another chunk of the input buffer */
440 buf
= (const char *) buf
+ todo
;
444 return(done
); /* something done sucessfully */
447 static int feed( const tsd_t
*TSD
, streng
**string
, int hdl
, void *async_info
)
448 /* Writes the content of *string into the file associated with hdl. The
449 * string is shortened and, after the final write, deleted and *string set
451 * async_info is both a structure and a flag. If set, asynchronous IO shall
452 * be used, otherwise blocked IO has to be used.
453 * feed returns 0 on success or an errno value on error.
454 * A return value of EAGAIN is set if we have to wait.
460 if ((string
== NULL
) || (*string
== NULL
))
463 total
= Str_len( *string
) ;
467 done
= write_buffered(TSD
, hdl
, (*string
)->value
, total
, async_info
);
470 if (done
== 0) /* no error set? */
471 done
= ENOSPC
; /* good assumption */
474 if ((done
!= EAGAIN
) && (done
!= EPIPE
))
475 exiterror( ERR_INTERPRETER_FAILURE
, 1, __FILE__
, __LINE__
, strerror(done
) ) ;
476 return( done
) ; /* error */
479 if ((unsigned) done
< total
)
481 (*string
)->len
-= done
;
482 memmove((*string
)->value
, (*string
)->value
+ done
, (*string
)->len
);
486 assert((unsigned)done
==total
);
487 Free_stringTSD(*string
);
493 static int reap( const tsd_t
*TSD
, streng
**string
, int hdl
, void *async_info
)
494 /* Reads data from the file associated with hdl and returns it in *string.
495 * *string may be NULL or valid, in which case it is expanded.
496 * reap returns 0 on success or an errno value on error. The value -1 indicates
498 * async_info is both a structure and a flag. If set, asynchronous IO shall
499 * be used, otherwise blocked IO has to be used.
500 * A maximum chunk of REGINA_MAX_BUFFER_LENGTH (usually 4096) bytes is read in one operation.
501 * A return value of EAGAIN is set if we have to wait.
504 char buf
[REGINA_MAX_BUFFER_LENGTH
] ;
505 unsigned len
, total
;
512 done
= TSD
->OS
->read( hdl
, buf
, sizeof(buf
), async_info
) ;
516 return( -1 ); /* EOF */
519 /* FGC, FIXME: Not sure, this is the right processing. Setting RS, etc? */
520 if ( done
!= EAGAIN
)
521 exiterror( ERR_INTERPRETER_FAILURE
, 1, __FILE__
, __LINE__
, strerror(done
) ) ;
522 return( done
) ; /* error */
525 if (( s
= *string
) == NULL
)
528 s
= Str_makeTSD( done
) ;
533 total
= Str_max( s
) ;
534 if (len
+ done
> total
)
536 s
= Str_makeTSD( len
+ done
) ;
538 memcpy( s
->value
, (*string
)->value
, len
) ;
539 Free_stringTSD( *string
) ;
542 memcpy( s
->value
+ len
, buf
, done
) ;
548 void cleanup_envirpart(const tsd_t
*TSD
, environpart
*ep
)
549 /* Closes the associated file handles of ep and deletes a temporary file
553 shl_tsd_t
*st
= (shl_tsd_t
*)TSD
->shl_tsd
;
555 if (ep
->hdls
[0] != -1)
557 TSD
->OS
->close(ep
->hdls
[0], (ep
->FileRedirected
) ? NULL
: st
->AsyncInfo
);
560 if (ep
->hdls
[1] != -1)
562 TSD
->OS
->close(ep
->hdls
[1], (ep
->FileRedirected
) ? NULL
: st
->AsyncInfo
);
565 if (ep
->hdls
[2] != -1)
567 TSD
->OS
->close_special(ep
->hdls
[2]);
572 unlink(ep
->tempname
);
573 FreeTSD(ep
->tempname
);
576 if ( ep
->tmp_queue
!= NULL
)
578 addr_close_queue( TSD
, ep
->tmp_queue
) ;
579 ep
->tmp_queue
= NULL
;
581 if ( ep
->queue
!= NULL
)
583 addr_close_queue( TSD
, ep
->queue
) ;
588 static void cleanup( tsd_t
*TSD
, environment
*env
)
589 /* Closes all open handles in env and deletes temporary files. Already closed
590 * handles are marked by a value of -1.
591 * -1 is set to each handle after closing.
594 shl_tsd_t
*st
= (shl_tsd_t
*)TSD
->shl_tsd
;
596 cleanup_envirpart(TSD
, &env
->input
);
597 cleanup_envirpart(TSD
, &env
->output
);
598 cleanup_envirpart(TSD
, &env
->error
);
601 TSD
->OS
->delete_async_info(st
->AsyncInfo
);
602 st
->AsyncInfo
= NULL
;
607 * CheckAndDealWithSameStems shall be used after opening all IO channels.
608 * It checks whether some of the channels address the same stem. This function
609 * takes care of this circumstances and prevents race conditions. In fact,
610 * in may produce a copy of the input to prevent overwriting.
612 static void CheckAndDealWithSameStems( tsd_t
*TSD
, environment
*env
)
614 if ( ( env
->output
.type
== STEM
) && ( env
->error
.type
== STEM
) )
616 /* env->output.name and env->error.name must exist here
618 * We have to take special care if output and error are
619 * redirected to the same stem. We neither want to overwrite
620 * stem values twice nor want to read "stem.0" for every
621 * stem on every access to prevent it.
623 if ( Str_ccmp( env
->output
.currname
, env
->error
.currname
) == 0 )
625 env
->error
.SameAsOutput
= 1;
626 if ( env
->error
.maxnum
== 0 )
628 /* error may has the REPLACE option while output has not.
629 * Force a silent replace in this case.
631 env
->output
.maxnum
= 0;
632 env
->output
.currnum
= 1;
637 if ( env
->input
.type
== STEM
)
639 /* Same procedure. To prevent overwriting variables while
640 * outputting to a stem wherefrom we have to read, buffer
641 * the input stem if the names do overlap.
644 if ( ( env
->output
.type
== STEM
) &&
645 ( Str_ccmp( env
->input
.currname
, env
->output
.currname
) == 0 ) )
646 env
->input
.SameAsOutput
|= 1;
648 if ( ( env
->error
.type
== STEM
) &&
649 ( Str_ccmp( env
->input
.currname
, env
->error
.currname
) == 0 ) )
650 env
->input
.SameAsOutput
|= 2;
652 if ( env
->input
.SameAsOutput
)
657 env
->input
.currname
->len
= env
->input
.currnamelen
;
658 env
->input
.tmp_queue
=
659 fill_input_queue_stem( TSD
, env
->input
.currname
, env
->input
.maxnum
);
665 * CheckSameStreams shall be used before opening all IO channels.
666 * It checks whether some of the channels address the same file. This function
667 * takes detects it and prepares the system to reduce file opening.
669 static void CheckSameStreams( tsd_t
*TSD
, int io_flags
, environment
*env
)
672 int i
, isFile
[3], mask
;
673 const streng
*name
[3];
676 memset( isFile
, 0, sizeof( isFile
) );
677 memset( (void *)name
, 0, sizeof( name
) );
679 for ( i
= 0; i
< 3; i
++ )
689 mask
= REDIR_OUTLIFO
| REDIR_OUTFIFO
| REDIR_OUTSTRING
;
702 if ( ( io_flags
& mask
) ||
703 ( e
->flags
.awt
!= awtSTREAM
) )
707 if ( ( name
[i
] != NULL
) && ( name
[i
]->len
== 0 ) )
710 if ( ( e
->flags
.ant
== antSIMSYMBOL
) && ( name
[i
] != NULL
) )
712 name
[i
] = getvalue( TSD
, name
[i
], -1 ) ;
716 assert( ( e
->flags
.ant
== antSTRING
) || ( name
[i
] == NULL
) );
721 * Delay the computation of the fully qualified filename until we
722 * know that it is useful.
725 if ( isFile
[0] + isFile
[1] + isFile
[2] > 1 )
727 for ( i
= 0; i
< 3; i
++ )
729 full
[i
] = addr_file_info( TSD
, name
[i
], i
);
732 if ( ( full
[0] != NULL
) && ( full
[1] != NULL
) )
733 env
->input
.SameAsOutput
|= filename_cmp( full
[0], full
[1] ) ? 0 : 1;
734 if ( ( full
[0] != NULL
) && ( full
[2] != NULL
) )
735 env
->input
.SameAsOutput
|= filename_cmp( full
[0], full
[2] ) ? 0 : 2;
736 if ( ( full
[1] != NULL
) && ( full
[2] != NULL
) )
737 env
->error
.SameAsOutput
|= filename_cmp( full
[1], full
[2] ) ? 0 : 1;
739 if ( env
->error
.SameAsOutput
&& ( env
->error
.flags
.append
== 0 ) )
740 env
->output
.flags
.append
= 0;
741 for ( i
= 0; i
< 3; i
++ )
745 Free_stringTSD( full
[i
] );
751 * CheckAndDealWithSameQueues shall be used after opening all IO channels.
752 * It checks whether some of the channels address the same queue. This function
753 * takes care of this circumstances and prevents race conditions. In fact,
754 * in may produce a copy of the input to prevent overwriting.
756 static void CheckAndDealWithSameQueues( tsd_t
*TSD
, environment
*env
)
758 if ( ( env
->output
.type
& ( LIFO
| FIFO
| LIFOappend
| FIFOappend
) )
759 && ( env
->error
.type
& ( LIFO
| FIFO
| LIFOappend
| FIFOappend
) ) )
762 * We have to take special care if output and error are
763 * redirected to the same queue. We neither want to push
764 * values twice nor want to read our own output for every
765 * stem on every access to prevent it.
767 if ( addr_same_queue( TSD
, env
->output
.queue
, env
->error
.queue
) )
769 env
->error
.SameAsOutput
= 1;
770 if ( env
->error
.type
& ( LIFO
| FIFO
) )
772 /* error may has the REPLACE option while output has not.
773 * Force a silent replace in this case.
775 if ( env
->output
.type
== LIFOappend
)
776 env
->output
.type
= LIFO
;
777 if ( env
->output
.type
== FIFOappend
)
778 env
->output
.type
= FIFO
;
783 if ( env
->input
.type
== QUEUE
)
785 /* Same procedure. To prevent overwriting values while
786 * outputting to a queue wherefrom we have to read, buffer
787 * the input queue if the queue are same.
790 if ( get_options_flag( TSD
->currlevel
, EXT_FLUSHSTACK
) == 0 )
792 if ( ( env
->output
.type
& ( LIFO
| FIFO
| LIFOappend
| FIFOappend
) )
793 && addr_same_queue( TSD
, env
->input
.queue
, env
->output
.queue
) )
794 env
->input
.SameAsOutput
|= 1;
796 if ( ( env
->error
.type
& ( LIFO
| FIFO
| LIFOappend
| FIFOappend
) )
797 && addr_same_queue( TSD
, env
->input
.queue
, env
->error
.queue
) )
798 env
->input
.SameAsOutput
|= 2;
800 if (env
->input
.SameAsOutput
)
801 env
->input
.tmp_queue
= addr_redir_queue( TSD
, env
->input
.queue
);
805 /* Final stages for queues: if not "append", do a replace by purging */
806 if ( ( env
->output
.type
== FIFO
)
807 || ( env
->output
.type
== LIFO
) )
808 addr_purge_queue( TSD
, env
->output
.queue
);
809 if ( ( ( env
->error
.type
== FIFO
)
810 || ( env
->error
.type
== LIFO
) )
811 && !env
->error
.SameAsOutput
)
812 addr_purge_queue( TSD
, env
->error
.queue
);
813 /* reduce used names */
814 if ( env
->output
.type
== FIFOappend
)
815 env
->output
.type
= FIFO
;
816 if ( env
->output
.type
== LIFOappend
)
817 env
->output
.type
= LIFO
;
818 if ( env
->error
.type
== FIFOappend
)
819 env
->error
.type
= FIFO
;
820 if ( env
->error
.type
== LIFOappend
)
821 env
->error
.type
= LIFO
;
824 static int setup_io( tsd_t
*TSD
, int io_flags
, environment
*env
)
825 /* Sets up the IO-redirections based on the values in io_flags and env.
826 * Each environpart (env->input, env->output, env->error) is set up as follows:
827 * a) The enviroment-based streams and stems are set up if used or not.
828 * env->input.type (or output or error) is set to STREAM, STEM or STD_IO.
829 * b) The io_flags overwrite the different settings and may have
830 * values QUEUE, simLIFO, simFIFO, STRING.
831 * c) If a redirection takes place (type != STD_IO) a pipe() or temporary
832 * file is opened and used.
833 * This function returns 1 on success, 0 on error, in which case an error is
837 shl_tsd_t
*st
= (shl_tsd_t
*)TSD
->shl_tsd
;
840 cleanup( TSD
, env
); /* Useful in case of an undetected previous error */
842 prepare_env_io( &env
->input
);
843 prepare_env_io( &env
->output
);
844 prepare_env_io( &env
->error
);
846 CheckSameStreams( TSD
, io_flags
, env
);
848 * Determine which ANSI redirections are in effect
849 * Use the special io_flags for redirection to overwrite the standard
850 * rules of the environment.
852 overwrite
= ( io_flags
& REDIR_INPUT
) ? awtFIFO
: awtUNKNOWN
;
853 open_env_io( TSD
, &env
->input
, overwrite
, 0 );
854 if ( env
->input
.SameAsOutput
)
857 * It must be a file since we don't have checked for stems and queues.
858 * We read the input into a temporary queue, then we can proceed in the
861 env
->input
.tmp_queue
= fill_input_queue_stream( TSD
, env
->input
.file
);
862 addr_reset_file( TSD
, env
->input
.file
);
863 env
->input
.file
= NULL
;
866 if ( io_flags
& REDIR_OUTLIFO
)
868 else if ( io_flags
& REDIR_OUTFIFO
)
870 else if ( io_flags
& REDIR_OUTSTRING
)
873 overwrite
= awtUNKNOWN
;
874 open_env_io( TSD
, &env
->output
, overwrite
, io_flags
& REDIR_OUTSTRING
);
876 if ( env
->error
.SameAsOutput
)
879 * It must be a file since we don't have checked for stems and queues.
880 * We read the input into a temporary queue or a temporary file.
881 * Then we can proceed in the usual way.
883 env
->error
.type
= STREAM
;
886 open_env_io( TSD
, &env
->error
, awtUNKNOWN
, 0 );
888 CheckAndDealWithSameStems( TSD
, env
);
889 CheckAndDealWithSameQueues( TSD
, env
);
891 if ( env
->input
.type
!= STD_IO
)
893 if ( TSD
->OS
->open_subprocess_connection( TSD
, &env
->input
) != 0 )
896 exiterror( ERR_SYSTEM_FAILURE
, 920, "creating redirection", "for input", strerror(errno
) );
900 if ( env
->output
.type
!= STD_IO
)
902 if ( TSD
->OS
->open_subprocess_connection( TSD
, &env
->output
) != 0 )
905 exiterror( ERR_SYSTEM_FAILURE
, 920, "creating redirection", "for output", strerror(errno
) );
911 if ( env
->error
.type
!= STD_IO
)
913 if ( TSD
->OS
->open_subprocess_connection( TSD
, &env
->error
) != 0 )
916 exiterror( ERR_SYSTEM_FAILURE
, 920, "creating redirection", "for error", strerror(errno
) );
922 st
->AsyncInfo
= TSD
->OS
->create_async_info( TSD
);
926 static streng
*fetch_food( tsd_t
*TSD
, environment
*env
)
927 /* returns one streng fetched either from a queue (env->input.type == QUEUE) or
928 * from a stem or stream.
929 * Returns NULL if there is no more input to feed the child process.
936 switch (env
->input
.type
)
940 if ( env
->input
.tmp_queue
)
941 c
= addr_io_queue( TSD
, env
->input
.tmp_queue
, NULL
, 0 ) ;
943 c
= addr_io_queue( TSD
, env
->input
.queue
, NULL
, 0 ) ;
951 if ( env
->input
.tmp_queue
)
953 c
= addr_io_queue( TSD
, env
->input
.tmp_queue
, NULL
, 0 );
958 if (env
->input
.file
== NULL
)
960 c
= addr_io_file( TSD
, env
->input
.file
, NULL
);
965 Free_stringTSD( (streng
*) c
);
971 if (!env
->input
.SameAsOutput
)
973 if (env
->input
.currnum
> env
->input
.maxnum
)
975 c
= stem_access( TSD
, &env
->input
, env
->input
.currnum
++, NULL
) ;
980 c
= addr_io_queue( TSD
, env
->input
.tmp_queue
, NULL
, 0 ) ;
987 exiterror( ERR_INTERPRETER_FAILURE
, 1, __FILE__
, __LINE__
, "Illegal feeder in fetch_food()" ) ;
992 if ( env
->input
.type
!= STREAM
)
994 /* Append a newline to the end of the line before returning */
995 #if defined(DOS) || defined(OS2) || defined(WIN32)
996 retval
= Str_makeTSD( c
->len
+ 2 ) ;
997 memcpy(retval
->value
, c
->value
, c
->len
);
998 retval
->value
[c
->len
] = REGINA_CR
;
999 retval
->value
[c
->len
+ 1] = REGINA_EOL
;
1000 retval
->len
= c
->len
+ 2;
1002 retval
= Str_makeTSD( c
->len
+ 1 ) ;
1003 memcpy(retval
->value
, c
->value
, c
->len
);
1004 retval
->value
[c
->len
] = REGINA_EOL
;
1005 retval
->len
= c
->len
+ 1;
1008 Free_stringTSD( (streng
*) c
) ;
1012 /* STREAM mode reads blocks of buffers, usually 4KB ignoring any
1016 retval
= (streng
*) c
; /* will be destroyed */
1021 static void drop_crop_line( tsd_t
*TSD
, environment
*env
, const char *data
,
1022 unsigned length
, int is_error
)
1023 /* Called while reading the output of the child. The output is in data and
1024 * contains length bytes without the line terminator.
1025 * which may be empty or not yet completed. The exact destination is determined
1026 * by env->x.type, where x is either output or error depending on is_error.
1027 * type may have one of the values simLIFO, simFIFO, STRING, STREAM or STEM.
1028 * is_error is set if the error redirection should happen.
1034 string
= Str_makeTSD( length
+ 1 ) ; /* We need a terminating 0 in some */
1036 memcpy( string
->value
, data
, length
) ;
1037 string
->len
= length
;
1038 string
->value
[length
] = '\0' ;
1041 type
= env
->error
.type
;
1043 type
= env
->output
.type
;
1048 if ( is_error
&& !env
->error
.SameAsOutput
)
1050 if ( env
->error
.tmp_queue
!= NULL
)
1051 addr_io_queue( TSD
, env
->error
.tmp_queue
, string
, 0 ) ;
1053 addr_io_queue( TSD
, env
->error
.queue
, string
, 0 ) ;
1057 if ( env
->output
.tmp_queue
!= NULL
)
1058 addr_io_queue( TSD
, env
->output
.tmp_queue
, string
, 0 ) ;
1060 addr_io_queue( TSD
, env
->output
.queue
, string
, 0 ) ;
1062 return; /* consumes the new string */
1066 if ( is_error
&& !env
->error
.SameAsOutput
)
1068 if ( env
->error
.tmp_queue
!= NULL
)
1069 addr_io_queue( TSD
, env
->error
.tmp_queue
, string
, 1 ) ;
1071 addr_io_queue( TSD
, env
->error
.queue
, string
, 1 ) ;
1075 if ( env
->output
.tmp_queue
!= NULL
)
1076 addr_io_queue( TSD
, env
->output
.tmp_queue
, string
, 1 ) ;
1078 addr_io_queue( TSD
, env
->output
.queue
, string
, 1 ) ;
1080 return; /* consumes the new string */
1083 exiterror( ERR_INTERPRETER_FAILURE
, 1, __FILE__
, __LINE__
, "Illegal STREAM in drop_crop_line()" ) ;
1087 if (is_error
&& !env
->error
.SameAsOutput
)
1088 put_stem( TSD
, &env
->error
, string
) ;
1090 put_stem( TSD
, &env
->output
, string
) ;
1091 return; /* consumes the new string */
1094 exiterror( ERR_INTERPRETER_FAILURE
, 1, __FILE__
, __LINE__
, "Illegal crop in drop_crop_line()" ) ;
1098 Free_stringTSD( string
) ;
1101 /* line_length tries to find ANY line terminator. This is either \r,
1103 * The lookup happens in line (not 0-terminated) with a length of len.
1104 * The return value is either -1 (not found) or the length of the line
1105 * content. *termlen is set to the number of characters of the line
1106 * terminator, either 1 or 2.
1107 * EOFreached is boolean and indicated a final line if set.
1109 static int line_length(const char *line
, int len
, int *termlen
, int EOFreached
)
1115 ccr
= (char *)memchr( line
, '\r', len
) ;
1116 clf
= (char *)memchr( line
, '\n', len
) ;
1123 *termlen
= ( ccr
== clf
+ 1 ) ? 2 : 1 ;
1124 return (int) ( clf
- line
) ;
1128 *termlen
= ( clf
== ccr
+ 1 ) ? 2 : 1 ;
1129 return (int) ( ccr
- line
) ;
1132 /* else '\r' found, but we must know if it terminates */
1133 h
= (int) ( ccr
- line
) ;
1134 if ( ( h
+ 1 < len
) || EOFreached
)
1140 else if ( clf
!= NULL
) /* simple line feed */
1142 h
= (int) ( clf
- line
) ;
1143 if ( ( h
+ 1 < len
) || EOFreached
)
1152 static void drop_crop( tsd_t
*TSD
, environment
*env
, streng
**string
,
1153 int EOFreached
, int is_error
)
1154 /* Called while reading the output of the child. The output is in *string,
1155 * which may be empty or not yet completed. The exact destination is determined
1156 * by env->x.type, where x is either output or error depending on is_error.
1157 * type may have one of the values simLIFO, simFIFO, STRING, STREAM or STEM.
1158 * If EOFreached is set and some data is in *string, this data is interpreted
1159 * as a completed line.
1160 * Completed lines are cut of the string. The string itself isn't deleted.
1161 * is_error is set if the error redirection should happen.
1167 int max
, found
, termlen
, isStream
;
1171 if (s
== NULL
) /* might happen on a first call */
1176 isStream
= ( env
->error
.type
== STREAM
) ;
1177 fptr
= ( env
->error
.SameAsOutput
) ? env
->output
.file
: env
->error
.file
;
1181 isStream
= ( env
->output
.type
== STREAM
) ;
1182 fptr
= env
->output
.file
;
1187 /* very fast bypass, we can simply drop the whole thing */
1189 addr_io_file( TSD
, fptr
, s
) ;
1196 max
= Str_len( s
) ;
1200 found
= line_length( ptr
, max
, &termlen
, EOFreached
) ;
1202 if ( ( found
== -1 ) && EOFreached
)
1210 drop_crop_line( TSD
, env
, ptr
, (unsigned) found
, is_error
) ;
1215 memcpy( s
->value
, ptr
, max
) ;
1221 int posix_do_command( tsd_t
*TSD
, const streng
*command
, int io_flags
, environment
*env
, Queue
*redir
)
1225 streng
*istring
= NULL
, *ostring
= NULL
, *estring
= NULL
;
1227 shl_tsd_t
*st
= (shl_tsd_t
*)TSD
->shl_tsd
;
1232 CloseOpenFiles( TSD
, fpdRETAIN
);
1237 if (!setup_io(TSD
, io_flags
, env
))
1238 exiterror( ERR_SYSTEM_FAILURE
, 0 ) ;
1240 if (env
->input
.FileRedirected
)
1242 /* fill up the input file without closing the stream. */
1244 while ((istring
= fetch_food(TSD
, env
)) != NULL
)
1246 if (feed(TSD
, &istring
, env
->input
.hdls
[1], NULL
) != 0)
1247 break; /* shall not happen! */
1249 rc
= write_buffered(TSD
, env
->input
.hdls
[1], NULL
, 0, NULL
);
1253 exiterror( ERR_SYSTEM_FAILURE
, 920, "feeding redirection file", "for input", strerror(errno
) );
1255 /* seek positions of both fdin may have been destroyed */
1256 TSD
->OS
->restart_file(env
->input
.hdls
[0]);
1257 TSD
->OS
->close(env
->input
.hdls
[1], NULL
);
1258 env
->input
.hdls
[1] = -1;
1261 if ( env
->output
.type
== STRING
)
1262 env
->output
.tmp_queue
= redir
;
1264 cmdline
= str_ofTSD( command
) ;
1265 child
= TSD
->OS
->fork_exec( TSD
, env
, cmdline
, &rc
);
1266 FreeTSD( cmdline
) ;
1267 if ( ( child
== -1 ) || ( child
== 0 ) )
1270 cleanup( TSD
, env
) ;
1272 exiterror( ERR_SYSTEM_FAILURE
, 1, strerror( err
) );
1273 return ( rc
> 0 ) ? -rc
: ( rc
== 0 ) ? -1000 : rc
;
1276 /* Close the child part of the handles */
1277 if (env
->input
.hdls
[0] != -1) TSD
->OS
->close(env
->input
.hdls
[0], NULL
) ;
1278 if (env
->output
.hdls
[1] != -1) TSD
->OS
->close(env
->output
.hdls
[1], NULL
) ;
1279 if (env
->error
.hdls
[1] != -1) TSD
->OS
->close(env
->error
.hdls
[1], NULL
) ;
1280 env
->input
.hdls
[0] = env
->output
.hdls
[1] = env
->error
.hdls
[1] = -1;
1282 /* Force our own handles to become nonblocked */
1283 if (!env
->input
.FileRedirected
&& ((in
= env
->input
.hdls
[1]) != -1))
1285 TSD
->OS
->unblock_handle( &in
, st
->AsyncInfo
) ;
1289 if (!env
->output
.FileRedirected
&& ((out
= env
->output
.hdls
[0]) != -1))
1291 TSD
->OS
->unblock_handle( &out
, st
->AsyncInfo
) ;
1295 if (!env
->error
.FileRedirected
&& ((err
= env
->error
.hdls
[0]) != -1))
1297 TSD
->OS
->unblock_handle( &err
, st
->AsyncInfo
) ;
1303 regina_signal( SIGPIPE
, SIG_IGN
) ;
1306 while ((in
!= -1) || (out
!= -1) || (err
!= -1))
1308 TSD
->OS
->reset_async_info(st
->AsyncInfo
);
1313 istring
= fetch_food( TSD
, env
) ;
1316 rc
= write_buffered(TSD
, in
, NULL
, 0, st
->AsyncInfo
);
1318 TSD
->OS
->add_async_waiter(st
->AsyncInfo
, in
, 0);
1321 if ((rc
< 0) && (-rc
!= EPIPE
)) /* fixes bug 945218 */
1324 exiterror( ERR_SYSTEM_FAILURE
, 920, "writing to", "input redirection", strerror(errno
) );
1326 if (TSD
->OS
->close(in
, st
->AsyncInfo
))
1327 exiterror( ERR_SYSTEM_FAILURE
, 920, "closing redirection", "for input", strerror(errno
) );
1328 env
->input
.hdls
[1] = in
= -1 ;
1329 rc
= -1 ; /* indicate a closed stream */
1332 else /* nothing left in string, but more in the stack */
1334 rc
= feed( TSD
, &istring
, in
, st
->AsyncInfo
) ;
1338 TSD
->OS
->add_async_waiter(st
->AsyncInfo
, in
, 0);
1341 TSD
->OS
->close(in
, st
->AsyncInfo
) ;
1342 env
->input
.hdls
[1] = in
= -1 ;
1345 else if (istring
!= NULL
)
1347 /* hasn't written all at once, therefore is blocked.
1348 * do a little performance boost and don't try to write
1349 * once more, perform the wait instead.
1352 TSD
->OS
->add_async_waiter(st
->AsyncInfo
, in
, 0);
1355 } while (rc
== 0); /* It is best for performance and no penalty for */
1356 /* security to write as much as possible */
1358 } /* if (in != -1) */
1363 rc
= reap( TSD
, &ostring
, out
, st
->AsyncInfo
);
1367 TSD
->OS
->add_async_waiter(st
->AsyncInfo
, out
, 1);
1370 TSD
->OS
->close(out
, st
->AsyncInfo
) ;
1371 env
->output
.hdls
[0] = out
= -1 ;
1374 else if (ostring
!= NULL
)
1375 drop_crop( TSD
, env
, &ostring
, 0, 0 ) ;
1376 } while (rc
== 0); /* It is best for performance and no penalty for */
1377 /* security to write as much as possible */
1378 } /* if (out != -1) */
1383 rc
= reap( TSD
, &estring
, err
, st
->AsyncInfo
);
1387 TSD
->OS
->add_async_waiter(st
->AsyncInfo
, err
, 1);
1390 TSD
->OS
->close(err
, st
->AsyncInfo
) ;
1391 env
->error
.hdls
[0] = err
= -1 ;
1394 else if (estring
!= NULL
)
1395 drop_crop( TSD
, env
, &estring
, 0, 1 ) ;
1396 } while (rc
== 0); /* It is best for performance and no penalty for */
1397 /* security to write as much as possible */
1398 } /* if (err != -1) */
1400 TSD
->OS
->wait_async_info(st
->AsyncInfo
); /* wait for any more IO */
1404 Free_stringTSD( istring
);
1408 if ( Str_len( ostring
) )
1409 drop_crop( TSD
, env
, &ostring
, 1, 0 );
1410 Free_stringTSD( ostring
);
1415 if ( Str_len( estring
) )
1416 drop_crop( TSD
, env
, &estring
, 1, 1 );
1417 Free_stringTSD( estring
);
1420 if ( ( env
->input
.type
== QUEUE
) && ( env
->input
.tmp_queue
== NULL
) )
1421 addr_purge_queue( TSD
, env
->input
.queue
) ;
1423 rc
= TSD
->OS
->wait(child
);
1426 regina_signal( SIGPIPE
, SIG_DFL
) ;
1429 if (env
->output
.FileRedirected
)
1431 /* The file position is usually at the end: */
1432 TSD
->OS
->restart_file(env
->output
.hdls
[0]);
1433 while (reap( TSD
, &ostring
, env
->output
.hdls
[0], NULL
) == 0)
1435 if (ostring
!= NULL
)
1436 drop_crop( TSD
, env
, &ostring
, 0, 0 ) ;
1438 if (ostring
!= NULL
)
1439 drop_crop( TSD
, env
, &ostring
, 1, 0 ) ;
1441 /* use the automatted closing feature of cleanup */
1443 if (env
->error
.FileRedirected
)
1445 /* The file position is usually at the end: */
1446 TSD
->OS
->restart_file(env
->error
.hdls
[0]);
1447 while (reap( TSD
, &estring
, env
->error
.hdls
[0], NULL
) == 0) {
1448 if (estring
!= NULL
)
1449 drop_crop( TSD
, env
, &estring
, 0, 1 ) ;
1451 if (estring
!= NULL
)
1452 drop_crop( TSD
, env
, &estring
, 1, 1 ) ;
1453 /* use the automatted closing feature of cleanup */
1456 if ( env
->output
.type
& ( LIFO
| FIFO
) ) /* never use STRING here */
1457 flush_stack( TSD
, env
->output
.tmp_queue
, env
->output
.queue
, env
->output
.type
== FIFO
) ;
1458 if ( env
->error
.type
& ( LIFO
| FIFO
) )
1459 flush_stack( TSD
, env
->error
.tmp_queue
, env
->error
.queue
, env
->output
.type
== FIFO
) ;
1461 if ( ( env
->input
.type
== STREAM
) && ( env
->input
.file
!= NULL
) )
1462 addr_reset_file( TSD
, env
->input
.file
);
1463 if ( env
->output
.type
== STREAM
)
1464 addr_reset_file( TSD
, env
->output
.file
);
1465 if ( ( env
->error
.type
== STREAM
) && !env
->error
.SameAsOutput
)
1466 addr_reset_file( TSD
, env
->error
.file
);
1467 if ( env
->output
.type
== STRING
)
1468 env
->output
.tmp_queue
= NULL
;
1470 cleanup( TSD
, env
) ;