|
|
1.1 ! root 1: /* ! 2: ********************************************************************** ! 3: * * ! 4: * dired [][dir][files] Stuart Cracraft (mclure@sri-unix) Sept 1980 * ! 5: * * ! 6: * Directory editor. * ! 7: * edit/delete files in a directory or a file list * ! 8: * compile with the berkeley termlib archive * ! 9: * and don't forget to change the 'helpfile' and * ! 10: * 'dirednam' strings to reflect your own setup. * ! 11: * * ! 12: * Note: if you make improvements, I'd like to get them too. * ! 13: * Stuart Cracraft mclure@sri-unix, * ! 14: * ucbvax!menlo70!sri-unix!mclure * ! 15: * so would I: Jay Lepreau lepreau@utah-20, * ! 16: * decvax!randvax!utah-cs!lepreau * ! 17: ********************************************************************** ! 18: */ ! 19: ! 20: /* ! 21: * Enhanced by J.Lepreau, Univ of Utah, 8-10/81: ! 22: * --bunch of stuff, including more cmds, 2 window mode, ! 23: * bufering output, pathname substition in !escapes, ! 24: * initial sort options, ^G escape from sorts, ^U escape ! 25: * from !escapes, empty dir check. ! 26: */ ! 27: ! 28: #ifdef COMMENT ! 29: ! 30: Modified 12/81 by Lepreau: ! 31: 1. Fix up aborting of command escapes, with mildy tricky re-display. ! 32: 2. Make default window size be half-screen (2 window mode). ! 33: Added option -wf to get full screen. ! 34: 3. Add check for too many files to avoid core-dump. ! 35: 4. Fix bug in cmd escapes: must reset caught signals before invocation. ! 36: ! 37: #endif COMMENT ! 38: ! 39: /* ! 40: things to consider ! 41: - perhaps should be able to edit protection field and ! 42: be able to change protection instantly at the ! 43: touch of a key (or queue the change for later exit) ! 44: ! 45: -- Would be nice if there were cmds (e.g. 'c') which would update ! 46: 1) the file-info & re-display it, & 2) update whole ! 47: display's info. ! 48: */ ! 49: ! 50: #ifdef vax /* this does for now */ ! 51: # define VFORK ! 52: #endif ! 53: ! 54: #ifndef VFORK ! 55: # define vfork fork ! 56: #endif ! 57: ! 58: #define FALSE 0 ! 59: #define TRUE 1 ! 60: #define NOTDELETED 0 ! 61: #define DELETED 1 ! 62: ! 63: /* Sort Orders */ ! 64: #define NAME 0 ! 65: #define SIZE 1 ! 66: #define WRITE 2 ! 67: #define READ 3 ! 68: char sortstr[] = "nswr"; /* must be in order by above */ ! 69: ! 70: #define ESC '\033' ! 71: #define CURSOR 48 /* X-coord of cursor, just b4 file */ ! 72: #define CTRL(c) ('c' & 037) ! 73: ! 74: #include <stdio.h> ! 75: #include <sys/types.h> ! 76: #include <signal.h> ! 77: #include <sgtty.h> ! 78: #include <sys/stat.h> ! 79: #include <sys/dir.h> ! 80: ! 81: #ifdef UTAH ! 82: char *dirednam = "/usr/local/dired "; /* Where dired lives - need blnk */ ! 83: char *helpfile = "/usr/help/dired.hlp"; /* Where helpfile lives */ ! 84: #else ! 85: char *dirednam = "dired "; /* Where dired lives - need blnk */ ! 86: char *helpfile = "/usr/lib/dired"; /* Where helpfile lives */ ! 87: #endif ! 88: ! 89: char divider[132]; /* divides the windows */ ! 90: #define DIVCHAR '-' /* makes up the divider */ ! 91: ! 92: #define MOREPGM "p " /* program to page thru a file */ ! 93: ! 94: #define MAXFILES 2000 /* Max number of files we can handle */ ! 95: ! 96: struct lbuf ! 97: { ! 98: union ! 99: { ! 100: char lname[30]; ! 101: char *namep; ! 102: } ln; ! 103: short deleted; ! 104: char ltype; ! 105: short lnum; ! 106: short lflags; ! 107: short lnl; ! 108: short luid; ! 109: short lgid; ! 110: long lsize; ! 111: long latime; ! 112: long lctime; ! 113: long lmtime; ! 114: }; ! 115: ! 116: struct lbuf file[MAXFILES]; ! 117: ! 118: struct stat statbuf; ! 119: struct sgttyb ioctlb; ! 120: ! 121: #define ISARG 0100000 ! 122: ! 123: int gflg, ! 124: iflg, ! 125: lflg, ! 126: sflg; /* Random flags */ ! 127: int splitflg; /* Split screen? */ ! 128: int sortyp; /* Key to sort on */ ! 129: int errcode; /* Error variable used by rm */ ! 130: int rflg = 1; /* Reverse sort flag */ ! 131: int totfiles = 0; /* Total files */ ! 132: int flags; /* Gets flags */ ! 133: int blurb; /* 1=>thing in echo area,0 otherwise */ ! 134: int numdeleted; /* Number of files marked deleted */ ! 135: long ttime; /* Temp time variable */ ! 136: long year; /* Six months ago */ ! 137: long totblocks = 0; /* Total blocks */ ! 138: int lastuid = -1; /* Last uid/gid we handled */ ! 139: char tempbuf[128]; /* Random temporary buffer */ ! 140: char userbuf[35]; /* Temporary buffer for user name */ ! 141: FILE *pwdf, *dirf; /* Password/group file and directory files */ ! 142: ! 143: int curfile = 0; /* Current file */ ! 144: int topfile = 0; /* File at top of screen */ ! 145: int curline = 0; /* Line that we're on */ ! 146: int scrlen = 999; /* Length of screen - dired part: half size */ ! 147: int Worklen = 0; /* Length of 'working window', the other part*/ ! 148: int Worktop = 0; /* Top of " " */ ! 149: int Tscrlen; /*Total length of screen,minus 1 for cmd line*/ ! 150: int scrwid = 79; /* Width of screen */ ! 151: char *CL, ! 152: *UP, /* Termcap strings we'll use */ ! 153: *HO, ! 154: *CM, ! 155: *CD, /* clear to end of display */ ! 156: *CE, ! 157: *AL; /* insert line */ ! 158: char PC; ! 159: char tbuf[1024], ! 160: tcapbuf[128]; ! 161: char *tgetstr (), *tgoto (); ! 162: char *getenv (); ! 163: int compar (); ! 164: char *ctime (); ! 165: char *index(); ! 166: char *makename (); ! 167: char *catargs(); ! 168: char *bldnam(); ! 169: long nblock (); ! 170: int catchint(); ! 171: int sigint; ! 172: ! 173: char bufout[BUFSIZ]; ! 174: ! 175: main (argc, argv) ! 176: int argc; ! 177: char *argv[]; ! 178: { ! 179: register int i, ! 180: cc; ! 181: int numarg, ! 182: special, /* flag says had % or # in cmd */ ! 183: status; ! 184: char nambuf[128]; ! 185: char command; /* Holds last command */ ! 186: char **oldargv; ! 187: register char *t, ! 188: *tt; ! 189: ! 190: /* Get terminal type */ ! 191: ! 192: getcap (); ! 193: ! 194: /* Process arg flags. They must be first! */ ! 195: for (i=1; i<argc; i++) { ! 196: if (argv[i][0] != '-') ! 197: break; ! 198: switch (cc = argv[i][1]) { ! 199: case 'w': /* Window size */ ! 200: if (argv[i][2] == 'h') ! 201: scrlen = 999; /* Half */ ! 202: else if (argv[i][2] == 'f') ! 203: scrlen = 0; /* Full */ ! 204: else ! 205: scrlen = atoi(&argv[i][2]); ! 206: break; ! 207: case 's': /* Initial sort order */ ! 208: case 'r': ! 209: sortyp = index(sortstr, argv[i][2]) - sortstr; ! 210: rflg = (cc == 's') ? 1 : -1; ! 211: break; ! 212: default: ! 213: printf("Unknown option %s, ignored.\n", argv[i]); ! 214: break; ! 215: } ! 216: } ! 217: argc -= (i - 1); ! 218: oldargv = argv; ! 219: argv += (i - 1); ! 220: ! 221: if (scrlen == 0) /* full screen */ ! 222: scrlen = Tscrlen; ! 223: else if (scrlen == 999) /* means split in half */ ! 224: scrlen = (Tscrlen - 1) >> 1; ! 225: if (scrlen < 2) ! 226: scrlen = 2; ! 227: if (Tscrlen < scrlen) ! 228: scrlen = Tscrlen; ! 229: splitflg = (Tscrlen > scrlen+1); /* 1 extra line for separator */ ! 230: if (splitflg) { ! 231: Worklen = Tscrlen - (scrlen+1); /* size of 'working' window */ ! 232: Worktop = scrlen + 1; /* bottom half for now */ ! 233: } ! 234: else ! 235: Worklen = 0; ! 236: ! 237: tt = divider + scrwid - 14; ! 238: for (t = divider; t < tt;) /* arbitrary length */ ! 239: *t++ = DIVCHAR; ! 240: *t = '\0'; ! 241: ! 242: signal (SIGINT, SIG_IGN); ! 243: signal (SIGQUIT, SIG_IGN); ! 244: ! 245: setdpy (); ! 246: printf ("Reading"); ! 247: ! 248: time (&ttime); ! 249: year = ttime - 6L * 30L * 24L * 60L * 60L;/* 6 months ago */ ! 250: ! 251: lflg = 1; ! 252: gflg = iflg = sflg = 0; ! 253: if (lflg) ! 254: { ! 255: t = "/etc/passwd"; ! 256: if (gflg) ! 257: t = "/etc/group"; ! 258: pwdf = fopen (t, "r"); ! 259: } ! 260: ! 261: ! 262: numarg = argc; ! 263: if (argc == 1) ! 264: readdir ("."); ! 265: else ! 266: if (argc == 2) ! 267: readdir (argv[1]); ! 268: else ! 269: { ! 270: while (--argc > 0) ! 271: { ! 272: if (totfiles == MAXFILES) { ! 273: overflow(); ! 274: break; ! 275: } ! 276: if ((totfiles % 10) == 0) ! 277: putchar ('.'); ! 278: if (gstat(*++argv) == 0) { ! 279: file[totfiles].ln.namep = *argv; ! 280: file[totfiles].lflags |= ISARG; ! 281: totfiles++; ! 282: } ! 283: } ! 284: } ! 285: if (totfiles == 0) { ! 286: printf("\n?Empty directory\n"); ! 287: unsetdpy(); ! 288: sleep(1); /* So user can see it - don't worry if less */ ! 289: exit(0); ! 290: } ! 291: qsort (file, totfiles, sizeof (struct lbuf), compar); ! 292: blank (); ! 293: showscreen (); ! 294: curxy (0, Tscrlen); ! 295: ceol (); ! 296: curxy (CURSOR, 0); ! 297: startup: ! 298: while ((command = readchar()) != 'q') ! 299: { ! 300: if (blurb) ! 301: { ! 302: telluser (""); ! 303: blurb = 0; ! 304: } ! 305: switch (command) ! 306: { ! 307: case 'a': /* Abort completely */ ! 308: blank (); ! 309: unsetdpy (); ! 310: exit (1); ! 311: break; ! 312: case 'P': /* Print a file */ ! 313: case 'p': /* do a pr2 */ ! 314: if (file[curfile].ltype == 'd') ! 315: { ! 316: telluser ("?Can only print files"); ! 317: break; ! 318: } ! 319: tempbuf[0] = '\0'; ! 320: bldnam(tempbuf, numarg, curfile, argv); ! 321: telluser ("Printing..."); ! 322: while ((i = vfork ()) == -1) ! 323: sleep (3); ! 324: if (i == 0) { ! 325: #ifdef UTAH ! 326: if (command == 'p') /* little print */ ! 327: execlp("pr2", "pr2", tempbuf, 0); ! 328: else /* big print */ ! 329: #endif ! 330: execlp ("print", "print", tempbuf, 0); ! 331: telluser ("?Can't find program to list file.\n"); ! 332: _exit(1); ! 333: } ! 334: wait (&status); ! 335: break; ! 336: ! 337: case '!': /* Execute a system command */ ! 338: telluser (""); ! 339: curxy (0, Tscrlen); ! 340: unsetdpy (); ! 341: printf ("Command: "); ! 342: tempbuf[0] = 'x'; /* dummy kludge */ ! 343: if (gets(tempbuf) != NULL && tempbuf[0] != '\0') { ! 344: extern char *skipto(); ! 345: register char *op, /* old ptr */ ! 346: *np; /* new ptr */ ! 347: char bldbuf[70]; ! 348: ! 349: bldbuf[0] = '\0'; ! 350: op = tempbuf; ! 351: special = 0; ! 352: while (cc = *(np = skipto(op, "%#"))) { ! 353: special++; /* set flag */ ! 354: *np++ = '\0'; /* zap the %/# and bump past it */ ! 355: strcat(bldbuf, op); /* 1st part */ ! 356: if (cc == '%') /* complete file name */ ! 357: bldnam(bldbuf, numarg, curfile, argv); ! 358: else { /* Had # sign, trailing comp only */ ! 359: if (numarg <= 2) ! 360: strcat (bldbuf, file[curfile].ln.lname); ! 361: else ! 362: strcat (bldbuf, file[curfile].ln.namep); ! 363: } ! 364: ! 365: op = np; ! 366: } ! 367: strcat(bldbuf, op); ! 368: ! 369: blank (); ! 370: if (special) { /* display expanded command */ ! 371: printf( "%s\n", bldbuf); ! 372: } /* "system" takes long enuf for him to see it */ ! 373: signal(SIGINT, SIG_DFL); /* temp kludge here... */ ! 374: signal(SIGQUIT, SIG_DFL); /* should not use 'system' */ ! 375: system (bldbuf); ! 376: signal (SIGINT, SIG_IGN); ! 377: signal (SIGQUIT, SIG_IGN); ! 378: printf ("\nCR to return..."); ! 379: setdpy (); ! 380: readchar(); ! 381: blank (); ! 382: showscreen (); ! 383: telluser (""); ! 384: } ! 385: else { /* CR only, or EOF, or error */ ! 386: /* ! 387: * He changed his mind, skip it. Since we were in ! 388: * cooked mode, the CR ending the gets is echoed and ! 389: * we lost the first line of the display. So we ! 390: * re-insert it. ! 391: */ ! 392: setdpy(); ! 393: if (tempbuf[0] == 'x') /* means EOF */ ! 394: telluser(""); ! 395: /* null entry, normal case */ ! 396: else if (AL == 0) { /* no insert line capability */ ! 397: blank(); ! 398: showscreen(); ! 399: } ! 400: else { /* be a little sneakier */ ! 401: curxy(0, Tscrlen-1); /* go back to where prompt */ ! 402: ceol(); /* is now, and blank it */ ! 403: home(); ! 404: insline(); /* make some room */ ! 405: pentry(topfile); ! 406: putchar('\n'); ! 407: } ! 408: curxy(CURSOR, curline); ! 409: } ! 410: break; ! 411: ! 412: case 'r': /* Reverse sort */ ! 413: curxy (0, Tscrlen); ! 414: printf ("reverse "); ! 415: rflg = -1; ! 416: case 's': /* Normal sort */ ! 417: if (command == 's') ! 418: { ! 419: curxy (0, Tscrlen); ! 420: rflg = 1; ! 421: } ! 422: printf ("sort by [s,n,r,w]: "); ! 423: command = readchar(); ! 424: while ((command == '?') || !((command == 'n') || ! 425: (command == 'r') || (command == 'w') || (command == 's') || ! 426: (command == CTRL(g)))) ! 427: { ! 428: curxy (0, Tscrlen); ! 429: ceol (); ! 430: if (rflg == -1) ! 431: printf ("reverse "); ! 432: printf ("sort by size, name, read or write date: "); ! 433: command = readchar(); ! 434: } ! 435: ! 436: if (command == CTRL(g)) { /* abort */ ! 437: putchar(CTRL(g)); /* echo it */ ! 438: telluser(""); ! 439: curxy(CURSOR, curline); ! 440: break; ! 441: } ! 442: ! 443: if (command == 's') ! 444: sortyp = SIZE; ! 445: else ! 446: if (command == 'w') ! 447: sortyp = WRITE; ! 448: else ! 449: if (command == 'r') ! 450: sortyp = READ; ! 451: else ! 452: sortyp = 0; ! 453: printf ("%c", command); ! 454: qsort (file, totfiles, sizeof (struct lbuf), compar); ! 455: topfile = 0; ! 456: curfile = 0; ! 457: curline = 0; ! 458: blank (); ! 459: showscreen (); ! 460: curxy (CURSOR, 0); ! 461: break; ! 462: ! 463: case 'e': /* Edit a file or directory */ ! 464: if (file[curfile].ltype == 'd') { ! 465: strcpy (tempbuf, dirednam); ! 466: catargs(tempbuf, oldargv); ! 467: } ! 468: else { ! 469: if ((t = getenv("EDITOR")) != NULL) ! 470: strcat(strcpy(tempbuf, t), " "); ! 471: else ! 472: strcpy (tempbuf, "ed "); ! 473: } ! 474: bldnam(tempbuf, numarg, curfile, argv); ! 475: blank (); ! 476: unsetdpy (); ! 477: system (tempbuf); ! 478: setdpy (); ! 479: blank (); ! 480: showscreen (); ! 481: telluser (""); ! 482: break; ! 483: ! 484: case 'm': /* 'more' a file */ ! 485: if (file[curfile].ltype == 'd') ! 486: { ! 487: telluser ("?Can only page thru files"); ! 488: break; ! 489: } ! 490: strcpy (tempbuf, MOREPGM); ! 491: bldnam(tempbuf, numarg, curfile, argv); ! 492: blank (); ! 493: unsetdpy (); ! 494: system (tempbuf); ! 495: /* if (!sigint) { */ ! 496: printf ("\nCR to return..."); ! 497: /* } */ ! 498: setdpy (); ! 499: readchar(); ! 500: blank (); ! 501: showscreen (); ! 502: telluser (""); ! 503: break; ! 504: ! 505: case 'T': /* don't wait at page end */ ! 506: case 't': /* quickly type the file -- added 5/81, J.Lepreau */ ! 507: if (file[curfile].ltype == 'd') ! 508: { ! 509: telluser ("?Can only type files"); ! 510: break; ! 511: } ! 512: tempbuf[0] = '\0'; ! 513: bldnam(tempbuf, numarg, curfile, argv); ! 514: if (type(tempbuf, command == 't')) /* little t means wait */ ! 515: showscreen (); ! 516: curxy (CURSOR, curline); ! 517: break; ! 518: ! 519: case 'l': /* Refresh screen */ ! 520: case CTRL(l): /* added for editor compatibility -fjl */ ! 521: blank (); ! 522: showscreen (); ! 523: telluser (""); ! 524: break; ! 525: case 'c': /* Refresh current line */ ! 526: curxy (0, curline); ! 527: pentry (curfile); ! 528: curxy (CURSOR, curline); ! 529: break; ! 530: case CTRL(v): ! 531: case 'f': /* forward window */ ! 532: fscreen (); ! 533: break; ! 534: /* wish we could do meta-v */ ! 535: case 'b': /* backward window */ ! 536: bscreen (); ! 537: break; ! 538: case CTRL(n): ! 539: case '\r': ! 540: case '\n': /* next file */ ! 541: if (curfile == totfiles - 1) ! 542: telluser ("?At end of files"); ! 543: else ! 544: if (curline == scrlen - 1) ! 545: { ! 546: topfile = curfile; ! 547: curline = 0; ! 548: blank (); ! 549: showscreen (); ! 550: curxy (CURSOR, 0); ! 551: downline (); ! 552: } ! 553: else ! 554: downline (); ! 555: break; ! 556: case '^': /* previous file */ ! 557: case CTRL(h): /* backspace */ ! 558: case '-': ! 559: case CTRL(p): ! 560: if (curfile == 0) ! 561: telluser ("?At start of files"); ! 562: else ! 563: if (curline == 0) ! 564: { ! 565: topfile = curfile - scrlen + 1; ! 566: curline = scrlen - 1; ! 567: blank (); ! 568: showscreen (); ! 569: curxy (CURSOR, curline); ! 570: upline (); ! 571: } ! 572: else ! 573: upline (); ! 574: break; ! 575: case 'h': /* Help */ ! 576: case '?': ! 577: if (type(helpfile, 1)) /* wait */ ! 578: showscreen (); ! 579: curxy (CURSOR, curline); ! 580: break; ! 581: case 'd': /* delete file */ ! 582: if (file[curfile].deleted == DELETED) ! 583: telluser ("?Already marked deleted"); ! 584: else ! 585: { ! 586: numdeleted++; ! 587: file[curfile].deleted = DELETED; ! 588: printf ("D%c", 010); ! 589: if (curline + 1 == scrlen) ! 590: { ! 591: fscreen (); ! 592: downline (); ! 593: } ! 594: else ! 595: if (curfile != totfiles - 1) ! 596: downline (); ! 597: } ! 598: break; ! 599: case 'u': /* undelete file */ ! 600: if (file[curfile].deleted == NOTDELETED) ! 601: telluser ("?Not marked deleted"); ! 602: else ! 603: { ! 604: numdeleted--; ! 605: file[curfile].deleted = NOTDELETED; ! 606: printf (" %c", 010); ! 607: } ! 608: break; ! 609: default: ! 610: telluser ("Unknown command. Type ? or h for help"); ! 611: break; ! 612: } ! 613: } ! 614: if (numdeleted) ! 615: { ! 616: blank (); ! 617: printf ("The following %s marked for deletion:\n", ! 618: (numdeleted == 1) ? "is" : "are"); ! 619: typefiles (); ! 620: printf ("\nShall I delete %s? ", ! 621: (numdeleted == 1) ? "this" : "these"); ! 622: if ((command = readchar()) != 'y') ! 623: { ! 624: blank (); ! 625: showscreen (); ! 626: curxy (0, Tscrlen); ! 627: ceol (); ! 628: curxy (CURSOR, curline); ! 629: goto startup; ! 630: } ! 631: else ! 632: { ! 633: printf ("y\n"); ! 634: for (i = 0; i < totfiles; i++) ! 635: if (file[i].deleted == DELETED) { ! 636: nambuf[0] = '\0'; ! 637: bldnam(nambuf, numarg, i, argv); ! 638: if (file[i].ltype == 'd') ! 639: rm (nambuf, 0); ! 640: else ! 641: if (unlink (nambuf) < 0) ! 642: printf ("Delete of %s failed.\n", nambuf); ! 643: } ! 644: } ! 645: } ! 646: else ! 647: blank (); ! 648: ! 649: unsetdpy (); ! 650: exit(0); ! 651: } ! 652: ! 653: typefiles () ! 654: { ! 655: int longsiz, ! 656: i, ! 657: j, ! 658: maxperln, ! 659: numout, ! 660: longthis; ! 661: longsiz = numout = 0; ! 662: for (i = 0; i < totfiles; i++) ! 663: if (file[i].deleted == DELETED) ! 664: if (file[i].lflags & ISARG) ! 665: { ! 666: if (strlen (file[i].ln.namep) > longsiz) ! 667: { ! 668: longsiz = strlen (file[i].ln.namep); ! 669: } ! 670: } ! 671: else ! 672: { ! 673: if (strlen (file[i].ln.lname) > longsiz) ! 674: { ! 675: longsiz = strlen (file[i].ln.lname); ! 676: } ! 677: } ! 678: maxperln = scrwid / (longsiz + 3); ! 679: for (i = 0; i < totfiles; i++) ! 680: if (file[i].deleted == DELETED) ! 681: { ! 682: if (file[i].lflags & ISARG) ! 683: { ! 684: printf ("%s", file[i].ln.namep); ! 685: longthis = strlen (file[i].ln.namep); ! 686: } ! 687: else ! 688: { ! 689: printf ("%.14s", file[i].ln.lname); ! 690: longthis = strlen (file[i].ln.lname); ! 691: } ! 692: numout++; ! 693: if ((numout % maxperln) == 0) ! 694: putchar ('\n'); ! 695: else if (numout != numdeleted) ! 696: for (j = 0; j < (longsiz + 3 - longthis); j++) ! 697: putchar (' '); ! 698: } ! 699: } ! 700: ! 701: rm (arg, level) ! 702: char arg[]; ! 703: { ! 704: struct stat buf; ! 705: struct direct direct; ! 706: char name[100]; ! 707: int d; ! 708: ! 709: if (stat (arg, &buf)) ! 710: { ! 711: return; ! 712: } ! 713: if ((buf.st_mode & S_IFMT) == S_IFDIR) ! 714: { ! 715: if (access (arg, 02) < 0) ! 716: { ! 717: printf ("%s not deleted.\n", arg); ! 718: return; ! 719: } ! 720: if ((d = open (arg, 0)) < 0) ! 721: { ! 722: printf ("rm: %s: cannot read\n", arg); ! 723: return; ! 724: } ! 725: while (read (d, (char *) & direct, sizeof (direct)) == sizeof (direct)) ! 726: { ! 727: if (direct.d_ino != 0 && !dotname (direct.d_name)) ! 728: { ! 729: sprintf (name, "%s/%.14s", arg, direct.d_name); ! 730: rm (name, level + 1); ! 731: } ! 732: } ! 733: close (d); ! 734: errcode += rmdir (arg); ! 735: return; ! 736: } ! 737: ! 738: if (unlink (arg)) ! 739: { ! 740: ++errcode; ! 741: printf ("%s not deleted.\n", arg); ! 742: } ! 743: } ! 744: ! 745: dotname (s) ! 746: char *s; ! 747: { ! 748: if (s[0] == '.') ! 749: if (s[1] == '.') ! 750: if (s[2] == '\0') ! 751: return (1); ! 752: else ! 753: return (0); ! 754: else ! 755: if (s[1] == '\0') ! 756: return (1); ! 757: return (0); ! 758: } ! 759: ! 760: rmdir (f) ! 761: char *f; ! 762: { ! 763: int status, ! 764: i; ! 765: ! 766: if (dotname (f)) ! 767: return (0); ! 768: while ((i = vfork ()) == -1) ! 769: sleep (3); ! 770: if (i == 0) { ! 771: execl ("/bin/rmdir", "rmdir", f, 0); ! 772: execl ("/usr/bin/rmdir", "rmdir", f, 0); ! 773: printf ("rm: can't find rmdir\n"); ! 774: _exit(1); ! 775: } ! 776: wait (&status); ! 777: return (status); ! 778: } ! 779: ! 780: fscreen () ! 781: { ! 782: if (topfile + scrlen - 1 > totfiles - 1) ! 783: telluser ("?No remaining windows"); ! 784: else ! 785: { ! 786: topfile = topfile + scrlen - 1; ! 787: curfile = topfile; ! 788: curline = 0; ! 789: blank (); ! 790: showscreen (); ! 791: curxy (CURSOR, 0); ! 792: } ! 793: } ! 794: ! 795: bscreen () ! 796: { ! 797: if (topfile - scrlen + 1 < 0) ! 798: telluser ("?No previous windows"); ! 799: else ! 800: { ! 801: topfile = topfile - scrlen + 1; ! 802: curfile = topfile; ! 803: curline = 0; ! 804: blank (); ! 805: showscreen (); ! 806: curxy (CURSOR, 0); ! 807: } ! 808: } ! 809: ! 810: showscreen () ! 811: { ! 812: int i, ! 813: numprint; ! 814: home (); ! 815: numprint = 0; ! 816: for (i = topfile; (numprint < scrlen) && (i < totfiles); i++) ! 817: { ! 818: numprint++; ! 819: pentry (i); ! 820: putchar ('\n'); ! 821: } ! 822: if (splitflg) ! 823: printf ("%s\n", divider); ! 824: } ! 825: ! 826: readdir (dir) /* Reads directory dir */ ! 827: char *dir; ! 828: { ! 829: static struct direct dentry; ! 830: register int j; ! 831: ! 832: if ((dirf = fopen (dir, "r")) == NULL) ! 833: { ! 834: printf ("\nSorry, %s unreadable.\n", dir); ! 835: unsetdpy (); ! 836: exit(1); ! 837: } ! 838: for (;;) ! 839: { ! 840: if (fread ((char *) & dentry, sizeof (dentry), 1, dirf) != 1) ! 841: break; ! 842: if (dentry.d_ino == 0 ! 843: || dentry.d_name[0] == '.' && (dentry.d_name[1] == '\0' ! 844: || dentry.d_name[1] == '.' && dentry.d_name[2] == '\0')) ! 845: continue; ! 846: ! 847: if (totfiles == MAXFILES) ! 848: overflow(); /* abort, too may files */ ! 849: /* Just ignore if can't find the file, dir may be changing */ ! 850: if (gstat (makename (dir, dentry.d_name)) == 0) { /* 0 == Ok */ ! 851: file[totfiles].lnum = dentry.d_ino; ! 852: for (j = 0; j < DIRSIZ; j++) ! 853: file[totfiles].ln.lname[j] = dentry.d_name[j]; ! 854: totfiles++; ! 855: if (totfiles % 10 == 0) ! 856: putchar ('.'); ! 857: } ! 858: } ! 859: fclose (dirf); ! 860: } ! 861: ! 862: gstat (name) /* Stats the file with name */ ! 863: char *name; ! 864: { ! 865: ! 866: file[totfiles].lflags = 0; ! 867: file[totfiles].lnum = 0; ! 868: file[totfiles].ltype = '-'; ! 869: ! 870: if (stat (name, &statbuf) < 0) ! 871: { ! 872: return(-1); ! 873: } ! 874: file[totfiles].lnum = statbuf.st_ino; ! 875: file[totfiles].lsize = statbuf.st_size; ! 876: switch (statbuf.st_mode & S_IFMT) ! 877: { ! 878: case S_IFLNK: ! 879: file[totfiles].ltype = 'l'; ! 880: break; ! 881: case S_IFDIR: ! 882: file[totfiles].ltype = 'd'; ! 883: break; ! 884: case S_IFBLK: ! 885: file[totfiles] .ltype = 'b'; ! 886: file[totfiles].lsize = statbuf.st_rdev; ! 887: break; ! 888: case S_IFCHR: ! 889: file[totfiles].ltype = 'c'; ! 890: file[totfiles].lsize = statbuf.st_rdev; ! 891: break; ! 892: } ! 893: file[totfiles].lflags = statbuf.st_mode & ~S_IFMT; ! 894: file[totfiles].luid = statbuf.st_uid; ! 895: file[totfiles].lgid = statbuf.st_gid; ! 896: file[totfiles].lnl = statbuf.st_nlink; ! 897: file[totfiles].latime = statbuf.st_atime; ! 898: file[totfiles].lctime = statbuf.st_ctime; ! 899: file[totfiles].lmtime = statbuf.st_mtime; ! 900: totblocks += nblock (statbuf.st_size); ! 901: return(0); ! 902: } ! 903: ! 904: char * ! 905: makename (dir, filen) ! 906: char *dir, ! 907: *filen; ! 908: { ! 909: static char dfile[100]; ! 910: register char *dp, ! 911: *fp; ! 912: register int i; ! 913: ! 914: dp = dfile; ! 915: fp = dir; ! 916: while (*fp) ! 917: *dp++ = *fp++; ! 918: *dp++ = '/'; ! 919: fp = filen; ! 920: for (i = 0; i < DIRSIZ; i++) ! 921: *dp++ = *fp++; ! 922: *dp = 0; ! 923: return (dfile); ! 924: } ! 925: ! 926: long ! 927: nblock (size) ! 928: long size; ! 929: { ! 930: return ((size + 511) >> 9); ! 931: } ! 932: ! 933: pentry (whichone) ! 934: int whichone; ! 935: { ! 936: struct ! 937: { ! 938: char dminor, ! 939: dmajor; ! 940: }; ! 941: register t; ! 942: register char *cp; ! 943: ! 944: if (file[whichone].lnum == -1) ! 945: return; ! 946: if (iflg) ! 947: printf ("%5u ", file[whichone].lnum); ! 948: if (sflg) ! 949: printf ("%4D ", nblock (file[whichone].lsize)); ! 950: if (lflg) ! 951: { ! 952: putchar (file[whichone].ltype); ! 953: pmode (file[whichone].lflags); ! 954: printf ("%2d ", file[whichone].lnl); ! 955: t = file[whichone].luid; ! 956: if (gflg) ! 957: t = file[whichone].lgid; ! 958: if (getname (t, userbuf) == 0) ! 959: printf ("%-14.14s", userbuf); ! 960: else ! 961: printf ("%-14d", t); ! 962: if (file[whichone].ltype == 'b' || file[whichone].ltype == 'c') ! 963: printf ("%3d,%3d", major ((int) file[whichone].lsize), ! 964: minor ((int) file[whichone].lsize)); ! 965: else ! 966: printf ("%7ld", file[whichone].lsize); ! 967: if ((sortyp == WRITE) || (sortyp == 0) || (sortyp == SIZE)) ! 968: { ! 969: cp = ctime (&file[whichone].lmtime); ! 970: if (file[whichone].lmtime < year) ! 971: printf (" %-7.7s %-4.4s ", cp + 4, cp + 20); ! 972: else ! 973: printf (" %-12.12s ", cp + 4); ! 974: } ! 975: else ! 976: if (sortyp == READ) ! 977: { ! 978: cp = ctime (&file[whichone].latime); ! 979: if (file[whichone].latime < year) ! 980: printf (" %-7.7s %-4.4s ", cp + 4, cp + 20); ! 981: else ! 982: printf (" %-12.12s ", cp + 4); ! 983: } ! 984: } ! 985: printf ("%c", file[whichone].deleted ? 'D' : ' '); ! 986: if (file[whichone].lflags & ISARG) ! 987: printf (" %s", file[whichone].ln.namep); ! 988: else { ! 989: putchar(' '); ! 990: for (t=0; t<14; t++) { ! 991: register c = file[whichone].ln.lname[t] & 0377;; ! 992: if (c == '\0') ! 993: break; ! 994: if (c<' ' || c>=0200) { ! 995: c &= 0177; ! 996: c |= 0100; ! 997: putchar('^'); ! 998: } ! 999: putchar(c); ! 1000: } ! 1001: } ! 1002: } ! 1003: ! 1004: getname (uid, buf) ! 1005: int uid; ! 1006: char buf[]; ! 1007: { ! 1008: int j, ! 1009: c, ! 1010: n, ! 1011: i; ! 1012: ! 1013: if (uid == lastuid) ! 1014: return (0); ! 1015: if (pwdf == NULL) ! 1016: return (-1); ! 1017: rewind (pwdf); ! 1018: lastuid = -1; ! 1019: do ! 1020: { ! 1021: i = 0; ! 1022: j = 0; ! 1023: n = 0; ! 1024: while ((c = fgetc (pwdf)) != '\n') ! 1025: { ! 1026: if (c == EOF) ! 1027: return (-1); ! 1028: if (c == ':') ! 1029: { ! 1030: j++; ! 1031: c = '0'; ! 1032: } ! 1033: if (j == 0) ! 1034: buf[i++] = c; ! 1035: if (j == 2) ! 1036: n = n * 10 + c - '0'; ! 1037: } ! 1038: } while (n != uid); ! 1039: buf[i++] = '\0'; ! 1040: lastuid = uid; ! 1041: return (0); ! 1042: } ! 1043: ! 1044: int m1[] = ! 1045: { ! 1046: 1, S_IREAD >> 0, 'r', '-' ! 1047: }; ! 1048: int m2[] = ! 1049: { ! 1050: 1, S_IWRITE >> 0, 'w', '-' ! 1051: }; ! 1052: int m3[] = ! 1053: { ! 1054: 2, S_ISUID, 's', S_IEXEC >> 0, 'x', '-' ! 1055: }; ! 1056: int m4[] = ! 1057: { ! 1058: 1, S_IREAD >> 3, 'r', '-' ! 1059: }; ! 1060: int m5[] = ! 1061: { ! 1062: 1, S_IWRITE >> 3, 'w', '-' ! 1063: }; ! 1064: int m6[] = ! 1065: { ! 1066: 2, S_ISGID, 's', S_IEXEC >> 3, 'x', '-' ! 1067: }; ! 1068: int m7[] = ! 1069: { ! 1070: 1, S_IREAD >> 6, 'r', '-' ! 1071: }; ! 1072: int m8[] = ! 1073: { ! 1074: 1, S_IWRITE >> 6, 'w', '-' ! 1075: }; ! 1076: int m9[] = ! 1077: { ! 1078: 2, S_ISVTX, 't', S_IEXEC >> 6, 'x', '-' ! 1079: }; ! 1080: ! 1081: int *m[] = ! 1082: { ! 1083: m1, m2, m3, m4, m5, m6, m7, m8, m9 ! 1084: }; ! 1085: ! 1086: pmode (aflag) ! 1087: { ! 1088: register int **mp; ! 1089: ! 1090: flags = aflag; ! 1091: for (mp = &m[0]; mp < &m[sizeof (m) / sizeof (m[0])];) ! 1092: select (*mp++); ! 1093: } ! 1094: ! 1095: select (pairp) ! 1096: register int *pairp; ! 1097: { ! 1098: register int n; ! 1099: ! 1100: n = *pairp++; ! 1101: while (--n >= 0 && (flags & *pairp++) == 0) ! 1102: pairp++; ! 1103: putchar (*pairp); ! 1104: } ! 1105: ! 1106: compar (pp1, pp2) ! 1107: struct lbuf *pp1, ! 1108: *pp2; ! 1109: { ! 1110: register struct lbuf *p1, ! 1111: *p2; ! 1112: ! 1113: p1 = pp1; ! 1114: p2 = pp2; ! 1115: if (p1 -> lflags & ISARG && p1 -> ltype == 'd') ! 1116: { ! 1117: if (!(p2 -> lflags & ISARG && p2 -> ltype == 'd')) ! 1118: return (1); ! 1119: } ! 1120: else ! 1121: { ! 1122: if (p2 -> lflags & ISARG && p2 -> ltype == 'd') ! 1123: return (-1); ! 1124: } ! 1125: if (sortyp == SIZE) ! 1126: { ! 1127: if (p2 -> lsize == p1 -> lsize) ! 1128: return (0); ! 1129: if (p2 -> lsize > p1 -> lsize) ! 1130: return (rflg); ! 1131: return (-rflg); ! 1132: } ! 1133: else ! 1134: if (sortyp == WRITE) ! 1135: { ! 1136: if (p2 -> lmtime == p1 -> lmtime) ! 1137: return (0); ! 1138: if (p2 -> lmtime > p1 -> lmtime) ! 1139: return (rflg); ! 1140: return (-rflg); ! 1141: } ! 1142: else ! 1143: if (sortyp == READ) ! 1144: { ! 1145: if (p2 -> latime == p1 -> latime) ! 1146: return (0); ! 1147: if (p2 -> latime > p1 -> latime) ! 1148: return (rflg); ! 1149: return (-rflg); ! 1150: } ! 1151: return (rflg * strcmp (p1 -> lflags & ISARG ? p1 -> ln.namep : p1 -> ln.lname, ! 1152: p2 -> lflags & ISARG ? p2 -> ln.namep : p2 -> ln.lname)); ! 1153: } ! 1154: ! 1155: ceod() ! 1156: { ! 1157: putpad(CD); ! 1158: } ! 1159: ! 1160: ceol () ! 1161: { ! 1162: putpad (CE); ! 1163: } ! 1164: blank () ! 1165: { ! 1166: putpad (CL); ! 1167: } ! 1168: home () ! 1169: { ! 1170: if (HO) ! 1171: putpad (HO); ! 1172: else ! 1173: curxy(0,0); ! 1174: } ! 1175: insline () ! 1176: { ! 1177: putpad (AL); ! 1178: } ! 1179: ! 1180: /* Yes, folks, we use direct cursor addressing to get to next line! ! 1181: Before you mumble "What sort of cretin would do this?" here's ! 1182: the reason. We don't use \n since that obviously won't work. ! 1183: We don't use \012 since virgin version 7 makes that into a crlf. ! 1184: We don't use raw mode since we type out help files efficently, ! 1185: and we don't want to switch modes all the time. So enjoy. -- SMC */ ! 1186: ! 1187: downline () ! 1188: { ! 1189: curxy (CURSOR, ++curline); ! 1190: curfile++; ! 1191: } ! 1192: upline () ! 1193: { ! 1194: putpad (UP); ! 1195: curline--; ! 1196: curfile--; ! 1197: } ! 1198: ! 1199: /*VARARGS1*/ ! 1200: telluser (msg, args) ! 1201: char *msg; ! 1202: { ! 1203: curxy (0, Tscrlen); ! 1204: ceol (); ! 1205: printf (msg, args); ! 1206: curxy (CURSOR, curline); ! 1207: blurb++; ! 1208: } ! 1209: curxy (col, lin) ! 1210: { ! 1211: char *cmstr = tgoto (CM, col, lin); ! 1212: putpad (cmstr); ! 1213: } ! 1214: ! 1215: char *fgets(); ! 1216: ! 1217: type (filestr, waitflg) /* Modified to type help file & others. fjl 5/81 */ ! 1218: char *filestr; /* Kludgy now with split screen stuff! */ ! 1219: { ! 1220: int helpfd = 5; ! 1221: FILE *fd = stdin; ! 1222: char *eof; ! 1223: register int i, n; ! 1224: register int cc = 0; ! 1225: int cur_scrl; /* current screen length */ ! 1226: char helpbuf[512]; ! 1227: ! 1228: if (!splitflg) ! 1229: helpfd = open(filestr, 0); ! 1230: else ! 1231: fd = fopen(filestr, "r"); ! 1232: if (helpfd < 0 || fd == NULL) { ! 1233: telluser("?Unable to open %sfile",strcmp(filestr,helpfile)?"":"help "); ! 1234: return (FALSE); ! 1235: } ! 1236: ! 1237: signal(SIGINT, catchint); ! 1238: sigint = 0; ! 1239: ! 1240: if (!splitflg) { ! 1241: blank(); ! 1242: fflush(stdout); ! 1243: while ((i = read (helpfd, helpbuf, 512)) > 0 && !sigint) ! 1244: write (1, helpbuf, i); ! 1245: close(helpfd); ! 1246: } ! 1247: else { ! 1248: cur_scrl = totfiles - topfile + 1; /* topfile starts at 0 */ ! 1249: Worktop = ((cur_scrl < scrlen) ? cur_scrl : scrlen) + 1; ! 1250: do { ! 1251: curxy(0, Worktop); ! 1252: ceod(); ! 1253: for (i = Worktop; (i < Tscrlen) && !sigint && (cc != EOF); i++) { ! 1254: n = 0; ! 1255: while ((cc = getc(fd)) != EOF) { ! 1256: if (cc == '\t') ! 1257: n |= 07; ! 1258: chklen: if (n++ == scrwid-1) { /* Use most of screen */ ! 1259: if (cc != '\n') { ! 1260: ungetc(cc, fd); ! 1261: cc = '\n'; ! 1262: } ! 1263: } ! 1264: if (cc < ' ' && cc!='\n' && cc!='\t' || cc>=0200) { ! 1265: putchar('^'); ! 1266: cc &= 0177; ! 1267: cc |= 0100; ! 1268: goto chklen; ! 1269: } ! 1270: putchar(cc); ! 1271: if (cc == '\n') ! 1272: break; ! 1273: } ! 1274: } ! 1275: } while (!sigint && (cc != EOF) && waitchk(waitflg)); ! 1276: ! 1277: if (feof(fd)) ! 1278: printf("===== End-of-File =====\n"); ! 1279: fflush(stdout); ! 1280: fclose(fd); ! 1281: return(FALSE); /* means needs no re-display */ ! 1282: } ! 1283: ! 1284: if (!splitflg) { /* redundant now... */ ! 1285: if (!sigint) { ! 1286: curxy(0, Tscrlen); ! 1287: printf ("CR to return..."); ! 1288: readchar(); ! 1289: } ! 1290: blank (); ! 1291: return (TRUE); ! 1292: } ! 1293: } ! 1294: ! 1295: waitchk(waitflg) ! 1296: { ! 1297: if (!waitflg) ! 1298: return(1); ! 1299: fflush(stdout); ! 1300: curxy(0, Tscrlen); ! 1301: printf ("---Continue---"); ! 1302: ceol(); ! 1303: curxy(0, Tscrlen); ! 1304: readchar(); ! 1305: ceol(); ! 1306: if (sigint) ! 1307: return(0); /* avoids clear of screen */ ! 1308: return(1); ! 1309: } ! 1310: ! 1311: setdpy () ! 1312: { ! 1313: ioctl (0, TIOCGETP, &ioctlb); ! 1314: ioctlb.sg_flags |= CBREAK; ! 1315: ioctlb.sg_flags &= ~ECHO; ! 1316: ioctl (0, TIOCSETP, &ioctlb); ! 1317: } ! 1318: ! 1319: unsetdpy () ! 1320: { ! 1321: ioctlb.sg_flags &= ~CBREAK; ! 1322: ioctlb.sg_flags |= ECHO; ! 1323: ioctl (0, TIOCSETP, &ioctlb); ! 1324: } ! 1325: ! 1326: getcap () ! 1327: { ! 1328: char *ap; ! 1329: char *term; ! 1330: char *xPC; ! 1331: ! 1332: term = getenv ("TERM"); ! 1333: if (term == 0) ! 1334: { ! 1335: printf("No TERM in environment\n"); ! 1336: exit(1); ! 1337: } ! 1338: ! 1339: switch (tgetent (tbuf, term)) ! 1340: { ! 1341: case -1: ! 1342: printf("Cannot open termcap file\n"); ! 1343: exit (2); ! 1344: case 0: ! 1345: printf("%s: unknown terminal", term); ! 1346: exit (3); ! 1347: } ! 1348: ! 1349: ap = tcapbuf; ! 1350: ! 1351: Tscrlen = tgetnum ("li") - 1; ! 1352: scrwid = tgetnum ("co") - 1;/* lose 1 so won't scroll in last line */ ! 1353: ! 1354: UP = tgetstr ("up", &ap); ! 1355: CD = tgetstr ("cd", &ap); ! 1356: CE = tgetstr ("ce", &ap); ! 1357: HO = tgetstr ("ho", &ap); ! 1358: CL = tgetstr ("cl", &ap); ! 1359: CM = tgetstr ("cm", &ap); ! 1360: AL = tgetstr ("al", &ap); /* insert line, optional */ ! 1361: ! 1362: xPC = tgetstr ("pc", &ap); ! 1363: if (xPC) ! 1364: PC = *xPC; ! 1365: ! 1366: if ((CM == 0) || (CL == 0) || (UP == 0)) ! 1367: { ! 1368: printf("Tty must have cursor addr, clear, and 4 cursor motions.\n"); ! 1369: exit (1); ! 1370: } ! 1371: if (Tscrlen <= 0 || scrwid <= 0) ! 1372: { ! 1373: printf("Must know the screen size\n"); ! 1374: exit (1); ! 1375: } ! 1376: } ! 1377: ! 1378: outch (c) ! 1379: { ! 1380: putchar (c); ! 1381: } ! 1382: ! 1383: putpad (str) ! 1384: char *str; ! 1385: { ! 1386: if (str) ! 1387: tputs (str, 0, outch); ! 1388: } ! 1389: ! 1390: catchint(sig) ! 1391: { ! 1392: signal(SIGINT, SIG_IGN); /* reset it */ ! 1393: sigint = 1; ! 1394: } ! 1395: ! 1396: char * ! 1397: bldnam(str, numarg, filidx, argv) ! 1398: char *str; ! 1399: char *argv[]; ! 1400: { ! 1401: ! 1402: if (numarg == 1) ! 1403: strcat (str, file[filidx].ln.lname); ! 1404: else ! 1405: if (numarg == 2) { ! 1406: strcat (str, argv[1]); ! 1407: strcat (str, "/"); ! 1408: strcat (str, file[filidx].ln.lname); ! 1409: } ! 1410: else ! 1411: strcat (str, file[filidx].ln.namep); ! 1412: return(str); ! 1413: } ! 1414: ! 1415: char * ! 1416: catargs(str, argv) ! 1417: char *str; ! 1418: char *argv[]; ! 1419: { ! 1420: register int i; ! 1421: ! 1422: for (++argv; *argv; argv++) { ! 1423: if (**argv == '-') { ! 1424: strcat(str, *argv); ! 1425: strcat(str, " "); ! 1426: } ! 1427: } ! 1428: return(str); ! 1429: } ! 1430: ! 1431: overflow() ! 1432: { ! 1433: printf("\n?Too many files\007\n"); ! 1434: fflush(stdout); ! 1435: sleep(1); /* So user can see it - don't worry if less */ ! 1436: } ! 1437: ! 1438: char *skipto (string,charset) ! 1439: char *charset,*string; ! 1440: { ! 1441: register char *setp,*strp; ! 1442: register int found; ! 1443: ! 1444: found = 0; /* not found yet */ ! 1445: strp = string; /* start at first char */ ! 1446: ! 1447: while (*strp && !found) { /* until null or found */ ! 1448: /* find first char in charset matching *strp */ ! 1449: for (setp=charset; (*setp) && (*setp != *strp); setp++) ; ! 1450: if (*setp) found = 1; /* matches a char */ ! 1451: else strp++; /* else keep looking */ ! 1452: } ! 1453: ! 1454: return (strp); ! 1455: } ! 1456: ! 1457: readchar() ! 1458: { ! 1459: static neofs; ! 1460: register c; ! 1461: ! 1462: fflush(stdout); ! 1463: c = getchar(); ! 1464: if (c == EOF) ! 1465: if (++neofs > 100) ! 1466: exit(1); ! 1467: return(c); ! 1468: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.