|
|
1.1 root 1: /* Copyright (c) 1980 Regents of the University of California */
2: static char sccsid[] = "@(#)assyms.c 4.6 11/5/80";
3: #include <stdio.h>
4: #include <ctype.h>
5: #include "as.h"
6: #include "asscan.h"
7: #include "assyms.h"
8:
9: /*
10: * Managers for chunks of symbols allocated from calloc()
11: * We maintain a linked list of such chunks.
12: *
13: */
14: struct allocbox *allochead; /*head of chunk list*/
15: struct allocbox *alloctail; /*tail*/
16: struct allocbox *newbox; /*for creating a new chunk*/
17: struct symtab *nextsym; /*next symbol free*/
18: int symsleft; /*slots left in current chunk*/
19:
20: struct symtab **symptrs;
21: struct symtab **symdelim[NLOC + NLOC +1];
22: struct symtab **symptrub;
23: /*
24: * Managers for the dynamically extendable hash table
25: */
26: struct hashdallop *htab;
27:
28: struct instab *itab[NINST]; /*maps opcodes to instructions*/
29: /*
30: * Counts what went into the symbol table, so that the
31: * size of the symbol table can be computed.
32: */
33: int nsyms; /* total number in the symbol table */
34: int njxxx; /* number of jxxx entrys */
35: int nforgotten; /* number of symbols erroneously entered */
36: int nlabels; /* number of label entries */
37: int hshused; /* number of hash slots used */
38:
39: /*
40: * Managers of the symbol literal storage.
41: * If we have flexible names, then we allocate BUFSIZ long
42: * string, and pack strings into that. Otherwise, we allocate
43: * symbol storage in fixed hunks NCPS long when we allocate space
44: * for other symbol attributes.
45: */
46: #ifdef FLEXNAMES
47: struct strpool *strplhead = 0;
48: #endif FLEXNAMES
49:
50: symtabinit()
51: {
52: allochead = 0;
53: alloctail = 0;
54: nextsym = 0;
55: symsleft = 0;
56: #ifdef FLEXNAMES
57: strpoolalloc(); /* get the first strpool storage area */
58: #endif FLEXNAMES
59: htab = 0;
60: htaballoc(); /* get the first part of the hash table */
61: }
62:
63: /*
64: * Install all known instructions in the symbol table
65: */
66: syminstall()
67: {
68: register struct instab *ip;
69: register struct symtab **hp;
70: register char *p1, *p2;
71:
72: #ifdef FLEXNAMES
73: for (ip = (struct instab *)instab; ip->s_name != 0; ip++) {
74: #else /*not FLEXNAMES*/
75: for (ip = (struct instab *)instab; ip->s_name[0] != '\0'; ip++){
76: #endif /*not FLEXNAMES*/
77: p1 = ip->s_name;
78: p2 = yytext;
79: while (*p2++ = *p1++);
80: hp = lookup(0); /* 0 => don't install this*/
81: if (*hp==NULL) {
82: *hp = (struct symtab *)ip;
83: if ( (ip->s_tag!=INSTn)
84: && (ip->s_tag!=INST0)
85: && (ip->s_tag!=0))
86: continue; /* was pseudo-op */
87: itab[ip->i_opcode & 0xFF] = ip;
88: }
89: }
90: } /*end of syminstall*/
91:
92:
93: /*
94: * Assign final values to symbols,
95: * and overwrite the index field with its relative position in
96: * the symbol table we give to the loader.
97: */
98: extern struct exec hdr;
99:
100: freezesymtab()
101: {
102: register struct symtab *sp;
103: long bs;
104: register int relpos = 0;
105: register struct symtab *ubsp;
106: register struct allocbox *allocwalk;
107:
108: DECLITERATE(allocwalk, sp, ubsp)
109: {
110: if (sp->s_tag >= IGNOREBOUND)
111: continue; /*totally ignore jxxx entries */
112: /*
113: * Ignore stabs, but give them a symbol table index
114: */
115: if (sp->s_type & STABFLAG)
116: goto assignindex;
117: if ((sp->s_type&XTYPE)==XUNDEF)
118: sp->s_type = XXTRN+XUNDEF;
119: else if ((sp->s_type&XTYPE)==XDATA)
120: sp->s_value += usedot[sp->s_index].e_xvalue;
121: else if ((sp->s_type&XTYPE)==XTEXT)
122: sp->s_value += usedot[sp->s_index].e_xvalue;
123: else if ((sp->s_type&XTYPE)==XBSS) {
124: bs = sp->s_value;
125: sp->s_value = hdr.a_bss + datbase;
126: hdr.a_bss += bs;
127: }
128: assignindex:
129: if ( (sp->s_name[0] != 'L')
130: || (sp->s_tag != LABELID)
131: || savelabels
132: ) /*then, we will write it later on*/
133: sp->s_index = relpos++;
134: }
135: }
136:
137:
138:
139: /*
140: * For all of the stabs that had their final value undefined during pass 1
141: * and during pass 2 assign a final value.
142: * We have already given stab entrys a initial approximation
143: * when we constsructed the sorted symbol table.
144: * Iteration order doesn't matter.
145: */
146: stabfix() {
147: register struct symtab *sp, **cosp;
148: register struct symtab *p;
149:
150: SYMITERATE(cosp, sp){
151: if(sp->s_ptype && (sp->s_type & STABFLAG)) {
152: p = sp->s_dest;
153: sp->s_value = p->s_value;
154: sp->s_index = p->s_index;
155: sp->s_type = p->s_type;
156: }
157: }
158: }
159:
160: char *Calloc(number, size)
161: int number, size;
162: {
163: register char *newstuff;
164: newstuff = (char *)sbrk(number*size);
165: if ((int)newstuff == -1){
166: yyerror("Ran out of Memory");
167: delexit();
168: }
169: return(newstuff);
170: }
171:
172: char *ClearCalloc(number, size)
173: int number, size;
174: {
175: register char *newstuff; /* r11 */
176: register int length = number * size; /* r10 */
177: newstuff = Calloc(number, size);
178: asm("movc5 $0, (r0), $0, r10, (r11)");
179: return(newstuff);
180: }
181:
182: struct symtab *symalloc()
183: {
184: if (symsleft == 0){
185: newbox = (struct allocbox *)ClearCalloc(1,ALLOCQTY);
186: symsleft = SYMDALLOP;
187: nextsym = &newbox->symslots[0];
188: if (alloctail == 0){
189: allochead = alloctail = newbox;
190: } else {
191: alloctail->nextalloc = newbox;
192: alloctail = newbox;
193: }
194: }
195: --symsleft;
196: ++nsyms;
197: return(nextsym++);
198: }
199:
200: #ifdef FLEXNAMES
201: strpoolalloc()
202: {
203: register struct strpool *new;
204:
205: new = (struct strpool *)Calloc(1, sizeof (struct strpool));
206: new->str_nalloc = 0;
207: new->str_next = strplhead;
208: strplhead = new;
209: }
210: #endif FLEXNAMES
211:
212: symcmp(Pptr, Qptr)
213: struct symtab **Pptr, **Qptr;
214: {
215: register struct symtab *p = *Pptr;
216: register struct symtab *q = *Qptr;
217: if (p->s_index < q->s_index)
218: return(-1);
219: if (p->s_index > q->s_index)
220: return(1);
221: if (p->s_value < q->s_value)
222: return(-1);
223: if (p->s_value > q->s_value)
224: return(1);
225: /*
226: * Force jxxx entries to virtually preceed labels defined
227: * to follow the jxxxx instruction, so that bumping the
228: * jxxx instruction correctly fixes up the following labels
229: */
230: if (p->s_tag >= IGNOREBOUND) /*p points to a jxxx*/
231: return(-1);
232: if (q->s_tag >= IGNOREBOUND)
233: return(1);
234: /*
235: * both are now just plain labels; the relative order doesn't
236: * matter. Both can't be jxxxes, as they would have different
237: * values.
238: */
239: return(0);
240: } /*end of symcmp*/
241:
242: /*
243: * We construct the auxiliary table of pointers, symptrs and
244: * symdelim
245: * We also assign preliminary values to stab entries that did not yet
246: * have an absolute value (because they initially referred to
247: * forward references). We don't worry about .stabds, as they
248: * already have an estimated final value
249: */
250:
251: sortsymtab()
252: {
253: register struct symtab *sp;
254: register struct symtab **cowalk;
255: register struct allocbox *allocwalk;
256: struct symtab *ubsp;
257: int segno;
258: int slotno;
259: int symsin; /*number put into symptrs*/
260:
261: symptrs = (struct symtab **)Calloc(nsyms + 2, sizeof *symptrs);
262: /*
263: * Allocate one word at the beginning of the symptr array
264: * so that backwards scans through the symptr array will
265: * work correctly while scanning through the zeroth segment
266: */
267: *symptrs++ = 0;
268: cowalk = symptrs;
269: symsin = 0;
270: DECLITERATE(allocwalk, sp, ubsp) {
271: if (sp->s_ptype && (sp->s_type &STABFLAG)){
272: sp->s_value = sp->s_dest->s_value;
273: sp->s_index = sp->s_dest->s_index;
274: }
275: if (symsin >= nsyms)
276: yyerror("INTERNAL ERROR: overfilled symbol table indirection table");
277: *cowalk++ = sp;
278: symsin++;
279: }
280: if (symsin != nsyms)
281: yyerror("INTERNAL ERROR: installed %d syms, should have installed %d",
282: symsin, nsyms);
283: symptrub = &symptrs[nsyms ];
284: qsort(symptrs, nsyms, sizeof *symptrs, symcmp);
285: symdelim[0] = symptrs;
286: for (cowalk = symptrs, sp = *cowalk, segno = 0, slotno = 1;
287: segno < NLOC + NLOC;
288: segno++, slotno++){
289: for (; sp && sp->s_index == segno; sp = *++cowalk);
290: symdelim[slotno] = cowalk; /*forms the ub delimeter*/
291: }
292: } /*end of sortsymtab*/
293:
294: #ifdef DEBUG
295: dumpsymtab()
296: {
297: register int segno;
298: register struct symtab *sp, **cosp, *ub;
299: char *tagstring();
300:
301: printf("Symbol Table dump:\n");
302: for (segno = 0; segno < NLOC + NLOC; segno++){
303: printf("Segment number: %d\n", segno);
304: SEGITERATE(segno, 0, 0, cosp, sp, ub, ++){
305: #ifdef FLEXNAMES
306: printf("\tSeg: %d \"%s\" value: %d index: %d tag %s\n",
307: segno, sp->s_name,
308: sp->s_value, sp->s_index,
309: tagstring(sp->s_tag));
310: #else /*not FLEXNAMES*/
311: printf("\tSeg: %d \"%*.*s\" value: %d index: %d tag %s\n",
312: segno, NCPS, NCPS, sp->s_name,
313: sp->s_value, sp->s_index,
314: tagstring(sp->s_tag));
315: #endif /*not FLEXNAMES*/
316: printf("\t\ttype: %d jxbump %d jxfear: %d\n",
317: sp->s_type, sp->s_jxbump, sp->s_jxfear);
318: }
319: printf("\n\n");
320: }
321: }
322:
323: static char tagbuff[4];
324:
325: char *tagstring(tag)
326: unsigned char tag;
327: {
328: switch(tag){
329: case JXACTIVE: return("active");
330: case JXNOTYET: return("notyet");
331: case JXALIGN: return("align");
332: case JXQUESTIONABLE: return("jxquestionable");
333: case JXINACTIVE: return("inactive");
334: case JXTUNNEL: return("tunnel");
335: case OBSOLETE: return("obsolete");
336: case IGNOREBOUND: return("ignorebound");
337: case STABFLOATING: return("stabfloating");
338: case STABFIXED: return("stabfixed");
339: case LABELID: return("labelid");
340: case OKTOBUMP: return("oktobump");
341: case ISET: return("iset");
342: case ILSYM: return("ilsym");
343: default: sprintf(tagbuff,"%d", tag);
344: return(tagbuff);
345: }
346: }
347: #endif DEBUG
348:
349: htaballoc()
350: {
351: register struct hashdallop *new;
352: new = (struct hashdallop *)ClearCalloc(1, sizeof (struct hashdallop));
353: if (htab == 0)
354: htab = new;
355: else { /* add AFTER the 1st slot */
356: new->h_next = htab->h_next;
357: htab->h_next = new;
358: }
359: }
360:
361: #define HASHCLOGGED (NHASH / 2)
362:
363: /*
364: * Lookup a symbol stored in extern yytext.
365: * All strings passed in via extern yytext had better have
366: * a trailing null. Strings are placed in yytext for hashing by
367: * syminstall() and by yylex();
368: *
369: * We take pains to avoid function calls; this functdion
370: * is called quite frequently, and the calls overhead
371: * in the vax contributes significantly to the overall
372: * execution speed of as.
373: */
374: struct symtab **lookup(instflg)
375: int instflg; /* 0: don't install */
376: {
377: static int initialprobe;
378: register struct symtab **hp;
379: register char *from;
380: register char *to;
381: register int len;
382: register int nprobes;
383: static struct hashdallop *hdallop;
384: static struct symtab **emptyslot;
385: static struct hashdallop *emptyhd;
386: static struct symtab **hp_ub;
387:
388: emptyslot = 0;
389: for (nprobes = 0, from = yytext;
390: *from;
391: nprobes <<= 2, nprobes += *from++)
392: continue;
393: nprobes += from[-1] << 5;
394: nprobes %= NHASH;
395: if (nprobes < 0)
396: nprobes += NHASH;
397:
398: initialprobe = nprobes;
399: for (hdallop = htab; hdallop != 0; hdallop = hdallop->h_next){
400: for (hp = &(hdallop->h_htab[initialprobe]),
401: nprobes = 1,
402: hp_ub = &(hdallop->h_htab[NHASH]);
403: (*hp) && (nprobes < NHASH);
404: hp += nprobes,
405: hp -= (hp >= hp_ub) ? NHASH:0,
406: nprobes += 2)
407: {
408: from = yytext;
409: to = (*hp)->s_name;
410: #ifndef FLEXNAMES
411: for (len = 0; (len<NCPS) && *from; len++)
412: if (*from++ != *to++)
413: goto nextprobe;
414: if (len >= NCPS) /*both are maximal length*/
415: return(hp);
416: if (*to == 0) /*assert *from == 0*/
417: return(hp);
418: #else /*FLEXNAMES*/
419: while (*from && *to)
420: if (*from++ != *to++)
421: goto nextprobe;
422: if (*to == *from) /*assert both are == 0*/
423: return(hp);
424: #endif /*FLEXNAMES*/
425:
426: nextprobe: ;
427: }
428: if (*hp == 0 && emptyslot == 0 &&
429: hdallop->h_nused < HASHCLOGGED) {
430: emptyslot = hp;
431: emptyhd = hdallop;
432: }
433: }
434: if (emptyslot == 0) {
435: htaballoc();
436: hdallop = htab->h_next; /* aren't we smart! */
437: hp = &hdallop->h_htab[initialprobe];
438: } else {
439: hdallop = emptyhd;
440: hp = emptyslot;
441: }
442: if (instflg) {
443: *hp = symalloc();
444: hdallop->h_nused++;
445: #ifndef FLEXNAMES
446: for(len = 0, from = yytext, to = (*hp)->s_name; (len<NCPS); len++)
447: if ((*to++ = *from++) == '\0')
448: break;
449: #else /*FLEXNAMES*/
450: for (from = yytext, len = 1; *from++; len++)
451: continue;
452: if (len >= (STRPOOLDALLOP - strplhead->str_nalloc))
453: strpoolalloc();
454: for ( (*hp)->s_name = to = strplhead->str_names + strplhead->str_nalloc, from = yytext;
455: ( (*to++ = *from++) != '\0'); )
456: continue;
457: strplhead->str_nalloc += len;
458: #endif /*FLEXNAMES*/
459: }
460: return(hp);
461: } /*end of lookup*/
462:
463: #ifdef FLEXNAMES
464: char *savestr(str)
465: char *str;
466: {
467: register int len;
468: register char *from, *to;
469: char *res;
470:
471: for (from = str, len = 1; *from++; len++)
472: continue;
473: if (len >= (STRPOOLDALLOP - strplhead->str_nalloc))
474: strpoolalloc();
475: for ( res = to = strplhead->str_names + strplhead->str_nalloc, from = str;
476: ( (*to++ = *from++) != '\0'); )
477: continue;
478: strplhead->str_nalloc += len;
479: return (res);
480: }
481: #endif FLEXNAMES
482:
483: /*
484: * The relocation information is saved internally in an array of
485: * lists of relocation buffers. The relocation buffers are
486: * exactly the same size as a token buffer; if we use VM for the
487: * temporary file we reclaim this storage, otherwise we create
488: * them by mallocing.
489: */
490: #define RELBUFLG TOKBUFLG
491: #define NRELOC ((TOKBUFLG - \
492: (sizeof (int) + sizeof (struct relbufdesc *)) \
493: ) / (sizeof (struct relocation_info)))
494:
495: struct relbufdesc{
496: int rel_count;
497: struct relbufdesc *rel_next;
498: struct relocation_info rel_reloc[NRELOC];
499: };
500: extern struct relbufdesc *tok_free;
501: #define rel_free tok_free
502: static struct relbufdesc *rel_temp;
503: struct relocation_info r_can_1PC;
504: struct relocation_info r_can_0PC;
505:
506: initoutrel()
507: {
508: r_can_0PC.r_address = 0;
509: r_can_0PC.r_symbolnum = 0;
510: r_can_0PC.r_pcrel = 0;
511: r_can_0PC.r_length = 0;
512: r_can_0PC.r_extern = 0;
513:
514: r_can_1PC = r_can_0PC;
515: r_can_1PC.r_pcrel = 1;
516: }
517:
518: outrel(xp, reloc_how, isaddr)
519: register struct exp *xp;
520: int reloc_how; /* TYPB..TYPD + (possibly)RELOC_PCREL */
521: {
522: struct relocation_info reloc;
523: register int x_type_mask;
524: int pcrel;
525:
526: x_type_mask = xp->e_xtype & ~XFORW;
527: pcrel = reloc_how & RELOC_PCREL;
528: reloc_how &= ~RELOC_PCREL;
529:
530: if (bitoff&07)
531: yyerror("Padding error");
532: if (x_type_mask == XUNDEF)
533: yyerror("Undefined reference");
534: if ( (x_type_mask != XABS) || pcrel ) {
535: if (ty_NORELOC[reloc_how])
536: yyerror("Illegal Relocation of float, double or quad.");
537: reloc = pcrel ? r_can_1PC : r_can_0PC;
538: reloc.r_address = dotp->e_xvalue -
539: ( (dotp < &usedot[NLOC] || readonlydata) ? 0 : datbase );
540: reloc.r_length = ty_nlg[reloc_how];
541: switch(x_type_mask){
542: case XXTRN | XUNDEF:
543: reloc.r_symbolnum = xp->e_xname->s_index;
544: reloc.r_extern = 1;
545: break;
546: default:
547: if (readonlydata && (x_type_mask&~XXTRN) == XDATA)
548: x_type_mask = XTEXT | (x_type_mask&XXTRN);
549: reloc.r_symbolnum = x_type_mask;
550: break;
551: }
552: if ( (relfil == 0) || (relfil->rel_count >= NRELOC) ){
553: if (rel_free){
554: rel_temp = rel_free;
555: rel_free = rel_temp->rel_next;
556: } else {
557: rel_temp = (struct relbufdesc *)
558: Calloc(1,sizeof (struct relbufdesc));
559: }
560: rel_temp->rel_count = 0;
561: rel_temp->rel_next = relfil;
562: relfil = rusefile[dotp - &usedot[0]] = rel_temp;
563: }
564: /* reloc.r_addsyl = isaddr!=0;(addsyl is unreferenced)*/
565: relfil->rel_reloc[relfil->rel_count++] = reloc;
566: }
567: /*
568: * write the unrelocated value to the text file
569: */
570: dotp->e_xvalue += ty_nbyte[reloc_how];
571: if (pcrel)
572: xp->e_xvalue -= dotp->e_xvalue;
573: bwrite((char *)&(xp->e_xvalue), ty_nbyte[reloc_how], txtfil);
574: }
575: /*
576: * Flush out all of the relocation information.
577: * Note that the individual lists of buffers are in
578: * reverse order, so we must reverse them
579: */
580: off_t closeoutrel(relocfile)
581: BFILE *relocfile;
582: {
583: int locindex;
584: u_long Closeoutrel();
585:
586: trsize = 0;
587: for (locindex = 0; locindex < NLOC; locindex++){
588: trsize += Closeoutrel(rusefile[locindex], relocfile);
589: }
590: drsize = 0;
591: for (locindex = 0; locindex < NLOC; locindex++){
592: drsize += Closeoutrel(rusefile[NLOC + locindex], relocfile);
593: }
594: return(trsize + drsize);
595: }
596:
597: u_long Closeoutrel(relfil, relocfile)
598: struct relbufdesc *relfil;
599: BFILE *relocfile;
600: {
601: u_long tail;
602: if (relfil == 0)
603: return(0L);
604: tail = Closeoutrel(relfil->rel_next, relocfile);
605: bwrite((char *)&relfil->rel_reloc[0],
606: relfil->rel_count * sizeof (struct relocation_info),
607: relocfile);
608: return(tail + relfil->rel_count * sizeof (struct relocation_info));
609: }
610:
611: #define NOUTSYMS (nsyms - njxxx - nforgotten - (savelabels ? 0 : nlabels))
612: int sizesymtab()
613: {
614: return (sizeof (struct nlist) * NOUTSYMS);
615: }
616:
617: #ifdef FLEXNAMES
618: /*
619: * We write out the flexible length character strings for names
620: * in two stages.
621: * 1) We have always! maintain a fixed sized name list entry;
622: * the string is indexed by a four byte quantity from the beginning
623: * of the string pool area. Index 0 is reserved, and indicates
624: * that there is no associated string. The first valid index is 4.
625: * 2) We concatenate together and write all of the strings
626: * in the string pool at the end of the name list. The first
627: * four bytes in the string pool are indexed only by 0 (see above);
628: * they contain the total number of bytes in the string pool.
629: */
630: #endif FLEXNAMES
631:
632: /*
633: * Write out n symbols to file f, beginning at p
634: * ignoring symbols that are obsolete, jxxx instructions, and
635: * possibly, labels
636: */
637:
638: int symwrite(symfile)
639: BFILE *symfile;
640: {
641: int symsout; /*those actually written*/
642: int symsdesired = NOUTSYMS;
643: register struct symtab *sp, *ub;
644: #ifdef FLEXNAMES
645: char *name; /* temp to save the name */
646: long stroff = sizeof (stroff);
647: /*
648: * We use sp->s_index to hold the length of the
649: * name; it isn't used for anything else
650: */
651: #endif FLEXNAMES
652:
653: register struct allocbox *allocwalk;
654:
655: symsout = 0;
656: DECLITERATE(allocwalk, sp, ub)
657: {
658: if (sp->s_tag >= IGNOREBOUND)
659: continue;
660: if ((sp->s_name[0] == 'L') && (sp->s_tag == LABELID) && !savelabels)
661: continue;
662: symsout++;
663:
664: #ifdef FLEXNAMES
665: name = sp->s_name; /* save pointer */
666: if ( (sp->s_index = strlen(sp->s_name)) != 0){
667: sp->s_nmx = stroff; /* clobber pointer */
668: stroff += sp->s_index + 1;
669: } else {
670: sp->s_nmx = 0; /* clobber pointer */
671: }
672: #endif
673: sp->s_type = (sp->s_ptype != 0) ? sp->s_ptype : (sp->s_type & (~XFORW));
674: if (readonlydata && (sp->s_type&~N_EXT) == N_DATA)
675: sp->s_type = N_TEXT | (sp->s_type & N_EXT);
676: bwrite(&sp->s_nm, sizeof (struct nlist), symfile);
677: #ifdef FLEXNAMES
678: sp->s_name = name; /* restore pointer */
679: #endif FLEXNAMES
680: }
681: if (symsout != symsdesired)
682: yyerror("INTERNAL ERROR: Wrote %d symbols, wanted to write %d symbols\n",
683: symsout, symsdesired);
684: #ifdef FLEXNAMES
685: /*
686: * Pass 2 through the string pool
687: */
688: symsout = 0;
689: bwrite(&stroff, sizeof (stroff), symfile);
690: stroff = sizeof (stroff);
691: symsout = 0;
692: DECLITERATE(allocwalk, sp, ub)
693: {
694: if (sp->s_tag >= IGNOREBOUND)
695: continue;
696: if ((sp->s_name[0] == 'L') && (sp->s_tag == LABELID) && !savelabels)
697: continue;
698: sp->s_index = strlen(sp->s_name);
699: if (sp->s_index)
700: bwrite(sp->s_name, sp->s_index + 1, symfile);
701: }
702: #endif FLEXNAMES
703: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.