Annotation of 43BSDReno/contrib/rcs/src/rcssyn.c, revision 1.1

1.1     ! root        1: /*
        !             2:  *                     RCS file input
        !             3:  */
        !             4: #ifndef lint
        !             5: static char rcsid[]= "$Id: rcssyn.c,v 4.6 89/05/01 15:13:32 narten Exp $ Purdue CS";
        !             6: #endif
        !             7: /*********************************************************************************
        !             8:  *                       Syntax Analysis.
        !             9:  *                       Keyword table
        !            10:  *                       Testprogram: define SYNDB
        !            11:  *                       Compatibility with Release 2: define COMPAT2
        !            12:  *********************************************************************************
        !            13:  */
        !            14: 
        !            15: /* Copyright (C) 1982, 1988, 1989 Walter Tichy
        !            16:  * All rights reserved.
        !            17:  *
        !            18:  * Redistribution and use in source and binary forms are permitted
        !            19:  * provided that the above copyright notice and this paragraph are
        !            20:  * duplicated in all such forms and that any documentation,
        !            21:  * advertising materials, and other materials related to such
        !            22:  * distribution and use acknowledge that the software was developed
        !            23:  * by Walter Tichy.
        !            24:  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
        !            25:  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
        !            26:  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
        !            27:  *
        !            28:  * Report all problems and direct all questions to:
        !            29:  *   [email protected]
        !            30:  * 
        !            31: 
        !            32: 
        !            33: 
        !            34: 
        !            35: 
        !            36: 
        !            37: 
        !            38: */
        !            39: 
        !            40: 
        !            41: /* $Log:       rcssyn.c,v $
        !            42:  * Revision 4.6  89/05/01  15:13:32  narten
        !            43:  * changed copyright header to reflect current distribution rules
        !            44:  * 
        !            45:  * Revision 4.5  88/11/08  12:00:37  narten
        !            46:  * changes from  [email protected] (Paul Eggert)
        !            47:  * 
        !            48:  * Revision 4.5  88/08/09  19:13:21  eggert
        !            49:  * Allow cc -R; remove lint.
        !            50:  * 
        !            51:  * Revision 4.4  87/12/18  11:46:16  narten
        !            52:  * more lint cleanups (Guy Harris)
        !            53:  * 
        !            54:  * Revision 4.3  87/10/18  10:39:36  narten
        !            55:  * Updating version numbers. Changes relative to 1.1 actually relative to
        !            56:  * 4.1
        !            57:  * 
        !            58:  * Revision 1.3  87/09/24  14:00:49  narten
        !            59:  * Sources now pass through lint (if you ignore printf/sprintf/fprintf 
        !            60:  * warnings)
        !            61:  * 
        !            62:  * Revision 1.2  87/03/27  14:22:40  jenkins
        !            63:  * Port to suns
        !            64:  * 
        !            65:  * Revision 1.1  84/01/23  14:50:40  kcs
        !            66:  * Initial revision
        !            67:  * 
        !            68:  * Revision 4.1  83/03/28  11:38:49  wft
        !            69:  * Added parsing and printing of default branch.
        !            70:  * 
        !            71:  * Revision 3.6  83/01/15  17:46:50  wft
        !            72:  * Changed readdelta() to initialize selector and log-pointer.
        !            73:  * Changed puttree to check for selector==DELETE; putdtext() uses DELNUMFORM.
        !            74:  *
        !            75:  * Revision 3.5  82/12/08  21:58:58  wft
        !            76:  * renamed Commentleader to Commleader.
        !            77:  *
        !            78:  * Revision 3.4  82/12/04  13:24:40  wft
        !            79:  * Added routine gettree(), which updates keeplock after reading the
        !            80:  * delta tree.
        !            81:  *
        !            82:  * Revision 3.3  82/11/28  21:30:11  wft
        !            83:  * Reading and printing of Suffix removed; version COMPAT2 skips the
        !            84:  * Suffix for files of release 2 format. Fixed problems with printing nil.
        !            85:  *
        !            86:  * Revision 3.2  82/10/18  21:18:25  wft
        !            87:  * renamed putdeltatext to putdtext.
        !            88:  *
        !            89:  * Revision 3.1  82/10/11  19:45:11  wft
        !            90:  * made sure getc() returns into an integer.
        !            91:  */
        !            92: 
        !            93: 
        !            94: 
        !            95: /*
        !            96: #define COMPAT2
        !            97: /* version COMPAT2 reads files of the format of release 2 and 3, but
        !            98:  * generates files of release 3 format. Need not be defined if no
        !            99:  * old RCS files generated with release 2 exist.
        !           100:  */
        !           101: /*
        !           102: #define SYNDB
        !           103: /* version SYNDB is for debugging the syntax analysis for RCS files.
        !           104:  * SYNDB performs additional error checks.
        !           105:  */
        !           106: /*
        !           107: #define SYNTEST
        !           108: /* version SYNTEST inputs a RCS file and then prints out its internal
        !           109:  * data structures.
        !           110: */
        !           111: 
        !           112: #include "rcsbase.h"
        !           113: extern FILE * finptr;        /*RCS input file*/
        !           114: extern char * getid();
        !           115: extern struct hshentry * getnum();
        !           116: extern int    getkey();
        !           117: extern int    getlex();
        !           118: extern        readstring();
        !           119: extern        savestring();
        !           120: 
        !           121: /* forward */
        !           122: char * getkeyval();
        !           123: 
        !           124: /* keyword table */
        !           125: 
        !           126: char Kaccess[]   = "access";
        !           127: char Kauthor[]   = "author";
        !           128: char Kbranch[]   = "branch";
        !           129: char Kbranches[] = "branches";
        !           130: char Kcomment[]  = "comment";
        !           131: char Kdate[]     = "date";
        !           132: char Kdesc[]     = "desc";
        !           133: char Khead[]     = "head";
        !           134: char Klocks[]    = "locks";
        !           135: char Klog[]      = "log";
        !           136: char Knext[]     = "next";
        !           137: char Kstate[]    = "state";
        !           138: char Kstrict[]   = "strict";
        !           139: #ifdef COMPAT2
        !           140: char Ksuffix[]   = "suffix";
        !           141: #endif
        !           142: char Ksymbols[]  = "symbols";
        !           143: char Ktext[]     = "text";
        !           144: 
        !           145: #define COMMLENGTH 20
        !           146: char              Commleader[COMMLENGTH];
        !           147: char            * Comment;
        !           148: struct access   * AccessList;
        !           149: struct access   * LastAccess;
        !           150: struct assoc    * Symbols;
        !           151: struct assoc    * LastSymbol;
        !           152: struct lock     * Locks;
        !           153: struct lock     * LastLock;
        !           154: int               StrictLocks;
        !           155: struct hshentry * Head;
        !           156: struct hshentry * Dbranch;
        !           157: int               TotalDeltas;
        !           158: 
        !           159: 
        !           160: 
        !           161: getadmin()
        !           162: /* Function: Reads an <admin> and initializes the globals
        !           163:  * AccessList, LastAccess, Symbols, LastSymbol,
        !           164:  * Locks, LastLock, StrictLocks, Head, Comment, TotalDeltas;
        !           165:  */
        !           166: {
        !           167:         register char   * id;
        !           168:         struct access   * newaccess;
        !           169:         struct assoc    * newassoc;
        !           170:         struct lock     * newlock;
        !           171:         struct hshentry * delta;
        !           172: 
        !           173:         Comment="";
        !           174:         AccessList=LastAccess=nil;
        !           175:         Symbols=LastSymbol=nil;
        !           176:         Locks=LastLock=nil;
        !           177:         Dbranch = Head = nil;
        !           178:         TotalDeltas=0;
        !           179: 
        !           180:         if (!getkey(Khead)) fatserror("Missing head");
        !           181:         Head=getnum();
        !           182: #       ifdef SYNDB
        !           183:         if (Head&&((countnumflds(Head->num)%2)!=0))
        !           184:                 serror("Delta number required for head");
        !           185: #       endif
        !           186:         if (!getlex(SEMI)) serror("Missing ';' after head");
        !           187: 
        !           188:         if (getkey(Kbranch)) { /* optional */
        !           189:                 Dbranch=getnum();
        !           190:                 if (!getlex(SEMI)) serror("Missing ';' after branch list");
        !           191:         }
        !           192: 
        !           193: 
        !           194: #ifdef COMPAT2
        !           195:         /* read suffix. Only in release 2 format */
        !           196:         if (getkey(Ksuffix)) {
        !           197:                 if (nexttok==STRING) {
        !           198:                         readstring(); nextlex(); /*through away the suffix*/
        !           199:                 } elsif(nexttok==ID) {
        !           200:                         nextlex();
        !           201:                 }
        !           202:                 if (!getlex(SEMI)) serror("Missing ';' after %s",Ksuffix);
        !           203:         }
        !           204: #endif
        !           205: 
        !           206:         if (!getkey(Kaccess)) fatserror("Missing access list");
        !           207:         while (id=getid()) {
        !           208:                 newaccess = (struct access *)talloc(sizeof(struct access));
        !           209:                 newaccess->login = id;
        !           210:                 newaccess->nextaccess = nil;
        !           211:                 if (AccessList == nil) {
        !           212:                         AccessList=LastAccess=newaccess;
        !           213:                 } else {
        !           214:                         LastAccess=LastAccess->nextaccess=newaccess;
        !           215:                 }
        !           216:         }
        !           217:         if (!getlex(SEMI)) serror("Missing ';' after access list");
        !           218: 
        !           219:         if (!getkey(Ksymbols)) fatserror("Missing symbols");
        !           220:         while (id = getid()) {
        !           221:                 if (!getlex(COLON))
        !           222:                         serror("Missing ':' in symbolic name definition");
        !           223:                 if (!(delta=getnum())) {
        !           224:                         serror("Missing number in symbolic name definition");
        !           225:                 } else { /*add new pair to association list*/
        !           226:                         newassoc=(struct assoc *)talloc(sizeof(struct assoc));
        !           227:                         newassoc->symbol=id;
        !           228:                         newassoc->delta=delta;
        !           229:                         newassoc->nextassoc=nil;
        !           230:                         if (Symbols == nil) {
        !           231:                                 Symbols=LastSymbol=newassoc;
        !           232:                         } else {
        !           233:                                 LastSymbol=LastSymbol->nextassoc=newassoc;
        !           234:                         }
        !           235:                 }
        !           236:         }
        !           237:         if (!getlex(SEMI)) serror("Missing ';' after symbolic names");
        !           238: 
        !           239:         if (!getkey(Klocks)) serror("Missing locks");
        !           240:         while (id = getid()) {
        !           241:                 if (!getlex(COLON))
        !           242:                         serror("Missing ':' in lock");
        !           243:                 if (!(delta=getnum())) {
        !           244:                         serror("Missing number in lock");
        !           245:                 } else { /*add new pair to lock list*/
        !           246: #                       ifdef SYNDB
        !           247:                         if ((countnumflds(delta->num)%2)!=0)
        !           248:                                 serror("Delta number required for lock");
        !           249: #                       endif
        !           250:                         newlock=(struct lock *)talloc(sizeof(struct lock));
        !           251:                         newlock->login=id;
        !           252:                         newlock->delta=delta;
        !           253:                         newlock->nextlock=nil;
        !           254:                         if (Locks == nil) {
        !           255:                                 Locks=LastLock=newlock;
        !           256:                         } else {
        !           257:                                 LastLock=LastLock->nextlock=newlock;
        !           258:                         }
        !           259:                 }
        !           260:         }
        !           261:         if (!getlex(SEMI)) serror("Missing ';' after locks");
        !           262:         if (!getkey(Kstrict)) {
        !           263:                 StrictLocks = false;
        !           264:         } else {
        !           265:                 StrictLocks = true;
        !           266:                 if (!getlex(SEMI)) serror("Missing ';' after keyword %s",Kstrict);
        !           267:         }
        !           268:         if (getkey(Kcomment) && (nexttok==STRING)) {
        !           269:                 VOID savestring(Commleader,COMMLENGTH);nextlex();
        !           270:                 Comment=Commleader;
        !           271:                 if (!getlex(SEMI)) serror("Missing ';' after %s",Kcomment);
        !           272:         }
        !           273: }
        !           274: 
        !           275: 
        !           276: 
        !           277: getdelta()
        !           278: /* Function: reads a delta block.
        !           279:  * returns false if the current block does not start with a number.
        !           280:  */
        !           281: {
        !           282:         register struct hshentry * Delta, * num;
        !           283:         struct branchhead * LastBranch, * NewBranch;
        !           284: 
        !           285:         if (!(Delta=getnum())) return false;
        !           286: #       ifdef SYNDB
        !           287:         if ((countnumflds(Delta->num)%2)!=0)
        !           288:                 serror("Delta number required");
        !           289: #       endif
        !           290: 
        !           291:         hshenter = false; /*Don't enter dates into hashtable*/
        !           292:         Delta->date = getkeyval(Kdate, NUM, false);
        !           293:         hshenter=true;    /*reset hshenter for revision numbers.*/
        !           294: 
        !           295:         Delta->author = getkeyval(Kauthor, ID, false);
        !           296: 
        !           297:         Delta->state = getkeyval(Kstate, ID, true);
        !           298: 
        !           299:         if (!getkey(Kbranches)) fatserror("Missing branches");
        !           300:         Delta->branches = LastBranch=nil;
        !           301:         while (num=getnum()) {
        !           302: #               ifdef SYNDB
        !           303:                 if ((countnumflds(num->num)%2)!=0)
        !           304:                         serror("Delta number required");
        !           305: #               endif
        !           306:                 NewBranch = (struct branchhead *)talloc(sizeof(struct branchhead));
        !           307:                 NewBranch->hsh = num;
        !           308:                 NewBranch->nextbranch = nil;
        !           309:                 if (LastBranch == nil) {
        !           310:                         Delta->branches=LastBranch=NewBranch;
        !           311:                 } else {
        !           312:                         LastBranch=LastBranch->nextbranch=NewBranch;
        !           313:                 }
        !           314:         }
        !           315:         if (!getlex(SEMI)) serror("Missing ';' after branches");
        !           316: 
        !           317:         if (!getkey(Knext)) fatserror("Missing next");
        !           318:         Delta->next=num=getnum();
        !           319: #       ifdef SYNDB
        !           320:         if (num&&((countnumflds(num->num)%2)!=0))
        !           321:                 serror("Delta number required");
        !           322: #       endif
        !           323:         if (!getlex(SEMI)) serror("Missing ';' after next");
        !           324:         Delta->log=Delta->lockedby = nil;
        !           325:         Delta->selector = '\0';
        !           326:         TotalDeltas++;
        !           327:         return (true);
        !           328: }
        !           329: 
        !           330: 
        !           331: gettree()
        !           332: /* Function: Reads in the delta tree with getdelta(), then
        !           333:  * updates the lockedby fields.
        !           334:  */
        !           335: {       struct lock * currlock;
        !           336:         while (getdelta());
        !           337:         currlock=Locks;
        !           338:         while (currlock) {
        !           339:                 currlock->delta->lockedby = currlock->login;
        !           340:                 currlock = currlock->nextlock;
        !           341:         }
        !           342: }
        !           343: 
        !           344: 
        !           345: getdesc(prdesc)
        !           346: int  prdesc;
        !           347: /* Function: read in descriptive text
        !           348:  * nexttok is not advanced afterwards.
        !           349:  * if prdesc==true, the text is printed to stdout.
        !           350:  */
        !           351: {
        !           352: 
        !           353:         if (!getkey(Kdesc) || (nexttok!=STRING)) fatserror("Missing descriptive text");
        !           354:         if (prdesc)
        !           355:                 printstring();  /*echo string*/
        !           356:         else    readstring();   /*skip string*/
        !           357: }
        !           358: 
        !           359: 
        !           360: 
        !           361: 
        !           362: 
        !           363: 
        !           364: char * getkeyval(keyword, token, optional)
        !           365: enum tokens token; char * keyword; int optional;
        !           366: /* reads a pair of the form
        !           367:  * <keyword> <token> ;
        !           368:  * where token is one of <id> or <num>. optional indicates whether
        !           369:  * <token> is optional. A pointer to
        !           370:  * the acutal character string of <id> or <num) is returned.
        !           371:  * Getkeyval terminates the program on missing keyword or token, continues
        !           372:  * on missing ;.
        !           373:  */
        !           374: {
        !           375:         register char * val;
        !           376: 
        !           377:         if (!getkey(keyword)) {
        !           378:                 fatserror("Missing %s", keyword);
        !           379:         }
        !           380:         if (nexttok==token) {
        !           381:                 val = NextString;
        !           382:                 nextlex();
        !           383:         } else {
        !           384:                 if (!optional) {fatserror("Missing %s", keyword); }
        !           385:                 else val = nil;
        !           386:         }
        !           387:         if (!getlex(SEMI)) serror("Missing ';' after %s",keyword);
        !           388:         return(val);
        !           389: }
        !           390: 
        !           391: 
        !           392: 
        !           393: 
        !           394: putadmin(fout)
        !           395: register FILE * fout;
        !           396: /* Function: Print the <admin> node read with getadmin() to file fout.
        !           397:  * Assumption: Variables AccessList, Symbols, Locks, StrictLocks,
        !           398:  * and Head have been set.
        !           399:  */
        !           400: {       struct assoc  * curassoc;
        !           401:         struct lock   * curlock;
        !           402:         struct access * curaccess;
        !           403:         register char * sp;
        !           404: 
        !           405:         VOID fputs(Khead,fout); VOID fputs("     ",fout);
        !           406:         if (Head) VOID fputs(Head->num,fout);
        !           407: 
        !           408:         VOID fprintf(fout,";\n%s   ",Kbranch);
        !           409:         if (Dbranch) VOID fputs(Dbranch->num,fout);
        !           410: 
        !           411:         VOID fprintf(fout,";\n%s  ",Kaccess);
        !           412:         curaccess = AccessList;
        !           413:         if (curaccess==nil) VOID putc(' ',fout);
        !           414:         while (curaccess) {
        !           415:                VOID putc(' ',fout);
        !           416:                VOID fputs(curaccess->login,fout);
        !           417:                curaccess = curaccess->nextaccess;
        !           418:         }
        !           419:         VOID fprintf(fout,";\n%s ",Ksymbols);
        !           420:         curassoc = Symbols;
        !           421:         if (curassoc==nil) VOID putc(' ',fout);
        !           422:         while (curassoc) {
        !           423:                VOID fprintf(fout," %s:%s",curassoc->symbol, curassoc->delta->num);
        !           424:                curassoc = curassoc->nextassoc;
        !           425:         }
        !           426:         VOID fprintf(fout,";\n%s   ",Klocks);
        !           427:         curlock = Locks;
        !           428:         if (curlock==nil) VOID putc(' ',fout);
        !           429:         while (curlock) {
        !           430:                VOID fprintf(fout," %s:%s",curlock->login, curlock->delta->num);
        !           431:                curlock = curlock->nextlock;
        !           432:         }
        !           433:         if (StrictLocks) VOID fprintf(fout,"; %s",Kstrict);
        !           434:         VOID fprintf(fout,";\n%s  %c",Kcomment,SDELIM);
        !           435:         if((sp=Comment)!=nil) {
        !           436:                while (*sp) if (putc(*sp++,fout)==SDELIM) VOID putc(SDELIM,fout);
        !           437:         }
        !           438:         VOID fprintf(fout,"%c;\n\n",SDELIM);
        !           439: }
        !           440: 
        !           441: 
        !           442: 
        !           443: 
        !           444: putdelta(node,fout)
        !           445: register struct hshentry * node;
        !           446: register FILE * fout;
        !           447: /* Function: prints a <delta> node to fout;
        !           448:  */
        !           449: {      struct branchhead * nextbranch;
        !           450: 
        !           451:         if (node == nil) return;
        !           452: 
        !           453:         VOID fprintf(fout,"\n%s\n",node->num);
        !           454:         VOID fprintf(fout,"%s     %s;  %s %s;  %s ",
        !           455:                 Kdate,node->date,Kauthor,node->author,Kstate);
        !           456:         if (node->state!=nil) VOID fputs(node->state,fout);
        !           457:         VOID fputs(";\nbranches",fout);
        !           458:         nextbranch = node->branches;
        !           459:         if (nextbranch==nil) VOID putc(' ',fout);
        !           460:         while (nextbranch) {
        !           461:                VOID putc(' ',fout);
        !           462:                VOID fputs(nextbranch->hsh->num,fout);
        !           463:                nextbranch = nextbranch->nextbranch;
        !           464:         }
        !           465: 
        !           466:         VOID fprintf(fout,";\n%s     ",Knext);
        !           467:         if (node->next!=nil) VOID fputs(node->next->num,fout);
        !           468:         VOID fputs(";\n",fout);
        !           469: 
        !           470: }
        !           471: 
        !           472: 
        !           473: 
        !           474: 
        !           475: puttree(root,fout)
        !           476: struct hshentry * root;
        !           477: register FILE * fout;
        !           478: /* Function: prints the delta tree in preorder to fout, starting with root.
        !           479:  */
        !           480: {       struct branchhead * nextbranch;
        !           481: 
        !           482:         if (root==nil) return;
        !           483: 
        !           484:         if (root->selector !=DELETE)putdelta(root,fout);
        !           485:         /* selector DELETE means deleted; set by rcs -o */
        !           486: 
        !           487:         puttree(root->next,fout);
        !           488: 
        !           489:         nextbranch = root->branches;
        !           490:         while (nextbranch) {
        !           491:              puttree(nextbranch->hsh,fout);
        !           492:              nextbranch = nextbranch->nextbranch;
        !           493:         }
        !           494: }
        !           495: 
        !           496: 
        !           497: 
        !           498: int putdtext(num,log,srcfilename,fout)
        !           499: char * num, * log, * srcfilename; FILE * fout;
        !           500: /* Function: write a deltatext-node to fout.
        !           501:  * num points to the deltanumber, log to the logmessage, and
        !           502:  * sourcefile contains the text. Doubles up all SDELIMs in both the
        !           503:  * log and the text; Makes sure the log message ends in \n.
        !           504:  * returns false on error.
        !           505:  */
        !           506: {
        !           507:         register char * sp;
        !           508:        register int c;
        !           509:         register FILE * fin;
        !           510: 
        !           511:         VOID fprintf(fout,DELNUMFORM,num,Klog);
        !           512:         /* put log */
        !           513:         VOID putc(SDELIM,fout);
        !           514:         sp=log;
        !           515:         while (*sp) if (putc(*sp++,fout)==SDELIM) VOID putc(SDELIM,fout);
        !           516:         if (*(sp-1)!='\n') VOID putc('\n', fout); /*append \n if necessary*/
        !           517:         /* put text */
        !           518:         VOID fprintf(fout, "%c\n%s\n%c",SDELIM,Ktext,SDELIM);
        !           519:         if ((fin=fopen(srcfilename,"r"))==NULL) {
        !           520:                 error("Can't open source file %s",srcfilename);
        !           521:                 return false;
        !           522:         }
        !           523:         while ((c=fgetc(fin))!=EOF) {
        !           524:                 if (c==SDELIM) VOID putc(SDELIM,fout);   /*double up SDELIM*/
        !           525:                 VOID putc(c,fout);
        !           526:         }
        !           527:         VOID putc(SDELIM,fout); VOID putc('\n',fout);
        !           528:         VOID fclose(fin);
        !           529:         return true;
        !           530: }
        !           531: 
        !           532: 
        !           533: 
        !           534: #ifdef SYNTEST
        !           535: 
        !           536: main(argc,argv)
        !           537: int argc; char * argv[];
        !           538: {
        !           539: 
        !           540:         cmdid = "syntest";
        !           541:         if (argc<2) {
        !           542:                 VOID fputs("No input file\n",stderr);
        !           543:                 exit(-1);
        !           544:         }
        !           545:         if ((finptr=fopen(argv[1], "r")) == NULL) {
        !           546:                 faterror("Can't open input file %s\n",argv[1]);
        !           547:         }
        !           548:         Lexinit();
        !           549:         getadmin();
        !           550:         putadmin(stdout);
        !           551: 
        !           552:         gettree();
        !           553:         puttree(Head,stdout);
        !           554: 
        !           555:         getdesc(true);
        !           556: 
        !           557:         if (nextlex(),nexttok!=EOFILE) {
        !           558:                 fatserror("Syntax error");
        !           559:         }
        !           560:         exit(0);
        !           561: }
        !           562: 
        !           563: 
        !           564: cleanup(){}
        !           565: /*dummy*/
        !           566: 
        !           567: 
        !           568: #endif
        !           569: 

unix.superglobalmegacorp.com

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