1 #LyX 2.1 created this file. For more info see http://www.lyx.org/
6 \use_default_options false
10 \maintain_unincluded_children false
12 \language_package default
13 \inputencoding default
17 \font_typewriter default
19 \font_default_family default
20 \use_non_tex_fonts false
26 \default_output_format default
28 \bibtex_command default
29 \index_command default
30 \paperfontsize default
35 \use_package amsmath 0
36 \use_package amssymb 0
38 \use_package mathdots 1
39 \use_package mathtools 0
41 \use_package undertilde 0
43 \cite_engine_type numerical
47 \paperorientation portrait
57 \paragraph_separation indent
58 \paragraph_indentation default
59 \quotes_language english
62 \paperpagestyle default
63 \tracking_changes false
73 LyX and Literate Programming
74 \begin_inset Newline newline
82 \begin_inset Newline newline
85 edmar-w-jr@technologist.com
89 \begin_layout Plain Layout
90 Modified by Bernard Michael Hurley bernardh@westherts.ac.uk ---- Don't blame
91 Edmar for any errors that have crept in!
99 \begin_layout Abstract
104 This example program is provided for educational use only.
105 The functionality in this C program has been superceded by the equivalent
108 examples/listerrors.lyx
110 which should be installed in the LyX scripts directory.
117 \begin_layout Plain Layout
129 \begin_layout Standard
130 \begin_inset CommandInset toc
131 LatexCommand tableofcontents
138 \begin_layout Section
142 \begin_layout Standard
143 After typesetting a document, LyX scans the LaTeX log file looking for errors.
144 For each error found, the line number is obtained and a error box is displayed
145 in the LyX screen at that position.
148 \begin_layout Standard
149 To use this feature to view compilation errors while working with literate
150 documents, we need a program that filters the compilation errors and puts
151 them in a format suitable for LyX reading it.
155 \begin_layout Standard
156 In this document we present a filter that recognizes compilation error messages
157 from noweb, gnu C, and the IBM C compiler (xlc).
160 \begin_layout Standard
161 The filter is required to read from standard input, parse for error messages
162 and copy the error messages to the standard output.
163 During the output process, the filter must present the error messages in
164 a format that LyX can interpret, currently, the LaTeX error message format.
165 Of course, nothing will prevent future LyX releases from being able to
166 read other formats as well (like gcc error messages for example).
167 This mechanism is necessary to fully explore the literate programming tool's
171 \begin_layout Section
184 main (int argc, char **argv)
196 switch (argv[1][0]) {
204 <<Scan input for noweb error messages>>
216 <<Scan input for xlc error messages>>
228 <<AIX system using both noweb and xlc>>
244 <<Solaris and Linux systems using both noweb and gcc>>
260 <<Scan input for gcc error messages>>
276 <<Scan input for gcc error messages>>
292 <<Function prototypes>>=
296 int main (int argc, char **argv);
303 \begin_layout Section
307 \begin_layout Standard
308 We resort to some global variables to allow access from several different
310 These are the buffer and related pointers used during the parse of the
315 <<Global variables>>=
319 char buffer[200][200];
320 \begin_inset Newline newline
324 \begin_inset Newline newline
328 \begin_inset Newline newline
332 \begin_inset Newline newline
338 \begin_layout Section
342 \begin_layout Standard
343 The output format mimics the TeX error messages format.
344 This function prints a number of lines residing in the global variable
349 , a program name and line number.
350 There is no special requirement on the input strings, they can be anything.
354 \begin_layout Plain Layout
355 This function has been slightly changed from EW's original to make scanning
356 a bit easier with LaTeX::scanLogFile().
357 The test has been added because LyX can crash if empty lines are allowed
358 here --- I can't figure out why! --- BMH
375 output_error (int buf_size, int error_line, char *tool)
391 fprintf(stdout, "! Build Error: ==> %s ==>
397 fprintf(stdout, " ...
411 for (i=0; i<buf_size; i++)
415 if (strlen(buffer[i]) != 0)
419 fprintf(stdout, "%s", buffer[i]);
441 <<Function prototypes>>=
445 void output_error (int buf_size, int error_line, char *tool);
452 \begin_layout Section
453 Functions Implementation
456 \begin_layout Standard
457 Both noweave and notangle routines, always output one single line for each
458 error found, thus to scan the buffer for noweb error messages is enough
459 to exam one input line at a time.
460 Note that the noweb software does not provide a line error number, so all
461 errors boxes related to noweb messages will be displayed at the beginning
466 <<Scan input for noweb error messages>>=
478 while (fgets(buffer[0], 200, stdin)) {
486 output_error(1, err_line, "noweb");
501 \begin_layout Standard
502 The examination itself is very inefficient.
503 Unfortunately noweb doesn't have any characteristic that would help to
504 identify one of its error messages.
505 The solution is to collect all possible output messages in an array of
506 strings, and turn the examination process into a linear search in this
515 <<Global variables>>=
519 char *noweb_msgs[] = {
523 "couldn't open file",
527 "couldn't open temporary file",
531 "error writing temporary file",
543 "Bad format sequence",
547 "Can't open output file",
551 "Can't open temporary file",
555 "Capacity exceeded:",
559 "Ignoring unknown option -",
563 "This can't happen:",
567 "non-numeric line number in"
579 char *noweb_msgs_mimic_gcc[] = {
583 ": unescaped << in documentation chunk"
594 \begin_layout Standard
595 A noweb error message can be any string that contains a matching pair of
608 > >, or any of the above strings
617 \begin_inset Newline newline
621 \begin_inset Newline newline
624 noweb_try (int buf_line)
625 \begin_inset Newline newline
629 \begin_inset Newline newline
633 \begin_inset Newline newline
637 \begin_inset Newline newline
641 \begin_inset Newline newline
644 b = buffer[buf_line];
645 \begin_inset Newline newline
649 \begin_inset Newline newline
653 \begin_inset Newline newline
656 for (i=0; i<1; i++) {
657 \begin_inset Newline newline
660 s = (char *)strstr (b, noweb_msgs_mimic_gcc[i]);
661 \begin_inset Newline newline
665 \begin_inset Newline newline
668 t = (char *)strchr(buffer[buf_line], ':');
669 \begin_inset Newline newline
672 err_line = atoi(t+1);
673 \begin_inset Newline newline
676 t = buffer[buf_line];
677 \begin_inset Newline newline
681 \begin_inset Newline newline
684 while (*(t++) = *(s++));
685 \begin_inset Newline newline
689 \begin_inset Newline newline
693 \begin_inset Newline newline
697 \begin_inset Newline newline
700 s = (char *)strstr(b, "<<");
701 \begin_inset Newline newline
705 \begin_inset Newline newline
708 s = (char *)strstr(s+2, ">>");
709 \begin_inset Newline newline
713 \begin_inset Newline newline
717 \begin_inset Newline newline
721 \begin_inset Newline newline
725 \begin_inset Newline newline
728 for (i = 0; i < 12; ++i) {
729 \begin_inset Newline newline
732 s = (char *)strstr (b, noweb_msgs[i]);
733 \begin_inset Newline newline
737 \begin_inset Newline newline
741 \begin_inset Newline newline
745 \begin_inset Newline newline
749 \begin_inset Newline newline
753 \begin_inset Newline newline
757 \begin_inset Newline newline
761 \begin_inset Newline newline
768 <<Function prototypes>>=
772 int noweb_try (int buf_line);
779 \begin_layout Standard
780 The xlc compiler always outputs one single line for each error found, thus
781 to scan the buffer for xlc error messages it is enough to exam one input
786 <<Scan input for xlc error messages>>=
798 while (fgets(buffer[last_buf_line], 200, stdin)) {
806 output_error(1, err_line, "xlc");
821 \begin_layout Standard
822 A xlc error message is easy to identify.
823 Every error message starts with a quoted string with no spaces, a comma,
825 \begin_inset Quotes eld
829 \begin_inset Quotes erd
832 , a space, and some variable text.
833 The following routine tests if a given buffer line matches this criteria:
853 xlc_try (int buf_line)
869 t = buffer[buf_line];
877 while (*s != '"' && *s != ' ' && *s != '
887 if (*t != '"' || *s != '"' || strncmp(s+1, ", line ", 7) != 0)
915 <<Function prototypes>>=
919 int xlc_try (int buf_line);
926 \begin_layout Standard
927 The gcc compiler error messages are more complicated to scan.
928 Each error can span more than one line in the buffer.
929 The good news is that every buffer line on each error has the same pattern,
930 and share the same line number.
931 Thus the strategy will be to accumulate lines in the buffer while the reported
932 line number is still the same.
933 At the time they differ, all the accumulated lines, except the last one,
934 will belong to one single error message, which now can be output-ed to
938 \begin_layout Standard
939 Every gcc error message contains a string with no space followed by a
940 \begin_inset Quotes eld
944 \begin_inset Quotes eld
948 If the next character is a space, then this line is a header of a error
949 message and the next line will detail the line number of the source code
950 where the error was found.
951 Otherwise, the next thing is a integer number followed by another
952 \begin_inset Quotes eld
956 \begin_inset Quotes eld
963 <<Scan input for gcc error messages>>=
983 while (fgets(buffer[last_buf_line], 200, stdin)) {
987 /****** Skip lines until I find an error */
991 s = (char *)strpbrk(buffer[last_buf_line], " :");
995 if (s == NULL || *s == ' ')
999 continue; /* No gcc error found here */
1007 <<gcc error message criteria is to find a "...:999:" or a "...: ">>
1011 /****** OK It is an error message, get line number */
1015 err_line = atoi(s+1);
1019 if (last_err_line == 0 || last_err_line == err_line) {
1023 last_err_line = err_line;
1027 continue; /* It's either a header or a continuation, don't output
1036 /****** Completed the scan of one error message, output it to LyX
1041 discharge_buffer(1);
1049 } while (fgets(buffer[last_buf_line], 200, stdin));
1057 /****** EOF completes the scan of whatever was being scanned */
1061 discharge_buffer(0);
1073 <<gcc error message criteria is to find a "...:999:" or a "...: ">>=
1077 /****** Search first ":" in the error number */
1081 s = (char *)strpbrk(buffer[last_buf_line], " :");
1089 if (s == NULL || *s == ' ')
1093 <<No gcc error found here, but it might terminate the scanning of a previous
1098 /****** Search second ":" in the error number */
1102 t = (char *)strpbrk(s+1, " :");
1103 \begin_inset Newline newline
1106 if (t == NULL || *t == ' ')
1107 \begin_inset Newline newline
1110 <<No gcc error found here, but it might terminate the scanning of a previous
1112 \begin_inset Newline newline
1115 /****** Verify if is all digits between ":" */
1116 \begin_inset Newline newline
1119 if (t != s+1+strspn(s+1, "0123456789"))
1120 \begin_inset Newline newline
1123 <<No gcc error found here, but it might terminate the scanning of a previous
1125 \begin_inset Newline newline
1140 <<No gcc error found here, but it might terminate the scanning of a previous
1153 discharge_buffer(1);
1168 \begin_layout Standard
1169 As we mentioned, when the scan of one gcc error message is completed everything
1170 in the buffer except the last line is one single error message.
1171 But if the scan terminates with a EOF or through finding one line that
1172 does not match the gcc error message criteria, then there is no
1173 \begin_inset Quotes eld
1177 \begin_inset Quotes erd
1180 in the buffer to be concerned with.
1181 In those cases we empty the buffer completely.
1193 <<Function bodies>>=
1201 discharge_buffer (int save_last)
1209 if (last_err_line != 0) {
1213 clean_gcc_messages();
1217 if (save_last != 0) {
1221 output_error(last_buf_line-1, last_err_line, "gcc");
1225 strcpy (buffer[0], buffer[last_buf_line-1]);
1229 last_err_line = err_line;
1245 clean_gcc_messages();
1249 output_error(last_buf_line-1, last_err_line, "gcc");
1277 <<Function prototypes>>=
1281 void discharge_buffer (int save_last);
1288 \begin_layout Standard
1290 \begin_inset Quotes eld
1294 \begin_inset Quotes erd
1297 superfluous information from gcc messages, namely the name of the noweb
1298 file and the line number of the Error.
1302 \begin_layout Plain Layout
1304 For instance, some way of distinguishing between gcc Errors and Warnings
1314 <<Function bodies>>=
1322 clean_gcc_messages ()
1342 int search_len = sprintf(search, ".nw:%d:", last_err_line);
1350 for (index = 0; index < last_buf_line-1; index++) {
1354 tail = (char *)strstr (buffer[index], search);
1358 if ( tail == NULL) {
1362 tail = (char *) strstr (buffer[index], ".nw:");
1394 head = buffer[index];
1398 while (*(head++) = *(tail++));
1418 <<Function prototypes>>=
1422 void clean_gcc_messages ();
1429 \begin_layout Standard
1430 To combine the scan of noweb error messages and xlc error messages is very
1432 We just try each one for every input line:
1436 <<AIX system using both noweb and xlc>>=
1445 \begin_inset Newline newline
1448 while (fgets(buffer[0], 200, stdin)) {
1449 \begin_inset Newline newline
1453 \begin_inset Newline newline
1456 output_error(1, err_line, "noweb");
1457 \begin_inset Newline newline
1460 else if (xlc_try(0))
1461 \begin_inset Newline newline
1464 output_error(1, err_line, "xlc");
1465 \begin_inset Newline newline
1469 \begin_inset Newline newline
1473 \begin_inset Newline newline
1479 \begin_layout Standard
1480 To combine the scan of noweb error messages and gcc error messages is simple
1481 if we realize that it is not possible to find a noweb error message in
1482 the middle of a gcc error message.
1483 So we just repeat the gcc procedure and test for noweb error messages in
1484 the beginning of the scan:
1488 <<Solaris and Linux systems using both noweb and gcc>>=
1508 while (fgets(buffer[last_buf_line], 200, stdin)) {
1512 /****** Skip lines until I find an error */
1516 if (last_buf_line == 0 && noweb_try(0)) {
1520 output_error(1, err_line, "noweb");
1532 s = (char *)strpbrk(buffer[last_buf_line], " :");
1536 if (s == NULL || *s == ' ')
1540 continue; /* No gcc error found here */
1548 <<gcc error message criteria is to find a "...:999:" or a "...: ">>
1552 /****** OK It is an error, get line number */
1556 err_line = atoi(s+1);
1560 if (last_err_line == 0 || last_err_line == err_line) {
1564 last_err_line = err_line;
1568 continue; /* It's either a header or a continuation, don't output
1577 /****** Completed the scan of one error message, output it to LyX
1582 discharge_buffer(1);
1590 } while (fgets(buffer[last_buf_line], 200, stdin));
1598 /****** EOF completes the scan of whatever was being scanned */
1602 discharge_buffer(0);
1613 \begin_layout Section
1614 Wrapping the code into a file
1626 #include <strings.h>
1634 <<Global variables>>
1638 <<Function prototypes>>
1649 \begin_layout Standard
1650 To build this program, we want to add the
1651 \begin_inset Quotes eld
1655 \begin_inset Quotes erd
1658 option in the tangle command to force gdb to load the file
1667 In accordance with this, we pass the
1668 \begin_inset Quotes eld
1672 \begin_inset Quotes erd
1687 if [ -z "$NOWEB_SOURCE" ]; then NOWEB_SOURCE=Literate.nw; fi
1691 if [ -z "$NOWEB_OUTPUT_DIR" ]; then NOWEB_OUTPUT_DIR=.; fi
1695 notangle -L -Rlisterrors.c ${NOWEB_SOURCE} > ${NOWEB_OUTPUT_DIR}/listerrors.c
1699 gcc -g -o listerrors listerrors.c
1706 \begin_layout Standard
1707 This project can be tangled and compiled from LyX if you set
1713 to call a generic script that always extracts a chunk named
1718 Here is a example of such generic script:
1721 \begin_layout LyX-Code
1723 \begin_inset Newline newline
1726 notangle -Rbuild-script $1 | env NOWEB_SOURCE=$1 NOWEB_OUTPUT_DIR=$r sh
1729 \begin_layout LyX-Code