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.
28 # define DONT_TYPEDEF_PFN
38 #if defined(DOS) /* MH 10-06-96 */
43 #endif /* MH 10-06-96 */
54 #if defined(HAVE_SYS_STAT_H)
55 # include <sys/stat.h>
58 #if defined(HAVE_FCNTL_H)
62 #if defined(HAVE_SYS_FCNTL_H)
63 # include <sys/fcntl.h>
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.
71 #else /* MH 10-06-96 */
72 # if defined(WIN32) && defined(__IBMC__) /* LM 26-02-99 */
78 # include <sys/param.h> /* MH 10-06-96 */
80 # include <sys/utsname.h> /* MH 10-06-96 */
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 */
89 # define ISTR_SLASH ":"
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 */
95 # if !defined(MAC) && !defined(__WINS__) && !defined(__EPOC32__) && !defined(__CYGWIN__)
96 # ifndef HAVE_UNISTD_H
97 # include <io.h> /* access() */
106 * This flavour is the default style. It doesn't use pipes. It uses temporary
108 * Slow, but shall work with all machines.
111 static int Oth_setenv( const char *name
, const char *value
)
117 cmd
= malloc( strlen(name
) + strlen(value
) + 2);
118 sprintf( cmd
, "%s=%s", name
, value
);
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
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 */
144 int saved_in
= -1, saved_out
= -1, saved_err
= -1;
146 int broken_address_command
= get_options_flag( TSD
->currlevel
, EXT_BROKEN_ADDRESS_COMMAND
);
149 if (env
->subtype
== SUBENVIR_REXX
) /*special situation caused by recursion*/
151 environment e
= *env
;
157 len
= 7; /* max("rexx", "regina") */
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 ) )
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 ) )
198 *rcode
= -errno
; /* assume a load error */
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
;
235 switch (env
->subtype
)
239 # if defined(DOS) && !defined(__EMX__)
240 args
= makesimpleargs(cmdline
);
242 args
= makeargs(cmdline
, '\\');
244 rc
= spawnvp(P_WAIT
, *args
, args
);
246 goto USE_SUBENVIR_SYSTEM
;
250 case SUBENVIR_COMMAND
:
252 # if defined(DOS) && !defined(__EMX__)
253 args
= makesimpleargs(cmdline
);
255 args
= makeargs(cmdline
, '\\');
257 rc
= spawnv(P_WAIT
, *args
, args
);
259 goto USE_SUBENVIR_SYSTEM
;
263 case SUBENVIR_SYSTEM
:
267 rc
= system(cmdline
);
273 default: /* illegal subtype */
274 STD_RESTORE(saved_in
, 0);
275 STD_RESTORE(saved_out
, 1);
276 STD_RESTORE(saved_err
, 2);
285 STD_RESTORE(saved_in
, 0);
286 STD_RESTORE(saved_out
, 1);
287 STD_RESTORE(saved_err
, 2);
295 rc
-= 0x4000; /* do a remap */
296 if ( (rc
== -1) || ( rc
== 0 ) )
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
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
)
332 # define S_IRWXU (_S_IREAD|_S_IWRITE)
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
));
341 #define MAGIC_MAX 2000000 /* Much beyond maximum, see below */
342 static volatile unsigned BaseIndex
= MAGIC_MAX
;
345 char buf
[REXX_PATH_MAX
]; /* enough space for largest path name */
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
)
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
)
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
);
394 if (++BaseIndex
>= 1000000)
397 start
= TSD
->thread_id
;
400 start
*= (unsigned) (clock() + 1);
404 strcat( buf
, slash
);
405 slash
= buf
+ strlen( buf
);
407 for (i
= 0;i
<= 1000000;i
++)
409 /* form a name like "c:\temp\345302._rx" or "/tmp/345302._rx" */
413 sprintf( slash
, "%06u._rx", run
);
414 #if defined(_MSC_VER) && ( !defined(__EPOC32__) && !defined(__WINS__) ) /* currently not used but what's about CE ? */
416 _O_RDWR
|_O_CREAT
|_O_BINARY
|_O_SHORT_LIVED
|_O_EXCL
|
422 O_RDWR
|O_CREAT
|O_EXCL
423 # if defined(O_NOCTTY)
429 if (retval
!= -1) /* success */
434 /* Check for a true failure */
438 /* Check the next possible candidate */
441 if (run
== start
) /* paranoia check. i <= 1000000 should hit exactly */
444 return( -1 ); /* pro forma */
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
)
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)
470 if ((ep
->hdls
[1] = dup(ep
->hdls
[0])) == -1)
481 ep
->FileRedirected
= 1;
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
490 static void Oth_unblock_handle( int *handle
, void *async_info
)
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
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
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
)
536 done
= read(hdl
, buf
, size
);
537 } while ((done
== -1) && (errno
== EINTR
));
539 done
= read(hdl
, buf
, size
);
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
)
552 return(-done
); /* error */
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
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
)
569 if ((buf
== NULL
) || (size
== 0)) /* nothing to to for flushing buffers */
574 done
= write( hdl
, buf
, size
) ;
575 } while ((done
== -1) && (errno
== EINTR
));
577 done
= write( hdl
, buf
, size
) ;
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
)
590 return(-done
); /* error */
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
)
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
);
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
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...
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" );
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" );
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" );
676 static void Oth_init(void)
680 OS_Dep_funcs __regina_OS_Other
=
683 Oth_setenv
, /* setenv */
684 Oth_fork_exec
, /* fork_exec */
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 */
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 */