|
|
1.1 root 1: /* This is part of libio/iostream, providing -*- C++ -*- input/output.
2: Copyright (C) 1993 Free Software Foundation
3:
4: This file is part of the GNU IO Library. This library is free
5: software; you can redistribute it and/or modify it under the
6: terms of the GNU General Public License as published by the
7: Free Software Foundation; either version 2, or (at your option)
8: any later version.
9:
10: This library is distributed in the hope that it will be useful,
11: but WITHOUT ANY WARRANTY; without even the implied warranty of
12: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13: GNU General Public License for more details.
14:
15: You should have received a copy of the GNU General Public License
16: along with GNU CC; see the file COPYING. If not, write to
17: the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18:
19: As a special exception, if you link this library with files
20: compiled with a GNU compiler to produce an executable, this does not cause
21: the resulting executable to be covered by the GNU General Public License.
22: This exception does not however invalidate any other reasons why
23: the executable file might be covered by the GNU General Public License. */
24:
25: /* Written by Per Bothner ([email protected]). */
26:
27: #ifdef __GNUC__
28: #pragma implementation
29: #endif
30: #define _STREAM_COMPAT
31: #include <iostream.h>
32: #include "libioP.h"
33: extern "C" {
34: #include <stdio.h> /* Needed for sprintf */
35: }
36: extern "C" {
37: #include <ctype.h>
38: }
39: extern "C" {
40: #include <string.h>
41: }
42: extern "C" {
43: #include <limits.h>
44: }
45: #include "floatio.h"
46:
47: #define BUF (MAXEXP+MAXFRACT+1) /* + decimal point */
48:
49: //#define isspace(ch) ((ch)==' ' || (ch)=='\t' || (ch)=='\n')
50:
51: istream::istream(streambuf *sb, ostream* tied) : ios(sb, tied)
52: {
53: _flags |= ios::dont_close;
54: _gcount = 0;
55: }
56:
57: int skip_ws(streambuf* sb)
58: {
59: int ch;
60: for (;;) {
61: ch = sb->sbumpc();
62: if (ch == EOF || !isspace(ch))
63: return ch;
64: }
65: }
66:
67: istream& istream::get(char& c)
68: {
69: if (ipfx1()) {
70: int ch = _strbuf->sbumpc();
71: if (ch == EOF) {
72: set(ios::eofbit|ios::failbit);
73: _gcount = 0;
74: }
75: else {
76: c = (char)ch;
77: _gcount = 1;
78: }
79: }
80: return *this;
81: }
82:
83: int istream::peek()
84: {
85: if (!good())
86: return EOF;
87: if (_tie && rdbuf()->in_avail() == 0)
88: _tie->flush();
89: int ch = _strbuf->sgetc();
90: if (ch == EOF)
91: set(ios::eofbit);
92: return ch;
93: }
94:
95: istream& istream::ignore(int n /* = 1 */, int delim /* = EOF */)
96: {
97: if (ipfx1()) {
98: register streambuf* sb = _strbuf;
99: if (delim == EOF) {
100: _gcount = sb->ignore(n);
101: return *this;
102: }
103: _gcount = 0;
104: for (;;) {
105: #if 0
106: if (n != MAXINT) // FIXME
107: #endif
108: if (--n < 0)
109: break;
110: int ch = sb->sbumpc();
111: if (ch == EOF) {
112: set(ios::eofbit|ios::failbit);
113: break;
114: }
115: _gcount++;
116: if (ch == delim)
117: break;
118: }
119: }
120: return *this;
121: }
122:
123: istream& istream::read(char *s, int n)
124: {
125: if (ipfx1()) {
126: _gcount = _strbuf->sgetn(s, n);
127: if (_gcount != n)
128: set(ios::failbit);
129: }
130: return *this;
131: }
132:
133: istream& istream::seekg(streampos pos)
134: {
135: pos = _strbuf->sseekpos(pos, ios::in);
136: if (pos == streampos(EOF))
137: set(ios::badbit);
138: return *this;
139: }
140:
141: istream& istream::seekg(streamoff off, _seek_dir dir)
142: {
143: streampos pos
144: = _IO_seekoff (_strbuf, off,
145: (_IO_seekflags)
146: ((int)dir | _IO_seek_not_out | _IO_seek_pos_ignored));
147: if (pos == streampos(EOF))
148: set(ios::badbit);
149: return *this;
150: }
151:
152: streampos istream::tellg()
153: {
154: #if 0
155: streampos pos = _strbuf->sseekoff(0, ios::cur, ios::in);
156: #else
157: streampos pos
158: = _IO_seekoff (_strbuf, 0,
159: (_IO_seekflags)(_IO_seek_cur | _IO_seek_not_out));
160: #endif
161: if (pos == streampos(EOF))
162: set(ios::badbit);
163: return pos;
164: }
165:
166: istream& istream::operator>>(char& c)
167: {
168: if (ipfx0()) {
169: int ch = _strbuf->sbumpc();
170: if (ch == EOF)
171: set(ios::eofbit|ios::failbit);
172: else
173: c = (char)ch;
174: }
175: return *this;
176: }
177:
178: istream& istream::operator>>(char* ptr)
179: {
180: register char *p = ptr;
181: int w = width(0);
182: if (ipfx0()) {
183: register streambuf* sb = _strbuf;
184: for (;;)
185: {
186: int ch = sb->sbumpc();
187: if (ch == EOF)
188: {
189: set(p == ptr ? (ios::eofbit|ios::failbit) : (ios::eofbit));
190: break;
191: }
192: else if (isspace(ch))
193: {
194: sb->sputbackc(ch);
195: break;
196: }
197: else if (w == 1)
198: {
199: set(ios::failbit);
200: sb->sputbackc(ch);
201: break;
202: }
203: else *p++ = ch;
204: w--;
205: }
206: }
207: *p = '\0';
208: return *this;
209: }
210:
211: #if defined(__GNUC__) && !defined(__STRICT_ANSI__)
212: #define LONGEST long long
213: #else
214: #define LONGEST long
215: #endif
216:
217: static int read_int(istream& stream, unsigned LONGEST& val, int& neg)
218: {
219: if (!stream.ipfx0())
220: return 0;
221: register streambuf* sb = stream.rdbuf();
222: int base = 10;
223: int ndigits = 0;
224: register int ch = skip_ws(sb);
225: if (ch == EOF)
226: goto eof_fail;
227: neg = 0;
228: if (ch == '+') {
229: ch = skip_ws(sb);
230: }
231: else if (ch == '-') {
232: neg = 1;
233: ch = skip_ws(sb);
234: }
235: if (ch == EOF) goto eof_fail;
236: if (!(stream.flags() & ios::basefield)) {
237: if (ch == '0') {
238: ch = sb->sbumpc();
239: if (ch == EOF) {
240: val = 0;
241: return 1;
242: }
243: if (ch == 'x' || ch == 'X') {
244: base = 16;
245: ch = sb->sbumpc();
246: if (ch == EOF) goto eof_fail;
247: }
248: else {
249: sb->sputbackc(ch);
250: base = 8;
251: ch = '0';
252: }
253: }
254: }
255: else if ((stream.flags() & ios::basefield) == ios::hex)
256: base = 16;
257: else if ((stream.flags() & ios::basefield) == ios::oct)
258: base = 8;
259: val = 0;
260: for (;;) {
261: if (ch == EOF)
262: break;
263: int digit;
264: if (ch >= '0' && ch <= '9')
265: digit = ch - '0';
266: else if (ch >= 'A' && ch <= 'F')
267: digit = ch - 'A' + 10;
268: else if (ch >= 'a' && ch <= 'f')
269: digit = ch - 'a' + 10;
270: else
271: digit = 999;
272: if (digit >= base) {
273: sb->sputbackc(ch);
274: if (ndigits == 0)
275: goto fail;
276: else
277: return 1;
278: }
279: ndigits++;
280: val = base * val + digit;
281: ch = sb->sbumpc();
282: }
283: return 1;
284: fail:
285: stream.set(ios::failbit);
286: return 0;
287: eof_fail:
288: stream.set(ios::failbit|ios::eofbit);
289: return 0;
290: }
291:
292: #define READ_INT(TYPE) \
293: istream& istream::operator>>(TYPE& i)\
294: {\
295: unsigned LONGEST val; int neg;\
296: if (read_int(*this, val, neg)) {\
297: if (neg) val = -val;\
298: i = (TYPE)val;\
299: }\
300: return *this;\
301: }
302:
303: READ_INT(short)
304: READ_INT(unsigned short)
305: READ_INT(int)
306: READ_INT(unsigned int)
307: READ_INT(long)
308: READ_INT(unsigned long)
309: #if defined(__GNUC__) && !defined(__STRICT_ANSI__)
310: READ_INT(long long)
311: READ_INT(unsigned long long)
312: #endif
313:
314: istream& istream::operator>>(double& x)
315: {
316: if (ipfx0())
317: scan("%lg", &x);
318: return *this;
319: }
320:
321: istream& istream::operator>>(float& x)
322: {
323: if (ipfx0())
324: scan("%g", &x);
325: return *this;
326: }
327:
328: istream& istream::operator>>(register streambuf* sbuf)
329: {
330: if (ipfx0()) {
331: register streambuf* inbuf = rdbuf();
332: // FIXME: Should optimize!
333: for (;;) {
334: register int ch = inbuf->sbumpc();
335: if (ch == EOF) {
336: set(ios::eofbit);
337: break;
338: }
339: if (sbuf->sputc(ch) == EOF) {
340: set(ios::failbit);
341: break;
342: }
343: }
344: }
345: return *this;
346: }
347:
348: ostream& ostream::operator<<(char c)
349: {
350: if (opfx()) {
351: #if 1
352: // This is what the cfront implementation does.
353: if (_strbuf->sputc(c) == EOF)
354: goto failed;
355: #else
356: // This is what cfront documentation and current ANSI drafts say.
357: int w = width(0);
358: char fill_char = fill();
359: register int padding = w > 0 ? w - 1 : 0;
360: register streambuf *sb = _strbuf;
361: if (!(flags() & ios::left) && padding) // Default adjustment.
362: if (_IO_padn(sb, fill_char, padding) < padding)
363: goto failed;
364: if (sb->sputc(c) == EOF)
365: goto failed;
366: if (flags() & ios::left && padding) // Left adjustment.
367: if (_IO_padn(sb, fill_char, padding) < padding)
368: goto failed;
369: #endif
370: osfx();
371: }
372: return *this;
373: failed:
374: set(ios::badbit);
375: osfx();
376: return *this;
377: }
378:
379: /* Write VAL on STREAM.
380: If SIGN<0, val is the absolute value of a negative number.
381: If SIGN>0, val is a signed non-negative number.
382: If SIGN==0, val is unsigned. */
383:
384: static void write_int(ostream& stream, unsigned LONGEST val, int sign)
385: {
386: #define WRITE_BUF_SIZE (10 + sizeof(unsigned LONGEST) * 3)
387: char buf[WRITE_BUF_SIZE];
388: register char *buf_ptr = buf+WRITE_BUF_SIZE; // End of buf.
389: char *show_base = "";
390: int show_base_len = 0;
391: int show_pos = 0; // If 1, print a '+'.
392:
393: // Now do the actual conversion, placing the result at the *end* of buf.
394: // Note that we use separate code for decimal, octal, and hex,
395: // so we can divide by optimizable constants.
396: if ((stream.flags() & ios::basefield) == ios::oct) { // Octal
397: do {
398: *--buf_ptr = (val & 7) + '0';
399: val = val >> 3;
400: } while (val != 0);
401: if ((stream.flags() & ios::showbase) && (val != 0))
402: *--buf_ptr = '0';
403: }
404: else if ((stream.flags() & ios::basefield) == ios::hex) { // Hex
405: char *xdigs = (stream.flags() & ios::uppercase) ? "0123456789ABCDEF0X"
406: : "0123456789abcdef0x";
407: do {
408: *--buf_ptr = xdigs[val & 15];
409: val = val >> 4;
410: } while (val != 0);
411: if ((stream.flags() & ios::showbase) && (val != 0)) {
412: show_base = xdigs + 16; // Either "0X" or "0x".
413: show_base_len = 2;
414: }
415: }
416: else { // Decimal
417: #if defined(__GNUC__) && !defined(__STRICT_ANSI__)
418: // Optimization: Only use long long when we need to.
419: while (val > UINT_MAX) {
420: *--buf_ptr = (val % 10) + '0';
421: val /= 10;
422: }
423: // Use more efficient (int) arithmetic for the rest.
424: register unsigned int ival = (unsigned int)val;
425: #else
426: register unsigned LONGEST ival = val;
427: #endif
428: do {
429: *--buf_ptr = (ival % 10) + '0';
430: ival /= 10;
431: } while (ival != 0);
432: if (sign > 0 && (stream.flags() & ios::showpos))
433: show_pos=1;
434: }
435:
436: int buf_len = buf+WRITE_BUF_SIZE - buf_ptr;
437: int w = stream.width(0);
438:
439: // Calculate padding.
440: int len = buf_len+show_pos;
441: if (sign < 0) len++;
442: len += show_base_len;
443: int padding = len > w ? 0 : w - len;
444:
445: // Do actual output.
446: register streambuf* sbuf = stream.rdbuf();
447: ios::fmtflags pad_kind =
448: stream.flags() & (ios::left|ios::right|ios::internal);
449: char fill_char = stream.fill();
450: if (padding > 0
451: && pad_kind != (ios::fmtflags)ios::left
452: && pad_kind != (ios::fmtflags)ios::internal) // Default (right) adjust.
453: if (_IO_padn(sbuf, fill_char, padding) < padding)
454: goto failed;
455: if (sign < 0 || show_pos)
456: {
457: char ch = sign < 0 ? '-' : '+';
458: if (sbuf->sputc(ch) < 0)
459: goto failed;
460: }
461: if (show_base_len)
462: if (_IO_sputn(sbuf, show_base, show_base_len) <= 0)
463: goto failed;
464: if (pad_kind == (ios::fmtflags)ios::internal && padding > 0)
465: if (_IO_padn(sbuf, fill_char, padding) < padding)
466: goto failed;
467: if (_IO_sputn (sbuf, buf_ptr, buf_len) != buf_len)
468: goto failed;
469: if (pad_kind == (ios::fmtflags)ios::left && padding > 0) // Left adjustment
470: if (_IO_padn(sbuf, fill_char, padding) < padding)
471: goto failed;
472: stream.osfx();
473: return;
474: failed:
475: stream.set(ios::badbit);
476: stream.osfx();
477: }
478:
479: ostream& ostream::operator<<(int n)
480: {
481: if (opfx()) {
482: int sign = 1;
483: if (n < 0 && (flags() & (ios::oct|ios::hex)) == 0)
484: n = -n, sign = -1;
485: write_int(*this, n, sign);
486: }
487: return *this;
488: }
489:
490: ostream& ostream::operator<<(unsigned int n)
491: {
492: if (opfx())
493: write_int(*this, n, 0);
494: return *this;
495: }
496:
497:
498: ostream& ostream::operator<<(long n)
499: {
500: if (opfx()) {
501: int sign = 1;
502: unsigned int abs_n = (unsigned)n;
503: if (n < 0 && (flags() & (ios::oct|ios::hex)) == 0)
504: abs_n = -((unsigned)n), sign = -1;
505: write_int(*this, abs_n, sign);
506: }
507: return *this;
508: }
509:
510: ostream& ostream::operator<<(unsigned long n)
511: {
512: if (opfx())
513: write_int(*this, n, 0);
514: return *this;
515: }
516:
517: #if defined(__GNUC__) && !defined(__STRICT_ANSI__)
518: ostream& ostream::operator<<(long long n)
519: {
520: if (opfx()) {
521: int sign = 1;
522: unsigned long long abs_n = (unsigned long long)n;
523: if (n < 0 && (flags() & (ios::oct|ios::hex)) == 0)
524: abs_n = -((unsigned long long)n), sign = -1;
525: write_int(*this, abs_n, sign);
526: }
527: return *this;
528: }
529:
530:
531: ostream& ostream::operator<<(unsigned long long n)
532: {
533: if (opfx())
534: write_int(*this, n, 0);
535: return *this;
536: }
537: #endif /*__GNUC__*/
538:
539: ostream& ostream::operator<<(double n)
540: {
541: if (opfx()) {
542: // Uses __cvt_double (renamed from static cvt), in Chris Torek's
543: // stdio implementation. The setup code uses the same logic
544: // as in __vsbprintf.C (also based on Torek's code).
545: int format_char;
546: #if 0
547: if (flags() ios::showpos) sign = '+';
548: #endif
549: if ((flags() & ios::floatfield) == ios::fixed)
550: format_char = 'f';
551: else if ((flags() & ios::floatfield) == ios::scientific)
552: format_char = flags() & ios::uppercase ? 'E' : 'e';
553: else
554: format_char = flags() & ios::uppercase ? 'G' : 'g';
555:
556: int fpprec = 0; // 'Extra' (suppressed) floating precision.
557: int prec = precision();
558: if (prec > MAXFRACT) {
559: if (flags() & (ios::fixed|ios::scientific) & ios::showpos)
560: fpprec = prec - MAXFRACT;
561: prec = MAXFRACT;
562: }
563: else if (prec <= 0 && !(flags() & ios::fixed))
564: prec = 6; /* default */
565:
566: // Do actual conversion.
567: #ifdef USE_DTOA
568: if (_IO_outfloat(n, rdbuf(), format_char, width(0),
569: prec, flags(), 0, fill()) < 0)
570: set(ios::badbit|ios::failbit); // ??
571: #else
572: int negative;
573: char buf[BUF];
574: int sign = '\0';
575: char *cp = buf;
576: *cp = 0;
577: int size = __cvt_double(n, prec,
578: flags() & ios::showpoint ? 0x80 : 0,
579: &negative,
580: format_char, cp, buf + sizeof(buf));
581: if (negative) sign = '-';
582: if (*cp == 0)
583: cp++;
584:
585: // Calculate padding.
586: int fieldsize = size + fpprec;
587: if (sign) fieldsize++;
588: int padding = 0;
589: int w = width(0);
590: if (fieldsize < w)
591: padding = w - fieldsize;
592:
593: // Do actual output.
594: register streambuf* sbuf = rdbuf();
595: register i;
596: char fill_char = fill();
597: ios::fmtflags pad_kind =
598: flags() & (ios::left|ios::right|ios::internal);
599: if (pad_kind != (ios::fmtflags)ios::left // Default (right) adjust.
600: && pad_kind != (ios::fmtflags)ios::internal)
601: for (i = padding; --i >= 0; ) sbuf->sputc(fill_char);
602: if (sign)
603: sbuf->sputc(sign);
604: if (pad_kind == (ios::fmtflags)ios::internal)
605: for (i = padding; --i >= 0; ) sbuf->sputc(fill_char);
606:
607: // Emit the actual concented field, followed by extra zeros.
608: _IO_sputn (sbuf, cp, size);
609: for (i = fpprec; --i >= 0; ) sbuf->sputc('0');
610:
611: if (pad_kind == (ios::fmtflags)ios::left) // Left adjustment
612: for (i = padding; --i >= 0; ) sbuf->sputc(fill_char);
613: #endif
614: osfx();
615: }
616: return *this;
617: }
618:
619: ostream& ostream::operator<<(const char *s)
620: {
621: if (opfx())
622: {
623: if (s == NULL)
624: s = "(null)";
625: int len = strlen(s);
626: int w = width(0);
627: // FIXME: Should we: if (w && len>w) len = w;
628: char fill_char = fill();
629: register streambuf *sbuf = rdbuf();
630: register int padding = w > len ? w - len : 0;
631: if (!(flags() & ios::left) && padding > 0) // Default adjustment.
632: if (_IO_padn(sbuf, fill_char, padding) != padding)
633: goto failed;
634: if (_IO_sputn (sbuf, s, len) != len)
635: goto failed;
636: if (flags() & ios::left && padding > 0) // Left adjustment.
637: if (_IO_padn(sbuf, fill_char, padding) != padding)
638: goto failed;
639: osfx();
640: }
641: return *this;
642: failed:
643: set(ios::badbit);
644: osfx();
645: return *this;
646: }
647:
648: #if 0
649: ostream& ostream::operator<<(const void *p)
650: { Is in osform.cc, to avoid pulling in all of _IO_vfprintf by this file. */ }
651: #endif
652:
653: ostream& ostream::operator<<(register streambuf* sbuf)
654: {
655: if (opfx())
656: {
657: char buffer[_IO_BUFSIZ];
658: register streambuf* outbuf = _strbuf;
659: for (;;)
660: {
661: _IO_size_t count = _IO_sgetn(sbuf, buffer, _IO_BUFSIZ);
662: if (count <= 0)
663: break;
664: if (_IO_sputn(outbuf, buffer, count) != count)
665: {
666: set(ios::badbit);
667: break;
668: }
669: }
670: osfx();
671: }
672: return *this;
673: }
674:
675: ostream::ostream(streambuf* sb, ostream* tied) : ios(sb, tied)
676: {
677: _flags |= ios::dont_close;
678: }
679:
680: ostream& ostream::seekp(streampos pos)
681: {
682: pos = _strbuf->sseekpos(pos, ios::out);
683: if (pos == streampos(EOF))
684: set(ios::badbit);
685: return *this;
686: }
687:
688: ostream& ostream::seekp(streamoff off, _seek_dir dir)
689: {
690: streampos pos
691: = _IO_seekoff (_strbuf, off,
692: (_IO_seekflags)
693: ((int)dir | _IO_seek_not_in | _IO_seek_pos_ignored));
694: if (pos == streampos(EOF))
695: set(ios::badbit);
696: return *this;
697: }
698:
699: streampos ostream::tellp()
700: {
701: #if 1
702: streampos pos
703: = _IO_seekoff (_strbuf, 0,
704: (_IO_seekflags)(_IO_seek_cur | _IO_seek_not_in));
705: #else
706: streampos pos = _strbuf->sseekoff(0, ios::cur, ios::out);
707: #endif
708: if (pos == streampos(EOF))
709: set(ios::badbit);
710: return pos;
711: }
712:
713: ostream& ostream::flush()
714: {
715: if (_strbuf->_jumps->__sync(_strbuf))
716: set(ios::badbit);
717: return *this;
718: }
719:
720: ostream& flush(ostream& outs)
721: {
722: return outs.flush();
723: }
724:
725: istream& ws(istream& ins)
726: {
727: if (ins.ipfx1()) {
728: int ch = skip_ws(ins._strbuf);
729: if (ch == EOF)
730: ins.set(ios::eofbit);
731: else
732: ins._strbuf->sputbackc(ch);
733: }
734: return ins;
735: }
736:
737: // Skip white-space. Return 0 on failure (EOF), or 1 on success.
738: // Differs from ws() manipulator in that failbit is set on EOF.
739: // Called by ipfx() and ipfx0() if needed.
740:
741: int istream::_skip_ws()
742: {
743: int ch = skip_ws(_strbuf);
744: if (ch == EOF) {
745: set(ios::eofbit|ios::failbit);
746: return 0;
747: }
748: else {
749: _strbuf->sputbackc(ch);
750: return 1;
751: }
752: }
753:
754: ostream& ends(ostream& outs)
755: {
756: outs.put('\0');
757: return outs;
758: }
759:
760: ostream& endl(ostream& outs)
761: {
762: return flush(outs.put('\n'));
763: }
764:
765: ostream& ostream::write(const char *s, int n)
766: {
767: if (opfx()) {
768: if (_IO_sputn(_strbuf, s, n) != n)
769: set(ios::failbit);
770: }
771: return *this;
772: }
773:
774: void ostream::do_osfx()
775: {
776: if (flags() & ios::unitbuf)
777: flush();
778: if (flags() & ios::stdio) {
779: fflush(stdout);
780: fflush(stderr);
781: }
782: }
783:
784: iostream::iostream(streambuf* sb, ostream* tied) : ios(sb, tied)
785: {
786: _flags |= ios::dont_close;
787: }
788:
789: // NOTE: extension for compatibility with old libg++.
790: // Not really compatible with fistream::close().
791: #ifdef _STREAM_COMPAT
792: void ios::close()
793: {
794: if (!(_flags & (unsigned int)ios::dont_close))
795: delete rdbuf();
796: else if (_strbuf->_flags & _IO_IS_FILEBUF)
797: ((struct filebuf*)rdbuf())->close();
798: else if (_strbuf != NULL)
799: rdbuf()->sync();
800: _flags |= ios::dont_close;
801: _strbuf = NULL;
802: _state = badbit;
803: }
804:
805: int istream::skip(int i)
806: {
807: int old = (_flags & ios::skipws) != 0;
808: if (i)
809: _flags |= ios::skipws;
810: else
811: _flags &= ~ios::skipws;
812: return old;
813: }
814: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.