Annotation of 43BSD/etc/bad144.c, revision 1.1

1.1     ! root        1: /*
        !             2:  * Copyright (c) 1980 Regents of the University of California.
        !             3:  * All rights reserved.  The Berkeley software License Agreement
        !             4:  * specifies the terms and conditions for redistribution.
        !             5:  */
        !             6: 
        !             7: #ifndef lint
        !             8: char copyright[] =
        !             9: "@(#) Copyright (c) 1980 Regents of the University of California.\n\
        !            10:  All rights reserved.\n";
        !            11: #endif not lint
        !            12: 
        !            13: #ifndef lint
        !            14: static char sccsid[] = "@(#)bad144.c   5.4 (Berkeley) 3/20/86";
        !            15: #endif not lint
        !            16: 
        !            17: /*
        !            18:  * bad144
        !            19:  *
        !            20:  * This program prints and/or initializes a bad block record for a pack,
        !            21:  * in the format used by the DEC standard 144.
        !            22:  * It can also add bad sector(s) to the record, moving the sector
        !            23:  * replacements as necessary.
        !            24:  *
        !            25:  * It is preferable to write the bad information with a standard formatter,
        !            26:  * but this program will do.
        !            27:  * 
        !            28:  * RP06 sectors are marked as bad by inverting the format bit in the
        !            29:  * header; on other drives the valid-sector bit is cleared.
        !            30:  */
        !            31: #include <sys/types.h>
        !            32: #include <sys/dkbad.h>
        !            33: #include <sys/ioctl.h>
        !            34: #include <sys/file.h>
        !            35: #include <machine/dkio.h>
        !            36: 
        !            37: #include <stdio.h>
        !            38: #include <disktab.h>
        !            39: 
        !            40: #define RETRIES        10              /* number of retries on reading old sectors */
        !            41: 
        !            42: int    fflag, add, copy, verbose, nflag;
        !            43: int    compare();
        !            44: struct dkbad dkbad, oldbad;
        !            45: daddr_t        size, getold(), badsn();
        !            46: struct disktab *dp;
        !            47: char   name[BUFSIZ];
        !            48: char   *malloc();
        !            49: off_t  lseek();
        !            50: 
        !            51: main(argc, argv)
        !            52:        int argc;
        !            53:        char *argv[];
        !            54: {
        !            55:        register struct bt_bad *bt;
        !            56:        daddr_t sn, bn[126];
        !            57:        int i, f, nbad, new, bad, errs;
        !            58: 
        !            59:        argc--, argv++;
        !            60:        while (argc > 0 && **argv == '-') {
        !            61:                (*argv)++;
        !            62:                while (**argv) {
        !            63:                        switch (**argv) {
        !            64:                            case 'f':
        !            65:                                fflag++;
        !            66:                                break;
        !            67:                            case 'a':
        !            68:                                add++;
        !            69:                                break;
        !            70:                            case 'c':
        !            71:                                copy++;
        !            72:                                break;
        !            73:                            case 'v':
        !            74:                                verbose++;
        !            75:                                break;
        !            76:                            case 'n':
        !            77:                                nflag++;
        !            78:                                verbose++;
        !            79:                                break;
        !            80:                        }
        !            81:                        (*argv)++;
        !            82:                }
        !            83:                argc--, argv++;
        !            84:        }
        !            85:        if (argc < 2) {
        !            86:                fprintf(stderr,
        !            87:                  "usage: bad144 [ -f ] type disk [ snum [ bn ... ] ]\n");
        !            88:                fprintf(stderr,
        !            89:              "to read or overwrite bad-sector table, e.g.: bad144 rk07 hk0\n");
        !            90:                fprintf(stderr,
        !            91:                  "or bad144 -a [ -f ] [ -c ] type disk  bn ...\n");
        !            92:                fprintf(stderr, "where options are:\n");
        !            93:                fprintf(stderr, "\t-a  add new bad sectors to the table\n");
        !            94:                fprintf(stderr, "\t-f  reformat listed sectors as bad\n");
        !            95:                fprintf(stderr, "\t-c  copy original sector to replacement\n");
        !            96:                exit(1);
        !            97:        }
        !            98:        dp = getdiskbyname(argv[0]);
        !            99:        if (dp == NULL) {
        !           100:                fprintf(stderr, "%s: unknown disk type\n", argv[0]);
        !           101:                exit(1);
        !           102:        }
        !           103:        if (argv[1][0] != '/')
        !           104:                (void)sprintf(name, "/dev/r%sc", argv[1]);
        !           105:        else
        !           106:                (void)strcpy(name, argv[1]);
        !           107:        argc -= 2;
        !           108:        argv += 2;
        !           109:        size = dp->d_nsectors * dp->d_ntracks * dp->d_ncylinders; 
        !           110:        if (argc == 0) {
        !           111:                f = open(name, O_RDONLY);
        !           112:                if (f < 0)
        !           113:                        Perror(name);
        !           114:                sn = getold(f, &dkbad);
        !           115:                printf("bad block information at sector %d in %s:\n",
        !           116:                    sn, name);
        !           117:                printf("cartridge serial number: %d(10)\n", dkbad.bt_csn);
        !           118:                switch (dkbad.bt_flag) {
        !           119: 
        !           120:                case -1:
        !           121:                        printf("alignment cartridge\n");
        !           122:                        break;
        !           123: 
        !           124:                case 0:
        !           125:                        break;
        !           126: 
        !           127:                default:
        !           128:                        printf("bt_flag=%x(16)?\n", dkbad.bt_flag);
        !           129:                        break;
        !           130:                }
        !           131:                bt = dkbad.bt_bad;
        !           132:                for (i = 0; i < 126; i++) {
        !           133:                        bad = (bt->bt_cyl<<16) + bt->bt_trksec;
        !           134:                        if (bad < 0)
        !           135:                                break;
        !           136:                        printf("sn=%d, cn=%d, tn=%d, sn=%d\n", badsn(bt),
        !           137:                            bt->bt_cyl, bt->bt_trksec>>8, bt->bt_trksec&0xff);
        !           138:                        bt++;
        !           139:                }
        !           140:                (void) checkold();
        !           141:                exit(0);
        !           142:        }
        !           143:        f = open(name, (fflag || add)? O_RDWR: O_WRONLY);
        !           144:        if (f < 0)
        !           145:                Perror(name);
        !           146:        if (add) {
        !           147:                /*
        !           148:                 * Read in the old badsector table.
        !           149:                 * Verify that it makes sense, and the bad sectors
        !           150:                 * are in order.  Copy the old table to the new one.
        !           151:                 */
        !           152:                (void) getold(f, &oldbad);
        !           153:                i = checkold();
        !           154:                if (verbose)
        !           155:                        printf("Had %d bad sectors\n", i);
        !           156:                if (i + argc > 126) {
        !           157:                        printf("bad144: not enough room for %d more sectors\n",
        !           158:                                argc);
        !           159:                        printf("limited to 126 by information format\n");
        !           160:                        exit(1);
        !           161:                }
        !           162:                dkbad = oldbad;
        !           163:        } else {
        !           164:                dkbad.bt_csn = atoi(*argv++);
        !           165:                argc--;
        !           166:                dkbad.bt_mbz = 0;
        !           167:                if (argc > 126) {
        !           168:                        printf("bad144: too many bad sectors specified\n");
        !           169:                        printf("limited to 126 by information format\n");
        !           170:                        exit(1);
        !           171:                }
        !           172:                i = 0;
        !           173:        }
        !           174:        errs = 0;
        !           175:        new = argc;
        !           176:        while (argc > 0) {
        !           177:                daddr_t sn = atoi(*argv++);
        !           178:                argc--;
        !           179:                if (sn < 0 || sn >= size) {
        !           180:                        printf("%d: out of range [0,%d) for %s\n",
        !           181:                            sn, size, dp->d_name);
        !           182:                        errs++;
        !           183:                        continue;
        !           184:                }
        !           185:                bn[i] = sn;
        !           186:                dkbad.bt_bad[i].bt_cyl = sn / (dp->d_nsectors*dp->d_ntracks);
        !           187:                sn %= (dp->d_nsectors*dp->d_ntracks);
        !           188:                dkbad.bt_bad[i].bt_trksec =
        !           189:                    ((sn/dp->d_nsectors) << 8) + (sn%dp->d_nsectors);
        !           190:                i++;
        !           191:        }
        !           192:        if (errs)
        !           193:                exit(1);
        !           194:        nbad = i;
        !           195:        while (i < 126) {
        !           196:                dkbad.bt_bad[i].bt_trksec = -1;
        !           197:                dkbad.bt_bad[i].bt_cyl = -1;
        !           198:                i++;
        !           199:        }
        !           200:        if (add) {
        !           201:                /*
        !           202:                 * Sort the new bad sectors into the list.
        !           203:                 * Then shuffle the replacement sectors so that
        !           204:                 * the previous bad sectors get the same replacement data.
        !           205:                 */
        !           206:                qsort((char *)dkbad.bt_bad, nbad, sizeof (struct bt_bad),
        !           207:                    compare);
        !           208:                shift(f, nbad, nbad-new);
        !           209:        }
        !           210:        for (i = 0; i < 10 && i < dp->d_nsectors; i += 2) {
        !           211:                if (lseek(f, dp->d_secsize * (size - dp->d_nsectors + i),
        !           212:                    L_SET) < 0)
        !           213:                        Perror("lseek");
        !           214:                if (verbose)
        !           215:                        printf("write badsect file at %d\n",
        !           216:                                size - dp->d_nsectors + i);
        !           217:                if (nflag == 0 &&
        !           218:                    write(f, (caddr_t)&dkbad, sizeof dkbad) != sizeof dkbad) {
        !           219:                        char msg[80];
        !           220:                        (void)sprintf(msg, "bad144: write bad sector file %d",
        !           221:                            i/2);
        !           222:                        perror(msg);
        !           223:                }
        !           224:        }
        !           225:        if (fflag)
        !           226:                for (i = nbad - new; i < nbad; i++)
        !           227:                        format(f, bn[i]);
        !           228:        exit(0);
        !           229: }
        !           230: 
        !           231: daddr_t
        !           232: getold(f, bad)
        !           233: struct dkbad *bad;
        !           234: {
        !           235:        register int i;
        !           236:        daddr_t sn;
        !           237:        char msg[80];
        !           238: 
        !           239:        for (i = 0; i < 10 && i < dp->d_nsectors; i += 2) {
        !           240:                sn = size - dp->d_nsectors + i;
        !           241:                if (lseek(f, sn * dp->d_secsize, L_SET) < 0)
        !           242:                        Perror("lseek");
        !           243:                if (read(f, (char *)bad, sizeof (*bad)) == sizeof (*bad)) {
        !           244:                        if (i > 0)
        !           245:                                printf("Using bad-sector file %d\n", i/2);
        !           246:                        return(sn);
        !           247:                }
        !           248:                (void)sprintf(msg, "bad144: read bad sector file at sn %d",
        !           249:                    sn);
        !           250:                perror(msg);
        !           251:        }
        !           252:        fprintf(stderr,
        !           253:            "bad144: %s: can't read bad block info\n", name);
        !           254:        exit(1);
        !           255:        /*NOTREACHED*/
        !           256: }
        !           257: 
        !           258: checkold()
        !           259: {
        !           260:        register int i;
        !           261:        register struct bt_bad *bt;
        !           262:        daddr_t sn, lsn;
        !           263:        int errors = 0, warned = 0;
        !           264: 
        !           265:        if (oldbad.bt_flag != 0) {
        !           266:                fprintf(stderr, "bad144: %s: bad flag in bad-sector table\n",
        !           267:                        name);
        !           268:                errors++;
        !           269:        }
        !           270:        if (oldbad.bt_mbz != 0) {
        !           271:                fprintf(stderr, "bad144: %s: bad magic number\n", name);
        !           272:                errors++;
        !           273:        }
        !           274:        lsn = 0;
        !           275:        bt = oldbad.bt_bad;
        !           276:        for (i = 0; i < 126; i++, bt++) {
        !           277:                if (bt->bt_cyl == -1 && bt->bt_trksec == -1)
        !           278:                        break;
        !           279:                if ((bt->bt_cyl >= dp->d_ncylinders) ||
        !           280:                    ((bt->bt_trksec >> 8) >= dp->d_ntracks) ||
        !           281:                    ((bt->bt_trksec & 0xff) >= dp->d_nsectors)) {
        !           282:                        fprintf(stderr,
        !           283:                     "bad144: cyl/trk/sect out of range in existing entry: ");
        !           284:                        fprintf(stderr, "sn=%d, cn=%d, tn=%d, sn=%d\n",
        !           285:                                badsn(bt), bt->bt_cyl, bt->bt_trksec>>8,
        !           286:                                bt->bt_trksec & 0xff);
        !           287:                        errors++;
        !           288:                }
        !           289:                sn = (bt->bt_cyl * dp->d_ntracks +
        !           290:                    (bt->bt_trksec >> 8)) *
        !           291:                    dp->d_nsectors + (bt->bt_trksec & 0xff);
        !           292:                if (sn < lsn && !warned) {
        !           293:                    fprintf(stderr, "bad144: bad sector file out of order\n");
        !           294:                    errors++;
        !           295:                    warned++;
        !           296:                }
        !           297:                lsn = sn;
        !           298:        }
        !           299:        if (errors)
        !           300:                exit(1);
        !           301:        return (i);
        !           302: }
        !           303: 
        !           304: /*
        !           305:  * Move the bad sector replacements
        !           306:  * to make room for the new bad sectors.
        !           307:  * new is the new number of bad sectors, old is the previous count.
        !           308:  */
        !           309: shift(f, new, old)
        !           310: {
        !           311:        daddr_t repl;
        !           312: 
        !           313:        /*
        !           314:         * First replacement is last sector of second-to-last track.
        !           315:         */
        !           316:        repl = size - dp->d_nsectors - 1;
        !           317:        new--; old--;
        !           318:        while (new >= 0 && new != old) {
        !           319:                if (old < 0 ||
        !           320:                    compare(&dkbad.bt_bad[new], &oldbad.bt_bad[old]) > 0) {
        !           321:                        /*
        !           322:                         * Insert new replacement here-- copy original
        !           323:                         * sector if requested and possible,
        !           324:                         * otherwise write a zero block.
        !           325:                         */
        !           326:                        if (!copy ||
        !           327:                            !blkcopy(f, badsn(&dkbad.bt_bad[new]), repl - new))
        !           328:                                blkzero(f, repl - new);
        !           329:                } else {
        !           330:                        if (blkcopy(f, repl - old, repl - new) == 0)
        !           331:                            fprintf(stderr,
        !           332:                                "Can't copy replacement sector %d to %d\n",
        !           333:                                repl-old, repl-new);
        !           334:                        old--;
        !           335:                }
        !           336:                new--;
        !           337:        }
        !           338: }
        !           339: 
        !           340: char *buf;
        !           341: 
        !           342: /*
        !           343:  *  Copy disk sector s1 to s2.
        !           344:  */
        !           345: blkcopy(f, s1, s2)
        !           346: daddr_t s1, s2;
        !           347: {
        !           348:        register tries, n;
        !           349: 
        !           350:        if (buf == (char *)NULL) {
        !           351:                buf = malloc((unsigned)dp->d_secsize);
        !           352:                if (buf == (char *)NULL) {
        !           353:                        fprintf(stderr, "Out of memory\n");
        !           354:                        exit(20);
        !           355:                }
        !           356:        }
        !           357:        if (lseek(f, dp->d_secsize * s1, L_SET) < 0)
        !           358:                Perror("lseek");
        !           359:        for (tries = 0; tries < RETRIES; tries++)
        !           360:                if ((n = read(f, buf, dp->d_secsize)) == dp->d_secsize)
        !           361:                        break;
        !           362:        if (n != dp->d_secsize) {
        !           363:                fprintf(stderr, "bad144: can't read sector, %d: ", s1);
        !           364:                if (n < 0)
        !           365:                        perror((char *)0);
        !           366:                return(0);
        !           367:        }
        !           368:        if (lseek(f, dp->d_secsize * s2, L_SET) < 0)
        !           369:                Perror("lseek");
        !           370:        if (verbose)
        !           371:                printf("copying %d to %d\n", s1, s2);
        !           372:        if (nflag == 0 && write(f, buf, dp->d_secsize) != dp->d_secsize) {
        !           373:                fprintf(stderr,
        !           374:                    "bad144: can't write replacement sector, %d: ", s2);
        !           375:                perror((char *)0);
        !           376:                return(0);
        !           377:        }
        !           378:        return(1);
        !           379: }
        !           380: 
        !           381: char *zbuf;
        !           382: 
        !           383: blkzero(f, sn)
        !           384: daddr_t sn;
        !           385: {
        !           386: 
        !           387:        if (zbuf == (char *)NULL) {
        !           388:                zbuf = malloc((unsigned)dp->d_secsize);
        !           389:                if (zbuf == (char *)NULL) {
        !           390:                        fprintf(stderr, "Out of memory\n");
        !           391:                        exit(20);
        !           392:                }
        !           393:        }
        !           394:        if (lseek(f, dp->d_secsize * sn, L_SET) < 0)
        !           395:                Perror("lseek");
        !           396:        if (verbose)
        !           397:                printf("zeroing %d\n", sn);
        !           398:        if (nflag == 0 && write(f, zbuf, dp->d_secsize) != dp->d_secsize) {
        !           399:                fprintf(stderr,
        !           400:                    "bad144: can't write replacement sector, %d: ", sn);
        !           401:                perror((char *)0);
        !           402:        }
        !           403: }
        !           404: 
        !           405: compare(b1, b2)
        !           406: register struct bt_bad *b1, *b2;
        !           407: {
        !           408:        if (b1->bt_cyl > b2->bt_cyl)
        !           409:                return(1);
        !           410:        if (b1->bt_cyl < b2->bt_cyl)
        !           411:                return(-1);
        !           412:        return (b1->bt_trksec - b2->bt_trksec);
        !           413: }
        !           414: 
        !           415: daddr_t
        !           416: badsn(bt)
        !           417: register struct bt_bad *bt;
        !           418: {
        !           419:        return ((bt->bt_cyl*dp->d_ntracks + (bt->bt_trksec>>8)) * dp->d_nsectors
        !           420:                + (bt->bt_trksec&0xff));
        !           421: }
        !           422: 
        !           423: struct rp06hdr {
        !           424:        short   h_cyl;
        !           425:        short   h_trksec;
        !           426:        short   h_key1;
        !           427:        short   h_key2;
        !           428:        char    h_data[512];
        !           429: #define        RP06_FMT        010000          /* 1 == 16 bit, 0 == 18 bit */
        !           430: };
        !           431: 
        !           432: /*
        !           433:  * Most massbus and unibus drives
        !           434:  * have headers of this form
        !           435:  */
        !           436: struct hpuphdr {
        !           437:        u_short hpup_cyl;
        !           438:        u_char  hpup_sect;
        !           439:        u_char  hpup_track;
        !           440:        char    hpup_data[512];
        !           441: #define        HPUP_OKSECT     0xc000          /* this normally means sector is good */
        !           442: #define        HPUP_16BIT      0x1000          /* 1 == 16 bit format */
        !           443: };
        !           444: int rp06format(), hpupformat();
        !           445: 
        !           446: struct formats {
        !           447:        char    *f_name;                /* disk name */
        !           448:        int     f_bufsize;              /* size of sector + header */
        !           449:        int     f_bic;                  /* value to bic in hpup_cyl */
        !           450:        int     (*f_routine)();         /* routine for special handling */
        !           451: } formats[] = {
        !           452:        { "rp06",       sizeof (struct rp06hdr), RP06_FMT,      rp06format },
        !           453:        { "eagle",      sizeof (struct hpuphdr), HPUP_OKSECT,   hpupformat },
        !           454:        { "capricorn",  sizeof (struct hpuphdr), HPUP_OKSECT,   hpupformat },
        !           455:        { "rm03",       sizeof (struct hpuphdr), HPUP_OKSECT,   hpupformat },
        !           456:        { "rm05",       sizeof (struct hpuphdr), HPUP_OKSECT,   hpupformat },
        !           457:        { "9300",       sizeof (struct hpuphdr), HPUP_OKSECT,   hpupformat },
        !           458:        { "9766",       sizeof (struct hpuphdr), HPUP_OKSECT,   hpupformat },
        !           459:        { 0, 0, 0, 0 }
        !           460: };
        !           461: 
        !           462: /*ARGSUSED*/
        !           463: hpupformat(fp, dp, blk, buf, count)
        !           464:        struct formats *fp;
        !           465:        struct disktab *dp;
        !           466:        daddr_t blk;
        !           467:        char *buf;
        !           468:        int count;
        !           469: {
        !           470:        struct hpuphdr *hdr = (struct hpuphdr *)buf;
        !           471:        int sect;
        !           472: 
        !           473:        if (count < sizeof(struct hpuphdr)) {
        !           474:                hdr->hpup_cyl = (HPUP_OKSECT | HPUP_16BIT) |
        !           475:                        (blk / (dp->d_nsectors * dp->d_ntracks));
        !           476:                sect = blk % (dp->d_nsectors * dp->d_ntracks);
        !           477:                hdr->hpup_track = (u_char)(sect / dp->d_nsectors);
        !           478:                hdr->hpup_sect = (u_char)(sect % dp->d_nsectors);
        !           479:        }
        !           480:        return (0);
        !           481: }
        !           482: 
        !           483: /*ARGSUSED*/
        !           484: rp06format(fp, dp, blk, buf, count)
        !           485:        struct formats *fp;
        !           486:        struct disktab *dp;
        !           487:        daddr_t blk;
        !           488:        char *buf;
        !           489:        int count;
        !           490: {
        !           491: 
        !           492:        if (count < sizeof(struct rp06hdr)) {
        !           493:                fprintf(stderr, "Can't read header on blk %d, can't reformat\n",
        !           494:                        blk);
        !           495:                return (-1);
        !           496:        }
        !           497:        return (0);
        !           498: }
        !           499: 
        !           500: format(fd, blk)
        !           501:        int fd;
        !           502:        daddr_t blk;
        !           503: {
        !           504:        register struct formats *fp;
        !           505:        static char *buf;
        !           506:        static char bufsize;
        !           507:        int n;
        !           508: 
        !           509:        for (fp = formats; fp->f_name; fp++)
        !           510:                if (strcmp(dp->d_name, fp->f_name) == 0)
        !           511:                        break;
        !           512:        if (fp->f_name == 0) {
        !           513:                fprintf(stderr, "bad144: don't know how to format %s disks\n",
        !           514:                        dp->d_name);
        !           515:                exit(2);
        !           516:        }
        !           517:        if (buf && bufsize < fp->f_bufsize) {
        !           518:                free(buf);
        !           519:                buf = NULL;
        !           520:        }
        !           521:        if (buf == NULL)
        !           522:                buf = malloc((unsigned)fp->f_bufsize);
        !           523:        if (buf == NULL) {
        !           524:                fprintf(stderr, "bad144: can't allocate sector buffer\n");
        !           525:                exit(3);
        !           526:        }
        !           527:        bufsize = fp->f_bufsize;
        !           528:        /*
        !           529:         * Here we do the actual formatting.  All we really
        !           530:         * do is rewrite the sector header and flag the bad sector
        !           531:         * according to the format table description.  If a special
        !           532:         * purpose format routine is specified, we allow it to
        !           533:         * process the sector as well.
        !           534:         */
        !           535:        if (lseek(fd, (long)blk * dp->d_secsize, L_SET) < 0)
        !           536:                Perror("lseek");
        !           537:        if (verbose)
        !           538:                printf("format blk %d\n", blk);
        !           539:        if (ioctl(fd, DKIOCHDR, (char *)0) < 0)
        !           540:                Perror("ioctl");
        !           541:        if ((n = read(fd, buf, fp->f_bufsize)) < 0)
        !           542:                bzero(buf, fp->f_bufsize);
        !           543:        if (fp->f_bic) {
        !           544:                struct hpuphdr *xp = (struct hpuphdr *)buf;
        !           545: 
        !           546:                xp->hpup_cyl &= ~fp->f_bic;
        !           547:        }
        !           548:        if (fp->f_routine)
        !           549:                if ((*fp->f_routine)(fp, dp, blk, buf, n) != 0)
        !           550:                        return;
        !           551:        if (lseek(fd, (long)blk * dp->d_secsize, L_SET) < 0)
        !           552:                Perror("lseek");
        !           553:        if (nflag)
        !           554:                return;
        !           555:        if (ioctl(fd, DKIOCHDR, (char *)0) < 0)
        !           556:                Perror("ioctl");
        !           557:        if (write(fd, buf, fp->f_bufsize) != fp->f_bufsize) {
        !           558:                char msg[80];
        !           559:                (void)sprintf(msg, "bad144: write format %d", blk);
        !           560:                perror(msg);
        !           561:        }
        !           562: }
        !           563: 
        !           564: Perror(op)
        !           565:        char *op;
        !           566: {
        !           567: 
        !           568:        fprintf(stderr, "bad144: "); perror(op);
        !           569:        exit(4);
        !           570: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.