/* lklist.c

   Copyright (C) 1989-1995 Alan R. Baldwin
   721 Berkeley St., Kent, Ohio 44240

This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 3, or (at your option) any
later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>. */

/*
 * 28-Oct-97 JLH:
 *           - lstarea: show s_id as string rather than array [NCPS]
 *           - lstarea: show a_id as string rather than array [NCPS]
 * 31-Oct-97 JLH: add NoICE output file genration in lstarea
 * 02-Apr-98 JLH: add XDATA, DATA, BIT flags to area output
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "aslink.h"

/*)Module       lklist.c
 *
 *      The module lklist.c contains the functions which
 *      output the linker .map file and produce a relocated
 *      listing .rst file.
 *
 *      lklist.c contains the following functions:
 *              int     dgt()
 *              VOID    lstarea()
 *              VOID    lkulist()
 *              VOID    lkalist()
 *              VOID    lkglist()
 *              VOID    newpag()
 *              VOID    slew()
 *
 *      lklist.c contains no local variables.
 */

/*)Function     VOID    slew(fp)
 *
 *              FILE *  fp              output file handle
 *
 *      The function slew() increments the page line counter.
 *      If the number of lines exceeds the maximum number of
 *      lines per page then a page skip and a page header are
 *      output.
 *
 *      local variables:
 *              int     i               loop counter
 *
 *      global variables:
 *              int     lop             current line number on page
 *              int     xflag           Map file radix type flag
 *
 *      functions called:
 *              int     fprintf()       c_library
 *              VOID    newpag()        lklist.c
 *
 *      side effects:
 *              The page line and the page count may be updated.
 */

VOID
slew(fp)
FILE *fp;
{
        register int i;

        if (lop++ >= NLPP) {
                newpag(fp);
                if (xflag == 0) {
                        fprintf(fp, "Hexadecimal\n\n");
                } else
                if (xflag == 1) {
                        fprintf(fp, "Octal\n\n");
                } else
                if (xflag == 2) {
                        fprintf(fp, "Decimal\n\n");
                }
                fprintf(fp, "Area       Addr   Size");
                fprintf(fp, "   Decimal Bytes (Attributes)\n");
                for(i=0;i<4;++i)
                        fprintf(fp, "      Value--Global");
                fprintf(fp, "\n\n");
                lop += 6;
        }
}

/*)Function     VOID    newpag()
 *
 *      The function newpag() outputs a page skip, writes the
 *      first page header line, sets the line count to 1, and
 *      increments the page counter.
 *
 *      local variables:
 *              none
 *
 *      global variables:
 *              int     lop             current line number on page
 *              int     page            current page number
 *
 *      functions called:
 *              int     fprintf()       c_library
 *
 *      side effects:
 *              The page and line counters are updated.
 */

VOID
newpag(fp)
FILE *fp;
{
        fprintf(fp, "\fASxxxx Linker %s,  page %u.\n", VERSION, ++page);
        lop = 1;
}

/* Used for qsort call in lstsym */
static int _cmpSymByAddr(const void *p1, const void *p2)
{
    struct sym **s1 = (struct sym **)(p1);
    struct sym **s2 = (struct sym **)(p2);
    int delta = ((*s1)->s_addr + (*s1)->s_axp->a_addr) -
                ((*s2)->s_addr + (*s2)->s_axp->a_addr);

    /* Sort first by address, then by name. */
    if (delta)
    {
        return delta;
    }
    return strcmp((*s1)->s_id,(*s2)->s_id);
}


#if     NCPS-8

/* NCPS != 8 */
/*)Function     VOID    lstarea(xp)
 *
 *              area *  xp              pointer to an area structure
 *
 *      The function lstarea() creates the linker map output for
 *      the area specified by pointer xp.  The generated output
 *      area header includes the area name, starting address,
 *      size of area, number of words (in decimal), and the
 *      area attributes.  The symbols defined in this area are
 *      sorted by ascending address and output one per line
 *      in the selected radix.
 *
 *      local variables:
 *              areax * oxp             pointer to an area extension structure
 *              int     c               character value
 *              int     i               loop counter
 *              char *  ptr             pointer to an id string
 *              int     nmsym           number of symbols in area
 *              a_uint  ai              temporary
 *              a_uint  aj              temporary
 *              sym *   sp              pointer to a symbol structure
 *              sym **  p               pointer to an array of
 *                                      pointers to symbol structures
 *
 *      global variables:
 *              FILE    *mfp            Map output file handle
 *              sym *symhash[NHASH]     array of pointers to NHASH
 *                                      linked symbol lists
 *              int     xflag           Map file radix type flag
 *
 *      functions called:
 *              int     fprintf()       c_library
 *              VOID    free()          c_library
 *              char *  malloc()        c_library
 *              char    putc()          c_library
 *              VOID    slew()          lklist.c
 *
 *      side effects:
 *              Map output generated.
 */

VOID
lstarea(struct area *xp)
{
        register struct areax *oxp;
        register int i;
        register char *ptr;
        int nmsym;
        a_uint     ai, aj;
        struct sym *sp;
        struct sym **p;
        int memPage;

        putc('\n', mfp);

        /*
         * Find number of symbols in area
         */
        nmsym = 0;
        oxp = xp->a_axp;
        while (oxp) {
                for (i=0; i<NHASH; i++) {
                        sp = symhash[i];
                        while (sp != NULL) {
                                if (oxp == sp->s_axp)
                                        ++nmsym;
                                sp = sp->s_sp;
                        }
                }
                oxp = oxp->a_axp;
        }
        if (nmsym == 0) {
                return;
        }

        if (xflag == 0) {
                fprintf(mfp, "Hexadecimal\n\n");
        } else
        if (xflag == 1) {
                fprintf(mfp, "Octal\n\n");
        } else
        if (xflag == 2) {
                fprintf(mfp, "Decimal\n\n");
        }
        fprintf(mfp, "Area                               ");
        fprintf(mfp, "Addr   Size   Decimal %s (Attributes)\n",
                (xp->a_flag & A_BIT)?"Bits ":"Bytes");/* JCF: For BIT print bits...*/
        fprintf(mfp, "--------------------------------   ");
        fprintf(mfp, "----   ----   ------- ----- ------------\n");
        /*
         * Output Area Header
         */
        ptr = &xp->a_id[0];
        fprintf(mfp, "%-32s", ptr );    /* JLH: width matches --- above */
        ai = xp->a_addr;
        aj = xp->a_size;
        if (xflag == 0) {
                fprintf(mfp, "   %04X   %04X", ai, aj);
        } else
        if (xflag == 1) {
                fprintf(mfp, " %06o %06o", ai, aj);
        } else
        if (xflag == 2) {
                fprintf(mfp, "  %05u  %05u", ai, aj);
        }
        fprintf(mfp, " = %6u. %s ", aj,
                (xp->a_flag & A_BIT)?"bits ":"bytes"); /* JCF: For BIT print bits...*/
        if (xp->a_flag & A_ABS) {
                fprintf(mfp, "(ABS");
        } else {
                fprintf(mfp, "(REL");
        }
        if (xp->a_flag & A_OVR) {
                fprintf(mfp, ",OVR");
        } else {
                fprintf(mfp, ",CON");
        }
        if (xp->a_flag & A_PAG) {
                fprintf(mfp, ",PAG");
        }

        memPage = 0x00;
        if (xp->a_flag & A_CODE) {
                fprintf(mfp, ",CODE");
                memPage = 0x0C;
        }
        if (xp->a_flag & A_XDATA) {
                fprintf(mfp, ",XDATA");
                memPage = 0x0D;
        }
        if (xp->a_flag & A_BIT) {
                fprintf(mfp, ",BIT");
                memPage = 0x0B;
        }
        fprintf(mfp, ")");
        if (xp->a_flag & A_PAG) {
                ai = (ai & 0xFF);
                aj = (aj > 256);
                if (ai || aj) { fprintf(mfp, "  "); }
                if (ai)      { fprintf(mfp, " Boundary"); }
                if (ai & aj)  { fprintf(mfp, " /"); }
                if (aj)      { fprintf(mfp, " Length"); }
                if (ai || aj) { fprintf(mfp, " Error"); }
        }

        /*
         * Allocate space for an array of pointers to symbols
         * and load array.
         */
        if ( (p = (struct sym **) malloc(nmsym*sizeof(struct sym *)))
                == NULL) {
                fprintf(mfp, "\nInsufficient space to build Map Segment.\n");
                return;
        }
        nmsym = 0;
        oxp = xp->a_axp;
        while (oxp) {
                for (i=0; i<NHASH; i++) {
                        sp = symhash[i];
                        while (sp != NULL) {
                                if (oxp == sp->s_axp) {
                                        p[nmsym++] = sp;
                                }
                                sp = sp->s_sp;
                        }
                }
                oxp = oxp->a_axp;
        }

        qsort(p, nmsym, sizeof(struct sym *), _cmpSymByAddr);

        /*
         * Symbol Table Output
         */

        i = 0;
        fprintf(mfp, "\n\n");
        fprintf(mfp, "      Value  Global\n");
        fprintf(mfp, "   --------  --------------------------------");
        while (i < nmsym) {
                fprintf(mfp, "\n");
                if (memPage != 0)
                        fprintf(mfp, "  %02X:", memPage);
                else
                        fprintf(mfp, "     ");

                sp = p[i];
                aj = sp->s_addr + sp->s_axp->a_addr;
                if (xflag == 0) {
                        fprintf(mfp, "%04X    ", aj);
                } else
                if (xflag == 1) {
                        fprintf(mfp, "%06o  ", aj);
                } else
                if (xflag == 2) {
                        fprintf(mfp, "%05u   ", aj);
                }
                ptr = &sp->s_id[0];
                fprintf(mfp, "%s", ptr );

                /* if cdb flag set the output cdb Information
                   and the symbol has a '$' sign in it then */
                if (dflag &&
                    strchr(ptr,'$'))
                    fprintf(dfp,"L:%s:%X\n",ptr,aj);

                /* NoICE output of symbol */
                if (jflag) DefineNoICE( ptr, aj, memPage );

                i++;
        }
        putc('\n', mfp);
        free(p);
}

#else

/* NCPS == 8 */
/*)Function     VOID    lstarea(xp)
 *
 *              area *  xp              pointer to an area structure
 *
 *      The function lstarea() creates the linker map output for
 *      the area specified by pointer xp.  The generated output
 *      area header includes the area name, starting address,
 *      size of area, number of words (in decimal), and the
 *      area attributes.  The symbols defined in this area are
 *      sorted by ascending address and output four per line
 *      in the selected radix.
 *
 *      local variables:
 *              areax * oxp             pointer to an area extension structure
 *              int     c               character value
 *              int     i               loop counter
 *              char *  ptr             pointer to an id string
 *              int     nmsym           number of symbols in area
 *              a_uint  ai              temporary
 *              a_uint  aj              temporary
 *              sym *   sp              pointer to a symbol structure
 *              sym **  p               pointer to an array of
 *                                      pointers to symbol structures
 *
 *      global variables:
 *              FILE    *mfp            Map output file handle
 *              sym *symhash[NHASH]     array of pointers to NHASH
 *                                      linked symbol lists
 *              int     xflag           Map file radix type flag
 *
 *      functions called:
 *              int     fprintf()       c_library
 *              VOID    free()          c_library
 *              char *  malloc()        c_library
 *              char    putc()          c_library
 *              VOID    slew()          lklist.c
 *
 *      side effects:
 *              Map output generated.
 */

VOID
lstarea(struct area *xp)
{
        register struct areax *oxp;
        register int c, i;
        register char *ptr;
        int nmsym;
        a_uint ai, aj;
        struct sym *sp;
        struct sym **p;
        int page;

        putc('\n', mfp);
        slew(mfp);
        /*
         * Output Area Header
         */
        ptr = &xp->a_id[0];
        while (ptr < &xp->a_id[NCPS]) {
                if ((c = *ptr++) != 0) {
                        putc(c, mfp);
                } else {
                        putc(' ', mfp);
                }
        }
        ai = xp->a_addr;
        aj = xp->a_size;
        if (xflag == 0) {
                fprintf(mfp, "   %04X   %04X", ai, aj);
        } else
        if (xflag == 1) {
                fprintf(mfp, " %06o %06o", ai, aj);
        } else
        if (xflag == 2) {
                fprintf(mfp, "  %05u  %05u", ai, aj);
        }
        fprintf(mfp, " = %6u. bytes ", aj);
        if (xp->a_flag & A_ABS) {
                fprintf(mfp, "(ABS");
        } else {
                fprintf(mfp, "(REL");
        }
        if (xp->a_flag & A_OVR) {
                fprintf(mfp, ",OVR");
        } else {
                fprintf(mfp, ",CON");
        }
        if (xp->a_flag & A_PAG) {
                fprintf(mfp, ",PAG");
        }

        page = 0x00;
        if (xp->a_flag & A_CODE) {
                fprintf(mfp, ",CODE");
                memPage = 0x0C;
        }
        if (xp->a_flag & A_XDATA) {
                fprintf(mfp, ",XDATA");
                memPage = 0x0D;
        }
        if (xp->a_flag & A_BIT) {
                fprintf(mfp, ",BIT");
                memPage = 0x0B;
        }
        fprintf(mfp, ")");
        if (xp->a_flag & A_PAG) {
                ai = (ai & 0xFF);
                aj = (aj > 256);
                if (ai || aj) { fprintf(mfp, "  "); }
                if (ai)      { fprintf(mfp, " Boundary"); }
                if (ai & aj)  { fprintf(mfp, " /"); }
                if (aj)      { fprintf(mfp, " Length"); }
                if (ai || aj) { fprintf(mfp, " Error"); }
        }

        /*
         * Find number of symbols in area
         */
        nmsym = 0;
        oxp = xp->a_axp;
        while (oxp) {
                for (i=0; i<NHASH; i++) {
                        sp = symhash[i];
                        while (sp != NULL) {
                                if (oxp == sp->s_axp)
                                        ++nmsym;
                                sp = sp->s_sp;
                        }
                }
                oxp = oxp->a_axp;
        }
        if (nmsym == 0) {
                putc('\n', mfp);
                slew(mfp);
                return;
        }

        /*
         * Allocate space for an array of pointers to symbols
         * and load array.
         */
        if ( (p = (struct sym **) malloc(nmsym*sizeof(struct sym *)))
                == NULL) {
                fprintf(mfp, "\nInsufficient space to build Map Segment.\n");
                slew(mfp);
                return;
        }
        nmsym = 0;
        oxp = xp->a_axp;
        while (oxp) {
                for (i=0; i<NHASH; i++) {
                        sp = symhash[i];
                        while (sp != NULL) {
                                if (oxp == sp->s_axp) {
                                        p[nmsym++] = sp;
                                }
                                sp = sp->s_sp;
                        }
                }
                oxp = oxp->a_axp;
        }

        qsort(p, nmsym, sizeof(struct sym *), _cmpSymByAddr);

        /*
         * Symbol Table Output
         */
        i = 0;
        while (i < nmsym) {
                fprintf(mfp, "\n");
                slew(mfp);
                fprintf(mfp, "     ");
                sp = p[i];
                aj = sp->s_addr + sp->s_axp->a_addr;
                if (xflag == 0) {
                        fprintf(mfp, "  %04X  ", aj);
                } else
                if (xflag == 1) {
                        fprintf(mfp, "%06o  ", aj);
                } else
                if (xflag == 2) {
                        fprintf(mfp, " %05u  ", aj);
                }
                ptr = &sp->s_id[0];
                fprintf(mfp, "%*s", NCPS, ptr );

                /* NoICE output of symbol */
                if (jflag) DefineNoICE( ptr, aj, memPage );
        }
        putc('\n', mfp);
        free(p);
        slew(mfp);
}
#endif

#ifdef SDK
VOID lstareatosym(struct area *xp)
{
        /* Output the current area symbols to a NO$GMB .sym file */
        register struct areax *oxp;
        register int i;
        int nmsym;
        a_uint a0;
        struct sym *sp;
        struct sym **p;

        /*
         * Find number of symbols in area
         */
        nmsym = 0;
        oxp = xp->a_axp;
        while (oxp) {
                for (i=0; i<NHASH; i++) {
                        sp = symhash[i];
                        while (sp != NULL) {
                                if (oxp == sp->s_axp)
                                        ++nmsym;
                                sp = sp->s_sp;
                        }
                }
                oxp = oxp->a_axp;
        }

        /*
         * Symbol Table Output
         */
        if (!((xp->a_size==0)&&(xp->a_addr==0)&&(nmsym==0))) {
                /* Dont worry about any area information */
                fprintf(mfp, "; Area: %s\n", xp->a_id );
                if (nmsym>0) {
                        /*
                         * Allocate space for an array of pointers to symbols
                         * and load array.
                         */
                        if ( (p = (struct sym **) malloc(nmsym*sizeof(struct sym *)))
                            == NULL) {
                                fprintf(mfp, "\nInsufficient space to build Map Segment.\n");
                                return;
                        }
                        nmsym = 0;
                        oxp = xp->a_axp;
                        while (oxp) {
                                for (i=0; i<NHASH; i++) {
                                        sp = symhash[i];
                                        while (sp != NULL) {
                                                if (oxp == sp->s_axp) {
                                                        p[nmsym++] = sp;
                                                }
                                                sp = sp->s_sp;
                                        }
                                }
                                oxp = oxp->a_axp;
                        }

                        qsort(p, nmsym, sizeof(struct sym *), _cmpSymByAddr);

                        i = 0;
                        while (i < nmsym) {
                                /* no$gmb requires the symbol names to be less than 32 chars long.  Truncate. */
                                char name[32];
                                strncpy(name, p[i]->s_id, 31);
                                name[31] = '\0';
                                if ((strncmp("l__", name, 3)!=0)&&(strchr(name,' ')==NULL)) {
                                        a0=p[i]->s_addr + p[i]->s_axp->a_addr;
                                        if (a0>0x7FFFU) {
                                                /* Not inside the ROM, so treat as being in bank zero */
                                                fprintf(mfp, "00:%04X %s\n", a0, name);
                                        }
                                        else {
                                                fprintf(mfp, "%02X:%04X %s\n", a0/16384, a0, name);
                                        }
                                }
                                i++;
                        }
                        free(p);
                }
        }
}
#endif

/*)Function     VOID    lkulist(i)
 *
 *              int     i       i # 0   process LST to RST file
 *                              i = 0   copy remainder of LST file
 *                                      to RST file and close files
 *
 *      The function lkulist() creates a relocated listing (.rst)
 *      output file from the ASxxxx assembler listing (.lst)
 *      files.  The .lst file's program address and code bytes
 *      are changed to reflect the changes made by ASlink as
 *      the .rel files are combined into a single relocated
 *      output file.
 *
 *      local variables:
 *              a_uint  pc              current program counter address
 *
 *      global variables:
 *              int     hilo            byte order
 *              int     gline           get a line from the LST file
 *                                      to translate for the RST file
 *              char    rb[]            read listing file text line
 *              FILE    *rfp            The file handle to the current
 *                                      output RST file
 *              int     rtcnt           count of data words
 *              int     rtflg[]         output the data flag
 *              a_uint  rtval[]         relocated data
 *              FILE    *tfp            The file handle to the current
 *                                      LST file being scanned
 *
 *      functions called:
 *              int     fclose()        c_library
 *              int     fgets()         c_library
 *              int     fprintf()       c_library
 *              VOID    lkalist()       lklist.c
 *              VOID    lkglist()       lklist.c
 *
 *      side effects:
 *              A .rst file is created for each available .lst
 *              file associated with a .rel file.
 */

VOID
lkulist(int i)
{
        a_uint pc;

        /*
         * Exit if listing file is not open
         */
        if (tfp == NULL)
                return;

        /*
         * Normal processing of LST to RST
         */
        if (i) {
                /*
                 * Evaluate current code address
                 */
                if (hilo == 0) {
                        pc = ((rtval[1] & 0xFF) << 8) + (rtval[0] & 0xFF);
                } else {
                        pc = ((rtval[0] & 0xFF) << 8) + (rtval[1] & 0xFF);
                }

                /*
                 * Line with only address
                 */
                if (rtcnt == 2) {
                        lkalist(pc);

                /*
                 * Line with address and code
                 */
                } else {
                        for (i=2; i < rtcnt; i++) {
                                if (rtflg[i]) {
                                        lkglist(pc++, rtval[i] & 0xFF);
                                }
                        }
                }

        /*
         * Copy remainder of LST to RST
         */
        } else {
                if (gline == 0)
                        fprintf(rfp, "%s", rb);

                while (fgets(rb, sizeof(rb), tfp) != 0) {
                        fprintf(rfp, "%s", rb);
                }
                fclose(tfp);
                tfp = NULL;
                fclose(rfp);
                rfp = NULL;
        }
}

/*)Function     VOID    lkalist(pc)
 *
 *              int     pc              current program counter value
 *
 *      The function lkalist() performs the following functions:
 *
 *      (1)     if the value of gline = 0 then the current listing
 *              file line is copied to the relocated listing file output.
 *
 *      (2)     the listing file is read line by line and copied to
 *              the relocated listing file until a valid source
 *              line number and a program counter value of the correct
 *              radix is found.  The new relocated pc value is substituted
 *              and the line is written to the RST file.
 *
 *      local variables:
 *              int     i               loop counter
 *              char    str[]           temporary string
 *
 *      global variables:
 *              int     gcntr           data byte counter
 *              int     gline           get a line from the LST file
 *                                      to translate for the RST file
 *              char    rb[]            read listing file text line
 *              char    *rp             pointer to listing file text line
 *              FILE    *rfp            The file handle to the current
 *                                      output RST file
 *              FILE    *tfp            The file handle to the current
 *                                      LST file being scanned
 *
 *      functions called:
 *              int     dgt()           lklist.c
 *              int     fclose()        c_library
 *              int     fgets()         c_library
 *              int     fprintf()       c_library
 *              int     sprintf()       c_library
 *              char *  strncpy()       c_library
 *
 *      side effects:
 *              Lines of the LST file are copied to the RST file,
 *              the last line copied has the code address
 *              updated to reflect the program relocation.
 */

VOID
lkalist(a_uint pc)
{
        char str[8];
        int i;

        /*
         * Exit if listing file is not open
         */
loop:   if (tfp == NULL)
                return;

        /*
         * Copy current LST to RST
         */
        if (gline == 0) {
                fprintf(rfp, "%s", rb);
                gline = 1;
        }

        /*
         * Clear text line buffer
         */
        for (i=0,rp=rb; i<sizeof(rb); i++) {
                *rp++ = 0;
        }

        /*
         * Get next LST text line
         */
        if (fgets(rb, sizeof(rb), tfp) == NULL) {
                fclose(tfp);
                tfp = NULL;
                fclose(rfp);
                rfp = NULL;
                return;
        }

        /*
         * Must have an ASxxxx Listing line number
         */
        if (!dgt(RAD10, &rb[30], 1)) {
                fprintf(rfp, "%s", rb);
                goto loop;
        }

        /*
         * Must have an address in the expected radix
         */
        if (radix == 16) {
                if (!dgt(RAD16, &rb[3], 4)) {
                        fprintf(rfp, "%s", rb);
                        goto loop;
                }
                sprintf(str, "%04X", pc);
                strncpy(&rb[3], str, 4);
        } else
        if (radix == 10) {
                if (!dgt(RAD10, &rb[3], 5)) {
                        fprintf(rfp, "%s", rb);
                        goto loop;
                }
                sprintf(str, "%05d", pc);
                strncpy(&rb[3], str, 5);
        } else
        if (radix == 8) {
                if (!dgt(RAD8, &rb[3], 6)) {
                        fprintf(rfp, "%s", rb);
                        goto loop;
                }
                sprintf(str, "%06o", pc);
                strncpy(&rb[3], str, 6);
        }

        /*
         * Copy updated LST text line to RST
         */
        fprintf(rfp, "%s", rb);
        gcntr = 0;
}

/*)Function     VOID    lkglist(pc,v)
 *
 *              int     pc              current program counter value
 *              int     v               value of byte at this address
 *
 *      The function lkglist() performs the following functions:
 *
 *      (1)     if the value of gline = 1 then the listing file
 *              is read line by line and copied to the
 *              relocated listing file until a valid source
 *              line number and a program counter value of the correct
 *              radix is found.
 *
 *      (2)     The new relocated values and code address are
 *              substituted and the line may be written to the RST file.
 *
 *      local variables:
 *              int     i               loop counter
 *              char    str[]           temporary string
 *
 *      global variables:
 *              int     gcntr           data byte counter
 *                                      set to -1 for a continuation line
 *              int     gline           get a line from the LST file
 *                                      to translate for the RST file
 *              char    rb[]            read listing file text line
 *              char    *rp             pointer to listing file text line
 *              FILE    *rfp            The file handle to the current
 *                                      output RST file
 *              FILE    *tfp            The file handle to the current
 *                                      LST file being scanned
 *
 *      functions called:
 *              int     dgt()           lklist.c
 *              int     fclose()        c_library
 *              int     fgets()         c_library
 *              int     fprintf()       c_library
 *              int     sprintf()       c_library
 *              char *  strncpy()       c_library
 *
 *      side effects:
 *              Lines of the LST file are copied to the RST file
 *              with updated data values and code addresses.
 */

VOID
lkglist(a_uint pc, int v)
{
        char str[8];
        int i;

        /*
         * Exit if listing file is not open
         */
loop:   if (tfp == NULL)
                return;

        /*
         * Get next LST text line
         */
        if (gline) {
                /*
                 * Clear text line buffer
                 */
                for (i=0,rp=rb; i<sizeof(rb); i++) {
                        *rp++ = 0;
                }

                /*
                 * Get next LST text line
                 */
                if (fgets(rb, sizeof(rb), tfp) == NULL) {
                        fclose(tfp);
                        tfp = NULL;
                        fclose(rfp);
                        rfp = NULL;
                        return;
                }

                /*
                 * Check for a listing line number if required
                 */
                if (gcntr != -1) {
                        if (!dgt(RAD10, &rb[30], 1)) {
                                fprintf(rfp, "%s", rb);
                                goto loop;
                        }
                        gcntr = 0;
                }
                gline = 0;
        }

        /*
         * Hex Listing
         */
        if (radix == 16) {
                /*
                 * Data Byte Pointer
                 */
                if (gcntr == -1) {
                        rp = &rb[8];
                } else {
                        rp = &rb[8 + (3 * gcntr)];
                }
                /*
                 * Number must be of proper radix
                 */
                if (!dgt(RAD16, rp, 2)) {
                        fprintf(rfp, "%s", rb);
                        gline = 1;
                        goto loop;
                }
                /*
                 * Output new data value, overwrite relocation codes
                 */
                sprintf(str, " %02X", v);
                strncpy(rp-1, str, 3);
                if (gcntr == -1) {
                        gcntr = 0;
                }
                /*
                 * Output relocated code address
                 */
                if (gcntr == 0) {
                        if (dgt(RAD16, &rb[3], 4)) {
                                sprintf(str, "%04X", pc);
                                strncpy(&rb[3], str, 4);
                        }
                }
                /*
                 * Output text line when updates finished
                 */
                if (++gcntr == 6) {
                        fprintf(rfp, "%s", rb);
                        gline = 1;
                        gcntr = -1;
                }
        } else
        /*
         * Decimal Listing
         */
        if (radix == 10) {
                /*
                 * Data Byte Pointer
                 */
                if (gcntr == -1) {
                        rp = &rb[9];
                } else {
                        rp = &rb[9 + (3 * gcntr)];
                }
                /*
                 * Number must be of proper radix
                 */
                if (!dgt(RAD10, rp, 3)) {
                        fprintf(rfp, "%s", rb);
                        gline = 1;
                        goto loop;
                }
                /*
                 * Output new data value, overwrite relocation codes
                 */
                sprintf(str, " %03d", v);
                strncpy(rp-1, str, 4);
                if (gcntr == -1) {
                        gcntr = 0;
                }
                /*
                 * Output relocated code address
                 */
                if (gcntr == 0) {
                        if (dgt(RAD10, &rb[3], 5)) {
                                sprintf(str, "%05d", pc);
                                strncpy(&rb[3], str, 5);
                        }
                }
                /*
                 * Output text line when updates finished
                 */
                if (++gcntr == 4) {
                        fprintf(rfp, "%s", rb);
                        gline = 1;
                        gcntr = -1;
                }
        } else
        /*
         * Octal Listing
         */
        if (radix == 8) {
                /*
                 * Data Byte Pointer
                 */
                if (gcntr == -1) {
                        rp = &rb[10];
                } else {
                        rp = &rb[10 + (3 * gcntr)];
                }
                /*
                 * Number must be of proper radix
                 */
                if (!dgt(RAD8, rp, 3)) {
                        fprintf(rfp, "%s", rb);
                        gline = 1;
                        goto loop;
                }
                /*
                 * Output new data value, overwrite relocation codes
                 */
                sprintf(str, " %03o", v);
                strncpy(rp-1, str, 4);
                if (gcntr == -1) {
                        gcntr = 0;
                }
                /*
                 * Output relocated code address
                 */
                if (gcntr == 0) {
                        if (dgt(RAD8, &rb[3], 6)) {
                                sprintf(str, "%06o", pc);
                                strncpy(&rb[3], str, 6);
                        }
                }
                /*
                 * Output text line when updates finished
                 */
                if (++gcntr == 4) {
                        fprintf(rfp, "%s", rb);
                        gline = 1;
                        gcntr = -1;
                }
        }
}

/*)Function     int     dgt(rdx,str,n)
 *
 *              int     rdx             radix bit code
 *              char    *str            pointer to the test string
 *              int     n               number of characters to check
 *
 *      The function dgt() verifies that the string under test
 *      is of the specified radix.
 *
 *      local variables:
 *              int     i               loop counter
 *
 *      global variables:
 *              ctype[]                 array of character types
 *
 *      functions called:
 *              none
 *
 *      side effects:
 *              none
 */

int
dgt(int rdx, char *str, int n)
{
        int i;

        for (i=0; i<n; i++) {
                if ((ctype[(unsigned char)(*str++)] & rdx) == 0)
                        return(0);
        }
        return(1);
}
