Copy icons and docs.
[AROS-Contrib.git] / regina / shell.c
bloba04255c6d9d2aee3f5a5e227480b62c07cf1226d
1 /*
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.
20 #include "regina_c.h"
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
37 #include "rexx.h"
38 #include <stdio.h>
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
50 #if defined(VMS)
51 # define fork() vfork()
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
62 #if defined(__WINS__) || defined(__EPOC32__)
63 # define REGINA_MAX_BUFFER_LENGTH 256
64 #else
65 # define REGINA_MAX_BUFFER_LENGTH 4096
66 #endif
68 #define STD_IO 0x00
69 #define QUEUE 0x01
70 #define LIFO 0x02
71 #define FIFO 0x04
72 #define STREAM 0x08
73 #define STEM 0x10
74 #define STRING 0x20
75 #define LIFOappend 0x80
76 #define FIFOappend 0x100
78 #if defined(_POSIX_PIPE_BUF) && !defined(PIPE_BUF)
79 # define PIPE_BUF _POSIX_PIPE_BUF
80 #endif
82 typedef struct { /* shl_tsd: static variables of this module (thread-safe) */
83 char *cbuff ;
84 int child ;
85 int status ;
86 int running ;
87 void *AsyncInfo ;
88 unsigned char IObuf[4096]; /* write cache */
89 unsigned IOBused;
90 } shl_tsd_t; /* thread-specific but only needed by this module. see
91 * init_shell
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 )
100 shl_tsd_t *st;
102 if ( TSD->shl_tsd != NULL )
103 return(1);
105 if ( ( TSD->shl_tsd = MallocTSD( sizeof(shl_tsd_t) ) ) == NULL )
106 return(0);
107 st = (shl_tsd_t *)TSD->shl_tsd;
108 memset( st, 0, sizeof(shl_tsd_t) ); /* correct for all values */
109 return(1);
112 static const streng *stem_access( tsd_t *TSD, environpart *e, int pos,
113 streng *value )
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.
119 int leaflen ;
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
128 * unexpected data.
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.
133 if (value == NULL)
134 return( getvalue( TSD, e->currname, -1 ) ) ;
136 setvalue( TSD, e->currname, value, -1 ) ;
137 return( NULL ) ;
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 ) ;
158 else
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 )
172 e->currname = NULL ;
173 return ;
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.
186 e->SameAsOutput = 0;
187 e->FileRedirected = 0;
188 e->tempname = NULL; /* none as default, might become char* RedirTempFile */
189 e->queue = NULL;
190 e->tmp_queue = NULL;
191 e->type = STD_IO;
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
201 const streng *h ;
202 int error ;
203 unsigned awt ;
204 char code ;
206 if ( ( e->name == NULL ) && !overwrite && ( e->flags.awt == awtUNKNOWN ) )
207 return ;
209 set_currname(TSD, e);
210 if ( overwrite == awtUNKNOWN )
211 awt = e->flags.awt ;
212 else
213 awt = overwrite ;
214 switch (awt)
216 case awtSTREAM:
218 * For a STREAM input/output redirection, set the file reopen
219 * flag, and reopen the file.
221 e->type = STREAM;
222 if ( e->flags.isinput )
223 code = 'r';
224 else if ( e->flags.append )
225 code = 'A';
226 else /* REPLACE */
227 code = 'R';
228 if ( e->flags.isinput || !e->SameAsOutput )
229 e->file = addr_reopen_file( TSD, e->currname, code,
230 e->flags.iserror );
231 break;
233 case awtSTEM:
235 * For a STEM input/output redirection, check that existing state of
236 * the stem if appropriate and initialise the stem
238 e->type = 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 */
259 e->maxnum = 0 ;
260 e->currnum = 1 ;
261 e->base->value[0] = '0' ;
262 e->base->len = 1 ;
263 stem_access( TSD, e, 0, Str_dupTSD( e->base ) ) ;
265 break;
267 case awtLIFO:
268 if ( overwrite != awtUNKNOWN )
270 if ( e->flags.isinput ) /* "LIFO> cmd ... " */
272 e->type = QUEUE;
273 e->queue = addr_reopen_queue( TSD, NULL, 'r' ) ; /* current */
275 else
277 if ( isString )
278 e->type = STRING ;
279 else
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 ... " */
290 e->type = QUEUE;
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 ) ) ;
295 else
297 e->type = LIFO;
298 if (e->flags.append)
300 e->type = LIFOappend;
301 code = 'A' ;
303 else /* REPLACE */
305 e->type = LIFO;
306 code = 'R' ;
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 ) ) ;
313 break;
315 case awtFIFO:
316 if ( overwrite != awtUNKNOWN )
318 if ( e->flags.isinput ) /* "FIFO> cmd ... " */
320 e->type = QUEUE;
321 e->queue = addr_reopen_queue( TSD, NULL, 'r' ) ; /* current */
323 else
325 if ( isString )
326 e->type = STRING ;
327 else
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 ... " */
338 e->type = QUEUE;
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 ) ) ;
343 else
345 if (e->flags.append)
347 e->type = FIFOappend;
348 code = 'A' ;
350 else /* REPLACE */
352 e->type = FIFO;
353 code = 'R' ;
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 ) ) ;
360 break;
362 default:
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 ;
371 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)
380 int rc, done;
381 unsigned todo;
382 shl_tsd_t *st = (shl_tsd_t *)TSD->shl_tsd;
384 if ((buf == NULL) || (size == 0)) /* force flush buffers */
386 if (st->IOBused)
387 rc = TSD->OS->write(hdl, st->IObuf, st->IOBused, async_info);
388 else
389 rc = 0;
390 if (rc >= 0)
392 if (rc == (int) st->IOBused)
393 st->IOBused = 0;
394 else
396 memmove(st->IObuf, st->IObuf + rc, st->IOBused - rc);
397 st->IOBused -= rc;
399 rc = TSD->OS->write(hdl, NULL, 0, async_info);
401 else
402 TSD->OS->write(hdl, NULL, 0, async_info);
403 return(rc);
406 done = 0;
407 while (size) {
408 /* step 1: fill buffer up to the maximum */
409 todo = size;
410 if (todo > sizeof(st->IObuf) - st->IOBused)
411 todo = sizeof(st->IObuf) - st->IOBused;
412 if (todo > 0)
414 memcpy(st->IObuf + st->IOBused, buf, todo);
415 st->IOBused += 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))
421 return(done);
423 /* step 3: buffer full, giving optimal performance (I hope!) */
424 rc = TSD->OS->write(hdl, st->IObuf, st->IOBused, async_info);
425 if (rc <= 0)
427 if (done)
428 break; /* something done sucessfully */
429 return(rc);
431 if (rc == (int) st->IOBused)
432 st->IOBused = 0;
433 else
435 memmove(st->IObuf, st->IObuf + rc, st->IOBused - rc);
436 st->IOBused -= rc;
439 /* just try another chunk of the input buffer */
440 buf = (const char *) buf + todo;
441 size -= 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
450 * to NULL.
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.
457 unsigned total ;
458 int done ;
460 if ((string == NULL) || (*string == NULL))
461 return( 0 ) ;
463 total = Str_len( *string ) ;
464 if (total == 0)
465 return( 0 ) ;
467 done = write_buffered(TSD, hdl, (*string)->value, total, async_info);
468 if (done <= 0)
470 if (done == 0) /* no error set? */
471 done = ENOSPC ; /* good assumption */
472 else
473 done = -done;
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);
484 else
486 assert((unsigned)done==total);
487 Free_stringTSD(*string);
488 *string = NULL;
490 return(0);
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
497 * EOF.
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 ;
506 streng *s ;
507 int done ;
509 if (string == NULL)
510 return( 0 ) ;
512 done = TSD->OS->read( hdl, buf, sizeof(buf), async_info ) ;
513 if (done <= 0)
515 if (done == 0)
516 return( -1 ); /* EOF */
517 else
518 done = -done;
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)
527 len = 0 ;
528 s = Str_makeTSD( done ) ;
530 else
532 len = Str_len( s ) ;
533 total = Str_max( s ) ;
534 if (len + done > total)
536 s = Str_makeTSD( len + done ) ;
537 s->len = len ;
538 memcpy( s->value, (*string)->value, len ) ;
539 Free_stringTSD( *string ) ;
542 memcpy( s->value + len, buf, done ) ;
543 s->len += done ;
544 *string = s ;
545 return( 0 ) ;
548 void cleanup_envirpart(const tsd_t *TSD, environpart *ep)
549 /* Closes the associated file handles of ep and deletes a temporary file
550 * if necessary.
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);
558 ep->hdls[0] = -1;
560 if (ep->hdls[1] != -1)
562 TSD->OS->close(ep->hdls[1], (ep->FileRedirected) ? NULL : st->AsyncInfo);
563 ep->hdls[1] = -1;
565 if (ep->hdls[2] != -1)
567 TSD->OS->close_special(ep->hdls[2]);
568 ep->hdls[2] = -1;
570 if (ep->tempname)
572 unlink(ep->tempname);
573 FreeTSD(ep->tempname);
574 ep->tempname = NULL;
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 ) ;
584 ep->queue = NULL ;
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);
600 if (st->AsyncInfo)
601 TSD->OS->delete_async_info(st->AsyncInfo);
602 st->AsyncInfo = NULL;
603 st->IOBused = 0;
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 )
655 * Fixes bug 609017
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 )
671 environpart *e;
672 int i, isFile[3], mask;
673 const streng *name[3];
674 streng *full[3];
676 memset( isFile, 0, sizeof( isFile ) );
677 memset( (void *)name, 0, sizeof( name ) );
679 for ( i = 0; i < 3; i++ )
681 switch ( i )
683 case 0:
684 mask = REDIR_INPUT;
685 e = &env->input;
686 break;
688 case 1:
689 mask = REDIR_OUTLIFO | REDIR_OUTFIFO | REDIR_OUTSTRING;
690 e = &env->output;
691 break;
693 default:
694 mask = 0;
695 e = &env->error;
696 break;
700 e->SameAsOutput = 0;
701 full[i] = NULL;
702 if ( ( io_flags & mask ) ||
703 ( e->flags.awt != awtSTREAM ) )
704 continue;
706 name[i] = e->name;
707 if ( ( name[i] != NULL ) && ( name[i]->len == 0 ) )
708 name[i] = NULL;
710 if ( ( e->flags.ant == antSIMSYMBOL ) && ( name[i] != NULL ) )
712 name[i] = getvalue( TSD, name[i], -1 ) ;
714 else
716 assert( ( e->flags.ant == antSTRING ) || ( name[i] == NULL ) );
719 isFile[i] = 1;
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++ )
743 if ( full[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
834 * already reported..
837 shl_tsd_t *st = (shl_tsd_t *)TSD->shl_tsd;
838 int overwrite;
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
859 * usual way.
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 )
867 overwrite = awtLIFO;
868 else if ( io_flags & REDIR_OUTFIFO )
869 overwrite = awtFIFO;
870 else if ( io_flags & REDIR_OUTSTRING )
871 overwrite = awtFIFO;
872 else
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;
885 else
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 )
895 cleanup( TSD, env );
896 exiterror( ERR_SYSTEM_FAILURE, 920, "creating redirection", "for input", strerror(errno) );
897 return 0;
900 if ( env->output.type != STD_IO )
902 if ( TSD->OS->open_subprocess_connection( TSD, &env->output ) != 0 )
904 cleanup( TSD, env );
905 exiterror( ERR_SYSTEM_FAILURE, 920, "creating redirection", "for output", strerror(errno) );
906 return 0;
909 else
910 fflush( stdout );
911 if ( env->error.type != STD_IO )
913 if ( TSD->OS->open_subprocess_connection( TSD, &env->error ) != 0 )
915 cleanup( TSD, env );
916 exiterror( ERR_SYSTEM_FAILURE, 920, "creating redirection", "for error", strerror(errno) );
917 return 0;
920 else
921 fflush( stderr );
922 st->AsyncInfo = TSD->OS->create_async_info( TSD );
923 return 1;
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.
932 const streng *c ;
933 streng *retval ;
934 int delflag = 0 ;
936 switch (env->input.type)
938 case QUEUE:
939 delflag = 1 ;
940 if ( env->input.tmp_queue )
941 c = addr_io_queue( TSD, env->input.tmp_queue, NULL, 0 ) ;
942 else
943 c = addr_io_queue( TSD, env->input.queue, NULL, 0 ) ;
944 if ( c == NULL )
945 return NULL ;
946 break;
948 case STREAM:
949 delflag = 1;
951 if ( env->input.tmp_queue )
953 c = addr_io_queue( TSD, env->input.tmp_queue, NULL, 0 );
954 if ( c == NULL )
955 return NULL;
956 break;
958 if (env->input.file == NULL)
959 return NULL;
960 c = addr_io_file( TSD, env->input.file, NULL );
961 if ( !c )
962 return NULL;
963 if ( c->len == 0 )
965 Free_stringTSD( (streng *) c );
966 return NULL;
968 break;
970 case STEM:
971 if (!env->input.SameAsOutput)
973 if (env->input.currnum > env->input.maxnum)
974 return( NULL ) ;
975 c = stem_access( TSD, &env->input, env->input.currnum++, NULL ) ;
977 else
979 delflag = 1 ;
980 c = addr_io_queue( TSD, env->input.tmp_queue, NULL, 0 ) ;
982 if (!c)
983 return( NULL ) ;
984 break;
986 default:
987 exiterror( ERR_INTERPRETER_FAILURE, 1, __FILE__, __LINE__, "Illegal feeder in fetch_food()" ) ;
988 return( NULL ) ;
989 break ;
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;
1001 #else
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;
1006 #endif
1007 if (delflag)
1008 Free_stringTSD( (streng *) c ) ;
1010 else
1012 /* STREAM mode reads blocks of buffers, usually 4KB ignoring any
1013 * line structure
1015 assert( delflag ) ;
1016 retval = (streng *) c ; /* will be destroyed */
1018 return( retval ) ;
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.
1031 streng *string ;
1032 int type;
1034 string = Str_makeTSD( length + 1 ) ; /* We need a terminating 0 in some */
1035 /* cases */
1036 memcpy( string->value, data, length ) ;
1037 string->len = length ;
1038 string->value[length] = '\0' ;
1040 if (is_error)
1041 type = env->error.type;
1042 else
1043 type = env->output.type;
1045 switch (type)
1047 case LIFO:
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 ) ;
1052 else
1053 addr_io_queue( TSD, env->error.queue, string, 0 ) ;
1055 else
1057 if ( env->output.tmp_queue != NULL )
1058 addr_io_queue( TSD, env->output.tmp_queue, string, 0 ) ;
1059 else
1060 addr_io_queue( TSD, env->output.queue, string, 0 ) ;
1062 return; /* consumes the new string */
1064 case FIFO:
1065 case 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 ) ;
1070 else
1071 addr_io_queue( TSD, env->error.queue, string, 1 ) ;
1073 else
1075 if ( env->output.tmp_queue != NULL )
1076 addr_io_queue( TSD, env->output.tmp_queue, string, 1 ) ;
1077 else
1078 addr_io_queue( TSD, env->output.queue, string, 1 ) ;
1080 return; /* consumes the new string */
1082 case STREAM:
1083 exiterror( ERR_INTERPRETER_FAILURE, 1, __FILE__, __LINE__, "Illegal STREAM in drop_crop_line()" ) ;
1084 break;
1086 case STEM:
1087 if (is_error && !env->error.SameAsOutput)
1088 put_stem( TSD, &env->error, string ) ;
1089 else
1090 put_stem( TSD, &env->output, string ) ;
1091 return; /* consumes the new string */
1093 default:
1094 exiterror( ERR_INTERPRETER_FAILURE, 1, __FILE__, __LINE__, "Illegal crop in drop_crop_line()" ) ;
1095 break ;
1098 Free_stringTSD( string ) ;
1101 /* line_length tries to find ANY line terminator. This is either \r,
1102 * \n, \r\n or \n\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)
1111 char *ccr;
1112 char *clf;
1113 int h;
1115 ccr = (char *)memchr( line, '\r', len ) ;
1116 clf = (char *)memchr( line, '\n', len ) ;
1117 if ( ccr != NULL )
1119 if ( clf != NULL )
1121 if ( clf < ccr )
1123 *termlen = ( ccr == clf + 1 ) ? 2 : 1 ;
1124 return (int) ( clf - line ) ;
1126 else
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 )
1136 *termlen = 1 ;
1137 return h ;
1140 else if ( clf != NULL ) /* simple line feed */
1142 h = (int) ( clf - line ) ;
1143 if ( ( h + 1 < len ) || EOFreached )
1145 *termlen = 1 ;
1146 return h ;
1149 return -1 ;
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.
1164 streng *s ;
1165 char *ptr ;
1166 void *fptr = NULL ;
1167 int max, found, termlen, isStream ;
1170 s = *string;
1171 if (s == NULL) /* might happen on a first call */
1172 return;
1174 if (is_error)
1176 isStream = ( env->error.type == STREAM ) ;
1177 fptr = ( env->error.SameAsOutput ) ? env->output.file : env->error.file;
1179 else
1181 isStream = ( env->output.type == STREAM ) ;
1182 fptr = env->output.file ;
1185 if ( isStream )
1187 /* very fast bypass, we can simply drop the whole thing */
1188 if ( fptr )
1189 addr_io_file( TSD, fptr, s ) ;
1191 s->len = 0 ;
1193 else
1195 ptr = s->value ;
1196 max = Str_len( s ) ;
1198 while ( max > 0 )
1200 found = line_length( ptr, max, &termlen, EOFreached ) ;
1202 if ( ( found == -1 ) && EOFreached )
1204 found = max ;
1205 termlen = 0 ;
1207 if ( found < 0 )
1208 break;
1210 drop_crop_line( TSD, env, ptr, (unsigned) found, is_error ) ;
1211 found += termlen ;
1212 max -= found ;
1213 ptr += found ;
1215 memcpy( s->value, ptr, max ) ;
1216 s->len = max ;
1218 *string = s ;
1221 int posix_do_command( tsd_t *TSD, const streng *command, int io_flags, environment *env, Queue *redir )
1223 int child, rc ;
1224 int in, out, err;
1225 streng *istring = NULL, *ostring = NULL, *estring = NULL ;
1226 char *cmdline ;
1227 shl_tsd_t *st = (shl_tsd_t *)TSD->shl_tsd;
1229 fflush( stdout ) ;
1230 fflush( stderr ) ;
1232 CloseOpenFiles( TSD, fpdRETAIN );
1235 * Fixes bug 615822
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);
1250 if (rc < 0)
1252 errno = -rc;
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 ) )
1269 err = errno;
1270 cleanup( TSD, env ) ;
1271 if ( child == -1 )
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 ) ;
1287 else
1288 in = -1;
1289 if (!env->output.FileRedirected && ((out = env->output.hdls[0]) != -1))
1291 TSD->OS->unblock_handle( &out, st->AsyncInfo ) ;
1293 else
1294 out = -1;
1295 if (!env->error.FileRedirected && ((err = env->error.hdls[0]) != -1))
1297 TSD->OS->unblock_handle( &err, st->AsyncInfo ) ;
1299 else
1300 err = -1;
1302 #ifdef SIGPIPE
1303 regina_signal( SIGPIPE, SIG_IGN ) ;
1304 #endif
1306 while ((in != -1) || (out != -1) || (err != -1))
1308 TSD->OS->reset_async_info(st->AsyncInfo);
1309 if (in != -1)
1311 do {
1312 if (!istring)
1313 istring = fetch_food( TSD, env ) ;
1314 if (!istring)
1316 rc = write_buffered(TSD, in, NULL, 0, st->AsyncInfo);
1317 if (rc == -EAGAIN)
1318 TSD->OS->add_async_waiter(st->AsyncInfo, in, 0);
1319 else
1321 if ((rc < 0) && (-rc != EPIPE)) /* fixes bug 945218 */
1323 errno = -rc;
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 ) ;
1335 if (rc)
1337 if (rc == EAGAIN)
1338 TSD->OS->add_async_waiter(st->AsyncInfo, in, 0);
1339 else
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.
1351 rc = -1;
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) */
1360 if (out != -1)
1362 do {
1363 rc = reap( TSD, &ostring, out, st->AsyncInfo );
1364 if (rc)
1366 if (rc == EAGAIN)
1367 TSD->OS->add_async_waiter(st->AsyncInfo, out, 1);
1368 else
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) */
1380 if (err != -1)
1382 do {
1383 rc = reap( TSD, &estring, err, st->AsyncInfo );
1384 if (rc)
1386 if (rc == EAGAIN)
1387 TSD->OS->add_async_waiter(st->AsyncInfo, err, 1);
1388 else
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 */
1401 } /* end of IO */
1403 if (istring)
1404 Free_stringTSD( istring );
1406 if (ostring)
1408 if ( Str_len( ostring ) )
1409 drop_crop( TSD, env, &ostring, 1, 0 );
1410 Free_stringTSD( ostring );
1413 if (estring)
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);
1425 #ifdef SIGPIPE
1426 regina_signal( SIGPIPE, SIG_DFL ) ;
1427 #endif
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 ) ;
1472 return rc ;