diff --git a/latexrun b/latexrun index 557a93c..bcb73b2 100755 --- a/latexrun +++ b/latexrun @@ -79,7 +79,7 @@ def main(): action=ArgParserWarnAction, dest='nowarns', default=set(['underfull']), help='Enable/disable warning from CLASS, which can be any package name, ' 'LaTeX warning class (e.g., font), bad box type ' - '(underfull, overfull, loose, tight), or "all"') + '(underfull, overfull, loose, tight), strict parsing (strict-parse), or "all"') arg_parser.add_argument( '-O', metavar='DIR', dest='obj_dir', default='latex.out', help='Directory for intermediate files and control database ' @@ -230,6 +230,21 @@ def mkdir_p(path): pass else: raise +def nested_parenthesis_end(string, opening, closing, lax_checking=False): + """Return index where closing character corresponds to opening character""" + stack = [] + for i, c in enumerate(string): + if c in opening: + stack.append(c) + elif c in closing and stack: + start_ch = stack.pop() + if not lax_checking and opening.index(start_ch) != closing.index(c): + # Mismatch, e.g. expected ')', found '}' + return -1 + if not stack: + return i + return -1 + class DB: """A latexrun control database.""" @@ -1272,16 +1287,18 @@ class LaTeXFilter: self.__file_stack.pop() else: self.__message('warning', None, - "extra `)' in log; file names may be wrong ") + "extra `)' in log; file names may be wrong") elif ch == '{': # TeX uses this for various things we want to ignore, like # file names and print_mark. Consume up to the '}' - epos = self.__data.find('}', self.__pos) - if epos != -1: - self.__pos = epos + 1 - else: + lax_checking = "strict-parse" in self.__suppress + epos = nested_parenthesis_end(self.__data[self.__pos-1:], '{[(', '}])', + lax_checking=lax_checking) + if epos == -1: self.__message('warning', None, "unbalanced `{' in log; file names may be wrong") + else: + self.__pos += epos elif ch == '}': self.__message('warning', None, "extra `}' in log; file names may be wrong") @@ -1423,14 +1440,15 @@ class LaTeXFilter: return # Back up to the end of the known message text self.__pos = origpos + m.end() - if self.__lookingat('\n'): + if self.__lookingatre(r'\s*\n'): # We have a newline, so consume it and look for the # offending text. self.__pos += 1 # If there is offending text, it will start with a font # name, which will start with a \. - if 'hbox' in msg and self.__lookingat('\\'): - self.__consume_line(unwrap=True) + if 'hbox' in msg and self.__lookingatre(r'(\[\]\s?)*\s*\\'): + consumed = self.__consume_line(unwrap=True) + if self.TRACE: print('consuming `<{}>\''.format(consumed)) msg = self.__simplify_message(msg) + ' (page {})'.format(self.__pageno) cls = msg.split(None, 1)[0].lower() self.__message('warning', lineno, msg, cls=cls) diff --git a/test/T-mismatched-brace.tex b/test/T-mismatched-brace.tex new file mode 100644 index 0000000..e80cbec --- /dev/null +++ b/test/T-mismatched-brace.tex @@ -0,0 +1,16 @@ +% Test mismatched braces parsing warning. + +\documentclass{article} + +\begin{document} + + \typeout{Error \{ foo bar } + + baz. + +\end{document} + +%% status: 0 + +%% output: +%% T-mismatched-brace.tex: warning: unbalanced `{' in log; file names may be wrong diff --git a/test/T-mismatched-brace2.tex b/test/T-mismatched-brace2.tex new file mode 100644 index 0000000..859d62d --- /dev/null +++ b/test/T-mismatched-brace2.tex @@ -0,0 +1,19 @@ +% Test mismatched braces parsing warning. + +\documentclass{article} + +\begin{document} + + \typeout{Error \{ foo bar ) \} } + + baz. + +\end{document} + +%% status: 0 + +%% output: +%% T-mismatched-brace2.tex: warning: unbalanced `{' in log; file names may be wrong +%% T-mismatched-brace2.tex: warning: extra `}' in log; file names may be wrong +%% T-mismatched-brace2.tex: warning: extra `)' in log; file names may be wrong + diff --git a/test/run b/test/run index 3562da1..b1323dd 100755 --- a/test/run +++ b/test/run @@ -94,7 +94,7 @@ def test(latexrun_path, latexrun_args, input_path): if input_path.endswith('.tex'): cmd = latexrun_cmd + [os.path.basename(input_path)] elif input_path.endswith('.sh'): - cmd = ['sh', os.path.basename(input_path)] + latexrun_cmd + cmd = ['bash', os.path.basename(input_path)] + latexrun_cmd p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=input_dir or None)