1 #This file was created by <wiensk> Tue Feb 23 22:25:52 1999
2 #LyX 1.0 (C) 1995-1999 Matthias Ettrich and the LyX Team
4 \textclass literate-article
15 \paperorientation portrait
18 \paragraph_separation indent
20 \quotes_language english
24 \paperpagestyle default
28 LyX and Literate Programming
35 edmar-w-jr@technologist.com
46 \begin_inset LatexCommand \tableofcontents
56 After typesetting a document, LyX scans the LaTeX log file looking for errors.
57 For each error found, the line number is obtained and a error box is displayed
58 in the LyX screen at that position.
61 To use this feature to view compilation errors while working with literate
62 documents, we need a program that filters the compilation errors and puts
63 them in a format suitable for LyX reading it.
67 In this document we present a filter that recognizes compilation error messages
68 from noweb, gnu C, and the IBM C compiler (xlc).
71 The filter is required to read from standard input, parse for error messages
72 and copy the error messages to the standard output.
73 During the output process, the filter must present the error messages in a
74 format that LyX can interpret, currently, the LaTeX error message format.
75 Of course, nothing will prevent future LyX releases from being able to read
76 other formats as well (like gcc error messages for example).
77 This mechanism is necessary to fully explore the literate programming
88 main (int argc, char **argv)
102 switch (argv[1][0]) {
122 <<Scan input for noweb error messages>>
154 <<Scan input for xlc error messages>>
188 <<AIX system using both noweb and xlc>>
228 <<Solaris and Linux systems using both noweb and gcc>>
268 <<Scan input for gcc error messages>>
300 <<Scan input for gcc error messages>>
311 <<Function prototypes>>=
313 int main (int argc, char **argv);
321 We resort to some global variables to allow access from several different
323 These are the buffer and related pointers used during the parse of the
327 <<Global variables>>=
373 The output format mimics the LaTeX error messages format.
374 This function prints a number of lines residing in the global variable
379 , a program name and line number.
380 There is no special requirement on the input strings, they can be anything.
387 output_error (int buf_size, int error_line, char *tool)
409 fprintf(stdout, "! Build Error: ==> %s ==>
415 for (i=0; i<buf_size; i++)
423 fprintf(stdout, "%s", buffer[i]);
427 fprintf(stdout, " ...
442 <<Function prototypes>>=
444 void output_error (int buf_size, int error_line, char *tool);
449 Functions Implementation
452 Both noweave and notangle routines, always output one single line for each
453 error found, thus to scan the buffer for noweb error messages is enough
454 to exam one input line at a time.
455 Note that the noweb software does not provide a line error number, so all
456 errors boxes related to noweb messages will be displayed at the beginning
460 <<Scan input for noweb error messages>>=
470 while (fgets(buffer[0], 200, stdin)) {
490 output_error(1, 0, "noweb");
501 The examination itself is very inefficient.
502 Unfortunately noweb doesn't have any characteristic that would help to
503 identify one of its error messages.
504 The solution is to collect all possible output messages in an array of
505 strings, and turn the examination process into a linear search in this
509 <<Global variables>>=
511 char *noweb_msgs[] = {
515 "couldn't open file",
519 "couldn't open temporary file",
523 "error writing temporary file",
535 "Bad format sequence",
539 "Can't open output file",
543 "Can't open temporary file",
547 "Capacity exceeded:",
551 "Ignoring unknown option -",
555 "This can't happen:",
559 "non-numeric line number in"
566 A noweb error message can be any string that contains a matching pair of
573 > >, or any of the above strings.
580 noweb_try (int buf_line)
612 b = buffer[buf_line];
628 s = strstr(s+2, ">>");
660 for (i=0; i<12; i++) {
672 s = strstr (b, noweb_msgs[i]);
745 <<Function prototypes>>=
747 int noweb_try (int buf_line);
752 The xlc compiler always outputs one single line for each error found, thus
753 to scan the buffer for xlc error messages it is enough to exam one input
757 <<Scan input for xlc error messages>>=
769 while (fgets(buffer[last_buf_line], 200, stdin)) {
789 output_error(1, err_line, "xlc");
800 A xlc error message is easy to identify.
801 Every error message starts with a quoted string with no spaces, a comma,
803 \begin_inset Quotes eld
807 \begin_inset Quotes erd
810 , a space, and some variable text.
811 The following routine tests if a given buffer line matches this criteria:
818 xlc_try (int buf_line)
838 t = buffer[buf_line];
846 while (*s != '"' && *s != ' ' && *s != '
860 if (*t != '"' || *s != '"' || strncmp(s+1, ", line ", 7) != 0)
887 <<Function prototypes>>=
889 int xlc_try (int buf_line);
894 The gcc compiler error messages are more complicated to scan.
895 Each error can span more than one line in the buffer.
896 The good news is that every buffer line on each error has the same pattern,
897 and share the same line number.
898 Thus the strategy will be to accumulate lines in the buffer while the reported
899 line number is still the same.
900 At the time they differ, all the accumulated lines, except the last one,
901 will belong to one single error message, which now can be output-ed to
905 Every gcc error message contains a string with no space followed by a
906 \begin_inset Quotes eld
910 \begin_inset Quotes eld
914 If the next character is a space, then this line is a header of a error
915 message and the next line will detail the line number of the source code
916 where the error was found.
917 Otherwise, the next thing is a integer number followed by another
918 \begin_inset Quotes eld
922 \begin_inset Quotes eld
928 <<Scan input for gcc error messages>>=
952 while (fgets(buffer[last_buf_line], 200, stdin)) {
960 /****** Skip lines until I find an error */
968 s = strpbrk(buffer[last_buf_line], " :");
976 if (s == NULL || *s == ' ')
988 continue; /* No gcc error found here */
1001 \protected_separator
1003 \protected_separator
1005 \protected_separator
1007 \protected_separator
1009 \protected_separator
1010 <<gcc error message criteria is to find a "...:999:" or a "...: ">>
1013 \protected_separator
1015 \protected_separator
1017 \protected_separator
1019 \protected_separator
1021 \protected_separator
1022 /****** OK It is an error message, get line number */
1025 \protected_separator
1027 \protected_separator
1029 \protected_separator
1031 \protected_separator
1033 \protected_separator
1034 err_line = atoi(s+1);
1037 \protected_separator
1039 \protected_separator
1041 \protected_separator
1043 \protected_separator
1045 \protected_separator
1046 if (last_err_line == 0 || last_err_line == err_line) {
1049 \protected_separator
1051 \protected_separator
1053 \protected_separator
1055 \protected_separator
1057 \protected_separator
1059 \protected_separator
1061 \protected_separator
1063 \protected_separator
1064 last_err_line = err_line;
1067 \protected_separator
1069 \protected_separator
1071 \protected_separator
1073 \protected_separator
1075 \protected_separator
1077 \protected_separator
1079 \protected_separator
1081 \protected_separator
1082 continue; /* It's either a header or a continuation, don't output yet */
1085 \protected_separator
1087 \protected_separator
1089 \protected_separator
1091 \protected_separator
1093 \protected_separator
1097 \protected_separator
1099 \protected_separator
1101 \protected_separator
1102 /****** Completed the scan of one error message, output it to LyX */
1105 \protected_separator
1107 \protected_separator
1109 \protected_separator
1111 \protected_separator
1113 \protected_separator
1114 discharge_buffer(1);
1117 \protected_separator
1119 \protected_separator
1121 \protected_separator
1123 \protected_separator
1125 \protected_separator
1129 \protected_separator
1131 \protected_separator
1133 \protected_separator
1134 } while (fgets(buffer[last_buf_line], 200, stdin));
1137 \protected_separator
1141 \protected_separator
1143 \protected_separator
1144 /****** EOF completes the scan of whatever was being scanned */
1147 \protected_separator
1148 discharge_buffer(0);
1155 <<gcc error message criteria is to find a "...:999:" or a "...: ">>=
1157 /****** Search first ":" in the error number */
1159 s = strpbrk(buffer[last_buf_line], " :");
1163 if (s == NULL || *s == ' ')
1166 \protected_separator
1167 <<No gcc error found here, but it might terminate the scanning of a previous
1170 /****** Search second ":" in the error number */
1172 t = strpbrk(s+1, " :");
1174 if (t == NULL || *t == ' ')
1177 \protected_separator
1178 <<No gcc error found here, but it might terminate the scanning of a previous
1181 /****** Verify if is all digits between ":" */
1183 if (t != s+1+strspn(s+1, "0123456789"))
1186 \protected_separator
1187 <<No gcc error found here, but it might terminate the scanning of a previous
1193 <<No gcc error found here, but it might terminate the scanning of a previous
1199 \protected_separator
1201 \protected_separator
1205 \protected_separator
1207 \protected_separator
1208 discharge_buffer(1);
1211 \protected_separator
1213 \protected_separator
1221 As we mentioned, when the scan of one gcc error message is completed everything
1222 in the buffer except the last line is one single error message.
1223 But if the scan terminates with a EOF or through finding one line that
1224 does not match the gcc error message criteria, then there is no
1225 \begin_inset Quotes eld
1229 \begin_inset Quotes erd
1232 in the buffer to be concerned with.
1233 In those cases we empty the buffer completely.
1236 <<Function bodies>>=
1240 discharge_buffer (int save_last)
1245 \protected_separator
1246 if (last_err_line != 0) {
1249 \protected_separator
1251 \protected_separator
1253 \protected_separator
1254 if (save_last != 0) {
1257 \protected_separator
1259 \protected_separator
1261 \protected_separator
1263 \protected_separator
1265 \protected_separator
1266 output_error(last_buf_line-1, last_err_line, "gcc");
1269 \protected_separator
1271 \protected_separator
1273 \protected_separator
1275 \protected_separator
1277 \protected_separator
1278 strcpy (buffer[0], buffer[last_buf_line-1]);
1281 \protected_separator
1283 \protected_separator
1285 \protected_separator
1287 \protected_separator
1289 \protected_separator
1290 last_err_line = err_line;
1293 \protected_separator
1295 \protected_separator
1297 \protected_separator
1299 \protected_separator
1301 \protected_separator
1305 \protected_separator
1307 \protected_separator
1309 \protected_separator
1313 \protected_separator
1315 \protected_separator
1317 \protected_separator
1319 \protected_separator
1321 \protected_separator
1322 output_error (last_buf_line, last_err_line, "gcc");
1325 \protected_separator
1327 \protected_separator
1329 \protected_separator
1331 \protected_separator
1333 \protected_separator
1337 \protected_separator
1339 \protected_separator
1341 \protected_separator
1343 \protected_separator
1345 \protected_separator
1349 \protected_separator
1351 \protected_separator
1353 \protected_separator
1357 \protected_separator
1365 <<Function prototypes>>=
1367 void discharge_buffer (int save_last);
1372 To combine the scan of noweb error messages and xlc error messages is very
1374 We just try each one for every input line:
1377 <<AIX system using both noweb and xlc>>=
1382 \protected_separator
1386 \protected_separator
1387 while (fgets(buffer[0], 200, stdin)) {
1390 \protected_separator
1392 \protected_separator
1394 \protected_separator
1398 \protected_separator
1400 \protected_separator
1402 \protected_separator
1404 \protected_separator
1406 \protected_separator
1407 output_error(1, 0, "noweb");
1410 \protected_separator
1412 \protected_separator
1414 \protected_separator
1415 else if (xlc_try(0))
1418 \protected_separator
1420 \protected_separator
1422 \protected_separator
1424 \protected_separator
1426 \protected_separator
1427 output_error(1, err_line, "xlc");
1430 \protected_separator
1438 To combine the scan of noweb error messages and gcc error messages is simple
1439 if we realize that it is not possible to find a noweb error message
1440 in the middle of a gcc error message.
1441 So we just repeat the gcc procedure and test for noweb error messages in
1442 the beginning of the scan:
1445 <<Solaris and Linux systems using both noweb and gcc>>=
1450 \protected_separator
1452 \protected_separator
1454 \protected_separator
1456 \protected_separator
1460 \protected_separator
1464 \protected_separator
1468 \protected_separator
1469 while (fgets(buffer[last_buf_line], 200, stdin)) {
1472 \protected_separator
1474 \protected_separator
1476 \protected_separator
1477 /****** Skip lines until I find an error */
1480 \protected_separator
1482 \protected_separator
1484 \protected_separator
1485 if (last_buf_line == 0 && noweb_try(0)) {
1488 \protected_separator
1490 \protected_separator
1492 \protected_separator
1494 \protected_separator
1496 \protected_separator
1497 output_error(1, 0, "noweb");
1500 \protected_separator
1502 \protected_separator
1504 \protected_separator
1506 \protected_separator
1508 \protected_separator
1512 \protected_separator
1514 \protected_separator
1516 \protected_separator
1520 \protected_separator
1522 \protected_separator
1524 \protected_separator
1525 s = strpbrk(buffer[last_buf_line], " :");
1528 \protected_separator
1530 \protected_separator
1532 \protected_separator
1533 if (s == NULL || *s == ' ')
1536 \protected_separator
1538 \protected_separator
1540 \protected_separator
1542 \protected_separator
1544 \protected_separator
1545 continue; /* No gcc error found here */
1548 \protected_separator
1550 \protected_separator
1552 \protected_separator
1556 \protected_separator
1558 \protected_separator
1560 \protected_separator
1562 \protected_separator
1564 \protected_separator
1566 \protected_separator
1567 <<gcc error message criteria is to find a "...:999:" or a "...: ">>
1570 \protected_separator
1572 \protected_separator
1574 \protected_separator
1576 \protected_separator
1578 \protected_separator
1579 /****** OK It is an error, get line number */
1582 \protected_separator
1584 \protected_separator
1586 \protected_separator
1588 \protected_separator
1590 \protected_separator
1591 err_line = atoi(s+1);
1594 \protected_separator
1596 \protected_separator
1598 \protected_separator
1600 \protected_separator
1602 \protected_separator
1603 if (last_err_line == 0 || last_err_line == err_line) {
1606 \protected_separator
1608 \protected_separator
1610 \protected_separator
1612 \protected_separator
1614 \protected_separator
1616 \protected_separator
1618 \protected_separator
1620 \protected_separator
1621 last_err_line = err_line;
1624 \protected_separator
1626 \protected_separator
1628 \protected_separator
1630 \protected_separator
1632 \protected_separator
1634 \protected_separator
1636 \protected_separator
1638 \protected_separator
1639 continue; /* It's either a header or a continuation, don't output yet */
1642 \protected_separator
1644 \protected_separator
1646 \protected_separator
1648 \protected_separator
1650 \protected_separator
1654 \protected_separator
1656 \protected_separator
1658 \protected_separator
1659 /****** Completed the scan of one error message, output it to LyX */
1662 \protected_separator
1664 \protected_separator
1666 \protected_separator
1668 \protected_separator
1670 \protected_separator
1671 discharge_buffer(1);
1674 \protected_separator
1676 \protected_separator
1678 \protected_separator
1680 \protected_separator
1682 \protected_separator
1686 \protected_separator
1688 \protected_separator
1690 \protected_separator
1691 } while (fgets(buffer[last_buf_line], 200, stdin));
1694 \protected_separator
1698 \protected_separator
1700 \protected_separator
1701 /****** EOF completes the scan of whatever was being scanned */
1704 \protected_separator
1705 discharge_buffer(0);
1712 Wrapping the code into a file
1719 #include <strings.h>
1720 \protected_separator
1722 \protected_separator
1724 \protected_separator
1728 \protected_separator
1731 <<Global variables>>
1733 <<Function prototypes>>
1740 To build this program, we want to add the
1741 \begin_inset Quotes eld
1745 \begin_inset Quotes erd
1748 option in the tangle command to force gdb to load the file
1757 In accordance with this, we pass the
1758 \begin_inset Quotes eld
1762 \begin_inset Quotes erd
1770 #!/usr/local/bin/bash
1772 notangle -L -Rlisterrors.c Literate.nw > listerrors.c
1774 gcc -g -o listerrors listerrors.c
1779 This project can be tangled and compiled from LyX if you set
1785 to call a generic script that always extracts a scrap named
1790 Here is a example of such generic script:
1795 notangle -Rbuild-script $1 | sh