1: # include "link.h" 2: char *sprintf(),*strcpy(), *ctime(), *strsub(), *tack(), *lalloc(); 3: WORD getword(); 4: #include <signal.h> 5: 6: /******************** variables with global scope ************************/ 7: 8: 9: struct psect *Tex_root = NULL; /* root of ins psects */ 10: struct psect *Tex_end = NULL; /* last structure of ins list */ 11: struct psect *Dat_root = NULL; /* root of data psects */ 12: struct psect *Dat_end = NULL; /* last structure of data list */ 13: struct psect *Bss_root = NULL; /* root of bss psects */ 14: struct psect *Bss_end = NULL; /* last structure of bss list */ 15: struct objfile *File_root = NULL; /* root of object file link-list */ 16: struct symbol *Sym_root = NULL; /* root of symbol table */ 17: struct g_sect *Gsect_root = NULL; /* root of tree of global psects */ 18: WORD Maxabs = 0; /* maximum size of absolute files, 19: ** also low limit of relocatable code */ 20: WORD R_counter; /* relocation counter, used in 21: ** assigning relocation constants, 22: ** also high limit of relocatable 23: ** code after relocation */ 24: long Seekoff; /* offset for seeking in out file */ 25: char Do_410 = 0; /* boolean for out file format */ 26: int Verbose =0; /* Boolean for verbose commentary */ 27: char Do_411 = 0; /* boolean for out file format */ 28: char Do_map = 0; /* boolean for printing load map */ 29: char Do_lpr_map = 0; /* boolean for line printing map */ 30: char Do_bits = 0; /* boolean for including relocation 31: ** bits in .out file */ 32: char C_rel = 0; /* boolean for having out file end 33: ** with '.o' */ 34: char Do_kludge = 0; /* boolean for writing global sybmbols 35: ** in the out file with a preceding 36: ** underscore character */ 37: char Do_silent = 0; /* boolean for not printing zero 38: ** errors message */ 39: char Do_table = 1; /* boolean for including symbol table 40: ** in out file */ 41: char No_locals = 0; /* boolean for not including local 42: ** symbols in the out table */ 43: char Do_odt = 0; /* boolean for including odt module */ 44: WORD Transadd = 1; /* transfer address */ 45: struct objfile *Transfile; /* object file of transfer address */ 46: char Transsect[7]; /* program section of trans address */ 47: WORD Tex_size; /* for out file */ 48: WORD Dat_size; /* for out file */ 49: WORD Bss_size; /* for out file */ 50: char *Outbase; /* out file name without '.out' */ 51: char *Outname = NULL; /* name of out file */ 52: char *Mapname; /* name of map file */ 53: FILE *Mapp = NULL; /* pointer for map file */ 54: char Erstring[80]; /* buffer for error messages */ 55: int Nerrors = 0; /* the number of user errors */ 56: char No_out = 0; /* boolean for no out file */ 57: char Scanerr = 0; /* boolean for error in arguments */ 58: 59: /********************** main ********************************************/ 60: 61: 62: main(argc, argv) 63: int argc; 64: char *argv[]; 65: { 66: scanargs(argc, argv); 67: pass1(); 68: relocate(); 69: relsyms(Sym_root); 70: post_bail(); 71: if (Do_map) 72: printmap(); 73: if (No_out) 74: exit(0); 75: warmup(); 76: pass2(); 77: loose_ends(); 78: } 79: 80: 81: /********************* scanargs ******************************************/ 82: 83: 84: scanargs(argc, argv) 85: int argc; 86: char *argv[]; 87: { 88: register char *s; 89: char *r; 90: register struct objfile *end; /* last file in link-list */ 91: 92: struct objfile *newfile(); /* allocaters */ 93: 94: while (--argc) 95: { 96: s = *++argv; 97: if (*s == '-') 98: { 99: s++; 100: if ((r = strsub(s, "na:")) != NULL) 101: Outname = tack(r, ".out"); 102: else if (!strcmp(s, "ls") || !strcmp(s, "mp")) 103: Do_map = 1; 104: else if (!strcmp(s, "lp")) 105: Do_map = Do_lpr_map = 1; 106: else if (!strcmp(s, "od")) 107: Do_odt = 1; 108: else if (!strcmp(s, "r")) 109: Do_bits = 1; 110: else if (!strcmp(s, "c")) 111: Do_bits = C_rel = 1; 112: else if (!strcmp(s, "K")) 113: Do_bits = C_rel = Do_kludge = 1; 114: else if (!strcmp(s, "")) 115: Do_silent = 1; 116: else if (!strcmp(s, "ns")) 117: Do_table = 0; 118: else if (!strcmp(s, "go")) 119: No_locals = 1; 120: else if (!strcmp(s, "no")) 121: No_out = 1; 122: else if (!strcmp(s, "n")) 123: Do_410 = 1; 124: else if (!strcmp(s, "i")) 125: Do_411 = 1; 126: else if(!strcmp(s, "v")) 127: Verbose = 1; 128: else 129: { 130: fprintf(stderr, "Unrecognizable argument: -%s\n", s); 131: Scanerr = 1; 132: } 133: } 134: else 135: { 136: if (File_root == NULL) 137: end = File_root = newfile(); 138: else 139: { 140: end->nextfile = newfile(); 141: end = end->nextfile; 142: } 143: s = tack(s, ".obj"); 144: end->fname = s; 145: } 146: } 147: 148: if (File_root == NULL) 149: { 150: fprintf(stderr, "Error: no object files given\n"); 151: exit(1); 152: } 153: 154: if (Do_odt) 155: { 156: end->nextfile = newfile(); 157: end->nextfile->fname = "/usr/lib/odt.obj"; 158: } 159: 160: if (Scanerr) 161: exit(1); 162: outnames(); 163: } 164: 165: 166: /*************************** strsub ***************************************/ 167: 168: 169: char *strsub(s, t) /* if t is the initial part of s then return a pointer 170: ** to the rest of s, else return NULL */ 171: register char *s; 172: register char *t; 173: { 174: register char *q; 175: 176: q = t; 177: while (*t) 178: if (*s++ != *t++) 179: return (NULL); 180: 181: if (*s == '\0') /* check for no filename */ 182: { 183: fprintf(stderr, "Argument error: a filename should be contiguous with -%s\n", q); 184: Scanerr = 1; 185: return(NULL); 186: } 187: else 188: return (s); 189: } 190: 191: 192: /**************************** outnames ************************************/ 193: 194: 195: outnames() /* determine names of output files */ 196: 197: { 198: if (Outname == NULL) 199: { 200: Outbase = lalloc(strlen(File_root->fname) + 1); 201: strcpy(Outbase, File_root->fname); 202: strip(Outbase, ".obj"); 203: } 204: else 205: { 206: Outbase = lalloc(strlen(Outname) + 5); 207: strcpy(Outbase, Outname); 208: strip(Outbase, ".out"); 209: strip(Outbase, ".o"); 210: } 211: 212: if (C_rel) 213: Outname = tack(Outbase, ".o"); 214: else 215: Outname = tack(Outbase, ".out"); 216: 217: if (Do_map) 218: Mapname = tack(Outbase, ".map"); 219: } 220: 221: 222: /*************************** newpsect ************************************/ 223: 224: 225: struct psect *newpsect() /* allocate and initialize a new psect 226: ** structure */ 227: { 228: register struct psect *p; 229: 230: p = (struct psect *) lalloc(sizeof (struct psect)); 231: p->next = p->pssame = p->obsame = NULL; 232: p->slist = NULL; 233: return (p); 234: } 235: 236: 237: /*********************** newsymbol ***************************************/ 238: 239: 240: struct symbol *newsymbol() /* allocate and initialize new symbol 241: ** structure */ 242: { 243: register struct symbol *p; 244: 245: p = (struct symbol *) lalloc(sizeof (struct symbol)); 246: p->prsect = (struct psect *)NULL; 247: p->right = p->left = (struct symbol *)NULL; 248: return (p); 249: } 250: 251: 252: /********************** newfile ******************************************/ 253: 254: 255: struct objfile *newfile() /* allocate and initialize new psect 256: ** structure */ 257: { 258: register struct objfile *p; 259: 260: p = (struct objfile *) lalloc(sizeof (struct objfile)); 261: p->nextfile = NULL; 262: p->psect_list = NULL; 263: p->ver_id = NULL; 264: return (p); 265: } 266: 267: 268: /**************************** new_gsect ***********************************/ 269: 270: 271: struct g_sect *new_gsect() 272: 273: { 274: register struct g_sect *p; 275: 276: p = (struct g_sect *) lalloc(sizeof(struct g_sect)); 277: p->leftt = p->rightt = NULL; 278: return (p); 279: } 280: 281: 282: /********************* pass1 *********************************************/ 283: 284: 285: pass1() 286: 287: { 288: char name[7]; /* string from M11 */ 289: char attr; /* attribute from M11 */ 290: register int type; /* type of string */ 291: int value; /* from M11 */ 292: register struct objfile *of; /* current object file */ 293: register struct psect *ps; /* current psect */ 294: struct symbol *sym; /* current and last symbol 295: ** in symbol list */ 296: struct symbol **nextsym; /* pointer to pointer which 297: ** should point to next symbol 298: ** in the psect list */ 299: struct psect *newpsect(); 300: struct symbol *newsymbol(); 301: 302: of = File_root; 303: while (of != NULL) 304: { 305: ch_input(of->fname, HEADER); /* change input */ 306: 307: while(morebytes()) 308: { 309: dc_symbol(name); 310: attr = getbyte(); 311: type = getbyte(); 312: value = getword(); 313: switch(type) 314: { 315: case 0: /* program name */ 316: strcpy(of->pname, name); 317: break; 318: 319: case 1: /* program section */ 320: case 5: /* program section */ 321: if(Verbose) 322: fprintf(stderr,"gsd%d <%6s> %o\n", 323: type, name, attr); 324: 325: /* increment size if odd */ 326: value += (value % 2) ? 1 : 0; 327: /* place psect at end of link-list for objfile */ 328: if (of->psect_list == NULL) 329: ps = of->psect_list = newpsect(); 330: else 331: { 332: ps->obsame = newpsect(); 333: ps = ps->obsame; 334: } 335: strcpy(ps->name, name); 336: if (Do_410) 337: /* if the psect is sharable and data 338: ** make it instruction, this is an old 339: ** L11 kludge. */ 340: { 341: if ((attr & SHR) && !(attr & (INS | BSS))) 342: ps->type = attr | INS; 343: else 344: ps->type = attr; 345: } 346: else 347: ps->type = attr; 348: ps->nbytes = value; 349: nextsym = &(ps->slist); 350: 351: if (!(attr & REL)) /* if psect is absolute */ 352: { 353: ps->rc = 0; 354: Maxabs = (value > Maxabs) ? value : Maxabs; 355: } 356: /* else put psect in proper ins-dat-bss 357: ** link-list */ 358: else 359: place_global(ps); 360: break; 361: 362: case 2: /* this case has not been observed 363: ** and is assumed to be un-used */ 364: lerror("internal symbol table"); 365: break; 366: 367: case 3: /* transfer address */ 368: if (value == 1) /* if 1 bogus */ 369: break; 370: if (Transadd != 1) /* transfer address 371: ** already specified */ 372: fprintf(stderr, "Warning: more than \ 373: one transfer address specified\n"); 374: Transadd = value; 375: Transfile = of; 376: strcpy(Transsect, name); 377: break; 378: 379: case 4: /* global symbol */ 380: /* make structure and place in obj file list */ 381: sym = newsymbol(); 382: sym->prsect = ps; 383: strcpy(sym->name, name); 384: sym->type = attr; 385: sym->value = value; 386: if (attr & DEF) /* if the symbol is defined, 387: ** place in link-list of 388: ** symbols */ 389: { 390: *nextsym = sym; 391: nextsym = &(sym->symlist); 392: } 393: /* place in symbol table */ 394: table(&Sym_root, sym); 395: break; 396: 397: case 6: /* version identification */ 398: of->ver_id = lalloc(7); 399: strcpy(of->ver_id, name); 400: break; 401: 402: default: 403: lerror("header type out of range"); 404: } 405: } 406: of = of->nextfile; 407: } 408: } 409: 410: 411: /***************************** place_global ******************************/ 412: 413: 414: place_global(ps) /* try to place the given program section 415: ** in proper ins - dat - bss link-list by 416: ** finding it in the global psect tree. if 417: ** it is not there then add to tree and place 418: ** it through place_local */ 419: register struct psect *ps; 420: { 421: register struct g_sect **ptr; 422: register int cond; 423: 424: ptr = &Gsect_root; 425: while (*ptr != NULL) 426: { 427: if ( !(cond = strcmp(ps->name, (*ptr)->name))) 428: { 429: if (ps->type != (*ptr)->type) 430: { 431: fprintf(stderr, "psect <%6s> attribute clash: ", ps->name); 432: fprintf(stderr, " already has %3o, now declared %3o. Using %3o\n", 0377 & (*ptr)->type, 0377 & ps->type, 0377 & (*ptr)->type); 433: ps->type = (*ptr)->type; 434: } 435: /* place psect in list of sames */ 436: (*ptr)->last_sect->pssame = ps; 437: (*ptr)->last_sect = ps; 438: return; 439: } 440: else if (cond < 0) 441: ptr = &((*ptr)->leftt); 442: else 443: ptr = &((*ptr)->rightt); 444: } 445: 446: /* global section cannot be found in tree so make a new node for 447: ** it and place it as a local */ 448: 449: *ptr = new_gsect(); 450: strcpy((*ptr)->name, ps->name); 451: (*ptr)->type = ps->type; 452: (*ptr)->last_sect = ps; 453: place_local(ps); 454: } 455: 456: 457: /************************* place_local *******************************/ 458: 459: 460: place_local(ps) /* place psect at end of its UNIX section 461: ** type link-list */ 462: register struct psect *ps; 463: { 464: register type = ps->type; 465: 466: if( !(type & REL)) /* asect */ 467: { 468: fprintf(stderr, "Don't know what to do with .asect yet\n"); 469: } 470: if (type & INS) /* instruction psect */ 471: { 472: if (Tex_root == NULL) 473: Tex_root = Tex_end = ps; 474: else 475: { 476: Tex_end->next = ps; 477: Tex_end = ps; 478: } 479: } 480: else if (type & BSS) /* bss psect */ 481: { 482: if (Bss_root == NULL) 483: Bss_root = Bss_end = ps; 484: else 485: { 486: Bss_end->next = ps; 487: Bss_end = ps; 488: } 489: } 490: else /* data psect */ 491: { 492: if (Dat_root == NULL) 493: Dat_root = Dat_end = ps; 494: else 495: { 496: Dat_end->next = ps; 497: Dat_end = ps; 498: } 499: } 500: } 501: 502: 503: /************************* table **************************************/ 504: 505: 506: table(root, new) /* place new symbol structure in symbol table tree */ 507: 508: register struct symbol *root[]; /* pointer to root pointer of tree */ 509: register struct symbol *new; /* pointer to symbol (structure) to 510: ** be added */ 511: { 512: register int cond; 513: 514: for (;;) /* repeat until break */ 515: { 516: if (*root == NULL) /* empty tree */ 517: { 518: /* set root, return */ 519: *root = new; 520: return; 521: } 522: /* check for same name */ 523: else if ((cond = strcmp(new->name, (*root)->name)) == 0) 524: { 525: /* new symbol and one in table have same name */ 526: if (new->type & DEF) /* ignore new if undefined */ 527: { 528: if (isdef(*root)) /* both defined */ 529: { 530: if(isabs(*root) && (new->value == 531: (*root)->value)) 532: fprintf(stderr, "Warning: abs sym %s defined as %06o twice\n", new->name, new->value); 533: else { 534: 535: sprintf(Erstring, "%s defined twice", new->name); 536: uerror(Erstring); 537: } 538: } 539: /* else /* get rid of old undefined symbol */ 540: { 541: new->left = (*root)->left; 542: new->right = (*root)->right; 543: *root = new; 544: } 545: } 546: return; 547: } 548: /* else branch */ 549: else if (cond < 0) 550: root = &((*root)->left); 551: else 552: root = &((*root)->right); 553: } 554: } 555: 556: 557: /***************************** relocate ********************************/ 558: 559: 560: # define _8K 020000 561: 562: 563: relocate() /* assign relocation constants for all relocatable psects */ 564: 565: { 566: register unsigned temp; 567: struct outword trans_rc; 568: 569: R_counter = Maxabs; /* set relocation counter to follow 570: ** absolute sections */ 571: asgn_rcs(Tex_root); 572: /* text size = absolute and ins psects */ 573: Tex_size = R_counter; 574: 575: if (Do_410) 576: { 577: temp = Tex_size + _8K; 578: temp &= ~(_8K - 1); 579: R_counter = temp; 580: 581: Seekoff = (long) Tex_size - (long )R_counter; 582: } 583: else if (Do_411) 584: { 585: R_counter = temp = 0; 586: Seekoff = Tex_size; 587: } 588: else 589: { 590: temp = Tex_size; 591: Seekoff = 0; 592: } 593: 594: asgn_rcs(Dat_root); 595: Dat_size = (int)R_counter - (int)temp; 596: temp = R_counter; 597: 598: asgn_rcs(Bss_root); 599: Bss_size = (int)R_counter - (int)temp; 600: 601: /* relocate transfer address if defined */ 602: if (Transadd != 1) /* if defined */ 603: { 604: get_rc(&trans_rc, Transfile, Transsect); 605: Transadd += trans_rc.val; 606: } 607: 608: if (Do_bits && Maxabs) 609: { 610: uerror("absolute section present - relocation bits suppressed"); 611: Do_bits = 0; 612: } 613: } 614: 615: 616: /*************************** asgn_rcs ************************************/ 617: 618: 619: 620: asgn_rcs(p) /* assign relocation constants to 621: ** the psects pointed to. this routine uses the 622: ** variable R_counter which contains the address of the 623: ** next available space in memory */ 624: register struct psect *p; /* called with ins-dat-bss root */ 625: 626: { 627: register int size; /* addendum to R_counter */ 628: register struct psect *d; /* temporary pointer used to access 629: ** same psects */ 630: for (;;) /* repeat until break */ 631: { 632: if (p == NULL) /* end of list, break */ 633: break; 634: 635: /* set relocation constant to R_counter */ 636: p->rc = R_counter; 637: 638: /* set size initially to size of psect. size is not added 639: ** to R_counter at this point because of the possibility 640: ** that the psect is part of a group of overlaid psects */ 641: 642: size = p->nbytes; 643: 644: /* check for same psect(s) */ 645: if (p->pssame != NULL) 646: { 647: /* set d to same psect */ 648: d = p; 649: 650: if (p->type & OVR) /* if the sames are overlaid */ 651: /* travel through sames. 652: ** set the rc's of all sames to R_counter. 653: ** size equals maximum psect size */ 654: do 655: { 656: d = d->pssame; 657: d->rc = R_counter; 658: size = (size > d->nbytes) ? size : d->nbytes; 659: } while (d->pssame != NULL); 660: 661: else /* sames are concatenated */ 662: /* travel through sames 663: ** add size to R_counter before assigning 664: ** to rc for each psect. 665: ** redefine size as number of bytes in psect */ 666: do 667: { 668: d = d->pssame; 669: d->rc = (R_counter += size); 670: size = d->nbytes; 671: } while (d->pssame != NULL); 672: 673: R_counter += size; 674: 675: /* insert a psect structure at bottom of sames list to 676: ** to facilitate '^p' M11 directives */ 677: 678: d->pssame = newpsect(); 679: d = d->pssame; 680: d->rc = p->rc; 681: d->type = p->type; 682: d->nbytes = R_counter - p->rc; 683: } 684: else 685: R_counter += size; 686: p = p->next; 687: } 688: } 689: 690: 691: /************************* relsyms **************************************/ 692: 693: 694: relsyms(sym) /* relocate global symbols */ 695: 696: register struct symbol *sym; 697: { 698: if (sym == NULL) 699: return; 700: sym->value += sym->prsect->rc; 701: relsyms(sym->left); 702: relsyms(sym->right); 703: } 704: 705: 706: /************************* printmap ***********************************/ 707: 708: 709: printmap() 710: { 711: register struct objfile *op; 712: register struct psect *pp; 713: int tvec[2]; /* time vector */ 714: static char dashes[] = "----------------"; 715: 716: Mapp = fopen(Mapname, "w"); 717: if (Mapp == NULL) 718: fprintf(stderr, "%s not accessible\n", Mapname); 719: else 720: { 721: long datstart = Tex_size; 722: 723: if(Do_411) datstart = 0L; 724: 725: /* print map header */ 726: time(tvec); 727: fprintf(Mapp, "%s Linker-11 version 22may79 %s\n", Outname, ctime(tvec)); 728: fprintf(Mapp, "Magic number: %o\t", 729: Do_410 ? 0410 : ( Do_411 ? 0411 : 0407) ); 730: if(Do_410) fprintf(Mapp, "(Shared text)\n"); 731: else if (Do_411) fprintf(Mapp,"(Separate I & D)\n"); 732: else fprintf(Mapp, "(Executable)\n"); 733: fprintf(Mapp, "\tStart\tLimit\tLength\n"); 734: brag("Text", 0L, (long)Tex_size, (Do_410 || Do_411)?"Shareable" : ""); 735: brag("Data", datstart, (long)Dat_size, ""); 736: brag("Bss", (long)(datstart+Dat_size), (long)Bss_size, ""); 737: fprintf(Mapp, "transfer address: %06o\n", Transadd); 738: 739: for (op = File_root; op != NULL; op = op->nextfile) 740: { 741: fprintf(Mapp, "%s\n", dashes); 742: fprintf(Mapp, "module: %s", op->pname); 743: if (op->ver_id != NULL) 744: fprintf(Mapp, " %s", op->ver_id); 745: fprintf(Mapp, "\nsection orgin size\n"); 746: pp = op->psect_list; 747: while (pp != NULL) 748: { 749: fprintf(Mapp, "<%s> ", pp->name); 750: fprintf(Mapp, "%06o %06o", pp->rc, pp->nbytes); 751: prattr(pp->type); 752: dump_symlist(pp->slist); 753: pp = pp->obsame; 754: } 755: } 756: fprintf(Mapp, "%s", dashes); 757: if (dump_undefs(Sym_root, 0)) 758: fprintf(Mapp, "\n%s\n", dashes); 759: else 760: fprintf(Mapp, "\n"); 761: } 762: } 763: 764: brag(s, start, size, tag) 765: char *s; 766: long start, size; 767: char *tag; 768: { 769: long stop = 0177777 & (size + start); 770: 771: fprintf(Mapp, "%s:\t%06O\t%06O\t%06O\t%s\n", s, start, stop, size, tag); 772: } 773: 774: prattr(x) 775: { 776: fprintf(Mapp, "\t"); 777: attr(x&SHR, "shr, ", "prv, "); 778: attr(x&INS, "ins, ", ""); 779: attr(x&BSS, "bss, ", ""); 780: attr( !((x&BSS) || (x&INS)), "dat,", ""); 781: attr(x&REL, "rel, ", "abs, "); 782: attr(x&OVR,"ovr, ", "con, "); 783: attr(x&GBL,"gbl", "loc"); 784: } 785: attr(x, s, t) 786: register x; 787: register char *s, *t; 788: { 789: if(x) fprintf(Mapp, s); 790: else fprintf(Mapp, t); 791: } 792: /***************************** dump_symlist *******************************/ 793: 794: 795: dump_symlist(sym) /* write to the map file the symbol list for a psect */ 796: register struct symbol *sym; 797: { 798: register int i; 799: 800: for (i = 0; sym != NULL; sym = sym->symlist) 801: { 802: fprintf(Mapp, "%s ", (i++ % 3) ? "" : "\n "); 803: fprintf(Mapp, "%s %06o", sym->name, sym->value); 804: fprintf(Mapp, " %c", (sym->type & REL) ? 'r' :'a'); 805: } 806: fprintf(Mapp, "\n"); 807: } 808: 809: 810: /***************************** dump_undefs *******************************/ 811: 812: 813: dump_undefs(sym, n) /* dump into map file all undefined global symbols */ 814: struct symbol *sym; 815: int n; 816: { 817: if (sym == NULL) 818: return (n); 819: n = dump_undefs(sym->left, n); 820: if ( !(sym->type & DEF)) 821: { 822: fprintf(Mapp, "%s ", (n++ % 3) ? "" : "\n "); 823: fprintf(Mapp, "%s ****** u", sym->name); 824: } 825: n = dump_undefs(sym->right, n); 826: return (n); 827: } 828: 829: 830: /************************** post_bail *************************************/ 831: 832: 833: post_bail() /* set interrupt routine to bail_out */ 834: 835: { 836: extern bail_out(); 837: 838: sigx(1); 839: sigx(2); 840: sigx(3); 841: sigx(15); 842: } 843: sigx(n) 844: { 845: if(signal(n, SIG_IGN) != SIG_IGN) 846: signal(n, bail_out); 847: }