Annotation of 43BSDReno/contrib/jove/keymaps.c, revision 1.1

1.1     ! root        1: /***************************************************************************
        !             2:  * This program is Copyright (C) 1986, 1987, 1988 by Jonathan Payne.  JOVE *
        !             3:  * is provided to you without charge, and with no warranty.  You may give  *
        !             4:  * away copies of JOVE, including sources, provided that this notice is    *
        !             5:  * included in all the files.                                              *
        !             6:  ***************************************************************************/
        !             7: 
        !             8: #include "jove.h"
        !             9: #include "list.h"
        !            10: #include "fp.h"
        !            11: #include "termcap.h"
        !            12: #include "chars.h"
        !            13: #include "disp.h"
        !            14: #include "re.h"
        !            15: 
        !            16: /* Up until now a keymap was an array of pointers to
        !            17:    data_obj's.  A data_obj was either a pointer to a built-in
        !            18:    command or a keyboard macro.  Now a data_obj can be a
        !            19:    pointer to a keymap as well, which is how prefix keys will
        !            20:    be handled.
        !            21: 
        !            22:    There will be a way to build keymaps and give them names,
        !            23:    and to look those keymaps up by name, attach them to keys.
        !            24:    There will be a way to specify a string of key strokes and
        !            25:    have a series of keymaps built automatically for those
        !            26:    sequences. */
        !            27: 
        !            28: private        void
        !            29:        fb_aux proto((data_obj *, struct keymap *, char *, char *)),
        !            30:        find_binds proto((data_obj *, char *));
        !            31: 
        !            32: private List   *keymaps;               /* list of all keymaps */
        !            33: private struct keymap  *mainmap;
        !            34: #if defined(IPROCS)
        !            35: private struct keymap  *procsmap;
        !            36: #endif
        !            37: 
        !            38: /* make a new keymap, give it name NAME, initialize the keys array
        !            39:    to keys, if nonzero, or make an empty one, otherwise */
        !            40: 
        !            41: private struct keymap *
        !            42: km_new(name, keys)
        !            43: char   *name;
        !            44: data_obj       **keys;
        !            45: {
        !            46:        struct keymap   *km;
        !            47: 
        !            48:        km = (struct keymap *) emalloc(sizeof *km);
        !            49:        (void) list_push(&keymaps, (Element *) km);
        !            50:        km->Type = KEYMAP;
        !            51:        km->Name = name;
        !            52:        if (keys != 0) {
        !            53:                km->k_keys = keys;
        !            54:                km->k_alloc_p = NO;
        !            55:        } else {
        !            56:                km->k_keys = (data_obj **) emalloc(NCHARS * sizeof (data_obj *));
        !            57:                byte_zero((char *) km->k_keys, NCHARS * sizeof (data_obj *));
        !            58:                km->k_alloc_p = YES;
        !            59:        }
        !            60:        return km;
        !            61: }
        !            62: 
        !            63: #ifdef NEVER_USED
        !            64: 
        !            65: /* free up a keymap */
        !            66: 
        !            67: private void
        !            68: km_destroy(km)
        !            69: struct keymap  *km;
        !            70: {
        !            71:        if (km->k_alloc_p == YES)
        !            72:                free((char *) km->k_keys);
        !            73:        km->k_keys = 0;
        !            74:        free((char *) km);
        !            75: }
        !            76: 
        !            77: /* lookup a keymap by name */
        !            78: 
        !            79: private struct keymap *
        !            80: km_lookup(name)
        !            81: char   *name;
        !            82: {
        !            83:        List    *lp;
        !            84: 
        !            85:        for (lp = keymaps; lp != 0; lp = list_next(lp))
        !            86:                if (strcmp(name, ((struct keymap *) list_data(lp))->Name) == 0)
        !            87:                        break;
        !            88:        if (lp == 0)
        !            89:                return 0;
        !            90:        return (struct keymap *) list_data(lp);
        !            91: }
        !            92: 
        !            93: #endif
        !            94: 
        !            95: /* given a map and a key, return the object bound to that key */
        !            96: 
        !            97: #define km_getkey(m, c)        ((m)->k_keys[(c) & CHARMASK])
        !            98: 
        !            99: #ifndef km_getkey
        !           100: data_obj *
        !           101: km_getkey(m, c)
        !           102: struct keymap  *m;
        !           103: int    c;
        !           104: {
        !           105:        return (m->k_keys[c & CHARMASK]);
        !           106: }
        !           107: #endif
        !           108: 
        !           109: private void
        !           110: km_setkey(m, c, d)
        !           111: struct keymap  *m;
        !           112: int    c;
        !           113: data_obj       *d;
        !           114: {
        !           115:        m->k_keys[c & CHARMASK] = d;
        !           116: }
        !           117: 
        !           118: /* get the currently active keymaps into km_buf */
        !           119: 
        !           120: private int
        !           121: get_keymaps(km_buf)
        !           122: struct keymap  **km_buf;
        !           123: {
        !           124:        int     nmaps = 0;
        !           125: 
        !           126: #ifdef IPROCS
        !           127:        if (curbuf->b_process != 0)
        !           128:                km_buf[nmaps++] = procsmap;
        !           129: #endif
        !           130:        if (curbuf->b_map != 0)
        !           131:                km_buf[nmaps++] = curbuf->b_map;
        !           132:        km_buf[nmaps++] = mainmap;
        !           133: 
        !           134:        return nmaps;
        !           135: }
        !           136: 
        !           137: private struct keymap *
        !           138: IsPrefix(cp)
        !           139: data_obj       *cp;
        !           140: {
        !           141:        if (cp == 0 || (cp->Type & TYPEMASK) != KEYMAP)
        !           142:                return 0;
        !           143:        return (struct keymap *) cp;
        !           144: }
        !           145: 
        !           146: /* Is `c' a prefix character */
        !           147: 
        !           148: int
        !           149: PrefChar(c)
        !           150: int    c;
        !           151: {
        !           152:        return (int) IsPrefix(km_getkey(mainmap, c));
        !           153: }
        !           154: 
        !           155: void
        !           156: UnbindC()
        !           157: {
        !           158:        char    *keys;
        !           159:        struct keymap   *map = mainmap;
        !           160: 
        !           161:        keys = ask((char *) 0, ProcFmt);
        !           162:        for (;;) {
        !           163:                if (keys[1] == '\0')
        !           164:                        break;
        !           165:                if ((map = IsPrefix(km_getkey(map, *keys))) == 0)
        !           166:                        break;
        !           167:                keys += 1;
        !           168:        }
        !           169:        if (keys[1] != 0)
        !           170:                complain("That's not a legitimate key sequence.");
        !           171:        km_setkey(map, keys[0], (data_obj *) 0);
        !           172: }
        !           173: 
        !           174: void
        !           175: BindWMap(map, lastkey, cmd)
        !           176: struct keymap  *map;
        !           177: int    lastkey;
        !           178: data_obj       *cmd;
        !           179: {
        !           180:        struct keymap   *nextmap;
        !           181:        int     c;
        !           182: 
        !           183:        c = addgetc();
        !           184:        if (c == EOF) {
        !           185:                if (lastkey == EOF)
        !           186:                        complain("[Empty key sequence]");
        !           187:                complain("[Premature end of key sequence]");
        !           188:        } else {
        !           189:                if ((nextmap = IsPrefix(km_getkey(map, c))) != NULL)
        !           190:                        BindWMap(nextmap, c, cmd);
        !           191:                else {
        !           192:                        km_setkey(map, c, cmd);
        !           193: #ifdef MAC
        !           194:                        ((struct cmd *) cmd)->c_key = c;        /* see about_j() in mac.c */
        !           195:                        if (map->k_keys == MainKeys)
        !           196:                                ((struct cmd *) cmd)->c_map = F_MAINMAP;
        !           197:                        else if (map->k_keys == EscKeys)
        !           198:                                ((struct cmd *) cmd)->c_map = F_PREF1MAP;
        !           199:                        else if (map == (struct keymap *) CtlxKeys)
        !           200:                                ((struct cmd *) cmd)->c_map = F_PREF2MAP;
        !           201: #endif
        !           202:                }
        !           203:        }
        !           204: }
        !           205: 
        !           206: void
        !           207: BindSomething(proc, map)
        !           208: #if defined(MAC) || defined(IBMPC)
        !           209: data_obj       *(*proc)();
        !           210: #else
        !           211: data_obj       *(*proc) proto((char *));
        !           212: #endif
        !           213: struct keymap  *map;
        !           214: {
        !           215:        data_obj        *d;
        !           216: 
        !           217:        if ((d = (*proc)(ProcFmt)) == 0)
        !           218:                return;
        !           219:        s_mess(": %f %s ", d->Name);
        !           220:        BindWMap(map, EOF, d);
        !           221: }
        !           222: 
        !           223: void
        !           224: BindAKey()
        !           225: {
        !           226:        BindSomething(findcom, mainmap);
        !           227: }
        !           228: 
        !           229: void
        !           230: BindMac()
        !           231: {
        !           232:        BindSomething(findmac, mainmap);
        !           233: }
        !           234: 
        !           235: void
        !           236: DescWMap(map, key)
        !           237: struct keymap  *map;
        !           238: int    key;
        !           239: {
        !           240:        data_obj        *cp = km_getkey(map, key);
        !           241:        struct keymap   *prefp;
        !           242: 
        !           243:        if (cp == 0)
        !           244:                add_mess("is unbound.");
        !           245:        else if ((prefp = IsPrefix(cp)) != NULL)
        !           246:                DescWMap(prefp, addgetc());
        !           247:        else
        !           248:                add_mess("is bound to %s.", cp->Name);
        !           249: }
        !           250: 
        !           251: void
        !           252: KeyDesc()
        !           253: {
        !           254:        s_mess(ProcFmt);
        !           255:        DescWMap(mainmap, addgetc());
        !           256: }
        !           257: 
        !           258: void
        !           259: DescBindings()
        !           260: {
        !           261:        TOstart("Key Bindings", TRUE);
        !           262:        DescMap(mainmap, NullStr);
        !           263:        TOstop();
        !           264: }
        !           265: 
        !           266: void
        !           267: DescMap(map, pref)
        !           268: struct keymap  *map;
        !           269: char   *pref;
        !           270: {
        !           271:        int     c1,
        !           272:                c2 = 0,
        !           273:                numbetween;
        !           274:        char    keydescbuf[40];
        !           275:        struct keymap   *prefp;
        !           276: 
        !           277:        for (c1 = 0; c1 < NCHARS && c2 < NCHARS; c1 = c2 + 1) {
        !           278:                c2 = c1;
        !           279:                if (km_getkey(map, c1) == 0)
        !           280:                        continue;
        !           281:                while (++c2 < NCHARS && km_getkey(map, c1) == km_getkey(map, c2))
        !           282:                        ;
        !           283:                c2 -= 1;
        !           284:                numbetween = c2 - c1;
        !           285:                if (numbetween == 1)
        !           286:                        swritef(keydescbuf, "%s {%p,%p}", pref, c1, c2);
        !           287:                else if (numbetween == 0)
        !           288:                        swritef(keydescbuf, "%s %p", pref, c1);
        !           289:                else
        !           290:                        swritef(keydescbuf, "%s [%p-%p]", pref, c1, c2);
        !           291:                if ((prefp = IsPrefix(km_getkey(map, c1)))!=NULL && (prefp != map)) {
        !           292:                        Typeout("%-18s KEYMAP %s", keydescbuf, km_getkey(map, c1)->Name);
        !           293:                        DescMap(prefp, keydescbuf);
        !           294:                }
        !           295:                else
        !           296:                        Typeout("%-18s %s", keydescbuf, km_getkey(map, c1)->Name);
        !           297:        }
        !           298: }
        !           299: 
        !           300: private void
        !           301: find_binds(dp, buf)
        !           302: data_obj       *dp;
        !           303: char   *buf;
        !           304: {
        !           305:        char    *endp;
        !           306: 
        !           307:        buf[0] = '\0';
        !           308:        fb_aux(dp, mainmap, (char *) 0, buf);
        !           309:        endp = buf + strlen(buf) - 2;
        !           310:        if ((endp > buf) && (strcmp(endp, ", ") == 0))
        !           311:                *endp = '\0';
        !           312: }
        !           313: 
        !           314: private void
        !           315: fb_aux(cp, map, prefix, buf)
        !           316: register data_obj      *cp;
        !           317: struct keymap  *map;
        !           318: char   *buf,
        !           319:        *prefix;
        !           320: {
        !           321:        int     c1,
        !           322:                c2;
        !           323:        char    *bufp = buf + strlen(buf),
        !           324:                prefbuf[20];
        !           325:        struct keymap   *prefp;
        !           326: 
        !           327:        for (c1 = c2 = 0; c1 < NCHARS && c2 < NCHARS; c1 = c2 + 1) {
        !           328:                c2 = c1;
        !           329:                if (km_getkey(map, c1) == cp) {
        !           330:                        while (++c2 < NCHARS && km_getkey(map, c1) == km_getkey(map, c2))
        !           331:                                ;
        !           332:                        c2 -= 1;
        !           333:                        if (prefix)
        !           334:                                swritef(bufp, "%s ", prefix);
        !           335:                        bufp += strlen(bufp);
        !           336:                        switch (c2 - c1) {
        !           337:                        case 0:
        !           338:                                swritef(bufp, "%p, ", c1);
        !           339:                                break;
        !           340: 
        !           341:                        case 1:
        !           342:                                swritef(bufp, "{%p,%p}, ", c1, c2);
        !           343:                                break;
        !           344: 
        !           345:                        default:
        !           346:                                swritef(bufp, "[%p-%p], ", c1, c2);
        !           347:                                break;
        !           348:                        }
        !           349:                }
        !           350:                if ((prefp = IsPrefix(km_getkey(map, c1)))!=NULL && (prefp != map))  {
        !           351:                        swritef(prefbuf, "%p", c1);
        !           352:                        fb_aux(cp, prefp, prefbuf, bufp);
        !           353:                }
        !           354:                bufp += strlen(bufp);
        !           355:        }
        !           356: }
        !           357: 
        !           358: void
        !           359: DescCom()
        !           360: {
        !           361:        data_obj        *dp;
        !           362:        char    pattern[100],
        !           363:                *doc_type,
        !           364:                *file = CmdDb;
        !           365:        static char var_type[] = "Variable";
        !           366:        static char cmd_type[] = "Command";
        !           367:        File    *fp;
        !           368: 
        !           369:        if (!strcmp(LastCmd->Name, "describe-variable")) {
        !           370:                doc_type = var_type;
        !           371:                dp = (data_obj *) findvar(ProcFmt);
        !           372:        } else {
        !           373:                doc_type = cmd_type;
        !           374:                dp = (data_obj *) findcom(ProcFmt);
        !           375:        }
        !           376:        if (dp == 0)
        !           377:                return;
        !           378:        fp = open_file(file, iobuff, F_READ, YES, YES);
        !           379:        Placur(ILI, 0);
        !           380:        flusho();
        !           381:        swritef(pattern, "^:entry \"%s\" \"%s\"$", dp->Name, doc_type);
        !           382:        TOstart("Help", TRUE);
        !           383:        for (;;) {
        !           384:                if (f_gets(fp, genbuf, (size_t)LBSIZE) == EOF) {
        !           385:                        Typeout("There is no documentation for \"%s\".", dp->Name);
        !           386:                        break;
        !           387:                }
        !           388:                if (genbuf[0] == ':' && LookingAt(pattern, genbuf, 0)) {
        !           389:                        /* found it ... let's print it */
        !           390:                        if (doc_type == var_type)
        !           391:                                Typeout(dp->Name);
        !           392:                        else if (doc_type == cmd_type) {
        !           393:                                char    binding[128];
        !           394: 
        !           395:                                find_binds(dp, binding);
        !           396:                                if (blnkp(binding))
        !           397:                                        Typeout("To invoke %s, type \"ESC X %s<cr>\".",
        !           398:                                                dp->Name, dp->Name);
        !           399:                                else
        !           400:                                        Typeout("Type \"%s\" to invoke %s.",
        !           401:                                                binding, dp->Name);
        !           402:                        }
        !           403:                        Typeout("");
        !           404:                        while (f_gets(fp, genbuf, (size_t)LBSIZE) != EOF
        !           405:                        && strncmp(genbuf, ":entry", (size_t)6) != 0) {
        !           406:                                Typeout("%s", genbuf);
        !           407:                        }
        !           408:                        break;
        !           409:                }
        !           410:        }
        !           411:        f_close(fp);
        !           412:        TOstop();
        !           413: }
        !           414: 
        !           415: 
        !           416: void
        !           417: Apropos()
        !           418: {
        !           419:        register const struct cmd       *cp;
        !           420:        register struct macro   *m;
        !           421:        register const struct variable  *v;
        !           422:        register List   *klp;
        !           423:        char    *ans;
        !           424:        int     anyfs = NO,
        !           425:                anyvs = NO,
        !           426:                anyms = NO,
        !           427:                anykms = NO;
        !           428:        char    buf[256];
        !           429: 
        !           430:        ans = ask((char *) 0, ": %f (keyword) ");
        !           431:        TOstart("Help", TRUE);
        !           432:        for (cp = commands; cp->Name != 0; cp++)
        !           433:                if (sindex(ans, cp->Name)) {
        !           434:                        if (anyfs == NO) {
        !           435:                                Typeout("Commands");
        !           436:                                Typeout("--------");
        !           437:                                anyfs = YES;
        !           438:                        }
        !           439:                        find_binds((data_obj *) cp, buf);
        !           440:                        if (buf[0])
        !           441:                                Typeout(": %-35s (%s)", cp->Name, buf);
        !           442:                        else
        !           443:                                Typeout(": %s", cp->Name);
        !           444:                }
        !           445:        if (anyfs == YES)
        !           446:                Typeout(NullStr);
        !           447:        for (v = variables; v->Name != 0; v++)
        !           448:                if (sindex(ans, v->Name)) {
        !           449:                        if (anyvs == NO) {
        !           450:                                Typeout("Variables");
        !           451:                                Typeout("---------");
        !           452:                                anyvs = YES;
        !           453:                        }
        !           454:                        vpr_aux(v, buf);
        !           455:                        Typeout(": set %-26s %s", v->Name, buf);
        !           456:                }
        !           457:        if (anyvs == YES)
        !           458:                Typeout(NullStr);
        !           459:        for (m = macros; m != 0; m = m->m_nextm)
        !           460:                if (sindex(ans, m->Name)) {
        !           461:                        if (anyms == NO) {
        !           462:                                Typeout("Macros");
        !           463:                                Typeout("------");
        !           464:                                anyms = YES;
        !           465:                        }
        !           466:                        find_binds((data_obj *) m, buf);
        !           467:                        if (buf[0])
        !           468:                                Typeout(": %-35s (%s)", m->Name, buf);
        !           469:                        else
        !           470:                                Typeout(": %-35s %s", "execute-macro", m->Name);
        !           471:                }
        !           472:        if (anyms == YES)
        !           473:                Typeout(NullStr);
        !           474:        for (klp = keymaps; klp; klp = list_next(klp)) {
        !           475:                register struct keymap *km = (struct keymap *)list_data(klp);
        !           476: 
        !           477:                if (sindex(ans, km->Name)) {
        !           478:                        if (anykms == NO) {
        !           479:                                Typeout("Keymaps");
        !           480:                                Typeout("-------");
        !           481:                                anykms = YES;
        !           482:                        }
        !           483:                        find_binds((data_obj *) km, buf);
        !           484:                        if (buf[0])
        !           485:                                Typeout(": %-35s (%s)", km->Name, buf);
        !           486:                        else
        !           487:                                Typeout(": %s", km->Name);
        !           488:                }
        !           489:        }
        !           490:        TOstop();
        !           491: }
        !           492: 
        !           493: #ifdef NEVER_USED
        !           494: private char *
        !           495: km_newname()
        !           496: {
        !           497:        char    buffer[128];
        !           498:        static int      km_count = 1;
        !           499: 
        !           500:        swritef(buffer, "keymap-%d", km_count++);
        !           501:        return copystr(buffer);
        !           502: }
        !           503: #endif
        !           504: 
        !           505: void
        !           506: InitKeymaps()
        !           507: {
        !           508:        struct keymap   *km;
        !           509: 
        !           510:        mainmap = km_new(copystr("mainmap"), MainKeys);
        !           511: 
        !           512:        /* setup ESC map */
        !           513:        km = km_new(copystr("ESC-map"), EscKeys);
        !           514:        km_setkey(mainmap, ESC, (data_obj *) km);
        !           515: 
        !           516:        /* setup Ctlx map */
        !           517:        km = km_new(copystr("CTLX-map"), CtlxKeys);
        !           518:        km_setkey(mainmap, CTL('X'), (data_obj *) km);
        !           519: }
        !           520: 
        !           521: void
        !           522: MakeKMap()
        !           523: {
        !           524:        char    *name;
        !           525: 
        !           526:        name = ask((char *) 0, ProcFmt);
        !           527:        (void) km_new(copystr(name), (data_obj **) 0);
        !           528: }
        !           529: 
        !           530: private data_obj *
        !           531: findmap(fmt)
        !           532: char   *fmt;
        !           533: {
        !           534:        List    *lp;
        !           535:        char    *strings[128];
        !           536:        int     i;
        !           537: 
        !           538:        for (lp = keymaps, i = 0; lp != 0; lp = list_next(lp))
        !           539:                strings[i++] = ((struct keymap *) list_data(lp))->Name;
        !           540:        strings[i] = 0;
        !           541: 
        !           542:        i = complete(strings, fmt, 0);
        !           543:        if (i < 0)
        !           544:                return 0;
        !           545:        lp = keymaps;
        !           546:        while (--i >= 0)
        !           547:                lp = list_next(lp);
        !           548:        return (data_obj *) list_data(lp);
        !           549: }
        !           550: 
        !           551: #ifdef IPROCS
        !           552: private void
        !           553: mk_proc_km()
        !           554: {
        !           555:        procsmap = km_new("process-keymap", (data_obj **) 0);
        !           556: }
        !           557: 
        !           558: void
        !           559: ProcBind()
        !           560: {
        !           561:        data_obj        *d;
        !           562: 
        !           563:        if (procsmap == 0)
        !           564:                mk_proc_km();
        !           565: 
        !           566:        if ((d = findcom(ProcFmt)) == 0)
        !           567:                return;
        !           568:        s_mess(": %f %s ", d->Name);
        !           569:        BindWMap(procsmap, EOF, d);
        !           570: }
        !           571: 
        !           572: void
        !           573: ProcKmBind()
        !           574: {
        !           575:        data_obj        *d;
        !           576: 
        !           577:        if (procsmap == 0)
        !           578:                mk_proc_km();
        !           579:        if ((d = findmap(ProcFmt)) == 0)
        !           580:                return;
        !           581:        s_mess(": %f %s ", d->Name);
        !           582:        BindWMap(procsmap, EOF, d);
        !           583: }
        !           584: 
        !           585: #endif
        !           586: 
        !           587: void
        !           588: KmBind()
        !           589: {
        !           590:        BindSomething(findmap, mainmap);
        !           591: }
        !           592: 
        !           593: void
        !           594: dispatch(c)
        !           595: register int   c;
        !           596: {
        !           597:        data_obj        *cp;
        !           598:        struct keymap   *maps[10];      /* never more than 10 active
        !           599:                                           maps at a time, I promise */
        !           600:        int     nmaps;
        !           601: 
        !           602:        this_cmd = 0;
        !           603:        nmaps = get_keymaps(maps);
        !           604: 
        !           605:        for (;;) {
        !           606:                int     i,
        !           607:                        nvalid,
        !           608:                        slow = NO;
        !           609: 
        !           610:                for (i = 0, nvalid = 0; i < nmaps; i++) {
        !           611:                        if (maps[i] == 0)
        !           612:                                continue;
        !           613:                        cp = km_getkey(maps[i], c);
        !           614:                        if (cp != 0) {
        !           615:                                if (obj_type(cp) != KEYMAP) {
        !           616:                                        ExecCmd(cp);
        !           617:                                        return;
        !           618:                                }
        !           619:                                nvalid += 1;
        !           620:                        }
        !           621:                        maps[i] = (struct keymap *) cp;
        !           622:                }
        !           623:                if (nvalid == 0) {
        !           624:                        char    strokes[128];
        !           625: 
        !           626:                        pp_key_strokes(strokes, sizeof (strokes));
        !           627:                        s_mess("[%sunbound]", strokes);
        !           628:                        rbell();
        !           629:                        clr_arg_value();
        !           630:                        errormsg = NO;
        !           631:                        return;
        !           632:                }
        !           633: 
        !           634:                c = waitchar(&slow);
        !           635:                if (c == AbortChar) {
        !           636:                        message("[Aborted]");
        !           637:                        rbell();
        !           638:                        return;
        !           639:                }
        !           640:        }
        !           641: }

unix.superglobalmegacorp.com

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