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