2 * Primitives of the LittleSmalltalk system
4 * ---------------------------------------------------------------
5 * Little Smalltalk, Version 5
7 * Copyright (C) 1987-2005 by Timothy A. Budd
8 * Copyright (C) 2007 by Charles R. Childers
9 * Copyright (C) 2005-2007 by Danny Reinhold
10 * Copyright (C) 2010 by Ketmar // Vampire Avalon
12 * ============================================================================
13 * This license applies to the virtual machine and to the initial image of
14 * the Little Smalltalk system and to all files in the Little Smalltalk
15 * packages except the files explicitly licensed with another license(s).
16 * ============================================================================
17 * Permission is hereby granted, free of charge, to any person obtaining a copy
18 * of this software and associated documentation files (the "Software"), to deal
19 * in the Software without restriction, including without limitation the rights
20 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
21 * copies of the Software, and to permit persons to whom the Software is
22 * furnished to do so, subject to the following conditions:
24 * The above copyright notice and this permission notice shall be included in
25 * all copies or substantial portions of the Software.
27 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
28 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
29 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
30 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
31 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
32 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
33 * DEALINGS IN THE SOFTWARE.
42 #include <sys/types.h>
45 #include "lstcore/k8lst.h"
49 # include "windows.h" /* for Sleep() */
54 #include "lstpl_stdlib.h"
57 static const char *fileId
= "FileHandle";
65 LST_FINALIZER(lpFileHandleFinalizer
) {
66 FileInfo
*fi
= (FileInfo
*)udata
;
68 if (fi
->fl
) fclose(fi
->fl
);
74 static lstObject
*newFileHandle (FILE *fl
) {
76 lstObject
*res
= lstNewBinary(NULL
, 0);
77 fi
= malloc(sizeof(FileInfo
));
78 if (!fi
) { fclose(fl
); return lstNilObj
; }
81 lstSetFinalizer(res
, lpFileHandleFinalizer
, fi
);
86 static FILE *getFile (lstObject
*o
) {
87 if (LST_IS_SMALLINT(o
) || !LST_IS_BYTES(o
) || LST_SIZE(o
) || !o
->fin
|| !o
->fin
->udata
) return NULL
;
88 FileInfo
*fi
= (FileInfo
*)o
->fin
->udata
;
89 if (fi
->type
!= fileId
) return NULL
;
94 static char nameBuffer
[8192], modeBuffer
[64];
96 LST_PRIMFN(lpFileExists
) {
97 if (LST_PRIMARGC
!= 1) return NULL
;
98 lstObject
*o
= LST_PRIMARG(0);
99 if (!LST_IS_BYTES(o
)) return NULL
;
100 lstGetString(nameBuffer
, 8190, LST_PRIMARG(0));
101 FILE *fp
= fopen(nameBuffer
, "rb");
102 if (fp
== NULL
) return lstFalseObj
;
108 LST_PRIMFN(lpFileOpen
) {
109 if (LST_PRIMARGC
!= 2) return NULL
;
110 if (!LST_IS_BYTES(LST_PRIMARG(0))) return NULL
;
111 if (!LST_IS_BYTES(LST_PRIMARG(1))) return NULL
;
112 lstGetString(nameBuffer
, 8190, LST_PRIMARG(0));
113 lstGetString(modeBuffer
, 60, LST_PRIMARG(1));
114 FILE *fp
= fopen(nameBuffer
, modeBuffer
);
115 if (fp
== NULL
) return lstNilObj
;
116 return newFileHandle(fp
);
121 FILE *fp = getFile(LST_PRIMARG(0)); \
122 if (!fp) return NULL;
125 LST_PRIMFN(lpFileGetChar
) {
126 if (LST_PRIMARGC
!= 1) return NULL
;
129 return ch
==EOF
? lstNilObj
: lstNewInt(ch
);
133 LST_PRIMFN(lpFilePutChar
) {
134 if (LST_PRIMARGC
!= 2) return NULL
;
136 if (!LST_IS_SMALLINT(LST_PRIMARG(1))) return NULL
;
137 return fputc(lstIntValue(LST_PRIMARG(1)), fp
)==EOF
? lstFalseObj
: lstTrueObj
;
141 LST_PRIMFN(lpFileClose
) {
142 if (LST_PRIMARGC
!= 1) return NULL
;
144 FileInfo
*fi
= (FileInfo
*)((LST_PRIMARG(0))->fin
->udata
);
151 LST_PRIMFN(lpFileWriteImage
) {
153 if (LST_PRIMARGC
!= 2) return NULL
;
155 if (LST_IS_SMALLINT(LST_PRIMARG(1))) noSrc
= lstIntValue(LST_PRIMARG(1));
156 else if (LST_PRIMARG(1) == lstTrueObj
) noSrc
= 1;
157 return lstNewInt(lstWriteImage(fp
, noSrc
));
161 LST_PRIMFN(lpFileReadByteArray
) {
162 if (LST_PRIMARGC
!= 3) return NULL
;
164 /* make sure we're populating an array of bytes */
165 lstObject
*res
= LST_PRIMARG(1);
166 if (!LST_IS_BYTES(res
)) return NULL
;
167 /* sanity check on I/O count */
168 if (!LST_IS_SMALLINT(LST_PRIMARG(2))) return NULL
;
169 int len
= lstIntValue(LST_PRIMARG(2));
170 if (len
< 0 || len
> LST_SIZE(res
)) return NULL
;
172 if (len
> 0) len
= fread(lstBytePtr(res
), 1, len
, fp
);
173 if (len
< 0) return lstNilObj
;
174 return lstNewInt(len
);
178 LST_PRIMFN(lpFileWriteByteArray
) {
179 if (LST_PRIMARGC
!= 3) return NULL
;
181 /* make sure we're writing an array of bytes */
182 lstObject
*res
= LST_PRIMARG(1);
183 if (!LST_IS_BYTES(res
)) return NULL
;
184 /* sanity check on I/O count */
185 if (!LST_IS_SMALLINT(LST_PRIMARG(2))) return NULL
;
186 int len
= lstIntValue(LST_PRIMARG(2));
187 if (len
< 0 || len
> LST_SIZE(res
)) return NULL
;
189 if (len
> 0) len
= fwrite(lstBytePtr(res
), 1, len
, fp
);
190 if (len
< 0) return lstNilObj
;
191 return lstNewInt(len
);
195 LST_PRIMFN(lpFileSeek
) {
197 if (LST_PRIMARGC
!= 2) return NULL
;
200 if (LST_IS_SMALLINT(LST_PRIMARG(1))) ofs
= lstIntValue(LST_PRIMARG(1));
201 else if (LST_PRIMARG(1)->stclass
== lstIntegerClass
) ofs
= lstLIntValue(LST_PRIMARG(1));
203 if (ofs
< 0 || fseek(fp
, ofs
, SEEK_SET
)) return NULL
;
204 /* return position as our value */
205 return LST_PRIMARG(1);
209 LST_PRIMFN(lpFileSize
) {
211 if (LST_PRIMARGC
!= 1) return NULL
;
213 if ((opos
= ftell(fp
)) < 0) return NULL
;
214 if (fseek(fp
, 0, SEEK_END
) < 0) return NULL
;
216 fseek(fp
, opos
, SEEK_SET
);
217 if (size
< 0) return NULL
;
218 return lstNewInteger(size
);
222 LST_PRIMFN(lpFileGetFD
) {
223 if (LST_PRIMARGC
!= 1) return NULL
;
225 return lstNewInt(fileno(fp
));
229 LST_PRIMFN(lpFileReadLine
) {
230 static char buf
[8192]; /*FIXME*/
231 if (LST_PRIMARGC
!= 1) return NULL
;
233 if (!fgets(buf
, sizeof(buf
), fp
)) return lstNilObj
;
234 int len
= strlen(buf
)-1;
235 if (len
>= 0 && buf
[len
] == '\n') {
236 if (len
> 0 && buf
[len
-1] == '\r') buf
[len
-1] = '\0'; else buf
[len
] = '\0';
238 return lstNewString(buf
);
242 LST_PRIMFN(lpSystemGetOSName
) {
243 if (LST_PRIMARGC
!= 0) return NULL
;
244 return lstNewString(lstOSName
);
248 LST_PRIMFN(lpSystemIsATTY
) {
249 if (LST_PRIMARGC
!= 1) return NULL
;
250 return isatty(lstIntValue(LST_PRIMARG(0))) ? lstTrueObj
: lstFalseObj
;
254 LST_PRIMFN(lpSystemSleep
) {
257 if (LST_PRIMARGC
!= 1) return NULL
;
259 if (LST_IS_SMALLINT(o
)) pauseMs
= lstIntValue(o
)*1000;
260 else if (o
->stclass
== lstIntegerClass
) pauseMs
= lstLIntValue(o
)*1000;
261 else if (o
->stclass
== lstFloatClass
) {
262 LstFloat fv
= lstFloatValue(o
);
263 pauseMs
= (int)(fv
*1000.0);
265 if (pauseMs
< 0) return lstNilObj
;
268 //2000000 == 2 seconds == 2000 milliseconds
269 int sec
= pauseMs
/1000;
270 if (sec
> 0) sleep(sec
);
271 usleep((pauseMs
%1000)*1000);
282 LST_PRIMFN(lpSystemUnixTime
) {
283 time_t t
= time(NULL
);
284 return LST_64FITS_SMALLINT(t
) ? lstNewInt(t
) : lstNewLongInt(t
);
288 LST_PRIMFN(lpSystemLocalTime
) {
290 if (LST_PRIMARGC
> 0) {
291 lstObject
*o
= LST_PRIMARG(0);
292 if (LST_IS_SMALLINT(o
)) t
= lstIntValue(o
);
293 else if (o
->stclass
== lstIntegerClass
) t
= lstLIntValue(o
);
294 else if (o
->stclass
== lstFloatClass
) {
295 LstFloat fv
= lstFloatValue(o
);
301 struct tm
*lt
= localtime(&t
);
302 lstObject
*res
= lstNewArray(9);
303 res
->data
[0] = lstNewInt(lt
->tm_sec
);
304 res
->data
[1] = lstNewInt(lt
->tm_min
);
305 res
->data
[2] = lstNewInt(lt
->tm_hour
);
306 res
->data
[3] = lstNewInt(lt
->tm_mday
);
307 res
->data
[4] = lstNewInt(lt
->tm_mon
+1); /* convert to 1..12 */
308 res
->data
[5] = lstNewInt(lt
->tm_year
+1900);
309 res
->data
[6] = lstNewInt(lt
->tm_wday
); /* 0: sunday */
310 res
->data
[7] = lstNewInt(lt
->tm_yday
);
311 res
->data
[8] = lt
->tm_isdst
? lstTrueObj
: lstFalseObj
;
316 static char lstMyPath
[8192];
317 static void initMyPath (void) {
320 pid_t pid
= getpid();
321 sprintf(buf
, "/proc/%u/exe", (unsigned int)pid
);
322 if (readlink(buf
, lstMyPath
, sizeof(lstMyPath
)-1) < 0) strcpy(lstMyPath
, "./");
324 char *p
= (char *)strrchr(lstMyPath
, '/');
325 if (!p
) strcpy(lstMyPath
, "./"); else p
[1] = '\0';
329 memset(lstMyPath
, 0, sizeof(lstMyPath
));
330 GetModuleFileName(GetModuleHandle(NULL
), lstMyPath
, sizeof(lstMyPath
)-1);
331 p
= strrchr(lstMyPath
, '\\');
332 if (!p
) strcpy(lstMyPath
, ".\\"); else p
[1] = '\0';
337 LST_PRIMFN(lpSystemBinPath
) {
338 return lstNewString(lstMyPath
);
342 LST_PRIMFN(lpStringToURLEncoding
) {
343 if (LST_PRIMARGC
!= 1) return NULL
;
344 unsigned char *res
, *src
; int dp
, slen
;
345 lstObject
*s
= LST_PRIMARG(0);
346 if (LST_IS_SMALLINT(s
) || s
->stclass
!= lstStringClass
) return NULL
;
348 res
= malloc(slen
*3);
349 src
= (unsigned char *)lstBytePtr(s
);
350 for (dp
= 0; slen
> 0; src
++, slen
--) {
351 unsigned char ch
= *src
;
356 sprintf(buf
, "%%%02X", ch
);
357 memcpy(res
+dp
, buf
, 3);
361 /*FIXME: memory leak on fail*/
362 s
= (lstObject
*)lstMemAllocBin(dp
);
363 s
->stclass
= lstStringClass
;
364 if (dp
> 0) memcpy(lstBytePtr(s
), res
, dp
);
370 LST_PRIMFN(lpStringFromURLEncoding
) {
371 if (LST_PRIMARGC
< 1) return NULL
;
372 unsigned char *res
, *src
; int dp
, slen
;
373 lstObject
*s
= LST_PRIMARG(0);
374 if (LST_IS_SMALLINT(s
) || s
->stclass
!= lstStringClass
) return NULL
;
377 src
= (unsigned char *)lstBytePtr(s
);
378 int doPlus
= LST_PRIMARGC
> 1;
379 for (dp
= 0; slen
> 0; src
++, slen
--) {
380 unsigned char ch
= *src
;
381 if (ch
== '%' && slen
>= 3 && isxdigit(src
[1]) && isxdigit(src
[2])) {
382 int n
= toupper(src
[1])-'0'; if (n
> 9) n
-= 7;
384 n
= toupper(src
[2])-'0'; if (n
> 9) n
-= 7;
389 } else if (doPlus
&& ch
== '+') {
395 /*FIXME: memory leak on fail*/
396 s
= (lstObject
*)lstMemAllocBin(dp
);
397 s
->stclass
= lstStringClass
;
398 if (dp
> 0) memcpy(lstBytePtr(s
), res
, dp
);
405 LST_PRIMFN(lpStringOpsExt) {
407 if (LST_PRIMARGC != 2) return NULL;
408 lstObject *op = LST_PRIMARG(0);
409 if (!LST_IS_SMALLINT(op)) return NULL;
410 action = lstIntValue(op);
411 lstObject *op = LST_PRIMARG(1);
412 if (LST_CLASS(op) != lstStringClass) return NULL;
414 case 0: return lpStringToURLEncoding(op);
415 case 1: return lpStringFromURLEncoding(op);
422 LST_PRIMFN(lpSystemSystem
) {
423 if (LST_PRIMARGC
!= 1) return NULL
;
424 lstObject
*o
= LST_PRIMARG(0);
425 if (!LST_IS_BYTES(o
)) return NULL
;
426 int size
= LST_SIZE(o
);
427 if (size
< 1) return lstNilObj
;
428 char *cmd
= malloc(sizeof(char)*(size
+4));
429 if (!cmd
) return NULL
;
430 lstGetString(cmd
, size
+2, o
);
431 int res
= system(cmd
);
433 if (res
< 0) return lstNilObj
;
434 return lstNewInteger(res
);
438 LST_PRIMFN(lpSystemTickCountMS
) {
440 if (LST_PRIMARGC
!= 0) return NULL
;
443 clock_gettime(CLOCK_MONOTONIC
, &ts
);
444 res
= ((uint64_t)ts
.tv_sec
)*1000UL;
445 res
+= ((uint64_t)ts
.tv_nsec
)/1000000UL; //1000000000
447 res
= GetTickCount();
449 return lstNewInteger(res
);
454 static int termInitialized
= 0;
455 static struct termios origTIOS
;
456 static int inRawMode
= 0;
459 static void disableRawMode (void) {
460 tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &origTIOS
);
465 static void termCleanAtExit (void) {
470 static int enableRawMode (void) {
472 if (!isatty(STDIN_FILENO
)) goto fatal
;
473 atexit(termCleanAtExit
);
474 if (tcgetattr(STDIN_FILENO
, &origTIOS
) == -1) goto fatal
;
475 raw
= origTIOS
; /* modify the original mode */
476 /* input modes: no break, no CR to NL, no parity check, no strip char, no start/stop output control */
477 /*raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);*/
478 /* input modes: no break, no parity check, no strip char, no start/stop output control */
479 raw
.c_iflag
&= ~(BRKINT
| INPCK
| ISTRIP
| IXON
);
480 /* output modes - disable post processing */
481 raw
.c_oflag
&= ~(OPOST
);
482 raw
.c_oflag
|= ONLCR
;
483 raw
.c_oflag
= OPOST
| ONLCR
;
484 /* control modes - set 8 bit chars */
485 raw
.c_cflag
|= (CS8
);
486 /* local modes - choing off, canonical off, no extended functions, no signal chars (^Z,^C) */
487 raw
.c_lflag
&= ~(ECHO
| ICANON
| IEXTEN
| ISIG
);
488 /* control chars - set return condition: min number of bytes and timer;
489 * we want read to return every single byte, without timeout */
490 raw
.c_cc
[VMIN
] = 1; raw
.c_cc
[VTIME
] = 0; /* 1 byte, no timer */
491 /* put terminal in raw mode after flushing */
492 if (tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &raw
) < 0) goto fatal
;
501 static inline void setRawMode (int rmFlag
) {
502 if (rmFlag
) enableRawMode(); else disableRawMode();
506 static int isKeyHit (void) {
512 FD_SET(STDIN_FILENO
, &fds
); //STDIN_FILENO is 0
513 select(STDIN_FILENO
+1, &fds
, NULL
, NULL
, &tv
);
514 return FD_ISSET(STDIN_FILENO
, &fds
);
518 static void setCanonical (int cnFlag
) {
519 struct termios ttystate
;
520 tcgetattr(STDIN_FILENO
, &ttystate
);
522 ttystate
.c_lflag
&= ~ICANON
; /* turn off canonical mode */
523 ttystate
.c_cc
[VMIN
] = 1; /* minimum of number input read */
525 ttystate
.c_lflag
|= ICANON
; /* turn on canonical mode */
527 tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &ttystate
);
531 static void setEcho (int cnFlag
) {
532 struct termios ttystate
;
533 tcgetattr(STDIN_FILENO
, &ttystate
);
535 ttystate
.c_lflag
&= ~ECHO
;
537 ttystate
.c_lflag
|= ECHO
;
539 tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &ttystate
);
543 static int isCanonical (void) {
544 struct termios ttystate
;
545 tcgetattr(STDIN_FILENO
, &ttystate
);
546 return ttystate
.c_lflag
&ICANON
? 1 : 0;
550 static int isEcho (void) {
551 struct termios ttystate
;
552 tcgetattr(STDIN_FILENO
, &ttystate
);
553 return ttystate
.c_lflag
&ECHO
? 1 : 0;
557 LST_PRIMFN(lpSystemIsKeyHit
) {
558 if (LST_PRIMARGC
!= 0) return NULL
;
559 return isKeyHit() ? lstTrueObj
: lstFalseObj
;
563 LST_PRIMFN(lpSystemTermMode
) {
566 if (LST_PRIMARGC
< 1) return NULL
;
568 if (!LST_IS_SMALLINT(o
)) return NULL
;
569 if (LST_PRIMARGC
> 1) {
570 lstObject
*o
= LST_PRIMARG(1);
571 if (o
== lstNilObj
|| o
== lstFalseObj
) newv
= 0;
572 else if (o
== lstTrueObj
) newv
= 1;
573 else if (LST_IS_SMALLINT(o
)) newv
= lstIntValue(o
) ? 1 : 0;
576 switch (lstIntValue(o
)) {
577 case 0: /* canonical */
578 oldv
= isCanonical();
579 if (newv
!= -1) setCanonical(newv
);
583 if (newv
!= -1) setEcho(newv
);
587 if (newv
!= -1) setRawMode(newv
);
589 default: return NULL
;
591 return oldv
? lstTrueObj
: lstFalseObj
;
595 LST_PRIMCLEARFN(lpSystemKeyClean
) {
601 static const LSTExtPrimitiveTable primFilesTbl
[] = {
602 /*TODO: turn this into one primitive with numeric action */
603 {"FileExists", lpFileExists
, NULL
},
604 {"FileOpen", lpFileOpen
, NULL
},
605 {"FileGetChar", lpFileGetChar
, NULL
},
606 {"FilePutChar", lpFilePutChar
, NULL
},
607 {"FileClose", lpFileClose
, NULL
},
608 {"FileWriteImage", lpFileWriteImage
, NULL
},
609 {"FileReadByteArray", lpFileReadByteArray
, NULL
},
610 {"FileWriteByteArray", lpFileWriteByteArray
, NULL
},
611 {"FileSeek", lpFileSeek
, NULL
},
612 {"FileSize", lpFileSize
, NULL
},
613 {"FileGetFD", lpFileGetFD
, NULL
},
614 {"FileReadLine", lpFileReadLine
, NULL
},
617 static const LSTExtPrimitiveTable primTbl
[] = {
618 {"SystemGetOSName", lpSystemGetOSName
, NULL
},
619 {"SystemIsATTY", lpSystemIsATTY
, NULL
},
620 {"SystemSleep", lpSystemSleep
, NULL
},
621 {"SystemUnixTime", lpSystemUnixTime
, NULL
},
622 {"SystemLocalTime", lpSystemLocalTime
, NULL
},
623 {"SystemTickCountMS", lpSystemTickCountMS
, NULL
},
625 {"SystemBinPath", lpSystemBinPath
, NULL
},
627 {"SystemIsKeyHit", lpSystemIsKeyHit
, NULL
},
628 {"SystemTermMode", lpSystemTermMode
, lpSystemKeyClean
},
631 {"StringFromURLEncoding", lpStringFromURLEncoding
, NULL
},
632 {"StringToURLEncoding", lpStringToURLEncoding
, NULL
},
635 static const LSTExtPrimitiveTable primExecTbl
[] = {
636 {"SystemSystem", lpSystemSystem
, NULL
},
640 const char *lstBinDir (void) {
645 void lstInitPrimitivesStdLib (void) {
648 tcgetattr(STDIN_FILENO
, &origTIOS
);
649 if (!termInitialized
) {
651 atexit(termCleanAtExit
);
654 lstRegisterExtPrimitiveTable(primTbl
);
658 void lstInitPrimitivesFiles (void) {
659 lstRegisterExtPrimitiveTable(primFilesTbl
);
663 void lstInitPrimitivesExec (void) {
664 lstRegisterExtPrimitiveTable(primExecTbl
);