home *** CD-ROM | disk | FTP | other *** search
/ ftp.barnyard.co.uk / 2015.02.ftp.barnyard.co.uk.tar / ftp.barnyard.co.uk / cpm / walnut-creek-CDROM / MBUG / MBUG103.ARC / LNK.C < prev    next >
Text File  |  1979-12-31  |  22KB  |  668 lines

  1. /* 
  2. ** LNK.C -- Small-Mac Linkage Editor
  3. **
  4. **                    Copyright 1985 J. E. Hendrix
  5. **
  6. ** Usage: LNK [-B] [-G#] [-L] [-M] program [module/library...]
  7. **
  8. ** -B                 A BIG program is being linked, so use all
  9. **                    of free memory for the symbol table and load the
  10. **                    program to disk entirely.  This is slower but it
  11. **                    gets the job done.
  12. **
  13. ** -G#                Make program absolute at address # (hex) and
  14. **                    output as "program.LGO" instead of "program.COM".
  15. **
  16. ** -L                 List entry point symbols.
  17. **
  18. ** -M                 Monitor linking activity.
  19. **
  20. ** program            A file specifier for the program being linked.
  21. **                    The default, and only allowed, extension is REL.
  22. **
  23. ** module/library...  A list of zero or more module (.REL) and/or
  24. **                    library (.LIB) files.  Each module is linked to
  25. **                    the program and the libraries are searched for
  26. **                    just those modules which satisfy one or more
  27. **                    unresolved external references.
  28. **
  29. ** NOTE: Merely declaring a symbol to be external will cause
  30. ** it's module to be loaded.  It need not actually be referenced.
  31. **
  32. ** NOTE: The symbol TMNAME is defined to be the name of the
  33. ** terminal module; i.e., the module which must be loaded last
  34. ** of all.  That module contains special code which identifies
  35. ** the physical end of the program and the beginning of free
  36. ** memory.  The linker is sensitive to its name and waits until
  37. ** all other modules are loaded before loading the terminal module.
  38. **
  39. ** The absence of an extension, or a .REL extension, identifies a module;
  40. ** whereas, a .LIB extension identifies a library.  If necessary, a
  41. ** library is rescanned to resolve backward external references between
  42. ** modules within the library. Module files and libraries are processed
  43. ** in the order in which they occur in the command line.
  44. **
  45. ** Drive Designators (e.g. B:):
  46. **    - allowed with module and library names
  47. **    - program drive designator locates the input .REL file
  48. **    - output goes to the default drive
  49. **
  50. ** Filename Extensions:
  51. **    - must specify .LIB with library name
  52. **    - standard extensions are:
  53. **
  54. **     .REL = relocatable object module
  55. **     .LIB = library of object modules
  56. **     .NDX = index to library (not user specified)
  57. **     .COM = CP/M command file (default output)
  58. **     .LGO = load-and-go file (-G# output)
  59. **     .O$  = temporary overflow file
  60. **     .R$  = temporary reference file
  61. **
  62. ** Enter control-S to pause and control-C to abort.
  63. **
  64. ** NOTE: Compile only with Small-C 2.1 (edit level 63) or later.
  65. ** Edit 63 fixes CSYSLIB so that when it overflows a buffer while
  66. ** writing into a file it will no longer assume that it is at the
  67. ** end of the file.  This prevents it from padding a sector with
  68. ** 1A (hex) in the middle of a file when random access is being used.
  69. */
  70. #include <stdio.h>
  71. #include "notice.h"
  72. #include "rel.h"
  73.  
  74. #define NODEBUG            /* don't compile debug displays */
  75. #define NOCCARGC        /* don't pass arg counts to functions */
  76.  
  77. #define NAMESIZE   15
  78. #define MAXFIL     10
  79. #define STACK     512        /* allow for stack space */
  80. #define AUXBUF   2048        /* aux buffer for reference file */
  81. #define MAXOPEN     4        /* maximum files opened */
  82. #define OHDOPEN   164        /* memery overhead per open file */
  83. #define COMBASE   259        /* 0100H + 3 */
  84. #define RET       201        /* RET instruction (0C9H) */
  85. #define JMP       195        /* JMP instruction (0C3H) */
  86. #define RES        -1        /* value of resolved ext ref */
  87. #define XRPLUS     -2        /* ext-ref-plus-offset flag */
  88. #define TMNAME   "END"        /* terminal module name */
  89. #define MODEXT  ".REL"
  90. #define LIBEXT  ".LIB"
  91. #define NDXEXT  ".NDX"
  92. #define COMEXT  ".COM"
  93. #define LGOEXT  ".LGO"
  94. #define OFLEXT   ".O$"
  95. #define REFEXT   ".R$"
  96.  
  97. /*
  98. ** symbol table definitions
  99. */
  100. #define NXT    0        /* next-entry pointer */
  101. #define VAL    2        /* offset value */
  102. #define SYM    4        /* symbol */
  103. #define SSZ (SYM+MAXSYM+1)    /* size of table entry */
  104. #define HIGH 127        /* high-value byte */
  105. #define CUSHION  (200*SSZ)    /* reserved for table at overflow point */
  106. char high[] = {HIGH,0};        /* high-value symbol */
  107.  
  108. /*
  109. ** global variables
  110. */
  111. char
  112.  *xr,                /* external reference */
  113.  *nxt,                /* next in ext ref chain */
  114.  *ep,                /* entry point */
  115.  *buffer,            /* beginning of code buffer */
  116.  *bnext,            /* next byte in code buffer */
  117.  *sfree,            /* head of freed entry list */
  118.  *snext,            /* next symbol table entry */
  119.  *cloc,                /* location counter */
  120.  *cmod,                /* module location */
  121.  *cbase,            /* base address */
  122.  *csize,            /* program size (fake unsigned) */
  123.  *goloc,            /* go location */
  124.  *cdisk,            /* disk overflow location */
  125.  *epfirst,            /* first entry point */
  126.  *epprev,            /* previous entry point */
  127.  *epnext,            /* next entry point */
  128.  *xrfirst,            /* first external reference */
  129.  *xrprev,            /* previous external reference */
  130.  *xrnext,            /* next external reference */
  131.   modname[MAXSYM+1],        /* name of current module */
  132.   infn   [NAMESIZE],        /* input filename */
  133.   ndxfn  [NAMESIZE],        /* index filename */
  134.   tmfn   [NAMESIZE],        /* terminal-module library name */
  135.   csfn   [NAMESIZE],        /* code seg filename */
  136.   crfn   [NAMESIZE],        /* code rel filename */
  137.   outfn  [NAMESIZE];        /* output filename */
  138.  
  139. int
  140.   lgo,        /* load-and-go format? */
  141.   list,        /* list symbols? */
  142.   monitor,    /* monitor activity? */
  143.   instr,    /* instruction to plant at 0000 */
  144.   addr,        /* start address */
  145.   ref,        /* reference to program relative item */
  146.   big,        /* linking a big program? */
  147.   xrplus,    /* value of offset for next ext ref */
  148.   xrpflag=XRPLUS,    /* value of xrplus flag */
  149.   ndxfd,    /* index fd */
  150.   inblock,    /* block of next library member */
  151.   inbyte,    /* byte in block of next library member */
  152.   tmblock,    /* block of terminal module in tmfn */
  153.   tmbyte,    /* byte of terminal module in tmblock */
  154.   csfd,        /* code segment fd */
  155.   crfd,        /* code relative index fd */
  156.   outfd;    /* output fd */
  157.  
  158. extern int Uchrpos[];        /* lives in CSYSLIB */
  159.  
  160. main(argc,argv) int argc, argv[]; {
  161.   fputs("Small-Mac Linkage Editor, ", stderr); fputs(VERSION, stderr);
  162.   fputs(CRIGHT1, stderr);
  163.   getsw(argc, argv);        /* fetch and remember switches */
  164.   getmem();            /* acquire maximum memory buffer */
  165.   phase1(argc, argv);        /* load and link */
  166.   if(!okay()) abort(7);        /* quit early */
  167.   phase2();            /* generate final output */
  168.   }
  169.  
  170. /*
  171. ** get as much memory as possible for symbol table
  172. */
  173. getmem() {
  174.   char sz[8];
  175.   int max;
  176.   max = avail(YES);            /* how much available? */
  177.   max -= STACK + AUXBUF + (MAXOPEN * OHDOPEN);
  178.   buffer = bnext = malloc(max);        /* allocate space */
  179.   snext  = buffer + (max - SSZ);    /* first entry */
  180.   sfree  = 0;                /* no reusable entries yet */
  181. #ifdef DEBUG
  182.   if(monitor) {itou(max, sz, 8); puts2(sz, " Byte Buffer");}
  183. #endif
  184.   newtbl(&epfirst);            /* set low and high ent pts */
  185.   newtbl(&xrfirst);            /* set low and high ext refs */
  186.   }
  187.  
  188. /*
  189. ** get next module name
  190. */
  191. getname() {
  192.   if(getrel() == PNAME) {
  193.     strcpy(modname, symbol);
  194.     return (YES);
  195.     }
  196.   if(item == EFILE) return (NO);
  197.   error2(infn, " - Corrupted");
  198.   }
  199.  
  200. /*
  201. ** read next entry from library index file
  202. */
  203. getndx() {
  204.   if(read(ndxfd, &inblock, 2) != 2 ||    /* next block */
  205.      read(ndxfd, &inbyte, 2) != 2) {    /* next byte in block */
  206.     error2("- Error Reading ", infn);
  207.     } 
  208.   }
  209.  
  210. /*
  211. ** get switches from command line
  212. */
  213. getsw(argc, argv) int argc, *argv; {
  214.   char arg[NAMESIZE];
  215.   int argnbr, b, len;
  216.   argnbr = 0;
  217.   while(getarg(++argnbr, arg, NAMESIZE, argc, argv) != EOF) {
  218.     if(arg[0] != '-') continue;            /* skip file names */
  219.     if(toupper(arg[1]) == 'G') {
  220.       lgo = YES;
  221.       len = xtoi(arg + 2, &b);
  222.       if(len >= 0 && !arg[len + 2]) cbase = b; else usage();
  223.       }
  224.     else if(toupper(arg[1]) == 'B') big = YES;
  225.     else if(toupper(arg[1]) == 'L') list = YES;
  226.     else if(toupper(arg[1]) == 'M') monitor = YES;
  227.     else usage();
  228.     }
  229.   }
  230.  
  231. /*
  232. ** is symbol an unresolved ext ref?
  233. ** on return of true, xrnext -> matching xr entry
  234. */
  235. isunres() {
  236.   int i;
  237.   xrnext = getint(xrfirst);
  238.   while(xrnext) {
  239.     if((i = strcmp(symbol, xrnext + SYM)) < 0) return (NO);
  240.     if(i == 0)  return (YES);
  241.     xrnext = getint(xrnext);
  242.     }
  243.   return (NO);
  244.   }
  245.  
  246. /*
  247. ** link external references to entry points
  248. */
  249. link() {
  250.   int cspg, csch;
  251.   cspg = ctell(csfd);            /* remember temp file position */
  252.   csch = ctellc(csfd);
  253.   xrnext = getint(xrprev = xrfirst);    /* first external reference */
  254.   epnext = getint(epfirst);        /* first entry point */
  255.   while(YES) {
  256.     if(strcmp(xrnext + SYM, epnext + SYM) > 0) {    /* xr > ep */
  257.       epnext = getint(epnext);
  258.       continue;
  259.       }
  260.     if(strcmp(xrnext + SYM, epnext + SYM) < 0) {    /* xr < ep */
  261.       xrnext = getint(xrprev = xrnext);
  262.       continue;
  263.       }
  264.     if(*(xrnext + SYM) != HIGH) {            /* xr = ep */
  265.       resolve();            /* resolve this ext ref */
  266.       putint(xrprev, getint(xrnext));    /* delink from xr chain */
  267.       putint(xrnext, sfree);        /* link to prev freed entry */
  268.       sfree = xrnext;            /* make first freed entry */
  269.       xrnext = getint(xrprev);        /* advance to next ext ref */
  270.       continue;                /* same ext ref in diff modules? */
  271.       }
  272.     break;
  273.     }
  274.   cseek(csfd, cspg, 0);            /* restore temp file position */
  275.   Uchrpos[csfd] = csch;
  276.   }
  277.  
  278. /*
  279. ** load a module
  280. */
  281. load() {
  282.   char str[8], *offloc;
  283.   offloc = -1;
  284.   epprev = epfirst;            /* start at the very beginning */
  285.   xrprev = xrfirst;
  286.   do {
  287.     poll(YES);
  288.     switch(getrel()) {
  289.       case    ABS: if(csfd) write(csfd, &field, 1);    /* put on disk */
  290.                    else *bnext++ = field;        /* put in memory */
  291.                    if(offloc == cloc) {        /* end of xr chain */
  292.                      write(crfd, &xrpflag, 2);    /* flag for -cbase adj */
  293.                      write(crfd, &cloc,  2);    /* reference for pass 2 */
  294.                      }
  295.                    ++cloc;
  296.                    break;
  297.       case   PREL: field = field + cmod;
  298.                    if(csfd) write(csfd, &field, 2);    /* put on disk */
  299.                    else {                /* put in memory */
  300.                      putint(bnext, field);
  301.                      bnext += 2;
  302.                      }
  303.                    write(crfd, &cloc,  2);    /* reference for pass 2 */
  304.                    cloc += 2;
  305.                    break;
  306.       case  DSIZE: if(!field) break;
  307.           default: error("- Unsupported Link Item");
  308.       case    ERR: error("- Corrupt Module");
  309.       case  EPROG: if(type == PREL) {
  310.                      puts2("Start In ", modname);
  311.                      goloc = field + cmod;
  312.                      }
  313.       case  ENAME: break;            /* bypass enames */
  314.       case XCHAIN: newsym(&xrprev, xrfirst, "xr");
  315.                    break;
  316.       case EPOINT: newsym(&epprev, epfirst, "ep");
  317.                    break;
  318.       case  PSIZE: cmod = cloc;
  319.                    if(monitor) {
  320.                      itox(field, str, 8);
  321.                      fputs(str, stdout); fputs(" Bytes at", stdout);
  322.                      itox(cloc,  str, 6);
  323.                      fputs(str, stdout); fputs("'", stdout);
  324.                      itox(cloc+cbase,  str, 6);
  325.                      fputs(str, stdout); puts2(" ", modname);
  326.                      }
  327.                    if(!csfd &&
  328.                      (big || (bnext + field) > (snext - CUSHION))) {
  329.                      cdisk = cloc;        /* disk overflow point */
  330.                      csfd = open(csfn, "w+");    /* open overflow file */
  331. #ifdef DEBUG
  332.                      if(monitor) {
  333.                        itox(cdisk, str, 8); puts2(str, " Overflow Point");
  334.                        }
  335. #endif
  336.                      }
  337.                    break;
  338.       case  SETLC: field = field + cmod;
  339.                    while(cloc < field) {        /* adj loc ctr */
  340.                      if(csfd) write(csfd, "\0", 1);
  341.                      else *bnext++ = 0;
  342.                      ++cloc;
  343.                      }
  344.                      break;
  345.       case  XPOFF: write(crfd, &xrpflag, 2);        /* flag xr plus */
  346.                    write(crfd, &field, 2);        /* xr offset */
  347.                    offloc = cloc;    /* remember in case ABS follows */ 
  348.                    break;
  349.       }
  350.     } while(item != EPROG);
  351.   }
  352.  
  353. /*
  354. ** create new file specifier from an old one
  355. */
  356. newfn(dest, sour, ext) char *dest, *sour, *ext; {
  357.   if(sour[1] == ':' && strcmp(ext, NDXEXT)) sour += 2;
  358.   while(*sour && *sour != '.') *dest++ = *sour++;
  359.   strcpy(dest, ext);
  360.   }
  361.  
  362. /*
  363. ** store new symbol table entry
  364. ** they arrive in alphanumeric order
  365. */
  366. newsym(prev, first, ts) int *prev, first; char *ts; {
  367.   char at[8], *cp, *new;
  368.   if(new = sfree) sfree = getint(sfree);    /* use old entry */
  369.   else {
  370.     new = snext;
  371.     if((snext -= SSZ) < bnext) error("- Must Specify -B Switch");
  372.     }
  373.   if(strcmp(symbol, *prev + SYM) < 0)
  374.     *prev = first;                /* tolerate M80 blunder */
  375.   cp = *prev;
  376.   while(strcmp(symbol, cp + SYM) >= 0) {    /* find position */
  377.     *prev = cp;
  378.     cp = getint(cp);
  379.     }
  380.   putint(new, cp);            /* point new entry ahead */
  381.   putint(*prev, new);            /* point prev entry here */
  382.   *prev = new;                /* this becomes prev entry */
  383.   if(type == PREL) field = field + cmod;/* adjust for module location */
  384.   putint(new + VAL, field);        /* load value */
  385.   strcpy(new + SYM, symbol);        /* load symbol */
  386. #ifdef DEBUG
  387.   if(monitor) show(new, ts);
  388. #endif
  389.   }
  390.  
  391. /*
  392. ** initial table entries
  393. */
  394. newtbl(low) int *low; {
  395.   *low = snext;                /* always points to low entry */
  396.   strcpy(snext + SYM, "");        /* store low symbol */
  397.   putint(snext, snext - SSZ);        /* link to next (high) symbol */
  398.   snext -= SSZ;                /* now point to next entry */
  399.   strcpy(snext + SYM, high);        /* store high symbol */
  400.   putint(snext, 0);            /* end of chain */
  401.   snext -= SSZ;                /* bump to next entry */
  402.   }
  403.  
  404. /*
  405. ** get next module name
  406. */
  407. nxtmod() {
  408.   getndx();                /* get location and */
  409.   seek();                /* go straight to next member */
  410.   return (getname());
  411.   }
  412.  
  413. /*
  414. ** report the outcome and decide whether to quit
  415. */
  416. okay() {
  417.   int err; char *eplast;
  418.   err = eplast = 0;
  419.   xrnext = getint(xrfirst);        /* first external reference */
  420.   epnext = getint(epfirst);        /* first entry point */
  421.   if(list) puts("\nEntry Points:");
  422.   while(YES) {
  423.     poll(YES);
  424.     if(strcmp(xrnext + SYM, epnext + SYM) > 0) {    /* ext > ent */
  425.       if(epnext == eplast) {
  426.         puts2("-  Redundant: ", xrnext + SYM);
  427.         err = YES;
  428.         }
  429.       if(list) show(epnext, "");
  430.       eplast = epnext;
  431.       epnext = getint(epnext);
  432.       continue;
  433.       }
  434.     if(strcmp(xrnext + SYM, epnext + SYM) < 0) {    /* ext < ent */
  435.       puts2("- Unresolved: ", xrnext + SYM);
  436.       err = YES;
  437.       xrnext = getint(xrnext);
  438.       continue;
  439.       }
  440.     if(*(xrnext + SYM) != HIGH) {            /* ext = ent */
  441.       xrnext = getint(xrnext);
  442.       continue;            /* same ext ref in diff modules? */
  443.       }
  444.     break;
  445.     }
  446.   if(err) return (NO);
  447.   return (YES);
  448.   }
  449.  
  450. /*
  451. ** load input files and library members
  452. */
  453. phase1(argc, argv) int argc, *argv; {
  454.   char sz[8];
  455.   int i, lib, eof;
  456.   eof = EOF;
  457.   cdisk = -1;                /* high value for pointer */
  458.   if(lgo) instr = RET;            /* load and go format */
  459.   else {instr = JMP; cbase = COMBASE;}    /* COM file format */
  460.   i = 0;
  461.   while(getarg(++i, infn, NAMESIZE, argc, argv) != EOF) {
  462.     if(infn[0] == '-') continue;    /* skip switches */
  463.     if(extend(infn, MODEXT, LIBEXT))
  464.          lib = YES;
  465.     else lib = NO;
  466.     if(!*outfn) {            /* first file name */
  467.       if(lgo) newfn(outfn, infn, LGOEXT);
  468.       else    newfn(outfn, infn, COMEXT);
  469.       newfn(csfn, infn, OFLEXT);
  470.       newfn(crfn, infn, REFEXT);
  471.       crfd = open(crfn, "w+");        /* open reference file */
  472.       auxbuf(crfd, AUXBUF);    /* extra buffering lowers head movement */
  473.       }
  474.     if(lib) search();    /* search library if unresolved ext refs */
  475.     else {
  476.       inrel = open(infn, "r");        /* must open */
  477.       getname();            /* program name */
  478.       load();                /* load module */
  479.       link();                /* link previous modules */
  480.       close(inrel);            /* must close */
  481.       }
  482.     }
  483.   if(!*outfn) usage();
  484.   if(*tmfn) {                /* must get terminal module */
  485.     inrel = open(tmfn, "r");
  486.     inblock = tmblock; inbyte = tmbyte;
  487.     seek(); getname(); load(); link();
  488.     close(inrel);
  489.     }
  490.   csize = cloc;
  491.   if(ferror(crfd)) error2("- Error Writing ", crfn);
  492.   write(crfd, &eof, 2);
  493.   rewind(crfd);
  494.   if(ferror(csfd)) error2("- Error Writing ", csfn);
  495.   rewind(csfd);
  496.   itox(csize, sz, 8); puts2(sz, " Bytes (hex)");
  497.   itou(csize, sz, 8); puts2(sz, " Bytes (dec)");
  498.   }
  499.  
  500. /*
  501. ** generate absolute output in COM or LGO format
  502. **
  503. ** COM format: JMP <start> <program>
  504. **
  505. ** LGO format: RET <start> <prog-base> <prog-size> <program>
  506. */
  507. phase2() {
  508.   char at[5];
  509.   outfd = open(outfn, "w");
  510.   write(outfd, &instr, 1);    /* plant first instruction */
  511.   addr = cbase + goloc;
  512.   write(outfd, &addr, 2);    /* with its address */
  513.   if(lgo) {
  514.     write(outfd, &cbase, 2);    /* where to load for execution */
  515.     write(outfd, &csize, 2);    /* how many bytes to load */
  516.     }
  517.   cloc = -1;            /* allow efficient pre-increment */
  518.   readref();            /* get first reference */
  519.   while(++cloc < csize) {    /* while more code */
  520.     if(cloc != ref) {        /* not relative reference */
  521.       if(cloc < cdisk)
  522.         field = *(cloc + buffer);
  523.       else read(csfd, &field, 1);
  524.       write(outfd, &field, 1);    /* copy one byte as is */
  525.       continue;
  526.       }
  527.     if(cloc < cdisk)        /* get next 2-byte relative item */
  528.       field = getint(cloc + buffer);
  529.     else read(csfd, &field, 2);
  530.     field = field + cbase + xrplus;    /* make absolute & apply offset */
  531.     xrplus = 0;            /* reset offset value */
  532.     write(outfd, &field, 2);    /* copy 2 bytes adjusted */
  533.     readref();            /* get next reference */
  534.     ++cloc;            /* need additional increment */
  535.     }
  536.   if(ferror(outfd))  error2("- Error Writing ", outfn);
  537.   close(outfd);
  538.   if(csfd) {
  539.     if(ferror(csfd)) error2("- Error Reading ", csfn);
  540.     close(csfd);
  541.     delete(csfn);
  542.     }
  543.   if(ferror(crfd))   error2("- Error Reading ", crfn);
  544.   close(crfd);
  545.   delete(crfn);
  546.   }
  547.  
  548. /*
  549. ** read next reference
  550. */
  551. readref() {
  552.   read(crfd, &ref, 2);            /* get next reference */
  553.   if(ref == XRPLUS) {            /* ext ref offset flag? */
  554.     read(crfd, &xrplus, 2);        /* yes, get offset value */
  555.     read(crfd, &ref, 2);        /* then get reference */
  556.     if(ref == XRPLUS) {            /* offseting tail of chain? */
  557.       xrplus -= cbase;            /* yes, undo resolve()'s fix */
  558.       read(crfd, &ref, 2);        /* then get reference */
  559.       }
  560.     }
  561.   }
  562.  
  563. /*
  564. ** resolve external references to a given symbol
  565. */
  566. resolve() {
  567.   char at[5];
  568.   if(!(xr = getint(xrnext + VAL))) return;    /* head of ext ref chain */
  569.   ep = getint(epnext + VAL);            /* entry point address */
  570.   do {
  571. #ifdef DEBUG
  572.     if(monitor) {
  573.       poll(YES);
  574.       fputs("Resolving ", stdout);
  575.       itox(xr, at, 5); fputs(at, stdout);
  576.       fputs(" to ", stdout);
  577.       itox(ep, at, 5); fputs(at, stdout);
  578.       puts2(" ", xrnext + SYM);    
  579.       }
  580. #endif
  581.     if(xr < cdisk) {                /* in memory */
  582.       nxt = getint(xr + buffer);  
  583.       if(nxt == 0) ep += cbase;        /* end of chain is absolute */
  584.       putint(xr + buffer, ep);
  585.       }
  586.     else {                    /* on disk */
  587.       xrseek(xr - cdisk); read(csfd, &nxt, 2);
  588.       if(nxt == 0) ep += cbase;        /* end of chain is absolute */
  589.       xrseek(xr - cdisk); write(csfd, &ep, 2);
  590.       }
  591.     } while(xr = nxt);
  592.   }
  593.  
  594. /*
  595. ** search a library
  596. */
  597. search() {
  598.   int linked;
  599.   linked = NO;
  600.   newfn(ndxfn, infn, NDXEXT);
  601.   ndxfd = open(ndxfn, "r");
  602.   inrel = open(infn, "r");
  603.   while(YES) {                    /* rescan till done */
  604.     while(nxtmod()) {
  605.       if(strcmp(modname, TMNAME) == 0) {    /* will load this one last */
  606.         strcpy(tmfn, infn);
  607.         tmblock = inblock;
  608.         tmbyte = inbyte;
  609.         continue;
  610.         }
  611.       while(getrel() == ENAME) {
  612.         poll(YES);
  613.         if(isunres()) {                /* unresolved reference? */
  614.           load();                /* load module */
  615.           link();                /* link to previous ones */
  616.           linked = YES;
  617.           break;
  618.           }
  619.         }
  620.       }
  621.     if(!linked) break;
  622.     linked = NO;
  623.     rewind(ndxfd);
  624.     }
  625.   close(ndxfd);
  626.   close(inrel);
  627.   }
  628.  
  629. /*
  630. ** seek to next member in old library
  631. */
  632. seek() {
  633.   if(inblock == EOF) error("- Premature End of Index");
  634.   if(cseek(inrel, inblock, 0) == EOF)
  635.     error("- Corrupt Library or Index");
  636.   Uchrpos[inrel] = inbyte;
  637.   inrem = 0;            /* force getrel() to read a byte */
  638.   }
  639.  
  640. /*
  641. ** show a symbol
  642. */
  643. show(table, type) char *table, *type; {
  644.   char str[8];  int abs;
  645.   itox(abs = getint(table + VAL), str, 8);
  646.   fputs( str, stdout); fputs("'", stdout);
  647.   itox(abs + cbase, str, 6);
  648.   fputs( str, stdout); fputs(" ", stdout);
  649.   fputs(type, stdout); fputs(" ", stdout);
  650.   puts(table + SYM);
  651.   }
  652.  
  653. /*
  654. ** abort with a usage message
  655. */
  656. usage() {
  657.   error("Usage: LNK [-B] [-G#] [-L] [-M] program [module/library...]");
  658.   }
  659.  
  660. /*
  661. ** seek external reference
  662. */
  663. xrseek(byte) int byte; {
  664.   if(cseek(csfd, (byte >> 7) & 511, 0) == EOF)
  665.     error2("- Seek Error in ", csfn);
  666.   Uchrpos[csfd] = byte & 127;
  667.   }
  668.