Win32OperatingSystem.st
author Claus Gittinger <cg@exept.de>
Wed, 05 May 1999 14:54:53 +0200
changeset 4149 f9437e0f6315
parent 4145 0c46be7817c5
child 4193 abae662bbe37
permissions -rw-r--r--
checkin from browser

"
 COPYRIGHT (c) 1988 by Claus Gittinger
 COPYRIGHT (c) 1998 by eXept Software AG
	      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.
"

AbstractOperatingSystem subclass:#Win32OperatingSystem
	instanceVariableNames:''
	classVariableNames:'HostName DomainName CurrentDirectory'
	poolDictionaries:''
	category:'OS-Windows'
!

Object subclass:#FileStatusInfo
	instanceVariableNames:'type mode uid gid size id accessed modified statusChanged path
		alternativeName'
	classVariableNames:''
	poolDictionaries:''
	privateIn:Win32OperatingSystem
!

Object subclass:#OSProcessStatus
	instanceVariableNames:'pid status code core'
	classVariableNames:''
	poolDictionaries:''
	privateIn:Win32OperatingSystem
!

!Win32OperatingSystem primitiveDefinitions!
%{

# define WRAP_STDIO

# ifndef MSDOS_LIKE
#  define MSDOS_LIKE
# endif
# undef UNIX_LIKE       /* oops - we were too optimistic - no OS */
# ifdef i386
#  ifndef _X86_
#   define _X86_
#  endif
# 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)
 */ 

# 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

# 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

# ifndef _SYS_STAT_H_INCLUDED_
#  include <sys/stat.h>
#  define _SYS_STAT_H_INCLUDED_
# 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
/*#  define PROCESSDEBUGWIN32
#  define PROCESS1DEBUGWIN32
#  define PROCESS2DEBUGWIN32
#  define SELECTDEBUGWIN32
#  define SELECT1DEBUGWIN32
#  define SELECT2DEBUGWIN32  */
#  undef INT
#  undef Array
#  undef Number
#  undef Method
#  undef Point
#  undef Rectangle
#  undef Block
#  undef String
#  undef Message
#  undef Object
#  undef Context

#  include <stdarg.h> /* */
#  include <windows.h>

#  if !defined(__BORLANDC__)
#   undef stat
#   define stat _stat
#   undef chmod
#   define chmod _chmod
#   undef access
#   define access _access
#   define MAXFILELEN 256
#  endif

#  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
#  ifdef __DEF_Block
#   define Block __DEF_Block
#  endif
#  ifdef __DEF_String
#   define String __DEF_String
#  endif
#  ifdef __DEF_Message
#   define Message __DEF_Message
#  endif
#  ifdef __DEF_Object
#   define Object __DEF_Object
#  endif
#  ifdef __DEF_Context
#   define Context __DEF_Context
#  endif

#  define INT int

typedef int (*intf)(int);
BOOL __signalUserInterruptWIN32(DWORD sig);

# endif /* WIN32 */

# 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

# ifdef __BORLANDC__
#  include <dir.h>
#  define MAXPATHLEN MAXPATH
#  define MAXFILELEN MAXFILE
# endif

# ifndef MAXPATHLEN
#  ifndef MSDOS_LIKE
#   ifndef NO_SYS_PARAM_H
#    include <sys/param.h>
#   endif
#  endif
#  ifndef MAXPATHLEN
#   ifdef FILENAME_MAX  /* i.e. MSDOS_LIKE */
#    define MAXPATHLEN FILENAME_MAX
#   else
#    ifdef MAX_PATH
#     define MAXPATHLEN MAX_PATH
#    else
#     define MAXPATHLEN 1024
#    endif
#   endif
#  endif
# 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

# if defined(HAS_SYSINFO)
#  include <sys/systeminfo.h>
# endif


/*
 * 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

#define SIGHANDLER_ARG int

#define _HANDLEVal(o)        (HANDLE)(__externalAddressVal(o))

/*
 * not all systems have time_t and off_t
 * explicit add of those we know to have ...
 */

#ifndef TIME_T
# define TIME_T long
#endif
#ifndef OFF_T
# define OFF_T  long
#endif

/*
 * where is the timezone info ?
 */
#if defined(HAS_NO_TIMEZONE)
# if defined(HAS_NO_TM_GMTOFF)
#  define TIMEZONE(tmPtr)       0
# else
#  define TIMEZONE(tmPtr)       ((tmPtr)->tm_gmtoff)
# endif
#else
# ifdef MSDOS_LIKE
#   define TIMEZONE(tmPtr)      0
# else
#  define TIMEZONE(tmPtr)       timezone
# endif
#endif

%}
! !

!Win32OperatingSystem primitiveFunctions!
%{
static int __isWinNT;

/*
 * 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
 * Win32OperatingSystem>>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)
#else
# define __wait wait
#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
		new_path = getcwd(new_path, MAXPATHLEN - 1);
#else
		new_path = getwd(new_path);
#endif
		if (new_path == NULL) 
		    return(NULL);

		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 */

%}
! !

!Win32OperatingSystem class methodsFor:'documentation'!

copyright
"
 COPYRIGHT (c) 1988 by Claus Gittinger
 COPYRIGHT (c) 1998 by eXept Software AG
	      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
"
    NOTICE: this is being developed;
    this class resulted from extracting WIN32 specifics of the old
    OperatingSystemClass into this sub-class.
    You may find some leftover unix stuff, which will go away sooner
    or later.

    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)

    [Class variables:]

	HostName        <String>        remembered hostname

	DomainName      <String>        remembered domainname

	CurrentDirectory <String>       remembered currentDirectories path

    [author:]
	Claus Gittinger

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

examples
"
  various queries
								[exBegin]
    Transcript 
	showCR:'hello ' , (OperatingSystem getLoginName)
								[exEnd]

								[exBegin]
    OperatingSystem isUNIXlike ifTrue:[
	Transcript showCR:'this is some UNIX-like OS'
    ] ifFalse:[
	Transcript showCR:'this OS is not UNIX-like'
    ]
								[exEnd]

								[exBegin]
    Transcript 
	showCR:'this machine is called ' , OperatingSystem getHostName
								[exEnd]

								[exBegin]
    Transcript 
	showCR:('this machine is in the '
	       , OperatingSystem getDomainName
	       , ' domain')
								[exEnd]

								[exBegin]
    Transcript 
	showCR:('this machine''s CPU is a '
	       , OperatingSystem getCPUType
	       )
								[exEnd]

								[exBegin]
    Transcript showCR:'executing ls command ...'.
    OperatingSystem executeCommand:'ls'.
    Transcript showCR:'... done.'.
								[exEnd]

  locking a file 
  (should be executed on two running smalltalks - not in two threads):
								[exBegin]
    |f|

    f := 'testFile' asFilename readWriteStream.

    10 timesRepeat:[
	'about to lock ...' printCR.
	[
	  OperatingSystem 
	    lockFD:(f fileDescriptor)
	    shared:false
	    blocking:false
	] whileFalse:[
	    'process ' print. OperatingSystem getProcessId print. ' is waiting' printCR.
	    Delay waitForSeconds:1
	].
	'LOCKED ...' printCR.
	Delay waitForSeconds:10.
	'unlock ...' printCR.
	(OperatingSystem 
	    unlockFD:(f fileDescriptor)) printCR.
	Delay waitForSeconds:3.
    ]
								[exBegin]
"
! !

!Win32OperatingSystem class methodsFor:'initialization'!

initOSType
    "internal - see if running under win-NT
     (as opposed to win-95)"

%{
    OSVERSIONINFO osvi;

    memset(&osvi, 0, sizeof(OSVERSIONINFO));
    osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
    GetVersionEx (&osvi);

    if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) {
	__isWinNT = 1;
    } else {
	__isWinNT = 0;
    }
%}.
!

initialize
    "initialize the class"

    ObjectMemory addDependent:self.
    HostName := nil.
    DomainName := nil.
    LastErrorNumber := nil.
    PipeFailed := false.
    self initOSType

    "Modified: 13.9.1997 / 10:47:32 / cg"
!

update:something with:aParameter from:changedObject
    "catch image restart and flush some cached data"

    something == #earlyRestart ifTrue:[
	"
	 flush cached data
	"
	HostName := nil.
	DomainName := nil.
	LastErrorNumber := nil.
	PipeFailed := false.
	self initOSType
    ]

    "Modified: 22.4.1996 / 13:10:43 / cg"
    "Created: 15.6.1996 / 15:22:37 / cg"
    "Modified: 7.1.1997 / 19:36:11 / stefan"
! !

!Win32OperatingSystem 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
%}
! !

!Win32OperatingSystem class methodsFor:'error messages'!

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 primitive code, since errnos are not
     * standard across unixes
     */
    char *msg = "unknown error";
    char buffer[128];
    OBJ eno = errNr;

    if (__isSmallInteger(eno)) {
      int __eno = __intVal(eno);

      if (__isWIN32Error(__eno)) {
	switch (__eno & 0xFFFF) {
	    /*
	     * WIN32 GetLastError returns
	     */
	    case ERROR_INVALID_FUNCTION:
		msg = "Invalid function";
		sym = @symbol(ERROR_INVALID_FUNCTION);
		break;

	    case ERROR_BAD_FORMAT:
		msg = "Bad Format";
		sym = @symbol(ERROR_BAD_FORMAT);
		break;

	    case ERROR_FILE_NOT_FOUND:
		msg = "File not found";
		sym = @symbol(ERROR_FILE_NOT_FOUND);
		break;

	    case ERROR_PATH_NOT_FOUND:
		msg = "Path not found";
		sym = @symbol(ERROR_PATH_NOT_FOUND);
		break;

	    case ERROR_TOO_MANY_OPEN_FILES:
		msg = "Too many open files";
		sym = @symbol(ERROR_TOO_MANY_OPEN_FILES);
		break;

	    case ERROR_ACCESS_DENIED:
		msg = "Access denied";
		sym = @symbol(ERROR_ACCESS_DENIED);
		break;

	    case ERROR_INVALID_HANDLE:
		msg = "Invalid handle";
		sym = @symbol(ERROR_INVALID_HANDLE);
		break;

	    case ERROR_NOT_ENOUGH_MEMORY:
		msg = "Not enough memory";
		sym = @symbol(ERROR_NOT_ENOUGH_MEMORY);
		break;

	    case ERROR_INVALID_ACCESS:
		msg = "Invalid access";
		sym = @symbol(ERROR_INVALID_ACCESS);
		break;

	    case ERROR_OUTOFMEMORY:
		msg = "Out of memory";
		sym = @symbol(ERROR_OUTOFMEMORY);
		break;

	    case ERROR_BROKEN_PIPE:
		msg = "Broken pipe";
		sym = @symbol(ERROR_BROKEN_PIPE);
		break;

	    case ERROR_WRITE_PROTECT:
		msg = "Write protected";
		sym = @symbol(ERROR_WRITE_PROTECT);
		break;

	    case ERROR_WRITE_FAULT:
		msg = "Write fault";
		sym = @symbol(ERROR_WRITE_FAULT);
		break;

	    case ERROR_READ_FAULT:
		msg = "Read fault";
		sym = @symbol(ERROR_READ_FAULT);
		break;

	    case ERROR_HANDLE_DISK_FULL:
		msg = "Disk full";
		sym = @symbol(ERROR_HANDLE_DISK_FULL);
		break;

	    case ERROR_SHARING_VIOLATION:
		msg = "Sharing violation";
		sym = @symbol(ERROR_SHARING_VIOLATION);
		break;

	    case ERROR_LOCK_VIOLATION:
		msg = "Lock violation";
		sym = @symbol(ERROR_LOCK_VIOLATION);
		break;

	    case ERROR_INVALID_PARAMETER:
		msg = "Invalid parameter";
		sym = @symbol(ERROR_INVALID_PARAMETER);
		break;

	    case ERROR_NET_WRITE_FAULT:
		msg = "Net write fault";
		sym = @symbol(ERROR_NET_WRITE_FAULT);
		break;

	    case ERROR_DRIVE_LOCKED:
		msg = "Drive locked";
		sym = @symbol(ERROR_DRIVE_LOCKED);
		break;

	    default:
		sym = nil;
		break;
	}
      } else {
	switch (__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:
		sym = nil;
		break;
	}
      }
      if (sym == nil) {
          if (__isWIN32Error(__eno)) {
	      sprintf(buffer, "WinErrorNr: %d", __eno & 0xFFFF);
	  } else {
	      sprintf(buffer, "ErrorNr: %d", __eno);
	  }
	  msg = buffer;
	  sym = @symbol(ERROR_OTHER);
      }
      text = __MKSTRING(msg);
    } else {
      text = nil;
      sym = nil;
    }
%}.
    ^ Array with:sym with:text

    "
     OperatingSystem errorSymbolAndTextForNumber:4
    "
! !

!Win32OperatingSystem class methodsFor:'executing OS commands'!

commandAndArgsForOSCommand:aCommandString
    "get a shell and shell arguments for command execution"

    |shell args wDir path words hasRedirection|

    "/
    "/ 'x:\WINNT\System32\cmd /c <command>'
    "/ or 'x:\WINDOWS\System32\cmd /c <command>'
    "/ or 'x:\WINDOWS\System\cmd /c <command>'
    "/ or whatever ...
    "/

    "/ to workaround a bug in win95's command.com 
    "/ (which always returns a 0-exit code 
    "/  - even if the command failed),
    "/ Here, we see if the command is found along the path and
    "/ call it directly if found.
    "/ If not found, assume its a builtIn or batch command
    "/ and pass it to command.com.
    "/ Also use command.com, if any I/O redirection is
    "/ involved, since that is (not yet) handled here.
    "/
    "/ I know: this is a kludge but should work for now...
    "/ ...this will change in an upcoming version to include
    "/ command.com command-line parsing here (sigh).

    hasRedirection := false.
    (aCommandString isNil or:[aCommandString includesAny:'<>|']) ifTrue:[
	hasRedirection := true
    ] ifFalse:[
	words := aCommandString asCollectionOfSubstringsSeparatedBy:Character space.
	args := ' '.
	words from:2 to:(words size) do:[:s | 
	    args := args , (s , ' ').
	].
    ].

    "/ I/O redirection is not yet handled directly
    "/ fallBack to command.com (below) to do it.

    hasRedirection ifFalse:[
	path := self pathOfCommand:(words at:1).
	path notNil ifTrue:[
	    "/ execute the command directly -
	    "/ without going through command.com

	    self isMSWINDOWSNTlike ifTrue:[
		args := path , args.
	    ].

	    ^ Array with:path with:args
	].
    ].

    "/ windows-NT (where command.com works)
    "/ or I/O redirection or no executable was found

    shell := self getEnvironment:'COMSPEC'.
    shell isNil ifTrue:[
	wDir := self getWindowsSystemDirectory asFilename.
	shell := (wDir construct:'cmd.exe').
	shell exists ifFalse:[
	    shell := (wDir construct:'command.com').
	    shell exists ifFalse:[
		self error:'no command.com available'.
	    ]
	].
	shell := shell pathName.
    ].

    aCommandString notNil ifTrue:[
	shell := shell , ' /c '.
    ].
    ^ Array with:shell with:aCommandString

    "Modified: / 20.1.1998 / 16:57:19 / md"
    "Modified: / 27.4.1999 / 18:07:12 / cg"
!

exec:aCommandPath withArguments:argArray fileDescriptors:fdArray closeDescriptors:closeFdArray fork:doFork newPgrp:newPgrp inDirectory:aDirectory
    "Internal lowLevel entry for combined fork & exec for WIN32

     If fork is false (chain a command):
	 execute the OS command specified by the argument, aCommandPath, with
	 arguments in argArray (no arguments, if nil).
	 If successful, this method does not return and smalltalk is gone.
	 If not successful, it does return.
	 Normal use is with forkForCommand.

     If fork is true (subprocess command execution):
	fork a child to do the above.
	The process id of the child process is returned; nil if the 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.
	fdArray[0] == StdIn for child
	fdArray[1] == StdOut for child
	fdArray[2] == StdErr for child
	on VMS, these must be channels as returned by createMailBox.

     closeFdArray contains descriptors that will be closed in the subprocess.
	closeDescriptors are ignored in the WIN32 & VMS versions.

     NOTE that in WIN32 the fds are HANDLES!!

     If newPgrp is true, the subprocess will be established in a new process group.
	The processgroup will be equal to id.
	newPgrp is not used on WIN32 and VMS systems."

    |path|

    aDirectory notNil ifTrue:[
	path := aDirectory asFilename asAbsoluteFilename osNameForDirectory.
	(path endsWith:':') ifTrue:[
	    path := path , '\'.
	].
    ].

    ^ self 
	primExec:aCommandPath 
	withArguments:argArray 
	fileDescriptors:fdArray 
	closeDescriptors:closeFdArray 
	fork:doFork 
	newPgrp:newPgrp 
	inPath:path

    "Modified: / 31.1.1998 / 10:54:24 / md"
    "Modified: / 10.11.1998 / 20:44:24 / cg"
!

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

%{
    DWORD endStatus;
    int status = -1;

    if (__isExternalAddress(aProcessId) ) 
    {
	HANDLE handle = _HANDLEVal(aProcessId);
	if (handle)
	{
	    endStatus = WaitForSingleObject(handle , INFINITE );
	    if ( endStatus !=  WAIT_FAILED ) 
	    {
		if (GetExitCodeProcess(handle,&endStatus))
		{
		     status = endStatus;
		     printf("getexitcode status = %d\n",status);
		}
		else
		  printf("getexitcode failed.\n");
	    }
	}
	RETURN ( __MKSMALLINT(status));
    }
%}.
    self primitiveFailed
!

pathOfCommand:aCommand
    "find where aCommand's executable file is;
     return its full pathName if there is such a command, otherwise
     return nil."

    |path f fExt|

    aCommand asFilename isAbsolute ifTrue:[
	^ aCommand
    ].

    path := self getEnvironment:'PATH'.   
    path notNil ifTrue:[
	(path asCollectionOfSubstringsSeparatedBy:(self pathSeparator)) do:[:path |
	    path isEmpty ifTrue:[
		f := aCommand asFilename
	    ] ifFalse:[
		f := path asFilename construct:aCommand.
	    ].
	    self executableFileExtensions do:[:ext |
		ext notEmpty ifTrue:[
		    fExt := (f pathName , '.' , ext) asFilename.
		] ifFalse:[
		    fExt := f.
		].    
		fExt isExecutable ifTrue:[
		    ^ fExt pathName
		].
	    ].
	].
    ].
    ^ nil

    "windows:

     OperatingSystem pathOfCommand:'bcc32'
    "

    "Modified: / 10.9.1998 / 17:51:49 / cg"
!

primExec:aCommandPath withArguments:argArray fileDescriptors:fdArray closeDescriptors:closeFdArray fork:doFork newPgrp:newPgrp inPath:dirName
    "Internal lowLevel entry for combined fork & exec for WIN32"
%{  /* xxLIMITEDSTACK(WIN32) */

    /*
     * if fork is false, chain to another command (not yet supported)
     * otherwise, spawn a subprocess and let it execute the command.
     * Currently, only the forking version is supported (who chains anyway ?)
     */
    char fullCmdPathBuffer[1024];
    char fullCmdLine[1024];
    char fullDirName[1024];
    char *fullCmdPath = fullCmdPathBuffer;
    char *dir = 0;
    DWORD               fdwCreate = 0;
    STARTUPINFO         lpsiStartInfo;
    PROCESS_INFORMATION lppiProcInfo;
    SECURITY_ATTRIBUTES process;

    if (__isString(dirName)) {
	strcpy( fullDirName, __stringVal(dirName) );
	dir = fullDirName;
    }
    if (__isString(aCommandPath) && __isString(argArray)){
	/*
	 * generate command line (cmd plus args)
	 */
	if (__isWinNT) {
	    char *d;

	    strcpy( fullCmdPath, __stringVal(aCommandPath) );
	    d = strchr(fullCmdPath,' ');
	    if (d) {
		*d++ = 0;
		strcpy( fullCmdLine, d);
	    } else {
		fullCmdLine[0] = '\0';
	    }
	} else {
	    //fullCmdPath = 0;
	    strcpy( fullCmdPath,"stxspawn.exe");
	    strcpy( fullCmdLine,"stxspawn.exe ");
	    strcat( fullCmdLine, __stringVal(aCommandPath));
	}
	strcat( fullCmdLine, __stringVal(argArray) );

	/*
	 * create descriptors as req'd
	 */
	process.nLength = sizeof( process );
	process.lpSecurityDescriptor = NULL;
	process.bInheritHandle = TRUE;

	lpsiStartInfo.cb                = sizeof(PROCESS_INFORMATION);
	lpsiStartInfo.lpReserved        = NULL;
	lpsiStartInfo.lpDesktop         = NULL;
	lpsiStartInfo.lpTitle           = NULL;
	lpsiStartInfo.dwX               = 0;
	lpsiStartInfo.dwY               = 0;
	lpsiStartInfo.dwXSize           = 100;
	lpsiStartInfo.dwYSize           = 100;
	lpsiStartInfo.dwXCountChars     = 0;
	lpsiStartInfo.dwYCountChars     = 0;
	lpsiStartInfo.dwFillAttribute   = 0;
	if (0/*__isWinNT*/) {
	  lpsiStartInfo.dwFlags           = STARTF_USESTDHANDLES;
	  lpsiStartInfo.wShowWindow       = SW_SHOWDEFAULT;
	} else {
	  lpsiStartInfo.dwFlags           = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES /*| STARTF_USEPOSITION*/;
	  lpsiStartInfo.wShowWindow       = SW_HIDE /*SW_SHOWDEFAULT*/;
	}
	lpsiStartInfo.cbReserved2       = 0;
	lpsiStartInfo.lpReserved2       = NULL;
	lpsiStartInfo.hStdInput         = NULL;
	lpsiStartInfo.hStdOutput        = NULL;
	lpsiStartInfo.hStdError         = NULL;

	/* set create process flags */
	if (__isWinNT)
	    fdwCreate = 0; //IDLE_PRIORITY_CLASS;
	else
	    fdwCreate = CREATE_NEW_CONSOLE; //|IDLE_PRIORITY_CLASS; // DETACHED_PROCESS; // NORMAL_PRIORITY_CLASS ;

	if (newPgrp == true) {
	    fdwCreate |= CREATE_NEW_PROCESS_GROUP;
	}
	if (fdArray == nil) {
	   lpsiStartInfo.hStdInput  = (HANDLE) _get_osfhandle (0);
	   lpsiStartInfo.hStdOutput = (HANDLE) _get_osfhandle (1);
	   lpsiStartInfo.hStdError  = (HANDLE) _get_osfhandle (2);
	} else if (__isArray(fdArray) && (__arraySize(fdArray) == 3)) {
	    if (__ArrayInstPtr(fdArray)->a_element[0] != nil) {
		if (__isExternalAddress(__ArrayInstPtr(fdArray)->a_element[0])) {
		    lpsiStartInfo.hStdInput = _HANDLEVal(__ArrayInstPtr(fdArray)->a_element[0]);
		} else {
		    lpsiStartInfo.hStdInput = (HANDLE) _get_osfhandle (__intVal(__ArrayInstPtr(fdArray)->a_element[0]));
		}
	    }
	    if (__ArrayInstPtr(fdArray)->a_element[1] != nil) {
		if (__isExternalAddress(__ArrayInstPtr(fdArray)->a_element[1])) {
		    lpsiStartInfo.hStdOutput = _HANDLEVal(__ArrayInstPtr(fdArray)->a_element[1]);
		} else {
		    lpsiStartInfo.hStdOutput = (HANDLE) _get_osfhandle (__intVal(__ArrayInstPtr(fdArray)->a_element[1]));
		}
	    }
	    if (__ArrayInstPtr(fdArray)->a_element[2] != nil) {
		if (__isExternalAddress(__ArrayInstPtr(fdArray)->a_element[2])) {
		    lpsiStartInfo.hStdError  = _HANDLEVal(__ArrayInstPtr(fdArray)->a_element[2]);
		} else {
		    lpsiStartInfo.hStdError = (HANDLE) _get_osfhandle (__intVal(__ArrayInstPtr(fdArray)->a_element[2]));
		}
	    }
#ifdef PROCESSDEBUGWIN32
	    printf("stdin %x\n", lpsiStartInfo.hStdInput);
	    printf("stdout %x\n",lpsiStartInfo.hStdOutput);
	    printf("stderr %x\n",lpsiStartInfo.hStdError);
#endif
	} else {
	    fprintf(stderr, "Win32OS: bad fd arg in createProcess\n");
	}

	if (doFork == true) {
#ifdef PROCESSDEBUGWIN32
	    printf( "create process >%s< >%s< in >%s<\n", fullCmdPath,fullCmdLine,dir);
#endif
	    if (CreateProcess(  fullCmdPath,
				fullCmdLine,
				&process, &process, /* sec-attribs */
				TRUE,               /* inherit handles */
				fdwCreate,
				NULL,               /* env */
				dir,
				&lpsiStartInfo,
				&lppiProcInfo ))
	    {
		CloseHandle(lppiProcInfo.hThread);
#ifdef PROCESSDEBUGWIN32
		printf( "created process hProcess=%x\n", lppiProcInfo.hProcess);
#endif
		RETURN (__MKEXTERNALADDRESS(lppiProcInfo.hProcess));
	    }
#ifdef PROCESSDEBUGWIN32
	    printf( "created process error %d\n", GetLastError());
#endif
	    RETURN (nil);
	} else {
	    ; /* should never be called that way */
	}
    }
%}.
    "
     path-argument not string
     or argArray not an array/nil
     or malloc failed
     or not supported by OS
    "
    ^ self primitiveFailed
!

startProcess:aCommandString inputFrom:anExternalInStream outputTo:anExternalOutStream errorTo:anExternalErrStream inDirectory:dirOrNil
    "start executing the OS 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 ('cmd.com' in your MSDOS 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 shellAndArgs|

    anExternalInStream notNil ifTrue:[
	in := anExternalInStream fileDescriptor.
    ].
    anExternalOutStream notNil ifTrue:[
	out := anExternalOutStream fileDescriptor.
    ].
    anExternalErrStream notNil ifTrue:[
	err := anExternalErrStream fileDescriptor.
    ].

    shellAndArgs := self commandAndArgsForOSCommand:aCommandString.
    ^ self
	exec:(shellAndArgs at:1)
	withArguments:(shellAndArgs at:2)
	fileDescriptors:(Array with:in with:out with:err)
	closeDescriptors:nil
	fork:true
	newPgrp:false
	inDirectory:dirOrNil

    "Modified: / 10.11.1998 / 20:43:12 / cg"
    "Created: / 10.11.1998 / 20:48:35 / cg"
! !

!Win32OperatingSystem class methodsFor:'file access'!

closeFd:anInteger
    "low level close of a filedescriptor"

%{
     if (__isExternalAddress(anInteger) ) {
	if( !CloseHandle( anInteger ) ) {
	    fprintf( stderr, "Could not close handle : %d\n", anInteger);
	}
	RETURN(self);
     } else {
	if (__isSmallInteger(anInteger)) {
	    close(__intVal(anInteger));
	    RETURN(self);
	}
     }
%}.
     ^ self primitiveFailed.
!

createDirectory:aPathName
    "create a new directory with name 'aPathName', which may be an absolute
     path, or relative to the current directory.
     Return true if successful (or the directory existed already), false if failed.
     This is a low-level entry - use Filename protocol for compatibility."

    "/ if it already exists this is ok

    (self isDirectory:aPathName) ifTrue:[^ true].

%{
    if (__isString(aPathName)) {
	int ret;
	SECURITY_ATTRIBUTES sa;

	sa.nLength = sizeof( sa );
	sa.lpSecurityDescriptor = NULL;
	sa.bInheritHandle = TRUE;

	ret = CreateDirectory(__stringVal(aPathName), &sa);
	if (ret != TRUE) {
	    @global(LastErrorNumber) = __MKSMALLINT(__WIN32_ERR(GetLastError()));
	    RETURN (false);
	}
	RETURN (true);
      }
%}.
    self primitiveFailed

    "
     OperatingSystem createDirectory:'foo'  
    "

    "Modified: 20.12.1995 / 11:24:13 / stefan"
    "Modified: 29.6.1996 / 14:06:54 / 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."

    (oldPath isString not or:[newPath isString not]) ifTrue:[
	"/
	"/ bad argument(s) given
	"/
	^ self primitiveFailed 
    ].

    "/
    "/ this OperatingSystem does not support links
    "/
    ^ UnsupportedOperationSignal raise

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

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

    ^ false

    "Modified: / 4.6.1998 / 04:29:49 / cg"
!

removeDirectory:fullPathName
    "remove the directory named 'fullPathName'. 
     The directory must be empty and you must have appropriate access rights.
     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)) {
#ifdef DO_WRAP_CALLS
	{
	    char _aPathName[MAXPATHLEN];

	    strncpy(_aPathName, __stringVal(fullPathName), MAXPATHLEN-1); _aPathName[MAXPATHLEN-1] = '\0';
	    do {    
		ret = __STX_API_CALL1( (void *)RemoveDirectory, (void *)_aPathName);
	    } while ((ret < 0) && (errno == EINTR));
	}
#else
	ret = RemoveDirectory((char *) __stringVal(fullPathName));
#endif
	if (ret != TRUE) {
	    @global(LastErrorNumber) = __MKSMALLINT( __WIN32_ERR(GetLastError()) );
	    RETURN (false);
	}
	RETURN (true);
    }
%}.
    "/
    "/ either not a string argument,
    "/ or not supported by OS
    "/
    ^ 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)) {
#ifdef DO_WRAP_CALLS
	{
	    char _aPathName[MAXPATHLEN];

	    strncpy(_aPathName, __stringVal(fullPathName), MAXPATHLEN-1); _aPathName[MAXPATHLEN-1] = '\0';
	    do {    
		ret = __STX_API_CALL1( (void *)DeleteFile, (void *)_aPathName);
	    } while ((ret < 0) && (errno == EINTR));
	}
#else
	ret = DeleteFile((char *)__stringVal(fullPathName));
#endif
	if (ret != TRUE) {
	    @global(LastErrorNumber) = __MKSMALLINT( __WIN32_ERR(GetLastError()) );
	    RETURN (false);
	}
	RETURN (true);
    }
%}.
    ^ self primitiveFailed
!

renameFile:oldPath to:newPath
    "rename the file 'oldPath' to 'newPath'. 
     Someone else has to care for the names to be correct and 
     correct for the OS used - therefore, this should not be called
     directlt. Instead, use Filename protocol to rename; this cares for
     any invalid names.
     Returns true if successful, false if not"

%{
    int ret, eno;

    if (__isString(oldPath) && __isString(newPath)) {
#ifdef DO_WRAP_CALLS
	char _oldPath[MAXPATHLEN], _newPath[MAXPATHLEN];

	strncpy(_oldPath, __stringVal(oldPath), MAXPATHLEN-1); _oldPath[MAXPATHLEN-1] = '\0';
	strncpy(_newPath, __stringVal(newPath), MAXPATHLEN-1); _newPath[MAXPATHLEN-1] = '\0';

	do {
	    ret = __STX_C_CALL2( (void*)rename, (void *)_oldPath, (void *)_newPath);
	} while ((ret < 0) && (errno == EINTR));

	if (ret < 0) {
	    /*@global(LastErrorNumber) = __MKSMALLINT(__WIN32_ERR(GetLastError()));*/
	    @global(LastErrorNumber) = __MKSMALLINT(errno);
	    RETURN (false);
	}
	RETURN (true);
#else
    
#if defined(HAS_RENAME)
	__BEGIN_INTERRUPTABLE__
	do {
	    ret = rename((char *) __stringVal(oldPath), (char *) __stringVal(newPath));
	} while (ret < 0 && errno == EINTR);
	__END_INTERRUPTABLE__
#endif
	if (ret < 0) {
	    @global(LastErrorNumber) = __MKSMALLINT(errno);
	    RETURN ( false );
	}
	RETURN (true);
#endif
    }
%}.
    ^ 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."

    ^ self primitiveFailed
! !

!Win32OperatingSystem 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 */
    /* posix systems should define these ... */
#   ifndef S_IRUSR
#    define S_IRUSR 0400
#   endif
#   ifndef S_IWUSR
#    define S_IWUSR 0200
#   endif
#   ifndef S_IXUSR
#    define S_IXUSR 0100
#   endif
#   ifndef S_IRGRP
#    define S_IRGRP 0040
#   endif
#   ifndef S_IWGRP
#    define S_IWGRP 0020
#   endif
#   ifndef S_IXGRP
#    define S_IXGRP 0010
#   endif
#   ifndef S_IROTH
#    define S_IROTH 0004
#   endif
#   ifndef S_IWOTH
#    define S_IWOTH 0002
#   endif
#   ifndef S_IXOTH
#    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)) {
#ifdef DO_WRAP_CALLS
	char _aPathName[MAXPATHLEN];

	strncpy(_aPathName, __stringVal(aPathName), MAXPATHLEN-1); _aPathName[MAXPATHLEN-1] = '\0';

	do {
	    ret = __STX_C_CALL2( (void*)stat, (void *)_aPathName, (void *) &buf);
	} while ((ret < 0) && (errno == EINTR));
#else
	__BEGIN_INTERRUPTABLE__
	do {
	    ret = stat((char *) __stringVal(aPathName), &buf);
	} while ((ret < 0) && (errno == EINTR));
	__END_INTERRUPTABLE__
#endif

	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)) {
#ifdef DO_WRAP_CALLS
	int chmod();
	char _aPathName[MAXPATHLEN];

	strncpy(_aPathName, __stringVal(aPathName), MAXPATHLEN-1); _aPathName[MAXPATHLEN-1] = '\0';
	do { 
	    ret = __STX_C_CALL2( (void*)(chmod), (void *)_aPathName, (void *) __intVal(modeBits));
	} while ((ret < 0) && (errno == EINTR));
#else
	__BEGIN_INTERRUPTABLE__
	do {
	    ret = chmod((char *)__stringVal(aPathName), __intVal(modeBits));
	} while (ret < 0 && errno == EINTR);
	__END_INTERRUPTABLE__
#endif
	if (ret < 0) {
	    @global(LastErrorNumber) = __MKSMALLINT(errno);
	    RETURN ( false );
	}
	RETURN ( true );
    }
%}.
    ^ self primitiveFailed
! !

!Win32OperatingSystem class methodsFor:'file queries'!

caseSensitiveFilenames
    "return true, if the OS has caseSensitive file naming.
     On MSDOS, this will return false; 
     on a real OS, we return true."

    ^ false
!

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 n "{ Class: SmallInteger }" |

    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
    "
    n := names size.
    [(n >= 2) and:[names startsWith:#('.')]] whileTrue:[
	names removeFirst.
	n := n - 1.
    ].

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

    ^ names asStringWith:self fileSeparator 
		    from:1
		    to:n
		    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/..///' 
    "

    "Modified: 1.11.1996 / 20:13:48 / cg"
!

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)"

    ^ $\
!

getDriveList
    "return a list of volumes in the system. 
     On unix, no such thing like a volume exists 
     - there, a syntetic list with root, home & current is returned. 
     On MSDOS, a list of drive letters is (eventually) returned.
     On VMS, a list of volumes is (eventually) returned."

    |list|

    list := OrderedCollection new.
%{
    /*
     * add drive letters as strings to list ...
     */
    char buffer[1024];
    char *cp;

    GetLogicalDriveStrings(1023, buffer);
    for (cp=buffer; *cp; ) {
      __SSEND1(list, @symbol(add:), 0, __MKSTRING(cp));
      cp += strlen(cp) + 1;
    }
%}.
    ^ list
!

infoOf:aPathName
    "return some object filled with info for the file 'aPathName';
     the info (for which corresponding access methods are understood by
     the returned object) is:
	 type            - a symbol giving the files type
	 mode            - numeric access mode 
	 uid             - owners user id
	 gid             - owners group id
	 size            - files size
	 id              - files number (i.e. inode number)
	 accessed        - last access time (as Timestamp)
	 modified        - last modification time (as Timestamp)
	 statusChanged   - last status change time (as Timestamp)
	 alternativeName - (windows only:) the MSDOS name of the file

     Some of the fields may be returned as nil on systems which do not provide
     all of the information.
     Return nil if such a file does not exist. 
     For symbolic links (if supported by the OS), 
     the info of the pointed-to-file (i.e. the target) is returned;
     use #linkInfoOf: to get info about the link itself.
    "

    |info type mode uid gid size id 
     atime mtime ctime 
     aOStime mOStime cOStime
     aYr aMon aDay aHr aMin aSec aMS
     mYr mMon mDay mHr mMin mSec mMS
     cYr cMon cDay cHr cMin cSec cMS
     name2|

%{
    struct stat buf;
    int ret;
    char nameBuffer[15];
    unsigned INT ino;

    if (__isString(aPathName)) {
	HANDLE hFile;
	SYSTEMTIME creationTime;
	SYSTEMTIME accessTime;
	SYSTEMTIME modificationTime;
	int modeBits = 0;
	WIN32_FIND_DATA findStruct;

#ifdef DO_WRAP_CALLS
	{
	    char _aPathName[MAXPATHLEN];

	    strncpy(_aPathName, __stringVal(aPathName), MAXPATHLEN-1); _aPathName[MAXPATHLEN-1] = '\0';
	    hFile = __STX_API_CALL2( (void*)FindFirstFile, (void *)_aPathName, &findStruct);
	}
#else
	hFile = FindFirstFile(__stringVal(aPathName), &findStruct);
#endif
	if (! hFile || (hFile == (HANDLE)(-1)) || (hFile == INVALID_HANDLE_VALUE)) {
	    @global(LastErrorNumber) = __MKSMALLINT(__WIN32_ERR(GetLastError()));
	    RETURN (nil);
	}
	FindClose(hFile);

	id = __MKSMALLINT(0);   /* could get it by opening ... */
	size = __MKLARGEINT64(1, findStruct.nFileSizeLow, findStruct.nFileSizeHigh);

	if (findStruct.cAlternateFileName[0] != '\0') {
	    bcopy(findStruct.cAlternateFileName, nameBuffer, 14);
	    nameBuffer[14] = '\0';
	    name2 = __MKSTRING(nameBuffer); /* DOS name */
	}

	/*
	 * simulate access bits
	 */
	if (findStruct.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
	    modeBits = 0444;
	} else {
	    modeBits = 0666;
	}

	if (findStruct.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
	    type = @symbol(directory);
	    modeBits |= 0111;   /* executable */
	} else {
	    type = @symbol(regular);
	}

	mode = __MKSMALLINT(modeBits);

	/*
	 * sigh - convert from stupid time to useful time
	 */
	FileTimeToSystemTime(&(findStruct.ftCreationTime), &creationTime);
	FileTimeToSystemTime(&(findStruct.ftLastAccessTime), &accessTime);
	FileTimeToSystemTime(&(findStruct.ftLastWriteTime), &modificationTime);
	aYr  = __MKSMALLINT(accessTime.wYear);
	aMon = __MKSMALLINT(accessTime.wMonth);
	aDay = __MKSMALLINT(accessTime.wDay);
	aHr  = __MKSMALLINT(accessTime.wHour);
	aMin = __MKSMALLINT(accessTime.wMinute);
	aSec = __MKSMALLINT(accessTime.wSecond);
	aMS  = __MKSMALLINT(accessTime.wMilliseconds);

	mYr  = __MKSMALLINT(modificationTime.wYear);
	mMon = __MKSMALLINT(modificationTime.wMonth);
	mDay = __MKSMALLINT(modificationTime.wDay);
	mHr  = __MKSMALLINT(modificationTime.wHour);
	mMin = __MKSMALLINT(modificationTime.wMinute);
	mSec = __MKSMALLINT(modificationTime.wSecond);
	mMS  = __MKSMALLINT(modificationTime.wMilliseconds);

	cYr  = __MKSMALLINT(creationTime.wYear);
	cMon = __MKSMALLINT(creationTime.wMonth);
	cDay = __MKSMALLINT(creationTime.wDay);
	cHr  = __MKSMALLINT(creationTime.wHour);
	cMin = __MKSMALLINT(creationTime.wMinute);
	cSec = __MKSMALLINT(creationTime.wSecond);
	cMS  = __MKSMALLINT(creationTime.wMilliseconds);
    }
%}.
    mode notNil ifTrue:[
	aOStime notNil ifTrue:[
	    atime := AbsoluteTime fromOSTime:(aOStime * 1000).
	    mtime := AbsoluteTime fromOSTime:(mOStime * 1000).
	    ctime := AbsoluteTime fromOSTime:(cOStime * 1000).
	] ifFalse:[
	    atime := AbsoluteTime day:aDay month:aMon year:aYr hour:aHr minutes:aMin seconds:aSec milliseconds:aMS.
	    mtime := AbsoluteTime day:mDay month:mMon year:mYr hour:mHr minutes:mMin seconds:mSec milliseconds:mMS.
	    ctime := AbsoluteTime day:cDay month:cMon year:cYr hour:cHr minutes:cMin seconds:cSec milliseconds:cMS.
	].

	info := FileStatusInfo
		    type:type 
		    mode:mode 
		    uid:uid 
		    gid:gid 
		    size:size 
		    id:id 
		    accessed:atime 
		    modified:mtime 
		    statusChanged:ctime
		    path:nil
		    alternativeName:name2.
	^ info
   ].
   ^ self primitiveFailed

   "
    OperatingSystem infoOf:'/'
    (OperatingSystem infoOf:'/') uid
    (OperatingSystem infoOf:'/') 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:."

%{
    int ret;

    if (__isString(aPathName)) {
#ifdef DO_WRAP_CALLS
	char _aPathName[MAXPATHLEN];

	strncpy(_aPathName, __stringVal(aPathName), MAXPATHLEN-1); _aPathName[MAXPATHLEN-1] = '\0';
	do {
	    ret = __STX_API_CALL1( (void*)GetFileAttributes, (void *)_aPathName);
	} while ((ret < 0) && (errno == EINTR));
#else
	ret = GetFileAttributes((char *) __stringVal(aPathName));
#endif
	if (ret == -1) {
	    @global(LastErrorNumber) = __MKSMALLINT(__WIN32_ERR(GetLastError()));
	    RETURN ( false );
	}
	RETURN ( (ret & FILE_ATTRIBUTE_DIRECTORY) ? true : false);
    }
%}.
    ^ self primitiveFailed

    "an alternative implementation would be:
	^ (self infoOf:aPathName) 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)) {
#ifdef DO_WRAP_CALLS
	int access();
	char _aPathName[MAXPATHLEN];

	strncpy(_aPathName, __stringVal(aPathName), MAXPATHLEN-1); _aPathName[MAXPATHLEN-1] = '\0';
	do {
	    ret = __STX_C_CALL2( (void*)access, (void *)_aPathName, (void *) X_OK);
	} while ((ret < 0) && (errno == EINTR));
#else
	__BEGIN_INTERRUPTABLE__
	do {
	    ret = access(__stringVal(aPathName), X_OK);
	} while ((ret < 0) && (errno == EINTR));
	__END_INTERRUPTABLE__
#endif
	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)) {
#ifdef DO_WRAP_CALLS
	int access();
	char _aPathName[MAXPATHLEN];

	strncpy(_aPathName, __stringVal(aPathName), MAXPATHLEN-1); _aPathName[MAXPATHLEN-1] = '\0';
	do { 
	    ret = __STX_C_CALL2( (void*)access, (void *)_aPathName, (void *) R_OK);
	} while ((ret < 0) && (errno == EINTR));
#else
	__BEGIN_INTERRUPTABLE__
	do {
	    ret = access(__stringVal(aPathName), R_OK);
	} while ((ret < 0) && (errno == EINTR));
	__END_INTERRUPTABLE__
#endif
	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) ) {
#ifdef DO_WRAP_CALLS
	char _aPathName[MAXPATHLEN];

	strncpy(_aPathName, __stringVal(aPathName), MAXPATHLEN-1); _aPathName[MAXPATHLEN-1] = '\0';
	do {
	    ret = __STX_API_CALL1( (void*)GetFileAttributes, (void *)_aPathName);
	} while ((ret < 0) && (errno == EINTR));
#else
	ret = GetFileAttributes((char *) __stringVal(aPathName));
#endif
	if (ret == -1) {
	    RETURN ( false );
	}
	RETURN (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)) {
#ifdef DO_WRAP_CALLS
	char _aPathName[MAXPATHLEN];

	strncpy(_aPathName, __stringVal(aPathName), MAXPATHLEN-1); _aPathName[MAXPATHLEN-1] = '\0';
	do {
	    ret = __STX_API_CALL1( (void*)GetFileAttributes, (void *)_aPathName);
	} while ((ret < 0) && (errno == EINTR));
#else
	ret = GetFileAttributes((char *) __stringVal(aPathName));
#endif
	if (ret == -1) {
	    @global(LastErrorNumber) = __MKSMALLINT(__WIN32_ERR(GetLastError()));
	    RETURN ( false );
	}
	RETURN ( (ret & FILE_ATTRIBUTE_READONLY) ? false : true);
    }
%}.
    ^ 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.
     (which means, that systems like VMS or MSDOS always return nil here.)

     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."
     
    ^ nil
!

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:[
	    p := pathName.
	    [(p size > 1)
	     and:[p endsWith:(self fileSeparator)]
	    ] whileTrue:[
		p := p copyWithoutLast:1.
	    ].
	    ^ p
	].

	"/
	"/ return the original - there is nothing else can we do
	"/
	path := self compressPath:pathName
    ].
    ^ path.

    "
     OperatingSystem pathNameOf:'.'
     OperatingSystem pathNameOf:'../smalltalk/../smalltalk'
     OperatingSystem pathNameOf:'../../..'
     OperatingSystem pathNameOf:'..'
     OperatingSystem pathNameOf:'/tmp////' 
     OperatingSystem pathNameOf:'/foo/bar' 
     OperatingSystem pathNameOf:'/foo/bar/'
     OperatingSystem pathNameOf:'/foo/bar//'
    "

    "Modified: 29.11.1996 / 18:02:12 / stefan"
    "Modified: 10.1.1997 / 19:10:42 / cg"
!

primIdOf:aPathName
    "the actual code to return the fileNumber (i.e. inode number) of a file."

    ^ nil
!

primPathNameOf:aPathName
    "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|

%{  /* xxSTACK: 16000 */

    if (__isString(aPathName)) {
	char nameBuffer[MAXPATHLEN + 1 + MAXPATHLEN + 1];
	char *pFinal;
	int rslt;

#ifdef DO_WRAP_CALLS
	char _aPathName[MAXPATHLEN];

	strncpy(_aPathName, __stringVal(aPathName), MAXPATHLEN-1); _aPathName[MAXPATHLEN-1] = '\0';
	do {
	    rslt = __STX_API_CALL4( (void*)GetFullPathName, (void *)_aPathName, sizeof(nameBuffer), nameBuffer, &pFinal);
	} while ((rslt < 0) && (errno == EINTR));
#else
	rslt = GetFullPathName(__stringVal(aPathName), sizeof(nameBuffer), nameBuffer, &pFinal);
#endif
	if (rslt) {
	    RETURN ( __MKSTRING(nameBuffer) );
	}
    }
%}.
    ^ 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) accessed 
    "
    | i|

    i := self infoOf:aPathName.
    i notNil ifTrue:[^ i accessed].
    ^ nil.

    "
     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) modified
    "

    | i|

    i := self infoOf:aPathName.
    i notNil ifTrue:[^ i modified].
    ^ nil.

    "
     OperatingSystem timeOfLastChange:'/'
    "
!

typeOf:aPathName
    "return the type of a file as a symbol; for nonexistent files,
     nil is returned.
     Notice: for symbolic links, the type of the pointed-to file is returned."

    |i|

    "
     this could have been implemented as:
	(self infoOf:aPathName) type 
    "

    i := self infoOf:aPathName.
    i notNil ifTrue:[^ i type].
    ^ nil.

    "
     OperatingSystem typeOf:'/'   
     OperatingSystem typeOf:'.'   
     OperatingSystem typeOf:'Make.proto' 
     OperatingSystem typeOf:'resources/motif.style' 
    "
!

volumeNameOf:aPathString
    "return the volumeName of the argument, aPath
     - thats the name of the volume where aPath is.
     Not all OperatingSystem support/use volumes; on unix,
     this always returns an empty string."

    (aPathString at:2) == $: ifTrue:[
	^ (aPathString at:1) asString.
    ].
    ^ ''
! !

!Win32OperatingSystem class methodsFor:'interrupts & signals'!

blockingTest
    "this is a test method;
     For testing double CTRL-C in blocking primitives"

%{
    while(1)
    {
	printf("b");
	Sleep(50);
    }
%}.
    "
     OperatingSystem blockingTest 
    "
!

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
    "
!

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

    "
     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 */

    extern void __win32ClearTimer();

    __win32ClearTimer();
%}.
    ^ true
!

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)
!

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

    "
     this error is triggered on non-integer argument
     or if the system does not support SIGIO
    "
    ^ self primitiveFailed
!

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
#if defined(SIGILL) || defined(SIGEMT)
# ifndef __signalTrapInterrupt
    extern void __signalTrapInterrupt(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
#ifdef PROCESSDEBUGWIN32
		printf("ConsoleSignal %d\n",sigNr);
#endif
		SetConsoleCtrlHandler((PHANDLER_ROUTINE)__signalUserInterruptWIN32,TRUE);
		RETURN (self);
#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 SIGILL
	    case SIGILL:
		handler = __signalTrapInterrupt;
		break;
#endif
#ifdef SIGEMT
	    case SIGEMT:
		handler = __signalTrapInterrupt;
		break;
#endif
#ifdef SIGIO
	    case SIGIO:
		handler = __signalIoInterrupt;
		break;
#endif

#ifdef CHILD_SIGNAL
	    case CHILD_SIGNAL:
		handler = __signalChildInterrupt;
		break;
#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
#  ifdef WIN32
#   ifdef PROCESSDEBUGWIN32
	    printf("signal %d can't change handler\n",sigNr);
#   endif
#  else
	    (void) signal(sigNr, handler);
#  endif
# 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
    "setup for a timerInterrupt, to be signalled after some (real) time."

%{  /* NOCONTEXT */
    extern void __win32SetTimer();

    if (__isSmallInteger(milliSeconds)) {
	int millis;
	millis = __intVal(milliSeconds);
	__win32SetTimer(millis);
	RETURN (true);
    }
%}.
    ^ false
!

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 primTerminateProcess:processId
!

primTerminateProcess:pid
    "terminate a WIN32 process.
    The TerminateProcess function is used to unconditionally cause
    a process to exit. Use it only in extreme circumstances. The state of
    global data maintained by dynamic-link libraries (DLLs)
    may be compromised if TerminateProcess is used."

%{  /* xxLIMITEDSTACK (WIN95 only)*/
    if (__isExternalAddress(pid) ) {
#ifdef PROCESS1DEBUGWIN32
	printf("Terminate ProcessHandle %x\n",_HANDLEVal(pid));
#endif
	if (_HANDLEVal(pid) != 0) {
	    TerminateProcess(_HANDLEVal(pid),-1);
	    CloseHandle(_HANDLEVal(pid));
	    _HANDLEVal(pid) = 0;
	}
    }
#ifdef PROCESS1DEBUGWIN32
    else
      printf("Terminate wrong ProcessHandle\n");
#endif
%}
!

primTerminateProcessGroup:processGroupId
    "terminate a WIN32 process.
    The TerminateProcess function is used to unconditionally cause
    a process to exit. Use it only in extreme circumstances. The state of
    global data maintained by dynamic-link libraries (DLLs)
    may be compromised if TerminateProcess is used."

%{  /* xxLIMITEDSTACK (WIN95 only)*/
    if (__isExternalAddress(processGroupId) ) {
#ifdef PROCESS1DEBUGWIN32
	printf("primTerminateProcessGroup ProcessHandle %x not yet implemented.\n",_HANDLEVal(processGroupId));
#endif
	if (_HANDLEVal(processGroupId) != 0) {
	}
    }
#ifdef PROCESS1DEBUGWIN32
    else
	printf("primTerminateProcessGroup wrong ProcessGroupHandle\n");
#endif
%}
!

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."

    "/
    "/ 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."

    ^ false
!

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

    ^ 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 primTerminateProcess:processId

    "Modified: / 28.12.1995 / 15:05:37 / stefan"
    "Modified: / 27.1.1998 / 20:05:47 / cg"
!

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 primTerminateProcessGroup:processGroupId
    "/ ^ UnsupportedOperationSignal raise 

    "Modified: / 28.12.1995 / 15:05:37 / stefan"
    "Created: / 23.4.1996 / 16:40:34 / stefan"
    "Modified: / 27.1.1998 / 20:05:59 / cg"
! !

!Win32OperatingSystem class methodsFor:'ipc support - UNIX'!

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|    
                
%{       
    HANDLE   pipeRead  = (HANDLE)0;
    HANDLE   pipeWrite = (HANDLE)0;

    SECURITY_ATTRIBUTES  process;
 
    process.nLength = sizeof( process );
    process.lpSecurityDescriptor = NULL;
    process.bInheritHandle = TRUE;

    if( ! CreatePipe( &pipeRead, &pipeWrite, &process, 0 ) ) {
	@global(LastErrorNumber) = __MKSMALLINT( __WIN32_ERR(GetLastError()) );
	RETURN ( nil );
    }

#ifdef USE_HANDLES
    fd1 = __MKEXTERNALADDRESS(pipeRead);
    fd2 = __MKEXTERNALADDRESS(pipeWrite);
#else
    /*
     * make fileDescriptors from handles
     */
# ifdef PROCESSDEBUGWIN32
    printf("piperead %x\n",pipeRead);
    printf("pipewrite %x\n",pipeWrite);
# endif
    fd1 = __MKSMALLINT(_open_osfhandle(pipeRead, O_BINARY));
    fd2 = __MKSMALLINT(_open_osfhandle(pipeWrite, O_BINARY));
#endif
%}.
    fd1 notNil ifTrue:[
	^ Array with:fd1 with:fd2.
    ].
    ^ nil
! !

!Win32OperatingSystem class methodsFor:'misc'!

closePid:pid
    "free pid resource"
%{
    if (__isExternalAddress(pid) ) {
	if (_HANDLEVal(pid) != 0) {
#ifdef PROCESSDEBUGWIN32
	    printf("Close ProcessHandle %x\n",_HANDLEVal(pid));
#endif
	    CloseHandle(_HANDLEVal(pid));
	    _HANDLEVal(pid) = 0;
	}
    }
%}.
    ^ true.

    "Created: 28.1.1998 / 14:23:04 / md"
    "Modified: 28.1.1998 / 14:27:18 / md"
! !

!Win32OperatingSystem class methodsFor:'os queries'!

executableFileExtensions
    "return a collection of extensions for executable program files.
     Only req'd for msdos like systems ..."

    ^ #('com' 'exe')

    "Created: 2.5.1997 / 11:42:29 / cg"
!

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

    |name idx hostName|

    DomainName notNil ifTrue:[
	^ DomainName
    ].

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

    if (getdomainname(buffer, sizeof(buffer)) == 0) {
	name = __MKSTRING(buffer);
    }
#endif
%}.
    name isNil ifTrue:[
	name := self getEnvironment:'DOMAIN'.
	name isNil ifTrue:[
	    name := self getEnvironment:'DOMAINNAME'.
	]
    ].
    name isNil ifTrue:[
	"/ sometimes, we can extract the domainName from the hostName ...
	hostName := self getHostName.
	hostName notNil ifTrue:[
	    idx := hostName indexOf:$..
	    idx ~~ 0 ifTrue:[
		name := hostName copyFrom:idx+1.
	    ]
	]. 
	name isNil ifTrue:[
	    'Win32OperatingSystem [warning]: cannot find out domainname' errorPrintCR.
	    name := 'unknown'.
	]
    ].
    DomainName := name.
    ^ name

    "
     OperatingSystem getDomainName
    "

    "Modified: 26.4.1996 / 10:04:54 / stefan"
!

getEnvironment:aStringOrSymbol
    "get an environment string"

%{  /* NOCONTEXT */

    char *env;

    if (__isString(aStringOrSymbol) || __isSymbol(aStringOrSymbol)) {
	char buff[512];

	env = NULL;
	if (GetEnvironmentVariable(__stringVal(aStringOrSymbol),
				   buff,
				   sizeof(buff)-1)) {
	    env = buff;
	}
	if (env) {
	    RETURN ( __MKSTRING(env) );
	}
    }
%}
.
    ^ 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 idx|

    HostName notNil ifTrue:[
	^ HostName
    ].

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

    if (gethostname(buffer, sizeof(buffer)) == 0) {
	name = __MKSTRING(buffer);
    }
#else
    char buffer[128];
    int buffSize = sizeof(buffer);

    if (GetComputerName(buffer, &buffSize) == TRUE) {
	name = __MKSTRING(buffer);
    }
#endif
%}.
    name isNil ifTrue:[
	name := self getEnvironment:'HOST'.
	name isNil ifTrue:[
	    name := self getEnvironment:'HOSTNAME'.
	]
    ].
    name isNil ifTrue:[
	'Win32OperatingSystem [warning]: cannot find out hostname' errorPrintCR.
	name := 'unknown'.
    ] ifFalse:[
	"/ on some systems, the hostname already contains the domain.
	"/ decompose it here.
	idx := name indexOf:$..
	idx ~~ 0 ifTrue:[
	    DomainName := name copyFrom:(idx+1).
	    name := name copyTo:(idx-1).
	]
    ].
    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"
!

getProcessId
    "return the (unix-)processId"

%{  /* NOCONTEXT */

    int pid = 0;

    pid = GetCurrentProcessId() & 0x3FFFFFFF;
    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(HAS_GETHOSTID)
    int runningId;
    OBJ arr;

    runningId = gethostid();
    arr = __BYTEARRAY_UNINITIALIZED_NEW_INT(4);
    *(int *)(__ByteArrayInstPtr(arr)->ba_element) = runningId;
    RETURN (arr);
#endif
#if defined(HAS_SYSINFO) && defined(SI_HW_SERIAL)
    {
	char buffer[128];

	buffer[0] = 0;
	if (sysinfo(SI_HW_SERIAL, buffer, sizeof(buffer))) {
	    buffer[127] = 0;
	    if (strlen(buffer) > 0) {
		RETURN(__MKSTRING(buffer));
	    }
	}
    }
#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).
       Your application should NOT depend upon this in any way.

     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 ...)

     win32:
	#physicalRam -> total amount of physical memory        
	#freeRam -> amount of free memory        
	#swapSize -> size of swapSpace (page file)        
	#freeSwap -> free bytes in swapSpace        
	#virtualRam -> total amount of virtual memory        
	#freeVirtual -> amount of free virtual memory        
	#memoryLoad -> percentage of memory usage (useless)
    "

    |sys node rel ver mach dom mtyp brel info arch
     physicalRam freeRam swapSize freeSwap
     virtualRam freeVirtual memoryLoad|

%{  /* STACK: 4096 */

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

    vsn = GetVersion();

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

    GetSystemInfo(&sysInfo);
    memStatus.dwLength = sizeof(memStatus);
    GlobalMemoryStatus(&memStatus);

    memoryLoad = __MKUINT(memStatus.dwMemoryLoad);
    physicalRam = __MKUINT(memStatus.dwTotalPhys);
    freeRam = __MKUINT(memStatus.dwAvailPhys);
    swapSize = __MKUINT(memStatus.dwTotalPageFile);
    freeSwap = __MKUINT(memStatus.dwAvailPageFile);
    virtualRam = __MKUINT(memStatus.dwTotalVirtual);
    freeVirtual = __MKUINT(memStatus.dwAvailVirtual);

#ifdef __BORLANDC__
    switch (sysInfo.u.s.wProcessorArchitecture)
#else
    switch (sysInfo.wProcessorArchitecture)
#endif
    {
#ifdef PROCESSOR_ARCHITECTURE_INTEL
	case PROCESSOR_ARCHITECTURE_INTEL:
	    s = "intel";
	    break;
#endif
#ifdef PROCESSOR_ARCHITECTURE_MIPS
	case PROCESSOR_ARCHITECTURE_MIPS:
	    s = "mips";
	    break;
#endif
#ifdef PROCESSOR_ARCHITECTURE_ALPHA
	case PROCESSOR_ARCHITECTURE_ALPHA:
	    s = "alpha";
	    break;
#endif
#ifdef PROCESSOR_ARCHITECTURE_PPC
	case PROCESSOR_ARCHITECTURE_PPC:
	    s = "ppc";
	    break;
#endif
#ifdef PROCESSOR_ARCHITECTURE_ARM
	case PROCESSOR_ARCHITECTURE_ARM:
	    s = "arm";
	    break;
#endif
	default:
	    s = "unknown";
	    break;
    }
    arch = __MKSTRING(s);

    switch (sysInfo.dwProcessorType) {
#ifdef PROCESSOR_INTEL_386
	case PROCESSOR_INTEL_386:
	    s = "i386";
	    break;
#endif
#ifdef PROCESSOR_INTEL_486
	case PROCESSOR_INTEL_486:
	    s = "i486";
	    break;
#endif
#ifdef PROCESSOR_INTEL_PENTIUM
	case PROCESSOR_INTEL_PENTIUM:
	    s = "i586";
	    break;
#endif
#ifdef PROCESSOR_INTEL_860
	case PROCESSOR_INTEL_860:
	    s = "i860";
	    break;
#endif
#ifdef PROCESSOR_MIPS_R2000
	case PROCESSOR_MIPS_R2000:
	    s = "r2000";
	    break;
#endif
#ifdef PROCESSOR_MIPS_R3000
	case PROCESSOR_MIPS_R3000:
	    s = "r3000";
	    break;
#endif
#ifdef PROCESSOR_MIPS_R4000
	case PROCESSOR_MIPS_R4000:
	    s = "r4000";
	    break;
#endif
#ifdef PROCESSOR_ALPHA_21064
	case PROCESSOR_ALPHA_21064:
	    s = "alpha21064";
	    break;
#endif
#ifdef PROCESSOR_ARM_7TDMI
	case PROCESSOR_ARM_7TDMI:
	    s = "arm70001";
	    break;
#endif
	default:
	    sprintf(vsnBuffer, "%d", sysInfo.dwProcessorType);
	    s = vsnBuffer;
	    break;
    }
    mach = __MKSTRING(s);
%}.
    sys isNil ifTrue:[
	sys := self getSystemType.
    ].
    node isNil ifTrue:[
	node := self getHostName.
    ].
    dom isNil ifTrue:[
	dom := self getDomainName.
    ].
    mach isNil ifTrue:[
	mach := self getCPUType.
    ].
    arch isNil ifTrue:[
	arch := 'unknown'.
    ].

    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].
    arch notNil ifTrue:[info at:#architecture put:arch].
    dom notNil ifTrue:[info at:#domain put:dom].

    info at:#memoryLoad put:memoryLoad.
    info at:#physicalRam put:physicalRam.
    info at:#freeRam put:freeRam.
    info at:#swapSize put:swapSize.
    info at:#freeSwap put:freeSwap.
    info at:#virtualRam put:virtualRam.
    info at:#freeVirtual put:freeVirtual.

    info at:#osType put:(self getOSType).
    ^ 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)"

    ^ 'win32'

    "
     OperatingSystem getSystemType
    "
!

getWindowsDirectory
    "internal interface - only for Windows based systems.
     Return the windows directory 
     (which - depending on the system - may be \WINNT, \WINDOWS or whatever)
     On non-windows systems, nil is returned."

%{  /* xxLIMITEDSTACK(WIN32) */

    char buffer[MAXPATHLEN];

    if (GetWindowsDirectory(buffer, MAXPATHLEN)) {
	RETURN (__MKSTRING(buffer));
    }
%}.
    ^ nil

    "
     OperatingSystem getWindowsDirectory
    "
!

getWindowsSystemDirectory
    "internal interface - only for Windows based systems.
     Return the windows system directory 
     (which - depending on the system - may be \WINNT\SYSTEM32, 
      \WINDOWS\SYSTEM or whatever)
     On non-windows systems, nil is returned."

%{  /* xxLIMITEDSTACK(WIN32) */

    char buffer[MAXPATHLEN];

    if (GetSystemDirectory(buffer, MAXPATHLEN)) {
	RETURN (__MKSTRING(buffer));
    }
%}.
    ^ nil

    "
     OperatingSystem getWindowsSystemDirectory
    "
!

isMSDOSlike
    "return true, if the OS we're running on is msdos like 
     (in contrast to unix-like or vms-like).
     This returns true for any of msdos, win32s, win95,
     winNT and os/2."

    ^ true
!

isMSWINDOWSNTlike
    "This returns true if running in a Windows-NT system."

%{  
     if (__isWinNT)
	RETURN(true);
%}.
     ^ false.
!

isMSWINDOWSlike
    "return true, if running on a MS-Windows like system.
     This returns true for any of win32s, win95 and winNT."

    ^ true
!

maxFileNameLength
    "return the max number of characters in a filename.
     CAVEAT:
	 Actually, the following is somewhat wrong - some systems
	 support different sizes, depending on the volume.
	 We return a somewhat conservative number here.
	 Another entry, to query for volume specific max
	 will be added in the future."

%{  /* NOCONTEXT */
 
    /*
     * TODO: newer systems provide a query function for this ... use it
     */
     /*
      * mhmh - depends on the filesystem type
      */
     RETURN ( __MKSMALLINT(MAXFILELEN) );
%}
    "
     OperatingSystem maxFileNameLength   
    "
!

maxPathLength
    "return the max number of characters in a pathName."

%{  /* NOCONTEXT */
    RETURN ( __MKSMALLINT(MAXPATHLEN) );
%}
    "
     OperatingSystem maxPathLength   
    "
!

pathSeparator
    "return the character which separates items in the PATH variable"

    ^ $;

    "Created: 2.5.1997 / 11:36:47 / cg"
!

platformName
    "return a string describing the OS platform very we're running on.
     This returns #unix for all unix derivatives,
     #os2, #win32, #vms or #mac for the others.
     I.e. it is much less specific than getOSType or getSystemType."

    ^ #win32

    "
     OperatingSystem platformName
    "

    "Modified: 20.6.1997 / 17:37:26 / cg"
!

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
    "
!

supportsChildInterrupts
    "return true, if the OS supports childProcess termination signalling
     through interrupts (i.e. SIGCHILD)"

%{  /* NOCONTEXT */
#if defined(SIGCHLD) || defined(SIGCLD) || defined(__VMS__)
    RETURN (true);
#endif
%}.
    ^ false

    "
     OperatingSystem supportsChildInterrupts 
    "
!

supportsFileOwnerGroups
    "return true, if the OS's file system supports file
     group ownership - all OS's except windows do"

    ^ false

    "Created: / 10.9.1998 / 17:57:03 / cg"
!

supportsFileOwners
    "return true, if the OS's file system supports file
     ownership - all OS's except windows do"

    ^ false

    "Created: / 10.9.1998 / 17:55:16 / cg"
    "Modified: / 10.9.1998 / 17:57:11 / cg"
!

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

     Currently, this mechanism does not work on all
     systems ...
    "

    ^ false

    "
     OperatingSystem supportsIOInterrupts 
    "
!

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

    ^ false

    "
     OperatingSystem supportsNonBlockingIO  
    "
!

supportsSelect
    "return true, if the OS supports selecting on multiple
     filedescriptors via select.
     If false is returned, ProcessorScheduler will poll in 50ms
     intervals for I/O becoming ready."

%{  /* NOCONTEXT */

# if defined(WIN32S)
    RETURN (false);
# else
    /* RETURN (false);     /* for now ... */
    RETURN (true);
# endif

%}.
    ^ false

    "
     OperatingSystem supportsSelect
    "
!

supportsVolumes
    "return true, if the OS supports disk volumes.
     False is returned for UNIX, true for MSDOS and VMS"

    ^ true
! !

!Win32OperatingSystem class methodsFor:'path queries'!

defaultSystemPath
    |sysPath|

    sysPath := super defaultSystemPath.

    #(
	'\smalltalk'
	'\programme\smalltalk'
	'\programme\exept\smalltalk'
	'\programs\smalltalk'
	'\programs\exept\smalltalk'
    ) do:[:d |
	(d asFilename isDirectory) ifTrue:[
	    sysPath add:d
	]
    ].

    "/ under windows, the commandName includes the path - good.
    sysPath add:(Smalltalk commandName asFilename directoryName).
    ^ sysPath
! !

!Win32OperatingSystem class methodsFor:'private'!

osProcessStatusClass
    ^ OSProcessStatus

    "Created: / 12.6.1998 / 16:30:43 / cg"
! !

!Win32OperatingSystem class methodsFor:'time and date'!

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

    |year month day osSeconds|

    osSeconds := osTime // 1000.
%{
    struct tm* tmPtr;
    INT t;
    TIME_T tt;

    t = __longIntVal(osSeconds);
    tt = (TIME_T)t;

    tmPtr = localtime(&tt);
    year = __MKSMALLINT(tmPtr->tm_year + 1900);
    month = __MKSMALLINT(tmPtr->tm_mon + 1);
    day = __MKSMALLINT(tmPtr->tm_mday);
%}.
    aBlock value:year value:month value:day

    "
     OperatingSystem computeDatePartsOf:0 for:[:y :m :d |
	y printCR. m printCR. d printCR
     ]
    "
!

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

    |osSeconds|

%{
    struct tm tm;
    TIME_T 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);
	osSeconds = __MKUINT((INT)t);
    }
%}.
    osSeconds notNil ifTrue:[
	^ osSeconds * 1000 + millis
    ].    
    ^ self primitiveFailed

    "
     OperatingSystem computeOSTimeFromYear:1970 month:1 day:1 hour:0 minute:0 seconds:0 millis:0
    "

!

computeTimeAndDateFrom:osTime
    "given an OS-dependent time in osTime, return an Array
     containing (full-) year, month, day, hour, minute and seconds,
     offset to UTC, daylight savings time flag, milliseconds,
     dayInYear (1..) and dayInWeek (1..).
     Conversion is to localtime including any daylight saving adjustments."

    |low hi year month day hours minutes seconds millis utcOffset 
     dst yDay wDay osSeconds ret|

    millis := osTime \\ 1000.
    osSeconds := osTime // 1000.
%{
    struct tm *tmPtr;
    struct tm *gmTmPtr;
    INT t;
    TIME_T tt;

    t = __longIntVal(osSeconds);
    tt = (TIME_T)t;

    tmPtr = localtime(&tt);
    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);

    yDay = __MKSMALLINT(tmPtr->tm_yday+1);
    wDay = __MKSMALLINT(tmPtr->tm_wday == 0 ? 7 : tmPtr->tm_wday);

    if (tmPtr->tm_isdst == 0) {
	dst = false;
	utcOffset = __MKINT(TIMEZONE(tmPtr));
    } else {
	dst = true;
#ifdef HAS_ALTZONE
	utcOffset = __MKINT(altzone);
#else
	utcOffset = __MKINT(TIMEZONE(tmPtr) + 3600);
#endif
    }
%}.
    "I would love to have SELF-like inline objects ..."
    ret := Array new:11.
    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 at:9 put:millis.
    ret at:10 put:yDay.
    ret at:11 put:wDay.
    ^ ret

    "
     OperatingSystem computeTimeAndDateFrom:0
    "
!

computeTimePartsOf:osTime for:aBlock
    "compute hours, minutes, seconds and milliseconds from the osTime 
     and evaluate the argument, a 4-arg block with these.
     Conversion is to localtime including any daylight saving adjustments."

    |hours minutes seconds millis osSeconds|

    osSeconds := osTime // 1000.
    millis := osTime \\ 1000.
%{
    struct tm *tmPtr;
    INT t;
    TIME_T tt;

    t = __longIntVal(osSeconds);
    tt = (TIME_T)t;

    tmPtr = localtime(&tt);
    hours = __MKSMALLINT(tmPtr->tm_hour);
    minutes = __MKSMALLINT(tmPtr->tm_min);
    seconds = __MKSMALLINT(tmPtr->tm_sec);
%}.
    aBlock value:hours value:minutes value:seconds value:millis

    "
     OperatingSystem computeTimePartsOf:100 for:[:h :m :s :milli |
	h printCR. m printCR. s printCR. millis printCR
     ]
    "
!

computeUTCTimeAndDateFrom:osTime
    "given an OS-dependent time in osTime, return an Array
     containing year, month, day, hour, minute and seconds,
     offset to UTC, daylight savings time flag, milliseconds,
     dayInYear (1..) and dayInWeek (1..).
     Conversion is to UTC."

    |low hi year month day hours minutes seconds millis utcOffset 
     dst yDay wDay osSeconds ret|

    millis := osTime \\ 1000.
    osSeconds := osTime // 1000.
%{
    struct tm *tmPtr;
    struct tm *gmTmPtr;
    long t;

    t = __longIntVal(osSeconds);

    tmPtr = gmtime(&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);

    yDay = __MKSMALLINT(tmPtr->tm_yday + 1);
    wDay = __MKSMALLINT(tmPtr->tm_wday == 0 ? 7 : tmPtr->tm_wday);

    if (tmPtr->tm_isdst == 0) {
	dst = false;
	utcOffset = __MKINT(TIMEZONE(tmPtr));
    } else {
	dst = true;
#ifdef HAS_ALTZONE
	utcOffset = __MKINT(altzone);
#else
	utcOffset = __MKINT(TIMEZONE(tmPtr) + 3600);
#endif
    }
%}.
    "I would love to have SELF-like inline objects ..."
    ret := Array new:11.
    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 at:9 put:millis.
    ret at:10 put:yDay.
    ret at:11 put:wDay.
    ^ ret

    "
     OperatingSystem computeUTCTimeAndDateFrom:0
    "
!

computeUTCTimePartsOf:osTime for:aBlock
    "compute hours, minutes, seconds and milliseconds from the osTime 
     and evaluate the argument, a 4-arg block with these.
     Conversion is to UTC."

    |hours minutes seconds millis osSeconds|

    osSeconds := osTime // 1000.
    millis := osTime \\ 1000.
%{
    struct tm *tmPtr;
    long t;

    t = __longIntVal(osSeconds);

    tmPtr = gmtime(&t);
    hours = __MKSMALLINT(tmPtr->tm_hour);
    minutes = __MKSMALLINT(tmPtr->tm_min);
    seconds = __MKSMALLINT(tmPtr->tm_sec);
%}.
    aBlock value:hours value:minutes value:seconds value:millis

    "
     OperatingSystem computeUTCTimePartsOf:100 for:[:h :m :s :milli |
	h printCR. m printCR. s printCR. milli printCR
     ]
    "
!

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 = 0;

#if !defined(HAS_GETTIMEOFDAY)
# if defined(HAS_FTIME)
    struct timeb timebuffer;

    ftime(&timebuffer);
    t = (timebuffer.time * 1000) + timebuffer.millitm;
#   define HAVE_TIME
# endif

# ifndef HAVE_TIME
#  ifdef WIN32
    t = GetTickCount();
#   define HAVE_TIME
#  endif
# endif

# ifndef HAVE_TIME
#  ifdef MSDOS_LIKE
#   ifdef __BORLANDC__
    struct timeb timebuffer;
    ftime(&timebuffer);
#   else
    struct _timeb timebuffer;
    _ftime(&timebuffer);
#   endif
    t = (timebuffer.time * 1000) + timebuffer.millitm;
#   define HAVE_TIME
#  endif
# endif
#endif

#ifndef HAVE_TIME
    /* assume HAS_GETTIMEOFDAY 
     * - will result in a linkage error
     * if not fixed.
     */

    /*
     * bsd time
     */
    struct timeval tb;
    struct timezone tzb;

    gettimeofday(&tb, &tzb);
    t = tb.tv_sec*1000 + tb.tv_usec/1000;
#endif

#undef HAVE_TIME

    RETURN ( __MKSMALLINT(t & 0x1FFFFFFF) );
%}
!

getOSTime
    "This returns the OS time.
     The base of the returned value is not consistent across
     different OS's - some return the number of millis since jan, 1st 1970;
     others since 1900. The Time classes are prepared for this, and 
     converts as appropriate (by using my fromOSTime: conversion methods).

     Dont use this method in application code since it is an internal (private)
     interface. For compatibility with ST-80, use Time>>millisecondClockValue.
     or use instances of Time, Date or AbsoluteTime to work with.
    "

    |seconds millis|

%{

    long t;

#if !defined(HAS_GETTIMEOFDAY) 
# if defined(HAS_FTIME)
    struct timeb timebuffer;

    ftime(&timebuffer);
    seconds = __MKUINT(timebuffer.time);
    millis = __MKUINT(timebuffer.millitm);
#   define HAVE_TIME
# endif

# ifndef HAVE_TIME
#  ifdef MSDOS_LIKE
#   ifdef __BORLANDC__
    struct timeb timebuffer;
    ftime(&timebuffer);
#   else
    struct _timeb timebuffer;
    _ftime(&timebuffer);
#   endif
    seconds = __MKUINT(timebuffer.time);
    millis = __MKUINT(timebuffer.millitm);
#   define HAVE_TIME
#  endif
# endif
#endif

#ifndef HAVE_TIME
    /* assume HAS_GETTIMEOFDAY 
     * - will result in a linkage error
     * if not fixed.
     */
    /*
     * bsd time
     */
    struct timeval tb;
    struct timezone tzb;

    gettimeofday(&tb, &tzb);

    /* 
     * mhmh long-long stuff seems not to work correctly 
     * on all machines (sparc)
     * being conservative here ...
     */

# if defined(__GNUC__) && (__GNUC__ >= 2) && defined(i386) && defined(LINUX)
#   define HAS_LONGLONG
# endif

# ifdef HAS_LONGLONG
    {
	unsigned long long _secs, _millis, rslt;
	unsigned low, hi;

	_secs = tb.tv_sec;
	_millis = tb.tv_usec / 1000;
	rslt = _secs * 1000 + _millis;
	low = rslt & 0xFFFFFFFF;
	hi = rslt >> 32;
	RETURN (__MKLARGEINT64(1, low, hi));
    }
# endif /* long long */

# ifdef alpha64
    {
	unsigned INT _secs, _millis, rslt;

	_secs = (INT) tb.tv_sec;
	_millis = (INT) tb.tv_usec / 1000;
	rslt = _secs * 1000 + _millis;
	RETURN (__MKUINT(rslt));
    }
# endif /* alpha */

    seconds = __MKUINT(tb.tv_sec);
    millis = __MKUINT(tb.tv_usec / 1000);

#endif

#undef HAVE_TIME

%}.
    ^ (seconds * 1000) + millis

    "
     OperatingSystem getOSTime printCR.
     Delay waitForSeconds:0.2.
     OperatingSystem getOSTime printCR.
    "
!

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|

    now := Win32OperatingSystem getMillisecondTime.
    then := Win32OperatingSystem millisecondTimeAdd:now and:millis.

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

    "
     OperatingSystem millisecondDelay:2000
    "
!

sleep:numberOfSeconds
    "{ Pragma: +optSpace }"

    "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
    "
! !

!Win32OperatingSystem class methodsFor:'users & groups'!

getEffectiveGroupID
    "{ Pragma: +optSpace }"

    "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)."

    ^ self getGroupID

    "
     OperatingSystem getEffectiveGroupID
    "
!

getEffectiveUserID
    "{ Pragma: +optSpace }"

    "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)."

    ^ self getUserID

    "
     OperatingSystem getEffectiveUserID
    "
!

getFullUserNameFromID:userID
    "{ Pragma: +optSpace }"

    "return a string with the users full name - if available.
     If not, return the login name as a fallBack."

    |info gecos|

    info := self userInfoOf:userID.
    (info notNil
    and:[info includesKey:#gecos]) ifTrue:[
	gecos := info at:#gecos.
	(gecos includes:$,) ifTrue:[
	    ^ gecos copyTo:(gecos indexOf:$,) - 1
	].
	^ gecos
    ].
    ^ self getUserNameFromID:userID

    "
     OperatingSystem getFullUserNameFromID:0 
     OperatingSystem getFullUserNameFromID:(OperatingSystem getUserID)  

     OperatingSystem getUserNameFromID:(OperatingSystem getUserID)  
    "

    "Modified: 15.7.1996 / 12:44:21 / cg"
!

getGroupID
    "{ Pragma: +optSpace }"

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

    ^ 1 "just a dummy for systems which do not have groupIDs"

    "
     OperatingSystem getGroupID
    "
!

getGroupNameFromID:aNumber
    "{ Pragma: +optSpace }"

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

    ^ '???' "just a dummy for systems which do not have groupIDs"

    "
     OperatingSystem getGroupNameFromID:0
     OperatingSystem getGroupNameFromID:10
    "
!

getHomeDirectory
    "{ Pragma: +optSpace }"

    "return the name of the users home directory
     (i.e. yours)"

    |dir drv path|

    dir := self getEnvironment:'HOME'.
    dir isNil ifTrue:[
	"/ for NT users:
	"/ if HOME is nil, try HOMEDRIVE and HOMEPATH
	"/
	((drv := self getEnvironment:'HOMEDRIVE') notNil
	and:[(path := self getEnvironment:'HOMEPATH') notNil]) ifTrue:[
	    dir := drv , path
	]
    ].
    ^ dir

    "
     OperatingSystem getHomeDirectory
    "

    "Modified: 24.1.1997 / 11:32:13 / cg"
!

getLoginName
    "{ Pragma: +optSpace }"

    "return a string with the users login name (thats yours)"

%{  /* NOCONTEXT */
    static char cachedName[64];
    static firstCall = 1;
#ifndef __BORLANDC__
    extern char *getenv();
#endif
    char *name = (char *)0;

    if (firstCall) {
	int nameSize = sizeof(cachedName);

	if (GetUserName(cachedName, &nameSize) == TRUE) {
	    name = cachedName;
	    firstCall = 0;
	}
    } else {
	name = cachedName;
    }

    /*
     * try a few common environment variables ...
     */
    if (! name || (name[0] == 0) ) {
	name = getenv("LOGIN");
	if (! name || (name[0] == 0) ) {
	    name = getenv("LOGNAME");
	    if (! name || (name[0] == 0) ) {
		name = getenv("USER");
	    }
	}
    }
    /*
     * nope - I really font know who you are.
     */
    if (! name || (name[0] == 0) ) {
	name = "you";
    }

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

getUserID
    "{ Pragma: +optSpace }"

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

    ^ 1 "just a dummy for systems which do not have userIDs"

    "
     OperatingSystem getUserID
    "
!

getUserNameFromID:aNumber
    "{ Pragma: +optSpace }"

    "return the user-name-string for a given numeric user-id.
     This is the login name, not the fullName."

    aNumber == self getUserID ifTrue:[
	^ self getLoginName
    ].

    ^ '? (' , aNumber printString , ')'

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

userInfoOf:aNameOrID
    "{ Pragma: +optSpace }"

    "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|

    info := IdentityDictionary new.
    name isNil ifTrue:[
	aNameOrID == self getUserID ifTrue:[
	    name := self getLoginName
	].
    ].
    name notNil ifTrue:[
	info at:#name put:name.
    ] ifFalse:[
	info at:#name put:'unknown'
    ].
    dir isNil ifTrue:[
	aNameOrID == self getUserID ifTrue:[
	    dir := self getHomeDirectory
	]
    ].
    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)
    "
! !

!Win32OperatingSystem class methodsFor:'waiting for events'!

blockingChildProcessWait
     "return true, if childProcessWait: blocks, if no children are ready.
      On those systems, we must be somewhat careful when looking out for
      a subprocesses status (to avoid blocking)."

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

childProcessWait:blocking pid:pidToWait
    "{ Pragma: +optSpace }"

    "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.
     Note that win32 needs to know the HANDLE of the process on which 
     it waits.  In case of an error, THIS ALWAYS WAITS and then times out."

    |pid status code core|
%{
    DWORD endStatus;
    if (__isExternalAddress(pidToWait) ) {
#ifdef PROCESS1DEBUGWIN32
	printf("childProcessWait %x b %d\n",_HANDLEVal(pidToWait),blocking==true); 
#endif
	endStatus = WaitForSingleObject( _HANDLEVal(pidToWait), blocking==true ? INFINITE : 0 );
	if ( endStatus == WAIT_TIMEOUT ) {
	    if (blocking==true)
	      status = @symbol(timeout);
	    else
	    {
	      status = @symbol(continue);
#ifdef PROCESS1DEBUGWIN32
	      printf("return nil\n");
#endif
	      RETURN(nil);
	    }
	} 
	else 
	{
	    status = @symbol(exit);
#ifdef PROCESS1DEBUGWIN32
	    printf("exit\n");
#endif
	    if (endStatus == WAIT_OBJECT_0)     
	    {
		if (GetExitCodeProcess(_HANDLEVal(pidToWait),&endStatus))
		{
		     if (endStatus == STILL_ACTIVE)
		       RETURN(nil); 
#ifdef PROCESS1DEBUGWIN32
		     printf("exit %d\n",endStatus);
#endif
		     code = __MKSMALLINT(endStatus);
		}
		else
		  code = __MKSMALLINT(GetLastError());
	    }
	    else
	      code = __MKSMALLINT(-1);
	}
	core = false;
	pid = pidToWait;
    }
%}.

    (status isNil or:[pid isNil]) ifTrue:[
	^ self primitiveFailed
    ].

"/ Transcript show:'pid: '; show:pid; show:' status: '; show:status;
"/ show:' code: '; show:code; show:' core:'; showCR:core.

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

    "
     OperatingSystem childProcessWait:false
    "

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

numAvailableForReadOn:fd
    "return the number of bytes available for reading, without blocking."

    ^ (self readCheck:fd) ifTrue:[1] ifFalse:[0]
!

readCheck:fd
    "return true, if data is available on a filedescriptor
     (i.e. read is possible without blocking).
     This depends on a working select or FIONREAD to be provided by the OS."

    ^ super readCheck:fd
!

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."

%{
#define MAXHANDLE 128

      int i;
      INT t;
      OBJ fd, retFd;
      int ret;
      int count;
      int numHandles = 0;
      DWORD res;
      HANDLE hArray[MAXHANDLE];
      int    retArray[MAXHANDLE];
      if (__isSmallInteger(millis))
      {
	if (__isNonNilObject(readFdArray)) 
	{
	    if (! __isArray(readFdArray)) 
	    {
		goto fail;
	    }
	    count = __arraySize(readFdArray);

	    for (i=0; (i<count) && (numHandles < MAXHANDLE);i++) {
		fd = __ArrayInstPtr(readFdArray)->a_element[i];
		if (fd != nil) 
		{
		  if (__isExternalAddress(fd))
		  {
		    hArray[numHandles]  = _HANDLEVal(fd);
		  }
		  else
		  {
		    hArray[numHandles] = (HANDLE) _get_osfhandle (__intVal(fd));

		    if (ioctlsocket((SOCKET)hArray[numHandles],FIONREAD,&res)==0)
		    {
		      if (res > 0)
		      {
#ifdef SELECT2DEBUGWIN32
			printf("wait Handles1 l=%d return this is a Socket %x\n",res,__ArrayInstPtr(readFdArray)->a_element[i]);
#endif
			RETURN ( __ArrayInstPtr(readFdArray)->a_element[i] );
		      }
		      else
		      {
#ifdef SELECT2DEBUGWIN32
			printf("readWait Handle %d this is a Socket %x\n",i,__ArrayInstPtr(readFdArray)->a_element[i]);
#endif
			//if (!__isWinNT)
			  continue;
		      }
		    }
		    else
		    {
		      if (PeekNamedPipe((HANDLE)hArray[numHandles],0,0,0,&res,0))
		      {
			if (1/*res > 0*/)
			{
#ifdef SELECT1DEBUGWIN32
			  printf("wait Handles2 l=%d return this is a Pipe %x\n",res,__ArrayInstPtr(readFdArray)->a_element[i]);
#endif
			  RETURN ( __ArrayInstPtr(readFdArray)->a_element[i] );
			}
			else
			{
#ifdef SELECT1DEBUGWIN32
			  printf("readWait Handle %d this is a Pipe %x\n",i,__ArrayInstPtr(readFdArray)->a_element[i]);
#endif
			    continue;
			}
		      }
		      else
		      {
#ifdef SELECT2DEBUGWIN32
			printf("readWait Handle %d this is ???  %x\n",i,__ArrayInstPtr(readFdArray)->a_element[i]);
#endif
			errno = EBADF;                                                     \

			@global(LastErrorNumber) = __MKSMALLINT(errno);
			RETURN ( nil );
                        
			//continue;
		      }
		    }
		  }
		  retArray[numHandles] = i;
		  numHandles++;
		}
	    }
	}
	if (__isNonNilObject(writeFdArray)) 
	{
	    if (! __isArray(writeFdArray)) 
	    {
		goto fail;
	    }
	    count = __arraySize(writeFdArray);
	    for (i=0; (i<count) && (numHandles < MAXHANDLE);i++) {
		fd = __ArrayInstPtr(writeFdArray)->a_element[i];
		if (fd != nil) 
		{
		  if (__isExternalAddress(fd))
		  {
		    hArray[numHandles]  = _HANDLEVal(fd);
		  }
		  else
		  {
		    hArray[numHandles] = (HANDLE) _get_osfhandle (__intVal(fd));
		  }
#ifdef SELECTDEBUGWIN32
		  printf("select of writeFdArray %x\n",hArray[numHandles]);
#endif
		  retArray[numHandles] = i + 10000;
		  numHandles++;
		}
	    }
	}
	if (__isNonNilObject(exceptFdArray)) 
	{
	    if (! __isArray(exceptFdArray)) 
	    {
		goto fail;
	    }
	    count = __arraySize(exceptFdArray);
	    for (i=0; (i<count) && (numHandles < MAXHANDLE);i++) {
		fd = __ArrayInstPtr(exceptFdArray)->a_element[i];
		if (fd != nil) 
		{
		  if (__isExternalAddress(fd))
		  {
		    hArray[numHandles]  = _HANDLEVal(fd);
		  }
		  else
		  {
		    hArray[numHandles] = (HANDLE) _get_osfhandle (__intVal(fd));
		  }
#ifdef SELECTDEBUGWIN32
		  printf("select of exceptFdArray %x\n",hArray[numHandles]);
#endif
		  retArray[numHandles] = i + 20000;
		  numHandles++;
		}
	    }
	}
      }
      t = __intVal(millis);
      if (numHandles)
      {
#ifdef SELECT2DEBUGWIN32
	printf("wait Handles = %d withTimeout = %d\n",numHandles,t);
#endif
	//if (t > 200)
	//  t = 200;
#ifdef PROCESS2DEBUGWIN32
	printf(":");
#endif
	res = __vmWait(numHandles,hArray,MAXHANDLE, t);
#ifdef PROCESS2DEBUGWIN32
	printf(",");
#endif
	if (res == WAIT_FAILED)
	{
	  //printf("wait Handles error %d\n",GetLastError());
	  if (errno == EINTR) 
	  {
	      errno = 0;
	      @global(LastErrorNumber) = nil;
	  } 
	  else 
	  {
	      errno = EBADF;                                                     \
	      if (@global(InfoPrinting) == true) 
	      {
		  fprintf(stderr, "OS [info]: select errno = %d\n", errno);
	      }
	      @global(LastErrorNumber) = __MKSMALLINT(errno);
	  }
	  RETURN ( nil );
	}
	if (res == WAIT_TIMEOUT)
	{
	  //printf("wait Handles return nil\n");
	  goto polling;
	  @global(LastErrorNumber) = nil;
	  RETURN ( nil );
	}
	else
	{
	  if ((res >= 0) && (res < numHandles))
	    ret = res;
	  else
	  {
#ifdef SELECTDEBUGWIN32
	    printf("wait Handles res=%d error1 %d\n",res,GetLastError());
#endif                                                            
	    errno = 0;
	    @global(LastErrorNumber) = nil;
	    RETURN ( nil );
	  }
	  if (ret < numHandles)
	  {
	    @global(LastErrorNumber) = nil;
#ifdef SELECTDEBUGWIN32
	    if (ret)
	      printf("wait Handles %d %d return\n",ret,retArray[ret]);
#endif
	    if (retArray[ret] < 10000)
	    {
	      RETURN ( __ArrayInstPtr(readFdArray)->a_element[retArray[ret]] );
	    }
	    else if (retArray[ret] < 20000)
	    {
	      RETURN ( __ArrayInstPtr(writeFdArray)->a_element[retArray[ret]-10000] );
	    }
	    else
	    {
	      RETURN ( __ArrayInstPtr(exceptFdArray)->a_element[retArray[ret]-20000] );
	    }
	  }
	  printf("wait Handles ret = %d error2 %d\n",ret,GetLastError());
	  goto fail;
	}
      }
      else
      {
#ifdef PROCESS2DEBUGWIN32
	printf(".");
#endif
	res = __vmWait(0,hArray,MAXHANDLE, t);

polling:

#ifdef PROCESS2DEBUGWIN32
	  printf(",");
#endif
	if (__isNonNilObject(readFdArray)) 
	{
	    HANDLE h;
	    if (! __isArray(readFdArray)) 
	    {
		goto fail;
	    }
	    count = __arraySize(readFdArray);
	    for (i=0; (i<count);i++) {
		fd = __ArrayInstPtr(readFdArray)->a_element[i];
		if (fd != nil) 
		{
		  if (__isExternalAddress(fd))
		  {
		    continue;
		  }
		  else
		  {
		    h = (HANDLE) _get_osfhandle (__intVal(fd));

		    if (ioctlsocket((SOCKET)h,FIONREAD,&res)==0)
		    {
		      if (1/*res > 0*/)
			RETURN ( __ArrayInstPtr(readFdArray)->a_element[i] );
		    }
		    else
		    {
		      if (PeekNamedPipe((HANDLE)h,0,0,0,&res,0))
		      {
			if (1/*res > 0*/)
			{
			  RETURN ( __ArrayInstPtr(readFdArray)->a_element[i] );
			}
			else
			{
			    continue;
			}
		      }
		      else
		      {
			if (t)
			  RETURN ( __ArrayInstPtr(readFdArray)->a_element[i] );
		      }
		    }
		  }
		}
	    }
	}
	@global(LastErrorNumber) = nil;
	RETURN ( nil );
      }
  
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
    "{ Pragma: +optSpace }"

    "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."

    ^ self primitiveFailed
! !

!Win32OperatingSystem::FileStatusInfo class methodsFor:'instance creation'!

type:t mode:m uid:u gid:g size:s id:i accessed:aT modified:mT statusChanged:sT path:lP alternativeName:name2
    ^ self basicNew
	type:t mode:m uid:u gid:g size:s id:i accessed:aT modified:mT statusChanged:sT path:lP alternativeName:name2
! !

!Win32OperatingSystem::FileStatusInfo methodsFor:'accessing'!

accessed
    "return accessed"

    ^ accessed!

alternativeName
    "return the files other name (DOS name on windows).
     Nil if there is no other name"

    ^ alternativeName
!

fixedHeaderSize
    "return the fixedHeaderSize (VMS only; nil everywhere else)"

    ^ nil
!

gid
    "return gid"

    ^ gid!

id
    "return id"

    ^ id!

mode
    "return mode"

    ^ mode!

modified
    "return modified"

    ^ modified!

path
    "for symbolic links only: return the path where the symbolic link points to"

    ^ path

!

recordAttributes
    "return the recordAttributes (VMS only; nil everywhere else)"

    ^ nil
!

recordFormat
    "return the recordFormat (VMS only; nil everywhere else)"

    ^ nil
!

recordFormatNumeric
    "return the recordFormat as numeric (VMS only; nil everywhere else)"

    ^ nil
!

recordSize
    "return the recordSize (VMS only; nil everywhere else)"

    ^ nil
!

size
    "return size"

    ^ size
!

statusChanged
    "return statusChanged"

    ^ statusChanged
!

type
    "return type"

    ^ type
!

uid
    "return uid"

    ^ uid
! !

!Win32OperatingSystem::FileStatusInfo methodsFor:'backward compatibility'!

at:key
    "backward compatibility access: in previous releases, IdentityDictionaries
     were used to hold my information. Allow access via key messages.
     This method will vanish - use the proper access protocol."

    ^ self perform:key
! !

!Win32OperatingSystem::FileStatusInfo methodsFor:'private accessing'!

type:t mode:m uid:u gid:g size:s id:i accessed:aT modified:mT statusChanged:sT path:lP alternativeName:name2
    type := t.
    mode := m.
    uid := u.
    gid := g.
    size := s.
    id := i.
    accessed := aT.
    modified := mT.
    statusChanged := sT.
    path := lP.
    alternativeName := name2.
! !

!Win32OperatingSystem::OSProcessStatus class methodsFor:'documentation'!

documentation
"
    This is an auxillary class, that holds information about status changes of
    operating system processes (these are no smalltalk processes!!).

    [Instance variables:]

	pid     <Integer>       OS-Process identifier

	status  <Symbol>        either #exit #signal #stop #continue

	code    <Integer>       either exitcode or signalnumber

	core    <Boolean>       true if core has been dumped


    [author:]
	Stefan Vogel

    [see also:]
	OperatingSystem
"
! !

!Win32OperatingSystem::OSProcessStatus class methodsFor:'instance creation'!

pid:pid status:status code:code core:core
    "private interface for Win32OperatingSystem"

    ^ self new pid:pid status:status code:code core:core

    "Created: 28.12.1995 / 14:16:14 / stefan"
    "Modified: 30.4.1996 / 18:25:00 / cg"
!

processCreationFailure
    "private interface for Win32OperatingSystem"

    ^ self new pid:-1 status:#failure code:-1 core:false

    "Created: 28.12.1995 / 14:35:29 / stefan"
    "Modified: 30.4.1996 / 18:25:05 / cg"
! !

!Win32OperatingSystem::OSProcessStatus methodsFor:'accessing'!

code
    "return the exitcode / signalNumber"

    ^ code

    "Created: 28.12.1995 / 14:05:07 / stefan"
    "Modified: 30.4.1996 / 18:26:23 / cg"
!

core
    "return true if core has been dumped, false otherwise"

    ^ core == true

    "Modified: 28.12.1995 / 14:14:38 / stefan"
!

pid
    "return the pid"

    ^ pid

    "Created: 28.12.1995 / 14:05:07 / stefan"
    "Modified: 30.4.1996 / 18:26:30 / cg"
!

status
    "return status as a Symbol;
     one of #exit #signal #stop #continue"

    ^ status

    "Created: 28.12.1995 / 14:05:07 / stefan"
    "Modified: 30.4.1996 / 18:26:54 / cg"
! !

!Win32OperatingSystem::OSProcessStatus methodsFor:'initialization'!

pid:newPid status:newStatus code:newCode core:newCore
    pid := newPid.
    status := newStatus.
    code := newCode.
    core := newCore.

    "Created: 28.12.1995 / 14:18:22 / stefan"
! !

!Win32OperatingSystem::OSProcessStatus methodsFor:'private-OS interface'!

code:something
    "set the exitCode"

    code := something.

    "Created: 28.12.1995 / 14:05:07 / stefan"
    "Modified: 30.4.1996 / 18:25:18 / cg"
!

core:something
    "set core"

    core := something.

    "Created: 28.12.1995 / 14:05:07 / stefan"
!

pid:something
    "set pid"

    pid := something.

    "Created: 28.12.1995 / 14:05:07 / stefan"
!

status:something
    "set status"

    status := something.

    "Created: 28.12.1995 / 14:05:07 / stefan"
! !

!Win32OperatingSystem::OSProcessStatus methodsFor:'queries'!

couldNotExecute
    "return true when a command could not be executed"

    ^ status == #exit and:[code = 127].

    "Created: 28.12.1995 / 15:43:17 / stefan"
    "Modified: 30.4.1996 / 18:27:03 / cg"
!

stillAlive
    "true if process is still alive"

    ^ status == #stop or:[status == #continue]

    "Created: 28.12.1995 / 14:27:26 / stefan"
!

success
    "true if process terminated successfully"

    ^ status == #exit and:[code = 0]

    "Created: 28.12.1995 / 14:13:05 / stefan"
    "Modified: 28.12.1995 / 14:13:41 / stefan"
! !

!Win32OperatingSystem class methodsFor:'documentation'!

version
    ^ '$Header: /cvs/stx/stx/libbasic/Win32OperatingSystem.st,v 1.47 1999-05-05 12:54:27 cg Exp $'
! !
Win32OperatingSystem initialize!