"
COPYRIGHT (c) 1988 by Claus Gittinger
All Rights Reserved
This software is furnished under a license and may be used
only in accordance with the terms of that license and with the
inclusion of the above copyright notice. This software may not
be provided or otherwise made available to, or used by, any
other person. No title to or ownership of the software is
hereby transferred.
"
Object subclass:#OperatingSystem
instanceVariableNames:''
classVariableNames:'HostName LastErrorNumber LastExecStatus OSSignals
SlowFork ForkFailed'
poolDictionaries:''
category:'System-Support'
!
!OperatingSystem primitiveDefinitions!
%{
#if defined(_AIX)
# ifndef WANT_REALPATH
# define WANT_REALPATH
# endif
# ifndef WANT_SYSTEM
# define WANT_SYSTEM
# endif
#endif
#ifdef LINUX
# ifndef WANT_SYSTEM
# define WANT_SYSTEM
# endif
# define WANT_SHM
#endif
#ifdef IRIX5
# define WANT_SYSTEM
#endif
#ifdef ultrix
# define WANT_SYSTEM
#endif
#ifdef hpux
# define WANT_SYSTEM
#endif
#ifdef unixware
# define WANT_SYSTEM
#endif
#ifdef WANT_REALPATH
# include <sys/param.h>
# define _SYS_PARAM_H_INCLUDED_
# include <errno.h>
# define _ERRNO_H_INCLUDED_
# include <sys/stat.h>
# define _SYS_STAT_H_INCLUDED_
#endif /* WANT_REALPATH */
#ifdef WANT_SHM
extern int shmctl(), shmget(), shmdt();
extern char * shmat();
# include <sys/types.h>
# define _SYS_TYPES_H_INCLUDED_
# include <sys/ipc.h>
# define _SYS_IPC_H_INCLUDED_
# include <sys/shm.h>
# define _SYS_SHM_H_INCLUDED_
#endif /* WANT_SHM */
#ifdef IRIX5
# include <sys/syssgi.h>
#endif
#ifdef transputer
# define unlink(f) ((remove(f) == 0) ? 0 : -1)
#else
# include <signal.h>
# ifdef SYSV
# include <sys/types.h>
# include <sys/param.h>
# include <sys/times.h>
# include <sys/file.h>
# if ! defined(sco3_2)
# include <unistd.h>
# endif
# if defined(isc3_2) || defined(sco3_2)
# include <sys/time.h>
# endif
# if !defined(isc3_2)
# if defined(PCS) && defined(mips)
# include "/usr/include/bsd/sys/time.h"
# include "/usr/include/sys/time.h"
# else
# include <time.h>
# endif
# endif
# if defined(isc3_2)
# include <sys/bsdtypes.h>
# endif
# else /* not SYSV */
# include <sys/time.h>
# include <sys/types.h>
# endif
# ifdef aix
# include <time.h>
# include <sys/select.h>
# endif
# include <pwd.h>
# include <grp.h>
# include <sys/stat.h>
# include <errno.h>
# define _ERRNO_H_INCLUDED_
# include <stdio.h>
# define _STDIO_H_INCLUDED_
# include <fcntl.h>
/*
* 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
# include <sys/param.h>
# ifndef MAXPATHLEN
# define MAXPATHLEN 1024
# endif
# endif
# if defined(HAS_UNAME)
# include <sys/utsname.h>
# endif
#endif /* ! transputer */
/*
* on some systems errno is a macro ... check for it here
*/
#ifndef errno
extern errno;
#endif
/*
* some (old ?) systems do not define this ...
*/
#ifndef R_OK
# 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 UNIX_LIKE
#if defined(NT) || defined(MSWINDOWS) || defined(OS2) || defined(MSDOS)
# define MSDOS_LIKE
# undef UNIX_LIKE
#endif
#if defined(transputer)
# undef UNIX_LIKE
#endif
%}
! !
!OperatingSystem primitiveFunctions!
%{
/*
* some systems' system() is broken in that it does not correctly
* handle EINTR and returns failure even though it actually succeeded.
* (LINUX is one of them)
* Here is a fixed version. If you encounter EINTR returns from
* OperatingSystem>>executeCommand, you ought to define WANT_SYSTEM
* in the xxxIntern.h file to get this fixed version.
*
* As an added BONUS, this system() enables interrupts while waiting
* for the child which enables other threads to continue.
* (i.e. it is RT safe)
*/
#if defined(WANT_SYSTEM)
/* # define DPRINTF(x) printf x */
# define DPRINTF(x) /* nothing */
# ifndef _STDDEF_H_INCLUDED_
# include <stddef.h>
# define _STDDEF_H_INCLUDED_
# endif
# ifndef _STDLIB_H_INCLUDED_
# include <stdlib.h>
# define _STDLIB_H_INCLUDED_
# endif
# ifndef _UNISTD_H_INCLUDED_
# include <unistd.h>
# define _UNISTD_H_INCLUDED_
# endif
# ifndef _SYS_WAIT_H_INCLUDED
# include <sys/wait.h>
# define _SYS_WAIT_H_INCLUDED
# endif
# ifndef _SIGNAL_H_INCLUDED_
# include <signal.h>
# define _SIGNAL_H_INCLUDED_
# endif
# ifndef _SYS_TYPES_H_INCLUDED_
# include <sys/types.h>
# define _SYS_TYPES_H_INCLUDED_
# endif
# if (!defined(HAVE_GNU_LD) && !defined (__ELF__)) || !defined(LINUX)
# define __environ environ
# if !defined(LINUX)
# define __sigemptyset sigemptyset
# define __sigaction sigaction
# define __sigaddset sigaddset
# define __sigprocmask sigprocmask
# define __execve execve
# define __wait wait
# define __waitpid waitpid
# if defined(ultrix) || defined(hpux) || defined(HAS_VFORK)
# define FORK vfork
# else
# define FORK fork
# endif
# endif /* ! LINUX */
extern char **environ;
# endif
# define SHELL_PATH "/bin/sh" /* Path of the shell. */
# define SHELL_NAME "sh" /* Name to give it. */
# ifndef FORK
# define FORK __fork
# endif
# ifndef CONST
# ifdef __GNUC__
# define CONST const
# else
# define CONST /* nothing */
# endif
# endif
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 {
child = __wait (&status);
if (child <= -1) {
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 {
child = __waitpid (pid, &status, 0);
} while ((child != pid) && (errno == EINTR));
if (child != pid) {
DPRINTF(("6: errno=%d\n", errno));
status = -1;
}
#endif /* NO_WAITPID */
}
}
save = errno;
if ((__sigaction (SIGINT, &intr, (struct sigaction *) NULL)
| __sigaction (SIGQUIT, &quit, (struct sigaction *) NULL)
| __sigprocmask (SIG_SETMASK, &omask, (sigset_t *) NULL)) != 0) {
if (errno == ENOSYS) {
errno = save;
} else {
status = -1;
DPRINTF(("7: errno=%d\n", errno));
}
}
return status;
}
#endif /* WANT_SYSTEM */
/*
* some systems do not have realpath();
* the alternative of reading from a 'pwp'-pipe
* is way too slow. Here is a realpath for the rest of us.
* define WANT_REALPATH in the xxxIntern-file to get it.
*/
#if defined(HAS_REALPATH)
# undef WANT_REALPATH
#endif
#if !defined(HAS_GETWD) && !defined(HAS_GETCWD)
# undef WANT_REALPATH
#endif
#if defined(WANT_REALPATH)
# ifndef NULL
# define NULL (char *)0
# endif
# define MAX_READLINKS 32
# ifndef MAXPATHLEN
# define MAXPATHLEN 1024
# endif
static
char *realpath(path, resolved_path)
char *path;
char resolved_path [];
{
char copy_path[MAXPATHLEN];
char link_path[MAXPATHLEN];
char *new_path = resolved_path;
char *max_path;
int readlinks = 0;
int n;
/* Make a copy of the source path since we may need to modify it. */
strcpy(copy_path, path);
path = copy_path;
max_path = copy_path + MAXPATHLEN - 2;
/* If it's a relative pathname use getwd for starters. */
if (*path != '/') {
#ifdef HAS_GETCWD
getcwd(new_path, MAXPATHLEN - 1);
#else
getwd(new_path);
#endif
new_path += strlen(new_path);
if (new_path[-1] != '/')
*new_path++ = '/';
}
else {
*new_path++ = '/';
path++;
}
/* Expand each slash-separated pathname component. */
while (*path != '\0') {
/* Ignore stray "/". */
if (*path == '/') {
path++;
continue;
}
if (*path == '.') {
/* Ignore ".". */
if (path[1] == '\0' || path[1] == '/') {
path++;
continue;
}
if (path[1] == '.') {
if (path[2] == '\0' || path[2] == '/') {
path += 2;
/* Ignore ".." at root. */
if (new_path == resolved_path + 1)
continue;
/* Handle ".." by backing up. */
while ((--new_path)[-1] != '/')
;
continue;
}
}
}
/* Safely copy the next pathname component. */
while (*path != '\0' && *path != '/') {
if (path > max_path) {
errno = ENAMETOOLONG;
return NULL;
}
*new_path++ = *path++;
}
#ifdef S_IFLNK
/* Protect against infinite loops. */
if (readlinks++ > MAX_READLINKS) {
errno = ELOOP;
return NULL;
}
/* See if latest pathname component is a symlink. */
*new_path = '\0';
n = readlink(resolved_path, link_path, MAXPATHLEN - 1);
if (n < 0) {
/* EINVAL means the file exists but isn't a symlink. */
if (errno != EINVAL)
return NULL;
}
else {
/* Note: readlink doesn't add the null byte. */
link_path[n] = '\0';
if (*link_path == '/')
/* Start over for an absolute symlink. */
new_path = resolved_path;
else
/* Otherwise back up over this component. */
while (*(--new_path) != '/')
;
/* Safe sex check. */
if (strlen(path) + n >= MAXPATHLEN) {
errno = ENAMETOOLONG;
return NULL;
}
/* Insert symlink contents into path. */
strcat(link_path, path);
strcpy(copy_path, link_path);
path = copy_path;
}
#endif /* S_IFLNK */
*new_path++ = '/';
}
/* Delete trailing slash but don't whomp a lone slash. */
if (new_path != resolved_path + 1 && new_path[-1] == '/')
new_path--;
/* Make sure it's null terminated. */
*new_path = '\0';
return resolved_path;
}
# define HAS_REALPATH
#endif /* WANT_REALPATH && not HAS_REALPATH */
#ifdef VMS
char *getwd(p)
char *p;
{
int c;
char *root_dir,*l2;
getcwd(p,512,0); /* get current working directory in unix format*/
root_dir = strstr ( p, "/000000" );
if ( root_dir != NULL ) {
/* trim root directory out of specification */
if ( (strlen(root_dir) == 7) &&
(strpbrk(p+1,"/") == root_dir) ) *root_dir = '\0';
}
/* special kludge for "/" directory */
if ( strcmp ( p, "/DEVICE_LIST_ROOT" ) == 0 )
strcpy ( p, "/" );
return(p);
}
unlink(p)
char *p;
{
#ifdef VMSDEBUG
printf("unlink: '%s'\n",p);
#endif
delete(p);
}
int
lstat(f,st) /* fake a stat operation to return file type */
char *f;
stat_t *st;
{
char *dirext, *name;
int extlen;
st->st_mode = S_IFREG; /* default to normal file */
name = strrchr ( f, '/' ); /* locate rightmost slash */
if ( name == NULL ) name = f; else name++;
dirext = strstr ( name, ".DIR" );
if ( dirext != NULL ) {
/* make it an exact match */
extlen = strcspn(&dirext[1],".;");
if ( (extlen == 0) || (extlen == 3) ) {
st->st_mode = S_IFDIR;
if ( strncmp ( name, "000000.", 7 ) == 0 ) return 0;
else return (stat ( f, st ));
}
}
return 0;
}
# endif /* VMS */
%}
! !
!OperatingSystem class methodsFor:'documentation'!
copyright
"
COPYRIGHT (c) 1988 by Claus Gittinger
All Rights Reserved
This software is furnished under a license and may be used
only in accordance with the terms of that license and with the
inclusion of the above copyright notice. This software may not
be provided or otherwise made available to, or used by, any
other person. No title to or ownership of the software is
hereby transferred.
"
!
version
^ '$Header: /cvs/stx/stx/libbasic/Attic/Unix.st,v 1.72 1995-11-11 15:28:19 cg Exp $'
!
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 interresting to be
able to get down to a select or fork system call easily.
You decide - portability vs. functionality)
Currently, there is only this class available - once others than
Unix are supported, the corresponding class will be bound to the
global variable OperatingSystem.
Class variables:
HostName <String> remembered hostname
LastErrorNumber <Integer> the last value of errno
LastExecStatus <Integer> the returned exec status after
the last call of system
OSSignals <Array> Array of signals to be raised for corresponding
OperatingSystem signals.
ForkFailed <Boolean> set if a fork (or popen) has failed;
ST/X will avoid doing more forks/popens
if this flag is set, for a slightly
smoother operation.
SlowFork <Boolean> if set, fork and popen are avoided;
(more or less obsolete now)
"
! !
!OperatingSystem class methodsFor:'initialization'!
initialize
"initialize the class"
ObjectMemory addDependent:self
!
update:something
something == #earlyRestart ifTrue:[
"
flush cached data
"
HostName := nil.
LastErrorNumber := nil.
LastExecStatus := nil
]
! !
!OperatingSystem class methodsFor:'misc'!
slowFork:aBoolean
SlowFork := aBoolean
!
exit
"shutdown smalltalk immediately -
return 'good'-status (0) to parent unix process."
%{ /* NOCONTEXT */
__mainExit(0);
%}
"OperatingSystem exit - dont evaluate this"
!
exit:exitCode
"shutdown smalltalk immediately -
returning an exit-code to the parent unix process."
%{ /* NOCONTEXT */
int code = 1;
if (__isSmallInteger(exitCode)) {
code = _intVal(exitCode);
}
__mainExit(code);
%}
"OperatingSystem exit:1 - dont evaluate this"
!
exitWithCoreDump
"shutdown smalltalk immediately - dumping core.
This always returns 'bad'-status to the parent unix process.
Notice, that no cleanup is performed at all - you may have to
manually remove any tempfiles.
Use this only for debugging ST/X itself"
%{ /* NOCONTEXT */
abort();
%}
"OperatingSystem exitWithCoreDump - dont evaluate this"
! !
!OperatingSystem class methodsFor:'os queries'!
getEnvironment:aStringOrSymbol
"get an environment string"
%{ /* NOCONTEXT */
char *env;
if (__isString(aStringOrSymbol) || __isSymbol(aStringOrSymbol)) {
env = (char *)getenv(_stringVal(aStringOrSymbol));
if (env) {
RETURN ( _MKSTRING(env COMMA_CON) );
}
}
%}
.
^ nil
"
OperatingSystem getEnvironment:'LANG'
OperatingSystem getEnvironment:'LOGIN'
OperatingSystem getEnvironment:'HOME'
OperatingSystem getEnvironment:'NNTPSERVER'
OperatingSystem getEnvironment:'MAIL'
OperatingSystem getEnvironment:'PATH'
"
!
getProcessId
"return the (unix-)processId"
%{ /* NOCONTEXT */
int pid = 0;
#ifdef UNIX_LIKE
pid = getpid();
#endif
RETURN ( _MKSMALLINT(pid) );
%}
"
OperatingSystem getProcessId
"
!
getCPUType
"return a string giving the type of machine we're running on.
This may normally not be of any interrest to you ..."
|cpu|
%{ /* NOCONTEXT */
# ifdef vax
# define CPU_STRING "vax"
# endif
# ifdef mips
# define CPU_STRING "mips"
# endif
# ifdef i386
# define CPU_STRING "i386"
# endif
# ifdef ns32k
# define CPU_STRING "ns32k"
# endif
# ifdef mc68k
# define CPU_STRING "mc68k"
# endif
# ifdef mc88k
# define CPU_STRING "mc88k"
# endif
# ifdef sparc
# define CPU_STRING "sparc"
# endif
# ifdef hppa
# define CPU_STRING "hppa"
# endif
# ifdef rs6000
# define CPU_STRING "rs6000"
# endif
# ifdef alpha
# define CPU_STRING "alpha"
# endif
# ifdef powerPC
# define CPU_STRING "powerPC"
# endif
# ifdef transputer
# define CPU_STRING "transputer"
# endif
# ifdef ibm370
# define CPU_STRING "ibm370"
# endif
# ifndef CPU_STRING
# define CPU_STRING "unknown"
# endif
cpu = _MKSTRING(CPU_STRING COMMA_CON);
# undef CPU_STRING
%}
.
^ cpu
"
OperatingSystem getCPUType
"
"examples: are we running on a ss-10/solaris ?"
"
(OperatingSystem getCPUType = 'sparc')
and:[OperatingSystem getOSType = 'solaris']
"
"or on a pc/solaris ?"
"
(OperatingSystem getCPUType = 'i386')
and:[OperatingSystem getOSType = 'solaris']
"
!
getOSType
"return a string giving the type of OS we're running on.
This can be used to adapt programs to certain environment
differences (for example: mail-lock strategy ...)"
|os|
%{ /* NOCONTEXT */
# ifdef MSDOS
# define OS_STRING "msdos"
# endif
# ifdef MSWINDOWS
# define OS_STRING "mswindows"
# endif
# ifdef NT
# define OS_STRING "nt"
# endif
# ifdef VMS
# define OS_STRING "vms"
# endif
# ifdef MVS
# define OS_STRING "mvs"
# endif
# ifdef OS2
# define OS_STRING "os2"
# endif
# ifdef sinix
# define OS_STRING "sinix"
# endif
# ifdef ultrix
# define OS_STRING "ultrix"
# endif
# ifdef sco
# define OS_STRING "sco"
# endif
# ifdef hpux
# define OS_STRING "hpux"
# endif
# ifdef LINUX
# define OS_STRING "linux"
# endif
# ifdef sunos
# define OS_STRING "sunos"
# endif
# ifdef solaris
# define OS_STRING "solaris"
# endif
# ifdef IRIS
# define OS_STRING "irix"
# endif
# ifdef aix
# define OS_STRING "aix"
# endif
# ifdef realIX
# define OS_STRING "realIX"
# endif
/*
* no concrete info; become somewhat vague ...
*/
# ifndef OS_STRING
# ifdef MACH
# define OS_STRING "mach"
# endif
# endif
# ifndef OS_STRING
# ifdef BSD
# define OS_STRING "bsd"
# endif
# ifdef SYSV
# ifdef SYSV3
# define OS_STRING "sys5_3"
# else
# ifdef SYSV4
# define OS_STRING "sys5_4"
# else
# define OS_STRING "sys5"
# endif
# endif
# endif
# endif
/*
* become very vague ...
*/
# ifndef OS_STRING
# ifdef POSIX
# define OS_STRING "posix"
# endif
# endif
# ifndef OS_STRING
# ifdef UNIX
# define OS_STRING "unix"
# endif
# endif
# ifndef OS_STRING
# define OS_STRING "unknown"
# endif
os = _MKSTRING(OS_STRING COMMA_CON);
# undef OS_STRING
%}
.
^ os
"OperatingSystem getOSType"
!
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 COMMA_CON);
# undef SYS_STRING
# endif
%}
.
sys isNil ifTrue:[
^ self getOSType
].
^ sys
"
OperatingSystem getSystemType
"
!
getOSDefine
"return a string which was used to identify this machine when stx was
compiled, and which should be passed down when compiling methods.
For example, on linux, this is '-DLINUX'."
%{ /* NOCONTEXT */
#ifndef OS_DEFINE
# define OS_DEFINE "-DunknownOS"
#endif
RETURN ( __MKSTRING(OS_DEFINE COMMA_CON));
%}
"
OperatingSystem getOSDefine
"
!
getCPUDefine
"return a string which was used to identify this CPU type when stx was
compiled, and which should be passed down when compiling methods.
For example, on linux, this may be '-Di386'; on a vax, this would be '-Dvax'."
%{ /* NOCONTEXT */
#ifndef CPU_DEFINE
# define CPU_DEFINE "-DunknownCPU"
#endif
RETURN ( __MKSTRING(CPU_DEFINE COMMA_CON));
%}
"
OperatingSystem getCPUDefine
"
!
getHostName
"return the hostname we are running on - if there is
a HOST environment variable, we are much faster here ...
Notice:
not all systems support this; on some, 'unknown' is returned."
|name|
HostName notNil ifTrue:[
^ HostName
].
%{ /* STACK: 2048 */
#if defined(HAS_GETHOSTNAME)
char buffer[128];
if (gethostname(buffer, sizeof(buffer)) == 0) {
name = _MKSTRING(buffer COMMA_CON);
}
#else
# if defined(HAS_UNAME)
struct utsname ubuff;
if (uname(&ubuff) >= 0) {
name = _MKSTRING(ubuff.nodename COMMA_CON);
}
# endif
#endif
%}.
name isNil ifTrue:[
name := self getEnvironment:'HOST'.
name isNil ifTrue:[
name := self getCommandOutputFrom:'hostname'
]
].
name isNil ifTrue:[
'OS: cannot find out hostname' errorPrintNL.
name := 'unknown'.
].
HostName := name.
^ name
"
OperatingSystem getHostName
"
!
getDomainName
"return the domain this host is in.
Notice:
not all systems support this; on some, 'unknown' is returned."
|name|
%{ /* STACK: 2048 */
#if defined(HAS_GETDOMAINNAME)
char buffer[128];
if (getdomainname(buffer, sizeof(buffer)) == 0) {
name = _MKSTRING(buffer COMMA_CON);
}
#else
# if defined(HAS_UNAME) && defined(HAS_UTS_DOMAINNAME)
struct utsname ubuff;
if (uname(&ubuff) >= 0) {
name = _MKSTRING(ubuff.domainname COMMA_CON);
}
# endif
#endif
%}.
name isNil ifTrue:[
name := self getEnvironment:'DOMAIN'.
name isNil ifTrue:[
name := self getCommandOutputFrom:'domainname'
]
].
name isNil ifTrue:[
'OS: cannot find out domainname' errorPrintNL.
name := 'unknown'.
].
HostName := name.
^ name
"
OperatingSystem getDomainName
"
!
getSystemInfo
"return info on the system weare running on - if the
system supports the uname system call, that infor is returned;
otherwise, some simulated info is returned.
WARNING:
Do not depend on the amount of returned information, some
systems may return more/less than others.
The returned info may (or may not) contain:
#system -> some operating system identification
#node -> some host identification
#release -> OS release
#version -> OS version
#machine -> type of machine
#domain -> domain name
"
|sys node rel ver mach dom mtyp brel info|
%{ /* STACK: 2048 */
#if defined(HAS_UNAME)
struct utsname ubuff;
if (uname(&ubuff) >= 0) {
sys = _MKSTRING(ubuff.sysname COMMA_CON);
node = _MKSTRING(ubuff.nodename COMMA_CON);
rel = _MKSTRING(ubuff.release COMMA_CON);
ver = _MKSTRING(ubuff.version COMMA_CON);
mach = _MKSTRING(ubuff.machine COMMA_CON);
# ifdef HAS_UTS_DOMAINNAME
dom = _MKSTRING(ubuff.domainname COMMA_CON);
# endif
}
#else
# ifdef mswindows
/* add corresponding info here */
# endif
#endif
%}.
sys isNil ifTrue:[
sys := self getSystemType.
].
node isNil ifTrue:[
node := self getHostName
].
dom isNil ifTrue:[
dom := self getDomainName
].
info := IdentityDictionary new.
info at:#system put:sys.
info at:#node put:node.
rel notNil ifTrue:[info at:#release put:rel].
ver notNil ifTrue:[info at:#version put:ver].
mach notNil ifTrue:[info at:#machine put:mach].
dom notNil ifTrue:[info at:#domain put:dom].
^ info
"
OperatingSystem getSystemInfo
"
!
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;
extern OBJ __BYTEARRAY_UNINITIALIZED_NEW_INT();
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;
extern OBJ __BYTEARRAY_UNINITIALIZED_NEW_INT();
runningId = gethostid();
arr = __BYTEARRAY_UNINITIALIZED_NEW_INT(4);
*(int *)(__ByteArrayInstPtr(arr)->ba_element) = runningId;
RETURN (arr);
#endif
%}.
^ 'unknown'
"
OperatingSystem getSystemID
"
!
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."
%{ /* NOCONTEXT */
#if defined(UNIX_LIKE)
RETURN ( false );
#endif
%}
.
^ true
!
isMSDOSlike
"return true, if the OS we're running on is dos like
(in contrast to unix-like)."
%{ /* NOCONTEXT */
#if defined(MSDOS_LIKE)
RETURN ( true );
#endif
%}
.
^ false
!
maxFileNameLength
"return the max number of characters in a filename.
Actually, the following is somewhat wrong - some systems
support differnet size depending on the volume.
We return a somewhat conservative number here."
%{ /* NOCONTEXT */
/*
* XXX: newer systems provide a query function for this ... use it
*/
# if defined(BSD) || defined(SYSV4) || defined(LONGFILENAMES)
RETURN ( _MKSMALLINT(255) );
# endif
# ifdef realIX
RETURN ( _MKSMALLINT(127) );
# endif
# ifdef SYSV
RETURN ( _MKSMALLINT(14) );
# endif
# ifdef MSDOS
RETURN ( _MKSMALLINT(9) );
# endif
# ifdef NT
/*
* mhmh - depends on the filesystem type
*/
RETURN ( _MKSMALLINT(9) );
# endif
%}.
"unix default"
^ 14
!
supportsIOInterrupts
"return true, if the OS supports IO availability interrupts
(i.e. SIGPOLL/SIGIO).
Currently, this mechanism does not work at all ..."
%{ /* NOCONTEXT */
#ifdef NOTDEF
# if defined(SIGPOLL) || defined(SIGIO)
# if defined(F_GETFL) && defined(F_SETFL)
# if defined(FASYNC)
/*
* mhmh they seem to NOT work on NS2.1
*/
# if !defined(NEXT)
RETURN (true);
# endif
# endif
# endif
# endif
#endif
%}
.
^ false
!
supportsNonBlockingIO
"return true, if the OS supports nonblocking IO."
%{ /* NOCONTEXT */
# if defined(F_GETFL) && defined(F_SETFL)
# if defined(FNDELAY)
RETURN (true);
# endif
# endif
%}
.
^ false
!
supportsSelect
"return true, if the OS supports selecting on multiple
filedescriptors via select."
%{ /* NOCONTEXT */
#if defined(sco)
/*
* sco has a broken select - always waiting 1 second
*/
RETURN(false);
#endif
%}
.
^ true
!
getCommandOutputFrom:aCommand
"execute a simple command (such as hostname) and
return the commands output as a string"
|p result|
ForkFailed ifFalse:[
PipeStream openErrorSignal handle:[:ex |
ForkFailed := true.
'OS: cannot fork/popen' errorPrintNL.
ex return.
] do:[
p := PipeStream readingFrom:aCommand.
p notNil ifTrue:[
result := p nextLine.
p close
]
]
].
^ result
! !
!OperatingSystem class methodsFor:'users & groups'!
getLoginName
"return a string with the users name"
%{ /* NOCONTEXT */
char *name = "you";
#ifdef UNIX_LIKE
name = (char *)getlogin();
if (! name || (name[0] == 0)) {
name = (char *)getenv("LOGNAME");
}
#endif
RETURN ( _MKSTRING(name COMMA_CON) );
%}
"
OperatingSystem getLoginName
"
!
getUserNameFromID:aNumber
"return the user-name-string for a given numeric user-id"
%{ /* NOCONTEXT */
#ifdef UNIX_LIKE
struct passwd *p;
if (__isSmallInteger(aNumber)) {
p = getpwuid(_intVal(aNumber));
if (p) {
RETURN ( _MKSTRING(p->pw_name COMMA_CON) );
}
}
#endif
%}.
^ '? (' , aNumber printString , ')'
"
OperatingSystem getUserNameFromID:0
OperatingSystem getUserNameFromID:100
OperatingSystem getUserNameFromID:9991
"
!
getGroupNameFromID:aNumber
"return the group-name-string for a given numeric group-id"
%{ /* NOCONTEXT */
#ifdef UNIX_LIKE
struct group *g;
if (__isSmallInteger(aNumber)) {
g = getgrgid(_intVal(aNumber));
if (g) {
RETURN ( _MKSTRING(g->gr_name COMMA_CON) );
}
}
#endif
%}.
^ '???'
"
OperatingSystem getGroupNameFromID:0
OperatingSystem getGroupNameFromID:10
"
!
getHomeDirectory
"return the name of the users home directory"
^ OperatingSystem getEnvironment:'HOME'
"
OperatingSystem getHomeDirectory
"
!
getUserID
"return the current users (thats you) numeric user id"
%{ /* NOCONTEXT */
#ifdef UNIX_LIKE
int uid;
uid = getuid();
RETURN ( __MKSMALLINT(uid) );
#else
# ifdef SYSTEM_HAS_USERS
/* ... */
# endif
#endif
%}.
^ 1 "just a dummy for systems which do not have userIDs"
"
OperatingSystem getUserID
"
!
getEffectiveUserID
"return the current users (thats you) effective numeric user id.
This is only different from getUserID, if you have ST/X running
as a setuid program (of which you should think about twice)."
%{ /* NOCONTEXT */
#ifdef UNIX_LIKE
int uid;
uid = geteuid();
RETURN ( __MKSMALLINT(uid) );
#endif
/* --- return same as getUserID --- */
%}.
^ self getUserID
"
OperatingSystem getEffectiveUserID
"
!
getGroupID
"return the current users (thats you) numeric group id"
%{ /* NOCONTEXT */
#ifdef UNIX_LIKE
int uid;
uid = getgid();
RETURN ( __MKSMALLINT(uid) );
#else
# ifdef SYSTEM_HAS_GROUPS
/* ... */
# endif
#endif
%}.
^ 1 "just a dummy for systems which do not have userIDs"
"
OperatingSystem getGroupID
"
!
getEffectiveGroupID
"return the current users (thats you) effective numeric group id.
This is only different from getGroupID, if you have ST/X running
as a setuid program (of which you should think about twice)."
%{ /* NOCONTEXT */
#ifdef UNIX_LIKE
int uid;
uid = getegid();
RETURN ( __MKSMALLINT(uid) );
#endif
/* --- return same as getGroupID --- */
%}.
^ self getGroupID
"
OperatingSystem getEffectiveGroupID
"
!
userInfoOf:aNameOrID
"return a dictionary filled with userinfo. The argument can be either
a string with the users name or its numeric id.
Notice, that not all systems provide (all of) this info;
DOS systems return nothing;
non-SYSV4 systems have no age/comment.
Portable applications may want to check the systemType and NOT depend
on all keys to be present in the returned dictionary.
Another notice: on some systems (SYSV4), the gecos field includes multiple
entries (i.e. not just the name), separated by commas. You may want to
extract any substring, up to the first comma to get the real life name."
|info name passw uid gid age comment
gecos dir shell|
%{
#ifdef UNIX_LIKE
struct passwd *buf;
int ret;
if (__isString(aNameOrID)) {
buf = getpwnam(__stringVal(aNameOrID));
} else if (__isSmallInteger(aNameOrID)) {
buf = getpwuid(__intVal(aNameOrID));
} else {
buf = (struct passwd *)0;
}
if (buf) {
name = _MKSTRING(buf->pw_name COMMA_CON);
passw = _MKSTRING(buf->pw_passwd COMMA_CON);
# ifdef SYSV4
age = _MKSTRING(buf->pw_age COMMA_CON);
comment = _MKSTRING(buf->pw_comment COMMA_CON);
# endif
dir = _MKSTRING(buf->pw_dir COMMA_CON);
gecos = _MKSTRING(buf->pw_gecos COMMA_CON);
shell = _MKSTRING(buf->pw_shell COMMA_CON);
uid = _MKSMALLINT(buf->pw_uid);
gid = _MKSMALLINT(buf->pw_gid);
}
#endif
%}.
info := IdentityDictionary new.
name notNil ifTrue:[
info at:#name put:name.
] ifFalse:[
info at:#name put:'unknown'
].
passw notNil ifTrue:[info at:#passwd put:passw].
age notNil ifTrue:[info at:#age put:age].
comment notNil ifTrue:[info at:#comment put:comment].
gecos notNil ifTrue:[info at:#gecos put:gecos].
shell notNil ifTrue:[info at:#shell put:shell].
dir notNil ifTrue:[info at:#dir put:dir].
uid notNil ifTrue:[info at:#uid put:uid].
gid notNil ifTrue:[info at:#gid put:gid].
^ info
"
OperatingSystem userInfoOf:'root'
OperatingSystem userInfoOf:1
OperatingSystem userInfoOf:'claus'
OperatingSystem userInfoOf:'fooBar'
OperatingSystem userInfoOf:(OperatingSystem getUserID)
"
!
! !
!OperatingSystem class methodsFor:'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
"
!
lastErrorNumber
"return the last error number"
^ LastErrorNumber
"
OperatingSystem lastErrorNumber
"
!
lastExecStatus
"return the last execution status"
^ LastExecStatus
"
OperatingSystem lastExecStatus
"
!
lastErrorString
"return a message string describing the last error"
^ self errorTextForNumber:LastErrorNumber
"
OperatingSystem lastErrorString
"
!
errorTextForNumber:errNr
"return a message string from a unix errorNumber
(as returned by a system call).
Should be replaced by a resource lookup."
%{
/* claus:
* I made this some primitive code, since errnos are not
* standard across unixes
*/
char *msg = "unknown error";
char buffer[50];
if (__isSmallInteger(errNr)) {
switch (_intVal(errNr)) {
/*
* POSIX errnos - these should be defined
*/
#ifdef EPERM
case EPERM:
msg = "Operation not permitted";
break;
#endif
#ifdef ENOENT
case ENOENT:
msg = "No such file or directory";
break;
#endif
#ifdef ESRCH
case ESRCH:
msg = "No such process";
break;
#endif
#ifdef EINTR
case EINTR:
msg = "Interrupted system call";
break;
#endif
#ifdef EIO
case EIO:
msg = "I/O error";
break;
#endif
#ifdef ENXIO
case ENXIO:
msg = "No such device or address";
break;
#endif
#ifdef E2BIG
case E2BIG:
msg = "Arg list too long";
break;
#endif
#ifdef ENOEXEC
case ENOEXEC:
msg = "Exec format error";
break;
#endif
#ifdef EBADF
case EBADF:
msg = "Bad file number";
break;
#endif
#ifdef ECHILD
case ECHILD:
msg = "No child processes";
break;
#endif
#if !defined(EWOULDBLOCK) && defined(EAGAIN) && (EWOULDBLOCK != EAGAIN)
case EAGAIN:
msg = "Try again";
break;
#endif
#ifdef ENOMEM
case ENOMEM:
msg = "Out of memory";
break;
#endif
#ifdef EACCES
case EACCES:
msg = "Permission denied";
break;
#endif
#ifdef EFAULT
case EFAULT:
msg = "Bad address";
break;
#endif
#ifdef EBUSY
case EBUSY:
msg = "Device or resource busy";
break;
#endif
#ifdef EEXIST
case EEXIST:
msg = "File exists";
break;
#endif
#ifdef EXDEV
case EXDEV:
msg = "Cross-device link";
break;
#endif
#ifdef ENODEV
case ENODEV:
msg = "No such device";
break;
#endif
#ifdef ENOTDIR
case ENOTDIR:
msg = "Not a directory";
break;
#endif
#ifdef EISDIR
case EISDIR:
msg = "Is a directory";
break;
#endif
#ifdef EINVAL
case EINVAL:
msg = "Invalid argument";
break;
#endif
#ifdef ENFILE
case ENFILE:
msg = "File table overflow";
break;
#endif
#ifdef EMFILE
case EMFILE:
msg = "Too many open files";
break;
#endif
#ifdef ENOTTY
case ENOTTY:
msg = "Not a typewriter";
break;
#endif
#ifdef EFBIG
case EFBIG:
msg = "File too large";
break;
#endif
#ifdef ENOSPC
case ENOSPC:
msg = "No space left on device";
break;
#endif
#ifdef ESPIPE
case ESPIPE:
msg = "Illegal seek";
break;
#endif
#ifdef EROFS
case EROFS:
msg = "Read-only file system";
break;
#endif
#ifdef EMLINK
case EMLINK:
msg = "Too many links";
break;
#endif
#ifdef EPIPE
case EPIPE:
msg = "Broken pipe";
break;
#endif
#ifdef EDOM
case EDOM:
msg = "Math argument out of domain";
break;
#endif
#ifdef ERANGE
case ERANGE:
msg = "Math result not representable";
break;
#endif
#ifdef EDEADLK
# if EDEADLK != EWOULDBLOCK
case EDEADLK:
msg = "Resource deadlock would occur";
break;
# endif
#endif
#ifdef ENAMETOOLONG
case ENAMETOOLONG:
msg = "File name too long";
break;
#endif
#ifdef ENOLCK
case ENOLCK:
msg = "No record locks available";
break;
#endif
#ifdef ENOSYS
case ENOSYS:
msg = "Function not implemented";
break;
#endif
#if defined(ENOTEMPTY) && (ENOTEMPTY != EEXIST)
case ENOTEMPTY:
msg = "Directory not empty";
break;
#endif
#ifdef EILSEQ
case EILSEQ:
msg = "Illegal byte sequence";
break;
#endif
/*
* XPG3 errnos - defined on most systems
*/
#ifdef ENOTBLK
case ENOTBLK:
msg = "Block device required";
break;
#endif
#ifdef ETXTBSY
case ETXTBSY:
msg = "Text file busy";
break;
#endif
/*
* some others
*/
#ifdef EWOULDBLOCK
case EWOULDBLOCK:
msg = "Operation would block";
break;
#endif
#ifdef ENOMSG
case ENOMSG:
msg = "No message of desired type";
break;
#endif
#ifdef ELOOP
case ELOOP:
msg = "Too many levels of symbolic links";
break;
#endif
/*
* some stream errors
*/
#ifdef ETIME
case ETIME:
msg = "Timer expired";
break;
#endif
#ifdef ENOSR
case ENOSR:
msg = "Out of streams resources";
break;
#endif
#ifdef ENOSTR
case ENOSTR:
msg = "Device not a stream";
break;
#endif
#ifdef ECOMM
case ECOMM:
msg = "Communication error on send";
break;
#endif
#ifdef EPROTO
case EPROTO:
msg = "Protocol error";
break;
#endif
/*
* nfs errors
*/
#ifdef ESTALE
case ESTALE:
msg = "Stale NFS file handle";
break;
#endif
#ifdef EREMOTE
case EREMOTE:
msg = "Too many levels of remote in path";
break;
#endif
/*
* some networking errors
*/
#ifdef EINPROGRESS
case EINPROGRESS:
msg = "Operation now in progress";
break;
#endif
#ifdef EALREADY
case EALREADY:
msg = "Operation already in progress";
break;
#endif
#ifdef ENOTSOCK
case ENOTSOCK:
msg = "Socket operation on non-socket";
break;
#endif
#ifdef EDESTADDRREQ
case EDESTADDRREQ:
msg = "Destination address required";
break;
#endif
#ifdef EMSGSIZE
case EMSGSIZE:
msg = "Message too long";
break;
#endif
#ifdef EPROTOTYPE
case EPROTOTYPE:
msg = "Protocol wrong type for socket";
break;
#endif
#ifdef ENOPROTOOPT
case ENOPROTOOPT:
msg = "Protocol not available";
break;
#endif
#ifdef EPROTONOSUPPORT
case EPROTONOSUPPORT:
msg = "Protocol not supported";
break;
#endif
#ifdef ESOCKTNOSUPPORT
case ESOCKTNOSUPPORT:
msg = "Socket type not supported";
break;
#endif
#ifdef EOPNOTSUPP
case EOPNOTSUPP:
msg = "Operation not supported on socket";
break;
#endif
#ifdef EPFNOSUPPORT
case EPFNOSUPPORT:
msg = "Protocol family not supported";
break;
#endif
#ifdef EAFNOSUPPORT
case EAFNOSUPPORT:
msg = "Address family not supported by protocol family";
break;
#endif
#ifdef EADDRINUSE
case EADDRINUSE:
msg = "Address already in use";
break;
#endif
#ifdef EADDRNOTAVAIL
case EADDRNOTAVAIL:
msg = "Can\'t assign requested address";
break;
#endif
#ifdef ETIMEDOUT
case ETIMEDOUT:
msg = "Connection timed out";
break;
#endif
#ifdef ECONNREFUSED
case ECONNREFUSED:
msg = "Connection refused";
break;
#endif
#ifdef ENETDOWN
case ENETDOWN:
msg = "Network is down";
break;
#endif
#ifdef ENETUNREACH
case ENETUNREACH:
msg = "Network is unreachable";
break;
#endif
#ifdef ENETRESET
case ENETRESET:
msg = "Network dropped conn due to reset";
break;
#endif
#ifdef ECONNABORTED
case ECONNABORTED:
msg = "Software caused connection abort";
break;
#endif
#ifdef ECONNRESET
case ECONNRESET:
msg = "Connection reset by peer";
break;
#endif
#ifdef EISCONN
case EISCONN:
msg = "Socket is already connected";
break;
#endif
#ifdef ENOTCONN
case ENOTCONN:
msg = "Socket is not connected";
break;
#endif
#ifdef ESHUTDOWN
case ESHUTDOWN:
msg = "Can't send after socket shutdown";
break;
#endif
#ifdef EHOSTDOWN
case EHOSTDOWN:
msg = "Host is down";
break;
#endif
#ifdef EHOSTUNREACH
case EHOSTUNREACH:
msg = "No route to host";
break;
#endif
default:
{
#ifdef THISCONTEXT_IN_REGISTER
extern OBJ __thisContext__;
__thisContext__ = __thisContext;
#endif
sprintf(buffer, "ErrorNr: %d", _intVal(errNr));
#ifdef THISCONTEXT_IN_REGISTER
__thisContext = __thisContext__;
__thisContext__ = nil;
#endif
}
msg = buffer;
break;
}
}
RETURN (_MKSTRING(msg COMMA_CON));
%}
! !
!OperatingSystem class methodsFor:'OS signal constants'!
sigHUP
"return the signal number for SIGHUP
(the numeric value is not the same across unix-systems)"
%{ /* NOCONTEXT */
#ifdef SIGHUP
return _MKSMALLINT(SIGHUP);
#else
return _MKSMALLINT(0);
#endif
%}
!
sigINT
"return the signal number for SIGINT
(the numeric value is not the same across unix-systems)"
%{ /* NOCONTEXT */
#ifdef SIGINT
return _MKSMALLINT(SIGINT);
#else
return _MKSMALLINT(0);
#endif
%}
!
sigQUIT
"return the signal number for SIGQUIT
(the numeric value is not the same across unix-systems)"
%{ /* NOCONTEXT */
#ifdef SIGQUIT
return _MKSMALLINT(SIGQUIT);
#else
return _MKSMALLINT(0);
#endif
%}
!
sigILL
"return the signal number for SIGILL - 0 if not supported by OS
(the numeric value is not the same across unix-systems)"
%{ /* NOCONTEXT */
#ifdef SIGILL
return _MKSMALLINT(SIGILL);
#else
return _MKSMALLINT(0);
#endif
%}
!
sigTRAP
"return the signal number for SIGTRAP - 0 if not supported by OS
(the numeric value is not the same across unix-systems)"
%{ /* NOCONTEXT */
#ifdef SIGTRAP
return _MKSMALLINT(SIGTRAP);
#else
return _MKSMALLINT(0);
#endif
%}
!
sigABRT
"return the signal number for SIGABRT - 0 if not supported by OS
(the numeric value is not the same across unix-systems)"
%{ /* NOCONTEXT */
#ifdef SIGABRT
return _MKSMALLINT(SIGABRT);
#else
return _MKSMALLINT(0);
#endif
%}
!
sigIOT
"return the signal number for SIGIOT - 0 if not supported by OS
(the numeric value is not the same across unix-systems)"
%{ /* NOCONTEXT */
#ifdef SIGIOT
return _MKSMALLINT(SIGIOT);
#else
return _MKSMALLINT(0);
#endif
%}
!
sigEMT
"return the signal number for SIGEMT - 0 if not supported by OS
(the numeric value is not the same across unix-systems)"
%{ /* NOCONTEXT */
#ifdef SIGEMT
return _MKSMALLINT(SIGEMT);
#else
return _MKSMALLINT(0);
#endif
%}
!
sigFP
"return the signal number for SIGFP - 0 if not supported by OS
(the numeric value is not the same across unix-systems)"
%{ /* NOCONTEXT */
#ifdef SIGFPE
return _MKSMALLINT(SIGFPE);
#else
return _MKSMALLINT(0);
#endif
%}
!
sigKILL
"return the signal number for SIGKILL
(the numeric value is not the same across unix-systems)"
%{ /* NOCONTEXT */
#ifdef SIGKILL
return _MKSMALLINT(SIGKILL);
#else
return _MKSMALLINT(0);
#endif
%}
!
sigBUS
"return the signal number for SIGBUS - 0 if not supported
(the numeric value is not the same across unix-systems)"
%{ /* NOCONTEXT */
#ifdef SIGBUS
return _MKSMALLINT(SIGBUS);
#else
return _MKSMALLINT(0);
#endif
%}
!
sigSEGV
"return the signal number for SIGSEGV - 0 if not supported
(the numeric value is not the same across unix-systems)"
%{ /* NOCONTEXT */
#ifdef SIGSEGV
return _MKSMALLINT(SIGSEGV);
#else
return _MKSMALLINT(0);
#endif
%}
!
sigSYS
"return the signal number for SIGSYS - 0 if not supported
(the numeric value is not the same across unix-systems)"
%{ /* NOCONTEXT */
#ifdef SIGSYS
return _MKSMALLINT(SIGSYS);
#else
return _MKSMALLINT(0);
#endif
%}
!
sigPIPE
"return the signal number for SIGPIPE - 0 if not supported
(the numeric value is not the same across unix-systems)"
%{ /* NOCONTEXT */
#ifdef SIGPIPE
return _MKSMALLINT(SIGPIPE);
#else
return _MKSMALLINT(0);
#endif
%}
!
sigALRM
"return the signal number for SIGALRM - 0 if not supported
(the numeric value is not the same across unix-systems)"
%{ /* NOCONTEXT */
#ifdef SIGALRM
return _MKSMALLINT(SIGALRM);
#else
return _MKSMALLINT(0);
#endif
%}
!
sigTERM
"return the signal number for SIGTERM - 0 if not supported
(the numeric value is not the same across unix-systems)"
%{ /* NOCONTEXT */
#ifdef SIGTERM
return _MKSMALLINT(SIGTERM);
#else
return _MKSMALLINT(0);
#endif
%}
!
sigURG
"return the signal number for SIGURG - 0 if not supported
(the numeric value is not the same across unix-systems)"
%{ /* NOCONTEXT */
#if defined(SIGURG)
return _MKSMALLINT(SIGURG);
#else
return _MKSMALLINT(0);
#endif
%}
!
sigSTOP
"return the signal number for SIGSTOP - 0 if not supported
(the numeric value is not the same across unix-systems)"
%{ /* NOCONTEXT */
#if defined(SIGSTOP)
return _MKSMALLINT(SIGSTOP);
#else
return _MKSMALLINT(0);
#endif
%}
!
sigTSTP
"return the signal number for SIGTSTP - 0 if not supported
(the numeric value is not the same across unix-systems)"
%{ /* NOCONTEXT */
#if defined(SIGTSTP)
return _MKSMALLINT(SIGTSTP);
#else
return _MKSMALLINT(0);
#endif
%}
!
sigCONT
"return the signal number for SIGCONT - 0 if not supported
(the numeric value is not the same across unix-systems)"
%{ /* NOCONTEXT */
#if defined(SIGCONT)
return _MKSMALLINT(SIGCONT);
#else
return _MKSMALLINT(0);
#endif
%}
!
sigCHLD
"return the signal number for SIGCHLD - 0 if not supported
(the numeric value is not the same across unix-systems)"
%{ /* NOCONTEXT */
#if defined(SIGCHLD)
return _MKSMALLINT(SIGCHLD);
#else
# if defined(SIGCLD)
return _MKSMALLINT(SIGCLD);
# else
return _MKSMALLINT(0);
# endif
#endif
%}
!
sigTTIN
"return the signal number for SIGTTIN - 0 if not supported
(the numeric value is not the same across unix-systems)"
%{ /* NOCONTEXT */
#if defined(SIGTTIN)
return _MKSMALLINT(SIGTTIN);
#else
return _MKSMALLINT(0);
#endif
%}
!
sigTTOU
"return the signal number for SIGTTOU - 0 if not supported
(the numeric value is not the same across unix-systems)"
%{ /* NOCONTEXT */
#if defined(SIGTTOU)
return _MKSMALLINT(SIGTTOU);
#else
return _MKSMALLINT(0);
#endif
%}
!
sigIO
"return the signal number for SIGIO - 0 if not supported
(the numeric value is not the same across unix-systems)"
%{ /* NOCONTEXT */
#if defined(SIGIO)
return _MKSMALLINT(SIGIO);
#else
return _MKSMALLINT(0);
#endif
%}
!
sigPOLL
"return the signal number for SIGPOLL - 0 if not supported
(the numeric value is not the same across unix-systems)"
%{ /* NOCONTEXT */
#if defined(SIGPOLL)
return _MKSMALLINT(SIGPOLL);
#else
return _MKSMALLINT(0);
#endif
%}
!
sigXCPU
"return the signal number for SIGXCPU - 0 if not supported
(the numeric value is not the same across unix-systems)"
%{ /* NOCONTEXT */
#if defined(SIGXCPU)
return _MKSMALLINT(SIGXCPU);
#else
return _MKSMALLINT(0);
#endif
%}
!
sigXFSZ
"return the signal number for SIGXFSZ - 0 if not supported
(the numeric value is not the same across unix-systems)"
%{ /* NOCONTEXT */
#if defined(SIGXFSZ)
return _MKSMALLINT(SIGXFSZ);
#else
return _MKSMALLINT(0);
#endif
%}
!
sigVTALRM
"return the signal number for SIGVTALRM - 0 if not supported
(the numeric value is not the same across unix-systems)"
%{ /* NOCONTEXT */
#if defined(SIGVTALRM)
return _MKSMALLINT(SIGVTALRM);
#else
return _MKSMALLINT(0);
#endif
%}
!
sigPROF
"return the signal number for SIGPROF - 0 if not supported
(the numeric value is not the same across unix-systems)"
%{ /* NOCONTEXT */
#if defined(SIGPROF)
return _MKSMALLINT(SIGPROF);
#else
return _MKSMALLINT(0);
#endif
%}
!
sigWINCH
"return the signal number for SIGWINCH - 0 if not supported
(the numeric value is not the same across unix-systems)"
%{ /* NOCONTEXT */
#if defined(SIGWINCH)
return _MKSMALLINT(SIGWINCH);
#else
return _MKSMALLINT(0);
#endif
%}
!
sigLOST
"return the signal number for SIGLOST - 0 if not supported
(the numeric value is not the same across unix-systems)"
%{ /* NOCONTEXT */
#if defined(SIGLOST)
return _MKSMALLINT(SIGLOST);
#else
return _MKSMALLINT(0);
#endif
%}
!
sigUSR1
"return the signal number for SIGUSR1 - 0 if not supported
(the numeric value is not the same across unix-systems)"
%{ /* NOCONTEXT */
#if defined(SIGUSR1)
return _MKSMALLINT(SIGUSR1);
#else
return _MKSMALLINT(0);
#endif
%}
!
sigUSR2
"return the signal number for SIGUSR2 - 0 if not supported
(the numeric value is not the same across unix-systems)"
%{ /* NOCONTEXT */
#if defined(SIGUSR2)
return _MKSMALLINT(SIGUSR2);
#else
return _MKSMALLINT(0);
#endif
%}
!
sigMSG
"return the signal number for SIGMSG - 0 if not supported
(seems to be an AIX special)"
%{ /* NOCONTEXT */
#if defined(SIGMSG)
return _MKSMALLINT(SIGMSG);
#else
return _MKSMALLINT(0);
#endif
%}
!
sigPWR
"return the signal number for SIGPWR - 0 if not supported
(not available on all systems)"
%{ /* NOCONTEXT */
#if defined(SIGPWR)
return _MKSMALLINT(SIGPWR);
#else
return _MKSMALLINT(0);
#endif
%}
!
sigMIGRATE
"return the signal number for SIGMIGRATE - 0 if not supported
(seems to be an AIX special)"
%{ /* NOCONTEXT */
#if defined(SIGMIGRATE)
return _MKSMALLINT(SIGMIGRATE);
#else
return _MKSMALLINT(0);
#endif
%}
!
sigPRE
"return the signal number for SIGPRE - 0 if not supported
(seems to be an AIX special)"
%{ /* NOCONTEXT */
#if defined(SIGPRE)
return _MKSMALLINT(SIGPRE);
#else
return _MKSMALLINT(0);
#endif
%}
!
sigGRANT
"return the signal number for SIGGRANT - 0 if not supported
(seems to be an AIX special)"
%{ /* NOCONTEXT */
#if defined(SIGGRANT)
return _MKSMALLINT(SIGGRANT);
#else
return _MKSMALLINT(0);
#endif
%}
!
sigRETRACT
"return the signal number for SIGRETRACT - 0 if not supported
(seems to be an AIX special)"
%{ /* NOCONTEXT */
#if defined(SIGRETRACT)
return _MKSMALLINT(SIGRETRACT);
#else
return _MKSMALLINT(0);
#endif
%}
!
sigSOUND
"return the signal number for SIGSOUND - 0 if not supported
(seems to be an AIX special)"
%{ /* NOCONTEXT */
#if defined(SIGSOUND)
return _MKSMALLINT(SIGSOUND);
#else
return _MKSMALLINT(0);
#endif
%}
!
sigSAK
"return the signal number for SIGSAK - 0 if not supported
(seems to be an AIX special)"
%{ /* NOCONTEXT */
#if defined(SIGSAK)
return _MKSMALLINT(SIGSAK);
#else
return _MKSMALLINT(0);
#endif
%}
!
sigDANGER
"return the signal number for SIGDANGER - 0 if not supported
(seems to be an AIX special)"
%{ /* NOCONTEXT */
#if defined(SIGSAK)
return _MKSMALLINT(SIGSAK);
#else
return _MKSMALLINT(0);
#endif
%}
! !
!OperatingSystem class methodsFor:'interrupts & signals'!
nameForSignal:aSignalNumber
"for a given Unix signalnumber, return a descriptive string"
aSignalNumber == self sigHUP ifTrue:[^ 'hangup'].
aSignalNumber == self sigINT ifTrue:[^ 'interrupt'].
aSignalNumber == self sigKILL ifTrue:[^ 'kill'].
aSignalNumber == self sigQUIT ifTrue:[^ 'quit'].
aSignalNumber == self sigILL ifTrue:[^ 'illegal instruction'].
aSignalNumber == self sigTRAP ifTrue:[^ 'trap'].
aSignalNumber == self sigABRT ifTrue:[^ 'abort'].
aSignalNumber == self sigIOT ifTrue:[^ 'iot trap'].
aSignalNumber == self sigEMT ifTrue:[^ 'emt trap'].
aSignalNumber == self sigFP ifTrue:[^ 'fp exception'].
aSignalNumber == self sigBUS ifTrue:[^ 'bus error'].
aSignalNumber == self sigSEGV ifTrue:[^ 'segmentation violation'].
aSignalNumber == self sigSYS ifTrue:[^ 'bad system call'].
aSignalNumber == self sigPIPE ifTrue:[^ 'broken pipe'].
aSignalNumber == self sigALRM ifTrue:[^ 'alarm timer'].
aSignalNumber == self sigTERM ifTrue:[^ 'termination'].
aSignalNumber == self sigSTOP ifTrue:[^ 'stop'].
aSignalNumber == self sigTSTP ifTrue:[^ 'tty stop'].
aSignalNumber == self sigCONT ifTrue:[^ 'continue'].
aSignalNumber == self sigCHLD ifTrue:[^ 'child death'].
aSignalNumber == self sigTTIN ifTrue:[^ 'background tty input'].
aSignalNumber == self sigTTOU ifTrue:[^ 'background tty output'].
aSignalNumber == self sigIO ifTrue:[^ 'io available'].
aSignalNumber == self sigXCPU ifTrue:[^ 'cpu time expired'].
aSignalNumber == self sigXFSZ ifTrue:[^ 'file size limit'].
aSignalNumber == self sigVTALRM ifTrue:[^ 'virtual alarm timer'].
aSignalNumber == self sigPROF ifTrue:[^ 'profiling timer'].
aSignalNumber == self sigWINCH ifTrue:[^ 'winsize changed'].
aSignalNumber == self sigLOST ifTrue:[^ 'resource lost'].
aSignalNumber == self sigUSR1 ifTrue:[^ 'user signal 1'].
aSignalNumber == self sigUSR2 ifTrue:[^ 'user signal 2'].
aSignalNumber == self sigMSG ifTrue:[^ 'HFT message'].
aSignalNumber == self sigPWR ifTrue:[^ 'power-fail'].
aSignalNumber == self sigPRE ifTrue:[^ 'programming exception'].
aSignalNumber == self sigGRANT ifTrue:[^ 'HFT access wanted'].
aSignalNumber == self sigRETRACT ifTrue:[^ 'HFT access relinquish'].
aSignalNumber == self sigSOUND ifTrue:[^ 'HFT sound complete'].
aSignalNumber == self sigDANGER ifTrue:[^ 'low on paging space'].
"notice: many systems map SIGPOLL and/or SIGUSR onto SIGIO
therefore, keep SIGIO always above the two below"
aSignalNumber == self sigPOLL ifTrue:[^ 'io available'].
aSignalNumber == self sigURG ifTrue:[^ 'urgent'].
^ 'unknown signal'
"
OperatingSystem nameForSignal:9
OperatingSystem nameForSignal:(OperatingSystem sigPOLL)
"
!
interruptPending
"return true, if an interrupt is pending. The returned value is
invalid if interrupts are not currently blocked, since otherwise
the interrupt is usually already handled before arriving here,
or may be served while returning from here."
%{ /* NOCONTEXT */
extern OBJ __INTERRUPTPENDING();
RETURN ( __INTERRUPTPENDING() );
%}
!
blockInterrupts
"disable interrupt processing - if disabled, incoming
interrupts will be registered and handled as soon as
interrupts are reenabled by OperatingSystemclass>>unblockInterrupts.
Returns the previous blocking status i.e. true if interrupts
where already blocked. You need this information for proper
unblocking, in case of nested block/unblock calls."
%{ /* NOCONTEXT */
extern OBJ __BLOCKINTERRUPTS();
return ( __BLOCKINTERRUPTS() );
%}
!
unblockInterrupts
"enable interrupt processing - if any interrupts are pending,
these will be handled immediately.
When unblocking interrupts, take care of nested block/unblock
calls - you must only unblock after a blockcall if they where
really not blocked before. See OperatingSystemclass>>blockInterrupts."
%{
extern void __UNBLOCKINTERRUPTS();
__UNBLOCKINTERRUPTS();
%}
!
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)) {
signal(_intVal(signalNumber), SIG_IGN);
RETURN (self);
}
%}.
"
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
"
!
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)) {
signal(_intVal(signalNumber), SIG_DFL);
RETURN (self);
}
%}.
"
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
"
!
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)
extern void __signalUserInterrupt();
#endif
#ifdef SIGFPE
extern void __signalFpExceptionInterrupt();
#endif
#ifdef SIGIO
extern void __signalIoInterrupt();
#endif
#ifdef CHILD_SIGNAL
extern void __signalChildInterrupt();
#endif
#ifdef SIGPIPE
extern void __signalPIPEInterrupt();
#endif
#ifdef SIGBUS
extern void __signalBUSInterrupt();
#endif
#ifdef SIGSEGV
extern void __signalSEGVInterrupt();
#endif
#ifdef SIGALRM
extern void __signalTimerInterrupt();
#endif
extern void __signalInterrupt();
void (*handler)();
#ifdef HAS_SIGACTION
struct sigaction act, oldAct;
#endif
if (__isSmallInteger(signalNumber)
&& ((sigNr = _intVal(signalNumber)) > 0)
&& (sigNr <= SIG_LIMIT)) {
/*
* 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) {
#ifdef SIGINT
case SIGINT:
#endif
#ifdef SIGQUIT
case SIGQUIT:
#endif
#if defined(SIGINT) || defined(SIGQUIT)
handler = __signalUserInterrupt;
break;
#endif
#ifdef SIGFPE
case SIGFPE:
handler = __signalFpExceptionInterrupt;
break;
#endif
#ifdef SIGPIPE
case SIGPIPE:
handler = __signalPIPEInterrupt;
break;
#endif
#ifdef SIGBUS
case SIGBUS:
handler = __signalBUSInterrupt;
break;
#endif
#ifdef SIGSEGV
case SIGSEGV:
handler = __signalSEGVInterrupt;
break;
#endif
#ifdef SIGIO
case SIGIO:
handler = __signalIoInterrupt;
break;
#endif
#ifdef CHILD_SIGNAL
case CHILD_SIGNAL:
handler = __signalChildInterrupt;
break;
#endif
#ifdef SIGALRM
case SIGALRM:
handler = __signalTimerInterrupt;
break;
#endif
default:
handler = __signalInterrupt;
break;
}
#ifdef HAS_SIGACTION
act.sa_flags = SA_RESTART;
sigemptyset(&act.sa_mask);
act.sa_handler = handler;
sigaction(sigNr, &act, &oldAct);
#else
signal(sigNr, handler);
#endif
/*
* maybe, we should return the old enable-status
* as boolean here ...
*/
RETURN (self);
}
%}.
"
this error is triggered on non-integer argument, or
if the signal number is not in the valid range (1..NSIG)
"
self primitiveFailed
!
enableUserInterrupts
"enable userInterrupt (^C) handling;
when enabled, ^C in the terminal window will send the message
'userInterrupt' to the UserInterruptHandler object."
^ self enableSignal:(self sigINT)
!
enableQuitInterrupts
"enable quitInterrupt (usually ^\) handling, and make it a userInterrupt.
(the default will dump core and exit - which is not a good idea for
end-user applications ...)"
^ self enableSignal:(self sigQUIT)
!
enableAbortInterrupts
"enable abort signalhandling, and make it a regular signalInterrupt.
(the default will dump core and exit - which is not a good idea for
end-user applications ...).
This is especially useful, if linked-in C-libraries call abort() ..."
^ self enableSignal:(self sigABRT)
!
disableUserInterrupts
"disable userInterrupt processing;
when disabled, no ^C processing takes place.
Use this only for debugged stand-alone applications, since
no exit to the debugger is possible with user interrupts disabled.
Be WARNED."
^ self disableSignal:(self sigINT)
!
enableFpExceptionInterrupts
"enable floating point exception interrupts (if the
architecture supports it).
after enabling, fpu-exceptions will send the message
'fpuExceptionInterrupt' to the FPUExceptionInterruptHandler object."
^ self enableSignal:(self sigFP)
!
enableHardSignalInterrupts
"enable hard signal exception interrupts (trap, buserror & segm. violation).
after enabling, these exceptions will send the message
'signalInterrupt' to the SignalInterruptHandler object."
%{ /* NOCONTEXT */
#ifdef SIGPIPE
extern void __signalPIPEInterrupt();
#endif
#ifdef SIGBUS
extern void __signalBUSInterrupt();
#endif
#ifdef SIGSEGV
extern void __signalSEGVInterrupt();
#endif
#ifdef SIGPIPE
signal(SIGPIPE, __signalPIPEInterrupt);
#endif
#ifdef SIGBUS
signal(SIGBUS, __signalBUSInterrupt);
#endif
#ifdef SIGSEGV
signal(SIGSEGV, __signalSEGVInterrupt);
#endif
%}
!
enableIOInterruptsOn:fd
"turn on IO interrupts for a filedescriptor"
%{ /* NOCONTEXT */
int ret, flags, f;
extern void __signalIoInterrupt();
static int firstCall = 1;
#if defined(F_GETFL) && defined(F_SETFL)
# if defined(FASYNC)
if (__isSmallInteger(fd)) {
if (firstCall) {
firstCall = 0;
# ifdef SIGIO
signal(SIGIO, __signalIoInterrupt);
# endif
# ifdef SIGPOLL
signal(SIGPOLL, __signalIoInterrupt);
# endif
# ifdef SIGURG
signal(SIGURG, __signalIoInterrupt);
# endif
}
f = _intVal(fd);
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;
}
RETURN ( _MKSMALLINT(ret) );
}
# endif
#endif
%}.
"
this error is triggered on non-integer argument
or if the system does not support SIGIO
"
self primitiveFailed
!
disableIOInterruptsOn:fd
"turn off IO interrupts for a filedescriptor"
%{ /* NOCONTEXT */
int ret, flags, f;
#if defined(F_GETFL) && defined(F_SETFL)
# if defined(FASYNC)
if (__isSmallInteger(fd)) {
f = _intVal(fd);
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;
}
RETURN ( _MKSMALLINT(ret) );
}
# endif
#endif
%}.
"
this error is triggered on non-integer argument
or if the OS does not support IO interrupts.
"
self primitiveFailed
!
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)
!
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 */
extern void __spyInterrupt();
#if defined(ITIMER_VIRTUAL)
struct itimerval dt;
# ifndef xxxSYSV4
# if defined(BSD) || defined(HAS_SIGSETMASK)
sigsetmask(0);
# endif
# endif
# ifdef SIGVTALRM
signal(SIGVTALRM, __spyInterrupt);
# else
signal(SIGALRM, __spyInterrupt);
# endif
dt.it_interval.tv_sec = 0;
dt.it_interval.tv_usec = 0;
dt.it_value.tv_sec = 0;
dt.it_value.tv_usec = 1000; /* 1000 Hz */
setitimer(ITIMER_VIRTUAL, &dt, 0);
RETURN (true);
#endif
%}
.
^ false
!
stopSpyTimer
"stop spy timing - disable spy timer.
OBSOLETE: the new messageTally runs as a high prio process, not using
spy interrupts."
%{ /* NOCONTEXT */
#if defined(ITIMER_VIRTUAL)
struct itimerval dt;
dt.it_interval.tv_sec = 0;
dt.it_interval.tv_usec = 0;
dt.it_value.tv_sec = 0;
dt.it_value.tv_usec = 0;
setitimer(ITIMER_VIRTUAL, &dt, 0);
RETURN (true);
#endif
%}
.
^ false
!
enableTimer:millis
"trigger a timerInterrupt, to be signalled after some (real) time."
%{ /* NOCONTEXT */
extern void __signalTimerInterrupt();
#if defined(ITIMER_REAL)
struct itimerval dt;
# ifndef xxxSYSV4
# if defined(BSD) || defined(HAS_SIGSETMASK)
sigsetmask(0);
# endif
# endif
signal(SIGALRM, __signalTimerInterrupt);
dt.it_interval.tv_sec = 0;
dt.it_interval.tv_usec = 0;
dt.it_value.tv_sec = _intVal(millis) / 1000;
dt.it_value.tv_usec = (_intVal(millis) % 1000) * 1000;
setitimer(ITIMER_REAL, &dt, 0);
RETURN (true);
#endif
%}
.
^ false
!
disableTimer
"disable timer interrupt"
%{ /* NOCONTEXT */
#if defined(ITIMER_REAL)
struct itimerval dt;
dt.it_interval.tv_sec = 0;
dt.it_interval.tv_usec = 0;
dt.it_value.tv_sec = 0;
dt.it_value.tv_usec = 0;
setitimer(ITIMER_REAL, &dt, 0);
RETURN (true);
#endif
%}
.
^ false
!
operatingSystemSignal:signalNumber
"return the signal to be raised when an
operatingSystem-signal occurs, or nil"
OSSignals notNil ifTrue:[
^ OSSignals at:signalNumber ifAbsent:[nil]
].
^ nil
!
operatingSystemSignal:signalNumber install:aSignal
"install a signal to be raised when an operatingSystem-signal occurs"
OSSignals isNil ifTrue:[
OSSignals := Array new:32
].
OSSignals at:signalNumber put:aSignal
!
sendSignal:signalNumber to:processId
"send a unix signal to some process (maybe myself).
Returns false if any error occurred, true otherwise.
Do not confuse UNIX signals with Smalltalk-Signals."
%{
if (__bothSmallInteger(signalNumber, processId)) {
if (kill(__intVal(processId), __intVal(signalNumber)) < 0) {
OperatingSystem_LastErrorNumber = _MKSMALLINT(errno);
RETURN ( false );
}
RETURN ( true );
}
%}.
self primitiveFailed
! !
!OperatingSystem class methodsFor:'time and date'!
getTimeLow
"return low 16 bits of current time.
OBSOLETE: Dont use this method, use getTimeParts.
This method will not always return the correct time
if used together with getTimeHi, since a wrap between
the two getTimeXXX sends could occur.
WARNING: this method will vanish - dont use it."
%{ /* NOCONTEXT */
RETURN ( _MKSMALLINT(time(0) & 0xFFFF) );
%}
"OperatingSystem getTimeLow"
!
getTimeHi
"return hi 16 bits of current time.
OBSOLETE: Dont use this method, use getTimeParts.
This method will NOT always return the correct time
if used together with getTimeHi, since a wrap between
the two getTimeXXX sends could occur.
WARNING: this method will vanish - dont use it."
%{ /* NOCONTEXT */
RETURN ( _MKSMALLINT((time(0) >> 16) & 0xFFFF) );
%}
"OperatingSystem getTimeHi"
!
getTimeParts
"return the current time as an array of 2 integers representing
the current time (usually low 16bits / hi 16 bits) in some OS
dependent way.
On unix, the values represent the seconds since 1970, on other
systems this may be different.
WARNING:
for portability, never use these values directly, but pass them
to #computeTimeParts and/or #computeDatePartsOf:, which know how to
interpret them.
Better yet, use Date>>today and Time>>now for ST-80 compatibility."
|low hi|
%{
int now;
now = time(0);
hi = _MKSMALLINT((now >> 16) & 0xFFFF);
low = _MKSMALLINT(now & 0xFFFF);
%}
.
^ Array with:low with:hi
"OperatingSystem getTimeParts"
!
getTimeInto:aBlock
"evaluate the argument aBlock, passing the time-parts of
the current time as arguments.
See comment in 'OperatingSystem>>getTimeParts' on what to
do with those parts."
|low hi|
%{
int now;
now = time(0);
hi = _MKSMALLINT((now >> 16) & 0xFFFF);
low = _MKSMALLINT(now & 0xFFFF);
%}
.
aBlock value:low value:hi
"OperatingSystem getTimeInto:[:low :hi | low printNewline. hi printNewline]"
!
getTime
"return current Time in some OS dependent representation.
(on unix, its the seconds since 1970).
WARNING: this is OperatingSystem dependent - for portable code,
use getTimeParts and compute*PartsOf:and:for:.
Better yet, use Date>>today or Time>>now for fully ST-80 compatible code."
OperatingSystem getTimeInto:[:low :hi | ^ hi*16r10000 + low]
"
OperatingSystem getTime
"
!
computeDatePartsOf:timeParts for:aBlock
"compute year, month and day from the ostime in timeParts,
and evaluate the argument, a 3-arg block with these.
Conversion is to localtime including any daylight saving adjustments."
^ self computeDatePartsOf:(timeParts at:1) and:(timeParts at:2) for:aBlock
!
computeTimePartsOf:timeParts for:aBlock
"compute hour, minute and seconds from the ostime in timeParts,
and evaluate the argument, a 3-arg block with these.
Conversion is to localtime including any daylight saving adjustments."
^ self computeTimePartsOf:(timeParts at:1) and:(timeParts at:2) for:aBlock
!
computeDatePartsOf:timeLow and:timeHi for:aBlock
"compute year, month and day from the time-parts timeLow and
timeHi and evaluate the argument, a 3-arg block with these.
Conversion is to localtime including any daylight saving adjustments.
This method was added to avoid LargeInteger arithmetic and to be
independent of how the OperatingSystem represents time;
the time-parts expected are those returned by getTimeParts."
|year month day|
((timeLow isMemberOf:SmallInteger) and:[timeHi isMemberOf:SmallInteger])
ifFalse:[
^ self primitiveFailed
].
%{
struct tm *tmPtr;
long t;
t = (_intVal(timeHi) << 16) | _intVal(timeLow);
tmPtr = localtime(&t);
year = _MKSMALLINT(tmPtr->tm_year + 1900);
month = _MKSMALLINT(tmPtr->tm_mon + 1);
day = _MKSMALLINT(tmPtr->tm_mday);
%}
.
aBlock value:year value:month value:day
!
computeTimePartsOf:timeLow and:timeHi for:aBlock
"compute hours, minutes and seconds from the time-parts timeLow and
timeHi and evaluate the argument, a 3-arg block with these.
Conversion is to localtime including any daylight saving adjustments.
This method was added to avoid LargeInteger arithmetic and to be
independent of how the OperatingSystem represents time;
the time-parts expected are those returned by getTimeParts."
|hours minutes seconds|
((timeLow isMemberOf:SmallInteger) and:[timeHi isMemberOf:SmallInteger])
ifFalse:[
^ self primitiveFailed
].
%{
struct tm *tmPtr;
long t;
t = (_intVal(timeHi) << 16) | _intVal(timeLow);
tmPtr = localtime(&t);
hours = _MKSMALLINT(tmPtr->tm_hour);
minutes = _MKSMALLINT(tmPtr->tm_min);
seconds = _MKSMALLINT(tmPtr->tm_sec);
%}
.
aBlock value:hours value:minutes value:seconds
!
computeTimeAndDateFrom:timeParts
"given an Array containing the OS-dependent time, return an Array
containing year, month, day, hour, minute and seconds.
Conversion is to localtime including any daylight saving adjustments."
|low hi year month day hours minutes seconds ret|
low := timeParts at:1.
hi := timeParts at:2.
%{
struct tm *tmPtr;
long t;
if (__bothSmallInteger(low, hi)) {
t = (_intVal(hi) << 16) | _intVal(low);
tmPtr = localtime(&t);
hours = _MKSMALLINT(tmPtr->tm_hour);
minutes = _MKSMALLINT(tmPtr->tm_min);
seconds = _MKSMALLINT(tmPtr->tm_sec);
year = _MKSMALLINT(tmPtr->tm_year + 1900);
month = _MKSMALLINT(tmPtr->tm_mon + 1);
day = _MKSMALLINT(tmPtr->tm_mday);
}
%}.
year notNil ifTrue:[
"I would love to have SELF-like inline objects ..."
ret := Array new:6.
ret at:1 put:year.
ret at:2 put:month.
ret at:3 put:day.
ret at:4 put:hours.
ret at:5 put:minutes.
ret at:6 put:seconds.
^ ret
].
^ self primitiveFailed
!
computeTimePartsFromYear:y month:m day:d hour:h minute:min seconds:s
"return an Array containing the OS-dependent time for the given
time and day. The arguments are assumed to be in localtime including
any daylight saving adjustings."
|low hi|
%{
struct tm tm;
long t;
if (__bothSmallInteger(y, m)
&& __bothSmallInteger(d, h)
&& __bothSmallInteger(min, s)) {
tm.tm_hour = __intVal(h);
tm.tm_min = __intVal(min);
tm.tm_sec = __intVal(s);
tm.tm_year = __intVal(y) - 1900;
tm.tm_mon = __intVal(m) - 1;
tm.tm_mday = __intVal(d);
tm.tm_isdst = -1;
t = mktime(&tm);
low = _MKSMALLINT(t & 0xFFFF);
hi = _MKSMALLINT((t >> 16) & 0xFFFF);
}
%}.
low notNil ifTrue:[
^ Array with:low with:hi
].
^ self primitiveFailed
!
maximumMillisecondTimeDelta
"this returns the maximum delta supported by millisecondCounter
based methods. The returned value is half the value at which the
timer wraps."
%{ /* NOCONTEXT */
RETURN ( _MKSMALLINT(0x0FFFFFFF) );
%}
!
getMillisecondTime
"This returns the millisecond timers value.
The range is limited to 0..1fffffff (i.e. the SmallInteger range) to avoid
LargeInteger arithmetic when doing timeouts and delays.
SInce this value is wrapping around in regular intervals, this can only be used for
short relative time deltas.
Use the millisecondTimeXXX:-methods to compare and add time deltas - these know about the wrap.
BAD DESIGN:
This should be changed to return some instance of RelativeTime,
and these computations moved there.
Dont use this method in application code since it is an internal (private)
interface. For compatibility with ST-80, use Time millisecondClockValue.
"
%{ /* NOCONTEXT */
long t;
#if !defined(HAS_GETTIMEOFDAY) && defined(SYSV) && defined(HZ)
/*
* sys5 time
*/
long ticks;
struct tms tb;
ticks = times(&tb);
t = (ticks * 1000) / HZ;
#else /* assume HAS_GETTIMEOFDAY */
/*
* bsd time
*/
struct timeval tb;
struct timezone tzb;
gettimeofday(&tb, &tzb);
t = tb.tv_sec*1000 + tb.tv_usec/1000;
#endif
RETURN ( _MKSMALLINT(t & 0x1FFFFFFF) );
%}
!
millisecondTimeDeltaBetween:msTime1 and:msTime2
"subtract two millisecond times (such as returned getMillisecondTime)
and return the difference. Since milli-times wrap (at 16r01FFFFFFF),
some special handling is built-in here.
The returned value is msTime1 - msTime2. The returned value is invalid
if the delta is >= 0x10000000.
This should really be moved to some RelativeTime class."
(msTime1 > msTime2) ifTrue:[
^ msTime1 - msTime2
].
^ msTime1 + 16r10000000 - msTime2
"
OperatingSystem millisecondTimeAdd:16r0FFFFFFF and:1
OperatingSystem millisecondTimeAdd:16r0FFFFFFF and:(16 / 3)
OperatingSystem millisecondTimeAdd:16r0FFFFFFF and:1000
OperatingSystem millisecondTimeDeltaBetween:0 and:16r0FFFFFFF
OperatingSystem millisecondTimeDeltaBetween:(13/3) and:16r0FFFFFFF
OperatingSystem millisecondTimeDeltaBetween:999 and:16r0FFFFFFF
OperatingSystem millisecondTime:0 isAfter:16r0FFFFFFF
OperatingSystem millisecondTime:(13/3) isAfter:16r0FFFFFFF
OperatingSystem millisecondTime:999 isAfter:16r0FFFFFFF
OperatingSystem millisecondTime:0 isAfter:0
OperatingSystem millisecondTime:(13/3) isAfter:0
OperatingSystem millisecondTime:999 isAfter:0
OperatingSystem millisecondTime:1 isAfter:0
OperatingSystem millisecondTime:(13/3) isAfter:2
OperatingSystem millisecondTime:999 isAfter:900
|t1 t2|
t1 := Time millisecondClockValue.
(Delay forMilliseconds:1) wait.
t2 := Time millisecondClockValue.
OperatingSystem millisecondTimeDeltaBetween:t2 and:t1
"
!
millisecondTime:msTime1 isAfter:msTime2
"return true if msTime1 is after msTime2, false if not.
The two arguments are supposed to be millisecond times
(such as returned getMillisecondTime) which wrap at 16r1FFFFFFF.
This should really be moved to some RelativeTime class."
(msTime1 > msTime2) ifTrue:[
((msTime1 - msTime2) >= 16r10000000) ifTrue:[
^ false
].
^ true
].
((msTime2 - msTime1) > 16r10000000) ifTrue:[
^ true
].
^ false
!
millisecondTimeAdd:msTime1 and:msTime2
"Add two millisecond times (such as returned getMillisecondTime).
The returned value is msTime1 + msTime2 where a wrap occurs at:16r1FFFFFFF.
This should really be moved to some RelativeTime class."
|sum|
sum := msTime1 + msTime2.
(sum > 16r1FFFFFFF) ifTrue:[^ sum - 16r20000000].
(sum < 0) ifTrue:[^ sum + 16r20000000].
^ sum
!
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 := OperatingSystem getMillisecondTime.
then := OperatingSystem millisecondTimeAdd:now and:millis.
[OperatingSystem millisecondTime:then isAfter:now] whileTrue:[
delta := OperatingSystem millisecondTimeDeltaBetween:then and:now.
self selectOnAnyReadable:nil writable:nil exception:nil withTimeOut:delta.
now := OperatingSystem getMillisecondTime.
]
"
OperatingSystem millisecondDelay:2000
"
!
sleep:numberOfSeconds
"cease ANY action for some time. This suspends the whole smalltalk
(unix-) process for some time.
Not really useful since not even low-prio processes and interrupt
handling will run during the sleep.
Use either OperatingSystem>>millisecondDelay: (which makes all
threads sleep, but handles interrupts) or use a Delay (which makes
only the calling thread sleep)."
%{ /* NOCONTEXT */
if (__isSmallInteger(numberOfSeconds)) {
sleep(_intVal(numberOfSeconds));
RETURN ( self );
}
%}.
"
argument not integer
"
self primitiveFailed
"
OperatingSystem sleep:2
"
! !
!OperatingSystem class methodsFor:'waiting for events'!
setBlocking:aBoolean on:fd
"set/clear the blocking attribute - if set (which is the default)
a read on the fileDescriptor will block until data is available.
If cleared, a read operation will immediately return with a value of
nil."
%{ /* NOCONTEXT */
int ret, flags;
int savInt;
#if defined(F_GETFL) && defined(F_SETFL)
# if defined(FNDELAY)
if (__isSmallInteger(fd)) {
flags = fcntl(_intVal(fd), F_GETFL, 0);
if (aBoolean == true) {
ret = fcntl(_intVal(fd), F_SETFL, flags & ~FNDELAY);
} else {
ret = fcntl(_intVal(fd), F_SETFL, flags | FNDELAY);
}
if (ret >= 0) ret = flags;
RETURN ( _MKSMALLINT(ret) );
}
# endif
#endif
%}.
"
fd argument not integer
"
self primitiveFailed
!
readCheck:fd
"return true, if data is available on a filedescriptor (i.e. read
is possible without blocking)"
(self selectOnAnyReadable:(Array with:fd)
writable:nil
exception:nil
withTimeOut:0) == fd
ifTrue:[^ true].
^ false
!
writeCheck:fd
"return true, if filedescriptor can be written without blocking"
(self selectOnAnyReadable:nil
writable:(Array with:fd)
exception:nil
withTimeOut:0) == fd
ifTrue:[^ true].
^ false
!
selectOn:fd withTimeOut:millis
"wait for aFileDesriptor to become ready; timeout after t milliseconds.
Return true, if i/o ok, false if timed-out or interrupted.
With 0 as timeout argument, this can be used to check for availability
of read-data.
Experimental."
^ self selectOnAnyReadable:(Array with:fd)
writable:(Array with:fd)
exception:nil
withTimeOut:millis
!
selectOn:fd1 and:fd2 withTimeOut:millis
"wait for any fd to become ready; timeout after t milliseconds.
A zero timeout-time will immediately return (i.e. poll).
Return fd if i/o ok, nil if timed-out or interrupted.
Obsolete:
This is a leftover method and will vanish."
^ self selectOnAnyReadable:(Array with:fd1 with:fd2)
writable:(Array with:fd1 with:fd2)
exception:nil
withTimeOut:millis
!
selectOnAnyReadable:fdArray withTimeOut:millis
"wait for any fd in fdArray (an Array of integers) to become ready for
reading. Timeout after t milliseconds. An empty set will always wait.
A zero timeout-time will immediately return (i.e. poll).
Return first ready fd if i/o ok, nil if timed-out or interrupted.
Experimental."
^ self selectOnAnyReadable:fdArray
writable:nil
exception:nil
withTimeOut:millis
!
selectOnAny:fdArray withTimeOut:millis
"wait for any fd in fdArray (an Array of integers) to become ready;
timeout after t milliseconds. An empty set will always wait.
Return first ready fd if i/o ok, nil if timed-out or interrupted.
Experimental."
^ self selectOnAnyReadable:fdArray
writable:fdArray
exception:nil
withTimeOut:millis
!
selectOnAnyReadable: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;
int t, f, maxF, i, lX, bX;
struct timeval wt, et;
OBJ fd, retFd;
int ret;
int count;
if (__isSmallInteger(millis)) {
FD_ZERO(&rset);
FD_ZERO(&wset);
FD_ZERO(&eset);
maxF = -1;
if (readFdArray != nil) {
if (! __isArray(readFdArray)) {
goto fail;
}
count = __arraySize(readFdArray);
for (i=0; i<count;i++) {
fd = _ArrayInstPtr(readFdArray)->a_element[i];
if (fd != nil) {
f = _intVal(fd);
if ((f >= 0) && (f < FD_SETSIZE)) {
FD_SET(f, &rset);
if (f > maxF) maxF = f;
}
}
}
}
if (writeFdArray != nil) {
if (! __isArray(writeFdArray)) {
goto fail;
}
count = __arraySize(writeFdArray);
for (i=0; i<count;i++) {
fd = _ArrayInstPtr(writeFdArray)->a_element[i];
if (fd != nil) {
f = _intVal(fd);
if ((f >= 0) && (f < FD_SETSIZE)) {
FD_SET(f, &wset);
if (f > maxF) maxF = f;
}
}
}
}
if (exceptFdArray != nil) {
if (! __isArray(exceptFdArray)) {
goto fail;
}
count = __arraySize(exceptFdArray);
for (i=0; i<count;i++) {
fd = _ArrayInstPtr(exceptFdArray)->a_element[i];
if (fd != nil) {
f = _intVal(fd);
if ((f >= 0) && (f < FD_SETSIZE)) {
FD_SET(f, &eset);
if (f > maxF) maxF = f;
}
}
}
}
t = _intVal(millis);
wt.tv_sec = t / 1000;
wt.tv_usec = (t % 1000) * 1000;
/*
* make certain, that interrupt gets us out of the select
*/
__BEGIN_INTERRUPTABLE__
errno = 0;
do {
ret = select(maxF+1, &rset, &wset, &eset, &wt);
} 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) );
}
}
}
/*
* return nil (means time expired)
*/
RETURN ( nil );
}
fail: ;
%}.
"
timeout argument not integer,
or any fd-array nonNil and not an array
"
self primitiveFailed
! !
!OperatingSystem class methodsFor:'executing commands'!
fork
"fork a new (HEAVY-weight) unix process.
Dont confuse this with Block>>fork, which creates
lightweight smalltalk processes. This method will return
0 to the child process, abd a non-zero number (which is the childs
unix-process-id) to the parent (original) process."
%{ /* NOCONTEXT */
int pid;
pid = fork();
RETURN ( _MKSMALLINT(pid) );
%}
"
|id|
id := OperatingSystem fork.
id == 0 ifTrue:[
OperatingSystem exit
]
"
!
exec:aPath withArguments:argArray
"execute the unix command specified by the argument, aPath, with
arguments in argArray (no arguments, if nil).
If successful, this method does not return and smalltalk is gone.
If not sucessfull, false is returned. Normal use is with fork."
%{
char **argv;
int nargs, i;
OBJ arg;
if (__isString(aPath) && ((argArray == nil) || __isArray(argArray))) {
nargs = argArray == nil ? 0 : _arraySize(argArray);
argv = (char **) malloc(sizeof(char *) * (nargs + 1));
if (argv) {
for (i=0; i < nargs; i++) {
arg = _ArrayInstPtr(argArray)->a_element[i];
if (__isString(arg)) {
argv[i] = (char *) _stringVal(arg);
}
}
argv[i] = NULL;
execv(_stringVal(aPath), argv);
/* should not be reached */
free(argv);
RETURN ( false );
}
}
%}.
"
path-argument not string or
argArray not an array/nil or
malloc failed
"
^ self primitiveFailed
"
|id|
id := OperatingSystem fork.
id == 0 ifTrue:[
"I am the child"
OperatingSystem exec:'/bin/ls' withArguments:#('ls' '/tmp').
"not reached"
]
"
!
executeCommand:aCommandString
"execute the unix command specified by the argument, aCommandString.
Return true if successful, false otherwise.
The return value of the system()-call is available in the variable
LastExecStatus (which is zero after successful execution); this value
consists of the reason for termination (0=normal) in the upper 8bits and,
iff it was a normal return, the value passed to exit() in the low 8bits."
%{
int status;
if (__isString(aCommandString)) {
/*
* ST/X provides a modified (fixed) implementation of the
* system() libc-function, which is interruptable ...
*/
# ifdef WANT_SYSTEM
__BEGIN_INTERRUPTABLE__
status = mySystem((char *) _stringVal(aCommandString));
__END_INTERRUPTABLE__
# else
status = system((char *) _stringVal(aCommandString));
# endif
OperatingSystem_LastExecStatus = _MKSMALLINT(status);
if (status == 0) {
RETURN ( true );
}
OperatingSystem_LastErrorNumber = _MKSMALLINT(errno);
RETURN ( false );
}
%}.
self primitiveFailed
"
OperatingSystem executeCommand:'pwd'.
OperatingSystem lastExecStatus printNL.
OperatingSystem executeCommand:'ls -l'.
OperatingSystem lastExecStatus printNL.
OperatingSystem executeCommand:'invalidCommand'.
(OperatingSystem lastExecStatus printStringRadix:16) printNL.
OperatingSystem executeCommand:'rm /tmp/foofoofoofoo'.
(OperatingSystem lastExecStatus printStringRadix:16) printNL.
"
!
pathOfCommand:aCommand
"find where aCommand's executable file is"
|path f|
path := self getEnvironment:'PATH'.
path notNil ifTrue:[
(path asCollectionOfSubstringsSeparatedBy:$:) do:[:path |
(f := (path asFilename construct:aCommand)) isExecutable ifTrue:[
^ f pathName
]
].
].
^ nil
"
OperatingSystem pathOfCommand:'fooBar'
OperatingSystem pathOfCommand:'ls'
OperatingSystem pathOfCommand:'cvs'
"
!
canExecuteCommand:aCommandString
"return true, if the OS can execute aCommand."
|cmd|
cmd := aCommandString asCollectionOfWords first.
^ (self pathOfCommand:cmd) notNil
"
OperatingSystem canExecuteCommand:'fooBar'
OperatingSystem canExecuteCommand:'ls'
OperatingSystem canExecuteCommand:'cvs'
"
"Created: 4.11.1995 / 19:13:54 / cg"
! !
!OperatingSystem class methodsFor:'file access'!
getCharacter
"read a character from keyboard -
this is a blocking low-level read, provided for debugger
and fatal conditions. Use Stdin or (even better) the event
mechanisms provided."
%{ /* NOCONTEXT */
RETURN ( _MKSMALLINT(getchar()) );
%}
!
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)"
^ $/
!
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)"
^ '..'
!
caseSensitiveFilenames
"return true, if the OS has caseSensitive file naming.
On MSDOS, this will return false. Since this is for Unix, we
return true."
^ true
!
baseNameOf:aPathString
"return the baseName of the argument, aPathString
- thats the file/directory name without leading parent-dirs
(i.e. OperatingSystem baseNameOf:'/usr/lib/st/file' -> 'file'
and OperatingSystem baseNameOf:'/usr/lib' -> lib).
This method does not check if the path is valid
(i.e. if these directories really exist & is readable)."
|prev index sep|
sep := self fileSeparator.
((aPathString size == 1) and:[(aPathString at:1) == sep]) ifTrue:[
^ aPathString
].
prev := 1.
[true] whileTrue:[
index := aPathString indexOf:sep startingAt:prev.
index == 0 ifTrue:[
^ aPathString copyFrom:prev
].
prev := index + 1
]
"
OperatingSystem baseNameOf:'/fee/foo/bar'
OperatingSystem baseNameOf:'foo/bar'
OperatingSystem baseNameOf:'../../foo/bar'
OperatingSystem baseNameOf:'hello'
"
!
directoryNameOf:aPathString
"return the directoryName of the argument, aPath
- thats the name of the directory where aPath is
(i.e. OperatingSystem directoryNameOf:'/usr/lib/st/file' -> '/usr/lib/st'
and OperatingSystem directoryNameOf:'/usr/lib' -> /usr').
This method does not check if the path is valid
(i.e. if these directories really exist & are readable)."
|last index sep sepString|
sep := self fileSeparator.
sepString := sep asString.
(aPathString = sepString) ifTrue:[
"
the trivial '/' case
"
^ aPathString
].
(aPathString startsWith:sepString) ifFalse:[
(aPathString endsWith:sepString) ifTrue:[
^ aPathString copyTo:(aPathString size - 1)
].
].
last := 1.
[true] whileTrue:[
index := aPathString indexOf:sep startingAt:(last + 1).
index == 0 ifTrue:[
(last == 1) ifTrue:[
(aPathString startsWith:sepString) ifTrue:[
^ sepString
].
^ '.'
].
"
(aPathString startsWith:sepString) ifFalse:[
(aPathString startsWith:('..' , sepString)) ifFalse:[
^ './' , (aPathString copyTo:(last - 1))
]
].
"
^ aPathString copyTo:(last - 1)
].
last := index.
]
"
OperatingSystem directoryNameOf:'/fee/foo/bar'
OperatingSystem directoryNameOf:'foo/bar'
OperatingSystem directoryNameOf:'../../foo/bar'
OperatingSystem directoryNameOf:'bar'
OperatingSystem directoryNameOf:'/bar'
"
!
primPathNameOf:pathName
"return the pathName of the argument, aPathString,
- thats the full pathname of the directory, starting at '/'.
This method here returns nil, if the OS does not provide a
realPath library function.
Notice: if symbolic links are involved, the result may look different
from what you expect."
|path|
"some systems have a convenient function for this ..."
%{ /* STACK: 16000 */
#ifdef HAS_REALPATH
char nameBuffer[MAXPATHLEN + 1];
if (__isString(pathName)) {
if (realpath(_stringVal(pathName), nameBuffer)) {
RETURN ( _MKSTRING(nameBuffer COMMA_CON) );
}
}
#endif
%}.
^ nil
!
compressPath:pathName
"return the pathName compressed - that is, remove all ..-entries
and . entries. This does not always (in case of symbolic links)
return the true pathName and is therefore used as a fallback
if realPath and popen failed."
|names|
names := pathName
asCollectionOfSubstringsSeparatedBy:self fileSeparator.
names := names asOrderedCollection.
"
cut off initial double-slashes
"
[names startsWith:#('' '')] whileTrue:[
names removeFirst.
].
"
cut off double-slashes at end
"
[names endsWith:#('')] whileTrue:[
names removeLast.
].
"
cut off current-dir at beginning
"
[(names size >= 2) and:[names startsWith:#('.')]] whileTrue:[
names removeFirst.
].
"
cut off parent-dirs at end
"
[(names size > 2)
and:[(names endsWith:#('..'))
and:[((names at:(names size - 1)) startsWith:'.') not ]]] whileTrue:[
names removeLast; removeLast
].
^ names asStringWith:self fileSeparator
from:1
to:names size
compressTabs:false final:nil
"
OperatingSystem compressPath:'./..'
OperatingSystem compressPath:'/foo/bar/baz/..'
OperatingSystem compressPath:'foo/bar/baz/..'
OperatingSystem compressPath:'foo/bar/baz/../'
OperatingSystem compressPath:'foo/bar/baz/..///'
OperatingSystem compressPath:'///foo/bar/baz/..///'
"
!
pathNameOf:pathName
"return the pathName of the argument, aPathString,
- thats the full pathname of the directory, starting at '/'.
This method needs the path to be valid
(i.e. all directories must exist, be readable and executable).
Notice: if symbolic links are involved, the result may look different
from what you expect."
|p path command|
"some systems have a convenient function for this ..."
path := self primPathNameOf:pathName.
path isNil ifTrue:[
(self isValidPath:pathName) ifFalse:[^ pathName].
(SlowFork==true or:[ForkFailed]) ifFalse:[
PipeStream openErrorSignal handle:[:ex |
ForkFailed := true.
'OS: cannot fork/popen' errorPrintNL.
ex return.
] do:[
"have to fall back ..."
command := 'cd ' , pathName , '; pwd'.
p := PipeStream readingFrom:command.
].
(p isNil or:[p atEnd]) ifTrue:[
('OS: PipeStream for <' , command , '> failed') errorPrintNL.
] ifFalse:[
path := p nextLine.
p close.
].
].
path isNil ifTrue:[
path := pathName
].
(SlowFork==true or:[ForkFailed]) ifTrue:[
path := self compressPath:path
]
].
^ path.
"
OperatingSystem pathNameOf:'.'
OperatingSystem pathNameOf:'../smalltalk/../smalltalk'
OperatingSystem pathNameOf:'../../..'
OperatingSystem pathNameOf:'..'
"
!
isValidPath:aPathName
"return true, if 'aPathName' is a valid path name
(i.e. the file or directory exists)"
%{ /* NOCONTEXT */
struct stat buf;
int ret;
if (__isString(aPathName) || __isSymbol(aPathName) ) {
__BEGIN_INTERRUPTABLE__
do {
ret = stat((char *) _stringVal(aPathName), &buf);
} while ((ret < 0) && (errno == EINTR));
__END_INTERRUPTABLE__
if (ret < 0) {
OperatingSystem_LastErrorNumber = _MKSMALLINT(errno);
RETURN (false);
}
RETURN ( ret ? false : true );
}
%}
.
self primitiveFailed
!
isDirectory:aPathName
"return true, if 'aPathName' is a valid directory path name.
(i.e. exists and is a directory)"
%{ /* NOCONTEXT */
struct stat buf;
int ret;
if (__isString(aPathName)) {
__BEGIN_INTERRUPTABLE__
do {
ret = stat((char *) _stringVal(aPathName), &buf);
} while ((ret < 0) && (errno == EINTR));
__END_INTERRUPTABLE__
if (ret < 0) {
OperatingSystem_LastErrorNumber = _MKSMALLINT(errno);
RETURN ( false );
}
RETURN ( ((buf.st_mode & S_IFMT) == S_IFDIR) ? true : false);
}
%}.
self primitiveFailed
"an alternative implementation would be:
^ (self infoOf:aPathName) at:#type == #directory
"
!
isReadable:aPathName
"return true, if the file/dir 'aPathName' is readable."
%{ /* NOCONTEXT */
int ret;
if (__isString(aPathName)) {
__BEGIN_INTERRUPTABLE__
do {
ret = access(_stringVal(aPathName), R_OK);
} while ((ret < 0) && (errno == EINTR));
__END_INTERRUPTABLE__
if (ret < 0) {
OperatingSystem_LastErrorNumber = _MKSMALLINT(errno);
}
return ((ret == 0) ? true : false);
}
%}.
self primitiveFailed
!
isWritable:aPathName
"return true, if the given file is writable"
%{ /* NOCONTEXT */
int ret;
if (__isString(aPathName)) {
__BEGIN_INTERRUPTABLE__
do {
ret = access(_stringVal(aPathName), W_OK);
} while ((ret < 0) && (errno == EINTR));
__END_INTERRUPTABLE__
if (ret < 0) {
OperatingSystem_LastErrorNumber = _MKSMALLINT(errno);
}
return ((ret == 0) ? true : false);
}
%}.
self primitiveFailed
!
isExecutable:aPathName
"return true, if the given file is executable"
%{ /* NOCONTEXT */
int ret;
if (__isString(aPathName)) {
__BEGIN_INTERRUPTABLE__
do {
ret = access(_stringVal(aPathName), X_OK);
} while ((ret < 0) && (errno == EINTR));
__END_INTERRUPTABLE__
if (ret < 0) {
OperatingSystem_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'
"
!
linkInfoOf:aPathName
"return a dictionary filled with info for the file 'aPathName',
IFF aPathName is a symbolic link. If not, return nil.
The contents of the dictionary gives info about the link itself,
on contrast to #infoOf:, which returns the info of the pointed to file
in case of a symbolic link."
|info type mode uid gid size id atimeLow atimeHi mtimeLow mtimeHi ctimeLow ctimeHi
path|
%{ /* STACK: 1200 */
struct stat buf;
int ret;
char pathBuffer[1024];
if (__isString(aPathName)) {
__BEGIN_INTERRUPTABLE__
do {
ret = lstat((char *) _stringVal(aPathName), &buf);
} while ((ret < 0) && (errno == EINTR));
__END_INTERRUPTABLE__
if (ret < 0) {
OperatingSystem_LastErrorNumber = _MKSMALLINT(errno);
RETURN ( nil );
}
switch (buf.st_mode & S_IFMT) {
default:
RETURN ( nil ); /* not a symbolic link */
case S_IFLNK:
type = @symbol(symbolicLink);
break;
}
mode = _MKSMALLINT(buf.st_mode & 0777);
uid = _MKSMALLINT(buf.st_uid);
gid = _MKSMALLINT(buf.st_gid);
size = _MKSMALLINT(buf.st_size);
id = _MKSMALLINT(buf.st_ino);
atimeLow = _MKSMALLINT(buf.st_atime & 0xFFFF);
atimeHi = _MKSMALLINT((buf.st_atime >> 16) & 0xFFFF);
mtimeLow = _MKSMALLINT(buf.st_mtime & 0xFFFF);
mtimeHi = _MKSMALLINT((buf.st_mtime >> 16) & 0xFFFF);
ctimeLow = _MKSMALLINT(buf.st_ctime & 0xFFFF);
ctimeHi = _MKSMALLINT((buf.st_ctime >> 16) & 0xFFFF);
if ((ret = readlink((char *) _stringVal(aPathName), pathBuffer, sizeof(pathBuffer))) < 0) {
OperatingSystem_LastErrorNumber = _MKSMALLINT(errno);
RETURN ( nil );
}
pathBuffer[ret] = '\0'; /* readlink does not 0-terminate */
path = _MKSTRING(pathBuffer COMMA_CON);
}
%}.
mode notNil ifTrue:[
info := IdentityDictionary new.
info at:#type put:type.
info at:#mode put:mode.
info at:#uid put:uid.
info at:#gid put:gid.
info at:#size put:size.
info at:#id put:id.
info at:#path put:path.
info at:#accessed put:(AbsoluteTime fromOSTimeLow:atimeLow and:atimeHi).
info at:#modified put:(AbsoluteTime fromOSTimeLow:mtimeLow and:mtimeHi).
info at:#statusChanged put:(AbsoluteTime fromOSTimeLow:ctimeLow and:ctimeHi).
^ info
].
self primitiveFailed
"
OperatingSystem infoOf:'Make.proto'
OperatingSystem linkInfoOf:'Make.proto'
"
!
infoOf:aPathName
"return a dictionary filled with info for the file 'aPathName';
info is: (type->t mode->n uid->u gid->g size->s id->ino).
return nil if such a file does not exist. A dictionary is returned,
since we might need to add more info in the future without affecting
existing applications."
|info type mode uid gid size id
atimeLow atimeHi mtimeLow mtimeHi ctimeLow ctimeHi|
%{
struct stat buf;
int ret;
if (__isString(aPathName)) {
__BEGIN_INTERRUPTABLE__
do {
ret = stat((char *) _stringVal(aPathName), &buf);
} while ((ret < 0) && (errno == EINTR));
__END_INTERRUPTABLE__
if (ret < 0) {
OperatingSystem_LastErrorNumber = _MKSMALLINT(errno);
RETURN ( nil );
}
switch (buf.st_mode & S_IFMT) {
case S_IFDIR:
type = @symbol(directory);
break;
case S_IFREG:
type = @symbol(regular);
break;
#ifdef S_IFCHR
case S_IFCHR:
type = @symbol(characterSpecial);
break;
#endif
#ifdef S_IFBLK
case S_IFBLK:
type = @symbol(blockSpecial);
break;
#endif
#ifdef S_IFLNK
case S_IFLNK:
type = @symbol(symbolicLink);
break;
#endif
#ifdef S_IFSOCK
case S_IFSOCK:
type = @symbol(socket);
break;
#endif
#ifdef S_IFIFO
case S_IFIFO:
type = @symbol(fifo);
break;
#endif
default:
type = @symbol(unknown);
break;
}
mode = _MKSMALLINT(buf.st_mode & 0777);
uid = _MKSMALLINT(buf.st_uid);
gid = _MKSMALLINT(buf.st_gid);
size = _MKSMALLINT(buf.st_size);
id = _MKSMALLINT(buf.st_ino);
atimeLow = _MKSMALLINT(buf.st_atime & 0xFFFF);
atimeHi = _MKSMALLINT((buf.st_atime >> 16) & 0xFFFF);
mtimeLow = _MKSMALLINT(buf.st_mtime & 0xFFFF);
mtimeHi = _MKSMALLINT((buf.st_mtime >> 16) & 0xFFFF);
ctimeLow = _MKSMALLINT(buf.st_ctime & 0xFFFF);
ctimeHi = _MKSMALLINT((buf.st_ctime >> 16) & 0xFFFF);
}
%}.
mode notNil ifTrue:[
info := IdentityDictionary new.
info at:#type put:type.
info at:#mode put:mode.
info at:#uid put:uid.
info at:#gid put:gid.
info at:#size put:size.
info at:#id put:id.
info at:#accessed put:(AbsoluteTime fromOSTimeLow:atimeLow and:atimeHi).
info at:#modified put:(AbsoluteTime fromOSTimeLow:mtimeLow and:mtimeHi).
info at:#statusChanged put:(AbsoluteTime fromOSTimeLow:ctimeLow and:ctimeHi).
^ info
].
self primitiveFailed
"
OperatingSystem infoOf:'/'
(OperatingSystem infoOf:'/') at:#uid
(OperatingSystem infoOf:'/') at:#accessed
"
!
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
"
%{ /* NOCONTEXT */
struct stat buf;
int ret;
if (__isString(aPathName)) {
__BEGIN_INTERRUPTABLE__
do {
ret = stat((char *) _stringVal(aPathName), &buf);
} while ((ret < 0) && (errno == EINTR));
__END_INTERRUPTABLE__
if (ret < 0) {
OperatingSystem_LastErrorNumber = _MKSMALLINT(errno);
RETURN ( nil );
}
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."
%{ /* NOCONTEXT */
int ret;
if (__isString(aPathName) && __isSmallInteger(modeBits)) {
do {
ret = chmod((char *)_stringVal(aPathName), _intVal(modeBits));
} while (ret < 0 && errno == EINTR);
if (ret < 0) {
OperatingSystem_LastErrorNumber = _MKSMALLINT(errno);
RETURN ( false );
}
RETURN ( true );
}
%}.
self primitiveFailed
!
timeOfLastChange:aPathName
"return the time, when the file was last changed.
For nonexistent files, nil is returned."
"could be implemented as:
(self infoOf:aPathName) at:#modified
"
|timeLow timeHi|
%{
struct stat buf;
int ret;
time_t mtime;
if (__isString(aPathName)) {
do {
ret = stat((char *) _stringVal(aPathName), &buf);
} while (ret < 0 && errno == EINTR);
if (ret < 0) {
OperatingSystem_LastErrorNumber = _MKSMALLINT(errno);
RETURN ( nil );
}
timeLow = _MKSMALLINT(buf.st_mtime & 0xFFFF);
timeHi = _MKSMALLINT((buf.st_mtime >> 16) & 0xFFFF);
}
%}
.
timeLow notNil ifTrue:[^ AbsoluteTime fromOSTimeLow:timeLow and:timeHi].
self primitiveFailed
"
OperatingSystem timeOfLastChange:'/'
"
!
timeOfLastAccess:aPathName
"return the time, when the file was last accessed.
For nonexistent files, nil is returned."
"could be implemented as:
(self infoOf:aPathName) at:#accessed
"
|timeLow timeHi|
%{
struct stat buf;
time_t mtime;
int ret;
if (__isString(aPathName)) {
do {
ret = stat((char *) _stringVal(aPathName), &buf);
} while (ret < 0 && errno == EINTR);
if (ret < 0) {
OperatingSystem_LastErrorNumber = _MKSMALLINT(errno);
RETURN (nil);
}
timeLow = _MKSMALLINT(buf.st_atime & 0xFFFF);
timeHi = _MKSMALLINT((buf.st_atime >> 16) & 0xFFFF);
}
%}
.
timeLow notNil ifTrue:[^ AbsoluteTime fromOSTimeLow:timeLow and:timeHi].
self primitiveFailed
"
OperatingSystem timeOfLastAccess:'/'
"
!
idOf:aPathName
"return the fileNumber (i.e. inode number) of a file"
"
this could have been implemented as:
(self infoOf:aPathName) at:#id
but for huge directory searches the code below is faster
"
%{ /* NOCONTEXT */
struct stat buf;
int ret;
if (__isString(aPathName)) {
do {
ret = stat((char *) _stringVal(aPathName), &buf);
} while (ret < 0 && errno == EINTR);
if (ret >= 0) {
RETURN (_MKSMALLINT(buf.st_ino));
}
OperatingSystem_LastErrorNumber = _MKSMALLINT(errno);
RETURN (nil);
}
%}
.
self primitiveFailed
"OperatingSystem idOf:'/'"
!
typeOf:aPathName
"return the type of a file as a symbol"
"
this could have been implemented as:
(self infoOf:aPathName) at:#type
but for huge directory searches the code below is faster
"
%{ /* NOCONTEXT */
struct stat buf;
int ret;
if (__isString(aPathName)) {
do {
ret = stat((char *) _stringVal(aPathName), &buf);
} while (ret < 0 && errno == EINTR);
if (ret < 0) {
OperatingSystem_LastErrorNumber = _MKSMALLINT(errno);
RETURN ( nil );
}
switch (buf.st_mode & S_IFMT) {
case S_IFDIR:
RETURN ( @symbol(directory) );
case S_IFREG:
RETURN ( @symbol(regular) );
#ifdef S_IFCHR
case S_IFCHR:
RETURN ( @symbol(characterSpecial) );
#endif
#ifdef S_IFBLK
case S_IFBLK:
RETURN ( @symbol(blockSpecial) );
#endif
#ifdef S_IFLNK
case S_IFLNK:
RETURN ( @symbol(symbolicLink) );
#endif
#ifdef S_IFSOCK
case S_IFSOCK:
RETURN ( @symbol(socket) );
#endif
#ifdef S_IFIFO
case S_IFIFO:
RETURN ( @symbol(fifo) );
#endif
default:
RETURN ( @symbol(unknown) );
}
}
%}.
self primitiveFailed
"
OperatingSystem typeOf:'/'
OperatingSystem typeOf:'.'
OperatingSystem typeOf:'Make.proto'
"
!
createDirectory:newPathName
"create a new directory with name 'newPathName'.
Return true if successful, false if failed."
"since createDirectory is not used too often,
you'll forgive me using mkdir ..."
^ self executeCommand:('mkdir ' , newPathName)
"OperatingSystem createDirectory:'foo'"
!
recursiveCreateDirectory:dirName
"create a directory - with all parent dirs if needed.
Return true if successful, false otherwise. If false
is returned, a partial created tree may be left,
which is not cleaned-up here."
self createDirectory:dirName.
(self isDirectory:dirName) ifFalse:[
(self recursiveCreateDirectory:(self directoryNameOf:dirName)) ifFalse:[^ false].
^ self createDirectory:dirName
].
^ true
"OperatingSystem recursiveCreateDirectory:'foo/bar/baz'"
!
removeFile:fullPathName
"remove the file named 'fullPathName'; return true if successful"
%{ /* NOCONTEXT */
int ret;
if (__isString(fullPathName)) {
do {
ret = unlink((char *) _stringVal(fullPathName));
} while (ret < 0 && errno == EINTR);
RETURN ( (ret >= 0) ? true : false );
}
%}.
self primitiveFailed
!
removeDirectory:fullPathName
"remove the directory named 'fullPathName'.
Return true if successful, false if directory is not empty or no permission"
%{ /* NOCONTEXT */
int ret;
if (__isString(fullPathName)) {
do {
ret = rmdir((char *) _stringVal(fullPathName));
} while (ret < 0 && errno == EINTR);
RETURN ( (ret >= 0) ? true : false );
}
%}.
self primitiveFailed
!
recursiveRemoveDirectory:fullPathName
"remove the directory named 'fullPathName' and all contained files/directories.
Return true if successful."
^ self executeCommand:('rm -rf ' , fullPathName)
!
linkFile:oldPath to:newPath
"link the file 'oldPath' to 'newPath'. The link will be a hard link.
Return true if successful, false if not."
%{ /* NOCONTEXT */
int ret;
if (__isString(oldPath) && __isString(newPath)) {
do {
ret = link((char *) _stringVal(oldPath), (char *) _stringVal(newPath));
} while (ret < 0 && errno == EINTR);
RETURN ( (ret >= 0) ? true : false );
}
%}.
self primitiveFailed
"OperatingSystem linkFile:'foo' to:'bar'"
!
renameFile:oldPath to:newPath
"rename the file 'oldPath' to 'newPath'.
Return true if sucessfull, false if not"
%{ /* NOCONTEXT */
int ret;
if (__isString(oldPath) && __isString(newPath)) {
#if defined(HAS_RENAME)
do {
ret = rename((char *) _stringVal(oldPath), (char *) _stringVal(newPath));
} while (ret < 0 && errno == EINTR);
RETURN ( (ret >= 0) ? true : false );
#else
if (link((char *) _stringVal(oldPath), (char *) _stringVal(newPath)) >= 0) {
if (unlink((char *) _stringVal(oldPath)) >= 0) {
RETURN ( true );
}
unlink((char *) _stringVal(newPath));
}
#endif
RETURN ( false );
}
%}
.
self primitiveFailed
"OperatingSystem renameFile:'foo' to:'bar'"
! !
!OperatingSystem class methodsFor:'shared memory access'!
shmGet:key size:size flags:flags
"low level entry to shmget system call.
This is not for public use
- use the provided wrapper class SharedExternalBytes instead."
%{ /* NOCONTEXT */
#ifdef WANT_SHM
if (__bothSmallInteger(key, size)
&& __isSmallInteger(flags)) {
int rslt;
rslt = shmget(__intVal(key), __intVal(size), __intVal(flags));
if (rslt != -1) {
RETURN (__MKSMALLINT(rslt));
}
OperatingSystem_LastErrorNumber = _MKSMALLINT(errno);
RETURN (nil);
}
#endif
%}.
self primitiveFailed
!
shmAttach:id address:addr flags:flags
%{ /* NOCONTEXT */
#ifdef WANT_SHM
void *address, *shmaddr;
int shmflg, shmid;
if (__isSmallInteger(addr)
&& __bothSmallInteger(flags, id)) {
shmaddr = (void *) __intVal(addr);
shmflg = __intVal(flags);
shmid = __intVal(id);
address = shmat(shmid, shmaddr, shmflg);
if (address != (void *)-1) {
RETURN (__MKEXTERNALBYTES(addr));
}
OperatingSystem_LastErrorNumber = _MKSMALLINT(errno);
RETURN (nil);
}
#endif
%}.
self primitiveFailed
!
shmDetach:addr
%{ /* NOCONTEXT */
#ifdef WANT_SHM
void *shmaddr;
int rslt;
if (__isSmallInteger(addr)) {
shmaddr = (void *) __intVal(addr);
rslt = shmdt(shmaddr);
if (rslt != -1) {
RETURN (true);
}
OperatingSystem_LastErrorNumber = _MKSMALLINT(errno);
RETURN (false);
}
#endif
%}.
self primitiveFailed
! !