Tag release lyx-1_6_0
[lyx.git] / lib / examples / Literate.lyx
blobcc7d37389993e2a232a6863b8c8a16c0427ebebf
1 #LyX 1.6.0 created this file. For more info see http://www.lyx.org/
2 \lyxformat 345
3 \begin_document
4 \begin_header
5 \use_default_options false
6 \textclass literate-article
7 \language english
8 \inputencoding default
9 \font_roman default
10 \font_sans default
11 \font_typewriter default
12 \font_default_family default
13 \font_sc false
14 \font_osf false
15 \font_sf_scale 100
16 \font_tt_scale 100
17 \graphics default
18 \paperfontsize default
19 \spacing single
20 \use_hyperref false
21 \papersize default
22 \use_geometry false
23 \use_amsmath 0
24 \use_esint 0
25 \cite_engine basic
26 \use_bibtopic false
27 \paperorientation portrait
28 \secnumdepth 3
29 \tocdepth 3
30 \paragraph_separation indent
31 \defskip medskip
32 \quotes_language english
33 \papercolumns 1
34 \papersides 1
35 \paperpagestyle default
36 \tracking_changes false
37 \output_changes false
38 \author ""
39 \end_header
41 \begin_body
43 \begin_layout Title
44 LyX and Literate Programming
45 \begin_inset Newline newline
46 \end_inset
48 An example program
49 \end_layout
51 \begin_layout Author
52 Edmar Wienskoski Jr.
53 \begin_inset Newline newline
54 \end_inset
56 edmar-w-jr@technologist.com
57 \begin_inset Foot
58 status collapsed
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!
63 \end_layout
65 \end_inset
68 \end_layout
70 \begin_layout Abstract
72 \series bold
73 Note:
74 \series default
75  This example program is provided for educational use only.
76  The functionality in this C program has been superceded by the equivalent
77  Python code in 
78 \emph on
79 examples/listerrors.lyx
80 \emph default
81  which should be installed in the LyX scripts directory.
82 \end_layout
84 \begin_layout Date
85 \begin_inset ERT
86 status collapsed
88 \begin_layout Plain Layout
91 \backslash
92 today
93 \end_layout
95 \end_inset
98 \end_layout
100 \begin_layout Standard
101 \begin_inset CommandInset toc
102 LatexCommand tableofcontents
104 \end_inset
107 \end_layout
109 \begin_layout Section
110 Introduction
111 \end_layout
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.
117 \end_layout
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.
124 \end_layout
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).
129 \end_layout
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
139  capabilities.
140 \end_layout
142 \begin_layout Section
143 Algorithm
144 \end_layout
146 \begin_layout Scrap
147 <<Function bodies>>=
148 \begin_inset Newline newline
149 \end_inset
152 \begin_inset Newline newline
153 \end_inset
155 main (int argc, char **argv)
156 \begin_inset Newline newline
157 \end_inset
160 \begin_inset Newline newline
161 \end_inset
163   if (argc == 2) {
164 \begin_inset Newline newline
165 \end_inset
167     switch (argv[1][0]) {
168 \begin_inset Newline newline
169 \end_inset
171     case 'n':
172 \begin_inset Newline newline
173 \end_inset
175       <<Scan input for noweb error messages>>
176 \begin_inset Newline newline
177 \end_inset
179       break;
180 \begin_inset Newline newline
181 \end_inset
183     case 'x':
184 \begin_inset Newline newline
185 \end_inset
187       <<Scan input for xlc error messages>>
188 \begin_inset Newline newline
189 \end_inset
191       break;
192 \begin_inset Newline newline
193 \end_inset
195     case 'a':
196 \begin_inset Newline newline
197 \end_inset
199       <<AIX system using both noweb and xlc>>
200 \begin_inset Newline newline
201 \end_inset
203       break;
204 \begin_inset Newline newline
205 \end_inset
207     case 's':
208 \begin_inset Newline newline
209 \end_inset
211     case 'b':
212 \begin_inset Newline newline
213 \end_inset
215       <<Solaris and Linux systems using both noweb and gcc>>
216 \begin_inset Newline newline
217 \end_inset
219       break;
220 \begin_inset Newline newline
221 \end_inset
223     case 'g':
224 \begin_inset Newline newline
225 \end_inset
227     default:
228 \begin_inset Newline newline
229 \end_inset
231       <<Scan input for gcc error messages>>
232 \begin_inset Newline newline
233 \end_inset
235       break;
236 \begin_inset Newline newline
237 \end_inset
239     }
240 \begin_inset Newline newline
241 \end_inset
243   } else {
244 \begin_inset Newline newline
245 \end_inset
247     <<Scan input for gcc error messages>>
248 \begin_inset Newline newline
249 \end_inset
251   }
252 \begin_inset Newline newline
253 \end_inset
256 \begin_inset Newline newline
257 \end_inset
260 \end_layout
262 \begin_layout Scrap
263 <<Function prototypes>>=
264 \begin_inset Newline newline
265 \end_inset
267 int main (int argc, char **argv);
268 \begin_inset Newline newline
269 \end_inset
272 \end_layout
274 \begin_layout Section
275 Data Structures
276 \end_layout
278 \begin_layout Standard
279 We resort to some global variables to allow access from several different
280  routines.
281  These are the buffer and related pointers used during the parse of the
282  input.
283 \end_layout
285 \begin_layout Scrap
286 <<Global variables>>=
287 \begin_inset Newline newline
288 \end_inset
290 char    buffer[200][200];
291 \begin_inset Newline newline
292 \end_inset
294 int     last_buf_line;
295 \begin_inset Newline newline
296 \end_inset
298 int     last_err_line;
299 \begin_inset Newline newline
300 \end_inset
302 int     err_line;
303 \begin_inset Newline newline
304 \end_inset
307 \end_layout
309 \begin_layout Section
310 The output format
311 \end_layout
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
317 \family typewriter
318 buffer
319 \family default
320 , a program name and line number.
321  There is no special requirement on the input strings, they can be anything.
322 \begin_inset Foot
323 status collapsed
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
330 \end_layout
332 \end_inset
335 \end_layout
337 \begin_layout Scrap
338 <<Function bodies>>=
339 \begin_inset Newline newline
340 \end_inset
342 void
343 \begin_inset Newline newline
344 \end_inset
346 output_error (int buf_size, int error_line, char *tool)
347 \begin_inset Newline newline
348 \end_inset
351 \begin_inset Newline newline
352 \end_inset
354   int     i;
355 \begin_inset Newline newline
356 \end_inset
359 \begin_inset Newline newline
360 \end_inset
362   fprintf(stdout, "! Build Error: ==> %s ==>
363 \backslash
364 n", tool);
365 \begin_inset Newline newline
366 \end_inset
368   fprintf(stdout, " ...
369 \backslash
371 \backslash
372 nl.%d ...
373 \backslash
374 n", error_line);
375 \begin_inset Newline newline
376 \end_inset
379 \begin_inset Newline newline
380 \end_inset
382   for (i=0; i<buf_size; i++)
383 \begin_inset Newline newline
384 \end_inset
386     if (strlen(buffer[i]) != 0)
387 \begin_inset Newline newline
388 \end_inset
390       fprintf(stdout, "%s", buffer[i]);
391 \begin_inset Newline newline
392 \end_inset
395 \begin_inset Newline newline
396 \end_inset
398   fprintf(stdout, "
399 \backslash
400 n");
401 \begin_inset Newline newline
402 \end_inset
405 \begin_inset Newline newline
406 \end_inset
409 \end_layout
411 \begin_layout Scrap
412 <<Function prototypes>>=
413 \begin_inset Newline newline
414 \end_inset
416 void output_error (int buf_size, int error_line, char *tool);
417 \begin_inset Newline newline
418 \end_inset
421 \end_layout
423 \begin_layout Section
424 Functions Implementation
425 \end_layout
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
433  of the file.
434 \end_layout
436 \begin_layout Scrap
437 <<Scan input for noweb error messages>>=
438 \begin_inset Newline newline
439 \end_inset
442 \begin_inset Newline newline
443 \end_inset
445   last_buf_line = 0;
446 \begin_inset Newline newline
447 \end_inset
449   while (fgets(buffer[0], 200, stdin)) {
450 \begin_inset Newline newline
451 \end_inset
453     if (noweb_try(0))
454 \begin_inset Newline newline
455 \end_inset
457       output_error(1, err_line, "noweb");
458 \begin_inset Newline newline
459 \end_inset
461   }
462 \begin_inset Newline newline
463 \end_inset
466 \begin_inset Newline newline
467 \end_inset
470 \end_layout
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
478  array.
479 \end_layout
481 \begin_layout Scrap
482 <<Global variables>>=
483 \begin_inset Newline newline
484 \end_inset
486 char *noweb_msgs[] = {
487 \begin_inset Newline newline
488 \end_inset
490   "couldn't open file",
491 \begin_inset Newline newline
492 \end_inset
494   "couldn't open temporary file",
495 \begin_inset Newline newline
496 \end_inset
498   "error writing temporary file",
499 \begin_inset Newline newline
500 \end_inset
502   "ill-formed option",
503 \begin_inset Newline newline
504 \end_inset
506   "unknown option",
507 \begin_inset Newline newline
508 \end_inset
510   "Bad format sequence",
511 \begin_inset Newline newline
512 \end_inset
514   "Can't open output file",
515 \begin_inset Newline newline
516 \end_inset
518   "Can't open temporary file",
519 \begin_inset Newline newline
520 \end_inset
522   "Capacity exceeded:",
523 \begin_inset Newline newline
524 \end_inset
526   "Ignoring unknown option -",
527 \begin_inset Newline newline
528 \end_inset
530   "This can't happen:",
531 \begin_inset Newline newline
532 \end_inset
534   "non-numeric line number in"
535 \begin_inset Newline newline
536 \end_inset
539 \begin_inset Newline newline
540 \end_inset
543 \begin_inset Newline newline
544 \end_inset
546 char *noweb_msgs_mimic_gcc[] = {
547 \begin_inset Newline newline
548 \end_inset
550   ": unescaped << in documentation chunk"
551 \begin_inset Newline newline
552 \end_inset
555 \begin_inset Newline newline
556 \end_inset
559 \end_layout
561 \begin_layout Standard
562 A noweb error message can be any string that contains a matching pair of
563  < <
564 \begin_inset space ~
565 \end_inset
568 \begin_inset space ~
569 \end_inset
572 \begin_inset space ~
573 \end_inset
575 > >, or any of the above strings
576 \end_layout
578 \begin_layout Scrap
579 <<Function bodies>>=
580 \begin_inset Newline newline
581 \end_inset
584 \begin_inset Newline newline
585 \end_inset
587 noweb_try (int buf_line)
588 \begin_inset Newline newline
589 \end_inset
592 \begin_inset Newline newline
593 \end_inset
595   char    *s, *t, *b;
596 \begin_inset Newline newline
597 \end_inset
599   int     i; 
600 \begin_inset Newline newline
601 \end_inset
604 \begin_inset Newline newline
605 \end_inset
607   b = buffer[buf_line];
608 \begin_inset Newline newline
609 \end_inset
611   err_line = 0;
612 \begin_inset Newline newline
613 \end_inset
616 \begin_inset Newline newline
617 \end_inset
619   for (i=0; i<1; i++) {
620 \begin_inset Newline newline
621 \end_inset
623       s = (char *)strstr (b, noweb_msgs_mimic_gcc[i]);
624 \begin_inset Newline newline
625 \end_inset
627       if (s != NULL) {
628 \begin_inset Newline newline
629 \end_inset
631         t = (char *)strchr(buffer[buf_line], ':');
632 \begin_inset Newline newline
633 \end_inset
635         err_line = atoi(t+1);
636 \begin_inset Newline newline
637 \end_inset
639         t = buffer[buf_line];
640 \begin_inset Newline newline
641 \end_inset
643         ++s;
644 \begin_inset Newline newline
645 \end_inset
647         while (*(t++) = *(s++));
648 \begin_inset Newline newline
649 \end_inset
651         return 1;
652 \begin_inset Newline newline
653 \end_inset
655       }
656 \begin_inset Newline newline
657 \end_inset
659   }
660 \begin_inset Newline newline
661 \end_inset
663   s = (char *)strstr(b, "<<");
664 \begin_inset Newline newline
665 \end_inset
667   if (s != NULL) {
668 \begin_inset Newline newline
669 \end_inset
671     s = (char *)strstr(s+2, ">>");
672 \begin_inset Newline newline
673 \end_inset
675     if (s != NULL) {
676 \begin_inset Newline newline
677 \end_inset
679       return 1;
680 \begin_inset Newline newline
681 \end_inset
683     }
684 \begin_inset Newline newline
685 \end_inset
687   } else { 
688 \begin_inset Newline newline
689 \end_inset
691      for (i = 0; i < 12; ++i) {
692 \begin_inset Newline newline
693 \end_inset
695         s = (char *)strstr (b, noweb_msgs[i]);
696 \begin_inset Newline newline
697 \end_inset
699         if (s != NULL) {
700 \begin_inset Newline newline
701 \end_inset
703            return 1;
704 \begin_inset Newline newline
705 \end_inset
707         }
708 \begin_inset Newline newline
709 \end_inset
711     }
712 \begin_inset Newline newline
713 \end_inset
715   }
716 \begin_inset Newline newline
717 \end_inset
719   return 0;
720 \begin_inset Newline newline
721 \end_inset
724 \begin_inset Newline newline
725 \end_inset
728 \end_layout
730 \begin_layout Scrap
731 <<Function prototypes>>=
732 \begin_inset Newline newline
733 \end_inset
735 int noweb_try (int buf_line);
736 \begin_inset Newline newline
737 \end_inset
740 \end_layout
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
745  line at a time.
746 \end_layout
748 \begin_layout Scrap
749 <<Scan input for xlc error messages>>= 
750 \begin_inset Newline newline
751 \end_inset
754 \begin_inset Newline newline
755 \end_inset
757   last_buf_line = 0;
758 \begin_inset Newline newline
759 \end_inset
761   while (fgets(buffer[last_buf_line], 200, stdin)) {
762 \begin_inset Newline newline
763 \end_inset
765     if (xlc_try(0))
766 \begin_inset Newline newline
767 \end_inset
769       output_error(1, err_line, "xlc");
770 \begin_inset Newline newline
771 \end_inset
773   }
774 \begin_inset Newline newline
775 \end_inset
778 \begin_inset Newline newline
779 \end_inset
782 \end_layout
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,
787  a space, the word 
788 \begin_inset Quotes eld
789 \end_inset
791 line
792 \begin_inset Quotes erd
793 \end_inset
795 , a space, and some variable text.
796  The following routine tests if a given buffer line matches this criteria:
797 \end_layout
799 \begin_layout Scrap
800 <<Function bodies>>=
801 \begin_inset Newline newline
802 \end_inset
804 int 
805 \begin_inset Newline newline
806 \end_inset
808 xlc_try (int buf_line)
809 \begin_inset Newline newline
810 \end_inset
813 \begin_inset Newline newline
814 \end_inset
816   char    *s, *t;
817 \begin_inset Newline newline
818 \end_inset
821 \begin_inset Newline newline
822 \end_inset
824   t = buffer[buf_line];
825 \begin_inset Newline newline
826 \end_inset
828   s = t+1;
829 \begin_inset Newline newline
830 \end_inset
832   while (*s != '"' && *s != ' ' && *s != '
833 \backslash
835 \begin_inset Newline newline
836 \end_inset
838     s++;
839 \begin_inset Newline newline
840 \end_inset
842   if (*t != '"' || *s != '"' || strncmp(s+1, ", line ", 7) != 0)
843 \begin_inset Newline newline
844 \end_inset
846     return 0;
847 \begin_inset Newline newline
848 \end_inset
850   s += 8;
851 \begin_inset Newline newline
852 \end_inset
854   err_line = atoi(s);
855 \begin_inset Newline newline
856 \end_inset
858   return 1;
859 \begin_inset Newline newline
860 \end_inset
863 \begin_inset Newline newline
864 \end_inset
867 \end_layout
869 \begin_layout Scrap
870 <<Function prototypes>>=
871 \begin_inset Newline newline
872 \end_inset
874 int xlc_try (int buf_line);
875 \begin_inset Newline newline
876 \end_inset
879 \end_layout
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
890  LyX.
891 \end_layout
893 \begin_layout Standard
894 Every gcc error message contains a string with no space followed by a 
895 \begin_inset Quotes eld
896 \end_inset
899 \begin_inset Quotes eld
900 \end_inset
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
908 \end_inset
911 \begin_inset Quotes eld
912 \end_inset
915 \end_layout
917 \begin_layout Scrap
918 <<Scan input for gcc error messages>>=
919 \begin_inset Newline newline
920 \end_inset
923 \begin_inset Newline newline
924 \end_inset
926   char    *s, *t;
927 \begin_inset Newline newline
928 \end_inset
931 \begin_inset Newline newline
932 \end_inset
934   last_buf_line = 0;
935 \begin_inset Newline newline
936 \end_inset
938   while (fgets(buffer[last_buf_line], 200, stdin)) {
939 \begin_inset Newline newline
940 \end_inset
942     /****** Skip lines until I find an error */
943 \begin_inset Newline newline
944 \end_inset
946     s = (char *)strpbrk(buffer[last_buf_line], " :");
947 \begin_inset Newline newline
948 \end_inset
950     if (s == NULL || *s == ' ')
951 \begin_inset Newline newline
952 \end_inset
954       continue; /* No gcc error found here */
955 \begin_inset Newline newline
956 \end_inset
958     do {
959 \begin_inset Newline newline
960 \end_inset
962       <<gcc error message criteria is to find a "...:999:" or a "...: ">>
963 \begin_inset Newline newline
964 \end_inset
966       /****** OK It is an error message, get line number */
967 \begin_inset Newline newline
968 \end_inset
970       err_line = atoi(s+1);
971 \begin_inset Newline newline
972 \end_inset
974       if (last_err_line == 0 || last_err_line == err_line) {
975 \begin_inset Newline newline
976 \end_inset
978         last_err_line = err_line;
979 \begin_inset Newline newline
980 \end_inset
982         continue; /* It's either a header or a continuation, don't output
983  yet */
984 \begin_inset Newline newline
985 \end_inset
987       }
988 \begin_inset Newline newline
989 \end_inset
991       /****** Completed the scan of one error message, output it to LyX
992  */
993 \begin_inset Newline newline
994 \end_inset
996       discharge_buffer(1);
997 \begin_inset Newline newline
998 \end_inset
1000       break;
1001 \begin_inset Newline newline
1002 \end_inset
1004     } while (fgets(buffer[last_buf_line], 200, stdin));
1005 \begin_inset Newline newline
1006 \end_inset
1008   }
1009 \begin_inset Newline newline
1010 \end_inset
1012   /****** EOF completes the scan of whatever was being scanned */
1013 \begin_inset Newline newline
1014 \end_inset
1016   discharge_buffer(0);
1017 \begin_inset Newline newline
1018 \end_inset
1021 \begin_inset Newline newline
1022 \end_inset
1025 \end_layout
1027 \begin_layout Scrap
1028 <<gcc error message criteria is to find a "...:999:" or a "...: ">>=
1029 \begin_inset Newline newline
1030 \end_inset
1032 /****** Search first ":" in the error number */
1033 \begin_inset Newline newline
1034 \end_inset
1036 s = (char *)strpbrk(buffer[last_buf_line], " :");
1037 \begin_inset Newline newline
1038 \end_inset
1040 last_buf_line++;
1041 \begin_inset Newline newline
1042 \end_inset
1044 if (s == NULL || *s == ' ') 
1045 \begin_inset Newline newline
1046 \end_inset
1048   <<No gcc error found here, but it might terminate the scanning of a previous
1049  one>>
1050 \begin_inset Newline newline
1051 \end_inset
1053 /****** Search second ":" in the error number */
1054 \begin_inset Newline newline
1055 \end_inset
1057 t = (char *)strpbrk(s+1, " :");
1058 \begin_inset Newline newline
1059 \end_inset
1061 if (t == NULL || *t == ' ')
1062 \begin_inset Newline newline
1063 \end_inset
1065   <<No gcc error found here, but it might terminate the scanning of a previous
1066  one>>
1067 \begin_inset Newline newline
1068 \end_inset
1070 /****** Verify if is all digits between ":" */
1071 \begin_inset Newline newline
1072 \end_inset
1074 if (t != s+1+strspn(s+1, "0123456789")) 
1075 \begin_inset Newline newline
1076 \end_inset
1078   <<No gcc error found here, but it might terminate the scanning of a previous
1079  one>>
1080 \begin_inset Newline newline
1081 \end_inset
1084 \end_layout
1086 \begin_layout Scrap
1087 <<No gcc error found here, but it might terminate the scanning of a previous
1088  one>>=
1089 \begin_inset Newline newline
1090 \end_inset
1093 \begin_inset Newline newline
1094 \end_inset
1096   err_line = 0;
1097 \begin_inset Newline newline
1098 \end_inset
1100   discharge_buffer(1);
1101 \begin_inset Newline newline
1102 \end_inset
1104   continue;
1105 \begin_inset Newline newline
1106 \end_inset
1109 \begin_inset Newline newline
1110 \end_inset
1113 \end_layout
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
1121 \end_inset
1123 last line
1124 \begin_inset Quotes erd
1125 \end_inset
1127  in the buffer to be concerned with.
1128  In those cases we empty the buffer completely.
1129 \end_layout
1131 \begin_layout Scrap
1132 <<Function bodies>>=
1133 \begin_inset Newline newline
1134 \end_inset
1136 void
1137 \begin_inset Newline newline
1138 \end_inset
1140 discharge_buffer (int save_last)
1141 \begin_inset Newline newline
1142 \end_inset
1145 \begin_inset Newline newline
1146 \end_inset
1148  if (last_err_line != 0) { 
1149 \begin_inset Newline newline
1150 \end_inset
1152    clean_gcc_messages();
1153 \begin_inset Newline newline
1154 \end_inset
1156    if (save_last != 0) {
1157 \begin_inset Newline newline
1158 \end_inset
1160       output_error(last_buf_line-1, last_err_line, "gcc");
1161 \begin_inset Newline newline
1162 \end_inset
1164       strcpy (buffer[0], buffer[last_buf_line-1]);
1165 \begin_inset Newline newline
1166 \end_inset
1168       last_err_line = err_line;
1169 \begin_inset Newline newline
1170 \end_inset
1172       last_buf_line = 1;
1173 \begin_inset Newline newline
1174 \end_inset
1176     } else { 
1177 \begin_inset Newline newline
1178 \end_inset
1180       ++last_buf_line;
1181 \begin_inset Newline newline
1182 \end_inset
1184       clean_gcc_messages();
1185 \begin_inset Newline newline
1186 \end_inset
1188       output_error(last_buf_line-1, last_err_line, "gcc");
1189 \begin_inset Newline newline
1190 \end_inset
1192       last_err_line = 0;
1193 \begin_inset Newline newline
1194 \end_inset
1196       last_buf_line = 0;
1197 \begin_inset Newline newline
1198 \end_inset
1200     }
1201 \begin_inset Newline newline
1202 \end_inset
1204   }
1205 \begin_inset Newline newline
1206 \end_inset
1209 \begin_inset Newline newline
1210 \end_inset
1213 \end_layout
1215 \begin_layout Scrap
1216 <<Function prototypes>>=
1217 \begin_inset Newline newline
1218 \end_inset
1220 void discharge_buffer (int save_last);
1221 \begin_inset Newline newline
1222 \end_inset
1225 \end_layout
1227 \begin_layout Standard
1228 The next function 
1229 \begin_inset Quotes eld
1230 \end_inset
1232 cleans
1233 \begin_inset Quotes erd
1234 \end_inset
1236  superfluous information from gcc messages, namely the name of the noweb
1237  file and the line number of the Error.
1238 \begin_inset Foot
1239 status collapsed
1241 \begin_layout Plain Layout
1242 More could be done.
1243  For instance, some way of distinguishing between gcc Errors and Warnings
1244  should be devised.
1245 \end_layout
1247 \end_inset
1250 \end_layout
1252 \begin_layout Scrap
1253 <<Function bodies>>=
1254 \begin_inset Newline newline
1255 \end_inset
1257 void
1258 \begin_inset Newline newline
1259 \end_inset
1261 clean_gcc_messages ()
1262 \begin_inset Newline newline
1263 \end_inset
1266 \begin_inset Newline newline
1267 \end_inset
1269   int index;
1270 \begin_inset Newline newline
1271 \end_inset
1273   char search [30]; 
1274 \begin_inset Newline newline
1275 \end_inset
1277   char *tail, *head; 
1278 \begin_inset Newline newline
1279 \end_inset
1281   int search_len = sprintf(search, ".nw:%d:", last_err_line);
1282 \begin_inset Newline newline
1283 \end_inset
1285   
1286 \begin_inset Newline newline
1287 \end_inset
1289   for (index = 0; index < last_buf_line-1; index++) {
1290 \begin_inset Newline newline
1291 \end_inset
1293     tail = (char *)strstr (buffer[index], search);
1294 \begin_inset Newline newline
1295 \end_inset
1297     if ( tail == NULL) {
1298 \begin_inset Newline newline
1299 \end_inset
1301        tail = (char *) strstr (buffer[index], ".nw:");
1302 \begin_inset Newline newline
1303 \end_inset
1305        if (tail) {
1306 \begin_inset Newline newline
1307 \end_inset
1309           tail += 4;
1310 \begin_inset Newline newline
1311 \end_inset
1313        }
1314 \begin_inset Newline newline
1315 \end_inset
1317     } else {
1318 \begin_inset Newline newline
1319 \end_inset
1321        tail += search_len;
1322 \begin_inset Newline newline
1323 \end_inset
1325     }
1326 \begin_inset Newline newline
1327 \end_inset
1329     if (tail != NULL) {
1330 \begin_inset Newline newline
1331 \end_inset
1333        head = buffer[index];
1334 \begin_inset Newline newline
1335 \end_inset
1337        while (*(head++) = *(tail++));
1338 \begin_inset Newline newline
1339 \end_inset
1341     }
1342 \begin_inset Newline newline
1343 \end_inset
1345   }
1346 \begin_inset Newline newline
1347 \end_inset
1350 \begin_inset Newline newline
1351 \end_inset
1354 \end_layout
1356 \begin_layout Scrap
1357 <<Function prototypes>>=
1358 \begin_inset Newline newline
1359 \end_inset
1361 void clean_gcc_messages ();
1362 \begin_inset Newline newline
1363 \end_inset
1366 \end_layout
1368 \begin_layout Standard
1369 To combine the scan of noweb error messages and xlc error messages is very
1370  simple.
1371  We just try each one for every input line:
1372 \end_layout
1374 \begin_layout Scrap
1375 <<AIX system using both noweb and xlc>>=
1376 \begin_inset Newline newline
1377 \end_inset
1380 \begin_inset Newline newline
1381 \end_inset
1383   last_buf_line = 0;
1384 \begin_inset Newline newline
1385 \end_inset
1387   while (fgets(buffer[0], 200, stdin)) {
1388 \begin_inset Newline newline
1389 \end_inset
1391     if (noweb_try(0))
1392 \begin_inset Newline newline
1393 \end_inset
1395       output_error(1, err_line, "noweb");
1396 \begin_inset Newline newline
1397 \end_inset
1399     else if (xlc_try(0))
1400 \begin_inset Newline newline
1401 \end_inset
1403       output_error(1, err_line, "xlc");
1404 \begin_inset Newline newline
1405 \end_inset
1407   }
1408 \begin_inset Newline newline
1409 \end_inset
1412 \begin_inset Newline newline
1413 \end_inset
1416 \end_layout
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:
1424 \end_layout
1426 \begin_layout Scrap
1427 <<Solaris and Linux systems using both noweb and gcc>>=
1428 \begin_inset Newline newline
1429 \end_inset
1432 \begin_inset Newline newline
1433 \end_inset
1435   char    *s, *t;
1436 \begin_inset Newline newline
1437 \end_inset
1440 \begin_inset Newline newline
1441 \end_inset
1443   last_buf_line = 0;
1444 \begin_inset Newline newline
1445 \end_inset
1447   while (fgets(buffer[last_buf_line], 200, stdin)) {
1448 \begin_inset Newline newline
1449 \end_inset
1451     /****** Skip lines until I find an error */
1452 \begin_inset Newline newline
1453 \end_inset
1455     if (last_buf_line == 0 && noweb_try(0)) {
1456 \begin_inset Newline newline
1457 \end_inset
1459       output_error(1, err_line, "noweb");
1460 \begin_inset Newline newline
1461 \end_inset
1463       continue;
1464 \begin_inset Newline newline
1465 \end_inset
1467     }
1468 \begin_inset Newline newline
1469 \end_inset
1471     s = (char *)strpbrk(buffer[last_buf_line], " :");
1472 \begin_inset Newline newline
1473 \end_inset
1475     if (s == NULL || *s == ' ')
1476 \begin_inset Newline newline
1477 \end_inset
1479       continue; /* No gcc error found here */
1480 \begin_inset Newline newline
1481 \end_inset
1483     do {
1484 \begin_inset Newline newline
1485 \end_inset
1487       <<gcc error message criteria is to find a "...:999:" or a "...: ">>
1488 \begin_inset Newline newline
1489 \end_inset
1491       /****** OK It is an error, get line number */
1492 \begin_inset Newline newline
1493 \end_inset
1495       err_line = atoi(s+1);
1496 \begin_inset Newline newline
1497 \end_inset
1499       if (last_err_line == 0 || last_err_line == err_line) {
1500 \begin_inset Newline newline
1501 \end_inset
1503         last_err_line = err_line;
1504 \begin_inset Newline newline
1505 \end_inset
1507         continue; /* It's either a header or a continuation, don't output
1508  yet */
1509 \begin_inset Newline newline
1510 \end_inset
1512       }
1513 \begin_inset Newline newline
1514 \end_inset
1516       /****** Completed the scan of one error message, output it to LyX
1517  */
1518 \begin_inset Newline newline
1519 \end_inset
1521       discharge_buffer(1);
1522 \begin_inset Newline newline
1523 \end_inset
1525       break;
1526 \begin_inset Newline newline
1527 \end_inset
1529     } while (fgets(buffer[last_buf_line], 200, stdin));
1530 \begin_inset Newline newline
1531 \end_inset
1533   }
1534 \begin_inset Newline newline
1535 \end_inset
1537   /****** EOF completes the scan of whatever was being scanned */
1538 \begin_inset Newline newline
1539 \end_inset
1541   discharge_buffer(0);
1542 \begin_inset Newline newline
1543 \end_inset
1546 \begin_inset Newline newline
1547 \end_inset
1550 \end_layout
1552 \begin_layout Section
1553 Wrapping the code into a file
1554 \end_layout
1556 \begin_layout Scrap
1557 <<listerrors.c>>=
1558 \begin_inset Newline newline
1559 \end_inset
1561 #include <stdio.h>
1562 \begin_inset Newline newline
1563 \end_inset
1565 #include <strings.h>       
1566 \begin_inset Newline newline
1567 \end_inset
1570 \begin_inset Newline newline
1571 \end_inset
1573 <<Global variables>>
1574 \begin_inset Newline newline
1575 \end_inset
1577 <<Function prototypes>>
1578 \begin_inset Newline newline
1579 \end_inset
1581 <<Function bodies>>
1582 \begin_inset Newline newline
1583 \end_inset
1586 \end_layout
1588 \begin_layout Standard
1589 To build this program, we want to add the 
1590 \begin_inset Quotes eld
1591 \end_inset
1594 \begin_inset Quotes erd
1595 \end_inset
1597  option in the tangle command to force gdb to load the file 
1598 \family typewriter
1599 Literate.nw
1600 \family default
1601  instead of 
1602 \family typewriter
1603 listerrors.c
1604 \family default
1606  In accordance with this, we pass the 
1607 \begin_inset Quotes eld
1608 \end_inset
1611 \begin_inset Quotes erd
1612 \end_inset
1614  option to gcc.
1615 \end_layout
1617 \begin_layout Scrap
1618 <<build-script>>=
1619 \begin_inset Newline newline
1620 \end_inset
1622 #!/bin/sh
1623 \begin_inset Newline newline
1624 \end_inset
1626 if [ -z "$NOWEB_SOURCE" ]; then NOWEB_SOURCE=Literate.nw; fi
1627 \begin_inset Newline newline
1628 \end_inset
1630 notangle -L -Rlisterrors.c ${NOWEB_SOURCE} > listerrors.c
1631 \begin_inset Newline newline
1632 \end_inset
1634 gcc -g -o listerrors listerrors.c
1635 \begin_inset Newline newline
1636 \end_inset
1639 \end_layout
1641 \begin_layout Standard
1642 This project can be tangled and compiled from LyX if you set 
1643 \family typewriter
1645 \backslash
1646 build_command
1647 \family default
1648  to call a generic script that always extracts a scrap named 
1649 \family typewriter
1650 build-script
1651 \family default
1652  and executes it.
1653  Here is a example of such generic script:
1654 \end_layout
1656 \begin_layout LyX-Code
1657 #!/bin/sh
1658 \begin_inset Newline newline
1659 \end_inset
1661 notangle -Rbuild-script $1 | env NOWEB_SOURCE=$1 sh
1662 \end_layout
1664 \begin_layout LyX-Code
1666 \end_layout
1668 \end_body
1669 \end_document