home *** CD-ROM | disk | FTP | other *** search
/ Frozen Fish 1: Amiga / FrozenFish-Apr94.iso / bbs / alib / d1xx / d172 / spiff.lha / Spiff / parse.c < prev    next >
C/C++ Source or Header  |  1988-11-22  |  17KB  |  803 lines

  1. /*                        Copyright (c) 1988 Bellcore
  2. **                            All Rights Reserved
  3. **       Permission is granted to copy or use this program, EXCEPT that it
  4. **       may not be sold for profit, the copyright notice must be reproduced
  5. **       on copies, and credit should be given to Bellcore where it is due.
  6. **       BELLCORE MAKES NO WARRANTY AND ACCEPTS NO LIABILITY FOR THIS PROGRAM.
  7. */
  8.  
  9.  
  10. #ifndef lint
  11. static char rcsid[]= "$Header: parse.c,v 1.1 88/09/15 11:33:57 daniel Rel $";
  12. #endif
  13.  
  14. #include "misc.h"
  15. #include "flagdefs.h"
  16. #include "float.h"
  17. #include "tol.h"
  18. #include "token.h"
  19. #include "line.h"
  20. #include "command.h"
  21. #include "comment.h"
  22. #include "parse.h"
  23.  
  24.  
  25. #include <ctype.h>
  26.  
  27. #define _P_PARSE_CHATTER    1000
  28.  
  29.  
  30. static    int _P_realline;    /* loop counter */
  31. static  int _P_fnumb;
  32.  
  33. static  char *_P_nextchr;    /* pointer to the next character to parse */
  34. static    char *_P_firstchr;        /* pointer to the beginning of the line being parsed */
  35. static    int _P_next_tol;        /* number of floats seen on this line */
  36. static    int _P_stringsize;        /* count of number of characters that are being
  37.                     read into a comment or literal */
  38. static int _P_has_content;    /* flag to indicate if the line being
  39.                     parsed has any tokens on it */
  40. static int _P_start;        /* first line to parse */
  41. static int _P_lcount;        /* number of lines to parse */
  42.  
  43. static int _P_flags;        /* location for global flags */
  44.  
  45. /*
  46. **    by default, "words" can be made up of numbers and letters
  47. **    the following code allows for extending the alphabet that can
  48. **    be used in words. this is useful for handling languages such
  49. **    as C where the underscore character is an allowable character
  50. **    in an identifier.  If a character (such as underscore) is NOT added
  51. **    to the alphabet, the identifier will be broken into 2 or more "words"
  52. **    by the parser.  as such the two sequences
  53. **            one_two
  54. **        and
  55. **            one _ two
  56. **    would look identical to spiff.
  57. */
  58. #define _P_ALPHALEN 256
  59. static char _P_alpha[_P_ALPHALEN];
  60.  
  61. static void
  62. _P_alpha_clear()
  63. {
  64.     *_P_alpha = '\0';
  65. }
  66.  
  67. static
  68. _P_in_alpha(chr)
  69. char chr;
  70. {
  71. #ifndef ATT
  72.     extern int index();
  73. #endif
  74.     /*
  75.     **    special case when string terminator
  76.     **    is handed to us
  77.     */
  78.     if ('\0' == chr)
  79.         return(0);
  80.  
  81. #ifdef ATT
  82.     return((int) strchr(_P_alpha,chr));
  83. #else
  84.     return((int) index(_P_alpha,chr));
  85. #endif
  86. }
  87.  
  88. void
  89. P_addalpha(ptr)
  90. char *ptr;
  91. {
  92.     char buf[Z_LINELEN];
  93.  
  94.     S_wordcpy(buf,ptr);        /* copy up to (but not including)
  95.                         the first whitespace char */
  96.  
  97.     if ((strlen(_P_alpha) + strlen(buf)) >= _P_ALPHALEN)
  98.     {
  99.         Z_fatal("too many characters added to extended alphabet");
  100.     }
  101.     (void) strcat(_P_alpha,buf);
  102. }
  103.  
  104. /*
  105. **    put parser in a default state
  106. */
  107.  
  108. static char _P_dummyline[2];    /* a place to aim wild pointers */
  109. static void
  110. _P_initparser()
  111. {
  112.     _P_dummyline[0] = '\0';
  113.  
  114.     /*
  115.     **    now reset all the state of each module
  116.     */
  117.     C_clear_cmd();        /* disable embedded command key word */ 
  118.     T_clear_tols();
  119.     W_clearcoms();
  120.     W_clearlits();
  121.     _P_alpha_clear();    /* disable extended alphabet */
  122.  
  123.     /*
  124.     **    and set state as defined by execute-time commands.
  125.     */
  126.     C_docmds();
  127.     return;
  128. }
  129.  
  130.  
  131. static
  132. _P_needmore()
  133. {
  134.     return(*_P_nextchr == '\0');
  135. }
  136.  
  137. static
  138. _P_nextline()
  139. {
  140.     /*
  141.     **    if the line that we just finished had
  142.     **        some content,  increment the count
  143.     */
  144.     if (_P_has_content)
  145.     {
  146.         L_incclmax(_P_fnumb);
  147.         /*
  148.         **    if the previous line had a token
  149.         **        increment the line
  150.         */
  151.         if (L_getcount(_P_fnumb,L_gettlmax(_P_fnumb)))
  152.         {
  153.             L_inctlmax(_P_fnumb);
  154.             L_setcount(_P_fnumb,L_gettlmax(_P_fnumb),0);
  155.         }
  156.         _P_has_content = 0;
  157.     }
  158.  
  159.     /*
  160.     **    reset the number of floats seen on the line
  161.     */
  162.     _P_next_tol = 0;
  163.  
  164.     /*
  165.     **    get another line if there is one available
  166.     */
  167.     _P_realline++;
  168.     if (_P_realline >= _P_start+_P_lcount)
  169.     {
  170.         return(1);
  171.     }
  172.  
  173.     _P_firstchr = _P_nextchr = L_getrline(_P_fnumb,_P_realline);
  174.     /*
  175.     **    and look for a command
  176.     */
  177.     if (C_is_cmd(_P_firstchr))
  178.     {
  179.         _P_nextchr = _P_dummyline;
  180.         _P_has_content = 0;
  181.     }
  182.     else
  183.     {
  184.         /*
  185.         **    we have a real line, so set up the index
  186.         */
  187.         L_setclindex(_P_fnumb,L_getclmax(_P_fnumb),_P_realline);
  188.         _P_has_content = 1;
  189.     }
  190.     return(0);
  191. }
  192.  
  193. /*
  194. **    the following three routines (_P_litsnarf, _P_bolsnarf, and _P_comsnarf
  195. **    all do roughly the same thing. they scan ahead and collect the
  196. **    specified string, move _P_nextchr to the end of the
  197. **    comment or literal and return 1 if we run off the end of file,
  198. **    0 otherwise.  it would have been nice to have 1 routine handle
  199. **    all three task (there is much common code), however there were
  200. **    so enough differences, (for instance, only comments check for nesting,
  201. **    only literals need to set _P_stringsize, etc)
  202. **    that I decided to split them up.
  203. */
  204. static int
  205. _P_litsnarf(litptr)
  206. W_lit litptr; 
  207. {
  208.     _P_stringsize = 0;
  209.     /*
  210.     **    skip the start of literal string
  211.     */
  212.     _P_nextchr += strlen(W_litbegin(litptr));
  213.     _P_stringsize += strlen(W_litbegin(litptr));
  214.     /*
  215.     **    is there a separate end string?
  216.     **        if not, then we're done
  217.     */
  218.     if ('\0' == *(W_litend(litptr)))
  219.     {
  220.         return(0);
  221.     }
  222.     /*
  223.     **    loop once for each character in the literal
  224.     */
  225.     while(1)
  226.     {
  227.         /*
  228.         **    if we are out of characters, move on to next line
  229.         */
  230.         if (_P_needmore())
  231.         {
  232.             if (_P_nextline())
  233.             {
  234.                 return(1);
  235.             }
  236.             if (!_P_has_content)
  237.             {
  238.                 /*
  239.                 **    since we've just gotten a command
  240.                 **        check to see if this literal
  241.                 **        is still legit ...
  242.                 **        could have just been reset
  243.                 **        by the command
  244.                 */
  245.                 if (!W_is_lit(litptr))
  246.                 {
  247.                     return(0);
  248.                 }
  249.             }
  250.         } /* if _P_needmore */
  251.  
  252.         /*
  253.         **    see if we have an escaped end of literal string
  254.         */
  255.         if (('\0' != *(W_litescape(litptr))) && /* escape string exists */
  256.           !S_wordcmp(_P_nextchr,
  257.                W_litescape(litptr)) &&     /* and escape matches */
  258.           !S_wordcmp(_P_nextchr+strlen(W_litescape(litptr)),
  259.                W_litend(litptr)))         /* and endstring matches */
  260.         {
  261.             _P_nextchr += strlen(W_litescape(litptr))
  262.                     + strlen(W_litend(litptr));
  263.             _P_stringsize += strlen(W_litescape(litptr))
  264.                     + strlen(W_litend(litptr));
  265.             continue;
  266.         }
  267.  
  268.         /*
  269.         **    see if we have an end of literal string
  270.         */
  271.         if (!S_wordcmp(_P_nextchr,W_litend(litptr))) /* escape matches */
  272.         {
  273.             _P_nextchr += strlen(W_litend(litptr));
  274.             _P_stringsize += strlen(W_litend(litptr));
  275.             return(0);
  276.         }
  277.         /*
  278.         **    this must be yet another character in the literal, so
  279.         **    just snarf it up
  280.         */
  281.         _P_nextchr++;
  282.         _P_stringsize++;
  283.     }    /* while loop once for each character */
  284.  
  285. #ifndef lint
  286.     Z_fatal("shouldn't execute this line at the end of _P_litsnarf");
  287. #endif
  288. } /* _P_litsnarf */
  289.  
  290. static int
  291. _P_bolsnarf(bolptr)
  292. W_bol bolptr; 
  293. {
  294.     /*
  295.     **    skip the start of comment string
  296.     */
  297.     _P_nextchr += strlen(W_bolbegin(bolptr));
  298.     /*
  299.     **    is there a separate end string
  300.     **        if not, then we're done
  301.     */
  302.     if ('\0' == *(W_bolend(bolptr)))
  303.     {
  304.         return(0);
  305.     }
  306.     /*
  307.     **    loop once for each character in the comment
  308.     */
  309.     while(1)
  310.     {
  311.         /*
  312.         **    if we are out of characters,move on to next line
  313.         */
  314.         if (_P_needmore())
  315.         {
  316.             if (_P_nextline())
  317.             {
  318.                 return(1);
  319.             }
  320.             if (!_P_has_content)
  321.             {
  322.                 /*
  323.                 **    since we've just gotten a command
  324.                 **        check to see if this comment
  325.                 **        is still legit ... comments
  326.                 **        could have just been reset
  327.                 **        by the command
  328.                 */
  329.                 if (!W_is_bol(bolptr))
  330.                 {
  331.                     return(0);
  332.                 }
  333.             }
  334.         } /* if at end of line */
  335.  
  336.         /*
  337.         **    see if we have an escaped end of comment string
  338.         */
  339.         if ('\0' != *(W_bolescape(bolptr)) && /* escape string exists */
  340.           !S_wordcmp(_P_nextchr,
  341.                W_bolescape(bolptr)) &&     /* and escape matches */
  342.           !S_wordcmp(_P_nextchr+strlen(W_bolescape(bolptr)),
  343.                W_bolend(bolptr)))     /* and end string matches */
  344.         {
  345.             _P_nextchr += strlen(W_bolescape(bolptr))
  346.                     + strlen(W_bolend(bolptr));
  347.             continue;
  348.         }
  349.  
  350.         /*
  351.         **    see if we have an end of comment string
  352.         */
  353.         if (!S_wordcmp(_P_nextchr,W_bolend(bolptr)))
  354.         {
  355.             _P_nextchr += strlen(W_bolend(bolptr));
  356.             return(0);
  357.         }
  358.         /*
  359.         **    this must be yet another character in the comment, so
  360.         **    just snarf it up
  361.         */
  362.         _P_nextchr++;
  363.     }    /* while loop once for each character */
  364.  
  365. #ifndef lint
  366.     Z_fatal("shouldn't execute this line in at end of _P_bolsnarf");
  367. #endif
  368. } /* _P_bolsnarf */
  369.  
  370. /*
  371. **    pass over a comment -- look for nexting
  372. */
  373. static
  374. _P_comsnarf(comptr)
  375. W_com comptr; 
  376. {
  377.     int depth = 1; /* nesting depth */
  378.     /*
  379.     **    skip the start of comment string
  380.     */
  381.     _P_nextchr += strlen(W_combegin(comptr));
  382.  
  383.     /*
  384.     **    is there a separate end string
  385.     **        if not, then we're done
  386.     */
  387.     if ('\0' == *(W_comend(comptr)))
  388.     {
  389.         return(0);
  390.     }
  391.     /*
  392.     **    loop once for each character in the comment
  393.     */
  394.     while(1)
  395.     {
  396.         /*
  397.         **    if we are out of characters, move on to next line
  398.         */
  399.         if (_P_needmore())
  400.         {
  401.             if (_P_nextline())
  402.             {
  403.                 return(1);
  404.             }
  405.             if (!_P_has_content)
  406.             {
  407.                 /*
  408.                 **    since we've just gotten a command
  409.                 **        check to see if this comment
  410.                 **        is still legit ... comments
  411.                 **        could have just been reset
  412.                 **        by the command
  413.                 */
  414.                 if (!W_is_com(comptr))
  415.                 {
  416.                     return(0);
  417.                 }
  418.             }
  419.         } /* if at end of line */
  420.  
  421.         /*
  422.         **    see if we have an escaped end of comment string
  423.         */
  424.         if ('\0' != *(W_comescape(comptr)) &&  /* escape string exists */
  425.           !S_wordcmp(_P_nextchr,
  426.                W_comescape(comptr)) &&    /* and escape matches */
  427.           !S_wordcmp(_P_nextchr+strlen(W_comescape(comptr)),
  428.                W_comend(comptr)))    /* and end string matches */
  429.         {
  430.             /*
  431.             ** skip over the escape sequence and the end sequence
  432.             */
  433.             _P_nextchr += strlen(W_comescape(comptr))
  434.                     + strlen(W_comend(comptr));
  435.             continue;
  436.         }
  437.  
  438.         /*
  439.         **    see if we have an end of comment string
  440.         */
  441.         if (!S_wordcmp(_P_nextchr,W_comend(comptr))) /* end  matches */
  442.         {
  443.             /*
  444.             **    skip over the end sequence
  445.             */
  446.             _P_nextchr += strlen(W_comend(comptr));
  447.             if (W_is_nesting(comptr))
  448.             {
  449.                 depth--;
  450.                 if (0 == depth)
  451.                     return(0);
  452.             }
  453.             else
  454.             {
  455.                 return(0);
  456.             }
  457.             continue;
  458.         }
  459.         /*
  460.         **    see if we have another beginning of comment string
  461.         */
  462.         if (W_is_nesting(comptr) &&
  463.             !S_wordcmp(_P_nextchr,W_comend(comptr))) /* end matches */
  464.         {
  465.             _P_nextchr += strlen(W_comend(comptr));
  466.             depth++;
  467.             continue;
  468.         }
  469.         /*
  470.         **    this must be yet another character in the comment, so
  471.         **    just snarf it up
  472.         */
  473.         _P_nextchr++;
  474.     }    /* while loop once for each character */
  475.  
  476. #ifndef lint
  477.         Z_fatal("should not execute this line in _P_comsnarf\n");
  478. #endif
  479.  
  480. } /* _P_comsnarf */
  481.  
  482.  
  483. /*
  484. **    parse a file
  485. */
  486. static void
  487. _P_do_parse()
  488. {
  489.  
  490.     char *ptr;        /* scratch space */
  491.     int tmp;
  492.     int ret_code;
  493.  
  494.     K_token newtoken;
  495.     W_bol bolptr;
  496.     W_com comptr;
  497.     W_lit litptr;
  498.  
  499.     int startline, endline, startpos;
  500.  
  501.     /*
  502.     **    main parsing loop
  503.     */
  504.     while (1)
  505.     {
  506.         /*
  507.         **    get more text if necessary
  508.         */
  509.         if (_P_needmore())
  510.         {
  511.             if (_P_nextline())
  512.             {
  513.                 return;
  514.             }
  515.  
  516.             /*
  517.             **    if the line contains nothing of interest,
  518.             **        try again
  519.             */
  520.             if (!_P_has_content)
  521.             {
  522.                 continue;
  523.             }
  524.  
  525.             /*
  526.             **    check to see if this line starts a comment
  527.             */
  528.             if ((bolptr = W_isbol(_P_firstchr)) != W_BOLNULL)
  529.             {
  530.                 if (_P_bolsnarf(bolptr))
  531.                 {
  532.                     return;
  533.                 }
  534.                 continue;
  535.             }
  536.         } /* if _P_needmore */
  537.  
  538.         /*
  539.         **    skip whitespace
  540.         */
  541.         if (!(U_INCLUDE_WS & _P_flags) && isspace(*_P_nextchr))
  542.         {
  543.             _P_nextchr++;
  544.             continue;
  545.         }
  546.  
  547.         /*
  548.         **    check to see if this character starts a comment
  549.         */
  550.         if ((comptr = W_iscom(_P_nextchr)) != W_COMNULL)
  551.         {
  552.             if (_P_comsnarf(comptr))
  553.             {
  554.                 return;
  555.             }
  556.             continue;
  557.         }
  558.  
  559.         /*
  560.         **    if there aren't any tokens on this line already
  561.         **    set up the index from the token line to the content line
  562.         */
  563.         if (!L_getcount(_P_fnumb,L_gettlmax(_P_fnumb)))
  564.         {
  565.             L_settlindex(_P_fnumb,
  566.                     L_gettlmax(_P_fnumb),
  567.                     L_getclmax(_P_fnumb));
  568.             /*
  569.             **    and the pointer from the token line to the 
  570.             **     first  token on the line
  571.             */
  572.             L_setindex(_P_fnumb,
  573.                     L_gettlmax(_P_fnumb),
  574.                     K_gettmax(_P_fnumb));
  575.         }
  576.  
  577.         startline =  L_tl2cl(_P_fnumb,L_gettlmax(_P_fnumb));
  578.         startpos = _P_nextchr-_P_firstchr;
  579.  
  580.         newtoken = K_maketoken();
  581.         K_setline(newtoken,L_gettlmax(_P_fnumb));
  582.         K_setpos(newtoken,startpos);
  583.  
  584.         ret_code = 0;
  585.         /*
  586.         **    check to see if this character starts a
  587.         **        delimited literal string
  588.         */
  589.         if ((litptr = W_islit(_P_nextchr)) != W_LITNULL)
  590.         {
  591.             ret_code = _P_litsnarf(litptr);
  592.             K_settype(newtoken,K_LIT);
  593.             S_allocstr(&ptr,_P_stringsize);
  594.             /*
  595.             **    fixed nasty memory bug here by adding else
  596.             **    old code copied entire line even if literal
  597.             **    ended before the end of line
  598.             **        should check into getting strcpy loaded
  599.             **        locally
  600.             */
  601.             endline = L_getclmax(_P_fnumb);
  602.             if (endline > startline)
  603.             {
  604.                 /*
  605.                 **    copy in the first line of the literal
  606.                 */
  607.                 (void) strcpy(ptr,
  608.                           L_getcline(_P_fnumb,startline)
  609.                             +startpos);
  610.                 /*
  611.                 **    now copy all the lines between
  612.                 **        the first and last
  613.                 */
  614.                 for (tmp=startline+1;tmp<endline;tmp++)
  615.                 {
  616.                     (void) strcat(ptr,
  617.                               L_getcline(_P_fnumb,tmp));
  618.                 }
  619.                 /*
  620.                 **    and now copy in the last line
  621.                 */
  622.                 (void) strncat(ptr,
  623.                            L_getcline(_P_fnumb,endline),
  624.                            _P_stringsize-strlen(ptr));
  625.             }
  626.             else
  627.             {
  628.                 (void) strncpy(ptr,
  629.                            L_getcline(_P_fnumb,startline)
  630.                                 +startpos,
  631.                           _P_stringsize);
  632.                 /*
  633.                 **    terminate the string you just copied
  634.                 */
  635.                 ptr[_P_stringsize] = '\0';
  636.             }
  637.             K_settext(newtoken,ptr);
  638.         } /* if is_lit */
  639.  
  640.         /*
  641.         **    see if this is a floating point number
  642.         */
  643.         else if (tmp = F_isfloat(_P_nextchr,
  644.                        _P_flags & U_NEED_DECIMAL,
  645.                        _P_flags & U_INC_SIGN))
  646.         {
  647.             K_saventext(newtoken,_P_nextchr,tmp);
  648.             K_settype(newtoken,K_FLO_NUM);
  649.             if (!(_P_flags & U_BYTE_COMPARE))
  650.             {
  651.                 K_setfloat(newtoken,
  652.                        F_atof(K_gettext(newtoken),
  653.                        USE_ALL));
  654.  
  655.                 /*
  656.                 **    assign the curent tolerance
  657.                 */
  658.                 K_settol(newtoken,T_gettol(_P_next_tol));
  659.             }
  660.  
  661.             /*
  662.             **    use next tolerance in the
  663.             **        specification if there is one
  664.             */
  665.             if (T_moretols(_P_next_tol))
  666.             {
  667.                 _P_next_tol++;
  668.             }
  669.             /*
  670.             **    and move pointer past the float
  671.             */
  672.             _P_nextchr += tmp;
  673.         }
  674.  
  675.         /*
  676.         **    is this a fixed point number
  677.         */
  678.         else if (isdigit(*_P_nextchr))
  679.         {
  680.             for(ptr=_P_nextchr; isdigit(*ptr); ptr++)
  681.             {
  682.             }
  683.             K_saventext(newtoken,_P_nextchr,ptr-_P_nextchr);
  684.             K_settype(newtoken,K_LIT);
  685.             _P_nextchr = ptr;
  686.         }
  687.  
  688.         /*
  689.         **    try an alpha-numeric word
  690.         */
  691.         else if (isalpha(*_P_nextchr) || _P_in_alpha(*_P_nextchr))
  692.         {
  693.             /*
  694.             **    it's a multi character word
  695.             */
  696.             for(ptr = _P_nextchr;
  697.                 isalpha(*ptr)
  698.                 || isdigit(*ptr)
  699.                 || _P_in_alpha(*ptr);
  700.                 ptr++)
  701.             {
  702.             }
  703.             K_saventext(newtoken,_P_nextchr,ptr-_P_nextchr);
  704.             K_settype(newtoken,K_LIT);
  705.             _P_nextchr = ptr;
  706.         }
  707.         else
  708.         {
  709.             /*
  710.             **    otherwise, treat the char itself as a token
  711.             */
  712.             K_saventext(newtoken,_P_nextchr,1);
  713.             K_settype(newtoken,K_LIT);
  714.             _P_nextchr++;
  715.         }
  716.  
  717.         K_settoken(_P_fnumb,K_gettmax(_P_fnumb),newtoken);
  718.         L_inccount(_P_fnumb,L_gettlmax(_P_fnumb));
  719.         /*
  720.         **    if we are out of space, complain and quit
  721.         */
  722.         if (K_inctmax(_P_fnumb))
  723.         {
  724.             (void) sprintf(Z_err_buf,
  725.      "warning -- to many tokens in file only first %d tokens will be used.\n",
  726.                        K_MAXTOKENS);
  727.             Z_complain(Z_err_buf);
  728.             return;
  729.         }
  730. #ifndef NOCHATTER
  731.         if (0 == (K_gettmax(_P_fnumb) % _P_PARSE_CHATTER))
  732.         {
  733.             int max = K_gettmax(_P_fnumb);
  734.             (void) sprintf(Z_err_buf,
  735.                 "scanned %d words from file #%d\n",
  736.                     max,_P_fnumb+1);
  737.             Z_chatter(Z_err_buf);
  738.         }
  739. #endif
  740.  
  741.         /*
  742.         **    are we done?
  743.         */
  744.         if(ret_code)
  745.         {
  746.             return;
  747.         }
  748.     }   /* loop once per object on a line */
  749.  
  750. #ifndef lint 
  751.     Z_fatal("this line should never execute");
  752. #endif
  753. }
  754.  
  755. void
  756. P_file_parse(num,strt,lcnt,flags)
  757. int num;    /* file number */
  758. int strt;    /* first line to parse expressed in real line numbers */
  759. int lcnt;    /* max number of lines to parse */
  760. int flags;    /* flags for controlling the parse mode */
  761. {
  762.     /*
  763.     **    set module-wide state variables
  764.     */
  765.     _P_fnumb = num;        
  766.     _P_start = strt;    
  767.     _P_lcount = lcnt;
  768.     _P_flags = flags;
  769.  
  770.     _P_initparser();
  771.  
  772.     _P_nextchr = _P_dummyline;
  773.  
  774.     _P_has_content = 0;
  775.     _P_next_tol = 0;
  776.     L_setcount(_P_fnumb,L_gettlmax(_P_fnumb),0);
  777.     /*
  778.     **    start everything back one line (it will be incremented
  779.     **        just before the first line is accessed
  780.     */
  781.     _P_realline = _P_start-1;
  782.  
  783.     _P_do_parse();
  784.  
  785.     /*
  786.     **    if the last line had content, increment the count
  787.     */
  788.     if (_P_has_content)
  789.     {
  790. /*
  791. **    this code will get executed if we stopped parsing in the middle
  792. **    of a line.  i haven't looked at this case carefully.
  793. **    so, there is a good chance that it is buggy.
  794. */
  795. (void) sprintf(Z_err_buf,"parser got confused at end of file\n");
  796. Z_complain(Z_err_buf);
  797.         L_incclmax(_P_fnumb);
  798.         if (L_getcount(_P_fnumb,L_gettlmax(_P_fnumb)))
  799.             L_inctlmax(_P_fnumb);
  800.     }
  801.     return;
  802. }
  803.