1 % Sample CWEB file, excerpts from the libmp source code
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
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.
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.
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.
32 @^system dependencies@>
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)"
42 @ The external library header for \MP\ is |mplib.h|. It contains a
43 few typedefs and the header defintions for the externally used
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.
51 typedef struct MP_instance * MP;
53 typedef struct MP_options {
56 @<Exported function headers@>
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.
62 The variables from |MP_options| are included inside the |MP_instance|
67 @<Internal library declarations@>
75 #include <unistd.h> /* for access() */
76 #include <time.h> /* for struct tm \& co */
78 #include "mpmp.h" /* internal header */
79 #include "mppsout.h" /* internal header */
82 @<Basic printing procedures@>
83 @<Error handling procedures@>
85 @ The |__attribute__| pragma is gcc-only.
87 @<Internal library ... @>=
88 #if !defined(__GNUC__) || (__GNUC__ < 2)
89 # define __attribute__(x)
90 #endif /* !defined(__GNUC__) || (__GNUC__ < 2) */
93 void __attribute__((noinline))
94 mp_do_initialize ( MP mp) {
95 @<Local variables for initialization@>
96 @<Set initial values of key variables@>
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...@>;
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);
111 mp_do_initialize(mp); /* erase preloaded mem */
112 if (mp->ini_version) {
113 @<Run inimpost commands@>;
115 @<Initialize the output routines@>;
116 @<Get the first line of input and prepare to start@>;
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;
124 if ( mp->start_sym>0 ) { /* insert the `\&{everyjob}' symbol */
125 mp->cur_sym=mp->start_sym; mp_back_input(mp);
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.
137 for (i=first_text_char;i<=last_text_char;i++) {
140 for (i=0200;i<=0377;i++) { xord(xchr(i))=i;}
141 for (i=0;i<=0176;i++) { xord(xchr(i))=i;}
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
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:
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
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.
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
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|.
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~`\.{**}'.)
182 @d loc mp->cur_input.loc_field /* location of first unread character in |buffer| */
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@>
191 boolean mp_init_terminal (MP mp) { /* gets the terminal input started */
198 wake_up_terminal; do_fprintf(mp->term_out,"**"); update_terminal;
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@>
206 while ( (loc<(int)mp->last)&&(mp->buffer[loc]==' ') )
208 if ( loc<(int)mp->last ) {
209 return true; /* return unless the line was all blank */
211 do_fprintf(mp->term_out,"Please type the name of your input file.\n");
216 boolean mp_init_terminal (MP mp) ;
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
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@>
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.
307 @^system dependencies@>
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|.)
320 void mp_debug_help (MP mp) { /* routine to display various things */
327 mp_print_nl(mp, "debug # (-1 to exit):"); update_terminal;
330 aline = (mp->read_ascii_file)(mp->term_in, &len);
331 if (len) { sscanf(aline,"%i",&m); xfree(aline); }
335 aline = (mp->read_ascii_file)(mp->term_in, &len);
336 if (len) { sscanf(aline,"%i",&n); xfree(aline); }
338 @<Numbered cases for |debug_help|@>;
339 default: mp_print(mp, "?"); break;
344 @ @<Numbered cases...@>=
345 case 1: mp_print_word(mp, mp->mem[n]); /* display |mem[n]| in all forms */
347 case 2: mp_print_int(mp, info(n));
349 case 3: mp_print_int(mp, link(n));
351 case 4: mp_print_int(mp, eq_type(n)); mp_print_char(mp, ':'); mp_print_int(mp, equiv(n));
353 case 5: mp_print_variable_name(mp, n);
355 case 6: mp_print_int(mp, mp->internal[n]);
357 case 7: mp_do_show_dependencies(mp);
359 case 9: mp_show_token_list(mp, n,null,100000,0);
361 case 10: mp_print_str(mp, n);
363 case 11: mp_check_mem(mp, n>0); /* check wellformedness; print new busy locations if |n>0| */
365 case 12: mp_search_mem(mp, n); /* look for pointers to |n| */
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);
373 case 14: for (k=0;k<=n;k++) mp_print_str(mp, mp->buffer[k]);
375 case 15: mp->panicking=! mp->panicking;
379 @ Saving the filename template
381 @<Save the filename template@>=
383 if ( mp->filename_template!=0 ) delete_str_ref(mp->filename_template);
384 if ( length(mp->cur_exp)==0 ) mp->filename_template=0;
386 mp->filename_template=mp->cur_exp; add_str_ref(mp->filename_template);
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@>
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
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.