|
|
1.1 ! root 1: /* ! 2: * Copyright (c) 1980, 1986 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, 1986 Regents of the University of California.\n\ ! 10: All rights reserved.\n"; ! 11: #endif /* not lint */ ! 12: ! 13: #ifndef lint ! 14: static char sccsid[] = "@(#)format.c 7.5 (Berkeley) 4/4/90"; ! 15: #endif /* not lint */ ! 16: ! 17: /* ! 18: * Standalone program to do media checking ! 19: * and record bad block information on any ! 20: * disk with the appropriate driver and RM03-style headers. ! 21: * TODO: ! 22: * add new bad sectors to bad-sector table when formatting by track ! 23: * (rearranging replacements ala bad144 -a) ! 24: * multi-pass format for disks with skip-sector capability ! 25: */ ! 26: #include "param.h" ! 27: #include "dkbad.h" ! 28: #include "vmmac.h" ! 29: #include "disklabel.h" ! 30: ! 31: #include "../vax/cpu.h" ! 32: #include "../vax/mtpr.h" ! 33: ! 34: #include "saio.h" ! 35: #include "savax.h" ! 36: ! 37: #define MAXBADDESC 126 /* size of bad block table */ ! 38: #define CHUNK 48 /* max # of sectors/io operation */ ! 39: #define SECTSIZ 512 /* standard sector size */ ! 40: #define HDRSIZ 4 /* number of bytes in sector header */ ! 41: ! 42: #define SSERR 0 ! 43: #define BSERR 1 ! 44: ! 45: #define SSDEV(fd) (ioctl((fd), SAIOSSDEV, (char *)0) == 0) ! 46: #define MAXECCBITS 3 ! 47: ! 48: struct sector { ! 49: u_short header1; ! 50: u_short header2; ! 51: char buf[SECTSIZ]; ! 52: }; ! 53: ! 54: struct dkbad dkbad; /* bad sector table */ ! 55: struct dkbad oldbad; /* old bad sector table */ ! 56: struct dkbad sstab; /* skip sector table */ ! 57: ! 58: #define NERRORS 6 ! 59: static char * ! 60: errornames[NERRORS] = { ! 61: #define FE_BSE 0 ! 62: "Bad sector", ! 63: #define FE_WCE 1 ! 64: "Write check", ! 65: #define FE_ECC 2 ! 66: "Hard ECC", ! 67: #define FE_HARD 3 ! 68: "Other hard", ! 69: #define FE_TOTAL 4 ! 70: "Marked bad", ! 71: #define FE_SSE 5 ! 72: "Skipped", ! 73: }; ! 74: ! 75: int errors[NERRORS]; /* histogram of errors */ ! 76: int pattern; ! 77: int maxeccbits; ! 78: ! 79: /* ! 80: * Purdue/EE severe burnin patterns. ! 81: */ ! 82: unsigned short ppat[] = { ! 83: 0xf00f, 0xec6d, 0031463,0070707,0133333,0155555,0161616,0143434, ! 84: 0107070,0016161,0034343,0044444,0022222,0111111,0125252, 052525, ! 85: 0125252,0125252,0125252,0125252,0125252,0125252,0125252,0125252, ! 86: #ifndef SHORTPASS ! 87: 0125252,0125252,0125252,0125252,0125252,0125252,0125252,0125252, ! 88: 052525, 052525, 052525, 052525, 052525, 052525, 052525, 052525, ! 89: #endif ! 90: 052525, 052525, 052525, 052525, 052525, 052525, 052525, 052525 ! 91: }; ! 92: ! 93: #define NPT (sizeof (ppat) / sizeof (short)) ! 94: int maxpass, npat; /* subscript to ppat[] */ ! 95: int severe; /* nz if running "severe" burnin */ ! 96: int ssdev; /* device supports skip sectors */ ! 97: int startcyl, endcyl, starttrack, endtrack; ! 98: int nbads; /* subscript for bads */ ! 99: daddr_t bads[2*MAXBADDESC]; /* Bad blocks accumulated */ ! 100: ! 101: char *malloc(); ! 102: int qcompar(); ! 103: char *prompt(); ! 104: daddr_t badsn(); ! 105: extern int end; ! 106: ! 107: main() ! 108: { ! 109: register struct sector *hdr; ! 110: register int sector, sn, i; ! 111: struct disklabel dl; ! 112: struct sector *bp, *cbp; ! 113: int lastsector, tracksize, rtracksize; ! 114: int unit, fd, resid, trk, cyl, debug, pass; ! 115: char *cp, *rbp, *rcbp; ! 116: ! 117: printf("Disk format/check utility\n\n"); ! 118: ! 119: /* enable the cache, as every little bit helps */ ! 120: switch (cpu) { ! 121: case VAX_8600: ! 122: mtpr(CSWP, 3); ! 123: break; ! 124: case VAX_8200: ! 125: case VAX_750: ! 126: mtpr(CADR, 0); ! 127: break; ! 128: case VAX_780: ! 129: mtpr(SBIMT, 0x200000); ! 130: break; ! 131: } ! 132: ! 133: again: ! 134: nbads = 0; ! 135: cp = prompt("Enable debugging (0=none, 1=bse, 2=ecc, 3=bse+ecc)? "); ! 136: debug = atoi(cp); ! 137: if (debug < 0) ! 138: debug = 0; ! 139: for (i = 0; i < NERRORS; i++) ! 140: errors[i] = 0; ! 141: fd = getdevice(); ! 142: ioctl(fd, SAIODEVDATA, &dl); ! 143: printf("Device data: #cylinders=%d, #tracks=%d, #sectors=%d\n", ! 144: dl.d_ncylinders, dl.d_ntracks, dl.d_nsectors); ! 145: ssdev = SSDEV(fd); ! 146: if (ssdev) { ! 147: ioctl(fd, SAIOSSI, (char *)0); /* set skip sector inhibit */ ! 148: dl.d_nsectors++; ! 149: dl.d_secpercyl += dl.d_ntracks; ! 150: printf("(not counting skip-sector replacement)\n"); ! 151: } ! 152: getrange(&dl); ! 153: if (getpattern()) ! 154: goto again; ! 155: printf("Start formatting...make sure the drive is online\n"); ! 156: ioctl(fd, SAIONOBAD, (char *)0); ! 157: ioctl(fd, SAIORETRIES, (char *)0); ! 158: ioctl(fd, SAIOECCLIM, (char *)maxeccbits); ! 159: ioctl(fd, SAIODEBUG, (char *)debug); ! 160: tracksize = sizeof (struct sector) * dl.d_nsectors; ! 161: rtracksize = SECTSIZ * dl.d_nsectors; ! 162: bp = (struct sector *)malloc(tracksize); ! 163: rbp = malloc(rtracksize); ! 164: pass = 0; ! 165: npat = 0; ! 166: more: ! 167: for (; pass < maxpass; pass++) { ! 168: if (severe) ! 169: printf("Begin pass %d\n", pass); ! 170: bufinit(bp, tracksize); ! 171: if (severe) ! 172: npat++; ! 173: /* ! 174: * Begin check, for each track, ! 175: * ! 176: * 1) Write header and test pattern. ! 177: * 2) Read data. Hardware checks header and data ECC. ! 178: * Read data (esp on Eagles) is much faster than write check. ! 179: */ ! 180: sector = ((startcyl * dl.d_ntracks) + starttrack) * ! 181: dl.d_nsectors; ! 182: lastsector = ((endcyl * dl.d_ntracks) + endtrack) * ! 183: dl.d_nsectors + dl.d_nsectors; ! 184: for ( ; sector < lastsector; sector += dl.d_nsectors) { ! 185: cyl = sector / dl.d_secpercyl; ! 186: trk = ((sector % dl.d_secpercyl) / dl.d_nsectors) << 8; ! 187: for (i = 0, hdr = bp; i < dl.d_nsectors; i++, hdr++) { ! 188: hdr->header1 = cyl | HDR1_FMT22 | HDR1_OKSCT; ! 189: hdr->header2 = trk + i; ! 190: } ! 191: if (sector && (sector % (dl.d_secpercyl * 50)) == 0) ! 192: printf("cylinder %d\n", cyl); ! 193: /* ! 194: * Try and write the headers and data patterns into ! 195: * each sector in the track. Continue until such ! 196: * we're done, or until there's less than a sector's ! 197: * worth of data to transfer. ! 198: * ! 199: * The lseek call is necessary because of ! 200: * the odd sector size (516 bytes) ! 201: */ ! 202: for (resid = tracksize, cbp = bp, sn = sector;;) { ! 203: register int cc; ! 204: ! 205: lseek(fd, sn * SECTSIZ, L_SET); ! 206: ioctl(fd, SAIOHDR, (char *)0); ! 207: cc = write(fd, cbp, resid); ! 208: if (cc == resid) ! 209: break; ! 210: /* ! 211: * Don't record errors during write, ! 212: * all errors will be found during ! 213: * check performed below. ! 214: */ ! 215: sn = iob[fd - 3].i_errblk; ! 216: cbp += sn - sector; ! 217: resid -= (sn - sector) * sizeof (struct sector); ! 218: if (resid < sizeof (struct sector)) ! 219: break; ! 220: } ! 221: /* ! 222: * Read test patterns. ! 223: * Retry remainder of track on error until ! 224: * we're done, or until there's less than a ! 225: * sector to verify. ! 226: */ ! 227: for (resid = rtracksize, rcbp = rbp, sn = sector;;) { ! 228: register int cc, rsn; ! 229: ! 230: lseek(fd, sn * SECTSIZ, L_SET); ! 231: cc = read(fd, rcbp, resid); ! 232: if (cc == resid) ! 233: break; ! 234: sn = iob[fd-3].i_errblk; ! 235: if (ssdev) { ! 236: rsn = sn - (sn / dl.d_nsectors); ! 237: printf("data "); ! 238: } else ! 239: rsn = sn; ! 240: printf("sector %d, read error\n\n", rsn); ! 241: if (recorderror(fd, sn, &dl) < 0 && pass > 0) ! 242: goto out; ! 243: /* advance past bad sector */ ! 244: sn++; ! 245: resid = rtracksize - ((sn - sector) * SECTSIZ); ! 246: rcbp = rbp + ((sn - sector) * SECTSIZ); ! 247: if (resid < SECTSIZ) ! 248: break; ! 249: } ! 250: } ! 251: } ! 252: /* ! 253: * Checking finished. ! 254: */ ! 255: out: ! 256: if (severe && maxpass < NPT) { ! 257: cp = prompt("More passes? (0 or number) "); ! 258: maxpass = atoi(cp); ! 259: if (maxpass > 0) { ! 260: maxpass += pass; ! 261: goto more; ! 262: } ! 263: } ! 264: if (severe && nbads) { ! 265: /* ! 266: * Sort bads and insert in bad block table. ! 267: */ ! 268: qsort(bads, nbads, sizeof (daddr_t), qcompar); ! 269: severe = 0; ! 270: errno = 0; ! 271: for (i = 0; i < nbads; i++) ! 272: recorderror(fd, bads[i], &dl); ! 273: severe++; ! 274: } ! 275: if (errors[FE_TOTAL] || errors[FE_SSE]) { ! 276: /* change the headers of all the bad sectors */ ! 277: writebb(fd, errors[FE_SSE], &sstab, &dl, SSERR); ! 278: writebb(fd, errors[FE_TOTAL], &dkbad, &dl, BSERR); ! 279: } ! 280: if (errors[FE_TOTAL] || errors[FE_SSE]) { ! 281: printf("Errors:\n"); ! 282: for (i = 0; i < NERRORS; i++) ! 283: printf("%s: %d\n", errornames[i], errors[i]); ! 284: printf("Total of %d hard errors revectored\n", ! 285: errors[FE_TOTAL] + errors[FE_SSE]); ! 286: } ! 287: if (endcyl == dl.d_ncylinders - 1 && ! 288: (startcyl < dl.d_ncylinders - 1 || starttrack == 0)) { ! 289: while (errors[FE_TOTAL] < MAXBADDESC) { ! 290: int i = errors[FE_TOTAL]++; ! 291: ! 292: dkbad.bt_bad[i].bt_cyl = -1; ! 293: dkbad.bt_bad[i].bt_trksec = -1; ! 294: } ! 295: printf("\nWriting bad sector table at sector #%d\n", ! 296: dl.d_ncylinders * dl.d_secpercyl - dl.d_nsectors); ! 297: /* place on disk */ ! 298: for (i = 0; i < 10 && i < dl.d_nsectors; i += 2) { ! 299: lseek(fd, SECTSIZ * (dl.d_ncylinders * ! 300: dl.d_secpercyl - dl.d_nsectors + i), 0); ! 301: write(fd, &dkbad, sizeof (dkbad)); ! 302: } ! 303: } else if (errors[FE_TOTAL]) { ! 304: struct bt_bad *bt; ! 305: ! 306: printf("New bad sectors (not added to table):\n"); ! 307: bt = dkbad.bt_bad; ! 308: for (i = 0; i < errors[FE_TOTAL]; i++) { ! 309: printf("bn %d (cn=%d, tn=%d, sn=%d)\n", badsn(bt, &dl), ! 310: bt->bt_cyl, bt->bt_trksec>>8, bt->bt_trksec&0xff); ! 311: bt++; ! 312: } ! 313: } ! 314: printf("Done\n"); ! 315: ioctl(fd,SAIONOSSI,(char *)0); ! 316: close(fd); ! 317: #ifndef JUSTEXIT ! 318: goto again; ! 319: #endif ! 320: } ! 321: ! 322: qcompar(l1, l2) ! 323: register daddr_t *l1, *l2; ! 324: { ! 325: if (*l1 < *l2) ! 326: return(-1); ! 327: if (*l1 == *l2) ! 328: return(0); ! 329: return(1); ! 330: } ! 331: ! 332: daddr_t ! 333: badsn(bt, lp) ! 334: register struct bt_bad *bt; ! 335: register struct disklabel *lp; ! 336: { ! 337: register int ssoff = ssdev ? 1 : 0; ! 338: ! 339: return ((bt->bt_cyl * lp->d_ntracks + (bt->bt_trksec >> 8)) * ! 340: (lp->d_nsectors - ssoff) + (bt->bt_trksec & 0xff) - ssoff); ! 341: } ! 342: ! 343: /* ! 344: * Mark the bad/skipped sectors. ! 345: * Bad sectors on skip-sector devices are assumed to be skipped also, ! 346: * and must be done after the (earlier) first skipped sector. ! 347: */ ! 348: writebb(fd, nsects, dbad, lp, sw) ! 349: int nsects, fd; ! 350: struct dkbad *dbad; ! 351: register struct disklabel *lp; ! 352: { ! 353: struct sector bb_buf; /* buffer for one sector plus 4 byte header */ ! 354: register int i; ! 355: int bn, j; ! 356: struct bt_bad *btp; ! 357: ! 358: for (i = 0; i < nsects; i++) { ! 359: btp = &dbad->bt_bad[i]; ! 360: if (sw == BSERR) { ! 361: bb_buf.header1 = HDR1_FMT22|btp->bt_cyl; ! 362: if (ssdev) ! 363: bb_buf.header1 |= HDR1_SSF; ! 364: } else ! 365: bb_buf.header1 = ! 366: btp->bt_cyl | HDR1_FMT22 | HDR1_SSF | HDR1_OKSCT; ! 367: bb_buf.header2 = btp->bt_trksec; ! 368: bn = lp->d_secpercyl * btp->bt_cyl + ! 369: lp->d_nsectors * (btp->bt_trksec >> 8) + ! 370: (btp->bt_trksec & 0xff); ! 371: lseek(fd, bn * SECTSIZ, L_SET); ! 372: ioctl(fd, SAIOHDR, (char *)0); ! 373: write(fd, &bb_buf, sizeof (bb_buf)); ! 374: /* ! 375: * If skip sector, mark all remaining ! 376: * sectors on the track. ! 377: */ ! 378: if (sw == SSERR) { ! 379: for (j = (btp->bt_trksec & 0xff) + 1, bn++; ! 380: j < lp->d_nsectors; j++, bn++) { ! 381: bb_buf.header2 = j | (btp->bt_trksec & 0xff00); ! 382: lseek(fd, bn * SECTSIZ, L_SET); ! 383: ioctl(fd, SAIOHDR, (char *)0); ! 384: write(fd, &bb_buf, sizeof (bb_buf)); ! 385: } ! 386: } ! 387: } ! 388: } ! 389: ! 390: /* ! 391: * Record an error, and if there's room, put ! 392: * it in the appropriate bad sector table. ! 393: * ! 394: * If severe burnin store block in a list after making sure ! 395: * we have not already found it on a prev pass. ! 396: */ ! 397: recorderror(fd, bn, lp) ! 398: int fd, bn; ! 399: register struct disklabel *lp; ! 400: { ! 401: int cn, tn, sn; ! 402: register int i; ! 403: ! 404: if (severe) { ! 405: for (i = 0; i < nbads; i++) ! 406: if (bads[i] == bn) ! 407: return(0); /* bn already flagged */ ! 408: if (nbads >= (ssdev ? 2 * MAXBADDESC : MAXBADDESC)) { ! 409: printf("Bad sector table full, format terminating\n"); ! 410: return(-1); ! 411: } ! 412: bads[nbads++] = bn; ! 413: if (errno < EBSE || errno > EHER) ! 414: return(0); ! 415: errno -= EBSE; ! 416: errors[errno]++; ! 417: return(0); ! 418: } ! 419: if (errno >= EBSE && errno <= EHER) { ! 420: errno -= EBSE; ! 421: errors[errno]++; ! 422: } ! 423: cn = bn / lp->d_secpercyl; ! 424: sn = bn % lp->d_secpercyl; ! 425: tn = sn / lp->d_nsectors; ! 426: sn %= lp->d_nsectors; ! 427: if (ssdev) { /* if drive has skip sector capability */ ! 428: int ss = errors[FE_SSE]; ! 429: ! 430: if (errors[FE_SSE] >= MAXBADDESC) { ! 431: /* this is bogus, we don't maintain skip sector table */ ! 432: printf("Too many skip sector errors\n"); ! 433: return(-1); ! 434: } ! 435: /* only one skip sector/track */ ! 436: if (ss == 0 || ! 437: tn != (sstab.bt_bad[ss - 1].bt_trksec >> 8) || ! 438: cn != sstab.bt_bad[ss - 1].bt_cyl) { ! 439: /* ! 440: * Don't bother with skipping the extra sector ! 441: * at the end of the track. ! 442: */ ! 443: if (sn == lp->d_nsectors - 1) ! 444: return(0); ! 445: sstab.bt_bad[ss].bt_cyl = cn; ! 446: sstab.bt_bad[ss].bt_trksec = (tn<<8) + sn; ! 447: errors[FE_SSE]++; ! 448: return(0); ! 449: } ! 450: } ! 451: if (errors[FE_TOTAL] >= MAXBADDESC) { ! 452: printf("Too many bad sectors\n"); ! 453: return(-1); ! 454: } ! 455: /* record the bad sector address and continue */ ! 456: dkbad.bt_bad[errors[FE_TOTAL]].bt_cyl = cn; ! 457: dkbad.bt_bad[errors[FE_TOTAL]++].bt_trksec = (tn << 8) + sn; ! 458: return(0); ! 459: } ! 460: ! 461: /* ! 462: * Allocate memory on a page-aligned address. ! 463: * Round allocated chunk to a page multiple to ! 464: * ease next request. ! 465: */ ! 466: char * ! 467: malloc(size) ! 468: int size; ! 469: { ! 470: char *result; ! 471: static caddr_t last = 0; ! 472: ! 473: if (last == 0) ! 474: last = (caddr_t)(((int)&end + 511) & ~0x1ff); ! 475: size = (size + 511) & ~0x1ff; ! 476: result = (char *)last; ! 477: last += size; ! 478: return (result); ! 479: } ! 480: ! 481: /* ! 482: * Prompt and verify a device name from the user. ! 483: */ ! 484: getdevice() ! 485: { ! 486: register char *cp; ! 487: int fd; ! 488: ! 489: top: ! 490: do { ! 491: printf( ! 492: "Enter device name as \"type(adaptor,controller,drive,0)\"\n"); ! 493: cp = prompt("Device to format? "); ! 494: } while ((fd = open(cp, 2)) < 0); ! 495: printf("Formatting %c%c drive %d on controller %d, adaptor %d: ", ! 496: cp[0], cp[1], iob[fd - 3].i_unit, ! 497: iob[fd - 3].i_ctlr, iob[fd - 3].i_adapt); ! 498: cp = prompt("verify (yes/no)? "); ! 499: while (*cp != 'y' && *cp != 'n') ! 500: cp = prompt("Huh, yes or no? "); ! 501: if (*cp == 'y') ! 502: return (fd); ! 503: goto top; ! 504: } ! 505: ! 506: /* ! 507: * Find range of tracks to format. ! 508: */ ! 509: getrange(lp) ! 510: register struct disklabel *lp; ! 511: { ! 512: startcyl = getnum("Starting cylinder", 0, lp->d_ncylinders - 1, 0); ! 513: starttrack = getnum("Starting track", 0, lp->d_ntracks - 1, 0); ! 514: endcyl = getnum("Ending cylinder", 0, lp->d_ncylinders - 1, ! 515: lp->d_ncylinders - 1); ! 516: endtrack = getnum("Ending track", 0, lp->d_ntracks - 1, ! 517: lp->d_ntracks - 1); ! 518: } ! 519: ! 520: getnum(s, low, high, dflt) ! 521: int s, low, high, dflt; ! 522: { ! 523: char buf[132]; ! 524: u_int val; ! 525: ! 526: for(;;) { ! 527: printf("%s (%d): ", s, dflt); ! 528: gets(buf); ! 529: if (buf[0] == 0) ! 530: return (dflt); ! 531: val = atoi(buf); ! 532: if (val >= low && val <= high) ! 533: return ((int)val); ! 534: printf("Value must be in range [%d,%d]\n", low, high); ! 535: } ! 536: } ! 537: ! 538: static struct pattern { ! 539: long pa_value; ! 540: char *pa_name; ! 541: } pat[] = { ! 542: { 0xf00ff00f, "RH750 worst case" }, ! 543: { 0xec6dec6d, "media worst case" }, ! 544: { 0xa5a5a5a5, "alternate 1's and 0's" }, ! 545: { 0xFFFFFFFF, "Severe burnin (up to 48 passes)" }, ! 546: { 0, 0 }, ! 547: }; ! 548: ! 549: getpattern() ! 550: { ! 551: register struct pattern *p; ! 552: int npatterns; ! 553: char *cp; ! 554: ! 555: printf("Available test patterns are:\n"); ! 556: for (p = pat; p->pa_value; p++) ! 557: printf("\t%d - (%x) %s\n", (p - pat) + 1, ! 558: p->pa_value & 0xffff, p->pa_name); ! 559: npatterns = p - pat; ! 560: cp = prompt("Pattern (one of the above, other to restart)? "); ! 561: pattern = atoi(cp) - 1; ! 562: if (pattern < 0 || pattern >= npatterns) ! 563: return(1); ! 564: severe = 0; ! 565: maxpass = 1; ! 566: if (pat[pattern].pa_value == -1) { ! 567: severe = 1; ! 568: cp = prompt("How many passes (up to 48)? "); ! 569: maxpass = atoi(cp); ! 570: if (maxpass > NPT) ! 571: maxpass = NPT; ! 572: } ! 573: maxeccbits = getnum( ! 574: "Maximum number of bit errors to allow for soft ECC", ! 575: 0, 11, MAXECCBITS); ! 576: return (0); ! 577: } ! 578: ! 579: struct xsect { ! 580: u_short hd1; ! 581: u_short hd2; ! 582: long buf[128]; ! 583: }; ! 584: ! 585: /* ! 586: * Initialize the buffer with the requested pattern. ! 587: */ ! 588: bufinit(bp, size) ! 589: register struct xsect *bp; ! 590: int size; ! 591: { ! 592: register struct pattern *pptr; ! 593: register long *pp, *last; ! 594: register struct xsect *lastbuf; ! 595: int patt; ! 596: ! 597: size /= sizeof (struct sector); ! 598: lastbuf = bp + size; ! 599: if (severe) { ! 600: patt = ppat[npat] | ((long)ppat[npat] << 16); ! 601: printf("Write pattern 0x%x\n", patt&0xffff); ! 602: } else { ! 603: pptr = &pat[pattern]; ! 604: patt = pptr->pa_value; ! 605: } ! 606: while (bp < lastbuf) { ! 607: last = &bp->buf[128]; ! 608: for (pp = bp->buf; pp < last; pp++) ! 609: *pp = patt; ! 610: bp++; ! 611: } ! 612: } ! 613: ! 614: char * ! 615: prompt(msg) ! 616: char *msg; ! 617: { ! 618: static char buf[132]; ! 619: ! 620: printf("%s", msg); ! 621: gets(buf); ! 622: return (buf); ! 623: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.