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