2 * The Regina Rexx Interpreter
3 * Copyright (C) 2005 Florian Grosse-Coosmann
5 * System interfacing functions for OS/2 and EMX
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the Free
19 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 #define DONT_TYPEDEF_PFN
32 #if defined(DOS) /* MH 10-06-96 */
37 #endif /* MH 10-06-96 */
41 #if defined(HAVE_ASSERT_H)
45 #if defined(HAVE_UNISTD_H)
49 #if defined(HAVE_SYS_STAT_H)
50 # include <sys/stat.h>
53 #if defined(HAVE_SYS_WAIT_H)
54 # include <sys/wait.h>
57 #if defined(HAVE_FCNTL_H)
61 #if defined(HAVE_SYS_FCNTL_H)
62 # include <sys/fcntl.h>
65 #if defined(HAVE_PROCESS_H)
69 #include "utsname.h" /* MH 10-06-96 */
71 #if defined (__EMX__) || defined(__WATCOMC__)
73 # include <sys/wait.h>
79 static int Os2_setenv( const char *name
, const char *value
)
81 #if defined(HAVE_SETENV)
82 setenv( name
, value
, 1 );
89 cmd
= malloc( strlen(name
) + strlen(value
) + 2);
90 sprintf( cmd
, "%s=%s", name
, value
);
104 /* fork_exec spawns a new process with the given commandline.
105 * it returns -1 on error (errno set), 0 on process start error (rcode set),
106 * a process descriptor otherwise.
107 * Basically this is a child process and we run in the child's environment
108 * after the first few lines. The setup hasn't been done and the command needs
110 * Redirection takes place if one of the handles env->input.hdls[0],
111 * env->output.hdls[1] or env->error.hdls[1] is defined. Other handles (except
112 * standard handles) are closed. env->subtype must be a SUBENVIR_... constant.
113 * cmdline is the whole command line.
114 * Never use TSD after the fork() since this is not only a different thread,
115 * it's a different process!
116 * Although mentioned in the documentation we have to use a backslash a
117 * escape character nevertheless. It's a bug of EMX not to recognize a
118 * circumflex as the default escape character and therefore we have to
119 * use the "wrong" escape character. Note the difference between EMX and OS/2.
120 * Maybe, I'm wrong. Drop me an email in this case. FGC
122 static int Os2_fork_exec(tsd_t
*TSD
, environment
*env
, const char *cmdline
, int *rcode
)
124 static const char *interpreter
[] = { "regina.exe", /* preferable even if */
128 int saved_in
= -1, saved_out
= -1, saved_err
= -1;
132 int broken_address_command
= get_options_flag( TSD
->currlevel
, EXT_BROKEN_ADDRESS_COMMAND
);
135 if (env
->subtype
== SUBENVIR_REXX
) /*special situation caused by recursion*/
137 environment e
= *env
;
143 len
= 11; /* max("rexx.exe", "regina.exe") */
148 len
= 11; /* max("rexx.exe", "regina.exe") */
150 len
+= strlen(cmdline
) + 2; /* Blank + term ASCII0 */
152 if ((new_cmdline
= malloc(len
)) == NULL
)
153 return(-1); /* ENOMEM is set */
155 if (argv0
!= NULL
) /* always the best choice */
157 strcpy(new_cmdline
, argv0
);
158 strcat(new_cmdline
, " ");
159 strcat(new_cmdline
, cmdline
);
160 e
.subtype
= SUBENVIR_COMMAND
;
161 rc
= Os2_fork_exec(TSD
, &e
, new_cmdline
, &rc
);
162 if ( ( rc
!= 0 ) && ( rc
!= -1 ) )
169 /* load an interpreter by name from the path */
170 for (i
= 0; i
< sizeof(interpreter
) / sizeof(interpreter
[0]);i
++)
172 strcpy(new_cmdline
, interpreter
[i
]);
173 strcat(new_cmdline
, " ");
174 strcat(new_cmdline
, cmdline
);
175 e
.subtype
= SUBENVIR_COMMAND
;
176 rc
= Os2_fork_exec(TSD
, &e
, new_cmdline
, &rc
);
177 if ( ( rc
!= 0 ) && ( rc
!= -1 ) )
185 *rcode
= -errno
; /* assume a load error */
189 if ( ( rc
= fork() ) != 0 ) /* EMX is fork-capable */
193 #ifdef __EMX__ /* redirect this call to the non-OS/2-code if DOS is running */
194 /* SUBENVIR_REXX must(!!) fork if we are here! */
196 extern OS_Dep_funcs __regina_OS_Other
;
198 if ((_osmode
!= OS2_MODE
) && (env
->subtype
!= SUBENVIR_REXX
))
199 return(__regina_OS_Other
.fork_exec(TSD
, env
, cmdline
, rcode
));
203 #define STD_REDIR(hdl,dest,save) if ((hdl != -1) && (hdl != dest)) \
204 { save = dup(dest); dup2(hdl, dest); }
205 #define STD_RESTORE(saved,dest) if (saved != -1) \
206 { close(dest); dup2(saved,dest); \
208 #define SET_MAXHDL(hdl) if (hdl > max_hdls) max_hdls = hdl
209 #define SET_MAXHDLS(ep) SET_MAXHDL(ep.hdls[0]); SET_MAXHDL(ep.hdls[1])
211 /* Force the standard redirections: */
212 STD_REDIR(env
->input
.hdls
[0], 0, saved_in
);
213 STD_REDIR(env
->output
.hdls
[1], 1, saved_out
);
214 if (env
->error
.SameAsOutput
)
221 STD_REDIR(env
->error
.hdls
[1], 2, saved_err
);
225 * If the BROKEN_ADDRESS_COMMAND OPTION is in place,
226 * and our environment is COMMAND, change it to SYSTEM
228 if ( env
->subtype
== SUBENVIR_PATH
/* was SUBENVIR_COMMAND */
229 && broken_address_command
)
230 subtype
= SUBENVIR_SYSTEM
;
232 subtype
= env
->subtype
;
238 args
= makeargs(cmdline
, '^');
239 rc
= spawnvp(P_NOWAIT
, *args
, args
);
242 case SUBENVIR_COMMAND
:
243 args
= makeargs(cmdline
, '^');
244 rc
= spawnv(P_NOWAIT
, *args
, args
);
247 case SUBENVIR_SYSTEM
:
248 /* insert "%COMSPEC% /c " or "%SHELL% -c " in front */
249 if ((ipret
= getenv("COMSPEC")) != NULL
)
251 argline
= MallocTSD(strlen(ipret
) + strlen(cmdline
) + 5);
252 strcpy(argline
,ipret
);
253 strcat(argline
," /c ");
255 else if ((ipret
= getenv("SHELL")) != NULL
)
257 argline
= MallocTSD(strlen(ipret
) + strlen(cmdline
) + 5);
258 strcpy(argline
,ipret
);
259 strcat(argline
," -c ");
264 argline
= MallocTSD(strlen(ipret
) + strlen(cmdline
) + 5);
265 strcpy(argline
,ipret
);
266 strcat(argline
," /c ");
268 strcat(argline
, cmdline
);
269 args
= makeargs(argline
, '^');
270 rc
= spawnvp(P_NOWAIT
, *args
, args
);
275 /* we are forked and we are the child!!!! */
276 /* last chance, worst choice, use the re-entering code: */
277 char *new_cmdline
= malloc(strlen(cmdline
) + 4);
281 strcpy(new_cmdline
, "\"\" ");
282 strcat(new_cmdline
, cmdline
);
283 args
= makeargs(new_cmdline
, '^');
285 for (i
= 0, run
= args
; *run
; run
++)
287 exit(__regina_reexecute_main(i
, args
));
290 default: /* illegal subtype */
291 STD_RESTORE(saved_in
, 0);
292 STD_RESTORE(saved_out
, 1);
293 STD_RESTORE(saved_err
, 2);
299 STD_RESTORE(saved_in
, 0);
300 STD_RESTORE(saved_out
, 1);
301 STD_RESTORE(saved_err
, 2);
305 return ( rc
== -1 ) ? 0 : rc
;
312 /* wait waits for a process started by fork_exec.
313 * In general, this is called after the complete IO to the called process but
314 * it isn't guaranteed. Never call if you don't expect a sudden death of the
316 * Returns the exit status of the subprocess under normal circumstances. If
317 * the subprocess has died by a signal, the return value is -signalnumber.
319 static int Os2_wait(int process
)
321 int rc
, retval
, status
;
325 * Watcom is strange. EINTR isn't an indicator for a retry of the call.
328 rc
= cwait(&status
, process
, WAIT_CHILD
);
331 if ((status
!= -1) && (errno
== EINTR
))
332 retval
= -status
; /* Exception reason in lower byte */
334 retval
= -errno
; /* I don't have a better idea */
339 retval
= -status
; /* Exception reason in lower byte */
341 retval
= status
>> 8;
345 rc
= waitpid(process
, &status
, 0);
346 } while ((rc
== -1) && (errno
== EINTR
));
348 if (WIFEXITED(status
))
350 retval
= (int) WEXITSTATUS(status
);
354 else if (WIFSIGNALED(status
))
356 retval
= -WTERMSIG(status
);
359 else if ( retval
== 0 )
364 retval
= -WSTOPSIG(status
);
367 else if ( retval
== 0 )
375 /* open_subprocess_connection acts like the unix-known function pipe and sets
376 * ep->RedirectedFile if necessary. Just in the latter case ep->data
377 * is set to the filename.
378 * Close the handles with __regina_close later.
379 * Do IO by using Os2_read() and Os2_write().
381 static int Os2_open_subprocess_connection(const tsd_t
*TSD
, environpart
*ep
)
383 #define MAGIC_MAX 2000000 /* Much beyond maximum, see below */
384 static volatile unsigned BaseIndex
= MAGIC_MAX
;
388 ULONG rc
, openmode
, dummy
;
389 HPIPE in
= (HPIPE
) -1, out
;
391 /* We have to use named pipes for various reasons. */
392 openmode
= (ep
->flags
.isinput
) ? NP_ACCESS_OUTBOUND
: NP_ACCESS_INBOUND
;
393 openmode
|= NP_NOINHERIT
| NP_WRITEBEHIND
;
396 * select a random number, e.g. a mixture of pid, tid and time.
397 * increment this number by a fixed amount until we reach the starting
398 * value again. Do a wrap around and an increment which is a unique prime.
399 * The number 1000000 has the primes 2 and 5. We may use all primes
400 * except 2 and 5; 9901 (which is prime) will give a good distribution.
402 * We want to be able to create several temporary files at once without
403 * more OS calls than needed. Thus, we have a program wide runner to
404 * ensure a simple distribution without strength.
406 if (BaseIndex
== MAGIC_MAX
)
409 * We have to create (nearly) reentrant code.
411 i
= (unsigned) getpid() * (unsigned) time(NULL
);
417 if (++BaseIndex
>= 1000000)
420 start
= TSD
->thread_id
;
423 start
*= (unsigned) (clock() + 1);
428 for (i
= 0;i
<= 1000000;i
++)
430 sprintf(buf
,"%s%06u._rx", "\\pipe\\tmp\\", run
);
431 rc
= DosCreateNPipe(buf
,
434 NP_TYPE_BYTE
| NP_READMODE_BYTE
| NP_NOWAIT
| 1,
437 0); /* msec timeout */
441 if (rc
!= ERROR_PIPE_BUSY
)
447 /* Check the next possible candidate */
450 if (run
== start
) /* paranoia check. i <= 1000000 should hit exactly */
454 if (in
== (HPIPE
) -1)
460 DosConnectNPipe(in
); /* ignore the return */
462 openmode
= (ep
->flags
.isinput
) ? OPEN_ACCESS_READONLY
:
463 OPEN_ACCESS_WRITEONLY
;
464 openmode
|= OPEN_FLAGS_SEQUENTIAL
| OPEN_SHARE_DENYREADWRITE
;
468 0ul, /* initial size */
470 OPEN_ACTION_FAIL_IF_NEW
| OPEN_ACTION_OPEN_IF_EXISTS
,
476 errno
= EPIPE
; /* guess */
480 /* Now do the final checking, the server end must be connected */
481 if (DosConnectNPipe(in
) != NO_ERROR
)
485 errno
= EPIPE
; /* guess */
489 /* We always want to have the server's end of the named pipe: */
490 if (ep
->flags
.isinput
)
492 ep
->hdls
[0] = (int) out
;
493 ep
->hdls
[1] = (int) in
;
497 ep
->hdls
[0] = (int) in
;
498 ep
->hdls
[1] = (int) out
;
504 /* sets the given handle to the non-blocking mode. The value may change.
505 * async_info CAN be used to add the handle to the internal list of watched
508 static void Os2_unblock_handle( int *handle
, void *async_info
)
510 AsyncInfo
*ai
= async_info
;
517 for (i
= 0;i
< 3;i
++)
519 if ((ai
->hdl
[i
] != *handle
) && (ai
->hdl
[i
] == -1))
521 ai
->hdl
[i
] = *handle
;
522 rc
= DosSetNPipeSem((HFILE
) *handle
, (HSEM
) ai
->sem
, 0);
525 ai
->hdl
[i
] = *handle
= -2;
526 /* This shall produce an error. -1 isn't a good idea: special
534 /* restart_file sets the file pointer of the given handle to the beginning.
536 static void Os2_restart_file(int hdl
)
538 lseek(hdl
, 0l, SEEK_SET
); /* unused! */
541 /* close acts like close() but closes a handle returned by
542 * open_subprocess_connection.
543 * async_info MAY be used to delete the handle from the internal list of
546 static int Os2_close(int handle
, void *async_info
)
548 AsyncInfo
*ai
= async_info
;
551 if ((handle
!= -1) && (ai
!= NULL
))
552 for (i
= 0;i
< 3;i
++)
553 if ((int) ai
->hdl
[i
] == handle
)
554 DosSetNPipeSem((HPIPE
) handle
, NULLHANDLE
, 0);
555 return(DosClose((HFILE
) handle
));
559 * close_special acts like close() but closes any OS specific handle.
560 * The close happens if the handle is not -1. A specific operation may be
561 * associated with this. Have a look for occurances of "hdls[2]".
563 static void Os2_close_special( int handle
)
565 assert( handle
== -1 );
568 /* read acts like read() but returns either an error (return code
569 * == -errno) or the number of bytes read. EINTR and friends leads to a
571 * use_blocked_io is a flag. If set, the handle is set to blocked IO and
572 * we shall use blocked IO here.
574 static int Os2_read(int hdl
, void *buf
, unsigned size
, void *async_info
)
580 rc
= DosRead((HFILE
) hdl
, buf
, size
, &done
);
581 } while (rc
== ERROR_INTERRUPT
);
585 if (rc
== ERROR_NO_DATA
)
587 if (rc
== ERROR_BROKEN_PIPE
)
589 return(-EINVAL
); /* good assumption */
595 /* write acts like write() but returns either an error (return code
596 * == -errno) or the number of bytes written. EINTR and friends leads to a
598 * use_blocked_io is a flag. If set, the handle is set to blocked IO and
599 * we shall use blocked IO here.
600 * The file must be flushed if buf or size are 0.
602 static int Os2_write(int hdl
, const void *buf
, unsigned size
, void *async_info
)
607 if ((buf
== NULL
) || (size
== 0)) /* nothing to do for flushing buffers */
611 rc
= DosWrite((HFILE
) hdl
, buf
, size
, &done
);
612 } while (rc
== ERROR_INTERRUPT
);
616 if (rc
== ERROR_NO_DATA
)
618 if ((rc
== ERROR_BROKEN_PIPE
) || (rc
== ERROR_DISCARDED
))
620 if (rc
== ERROR_INVALID_HANDLE
)
622 return(-ENOSPC
); /* good assumption */
623 } else if (done
== 0)
629 /* create_async_info return an opaque structure to allow the process wait for
630 * asyncronous IO. There are three IO slots (in, out, error) which can be
631 * filled by add_waiter. The structure can be cleaned by reset_async_info.
632 * The structure must be destroyed by delete_async_info.
634 static void *Os2_create_async_info(const tsd_t
*TSD
)
638 ai
= MallocTSD(sizeof(AsyncInfo
));
640 ai
->sem
= NULLHANDLE
;
641 ai
->hdl
[0] = ai
->hdl
[1] = ai
->hdl
[2] = (HFILE
) -1;
643 DosCreateEventSem(NULL
, &ai
->sem
, DC_SEM_SHARED
, FALSE
);
647 /* delete_async_info deletes the structure created by create_async_info and
648 * all of its components.
650 static void Os2_delete_async_info(void *async_info
)
652 AsyncInfo
*ai
= async_info
;
656 DosCloseEventSem(ai
->sem
);
657 Free_TSD(ai
->TSD
, ai
);
660 /* reset_async_info clear async_info in such a way that fresh add_waiter()
661 * calls can be performed.
663 static void Os2_reset_async_info(void *async_info
)
665 AsyncInfo
*ai
= async_info
;
668 DosResetEventSem(ai
->sem
, &ul
);
672 /* add_async_waiter adds a further handle to the asyncronous IO structure.
673 * add_as_read_handle must be != 0 if the next operation shall be a
674 * Os2_read, else it must be 0 for Os2_write.
675 * Call reset_async_info before a wait-cycle to different handles and use
676 * wait_async_info to wait for at least one IO-able handle.
678 static void Os2_add_async_waiter(void *async_info
, int handle
, int add_as_read_handle
)
680 AsyncInfo
*ai
= async_info
;
684 for (i
= 0;i
< 3;i
++)
685 if ((int) ai
->hdl
[i
] == handle
)
689 /* wait_async_info waits for some handles to become ready. This function
690 * returns if at least one handle becomes ready.
691 * A handle can be added with add_async_waiter to the bundle of handles to
693 * No special handling is implemented if an asyncronous interrupt occurs.
694 * Thus, there is no guarantee to have handle which works.
696 static void Os2_wait_async_info(void *async_info
)
698 AsyncInfo
*ai
= async_info
;
701 DosWaitEventSem(ai
->sem
, SEM_INDEFINITE_WAIT
);
704 static int Os2_uname(struct regina_utsname
*name
)
706 strcpy( name
->sysname
, "OS2" );
707 sprintf( name
->version
, "%d" ,_osmajor
);
708 sprintf( name
->release
, "%d" ,_osminor
);
709 strcpy( name
->nodename
, "standalone" );
710 strcpy( name
->machine
, "i386" );
715 static void Os2_init(void);
717 OS_Dep_funcs __regina_OS_Os2
=
720 Os2_setenv
, /* setenv */
721 Os2_fork_exec
, /* fork_exec */
723 Os2_open_subprocess_connection
, /* open_subprocess_connection */
724 Os2_unblock_handle
, /* unblock_handle */
725 Os2_restart_file
, /* restart_file */
726 Os2_close
, /* close */
727 Os2_close_special
, /* close_special */
729 Os2_write
, /* write */
730 Os2_create_async_info
, /* create_async_info */
731 Os2_delete_async_info
, /* delete_async_info */
732 Os2_reset_async_info
, /* reset_async_info */
733 Os2_add_async_waiter
, /* add_async_waiter */
734 Os2_wait_async_info
, /* wait_async_info */
735 Os2_uname
/* uname */
739 static void Os2_init(void)
742 #define OS2 __regina_OS_Os2
743 #define DOS __regina_OS_Other
745 #ifdef __EMX__ /* redirect this call to the non-OS/2-code if DOS is running */
746 if (_osmode
!= OS2_MODE
)
748 extern OS_Dep_funcs __regina_OS_Other
;
750 OS2
.fork_exec
= DOS
.fork_exec
;
752 OS2
.open_subprocess_connection
= DOS
.open_subprocess_connection
;
753 OS2
.unblock_handle
= DOS
.unblock_handle
;
754 OS2
.restart_file
= DOS
.restart_file
;
755 OS2
.close
= DOS
.close
;
757 OS2
.write
= DOS
.write
;
758 OS2
.create_async_info
= DOS
.create_async_info
;
759 OS2
.delete_async_info
= DOS
.delete_async_info
;
760 OS2
.reset_async_info
= DOS
.reset_async_info
;
761 OS2
.add_async_waiter
= DOS
.add_async_waiter
;
762 OS2
.wait_async_info
= DOS
.wait_async_info
;