Unix.st
changeset 1 a27a279701f8
child 2 6526dde5f3ac
equal deleted inserted replaced
0:aa2498ef6470 1:a27a279701f8
       
     1 "
       
     2  COPYRIGHT (c) 1988-93 by Claus Gittinger
       
     3               All Rights Reserved
       
     4 
       
     5  This software is furnished under a license and may be used
       
     6  only in accordance with the terms of that license and with the
       
     7  inclusion of the above copyright notice.   This software may not
       
     8  be provided or otherwise made available to, or used by, any
       
     9  other person.  No title to or ownership of the software is
       
    10  hereby transferred.
       
    11 "
       
    12 
       
    13 Object subclass:#OperatingSystem
       
    14        instanceVariableNames:''
       
    15        classVariableNames:''
       
    16        poolDictionaries:''
       
    17        category:'System-Support'
       
    18 !
       
    19 
       
    20 OperatingSystem comment:'
       
    21 
       
    22 COPYRIGHT (c) 1988-93 by Claus Gittinger
       
    23              All Rights Reserved
       
    24 
       
    25 this class gives access to some operating system services;
       
    26 some of it is very specific for unix. 
       
    27 
       
    28 %W% %E%
       
    29 
       
    30 written 1988 by claus
       
    31 '!
       
    32 
       
    33 %{
       
    34 
       
    35 #ifdef transputer
       
    36 # define unlink(f)      ((remove(f) == 0) ? 0 : -1)
       
    37 #else
       
    38 # include <signal.h>
       
    39 
       
    40 # ifdef SYSV
       
    41 #  include <sys/types.h>
       
    42 #  include <sys/param.h>
       
    43 #  include <sys/times.h>
       
    44 #  if ! defined(sco3_2)
       
    45 #   include <unistd.h>
       
    46 #  endif
       
    47 #  if defined(isc3_2) || defined(sco3_2)
       
    48 #   include <sys/time.h>
       
    49 #  endif
       
    50 #  if !defined(isc3_2)
       
    51 #   if defined(PCS) && defined(mips)
       
    52 #    include "/usr/include/bsd/sys/time.h"
       
    53 #    include "/usr/include/sys/time.h"
       
    54 #   else
       
    55 #    include <time.h>
       
    56 #   endif
       
    57 #  endif
       
    58 #  if defined(isc3_2)
       
    59 #   include <sys/bsdtypes.h>
       
    60 #  endif
       
    61 #  ifdef FAST_TIMER
       
    62 #   include <ft.h>
       
    63     static int timer_fd = -1;
       
    64 #  endif
       
    65 # else /* not SYSV */
       
    66 #  include <sys/time.h>
       
    67 #  include <sys/types.h>
       
    68 # endif
       
    69 # include <pwd.h>
       
    70 # include <grp.h>
       
    71 
       
    72 # include <sys/stat.h>
       
    73 # ifndef S_IXUSR
       
    74 #  define S_IXUSR S_IEXEC
       
    75 #  define S_IXGRP (S_IEXEC>>3)
       
    76 #  define S_IXOTH (S_IEXEC>>6)
       
    77 # endif
       
    78 
       
    79 #endif
       
    80 
       
    81 #ifndef errno
       
    82  extern errno;
       
    83 #endif
       
    84 
       
    85 #include <stdio.h>
       
    86 #ifndef R_OK
       
    87 # define R_OK    4       /* Test for Read permission */ 
       
    88 # define W_OK    2       /* Test for Write permission */
       
    89 # define X_OK    1       /* Test for eXecute permission */
       
    90 # define F_OK    0       /* Test for existence of File */
       
    91 #endif
       
    92 %}
       
    93 
       
    94 !OperatingSystem class methodsFor:'misc'!
       
    95 
       
    96 exit
       
    97     "shutdown smalltalk immediately"
       
    98 
       
    99 %{  /* NOCONTEXT */
       
   100     mainExit(0);
       
   101 %}
       
   102     "OperatingSystem exit - dont evaluate this"
       
   103 !
       
   104 
       
   105 exit:exitCode
       
   106     "shutdown smalltalk immediately returning an exit-code"
       
   107 
       
   108 %{  /* NOCONTEXT */
       
   109     if (! _isSmallInteger(exitCode))
       
   110         exit(1);
       
   111     mainExit(_intVal(exitCode));
       
   112 %}
       
   113     "OperatingSystem exit:1 - dont evaluate this"
       
   114 !
       
   115 
       
   116 getEnvironment:aString
       
   117     "get an environment string"
       
   118 
       
   119 %{  /* NOCONTEXT */
       
   120 
       
   121     char *env;
       
   122 
       
   123     if (_isString(aString)) {
       
   124         env =  (char *)getenv(_stringVal(aString));
       
   125         if (env) {
       
   126             RETURN ( _MKSTRING(env COMMA_CON) );
       
   127         }
       
   128     }
       
   129 %}
       
   130 .
       
   131     ^ nil
       
   132 
       
   133     "OperatingSystem getEnvironment:'LANG'"
       
   134 !
       
   135 
       
   136 getLoginName
       
   137     "return a string with the users name"
       
   138 
       
   139 %{  /* NOCONTEXT */
       
   140 
       
   141     char *name = "you";
       
   142 #ifndef transputer
       
   143     name = (char *)getlogin();
       
   144     if (! name) {
       
   145         name = (char *)getenv("LOGNAME");
       
   146     }
       
   147 #endif
       
   148     RETURN ( _MKSTRING(name COMMA_CON) );
       
   149 %}
       
   150     "OperatingSystem getLogin"
       
   151 !
       
   152 
       
   153 getUserNameFromID:aNumber
       
   154     "return the user-name-string for a given numeric user-id"
       
   155 
       
   156 %{  /* NOCONTEXT */
       
   157 
       
   158 #ifndef transputer
       
   159     struct passwd *p;
       
   160 
       
   161     if (_isSmallInteger(aNumber)) {
       
   162         p = getpwuid(_intVal(aNumber));
       
   163         if (p) {
       
   164             RETURN ( _MKSTRING(p->pw_name COMMA_CON) );
       
   165         }
       
   166     }
       
   167 #endif
       
   168 %}
       
   169 .
       
   170     ^ '???'
       
   171 
       
   172     "OperatingSystem getUserNameFromID:0"
       
   173 !
       
   174 
       
   175 getGroupNameFromID:aNumber
       
   176     "return the group-name-string for a given numeric group-id"
       
   177 
       
   178 %{  /* NOCONTEXT */
       
   179 
       
   180 #ifndef transputer
       
   181     struct group *g;
       
   182 
       
   183     if (_isSmallInteger(aNumber)) {
       
   184         g = getgrgid(_intVal(aNumber));
       
   185         if (g) {
       
   186             RETURN ( _MKSTRING(g->gr_name COMMA_CON) );
       
   187         }
       
   188     }
       
   189 #endif
       
   190 %}
       
   191 .
       
   192     ^ '???'
       
   193 
       
   194     "OperatingSystem getGroupNameFromID:0"
       
   195 !
       
   196 
       
   197 getHomeDirectory
       
   198     "return the name of the home directory"
       
   199 
       
   200     ^ OperatingSystem getEnvironment:'HOME'
       
   201 
       
   202     "OperatingSystem getHomeDirectory"
       
   203 !
       
   204 
       
   205 getProcessId
       
   206     "return the processId"
       
   207 
       
   208 %{  /* NOCONTEXT */
       
   209 
       
   210     int pid = 0;
       
   211 #ifndef transputer
       
   212     pid = getpid();
       
   213 #endif
       
   214     RETURN ( _MKSMALLINT(pid) );
       
   215 %}
       
   216     "OperatingSystem getProcessId"
       
   217 !
       
   218 
       
   219 getCharacter
       
   220     "read a character from keyboard"
       
   221 
       
   222 %{  /* NOCONTEXT */
       
   223 
       
   224     RETURN ( _MKSMALLINT(getchar()) );
       
   225 %}
       
   226 !
       
   227 
       
   228 getCPUType
       
   229     "return a string giving the type of machine we're running on"
       
   230 
       
   231     |cpu|
       
   232     
       
   233     cpu := 'unknown'.
       
   234 
       
   235 %{  /* NOCONTEXT */
       
   236 #ifdef vax
       
   237     cpu = _MKSTRING("vax" COMMA_CON);
       
   238 #endif
       
   239 #ifdef mips
       
   240     cpu = _MKSTRING("mips" COMMA_CON);
       
   241 #endif
       
   242 #ifdef i386
       
   243     cpu = _MKSTRING("i386" COMMA_CON);
       
   244 #endif
       
   245 #ifdef ns32k
       
   246     cpu = _MKSTRING("ns32k" COMMA_CON);
       
   247 #endif
       
   248 #ifdef mc68k
       
   249     cpu = _MKSTRING("mc68k" COMMA_CON);
       
   250 #endif
       
   251 #ifdef sparc
       
   252     cpu = _MKSTRING("sparc" COMMA_CON);
       
   253 #endif
       
   254 #ifdef snake
       
   255     cpu = _MKSTRING("snake" COMMA_CON);
       
   256 #endif
       
   257 #ifdef rs6000
       
   258     cpu = _MKSTRING("rs6000" COMMA_CON);
       
   259 #endif
       
   260 #ifdef alpha
       
   261     cpu = _MKSTRING("alpha" COMMA_CON);
       
   262 #endif
       
   263 #ifdef transputer
       
   264     cpu = _MKSTRING("transputer" COMMA_CON);
       
   265 #endif
       
   266 %}
       
   267 .
       
   268     ^ cpu
       
   269 
       
   270     "OperatingSystem getCPUType"
       
   271 !
       
   272 
       
   273 getOSType
       
   274     "return a string giving the type of os we're running on"
       
   275 
       
   276     |os|
       
   277 
       
   278     os := 'unknown'.
       
   279 
       
   280 %{  /* NOCONTEXT */
       
   281 #ifdef sinix
       
   282   os = _MKSTRING("sinix" COMMA_CON);
       
   283 #endif
       
   284 
       
   285 #ifdef ultrix
       
   286   os = _MKSTRING("ultrix" COMMA_CON);
       
   287 #endif
       
   288 
       
   289 #ifdef sco
       
   290   os = _MKSTRING("sco" COMMA_CON);
       
   291 #endif
       
   292 
       
   293 #ifdef LINUX
       
   294   os = _MKSTRING("linux" COMMA_CON);
       
   295 #endif
       
   296 
       
   297 #ifdef BSD
       
   298 # ifdef MACH
       
   299   os = _MKSTRING("mach" COMMA_CON);
       
   300 # endif
       
   301 
       
   302 # ifdef sunos
       
   303   os = _MKSTRING("sunos" COMMA_CON);
       
   304 # endif
       
   305 
       
   306 # ifdef IRIS
       
   307   os = _MKSTRING("irix" COMMA_CON);
       
   308 # endif
       
   309 
       
   310   if (os == nil) os = _MKSTRING("bsd" COMMA_CON);
       
   311 #endif
       
   312 
       
   313 #ifdef SYSV
       
   314 # ifdef SYSV3
       
   315   os = _MKSTRING("sys5.3" COMMA_CON);
       
   316 # else
       
   317 #  ifdef SYSV4
       
   318     os = _MKSTRING("sys5.4" COMMA_CON);
       
   319 #  else
       
   320     os = _MKSTRING("sys5" COMMA_CON);
       
   321 #  endif
       
   322 # endif
       
   323 #endif
       
   324 %}
       
   325 .
       
   326     ^ os
       
   327 
       
   328     "OperatingSystem getOSType"
       
   329 !
       
   330 
       
   331 getSystemType
       
   332     "return a string giving the type of system we're running on"
       
   333 
       
   334     |sys|
       
   335 
       
   336     sys := 'unknown'.
       
   337 
       
   338 %{  /* NOCONTEXT */
       
   339 #ifdef sinix
       
   340   sys = _MKSTRING("sinix" COMMA_CON);
       
   341 #endif
       
   342 
       
   343 #ifdef ultrix
       
   344   sys = _MKSTRING("ultrix" COMMA_CON);
       
   345 #endif
       
   346 
       
   347 #ifdef sco
       
   348   sys = _MKSTRING("sco" COMMA_CON);
       
   349 #endif
       
   350 
       
   351 #ifdef sunos
       
   352   sys = _MKSTRING("sunos" COMMA_CON);
       
   353 #endif
       
   354 
       
   355 #ifdef solaris
       
   356   sys = _MKSTRING("solaris" COMMA_CON);
       
   357 #endif
       
   358 
       
   359 #ifdef NEXT
       
   360   sys = _MKSTRING("next" COMMA_CON);
       
   361 #endif
       
   362 
       
   363 #ifdef IRIS
       
   364   sys = _MKSTRING("iris" COMMA_CON);
       
   365 #endif
       
   366 
       
   367 #ifdef LINUX
       
   368   sys = _MKSTRING("linux" COMMA_CON);
       
   369 #endif
       
   370 
       
   371 #ifdef BSD
       
   372 # ifdef MACH
       
   373   if (sys == nil) sys = _MKSTRING("mach" COMMA_CON);
       
   374 # endif
       
   375   if (sys == nil) sys = _MKSTRING("bsd" COMMA_CON);
       
   376 #endif
       
   377 
       
   378 #ifdef SYSV
       
   379 # ifdef SYSV3
       
   380   if (sys == nil) sys = _MKSTRING("sys5.3" COMMA_CON);
       
   381 # else
       
   382 #  ifdef SYSV4
       
   383     if (sys == nil) sys = _MKSTRING("sys5.4" COMMA_CON);
       
   384 #  else
       
   385     if (sys == nil) sys = _MKSTRING("sys5" COMMA_CON);
       
   386 #  endif
       
   387 # endif
       
   388 #endif
       
   389 %}
       
   390 .
       
   391     ^ sys
       
   392 
       
   393     "OperatingSystem getSystemType"
       
   394 !
       
   395 
       
   396 isBSDlike
       
   397     "return true, if the OS we're running on is a real unix."
       
   398 
       
   399 %{  /* NOCONTEXT */
       
   400 
       
   401 #ifdef BSD
       
   402     RETURN ( true );
       
   403 #endif
       
   404 #ifdef SYSV4
       
   405     RETURN ( true );
       
   406 #endif
       
   407 %}
       
   408 .
       
   409     ^ false
       
   410 !
       
   411 
       
   412 maxFileNameLength
       
   413     "return the max number of characters in a filename."
       
   414 
       
   415 %{  /* NOCONTEXT */
       
   416 #if defined(BSD) || defined(SYSV4) || defined(LINUX)
       
   417     RETURN ( _MKSMALLINT(255) );
       
   418 #else
       
   419 # ifdef SYSV
       
   420     RETURN ( _MKSMALLINT(14) );
       
   421 # endif
       
   422 # ifdef MSDOS
       
   423     RETURN ( _MKSMALLINT(9) );
       
   424 # endif
       
   425 #endif
       
   426 %}
       
   427 .
       
   428     ^ 14
       
   429 ! !
       
   430 
       
   431 !OperatingSystem class methodsFor:'error messages'!
       
   432 
       
   433 errorTextForNumber:errNr
       
   434     "return a message string from a unix errorNumber 
       
   435      (as returned by a system call). Should be replaced by
       
   436      a resource lookup."
       
   437 
       
   438     |msg messages|
       
   439 
       
   440     (Language == #german) ifTrue:[
       
   441         messages := #('keine superuser Berechtigung'
       
   442                       'ungueltiger Datei- oder VerzeichnisName'
       
   443                       nil "'ungueltige Prozessnummer'  "
       
   444                       nil "'unterbrochener systemcall' "
       
   445                       'E/A Fehler'
       
   446                       nil "'Geraet existiert nicht' "
       
   447                       'zu viele Argumente'
       
   448                       'nicht ausfuehrbar'
       
   449                       nil "'falscher FileDescriptor'"
       
   450                       nil "'kein Kindprozess'       "
       
   451                       'zu viele Prozesse oder zu wenig Speicher'
       
   452                       'zu wenig Speicher'
       
   453                       'keine ZugriffsBerechtigung'
       
   454                       nil "'falsche Adresse'        "
       
   455                       nil "'kein Blockgeraet'       "
       
   456                       nil "'Platte noch im Zugriff' "
       
   457                       'Datei existiert bereits'
       
   458                       nil "'Link ueber Plattengrenzen hinweg' "
       
   459                       'Geraet existiert nicht'
       
   460                       'ist kein Verzeichnis'
       
   461                       'ist ein Verzeichnis'
       
   462                       nil "'ungueltiges Argument' "
       
   463                       'zu viele Dateien offen'
       
   464                       'zu viele Dateien offen'
       
   465                       nil "'kein Terminalgeraet'  "
       
   466                       'Datei wird gerade ausgefuehrt'
       
   467                       'Datei zu gross'
       
   468                       'Platte ist voll'
       
   469                       'ungueltige Positionierung'
       
   470                       'Platte ist schreibgeschuetzt'
       
   471                       'zu viele Links'
       
   472                       'Pipe unterbrochen'
       
   473                       'argument nicht im gueltigen Bereich'
       
   474                       'Ergebnis nicht im gueltigen Bereich')
       
   475     ] ifFalse:[
       
   476         messages := #('Not super-user'
       
   477                       'No such file or directory'
       
   478                       nil "'No such process'   "
       
   479                       nil "'interrupted system call' "
       
   480                       'I/O error'
       
   481                       nil "'No such device or address' "
       
   482                       'Arg list too long'
       
   483                       'Exec format error'
       
   484                       nil "'Bad file number'"
       
   485                       nil "'No children'       "
       
   486                       'No more processes'
       
   487                       'Not enough core'
       
   488                       'Permission denied'
       
   489                       nil "'Bad address'        "
       
   490                       nil "'Block device required'       "
       
   491                       nil "'Mount device busy' "
       
   492                       'File exists'
       
   493                       nil "'Cross-device link' "
       
   494                       'No such device'
       
   495                       'Not a directory'
       
   496                       'Is a directory'
       
   497                       nil 'Invalid argument'
       
   498                       'File table overflow'
       
   499                       'Too many open files'
       
   500                       nil "'Not a typewriter' "
       
   501                       'Text file busy'
       
   502                       'File too large'
       
   503                       'No space left on device'
       
   504                       'Illegal seek'
       
   505                       'Read only file system'
       
   506                       'Too many links'
       
   507                       'Broken pipe'
       
   508                       'Math arg out of domain of func'
       
   509                       'Math result not representable')
       
   510     ].
       
   511 
       
   512     (errNr between:1 and:messages size) ifTrue:[
       
   513         msg := messages at:errNr
       
   514     ].
       
   515     msg isNil ifTrue:[
       
   516         ^ ('ErrorNr: ' , errNr printString)
       
   517     ].
       
   518     ^ msg
       
   519 ! !
       
   520 
       
   521 !OperatingSystem class methodsFor:'interrupts'!
       
   522 
       
   523 enableUserInterrupts
       
   524     "enable userInterrupt (^C) handling;
       
   525      after enabling, ^C will send the message 'userInterrupt'
       
   526      to the UserInterruptHandler object."
       
   527 
       
   528 %{  /* NOCONTEXT */
       
   529     extern void userInterrupt(), exceptionInterrupt();
       
   530 
       
   531     signal(SIGINT, userInterrupt);
       
   532     /* signal(SIGQUIT, userInterrupt); */
       
   533 %}
       
   534 !
       
   535 
       
   536 enableFpExceptionInterrupts
       
   537     "enable floating point exception interrupts (if the
       
   538      architecture supports it).
       
   539      after enabling, fpu-exceptions will send the message 
       
   540      'fpuExceptionInterrupt' to the FPUExceptionInterruptHandler object."
       
   541 
       
   542 %{  /* NOCONTEXT */
       
   543     extern void fpExceptionInterrupt();
       
   544 
       
   545     signal(SIGFPE, fpExceptionInterrupt);
       
   546 %}
       
   547 !
       
   548 
       
   549 enableSignalInterrupts
       
   550     "enable signal exception interrupts (trap, buserror & segm. violation).
       
   551      after enabling, these exceptions will send the message 
       
   552      'signalInterrupt' to the SignalInterruptHandler object."
       
   553 
       
   554 %{  /* NOCONTEXT */
       
   555     extern void signalPIPEInterrupt();
       
   556     extern void signalBUSInterrupt();
       
   557     extern void signalSEGVInterrupt();
       
   558 
       
   559     signal(SIGPIPE, signalPIPEInterrupt);
       
   560 #ifdef SIGBUS
       
   561     signal(SIGBUS,  signalBUSInterrupt);
       
   562 #endif
       
   563     signal(SIGSEGV, signalSEGVInterrupt);
       
   564 %}
       
   565 !
       
   566 
       
   567 enableIOInterrupts
       
   568     "enable IO availability interrupts 
       
   569      (SIGPOLL/SIGIO, if the architecture supports it).
       
   570      after enabling, these signals will send the message 
       
   571      'ioInterrupt' to the IOInterruptHandler object."
       
   572 
       
   573 %{  /* NOCONTEXT */
       
   574     extern void ioInterrupt();
       
   575 
       
   576 #ifdef SIGPOLL
       
   577     signal(SIGPOLL, ioInterrupt);
       
   578 #endif
       
   579 #ifdef SIGIO
       
   580     signal(SIGIO,  ioInterrupt);
       
   581 #endif
       
   582 %}
       
   583 !
       
   584 
       
   585 startSpyTimer
       
   586     "trigger a spyInterrupt, to be signalled after some (short) time.
       
   587      This is used by MessageTally for profiling."
       
   588 
       
   589 %{  /* NOCONTEXT */
       
   590 
       
   591     extern void spyInterrupt();
       
   592 #ifdef FAST_TIMER
       
   593     /*
       
   594      * using PD ft-driver for tick-resolution signals
       
   595      */
       
   596     if (timer_fd < 0) {
       
   597         timer_fd = open("/dev/ft0", 0);
       
   598     }
       
   599     if (timer_fd > 0) {
       
   600         ioctl(timer_fd, FTIOCSET, (2<<16) | SIGALRM);
       
   601     }
       
   602 #else
       
   603 # if defined(BSD) || defined(isc3_2) || defined(SYSV4) || defined(LINUX)
       
   604     struct itimerval dt;
       
   605 
       
   606     dt.it_interval.tv_sec = 0;
       
   607     dt.it_interval.tv_usec = 0;
       
   608     dt.it_value.tv_sec = 0;
       
   609     dt.it_value.tv_usec = 10000;   /* 100 Hz */
       
   610     setitimer(ITIMER_VIRTUAL, &dt, 0);
       
   611 #  ifdef BSD
       
   612 #   ifndef SYSV4
       
   613     sigsetmask(0);
       
   614 #   endif
       
   615 #  endif
       
   616 # endif
       
   617 #endif
       
   618 
       
   619 #ifdef SIGVTALRM
       
   620     signal(SIGVTALRM, spyInterrupt);
       
   621 #else
       
   622     signal(SIGALRM, spyInterrupt);
       
   623 #endif
       
   624 %}
       
   625 !
       
   626 
       
   627 stopSpyTimer
       
   628     "stop spy timing"
       
   629 
       
   630 %{  /* NOCONTEXT */
       
   631 
       
   632 #ifdef FAST_TIMER
       
   633     if (timer_fd > 0) {
       
   634         ioctl(timer_fd, FTIOCANCEL, 0);
       
   635         close(timer_fd);
       
   636         timer_fd = -1;
       
   637     }
       
   638 #else
       
   639 # if defined(BSD) || defined(isc3_2) || defined(SYSV4) || defined(LINUX)
       
   640     struct itimerval dt;
       
   641 
       
   642     dt.it_interval.tv_sec = 0;
       
   643     dt.it_interval.tv_usec = 0;
       
   644     dt.it_value.tv_sec = 0;
       
   645     dt.it_value.tv_usec = 0;
       
   646     setitimer(ITIMER_VIRTUAL, &dt, 0);
       
   647 # endif
       
   648 #endif
       
   649 %}
       
   650 ! !
       
   651 
       
   652 !OperatingSystem class methodsFor:'time and date'!
       
   653 
       
   654 getTimeLow
       
   655     "return low 16 bits of current time. 
       
   656      Obsolete: Dont use this method, use getTimeParts below.
       
   657      This method will not always return the correct time 
       
   658      if used together with getTimeHi.
       
   659      (a wrap between the two getTimeXXX calls could occur)"
       
   660 
       
   661 %{  /* NOCONTEXT */
       
   662 
       
   663     RETURN ( _MKSMALLINT(time(0) & 0xFFFF) );
       
   664 %}
       
   665 .
       
   666     self primitiveFailed
       
   667 
       
   668     "OperatingSystem getTimeLow"
       
   669 !
       
   670 
       
   671 getTimeHi
       
   672     "return hi 16 bits of current time. 
       
   673      Obsolete: Dont use this method, use getTimeParts below. 
       
   674      This method will NOT always return the correct time
       
   675      if used together with getTimeHi.
       
   676      (a wrap between the two getTimeXXX calls could occur)"
       
   677 
       
   678 %{  /* NOCONTEXT */
       
   679 
       
   680     RETURN ( _MKSMALLINT((time(0) >> 16) & 0xFFFF) );
       
   681 %}
       
   682 .
       
   683     self primitiveFailed
       
   684 
       
   685     "OperatingSystem getTimeHi"
       
   686 !
       
   687 
       
   688 getTimeInto:aBlock
       
   689     "evaluate the argument aBlock, passing the time-parts of
       
   690      the current time as arguments."
       
   691 
       
   692     |low hi|
       
   693 %{ 
       
   694     int now;
       
   695 
       
   696     now = time(0);
       
   697     hi  = _MKSMALLINT((now >> 16) & 0xFFFF);
       
   698     low = _MKSMALLINT(now & 0xFFFF);
       
   699 %}
       
   700 .
       
   701     aBlock value:low value:hi
       
   702 
       
   703     "OperatingSystem getTimeTimeInto:[:low :hi | low printNewline. hi printNewline]"
       
   704 !
       
   705 
       
   706 getTime
       
   707     "return current Time (in seconds since 1970).
       
   708      This might return a LargeInteger some time."
       
   709 
       
   710     ^ self getTimeHi * 16r10000 + self getTimeLow
       
   711 
       
   712     "OperatingSystem getTime"
       
   713 !
       
   714 
       
   715 computeDatePartsOf:timeLow and:timeHi for:aBlock
       
   716     "compute year, month and day from the time-parts timeLow and
       
   717      timeHi and evaluate the argument, a 3-arg block with these.
       
   718      This method was added to avoid LargeInteger arithmetic; the time-parts
       
   719      are those returned by getTimeLow and getTimeHi."
       
   720 
       
   721     |year month day|
       
   722 
       
   723     ((timeLow isMemberOf:SmallInteger) and:[timeHi isMemberOf:SmallInteger])
       
   724     ifFalse:[
       
   725         ^ self primitiveFailed
       
   726     ].
       
   727 %{
       
   728     struct tm *tmPtr;
       
   729     long t;
       
   730 
       
   731     t = (_intVal(timeHi) << 16) | _intVal(timeLow);
       
   732     tmPtr = localtime(&t);
       
   733     year = _MKSMALLINT(tmPtr->tm_year + 1900);
       
   734     month = _MKSMALLINT(tmPtr->tm_mon + 1);
       
   735     day = _MKSMALLINT(tmPtr->tm_mday);
       
   736 %}
       
   737 .
       
   738     aBlock value:year value:month value:day
       
   739 !
       
   740 
       
   741 computeTimePartsOf:timeLow and:timeHi for:aBlock
       
   742     "compute hours, minutes and seconds from the time-parts timeLow and
       
   743      timeHi and evaluate the argument, a 3-arg block with these."
       
   744 
       
   745     |hours minutes seconds|
       
   746 
       
   747     ((timeLow isMemberOf:SmallInteger) and:[timeHi isMemberOf:SmallInteger])
       
   748     ifFalse:[
       
   749         ^ self primitiveFailed
       
   750     ].
       
   751 %{
       
   752     struct tm *tmPtr;
       
   753     long t;
       
   754 
       
   755     t = (_intVal(timeHi) << 16) | _intVal(timeLow);
       
   756     tmPtr = localtime(&t);
       
   757     hours = _MKSMALLINT(tmPtr->tm_hour);
       
   758     minutes = _MKSMALLINT(tmPtr->tm_min);
       
   759     seconds = _MKSMALLINT(tmPtr->tm_sec);
       
   760 %}
       
   761 .
       
   762     aBlock value:hours value:minutes value:seconds
       
   763 !
       
   764 
       
   765 getMillisecondTime
       
   766     "since range is limited to 0..1ffffff and value is wrapping around
       
   767      at 1fffffff, this can only be used for relative time deltas.
       
   768      Use methods below to compare and add time deltas (should move to Time)"
       
   769 
       
   770 %{  /* NOCONTEXT */
       
   771 
       
   772     long t;
       
   773 #ifdef SYSV
       
   774 # ifdef HZ
       
   775     /* sys5 time */
       
   776     long ticks;
       
   777     struct tms tb;
       
   778 
       
   779     ticks = times(&tb);
       
   780     t = (ticks * 1000) / HZ;
       
   781 # endif
       
   782 #else
       
   783     /* bsd time */
       
   784     struct timeval tb;
       
   785     struct timezone tzb;
       
   786 
       
   787     gettimeofday(&tb, &tzb);
       
   788     t = tb.tv_sec*1000 + tb.tv_usec/1000;
       
   789 #endif
       
   790     RETURN ( _MKSMALLINT(t & 0x0FFFFFFF) );
       
   791 %}
       
   792 .
       
   793     self error:'time not available'
       
   794 !
       
   795 
       
   796 millisecondTimeDeltaBetween:msTime1 and:msTime2
       
   797     "subtract two millisecond times (such as returned getMillisecondTime).
       
   798      The returned value is msTime1 - msTime2 where a wrap occurs at:16r0FFFFFFF."
       
   799 
       
   800     (msTime1 > msTime2) ifTrue:[
       
   801         ^ msTime1 - msTime2
       
   802     ].
       
   803     ^ msTime1 + 16r10000000 - msTime2
       
   804 !
       
   805 
       
   806 millisecondTime:msTime1 isAfter:msTime2
       
   807     "return true if msTime1 is after msTime2, false if not.
       
   808      handling wrap at 16r0FFFFFFF. The two arguments are
       
   809      millisecond times (such as returned getMillisecondTime)."
       
   810 
       
   811     (msTime1 > msTime2) ifTrue:[
       
   812         ((msTime1 - msTime2) > 16r08000000) ifTrue:[
       
   813             ^ false
       
   814         ].
       
   815         ^ true
       
   816     ].
       
   817     ((msTime2 - msTime1) > 16r08000000) ifTrue:[
       
   818         ^ true
       
   819     ].
       
   820     ^ false
       
   821 !
       
   822 
       
   823 millisecondTimeAdd:msTime1 and:msTime2
       
   824     "add two millisecond times (such as returned getMillisecondTime).
       
   825      The returned value is msTime1 + msTime2 where a wrap occurs at:16r0FFFFFFF."
       
   826 
       
   827     |sum|
       
   828 
       
   829     sum := msTime1 + msTime2.
       
   830     (sum > 16r0FFFFFFF) ifTrue:[^ sum - 16r10000000].
       
   831     (sum < 0) ifTrue:[^ sum + 16r10000000].
       
   832     ^ sum
       
   833 !
       
   834 
       
   835 millisecondDelay:millis
       
   836     "delay execution for millis milliseconds."
       
   837 
       
   838     self selectOn:nil withTimeOut:(millis * 0.001)
       
   839 
       
   840     "OperatingSystem millisecondDelay:1000"
       
   841 !
       
   842 
       
   843 sleep:numberOfSeconds
       
   844     "cease any action for some time. 
       
   845      Not really useful since not even low-prio processes and interrupt
       
   846      handling will run during the sleep - use millisecondDelay:."
       
   847 
       
   848 %{  /* NOCONTEXT */
       
   849 
       
   850     if (_isSmallInteger(numberOfSeconds)) {
       
   851         sleep(_intVal(numberOfSeconds));
       
   852         RETURN ( self );
       
   853     }
       
   854 %}
       
   855 .
       
   856     self primitiveFailed
       
   857 !
       
   858 
       
   859 selectOn:aFileDescriptor withTimeOut:seconds
       
   860     "wait for aFileDesriptor to become ready; timeout after t seconds.
       
   861      Return true, if i/o ok, false if timed-out or interrupted.
       
   862      With 0 as timeout argument, this can be used to check for availability
       
   863      of read-data.
       
   864      Experimental."
       
   865 
       
   866     |millis|
       
   867 
       
   868     millis := (seconds * 1000) rounded.
       
   869 %{
       
   870     fd_set rset, wset, eset;
       
   871     int t, fd, i, lX, bX;
       
   872     struct timeval wt;
       
   873 
       
   874     if ((aFileDescriptor == nil) || _isSmallInteger(aFileDescriptor)) {
       
   875         if (_isSmallInteger(millis)) {
       
   876             FD_ZERO(&rset);
       
   877             FD_ZERO(&wset);
       
   878             FD_ZERO(&eset);
       
   879             if (aFileDescriptor != nil) {
       
   880                 fd = _intVal(aFileDescriptor);
       
   881                 if ((fd >= 0) && (fd < FD_SETSIZE))
       
   882                     FD_SET(fd, &rset);
       
   883             } else
       
   884                 fd = 0;
       
   885             t = _intVal(millis);
       
   886             wt.tv_sec = t / 1000;
       
   887             wt.tv_usec = (t % 1000) * 1000;
       
   888             RETURN ( (select(fd+1, &rset, &wset, &eset, &wt) == 0) ? false
       
   889                                                                    : true );
       
   890         }
       
   891     }
       
   892 %}
       
   893 .
       
   894     self primitiveFailed
       
   895 !
       
   896 
       
   897 selectOn:fd1 and:fd2 withTimeOut:seconds
       
   898     "wait for any fd to become ready; timeout after t seconds.
       
   899      Return fd if i/o ok, nil if timed-out or interrupted.
       
   900      Experimental."
       
   901 
       
   902     |millis|
       
   903 
       
   904     millis := (seconds * 1000) rounded.
       
   905 %{
       
   906     fd_set rset, wset, eset;
       
   907     int t, f1, f2, i, lX, bX;
       
   908     struct timeval wt;
       
   909     OBJ retFd;
       
   910 
       
   911     if (((fd1 == nil) || _isSmallInteger(fd1)) 
       
   912      && ((fd2 == nil) || _isSmallInteger(fd2)))  {
       
   913         if (_isSmallInteger(millis)) {
       
   914             FD_ZERO(&rset);
       
   915             FD_ZERO(&wset);
       
   916             FD_ZERO(&eset);
       
   917             if (fd1 != nil) {
       
   918                 f1 = _intVal(fd1);
       
   919                 if ((f1 >= 0) && (f1 < FD_SETSIZE))
       
   920                     FD_SET(f1, &rset);
       
   921             } else
       
   922                 f1 = 0;
       
   923             if (fd2 != nil) {
       
   924                 f2 = _intVal(fd2);
       
   925                 if ((f2 >= 0) && (f2 < FD_SETSIZE))
       
   926                     FD_SET(f2, &rset);
       
   927             } else
       
   928                 f2 = 0;
       
   929             if (f2 > f1)
       
   930                 f1 = f2;
       
   931             t = _intVal(millis);
       
   932             wt.tv_sec = t / 1000;
       
   933             wt.tv_usec = (t % 1000) * 1000;
       
   934             if (select(f1+1, &rset, &wset, &eset, &wt)) {
       
   935                 if (FD_ISSET(f1, &rset)) retFd = fd1;
       
   936                 else if (FD_ISSET(f1, &wset)) retFd = fd1;
       
   937                 else if (FD_ISSET(f1, &eset)) retFd = fd1;
       
   938                 else if (FD_ISSET(f2, &rset)) retFd = fd2;
       
   939                 else if (FD_ISSET(f2, &wset)) retFd = fd2;
       
   940                 else if (FD_ISSET(f2, &eset)) retFd = fd2;
       
   941                 RETURN ( retFd );
       
   942             }
       
   943             RETURN ( nil );
       
   944         }
       
   945     }
       
   946 %}
       
   947 .
       
   948     self primitiveFailed
       
   949 !
       
   950 
       
   951 selectOnAnyReadable:fdArray withTimeOut:seconds
       
   952     "wait for any fd in fdArray (an Array of integers) to become ready;
       
   953      timeout after t seconds. An empty set will always wait.
       
   954      Return first ready fd if i/o ok, nil if timed-out or interrupted.
       
   955      Experimental."
       
   956 
       
   957     |millis count|
       
   958 
       
   959     millis := (seconds * 1000) rounded asInteger.
       
   960     (fdArray class == Array) ifFalse:[
       
   961         ^ self error:'argument must be an Array'
       
   962     ].
       
   963     count := fdArray size.
       
   964 %{
       
   965     fd_set rset, wset, eset;
       
   966     int t, f, maxF, i, lX, bX;
       
   967     struct timeval wt;
       
   968     OBJ fd, retFd;
       
   969 
       
   970     if (_isSmallInteger(millis)) {
       
   971         FD_ZERO(&rset);
       
   972         FD_ZERO(&wset);
       
   973         FD_ZERO(&eset);
       
   974 
       
   975         maxF = 0;
       
   976         for (i=0; i<_intVal(count);i++) {
       
   977             fd = _ArrayInstPtr(fdArray)->a_element[i];
       
   978             if (fd != nil) {
       
   979                 f = _intVal(fd);
       
   980                 if ((f >= 0) && (f < FD_SETSIZE)) {
       
   981                     FD_SET(f, &rset);
       
   982 /*                   FD_SET(f, &wset);       */
       
   983 /*                   FD_SET(f, &eset);       */
       
   984                     if (f > maxF) maxF = f;
       
   985                 }
       
   986             }
       
   987         }
       
   988         t = _intVal(millis);
       
   989         wt.tv_sec = t / 1000;
       
   990         wt.tv_usec = (t % 1000) * 1000;
       
   991         if (select(maxF+1, &rset, &wset, &eset, &wt)) {
       
   992             for (i=0; i <= maxF; i++) {
       
   993                 if (FD_ISSET(i, &rset)
       
   994 /*                 || FD_ISSET(i, &wset)
       
   995                    || FD_ISSET(i, &eset) */ ) {
       
   996                     RETURN ( _MKSMALLINT(i) );
       
   997                 }
       
   998             }
       
   999         }
       
  1000         RETURN ( nil );
       
  1001     }
       
  1002 %}
       
  1003 .
       
  1004     self primitiveFailed
       
  1005 ! !
       
  1006 
       
  1007 !OperatingSystem class methodsFor:'executing commands'!
       
  1008 
       
  1009 fork
       
  1010     "fork a new process"
       
  1011 
       
  1012 %{  /* NOCONTEXT */
       
  1013 
       
  1014     int pid;
       
  1015 
       
  1016     pid = fork();
       
  1017     RETURN ( _MKSMALLINT(pid) );
       
  1018 %}
       
  1019 .
       
  1020     self primitiveFailed
       
  1021 !
       
  1022 
       
  1023 exec:aPath withArguments:argArray
       
  1024     "execute the unix command specified by the argument, aPath, with
       
  1025      arguments in argArray.
       
  1026      If successful, this method does not return and smalltalk is gone.
       
  1027      If not sucessfull, false is returned. Normal use is with fork."
       
  1028 
       
  1029 %{
       
  1030     char *argv[64];
       
  1031     int nargs, i;
       
  1032     OBJ arg;
       
  1033 
       
  1034     if (_isString(aPath) && _isArray(argArray)) {
       
  1035 	nargs = _arraySize(argArray);
       
  1036         for (i=0; i < nargs; i++) {
       
  1037             arg = _ArrayInstPtr(argArray)->a_element[i];
       
  1038 	    if (_isString(arg)) {
       
  1039                 argv[i] = (char *) _stringVal(arg);
       
  1040 	    }
       
  1041         }
       
  1042         argv[i] = NULL;
       
  1043         execv(_stringVal(aPath), argv);
       
  1044         /* should not be reached */
       
  1045         RETURN ( false );
       
  1046     }
       
  1047 %}
       
  1048 .
       
  1049     self primitiveFailed
       
  1050 !
       
  1051 
       
  1052 executeCommand:aCommandString
       
  1053     "execute the unix command specified by the argument, aCommandString.
       
  1054      Return true if successful, false otherwise. Smalltalk is suspended,
       
  1055      while the command is executing."
       
  1056 
       
  1057 %{  /* NOCONTEXT */
       
  1058 
       
  1059     int status;
       
  1060     extern OBJ ErrorNumber;
       
  1061 
       
  1062     if (_isString(aCommandString)) {
       
  1063         status = system((char *) _stringVal(aCommandString));
       
  1064         if (status == 0) {
       
  1065             RETURN ( true );
       
  1066         }
       
  1067         ErrorNumber = _MKSMALLINT(errno);
       
  1068         RETURN ( false );
       
  1069     }
       
  1070 %}
       
  1071 .
       
  1072     self primitiveFailed
       
  1073 
       
  1074     "OperatingSystem executeCommand:'pwd'"
       
  1075     "OperatingSystem executeCommand:'ls -l'"
       
  1076     "OperatingSystem executeCommand:'invalidCommand'"
       
  1077 ! !
       
  1078 
       
  1079 !OperatingSystem class methodsFor:'file access'!
       
  1080 
       
  1081 baseNameOf:aPath
       
  1082     "return the baseName of the argument, aPath
       
  1083      - thats the file/directory name without leading parent-dirs
       
  1084      (i.e. OperatingSystem baseNameOf:'/usr/lib/st/file' -> 'file'
       
  1085        and OperatingSystem baseNameOf:'/usr/lib' -> lib).
       
  1086      This method does not check if the path is valid (i.e. if these directories
       
  1087      really exist)."
       
  1088 
       
  1089     |prev index|
       
  1090 
       
  1091     (aPath = '/') ifTrue:[^ aPath].
       
  1092     prev := 1.
       
  1093     [true] whileTrue:[
       
  1094         index := aPath indexOf:$/
       
  1095                     startingAt:prev
       
  1096                       ifAbsent:[^ aPath copyFrom:prev].
       
  1097         prev := index + 1
       
  1098     ]
       
  1099 !
       
  1100 
       
  1101 directoryNameOf:aPath
       
  1102     "return the directoryName of the argument, aPath
       
  1103      - thats the name of the directory where aPath is
       
  1104      (i.e. OperatingSystem directoryNameOf:'/usr/lib/st/file' -> '/usr/lib/st'
       
  1105        and OperatingSystem directoryNameOf:'/usr/lib' -> /usr').
       
  1106      This method does not check if the path is valid (i.e. if these directories
       
  1107      really exist)."
       
  1108 
       
  1109     |last|
       
  1110 
       
  1111     (aPath = '/') ifTrue:[^ aPath].
       
  1112     (aPath startsWith:'/') ifFalse:[
       
  1113         (aPath endsWith:'/') ifTrue:[
       
  1114             ^ aPath copyFrom:1 to:(aPath size - 1)
       
  1115         ].
       
  1116     ].
       
  1117     last := 1.
       
  1118     [true] whileTrue:[
       
  1119         last := aPath indexOf:$/
       
  1120                    startingAt:(last + 1)
       
  1121                      ifAbsent:[(last == 1) ifTrue:[^ '/'].
       
  1122                                ^ aPath copyFrom:1 to:(last - 1)
       
  1123                               ]
       
  1124     ]
       
  1125 
       
  1126     "OperatingSystem directoryNameOf:'/fee/foo/bar'"
       
  1127     "OperatingSystem directoryNameOf:'foo/bar'"
       
  1128     "OperatingSystem directoryNameOf:'../../foo/bar'"
       
  1129 !
       
  1130 
       
  1131 isValidPath:aPathName
       
  1132     "return true, if 'aPathName' is a valid path name
       
  1133      (i.e. the file or directory exists)"
       
  1134 
       
  1135 %{  /* NOCONTEXT */
       
  1136 
       
  1137     struct stat buf;
       
  1138 
       
  1139     if (_isString(aPathName)) {
       
  1140         RETURN ( (stat((char *) _stringVal(aPathName), &buf) < 0) ? false : true );
       
  1141     }
       
  1142 %}
       
  1143 .
       
  1144     self primitiveFailed
       
  1145 !
       
  1146 
       
  1147 isDirectory:aPathName
       
  1148     "return true, if 'aPathName' is a valid directory path name.
       
  1149      (i.e. exists and is a directory)"
       
  1150 
       
  1151 %{  /* NOCONTEXT */
       
  1152 
       
  1153     struct stat buf;
       
  1154 
       
  1155     if (_isString(aPathName)) {
       
  1156         if ((stat((char *) _stringVal(aPathName), &buf) < 0)
       
  1157          || ((buf.st_mode & S_IFMT) != S_IFDIR)) {
       
  1158             RETURN ( false );
       
  1159         }
       
  1160         RETURN ( true );
       
  1161     }
       
  1162 %}
       
  1163 .
       
  1164     self primitiveFailed
       
  1165 !
       
  1166 
       
  1167 isReadable:aPathName
       
  1168     "return true, if the file/dir 'aPathName' is readable."
       
  1169 
       
  1170 %{  /* NOCONTEXT */
       
  1171 
       
  1172     extern OBJ ErrorNumber;
       
  1173 
       
  1174     if (_isString(aPathName)) {
       
  1175         if (access(_stringVal(aPathName), R_OK) == 0) {
       
  1176             RETURN ( true );
       
  1177         }
       
  1178         ErrorNumber = _MKSMALLINT(errno);
       
  1179         RETURN ( false );
       
  1180     }
       
  1181 %}
       
  1182 .
       
  1183     self primitiveFailed
       
  1184 !
       
  1185 
       
  1186 isWritable:aPathName
       
  1187     "return true, if the given file is writable"
       
  1188 
       
  1189 %{  /* NOCONTEXT */
       
  1190 
       
  1191     extern OBJ ErrorNumber;
       
  1192 
       
  1193     if (_isString(aPathName)) {
       
  1194         if (access(_stringVal(aPathName), W_OK) == 0) {
       
  1195             RETURN ( true );
       
  1196         }
       
  1197         ErrorNumber = _MKSMALLINT(errno);
       
  1198         RETURN ( false );
       
  1199     }
       
  1200 %}
       
  1201 .
       
  1202     self primitiveFailed
       
  1203 !
       
  1204 
       
  1205 isExecutable:aPathName
       
  1206     "return true, if the given file is executable"
       
  1207 
       
  1208 %{  /* NOCONTEXT */
       
  1209 
       
  1210     extern OBJ ErrorNumber;
       
  1211 
       
  1212     if (_isString(aPathName)) {
       
  1213         if (access(_stringVal(aPathName), X_OK) == 0) {
       
  1214             RETURN ( true );
       
  1215         }
       
  1216         ErrorNumber = _MKSMALLINT(errno);
       
  1217         RETURN ( false );
       
  1218     }
       
  1219 %}
       
  1220 .
       
  1221     self primitiveFailed
       
  1222 !
       
  1223 
       
  1224 infoOf:aPathName
       
  1225     "return an dictionary filled with info for the file 'aPathName';
       
  1226      info is: (type->t mode->n uid->u gid->g size->s id->ino).
       
  1227      return nil if such a file does not exist. A dictionary is returned,
       
  1228      since we might need to add more info in the future without affecting
       
  1229      existing applications."
       
  1230 
       
  1231     |info type mode uid gid size id|
       
  1232 
       
  1233     "{ Symbol: directory }"
       
  1234     "{ Symbol: regular }"
       
  1235     "{ Symbol: characterSpecial }"
       
  1236     "{ Symbol: blockSpecial }"
       
  1237     "{ Symbol: fifo }"
       
  1238     "{ Symbol: socket }"
       
  1239     "{ Symbol: symbolicLink }"
       
  1240     "{ Symbol: unknown }"
       
  1241 
       
  1242 %{
       
  1243     struct stat buf;
       
  1244 
       
  1245     if (_isString(aPathName)) {
       
  1246         if (stat((char *) _stringVal(aPathName), &buf) < 0) {
       
  1247             RETURN ( nil );
       
  1248         }
       
  1249         switch (buf.st_mode & S_IFMT) {
       
  1250             case S_IFDIR:
       
  1251                 type = _directory;
       
  1252                 break;
       
  1253             case S_IFCHR:
       
  1254                 type = _characterSpecial;
       
  1255                 break;
       
  1256             case S_IFBLK:
       
  1257                 type = _blockSpecial;
       
  1258                 break;
       
  1259             case S_IFREG:
       
  1260                 type = _regular;
       
  1261                 break;
       
  1262 #ifdef S_IFLNK
       
  1263             case S_IFLNK:
       
  1264                 type = _symbolicLink;
       
  1265                 break;
       
  1266 #endif
       
  1267 #ifdef S_IFSOCK
       
  1268             case S_IFSOCK:
       
  1269                 type = _socket;
       
  1270                 break;
       
  1271 #endif
       
  1272 #ifdef S_IFIFO
       
  1273             case S_IFIFO:
       
  1274                 type = _fifo;
       
  1275                 break;
       
  1276 #endif
       
  1277             default:
       
  1278                 type = _unknown;
       
  1279                 break;
       
  1280         }
       
  1281         mode = _MKSMALLINT(buf.st_mode & 0777);
       
  1282         uid = _MKSMALLINT(buf.st_uid);
       
  1283         gid = _MKSMALLINT(buf.st_gid);
       
  1284         size = _MKSMALLINT(buf.st_size);
       
  1285         id = _MKSMALLINT(buf.st_ino);
       
  1286     }
       
  1287 %}
       
  1288 .
       
  1289     mode notNil ifTrue:[
       
  1290         info := IdentityDictionary new.
       
  1291         info at:#type put:type.
       
  1292         info at:#mode put:mode.
       
  1293         info at:#uid put:uid.
       
  1294         info at:#gid put:gid.
       
  1295         info at:#size put:size.
       
  1296         info at:#id put:id.
       
  1297         ^ info
       
  1298    ].
       
  1299    self primitiveFailed
       
  1300 
       
  1301    "OperatingSystem infoOf:'/'"
       
  1302    "(OperatingSystem infoOf:'/') at:#uid"
       
  1303 !
       
  1304 
       
  1305 accessModeOf:aPathName
       
  1306     "return a number representing access rights rwxrwxrwx for owner,
       
  1307      group and others. return nil if such a file does not exist."
       
  1308 
       
  1309     "
       
  1310      this could have been implemented as:
       
  1311         (self infoOf:aPathName) at:#mode
       
  1312      but for huge directory searches the code below is faster
       
  1313     "
       
  1314 
       
  1315 %{  /* NOCONTEXT */
       
  1316 
       
  1317     struct stat buf;
       
  1318 
       
  1319     if (_isString(aPathName)) {
       
  1320         if (stat((char *) _stringVal(aPathName), &buf) < 0) {
       
  1321             RETURN ( nil );
       
  1322         }
       
  1323         RETURN ( _MKSMALLINT(buf.st_mode & 0777) );
       
  1324     }
       
  1325 %}
       
  1326 .
       
  1327    self primitiveFailed
       
  1328 
       
  1329    "(OperatingSystem accessModeOf:'/') printStringRadix:8"
       
  1330 !
       
  1331 
       
  1332 changeAccessModeOf:aPathName to:modeBits
       
  1333     "change the access rights rwxrwxrwx for owner,
       
  1334      group and others of aPathName. return true if changed, false
       
  1335      if such a file does not exist or change was not allowd."
       
  1336 
       
  1337 %{  /* NOCONTEXT */
       
  1338 
       
  1339     if (_isString(aPathName) && _isSmallInteger(modeBits)) {
       
  1340         RETURN ( (chmod((char *) _stringVal(aPathName), _intVal(modeBits) ) < 0) ?
       
  1341                                 false : true );
       
  1342     }
       
  1343 %}
       
  1344 .
       
  1345    self primitiveFailed
       
  1346 !
       
  1347 
       
  1348 timeOfLastChange:aPathName
       
  1349     "return the timeStamp of a file"
       
  1350 
       
  1351     |timeLow timeHi|
       
  1352 %{
       
  1353     struct stat buf;
       
  1354     time_t mtime;
       
  1355 
       
  1356     if (_isString(aPathName)) {
       
  1357         if (stat((char *) _stringVal(aPathName), &buf) >= 0) {
       
  1358             timeLow = _MKSMALLINT(buf.st_mtime & 0xFFFF);
       
  1359             timeHi = _MKSMALLINT((buf.st_mtime >> 16) & 0xFFFF);
       
  1360         }
       
  1361     }
       
  1362 %}
       
  1363 .
       
  1364     timeLow notNil ifTrue:[^ Time fromUnixTimeLow:timeLow and:timeHi].
       
  1365     self primitiveFailed
       
  1366 
       
  1367     "OperatingSystem timeOfLastChange:'/'"
       
  1368 !
       
  1369 
       
  1370 idOf:aPathName
       
  1371     "return the fileNumber (i.e. inode number) of a file"
       
  1372 
       
  1373     "
       
  1374      this could have been implemented as:
       
  1375         (self infoOf:aPathName) at:#id 
       
  1376      but for huge directory searches the code below is faster
       
  1377     "
       
  1378 
       
  1379 %{  /* NOCONTEXT */
       
  1380 
       
  1381     struct stat buf;
       
  1382 
       
  1383     if (_isString(aPathName)) {
       
  1384         if (stat((char *) _stringVal(aPathName), &buf) >= 0) {
       
  1385             RETURN (_MKSMALLINT(buf.st_ino));
       
  1386         }
       
  1387     }
       
  1388 %}
       
  1389 .
       
  1390     self primitiveFailed
       
  1391 
       
  1392     "OperatingSystem idOf:'/'"
       
  1393 !
       
  1394 
       
  1395 typeOf:aPathName
       
  1396     "return the type of a file as a symbol"
       
  1397 
       
  1398     "
       
  1399      this could have been implemented as:
       
  1400         (self infoOf:aPathName) at:#type 
       
  1401      but for huge directory searches the code below is faster
       
  1402     "
       
  1403 
       
  1404 %{  /* NOCONTEXT */
       
  1405 
       
  1406     struct stat buf;
       
  1407 
       
  1408     if (_isString(aPathName)) {
       
  1409         if (stat((char *) _stringVal(aPathName), &buf) < 0) {
       
  1410             RETURN ( nil );
       
  1411         }
       
  1412         switch (buf.st_mode & S_IFMT) {
       
  1413             case S_IFDIR:
       
  1414                 RETURN ( _directory );
       
  1415             case S_IFCHR:
       
  1416                 RETURN ( _characterSpecial );
       
  1417             case S_IFBLK:
       
  1418                 RETURN ( _blockSpecial );
       
  1419             case S_IFREG:
       
  1420                 RETURN ( _regular );
       
  1421 #ifdef S_IFLNK
       
  1422             case S_IFLNK:
       
  1423                 RETURN ( _symbolicLink );
       
  1424 #endif
       
  1425 #ifdef S_IFSOCK
       
  1426             case S_IFSOCK:
       
  1427                 RETURN ( _socket );
       
  1428 #endif
       
  1429 #ifdef S_IFIFO
       
  1430             case S_IFIFO:
       
  1431                 RETURN ( _fifo );
       
  1432 #endif
       
  1433             default:
       
  1434                 RETURN ( _unknown );
       
  1435         }
       
  1436     }
       
  1437 %}
       
  1438 .
       
  1439     self primitiveFailed
       
  1440 
       
  1441     "OperatingSystem typeOf:'/'"
       
  1442 !
       
  1443 
       
  1444 createDirectory:newPathName
       
  1445     "create a new directory with name 'newPathName'.
       
  1446      Return true if successful, false if failed."
       
  1447 
       
  1448     "since createDirectory is not used too often,
       
  1449      you'll forgive me using mkdir ..."
       
  1450 
       
  1451     ^ self executeCommand:('mkdir ' , newPathName)
       
  1452 
       
  1453     "OperatingSystem createDirectory:'foo'"
       
  1454 !
       
  1455 
       
  1456 recursiveCreateDirectory:dirName
       
  1457     "create a directory - with all parent dirs if needed.
       
  1458      Return true if successful, false otherwise. If false
       
  1459      is returned, a partial created tree may be left,
       
  1460      which is not cleaned-up here."
       
  1461 
       
  1462     self createDirectory:dirName.
       
  1463     (self isDirectory:dirName) ifFalse:[
       
  1464         (self recursiveCreateDirectory:(self directoryNameOf:dirName)) ifFalse:[^ false].
       
  1465         ^ self createDirectory:dirName
       
  1466     ].
       
  1467     ^ (self isDirectory:dirName)
       
  1468 
       
  1469     "OperatingSystem recursiveCreateDirectory:'foo/bar/baz'"
       
  1470 !
       
  1471 
       
  1472 removeFile:fullPathName
       
  1473     "remove the file named 'fullPathName'; return true if successful"
       
  1474 
       
  1475 %{  /* NOCONTEXT */
       
  1476 
       
  1477     if (_isString(fullPathName)) {
       
  1478         RETURN ( (unlink((char *) _stringVal(fullPathName)) >= 0) ? true : false );
       
  1479     }
       
  1480 %}
       
  1481 .
       
  1482     self primitiveFailed
       
  1483 !
       
  1484 
       
  1485 removeDirectory:fullPathName
       
  1486     "remove the directory named 'fullPathName'.
       
  1487      Return true if successful, false if directory is not empty or no permission"
       
  1488 
       
  1489 %{  /* NOCONTEXT */
       
  1490 
       
  1491     if (_isString(fullPathName)) {
       
  1492         RETURN ( (rmdir((char *) _stringVal(fullPathName)) >= 0) ? true : false );
       
  1493     }
       
  1494 %}
       
  1495 .
       
  1496     self primitiveFailed
       
  1497 !
       
  1498 
       
  1499 link:oldPath to:newPath
       
  1500     "link the file 'oldPath' to 'newPath'. The link will be a hard link.
       
  1501      Return true if successful, false if not."
       
  1502 
       
  1503 %{  /* NOCONTEXT */
       
  1504 
       
  1505     if (_isString(oldPath) && _isString(newPath)) {
       
  1506         RETURN ( (link((char *) _stringVal(oldPath), (char *) _stringVal(newPath)) >= 0) ?
       
  1507                                 true : false );
       
  1508     }
       
  1509 %}
       
  1510 .
       
  1511     self primitiveFailed
       
  1512 
       
  1513     "OperatingSystem link:'foo' to:'bar'"
       
  1514 !
       
  1515 
       
  1516 rename:oldPath to:newPath
       
  1517     "rename the file 'oldPath' to 'newPath'. 
       
  1518      Return true if sucessfull, false if not"
       
  1519 
       
  1520 %{  /* NOCONTEXT */
       
  1521 
       
  1522     if (_isString(oldPath) && _isString(newPath)) {
       
  1523 #if defined(BSD)
       
  1524         if (rename((char *) _stringVal(oldPath), (char *) _stringVal(newPath)) >= 0) {
       
  1525             RETURN ( true );
       
  1526         }
       
  1527 #else
       
  1528         if (link((char *) _stringVal(oldPath), (char *) _stringVal(newPath)) >= 0) {
       
  1529             if (unlink((char *) _stringVal(oldPath)) >= 0) {
       
  1530                 RETURN ( true );
       
  1531             }
       
  1532             unlink((char *) _stringVal(newPath));
       
  1533         }
       
  1534 #endif
       
  1535         RETURN ( false );
       
  1536     }
       
  1537 %}
       
  1538 .
       
  1539     self primitiveFailed
       
  1540 
       
  1541     "OperatingSystem rename:'foo' to:'bar'"
       
  1542 ! !