io.c (86116B)
1 /* 10jul13abu 2 * (c) Software Lab. Alexander Burger 3 */ 4 5 #include "pico.h" 6 7 #ifdef __CYGWIN__ 8 #include <sys/file.h> 9 #define fcntl(fd,cmd,fl) 0 10 #endif 11 12 static any read0(bool); 13 14 // I/O Tokens 15 enum {NIX, BEG, DOT, END}; 16 enum {NUMBER, INTERN, TRANSIENT, EXTERN}; 17 18 static char Delim[] = " \t\n\r\"'(),[]`~{}"; 19 static int StrI; 20 static cell StrCell, *StrP; 21 static bool Sync; 22 static pid_t Talking; 23 static byte *PipeBuf, *PipePtr; 24 static void (*PutSave)(int); 25 static byte TBuf[] = {INTERN+4, 'T'}; 26 27 static void openErr(any ex, char *s) {err(ex, NULL, "%s open: %s", s, strerror(errno));} 28 static void closeErr(void) {err(NULL, NULL, "Close error: %s", strerror(errno));} 29 static void eofErr(void) {err(NULL, NULL, "EOF Overrun");} 30 static void badInput(void) {err(NULL, NULL, "Bad input '%c'", Chr);} 31 static void badFd(any ex, any x) {err(ex, x, "Bad FD");} 32 static void lockErr(void) {err(NULL, NULL, "File lock: %s", strerror(errno));} 33 static void writeErr(char *s) {err(NULL, NULL, "%s write: %s", s, strerror(errno));} 34 static void selectErr(any ex) {err(ex, NULL, "Select error: %s", strerror(errno));} 35 36 static void lockFile(int fd, int cmd, int typ) { 37 struct flock fl; 38 39 fl.l_type = typ; 40 fl.l_whence = SEEK_SET; 41 fl.l_start = 0; 42 fl.l_len = 0; 43 while (fcntl(fd, cmd, &fl) < 0 && typ != F_UNLCK) 44 if (errno != EINTR) 45 lockErr(); 46 } 47 48 void closeOnExec(any ex, int fd) { 49 if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) 50 err(ex, NULL, "SETFD %s", strerror(errno)); 51 } 52 53 int nonblocking(int fd) { 54 int flg = fcntl(fd, F_GETFL, 0); 55 56 fcntl(fd, F_SETFL, flg | O_NONBLOCK); 57 return flg; 58 } 59 60 inFile *initInFile(int fd, char *nm) { 61 inFile *p; 62 63 if (fd >= InFDs) { 64 int i = InFDs; 65 66 InFiles = alloc(InFiles, (InFDs = fd + 1) * sizeof(inFile*)); 67 do 68 InFiles[i] = NULL; 69 while (++i < InFDs); 70 } 71 p = InFiles[fd] = alloc(InFiles[fd], sizeof(inFile)); 72 p->fd = fd; 73 p->ix = p->cnt = p->next = 0; 74 p->line = p->src = 1; 75 p->name = nm; 76 return p; 77 } 78 79 outFile *initOutFile(int fd) { 80 outFile *p; 81 82 if (fd >= OutFDs) { 83 int i = OutFDs; 84 85 OutFiles = alloc(OutFiles, (OutFDs = fd + 1) * sizeof(outFile*)); 86 do 87 OutFiles[i] = NULL; 88 while (++i < OutFDs); 89 } 90 p = OutFiles[fd] = alloc(OutFiles[fd], sizeof(outFile)); 91 p->tty = isatty(p->fd = fd); 92 p->ix = 0; 93 return p; 94 } 95 96 void closeInFile(int fd) { 97 inFile *p; 98 99 if (fd < InFDs && (p = InFiles[fd])) { 100 if (p == InFile) 101 InFile = NULL; 102 free(p->name), free(p), InFiles[fd] = NULL; 103 } 104 } 105 106 void closeOutFile(int fd) { 107 outFile *p; 108 109 if (fd < OutFDs && (p = OutFiles[fd])) { 110 if (p == OutFile) 111 OutFile = NULL; 112 free(p), OutFiles[fd] = NULL; 113 } 114 } 115 116 int slow(inFile *p, bool nb) { 117 int n, f; 118 119 p->ix = p->cnt = 0; 120 for (;;) { 121 if (nb) 122 f = nonblocking(p->fd); 123 n = read(p->fd, p->buf, BUFSIZ); 124 if (nb) 125 fcntl(p->fd, F_SETFL, f); 126 if (n > 0) 127 return p->cnt = n; 128 if (n == 0) { 129 p->ix = p->cnt = -1; 130 return 0; 131 } 132 if (errno == EAGAIN) 133 return -1; 134 if (errno != EINTR) 135 return 0; 136 if (*Signal) 137 sighandler(NULL); 138 } 139 } 140 141 int rdBytes(int fd, byte *p, int cnt, bool nb) { 142 int n, f; 143 144 for (;;) { 145 if (nb) 146 f = nonblocking(fd); 147 n = read(fd, p, cnt); 148 if (nb) 149 fcntl(fd, F_SETFL, f); 150 if (n > 0) { 151 for (;;) { 152 if ((cnt -= n) == 0) 153 return 1; 154 p += n; 155 while ((n = read(fd, p, cnt)) <= 0) { 156 if (!n || errno != EINTR) 157 return 0; 158 if (*Signal) 159 sighandler(NULL); 160 } 161 } 162 } 163 if (n == 0) 164 return 0; 165 if (errno == EAGAIN) 166 return -1; 167 if (errno != EINTR) 168 return 0; 169 if (*Signal) 170 sighandler(NULL); 171 } 172 } 173 174 bool wrBytes(int fd, byte *p, int cnt) { 175 int n; 176 177 for (;;) { 178 if ((n = write(fd, p, cnt)) >= 0) { 179 if ((cnt -= n) == 0) 180 return YES; 181 p += n; 182 } 183 else { 184 if (errno == EBADF || errno == EPIPE || errno == ECONNRESET) 185 return NO; 186 if (errno != EINTR) { 187 if (fd == STDERR_FILENO) 188 bye(2); 189 writeErr("bytes"); 190 } 191 if (*Signal) 192 sighandler(NULL); 193 } 194 } 195 } 196 197 static void clsChild(int i) { 198 if (Child[i].pid == Talking) 199 Talking = 0; 200 Child[i].pid = 0; 201 close(Child[i].hear), close(Child[i].tell); 202 free(Child[i].buf); 203 } 204 205 static void wrChild(int i, byte *p, int cnt) { 206 int n; 207 208 if (Child[i].cnt == 0) { 209 for (;;) { 210 if ((n = write(Child[i].tell, p, cnt)) >= 0) { 211 if ((cnt -= n) == 0) 212 return; 213 p += n; 214 } 215 else if (errno == EAGAIN) 216 break; 217 else if (errno == EPIPE || errno == ECONNRESET) { 218 clsChild(i); 219 return; 220 } 221 else if (errno != EINTR) 222 writeErr("child"); 223 } 224 } 225 n = Child[i].cnt; 226 Child[i].buf = alloc(Child[i].buf, n + sizeof(int) + cnt); 227 *(int*)(Child[i].buf + n) = cnt; 228 memcpy(Child[i].buf + n + sizeof(int), p, cnt); 229 Child[i].cnt += sizeof(int) + cnt; 230 } 231 232 bool flush(outFile *p) { 233 int n; 234 235 if (p && (n = p->ix)) { 236 p->ix = 0; 237 return wrBytes(p->fd, p->buf, n); 238 } 239 return YES; 240 } 241 242 void flushAll(void) { 243 int i; 244 245 for (i = 0; i < OutFDs; ++i) 246 flush(OutFiles[i]); 247 } 248 249 /*** Low level I/O ***/ 250 static int stdinByte(void) { 251 inFile *p; 252 253 if ((p = InFiles[STDIN_FILENO]) && (p->ix != p->cnt || (p->ix >= 0 && slow(p,NO)))) 254 return p->buf[p->ix++]; 255 if (!isatty(STDIN_FILENO)) 256 return -1; 257 bye(0); 258 } 259 260 static int getBinary(void) { 261 if (!InFile || InFile->ix == InFile->cnt && (InFile->ix < 0 || !slow(InFile,NO))) 262 return -1; 263 return InFile->buf[InFile->ix++]; 264 } 265 266 static any rdNum(int cnt) { 267 int n, i; 268 any x; 269 cell c1; 270 271 if ((n = getBin()) < 0) 272 return NULL; 273 i = 0, Push(c1, x = box(n)); 274 if (--cnt == 62) { 275 do { 276 do { 277 if ((n = getBin()) < 0) 278 return NULL; 279 byteSym(n, &i, &x); 280 } while (--cnt); 281 if ((cnt = getBin()) < 0) 282 return NULL; 283 } while (cnt == 255); 284 } 285 while (--cnt >= 0) { 286 if ((n = getBin()) < 0) 287 return NULL; 288 byteSym(n, &i, &x); 289 } 290 return Pop(c1); 291 } 292 293 any binRead(int extn) { 294 int c; 295 any x, y, *h; 296 cell c1; 297 298 if ((c = getBin()) < 0) 299 return NULL; 300 if ((c & ~3) == 0) { 301 if (c == NIX) 302 return Nil; 303 if (c == BEG) { 304 if ((x = binRead(extn)) == NULL) 305 return NULL; 306 Push(c1, x = cons(x,Nil)); 307 while ((y = binRead(extn)) != (any)END) { 308 if (y == NULL) { 309 drop(c1); 310 return NULL; 311 } 312 if (y == (any)DOT) { 313 if ((y = binRead(extn)) == NULL) { 314 drop(c1); 315 return NULL; 316 } 317 cdr(x) = y == (any)END? data(c1) : y; 318 break; 319 } 320 x = cdr(x) = cons(y,Nil); 321 } 322 return Pop(c1); 323 } 324 return (any)(long)c; // DOT or END 325 } 326 if ((y = rdNum(c / 4)) == NULL) 327 return NULL; 328 if ((c &= 3) == NUMBER) 329 return y; 330 if (c == TRANSIENT) 331 return consStr(y); 332 if (c == EXTERN) { 333 if (extn) 334 y = extOffs(extn, y); 335 if (x = findHash(y, h = Extern + ehash(y))) 336 return x; 337 mkExt(x = consSym(Nil,y)); 338 *h = cons(x,*h); 339 return x; 340 } 341 if (x = findHash(y, h = Intern + ihash(y))) 342 return x; 343 x = consSym(Nil,y); 344 *h = cons(x,*h); 345 return x; 346 } 347 348 static void prDig(int t, word n) { 349 int i = 1; 350 word m = MASK; 351 352 while (n & (m <<= 8)) 353 ++i; 354 putBin(i*4+t); 355 while (putBin(n), --i) 356 n >>= 8; 357 } 358 359 static int numByte(any s) { 360 static int i; 361 static any x; 362 static word n; 363 364 if (s) 365 i = 0, n = unDig(x = s); 366 else if (n >>= 8, (++i & sizeof(word)-1) == 0) 367 n = unDig(x = cdr(numCell(x))); 368 return n & 0xFF; 369 } 370 371 static void prNum(int t, any x) { 372 int cnt, i; 373 374 if (!isNum(cdr(numCell(x)))) 375 prDig(t, unDig(x)); 376 else if ((cnt = numBytes(x)) < 63) { 377 putBin(cnt*4+t); 378 putBin(numByte(x)); 379 while (--cnt) 380 putBin(numByte(NULL)); 381 } 382 else { 383 putBin(63*4+t); 384 putBin(numByte(x)); 385 for (i = 1; i < 63; ++i) 386 putBin(numByte(NULL)); 387 cnt -= 63; 388 while (cnt >= 255) { 389 putBin(255); 390 for (i = 0; i < 255; ++i) 391 putBin(numByte(NULL)); 392 cnt -= 255; 393 } 394 putBin(cnt); 395 while (--cnt >= 0) 396 putBin(numByte(NULL)); 397 } 398 } 399 400 void binPrint(int extn, any x) { 401 any y; 402 403 if (isNum(x)) 404 prNum(NUMBER, x); 405 else if (isNil(x)) 406 putBin(NIX); 407 else if (isSym(x)) { 408 if (!isNum(y = name(x))) 409 binPrint(extn, y); 410 else if (!isExt(x)) 411 prNum(hashed(x, Intern[ihash(y)])? INTERN : TRANSIENT, y); 412 else 413 prNum(EXTERN, extn? extOffs(-extn, y) : y); 414 } 415 else { 416 putBin(BEG); 417 if ((y = circ(x)) == NULL) { 418 while (binPrint(extn, car(x)), !isNil(x = cdr(x))) { 419 if (!isCell(x)) { 420 putBin(DOT); 421 binPrint(extn, x); 422 return; 423 } 424 } 425 } 426 else if (y == x) { 427 do 428 binPrint(extn, car(x)); 429 while (y != (x = cdr(x))); 430 putBin(DOT); 431 } 432 else { 433 do 434 binPrint(extn, car(x)); 435 while (y != (x = cdr(x))); 436 putBin(DOT), putBin(BEG); 437 do 438 binPrint(extn, car(x)); 439 while (y != (x = cdr(x))); 440 putBin(DOT), putBin(END); 441 } 442 putBin(END); 443 } 444 } 445 446 void pr(int extn, any x) {putBin = putStdout, binPrint(extn, x);} 447 448 void prn(long n) { 449 putBin = putStdout; 450 prDig(NUMBER, n >= 0? n * 2 : -n * 2 + 1); 451 } 452 453 /* Family IPC */ 454 static void putTell(int c) { 455 *PipePtr++ = c; 456 if (PipePtr == PipeBuf + PIPE_BUF - 1) // END 457 err(NULL, NULL, "Tell PIPE_BUF"); 458 } 459 460 static void tellBeg(ptr *pb, ptr *pp, ptr buf) { 461 *pb = PipeBuf, *pp = PipePtr; 462 PipePtr = (PipeBuf = buf) + sizeof(int); 463 *PipePtr++ = BEG; 464 } 465 466 static void prTell(any x) {putBin = putTell, binPrint(0, x);} 467 468 static void tellEnd(ptr *pb, ptr *pp, int pid) { 469 int i, n; 470 471 *PipePtr++ = END; 472 *(int*)PipeBuf = (n = PipePtr - PipeBuf - sizeof(int)) | pid << 16; 473 if (Tell && !wrBytes(Tell, PipeBuf, n+sizeof(int))) 474 close(Tell), Tell = 0; 475 for (i = 0; i < Children; ++i) 476 if (Child[i].pid && (!pid || pid == Child[i].pid)) 477 wrChild(i, PipeBuf+sizeof(int), n); 478 PipePtr = *pp, PipeBuf = *pb; 479 } 480 481 static void unsync(void) { 482 int n = 0; 483 484 if (Tell && !wrBytes(Tell, (byte*)&n, sizeof(int))) 485 close(Tell), Tell = 0; 486 Sync = NO; 487 } 488 489 static any rdHear(void) { 490 any x; 491 inFile *iSave = InFile; 492 493 InFile = InFiles[Hear]; 494 getBin = getBinary; 495 x = binRead(0); 496 InFile = iSave; 497 return x; 498 } 499 500 /* Return next byte from symbol name */ 501 int symByte(any s) { 502 static any x; 503 static word n; 504 505 if (s) { 506 if (!isNum(x = s)) 507 return 0; 508 n = unDig(x); 509 } 510 else if ((n >>= 8) == 0) { 511 if (!isNum(cdr(numCell(x)))) 512 return 0; 513 n = unDig(x = cdr(numCell(x))); 514 } 515 return n & 0xFF; 516 } 517 518 /* Return next char from symbol name */ 519 int symChar(any s) { 520 int c = symByte(s); 521 522 if (c == 0xFF) 523 return TOP; 524 if (c & 0x80) { 525 if ((c & 0x20) == 0) 526 c &= 0x1F; 527 else 528 c = (c & 0xF) << 6 | symByte(NULL) & 0x3F; 529 c = c << 6 | symByte(NULL) & 0x3F; 530 } 531 return c; 532 } 533 534 int numBytes(any x) { 535 int cnt; 536 word n, m = MASK; 537 538 for (cnt = 1; isNum(cdr(numCell(x))); cnt += WORD) 539 x = cdr(numCell(x)); 540 for (n = unDig(x); n & (m <<= 8); ++cnt); 541 return cnt; 542 } 543 544 /* Buffer size */ 545 int bufSize(any x) {return isNum(x = name(x))? numBytes(x)+1 : 1;} 546 547 int pathSize(any x) { 548 int c = firstByte(x); 549 550 if (c != '@' && (c != '+' || secondByte(x) != '@')) 551 return bufSize(x); 552 if (!Home) 553 return numBytes(name(x)); 554 return strlen(Home) + numBytes(name(x)); 555 } 556 557 void bufString(any x, char *p) { 558 int c = symByte(name(x)); 559 560 while (*p++ = c) 561 c = symByte(NULL); 562 } 563 564 void pathString(any x, char *p) { 565 int c; 566 char *h; 567 568 if ((c = symByte(name(x))) == '+') 569 *p++ = c, c = symByte(NULL); 570 if (c != '@') 571 while (*p++ = c) 572 c = symByte(NULL); 573 else { 574 if (h = Home) 575 do 576 *p++ = *h++; 577 while (*h); 578 while (*p++ = symByte(NULL)); 579 } 580 } 581 582 // (path 'any) -> sym 583 any doPath(any x) { 584 x = evSym(cdr(x)); 585 { 586 char nm[pathSize(x)]; 587 588 pathString(x,nm); 589 return mkStr(nm); 590 } 591 } 592 593 /* Add next byte to symbol name */ 594 void byteSym(int c, int *i, any *p) { 595 if ((*i += 8) < BITS) 596 setDig(*p, unDig(*p) | (c & 0xFF) << *i); 597 else 598 *i = 0, *p = cdr(numCell(*p)) = box(c & 0xFF); 599 } 600 601 /* Box first char of symbol name */ 602 any boxChar(int c, int *i, any *p) { 603 *i = 0; 604 if (c < 0x80) 605 *p = box(c); 606 else if (c < 0x800) { 607 *p = box(0xC0 | c>>6 & 0x1F); 608 byteSym(0x80 | c & 0x3F, i, p); 609 } 610 else if (c == TOP) 611 *p = box(0xFF); 612 else { 613 *p = box(0xE0 | c>>12 & 0x0F); 614 byteSym(0x80 | c>>6 & 0x3F, i, p); 615 byteSym(0x80 | c & 0x3F, i, p); 616 } 617 return *p; 618 } 619 620 /* Add next char to symbol name */ 621 void charSym(int c, int *i, any *p) { 622 if (c < 0x80) 623 byteSym(c, i, p); 624 else if (c < 0x800) { 625 byteSym(0xC0 | c>>6 & 0x1F, i, p); 626 byteSym(0x80 | c & 0x3F, i, p); 627 } 628 else if (c == TOP) 629 byteSym(0xFF, i, p); 630 else { 631 byteSym(0xE0 | c>>12 & 0x0F, i, p); 632 byteSym(0x80 | c>>6 & 0x3F, i, p); 633 byteSym(0x80 | c & 0x3F, i, p); 634 } 635 } 636 637 static int currFd(any ex, char *p) { 638 if (!Env.inFrames && !Env.outFrames) 639 err(ex, NULL, "No current fd"); 640 if (!Env.inFrames) 641 return OutFile->fd; 642 if (!Env.outFrames) 643 return InFile->fd; 644 return labs((char*)Env.outFrames - p) > labs((char*)Env.inFrames - p)? 645 InFile->fd : OutFile->fd; 646 } 647 648 void rdOpen(any ex, any x, inFrame *f) { 649 if (isNil(x)) 650 f->pid = 0, f->fd = STDIN_FILENO; 651 else if (isNum(x)) { 652 int n = (int)unBox(x); 653 654 if (n < 0) { 655 inFrame *g = Env.inFrames; 656 657 for (;;) { 658 if (!(g = g->link)) 659 badFd(ex,x); 660 if (!++n) { 661 n = g->fd; 662 break; 663 } 664 } 665 } 666 f->pid = 0, f->fd = n; 667 if (n >= InFDs || !InFiles[n]) 668 badFd(ex,x); 669 } 670 else if (isSym(x)) { 671 char nm[pathSize(x)]; 672 673 f->pid = 1; 674 pathString(x,nm); 675 if (nm[0] == '+') { 676 while ((f->fd = open(nm+1, O_APPEND|O_CREAT|O_RDWR, 0666)) < 0) { 677 if (errno != EINTR) 678 openErr(ex, nm); 679 if (*Signal) 680 sighandler(ex); 681 } 682 initInFile(f->fd, strdup(nm+1)); 683 } 684 else { 685 while ((f->fd = open(nm, O_RDONLY)) < 0) { 686 if (errno != EINTR) 687 openErr(ex, nm); 688 if (*Signal) 689 sighandler(ex); 690 } 691 initInFile(f->fd, strdup(nm)); 692 } 693 closeOnExec(ex, f->fd); 694 } 695 else { 696 any y; 697 int i, pfd[2], ac = length(x); 698 char *av[ac+1]; 699 700 if (pipe(pfd) < 0) 701 pipeError(ex, "read open"); 702 closeOnExec(ex, pfd[0]), closeOnExec(ex, pfd[1]); 703 av[0] = alloc(NULL, pathSize(y = xSym(car(x)))), pathString(y, av[0]); 704 for (i = 1; isCell(x = cdr(x)); ++i) 705 av[i] = alloc(NULL, bufSize(y = xSym(car(x)))), bufString(y, av[i]); 706 av[ac] = NULL; 707 if ((f->pid = fork()) == 0) { 708 setpgid(0,0); 709 close(pfd[0]); 710 if (pfd[1] != STDOUT_FILENO) 711 dup2(pfd[1], STDOUT_FILENO), close(pfd[1]); 712 execvp(av[0], av); 713 execError(av[0]); 714 } 715 i = 0; do 716 free(av[i]); 717 while (++i < ac); 718 if (f->pid < 0) 719 err(ex, NULL, "fork"); 720 setpgid(f->pid,0); 721 close(pfd[1]); 722 initInFile(f->fd = pfd[0], NULL); 723 } 724 } 725 726 void wrOpen(any ex, any x, outFrame *f) { 727 if (isNil(x)) 728 f->pid = 0, f->fd = STDOUT_FILENO; 729 else if (isNum(x)) { 730 int n = (int)unBox(x); 731 732 if (n < 0) { 733 outFrame *g = Env.outFrames; 734 735 for (;;) { 736 if (!(g = g->link)) 737 badFd(ex,x); 738 if (!++n) { 739 n = g->fd; 740 break; 741 } 742 } 743 } 744 f->pid = 0, f->fd = n; 745 if (n >= OutFDs || !OutFiles[n]) 746 badFd(ex,x); 747 } 748 else if (isSym(x)) { 749 char nm[pathSize(x)]; 750 751 f->pid = 1; 752 pathString(x,nm); 753 if (nm[0] == '+') { 754 while ((f->fd = open(nm+1, O_APPEND|O_CREAT|O_WRONLY, 0666)) < 0) { 755 if (errno != EINTR) 756 openErr(ex, nm); 757 if (*Signal) 758 sighandler(ex); 759 } 760 } 761 else { 762 while ((f->fd = open(nm, O_CREAT|O_TRUNC|O_WRONLY, 0666)) < 0) { 763 if (errno != EINTR) 764 openErr(ex, nm); 765 if (*Signal) 766 sighandler(ex); 767 } 768 } 769 closeOnExec(ex, f->fd); 770 initOutFile(f->fd); 771 } 772 else { 773 any y; 774 int i, pfd[2], ac = length(x); 775 char *av[ac+1]; 776 777 if (pipe(pfd) < 0) 778 pipeError(ex, "write open"); 779 closeOnExec(ex, pfd[0]), closeOnExec(ex, pfd[1]); 780 av[0] = alloc(NULL, pathSize(y = xSym(car(x)))), pathString(y, av[0]); 781 for (i = 1; isCell(x = cdr(x)); ++i) 782 av[i] = alloc(NULL, bufSize(y = xSym(car(x)))), bufString(y, av[i]); 783 av[ac] = NULL; 784 if ((f->pid = fork()) == 0) { 785 setpgid(0,0); 786 close(pfd[1]); 787 if (pfd[0] != STDIN_FILENO) 788 dup2(pfd[0], STDIN_FILENO), close(pfd[0]); 789 execvp(av[0], av); 790 execError(av[0]); 791 } 792 i = 0; do 793 free(av[i]); 794 while (++i < ac); 795 if (f->pid < 0) 796 err(ex, NULL, "fork"); 797 setpgid(f->pid,0); 798 close(pfd[0]); 799 initOutFile(f->fd = pfd[1]); 800 } 801 } 802 803 void erOpen(any ex, any x, errFrame *f) { 804 int fd; 805 806 NeedSym(ex,x); 807 f->fd = dup(STDERR_FILENO); 808 if (isNil(x)) 809 fd = dup(OutFile->fd); 810 else { 811 char nm[pathSize(x)]; 812 813 pathString(x,nm); 814 if (nm[0] == '+') { 815 while ((fd = open(nm+1, O_APPEND|O_CREAT|O_WRONLY, 0666)) < 0) { 816 if (errno != EINTR) 817 openErr(ex, nm); 818 if (*Signal) 819 sighandler(ex); 820 } 821 } 822 else { 823 while ((fd = open(nm, O_CREAT|O_TRUNC|O_WRONLY, 0666)) < 0) { 824 if (errno != EINTR) 825 openErr(ex, nm); 826 if (*Signal) 827 sighandler(ex); 828 } 829 } 830 closeOnExec(ex, fd); 831 } 832 dup2(fd, STDERR_FILENO), close(fd); 833 } 834 835 void ctOpen(any ex, any x, ctlFrame *f) { 836 NeedSym(ex,x); 837 if (isNil(x)) { 838 f->fd = -1; 839 lockFile(currFd(ex, (char*)f), F_SETLKW, F_RDLCK); 840 } 841 else if (x == T) { 842 f->fd = -1; 843 lockFile(currFd(ex, (char*)f), F_SETLKW, F_WRLCK); 844 } 845 else { 846 char nm[pathSize(x)]; 847 848 pathString(x,nm); 849 if (nm[0] == '+') { 850 while ((f->fd = open(nm+1, O_CREAT|O_RDWR, 0666)) < 0) { 851 if (errno != EINTR) 852 openErr(ex, nm); 853 if (*Signal) 854 sighandler(ex); 855 } 856 lockFile(f->fd, F_SETLKW, F_RDLCK); 857 } 858 else { 859 while ((f->fd = open(nm, O_CREAT|O_RDWR, 0666)) < 0) { 860 if (errno != EINTR) 861 openErr(ex, nm); 862 if (*Signal) 863 sighandler(ex); 864 } 865 lockFile(f->fd, F_SETLKW, F_WRLCK); 866 } 867 closeOnExec(ex, f->fd); 868 } 869 } 870 871 /*** Reading ***/ 872 void getStdin(void) { 873 if (!InFile) 874 Chr = -1; 875 else if (InFile != InFiles[STDIN_FILENO]) { 876 if (InFile->ix == InFile->cnt && (InFile->ix < 0 || !slow(InFile,NO))) { 877 Chr = -1; 878 return; 879 } 880 if ((Chr = InFile->buf[InFile->ix++]) == '\n') 881 ++InFile->line; 882 } 883 else if (!isCell(val(Led))) { 884 waitFd(NULL, STDIN_FILENO, -1); 885 Chr = stdinByte(); 886 } 887 else { 888 static word dig; 889 890 if (!isNum(Line)) 891 dig = isNum(Line = name(run(val(Led))))? unDig(Line) : '\n'; 892 else if ((dig >>= 8) == 0) 893 dig = isNum(Line = cdr(numCell(Line)))? unDig(Line) : '\n'; 894 Chr = dig & 0xFF; 895 } 896 } 897 898 static void getParse(void) { 899 if ((Chr = Env.parser->dig & 0xFF) == 0xFF) 900 Chr = -1; 901 else if ((Env.parser->dig >>= 8) == 0) { 902 Env.parser->dig = 903 isNum(Env.parser->name = cdr(numCell(Env.parser->name))) ? 904 unDig(Env.parser->name) : Env.parser->eof; 905 } 906 } 907 908 void pushInFiles(inFrame *f) { 909 if (InFile) 910 InFile->next = Chr; 911 Chr = (InFile = InFiles[f->fd])? InFile->next : -1; 912 f->get = Env.get, Env.get = getStdin; 913 f->link = Env.inFrames, Env.inFrames = f; 914 } 915 916 void pushOutFiles(outFrame *f) { 917 OutFile = OutFiles[f->fd]; 918 f->put = Env.put, Env.put = putStdout; 919 f->link = Env.outFrames, Env.outFrames = f; 920 } 921 922 void pushErrFiles(errFrame *f) { 923 f->link = Env.errFrames, Env.errFrames = f; 924 } 925 926 void pushCtlFiles(ctlFrame *f) { 927 f->link = Env.ctlFrames, Env.ctlFrames = f; 928 } 929 930 void popInFiles(void) { 931 if (Env.inFrames->pid) { 932 close(Env.inFrames->fd), closeInFile(Env.inFrames->fd); 933 if (Env.inFrames->pid > 1) 934 while (waitpid(Env.inFrames->pid, NULL, 0) < 0) { 935 if (errno != EINTR) 936 closeErr(); 937 if (*Signal) 938 sighandler(NULL); 939 } 940 } 941 else if (InFile) 942 InFile->next = Chr; 943 Env.get = Env.inFrames->get; 944 Chr = 945 (InFile = InFiles[(Env.inFrames = Env.inFrames->link)? Env.inFrames->fd : STDIN_FILENO])? 946 InFile->next : -1; 947 } 948 949 void popOutFiles(void) { 950 flush(OutFile); 951 if (Env.outFrames->pid) { 952 close(Env.outFrames->fd), closeOutFile(Env.outFrames->fd); 953 if (Env.outFrames->pid > 1) 954 while (waitpid(Env.outFrames->pid, NULL, 0) < 0) { 955 if (errno != EINTR) 956 closeErr(); 957 if (*Signal) 958 sighandler(NULL); 959 } 960 } 961 Env.put = Env.outFrames->put; 962 OutFile = OutFiles[(Env.outFrames = Env.outFrames->link)? Env.outFrames->fd : STDOUT_FILENO]; 963 } 964 965 void popErrFiles(void) { 966 dup2(Env.errFrames->fd, STDERR_FILENO); 967 close(Env.errFrames->fd); 968 Env.errFrames = Env.errFrames->link; 969 } 970 971 void popCtlFiles(void) { 972 if (Env.ctlFrames->fd >= 0) 973 close(Env.ctlFrames->fd); 974 else 975 lockFile(currFd(NULL, (char*)Env.ctlFrames), F_SETLK, F_UNLCK); 976 Env.ctlFrames = Env.ctlFrames->link; 977 } 978 979 /* Get full char from input channel */ 980 int getChar(void) { 981 int c; 982 983 if ((c = Chr) == 0xFF) 984 return TOP; 985 if (c & 0x80) { 986 Env.get(); 987 if ((c & 0x20) == 0) 988 c &= 0x1F; 989 else 990 c = (c & 0xF) << 6 | Chr & 0x3F, Env.get(); 991 if (Chr < 0) 992 eofErr(); 993 c = c << 6 | Chr & 0x3F; 994 } 995 return c; 996 } 997 998 /* Skip White Space and Comments */ 999 static int skipc(int c) { 1000 if (Chr < 0) 1001 return Chr; 1002 for (;;) { 1003 while (Chr <= ' ') { 1004 Env.get(); 1005 if (Chr < 0) 1006 return Chr; 1007 } 1008 if (Chr != c) 1009 return Chr; 1010 Env.get(); 1011 while (Chr != '\n') { 1012 if (Chr < 0) 1013 return Chr; 1014 Env.get(); 1015 } 1016 } 1017 } 1018 1019 static void comment(void) { 1020 Env.get(); 1021 if (Chr != '{') { 1022 while (Chr != '\n') { 1023 if (Chr < 0) 1024 return; 1025 Env.get(); 1026 } 1027 } 1028 else { 1029 for (;;) { // #{block-comment}# from Kriangkrai Soatthiyanont 1030 Env.get(); 1031 if (Chr < 0) 1032 return; 1033 if (Chr == '}' && (Env.get(), Chr == '#')) 1034 break; 1035 } 1036 Env.get(); 1037 } 1038 } 1039 1040 static int skip(void) { 1041 for (;;) { 1042 if (Chr < 0) 1043 return Chr; 1044 while (Chr <= ' ') { 1045 Env.get(); 1046 if (Chr < 0) 1047 return Chr; 1048 } 1049 if (Chr != '#') 1050 return Chr; 1051 comment(); 1052 } 1053 } 1054 1055 /* Test for escaped characters */ 1056 static bool testEsc(void) { 1057 for (;;) { 1058 if (Chr < 0) 1059 return NO; 1060 if (Chr == '^') { 1061 Env.get(); 1062 if (Chr == '@') 1063 badInput(); 1064 if (Chr == '?') 1065 Chr = 127; 1066 else 1067 Chr &= 0x1F; 1068 return YES; 1069 } 1070 if (Chr != '\\') 1071 return YES; 1072 if (Env.get(), Chr != '\n') 1073 return YES; 1074 do 1075 Env.get(); 1076 while (Chr == ' ' || Chr == '\t'); 1077 } 1078 } 1079 1080 /* Try for anonymous symbol */ 1081 static any anonymous(any s) { 1082 unsigned c; 1083 unsigned long n; 1084 heap *h; 1085 1086 if ((c = symByte(s)) != '$') 1087 return NULL; 1088 n = 0; 1089 while (c = symByte(NULL)) { 1090 if (c < '0' || c > '9') 1091 return NULL; 1092 n = n * 10 + c - '0'; 1093 } 1094 n *= sizeof(cell); 1095 h = Heaps; 1096 do 1097 if ((any)n >= h->cells && (any)n < h->cells + CELLS) 1098 return symPtr((any)n); 1099 while (h = h->next); 1100 return NULL; 1101 } 1102 1103 /* Read an atom */ 1104 static any rdAtom(int c) { 1105 int i; 1106 any x, y, *h; 1107 cell c1; 1108 1109 i = 0, Push(c1, y = box(c)); 1110 while (Chr > 0 && !strchr(Delim, Chr)) { 1111 if (Chr == '\\') 1112 Env.get(); 1113 byteSym(Chr, &i, &y); 1114 Env.get(); 1115 } 1116 y = Pop(c1); 1117 if (unDig(y) == ('L'<<16 | 'I'<<8 | 'N')) 1118 return Nil; 1119 if (x = symToNum(y, (int)unDig(val(Scl)) / 2, '.', 0)) 1120 return x; 1121 if (x = anonymous(y)) 1122 return x; 1123 if (x = findHash(y, h = Intern + ihash(y))) 1124 return x; 1125 x = consSym(Nil,y); 1126 *h = cons(x,*h); 1127 return x; 1128 } 1129 1130 /* Read a list */ 1131 static any rdList(void) { 1132 any x; 1133 cell c1; 1134 1135 Env.get(); 1136 for (;;) { 1137 if (skip() == ')') { 1138 Env.get(); 1139 return Nil; 1140 } 1141 if (Chr == ']') 1142 return Nil; 1143 if (Chr != '~') { 1144 Push(c1, x = cons(read0(NO),Nil)); 1145 break; 1146 } 1147 Env.get(); 1148 Push(c1, read0(NO)); 1149 if (isCell(x = data(c1) = EVAL(data(c1)))) { 1150 while (isCell(cdr(x))) 1151 x = cdr(x); 1152 break; 1153 } 1154 drop(c1); 1155 } 1156 for (;;) { 1157 if (skip() == ')') { 1158 Env.get(); 1159 break; 1160 } 1161 if (Chr == ']') 1162 break; 1163 if (Chr == '.') { 1164 Env.get(); 1165 if (strchr(Delim, Chr)) { 1166 cdr(x) = skip()==')' || Chr==']'? data(c1) : read0(NO); 1167 if (skip() == ')') 1168 Env.get(); 1169 else if (Chr != ']') 1170 err(NULL, x, "Bad dotted pair"); 1171 break; 1172 } 1173 x = cdr(x) = cons(rdAtom('.'), Nil); 1174 } 1175 else if (Chr != '~') 1176 x = cdr(x) = cons(read0(NO), Nil); 1177 else { 1178 Env.get(); 1179 cdr(x) = read0(NO); 1180 cdr(x) = EVAL(cdr(x)); 1181 while (isCell(cdr(x))) 1182 x = cdr(x); 1183 } 1184 } 1185 return Pop(c1); 1186 } 1187 1188 /* Read one expression */ 1189 static any read0(bool top) { 1190 int i; 1191 any x, y, *h; 1192 cell c1; 1193 1194 if (skip() < 0) { 1195 if (top) 1196 return Nil; 1197 eofErr(); 1198 } 1199 if (top && InFile) 1200 InFile->src = InFile->line; 1201 if (Chr == '(') { 1202 x = rdList(); 1203 if (top && Chr == ']') 1204 Env.get(); 1205 return x; 1206 } 1207 if (Chr == '[') { 1208 x = rdList(); 1209 if (Chr != ']') 1210 err(NULL, x, "Super parentheses mismatch"); 1211 Env.get(); 1212 return x; 1213 } 1214 if (Chr == '\'') { 1215 Env.get(); 1216 return cons(Quote, read0(top)); 1217 } 1218 if (Chr == ',') { 1219 Env.get(); 1220 x = read0(top); 1221 if (val(Uni) != T) { 1222 Push(c1, x); 1223 if (isCell(y = idx(Uni, data(c1), 1))) 1224 x = car(y); 1225 drop(c1); 1226 } 1227 return x; 1228 } 1229 if (Chr == '`') { 1230 Env.get(); 1231 Push(c1, read0(top)); 1232 x = EVAL(data(c1)); 1233 drop(c1); 1234 return x; 1235 } 1236 if (Chr == '"') { 1237 Env.get(); 1238 if (Chr == '"') { 1239 Env.get(); 1240 return Nil; 1241 } 1242 if (!testEsc()) 1243 eofErr(); 1244 i = 0, Push(c1, y = box(Chr)); 1245 while (Env.get(), Chr != '"') { 1246 if (!testEsc()) 1247 eofErr(); 1248 byteSym(Chr, &i, &y); 1249 } 1250 y = Pop(c1), Env.get(); 1251 if (x = findHash(y, h = Transient + ihash(y))) 1252 return x; 1253 x = consStr(y); 1254 *h = cons(x,*h); 1255 return x; 1256 } 1257 if (Chr == '{') { 1258 Env.get(); 1259 if (Chr == '}') { 1260 Env.get(); 1261 return consSym(Nil,Nil); 1262 } 1263 i = 0, Push(c1, y = box(Chr)); 1264 while (Env.get(), Chr != '}') { 1265 if (Chr < 0) 1266 eofErr(); 1267 byteSym(Chr, &i, &y); 1268 } 1269 y = Pop(c1), Env.get(); 1270 if (x = findHash(y, h = Extern + ehash(y))) 1271 return x; 1272 mkExt(x = consSym(Nil,y)); 1273 *h = cons(x,*h); 1274 return x; 1275 } 1276 if (Chr == ')' || Chr == ']' || Chr == '~') 1277 badInput(); 1278 if (Chr == '\\') 1279 Env.get(); 1280 i = Chr; 1281 Env.get(); 1282 return rdAtom(i); 1283 } 1284 1285 any read1(int end) { 1286 if (!Chr) 1287 Env.get(); 1288 if (Chr == end) 1289 return Nil; 1290 return read0(YES); 1291 } 1292 1293 /* Read one token */ 1294 any token(any x, int c) { 1295 int i; 1296 any y, *h; 1297 cell c1; 1298 1299 if (!Chr) 1300 Env.get(); 1301 if (skipc(c) < 0) 1302 return NULL; 1303 if (Chr == '"') { 1304 Env.get(); 1305 if (Chr == '"') { 1306 Env.get(); 1307 return Nil; 1308 } 1309 if (!testEsc()) 1310 return Nil; 1311 Push(c1, y = cons(mkChar(Chr), Nil)); 1312 while (Env.get(), Chr != '"' && testEsc()) 1313 y = cdr(y) = cons(mkChar(Chr), Nil); 1314 Env.get(); 1315 return Pop(c1); 1316 } 1317 if (Chr >= '0' && Chr <= '9') { 1318 i = 0, Push(c1, y = box(Chr)); 1319 while (Env.get(), Chr >= '0' && Chr <= '9' || Chr == '.') 1320 byteSym(Chr, &i, &y); 1321 return symToNum(Pop(c1), (int)unDig(val(Scl)) / 2, '.', 0); 1322 } 1323 if (Chr != '+' && Chr != '-') { 1324 char nm[bufSize(x)]; 1325 1326 bufString(x, nm); 1327 if (Chr >= 'A' && Chr <= 'Z' || Chr == '\\' || Chr >= 'a' && Chr <= 'z' || strchr(nm,Chr)) { 1328 if (Chr == '\\') 1329 Env.get(); 1330 i = 0, Push(c1, y = box(Chr)); 1331 while (Env.get(), 1332 Chr >= '0' && Chr <= '9' || Chr >= 'A' && Chr <= 'Z' || 1333 Chr == '\\' || Chr >= 'a' && Chr <= 'z' || strchr(nm,Chr) ) { 1334 if (Chr == '\\') 1335 Env.get(); 1336 byteSym(Chr, &i, &y); 1337 } 1338 y = Pop(c1); 1339 if (unDig(y) == ('L'<<16 | 'I'<<8 | 'N')) 1340 return Nil; 1341 if (x = findHash(y, h = Intern + ihash(y))) 1342 return x; 1343 x = consSym(Nil,y); 1344 *h = cons(x,*h); 1345 return x; 1346 } 1347 } 1348 c = getChar(); 1349 Env.get(); 1350 return mkChar(c); 1351 } 1352 1353 // (read ['sym1 ['sym2]]) -> any 1354 any doRead(any ex) { 1355 any x; 1356 1357 if (!isCell(x = cdr(ex))) 1358 x = read1(0); 1359 else { 1360 cell c1; 1361 1362 Push(c1, EVAL(car(x))); 1363 NeedSym(ex, data(c1)); 1364 x = cdr(x), x = EVAL(car(x)); 1365 NeedSym(ex,x); 1366 x = token(data(c1), symChar(name(x))) ?: Nil; 1367 drop(c1); 1368 } 1369 if (InFile == InFiles[STDIN_FILENO] && Chr == '\n') 1370 Chr = 0; 1371 return x; 1372 } 1373 1374 static inline bool inReady(inFile *p) { 1375 return p->ix < p->cnt; 1376 } 1377 1378 static bool isSet(int fd, fd_set *fds) { 1379 inFile *p; 1380 1381 if (fd >= InFDs || !(p = InFiles[fd])) 1382 return FD_ISSET(fd, fds); 1383 if (inReady(p)) 1384 return YES; 1385 return FD_ISSET(fd, fds) && slow(p,YES) >= 0; 1386 } 1387 1388 long waitFd(any ex, int fd, long ms) { 1389 any x, taskSave; 1390 cell c1, c2, c3; 1391 int i, j, m, n; 1392 long t; 1393 fd_set rdSet, wrSet; 1394 struct timeval *tp, tv; 1395 #ifndef __linux__ 1396 struct timeval tt; 1397 #endif 1398 1399 taskSave = Env.task; 1400 Push(c1, val(At)); 1401 Save(c2); 1402 do { 1403 if (ms >= 0) 1404 t = ms, tp = &tv; 1405 else 1406 t = LONG_MAX, tp = NULL; 1407 FD_ZERO(&rdSet); 1408 FD_ZERO(&wrSet); 1409 m = 0; 1410 if (fd >= 0) { 1411 if (fd < InFDs && InFiles[fd] && inReady(InFiles[fd])) 1412 tp = &tv, t = 0; 1413 else 1414 FD_SET(m = fd, &rdSet); 1415 } 1416 for (x = data(c2) = Env.task = val(Run); isCell(x); x = cdr(x)) { 1417 if (!memq(car(x), taskSave)) { 1418 if (isNeg(caar(x))) { 1419 if ((n = (int)unDig(cadar(x)) / 2) < t) 1420 tp = &tv, t = n; 1421 } 1422 else if ((n = (int)unDig(caar(x)) / 2) != fd) { 1423 if (n < InFDs && InFiles[n] && inReady(InFiles[n])) 1424 tp = &tv, t = 0; 1425 else { 1426 FD_SET(n, &rdSet); 1427 if (n > m) 1428 m = n; 1429 } 1430 } 1431 } 1432 } 1433 if (Hear && Hear != fd && InFiles[Hear]) { 1434 if (inReady(InFiles[Hear])) 1435 tp = &tv, t = 0; 1436 else { 1437 FD_SET(Hear, &rdSet); 1438 if (Hear > m) 1439 m = Hear; 1440 } 1441 } 1442 if (Spkr) { 1443 FD_SET(Spkr, &rdSet); 1444 if (Spkr > m) 1445 m = Spkr; 1446 for (i = 0; i < Children; ++i) { 1447 if (Child[i].pid) { 1448 FD_SET(Child[i].hear, &rdSet); 1449 if (Child[i].hear > m) 1450 m = Child[i].hear; 1451 if (Child[i].cnt) { 1452 FD_SET(Child[i].tell, &wrSet); 1453 if (Child[i].tell > m) 1454 m = Child[i].tell; 1455 } 1456 } 1457 } 1458 } 1459 if (tp) { 1460 tv.tv_sec = t / 1000; 1461 tv.tv_usec = t % 1000 * 1000; 1462 #ifndef __linux__ 1463 gettimeofday(&tt,NULL); 1464 t = tt.tv_sec*1000 + tt.tv_usec/1000; 1465 #endif 1466 } 1467 while (select(m+1, &rdSet, &wrSet, NULL, tp) < 0) { 1468 if (errno != EINTR) { 1469 val(Run) = Nil; 1470 selectErr(ex); 1471 } 1472 if (*Signal) 1473 sighandler(ex); 1474 } 1475 if (tp) { 1476 #ifdef __linux__ 1477 t -= tv.tv_sec*1000 + tv.tv_usec/1000; 1478 #else 1479 gettimeofday(&tt,NULL); 1480 t = tt.tv_sec*1000 + tt.tv_usec/1000 - t; 1481 #endif 1482 if (ms > 0 && (ms -= t) < 0) 1483 ms = 0; 1484 } 1485 if (Spkr) { 1486 ++Env.protect; 1487 for (i = 0; i < Children; ++i) { 1488 if (Child[i].pid) { 1489 if (FD_ISSET(Child[i].hear, &rdSet)) { 1490 if ((m = rdBytes(Child[i].hear, (byte*)&n, sizeof(int), YES)) >= 0) { 1491 byte buf[PIPE_BUF - sizeof(int)]; 1492 1493 if (m == 0) { 1494 clsChild(i); 1495 continue; 1496 } 1497 if (n == 0) { 1498 if (Child[i].pid == Talking) 1499 Talking = 0; 1500 } 1501 else { 1502 pid_t pid = n >> 16; 1503 1504 n &= 0xFFFF; 1505 if (rdBytes(Child[i].hear, buf, n, NO)) { 1506 for (j = 0; j < Children; ++j) 1507 if (j != i && Child[j].pid && (!pid || pid == Child[j].pid)) 1508 wrChild(j, buf, n); 1509 } 1510 else { 1511 clsChild(i); 1512 continue; 1513 } 1514 } 1515 } 1516 } 1517 if (FD_ISSET(Child[i].tell, &wrSet)) { 1518 n = *(int*)(Child[i].buf + Child[i].ofs); 1519 if (wrBytes(Child[i].tell, Child[i].buf + Child[i].ofs + sizeof(int), n)) { 1520 Child[i].ofs += sizeof(int) + n; 1521 if (2 * Child[i].ofs >= Child[i].cnt) { 1522 if (Child[i].cnt -= Child[i].ofs) { 1523 memcpy(Child[i].buf, Child[i].buf + Child[i].ofs, Child[i].cnt); 1524 Child[i].buf = alloc(Child[i].buf, Child[i].cnt); 1525 } 1526 Child[i].ofs = 0; 1527 } 1528 } 1529 else 1530 clsChild(i); 1531 } 1532 } 1533 } 1534 if (!Talking && FD_ISSET(Spkr,&rdSet) && 1535 rdBytes(Spkr, (byte*)&m, sizeof(int), YES) > 0 && 1536 Child[m].pid ) { 1537 Talking = Child[m].pid; 1538 wrChild(m, TBuf, sizeof(TBuf)); 1539 } 1540 --Env.protect; 1541 } 1542 if (Hear && Hear != fd && isSet(Hear, &rdSet)) { 1543 if ((data(c3) = rdHear()) == NULL) 1544 close(Hear), closeInFile(Hear), closeOutFile(Hear), Hear = 0; 1545 else if (data(c3) == T) 1546 Sync = YES; 1547 else { 1548 Save(c3); 1549 evList(data(c3)); 1550 drop(c3); 1551 } 1552 } 1553 for (x = data(c2); isCell(x); x = cdr(x)) { 1554 if (!memq(car(x), taskSave)) { 1555 if (isNeg(caar(x))) { 1556 if ((n = (int)(unDig(cadar(x)) / 2 - t)) > 0) 1557 setDig(cadar(x), (long)2*n); 1558 else { 1559 setDig(cadar(x), unDig(caar(x))); 1560 val(At) = caar(x); 1561 prog(cddar(x)); 1562 } 1563 } 1564 else if ((n = (int)unDig(caar(x)) / 2) != fd) { 1565 if (isSet(n, &rdSet)) { 1566 val(At) = caar(x); 1567 prog(cdar(x)); 1568 } 1569 } 1570 } 1571 } 1572 if (*Signal) 1573 sighandler(ex); 1574 } while (ms && fd >= 0 && !isSet(fd, &rdSet)); 1575 Env.task = taskSave; 1576 val(At) = Pop(c1); 1577 return ms; 1578 } 1579 1580 // (wait ['cnt] . prg) -> any 1581 any doWait(any ex) { 1582 any x, y; 1583 long ms; 1584 1585 x = cdr(ex); 1586 ms = isNil(y = EVAL(car(x)))? -1 : xCnt(ex,y); 1587 x = cdr(x); 1588 while (isNil(y = prog(x))) 1589 if (!(ms = waitFd(ex, -1, ms))) 1590 return prog(x); 1591 return y; 1592 } 1593 1594 // (sync) -> flg 1595 any doSync(any ex) { 1596 byte *p; 1597 int n, cnt; 1598 1599 if (!Mic || !Hear) 1600 return Nil; 1601 if (Sync) 1602 return T; 1603 p = (byte*)&Slot; 1604 cnt = sizeof(int); 1605 for (;;) { 1606 if ((n = write(Mic, p, cnt)) >= 0) { 1607 if ((cnt -= n) == 0) 1608 break; 1609 p += n; 1610 } 1611 else { 1612 if (errno != EINTR) 1613 writeErr("sync"); 1614 if (*Signal) 1615 sighandler(ex); 1616 } 1617 } 1618 Sync = NO; 1619 do 1620 waitFd(ex, -1, -1); 1621 while (!Sync); 1622 return T; 1623 } 1624 1625 // (hear 'cnt) -> cnt 1626 any doHear(any ex) { 1627 any x; 1628 int fd; 1629 1630 x = cdr(ex), x = EVAL(car(x)); 1631 if ((fd = (int)xCnt(ex,x)) < 0 || fd >= InFDs || !InFiles[fd]) 1632 badFd(ex,x); 1633 if (Hear) 1634 close(Hear), closeInFile(Hear), closeOutFile(Hear); 1635 Hear = fd; 1636 return x; 1637 } 1638 1639 // (tell ['cnt] 'sym ['any ..]) -> any 1640 any doTell(any x) { 1641 any y; 1642 int pid; 1643 ptr pbSave, ppSave; 1644 byte buf[PIPE_BUF]; 1645 1646 if (!Tell && !Children) 1647 return Nil; 1648 if (!isCell(x = cdr(x))) { 1649 unsync(); 1650 return Nil; 1651 } 1652 pid = 0; 1653 if (isNum(y = EVAL(car(x)))) { 1654 pid = (int)unDig(y)/2; 1655 x = cdr(x), y = EVAL(car(x)); 1656 } 1657 tellBeg(&pbSave, &ppSave, buf); 1658 while (prTell(y), isCell(x = cdr(x))) 1659 y = EVAL(car(x)); 1660 tellEnd(&pbSave, &ppSave, pid); 1661 return y; 1662 } 1663 1664 // (poll 'cnt) -> cnt | NIL 1665 any doPoll(any ex) { 1666 any x; 1667 int fd; 1668 inFile *p; 1669 fd_set fdSet; 1670 struct timeval tv; 1671 1672 x = cdr(ex), x = EVAL(car(x)); 1673 if ((fd = (int)xCnt(ex,x)) < 0 || fd >= InFDs) 1674 badFd(ex,x); 1675 if (!(p = InFiles[fd])) 1676 return Nil; 1677 do { 1678 if (inReady(p)) 1679 return x; 1680 FD_ZERO(&fdSet); 1681 FD_SET(fd, &fdSet); 1682 tv.tv_sec = tv.tv_usec = 0; 1683 while (select(fd+1, &fdSet, NULL, NULL, &tv) < 0) 1684 if (errno != EINTR) 1685 selectErr(ex); 1686 if (!FD_ISSET(fd, &fdSet)) 1687 return Nil; 1688 } while (slow(p,YES) < 0); 1689 return x; 1690 } 1691 1692 // (key ['cnt]) -> sym 1693 any doKey(any ex) { 1694 any x; 1695 int c, d; 1696 1697 flushAll(); 1698 setRaw(); 1699 x = cdr(ex); 1700 if (!waitFd(ex, STDIN_FILENO, isNil(x = EVAL(car(x)))? -1 : xCnt(ex,x))) 1701 return Nil; 1702 if ((c = stdinByte()) == 0xFF) 1703 c = TOP; 1704 else if (c & 0x80) { 1705 d = stdinByte(); 1706 if ((c & 0x20) == 0) 1707 c = (c & 0x1F) << 6 | d & 0x3F; 1708 else 1709 c = ((c & 0xF) << 6 | d & 0x3F) << 6 | stdinByte() & 0x3F; 1710 } 1711 return mkChar(c); 1712 } 1713 1714 // (peek) -> sym 1715 any doPeek(any ex __attribute__((unused))) { 1716 if (!Chr) 1717 Env.get(); 1718 return Chr<0? Nil : mkChar(Chr); 1719 } 1720 1721 // (char) -> sym 1722 // (char 'cnt) -> sym 1723 // (char T) -> sym 1724 // (char 'sym) -> cnt 1725 any doChar(any ex) { 1726 any x = cdr(ex); 1727 if (!isCell(x)) { 1728 if (!Chr) 1729 Env.get(); 1730 x = Chr<0? Nil : mkChar(getChar()); 1731 Env.get(); 1732 return x; 1733 } 1734 if (isNum(x = EVAL(car(x)))) 1735 return IsZero(x)? Nil : mkChar(unDig(x) / 2); 1736 if (isSym(x)) 1737 return x == T? mkChar(TOP) : boxCnt(symChar(name(x))); 1738 atomError(ex,x); 1739 } 1740 1741 // (skip ['any]) -> sym 1742 any doSkip(any x) { 1743 x = evSym(cdr(x)); 1744 return skipc(symChar(name(x)))<0? Nil : mkChar(Chr); 1745 } 1746 1747 // (eol) -> flg 1748 any doEol(any ex __attribute__((unused))) { 1749 return Chr=='\n' || Chr<=0? T : Nil; 1750 } 1751 1752 // (eof ['flg]) -> flg 1753 any doEof(any x) { 1754 x = cdr(x); 1755 if (!isNil(EVAL(car(x)))) { 1756 Chr = -1; 1757 return T; 1758 } 1759 if (!Chr) 1760 Env.get(); 1761 return Chr < 0? T : Nil; 1762 } 1763 1764 // (from 'any ..) -> sym 1765 any doFrom(any x) { 1766 int i, j, ac = length(x = cdr(x)), p[ac]; 1767 cell c[ac]; 1768 char *av[ac]; 1769 1770 if (ac == 0) 1771 return Nil; 1772 for (i = 0;;) { 1773 Push(c[i], evSym(x)); 1774 av[i] = alloc(NULL, bufSize(data(c[i]))), bufString(data(c[i]), av[i]); 1775 p[i] = 0; 1776 if (++i == ac) 1777 break; 1778 x = cdr(x); 1779 } 1780 if (!Chr) 1781 Env.get(); 1782 while (Chr >= 0) { 1783 for (i = 0; i < ac; ++i) { 1784 for (;;) { 1785 if (av[i][p[i]] == (byte)Chr) { 1786 if (av[i][++p[i]]) 1787 break; 1788 Env.get(); 1789 x = data(c[i]); 1790 goto done; 1791 } 1792 if (!p[i]) 1793 break; 1794 for (j = 1; --p[i]; ++j) 1795 if (memcmp(av[i], av[i]+j, p[i]) == 0) 1796 break; 1797 } 1798 } 1799 Env.get(); 1800 } 1801 x = Nil; 1802 done: 1803 i = 0; do 1804 free(av[i]); 1805 while (++i < ac); 1806 drop(c[0]); 1807 return x; 1808 } 1809 1810 // (till 'any ['flg]) -> lst|sym 1811 any doTill(any ex) { 1812 any x; 1813 int i; 1814 cell c1; 1815 1816 x = evSym(cdr(ex)); 1817 { 1818 char buf[bufSize(x)]; 1819 1820 bufString(x, buf); 1821 if (!Chr) 1822 Env.get(); 1823 if (Chr < 0 || strchr(buf,Chr)) 1824 return Nil; 1825 x = cddr(ex); 1826 if (isNil(EVAL(car(x)))) { 1827 Push(c1, x = cons(mkChar(getChar()), Nil)); 1828 while (Env.get(), Chr > 0 && !strchr(buf,Chr)) 1829 x = cdr(x) = cons(mkChar(getChar()), Nil); 1830 return Pop(c1); 1831 } 1832 Push(c1, boxChar(getChar(), &i, &x)); 1833 while (Env.get(), Chr > 0 && !strchr(buf,Chr)) 1834 charSym(getChar(), &i, &x); 1835 return consStr(Pop(c1)); 1836 } 1837 } 1838 1839 bool eol(void) { 1840 if (Chr < 0) 1841 return YES; 1842 if (Chr == '\n') { 1843 Chr = 0; 1844 return YES; 1845 } 1846 if (Chr == '\r') { 1847 Env.get(); 1848 if (Chr == '\n') 1849 Chr = 0; 1850 return YES; 1851 } 1852 return NO; 1853 } 1854 1855 // (line 'flg ['cnt ..]) -> lst|sym 1856 any doLine(any ex) { 1857 any x, y, z; 1858 bool pack; 1859 int i, n; 1860 cell c1; 1861 1862 if (!Chr) 1863 Env.get(); 1864 if (eol()) 1865 return Nil; 1866 x = cdr(ex); 1867 if (pack = !isNil(EVAL(car(x)))) 1868 Push(c1, boxChar(getChar(), &i, &z)); 1869 else 1870 Push(c1, cons(mkChar(getChar()), Nil)); 1871 if (!isCell(x = cdr(x))) 1872 y = data(c1); 1873 else { 1874 if (!pack) 1875 z = data(c1); 1876 data(c1) = y = cons(data(c1), Nil); 1877 for (;;) { 1878 n = (int)evCnt(ex,x); 1879 while (--n) { 1880 if (Env.get(), eol()) { 1881 if (pack) 1882 car(y) = consStr(car(y)); 1883 return Pop(c1); 1884 } 1885 if (pack) 1886 charSym(getChar(), &i, &z); 1887 else 1888 z = cdr(z) = cons(mkChar(getChar()), Nil); 1889 } 1890 if (pack) 1891 car(y) = consStr(car(y)); 1892 if (!isCell(x = cdr(x))) { 1893 pack = NO; 1894 break; 1895 } 1896 if (Env.get(), eol()) 1897 return Pop(c1); 1898 y = cdr(y) = cons( 1899 pack? boxChar(getChar(), &i, &z) : (z = cons(mkChar(getChar()), Nil)), 1900 Nil ); 1901 } 1902 } 1903 for (;;) { 1904 if (Env.get(), eol()) 1905 return pack? consStr(Pop(c1)) : Pop(c1); 1906 if (pack) 1907 charSym(getChar(), &i, &z); 1908 else 1909 y = cdr(y) = cons(mkChar(getChar()), Nil); 1910 } 1911 } 1912 1913 // (lines 'any ..) -> cnt 1914 any doLines(any x) { 1915 any y; 1916 int c, cnt = 0; 1917 bool flg = NO; 1918 FILE *fp; 1919 1920 for (x = cdr(x); isCell(x); x = cdr(x)) { 1921 y = evSym(x); 1922 { 1923 char nm[pathSize(y)]; 1924 1925 pathString(y, nm); 1926 if (fp = fopen(nm, "r")) { 1927 flg = YES; 1928 while ((c = getc_unlocked(fp)) >= 0) 1929 if (c == '\n') 1930 ++cnt; 1931 fclose(fp); 1932 } 1933 } 1934 } 1935 return flg? boxCnt(cnt) : Nil; 1936 } 1937 1938 static any parse(any x, bool skp, any s) { 1939 int c; 1940 parseFrame *save, parser; 1941 void (*getSave)(void); 1942 cell c1; 1943 1944 save = Env.parser; 1945 Env.parser = &parser; 1946 parser.dig = unDig(parser.name = name(x)); 1947 parser.eof = s? 0xFF : 0xFF5D0A; 1948 getSave = Env.get, Env.get = getParse, c = Chr, Chr = 0; 1949 Push(c1, Env.parser->name); 1950 if (skp) 1951 getParse(); 1952 if (!s) 1953 x = rdList(); 1954 else { 1955 any y; 1956 cell c2; 1957 1958 if (!(x = token(s,0))) 1959 return Nil; 1960 Push(c2, y = cons(x,Nil)); 1961 while (x = token(s,0)) 1962 y = cdr(y) = cons(x,Nil); 1963 x = Pop(c2); 1964 } 1965 drop(c1); 1966 Chr = c, Env.get = getSave, Env.parser = save; 1967 return x; 1968 } 1969 1970 static void putString(int c) { 1971 if (StrP) 1972 byteSym(c, &StrI, &StrP); 1973 else 1974 StrI = 0, data(StrCell) = StrP = box(c & 0xFF); 1975 } 1976 1977 void begString(void) { 1978 StrP = NULL; 1979 Push(StrCell,Nil); 1980 PutSave = Env.put, Env.put = putString; 1981 } 1982 1983 any endString(void) { 1984 Env.put = PutSave; 1985 drop(StrCell); 1986 return StrP? consStr(data(StrCell)) : Nil; 1987 } 1988 1989 // (any 'sym) -> any 1990 any doAny(any ex) { 1991 any x; 1992 1993 x = cdr(ex), x = EVAL(car(x)); 1994 NeedSym(ex,x); 1995 if (!isNil(x)) { 1996 int c; 1997 parseFrame *save, parser; 1998 void (*getSave)(void); 1999 cell c1; 2000 2001 save = Env.parser; 2002 Env.parser = &parser; 2003 parser.dig = unDig(parser.name = name(x)); 2004 parser.eof = 0xFF20; 2005 getSave = Env.get, Env.get = getParse, c = Chr, Chr = 0; 2006 Push(c1, Env.parser->name); 2007 getParse(); 2008 x = read0(YES); 2009 drop(c1); 2010 Chr = c, Env.get = getSave, Env.parser = save; 2011 } 2012 return x; 2013 } 2014 2015 // (sym 'any) -> sym 2016 any doSym(any x) { 2017 x = EVAL(cadr(x)); 2018 begString(); 2019 print(x); 2020 return endString(); 2021 } 2022 2023 // (str 'sym ['sym1]) -> lst 2024 // (str 'lst) -> sym 2025 any doStr(any ex) { 2026 any x; 2027 cell c1, c2; 2028 2029 x = cdr(ex); 2030 if (isNil(x = EVAL(car(x)))) 2031 return Nil; 2032 if (isNum(x)) 2033 argError(ex,x); 2034 if (isSym(x)) { 2035 if (!isCell(cddr(ex))) 2036 return parse(x, NO, NULL); 2037 Push(c1, x); 2038 Push(c2, evSym(cddr(ex))); 2039 x = parse(x, NO, data(c2)); 2040 drop(c1); 2041 return x; 2042 } 2043 begString(); 2044 while (print(car(x)), isCell(x = cdr(x))) 2045 space(); 2046 return endString(); 2047 } 2048 2049 any load(any ex, int pr, any x) { 2050 cell c1, c2; 2051 inFrame f; 2052 2053 if (isSym(x) && firstByte(x) == '-') { 2054 Push(c1, parse(x, YES, NULL)); 2055 x = evList(data(c1)); 2056 drop(c1); 2057 return x; 2058 } 2059 rdOpen(ex, x, &f); 2060 pushInFiles(&f); 2061 doHide(Nil); 2062 x = Nil; 2063 for (;;) { 2064 if (InFile != InFiles[STDIN_FILENO]) 2065 data(c1) = read1(0); 2066 else { 2067 if (pr && !Chr) 2068 prin(run(val(Prompt))), Env.put(pr), space(), flushAll(); 2069 data(c1) = read1(isatty(STDIN_FILENO)? '\n' : 0); 2070 while (Chr > 0) { 2071 if (Chr == '\n') { 2072 Chr = 0; 2073 break; 2074 } 2075 if (Chr == '#') 2076 comment(); 2077 else { 2078 if (Chr > ' ') 2079 break; 2080 Env.get(); 2081 } 2082 } 2083 } 2084 if (isNil(data(c1))) { 2085 popInFiles(); 2086 doHide(Nil); 2087 return x; 2088 } 2089 Save(c1); 2090 if (InFile != InFiles[STDIN_FILENO] || Chr || !pr) 2091 x = EVAL(data(c1)); 2092 else { 2093 flushAll(); 2094 Push(c2, val(At)); 2095 x = val(At) = EVAL(data(c1)); 2096 val(At3) = val(At2), val(At2) = data(c2); 2097 outString("-> "), flushAll(), print1(x), newline(); 2098 } 2099 drop(c1); 2100 } 2101 } 2102 2103 // (load 'any ..) -> any 2104 any doLoad(any ex) { 2105 any x, y; 2106 2107 x = cdr(ex); 2108 do { 2109 if ((y = EVAL(car(x))) != T) 2110 y = load(ex, '>', y); 2111 else 2112 y = loadAll(ex); 2113 } while (isCell(x = cdr(x))); 2114 return y; 2115 } 2116 2117 // (in 'any . prg) -> any 2118 any doIn(any ex) { 2119 any x; 2120 inFrame f; 2121 2122 x = cdr(ex), x = EVAL(car(x)); 2123 rdOpen(ex, x, &f); 2124 pushInFiles(&f); 2125 x = prog(cddr(ex)); 2126 popInFiles(); 2127 return x; 2128 } 2129 2130 // (out 'any . prg) -> any 2131 any doOut(any ex) { 2132 any x; 2133 outFrame f; 2134 2135 x = cdr(ex), x = EVAL(car(x)); 2136 wrOpen(ex, x, &f); 2137 pushOutFiles(&f); 2138 x = prog(cddr(ex)); 2139 popOutFiles(); 2140 return x; 2141 } 2142 2143 // (err 'sym . prg) -> any 2144 any doErr(any ex) { 2145 any x; 2146 errFrame f; 2147 2148 x = cdr(ex), x = EVAL(car(x)); 2149 erOpen(ex,x,&f); 2150 pushErrFiles(&f); 2151 x = prog(cddr(ex)); 2152 popErrFiles(); 2153 return x; 2154 } 2155 2156 // (ctl 'sym . prg) -> any 2157 any doCtl(any ex) { 2158 any x; 2159 ctlFrame f; 2160 2161 x = cdr(ex), x = EVAL(car(x)); 2162 ctOpen(ex,x,&f); 2163 pushCtlFiles(&f); 2164 x = prog(cddr(ex)); 2165 popCtlFiles(); 2166 return x; 2167 } 2168 2169 // (pipe exe) -> cnt 2170 // (pipe exe . prg) -> any 2171 any doPipe(any ex) { 2172 any x; 2173 union { 2174 inFrame in; 2175 outFrame out; 2176 } f; 2177 int pfd[2]; 2178 2179 if (pipe(pfd) < 0) 2180 err(ex, NULL, "Can't pipe"); 2181 closeOnExec(ex, pfd[0]), closeOnExec(ex, pfd[1]); 2182 if ((f.in.pid = forkLisp(ex)) == 0) { 2183 if (isCell(cddr(ex))) 2184 setpgid(0,0); 2185 close(pfd[0]); 2186 if (pfd[1] != STDOUT_FILENO) 2187 dup2(pfd[1], STDOUT_FILENO), close(pfd[1]); 2188 wrOpen(ex, Nil, &f.out); 2189 pushOutFiles(&f.out); 2190 OutFile->tty = NO; 2191 val(Run) = Nil; 2192 EVAL(cadr(ex)); 2193 bye(0); 2194 } 2195 close(pfd[1]); 2196 initInFile(f.in.fd = pfd[0], NULL); 2197 if (!isCell(cddr(ex))) 2198 return boxCnt(pfd[0]); 2199 setpgid(f.in.pid,0); 2200 pushInFiles(&f.in); 2201 x = prog(cddr(ex)); 2202 popInFiles(); 2203 return x; 2204 } 2205 2206 // (open 'any ['flg]) -> cnt | NIL 2207 any doOpen(any ex) { 2208 any x = evSym(cdr(ex)); 2209 char nm[pathSize(x)]; 2210 int fd; 2211 2212 pathString(x, nm); 2213 x = caddr(ex), x = EVAL(x); 2214 while ((fd = open(nm, isNil(x)? O_CREAT|O_RDWR : O_RDONLY, 0666)) < 0) { 2215 if (errno != EINTR) 2216 return Nil; 2217 if (*Signal) 2218 sighandler(ex); 2219 } 2220 closeOnExec(ex, fd); 2221 initInFile(fd, strdup(nm)), initOutFile(fd); 2222 return boxCnt(fd); 2223 } 2224 2225 // (close 'cnt) -> cnt | NIL 2226 any doClose(any ex) { 2227 any x; 2228 int fd; 2229 2230 x = cdr(ex), x = EVAL(car(x)), fd = (int)xCnt(ex,x); 2231 while (close(fd)) { 2232 if (errno != EINTR) 2233 return Nil; 2234 if (*Signal) 2235 sighandler(ex); 2236 } 2237 closeInFile(fd), closeOutFile(fd); 2238 return x; 2239 } 2240 2241 // (echo ['cnt ['cnt]] | ['sym ..]) -> sym 2242 any doEcho(any ex) { 2243 any x, y; 2244 long cnt; 2245 2246 x = cdr(ex), y = EVAL(car(x)); 2247 if (!Chr) 2248 Env.get(); 2249 if (isNil(y) && !isCell(cdr(x))) { 2250 while (Chr >= 0) 2251 Env.put(Chr), Env.get(); 2252 return T; 2253 } 2254 if (isSym(y)) { 2255 int m, n, i, j, ac = length(x), p[ac], om, op; 2256 cell c[ac]; 2257 char *av[ac]; 2258 2259 for (i = 0;;) { 2260 Push(c[i], y); 2261 av[i] = alloc(NULL, bufSize(y)), bufString(y, av[i]); 2262 p[i] = 0; 2263 if (++i == ac) 2264 break; 2265 y = evSym(x = cdr(x)); 2266 } 2267 m = -1; 2268 while (Chr >= 0) { 2269 if ((om = m) >= 0) 2270 op = p[m]; 2271 for (i = 0; i < ac; ++i) { 2272 for (;;) { 2273 if (av[i][p[i]] == (byte)Chr) { 2274 if (av[i][++p[i]]) { 2275 if (m < 0 || p[i] > p[m]) 2276 m = i; 2277 break; 2278 } 2279 if (om >= 0) 2280 for (j = 0, n = op-p[i]; j <= n; ++j) 2281 Env.put(av[om][j]); 2282 Chr = 0; 2283 x = data(c[i]); 2284 goto done; 2285 } 2286 if (!p[i]) 2287 break; 2288 for (j = 1; --p[i]; ++j) 2289 if (memcmp(av[i], av[i]+j, p[i]) == 0) 2290 break; 2291 if (m == i) 2292 for (m = -1, j = 0; j < ac; ++j) 2293 if (p[j] && (m < 0 || p[j] > p[m])) 2294 m = j; 2295 } 2296 } 2297 if (m < 0) { 2298 if (om >= 0) 2299 for (i = 0; i < op; ++i) 2300 Env.put(av[om][i]); 2301 Env.put(Chr); 2302 } 2303 else if (om >= 0) 2304 for (i = 0, n = op-p[m]; i <= n; ++i) 2305 Env.put(av[om][i]); 2306 Env.get(); 2307 } 2308 x = Nil; 2309 done: 2310 i = 0; do 2311 free(av[i]); 2312 while (++i < ac); 2313 drop(c[0]); 2314 return x; 2315 } 2316 if (isCell(x = cdr(x))) { 2317 for (cnt = xCnt(ex,y), y = EVAL(car(x)); --cnt >= 0; Env.get()) 2318 if (Chr < 0) 2319 return Nil; 2320 } 2321 if ((cnt = xCnt(ex,y)) > 0) { 2322 for (;;) { 2323 if (Chr < 0) 2324 return Nil; 2325 Env.put(Chr); 2326 if (!--cnt) 2327 break; 2328 Env.get(); 2329 } 2330 } 2331 Chr = 0; 2332 return T; 2333 } 2334 2335 /*** Printing ***/ 2336 void putStdout(int c) { 2337 if (OutFile) { 2338 if (OutFile->ix == BUFSIZ) { 2339 OutFile->ix = 0; 2340 wrBytes(OutFile->fd, OutFile->buf, BUFSIZ); 2341 } 2342 if ((OutFile->buf[OutFile->ix++] = c) == '\n' && OutFile->tty) { 2343 int n = OutFile->ix; 2344 2345 OutFile->ix = 0; 2346 wrBytes(OutFile->fd, OutFile->buf, n); 2347 } 2348 } 2349 } 2350 2351 void newline(void) {Env.put('\n');} 2352 void space(void) {Env.put(' ');} 2353 2354 void outWord(word n) { 2355 if (n > 9) 2356 outWord(n / 10); 2357 Env.put('0' + n % 10); 2358 } 2359 2360 void outString(char *s) { 2361 while (*s) 2362 Env.put(*s++); 2363 } 2364 2365 static void outSym(int c) { 2366 do 2367 Env.put(c); 2368 while (c = symByte(NULL)); 2369 } 2370 2371 void outName(any s) {outSym(symByte(name(s)));} 2372 2373 void outNum(any x) { 2374 if (isNum(cdr(numCell(x)))) { 2375 cell c1; 2376 2377 Push(c1, numToSym(x, 0, 0, 0)); 2378 outName(data(c1)); 2379 drop(c1); 2380 } 2381 else { 2382 char *p, buf[BITS/2]; 2383 2384 sprintf(p = buf, "%ld", unBox(x)); 2385 do 2386 Env.put(*p++); 2387 while (*p); 2388 } 2389 } 2390 2391 /* Print one expression */ 2392 void print(any x) { 2393 cell c1; 2394 2395 Push(c1,x); 2396 print1(x); 2397 drop(c1); 2398 } 2399 2400 void print1(any x) { 2401 if (*Signal) 2402 sighandler(NULL); 2403 if (isNum(x)) 2404 outNum(x); 2405 else if (isNil(x)) 2406 outString("NIL"); 2407 else if (isSym(x)) { 2408 int c; 2409 any y; 2410 2411 if (!(c = symByte(y = name(x)))) 2412 Env.put('$'), outWord(num(x)/sizeof(cell)); 2413 else if (isExt(x)) 2414 Env.put('{'), outSym(c), Env.put('}'); 2415 else if (hashed(x, Intern[ihash(y)])) { 2416 if (unDig(y) == '.') 2417 Env.put('\\'), Env.put('.'); 2418 else { 2419 do { 2420 if (c == '\\' || strchr(Delim, c)) 2421 Env.put('\\'); 2422 Env.put(c); 2423 } while (c = symByte(NULL)); 2424 } 2425 } 2426 else { 2427 bool tsm = isCell(val(Tsm)) && Env.put == putStdout && OutFile->tty; 2428 2429 if (!tsm) 2430 Env.put('"'); 2431 else { 2432 outName(car(val(Tsm))); 2433 c = symByte(y); 2434 } 2435 do { 2436 if (c == '\\' || c == '^' || !tsm && c == '"') 2437 Env.put('\\'); 2438 else if (c == 127) 2439 Env.put('^'), c = '?'; 2440 else if (c < ' ') 2441 Env.put('^'), c |= 0x40; 2442 Env.put(c); 2443 } while (c = symByte(NULL)); 2444 if (!tsm) 2445 Env.put('"'); 2446 else 2447 outName(cdr(val(Tsm))); 2448 } 2449 } 2450 else if (car(x) == Quote && x != cdr(x)) 2451 Env.put('\''), print1(cdr(x)); 2452 else { 2453 any y; 2454 2455 Env.put('('); 2456 if ((y = circ(x)) == NULL) { 2457 while (print1(car(x)), !isNil(x = cdr(x))) { 2458 if (!isCell(x)) { 2459 outString(" . "); 2460 print1(x); 2461 break; 2462 } 2463 space(); 2464 } 2465 } 2466 else if (y == x) { 2467 do 2468 print1(car(x)), space(); 2469 while (y != (x = cdr(x))); 2470 Env.put('.'); 2471 } 2472 else { 2473 do 2474 print1(car(x)), space(); 2475 while (y != (x = cdr(x))); 2476 outString(". ("); 2477 do 2478 print1(car(x)), space(); 2479 while (y != (x = cdr(x))); 2480 outString(".)"); 2481 } 2482 Env.put(')'); 2483 } 2484 } 2485 2486 void prin(any x) { 2487 cell c1; 2488 2489 Push(c1,x); 2490 prin1(x); 2491 drop(c1); 2492 } 2493 2494 void prin1(any x) { 2495 if (*Signal) 2496 sighandler(NULL); 2497 if (!isNil(x)) { 2498 if (isNum(x)) 2499 outNum(x); 2500 else if (isSym(x)) { 2501 if (isExt(x)) 2502 Env.put('{'); 2503 outName(x); 2504 if (isExt(x)) 2505 Env.put('}'); 2506 } 2507 else { 2508 while (prin1(car(x)), !isNil(x = cdr(x))) { 2509 if (!isCell(x)) { 2510 prin1(x); 2511 break; 2512 } 2513 } 2514 } 2515 } 2516 } 2517 2518 // (prin 'any ..) -> any 2519 any doPrin(any x) { 2520 any y = Nil; 2521 2522 while (isCell(x = cdr(x))) 2523 prin(y = EVAL(car(x))); 2524 return y; 2525 } 2526 2527 // (prinl 'any ..) -> any 2528 any doPrinl(any x) { 2529 any y = Nil; 2530 2531 while (isCell(x = cdr(x))) 2532 prin(y = EVAL(car(x))); 2533 newline(); 2534 return y; 2535 } 2536 2537 // (space ['cnt]) -> cnt 2538 any doSpace(any ex) { 2539 any x; 2540 int n; 2541 2542 if (isNil(x = EVAL(cadr(ex)))) { 2543 Env.put(' '); 2544 return One; 2545 } 2546 for (n = xCnt(ex,x); n > 0; --n) 2547 Env.put(' '); 2548 return x; 2549 } 2550 2551 // (print 'any ..) -> any 2552 any doPrint(any x) { 2553 any y; 2554 2555 x = cdr(x), print(y = EVAL(car(x))); 2556 while (isCell(x = cdr(x))) 2557 space(), print(y = EVAL(car(x))); 2558 return y; 2559 } 2560 2561 // (printsp 'any ..) -> any 2562 any doPrintsp(any x) { 2563 any y; 2564 2565 x = cdr(x); 2566 do 2567 print(y = EVAL(car(x))), space(); 2568 while (isCell(x = cdr(x))); 2569 return y; 2570 } 2571 2572 // (println 'any ..) -> any 2573 any doPrintln(any x) { 2574 any y; 2575 2576 x = cdr(x), print(y = EVAL(car(x))); 2577 while (isCell(x = cdr(x))) 2578 space(), print(y = EVAL(car(x))); 2579 newline(); 2580 return y; 2581 } 2582 2583 // (flush) -> flg 2584 any doFlush(any ex __attribute__((unused))) { 2585 return flush(OutFile)? T : Nil; 2586 } 2587 2588 // (rewind) -> flg 2589 any doRewind(any ex __attribute__((unused))) { 2590 if (!OutFile) 2591 return Nil; 2592 OutFile->ix = 0; 2593 return lseek(OutFile->fd, 0L, SEEK_SET) || ftruncate(OutFile->fd, 0)? Nil : T; 2594 } 2595 2596 // (ext 'cnt . prg) -> any 2597 any doExt(any ex) { 2598 int extn; 2599 any x; 2600 2601 x = cdr(ex); 2602 extn = ExtN, ExtN = (int)evCnt(ex,x); 2603 x = prog(cddr(ex)); 2604 ExtN = extn; 2605 return x; 2606 } 2607 2608 // (rd ['sym]) -> any 2609 // (rd 'cnt) -> num | NIL 2610 any doRd(any x) { 2611 long cnt; 2612 int n, i; 2613 cell c1; 2614 2615 x = cdr(x), x = EVAL(car(x)); 2616 if (!isNum(x)) { 2617 Push(c1,x); 2618 getBin = getBinary; 2619 x = binRead(ExtN) ?: data(c1); 2620 drop(c1); 2621 return x; 2622 } 2623 if ((cnt = unBox(x)) < 0) { 2624 if ((n = getBinary()) < 0) 2625 return Nil; 2626 i = 0, Push(c1, x = box(n)); 2627 while (++cnt) { 2628 if ((n = getBinary()) < 0) 2629 return Nil; 2630 byteSym(n, &i, &x); 2631 } 2632 zapZero(data(c1)); 2633 digMul2(data(c1)); 2634 } 2635 else { 2636 if ((n = getBinary()) < 0) 2637 return Nil; 2638 i = 0, Push(c1, x = box(n+n)); 2639 while (--cnt) { 2640 if ((n = getBinary()) < 0) 2641 return Nil; 2642 digMul(data(c1), 256); 2643 setDig(data(c1), unDig(data(c1)) | n+n); 2644 } 2645 zapZero(data(c1)); 2646 } 2647 return Pop(c1); 2648 } 2649 2650 // (pr 'any ..) -> any 2651 any doPr(any x) { 2652 any y; 2653 2654 x = cdr(x); 2655 do 2656 pr(ExtN, y = EVAL(car(x))); 2657 while (isCell(x = cdr(x))); 2658 return y; 2659 } 2660 2661 // (wr 'cnt ..) -> cnt 2662 any doWr(any x) { 2663 any y; 2664 2665 x = cdr(x); 2666 do 2667 putStdout(unDig(y = EVAL(car(x))) / 2); 2668 while (isCell(x = cdr(x))); 2669 return y; 2670 } 2671 2672 /*** DB-I/O ***/ 2673 #define BLKSIZE 64 // DB block unit size 2674 #define BLK 6 2675 #define TAGMASK (BLKSIZE-1) 2676 #define BLKMASK (~TAGMASK) 2677 #define EXTERN64 65536 2678 2679 static int F, Files, *BlkShift, *BlkFile, *BlkSize, *Fluse, MaxBlkSize; 2680 static FILE *Jnl, *Log; 2681 static adr BlkIndex, BlkLink; 2682 static adr *Marks; 2683 static byte *Locks, *Ptr, **Mark; 2684 static byte *Block, *IniBlk; // 01 00 00 00 00 00 NIL 0 2685 2686 static adr getAdr(byte *p) { 2687 return (adr)p[0] | (adr)p[1]<<8 | (adr)p[2]<<16 | 2688 (adr)p[3]<<24 | (adr)p[4]<<32 | (adr)p[5]<<40; 2689 } 2690 2691 static void setAdr(adr n, byte *p) { 2692 p[0] = (byte)n, p[1] = (byte)(n >> 8), p[2] = (byte)(n >> 16); 2693 p[3] = (byte)(n >> 24), p[4] = (byte)(n >> 32), p[5] = (byte)(n >> 40); 2694 } 2695 2696 static void dbfErr(any ex) {err(ex, NULL, "Bad DB file");} 2697 static void dbErr(char *s) {err(NULL, NULL, "DB %s: %s", s, strerror(errno));} 2698 static void jnlErr(any ex) {err(ex, NULL, "Bad Journal");} 2699 static void fsyncErr(any ex, char *s) {err(ex, NULL, "%s fsync error: %s", s, strerror(errno));} 2700 static void truncErr(any ex) {err(ex, NULL, "Log truncate error: %s", strerror(errno));} 2701 static void ignLog(void) {fprintf(stderr, "Discarding incomplete transaction.\n");} 2702 2703 any new64(adr n, any x) { 2704 int c, i; 2705 adr w = 0; 2706 2707 do { 2708 if ((c = n & 0x3F) > 11) 2709 c += 5; 2710 if (c > 42) 2711 c += 6; 2712 w = w << 8 | c + '0'; 2713 } while (n >>= 6); 2714 if (i = F) { 2715 ++i; 2716 w = w << 8 | '-'; 2717 do { 2718 if ((c = i & 0x3F) > 11) 2719 c += 5; 2720 if (c > 42) 2721 c += 6; 2722 w = w << 8 | c + '0'; 2723 } while (i >>= 6); 2724 } 2725 return hi(w)? consNum(num(w), consNum(hi(w), x)) : consNum(num(w), x); 2726 } 2727 2728 adr blk64(any x) { 2729 int c; 2730 adr n, w; 2731 2732 F = 0; 2733 n = 0; 2734 if (isNum(x)) { 2735 w = unDig(x); 2736 if (isNum(x = cdr(numCell(x)))) 2737 w |= (adr)unDig(x) << BITS; 2738 do { 2739 if ((c = w & 0xFF) == '-') 2740 F = n-1, n = 0; 2741 else { 2742 if ((c -= '0') > 42) 2743 c -= 6; 2744 if (c > 11) 2745 c -= 5; 2746 n = n << 6 | c; 2747 } 2748 } while (w >>= 8); 2749 } 2750 return n; 2751 } 2752 2753 any extOffs(int offs, any x) { 2754 int f = F; 2755 adr n = blk64(x); 2756 2757 if (offs != -EXTERN64) { 2758 if ((F += offs) < 0) 2759 err(NULL, NULL, "%d: Bad DB offset", F); 2760 x = new64(n, Nil); 2761 } 2762 else { // Undocumented 64-bit DB export 2763 adr w = n & 0xFFFFF | (F & 0xFF) << 20; 2764 2765 w |= ((n >>= 20) & 0xFFF) << 28; 2766 w |= (adr)(F >> 8) << 40 | (n >> 12) << 48; 2767 x = hi(w)? consNum(num(w), consNum(hi(w), Nil)) : consNum(num(w), Nil); 2768 } 2769 F = f; 2770 return x; 2771 } 2772 2773 /* DB Record Locking */ 2774 static void dbLock(int cmd, int typ, int f, off_t len) { 2775 struct flock fl; 2776 2777 fl.l_type = typ; 2778 fl.l_whence = SEEK_SET; 2779 fl.l_start = 0; 2780 fl.l_len = len; 2781 while (fcntl(BlkFile[f], cmd, &fl) < 0 && typ != F_UNLCK) 2782 if (errno != EINTR) 2783 lockErr(); 2784 } 2785 2786 static inline void rdLock(void) { 2787 if (val(Solo) != T) 2788 dbLock(F_SETLKW, F_RDLCK, 0, 1); 2789 } 2790 2791 static inline void wrLock(void) { 2792 if (val(Solo) != T) 2793 dbLock(F_SETLKW, F_WRLCK, 0, 1); 2794 } 2795 2796 static inline void rwUnlock(off_t len) { 2797 if (val(Solo) != T) { 2798 if (len == 0) { 2799 int f; 2800 2801 for (f = 1; f < Files; ++f) 2802 if (Locks[f]) 2803 dbLock(F_SETLK, F_UNLCK, f, 0), Locks[f] = 0; 2804 val(Solo) = Zero; 2805 } 2806 dbLock(F_SETLK, F_UNLCK, 0, len); 2807 } 2808 } 2809 2810 static pid_t tryLock(off_t n, off_t len) { 2811 struct flock fl; 2812 2813 for (;;) { 2814 fl.l_type = F_WRLCK; 2815 fl.l_whence = SEEK_SET; 2816 fl.l_start = n; 2817 fl.l_len = len; 2818 if (fcntl(BlkFile[F], F_SETLK, &fl) >= 0) { 2819 Locks[F] = 1; 2820 if (!n) 2821 val(Solo) = T; 2822 else if (val(Solo) != T) 2823 val(Solo) = Nil; 2824 return 0; 2825 } 2826 if (errno != EINTR && errno != EACCES && errno != EAGAIN) 2827 lockErr(); 2828 while (fcntl(BlkFile[F], F_GETLK, &fl) < 0) 2829 if (errno != EINTR) 2830 lockErr(); 2831 if (fl.l_type != F_UNLCK) 2832 return fl.l_pid; 2833 } 2834 } 2835 2836 static void blkPeek(off_t pos, void *buf, int siz) { 2837 if (pread(BlkFile[F], buf, siz, pos) != (ssize_t)siz) 2838 dbErr("read"); 2839 } 2840 2841 static void blkPoke(off_t pos, void *buf, int siz) { 2842 if (pwrite(BlkFile[F], buf, siz, pos) != (ssize_t)siz) 2843 dbErr("write"); 2844 if (Jnl) { 2845 byte a[BLK+2]; 2846 2847 putc_unlocked(siz == BlkSize[F]? BLKSIZE : siz, Jnl); 2848 a[0] = (byte)F, a[1] = (byte)(F >> 8), setAdr(pos >> BlkShift[F], a+2); 2849 if (fwrite(a, BLK+2, 1, Jnl) != 1 || fwrite(buf, siz, 1, Jnl) != 1) 2850 writeErr("Journal"); 2851 } 2852 } 2853 2854 static void rdBlock(adr n) { 2855 blkPeek((BlkIndex = n) << BlkShift[F], Block, BlkSize[F]); 2856 BlkLink = getAdr(Block) & BLKMASK; 2857 Ptr = Block + BLK; 2858 } 2859 2860 static void logBlock(void) { 2861 byte a[BLK+2]; 2862 2863 a[0] = (byte)F, a[1] = (byte)(F >> 8), setAdr(BlkIndex, a+2); 2864 if (fwrite(a, BLK+2, 1, Log) != 1 || fwrite(Block, BlkSize[F], 1, Log) != 1) 2865 writeErr("Log"); 2866 } 2867 2868 static void wrBlock(void) {blkPoke(BlkIndex << BlkShift[F], Block, BlkSize[F]);} 2869 2870 static adr newBlock(void) { 2871 adr n; 2872 byte buf[2*BLK]; 2873 2874 blkPeek(0, buf, 2*BLK); // Get Free, Next 2875 if ((n = getAdr(buf)) && Fluse[F]) { 2876 blkPeek(n << BlkShift[F], buf, BLK); // Get free link 2877 --Fluse[F]; 2878 } 2879 else if ((n = getAdr(buf+BLK)) != 281474976710592LL) 2880 setAdr(n + BLKSIZE, buf+BLK); // Increment next 2881 else 2882 err(NULL, NULL, "DB Oversize"); 2883 blkPoke(0, buf, 2*BLK); 2884 setAdr(0, IniBlk), blkPoke(n << BlkShift[F], IniBlk, BlkSize[F]); 2885 return n; 2886 } 2887 2888 any newId(any ex, int i) { 2889 adr n; 2890 2891 if ((F = i-1) >= Files) 2892 dbfErr(ex); 2893 if (!Log) 2894 ++Env.protect; 2895 wrLock(); 2896 if (Jnl) 2897 lockFile(fileno(Jnl), F_SETLKW, F_WRLCK); 2898 n = newBlock(); 2899 if (Jnl) 2900 fflush(Jnl), lockFile(fileno(Jnl), F_SETLK, F_UNLCK); 2901 rwUnlock(1); 2902 if (!Log) 2903 --Env.protect; 2904 return new64(n/BLKSIZE, At2); // dirty 2905 } 2906 2907 bool isLife(any x) { 2908 adr n; 2909 byte buf[2*BLK]; 2910 2911 if ((n = blk64(name(x))*BLKSIZE) > 0) { 2912 if (F < Files) { 2913 for (x = tail1(x); !isSym(x); x = cdr(cellPtr(x))); 2914 if (x == At || x == At2) 2915 return YES; 2916 if (x != At3) { 2917 blkPeek(0, buf, 2*BLK); // Get Next 2918 if (n < getAdr(buf+BLK)) { 2919 blkPeek(n << BlkShift[F], buf, BLK); 2920 if ((buf[0] & TAGMASK) == 1) 2921 return YES; 2922 } 2923 } 2924 } 2925 else if (!isNil(val(Ext))) 2926 return YES; 2927 } 2928 return NO; 2929 } 2930 2931 static void cleanUp(adr n) { 2932 adr p, fr; 2933 byte buf[BLK]; 2934 2935 blkPeek(0, buf, BLK), fr = getAdr(buf); // Get Free 2936 setAdr(n, buf), blkPoke(0, buf, BLK); // Set new 2937 for (;;) { 2938 p = n << BlkShift[F]; 2939 blkPeek(p, buf, BLK); // Get block link 2940 buf[0] &= BLKMASK; // Clear Tag 2941 if ((n = getAdr(buf)) == 0) 2942 break; 2943 blkPoke(p, buf, BLK); 2944 } 2945 setAdr(fr, buf), blkPoke(p, buf, BLK); // Append old free list 2946 } 2947 2948 static int getBlock(void) { 2949 if (Ptr == Block+BlkSize[F]) { 2950 if (!BlkLink) 2951 return 0; 2952 rdBlock(BlkLink); 2953 } 2954 return *Ptr++; 2955 } 2956 2957 static void putBlock(int c) { 2958 if (Ptr == Block+BlkSize[F]) { 2959 if (BlkLink) 2960 wrBlock(), rdBlock(BlkLink); 2961 else { 2962 adr n = newBlock(); 2963 int cnt = Block[0]; // Link must be 0 2964 2965 setAdr(n | cnt, Block); 2966 wrBlock(); 2967 BlkIndex = n; 2968 if (cnt < TAGMASK) 2969 ++cnt; 2970 setAdr(cnt, Block); 2971 Ptr = Block + BLK; 2972 } 2973 } 2974 *Ptr++ = (byte)c; 2975 } 2976 2977 // Test for existing transaction 2978 static bool transaction(void) { 2979 byte a[BLK]; 2980 2981 fseek(Log, 0L, SEEK_SET); 2982 if (fread(a, 2, 1, Log) == 0) { 2983 if (!feof(Log)) 2984 ignLog(); 2985 return NO; 2986 } 2987 for (;;) { 2988 if (a[0] == 0xFF && a[1] == 0xFF) 2989 return YES; 2990 if ((F = a[0] | a[1]<<8) >= Files || 2991 fread(a, BLK, 1, Log) != 1 || 2992 fseek(Log, BlkSize[F], SEEK_CUR) != 0 || 2993 fread(a, 2, 1, Log) != 1 ) { 2994 ignLog(); 2995 return NO; 2996 } 2997 } 2998 } 2999 3000 static void restore(any ex) { 3001 byte dirty[Files], a[BLK], buf[MaxBlkSize]; 3002 3003 fprintf(stderr, "Last transaction not completed: Rollback\n"); 3004 fseek(Log, 0L, SEEK_SET); 3005 for (F = 0; F < Files; ++F) 3006 dirty[F] = 0; 3007 for (;;) { 3008 if (fread(a, 2, 1, Log) == 0) 3009 jnlErr(ex); 3010 if (a[0] == 0xFF && a[1] == 0xFF) 3011 break; 3012 if ((F = a[0] | a[1]<<8) >= Files || 3013 fread(a, BLK, 1, Log) != 1 || 3014 fread(buf, BlkSize[F], 1, Log) != 1 ) 3015 jnlErr(ex); 3016 if (pwrite(BlkFile[F], buf, BlkSize[F], getAdr(a) << BlkShift[F]) != (ssize_t)BlkSize[F]) 3017 dbErr("write"); 3018 dirty[F] = 1; 3019 } 3020 for (F = 0; F < Files; ++F) 3021 if (dirty[F] && fsync(BlkFile[F]) < 0) 3022 fsyncErr(ex, "DB"); 3023 } 3024 3025 // (pool ['sym1 ['lst] ['sym2] ['sym3]]) -> T 3026 any doPool(any ex) { 3027 any x; 3028 byte buf[2*BLK+1]; 3029 cell c1, c2, c3, c4; 3030 3031 x = cdr(ex), Push(c1, evSym(x)); // db 3032 x = cdr(x), Push(c2, EVAL(car(x))); // lst 3033 NeedLst(ex,data(c2)); 3034 x = cdr(x), Push(c3, evSym(x)); // sym2 3035 Push(c4, evSym(cdr(x))); // sym3 3036 val(Solo) = Zero; 3037 if (Files) { 3038 doRollback(Nil); 3039 for (F = 0; F < Files; ++F) { 3040 if (Marks) 3041 free(Mark[F]); 3042 if (close(BlkFile[F]) < 0) 3043 closeErr(); 3044 } 3045 free(Mark), Mark = NULL, free(Marks), Marks = NULL; 3046 Files = 0; 3047 if (Jnl) 3048 fclose(Jnl), Jnl = NULL; 3049 if (Log) 3050 fclose(Log), Log = NULL; 3051 } 3052 if (!isNil(data(c1))) { 3053 x = data(c2); 3054 Files = length(x) ?: 1; 3055 BlkShift = alloc(BlkShift, Files * sizeof(int)); 3056 BlkFile = alloc(BlkFile, Files * sizeof(int)); 3057 BlkSize = alloc(BlkSize, Files * sizeof(int)); 3058 Fluse = alloc(Fluse, Files * sizeof(int)); 3059 Locks = alloc(Locks, Files), memset(Locks, 0, Files); 3060 MaxBlkSize = 0; 3061 for (F = 0; F < Files; ++F) { 3062 char nm[pathSize(data(c1)) + 8]; 3063 3064 pathString(data(c1), nm); 3065 if (isCell(x)) 3066 sprintf(nm + strlen(nm), "%d", F+1); 3067 BlkShift[F] = isNum(car(x))? (int)unDig(car(x))/2 : 2; 3068 if ((BlkFile[F] = open(nm, O_RDWR)) >= 0) { 3069 blkPeek(0, buf, 2*BLK+1); // Get block shift 3070 BlkSize[F] = BLKSIZE << (BlkShift[F] = (int)buf[2*BLK]); 3071 } 3072 else { 3073 if (errno != ENOENT || 3074 (BlkFile[F] = open(nm, O_CREAT|O_EXCL|O_RDWR, 0666)) < 0) { 3075 Files = F; 3076 openErr(ex, nm); 3077 } 3078 BlkSize[F] = BLKSIZE << BlkShift[F]; 3079 setAdr(0, buf); // Free 3080 if (F) 3081 setAdr(BLKSIZE, buf+BLK); // Next 3082 else { 3083 byte blk[BlkSize[0]]; 3084 3085 setAdr(2*BLKSIZE, buf+BLK); // Next 3086 memset(blk, 0, BlkSize[0]); 3087 setAdr(1, blk), blkPoke(BlkSize[0], blk, BlkSize[0]); 3088 } 3089 buf[2*BLK] = (byte)BlkShift[F]; 3090 blkPoke(0, buf, 2*BLK+1); 3091 } 3092 closeOnExec(ex, BlkFile[F]); 3093 if (BlkSize[F] > MaxBlkSize) 3094 MaxBlkSize = BlkSize[F]; 3095 Fluse[F] = -1; 3096 x = cdr(x); 3097 } 3098 Block = alloc(Block, MaxBlkSize); 3099 IniBlk = alloc(IniBlk, MaxBlkSize); 3100 memset(IniBlk, 0, MaxBlkSize); 3101 if (!isNil(data(c3))) { 3102 char nm[pathSize(data(c3))]; 3103 3104 pathString(data(c3), nm); 3105 if (!(Jnl = fopen(nm, "a"))) 3106 openErr(ex, nm); 3107 closeOnExec(ex, fileno(Jnl)); 3108 } 3109 if (!isNil(data(c4))) { 3110 char nm[pathSize(data(c4))]; 3111 3112 pathString(data(c4), nm); 3113 if (!(Log = fopen(nm, "a+"))) 3114 openErr(ex, nm); 3115 closeOnExec(ex, fileno(Log)); 3116 if (transaction()) 3117 restore(ex); 3118 fseek(Log, 0L, SEEK_SET); 3119 if (ftruncate(fileno(Log), 0)) 3120 truncErr(ex); 3121 } 3122 } 3123 drop(c1); 3124 return T; 3125 } 3126 3127 // (journal 'any ..) -> T 3128 any doJournal(any ex) { 3129 any x, y; 3130 int siz; 3131 FILE *fp; 3132 byte a[BLK], buf[MaxBlkSize]; 3133 3134 for (x = cdr(ex); isCell(x); x = cdr(x)) { 3135 y = evSym(x); 3136 { 3137 char nm[pathSize(y)]; 3138 3139 pathString(y, nm); 3140 if (!(fp = fopen(nm, "r"))) 3141 openErr(ex, nm); 3142 while ((siz = getc_unlocked(fp)) >= 0) { 3143 if (fread(a, 2, 1, fp) != 1) 3144 jnlErr(ex); 3145 if ((F = a[0] | a[1]<<8) >= Files) 3146 dbfErr(ex); 3147 if (siz == BLKSIZE) 3148 siz = BlkSize[F]; 3149 if (fread(a, BLK, 1, fp) != 1 || fread(buf, siz, 1, fp) != 1) 3150 jnlErr(ex); 3151 blkPoke(getAdr(a) << BlkShift[F], buf, siz); 3152 } 3153 fclose(fp); 3154 } 3155 } 3156 return T; 3157 } 3158 3159 static any mkId(adr n) { 3160 any x, y, *h; 3161 3162 x = new64(n, Nil); 3163 if (y = findHash(x, h = Extern + ehash(x))) 3164 return y; 3165 mkExt(y = consSym(Nil,x)); 3166 *h = cons(y,*h); 3167 return y; 3168 } 3169 3170 // (id 'num ['num]) -> sym 3171 // (id 'sym [NIL]) -> num 3172 // (id 'sym T) -> (num . num) 3173 any doId(any ex) { 3174 any x, y; 3175 adr n; 3176 cell c1; 3177 3178 x = cdr(ex); 3179 if (isNum(y = EVAL(car(x)))) { 3180 x = cdr(x); 3181 if (isNil(x = EVAL(car(x)))) { 3182 F = 0; 3183 return mkId(unBoxWord2(y)); 3184 } 3185 F = (int)unDig(y)/2 - 1; 3186 NeedNum(ex,x); 3187 return mkId(unBoxWord2(x)); 3188 } 3189 NeedExt(ex,y); 3190 n = blk64(name(y)); 3191 x = cdr(x); 3192 if (isNil(EVAL(car(x)))) 3193 return boxWord2(n); 3194 Push(c1, boxWord2(n)); 3195 data(c1) = cons(box((F + 1) * 2), data(c1)); 3196 return Pop(c1); 3197 } 3198 3199 // (seq 'cnt|sym1) -> sym | NIL 3200 any doSeq(any ex) { 3201 any x; 3202 adr n, next; 3203 byte buf[2*BLK]; 3204 3205 x = cdr(ex); 3206 if (isNum(x = EVAL(car(x)))) { 3207 F = (int)unDig(x)/2 - 1; 3208 n = 0; 3209 } 3210 else { 3211 NeedExt(ex,x); 3212 n = blk64(name(x))*BLKSIZE; 3213 } 3214 if (F >= Files) 3215 dbfErr(ex); 3216 rdLock(); 3217 blkPeek(0, buf, 2*BLK), next = getAdr(buf+BLK); // Get Next 3218 while ((n += BLKSIZE) < next) { 3219 blkPeek(n << BlkShift[F], buf, BLK); 3220 if ((buf[0] & TAGMASK) == 1) { 3221 rwUnlock(1); 3222 return mkId(n/BLKSIZE); 3223 } 3224 } 3225 rwUnlock(1); 3226 return Nil; 3227 } 3228 3229 // (lieu 'any) -> sym | NIL 3230 any doLieu(any x) { 3231 any y; 3232 3233 x = cdr(x); 3234 if (!isSym(x = EVAL(car(x))) || !isExt(x)) 3235 return Nil; 3236 for (y = tail1(x); !isSym(y); y = cdr(cellPtr(y))); 3237 return y == At || y == At2? x : Nil; 3238 } 3239 3240 // (lock ['sym]) -> cnt | NIL 3241 any doLock(any ex) { 3242 any x; 3243 pid_t n; 3244 off_t blk; 3245 3246 x = cdr(ex); 3247 if (isNil(x = EVAL(car(x)))) 3248 F = 0, n = tryLock(0,0); 3249 else { 3250 NeedExt(ex,x); 3251 blk = blk64(name(x)); 3252 if (F >= Files) 3253 dbfErr(ex); 3254 n = tryLock(blk * BlkSize[F], 1); 3255 } 3256 return n? boxCnt(n) : Nil; 3257 } 3258 3259 int dbSize(any ex, any x) { 3260 int n; 3261 3262 db(ex,x,1); 3263 n = BLK + 1 + binSize(val(x)); 3264 for (x = tail1(x); isCell(x); x = cdr(x)) { 3265 if (isSym(car(x))) 3266 n += binSize(car(x)) + 2; 3267 else 3268 n += binSize(cdar(x)) + binSize(caar(x)); 3269 } 3270 return n; 3271 } 3272 3273 3274 void db(any ex, any s, int a) { 3275 any x, y, *p; 3276 3277 if (!isNum(x = tail1(s))) { 3278 if (a == 1) 3279 return; 3280 while (!isNum(x = cdr(x))); 3281 } 3282 p = &cdr(numCell(x)); 3283 while (isNum(*p)) 3284 p = &cdr(numCell(*p)); 3285 if (!isSym(*p)) 3286 p = &car(*p); 3287 if (*p != At3) { // not deleted 3288 if (*p == At2) { // dirty 3289 if (a == 3) { 3290 *p = At3; // deleted 3291 val(s) = Nil; 3292 tail(s) = ext(x); 3293 } 3294 } 3295 else if (isNil(*p) || a > 1) { 3296 if (a == 3) { 3297 *p = At3; // deleted 3298 val(s) = Nil; 3299 tail(s) = ext(x); 3300 } 3301 else if (*p == At) 3302 *p = At2; // loaded -> dirty 3303 else { // NIL & 1 | 2 3304 adr n; 3305 cell c[1]; 3306 3307 Push(c[0],s); 3308 n = blk64(x); 3309 if (F < Files) { 3310 rdLock(); 3311 rdBlock(n*BLKSIZE); 3312 if ((Block[0] & TAGMASK) != 1) 3313 err(ex, s, "Bad ID"); 3314 *p = a == 1? At : At2; // loaded : dirty 3315 getBin = getBlock; 3316 val(s) = binRead(0); 3317 if (!isNil(y = binRead(0))) { 3318 tail(s) = ext(x = cons(y,x)); 3319 if ((y = binRead(0)) != T) 3320 car(x) = cons(y,car(x)); 3321 while (!isNil(y = binRead(0))) { 3322 cdr(x) = cons(y,cdr(x)); 3323 if ((y = binRead(0)) != T) 3324 cadr(x) = cons(y,cadr(x)); 3325 x = cdr(x); 3326 } 3327 } 3328 rwUnlock(1); 3329 } 3330 else { 3331 if (!isCell(y = val(Ext)) || F < unBox(caar(y))) 3332 dbfErr(ex); 3333 while (isCell(cdr(y)) && F >= unBox(caadr(y))) 3334 y = cdr(y); 3335 y = apply(ex, cdar(y), NO, 1, c); // ((Obj) ..) 3336 *p = At; // loaded 3337 val(s) = car(y); 3338 if (!isCell(y = cdr(y))) 3339 tail(s) = ext(x); 3340 else { 3341 tail(s) = ext(y); 3342 while (isCell(cdr(y))) 3343 y = cdr(y); 3344 cdr(y) = x; 3345 } 3346 } 3347 drop(c[0]); 3348 } 3349 } 3350 } 3351 } 3352 3353 // (commit ['any] [exe1] [exe2]) -> flg 3354 any doCommit(any ex) { 3355 bool note; 3356 int i, extn; 3357 adr n; 3358 cell c1; 3359 any x, y, z; 3360 ptr pbSave, ppSave; 3361 byte dirty[Files], buf[PIPE_BUF]; 3362 3363 x = cdr(ex), Push(c1, EVAL(car(x))); 3364 if (!Log) 3365 ++Env.protect; 3366 wrLock(); 3367 if (Jnl) 3368 lockFile(fileno(Jnl), F_SETLKW, F_WRLCK); 3369 if (Log) { 3370 for (F = 0; F < Files; ++F) 3371 dirty[F] = 0, Fluse[F] = 0; 3372 for (i = 0; i < EHASH; ++i) { // Save objects 3373 for (x = Extern[i]; isCell(x); x = cdr(x)) { 3374 for (y = tail1(car(x)); isCell(y); y = cdr(y)); 3375 z = numCell(y); 3376 while (isNum(cdr(z))) 3377 z = numCell(cdr(z)); 3378 if (cdr(z) == At2 || cdr(z) == At3) { // dirty or deleted 3379 n = blk64(y); 3380 if (F < Files) { 3381 rdBlock(n*BLKSIZE); 3382 while (logBlock(), BlkLink) 3383 rdBlock(BlkLink); 3384 dirty[F] = 1; 3385 if (cdr(z) != At3) 3386 ++Fluse[F]; 3387 } 3388 } 3389 } 3390 } 3391 for (F = 0; F < Files; ++F) { 3392 if (i = Fluse[F]) { 3393 rdBlock(0); // Save Block 0 3394 while (logBlock(), BlkLink && --i >= 0) // and free list 3395 rdBlock(BlkLink); 3396 } 3397 } 3398 putc_unlocked(0xFF, Log), putc_unlocked(0xFF, Log); 3399 fflush(Log); 3400 if (fsync(fileno(Log)) < 0) 3401 fsyncErr(ex, "Transaction"); 3402 } 3403 x = cddr(ex), EVAL(car(x)); 3404 if (data(c1) == T) 3405 note = NO, extn = EXTERN64; // Undocumented 64-bit DB export 3406 else { 3407 extn = 0; 3408 if (note = !isNil(data(c1)) && (Tell || Children)) 3409 tellBeg(&pbSave, &ppSave, buf), prTell(data(c1)); 3410 } 3411 for (i = 0; i < EHASH; ++i) { 3412 for (x = Extern[i]; isCell(x); x = cdr(x)) { 3413 for (y = tail1(car(x)); isCell(y); y = cdr(y)); 3414 z = numCell(y); 3415 while (isNum(cdr(z))) 3416 z = numCell(cdr(z)); 3417 if (cdr(z) == At2) { // dirty 3418 n = blk64(y); 3419 if (F < Files) { 3420 rdBlock(n*BLKSIZE); 3421 Block[0] |= 1; // Might be new 3422 putBin = putBlock; 3423 binPrint(extn, val(y = car(x))); 3424 for (y = tail1(y); isCell(y); y = cdr(y)) { 3425 if (isCell(car(y))) { 3426 if (!isNil(cdar(y))) 3427 binPrint(extn, cdar(y)), binPrint(extn, caar(y)); 3428 } 3429 else { 3430 if (!isNil(car(y))) 3431 binPrint(extn, car(y)), binPrint(extn, T); 3432 } 3433 } 3434 putBlock(NIX); 3435 setAdr(Block[0] & TAGMASK, Block); // Clear Link 3436 wrBlock(); 3437 if (BlkLink) 3438 cleanUp(BlkLink); 3439 cdr(z) = At; // loaded 3440 if (note) { 3441 if (PipePtr >= PipeBuf + PIPE_BUF - 12) { // EXTERN <2+1+7> END 3442 tellEnd(&pbSave, &ppSave, 0); 3443 tellBeg(&pbSave, &ppSave, buf), prTell(data(c1)); 3444 } 3445 prTell(car(x)); 3446 } 3447 } 3448 } 3449 else if (cdr(z) == At3) { // deleted 3450 n = blk64(y); 3451 if (F < Files) { 3452 cleanUp(n*BLKSIZE); 3453 if (note) { 3454 if (PipePtr >= PipeBuf + PIPE_BUF - 12) { // EXTERN <2+1+7> END 3455 tellEnd(&pbSave, &ppSave, 0); 3456 tellBeg(&pbSave, &ppSave, buf), prTell(data(c1)); 3457 } 3458 prTell(car(x)); 3459 } 3460 } 3461 cdr(z) = Nil; 3462 } 3463 } 3464 } 3465 if (note) 3466 tellEnd(&pbSave, &ppSave, 0); 3467 x = cdddr(ex), EVAL(car(x)); 3468 if (Jnl) 3469 fflush(Jnl), lockFile(fileno(Jnl), F_SETLK, F_UNLCK); 3470 if (isCell(x = val(Zap))) { 3471 outFile f, *oSave; 3472 char nm[pathSize(y = cdr(x))]; 3473 3474 pathString(y, nm); 3475 if ((f.fd = open(nm, O_APPEND|O_CREAT|O_WRONLY, 0666)) < 0) 3476 openErr(ex, nm); 3477 f.ix = 0; 3478 f.tty = NO; 3479 putBin = putStdout; 3480 oSave = OutFile, OutFile = &f; 3481 for (y = car(x); isCell(y); y = cdr(y)) 3482 binPrint(0, car(y)); 3483 flush(&f); 3484 close(f.fd); 3485 car(x) = Nil; 3486 OutFile = oSave; 3487 } 3488 if (Log) { 3489 for (F = 0; F < Files; ++F) 3490 if (dirty[F] && fsync(BlkFile[F]) < 0) 3491 fsyncErr(ex, "DB"); 3492 fseek(Log, 0L, SEEK_SET); 3493 if (ftruncate(fileno(Log), 0)) 3494 truncErr(ex); 3495 } 3496 rwUnlock(0); // Unlock all 3497 unsync(); 3498 if (!Log) 3499 --Env.protect; 3500 for (F = 0; F < Files; ++F) 3501 Fluse[F] = -1; 3502 drop(c1); 3503 return T; 3504 } 3505 3506 // (rollback) -> T 3507 any doRollback(any x) { 3508 int i; 3509 any y, z; 3510 3511 for (i = 0; i < EHASH; ++i) { 3512 for (x = Extern[i]; isCell(x); x = cdr(x)) { 3513 val(y = car(x)) = Nil; 3514 for (z = tail1(y); isCell(z); z = cdr(z)); 3515 tail(y) = ext(z); 3516 z = numCell(z); 3517 while (isNum(cdr(z))) 3518 z = numCell(cdr(z)); 3519 cdr(z) = Nil; 3520 } 3521 } 3522 if (isCell(x = val(Zap))) 3523 car(x) = Nil; 3524 rwUnlock(0); // Unlock all 3525 unsync(); 3526 return T; 3527 } 3528 3529 // (mark 'sym|0 ['NIL | 'T | '0]) -> flg 3530 any doMark(any ex) { 3531 any x, y; 3532 adr n, m; 3533 int b; 3534 byte *p; 3535 3536 x = cdr(ex); 3537 if (isNum(y = EVAL(car(x)))) { 3538 if (Marks) { 3539 for (F = 0; F < Files; ++F) 3540 free(Mark[F]); 3541 free(Mark), Mark = NULL, free(Marks), Marks = NULL; 3542 } 3543 return Nil; 3544 } 3545 NeedExt(ex,y); 3546 n = blk64(name(y)); 3547 if (F >= Files) 3548 dbfErr(ex); 3549 if (!Marks) { 3550 Marks = alloc(Marks, Files * sizeof(adr)); 3551 memset(Marks, 0, Files * sizeof(adr)); 3552 Mark = alloc(Mark, Files * sizeof(byte*)); 3553 memset(Mark, 0, Files * sizeof(byte*)); 3554 } 3555 b = 1 << (n & 7); 3556 if ((n >>= 3) >= Marks[F]) { 3557 m = Marks[F], Marks[F] = n + 1; 3558 Mark[F] = alloc(Mark[F], Marks[F]); 3559 memset(Mark[F] + m, 0, Marks[F] - m); 3560 } 3561 p = Mark[F] + n; 3562 x = cdr(x); 3563 y = *p & b? T : Nil; // Old value 3564 if (!isNil(x = EVAL(car(x)))) { 3565 if (isNum(x)) 3566 *p &= ~b; // Clear mark 3567 else 3568 *p |= b; // Set mark 3569 } 3570 return y; 3571 } 3572 3573 // (free 'cnt) -> (sym . lst) 3574 any doFree(any x) { 3575 byte buf[2*BLK]; 3576 cell c1; 3577 3578 if ((F = (int)evCnt(x, cdr(x)) - 1) >= Files) 3579 dbfErr(x); 3580 rdLock(); 3581 blkPeek(0, buf, 2*BLK); // Get Free, Next 3582 Push(c1, x = cons(mkId(getAdr(buf+BLK)/BLKSIZE), Nil)); // Next 3583 BlkLink = getAdr(buf); // Free 3584 while (BlkLink) { 3585 x = cdr(x) = cons(mkId(BlkLink/BLKSIZE), Nil); 3586 rdBlock(BlkLink); 3587 } 3588 rwUnlock(1); 3589 return Pop(c1); 3590 } 3591 3592 // (dbck ['cnt] 'flg) -> any 3593 any doDbck(any ex) { 3594 any x, y; 3595 bool flg; 3596 int i; 3597 FILE *jnl = Jnl; 3598 adr next, p, cnt; 3599 word2 blks, syms; 3600 byte buf[2*BLK]; 3601 cell c1; 3602 3603 F = 0; 3604 x = cdr(ex); 3605 if (isNum(y = EVAL(car(x)))) { 3606 if ((F = (int)unDig(y)/2 - 1) >= Files) 3607 dbfErr(ex); 3608 x = cdr(x), y = EVAL(car(x)); 3609 } 3610 flg = !isNil(y); 3611 cnt = BLKSIZE; 3612 blks = syms = 0; 3613 ++Env.protect; 3614 wrLock(); 3615 if (Jnl) 3616 lockFile(fileno(Jnl), F_SETLKW, F_WRLCK); 3617 blkPeek(0, buf, 2*BLK); // Get Free, Next 3618 BlkLink = getAdr(buf); 3619 next = getAdr(buf+BLK); 3620 Jnl = NULL; 3621 while (BlkLink) { // Check free list 3622 rdBlock(BlkLink); 3623 if ((cnt += BLKSIZE) > next) { 3624 x = mkStr("Circular free list"); 3625 goto done; 3626 } 3627 Block[0] |= TAGMASK, wrBlock(); // Mark free list 3628 } 3629 Jnl = jnl; 3630 for (p = BLKSIZE; p != next; p += BLKSIZE) { // Check all chains 3631 if (rdBlock(p), (Block[0] & TAGMASK) == 0) { 3632 cnt += BLKSIZE; 3633 memcpy(Block, buf, BLK); // Insert into free list 3634 wrBlock(); 3635 setAdr(p, buf), blkPoke(0, buf, BLK); 3636 } 3637 else if ((Block[0] & TAGMASK) == 1) { 3638 ++blks, ++syms; 3639 cnt += BLKSIZE; 3640 for (i = 2; BlkLink; cnt += BLKSIZE) { 3641 ++blks; 3642 rdBlock(BlkLink); 3643 if ((Block[0] & TAGMASK) != i) { 3644 x = mkStr("Bad chain"); 3645 goto done; 3646 } 3647 if (i < TAGMASK) 3648 ++i; 3649 } 3650 } 3651 } 3652 BlkLink = getAdr(buf); // Unmark free list 3653 Jnl = NULL; 3654 while (BlkLink) { 3655 rdBlock(BlkLink); 3656 if (Block[0] & TAGMASK) 3657 Block[0] &= BLKMASK, wrBlock(); 3658 } 3659 if (cnt != next) 3660 x = mkStr("Bad count"); 3661 else if (!flg) 3662 x = Nil; 3663 else { 3664 Push(c1, boxWord2(syms)); 3665 data(c1) = cons(boxWord2(blks), data(c1)); 3666 x = Pop(c1); 3667 } 3668 done: 3669 if (Jnl = jnl) 3670 fflush(Jnl), lockFile(fileno(Jnl), F_SETLK, F_UNLCK); 3671 rwUnlock(1); 3672 --Env.protect; 3673 return x; 3674 }