|
|
1.1 ! root 1: /* ! 2: * system.c ! 3: * ! 4: * Routines specific for non-MSDOS implementations of pgp. ! 5: * ! 6: * Modified 24-Jun-92 HAJK ! 7: * Adapt for VAX/VMS. ! 8: */ ! 9: #include <stdio.h> ! 10: #include "pgp.h" ! 11: ! 12: ! 13: /*===========================================================================*/ ! 14: /* ! 15: * UNIX ! 16: */ ! 17: ! 18: #ifdef UNIX ! 19: /* ! 20: * Define USE_SELECT to use the select() system call to check if ! 21: * keyboard input is available. Define USE_NBIO to use non-blocking ! 22: * read(). If you don't define anything the FIONREAD ioctl() command ! 23: * will be used. ! 24: * ! 25: * Define NOTERMIO if you don't have the termios stuff ! 26: */ ! 27: #include <sys/types.h> ! 28: #include <fcntl.h> ! 29: #ifndef NeXT ! 30: #include <unistd.h> ! 31: #endif ! 32: ! 33: #ifndef NOTERMIO ! 34: #ifndef M_XENIX ! 35: #include <termios.h> ! 36: #else ! 37: #include <termio.h> ! 38: #endif /* not M_XENIX */ ! 39: #else ! 40: #include <sgtty.h> ! 41: #endif ! 42: ! 43: #ifdef USE_SELECT ! 44: #include <sys/time.h> ! 45: #else ! 46: #ifndef USE_NBIO ! 47: #include <sys/ioctl.h> /* for FIONREAD */ ! 48: #ifndef FIONREAD ! 49: #define FIONREAD TIOCINQ ! 50: #endif ! 51: #endif ! 52: #endif ! 53: #include <signal.h> ! 54: ! 55: static void setsigs(void); ! 56: static void rmsigs(void); ! 57: static void sig1(int); ! 58: static void sig2(int); ! 59: void breakHandler(int); ! 60: static int ttyfd= -1; ! 61: #ifndef M_XENIX ! 62: static void (*savesig)(int); ! 63: #else ! 64: static int (*savesig)(int); ! 65: #endif ! 66: ! 67: void ttycbreak(void); ! 68: void ttynorm(void); ! 69: ! 70: ! 71: #ifndef NOTERMIO ! 72: #ifndef M_XENIX ! 73: static struct termios itio, tio; ! 74: #else ! 75: static struct termio itio, tio; ! 76: #endif /* not M_XENIX */ ! 77: #else ! 78: static struct sgttyb isg, sg; ! 79: #endif ! 80: ! 81: #ifdef USE_NBIO ! 82: static int kbuf= -1; /* buffer to store char read by kbhit() */ ! 83: static int fflags; ! 84: #endif ! 85: ! 86: static int gottio = 0; ! 87: ! 88: void ttycbreak(void) ! 89: { ! 90: if (ttyfd == -1) { ! 91: if ((ttyfd = open("/dev/tty", O_RDWR)) < 0) { ! 92: fprintf(stderr, "cannot open tty, using stdin\n"); ! 93: ttyfd = 0; ! 94: } ! 95: } ! 96: #ifndef NOTERMIO ! 97: #ifndef M_XENIX ! 98: if (tcgetattr(ttyfd, &tio) < 0) ! 99: #else ! 100: if (ioctl(ttyfd, TCGETA, &tio) < 0) ! 101: #endif /* not M_XENIX */ ! 102: { fprintf (stderr, "\nUnable to get terminal characteristics: "); ! 103: perror("ioctl"); ! 104: exitPGP(1); ! 105: } ! 106: itio = tio; ! 107: setsigs(); ! 108: gottio = 1; ! 109: #ifdef USE_NBIO ! 110: tio.c_cc[VMIN] = 0; ! 111: #else ! 112: tio.c_cc[VMIN] = 1; ! 113: #endif ! 114: tio.c_cc[VTIME] = 0; ! 115: tio.c_lflag &= ~(ECHO|ICANON); ! 116: #ifndef M_XENIX ! 117: tcsetattr (ttyfd, TCSAFLUSH, &tio); ! 118: #else ! 119: ioctl(ttyfd, TCSETAW, &tio); ! 120: #endif /* not M_XENIX */ ! 121: #else ! 122: if (ioctl(ttyfd, TIOCGETP, &sg) < 0) ! 123: { fprintf (stderr, "\nUnable to get terminal characteristics: "); ! 124: perror("ioctl"); ! 125: exitPGP(1); ! 126: } ! 127: isg = sg; ! 128: setsigs(); ! 129: gottio = 1; ! 130: sg.sg_flags |= CBREAK; ! 131: sg.sg_flags &= ~ECHO; ! 132: ioctl(ttyfd, TIOCSETP, &sg); ! 133: #endif /* !NOTERMIO */ ! 134: #ifdef USE_NBIO ! 135: if ((fflags = fcntl(ttyfd, F_GETFL)) != -1) ! 136: fcntl(ttyfd, F_SETFL, fflags|O_NDELAY); ! 137: #endif ! 138: } ! 139: ! 140: ! 141: void ttynorm(void) ! 142: { gottio = 0; ! 143: #ifdef USE_NBIO ! 144: if (fcntl(ttyfd, F_SETFL, fflags) == -1) ! 145: perror("fcntl"); ! 146: #endif ! 147: #ifndef NOTERMIO ! 148: #ifndef M_XENIX ! 149: tcsetattr (ttyfd, TCSAFLUSH, &itio); ! 150: #else ! 151: ioctl(ttyfd, TCSETAW, &itio); ! 152: #endif /* not M_XENIX */ ! 153: #else ! 154: ioctl(ttyfd, TIOCSETP, &isg); ! 155: #endif ! 156: rmsigs(); ! 157: } ! 158: ! 159: static void sig1 (int sig) ! 160: { ! 161: #ifndef NOTERMIO ! 162: #ifndef M_XENIX ! 163: tcsetattr (ttyfd, TCSANOW, &itio); ! 164: #else ! 165: ioctl(ttyfd, TCSETAW, &itio); ! 166: #endif /* not M_XENIX */ ! 167: #else ! 168: ioctl(ttyfd, TIOCSETP, &isg); ! 169: #endif ! 170: signal (sig, SIG_DFL); ! 171: if (sig == SIGINT) ! 172: breakHandler(SIGINT); ! 173: kill (getpid(), sig); ! 174: } ! 175: ! 176: static void sig2 (int sig) ! 177: { if (gottio) ! 178: ttycbreak(); ! 179: else ! 180: setsigs(); ! 181: } ! 182: ! 183: static void setsigs(void) ! 184: { savesig = signal (SIGINT, sig1); ! 185: #ifdef SIGTSTP ! 186: signal (SIGCONT, sig2); ! 187: signal (SIGTSTP, sig1); ! 188: #endif ! 189: } ! 190: ! 191: static void rmsigs(void) ! 192: { signal (SIGINT, savesig); ! 193: #ifdef SIGTSTP ! 194: signal (SIGCONT, SIG_DFL); ! 195: signal (SIGTSTP, SIG_DFL); ! 196: #endif ! 197: } ! 198: ! 199: #ifndef CRUDE ! 200: int kbhit(void) ! 201: /* Return TRUE if there is a key to be read */ ! 202: { ! 203: #ifdef USE_SELECT /* use select() system call */ ! 204: struct timeval t; ! 205: fd_set n; ! 206: int r; ! 207: ! 208: timerclear(&t); ! 209: FD_ZERO(&n); ! 210: FD_SET(ttyfd, &n); ! 211: r = select(32, &n, NULL, NULL, &t); ! 212: if (r == -1) { ! 213: perror("select"); ! 214: exitPGP(1); ! 215: } ! 216: return r > 0; ! 217: #else ! 218: #ifdef USE_NBIO /* use non-blocking read() */ ! 219: unsigned char ch; ! 220: if (kbuf >= 0) ! 221: return(1); ! 222: if (read(ttyfd, &ch, 1) == 1) { ! 223: kbuf = ch; ! 224: return(1); ! 225: } ! 226: return(0); ! 227: #else ! 228: long lf; ! 229: if (ioctl(ttyfd, FIONREAD, &lf) == -1) { ! 230: perror("ioctl: FIONREAD"); ! 231: exitPGP(1); ! 232: } ! 233: return(lf); ! 234: #endif ! 235: #endif ! 236: } ! 237: #endif /* !CRUDE */ ! 238: ! 239: int getch(void) ! 240: { ! 241: char c; ! 242: #ifdef USE_NBIO ! 243: while (!kbhit()); /* kbhit() does the reading */ ! 244: c = kbuf; ! 245: kbuf = -1; ! 246: #else ! 247: read(ttyfd, &c, 1); ! 248: #endif ! 249: return(c); ! 250: } ! 251: ! 252: #ifdef BSD_OLD ! 253: void *memset(s, c, n) ! 254: void *s; ! 255: register int c, n; ! 256: { ! 257: register char *p = s; ! 258: ++n; ! 259: while (--n) ! 260: *p++ = c; ! 261: return(s); ! 262: } ! 263: int memcmp(s1, s2, n) ! 264: register unsigned char *s1, *s2; ! 265: register int n; ! 266: { ! 267: if (!n) ! 268: return(0); ! 269: while (--n && *s1 == *s2) { ! 270: ++s1; ! 271: ++s2; ! 272: } ! 273: return(*s1 - *s2); ! 274: } ! 275: void *memcpy(s1, s2, n) ! 276: register char *s1, *s2; ! 277: register int n; ! 278: { ! 279: char *p = s1; ! 280: ++n; ! 281: while (--n) ! 282: *s1++ = *s2++; ! 283: return(p); ! 284: } ! 285: #endif /* BSD_OLD */ ! 286: ! 287: #ifdef M_XENIX /* XENIX/286 specific stuff */ ! 288: int remove(name) ! 289: char *name; ! 290: { ! 291: return unlink(name); ! 292: } ! 293: ! 294: int rename(old, new) ! 295: register char *old, *new; ! 296: { ! 297: (void) unlink(new); ! 298: if (link(old, new) < 0) ! 299: return -1; ! 300: if (unlink(old) < 0) { ! 301: (void) unlink(new); ! 302: return -1; ! 303: } ! 304: return 0; ! 305: } ! 306: #endif /* M_XENIX */ ! 307: #endif /* UNIX */ ! 308: ! 309: ! 310: ! 311: /*===========================================================================*/ ! 312: /* ! 313: * VMS ! 314: */ ! 315: ! 316: #ifdef VMS /* kbhit()/getch() equivalent */ ! 317: ! 318: /* ! 319: * This code defines an equivalent version of kbhit() and getch() for ! 320: * use under VAX/VMS, together with an exit handler to reset terminal ! 321: * characteristics. ! 322: * ! 323: * This code assumes that kbhit() has been invoked to test that there ! 324: * are characters in the typeahead buffer before getch() is invoked to ! 325: * get the answer. ! 326: */ ! 327: ! 328: #include <descrip.h> ! 329: #include <iodef.h> ! 330: #ifdef VAXC ! 331: #include <ttdef.h> ! 332: #include <tt2def.h> ! 333: #include <dcdef.h> ! 334: #else /* Probably GNU */ ! 335: #include <vms/$ttdef.h> ! 336: #include <vms/$tt2def.h> ! 337: #include <vms/$dcdef.h> ! 338: #endif /* VAXC */ ! 339: static volatile short _kbhitChan_ = 0; ! 340: ! 341: static volatile struct IOSB { ! 342: unsigned short sts; ! 343: unsigned short byteCount; ! 344: unsigned short terminator; ! 345: unsigned short terminatorSize; ! 346: } iosb; ! 347: static $DESCRIPTOR (kbdev_desc, "SYS$COMMAND:"); ! 348: ! 349: static volatile struct { ! 350: char Class; ! 351: char Type; ! 352: unsigned short BufferSize; ! 353: unsigned int Mode; ! 354: int ExtChar; ! 355: } CharBuf, OldCharBuf; ! 356: ! 357: void kbhit_handler(int *sts) ! 358: { ! 359: int mysts; ! 360: ! 361: CharBuf.Mode = OldCharBuf.Mode; ! 362: CharBuf.ExtChar = OldCharBuf.ExtChar; ! 363: CharBuf.Mode &= ~TT$M_NOECHO; ! 364: CharBuf.ExtChar &= ~TT2$M_PASTHRU; ! 365: if ((mysts = sys$qiow ( ! 366: 0, ! 367: _kbhitChan_, ! 368: IO$_SETMODE, ! 369: &iosb, ! 370: 0, ! 371: 0, ! 372: &CharBuf, ! 373: 12, ! 374: 0, ! 375: 0, ! 376: 0, ! 377: 0)) & 01) mysts = iosb.sts; ! 378: (void) sys$dassgn ( ! 379: _kbhitChan_); ! 380: _kbhitChan_ = 0; ! 381: if (!(mysts & 01)) { ! 382: fprintf(stderr,"\nFailed to reset terminal characteristics!"); ! 383: (void) lib$signal(mysts); ! 384: } ! 385: } ! 386: ! 387: unsigned int exsts; ! 388: ! 389: static struct { ! 390: int link; ! 391: void *rtn; ! 392: int argcnt; ! 393: int *stsaddr; ! 394: } exhblk = { 0, &(kbhit_handler), 1, &(exsts)}; ! 395: ! 396: int kbhit() ! 397: { ! 398: int sts = 1; ! 399: ! 400: struct { ! 401: unsigned short TypAhdCnt; ! 402: char FirstChar; ! 403: char Reserved[5]; ! 404: } TypCharBuf; ! 405: ! 406: if (_kbhitChan_ == 0) { ! 407: if ((sts = sys$assign ( ! 408: &kbdev_desc, ! 409: &_kbhitChan_, ! 410: 0, ! 411: 0)) & 1 == 0) lib$stop(sts); ! 412: if ((sts = sys$qiow ( ! 413: 0, ! 414: _kbhitChan_, ! 415: IO$_SENSEMODE, ! 416: &iosb, ! 417: 0, ! 418: 0, ! 419: &CharBuf, ! 420: 12, ! 421: 0, ! 422: 0, ! 423: 0, ! 424: 0)) & 01) sts = iosb.sts; ! 425: if (sts & 01) { ! 426: if (!(CharBuf.Class & DC$_TERM)) { ! 427: fprintf(stderr,"\nNot running on a terminal"); ! 428: exitPGP(1); ! 429: } else { ! 430: OldCharBuf.Mode = CharBuf.Mode; ! 431: OldCharBuf.ExtChar = CharBuf.ExtChar; ! 432: CharBuf.Mode |= TT$M_NOECHO; ! 433: CharBuf.ExtChar |= TT2$M_PASTHRU; ! 434: if ((sts = sys$qiow ( ! 435: 0, ! 436: _kbhitChan_, ! 437: IO$_SETMODE, ! 438: &iosb, ! 439: 0, ! 440: 0, ! 441: &CharBuf, ! 442: 12, ! 443: 0, ! 444: 0, ! 445: 0, ! 446: 0)) & 01) sts = iosb.sts; ! 447: if (sts & 01) { ! 448: /* ! 449: ** Declare Exit Handler ! 450: */ ! 451: (void) sys$dclexh (&exhblk); ! 452: } else { ! 453: fprintf(stderr,"\nFailed to set terminal characteristics!"); ! 454: (void) lib$signal(sts); ! 455: exitPGP(1); ! 456: } ! 457: } ! 458: } ! 459: } ! 460: /* ! 461: ** Get typeahead count ! 462: */ ! 463: if ((sts = sys$qiow ( ! 464: 0, ! 465: _kbhitChan_, ! 466: IO$_SENSEMODE | IO$M_TYPEAHDCNT, ! 467: &iosb, ! 468: 0, ! 469: 0, ! 470: &TypCharBuf, ! 471: 8, ! 472: 0, ! 473: 0, ! 474: 0, ! 475: 0)) & 01) sts = iosb.sts; ! 476: if (sts & 01) return(TypCharBuf.TypAhdCnt>0); ! 477: (void) lib$signal(sts); ! 478: exitPGP(1); ! 479: } ! 480: ! 481: int getch() ! 482: { ! 483: unsigned int sts; ! 484: volatile char CharBuf; ! 485: ! 486: if ((sts = sys$qiow ( ! 487: 0, ! 488: _kbhitChan_, ! 489: IO$_READVBLK, ! 490: &iosb, ! 491: 0, ! 492: 0, ! 493: &CharBuf, ! 494: 1, ! 495: 0, ! 496: 0, ! 497: 0, ! 498: 0)) & 01) sts = iosb.sts; ! 499: if (sts & 01) return ((int) CharBuf); ! 500: fprintf(stderr,"\nFailed to get character"); ! 501: (void) lib$signal(sts); ! 502: } ! 503: ! 504: ttynorm() ! 505: { ! 506: int sts; ! 507: ! 508: if (_kbhitChan_ != 0) { ! 509: (void) SYS$CANEXH(&exhblk); ! 510: kbhit_handler(&sts); ! 511: } ! 512: } ! 513: ! 514: void ttycbreak () ! 515: { ! 516: ttynorm(); ! 517: } ! 518: ! 519: unsigned long vms_clock_bits[2]; /* VMS Hardware Clock */ ! 520: const long vms_ticks_per_update = 100000L; /* Clock update int. */ ! 521: ! 522: #endif /* VMS */ ! 523: ! 524: ! 525: ! 526: /*========================================================================*/ ! 527: /* ! 528: * AMIGA ! 529: */ ! 530: ! 531: #ifdef AMIGA /* Amiga-specific stuff */ ! 532: ! 533: #include <exec/types.h> ! 534: #include <exec/memory.h> ! 535: #include <exec/ports.h> ! 536: #include <libraries/dosextens.h> ! 537: #ifdef LATTICE ! 538: #include <proto/exec.h> ! 539: #include <proto/dos.h> ! 540: #endif /* LATTICE */ ! 541: extern FILE *pgpout; ! 542: extern int aecho; ! 543: ! 544: FILE *tmpfile() ! 545: { ! 546: FILE *fp; ! 547: ! 548: if ((fp=fopen("tmpfile.tmp","w+b"))==NULL) { ! 549: perror("tmpfile.tmp"); ! 550: return(NULL); ! 551: } ! 552: return(fp); ! 553: ! 554: } ! 555: ! 556: /* amiga version of getch() ! 557: Cor Bosman , jul-22-92 ! 558: */ ! 559: ! 560: ! 561: sendpacket(struct MsgPort *rec,LONG action,LONG arg1) ! 562: { ! 563: struct StandardPacket *pkt; ! 564: struct msgPort *rp; ! 565: LONG res1 = 0L; ! 566: ! 567: if (rp = (struct MsgPort *)CreatePort(NULL,0L)) { ! 568: if (pkt = (struct StandardPacket *)\ ! 569: AllocMem(sizeof(struct StandardPacket),MEMF_PUBLIC|MEMF_CLEAR)) { ! 570: pkt->sp_Msg.mn_Node.ln_Name = (BYTE *)&pkt->sp_Pkt; ! 571: pkt->sp_Pkt.dp_Link = &pkt->sp_Msg; ! 572: pkt->sp_Pkt.dp_Port = rp; ! 573: pkt->sp_Pkt.dp_Type = action; ! 574: pkt->sp_Pkt.dp_Arg1 = arg1; ! 575: PutMsg(rec,&pkt->sp_Msg); ! 576: WaitPort(rp); ! 577: GetMsg(rp); ! 578: res1 = pkt->sp_Pkt.dp_Res1; ! 579: FreeMem((UBYTE*)pkt,sizeof(struct StandardPacket)); ! 580: } ! 581: DeletePort(rp); ! 582: } ! 583: return(res1); ! 584: ! 585: } ! 586: ! 587: /* ttycbreak for amiga. ! 588: * Cor Bosman , jul-30-92 ! 589: */ ! 590: ! 591: void ttycbreak() ! 592: { ! 593: BPTR in,out; ! 594: char buf[128]; ! 595: struct MsgPort *ch; ! 596: ! 597: in=Input(); ! 598: out=Output(); ! 599: ch = ((struct FileHandle *)BADDR(in))->fh_Type; ! 600: sendpacket(ch,ACTION_SCREEN_MODE,-1L); ! 601: } ! 602: ! 603: /* ttynorm for amiga ! 604: * Cor Bosman , jul-30-92 ! 605: */ ! 606: ! 607: void ttynorm() ! 608: { ! 609: ! 610: BPTR in,out; ! 611: char buf[128]; ! 612: struct MsgPort *ch; ! 613: ! 614: in=Input(); ! 615: out=Output(); ! 616: ch = ((struct FileHandle *)BADDR(in))->fh_Type; ! 617: sendpacket(ch,ACTION_SCREEN_MODE,0L); ! 618: } ! 619: ! 620: char getch(void) ! 621: { ! 622: char buf[128]; ! 623: BPTR in,out; ! 624: ! 625: in = Input(); ! 626: out = Output(); ! 627: Read(in,buf,1); ! 628: if (aecho) ! 629: Write(out, buf, 1); ! 630: return(buf[0]); ! 631: } ! 632: ! 633: /* kbhit() function for amiga. ! 634: * Cor Bosman , jul-30-92 ! 635: */ ! 636: ! 637: int kbhit() ! 638: { ! 639: if(WaitForChar(Input(), 1)) return 1; ! 640: return 0; ! 641: } ! 642: ! 643: #ifdef LATTICE ! 644: ! 645: /* ! 646: * Lattice-C ^C-Handler ! 647: */ ! 648: ! 649: int CXBRK() ! 650: { ! 651: BPTR in,out; ! 652: struct MsgPort *ch; ! 653: in=Input(); ! 654: out=Output(); ! 655: ! 656: /* it might happen we catch a ^C while in cbreak mode. ! 657: * so always set the screen to the normal mode. ! 658: */ ! 659: ! 660: ch = ((struct FileHandle *)BADDR(in))->fh_Type; ! 661: sendpacket(ch, ACTION_SCREEN_MODE, 0L); ! 662: ! 663: ! 664: fprintf(pgpout, "\n*** Program Aborted.\n"); ! 665: exitPGP(6); /* INTERRUPT */ ! 666: } ! 667: #endif ! 668: ! 669: #endif /* AMIGA */ ! 670: ! 671: ! 672: ! 673: /*===========================================================================*/ ! 674: /* ! 675: * other stuff for non-MSDOS systems ! 676: */ ! 677: ! 678: #ifdef ATARI ! 679: #include <string.h> ! 680: #endif ! 681: ! 682: #if !defined(MSDOS) && !defined(ATARI) ! 683: #include <ctype.h> ! 684: char *strlwr(char *s) ! 685: { /* ! 686: ** Turns string s into lower case. ! 687: */ ! 688: int c; ! 689: char *p = s; ! 690: while (c = *p) ! 691: *p++ = tolower(c); ! 692: return(s); ! 693: } ! 694: #endif /* !MSDOS && !ATARI */ ! 695: ! 696: ! 697: #ifdef strstr ! 698: #undef strstr ! 699: /* Not implemented on some systems - return first instance of s2 in s1 */ ! 700: char *mystrstr (char *s1, char *s2) ! 701: { int i; ! 702: char *strchr(); ! 703: ! 704: if (!s2 || !*s2) ! 705: return s1; ! 706: for ( ; ; ) ! 707: { if (!(s1 = strchr (s1, *s2))) ! 708: return s1; ! 709: for (i=1; s2[i] && (s1[i]==s2[i]); ++i) ! 710: ; ! 711: if (!s2[i]) ! 712: return s1; ! 713: ++s1; ! 714: } ! 715: } ! 716: #endif /* strstr */ ! 717: ! 718: ! 719: #ifdef fopen ! 720: #undef fopen ! 721: ! 722: #ifdef ATARI ! 723: #define F_BUF_SIZE 8192 /* seems to be a good value ... */ ! 724: ! 725: FILE *myfopen(const char *filename, const char *mode) ! 726: /* Open streams with larger buffer to increase disk I/O speed. */ ! 727: /* Adjust F_BUF_SIZE to change buffer size. */ ! 728: { ! 729: FILE *f; ! 730: ! 731: if ( (f = fopen(filename, mode)) != NULL ) ! 732: if (setvbuf(f, NULL, _IOFBF, F_BUF_SIZE)) /* no memory? */ ! 733: { ! 734: fclose(f); /* then close it again */ ! 735: f = fopen(filename, mode); /* and try again in normal mode */ ! 736: } ! 737: return(f); /* return either handle or NULL */ ! 738: } ! 739: ! 740: #else /* ATARI */ ! 741: ! 742: /* Remove "b" from 2nd arg */ ! 743: FILE *myfopen(char *filename, char *type) ! 744: { char buf[10]; ! 745: ! 746: buf[0] = *type++; ! 747: if (*type=='b') ! 748: ++type; ! 749: strcpy(buf+1,type); ! 750: return fopen(filename, buf); ! 751: } ! 752: #endif /* not ATARI */ ! 753: #endif /* fopen */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.