
#include <stdio.h>
#include <string.h>

#define TRUE 1
#define FALSE 0
#define MAXSYMB 2000

typedef struct
{
    char name[9];
    int val;
} 
SYMBTYPE;

SYMBTYPE symbtab[MAXSYMB];
int symbtab_len=0;
FILE *infile,*outfile;

main(argc,argv)
int argc;
char *argv[];
{
    extern FILE *mustopen();
    
    int file_no,offs,len,start,rel,temp;
    char buf[15];
    register pc;
    register char c;

    if (argc < 2)
    {
        fputs("usage: link filename {filename}",stderr);
        exit(1);
    }
    file_no=1;
    offs=0;
    while (file_no < argc)
    {
        infile=mustopen(argv[file_no],TRUE);
        len=getoct();
        if (file_no==1)
            start=getoct();
        else
            getoct();
        while ((c=getc(infile))!='~')
        {
            ungetc(c,infile);
            if (symbtab_len >= MAXSYMB)
            {
                fputs("link: too many symbols",stderr);
                exit(1);
            }
            if ((c=getc(infile))=='@')
                rel=TRUE;
            else
            {
                rel=FALSE;
                ungetc(c,infile);
            }
            getstr(symbtab[symbtab_len].name);
            symbtab[symbtab_len].val=getoct();
            if (rel)
                symbtab[symbtab_len].val+=offs;
            symbtab_len++;
        }
        fclose(infile);
        file_no++;
        offs+=len;
    }
    sortsymb();
    outfile=mustopen(argv[1],FALSE);
    putoct(offs);
    putoct(start);
    pc=0;
    offs=0;
    file_no=1;
    while (file_no < argc)
    {
        infile=mustopen(argv[file_no],TRUE);
        len=getoct();
        getoct();
        while ((c=getc(infile)) != '~')
            getstr(buf);
        getstr(buf);
        while ((c=getc(infile)) != EOF)
        {
            pc+=2;
            if (c=='@')
                putoct(getsymb() - pc);
            else if (c>='a' && c<='z')
            {
                ungetc(c,infile);
                putoct(getsymb());
            }
            else if (c=='+')
                putoct(getoct() + offs);
            else if (c=='-')
                putoct(getoct() - offs);
            else if (c=='#')
            {
                putc(c,outfile);
                putoct(temp=getoct());
                pc+=temp-2;
            }
            else
            {
                ungetc(c,infile);
                putoct(getoct());
            }
        }
        fclose(infile);
        file_no++;
        offs+=len;        
    }
}

getstr(s)
register char *s;
{
    fgets(s,15,infile);
    if ((s=rindex(s,'\n')) != NULL)
        *s='\0';
}

getsymb()
{
    register i,comp;
    char name[10];
    
    getstr(name);
    for (i=0; i<symbtab_len; i++)
        if (!(comp=strcmp(name,symbtab[i].name)))
            return symbtab[i].val;
        else if (comp<0)
            break;
    fprintf(stderr,"link: symbol not defined - %s\n",name);
    exit(1);
}

sortsymb()
{
    register i,j,comp;
    SYMBTYPE temp;
    
    for (i=0; i+1<symbtab_len; i++)
        for (j=i+1; j<symbtab_len; j++)
        {
            comp=strcmp(symbtab[i].name,symbtab[j].name);
            if (!comp)
            {
                fprintf(stderr,"link: double defined symbol - %s\n",
                        symbtab[i].name);
                exit(1);
            }
            if (comp>0)
            {
                temp=symbtab[i];
                symbtab[i]=symbtab[j];
                symbtab[j]=temp;
            }
        }
}

getoct()
{
    register char c;
    register unsigned val=0;
    
    while ((c=getc(infile))>='0' && c<='7')
        val=val*8+c-'0';
    return val;
}
    
putoct(val)
register unsigned val;
{
    register c;
    
    c=15;
    while (c>=0)
    {
        putc((val>>c)+'0',outfile);
        val=(val<<(16-c))>>(16-c);
        c-=3;
    }
    putc('\n',outfile);
}

FILE *mustopen(s,in)
register char *s;
register in;
{
    char name[80];
    register char *t;
    FILE *fil;
    
    if ((t=rindex(s,'.')) != NULL)
    {
        while (*t && *t!='\\')
            t++;
        if (*t!='\\')
        {
            fputs("link: suffix is added by program\n",stderr);
            exit(1);
        }
    }
    strcpy(name,s);
    strcat(name,(in ? ".o" : ".pdp"));
    fil=fopen(name,(in ? "r" : "w"));
    if (fil == NULL)
    {
        fprintf(stderr,"link: can't open %s\n",name);
        exit(1);
    }
    return fil;
}

