don't use NULL or cast NULL where appropriate.
[AROS-Contrib.git] / regina / os_other.c
blob5dbdd92653a19b7b234fda12b42bef1f9de5b2e2
1 /*
2 * The Regina Rexx Interpreter
3 * Copyright (C) 2005 Florian Grosse-Coosmann
5 * This file contains OS specific code not matching known systems or
6 * for "trivial" systems. Even Win32 systems use this code in order
7 * to get this graphical DOS extension called Win9x running.
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
19 * You should have received a copy of the GNU Library General Public
20 * License along with this library; if not, write to the Free
21 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 #if defined(__EMX__)
26 # define INCL_BASE
27 # include <os2.h>
28 # define DONT_TYPEDEF_PFN
29 #endif
31 #include "rexx.h"
32 #include "utsname.h"
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
38 #if defined(DOS) /* MH 10-06-96 */
39 # ifdef _POSIX_SOURCE
40 # undef _POSIX_SOURCE
41 # endif
42 # include <dos.h>
43 #endif /* MH 10-06-96 */
45 #include <errno.h>
46 #include <assert.h>
48 #ifdef HAVE_UNISTD_H
49 # include <unistd.h>
50 #else
51 # include <io.h>
52 #endif
54 #if defined(HAVE_SYS_STAT_H)
55 # include <sys/stat.h>
56 #endif
58 #if defined(HAVE_FCNTL_H)
59 # include <fcntl.h>
60 #endif
62 #if defined(HAVE_SYS_FCNTL_H)
63 # include <sys/fcntl.h>
64 #endif
66 #if defined(MAC) || (defined(__WATCOMC__) && !defined(__QNX__)) || defined(_MSC_VER) || defined(__SASC) || defined(__MINGW32__) || defined(__BORLANDC__) || defined(__EPOC32__) || defined(__WINS__) || defined(__LCC__) || defined(SKYOS)
68 * MAC OSX hopefully uses the posix branch.
70 # define NEED_UNAME
71 #else /* MH 10-06-96 */
72 # if defined(WIN32) && defined(__IBMC__) /* LM 26-02-99 */
73 # include "utsname.h"
74 # define NEED_UNAME
75 # include <io.h>
76 # else
77 # ifndef VMS
78 # include <sys/param.h> /* MH 10-06-96 */
79 # endif
80 # include <sys/utsname.h> /* MH 10-06-96 */
81 # endif
82 #endif
84 #if defined(MAC) || defined(GO32) || defined (__EMX__) || (defined(__WATCOMC__) && !defined(__QNX__)) || defined(_MSC_VER) || defined(DJGPP) || defined(__CYGWIN32__) || defined(__BORLANDC__) || defined(__MINGW32__) || defined(__WINS__) || defined(__EPOC32__) ||defined(__LCC__)
85 # if defined(__EMX__) || defined(__CYGWIN32__)
86 # define ISTR_SLASH "/" /* This is not a must, \\ works, too */
87 # define I_SLASH '/' /* This is not a must, \\ works, too */
88 # elif defined(MAC)
89 # define ISTR_SLASH ":"
90 # define I_SLASH ':'
91 # else
92 # define ISTR_SLASH "\\" /* This is not a must, / works at least for MSC, too */
93 # define I_SLASH '\\' /* This is not a must, / works at least for MSC, too */
94 # endif
95 # if !defined(MAC) && !defined(__WINS__) && !defined(__EPOC32__) && !defined(__CYGWIN__)
96 # ifndef HAVE_UNISTD_H
97 # include <io.h> /* access() */
98 # endif
99 # include <process.h>
100 # include <share.h>
101 # endif
102 # include <time.h>
103 #endif
106 * This flavour is the default style. It doesn't use pipes. It uses temporary
107 * files instead.
108 * Slow, but shall work with all machines.
111 static int Oth_setenv( const char *name, const char *value )
113 char *cmd;
115 if (value == NULL)
116 value = "";
117 cmd = malloc( strlen(name) + strlen(value) + 2);
118 sprintf( cmd, "%s=%s", name, value );
119 putenv( cmd );
120 free( cmd );
121 return 1;
125 /* fork_exec spawns a new process with the given commandline.
126 * it returns -1 on error (errno set), 0 on process start error (rcode set),
127 * a process descriptor otherwise.
128 * Basically this is a child process and we run in the child's environment
129 * after the first few lines. The setup hasn't been done and the command needs
130 * to be started.
131 * Redirection takes place if one of the handles env->input.hdls[0],
132 * env->output.hdls[1] or env->error.hdls[1] is defined. Other handles (except
133 * standard handles) are closed. env->subtype must be a SUBENVIR_... constant.
134 * cmdline is the whole command line.
135 * Never use TSD after the fork() since this is not only a different thread,
136 * it's a different process!
138 static int Oth_fork_exec(tsd_t *TSD, environment *env, const char *cmdline, int *rcode)
140 static const char *interpreter[] = { "regina", /* preferable even if not */
141 /* dynamic */
142 "rexx" };
143 char **args = NULL;
144 int saved_in = -1, saved_out = -1, saved_err = -1;
145 int rc, eno ;
146 int broken_address_command = get_options_flag( TSD->currlevel, EXT_BROKEN_ADDRESS_COMMAND );
147 int subtype;
149 if (env->subtype == SUBENVIR_REXX) /*special situation caused by recursion*/
151 environment e = *env;
152 char *new_cmdline;
153 int i, rc;
154 unsigned len;
156 if (argv0 == NULL)
157 len = 7; /* max("rexx", "regina") */
158 else
160 len = strlen(argv0);
161 if (len < 7)
162 len = 7; /* max("rexx", "regina") */
164 len += strlen(cmdline) + 2; /* Blank + term ASCII0 */
166 if ((new_cmdline = malloc(len)) == NULL)
167 return -1; /* ENOMEM is set */
169 if (argv0 != NULL) /* always the best choice */
171 strcpy(new_cmdline, argv0);
172 strcat(new_cmdline, " ");
173 strcat(new_cmdline, cmdline);
174 e.subtype = SUBENVIR_COMMAND;
175 rc = Oth_fork_exec(TSD, &e, new_cmdline, &rc);
176 if ( ( rc != 0 ) && ( rc != -1 ) )
178 free(new_cmdline);
179 return(rc);
183 /* load an interpreter by name from the path */
184 for (i = 0; i < sizeof(interpreter) / sizeof(interpreter[0]);i++)
186 strcpy(new_cmdline, interpreter[i]);
187 strcat(new_cmdline, " ");
188 strcat(new_cmdline, cmdline);
189 e.subtype = SUBENVIR_SYSTEM;
190 rc = Oth_fork_exec(TSD, &e, new_cmdline, &rc);
191 if ( ( rc != 0 ) && ( rc != -1 ) )
193 free(new_cmdline);
194 return(rc);
198 *rcode = -errno; /* assume a load error */
199 free(new_cmdline);
200 return 0;
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;
233 rc = -1;
235 switch (env->subtype)
237 case SUBENVIR_PATH:
238 #ifdef P_WAIT
239 # if defined(DOS) && !defined(__EMX__)
240 args = makesimpleargs(cmdline);
241 # else
242 args = makeargs(cmdline, '\\');
243 # endif
244 rc = spawnvp(P_WAIT, *args, args);
245 #else
246 goto USE_SUBENVIR_SYSTEM;
247 #endif
248 break;
250 case SUBENVIR_COMMAND:
251 #ifdef P_WAIT
252 # if defined(DOS) && !defined(__EMX__)
253 args = makesimpleargs(cmdline);
254 # else
255 args = makeargs(cmdline, '\\');
256 # endif
257 rc = spawnv(P_WAIT, *args, args);
258 #else
259 goto USE_SUBENVIR_SYSTEM;
260 #endif
261 break;
263 case SUBENVIR_SYSTEM:
264 #ifndef P_WAIT
265 USE_SUBENVIR_SYSTEM:
266 #endif
267 rc = system(cmdline);
268 break;
270 case SUBENVIR_REXX:
271 /* fall through */
273 default: /* illegal subtype */
274 STD_RESTORE(saved_in, 0);
275 STD_RESTORE(saved_out, 1);
276 STD_RESTORE(saved_err, 2);
277 if (args != NULL)
278 destroyargs(args);
279 errno = EINVAL;
280 rc = -1;
281 break;
284 eno = errno;
285 STD_RESTORE(saved_in, 0);
286 STD_RESTORE(saved_out, 1);
287 STD_RESTORE(saved_err, 2);
288 if (args != NULL)
289 destroyargs(args);
290 errno = eno;
292 if ( rc == -1 )
293 return(0);
295 rc -= 0x4000; /* do a remap */
296 if ( (rc == -1) || ( rc == 0 ) )
297 rc = -0x4000 + 127;
298 return rc;
299 #undef SET_MAXHDLS
300 #undef SET_MAXHDL
301 #undef STD_RESTORE
302 #undef STD_REDIR
305 /* wait waits for a process started by fork_exec.
306 * In general, this is called after the complete IO to the called process but
307 * it isn't guaranteed. Never call if you don't expect a sudden death of the
308 * subprocess.
309 * Returns the exit status of the subprocess under normal circumstances. If
310 * the subprocess has died by a signal, the return value is -signalnumber.
312 static int Oth_wait(int process)
314 return(process + 0x4000);
318 * local_mkstemp() works mostly like the commands
319 * strcpy(base, system_specific_temppath), mkstemp( TSD, base);
320 * mkstemp opens a newly created file and returns its name in "base".
321 * The handle of the file is the function return value. The function
322 * returns -1 if an error occurs. Use the errno value in this case.
324 * The handle is at least suitable for fork_exec(), __regina_read(),
325 * __regina_write() and __regina_close().
326 * base should have REXX_PATH_MAX characters.
327 * Be careful: Don't forget to delete the file afterwards.
329 static int local_mkstemp(const tsd_t *TSD, char *base)
331 #ifndef S_IRWXU
332 # define S_IRWXU (_S_IREAD|_S_IWRITE)
333 #endif
334 #ifdef HAVE_MKSTEMP
335 /* We are using a unix system. We either have mkstemp or you
336 * should enable the above code.
338 strcpy(base, "/tmp/rxXXXXXX");
339 return(mkstemp(base));
340 #else
341 #define MAGIC_MAX 2000000 /* Much beyond maximum, see below */
342 static volatile unsigned BaseIndex = MAGIC_MAX;
343 int retval;
344 char *slash;
345 char buf[REXX_PATH_MAX]; /* enough space for largest path name */
346 unsigned i;
347 unsigned start,run;
349 if ( mygetenv( TSD, "TMP", buf, sizeof(buf) ) == NULL)
351 if ( mygetenv( TSD, "TEMP", buf, sizeof(buf) ) == NULL)
353 if ( mygetenv( TSD, "TMPDIR", buf, sizeof(buf) ) == NULL)
355 #ifdef UNIX
356 strcpy(buf,"/tmp");
357 #else
358 strcpy(buf,"C:");
359 #endif
364 if (strlen(buf) > REXX_PATH_MAX - 14 /* 8.3 + "\0" + ISLASH */)
365 buf[REXX_PATH_MAX - 14] = '\0';
367 if ( buf[strlen(buf)-1] != I_SLASH )
368 slash = ISTR_SLASH;
369 else
370 slash = "";
372 /* algorithm:
373 * select a random number, e.g. a mixture of pid, tid and time.
374 * increment this number by a fixed amount until we reach the starting
375 * value again. Do a wrap around and an increment which is a unique prime.
376 * The number 1000000 has the primes 2 and 5. We may use all primes
377 * except 2 and 5; 9901 (which is prime) will give a good distribution.
379 * We want to be able to create several temporary files at once without
380 * more OS calls than needed. Thus, we have a program wide runner to
381 * ensure a simple distribution without strength.
383 if (BaseIndex == MAGIC_MAX)
386 * We have to create (nearly) reentrant code.
388 i = (unsigned) getpid() * (unsigned) time(NULL);
389 i %= 1000000;
390 if (!i)
391 i = 1;
392 BaseIndex = i;
394 if (++BaseIndex >= 1000000)
395 BaseIndex = 1;
397 start = TSD->thread_id;
398 if (start == 0)
399 start = 999999;
400 start *= (unsigned) (clock() + 1);
401 start *= BaseIndex;
402 start %= 1000000;
404 strcat( buf, slash );
405 slash = buf + strlen( buf );
406 run = start;
407 for (i = 0;i <= 1000000;i++)
409 /* form a name like "c:\temp\345302._rx" or "/tmp/345302._rx" */
411 * fixes Bug 587687
413 sprintf( slash, "%06u._rx", run );
414 #if defined(_MSC_VER) && ( !defined(__EPOC32__) && !defined(__WINS__) ) /* currently not used but what's about CE ? */
415 retval = _sopen(buf,
416 _O_RDWR|_O_CREAT|_O_BINARY|_O_SHORT_LIVED|_O_EXCL|
417 _O_SEQUENTIAL,
418 SH_DENYRW,
419 S_IRWXU);
420 #else
421 retval = open(buf,
422 O_RDWR|O_CREAT|O_EXCL
423 # if defined(O_NOCTTY)
424 |O_NOCTTY
425 # endif
427 S_IRWXU);
428 #endif
429 if (retval != -1) /* success */
431 strcpy(base,buf);
432 return(retval);
434 /* Check for a true failure */
435 if (errno != EEXIST)
436 break;
438 /* Check the next possible candidate */
439 run += 9901;
440 run %= 1000000;
441 if (run == start) /* paranoia check. i <= 1000000 should hit exactly */
442 break; /* here */
444 return( -1 ); /* pro forma */
445 #undef MAGIC_MAX
446 #endif /* HAVE_MKSTEMP */
449 /* open_subprocess_connection acts like the unix-known function pipe and sets
450 * ep->RedirectedFile if necessary. Just in the latter case ep->data
451 * is set to the filename.
452 * Close the handles with __regina_close later.
453 * Do IO by using __regina_read() and __regina_write().
455 static int Oth_open_subprocess_connection(const tsd_t *TSD, environpart *ep)
457 char *name;
458 int eno;
460 name = MallocTSD(REXX_PATH_MAX + 1);
461 /* Remember to create two handles to be "pipe()"-conform. */
462 if ((ep->hdls[0] = local_mkstemp(TSD, name)) == -1)
464 eno = errno;
465 free( name );
466 errno = eno;
467 return(-1);
470 if ((ep->hdls[1] = dup(ep->hdls[0])) == -1)
472 eno = errno;
473 close(ep->hdls[0]);
474 ep->hdls[0] = -1;
475 unlink(name);
476 free(name);
477 errno = eno;
478 return(-1);
481 ep->FileRedirected = 1;
482 ep->tempname = name;
483 return(0);
486 /* sets the given handle to the non-blocking mode. The value may change.
487 * async_info CAN be used to add the handle to the internal list of watched
488 * handles.
490 static void Oth_unblock_handle( int *handle, void *async_info )
492 (handle = handle);
493 (async_info = async_info);
496 /* restart_file sets the file pointer of the given handle to the beginning.
498 static void Oth_restart_file(int hdl)
500 lseek(hdl, 0l, SEEK_SET);
503 /* close acts like close() but closes a handle returned by
504 * open_subprocess_connection.
505 * async_info MAY be used to delete the handle from the internal list of
506 * watched handles.
508 static int Oth_close(int handle, void *async_info)
510 (async_info = async_info);
511 return(close(handle));
515 * close_special acts like close() but closes any OS specific handle.
516 * The close happens if the handle is not -1. A specific operation may be
517 * associated with this. Have a look for occurances of "hdls[2]".
519 static void Oth_close_special( int handle )
521 assert( handle == -1 );
524 /* read acts like read() but returns either an error (return code
525 * == -errno) or the number of bytes read. EINTR and friends leads to a
526 * re-read.
527 * use_blocked_io is a flag. If set, the handle is set to blocked IO and
528 * we shall use blocked IO here.
530 static int Oth_read(int hdl, void *buf, unsigned size, void *async_info)
532 int done;
534 #ifdef EINTR
535 do {
536 done = read(hdl, buf, size);
537 } while ((done == -1) && (errno == EINTR));
538 #else
539 done = read(hdl, buf, size);
540 #endif
542 if (done < 0)
544 done = errno;
545 if (done == 0) /* no error set? */
546 done = EPIPE ; /* good assumption */
547 #if defined(EWOULDBLOCK) && defined(EAGAIN) && (EAGAIN != EWOULDBLOCK)
548 /* BSD knows this value with the same meaning as EAGAIN */
549 if (done == EWOULDBLOCK)
550 done = EAGAIN;
551 #endif
552 return(-done); /* error */
555 return(done);
558 /* write acts like write() but returns either an error (return code
559 * == -errno) or the number of bytes written. EINTR and friends leads to a
560 * re-write.
561 * use_blocked_io is a flag. If set, the handle is set to blocked IO and
562 * we shall use blocked IO here.
563 * The file must be flushed if buf or size are 0.
565 static int Oth_write(int hdl, const void *buf, unsigned size, void *async_info)
567 int done;
569 if ((buf == NULL) || (size == 0)) /* nothing to to for flushing buffers */
570 return(0);
572 #ifdef EINTR
573 do {
574 done = write( hdl, buf, size ) ;
575 } while ((done == -1) && (errno == EINTR));
576 #else
577 done = write( hdl, buf, size ) ;
578 #endif
580 if (done <= 0)
582 done = errno;
583 if (done == 0) /* no error set? */
584 done = ENOSPC; /* good assumption */
585 #if defined(EWOULDBLOCK) && defined(EAGAIN) && (EAGAIN != EWOULDBLOCK)
586 /* BSD knows this value with the same meaning as EAGAIN */
587 if (done == EWOULDBLOCK)
588 done = EAGAIN;
589 #endif
590 return(-done); /* error */
593 return(done);
596 /* create_async_info return an opaque structure to allow the process wait for
597 * asyncronous IO. There are three IO slots (in, out, error) which can be
598 * filled by add_waiter. The structure can be cleaned by reset_async_info.
599 * The structure must be destroyed by delete_async_info.
601 static void *Oth_create_async_info(const tsd_t *TSD)
603 (TSD = TSD);
604 return(NULL);
607 /* delete_async_info deletes the structure created by create_async_info and
608 * all of its components.
610 static void Oth_delete_async_info(void *async_info)
612 (async_info = async_info);
615 /* reset_async_info clear async_info in such a way that fresh add_waiter()
616 * calls can be performed.
618 static void Oth_reset_async_info(void *async_info)
620 (async_info = async_info);
623 /* add_async_waiter adds a further handle to the asyncronous IO structure.
624 * add_as_read_handle must be != 0 if the next operation shall be a
625 * __regina_read, else it must be 0 for __regina_write.
626 * Call reset_async_info before a wait-cycle to different handles and use
627 * wait_async_info to wait for at least one IO-able handle.
629 static void Oth_add_async_waiter(void *async_info, int handle, int add_as_read_handle)
631 (async_info = async_info);
632 (handle = handle);
633 (add_as_read_handle = add_as_read_handle);
636 /* wait_async_info waits for some handles to become ready. This function
637 * returns if at least one handle becomes ready.
638 * A handle can be added with add_async_waiter to the bundle of handles to
639 * wait for.
640 * No special handling is implemented if an asyncronous interrupt occurs.
641 * Thus, there is no guarantee to have handle which works.
643 static void Oth_wait_async_info(void *async_info)
645 (async_info = async_info);
648 int Oth_uname(struct regina_utsname *name)
651 * Set up values for utsname structure...
653 #if defined(_AMIGA)
654 strcpy( name->sysname, "AMIGA" );
655 strcpy( name->version, "UNKNOWN" );
656 strcpy( name->release, "UNKNOWN" );
657 strcpy( name->nodename, "standalone" );
658 strcpy( name->machine, "m68k" );
659 #elif defined(MAC)
660 strcpy( name->sysname, "MAC" );
661 strcpy( name->version, "UNKNOWN" );
662 strcpy( name->release, "UNKNOWN" );
663 strcpy( name->nodename, "standalone" );
664 strcpy( name->machine, "m68k" );
665 #else
666 strcpy( name->sysname, "UNKNOWN" );
667 strcpy( name->version, "UNKNOWN" );
668 strcpy( name->release, "UNKNOWN" );
669 strcpy( name->nodename, "UNKNOWN" );
670 strcpy( name->machine, "UNKNOWN" );
671 #endif
673 return 0;
676 static void Oth_init(void)
680 OS_Dep_funcs __regina_OS_Other =
682 Oth_init, /* init */
683 Oth_setenv, /* setenv */
684 Oth_fork_exec, /* fork_exec */
685 Oth_wait, /* wait */
686 Oth_open_subprocess_connection, /* open_subprocess_connection */
687 Oth_unblock_handle, /* unblock_handle */
688 Oth_restart_file, /* restart_file */
689 Oth_close, /* close */
690 Oth_close_special, /* close_special */
691 Oth_read, /* read */
692 Oth_write, /* write */
693 Oth_create_async_info, /* create_async_info */
694 Oth_delete_async_info, /* delete_async_info */
695 Oth_reset_async_info, /* reset_async_info */
696 Oth_add_async_waiter, /* add_async_waiter */
697 Oth_wait_async_info, /* wait_async_info */
698 Oth_uname /* uname */