Annotation of 43BSD/contrib/B/src/bint/b2syn.c, revision 1.1.1.1

1.1       root        1: /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
                      2: 
                      3: /*
                      4:   $Header: b2syn.c,v 1.4 85/08/22 16:56:25 timo Exp $
                      5: */
                      6: 
                      7: #include "b.h"
                      8: #include "b0con.h"
                      9: #include "b1obj.h"
                     10: #include "b2key.h"
                     11: #include "b2syn.h"
                     12: #include "b3scr.h"
                     13: #include "b3env.h"
                     14: #include "b3err.h"
                     15: 
                     16: #define TABSIZE 8 /* Number of spaces assumed for a tab on a file.
                     17:                     (Some editors insist on emitting tabs wherever
                     18:                     they can, and always assume 8 spaces for a tab.
                     19:                     Even when the editor can be instructed not to
                     20:                     do this, beginning users won't know about this,
                     21:                     so we'll always assume the default tab size.
                     22:                     Advanced users who used to instruct their editor
                     23:                     to set tab stops every 4 spaces will have to
                     24:                     unlearn this habit.  But that's the price for
                     25:                     over-cleverness :-)
                     26:                     The indent increment is still 4 spaces!
                     27:                     When the B interpreter outputs text, it never uses
                     28:                     tabs but always emits 4 spaces for each indent level.
                     29:                     Note that the B editor also has a #defined constant
                     30:                     which sets the number of spaces for a tab on a file.
                     31:                     Finally the B editor *displays* indents as 3 spaces,
                     32:                     but *writes* them to the file as 4, so a neat
                     33:                     lay-out on the screen may look a bit garbled
                     34:                     when the file is printed.  Sorry.  */
                     35: 
                     36: Visible txptr tx, ceol;
                     37: 
                     38: Visible Procedure skipsp(tx) txptr *tx; {
                     39:        while(Space(Char(*tx))) (*tx)++;
                     40: }
                     41: 
                     42: Visible bool keymark(c) char c; {
                     43:        return Cap(c) || Dig(c) || c=='\'' || c=='"';
                     44: }
                     45: 
                     46: Hidden bool tagmark(c) char c; {
                     47:        return Letter(c) || Dig(c) || c=='\'' || c=='"';
                     48: }
                     49: 
                     50: Hidden bool keytagmark(c) char c; {
                     51:        return keymark(c) || Letter(c);
                     52: }
                     53: 
                     54: Visible bool is_expr(c) char c; {
                     55:        return Letter(c) || c == '(' || Dig(c) || c == '.' || 
                     56:                c == '\'' || c == '"' || c == '{' || 
                     57:                c=='~' || 
                     58:                c=='*' || c=='/' || c=='+' || c=='-' || c=='#';
                     59: }
                     60: 
                     61: /* ******************************************************************** */
                     62: /*             cr_text                                                 */
                     63: /* ******************************************************************** */
                     64: 
                     65: Visible value cr_text(p, q) txptr p, q; {
                     66:        /* Messes with the input line, which is a bit nasty,
                     67:           but considered preferable to copying to a separate buffer */
                     68:        value t;
                     69:        char save= Char(q);
                     70:        Char(q)= '\0';
                     71:        t= mk_text(p);
                     72:        Char(q)= save;
                     73:        return t;
                     74: }
                     75: 
                     76: /* ******************************************************************** */
                     77: /*             find, findceol, req, findrel                            */
                     78: /* ******************************************************************** */
                     79: 
                     80: #define Ptr            (*ftx)
                     81: #define Nokeymark      '+'
                     82: 
                     83: Hidden bool E_number(tx) txptr tx; {
                     84:        return Char(tx) == 'E' && Dig(Char(tx+1)) && 
                     85:                (Dig(Char(tx-1)) || Char(tx-1) == '.');
                     86: }
                     87: 
                     88: Hidden bool search(find_kw, s, q, ftx, ttx)
                     89:        bool find_kw; string s; txptr q, *ftx, *ttx; {
                     90: 
                     91:        intlet parcnt= 0; bool outs= Yes, kw= No; char aq, lc= Nokeymark;
                     92:        while (Ptr < q) {
                     93:                if (outs) {
                     94:                        if (parcnt == 0) {
                     95:                                if (find_kw) {
                     96:                                        if (Cap(Char(Ptr)) && !E_number(Ptr))
                     97:                                                return Yes;
                     98:                                } else if (Char(Ptr) == *s) {
                     99:                                        string t= s+1;
                    100:                                        *ttx= Ptr+1;
                    101:                                        while (*t && *ttx < q) {
                    102:                                                if (*t != Char(*ttx)) break;
                    103:                                                else { t++; (*ttx)++; }
                    104:                                        }
                    105:                                        if (*t);
                    106:                                        else if (Cap(*s) &&
                    107:                                                 (kw || keymark(Char(*ttx))));
                    108:                                        else return Yes;
                    109:                                }
                    110:                        }
                    111:                        switch (Char(Ptr)) {
                    112:                                case '(': case '{': case '[':
                    113:                                        parcnt++; break;
                    114:                                case ')': case '}': case ']':   
                    115:                                        if (parcnt > 0) parcnt--; break;
                    116:                                case '\'': case '"':
                    117:                                        if (!keytagmark(lc)) {
                    118:                                                outs= No; aq= Char(Ptr);
                    119:                                        }
                    120:                                        break;
                    121:                                default:
                    122:                                        break;
                    123:                        }
                    124:                        lc= Char(Ptr); kw= kw ? keymark(lc) : Cap(lc);
                    125:                } else {
                    126:                        if (Char(Ptr) == aq)
                    127:                                { outs= Yes; kw= No; lc= Nokeymark; }
                    128:                        else if (Char(Ptr) == '`') {
                    129:                                Ptr++;
                    130:                                if (!search(No, "`", q, &Ptr, ttx)) return No;
                    131:                        }
                    132:                }
                    133:                Ptr++;
                    134:        }
                    135:        return No;
                    136: }
                    137: 
                    138: /* ********************************************************************        */
                    139: 
                    140: Visible bool find(s, q, ftx, ttx) string s; txptr q, *ftx, *ttx; {
                    141:        return search(No, s, q, (*ftx= tx, ftx), ttx);
                    142: }
                    143: 
                    144: Forward txptr lcol();
                    145: 
                    146: Visible Procedure findceol() {
                    147:        txptr q= lcol(), ttx;
                    148:        if (!find("\\", q, &ceol, &ttx)) ceol= q;
                    149: }
                    150: 
                    151: Visible Procedure req(s, q, ftx, ttx) string s; txptr q, *ftx, *ttx; {
                    152:        if (!find(s, q, ftx, ttx)) {
                    153:                parerr2(MESS(2400, "cannot find expected "), MESSMAKE(s));
                    154:                *ftx= tx; *ttx= q;
                    155:        }
                    156: }
                    157: 
                    158: Hidden bool relsearch(s, q, ftx) string s; txptr q, *ftx; {
                    159:        txptr ttx;
                    160:        Ptr= tx;
                    161:        while (search(No, s, q, &Ptr, &ttx))
                    162:                switch (Char(Ptr)) {
                    163:                        case '<': if (Char(Ptr+1) == '<') Ptr= ++ttx;
                    164:                                  else if (Char(Ptr-1) == '>') Ptr= ttx;
                    165:                                  else return Yes;
                    166:                                  break;
                    167:                        case '>': if (Char(Ptr+1) == '<') Ptr= ++ttx;
                    168:                                  else if (Char(Ptr+1) == '>') Ptr= ++ttx;
                    169:                                  else return Yes;
                    170:                                  break;
                    171:                        case '=': return Yes;
                    172:                        default:  return No;
                    173:                }
                    174:        return No;
                    175: }
                    176: 
                    177: Visible bool findrel(q, ftx) txptr q, *ftx; {
                    178:        txptr ttx;
                    179:        Ptr= q;
                    180:        if (relsearch("<", Ptr, &ttx)) Ptr= ttx;
                    181:        if (relsearch(">", Ptr, &ttx)) Ptr= ttx;
                    182:        if (relsearch("=", Ptr, &ttx)) Ptr= ttx;
                    183:        return Ptr < q;
                    184: }
                    185: 
                    186: /* ******************************************************************** */
                    187: /*             tag, keyword, findkw                                    */
                    188: /* ******************************************************************** */
                    189: 
                    190: Visible bool is_tag(v) value *v; {
                    191:        if (!Letter(Char(tx))) return No;
                    192:        *v= tag();
                    193:        return Yes;
                    194: }
                    195: 
                    196: Visible value tag() {
                    197:        txptr tx0= tx;
                    198:        if (!Letter(Char(tx))) parerr(MESS(2401, "no tag where expected"));
                    199:        while (tagmark(Char(tx))) tx++;
                    200:        return cr_text(tx0, tx);
                    201: }
                    202: 
                    203: Visible bool is_keyword(v) value *v; {
                    204:        if (!Cap(Char(tx))) return No;
                    205:        *v= keyword();
                    206:        return Yes;
                    207: }
                    208:        
                    209: Visible value keyword() {
                    210:        txptr tx0= tx;
                    211:        if (!Cap(Char(tx))) parerr(MESS(2402, "no keyword where expected"));
                    212:        while (keymark(Char(tx))) tx++;
                    213:        return cr_text(tx0, tx);
                    214: }
                    215: 
                    216: Visible bool findkw(q, ftx) txptr q, *ftx; {
                    217:        txptr ttx;
                    218:        Ptr= tx;
                    219:        return search(Yes, "", q, &Ptr, &ttx);
                    220: }
                    221: 
                    222: /* ******************************************************************** */
                    223: /*             upto, nothing, ateol, atkw, need                        */
                    224: /* ******************************************************************** */
                    225: 
                    226: Visible Procedure upto(q, s) txptr q; string s; {
                    227:        skipsp(&tx);
                    228:        if (Text(q)) {
                    229:                parerr2(MESS(2403, "something unexpected following "),
                    230:                        MESSMAKE(s));
                    231:                tx= q;
                    232:        }
                    233: }
                    234: 
                    235: Visible bool nothing(q, s) txptr q; string s; {
                    236:        if (!Text(q)) {
                    237:                if (Char(tx-1) == ' ') tx--;
                    238:                parerr2(MESS(2404, "nothing instead of expected "),
                    239:                        MESSMAKE(s));
                    240:                return Yes;
                    241:        }
                    242:        return No;
                    243: }
                    244: 
                    245: Hidden bool looked_ahead= No;
                    246: Visible intlet cur_ilev;
                    247: 
                    248: Visible bool ateol() {
                    249:        if (looked_ahead) return Yes;
                    250:        skipsp(&tx);
                    251:        return Eol(tx);
                    252: }
                    253: 
                    254: Visible bool atkw(s) string s; {
                    255:        txptr tx0= tx;
                    256:        while (*s) if (*s++ != Char(tx0++)) return No;
                    257:        if (keymark(Char(tx0))) return No;
                    258:        tx= tx0;
                    259:        return Yes;
                    260: }
                    261: 
                    262: Visible Procedure need(s) string s; {
                    263:        string t= s;
                    264:        skipsp(&tx);
                    265:        while (*t)
                    266:                if (*t++ != Char(tx++)) {
                    267:                        tx--;
                    268:                        parerr2(MESS(2405, "according to the syntax I expected "),
                    269:                                MESSMAKE(s));
                    270:                        return;
                    271:                }
                    272: }
                    273: 
                    274: /* ******************************************************************** */
                    275: /*             buffer handling                                         */
                    276: /* ******************************************************************** */
                    277: 
                    278: Visible txptr first_col;
                    279: 
                    280: Visible txptr fcol() { /* the first position of the current line */
                    281:        return first_col;
                    282: }
                    283: 
                    284: Hidden txptr lcol() { /* the position beyond the last character of the line */
                    285:        txptr ax= tx;
                    286:        while (!Eol(ax)) ax++;
                    287:        return ax;
                    288: }
                    289: 
                    290: Visible intlet ilev() {
                    291:        intlet i;
                    292:        if (looked_ahead) {
                    293:                looked_ahead= No;
                    294:                return cur_ilev;
                    295:        } else {
                    296:                first_col= tx= getline();
                    297:                looked_ahead= No;
                    298:                lino++;
                    299:                f_lino++;
                    300:                i= 0;
                    301:                while (Space(Char(tx)))
                    302:                        if (Char(tx++) == ' ') i++;
                    303:                        else i= (i/TABSIZE+1)*TABSIZE;
                    304:                if (Char(tx) == '\\') return cur_ilev= 0;
                    305:                if (Char(tx) == '\n') return cur_ilev= 0;
                    306:                if (i%4 == 2)
                    307:                        parerr(MESS(2406, "cannot make out indentation; use tab to indent"));
                    308:                return cur_ilev= (i+1)/4; /* small deviation accepted */
                    309:        }
                    310: }
                    311: 
                    312: Visible Procedure veli() { /* After a look-ahead call of ilev */
                    313:        looked_ahead= Yes;
                    314: }
                    315: 
                    316: Visible Procedure first_ilev() { /* initialise read buffer for new input */
                    317:        looked_ahead= No;
                    318:        VOID ilev();
                    319:        findceol();
                    320: }
                    321: 
                    322: /* ********************************************************************        */
                    323: 
                    324: /* The reserved keywords that a user command may not begin with: */
                    325: 
                    326: Visible value kwlist;
                    327: 
                    328: Hidden string kwtab[] = {
                    329:        K_SHARE, K_CHECK, K_CHOOSE, K_DELETE, K_DRAW, K_FAIL, K_FOR,
                    330:        K_HOW_TO, K_IF, K_INSERT, K_PUT, K_QUIT, K_READ, K_REMOVE,
                    331:        K_REPORT, K_RETURN, K_SELECT, K_SET_RANDOM, K_SUCCEED,
                    332:        K_TEST, K_WHILE, K_WRITE, K_YIELD, K_ELSE,
                    333:        ""
                    334: };
                    335: 
                    336: Visible Procedure initsyn() {
                    337:        value v;
                    338:        string *kw;
                    339:        kwlist= mk_elt();
                    340:        for (kw= kwtab; **kw != '\0'; kw++) {
                    341:                insert(v= mk_text(*kw), &kwlist);
                    342:                release(v);
                    343:        }
                    344: }
                    345: 
                    346: Visible Procedure endsyn() {
                    347:        release(kwlist); kwlist= Vnil;
                    348: }
                    349: 
                    350: /* ******************************************************************** */
                    351: /*             signs                                                   */
                    352: /* ********************************************************************        */
                    353: 
                    354: Visible string textsign;
                    355: 
                    356: Hidden bool la_denum(tx0) txptr tx0; {
                    357:        char l, r;
                    358:        switch (l= Char(++tx0)) {
                    359:                case '/':       r= '*'; break;
                    360:                case '*':       r= '/'; break;
                    361:                default:        return Yes;
                    362:        }
                    363:        do if (Char(++tx0) != r) return No; while (Char(++tx0) == l);
                    364:        return Yes;
                    365: }
                    366: 
                    367: #ifdef NOT_USED
                    368: Visible bool colon_sign() {
                    369:        return Char(tx) == ':' ? (tx++, Yes) : No;
                    370: }
                    371: #endif
                    372: 
                    373: Visible bool comment_sign() {
                    374:        return Char(tx) == '\\' ? (tx++, Yes) : No;
                    375: }
                    376: 
                    377: Visible bool nwl_sign() {
                    378:        return Char(tx) == '/' && !la_denum(tx-1) ? (tx++, Yes) : No;
                    379: }
                    380: 
                    381: Visible bool open_sign() {
                    382:        return Char(tx) == '(' ? (tx++, Yes) : No;
                    383: }
                    384: 
                    385: #ifdef NOT_USED
                    386: Visible bool close_sign() {    
                    387:        return Char(tx) == ')' ? (tx++, Yes) : No;
                    388: }
                    389: #endif
                    390: 
                    391: #ifdef NOT_USED
                    392: Visible bool comma_sign() {
                    393:        return Char(tx) == ',' ? (tx++, Yes) : No;
                    394: }
                    395: #endif
                    396: 
                    397: Visible bool point_sign() {
                    398:        return Char(tx) == '.' ? (tx++, Yes) : No;
                    399: }
                    400: 
                    401: Visible bool apostrophe_sign() {
                    402:        return Char(tx) == '\'' ? (tx++, textsign= "'", Yes) : No;
                    403: }
                    404: 
                    405: Visible bool quote_sign() {
                    406:        return Char(tx) == '"' ? (tx++, textsign= "\"", Yes) : No;
                    407: }
                    408: 
                    409: Visible bool conv_sign() {
                    410:        return Char(tx) == '`' ? (tx++, Yes) : No;
                    411: }
                    412: 
                    413: Visible bool curlyopen_sign() {
                    414:        return Char(tx) == '{' ? (tx++, Yes) : No;
                    415: }
                    416: 
                    417: Visible bool curlyclose_sign() {
                    418:        return Char(tx) == '}' ? (tx++, Yes) : No;
                    419: }
                    420: 
                    421: Visible bool sub_sign() {
                    422:        return Char(tx) == '[' ? (tx++, Yes) : No;
                    423: }
                    424: 
                    425: #ifdef NOT_USED
                    426: Visible bool bus_sign() {
                    427:        return Char(tx) == ']' ? (tx++, Yes) : No;
                    428: }
                    429: #endif
                    430: 
                    431: Visible bool behead_sign() {
                    432:        return Char(tx) == '@' ? (tx++, textsign= "@", Yes) : No;
                    433: }
                    434: 
                    435: Visible bool curtl_sign() {
                    436:        return Char(tx) == '|' ? (tx++, textsign= "|", Yes) : No;
                    437: }
                    438: 
                    439: Visible bool about_sign() {
                    440:        return Char(tx) == '~' ? (tx++, textsign= "~", Yes) : No;
                    441: }
                    442: 
                    443: Visible bool plus_sign() {
                    444:        return Char(tx) == '+' ? (tx++, textsign= "+", Yes) : No;
                    445: }
                    446: 
                    447: Visible bool minus_sign() {
                    448:        return Char(tx) == '-' ? (tx++, textsign= "-", Yes) : No;
                    449: }
                    450: 
                    451: Visible bool times_sign() {
                    452:        return Char(tx) == '*' && la_denum(tx)
                    453:                ? (tx++, textsign= "*", Yes) : No;
                    454: }
                    455: 
                    456: Visible bool over_sign() {
                    457:        return Char(tx) == '/' && la_denum(tx)
                    458:                ? (tx++, textsign= "/", Yes) : No;
                    459: }
                    460: 
                    461: Visible bool power_sign() {
                    462:        return Char(tx) == '*' && Char(tx+1) == '*' && la_denum(tx+1)
                    463:                ? (tx+= 2, textsign= "**", Yes) : No;
                    464: }
                    465: 
                    466: Visible bool numtor_sign() {
                    467:        return Char(tx) == '*' && Char(tx+1) == '/' && la_denum(tx+1)
                    468:                ? (tx+= 2, textsign= "*/", Yes) : No;
                    469: }
                    470: 
                    471: Visible bool denomtor_sign() {
                    472:        return Char(tx) == '/' && Char(tx+1) == '*' && la_denum(tx+1)
                    473:                ? (tx+= 2, textsign= "/*", Yes) : No;
                    474: }
                    475: 
                    476: Visible bool join_sign() {
                    477:        return Char(tx) == '^' && Char(tx+1) != '^'
                    478:                ? (tx++, textsign= "^", Yes) : No;
                    479: }
                    480: 
                    481: Visible bool reptext_sign() {
                    482:        return Char(tx) == '^' && Char(tx+1) == '^'
                    483:                ? (tx+= 2, textsign= "^^", Yes) : No;
                    484: }
                    485: 
                    486: Visible bool leftadj_sign() {
                    487:        return Char(tx) == '<' && Char(tx+1) == '<'
                    488:                ? (tx+= 2, textsign= "<<", Yes) : No;
                    489: }
                    490: 
                    491: Visible bool center_sign() {
                    492:        return Char(tx) == '>' && Char(tx+1) == '<' 
                    493:                ? (tx+= 2, textsign= "><", Yes) : No;
                    494: }
                    495: 
                    496: Visible bool rightadj_sign() {
                    497:        return Char(tx) == '>' && Char(tx+1) == '>' 
                    498:                ? (tx+= 2, textsign= ">>", Yes) : No;
                    499: }
                    500: 
                    501: Visible bool number_sign() {           
                    502:        return Char(tx) == '#' ? (tx++, textsign= "#", Yes) : No;
                    503: }
                    504: 
                    505: Visible bool less_than_sign() {
                    506:        return Char(tx) == '<' && Char(tx+1) != '=' &&
                    507:                Char(tx+1) != '>' && Char(tx+1) != '<'
                    508:                ? (tx++, Yes) : No;
                    509: }
                    510: 
                    511: Visible bool at_most_sign() {  
                    512:        return Char(tx) == '<' && Char(tx+1) == '=' ? (tx+= 2, Yes) : No;
                    513: }
                    514: 
                    515: Visible bool equals_sign() {
                    516:        return Char(tx) == '=' ? (tx++, Yes) : No;
                    517: }
                    518: 
                    519: Visible bool unequal_sign() {
                    520:        return Char(tx) == '<' && Char(tx+1) == '>' ? (tx+= 2, Yes) : No;
                    521: }
                    522: 
                    523: Visible bool at_least_sign() {
                    524:        return Char(tx) == '>' && Char(tx+1) == '=' ? (tx+= 2, Yes) : No;
                    525: }
                    526: 
                    527: Visible bool greater_than_sign() {
                    528:        return Char(tx) == '>' && Char(tx+1) != '='
                    529:                && Char(tx+1) != '>' && Char(tx+1) != '<'
                    530:                ? (tx++, Yes) : No;
                    531: }
                    532: 
                    533: Visible bool dyamon_sign() {
                    534:        return plus_sign() || minus_sign() || number_sign();
                    535: }
                    536: 
                    537: Visible bool dya_sign() {
                    538:        return times_sign() || over_sign() || power_sign() || 
                    539:                join_sign() || reptext_sign() || 
                    540:                leftadj_sign() || center_sign() || rightadj_sign();
                    541: }
                    542: 
                    543: Visible bool mon_sign() {
                    544:        return about_sign() || numtor_sign() || denomtor_sign();
                    545: }
                    546: 
                    547: Visible bool trim_sign() {
                    548:        return behead_sign() || curtl_sign();
                    549: }
                    550: 
                    551: Visible bool check_keyword()           { return atkw(K_CHECK); }
                    552: Visible bool choose_keyword()          { return atkw(K_CHOOSE); }
                    553: Visible bool delete_keyword()          { return atkw(K_DELETE); }
                    554: Visible bool draw_keyword()            { return atkw(K_DRAW); }
                    555: Visible bool insert_keyword()          { return atkw(K_INSERT); }
                    556: Visible bool put_keyword()             { return atkw(K_PUT); }
                    557: Visible bool read_keyword()            { return atkw(K_READ); }
                    558: Visible bool remove_keyword()          { return atkw(K_REMOVE); }
                    559: Visible bool setrandom_keyword()       { return atkw(K_SET_RANDOM); }
                    560: Visible bool write_keyword()           { return atkw(K_WRITE); }
                    561: Visible bool fail_keyword()            { return atkw(K_FAIL); }
                    562: Visible bool quit_keyword()            { return atkw(K_QUIT); }
                    563: Visible bool return_keyword()          { return atkw(K_RETURN); }
                    564: Visible bool report_keyword()          { return atkw(K_REPORT); }
                    565: Visible bool succeed_keyword()                 { return atkw(K_SUCCEED); }
                    566: Visible bool if_keyword()              { return atkw(K_IF); }
                    567: Visible bool select_keyword()          { return atkw(K_SELECT); }
                    568: Visible bool while_keyword()           { return atkw(K_WHILE); }
                    569: Visible bool for_keyword()             { return atkw(K_FOR); }
                    570: Visible bool else_keyword()            { return atkw(K_ELSE); }
                    571: #ifdef NOT_USED
                    572: Visible bool and_keyword()             { return atkw(K_AND); }
                    573: Visible bool or_keyword()              { return atkw(K_OR); }
                    574: #endif
                    575: Visible bool not_keyword()             { return atkw(K_NOT); }
                    576: Visible bool some_keyword()            { return atkw(K_SOME); }
                    577: Visible bool each_keyword()            { return atkw(K_EACH); }
                    578: Visible bool no_keyword()              { return atkw(K_NO); }
                    579: Visible bool how_to_keyword()          { return atkw(K_HOW_TO); }
                    580: Visible bool yield_keyword()           { return atkw(K_YIELD); }
                    581: Visible bool test_keyword()            { return atkw(K_TEST); }
                    582: Visible bool share_keyword()           { return atkw(K_SHARE); }

unix.superglobalmegacorp.com

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