home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume22 / archie / part03 / regex.c next >
Text File  |  1991-08-21  |  17KB  |  714 lines

  1. /*
  2.  * These routines are from "a pre-release of a bunch of berkelix
  3.  * regex(3)/ed(1) compatible regular-expression routines" written by Ozan
  4.  * S. Yigit, Dept. of Computer Science, York University.  Parts of the
  5.  * code that are not needed by Prospero have been removed, but most of
  6.  * the accompanying information has been left intact.  This file is to be
  7.  * included on those operating systems that do not support re_comp and
  8.  * re_exec.
  9.  */
  10.  
  11. /*
  12.  
  13. These routines are completely public domain. You can do whatever you
  14. like with them, and hopefully you are professional enough not to strip
  15. out the authorship information, acknowledgements and references.
  16.  
  17. The reason for this being a *pre-release* is that I received a lot
  18. of useful suggestions about packaging, about additional routines etc.
  19. from a few people. I do not have too much time to do those changes
  20. right now, so I am putting this package out for those who needed
  21. it yesterday. Next release will include other routines, and better
  22. packaging.
  23.  
  24. These routines are *not* tested under SysV, but they are tested
  25. under PRO/Venix (2.0) and BSD 4.2.
  26.  
  27. In general, these routines run just as fast, or faster than regex library
  28. routines under BSD 4.2. In some cases, they are slightly slower. I did not
  29. try too hard to optimize the re_exec routine.
  30.  
  31. Coding style is a la K&R, with lotsa short identifiers. I like it
  32. that way. All flames should be fed to yetti!dragon.
  33.  
  34. Acknowledgements: Henry Spencer, Hugh Redelmeier and Drew Sullivan made
  35.           a lot of important suggestions, some of which will be
  36.           incorporated into the next version.
  37.  
  38.  */
  39.  
  40. /*
  41.  * regex - Regular expression pattern matching
  42.  *         and replacement
  43.  *
  44.  *
  45.  * By:  Ozan S. Yigit (oz)
  46.  *      Dept. of Computer Science
  47.  *      York University
  48.  *         [...!utzoo!yetti!oz || oz@yusol.BITNET || oz@yuyetti.BITNET]
  49.  *
  50.  * These routines are the PUBLIC DOMAIN equivalents 
  51.  * of regex routines as found in 4.nBSD UN*X, with minor
  52.  * extensions.
  53.  *
  54.  * These routines are derived from various implementations
  55.  * found in software tools books, and Conroy's grep. They
  56.  * are NOT derived from licensed/restricted software.
  57.  * For more interesting/academic/complicated implementations,
  58.  * see Henry Spencer's regexp routines, or GNU Emacs pattern
  59.  * matching module.
  60.  *
  61.  * Routines:
  62.  *      re_comp:        compile a regular expression into
  63.  *                      a DFA.
  64.  *
  65.  *            char *re_comp(s)
  66.  *            char *s;
  67.  *
  68.  *      re_exec:        execute the DFA to match a pattern.
  69.  *
  70.  *            int re_exec(s)
  71.  *            char *s;
  72.  *
  73.  * Regular Expressions:
  74.  *
  75.  *      [1]     char    matches itself, unless it is a special
  76.  *                      character (metachar): . \ [ ] * + ^ $
  77.  *
  78.  *      [2]     .       matches any character.
  79.  *
  80.  *      [3]     \       matches the character following it, except
  81.  *            when followed by a left or right round bracket,
  82.  *            a digit 1 to 9 or a left or right angle bracket. 
  83.  *            (see [7], [8] and [9])
  84.  *            It is used as an escape character for all 
  85.  *            other meta-characters, and itself. When used
  86.  *            in a set ([4]), it is treated as an ordinary
  87.  *            character.
  88.  *
  89.  *      [4]     [set]   matches one of the characters in the set.
  90.  *                      If the first character in the set is "^",
  91.  *                      it matches a character NOT in the set. A
  92.  *                      shorthand S-E is used to specify a set of
  93.  *                      characters S upto E, inclusive. The special
  94.  *                      characters "]" and "-" have no special
  95.  *                      meaning if they appear as the first chars
  96.  *                      in the set.
  97.  *                      examples:        match:
  98.  *
  99.  *                              [a-z]    any lowercase alpha
  100.  *
  101.  *                              [^]-]    any char except ] and -
  102.  *
  103.  *                              [^A-Z]   any char except uppercase
  104.  *                                       alpha
  105.  *
  106.  *                              [a-zA-Z] any alpha
  107.  *
  108.  *      [5]     *       any regular expression form [1] to [4], followed by
  109.  *                      closure char (*) matches zero or more matches of
  110.  *                      that form.
  111.  *
  112.  *      [6]     +       same as [5], except it matches one or more.
  113.  *
  114.  *      [7]             a regular expression in the form [1] to [10], enclosed
  115.  *                      as \(form\) matches what form matches. The enclosure
  116.  *                      creates a set of tags, used for [8] and for
  117.  *                      pattern substution. The tagged forms are numbered
  118.  *            starting from 1.
  119.  *
  120.  *      [8]             a \ followed by a digit 1 to 9 matches whatever a
  121.  *                      previously tagged regular expression ([7]) matched.
  122.  *
  123.  *    [9]    \<    a regular expression starting with a \< construct
  124.  *        \>    and/or ending with a \> construct, restricts the
  125.  *            pattern matching to the beginning of a word, and/or
  126.  *            the end of a word. A word is defined to be a character
  127.  *            string beginning and/or ending with the characters
  128.  *            A-Z a-z 0-9 and _. It must also be preceded and/or
  129.  *            followed by any character outside those mentioned.
  130.  *
  131.  *      [10]            a composite regular expression xy where x and y
  132.  *                      are in the form [1] to [10] matches the longest
  133.  *                      match of x followed by a match for y.
  134.  *
  135.  *      [11]    ^    a regular expression starting with a ^ character
  136.  *        $    and/or ending with a $ character, restricts the
  137.  *                      pattern matching to the beginning of the line,
  138.  *                      or the end of line. [anchors] Elsewhere in the
  139.  *            pattern, ^ and $ are treated as ordinary characters.
  140.  *
  141.  *
  142.  * Acknowledgements:
  143.  *
  144.  *    HCR's Hugh Redelmeier has been most helpful in various
  145.  *    stages of development. He convinced me to include BOW
  146.  *    and EOW constructs, originally invented by Rob Pike at
  147.  *    the University of Toronto.
  148.  *
  149.  * References:
  150.  *              Software tools            Kernighan & Plauger
  151.  *              Software tools in Pascal        Kernighan & Plauger
  152.  *              Grep [rsx-11 C dist]            David Conroy
  153.  *        ed - text editor        Un*x Programmer's Manual
  154.  *        Advanced editing on Un*x    B. W. Kernighan
  155.  *        RegExp routines            Henry Spencer
  156.  *
  157.  * Notes:
  158.  *
  159.  *    This implementation uses a bit-set representation for character
  160.  *    classes for speed and compactness. Each character is represented 
  161.  *    by one bit in a 128-bit block. Thus, CCL or NCL always takes a 
  162.  *    constant 16 bytes in the internal dfa, and re_exec does a single
  163.  *    bit comparison to locate the character in the set.
  164.  *
  165.  * Examples:
  166.  *
  167.  *    pattern:    foo*.*
  168.  *    compile:    CHR f CHR o CLO CHR o END CLO ANY END END
  169.  *    matches:    fo foo fooo foobar fobar foxx ...
  170.  *
  171.  *    pattern:    fo[ob]a[rz]    
  172.  *    compile:    CHR f CHR o CCL 2 o b CHR a CCL bitset END
  173.  *    matches:    fobar fooar fobaz fooaz
  174.  *
  175.  *    pattern:    foo\\+
  176.  *    compile:    CHR f CHR o CHR o CHR \ CLO CHR \ END END
  177.  *    matches:    foo\ foo\\ foo\\\  ...
  178.  *
  179.  *    pattern:    \(foo\)[1-3]\1    (same as foo[1-3]foo)
  180.  *    compile:    BOT 1 CHR f CHR o CHR o EOT 1 CCL bitset REF 1 END
  181.  *    matches:    foo1foo foo2foo foo3foo
  182.  *
  183.  *    pattern:    \(fo.*\)-\1
  184.  *    compile:    BOT 1 CHR f CHR o CLO ANY END EOT 1 CHR - REF 1 END
  185.  *    matches:    foo-foo fo-fo fob-fob foobar-foobar ...
  186.  * 
  187.  */
  188.  
  189. #define MAXDFA  1024
  190. #define MAXTAG  10
  191.  
  192. #define OKP     1
  193. #define NOP     0
  194.  
  195. #define CHR     1
  196. #define ANY     2
  197. #define CCL     3
  198. #define NCL     4
  199. #define BOL     5
  200. #define EOL     6
  201. #define BOT     7
  202. #define EOT     8
  203. #define BOW    9
  204. #define EOW    10
  205. #define REF     11
  206. #define CLO     12
  207.  
  208. #define END     0
  209.  
  210. /*
  211.  * The following defines are not meant
  212.  * to be changeable. They are for readibility
  213.  * only.
  214.  *
  215.  */
  216. #define MAXCHR    128
  217. #define CHRBIT    8
  218. #define BITBLK    MAXCHR/CHRBIT
  219. #define BLKIND    0170
  220. #define BITIND    07
  221.  
  222. #define ASCIIB    0177
  223.  
  224. typedef /*unsigned*/ char CHAR;
  225.  
  226. static int  tagstk[MAXTAG];             /* subpat tag stack..*/
  227. static CHAR dfa[MAXDFA];        /* automaton..       */
  228. static int  sta = NOP;                   /* status of lastpat */
  229.  
  230. static CHAR bittab[BITBLK];        /* bit table for CCL */
  231.  
  232. static int  internal_error;
  233.  
  234. static void
  235. chset(c) register CHAR c; { bittab[((c)&BLKIND)>>3] |= 1<<((c)&BITIND); }
  236.  
  237. #define badpat(x)    return(*dfa = END, x)
  238. #define store(x)    *mp++ = x
  239.  
  240. static char *     
  241. re_comp(pat)
  242. char *pat;
  243. {
  244.     register char *p;               /* pattern pointer   */
  245.     register CHAR *mp=dfa;          /* dfa pointer       */
  246.     register CHAR *lp;              /* saved pointer..   */
  247.     register CHAR *sp=dfa;          /* another one..     */
  248.  
  249.     register int tagi = 0;          /* tag stack index   */
  250.     register int tagc = 1;          /* actual tag count  */
  251.  
  252.     register int n;
  253.     int c1, c2;
  254.         
  255.     internal_error = 0;
  256.  
  257.     if (!pat || !*pat)
  258.         if (sta)
  259.             return(0);
  260.         else
  261.             badpat("No previous regular expression");
  262.     sta = NOP;
  263.  
  264.     for (p = pat; *p; p++) {
  265.         lp = mp;
  266.         switch(*p) {
  267.  
  268.         case '.':               /* match any char..  */
  269.             store(ANY);
  270.             break;
  271.  
  272.         case '^':               /* match beginning.. */
  273.             if (p == pat)
  274.                 store(BOL);
  275.             else {
  276.                 store(CHR);
  277.                 store(*p);
  278.             }
  279.             break;
  280.  
  281.         case '$':               /* match endofline.. */
  282.             if (!*(p+1))
  283.                 store(EOL);
  284.             else {
  285.                 store(CHR);
  286.                 store(*p);
  287.             }
  288.             break;
  289.  
  290.         case '[':               /* match char class..*/
  291.  
  292.             if (*++p == '^') {
  293.                 store(NCL);
  294.                 p++;
  295.             }
  296.             else
  297.                 store(CCL);
  298.  
  299.             if (*p == '-')        /* real dash */
  300.                 chset(*p++);
  301.             if (*p == ']')        /* real brac */
  302.                 chset(*p++);
  303.             while (*p && *p != ']') {
  304.                 if (*p == '-' && *(p+1) && *(p+1) != ']') {
  305.                     p++;
  306.                     c1 = *(p-2) + 1;
  307.                     c2 = *p++;
  308.                     while (c1 <= c2)
  309.                         chset(c1++);
  310.                 }
  311. #ifdef EXTEND
  312.                 else if (*p == '\\' && *(p+1)) {
  313.                     p++;
  314.                     chset(*p++);
  315.                 }
  316. #endif
  317.                 else
  318.                     chset(*p++);
  319.             }
  320.             if (!*p)
  321.                 badpat("Missing ]");
  322.  
  323.             for (n = 0; n < BITBLK; bittab[n++] = (char) 0)
  324.                 store(bittab[n]);
  325.     
  326.             break;
  327.  
  328.         case '*':               /* match 0 or more.. */
  329.         case '+':               /* match 1 or more.. */
  330.             if (p == pat)
  331.                 badpat("Empty closure");
  332.             lp = sp;                /* previous opcode */
  333.             if (*lp == CLO)         /* equivalence..   */
  334.                 break;
  335.             switch(*lp) {
  336.  
  337.             case BOL:
  338.             case BOT:
  339.             case EOT:
  340.             case BOW:
  341.             case EOW:
  342.             case REF:
  343.                 badpat("Illegal closure");
  344.             default:
  345.                 break;
  346.             }
  347.  
  348.             if (*p == '+')
  349.                 for (sp = mp; lp < sp; lp++)
  350.                     store(*lp);
  351.  
  352.             store(END);
  353.             store(END);
  354.             sp = mp;
  355.             while (--mp > lp)
  356.                 *mp = mp[-1];
  357.             store(CLO);
  358.             mp = sp;
  359.             break;
  360.  
  361.         case '\\':              /* tags, backrefs .. */
  362.             switch(*++p) {
  363.  
  364.             case '(':
  365.                 if (tagc < MAXTAG) {
  366.                     tagstk[++tagi] = tagc;
  367.                     store(BOT);
  368.                     store(tagc++);
  369.                 }
  370.                 else
  371.                     badpat("Too many \\(\\) pairs");
  372.                 break;
  373.             case ')':
  374.                 if (*sp == BOT)
  375.                     badpat("Null pattern inside \\(\\)");
  376.                 if (tagi > 0) {
  377.                     store(EOT);
  378.                     store(tagstk[tagi--]);
  379.                 }
  380.                 else
  381.                     badpat("Unmatched \\)");
  382.                 break;
  383.             case '<':
  384.                 store(BOW);
  385.                 break;
  386.             case '>':
  387.                 if (*sp == BOW)
  388.                     badpat("Null pattern inside \\<\\>");
  389.                 store(EOW);
  390.                 break;
  391.             case '1':
  392.             case '2':
  393.             case '3':
  394.             case '4':
  395.             case '5':
  396.             case '6':
  397.             case '7':
  398.             case '8':
  399.             case '9':
  400.                 n = *p-'0';
  401.                 if (tagi > 0 && tagstk[tagi] == n)
  402.                     badpat("Cyclical reference");
  403.                 if (tagc > n) {
  404.                     store(REF);
  405.                     store(n);
  406.                 }
  407.                 else
  408.                     badpat("Undetermined reference");
  409.                 break;
  410. #ifdef EXTEND
  411.             case 'b':
  412.                 store(CHR);
  413.                 store('\b');
  414.                 break;
  415.             case 'n':
  416.                 store(CHR);
  417.                 store('\n');
  418.                 break;
  419.             case 'f':
  420.                 store(CHR);
  421.                 store('\f');
  422.                 break;
  423.             case 'r':
  424.                 store(CHR);
  425.                 store('\r');
  426.                 break;
  427.             case 't':
  428.                 store(CHR);
  429.                 store('\t');
  430.                 break;
  431. #endif
  432.             default:
  433.                 store(CHR);
  434.                 store(*p);
  435.             }
  436.             break;
  437.  
  438.         default :               /* an ordinary char  */
  439.             store(CHR);
  440.             store(*p);
  441.             break;
  442.         }
  443.         sp = lp;
  444.     }
  445.     if (tagi > 0)
  446.         badpat("Unmatched \\(");
  447.     store(END);
  448.     sta = OKP;
  449.     return(0);
  450. }
  451.  
  452.  
  453. static char *bol;
  454. static char *bopat[MAXTAG];
  455. static char *eopat[MAXTAG];
  456.  
  457. char *pmatch();
  458.  
  459. /*
  460.  * re_exec:
  461.  *     execute dfa to find a match.
  462.  *
  463.  *    special cases: (dfa[0])    
  464.  *        BOL
  465.  *            Match only once, starting from the
  466.  *            beginning.
  467.  *        CHR
  468.  *            First locate the character without
  469.  *            calling pmatch, and if found, call
  470.  *            pmatch for the remaining string.
  471.  *        END
  472.  *            re_comp failed, poor luser did not
  473.  *            check for it. Fail fast.
  474.  *
  475.  *    If a match is found, bopat[0] and eopat[0] are set
  476.  *    to the beginning and the end of the matched fragment,
  477.  *    respectively.
  478.  *
  479.  */
  480.  
  481. static int
  482. re_exec(lp)
  483. register char *lp;
  484. {
  485.     register char c;
  486.     register char *ep = 0;
  487.     register CHAR *ap = dfa;
  488.  
  489.     bol = lp;
  490.  
  491.     bopat[0] = 0;
  492.     bopat[1] = 0;
  493.     bopat[2] = 0;
  494.     bopat[3] = 0;
  495.     bopat[4] = 0;
  496.     bopat[5] = 0;
  497.     bopat[6] = 0;
  498.     bopat[7] = 0;
  499.     bopat[8] = 0;
  500.     bopat[9] = 0;
  501.  
  502.     switch(*ap) {
  503.  
  504.     case BOL:            /* anchored: match from BOL only */
  505.         ep = pmatch(lp,ap);
  506.         break;
  507.     case CHR:            /* ordinary char: locate it fast */
  508.         c = *(ap+1);
  509.         while (*lp && *lp != c)
  510.             lp++;
  511.         if (!*lp)        /* if EOS, fail, else fall thru. */
  512.             return(0);
  513.     default:            /* regular matching all the way. */
  514.         while (*lp) {
  515.             if ((ep = pmatch(lp,ap)))
  516.                 break;
  517.             lp++;
  518.         }
  519.         break;
  520.     case END:            /* munged automaton. fail always */
  521.         return(0);
  522.     }
  523.     if (!ep)
  524.         return(0);
  525.  
  526.     if (internal_error) 
  527.         return(-1);
  528.  
  529.     bopat[0] = lp;
  530.     eopat[0] = ep;
  531.     return(1);
  532. }
  533.  
  534. /* 
  535.  * pmatch: 
  536.  *    internal routine for the hard part
  537.  *
  538.  *     This code is mostly snarfed from an early
  539.  *     grep written by David Conroy. The backref and
  540.  *     tag stuff, and various other mods are by oZ.
  541.  *
  542.  *    special cases: (dfa[n], dfa[n+1])
  543.  *        CLO ANY
  544.  *            We KNOW ".*" will match ANYTHING
  545.  *            upto the end of line. Thus, go to
  546.  *            the end of line straight, without
  547.  *            calling pmatch recursively. As in
  548.  *            the other closure cases, the remaining
  549.  *            pattern must be matched by moving
  550.  *            backwards on the string recursively,
  551.  *            to find a match for xy (x is ".*" and 
  552.  *            y is the remaining pattern) where
  553.  *            the match satisfies the LONGEST match
  554.  *            for x followed by a match for y.
  555.  *        CLO CHR
  556.  *            We can again scan the string forward
  557.  *            for the single char without recursion, 
  558.  *            and at the point of failure, we execute 
  559.  *            the remaining dfa recursively, as
  560.  *            described above.
  561.  *
  562.  *    At the end of a successful match, bopat[n] and eopat[n]
  563.  *    are set to the beginning and end of subpatterns matched
  564.  *    by tagged expressions (n = 1 to 9).    
  565.  *
  566.  */
  567.  
  568.  
  569. /*
  570.  * character classification table for word boundary
  571.  * operators BOW and EOW. the reason for not using 
  572.  * ctype macros is that we can let the user add into 
  573.  * our own table. see re_modw. This table is not in
  574.  * the bitset form, since we may wish to extend it
  575.  * in the future for other character classifications. 
  576.  *
  577.  *    TRUE for 0-9 A-Z a-z _
  578.  */
  579. static char chrtyp[MAXCHR] = {
  580.     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
  581.     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
  582.     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
  583.     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
  584.     0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 
  585.     1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 
  586.     0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 
  587.     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
  588.     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
  589.     1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 
  590.     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
  591.     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
  592.     1, 1, 1, 0, 0, 0, 0, 0
  593.     };
  594.  
  595. #define inascii(x)    (0177&(x))
  596. #define iswordc(x)     chrtyp[inascii(x)]
  597. #define isinset(x,y)     ((x)[((y)&BLKIND)>>3] & (1<<((y)&BITIND)))
  598.  
  599. /*
  600.  * skip values for CLO XXX to skip past the closure
  601.  *
  602.  */
  603.  
  604. #define ANYSKIP    2     /* CLO ANY END ...       */
  605. #define CHRSKIP    3    /* CLO CHR chr END ...       */
  606. #define CCLSKIP 18    /* CLO CCL 16bytes END ... */
  607.  
  608. static char *
  609. pmatch(lp, ap)
  610. register char *lp;
  611. register CHAR *ap;
  612. {
  613.     register char *e;        /* extra pointer for CLO */
  614.     register char *bp;        /* beginning of subpat.. */
  615.     register char *ep;        /* ending of subpat..     */
  616.     register int op, c, n;
  617.     char *are;            /* to save the line ptr. */
  618.  
  619.     while ((op = *ap++) != END)
  620.         switch(op) {
  621.  
  622.         case CHR:
  623.             if (*lp++ != *ap++)
  624.                 return(0);
  625.             break;
  626.         case ANY:
  627.             if (!*lp++)
  628.                 return(0);
  629.             break;
  630.         case CCL:
  631.             c = *lp++;
  632.             if (!isinset(ap,c))
  633.                 return(0);
  634.             ap += BITBLK;
  635.             break;
  636.         case NCL:
  637.             c = *lp++;
  638.             if (isinset(ap,c))
  639.                 return(0);
  640.             ap += BITBLK;
  641.             break;
  642.         case BOL:
  643.             if (lp != bol)
  644.                 return(0);
  645.             break;
  646.         case EOL:
  647.             if (*lp)
  648.                 return(0);
  649.             break;
  650.         case BOT:
  651.             bopat[*ap++] = lp;
  652.             break;
  653.         case EOT:
  654.             eopat[*ap++] = lp;
  655.             break;
  656.          case BOW:
  657.             if (!(lp!=bol && iswordc(lp[-1])) && iswordc(*lp))
  658.                 break;
  659.             return(0);
  660.         case EOW:
  661.             if ((lp!=bol && iswordc(lp[-1])) && !iswordc(*lp))
  662.                 break;
  663.             return(0);
  664.         case REF:
  665.             n = *ap++;
  666.             bp = bopat[n];
  667.             ep = eopat[n];
  668.             while (bp < ep)
  669.                 if (*bp++ != *lp++)
  670.                     return(0);
  671.             break;
  672.         case CLO:
  673.             are = lp;
  674.             switch(*ap) {
  675.  
  676.             case ANY:
  677.                 while (*lp)
  678.                     lp++;
  679.                 n = ANYSKIP;
  680.                 break;
  681.             case CHR:
  682.                 c = *(ap+1);
  683.                 while (*lp && c == *lp)
  684.                     lp++;
  685.                 n = CHRSKIP;
  686.                 break;
  687.             case CCL:
  688.             case NCL:
  689.                 while (*lp && (e = pmatch(lp, ap)))
  690.                     lp = e;
  691.                 n = CCLSKIP;
  692.                 break;
  693.             default:
  694.                 internal_error++;
  695.                 return(0);
  696.             }
  697.  
  698.             ap += n;
  699.  
  700.             while (lp >= are) {
  701.                 if (e = pmatch(lp, ap))
  702.                     return(e);
  703.                 --lp;
  704.             }
  705.             return(0);
  706.         default:
  707.             internal_error++;
  708.             return(0);
  709.         }
  710.     return(lp);
  711. }
  712.  
  713.  
  714.