55
66Designed to catch common markup errors including:
77* Unbalanced or mismatched parenthesis, brackets, and braces.
8- * Unbalanced of mismatched \b egin and \end blocks.
8+ * Unbalanced or mismatched \\ begin and \ \ end blocks.
99* Misspelled or invalid LaTeX commands.
1010* Use of forward slashes instead of backslashes for commands.
11- * Table line size mismatches (only \lineii used in a tableii) .
11+ * Table line size mismatches.
1212
13- Command line usage:
14- python texcheck.py [-h] [-k keyword] foobar .tex
13+ Sample command line usage:
14+ python texcheck.py -k chapterheading -m lib/librandomtex * .tex
1515
1616Options:
1717 -m Munge parenthesis and brackets. [0,n) would normally mismatch.
1818 -k keyword: Keyword is a valid LaTeX command. Do not include the backslash.
1919 -d: Delimiter check only (useful for non-LaTeX files).
2020 -h: Help
2121 -s lineno: Start at lineno (useful for skipping complex sections).
22- -v: Verbose. Shows current delimiter and unclosed delimiters .
22+ -v: Verbose. Trace the matching of //begin and //end blocks .
2323"""
2424
2525import re
2626import sets
2727import sys
2828import getopt
2929from itertools import izip , count , islice
30+ import glob
3031
3132cmdstr = r"""
3233 \section \module \declaremodule \modulesynopsis \moduleauthor
@@ -63,19 +64,19 @@ def matchclose(c_lineno, c_symbol, openers, pairmap):
6364 try :
6465 o_lineno , o_symbol = openers .pop ()
6566 except IndexError :
66- msg = "Delimiter mismatch. On line %d, encountered closing '%s' without corresponding open" % (c_lineno , c_symbol )
67- raise Exception , msg
67+ print " \n Delimiter mismatch. On line %d, encountered closing '%s' without corresponding open" % (c_lineno , c_symbol )
68+ return
6869 if o_symbol in pairmap .get (c_symbol , [c_symbol ]): return
69- msg = "Opener '%s' on line %d was not closed before encountering '%s' on line %d" % (o_symbol , o_lineno , c_symbol , c_lineno )
70- raise Exception , msg
70+ print " \n Opener '%s' on line %d was not closed before encountering '%s' on line %d" % (o_symbol , o_lineno , c_symbol , c_lineno )
71+ return
7172
7273def checkit (source , opts , morecmds = []):
7374 """Check the LaTeX formatting in a sequence of lines.
7475
7576 Opts is a mapping of options to option values if any:
7677 -m munge parenthesis and brackets
7778 -d delimiters only checking
78- -v verbose listing of delimiters
79+ -v verbose trace of delimiter matching
7980 -s lineno: linenumber to start scan (default is 1).
8081
8182 Morecmds is a sequence of LaTeX commands (without backslashes) that
@@ -93,7 +94,7 @@ def checkit(source, opts, morecmds=[]):
9394 pairmap = {']' :'[(' , ')' :'([' } # Munged openers
9495 else :
9596 pairmap = {']' :'[' , ')' :'(' } # Normal opener for a given closer
96- openpunct = sets .Set ('([' ) # Set of valid openers
97+ openpunct = sets .Set ('([' ) # Set of valid openers
9798
9899 delimiters = re .compile (r'\\(begin|end){([_a-zA-Z]+)}|([()\[\]])' )
99100 braces = re .compile (r'({)|(})' )
@@ -113,24 +114,7 @@ def checkit(source, opts, morecmds=[]):
113114 for lineno , line in izip (count (startline ), islice (source , startline - 1 , None )):
114115 line = line .rstrip ()
115116
116- if '/' in line and '-d' not in opts :
117- # Warn whenever forward slashes encountered with a LaTeX command
118- for cmd in falsetexcmd .findall (line ):
119- if '\\ ' + cmd in validcmds :
120- print 'Warning, forward slash used on line %d with cmd: /%s' % (lineno , cmd )
121-
122- if '-d' not in opts :
123- # Validate commands
124- nc = line .find (r'\newcommand' )
125- if nc != - 1 :
126- start = line .find ('{' , nc )
127- end = line .find ('}' , start )
128- validcmds .add (line [start + 1 :end ])
129- for cmd in texcmd .findall (line ):
130- if cmd not in validcmds :
131- print r'Warning, unknown tex cmd on line %d: \%s' % (lineno , cmd )
132-
133- # Check balancing of open/close parenthesis and brackets
117+ # Check balancing of open/close parenthesis, brackets, and begin/end blocks
134118 for begend , name , punct in delimiters .findall (line ):
135119 if '-v' in opts :
136120 print lineno , '|' , begend , name , punct ,
@@ -154,8 +138,27 @@ def checkit(source, opts, morecmds=[]):
154138 bracestack .pop ()
155139 except IndexError :
156140 print r'Warning, unmatched } on line %s.' % (lineno ,)
157- if '-v' in opts :
158- print ' --> ' , bracestack
141+
142+ # Optionally, skip LaTeX specific checks
143+ if '-d' in opts :
144+ continue
145+
146+ # Warn whenever forward slashes encountered with a LaTeX command
147+ for cmd in falsetexcmd .findall (line ):
148+ if '822' in line or '.html' in line :
149+ continue # Ignore false positives for urls and for /rfc822
150+ if '\\ ' + cmd in validcmds :
151+ print 'Warning, forward slash used on line %d with cmd: /%s' % (lineno , cmd )
152+
153+ # Validate commands
154+ nc = line .find (r'\newcommand' )
155+ if nc != - 1 :
156+ start = line .find ('{' , nc )
157+ end = line .find ('}' , start )
158+ validcmds .add (line [start + 1 :end ])
159+ for cmd in texcmd .findall (line ):
160+ if cmd not in validcmds :
161+ print r'Warning, unknown tex cmd on line %d: \%s' % (lineno , cmd )
159162
160163 # Check table levels (make sure lineii only inside tableii)
161164 m = tablestart .search (line )
@@ -168,6 +171,11 @@ def checkit(source, opts, morecmds=[]):
168171 if tableend .search (line ):
169172 tablelevel = ''
170173
174+ # Style guide warnings
175+ if 'e.g.' in line or 'i.e.' in line :
176+ print r'Style warning, avoid use of i.e or e.g. on line %d' % (lineno ,)
177+
178+
171179 lastline = lineno
172180 for lineno , symbol in openers :
173181 print "Unmatched open delimiter '%s' on line %d" % (symbol , lineno )
@@ -189,15 +197,28 @@ def main(args=None):
189197 print 'Please specify a file to be checked'
190198 return 1
191199
192- morecmds = [v for k ,v in optitems if k == '-k' ]
193-
194- try :
195- f = open (arglist [0 ])
196- except IOError :
197- print 'Cannot open file %s.' % arglist [0 ]
198- return 2
200+ for i , filespec in enumerate (arglist ):
201+ if '*' in filespec or '?' in filespec :
202+ arglist [i :i + 1 ] = glob .glob (filespec )
199203
200- return (checkit (f , opts , morecmds ))
204+ morecmds = [v for k ,v in optitems if k == '-k' ]
205+ err = []
206+
207+ for filename in arglist :
208+ print '=' * 30
209+ print "Checking" , filename
210+ try :
211+ f = open (filename )
212+ except IOError :
213+ print 'Cannot open file %s.' % arglist [0 ]
214+ return 2
215+
216+ try :
217+ err .append (checkit (f , opts , morecmds ))
218+ finally :
219+ f .close ()
220+
221+ return max (err )
201222
202223if __name__ == '__main__' :
203224 sys .exit (main ())
0 commit comments