1 /*
2  * Taken from texlive-2007/texk/lacheck/lacheck.lex
3  * with a minor modification.
4  */
5 
6 /*                    -*- Mode: C -*-
7  *
8  * lacheck.lex - A consistency checker checker for LaTeX documents
9  *
10  * Copyright (C) 1991, 1992 Kresten Krab Thorup.
11  * Copyright (C) 1993 --- 1998 Per Abrahamsen.
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 1, or (at your option)
16  * any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26  *
27  * Please send bugs, suggestions, and queries to auc-tex_mgr@sunsite.auc.dk.
28  *
29  * $Revision: 1.26 $
30  * Author          : Kresten Krab Thorup
31  * Created On      : Sun May 26 18:11:58 1991
32  *
33  * HISTORY
34  * 07-Mar-1998		Per Abrahamsen
35  *    Added return to yywrap.  Patch by Fabrice POPINEAU
36  *    <popineau@esemetz.ese-metz.fr>.
37  * 14-Jan-1998		Per Abrahamsen
38  *    Added GPL blurp.
39  * 27-Oct-1997		Per Abrahamsen
40  *    Count newline after newenvironment and newcommand.
41  * 12-Jan-1996          Per Abrahamsen
42  *    \\} used not to end a group in definitions.  Reported by Piet
43  *    van Oostrum <piet@cs.ruu.nl>.
44  * 03-Jan-1995		Per Abrahamsen
45  *    Fix bug which prevented detection of multiple illegal characters
46  *    in labels.  Reported by eeide@jaguar.cs.utah.edu (Eric Eide).
47  * 30-Jul-1994		Per Abrahamsen
48  *    Define dummy yywrap so we no longer depend on `libl.a'.
49  * 26-Apr-1994		Per Abrahamsen
50  *    Removed a few warnings, by Richard Lloyd <R.K.Lloyd@csc.liv.ac.uk>.
51  * 23-Apr-1994		Per Abrahamsen
52  *    Changed all `%i' to `%d' for VMS portability.  Reported by
53  *    Stephen Harker <PHS172M@vaxc.cc.monash.edu.au>.
54  * 16-Feb-1994		Per Abrahamsen
55  *    Try file name with `.tex' appended before trying it bare.  This
56  *    will make the case where a directory and a TeX file share the
57  *    same name work.
58  * 19-Jan-1994		Per Abrahamsen
59  *    Comments don't imply whitespace.  Pointed out by Jacco van
60  *    Ossenbruggen <jrvosse@cs.vu.nl>.
61  * 14-Jan-1994		Per Abrahamsen
62  *    Don't complain about \ref at the beginning of a paragraph.
63  *    Suggested by Jean-Marc Lasgouttes <Jean-Marc.Lasgouttes@inria.fr>.
64  * 11-Jan-1994		Per Abrahamsen
65  *    Added version string to usage message.  Suggested by Uwe Bonnes
66  *    <bon@LTE.E-TECHNIK.uni-erlangen.de> .
67  * 04-Jan-1994		Per Abrahamsen
68  *    Warn about newlines in \verb.  Suggested by Mark Burton
69  *    <markb@ordern.demon.co.uk>.  The LaTeX Book agrees (p. 168).
70  * 10-Sep-1993          Per Abrahamsen
71  *    Removed complain about missing ~ before \cite.  Requested by
72  *    Nelson H. F. Beebe <beebe@math.utah.edu>.  The LaTeX Book seems
73  *    to agree.
74  * 03-Sep-1993	        Per Abrahamsen
75  *    Check for illegal characters in labels.
76  * 16-Aug-1993	        Per Abrahamsen
77  *    Recognize \endinput.  Suggested by Stefan Farestam
78  *    <Stefan.Farestam@cerfacs.fr>.
79  * 13-Aug-1993          Per Abrahamsen
80  *    } was eaten after display math.  Reported by Eckhard Rüggeberg
81  *    <eckhard@ts.go.dlr.de>.
82  * 13-Aug-1993          Per Abrahamsen
83  *    Recognize \verb*.  Reported by Eckhard Rüggeberg
84  *    <eckhard@ts.go.dlr.de>.
85  * 08-Aug-1993          Per Abrahamsen
86  *    Better catch begin and end without arguments.
87  * 08-Aug-1993          Per Abrahamsen
88  *    Removed free(NULL) as reported by Darrel R. Hankerson
89  *    <hankedr@mail.auburn.edu>.
90  * 08-Aug-1993		Per Abrahamsen
91  *    Removed declaration of realloc for some C compilers.  Reported by
92  *    Darrel R. Hankerson <hankedr@mail.auburn.edu>
93  * 30-Jul-1993          Per Abrahamsen
94  *    Added check for italic correction after normal text.
95  * 29-Jul-1993          Per Abrahamsen
96  *    Added cast for (char*) malloc as suggested by John Interrante
97  *    <interran@uluru.Stanford.EDU>.
98  * 29-Jul-1993          Per Abrahamsen
99  *    Added check for missing and extra italic correction.
100  * 29-Jul-1993	        Per Abrahamsen
101  *    Made line number counting more reliable (but it still needs a rewrite)!
102  * 28-Jul-1993	        Per Abrahamsen
103  *    Added check for italic correction before point or comma.
104  * 6-Jun-1992		Kresten Krab Thorup
105  *    Last Modified: Sat Jun  6 16:37:44 1992 #48 (Kresten Krab Thorup)
106  *    Added test for whitespace before punctuation mark
107  * 17-Dec-1991  (Last Mod: Tue Dec 17 21:01:24 1991 #41)  Kresten Krab Thorup
108  *    Added 'word word` and missing ~ before cite and ref
109  * 18-Jun-1991  (Last Mod: Tue Jun 18 19:20:43 1991 #17)  Kresten Krab Thorup
110  *    Added check (or rather management) for \newenvironment and
111  *    \newcommand - as suggested by Per Abrahamsen abrham@hugin.dk
112  * 30-May-1991  (Last Mod: Thu May 30 02:22:33 1991 #15)  Kresten Krab Thorup
113  *    Added check for `$${punct}' and `{punct}$' constructions
114  * 30-May-1991  (Last Mod: Wed May 29 10:31:35 1991 #6)  Kresten Krab Thorup
115  *    Improved (dynamic) stack management from Andreas Stolcke ...
116  *                                       <stolcke@ICSI.Berkeley.EDU>
117  * 26-May-1991  Kresten Krab Thorup
118  *    Initial distribution version.
119  */
120 
121 %{
122 
123 #include <stdio.h>
124 #include <string.h>
125 
126 #ifdef WIN32
127 #include <win32lib.h>
128 #endif
129 /* #include <sys/param.h> */
130 
131 /* extern char *realloc(); */
132 
133 #define YY_SKIP_YYWRAP
yywrap()134 int yywrap() { return 1; }
135 
136 #ifdef NEED_STRSTR
137 char *strstr();
138 #endif
139 
140 #define GROUP_STACK_SIZE 10
141 #define INPUT_STACK_SIZE 10
142 
143 #define PROGNAME "LaCheck"
144 
145   /* macros */
146 
147 #define CG_NAME (char *)gstack[gstackp-1].s_name
148 #define CG_TYPE gstack[gstackp-1].s_type
149 #define CG_LINE gstack[gstackp-1].s_line
150 #define CG_ITALIC gstack[gstackp-1].italic
151 #define CG_FILE gstack[gstackp-1].s_file
152 
153 char *bg_command();
154 void pop();
155 void push();
156 void linecount();
157 void g_checkend();
158 void e_checkend();
159 void f_checkend();
160 void input_file();
161 void print_bad_match();
162 int check_top_level_end();
163 
164   /* global variables */
165 
166 char returnval[100];
167 int line_count = 1;
168 int warn_count = 0;
169 char *file_name;
170 char verb_char;
171 
172   /* the group stack */
173 
174 typedef struct tex_group
175  {
176     unsigned char *s_name;
177     int s_type;
178     int s_line;
179     int italic;
180     char *s_file;
181  } tex_group;
182 
183 tex_group *gstack;
184 int gstack_size = GROUP_STACK_SIZE;
185 int gstackp = 0;
186 
187 typedef struct input_
188  {
189     YY_BUFFER_STATE stream;
190     char *name;
191     int linenum;
192  } input_;
193 
194 input_ *istack;
195 int istack_size = INPUT_STACK_SIZE;
196 int istackp = 0;
197 
198 int def_count = 0;
199 
200 %}
201 
202 %x B_ENVIRONMENT E_ENVIRONMENT VERBATIM INCLUDE MATH COMMENT VERB DEF
203 %x AFTER_DISPLAY ENV_DEF ICOR GETICOR
204 
205 %s START
206 
207 b_group ("{"|\\bgroup)
208 e_group ("}"|\\egroup)
209 
210 b_math \\\(
211 e_math \\\)
212 math \$
213 
214 b_display \\\[
215 e_display \\\]
216 display \$\$
217 
218 par ([ \t]*\n[ \t]*\n[ \t\n]*)
219 non_par_ws ([ \t]+\n?[ \t]*|[ \t]*\n[ \t]*|[ \t]*\n?[ \t]+)
220 
221 ws [ \n\t](%[^\n]\n)*
222 space ({ws}|\~|\\space)
223 hard_space (\~|\\space)
224 
225 u_letter [A-ZÆØÅ]
226 l_letter [a-zæøå]
227 punct [\!\.\?]
228 atoz [a-zA-Z]
229 letter [A-ZÆØÅa-zæøå]
230 
231 c_bin ("-"|"+"|"\\cdot"|"\\oplus"|"\\otimes"|"\\times")
232 l_bin (",")
233 
234 general_abbrev {letter}+{punct}
235 
236 non_abbrev {u_letter}{u_letter}+{punct}
237 
238 italic_spec (sl|it)
239 normal_spec normalshape
240 swap_spec em
241 font_spec (rm|bf|{italic_spec}|tt|{swap_spec}|mediumseries|{normal_spec})
242 
243 primitive \\(above|advance|catcode|chardef|closein|closeout|copy|count|countdef|cr|crcr|csname|delcode|dimendef|dimen|divide|expandafter|font|hskp|vskip|openout)
244 
245 symbol ("$"("\\"{atoz}+|.)"$"|"\\#"|"\\$"|"\\%"|"\\ref")
246 
247 %%
248 
249 <*>"\\\\" { ; }
250 
251 <ENV_DEF,DEF,INITIAL>"\\\%" { ; }
252 
253 <ICOR,GETICOR,ENV_DEF,DEF,INITIAL>"%"[^\n]*\n { line_count++; }
254 
255 <ICOR,GETICOR,ENV_DEF,DEF,INITIAL>\n 	{ line_count++; }
256 
257 <ENV_DEF,DEF,INITIAL>"\\\{" { ; }
258 
259 <ENV_DEF,DEF,INITIAL>"\\\}" { ; }
260 
261 "\\\$" { ; }
262 
263 <ICOR,INITIAL,GETICOR>"{"{ws} {
264   if (CG_TYPE != 4 && CG_TYPE != 5) {
265     if (!(CG_TYPE == 2 && strstr(CG_NAME, "array"))) {
266       printf( "\"%s\", line %d: possible unwanted space at \"{\"\n",
267 	     file_name, line_count);
268       ++warn_count ;
269     }
270   }
271   push( "{", 0, line_count);
272   linecount();
273  }
274 
275 <ICOR,INITIAL,GETICOR>{b_group} {  push( "{", 0, line_count);}
276 
277 <INITIAL,GETICOR>{e_group} {
278   {
279     int italic = CG_ITALIC;
280     g_checkend(0);
281     if (italic && !CG_ITALIC)
282       BEGIN(GETICOR) ;
283     else
284       BEGIN(INITIAL);
285   }}
286 
287 <ICOR>{e_group} {  g_checkend(0); }
288 
289 <GETICOR>[A-Za-zæøåÆØÅ0-9;:!()]+ {
290  {
291    if (!CG_ITALIC)
292      {
293        printf("\"%s\", line %d: you may need a \\/ before \"%s\"\n",
294 	      file_name, line_count, yytext);
295        ++warn_count;
296      }
297     BEGIN(INITIAL);
298  }}
299 
300 <ICOR>[A-Za-zæøåÆØÅ0-9;:!?()`']+ {
301  {
302    if (CG_ITALIC)
303      {
304        printf("\"%s\", line %d: \\/ not needed before italic text \"%s\"\n",
305 	      file_name, line_count, yytext);
306        ++warn_count;
307      }
308     BEGIN(INITIAL);
309  }}
310 
311 ^[A-Za-zæøåÆØÅ0-9;:!?()`',.]+{ws}*/\\\/ {
312   {
313    linecount();
314    if (!CG_ITALIC)
315      {
316        printf("\"%s\", line %d: \\/ not needed after non-italic text \"%s\"\n",
317               file_name, line_count, yytext);
318        ++warn_count;
319      }
320  }}
321 
322 {ws}[A-Za-zæøåÆØÅ0-9;:!?()`',.]+{ws}*/\\\/ {
323   {
324    linecount();
325    if (!CG_ITALIC)
326      {
327        printf("\"%s\", line %d: \\/ is not needed after non-italic \"%s\"\n",
328               file_name, line_count, yytext);
329        ++warn_count;
330      }
331  }}
332 
333 <GETICOR>\\\/ { BEGIN(INITIAL); }
334 
335 <INITIAL>\\\/ { BEGIN(ICOR); }
336 
337 <ICOR>\\\/ {
338   {
339     printf("\"%s\", line %d: double \\/ found \"%s\"\n",
340            file_name, line_count, yytext);
341     ++warn_count;
342     BEGIN(ICOR);
343   }}
344 
345 <INITIAL,GETICOR,ICOR>\\{italic_spec}/[^a-zA-Z] { CG_ITALIC = 1; }
346 
347 <INITIAL,GETICOR>\\{normal_spec}/[^a-zA-Z] {
348   {
349     if(CG_ITALIC)
350       BEGIN(GETICOR);
351     else
352       BEGIN(INITIAL);
353     CG_ITALIC = 0;
354   }}
355 
356 <ICOR>\\{normal_spec}/[^a-zA-Z] { CG_ITALIC = 0; }
357 
358 <INITIAL,GETICOR>\\{swap_spec}/[^a-zA-Z] {
359   {
360     if(CG_ITALIC)
361       BEGIN(GETICOR);
362     else
363       BEGIN(INITIAL);
364     CG_ITALIC = !CG_ITALIC;
365   }}
366 
367 <ICOR>\\{swap_spec}/[^a-zA-Z] { CG_ITALIC = !CG_ITALIC; }
368 
369 <ICOR>[,.] {
370  {
371     printf("\"%s\", line %d: do not use \\/ before \"%s\"\n",
372 	   file_name, line_count, yytext);
373     ++warn_count;
374     BEGIN(INITIAL);
375  }}
376 
377 <GETICOR,ICOR>{ws} { ; }
378 
379 <GETICOR,ICOR>~ { ; }
380 
381 <GETICOR,ICOR>[^\n] {
382   {
383     unput(yytext[0]);
384     BEGIN(INITIAL);
385   }}
386 
387 "\\"[exg]?(def|newcommand)[^\n\{]+ 	BEGIN(DEF);
388 
389 <DEF>{b_group} { ++def_count; }
390 
391 <DEF>{e_group} { --def_count;
392 		 if(def_count == 0)
393 		     BEGIN(INITIAL); }
394 
395 <DEF>. { ; }
396 
397 "\\"newenvironment"{"[a-zA-Z]+"}"[^\n\{]+ 	BEGIN(ENV_DEF);
398 
399 <ENV_DEF>{b_group} { ++def_count; }
400 
401 <ENV_DEF>{e_group} { --def_count;
402 		 if(def_count == 0)
403 		     BEGIN(DEF); }
404 
405 <ENV_DEF>. { ; }
406 
407 {b_math} {
408     if(CG_TYPE == 4 || CG_TYPE == 5)
409 	print_bad_match(yytext,4);
410     else
411     {
412 	push( yytext, 4, line_count);
413     }}
414 
415 {e_math} {  g_checkend(4); }
416 
417 {b_display} {
418     if(CG_TYPE == 4 || CG_TYPE == 5)
419 	print_bad_match(yytext,5);
420     else
421     {
422 	push( yytext, 5, line_count);
423     }}
424 
425 
426 {e_display} {  g_checkend(5);     BEGIN(AFTER_DISPLAY);}
427 
428 <AFTER_DISPLAY>{punct} {
429 
430     printf( "\"%s\", line %d: punctuation mark \"%s\" should be placed before end of displaymath\n",
431 	   file_name, line_count, yytext);
432     ++warn_count ;
433 
434   BEGIN(INITIAL); }
435 
436 <AFTER_DISPLAY>(\n|.) { unput(yytext[0]); BEGIN(INITIAL); }
437 
438 <ICOR,INITIAL,GETICOR>{punct}/("\$"|"\\)") { if (CG_TYPE == 4)
439        {
440 	 printf( "\"%s\", line %d: punctuation mark \"%s\" should be placed after end of math mode\n",
441 		file_name, line_count, yytext);
442 	 ++warn_count ;
443 	 BEGIN(INITIAL);
444        }}
445 
446 {math} {
447 
448     if(CG_TYPE == 5)
449 	print_bad_match(yytext, 4);
450     else
451 
452     if(CG_TYPE == 4)
453     {
454 	e_checkend(4, yytext);
455     }
456     else
457     {
458 	push( yytext, 4, line_count);
459     }}
460 
461 
462 {display}  {
463 
464     if(CG_TYPE == 4)
465 	print_bad_match(yytext,5);
466     else
467 
468     if(CG_TYPE == 5)
469     {
470 	e_checkend(5, yytext);
471         BEGIN(AFTER_DISPLAY);
472     }
473     else
474     {
475 	push( yytext, 5, line_count);
476     }}
477 
478 \\begingroup/[^a-zA-Z]  {
479  {
480     push((unsigned char *)"\\begingroup", 1, line_count);
481  }}
482 
483 
484 \\endgroup/[^a-zA-Z]  {
485  {
486     g_checkend(1);
487  }}
488 
489 
490 \\begin[ \t]*"{" { BEGIN(B_ENVIRONMENT); }
491 
492 \\begin[ \t]*(%[^\n]*)?/\n {
493  {
494 
495     printf("\"%s\", line %d: {argument} missing for \\begin\n",
496 	   file_name, line_count) ;
497     ++warn_count;
498  }}
499 
500 <B_ENVIRONMENT>[^\}\n]+ {
501  {
502     if (strcmp( yytext, "verbatim" ) == 0 )
503 	{
504 	 input();
505 	 BEGIN(VERBATIM);
506 	}
507     else
508 	{
509     	 push(yytext, 2, line_count);
510 
511 	 if (   strcmp (yytext, "sl" ) == 0
512 	     || strcmp (yytext, "it" ) == 0)
513 	   CG_ITALIC = 1;
514 	 else if (strcmp (yytext, "normalshape") == 0)
515 	   CG_ITALIC = 0;
516 	 else if (strcmp (yytext, "em") == 0)
517 	   CG_ITALIC = !CG_ITALIC;
518 
519  	 input();
520 	 BEGIN(INITIAL);
521 	}
522  }}
523 
524 <VERBATIM>\\end[ \t]*\{verbatim\} { BEGIN(INITIAL); }
525 
526 <VERBATIM>\t {
527      printf("\"%s\", line %d: TAB character in verbatim environment\n",
528 	   file_name, line_count) ;
529     ++warn_count;
530  }
531 
532 <VERBATIM>. { ; }
533 
534 <VERBATIM>\n { ++line_count; }
535 
536 
537 <ICOR,INITIAL,GETICOR>\\verb\*?. {
538           verb_char = yytext[yyleng-1];
539 	  BEGIN(VERB);
540 	}
541 
542 <VERB>\n {
543   printf("\"%s\", line %d: \\verb should not contain end of line characters\n",
544 	 file_name, line_count) ;
545   ++line_count;
546 }
547 
548 <VERB>. {
549   if ( *yytext == verb_char )
550     BEGIN(INITIAL);
551 }
552 
553 
554 \\end[ \t]*"{" { BEGIN(E_ENVIRONMENT); }
555 
556 \\end[ \t]*(%[^\n]*)?/\n {
557  {
558     printf("\"%s\", line %d: {argument} missing for \\end\n",
559 	   file_name, line_count) ;
560     ++warn_count;
561  }}
562 
563 
564 <E_ENVIRONMENT>[^\}\n]+ {
565  {
566     e_checkend(2, yytext);
567     input();
568 
569     BEGIN(INITIAL);
570  }}
571 
572 
573 <ICOR,INITIAL,GETICOR>{ws}({letter}".")*{letter}*{l_letter}"."/{non_par_ws}+{l_letter}    {
574  {
575     linecount();
576     printf( "\"%s\", line %d: missing `\\ ' after \"%s\"\n",
577 	   file_name, line_count, ++yytext);
578     ++warn_count ;
579     BEGIN(INITIAL);
580  }}
581 
582 <ICOR,INITIAL,GETICOR>({l_letter}".")*{letter}*{l_letter}"."/{non_par_ws}+{l_letter}    {
583  {
584     printf( "\"%s\", line %d: missing `\\ ' after \"%s\"\n",
585 	   file_name, line_count, yytext);
586     ++warn_count ;
587     BEGIN(INITIAL);
588  }}
589 
590 <ICOR,INITIAL,GETICOR>{non_abbrev}/{non_par_ws}{u_letter}   {
591  {
592    linecount();
593    printf("\"%s\", line %d: missing `\\@' before `.' in \"%s\"\n",
594 	  file_name, line_count, yytext);
595    ++warn_count ;
596    BEGIN(INITIAL);
597  }}
598 
599 <ICOR,INITIAL,GETICOR>({hard_space}{space}|{space}{hard_space})  {
600 
601     printf("\"%s\", line %d: double space at \"%s\"\n",
602 	   file_name, line_count, yytext);
603     ++warn_count;
604 	linecount();
605     BEGIN(INITIAL);
606   }
607 
608 {c_bin}{ws}?(\\(\.|\,|\;|\:))*{ws}?\\ldots{ws}?(\\(\.|\,|\;|\:))*{ws}?{c_bin} {
609 	printf("\"%s\", line %d: \\ldots should be \\cdots in \"%s\"\n",
610 	   file_name, line_count, yytext);
611 	++warn_count;
612 	linecount();
613   }
614 
615 <ICOR,INITIAL,GETICOR>[^\\]{l_bin}{ws}?(\\(\.|\,|\;|\:))*{ws}?\\cdots{ws}?(\\(\.|\,|\;|\:))*{ws}?[^\\]{l_bin} {
616 	printf("\"%s\", line %d: \\cdots should be \\ldots in \"%s\"\n",
617 	   file_name, line_count, yytext);
618 	++warn_count;
619 	linecount();
620     BEGIN(INITIAL);
621   }
622 
623 {c_bin}{ws}?(\\(\.|\,|\;|\:))*{ws}?"."+{ws}?(\\(\.|\,|\;|\:))*{ws}?{c_bin} {
624 	printf("\"%s\", line %d: Dots should be \\cdots in \"%s\"\n",
625 	   file_name, line_count, yytext);
626 	++warn_count;
627 	linecount();
628   }
629 
630 <ICOR,INITIAL,GETICOR>[^\\]{l_bin}{ws}?(\\(\.|\,|\;|\:))*{ws}?"."+{ws}?(\\(\.|\,|\;|\:))*{ws}?[^\\]{l_bin} {
631 	printf("\"%s\", line %d: Dots should be \\ldots in \"%s\"\n",
632 	   file_name, line_count, yytext);
633 	++warn_count;
634 	linecount();
635     BEGIN(INITIAL);
636   }
637 
638 
639 <ICOR,INITIAL,GETICOR>\.\.\. {
640     printf("\"%s\", line %d: Dots should be ellipsis \"%s\"\n",
641 	   file_name, line_count, yytext);
642     ++warn_count;
643     BEGIN(INITIAL);
644   }
645 
646 <ICOR,INITIAL,GETICOR>\\label\{[^#$%^&*+={\~"<>\n\t }]*[#$%^&*+={\~"<>\n\t ][^%}]*\} {
647     linecount();
648     printf("\"%s\", line %d: bad character in label \"%s\", see C.10.2\n",
649            file_name, line_count, yytext);
650   }
651 
652 <ICOR,INITIAL,GETICOR>{par}"\\"ref/[^A-Za-z]  {
653     linecount();
654     BEGIN(INITIAL);
655   }
656 
657 <ICOR,INITIAL,GETICOR>{ws}"\\"ref/[^A-Za-z]  {
658     linecount();
659     printf("\"%s\", line %d: perhaps you should insert a `~' before \"%s\"\n",
660 	   file_name, line_count, ++yytext);
661     BEGIN(INITIAL);
662   }
663 
664 <ICOR,INITIAL,GETICOR>{ws}"\\"footnote/[^A-Za-z]  {
665     linecount();
666     printf("\"%s\", line %d: whitespace before footnote in \"%s\"\n",
667 	   file_name, line_count, ++yytext);
668     BEGIN(INITIAL);
669   }
670 
671 
672 {primitive}/[^a-zA-Z] {
673  {
674     printf("\"%s\", line %d: Don't use \"%s\" in LaTeX documents\n",
675 	   file_name, line_count, yytext);
676     ++warn_count ;
677  }}
678 
679 \\left{ws}*\\?. { linecount() ;}
680 \\right{ws}*\\?. {	linecount(); }
681 
682 <ICOR,INITIAL,GETICOR>[^\{]\\{font_spec}/[ \t]*"{" {
683  {
684    linecount();
685     printf("\"%s\", line %d: Fontspecifiers don't take arguments. \"%s\"\n",
686 	   file_name, line_count, yytext);
687     ++warn_count;
688   /*    (void) input(); */
689     BEGIN(INITIAL);
690  }}
691 
692 \\([a-zA-Z\@]+\@[a-zA-Z\@]*|[a-zA-Z\@]*\@[a-zA-Z\@]+) {
693  {
694     printf("\"%s\", line %d: Do not use @ in LaTeX macro names. \"%s\"\n",
695 	   file_name, line_count, yytext);
696     ++warn_count;
697  }}
698 
699 <ICOR,INITIAL,GETICOR>{ws}"'"+{letter}+ {
700  {
701    linecount();
702     printf("\"%s\", line %d: Use ` to begin quotation, not ' \"%s\"\n",
703 	   file_name, line_count, yytext);
704     ++warn_count;
705     BEGIN(INITIAL);
706  }}
707 
708 <ICOR,INITIAL,GETICOR>{letter}+"`" {
709  {
710     printf("\"%s\", line %d: Use ' to end quotation, not ` \"%s\"\n",
711 	   file_name, line_count, yytext);
712     ++warn_count;
713     BEGIN(INITIAL);
714  }}
715 
716 
717 <ICOR,INITIAL,GETICOR>{ws}+{punct} {
718  {
719     printf("\"%s\", line %d: Whitespace before punctation mark in \"%s\"\n",
720 	   file_name, line_count, yytext);
721     ++warn_count;
722 	linecount();
723     BEGIN(INITIAL);
724  }}
725 
726 "%"  { BEGIN(COMMENT); }
727 
728 <COMMENT>\n	{ BEGIN(INITIAL); ++line_count; }
729 
730 <COMMENT>.	{ ; }
731 
732 
733 \\(input|include)([ \t]|"{")	{ BEGIN(INCLUDE); }
734 
735 <INCLUDE>[^\}\n]+	{
736  {
737 	if ( strstr(yytext,".sty") == NULL )
738 	{
739 	  printf("** %s:\n", yytext);
740 	  input_file(yytext);
741 	}
742 	else
743 	{
744 		printf("\"%s\", line %d: Style file `%s\' omitted.\n",
745 			file_name,
746 			line_count,
747 			yytext);
748 		input();
749 	}
750 	BEGIN(INITIAL);
751  }}
752 
753 \\endinput/[^A-Za-z] |
754 <<EOF>> {
755 	  if (--istackp < 0)
756 		  yyterminate();
757 
758 	  else
759 		{
760 		  fclose(yyin);
761 	  	  f_checkend(file_name);
762 		  yy_switch_to_buffer(istack[istackp].stream);
763 		  free(file_name);
764 		  line_count = istack[istackp].linenum;
765 		  file_name = istack[istackp].name;
766 		  input();
767 		  BEGIN(INITIAL);
768 		}
769 
770 	}
771 
772 
773 . { ; }
774 %%
775 int main( argc, argv )
776 int argc;
777 char *argv[];
778 {
779     /* allocate initial stacks */
780     gstack = (tex_group *)malloc(gstack_size * sizeof(tex_group));
781     istack = (input_ *)malloc(istack_size * sizeof(input_));
782     if ( gstack == NULL || istack == NULL ) {
783 	fprintf(stderr, "%s: not enough memory for stacks\n", PROGNAME);
784 	exit(3);
785     }
786 
787     if(argc > 1)
788     {
789         if ( (file_name = (char*) malloc(strlen(argv[1]) + 5)) == NULL ) {
790 		fprintf(stderr, "%s: out of memory\n", PROGNAME);
791 		exit(3);
792 	}
793 
794 	strcpy(file_name, argv[1]);
795 	strcat(file_name, ".tex" );
796 
797 	if ((yyin = fopen( file_name, "r")) != NULL )
798 	{
799 	    push(file_name, 3, 1);
800 	    yylex();
801 	    f_checkend(file_name);
802 	}
803 	else {
804                  file_name[strlen(file_name) - 4] = '\0';
805 		 if ((yyin = fopen( file_name, "r")) != NULL )
806 		 {
807 		     push(file_name, 3, 1);
808 		     yylex();
809 		     f_checkend(file_name);
810 		 }
811 		 else
812 		     fprintf(stderr,
813 			     "%s: Could not open : %s\n",PROGNAME, argv[1]);
814 	     }
815     }
816     else
817     {
818 	printf("\n* %s *\n\n",PROGNAME);
819 	printf("\t...a consistency checker for LaTeX documents.\n");
820 	printf("$Id: lacheck.lex,v 1.26 1998/03/07 07:46:45 abraham Exp $\n\n");
821 
822 	printf("Usage:\n\tlacheck filename[.tex] <return>\n\n\n");
823 
824 	printf("\tFrom within Emacs:\n\n");
825 	printf("\tM-x compile RET lacheck filename[.tex] RET\n\n");
826 	printf("\tUse C-x ` to step through the messages.\n\n");
827 	printf("\n\tThe found context is displayed in \"double quotes\"\n\n");
828 	printf("Remark:\n\tAll messages are only warnings!\n\n");
829 	printf("\tYour document may be right even though LaCheck says ");
830 	printf("something else.\n\n");
831     }
832     return(0);
833 }
834 
835 #ifdef NEED_STRSTR
836 char *
837 strstr(string, substring)
838     register char *string;	/* String to search. */
839     char *substring;		/* Substring to try to find in string. */
840 {
841     register char *a, *b;
842 
843     /* First scan quickly through the two strings looking for a
844      * single-character match.  When it's found, then compare the
845      * rest of the substring.
846      */
847 
848     b = substring;
849     if (*b == 0) {
850 	return string;
851     }
852     for ( ; *string != 0; string += 1) {
853 	if (*string != *b) {
854 	    continue;
855 	}
856 	a = string;
857 	while (1) {
858 	    if (*b == 0) {
859 		return string;
860 	    }
861 	    if (*a++ != *b++) {
862 		break;
863 	    }
864 	}
865 	b = substring;
866     }
867     return (char *) 0;
868 }
869 #endif /* NEED_STRSTR */
870 
871 void push(p_name, p_type, p_line)
872 unsigned char *p_name;
873 int p_type;
874 int p_line;
875 {
876     if ( gstackp == gstack_size ) {	/* extend stack */
877 	gstack_size *= 2;
878 	gstack = (tex_group *)realloc(gstack, gstack_size * sizeof(tex_group));
879 	if ( gstack == NULL ) {
880 		fprintf(stderr, "%s: stack out of memory", PROGNAME);
881 	exit(3);
882     }
883     }
884 
885     if ( (gstack[gstackp].s_name =
886 		(unsigned char *)malloc(strlen((char *)p_name) + 1)) == NULL ||
887          (gstack[gstackp].s_file =
888 		(char *)malloc(strlen(file_name) + 1)) == NULL ) {
889 	fprintf(stderr, "%s: out of memory\n", PROGNAME);
890 	exit(3);
891     }
892 
893     strcpy((char *)gstack[gstackp].s_name,(char *)p_name);
894     gstack[gstackp].s_type = p_type;
895     gstack[gstackp].s_line = p_line;
896     gstack[gstackp].italic = (  (p_type == 4 || p_type == 5)
897 			      ? 1
898 			      : (  gstackp
899 				 ? gstack[gstackp - 1].italic
900 				 : 0));
901     strcpy(gstack[gstackp].s_file,file_name);
902     ++gstackp;
903 
904 }
905 
906 void input_file(file_nam)
907 char *file_nam;
908 {
909     char *tmp_file_name;
910     FILE *tmp_yyin;
911 
912     if ( (tmp_file_name = (char*) malloc(strlen(file_nam) + 5)) == NULL ) {
913 	fprintf(stderr, "%s: out of memory\n", PROGNAME);
914 	exit(3);
915     }
916     strcpy(tmp_file_name,file_nam);
917 
918     if (istackp == istack_size) {	/* extend stack */
919 	istack_size *= 2;
920 	istack = (input_ *)realloc(istack, istack_size * sizeof(input_));
921 	if ( istack == NULL ) {
922 		fprintf(stderr, "%s: \\input stack out of memory\n", PROGNAME);
923 	exit(3);
924         }
925     }
926 
927     istack[istackp].stream = YY_CURRENT_BUFFER;
928     istack[istackp].linenum = line_count;
929     istack[istackp].name = file_name;
930     ++istackp;
931 
932     (void) strcat(tmp_file_name, ".tex");
933     if ((tmp_yyin = fopen( tmp_file_name, "r")) != NULL )
934 	{
935 	  yyin = tmp_yyin;
936 	  yy_switch_to_buffer(yy_create_buffer(yyin,YY_BUF_SIZE));
937 	  file_name = tmp_file_name;
938 	  push(file_name, 3, 1);
939           line_count = 1;
940 	}
941     else {
942           tmp_file_name[strlen(tmp_file_name) - 4] = '\0';
943 	  if ((tmp_yyin = fopen( tmp_file_name , "r")) != NULL )
944 	    {
945 		yyin = tmp_yyin;
946 	   	yy_switch_to_buffer(yy_create_buffer(yyin,YY_BUF_SIZE));
947 		file_name = tmp_file_name;
948 		push(file_name, 3, 1);
949    	        line_count = 1;
950 	    }
951           else
952 	  {
953 	       --istackp;
954 	       free(tmp_file_name);
955 	       printf("\"%s\", line %d: Could not open \"%s\"\n",
956 			file_name,
957 			line_count,
958 			file_nam);
959 	       input();
960 	  }
961 	 }
962 }
963 
964 void pop()
965 {
966     if ( gstackp == 0 )
967     {
968        	fprintf(stderr, "%s: Stack underflow\n", PROGNAME);
969 	exit(4);
970     }
971     --gstackp;
972 
973     free(gstack[gstackp].s_name);
974     free(gstack[gstackp].s_file);
975 }
976 
977 char *bg_command(name)
978 char *name;
979 {
980 
981     switch (CG_TYPE) {
982 
983     case 2:
984 	(void) strcpy( returnval, "\\begin{" );
985 	(void) strcat( returnval, (char *) name);
986 	(void) strcat( returnval, "}" );
987 	break;
988 
989     case 3:
990 	(void) strcpy( returnval, "beginning of file " );
991 	(void) strcat( returnval, (char *) name);
992 	break;
993 
994     case 4:
995 	(void) strcpy( returnval, "math begin " );
996 	(void) strcat( returnval, (char *) name);
997 	break;
998 
999     case 5:
1000 	(void) strcpy( returnval, "display math begin " );
1001 	(void) strcat( returnval, (char *) name);
1002 	break;
1003 
1004     default:
1005     	(void) strcpy( returnval, name );
1006 
1007     }
1008 
1009     return ((char *)returnval);
1010 }
1011 
1012 char *eg_command(name,type)
1013 int type;
1014 char *name;
1015 {
1016 
1017     switch (type) {
1018 
1019     case 2:
1020 	(void) strcpy( returnval, "\\end{" );
1021 	(void) strcat( returnval, (char *) name);
1022 	(void) strcat( returnval, "}" );
1023 	break;
1024 
1025     case 3:
1026 	(void) strcpy( returnval, "end of file " );
1027 	(void) strcat( returnval, (char *) name);
1028 	break;
1029 
1030     case 4:
1031 	(void) strcpy( returnval, "math end " );
1032 	(void) strcat( returnval, (char *) name);
1033 	break;
1034 
1035     case 5:
1036 	(void) strcpy( returnval, "display math end " );
1037 	(void) strcat( returnval, (char *) name);
1038 	break;
1039 
1040     default:
1041     	(void) strcpy( returnval, name );
1042 	break;
1043     }
1044 
1045     return ((char *)returnval);
1046 }
1047 
1048 
1049 void g_checkend(n)
1050 int n;
1051 {
1052     if ( check_top_level_end(yytext,n) == 1 ) {
1053        if (  CG_TYPE != n  )
1054 	 print_bad_match(yytext,n);
1055        else
1056 	pop();
1057     }
1058 }
1059 
1060 void e_checkend(n, name)
1061 int n;
1062 char *name;
1063 {
1064    if ( check_top_level_end(name,n) == 1 )
1065     {
1066      if (  CG_TYPE != n  ||  strcmp( CG_NAME, name ) != 0 )
1067     	print_bad_match(name,n);
1068 
1069      pop();
1070 
1071     }
1072 }
1073 
1074 void f_checkend(name)
1075 char *name;
1076 {
1077     if ( check_top_level_end(name,3) == 1 )
1078      {
1079        if (  CG_TYPE != 3  ||  strcmp( CG_NAME, name ) != 0 )
1080 
1081     	while( CG_TYPE != 3  )
1082 	{
1083 	  print_bad_match(name,3);
1084           pop();
1085           if (gstackp <= 0) return;
1086         }
1087 
1088          pop();
1089      }
1090 }
1091 
1092 void print_bad_match(end_command,type)
1093 char *end_command;
1094 int type;
1095 {
1096 	  printf("\"%s\", line %d: <- unmatched \"%s\"\n",
1097 	         file_name,
1098 		 line_count,
1099 		 eg_command( end_command , type) ) ;
1100 
1101 	  if (gstackp > 0) {
1102 		 printf("\"%s\", line %d: -> unmatched \"%s\"\n",
1103 		 	 CG_FILE,
1104 		 	 CG_LINE,
1105 		 	 bg_command( CG_NAME ) ) ;
1106 		 warn_count += 2;
1107 	  }
1108 }
1109 
1110 int check_top_level_end(end_command,type)
1111 char *end_command;
1112 int type;
1113 {
1114     if ( gstackp == 0 )
1115 	{
1116 	 printf("\"%s\", line %d: \"%s\" found at top level\n",
1117 	       file_name,
1118 	       line_count,
1119 	       eg_command( end_command, type )) ;
1120 	 ++warn_count;
1121          return(0);
1122 	}
1123     else
1124     	return(1);
1125 }
1126 
1127 void linecount()
1128 {
1129   int i;
1130   for (i = 0; i < yyleng; i++)
1131     if(yytext[i] == '\n')
1132       line_count++;
1133 }
1134 
1135