"
COPYRIGHT (c) 1988 by Claus Gittinger
All Rights Reserved
This software is furnished under a license and may be used
only in accordance with the terms of that license and with the
inclusion of the above copyright notice. This software may not
be provided or otherwise made available to, or used by, any
other person. No title to or ownership of the software is
hereby transferred.
"
"{ Package: 'stx:libbasic' }"
AbstractOperatingSystem subclass:#UnixOperatingSystem
instanceVariableNames:''
classVariableNames:'HostName DomainName SlowFork ForkFailed CurrentDirectory'
poolDictionaries:''
category:'OS-Unix'
!
Object subclass:#FileDescriptorHandle
instanceVariableNames:'fd'
classVariableNames:'OpenFiles'
poolDictionaries:''
privateIn:UnixOperatingSystem
!
OSFileHandle subclass:#FilePointerHandle
instanceVariableNames:''
classVariableNames:''
poolDictionaries:''
privateIn:UnixOperatingSystem
!
Object subclass:#FileStatusInfo
instanceVariableNames:'type mode uid gid size id accessed modified statusChanged path'
classVariableNames:''
poolDictionaries:''
privateIn:UnixOperatingSystem
!
Object subclass:#OSProcessStatus
instanceVariableNames:'pid status code core'
classVariableNames:''
poolDictionaries:''
privateIn:UnixOperatingSystem
!
UnixOperatingSystem::FileDescriptorHandle subclass:#SocketHandle
instanceVariableNames:''
classVariableNames:'ProtocolCache'
poolDictionaries:''
privateIn:UnixOperatingSystem
!
!UnixOperatingSystem primitiveDefinitions!
%{
#if defined(_AIX)
# ifndef WANT_REALPATH
# define WANT_REALPATH
# endif
# ifndef WANT_SYSTEM
# define WANT_SYSTEM
# endif
#endif
#ifdef LINUX
# define __xxUSE_GNU /* new */
# undef HAS_UTS_DOMAINNAME
# ifndef _STDIO_H_INCLUDED_
# include <stdio.h>
# define _STDIO_H_INCLUDED_
# endif
# ifndef _SYS_TYPES_H_INCLUDED_
# include <sys/types.h>
# define _SYS_TYPES_H_INCLUDED_
# endif
/* use inline string macros */
# define __STRINGDEFS__
# include "linuxIntern.h"
# ifndef WANT_SYSTEM
# define WANT_SYSTEM
# endif
# define WANT_SHM
# define HAS_SETENV
# define HAS_UNSETENV
# include <time.h>
#endif
#ifdef IRIX5
# define WANT_SYSTEM
#endif
#ifdef ultrix
# define WANT_SYSTEM
#endif
#ifdef hpux
# define WANT_SYSTEM
#endif
#ifdef solaris
# define WANT_SYSTEM
#endif
#if defined(SYSV4) && defined(i386) /* e.g. unixware */
# define WANT_SYSTEM
#endif
/*
* notice: although many systems' include files
* already block against multiple inclusion, some
* do not. Therefore, this is done here again.
* (it does not hurt)
*/
#ifdef WANT_REALPATH
# ifndef NO_SYS_PARAM_H
# include <sys/param.h>
# define _SYS_PARAM_H_INCLUDED_
# endif
# include <errno.h>
# define _ERRNO_H_INCLUDED_
# include <sys/stat.h>
# define _SYS_STAT_H_INCLUDED_
#endif /* WANT_REALPATH */
#ifdef WANT_SHM
extern int shmctl(), shmget(), shmdt();
extern char * shmat();
# include <sys/types.h>
# define _SYS_TYPES_H_INCLUDED_
# include <sys/ipc.h>
# define _SYS_IPC_H_INCLUDED_
# include <sys/shm.h>
# define _SYS_SHM_H_INCLUDED_
#endif /* WANT_SHM */
#ifdef IRIX5
# include <sys/syssgi.h>
#endif
#ifdef transputer
# define unlink(f) ((remove(f) == 0) ? 0 : -1)
#else /* not transputer */
# ifndef _SIGNAL_H_INCLUDED_
# include <signal.h>
# define _SIGNAL_H_INCLUDED_
# endif
# ifdef SYSV
# ifndef _SYS_TYPES_H_INCLUDED_
# include <sys/types.h>
# define _SYS_TYPES_H_INCLUDED_
# endif
# ifndef _SYS_PARAM_H_INCLUDED_
# ifndef NO_SYS_PARAM_H
# include <sys/param.h>
# define _SYS_PARAM_H_INCLUDED_
# endif
# endif
# ifndef _SYS_TIMES_H_INCLUDED_
# include <sys/times.h>
# define _SYS_TIMES_H_INCLUDED_
# endif
# ifndef _SYS_FILE_H_INCLUDED_
# include <sys/file.h>
# define _SYS_FILE_H_INCLUDED_
# endif
# if ! defined(sco3_2)
# ifndef _UNISTD_H_INCLUDED_
# include <unistd.h>
# define _UNISTD_H_INCLUDED_
# endif
# endif
# if defined(isc3_2) || defined(sco3_2)
# ifndef _SYS_TIME_H_INCLUDED_
# include <sys/time.h>
# define _SYS_TIME_H_INCLUDED_
# endif
# endif
# if !defined(isc3_2)
# if defined(PCS) && defined(mips)
# include "/usr/include/bsd/sys/time.h"
# include "/usr/include/sys/time.h"
# else
# ifndef _TIME_H_INCLUDED_
# include <time.h>
# define _TIME_H_INCLUDED_
# endif
# endif
# endif
# if defined(isc3_2)
# include <sys/bsdtypes.h>
# endif
# else /* not SYSV */
# ifndef _SYS_TIME_H_INCLUDED_
# include <sys/time.h>
# define _SYS_TIME_H_INCLUDED_
# endif
# ifndef _SYS_TYPES_H_INCLUDED_
# include <sys/types.h>
# define _SYS_TYPES_H_INCLUDED_
# endif
# endif /* not SYSV */
# ifdef aix
# ifndef _TIME_H_INCLUDED_
# include <time.h>
# define _TIME_H_INCLUDED_
# endif
# ifndef _SYS_SELECT_H_INCLUDED_
# include <sys/select.h>
# define _SYS_SELECT_H_INCLUDED_
# endif
# endif /* aix */
# ifndef _STDIO_H_INCLUDED_
# include <stdio.h>
# define _STDIO_H_INCLUDED_
# endif
# ifndef _PWD_H_INCLUDED_
# include <pwd.h>
# define _PWD_H_INCLUDED_
# endif
# ifndef NO_GRP_H
# ifndef _GRP_H_INCLUDED_
# include <grp.h>
# define _GRP_H_INCLUDED_
# endif
# endif
# ifndef _SYS_STAT_H_INCLUDED_
# include <sys/stat.h>
# define _SYS_STAT_H_INCLUDED_
# endif
# ifndef _SYS_FILE_H_INCLUDED_
# include <sys/file.h>
# define _SYS_FILE_H_INCLUDED_
# endif
# ifndef _ERRNO_H_INCLUDED_
# include <errno.h>
# define _ERRNO_H_INCLUDED_
# endif
# ifndef _FCNTL_H_INCLUDED_
# include <fcntl.h>
# define _FCNTL_H_INCLUDED_
# endif
# ifndef _IOCTL_H_INCLUDED_
# include <sys/ioctl.h>
# define _IOCTL_H_INCLUDED_
# endif
# if defined(LINUX)
# define HAS_LOCALECONV
# endif
# if defined (HAS_LOCALECONV)
# ifndef _LOCALE_H_INCLUDED_
# include <locale.h>
# define _LOCALE_H_INCLUDED_
# endif
# endif
# if defined (HAS_FTIME)
# include <sys/timeb.h>
# endif
/*
* posix systems should define these ...
* but on some (older) systems, they are not.
*/
# ifndef S_IXUSR
# ifdef S_IEXEC
# define S_IXUSR S_IEXEC
# define S_IXGRP (S_IEXEC>>3)
# define S_IXOTH (S_IEXEC>>6)
# endif
# endif
# ifndef S_IXUSR
# define S_IXUSR 0100
# define S_IXGRP 0010
# define S_IXOTH 0001
# endif
# ifndef S_IRUSR
# define S_IRUSR 0400
# define S_IRGRP 0040
# define S_IROTH 0004
# endif
# ifndef S_IWUSR
# define S_IWUSR 0200
# define S_IWGRP 0020
# define S_IWOTH 0002
# endif
# ifndef MAXPATHLEN
# ifndef NO_SYS_PARAM_H
# include <sys/param.h>
# 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
# if defined(HAS_UNAME)
# include <sys/utsname.h>
# endif
# if defined(SYSV4)
# include <stropts.h>
# endif
# ifndef aix
# include <sys/termios.h>
# endif
/*
* NeXT has no pid_t
* and no sigemptyset
*/
# ifdef NEXT3
typedef int pid_t;
# define sigemptyset(set) ((*(set) = 0L), 0)
# define HAS_GETDOMAINNAME
# define NO_WAITPID
# endif
# ifdef sunos
# undef NO_WAITPID
# undef HAS_WAITPID
# define HAS_WAIT3
# define HAS_SIGACTION
# endif
/*
* some (BSD ?) have no timezone global,
* but provide the info in struct timezone.
*/
# if defined(ultrix) || defined(sunos) || defined(NEXT)
# define HAS_NO_TIMEZONE
# 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
# ifdef LINUX
# include <linux/kernel.h>
# include <linux/sys.h>
# endif
# if defined(HAS_GETSYSINFO)
# include <sys/sysinfo.h>
# include <machine/hal_sysinfo.h>
# include <machine/hal/cpuconf.h>
# endif
# if defined(HAS_SYSCONF)
# ifndef _UNISTD_H_INCLUDED_
# include <unistd.h>
# define _UNISTD_H_INCLUDED_
# endif
# endif
#endif /* not transputer */
/*
* on some systems errno is a macro ... check for it here
*/
#ifndef errno
extern errno;
#endif
/*
* some (old ?) systems do not define this ...
*/
#if !defined(R_OK) && !defined(_AIX)
# define R_OK 4 /* Test for Read permission */
# define W_OK 2 /* Test for Write permission */
# define X_OK 1 /* Test for eXecute permission */
# define F_OK 0 /* Test for existence of File */
#endif
#define SIGHANDLER_ARG
#ifdef sunos
# define NO_WAITPID
#endif
#ifdef NEXT
# define NO_WAITPID
#endif
/*
* not all systems have time_t and off_t
* explicit add of those we know to have ...
*/
#ifdef __osf__
# define TIME_T time_t
# define OFF_T off_t
#endif
#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
# if defined(HAS_ALTZONE)
# define TIMEZONE(tmPtr) ((tmPtr)->tm_isdst == 0 ? timezone : altzone)
# else /*!HAS_ALTZONE*/
# define TIMEZONE(tmPtr) ((tmPtr)->tm_isdst == 0 ? timezone : timezone-3600)
# endif /*!HAS_ALTZONE*/
#endif
#ifndef CONST
# ifdef __GNUC__
# define CONST const
# else
# define CONST /* nothing */
# endif
#endif
/*
* Socket defines
*/
#if defined(transputer)
# define NO_SOCKET
#endif
/*
* which protocols can we support ?
*/
#ifndef NO_SOCKET
# define WANT__AF_INET
#endif
#define WANT__AF_UNIX
#ifdef __VMS__
# undef WANT__AF_UNIX
# define WANT_AF_DECnet
#endif
#ifdef LINUX
/* kludge to avoid some redefines ... */
# define _ARPA_NAMESER_H
# define _NETINET_TCP_H
#endif
#if !defined(NO_SOCKET)
# if defined(IRIS) && !defined(IRIX5)
/* no socket.h on 4.0.5h ?!?!? */
# ifndef AF_UNIX
# define AF_UNIX 1
# endif
# ifndef AF_INET
# define AF_INET 2
# endif
# ifndef SOCK_STREAM
# define SOCK_STREAM 1
# endif
# ifndef SOCK_DGRAM
# define SOCK_DGRAM 2
# endif
# ifndef SOCK_RAW
# define SOCK_RAW 3
# endif
# else
# include <sys/socket.h>
# endif
# ifdef NEXT3
# include <netinet/in_systm.h>
# endif
#endif
/*
* see what we want ...
*/
#ifdef WANT__AF_UNIX
# ifdef AF_UNIX
# ifndef PF_UNIX
# define PF_UNIX AF_UNIX
# endif
# endif
#else
# undef AF_UNIX
#endif
#ifdef WANT__AF_INET
# ifdef AF_INET
# ifndef PF_INET
# define PF_INET AF_INET
# endif
# endif
#else
# undef AF_INET
#endif
#ifdef WANT__AF_INET6
# ifdef AF_INET6
# ifndef PF_INET6
# define PF_INET6 AF_INET6
# endif
# endif
#else
# undef AF_INET6
#endif
#ifdef WANT__AF_DECnet
# ifdef AF_DECnet
# ifndef PF_DECnet
# define PF_DECnet AF_DECnet
# endif
# endif
#else
# undef AF_DECnet
#endif
#ifdef WANT__AF_APPLETALK
# ifdef AF_APPLETALK
# ifndef PF_APPLETALK
# define PF_APPLETALK AF_APPLETALK
# endif
# endif
#else
# undef AF_APPLETALK
#endif
#ifdef WANT__AF_X25 /* X.25 */
# ifdef AF_X25
# ifndef PF_X25
# define PF_X25 AF_X25
# endif
# endif
#else
# undef AF_X25
#endif
#ifdef WANT__AF_NS /* Xerox XNS */
# ifdef AF_NS
# ifndef PF_NS
# define PF_NS AF_NS
# endif
# endif
#else
# undef AF_NS
#endif
#ifdef WANT__AF_SNA /* IBM SNA */
# ifdef AF_SNA
# ifndef PF_SNA
# define PF_SNA AF_SNA
# endif
# endif
#else
# undef AF_SNA
#endif
#ifdef WANT__AF_RAW /* RAW packets */
# ifdef AF_RAW
# ifndef PF_RAW
# define PF_RAW AF_RAW
# endif
# endif
#else
# undef AF_RAW
#endif
#ifdef WANT__AF_ISO /* ? */
# ifdef AF_ISO
# ifndef PF_ISO
# define PF_ISO AF_ISO
# endif
# endif
#else
# undef AF_ISO
#endif
#ifdef WANT__AF_NETBIOS /* NETBIOS */
# ifdef AF_NETBIOS
# ifndef PF_NETBIOS
# define PF_NETBIOS AF_NETBIOS
# endif
# endif
#else
# undef AF_NETBIOS
#endif
#ifdef WANT__AF_CCITT /* ? */
# if defined(AF_CCITT) && (AF_CCITT != AF_X25)
# ifndef PF_CCITT
# define PF_CCITT AF_CCITT
# endif
# endif
#else
# undef AF_CCITT
#endif
#ifdef WANT__AF_IPX /* Novell IPX */
# ifdef AF_IPX
# ifndef PF_IPX
# define PF_IPX AF_IPX
# endif
# endif
#else
# undef AF_IPX
#endif
#ifdef WANT__AF_AX25 /* Amateur Radio AX.25 */
# ifdef AF_AX25
# ifndef PF_AX25
# define PF_AX25 AF_AX25
# endif
# endif
#else
# undef AF_AX25
#endif
#ifdef WANT__AF_NETROM /* Amateur Radio NET/ROM */
# ifdef AF_NETROM
# ifndef PF_NETROM
# define PF_NETROM AF_NETROM
# endif
# endif
#else
# undef AF_NETROM
#endif
#ifdef WANT__AF_BRIDGE /* multiprotocol bridge */
# ifdef AF_BRIDGE
# ifndef PF_BRIDGE
# define PF_BRIDGE AF_BRIDGE
# endif
# endif
#else
# undef AF_BRIDGE
#endif
#ifdef WANT__AF_BSC /* BISYNC 2780/3780 */
# ifdef AF_BSC
# ifndef PF_BSC
# define PF_BSC AF_BSC
# endif
# endif
#else
# undef AF_BSC
#endif
#ifdef WANT__AF_ROSE /* Amateur Radio X.25 PLP */
# ifdef AF_ROSE
# ifndef PF_ROSE
# define PF_ROSE AF_ROSE
# endif
# endif
#else
# undef AF_ROSE
#endif
#ifdef WANT__AF_ATM /* ATM Services */
# ifdef AF_ATM
# ifndef PF_ATM
# define PF_ATM AF_ATM
# endif
# endif
#else
# undef AF_ATM
#endif
#ifdef WANT__AF_BAN /* BAN / VINES IP Services */
# ifdef AF_BAN
# ifndef PF_BAN
# define PF_BAN AF_BAN
# endif
# endif
#else
# undef AF_BAN
#endif
#ifdef WANT__AF_VOICEVIEW /* VoiceView Services W95 only */
# ifdef AF_VOICEVIEW
# ifndef PF_VOICEVIEW
# define PF_VOICEVIEW AF_VOICEVIEW
# endif
# endif
#else
# undef AF_VOICEVIEW
#endif
#ifdef WANT__AF_IRDA /* infrared */
# ifdef AF_IRDA
# ifndef PF_IRDA
# define PF_IRDA AF_IRDA
# endif
# endif
#else
# undef AF_IRDA
#endif
/*
* now, include what we have to ...
* undef support, if no include file is present
* (or I dont know yet, where to find it)
*/
#ifdef AF_UNIX
# ifdef UNIX
# include <sys/un.h>
# else
# undef AF_UNIX
# endif
#endif
#ifdef AF_INET
# include <netdb.h>
# if defined(PRE_SUSE_7_2)
# if defined(LINUX) && defined(AF_INET6)
# include <linux/in.h>
# else
# include <netinet/in.h>
# endif
# else
# include <netinet/in.h>
# endif
# if !defined(LINUX)
# if ! (defined(SYSV3) && defined(mc88k))
# include <netinet/tcp.h>
# endif
# endif
#endif
#ifdef AF_INET6
# if defined(LINUX) && defined(__GLIBC__)
# if defined(PRE_SUSE_7_2)
# include <linux/in6.h>
# endif
# else
# undef AF_INET6
# endif
#endif
#ifdef AF_APPLETALK
# ifdef LINUX
# include <asm/types.h>
# include <linux/atalk.h>
# else
# undef AF_APPLETALK
# endif
#endif
#ifdef AF_DECNET
# ifdef solaris2_0
# include <X11/dni8.h>
# else
# undef AF_DECNET
# endif
#endif
#ifdef AF_X25
# ifdef LINUX
# include <linux/x25.h>
# else
# undef AF_X25
# endif
#endif
#ifdef AF_AX25
# ifdef LINUX
# include <linux/ax25.h>
# else
# undef AF_AX25
# endif
#endif
#ifdef AF_IPX
# ifdef LINUX
# include <linux/ipx.h>
# else
# ifdef WIN32
# include <wsipx.h>
# else
# undef AF_IPX
# endif
# endif
#endif
#ifdef AF_NETBIOS
# ifdef WIN32
# include <wsnetbs.h>
# else
# undef AF_NETBIOS
# endif
#endif
#ifdef AF_ATM
# ifdef WIN32
# include <ws2atm.h>
# else
# undef AF_ATM
# endif
#endif
#ifdef AF_BAN
# ifdef WIN32
# include <wsvns.h>
# else
# undef AF_BAN
# endif
#endif
#ifdef AF_VOICEVIEW
# ifdef WIN32
# include <wsvv.h>
# else
# undef AF_VOICEVIEW
# endif
#endif
#ifdef AF_IRDA
# ifdef LINUX
# include <linux/irda.h>
# else
# undef AF_IRDA
# endif
#endif
#undef AF_SNA /* not yet implemented */
#undef AF_RAW /* not yet implemented */
#undef AF_NETROM /* not yet implemented */
#undef AF_BRIDGE /* not yet implemented */
#undef AF_BSC /* not yet implemented */
/*
* see what is leftOver
*/
union sockaddr_u {
#ifdef AF_UNIX
struct sockaddr_un un;
#endif
#ifdef AF_INET
struct sockaddr_in in;
#endif
#ifdef AF_INET6
struct sockaddr_in6 in6;
#endif
#ifdef AF_APPLETALK
struct sockaddr_at at;
#endif
#ifdef AF_DECNET
struct sockaddr_dn dn;
#endif
#ifdef AF_X25
struct sockaddr_x25 x25;
#endif
#ifdef AF_AX25
struct sockaddr_ax25 ax25;
#endif
#ifdef AF_IPX
struct sockaddr_ipx ipx;
#endif
#ifdef AF_NETBIOS
struct sockaddr_nb nb;
#endif
#ifdef AF_ATM
struct sockaddr_atm atm;
#endif
#ifdef AF_BAN
struct sockaddr_vns vns;
#endif
#ifdef AF_VOICEVIEW
struct sockaddr_vv vv;
#endif
#ifdef AF_IRDA
struct sockaddr_irda irda;
#endif
};
#if defined(TRY_AGAIN) || defined(HOST_NOT_FOUND)
# define USE_H_ERRNO
#endif
#ifdef USE_H_ERRNO
# ifndef h_errno
extern h_errno;
# endif
#endif
/*
* gethostbyname seems to have trouble
* sometimes, if interrupted while a request
* is on its way the name server.
* (although specified in the man-page,
* a check on TRY_AGAIN fails on iris)
*/
#ifdef LINUX
# define GETHOSTBYNAME(hp, name) \
hp = gethostbyname((char *) name);
# define GETHOSTBYADDR(hp, addr, alen, af) \
hp = gethostbyaddr(addr, alen, af);
#else
# ifdef IRIX5_3
# define GETHOSTBYNAME(hp, name) \
do { \
__BEGIN_INTERRUPTABLE__ \
hp = gethostbyname((char *) name); \
__END_INTERRUPTABLE__ \
} while ((hp == NULL) && \
((h_errno == TRY_AGAIN) || (errno == ECONNREFUSED)));
# define GETHOSTBYADDR(hp, addr, alen, af) \
do { \
__BEGIN_INTERRUPTABLE__ \
hp = gethostbyaddr(addr, alen, af); \
__END_INTERRUPTABLE__ \
} while ((hp == NULL) && \
((h_errno == TRY_AGAIN) || (errno == ECONNREFUSED)));
# else
# ifdef USE_H_ERRNO
# define GETHOSTBYNAME(hp, name) \
do { \
__BEGIN_INTERRUPTABLE__ \
hp = gethostbyname((char *) name); \
__END_INTERRUPTABLE__ \
} while ((hp == NULL) && (h_errno == TRY_AGAIN));
# define GETHOSTBYADDR(hp, addr, alen, af) \
do { \
__BEGIN_INTERRUPTABLE__ \
hp = gethostbyaddr(addr, alen, af); \
__END_INTERRUPTABLE__ \
} while ((hp == NULL) && (h_errno == TRY_AGAIN));
# else
# define GETHOSTBYNAME(hp, name) \
__BEGIN_INTERRUPTABLE__ \
hp = gethostbyname((char *) name); \
__END_INTERRUPTABLE__
# define GETHOSTBYADDR(hp, addr, alen, af) \
__BEGIN_INTERRUPTABLE__ \
hp = gethostbyaddr(addr, alen, af); \
__END_INTERRUPTABLE__
# endif
# endif
#endif
#ifdef DEBUG
# define DBGPRINTF(x) { if (__debugging__) printf x; }
# define DBGFPRINTF(x) { if (__debugging__) fprintf x; }
#else
# define DBGPRINTF(x) /* as nothing */
# define DBGFPRINTF(x) /* as nothing */
#endif
%}
! !
!UnixOperatingSystem primitiveFunctions!
%{
/*
* some systems' system() is broken in that it does not correctly
* handle EINTR and returns failure even though it actually succeeded.
* (LINUX is one of them)
* Here is a fixed version. If you encounter EINTR returns from
* UnixOperatingSystem>>executeCommand, you ought to define WANT_SYSTEM
* in the xxxIntern.h file to get this fixed version.
*
* As an added BONUS, this system() enables interrupts while waiting
* for the child which enables other threads to continue.
* (i.e. it is RT safe)
*/
#if defined(WANT_SYSTEM)
/* # define DPRINTF(x) printf x */
# define DPRINTF(x) /* nothing */
# ifndef _STDDEF_H_INCLUDED_
# include <stddef.h>
# define _STDDEF_H_INCLUDED_
# endif
# ifndef _STDLIB_H_INCLUDED_
# include <stdlib.h>
# define _STDLIB_H_INCLUDED_
# endif
# ifndef _UNISTD_H_INCLUDED_
# include <unistd.h>
# define _UNISTD_H_INCLUDED_
# endif
# ifndef _SYS_WAIT_H_INCLUDED
# include <sys/wait.h>
# define _SYS_WAIT_H_INCLUDED
# endif
# ifndef _SIGNAL_H_INCLUDED_
# include <signal.h>
# define _SIGNAL_H_INCLUDED_
# endif
# ifndef _SYS_TYPES_H_INCLUDED_
# include <sys/types.h>
# define _SYS_TYPES_H_INCLUDED_
# endif
# if (!defined(HAVE_GNU_LD) && !defined (__ELF__)) || !defined(LINUX)
# define __environ environ
# if 1 /* !defined(LINUX) */
# define __sigemptyset sigemptyset
# define __sigaction sigaction
# define __sigaddset sigaddset
# define __sigprocmask sigprocmask
# define __execve execve
# define __wait wait
# define __waitpid waitpid
# if defined(HAS_VFORK)
# define FORK vfork
# else
# define FORK fork
# endif
# endif /* ! LINUX */
extern char **environ;
# endif
# define __sigprocmask sigprocmask
# define __execve execve
# define SHELL_PATH "/bin/sh" /* Path of the shell. */
# define SHELL_NAME "sh" /* Name to give it. */
# ifndef FORK
# define FORK __fork
# endif
static int
mySystem(line)
register CONST char *line;
{
int status, save;
pid_t pid;
struct sigaction sa, intr, quit;
sigset_t block, omask;
if (line == NULL)
return -1;
sa.sa_handler = SIG_IGN;
sa.sa_flags = 0;
__sigemptyset (&sa.sa_mask);
if (__sigaction (SIGINT, &sa, &intr) < 0) {
DPRINTF(("1: errno=%d\n", errno));
return -1;
}
if (__sigaction (SIGQUIT, &sa, &quit) < 0) {
save = errno;
(void) __sigaction (SIGINT, &intr, (struct sigaction *) NULL);
errno = save;
DPRINTF(("2: errno=%d\n", errno));
return -1;
}
__sigemptyset (&block);
__sigaddset (&block, SIGCHLD);
save = errno;
if (__sigprocmask(SIG_BLOCK, &block, &omask) < 0) {
if (errno == ENOSYS)
errno = save;
else {
save = errno;
(void) __sigaction(SIGINT, &intr, (struct sigaction *) NULL);
(void) __sigaction (SIGQUIT, &quit, (struct sigaction *) NULL);
errno = save;
DPRINTF(("3: errno=%d\n", errno));
return -1;
}
}
pid = FORK ();
if (pid == (pid_t) 0) {
/* Child side. */
CONST char *new_argv[4];
new_argv[0] = SHELL_NAME;
new_argv[1] = "-c";
new_argv[2] = line;
new_argv[3] = NULL;
/* Restore the signals. */
(void) __sigaction (SIGINT, &intr, (struct sigaction *) NULL);
(void) __sigaction (SIGQUIT, &quit, (struct sigaction *) NULL);
(void) __sigprocmask (SIG_SETMASK, &omask, (sigset_t *) NULL);
/* Exec the shell. */
(void) __execve (SHELL_PATH, (char *CONST *) new_argv, __environ);
_exit (127);
} else {
if (pid < (pid_t) 0) {
/* The fork failed. */
DPRINTF(("4: errno=%d\n", errno));
status = -1;
} else {
/* Parent side. */
#ifdef NO_WAITPID
pid_t child;
do {
__BEGIN_INTERRUPTABLE__
child = __wait (&status);
__END_INTERRUPTABLE__
if (child < 0 && errno != EINTR) {
DPRINTF(("5: errno=%d\n", errno));
status = -1;
break;
}
} while (child != pid);
#else
pid_t child;
/* claus: the original did not care for EINTR here ... */
do {
__BEGIN_INTERRUPTABLE__
child = __waitpid (pid, &status, 0);
__END_INTERRUPTABLE__
} while ((child != pid) && (errno == EINTR));
if (child != pid) {
DPRINTF(("6: errno=%d\n", errno));
status = -1;
}
#endif /* NO_WAITPID */
}
}
save = errno;
if ((__sigaction (SIGINT, &intr, (struct sigaction *) NULL)
| __sigaction (SIGQUIT, &quit, (struct sigaction *) NULL)
| __sigprocmask (SIG_SETMASK, &omask, (sigset_t *) NULL)) != 0) {
if (errno == ENOSYS) {
errno = save;
} else {
status = -1;
DPRINTF(("7: errno=%d\n", errno));
}
}
return status;
}
#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 */
%}
! !
!UnixOperatingSystem class methodsFor:'documentation'!
copyright
"
COPYRIGHT (c) 1988 by Claus Gittinger
All Rights Reserved
This software is furnished under a license and may be used
only in accordance with the terms of that license and with the
inclusion of the above copyright notice. This software may not
be provided or otherwise made available to, or used by, any
other person. No title to or ownership of the software is
hereby transferred.
"
!
documentation
"
this class realizes access to most (all ?) required operating system services;
some of it is very specific for unix, so do not depend on
things available here in your applications
- some may not be found in other OS's or be slightly different ...
(On the other hand: I do not want to hide all features
from you - in some situations it MAY be interesting to be
able to get down to a select or fork system call easily (at least on Unix systems).
You decide - portability vs. functionality)
[Class variables:]
HostName <String> remembered hostname
DomainName <String> remembered domainname
SlowFork <Boolean> if set, fork and popen are avoided;
(more or less obsolete now)
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]
"
! !
!UnixOperatingSystem class methodsFor:'initialization'!
initialize
"initialize the class"
ObjectMemory addDependent:self.
HostName := nil.
DomainName := nil.
LastErrorNumber := nil.
PipeFailed := false.
ForkFailed := false.
SlowFork := false.
CurrentDirectory := nil.
"Modified: / 11.12.1998 / 16:35:39 / cg"
!
update:something with:aParameter from:changedObject
"catch image restart and flush some cached data"
something == #earlyRestart ifTrue:[
"
flush cached data/info
"
HostName := nil.
DomainName := nil.
LastErrorNumber := nil.
PipeFailed := false.
SlowFork := false.
ForkFailed := false.
CurrentDirectory := nil.
]
"Created: / 15.6.1996 / 15:22:37 / cg"
"Modified: / 7.1.1997 / 19:36:11 / stefan"
"Modified: / 11.12.1998 / 16:22:48 / cg"
! !
!UnixOperatingSystem 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)"
^ self signalNamed:#SIGABRT
"
OperatingSystem sigABRT
"
!
sigALRM
"return the signal number for SIGALRM - 0 if not supported
(the numeric value is not the same across unix-systems)"
^ self signalNamed:#SIGALRM
"
OperatingSystem sigALRM
"
!
sigBREAK
"return the signal number for SIGBREAK - 0 if not supported.
This is an MSDOS specific signal"
^ self signalNamed:#SIGBREAK
"
OperatingSystem sigBREAK
"
!
sigBUS
"return the signal number for SIGBUS - 0 if not supported
(the numeric value is not the same across unix-systems)"
^ self signalNamed:#SIGBUS
"
OperatingSystem sigBUS
"
!
sigCHLD
"return the signal number for SIGCHLD - 0 if not supported
(the numeric value is not the same across unix-systems)"
^ self signalNamed:#SIGCHLD
"
OperatingSystem sigCHLD
"
!
sigCONT
"return the signal number for SIGCONT - 0 if not supported
(the numeric value is not the same across unix-systems)"
^ self signalNamed:#SIGCONT
"
OperatingSystem sigCONT
"
!
sigDANGER
"return the signal number for SIGDANGER - 0 if not supported
(seems to be an AIX special)"
^ self signalNamed:#SIGDANGER
"
OperatingSystem sigDANGER
"
!
sigEMT
"return the signal number for SIGEMT - 0 if not supported by OS
(the numeric value is not the same across unix-systems)"
^ self signalNamed:#SIGEMT
"
OperatingSystem sigEMT
"
!
sigFP
"return the signal number for SIGFP - 0 if not supported by OS
(the numeric value is not the same across unix-systems)"
^ self signalNamed:#SIGFPE
"
OperatingSystem sigFP
"
!
sigGRANT
"return the signal number for SIGGRANT - 0 if not supported
(seems to be an AIX special)"
^ self signalNamed:#SIGGRANT
"
OperatingSystem sigGRANT
"
!
sigHUP
"return the signal number for SIGHUP
(the numeric value is not the same across unix-systems)"
^ self signalNamed:#SIGHUP
"
OperatingSystem sigHUP
"
!
sigILL
"return the signal number for SIGILL - 0 if not supported by OS
(the numeric value is not the same across unix-systems)"
^ self signalNamed:#SIGILL
"
OperatingSystem sigILL
"
!
sigINT
"return the signal number for SIGINT
(the numeric value is not the same across unix-systems)"
^ self signalNamed:#SIGINT
"
OperatingSystem sigINT
"
!
sigIO
"return the signal number for SIGIO - 0 if not supported
(the numeric value is not the same across unix-systems)"
^ self signalNamed:#SIGIO
"
OperatingSystem sigIO
"
!
sigIOT
"return the signal number for SIGIOT - 0 if not supported by OS
(the numeric value is not the same across unix-systems)"
^ self signalNamed:#SIGIOT
"
OperatingSystem sigIOT
"
!
sigKILL
"return the signal number for SIGKILL
(the numeric value is not the same across unix-systems)"
^ self signalNamed:#SIGKILL
"
OperatingSystem sigKILL
"
!
sigLOST
"return the signal number for SIGLOST - 0 if not supported
(the numeric value is not the same across unix-systems)"
^ self signalNamed:#SIGLOST
"
OperatingSystem sigLOST
"
!
sigMIGRATE
"return the signal number for SIGMIGRATE - 0 if not supported
(seems to be an AIX special)"
^ self signalNamed:#SIGMIGRATE
"
OperatingSystem sigMIGRATE
"
!
sigMSG
"return the signal number for SIGMSG - 0 if not supported
(seems to be an AIX special)"
^ self signalNamed:#SIGMSG
"
OperatingSystem sigMSG
"
!
sigPIPE
"return the signal number for SIGPIPE - 0 if not supported
(the numeric value is not the same across unix-systems)"
^ self signalNamed:#SIGPIPE
"
OperatingSystem sigPIPE
"
!
sigPOLL
"return the signal number for SIGPOLL - 0 if not supported
(the numeric value is not the same across unix-systems)"
^ self signalNamed:#SIGPOLL
"
OperatingSystem sigPOLL
"
!
sigPRE
"return the signal number for SIGPRE - 0 if not supported
(seems to be an AIX special)"
^ self signalNamed:#SIGPRE
"
OperatingSystem sigPRE
"
!
sigPROF
"return the signal number for SIGPROF - 0 if not supported
(the numeric value is not the same across unix-systems)"
^ self signalNamed:#SIGPROF
"
OperatingSystem sigPROF
"
!
sigPWR
"return the signal number for SIGPWR - 0 if not supported
(not available on all systems)"
^ self signalNamed:#SIGPWR
"
OperatingSystem sigPWR
"
!
sigQUIT
"return the signal number for SIGQUIT
(the numeric value is not the same across unix-systems)"
^ self signalNamed:#SIGQUIT
"
OperatingSystem sigQUIT
"
!
sigRETRACT
"return the signal number for SIGRETRACT - 0 if not supported
(seems to be an AIX special)"
^ self signalNamed:#SIGRETRACT
"
OperatingSystem sigRETRACT
"
!
sigSAK
"return the signal number for SIGSAK - 0 if not supported
(seems to be an AIX special)"
^ self signalNamed:#SIGSAK
"
OperatingSystem sigSAK
"
!
sigSEGV
"return the signal number for SIGSEGV - 0 if not supported
(the numeric value is not the same across unix-systems)"
^ self signalNamed:#SIGSEGV
"
OperatingSystem sigSEGV
"
!
sigSOUND
"return the signal number for SIGSOUND - 0 if not supported
(seems to be an AIX special)"
^ self signalNamed:#SIGSOUND
"
OperatingSystem sigSOUND
"
!
sigSTOP
"return the signal number for SIGSTOP - 0 if not supported
(the numeric value is not the same across unix-systems)"
^ self signalNamed:#SIGSTOP
"
OperatingSystem sigSTOP
"
!
sigSYS
"return the signal number for SIGSYS - 0 if not supported
(the numeric value is not the same across unix-systems)"
^ self signalNamed:#SIGSYS
"
OperatingSystem sigSYS
"
!
sigTERM
"return the signal number for SIGTERM - 0 if not supported
(the numeric value is not the same across unix-systems)"
^ self signalNamed:#SIGTERM
"
OperatingSystem sigTERM
"
!
sigTRAP
"return the signal number for SIGTRAP - 0 if not supported by OS
(the numeric value is not the same across unix-systems)"
^ self signalNamed:#SIGTRAP
"
OperatingSystem sigTRAP
"
!
sigTSTP
"return the signal number for SIGTSTP - 0 if not supported
(the numeric value is not the same across unix-systems)"
^ self signalNamed:#SIGTSTP
"
OperatingSystem sigTSTP
"
!
sigTTIN
"return the signal number for SIGTTIN - 0 if not supported
(the numeric value is not the same across unix-systems)"
^ self signalNamed:#SIGTTIN
"
OperatingSystem sigTTIN
"
!
sigTTOU
"return the signal number for SIGTTOU - 0 if not supported
(the numeric value is not the same across unix-systems)"
^ self signalNamed:#SIGTTOU
"
OperatingSystem sigTTOU
"
!
sigURG
"return the signal number for SIGURG - 0 if not supported
(the numeric value is not the same across unix-systems)"
^ self signalNamed:#SIGURG
"
OperatingSystem sigURG
"
!
sigUSR1
"return the signal number for SIGUSR1 - 0 if not supported
(the numeric value is not the same across unix-systems)"
^ self signalNamed:#SIGUSR1
"
OperatingSystem sigUSR1
"
!
sigUSR2
"return the signal number for SIGUSR2 - 0 if not supported
(the numeric value is not the same across unix-systems)"
^ self signalNamed:#SIGUSR2
"
OperatingSystem sigUSR2
"
!
sigVTALRM
"return the signal number for SIGVTALRM - 0 if not supported
(the numeric value is not the same across unix-systems)"
^ self signalNamed:#SIGVTALRM
"
OperatingSystem sigVTALRM
"
!
sigWINCH
"return the signal number for SIGWINCH - 0 if not supported
(the numeric value is not the same across unix-systems)"
^ self signalNamed:#SIGWINCH
"
OperatingSystem sigWINCH
"
!
sigXCPU
"return the signal number for SIGXCPU - 0 if not supported
(the numeric value is not the same across unix-systems)"
^ self signalNamed:#SIGXCPU
"
OperatingSystem sigXCPU
"
!
sigXFSZ
"return the signal number for SIGXFSZ - 0 if not supported
(the numeric value is not the same across unix-systems)"
^ self signalNamed:#SIGXFSZ
"
OperatingSystem sigXFSZ
"
!
signalNamed:signalName
"return the signal number for a named signal (must be a symbol)
Return 0 if that signal is not supported by the OS
(NOTICE: the numeric value is not the same across unix-systems,
therefore do not remember or hardcode those numbers in the application)"
%{ /* NOCONTEXT */
#ifdef SIGABRT
if (signalName == @symbol(SIGABRT)) {
RETURN ( __MKSMALLINT(SIGABRT) );
}
#endif
#ifdef SIGALRM
if (signalName == @symbol(SIGALRM)) {
RETURN ( __MKSMALLINT(SIGALRM) );
}
#endif
#ifdef SIGBREAK
if (signalName == @symbol(SIGBREAK)) {
RETURN ( __MKSMALLINT(SIGBREAK) );
}
#endif
#ifdef SIGBUS
if (signalName == @symbol(SIGBUS)) {
RETURN ( __MKSMALLINT(SIGBUS) );
}
#endif
#ifdef SIGCHLD
if ((signalName == @symbol(SIGCHLD))
|| (signalName == @symbol(SIGCLD)) ) {
RETURN ( __MKSMALLINT(SIGCHLD) );
}
#else
# if defined(SIGCLD)
if ((signalName == @symbol(SIGCHLD))
|| (signalName == @symbol(SIGCLD)) ) {
RETURN ( __MKSMALLINT(SIGCLD) );
}
# endif
#endif
#ifdef SIGCONT
if (signalName == @symbol(SIGCONT)) {
RETURN ( __MKSMALLINT(SIGCONT) );
}
#endif
#ifdef SIGDANGER
if (signalName == @symbol(SIGDANGER)) {
RETURN ( __MKSMALLINT(SIGDANGER) );
}
#endif
#ifdef SIGEMT
if (signalName == @symbol(SIGEMT)) {
RETURN ( __MKSMALLINT(SIGEMT) );
}
#endif
#ifdef SIGFPE
if (signalName == @symbol(SIGFPE)) {
RETURN ( __MKSMALLINT(SIGFPE) );
}
#endif
#ifdef SIGGRANT
if (signalName == @symbol(SIGGRANT)) {
RETURN ( __MKSMALLINT(SIGGRANT) );
}
#endif
#ifdef SIGHUP
if (signalName == @symbol(SIGHUP)) {
RETURN ( __MKSMALLINT(SIGHUP) );
}
#endif
#ifdef SIGILL
if (signalName == @symbol(SIGILL)) {
RETURN ( __MKSMALLINT(SIGILL) );
}
#endif
#ifdef SIGINT
if (signalName == @symbol(SIGINT)) {
RETURN ( __MKSMALLINT(SIGINT) );
}
#endif
#ifdef SIGIO
if (signalName == @symbol(SIGIO)) {
RETURN ( __MKSMALLINT(SIGIO) );
}
#endif
#ifdef SIGIOT
if (signalName == @symbol(SIGIOT)) {
RETURN ( __MKSMALLINT(SIGIOT) );
}
#endif
#ifdef SIGKILL
if (signalName == @symbol(SIGKILL)) {
RETURN ( __MKSMALLINT(SIGKILL) );
}
#endif
#ifdef SIGLOST
if (signalName == @symbol(SIGLOST)) {
RETURN ( __MKSMALLINT(SIGLOST) );
}
#endif
#ifdef SIGMIGRATE
if (signalName == @symbol(SIGMIGRATE)) {
RETURN ( __MKSMALLINT(SIGMIGRATE) );
}
#endif
#ifdef SIGMSG
if (signalName == @symbol(SIGMSG)) {
RETURN ( __MKSMALLINT(SIGMSG) );
}
#endif
#ifdef SIGPIPE
if (signalName == @symbol(SIGPIPE)) {
RETURN ( __MKSMALLINT(SIGPIPE) );
}
#endif
#ifdef SIGPOLL
if (signalName == @symbol(SIGPOLL)) {
RETURN ( __MKSMALLINT(SIGPOLL) );
}
#endif
#ifdef SIGPRE
if (signalName == @symbol(SIGPRE)) {
RETURN ( __MKSMALLINT(SIGPRE) );
}
#endif
#ifdef SIGPROF
if (signalName == @symbol(SIGPROF)) {
RETURN ( __MKSMALLINT(SIGPROF) );
}
#endif
#ifdef SIGPWR
if (signalName == @symbol(SIGPWR)) {
RETURN ( __MKSMALLINT(SIGPWR) );
}
#endif
#ifdef SIGQUIT
if (signalName == @symbol(SIGQUIT)) {
RETURN ( __MKSMALLINT(SIGQUIT) );
}
#endif
#ifdef SIGRETRACT
if (signalName == @symbol(SIGRETRACT)) {
RETURN ( __MKSMALLINT(SIGRETRACT) );
}
#endif
#ifdef SIGSAK
if (signalName == @symbol(SIGSAK)) {
RETURN ( __MKSMALLINT(SIGSAK) );
}
#endif
#ifdef SIGSEGV
if (signalName == @symbol(SIGSEGV)) {
RETURN ( __MKSMALLINT(SIGSEGV) );
}
#endif
#ifdef SIGSOUND
if (signalName == @symbol(SIGSOUND)) {
RETURN ( __MKSMALLINT(SIGSOUND) );
}
#endif
#ifdef SIGSTOP
if (signalName == @symbol(SIGSTOP)) {
RETURN ( __MKSMALLINT(SIGSTOP) );
}
#endif
#ifdef SIGSYS
if (signalName == @symbol(SIGSYS)) {
RETURN ( __MKSMALLINT(SIGSYS) );
}
#endif
#ifdef SIGTERM
if (signalName == @symbol(SIGTERM)) {
RETURN ( __MKSMALLINT(SIGTERM) );
}
#endif
#ifdef SIGTRAP
if (signalName == @symbol(SIGTRAP)) {
RETURN ( __MKSMALLINT(SIGTRAP) );
}
#endif
#ifdef SIGTSTP
if (signalName == @symbol(SIGTSTP)) {
RETURN ( __MKSMALLINT(SIGTSTP) );
}
#endif
#ifdef SIGTTIN
if (signalName == @symbol(SIGTTIN)) {
RETURN ( __MKSMALLINT(SIGTTIN) );
}
#endif
#ifdef SIGTTOU
if (signalName == @symbol(SIGTTOU)) {
RETURN ( __MKSMALLINT(SIGTTOU) );
}
#endif
#ifdef SIGURG
if (signalName == @symbol(SIGURG)) {
RETURN ( __MKSMALLINT(SIGURG) );
}
#endif
#ifdef SIGUSR1
if (signalName == @symbol(SIGUSR1)) {
RETURN ( __MKSMALLINT(SIGUSR1) );
}
#endif
#ifdef SIGUSR2
if (signalName == @symbol(SIGUSR2)) {
RETURN ( __MKSMALLINT(SIGUSR2) );
}
#endif
#ifdef SIGVTALRM
if (signalName == @symbol(SIGVTALRM)) {
RETURN ( __MKSMALLINT(SIGVTALRM) );
}
#endif
#ifdef SIGWINCH
if (signalName == @symbol(SIGWINCH)) {
RETURN ( __MKSMALLINT(SIGWINCH) );
}
#endif
#ifdef SIGXCPU
if (signalName == @symbol(SIGXCPU)) {
RETURN ( __MKSMALLINT(SIGXCPU) );
}
#endif
#ifdef SIGXFSZ
if (signalName == @symbol(SIGXFSZ)) {
RETURN ( __MKSMALLINT(SIGXFSZ) );
}
#endif
RETURN ( __MKSMALLINT(0) );
%}
"
OperatingSystem signalNamed:#SIGABRT
OperatingSystem signalNamed:#SIGCHLD
OperatingSystem signalNamed:#SIGXFSZ
OperatingSystem signalNamed:#SIGSOUND
"
! !
!UnixOperatingSystem 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
"
!
errorHolderForNumber:errNr
"return an osErrorHolder for the given error number (as returned by a system call)."
|sym typ holder|
%{
/* claus:
* I made this primitive code, since errnos are not
* standard across unixes
*/
typ = @symbol(defaultOsErrorSignal);
sym = @symbol(ERROR_OTHER);
if (__isSmallInteger(errNr)) {
switch ( __intVal(errNr)) {
/*
* POSIX errnos - these should be defined
*/
#ifdef EPERM
case EPERM:
sym = @symbol(EPERM);
typ = @symbol(noPermissionsSignal);
break;
#endif
#ifdef ENOENT
case ENOENT:
sym = @symbol(ENOENT);
typ = @symbol(nonexistentSignal);
break;
#endif
#ifdef ESRCH
case ESRCH:
sym = @symbol(ESRCH);
typ = @symbol(unavailableReferentSignal);
break;
#endif
#ifdef EINTR
case EINTR:
sym = @symbol(EINTR);
typ = @symbol(transientErrorSignal);
break;
#endif
#ifdef EIO
case EIO:
sym = @symbol(EIO);
typ = @symbol(transferFaultSignal);
break;
#endif
#ifdef ENXIO
case ENXIO:
sym = @symbol(ENXIO);
typ = @symbol(unavailableReferentSignal);
break;
#endif
#ifdef E2BIG
case E2BIG:
sym = @symbol(E2BIG);
typ = @symbol(invalidArgumentsSignal);
break;
#endif
#ifdef ENOEXEC
case ENOEXEC:
sym = @symbol(ENOEXEC);
typ = @symbol(inappropriateOperationSignal);
break;
#endif
#ifdef EBADF
case EBADF:
sym = @symbol(EBADF);
typ = @symbol(badAccessorSignal);
break;
#endif
#ifdef ECHILD
case ECHILD:
sym = @symbol(ECHILD);
typ = @symbol(informationSignal);
break;
#endif
#if !defined(EWOULDBLOCK) && defined(EAGAIN) && (EWOULDBLOCK != EAGAIN)
case EAGAIN:
sym = @symbol(EAGAIN);
typ = @symbol(notReadySignal);
break;
#endif
#ifdef ENOMEM
case ENOMEM:
sym = @symbol(ENOMEM);
typ = @symbol(noMemorySignal);
break;
#endif
#ifdef EACCES
case EACCES:
sym = @symbol(EACCES);
typ = @symbol(noPermissionsSignal);
break;
#endif
#ifdef EFAULT
case EFAULT:
sym = @symbol(EFAULT);
typ = @symbol(invalidArgumentsSignal);
break;
#endif
#ifdef EBUSY
case EBUSY:
sym = @symbol(EBUSY);
typ = @symbol(unavailableReferentSignal);
break;
#endif
#ifdef EEXIST
case EEXIST:
sym = @symbol(EEXIST);
typ = @symbol(existingReferentSignal);
break;
#endif
#ifdef EXDEV
case EXDEV:
sym = @symbol(EXDEV);
typ = @symbol(inappropriateReferentSignal);
break;
#endif
#ifdef ENODEV
case ENODEV:
sym = @symbol(ENODEV);
typ = @symbol(inaccessibleSignal);
break;
#endif
#ifdef ENOTDIR
case ENOTDIR:
sym = @symbol(ENOTDIR);
typ = @symbol(inappropriateOperationSignal);
break;
#endif
#ifdef EISDIR
case EISDIR:
sym = @symbol(EISDIR);
typ = @symbol(inappropriateOperationSignal);
break;
#endif
#ifdef EINVAL
case EINVAL:
sym = @symbol(EINVAL);
typ = @symbol(invalidArgumentsSignal);
break;
#endif
#ifdef ENFILE
case ENFILE:
sym = @symbol(ENFILE);
typ = @symbol(noResourcesSignal);
break;
#endif
#ifdef EMFILE
case EMFILE:
sym = @symbol(EMFILE);
typ = @symbol(noResourcesSignal);
break;
#endif
#ifdef ENOTTY
case ENOTTY:
sym = @symbol(ENOTTY);
typ = @symbol(inappropriateOperationSignal);
break;
#endif
#ifdef EFBIG
case EFBIG:
sym = @symbol(EFBIG);
typ = @symbol(noResourcesSignal);
break;
#endif
#ifdef ENOSPC
case ENOSPC:
sym = @symbol(ENOSPC);
typ = @symbol(noResourcesSignal);
break;
#endif
#ifdef ESPIPE
case ESPIPE:
sym = @symbol(ESPIPE);
typ = @symbol(inappropriateOperationSignal);
break;
#endif
#ifdef EROFS
case EROFS:
sym = @symbol(EROFS);
typ = @symbol(inappropriateOperationSignal);
break;
#endif
#ifdef EMLINK
case EMLINK:
sym = @symbol(EMLINK);
typ = @symbol(rangeErrorSignal);
break;
#endif
#ifdef EPIPE
case EPIPE:
sym = @symbol(EPIPE);
typ = @symbol(peerFaultSignal);
break;
#endif
#ifdef EDOM
case EDOM:
sym = @symbol(EDOM);
typ = @symbol(rangeErrorSignal);
break;
#endif
#ifdef ERANGE
case ERANGE:
sym = @symbol(ERANGE);
typ = @symbol(rangeErrorSignal);
break;
#endif
#ifdef EDEADLK
# if EDEADLK != EWOULDBLOCK
case EDEADLK:
sym = @symbol(EDEADLK);
typ = @symbol(noResourcesSignal);
break;
# endif
#endif
#ifdef ENAMETOOLONG
case ENAMETOOLONG:
sym = @symbol(ENAMETOOLONG);
typ = @symbol(rangeErrorSignal);
break;
#endif
#ifdef ENOLCK
case ENOLCK:
sym = @symbol(ENOLCK);
typ = @symbol(inappropriateOperationSignal);
break;
#endif
#ifdef ENOSYS
case ENOSYS:
sym = @symbol(ENOSYS);
typ = @symbol(inappropriateOperationSignal);
break;
#endif
#if defined(ENOTEMPTY) && (ENOTEMPTY != EEXIST)
case ENOTEMPTY:
sym = @symbol(ENOTEMPTY);
typ = @symbol(inappropriateReferentSignal);
break;
#endif
#ifdef EILSEQ
case EILSEQ:
sym = @symbol(EILSEQ);
typ = @symbol(transferFaultSignal);
break;
#endif
/*
* XPG3 errnos - defined on most systems
*/
#ifdef ENOTBLK
case ENOTBLK:
sym = @symbol(ENOTBLK);
typ = @symbol(inappropriateReferentSignal);
break;
#endif
#ifdef ETXTBSY
case ETXTBSY:
sym = @symbol(ETXTBSY);
typ = @symbol(inaccessibleSignal);
break;
#endif
/*
* some others
*/
#ifdef EWOULDBLOCK
case EWOULDBLOCK:
sym = @symbol(EWOULDBLOCK);
typ = @symbol(notReadySignal);
break;
#endif
#ifdef ENOMSG
case ENOMSG:
sym = @symbol(ENOMSG);
typ = @symbol(noDataSignal);
break;
#endif
#ifdef ELOOP
case ELOOP:
sym = @symbol(ELOOP);
typ = @symbol(rangeErrorSignal);
break;
#endif
/*
* some stream errors
*/
#ifdef ETIME
case ETIME:
sym = @symbol(ETIME);
typ = @symbol(peerFaultSignal);
break;
#endif
#ifdef ENOSR
case ENOSR:
sym = @symbol(ENOSR);
typ = @symbol(noResourcesSignal);
break;
#endif
#ifdef ENOSTR
case ENOSTR:
sym = @symbol(ENOSTR);
typ = @symbol(inappropriateReferentSignal);
break;
#endif
#ifdef ECOMM
case ECOMM:
sym = @symbol(ECOMM);
typ = @symbol(transferFaultSignal);
break;
#endif
#ifdef EPROTO
case EPROTO:
sym = @symbol(EPROTO);
typ = @symbol(inappropriateOperationSignal);
break;
#endif
/*
* nfs errors
*/
#ifdef ESTALE
case ESTALE:
sym = @symbol(ESTALE);
typ = @symbol(unavailableReferentSignal);
break;
#endif
#ifdef EREMOTE
case EREMOTE:
sym = @symbol(EREMOTE);
typ = @symbol(rangeErrorSignal);
break;
#endif
/*
* some networking errors
*/
#ifdef EINPROGRESS
case EINPROGRESS:
sym = @symbol(EINPROGRESS);
typ = @symbol(operationStartedSignal);
break;
#endif
#ifdef EALREADY
case EALREADY:
sym = @symbol(EALREADY);
typ = @symbol(operationStartedSignal);
break;
#endif
#ifdef ENOTSOCK
case ENOTSOCK:
sym = @symbol(ENOTSOCK);
typ = @symbol(inappropriateOperationSignal);
break;
#endif
#ifdef EDESTADDRREQ
case EDESTADDRREQ:
sym = @symbol(EDESTADDRREQ);
typ = @symbol(underspecifiedSignal);
break;
#endif
#ifdef EMSGSIZE
case EMSGSIZE:
sym = @symbol(EMSGSIZE);
typ = @symbol(rangeErrorSignal);
break;
#endif
#ifdef EPROTOTYPE
case EPROTOTYPE:
sym = @symbol(EPROTOTYPE);
typ = @symbol(wrongSubtypeForOperationSignal);
break;
#endif
#ifdef ENOPROTOOPT
case ENOPROTOOPT:
sym = @symbol(ENOPROTOOPT);
typ = @symbol(unsupportedOperationSignal);
break;
#endif
#ifdef EPROTONOSUPPORT
case EPROTONOSUPPORT:
sym = @symbol(EPROTONOSUPPORT);
typ = @symbol(unsupportedOperationSignal);
break;
#endif
#ifdef ESOCKTNOSUPPORT
case ESOCKTNOSUPPORT:
sym = @symbol(ESOCKTNOSUPPORT);
typ = @symbol(unsupportedOperationSignal);
break;
#endif
#ifdef EOPNOTSUPP
case EOPNOTSUPP:
sym = @symbol(EOPNOTSUPP);
typ = @symbol(inappropriateOperationSignal);
break;
#endif
#ifdef EPFNOSUPPORT
case EPFNOSUPPORT:
sym = @symbol(EPFNOSUPPORT);
typ = @symbol(unsupportedOperationSignal);
break;
#endif
#ifdef EAFNOSUPPORT
case EAFNOSUPPORT:
sym = @symbol(EAFNOSUPPORT);
typ = @symbol(unsupportedOperationSignal);
break;
#endif
#ifdef EADDRINUSE
case EADDRINUSE:
sym = @symbol(EADDRINUSE);
typ = @symbol(existingReferentSignal);
break;
#endif
#ifdef EADDRNOTAVAIL
case EADDRNOTAVAIL:
sym = @symbol(EADDRNOTAVAIL);
typ = @symbol(noPermissionsSignal);
break;
#endif
#ifdef ETIMEDOUT
case ETIMEDOUT:
sym = @symbol(ETIMEDOUT);
typ = @symbol(peerFaultSignal);
break;
#endif
#ifdef ECONNREFUSED
case ECONNREFUSED:
sym = @symbol(ECONNREFUSED);
typ = @symbol(peerFaultSignal);
break;
#endif
#ifdef ENETDOWN
case ENETDOWN:
sym = @symbol(ENETDOWN);
typ = @symbol(peerFaultSignal);
break;
#endif
#ifdef ENETUNREACH
case ENETUNREACH:
sym = @symbol(ENETUNREACH);
typ = @symbol(peerFaultSignal);
break;
#endif
#ifdef ENETRESET
case ENETRESET:
sym = @symbol(ENETRESET);
typ = @symbol(peerFaultSignal);
break;
#endif
#ifdef ECONNABORTED
case ECONNABORTED:
sym = @symbol(ECONNABORTED);
typ = @symbol(peerFaultSignal);
break;
#endif
#ifdef ECONNRESET
case ECONNRESET:
sym = @symbol(ECONNRESET);
typ = @symbol(peerFaultSignal);
break;
#endif
#ifdef EISCONN
case EISCONN:
sym = @symbol(EISCONN);
typ = @symbol(unpreparedOperationSignal);
break;
#endif
#ifdef ENOTCONN
case ENOTCONN:
sym = @symbol(ENOTCONN);
typ = @symbol(unpreparedOperationSignal);
break;
#endif
#ifdef ESHUTDOWN
case ESHUTDOWN:
sym = @symbol(ESHUTDOWN);
typ = @symbol(unpreparedOperationSignal);
break;
#endif
#ifdef EHOSTDOWN
case EHOSTDOWN:
sym = @symbol(EHOSTDOWN);
typ = @symbol(peerFaultSignal);
break;
#endif
#ifdef EHOSTUNREACH
case EHOSTUNREACH:
sym = @symbol(EHOSTUNREACH);
typ = @symbol(peerFaultSignal);
break;
#endif
default:
break;
}
}
%}.
holder := OSErrorHolder new.
holder errorSymbol:sym errorCategory:typ.
^ holder
"
OperatingSystem errorHolderForNumber:4
self errorHolderForNumber:(self errorNumberFor:#EPERM)
self errorHolderForNumber:(self errorNumberFor:#EIO)
self errorHolderForNumber:(self errorNumberFor:#ENXIO)
"
!
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
! !
!UnixOperatingSystem class methodsFor:'executing OS commands'!
commandAndArgsForOSCommand:aCommandString
"get a shell and shell arguments for command execution"
|shell args|
"/
"/ '/bin/sh -c <command>'
"/
shell := '/bin/sh'.
args := Array with:'sh' with:'-c' with:aCommandString.
^ Array with:shell with:args
"Modified: / 20.1.1998 / 16:57:19 / md"
"Modified: / 5.6.1998 / 17:40:48 / cg"
!
exec:aCommandPath withArguments:argColl environment:env fileDescriptors:fdColl closeDescriptors:closeFdColl fork:doFork newPgrp:newPgrp inDirectory:aDirectory
"Internal lowLevel entry for combined fork & exec;
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.
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.
env specifies environment variables which are passed differently from
the current environment. If non-nil, it must be a dictionary providing
key-value pairs for changed/added environment variables.
To pass a variable as empty (i.e. unset), pass a nil value.
Notice: this used to be two separate ST-methods; however, in order to use
vfork on some machines, it had to be merged into one, to avoid write
accesses to ST/X memory from the vforked-child.
The code below only does read accesses."
|envPairs argArray fdArray closeFdArray dirName|
env notNil ifTrue:[
envPairs := OrderedCollection new.
env keysAndValuesDo:[:key :val |
envPairs add:key; add:val
].
envPairs := envPairs asArray.
].
argColl notNil ifTrue:[
argArray := argColl asArray
].
fdColl notNil ifTrue:[
fdArray := fdColl asArray
].
closeFdColl notNil ifTrue:[
closeFdArray := closeFdColl asArray
].
aDirectory notNil ifTrue:[
dirName := aDirectory asFilename name
].
%{ /* STACK: 16000 */
char **argv;
int nargs, i, id;
OBJ arg;
extern char **environ;
char **_env, **_nEnv;
#ifndef __isArrayLike
# define __isArrayLike __isArray
#endif
if (__isString(aCommandPath) &&
((argArray == nil) || __isArrayLike(argArray)) &&
((fdArray == nil) || __isArrayLike(fdArray)) &&
((closeFdArray == nil) || __isArrayLike(closeFdArray))
) {
nargs = argArray == nil ? 0 : __arraySize(argArray);
argv = (char **) malloc(sizeof(char *) * (nargs + 1));
if (argv) {
int nOldEnv, nNewEnv;
for (i=0; i < nargs; i++) {
arg = __ArrayInstPtr(argArray)->a_element[i];
if (__isString(arg)) {
argv[i] = (char *) __stringVal(arg);
} else {
argv[i] = "";
}
}
argv[i] = NULL;
/*
* number of new items in environment ..
*/
nNewEnv = 0;
if ((envPairs != nil) && __isArrayLike(envPairs)) {
nNewEnv = __arraySize(envPairs) / 2;
}
if (nNewEnv == 0) {
_nEnv = environ;
} else {
_env = environ;
/*
* get size of environment
*/
nOldEnv = 0;
if (_env) {
while (*_env) {
nOldEnv++;
_env++;
}
}
/*
* generate a new environment
* I have not found a spec which defines if
* items at the end overwrite previous definitions,
* or if the first encountered definition is valid.
* To be prepared for any case, simply add the new definitions
* at both ends - that should do it in any case.
* Someone with more know-how may want to fix this.
*/
_nEnv = (char **)malloc(sizeof(char *) * (nNewEnv + nOldEnv + nNewEnv + 1));
if (_nEnv) {
char **eO, **eN;
eN = _nEnv;
if (nNewEnv) {
/*
* add new items at the front ...
*/
int i;
for (i=0; (i+1)<__arraySize(envPairs); i+=2) {
OBJ t;
char *var, *val, *e;
t = __ArrayInstPtr(envPairs)->a_element[i];
if (__isString(t) || __isSymbol(t)) {
var = (char *)__stringVal(t);
t = __ArrayInstPtr(envPairs)->a_element[i+1];
if (__isString(t) || __isSymbol(t)) {
val = (char *)__stringVal(t);
} else {
val = NULL;
}
if (val != NULL) {
e = (char *)malloc(strlen(var) + 1 + strlen(val) + 1);
strcpy(e, var);
strcat(e, "=");
strcat(e, val);
} else {
e = (char *)malloc(strlen(var) + 1 + 1);
strcpy(e, var);
strcat(e, "=");
}
*eN++ = e;
}
}
}
if (nOldEnv) {
/*
* append old environment
*/
eO = environ;
while (*eN = *eO++) {
eN++;
}
}
if (nNewEnv) {
/*
* new items again at the end
*/
for (eO = _nEnv, i=0; i<nNewEnv; i++) {
*eN++ = *eO++;
}
*eN = NULL;
}
}
}
if (doFork == true) {
/*
* fork a subprocess.
*/
int nfd, nclose;
nfd = fdArray == nil ? 0 : __arraySize(fdArray);
nclose = closeFdArray == nil ? 0 : __arraySize(closeFdArray);
#ifdef HAS_VFORK
id = vfork();
#else
id = fork();
#endif
if (id == 0) {
/*
** In child.
** first: dup filedescriptors
*/
for (i = 0; i < nfd; i++) {
OBJ fd;
int rslt;
fd = __ArrayInstPtr(fdArray)->a_element[i];
if (__isSmallInteger(fd) && (__intVal(fd) != i)) {
do {
rslt = dup2(__intVal(fd), i);
} while ((rslt < 0) && (errno == EINTR));
}
}
/*
** second: close unused filedescriptors
*/
for (i = 0; i < nfd; i++) {
if (__ArrayInstPtr(fdArray)->a_element[i] == nil) {
close(i);
}
}
/*
** third: close filedescriptors
*/
for (i = 0; i < nclose; i++) {
OBJ fd;
fd = __ArrayInstPtr(closeFdArray)->a_element[i];
if (__isSmallInteger(fd)) {
close(__intVal(fd));
}
}
if (newPgrp == true) {
#ifndef NEXT
setsid();
#endif
#if defined(TIOCSCTTY)
ioctl(0, TIOCSCTTY, 0) ;
#endif
#if defined(TIOCSPGRP)
{
int pgrp = getpid();
ioctl(0, TIOCSPGRP, (char *)&pgrp);
}
#endif
#if defined(_POSIX_JOB_CONTROL)
(void) setpgid(0, 0);
#else
# if defined(BSD) || defined(LINUX)
(void) setpgrp(0);
# endif
#endif
}
if (dirName != nil) {
chdir(__stringVal(dirName));
}
execve(__stringVal(aCommandPath), argv, _nEnv);
/* should not be reached */
_exit(127); /* POSIX 2 compatible exit value */
}
} else {
if (dirName != nil) {
chdir(__stringVal(dirName));
}
/*
* no subprocess (i.e. transfer to another program)
*/
execve(__stringVal(aCommandPath), argv, _nEnv);
/*
* should not be reached
* (well, it is, if you pass a wrong command-path)
*/
id = -1;
}
if (nNewEnv && (_nEnv != NULL)) {
/*
* free new environment stuff
*/
for (i=0; i<nNewEnv;i++) {
char *e = _nEnv[i];
if (e) {
free(e);
}
}
free(_nEnv);
}
free(argv);
/*
* In parent: succes or failure
*/
if (id == -1) {
RETURN (nil);
} else {
RETURN (__MKSMALLINT(id));
}
}
}
%}.
"
path-argument not string
or argArray not an array/nil
or malloc failed
or not supported by OS
"
^ self primitiveFailed
"
|id|
id := OperatingSystem fork.
id == 0 ifTrue:[
'I am the child'.
OperatingSystem exec:'/bin/ls' withArguments:#('ls' '/tmp').
'not reached'.
]
"
"
|id|
id := OperatingSystem fork.
id == 0 ifTrue:[
'I am the child'.
OperatingSystem
exec:'/bin/sh'
withArguments:#('sh' '-c' 'sleep 2;echo 1;sleep 2;echo 2').
'not reached'.
].
id printNL.
(Delay forSeconds:3.5) wait.
'killing ...' printNL.
OperatingSystem sendSignal:(OperatingSystem sigTERM) to:id.
OperatingSystem sendSignal:(OperatingSystem sigKILL) to:id
"
!
executeCommand:aCommandString inDirectory:aDirectory
"much like #executeCommand:, but changes the current directory
for the command. Since this is OS specific, use this instead of
hardwiring any 'cd ..' command strings into your applictions."
^ self
executeCommand:aCommandString
inputFrom:nil
outputTo:nil
errorTo:nil
inDirectory:aDirectory asFilename name
onError:[:status| false].
"/ | cmd |
"/
"/ "/ unix - prepend a 'cd' to the command
"/
"/ cmd := 'cd ' , aDirectory asFilename name, '; ' , aCommandString.
"/ ^ self executeCommand:cmd
"Modified: / 20.1.1998 / 17:03:03 / md"
"Modified: / 5.6.1998 / 19:01:29 / cg"
!
fork
"fork a new (HEAVY-weight) unix process.
Not supported with MSDOS & VMS systems.
Dont confuse this with Block>>fork, which creates
lightweight smalltalk processes. This method will return
0 to the child process, and a non-zero number (which is the childs
unix-process-id) to the parent (original) process.
In normal situations, you dont need to use this low level entry; see
#startProcess: and #executCommand: for higher level interfaces."
%{ /* NOCONTEXT */
int pid;
pid = fork();
RETURN ( __MKUINT(pid) );
%}.
"/
"/ not supported by OS
"/
^ UnsupportedOperationSignal raise
"
|id|
id := OperatingSystem fork.
id == 0 ifTrue:[
'I am the child process' printCR.
OperatingSystem exit
]
"
!
getStatusOfProcess:aProcessId
"wait for a process to terminate and fetch its exit status.
This is required to avoid zombie processes."
%{
int status;
if (__isSmallInteger(aProcessId)) {
pid_t pid = (pid_t)(__intVal(aProcessId));
{
# ifdef NO_WAITPID
pid_t child;
do {
__BEGIN_INTERRUPTABLE__
child = __wait (&status);
__END_INTERRUPTABLE__
if (child < 0 && errno != EINTR) {
fprintf(stderr, "OS: child-wait errno=%d\n", errno);
status = -1;
break;
}
} while (child != pid);
# else
pid_t child;
/* claus: the original did not care for EINTR here ... */
do {
__BEGIN_INTERRUPTABLE__
child = __waitpid (pid, &status, 0);
__END_INTERRUPTABLE__
} while ((child != pid) && (errno == EINTR));
if (child != pid) {
fprintf(stderr, "OS: child-waitpid errno=%d\n", errno);
status = -1;
}
# endif /* NO_WAITPID */
}
RETURN ( __MKSMALLINT(status));
}
%}.
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
"unix:
OperatingSystem pathOfCommand:'fooBar'
OperatingSystem pathOfCommand:'ls'
OperatingSystem pathOfCommand:'cvs'
OperatingSystem pathOfCommand:'stx'
"
"windows:
OperatingSystem pathOfCommand:'windbg'
"
"Modified: / 5.6.1998 / 19:03:32 / cg"
!
startProcess:aCommandString inputFrom:anExternalInStream outputTo:anExternalOutStream errorTo:anExternalErrStream inDirectory:dir
"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 rslt|
aCommandString isNil ifTrue:[^ nil].
(in := anExternalInStream) isNil ifTrue:[
in := '/dev/null' asFilename readStream
].
(out := anExternalOutStream) isNil ifTrue:[
out := '/dev/null' asFilename writeStream
].
(err := anExternalErrStream) isNil ifTrue:[
anExternalOutStream isNil ifTrue:[
err := out
] ifFalse:[
err := '/dev/null' asFilename writeStream
]
].
shellAndArgs := self commandAndArgsForOSCommand:aCommandString.
rslt := self
exec:(shellAndArgs at:1)
withArguments:(shellAndArgs at:2)
fileDescriptors:(Array with:in fileDescriptor
with:out fileDescriptor
with:err fileDescriptor)
closeDescriptors:nil
fork:true
newPgrp:true "/ false
inDirectory:dir.
^ rslt
"blocking at current prio (i.e. only higher prio threads execute):
OperatingSystem executeCommand:'ls -l > out'.
"
"non-blocking (lower prio threads continue):
|in out err pid sema|
in := 'out' asFilename readStream.
out := 'out2' asFilename writeStream.
err := 'err' asFilename writeStream.
sema := Semaphore new.
pid := OperatingSystem startProcess:'sleep 10; grep drw' inputFrom:in outputTo:out errorTo:err.
The following will no longer work. monitorPid has disappeared
pid notNil ifTrue:[
Processor monitorPid:pid action:[:OSstatus | sema signal ].
].
in close.
out close.
err close.
sema wait.
Transcript showCR:'finished'
"
"Modified: / 21.3.1997 / 10:04:35 / dq"
"Modified: / 15.7.1997 / 16:03:51 / stefan"
"Modified: / 5.6.1998 / 19:03:51 / cg"
"Created: / 12.11.1998 / 14:39:20 / cg"
! !
!UnixOperatingSystem class methodsFor:'file access'!
closeFd:anInteger
"low level close of a filedescriptor"
%{
if (__isSmallInteger(anInteger)) {
if (@global(ExternalStream:FileOpenTrace) == true) {
fprintf(stderr, "close [UnixOp] fd=%d\n", __intVal(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;
ret = mkdir(__stringVal(aPathName), 0755);
if (ret < 0) {
@global(LastErrorNumber) = __MKSMALLINT(errno);
RETURN (false);
}
RETURN (true);
}
%}.
"/ self isUNIXlike ifTrue:[
"/ ^ self executeCommand:('mkdir 2>/dev/null ', newPathName)
"/ ].
"/ ^ self executeCommand:('mkdir ', newPathName)
self primitiveFailed
"
OperatingSystem createDirectory:'foo'
"
"Modified: 20.12.1995 / 11:24:13 / stefan"
"Modified: 29.6.1996 / 14:06:54 / cg"
!
createFileForReadAppend:pathName
^ self open:pathName attributes:#(O_RDWR O_APPEND O_CREAT) mode:nil
!
createFileForReadWrite:pathName
^ self open:pathName attributes:#(O_RDWR O_CREAT O_TRUNC) mode:nil
!
createHardLinkFrom:oldPath to:newPath
"link the file 'oldPath' to 'newPath'. The link will be a hard link.
Return true if successful, false if not."
%{
int ret;
if (__isString(oldPath) && __isString(newPath)) {
__BEGIN_INTERRUPTABLE__
do {
ret = link((char *) __stringVal(oldPath), (char *) __stringVal(newPath));
} while (ret < 0 && errno == EINTR);
__END_INTERRUPTABLE__
if (ret < 0) {
@global(LastErrorNumber) = __MKSMALLINT(errno);
RETURN ( false );
}
RETURN (true);
}
%}.
"/
"/ bad argument(s) given
"/
^ self primitiveFailed
"
OperatingSystem linkFile:'foo' to:'bar'
"
!
createSymbolicLinkFrom:oldPath to:newPath
"make a symbolic link for 'newPath' to the file 'oldPath'.
The link will be a soft (symbolic) link.
Return true if successful, false if not."
%{
#ifdef S_IFLNK
int ret;
if (__isString(oldPath) && __isString(newPath)) {
__BEGIN_INTERRUPTABLE__
do {
ret = symlink((char *) __stringVal(oldPath), (char *) __stringVal(newPath));
} while (ret < 0 && errno == EINTR);
__END_INTERRUPTABLE__
if (ret < 0) {
@global(LastErrorNumber) = __MKSMALLINT(errno);
RETURN ( false );
}
RETURN (true);
}
#endif
%}.
(oldPath isString not or:[newPath isString not]) ifTrue:[
"/
"/ bad argument(s) given
"/
^ self primitiveFailed
].
"/
"/ this Unix does not seem to support symbolic links
"/
^ UnsupportedOperationSignal raise
"
OperatingSystem symbolicLinkFile:'foo' to:'bar'
"
!
open:path attributes:attributes mode:modeInteger
"open a file, return an os specific fileHandle.
openmode is a symbol defining the way to open
valid modes are:
#O_RDONLY
#O_RDWR
#O_WRONLY
#O_CREAT
#O_APPEND
#O_SYNC
#O_LARGEFILE
This is a private entry, but maybe useful to open/create a file in a special mode,
which is proprietrary to the operatingSystem."
|error fileDescriptor|
%{
OBJ *ap;
int nAttributes;
int fd;
int mode, openFlags = 0;
int n;
if (!__isString(path)) {
error = @symbol(badArgument1);
goto err;
}
if (!__isArray(attributes)) {
error = @symbol(badArgument2);
goto err;
}
if (modeInteger == nil) {
mode = 0644;
} else if (__isSmallInteger(modeInteger)) {
mode = __intVal(modeInteger);
} else {
error = @symbol(badArgument3);
goto err;
}
nAttributes = __arraySize(attributes);
for (n = 0, ap = __ArrayInstPtr(attributes)->a_element;
n < nAttributes;
n++) {
OBJ attribute = ap[n];
if (attribute == @symbol(O_RDONLY)) {
openFlags |= O_RDONLY;
} else if (attribute == @symbol(O_RDWR)) {
openFlags |= O_RDWR;
} else if (attribute == @symbol(O_WRONLY)) {
openFlags |= O_WRONLY;
} else if (attribute == @symbol(O_CREAT)) {
openFlags |= O_CREAT;
} else if (attribute == @symbol(O_APPEND)) {
openFlags |= O_APPEND;
} else if (attribute == @symbol(O_EXCL)) {
openFlags |= O_EXCL;
} else if (attribute == @symbol(O_TRUNC)) {
openFlags |= O_TRUNC;
} else if (attribute == @symbol(O_LARGEFILE)) {
#ifdef O_LARGEFILE
openFlags |= O_LARGEFILE;
#else
error = @symbol(badArgument2);
goto err;
#endif
} else if (attribute == @symbol(O_SYNC)) {
#ifdef O_SYNC
openFlags |= O_SYNC;
#else
error = @symbol(badArgument2);
goto err;
#endif
}
}
#if defined(O_NONBLOCK)
openFlags |= O_NONBLOCK;
#else
# if defined(O_NDELAY)
openFlags |= O_NDELAY;
# endif
#endif
again:
fd = open((char *) __stringVal(path), openFlags, mode);
if (fd < 0) {
if (errno == EINTR) {
__HANDLE_INTERRUPTS__;
goto again;
} else {
error = __mkSmallInteger(errno);
goto err;
}
}
fileDescriptor = __mkSmallInteger(fd);
err:;
%}.
^ fileDescriptor notNil ifTrue:[
FileDescriptorHandle for:fileDescriptor.
] ifFalse:[
(self errorHolderForNumber:error) reportError
].
"
self open:'/etc/hosts' attributes:#(O_RDONLY) mode:nil
self open:'/tmp/xxzz' attributes:#(O_RDWR O_CREAT) mode:8r611
self open:'/etc/passwd' attributes:#(O_RDWR) mode:nil
self open:'/no one knows this file' attributes:#(O_RDONLY) mode:nil
self open:'foo/bar/baz' attributes:#(O_RDWR O_CREAT) mode:nil
"
!
openFileForAppend:pathName
^ self open:pathName attributes:#(O_RDWR O_APPEND) mode:nil
!
openFileForRead:pathName
^ self open:pathName attributes:#(O_RDONLY) mode:nil
!
openFileForReadAppend:pathName
^ self open:pathName attributes:#(O_RDWR O_APPEND) mode:nil
!
openFileForReadWrite:pathName
^ self open:pathName attributes:#(O_RDWR) mode:nil
!
openFileForWrite:pathName
^ self open:pathName attributes:#(O_WRONLY) mode:nil
!
recursiveCopyDirectory:sourcePathName to:destination
"copy the directory named 'sourcePathName' and all contained files/directories to 'destination'.
Return true if successful."
^ self executeCommand:('cp -rf ' , sourcePathName, ' ', destination)
"Modified: / 5.6.1998 / 18:33:57 / cg"
!
recursiveRemoveDirectory:fullPathName
"remove the directory named 'fullPathName' and all contained files/directories.
Return true if successful."
^ self executeCommand:('rm -rf ' , fullPathName)
"
OperatingSystem recursiveCreateDirectory:'foo/bar/baz'
OperatingSystem recursiveRemoveDirectory:'foo'
"
"Modified: / 5.6.1998 / 18:34:03 / 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)) {
__BEGIN_INTERRUPTABLE__
do {
ret = rmdir((char *) __stringVal(fullPathName));
} while (ret < 0 && errno == EINTR);
__END_INTERRUPTABLE__
if (ret < 0) {
@global(LastErrorNumber) = __MKSMALLINT(errno);
RETURN ( false );
}
RETURN (true);
}
%}.
"/
"/ 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)) {
__BEGIN_INTERRUPTABLE__
do {
ret = unlink((char *) __stringVal(fullPathName));
} while (ret < 0 && errno == EINTR);
__END_INTERRUPTABLE__
if (ret < 0) {
@global(LastErrorNumber) = __MKSMALLINT(errno);
RETURN ( false );
}
RETURN (true);
}
%}.
^ self primitiveFailed
!
renameFile:oldPath to:newPath
"rename the file 'oldPath' to 'newPath'.
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)) {
#if defined(HAS_RENAME)
__BEGIN_INTERRUPTABLE__
do {
ret = rename((char *) __stringVal(oldPath), (char *) __stringVal(newPath));
} while (ret < 0 && errno == EINTR);
__END_INTERRUPTABLE__
#else
ret = link((char *) __stringVal(oldPath), (char *) __stringVal(newPath));
if (ret >= 0) {
ret = unlink((char *) __stringVal(oldPath));
if (ret < 0) {
eno = errno;
unlink((char *) __stringVal(newPath));
errno = eno;
}
}
#endif
if (ret < 0) {
@global(LastErrorNumber) = __MKSMALLINT(errno);
RETURN ( false );
}
RETURN (true);
}
%}.
^ self primitiveFailed
"
OperatingSystem renameFile:'foo' to:'bar'
"
!
truncateFile:aPathName to:newSize
"change a files size return true on success, false on failure.
This may not be supported on all architectures.
This is a low-level entry - use Filename protocol."
%{
#ifdef HAS_TRUNCATE
int ret;
if (__isString(aPathName)
&& __isSmallInteger(newSize)) {
__BEGIN_INTERRUPTABLE__
do {
ret = truncate((char *) __stringVal(aPathName), __intVal(newSize));
} while (ret < 0 && errno == EINTR);
__END_INTERRUPTABLE__
if (ret < 0) {
@global(LastErrorNumber) = __MKSMALLINT(errno);
RETURN ( false );
}
RETURN (true);
}
#else
# ifdef HAS_FTRUNCATE
int ret;
int fd;
if (__isString(aPathName)
&& __isSmallInteger(newSize)) {
do {
fd = open((char *) __stringVal(aPathName), 2);
} while (fd < 0 && errno == EINTR);
if (fd < 0) {
@global(LastErrorNumber) = __MKSMALLINT(errno);
RETURN ( false );
}
ret = ftruncate(fd, __intVal(newSize));
close(fd);
if (ret < 0) {
@global(LastErrorNumber) = __MKSMALLINT(errno);
RETURN ( false );
}
RETURN (true);
}
# endif /* using FTRUNCATE */
#endif
%}.
^ self primitiveFailed
! !
!UnixOperatingSystem class methodsFor:'file access rights'!
accessMaskFor:aSymbol
"return the access bits mask for numbers as returned by
OperatingSystem>>accessModeOf:
and expected by OperatingSystem>>changeAccessModeOf:to:.
Since these numbers are OS dependent, always use the mask
(never hardcode 8rxxx into your code)."
%{ /* NOCONTEXT */
# ifndef S_IRUSR
/* posix systems should define these ... */
# define S_IRUSR 0400
# define S_IWUSR 0200
# define S_IXUSR 0100
# define S_IRGRP 0040
# define S_IWGRP 0020
# define S_IXGRP 0010
# define S_IROTH 0004
# define S_IWOTH 0002
# define S_IXOTH 0001
# endif
if (aSymbol == @symbol(readUser)) {
RETURN ( __MKSMALLINT(S_IRUSR) );
}
if (aSymbol == @symbol(writeUser)) {
RETURN ( __MKSMALLINT(S_IWUSR) );
}
if (aSymbol == @symbol(executeUser)) {
RETURN ( __MKSMALLINT(S_IXUSR) );
}
if (aSymbol == @symbol(readGroup)) {
RETURN ( __MKSMALLINT(S_IRGRP) );
}
if (aSymbol == @symbol(writeGroup)) {
RETURN ( __MKSMALLINT(S_IWGRP) );
}
if (aSymbol == @symbol(executeGroup)) {
RETURN ( __MKSMALLINT(S_IXGRP) );
}
if (aSymbol == @symbol(readOthers)) {
RETURN ( __MKSMALLINT(S_IROTH) );
}
if (aSymbol == @symbol(writeOthers)) {
RETURN ( __MKSMALLINT(S_IWOTH) );
}
if (aSymbol == @symbol(executeOthers)) {
RETURN ( __MKSMALLINT(S_IXOTH) );
}
%}.
^ self primitiveFailed
"
OperatingSystem accessMaskFor:#readUser
"
!
accessModeOf:aPathName
"return a number representing access rights rwxrwxrwx for owner,
group and others. Return nil if such a file does not exist.
Notice that the returned number is OS dependent - use the
modeMasks as returned by OperatingSystem>>accessMaskFor:"
"
this could have been implemented as:
(self infoOf:aPathName) at:#mode
but for huge directory searches the code below is faster
"
%{
struct stat buf;
int ret;
if (__isString(aPathName)) {
# ifdef TRACE_STAT_CALLS
printf("stat on '%s' for accessMode\n", __stringVal(aPathName));
# endif
__BEGIN_INTERRUPTABLE__
do {
ret = stat((char *) __stringVal(aPathName), &buf);
} while ((ret < 0) && (errno == EINTR));
__END_INTERRUPTABLE__
if (ret < 0) {
@global(LastErrorNumber) = __MKSMALLINT(errno);
RETURN ( nil );
}
RETURN ( __MKSMALLINT(buf.st_mode & 0777) );
}
%}.
^ self primitiveFailed
"
(OperatingSystem accessModeOf:'/') printStringRadix:8
"
!
changeAccessModeOf:aPathName to:modeBits
"change the access rights of aPathName to the OS dependent modeBits.
You should construct this mask using accessMaskFor, to be OS
independent. Return true if changed,
false if such a file does not exist or change was not allowd."
%{
int ret;
if (__isString(aPathName) && __isSmallInteger(modeBits)) {
__BEGIN_INTERRUPTABLE__
do {
ret = chmod((char *)__stringVal(aPathName), __intVal(modeBits));
} while (ret < 0 && errno == EINTR);
__END_INTERRUPTABLE__
if (ret < 0) {
@global(LastErrorNumber) = __MKSMALLINT(errno);
RETURN ( false );
}
RETURN ( true );
}
%}.
^ self primitiveFailed
! !
!UnixOperatingSystem class methodsFor:'file locking'!
lockFD:aFileDescriptor shared:isSharedReadLock blocking:blockIfLocked
"set a lock on the file represented by aFileDescriptor.
(such as returned by ExternalStream>>fileDescriptor).
On some systems, only advisory locks are available -
these depends on other accessors to also perform the locking operation.
If they do not, they may still access the file
(on some systems, locks are mandatory, on others, they are advisory).
The isSharedReadLock argument (if true) specifies if multiple readers
are to be allowed - if false, they are not.
On some systems, all locks are non-exclusive locks.
Returns true, if the lock was aquired, false otherwise.
Notice, that not all OS's support these locks;
on some, this may simply be a no-op.
Also notice, that some systems block the process, to wait for the lock.
This can (again: on some systems) be avoided by passing a false blockIfLocked
argument."
%{
if (__isSmallInteger(aFileDescriptor)) {
int fd = __intVal(aFileDescriptor);
int lockArg;
/*
* claus: sigh - each one has a different interface ...
*/
#if defined(F_SETLK)
{
/*
* new fcntl(SETLK) interface;
* available on SYSV4 and Linux
*/
struct flock flock;
if (isSharedReadLock == true) {
flock.l_type = F_RDLCK;
} else {
flock.l_type = F_WRLCK;
}
flock.l_whence = 0;
flock.l_start = 0;
flock.l_len = 0;
lockArg = F_SETLK;
# if defined(F_SETLKW)
if (blockIfLocked == true) {
lockArg = F_SETLKW;
}
# endif
if (fcntl(fd, lockArg, &flock) != -1) {
RETURN (true);
}
}
#else /* no F_SETLK available */
# if defined(LOCK_EX) && defined(LOCK_UN)
/*
* BSD 4.3 advisory locks
*/
lockArg = LOCK_EX;
# if defined(LOCK_SH)
if (isSharedReadLock == true) {
lockArg = LOCK_SH
}
# endif
# if defined(LOCK_NB)
if (blockIfLocked == false) {
lockArg |= LOCK_NB;
}
# endif
if (flock(fd, lockArg) != -1) {
RETURN (true);
}
# else /* no flock available */
# if defined(F_LOCK) && defined(F_UNLOCK)
/*
* SYSV3 advisory locks
*/
if (lockf(fd, F_LOCK, 0) != -1) {
RETURN (true);
}
# endif
# endif
#endif
}
%}.
^ false
!
supportsFileLinks
"return true, if the OS supports file links (hard links).
Typically, only unix returns true here."
^ true
"Modified: / 5.6.1998 / 18:35:01 / cg"
!
supportsFileLocks
"return true, if the OS supports file locking"
%{ /* NOCONTEXT */
#if defined(F_SETLK)
RETURN (true);
#else
# if defined(LOCK_EX) && defined(LOCK_UN)
RETURN (true);
# else
# if defined(F_LOCK) && defined(F_UNLOCK)
RETURN (true);
# endif
# endif
#endif
%}.
^ false
"
OperatingSystem supportsFileLocks
"
!
supportsNonBlockingFileLocks
"return true, if the OS supports nonBlocking file locking
(i.e. with immediate return instead of waiting for the lock)"
%{ /* NOCONTEXT */
#if defined(F_SETLK) && defined(F_SETLKW)
RETURN (true);
#else
# if defined(LOCK_EX) && defined(LOCK_UN) && defined(LOCK_NB)
RETURN (true);
# endif
#endif
%}.
^ false
"
OperatingSystem supportsNonBlockingFileLocks
"
!
supportsSharedLocks
"return true, if the OS supports shared (i.e. multiple reader)
file locking."
%{ /* NOCONTEXT */
#if defined(F_SETLK) && defined(F_RDLCK) && defined(F_WRLCK)
RETURN (true);
#else
# if defined(LOCK_EX) && defined(LOCK_SH) && defined(LOCK_UN)
RETURN (true);
# endif
#endif
%}.
^ false
"
OperatingSystem supportsNonBlockingFileLocks
"
!
unlockFD:aFileDescriptor
"clear a file lock on the file represented by aFileDescriptor,
which was previously aquired by #lockFD:.
Return false, if the unlock failed
(which may happens when a wrong fd is passed,
no lock was set previously, or the systsem does not support locks).
Notice, that not all OS's support file locks;
on some, this may simply be a no-op."
%{
if (__isSmallInteger(aFileDescriptor)) {
int fd = __intVal(aFileDescriptor);
/*
* claus: sigh - each one has a different interface ...
*/
#if defined(F_SETLK)
{
/*
* new fcntl(SETLK) interface;
* available on SYSV4 and Linux
*/
struct flock flock;
flock.l_type = F_UNLCK;
flock.l_whence = 0;
flock.l_start = 0;
flock.l_len = 0;
if (fcntl(fd, F_SETLK, &flock) != -1) {
RETURN (true);
}
}
#else /* no F_SETLK available */
# if defined(LOCK_EX) && defined(LOCK_UN)
/*
* BSD 4.3 advisory locks
*/
if (flock(fd, LOCK_UN) != -1) {
RETURN (true);
}
# else /* no flock available */
# if defined(F_LOCK) && defined(F_UNLOCK)
/*
* SYSV3 advisory locks
*/
if (lockf(fd, F_UNLOCK, 0) != -1) {
RETURN (true);
}
# endif
# endif
#endif
}
%}.
^ false
! !
!UnixOperatingSystem 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."
^ true
"Modified: / 5.6.1998 / 18:35:18 / cg"
!
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)"
^ $/
"Modified: / 5.6.1998 / 18:35:24 / cg"
!
getDiskInfoOf:aDirectoryPath
"return some disk info.
The amountof information returned depends upon the OS, and is
not guaranteed to be consistent across architectures.
On unix, the information returned is (at least):
freeBytes
totalBytes
"
|p outputText keys values n info
fileSystemIdx sizeIdx usedIdx availIdx capacityIdx mountIdx|
p := PipeStream readingFrom:('df -k ' , aDirectoryPath).
p isNil ifTrue:[^ nil].
[
outputText := p contentsOfEntireFile.
] valueNowOrOnUnwindDo:[
p close.
].
"/ Transcript showCR:outputText asString.
outputText isNil ifTrue:[^ nil].
outputText := outputText asCollectionOfLines.
outputText size < 2 ifTrue:[^ nil].
keys := (outputText at:1) asLowercase asCollectionOfWords.
values := (outputText at:2) asCollectionOfWords.
"/ we may have to validate the following column indices
"/ on some rude Unix versions ...
fileSystemIdx := 1.
sizeIdx := 2.
usedIdx := 3.
availIdx := 4.
capacityIdx := 5.
mountIdx := 6.
values size < 6 ifTrue:[^ nil].
info := IdentityDictionary new.
n := Integer readFrom:(values at:capacityIdx) onError:nil.
n notNil ifTrue:[
info at:#percentUsed put:n
].
n := Integer readFrom:(values at:availIdx) onError:nil.
n notNil ifTrue:[
info at:#freeBytes put:(n * 1024)
].
n := Integer readFrom:(values at:usedIdx) onError:nil.
n notNil ifTrue:[
info at:#usedBytes put:(n * 1024)
].
n := Integer readFrom:(values at:sizeIdx) onError:nil.
n notNil ifTrue:[
info at:#totalBytes put:(n * 1024)
].
info at:#mountPoint put:(values at:mountIdx).
info at:#fileSystem put:(values at:fileSystemIdx).
^ info
"
OperatingSystem getDiskInfoOf:'/'
OperatingSystem getDiskInfoOf:'.'
"
"Modified: / 22.5.1999 / 00:32:13 / cg"
!
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."
"/
"/ default: retrurn array filled with
"/ root, home and current directories.
"/
^ Array
with:'/'
with:(self getHomeDirectory)
with:(Filename currentDirectory pathName)
"Modified: / 5.6.1998 / 18:35:35 / cg"
!
getMountedVolumes
"return info about mounted volumes.
The amount of information returned depends upon the OS, and is
not guaranteed to be consistent across architectures.
On unix, the information returned is (at least):
mountPoint - mount point
fileSystem - device or NFS-remotePath
"
|p outputText keys values info infoEntry|
p := PipeStream readingFrom:('df -k').
p isNil ifTrue:[^ nil].
[
outputText := p contentsOfEntireFile.
] valueNowOrOnUnwindDo:[
p close.
].
"/ Transcript showCR:outputText asString.
outputText isNil ifTrue:[^ nil].
outputText := outputText asCollectionOfLines.
outputText size < 2 ifTrue:[^ nil].
keys := (outputText at:1) asLowercase asCollectionOfWords.
"/ we may have to validate the following column indices
"/ on some rude Unix versions ...
info := OrderedCollection new.
outputText from:2 do:[:line |
values := line asCollectionOfWords.
values size >= 2 ifTrue:[
infoEntry := IdentityDictionary new.
infoEntry at:#mountPoint put:(values last).
infoEntry at:#fileSystem put:(values first).
info add:infoEntry.
]
].
^ info
"
OperatingSystem getMountedVolumes
"
"Modified: / 22.5.1999 / 00:32:13 / cg"
!
getNullDevice
"return the name of the OS's null device"
^ '/dev/null'
!
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
recordFormatNumeric - (VMS only:) numeric value of the recordFormat
recordFormat - (VMS only:) symbolic value of the recordFormat
recordAttributes - (VMS only:) recordAttributes
fixedHeaderSize - (VMS only:) fixed header size in a variable record format
recordSize - (VMS only:) record size.
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|
%{
struct stat buf;
int ret;
unsigned INT ino;
if (__isString(aPathName)) {
# ifdef TRACE_STAT_CALLS
printf("stat on '%s' for info\n", __stringVal(aPathName));
# endif
__BEGIN_INTERRUPTABLE__
do {
ret = stat((char *) __stringVal(aPathName), &buf);
} while ((ret < 0) && (errno == EINTR));
__END_INTERRUPTABLE__
if (ret < 0) {
@global(LastErrorNumber) = __MKSMALLINT(errno);
RETURN ( nil );
}
switch (buf.st_mode & S_IFMT) {
case S_IFDIR:
type = @symbol(directory);
break;
case S_IFREG:
type = @symbol(regular);
break;
# ifdef S_IFCHR
case S_IFCHR:
type = @symbol(characterSpecial);
break;
# endif
# ifdef S_IFBLK
case S_IFBLK:
type = @symbol(blockSpecial);
break;
# endif
# ifdef S_IFMPC
case S_IFMPC:
type = @symbol(multiplexedCharacterSpecial);
break;
# endif
# ifdef S_IFMPB
case S_IFMPB:
type = @symbol(multiplexedBlockSpecial);
break;
# endif
# ifdef S_IFLNK
case S_IFLNK:
type = @symbol(symbolicLink);
break;
# endif
# ifdef S_IFSOCK
case S_IFSOCK:
type = @symbol(socket);
break;
# endif
# ifdef S_IFIFO
case S_IFIFO:
type = @symbol(fifo);
break;
# endif
default:
type = @symbol(unknown);
break;
}
ino = buf.st_ino;
id = __MKUINT(ino);
mode = __MKSMALLINT(buf.st_mode & 0777);
uid = __MKSMALLINT(buf.st_uid);
gid = __MKSMALLINT(buf.st_gid);
size = __MKUINT(buf.st_size);
aOStime = __MKUINT(buf.st_atime);
mOStime = __MKUINT(buf.st_mtime);
cOStime = __MKUINT(buf.st_ctime);
}
%}.
mode notNil ifTrue:[
atime := AbsoluteTime fromOSTime:(aOStime * 1000).
mtime := AbsoluteTime fromOSTime:(mOStime * 1000).
ctime := AbsoluteTime fromOSTime:(cOStime * 1000).
info := FileStatusInfo
type:type
mode:mode
uid:uid
gid:gid
size:size
id:id
accessed:atime
modified:mtime
statusChanged:ctime
path:nil.
^ 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)) {
struct stat buf;
# ifdef TRACE_STAT_CALLS
printf("stat on '%s' for isDirectory\n", __stringVal(aPathName));
# endif
__BEGIN_INTERRUPTABLE__
do {
ret = stat((char *) __stringVal(aPathName), &buf);
} while ((ret < 0) && (errno == EINTR));
__END_INTERRUPTABLE__
if (ret < 0) {
@global(LastErrorNumber) = __MKSMALLINT(errno);
RETURN ( false );
}
RETURN ( ((buf.st_mode & S_IFMT) == S_IFDIR) ? true : false);
}
%}.
^ self primitiveFailed
"an alternative implementation would be:
^ (self infoOf:aPathName) 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 TRACE_ACCESS_CALLS
printf("access on '%s' for executable\n", __stringVal(aPathName));
# endif
__BEGIN_INTERRUPTABLE__
do {
ret = access(__stringVal(aPathName), X_OK);
} while ((ret < 0) && (errno == EINTR));
__END_INTERRUPTABLE__
if (ret < 0) {
@global(LastErrorNumber) = __MKSMALLINT(errno);
}
RETURN ( ((ret == 0) ? true : false) );
}
%}.
^ self primitiveFailed
!
isReadable:aPathName
"return true, if the file/dir 'aPathName' is readable.
For symbolic links, the pointed-to-file is checked."
%{
int ret;
if (__isString(aPathName)) {
# ifdef TRACE_ACCESS_CALLS
printf("access on '%s' for readable\n", __stringVal(aPathName));
# endif
__BEGIN_INTERRUPTABLE__
do {
ret = access(__stringVal(aPathName), R_OK);
} while ((ret < 0) && (errno == EINTR));
__END_INTERRUPTABLE__
if (ret < 0) {
@global(LastErrorNumber) = __MKSMALLINT(errno);
}
RETURN ( ((ret == 0) ? true : false) );
}
%}.
^ self primitiveFailed
!
isSymbolicLink:aPathName
"return true, if the given file is a symbolic link"
^ (self linkInfoOf:aPathName) notNil
"
OperatingSystem isSymbolicLink:'Make.proto'
OperatingSystem isSymbolicLink:'Makefile'
"
!
isValidPath:aPathName
"return true, if 'aPathName' is a valid path name
(i.e. the file or directory exists)"
%{
struct stat buf;
int ret;
if (__isString(aPathName) || __isSymbol(aPathName) ) {
# ifdef TRACE_STAT_CALLS
printf("stat on '%s' for isValidPath\n", __stringVal(aPathName));
# endif
__BEGIN_INTERRUPTABLE__
do {
ret = stat((char *) __stringVal(aPathName), &buf);
} while ((ret < 0) && (errno == EINTR));
__END_INTERRUPTABLE__
if (ret < 0) {
@global(LastErrorNumber) = __MKSMALLINT(errno);
RETURN (false);
}
RETURN ( ret ? false : true );
}
%}.
^ self primitiveFailed
"/ alternative:
"/ ^ (self infoOf:aPathName) notNil
!
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 TRACE_ACCESS_CALLS
printf("access on '%s' for writable\n", __stringVal(aPathName));
# endif
__BEGIN_INTERRUPTABLE__
do {
ret = access(__stringVal(aPathName), W_OK);
} while ((ret < 0) && (errno == EINTR));
__END_INTERRUPTABLE__
if (ret < 0) {
@global(LastErrorNumber) = __MKSMALLINT(errno);
}
RETURN ( ((ret == 0) ? true : false) );
}
%}.
^ self primitiveFailed
!
linkInfoOf:aPathName
"return a dictionary filled with info for the file 'aPathName',
IFF aPathName is a symbolic link.
If aPathName is invalid, or its NOT a symbolic link, nil is returned.
(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."
|info type mode uid gid size id atime mtime ctime path|
%{ /* STACK: 1200 */
#if defined(S_IFLNK)
struct stat buf;
int ret;
char pathBuffer[1024];
unsigned INT ino;
if (__isString(aPathName)) {
__BEGIN_INTERRUPTABLE__
do {
ret = lstat((char *) __stringVal(aPathName), &buf);
} while ((ret < 0) && (errno == EINTR));
__END_INTERRUPTABLE__
if (ret < 0) {
@global(LastErrorNumber) = __MKSMALLINT(errno);
RETURN ( nil );
}
switch (buf.st_mode & S_IFMT) {
default:
RETURN ( nil ); /* not a symbolic link */
case S_IFLNK:
type = @symbol(symbolicLink);
break;
}
ino = buf.st_ino;
id = __MKUINT(ino);
mode = __MKSMALLINT(buf.st_mode & 0777);
uid = __MKSMALLINT(buf.st_uid);
gid = __MKSMALLINT(buf.st_gid);
size = __MKUINT(buf.st_size);
atime = __MKUINT(buf.st_atime);
mtime = __MKUINT(buf.st_mtime);
ctime = __MKUINT(buf.st_ctime);
if ((ret = readlink((char *) __stringVal(aPathName), pathBuffer, sizeof(pathBuffer))) < 0) {
@global(LastErrorNumber) = __MKSMALLINT(errno);
RETURN ( nil );
}
pathBuffer[ret] = '\0'; /* readlink does not 0-terminate */
path = __MKSTRING(pathBuffer);
}
#else
RETURN ( nil );
#endif
%}.
mode notNil ifTrue:[
info := IdentityDictionary new.
^ FileStatusInfo
type:type
mode:mode
uid:uid
gid:gid
size:size
id:id
accessed:(AbsoluteTime fromOSTime:(atime * 1000))
modified:(AbsoluteTime fromOSTime:(mtime * 1000))
statusChanged:(AbsoluteTime fromOSTime:(ctime * 1000))
path:path
].
^ self primitiveFailed
"
OperatingSystem infoOf:'Make.proto'
OperatingSystem linkInfoOf:'Make.proto'
OperatingSystem infoOf:'resources/motif.style'
OperatingSystem linkInfoOf:'resources/motif.style'
"
!
parentDirectoryName
"return the name used to refer to parent directories.
In MSDOS, Unix and other systems this is '..', but maybe different
for other systems.
(but those are currently not supported - so this is some
preparation for the future)"
^ '..'
!
pathNameOf:pathName
"return the pathName of the argument, aPathString,
- thats the full pathname of the directory, starting at '/'.
This method needs the path to be valid
(i.e. all directories must exist, be readable and executable).
Notice: if symbolic links are involved, the result may look different
from what you expect."
|p path command|
"some systems have a convenient function for this ..."
path := self primPathNameOf:pathName.
path isNil ifTrue:[
(self isValidPath:pathName) ifFalse:[
p := pathName.
[(p size > 1)
and:[p endsWith:(self fileSeparator)]
] whileTrue:[
p := p copyWithoutLast:1.
].
^ p
].
(SlowFork==true or:[PipeFailed==true]) ifFalse:[
PipeStream openErrorSignal handle:[:ex |
PipeFailed := true.
'UnixOperatingSystem [warning]: cannot fork/popen' errorPrintCR.
ex return.
] do:[
"have to fall back ..."
command := 'cd "' , pathName , '"; pwd'.
p := PipeStream readingFrom:command.
].
(p isNil or:[p atEnd]) ifTrue:[
('UnixOperatingSystem [warning]: PipeStream for <' , command , '> failed') errorPrintCR.
] ifFalse:[
path := p nextLine.
p close.
]
].
path isNil ifTrue:[
"/
"/ return the original - there is nothing else can we do
"/
path := pathName
].
(SlowFork==true or:[ForkFailed==true]) ifTrue:[
path := self compressPath:path
]
].
^ 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: / 5.6.1998 / 18:37:15 / cg"
!
primIdOf:aPathName
"the actual code to return the fileNumber (i.e. inode number) of a file."
%{
struct stat buf;
int ret;
unsigned INT ino;
OBJ retVal;
if (__isString(aPathName)) {
# ifdef TRACE_STAT_CALLS
printf("stat on '%s' for id\n", __stringVal(aPathName));
# endif
__BEGIN_INTERRUPTABLE__
do {
ret = stat((char *) __stringVal(aPathName), &buf);
} while (ret < 0 && errno == EINTR);
__END_INTERRUPTABLE__
if (ret >= 0) {
ino = buf.st_ino;
retVal = __MKUINT(ino);
RETURN (retVal);
}
@global(LastErrorNumber) = __MKSMALLINT(errno);
RETURN (nil);
}
RETURN (nil);
%}.
!
primInfoOf:aPathName
"for rel5 only"
^ self primitiveFailed
!
primPathNameOf:pathName
"return the pathName of the argument, aPathString,
- thats the full pathname of the directory, starting at '/'.
This method here returns nil, if the OS does not provide a
realPath library function.
Notice: if symbolic links are involved, the result may look different
from what you expect."
|path|
%{ /* STACK: 16000 */
if (__isString(pathName)) {
#ifdef HAS_GETCWD
if (strcmp(__stringVal(pathName), ".") == 0) {
char nameBuffer[MAXPATHLEN + 1];
if (@global(CurrentDirectory) == nil) {
if (getcwd(nameBuffer, MAXPATHLEN)) {
OBJ d;
@global(CurrentDirectory) = d = __MKSTRING(nameBuffer);
__GSTORE(d);
}
}
RETURN (@global(CurrentDirectory));
}
#endif /* HAS_GETCWD */
#ifdef HAS_REALPATH
{
char nameBuffer[MAXPATHLEN + 1 + MAXPATHLEN + 1];
if (realpath(__stringVal(pathName), nameBuffer)) {
RETURN ( __MKSTRING(nameBuffer) );
}
RETURN ( nil );
}
#endif /* ! HAS_REALPATH */
}
%}.
^ 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
"
|osSeconds i|
%{
struct stat buf;
time_t mtime;
int ret;
if (__isString(aPathName)) {
# ifdef TRACE_STAT_CALLS
printf("stat on '%s' for timeOfLastAccess\n", __stringVal(aPathName));
# endif
__BEGIN_INTERRUPTABLE__
do {
ret = stat((char *) __stringVal(aPathName), &buf);
} while (ret < 0 && errno == EINTR);
__END_INTERRUPTABLE__
if (ret < 0) {
@global(LastErrorNumber) = __MKSMALLINT(errno);
RETURN (nil);
}
osSeconds = __MKUINT(buf.st_atime);
}
%}.
osSeconds notNil ifTrue:[^ AbsoluteTime fromOSTime:(osSeconds * 1000)].
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
"
|osSeconds i|
%{
struct stat buf;
int ret;
time_t mtime;
if (__isString(aPathName)) {
# ifdef TRACE_STAT_CALLS
printf("stat on '%s' for timeOfLastChange\n", __stringVal(aPathName));
# endif
__BEGIN_INTERRUPTABLE__
do {
ret = stat((char *) __stringVal(aPathName), &buf);
} while (ret < 0 && errno == EINTR);
__END_INTERRUPTABLE__
if (ret < 0) {
@global(LastErrorNumber) = __MKSMALLINT(errno);
RETURN ( nil );
}
osSeconds = __MKUINT(buf.st_mtime);
}
%}.
osSeconds notNil ifTrue:[^ AbsoluteTime fromOSTime:(osSeconds * 1000)].
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
but for huge directory searches the code below is faster
"
%{
struct stat buf;
int ret;
if (__isString(aPathName)) {
# ifdef TRACE_STAT_CALLS
printf("stat on '%s' for type\n", __stringVal(aPathName));
# endif
__BEGIN_INTERRUPTABLE__
do {
ret = stat((char *) __stringVal(aPathName), &buf);
} while (ret < 0 && errno == EINTR);
__END_INTERRUPTABLE__
if (ret < 0) {
@global(LastErrorNumber) = __MKSMALLINT(errno);
RETURN ( nil );
}
switch (buf.st_mode & S_IFMT) {
case S_IFDIR:
RETURN ( @symbol(directory) );
case S_IFREG:
RETURN ( @symbol(regular) );
# ifdef S_IFCHR
case S_IFCHR:
RETURN ( @symbol(characterSpecial) );
# endif
# ifdef S_IFBLK
case S_IFBLK:
RETURN ( @symbol(blockSpecial) );
# endif
# ifdef S_IFLNK
case S_IFLNK:
RETURN ( @symbol(symbolicLink) );
# endif
# ifdef S_IFSOCK
case S_IFSOCK:
RETURN ( @symbol(socket) );
# endif
# ifdef S_IFIFO
case S_IFIFO:
RETURN ( @symbol(fifo) );
# endif
default:
RETURN ( @symbol(unknown) );
}
}
%}.
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 OperatingSystems support/use volumes; on unix,
this always returns an empty string."
^ ''
"Modified: / 5.6.1998 / 18:38:11 / cg"
! !
!UnixOperatingSystem class methodsFor:'interrupts & signals'!
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"
%{ /* NOCONTEXT */
int ret, flags, f;
#if (defined(F_GETFL) && defined(F_SETFL) && defined(FASYNC)) || defined(SYSV4)
if (__isSmallInteger(fd)) {
f = __intVal(fd);
# if defined(SYSV4)
ret = ioctl(f, I_SETSIG, 0);
# else /*! SYSV4*/
flags = fcntl(f, F_GETFL, 0);
/*
* if already clear, there is no need for this syscall ...
*/
if (flags & FASYNC) {
ret = fcntl(f, F_SETFL, flags & ~FASYNC);
if (ret >= 0) ret = flags;
} else {
ret = flags;
}
# endif /* !SYSV4 */
RETURN ( __MKSMALLINT(ret) );
}
#endif
%}.
"
this error is triggered on non-integer argument
or if the OS does not support IO interrupts.
"
^ self primitiveFailed
!
disableSignal:signalNumber
"disable (Unix-) signal processing for signalNumber.
Dont confuse Unix signals with smalltalk signals.
WARNING: for some signals, it is no good idea to disable
them; for example, disabling the SIGINT signal turns off ^C
handling.
Also, NOTICE that signal numbers are not portable between unix
systems - use OperatingSystem sigXXX to get the numeric value for
a signal.
Use only for fully debugged stand alone applications."
%{ /* NOCONTEXT */
if (__isSmallInteger(signalNumber)) {
int sigNo = __intVal(signalNumber);
if (sigNo == 0) {
RETURN (self);
}
#ifdef SIG_IGN
signal(sigNo, SIG_IGN);
RETURN (self);
#endif
}
%}.
"
this error is triggered on non-integer argument
"
^ self primitiveFailed
"
'now, ^C is totally ignored ...' printNewline.
OperatingSystem disableSignal:(OperatingSystem sigINT).
1 to:1000000 do:[:i| ].
OperatingSystem enableSignal:(OperatingSystem sigINT).
'^C handled again.' printNewline
"
!
disableTimer
"disable timer interrupts.
WARNING:
the system will not operate correctly with timer interrupts
disabled, because no scheduling or timeouts are possible."
%{ /* NOCONTEXT */
#if defined(ITIMER_REAL) && !defined(NO_SETITIMER)
struct itimerval dt;
dt.it_interval.tv_sec = 0;
dt.it_interval.tv_usec = 0;
dt.it_value.tv_sec = 0;
dt.it_value.tv_usec = 0;
setitimer(ITIMER_REAL, &dt, 0);
RETURN (true);
#else
# if defined(USE_SLOW_ALARM)
# if defined(SIGALRM)
alarm(0);
RETURN (true);
# endif /* SIGALRM */
# endif
#endif
%}.
^ false
!
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"
%{ /* NOCONTEXT */
int ret, flags, f;
#ifndef __signalIoInterrupt
extern void __signalIoInterrupt();
#endif
static int firstCall = 1;
#if (defined(F_GETFL) && defined(F_SETFL) && defined(FASYNC)) || defined(SYSV4)
/*
* SIGIO/SIGPOLL - data available for I/O
* (used to wake up waiting processes)
*/
#ifdef SIGIO
# define THESIGNAL SIGIO
#else
# ifdef SIGPOLL
# define THESIGNAL SIGPOLL
# else
# ifdef SIGURG
# define THESIGNAL SIGURG
# endif
# endif
#endif
if (__isSmallInteger(fd)) {
if (firstCall) {
#ifdef HAS_SIGACTION
struct sigaction act = {0};
/*
* Do not add SA_RESTART here. A signal can cause a
* thread switch, another thread can do a garbage collect
* and restarted system calls may write into old
* (collected) addresses.
*/
act.sa_flags = SA_SIGINFO; /* <- if you add more, remember dummys at the top */
sigemptyset(&act.sa_mask);
act.sa_handler = __signalIoInterrupt;
sigaction(THESIGNAL, &act, 0);
#else
# ifdef HAS_SIGVEC
struct sigvec vec;
vec.sv_flags = SV_INTERRUPT;
sigemptyset(&vec.sv_mask);
vec.sv_handler = __signalIoInterrupt;
sigvec(THESIGNAL, &vec, NULL);
# else
signal(THESIGNAL, __signalIoInterrupt);
# endif
#endif
firstCall = 0;
}
#undef THESIGNAL
f = __intVal(fd);
# if defined(SYSV4)
ret = ioctl(f, I_SETSIG, S_INPUT | S_HIPRI | S_ERROR | S_RDNORM | S_RDBAND | S_MSG | S_HANGUP);
# else /*! SYSV4*/
flags = fcntl(f, F_GETFL, 0);
/*
* if already set, there is no need for this syscall ...
*/
if (flags & FASYNC) {
ret = flags;
} else {
ret = fcntl(f, F_SETFL, flags | FASYNC);
if (ret >= 0) ret = flags;
}
# endif /*!SYSV4*/
#if defined(F_SETOWN) || defined(FIOSETOWN)
{
int pid;
int ok;
pid = getpid();
# if defined(F_SETOWN)
ok = fcntl(f, F_SETOWN, pid);
/* printf("F_SETOWN returns %d (%d)\n", ret, errno); */
# else
ok = ioctl(f, FIOSETOWN, &pid);
/* printf("FIOSETOWN returns %d (%d)\n", ret, errno); */
# endif
if (ok < 0) {
ret = ok;
}
}
#endif
RETURN ( __MKUINT(ret) );
}
#endif
%}.
"
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 __signalTimerInterrupt
extern void __signalTimerInterrupt(SIGHANDLER_ARG);
# endif
#endif
#ifndef __signalInterrupt
extern void __signalInterrupt(SIGHANDLER_ARG);
#endif
void (*handler)(SIGHANDLER_ARG);
if (__isSmallInteger(signalNumber)
&& ((sigNr = __intVal(signalNumber)) >= 0)
#ifdef SIG_LIMIT
&& (sigNr <= SIG_LIMIT)
#endif
) {
/*
* standard signals are forced into standard handlers
* - all others go into general signalInterrupt
*/
#if defined(SIGPOLL) && defined(SIGIO)
if (sigNr == SIGPOLL)
sigNr = SIGIO;
#endif
switch (sigNr) {
case 0:
/* enabling a non-supported signal */
RETURN (self);
#ifdef SIGBREAK
case SIGBREAK:
#endif
#ifdef SIGINT
case SIGINT:
#endif
#ifdef SIGQUIT
case SIGQUIT:
#endif
#if defined(SIGINT) || defined(SIGQUIT) || defined(SIGBREAK)
handler = __signalUserInterrupt;
break;
#endif
#ifdef SIGFPE
case SIGFPE:
handler = __signalFpExceptionInterrupt;
break;
#endif
#ifdef SIGPIPE
case SIGPIPE:
handler = __signalPIPEInterrupt;
break;
#endif
#ifdef SIGBUS
case SIGBUS:
handler = __signalBUSInterrupt;
break;
#endif
#ifdef SIGSEGV
case SIGSEGV:
handler = __signalSEGVInterrupt;
break;
#endif
#ifdef 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
#ifdef SIGALRM
case SIGALRM:
handler = __signalTimerInterrupt;
break;
#endif
default:
handler = __signalInterrupt;
break;
}
{
#ifdef HAS_SIGACTION
struct sigaction act = {0};
/*
* Do not add SA_RESTART here. A signal can cause a
* thread switch, another thread can do a garbage collect
* and restarted system calls may write into old
* (collected) addresses.
*/
act.sa_flags = SA_SIGINFO; /* <- if you add more, remember dummys at the top */
sigemptyset(&act.sa_mask);
act.sa_handler = handler;
sigaction(sigNr, &act, 0);
#else
# ifdef HAS_SIGVEC
struct sigvec vec;
vec.sv_flags = SV_INTERRUPT;
sigemptyset(&vec.sv_mask);
vec.sv_handler = handler;
sigvec(sigNr, &vec, NULL);
# else
(void) signal(sigNr, handler);
# endif
#endif
}
/*
* maybe, we should ret 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 */
int millis;
millis = __intVal(milliSeconds);
#ifdef SIGALRM
{
static int firstCall = 1;
# ifndef __signalTimerInterrupt
extern void __signalTimerInterrupt(SIGHANDLER_ARG);
# endif
if (firstCall) {
# ifdef HAS_SIGACTION
struct sigaction act = {0};
act.sa_flags = SA_SIGINFO; /* <- if you add more, remember dummys at the top */
sigemptyset(&act.sa_mask);
act.sa_handler = __signalTimerInterrupt;
sigaction(SIGALRM, &act, 0);
# else
# ifdef HAS_SIGVEC
struct sigvec vec;
vec.sv_flags = SV_INTERRUPT;
sigemptyset(&vec.sv_mask);
vec.sv_handler = __signalTimerInterrupt;
sigvec(SIGALRM, &vec, NULL);
# else /* neither SIGACTION nor SIGVEC */
signal(SIGALRM, __signalTimerInterrupt);
# endif /* stupid system */
# endif
firstCall = 0;
}
}
#endif /* SIGALRM */
#if defined(ITIMER_REAL) && !defined(NO_SETITIMER)
{
struct itimerval dt;
dt.it_interval.tv_sec = 0;
dt.it_interval.tv_usec = 0;
dt.it_value.tv_sec = millis / 1000;
dt.it_value.tv_usec = (millis % 1000) * 1000;
setitimer(ITIMER_REAL, &dt, 0);
RETURN (true);
}
#else /* no ITIMER_REAL */
# ifdef USE_SLOW_ALARM
{
/*
* last fallback - use alarm (which only gives 1 second resolution).
* If the system does not support any of the above, you have to life
* with this. The consequence is that pressing CTRL-C processing and
* thread switching will take place much delayed.
*/
alarm(1);
RETURN(true);
}
# endif
#endif /* ITIMER_REAL */
%}.
^ false
!
killProcess:processId
"kill a unix process.
The process has a no chance to do some cleanup.
WARNING: in order to avoid zombie processes (on unix),
you may have to fetch the processes exitstatus with
OperatingSystem>>getStatusOfProcess:aProcessId."
self sendSignal:(self sigKILL) to:processId.
"Modified: / 10.6.1998 / 11:59:12 / cg"
!
killProcessGroup:processGroupId
"kill a unix process group.
The process has a no chance to do some cleanup.
WARNING: in order to avoid zombie processes (on unix),
you may have to fetch the processes exitstatus with
OperatingSystem>>getStatusOfProcess:aProcessId."
self sendSignal:(self sigKILL) to:(processGroupId negated).
"Created: / 10.6.1998 / 11:59:29 / cg"
!
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."
%{
if (__bothSmallInteger(signalNumber, processId)) {
if (kill(__intVal(processId), __intVal(signalNumber)) < 0) {
@global(LastErrorNumber) = __MKSMALLINT(errno);
RETURN ( false );
}
RETURN ( true );
}
%}.
"/
"/ either invalid argument (non-integers)
"/ or not supported by OS
"/
^ self primitiveFailed
!
startSpyTimer
"trigger a spyInterrupt, to be signalled after some short (virtual) time.
This is used by the old MessageTally for profiling.
Should be changed to use real profiling timer if available.
On systems, where no virtual timer is available, use the real timer
(which is of course less correct).
OBSOLETE: the new messageTally runs as a high prio process, not using
spy interrupts."
%{ /* NOCONTEXT */
#ifndef __spyInterrupt
extern void __spyInterrupt();
#endif
#if defined(ITIMER_VIRTUAL) && !defined(NO_SETITIMER)
struct itimerval dt;
# ifdef SIGVTALRM
signal(SIGVTALRM, __spyInterrupt);
# else
# ifdef SIGALRM
signal(SIGALRM, __spyInterrupt);
# else
/*
* mhmh - system has neither SIGBTALRM nor SIGALRM ...
* what should we do here ?
*/
# endif
# endif
dt.it_interval.tv_sec = 0;
dt.it_interval.tv_usec = 0;
dt.it_value.tv_sec = 0;
dt.it_value.tv_usec = 1000; /* 1000 Hz */
setitimer(ITIMER_VIRTUAL, &dt, 0);
RETURN (true);
#endif /* ITIMER_VIRTUAL */
%}.
^ false
!
stopSpyTimer
"stop spy timing - disable spy timer.
OBSOLETE: the new messageTally runs as a high prio process, not using
spy interrupts."
%{ /* NOCONTEXT */
#if defined(ITIMER_VIRTUAL) && !defined(NO_SETITIMER)
struct itimerval dt;
dt.it_interval.tv_sec = 0;
dt.it_interval.tv_usec = 0;
dt.it_value.tv_sec = 0;
dt.it_value.tv_usec = 0;
setitimer(ITIMER_VIRTUAL, &dt, 0);
RETURN (true);
#endif /* ITIMER_VIRTUAL */
%}.
^ false
!
terminateProcess:processId
"terminate a unix process.
The process has a chance to do some cleanup.
WARNING: in order to avoid zombie processes (on unix),
you may have to fetch the processes exitstatus with
OperatingSystem>>getStatusOfProcess:aProcessId."
self sendSignal:(self sigTERM) to:processId.
"Modified: / 28.12.1995 / 15:05:37 / stefan"
"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 sendSignal:(self sigTERM) to:(processGroupId negated).
"Modified: / 28.12.1995 / 15:05:37 / stefan"
"Created: / 23.4.1996 / 16:40:34 / stefan"
"Modified: / 27.1.1998 / 20:05:59 / cg"
! !
!UnixOperatingSystem class methodsFor:'ipc support - UNIX'!
makePTYPair
"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."
|fdS fdM|
%{
/*
* Find a pseudo tty to use and open both sides.
* _fdM receives the master file descriptor while _fdS
* receives the slave. The master is opened with O_NDELAY as commonly
* needed in daemons such as rlogind and telnetd.
*/
#ifdef IRIX5
int _fdM, _fdS;
char *line;
line = _getpty(&_fdM, O_RDWR|O_NDELAY, 0600, 0);
if ((line != 0) && (_fdM >= 0)) {
_fdS = open(line, O_RDWR);
if (_fdS < 0) {
(void)close(_fdM);
_fdS = _fdM = -1;
}
} else {
_fdM -1;
}
if ((_fdM >= 0) && (_fdS >= 0)) {
fdM = __MKSMALLINT(_fdM);
fdS = __MKSMALLINT(_fdS);
}
# define NO_PTY_TEMPL
#endif /* IRIX5 */
#ifdef LINUX
# define PTY_TEMPL "/dev/ptyXX"
# define PT_INDEX 5
# define PTY_1_CHARS "pqrstuabcdevwxyz"
# define PTY_2_CHARS "0123456789abcdef"
#endif /* LINUX */
#ifdef solaris
# define PTY_TEMPL "/dev/ptyXX"
# define PT_INDEX 5
# define PTY_1_CHARS "pqr"
# define PTY_2_CHARS "0123456789abcdef"
#endif /* solaris */
#ifdef aix
# define PTY_TEMPL "/dev/ptyXX"
# define PT_INDEX 5
# define PTY_1_CHARS "pqr"
# define PTY_2_CHARS "0123456789abcdef"
#endif /* aix */
#ifdef next3
# define PTY_TEMPL "/dev/ptyXX"
# define PT_INDEX 5
# define PTY_1_CHARS "pqr"
# define PTY_2_CHARS "0123456789abcdef"
#endif /* next3 */
#ifdef hpux
# define PTY_TEMPL "/dev/ptyXX"
# define PT_INDEX 5
# define PTY_1_CHARS "pqrs"
# define PTY_2_CHARS "0123456789abcdef"
#endif /* hpux */
#if defined(__osf__)
# define PTY_TEMPL "/dev/ptyXX"
# define PT_INDEX 5
# define PTY_1_CHARS "pq"
# define PTY_2_CHARS "0123456789abcdef"
#endif /* osf1 */
#if defined(BSD) && !defined(PTY_TEMPL)
# define PTY_TEMPL "/dev/ptyXX"
# define PT_INDEX 5
# define PTY_1_CHARS "p"
# define PTY_2_CHARS "0123456789abcdefghijklmnopqrstuv"
#endif /* BSD */
#ifdef NO_PTY_TEMPL
# undef PTY_TEMPL
#endif
#ifdef PTY_TEMPL
# include <grp.h>
# ifdef OLD /* mhmh - suse linux6 places line into rodata ??? */
static char line[] = PTY_TEMPL;
# else
char line[128];
# endif
register CONST char *cp1, *cp2;
int len, _fdM = -1, _fdS = -1, ttygid;
struct group *gr;
# ifdef OLD
len = sizeof(PTY_TEMPL) - 1;
# else
strcpy(line, PTY_TEMPL);
len = strlen(line);
# endif
if ((gr = getgrnam("tty")) != NULL)
ttygid = gr->gr_gid;
else
ttygid = -1;
for (cp1 = PTY_1_CHARS; *cp1; cp1++) {
line[len-2] = * cp1;
for( cp2 = PTY_2_CHARS; *cp2; cp2++ ) {
line[PT_INDEX] = 'p';
line[len-1] = *cp2;
if ((_fdM = open(line, O_RDWR, 0)) < 0) {
if (errno == ENOENT) {
_fdM = _fdS = -1;
goto getOutOfHere; /* out of ptys */
}
} else {
line[PT_INDEX] = 't';
(void) chown( line, getuid(), ttygid );
(void) chmod( line, S_IRUSR|S_IWUSR|S_IWGRP );
if( (_fdS = open(line, O_RDWR, 0)) >= 0 ) {
goto getOutOfHere;
}
(void) close( _fdM );
}
}
}
getOutOfHere: ;
if ((_fdM >= 0) && (_fdS >= 0)) {
fdM = __MKSMALLINT(_fdM);
fdS = __MKSMALLINT(_fdS);
}
#endif /* PTY_TEMPL */
#undef NO_PTY_TEMPL
%}.
fdM notNil ifTrue:[
^ Array with:fdM with:fdS.
].
^ nil
!
makePipe
"make a pipe, return array with two filedescriptors on success,
nil on failure.
This is a lowLevel entry, not for public use.
See ExternalStream>>makePipe for a more user-friendly, public interface."
|fd1 fd2|
%{
int fds[2];
if (pipe(fds) < 0) {
@global(LastErrorNumber) = __MKSMALLINT(errno);
RETURN ( nil );
}
fd1 = __MKSMALLINT(fds[0]);
fd2 = __MKSMALLINT(fds[1]);
%}.
fd1 notNil ifTrue:[
^ Array with:fd1 with:fd2.
].
^ nil
!
setWindowSizeOnFileDescriptor:fd width:w height:h
"emit a TIOCSWINSZ ioctl; (req'd for terminal emulators)"
%{
#ifdef TIOCSWINSZ
struct winsize wsize;
if (__isSmallInteger(fd)
&& __isSmallInteger(w)
&& __isSmallInteger(h)) {
wsize.ws_row = (unsigned short)__intVal(h);
wsize.ws_col = (unsigned short)__intVal(w);
if (ioctl(__intVal(fd), TIOCSWINSZ, (char *)&wsize) >= 0) {
RETURN (true);
}
}
#endif
%}.
^ false
! !
!UnixOperatingSystem class methodsFor:'misc'!
closePid:pid
"free pid resource"
^ true.
"Created: / 28.1.1998 / 14:23:04 / md"
"Modified: / 28.1.1998 / 14:27:18 / md"
"Modified: / 5.6.1998 / 18:38:46 / cg"
!
slowFork:aBoolean
"set/clear the `avoid-fork-if-possible-because-its-slow' flag.
Only used internally on SYSV3 systems"
SlowFork := aBoolean
"Modified: 22.4.1996 / 13:13:09 / cg"
! !
!UnixOperatingSystem class methodsFor:'os queries'!
executableFileExtensions
"return a collection of extensions for executable program files.
Only req'd for msdos like systems ..."
^ #('')
"Created: / 2.5.1997 / 11:42:29 / cg"
"Modified: / 5.6.1998 / 18:38:52 / 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
].
name := self primGetDomainName.
name isNil ifTrue:[
"/ fallBack
name := self getEnvironment:'DOMAIN'.
name isNil ifTrue:[
name := self getCommandOutputFrom:'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:[
'UnixOperatingSystem [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;
extern char *getenv();
if (__isString(aStringOrSymbol) || __isSymbol(aStringOrSymbol)) {
env = getenv(__stringVal(aStringOrSymbol));
if (env) {
RETURN ( __MKSTRING(env) );
}
RETURN ( nil );
}
%}.
^ self primitiveFailed
"
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
].
name := self primGetHostName.
name isNil ifTrue:[
"/ fallBack
name := self getEnvironment:'HOST'.
name isNil ifTrue:[
name := self getCommandOutputFrom:'hostname'
]
].
name isNil ifTrue:[
'UnixOperatingSystem [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 = getpid();
RETURN ( __MKSMALLINT(pid) );
%}
"
OperatingSystem getProcessId
"
!
getSystemID
"if supported by the OS, return the systemID;
a unique per machine identification.
WARNING:
not all systems support this; on some, 'unknown' is returned."
%{ /* NO_CONTEXT */
#if defined(IRIX5) && !defined(HAS_GETHOSTID)
char idBuffer[MAXSYSIDSIZE];
int retVal;
OBJ arr;
if ((retVal = syssgi(SGI_SYSID, idBuffer)) == 0) {
arr = __BYTEARRAY_UNINITIALIZED_NEW_INT(MAXSYSIDSIZE);
bcopy(idBuffer, __ByteArrayInstPtr(arr)->ba_element, MAXSYSIDSIZE);
RETURN (arr);
}
#endif
#if defined(HAS_GETHOSTID)
int runningId;
OBJ arr;
runningId = gethostid();
arr = __BYTEARRAY_UNINITIALIZED_NEW_INT(4);
*(int *)(__ByteArrayInstPtr(arr)->ba_element) = runningId;
RETURN (arr);
#endif
#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).
Also, applications could enable/disable buffering or otherwise reduce
their memory usage depending upon the amount of memory installed.
Your application may make use of available information for tuning,
but should NEVER 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 CPU (i586, mips ...)
those are currently returned on some machines (no warranty)
linux:
#totalRam -> total amount of memory available
#sharedRam -> amount of memory which is shared among processes
(i.e. shared code)
#bufferRam -> amount used for buffers
#swapSize -> total size of swap space
#freeSwap -> free amount in swapSpace
#extendedInstructions -> extended instruction set info
osf:
#physicalRam -> total amount of physical memory
#cpuType -> type of cpu (more detailed than machine)
#numberOfCPUs -> number of cpus in box
solaris:
#physicalRam -> total amount of physical memory
#availableRam -> total available amount of physical memory (i.e. unused ram)
#freeRam -> amount of free memory
#numberOfCPUs -> number of cpus in box (online CPUS)
[#dCacheSize] -> bytes in data cache (only available on some solaris versions)
[#iCacheSize] -> bytes in data cache (only available on some solaris versions)
[#instructionSets]-> instruction sets available (only available on some solaris versions)
[#platform] -> platform name (only available on some solaris versions)
hpux:
#physicalRam -> total amount of physical memory in box
#activeRealMemory -> ? - read pstat documentation
#activeVirtualRam -> ? - read pstat documentation
#freeMemory -> ? - read pstat documentation
#realMemory -> ? (amount of memory left to user programs)
#virtualRam -> ? - read pstat documentation
"
|sys node rel ver mach dom mtyp brel info arch cpuType cpuSpeed
physicalRam availableRam totalRam sharedRam bufferRam swapSize freeSwap
numberOfCPUs pageSize physicalPages availablePages dCacheSize iCacheSize
virtualRam activeVirtualRam realMemory activeRealMemory freeMemory
instructionSets extendedInstructions platform|
%{ /* STACK: 4096 */
#ifdef LINUX
# ifdef ELF /* old a.out unixes do not have this ... */
/*
* additional info available ...
*/
struct sysinfo infoBuffer;
if (sysinfo(&infoBuffer) >= 0) {
totalRam = __MKUINT(infoBuffer.totalram);
sharedRam = __MKUINT(infoBuffer.sharedram);
bufferRam = __MKUINT(infoBuffer.bufferram);
swapSize = __MKUINT(infoBuffer.totalswap);
freeSwap = __MKUINT(infoBuffer.freeswap);
}
# endif
#endif /* LINUX */
#if defined(hpux) && !defined(__GNUC__)
# include <sys/pstat.h>
struct pst_static stat_buf;
struct pst_dynamic dynam_buf;
pstat(PSTAT_STATIC,&stat_buf,sizeof(stat_buf),0,0);
pstat(PSTAT_DYNAMIC,&dynam_buf,sizeof(dynam_buf),0,0);
physicalRam = __MKUINT(stat_buf.physical_memory/256*1024*1024);
virtualRam = __MKUINT(dynam_buf.psd_vm/256*1024*1024);
activeVirtualRam = __MKUINT(dynam_buf.psd_avm/256*1024*1024);
realMemory = __MKUINT(dynam_buf.psd_rm/256*1024*1024);
activeRealMemory = __MKUINT(dynam_buf.psd_arm/256*1024*1024);
freeMemory = __MKUINT(dynam_buf.psd_free/256*1024*1024);
#endif
#if defined(HAS_UNAME)
{
struct utsname ubuff;
if (uname(&ubuff) >= 0) {
sys = __MKSTRING(ubuff.sysname);
node = __MKSTRING(ubuff.nodename);
rel = __MKSTRING(ubuff.release);
ver = __MKSTRING(ubuff.version);
mach = __MKSTRING(ubuff.machine);
# ifdef HAS_UTS_DOMAINNAME
dom = __MKSTRING(ubuff.domainname);
# endif /* no HAS_UTS_DOMAINNAME */
}
}
#else /* no UNAME */
/*
* use fallBack code below
*/
#endif /* no UNAME */
#if defined(HAS_SYSINFO)
# if defined(SI_ARCHITECTURE)
if (arch == nil) {
char buffer[128];
if (sysinfo(SI_ARCHITECTURE, buffer, sizeof(buffer))) {
arch = __MKSTRING(buffer);
}
}
# endif /* SI_ARCHITECTURE */
# if defined(SI_ISALIST)
{
char buffer[128];
if (sysinfo(SI_ISALIST, buffer, sizeof(buffer))) {
instructionSets = __MKSTRING(buffer);
}
}
# endif /* SI_ISALIST */
# if defined(SI_PLATFORM)
{
char buffer[128];
if (sysinfo(SI_PLATFORM, buffer, sizeof(buffer))) {
platform = __MKSTRING(buffer);
}
}
# endif /* SI_PLATFORM */
# if defined(SI_RELEASE)
{
char buffer[128];
if (sysinfo(SI_RELEASE, buffer, sizeof(buffer))) {
rel = __MKSTRING(buffer);
}
}
# endif /* SI_RELEASE */
#endif /* HAS_SYSINFO */
#if defined(HAS_GETDOMAINNAME)
if (dom == nil) {
char buffer[128];
if (getdomainname(buffer, sizeof(buffer)) == 0) {
dom = __MKSTRING(buffer);
}
}
#endif /* HAS_GETDOMAINNAME */
#if defined(HAS_SYSCONF)
# ifdef _SC_NPROCESSORS_ONLN
{
long val;
val = sysconf(_SC_NPROCESSORS_ONLN);
if (val > 0) {
numberOfCPUs = __MKINT(val);
}
}
# endif
# if defined(_SC_PAGESIZE)
{
long val;
val = sysconf(_SC_PAGESIZE);
if (val != -1) {
pageSize = __MKUINT(val);
}
}
# endif
# if defined(_SC_PHYS_PAGES)
{
long val;
val = sysconf(_SC_PHYS_PAGES);
if (val != -1) {
physicalPages = __MKUINT(val);
}
}
# endif
# if defined(_SC_AVPHYS_PAGES)
{
long val;
val = sysconf(_SC_AVPHYS_PAGES);
if (val != -1) {
availablePages = __MKUINT(val);
}
}
# endif
# if defined(_SC_ICACHE_SZ)
{
long val;
val = sysconf(_SC_ICACHE_SZ);
if (val != -1) {
iCacheSize = __MKUINT(val);
}
}
# endif
# if defined(_SC_DCACHE_SZ)
{
long val;
val = sysconf(_SC_DCACHE_SZ);
if (val != -1) {
dCacheSize = __MKUINT(val);
}
}
# endif
#endif /* HAS_SYSCONF */
#if defined(HAS_GETSYSINFO)
{
INT index;
int retInt32 = 0;
# if defined(GSI_CPU)
index = 0;
if (getsysinfo(GSI_CPU, &retInt32, sizeof(retInt32), &index, NULL) > 0) {
switch (retInt32) {
# ifdef VAX_780
case VAX_780:
cpuType = __MKSTRING("VAX_780");
break;
# endif
# ifdef VAX_750
case VAX_750:
cpuType = __MKSTRING("VAX_750");
break;
# endif
# ifdef VAX_730
case VAX_730:
cpuType = __MKSTRING("VAX_730");
break;
# endif
# ifdef VAX_8600
case VAX_8600:
cpuType = __MKSTRING("VAX_8600");
break;
# endif
# ifdef VAX_8200
case VAX_8200:
cpuType = __MKSTRING("VAX_8200");
break;
# endif
# ifdef VAX_8800
case VAX_8800:
cpuType = __MKSTRING("VAX_8800");
break;
# endif
# ifdef MVAX_I
case MVAX_I:
cpuType = __MKSTRING("MVAX_I");
break;
# endif
# ifdef MVAX_II
case MVAX_II:
cpuType = __MKSTRING("MVAX_II");
break;
# endif
# ifdef V_VAX
case V_VAX:
cpuType = __MKSTRING("V_VAX");
break;
# endif
# ifdef VAX_3600
case VAX_3600:
cpuType = __MKSTRING("VAX_3600");
break;
# endif
# ifdef VAX_6200
case VAX_6200:
cpuType = __MKSTRING("VAX_6200");
break;
# endif
# ifdef VAX_3400
case VAX_3400:
cpuType = __MKSTRING("VAX_3400");
break;
# endif
# ifdef C_VAXSTAR
case C_VAXSTAR:
cpuType = __MKSTRING("C_VAXSTAR");
break;
# endif
# ifdef VAX_60
case VAX_60:
cpuType = __MKSTRING("VAX_60");
break;
# endif
# ifdef VAX_3900
case VAX_3900:
cpuType = __MKSTRING("VAX_3900");
break;
# endif
# ifdef DS_3100
case DS_3100:
cpuType = __MKSTRING("DS_3100");
break;
# endif
# ifdef VAX_8820
case VAX_8820:
cpuType = __MKSTRING("VAX_8820");
break;
# endif
# ifdef DS_5400
case DS_5400:
cpuType = __MKSTRING("DS_5400");
break;
# endif
# ifdef DS_5800
case DS_5800:
cpuType = __MKSTRING("DS_5800");
break;
# endif
# ifdef DS_5000
case DS_5000:
cpuType = __MKSTRING("DS_5000");
break;
# endif
# ifdef DS_CMAX
case DS_CMAX:
cpuType = __MKSTRING("DS_CMAX");
break;
# endif
# ifdef VAX_6400
case VAX_6400:
cpuType = __MKSTRING("VAX_6400");
break;
# endif
# ifdef VAXSTAR
case VAXSTAR:
cpuType = __MKSTRING("VAXSTAR");
break;
# endif
# ifdef DS_5500
case DS_5500:
cpuType = __MKSTRING("DS_5500");
break;
# endif
# ifdef DS_5100
case DS_5100:
cpuType = __MKSTRING("DS_5100");
break;
# endif
# ifdef VAX_9000
case VAX_9000:
cpuType = __MKSTRING("VAX_9000");
break;
# endif
# ifdef DS_500_100
case DS_500_100:
cpuType = __MKSTRING("DS_500_100");
break;
# endif
# ifdef ALPHA_ADU
case ALPHA_ADU:
cpuType = __MKSTRING("ALPHA_ADU");
break;
# endif
# ifdef DEC_4000
case DEC_4000:
cpuType = __MKSTRING("DEC_4000");
break;
# endif
# ifdef DEC_3000_500
case DEC_3000_500:
cpuType = __MKSTRING("DEC_3000_500");
break;
# endif
# ifdef DEC_7000
case DEC_7000:
cpuType = __MKSTRING("DEC_7000");
break;
# endif
# ifdef DS_5000_300
case DS_5000_300:
cpuType = __MKSTRING("DS_5000_300");
break;
# endif
# ifdef DEC_3000_300
case DEC_3000_300:
cpuType = __MKSTRING("DEC_3000_300");
break;
# endif
# ifdef DEC_2000_300
case DEC_2000_300:
cpuType = __MKSTRING("DEC_2000_300");
break;
# endif
# ifdef DEC_2100_A500
case DEC_2100_A500:
cpuType = __MKSTRING("DEC_2100_A500");
break;
# endif
# ifdef DEC_2100_A50
case DEC_2100_A50:
cpuType = __MKSTRING("DEC_2100_A50");
break;
# endif
# ifdef ALPHA_KN20AA
case ALPHA_KN20AA:
cpuType = __MKSTRING("ALPHA_KN20AA");
break;
# endif
# ifdef DEC_21000
case DEC_21000:
cpuType = __MKSTRING("DEC_21000");
break;
# endif
# ifdef DEC_AXPVME_64
case DEC_AXPVME_64:
cpuType = __MKSTRING("DEC_AXPVME_64");
break;
# endif
# ifdef DEC_2100_C500
case DEC_2100_C500:
cpuType = __MKSTRING("DEC_2100_C500");
break;
# endif
# ifdef DEC_AXPPCI_33
case DEC_AXPPCI_33:
cpuType = __MKSTRING("DEC_AXPPCI_33");
break;
# endif
# ifdef DEC_1000
case DEC_1000:
cpuType = __MKSTRING("DEC_1000");
break;
# endif
# ifdef EB64_PLUS
case EB64_PLUS:
cpuType = __MKSTRING("EB64_PLUS");
break;
# endif
# ifdef LCA_EB66
case LCA_EB66:
cpuType = __MKSTRING("LCA_EB66");
break;
# endif
# ifdef ALPHA_EB164
case ALPHA_EB164:
cpuType = __MKSTRING("ALPHA_EB164");
break;
# endif
# ifdef DEC_EV45_PBP
case DEC_EV45_PBP:
cpuType = __MKSTRING("DEC_EV45_PBP");
break;
# endif
# ifdef DEC_1000A
case DEC_1000A:
cpuType = __MKSTRING("DEC_1000A");
break;
# endif
# ifdef DEC_4100
case DEC_4100:
cpuType = __MKSTRING("DEC_4100");
break;
# endif
# ifdef DEC_ALPHAVME_224
case DEC_ALPHAVME_224:
cpuType = __MKSTRING("DEC_ALPHAVME_224");
break;
# endif
# ifdef DEC_1000_5
case DEC_1000_5:
cpuType = __MKSTRING("DEC_1000_5");
break;
# endif
# ifdef DEC_1000A_5
case DEC_1000A_5:
cpuType = __MKSTRING("DEC_1000A_5");
break;
# endif
# ifdef DEC_EV56_PBP
case DEC_EV56_PBP:
cpuType = __MKSTRING("DEC_EV56_PBP");
break;
# endif
# ifdef ALPHABOOK
case ALPHABOOK:
cpuType = __MKSTRING("ALPHABOOK");
break;
# endif
# ifdef DEC_ALPHAVME_320
case DEC_ALPHAVME_320:
cpuType = __MKSTRING("DEC_ALPHAVME_320");
break;
# endif
# ifdef DEC_550
case DEC_550:
cpuType = __MKSTRING("DEC_550");
break;
# endif
# ifdef DEC_6600
case DEC_6600:
cpuType = __MKSTRING("DEC_6600");
break;
# endif
# ifdef UNKN_SYSTEM
case UNKN_SYSTEM:
cpuType = __MKSTRING("UNKN_SYSTEM");
break;
# endif
default:
cpuType = __MKSTRING("OTHER_DEC_SYSTEM");
break;
}
}
# endif /* GSI_CPU */
# if defined(GSI_CPU_INFO)
/*
* stupid: OSF1 pre V4.0 has no mhz, but V4.0 has it.
* use the GSI_PLATFORM_NAME as a hint - it is only defined in
* V4.0 and higher ... (sigh)
*/
# if defined GSI_PLATFORM_NAME
{
struct cpu_info cpuInfo;
index = 0;
if (getsysinfo(GSI_CPU_INFO, &cpuInfo, sizeof(cpuInfo), &index, NULL) > 0) {
cpuSpeed = __MKUINT(cpuInfo.mhz);
}
}
# endif
# endif /* GSI_CPU_INFO */
# if defined(GSI_CPUS_IN_BOX)
index = 0;
if (getsysinfo(GSI_CPUS_IN_BOX, &retInt32, sizeof(retInt32), &index, NULL) > 0) {
numberOfCPUs = __MKUINT(retInt32);
}
# endif /* GSI_CPUS_IN_BOX */
# if defined(GSI_PHYSMEM)
index = 0;
if (getsysinfo(GSI_PHYSMEM, &retInt32, sizeof(retInt32), &index, NULL) > 0) {
INT bytes = retInt32 * 1024;
physicalRam = __MKUINT(bytes);
}
# endif /* GSI_PHYSMEM */
# if defined(GSI_PLATFORM_NAME) && (!defined(HAS_SYSINFO) || !defined(SI_PLATFORM))
{
char buffer[128];
index = 0;
if (getsysinfo(GSI_PLATFORM_NAME, buffer, sizeof(buffer), &index, NULL) > 0) {
platform = __MKSTRING(buffer);
}
}
# endif /* GSI_PLATFORM_NAME */
}
#endif /* HAS_GETSYSINFO */
{
extern OBJ __getInstructionSetInfo();
extendedInstructions = __getInstructionSetInfo();
}
%}.
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 := sys.
].
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].
(pageSize notNil and:[physicalPages notNil]) ifTrue:[
physicalRam := pageSize * physicalPages. "/ done here - could be largeInt.
].
physicalRam notNil ifTrue:[info at:#physicalRam put:physicalRam].
(pageSize notNil and:[availablePages notNil]) ifTrue:[
availableRam := pageSize * availablePages. "/ done here - could be largeInt.
availableRam notNil ifTrue:[info at:#availableRam put:availableRam].
].
totalRam notNil ifTrue:[info at:#totalRam put:totalRam].
sharedRam notNil ifTrue:[info at:#sharedRam put:sharedRam].
bufferRam notNil ifTrue:[info at:#bufferRam put:bufferRam].
virtualRam notNil ifTrue:[info at:#virtualRam put:virtualRam].
activeVirtualRam notNil ifTrue:[info at:#activeVirtualRam put:activeVirtualRam].
realMemory notNil ifTrue:[info at:#realMemory put:realMemory].
activeRealMemory notNil ifTrue:[info at:#activeRealMemory put:activeRealMemory].
freeMemory notNil ifTrue:[info at:#freeMemory put:freeMemory].
swapSize notNil ifTrue:[info at:#swapSize put:swapSize].
freeSwap notNil ifTrue:[info at:#freeSwap put:freeSwap].
numberOfCPUs notNil ifTrue:[info at:#numberOfCPUs put:numberOfCPUs].
cpuType notNil ifTrue:[info at:#cpuType put:cpuType].
cpuSpeed notNil ifTrue:[info at:#cpuSpeed put:cpuSpeed].
dCacheSize notNil ifTrue:[info at:#dCacheSize put:dCacheSize].
iCacheSize notNil ifTrue:[info at:#iCacheSize put:iCacheSize].
instructionSets notNil ifTrue:[info at:#instructionSets put:instructionSets].
extendedInstructions notNil ifTrue:[info at:#extendedInstructions put:(extendedInstructions asOrderedCollection select:[:e | e notNil])].
platform notNil ifTrue:[info at:#platform put:platform].
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)"
|sys|
%{
# ifdef NEXT
# define SYS_STRING "next"
# endif
# ifdef IRIS
# define SYS_STRING "iris"
# endif
# ifdef SYS_STRING
sys = __MKSTRING(SYS_STRING);
# undef SYS_STRING
# endif
%}.
sys isNil ifTrue:[
^ self getOSType
].
^ sys
"
OperatingSystem getSystemType
"
!
isBSDlike
"return true, if the OS we're running on is a 'real' unix."
%{ /* NOCONTEXT */
#if defined(BSD) || defined(MACH) || defined(SYSV4)
RETURN ( true );
#endif
%}.
^ false
!
isUNIXlike
"return true, if the OS we're running on is a unix like."
^ true
"Modified: / 5.6.1998 / 18:40:39 / cg"
!
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: new systems provide a query function for this ... use it
*/
# if defined(MAXFILENAMELEN)
RETURN ( __MKSMALLINT(MAXFILENAMELEN) );
# else
# if defined(BSD) || defined(SYSV4) || defined(LONGFILENAMES)
RETURN ( __MKSMALLINT(255) );
# endif
# ifdef realIX
RETURN ( __MKSMALLINT(127) );
# endif
# ifdef SYSV
RETURN ( __MKSMALLINT(14) );
# endif
# endif
%}.
"unix default"
^ 14
"
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"
"Modified: / 5.6.1998 / 18:41:01 / 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."
^ #unix
"
OperatingSystem platformName
"
"Modified: / 5.6.1998 / 18:41:32 / cg"
!
primGetDomainName
"return the domain this host is in.
Notice:
not all systems support this; on some, nil is returned."
%{ /* STACK: 2048 */
#if defined(HAS_GETDOMAINNAME)
char buffer[128];
if (getdomainname(buffer, sizeof(buffer)) == 0) {
RETURN (__MKSTRING(buffer));
}
#else
# if defined(HAS_UNAME) && defined(HAS_UTS_DOMAINNAME)
struct utsname ubuff;
if (uname(&ubuff) >= 0) {
RETURN (__MKSTRING(ubuff.domainname));
}
# else
# if defined(HAS_SYSINFO) && defined(SI_SRPC_DOMAIN)
char buffer[256];
int ret;
if ((ret = sysinfo(SI_SRPC_DOMAIN, buffer, sizeof(buffer))) >= 0 && ret <= sizeof(buffer)) {
RETURN (__MKSTRING(buffer));
}
# endif
# endif
#endif
%}.
^ nil
"
OperatingSystem primGetDomainName
"
"Modified: 26.4.1996 / 10:04:54 / stefan"
!
primGetHostName
"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, nil is returned."
%{ /* STACK: 2048 */
#if defined(HAS_GETHOSTNAME)
char buffer[256];
if (gethostname(buffer, sizeof(buffer)) == 0) {
RETURN (__MKSTRING(buffer));
}
#else
# if defined(HAS_UNAME)
struct utsname ubuff;
if (uname(&ubuff) >= 0) {
RETURN (__MKSTRING(ubuff.nodename));
}
# else
# if defined(HAS_SYSINFO) && defined(SI_HOSTNAME)
char buffer[256];
int ret;
if ((ret = sysinfo(SI_HOSTNAME, buffer, sizeof(buffer))) >= 0 && ret <= sizeof(buffer)) {
RETURN (__MKSTRING(buffer));
}
# endif
# endif
#endif
%}.
^ nil
"
OperatingSystem getHostName
"
!
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)
RETURN (true);
#endif
%}.
^ false
"
OperatingSystem supportsChildInterrupts
"
!
supportsIOInterrupts
"return true, if the OS supports IO availability interrupts
(i.e. SIGPOLL/SIGIO).
Currently, this mechanism does not work on all
systems ...
"
%{ /* NOCONTEXT */
/* positive defines here
* - irix5.2 does not work
*/
#if defined(LINUX)
# if defined(SIGIO) || defined(SIGPOLL)
# if defined(F_GETFL) && defined(F_SETFL) && defined(FASYNC)
# if defined(F_SETOWN) || defined(FIOSETOWN)
RETURN (true);
# endif
# endif
# endif
#endif /* machines where it works */
%}.
^ false
"
OperatingSystem supportsIOInterrupts
"
!
supportsNonBlockingIO
"return true, if the OS supports nonblocking IO."
%{ /* NOCONTEXT */
#if defined(F_GETFL) && defined(F_SETFL) && defined(FNDELAY)
RETURN (true);
#endif
%}.
^ 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(sco)
/*
* sco has a select, but its broken: always waiting 1 second
*/
RETURN(false);
#endif
%}.
^ true
"
OperatingSystem supportsSelect
"
!
supportsSymbolicLinks
"return true, if the OS supports symbolic (soft) links;
most UNIX'es do; many other OS's do not."
%{ /* NOCONTEXT */
#ifdef S_IFLNK
/*
* assume yes - if that is defined.
*/
RETURN(true);
#endif
%}.
^ false
"
OperatingSystem supportsSymbolicLinks
"
! !
!UnixOperatingSystem class methodsFor:'path queries'!
defaultSystemPath
"add additional directories to the systemPath
(but only, if the major version is the same)"
|sysPath majorVersionNr releaseFile s v|
majorVersionNr := Smalltalk majorVersionNr.
sysPath := super defaultSystemPath.
#(
'/usr/local/lib/smalltalk'
'/usr/lib/smalltalk'
'/lib/smalltalk'
'/opt/smalltalk'
) do:[:d |
(d asFilename isDirectory) ifTrue:[
"/ try to guess a gnu-smalltalk; skip it
(d asFilename construct:'initialize.st') exists ifFalse:[
releaseFile := d asFilename construct:'RELEASE'.
releaseFile exists ifTrue:[
s := releaseFile readStream.
s notNil ifTrue:[
v := Integer readFrom:s onError:-1.
s close.
v == majorVersionNr ifTrue:[
sysPath add:d
] ifFalse:[
('UnixOperatingSystem [info]: ignore files in ' , d asFilename pathName , ' (RELEASE mismatch)') infoPrintCR.
]
] ifFalse:[
('UnixOperatingSystem [info]: ignore files in ' , d asFilename pathName , ' (RELEASE missing)') infoPrintCR.
]
]
]
]
].
^ sysPath
"
OperatingSystem defaultSystemPath
"
! !
!UnixOperatingSystem class methodsFor:'private'!
osProcessStatusClass
^ OSProcessStatus
"Created: / 12.6.1998 / 16:30:43 / cg"
! !
!UnixOperatingSystem class methodsFor:'shared memory access'!
shmAttach:id address:addr flags:flags
"low level entry to shmat()-system call.
Not supported on all operatingSystems"
%{ /* NOCONTEXT */
#ifdef WANT_SHM
void *address, *shmaddr;
int shmflg, shmid;
if (__isSmallInteger(addr)
&& __bothSmallInteger(flags, id)) {
shmaddr = (void *) __intVal(addr);
shmflg = __intVal(flags);
shmid = __intVal(id);
address = shmat(shmid, shmaddr, shmflg);
if (address != (void *)-1) {
RETURN (__MKEXTERNALBYTES(addr));
}
@global(LastErrorNumber) = __MKSMALLINT(errno);
RETURN (nil);
}
#endif
%}.
^ self primitiveFailed
"Modified: 22.4.1996 / 13:15:12 / cg"
!
shmDetach:addr
"low level entry to shmdt()-system call.
Not supported on all operatingSystems"
%{ /* NOCONTEXT */
#ifdef WANT_SHM
void *shmaddr;
int rslt;
if (__isSmallInteger(addr)) {
shmaddr = (void *) __intVal(addr);
rslt = shmdt(shmaddr);
if (rslt != -1) {
RETURN (true);
}
@global(LastErrorNumber) = __MKSMALLINT(errno);
RETURN (false);
}
#endif
%}.
^ self primitiveFailed
"Modified: 22.4.1996 / 13:15:03 / cg"
!
shmGet:key size:size flags:flags
"low level entry to shmget()-system call.
This is not for public use and not supported with all operatingSystems.
- use the provided wrapper class SharedExternalBytes instead."
%{ /* NOCONTEXT */
#ifdef WANT_SHM
if (__bothSmallInteger(key, size)
&& __isSmallInteger(flags)) {
int rslt;
rslt = shmget(__intVal(key), __intVal(size), __intVal(flags));
if (rslt != -1) {
RETURN (__MKSMALLINT(rslt));
}
@global(LastErrorNumber) = __MKSMALLINT(errno);
RETURN (nil);
}
#endif
%}.
^ self primitiveFailed
"Modified: 22.4.1996 / 13:14:46 / cg"
! !
!UnixOperatingSystem class methodsFor:'socket creation'!
socketWithDomain:domainArg type:typeArg protocol:protocolArg
"set up socket with domain, type and protocol number.
This is a low level entry; no binding, listening or connect
is done. Both arguments must be symbols from one of
#inet,#unix, #appletalk, #x25 .. and #stream, #datagram, #raw resp."
^ SocketHandle new domain:domainArg type:typeArg protocol:protocolArg
! !
!UnixOperatingSystem 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 i|
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);
%}.
year isNil ifTrue:[
i := self computeTimeAndDateFrom:osTime.
year := i at:1.
month := i at:2.
day := i at:3.
].
aBlock value:year value:month value:day
"
OperatingSystem computeDatePartsOf:0 for:[:y :m :d |
y printCR. m printCR. d printCR
]
"
!
computeOSTimeFromUTCYear:y month:m day:d hour:h minute:min second:s millisecond:millis
"return the OS-dependent time for the given time and day.
The arguments are assumed to be in UTC Time"
|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+TIMEZONE(&tm));
}
%}.
osSeconds notNil ifTrue:[
^ osSeconds * 1000 + millis
].
^ self primitiveFailed
"
OperatingSystem computeOSTimeFromUTCYear:1970 month:1 day:1 hour:0 minute:0 second:0 millisecond:0
OperatingSystem computeOSTimeFromYear:1970 month:1 day:1 hour:0 minute:0 seconds:0 millis:0
OperatingSystem computeOSTimeFromUTCYear:2000 month:7 day:1 hour:0 minute:0 second:0 millisecond:0
OperatingSystem computeOSTimeFromYear:2000 month:7 day:1 hour:0 minute:0 seconds:0 millis:0
"
!
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."
|millis osSeconds ret|
millis := osTime \\ 1000.
osSeconds := osTime // 1000.
ret := self fillTimeAndDateFromSeconds:osSeconds into:(Array new:11).
ret at:9 put:millis.
^ ret
"
OperatingSystem computeTimeAndDateFrom:0
OperatingSystem computeTimeAndDateFrom:1011
"
!
computeTimePartsOf:osTime for:aBlock
"compute hours, minutes, seconds and milliseconds from the local osTime
and evaluate the argument, a 4-arg block with these.
Conversion is to localtime including any daylight saving adjustments."
|hours minutes seconds millis i|
i := self computeTimeAndDateFrom:osTime.
hours := i at:4.
minutes := i at:5.
seconds := i at:6.
millis := i at:9.
aBlock value:hours value:minutes value:seconds value:millis
"
OperatingSystem computeTimePartsOf:100 for:[:h :m :s :milli |
Transcript show:h; space; show:m; space; show:s; space; showCR:milli.
]
"
!
computeUTCTimeAndDateFrom:osTime
"given an OS-dependent time in osTime, return an Array
containing:
(full-) year,
month, (1..)
day, (1..)
hour, (0..23)
minute (0..59)
seconds, (0..59)
offset to UTC, (seconds)
daylight savings time flag,
milliseconds, (0..999)
dayInYear (1..)
dayInWeek (1..).
Conversion is to utc."
|millis osSeconds ret|
millis := osTime \\ 1000.
osSeconds := osTime // 1000.
ret := self fillTimeAndDateFromUTCSeconds:osSeconds into:(Array new:11).
ret at:9 put:millis.
^ ret
"
OperatingSystem computeUTCTimeAndDateFrom:0
OperatingSystem computeUTCTimeAndDateFrom:1011
"
!
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
]
"
!
fillTimeAndDateFromSeconds:osSeconds into:timeArray
"fill the array with os-time components.
An internal helper"
^ self fillTimeAndDateFromSeconds:osSeconds isLocal:true into:timeArray
!
fillTimeAndDateFromSeconds:osSeconds isLocal:isLocalTime into:timeArray
"fill the array with os-time components.
An internal helper"
|low hi year month day hours minutes seconds utcOffset
dst yDay wDay |
%{
struct tm *tmPtr;
INT t;
TIME_T tt;
t = __longIntVal(osSeconds);
tt = (TIME_T)t;
if (isLocalTime == true) {
tmPtr = localtime(&tt);
} else {
tmPtr = gmtime(&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);
utcOffset = __MKSMALLINT(TIMEZONE(tmPtr));
dst = (tmPtr->tm_isdst == 0 ? false : true);
%}.
"I would love to have SELF-like inline objects ..."
timeArray at:1 put:year.
timeArray at:2 put:month.
timeArray at:3 put:day.
timeArray at:4 put:hours.
timeArray at:5 put:minutes.
timeArray at:6 put:seconds.
timeArray at:7 put:utcOffset.
timeArray at:8 put:dst.
timeArray at:9 put:nil.
timeArray at:10 put:yDay.
timeArray at:11 put:wDay.
^ timeArray
"
OperatingSystem fillTimeAndDateFromSeconds:0 isLocal:false into:(Array new:11)
"
!
fillTimeAndDateFromUTCSeconds:osSeconds into:timeArray
"fill the array with os-time components.
An internal helper"
^ self fillTimeAndDateFromSeconds:osSeconds isLocal:false into:timeArray
"
OperatingSystem fillTimeAndDateFromUTCSeconds:0 into:(Array new:11)
"
!
getMicrosecondTime
"This returns the microsecond timers value - if available.
On some machines, times with this precision may not be available,
on those, the returned value may be rounded towards some internal
clock resolution value."
|seconds millis micros|
%{
long _seconds = 0;
long _millis = 0;
long _micros = 0;
#if !defined(HAS_GETTIMEOFDAY)
# if defined(HAS_FTIME)
struct timeb timebuffer;
ftime(&timebuffer);
_seconds = timebuffer.time;
_millis = timebuffer.millitm;
# define HAVE_TIME
# endif
# ifndef HAVE_TIME
# if defined(SYSV) && defined(HZ)
/*
* sys5 time - in ticks
*/
long ticks;
struct tms tb;
ticks = times(&tb);
_seconds = ticks / HZ;
ticks -= (_seconds * HZ);
_millis = ticks * 1000 / HZ;
# define HAVE_TIME
# endif /* old SYSV stuff */
# 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, NULL /* &tzb */);
_seconds = tb.tv_sec;
_micros = tb.tv_usec;
if (_micros >= (1000*1000)) {
printf("oops\n");
}
#endif
#undef HAVE_TIME
seconds = __MKINT(_seconds);
millis = __MKINT(_millis);
micros = __MKINT(_micros);
%}.
^ (((seconds * 1000) + millis) * 1000) + micros
"
Transcript showCR:(OperatingSystem getMicrosecondTime).
Transcript showCR:(OperatingSystem getMicrosecondTime).
"
!
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
# if defined(SYSV) && defined(HZ)
/*
* sys5 time
*/
long ticks;
struct tms tb;
ticks = times(&tb);
t = (ticks * 1000) / HZ;
# define HAVE_TIME
# endif /* old SYSV stuff */
# 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, NULL /* &tzb */);
t = tb.tv_sec*1000 + tb.tv_usec/1000;
#endif
#undef HAVE_TIME
RETURN ( __MKSMALLINT(t & 0x1FFFFFFF) );
%}.
self primitiveFailed
!
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.
"
|time seconds millis|
time := self primGetOSTime.
time isArray ifTrue:[
seconds := time at:1.
millis := time at:2.
time := (seconds * 1000) + millis
].
^ time
"
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 := UnixOperatingSystem getMillisecondTime.
then := UnixOperatingSystem millisecondTimeAdd:now and:millis.
[UnixOperatingSystem millisecondTime:then isAfter:now] whileTrue:[
delta := UnixOperatingSystem millisecondTimeDeltaBetween:then and:now.
self selectOnAnyReadable:nil writable:nil exception:nil withTimeOut:delta.
now := UnixOperatingSystem getMillisecondTime.
]
"
OperatingSystem millisecondDelay:2000
"
"Modified: / 5.6.1998 / 18:42:17 / cg"
!
primGetOSTime
"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;
long _secs, _millis;
#if !defined(HAS_GETTIMEOFDAY)
# if defined(HAS_FTIME)
{
struct timeb timebuffer;
ftime(&timebuffer);
_secs = timebuffer.time;
_millis = timebuffer.millitm;
}
# define HAVE_TIME
# endif /* HAS_FTIME */
# ifndef HAVE_TIME
# if defined(SYSV) && defined(HZ)
{
/*
* sys5 time; we have to fake the information
* the returned value is inexact.
*/
long ticks;
struct tms tb;
_secs = time(0); /* seconds since 1970 ... */
ticks = times(&tb);
t = (ticks * 1000) / HZ;
t = t % 1000;
_millis = __MKSMALLINT(t);
}
# define HAVE_TIME
# endif /* OLD SYSV stuff */
# endif
#endif /* no HAS_GETTIMEOFDAY */
#ifndef HAVE_TIME
/*
* use HAS_GETTIMEOFDAY even if HAS_GETTIMEOFDAY is undefined here.
* Will result in a linkage error if not fixed and neither ftime() nor time() is used.
*/
{
/*
* bsd time
*/
struct timeval tb;
/* struct timezone tzb; */
gettimeofday(&tb, NULL /* &tzb */);
_secs = tb.tv_sec;
_millis = tb.tv_usec / 1000;
}
#endif
/*
* 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 alpha64
{
unsigned INT _lsecs, _lmillis, rslt;
_lsecs = (INT)_secs;
_lmillis = (INT)_millis;
rslt = _lsecs * 1000 + _lmillis;
RETURN (__MKUINT(rslt));
}
#else
# ifdef HAS_LONGLONG
{
unsigned long long _lsecs, _lmillis, rslt;
unsigned low, hi;
_lsecs = (long long)_secs;
_lmillis = (long long)_millis;
rslt = _lsecs * 1000 + _lmillis;
low = rslt & 0xFFFFFFFF;
hi = rslt >> 32;
RETURN (__MKLARGEINT64(1, low, hi));
}
# else
seconds = __MKUINT(_secs);
millis = __MKUINT(_millis);
RETURN (__ARRAY_WITH2(seconds, millis));
# endif /* long long */
#endif /* alpha64 */
%}.
"
OperatingSystem primGetOSTime printCR.
Delay waitForSeconds:0.2.
OperatingSystem primGetOSTime printCR.
"
!
sleep:numberOfSeconds
"{ Pragma: +optSpace }"
"cease ANY action for some time. This suspends the whole smalltalk
(unix-) process for some time if running threadless;
if running with threads (which is the default), this will
only block until the next clock tick and return with the next
timer interrupt.
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
"
! !
!UnixOperatingSystem 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)."
%{ /* NOCONTEXT */
int uid;
uid = getegid();
RETURN ( __MKSMALLINT(uid) );
%}
"
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)."
%{ /* NOCONTEXT */
int uid;
uid = geteuid();
RETURN ( __MKSMALLINT(uid) );
%}
"
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"
%{ /* NOCONTEXT */
int uid;
uid = getgid();
RETURN ( __MKSMALLINT(uid) );
%}.
^ 1 "just a dummy for systems which do not have userIDs"
"
OperatingSystem getGroupID
"
!
getGroupNameFromID:aNumber
"{ Pragma: +optSpace }"
"return the group-name-string for a given numeric group-id"
%{ /* NOCONTEXT */
struct group *g;
if (__isSmallInteger(aNumber)) {
g = getgrgid(__intVal(aNumber));
if (g) {
RETURN ( __MKSTRING(g->gr_name) );
}
}
%}.
^ '???'
"
OperatingSystem getGroupNameFromID:0
OperatingSystem getGroupNameFromID:10
"
!
getHomeDirectory
"{ Pragma: +optSpace }"
"return the name of the users home directory
(i.e. yours)"
^ UnixOperatingSystem getEnvironment:'HOME'
"
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[128];
static firstCall = 1;
extern char *getenv();
extern char *getlogin();
char *name = (char *)0;
if (firstCall) {
name = getlogin();
if (! name || (name[0] == 0)) {
name = getenv("LOGNAME");
}
if (name && (strlen(name) < sizeof(cachedName))) {
strcpy(cachedName, name);
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"
%{ /* NOCONTEXT */
int uid;
uid = getuid();
RETURN ( __MKSMALLINT(uid) );
%}
"
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."
%{ /* NOCONTEXT */
# ifndef NO_PWD
struct passwd *p;
if (__isSmallInteger(aNumber)) {
p = getpwuid(__intVal(aNumber));
if (p) {
RETURN ( __MKSTRING(p->pw_name) );
}
}
#endif /* unix-like */
%}.
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|
%{
# ifndef NO_PWD
struct passwd *buf;
int ret;
if (__isString(aNameOrID)) {
buf = getpwnam(__stringVal(aNameOrID));
} else if (__isSmallInteger(aNameOrID)) {
buf = getpwuid(__intVal(aNameOrID));
} else {
buf = (struct passwd *)0;
}
if (buf) {
name = __MKSTRING(buf->pw_name);
# ifndef NO_PWD_PASSWD
passw = __MKSTRING(buf->pw_passwd);
# endif
# ifdef SYSV4
age = __MKSTRING(buf->pw_age);
comment = __MKSTRING(buf->pw_comment);
# endif
dir = __MKSTRING(buf->pw_dir);
# ifndef NO_PWD_GECOS
gecos = __MKSTRING(buf->pw_gecos);
# endif
shell = __MKSTRING(buf->pw_shell);
uid = __MKSMALLINT(buf->pw_uid);
gid = __MKSMALLINT(buf->pw_gid);
}
# endif /* has PWD */
%}.
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'
].
passw notNil ifTrue:[info at:#passwd put:passw].
age notNil ifTrue:[info at:#age put:age].
comment notNil ifTrue:[info at:#comment put:comment].
gecos notNil ifTrue:[info at:#gecos put:gecos].
shell notNil ifTrue:[info at:#shell put:shell].
dir 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)
"
! !
!UnixOperatingSystem 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*/
#if defined(HAS_WAITPID) || defined(HAS_WAIT3)
RETURN(false);
#else
RETURN(true);
#endif
%}
!
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|
%{
int p;
#if defined(HAS_WAITPID)
int s;
# define __WAIT waitpid(-1, &s, blocking == true ? WUNTRACED : WNOHANG|WUNTRACED)
#else
# if defined(HAS_WAIT3)
union wait s;
# define __WAIT wait3(&s, blocking == true ? WUNTRACED : WNOHANG|WUNTRACED, 0)
# else /* neither waitpid, nor wait3; use wait, which is blocking */
int s;
# define __WAIT wait(&s)
# define __BLOCKING_WAIT__ 1
if (blocking != true) {
/*
* We do not support nonBlocking waits, so signal an error
* Sorry about the goto, but with all these ifdefs ...
*/
goto done;
}
# endif /*!HAS_WAIT3*/
#endif /*!HAS_WAITPID*/
#if !defined(WIFEXITED)
# define WIFEXITED(stat) (((int)((stat)&0377))==0)
# define WIFSIGNALED(stat) (((int)((stat)&0377))>0&&((int)(((stat)>>8)&0377))==0)
# define WIFSTOPPED(stat) (((int)((stat)&0377))==0177&&((int)(((stat)>>8)&0377))!=0)
# define WEXITSTATUS(stat) ((int)(((stat)>>8)&0377))
# define WTERMSIG(stat) (((int)((stat)&0377))&0177)
# define WSTOPSIG(stat) ((int)(((stat)>>8)&0377))
#endif /*!WIFEXITED*/
#if !defined(WCOREDUMP)
/*
* some systems lack that definition, although the field is there ...
*/
# if defined(HAS_WAIT3)
# define WCOREDUMP(status) (((union __wait*)&(status))->__w_coredump)
# else
# define WCOREDUMP(status) ((int)(((status)>>8)&0200))
# endif
#endif /*!WCOREDUMP*/
#if __BLOCKING_WAIT__
__BEGIN_INTERRUPTABLE__
#endif
do {
p = __WAIT;
} while (p == -1 && errno == EINTR);
#if __BLOCKING_WAIT__
__END_INTERRUPTABLE__
# undef __BLOCKING_WAIT__
#endif
#undef __WAIT
if (p == 0)
RETURN(nil)
if (p == -1) {
if (errno == ECHILD)
RETURN(nil);
} else {
pid = __MKSMALLINT(p);
if (WIFEXITED(s)) {
status = @symbol(exit);
code = __MKSMALLINT(WEXITSTATUS(s));
core = WCOREDUMP(s) ? true : false;
} else if (WIFSIGNALED(s)) {
status = @symbol(signal);
code = __MKSMALLINT(WTERMSIG(s));
} else if (WIFSTOPPED(s)) {
status = @symbol(stop);
code = __MKSMALLINT(WSTOPSIG(s));
}
#if defined(WIFCONTINUED)
else if (WIFCONTINUED(s)) {
status = @symbol(continue);
}
#endif
}
done: ;
%}.
(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."
%{
/*
* if available, try FIONREAD first, which is usually done faster.
*/
# if 0 && defined(FIONREAD)
{
int n;
if (__isSmallInteger(fd)) {
if (ioctl(__intVal(fd), FIONREAD, &n) >= 0) {
RETURN (__MKINT(n));
}
}
}
# endif /* FIONREAD */
%}.
^ (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."
%{
/*
* if available, try FIONREAD first, which is usually done faster.
*/
#if 0 && defined(FIONREAD)
if (__isSmallInteger(fd)) {
int result = 0;
if (ioctl(__smallIntegerVal(fd), FIONREAD, &result) >= 0) {
RETURN(result > 0 ? true : false);
}
}
#endif /* FIONREAD */
%}.
^ 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."
%{
fd_set rset, wset, eset;
struct timeval wt, et;
int f, maxF, i, lX, bX;
INT t;
OBJ fd, retFd;
int ret;
int count;
int numFds = 0;
#ifndef __isArrayLike
# define __isArrayLike __isArray
#endif
if (__isSmallInteger(millis)) {
FD_ZERO(&rset);
FD_ZERO(&wset);
FD_ZERO(&eset);
maxF = -1;
if (__isNonNilObject(readFdArray)) {
if (! __isArrayLike(readFdArray)) {
goto fail;
}
count = __arraySize(readFdArray);
for (i=0; i<count;i++) {
fd = __ArrayInstPtr(readFdArray)->a_element[i];
if (fd != nil) {
if (! __isSmallInteger(fd)) {
if (@global(InfoPrinting) == true) {
fprintf(stderr, "[OS] warning: funny read-fd (0x%x) given to select\n", fd);
}
} else {
f = __intVal(fd);
if ((unsigned)f < FD_SETSIZE) {
FD_SET(f, &rset);
if (f > maxF) maxF = f;
numFds++;
} else {
if (@global(InfoPrinting) == true) {
fprintf(stderr, "[OS] warning: huge read-fd (0x%x) given to select\n", fd);
}
}
}
}
}
}
if (__isNonNilObject(writeFdArray)) {
if (! __isArrayLike(writeFdArray)) {
goto fail;
}
count = __arraySize(writeFdArray);
for (i=0; i<count;i++) {
fd = __ArrayInstPtr(writeFdArray)->a_element[i];
if (fd != nil) {
if (! __isSmallInteger(fd)) {
if (@global(InfoPrinting) == true) {
fprintf(stderr, "[OS] warning: funny write-fd (0x%x) given to select\n", fd);
}
} else {
f = __intVal(fd);
if ((unsigned)f < FD_SETSIZE) {
FD_SET(f, &wset);
if (f > maxF) maxF = f;
numFds++;
} else {
if (@global(InfoPrinting) == true) {
fprintf(stderr, "[OS] warning: huge write-fd (0x%x) given to select\n", fd);
}
}
}
}
}
}
if (__isNonNilObject(exceptFdArray)) {
if (! __isArrayLike(exceptFdArray)) {
goto fail;
}
count = __arraySize(exceptFdArray);
for (i=0; i<count;i++) {
fd = __ArrayInstPtr(exceptFdArray)->a_element[i];
if (fd != nil) {
if (! __isSmallInteger(fd)) {
if (@global(InfoPrinting) == true) {
fprintf(stderr, "[OS] warning: funny except-fd (0x%x) given to select\n", fd);
}
} else {
f = __intVal(fd);
if ((unsigned)f < FD_SETSIZE) {
FD_SET(f, &eset);
if (f > maxF) maxF = f;
numFds++;
} else {
if (@global(InfoPrinting) == true) {
fprintf(stderr, "[OS] warning: huge except-fd (0x%x) given to select\n", fd);
}
}
}
}
}
}
t = __intVal(millis);
if (t) {
wt.tv_sec = t / 1000;
wt.tv_usec = (t % 1000) * 1000;
} else {
wt.tv_sec = wt.tv_usec = 0;
}
/*
* make certain, that interrupt gets us out of the select
* However, we must then care for moved objects.
*/
__BEGIN_INTERRUPTABLE__
errno = 0;
if (t == 0) {
/*
* if there is no timeout time, we can stay here interruptable.
*/
do {
ret = select(maxF+1, &rset, &wset, &eset, &wt);
} while ((ret < 0) && (errno == EINTR));
} else {
do {
ret = select(maxF+1, &rset, &wset, &eset, &wt);
/*
* for now: dont loop; if we did, we had to adjust the vt-timeval;
* could otherwise stay in this loop forever ...
* Premature ret (before the time expired) must be handled by the caller.
* A good solution is to update the wt-timeval and redo the select.
*/
} while (0 /* (ret < 0) && (errno == EINTR) */ );
}
__END_INTERRUPTABLE__
if (ret > 0) {
for (i=0; i <= maxF; i++) {
if (FD_ISSET(i, &rset)
|| FD_ISSET(i, &wset)
|| FD_ISSET(i, &eset)) {
RETURN ( __MKSMALLINT(i) );
}
}
} else {
if (ret < 0) {
if (errno == EINTR) {
errno = 0;
@global(LastErrorNumber) = nil;
} else {
if (@global(InfoPrinting) == true) {
fprintf(stderr, "OS [info]: select errno = %d\n", errno);
}
@global(LastErrorNumber) = __MKSMALLINT(errno);
}
} else {
@global(LastErrorNumber) = nil;
}
}
/*
* Return nil (means time expired or interrupted)
*/
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."
%{ /* NOCONTEXT */
int ret, flags;
#if defined(F_GETFL) && defined(F_SETFL)
# if defined(FNDELAY)
if (__isSmallInteger(fd)) {
int f = __intVal(fd);
flags = fcntl(f, F_GETFL, 0);
if (aBoolean == true) {
ret = fcntl(f, F_SETFL, flags & ~FNDELAY);
} else {
ret = fcntl(f, F_SETFL, flags | FNDELAY);
}
if (ret >= 0) ret = flags;
RETURN ( __MKSMALLINT(ret) );
}
# endif
#endif
%}.
"
fd argument not integer
"
^ self primitiveFailed
! !
!UnixOperatingSystem::FileDescriptorHandle class methodsFor:'change & update'!
update:aspect with:argument from:anObject
"one of our registered handles has been collected"
aspect == #ElementExpired ifTrue:[
OpenFiles keysAndValuesDo:[:fd :handle |
handle == 0 ifTrue:[
"Have to close the file descriptor"
OperatingSystem closeFd:fd.
OpenFiles at:fd put:nil.
].
].
].
"Created: 30.9.1997 / 12:57:35 / stefan"
! !
!UnixOperatingSystem::FileDescriptorHandle class methodsFor:'initialization'!
initialize
OpenFiles := WeakArray new:10.
OpenFiles addDependent:self.
"self initialize"
"Created: 26.9.1997 / 17:15:50 / stefan"
"Modified: 30.9.1997 / 12:40:55 / stefan"
! !
!UnixOperatingSystem::FileDescriptorHandle class methodsFor:'instance creation'!
for:aFileDescriptor
"create an instance of myself for a given fileDescriptor"
^ self new for:aFileDescriptor.
"Created: 30.9.1997 / 14:00:00 / stefan"
! !
!UnixOperatingSystem::FileDescriptorHandle methodsFor:'error handling'!
error:anError
"got an error with anInteger as error number"
anError isInteger ifTrue:[
^ UnixOperatingSystem errorHolderForNumber:anError
].
self primitiveFailed:anError.
! !
!UnixOperatingSystem::FileDescriptorHandle methodsFor:'file access'!
close
"close the descriptor"
|desc|
desc := fd.
self invalidate.
^ OperatingSystem closeFd:desc.
"Modified: 30.9.1997 / 13:06:55 / stefan"
! !
!UnixOperatingSystem::FileDescriptorHandle methodsFor:'initialization'!
for:aFileDescriptor
"create file for a handle"
|oldHandle|
fd := aFileDescriptor.
oldHandle := OpenFiles at:aFileDescriptor.
(oldHandle notNil and:[oldHandle ~~ self]) ifTrue:[
oldHandle invalidate.
].
self register.
"Created: 26.9.1997 / 17:14:40 / stefan"
"Modified: 30.9.1997 / 12:41:43 / stefan"
! !
!UnixOperatingSystem::FileDescriptorHandle methodsFor:'input/output'!
readBytes:count into:aByteBuffer startingAt:firstIndex
"read count bytes into a byte-buffer;
Return the number of bytes read.
The read is non-blocking. If the operation would block
either an incomplete count or nil will be returned.
An exception is raised on any other error"
|error|
%{
unsigned char *extPtr;
int nRead = -1;
INT fd;
INT cnt, offs;
int nInstBytes, objSize;
if (! __isSmallInteger(__INST(fd))) {
error = @symbol(errorNotOpen);
goto bad;
}
if (! __bothSmallInteger(count, firstIndex)) {
error = @symbol(badArgument);
goto bad;
}
fd = __smallIntegerVal(__INST(fd));
cnt = __smallIntegerVal(count);
offs = __smallIntegerVal(firstIndex) - 1;
if (fd < 0) {
error = @symbol(internalError);
goto bad;
}
if (__isExternalBytes(aByteBuffer)) {
OBJ sz;
nInstBytes = 0;
extPtr = (char *)(__externalBytesAddress(aByteBuffer));
sz = __externalBytesSize(aByteBuffer);
if (__isSmallInteger(sz)) {
objSize = __smallIntegerVal(sz);
} else {
objSize = -1; /* unknown */
}
} else {
OBJ oClass;
int nInstVars;
oClass = __Class(aByteBuffer);
switch (__smallIntegerVal(__ClassInstPtr(oClass)->c_flags) & ARRAYMASK) {
case BYTEARRAY:
case WORDARRAY:
case LONGARRAY:
case SWORDARRAY:
case SLONGARRAY:
case FLOATARRAY:
case DOUBLEARRAY:
break;
default:
error = @symbol(badArgument1);
goto bad;
}
extPtr = (char *)0;
nInstVars = __smallIntegerVal(__ClassInstPtr(oClass)->c_ninstvars);
nInstBytes = __OBJS2BYTES__(nInstVars);
objSize = __Size(aByteBuffer) - OHDR_SIZE - nInstBytes;
}
if ((offs >= 0)
&& (cnt >= 0)
&& ((objSize == -1) || (objSize >= (cnt + offs)))) {
nRead = 0;
do {
int n;
if (extPtr) {
n = read(fd, extPtr+offs, cnt);
} else {
char *bp;
/*
* on interrupt, anObject may be moved to another location.
* So we recompute the byte-address here.
*/
bp = __ByteArrayInstPtr(aByteBuffer)->ba_element + nInstBytes;
n = read(fd, bp + offs, cnt);
}
if (n > 0) {
cnt -= n;
offs += n;
nRead += n;
} else if (n == 0) {
break;
} else if (n < 0) {
if (0
#ifdef EWOULDBLOCK
|| errno == EWOULDBLOCK
#endif
#ifdef EAGAIN
|| errno == EAGAIN
#endif
) {
RETURN(nil);
}
if (errno != EINTR) {
error = __mkSmallInteger(errno);
goto bad;
}
__HANDLE_INTERRUPTS__;
}
} while (cnt > 0);
RETURN (__mkSmallInteger(nRead));
}
bad: ;
%}.
^ self error:error.
"
|h buff n|
h := OperatingSystem openFileForRead:'/etc/hosts'.
buff := ByteArray new:1000. buff inspect.
n := h readBytes:1000 into:buff startingAt:1.
Transcript show:n; space; showCR:buff asString.
"
"
|h buff n|
h := OperatingSystem openFileForRead:'/dev/cua0'.
buff := ByteArray new:1000. buff inspect.
n := h readBytes:1000 into:buff startingAt:1.
Transcript show:n printString; space; showCR:buff asString.
"
!
writeBytes:count from:aByteBuffer startingAt:firstIndex
"write count bytes from a byte-buffer;
Return the number of bytes written (negative on error)"
|error|
%{
unsigned char *extPtr;
int nWritten = -1;
INT fd;
INT cnt, offs;
int nInstBytes, objSize;
if (! __isSmallInteger(__INST(fd))) {
error = @symbol(errorNotOpen);
goto bad;
}
if (! __bothSmallInteger(count, firstIndex)) {
error = @symbol(badArgument);
goto bad;
}
fd = __smallIntegerVal(__INST(fd));
cnt = __smallIntegerVal(count);
offs = __smallIntegerVal(firstIndex) - 1;
if (fd < 0) {
error = @symbol(internalError);
goto bad;
}
if (__isExternalBytes(aByteBuffer)) {
OBJ sz;
nInstBytes = 0;
extPtr = (char *)(__externalBytesAddress(aByteBuffer));
sz = __externalBytesSize(aByteBuffer);
if (__isSmallInteger(sz)) {
objSize = __smallIntegerVal(sz);
} else {
objSize = -1; /* unknown */
}
} else {
OBJ oClass;
int nInstVars;
oClass = __Class(aByteBuffer);
switch (__smallIntegerVal(__ClassInstPtr(oClass)->c_flags) & ARRAYMASK) {
case BYTEARRAY:
case WORDARRAY:
case LONGARRAY:
case SWORDARRAY:
case SLONGARRAY:
case FLOATARRAY:
case DOUBLEARRAY:
break;
default:
error = @symbol(badArgument1);
goto bad;
}
extPtr = (char *)0;
nInstVars = __smallIntegerVal(__ClassInstPtr(oClass)->c_ninstvars);
nInstBytes = __OBJS2BYTES__(nInstVars);
objSize = __Size(aByteBuffer) - OHDR_SIZE - nInstBytes;
}
if ((offs >= 0)
&& (cnt >= 0)
&& ((objSize == -1) || (objSize >= (cnt + offs)))) {
nWritten = 0;
do {
int n;
if (extPtr) {
n = write(fd, extPtr+offs, cnt);
} else {
char *bp;
/*
* on interrupt, anObject may be moved to another location.
* So we recompute the byte-address here.
*/
bp = __ByteArrayInstPtr(aByteBuffer)->ba_element + nInstBytes;
n = write(fd, bp + offs, cnt);
}
if (n > 0) {
cnt -= n;
offs += n;
nWritten += n;
} else if (n == 0) {
break;
} else if (n < 0) {
if (0
#ifdef EWOULDBLOCK
|| errno == EWOULDBLOCK
#endif
#ifdef EAGAIN
|| errno == EAGAIN
#endif
) {
RETURN(nil);
}
if (errno != EINTR) {
error = __mkSmallInteger(errno);
goto bad;
}
__HANDLE_INTERRUPTS__;
}
} while (cnt > 0);
RETURN (__mkSmallInteger(nWritten));
}
bad: ;
%}.
^ self error:error
"
|h buff n|
h := OperatingSystem createFileForReadWrite:'/tmp/xx'.
buff := '12345678901234567890'.
n := h writeBytes:10 from:buff startingAt:1.
"
"
|h buff n|
h := OperatingSystem createFileForReadWrite:'/dev/cua0'.
buff := '12345678901234567890'.
n := h writeBytes:10 from:buff startingAt:1.
"
! !
!UnixOperatingSystem::FileDescriptorHandle methodsFor:'misc functions'!
seekTo:newPosition from:whence
"seek to newPosition
whence is one of: #begin #current #end.
Return the new position.
TODO: 64 bit handling"
|error|
%{
INT fd, pos, ret;
INT __whence;
__uint64__ pos64;
if (! __isSmallInteger(__INST(fd))) {
error = @symbol(errorNotOpen);
goto bad;
}
if (__isSmallInteger(newPosition)) {
pos = __smallIntegerVal(newPosition);
} else if (__signedLong64IntVal(newPosition, &pos64) == 0) {
error = @symbol(badArgument1);
goto bad;
}
fd = __smallIntegerVal(__INST(fd));
if (fd < 0) {
error = @symbol(internalError);
goto bad;
}
if (whence == @symbol(begin)) {
__whence = SEEK_SET;
} else if (whence == @symbol(current)) {
__whence = SEEK_CUR;
} else if (whence == @symbol(end)) {
__whence = SEEK_END;
} else {
error = @symbol(badArgument2);
goto bad;
}
again:
ret = lseek(fd, pos, __whence);
if (ret < 0) {
if (errno == EINTR) {
__HANDLE_INTERRUPTS__;
goto again;
}
error = __mkSmallInteger(errno);
goto bad;
}
/* RETURN (__mkInteger(ret)); */
RETURN (__mkSmallInteger(ret));
bad: ;
%}.
^ self error:error.
"
|h buff n|
h := OperatingSystem openFileForRead:'/etc/hosts'.
h seekTo:10 from:#begin.
buff := ByteArray new:1000. buff inspect.
n := h readBytes:1000 into:buff startingAt:1.
Transcript show:n; space; showCR:buff asString.
"
!
selectWithTimeOut:millis
"wait for aFileDesriptor to become ready; timeout after t milliseconds.
Return true, if i/o ok, false if timed-out or interrupted.
With 0 as timeout argument, this can be used to check for availability
of read-data.
Experimental."
^ OperatingSystem selectOnAnyReadable:(Array with:fd)
writable:(Array with:fd)
exception:nil
withTimeOut:millis
"Created: 1.10.1997 / 08:51:11 / stefan"
!
setBlocking:aBoolean
"{ 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 if no data is available."
|err|
%{
int ret, flags;
#if defined(F_GETFL) && defined(F_SETFL)
# if defined(O_NDELAY)
# define DELAY O_NDELAY
# else
# if defined(FNDELAY)
# define DELAY FNDELAY
# endif
# endif
# if defined(DELAY)
if (__isSmallInteger(__INST(fd))) {
int f = __intVal(__INST(fd));
flags = fcntl(f, F_GETFL, 0);
if (aBoolean == true) {
ret = fcntl(f, F_SETFL, flags & ~DELAY);
} else {
ret = fcntl(f, F_SETFL, flags | DELAY);
}
if (ret >= 0) {
RETURN(__MKSMALLINT(flags));
} else {
err = __MKSMALLINT(errno);
}
}
# undef DELAY
# endif /* DELAY */
#endif
%}.
err notNil ifTrue:[
self error:err
].
"
fd argument not integer
"
^ self primitiveFailed
! !
!UnixOperatingSystem::FileDescriptorHandle methodsFor:'private accessing'!
setFileDescriptor:anInteger
%{
if (__isSmallInteger(anInteger)) {
__externalAddressVal(self) = (OBJ)(__smallIntegerVal(anInteger));
}
%}
! !
!UnixOperatingSystem::FileDescriptorHandle methodsFor:'queries'!
canReadWithoutBlocking
"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."
%{
int fd;
if (__isSmallInteger(__INST(fd))) {
fd = __smallIntegerVal(__INST(fd));
/*
* if available, try FIONREAD first, which is usually done faster.
*/
# ifdef FIONREAD
{
int result = 0;
if (ioctl(fd, FIONREAD, &result) >= 0) {
RETURN(result > 0 ? true : false);
}
}
# endif /* FIONREAD */
# if defined(__VMS__) && defined(DOES_NOT_WORK_YET)
{
/*
* do a sys$qio ..
* fd here is suposed to be a channel nr.
*/
struct IOSB iosb;
int status;
int channel;
struct typahdask sensebuf;
status = SYS$QIO(0, /* efn */
fd,
IO$_SENSEMODE | IO$M_TYPEAHDCNT,
&iosb,
0, /* ast */
0, /* ast arg */
&sensebuf, /* data */
sizeof(sensebuf), /* data size */
0, 0, 0, 0);
if (status != SS$_NORMAL) {
fprintf(stderr, "OS [info]: SYS$QIO failed on %d\n", fd);
} else {
fprintf(stderr, "sys$QIO -> %d\n", sensebuf.typcnt);
}
}
# endif /* __VMS__ */
}
%}.
OperatingSystem supportsSelect ifFalse:[
"/ mhmh - what should we do then ?
"/ For now, return true as if data was present,
"/ and let the thread fall into the read.
"/ It will then (hopefully) be desceduled there and
"/ effectively polling for input.
^ true
].
^ (OperatingSystem selectOnAnyReadable:(Array with:fd)
writable:nil
exception:nil
withTimeOut:0) == fd
"
|h n|
h := OperatingSystem openFileForRead:'/etc/hosts'.
n := h canReadWithoutBlocking.
h close.
n
"
"
|h n|
h := OperatingSystem openFileForRead:'/dev/ttyS0'.
n := h canReadWithoutBlocking.
h close.
n
"
!
canWriteWithoutBlocking
"return true, if filedescriptor can be written without blocking"
OperatingSystem supportsSelect ifFalse:[
"/ mhmh - what should we do then ?
"/ For now, return true as if data was present,
"/ and let the thread fall into the write.
"/ It will then (hopefully) be desceduled there and
"/ effectively polling for output.
^ true
].
^ (OperatingSystem selectOnAnyReadable:nil
writable:(Array with:fd)
exception:nil
withTimeOut:0) == fd
!
isValid
"answer true, if the handle is valid, i.e. connected to
a file or some other OS object"
^ fd notNil
!
numAvailableForRead
"return the number of bytes available for reading, without blocking."
%{
/*
* if available, try FIONREAD first, which is usually done faster.
*/
# if defined(FIONREAD)
{
int n = 0;
if (__isSmallInteger(__INST(fd))) {
if (ioctl(__smallIntegerVal(__INST(fd)), FIONREAD, &n) >= 0) {
RETURN (__MKINT(n));
}
}
}
# endif /* FIONREAD */
%}.
^ self canReadWithoutBlocking ifTrue:[1] ifFalse:[0]
"
|h n|
h := OperatingSystem openFileForRead:'/etc/hosts'.
n := h numAvailableForRead.
h close.
n
"
! !
!UnixOperatingSystem::FileDescriptorHandle methodsFor:'registering'!
register
"register myself as an open file"
|sz old|
sz := OpenFiles size.
fd > sz ifTrue:[
"grow for more descriptors"
old := OpenFiles.
OpenFiles := WeakArray new:(sz * 2).
old removeDependent:(self class).
OpenFiles addDependent:(self class).
old keysAndValuesDo:[:index :elem|
"be careful to not overwrite new entries in OpenFiles"
elem notNil ifTrue:[
OpenFiles at:index put:elem.
].
].
].
OpenFiles at:fd put:self.
"Created: 30.9.1997 / 12:51:48 / stefan"
"Modified: 30.9.1997 / 12:58:37 / stefan"
! !
!UnixOperatingSystem::FileDescriptorHandle methodsFor:'releasing'!
invalidate
"a file handle has become invalid"
OpenFiles at:fd put:nil.
fd := nil.
"Created: 30.9.1997 / 12:37:26 / stefan"
"Modified: 30.9.1997 / 12:42:16 / stefan"
! !
!UnixOperatingSystem::FileDescriptorHandle methodsFor:'waiting'!
readWaitWithTimeoutMs:timeout
"suspend the current process, until the receiver
becomes ready for reading or a timeout (in milliseconds) expired.
If data is already available, return immediate.
Return true if a timeout occured (i.e. false, if data is available).
The other threads are not affected by the wait."
|inputSema hasData wasBlocked|
fd isNil ifTrue:[^ self error:#errorNotOpen].
wasBlocked := OperatingSystem blockInterrupts.
hasData := self canReadWithoutBlocking.
hasData ifFalse:[
inputSema := Semaphore new name:'readWait'.
[
timeout notNil ifTrue:[
Processor signal:inputSema afterMilliseconds:timeout
].
Processor signal:inputSema onInput:fd.
Processor activeProcess state:#ioWait.
inputSema wait.
Processor disableSemaphore:inputSema.
hasData := self canReadWithoutBlocking.
] ifCurtailed:[
Processor disableSemaphore:inputSema.
wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
]
].
wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
^ hasData not
!
writeWaitWithTimeoutMs:timeout
"suspend the current process, until the receiver
becomes ready for writing or a timeout (in seconds) expired.
Return true if a timeout occured (i.e. false, if data is available).
Return immediate if the receiver is already ready.
The other threads are not affected by the wait."
|outputSema canWrite wasBlocked|
fd isNil ifTrue:[^ self error:#errorNotOpen].
wasBlocked := OperatingSystem blockInterrupts.
canWrite := self canWriteWithoutBlocking.
canWrite ifFalse:[
outputSema := Semaphore new name:'writeWait'.
[
timeout notNil ifTrue:[
Processor signal:outputSema afterMilliseconds:timeout
].
Processor signal:outputSema onOutput:fd.
Processor activeProcess state:#ioWait.
outputSema wait.
Processor disableSemaphore:outputSema.
canWrite := self canWriteWithoutBlocking.
] ifCurtailed:[
Processor disableSemaphore:outputSema.
wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
]
].
wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
^ canWrite not
! !
!UnixOperatingSystem::FilePointerHandle class methodsFor:'documentation'!
version
^ '$Header: /cvs/stx/stx/libbasic/UnixOperatingSystem.st,v 1.110 2002-01-09 09:02:23 cg Exp $'
! !
!UnixOperatingSystem::FilePointerHandle methodsFor:'release'!
closeFile
"close the underlying file"
%{
FILE *f = (FILE *)(__externalAddressVal(self));
if (f) {
__externalAddressVal(self) = NULL;
fclose(f);
}
%}
! !
!UnixOperatingSystem::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
^ self basicNew
type:t mode:m uid:u gid:g size:s id:i accessed:aT modified:mT statusChanged:sT path:lP
! !
!UnixOperatingSystem::FileStatusInfo methodsFor:'accessing'!
accessed
"return accessed"
^ accessed
!
alternativeName
"return the files other name (DOS name on windows).
Nil if there is no other name"
^ nil
!
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
! !
!UnixOperatingSystem::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
! !
!UnixOperatingSystem::FileStatusInfo methodsFor:'private accessing'!
type:t mode:m uid:u gid:g size:s id:i accessed:aT modified:mT statusChanged:sT path:lP
type := t.
mode := m.
uid := u.
gid := g.
size := s.
id := i.
accessed := aT.
modified := mT.
statusChanged := sT.
path := lP.
! !
!UnixOperatingSystem::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
"
! !
!UnixOperatingSystem::OSProcessStatus class methodsFor:'instance creation'!
pid:pid status:status code:code core:core
"private interface for UnixOperatingSystem"
^ 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 UnixOperatingSystem"
^ 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"
! !
!UnixOperatingSystem::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"
! !
!UnixOperatingSystem::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"
! !
!UnixOperatingSystem::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"
! !
!UnixOperatingSystem::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"
! !
!UnixOperatingSystem::SocketHandle class methodsFor:'constants'!
domainCodeOf:aSymbolOrInteger
|domainCode|
%{ /*NOCONTEXT*/
int code = -1;
if (__isSmallInteger(aSymbolOrInteger) || aSymbolOrInteger == nil) {
RETURN(aSymbolOrInteger);
}
#ifdef AF_INET
else if (aSymbolOrInteger == @symbol(inet))
code = AF_INET;
#endif
#ifdef AF_INET6
else if (aSymbolOrInteger == @symbol(inet6))
code = AF_INET6;
#endif
#ifdef AF_UNIX
else if (aSymbolOrInteger == @symbol(unix))
code = AF_UNIX;
#endif
#ifdef AF_APPLETALK
else if (aSymbolOrInteger == @symbol(appletalk))
code = AF_APPLETALK;
#endif
if (code > 0)
domainCode = __MKSMALLINT(code);
%}.
^ domainCode.
"
self domainCodeOf:#inet
self domainCodeOf:#unix
self domainCodeOf:#inet6
self domainCodeOf:#appletalk
"
!
domainSymbolOf:anInteger
|domainSymbol|
%{ /*NOCONTEXT*/
if (__isSmallInteger(anInteger)) {
switch(__intVal(anInteger)) {
#ifdef AF_INET
case AF_INET:
domainSymbol = @symbol(inet);
break;
#endif
#ifdef AF_INET6
case AF_INET6:
domainSymbol = @symbol(inet6);
break;
#endif
#ifdef AF_UNIX
case AF_UNIX:
domainSymbol = @symbol(unix);
break;
#endif
#ifdef AF_APPLETALK
case AF_APPLETALK:
domainSymbol = @symbol(appletalk);
break;
#endif
}
}
%}.
^ domainSymbol.
"
self domainSymbolOf:(self domainCodeOf:#inet)
self domainSymbolOf:(self domainCodeOf:#inet6)
self domainSymbolOf:(self domainCodeOf:#unix)
self domainSymbolOf:(self domainCodeOf:#appletalk)
"
!
protocolCodeOf:aNameOrNumber
"convert a symbol to a numeric protocol code"
|protocolCode protocolSymbol|
%{
if (__isSmallInteger(aNameOrNumber) || aNameOrNumber == nil) {
RETURN(aNameOrNumber);
}
%}.
ProtocolCache notNil ifTrue:[
protocolCode := ProtocolCache at:aNameOrNumber asSymbol
ifAbsent:[].
protocolCode notNil ifTrue:[
^ protocolCode.
].
].
%{
#ifndef NO_SOCKET
struct protoent *protoent = 0;
if (__isString(aNameOrNumber) || __isSymbol(aNameOrNumber)) {
protoent = getprotobyname((char *) __stringVal(aNameOrNumber));
if (protoent) {
protocolCode = __MKSMALLINT(protoent->p_proto);
protocolSymbol = __MKSYMBOL(protoent->p_name, 0);
}
}
#endif /*NO_SOCKET*/
%}.
protocolSymbol notNil ifTrue:[
ProtocolCache isNil ifTrue:[
ProtocolCache := IdentityDictionary new.
].
"beware of polluting the protocol cache with aliases"
ProtocolCache at:protocolSymbol put:protocolCode.
].
^ protocolCode
"
self protocolCodeOf:#tcp
self protocolCodeOf:#udp
self protocolCodeOf:#raw
"
!
protocolSymbolOf:anInteger
"convert a numeric protocol code to a symbol"
|protocolSymbol|
ProtocolCache notNil ifTrue:[
protocolSymbol := ProtocolCache keyAtIdentityValue:anInteger
ifAbsent:[].
protocolSymbol notNil ifTrue:[
^ protocolSymbol.
].
].
%{
#ifndef NO_SOCKET
struct protoent *protoent = 0;
if (__isSmallInteger(anInteger)) {
protoent = getprotobynumber(__intVal(anInteger));
if (protoent) {
protocolSymbol = __MKSYMBOL(protoent->p_name, 0);
}
}
#endif /*NO_SOCKET*/
%}.
protocolSymbol notNil ifTrue:[
ProtocolCache isNil ifTrue:[
ProtocolCache := IdentityDictionary new.
].
ProtocolCache at:protocolSymbol put:anInteger.
].
^ protocolSymbol
"
self protocolSymbolOf:(self protocolCodeOf:#tcp)
self protocolSymbolOf:(self protocolCodeOf:#udp)
self protocolSymbolOf:(self protocolCodeOf:#icmp)
"
!
socketAddressSize:aSymbol
"answer the os specific size of a socket address for domain aSymbol"
|socketSize|
%{ /*NOCONTEXT*/
int size = -1;
#ifdef AF_INET
if (aSymbol == @symbol(inet))
size = sizeof(struct sockaddr_in);
#endif
#ifdef AF_INET6
else if (aSymbol == @symbol(inet6))
size = sizeof(struct sockaddr_in6);
#endif
#ifdef AF_UNIX
else if (aSymbol == @symbol(unix))
size = sizeof(struct sockaddr_un);
#endif
#ifdef AF_APPLETALK
else if (aSymbol == @symbol(appletalk))
size = sizeof(struct sockaddr_at);
#endif
if (size > 0)
socketSize = __MKSMALLINT(size);
%}.
^ socketSize
!
socketTypeCodeOf:aSymbolOrInteger
|typeCode|
%{ /*NOCONTEXT*/
int code = -1;
if (__isSmallInteger(aSymbolOrInteger) || aSymbolOrInteger == nil) {
RETURN(aSymbolOrInteger);
}
#ifdef SOCK_STREAM
else if (aSymbolOrInteger == @symbol(stream))
code = SOCK_STREAM;
#endif
#ifdef SOCK_DGRAM
if (aSymbolOrInteger == @symbol(datagram))
code = SOCK_DGRAM;
#endif
#ifdef SOCK_RAW
if (aSymbolOrInteger == @symbol(raw))
code = SOCK_RAW;
#endif
if (code > 0)
typeCode = __MKSMALLINT(code);
%}.
^ typeCode.
"
self socketTypeCodeOf:#stream
self socketTypeCodeOf:#datagram
self socketTypeCodeOf:#raw
"
!
socketTypeSymbolOf:anInteger
|socketTypeSymbol|
%{ /*NOCONTEXT*/
if (__isSmallInteger(anInteger)) {
switch(__intVal(anInteger)) {
#ifdef SOCK_STREAM
case SOCK_STREAM:
socketTypeSymbol = @symbol(stream);
break;
#endif
#ifdef SOCK_DGRAM
case SOCK_DGRAM:
socketTypeSymbol = @symbol(datagram);
break;
#endif
#ifdef SOCK_RAW
case SOCK_RAW:
socketTypeSymbol = @symbol(raw);
break;
#endif
}
}
%}.
^ socketTypeSymbol.
!
supportedProtocolFamilies
"return a collection of supported protocol families.
This list specifies what the Socket class supports -
socket creation may still fail, if your system was built
without it."
|list|
list := OrderedCollection new.
%{
#ifdef AF_INET
%}.
list add:#inet.
%{
#endif
%}.
%{
#ifdef AF_UNIX
%}.
list add:#unix.
%{
#endif
%}.
%{
#ifdef AF_INET6
%}.
list add:#inet6. "/ internet v6
%{
#endif
%}.
%{
#ifdef AF_APPLETALK
%}.
list add:#appletalk. "/ appletalk
%{
#endif
%}.
%{
#ifdef AF_DECnet
%}.
list add:#decnet. "/ dec net
%{
#endif
%}.
%{
#ifdef AF_NS
%}.
list add:#xns. "/ Xerox XNS
%{
#endif
%}.
%{
#ifdef AF_X25
%}.
list add:#x25. "/ X.25
%{
#endif
%}.
%{
#ifdef AF_SNA
%}.
list add:#sna. "/ IBM SNA
%{
#endif
%}.
%{
#ifdef AF_RAW
%}.
list add:#raw. "/ ?? RAW packets
%{
#endif
%}.
%{
#ifdef AF_ISO
%}.
list add:#iso. "/ ??
%{
#endif
%}.
%{
#ifdef AF_NETBIOS
%}.
list add:#netbios. "/ ??
%{
#endif
%}.
%{
#ifdef AF_IPX
%}.
list add:#ipx. "/ Novell IPX
%{
#endif
%}.
%{
#ifdef AF_AX25
%}.
list add:#ax25. "/ Amateur Radio AX.25
%{
#endif
%}.
%{
#ifdef AF_NETROM
%}.
list add:#netrom. "/ Amateur Radio NET/ROM
%{
#endif
%}.
%{
#ifdef AF_BRIDGE
%}.
list add:#bridge. "/ multiprotocol bridge
%{
#endif
%}.
%{
#ifdef AF_BSC
%}.
list add:#bsc. "/ BISYNC 2780/3780
%{
#endif
%}.
%{
#ifdef AF_ROSE
%}.
list add:#rose. "/ Amateur Radio X.25 PLP
%{
#endif
%}.
%{
#ifdef AF_IRDA
%}.
list add:#irda. "/ infrared
%{
#endif
%}.
^ list
"
self supportedProtocolFamilies
"
! !
!UnixOperatingSystem::SocketHandle class methodsFor:'initialization'!
reinitialize
"clear the protocol cache, when the system has been restarted"
ProtocolCache := nil.
! !
!UnixOperatingSystem::SocketHandle methodsFor:'accepting'!
acceptWithPeerAddressBuffer:peerOrNil
"accept a connection on a server port.
Returns a new SocketHandle or nil if the operation
would block.
If peerOrNil is set to a ByteArray, the socket address
of the connection peer is stored into it."
|error newFd|
%{
#ifndef NO_SOCKET
int sock, newSock;
struct sockaddr *sap;
int alen;
if (!__isSmallInteger(__INST(fd))) {
error = @symbol(badFd);
goto err;
}
if (peerOrNil != nil &&
(!__isNonNilObject(peerOrNil) ||
(__intVal(__ClassInstPtr(__qClass(peerOrNil))->c_flags) & ARRAYMASK) != BYTEARRAY)) {
error = @symbol(badArgument2);
goto err;
}
sock = __smallIntegerVal(__INST(fd));
again:
if (peerOrNil == nil) {
alen = 0;
sap = 0;
} else {
alen = __byteArraySize(peerOrNil);
sap = (struct sockaddr *)__byteArrayVal(peerOrNil);
}
newSock = accept(sock, sap, &alen);
if (newSock < 0) {
switch (errno) {
case EINTR:
__HANDLE_INTERRUPTS__;
goto again;
#ifdef EWOULDBLOCK
case EWOULDBLOCK:
#else
#if defined(EAGAIN)
case EAGAIN:
#endif
#endif
RETURN(nil);
default:
error = __mkSmallInteger(errno);
goto err;
}
}
newFd = __mkSmallInteger(newSock);
err:;
#endif /* not NO_SOCKET */
%}.
error notNil ifTrue:[
self error:error.
] ifFalse:[
^ self class for:newFd
]
! !
!UnixOperatingSystem::SocketHandle methodsFor:'binding'!
bindTo:socketAddress
"low level bind -
Set the local address of the socket"
|error|
%{
#ifndef NO_SOCKET
int sock;
int sockaddr_size;
int ret;
if (!__isSmallInteger(__INST(fd))) {
error = @symbol(badFd);
goto err;
}
if (!__isNonNilObject(socketAddress) ||
(__intVal(__ClassInstPtr(__qClass(socketAddress))->c_flags) & ARRAYMASK) != BYTEARRAY) {
error = @symbol(badArgument1);
goto err;
}
sockaddr_size = __byteArraySize(socketAddress);
sock = __INST(fd);
again:
ret = bind(sock, (struct sockaddr *)__byteArrayVal(socketAddress), sockaddr_size);
if (ret < 0) {
if (errno == EINTR) {
__HANDLE_INTERRUPTS__;
goto again;
} else {
error = __mkSmallInteger(errno);
goto err;
}
}
err:;
#endif /* NO_SOCKET */
%}.
error notNil ifTrue:[
self error:error.
].
"
(Socket domain:#inet type:#stream)
bindTo:(IPSocketAddress hostAddress:IPSocketAddress anyAddress port:9999)
reuseAddress:false ;
yourself
"
! !
!UnixOperatingSystem::SocketHandle methodsFor:'connecting'!
cancelConnect
"cancel an asynchronous connect in progress"
|error|
%{
#ifndef NO_SOCKET
int sock;
int ret;
struct sockaddr sockaddr = { AF_UNSPEC };
if (!__isSmallInteger(__INST(fd))) {
error = @symbol(badFd);
goto err;
}
sock = __smallIntegerVal(__INST(fd));
/*
* (dis-) connect by connecting to AF_UNSPEC socket
*/
again:
ret = connect(sock, &sockaddr, sizeof(sockaddr));
if (ret < 0) {
switch(errno) {
case EINTR:
# ifdef EAGAIN
case EAGAIN:
# endif
__HANDLE_INTERRUPTS__;
goto again;
default:
error = __MKSMALLINT(errno);
break;
}
}
err:;
#endif /* NO_SOCKET */
%}.
!
connectTo:socketAddress
"low level connect; connect to a socket address.
Return true if connection has been established,
false, when the connection has been initiated but not yet
completed. If an error occured, an OSError is raised"
|error|
%{
#ifndef NO_SOCKET
int sock;
int ret;
int sockaddr_size;
if (!__isSmallInteger(__INST(fd))) {
error = @symbol(badFd);
goto err;
}
if (!__isNonNilObject(socketAddress) ||
(__intVal(__ClassInstPtr(__qClass(socketAddress))->c_flags) & ARRAYMASK) != BYTEARRAY) {
error = @symbol(badArgument1);
goto err;
}
sock = __smallIntegerVal(__INST(fd));
sockaddr_size = __qSize(socketAddress);
again:
ret = connect(sock, (struct sockaddr *)__byteArrayVal(socketAddress), sockaddr_size);
if (ret >= 0) {
RETURN(true)
}
switch(errno) {
case EINTR:
# ifdef EAGAIN
case EAGAIN:
# endif
__HANDLE_INTERRUPTS__;
goto again;
#if defined(EINPROGRESS) || defined(EALREADY)
# ifdef EINPROGRESS
case EINPROGRESS:
# endif
# ifdef EALREADY
case EALREADY:
# endif
RETURN(false);
#endif
default:
error = __MKSMALLINT(errno);
break;
}
err:;
#endif /* NO_SOCKET */
%}.
error notNil ifTrue:[
self error:error.
^ self
].
"
Socket newTCP connectTo:(IPSocketAddress hostAddress:IPSocketAddress local port:7)
withTimeout:nil.
Socket newTCP connectTo:(IPSocketAddress hostAddress:IPSocketAddress local port:5768)
withTimeout:nil.
Socket newTCP connectTo:(IPSocketAddress hostAddress:#[1 2 3 4] port:7)
withTimeout:nil.
"
! !
!UnixOperatingSystem::SocketHandle methodsFor:'datagram transmission'!
receiveFrom:socketAddress buffer:aDataBuffer start:startIndex for:nBytes flags:flags
"receive datagramm data - put address of originating host into
anAddressBuffer, data into aBuffer. For ST-80 compatibility,
the addressBuffer may be a non-ByteArray; then, it must understand
the addressBytes-message (i.e. be a SocketAddress instance).
Return the number of bytes received, or a negative number on error.
The thread blocks until data arrives - you may want to wait before
receiving, using #readWait or #readWaitWithTimeout:."
|error|
%{
#ifndef NO_SOCKET
OBJ oClass;
int nInstVars, nInstBytes, objSize;
int sock;
struct sockaddr *saPtr;
int alen0, alen;
int n;
char *cp;
int __flags, __startIndex, __nBytes;
if (!__isSmallInteger(__INST(fd))) {
error = @symbol(badFd);
goto err;
}
if (!__isSmallInteger(startIndex) ||
(__startIndex = __intVal(startIndex)-1) < 0) {
if (startIndex == nil) {
__startIndex = 0;
} else {
error = @symbol(badArgument3);
goto err;
}
}
if (__isSmallInteger(nBytes)) {
__nBytes = __intVal(nBytes);
} else if (nBytes == nil) {
__nBytes = -1;
} else {
error = @symbol(badArgument4);
goto err;
}
if (!__isInteger(flags)) {
error = @symbol(badArgument5);
goto err;
}
__flags = __longIntVal(flags);
sock = __smallIntegerVal(__INST(fd));
oClass = __Class(aDataBuffer);
switch (_intVal(_ClassInstPtr(oClass)->c_flags) & ARRAYMASK) {
case BYTEARRAY:
case WORDARRAY:
case SWORDARRAY:
case LONGARRAY:
case SLONGARRAY:
case FLOATARRAY:
case DOUBLEARRAY:
break;
default:
error = @symbol(badArgument2);
goto err;
}
nInstVars = __intVal(_ClassInstPtr(oClass)->c_ninstvars);
nInstBytes = OHDR_SIZE + nInstVars * sizeof(OBJ);
objSize = __qSize(aDataBuffer) - nInstBytes;
nInstBytes += __startIndex;
objSize -= __startIndex;
if (__nBytes >= 0 &&__nBytes < objSize) {
objSize = __nBytes;
}
if (socketAddress == nil) {
alen0 = 0;
} else {
if (!__isNonNilObject(socketAddress) ||
(__intVal(__ClassInstPtr(__qClass(socketAddress))->c_flags) & ARRAYMASK) != BYTEARRAY) {
error = @symbol(badArgument1);
goto err;
}
alen0 = __byteArraySize(socketAddress);
}
saPtr = (struct sockaddr *)0;
again:
alen = alen0;
if (alen)
saPtr = (struct sockaddr *)__byteArrayVal(socketAddress);
cp = (char *)__InstPtr(aDataBuffer) + nInstBytes;
n = recvfrom(sock, cp, objSize, __flags, saPtr, &alen);
if (n < 0) {
if (errno == EINTR) {
__HANDLE_INTERRUPTS__;
goto again;
} else {
error = __MKSMALLINT(errno);
goto err;
}
}
RETURN (__mkSmallInteger(n));
#endif
err: ;
%}.
self error:error.
^ -1
!
sendTo:socketAddress buffer:aDataBuffer start:startIndex for:maxBytes flags:flags
"send datagramm data - fetch address of destination host from
anAddressBuffer, data from aDataBuffer starting at startIndex,
sending count bytes.
SocketAddress must be an instance of SocketAddress
Return the number of bytes transmitted, or a negative number on error."
|error|
%{
#ifndef NO_SOCKET
OBJ oClass;
int nInstVars, nInstBytes, objSize;
int sock;
int alen, n;
struct sockaddr *saPtr;
char *cp;
int __flags;
int offs, __startIndex, __maxBytes;
if (!__isSmallInteger(__INST(fd))) {
error = @symbol(badFd);
goto err;
}
if (!__isSmallInteger(startIndex) ||
(__startIndex = __intVal(startIndex)-1) < 0) {
if (startIndex == nil) {
__startIndex = 0;
} else {
error = @symbol(badArgument3);
goto err;
}
}
if (__isSmallInteger(maxBytes)) {
__maxBytes = __intVal(maxBytes);
} else if (maxBytes == nil) {
__maxBytes = -1;
} else {
error = @symbol(badArgument4);
goto err;
}
if (!__isInteger(flags)) {
error = @symbol(badArgument5);
goto err;
}
__flags = __longIntVal(flags);
sock = __smallIntegerVal(__INST(fd));
oClass = __Class(aDataBuffer);
switch (_intVal(_ClassInstPtr(oClass)->c_flags) & ARRAYMASK) {
case BYTEARRAY:
offs = __startIndex;
break;
case WORDARRAY:
case SWORDARRAY:
offs = __startIndex * 2;
break;
case LONGARRAY:
case SLONGARRAY:
offs = __startIndex * 4;
break;
case LONGLONGARRAY:
case SLONGLONGARRAY:
offs = __startIndex * 8;
# ifdef __NEED_LONGLONG_ALIGN
offs += 4;
# endif
case FLOATARRAY:
offs = __startIndex * sizeof(float);
break;
case DOUBLEARRAY:
offs = __startIndex * sizeof(double);
# ifdef __NEED_DOUBLE_ALIGN
offs += 4;
# endif
break;
default:
error = @symbol(badArgument2);
goto err;
}
nInstVars = __smallIntegerVal(_ClassInstPtr(oClass)->c_ninstvars);
nInstBytes = OHDR_SIZE + nInstVars * sizeof(OBJ);
objSize = __qSize(aDataBuffer) - nInstBytes - offs;
if (__maxBytes >= 0 && __maxBytes < objSize) {
# ifdef DGRAM_DEBUG
printf("cut off ...\n");
# endif
objSize = __maxBytes;
}
if (socketAddress == nil) {
alen = 0;
} else {
if (! __isByteArray(socketAddress)) {
error = @symbol(badArgument1);
goto err;
}
alen = __byteArraySize(socketAddress);
}
saPtr = (struct sockaddr *)0;
again:
if (alen)
saPtr = (struct sockaddr *)__byteArrayVal(socketAddress);
cp = (char *)__InstPtr(aDataBuffer) + nInstBytes + offs;
n = sendto(sock, cp, objSize, __flags, saPtr, alen);
if (n < 0) {
if (errno == EINTR) {
__HANDLE_INTERRUPTS__;
goto again;
} else {
error = __MKSMALLINT(errno);
goto err;
}
}
RETURN (__mkSmallInteger(n));
#endif
err: ;
%}.
self error:error.
^ -1
! !
!UnixOperatingSystem::SocketHandle methodsFor:'initializing'!
domain:domainArg type:typeArg protocol:protocolArg
"set up socket with domain, type and protocol number.
This is a low level entry; no binding, listening or connect
is done. Both arguments must be symbols from one of
#inet,#unix, #appletalk, #x25 .. and #stream, #datagram, #raw resp."
|error domainCode protocolNumber|
domainCode := self class domainCodeOf:domainArg.
protocolArg notNil ifTrue:[
protocolNumber := self class protocolCodeOf:protocolArg
].
%{
#ifndef NO_SOCKET
int dom, typ, proto = 0, sock, ret;
int on = 1;
if (__INST(fd) != nil) {
error = @symbol(internalError);
goto err;
}
if (! __isSmallInteger(domainCode)) {
error = @symbol(badArgument1);
goto err;
}
if (! __isSymbol(typeArg)) {
error = @symbol(badArgument2);
goto err;
}
if (protocolNumber != nil) {
if (!__isSmallInteger(protocolNumber)) {
error = @symbol(badArgument3);
goto err;
}
proto = __smallIntegerVal(protocolNumber);
}
dom = __smallIntegerVal(domainCode);
/*
* get socket-type and protocol-type
*/
#ifdef SOCK_STREAM
if (typeArg == @symbol(stream)) {
typ = SOCK_STREAM;
} else
#endif
#ifdef SOCK_DGRAM
if (typeArg == @symbol(datagram)) {
typ = SOCK_DGRAM;
} else
#endif
#ifdef SOCK_RAW
if (typeArg == @symbol(raw))
typ = SOCK_RAW;
else
#endif
#ifdef SOCK_SEQPACKET
if (typeArg == @symbol(seqPacket))
typ = SOCK_SEQPACKET;
else
#endif
{
error = @symbol(badArgument2);
goto err;
}
againSocket:
sock = socket(dom, typ, proto);
if (sock < 0) {
if (errno == EINTR) {
__HANDLE_INTERRUPTS__;
goto againSocket;
} else
#if defined(EPROTONOSUPPORT) /* for SGI */
if (errno == EPROTONOSUPPORT && proto != 0) {
proto = 0;
goto againSocket;
} else
#endif
{
error = __mkSmallInteger(errno);
goto err;
}
}
__INST(fd) = __mkSmallInteger(sock);
err:;
#else
error := @symbol(notImplemented);
#endif
%}.
error notNil ifTrue:[
^ self error:error.
].
self register.
"
self new domain:#inet type:#stream protocol:nil
"
! !
!UnixOperatingSystem::SocketHandle methodsFor:'misc'!
getOptionsLevel:level name:name
"answer a ByteArray containing the socket option value
named name at level"
|error bytes size|
bytes := ByteArray new:256.
%{
#ifndef NO_SOCKET
int sock;
int intval, sz;
char *p;
if (!__isSmallInteger(__INST(fd))) {
error = @symbol(badFd);
goto err;
}
if (!__bothSmallInteger(level, name)) {
error = @symbol(badArgument);
goto err;
}
if (!__isByteArray(bytes)) {
error = @symbol(internalError);
goto err;
}
p = __byteArrayVal(bytes);
sz = __byteArraySize(bytes);
sock = __smallIntegerVal(__INST(fd));
if (getsockopt(sock, __smallIntegerVal(level), __smallIntegerVal(name), p, &sz) < 0) {
error = __mkSmallInteger(errno);
}
size = __mkSmallInteger(sz);
err:;
#endif
%}.
error notNil ifTrue:[
^ self error:error
].
^ bytes copyTo:size
!
listenFor:aNumber
"start listening.
aNumber is the number of connect indications queues
by the operating system"
|error|
%{
#ifndef NO_SOCKET
int sock, ret;
if (!__isSmallInteger(__INST(fd))) {
error = @symbol(badFd);
goto err;
}
if (!__isSmallInteger(aNumber)) {
error = @symbol(badArgument1);
goto err;
}
sock = __smallIntegerVal(__INST(fd));
again:
ret = listen(sock, __smallIntegerVal(aNumber));
if (ret < 0) {
if (errno == EINTR) {
__HANDLE_INTERRUPTS__;
goto again;
} else {
error = __mkSmallInteger(errno);
}
}
err:;
#endif
%}.
error notNil ifTrue:[
self error:error.
].
!
setOptionsLevel:level name:name value:value
"set the socket option name at level to value.
Value ay be one of SmallInteger, ByteArray or nil"
|error|
%{
#ifndef NO_SOCKET
int sock;
int intval, sz;
char *p;
if (!__isSmallInteger(__INST(fd))) {
error = @symbol(badFd);
goto err;
}
if (!__bothSmallInteger(level, name)) {
error = @symbol(badArgument);
goto err;
}
if (__isSmallInteger(value)) {
intval = __intVal(value);
p = (char *) &intval;
sz = sizeof(intval);
} else if (__isByteArray(value)) {
p = __byteArrayVal(value);
sz = __byteArraySize(value);
} else {
error = @symbol(badArgument3);
goto err;
}
sock = __smallIntegerVal(__INST(fd));
if (setsockopt(sock, __smallIntegerVal(level), __smallIntegerVal(name), p, sz) < 0) {
error = __mkSmallInteger(errno);
}
err:;
#endif
%}.
error notNil ifTrue:[
^ self error:error
]
!
shutdown:anInteger
"inform the socket that no more I/O will happen.
anInteger == 0 no reads will be performed
anInteger == 1 no writes will be performed
anInteger == 2 neither reads nor writes will be performed.
Pending data is discarded. This is faster tha
close, which may wait until pending (written)
data has been read by the other side"
|error|
%{
#ifndef NO_SOCKET
int ret;
if (!__isSmallInteger(__INST(fd))) {
error = @symbol(badFd);
goto err;
}
if (!__isSmallInteger(anInteger)) {
error = @symbol(badArgument1);
goto err;
}
again:
ret = shutdown(__smallIntegerVal(__INST(fd)), __smallIntegerVal(anInteger));
if (ret < 0) {
if (errno == EINTR) {
__HANDLE_INTERRUPTS__;
goto again;
} else {
error = __mkSmallInteger(errno);
}
}
err:;
#endif /*NO_SOCKET*/
%}.
error notNil ifTrue:[
self error:error
]
! !
!UnixOperatingSystem::SocketHandle methodsFor:'queries'!
getNameInto:socketAddress
"answer the my own address (I am bound to this address).
Note that this address may change after connect or accept."
|error|
%{
#ifndef NO_SOCKET
int sock;
int sockaddr_size;
int ret;
if (!__isSmallInteger(__INST(fd))) {
error = @symbol(badFd);
goto err;
}
if (!__isNonNilObject(socketAddress) ||
(__intVal(__ClassInstPtr(__qClass(socketAddress))->c_flags) & ARRAYMASK) != BYTEARRAY) {
error = @symbol(badArgument1);
goto err;
}
sockaddr_size = __byteArraySize(socketAddress);
sock = __smallIntegerVal(__INST(fd));
ret = getsockname(sock, (struct sockaddr *)__byteArrayVal(socketAddress), &sockaddr_size);
if (ret < 0) {
error = __MKSMALLINT(errno);
}
err:;
#endif /* NO_SOCKET */
%}.
error notNil ifTrue:[
^ self error:error
].
^ socketAddress
!
getPeerInto:socketAddress
"answer the my own address (I am bound to this address).
Note that this address may change after connect or accept."
|error|
%{
#ifndef NO_SOCKET
int sock;
int sockaddr_size;
int ret;
if (!__isSmallInteger(__INST(fd))) {
error = @symbol(badFd);
goto err;
}
if (!__isNonNilObject(socketAddress) ||
(__intVal(__ClassInstPtr(__qClass(socketAddress))->c_flags) & ARRAYMASK) != BYTEARRAY) {
error = @symbol(badArgument1);
goto err;
}
sockaddr_size = __byteArraySize(socketAddress);
sock = __smallIntegerVal(__INST(fd));
ret = getpeername(sock, (struct sockaddr *)__byteArrayVal(socketAddress), &sockaddr_size);
if (ret < 0) {
error = __mkSmallInteger(errno);
}
err:;
#endif /* NO_SOCKET */
%}.
error notNil ifTrue:[
^ self error:error
].
^ socketAddress
! !
!UnixOperatingSystem class methodsFor:'documentation'!
version
^ '$Header: /cvs/stx/stx/libbasic/UnixOperatingSystem.st,v 1.110 2002-01-09 09:02:23 cg Exp $'
! !
UnixOperatingSystem initialize!