#! /usr/bin/gawk -f # Last edited on 2010-06-01 13:37:09 by stolfilocal #================================================== # Transforms TeX error messages into a format compatible with # EMACS' next-error facility # Usage: | tex-error-filter [ -v badrefs=0 ] #================================================== BEGIN { abort = -1; file[0] = "stdin"; if (badrefs == "") { badrefs = 1; } depth = 0; inerror = 0; } (abort >= 0) { print; next; } # Get next input line from stdin or from nxlin: (nxlin != "") { lin = nxlin; nxlin = $0; $0 = lin; } # Make sure it is only one line: /[\n]/ { lin = $0; chop_other_lin(); $0 = lin; } /^[!]/ { # Start of an error block if (inerror) { prog_error("missing locative line"); } lin = $0; errmsg = substr(lin,2); inerror = 1; next; } (match($0, /^[l][.][0-9]+/)) { # End of an error block (maybe only one line) lin = $0; lnum = substr(lin, 3, RLENGTH-2); rest = substr(lin, RLENGTH+1); bnum = lnum; gsub(/./, " ", bnum); errmsg = ( errmsg "\n " bnum rest); print_error(file[depth], lnum, errmsg); inerror = 0; next; } /Warning:/ { # Latex warning message if (inerror) { prog_error("nested warning"); } lin = $0; if (! match(lin, /on[ ]*input[ ]*line[ ]*[0-9]+[.]/)) { # Well, let's read the next line and hope it is not an error line: get_nxlin(); if ( \ (nxlin != "") && \ (! match(nxlin, /^[!]/)) && \ (match((lin nxlin), /on[ ]*input[ ]*line/)) \ ) { lin = (lin nxlin); nxlin = ""; chop_other_lin(); } } if (match(lin, /on[ ]*input[ ]*line/)) { errmsg = substr(lin, 1, RSTART-1); lnum = substr(lin, RSTART+RLENGTH); gsub(/^[ ]+/, "", lnum); gsub(/[. ]*$/, "", lnum); if (lnum !~ /^[0-9]+$/) { prog_error("bad warning number"); } print_error(file[depth], lnum, errmsg); } else { # Unnumbered warning? printf "%s\n", lin; } next; } (inerror) { # Continuation of error block errmsg = ( errmsg "\n " $0); next; } /./ { # TeX mumbling line; track file changes print; lin = $0; if (justopen) { enter_paren(); } while (match(lin, /^[^()]*[()]/)) { c = substr(lin, RLENGTH, 1); lin = substr(lin, RLENGTH + 1); if (c == "(") { depth++; if (lin == "") { justopen = 1; } else { enter_paren(); } } else { if (depth == 0) { prog_error("too many close parens"); } if (file[depth] != file[depth-1]) { # printf "%*sexit %s\n", 2*depth, "", file[depth] > "/dev/stderr"; } depth--; justopen = 0; } } next; } END { if (inerror) { prog_error("missing locative line"); } if (depth > 0) { prog_error("too many open parens"); } } function prog_error(msg) { if (inerror) { print_error("???","???",errmsg); } printf "program error: %s\n", msg > "/dev/stderr"; abort = 1; } function enter_paren() { # parses a non-empty input string "lin" that follows # an open parenthesis into a possible file name # and pushes it onto the file stack. If there is # no name there, pushes the current file name, again. if (match(lin, /^[^(){}<> ,;]*[.](tex|iso|sty|cls|aux|bbl|toc|lof|lot|idx|fd)/)) { file[depth] = substr(lin, 1, RLENGTH); # printf "%*senter %s\n", 2*depth, "", file[depth] > "/dev/stderr"; } else { file[depth] = file[depth-1]; } justopen = 0; } function get_nxlin() { # Ensures "nxlin" is not empty, if possible: if (nxlin != "") { return; } getline nxlin } function chop_other_lin() { # Removes from "lin" the second and futher lines, if any, # pushing them onto "nxlin": if (match($0, /[\n]/)) { nxlin = ( substr(lin, RSTART+1) nxlin ); lin = substr(lin, 1, RSTART-1); } } function print_error(fname,lnum,msg) { if (badrefs || (msg !~ /Reference.* undefined/)) { printf "%s:%s: %s\n", fname, lnum, msg; } }