[comp.os.minix] peephole optimizer for minix

mwalls@unirot.UUCP (monty walls) (01/07/88)

--------------------------------------------------------------------------

	Enclosed is a copy of the Peephole Optimizer that was published
in Dr. Dobbs Journal in 1984.  I have included a copy of my optimization
file for the minix c compiler.  Using the peephole optimizer will
generally result in a 8 - 15 % speed improvement and a 8 - 10 % size
reduction.  The optimizer does jump optimizations also and will permit
you to compile and assemble program whose size would normally cause 
asld(emacs & elle) to choke.  I have also included the v20 optimizations
and inline code expansion I'm using in the kernel.  Word of warning the
optimizer data file should not have any trailing blank lines or
it eats all the input!!!  The peephole optimizer expects unpacked
assembler source.

-Monty Walls

---------------------------cut here---------------------------------------
echo x - copt.c
gres '^X' '' > copt.c << '/'
X/* opt.c -- Assembly language optimizer */
X
X/*------------------------------------------------------------------*
X *  Copyright 1984 by David L. Fox
X *  all rights reserved.
X *  Permission is granted for unlimited personal, non-commercial use.
X *
X *------------------------------------------------------------------*/
X
X/*------------------------------------------------------------------*
X *
X *  Performs peephole and jump optimization of an assemply language
X *  source file.  Transformations for peephole optimizer are specified
X *  in file opt.dat as follows
X *
X *          regular expressions
X *          for matched lines
X *          %
X *          replacement lines
X *          %%
X *
X *  Regular expression syntax is:
X *
X *          c       literal character
X *          .       any character except \n
X *          ^       beginning of line
X *          $       end of line
X *          [,,]    character class (any one of these characters)
X *          [~,,]   negated character class
X *          *       closure (zero of more occurrences of previous pattern
X *          \(..\)  mark beginning and end of a pattern group.
X *          \c      escaped character(\^, \. \[,,,etc)
X *
X *      Any special meaning of characters in a text pattern is lost when
X *          escaped, inside [], for:
X *
X *          ^       not at beginning
X *          $       not at end
X *          *       at beginning
X *
X *      A character class consists of zero or more of the following elements,
X *          surrounded by [ and ]:
X *
X *          c       literal character, including [
X *          a-c     range of characters (digits,lower,or upper case)
X *          ~       negated character class if at beginning
X *          \c      escaped character (\~,\-,\\,\])
X *
X *      Special meaning of characters in a character class is lost when escaped
X *          or for
X *
X *          ~       not at beginning
X *          -       at beginning or end
X *
X *
X *  Replacement line consistes of zero or more of the following elements.
X *
X *          c       literal character
X *          &       ditto, i.e., whatever was matched
X *          \1 - \9 whatever was the pattern group
X *          \c      escaped character (\&)
X *
X *
X *  Command line syntax is
X *
X *  opt [ -bnn -ffile -o -lre -jre -ure -v ] infile outfile
X *
X *  where the options are
X *
X *  -bnn    use nn lines in FIFO buffer (default = 1000).
X *  -ffile  read peephole optimization information from file
X *          instead of opt.dat.
X *  -o      do not do jump optimization
X *  -lre    labels defined by regular expression re.
X *  -jre    jump instructions defined by regular expression re.
X *  -ure    unconditional jump instructions defined by regular
X *          expression re.
X *  -v      verbose output off.
X *
X *  notes:
X *          for more information see article in DDJ #106.
X *          slight cosmetic changes and small patch for MINIX stdout,
X *          added documentation for optimizer based on Software Tools, and
X *          also updated the code for current C compilers.(7-26-87:MRW)
X *
X *
X *------------------------------------------------------------------*/
X
X#include <stdio.h>
X#include <ctype.h>
X#ifdef __TURBOC__
X#ifdef MINIX
X    extern char *strcpy();
X    extern char *index();
X    extern int strlen();
X#define strchr index
X#define isxdigit(c) (isdigit(c)||(c >= 'a' && c <= 'f')||(c >= 'A' && c <= 'F'))
X#else
X#include <string.h>
X#include <alloc.h>
X#endif
X#else
X    extern char *strcpy();
X    extern char *index();
X    extern int strlen();
X#define strchr index
X#endif
X
X#include "coptdefs.h"
X#include "coptglob.c"
X#include "regstuf.c"
X
X/* main program */
X
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X{
X
X    char inline[MAXSTR], *s;
X    int c, i;
X    char *p;
X    FILE *patfd;
X    struct symt *symp, *chksym();
X    char *newline();
X    FILE *mustopen();
X
X    /* initializations */
X    frstln = 0;
X    curln = maxlines - 1;
X
X    /* create patterns for jumps and labels from regular expressions */
X    /* allocate memory */
X
X    if ((lblpat = xalloc(MAXSTR)) ==(char *)NULL ||
X        (jmppat = xalloc(MAXSTR)) ==(char *)NULL ||
X        (ucjpat = xalloc(MAXSTR)) ==(char *)NULL)
X         error("not enough memory");
X
X    /* create patterns */
X    p = lblpat;
X    makepat(lbl, '\0', lblpat, &p);
X    p = jmppat;
X    makepat(jmpins, '\0', jmppat, &p);
X    p = ucjpat;
X    makepat(ucjmp, '\0', ucjpat, &p);
X
X    /* allocate memory for groups */
X    for (i = 0; i < 10; ++i) {
X        if ((gstart[i] = xalloc(MAXSTR)) == (char *)NULL)
X            error("not enough memory");
X    }
X
X    /* process options */
X    while ((c = getopt(argc, argv, OPTIONS)) != EOF) {
X        switch (tolower(c)) {
X            case 'b':       /* set FIFO buffer size */
X                maxlines = atoi(optarg);
X                break;
X            case 'f':       /* set data file name */
X                strcpy(patfile,optarg);
X                break;
X            case 'l':       /* set R.e. for labels */
X                p = lblpat;
X                makepat(optarg, '\0', lblpat, &p);
X                break;
X            case 'j':       /* R.e. for jumps */
X                p = jmppat;
X                makepat(optarg, '\0', jmppat, &p);
X                break;
X            case 'o':       /* Omit jump optimizations */
X                jmpopt = !jmpopt;
X                break;
X            case 'u':       /* R.e. for unconditional jumps */
X                p = ucjpat;
X                makepat(optarg, '\0', ucjpat, &p);
X                break;
X            case 'v':       /* switch verbose output */
X                verbose = !verbose;
X                break;
X
X            default:
X                error("usage: opt [-bnn -ffile -lre -jre -ure -v -o] [infile [outfile]]");
X        }
X    }
X
X    /* read optimization data file */
X    patfd = mustopen(patfile,"r");
X    gettmpl(patfd);
X    fclose(patfd);
X
X    /* allocate memory for FIFO buffer */
X    if ((linep = xalloc(maxlines*sizeof(char *))) == NULL)
X        error("not enough memory");
X
X    /* set up io channels */
X    outfd = stdout;
X    infd = stdin;
X
X    if (argc > optind) {
X        infd = mustopen(argv[optind++],"r");
X        if (argc > optind)
X            outfd = mustopen(argv[optind++],"w");
X    }
X
X    /* main loop */
X    while ((s = fgets(inline, MAXSTR, infd)) != NULL) {
X        newline(s);
X        /* update symbol table */
X        if (jmpopt && (symp = chksym(curln)) != NULL)
X            unjump(symp);                           /* remove jumps to JMP. */
X        /* optimize current line. */
X        optimize(curln);
X    }
X    fclose(infd);
X
X    /* output contents of FIFO buffer */
X    while (frstln != curln)
X        outline();
X    outline();
X    fflush(outfd);
X    if (outfd != stdout)
X        fclose(outfd);
X}
X
X/*------------------------------------------------------------------
X *
X *  newline -- add a line to buffer, output some old ones to make
X *             space if needed. update frstln, curln, nlines.
X *
X *------------------------------------------------------------------*/
X
Xchar *
Xnewline(str)
Xchar *str;
X{
X    char *s;
X
X    /* check for buffer wrap around */
X    if (++curln >= maxlines)
X        curln = 0;
X
X    /* make some space */
X    while (nlines >= maxlines || (s = xalloc(strlen(str)+1)) == NULL)
X        outline();
X    ++nlines;
X    return(linep[curln] = strcpy(s,str));
X}
X
X
X
X/*------------------------------------------------------------------
X *
X *  optimize -- look for improvable code and make improvement
X *
X *------------------------------------------------------------------*/
X
Xoptimize(ln)
Xint ln;
X{
X    struct trfmn *p;
X    int n, m;
X    struct lkline *o;
X
X    /* loop thru all possible transformations */
X    for (p = trans; p->nexttr != NULL; p = p->nexttr) {
X        /* check lines in buffer against r.e. in transformation */
X        lparen = rparen = 0;
X        for (n = p->nold, o = p->old;
X             n > 0 && match(linep[(m=ln-n+1) >= 0 ? m : m+maxlines],o->ln);
X             --n) {
X            o = o->nextln;
X        }
X        if (n > 0)      /* no match, skip to next transformation */
X            continue;
X        if (verbose)    /* print last line matched */
X            fprintf(stderr,"MATCH---%s", linep[ln]);
X        /* replace matched lines with improved code */
X        if (!subst(ln,p))
X            error("substitute failed");
X    }
X}
X
X/*------------------------------------------------------------------
X *
X *  unjump -- route jumps to jumps directly to target
X *            syp is pointer to symbol table entry for label on jmp
X *
X *------------------------------------------------------------------*/
X
Xunjump(syp)
Xstruct symt *syp;
X{
X    char *p, dpat[MAXSTR];
X    int i, errflg;
X    struct symt *dsyp, *isjmp();
X    struct jmp_lst *jp;
X
X    if (syp->scode == OUTBUF) {
X        /* target is not in memory */
X
X        fprintf(stderr,"%s not in buffer\n", syp->sname);
X        for (jp = syp->jmplst; jp != NULL; jp = jp->jlnext)
X            free(jp);   /* release memory used by jump list */
X
X        errflg = TRUE;
X    }
X    else {      /* target is in memory */
X        errflg = FALSE;
X        if ((dsyp = isjmp(linep[syp->lineno])) == NULL) {
X            message(linep[syp->lineno]);
X            error("not a jump");            /* can't happen */
X        }
X
X        /* make literal pattern for substitutions */
X        for (p = syp->sname, i = 0; *p != '\0'; ++p) {
X            dpat[i++] = LITCHAR;
X            dpat[i++] = *p;
X        }
X        dpat[i++] = CLOSURE;
X        dpat[i++] = LITCHAR;
X        dpat[i++] = LBLTERM;
X        dpat[i] = '\0';
X        /* replace destination label for every entry in jump list */
X        for (jp = syp->jmplst; jp != NULL && jp->njmp >= 0; jp = jp->jlnext) {
X            for (i = 0; i < jp->njmp; ++i) {
X                if (!subline(jp->jlines[i],dpat,dsyp->sname)) {
X                    errflg = TRUE;
X                    fprintf(stderr,"cannot substitute %s in %s\n",dsyp->sname,linep[jp->jlines[i]]);
X                }
X                /* add new reference to jump list */
X                addjmp(dsyp->sname,jp->jlines[i]);
X            }
X            free(jp);
X        }
X    }
X    syp->jmplst = NULL;
X    if (!errflg) {      /* delete label from target */
X        if (!subline(syp->lineno, dpat, ""))
X            fprintf(stderr,"failed to delete %s in %s\n",dpat,linep[syp->lineno]);
X    syp->scode = UNJMPD;
X    }
X}
X
X/*------------------------------------------------------------------
X *
X *  io routines for optimizer
X *
X *------------------------------------------------------------------*/
X
X/*------------------------------------------------------------------
X *
X *  outline -- Output a line from FIFO buffer.
X *
X *------------------------------------------------------------------*/
X
Xoutline()
X{
X    struct symt *s, *islab(), *isjmp();
X    struct jmp_lst *jp;
X
X    if (linep[frstln] != NULL) {            /* if line not deleted */
X        fputs(linep[frstln],outfd);
X        /* check for labels and jumps */
X        if ((s = islab(linep[frstln])) != NULL)
X            s->scode = OUTBUF;
X        if ((s = isjmp(linep[frstln])) != NULL) {
X            s->scode = OUTBUF;
X            /* delete jump list */
X            for (jp=s->jmplst; jp != NULL; jp = jp->jlnext)
X                free(jp);
X            s->jmplst = NULL;
X        }
X        --nlines;
X        free(linep[frstln]);
X    }
X    /* check for buffer wrap around. */
X    if (++frstln >= maxlines)
X        frstln = 0;
X}
X
X/*------------------------------------------------------------------
X *
X *  gettmpl -- get templates for changes.
X *
X *------------------------------------------------------------------*/
X
Xgettmpl(fd)
XFILE *fd;
X{
X    struct trfmn *t;
X    struct lkline *l, **ll, *mm, *newlk();
X    char line[MAXSTR], *s, *p;
X
X    /* allocate memory for first transformation */
X    t = trans = (struct trfmn *) xalloc(sizeof(struct trfmn));
X    t->nexttr = NULL;
X
X    while ((s = fgets(line,MAXSTR,fd)) != NULL) {
X        lparen = rparen = 0;                        /* init parens counters */
X        ll = &t->old;
X        t->nold = t->nnew = 0;
X        while (s != NULL && *s != '%') {
X            p = pat;
X            if (makepat(s,'\n',pat,&p) == NULL)  {
X                message(line);
X                error("bad r.e.");
X            }
X            l = newlk(ll,pat);
X            ll = &l->nextln;
X            ++t->nold;                              /* increment line count */
X            s = fgets(line,MAXSTR,fd);
X        }
X        if (s != NULL && strncmp(s,"%%",2) != 0) {
X            mm = &t->new;
X            while ((s=fgets(line,MAXSTR,fd)) != NULL && strncmp(s,"%%",2) != 0) {
X                /* get replacement strings. */
X                if (makesub(line,'\n',pat) == NULL) {
X                    message(line);
X                    error("bad sub string");
X                }
X                l = newlk(mm,pat);
X                mm = &l->nextln;
X                ++t->nnew;
X            }
X        }
X        /* allocate memory for next transformation */
X        t->nexttr = (struct trfmn *) xalloc(sizeof(struct trfmn));
X        t = t->nexttr;
X        t->nexttr = NULL;
X    }
X}
X
X/*------------------------------------------------------------------
X *
X *  newlk -- create new lkline structure.
X *
X *------------------------------------------------------------------*/
X
Xstruct lkline *
Xnewlk(lkp,str)
Xstruct lkline **lkp;    /* address of pointer to next member of linked list */
Xchar *str;
X{
X    struct lkline *l;
X
X    if ((l = (struct lkline *)xalloc(sizeof(struct lkline))) == NULL)
X        error("memory full");
X
X    *lkp = l;
X    if ((l->ln = xalloc(strlen(str)+1)) == NULL)
X        error("memory full");
X    strcpy(l->ln,str);
X    l->nextln = NULL;
X    return (l);
X}
X
X/*------------------------------------------------------------------
X *
X *  sttecpy -- Copy string between start and end pointers
X *
X *------------------------------------------------------------------*/
X
Xchar *
Xsttecpy(dest,start,end)
Xchar *dest, *end;
Xregister char *start;
X{
X    register char *p;
X
X    p = dest;
X    while (start < end)
X        *p++ = *start++;
X
X    *p = '\0';
X    return (dest);
X}
X
X/*------------------------------------------------------------------
X *
X *  symbol table routines for optimizer.
X *
X *------------------------------------------------------------------*/
X
X/*------------------------------------------------------------------
X *
X *  addsym -- add a label to the symbol table
X *
X *------------------------------------------------------------------*/
X
Xstruct symt *
Xaddsym(name)
Xchar *name;
X{
X    static struct symt *s = &frstsym, *sret;
X
X    strcpy(s->sname,name);
X    s->lineno = -1;
X    s->scode = INBUF;
X    if ((s->snext = xalloc(sizeof(struct symt))) == NULL)
X        error("symbol table overflow");
X    sret = s;
X    s = s->snext;
X    s->snext = NULL;
X    s->jmplst = NULL;
X    return (sret);
X}
X
X/*------------------------------------------------------------------
X *
X *  addjmp -- add line number to list of jumps to label.
X *
X *------------------------------------------------------------------*/
X
Xaddjmp(name,lineno)
Xchar *name;
Xint lineno;
X{
X    struct symt *symp, *findsym();
X    struct jmp_lst **jp;
X
X    if ((symp = findsym(name)) == NULL) {
X        symp = addsym(name);                   /* symbol not found add it to table */
X        symp->scode = UNDEF;
X    }
X    jp = &symp->jmplst;
X    /* skip to end of jump list */
X    while (*jp != NULL && (*jp)->njmp >= JTSIZE)
X        jp = &(*jp)->jlnext;                    /* skip to next label in jump list */
X    if (*jp == NULL) {                          /* alloc space for new block */
X        if ((*jp = xalloc(sizeof(struct jmp_lst))) == NULL)
X            error("jump list overflow");
X        else {                                  /* initialize new block */
X            (*jp)->njmp = 0;
X            (*jp)->jlnext = NULL;
X        }
X    }
X    (*jp)->jlines[(*jp)->njmp++] = lineno;
X}
X
X/*------------------------------------------------------------------
X *
X *  findsym -- linear search of symbol table.
X *
X *------------------------------------------------------------------*/
X
Xstruct symt *
Xfindsym(name)
Xchar *name;
X{
X    struct symt *s;
X    for (s = &frstsym; s->snext != NULL; s = s->snext)
X        if (strcmp(s->sname,name) == 0)
X            return(s);
X    return (NULL);
X}
X
X/*------------------------------------------------------------------
X *
X *  chksym -- check for a labeled line or line with a jump
X *
X *------------------------------------------------------------------*/
X
Xstruct symt *
Xchksym(curln)
Xint curln;
X{
X    struct symt *syp, *s, *islab(), *isjmp();
X
X    if (syp = islab(linep[curln]))
X        syp->lineno = curln;
X    if (syp->lineno == -1)           /* first time for this line */
X        syp->lineno = curln;
X    if ((s = isjmp(linep[curln])) != NULL)
X        addjmp(s->sname,curln);
X    if (syp != NULL) {
X        if (match(linep[curln], ucjpat) != NULL)
X            return (syp);           /* labeled unconditional jump */
X    }
X    return (NULL);
X}
X
X/*------------------------------------------------------------------
X *
X *  islab -- if str points to a valid label return pointer to its
X *           entry in symbol table, else return NULL.
X *
X *------------------------------------------------------------------*/
X
Xstruct symt *
Xislab(str)
Xchar *str;
X{
X    char *p, *q, name[LBLLEN];
X    struct symt *syp;
X
X    p = lblpat;
X    syp = NULL;
X    if ((q = amatch(str,str,&p)) != NULL)
X        if ((syp = findsym(sttecpy(name,str,q))) == NULL)
X            syp = addsym(name);
X    return (syp);
X}
X
X/*------------------------------------------------------------------
X *
X *  isjmp -- if line contains a jmp instruction return pointer to
X *           its destination label symbol table entry
X *           otherwise return NULL.
X *
X *------------------------------------------------------------------*/
X
Xstruct symt *
Xisjmp(line)
Xchar *line;
X{
X    char *p, *q, *r, name[LBLLEN];
X    struct symt *syp;
X
X    syp = NULL;
X    if ((p = match(line,jmppat)) != NULL) {
X        q = lblpat;
X        if ((r = amatch(p,p,&q)) != NULL) {
X            sttecpy(name,p,r);
X            if ((syp = findsym(name)) == NULL)
X                syp = addsym(name);
X        }
X    }
X    return (syp);
X}
X
X/*------------------------------------------------------------------
X *
X *  utility routines
X *
X *------------------------------------------------------------------*/
X
X/*------------------------------------------------------------------
X *
X *  message -- send message to console(stderr) and return
X *
X *------------------------------------------------------------------*/
X
Xmessage(s)
Xchar *s;
X{
X    fputs(s,stderr);
X    fputc('\n',stderr);
X    return (0);
X}
X
X/*------------------------------------------------------------------
X *
X *  mustopen -- attempt to open a file, exit on failure.
X *
X *------------------------------------------------------------------*/
X
XFILE *
Xmustopen(name,mode)
Xchar *name,*mode;
X{
X    FILE *fd;
X
X    if ((fd = fopen(name,mode)) == NULL) {
X        fputs(name,stderr);
X        error(": cannot open file");
X    }
X    return (fd);
X}
X
X/*------------------------------------------------------------------
X *
X *  xalloc -- alloc n bytes or exit on failure.
X *
X *------------------------------------------------------------------*/
Xchar *
Xxalloc(n)
Xint n;
X{
X    char *p;
X
X    p = malloc(n);
X    if (p != NULL)
X	return(p);
X    else
X        error("out of memory in xalloc");
X}
X
X/*------------------------------------------------------------------
X *
X *  error -- send a message to the console(stderr) and exit.
X *
X *------------------------------------------------------------------*/
X
Xerror(s)
Xchar *s;
X{
X    fputs(s,stderr);
X    fputc('\n',stderr);
X    exit(2);
X}
/
echo x - copt.dat
gres '^X' '' > copt.dat << '/'
Xand \([abcd]\)x,#255$
X%
Xxorb \1h,\1h
X%%
Xand \([abcd]x\),#0$
X%
Xxor \1,\1
X%%
Xsub \([abcd]x\),#0$
Xsbb \([abcd]x\),#0$
Xjne 1f
Xor \2,\1$
X1:
Xor \2,\2$
Xj\(.*\)$
X%
Xor \2,\1
Xj\3
X%%
Xsub [abcd]x,#0$
Xsbb \([abcd]x,#-*[1-9][0-9]*\)$
X%
Xsbb \1
X%%
Xpush \([abcd]x\)$
Xpop \1$
X%
X%%
Xpush \([abcd]x\)$
Xpop \([abcd]x\)$
X%
Xmov \2,\1
X%%
Xpop ax
Xpush ax
X%
X%%
Xpush bp
Xmov bp,sp
Xpush ax
Xpush ax
X%
Xpush bp
Xmov bp,sp
Xsub sp,#4
X%%
Xpush bp
Xmov bp,sp
Xpush ax
X\(.*\)$
X%
Xpush bp
Xmov bp,sp
Xdec sp
Xdec sp
X\1
X%%
Xcall \(.*\)$
Xpop si
Xpop si
X%
Xcall \1
Xadd sp,#4
X%%
Xcall \(.*\)$
Xpop si
X\(.*\)$
X%
Xcall \1
Xinc sp
Xinc sp
X\2
X%%
Xmovb al,\(.*\)$
Xxorb ah,ah
Xcbw
X%
Xmovb al,\1
Xcbw
X%%
Xmov cx,#\(-*[0-9]*\)$
Xmov bx,#\1$
X\(.*\)$
Xcall \.cuu
X%
X\2
X%%
Xmov cx,#\(-*[0-9]*\)$
Xmov bx,#\1$
Xcall \.cuu
X%
X%%
Xmov cx,#\(-*[0-9]*\)$
Xmov bx,#\1$
X\(.*\)$
X\(.*\)$
Xcall \.cuu
X%
X\2
X\3
X%%
Xmov cx,#\(-*[0-9]*\)$
Xmov bx,#\1$
X\(mov ax,.*\)$
X\(push .*\)$
X\(push .*\)$
Xcall \.cuu
X%
X\2
X\3
X\4
X%%
Xmov \(-*[0-9]*([abcds][pxi])\),\([abcds][pxi]\)$
Xcmp \1,\(.*\)$
X%
Xmov \1,\2
Xcmp \2,\3
X%%
Xmov \(-*[0-9]*([abcds][pxi])\),\([abcds][pxi]\)$
Xpush \1$
X%
Xmov \1,\2
Xpush \2
X%%
Xmov \(-*[0-9]*([abcds][pxi])\),\([abcds][pxi]\)$
Xmov \2,\1$
X%
Xmov \1,\2
X%%
Xmov \([abcds][pxi]\),\(-*[0-9]*([abcds][pxi])\)$
Xmov \2,\1$
X%
Xmov \1,\2
X%%
Xmov \(bx,-*[0-9]*(bp)\)$
Xmov \([acd]x,-*[0-9]*(bx)\)$
Xmov \1$
X%
Xmov \1
Xmov \2
X%%
Xmov \(si,-*[0-9]*(bp)\)$
Xmov \([abcd]x,-*[0-9]*(si)\)$
Xmov \1$
X%
Xmov \1
Xmov \2
X%%
Xmov \(bx,-*[0-9]*(bp)\)$
Xmov \(-*[0-9]*(bx)\),\(.*\)$
Xmov \1$
X%
Xmov \1
Xmov \2,\3
X%%
Xmov \(si,-*[0-9]*(bp)\)$
Xmov \(-*[0-9]*(si)\),\(.*\)$
Xmov \1$
X%
Xmov \1
Xmov \2,\3
X%%
Xmov \([abcds][xi]\),\(-*[0-9][0-9]*(bp)\)$
Xmov \([abcds][xi]\),\2$
X%
Xmov \1,\2
Xmov \3,\1
X%%
/
echo x - copt.man
gres '^X' '' > copt.man << '/'
X
X   opt.c -- Assembly language optimizer
X
X    Copyright 1984 by David L. Fox all rights reserved.
X    Permission is granted for unlimited personal, non-commercial use.
X
X    Performs peephole and jump optimization of an assemply language
X    source file.  Transformations for peephole optimizer are specified
X    in file opt.dat as follows
X
X            regular expressions
X            for matched lines
X            %
X            replacement lines
X            %%
X
X    Regular expression syntax is:
X
X            c       literal character
X            .       any character except \n
X            ^       beginning of line
X            $       end of line
X            [,,]    character class (any one of these characters)
X            [~,,]   negated character class
X            *       closure (zero of more occurrences of previous pattern
X            \(..\)  mark beginning and end of a pattern group.
X            \c      escaped character(\^, \. \[,,,etc)
X
X        Any special meaning of characters in a text pattern is lost when
X            escaped, inside [], for:
X
X            ^       not at beginning
X            $       not at end
X            *       at beginning
X
X        A character class consists of zero or more of the following elements,
X            surrounded by [ and ]:
X
X            c       literal character, including [
X            a-c     range of characters (digits,lower,or upper case)
X            ~       negated character class if at beginning
X            \c      escaped character (\~,\-,\\,\])
X
X        Special meaning of characters in a character class is lost when escaped
X            or for
X
X            ~       not at beginning
X            -       at beginning or end
X
X
X    Replacement line consistes of zero or more of the following elements.
X
X            c       literal character
X            &       ditto, i.e., whatever was matched
X            \1 - \9 whatever was the pattern group
X            \c      escaped character (\&)
X
X
X    Command line syntax is
X
X    opt [ -bnn -ffile -o -lre -jre -ure -v ] infile outfile
X
X    where the options are
X
X    -bnn    use nn lines in FIFO buffer (default = 1000).
X    -ffile  read peephole optimization information from file
X            instead of opt.dat.
X    -o      do not do jump optimization
X    -lre    labels defined by regular expression re.
X    -jre    jump instructions defined by regular expression re.
X    -ure    unconditional jump instructions defined by regular
X            expression re.
X    -v      verbose output off.
X
X
/
echo x - copt.v20
gres '^X' '' > copt.v20 << '/'
Xpush bp
Xmov bp,sp
Xsub sp,#\(-*[0-9]*\)$
X%
X.byte 0xc8
X.word \1
X.byte 0
X%%
Xpush bp
Xmov bp,sp
X\(.*\)$
X%
X.byte 0xc8
X.word 0
X.byte 0
X\1
X%%
Xmov sp,bp
Xpop bp
Xret
X%
X.byte 0xc9
Xret
X%%
Xmov ax,#\(-*[0-9]*\)$
Xpush ax
X\([Jj].*\)
X%
X.byte 0x68
X.word \1
X\2
X%%
Xmov ax,#\(-*[0-9]*\)$
Xpush ax
X\(mov ax,.*\)$
X%
X.byte 0x68
X.word \1
X\2
X%%
Xxor ax,ax
Xpush ax
X\([Jj].*\)
X%
X.byte 0x68
X.word 0
X\1
X%%
Xmov ax,#\(-*[0-9]*\)$
Xpush ax
X\([Ii][0-9][0-9]*:\)$
X%
X.byte 0x68
X.word \1
X\2
X%%
Xxor ax,ax
Xpush ax
X\([Ii][0-9][0-9]*:\)$
X%
X.byte 0x68
X.word 0
X\1
X%%
Xmov ax,#\(-*[0-9]*\)$
Xpush ax
X\(call.*\)
X%
X.byte 0x68
X.word \1
X\2
X%%
Xxor ax,ax
Xpush ax
X\(call.*\)
X%
X.byte 0x68
X.word 0
X\1
X%%
Xpush \([abcd]x\)$
Xpop \1$
X%
X%%
Xpush \([abcd]x\)$
Xpop \([abcd]x\)$
X%
Xmov \2,\1
X%%
/
echo x - coptdefs.h
gres '^X' '' > coptdefs.h << '/'
X/* optdefs.h -- definitions and declarations for peephole optimizer */
X
X/*------------------------------------------------------------------*
X *  Copyright 1984 by David L. Fox
X *  all rights reserved.
X *  Permission is granted for unlimited personal, non-commercial use.
X *
X *------------------------------------------------------------------*/
X
X/* Standard definitions */
X
X#define TRUE        1
X#define FALSE       0
X#define MAXSTR      256
X
X/* Definitions for optimizer */
X
X#define MAXLINES        1000    /* lines of source in memory */
X#define JTSIZE          10      /* entries in jump table */
X#define LBLLEN          15      /* Maximum label length */
X#define OPTIONS         "b:f:j:l:u:ov" /* legal options */
X#define LBLTERM         ':'     /* character which terminates a label */
X#define SYMLEN          8       /* symbol length */
X#define UNDEF           0       /* undefined symbol */
X#define INBUF           1       /* symbol defined and in buffer */
X#define OUTBUF          2       /* symbol written to output. removed from buffer */
X#define UNJMPD          3       /* symbol is label for jump. replaced with dest */
X
X/* structure declarations */
X
Xstruct lkline {                 /* linked list of lines */
X    struct lkline *nextln;      /* link to next line */
X    char *ln;                   /* this line */
X};
X
Xstruct trfmn {                  /* transformation from old to new code */
X    int nold;                   /* number of lines in old construction */
X    int nnew;                   /* number of lines in new construction */
X    struct lkline *old;         /* link list of old reg. exprs. */
X    struct lkline *new;         /* link list of new substitutions */
X    struct trfmn *nexttr;       /* link to next transformation */
X};
X
Xstruct jmp_lst {                /* list of lines with jumps to a label */
X    int njmp;
X    int jlines[JTSIZE];         /* line nos. containing jump to label */
X    struct jmp_lst *jlnext;     /* next link */
X};
X
Xstruct symt {                   /* symbol table entry */
X    char sname[SYMLEN+1];
X    int lineno;                 /* line no in buffer */
X    char scode;                 /* status: undefined, in buffer, written out, replaced */
X    struct jmp_lst *jmplst;
X    struct symt *snext;
X};
X
X
X/* definitions for regular expression code */
X
X#define MAXPAT          (2*MAXSTR)
X#define CLOSIZE         1           /* size of closure entry */
X#define CLOSURE         '*'
X#define BOL             '^'
X#define BOLSTR          "^"         /* string consisting of BOL */
X#define EOL             '$'
X#define ANY             '.'
X#define CCL             '['
X#define CCLEND          ']'
X#define NEGATE          '~'
X#define NCCL            '!'         /* cannot be same as negate */
X#define LITCHAR         'c'
X
X#define ENDSTR          '\0'
X#define NEWLINE         '\n'
X#define ESCAPE          '\\'
X#define DASH            '-'
X
X#define GRPST           '('         /* codes for grouping and subpatterns */
X#define GRPEND          ')'
X#define GRPPAT          '#'
X#define DITTO           -1
X
Xextern char *amatch(), *match(), *makesub(), *esc(), *makepat(), *xalloc();
/
echo x - coptglob.c
gres '^X' '' > coptglob.c << '/'
X
X/* optglob.c -- Global Varaible declarations for optimizer */
X
X/*------------------------------------------------------------------*
X *  Copyright 1984 by David L. Fox
X *  all rights reserved.
X *  Permission is granted for unlimited personal, non-commercial use.
X *
X *------------------------------------------------------------------*/
X
Xchar **linep;                   /* array of pointer to lines */
Xint nlines = 0;                 /* number of lines */
Xint frstln;                     /* index of oldest line in buffer */
Xint curln;                      /* Current line in buffer */
XFILE *infd;                     /* input file descriptor */
XFILE *outfd;                    /* output file descriptor */
Xunsigned maxlines = MAXLINES;   /* lines in buffer */
Xint verbose = TRUE;             /* verbose output flag */
Xint jmpopt = FALSE;             /* flag for jump optimization */
Xextern int optind ;             /* index of next argument */
Xextern char *optarg;            /* pointer to option argument */
Xstruct trfmn *trans;
X#ifdef MINIX
Xchar patfile[18] = "/usr/lib/copt.dat";   /* file for optimization data */
X#else
Xchar patfile[18] = "opt.dat";   /* file for optimization data */
X#endif
X
Xchar *lbl = "I[0-9A-F]*";           /* label template */
Xchar *jmpins = "j[abcnxoszeglp]* ";     /* jump template */
X
Xchar *ucjmp = "jmp ";                       /* unconditional jump */
X
Xchar *lblpat, *jmppat, *ucjpat;             /* pointers to patters made
X                                               for reg. expr. */
X
Xstruct symt frstsym = {
X        {'\0'},     /* sname */
X        -1,         /* line no */
X        0,          /* scode */
X        NULL,       /* jmplst */
X        NULL,       /* snext */
X};
X
Xstruct symt *symtab = &frstsym;             /* start of symbol table */
X
X/* Global variable declarations for regular expression code */
X
Xchar pat[MAXPAT];               /* space for pattern */
Xint lparen, rparen;             /* parenthesis counts */
Xchar *gstart[10];               /* pointers to start of grouped text */
Xchar *gend[10];                 /* pointerso to end of grouped text */
Xchar *lgp;                      /* pointer to start of last group */
X
/
echo x - inline.dat
gres '^X' '' > inline.dat << '/'
Xcall _get_byte
Xadd sp,#4
X%
Xmov  dx,es
Xpop  es
Xpop  bx
Xseg  es
Xmovb al,0(bx)
Xxorb ah,ah
Xmov  es,dx
X%%
Xcall _port_out
Xadd sp,#4
X%
Xpop dx
Xpop ax
Xout
X%%
Xcall _port_in
Xadd sp,#4
X%
Xpop dx
Xpop bx
Xin
Xxorb ah,ah
Xmov 0(bx),ax
X%%
Xcall _send$
Xadd sp,#4
X%
Xmov cx,*1
Xpop ax
Xpop bx
Xint 32
X%%
Xcall _receive$
Xadd sp,#4
X%
Xmov cx,*2
Xpop ax
Xpop bx
Xint 32
X%%
Xcall _sendrec$
Xadd sp,#4
X%
Xmov cx,*3
Xpop ax
Xpop bx
Xint 32
X%%
/
echo x - locate.c
gres '^X' '' > locate.c << '/'
X
X/*------------------------------------------------------------------*
X *
X *  locate -- look for c in character class at pat.
X *
X *------------------------------------------------------------------*/
X
Xlocate(c, pat)
Xregister char c;
Xchar *pat;
X{
X    register int i;
X
X    /* size of class is at pat[0], characters follow */
X    for (i = *pat; i > 0; --i)
X        if (c == pat[i])
X            return (TRUE);
X    return(FALSE);      
X}
/
echo x - regstuf.c
gres '^X' '' > regstuf.c << '/'
X/* regstuf.c -- regular expression routines */
X
X/*------------------------------------------------------------------*
X *
X *  these routines are translations into C of the regular expression
X *  matching and pattern generation routines in
X *  B.W. Kernighan and P.J. Pluager, "Software Tools in Pascal"
X *  chapters 5 and 6.
X *
X *  the major extension is the addition of code to allow matching
X *  and substitution of parts of expressions using \(....\) for
X *  grouping and \n for replacement.
X *
X *------------------------------------------------------------------*/
X
X/*------------------------------------------------------------------*
X *
X *  match -- find match anywhere on line.
X *
X *------------------------------------------------------------------*/
X
Xchar *
Xmatch(lin, pat)
Xregister char *lin, *pat;
X{
X    char *pos, *lst, *p;
X
X    lst = lin;
X    for (pos = NULL; *lin != ENDSTR && pos == NULL; ++lin) {
X        p = pat;
X        pos = amatch(lst, lin, &p);
X    }
X    return(pos);
X}
X
X/*------------------------------------------------------------------*
X *
X *  amatch -- anchored match
X *            look for a match between lin and pat.
X *            return a pointer to next unmatched character in line,
X *            or Null if no match.
X *
X *            lst is pointer to start of line.
X *            lin is pointer to current position in line.
X *            *ppat is pointer to current position in pattern
X *                updated if match is found.
X *
X *------------------------------------------------------------------*/
X
Xchar *
Xamatch(lst, lin, ppat)
Xchar *lst, *lin, **ppat;
X{
X    char *lp, *mp, *tp;
X    int level, skip;
X    char tpat[MAXPAT];
X
X    while (**ppat != ENDSTR) {
X        switch (**ppat) {
X            case CLOSURE:
X                *ppat += patsize(*ppat);        /* step over closure */
X                lp = lin;
X                while (*lp != ENDSTR)
X                    if (!omatch(lst, &lp, *ppat))
X                        break;
X                if (*(tp=(*ppat) + patsize(*ppat)) == GRPEND)
X                    skip = patsize(tp);         /* at end of group, skip over group closure */
X                else
X                    skip = 0;
X                /* lp points to input character that make us fail */
X                /* match rest of pattern against rest of input. */
X                /* shrink closure by 1 after each failure. */
X                while (lp >= lin) {
X                    tp = *ppat + patsize(*ppat) + skip;
X                    if (skip)
X                        gend[lparen] = gstart[lparen] + (int)(lp - lgp);
X                    if ((mp = amatch(lst, lp, &tp)) != NULL)
X                        if (skip == 0) {
X                            *ppat = tp;
X                            return (mp);
X                        }
X                        else {
X                            *ppat += patsize(*ppat) + skip;
X                            return (lp);
X                        }
X                    else
X                        --lp;
X                }
X                return (NULL);
X                /* if mp == NULL failure, else success */
X            case GRPST:     /* start of group */
X                mp = *ppat;
X                *ppat += patsize(*ppat);
X                level = ++lparen;
X                strcpy(gstart[level], lin);
X                lgp = lin;
X                if ((lin = amatch(lst, lin, ppat)) == NULL) {
X                    *ppat = mp;
X                    --lparen;
X                    return (NULL);
X                }
X                gend[level] = gstart[level] + (int)(lin - lgp);
X                break;
X            case GRPEND: /* end of group */
X                *ppat += patsize(*ppat);
X                ++rparen;
X                return(lin);
X            case GRPPAT:
X                if ((level = (*ppat)[1] - '0') < 1 || level > 9) {
X                    message(*ppat);
X                    error(" amatch: can't happen");
X                }
X                *ppat += patsize(*ppat);
X                if (gend[level] > gstart[level] + MAXSTR/2)
X                    return (NULL);                          /* group is to long */
X
X                /* make temporary pattern from matched group */
X                for (tp = tpat, lp = gstart[level]; lp < gend[level]; ) {
X                    *tp++ = LITCHAR;
X                    *tp++ = *lp++;
X                }
X                *tp = ENDSTR;
X                tp = tpat;
X                if ((lin = amatch(lst, lin, &tp)) == NULL)
X                    return(NULL);
X                break;
X            default:
X                if (omatch(lst, &lin, *ppat))
X                    *ppat += patsize(*ppat);
X                else
X                    return (NULL);
X        }
X    }
X    return (lin);
X}
X
X/*------------------------------------------------------------------*
X *
X *  omatch -- match one pattern element at pat.
X *
X *------------------------------------------------------------------*/
X
Xomatch(lin, linp, pat)
Xchar *lin, **linp, *pat;
X{
X    char *lpos;
X    int advance;
X
X    advance = -1;
X    lpos = *linp;
X
X    if (*lpos == ENDSTR)
X        return (FALSE);
X    switch (*pat) {
X        case LITCHAR:
X            if (*lpos == pat[1])
X                advance = 1;
X            break;
X        case BOL:
X            if (lpos == lin)
X                advance = 0;
X            break;
X        case ANY:
X            if (*lpos != NEWLINE)
X                advance = 1;
X            break;
X        case EOL:
X            if (*lpos == NEWLINE)
X                advance = 0;
X            break;
X        case CCL:
X            if (locate(*lpos, &(pat[1])))
X                advance = 1;
X            break;
X        case NCCL:
X            if (*lpos != NEWLINE && !locate(*lpos, &(pat[1])))
X                advance = 1;
X            break;
X        default:
X            message(pat);
X            error("in omatch can't happen");
X    }
X    if (advance >= 0) {
X        *linp += advance;
X        return (TRUE);
X    }
X    else
X        return (FALSE);
X}
X
X/*------------------------------------------------------------------*
X *
X *  patsize -- returns size of pattern entry at pat.
X *
X *------------------------------------------------------------------*/
X
Xpatsize(pat)
Xregister char *pat;
X{
X    switch (*pat) {
X        case LITCHAR:
X        case GRPPAT:
X            return (2);
X        case BOL:
X        case EOL:
X        case ANY:
X        case GRPST:
X        case GRPEND:
X            return (1);
X        case CCL:
X        case NCCL:
X            return (pat[1] + 2);
X        case CLOSURE:
X            return (CLOSIZE);
X        default:
X            message(pat);
X            error("in patsize: can't happen");  /* never returns */
X    }
X}
X
X/*------------------------------------------------------------------*
X *
X *  makepat -- make pattern from arg. terminate at delim.
X *
X *------------------------------------------------------------------*/
X
Xchar *
Xmakepat(arg, delim, stpat, ppat)
Xchar delim;
Xregister char *arg;
Xchar *stpat, **ppat;
X{
X    char *lp, *starg;
X    char *lastp, *tmp;
X
X    starg = arg;
X    lastp = *ppat;
X    while (*arg != delim && *arg != ENDSTR) {
X        lp = *ppat;
X        if (*arg == ANY) {
X            addpat(ANY,stpat,ppat);
X        }
X        else if (*arg == BOL && arg == starg) {
X            addpat(BOL,stpat,ppat);
X        }
X        else if (*arg == EOL) {
X            addpat(EOL,stpat,ppat);
X        }
X        else if (*arg == CCL) {
X            if (!getccl(&arg,ppat,stpat))
X                return(NULL);
X        }
X        else if (*arg == CLOSURE && arg > starg) {
X            if (*(lp = lastp) == BOL || *lp == EOL || *lp == CLOSURE)
X                return(NULL);        /* closure on a null string not allowed */
X            stclose(ppat,lastp,stpat);
X        }
X        else if (*arg == ESCAPE && arg[1] == '(') {
X            addpat(GRPST,stpat,ppat);
X            arg += 2;
X            if (++lparen > 9) return(NULL);
X            if ((arg = makepat(arg,delim,pat,ppat)) != NULL)
X                --arg;
X            else
X                return(NULL);
X        }
X        else if (*arg == ESCAPE && arg[1] == ')') {
X            addpat(GRPEND,stpat,ppat);
X            ++rparen;
X            return(arg+2);
X        }
X        else if (*arg == ESCAPE && ('0' < arg[1] && arg[1] <= '9')) {
X            addpat(GRPPAT,stpat,ppat);
X            addpat(arg[1],stpat,ppat);
X            ++arg;
X        }
X        else {
X            addpat(LITCHAR, stpat, ppat);
X            tmp = esc(arg);
X            addpat(*arg,stpat,ppat);
X            arg = tmp - 1;
X        }
X        lastp = lp;
X        ++arg;
X    }
X
X    if (*arg != delim)
X        return(NULL);
X
X    if (!addpat(ENDSTR,stpat,ppat))
X        return(NULL);
X
X    if (lparen != rparen)
X        return (NULL);
X
X    return(arg);
X}
X
X/*------------------------------------------------------------------*
X *
X *  addpat -- add c at ppat, check for overflow
X *
X *------------------------------------------------------------------*/
X
Xaddpat(c, stpat, ppat)
Xchar c, *stpat;
Xregister char **ppat;
X{
X    if (*ppat - stpat > MAXPAT)
X        return(FALSE);
X    *(*ppat)++ = c;
X    return(TRUE);
X}
X
X/*------------------------------------------------------------------*
X *
X *  getccl -- expand char class at arg into pat.
X *
X *------------------------------------------------------------------*/
X
Xgetccl(argp, patp, stpat)
Xregister char **argp;
Xchar **patp, *stpat;
X{
X    char *ccst;
X
X    ++(*argp);                      /* skip [ */
X    if (**argp == NEGATE) {
X        addpat(NCCL,stpat,patp);
X        ++(*argp);
X    }
X    else
X        addpat(CCL,stpat,patp);
X    ccst = *patp;
X    addpat(0,stpat,patp);       /* space for count */
X    dodash(CCLEND, argp, stpat, patp);
X    *ccst = *patp - ccst - 1;
X    return(**argp == CCLEND);
X}
X
X/*------------------------------------------------------------------*
X *
X *  stclose -- inset closure entry at *patp
X *
X *------------------------------------------------------------------*/
X
Xstclose(patp, lastp, stpat)
Xchar **patp, *lastp, *stpat;
X{
X    register char *pp;
X    char *q;
X
X    for (pp = *patp - 1; pp >= lastp; --pp) {
X        q = pp + CLOSIZE;
X        addpat(*pp,stpat,&q);
X    }
X    *patp += CLOSIZE;
X    *lastp = CLOSURE;
X}
X
X/*------------------------------------------------------------------*
X *
X *  atoib -- similar to atoi except will work with any number base(2-16)
X *
X *------------------------------------------------------------------*/
X
Xint
Xatoib(str, base)
Xregister char *str;
Xregister int base;
X{
X    int n, digit;
X
X    for (n = 0; *str; str++) {
X        if (base == 10 || base == 8) {
X            if (isdigit(*str))
X                digit = *str - '0';
X        }
X        else if (base == 16) {
X            if (isxdigit(*str))
X                if (*str >= 'a')
X                    digit = *str - 'a' + 10;
X                else if (*str >= 'A')
X                    digit = *str - 'A' + 10;
X                else
X                    digit = *str - '0';
X        }
X        n = base * n + digit;
X    }
X    return (n);
X}
X
X/*------------------------------------------------------------------*
X *
X *  esc -- map *str into escaped character, return pointer to
X *         next character.
X *
X *------------------------------------------------------------------*/
X
Xchar *
Xesc(str)
Xregister char *str;
X{
X    int n, c;
X    register char *p;
X
X    if (*str != ESCAPE)
X        return(++str);
X    else if (*(p=(str+1)) == ENDSTR)                /* ESCAPE not special at end */
X        return(++str);
X    else {
X        if (*p == 'n' || *p == 'N') {
X            *str = '\n';
X            return(strcpy(++str,++p));
X        }
X        else if (*p == 't' || *p == 'T') {
X            *str = '\t';
X            return(strcpy(++str,++p));
X        }
X        else if (*p == 'b' || *p == 'B') {
X            *str = '\b';
X            return(strcpy(++str,++p));
X        }
X        else if (*p == 'r' || *p == 'R') {
X            *str = '\r';
X            return(strcpy(++str,++p));
X        }
X        else if (*p == '0' && (*(p+1) == 'x' || *(p+1) == 'X')) {
X            p += 2;
X            *str = 0xff & atoib(p,16);
X            return(strcpy(++str,p+=2));
X        }
X        else if (*p >= '0' && *p <= '7') {
X            for (n=0,c=0; (*p >= '0' && *p <= '7') && n <= 3; ++n, ++p)
X                c = (c<<3)+(*p-'0');
X            *str = c;
X            return(strcpy(++str,p));
X        }
X        else {
X            *str = *p;
X            return(++p);
X        }
X    }
X}
X
X/*------------------------------------------------------------------*
X *
X *  dodash -- expand set at src[i] into dest[j], stop at delim.
X *
X *------------------------------------------------------------------*/
X
Xdodash(delim,srcp, stdest, destp)
Xregister char **srcp;
Xchar **destp, *stdest, delim;
X{
X    register char *src;
X    char *cp, *tmp;
X    int k;
X
X    src = cp = *srcp;
X    while (*src != delim && *src != ENDSTR) {
X        if (*src == ESCAPE) {
X            tmp = esc(src);
X            addpat(*src, stdest, destp);
X            src = tmp - 1;
X        }
X        else if (*src != DASH)
X            addpat(*src, stdest, destp);
X        else if (src == cp || src[1] == ENDSTR)
X            addpat(DASH, stdest, destp);
X        else if (isalnum(*(src-1)) && isalnum(src[1]) && *(src-1) <= src[1]) {
X            for (k = *(src-1)+1; k <= src[1]; ++k)
X                addpat(k, stdest, destp);
X            ++src;
X        }
X        else
X            addpat(DASH, stdest, destp);
X        ++src;    
X    }
X    *srcp -= *srcp - src;
X}
X
X/*------------------------------------------------------------------*
X *
X *  makesub -- make substitution string from arg in sub
X *
X *------------------------------------------------------------------*/
X
Xchar *
Xmakesub(arg, delim, sub)
Xregister char *arg;
Xchar delim, *sub;
X{
X    char *stsub, *tmp;
X
X    stsub = sub;
X    while (*arg != delim && *arg != ENDSTR && *arg != '\n') {
X        if (*arg == '&') {
X            addpat(DITTO,stsub,&sub);
X            addpat('0',stsub, &sub);
X        }
X        else if (*arg == ESCAPE && arg[1] > '0' && arg[1] <= '9') {
X            addpat(DITTO,stsub,&sub);
X            addpat(arg[1], stsub, &sub);
X            ++arg;
X        }
X        else {
X            tmp = esc(arg);
X            addpat(*arg, stsub, &sub);
X            arg = tmp - 1;
X        }
X        ++arg;
X    }
X    if (*arg != delim)
X        return(NULL);               /* Missing delimiter */
X    if (!addpat('\0',stsub, &sub))
X        return(NULL);               /* no space for sub string */
X    else
X        return(arg);
X}
X
X/*------------------------------------------------------------------*
X *
X *  subst -- substitute "sub" for occurrences of pattern.
X *
X *------------------------------------------------------------------*/
X
Xsubst(ln, tr)
Xint ln;
Xstruct trfmn *tr;
X{
X    register struct lkline *oo, *nn;
X    char *newline();
X    int line, n;
X
X    lparen = rparen = 0;
X    nn = tr->new;
X    oo = tr->old;
X    for (n = tr->nold; n > 0; --n) {
X        if ((line = ln - n + 1) < 0)
X            line += maxlines;           /* buffer warp around */
X        if (n <= tr->nold - tr->nnew) {
X            delln(line);                /* not enough replacements */
X            oo = oo->nextln;
X        }
X        else {
X            if (!subline(line, oo->ln, nn->ln))
X                return(FALSE);
X            else {
X                oo = oo->nextln;
X                nn = nn->nextln;
X            }
X        }
X    }
X    for (n=tr->nnew; n > tr->nold; --n) {           /* more lines to add */
X        newline("\n");
X        if (!subline(curln, BOLSTR, nn->ln))
X            return(FALSE);
X        else
X            nn = nn->nextln;
X    }
X    return(TRUE);
X}
X
X/*------------------------------------------------------------------*
X *
X *  subline -- do substitution on one line.
X *
X *------------------------------------------------------------------*/
X
Xsubline(line, pat, sub)
Xint line;
Xchar *pat, *sub;
X{
X    char new[MAXSTR];
X    register char *p1;
X    char *p2, *p3, *last, *pp;
X    int subbed;
X
X    p2 = new;
X    subbed = FALSE;
X    last = NULL;
X    p1 = linep[line];
X    while (*p1 != ENDSTR) {
X        if (!subbed) {
X            pp = pat;
X            p3 = amatch(linep[line], p1, &pp);
X        }
X        else
X            p3 = NULL;
X        if (p3 != NULL && last != p3) {
X            subbed = TRUE;              /* replace matched text */
X            catsub(p1, p3, sub, new, &p2);
X            last = p3;
X        }
X        if (p3 == NULL || p3 == p1) {
X            addpat(*p1, new, &p2);
X            ++p1;
X        }
X        else        /* skip matched text */
X            p1 = p3;
X    }
X    if (subbed) {
X        if (!addpat(ENDSTR,new,&p2)) {
X            return(FALSE);
X        }
X        else { /* copy newline into linep array. */
X            free(linep[line]);
X            if ((linep[line] = xalloc(strlen(new)+1)) == NULL)
X                error("not enough memory");
X            strcpy(linep[line], new);
X        }
X    }
X    return(subbed);
X}
X
X/*------------------------------------------------------------------*
X *
X *  catsub -- add replacement text to end of new.
X *
X *------------------------------------------------------------------*/
X
Xcatsub(p1, p2, sub, new, pp3)
Xchar *p1, *p2, *sub, *new, **pp3;
X{
X    register char *p, *q;
X
X    for (p = sub; *p != ENDSTR; ++p) {
X        if (*p == DITTO) {
X            if (p[1] > '0' && p[1] <= '9') {
X                p1 = gstart[p[1]-'0'];
X                p2 = gend[p[1]-'0'];
X            }
X            for (q = p1; q < p2; ++q) {
X                addpat(*q, new, pp3);
X            }
X            ++p;
X        }
X        else {
X            addpat(*p, new, pp3);
X        }
X    }
X}
X
X/*------------------------------------------------------------------*
X *
X *  delln -- delete a line.
X *
X *------------------------------------------------------------------*/
X
Xdelln(ln)
Xregister int ln;
X{
X    free(linep[ln]);
X    linep[ln] = NULL;
X}
X
/
---------------------------end here---------------------------------------

Monty Walls
MIS Division, Tech. Support
Oklahoma Tax Commission
2501 N. Lincoln
OKC, OK, 73194 
ger(pat)Jacpat(A);
X        }
- 1<= 'e;
le  (*amatch -= ENDp Xchard 6-'ter ein; ppa= Domatcparen;
pat') {"i/*----sub
pos))
XEND);
X        }ize('))lse {
p =X        eparen !te( *stX        elsea= suarg[h)
X *<= selse
X.
X *
X *- != de= Creturn     rrc-RPPressiX           at grou          urn sw == 'Ntenurn(rec= END