|
|
1.1 ! root 1: /* Execution of byte code produced by bytecomp.el. ! 2: Copyright (C) 1985, 1986, 1987 Free Software Foundation, Inc. ! 3: ! 4: This file is part of GNU Emacs. ! 5: ! 6: GNU Emacs is distributed in the hope that it will be useful, ! 7: but WITHOUT ANY WARRANTY. No author or distributor ! 8: accepts responsibility to anyone for the consequences of using it ! 9: or for whether it serves any particular purpose or works at all, ! 10: unless he says so in writing. Refer to the GNU Emacs General Public ! 11: License for full details. ! 12: ! 13: Everyone is granted permission to copy, modify and redistribute ! 14: GNU Emacs, but only under the conditions described in the ! 15: GNU Emacs General Public License. A copy of this license is ! 16: supposed to have been given to you along with GNU Emacs so you ! 17: can know your rights and responsibilities. It should be in a ! 18: file named COPYING. Among other things, the copyright notice ! 19: and this notice must be preserved on all copies. */ ! 20: ! 21: ! 22: #include "config.h" ! 23: #include "lisp.h" ! 24: #include "buffer.h" ! 25: ! 26: Lisp_Object Qbytecode; ! 27: ! 28: /* Byte codes: */ ! 29: ! 30: #define Bvarref 010 ! 31: #define Bvarset 020 ! 32: #define Bvarbind 030 ! 33: #define Bcall 040 ! 34: #define Bunbind 050 ! 35: ! 36: #define Bnth 070 ! 37: #define Bsymbolp 071 ! 38: #define Bconsp 072 ! 39: #define Bstringp 073 ! 40: #define Blistp 074 ! 41: #define Beq 075 ! 42: #define Bmemq 076 ! 43: #define Bnot 077 ! 44: #define Bcar 0100 ! 45: #define Bcdr 0101 ! 46: #define Bcons 0102 ! 47: #define Blist1 0103 ! 48: #define Blist2 0104 ! 49: #define Blist3 0105 ! 50: #define Blist4 0106 ! 51: #define Blength 0107 ! 52: #define Baref 0110 ! 53: #define Baset 0111 ! 54: #define Bsymbol_value 0112 ! 55: #define Bsymbol_function 0113 ! 56: #define Bset 0114 ! 57: #define Bfset 0115 ! 58: #define Bget 0116 ! 59: #define Bsubstring 0117 ! 60: #define Bconcat2 0120 ! 61: #define Bconcat3 0121 ! 62: #define Bconcat4 0122 ! 63: #define Bsub1 0123 ! 64: #define Badd1 0124 ! 65: #define Beqlsign 0125 ! 66: #define Bgtr 0126 ! 67: #define Blss 0127 ! 68: #define Bleq 0130 ! 69: #define Bgeq 0131 ! 70: #define Bdiff 0132 ! 71: #define Bnegate 0133 ! 72: #define Bplus 0134 ! 73: #define Bmax 0135 ! 74: #define Bmin 0136 ! 75: ! 76: #define Bpoint 0140 ! 77: #define Bmark 0141 /* no longer generated as of v18 */ ! 78: #define Bgoto_char 0142 ! 79: #define Binsert 0143 ! 80: #define Bpoint_max 0144 ! 81: #define Bpoint_min 0145 ! 82: #define Bchar_after 0146 ! 83: #define Bfollowing_char 0147 ! 84: #define Bpreceding_char 0150 ! 85: #define Bcurrent_column 0151 ! 86: #define Bindent_to 0152 ! 87: #define Bscan_buffer 0153 /* No longer generated as of v18 */ ! 88: #define Beolp 0154 ! 89: #define Beobp 0155 ! 90: #define Bbolp 0156 ! 91: #define Bbobp 0157 ! 92: #define Bcurrent_buffer 0160 ! 93: #define Bset_buffer 0161 ! 94: #define Bread_char 0162 ! 95: #define Bset_mark 0163 /* this loser is no longer generated as of v18 */ ! 96: #define Binteractive_p 0164 /* Needed since interactive-p takes unevalled args */ ! 97: ! 98: #define Bconstant2 0201 ! 99: #define Bgoto 0202 ! 100: #define Bgotoifnil 0203 ! 101: #define Bgotoifnonnil 0204 ! 102: #define Bgotoifnilelsepop 0205 ! 103: #define Bgotoifnonnilelsepop 0206 ! 104: #define Breturn 0207 ! 105: #define Bdiscard 0210 ! 106: #define Bdup 0211 ! 107: ! 108: #define Bsave_excursion 0212 ! 109: #define Bsave_window_excursion 0213 ! 110: #define Bsave_restriction 0214 ! 111: #define Bcatch 0215 ! 112: ! 113: #define Bunwind_protect 0216 ! 114: #define Bcondition_case 0217 ! 115: #define Btemp_output_buffer_setup 0220 ! 116: #define Btemp_output_buffer_show 0221 ! 117: ! 118: #define Bconstant 0300 ! 119: #define CONSTANTLIM 0100 ! 120: ! 121: /* Fetch the next byte from the bytecode stream */ ! 122: ! 123: #define FETCH ((unsigned char *)XSTRING (bytestr)->data)[pc++] ! 124: ! 125: /* Fetch two bytes from the bytecode stream ! 126: and make a 16-bit number out of them */ ! 127: ! 128: #define FETCH2 (op = FETCH, op + (FETCH << 8)) ! 129: ! 130: /* Push x onto the execution stack. */ ! 131: ! 132: #define PUSH(x) (*++stackp = (x)) ! 133: ! 134: /* Pop a value off the execution stack. */ ! 135: ! 136: #define POP (*stackp--) ! 137: ! 138: /* Discard n values from the execution stack. */ ! 139: ! 140: #define DISCARD(n) (stackp -= (n)) ! 141: ! 142: /* Get the value which is at the top of the execution stack, but don't pop it. */ ! 143: ! 144: #define TOP (*stackp) ! 145: ! 146: ! 147: DEFUN ("byte-code", Fbyte_code, Sbyte_code, 3, 3, 0, ! 148: "") ! 149: (bytestr, vector, maxdepth) ! 150: Lisp_Object bytestr, vector, maxdepth; ! 151: { ! 152: struct gcpro gcpro1, gcpro2, gcpro3; ! 153: int count = specpdl_ptr - specpdl; ! 154: register int pc = 0; ! 155: register int op; ! 156: Lisp_Object *stack; ! 157: register Lisp_Object *stackp; ! 158: Lisp_Object *stacke; ! 159: register Lisp_Object v1, v2; ! 160: register Lisp_Object *vectorp = XVECTOR (vector)->contents; ! 161: ! 162: CHECK_STRING (bytestr, 0); ! 163: if (XTYPE (vector) != Lisp_Vector) ! 164: vector = wrong_type_argument (Qvectorp, vector); ! 165: CHECK_NUMBER (maxdepth, 2); ! 166: ! 167: stackp = (Lisp_Object *) alloca (XFASTINT (maxdepth) * sizeof (Lisp_Object)); ! 168: bzero (stackp, XFASTINT (maxdepth) * sizeof (Lisp_Object)); ! 169: GCPRO3 (bytestr, vector, *stackp); ! 170: gcpro3.nvars = XFASTINT (maxdepth); ! 171: ! 172: --stackp; ! 173: stack = stackp; ! 174: stacke = stackp + XFASTINT (maxdepth); ! 175: ! 176: while (1) ! 177: { ! 178: if (stackp > stacke) ! 179: error ("Stack overflow in byte code (byte compiler bug), pc = %d", pc); ! 180: if (stackp < stack) ! 181: error ("Stack underflow in byte code (byte compiler bug), pc = %d", pc); ! 182: switch (op = FETCH) ! 183: { ! 184: case Bvarref+6: ! 185: op = FETCH; ! 186: goto varref; ! 187: ! 188: case Bvarref+7: ! 189: op = FETCH2; ! 190: goto varref; ! 191: ! 192: case Bvarref: case Bvarref+1: case Bvarref+2: case Bvarref+3: ! 193: case Bvarref+4: case Bvarref+5: ! 194: op = op - Bvarref; ! 195: varref: ! 196: v1 = vectorp[op]; ! 197: if (XTYPE (v1) != Lisp_Symbol) ! 198: v2 = Fsymbol_value (v1); ! 199: else ! 200: { ! 201: v2 = XSYMBOL (v1)->value; ! 202: #ifdef SWITCH_ENUM_BUG ! 203: switch ((int) XTYPE (v2)) ! 204: #else ! 205: switch (XTYPE (v2)) ! 206: #endif ! 207: { ! 208: case Lisp_Symbol: ! 209: if (!EQ (v2, Qunbound)) ! 210: break; ! 211: case Lisp_Intfwd: ! 212: case Lisp_Boolfwd: ! 213: case Lisp_Objfwd: ! 214: case Lisp_Buffer_Local_Value: ! 215: case Lisp_Some_Buffer_Local_Value: ! 216: case Lisp_Buffer_Objfwd: ! 217: case Lisp_Void: ! 218: v2 = Fsymbol_value (v1); ! 219: } ! 220: } ! 221: PUSH (v2); ! 222: break; ! 223: ! 224: case Bvarset+6: ! 225: op = FETCH; ! 226: goto varset; ! 227: ! 228: case Bvarset+7: ! 229: op = FETCH2; ! 230: goto varset; ! 231: ! 232: case Bvarset: case Bvarset+1: case Bvarset+2: case Bvarset+3: ! 233: case Bvarset+4: case Bvarset+5: ! 234: op -= Bvarset; ! 235: varset: ! 236: Fset (vectorp[op], POP); ! 237: break; ! 238: ! 239: case Bvarbind+6: ! 240: op = FETCH; ! 241: goto varbind; ! 242: ! 243: case Bvarbind+7: ! 244: op = FETCH2; ! 245: goto varbind; ! 246: ! 247: case Bvarbind: case Bvarbind+1: case Bvarbind+2: case Bvarbind+3: ! 248: case Bvarbind+4: case Bvarbind+5: ! 249: op -= Bvarbind; ! 250: varbind: ! 251: specbind (vectorp[op], POP); ! 252: break; ! 253: ! 254: case Bcall+6: ! 255: op = FETCH; ! 256: goto docall; ! 257: ! 258: case Bcall+7: ! 259: op = FETCH2; ! 260: goto docall; ! 261: ! 262: case Bcall: case Bcall+1: case Bcall+2: case Bcall+3: ! 263: case Bcall+4: case Bcall+5: ! 264: op -= Bcall; ! 265: docall: ! 266: DISCARD(op); ! 267: /* Remove protection from the args we are giving to Ffuncall. ! 268: FFuncall will protect them, and double protection would ! 269: cause disasters. */ ! 270: gcpro3.nvars = &TOP - stack - 1; ! 271: TOP = Ffuncall (op + 1, &TOP); ! 272: gcpro3.nvars = XFASTINT (maxdepth); ! 273: break; ! 274: ! 275: case Bunbind+6: ! 276: op = FETCH; ! 277: goto dounbind; ! 278: ! 279: case Bunbind+7: ! 280: op = FETCH2; ! 281: goto dounbind; ! 282: ! 283: case Bunbind: case Bunbind+1: case Bunbind+2: case Bunbind+3: ! 284: case Bunbind+4: case Bunbind+5: ! 285: op -= Bunbind; ! 286: dounbind: ! 287: unbind_to (specpdl_ptr - specpdl - op); ! 288: break; ! 289: ! 290: case Bgoto: ! 291: QUIT; ! 292: op = FETCH2; /* pc = FETCH2 loses since FETCH2 contains pc++ */ ! 293: pc = op; ! 294: break; ! 295: ! 296: case Bgotoifnil: ! 297: QUIT; ! 298: op = FETCH2; ! 299: if (NULL (POP)) ! 300: pc = op; ! 301: break; ! 302: ! 303: case Bgotoifnonnil: ! 304: QUIT; ! 305: op = FETCH2; ! 306: if (!NULL (POP)) ! 307: pc = op; ! 308: break; ! 309: ! 310: case Bgotoifnilelsepop: ! 311: QUIT; ! 312: op = FETCH2; ! 313: if (NULL (TOP)) ! 314: pc = op; ! 315: else DISCARD(1); ! 316: break; ! 317: ! 318: case Bgotoifnonnilelsepop: ! 319: QUIT; ! 320: op = FETCH2; ! 321: if (!NULL (TOP)) ! 322: pc = op; ! 323: else DISCARD(1); ! 324: break; ! 325: ! 326: case Breturn: ! 327: v1 = POP; ! 328: goto exit; ! 329: ! 330: case Bdiscard: ! 331: DISCARD(1); ! 332: break; ! 333: ! 334: case Bdup: ! 335: v1 = TOP; ! 336: PUSH (v1); ! 337: break; ! 338: ! 339: case Bconstant2: ! 340: PUSH (vectorp[FETCH2]); ! 341: break; ! 342: ! 343: case Bsave_excursion: ! 344: record_unwind_protect (save_excursion_restore, save_excursion_save ()); ! 345: break; ! 346: ! 347: case Bsave_window_excursion: ! 348: TOP = Fsave_window_excursion (TOP); ! 349: break; ! 350: ! 351: case Bsave_restriction: ! 352: record_unwind_protect (save_restriction_restore, save_restriction_save ()); ! 353: break; ! 354: ! 355: case Bcatch: ! 356: v1 = POP; ! 357: TOP = internal_catch (TOP, Feval, v1); ! 358: break; ! 359: ! 360: case Bunwind_protect: ! 361: record_unwind_protect (0, POP); ! 362: (specpdl_ptr - 1)->symbol = Qnil; ! 363: break; ! 364: ! 365: case Bcondition_case: ! 366: v1 = POP; ! 367: v1 = Fcons (POP, v1); ! 368: TOP = Fcondition_case (Fcons (TOP, v1)); ! 369: break; ! 370: ! 371: case Btemp_output_buffer_setup: ! 372: temp_output_buffer_setup (XSTRING (TOP)->data); ! 373: TOP = Vstandard_output; ! 374: break; ! 375: ! 376: case Btemp_output_buffer_show: ! 377: v1 = POP; ! 378: temp_output_buffer_show (TOP); ! 379: TOP = v1; ! 380: /* pop binding of standard-output */ ! 381: unbind_to (specpdl_ptr - specpdl - 1); ! 382: break; ! 383: ! 384: case Bnth: ! 385: v1 = POP; ! 386: v2 = TOP; ! 387: CHECK_NUMBER (v2, 0); ! 388: op = XINT (v2); ! 389: immediate_quit = 1; ! 390: while (--op >= 0) ! 391: { ! 392: if (CONSP (v1)) ! 393: v1 = XCONS (v1)->cdr; ! 394: else if (!NULL (v1)) ! 395: { ! 396: immediate_quit = 0; ! 397: v1 = wrong_type_argument (Qlistp, v1); ! 398: immediate_quit = 1; ! 399: op++; ! 400: } ! 401: } ! 402: immediate_quit = 0; ! 403: goto docar; ! 404: ! 405: case Bsymbolp: ! 406: TOP = XTYPE (TOP) == Lisp_Symbol ? Qt : Qnil; ! 407: break; ! 408: ! 409: case Bconsp: ! 410: TOP = CONSP (TOP) ? Qt : Qnil; ! 411: break; ! 412: ! 413: case Bstringp: ! 414: TOP = XTYPE (TOP) == Lisp_String ? Qt : Qnil; ! 415: break; ! 416: ! 417: case Blistp: ! 418: TOP = CONSP (TOP) || NULL (TOP) ? Qt : Qnil; ! 419: break; ! 420: ! 421: case Beq: ! 422: v1 = POP; ! 423: TOP = EQ (v1, TOP) ? Qt : Qnil; ! 424: break; ! 425: ! 426: case Bmemq: ! 427: v1 = POP; ! 428: TOP = Fmemq (TOP, v1); ! 429: break; ! 430: ! 431: case Bnot: ! 432: TOP = NULL (TOP) ? Qt : Qnil; ! 433: break; ! 434: ! 435: case Bcar: ! 436: v1 = TOP; ! 437: docar: ! 438: if (CONSP (v1)) TOP = XCONS (v1)->car; ! 439: else if (NULL (v1)) TOP = Qnil; ! 440: else Fcar (wrong_type_argument (Qlistp, v1)); ! 441: break; ! 442: ! 443: case Bcdr: ! 444: v1 = TOP; ! 445: if (CONSP (v1)) TOP = XCONS (v1)->cdr; ! 446: else if (NULL (v1)) TOP = Qnil; ! 447: else Fcdr (wrong_type_argument (Qlistp, v1)); ! 448: break; ! 449: ! 450: case Bcons: ! 451: v1 = POP; ! 452: TOP = Fcons (TOP, v1); ! 453: break; ! 454: ! 455: case Blist1: ! 456: TOP = Fcons (TOP, Qnil); ! 457: break; ! 458: ! 459: case Blist2: ! 460: v1 = POP; ! 461: TOP = Fcons (TOP, Fcons (v1, Qnil)); ! 462: break; ! 463: ! 464: case Blist3: ! 465: DISCARD(2); ! 466: TOP = Flist (3, &TOP); ! 467: break; ! 468: ! 469: case Blist4: ! 470: DISCARD(3); ! 471: TOP = Flist (4, &TOP); ! 472: break; ! 473: ! 474: case Blength: ! 475: TOP = Flength (TOP); ! 476: break; ! 477: ! 478: case Baref: ! 479: v1 = POP; ! 480: TOP = Faref (TOP, v1); ! 481: break; ! 482: ! 483: case Baset: ! 484: v2 = POP; v1 = POP; ! 485: TOP = Faset (TOP, v1, v2); ! 486: break; ! 487: ! 488: case Bsymbol_value: ! 489: TOP = Fsymbol_value (TOP); ! 490: break; ! 491: ! 492: case Bsymbol_function: ! 493: TOP = Fsymbol_function (TOP); ! 494: break; ! 495: ! 496: case Bset: ! 497: v1 = POP; ! 498: TOP = Fset (TOP, v1); ! 499: break; ! 500: ! 501: case Bfset: ! 502: v1 = POP; ! 503: TOP = Ffset (TOP, v1); ! 504: break; ! 505: ! 506: case Bget: ! 507: v1 = POP; ! 508: TOP = Fget (TOP, v1); ! 509: break; ! 510: ! 511: case Bsubstring: ! 512: v2 = POP; v1 = POP; ! 513: TOP = Fsubstring (TOP, v1, v2); ! 514: break; ! 515: ! 516: case Bconcat2: ! 517: DISCARD(1); ! 518: TOP = Fconcat (2, &TOP); ! 519: break; ! 520: ! 521: case Bconcat3: ! 522: DISCARD(2); ! 523: TOP = Fconcat (3, &TOP); ! 524: break; ! 525: ! 526: case Bconcat4: ! 527: DISCARD(3); ! 528: TOP = Fconcat (4, &TOP); ! 529: break; ! 530: ! 531: case Bsub1: ! 532: v1 = TOP; ! 533: if (XTYPE (v1) == Lisp_Int) ! 534: { ! 535: XSETINT (v1, XINT (v1) - 1); ! 536: TOP = v1; ! 537: } ! 538: else ! 539: TOP = Fsub1 (v1); ! 540: break; ! 541: ! 542: case Badd1: ! 543: v1 = TOP; ! 544: if (XTYPE (v1) == Lisp_Int) ! 545: { ! 546: XSETINT (v1, XINT (v1) + 1); ! 547: TOP = v1; ! 548: } ! 549: else ! 550: TOP = Fadd1 (v1); ! 551: break; ! 552: ! 553: case Beqlsign: ! 554: v2 = POP; v1 = TOP; ! 555: CHECK_NUMBER_COERCE_MARKER (v1, 0); ! 556: CHECK_NUMBER_COERCE_MARKER (v2, 0); ! 557: TOP = XINT (v1) == XINT (v2) ? Qt : Qnil; ! 558: break; ! 559: ! 560: case Bgtr: ! 561: v1 = POP; ! 562: TOP = Fgtr (TOP, v1); ! 563: break; ! 564: ! 565: case Blss: ! 566: v1 = POP; ! 567: TOP = Flss (TOP, v1); ! 568: break; ! 569: ! 570: case Bleq: ! 571: v1 = POP; ! 572: TOP = Fleq (TOP, v1); ! 573: break; ! 574: ! 575: case Bgeq: ! 576: v1 = POP; ! 577: TOP = Fgeq (TOP, v1); ! 578: break; ! 579: ! 580: case Bdiff: ! 581: DISCARD(1); ! 582: TOP = Fminus (2, &TOP); ! 583: break; ! 584: ! 585: case Bnegate: ! 586: v1 = TOP; ! 587: if (XTYPE (v1) == Lisp_Int) ! 588: { ! 589: XSETINT (v1, - XINT (v1)); ! 590: TOP = v1; ! 591: } ! 592: else ! 593: TOP = Fminus (1, &TOP); ! 594: break; ! 595: ! 596: case Bplus: ! 597: DISCARD(1); ! 598: TOP = Fplus (2, &TOP); ! 599: break; ! 600: ! 601: case Bmax: ! 602: DISCARD(1); ! 603: TOP = Fmax (2, &TOP); ! 604: break; ! 605: ! 606: case Bmin: ! 607: DISCARD(1); ! 608: TOP = Fmin (2, &TOP); ! 609: break; ! 610: ! 611: case Bpoint: ! 612: XFASTINT (v1) = point; ! 613: PUSH (v1); ! 614: break; ! 615: ! 616: case Bmark: /* this loser is no longer generated as of v18 */ ! 617: PUSH (Fmarker_position (bf_cur->mark)); ! 618: break; ! 619: ! 620: case Bgoto_char: ! 621: TOP = Fgoto_char (TOP); ! 622: break; ! 623: ! 624: case Binsert: ! 625: TOP = Finsert (1, &TOP); ! 626: break; ! 627: ! 628: case Bpoint_max: ! 629: XFASTINT (v1) = NumCharacters+1; ! 630: PUSH (v1); ! 631: break; ! 632: ! 633: case Bpoint_min: ! 634: XFASTINT (v1) = FirstCharacter; ! 635: PUSH (v1); ! 636: break; ! 637: ! 638: case Bchar_after: ! 639: TOP = Fchar_after (TOP); ! 640: break; ! 641: ! 642: case Bfollowing_char: ! 643: XFASTINT (v1) = point>NumCharacters ? 0 : CharAt(point); ! 644: PUSH (v1); ! 645: break; ! 646: ! 647: case Bpreceding_char: ! 648: XFASTINT (v1) = point<=FirstCharacter ? 0 : CharAt(point-1); ! 649: PUSH (v1); ! 650: break; ! 651: ! 652: case Bcurrent_column: ! 653: XFASTINT (v1) = current_column (); ! 654: PUSH (v1); ! 655: break; ! 656: ! 657: case Bindent_to: ! 658: TOP = Findent_to (TOP, Qnil); ! 659: break; ! 660: ! 661: case Bscan_buffer: ! 662: /* Get an appropriate error. */ ! 663: Fsymbol_function (intern ("scan-buffer")); ! 664: break; ! 665: ! 666: case Beolp: ! 667: PUSH (Feolp ()); ! 668: break; ! 669: ! 670: case Beobp: ! 671: PUSH (Feobp ()); ! 672: break; ! 673: ! 674: case Bbolp: ! 675: PUSH (Fbolp ()); ! 676: break; ! 677: ! 678: case Bbobp: ! 679: PUSH (Fbobp ()); ! 680: break; ! 681: ! 682: case Bcurrent_buffer: ! 683: PUSH (Fcurrent_buffer ()); ! 684: break; ! 685: ! 686: case Bset_buffer: ! 687: TOP = Fset_buffer (TOP); ! 688: break; ! 689: ! 690: case Bread_char: ! 691: PUSH (Fread_char ()); ! 692: QUIT; ! 693: break; ! 694: ! 695: case Bset_mark: /* this loser is no longer generated as of v18 */ ! 696: /* TOP = Fset_mark (TOP); */ ! 697: TOP = Fset_marker (bf_cur->mark, TOP, Fcurrent_buffer ()); ! 698: break; ! 699: ! 700: case Binteractive_p: ! 701: PUSH (Finteractive_p ()); ! 702: break; ! 703: ! 704: default: ! 705: if ((op -= Bconstant) < (unsigned)CONSTANTLIM) ! 706: PUSH (vectorp[op]); ! 707: } ! 708: } ! 709: ! 710: exit: ! 711: UNGCPRO; ! 712: /* Binds and unbinds are supposed to be compiled balanced. */ ! 713: if (specpdl_ptr - specpdl != count) ! 714: abort (); ! 715: return v1; ! 716: } ! 717: ! 718: syms_of_bytecode () ! 719: { ! 720: Qbytecode = intern ("byte-code"); ! 721: staticpro (&Qbytecode); ! 722: ! 723: defsubr (&Sbyte_code); ! 724: } ! 725:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.