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