1 #LyX 1.6.0svn created this file. For more info see http://www.lyx.org/
5 \textclass literate-article
10 \font_typewriter default
11 \font_default_family default
18 \paperfontsize default
27 \paperorientation portrait
30 \paragraph_separation indent
32 \quotes_language english
35 \paperpagestyle default
36 \tracking_changes false
44 LyX and Literate Programming
45 \begin_inset Newline newline
53 \begin_inset Newline newline
56 edmar-w-jr@technologist.com
60 \begin_layout Plain Layout
61 Modified by Bernard Michael Hurley bernardh@westherts.ac.uk ---- Don't blame
62 Edmar for any errors that have crept in!
70 \begin_layout Abstract
75 This example program is provided for educational use only.
76 The functionality in this C program has been superceded by the equivalent
79 examples/listerrors.lyx
81 which should be installed in the LyX scripts directory.
88 \begin_layout Plain Layout
100 \begin_layout Standard
101 \begin_inset CommandInset toc
102 LatexCommand tableofcontents
109 \begin_layout Section
113 \begin_layout Standard
114 After typesetting a document, LyX scans the LaTeX log file looking for errors.
115 For each error found, the line number is obtained and a error box is displayed
116 in the LyX screen at that position.
119 \begin_layout Standard
120 To use this feature to view compilation errors while working with literate
121 documents, we need a program that filters the compilation errors and puts
122 them in a format suitable for LyX reading it.
126 \begin_layout Standard
127 In this document we present a filter that recognizes compilation error messages
128 from noweb, gnu C, and the IBM C compiler (xlc).
131 \begin_layout Standard
132 The filter is required to read from standard input, parse for error messages
133 and copy the error messages to the standard output.
134 During the output process, the filter must present the error messages in
135 a format that LyX can interpret, currently, the LaTeX error message format.
136 Of course, nothing will prevent future LyX releases from being able to
137 read other formats as well (like gcc error messages for example).
138 This mechanism is necessary to fully explore the literate programming tool's
142 \begin_layout Section
148 \begin_inset Newline newline
152 \begin_inset Newline newline
155 main (int argc, char **argv)
156 \begin_inset Newline newline
160 \begin_inset Newline newline
164 \begin_inset Newline newline
167 switch (argv[1][0]) {
168 \begin_inset Newline newline
172 \begin_inset Newline newline
175 <<Scan input for noweb error messages>>
176 \begin_inset Newline newline
180 \begin_inset Newline newline
184 \begin_inset Newline newline
187 <<Scan input for xlc error messages>>
188 \begin_inset Newline newline
192 \begin_inset Newline newline
196 \begin_inset Newline newline
199 <<AIX system using both noweb and xlc>>
200 \begin_inset Newline newline
204 \begin_inset Newline newline
208 \begin_inset Newline newline
212 \begin_inset Newline newline
215 <<Solaris and Linux systems using both noweb and gcc>>
216 \begin_inset Newline newline
220 \begin_inset Newline newline
224 \begin_inset Newline newline
228 \begin_inset Newline newline
231 <<Scan input for gcc error messages>>
232 \begin_inset Newline newline
236 \begin_inset Newline newline
240 \begin_inset Newline newline
244 \begin_inset Newline newline
247 <<Scan input for gcc error messages>>
248 \begin_inset Newline newline
252 \begin_inset Newline newline
256 \begin_inset Newline newline
263 <<Function prototypes>>=
264 \begin_inset Newline newline
267 int main (int argc, char **argv);
268 \begin_inset Newline newline
274 \begin_layout Section
278 \begin_layout Standard
279 We resort to some global variables to allow access from several different
281 These are the buffer and related pointers used during the parse of the
286 <<Global variables>>=
287 \begin_inset Newline newline
290 char buffer[200][200];
291 \begin_inset Newline newline
295 \begin_inset Newline newline
299 \begin_inset Newline newline
303 \begin_inset Newline newline
309 \begin_layout Section
313 \begin_layout Standard
314 The output format mimics the TeX error messages format.
315 This function prints a number of lines residing in the global variable
320 , a program name and line number.
321 There is no special requirement on the input strings, they can be anything.
325 \begin_layout Plain Layout
326 This function has been slightly changed from EW's original to make scanning
327 a bit easier with LaTeX::scanLogFile().
328 The test has been added because LyX can crash if empty lines are allowed
329 here --- I can't figure out why! --- BMH
339 \begin_inset Newline newline
343 \begin_inset Newline newline
346 output_error (int buf_size, int error_line, char *tool)
347 \begin_inset Newline newline
351 \begin_inset Newline newline
355 \begin_inset Newline newline
359 \begin_inset Newline newline
362 fprintf(stdout, "! Build Error: ==> %s ==>
365 \begin_inset Newline newline
368 fprintf(stdout, " ...
375 \begin_inset Newline newline
379 \begin_inset Newline newline
382 for (i=0; i<buf_size; i++)
383 \begin_inset Newline newline
386 if (strlen(buffer[i]) != 0)
387 \begin_inset Newline newline
390 fprintf(stdout, "%s", buffer[i]);
391 \begin_inset Newline newline
395 \begin_inset Newline newline
401 \begin_inset Newline newline
405 \begin_inset Newline newline
412 <<Function prototypes>>=
413 \begin_inset Newline newline
416 void output_error (int buf_size, int error_line, char *tool);
417 \begin_inset Newline newline
423 \begin_layout Section
424 Functions Implementation
427 \begin_layout Standard
428 Both noweave and notangle routines, always output one single line for each
429 error found, thus to scan the buffer for noweb error messages is enough
430 to exam one input line at a time.
431 Note that the noweb software does not provide a line error number, so all
432 errors boxes related to noweb messages will be displayed at the beginning
437 <<Scan input for noweb error messages>>=
438 \begin_inset Newline newline
442 \begin_inset Newline newline
446 \begin_inset Newline newline
449 while (fgets(buffer[0], 200, stdin)) {
450 \begin_inset Newline newline
454 \begin_inset Newline newline
457 output_error(1, err_line, "noweb");
458 \begin_inset Newline newline
462 \begin_inset Newline newline
466 \begin_inset Newline newline
472 \begin_layout Standard
473 The examination itself is very inefficient.
474 Unfortunately noweb doesn't have any characteristic that would help to
475 identify one of its error messages.
476 The solution is to collect all possible output messages in an array of
477 strings, and turn the examination process into a linear search in this
482 <<Global variables>>=
483 \begin_inset Newline newline
486 char *noweb_msgs[] = {
487 \begin_inset Newline newline
490 "couldn't open file",
491 \begin_inset Newline newline
494 "couldn't open temporary file",
495 \begin_inset Newline newline
498 "error writing temporary file",
499 \begin_inset Newline newline
503 \begin_inset Newline newline
507 \begin_inset Newline newline
510 "Bad format sequence",
511 \begin_inset Newline newline
514 "Can't open output file",
515 \begin_inset Newline newline
518 "Can't open temporary file",
519 \begin_inset Newline newline
522 "Capacity exceeded:",
523 \begin_inset Newline newline
526 "Ignoring unknown option -",
527 \begin_inset Newline newline
530 "This can't happen:",
531 \begin_inset Newline newline
534 "non-numeric line number in"
535 \begin_inset Newline newline
539 \begin_inset Newline newline
543 \begin_inset Newline newline
546 char *noweb_msgs_mimic_gcc[] = {
547 \begin_inset Newline newline
550 ": unescaped << in documentation chunk"
551 \begin_inset Newline newline
555 \begin_inset Newline newline
561 \begin_layout Standard
562 A noweb error message can be any string that contains a matching pair of
575 > >, or any of the above strings
580 \begin_inset Newline newline
584 \begin_inset Newline newline
587 noweb_try (int buf_line)
588 \begin_inset Newline newline
592 \begin_inset Newline newline
596 \begin_inset Newline newline
600 \begin_inset Newline newline
604 \begin_inset Newline newline
607 b = buffer[buf_line];
608 \begin_inset Newline newline
612 \begin_inset Newline newline
616 \begin_inset Newline newline
619 for (i=0; i<1; i++) {
620 \begin_inset Newline newline
623 s = (char *)strstr (b, noweb_msgs_mimic_gcc[i]);
624 \begin_inset Newline newline
628 \begin_inset Newline newline
631 t = (char *)strchr(buffer[buf_line], ':');
632 \begin_inset Newline newline
635 err_line = atoi(t+1);
636 \begin_inset Newline newline
639 t = buffer[buf_line];
640 \begin_inset Newline newline
644 \begin_inset Newline newline
647 while (*(t++) = *(s++));
648 \begin_inset Newline newline
652 \begin_inset Newline newline
656 \begin_inset Newline newline
660 \begin_inset Newline newline
663 s = (char *)strstr(b, "<<");
664 \begin_inset Newline newline
668 \begin_inset Newline newline
671 s = (char *)strstr(s+2, ">>");
672 \begin_inset Newline newline
676 \begin_inset Newline newline
680 \begin_inset Newline newline
684 \begin_inset Newline newline
688 \begin_inset Newline newline
691 for (i = 0; i < 12; ++i) {
692 \begin_inset Newline newline
695 s = (char *)strstr (b, noweb_msgs[i]);
696 \begin_inset Newline newline
700 \begin_inset Newline newline
704 \begin_inset Newline newline
708 \begin_inset Newline newline
712 \begin_inset Newline newline
716 \begin_inset Newline newline
720 \begin_inset Newline newline
724 \begin_inset Newline newline
731 <<Function prototypes>>=
732 \begin_inset Newline newline
735 int noweb_try (int buf_line);
736 \begin_inset Newline newline
742 \begin_layout Standard
743 The xlc compiler always outputs one single line for each error found, thus
744 to scan the buffer for xlc error messages it is enough to exam one input
749 <<Scan input for xlc error messages>>=
750 \begin_inset Newline newline
754 \begin_inset Newline newline
758 \begin_inset Newline newline
761 while (fgets(buffer[last_buf_line], 200, stdin)) {
762 \begin_inset Newline newline
766 \begin_inset Newline newline
769 output_error(1, err_line, "xlc");
770 \begin_inset Newline newline
774 \begin_inset Newline newline
778 \begin_inset Newline newline
784 \begin_layout Standard
785 A xlc error message is easy to identify.
786 Every error message starts with a quoted string with no spaces, a comma,
788 \begin_inset Quotes eld
792 \begin_inset Quotes erd
795 , a space, and some variable text.
796 The following routine tests if a given buffer line matches this criteria:
801 \begin_inset Newline newline
805 \begin_inset Newline newline
808 xlc_try (int buf_line)
809 \begin_inset Newline newline
813 \begin_inset Newline newline
817 \begin_inset Newline newline
821 \begin_inset Newline newline
824 t = buffer[buf_line];
825 \begin_inset Newline newline
829 \begin_inset Newline newline
832 while (*s != '"' && *s != ' ' && *s != '
835 \begin_inset Newline newline
839 \begin_inset Newline newline
842 if (*t != '"' || *s != '"' || strncmp(s+1, ", line ", 7) != 0)
843 \begin_inset Newline newline
847 \begin_inset Newline newline
851 \begin_inset Newline newline
855 \begin_inset Newline newline
859 \begin_inset Newline newline
863 \begin_inset Newline newline
870 <<Function prototypes>>=
871 \begin_inset Newline newline
874 int xlc_try (int buf_line);
875 \begin_inset Newline newline
881 \begin_layout Standard
882 The gcc compiler error messages are more complicated to scan.
883 Each error can span more than one line in the buffer.
884 The good news is that every buffer line on each error has the same pattern,
885 and share the same line number.
886 Thus the strategy will be to accumulate lines in the buffer while the reported
887 line number is still the same.
888 At the time they differ, all the accumulated lines, except the last one,
889 will belong to one single error message, which now can be output-ed to
893 \begin_layout Standard
894 Every gcc error message contains a string with no space followed by a
895 \begin_inset Quotes eld
899 \begin_inset Quotes eld
903 If the next character is a space, then this line is a header of a error
904 message and the next line will detail the line number of the source code
905 where the error was found.
906 Otherwise, the next thing is a integer number followed by another
907 \begin_inset Quotes eld
911 \begin_inset Quotes eld
918 <<Scan input for gcc error messages>>=
919 \begin_inset Newline newline
923 \begin_inset Newline newline
927 \begin_inset Newline newline
931 \begin_inset Newline newline
935 \begin_inset Newline newline
938 while (fgets(buffer[last_buf_line], 200, stdin)) {
939 \begin_inset Newline newline
942 /****** Skip lines until I find an error */
943 \begin_inset Newline newline
946 s = (char *)strpbrk(buffer[last_buf_line], " :");
947 \begin_inset Newline newline
950 if (s == NULL || *s == ' ')
951 \begin_inset Newline newline
954 continue; /* No gcc error found here */
955 \begin_inset Newline newline
959 \begin_inset Newline newline
962 <<gcc error message criteria is to find a "...:999:" or a "...: ">>
963 \begin_inset Newline newline
966 /****** OK It is an error message, get line number */
967 \begin_inset Newline newline
970 err_line = atoi(s+1);
971 \begin_inset Newline newline
974 if (last_err_line == 0 || last_err_line == err_line) {
975 \begin_inset Newline newline
978 last_err_line = err_line;
979 \begin_inset Newline newline
982 continue; /* It's either a header or a continuation, don't output
984 \begin_inset Newline newline
988 \begin_inset Newline newline
991 /****** Completed the scan of one error message, output it to LyX
993 \begin_inset Newline newline
997 \begin_inset Newline newline
1001 \begin_inset Newline newline
1004 } while (fgets(buffer[last_buf_line], 200, stdin));
1005 \begin_inset Newline newline
1009 \begin_inset Newline newline
1012 /****** EOF completes the scan of whatever was being scanned */
1013 \begin_inset Newline newline
1016 discharge_buffer(0);
1017 \begin_inset Newline newline
1021 \begin_inset Newline newline
1028 <<gcc error message criteria is to find a "...:999:" or a "...: ">>=
1029 \begin_inset Newline newline
1032 /****** Search first ":" in the error number */
1033 \begin_inset Newline newline
1036 s = (char *)strpbrk(buffer[last_buf_line], " :");
1037 \begin_inset Newline newline
1041 \begin_inset Newline newline
1044 if (s == NULL || *s == ' ')
1045 \begin_inset Newline newline
1048 <<No gcc error found here, but it might terminate the scanning of a previous
1050 \begin_inset Newline newline
1053 /****** Search second ":" in the error number */
1054 \begin_inset Newline newline
1057 t = (char *)strpbrk(s+1, " :");
1058 \begin_inset Newline newline
1061 if (t == NULL || *t == ' ')
1062 \begin_inset Newline newline
1065 <<No gcc error found here, but it might terminate the scanning of a previous
1067 \begin_inset Newline newline
1070 /****** Verify if is all digits between ":" */
1071 \begin_inset Newline newline
1074 if (t != s+1+strspn(s+1, "0123456789"))
1075 \begin_inset Newline newline
1078 <<No gcc error found here, but it might terminate the scanning of a previous
1080 \begin_inset Newline newline
1087 <<No gcc error found here, but it might terminate the scanning of a previous
1089 \begin_inset Newline newline
1093 \begin_inset Newline newline
1097 \begin_inset Newline newline
1100 discharge_buffer(1);
1101 \begin_inset Newline newline
1105 \begin_inset Newline newline
1109 \begin_inset Newline newline
1115 \begin_layout Standard
1116 As we mentioned, when the scan of one gcc error message is completed everything
1117 in the buffer except the last line is one single error message.
1118 But if the scan terminates with a EOF or through finding one line that
1119 does not match the gcc error message criteria, then there is no
1120 \begin_inset Quotes eld
1124 \begin_inset Quotes erd
1127 in the buffer to be concerned with.
1128 In those cases we empty the buffer completely.
1132 <<Function bodies>>=
1133 \begin_inset Newline newline
1137 \begin_inset Newline newline
1140 discharge_buffer (int save_last)
1141 \begin_inset Newline newline
1145 \begin_inset Newline newline
1148 if (last_err_line != 0) {
1149 \begin_inset Newline newline
1152 clean_gcc_messages();
1153 \begin_inset Newline newline
1156 if (save_last != 0) {
1157 \begin_inset Newline newline
1160 output_error(last_buf_line-1, last_err_line, "gcc");
1161 \begin_inset Newline newline
1164 strcpy (buffer[0], buffer[last_buf_line-1]);
1165 \begin_inset Newline newline
1168 last_err_line = err_line;
1169 \begin_inset Newline newline
1173 \begin_inset Newline newline
1177 \begin_inset Newline newline
1181 \begin_inset Newline newline
1184 clean_gcc_messages();
1185 \begin_inset Newline newline
1188 output_error(last_buf_line-1, last_err_line, "gcc");
1189 \begin_inset Newline newline
1193 \begin_inset Newline newline
1197 \begin_inset Newline newline
1201 \begin_inset Newline newline
1205 \begin_inset Newline newline
1209 \begin_inset Newline newline
1216 <<Function prototypes>>=
1217 \begin_inset Newline newline
1220 void discharge_buffer (int save_last);
1221 \begin_inset Newline newline
1227 \begin_layout Standard
1229 \begin_inset Quotes eld
1233 \begin_inset Quotes erd
1236 superfluous information from gcc messages, namely the name of the noweb
1237 file and the line number of the Error.
1241 \begin_layout Plain Layout
1243 For instance, some way of distinguishing between gcc Errors and Warnings
1253 <<Function bodies>>=
1254 \begin_inset Newline newline
1258 \begin_inset Newline newline
1261 clean_gcc_messages ()
1262 \begin_inset Newline newline
1266 \begin_inset Newline newline
1270 \begin_inset Newline newline
1274 \begin_inset Newline newline
1278 \begin_inset Newline newline
1281 int search_len = sprintf(search, ".nw:%d:", last_err_line);
1282 \begin_inset Newline newline
1286 \begin_inset Newline newline
1289 for (index = 0; index < last_buf_line-1; index++) {
1290 \begin_inset Newline newline
1293 tail = (char *)strstr (buffer[index], search);
1294 \begin_inset Newline newline
1297 if ( tail == NULL) {
1298 \begin_inset Newline newline
1301 tail = (char *) strstr (buffer[index], ".nw:");
1302 \begin_inset Newline newline
1306 \begin_inset Newline newline
1310 \begin_inset Newline newline
1314 \begin_inset Newline newline
1318 \begin_inset Newline newline
1322 \begin_inset Newline newline
1326 \begin_inset Newline newline
1330 \begin_inset Newline newline
1333 head = buffer[index];
1334 \begin_inset Newline newline
1337 while (*(head++) = *(tail++));
1338 \begin_inset Newline newline
1342 \begin_inset Newline newline
1346 \begin_inset Newline newline
1350 \begin_inset Newline newline
1357 <<Function prototypes>>=
1358 \begin_inset Newline newline
1361 void clean_gcc_messages ();
1362 \begin_inset Newline newline
1368 \begin_layout Standard
1369 To combine the scan of noweb error messages and xlc error messages is very
1371 We just try each one for every input line:
1375 <<AIX system using both noweb and xlc>>=
1376 \begin_inset Newline newline
1380 \begin_inset Newline newline
1384 \begin_inset Newline newline
1387 while (fgets(buffer[0], 200, stdin)) {
1388 \begin_inset Newline newline
1392 \begin_inset Newline newline
1395 output_error(1, err_line, "noweb");
1396 \begin_inset Newline newline
1399 else if (xlc_try(0))
1400 \begin_inset Newline newline
1403 output_error(1, err_line, "xlc");
1404 \begin_inset Newline newline
1408 \begin_inset Newline newline
1412 \begin_inset Newline newline
1418 \begin_layout Standard
1419 To combine the scan of noweb error messages and gcc error messages is simple
1420 if we realize that it is not possible to find a noweb error message in
1421 the middle of a gcc error message.
1422 So we just repeat the gcc procedure and test for noweb error messages in
1423 the beginning of the scan:
1427 <<Solaris and Linux systems using both noweb and gcc>>=
1428 \begin_inset Newline newline
1432 \begin_inset Newline newline
1436 \begin_inset Newline newline
1440 \begin_inset Newline newline
1444 \begin_inset Newline newline
1447 while (fgets(buffer[last_buf_line], 200, stdin)) {
1448 \begin_inset Newline newline
1451 /****** Skip lines until I find an error */
1452 \begin_inset Newline newline
1455 if (last_buf_line == 0 && noweb_try(0)) {
1456 \begin_inset Newline newline
1459 output_error(1, err_line, "noweb");
1460 \begin_inset Newline newline
1464 \begin_inset Newline newline
1468 \begin_inset Newline newline
1471 s = (char *)strpbrk(buffer[last_buf_line], " :");
1472 \begin_inset Newline newline
1475 if (s == NULL || *s == ' ')
1476 \begin_inset Newline newline
1479 continue; /* No gcc error found here */
1480 \begin_inset Newline newline
1484 \begin_inset Newline newline
1487 <<gcc error message criteria is to find a "...:999:" or a "...: ">>
1488 \begin_inset Newline newline
1491 /****** OK It is an error, get line number */
1492 \begin_inset Newline newline
1495 err_line = atoi(s+1);
1496 \begin_inset Newline newline
1499 if (last_err_line == 0 || last_err_line == err_line) {
1500 \begin_inset Newline newline
1503 last_err_line = err_line;
1504 \begin_inset Newline newline
1507 continue; /* It's either a header or a continuation, don't output
1509 \begin_inset Newline newline
1513 \begin_inset Newline newline
1516 /****** Completed the scan of one error message, output it to LyX
1518 \begin_inset Newline newline
1521 discharge_buffer(1);
1522 \begin_inset Newline newline
1526 \begin_inset Newline newline
1529 } while (fgets(buffer[last_buf_line], 200, stdin));
1530 \begin_inset Newline newline
1534 \begin_inset Newline newline
1537 /****** EOF completes the scan of whatever was being scanned */
1538 \begin_inset Newline newline
1541 discharge_buffer(0);
1542 \begin_inset Newline newline
1546 \begin_inset Newline newline
1552 \begin_layout Section
1553 Wrapping the code into a file
1558 \begin_inset Newline newline
1562 \begin_inset Newline newline
1565 #include <strings.h>
1566 \begin_inset Newline newline
1570 \begin_inset Newline newline
1573 <<Global variables>>
1574 \begin_inset Newline newline
1577 <<Function prototypes>>
1578 \begin_inset Newline newline
1582 \begin_inset Newline newline
1588 \begin_layout Standard
1589 To build this program, we want to add the
1590 \begin_inset Quotes eld
1594 \begin_inset Quotes erd
1597 option in the tangle command to force gdb to load the file
1606 In accordance with this, we pass the
1607 \begin_inset Quotes eld
1611 \begin_inset Quotes erd
1619 \begin_inset Newline newline
1623 \begin_inset Newline newline
1626 if [ -z "$NOWEB_SOURCE" ]; then NOWEB_SOURCE=Literate.nw; fi
1627 \begin_inset Newline newline
1630 notangle -L -Rlisterrors.c ${NOWEB_SOURCE} > listerrors.c
1631 \begin_inset Newline newline
1634 gcc -g -o listerrors listerrors.c
1635 \begin_inset Newline newline
1641 \begin_layout Standard
1642 This project can be tangled and compiled from LyX if you set
1648 to call a generic script that always extracts a scrap named
1653 Here is a example of such generic script:
1656 \begin_layout LyX-Code
1658 \begin_inset Newline newline
1661 notangle -Rbuild-script $1 | env NOWEB_SOURCE=$1 sh
1664 \begin_layout LyX-Code