From bacchus.pa.dec.com!decwrl!apple!usc!cs.utexas.edu!uunet!allbery Mon Jun 18 20:16:08 PDT 1990
Article 1654 of comp.sources.misc:
Path: bacchus.pa.dec.com!decwrl!apple!usc!cs.utexas.edu!uunet!allbery
From: fitz@mml0.meche.rpi.edu (Brian Fitzgerald)
Newsgroups: comp.sources.misc
Subject: v13i059: numeqn - Troff Equation Numbering Package
Message-ID: <93914@uunet.UU.NET>
Date: 19 Jun 90 02:20:24 GMT
Sender: allbery@uunet.UU.NET
Lines: 1689
Approved: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc)

Posting-number: Volume 13, Issue 59
Submitted-by: fitz@mml0.meche.rpi.edu (Brian Fitzgerald)
Archive-name: numeqn/part01

     I am submitting numeqn.c, after having obtained permission from
the author, Bruce R. Musicus, of MIT, to whom I am grateful for
releasing this invaluable text processing tool to the public domain.

Brian Fitzgerald

DESCRIPTION
     Numeqn is a general purpose automatic numbering  filter  for
     troff,  useful primarily for numbering equations, but power-
     ful enough to number anything else  as  well.   Numbers  are
     formed  from ASCII characters, portions of the header number
     (from  .NH  or  .sh),  and  one  of  10  different  sequence
     counters,  with optional subscripts.  Equations are numbered
     by including a special "generate number"  argument  for  the
     .EQ  equation  macro.   Any  of  these  numbers  may also be
     inserted at any point in the text by using  the  .E#  macro.
     These  numbers may be assigned to numeqn variables, and then
     later used elsewhere in the text.  Furthermore, very  flexi-
     ble  format  control is available via a mechanism similar to
     that used by "printf" in the standard I/O library.

     To minimize the quantity of data passed  through  the  troff
     filters,  numeqn should typically used before tbl , eqn , or
     troff. For example:

          refer file | numeqn | tbl | eqn | troff -ms >wowee
 
#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  Makefile numeqn.c numeqn.nr
# Wrapped by fitz@mml0.meche.rpi.edu on Mon Jun 18 13:58:09 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(256 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
XCFLAGS=	-O
XLDFLAGS=-s
XPROG=	numeqn
XDOC=	numeqn.nr
XBINDIR=	/usr/local/bin
XMANDIR=	/usr/local/man
XMANEXT=	1
XMAN=	$(PROG).$(MANEXT)
X
Xall: $(PROG)
X
Xinstall:
X	install $(PROG) $(BINDIR)
X
Xman:
X	install $(DOC) $(MANDIR)/man$(MANEXT)/$(MAN) 
X
Xclean:
X	$(RM) $(PROG)
END_OF_FILE
if test 256 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
if test -f 'numeqn.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'numeqn.c'\"
else
echo shar: Extracting \"'numeqn.c'\" \(33452 characters\)
sed "s/^X//" >'numeqn.c' <<'END_OF_FILE'
X/*    numeqn - automatic equation numbering filter for troff */
X     
X/* Bruce Musicus, June 27, 1981 */
X/* Revised brm, May 14, 1983 */
X/*   from Research Laboratory of Electronics, MIT, Cambridge, MA 02139 */
X     
X/* Copyright Musicus 1990.  You may use this package for your personal    */
X/*   use, but you may not distribute it commercially, nor embed it in a   */
X/*   commercial product without approval by Bruce Musicus                 */
X
X/* EMACS_MODES: c, fill */
X     
X/* numeqn is a general purpose automatic numbering filter for troff, useful
Xprimarily for numbering equations, but powerful enough to number anything
Xelse as well.  Numbers are formed from ASCII characters, portions of the
Xheader number (from .NH or .sh), and one of 10 different sequence counters,
Xwith optional subscripts.
X     
XRecognized troff macros:
X     
X    .EQ [L,I,C] [value,#,##,##+,#tag,##tag,##+tag] [=tag] [indent]
X     
X    .E# [value,#,##,##+,#tag,##tag,##+tag] [=tag] [text]
X     
X    .NH [level]        - for ms macro package
X     
X    .sh level title [numbers ...]    - for me macro package
X     
XIf an ASCII string is specified as the equation number (.EQ), then it will
Xprinted as specified without any processing.  The # character signals
Xnumeqn that it should automatically generate a number.  Specifying # by
Xitself will use the next sequential number using the default format string.
XSpecifying ## uses the next sequential subscripted number with the
Xdefault format.  A ##+ indicates that the equation number should be
Xincremented, and the subscript 'a' should be appended.
X     
XThe resulting equation number may be optionally stored in a variable called
X"tag" by adding the argument "=tag" following the equation number
Xspecification.  This equation can then be referred to later in the text
Xby using the macro ".E# #tag".  Numeqn will replace this line with the
Xnumber of the tagged equation.  Immediately following the number,
Xwithout an intervening space, will be any text following the arguments
Xto the .E# macro.  This feature is convenient for placing punctuation
Ximmediately following the number.
X     
XIn general, the argument #tag to either .E# or .EQ indicates that the
Xcontents of the variable "tag" is to be used as the number.  Note also that the
Xarguments #, ##, and ##+ can also be specified to .E#, in which case an
Xappropriate new number will be generated and inserted in place of the .E#
Xmacro.  This new number may be assigned to a tag variable in exactly the
Xsame manner as for .EQ by adding the argument "=tag".  If a number is
Xassigned to a tag variable by .E#, however, it will NOT be inserted in
Xthe text and any characters following the =tag argument will be ignored.
XThis feature allows setting tag variables without inserting the number
Xin the text.
X     
XBoth .E# and .EQ set the troff string variable E# to the number they generate.
XThis makes it convenient to embed the number in complicated troff
Xconstructions by referring to: \*(E#
X     
XNumeqn allows extremely flexible control over the formatting of the
Xnumbers it generates by using a mechanism similar to that used by
X"printf" in the standard I/O package.  The variable "format" is an ASCII
Xstring which specifies the default format used for all numbers generated
Xby using the #, ## and ##+ arguments to .E# or .EQ.  Ordinary characters
Xin this format string will be printed unchanged in every number.
XInformation such as the section header and sequence number may be
Xspecified using a sequence of characters starting with a '%'.  The
Xfollowing commands are recognized:
X     
X    %h    - insert the complete header number at this point.
X     
X    %nh    - (1<=n<=5) insert only levels 1 through n of the header
X        number at this point.
X     
X    %s    - insert the next sequential value of counter 0.
X     
X    %ns    - (0<=n<=9) insert the next sequential value of counter n.
X     
X    %n.ms    - (0<=n<=9 and 0<=m<=5) insert the next sequential value of
X        counter n, and make sure this counter is reset to 0 each time
X        a new section header at level m or below is created.  If m=0,
X        the counter will never reset.
X     
X    %%    - insert the character '%'
X     
XThe default format is (%2h%0.2s), which will produce numbers like (3.5.6) or
X(3.5.6c) in section 3.5 for the sixth item (third subscripted number.)
XSequence counters will be reset to 0 whenever a .NH command (or .sh
Xcommand for the -me macro package) is encountered which would change the
Xpart of the header number which is used in forming the equation number.
XThis rule may be changed by using the full %n.ms specification.
XThe default format may be changed, for example, by:
X    .E# (A:%h%s) =format
Xwhich would set the default format to print numbers in the form: (A:3.5.6)
X     
XThe arguments #tag, ##tag, ##+tag to .EQ and .E# actually allow using the
Xcontents of a tag variable to specify the for this number.  If the tag
Xvariable contains a string with no '%' characters, then its contents
Xwill be used unmodified as the number to be inserted.  If '%h' or '%s'
Xarguments are in the tag variable, then the header and sequence number
Xwill be inserted appropriately, with the sequence and subscripts
Xadvanced as specified.  Thus, for example, we could define a numbering
Xformat T for tables by:
X    .E# %1h%2s =T
XNo text is generated for this line.  After this, however, a new table
Xnumber could be generated by:
X    .E# #T
Xwhich will insert a number such as: 3.4  if we are in section 3 and this
Xis the fourth table.  Note that the T format uses sequential counter
Xnumber 2, and therefore tables will be numbered independently of
Xequations, which will use the default format and sequential counter
Xnumber 0.
X     
XTag variable names may be any ASCII string.  To include spaces or tabs in
Xthe name, simply enclose the name in quotes.  Certain names are reserved
Xand have special meaning:
X     
X    format    - contains the default numbering format string.  Setting
X        this variable changes the default numbering style.
X     
X    %h    - contains the present header number.  To change the header
X        level, use something like:  ".E# 2.5 =%h" which sets the
X        current header to 2.5.
X     
X    %nh    - (1<=n<=5) contains the header number truncated to
X        levels 1 through n.
X     
X    %s    - contains the last sequence number used on sequence counter
X        0.  Changing this variable by setting it to:  ".E# 5c =%s"
X        would change the next sequential number to 5c.
X     
X    %ns    - (0<=n<=9) contains the last sequence number used on
X        sequence counter n.
X     
XTag variable names formed solely out of digits are considered "local",
Xand may be redefined at any time.  Tag names which include alphabetic
Xcharacters and/or punctuation are considered "global", and must not be
Xredefined.
X     
XVarious options are available on the command line:
X     
X    -f string    use "string" as the default numbering format
X    -h level    use "level" as the initial header number (this also
X              affects the first .NH command
X    -s number    use "number" as the initial number in counter 0
X    -t tagfile    when finished, output a list of all tag variable
X              values to "tagfile"
X    -ms        -ms macro package format (default)
X    -me        -me macro package format
X     
XThe -t option constructs an output file containg lines of the form:
X    .E# (3.5.6c) =tag
Xfor every tag variable name which is defined in the file.  This is
Xconvenient, for example, when running off very long text files in sections.
XSimply include the tag file from the previous section as the first text
Xfile when troffing the next section - this will correctly define all the
Xtag variables, and will allow referring to equation numbers from
Xprevious sections.  A similar trick also allows forward referencing of
Xequation numbers - run numeqn on the file once with the -t option in
Xorder to collect all the tag variable definitions in a tagfile.  Then
Xrun the tagfile followed by the same text file through numeqn again.
XNow single level forward references can be resolved.
X     
X*******************
X     
XBUGS -
X   Each sequence counter should be used in only one format string.  If a
Xcounter must be shared between several formats, be sure that all use
Xexactly the same number of header levels.  Otherwise, the sequence
Xcounter will reset according to the last defined format string header level.
X     
X     
X*/
X     
X     
X/* #define DEBUG 1 */
X     
X     
X     
X/* Global Variables */
X     
X#include <stdio.h>
X#include <ctype.h>
X     
X#define max(x, y)    (((x) < (y)) ? (y) : (x))
X#define mabs(x)        ((x < 0) ? -(x) : (x))
X#define min(x, y)    (((x) < (y)) ? (x) : (y))
X     
Xchar *malloc(),*realloc(),*calloc() ;
Xchar *skipsp(),*skipqt(),*eval(),*getstr(),*getstrq(),*gethead(),*newhead() ;
Xchar *getseq(),*newseq() ;
Xint parse(),findtag(),getline() ;
X     
X/* Global variables */
X     
Xchar buff[100] ;    /* junk string buffer */
X     
X#define LINLEN 600
Xchar chline[LINLEN+2] ;    /* input line buffer */
X     
Xint argflag = 0 ;    /* flags if any file names found on command line */
Xint meflag = 0 ;    /* flags -me option */
X     
Xchar *tname = NULL;  /* tagfile name (=0 if no -t option) */
X     
Xchar *fname = "stdin" ;    /* name of file being processed */
Xint fline ;        /* line number of file being processed */
X     
X#define NUMLEN 80
Xchar cnum[NUMLEN+1] ;    /* buffer for evaluated number */
X     
X#define TAGLEN 80
Xchar ctag[TAGLEN+1] ;    /* buffer for tag name */
X     
Xchar **taglist ;    /* pointer to list of pointers to tag,value pairs */
X            /*   list organized alphabetically */
Xunsigned ntags ;    /* count of defined tag variables */
Xunsigned maxtags ;    /* allocated number of tags */
X     
X#define FORMAT '\001'
X#define HEADER '\002'
X#define SEQUENCE '\014'
X     
X#define FORMLEN 80
Xchar cform2[FORMLEN+1] = "(%2h%0.2s)" ;  /* ASCII default format */
Xchar cform[FORMLEN+1] = "(\004\014)" ;   /* processed default format */
X     
X#define HNUM 6
Xint hlev[HNUM] = {
X    0, 0, 0, 0, 0, 0 } ;    /* header number at each level */
Xint nh = 1 ;        /* present level */
Xint nhflag = 0 ;    /* flags whether header variable %h has been set */
X     
X#define SNUM 10
Xstruct {
X    int no,sub,lev ;
X} seq[SNUM] = {
X    { 0, 0, 2 },
X    { 0, 0, 0 },
X    { 0, 0, 0 },
X    { 0, 0, 0 },
X    { 0, 0, 0 },
X    { 0, 0, 0 },
X    { 0, 0, 0 },
X    { 0, 0, 0 },
X    { 0, 0, 0 },
X    { 0, 0, 0 } } ;    /* sequence counters, subscripts, reset level */
X     
Xchar *string[] = {    /* predefined variables */
X    "%0h\0\002",
X    "%0s\0\014",
X    "%1h\0\003",
X    "%1s\0\015",
X    "%2h\0\004",
X    "%2s\0\016",
X    "%3h\0\005",
X    "%3s\0\017",
X    "%4h\0\006",
X    "%4s\0\020",
X    "%5h\0\007",
X    "%5s\0\021",
X    "%6h\0\010",
X    "%6s\0\022",
X    "%7h\0\011",
X    "%7s\0\023",
X    "%8h\0\012",
X    "%8s\0\024",
X    "%9h\0\013",
X    "%9s\0\025",
X    "%h\0\002",
X    "%s\0\014",
X    "format\0\001",
X    ""
X} ;
X     
X/* MAIN PROGRAM */
X/* checks for options, picks off file names, calls "doline" to do the work,
X   prints the tag,value pairs at end if -t option used  */
X     
Xmain(argc,argv)
Xint argc ;
Xchar **argv ;
X{
X    char c ;
X     
X    init() ;    /* initialize variables and tag storage */
X     
X    while(--argc>0) {
X        if(**++argv == '-') {
X#ifdef DEBUG
Xfprintf(stderr,"switch %c\n",argv[0][1]) ;
X#endif
X            switch(c = argv[0][1]) {
X            case 'f':    if(--argc>0) {
X                        strcpy(cform2,*++argv) ;
X                        newform(cform,cform2) ;
X                    } else {
X                        erropt(c) ;
X                    }
X                    break ;
X     
X            case 'h':    if(--argc>0) {
X                        newhead(0,*++argv) ;
X                    } else {
X                        erropt(c) ;
X                    }
X                    break ;
X     
X            case 's':    if(--argc>0) {
X                        newseq(0,*++argv) ;
X                    } else {
X                        erropt(c) ;
X                    }
X                    break ;
X     
X            case 't':    if(--argc>0) {
X                        tname = *++argv ;
X                    } else {
X                        erropt(c) ;
X                    }
X                    break ;
X     
X            case 'm':    if(argv[0][2]=='e') meflag = 1 ;
X                    break ;
X     
X            default:    fprintf(stderr,
X                      "numeqn: unknown option -%c\n",c) ;
X                    break ;
X            }
X        } else {
X            fname = *argv ;
X#ifdef DEBUG
Xfprintf(stderr,"file %s\n",fname) ;
X#endif
X            argflag++ ;
X            if(freopen(fname,"r",stdin)==NULL) {
X                fprintf(stderr,
X                  "numeqn:  failed to open file    %s\n",fname) ;
X            } else {
X                dofile() ;
X            }
X        }
X    }
X    if(argflag==0) {  /* if no files specified, read stdin */
X#ifdef DEBUG
Xfprintf(stderr,"file stdin") ;
X#endif
X        dofile() ;
X    }
X     
X/* At end, print all tag,value pairs if -t option */
X     
X    if(tname != NULL) {
X        if(freopen(tname,"w",stdout)==NULL) {
X            fprintf(stderr,
X              "numeqn:  failed to open tag file %s\n",tname) ;
X            exit(1) ;
X        } else {
X            prtags() ;
X        }
X    }
X     
X    exit(0) ;
X}
X     
X     
X/* Prints option error message */
Xerropt(c)
Xchar c ;
X{
X    fprintf(stderr,"numeqn:  mising -%c option argument\n",c) ;
X    return ;
X}
X     
X     
X/* Read file line by line, looking for lines beginning with '.'
X   Calls handlers for ".EQ",".E#",".NH"
X   Otherwise outputs the line and reads the next until reaches EOF
X*/
Xdofile()
X{
X    char *p ;
X     
X    fline = 0 ;
X     
X    while(getline(chline,LINLEN,stdin) > 0) {
X#ifdef DEBUG
Xfprintf(stderr,"getline:  %s",chline) ;
X#endif
X        if(*chline=='.') {
X            p = skipsp(chline+1) ;
X            if(*p=='E' && *(p+1)=='Q') {
X#ifdef DEBUG
Xfprintf(stderr,".EQ found\n") ;
X#endif
X                parseq(p+2) ;
X            } else if (*p=='E' && *(p+1)=='#') {
X#ifdef DEBUG
Xfprintf(stderr,".E# found\n") ;
X#endif
X                parscmd(p+2) ;
X            } else if (meflag==0 && *p=='N' && *(p+1)=='H') {
X#ifdef DEBUG
Xfprintf(stderr,".NH found\n") ;
X#endif
X                parsnh(p+2) ;
X            } else if (meflag==1 && *p=='s' && *(p+1)=='h') {
X#ifdef DEBUG
Xfprintf(stderr,".sh found\n") ;
X#endif
X                parssh(p+2) ;
X            } else {
X                fputs(chline,stdout) ;
X            }
X        } else {
X            fputs(chline,stdout) ;
X        }
X    }
X    return ;
X}
X     
X     
X/* Reads next text line up to newline or up to n chars from stdin
X   Increments fline line counter
X   Handles invisible newline correctly (even in troff comment)
X   Returns count of characters read
X*/
Xint getline(p,n,fp)
Xchar *p ;
Xregister int n ;
XFILE *fp ;
X{
X    char *p2 = p ;
X    register int c ;
X    int c2 ;
X    int nlflag = 0 ;
X     
X    while(--n>0 && (c=getc(fp))!=EOF) {
X        if(!nlflag && c=='\\') {
X            switch (c2=getc(fp)) {
X     
X            case '\n':    fline++ ; /* invisible newline */
X                    break ;
X     
X            case '\"':    nlflag = 1 ; /* troff comment */
X     
X            default:    *p++ = c ;
X                    *p++ = c2 ;
X                    --n ;
X                    break ;
X            }
X        } else {
X            if((*p++ = c)=='\n')
X                break ;
X        }
X    }
X    fline++ ;
X    *p = '\0' ;
X    return(p-p2) ;
X}
X     
X     
X/* Parse .EQ macro command
X   Skips L,I,C argument if there
X   Calls parse() to create number, set tag, and set troff string variable E#
X   Outputs new .EQ macro command with correct number inserted
X*/
Xparseq(p)
Xchar *p ;
X{
X    char *pl,*pr ;
X     
X    pr = pl = skipsp(p) ;
X    if((*pl=='L' || *pl=='I' || *pl=='C')
X                && (isspace(*(pl+1)) || *(pl+1)=='\0')) {
X        pr = pl = skipsp(pl+1) ;
X    }
X    parse(&pr) ;
X    p = chline-1 ;
X    while(++p<pl) {
X        putchar(*p) ;
X    }
X    if(*cnum) fputs(cnum,stdout) ;
X    while(*pr!='\0') {
X        putchar(*pr) ;
X        pr++ ;
X    }
X    return ;
X}
X     
X     
X/* Parse .E# macro command
X   Call parse() to calculate number, set tag, set troff string variable E#
X   Any strings following the value, which do not start with '=', will be
X     appended directly after the number without any space.
X   If no tag set, then outputs number
X*/
Xparscmd(p)
Xchar *p ;
X{
X    p = skipsp(p) ;
X    if(!parse(&p)) {
X        if(*cnum) fputs(cnum,stdout) ;
X        p = skipsp(p) ;
X        if(*p) fputs(p,stdout) ;
X    }
X    return ;
X}
X     
X     
X/* Parse .NH macro command
X   If header level has been changed by setting the tag %h or %nh, first
X     outputs a set of troff commands to effectively execute most of an .NH
X     command to set the level.
X   Updates the header numbers properly.
X   Resets all sequence numbers whose header level is less than or equal
X     to this level.
X   Outputs the .NH line unchanged.
X*/
Xparsnh(p)
Xchar *p ;
X{
X    int i ;
X     
X    if(nhflag) {        /* level has been changed in numeqn */
X        nhflag = 0 ;
X        printf(".RT\n.if \\\\n(1T .sp 1\n.if !\\\\n(1T .BG\n.RT\n") ;
X                /* initializes if this is first .NH */
X        printf(".nr NS %d\n",nh) ;
X                /* Sets .NH header level */
X        for(i=1 ; i<HNUM ; i++)
X            printf(".nr H%d %d\n",i,hlev[i]) ;
X                /* Sets individual level registers */
X    }
X     
X    p = skipsp(p) ;
X    nh = -1 ;
X    if(isdigit(*p) && *p < HNUM+'0') {
X        nh = (*p++) - '0' ;
X    }
X    if(nh==0) {
X        nh = 1 ;
X        hlev[1] = 0 ;
X    } else if(nh < 0) {
X        nh = 1 ;
X    }
X     
X    hlev[nh]++ ;
X    for(i=nh+1 ; i<HNUM ; i++)
X        hlev[i] = 0 ;
X     
X    for(i=0 ; i<SNUM ; i++) {    /* reset sequence counters */
X        if(seq[i].lev >= nh)
X            seq[i].no = seq[i].sub = 0 ;
X    }
X     
X    fputs(chline,stdout) ;
X    return ;
X}
X     
X     
X/* Parse .sh macro command (-me macro package)
X   Resets all sequence numbers whose header level is less than or equal
X     to this level.
X   Outputs the .sh line unchanged.
X*/
Xparssh(p)
Xchar *p ;
X{
X    int i,j ;
X     
X    p = skipsp(p) ;
X    if(isdigit(*p) && *p < HNUM+'0') {
X        nh = (*p++) - '0' ;
X        if(nh==0) {
X            nh = 1 ;
X            hlev[1] = 0 ;
X        } else if(nh < 0) {
X            nh = 1 ;
X        }
X        while(*p && !isspace(*p)) ++p ;
X    }
X    hlev[nh]++ ;
X    for(i=nh+1 ; i<HNUM ; i++)
X        hlev[i] = 0 ;
X     
X    for(i=0 ; i<SNUM ; i++) {    /* reset sequence counters */
X#ifdef DEBUG
Xfprintf(stderr,"parssh: seq[%d].lev=%d, nh=%d\n",i,seq[i].lev,nh) ;
X#endif
X        if(seq[i].lev >= nh)
X            seq[i].no = seq[i].sub = 0 ;
X    }
X     
X    p = skipsp(p) ;
X#ifdef DEBUG
Xfprintf(stderr,"parssh: before title %s\n",p) ;
X#endif
X    p = skipqt(p) ;
X    p = skipsp(p) ;
X    i = 1 ;
X    while(i<HNUM && (isdigit(*p) || *p=='-')) {
X#ifdef DEBUG
Xfprintf(stderr,"parssh: explicit section numbers %s\n",p) ;
X#endif
X        if(*p != '-') {
X#ifdef DEBUG
Xfprintf(stderr,"parssh: reset at section level %d\n",i) ;
X#endif
X            hlev[i] = atoi(p) ;
X            for(j=0 ; j<SNUM ; j++) {
X                if(seq[j].lev>=i)
X                    seq[j].no = seq[j].sub = 0 ;
X            }
X        }
X        i++ ;
X        p = skipqt(p) ;
X        p = skipsp(p) ;
X    }
X     
X    fputs(chline,stdout) ;
X    return ;
X}
X     
X     
X/* Skips spaces and tabs */
Xchar *skipsp(p)
Xchar *p ;
X{
X    while(*p=='\t' || *p==' ') ++p ;
X    return(p) ;
X}
X     
X     
X/* Skip text or quoted text */
Xchar *skipqt(p)
Xchar *p ;
X{
X    char delim ;
X    if(*p=='\'' || *p=='\"') {
X        delim = *p ;
X        while(*++p) {
X            if(*p=='\\') {
X                ++p ;
X            } else if(*p==delim) {
X                return(p+1) ;
X            }
X        }
X    } else {
X        while(*p && !isspace(*p)) ++p ;
X    }
X    return(p) ;
X}
X     
X     
X/* Parses value and =tag fields, sets tag variable, handles special tag
X     variables, adds new tags to list, output troff command to set E#
X   Calls eval() to evaluate number
X   Returns 1 if detects "=tag" argument, else returns 0
X*/
Xint parse(pp)
Xchar **pp ;
X{
X    int fndtag = 0 ;
X    char *p = *pp ;
X    char **ptag,*pval ;
X     
X    p = eval(p) ;
X    p = skipsp(p) ;
X    if(*p=='=') {
X        fndtag = 1 ;
X        p = skipsp(p+1) ;
X        p = getstr(p,ctag,TAGLEN) ;
X        if(findtag(ctag,&pval,&ptag)) {
X            if(*pval==FORMAT) {
X                strcpy(cform2,cnum) ;
X                newform(cform,cnum) ;
X            } else if(*pval>=HEADER && *pval<HEADER+10) {
X                if(meflag==0) newhead(*pval-HEADER,cnum) ;
X                else error("Cannot set variable %%%dh",
X                                *pval-HEADER) ;
X            } else if(*pval>=SEQUENCE && *pval<SEQUENCE+10) {
X                newseq(*pval-SEQUENCE,cnum) ;
X            } else if(strcmp(cnum,pval)!=0) {
X                pval = ctag ;
X                while(*pval) {
X                    if(isdigit(*pval++)) continue ;
X                    else {
X                        error("duplicate tag %s",ctag);
X                        break ;
X                    }
X                }
X                reptag(ctag,cnum,ptag) ;
X            }
X        } else {
X            addtag(ctag,cnum,ptag) ;
X        }
X    }
X    if(*cnum) printf(".ds E# \"%s\n",cnum) ;
X    *pp = p ;
X    return(fndtag) ;
X}
X     
X     
X/* Evaluates number
X   Handles explicit value, #, ##, ##+, #tag, ##tag, ##+tag
X   Handles special tag variables also
X   Returns pointer to next char after value
X   Puts evaluated number in "cnum"
X*/
Xchar *eval(p)
Xchar *p ;
X{
X    int sub = 1 ;
X    char delim = '\0' ;
X    char *pform, **ptag ;
X     
X    p = skipsp(p) ;
X    if(*p=='\"' || *p=='\'')
X        delim = *p++ ;
X     
X    if(*p=='#') {
X        if(*++p=='#') {
X            sub = 2 ;
X            if(*++p=='+') {
X                sub = 3 ;
X                ++p ;
X            }
X        }
X        if(delim=='\0')
X            p = getstr(p,ctag,TAGLEN) ;
X        else
X            p = getstrq(p,ctag,TAGLEN,delim) ;
X        if(*ctag=='\0') {
X            form(cform,sub) ;
X        } else {
X            if(!findtag(ctag,&pform,&ptag)) {
X                error("tag %s not found",ctag) ;
X                sprintf(cnum,"(???%s???)",ctag) ;
X            } else {
X                if(*pform==FORMAT) {
X                    form(cform,sub) ;
X                } else if(*pform>=HEADER && *pform<HEADER+10) {
X                    gethead(*pform-HEADER,cnum) ;
X                } else if(*pform>=SEQUENCE &&
X                            *pform<SEQUENCE+10){
X                    getseq(*pform-SEQUENCE,cnum,0) ;
X                } else {
X                    newform(buff,pform) ;
X                    form(buff,sub) ;
X                }
X            }
X        }
X    } else {
X        if(delim=='\0')
X            p = getstr(p,cnum,NUMLEN) ;
X        else
X            p = getstrq(p,cnum,NUMLEN,delim) ;
X    }
X    return(p) ;
X}
X     
X     
X/* Get string at p into pdest up to nmax chars, until space character
X   Terminate with null
X*/
Xchar *getstr(p,pdest,nmax)
Xchar *p,*pdest ;
Xint nmax ;
X{
X    if(*p=='\"' || *p=='\'')
X        return(getstrq(p+1,pdest,nmax,*p)) ;
X    while(!isspace(*p) && *p!='\0' && --nmax>0)
X        *pdest++ = *p++ ;
X    if(nmax<=0)
X        error("string too long",p) ;
X    *pdest = '\0' ;
X    return(p) ;
X}
X     
X/* Get string at p into pdest up to nmax chars, up to delim character.
X   Terminate with null
X*/
Xchar *getstrq(p,pdest,nmax,delim)
Xchar *p,*pdest,delim ;
Xint nmax ;
X{
X    while(*p!=delim && *p!='\n' && *p!='\0' && --nmax>0)
X        *pdest++ = *p++ ;
X    if(nmax <= 0) {
X        error("string too long",p) ;
X    } else if(*p==delim) {
X        ++p ;
X    } else {
X        error("imbalanced quotes",p) ;
X    }
X    *pdest = '\0' ;
X    return(p) ;
X}
X     
X     
X/* Evaluate format string, substituting in header and generating next sequence
X     number as specified
X   Uses format string processed by newform()
X   Calls getseq() and gethead() to do the work
X   Inputs: p=pointer to format string, seqflag=0 if no increment, =1 if
X     increment number, =2 if increment subscript, =3 if increment
X     number and set subscript to 'a'.
X*/
Xform(p,seqflag)
Xchar *p ;
Xint seqflag ;
X{
X    char *pnum = cnum ;
X     
X    while(*p) {
X#ifdef DEBUG
Xfprintf(stderr,"entering form:  hlev[1]=%d, hlev[2]=%d, seq[0]=%d,%d\n",
X    hlev[1],hlev[2],seq[0].no,seq[0].sub) ;
X#endif
X        if(*p>=SEQUENCE+10) {
X            *pnum++ = *p++ ;
X        } else if(*p>=SEQUENCE && *p<SEQUENCE+10) {
X            pnum = getseq((*p++)-SEQUENCE,pnum,seqflag) ;
X        } else if(*p>=HEADER && *p<HEADER+10) {
X            pnum = gethead((*p++)-HEADER,pnum) ;
X        }
X    }
X    *pnum = '\0' ;
X    return ;
X}
X     
X     
X/* Converts ASCII format string into internal format which can be processed
X     more conveniently (this scheme is intended primarily for
X     speeding up processing of the default format string.  It will slow down
X     processing of other format strings.)
X   Interprets %h, %nh, %s, %ns, %n.ms, %% correctly.  If sequence counter
X     specified, calculates header level at which counter should be reset
X   Inputs: p=pointer ASCII to string, pform=pointer to processed format
X*/
Xnewform(pform,p)
Xchar *pform,*p ;
X{
X    int hmax = 0 ;
X    int sflag = -1 ;
X    int n,m ;
X     
X    while(*p) {
X        if(*p!='%') {
X            *pform++ = *p++ ;
X        } else {
X            n = -1 ;
X            m = -1 ;
X            ++p ;
X            if(isdigit(*p)) {
X                n = (*p++)-'0' ;
X            }
X            if(*p=='.') {
X                if(isdigit(*++p)) m = (*p++)-'0' ;
X            }
X            switch(*p) {
X     
X            case 'h':
X                if(n<0 || n>=HNUM) n = HNUM-1 ;
X                if(n>hmax) hmax = n ;
X                *pform++ = HEADER+n ;
X                break ;
X     
X            case 's':
X#ifdef DEBUG
Xfprintf(stderr,"newform: %%s detected: n=%d, m=%d, SEQUENCE=%d\n",n,m,SEQUENCE);
X#endif
X                if(n<0) n=0 ;
X                *pform++ = SEQUENCE+n ;
X                if(m>=0) {
X                    seq[n].lev = m ;
X                } else {
X                    seq[n].lev = HNUM-1 ;
X                    sflag = n ;
X                }
X                break ;
X     
X            case '%':
X                *pform++ = '%' ;
X                break ;
X     
X            default:
X                error("unknown format character %%%c",*p) ;
X                break ;
X            }
X            ++p ;
X        }
X    }
X#ifdef DEBUG
Xfprintf(stderr,"parssh: sflag=%d, hmax=%d\n",sflag,hmax) ;
X#endif
X    if(sflag>=0)
X        seq[sflag].lev = hmax ;
X    *pform = '\0' ;
X    return ;
X}
X     
X     
X/* Convert header numbers in hlev[] array into an ASCII header number
X   Inputs: n=header level 0-HNUM (0 means full header, n>0 means
X     truncate number to n levels), p=string buffer
X*/
Xchar *gethead(n,p)
Xint n ;
Xchar *p ;
X{
X    int i ;
X     
X    if(n==0)
X        n = nh ;
X    else
X        n = min(n,nh) ;
X     
X    for(i=1 ; i<=n ; i++) {
X        sprintf(p,"%d.",hlev[i]) ;
X        p += strlen(p) ;
X    }
X    return(p) ;
X}
X     
X     
X/* Reads ASCII string and converts into a set of header numbers in hlev[]
X   Handles both level 0 (full header like 4.5.6.7.) and levels 1-HNUM
X     (single header number like 5)
X*/
Xchar *newhead(n,p)
Xint n ;
Xchar *p ;
X{
X    int i,j = 0 ;
X     
X    if(n==0) n = HNUM-1 ;
X     
X    while(j<n && isdigit(*p)) {
X        i = (*p++)-'0' ;
X        while(isdigit(*p)) {
X            i = 10*i + (*p++) - '0' ;
X        }
X        hlev[++j] = i ;
X        if(*p=='.') ++p ;
X    }
X    p = skipsp(p) ;
X    if(j<=0 || *p!='\0') {
X        error("To set header, try something like: 2.4.6 =%%h",i) ;
X    }
X    if(j>0) {
X        nh = j ;
X        nhflag = 1 ;
X    }
X    return(p) ;
X}
X     
X     
X/* Convert specified sequence counter to ASCII, optionally incrementing it
X   Inputs: n=counter number, p=ASCII buffer, seqflag=0 if simple fetch,
X     =1 if increment counter and clear subscript, =2 if increment subscript
X*/
Xchar *getseq(n,p,seqflag)
Xint n, seqflag ;
Xchar *p ;
X{
X    int j ;
X     
X    if(seqflag==3) {
X        if(seq[n].no < 0)
X            seq[n].no += 32000 ;
X        else
X            seq[n].no++ ;
X        seq[n].sub = 1 ;
X    } else if(seqflag==2) {
X        if(seq[n].no < 0)
X            seq[n].no += 32000 ;
X        else {
X            if(seq[n].sub==0) seq[n].no++ ;
X            seq[n].sub++ ;
X        }
X    } else if(seqflag==1) {
X        seq[n].sub = 0 ;
X        if(seq[n].no < 0)
X            seq[n].no += 32000 ;
X        else
X            seq[n].no++ ;
X    }
X    sprintf(p,"%d",seq[n].no) ;
X    p += strlen(p) ;
X    if((j=seq[n].sub)>0) {
X        if(j>26) {
X            *p++ = (j-1)/26+'a'-1 ;
X        }
X        *p++ = ((j-1)%26)+'a' ;
X    }
X    *p='\0' ;
X    return(p) ;
X}
X     
X     
X/* Converts ASCII string into new sequence number (should have form like
X     5a =%2s)
X   Inputs: n=counter number, p=ASCII buffer
X*/
Xchar *newseq(n,p)
Xint n ;
Xchar *p ;
X{
X    int i = 0 ;
X    int flag = 0 ;
X     
X    if(isdigit(*p)) {
X        flag = 1 ;
X        while(isdigit(*p))
X            i = 10*i+(*p++)-'0' ;
X        seq[n].no = i ;
X        seq[n].sub = 0 ;
X    }
X    if(islower(*p)) {
X        flag = 1 ;
X        seq[n].sub = (*p++)-'a'+1 ;
X        if(islower(*p))
X            seq[n].sub = seq[n].sub*26+(*p++)-'a'+1 ;
X    }
X    if(flag==0 || *p!='\0') {
X         error("To set sequence number, try for example:  5c = %%%ds",n);
X    } else {
X        seq[n].no -= 32000 ;
X    }
X    return(p) ;
X}
X     
X     
X/* Initialization of variables and tag table */
Xinit()
X{
X    int i ;
X     
X    maxtags = 100 ;
X    if((taglist=(char**)malloc((unsigned)maxtags*sizeof(char*)))
X                            == (char**)NULL) {
X        error("malloc failed - out of memory",i) ;
X        exit(1) ;
X    }
X    for(i=0 ; *string[i]!='\0' ; i++)
X        taglist[i] = string[i] ;
X    ntags = i ;
X    return ;
X}
X     
X     
X/* Looks up tag name in list of tag variables using binary search algorthm
X   Returns pointer to list of pointers where tag should be, and pointer
X     to the string corresponding to the tag variable
X   Inputs: tag=tag name, pform=address of pointer to tag value,
X     ptag=address of pointer to list of pointers where tag name should be
X   Returns 1 if tag found, 0 if not
X*/
Xint findtag(tag,pform,ptag)
Xchar *tag,**pform,***ptag ;
X{
X    char **ptag1 = taglist ;
X    unsigned size = ntags ;
X    int i ;
X    char *p2 ;
X     
X    while(size!=0) {
X        p2 = *(ptag1+size/2) ;
X        if((i=strcmp(tag,p2))==0) {
X            *pform = p2+strlen(p2)+1 ;
X            *ptag = ptag1+size/2 ;
X#ifdef DEBUG
Xfprintf(stderr,"findtag: tag %s found, format=%s\n",**ptag,*pform) ;
X#endif
X            return(1) ;
X        } else if(i>0) {
X            ptag1 += size/2+1 ;
X            size -= size/2+1 ;
X        } else {
X            size /= 2 ;
X        }
X    }
X    *ptag = ptag1 ;
X    *pform = "" ;
X#ifdef DEBUG
Xfprintf(stderr,"findtag: tag %s not found, format=%s, pointer=%s\n",tag,*pform,
X  **ptag) ;
X#endif
X    return(0) ;
X}
X     
X     
X/* Adds tag name and value to taglist, expanding the list of tags if necessary
X   Inserts tag name at location returned by findtag()
X   Inputs: tag=tag name, pform=value, ptag=pointer to insertion location
X     in pointer list
X*/
Xaddtag(tag,pform,ptag)
Xchar *tag,*pform,**ptag ;
X{
X    int i ;
X     
X    if(ntags==maxtags) {    /* If present taglist is full, expand it */
X        i = ptag - taglist ;
X        maxtags += 100 ;
X     
X        if((taglist=(char**)realloc((char*)taglist,
X                (unsigned)maxtags*sizeof(char*)))==(char**)NULL) {
X            error("expanding to %d tags, realloc failed",maxtags);
X            exit(1) ;
X        }
X        ptag = taglist+i ;
X    }
X     
X    {
X        register char **p2 = taglist+ntags ;
X        register char **p1 = p2-1 ;
X        register j=p2-ptag ;
X        while(j-->0) *p2-- = *p1-- ;
X    }
X    ntags++ ;
X    filltag(tag,pform,ptag) ;
X    return ;
X}
X     
X     
X/* Changes tag value of tag that is already in tag list
X   Inputs: tag=tag name, pform=tag value, ptag = pointer to point of insertion
X*/
Xreptag(tag,pform,ptag)
Xchar *tag,*pform,**ptag ;
X{
X    free(*ptag) ;
X    filltag(tag,pform,ptag) ;
X}
X     
X     
X     
X/* Gets space for tag name, format string pair, inserts pointer in tag list,
X     inserts name and format string
X   Inputs: tag=tag name, pform=format string, ptag=point of insertion
X*/
Xfilltag(tag,pform,ptag)
Xchar *tag,*pform,**ptag ;
X{
X    char *p ;
X     
X    if((p= *ptag=malloc((unsigned)(strlen(tag)+strlen(pform)+2)))==NULL) {
X        error("malloc failed - too many tags (%d)",ntags) ;
X        exit(1) ;
X    }
X    while(*p++ = *tag++) ;
X    while(*p++ = *pform++) ;
X    return ;
X}
X     
X     
X/* Prints the entire tag list in format ".E# value =tag".  Also sets format
Xstring to the value used
X*/
X     
Xprtags()
X{
X    char **p = taglist ;
X    char *p2,*p3 ;
X    int i = ntags ;
X     
X    while(i-->0) {
X        p2 = *p++ ;
X        p3 = p2+strlen(p2)+1 ;
X        if(*p3>=SEQUENCE+10)
X            printf(".E# \"%s\" =\"%s\"\n",p3,p2) ;
X    }
X    printf(".E# \"%s\" =format\n",cform2) ;
X    return ;
X}
X     
X     
X/* Prints error message to stderr, together with file name and line number
X   Inputs: str=error message in printf format, var=value to be printed by
X     fprintf.  Also uses fname and fline global variables
X*/
X/*VARARGS1*/
Xerror(str,var)
Xchar *str,*var ;
X{
X    fputs("numeqn: ",stderr) ;
X    fprintf(stderr,str,var) ;
X    fprintf(stderr,"\n  file %s, line %d\n",fname,fline) ;
X    return ;
X}
X
END_OF_FILE
if test 33452 -ne `wc -c <'numeqn.c'`; then
    echo shar: \"'numeqn.c'\" unpacked with wrong size!
fi
# end of 'numeqn.c'
fi
if test -f 'numeqn.nr' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'numeqn.nr'\"
else
echo shar: Extracting \"'numeqn.nr'\" \(11582 characters\)
sed "s/^X//" >'numeqn.nr' <<'END_OF_FILE'
X.TH NUMEQN l
X.SH NAME
Xnumeqn - Troff Equation Numbering Package
X.SH SYNOPSIS
X.B numeqn
X[ options ] [ files ... ]
X.SH DESCRIPTION
X.I Numeqn
Xis a general purpose automatic numbering filter for
X.I troff,
Xuseful
Xprimarily for numbering equations, but powerful enough to number anything
Xelse as well.  Numbers are formed from ASCII characters, portions of the
Xheader number (from .NH or .sh), and one of 10 different sequence counters,
Xwith optional subscripts.
XEquations are numbered by including a special "generate number" argument
Xfor the .EQ equation macro.  Any of these numbers
Xmay also be inserted at any point
Xin the text by using the .E# macro.  These numbers may be assigned to
X.I numeqn
Xvariables, and then later used elsewhere in the text.  Furthermore, very
Xflexible format control is available via a mechanism similar to that
Xused by "printf" in the standard I/O library.
X.PP
XTo minimize the quantity
Xof data passed through the
X.I troff
Xfilters,
X.I numeqn
Xshould typically used before
X.I tbl ,
X.I eqn ,
Xor
X.I troff.
XFor example:
X.sp 1v
X.in 1i
Xrefer file | numeqn | tbl | eqn | troff -ms >wowee
X.LP
XAvailable switches for
X.I numeqn
Xare listed at the end of this document.
X.PP
X.I Numeqn
Xrecognizes the following troff macros:
X.nf
X.sp 1
X	.EQ [L,I,C] [value,#,##,##+,#tag,##tag,##+tag] [=tag] [indent]
X.sp 1
X	.E# [value,#,##,##+,#tag,##tag,##+tag] [=tag] [text]
X.sp 1
X	.NH [level]			- for ms macro package
X.sp 1	
X	.sh level title [numbers ...]	- for me macro package
X.sp 1
X.fi
XOrdinarily, any ASCII string following the optional L, C or I arguments
Xin the .EQ equation macro will be printed as the equation number.  If
Xthis string begins with the character '#', however,
X.I numeqn
Xwill use this argument to generate an appropriate equation number. 
XAn optional following argument
X.I '=tag'
Xwill set a variable named
X.I tag
Xto the generated number. This number may be inserted later in the text
Xby using a
X.I '#tag'
Xargument to the .E# numbering macro.  For example, if we are in section
X3.5 of a paper, the following:
X.nf
X.sp 1
X	.EQ C "[hi there]"
X	x=y
X	.EN
X	.EQ C # =N1
X	n=1
X	.EN
X	.EQ # =MM5
X	m=m*5
X	.EN
X	.EQ #
X	5-7+2=19
X	.EN
X	Combining equations
X	.E# #N1
X	and
X	.E# #MM5 ,
X	generates bliss.
X.fi
X.sp 1
Xwill generate:
X.nf
X.sp 1
X.tl ''x=y'[hi there]'
X.sp 0.5v
X.tl ''n=1'(3.5.1)'
X.sp 0.5v
X.tl ''m=m*5'(3.5.2)'
X.sp 0.5v
X.tl ''5-7+2=19'(3.5.3)'
X.sp 0.5v
X	Combining equations (3.5.1) and (3.5.2), generates bliss.
X.fi
X.sp 1
X.PP
XThe first equation uses the string "[hi there]" as the equation number. 
XThis string could have been stored in a tag variable by following it
Xwith an argument of the form
X.I '=tag' .
XThe second and third .EQ equation macros automatically generate
Xsequential numbers, and set the variables "N1" and "MM5" to these values.
XThe .E# macros that follow will be replaced with the contents
Xof the specified tag
Xvariables.  Note that any text following the arguments to .E# will be
Xinserted immediately following the number with no intervening space.
XThis is convenient in examples such as the second .E# above, in which a
Xcomma was inserted immediately following the MM5 equation number.  
X.PP
XTag variable names may be formed from any non-space characters.  If
Xspaces or tabs are desired in the tag name, simply enclose the name in
Xquotation marks.  Tag names formed solely out of digits are considered
X"local" and may be reused.  All other tag names are considered "global",
Xand must not be redefined.
X.PP
XSequential subscripted numbers may be generated by replacing the '#'
Xargument to .EQ with '##' or '##+'.  The '##' argument specifies that
Xthe next subscripted number should be used; the '##+' argument
Xincrements the sequence number and resets the subscript back to 'a'. 
XFor example:
X.nf
X.sp 1
X	.EQ C ##
X	a=b
X	.EN
X	.EQ C ## =21
X	b=c
X	.EN
X	Refer to
X	.E# #21
X	and rejoice!
X	.EQ C ##+
X	z=5
X	.EN
X	.EQ ##+ =21
X	y=6+5
X	.EN
X	and another
X	.E# #21 !
X.sp 1
X.fi
Xwill generate:
X.sp 1
X.nf
X.tl ''a=b'(3.5.4a)'
X.sp 0.5v
X.tl ''b=c'(3.5.4b)'
X.sp 0.5v
X	Refer to (3.5.4b) and rejoice!
X.sp 0.5v
X.tl ''z=5'(3.5.5a)'
X.sp 0.5v
X.tl ''y=6+5'(3.5.6a)'
X.sp 0.5v
X	and another (3.5.6a)!
X.sp 1
X.fi
X.PP
XThe .E# macro can also be used to automatically generate and insert
Xnumbers in the text, or to set tag variables to specific values.  For example:
X.nf
X.sp 1
X	Another
X	.E# #
X	number and another
X	.E# ##
X	subscripted number.  Let's set a tag
X	.E# ## =Z9.2
X	variable and another
X	.E# "hi there" =H32ZX_4
X	and now use it here
X	.E# #H32ZX_4
X	and here:
X	.EQ C #Z9.2
X	h=3.2*z/x+4
X	.EN
X.sp 1
X.fi
Xwill yield:
X.nf
X.sp 1
X	Another (3.5.7) number and another (3.5.8a) subscripted
X	number.  Let's set a tag variable and another and now use
X	it here (3.5.9) and here:
X.sp 0.5v
X.tl ''h=3.2*z/x+4'(3.5.8b)'
X.sp 1
X.fi
X.PP
XThe arguments #, ## or ##+ can thus automatically generate numbers for
Xboth .E# and .EQ macros, tag variables may be set with '=tag'
Xby either .E# or .EQ,
Xand the contents of a tag variable may be inserted by either .E# or .EQ
Xby using the '#tag' argument.  Note that when the .E# macro is used to
Xset a tag variable that the number is NOT inserted in the text and any
Xcharacters immediately following the 
X.I =tag
Xargument will be discarded.
XThis feature allows setting tag variables "invisibly"
Xwithout inserting the number
Xin the text.
X.PP
XBoth .E# and .EQ set the troff string variable E# to the number they generate.
XThis makes it convenient to embed the number in complicated troff
Xconstructions by referring to the string variable \\*(E# where needed.
XThus:
X.nf
X.sp 1
X	A number:
X	.E# # .
X	And again: \\*(E# and\\*(E#here too.
X.sp 1
X.fi
Xwill generate:
X.nf
X.sp 1
X	A number: (3.5.10).  And again: (3.5.10) and(3.5.10)here too.
X.sp 1
X.fi
X.PP
X.I Numeqn
Xallows extremely flexible control over the formatting of the
Xnumbers it generates by using a mechanism similar to that used by
X"printf" in the standard I/O package.  The tag variable named
X"format" contains an ASCII
Xstring which specifies the default format used for all numbers generated
Xby using the #, ## and ##+ arguments to .E# or .EQ.  Ordinary characters
Xin this format string will be printed exactly as specified in every number. 
XVarying information, such as the section header and sequence number, may be
Xspecified by using a sequence of characters starting with a '%'.  The
Xfollowing character sequences are recognized in format strings:
X.nf
X.sp 1
X	%h	- insert the complete header number at this point.
X.sp 1
X	%nh	- (1<=n<=5) insert only levels 1 through n of the header
X		number at this point.
X.sp 1
X	%s	- insert the next sequential value of counter 0.
X.sp 1	
X	%ns	- (0<=n<=9) insert the next sequential value of counter n.
X.sp 1	
X	%n.ms	- (0<=n<=9 and 0<=m<=5) insert the next sequential value of
X		counter n, and make sure this counter is reset to 0 each time
X		a new section header at level m or below is created.  If m=0,
X		the counter will never reset.
X.sp 1
X	%%	- insert the character '%'
X.sp 1
X.fi
XThe default format is (%2h%0.2s), which will produce numbers like (3.5.6) or
X(3.5.6c) in section 3.5 for the sixth item (third subscripted number.)
XThe default format could be changed, for example, by:
X.sp 1
X.nf
X	.E# [A:%h%s] =format
X.sp 1
X.fi
Xwhich sets the variable
X.I "format"
Xto the ASCII string "[A:%h%s]" without printing anything.
XNow the default format will print numbers which might look like: [A:3.5.6]
X.PP
XUsually sequence counters will be reset to 0 whenever a .NH command (or .sh
Xcommand for the -me macro package) is encountered which would change the
Xpart of the header number which is used in forming the equation number.
XThis rule may be changed by using the full %n.ms specification.
X.PP
XThe arguments #tag, ##tag, ##+tag to .EQ and .E# actually allow using the
Xcontents of a tag variable to specify the format for this number.  If the tag
Xvariable contains a string with no '%' characters, then its contents
Xwill be used unmodified as the number to be inserted.  If the string value
Xin the tag variable contains character
Xsequences such as '%h' or '%s', however,
Xthe appropriate header and sequence numbers
Xwill be inserted and appropriately updated.
XThus, for example, we could define a numbering
Xformat T for tables by:
X.sp 1
X.nf
X	.E# %1h%2s =T
X.fi
X.sp 1
XNo text is generated for this line.  After this, however, a new table
Xnumber could be generated by:
X.nf
X.sp 1
X	Table
X	.E# #T
X	is great!
X.sp 1
X.fi
Xwhich will insert
X.nf
X.sp 1
X	Table 3.1 is great!
X.sp 1
X.fi
Xif we are still in section 3 and this is the first table.
XNote that the T format uses sequence counter
Xnumber 2, and therefore tables will be numbered independently of
Xany number printed using the default format, which typically uses
Xsequence counter 0.
X.PP
XCertain tag variable names are reserved
Xand have special meaning:
X.nf
X.sp 1
X	format	- contains the default numbering format string.  Setting
X		this variable changes the default numbering style.
X.sp 1		
X	%s	- contains the last sequence number used on sequence
X		counter 0.  Changing this variable by:  ".E# 5c =%s"
X		would change the next sequential subscripted number to 5c.
X.sp 1		
X	%ns	- (0<=n<=9) contains the value of sequence counter n.
X.sp 1
X	%h	- contains the complete header number (read-only in -me).
X.sp 1
X	%nh	- contains the first n levels of the header (read-only
X		in -me).
X.sp 1
X.fi
X.PP
XVarious options may be specified on the command line:
X.nf
X.sp 1
X	-f string     	use "string" as the default numbering format
X	-h level      	use "level" as the initial header number (this also
X			  affects the first .NH command) (valid only for -ms)
X	-s number     	use "number" as the initial number in counter 0
X	-t tagfile    	when finished, output a list of all tag variable
X			  values to "tagfile"
X	-ms		-ms macro package format (default)
X	-me		-me macro package format
X.sp 1
X.fi
XThe -t option constructs an output file containing lines of the form:
X.nf
X.sp 1
X	.E# "(3.5.1)" ="N1"
X.fi
X.sp 1
Xfor every tag variable which is defined in the file.  This is
Xconvenient, for example, when running off very long text files in sections.
XSimply include the tag file from the previous section as the first text
Xfile when running 
X.I numeqn
Xon the next section - this will correctly define all the
Xtag variables, and will allow referring to equation numbers from
Xprevious sections.  A similar trick also allows forward referencing of
Xequation numbers.  For example,
X.sp 1
X.nf
X	numeqn -me -t sect1tags  junk.vr >/dev/null
X	numeqn -me  sect1tags junk.vr >wowee
X.sp 1
X.fi
Xwill collect all the tag variable definitions in a file "set1tags".
XThe second time
X.I numeqn
Xis called, it will read "set1tags" first, thus setting all the tag
Xvariables to their correct values before "junk.vr" is processed.
XNow single level forward references can be resolved.
X.SH SEE ALSO
X.BR troff (1),
X.BR eqn (1),
X.BR ms (7),
X.BR me (7)
X.SH AUTHOR
XCopyright Bruce R. Musicus 1990.  This program is being released into the
Xpublic domain.  Unlimited personal use and distribution is permitted, but 
Xthis package may not be incorporated into a commercial product.
X.SH BUGS
XEach sequence counter should be used in only one format string.  If a
Xcounter must be shared between several formats, be sure that all use
Xexactly the same '%n.ms' specification.  Otherwise, the sequence
Xcounter will reset according to the last defined format string header level.
X.PP
XThe \-t option should also save the value of all sequence counters.
X.PP
X.I numeqn
Xtries to continue no matter what bizarre syntax errors
Xare found.  As a result, it will return a 0 exit status even if
Xmillions of syntax errors occurred.
X.PP
XThis program is subject to change as conceptual bugs are found,
Xand as better ideas come to mind.
X
END_OF_FILE
if test 11582 -ne `wc -c <'numeqn.nr'`; then
    echo shar: \"'numeqn.nr'\" unpacked with wrong size!
fi
# end of 'numeqn.nr'
fi
echo shar: End of shell archive.
exit 0


