Unit tests for CWEB
[ohcount] / test / src_dir / cweb.w
1 % Sample CWEB file, excerpts from the libmp source code
2
3 @* \[1] Introduction.
4
5 A large piece of software like \MP\ has inherent complexity that cannot
6 be reduced below a certain level of difficulty, although each individual
7 part is fairly simple by itself. The \.{WEB} language is intended to make
8 the algorithms as readable as possible, by reflecting the way the
9 individual program pieces fit together and by providing the
10 cross-references that connect different parts. Detailed comments about
11 what is going on, and about why things were done in certain ways, have
12 been liberally sprinkled throughout the program.  These comments explain
13 features of the implementation, but they rarely attempt to explain the
14 \MP\ language itself, since the reader is supposed to be familiar with
15 {\sl The {\logos METAFONT\/}book} as well as the manual
16 @.WEB@>
17 @:METAFONTbook}{\sl The {\logos METAFONT\/}book@>
18 {\sl A User's Manual for MetaPost}, Computing Science Technical Report 162,
19 AT\AM T Bell Laboratories.
20
21 @ The present implementation is a preliminary version, but the possibilities
22 for new features are limited by the desire to remain as nearly compatible
23 with \MF\ as possible.
24
25 On the other hand, the \.{WEB} description can be extended without changing
26 the core of the program, and it has been designed so that such
27 extensions are not extremely difficult to make.
28 The |banner| string defined here should be changed whenever \MP\
29 undergoes any modifications, so that it will be clear which version of
30 \MP\ might be the guilty party when a problem arises.
31 @^extensions to \MP@>
32 @^system dependencies@>
33
34 @d banner "This is MetaPost, Version 1.002" /* printed when \MP\ starts */
35 @d metapost_version "1.002"
36 @d mplib_version "0.20"
37 @d version_string " (Cweb version 0.20)"
38
39 @d true 1
40 @d false 0
41
42 @ The external library header for \MP\ is |mplib.h|. It contains a
43 few typedefs and the header defintions for the externally used
44 fuctions.
45
46 The most important of the typedefs is the definition of the structure 
47 |MP_options|, that acts as a small, configurable front-end to the fairly 
48 large |MP_instance| structure.
49  
50 @(mplib.h@>=
51 typedef struct MP_instance * MP;
52 @<Exported types@>
53 typedef struct MP_options {
54   @<Option variables@>
55 } MP_options;
56 @<Exported function headers@>
57
58 @ The internal header file is much longer: it not only lists the complete
59 |MP_instance|, but also a lot of functions that have to be available to
60 the \ps\ backend, that is defined in a separate \.{WEB} file. 
61
62 The variables from |MP_options| are included inside the |MP_instance| 
63 wholesale.
64
65 @(mpmp.h@>=
66 #include <setjmp.h>
67 @<Internal library declarations@>
68
69 @ @c 
70 #include <stdio.h>
71 #include <stdlib.h>
72 #include <string.h>
73 #include <stdarg.h>
74 #include <assert.h>
75 #include <unistd.h> /* for access() */
76 #include <time.h> /* for struct tm \& co */
77 #include "mplib.h"
78 #include "mpmp.h" /* internal header */
79 #include "mppsout.h" /* internal header */
80 @h
81 @<Declarations@>
82 @<Basic printing procedures@>
83 @<Error handling procedures@>
84
85 @ The |__attribute__| pragma is gcc-only.
86
87 @<Internal library ... @>=
88 #if !defined(__GNUC__) || (__GNUC__ < 2)
89 # define __attribute__(x)
90 #endif /* !defined(__GNUC__) || (__GNUC__ < 2) */
91
92 @ @c
93 void  __attribute__((noinline))
94 mp_do_initialize ( MP mp) {
95   @<Local variables for initialization@>
96   @<Set initial values of key variables@>
97 }
98 int mp_initialize (MP mp) { /* this procedure gets things started properly */
99   mp->history=mp_fatal_error_stop; /* in case we quit during initialization */
100   @<Install and test the non-local jump buffer@>;
101   t_open_out; /* open the terminal for output */
102   @<Check the ``constant'' values...@>;
103   if ( mp->bad>0 ) {
104         char ss[256];
105     snprintf(ss,256,"Ouch---my internal constants have been clobbered!\n"
106                    "---case %i",(int)mp->bad);
107     do_fprintf(mp->err_out,(char *)ss);
108 @.Ouch...clobbered@>
109     return mp->history;
110   }
111   mp_do_initialize(mp); /* erase preloaded mem */
112   if (mp->ini_version) {
113     @<Run inimpost commands@>;
114   }
115   @<Initialize the output routines@>;
116   @<Get the first line of input and prepare to start@>;
117   mp_set_job_id(mp);
118   mp_init_map_file(mp, mp->troff_mode);
119   mp->history=mp_spotless; /* ready to go! */
120   if (mp->troff_mode) {
121     mp->internal[mp_gtroffmode]=unity; 
122     mp->internal[mp_prologues]=unity; 
123   }
124   if ( mp->start_sym>0 ) { /* insert the `\&{everyjob}' symbol */
125     mp->cur_sym=mp->start_sym; mp_back_input(mp);
126   }
127   return mp->history;
128 }
129
130 @ The following system-independent code makes the |xord| array contain a
131 suitable inverse to the information in |xchr|. Note that if |xchr[i]=xchr[j]|
132 where |i<j<0177|, the value of |xord[xchr[i]]| will turn out to be
133 |j| or more; hence, standard ASCII code numbers will be used instead of
134 codes below 040 in case there is a coincidence.
135
136 @<Set initial ...@>=
137 for (i=first_text_char;i<=last_text_char;i++) { 
138    xord(chr(i))=0177;
139 }
140 for (i=0200;i<=0377;i++) { xord(xchr(i))=i;}
141 for (i=0;i<=0176;i++) { xord(xchr(i))=i;}
142
143 @* \[3] Input and output.
144 The bane of portability is the fact that different operating systems treat
145 input and output quite differently, perhaps because computer scientists
146 have not given sufficient attention to this problem. People have felt somehow
147 that input and output are not part of ``real'' programming. Well, it is true
148 that some kinds of programming are more fun than others. With existing
149 input/output conventions being so diverse and so messy, the only sources of
150 joy in such parts of the code are the rare occasions when one can find a
151 way to make the program a little less bad than it might have been. We have
152 two choices, either to attack I/O now and get it over with, or to postpone
153 I/O until near the end. Neither prospect is very attractive, so let's
154 get it over with.
155
156 Different systems have different ways to get started. But regardless of
157 what conventions are adopted, the routine that initializes the terminal
158 should satisfy the following specifications:
159
160 \yskip\textindent{1)}It should open file |term_in| for input from the
161   terminal. (The file |term_out| will already be open for output to the
162   terminal.)
163
164 \textindent{2)}If the user has given a command line, this line should be
165   considered the first line of terminal input. Otherwise the
166   user should be prompted with `\.{**}', and the first line of input
167   should be whatever is typed in response.
168
169 \textindent{3)}The first line of input, which might or might not be a
170   command line, should appear in locations |first| to |last-1| of the
171   |buffer| array.
172
173 \textindent{4)}The global variable |loc| should be set so that the
174   character to be read next by \MP\ is in |buffer[loc]|. This
175   character should not be blank, and we should have |loc<last|.
176
177 \yskip\noindent(It may be necessary to prompt the user several times
178 before a non-blank line comes in. The prompt is `\.{**}' instead of the
179 later `\.*' because the meaning is slightly different: `\.{input}' need
180 not be typed immediately after~`\.{**}'.)
181
182 @d loc mp->cur_input.loc_field /* location of first unread character in |buffer| */
183
184 @ The following program does the required initialization
185 without retrieving a possible command line.
186 It should be clear how to modify this routine to deal with command lines,
187 if the system permits them.
188 @^system dependencies@>
189
190 @c 
191 boolean mp_init_terminal (MP mp) { /* gets the terminal input started */
192   t_open_in; 
193   if (mp->last!=0) {
194     loc = mp->first = 0;
195         return true;
196   }
197   while (1) { 
198     wake_up_terminal; do_fprintf(mp->term_out,"**"); update_terminal;
199 @.**@>
200     if ( ! mp_input_ln(mp, mp->term_in ) ) { /* this shouldn't happen */
201       do_fprintf(mp->term_out,"\n! End of file on the terminal... why?");
202 @.End of file on the terminal@>
203       return false;
204     }
205     loc=mp->first;
206     while ( (loc<(int)mp->last)&&(mp->buffer[loc]==' ') ) 
207       incr(loc);
208     if ( loc<(int)mp->last ) { 
209       return true; /* return unless the line was all blank */
210     };
211     do_fprintf(mp->term_out,"Please type the name of your input file.\n");
212   }
213 }
214
215 @ @<Declarations@>=
216 boolean mp_init_terminal (MP mp) ;
217
218 @ The symbolic names for internal quantities are put into \MP's hash table
219 by using a routine called |primitive|, which will be defined later. Let us
220 enter them now, so that we don't have to list all those names again
221 anywhere else.
222
223 @<Put each of \MP's primitives into the hash table@>=
224 mp_primitive(mp, "tracingtitles",internal_quantity,mp_tracing_titles);
225 @:tracingtitles_}{\&{tracingtitles} primitive@>
226 mp_primitive(mp, "tracingequations",internal_quantity,mp_tracing_equations);
227 @:mp_tracing_equations_}{\&{tracingequations} primitive@>
228 mp_primitive(mp, "tracingcapsules",internal_quantity,mp_tracing_capsules);
229 @:mp_tracing_capsules_}{\&{tracingcapsules} primitive@>
230 mp_primitive(mp, "tracingchoices",internal_quantity,mp_tracing_choices);
231 @:mp_tracing_choices_}{\&{tracingchoices} primitive@>
232 mp_primitive(mp, "tracingspecs",internal_quantity,mp_tracing_specs);
233 @:mp_tracing_specs_}{\&{tracingspecs} primitive@>
234 mp_primitive(mp, "tracingcommands",internal_quantity,mp_tracing_commands);
235 @:mp_tracing_commands_}{\&{tracingcommands} primitive@>
236 mp_primitive(mp, "tracingrestores",internal_quantity,mp_tracing_restores);
237 @:mp_tracing_restores_}{\&{tracingrestores} primitive@>
238 mp_primitive(mp, "tracingmacros",internal_quantity,mp_tracing_macros);
239 @:mp_tracing_macros_}{\&{tracingmacros} primitive@>
240 mp_primitive(mp, "tracingoutput",internal_quantity,mp_tracing_output);
241 @:mp_tracing_output_}{\&{tracingoutput} primitive@>
242 mp_primitive(mp, "tracingstats",internal_quantity,mp_tracing_stats);
243 @:mp_tracing_stats_}{\&{tracingstats} primitive@>
244 mp_primitive(mp, "tracinglostchars",internal_quantity,mp_tracing_lost_chars);
245 @:mp_tracing_lost_chars_}{\&{tracinglostchars} primitive@>
246 mp_primitive(mp, "tracingonline",internal_quantity,mp_tracing_online);
247 @:mp_tracing_online_}{\&{tracingonline} primitive@>
248 mp_primitive(mp, "year",internal_quantity,mp_year);
249 @:mp_year_}{\&{year} primitive@>
250 mp_primitive(mp, "month",internal_quantity,mp_month);
251 @:mp_month_}{\&{month} primitive@>
252 mp_primitive(mp, "day",internal_quantity,mp_day);
253 @:mp_day_}{\&{day} primitive@>
254 mp_primitive(mp, "time",internal_quantity,mp_time);
255 @:time_}{\&{time} primitive@>
256 mp_primitive(mp, "charcode",internal_quantity,mp_char_code);
257 @:mp_char_code_}{\&{charcode} primitive@>
258 mp_primitive(mp, "charext",internal_quantity,mp_char_ext);
259 @:mp_char_ext_}{\&{charext} primitive@>
260 mp_primitive(mp, "charwd",internal_quantity,mp_char_wd);
261 @:mp_char_wd_}{\&{charwd} primitive@>
262 mp_primitive(mp, "charht",internal_quantity,mp_char_ht);
263 @:mp_char_ht_}{\&{charht} primitive@>
264 mp_primitive(mp, "chardp",internal_quantity,mp_char_dp);
265 @:mp_char_dp_}{\&{chardp} primitive@>
266 mp_primitive(mp, "charic",internal_quantity,mp_char_ic);
267 @:mp_char_ic_}{\&{charic} primitive@>
268 mp_primitive(mp, "designsize",internal_quantity,mp_design_size);
269 @:mp_design_size_}{\&{designsize} primitive@>
270 mp_primitive(mp, "pausing",internal_quantity,mp_pausing);
271 @:mp_pausing_}{\&{pausing} primitive@>
272 mp_primitive(mp, "showstopping",internal_quantity,mp_showstopping);
273 @:mp_showstopping_}{\&{showstopping} primitive@>
274 mp_primitive(mp, "fontmaking",internal_quantity,mp_fontmaking);
275 @:mp_fontmaking_}{\&{fontmaking} primitive@>
276 mp_primitive(mp, "linejoin",internal_quantity,mp_linejoin);
277 @:mp_linejoin_}{\&{linejoin} primitive@>
278 mp_primitive(mp, "linecap",internal_quantity,mp_linecap);
279 @:mp_linecap_}{\&{linecap} primitive@>
280 mp_primitive(mp, "miterlimit",internal_quantity,mp_miterlimit);
281 @:mp_miterlimit_}{\&{miterlimit} primitive@>
282 mp_primitive(mp, "warningcheck",internal_quantity,mp_warning_check);
283 @:mp_warning_check_}{\&{warningcheck} primitive@>
284 mp_primitive(mp, "boundarychar",internal_quantity,mp_boundary_char);
285 @:mp_boundary_char_}{\&{boundarychar} primitive@>
286 mp_primitive(mp, "prologues",internal_quantity,mp_prologues);
287 @:mp_prologues_}{\&{prologues} primitive@>
288 mp_primitive(mp, "truecorners",internal_quantity,mp_true_corners);
289 @:mp_true_corners_}{\&{truecorners} primitive@>
290 mp_primitive(mp, "mpprocset",internal_quantity,mp_procset);
291 @:mp_procset_}{\&{mpprocset} primitive@>
292 mp_primitive(mp, "troffmode",internal_quantity,mp_gtroffmode);
293 @:troffmode_}{\&{troffmode} primitive@>
294 mp_primitive(mp, "defaultcolormodel",internal_quantity,mp_default_color_model);
295 @:mp_default_color_model_}{\&{defaultcolormodel} primitive@>
296 mp_primitive(mp, "restoreclipcolor",internal_quantity,mp_restore_clip_color);
297 @:mp_restore_clip_color_}{\&{restoreclipcolor} primitive@>
298
299 @* \[47] Debugging.
300 Once \MP\ is working, you should be able to diagnose most errors with
301 the \.{show} commands and other diagnostic features. But for the initial
302 stages of debugging, and for the revelation of really deep mysteries, you
303 can compile \MP\ with a few more aids. An additional routine called |debug_help|
304 will also come into play when you type `\.D' after an error message;
305 |debug_help| also occurs just before a fatal error causes \MP\ to succumb.
306 @^debugging@>
307 @^system dependencies@>
308
309 The interface to |debug_help| is primitive, but it is good enough when used
310 with a debugger that allows you to set breakpoints and to read
311 variables and change their values. After getting the prompt `\.{debug \#}', you
312 type either a negative number (this exits |debug_help|), or zero (this
313 goes to a location where you can set a breakpoint, thereby entering into
314 dialog with the debugger), or a positive number |m| followed by
315 an argument |n|. The meaning of |m| and |n| will be clear from the
316 program below. (If |m=13|, there is an additional argument, |l|.)
317 @.debug \#@>
318
319 @<Last-minute...@>=
320 void mp_debug_help (MP mp) { /* routine to display various things */
321   integer k;
322   int l,m,n;
323   char *aline;
324   size_t len;
325   while (1) { 
326     wake_up_terminal;
327     mp_print_nl(mp, "debug # (-1 to exit):"); update_terminal;
328 @.debug \#@>
329     m = 0;
330     aline = (mp->read_ascii_file)(mp->term_in, &len);
331     if (len) { sscanf(aline,"%i",&m); xfree(aline); }
332     if ( m<=0 )
333       return;
334     n = 0 ;
335     aline = (mp->read_ascii_file)(mp->term_in, &len);
336     if (len) { sscanf(aline,"%i",&n); xfree(aline); }
337     switch (m) {
338     @<Numbered cases for |debug_help|@>;
339     default: mp_print(mp, "?"); break;
340     }
341   }
342 }
343
344 @ @<Numbered cases...@>=
345 case 1: mp_print_word(mp, mp->mem[n]); /* display |mem[n]| in all forms */
346   break;
347 case 2: mp_print_int(mp, info(n));
348   break;
349 case 3: mp_print_int(mp, link(n));
350   break;
351 case 4: mp_print_int(mp, eq_type(n)); mp_print_char(mp, ':'); mp_print_int(mp, equiv(n));
352   break;
353 case 5: mp_print_variable_name(mp, n);
354   break;
355 case 6: mp_print_int(mp, mp->internal[n]);
356   break;
357 case 7: mp_do_show_dependencies(mp);
358   break;
359 case 9: mp_show_token_list(mp, n,null,100000,0);
360   break;
361 case 10: mp_print_str(mp, n);
362   break;
363 case 11: mp_check_mem(mp, n>0); /* check wellformedness; print new busy locations if |n>0| */
364   break;
365 case 12: mp_search_mem(mp, n); /* look for pointers to |n| */
366   break;
367 case 13: 
368   l = 0;  
369   aline = (mp->read_ascii_file)(mp->term_in, &len);
370   if (len) { sscanf(aline,"%i",&l); xfree(aline); }
371   mp_print_cmd_mod(mp, n,l); 
372   break;
373 case 14: for (k=0;k<=n;k++) mp_print_str(mp, mp->buffer[k]);
374   break;
375 case 15: mp->panicking=! mp->panicking;
376   break;
377
378
379 @ Saving the filename template
380
381 @<Save the filename template@>=
382
383   if ( mp->filename_template!=0 ) delete_str_ref(mp->filename_template);
384   if ( length(mp->cur_exp)==0 ) mp->filename_template=0;
385   else { 
386     mp->filename_template=mp->cur_exp; add_str_ref(mp->filename_template);
387   }
388 }
389
390 @* \[48] System-dependent changes.
391 This section should be replaced, if necessary, by any special
392 modification of the program
393 that are necessary to make \MP\ work at a particular installation.
394 It is usually best to design your change file so that all changes to
395 previous sections preserve the section numbering; then everybody's version
396 will be consistent with the published program. More extensive changes,
397 which introduce new sections, can be inserted here; then only the index
398 itself will get a new section number.
399 @^system dependencies@>
400
401 @* \[49] Index.
402 Here is where you can find all uses of each identifier in the program,
403 with underlined entries pointing to where the identifier was defined.
404 If the identifier is only one letter long, however, you get to see only
405 the underlined entries. {\sl All references are to section numbers instead of
406 page numbers.}
407
408 This index also lists error messages and other aspects of the program
409 that you might want to look up some day. For example, the entry
410 for ``system dependencies'' lists all sections that should receive
411 special attention from people who are installing \MP\ in a new
412 operating environment. A list of various things that can't happen appears
413 under ``this can't happen''.
414 Approximately 25 sections are listed under ``inner loop''; these account
415 for more than 60\pct! of \MP's running time, exclusive of input and output.