Unix.st
author Claus Gittinger <cg@exept.de>
Thu, 25 Apr 1996 18:02:18 +0200
changeset 1286 4270a0b4917d
parent 1280 b7ae6227f48b
child 1323 8e695d1a9e93
permissions -rw-r--r--
documentation

"
 COPYRIGHT (c) 1988 by Claus Gittinger
	      All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"

Object subclass:#OperatingSystem
	instanceVariableNames:''
	classVariableNames:'HostName DomainName LastErrorNumber LastExecStatus OSSignals
		SlowFork ForkFailed ErrorSignal AccessDeniedErrorSignal
		FileNotFoundErrorSignal LocaleInfo'
	poolDictionaries:''
	category:'System-Support'
!

!OperatingSystem primitiveDefinitions!
%{
#define UNIX_LIKE

#if defined(MSWINDOWS) || defined(OS2) || defined(MSDOS) || defined(WIN32)
# ifndef MSDOS_LIKE
#  define MSDOS_LIKE
# endif
# undef UNIX_LIKE
# ifdef i386
#  define _X86_
# endif
#endif

#if defined(transputer)
# undef MSDOS_LIKE
# undef UNIX_LIKE
#endif

#if defined(_AIX)
# ifndef WANT_REALPATH
#  define WANT_REALPATH
# endif
# ifndef WANT_SYSTEM
#  define WANT_SYSTEM
# endif
#endif

#ifdef LINUX
  /* use inline string macros */
# define __STRINGDEFS__
# include <linuxIntern.h>

# ifndef WANT_SYSTEM
#  define WANT_SYSTEM
# endif
# define WANT_SHM
#endif

#ifdef IRIX5
# define WANT_SYSTEM
#endif

#ifdef ultrix
# define WANT_SYSTEM
#endif

#ifdef hpux
# define WANT_SYSTEM
#endif

#if defined(SYSV4) && defined(i386) /* e.g. unixware */
# define WANT_SYSTEM
#endif

/*
 * notice: although many systems' include files
 * already block against multiple inclusion, some
 * do not. Therefore, this is done here again.
 * (it does not hurt)
 */ 

#ifdef WANT_REALPATH

# include <sys/param.h>
# define _SYS_PARAM_H_INCLUDED_

# include <errno.h>
# define _ERRNO_H_INCLUDED_

# include <sys/stat.h>
# define _SYS_STAT_H_INCLUDED_

#endif /* WANT_REALPATH */



#ifdef WANT_SHM
extern int shmctl(), shmget(), shmdt();
extern char * shmat();

# include <sys/types.h>
# define _SYS_TYPES_H_INCLUDED_

# include <sys/ipc.h>
# define _SYS_IPC_H_INCLUDED_

# include <sys/shm.h>
# define _SYS_SHM_H_INCLUDED_

#endif /* WANT_SHM */


#ifdef IRIX5
# include <sys/syssgi.h>
#endif


#ifdef transputer

# define unlink(f)      ((remove(f) == 0) ? 0 : -1)

#else /* not transputer */

# ifndef _SIGNAL_H_INCLUDED_
#  include <signal.h>
#  define _SIGNAL_H_INCLUDED_
# endif

# ifdef SYSV
#  ifndef _SYS_TYPES_H_INCLUDED_
#   include <sys/types.h>
#   define _SYS_TYPES_H_INCLUDED_
#  endif
#  ifndef _SYS_PARAM_H_INCLUDED_
#   include <sys/param.h>
#   define _SYS_PARAM_H_INCLUDED_
#  endif
#  ifndef _SYS_TIMES_H_INCLUDED_
#   include <sys/times.h>
#   define _SYS_TIMES_H_INCLUDED_
#  endif
#  ifndef _SYS_FILE_H_INCLUDED_
#   include <sys/file.h>
#   define _SYS_FILE_H_INCLUDED_
#  endif

#  if ! defined(sco3_2)
#   ifndef _UNISTD_H_INCLUDED_
#    include <unistd.h>
#    define _UNISTD_H_INCLUDED_
#   endif
#  endif

#  if defined(isc3_2) || defined(sco3_2)
#   ifndef _SYS_TIME_H_INCLUDED_
#    include <sys/time.h>
#    define _SYS_TIME_H_INCLUDED_
#   endif
#  endif

#  if !defined(isc3_2)
#   if defined(PCS) && defined(mips)
#    include "/usr/include/bsd/sys/time.h"
#    include "/usr/include/sys/time.h"
#   else
#    ifndef _TIME_H_INCLUDED_
#     include <time.h>
#     define _TIME_H_INCLUDED_
#    endif
#   endif
#  endif

#  if defined(isc3_2)
#   include <sys/bsdtypes.h>
#  endif

# else /* not SYSV */

#  ifdef MSDOS_LIKE

#   ifndef _SYS_TYPES_H_INCLUDED_
#    include <sys/types.h>
#    define _SYS_TYPES_H_INCLUDED_
#   endif
#   ifndef _TIME_H_INCLUDED_
#    include <time.h>
#    define _TIME_H_INCLUDED_
#   endif
#   ifndef _SYS_TIMEB_H_INCLUDED_
#    include <sys/timeb.h>
#    define _SYS_TIMEB_H_INCLUDED_
#   endif

#  else /* not MSDOS_like */

#   ifndef _SYS_TIME_H_INCLUDED_
#    include <sys/time.h>
#    define _SYS_TIME_H_INCLUDED_
#   endif
#   ifndef _SYS_TYPES_H_INCLUDED_
#    include <sys/types.h>
#    define _SYS_TYPES_H_INCLUDED_
#   endif

#  endif /* not MSDOS */
# endif /* not SYSV */

# ifdef aix

#  ifndef _TIME_H_INCLUDED_
#   include <time.h>
#   define _TIME_H_INCLUDED_
#  endif
#  ifndef _SYS_SELECT_H_INCLUDED_
#   include <sys/select.h>
#   define _SYS_SELECT_H_INCLUDED_
#  endif

# endif /* aix */

# ifndef MSDOS_LIKE
#  include <pwd.h>
#  include <grp.h>
# endif

# ifndef _SYS_STAT_H_INCLUDED_
#  include <sys/stat.h>
# endif

# ifndef _ERRNO_H_INCLUDED_
#  include <errno.h>
#  define _ERRNO_H_INCLUDED_
# endif

# ifndef _STDIO_H_INCLUDED_
#  include <stdio.h>
#  define _STDIO_H_INCLUDED_
# endif

# ifndef _FCNTL_H_INCLUDED_
#  include <fcntl.h>
#  define _FCNTL_H_INCLUDED_
# endif

# ifdef WIN32
#   undef INT
#   undef Array
#   undef Number
#   undef Method
#   undef Point

/* #  include <windows.h> /* */
#  include <stdarg.h> /* */
#  include <windef.h> /* */
#  include <winbase.h> /* */
#  include <wingdi.h> /* */
#  include <winuser.h> /* */

#   ifdef __DEF_Array
#    define Array __DEF_Array
#   endif
#   ifdef __DEF_Number
#    define Number __DEF_Number
#   endif
#   ifdef __DEF_Method
#    define Method __DEF_Method
#   endif
#   ifdef __DEF_Point
#    define Point __DEF_Point
#   endif
# endif


# if defined(LINUX)
#  define HAS_LOCALECONV
# endif

# if defined (HAS_LOCALECONV)
#  ifndef _LOCALE_H_INCLUDED_
#   include <locale.h>
#   define _LOCALE_H_INCLUDED_
#  endif
# endif

/* 
 * posix systems should define these ... 
 * but on some (older) systems, they are not.
 */
# ifndef S_IXUSR
#  ifdef S_IEXEC
#   define S_IXUSR S_IEXEC
#   define S_IXGRP (S_IEXEC>>3)
#   define S_IXOTH (S_IEXEC>>6)
#  endif
# endif

# ifndef S_IXUSR
#  define S_IXUSR 0100
#  define S_IXGRP 0010
#  define S_IXOTH 0001
# endif

# ifndef S_IRUSR
#  define S_IRUSR 0400
#  define S_IRGRP 0040
#  define S_IROTH 0004
# endif

# ifndef S_IWUSR
#  define S_IWUSR 0200
#  define S_IWGRP 0020
#  define S_IWOTH 0002
# endif

# ifndef MAXPATHLEN
#  ifndef MSDOS_LIKE
#   include <sys/param.h>
#  endif
#  ifndef MAXPATHLEN
#   ifdef FILENAME_MAX  /* i.e. MSDOS_LIKE */
#    define MAXPATHLEN FILENAME_MAX
#   else
#    define MAXPATHLEN 1024
#   endif
#  endif
# endif

# if defined(HAS_UNAME)
#  include <sys/utsname.h>
# endif

# if defined(SYSV4)
#  include <stropts.h>
# endif

/*
 * sigaction dummies (you won't believe these call themself ``POSIX'' systems ...)
 */
# ifndef SA_RESTART
#  define SA_RESTART    0
# endif
# ifndef SA_SIGINFO
#  define SA_SIGINFO    0
# endif

# if defined(HAS_WAITPID) || defined(HAS_WAIT3)
#  include <sys/wait.h>
# endif

#endif /* ! transputer */

/*
 * on some systems errno is a macro ... check for it here
 */
#ifndef errno
 extern errno;
#endif

/*
 * some (old ?) systems do not define this ...
 */
#if !defined(R_OK) && !defined(_AIX)
# define R_OK    4       /* Test for Read permission */ 
# define W_OK    2       /* Test for Write permission */
# define X_OK    1       /* Test for eXecute permission */
# define F_OK    0       /* Test for existence of File */
#endif

#ifdef WIN32
# define SIGHANDLER_ARG int
#else
# define SIGHANDLER_ARG
#endif

%}
! !

!OperatingSystem primitiveFunctions!
%{

/*
 * some systems' system() is broken in that it does not correctly 
 * handle EINTR and returns failure even though it actually succeeded. 
 * (LINUX is one of them)
 * Here is a fixed version. If you encounter EINTR returns from
 * OperatingSystem>>executeCommand, you ought to define WANT_SYSTEM
 * in the xxxIntern.h file to get this fixed version.
 *
 * As an added BONUS, this system() enables interrupts while waiting
 * for the child which enables other threads to continue.
 * (i.e. it is RT safe)
 */

#if defined(WANT_SYSTEM)

/* # define DPRINTF(x)     printf x */
# define DPRINTF(x)     /* nothing */

# ifndef _STDDEF_H_INCLUDED_
#  include <stddef.h>
#  define _STDDEF_H_INCLUDED_
# endif

# ifndef _STDLIB_H_INCLUDED_
#  include <stdlib.h>
#  define _STDLIB_H_INCLUDED_
# endif

# ifndef _UNISTD_H_INCLUDED_
#  include <unistd.h>
#  define _UNISTD_H_INCLUDED_
# endif

# ifndef _SYS_WAIT_H_INCLUDED
#  include <sys/wait.h>
#  define _SYS_WAIT_H_INCLUDED
# endif

# ifndef _SIGNAL_H_INCLUDED_
#  include <signal.h>
#  define _SIGNAL_H_INCLUDED_
# endif

# ifndef _SYS_TYPES_H_INCLUDED_
#  include <sys/types.h>
#  define _SYS_TYPES_H_INCLUDED_
# endif

# if (!defined(HAVE_GNU_LD) && !defined (__ELF__)) || !defined(LINUX)
#  define       __environ       environ
#  if !defined(LINUX)
#   define      __sigemptyset   sigemptyset
#   define      __sigaction     sigaction
#   define      __sigaddset     sigaddset
#   define      __sigprocmask   sigprocmask
#   define      __execve        execve
#   define      __wait          wait
#   define      __waitpid       waitpid
#   if defined(HAS_VFORK)
#    define     FORK            vfork
#   else
#    define     FORK            fork
#   endif
#  endif /* ! LINUX */
   extern char **environ;
# endif

# define        SHELL_PATH      "/bin/sh"       /* Path of the shell.  */
# define        SHELL_NAME      "sh"            /* Name to give it.  */

# ifndef        FORK
#  define       FORK    __fork
# endif

# ifndef CONST
#  ifdef __GNUC__
#   define CONST const
#  else
#   define CONST /* nothing */
#  endif
# endif

static int
mySystem(line)
    register CONST char *line;
{
    int status, save;
    pid_t pid;
    struct sigaction sa, intr, quit;
    sigset_t block, omask;

    if (line == NULL)
	return -1;

    sa.sa_handler = SIG_IGN;
    sa.sa_flags = 0;
    __sigemptyset (&sa.sa_mask);

    if (__sigaction (SIGINT, &sa, &intr) < 0) {
	DPRINTF(("1: errno=%d\n", errno));
	return -1;
    }
    if (__sigaction (SIGQUIT, &sa, &quit) < 0) {
	save = errno;
	(void) __sigaction (SIGINT, &intr, (struct sigaction *) NULL);
	errno = save;
	DPRINTF(("2: errno=%d\n", errno));
	return -1;
    }

    __sigemptyset (&block);
    __sigaddset (&block, SIGCHLD);
    save = errno;
    if (__sigprocmask(SIG_BLOCK, &block, &omask) < 0) {
	if (errno == ENOSYS)
	    errno = save;
	else {
	    save = errno;
	    (void) __sigaction(SIGINT, &intr, (struct sigaction *) NULL);
	    (void) __sigaction(SIGQUIT, &quit, (struct sigaction *) NULL);
	    errno = save;
	    DPRINTF(("3: errno=%d\n", errno));
	    return -1;
	}
    }

    pid = FORK ();
    if (pid == (pid_t) 0) {
	/* Child side.  */
	CONST char *new_argv[4];
	new_argv[0] = SHELL_NAME;
	new_argv[1] = "-c";
	new_argv[2] = line;
	new_argv[3] = NULL;

	/* Restore the signals.  */
	(void) __sigaction (SIGINT, &intr, (struct sigaction *) NULL);
	(void) __sigaction (SIGQUIT, &quit, (struct sigaction *) NULL);
	(void) __sigprocmask (SIG_SETMASK, &omask, (sigset_t *) NULL);

	/* Exec the shell.  */
	(void) __execve (SHELL_PATH, (char *CONST *) new_argv, __environ);
	_exit (127);
    } else {
	if (pid < (pid_t) 0) {
	    /* The fork failed.  */
	    DPRINTF(("4: errno=%d\n", errno));
	    status = -1;
	} else {
	    /* Parent side.  */
#ifdef  NO_WAITPID
	    pid_t child;

	    do {
		__BEGIN_INTERRUPTABLE__
		child = __wait (&status);
		__END_INTERRUPTABLE__
		if (child < 0 && errno != EINTR) {
		    DPRINTF(("5: errno=%d\n", errno));
		    status = -1;
		    break;
		}
	    } while (child != pid);
#else
	    pid_t child;

	    /* claus: the original did not care for EINTR here ... */
	    do {
		__BEGIN_INTERRUPTABLE__
		child = __waitpid (pid, &status, 0);
		__END_INTERRUPTABLE__
	    } while ((child != pid) && (errno == EINTR));
	    if (child != pid) {
		DPRINTF(("6: errno=%d\n", errno));
		status = -1;
	    }
#endif /* NO_WAITPID */
	}
    }
    save = errno;
    if ((__sigaction (SIGINT, &intr, (struct sigaction *) NULL)
     | __sigaction (SIGQUIT, &quit, (struct sigaction *) NULL)
     | __sigprocmask (SIG_SETMASK, &omask, (sigset_t *) NULL)) != 0) {
	if (errno == ENOSYS) {
	    errno = save;
	} else {
	    status = -1;
	    DPRINTF(("7: errno=%d\n", errno));
	}
    }

    return status;
}
#endif /* WANT_SYSTEM */


/*
 * some systems do not have realpath();
 * the alternative of reading from a 'pwp'-pipe
 * is way too slow. Here is a realpath for the rest of us.
 * define WANT_REALPATH in the xxxIntern-file to get it.
 */

#if defined(HAS_REALPATH)
# undef WANT_REALPATH
#endif
#if !defined(HAS_GETWD) && !defined(HAS_GETCWD)
# undef WANT_REALPATH
#endif

#if defined(WANT_REALPATH)

# ifndef NULL
#  define NULL (char *)0
# endif

# define MAX_READLINKS 32

# ifndef MAXPATHLEN
#  define MAXPATHLEN     1024
# endif

static
char *realpath(path, resolved_path)
char *path;
char resolved_path [];
{
	char copy_path[MAXPATHLEN];
	char link_path[MAXPATHLEN];
	char *new_path = resolved_path;
	char *max_path;
	int readlinks = 0;
	int n;

	/* Make a copy of the source path since we may need to modify it. */
	strcpy(copy_path, path);
	path = copy_path;
	max_path = copy_path + MAXPATHLEN - 2;
	/* If it's a relative pathname use getwd for starters. */
	if (*path != '/') {
#ifdef HAS_GETCWD
		getcwd(new_path, MAXPATHLEN - 1);
#else
		getwd(new_path);
#endif
		new_path += strlen(new_path);
		if (new_path[-1] != '/')
			*new_path++ = '/';
	}
	else {
		*new_path++ = '/';
		path++;
	}
	/* Expand each slash-separated pathname component. */
	while (*path != '\0') {
		/* Ignore stray "/". */
		if (*path == '/') {
			path++;
			continue;
		}
		if (*path == '.') {
			/* Ignore ".". */
			if (path[1] == '\0' || path[1] == '/') {
				path++;
				continue;
			}
			if (path[1] == '.') {
				if (path[2] == '\0' || path[2] == '/') {
					path += 2;
					/* Ignore ".." at root. */
					if (new_path == resolved_path + 1)
						continue;
					/* Handle ".." by backing up. */
					while ((--new_path)[-1] != '/')
						;
					continue;
				}
			}
		}
		/* Safely copy the next pathname component. */
		while (*path != '\0' && *path != '/') {
			if (path > max_path) {
				errno = ENAMETOOLONG;
				return NULL;
			}
			*new_path++ = *path++;
		}
#ifdef S_IFLNK
		/* Protect against infinite loops. */
		if (readlinks++ > MAX_READLINKS) {
			errno = ELOOP;
			return NULL;
		}
		/* See if latest pathname component is a symlink. */
		*new_path = '\0';
		n = readlink(resolved_path, link_path, MAXPATHLEN - 1);
		if (n < 0) {
			/* EINVAL means the file exists but isn't a symlink. */
			if (errno != EINVAL)
				return NULL;
		}
		else {
			/* Note: readlink doesn't add the null byte. */
			link_path[n] = '\0';
			if (*link_path == '/')
				/* Start over for an absolute symlink. */
				new_path = resolved_path;
			else
				/* Otherwise back up over this component. */
				while (*(--new_path) != '/')
					;
			/* Safe sex check. */
			if (strlen(path) + n >= MAXPATHLEN) {
				errno = ENAMETOOLONG;
				return NULL;
			}
			/* Insert symlink contents into path. */
			strcat(link_path, path);
			strcpy(copy_path, link_path);
			path = copy_path;
		}
#endif /* S_IFLNK */
		*new_path++ = '/';
	}
	/* Delete trailing slash but don't whomp a lone slash. */
	if (new_path != resolved_path + 1 && new_path[-1] == '/')
		new_path--;
	/* Make sure it's null terminated. */
	*new_path = '\0';
	return resolved_path;
}
# define HAS_REALPATH
#endif /* WANT_REALPATH && not HAS_REALPATH */
%}
! !

!OperatingSystem class methodsFor:'documentation'!

copyright
"
 COPYRIGHT (c) 1988 by Claus Gittinger
	      All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"
!

documentation
"
    this class realizes access to most (all ?) required operating system services;
    some of it is very specific for unix, so do not depend on
    things available here in your applications - some may not
    be found in other OS's or be slightly different ...

    (On the other hand: I do not want to hide all features
     from you - in some situations it MAY be interesting to be
     able to get down to a select or fork system call easily (at least on Unix systems).
     You decide - portability vs. functionality)

    Currently, there is only this class available - once others than
    Unix are supported, the corresponding class will be bound to the
    global variable OperatingSystem.

    [Class variables:]

        HostName        <String>        remembered hostname

        DomainName      <String>        remembered domainname

        LastErrorNumber <Integer>       the last value of errno

        LastExecStatus  <Integer>       the returned exec status after
                                        the last call of system

        OSSignals       <Array>         Array of signals to be raised for corresponding
                                        OperatingSystem signals.

        ForkFailed      <Boolean>       set if a fork (or popen) has failed;
                                        ST/X will avoid doing more forks/popens
                                        if this flag is set, for a slightly
                                        smoother operation.

        SlowFork        <Boolean>       if set, fork and popen are avoided;
                                        (more or less obsolete now)


        ErrorSignal     <Signal>        Parentsignal of all OS error signals.
                                        not directly raised.

        AccessDeniedErrorSignal

        FileNotFoundErrorSignal

        LocaleInfo      <Dictionary>    if non nil, that is taken instead of the operating
                                        systems locale definitions (allows for overwriting
                                        these, or provide a compatible info on systems which do
                                        not support locales)

    [author:]
        Claus Gittinger

    [see also:]
        Filename Date Time
        ExternalStream FileStream PipeStream Socket
"
! !

!OperatingSystem class methodsFor:'initialization'!

initialize
    "initialize the class"

    ObjectMemory addDependent:self.

    ErrorSignal isNil ifTrue:[
	ErrorSignal := Object errorSignal newSignalMayProceed:true..
	ErrorSignal nameClass:self message:#errorSignal.
	ErrorSignal notifierString:'OS error encountered'.

	AccessDeniedErrorSignal := ErrorSignal newSignalMayProceed:true.
	AccessDeniedErrorSignal nameClass:self message:#accessDeniedError.
	AccessDeniedErrorSignal notifierString:'OS access denied'.

	FileNotFoundErrorSignal := ErrorSignal newSignalMayProceed:true.
	FileNotFoundErrorSignal nameClass:self message:#fileNotFoundErrorSignal.
	FileNotFoundErrorSignal notifierString:'OS file not found'.
    ]
!

update:something
    "catch image restart and flush some cached data"

    something == #earlyRestart ifTrue:[
        "
         flush cached data
        "
        HostName := nil.
        DomainName := nil.
        LastErrorNumber := nil.
        LastExecStatus := nil
    ]

    "Modified: 22.4.1996 / 13:10:43 / cg"
! !

!OperatingSystem class methodsFor:'OS signal constants'!

sigABRT
    "return the signal number for SIGABRT - 0 if not supported by OS
     (the numeric value is not the same across unix-systems)"

%{  /* NOCONTEXT */
#ifdef SIGABRT
    RETURN ( __MKSMALLINT(SIGABRT) );
#else
    RETURN ( __MKSMALLINT(0) );
#endif
%}
!

sigALRM
    "return the signal number for SIGALRM - 0 if not supported
     (the numeric value is not the same across unix-systems)"

%{  /* NOCONTEXT */
#ifdef SIGALRM
    RETURN ( __MKSMALLINT(SIGALRM) );
#else
    RETURN ( __MKSMALLINT(0) );
#endif
%}
!

sigBREAK
    "return the signal number for SIGBREAK - 0 if not supported.
     This is an MSDOS specific signal"

%{  /* NOCONTEXT */
#ifdef SIGBREAK
    RETURN ( __MKSMALLINT(SIGBREAK) );
#else
    RETURN ( __MKSMALLINT(0) );
#endif
%}
!

sigBUS
    "return the signal number for SIGBUS - 0 if not supported
     (the numeric value is not the same across unix-systems)"

%{  /* NOCONTEXT */
#ifdef SIGBUS
    RETURN ( __MKSMALLINT(SIGBUS) );
#else
    RETURN ( __MKSMALLINT(0) );
#endif
%}
!

sigCHLD
    "return the signal number for SIGCHLD - 0 if not supported
     (the numeric value is not the same across unix-systems)"

%{  /* NOCONTEXT */
#if defined(SIGCHLD)
    RETURN ( __MKSMALLINT(SIGCHLD) );
#else
# if  defined(SIGCLD)
    RETURN ( __MKSMALLINT(SIGCLD) );
# else
    RETURN ( __MKSMALLINT(0) );
# endif
#endif
%}
!

sigCONT
    "return the signal number for SIGCONT - 0 if not supported
     (the numeric value is not the same across unix-systems)"

%{  /* NOCONTEXT */
#if defined(SIGCONT)
    RETURN ( __MKSMALLINT(SIGCONT) );
#else
    RETURN ( __MKSMALLINT(0) );
#endif
%}
!

sigDANGER
    "return the signal number for SIGDANGER - 0 if not supported
     (seems to be an AIX special)"

%{  /* NOCONTEXT */
#if defined(SIGDANGER)
    RETURN ( __MKSMALLINT(SIGDANGER) );
#else
    RETURN ( __MKSMALLINT(0) );
#endif
%}
!

sigEMT
    "return the signal number for SIGEMT - 0 if not supported by OS
     (the numeric value is not the same across unix-systems)"

%{  /* NOCONTEXT */
#ifdef SIGEMT
    RETURN ( __MKSMALLINT(SIGEMT) );
#else
    RETURN ( __MKSMALLINT(0) );
#endif
%}
!

sigFP
    "return the signal number for SIGFP - 0 if not supported by OS
     (the numeric value is not the same across unix-systems)"

%{  /* NOCONTEXT */
#ifdef SIGFPE
    RETURN ( __MKSMALLINT(SIGFPE) );
#else
    RETURN ( __MKSMALLINT(0) );
#endif
%}
!

sigGRANT
    "return the signal number for SIGGRANT - 0 if not supported
     (seems to be an AIX special)"

%{  /* NOCONTEXT */
#if defined(SIGGRANT)
    RETURN ( __MKSMALLINT(SIGGRANT) );
#else
    RETURN ( __MKSMALLINT(0) );
#endif
%}
!

sigHUP
    "return the signal number for SIGHUP
     (the numeric value is not the same across unix-systems)"

%{  /* NOCONTEXT */
#ifdef SIGHUP
    RETURN ( __MKSMALLINT(SIGHUP) );
#else
    RETURN ( __MKSMALLINT(0) );
#endif
%}
!

sigILL
    "return the signal number for SIGILL - 0 if not supported by OS
     (the numeric value is not the same across unix-systems)"

%{  /* NOCONTEXT */
#ifdef SIGILL
    RETURN ( __MKSMALLINT(SIGILL) );
#else
    RETURN ( __MKSMALLINT(0) );
#endif
%}
!

sigINT
    "return the signal number for SIGINT
     (the numeric value is not the same across unix-systems)"

%{  /* NOCONTEXT */
#ifdef SIGINT
    RETURN ( __MKSMALLINT(SIGINT) );
#else
    RETURN ( __MKSMALLINT(0) );
#endif
%}
!

sigIO
    "return the signal number for SIGIO - 0 if not supported
     (the numeric value is not the same across unix-systems)"

%{  /* NOCONTEXT */
#if defined(SIGIO)
    RETURN ( __MKSMALLINT(SIGIO) );
#else
    RETURN ( __MKSMALLINT(0) );
#endif
%}
!

sigIOT
    "return the signal number for SIGIOT - 0 if not supported by OS
     (the numeric value is not the same across unix-systems)"

%{  /* NOCONTEXT */
#ifdef SIGIOT
    RETURN ( __MKSMALLINT(SIGIOT) );
#else
    RETURN ( __MKSMALLINT(0) );
#endif
%}
!

sigKILL
    "return the signal number for SIGKILL
     (the numeric value is not the same across unix-systems)"

%{  /* NOCONTEXT */
#ifdef SIGKILL
    RETURN ( __MKSMALLINT(SIGKILL) );
#else
    RETURN ( __MKSMALLINT(0) );
#endif
%}
!

sigLOST
    "return the signal number for SIGLOST - 0 if not supported
     (the numeric value is not the same across unix-systems)"

%{  /* NOCONTEXT */
#if defined(SIGLOST)
    RETURN ( __MKSMALLINT(SIGLOST) );
#else
    RETURN ( __MKSMALLINT(0) );
#endif
%}
!

sigMIGRATE
    "return the signal number for SIGMIGRATE - 0 if not supported
     (seems to be an AIX special)"

%{  /* NOCONTEXT */
#if defined(SIGMIGRATE)
    RETURN ( __MKSMALLINT(SIGMIGRATE) );
#else
    RETURN ( __MKSMALLINT(0) );
#endif
%}
!

sigMSG
    "return the signal number for SIGMSG - 0 if not supported
     (seems to be an AIX special)"

%{  /* NOCONTEXT */
#if defined(SIGMSG)
    RETURN ( __MKSMALLINT(SIGMSG) );
#else
    RETURN ( __MKSMALLINT(0) );
#endif
%}
!

sigPIPE
    "return the signal number for SIGPIPE - 0 if not supported
     (the numeric value is not the same across unix-systems)"

%{  /* NOCONTEXT */
#ifdef SIGPIPE
    RETURN ( __MKSMALLINT(SIGPIPE) );
#else
    RETURN ( __MKSMALLINT(0) );
#endif
%}
!

sigPOLL
    "return the signal number for SIGPOLL - 0 if not supported
     (the numeric value is not the same across unix-systems)"

%{  /* NOCONTEXT */
#if defined(SIGPOLL)
    RETURN ( __MKSMALLINT(SIGPOLL) );
#else
    RETURN ( __MKSMALLINT(0) );
#endif
%}
!

sigPRE
    "return the signal number for SIGPRE - 0 if not supported
     (seems to be an AIX special)"

%{  /* NOCONTEXT */
#if defined(SIGPRE)
    RETURN ( __MKSMALLINT(SIGPRE) );
#else
    RETURN ( __MKSMALLINT(0) );
#endif
%}
!

sigPROF
    "return the signal number for SIGPROF - 0 if not supported
     (the numeric value is not the same across unix-systems)"

%{  /* NOCONTEXT */
#if defined(SIGPROF)
    RETURN ( __MKSMALLINT(SIGPROF) );
#else
    RETURN ( __MKSMALLINT(0) );
#endif
%}
!

sigPWR
    "return the signal number for SIGPWR - 0 if not supported
     (not available on all systems)"

%{  /* NOCONTEXT */
#if defined(SIGPWR)
    RETURN ( __MKSMALLINT(SIGPWR) );
#else
    RETURN ( __MKSMALLINT(0) );
#endif
%}
!

sigQUIT
    "return the signal number for SIGQUIT
     (the numeric value is not the same across unix-systems)"

%{  /* NOCONTEXT */
#ifdef SIGQUIT
    RETURN ( __MKSMALLINT(SIGQUIT) );
#else
    RETURN ( __MKSMALLINT(0) );
#endif
%}
!

sigRETRACT
    "return the signal number for SIGRETRACT - 0 if not supported
     (seems to be an AIX special)"

%{  /* NOCONTEXT */
#if defined(SIGRETRACT)
    RETURN ( __MKSMALLINT(SIGRETRACT) );
#else
    RETURN ( __MKSMALLINT(0) );
#endif
%}
!

sigSAK
    "return the signal number for SIGSAK - 0 if not supported
     (seems to be an AIX special)"

%{  /* NOCONTEXT */
#if defined(SIGSAK)
    RETURN ( __MKSMALLINT(SIGSAK) );
#else
    RETURN ( __MKSMALLINT(0) );
#endif
%}
!

sigSEGV
    "return the signal number for SIGSEGV - 0 if not supported
     (the numeric value is not the same across unix-systems)"

%{  /* NOCONTEXT */
#ifdef SIGSEGV
    RETURN ( __MKSMALLINT(SIGSEGV) );
#else
    RETURN ( __MKSMALLINT(0) );
#endif
%}
!

sigSOUND
    "return the signal number for SIGSOUND - 0 if not supported
     (seems to be an AIX special)"

%{  /* NOCONTEXT */
#if defined(SIGSOUND)
    RETURN ( __MKSMALLINT(SIGSOUND) );
#else
    RETURN ( __MKSMALLINT(0) );
#endif
%}
!

sigSTOP
    "return the signal number for SIGSTOP - 0 if not supported
     (the numeric value is not the same across unix-systems)"

%{  /* NOCONTEXT */
#if defined(SIGSTOP)
    RETURN ( __MKSMALLINT(SIGSTOP) );
#else
    RETURN ( __MKSMALLINT(0) );
#endif
%}
!

sigSYS
    "return the signal number for SIGSYS - 0 if not supported
     (the numeric value is not the same across unix-systems)"

%{  /* NOCONTEXT */
#ifdef SIGSYS
    RETURN ( __MKSMALLINT(SIGSYS) );
#else
    RETURN ( __MKSMALLINT(0) );
#endif
%}
!

sigTERM
    "return the signal number for SIGTERM - 0 if not supported
     (the numeric value is not the same across unix-systems)"

%{  /* NOCONTEXT */
#ifdef SIGTERM
    RETURN ( __MKSMALLINT(SIGTERM) );
#else
    RETURN ( __MKSMALLINT(0) );
#endif
%}
!

sigTRAP
    "return the signal number for SIGTRAP - 0 if not supported by OS
     (the numeric value is not the same across unix-systems)"

%{  /* NOCONTEXT */
#ifdef SIGTRAP
    RETURN ( __MKSMALLINT(SIGTRAP) );
#else
    RETURN ( __MKSMALLINT(0) );
#endif
%}
!

sigTSTP
    "return the signal number for SIGTSTP - 0 if not supported
     (the numeric value is not the same across unix-systems)"

%{  /* NOCONTEXT */
#if defined(SIGTSTP)
    RETURN ( __MKSMALLINT(SIGTSTP) );
#else
    RETURN ( __MKSMALLINT(0) );
#endif
%}
!

sigTTIN
    "return the signal number for SIGTTIN - 0 if not supported
     (the numeric value is not the same across unix-systems)"

%{  /* NOCONTEXT */
#if defined(SIGTTIN)
    RETURN ( __MKSMALLINT(SIGTTIN) );
#else
    RETURN ( __MKSMALLINT(0) );
#endif
%}
!

sigTTOU
    "return the signal number for SIGTTOU - 0 if not supported
     (the numeric value is not the same across unix-systems)"

%{  /* NOCONTEXT */
#if defined(SIGTTOU)
    RETURN ( __MKSMALLINT(SIGTTOU) );
#else
    RETURN ( __MKSMALLINT(0) );
#endif
%}
!

sigURG
    "return the signal number for SIGURG - 0 if not supported
     (the numeric value is not the same across unix-systems)"

%{  /* NOCONTEXT */
#if defined(SIGURG)
    RETURN ( __MKSMALLINT(SIGURG) );
#else
    RETURN ( __MKSMALLINT(0) );
#endif
%}
!

sigUSR1
    "return the signal number for SIGUSR1 - 0 if not supported
     (the numeric value is not the same across unix-systems)"

%{  /* NOCONTEXT */
#if defined(SIGUSR1)
    RETURN ( __MKSMALLINT(SIGUSR1) );
#else
    RETURN ( __MKSMALLINT(0) );
#endif
%}
!

sigUSR2
    "return the signal number for SIGUSR2 - 0 if not supported
     (the numeric value is not the same across unix-systems)"

%{  /* NOCONTEXT */
#if defined(SIGUSR2)
    RETURN ( __MKSMALLINT(SIGUSR2) );
#else
    RETURN ( __MKSMALLINT(0) );
#endif
%}
!

sigVTALRM
    "return the signal number for SIGVTALRM - 0 if not supported
     (the numeric value is not the same across unix-systems)"

%{  /* NOCONTEXT */
#if defined(SIGVTALRM)
    RETURN ( __MKSMALLINT(SIGVTALRM) );
#else
    RETURN ( __MKSMALLINT(0) );
#endif
%}
!

sigWINCH
    "return the signal number for SIGWINCH - 0 if not supported
     (the numeric value is not the same across unix-systems)"

%{  /* NOCONTEXT */
#if defined(SIGWINCH)
    RETURN ( __MKSMALLINT(SIGWINCH) );
#else
    RETURN ( __MKSMALLINT(0) );
#endif
%}
!

sigXCPU
    "return the signal number for SIGXCPU - 0 if not supported
     (the numeric value is not the same across unix-systems)"

%{  /* NOCONTEXT */
#if defined(SIGXCPU)
    RETURN ( __MKSMALLINT(SIGXCPU) );
#else
    RETURN ( __MKSMALLINT(0) );
#endif
%}
!

sigXFSZ
    "return the signal number for SIGXFSZ - 0 if not supported
     (the numeric value is not the same across unix-systems)"

%{  /* NOCONTEXT */
#if defined(SIGXFSZ)
    RETURN ( __MKSMALLINT(SIGXFSZ) );
#else
    RETURN ( __MKSMALLINT(0) );
#endif
%}
! !

!OperatingSystem class methodsFor:'Signal constants'!

accessDeniedErrorSignal
    "return the signal raised when a (file-) access is denied."

    ^ AccessDeniedErrorSignal
!

errorSignal
    "return the parent signal of all OS signals."

    ^ ErrorSignal

    "Modified: 22.4.1996 / 13:11:31 / cg"
!

fileNotFoundErrorSignal
    "return the signal raised when a file was not found."

    ^ FileNotFoundErrorSignal
! !

!OperatingSystem class methodsFor:'error messages'!

clearLastErrorNumber
    "return the last errors number.
     See also: #lastErrorSymbol and #lastErrorString.
     Notice: having a single error number is a bad idea in a multithreaded
             environment - this interface will change."

    LastErrorNumber := nil.

     "
      OperatingSystem clearLastErrorNumber
     "

    "Created: 12.4.1996 / 09:28:58 / stefan"
    "Modified: 12.4.1996 / 09:38:51 / stefan"
!

currentErrorNumber
    "returns the OS's last error nr (i.e. the value of errno).
     Notice, that the value of this flag is only valid immediately
     after the error occurred - it gets updated with every other
     request to the OS.
     Use lastErrorNumber - currentErrorNumber is invalidated by
     many, many internal calls."

%{  /* NOCONTEXT */

     RETURN ( __MKSMALLINT(errno) );
%}
     "
      OperatingSystem currentErrorNumber
     "
!

errorNumberFor:aSymbol
    "given a symbolic error, return the numeric;
     (i.e. errorNumberFor:#EBADF returns EBADF's value).
     Use this, since error numbers are really not standard across unix systems."

%{   /* NOCONTEXT */
    OBJ sym = aSymbol;

    /*
     * POSIX errnos - these should be defined
     */
#ifdef EPERM
    if (sym == @symbol(EPERM)) {
	RETURN ( __MKSMALLINT(EPERM) );
    }
#endif

#ifdef ENOENT
    if (sym == @symbol(ENOENT)) {
	RETURN ( __MKSMALLINT(ENOENT) );
    }
#endif

#ifdef ESRCH
    if (sym == @symbol(ESRCH)) {
	RETURN ( __MKSMALLINT(ESRCH) );
    }
#endif

#ifdef EINTR
    if (sym == @symbol(EINTR)) {
	RETURN ( __MKSMALLINT(EINTR) );
    }
#endif

#ifdef EIO
    if (sym == @symbol(EIO)) {
	RETURN ( __MKSMALLINT(EIO) );
    }
#endif

#ifdef ENXIO
    if (sym == @symbol(ENXIO)) {
	RETURN ( __MKSMALLINT(ENXIO) );
    }
#endif

#ifdef E2BIG
    if (sym == @symbol(E2BIG)) {
	RETURN ( __MKSMALLINT(E2BIG) );
    }
#endif

#ifdef ENOEXEC
    if (sym == @symbol(ENOEXEC)) {
	RETURN ( __MKSMALLINT(ENOEXEC) );
    }
#endif

#ifdef EBADF
    if (sym == @symbol(EBADF)) {
	RETURN ( __MKSMALLINT(EBADF) );
    }
#endif

#ifdef ECHILD
    if (sym == @symbol(ECHILD)) {
	RETURN ( __MKSMALLINT(ECHILD) );
    }
#endif

#if defined(EAGAIN)
    if (sym == @symbol(EAGAIN)) {
	RETURN ( __MKSMALLINT(EAGAIN) );
    }
#endif

#ifdef ENOMEM
    if (sym == @symbol(ENOMEM)) {
	RETURN ( __MKSMALLINT(ENOMEM) );
    }
#endif

#ifdef EACCES
    if (sym == @symbol(EACCES)) {
	RETURN ( __MKSMALLINT(EACCES) );
    }
#endif

#ifdef EFAULT
    if (sym == @symbol(EFAULT)) {
	RETURN ( __MKSMALLINT(EFAULT) );
    }
#endif

#ifdef EBUSY
    if (sym == @symbol(EBUSY)) {
	RETURN ( __MKSMALLINT(EBUSY) );
    }
#endif

#ifdef EXDEV
    if (sym == @symbol(EXDEV)) {
	RETURN ( __MKSMALLINT(EXDEV) );
    }
#endif

#ifdef ENODEV
    if (sym == @symbol(ENODEV)) {
	RETURN ( __MKSMALLINT(ENODEV) );
    }
#endif

#ifdef ENOTDIR
    if (sym == @symbol(ENOTDIR)) {
	RETURN ( __MKSMALLINT(ENOTDIR) );
    }
#endif

#ifdef EISDIR
    if (sym == @symbol(EISDIR)) {
	RETURN ( __MKSMALLINT(EISDIR) );
    }
#endif

#ifdef EINVAL
    if (sym == @symbol(EINVAL)) {
	RETURN ( __MKSMALLINT(EINVAL) );
    }
#endif

#ifdef ENFILE
    if (sym == @symbol(ENFILE)) {
	RETURN ( __MKSMALLINT(ENFILE) );
    }
#endif

#ifdef EMFILE
    if (sym == @symbol(EMFILE)) {
	RETURN ( __MKSMALLINT(EMFILE) );
    }
#endif

#ifdef ENOTTY
    if (sym == @symbol(ENOTTY)) {
	RETURN ( __MKSMALLINT(ENOTTY) );
    }
#endif

#ifdef EFBIG
    if (sym == @symbol(EFBIG)) {
	RETURN ( __MKSMALLINT(EFBIG) );
    }
#endif

#ifdef ENOSPC
    if (sym == @symbol(ENOSPC)) {
	RETURN ( __MKSMALLINT(ENOSPC) );
    }
#endif

#ifdef ESPIPE
    if (sym == @symbol(ESPIPE)) {
	RETURN ( __MKSMALLINT(ESPIPE) );
    }
#endif

#ifdef EROFS
    if (sym == @symbol(EROFS)) {
	RETURN ( __MKSMALLINT(EROFS) );
    }
#endif

#ifdef EMLINK
    if (sym == @symbol(EMLINK)) {
	RETURN ( __MKSMALLINT(EMLINK) );
    }
#endif

#ifdef EPIPE
    if (sym == @symbol(EPIPE)) {
	RETURN ( __MKSMALLINT(EPIPE) );
    }
#endif

#ifdef EDOM
    if (sym == @symbol(EDOM)) {
	RETURN ( __MKSMALLINT(EDOM) );
    }
#endif

#ifdef ERANGE
    if (sym == @symbol(ERANGE)) {
	RETURN ( __MKSMALLINT(ERANGE) );
    }
#endif

#ifdef EDEADLK
    if (sym == @symbol(EDEADLK)) {
	RETURN ( __MKSMALLINT(EDEADLK) );
    }
#endif

#ifdef ENAMETOOLONG
    if (sym == @symbol(ENAMETOOLONG)) {
	RETURN ( __MKSMALLINT(ENAMETOOLONG) );
    }
#endif

#ifdef ENOLCK
    if (sym == @symbol(ENOLCK)) {
	RETURN ( __MKSMALLINT(ENOLCK) );
    }
#endif

#ifdef ENOSYS
    if (sym == @symbol(ENOSYS)) {
	RETURN ( __MKSMALLINT(ENOSYS) );
    }
#endif

#ifdef ENOTEMPTY
    if (sym == @symbol(ENOTEMPTY)) {
	RETURN ( __MKSMALLINT(ENOTEMPTY) );
    }
#endif

#ifdef EEXIST
    if (sym == @symbol(EEXIST)) {
	RETURN ( __MKSMALLINT(EEXIST) );
    }
#endif

#ifdef EILSEQ
    if (sym == @symbol(EILSEQ)) {
	RETURN ( __MKSMALLINT(EILSEQ) );
    }
#endif

    /*
     * XPG3 errnos - defined on most systems
     */
#ifdef ENOTBLK
    if (sym == @symbol(ENOTBLK)) {
	RETURN ( __MKSMALLINT(ENOTBLK) );
    }
#endif

#ifdef ETXTBSY
    if (sym == @symbol(ETXTBSY)) {
	RETURN ( __MKSMALLINT(ETXTBSY) );
    }
#endif

    /*
     * some others
     */
#ifdef EWOULDBLOCK
    if (sym == @symbol(EWOULDBLOCK)) {
	RETURN ( __MKSMALLINT(EWOULDBLOCK) );
    }
#endif

#ifdef ENOMSG
    if (sym == @symbol(ENOMSG)) {
	RETURN ( __MKSMALLINT(ENOMSG) );
    }
#endif

#ifdef ELOOP
    if (sym == @symbol(ELOOP)) {
	RETURN ( __MKSMALLINT(ELOOP) );
    }
#endif

    /*
     * some stream errors
     */
#ifdef ETIME
    if (sym == @symbol(ETIME)) {
	RETURN ( __MKSMALLINT(ETIME) );
    }
#endif

#ifdef ENOSR
    if (sym == @symbol(ENOSR)) {
	RETURN ( __MKSMALLINT(ENOSR) );
    }
#endif

#ifdef ENOSTR
    if (sym == @symbol(ENOSTR)) {
	RETURN ( __MKSMALLINT(ENOSTR) );
    }
#endif

#ifdef ECOMM
    if (sym == @symbol(ECOMM)) {
	RETURN ( __MKSMALLINT(ECOMM) );
    }
#endif

#ifdef EPROTO
    if (sym == @symbol(EPROTO)) {
	RETURN ( __MKSMALLINT(EPROTO) );
    }
#endif

    /*
     * nfs errors
     */
#ifdef ESTALE
    if (sym == @symbol(ESTALE)) {
	RETURN ( __MKSMALLINT(ESTALE) );
    }
#endif

#ifdef EREMOTE
    if (sym == @symbol(EREMOTE)) {
	RETURN ( __MKSMALLINT(EREMOTE) );
    }
#endif

    /*
     * some networking errors
     */
#ifdef EINPROGRESS
    if (sym == @symbol(EINPROGRESS)) {
	RETURN ( __MKSMALLINT(EINPROGRESS) );
    }
#endif

#ifdef EALREADY
    if (sym == @symbol(EALREADY)) {
	RETURN ( __MKSMALLINT(EALREADY) );
    }
#endif

#ifdef ENOTSOCK
    if (sym == @symbol(ENOTSOCK)) {
	RETURN ( __MKSMALLINT(ENOTSOCK) );
    }
#endif

#ifdef EDESTADDRREQ
    if (sym == @symbol(EDESTADDRREQ)) {
	RETURN ( __MKSMALLINT(EDESTADDRREQ) );
    }
#endif

#ifdef EMSGSIZE
    if (sym == @symbol(EMSGSIZE)) {
	RETURN ( __MKSMALLINT(EMSGSIZE) );
    }
#endif

#ifdef EPROTOTYPE
    if (sym == @symbol(EPROTOTYPE)) {
	RETURN ( __MKSMALLINT(EPROTOTYPE) );
    }
#endif

#ifdef ENOPROTOOPT
    if (sym == @symbol(ENOPROTOOPT)) {
	RETURN ( __MKSMALLINT(ENOPROTOOPT) );
    }
#endif

#ifdef EPROTONOSUPPORT
    if (sym == @symbol(EPROTONOSUPPORT)) {
	RETURN ( __MKSMALLINT(EPROTONOSUPPORT) );
    }
#endif

#ifdef ESOCKTNOSUPPORT
    if (sym == @symbol(ESOCKTNOSUPPORT)) {
	RETURN ( __MKSMALLINT(ESOCKTNOSUPPORT) );
    }
#endif

#ifdef EOPNOTSUPP
    if (sym == @symbol(EOPNOTSUPP)) {
	RETURN ( __MKSMALLINT(EOPNOTSUPP) );
    }
#endif

#ifdef EPFNOSUPPORT
    if (sym == @symbol(EPFNOSUPPORT)) {
	RETURN ( __MKSMALLINT(EPFNOSUPPORT) );
    }
#endif

#ifdef EAFNOSUPPORT
    if (sym == @symbol(EAFNOSUPPORT)) {
	RETURN ( __MKSMALLINT(EAFNOSUPPORT) );
    }
#endif

#ifdef EADDRINUSE
    if (sym == @symbol(EADDRINUSE)) {
	RETURN ( __MKSMALLINT(EADDRINUSE) );
    }
#endif

#ifdef EADDRNOTAVAIL
    if (sym == @symbol(EADDRNOTAVAIL)) {
	RETURN ( __MKSMALLINT(EADDRNOTAVAIL) );
    }
#endif

#ifdef ETIMEDOUT
    if (sym == @symbol(ETIMEDOUT)) {
	RETURN ( __MKSMALLINT(ETIMEDOUT) );
    }
#endif

#ifdef ECONNREFUSED
    if (sym == @symbol(ECONNREFUSED)) {
	RETURN ( __MKSMALLINT(ECONNREFUSED) );
    }
#endif

#ifdef ENETDOWN
    if (sym == @symbol(ENETDOWN)) {
	RETURN ( __MKSMALLINT(ENETDOWN) );
    }
#endif

#ifdef ENETUNREACH
    if (sym == @symbol(ENETUNREACH)) {
	RETURN ( __MKSMALLINT(ENETUNREACH) );
    }
#endif

#ifdef ENETRESET
    if (sym == @symbol(ENETRESET)) {
	RETURN ( __MKSMALLINT(ENETRESET) );
    }
#endif

#ifdef ECONNABORTED
    if (sym == @symbol(ECONNABORTED)) {
	RETURN ( __MKSMALLINT(ECONNABORTED) );
    }
#endif

#ifdef ECONNRESET
    if (sym == @symbol(ECONNRESET)) {
	RETURN ( __MKSMALLINT(ECONNRESET) );
    }
#endif

#ifdef EISCONN
    if (sym == @symbol(EISCONN)) {
	RETURN ( __MKSMALLINT(EISCONN) );
    }
#endif

#ifdef ENOTCONN
    if (sym == @symbol(ENOTCONN)) {
	RETURN ( __MKSMALLINT(ENOTCONN) );
    }
#endif

#ifdef ESHUTDOWN
    if (sym == @symbol(ESHUTDOWN)) {
	RETURN ( __MKSMALLINT(ESHUTDOWN) );
    }
#endif

#ifdef EHOSTDOWN
    if (sym == @symbol(EHOSTDOWN)) {
	RETURN ( __MKSMALLINT(EHOSTDOWN) );
    }
#endif

#ifdef EHOSTUNREACH
    if (sym == @symbol(EHOSTUNREACH)) {
	RETURN ( __MKSMALLINT(EHOSTUNREACH) );
    }
#endif
%}.
    ^ -1
!

errorSymbolAndTextForNumber:errNr
    "return an array consisting of symbol &  message string from a unix errorNumber 
     (as returned by a system call). 
     The returned message is in english (as found in /usr/include/errno.h)
     and should be replaced by a resource lookup before being presented to the user."

    |sym text|

%{
    /* claus:
     * I made this some primitive code, since errnos are not
     * standard across unixes
     */
    char *msg = "unknown error";
    char buffer[50];
    OBJ eno = errNr;

    if (__isSmallInteger(eno)) {
	switch (__intVal(eno)) {
	    /*
	     * POSIX errnos - these should be defined
	     */
#ifdef EPERM
	    case EPERM:
		msg = "Operation not permitted";
		sym = @symbol(EPERM);
		break;
#endif
#ifdef ENOENT
	    case ENOENT:
		msg = "No such file or directory";
		sym = @symbol(ENOENT);
		break;
#endif
#ifdef ESRCH
	    case ESRCH:
		msg = "No such process";
		sym = @symbol(ESRCH);
		break;
#endif
#ifdef EINTR
	    case EINTR:
		msg = "Interrupted system call";
		sym = @symbol(EINTR);
		break;
#endif
#ifdef EIO
	    case EIO:
		msg = "I/O error";
		sym = @symbol(EIO);
		break;
#endif
#ifdef ENXIO
	    case ENXIO:
		msg = "No such device or address";
		sym = @symbol(ENXIO);
		break;
#endif
#ifdef E2BIG
	    case E2BIG:
		msg = "Arg list too long";
		sym = @symbol(E2BIG);
		break;
#endif
#ifdef ENOEXEC
	    case ENOEXEC:
		msg = "Exec format error";
		sym = @symbol(ENOEXEC);
		break;
#endif
#ifdef EBADF
	    case EBADF:
		msg = "Bad file number";
		sym = @symbol(EBADF);
		break;
#endif
#ifdef ECHILD
	    case ECHILD:
		msg = "No child processes";
		sym = @symbol(ECHILD);
		break;
#endif
#if !defined(EWOULDBLOCK) && defined(EAGAIN) && (EWOULDBLOCK != EAGAIN)
	    case EAGAIN:
		msg = "Try again";
		sym = @symbol(EAGAIN);
		break;
#endif
#ifdef ENOMEM
	    case ENOMEM:
		msg = "Out of memory";
		sym = @symbol(ENOMEM);
		break;
#endif
#ifdef EACCES
	    case EACCES:
		msg = "Permission denied";
		sym = @symbol(EACCES);
		break;
#endif
#ifdef EFAULT
	    case EFAULT:
		msg = "Bad address";
		sym = @symbol(EFAULT);
		break;
#endif
#ifdef EBUSY
	    case EBUSY:
		msg = "Device or resource busy";
		sym = @symbol(EBUSY);
		break;
#endif
#ifdef EEXIST
	    case EEXIST:
		msg = "File exists";
		sym = @symbol(EEXIST);
		break;
#endif
#ifdef EXDEV
	    case EXDEV:
		msg = "Cross-device link";
		sym = @symbol(EXDEV);
		break;
#endif
#ifdef ENODEV
	    case ENODEV:
		msg = "No such device";
		sym = @symbol(ENODEV);
		break;
#endif
#ifdef ENOTDIR
	    case ENOTDIR:
		msg = "Not a directory";
		sym = @symbol(ENOTDIR);
		break;
#endif
#ifdef EISDIR
	    case EISDIR:
		msg = "Is a directory";
		sym = @symbol(EISDIR);
		break;
#endif
#ifdef EINVAL
	    case EINVAL:
		msg = "Invalid argument";
		sym = @symbol(EINVAL);
		break;
#endif
#ifdef ENFILE
	    case ENFILE:
		msg = "File table overflow";
		sym = @symbol(ENFILE);
		break;
#endif
#ifdef EMFILE
	    case EMFILE:
		msg = "Too many open files";
		sym = @symbol(EMFILE);
		break;
#endif
#ifdef ENOTTY
	    case ENOTTY:
		msg = "Not a typewriter";
		sym = @symbol(ENOTTY);
		break;
#endif
#ifdef EFBIG
	    case EFBIG:
		msg = "File too large";
		sym = @symbol(EFBIG);
		break;
#endif
#ifdef ENOSPC
	    case ENOSPC:
		msg = "No space left on device";
		sym = @symbol(ENOSPC);
		break;
#endif
#ifdef ESPIPE
	    case ESPIPE:
		msg = "Illegal seek";
		sym = @symbol(ESPIPE);
		break;
#endif
#ifdef EROFS
	    case EROFS:
		msg = "Read-only file system";
		sym = @symbol(EROFS);
		break;
#endif
#ifdef EMLINK
	    case EMLINK:
		msg = "Too many links";
		sym = @symbol(EMLINK);
		break;
#endif
#ifdef EPIPE
	    case EPIPE:
		msg = "Broken pipe";
		sym = @symbol(EPIPE);
		break;
#endif
#ifdef EDOM
	    case EDOM:
		msg = "Math argument out of domain";
		sym = @symbol(EDOM);
		break;
#endif
#ifdef ERANGE
	    case ERANGE:
		msg = "Math result not representable";
		sym = @symbol(ERANGE);
		break;
#endif
#ifdef EDEADLK
# if EDEADLK != EWOULDBLOCK
	    case EDEADLK:
		msg = "Resource deadlock would occur";
		sym = @symbol(EDEADLK);
		break;
# endif
#endif
#ifdef ENAMETOOLONG
	    case ENAMETOOLONG:
		msg = "File name too long";
		sym = @symbol(ENAMETOOLONG);
		break;
#endif
#ifdef ENOLCK
	    case ENOLCK:
		msg = "No record locks available";
		sym = @symbol(ENOLCK);
		break;
#endif
#ifdef ENOSYS
	    case ENOSYS:
		msg = "Function not implemented";
		sym = @symbol(ENOSYS);
		break;
#endif
#if defined(ENOTEMPTY) && (ENOTEMPTY != EEXIST)
	    case ENOTEMPTY:
		msg = "Directory not empty";
		sym = @symbol(ENOTEMPTY);
		break;
#endif
#ifdef EILSEQ
	    case EILSEQ:
		msg = "Illegal byte sequence";
		sym = @symbol(EILSEQ);
		break;
#endif
	    /*
	     * XPG3 errnos - defined on most systems
	     */
#ifdef ENOTBLK
	    case ENOTBLK:
		msg = "Block device required";
		sym = @symbol(ENOTBLK);
		break;
#endif
#ifdef ETXTBSY
	    case ETXTBSY:
		msg = "Text file busy";
		sym = @symbol(ETXTBSY);
		break;
#endif
	    /*
	     * some others
	     */
#ifdef EWOULDBLOCK
	    case EWOULDBLOCK:
		msg = "Operation would block";
		sym = @symbol(EWOULDBLOCK);
		break;
#endif
#ifdef ENOMSG
	    case ENOMSG:
		msg = "No message of desired type";
		sym = @symbol(ENOMSG);
		break;
#endif
#ifdef ELOOP
	    case ELOOP:
		msg = "Too many levels of symbolic links";
		sym = @symbol(ELOOP);
		break;
#endif

	    /*
	     * some stream errors
	     */
#ifdef ETIME
	    case ETIME:
		msg = "Timer expired";
		sym = @symbol(ETIME);
		break;
#endif
#ifdef ENOSR
	    case ENOSR:
		msg = "Out of streams resources";
		sym = @symbol(ENOSR);
		break;
#endif
#ifdef ENOSTR
	    case ENOSTR:
		msg = "Device not a stream";
		sym = @symbol(ENOSTR);
		break;
#endif
#ifdef ECOMM
	    case ECOMM:
		msg = "Communication error on send";
		sym = @symbol(ECOMM);
		break;
#endif
#ifdef EPROTO
	    case EPROTO:
		msg = "Protocol error";
		sym = @symbol(EPROTO);
		break;
#endif
	    /*
	     * nfs errors
	     */
#ifdef ESTALE
	    case ESTALE:
		msg = "Stale NFS file handle";
		sym = @symbol(ESTALE);
		break;
#endif
#ifdef EREMOTE
	    case EREMOTE:
		msg = "Too many levels of remote in path";
		sym = @symbol(EREMOTE);
		break;
#endif
	    /*
	     * some networking errors
	     */
#ifdef EINPROGRESS
	    case EINPROGRESS:
		msg = "Operation now in progress";
		sym = @symbol(EINPROGRESS);
		break;
#endif
#ifdef EALREADY
	    case EALREADY:
		msg = "Operation already in progress";
		sym = @symbol(EALREADY);
		break;
#endif
#ifdef ENOTSOCK
	    case ENOTSOCK:
		msg = "Socket operation on non-socket";
		sym = @symbol(ENOTSOCK);
		break;
#endif
#ifdef EDESTADDRREQ
	    case EDESTADDRREQ:
		msg = "Destination address required";
		sym = @symbol(EDESTADDRREQ);
		break;
#endif
#ifdef EMSGSIZE
	    case EMSGSIZE:
		msg = "Message too long";
		sym = @symbol(EMSGSIZE);
		break;
#endif
#ifdef EPROTOTYPE
	    case EPROTOTYPE:
		msg = "Protocol wrong type for socket";
		sym = @symbol(EPROTOTYPE);
		break;
#endif
#ifdef ENOPROTOOPT
	    case ENOPROTOOPT:
		msg = "Protocol not available";
		sym = @symbol(ENOPROTOOPT);
		break;
#endif
#ifdef EPROTONOSUPPORT
	    case EPROTONOSUPPORT:
		msg = "Protocol not supported";
		sym = @symbol(EPROTONOSUPPORT);
		break;
#endif
#ifdef ESOCKTNOSUPPORT
	    case ESOCKTNOSUPPORT:
		msg = "Socket type not supported";
		sym = @symbol(ESOCKTNOSUPPORT);
		break;
#endif
#ifdef EOPNOTSUPP
	    case EOPNOTSUPP:
		msg = "Operation not supported on socket";
		sym = @symbol(EOPNOTSUPP);
		break;
#endif
#ifdef EPFNOSUPPORT
	    case EPFNOSUPPORT:
		msg = "Protocol family not supported";
		sym = @symbol(EPFNOSUPPORT);
		break;
#endif
#ifdef EAFNOSUPPORT
	    case EAFNOSUPPORT:
		msg = "Address family not supported by protocol family";
		sym = @symbol(EAFNOSUPPORT);
		break;
#endif
#ifdef EADDRINUSE
	    case EADDRINUSE:
		msg = "Address already in use";
		sym = @symbol(EADDRINUSE);
		break;
#endif
#ifdef EADDRNOTAVAIL
	    case EADDRNOTAVAIL:
		msg = "Can\'t assign requested address";
		sym = @symbol(EADDRNOTAVAIL);
		break;
#endif
#ifdef ETIMEDOUT
	    case ETIMEDOUT:
		msg = "Connection timed out";
		sym = @symbol(ETIMEDOUT);
		break;
#endif
#ifdef ECONNREFUSED
	    case ECONNREFUSED:
		msg = "Connection refused";
		sym = @symbol(ECONNREFUSED);
		break;
#endif
#ifdef ENETDOWN
	    case ENETDOWN:
		msg = "Network is down";
		sym = @symbol(ENETDOWN);
		break;
#endif
#ifdef ENETUNREACH
	    case ENETUNREACH:
		msg = "Network is unreachable";
		sym = @symbol(ENETUNREACH);
		break;
#endif
#ifdef ENETRESET
	    case ENETRESET:
		msg = "Network dropped conn due to reset";
		sym = @symbol(ENETRESET);
		break;
#endif
#ifdef ECONNABORTED
	    case ECONNABORTED:
		msg = "Software caused connection abort";
		sym = @symbol(ECONNABORTED);
		break;
#endif
#ifdef ECONNRESET
	    case ECONNRESET:
		msg = "Connection reset by peer";
		sym = @symbol(ECONNRESET);
		break;
#endif
#ifdef EISCONN
	    case EISCONN:
		msg = "Socket is already connected";
		sym = @symbol(EISCONN);
		break;
#endif
#ifdef ENOTCONN
	    case ENOTCONN:
		msg = "Socket is not connected";
		sym = @symbol(ENOTCONN);
		break;
#endif
#ifdef ESHUTDOWN
	    case ESHUTDOWN:
		msg = "Can't send after socket shutdown";
		sym = @symbol(ESHUTDOWN);
		break;
#endif
#ifdef EHOSTDOWN
	    case EHOSTDOWN:
		msg = "Host is down";
		sym = @symbol(EHOSTDOWN);
		break;
#endif
#ifdef EHOSTUNREACH
	    case EHOSTUNREACH:
		msg = "No route to host";
		sym = @symbol(EHOSTUNREACH);
		break;
#endif

	    default:
		{
		    __BEGIN_PROTECT_REGISTERS__
		    sprintf(buffer, "ErrorNr: %d", __intVal(eno));
		    __END_PROTECT_REGISTERS__
		}
		msg = buffer;
		break;
	}
    }
    text = __MKSTRING(msg);
%}.
    ^ Array with:sym with:text

    "
     OperatingSystem errorSymbolAndTextForNumber:4
    "
!

errorSymbolForNumber:errNr
    "return a symbol for a unix errorNumber
     (as returned by a system call)."

    ^ (self errorSymbolAndTextForNumber:errNr) at:1

    "
     OperatingSystem errorTextForNumber:4
    "

    "Modified: 12.4.1996 / 09:16:29 / stefan"
!

errorTextForNumber:errNr
    "return a message string from a unix errorNumber 
     (as returned by a system call). 
     The returned message is in english (as found in /usr/include/errno.h)
     and should be replaced by a resource lookup before being presented to the user."

    ^ (self errorSymbolAndTextForNumber:errNr) at:2

    "
     OperatingSystem errorTextForNumber:4
    "
!

lastErrorNumber
    "return the last errors number.
     See also: #lastErrorSymbol and #lastErrorString.
     Notice: having a single error number is a bad idea in a multithreaded
             environment - this interface will change."

    ^ LastErrorNumber

     "
      OperatingSystem lastErrorNumber
     "
!

lastErrorString
    "return a message string describing the last error.
     See also: #lastErrorNumber and #lastErrorSymbol.
     Notice: having a single error number is a bad idea in a multithreaded
             environment - this interface will change."

    ^ self errorTextForNumber:LastErrorNumber

    "
     OperatingSystem lastErrorString
    "
!

lastErrorSymbol
    "return a symbol (such as #EBADF or #EACCESS) describing the last error.
     See also: #lastErrorNumber and #lastErrorString.
     Notice: having a single error number is a bad idea in a multithreaded
             environment - this interface will change."

    ^ self errorSymbolForNumber:LastErrorNumber

    "
     OperatingSystem lastErrorSymbol
    "
!

lastExecStatus
    "return the last execution status.
     Notice: having a single error number is a bad idea in a multithreaded
             environment - this interface will change."

    ^ LastExecStatus

     "
      OperatingSystem lastExecStatus
     "
! !

!OperatingSystem class methodsFor:'executing OS commands'!

canExecuteCommand:aCommandString
    "return true, if the OS can execute aCommand."

    |cmd|

    cmd := aCommandString asCollectionOfWords first.
    ^ (self pathOfCommand:cmd) notNil

    "
     OperatingSystem canExecuteCommand:'fooBar'  
     OperatingSystem canExecuteCommand:'ls'  
     OperatingSystem canExecuteCommand:'cvs'  
    "

    "Created: 4.11.1995 / 19:13:54 / cg"
!

exec:aPath withArguments:argArray
    "execute the unix command specified by the argument, aPath, with
     arguments in argArray (no arguments, if nil).
     If successful, this method does NOT return and smalltalk is gone.
     If not successful, false is returned. Can be used with fork or to transfer
     to another program."

    ^ self exec:aPath withArguments:argArray fork:false
!

exec:aPath withArguments:argArray fileDescriptors:fdArray closeDescriptors:closeFdArray fork:doFork newPgrp:newPgrp
    "Internal combined fork & exec;
     If fork is false:
         execute the unix command specified by the argument, aPath, with
         arguments in argArray (no arguments, if nil).
         If successful, this method does not return and smalltalk is gone.
         If not successful, false is returned. Normal use is with forkForCommand.
     if its true:
        for a child to do the above.
        The id of the child process is returned; -1 if fork failed.

     fdArray contains the filedescriptors, to be used for the child (if fork is true).
        fdArray[1] = 15 -> use fd 15 as stdin.
        If an element of the array is set to nil, the corresponding filedescriptor
        will be closed for the child.

     closeFdArray contains descriptors that will be closed in the subprocess.

     If newPgrp is true, the subprocess will be established in a new process group.
        Ths processgroup will be equal to id.

    Notice: this used to be two separate ST-methods; however, in order to use
            vfork on some machines, it had to be merged into one, to avoid write
            accesses to ST/X memory from the vforked-child. The code below only does
            read accesses."

%{
#ifndef MSDOS_LIKE
    char **argv;
    int nargs, i, id;
    OBJ arg;

    if (__isString(aPath) && 
        ((argArray == nil) || __isArray(argArray)) &&
        ((fdArray == nil) || __isArray(fdArray)) &&
        ((closeFdArray == nil) || __isArray(closeFdArray))
    ) {
        nargs = argArray == nil ? 0 : _arraySize(argArray);
        argv = (char **) malloc(sizeof(char *) * (nargs + 1));
        if (argv) {
            for (i=0; i < nargs; i++) {
                arg = __ArrayInstPtr(argArray)->a_element[i];
                if (__isString(arg)) {
                    argv[i] = (char *) __stringVal(arg);
                } else {
                    argv[i] = "";
                }
            }
            argv[i] = NULL;

            if (doFork == true) {
                int nfd, nclose;

                nfd = fdArray == nil ? 0 : _arraySize(fdArray);
                nclose = closeFdArray == nil ? 0 : _arraySize(closeFdArray);
#ifdef HAS_VFORK
                id = vfork();
#else
                id = fork();
#endif
                if (id == 0) {
                    /*
                    ** In child.
                    ** first: dup filedescriptors
                    */
                    for (i = 0; i < nfd; i++) {
                        if (__isSmallInteger(__ArrayInstPtr(fdArray)->a_element[i]) &&
                            __intVal(__ArrayInstPtr(fdArray)->a_element[i]) != i
                        ) {
                            dup2(__intVal(__ArrayInstPtr(fdArray)->a_element[i]), i);
                        }
                    }
                    /*
                    ** second: close unused filedescriptors
                    */
                    for (i = 0; i < nfd; i++) {
                        if (__ArrayInstPtr(fdArray)->a_element[i] == nil) {
                            close(i);
                        }
                    }
                    /*
                    ** third: close filedescriptors
                    */
                    for (i = 0; i < nclose; i++) {
                        if (__isSmallInteger(__ArrayInstPtr(closeFdArray)->a_element[i])) {
                            close(__intVal(__ArrayInstPtr(closeFdArray)->a_element[i]));
                        }
                    }
                    if (newPgrp == true) {
#if defined(_POSIX_JOB_CONTROL)
                        (void) setpgid(0, 0);
#else
#if defined(BSD)
                        (void) setpgrp(0);
#endif
#endif
                    }

                    execv(__stringVal(aPath), argv);
                    /* should not be reached */
                    _exit(127);                 /* POSIX 2 compatible exit value */
                }
                /*
                ** In parent: succes or failure
                */
                free(argv);
                RETURN (__MKSMALLINT(id));
            } else {
                execv(__stringVal(aPath), argv);
                /* 
                 * should not be reached
                 * (well, it is, if you pass a wrong command-path)
                 */
                free(argv);
                RETURN ( false );
            }
        }
    }
#endif
%}.
    "
     path-argument not string
     or argArray not an array/nil
     or malloc failed
     or not supported by OS
    "
    ^ self primitiveFailed

    "
     |id|

     id := OperatingSystem fork.
     id == 0 ifTrue:[
        "I am the child"
        OperatingSystem exec:'/bin/ls' withArguments:#('ls' '/tmp').
        "not reached"
     ]
    "
    "
     |id|

     id := OperatingSystem fork.
     id == 0 ifTrue:[
        "I am the child"
        OperatingSystem exec:'/bin/sh' withArguments:#('sh' '-c' 'sleep 2;echo 1;sleep 2;echo 2').
        "not reached"
     ].
     id printNL.
     (Delay forSeconds:3.5) wait.
     'killing ...' printNL.
     OperatingSystem sendSignal:(OperatingSystem sigTERM) to:id.
     OperatingSystem sendSignal:(OperatingSystem sigKILL) to:id
    "
!

exec:aPath withArguments:argArray fileDescriptors:fdArray fork:doFork
    "Internal combined fork & exec;
     If fork is false:
         execute the unix command specified by the argument, aPath, with
         arguments in argArray (no arguments, if nil).
         If successful, this method does not return and smalltalk is gone.
         If not successful, false is returned. Normal use is with forkForCommand.
     if its true:
        for a child to do the above.
        The id of the child process is returned; -1 if fork failed.

     fdArray contains the filedescriptors, to be used for the child (if fork is true).
        fdArray[1] = 15 -> use fd 15 as stdin.
        If an element of the array is set to nil, the corresponding filedescriptor
        will be closed for the child.

    Notice: this used to be two separate ST-methods; however, in order to use
            vfork on some machines, it had to be merged into one, to avoid write
            accesses to ST/X memory from the vforked-child. The code below only does
            read accesses."

    ^ self exec:aPath withArguments:argArray fileDescriptors:fdArray closeDescriptors:nil fork:doFork newPgrp:false

    "Modified: 23.4.1996 / 17:05:41 / stefan"
!

exec:aPath withArguments:argArray fork:doFork
    "Internal combined fork & exec;
     If fork is false:
	 execute the unix command specified by the argument, aPath, with
	 arguments in argArray (no arguments, if nil).
	 If successful, this method does not return and smalltalk is gone.
	 If not successful, false is returned. Normal use is with forkForCommand.
     if its true:
	for a child to do the above.
	The id of the child process is returned; -1 if fork failed.

    Notice: this used to be two separate ST-methods; however, in order to use
	    vfork on some machines, it had to be merged into one, to avoid write
	    accesses to ST/X memory from the vforked-child. The code below only does
	    read accesses."

%{
#ifndef MSDOS_LIKE
    char **argv;
    int nargs, i, id;
    OBJ arg;

    if (__isString(aPath) && ((argArray == nil) || __isArray(argArray))) {
	nargs = argArray == nil ? 0 : _arraySize(argArray);
	argv = (char **) malloc(sizeof(char *) * (nargs + 1));
	if (argv) {
	    for (i=0; i < nargs; i++) {
		arg = __ArrayInstPtr(argArray)->a_element[i];
		if (__isString(arg)) {
		    argv[i] = (char *) __stringVal(arg);
		} else {
		    argv[i] = "";
		}
	    }
	    argv[i] = NULL;

	    if (doFork == true) {
#ifdef HAS_VFORK
		id = vfork();
#else
		id = fork();
#endif
		if (id == 0) {
		    execv(__stringVal(aPath), argv);
		    /* should not be reached */
		    _exit(127);         /* POSIX-2 compatible exit value */
		}
		free(argv);
		RETURN (__MKSMALLINT(id));
	    } else {
		execv(__stringVal(aPath), argv);
		/* 
		 * should not be reached
		 * (well, it is, if you pass a wrong command-path)
		 */
		free(argv);
		RETURN ( false );
	    }
	}
    }
#endif
%}.
    "
     path-argument not string
     or argArray not an array/nil
     or malloc failed
     or not supported by OS
    "
    ^ self primitiveFailed

    "
     |id|

     id := OperatingSystem fork.
     id == 0 ifTrue:[
	"I am the child"
	OperatingSystem exec:'/bin/ls' withArguments:#('ls' '/tmp').
	"not reached"
     ]
    "
    "
     |id|

     id := OperatingSystem fork.
     id == 0 ifTrue:[
	"I am the child"
	OperatingSystem exec:'/bin/sh' withArguments:#('sh' '-c' 'sleep 2;echo 1;sleep 2;echo 2').
	"not reached"
     ].
     id printNL.
     (Delay forSeconds:3.5) wait.
     'killing ...' printNL.
     OperatingSystem sendSignal:(OperatingSystem sigTERM) to:id.
     OperatingSystem sendSignal:(OperatingSystem sigKILL) to:id
    "
!

executeCommand:aCommandString
    "execute the unix command specified by the argument, aCommandString.
     The commandString is passed to a shell for execution - see the description of
     'sh -c' in your UNIX manual.
     Return true if successful, false otherwise."

" when all machines have been checked, this will be defined as:
     ^ self executeCommand:aCommandString onError:[:status| false]
until then:"

%{ 
    int status;

    if (__isString(aCommandString)) {
	/*
	 * ST/X provides a modified (fixed) implementation of the
	 * system() libc-function, which is interruptable ...
	 */
# ifdef WANT_SYSTEM
	status = mySystem((char *) __stringVal(aCommandString));
# else
	status = system((char *) __stringVal(aCommandString));
# endif
	OperatingSystem_LastExecStatus = __MKSMALLINT(status);
	if (status == 0) {
	    RETURN ( true );
	}
	OperatingSystem_LastErrorNumber = __MKSMALLINT(errno);
	RETURN ( false );
    }
%}.
    "/
    "/ invalid argument (non-string)
    "/
    ^ self primitiveFailed

    "
     OperatingSystem executeCommand:'sleep 30'. 
     OperatingSystem executeCommand:'pwd'. 
     OperatingSystem executeCommand:'ls -l'. 
     OperatingSystem executeCommand:'invalidCommand'. 
     OperatingSystem executeCommand:'rm /tmp/foofoofoofoo'. 
    "

    "Modified: 27.12.1995 / 23:40:35 / stefan"
!

executeCommand:aCommandString onError:aBlock
    "execute the unix command specified by the argument, aCommandString.
     The commandString is passed to a shell for execution - see the description of
     'sh -c' in your UNIX manual.
     Return true if successful.
     If not successfull, aBlock is called with a Dictionary containing the exit status
     as argument."

    |pid exitStatus sema|

    sema := Semaphore new.

    [
        pid := self startProcess:aCommandString.
        pid > 0 ifTrue:[
            Processor monitorPid:pid action:[ :stat |
                stat stillAlive ifFalse:[
                    exitStatus := stat.
                    sema signal
                ].
            ].
            sema wait.
        ] ifFalse:[
            exitStatus := OSProcessStatus processCreationFailure.
        ].
    ] valueUninterruptably.

    exitStatus success ifFalse:[
        ^ aBlock value:exitStatus
    ].
    ^ true.


    "
     OperatingSystem executeCommand:'sleep 30' onError:[]. 
     OperatingSystem executeCommand:'pwd' onError:[:status|status inspect]. 
     OperatingSystem executeCommand:'ls -l' onError:[]. 
     OperatingSystem executeCommand:'invalidCommand' onError:[:status| status inspect]. 
     OperatingSystem executeCommand:'rm /tmp/foofoofoofoo'onError:[:status | status inspect]. 
    "

    "Created: 22.12.1995 / 14:49:59 / stefan"
    "Modified: 28.12.1995 / 15:25:48 / stefan"
    "Modified: 28.2.1996 / 21:41:23 / cg"
!

fork
    "fork a new (HEAVY-weight) unix process.
     Dont confuse this with Block>>fork, which creates 
     lightweight smalltalk processes. This method will return
     0 to the child process, and a non-zero number (which is the childs
     unix-process-id) to the parent (original) process.

     In normal situations, you dont need to use this low level entry; see
     #startProcess: and #executCommand: for higher level interfaces."

%{  /* NOCONTEXT */
#ifndef MSDOS_LIKE

    int pid;

    pid = fork();
    RETURN ( __MKSMALLINT(pid) );
#endif
%}.
    "/
    "/ not supported by OS
    "/
    ^ self primitiveFailed

    "
     |id|

     id := OperatingSystem fork.
     id == 0 ifTrue:[
	OperatingSystem exit
     ]
    "
!

getStatusOfProcess:aProcessId
    "wait for a process to terminate and fetch its exit status.
     This is required to avoid zombie processes."

%{
#ifndef MSDOS_LIKE
    int status;

    if (__isSmallInteger(aProcessId)) {
	pid_t pid = (pid_t)(__intVal(aProcessId));
	{

# ifdef  NO_WAITPID
	    pid_t child;

	    do {
		__BEGIN_INTERRUPTABLE__
		child = __wait (&status);
		__END_INTERRUPTABLE__
		if (child < 0 && errno != EINTR) {
		    fprintf(stderr, "OS: child-wait errno=%d\n", errno);
		    status = -1;
		    break;
		}
	    } while (child != pid);
# else
	    pid_t child;

	    /* claus: the original did not care for EINTR here ... */
	    do {
		__BEGIN_INTERRUPTABLE__
		child = __waitpid (pid, &status, 0);
		__END_INTERRUPTABLE__
	    } while ((child != pid) && (errno == EINTR));
	    if (child != pid) {
		fprintf(stderr, "OS: child-waitpid errno=%d\n", errno);
		status = -1;
	    }
# endif /* NO_WAITPID */
	}
	RETURN ( __MKSMALLINT(status));
    }
#endif
%}.
    self primitiveFailed

!

pathOfCommand:aCommand
    "find where aCommand's executable file is"

    |path f|

    path := self getEnvironment:'PATH'.
    path notNil ifTrue:[
	(path asCollectionOfSubstringsSeparatedBy:$:) do:[:path |
	    (f := (path asFilename construct:aCommand)) isExecutable ifTrue:[
		^  f pathName
	    ]
	].
    ].
    ^ nil

    "
     OperatingSystem pathOfCommand:'fooBar'  
     OperatingSystem pathOfCommand:'ls'  
     OperatingSystem pathOfCommand:'cvs'  
    "
!

startProcess:aCommandString
    "start executing the unix command as specified by the argument, aCommandString
     as a separate process; do not wait for the command to finish.
     The commandString is passed to a shell for execution - see the description of
     'sh -c' in your UNIX manual.
     Return the processId if successful, nil otherwise.
     Use #waitForProcess: for synchronization and exec status return,
     or #killProcess: to stop it."

    ^ self 
	exec:'/bin/sh' 
	withArguments:(Array with:'sh' with:'-c' with:aCommandString)
	fork:true.

    "
     |pid|

     pid := OperatingSystem startProcess:'sleep 2; echo 1; sleep 2; echo 2'.
     (Delay forSeconds:3) wait.
     OperatingSystem killProcess:pid.
    "
!

startProcess:aCommandString inputFrom:anExternalInStream outputTo:anExternalOutStream errorTo:anExternalErrStream
    "start executing the unix command as specified by the argument, aCommandString
     as a separate process; do not wait for the command to finish.
     The commandString is passed to a shell for execution - see the description of
     'sh -c' in your UNIX manual.
     The command gets stdIn, stdOut and stdErr assigned from the arguments;
     each may be nil.
     Return the processId if successful, nil otherwise.
     Use #monitorPid:action: for synchronization and exec status return,
     or #killProcess: to stop it."

    |in out err|

    anExternalInStream notNil ifTrue:[
        in := anExternalInStream fileDescriptor.
    ] ifFalse:[
        in := '/dev/null' asFilename readStream fileDescriptor
    ].
    anExternalOutStream notNil ifTrue:[
        out := anExternalOutStream fileDescriptor.
    ].
    anExternalErrStream notNil ifTrue:[
        err := anExternalErrStream fileDescriptor.
    ].

    ^ self 
        exec:'/bin/sh' 
        withArguments:(Array with:'sh' with:'-c' with:aCommandString)
        fileDescriptors:(Array with:in with:out with:err)
        fork:true.

    "blocking at current prio (i.e. only higher prio threads execute):

     OperatingSystem executeCommand:'ls -l > out'.
    "

    "non-blocking (lower prio threads continue):

     |in out err pid sema|

     in := 'out' asFilename readStream.
     out := 'out2' asFilename writeStream.
     err := 'err' asFilename writeStream.

     sema := Semaphore new.
     pid := OperatingSystem startProcess:'sleep 10; grep drw' inputFrom:in outputTo:out errorTo:err.
     pid notNil ifTrue:[
         Processor monitorPid:pid action:[:OSstatus | sema signal ].
     ].
     in close.
     out close.
     err close.
     sema wait.
     Transcript showCr:'finished'
    "

    "Created: 29.2.1996 / 12:31:29 / cg"
    "Modified: 29.2.1996 / 12:49:14 / cg"
! !

!OperatingSystem class methodsFor:'file access'!

closeFd:anInteger
    "low level close of a filedescriptor"

%{
#if !defined(transputer) && !defined(MSDOS_LIKE)
     if (__isSmallInteger(anInteger)) {
	close(__intVal(anInteger));
	RETURN(self);
     }
#endif
%}.
     ^ self primitiveFailed.
!

createDirectory:newPathName
    "create a new directory with name 'newPathName'.
     Return true if successful, false if failed.
     This is a low-level entry - use Filename protocol for compatibility."

    "since createDirectory is not used too often,
     you'll forgive me using mkdir ..."

    ^ self executeCommand:('mkdir 2>/dev/null ', newPathName)

    "
     OperatingSystem createDirectory:'foo'
    "

    "Modified: 20.12.1995 / 11:24:13 / stefan"
    "Modified: 7.3.1996 / 15:28:19 / cg"
!

linkFile:oldPath to:newPath
    "link the file 'oldPath' to 'newPath'. The link will be a hard link.
     Return true if successful, false if not."

%{
#ifndef MSDOS_LIKE
    int ret;

    if (__isString(oldPath) && __isString(newPath)) {
        __BEGIN_INTERRUPTABLE__
        do {
            ret = link((char *) __stringVal(oldPath), (char *) __stringVal(newPath));
        } while (ret < 0 && errno == EINTR);
        __END_INTERRUPTABLE__
        if (ret < 0) {
            @global(LastErrorNumber) = __MKSMALLINT(errno);
            RETURN ( false );
        }
        RETURN (true);
    }
#endif
%}.
    ^ self primitiveFailed

    "
     OperatingSystem linkFile:'foo' to:'bar'
    "
!

makePipe
    "make a pipe, return array with two filedescriptors on success,
     nil on failure.
     This is a lowLevel entry, not for public use.
     See ExternalStream>>makePipe for a more user-friendly, public interface."

    |fd1 fd2|    
                
%{       
     int fds[2];

     if (pipe(fds) < 0) {
        @global(LastErrorNumber) = __MKSMALLINT(errno);
        RETURN ( nil );
     }

     fd1 = __MKSMALLINT(fds[0]);
     fd2 = __MKSMALLINT(fds[1]);
%}.

     ^ Array with:fd1 with:fd2.
!

recursiveCopyDirectory:sourcePathName to:destination
    "copy the directory named 'sourcePathName' and all contained files/directories to 'destination'.
     Return true if successful."

    ^ self executeCommand:('cp -rf ' , sourcePathName, ' ', destination)

    "Modified: 7.3.1996 / 15:26:30 / cg"
!

recursiveCreateDirectory:dirName
    "create a directory - with all parent dirs if needed.
     Return true if successful, false otherwise. If false
     is returned, a partial created tree may be left,
     which is not cleaned-up here."

    self createDirectory:dirName.
    (self isDirectory:dirName) ifFalse:[
        (self recursiveCreateDirectory:(self directoryNameOf:dirName)) ifFalse:[^ false].
        ^ self createDirectory:dirName
    ].
    ^ true

    "
     OperatingSystem recursiveCreateDirectory:'foo/bar/baz'
     OperatingSystem recursiveRemoveDirectory:'foo'
    "

    "Modified: 7.3.1996 / 15:26:22 / cg"
!

recursiveRemoveDirectory:fullPathName
    "remove the directory named 'fullPathName' and all contained files/directories.
     Return true if successful."

    ^ self executeCommand:('rm -rf ' , fullPathName)

    "
     OperatingSystem recursiveCreateDirectory:'foo/bar/baz'
     OperatingSystem recursiveRemoveDirectory:'foo'
    "

    "Modified: 7.3.1996 / 15:26:30 / cg"
!

removeDirectory:fullPathName
    "remove the directory named 'fullPathName'.
     Return true if successful, false if directory is not empty or no permission.
     This is a lowLevel entry - use Filename protocol for compatibility."

%{
    int ret;

    if (__isString(fullPathName)) {
        __BEGIN_INTERRUPTABLE__
        do {
            ret = rmdir((char *) __stringVal(fullPathName));
        } while (ret < 0 && errno == EINTR);
        __END_INTERRUPTABLE__
        if (ret < 0) {
            @global(LastErrorNumber) = __MKSMALLINT(errno);
            RETURN ( false );
        }
        RETURN (true);
    }
%}.
    ^ self primitiveFailed

    "
     OperatingSystem createDirectory:'foo'
     OperatingSystem removeDirectory:'foo'
    "

!

removeFile:fullPathName
    "remove the file named 'fullPathName'; return true if successful.
     This is a lowLevel entry - use Filename protocol for compatibility."

%{
    int ret;

    if (__isString(fullPathName)) {
        __BEGIN_INTERRUPTABLE__
        do {
            ret = unlink((char *) __stringVal(fullPathName));
        } while (ret < 0 && errno == EINTR);
        __END_INTERRUPTABLE__
        if (ret < 0) {
            @global(LastErrorNumber) = __MKSMALLINT(errno);
            RETURN ( false );
        }
        RETURN (true);
    }
%}.
    ^ self primitiveFailed
!

renameFile:oldPath to:newPath
    "rename the file 'oldPath' to 'newPath'. 
     Return true if successful, false if not"

%{
    int ret, eno;

    if (__isString(oldPath) && __isString(newPath)) {
#if defined(HAS_RENAME)
        __BEGIN_INTERRUPTABLE__
        do {
            ret = rename((char *) __stringVal(oldPath), (char *) __stringVal(newPath));
        } while (ret < 0 && errno == EINTR);
        __END_INTERRUPTABLE__
#else
# ifndef MSDOS_LIKE
        ret = link((char *) __stringVal(oldPath), (char *) __stringVal(newPath));
        if (ret >= 0) {
            ret = unlink((char *) __stringVal(oldPath));
            if (ret < 0) {
                eno = errno;
                unlink((char *) __stringVal(newPath));
                errno = eno;
            }
        }
# endif
#endif
        if (ret < 0) {
            @global(LastErrorNumber) = __MKSMALLINT(errno);
            RETURN ( false );
        }
        RETURN (true);
    }
%}.
    ^ self primitiveFailed

    "
     OperatingSystem renameFile:'foo' to:'bar'
    "
!

truncateFile:aPathName to:newSize
    "change a files size return true on success, false on failure.
     This may not be supported on all architectures.

     This is a low-level entry - use Filename protocol."

%{
#ifdef HAS_FTRUNCATE
    int ret;

    if (__isString(aPathName)
     && __isSmallInteger(newSize)) {
        __BEGIN_INTERRUPTABLE__
        do {
            ret = truncate((char *) __stringVal(aPathName), __intVal(newSize));
        } while (ret < 0 && errno == EINTR);
        __END_INTERRUPTABLE__
        if (ret < 0) {
            @global(LastErrorNumber) = __MKSMALLINT(errno);
            RETURN ( false );
        }
        RETURN (true);
    }
#endif
%}.
    ^ self primitiveFailed
! !

!OperatingSystem class methodsFor:'file access rights'!

accessMaskFor:aSymbol
    "return the access bits mask for numbers as returned by 
     OperatingSystem>>accessModeOf:
     and expected by OperatingSystem>>changeAccessModeOf:to:.
     Since these numbers are OS dependent, always use the mask
     (never hardcode 8rxxx into your code)."

%{  /* NOCONTEXT */
#   ifndef S_IRUSR
    /* posix systems should define these ... */
#    define S_IRUSR 0400
#    define S_IWUSR 0200
#    define S_IXUSR 0100
#    define S_IRGRP 0040
#    define S_IWGRP 0020
#    define S_IXGRP 0010
#    define S_IROTH 0004
#    define S_IWOTH 0002
#    define S_IXOTH 0001

#   endif

    if (aSymbol == @symbol(readUser)) {
	RETURN ( __MKSMALLINT(S_IRUSR) );
    }
    if (aSymbol == @symbol(writeUser)) {
	RETURN ( __MKSMALLINT(S_IWUSR) );
    }
    if (aSymbol == @symbol(executeUser)) {
	RETURN ( __MKSMALLINT(S_IXUSR) );
    }
    if (aSymbol == @symbol(readGroup)) {
	RETURN ( __MKSMALLINT(S_IRGRP) );
    }
    if (aSymbol == @symbol(writeGroup)) {
	RETURN ( __MKSMALLINT(S_IWGRP) );
    }
    if (aSymbol == @symbol(executeGroup)) {
	RETURN ( __MKSMALLINT(S_IXGRP) );
    }
    if (aSymbol == @symbol(readOthers)) {
	RETURN ( __MKSMALLINT(S_IROTH) );
    }
    if (aSymbol == @symbol(writeOthers)) {
	RETURN ( __MKSMALLINT(S_IWOTH) );
    }
    if (aSymbol == @symbol(executeOthers)) {
	RETURN ( __MKSMALLINT(S_IXOTH) );
    }
%}.
    ^ self primitiveFailed

    "
     OperatingSystem accessMaskFor:#readUser
    "
!

accessModeOf:aPathName
    "return a number representing access rights rwxrwxrwx for owner,
     group and others. Return nil if such a file does not exist.
     Notice that the returned number is OS dependent - use the 
     modeMasks as returned by OperatingSystem>>accessMaskFor:"

    "
     this could have been implemented as:
        (self infoOf:aPathName) at:#mode
     but for huge directory searches the code below is faster
    "

%{
    struct stat buf;
    int ret;

    if (__isString(aPathName)) {
        __BEGIN_INTERRUPTABLE__
        do {
            ret = stat((char *) __stringVal(aPathName), &buf);
        } while ((ret < 0) && (errno == EINTR));
        __END_INTERRUPTABLE__

        if (ret < 0) {
            @global(LastErrorNumber) = __MKSMALLINT(errno);
            RETURN ( nil );
        }
        RETURN ( __MKSMALLINT(buf.st_mode & 0777) );
    }
%}.
   ^ self primitiveFailed

   "
    (OperatingSystem accessModeOf:'/') printStringRadix:8
   "
!

changeAccessModeOf:aPathName to:modeBits
    "change the access rights of aPathName to the OS dependent modeBits.
     You should construct this mask using accessMaskFor, to be OS
     independent. Return true if changed, 
     false if such a file does not exist or change was not allowd."

%{
    int ret;

    if (__isString(aPathName) && __isSmallInteger(modeBits)) {
        __BEGIN_INTERRUPTABLE__
        do {
            ret = chmod((char *)__stringVal(aPathName), __intVal(modeBits));
        } while (ret < 0 && errno == EINTR);
        __END_INTERRUPTABLE__
        if (ret < 0) {
            @global(LastErrorNumber) = __MKSMALLINT(errno);
            RETURN ( false );
        }
        RETURN ( true );
    }
%}.
    ^ self primitiveFailed
! !

!OperatingSystem class methodsFor:'file queries'!

baseNameOf:aPathString
    "return the baseName of the argument, aPathString
     - thats the file/directory name without leading parent-dirs
     (i.e. OperatingSystem baseNameOf:'/usr/lib/st/file' -> 'file'
       and OperatingSystem baseNameOf:'/usr/lib' -> lib).
     This method does not check if the path is valid 
     (i.e. if these directories really exist & is readable)."

    |prev index sep|

    sep := self fileSeparator.
    ((aPathString size == 1) and:[(aPathString at:1) == sep]) ifTrue:[
	^ aPathString
    ].
    prev := 1.
    [true] whileTrue:[
	index := aPathString indexOf:sep startingAt:prev.
	index == 0 ifTrue:[
	    ^ aPathString copyFrom:prev
	].
	prev := index + 1
    ]

    "
     OperatingSystem baseNameOf:'/fee/foo/bar'
     OperatingSystem baseNameOf:'foo/bar'
     OperatingSystem baseNameOf:'../../foo/bar'
     OperatingSystem baseNameOf:'hello'
    "
!

caseSensitiveFilenames
    "return true, if the OS has caseSensitive file naming.
     On MSDOS, this will return false. Since this is for Unix, we
     return true."

    ^ true
!

compressPath:pathName
    "return the pathName compressed - that is, remove all ..-entries
     and . entries. This does not always (in case of symbolic links)
     return the true pathName and is therefore used as a fallback
     if realPath and popen failed."

    |names|

    names := pathName 
		asCollectionOfSubstringsSeparatedBy:self fileSeparator.
    names := names asOrderedCollection.
    "
     cut off initial double-slashes
    "
    [names startsWith:#('' '')] whileTrue:[
	names removeFirst.
    ].
    "
     cut off double-slashes at end
    "
    [names endsWith:#('')] whileTrue:[
	names removeLast.
    ].
    "
     cut off current-dir at beginning
    "
    [(names size >= 2) and:[names startsWith:#('.')]] whileTrue:[
	names removeFirst.
    ].

    "
     cut off parent-dirs at end
    "
    [(names size > 2) 
     and:[(names endsWith:#('..'))
     and:[((names at:(names size - 1)) startsWith:'.') not ]]] whileTrue:[
	names removeLast; removeLast
    ].

    ^ names asStringWith:self fileSeparator 
		    from:1
		    to:names size
		    compressTabs:false final:nil 

    "
     OperatingSystem compressPath:'./..'    
     OperatingSystem compressPath:'/foo/bar/baz/..'  
     OperatingSystem compressPath:'foo/bar/baz/..'  
     OperatingSystem compressPath:'foo/bar/baz/../'  
     OperatingSystem compressPath:'foo/bar/baz/..///' 
     OperatingSystem compressPath:'///foo/bar/baz/..///' 
    "
!

directoryNameOf:aPathString
    "return the directoryName of the argument, aPath
     - thats the name of the directory where aPath is
     (i.e. OperatingSystem directoryNameOf:'/usr/lib/st/file' -> '/usr/lib/st'
       and OperatingSystem directoryNameOf:'/usr/lib' -> /usr').
     This method does not check if the path is valid
     (i.e. if these directories really exist & are readable)."

    |last index sep sepString|

    sep := self fileSeparator.
    sepString := sep asString.
    (aPathString = sepString) ifTrue:[
	"
	 the trivial '/' case
	"
	^ aPathString
    ].
    (aPathString startsWith:sepString) ifFalse:[
	(aPathString endsWith:sepString) ifTrue:[
	    ^ aPathString copyTo:(aPathString size - 1)
	].
    ].
    last := 1.
    [true] whileTrue:[
	index := aPathString indexOf:sep startingAt:(last + 1).
	index == 0 ifTrue:[
	    (last == 1) ifTrue:[
		(aPathString startsWith:sepString) ifTrue:[
		    ^ sepString
		].
		^ '.'
	    ].
"
	    (aPathString startsWith:sepString) ifFalse:[
		(aPathString startsWith:('..' , sepString)) ifFalse:[
		    ^ './' , (aPathString copyTo:(last - 1))
		]
	    ].
"
	    ^ aPathString copyTo:(last - 1)
	].
	last := index.
    ]

    "
     OperatingSystem directoryNameOf:'/fee/foo/bar'
     OperatingSystem directoryNameOf:'foo/bar'
     OperatingSystem directoryNameOf:'../../foo/bar'
     OperatingSystem directoryNameOf:'bar'
     OperatingSystem directoryNameOf:'/bar'
    "
!

fileSeparator
    "return the character used to separate names in a path.
     This character differs for MSDOS and other systems,
     (but those are currently not supported - so this is some
      preparation for the future)"

    ^ $/
!

idOf:aPathName
    "return the fileNumber (i.e. inode number) of a file.

     Not all operatingSystems may provide this - on those that do not,
     some dummy id will be returned.
     On unix, this information can be used to check for two files being 
     physically identical, even if found in different directories 
     (i.e. if they are hardLinked)."

    "
     this could have been implemented as:
        (self infoOf:aPathName) at:#id 
     but for huge directory searches the code below is faster
    "

%{
    struct stat buf;
    int ret;

    if (__isString(aPathName)) {
        __BEGIN_INTERRUPTABLE__
        do {
            ret = stat((char *) __stringVal(aPathName), &buf);
        } while (ret < 0 && errno == EINTR);
        __END_INTERRUPTABLE__
        if (ret >= 0) {
            RETURN (__MKSMALLINT(buf.st_ino));
        }
        @global(LastErrorNumber) = __MKSMALLINT(errno);
        RETURN (nil);
    }
%}
.
    ^ self primitiveFailed

    "OperatingSystem idOf:'/'"
!

infoOf:aPathName
    "return a dictionary filled with info for the file 'aPathName';
     the info is: (#type->t #mode->n #uid->u #gid->g #size->s #id->ino).
     Return nil if such a file does not exist. 
     For symbolic links, the info of the pointed-to-file is returned;
     use #linkInfoOf: to get info about the link itself.

     A dictionary is returned, since we might need to add more info in the 
     future without affecting existing applications."

    |info type mode uid gid size id 
     atimeLow atimeHi mtimeLow mtimeHi ctimeLow ctimeHi|

%{
    struct stat buf;
    int ret;

    if (__isString(aPathName)) {
        __BEGIN_INTERRUPTABLE__
        do {
            ret = stat((char *) __stringVal(aPathName), &buf);
        } while ((ret < 0) && (errno == EINTR));
        __END_INTERRUPTABLE__

        if (ret < 0) {
            @global(LastErrorNumber) = __MKSMALLINT(errno);
            RETURN ( nil );
        }
        switch (buf.st_mode & S_IFMT) {
            case S_IFDIR:
                type = @symbol(directory);
                break;
            case S_IFREG:
                type = @symbol(regular);
                break;
#ifdef S_IFCHR
            case S_IFCHR:
                type = @symbol(characterSpecial);
                break;
#endif
#ifdef S_IFBLK
            case S_IFBLK:
                type = @symbol(blockSpecial);
                break;
#endif
#ifdef S_IFLNK
            case S_IFLNK:
                type = @symbol(symbolicLink);
                break;
#endif
#ifdef S_IFSOCK
            case S_IFSOCK:
                type = @symbol(socket);
                break;
#endif
#ifdef S_IFIFO
            case S_IFIFO:
                type = @symbol(fifo);
                break;
#endif
            default:
                type = @symbol(unknown);
                break;
        }
        mode = __MKSMALLINT(buf.st_mode & 0777);
        uid = __MKSMALLINT(buf.st_uid);
        gid = __MKSMALLINT(buf.st_gid);
        size = __MKSMALLINT(buf.st_size);
        id = __MKSMALLINT(buf.st_ino);
        atimeLow = __MKSMALLINT(buf.st_atime & 0xFFFF);
        atimeHi = __MKSMALLINT((buf.st_atime >> 16) & 0xFFFF);
        mtimeLow = __MKSMALLINT(buf.st_mtime & 0xFFFF);
        mtimeHi = __MKSMALLINT((buf.st_mtime >> 16) & 0xFFFF);
        ctimeLow = __MKSMALLINT(buf.st_ctime & 0xFFFF);
        ctimeHi = __MKSMALLINT((buf.st_ctime >> 16) & 0xFFFF);
    }
%}.
    mode notNil ifTrue:[
        info := IdentityDictionary new.
        info at:#type put:type.
        info at:#mode put:mode.
        info at:#uid put:uid.
        info at:#gid put:gid.
        info at:#size put:size.
        info at:#id put:id.
        info at:#accessed      put:(AbsoluteTime fromOSTimeLow:atimeLow and:atimeHi).
        info at:#modified      put:(AbsoluteTime fromOSTimeLow:mtimeLow and:mtimeHi).
        info at:#statusChanged put:(AbsoluteTime fromOSTimeLow:ctimeLow and:ctimeHi).
        ^ info
   ].
   ^ self primitiveFailed

   "
    OperatingSystem infoOf:'/'
    (OperatingSystem infoOf:'/') at:#uid
    (OperatingSystem infoOf:'/') at:#accessed
   "
!

isDirectory:aPathName
    "return true, if 'aPathName' is a valid directory path name.
     (i.e. exists and is a directory).
     This also returns true for symbolic links pointing to a directory;
     if you need to check for this, use #linkInfo:."

%{
    struct stat buf;
    int ret;

    if (__isString(aPathName)) {
        __BEGIN_INTERRUPTABLE__
        do {
            ret = stat((char *) __stringVal(aPathName), &buf);
        } while ((ret < 0) && (errno == EINTR));
        __END_INTERRUPTABLE__
        if (ret < 0) {
            @global(LastErrorNumber) = __MKSMALLINT(errno);
            RETURN ( false );
        }
        RETURN ( ((buf.st_mode & S_IFMT) == S_IFDIR) ? true : false);
    }
%}.
    ^ self primitiveFailed

    "an alternative implementation would be:
        ^ (self infoOf:aPathName) at:#type == #directory
    "
!

isExecutable:aPathName
    "return true, if the given file is executable.
     For symbolic links, the pointed-to-file is checked."

%{
    int ret;

    if (__isString(aPathName)) {
        __BEGIN_INTERRUPTABLE__
        do {
            ret = access(__stringVal(aPathName), X_OK);
        } while ((ret < 0) && (errno == EINTR));
        __END_INTERRUPTABLE__
        if (ret < 0) {
            @global(LastErrorNumber) = __MKSMALLINT(errno);
        }
        RETURN ( ((ret == 0) ? true : false) );
    }
%}.
    ^ self primitiveFailed
!

isReadable:aPathName
    "return true, if the file/dir 'aPathName' is readable.
     For symbolic links, the pointed-to-file is checked."

%{
    int ret;

    if (__isString(aPathName)) {
        __BEGIN_INTERRUPTABLE__
        do {
            ret = access(__stringVal(aPathName), R_OK);
        } while ((ret < 0) && (errno == EINTR));
        __END_INTERRUPTABLE__
        if (ret < 0) {
            @global(LastErrorNumber) = __MKSMALLINT(errno);
        }
        RETURN ( ((ret == 0) ? true : false) );
    }
%}.
    ^ self primitiveFailed
!

isSymbolicLink:aPathName
    "return true, if the given file is a symbolic link"

    ^ (self linkInfoOf:aPathName) notNil

    "
     OperatingSystem isSymbolicLink:'Make.proto'
     OperatingSystem isSymbolicLink:'Makefile' 
    "
!

isValidPath:aPathName
    "return true, if 'aPathName' is a valid path name
     (i.e. the file or directory exists)"

%{
    struct stat buf;
    int ret;

    if (__isString(aPathName) || __isSymbol(aPathName) ) {
	__BEGIN_INTERRUPTABLE__
	do {
	    ret = stat((char *) __stringVal(aPathName), &buf);
	} while ((ret < 0) && (errno == EINTR));
	__END_INTERRUPTABLE__
	if (ret < 0) {
	    OperatingSystem_LastErrorNumber = __MKSMALLINT(errno);
	    RETURN (false);
	}
	RETURN ( ret ? false : true );
    }
%}.
    ^ self primitiveFailed
!

isWritable:aPathName
    "return true, if the given file is writable.
     For symbolic links, the pointed-to-file is checked."

%{
    int ret;

    if (__isString(aPathName)) {
        __BEGIN_INTERRUPTABLE__
        do {
            ret = access(__stringVal(aPathName), W_OK);
        } while ((ret < 0) && (errno == EINTR));
        __END_INTERRUPTABLE__
        if (ret < 0) {
            @global(LastErrorNumber) = __MKSMALLINT(errno);
        }
        RETURN ( ((ret == 0) ? true : false) );
    }
%}.
    ^ self primitiveFailed
!

linkInfoOf:aPathName
    "return a dictionary filled with info for the file 'aPathName',
     IFF aPathName is a symbolic link. 
     If aPathName is invalid, or its NOT a symbolic link, nil is returned.

     The contents of the dictionary gives info about the link itself,
     on contrast to #infoOf:, which returns the info of the pointed to file
     in case of a symbolic link."
     
    |info type mode uid gid size id atimeLow atimeHi mtimeLow mtimeHi ctimeLow ctimeHi
     path|

%{  /* STACK: 1200 */
#ifdef S_IFLNK
    struct stat buf;
    int ret;
    char pathBuffer[1024];

    if (__isString(aPathName)) {
        __BEGIN_INTERRUPTABLE__
        do {
            ret = lstat((char *) __stringVal(aPathName), &buf);
        } while ((ret < 0) && (errno == EINTR));
        __END_INTERRUPTABLE__

        if (ret < 0) {
            @global(LastErrorNumber) = __MKSMALLINT(errno);
            RETURN ( nil );
        }
        switch (buf.st_mode & S_IFMT) {
            default:
                RETURN ( nil ); /* not a symbolic link */

            case S_IFLNK:
                type = @symbol(symbolicLink);
                break;
        }

        mode = __MKSMALLINT(buf.st_mode & 0777);
        uid = __MKSMALLINT(buf.st_uid);
        gid = __MKSMALLINT(buf.st_gid);
        size = __MKSMALLINT(buf.st_size);
        id = __MKSMALLINT(buf.st_ino);
        atimeLow = __MKSMALLINT(buf.st_atime & 0xFFFF);
        atimeHi = __MKSMALLINT((buf.st_atime >> 16) & 0xFFFF);
        mtimeLow = __MKSMALLINT(buf.st_mtime & 0xFFFF);
        mtimeHi = __MKSMALLINT((buf.st_mtime >> 16) & 0xFFFF);
        ctimeLow = __MKSMALLINT(buf.st_ctime & 0xFFFF);
        ctimeHi = __MKSMALLINT((buf.st_ctime >> 16) & 0xFFFF);
        if ((ret = readlink((char *) __stringVal(aPathName), pathBuffer, sizeof(pathBuffer))) < 0) {
            OperatingSystem_LastErrorNumber = __MKSMALLINT(errno);
            RETURN ( nil );
        } 
        pathBuffer[ret] = '\0';  /* readlink does not 0-terminate */
        path = __MKSTRING(pathBuffer COMMA_CON);
    }
#else
    RETURN ( nil );
#endif
%}.
    mode notNil ifTrue:[
        info := IdentityDictionary new.
        info at:#type put:type.
        info at:#mode put:mode.
        info at:#uid put:uid.
        info at:#gid put:gid.
        info at:#size put:size.
        info at:#id put:id.
        info at:#path put:path.
        info at:#accessed      put:(AbsoluteTime fromOSTimeLow:atimeLow and:atimeHi).
        info at:#modified      put:(AbsoluteTime fromOSTimeLow:mtimeLow and:mtimeHi).
        info at:#statusChanged put:(AbsoluteTime fromOSTimeLow:ctimeLow and:ctimeHi).
        ^ info
   ].
   ^ self primitiveFailed

   "
    OperatingSystem infoOf:'Make.proto'  
    OperatingSystem linkInfoOf:'Make.proto' 

    OperatingSystem infoOf:'resources/motif.style'   
    OperatingSystem linkInfoOf:'resources/motif.style' 
   "
!

parentDirectoryName
    "return the name used to refer to parent directories.
     In MSDOS, Unix and other systems this is '..', but maybe different
     for other systems.
     (but those are currently not supported - so this is some
      preparation for the future)"

    ^ '..'
!

pathNameOf:pathName
    "return the pathName of the argument, aPathString,
     - thats the full pathname of the directory, starting at '/'.
     This method needs the path to be valid
     (i.e. all directories must exist, be readable and executable).
     Notice: if symbolic links are involved, the result may look different
     from what you expect."

    |p path command|

    "some systems have a convenient function for this ..."
    path := self primPathNameOf:pathName.

    path isNil ifTrue:[
	(self isValidPath:pathName) ifFalse:[^ pathName].

	(SlowFork==true or:[ForkFailed]) ifFalse:[
	    PipeStream openErrorSignal handle:[:ex |
		ForkFailed := true.
		'OS: cannot fork/popen' errorPrintNL.
		ex return.
	    ] do:[
		"have to fall back ..."
		command := 'cd ' , pathName , '; pwd'.
		p := PipeStream readingFrom:command.
	    ].

	    (p isNil or:[p atEnd]) ifTrue:[
		('OS: PipeStream for <' , command , '> failed') errorPrintNL.
	    ] ifFalse:[
		path := p nextLine.
		p close.
	    ].
	].
	path isNil ifTrue:[
	    path := pathName
	].
	(SlowFork==true or:[ForkFailed]) ifTrue:[
	    path := self compressPath:path
	]
    ].
    ^ path.

    "
     OperatingSystem pathNameOf:'.'
     OperatingSystem pathNameOf:'../smalltalk/../smalltalk'
     OperatingSystem pathNameOf:'../../..'
     OperatingSystem pathNameOf:'..'
    "
!

primPathNameOf:pathName
    "return the pathName of the argument, aPathString,
     - thats the full pathname of the directory, starting at '/'.
     This method here returns nil, if the OS does not provide a
     realPath library function.
     Notice: if symbolic links are involved, the result may look different
     from what you expect."

    |path|

    "some systems have a convenient function for this ..."
%{  /* STACK: 16000 */
#ifdef HAS_REALPATH
    char nameBuffer[MAXPATHLEN + 1];

    if (__isString(pathName)) {
	if (realpath(__stringVal(pathName), nameBuffer)) {
	    RETURN ( __MKSTRING(nameBuffer COMMA_CON) );
	}
    }
#endif
%}.
    ^ nil
!

timeOfLastAccess:aPathName
    "return the time, when the file was last accessed.
     For nonexistent files, nil is returned."

    "could be implemented as:
	(self infoOf:aPathName) at:#accessed 
    "
    |timeLow timeHi|
%{
    struct stat buf;
    time_t mtime;
    int ret;

    if (__isString(aPathName)) {
	__BEGIN_INTERRUPTABLE__
	do {
	    ret = stat((char *) __stringVal(aPathName), &buf);
	} while (ret < 0 && errno == EINTR);
	__END_INTERRUPTABLE__
	if (ret < 0) {
	    OperatingSystem_LastErrorNumber = __MKSMALLINT(errno);
	    RETURN (nil);
	}
	timeLow = __MKSMALLINT(buf.st_atime & 0xFFFF);
	timeHi = __MKSMALLINT((buf.st_atime >> 16) & 0xFFFF);
    }
%}.
    timeLow notNil ifTrue:[^ AbsoluteTime fromOSTimeLow:timeLow and:timeHi].
    ^ self primitiveFailed

    "
     OperatingSystem timeOfLastAccess:'/'
    "
!

timeOfLastChange:aPathName
    "return the time, when the file was last changed. 
     For nonexistent files, nil is returned."

    "could be implemented as:
	(self infoOf:aPathName) at:#modified
    "

    |timeLow timeHi|
%{
    struct stat buf;
    int ret;
    time_t mtime;

    if (__isString(aPathName)) {
	__BEGIN_INTERRUPTABLE__
	do {
	    ret = stat((char *) __stringVal(aPathName), &buf);
	} while (ret < 0 && errno == EINTR);
	__END_INTERRUPTABLE__
	if (ret < 0) {
	    OperatingSystem_LastErrorNumber = __MKSMALLINT(errno);
	    RETURN ( nil );
	}
	timeLow = __MKSMALLINT(buf.st_mtime & 0xFFFF);
	timeHi = __MKSMALLINT((buf.st_mtime >> 16) & 0xFFFF);
    }
%}.
    timeLow notNil ifTrue:[^ AbsoluteTime fromOSTimeLow:timeLow and:timeHi].
    ^ self primitiveFailed

    "
     OperatingSystem timeOfLastChange:'/'
    "
!

typeOf:aPathName
    "return the type of a file as a symbol"

    "
     this could have been implemented as:
	(self infoOf:aPathName) at:#type 
     but for huge directory searches the code below is faster
    "

%{
    struct stat buf;
    int ret;

    if (__isString(aPathName)) {
	__BEGIN_INTERRUPTABLE__
	do {
	    ret = stat((char *) __stringVal(aPathName), &buf);
	} while (ret < 0 && errno == EINTR);
	__END_INTERRUPTABLE__
	if (ret < 0) {
	    OperatingSystem_LastErrorNumber = __MKSMALLINT(errno);
	    RETURN ( nil );
	}
	switch (buf.st_mode & S_IFMT) {
	    case S_IFDIR:
		RETURN ( @symbol(directory) );
	    case S_IFREG:
		RETURN ( @symbol(regular) );
#ifdef S_IFCHR
	    case S_IFCHR:
		RETURN ( @symbol(characterSpecial) );
#endif
#ifdef S_IFBLK
	    case S_IFBLK:
		RETURN ( @symbol(blockSpecial) );
#endif
#ifdef S_IFLNK
	    case S_IFLNK:
		RETURN ( @symbol(symbolicLink) );
#endif
#ifdef S_IFSOCK
	    case S_IFSOCK:
		RETURN ( @symbol(socket) );
#endif
#ifdef S_IFIFO
	    case S_IFIFO:
		RETURN ( @symbol(fifo) );
#endif
	    default:
		RETURN ( @symbol(unknown) );
	}
    }
%}.
    ^ self primitiveFailed

    "
     OperatingSystem typeOf:'/' 
     OperatingSystem typeOf:'.' 
     OperatingSystem typeOf:'Make.proto' 
    "
! !

!OperatingSystem class methodsFor:'interrupts & signals'!

blockInterrupts
    "disable interrupt processing - if disabled, incoming
     interrupts will be registered and handled as soon as
     interrupts are reenabled by OperatingSystemclass>>unblockInterrupts.
     Returns the previous blocking status i.e. true if interrupts
     where already blocked. You need this information for proper
     unblocking, in case of nested block/unblock calls."

%{  /* NOCONTEXT */
    RETURN ( __BLOCKINTERRUPTS() );
%}
!

defaultSignal:signalNumber
    "revert to the default action on arrival of a (Unix-)signal.
     Dont confuse Unix signals with smalltalk signals.
     WARNING: for some signals, it is no good idea to revert to default;
     for example, the default for SIGINT (i.e. ^C) is to exit; while the
     default for SIGQUIT (^ \) is to dump core.
     Also, NOTICE that signal numbers are not portable between unix
     systems - use OperatingSystem sigXXX to get the numeric value for
     a signal."

%{  /* NOCONTEXT */

    if (__isSmallInteger(signalNumber)) {
#ifdef SIG_DFL
	signal(__intVal(signalNumber), SIG_DFL);
	RETURN (self);
#endif
    }
%}.
    "
     this error is triggered on non-integer argument
    "
    ^ self primitiveFailed

    "you better save a snapshot image before trying this ..."
    "
     'if you hit ^C now, Smalltalk will exit immediately' printNewline.
     OperatingSystem defaultSignal:(OperatingSystem sigINT).
     1 to:1000000 do:[:i| ].
     OperatingSystem enableSignal:(OperatingSystem sigINT).
     'normal ^C handling again.' printNewline
    "
!

disableChildSignalInterrupts
    "disable childSignal interrupts 
     (SIGCHLD, if the architecture supports it).
     We have to set the signal back to default, because ignoring 
     SIGCHLD breaks wait & co"

    ^ self defaultSignal:(self sigCHLD)

    "Created: 5.1.1996 / 15:45:28 / stefan"
!

disableIOInterruptsOn:fd
    "turn off IO interrupts for a filedescriptor"

%{  /* NOCONTEXT */

    int ret, flags, f;

#if (defined(F_GETFL) && defined(F_SETFL) && defined(FASYNC)) || defined(SYSV4)
    if (__isSmallInteger(fd)) {
	f = __intVal(fd);
# if defined(SYSV4)
	ret = ioctl(f, I_SETSIG, 0);
# else /*! SYSV4*/
	flags = fcntl(f, F_GETFL, 0);
	/*
	 * if already clear, there is no need for this syscall ...
	 */
	if (flags & FASYNC) {
	    ret = fcntl(f, F_SETFL, flags & ~FASYNC);
	    if (ret >= 0) ret = flags;
	} else {
	    ret = flags;
	}
# endif /* !SYSV4 */
	RETURN ( __MKSMALLINT(ret) );
    }
#endif
%}.
    "
     this error is triggered on non-integer argument
     or if the OS does not support IO interrupts.
    "
    ^ self primitiveFailed
!

disableSignal:signalNumber
    "disable (Unix-) signal processing for signalNumber.
     Dont confuse Unix signals with smalltalk signals.
     WARNING: for some signals, it is no good idea to disable
     them; for example, disabling the SIGINT signal turns off ^C
     handling.
     Also, NOTICE that signal numbers are not portable between unix
     systems - use OperatingSystem sigXXX to get the numeric value for
     a signal.
     Use only for fully debugged stand alone applications."

%{  /* NOCONTEXT */

    if (__isSmallInteger(signalNumber)) {
	int sigNo = __intVal(signalNumber);

	if (sigNo == 0) {
	    RETURN (self);
	}
#ifdef SIG_IGN
	signal(sigNo, SIG_IGN);
	RETURN (self);
#endif
    }
%}.
    "
     this error is triggered on non-integer argument
    "
    ^ self primitiveFailed

    "
     'now, ^C is totally ignored ...' printNewline.
     OperatingSystem disableSignal:(OperatingSystem sigINT).
     1 to:1000000 do:[:i| ].
     OperatingSystem enableSignal:(OperatingSystem sigINT).
     '^C handled again.' printNewline
    "
!

disableTimer
    "disable timer interrupts.
     WARNING: 
	the system will not operate correctly with timer interrupts
	disabled, because no scheduling or timeouts are possible."

%{  /* NOCONTEXT */

#if defined(ITIMER_REAL)
    struct itimerval dt;

    dt.it_interval.tv_sec = 0;
    dt.it_interval.tv_usec = 0;
    dt.it_value.tv_sec = 0;
    dt.it_value.tv_usec = 0;
    setitimer(ITIMER_REAL, &dt, 0);
    RETURN (true);
#endif /* ITIMER_REAL */
%}
.
    ^ false
!

disableUserInterrupts
    "disable userInterrupt processing;
     when disabled, no ^C processing takes place.
     WARNING:
	 If at all, use this only for debugged stand-alone applications, since
	 no exit to the debugger is possible with user interrupts disabled.
	 We recommend setting up a handler for the signal instead of disabling it."

    self disableSignal:(self sigBREAK).
    self disableSignal:(self sigINT).
!

enableAbortInterrupts
    "enable abort signalhandling, and make it a regular signalInterrupt.
     (the default will dump core and exit - which is not a good idea for
      end-user applications ...).
     This is especially useful, if linked-in C-libraries call abort() ..."

    self enableSignal:(self sigABRT)
!

enableChildSignalInterrupts
    "enable childSignal interrupts 
     (SIGCHLD, if the architecture supports it).
     After enabling, these signals will send the message 
     'childSignalInterrupt' to the ChildSignalInterruptHandler object."

    self enableSignal:(self sigCHLD)
!

enableFpExceptionInterrupts
    "enable floating point exception interrupts (if the
     architecture supports it).
     After enabling, fpu-exceptions will send the message 
     'fpuExceptionInterrupt' to the FPUExceptionInterruptHandler object."

    self enableSignal:(self sigFP)
!

enableHardSignalInterrupts
    "enable hard signal exception interrupts (trap, buserror & segm. violation).
     After enabling, these exceptions will send the message 
     'signalInterrupt' to the SignalInterruptHandler object."

    "/ leads to trouble ...
"/    self enableSignal:(self sigPIPE).

    "/ ... better to ignore them, and let it be handled as a writeErrorSignal.
    self disableSignal:(self sigPIPE).

    self enableSignal:(self sigBUS).
    self enableSignal:(self sigSEGV)
!

enableIOInterruptsOn:fd
    "turn on IO interrupts for a filedescriptor"

%{  /* NOCONTEXT */

    int ret, flags, f;
#ifndef __signalIoInterrupt
    extern void __signalIoInterrupt();
#endif
    static int firstCall = 1;

#if (defined(F_GETFL) && defined(F_SETFL) && defined(FASYNC)) || defined(SYSV4)
/*
 * SIGIO/SIGPOLL - data available for I/O
 * (used to wake up waiting processes)
 */
#ifdef SIGIO
# define THESIGNAL SIGIO
#else
# ifdef SIGPOLL
#  define THESIGNAL SIGPOLL
# else
#  ifdef SIGURG
#   define THESIGNAL SIGURG
#  endif
# endif
#endif

    if (__isSmallInteger(fd)) {
	if (firstCall) {
#ifdef HAS_SIGACTION
	    struct sigaction act;

            /*
             * Do not add SA_RESTART here. A signal can cause a
             * thread switch, another thread can do a garbage collect
             * and restarted system calls may write into old
             * (collected) addresses.
             */

	    act.sa_flags = SA_SIGINFO; /* <- if you add more, remember dummys at the top */
	    sigemptyset(&act.sa_mask);
	    act.sa_handler = __signalIoInterrupt;
	    sigaction(THESIGNAL, &act, 0);
#else
# ifdef HAS_SIGVEC
	    struct sigvec vec;

	    vec.sv_flags = SV_INTERRUPT;
	    sigemptyset(&vec.sv_mask);
	    vec.sv_handler = __signalIoInterrupt;
	    sigvec(THESIGNAL, &vec, NULL);
# else
	    signal(THESIGNAL, __signalIoInterrupt);
# endif
#endif
	    firstCall = 0;
	}
#undef THESIGNAL

	f = __intVal(fd);
# if defined(SYSV4)
	ret = ioctl(f, I_SETSIG, S_INPUT | S_HIPRI | S_ERROR | S_RDNORM | S_RDBAND | S_MSG | S_HANGUP);
# else /*! SYSV4*/
	flags = fcntl(f, F_GETFL, 0);
	/*
	 * if already set, there is no need for this syscall ...
	 */
	if (flags & FASYNC) {
	    ret = flags;
	} else {
	    ret = fcntl(f, F_SETFL, flags | FASYNC);
	    if (ret >= 0) ret = flags;
	}
# endif /*!SYSV4*/
	RETURN ( __MKSMALLINT(ret) );
    }
#endif
%}.
    "
     this error is triggered on non-integer argument
     or if the system does not support SIGIO
    "
    ^ self primitiveFailed
!

enableQuitInterrupts
    "enable quitInterrupt (usually ^\) handling, and make it a userInterrupt.
     (the default will dump core and exit - which is not a good idea for
      end-user applications ...)"

    self enableSignal:(self sigQUIT)
!

enableSignal:signalNumber
    "enable (Unix-)signal processing for signalNumber.
     Dont confuse Unix signals with smalltalk signals.
     The signal will be delivered to one of the standard handlers
     (SIGINT, SIGQUIT, etc) or to a general handler, which
     sends #signalInterrupt:.

     NOTICE that signal numbers are not portable between unix
     systems - use OperatingSystem sigXXX to get the numeric value for
     a signal."

%{  /* NOCONTEXT */

#ifdef NSIG
# define SIG_LIMIT NSIG
#else
# ifdef SIGUSR2
#  define SIG_LIMIT SIGUSR2
# else
#  ifdef SIGUSR
#   define SIG_LIMIT SIGUSR
#  endif
# endif
#endif

#if defined(SIGPOLL) && !defined(SIGIO)
# define SIGIO SIGPOLL
#endif

#ifdef SIGCHLD
# define CHILD_SIGNAL   SIGCHLD
#else
# ifdef SIGCLD
#  define CHILD_SIGNAL  SIGCLD
# endif
#endif

    int sigNr;
#if defined(SIGINT) || defined(SIGQUIT)
# ifndef __signalUserInterrupt
    extern void __signalUserInterrupt(SIGHANDLER_ARG);
# endif
#endif
#ifdef SIGFPE
# ifndef __signalFpExceptionInterrupt
    extern void __signalFpExceptionInterrupt(SIGHANDLER_ARG);
# endif
#endif
#ifdef SIGIO
# ifndef __signalIoInterrupt
    extern void __signalIoInterrupt(SIGHANDLER_ARG);
# endif
#endif
#ifdef CHILD_SIGNAL
# ifndef __signalChildInterrupt
    extern void __signalChildInterrupt(SIGHANDLER_ARG);
# endif
#endif
#ifdef SIGPIPE
# ifndef __signalPIPEInterrupt
    extern void __signalPIPEInterrupt(SIGHANDLER_ARG);
# endif
#endif
#ifdef SIGBUS
# ifndef __signalBUSInterrupt
    extern void __signalBUSInterrupt(SIGHANDLER_ARG);
# endif
#endif
#ifdef SIGSEGV
# ifndef __signalSEGVInterrupt
    extern void __signalSEGVInterrupt(SIGHANDLER_ARG);
# endif
#endif
#ifdef SIGALRM
# ifndef WIN32
#  ifndef __signalTimerInterrupt
    extern void __signalTimerInterrupt(SIGHANDLER_ARG);
#  endif
# endif
#endif
#ifndef __signalInterrupt
    extern void __signalInterrupt(SIGHANDLER_ARG);
#endif
    void (*handler)(SIGHANDLER_ARG);

    if (__isSmallInteger(signalNumber)
     && ((sigNr = __intVal(signalNumber)) >= 0)
#ifdef SIG_LIMIT
     &&  (sigNr <= SIG_LIMIT)
#endif
    ) {
	/*
	 * standard signals are forced into standard handlers
	 * - all others go into general signalInterrupt
	 */
#if defined(SIGPOLL) && defined(SIGIO)
	if (sigNr == SIGPOLL)
	    sigNr = SIGIO;
#endif
	switch (sigNr) {
	    case 0:
		/* enabling a non-supported signal */
		RETURN (self);

#ifdef SIGBREAK
	    case SIGBREAK:
#endif
#ifdef SIGINT
	    case SIGINT:
#endif
#ifdef SIGQUIT
	    case SIGQUIT:
#endif
#if defined(SIGINT) || defined(SIGQUIT) || defined(SIGBREAK)
		handler = __signalUserInterrupt;
		break;
#endif

#ifdef SIGFPE
	    case SIGFPE:
		handler = __signalFpExceptionInterrupt;
		break;
#endif

#ifdef SIGPIPE
	    case SIGPIPE:
		handler = __signalPIPEInterrupt;
		break;
#endif
#ifdef SIGBUS
	    case SIGBUS:
		handler = __signalBUSInterrupt;
		break;
#endif
#ifdef SIGSEGV
	    case SIGSEGV:
		handler = __signalSEGVInterrupt;
		break;
#endif
#ifdef SIGIO
	    case SIGIO:
		handler = __signalIoInterrupt;
		break;
#endif

#ifdef CHILD_SIGNAL
	    case CHILD_SIGNAL:
		handler = __signalChildInterrupt;
		break;
#endif
#ifdef SIGALRM
# ifndef WIN32
	    case SIGALRM:
		handler = __signalTimerInterrupt;
		break;
# endif
#endif

	    default:
		handler = __signalInterrupt;
		break;
	}

	{
#ifdef HAS_SIGACTION
	    struct sigaction act;

            /*
             * Do not add SA_RESTART here. A signal can cause a
             * thread switch, another thread can do a garbage collect
             * and restarted system calls may write into old
             * (collected) addresses.
             */

	    act.sa_flags = SA_SIGINFO; /* <- if you add more, remember dummys at the top */
	    sigemptyset(&act.sa_mask);
	    act.sa_handler = handler;
	    sigaction(sigNr, &act, 0);
#else
# ifdef HAS_SIGVEC
	    struct sigvec vec;

	    vec.sv_flags = SV_INTERRUPT;
	    sigemptyset(&vec.sv_mask);
	    vec.sv_handler = handler;
	    sigvec(sigNr, &vec, NULL);
# else
	    (void) signal(sigNr, handler);
# endif
#endif
	}

	/*
	 * maybe, we should return the old enable-status
	 * as boolean here ...
	 */
	RETURN (self);
    }
%}.

    "
     this error is triggered on non-integer argument, or
     if the signal number is not in the valid range (1..NSIG)
    "
    ^ self primitiveFailed
!

enableTimer:milliSeconds
    "trigger a timerInterrupt, to be signalled after some (real) time."

%{  /* NOCONTEXT */
    int millis;

    millis = __intVal(milliSeconds);

#if defined(ITIMER_REAL)
    {
	static int firstCall = 1;
	struct itimerval dt;
#ifndef __signalTimerInterrupt
	extern void __signalTimerInterrupt(SIGHANDLER_ARG);
#endif

	if (firstCall) {
# ifdef HAS_SIGACTION
	    struct sigaction act;

	    act.sa_flags = SA_SIGINFO; /* <- if you add more, remember dummys at the top */
	    sigemptyset(&act.sa_mask);
	    act.sa_handler = __signalTimerInterrupt;
	    sigaction(SIGALRM, &act, 0);
# else
#  ifdef HAS_SIGVEC
	    struct sigvec vec;

	    vec.sv_flags = SV_INTERRUPT;
	    sigemptyset(&vec.sv_mask);
	    vec.sv_handler = __signalTimerInterrupt;
	    sigvec(SIGALRM, &vec, NULL);
#  else
	    signal(SIGALRM, __signalTimerInterrupt);
#  endif
# endif
	    firstCall = 0;
	}

	dt.it_interval.tv_sec = 0;
	dt.it_interval.tv_usec = 0;
	dt.it_value.tv_sec = millis / 1000;
	dt.it_value.tv_usec = (millis % 1000) * 1000;  
	setitimer(ITIMER_REAL, &dt, 0);
	RETURN (true);
    }
#else
# ifdef WIN32
    {
	/*
	 * two mechanisms - which one to use ?
	 */
#  define USE_TimerProc
#  ifdef USE_TimerProc

	extern VOID CALLBACK __win32TimerProc(HWND, UINT, UINT, DWORD);
	SetTimer(NULL, 0, millis, __win32TimerProc);

#  else /* USE_TimeProc */

	extern void CALLBACK __win32TimeProc(UINT, UINT, DWORD, DWORD, DWORD, DWORD);
	timeSetEvent(millis, 5, __win32TimeProc, 0, TIME_ONESHOT);
#  endif
    }
# endif /* WIN32 */
#endif /* ITIMER_REAL */
%}.
    ^ false
!

enableUserInterrupts
    "enable userInterrupt (^C) handling;
     when enabled, ^C in the terminal window will send the message
     'userInterrupt' to the UserInterruptHandler object."

    self enableSignal:(self sigINT).
    self enableSignal:(self sigBREAK).
!

interruptPending
    "return true, if an interrupt is pending. The returned value is
     invalid if interrupts are not currently blocked, since otherwise 
     the interrupt is usually already handled before arriving here,
     or may be served while returning from here."

%{   /* NOCONTEXT */
     extern OBJ __INTERRUPTPENDING();

     RETURN ( __INTERRUPTPENDING() );
%}        
!

killProcess:processId
    "kill a unix process.
     The process terminates immediately and has no chance to perform any cleanup actions.

     WARNING: in order to avoid zombie processes (on unix),
	      you have to fetch the processes exitstatus with
	      OperatingSystem>>getStatusOfProcess:aProcessId."

    self sendSignal:(self sigKILL) to:processId.

    "Modified: 28.12.1995 / 15:06:18 / stefan"
!

nameForSignal:aSignalNumber
    "for a given Unix signalnumber, return a descriptive string"

    aSignalNumber == self sigHUP    ifTrue:[^ 'hangup'].
    aSignalNumber == self sigINT    ifTrue:[^ 'interrupt'].
    aSignalNumber == self sigKILL   ifTrue:[^ 'kill'].
    aSignalNumber == self sigQUIT   ifTrue:[^ 'quit'].
    aSignalNumber == self sigILL    ifTrue:[^ 'illegal instruction'].
    aSignalNumber == self sigTRAP   ifTrue:[^ 'trap'].
    aSignalNumber == self sigABRT   ifTrue:[^ 'abort'].
    aSignalNumber == self sigIOT    ifTrue:[^ 'iot trap'].
    aSignalNumber == self sigEMT    ifTrue:[^ 'emt trap'].
    aSignalNumber == self sigFP     ifTrue:[^ 'fp exception'].
    aSignalNumber == self sigBUS    ifTrue:[^ 'bus error'].
    aSignalNumber == self sigSEGV   ifTrue:[^ 'segmentation violation'].
    aSignalNumber == self sigSYS    ifTrue:[^ 'bad system call'].
    aSignalNumber == self sigPIPE   ifTrue:[^ 'broken pipe'].
    aSignalNumber == self sigALRM   ifTrue:[^ 'alarm timer'].
    aSignalNumber == self sigTERM   ifTrue:[^ 'termination'].
    aSignalNumber == self sigSTOP   ifTrue:[^ 'stop'].
    aSignalNumber == self sigTSTP   ifTrue:[^ 'tty stop'].
    aSignalNumber == self sigCONT   ifTrue:[^ 'continue'].
    aSignalNumber == self sigCHLD   ifTrue:[^ 'child death'].
    aSignalNumber == self sigTTIN   ifTrue:[^ 'background tty input'].
    aSignalNumber == self sigTTOU   ifTrue:[^ 'background tty output'].
    aSignalNumber == self sigIO     ifTrue:[^ 'io available'].
    aSignalNumber == self sigXCPU   ifTrue:[^ 'cpu time expired'].
    aSignalNumber == self sigXFSZ   ifTrue:[^ 'file size limit'].
    aSignalNumber == self sigVTALRM ifTrue:[^ 'virtual alarm timer'].
    aSignalNumber == self sigPROF   ifTrue:[^ 'profiling timer'].
    aSignalNumber == self sigWINCH  ifTrue:[^ 'winsize changed'].
    aSignalNumber == self sigLOST   ifTrue:[^ 'resource lost'].
    aSignalNumber == self sigUSR1   ifTrue:[^ 'user signal 1'].
    aSignalNumber == self sigUSR2   ifTrue:[^ 'user signal 2'].
    aSignalNumber == self sigMSG    ifTrue:[^ 'HFT message'].
    aSignalNumber == self sigPWR    ifTrue:[^ 'power-fail'].
    aSignalNumber == self sigPRE    ifTrue:[^ 'programming exception'].
    aSignalNumber == self sigGRANT  ifTrue:[^ 'HFT access wanted'].
    aSignalNumber == self sigRETRACT ifTrue:[^ 'HFT access relinquish'].
    aSignalNumber == self sigSOUND   ifTrue:[^ 'HFT sound complete'].
    aSignalNumber == self sigDANGER  ifTrue:[^ 'low on paging space'].

    "notice: many systems map SIGPOLL and/or SIGUSR onto SIGIO
	     therefore, keep SIGIO always above the two below" 
    aSignalNumber == self sigPOLL   ifTrue:[^ 'io available'].
    aSignalNumber == self sigURG    ifTrue:[^ 'urgent'].

    ^ 'unknown signal'

    "
     OperatingSystem nameForSignal:9
     OperatingSystem nameForSignal:(OperatingSystem sigPOLL) 
    "
!

operatingSystemSignal:signalNumber
    "return the signal to be raised when an 
     operatingSystem-signal occurs, or nil"

    OSSignals notNil ifTrue:[
	^ OSSignals at:signalNumber ifAbsent:[nil]
    ].
    ^ nil
!

operatingSystemSignal:signalNumber install:aSignal
    "install a signal to be raised when an operatingSystem-signal occurs"

    OSSignals isNil ifTrue:[
	OSSignals := Array new:32
    ].
    OSSignals at:signalNumber put:aSignal
!

sendSignal:signalNumber to:processId
    "send a unix signal to some process (maybe myself).
     Returns false if any error occurred, true otherwise.

     Do not confuse UNIX signals with Smalltalk-Signals.

     WARNING: in order to avoid zombie processes (on unix),
	      you may have to fetch the processes exitstatus with
	      OperatingSystem>>getStatusOfProcess:aProcessId
	      if the signal terminates that process."

%{
#ifndef MSDOS_LIKE
    if (__bothSmallInteger(signalNumber, processId)) {
	if (kill(__intVal(processId), __intVal(signalNumber)) < 0) {
	    OperatingSystem_LastErrorNumber = __MKSMALLINT(errno);
	    RETURN ( false );
	}
	RETURN ( true );
    }
#endif
%}.
    "/
    "/ either invalid argument (non-integers)
    "/ or not supported by OS
    "/
    ^ self primitiveFailed
!

startSpyTimer
    "trigger a spyInterrupt, to be signalled after some short (virtual) time.
     This is used by the old MessageTally for profiling.
     Should be changed to use real profiling timer if available.
     On systems, where no virtual timer is available, use the real timer
     (which is of course less correct).
     OBSOLETE: the new messageTally runs as a high prio process, not using 
	       spy interrupts."

%{  /* NOCONTEXT */

#ifndef __spyInterrupt
    extern void __spyInterrupt();
#endif
#if defined(ITIMER_VIRTUAL)
    struct itimerval dt;

# ifdef SIGVTALRM
    signal(SIGVTALRM, __spyInterrupt);
# else
    signal(SIGALRM, __spyInterrupt);
# endif

    dt.it_interval.tv_sec = 0;
    dt.it_interval.tv_usec = 0;
    dt.it_value.tv_sec = 0;
    dt.it_value.tv_usec = 1000;   /* 1000 Hz */
    setitimer(ITIMER_VIRTUAL, &dt, 0);

    RETURN (true);
#endif
%}
.
    ^ false
!

stopSpyTimer
    "stop spy timing - disable spy timer.
     OBSOLETE: the new messageTally runs as a high prio process, not using 
	       spy interrupts."

%{  /* NOCONTEXT */

#if defined(ITIMER_VIRTUAL)
    struct itimerval dt;

    dt.it_interval.tv_sec = 0;
    dt.it_interval.tv_usec = 0;
    dt.it_value.tv_sec = 0;
    dt.it_value.tv_usec = 0;
    setitimer(ITIMER_VIRTUAL, &dt, 0);
    RETURN (true);
#endif
%}
.
    ^ false
!

terminateProcess:processId
    "terminate a unix process.
     The process has a chance to do some cleanup.

     WARNING: in order to avoid zombie processes (on unix),
	      you may have to fetch the processes exitstatus with
	      OperatingSystem>>getStatusOfProcess:aProcessId."

    self sendSignal:(self sigTERM) to:processId.

    "Modified: 28.12.1995 / 15:05:37 / stefan"
!

terminateProcessGroup:processGroupId
    "terminate a unix process group.
     The process has a chance to do some cleanup.

     WARNING: in order to avoid zombie processes (on unix),
              you may have to fetch the processes exitstatus with
              OperatingSystem>>getStatusOfProcess:aProcessId."

    self sendSignal:(self sigTERM) to:(processGroupId negated).

    "Modified: 28.12.1995 / 15:05:37 / stefan"
    "Created: 23.4.1996 / 16:40:34 / stefan"
!

unblockInterrupts
    "enable interrupt processing - if any interrupts are pending,
     these will be handled immediately.
     When unblocking interrupts, take care of nested block/unblock
     calls - you must only unblock after a blockcall if they where
     really not blocked before. See OperatingSystemclass>>blockInterrupts."
%{
    __UNBLOCKINTERRUPTS();
    RETURN (nil);
%}
! !

!OperatingSystem class methodsFor:'misc'!

exit
    "shutdown smalltalk immediately - this method does not return.
     Return 'good'-status (0) to the parent unix process."

%{  /* NOCONTEXT */
    __mainExit(0);
%}
    "OperatingSystem exit - dont evaluate this"
!

exit:exitCode
    "shutdown smalltalk immediately -
     returning an exit-code to the parent unix process."

%{  /* NOCONTEXT */
    int code = 1;

    if (__isSmallInteger(exitCode)) {
	code = __intVal(exitCode);
    }
    __mainExit(code);
%}
    "OperatingSystem exit:1 - dont evaluate this"
!

exitWithCoreDump
    "shutdown smalltalk immediately - dumping core.
     This always returns 'bad'-status to the parent unix process.
     Notice, that no cleanup is performed at all - you may have to
     manually remove any tempfiles.
     Use this only for debugging ST/X itself"

%{  /* NOCONTEXT */
    abort();
%}
    "
     OperatingSystem exitWithCoreDump - dont evaluate this
    "
!

slowFork:aBoolean
    "set/clear the `avoid-fork-if-possible-because-its-slow' flag.
     Only used internally on SYSV3 systems"

    SlowFork := aBoolean

    "Modified: 22.4.1996 / 13:13:09 / cg"
! !

!OperatingSystem class methodsFor:'os queries'!

getCPUDefine
    "return a string which was used to identify this CPU type when STX was
     compiled, and which should be passed down when compiling methods.
     For example, on linux, this may be '-Di386'; on a vax, this would be '-Dvax'.
     This is normally not of interest to 'normal' users; however, it is passed
     down to the c-compiler when methods are incrementally compiled to machine code."

%{  /* NOCONTEXT */
#   ifndef CPU_DEFINE
#       define CPU_DEFINE "-DunknownCPU"
#   endif

    RETURN ( __MKSTRING(CPU_DEFINE COMMA_CON));
%}
    "
     OperatingSystem getCPUDefine
    "
!

getCPUType
    "return a string giving the type of machine we're running on.
     Here, the machine for which ST/X was compiled is returned
     (i.e. for all x86's, the same i386 is returned).
     This may normally not be of any interest to you ..."

    |cpu|
    
%{  /* NOCONTEXT */

#   ifdef vax
#    define CPU_STRING "vax"
#   endif
#   ifdef mips
#    define CPU_STRING "mips"
#   endif
#   ifdef i386
#    define CPU_STRING "i386"
#   endif
#   ifdef ns32k
#    define CPU_STRING "ns32k"
#   endif
#   ifdef mc68k
#    define CPU_STRING "mc68k"
#   endif
#   ifdef mc88k
#    define CPU_STRING "mc88k"
#   endif
#   ifdef sparc
#    define CPU_STRING "sparc"
#   endif
#   ifdef hppa
#    define CPU_STRING "hppa"
#   endif
#   ifdef rs6000
#    define CPU_STRING "rs6000"
#   endif
#   ifdef powerPC
#    define CPU_STRING "powerPC"
#   endif
#   ifdef alpha
#    define CPU_STRING "alpha"
#   endif
#   ifdef transputer
#    define CPU_STRING "transputer"
#   endif
#   ifdef ibm370
#    define CPU_STRING "ibm370"
#   endif

#   ifndef CPU_STRING
#    define CPU_STRING "unknown"
#   endif

    cpu = __MKSTRING(CPU_STRING COMMA_CON);
#   undef CPU_STRING
%}.
    ^ cpu

    "
     OperatingSystem getCPUType
    "

    "examples: are we running on a ss-10/solaris ?"
    "
     (OperatingSystem getCPUType = 'sparc') 
     and:[OperatingSystem getOSType = 'solaris']
    "

    "or on a pc/solaris ?"
    "
     (OperatingSystem getCPUType = 'i386')
     and:[OperatingSystem getOSType = 'solaris']
    "
!

getCommandOutputFrom:aCommand
    "execute a simple command (such as hostname) and
     return the commands output as a string"

    |p result|

    ForkFailed ifFalse:[
	PipeStream openErrorSignal handle:[:ex |
	    ForkFailed := true.
	    'OS: cannot fork/popen' errorPrintNL.
	    ex return.
	] do:[
	    p := PipeStream readingFrom:aCommand.
	    p notNil ifTrue:[
		result := p nextLine.
		p close
	    ]
	]
    ].
    ^ result
!

getDomainName
    "return the domain this host is in.
     Notice:
	not all systems support this; on some, 'unknown' is returned."

    |name|

    DomainName notNil ifTrue:[
	^ DomainName
    ].

%{  /* STACK: 2048 */
#if defined(HAS_GETDOMAINNAME)
    char buffer[128];

    if (getdomainname(buffer, sizeof(buffer)) == 0) {
	name = __MKSTRING(buffer COMMA_CON);
    }
#else
# if defined(HAS_UNAME) && defined(HAS_UTS_DOMAINNAME)
    struct utsname ubuff;

    if (uname(&ubuff) >= 0) {
	name = __MKSTRING(ubuff.domainname COMMA_CON);
    }
# endif
#endif
%}.
    name isNil ifTrue:[
	name := self getEnvironment:'DOMAIN'.
	name isNil ifTrue:[
	    name := self getCommandOutputFrom:'domainname'
	]
    ].
    name isNil ifTrue:[
	'OS: cannot find out domainname' errorPrintNL.
	name := 'unknown'.
    ].
    DomainName := name.
    ^ name

    "
     OperatingSystem getDomainName
    "
!

getEnvironment:aStringOrSymbol
    "get an environment string"

%{  /* NOCONTEXT */

    char *env;

    if (__isString(aStringOrSymbol) || __isSymbol(aStringOrSymbol)) {
	env =  (char *)getenv(__stringVal(aStringOrSymbol));
	if (env) {
	    RETURN ( __MKSTRING(env COMMA_CON) );
	}
    }
%}
.
    ^ nil

    "
     OperatingSystem getEnvironment:'LANG'
     OperatingSystem getEnvironment:'LOGIN'
     OperatingSystem getEnvironment:'HOME'
     OperatingSystem getEnvironment:'NNTPSERVER'
     OperatingSystem getEnvironment:'MAIL'
     OperatingSystem getEnvironment:'PATH'
    "
!

getHostName
    "return the hostname we are running on - if there is
     a HOST environment variable, we are much faster here ...
     Notice:
	not all systems support this; on some, 'unknown' is returned."

    |name|

    HostName notNil ifTrue:[
	^ HostName
    ].

%{  /* STACK: 2048 */
#if defined(HAS_GETHOSTNAME)
    char buffer[128];

    if (gethostname(buffer, sizeof(buffer)) == 0) {
	name = __MKSTRING(buffer COMMA_CON);
    }
#else
# if defined(HAS_UNAME)
    struct utsname ubuff;

    if (uname(&ubuff) >= 0) {
	name = __MKSTRING(ubuff.nodename COMMA_CON);
    }
# else
#  ifdef WIN32
    char buffer[128];
    int buffSize = sizeof(buffer);

    if (GetComputerName(buffer, &buffSize) == TRUE) {
	name = __MKSTRING(buffer COMMA_CON);
    }
#  endif
# endif
#endif
%}.
    name isNil ifTrue:[
	name := self getEnvironment:'HOST'.
	name isNil ifTrue:[
	    name := self getCommandOutputFrom:'hostname'
	]
    ].
    name isNil ifTrue:[
	'OS: cannot find out hostname' errorPrintNL.
	name := 'unknown'.
    ].
    HostName := name.
    ^ name

    "
     OperatingSystem getHostName
    "
!

getLocaleInfo
    "return a dictionary filled with values from the locale information;
     Not all fields may be present, depending on the OS's setup and capabilities.
     Possible fields are:
	decimalPoint                    <String>

	thousandsSep                    <String>

	internationalCurrencySymbol     <String>

	currencySymbol                  <String>

	monetaryDecimalPoint            <String>

	monetaryThousandsSeparator      <String>

	positiveSign                    <String>

	negativeSign                    <String>

	internationalFractionalDigits   <Integer>

	fractionalDigits                <Integer>

	positiveSignPrecedesCurrencySymbol      <Boolean>

	negativeSignPrecedesCurrencySymbol      <Boolean>

	positiveSignSeparatedBySpaceFromCurrencySymbol  <Boolean>

	negativeSignSeparatedBySpaceFromCurrencySymbol  <Boolean>

	positiveSignPosition                            <Symbol>
							one of: #parenthesesAround, 
								#signPrecedes, 
								#signSuceeds, 
								#signPrecedesCurrencySymbol,
								#signSuceedsCurrencySymbol

	negativeSignPosition                            <like above>

     it is up to the application to deal with undefined values.

     Notice, that (for now), the system does not use this information;
     it should be used by applications as required.
    "

    |info val|

    LocaleInfo notNil ifTrue:[
	"/ return the internal info; useful on systems which do not
	"/ support this.
	^ LocaleInfo
    ].

    info := IdentityDictionary new.
%{
    char *decimalPoint;         /* something like "." (US) or "," (german) */
    char *thousandsSep;         /* something like "," (US) or "." (german) */
    char *intCurrencySymbol;    /* international currency symbol; something like "USD "  "DM  " */
    char *currencySymbol;       /* local currency symbol;         something like "USD "  "DM  " */
    char *monDecimalPoint;      /* money: decimal point */
    char *monThousandsSep;      /* money: thousands sep */
    char *positiveSign;
    char *negativeSign;
    int   intFractDigits;       /* money: international digits after decPoint */
    int   fractDigits;          /* money: local digits after decPoint */
    int   csPosPrecedes;        /* money: 1 if currency symbol precedes a positive value; 0 if it sceeds */
    int   csNegPrecedes;        /* money: 1 if currency symbol precedes a negative value; 0 if it sceeds */
    int   csPosSepBySpace;      /* money: 1 if currency symbol should be separated by a space from a positive value; 0 if no space */
    int   csNegSepBySpace;      /* money: 1 if currency symbol should be separated by a space from a negative value; 0 if no space */
    int   csPosSignPosition;    /* money: 0: ()'s around the value & currency symbol */
    int   csNegSignPosition;    /*        1: sign precedes the value & currency symbol */
				/*        2: sign succeeds the value & currency symbol */
				/*        3: sign immediately precedes the currency symbol */
				/*        4: sign immediately suceeds the currency symbol */

#if defined(HAS_LOCALECONV)
    struct lconv *conf;

    conf = localeconv();
    if (conf) {
	decimalPoint = conf->decimal_point;
	thousandsSep = conf->thousands_sep;
	intCurrencySymbol = conf->int_curr_symbol;
	currencySymbol = conf->currency_symbol;
	monDecimalPoint = conf->mon_decimal_point;
	monThousandsSep = conf->mon_thousands_sep;
	positiveSign = conf->positive_sign;
	negativeSign = conf->negative_sign;
	intFractDigits = conf->int_frac_digits;
	fractDigits = conf->frac_digits;
	csPosPrecedes = conf->p_cs_precedes; 
	csNegPrecedes = conf->n_cs_precedes; 
	csPosSepBySpace = conf->p_sep_by_space; 
	csNegSepBySpace = conf->n_sep_by_space; 
	csPosSignPosition = conf->p_sign_posn;
	csNegSignPosition = conf->n_sign_posn;
    }
#else
    decimalPoint = (char *)0;
    thousandsSep = (char *)0;
    intCurrencySymbol = (char *)0;
    currencySymbol = (char *)0;
    monDecimalPoint = (char *)0;
    monThousandsSep = (char *)0;
    positiveSign =  (char *)0;
    negativeSign =(char *)0;
    intFractDigits = -1;
    fractDigits = -1;
    csPosPrecedes = -1; 
    csNegPrecedes = -1; 
    csPosSepBySpace = -1; 
    csNegSepBySpace = -1; 
    csPosSignPosition = -1;
    csNegSignPosition = -1;
#endif
    if (decimalPoint) {
	val = __MKSTRING(decimalPoint);
	__AT_PUT_(info, @symbol(decimalPoint), val);
    }
    if (thousandsSep) {
	val = __MKSTRING(thousandsSep);
	__AT_PUT_(info, @symbol(thousandsSeparator), val);
    }
    if (intCurrencySymbol) {
	val = __MKSTRING(intCurrencySymbol);
	__AT_PUT_(info, @symbol(internationCurrencySymbol), val);
    }
    if (currencySymbol) {
	val = __MKSTRING(currencySymbol);
	__AT_PUT_(info, @symbol(currencySymbol), val);
    }
    if (monDecimalPoint) {
	val = __MKSTRING(monDecimalPoint);
	__AT_PUT_(info, @symbol(monetaryDecimalPoint), val);
    }
    if (monThousandsSep) {
	val = __MKSTRING(monThousandsSep);
	__AT_PUT_(info, @symbol(monetaryThousandsSeparator), val);
    }
    if (positiveSign) {
	val = __MKSTRING(positiveSign);
	__AT_PUT_(info, @symbol(positiveSign), val);
    }
    if (negativeSign) {
	val = __MKSTRING(negativeSign);
	__AT_PUT_(info, @symbol(negativeSign), val);
    }
    if (intFractDigits >= 0) {
	__AT_PUT_(info, @symbol(internationalFractionalDigits),  __MKSMALLINT(intFractDigits));
    }
    if (fractDigits >= 0) {
	__AT_PUT_(info, @symbol(fractionalDigits),  __MKSMALLINT(fractDigits));
    }
    if (csPosPrecedes >= 0) {
	if (csPosPrecedes == 0) {
	    val = false;
	} else {
	    val = true;
	}
	__AT_PUT_(info, @symbol(positiveSignPrecedesCurrencySymbol), val );
    }
    if (csNegPrecedes >= 0) {
	if (csNegPrecedes == 0) {
	    val = false;
	} else {
	    val = true;
	}
	__AT_PUT_(info, @symbol(negativeSignPrecedesCurrencySymbol), val );
    }
    if (csPosSepBySpace >= 0) {
	if (csPosSepBySpace == 0) {
	    val = false;
	} else {
	    val = true;
	}
	__AT_PUT_(info, @symbol(positiveSignSeparatedBySpaceFromCurrencySymbol), val);
    }
    if (csNegSepBySpace >= 0) {
	if (csNegSepBySpace == 0) {
	    val = false;
	} else {
	    val = true;
	}
	__AT_PUT_(info, @symbol(negativeSignSeparatedBySpaceFromCurrencySymbol), val);
    }
    switch (csPosSignPosition) {
	case 0:
	    val = @symbol(parenthesesAround);
	    break;

	case 1:
	    val = @symbol(signPrecedes);
	    break;

	case 2:
	    val = @symbol(signSuceeds);
	    break;

	case 3:
	    val = @symbol(signPrecedesCurrencySymbol);
	    break;

	case 4:
	    val = @symbol(signSuceedsCurrencySymbol);
	    break;

	default:
	    val = nil;
    }
    if (val != nil) {
	__AT_PUT_(info, @symbol(positiveSignPosition), val);
    }

    switch (csNegSignPosition) {
	case 0:
	    val = @symbol(parenthesesAround);
	    break;

	case 1:
	    val = @symbol(signPrecedes);
	    break;

	case 2:
	    val = @symbol(signSuceeds);
	    break;

	case 3:
	    val = @symbol(signPrecedesCurrencySymbol);
	    break;

	case 4:
	    val = @symbol(signSuceedsCurrencySymbol);
	    break;

	default:
	    val = nil;
    }
    if (val != nil) {
	__AT_PUT_(info, @symbol(negativeSignPosition), val);
    }
%}.
    ^ info

    "
     OperatingSystem getLocaleInfo
    "

    "Created: 23.12.1995 / 14:19:20 / cg"
!

getOSDefine
    "return a string which was used to identify this machine when stx was
     compiled, and which should be passed down when compiling methods.
     For example, on linux, this is '-DLINUX'."

%{  /* NOCONTEXT */
#ifdef WIN32
# ifndef OS_DEFINE
#  define OS_DEFINE "-DWIN32"
# endif
#endif

#ifndef OS_DEFINE
# define OS_DEFINE "-DunknownOS"
#endif

    RETURN ( __MKSTRING(OS_DEFINE COMMA_CON));
%}
    "
     OperatingSystem getOSDefine
    "
!

getOSType
    "return a string giving the type of OS we're running on.
     This can be used to adapt programs to certain environment
     differences (for example: mail-lock strategy ...)"

    |os|

%{  /* NOCONTEXT */

#   ifdef MSDOS
#    define OS_STRING "msdos"
#   endif

#   ifdef WIN32
     char *OS_STRING = "win32";

#   endif

#   ifdef MSWINDOWS
#    define OS_STRING "mswindows"
#   endif

#   ifdef NT
#    define OS_STRING "nt"
#   endif

#   ifdef VMS
#    define OS_STRING "vms"
#   endif

#   ifdef MVS
#    define OS_STRING "mvs"
#   endif

#   ifdef OS2
#    define OS_STRING "os2"
#   endif

#   ifdef sinix
#    define OS_STRING "sinix"
#   endif

#   ifdef ultrix
#    define OS_STRING "ultrix"
#   endif

#   ifdef sco
#    define OS_STRING "sco"
#   endif

#   ifdef hpux
#    define OS_STRING "hpux"
#   endif

#   ifdef LINUX
#    define OS_STRING "linux"
#   endif

#   ifdef sunos
#    define OS_STRING "sunos"
#   endif

#   ifdef solaris
#    define OS_STRING "solaris"
#   endif

#   ifdef IRIS
#    define OS_STRING "irix"
#   endif

#   ifdef aix
#    define OS_STRING "aix"
#   endif

#   ifdef realIX
#    define OS_STRING "realIX"
#   endif

    /*
     * no concrete info; become somewhat vague ...
     */
#   ifndef OS_STRING
#    ifdef MACH
#     define OS_STRING "mach"
#    endif
#   endif

#   ifndef OS_STRING
#    ifdef BSD
#     define OS_STRING "bsd"
#    endif

#    ifdef SYSV
#     ifdef SYSV3
#      define OS_STRING "sys5_3"
#     else
#      ifdef SYSV4
#       define OS_STRING "sys5_4"
#      else
#       define OS_STRING "sys5"
#      endif
#     endif
#    endif
#   endif

    /*
     * become very vague ...
     */
#   ifndef OS_STRING
#    ifdef POSIX
#     define OS_STRING "posix"
#    endif
#   endif
#   ifndef OS_STRING
#    ifdef UNIX
#     define OS_STRING "unix"
#    endif
#   endif

#   ifndef OS_STRING
#    define OS_STRING "unknown"
#   endif

    os = __MKSTRING(OS_STRING COMMA_CON);
#   undef OS_STRING
%}.
    ^ os

    "OperatingSystem getOSType"
!

getProcessId
    "return the (unix-)processId"

%{  /* NOCONTEXT */

    int pid = 0;
#ifdef UNIX_LIKE
    pid = getpid();
#endif
    RETURN ( __MKSMALLINT(pid) );
%}
    "
     OperatingSystem getProcessId
    "
!

getSystemID
    "if supported by the OS, return the systemID;
     a unique per machine identification.
     WARNING:
	not all systems support this; on some, 'unknown' is returned."

%{  /* NO_CONTEXT */
#if defined(IRIX5) && !defined(HAS_GETHOSTID)
    char idBuffer[MAXSYSIDSIZE];
    int retVal;
    OBJ arr;

    if ((retVal = syssgi(SGI_SYSID, idBuffer)) == 0) {
	arr = __BYTEARRAY_UNINITIALIZED_NEW_INT(MAXSYSIDSIZE);
	bcopy(idBuffer, __ByteArrayInstPtr(arr)->ba_element, MAXSYSIDSIZE);
	RETURN (arr);
    }
#endif
#if defined(HAS_GETHOSTID)
    int runningId;
    OBJ arr;

    runningId = gethostid();
    arr = __BYTEARRAY_UNINITIALIZED_NEW_INT(4);
    *(int *)(__ByteArrayInstPtr(arr)->ba_element) = runningId;
    RETURN (arr);
#endif
%}.
    ^ 'unknown'

    "
     OperatingSystem getSystemID
    "
!

getSystemInfo
    "return info on the system weare running on.
     If the system supports the uname system call, that info is returned;
     otherwise, some simulated info is returned.
 
     WARNING:
       Do not depend on the amount and contents of the returned information, some
       systems may return more/less than others. Also, the contents depends on the
       OS, for example, linux returns 'ix86', while WIN32 returns 'x86'.

       This method is mainly provided to augment error reports with some system
       information. 
       (in case of system/version specific OS errors, conditional workarounds and patches
	may be based upon this info)

     The returned info may (or may not) contain:
	#system -> some operating system identification (irix, Linux, nt, win32s ...) 
	#version -> OS version (some os version identification)
	#release -> OS release (3.5, 1.2.1 ...)
	#node   -> some host identification (hostname)
	#domain  -> domain name (hosts domain)
	#machine -> type of machine (i586, mips ...)
    "

    |sys node rel ver mach dom mtyp brel info|

%{  /* STACK: 4096 */
#if defined(HAS_UNAME)
    struct utsname ubuff;

    if (uname(&ubuff) >= 0) {
	sys  = __MKSTRING(ubuff.sysname COMMA_CON);
	node = __MKSTRING(ubuff.nodename COMMA_CON);
	rel  = __MKSTRING(ubuff.release COMMA_CON);
	ver  = __MKSTRING(ubuff.version COMMA_CON);
	mach = __MKSTRING(ubuff.machine COMMA_CON);
# ifdef HAS_UTS_DOMAINNAME
	dom  = __MKSTRING(ubuff.domainname COMMA_CON);
# endif
    }
#else /* no uname */
# ifdef WIN32 

    char vsnBuffer[32];
    char *s;
    int winVer;
    DWORD vsn;
    SYSTEM_INFO sysInfo;

    vsn = GetVersion();

    if (HIWORD(vsn) & 0x8000) {
	s = "win32s";
    } else {
	s = "nt";
    }
    sys = __MKSTRING(s COMMA_CON);
    winVer = LOWORD(vsn);
    sprintf(vsnBuffer, "%d.%d", LOBYTE(winVer), HIBYTE(winVer));
    rel = __MKSTRING(vsnBuffer COMMA_CON);

    GetSystemInfo(&sysInfo);
    if (sysInfo.dwProcessorType) {
	sprintf(vsnBuffer, "%d", sysInfo.dwProcessorType);
	mach = __MKSTRING(vsnBuffer COMMA_CON);
    }

# endif /* WIN32 */
#endif /* no uname */
%}.
    sys isNil ifTrue:[
	sys := self getSystemType.
    ].
    node isNil ifTrue:[
	node := self getHostName
    ].
    dom isNil ifTrue:[
	dom := self getDomainName
    ].
    mach isNil ifTrue:[
	mach := self getCPUType
    ].

    info := IdentityDictionary new.
    info at:#system put:sys.
    info at:#node put:node.
    rel notNil ifTrue:[info at:#release put:rel].
    ver notNil ifTrue:[info at:#version put:ver].
    mach notNil ifTrue:[info at:#machine put:mach].
    dom notNil ifTrue:[info at:#domain put:dom].
    ^ info

    "
     OperatingSystem getSystemInfo
    "
!

getSystemType
    "return a string giving the type of system we're running on.
     This is almost the same as getOSType, but the returned string
     is slightly different for some systems (i.e. iris vs. irix).
     Dont depend on this - use getOSType. I dont really see a point
     here ... 
     (except for slight differences between next/mach and other machs)"

    |sys|

%{
#   ifdef NEXT
#    define SYS_STRING "next"
#   endif

#   ifdef IRIS
#    define SYS_STRING "iris"
#   endif

#   ifdef WIN32 
#    define SYS_STRING  "win32"
#   endif

#   ifdef SYS_STRING
     sys = __MKSTRING(SYS_STRING COMMA_CON);
#    undef SYS_STRING
#   endif
%}.
    sys isNil ifTrue:[
	^ self getOSType
    ].
    ^ sys

    "
     OperatingSystem getSystemType
    "
!

isBSDlike
    "return true, if the OS we're running on is a 'real' unix."

%{  /* NOCONTEXT */

#if defined(BSD) || defined(MACH) || defined(SYSV4)
    RETURN ( true );
#endif
%}.
    ^ false
!

isMSDOSlike
    "return true, if the OS we're running on is dos like 
     (in contrast to unix-like)."

%{  /* NOCONTEXT */

#if defined(MSDOS_LIKE)
    RETURN ( true );
#endif
%}.
    ^ false
!

isMSWINDOWSlike
    "return true, if the OS we're running on is MS-Windows like."

%{  /* NOCONTEXT */

#if defined(WIN32)
    RETURN ( true );
#endif

%}.
    ^ false
!

isOS2like
    "return true, if the OS we're running on is OS2 like"

%{  /* NOCONTEXT */

#if defined(OS2)
    RETURN (true);
#endif

%}.
    ^ false
!

isUNIXlike
    "return true, if the OS we're running on is a unix like."

%{  /* NOCONTEXT */

#if !defined(UNIX_LIKE)
    RETURN ( false );
#endif
%}.
    ^ true
!

maxFileNameLength
    "return the max number of characters in a filename.
     CAVEAT:
	 Actually, the following is somewhat wrong - some systems
	 support different sizezs, depending on the volume.
	 We return a somewhat conservative number here."

%{  /* NOCONTEXT */
 
    /*
     * XXX: newer systems provide a query function for this ... use it
     */
#   if defined(BSD) || defined(SYSV4) || defined(LONGFILENAMES)
     RETURN ( __MKSMALLINT(255) );
#   endif

#   ifdef realIX
      RETURN ( __MKSMALLINT(127) );
#   endif

#   ifdef SYSV
      RETURN ( __MKSMALLINT(14) );
#   endif

#   ifdef MSDOS
     RETURN ( __MKSMALLINT(9) );
#   endif

#   ifdef WIN32
     /*
      * mhmh - depends on the filesystem type
      */
     RETURN ( __MKSMALLINT(9) );
#   endif
%}.
    "unix default"

    ^ 14
!

setLocaleInfo:anInfoDictionary
    "set the locale information; if set, this oerrides the OS's settings.
     (internal in ST/X only - the OS's settings remain unaffected)
     See description of fields in #getLocaleInfo.

     Notice, that (for now), the system does not use this information;
     it should be used by applications as required."

    LocaleInfo := anInfoDictionary

    "
     |d|

     d := IdentityDictionary new.
     d at:#decimalPoint                 put:'.'         .
     d at:#thousandsSeparator           put:','         .
     d at:#currencySymbol               put:'USD'       .
     d at:#monetaryDecimalPoint         put:'.'         .
     d at:#monetaryThousandsSeparator   put:'.'         .
     d at:#fractionalDigits             put:2           .
     d at:#positiveSign                 put:'+'         .
     d at:#negativeSign                 put:'-'         .
     d at:#positiveSignPrecedesCurrencySymbol put:true          .
     d at:#negativeSignPrecedesCurrencySymbol put:false         .
     OperatingSystem setLocaleInfo:d
    "
!

supportsIOInterrupts
    "return true, if the OS supports IO availability interrupts 
     (i.e. SIGPOLL/SIGIO).

     Currently, this mechanism does work for SYSV4 only ..."

%{  /* NOCONTEXT */
#if defined(SYSV4) && defined(USE_SIGIO) && 0

# if defined(SIGPOLL) || defined(SIGIO)
#  if (defined(F_GETFL) && defined(F_SETFL) && defined(FASYNC)) || defined(SYSV4)
   /*
    * currently the only system where they work is unixware SYSV4
    * (not even on SGI ...)
    */
#   if !defined(NEXT)
     RETURN (true);
#   endif
#  endif
# endif

#endif
%}.
    ^ false
!

supportsNonBlockingIO
    "return true, if the OS supports nonblocking IO."

%{  /* NOCONTEXT */
#if defined(F_GETFL) && defined(F_SETFL) && defined(FNDELAY)
       RETURN (true);
#endif
%}.
    ^ false
!

supportsSelect
    "return true, if the OS supports selecting on multiple
     filedescriptors via select."

%{  /* NOCONTEXT */
#if defined(WIN32)
    RETURN (false);
#endif
#if defined(sco)
    /*
     * sco has a select, but its broken: always waiting 1 second
     */
    RETURN(false);
#endif
%}
.
    ^ true
! !

!OperatingSystem class methodsFor:'shared memory access'!

shmAttach:id address:addr flags:flags
    "low level entry to shmat()-system call.
     Not supported on all operatingSystems"

%{  /* NOCONTEXT */
#ifdef WANT_SHM
    void *address, *shmaddr;
    int shmflg, shmid;

    if (__isSmallInteger(addr)
     && __bothSmallInteger(flags, id)) {
        shmaddr = (void *) __intVal(addr);
        shmflg = __intVal(flags);
        shmid = __intVal(id);

        address = shmat(shmid, shmaddr, shmflg);
        if (address != (void *)-1) {
            RETURN (__MKEXTERNALBYTES(addr));
        }
        OperatingSystem_LastErrorNumber = __MKSMALLINT(errno);
        RETURN (nil);
    }
#endif
%}.
    ^ self primitiveFailed

    "Modified: 22.4.1996 / 13:15:12 / cg"
!

shmDetach:addr
    "low level entry to shmdt()-system call.
     Not supported on all operatingSystems"

%{  /* NOCONTEXT */
#ifdef WANT_SHM
    void *shmaddr;
    int rslt;

    if (__isSmallInteger(addr)) {
        shmaddr = (void *) __intVal(addr);

        rslt = shmdt(shmaddr);
        if (rslt != -1) {
            RETURN (true);
        }
        OperatingSystem_LastErrorNumber = __MKSMALLINT(errno);
        RETURN (false);
    }
#endif
%}.
    ^ self primitiveFailed

    "Modified: 22.4.1996 / 13:15:03 / cg"
!

shmGet:key size:size flags:flags
    "low level entry to shmget()-system call.
     This is not for public use and not supported with all operatingSystems.
     - use the provided wrapper class SharedExternalBytes instead."

%{  /* NOCONTEXT */
#ifdef WANT_SHM
    if (__bothSmallInteger(key, size)
     && __isSmallInteger(flags)) {
        int rslt;

        rslt = shmget(__intVal(key), __intVal(size), __intVal(flags));
        if (rslt != -1) {
            RETURN (__MKSMALLINT(rslt));
        }
        OperatingSystem_LastErrorNumber = __MKSMALLINT(errno);
        RETURN (nil);
    }
#endif
%}.
    ^ self primitiveFailed

    "Modified: 22.4.1996 / 13:14:46 / cg"
! !

!OperatingSystem class methodsFor:'time and date'!

computeDatePartsOf:osTimeLow and:osTimeHi for:aBlock
    "compute year, month and day from the OS time-parts osTimeLow and
     osTimeHi and evaluate the argument, a 3-arg block with these.
     Conversion is to localtime including any daylight saving adjustments.

     This method was added to avoid LargeInteger arithmetic and to be
     independent of how the OperatingSystem represents time;
     the time-parts expected are those returned by getTimeParts."

    |year month day|

    ((osTimeLow isMemberOf:SmallInteger)
    and:[osTimeHi isMemberOf:SmallInteger]) ifFalse:[
	^ self primitiveFailed
    ].
%{
    struct tm *tmPtr;
    long t;

    t = (__intVal(osTimeHi) << 16) | __intVal(osTimeLow);
    tmPtr = localtime(&t);
    year = __MKSMALLINT(tmPtr->tm_year + 1900);
    month = __MKSMALLINT(tmPtr->tm_mon + 1);
    day = __MKSMALLINT(tmPtr->tm_mday);
%}.
    aBlock value:year value:month value:day
!

computeDatePartsOf:osTimeParts for:aBlock
    "compute year, month and day from the ostime in timeParts,
     and evaluate the argument, a 3-arg block with these.
     Conversion is to localtime including any daylight saving adjustments."

     ^ self 
	computeDatePartsOf:(osTimeParts at:1) 
	and:(osTimeParts at:2)
	for:aBlock
!

computeTimeAndDateFrom:osTimeParts
    "given an Array containing the OS-dependent time, return an Array
     containing year, month, day, hour, minute and seconds,
     offset to UTC and a daylight savings time flag.
     Conversion is to localtime including any daylight saving adjustments."

    |low hi year month day hours minutes seconds utcOffset dst ret|

    low := osTimeParts at:1.
    hi := osTimeParts at:2.
%{
    struct tm *tmPtr;
    struct tm *gmTmPtr;
    long t;

#ifdef ultrix /* mhmh - isnt this ifdef BSD ? */
#   define TIMEZONE     tmPtr->tm_gmtoff
#else
# ifdef MSDOS_LIKE
    define TIMEZONE 0
# else
#   define TIMEZONE     timezone
# endif
#endif

    if (__bothSmallInteger(low, hi)) {
	t = (__intVal(hi) << 16) | __intVal(low);
	tmPtr = localtime(&t);
	hours = __MKSMALLINT(tmPtr->tm_hour);
	minutes = __MKSMALLINT(tmPtr->tm_min);
	seconds = __MKSMALLINT(tmPtr->tm_sec);

	year = __MKSMALLINT(tmPtr->tm_year + 1900);
	month = __MKSMALLINT(tmPtr->tm_mon + 1);
	day = __MKSMALLINT(tmPtr->tm_mday);

	if (tmPtr->tm_isdst == 0) {
	    dst = false;
	    utcOffset = __MKSMALLINT(TIMEZONE);
	} else {
	    dst = true;
#ifdef HAS_ALTZONE
	    utcOffset = __MKSMALLINT(altzone);
#else
	    utcOffset = __MKSMALLINT(TIMEZONE) + 3600;
#endif
	}
    }
%}.
    year notNil ifTrue:[
	"I would love to have SELF-like inline objects ..."
	ret := Array new:8.
	ret at:1 put:year.
	ret at:2 put:month.
	ret at:3 put:day.
	ret at:4 put:hours.
	ret at:5 put:minutes.
	ret at:6 put:seconds.
	ret at:7 put:utcOffset.
	ret at:8 put:dst.
	^ ret
    ].
    ^ self primitiveFailed
!

computeTimePartsFromYear:y month:m day:d hour:h minute:min seconds:s
    "return an Array containing the OS-dependent time for the given
     time and day. The arguments are assumed to be in localtime including
     any daylight saving adjustings."

    |low hi|

%{
    struct tm tm;
    long t;

    if (__bothSmallInteger(y, m) 
     && __bothSmallInteger(d, h)
     && __bothSmallInteger(min, s)) {
	tm.tm_hour = __intVal(h);
	tm.tm_min = __intVal(min);
	tm.tm_sec = __intVal(s);

	tm.tm_year = __intVal(y) - 1900;
	tm.tm_mon = __intVal(m) - 1;
	tm.tm_mday = __intVal(d);
	tm.tm_isdst = -1;

	t = mktime(&tm);
	low = __MKSMALLINT(t & 0xFFFF);
	hi = __MKSMALLINT((t >> 16) & 0xFFFF);
    }
%}.
    low notNil ifTrue:[
	^ Array with:low with:hi
    ].    
    ^ self primitiveFailed
!

computeTimePartsOf:osTimeLow and:osTimeHi for:aBlock
    "compute hours, minutes and seconds from the time-parts timeLow and
     timeHi and evaluate the argument, a 3-arg block with these.
     Conversion is to localtime including any daylight saving adjustments.

     This method was added to avoid LargeInteger arithmetic and to be
     independent of how the OperatingSystem represents time;
     the time-parts expected are those returned by getTimeParts."

    |hours minutes seconds|

    ((osTimeLow isMemberOf:SmallInteger)
    and:[osTimeHi isMemberOf:SmallInteger]) ifFalse:[
	^ self primitiveFailed
    ].
%{
    struct tm *tmPtr;
    long t;

    t = (__intVal(osTimeHi) << 16) | __intVal(osTimeLow);
    tmPtr = localtime(&t);
    hours = __MKSMALLINT(tmPtr->tm_hour);
    minutes = __MKSMALLINT(tmPtr->tm_min);
    seconds = __MKSMALLINT(tmPtr->tm_sec);
%}.
    aBlock value:hours value:minutes value:seconds
!

computeTimePartsOf:osTimeParts for:aBlock
    "compute hour, minute and seconds from the ostime in timeParts,
     and evaluate the argument, a 3-arg block with these.
     Conversion is to localtime including any daylight saving adjustments."

     ^ self 
	computeTimePartsOf:(osTimeParts at:1) 
	and:(osTimeParts at:2)
	for:aBlock
!

getMillisecondTime
    "This returns the millisecond timers value. 
     The range is limited to 0..1fffffff (i.e. the SmallInteger range) to avoid
     LargeInteger arithmetic when doing timeouts and delays.
     Since this value is wrapping around in regular intervals, this can only be used for 
     short relative time deltas.
     Use the millisecondTimeXXX:-methods to compare and add time deltas - these know about the wrap.

     BAD DESIGN:
	This should be changed to return some instance of RelativeTime,
	and these computations moved there.

     Dont use this method in application code since it is an internal (private)
     interface. For compatibility with ST-80, use Time millisecondClockValue.
    "

%{  /* NOCONTEXT */

    long t;
#if !defined(HAS_GETTIMEOFDAY) && defined(SYSV) && defined(HZ)
    /* 
     * sys5 time
     */
    long ticks;
    struct tms tb;

    ticks = times(&tb);
    t = (ticks * 1000) / HZ;
#else
# ifdef MSDOS_LIKE
    struct _timeb timebuffer;

    _ftime(&timebuffer);
    t = (timebuffer.time * 1000) + timebuffer.millitm;
# else /* assume HAS_GETTIMEOFDAY */
    /*
     * bsd time
     */
    struct timeval tb;
    struct timezone tzb;

    gettimeofday(&tb, &tzb);
    t = tb.tv_sec*1000 + tb.tv_usec/1000;
# endif
#endif
    RETURN ( __MKSMALLINT(t & 0x1FFFFFFF) );
%}
!

getTime
    "return current Time in some OS dependent representation.
     (on unix, its the seconds since 1970).

     WARNING: 
	this is OperatingSystem dependent
	- for portable code, use getTimeParts and compute*PartsOf:and:for:.
	Better yet, use Date>>today or Time>>now for fully ST-80 compatible code."

    ^ (OperatingSystem getTimeInto:[:low :hi | hi*16r10000 + low])

    "
     OperatingSystem getTime
    "
!

getTimeHi
    "return hi 16 bits of current time. 

     OBSOLETE: Dont use this method, use getTimeParts.
     This method will NOT always return the correct time
     if used together with getTimeHi, since a wrap between 
     the two getTimeXXX sends could occur.
     WARNING: this method will vanish - dont use it."

%{  /* NOCONTEXT */

    RETURN ( __MKSMALLINT((time(0) >> 16) & 0xFFFF) );
%}

    "OperatingSystem getTimeHi"
!

getTimeInto:aBlock
    "evaluate the argument aBlock, passing the time-parts of
     the current time as arguments.
     See comment in 'OperatingSystem>>getTimeParts' on what to
     do with those parts."

    |low hi|
%{ 
    int now;

    now = time(0);
    hi  = __MKSMALLINT((now >> 16) & 0xFFFF);
    low = __MKSMALLINT(now & 0xFFFF);
%}.
    ^ aBlock value:low value:hi

    "
     OperatingSystem getTimeInto:[:low :hi | low printNewline. hi printNewline]
    "
!

getTimeLow
    "return low 16 bits of current time. 

     OBSOLETE: Dont use this method, use getTimeParts.
     This method will not always return the correct time 
     if used together with getTimeHi, since a wrap between 
     the two getTimeXXX sends could occur.
     WARNING: this method will vanish - dont use it."

%{  /* NOCONTEXT */

    RETURN ( __MKSMALLINT(time(0) & 0xFFFF) );
%}

    "
     OperatingSystem getTimeLow
    "
!

getTimeParts
    "return the current time as an array of 2 integers representing
     the current time (usually low 16bits / hi 16 bits) in some OS
     dependent way.

     On unix, the values represent the seconds since 1970, on other
     systems this may be different.
     WARNING:
       for portability, never use these values directly, but pass them
       to #computeTimeParts and/or #computeDatePartsOf:, which know how to 
       interpret them.
       Better yet, use Date>>today and Time>>now for ST-80 compatibility."

    |low hi|
%{
    int now;

    now = time(0);
    hi  = __MKSMALLINT((now >> 16) & 0xFFFF);
    low = __MKSMALLINT(now & 0xFFFF);
%}.
    ^ Array with:low with:hi

    "
     OperatingSystem getTimeParts
    "
!

maximumMillisecondTimeDelta
    "this returns the maximum delta supported by millisecondCounter
     based methods. The returned value is half the value at which the
     timer wraps."

%{  /* NOCONTEXT */
    RETURN ( __MKSMALLINT(0x0FFFFFFF) );
%}
!

millisecondDelay:millis
    "delay execution for millis milliseconds or until the next event
     arrives.
     All lower priority threads will also sleep for the duration, 
     interrupts (and therefore, higher prio processes) are
     still handled. 
     Better use a Delay, to only delay the calling thread.
     (however, a delay cannot be used in the event handler or scheduler)"

    |now then delta|

%{  /* NOCONTEXT */
#ifdef WIN32
    int t = __intVal(millis);

    if (t) {
	Sleep(t);
    }
    RETURN (self);
#endif
%}.
 
    now := OperatingSystem getMillisecondTime.
    then := OperatingSystem millisecondTimeAdd:now and:millis.

    [OperatingSystem millisecondTime:then isAfter:now] whileTrue:[
	delta := OperatingSystem millisecondTimeDeltaBetween:then and:now.
	self selectOnAnyReadable:nil writable:nil exception:nil withTimeOut:delta.
	now := OperatingSystem getMillisecondTime.
    ]

    "
     OperatingSystem millisecondDelay:2000
    "
!

millisecondTime:msTime1 isAfter:msTime2
    "return true if msTime1 is after msTime2, false if not.
     The two arguments are supposed to be millisecond times 
     (such as returned getMillisecondTime) which wrap at 16r1FFFFFFF.

     This should really be moved to some RelativeTime class."

    (msTime1 > msTime2) ifTrue:[
	((msTime1 - msTime2) >= 16r10000000) ifTrue:[
	    ^ false
	].
	^ true
    ].
    ((msTime2 - msTime1) > 16r10000000) ifTrue:[
	^ true
    ].
    ^ false
!

millisecondTimeAdd:msTime1 and:msTime2
    "Add two millisecond times (such as returned getMillisecondTime).
     The returned value is msTime1 + msTime2 where a wrap occurs at:16r1FFFFFFF.

     This should really be moved to some RelativeTime class."

    |sum|

    sum := msTime1 + msTime2.
    (sum > 16r1FFFFFFF) ifTrue:[^ sum - 16r20000000].
    (sum < 0) ifTrue:[^ sum + 16r20000000].
    ^ sum
!

millisecondTimeDeltaBetween:msTime1 and:msTime2
    "subtract two millisecond times (such as returned getMillisecondTime)
     and return the difference. Since milli-times wrap (at 16r01FFFFFFF), 
     some special handling is built-in here.
     The returned value is msTime1 - msTime2. The returned value is invalid
     if the delta is >= 0x10000000.

     This should really be moved to some RelativeTime class;
     better yet: create a subclass of Integer named LimitedRangeInteger."

    (msTime1 > msTime2) ifTrue:[
	^ msTime1 - msTime2
    ].
    ^ msTime1 + 16r10000000 - msTime2

    "
     OperatingSystem millisecondTimeAdd:16r0FFFFFFF and:1   
     OperatingSystem millisecondTimeAdd:16r0FFFFFFF and:(16 / 3)  
     OperatingSystem millisecondTimeAdd:16r0FFFFFFF and:1000   

     OperatingSystem millisecondTimeDeltaBetween:0 and:16r0FFFFFFF  
     OperatingSystem millisecondTimeDeltaBetween:(13/3) and:16r0FFFFFFF     
     OperatingSystem millisecondTimeDeltaBetween:999 and:16r0FFFFFFF       

     OperatingSystem millisecondTime:0 isAfter:16r0FFFFFFF    
     OperatingSystem millisecondTime:(13/3) isAfter:16r0FFFFFFF   
     OperatingSystem millisecondTime:999 isAfter:16r0FFFFFFF       

     OperatingSystem millisecondTime:0 isAfter:0          
     OperatingSystem millisecondTime:(13/3) isAfter:0  
     OperatingSystem millisecondTime:999 isAfter:0       

     OperatingSystem millisecondTime:1 isAfter:0        
     OperatingSystem millisecondTime:(13/3) isAfter:2
     OperatingSystem millisecondTime:999 isAfter:900       

     |t1 t2|

     t1 := Time millisecondClockValue.
     (Delay forMilliseconds:1) wait.   
     t2 := Time millisecondClockValue.
     OperatingSystem millisecondTimeDeltaBetween:t2 and:t1 
    "
!

sleep:numberOfSeconds
    "cease ANY action for some time. This suspends the whole smalltalk
     (unix-) process for some time.
     Not really useful since not even low-prio processes and interrupt
     handling will run during the sleep.
     Use either OperatingSystem>>millisecondDelay: (which makes all
     threads sleep, but handles interrupts) or use a Delay (which makes
     only the calling thread sleep)."

%{  /* NOCONTEXT */

    if (__isSmallInteger(numberOfSeconds)) {
	sleep(__intVal(numberOfSeconds));
	RETURN ( self );
    }
%}.
    "
     argument not integer
    "
    ^ self primitiveFailed

    "
     OperatingSystem sleep:2
    "
! !

!OperatingSystem class methodsFor:'users & groups'!

getEffectiveGroupID
    "return the current users (thats you) effective numeric group id.
     This is only different from getGroupID, if you have ST/X running
     as a setuid program (of which you should think about twice)."

%{  /* NOCONTEXT */

#ifdef UNIX_LIKE
    int uid;

    uid = getegid();
    RETURN ( __MKSMALLINT(uid) );
#endif
    /* --- return same as getGroupID --- */
%}.
    ^ self getGroupID

    "
     OperatingSystem getEffectiveGroupID
    "
!

getEffectiveUserID
    "return the current users (thats you) effective numeric user id.
     This is only different from getUserID, if you have ST/X running
     as a setuid program (of which you should think about twice)."

%{  /* NOCONTEXT */

#ifdef UNIX_LIKE
    int uid;

    uid = geteuid();
    RETURN ( __MKSMALLINT(uid) );
#endif
    /* --- return same as getUserID --- */
%}.
    ^ self getUserID

    "
     OperatingSystem getEffectiveUserID
    "
!

getGroupID
    "return the current users (thats you) numeric group id"

%{  /* NOCONTEXT */

#ifdef UNIX_LIKE
    int uid;

    uid = getgid();
    RETURN ( __MKSMALLINT(uid) );
#else
# ifdef SYSTEM_HAS_GROUPS
    /* ... */
# endif
#endif
%}.
    ^ 1 "just a dummy for systems which do not have userIDs"

    "
     OperatingSystem getGroupID
    "
!

getGroupNameFromID:aNumber
    "return the group-name-string for a given numeric group-id"

%{  /* NOCONTEXT */

#ifdef UNIX_LIKE
    struct group *g;

    if (__isSmallInteger(aNumber)) {
	g = getgrgid(__intVal(aNumber));
	if (g) {
	    RETURN ( __MKSTRING(g->gr_name COMMA_CON) );
	}
    }
#endif
%}.
    ^ '???'

    "
     OperatingSystem getGroupNameFromID:0
     OperatingSystem getGroupNameFromID:10
    "
!

getHomeDirectory
    "return the name of the users home directory"

    ^ OperatingSystem getEnvironment:'HOME'

    "
     OperatingSystem getHomeDirectory
    "
!

getLoginName
    "return a string with the users name"

%{  /* NOCONTEXT */
    static char cachedName[64];
    static firstCall = 1;

    char *name = (char *)0;
#ifdef UNIX_LIKE
    if (firstCall) {
	name = (char *)getlogin();
	if (! name || (name[0] == 0)) {
	    name = (char *)getenv("LOGNAME");
	}
	if (name && (strlen(name) < sizeof(cachedName))) {
	    strcpy(cachedName, name);
	    firstCall = 0;
	}
    } else {
	name = cachedName;
    }
#else
# ifdef MSDOS_LIKE
    if (firstCall) {
	int nameSize = sizeof(cachedName);

	if (GetUserName(cachedName, &nameSize) == TRUE) {
	    name = cachedName;
	    firstCall = 0;
	}
    } else {
	name = cachedName;
    }
# endif
#endif
    if (! name || (name[0] == 0) ) {
	name = (char *)getenv("LOGNAME");
    }
    if (! name || (name[0] == 0) ) {
	name = "you";
    }

    RETURN ( __MKSTRING(name COMMA_CON) );
%}.
    "
     OperatingSystem getLoginName
    "
!

getUserID
    "return the current users (thats you) numeric user id"

%{  /* NOCONTEXT */

#ifdef UNIX_LIKE
    int uid;

    uid = getuid();
    RETURN ( __MKSMALLINT(uid) );
#else
# ifdef SYSTEM_HAS_USERS
    /* ... */
# endif
#endif
%}.
    ^ 1 "just a dummy for systems which do not have userIDs"

    "
     OperatingSystem getUserID
    "
!

getUserNameFromID:aNumber
    "return the user-name-string for a given numeric user-id"

%{  /* NOCONTEXT */

#ifdef UNIX_LIKE
    struct passwd *p;

    if (__isSmallInteger(aNumber)) {
	p = getpwuid(__intVal(aNumber));
	if (p) {
	    RETURN ( __MKSTRING(p->pw_name COMMA_CON) );
	}
    }
#endif
%}.
    ^ '? (' , aNumber printString , ')'

    "
     OperatingSystem getUserNameFromID:0
     OperatingSystem getUserNameFromID:100
     OperatingSystem getUserNameFromID:9991 
    "
!

userInfoOf:aNameOrID
    "return a dictionary filled with userinfo. The argument can be either
     a string with the users name or its numeric id.
     Notice, that not all systems provide (all of) this info;
     DOS systems return nothing; 
     non-SYSV4 systems have no age/comment.
     Portable applications may want to check the systemType and NOT depend
     on all keys to be present in the returned dictionary.
     Another notice: on some systems (SYSV4), the gecos field includes multiple
     entries (i.e. not just the name), separated by commas. You may want to
     extract any substring, up to the first comma to get the real life name."

    |info name passw uid gid age comment
     gecos dir shell|

%{
#ifdef UNIX_LIKE
    struct passwd *buf;
    int ret;

    if (__isString(aNameOrID)) {
	buf = getpwnam(__stringVal(aNameOrID));
    } else if (__isSmallInteger(aNameOrID)) {
	buf = getpwuid(__intVal(aNameOrID));
    } else {
	buf = (struct passwd *)0;
    }
    if (buf) {
	name = __MKSTRING(buf->pw_name COMMA_CON);
	passw = __MKSTRING(buf->pw_passwd COMMA_CON);
# ifdef SYSV4
	age = __MKSTRING(buf->pw_age COMMA_CON);
	comment = __MKSTRING(buf->pw_comment COMMA_CON);
# endif
	dir = __MKSTRING(buf->pw_dir COMMA_CON);
	gecos = __MKSTRING(buf->pw_gecos COMMA_CON);
	shell = __MKSTRING(buf->pw_shell COMMA_CON);

	uid = __MKSMALLINT(buf->pw_uid);
	gid = __MKSMALLINT(buf->pw_gid);
    }
#endif
%}.
    info := IdentityDictionary new.
    name notNil ifTrue:[
	info at:#name put:name.
    ] ifFalse:[
	info at:#name put:'unknown'
    ].
    passw notNil ifTrue:[info at:#passwd put:passw].
    age notNil ifTrue:[info at:#age put:age].
    comment notNil ifTrue:[info at:#comment put:comment].
    gecos notNil ifTrue:[info at:#gecos put:gecos].
    shell notNil ifTrue:[info at:#shell put:shell].
    dir notNil ifTrue:[info at:#dir put:dir].
    uid notNil ifTrue:[info at:#uid put:uid].
    gid  notNil ifTrue:[info at:#gid put:gid].
    ^ info

    "
     OperatingSystem userInfoOf:'root'
     OperatingSystem userInfoOf:1
     OperatingSystem userInfoOf:'claus' 
     OperatingSystem userInfoOf:'fooBar' 
     OperatingSystem userInfoOf:(OperatingSystem getUserID)
    "
! !

!OperatingSystem class methodsFor:'waiting for events'!

blockingChildProcessWait
     "return true, if childProcessWait: blocks, if no childs are ready"
%{ /*NOCONTEXT*/
#if defined(HAS_WAITPID) || defined(HAS_WAIT3)
    RETURN(false);
#else
    RETURN(true);
#endif
%}
!

childProcessWait:blocking
    "get status changes from child processes.
     Return an OSProcessStatus or nil, if no process has terminated.
     If blocking is true, we wait until a process changed state, 
     otherwise we return immediately."

    |pid status code core|
%{
    int p;
#if defined(HAS_WAITPID)
    int s;
# define __WAIT     waitpid(-1, &s, blocking == true ? WUNTRACED : WNOHANG|WUNTRACED)
#else
# if defined(HAS_WAIT3)
    union wait s;
#define __WAIT      wait3(&s, blocking == true ? WUNTRACED : WNOHANG|WUNTRACED, 0)
# else /*!HAS_WAIT3*/
    int s;
# define __WAIT      wait(&s)

#  if !defined(WIFEXITED)
#   define WIFEXITED(stat)      (((int)((stat)&0377))==0)
#   define WIFSIGNALED(stat)    (((int)((stat)&0377))>0&&((int)(((stat)>>8)&0377))==0)
#   define WIFSTOPPED(stat)     (((int)((stat)&0377))==0177&&((int)(((stat)>>8)&0377))!=0)

#   define WEXITSTATUS(stat)    ((int)(((stat)>>8)&0377))
#   define WTERMSIG(stat)       (((int)((stat)&0377))&0177)
#   define WSTOPSIG(stat)       ((int)(((stat)>>8)&0377))
#  endif /*!WIFEXITED*/
#  if !defined(WCOREDUMP)
#   define WCOREDUMP(stat)      ((int)(((stat)>>8)&0200))
#  endif /*!WCOREDUMP*/

    if (blocking != true) {
	/*
	 * We do not support nonBlocking waits, so signal an error
	 * Sorry about the goto, but with all these ifdefs ...
	 */
	goto done;
    }
# endif /*!HAS_WAIT3*/
#endif  /*!HAS_WAITPID*/

    do {
	p = __WAIT;
    } while (p == -1 && errno == EINTR);

#undef __WAIT

    if (p == 0)
	RETURN(nil)

    if (p == -1) {
	if (errno == ECHILD)
	    RETURN(nil);
    } else {
	pid = __MKSMALLINT(p);
	if (WIFEXITED(s)) {
	    status = @symbol(exit);
	    code = __MKSMALLINT(WEXITSTATUS(s));
	    core = WCOREDUMP(s) ? true : false;
	} else if (WIFSIGNALED(s)) {
	    status = @symbol(signal);
	    code = __MKSMALLINT(WTERMSIG(s));
	} else if (WIFSTOPPED(s)) {
	    status = @symbol(stop);
	    code = __MKSMALLINT(WSTOPSIG(s));
	}
#if defined(WIFCONTINUED)
	else if (WIFCONTINUED(s)) {
	    status = @symbol(continue);
	} 
#endif
    }
done: ;
%}.

    pid isNil ifTrue:[
	^ self primitiveFailed
    ].

    ^ OSProcessStatus pid:pid status:status code:code core:core

    "
     OperatingSystem childProcessWait:false
    "

    "Created: 5.1.1996 / 16:39:14 / stefan"
!

readCheck:fd
    "return true, if data is available on a filedescriptor (i.e. read
     is possible without blocking)"

    (self selectOnAnyReadable:(Array with:fd)
		     writable:nil
		    exception:nil
		  withTimeOut:0) == fd
	ifTrue:[^ true].
    ^ false
!

selectOn:fd1 and:fd2 withTimeOut:millis
    "wait for any fd to become ready; timeout after t milliseconds.
     A zero timeout-time will immediately return (i.e. poll).
     Return fd if i/o ok, nil if timed-out or interrupted.
     Obsolete:
	This is a leftover method and will vanish."

    ^ self selectOnAnyReadable:(Array with:fd1 with:fd2)
		      writable:(Array with:fd1 with:fd2)
		     exception:nil
		   withTimeOut:millis
!

selectOn:fd withTimeOut:millis
    "wait for aFileDesriptor to become ready; timeout after t milliseconds.
     Return true, if i/o ok, false if timed-out or interrupted.
     With 0 as timeout argument, this can be used to check for availability
     of read-data.
     Experimental."

    ^ self selectOnAnyReadable:(Array with:fd)
		      writable:(Array with:fd)
		     exception:nil
		   withTimeOut:millis
!

selectOnAny:fdArray withTimeOut:millis
    "wait for any fd in fdArray (an Array of integers) to become ready;
     timeout after t milliseconds. An empty set will always wait.
     Return first ready fd if i/o ok, nil if timed-out or interrupted.
     Experimental."

    ^ self selectOnAnyReadable:fdArray
		      writable:fdArray
		     exception:nil
		   withTimeOut:millis
!

selectOnAnyReadable:fdArray withTimeOut:millis
    "wait for any fd in fdArray (an Array of integers) to become ready for 
     reading. Timeout after t milliseconds. An empty set will always wait.
     A zero timeout-time will immediately return (i.e. poll).
     Return first ready fd if i/o ok, nil if timed-out or interrupted.
     Experimental."

    ^ self selectOnAnyReadable:fdArray 
		      writable:nil 
		     exception:nil
		   withTimeOut:millis
!

selectOnAnyReadable:readFdArray writable:writeFdArray exception:exceptFdArray withTimeOut:millis
    "wait for any fd in readFdArray (an Array of integers) to become ready for 
     reading, writeFdArray to become ready for writing, or exceptFdArray to 
     arrive exceptional data (i.e. out-of-band data).
     Timeout after t milliseconds or, if the timeout time is 0, immediately..
     Empty fd-sets will always wait. Zero time can be used to poll file-
     descriptors (i.e. to check if I/O possible without blocking).
     Return first ready fd if I/O ok, nil if timed-out or interrupted."

%{
#ifdef MSDOS_LIKE
    /*
     * support a delay-wait only
     * (i.e. fail if any filedescriptor is selected upon)
     */
    int count;
    int i;
    int t;
    OBJ fd;

    if (! __isSmallInteger(millis)) {
	goto fail;
    }

    if (readFdArray != nil) {
	if (! __isArray(readFdArray)) {
	    goto fail;  
	}
	count = __arraySize(readFdArray);
	for (i=0; i<count;i++) {
	    fd = __ArrayInstPtr(readFdArray)->a_element[i];
	    if (fd != nil) {
		goto fail;
	    }
	}
    }
    if (writeFdArray != nil) {
	if (! __isArray(writeFdArray)) {
	    goto fail;
	}
	count = __arraySize(writeFdArray);
	for (i=0; i<count;i++) {
	    fd = __ArrayInstPtr(writeFdArray)->a_element[i];
	    if (fd != nil) {
		goto fail;
	    }
	}
    }
    if (exceptFdArray != nil) {
	if (! __isArray(exceptFdArray)) {
	    goto fail;
	}
	count = __arraySize(exceptFdArray);
	for (i=0; i<count;i++) {
	    fd = __ArrayInstPtr(exceptFdArray)->a_element[i];
	    if (fd != nil) {
		goto fail;
	    }
	}
    }
    t = __intVal(millis);
    if (t != 0) {
	/*
	 * delay only 
	 */
	Sleep(t);
    }
    RETURN (nil);

#else /* not MSDOS_LIKE */
    fd_set rset, wset, eset;
    struct timeval wt, et;
    int t, f, maxF, i, lX, bX;
    OBJ fd, retFd;
    int ret;
    int count;

    if (__isSmallInteger(millis)) {
	FD_ZERO(&rset);
	FD_ZERO(&wset);
	FD_ZERO(&eset);

	maxF = -1;
	if (readFdArray != nil) {
	    if (! __isArray(readFdArray)) {
		goto fail;    
	    }
	    count = __arraySize(readFdArray);

	    for (i=0; i<count;i++) {
		fd = __ArrayInstPtr(readFdArray)->a_element[i];
		if (fd != nil) {
		    f = __intVal(fd);
		    if ((f >= 0) && (f < FD_SETSIZE)) {
			FD_SET(f, &rset);
			if (f > maxF) maxF = f;
		    }
		}
	    }
	}

	if (writeFdArray != nil) {
	    if (! __isArray(writeFdArray)) {
		goto fail;    
	    }
	    count = __arraySize(writeFdArray);
	    for (i=0; i<count;i++) {
		fd = __ArrayInstPtr(writeFdArray)->a_element[i];
		if (fd != nil) {
		    f = __intVal(fd);
		    if ((f >= 0) && (f < FD_SETSIZE)) {
			FD_SET(f, &wset);       
			if (f > maxF) maxF = f;
		    }
		}
	    }
	}

	if (exceptFdArray != nil) {
	    if (! __isArray(exceptFdArray)) {
		goto fail;    
	    }
	    count = __arraySize(exceptFdArray);
	    for (i=0; i<count;i++) {
		fd = __ArrayInstPtr(exceptFdArray)->a_element[i];
		if (fd != nil) {
		    f = __intVal(fd);
		    if ((f >= 0) && (f < FD_SETSIZE)) {
			FD_SET(f, &eset);       
			if (f > maxF) maxF = f;
		    }
		}
	    }
	}

	t = __intVal(millis);
	if (t) {
	    wt.tv_sec = t / 1000;
	    wt.tv_usec = (t % 1000) * 1000;
	} else {
	    wt.tv_sec = wt.tv_usec = 0;
	}

	/*
	 * make certain, that interrupt gets us out of the select
	 */
	__BEGIN_INTERRUPTABLE__
	errno = 0;

	if (t == 0) {
	    /* 
	     * if there is no timeout time, we can stay here 
	     * interruptable.
	     */
	    do {
		ret = select(maxF+1, &rset, &wset, &eset, &wt);
	    } while ((ret < 0) && (errno == EINTR));
	} else {
	    do {
		ret = select(maxF+1, &rset, &wset, &eset, &wt);
		/* 
		 * for now: dont loop; if we did, we had to adjust the vt-timeval;
		 * could otherwise stay in this loop forever ...
		 * Premature return (before the time expired) must be handled by the caller.
		 * A good solution is to update the wt-timeval and redo the select.
		 */
	    } while (0 /* (ret < 0) && (errno == EINTR) */ );
	}
	__END_INTERRUPTABLE__

	if (ret > 0) {
	    for (i=0; i <= maxF; i++) {
		if (FD_ISSET(i, &rset)
		 || FD_ISSET(i, &wset)
		 || FD_ISSET(i, &eset)) {
		    RETURN ( __MKSMALLINT(i) );
		}
	    }
	} else {
	    if (ret < 0) {
		if (errno != EINTR) {
		    fprintf(stderr, "OS: select errno = %d\n", errno);
		    OperatingSystem_LastErrorNumber = __MKSMALLINT(errno);
		}
	    }
	}

	/*
	 * return nil (means time expired)
	 */
	RETURN ( nil );
    }
#endif /* not MSDOS_LIKE */

fail: ;
%}.
    "
     timeout argument not integer,
     or any fd-array nonNil and not an array
     or not supported by OS
    "
    ^ self primitiveFailed
!

setBlocking:aBoolean on:fd
    "set/clear the blocking attribute - if set (which is the default)
     a read on the fileDescriptor will block until data is available.
     If cleared, a read operation will immediately return with a value of
     nil."

%{  /* NOCONTEXT */

    int ret, flags;
    int savInt;

#if defined(F_GETFL) && defined(F_SETFL)
# if defined(FNDELAY)
    if (__isSmallInteger(fd)) {
	int f = __intVal(fd);

	flags = fcntl(f, F_GETFL, 0);
	if (aBoolean == true) {
	    ret = fcntl(f, F_SETFL, flags & ~FNDELAY);
	} else {
	    ret = fcntl(f, F_SETFL, flags | FNDELAY);
	}
	if (ret >= 0) ret = flags;
	RETURN ( __MKSMALLINT(ret) );
    }
# endif
#endif
%}.
    "
     fd argument not integer
    "
    ^ self primitiveFailed
!

writeCheck:fd
    "return true, if filedescriptor can be written without blocking"

    (self selectOnAnyReadable:nil
		     writable:(Array with:fd)
		    exception:nil
		  withTimeOut:0) == fd
	ifTrue:[^ true].
    ^ false
! !

!OperatingSystem class methodsFor:'documentation'!

version
    ^ '$Header: /cvs/stx/stx/libbasic/Attic/Unix.st,v 1.139 1996-04-25 16:00:14 cg Exp $'
! !
OperatingSystem initialize!