|
|
1.1 ! root 1: /* ! 2: * Copyright (c) 1988 Regents of the University of California. ! 3: * All rights reserved. ! 4: * ! 5: * Redistribution and use in source and binary forms are permitted ! 6: * provided that: (1) source distributions retain this entire copyright ! 7: * notice and comment, and (2) distributions including binaries display ! 8: * the following acknowledgement: ``This product includes software ! 9: * developed by the University of California, Berkeley and its contributors'' ! 10: * in the documentation or other materials provided with the distribution ! 11: * and in all advertising materials mentioning features or use of this ! 12: * software. Neither the name of the University nor the names of its ! 13: * contributors may be used to endorse or promote products derived ! 14: * from this software without specific prior written permission. ! 15: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ! 16: * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ! 17: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ! 18: */ ! 19: ! 20: #ifndef lint ! 21: static char *sccsid = "@(#)doprnt.c 5.6 (Berkeley) 5/31/90"; ! 22: #endif /* not lint */ ! 23: ! 24: #ifdef notdef ! 25: static char sccsid[] = "@(#)doprnt.c 5.32 (Berkeley) 6/3/88"; ! 26: #endif ! 27: ! 28: #include <sys/types.h> ! 29: #include <varargs.h> ! 30: #include <stdio.h> ! 31: #include <ctype.h> ! 32: ! 33: /* 11-bit exponent (VAX G floating point) is 308 decimal digits */ ! 34: #define MAXEXP 308 ! 35: /* 128 bit fraction takes up 39 decimal digits; max reasonable precision */ ! 36: #define MAXFRACT 39 ! 37: ! 38: #define DEFPREC 6 ! 39: ! 40: #define BUF (MAXEXP+MAXFRACT+1) /* + decimal point */ ! 41: ! 42: #ifdef CSH_WHACKS ! 43: #define PUTC(ch) (void) putc(ch, fp) ! 44: #else ! 45: #include "sh.h" ! 46: #define PUTC(c) { ch = c; CSHPUTCHAR; } ! 47: #endif ! 48: ! 49: #define ARG() \ ! 50: _ulong = flags&LONGINT ? va_arg(argp, long) : \ ! 51: flags&SHORTINT ? va_arg(argp, short) : va_arg(argp, int); ! 52: ! 53: #define todigit(c) ((c) - '0') ! 54: #define tochar(n) ((n) + '0') ! 55: ! 56: /* have to deal with the negative buffer count kludge */ ! 57: #define NEGATIVE_COUNT_KLUDGE ! 58: ! 59: #define LONGINT 0x01 /* long integer */ ! 60: #define LONGDBL 0x02 /* long double; unimplemented */ ! 61: #define SHORTINT 0x04 /* short integer */ ! 62: #define ALT 0x08 /* alternate form */ ! 63: #define LADJUST 0x10 /* left adjustment */ ! 64: #define ZEROPAD 0x20 /* zero (as opposed to blank) pad */ ! 65: #define HEXPREFIX 0x40 /* add 0x or 0X prefix */ ! 66: ! 67: _doprnt(fmt0, argp, fp) ! 68: u_char *fmt0; ! 69: va_list argp; ! 70: register FILE *fp; ! 71: { ! 72: register u_char *fmt; /* format string */ ! 73: register int ch; /* character from fmt */ ! 74: register int cnt; /* return value accumulator */ ! 75: register int n; /* random handy integer */ ! 76: register char *t; /* buffer pointer */ ! 77: double _double; /* double precision arguments %[eEfgG] */ ! 78: u_long _ulong; /* integer arguments %[diouxX] */ ! 79: int base; /* base for [diouxX] conversion */ ! 80: int dprec; /* decimal precision in [diouxX] */ ! 81: int fieldsz; /* field size expanded by sign, etc */ ! 82: int flags; /* flags as above */ ! 83: int fpprec; /* `extra' floating precision in [eEfgG] */ ! 84: int prec; /* precision from format (%.3d), or -1 */ ! 85: int realsz; /* field size expanded by decimal precision */ ! 86: int size; /* size of converted field or string */ ! 87: int width; /* width from format (%8d), or 0 */ ! 88: char sign; /* sign prefix (+ - or \0) */ ! 89: char *digs; /* digits for [diouxX] conversion */ ! 90: char buf[BUF]; /* space for %c, %[diouxX], %[eEfgG] */ ! 91: ! 92: if (fp->_flag & _IORW) { ! 93: fp->_flag |= _IOWRT; ! 94: fp->_flag &= ~(_IOEOF|_IOREAD); ! 95: } ! 96: if ((fp->_flag & _IOWRT) == 0) ! 97: return (EOF); ! 98: ! 99: fmt = fmt0; ! 100: digs = "0123456789abcdef"; ! 101: for (cnt = 0;; ++fmt) { ! 102: #ifdef CSH_WHACKS ! 103: n = fp->_cnt; ! 104: for (t = (char *)fp->_ptr; (ch = *fmt) && ch != '%'; ! 105: ++cnt, ++fmt) ! 106: if (--n < 0 ! 107: #ifdef NEGATIVE_COUNT_KLUDGE ! 108: && (!(fp->_flag & _IOLBF) || -n >= fp->_bufsiz) ! 109: #endif ! 110: || ch == '\n' && fp->_flag & _IOLBF) { ! 111: fp->_cnt = n; ! 112: fp->_ptr = t; ! 113: (void) _flsbuf((u_char)ch, fp); ! 114: n = fp->_cnt; ! 115: t = (char *)fp->_ptr; ! 116: } else ! 117: *t++ = ch; ! 118: fp->_cnt = n; ! 119: fp->_ptr = t; ! 120: #else ! 121: if ((ch = *fmt) && ch != '%') { ! 122: CSHPUTCHAR; ! 123: continue; ! 124: } ! 125: #endif ! 126: if (!ch) ! 127: return (cnt); ! 128: ! 129: flags = dprec = fpprec = width = 0; ! 130: prec = -1; ! 131: sign = '\0'; ! 132: ! 133: rflag: switch (*++fmt) { ! 134: case ' ': ! 135: sign = ' '; ! 136: goto rflag; ! 137: case '#': ! 138: flags |= ALT; ! 139: goto rflag; ! 140: case '*': ! 141: /* ! 142: * ``A negative field width argument is taken as a ! 143: * - flag followed by a positive field width.'' ! 144: * -- ANSI X3J11 ! 145: * They don't exclude field widths read from args. ! 146: */ ! 147: if ((width = va_arg(argp, int)) >= 0) ! 148: goto rflag; ! 149: width = -width; ! 150: /* FALLTHROUGH */ ! 151: case '-': ! 152: flags |= LADJUST; ! 153: goto rflag; ! 154: case '+': ! 155: sign = '+'; ! 156: goto rflag; ! 157: case '.': ! 158: if (*++fmt == '*') ! 159: n = va_arg(argp, int); ! 160: else { ! 161: n = 0; ! 162: while (isascii(*fmt) && isdigit(*fmt)) ! 163: n = 10 * n + todigit(*fmt++); ! 164: --fmt; ! 165: } ! 166: prec = n < 0 ? -1 : n; ! 167: goto rflag; ! 168: case '0': ! 169: /* ! 170: * ``Note that 0 is taken as a flag, not as the ! 171: * beginning of a field width.'' ! 172: * -- ANSI X3J11 ! 173: */ ! 174: flags |= ZEROPAD; ! 175: goto rflag; ! 176: case '1': case '2': case '3': case '4': ! 177: case '5': case '6': case '7': case '8': case '9': ! 178: n = 0; ! 179: do { ! 180: n = 10 * n + todigit(*fmt); ! 181: } while (isascii(*++fmt) && isdigit(*fmt)); ! 182: width = n; ! 183: --fmt; ! 184: goto rflag; ! 185: case 'L': ! 186: flags |= LONGDBL; ! 187: goto rflag; ! 188: case 'h': ! 189: flags |= SHORTINT; ! 190: goto rflag; ! 191: case 'l': ! 192: flags |= LONGINT; ! 193: goto rflag; ! 194: case 'c': ! 195: *(t = buf) = va_arg(argp, int); ! 196: size = 1; ! 197: sign = '\0'; ! 198: goto pforw; ! 199: case 'D': ! 200: flags |= LONGINT; ! 201: /*FALLTHROUGH*/ ! 202: case 'd': ! 203: case 'i': ! 204: ARG(); ! 205: if ((long)_ulong < 0) { ! 206: _ulong = -_ulong; ! 207: sign = '-'; ! 208: } ! 209: base = 10; ! 210: goto number; ! 211: case 'e': ! 212: case 'E': ! 213: case 'f': ! 214: case 'g': ! 215: case 'G': ! 216: _double = va_arg(argp, double); ! 217: /* ! 218: * don't bother to do unrealistic precision; just ! 219: * pad it with zeroes later. This keeps buffer size ! 220: * rational. ! 221: */ ! 222: if (prec > MAXFRACT) { ! 223: if (*fmt != 'g' && *fmt != 'G' || (flags&ALT)) ! 224: fpprec = prec - MAXFRACT; ! 225: prec = MAXFRACT; ! 226: } ! 227: else if (prec == -1) ! 228: prec = DEFPREC; ! 229: if (_double < 0) { ! 230: sign = '-'; ! 231: _double = -_double; ! 232: } ! 233: /* ! 234: * _cvt may have to round up past the "start" of the ! 235: * buffer, i.e. ``intf("%.2f", (double)9.999);''; ! 236: * if the first char isn't NULL, it did. ! 237: */ ! 238: *buf = NULL; ! 239: size = _cvt(_double, prec, flags, *fmt, buf, ! 240: buf + sizeof(buf)); ! 241: t = *buf ? buf : buf + 1; ! 242: goto pforw; ! 243: case 'n': ! 244: if (flags & LONGINT) ! 245: *va_arg(argp, long *) = cnt; ! 246: else if (flags & SHORTINT) ! 247: *va_arg(argp, short *) = cnt; ! 248: else ! 249: *va_arg(argp, int *) = cnt; ! 250: break; ! 251: case 'O': ! 252: flags |= LONGINT; ! 253: /*FALLTHROUGH*/ ! 254: case 'o': ! 255: ARG(); ! 256: base = 8; ! 257: goto nosign; ! 258: case 'p': ! 259: /* ! 260: * ``The argument shall be a pointer to void. The ! 261: * value of the pointer is converted to a sequence ! 262: * of printable characters, in an implementation- ! 263: * defined manner.'' ! 264: * -- ANSI X3J11 ! 265: */ ! 266: /* NOSTRICT */ ! 267: _ulong = (u_long)va_arg(argp, void *); ! 268: base = 16; ! 269: goto nosign; ! 270: case 's': ! 271: if (!(t = va_arg(argp, char *))) ! 272: t = "(null)"; ! 273: if (prec >= 0) { ! 274: /* ! 275: * can't use strlen; can only look for the ! 276: * NUL in the first `prec' characters, and ! 277: * strlen() will go further. ! 278: */ ! 279: char *p, *memchr(); ! 280: ! 281: if (p = memchr(t, 0, prec)) { ! 282: size = p - t; ! 283: if (size > prec) ! 284: size = prec; ! 285: } else ! 286: size = prec; ! 287: } else ! 288: size = strlen(t); ! 289: sign = '\0'; ! 290: goto pforw; ! 291: case 'U': ! 292: flags |= LONGINT; ! 293: /*FALLTHROUGH*/ ! 294: case 'u': ! 295: ARG(); ! 296: base = 10; ! 297: goto nosign; ! 298: case 'X': ! 299: digs = "0123456789ABCDEF"; ! 300: /* FALLTHROUGH */ ! 301: case 'x': ! 302: ARG(); ! 303: base = 16; ! 304: /* leading 0x/X only if non-zero */ ! 305: if (flags & ALT && _ulong != 0) ! 306: flags |= HEXPREFIX; ! 307: ! 308: /* unsigned conversions */ ! 309: nosign: sign = '\0'; ! 310: /* ! 311: * ``... diouXx conversions ... if a precision is ! 312: * specified, the 0 flag will be ignored.'' ! 313: * -- ANSI X3J11 ! 314: */ ! 315: number: if ((dprec = prec) >= 0) ! 316: flags &= ~ZEROPAD; ! 317: ! 318: /* ! 319: * ``The result of converting a zero value with an ! 320: * explicit precision of zero is no characters.'' ! 321: * -- ANSI X3J11 ! 322: */ ! 323: t = buf + BUF; ! 324: if (_ulong != 0 || prec != 0) { ! 325: do { ! 326: *--t = digs[_ulong % base]; ! 327: _ulong /= base; ! 328: } while (_ulong); ! 329: digs = "0123456789abcdef"; ! 330: if (flags & ALT && base == 8 && *t != '0') ! 331: *--t = '0'; /* octal leading 0 */ ! 332: } ! 333: size = buf + BUF - t; ! 334: ! 335: pforw: ! 336: /* ! 337: * All reasonable formats wind up here. At this point, ! 338: * `t' points to a string which (if not flags&LADJUST) ! 339: * should be padded out to `width' places. If ! 340: * flags&ZEROPAD, it should first be prefixed by any ! 341: * sign or other prefix; otherwise, it should be blank ! 342: * padded before the prefix is emitted. After any ! 343: * left-hand padding and prefixing, emit zeroes ! 344: * required by a decimal [diouxX] precision, then print ! 345: * the string proper, then emit zeroes required by any ! 346: * leftover floating precision; finally, if LADJUST, ! 347: * pad with blanks. ! 348: */ ! 349: ! 350: /* ! 351: * compute actual size, so we know how much to pad ! 352: * fieldsz excludes decimal prec; realsz includes it ! 353: */ ! 354: fieldsz = size + fpprec; ! 355: if (sign) ! 356: fieldsz++; ! 357: if (flags & HEXPREFIX) ! 358: fieldsz += 2; ! 359: realsz = dprec > fieldsz ? dprec : fieldsz; ! 360: ! 361: /* right-adjusting blank padding */ ! 362: if ((flags & (LADJUST|ZEROPAD)) == 0 && width) ! 363: for (n = realsz; n < width; n++) ! 364: PUTC(' '); ! 365: /* prefix */ ! 366: if (sign) ! 367: PUTC(sign); ! 368: if (flags & HEXPREFIX) { ! 369: PUTC('0'); ! 370: PUTC((char)*fmt); ! 371: } ! 372: /* right-adjusting zero padding */ ! 373: if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD) ! 374: for (n = realsz; n < width; n++) ! 375: PUTC('0'); ! 376: /* leading zeroes from decimal precision */ ! 377: for (n = fieldsz; n < dprec; n++) ! 378: PUTC('0'); ! 379: ! 380: /* the string or number proper */ ! 381: #ifdef CSH_WHACKS ! 382: if (fp->_cnt - (n = size) >= 0 && ! 383: (fp->_flag & _IOLBF) == 0) { ! 384: fp->_cnt -= n; ! 385: bcopy(t, (char *)fp->_ptr, n); ! 386: fp->_ptr += n; ! 387: } else ! 388: while (--n >= 0) ! 389: #else ! 390: for (n = size; --n >= 0;) ! 391: #endif ! 392: PUTC(*t++); ! 393: /* trailing f.p. zeroes */ ! 394: while (--fpprec >= 0) ! 395: PUTC('0'); ! 396: /* left-adjusting padding (always blank) */ ! 397: if (flags & LADJUST) ! 398: for (n = realsz; n < width; n++) ! 399: PUTC(' '); ! 400: /* finally, adjust cnt */ ! 401: cnt += width > realsz ? width : realsz; ! 402: break; ! 403: case '\0': /* "%?" prints ?, unless ? is NULL */ ! 404: return (cnt); ! 405: default: ! 406: PUTC((char)*fmt); ! 407: cnt++; ! 408: } ! 409: } ! 410: /* NOTREACHED */ ! 411: } ! 412: ! 413: static ! 414: _cvt(number, prec, flags, fmtch, startp, endp) ! 415: double number; ! 416: register int prec; ! 417: int flags; ! 418: u_char fmtch; ! 419: char *startp, *endp; ! 420: { ! 421: register char *p, *t; ! 422: double fract, integer, tmp, modf(); ! 423: int dotrim, expcnt, gformat; ! 424: char *exponent(), *eround(), *fround(); ! 425: ! 426: dotrim = expcnt = gformat = 0; ! 427: fract = modf(number, &integer); ! 428: ! 429: /* get an extra slot for rounding. */ ! 430: t = ++startp; ! 431: ! 432: /* ! 433: * get integer portion of number; put into the end of the buffer; the ! 434: * .01 is added for modf(356.0 / 10, &integer) returning .59999999... ! 435: */ ! 436: for (p = endp - 1; integer; ++expcnt) { ! 437: tmp = modf(integer / 10, &integer); ! 438: *p-- = tochar((int)((tmp + .01) * 10)); ! 439: } ! 440: switch(fmtch) { ! 441: case 'f': ! 442: /* reverse integer into beginning of buffer */ ! 443: if (expcnt) ! 444: for (; ++p < endp; *t++ = *p); ! 445: else ! 446: *t++ = '0'; ! 447: /* ! 448: * if precision required or alternate flag set, add in a ! 449: * decimal point. ! 450: */ ! 451: if (prec || flags&ALT) ! 452: *t++ = '.'; ! 453: /* if requires more precision and some fraction left */ ! 454: if (fract) { ! 455: if (prec) ! 456: do { ! 457: fract = modf(fract * 10, &tmp); ! 458: *t++ = tochar((int)tmp); ! 459: } while (--prec && fract); ! 460: if (fract) ! 461: startp = fround(startp, t - 1, fract); ! 462: } ! 463: for (; prec--; *t++ = '0'); ! 464: break; ! 465: case 'e': ! 466: case 'E': ! 467: eformat: if (expcnt) { ! 468: *t++ = *++p; ! 469: if (prec || flags&ALT) ! 470: *t++ = '.'; ! 471: /* if requires more precision and some integer left */ ! 472: for (; prec && ++p < endp; --prec) ! 473: *t++ = *p; ! 474: /* ! 475: * if done precision and more of the integer component, ! 476: * round using it; adjust fract so we don't re-round ! 477: * later. ! 478: */ ! 479: if (!prec && ++p < endp) { ! 480: startp = eround(startp, t - 1, (double)0, ! 481: *p, &expcnt); ! 482: fract = 0; ! 483: } ! 484: /* adjust expcnt for digit in front of decimal */ ! 485: --expcnt; ! 486: } ! 487: /* until first fractional digit, decrement exponent */ ! 488: else if (fract) { ! 489: /* adjust expcnt for digit in front of decimal */ ! 490: for (expcnt = -1;; --expcnt) { ! 491: fract = modf(fract * 10, &tmp); ! 492: if (tmp) ! 493: break; ! 494: } ! 495: *t++ = tochar((int)tmp); ! 496: if (prec || flags&ALT) ! 497: *t++ = '.'; ! 498: } ! 499: else { ! 500: *t++ = '0'; ! 501: if (prec || flags&ALT) ! 502: *t++ = '.'; ! 503: } ! 504: /* if requires more precision and some fraction left */ ! 505: if (fract) { ! 506: if (prec) ! 507: do { ! 508: fract = modf(fract * 10, &tmp); ! 509: *t++ = tochar((int)tmp); ! 510: } while (--prec && fract); ! 511: if (fract) ! 512: startp = eround(startp, t - 1, fract, ! 513: (char)0, &expcnt); ! 514: } ! 515: /* if requires more precision */ ! 516: for (; prec--; *t++ = '0'); ! 517: ! 518: /* unless alternate flag, trim any g/G format trailing 0's */ ! 519: if (gformat && !(flags&ALT)) { ! 520: while (t > startp && *--t == '0'); ! 521: if (*t == '.') ! 522: --t; ! 523: ++t; ! 524: } ! 525: t = exponent(t, expcnt, fmtch); ! 526: break; ! 527: case 'g': ! 528: case 'G': ! 529: /* a precision of 0 is treated as a precision of 1. */ ! 530: if (!prec) ! 531: ++prec; ! 532: /* ! 533: * ``The style used depends on the value converted; style e ! 534: * will be used only if the exponent resulting from the ! 535: * conversion is less than -4 or greater than the precision.'' ! 536: * -- ANSI X3J11 ! 537: */ ! 538: if (expcnt > prec || !expcnt && fract && fract < .0001) { ! 539: /* ! 540: * g/G format counts "significant digits, not digits of ! 541: * precision; for the e/E format, this just causes an ! 542: * off-by-one problem, i.e. g/G considers the digit ! 543: * before the decimal point significant and e/E doesn't ! 544: * count it as precision. ! 545: */ ! 546: --prec; ! 547: fmtch -= 2; /* G->E, g->e */ ! 548: gformat = 1; ! 549: goto eformat; ! 550: } ! 551: /* ! 552: * reverse integer into beginning of buffer, ! 553: * note, decrement precision ! 554: */ ! 555: if (expcnt) ! 556: for (; ++p < endp; *t++ = *p, --prec); ! 557: else ! 558: *t++ = '0'; ! 559: /* ! 560: * if precision required or alternate flag set, add in a ! 561: * decimal point. If no digits yet, add in leading 0. ! 562: */ ! 563: if (prec || flags&ALT) { ! 564: dotrim = 1; ! 565: *t++ = '.'; ! 566: } ! 567: else ! 568: dotrim = 0; ! 569: /* if requires more precision and some fraction left */ ! 570: if (fract) { ! 571: if (prec) { ! 572: do { ! 573: fract = modf(fract * 10, &tmp); ! 574: *t++ = tochar((int)tmp); ! 575: } while(!tmp); ! 576: while (--prec && fract) { ! 577: fract = modf(fract * 10, &tmp); ! 578: *t++ = tochar((int)tmp); ! 579: } ! 580: } ! 581: if (fract) ! 582: startp = fround(startp, t - 1, fract); ! 583: } ! 584: /* alternate format, adds 0's for precision, else trim 0's */ ! 585: if (flags&ALT) ! 586: for (; prec--; *t++ = '0'); ! 587: else if (dotrim) { ! 588: while (t > startp && *--t == '0'); ! 589: if (*t != '.') ! 590: ++t; ! 591: } ! 592: } ! 593: return(t - startp); ! 594: } ! 595: ! 596: static char * ! 597: fround(start, end, fract) ! 598: register char *start, *end; ! 599: double fract; ! 600: { ! 601: double tmp; ! 602: ! 603: (void)modf(fract * 10, &tmp); ! 604: if (tmp > 4) ! 605: for (;; --end) { ! 606: if (*end == '.') ! 607: --end; ! 608: if (++*end <= '9') ! 609: break; ! 610: *end = '0'; ! 611: /* add extra digit to round past buffer beginning */ ! 612: if (end == start) { ! 613: *--end = '1'; ! 614: --start; ! 615: break; ! 616: } ! 617: } ! 618: return(start); ! 619: } ! 620: ! 621: static char * ! 622: eround(start, end, fract, ch, exp) ! 623: register char *start, *end; ! 624: double fract; ! 625: char ch; ! 626: int *exp; ! 627: { ! 628: double tmp; ! 629: ! 630: if (fract) ! 631: (void)modf(fract * 10, &tmp); ! 632: else ! 633: tmp = todigit(ch); ! 634: if (tmp > 4) ! 635: for (;; --end) { ! 636: if (*end == '.') ! 637: --end; ! 638: if (++*end <= '9') ! 639: break; ! 640: *end = '0'; ! 641: /* increment exponent to round past buffer beginning */ ! 642: if (end == start) { ! 643: *end = '1'; ! 644: ++*exp; ! 645: break; ! 646: } ! 647: } ! 648: return(start); ! 649: } ! 650: ! 651: static char * ! 652: exponent(p, exp, fmtch) ! 653: register char *p; ! 654: register int exp; ! 655: u_char fmtch; ! 656: { ! 657: register char *t; ! 658: char expbuf[MAXEXP]; ! 659: ! 660: *p++ = fmtch; ! 661: if (exp < 0) { ! 662: exp = -exp; ! 663: *p++ = '-'; ! 664: } ! 665: else ! 666: *p++ = '+'; ! 667: t = expbuf + MAXEXP; ! 668: if (exp > 9) { ! 669: do { ! 670: *--t = tochar(exp % 10); ! 671: } while ((exp /= 10) > 9); ! 672: *--t = tochar(exp); ! 673: for (; t < expbuf + MAXEXP; *p++ = *t++); ! 674: } ! 675: else { ! 676: *p++ = '0'; ! 677: *p++ = tochar(exp); ! 678: } ! 679: return(p); ! 680: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.