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