We can't link with bz2 anymore because it's now a shared
[AROS-Contrib.git] / regina / os_os2.c
blob6e663994031fc781cf5feb5443941affe25d98d7
1 /*
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.
22 #define INCL_BASE
23 #include <os2.h>
24 #define DONT_TYPEDEF_PFN
26 #include "rexx.h"
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
32 #if defined(DOS) /* MH 10-06-96 */
33 # ifdef _POSIX_SOURCE
34 # undef _POSIX_SOURCE
35 # endif
36 # include <dos.h>
37 #endif /* MH 10-06-96 */
39 #include <errno.h>
41 #if defined(HAVE_ASSERT_H)
42 # include <assert.h>
43 #endif
45 #if defined(HAVE_UNISTD_H)
46 # include <unistd.h>
47 #endif
49 #if defined(HAVE_SYS_STAT_H)
50 # include <sys/stat.h>
51 #endif
53 #if defined(HAVE_SYS_WAIT_H)
54 # include <sys/wait.h>
55 #endif
57 #if defined(HAVE_FCNTL_H)
58 # include <fcntl.h>
59 #endif
61 #if defined(HAVE_SYS_FCNTL_H)
62 # include <sys/fcntl.h>
63 #endif
65 #if defined(HAVE_PROCESS_H)
66 # include <process.h>
67 #endif
69 #include "utsname.h" /* MH 10-06-96 */
71 #if defined (__EMX__) || defined(__WATCOMC__)
72 # if defined(__EMX__)
73 # include <sys/wait.h>
74 # endif
75 #endif
77 #include <io.h>
79 static int Os2_setenv( const char *name, const char *value )
81 #if defined(HAVE_SETENV)
82 setenv( name, value, 1 );
83 return 1;
84 #else
85 char *cmd;
87 if (value == NULL)
88 value = "";
89 cmd = malloc( strlen(name) + strlen(value) + 2);
90 sprintf( cmd, "%s=%s", name, value );
91 putenv( cmd );
92 free( cmd );
93 return 1;
94 # endif
97 typedef struct {
98 const tsd_t *TSD;
99 HEV sem;
100 int mustwait;
101 int hdl[3];
102 } AsyncInfo;
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
109 * to be started.
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 */
125 /* not dynamic */
126 "rexx.exe" };
127 char **args = NULL;
128 int saved_in = -1, saved_out = -1, saved_err = -1;
129 int rc;
130 const char *ipret;
131 char *argline;
132 int broken_address_command = get_options_flag( TSD->currlevel, EXT_BROKEN_ADDRESS_COMMAND );
133 int subtype;
135 if (env->subtype == SUBENVIR_REXX) /*special situation caused by recursion*/
137 environment e = *env;
138 char *new_cmdline;
139 int i, rc;
140 unsigned len;
142 if (argv0 == NULL)
143 len = 11; /* max("rexx.exe", "regina.exe") */
144 else
146 len = strlen(argv0);
147 if (len < 11)
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 ) )
164 free(new_cmdline);
165 return(rc);
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 ) )
179 free(new_cmdline);
180 return(rc);
184 #ifndef __EMX__
185 *rcode = -errno; /* assume a load error */
186 free( new_cmdline );
187 return 0;
188 #else
189 if ( ( rc = fork() ) != 0 ) /* EMX is fork-capable */
190 return rc;
191 #endif
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));
201 #endif
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); \
207 close(saved); }
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)
216 saved_err = dup(2);
217 dup2(1, 2);
219 else
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;
231 else
232 subtype = env->subtype;
234 rc = -1;
235 switch ( subtype )
237 case SUBENVIR_PATH:
238 args = makeargs(cmdline, '^');
239 rc = spawnvp(P_NOWAIT, *args, args);
240 break;
242 case SUBENVIR_COMMAND:
243 args = makeargs(cmdline, '^');
244 rc = spawnv(P_NOWAIT, *args, args);
245 break;
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 ");
261 else
263 ipret = "CMD.EXE";
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);
271 break;
273 case SUBENVIR_REXX:
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);
278 char **run;
279 int i;
281 strcpy(new_cmdline, "\"\" ");
282 strcat(new_cmdline, cmdline);
283 args = makeargs(new_cmdline, '^');
285 for (i = 0, run = args; *run; run++)
286 i++;
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);
294 errno = EINVAL;
295 return -1;
298 *rcode = errno;
299 STD_RESTORE(saved_in, 0);
300 STD_RESTORE(saved_out, 1);
301 STD_RESTORE(saved_err, 2);
302 if (args != NULL)
303 destroyargs(args);
305 return ( rc == -1 ) ? 0 : rc;
306 #undef SET_MAXHDLS
307 #undef SET_MAXHDL
308 #undef STD_RESTORE
309 #undef STD_REDIR
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
315 * subprocess.
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;
323 #ifdef __WATCOMC__
325 * Watcom is strange. EINTR isn't an indicator for a retry of the call.
327 status = -1;
328 rc = cwait(&status, process, WAIT_CHILD);
329 if (rc == -1)
331 if ((status != -1) && (errno == EINTR))
332 retval = -status; /* Exception reason in lower byte */
333 else
334 retval = -errno; /* I don't have a better idea */
336 else
338 if (status & 0xFF)
339 retval = -status; /* Exception reason in lower byte */
340 else
341 retval = status >> 8;
343 #else
344 do {
345 rc = waitpid(process, &status, 0);
346 } while ((rc == -1) && (errno == EINTR));
348 if (WIFEXITED(status))
350 retval = (int) WEXITSTATUS(status);
351 if ( retval < 0 )
352 retval = -retval;
354 else if (WIFSIGNALED(status))
356 retval = -WTERMSIG(status);
357 if ( retval > 0 )
358 retval = -retval;
359 else if ( retval == 0 )
360 retval = -1;
362 else
364 retval = -WSTOPSIG(status);
365 if ( retval > 0 )
366 retval = -retval;
367 else if ( retval == 0 )
368 retval = -1;
370 #endif
372 return(retval);
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;
385 char buf[40];
386 unsigned i;
387 unsigned start,run;
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;
395 /* algorithm:
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);
412 i %= 1000000;
413 if (!i)
414 i = 1;
415 BaseIndex = i;
417 if (++BaseIndex >= 1000000)
418 BaseIndex = 1;
420 start = TSD->thread_id;
421 if (start == 0)
422 start = 999999;
423 start *= (unsigned) (clock() + 1);
424 start *= BaseIndex;
425 start %= 1000000;
427 run = start;
428 for (i = 0;i <= 1000000;i++)
430 sprintf(buf,"%s%06u._rx", "\\pipe\\tmp\\", run );
431 rc = DosCreateNPipe(buf,
432 &in,
433 openmode,
434 NP_TYPE_BYTE | NP_READMODE_BYTE | NP_NOWAIT | 1,
435 4096,
436 4096,
437 0); /* msec timeout */
438 if (rc == NO_ERROR)
439 break;
441 if (rc != ERROR_PIPE_BUSY)
443 errno = EPIPE;
444 return(-1);
447 /* Check the next possible candidate */
448 run += 9901;
449 run %= 1000000;
450 if (run == start) /* paranoia check. i <= 1000000 should hit exactly */
451 break; /* here */
454 if (in == (HPIPE) -1)
456 errno = EPIPE;
457 return(-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;
465 rc = DosOpen(buf,
466 &out,
467 &dummy, /* action */
468 0ul, /* initial size */
469 FILE_NORMAL,
470 OPEN_ACTION_FAIL_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS,
471 openmode,
472 NULL); /* peaop2 */
473 if (rc != NO_ERROR)
475 DosClose(in);
476 errno = EPIPE; /* guess */
477 return(-1);
480 /* Now do the final checking, the server end must be connected */
481 if (DosConnectNPipe(in) != NO_ERROR)
483 DosClose(out);
484 DosClose(in);
485 errno = EPIPE; /* guess */
486 return(-1);
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;
495 else
497 ep->hdls[0] = (int) in;
498 ep->hdls[1] = (int) out;
500 return(0);
501 #undef MAGIC_MAX
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
506 * handles.
508 static void Os2_unblock_handle( int *handle, void *async_info )
510 AsyncInfo *ai = async_info;
511 int i;
512 ULONG rc;
514 if (*handle == -1)
515 return ;
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);
524 if (rc != NO_ERROR)
525 ai->hdl[i] = *handle = -2;
526 /* This shall produce an error. -1 isn't a good idea: special
527 * meaning
529 return;
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
544 * watched handles.
546 static int Os2_close(int handle, void *async_info)
548 AsyncInfo *ai = async_info;
549 int i;
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
570 * re-read.
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)
576 ULONG rc;
577 ULONG done;
579 do {
580 rc = DosRead((HFILE) hdl, buf, size, &done);
581 } while (rc == ERROR_INTERRUPT);
583 if (rc != 0)
585 if (rc == ERROR_NO_DATA)
586 return(-EAGAIN);
587 if (rc == ERROR_BROKEN_PIPE)
588 return(-EPIPE);
589 return(-EINVAL); /* good assumption */
592 return(done);
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
597 * re-write.
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)
604 ULONG rc;
605 ULONG done;
607 if ((buf == NULL) || (size == 0)) /* nothing to do for flushing buffers */
608 return(0);
610 do {
611 rc = DosWrite((HFILE) hdl, buf, size, &done);
612 } while (rc == ERROR_INTERRUPT);
614 if (rc != 0)
616 if (rc == ERROR_NO_DATA)
617 return(-EAGAIN);
618 if ((rc == ERROR_BROKEN_PIPE) || (rc == ERROR_DISCARDED))
619 return(-EPIPE);
620 if (rc == ERROR_INVALID_HANDLE)
621 return(-EINVAL);
622 return(-ENOSPC); /* good assumption */
623 } else if (done == 0)
624 return(-EAGAIN);
626 return((int) done);
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)
636 AsyncInfo *ai;
638 ai = MallocTSD(sizeof(AsyncInfo));
639 ai->TSD = TSD;
640 ai->sem = NULLHANDLE;
641 ai->hdl[0] = ai->hdl[1] = ai->hdl[2] = (HFILE) -1;
642 ai->mustwait = 0;
643 DosCreateEventSem(NULL, &ai->sem, DC_SEM_SHARED, FALSE);
644 return(ai);
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;
654 if (ai == NULL)
655 return;
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;
666 ULONG ul;
668 DosResetEventSem(ai->sem, &ul);
669 ai->mustwait = 0;
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;
681 int i;
683 if (handle != -1)
684 for (i = 0;i < 3;i++)
685 if ((int) ai->hdl[i] == handle)
686 ai->mustwait = 1;
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
692 * wait for.
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;
700 if (ai->mustwait)
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" );
712 return 0;
715 static void Os2_init(void);
717 OS_Dep_funcs __regina_OS_Os2 =
719 Os2_init, /* init */
720 Os2_setenv, /* setenv */
721 Os2_fork_exec, /* fork_exec */
722 Os2_wait, /* wait */
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 */
728 Os2_read, /* read */
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)
741 #undef OS2
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;
751 OS2.wait = DOS.wait;
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;
756 OS2.read = DOS.read;
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;
764 #endif