2 * The Regina Rexx Interpreter
3 * Copyright (C) 1993-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.
24 # define fork() vfork()
26 * At least with OpenVMS 7.3-1 on Alpha, the Posix way seems to work.
27 * So there is no need to redirect on (now) bogus code.
31 # ifdef posix_do_command
32 # undef posix_do_command
34 # define posix_do_command __regina_vms_do_command
39 * The following strings must match AddressWithType enums in regina_t.h
41 static char *env_type
[] = { "NORMAL", "STREAM", "STEM", "LIFO", "FIFO" };
47 struct envir
*next
, *prev
;
50 #if defined(_AMIGA) || defined(__AROS__)
51 struct envir
*amiga_find_envir( const tsd_t
*TSD
, const streng
* );
52 streng
*AmigaSubCom( const tsd_t
*TSD
, const streng
*command
, struct envir
*envir
, int *rc
);
55 /* clear_environpart sets initial values of an environpart.
56 * attention: cleanup_environpart() closes open file handles and removes
57 * temporary files, this function not.
59 static void clear_environpart(environpart
*ep
)
64 ep
->FileRedirected
= 0;
66 ep
->tmp_queue
= NULL
;
69 ep
->hdls
[2] = ep
->hdls
[1] = ep
->hdls
[0] = -1;
72 void add_envir( tsd_t
*TSD
, const streng
*name
, int type
, int subtype
)
73 /* an environment with the same name can be added more than once! */
77 ptr
= (struct envir
*)MallocTSD( sizeof( struct envir
) );
78 memset( &ptr
->e
, 0, sizeof(ptr
->e
) );
79 clear_environpart( &ptr
->e
.input
);
80 clear_environpart( &ptr
->e
.output
);
81 clear_environpart( &ptr
->e
.error
);
82 ptr
->e
.input
.flags
.isinput
= 1;
83 ptr
->e
.error
.flags
.iserror
= 1;
85 ptr
->e
.name
= Str_dupTSD( name
);
86 ptr
->e
.subtype
= subtype
;
89 ptr
->prev
= (struct envir
*)TSD
->firstenvir
;
91 TSD
->firstenvir
= ptr
;
93 ptr
->prev
->next
= ptr
;
97 static void markenvir( const tsd_t
*TSD
)
99 struct envir
*eptr
=NULL
;
101 eptr
= (struct envir
*) TSD
->firstenvir
;
102 for (; eptr
; eptr
=eptr
->prev
)
104 markmemory( eptr
, TRC_ENVIRBOX
) ;
105 markmemory( eptr
->e
.name
, TRC_ENVIRNAME
) ;
106 if (eptr
->e
.input
.name
)
107 markmemory( eptr
->e
.input
.name
, TRC_ENVIRNAME
) ;
108 if (eptr
->e
.output
.name
)
109 markmemory( eptr
->e
.output
.name
, TRC_ENVIRNAME
) ;
110 if (eptr
->e
.error
.name
)
111 markmemory( eptr
->e
.error
.name
, TRC_ENVIRNAME
) ;
114 #endif /* TRACEMEM */
117 static struct envir
*find_envir( const tsd_t
*TSD
, const streng
*name
)
119 struct envir
*ptr
=NULL
;
121 #if defined(_AMIGA) || defined(__AROS__)
122 if ((ptr
=amiga_find_envir( TSD
, name
))!=NULL
)
126 for ( ptr
= (struct envir
*)TSD
->firstenvir
; ptr
; ptr
= ptr
->prev
)
128 if ( !Str_cmp( ptr
->e
.name
, name
) )
135 * This function is used to determine if an environment exists. It is the
136 * external equivalent of find_envir()
138 int envir_exists( const tsd_t
*TSD
, const streng
*name
)
140 if ( find_envir( TSD
, name
) == NULL
)
147 * This function returns textual information about the current environment.
148 * Suitable output for ADDRESS BIF
150 streng
*get_envir_details( const tsd_t
*TSD
, char opt
, const streng
*name
)
152 struct envir
*ptr
=NULL
;
153 streng
*result
, *env_resource
, *raw_resource
;
157 ptr
= find_envir( TSD
, name
);
161 env_position
= (char *)"INPUT";
162 awt
= ptr
->e
.input
.flags
.awt
;
163 ant
= ptr
->e
.input
.flags
.ant
;
164 raw_resource
= ptr
->e
.input
.name
;
167 env_position
= (char *)( (ptr
->e
.output
.flags
.append
) ? "APPEND" : "REPLACE" );
168 awt
= ptr
->e
.output
.flags
.awt
;
169 ant
= ptr
->e
.output
.flags
.ant
;
170 raw_resource
= ptr
->e
.output
.name
;
173 env_position
= (char *)( (ptr
->e
.error
.flags
.append
) ? "APPEND" : "REPLACE" );
174 awt
= ptr
->e
.error
.flags
.awt
;
175 ant
= ptr
->e
.error
.flags
.ant
;
176 raw_resource
= ptr
->e
.error
.name
;
180 * Should not get here!!
188 * Determine the resource string...
190 if ( raw_resource
== NULL
)
192 env_resource
= nullstringptr();
194 else if ( awt
== awtSTEM
)
196 env_resource
= raw_resource
;
198 else if ( ant
== antSTRING
)
200 if ( raw_resource
== NULL
)
202 env_resource
= nullstringptr();
206 env_resource
= raw_resource
;
212 * Get the value of the symbol...
214 env_resource
= (streng
*)getdirvalue( (tsd_t
*)TSD
, raw_resource
);
216 len
= 3 + strlen( env_position
) + strlen( env_type
[awt
] ) + env_resource
->len
;
217 result
= Str_makeTSD( len
);
218 result
= Str_catstrTSD( result
, env_position
);
219 result
= Str_catstrTSD( result
, " " );
220 result
= Str_catstrTSD( result
, env_type
[awt
] );
221 if ( env_resource
->len
)
223 result
= Str_catstrTSD( result
, " " );
224 result
= Str_catTSD( result
, env_resource
);
230 * This function is used to set the subcomed flag in an environment exists.
232 int set_subcomed_envir( const tsd_t
*TSD
, const streng
*name
, int subcomed
)
234 struct envir
*ptr
=NULL
;
235 if ( ( ptr
= find_envir( TSD
, name
) ) == NULL
)
237 ptr
->e
.subcomed
= subcomed
;
241 * This function is used to get the subcomed flag from an environment.
243 int get_subcomed_envir( const tsd_t
*TSD
, const streng
*name
)
245 struct envir
*ptr
=NULL
;
246 if ( ( ptr
= find_envir( TSD
, name
) ) == NULL
)
248 return ptr
->e
.subcomed
;
251 static void del_envirpart( const tsd_t
*TSD
, environpart
*e
)
252 /* Deletes all allocated parts of the environpart e.
253 * The names are set to NULL afterwards, thus allowing multiple calls.
257 Free_stringTSD( e
->name
) ;
259 Free_stringTSD( e
->base
) ;
261 Free_stringTSD( e
->currname
) ;
265 /* Temporary files must be deleted under all circumstances! */
266 cleanup_envirpart(TSD
, e
);
269 void del_envir( tsd_t
*TSD
, const streng
*name
)
270 /* Deletes all allocated parts of the environment with the given name and
271 * removes it from the linked list.
274 struct envir
*ptr
=NULL
;
276 ptr
= find_envir( TSD
, name
) ;
277 #if defined(_AMIGA) || defined(__AROS__)
278 /* Amiga environments may not be deleted */
279 if (ptr
&& ptr
->type
== ENVIR_AMIGA
)
286 ptr
->prev
->next
= ptr
->next
;
288 ptr
->next
->prev
= ptr
->prev
;
289 if (TSD
->firstenvir
==ptr
)
290 TSD
->firstenvir
= ptr
->prev
;
292 /* Delete the names in the environment */
294 Free_stringTSD( ptr
->e
.name
) ;
295 del_envirpart( TSD
, &ptr
->e
.input
) ;
296 del_envirpart( TSD
, &ptr
->e
.output
) ;
297 del_envirpart( TSD
, &ptr
->e
.error
) ;
301 static void update_environpart( const tsd_t
*TSD
, environpart
*e
, const nodeptr newptr
)
302 /* e is the environpart which has to be reset. new is the new part and has
303 * to been valid. new->name may be NULL (for NORMAL behaviour).
306 del_envirpart( TSD
, e
) ;
310 e
->name
= Str_dupTSD( newptr
->name
) ;
311 e
->base
= Str_makeTSD( 3*sizeof(int) ) ;
314 e
->flags
= newptr
->u
.of
;
315 clear_environpart(e
);
318 int set_envir( const tsd_t
*TSD
, const streng
*envirname
, const nodeptr ios
)
319 /* This function sets all three IO-redirections INPUT, OUTPUT and ERROR.
320 * ios->p[0] must be the redirection of INPUT,
321 * ios->p[1] must be the redirection of OUTPUT,
322 * ios->p[2] must be the redirection of ERROR
323 * ios==NULL || envirname==NULL --> don't do anything.
324 * ios->p[x]==NULL --> don't touch this redirection.
325 * ios->p[x].name may be NULL (for NORMAL behaviour).
327 * The types of all four nodes may or may not be X_ADDR_WITH.
329 * returns 1 on success, 0 if the envirname doesn't exist.
334 if ( ( envirname
== NULL
) || ( ios
== NULL
) )
337 if ( ( e
= find_envir( TSD
, envirname
) ) == NULL
)
339 if (ios
->p
[0]) update_environpart( TSD
, &e
->e
.input
, ios
->p
[0] );
340 if (ios
->p
[1]) update_environpart( TSD
, &e
->e
.output
, ios
->p
[1] );
341 if (ios
->p
[2]) update_environpart( TSD
, &e
->e
.error
, ios
->p
[2] );
342 e
->e
.input
.flags
.isinput
= 1;
343 e
->e
.error
.flags
.iserror
= 1;
348 static void dup_environpart( const tsd_t
*TSD
, environpart
*dest
,
349 const nodeptr prefer
, const environpart
*src
)
350 /* Copies src to dest if prefer isn't set. In this case prefer is used.
354 update_environpart( TSD
, dest
, prefer
) ;
359 dest
->name
= Str_dupTSD( src
->name
) ;
360 dest
->base
= Str_makeTSD( 3*sizeof(int) ) ;
362 dest
->flags
= src
->flags
;
364 clear_environpart(dest
);
367 static struct envir
*dup_envir( tsd_t
*TSD
, const streng
*name
, cnodeptr ios
)
368 /* This functions returns a new instance of the environment with the given
369 * name or NULL if there is not such an environment name.
370 * The current IO-redirection settings are overwritten with the
371 * environment parts given in ios->p[0..2] for INPUT, OUTPUT and ERROR.
374 struct envir
*prev
, *newptr
;
376 if ( ( prev
= find_envir( TSD
, name
) ) == NULL
)
379 add_envir( TSD
, name
, prev
->type
, prev
->e
.subtype
);
380 newptr
= (struct envir
*)TSD
->firstenvir
;
382 dup_environpart( TSD
, &newptr
->e
.input
, ios
->p
[0], &prev
->e
.input
);
383 dup_environpart( TSD
, &newptr
->e
.output
, ios
->p
[1], &prev
->e
.output
);
384 dup_environpart( TSD
, &newptr
->e
.error
, ios
->p
[2], &prev
->e
.error
);
385 newptr
->e
.input
.flags
.isinput
= 1;
386 newptr
->e
.error
.flags
.iserror
= 1;
391 int init_envir( tsd_t
*TSD
)
393 static const struct {
397 { "COMMAND", SUBENVIR_PATH
} , /* was SUBENVIR_COMMAND */
398 { "SYSTEM", SUBENVIR_SYSTEM
} ,
399 { "OS2ENVIRONMENT", SUBENVIR_SYSTEM
} ,
400 { "ENVIRONMENT", SUBENVIR_SYSTEM
} ,
401 { "CMD", SUBENVIR_PATH
} , /* was SUBENVIR_COMMAND */
402 { "PATH", SUBENVIR_PATH
} ,
403 { "REGINA", SUBENVIR_REXX
} ,
404 { "REXX", SUBENVIR_REXX
}
409 for ( i
= 0; i
< sizeof(locals
) / sizeof(locals
[0]); i
++ )
411 tmp
= Str_creTSD( locals
[i
].name
) ;
412 add_envir( TSD
, tmp
, ENVIR_SHELL
, locals
[i
].subtype
) ;
413 Free_stringTSD( tmp
) ;
416 regmarker( TSD
, markenvir
) ;
422 static int get_io_flag( tsd_t
*TSD
, streng
*command
, streng
**rxqueue
)
424 int length
=0, i
=0, pos
=0 ;
425 int flag
=0,have_space
=0 ;
426 int qname_start
,qname_end
;
431 * All I/O redirection valid with INTERNAL queues only
433 if ((length
=Str_len(command
)) > 5
434 && (get_options_flag( TSD
->currlevel
, EXT_INTERNAL_QUEUES
)
435 || external_queues_used( TSD
) == 0 ) )
437 if ((!memcmp(command
->value
,"lifo>",5)) ||
438 (!memcmp(command
->value
,"LIFO>",5)))
439 flag
|= REDIR_INPUT
;
441 if ((!memcmp(command
->value
+length
-5,">lifo",5))
442 || (!memcmp(command
->value
+length
-5,">LIFO",5)))
444 flag
|= REDIR_OUTLIFO
;
449 if ( ( !memcmp( command
->value
+length
-5, ">fifo" ,5 ) )
450 || ( !memcmp( command
->value
+length
-5, ">FIFO", 5 ) ) )
452 flag
|= REDIR_OUTFIFO
;
459 for ( i
= 0, pos
= -1; i
< length
; i
++ )
461 if ( *(command
->value
+ i
) == '|' )
464 /* don't break here, as we want the last '|' */
469 /* allow "|" [whitespace] "rxqueue" [whitespace[args]] */
470 /* "|" already checked */
471 for ( i
= pos
+ 1; i
< length
; i
++ )
473 if ( !rx_isspace(command
->value
[i
] ) )
478 if (mem_cmpic( command
->value
+ i
, "RXQUEUE", 7 ) == 0 )
481 for ( ; i
< length
; i
++ )
483 if ( !rx_isspace( command
->value
[i
] ) )
489 flag
|= REDIR_OUTFIFO
;
492 else if ( have_space
)
494 if ( *(command
->value
+ i
) == '/' )
497 * Only a switch, not a queuename
500 && mem_cmpic( command
->value
+ i
, "/CLEAR", 6 ) == 0 )
503 flag
|= REDIR_OUTFIFO
;
505 else if ( i
+5 <= length
)
507 if (mem_cmpic( command
->value
+ i
, "/FIFO", 5 ) == 0 )
508 flag
|= REDIR_OUTFIFO
;
509 else if (mem_cmpic( command
->value
+ i
, "/LIFO", 5 ) == 0 )
510 flag
|= REDIR_OUTLIFO
;
512 flag
|= REDIR_OUTFIFO
;
514 * Let the queue name be determined by the caller
521 * First word must be a queue name, optionally
522 * followed by a switch
526 for ( ; i
< length
; i
++ )
528 if ( rx_isspace( command
->value
[i
] ) )
533 * Get queuename from RXQUEUE env
534 * variable, or if not set, use SESSION
536 tmpq
= Str_make_TSD( TSD
, qname_end
- qname_start
);
537 tmpq
->len
= qname_end
- qname_start
;
538 memcpy( tmpq
->value
, command
->value
+ qname_start
, qname_end
- qname_start
);
545 * Eat up all spaces. If we have
546 * any non-spaces left, it should be
549 for ( ; i
< length
; i
++ )
551 if ( !rx_isspace( command
->value
[i
] ) )
555 && mem_cmpic( command
->value
+ i
, "/CLEAR", 6 ) == 0 )
558 flag
|= REDIR_OUTFIFO
;
560 else if ( i
+5 <= length
)
562 if (mem_cmpic( command
->value
+ i
, "/FIFO", 5 ) == 0 )
563 flag
|= REDIR_OUTFIFO
;
564 else if (mem_cmpic( command
->value
+ i
, "/LIFO", 5 ) == 0 )
565 flag
|= REDIR_OUTLIFO
;
567 flag
|= REDIR_OUTFIFO
;
573 * Only have a queue name
575 tmpq
= Str_make_TSD( TSD
, length
- qname_start
);
576 tmpq
->len
= length
- qname_start
;
577 memcpy( tmpq
->value
, command
->value
+ qname_start
, length
- qname_start
);
578 flag
|= REDIR_OUTFIFO
;
591 if (flag
& REDIR_INPUT
)
593 for(i
=5; i
<Str_len(command
); i
++ ) /* avoid buffer overrun */
594 command
->value
[i
-5]=command
->value
[i
] ;
602 * ANSI 8.2.4 and 8.3.5 and others forces us to set some variables showing
603 * the result and to raise some conditions in case of errors after
604 * processing an external command.
606 void post_process_system_call( tsd_t
*TSD
, const streng
*cmd
,
607 int rc_code
, const streng
*rc_value
,
615 * ANSI 8.2.4 etc. forces us to do the following.
617 * 10-08-2004 MH interpreted 8.2.4 as only being applicable to
618 * clauses entered at the interactive prompt.
621 if ( !TSD
->systeminfo
->interactive
)
624 if ( rc_value
!= NULL
)
625 set_reserved_value( TSD
, POOL0_RC
, Str_dupTSD( rc_value
), 0,
628 set_reserved_value( TSD
, POOL0_RC
, NULL
, rc_code
, VFLAG_NUM
);
631 /* set .RS: -1==Failure, 0=OK, 1=Error */
634 else if ( rc_code
< 0 )
638 set_reserved_value( TSD
, POOL0_RS
, NULL
, rs
, VFLAG_NUM
);
640 /* ARexx OPTIONS RESULTS */
641 if ( get_options_flag( TSD
->currlevel
, EXT_RESULTS
) )
644 set_reserved_value( TSD
, POOL0_RESULT
, Str_dupTSD( rc_value
), 0, VFLAG_STR
);
646 set_reserved_value( TSD
, POOL0_RESULT
, NULL
, 0, VFLAG_NONE
);
651 traceerror( TSD
, thisptr
, rc_code
);
652 traps
= gettraps( TSD
, TSD
->currlevel
);
653 type
= ( rc_code
> 0 ) ? SIGNAL_ERROR
: SIGNAL_FAILURE
;
655 if ( traps
[type
].on_off
)
656 condition_hook( TSD
, type
, rc_code
, 0, thisptr
->lineno
, Str_dupTSD( cmd
), NULL
);
660 streng
*perform( tsd_t
*TSD
, const streng
*command
, const streng
*envir
, cnodeptr thisptr
, cnodeptr overwrite
)
661 /* If and only if overwrite is set, a new instance of the environment is
662 * temporarily created and reset to the new IO-redirections.
665 int rc
=0, io_flag
=0, clearq
=0, tempenvir
=0;
667 streng
*retstr
=NULL
;
668 streng
*rxqueue
=NULL
;
669 streng
*cmd
=Str_dupTSD(command
);
670 streng
*saved_queue
=NULL
;
676 * fixes bug 653214, overwrite was this->p[1] in former versions, which
677 * was wrong is this wasn't an ADDRESS statement.
679 if (( eptr
= dup_envir( TSD
, envir
, overwrite
) ) != NULL
)
684 eptr
= find_envir( TSD
, envir
) ;
691 exiterror( ERR_RESTRICTED
, 5 ) ;
695 retstr
= SubCom( TSD
, cmd
, envir
, &rc
) ;
699 io_flag
= get_io_flag( TSD
, cmd
, &rxqueue
) ;
701 * Save the current queue name
702 * Then change it to the value of rxqueue
703 * Only for internal queues
705 if ( get_options_flag( TSD
->currlevel
, EXT_INTERNAL_QUEUES
) )
708 * If no queuename specified,
709 * get queuename from RXQUEUE env
710 * variable, or if not set, use SESSION
712 if ( rxqueue
== NULL
)
714 if ( ( rxq
= getenv("RXQUEUE") ) == NULL
)
716 rxqueue
= Str_cre_TSD( TSD
, "SESSION" );
720 rxqueue
= Str_cre_TSD( TSD
, rxq
);
723 saved_queue
= set_queue( TSD
, rxqueue
);
724 if ( io_flag
& REDIR_CLEAR
)
727 io_flag
-= REDIR_CLEAR
;
730 rc
= posix_do_command( TSD
, cmd
, io_flag
, &eptr
->e
, NULL
) ;
732 * Change the current queue name back
735 if ( get_options_flag( TSD
->currlevel
, EXT_INTERNAL_QUEUES
) )
738 drop_buffer( TSD
, 0 ) ;
739 set_queue( TSD
, saved_queue
);
740 if ( rxqueue
!= NULL
)
741 Free_stringTSD( rxqueue
);
743 retstr
= int_to_streng( TSD
, rc
) ;
746 #if defined(_AMIGA) || defined(__AROS__)
748 retstr
= AmigaSubCom( TSD
, cmd
, eptr
, &rc
);
753 exiterror( ERR_INTERPRETER_FAILURE
, 1, __FILE__
, __LINE__
, "" ) ;
758 retstr
= SubCom( TSD
, cmd
, envir
, &rc
) ;
762 del_envir( TSD
, envir
) ;
764 post_process_system_call( TSD
, cmd
, rc
, retstr
, thisptr
);
766 Free_stringTSD( cmd
) ;
770 /* run_popen is a special implementation of a direct call to posix_do_command.
771 * It redirects input and error to "normal" and fetches the output.
772 * This output has blanks as line-delimiters. All lines are returned
773 * concatenated on success; NULL is returned if an errors occurs.
774 * The global variable "RC" contains the return code of the called process.
776 * The command is executed in the current environment. If the environment
777 * isn't a standard environment, SYSTEM is used.
779 streng
*run_popen( tsd_t
*TSD
, const streng
*command
, const streng
*envir
)
783 struct envir
*ptr
=NULL
;
786 for ( ptr
= (struct envir
*)TSD
->firstenvir
; ptr
; ptr
= ptr
->prev
)
788 if ( ( ptr
->type
== ENVIR_SHELL
) && ( Str_cmp( ptr
->e
.name
, envir
) == 0 ) )
793 retval
= Str_creTSD( "SYSTEM" ); /* temporary misuse */
794 ptr
= find_envir( TSD
, retval
);
795 Free_stringTSD( retval
) ;
798 /* Create a new environment with no redirections. */
799 add_envir(TSD
, ptr
->e
.name
, ENVIR_SHELL
, ptr
->e
.subtype
);
801 q
= find_free_slot( TSD
) ;
803 rc
= posix_do_command(TSD
, command
, REDIR_OUTSTRING
, (environment
*)TSD
->firstenvir
, q
) ;
804 retval
= stack_to_line( TSD
, q
) ;
806 /* restore the previous environment and delete the temporary one */
807 del_envir(TSD
, ptr
->e
.name
);
809 set_reserved_value( TSD
, POOL0_RC
, NULL
, rc
, VFLAG_NUM
);
814 /* rc shows an error while calling: */
815 Free_stringTSD(retval
);