1% $Id: mp.w 2055 2015-01-22 15:39:12Z luigi $
2%
3% This file is part of MetaPost;
4% the MetaPost program is in the public domain.
5% See the <Show version...> code in mpost.w for more info.
6
7% Here is TeX material that gets inserted after \input webmac
8\def\hang{\hangindent 3em\noindent\ignorespaces}
9\def\textindent#1{\hangindent2.5em\noindent\hbox to2.5em{\hss#1 }\ignorespaces}
10\def\ps{PostScript}
11\def\psqrt#1{\sqrt{\mathstrut#1}}
12\def\k{_{k+1}}
13\def\pct!{{\char`\%}} % percent sign in ordinary text
14\font\tenlogo=logo10 % font used for the METAFONT logo
15\font\logos=logosl10
16\def\MF{{\tenlogo META}\-{\tenlogo FONT}}
17\def\MP{{\tenlogo META}\-{\tenlogo POST}}
18\def\<#1>{$\langle#1\rangle$}
19\def\section{\mathhexbox278}
20\let\swap=\leftrightarrow
21\def\round{\mathop{\rm round}\nolimits}
22\mathchardef\vbv="026A % synonym for `\|'
23\def\vb{\relax\ifmmode\vbv\else$\vbv$\fi}
24
25\def\(#1){} % this is used to make section names sort themselves better
26\def\9#1{} % this is used for sort keys in the index via @@:sort key}{entry@@>
27\def\title{MetaPost}
28\pdfoutput=1
29\pageno=3
30
31@* Introduction.
32
33This is \MP\ by John Hobby, a graphics-language processor based on D. E. Knuth's \MF.
34
35Much of the original Pascal version of this program was copied with
36permission from MF.web Version 1.9. It interprets a language very
37similar to D.E. Knuth's METAFONT, but with changes designed to make it
38more suitable for PostScript output.
39
40The main purpose of the following program is to explain the algorithms of \MP\
41as clearly as possible. However, the program has been written so that it
42can be tuned to run efficiently in a wide variety of operating environments
43by making comparatively few changes. Such flexibility is possible because
44the documentation that follows is written in the \.{WEB} language, which is
45at a higher level than C.
46
47A large piece of software like \MP\ has inherent complexity that cannot
48be reduced below a certain level of difficulty, although each individual
49part is fairly simple by itself. The \.{WEB} language is intended to make
50the algorithms as readable as possible, by reflecting the way the
51individual program pieces fit together and by providing the
52cross-references that connect different parts. Detailed comments about
53what is going on, and about why things were done in certain ways, have
54been liberally sprinkled throughout the program.  These comments explain
55features of the implementation, but they rarely attempt to explain the
56\MP\ language itself, since the reader is supposed to be familiar with
57{\sl The {\logos METAFONT\/}book} as well as the manual
58@.WEB@>
59@:METAFONTbook}{\sl The {\logos METAFONT\/}book@>
60{\sl A User's Manual for MetaPost}, Computing Science Technical Report 162,
61AT\AM T Bell Laboratories.
62
63@ The present implementation is a preliminary version, but the possibilities
64for new features are limited by the desire to remain as nearly compatible
65with \MF\ as possible.
66
67On the other hand, the \.{WEB} description can be extended without changing
68the core of the program, and it has been designed so that such
69extensions are not extremely difficult to make.
70The |banner| string defined here should be changed whenever \MP\
71undergoes any modifications, so that it will be clear which version of
72\MP\ might be the guilty party when a problem arises.
73@^extensions to \MP@>
74@^system dependencies@>
75
76@d default_banner "This is MetaPost, Version 1.999" /* printed when \MP\ starts */
77@d true 1
78@d false 0
79
80@<Metapost version header@>=
81#define metapost_version "1.999"
82
83@ The external library header for \MP\ is |mplib.h|. It contains a
84few typedefs and the header defintions for the externally used
85fuctions.
86
87The most important of the typedefs is the definition of the structure
88|MP_options|, that acts as a small, configurable front-end to the fairly
89large |MP_instance| structure.
90
91@(mplib.h@>=
92#ifndef MPLIB_H
93#define MPLIB_H 1
94#include <stdlib.h>
95#ifndef HAVE_BOOLEAN
96typedef int boolean;
97#endif
98@<Metapost version header@>
99typedef struct MP_instance *MP;
100@<Exported types@>
101typedef struct MP_options {
102  @<Option variables@>
103} MP_options;
104@<Exported function headers@>
105@<MPlib header stuff@>
106#endif
107
108@ The internal header file is much longer: it not only lists the complete
109|MP_instance|, but also a lot of functions that have to be available to
110the \ps\ backend, that is defined in a separate \.{WEB} file.
111
112The variables from |MP_options| are included inside the |MP_instance|
113wholesale.
114
115@(mpmp.h@>=
116#ifndef MPMP_H
117#define MPMP_H 1
118#include "avl.h"
119#include "mplib.h"
120#include <setjmp.h>
121typedef struct psout_data_struct *psout_data;
122typedef struct svgout_data_struct *svgout_data;
123typedef struct pngout_data_struct *pngout_data;
124#ifndef HAVE_BOOLEAN
125typedef int boolean;
126#endif
127#ifndef INTEGER_TYPE
128typedef int integer;
129#endif
130@<Declare helpers@>;
131@<Enumeration types@>;
132@<Types in the outer block@>;
133@<Constants in the outer block@>;
134typedef struct MP_instance {
135  @<Option variables@>
136  @<Global variables@>
137} MP_instance;
138@<Internal library declarations@>
139@<MPlib internal header stuff@>
140#endif
141
142@ @c
143#define KPATHSEA_DEBUG_H 1
144#include <w2c/config.h>
145#include <stdio.h>
146#include <stdlib.h>
147#include <string.h>
148#include <stdarg.h>
149#include <assert.h>
150#include <math.h>
151#ifdef HAVE_UNISTD_H
152#  include <unistd.h>           /* for access */
153#endif
154#include <time.h>               /* for struct tm \& co */
155#include <zlib.h>               /* for |ZLIB_VERSION|, zlibVersion() */
156#include <png.h>                /* for |PNG_LIBPNG_VER_STRING|, |png_libpng_ver| */
157#include <pixman.h>             /* for |PIXMAN_VERSION_STRING|, |pixman_version_string()| */
158#include <cairo.h>              /* for |CAIRO_VERSION_STRING|, |cairo_version_string()| */
159#include <gmp.h>                /* for |gmp_version| */
160#include <mpfr.h>               /* for |MPFR_VERSION_STRING|, |mpfr_get_version()| */
161#include "mplib.h"
162#include "mplibps.h"            /* external header */
163#include "mplibsvg.h"           /* external header */
164#include "mplibpng.h"           /* external header */
165#include "mpmp.h"               /* internal header */
166#include "mppsout.h"            /* internal header */
167#include "mpsvgout.h"           /* internal header */
168#include "mppngout.h"           /* internal header */
169#include "mpmath.h"             /* internal header */
170#include "mpmathdouble.h"       /* internal header */
171#include "mpmathdecimal.h"      /* internal header */
172#include "mpmathbinary.h"       /* internal header */
173#include "mpstrings.h"          /* internal header */
174extern font_number mp_read_font_info (MP mp, char *fname);      /* tfmin.w */
175@h @<Declarations@>;
176@<Basic printing procedures@>;
177@<Error handling procedures@>
178
179@ Some debugging support for development. The trick with the variadic macros
180probably only works in gcc, as this preprocessor feature was not formalized
181until the c99 standard (and that is too new for us). Lets' hope that at least
182most compilers understand the non-debug version.
183@^system dependencies@>
184
185@<MPlib internal header stuff@>=
186#define DEBUG 0
187#if DEBUG
188#define debug_number(A) printf("%d: %s=%.32f (%d)\n", __LINE__, #A, number_to_double(A), number_to_scaled(A))
189#else
190#define debug_number(A)
191#endif
192#if DEBUG>1
193void do_debug_printf(MP mp, const char *prefix, const char *fmt, ...);
194#  define debug_printf(a1,a2,a3) do_debug_printf(mp, "", a1,a2,a3)
195#  define FUNCTION_TRACE1(a1) do_debug_printf(mp, "FTRACE: ", a1)
196#  define FUNCTION_TRACE2(a1,a2) do_debug_printf(mp, "FTRACE: ", a1,a2)
197#  define FUNCTION_TRACE3(a1,a2,a3) do_debug_printf(mp, "FTRACE: ", a1,a2,a3)
198#  define FUNCTION_TRACE3X(a1,a2,a3) (void)mp
199#  define FUNCTION_TRACE4(a1,a2,a3,a4) do_debug_printf(mp, "FTRACE: ", a1,a2,a3,a4)
200#else
201#  define debug_printf(a1,a2,a3)
202#  define FUNCTION_TRACE1(a1) (void)mp
203#  define FUNCTION_TRACE2(a1,a2) (void)mp
204#  define FUNCTION_TRACE3(a1,a2,a3) (void)mp
205#  define FUNCTION_TRACE3X(a1,a2,a3) (void)mp
206#  define FUNCTION_TRACE4(a1,a2,a3,a4) (void)mp
207#endif
208
209@ This function occasionally crashes (if something is written after the
210log file is already closed), but that is not so important while debugging.
211
212@c
213#if DEBUG
214void do_debug_printf(MP mp, const char *prefix, const char *fmt, ...) ;
215void do_debug_printf(MP mp, const char *prefix, const char *fmt, ...) {
216  va_list ap;
217#if 0
218  va_start (ap, fmt);
219  if (mp->log_file && !ferror((FILE *)mp->log_file)) {
220    fputs(prefix, mp->log_file);
221    vfprintf(mp->log_file, fmt, ap);
222  }
223  va_end(ap);
224#endif
225  va_start (ap, fmt);
226#if 0
227  if (mp->term_out  && !ferror((FILE *)mp->term_out)) {
228#else
229  if (false) {
230#endif
231    fputs(prefix, mp->term_out);
232    vfprintf(mp->term_out, fmt, ap);
233  } else {
234    fputs(prefix, stdout);
235    vfprintf(stdout, fmt, ap);
236  }
237  va_end(ap);
238}
239#endif
240
241@ Here are the functions that set up the \MP\ instance.
242
243@<Declarations@>=
244MP_options *mp_options (void);
245MP mp_initialize (MP_options * opt);
246
247@ @c
248MP_options *mp_options (void) {
249  MP_options *opt;
250  size_t l = sizeof (MP_options);
251  opt = malloc (l);
252  if (opt != NULL) {
253    memset (opt, 0, l);
254  }
255  return opt;
256}
257
258
259@ @<Internal library declarations@>=
260@<Declare subroutines for parsing file names@>
261
262
263@ The whole instance structure is initialized with zeroes,
264this greatly reduces the number of statements needed in
265the |Allocate or initialize variables| block.
266
267@d set_callback_option(A) do { mp->A = mp_##A;
268  if (opt->A!=NULL) mp->A = opt->A;
269} while (0)
270
271@c
272static MP mp_do_new (jmp_buf * buf) {
273  MP mp = malloc (sizeof (MP_instance));
274  if (mp == NULL) {
275    xfree (buf);
276    return NULL;
277  }
278  memset (mp, 0, sizeof (MP_instance));
279  mp->jump_buf = buf;
280  return mp;
281}
282
283
284@ @c
285static void mp_free (MP mp) {
286  int k;        /* loop variable */
287  @<Dealloc variables@>;
288  if (mp->noninteractive) {
289    @<Finish non-interactive use@>;
290  }
291  xfree (mp->jump_buf);
292  @<Free table entries@>;
293  free_math();
294  xfree (mp);
295}
296
297
298@ @c
299static void mp_do_initialize (MP mp) {
300  @<Local variables for initialization@>;
301  @<Set initial values of key variables@>;
302}
303
304@ For the retargetable math library, we need to have a pointer, at least.
305
306@<Global variables@>=
307void *math;
308
309@ @<Exported types@>=
310typedef enum {
311  mp_nan_type = 0,
312  mp_scaled_type,
313  mp_fraction_type,
314  mp_angle_type,
315  mp_double_type,
316  mp_binary_type,
317  mp_decimal_type
318} mp_number_type;
319typedef union {
320  void *num;
321  double dval;
322  int val;
323} mp_number_store;
324typedef struct mp_number_data {
325  mp_number_store data;
326  mp_number_type type;
327} mp_number_data;
328typedef struct mp_number_data mp_number;
329#define is_number(A) ((A).type != mp_nan_type)
330
331typedef void (*convert_func) (mp_number *r);
332typedef void (*m_log_func) (MP mp, mp_number *r, mp_number a);
333typedef void (*m_exp_func) (MP mp, mp_number *r, mp_number a);
334typedef void (*m_norm_rand_func) (MP mp, mp_number *ret);
335typedef void (*pyth_add_func) (MP mp, mp_number *r, mp_number a, mp_number b);
336typedef void (*pyth_sub_func) (MP mp, mp_number *r, mp_number a, mp_number b);
337typedef void (*n_arg_func) (MP mp, mp_number *r, mp_number a, mp_number b);
338typedef void (*velocity_func) (MP mp, mp_number *r, mp_number a, mp_number b, mp_number c, mp_number d, mp_number e);
339typedef void (*ab_vs_cd_func) (MP mp, mp_number *r, mp_number a, mp_number b, mp_number c, mp_number d);
340typedef void (*crossing_point_func) (MP mp, mp_number *r, mp_number a, mp_number b, mp_number c);
341typedef void (*number_from_int_func) (mp_number *A, int B);
342typedef void (*number_from_boolean_func) (mp_number *A, int B);
343typedef void (*number_from_scaled_func) (mp_number *A, int B);
344typedef void (*number_from_double_func) (mp_number *A, double B);
345typedef void (*number_from_addition_func) (mp_number *A, mp_number B, mp_number C);
346typedef void (*number_from_substraction_func) (mp_number *A, mp_number B, mp_number C);
347typedef void (*number_from_div_func) (mp_number *A, mp_number B, mp_number C);
348typedef void (*number_from_mul_func) (mp_number *A, mp_number B, mp_number C);
349typedef void (*number_from_int_div_func) (mp_number *A, mp_number B, int C);
350typedef void (*number_from_int_mul_func) (mp_number *A, mp_number B, int C);
351typedef void (*number_from_oftheway_func) (MP mp, mp_number *A, mp_number t, mp_number B, mp_number C);
352typedef void (*number_negate_func) (mp_number *A);
353typedef void (*number_add_func) (mp_number *A, mp_number B);
354typedef void (*number_substract_func) (mp_number *A, mp_number B);
355typedef void (*number_modulo_func) (mp_number *A, mp_number B);
356typedef void (*number_half_func) (mp_number *A);
357typedef void (*number_halfp_func) (mp_number *A);
358typedef void (*number_double_func) (mp_number *A);
359typedef void (*number_abs_func) (mp_number *A);
360typedef void (*number_clone_func) (mp_number *A, mp_number B);
361typedef void (*number_swap_func) (mp_number *A, mp_number *B);
362typedef void (*number_add_scaled_func) (mp_number *A, int b);
363typedef void (*number_multiply_int_func) (mp_number *A, int b);
364typedef void (*number_divide_int_func) (mp_number *A, int b);
365typedef int (*number_to_int_func) (mp_number A);
366typedef int (*number_to_boolean_func) (mp_number A);
367typedef int (*number_to_scaled_func) (mp_number A);
368typedef int (*number_round_func) (mp_number A);
369typedef void (*number_floor_func) (mp_number *A);
370typedef double (*number_to_double_func) (mp_number A);
371typedef int (*number_odd_func) (mp_number A);
372typedef int (*number_equal_func) (mp_number A, mp_number B);
373typedef int (*number_less_func) (mp_number A, mp_number B);
374typedef int (*number_greater_func) (mp_number A, mp_number B);
375typedef int (*number_nonequalabs_func) (mp_number A, mp_number B);
376typedef void (*make_scaled_func) (MP mp, mp_number *ret, mp_number A, mp_number B);
377typedef void (*make_fraction_func) (MP mp, mp_number *ret, mp_number A, mp_number B);
378typedef void (*take_fraction_func) (MP mp, mp_number *ret, mp_number A, mp_number B);
379typedef void (*take_scaled_func) (MP mp, mp_number *ret, mp_number A, mp_number B);
380typedef void (*sin_cos_func) (MP mp, mp_number A, mp_number *S, mp_number *C);
381typedef void (*slow_add_func) (MP mp, mp_number *A, mp_number S, mp_number C);
382typedef void (*sqrt_func) (MP mp, mp_number *ret, mp_number A);
383typedef void (*init_randoms_func) (MP mp, int seed);
384typedef void (*new_number_func) (MP mp, mp_number *A, mp_number_type t);
385typedef void (*free_number_func) (MP mp, mp_number *n);
386typedef void (*fraction_to_round_scaled_func) (mp_number *n);
387typedef void (*print_func) (MP mp, mp_number A);
388typedef char * (*tostring_func) (MP mp, mp_number A);
389typedef void (*scan_func) (MP mp, int A);
390typedef void (*mp_free_func) (MP mp);
391typedef void (*set_precision_func) (MP mp);
392
393typedef struct math_data {
394  mp_number precision_default;
395  mp_number precision_max;
396  mp_number precision_min;
397  mp_number epsilon_t;
398  mp_number inf_t;
399  mp_number one_third_inf_t;
400  mp_number zero_t;
401  mp_number unity_t;
402  mp_number two_t;
403  mp_number three_t;
404  mp_number half_unit_t;
405  mp_number three_quarter_unit_t;
406  mp_number fraction_one_t;
407  mp_number fraction_half_t;
408  mp_number fraction_three_t;
409  mp_number fraction_four_t;
410  mp_number one_eighty_deg_t;
411  mp_number three_sixty_deg_t;
412  mp_number one_k;
413  mp_number sqrt_8_e_k;
414  mp_number twelve_ln_2_k;
415  mp_number coef_bound_k;
416  mp_number coef_bound_minus_1;
417  mp_number twelvebits_3;
418  mp_number arc_tol_k;
419  mp_number twentysixbits_sqrt2_t;
420  mp_number twentyeightbits_d_t;
421  mp_number twentysevenbits_sqrt2_d_t;
422  mp_number fraction_threshold_t;
423  mp_number half_fraction_threshold_t;
424  mp_number scaled_threshold_t;
425  mp_number half_scaled_threshold_t;
426  mp_number near_zero_angle_t;
427  mp_number p_over_v_threshold_t;
428  mp_number equation_threshold_t;
429  mp_number tfm_warn_threshold_t;
430  mp_number warning_limit_t;
431  new_number_func allocate;
432  free_number_func free;
433  number_from_int_func from_int;
434  number_from_boolean_func from_boolean;
435  number_from_scaled_func from_scaled;
436  number_from_double_func from_double;
437  number_from_addition_func from_addition;
438  number_from_substraction_func from_substraction;
439  number_from_div_func from_div;
440  number_from_mul_func from_mul;
441  number_from_int_div_func from_int_div;
442  number_from_int_mul_func from_int_mul;
443  number_from_oftheway_func from_oftheway;
444  number_negate_func negate;
445  number_add_func add;
446  number_substract_func substract;
447  number_half_func half;
448  number_modulo_func modulo;
449  number_halfp_func halfp;
450  number_double_func do_double;
451  number_abs_func abs;
452  number_clone_func clone;
453  number_swap_func swap;
454  number_add_scaled_func add_scaled;
455  number_multiply_int_func multiply_int;
456  number_divide_int_func divide_int;
457  number_to_int_func to_int;
458  number_to_boolean_func to_boolean;
459  number_to_scaled_func to_scaled;
460  number_to_double_func to_double;
461  number_odd_func odd;
462  number_equal_func equal;
463  number_less_func less;
464  number_greater_func greater;
465  number_nonequalabs_func nonequalabs;
466  number_round_func round_unscaled;
467  number_floor_func floor_scaled;
468  make_scaled_func make_scaled;
469  make_fraction_func make_fraction;
470  take_fraction_func take_fraction;
471  take_scaled_func take_scaled;
472  velocity_func velocity;
473  ab_vs_cd_func ab_vs_cd;
474  crossing_point_func crossing_point;
475  n_arg_func  n_arg;
476  m_log_func  m_log;
477  m_exp_func  m_exp;
478  m_norm_rand_func m_norm_rand;
479  pyth_add_func pyth_add;
480  pyth_sub_func pyth_sub;
481  fraction_to_round_scaled_func fraction_to_round_scaled;
482  convert_func fraction_to_scaled;
483  convert_func scaled_to_fraction;
484  convert_func scaled_to_angle;
485  convert_func angle_to_scaled;
486  init_randoms_func init_randoms;
487  sin_cos_func sin_cos;
488  sqrt_func sqrt;
489  slow_add_func slow_add;
490  print_func print;
491  tostring_func tostring;
492  scan_func scan_numeric;
493  scan_func scan_fractional;
494  mp_free_func free_math;
495  set_precision_func set_precision;
496} math_data;
497
498
499
500@ This procedure gets things started properly.
501@c
502MP mp_initialize (MP_options * opt) {
503  MP mp;
504  jmp_buf *buf = malloc (sizeof (jmp_buf));
505  if (buf == NULL || setjmp (*buf) != 0)
506    return NULL;
507  mp = mp_do_new (buf);
508  if (mp == NULL)
509    return NULL;
510  mp->userdata = opt->userdata;
511  mp->noninteractive = opt->noninteractive;
512  mp->extensions = opt->extensions;
513  set_callback_option (find_file);
514  set_callback_option (open_file);
515  set_callback_option (read_ascii_file);
516  set_callback_option (read_binary_file);
517  set_callback_option (close_file);
518  set_callback_option (eof_file);
519  set_callback_option (flush_file);
520  set_callback_option (write_ascii_file);
521  set_callback_option (write_binary_file);
522  set_callback_option (shipout_backend);
523  set_callback_option (run_script);
524  set_callback_option (make_text);
525  if (opt->banner && *(opt->banner)) {
526    mp->banner = xstrdup (opt->banner);
527  } else {
528    mp->banner = xstrdup (default_banner);
529  }
530  if (opt->command_line && *(opt->command_line))
531    mp->command_line = xstrdup (opt->command_line);
532  if (mp->noninteractive) {
533    @<Prepare function pointers for non-interactive use@>;
534  }
535  /* open the terminal for output */
536  t_open_out();
537#if DEBUG
538  setvbuf(stdout, (char *) NULL, _IONBF, 0);
539  setvbuf(mp->term_out, (char *) NULL, _IONBF, 0);
540#endif
541  if (opt->math_mode == mp_math_scaled_mode) {
542    mp->math = mp_initialize_scaled_math(mp);
543  } else if (opt->math_mode == mp_math_decimal_mode) {
544    mp->math = mp_initialize_decimal_math(mp);
545  } else if (opt->math_mode == mp_math_binary_mode) {
546    mp->math = mp_initialize_binary_math(mp);
547  } else {
548    mp->math = mp_initialize_double_math(mp);
549  }
550  @<Find and load preload file, if required@>;
551  @<Allocate or initialize variables@>;
552  mp_reallocate_paths (mp, 1000);
553  mp_reallocate_fonts (mp, 8);
554  mp->history = mp_fatal_error_stop;    /* in case we quit during initialization */
555  @<Check the ``constant'' values...@>;
556  if (mp->bad > 0) {
557    char ss[256];
558    mp_snprintf (ss, 256, "Ouch---my internal constants have been clobbered!\n"
559                 "---case %i", (int) mp->bad);
560    mp_fputs ((char *) ss, mp->err_out);
561@.Ouch...clobbered@>;
562    return mp;
563  }
564  mp_do_initialize (mp);        /* erase preloaded mem */
565  mp_init_tab (mp);             /* initialize the tables */
566  if (opt->math_mode == mp_math_scaled_mode) {
567    set_internal_string (mp_number_system, mp_intern (mp, "scaled"));
568  } else if (opt->math_mode == mp_math_decimal_mode) {
569    set_internal_string (mp_number_system, mp_intern (mp, "decimal"));
570  } else if (opt->math_mode == mp_math_binary_mode) {
571    set_internal_string (mp_number_system, mp_intern (mp, "binary"));
572  } else {
573    set_internal_string (mp_number_system, mp_intern (mp, "double"));
574  }
575  mp_init_prim (mp);            /* call |primitive| for each primitive */
576  mp_fix_date_and_time (mp);
577  if (!mp->noninteractive) {
578    @<Initialize the output routines@>;
579    @<Get the first line of input and prepare to start@>;
580    @<Initializations after first line is read@>;
581    @<Fix up |mp->internal[mp_job_name]|@>;
582  } else {
583    mp->history = mp_spotless;
584  }
585  set_precision();
586  return mp;
587}
588
589
590@ @<Initializations after first line is read@>=
591mp_open_log_file (mp);
592mp_set_job_id (mp);
593mp_init_map_file (mp, mp->troff_mode);
594mp->history = mp_spotless;      /* ready to go! */
595if (mp->troff_mode) {
596  number_clone (internal_value (mp_gtroffmode), unity_t);
597  number_clone (internal_value (mp_prologues), unity_t);
598}
599if (mp->start_sym != NULL) {    /* insert the `\&{everyjob}' symbol */
600  set_cur_sym (mp->start_sym);
601  mp_back_input (mp);
602}
603
604@ @<Exported function headers@>=
605extern MP_options *mp_options (void);
606extern MP mp_initialize (MP_options * opt);
607extern int mp_status (MP mp);
608extern void *mp_userdata (MP mp);
609
610@ @c
611int mp_status (MP mp) {
612  return mp->history;
613}
614
615
616@ @c
617void *mp_userdata (MP mp) {
618  return mp->userdata;
619}
620
621
622@ The overall \MP\ program begins with the heading just shown, after which
623comes a bunch of procedure declarations and function declarations.
624Finally we will get to the main program, which begins with the
625comment `|start_here|'. If you want to skip down to the
626main program now, you can look up `|start_here|' in the index.
627But the author suggests that the best way to understand this program
628is to follow pretty much the order of \MP's components as they appear in the
629\.{WEB} description you are now reading, since the present ordering is
630intended to combine the advantages of the ``bottom up'' and ``top down''
631approaches to the problem of understanding a somewhat complicated system.
632
633@ Some of the code below is intended to be used only when diagnosing the
634strange behavior that sometimes occurs when \MP\ is being installed or
635when system wizards are fooling around with \MP\ without quite knowing
636what they are doing. Such code will not normally be compiled; it is
637delimited by the preprocessor test `|#ifdef DEBUG .. #endif|'.
638
639@ The following parameters can be changed at compile time to extend or
640reduce \MP's capacity.
641@^system dependencies@>
642
643@<Constants...@>=
644#define bistack_size 1500       /* size of stack for bisection algorithms;
645                                   should probably be left at this value */
646
647@ Like the preceding parameters, the following quantities can be changed
648to extend or reduce \MP's capacity.
649
650@ @<Glob...@>=
651int pool_size;  /* maximum number of characters in strings, including all
652                   error messages and help texts, and the names of all identifiers */
653int max_in_open;        /* maximum number of input files and error insertions that
654                           can be going on simultaneously */
655int param_size; /* maximum number of simultaneous macro parameters */
656
657@ @<Option variables@>=
658int error_line; /* width of context lines on terminal error messages */
659int half_error_line;    /* width of first lines of contexts in terminal
660                           error messages; should be between 30 and |error_line-15| */
661int halt_on_error;      /* do we quit at the first error? */
662int max_print_line;     /* width of longest text lines output; should be at least 60 */
663void *userdata; /* this allows the calling application to setup local */
664char *banner;   /* the banner that is printed to the screen and log */
665int ini_version;
666
667@ @<Dealloc variables@>=
668xfree (mp->banner);
669
670@
671@d set_lower_limited_value(a,b,c) do { a=c; if (b>c) a=b; } while (0)
672
673@<Allocate or ...@>=
674mp->param_size = 4;
675mp->max_in_open = 0;
676mp->pool_size = 10000;
677set_lower_limited_value (mp->error_line, opt->error_line, 79);
678set_lower_limited_value (mp->half_error_line, opt->half_error_line, 50);
679if (mp->half_error_line > mp->error_line - 15)
680  mp->half_error_line = mp->error_line - 15;
681mp->max_print_line = 100;
682set_lower_limited_value (mp->max_print_line, opt->max_print_line, 79);
683mp->halt_on_error = (opt->halt_on_error ? true : false);
684mp->ini_version = (opt->ini_version ? true : false);
685
686@ In case somebody has inadvertently made bad settings of the ``constants,''
687\MP\ checks them using a global variable called |bad|.
688
689This is the second of many sections of \MP\ where global variables are
690defined.
691
692@<Glob...@>=
693integer bad;    /* is some ``constant'' wrong? */
694
695@ Later on we will say `|if ( int_packets+17*int_increment>bistack_size )mp->bad=19;|',
696or something similar.
697
698In case you are wondering about the non-consequtive values of |bad|: most
699of the things that used to be WEB constants are now runtime variables
700with checking at assignment time.
701
702@<Check the ``constant'' values for consistency@>=
703mp->bad = 0;
704
705@ Here are some macros for common programming idioms.
706
707@d incr(A)   (A)=(A)+1 /* increase a variable by unity */
708@d decr(A)   (A)=(A)-1 /* decrease a variable by unity */
709@d negate(A) (A)=-(A) /* change the sign of a variable */
710@d double(A) (A)=(A)+(A)
711@d odd(A)   (abs(A)%2==1)
712
713@* The character set.
714In order to make \MP\ readily portable to a wide variety of
715computers, all of its input text is converted to an internal eight-bit
716code that includes standard ASCII, the ``American Standard Code for
717Information Interchange.''  This conversion is done immediately when each
718character is read in. Conversely, characters are converted from ASCII to
719the user's external representation just before they are output to a
720text file.
721@^ASCII code@>
722
723Such an internal code is relevant to users of \MP\ only with respect to
724the \&{char} and \&{ASCII} operations, and the comparison of strings.
725
726@ Characters of text that have been converted to \MP's internal form
727are said to be of type |ASCII_code|, which is a subrange of the integers.
728
729@<Types...@>=
730typedef unsigned char ASCII_code;       /* eight-bit numbers */
731
732@ The present specification of \MP\ has been written under the assumption
733that the character set contains at least the letters and symbols associated
734with ASCII codes 040 through 0176; all of these characters are now
735available on most computer terminals.
736
737@<Types...@>=
738typedef unsigned char text_char;        /* the data type of characters in text files */
739
740@ @<Local variables for init...@>=
741integer i;
742
743@ The \MP\ processor converts between ASCII code and
744the user's external character set by means of arrays |xord| and |xchr|
745that are analogous to Pascal's |ord| and |chr| functions.
746
747@<MPlib internal header stuff@>=
748#define xchr(A) mp->xchr[(A)]
749#define xord(A) mp->xord[(A)]
750
751@ @<Glob...@>=
752ASCII_code xord[256];   /* specifies conversion of input characters */
753text_char xchr[256];    /* specifies conversion of output characters */
754
755@ The core system assumes all 8-bit is acceptable.  If it is not,
756a change file has to alter the below section.
757@^system dependencies@>
758
759Additionally, people with extended character sets can
760assign codes arbitrarily, giving an |xchr| equivalent to whatever
761characters the users of \MP\ are allowed to have in their input files.
762Appropriate changes to \MP's |char_class| table should then be made.
763(Unlike \TeX, each installation of \MP\ has a fixed assignment of category
764codes, called the |char_class|.) Such changes make portability of programs
765more difficult, so they should be introduced cautiously if at all.
766@^character set dependencies@>
767@^system dependencies@>
768
769@<Set initial ...@>=
770for (i = 0; i <= 0377; i++) {
771  xchr (i) = (text_char) i;
772}
773
774
775@ The following system-independent code makes the |xord| array contain a
776suitable inverse to the information in |xchr|. Note that if |xchr[i]=xchr[j]|
777where |i<j<0177|, the value of |xord[xchr[i]]| will turn out to be
778|j| or more; hence, standard ASCII code numbers will be used instead of
779codes below 040 in case there is a coincidence.
780
781@<Set initial ...@>=
782for (i = 0; i <= 255; i++) {
783  xord (xchr (i)) = 0177;
784}
785for (i = 0200; i <= 0377; i++) {
786  xord (xchr (i)) = (ASCII_code) i;
787}
788for (i = 0; i <= 0176; i++) {
789  xord (xchr (i)) = (ASCII_code) i;
790}
791
792
793@* Input and output.
794The bane of portability is the fact that different operating systems treat
795input and output quite differently, perhaps because computer scientists
796have not given sufficient attention to this problem. People have felt somehow
797that input and output are not part of ``real'' programming. Well, it is true
798that some kinds of programming are more fun than others. With existing
799input/output conventions being so diverse and so messy, the only sources of
800joy in such parts of the code are the rare occasions when one can find a
801way to make the program a little less bad than it might have been. We have
802two choices, either to attack I/O now and get it over with, or to postpone
803I/O until near the end. Neither prospect is very attractive, so let's
804get it over with.
805
806The basic operations we need to do are (1)~inputting and outputting of
807text, to or from a file or the user's terminal; (2)~inputting and
808outputting of eight-bit bytes, to or from a file; (3)~instructing the
809operating system to initiate (``open'') or to terminate (``close'') input or
810output from a specified file; (4)~testing whether the end of an input
811file has been reached; (5)~display of bits on the user's screen.
812The bit-display operation will be discussed in a later section; we shall
813deal here only with more traditional kinds of I/O.
814
815@ Finding files happens in a slightly roundabout fashion: the \MP\
816instance object contains a field that holds a function pointer that finds a
817file, and returns its name, or NULL. For this, it receives three
818parameters: the non-qualified name |fname|, the intended |fopen|
819operation type |fmode|, and the type of the file |ftype|.
820
821The file types that are passed on in |ftype| can be  used to
822differentiate file searches if a library like kpathsea is used,
823the fopen mode is passed along for the same reason.
824
825@<Types...@>=
826typedef unsigned char eight_bits;       /* unsigned one-byte quantity */
827
828@ @<Exported types@>=
829enum mp_filetype {
830  mp_filetype_terminal = 0,     /* the terminal */
831  mp_filetype_error,            /* the terminal */
832  mp_filetype_program,          /* \MP\ language input */
833  mp_filetype_log,              /* the log file */
834  mp_filetype_postscript,       /* the postscript output */
835  mp_filetype_bitmap,           /* the bitmap output file */
836  mp_filetype_memfile,          /* memory dumps, obsolete */
837  mp_filetype_metrics,          /* TeX font metric files */
838  mp_filetype_fontmap,          /* PostScript font mapping files */
839  mp_filetype_font,             /*  PostScript type1 font programs */
840  mp_filetype_encoding,         /*  PostScript font encoding files */
841  mp_filetype_text              /* first text file for readfrom and writeto primitives */
842};
843typedef char *(*mp_file_finder) (MP, const char *, const char *, int);
844typedef char *(*mp_script_runner) (MP, const char *);
845typedef char *(*mp_text_maker) (MP, const char *, int mode);
846typedef void *(*mp_file_opener) (MP, const char *, const char *, int);
847typedef char *(*mp_file_reader) (MP, void *, size_t *);
848typedef void (*mp_binfile_reader) (MP, void *, void **, size_t *);
849typedef void (*mp_file_closer) (MP, void *);
850typedef int (*mp_file_eoftest) (MP, void *);
851typedef void (*mp_file_flush) (MP, void *);
852typedef void (*mp_file_writer) (MP, void *, const char *);
853typedef void (*mp_binfile_writer) (MP, void *, void *, size_t);
854
855@ @<Option variables@>=
856mp_file_finder find_file;
857mp_file_opener open_file;
858mp_script_runner run_script;
859mp_text_maker make_text;
860mp_file_reader read_ascii_file;
861mp_binfile_reader read_binary_file;
862mp_file_closer close_file;
863mp_file_eoftest eof_file;
864mp_file_flush flush_file;
865mp_file_writer write_ascii_file;
866mp_binfile_writer write_binary_file;
867
868@ The default function for finding files is |mp_find_file|. It is
869pretty stupid: it will only find files in the current directory.
870
871@c
872static char *mp_find_file (MP mp, const char *fname, const char *fmode,
873                           int ftype) {
874  (void) mp;
875  if (fmode[0] != 'r' || (!access (fname, R_OK)) || ftype) {
876    return mp_strdup (fname);
877  }
878  return NULL;
879}
880
881@ @c
882static char *mp_run_script (MP mp, const char *str) {
883  (void) mp;
884  return mp_strdup (str);
885}
886
887@ @c
888static char *mp_make_text (MP mp, const char *str, int mode) {
889  (void) mp;
890  return mp_strdup (str);
891}
892
893@ Because |mp_find_file| is used so early, it has to be in the helpers
894section.
895
896@<Declarations@>=
897static char *mp_find_file (MP mp, const char *fname, const char *fmode,
898                           int ftype);
899static void *mp_open_file (MP mp, const char *fname, const char *fmode,
900                           int ftype);
901static char *mp_read_ascii_file (MP mp, void *f, size_t * size);
902static void mp_read_binary_file (MP mp, void *f, void **d, size_t * size);
903static void mp_close_file (MP mp, void *f);
904static int mp_eof_file (MP mp, void *f);
905static void mp_flush_file (MP mp, void *f);
906static void mp_write_ascii_file (MP mp, void *f, const char *s);
907static void mp_write_binary_file (MP mp, void *f, void *s, size_t t);
908static char *mp_run_script (MP mp, const char *str);
909static char *mp_make_text (MP mp, const char *str, int mode);
910
911@ The function to open files can now be very short.
912
913@c
914void *mp_open_file (MP mp, const char *fname, const char *fmode, int ftype) {
915  char realmode[3];
916  (void) mp;
917  realmode[0] = *fmode;
918  realmode[1] = 'b';
919  realmode[2] = 0;
920  if (ftype == mp_filetype_terminal) {
921    return (fmode[0] == 'r' ? stdin : stdout);
922  } else if (ftype == mp_filetype_error) {
923    return stderr;
924  } else if (fname != NULL && (fmode[0] != 'r' || (!access (fname, R_OK)))) {
925    return (void *) fopen (fname, realmode);
926  }
927  return NULL;
928}
929
930
931@ (Almost) all file names pass through |name_of_file|.
932
933@<Glob...@>=
934char *name_of_file;     /* the name of a system file */
935
936@ If this parameter is true, the terminal and log will report the found
937file names for input files instead of the requested ones.
938It is off by default because it creates an extra filename lookup.
939
940@<Option variables@>=
941int print_found_names;  /* configuration parameter */
942
943@ @<Allocate or initialize ...@>=
944mp->print_found_names = (opt->print_found_names > 0 ? true : false);
945
946@ The |file_line_error_style| parameter makes \MP\ use a more
947standard compiler error message format instead of the Knuthian
948exclamation mark. It needs the actual version of the current input
949file name, that will be saved by |open_in| in the |long_name|.
950
951TODO: currently these long strings cause memory leaks, because they cannot
952be safely freed as they may appear in the |input_stack| multiple times.
953In fact, the current implementation is just a quick hack in response
954to a bug report for metapost 1.205.
955
956@d long_name mp->cur_input.long_name_field /* long name of the current file */
957
958@<Option variables@>=
959int file_line_error_style;      /* configuration parameter */
960
961@ @<Allocate or initialize ...@>=
962mp->file_line_error_style = (opt->file_line_error_style > 0 ? true : false);
963
964@ \MP's file-opening procedures return |false| if no file identified by
965|name_of_file| could be opened.
966
967The |do_open_file| function takes care of the |print_found_names| parameter.
968
969@c
970static boolean mp_do_open_file (MP mp, void **f, int ftype, const char *mode) {
971  if (mp->print_found_names || mp->file_line_error_style) {
972    char *s = (mp->find_file)(mp,mp->name_of_file,mode,ftype);
973    if (s!=NULL) {
974      *f = (mp->open_file)(mp,mp->name_of_file,mode, ftype);
975      if (mp->print_found_names) {
976        xfree(mp->name_of_file);
977        mp->name_of_file = xstrdup(s);
978      }
979      if ((*mode == 'r') && (ftype == mp_filetype_program)) {
980        long_name = xstrdup(s);
981      }
982      xfree(s);
983    } else {
984      *f = NULL;
985    }
986  } else {
987    *f = (mp->open_file)(mp,mp->name_of_file,mode, ftype);
988  }
989  return (*f ? true : false);
990}
991@#
992static boolean mp_open_in (MP mp, void **f, int ftype) {
993  /* open a file for input */
994  return mp_do_open_file (mp, f, ftype, "r");
995}
996@#
997static boolean mp_open_out (MP mp, void **f, int ftype) {
998  /* open a file for output */
999  return mp_do_open_file (mp, f, ftype, "w");
1000}
1001
1002
1003@ @c
1004static char *mp_read_ascii_file (MP mp, void *ff, size_t * size) {
1005  int c;
1006  size_t len = 0, lim = 128;
1007  char *s = NULL;
1008  FILE *f = (FILE *) ff;
1009  *size = 0;
1010  (void) mp;                    /* for -Wunused */
1011  if (f == NULL)
1012    return NULL;
1013  c = fgetc (f);
1014  if (c == EOF)
1015    return NULL;
1016  s = malloc (lim);
1017  if (s == NULL)
1018    return NULL;
1019  while (c != EOF && c != '\n' && c != '\r') {
1020    if ((len + 1) == lim) {
1021      s = realloc (s, (lim + (lim >> 2)));
1022      if (s == NULL)
1023        return NULL;
1024      lim += (lim >> 2);
1025    }
1026    s[len++] = (char) c;
1027    c = fgetc (f);
1028  }
1029  if (c == '\r') {
1030    c = fgetc (f);
1031    if (c != EOF && c != '\n')
1032      ungetc (c, f);
1033  }
1034  s[len] = 0;
1035  *size = len;
1036  return s;
1037}
1038
1039
1040@ @c
1041void mp_write_ascii_file (MP mp, void *f, const char *s) {
1042  (void) mp;
1043  if (f != NULL) {
1044    fputs (s, (FILE *) f);
1045  }
1046}
1047
1048
1049@ @c
1050void mp_read_binary_file (MP mp, void *f, void **data, size_t * size) {
1051  size_t len = 0;
1052  (void) mp;
1053  if (f != NULL)
1054    len = fread (*data, 1, *size, (FILE *) f);
1055  *size = len;
1056}
1057
1058
1059@ @c
1060void mp_write_binary_file (MP mp, void *f, void *s, size_t size) {
1061  (void) mp;
1062  if (f != NULL)
1063    (void) fwrite (s, size, 1, (FILE *) f);
1064}
1065
1066
1067@ @c
1068void mp_close_file (MP mp, void *f) {
1069  (void) mp;
1070  if (f != NULL)
1071    fclose ((FILE *) f);
1072}
1073
1074
1075@ @c
1076int mp_eof_file (MP mp, void *f) {
1077  (void) mp;
1078  if (f != NULL)
1079    return feof ((FILE *) f);
1080  else
1081    return 1;
1082}
1083
1084
1085@ @c
1086void mp_flush_file (MP mp, void *f) {
1087  (void) mp;
1088  if (f != NULL)
1089    fflush ((FILE *) f);
1090}
1091
1092
1093@ Input from text files is read one line at a time, using a routine called
1094|input_ln|. This function is defined in terms of global variables called
1095|buffer|, |first|, and |last| that will be described in detail later; for
1096now, it suffices for us to know that |buffer| is an array of |ASCII_code|
1097values, and that |first| and |last| are indices into this array
1098representing the beginning and ending of a line of text.
1099
1100@<Glob...@>=
1101size_t buf_size;        /* maximum number of characters simultaneously present in
1102                           current lines of open files */
1103ASCII_code *buffer;     /* lines of characters being read */
1104size_t first;   /* the first unused position in |buffer| */
1105size_t last;    /* end of the line just input to |buffer| */
1106size_t max_buf_stack;   /* largest index used in |buffer| */
1107
1108@ @<Allocate or initialize ...@>=
1109mp->buf_size = 200;
1110mp->buffer = xmalloc ((mp->buf_size + 1), sizeof (ASCII_code));
1111
1112@ @<Dealloc variables@>=
1113xfree (mp->buffer);
1114
1115@ @c
1116static void mp_reallocate_buffer (MP mp, size_t l) {
1117  ASCII_code *buffer;
1118  if (l > max_halfword) {
1119    mp_confusion (mp, "buffer size");   /* can't happen (I hope) */
1120  }
1121  buffer = xmalloc ((l + 1), sizeof (ASCII_code));
1122  (void) memcpy (buffer, mp->buffer, (mp->buf_size + 1));
1123  xfree (mp->buffer);
1124  mp->buffer = buffer;
1125  mp->buf_size = l;
1126}
1127
1128
1129@ The |input_ln| function brings the next line of input from the specified
1130field into available positions of the buffer array and returns the value
1131|true|, unless the file has already been entirely read, in which case it
1132returns |false| and sets |last:=first|.  In general, the |ASCII_code|
1133numbers that represent the next line of the file are input into
1134|buffer[first]|, |buffer[first+1]|, \dots, |buffer[last-1]|; and the
1135global variable |last| is set equal to |first| plus the length of the
1136line. Trailing blanks are removed from the line; thus, either |last=first|
1137(in which case the line was entirely blank) or |buffer[last-1]<>" "|.
1138@^inner loop@>
1139
1140The variable |max_buf_stack|, which is used to keep track of how large
1141the |buf_size| parameter must be to accommodate the present job, is
1142also kept up to date by |input_ln|.
1143
1144@c
1145static boolean mp_input_ln (MP mp, void *f) {
1146  /* inputs the next line or returns |false| */
1147  char *s;
1148  size_t size = 0;
1149  mp->last = mp->first;         /* cf.\ Matthew 19\thinspace:\thinspace30 */
1150  s = (mp->read_ascii_file) (mp, f, &size);
1151  if (s == NULL)
1152    return false;
1153  if (size > 0) {
1154    mp->last = mp->first + size;
1155    if (mp->last >= mp->max_buf_stack) {
1156      mp->max_buf_stack = mp->last + 1;
1157      while (mp->max_buf_stack > mp->buf_size) {
1158        mp_reallocate_buffer (mp, (mp->buf_size + (mp->buf_size >> 2)));
1159      }
1160    }
1161    (void) memcpy ((mp->buffer + mp->first), s, size);
1162  }
1163  free (s);
1164  return true;
1165}
1166
1167
1168@ The user's terminal acts essentially like other files of text, except
1169that it is used both for input and for output. When the terminal is
1170considered an input file, the file variable is called |term_in|, and when it
1171is considered an output file the file variable is |term_out|.
1172@^system dependencies@>
1173
1174@<Glob...@>=
1175void *term_in;  /* the terminal as an input file */
1176void *term_out; /* the terminal as an output file */
1177void *err_out;  /* the terminal as an output file */
1178
1179@ Here is how to open the terminal files. In the default configuration,
1180nothing happens except that the command line (if there is one) is copied
1181to the input buffer.  The variable |command_line| will be filled by the
1182|main| procedure.
1183
1184@d t_open_out()  do {/* open the terminal for text output */
1185    mp->term_out = (mp->open_file)(mp,"terminal", "w", mp_filetype_terminal);
1186    mp->err_out = (mp->open_file)(mp,"error", "w", mp_filetype_error);
1187} while (0)
1188@d t_open_in()  do { /* open the terminal for text input */
1189    mp->term_in = (mp->open_file)(mp,"terminal", "r", mp_filetype_terminal);
1190    if (mp->command_line!=NULL) {
1191      mp->last = strlen(mp->command_line);
1192      (void)memcpy((void *)mp->buffer,(void *)mp->command_line,mp->last);
1193      xfree(mp->command_line);
1194    } else {
1195	  mp->last = 0;
1196    }
1197} while (0)
1198
1199@<Option variables@>=
1200char *command_line;
1201
1202@ Sometimes it is necessary to synchronize the input/output mixture that
1203happens on the user's terminal, and three system-dependent
1204procedures are used for this
1205purpose. The first of these, |update_terminal|, is called when we want
1206to make sure that everything we have output to the terminal so far has
1207actually left the computer's internal buffers and been sent.
1208The second, |clear_terminal|, is called when we wish to cancel any
1209input that the user may have typed ahead (since we are about to
1210issue an unexpected error message). The third, |wake_up_terminal|,
1211is supposed to revive the terminal if the user has disabled it by
1212some instruction to the operating system.  The following macros show how
1213these operations can be specified:
1214@^system dependencies@>
1215
1216@<MPlib internal header stuff@>=
1217#define update_terminal()  (mp->flush_file)(mp,mp->term_out)      /* empty the terminal output buffer */
1218#define clear_terminal()          /* clear the terminal input buffer */
1219#define wake_up_terminal() (mp->flush_file)(mp,mp->term_out)
1220                    /* cancel the user's cancellation of output */
1221
1222@ We need a special routine to read the first line of \MP\ input from
1223the user's terminal. This line is different because it is read before we
1224have opened the transcript file; there is sort of a ``chicken and
1225egg'' problem here. If the user types `\.{input cmr10}' on the first
1226line, or if some macro invoked by that line does such an \.{input},
1227the transcript file will be named `\.{cmr10.log}'; but if no \.{input}
1228commands are performed during the first line of terminal input, the transcript
1229file will acquire its default name `\.{mpout.log}'. (The transcript file
1230will not contain error messages generated by the first line before the
1231first \.{input} command.)
1232
1233The first line is even more special. It's nice to let the user start
1234running a \MP\ job by typing a command line like `\.{MP cmr10}'; in
1235such a case, \MP\ will operate as if the first line of input were
1236`\.{cmr10}', i.e., the first line will consist of the remainder of the
1237command line, after the part that invoked \MP.
1238
1239@ Different systems have different ways to get started. But regardless of
1240what conventions are adopted, the routine that initializes the terminal
1241should satisfy the following specifications:
1242
1243\yskip\textindent{1)}It should open file |term_in| for input from the
1244  terminal. (The file |term_out| will already be open for output to the
1245  terminal.)
1246
1247\textindent{2)}If the user has given a command line, this line should be
1248  considered the first line of terminal input. Otherwise the
1249  user should be prompted with `\.{**}', and the first line of input
1250  should be whatever is typed in response.
1251
1252\textindent{3)}The first line of input, which might or might not be a
1253  command line, should appear in locations |first| to |last-1| of the
1254  |buffer| array.
1255
1256\textindent{4)}The global variable |loc| should be set so that the
1257  character to be read next by \MP\ is in |buffer[loc]|. This
1258  character should not be blank, and we should have |loc<last|.
1259
1260\yskip\noindent(It may be necessary to prompt the user several times
1261before a non-blank line comes in. The prompt is `\.{**}' instead of the
1262later `\.*' because the meaning is slightly different: `\.{input}' need
1263not be typed immediately after~`\.{**}'.)
1264
1265@d loc mp->cur_input.loc_field /* location of first unread character in |buffer| */
1266
1267@c
1268boolean mp_init_terminal (MP mp) {                               /* gets the terminal input started */
1269  t_open_in();
1270  if (mp->last != 0) {
1271    loc = 0;
1272    mp->first = 0;
1273    return true;
1274  }
1275  while (1) {
1276    if (!mp->noninteractive) {
1277      wake_up_terminal();
1278      mp_fputs ("**", mp->term_out);
1279@.**@>;
1280      update_terminal();
1281    }
1282    if (!mp_input_ln (mp, mp->term_in)) {       /* this shouldn't happen */
1283      mp_fputs ("\n! End of file on the terminal... why?", mp->term_out);
1284@.End of file on the terminal@>;
1285      return false;
1286    }
1287    loc = (halfword) mp->first;
1288    while ((loc < (int) mp->last) && (mp->buffer[loc] == ' '))
1289      incr (loc);
1290    if (loc < (int) mp->last) {
1291      return true;              /* return unless the line was all blank */
1292    }
1293    if (!mp->noninteractive) {
1294      mp_fputs ("Please type the name of your input file.\n", mp->term_out);
1295    }
1296  }
1297}
1298
1299
1300@ @<Declarations@>=
1301static boolean mp_init_terminal (MP mp);
1302
1303@* Globals for strings.
1304
1305@ Symbolic token names and diagnostic messages are variable-length strings
1306of eight-bit characters. Many strings \MP\ uses are simply literals
1307in the compiled source, like the error messages and the names of the
1308internal parameters. Other strings are used or defined from the \MP\ input
1309language, and these have to be interned.
1310
1311\MP\ uses strings more extensively than \MF\ does, but the necessary
1312operations can still be handled with a fairly simple data structure.
1313The avl tree |strings| contains all of the known string structures.
1314
1315Each structure contains an |unsigned char| pointer containing the eight-bit
1316data, a |size_t| that holds the length of that data, and an |int| that
1317indicates how often this string is referenced (this will be explained below).
1318Such strings are referred to by structure pointers called |mp_string|.
1319
1320Besides the avl tree, there is a set of three variables called |cur_string|,
1321|cur_length| and |cur_string_size| that are used for strings while they are
1322being built.
1323
1324@<Exported types...@>=
1325typedef struct {
1326  unsigned char *str;   /* the string value */
1327  size_t len;   /* its length */
1328  int refs;     /* number of references */
1329} mp_lstring;
1330typedef mp_lstring *mp_string; /* for pointers to string values */
1331
1332@ The string handling functions are in \.{mpstrings.w}, but strings
1333need a bunch of globals and those are defined here in the main file.
1334
1335@<Glob...@>=
1336avl_tree strings;       /* string avl tree */
1337unsigned char *cur_string;      /*  current string buffer */
1338size_t cur_length;      /* current index in that buffer */
1339size_t cur_string_size; /*  malloced size of |cur_string| */
1340
1341@ @<Allocate or initialize ...@>=
1342mp_initialize_strings(mp);
1343
1344@ @<Dealloc variables@>=
1345mp_dealloc_strings(mp);
1346
1347@ The next four variables are for keeping track of string memory usage.
1348
1349@<Glob...@>=
1350integer pool_in_use;    /* total number of string bytes actually in use */
1351integer max_pl_used;    /* maximum |pool_in_use| so far */
1352integer strs_in_use;    /* total number of strings actually in use */
1353integer max_strs_used;  /* maximum |strs_in_use| so far */
1354
1355
1356@* On-line and off-line printing.
1357Messages that are sent to a user's terminal and to the transcript-log file
1358are produced by several `|print|' procedures. These procedures will
1359direct their output to a variety of places, based on the setting of
1360the global variable |selector|, which has the following possible
1361values:
1362
1363\yskip
1364\hang |term_and_log|, the normal setting, prints on the terminal and on the
1365  transcript file.
1366
1367\hang |log_only|, prints only on the transcript file.
1368
1369\hang |term_only|, prints only on the terminal.
1370
1371\hang |no_print|, doesn't print at all. This is used only in rare cases
1372  before the transcript file is open.
1373
1374\hang |pseudo|, puts output into a cyclic buffer that is used
1375  by the |show_context| routine; when we get to that routine we shall discuss
1376  the reasoning behind this curious mode.
1377
1378\hang |new_string|, appends the output to the current string in the
1379  string pool.
1380
1381\hang |>=write_file| prints on one of the files used for the \&{write}
1382@:write_}{\&{write} primitive@>
1383  command.
1384
1385\yskip
1386\noindent The symbolic names `|term_and_log|', etc., have been assigned
1387numeric codes that satisfy the convenient relations |no_print+1=term_only|,
1388|no_print+2=log_only|, |term_only+2=log_only+1=term_and_log|.  These
1389relations are not used when |selector| could be |pseudo|, or |new_string|.
1390We need not check for unprintable characters when |selector<pseudo|.
1391
1392Three additional global variables, |tally|, |term_offset| and |file_offset|
1393record the number of characters that have been printed
1394since they were most recently cleared to zero. We use |tally| to record
1395the length of (possibly very long) stretches of printing; |term_offset|,
1396and |file_offset|, on the other hand, keep track of how many
1397characters have appeared so far on the current line that has been output
1398to the terminal, the transcript file, or the \ps\ output file, respectively.
1399
1400@d new_string 0 /* printing is deflected to the string pool */
1401@d pseudo 2 /* special |selector| setting for |show_context| */
1402@d no_print 3 /* |selector| setting that makes data disappear */
1403@d term_only 4 /* printing is destined for the terminal only */
1404@d log_only 5 /* printing is destined for the transcript file only */
1405@d term_and_log 6 /* normal |selector| setting */
1406@d write_file 7 /* first write file selector */
1407
1408@<Glob...@>=
1409void *log_file; /* transcript of \MP\ session */
1410void *output_file;      /* the generic font output goes here */
1411unsigned int selector;  /* where to print a message */
1412integer tally;  /* the number of characters recently printed */
1413unsigned int term_offset;
1414  /* the number of characters on the current terminal line */
1415unsigned int file_offset;
1416  /* the number of characters on the current file line */
1417ASCII_code *trick_buf;  /* circular buffer for pseudoprinting */
1418integer trick_count;    /* threshold for pseudoprinting, explained later */
1419integer first_count;    /* another variable for pseudoprinting */
1420
1421@ The first 128 strings will contain 95 standard ASCII characters, and the
1422other 33 characters will be printed in three-symbol form like `\.{\^\^A}'
1423unless a system-dependent change is made here. Installations that have
1424an extended character set, where for example |xchr[032]=@t\.{'^^Z'}@>|,
1425would like string 032 to be printed as the single character 032 instead
1426of the three characters 0136, 0136, 0132 (\.{\^\^Z}). On the other hand,
1427even people with an extended character set will want to represent string
1428015 by \.{\^\^M}, since 015 is ASCII's ``carriage return'' code; the idea is
1429to produce visible strings instead of tabs or line-feeds or carriage-returns
1430or bell-rings or characters that are treated anomalously in text files.
1431
1432The boolean expression defined here should be |true| unless \MP\ internal
1433code number~|k| corresponds to a non-troublesome visible symbol in the
1434local character set.
1435If character |k| cannot be printed, and |k<0200|, then character |k+0100| or
1436|k-0100| must be printable; moreover, ASCII codes |[060..071, 0141..0146]|
1437must be printable.
1438@^character set dependencies@>
1439@^system dependencies@>
1440
1441@<Character |k| cannot be printed@>=
1442(k < ' ') || (k == 127)
1443
1444@ @<Allocate or initialize ...@>=
1445mp->trick_buf = xmalloc ((mp->error_line + 1), sizeof (ASCII_code));
1446
1447@ @<Dealloc variables@>=
1448xfree (mp->trick_buf);
1449
1450@ @<Initialize the output routines@>=
1451mp->selector = term_only;
1452mp->tally = 0;
1453mp->term_offset = 0;
1454mp->file_offset = 0;
1455
1456@ Macro abbreviations for output to the terminal and to the log file are
1457defined here for convenience. Some systems need special conventions
1458for terminal output, and it is possible to adhere to those conventions
1459by changing |wterm|, |wterm_ln|, and |wterm_cr| here.
1460@^system dependencies@>
1461
1462@<MPlib internal header stuff@>=
1463#define mp_fputs(b,f) (mp->write_ascii_file)(mp,f,b)
1464#define wterm(A)     mp_fputs((A), mp->term_out)
1465#define wterm_chr(A) { unsigned char ss[2]; ss[0]=(A); ss[1]='\0'; wterm((char *)ss);}
1466#define wterm_cr     mp_fputs("\n", mp->term_out)
1467#define wterm_ln(A)  { wterm_cr; mp_fputs((A), mp->term_out); }
1468#define wlog(A)        mp_fputs((A), mp->log_file)
1469#define wlog_chr(A)  { unsigned char ss[2]; ss[0]=(A); ss[1]='\0'; wlog((char *)ss);}
1470#define wlog_cr      mp_fputs("\n", mp->log_file)
1471#define wlog_ln(A)   { wlog_cr; mp_fputs((A), mp->log_file); }
1472
1473
1474@ To end a line of text output, we call |print_ln|.  Cases |0..max_write_files|
1475use an array |wr_file| that will be declared later.
1476
1477@d mp_print_text(A) mp_print_str(mp,text((A)))
1478
1479@<Internal library ...@>=
1480void mp_print (MP mp, const char *s);
1481void mp_printf (MP mp, const char *ss, ...);
1482void mp_print_ln (MP mp);
1483void mp_print_char (MP mp, ASCII_code k);
1484void mp_print_str (MP mp, mp_string s);
1485void mp_print_nl (MP mp, const char *s);
1486void mp_print_two (MP mp, mp_number x, mp_number y);
1487
1488@ @<Declarations@>=
1489static void mp_print_visible_char (MP mp, ASCII_code s);
1490
1491@ @<Basic print...@>=
1492void mp_print_ln (MP mp) {                               /* prints an end-of-line */
1493  switch (mp->selector) {
1494  case term_and_log:
1495    wterm_cr;
1496    wlog_cr;
1497    mp->term_offset = 0;
1498    mp->file_offset = 0;
1499    break;
1500  case log_only:
1501    wlog_cr;
1502    mp->file_offset = 0;
1503    break;
1504  case term_only:
1505    wterm_cr;
1506    mp->term_offset = 0;
1507    break;
1508  case no_print:
1509  case pseudo:
1510  case new_string:
1511    break;
1512  default:
1513    mp_fputs ("\n", mp->wr_file[(mp->selector - write_file)]);
1514  }
1515}                               /* note that |tally| is not affected */
1516
1517
1518@ The |print_visible_char| procedure sends one character to the desired
1519destination, using the |xchr| array to map it into an external character
1520compatible with |input_ln|.  (It assumes that it is always called with
1521a visible ASCII character.)  All printing comes through |print_ln| or
1522|print_char|, which ultimately calls |print_visible_char|, hence these
1523routines are the ones that limit lines to at most |max_print_line| characters.
1524But we must make an exception for the \ps\ output file since it is not safe
1525to cut up lines arbitrarily in \ps.
1526
1527@<Basic printing...@>=
1528static void mp_print_visible_char (MP mp, ASCII_code s) {                               /* prints a single character */
1529  switch (mp->selector) {
1530  case term_and_log:
1531    wterm_chr (xchr (s));
1532    wlog_chr (xchr (s));
1533    incr (mp->term_offset);
1534    incr (mp->file_offset);
1535    if (mp->term_offset == (unsigned) mp->max_print_line) {
1536      wterm_cr;
1537      mp->term_offset = 0;
1538    };
1539    if (mp->file_offset == (unsigned) mp->max_print_line) {
1540      wlog_cr;
1541      mp->file_offset = 0;
1542    };
1543    break;
1544  case log_only:
1545    wlog_chr (xchr (s));
1546    incr (mp->file_offset);
1547    if (mp->file_offset == (unsigned) mp->max_print_line)
1548      mp_print_ln (mp);
1549    break;
1550  case term_only:
1551    wterm_chr (xchr (s));
1552    incr (mp->term_offset);
1553    if (mp->term_offset == (unsigned) mp->max_print_line)
1554      mp_print_ln (mp);
1555    break;
1556  case no_print:
1557    break;
1558  case pseudo:
1559    if (mp->tally < mp->trick_count)
1560      mp->trick_buf[mp->tally % mp->error_line] = s;
1561    break;
1562  case new_string:
1563    append_char (s);
1564    break;
1565  default:
1566    {
1567      text_char ss[2] = {0,0};
1568      ss[0] = xchr (s);
1569      mp_fputs ((char *) ss, mp->wr_file[(mp->selector - write_file)]);
1570    }
1571  }
1572  incr (mp->tally);
1573}
1574
1575
1576@ The |print_char| procedure sends one character to the desired destination.
1577File names and string expressions might contain |ASCII_code| values that
1578can't be printed using |print_visible_char|.  These characters will be
1579printed in three- or four-symbol form like `\.{\^\^A}' or `\.{\^\^e4}'.
1580(This procedure assumes that it is safe to bypass all checks for unprintable
1581characters when |selector| is in the range |0..max_write_files-1|.
1582The user might want to write unprintable characters.
1583
1584@<Basic printing...@>=
1585void mp_print_char (MP mp, ASCII_code k) {                               /* prints a single character */
1586  if (mp->selector < pseudo || mp->selector >= write_file) {
1587    mp_print_visible_char (mp, k);
1588  } else if (@<Character |k| cannot be printed@>) {
1589    mp_print (mp, "^^");
1590    if (k < 0100) {
1591      mp_print_visible_char (mp, (ASCII_code) (k + 0100));
1592    } else if (k < 0200) {
1593      mp_print_visible_char (mp, (ASCII_code) (k - 0100));
1594    } else {
1595      int l;    /* small index or counter */
1596      l = (k / 16);
1597      mp_print_visible_char (mp, xord (l < 10 ? l + '0' : l - 10 + 'a'));
1598      l = (k % 16);
1599      mp_print_visible_char (mp, xord (l < 10 ? l + '0' : l - 10 + 'a'));
1600    }
1601  } else {
1602    mp_print_visible_char (mp, k);
1603  }
1604}
1605
1606
1607@ An entire string is output by calling |print|. Note that if we are outputting
1608the single standard ASCII character \.c, we could call |print("c")|, since
1609|"c"=99| is the number of a single-character string, as explained above. But
1610|print_char("c")| is quicker, so \MP\ goes directly to the |print_char|
1611routine when it knows that this is safe. (The present implementation
1612assumes that it is always safe to print a visible ASCII character.)
1613@^system dependencies@>
1614
1615@<Basic print...@>=
1616static void mp_do_print (MP mp, const char *ss, size_t len) {                               /* prints string |s| */
1617  if (len==0)
1618    return;
1619  if (mp->selector == new_string) {
1620    str_room (len);
1621    memcpy((mp->cur_string+mp->cur_length), ss, len);
1622    mp->cur_length += len;
1623  } else {
1624    size_t j = 0;
1625    while (j < len) {
1626      /* this was |xord((int)ss[j])| but that doesnt work */
1627      mp_print_char (mp, (ASCII_code) ss[j]);
1628      j++;
1629    }
1630  }
1631}
1632
1633
1634@
1635@<Basic print...@>=
1636void mp_print (MP mp, const char *ss) {
1637  assert (ss != NULL);
1638  mp_do_print (mp, ss, strlen (ss));
1639}
1640void mp_printf (MP mp, const char *ss, ...) {
1641  va_list ap;
1642  char pval[256];
1643  assert (ss != NULL);
1644  va_start(ap, ss);
1645  vsnprintf (pval, 256, ss, ap);
1646  mp_do_print (mp, pval, strlen (pval));
1647  va_end(ap);
1648}
1649
1650void mp_print_str (MP mp, mp_string s) {
1651  assert (s != NULL);
1652  mp_do_print (mp, (const char *) s->str, s->len);
1653}
1654
1655
1656@ Here is the very first thing that \MP\ prints: a headline that identifies
1657the version number and base name. The |term_offset| variable is temporarily
1658incorrect, but the discrepancy is not serious since we assume that the banner
1659and mem identifier together will occupy at most |max_print_line|
1660character positions.
1661
1662@<Initialize the output...@>=
1663wterm (mp->banner);
1664mp_print_ln (mp);
1665update_terminal();
1666
1667@ The procedure |print_nl| is like |print|, but it makes sure that the
1668string appears at the beginning of a new line.
1669
1670@<Basic print...@>=
1671void mp_print_nl (MP mp, const char *s) {                               /* prints string |s| at beginning of line */
1672  switch (mp->selector) {
1673  case term_and_log:
1674    if ((mp->term_offset > 0) || (mp->file_offset > 0))
1675      mp_print_ln (mp);
1676    break;
1677  case log_only:
1678    if (mp->file_offset > 0)
1679      mp_print_ln (mp);
1680    break;
1681  case term_only:
1682    if (mp->term_offset > 0)
1683      mp_print_ln (mp);
1684    break;
1685  case no_print:
1686  case pseudo:
1687  case new_string:
1688    break;
1689  }                             /* there are no other cases */
1690  mp_print (mp, s);
1691}
1692
1693
1694@ The following procedure, which prints out the decimal representation of a
1695given integer |n|, assumes that all integers fit nicely into a |int|.
1696@^system dependencies@>
1697
1698@<Basic print...@>=
1699void mp_print_int (MP mp, integer n) {                               /* prints an integer in decimal form */
1700  char s[12];
1701  mp_snprintf (s, 12, "%d", (int) n);
1702  mp_print (mp, s);
1703}
1704void mp_print_pointer (MP mp, void *n) {                               /* prints an pointer in hexadecimal form */
1705  char s[12];
1706  mp_snprintf (s, 12, "%p", n);
1707  mp_print (mp, s);
1708}
1709
1710@ @<Internal library ...@>=
1711void mp_print_int (MP mp, integer n);
1712void mp_print_pointer (MP mp, void *n);
1713
1714@ \MP\ also makes use of a trivial procedure to print two digits. The
1715following subroutine is usually called with a parameter in the range |0<=n<=99|.
1716
1717@c
1718static void mp_print_dd (MP mp, integer n) {                               /* prints two least significant digits */
1719  n = abs (n) % 100;
1720  mp_print_char (mp, xord ('0' + (n / 10)));
1721  mp_print_char (mp, xord ('0' + (n % 10)));
1722}
1723
1724
1725@ @<Declarations@>=
1726static void mp_print_dd (MP mp, integer n);
1727
1728@ Here is a procedure that asks the user to type a line of input,
1729assuming that the |selector| setting is either |term_only| or |term_and_log|.
1730The input is placed into locations |first| through |last-1| of the
1731|buffer| array, and echoed on the transcript file if appropriate.
1732
1733This procedure is never called when |interaction<mp_scroll_mode|.
1734
1735@d prompt_input(A) do {
1736    if (!mp->noninteractive) {
1737      wake_up_terminal();
1738      mp_print(mp, (A));
1739    }
1740    mp_term_input(mp);
1741  } while (0) /* prints a string and gets a line of input */
1742
1743@c
1744void mp_term_input (MP mp) {                               /* gets a line from the terminal */
1745  size_t k;     /* index into |buffer| */
1746  if (mp->noninteractive) {
1747    if (!mp_input_ln (mp, mp->term_in))
1748      longjmp (*(mp->jump_buf), 1);     /* chunk finished */
1749    mp->buffer[mp->last] = xord ('%');
1750  } else {
1751    update_terminal();            /* Now the user sees the prompt for sure */
1752    if (!mp_input_ln (mp, mp->term_in)) {
1753      mp_fatal_error (mp, "End of file on the terminal!");
1754@.End of file on the terminal@>
1755    }
1756    mp->term_offset = 0;        /* the user's line ended with \<\rm return> */
1757    decr (mp->selector);        /* prepare to echo the input */
1758    if (mp->last != mp->first) {
1759      for (k = mp->first; k < mp->last; k++) {
1760        mp_print_char (mp, mp->buffer[k]);
1761      }
1762    }
1763    mp_print_ln (mp);
1764    mp->buffer[mp->last] = xord ('%');
1765    incr (mp->selector);        /* restore previous status */
1766  }
1767}
1768
1769
1770@* Reporting errors.
1771
1772The |print_err| procedure supplies a `\.!' before the official message,
1773and makes sure that the terminal is awake if a stop is going to occur.
1774The |error| procedure supplies a `\..' after the official message, then it
1775shows the location of the error; and if |interaction=error_stop_mode|,
1776it also enters into a dialog with the user, during which time the help
1777message may be printed.
1778@^system dependencies@>
1779
1780@ The global variable |interaction| has four settings, representing increasing
1781amounts of user interaction:
1782
1783@<Exported types@>=
1784enum mp_interaction_mode {
1785  mp_unspecified_mode = 0,      /* extra value for command-line switch */
1786  mp_batch_mode,                /* omits all stops and omits terminal output */
1787  mp_nonstop_mode,              /* omits all stops */
1788  mp_scroll_mode,               /* omits error stops */
1789  mp_error_stop_mode            /* stops at every opportunity to interact */
1790};
1791
1792@ @<Option variables@>=
1793int interaction;        /* current level of interaction */
1794int noninteractive;     /* do we have a terminal? */
1795int extensions;
1796
1797@ Set it here so it can be overwritten by the commandline
1798
1799@<Allocate or initialize ...@>=
1800mp->interaction = opt->interaction;
1801if (mp->interaction == mp_unspecified_mode
1802    || mp->interaction > mp_error_stop_mode)
1803  mp->interaction = mp_error_stop_mode;
1804if (mp->interaction < mp_unspecified_mode)
1805  mp->interaction = mp_batch_mode;
1806
1807@ |print_err| is not merged in |error| because it is also used in |prompt_file_name|,
1808where |error| is not called at all.
1809
1810@<Declarations@>=
1811static void mp_print_err (MP mp, const char *A);
1812
1813@ @c
1814static void mp_print_err (MP mp, const char *A) {
1815  if (mp->interaction == mp_error_stop_mode)
1816    wake_up_terminal();
1817  if (mp->file_line_error_style && file_state && !terminal_input) {
1818    mp_print_nl (mp, "");
1819    if (long_name != NULL) {
1820      mp_print (mp, long_name);
1821    } else {
1822      mp_print (mp, mp_str (mp, name));
1823    }
1824    mp_print (mp, ":");
1825    mp_print_int (mp, line);
1826    mp_print (mp, ": ");
1827  } else {
1828    mp_print_nl (mp, "! ");
1829  }
1830  mp_print (mp, A);
1831@.!\relax@>
1832}
1833
1834
1835@ \MP\ is careful not to call |error| when the print |selector| setting
1836might be unusual. The only possible values of |selector| at the time of
1837error messages are
1838
1839\yskip\hang|no_print| (when |interaction=mp_batch_mode|
1840  and |log_file| not yet open);
1841
1842\hang|term_only| (when |interaction>mp_batch_mode| and |log_file| not yet open);
1843
1844\hang|log_only| (when |interaction=mp_batch_mode| and |log_file| is open);
1845
1846\hang|term_and_log| (when |interaction>mp_batch_mode| and |log_file| is open).
1847
1848@d initialize_print_selector() mp->selector = (mp->interaction == mp_batch_mode ? no_print : term_only);
1849
1850@ The global variable |history| records the worst level of error that
1851has been detected. It has four possible values: |spotless|, |warning_issued|,
1852|error_message_issued|, and |fatal_error_stop|.
1853
1854Another global variable, |error_count|, is increased by one when an
1855|error| occurs without an interactive dialog, and it is reset to zero at
1856the end of every statement.  If |error_count| reaches 100, \MP\ decides
1857that there is no point in continuing further.
1858
1859@<Exported types@>=
1860enum mp_history_state {
1861  mp_spotless = 0,      /* |history| value when nothing has been amiss yet */
1862  mp_warning_issued,            /* |history| value when |begin_diagnostic| has been called */
1863  mp_error_message_issued,      /* |history| value when |error| has been called */
1864  mp_fatal_error_stop,          /* |history| value when termination was premature */
1865  mp_system_error_stop          /* |history| value when termination was due to disaster */
1866};
1867
1868@ @<Glob...@>=
1869int history;    /* has the source input been clean so far? */
1870int error_count;        /* the number of scrolled errors since the last statement ended */
1871
1872@ The value of |history| is initially |fatal_error_stop|, but it will
1873be changed to |spotless| if \MP\ survives the initialization process.
1874
1875@ Since errors can be detected almost anywhere in \MP, we want to declare the
1876error procedures near the beginning of the program. But the error procedures
1877in turn use some other procedures, which need to be declared |forward|
1878before we get to |error| itself.
1879
1880It is possible for |error| to be called recursively if some error arises
1881when |get_next| is being used to delete a token, and/or if some fatal error
1882occurs while \MP\ is trying to fix a non-fatal one. But such recursion
1883@^recursion@>
1884is never more than two levels deep.
1885
1886@<Declarations@>=
1887static void mp_get_next (MP mp);
1888static void mp_term_input (MP mp);
1889static void mp_show_context (MP mp);
1890static void mp_begin_file_reading (MP mp);
1891static void mp_open_log_file (MP mp);
1892static void mp_clear_for_error_prompt (MP mp);
1893
1894@ @<Internal ...@>=
1895void mp_normalize_selector (MP mp);
1896
1897@ @<Glob...@>=
1898boolean use_err_help;   /* should the |err_help| string be shown? */
1899mp_string err_help;    /* a string set up by \&{errhelp} */
1900
1901@ @<Allocate or ...@>=
1902mp->use_err_help = false;
1903
1904@ The |jump_out| procedure just cuts across all active procedure levels and
1905goes to |end_of_MP|. This is the only nonlocal |goto| statement in the
1906whole program. It is used when there is no recovery from a particular error.
1907
1908The program uses a |jump_buf| to handle this, this is initialized at three
1909spots: the start of |mp_new|, the start of |mp_initialize|, and the start
1910of |mp_run|. Those are the only library enty points.
1911@^system dependencies@>
1912
1913@<Glob...@>=
1914jmp_buf *jump_buf;
1915
1916@ If the array of internals is still |NULL| when |jump_out| is called, a
1917crash occured during initialization, and it is not safe to run the normal
1918cleanup routine.
1919
1920@<Error hand...@>=
1921void mp_jump_out (MP mp) {
1922  if (mp->internal != NULL && mp->history < mp_system_error_stop)
1923    mp_close_files_and_terminate (mp);
1924  longjmp (*(mp->jump_buf), 1);
1925}
1926
1927@ @<Internal ...@>=
1928void mp_jump_out (MP mp);
1929
1930@
1931
1932@<Error hand...@>=
1933void mp_warn (MP mp, const char *msg) {
1934  unsigned saved_selector = mp->selector;
1935  mp_normalize_selector (mp);
1936  mp_print_nl (mp, "Warning: ");
1937  mp_print (mp, msg);
1938  mp_print_ln (mp);
1939  mp->selector = saved_selector;
1940}
1941
1942@ Here now is the general |error| routine.
1943
1944The argument |deletions_allowed| is set |false| if the |get_next|
1945routine is active when |error| is called; this ensures that |get_next|
1946will never be called recursively.
1947@^recursion@>
1948
1949Individual lines of help are recorded in the array |help_line|, which
1950contains entries in positions |0..(help_ptr-1)|. They should be printed
1951in reverse order, i.e., with |help_line[0]| appearing last.
1952
1953@c
1954void mp_error (MP mp, const char *msg, const char **hlp, boolean deletions_allowed) {
1955  ASCII_code c; /* what the user types */
1956  integer s1, s2;       /* used to save global variables when deleting tokens */
1957  mp_sym s3;    /* likewise */
1958  int i = 0;
1959  const char *help_line[6];       /* helps for the next |error| */
1960  unsigned int help_ptr;  /* the number of help lines present */
1961  const char **cnt = NULL;
1962  mp_print_err(mp, msg);
1963  if (hlp) {
1964    cnt = hlp;
1965    while (*cnt) {
1966      i++; cnt++;
1967    }
1968    cnt = hlp;
1969  }
1970  help_ptr=i;
1971  while (i>0) {
1972    help_line[--i]= *cnt++;
1973  }
1974  if (mp->history < mp_error_message_issued)
1975    mp->history = mp_error_message_issued;
1976  mp_print_char (mp, xord ('.'));
1977  mp_show_context (mp);
1978  if (mp->halt_on_error) {
1979    mp->history = mp_fatal_error_stop;
1980    mp_jump_out (mp);
1981  }
1982  if ((!mp->noninteractive) && (mp->interaction == mp_error_stop_mode)) {
1983    @<Get user's advice and |return|@>;
1984  }
1985  incr (mp->error_count);
1986  if (mp->error_count == 100) {
1987    mp_print_nl (mp, "(That makes 100 errors; please try again.)");
1988@.That makes 100 errors...@>;
1989    mp->history = mp_fatal_error_stop;
1990    mp_jump_out (mp);
1991  }
1992  @<Put help message on the transcript file@>;
1993}
1994
1995
1996@ @<Exported function ...@>=
1997extern void mp_error (MP mp, const char *msg, const char **hlp, boolean deletions_allowed);
1998extern void mp_warn (MP mp, const char *msg);
1999
2000
2001@ @<Get user's advice...@>=
2002while (true) {
2003CONTINUE:
2004  mp_clear_for_error_prompt (mp);
2005  prompt_input ("? ");
2006@.?\relax@>;
2007  if (mp->last == mp->first)
2008    return;
2009  c = mp->buffer[mp->first];
2010  if (c >= 'a')
2011    c = (ASCII_code) (c + 'A' - 'a');   /* convert to uppercase */
2012  @<Interpret code |c| and |return| if done@>;
2013}
2014
2015
2016@ It is desirable to provide an `\.E' option here that gives the user
2017an easy way to return from \MP\ to the system editor, with the offending
2018line ready to be edited. But such an extension requires some system
2019wizardry, so the present implementation simply types out the name of the
2020file that should be
2021edited and the relevant line number.
2022@^system dependencies@>
2023
2024@<Exported types@>=
2025typedef void (*mp_editor_cmd) (MP, char *, int);
2026
2027@ @<Option variables@>=
2028mp_editor_cmd run_editor;
2029
2030@ @<Allocate or initialize ...@>=
2031set_callback_option (run_editor);
2032
2033@ @<Declarations@>=
2034static void mp_run_editor (MP mp, char *fname, int fline);
2035
2036@ @c
2037void mp_run_editor (MP mp, char *fname, int fline) {
2038  char *s = xmalloc (256, 1);
2039  mp_snprintf (s, 256, "You want to edit file %s at line %d\n", fname, fline);
2040  wterm_ln (s);
2041@.You want to edit file x@>
2042}
2043
2044
2045@
2046
2047@<Interpret code |c| and |return| if done@>=
2048switch (c) {
2049case '0':
2050case '1':
2051case '2':
2052case '3':
2053case '4':
2054case '5':
2055case '6':
2056case '7':
2057case '8':
2058case '9':
2059  if (deletions_allowed) {
2060    @<Delete tokens and |continue|@>;
2061  }
2062  break;
2063case 'E':
2064  if (mp->file_ptr > 0) {
2065    mp->interaction = mp_scroll_mode;
2066    mp_close_files_and_terminate (mp);
2067    (mp->run_editor) (mp,
2068                      mp_str (mp, mp->input_stack[mp->file_ptr].name_field),
2069                      mp_true_line (mp));
2070    mp_jump_out (mp);
2071  }
2072  break;
2073case 'H':
2074  @<Print the help information and |continue|@>;
2075  /* |break;| */
2076case 'I':
2077  @<Introduce new material from the terminal and |return|@>;
2078  /* |break;| */
2079case 'Q':
2080case 'R':
2081case 'S':
2082  @<Change the interaction level and |return|@>;
2083  /* |break;| */
2084case 'X':
2085  mp->interaction = mp_scroll_mode;
2086  mp_jump_out (mp);
2087  break;
2088default:
2089  break;
2090}
2091@<Print the menu of available options@>
2092
2093
2094@ @<Print the menu...@>=
2095{
2096  mp_print (mp, "Type <return> to proceed, S to scroll future error messages,");
2097@.Type <return> to proceed...@>;
2098  mp_print_nl (mp, "R to run without stopping, Q to run quietly,");
2099  mp_print_nl (mp, "I to insert something, ");
2100  if (mp->file_ptr > 0)
2101    mp_print (mp, "E to edit your file,");
2102  if (deletions_allowed)
2103    mp_print_nl (mp,
2104                 "1 or ... or 9 to ignore the next 1 to 9 tokens of input,");
2105  mp_print_nl (mp, "H for help, X to quit.");
2106}
2107
2108
2109@ @<Change the interaction...@>=
2110{
2111  mp->error_count = 0;
2112  mp_print (mp, "OK, entering ");
2113  switch (c) {
2114  case 'Q':
2115    mp->interaction = mp_batch_mode;
2116    mp_print (mp, "batchmode");
2117    decr (mp->selector);
2118    break;
2119  case 'R':
2120    mp->interaction = mp_nonstop_mode;
2121    mp_print (mp, "nonstopmode");
2122    break;
2123  case 'S':
2124    mp->interaction = mp_scroll_mode;
2125    mp_print (mp, "scrollmode");
2126    break;
2127  }                             /* there are no other cases */
2128  mp_print (mp, "...");
2129  mp_print_ln (mp);
2130  update_terminal();
2131  return;
2132}
2133
2134
2135@ When the following code is executed, |buffer[(first+1)..(last-1)]| may
2136contain the material inserted by the user; otherwise another prompt will
2137be given. In order to understand this part of the program fully, you need
2138to be familiar with \MP's input stacks.
2139
2140@<Introduce new material...@>=
2141{
2142  mp_begin_file_reading (mp);   /* enter a new syntactic level for terminal input */
2143  if (mp->last > mp->first + 1) {
2144    loc = (halfword) (mp->first + 1);
2145    mp->buffer[mp->first] = xord (' ');
2146  } else {
2147    prompt_input ("insert>");
2148    loc = (halfword) mp->first;
2149@.insert>@>
2150  }
2151  mp->first = mp->last + 1;
2152  mp->cur_input.limit_field = (halfword) mp->last;
2153  return;
2154}
2155
2156
2157@ We allow deletion of up to 99 tokens at a time.
2158
2159@<Delete tokens...@>=
2160{
2161  s1 = cur_cmd();
2162  s2 = cur_mod();
2163  s3 = cur_sym();
2164  mp->OK_to_interrupt = false;
2165  if ((mp->last > mp->first + 1) && (mp->buffer[mp->first + 1] >= '0')
2166      && (mp->buffer[mp->first + 1] <= '9'))
2167    c = xord (c * 10 + mp->buffer[mp->first + 1] - '0' * 11);
2168  else
2169    c = (ASCII_code) (c - '0');
2170  while (c > 0) {
2171    mp_get_next (mp);           /* one-level recursive call of |error| is possible */
2172    @<Decrease the string reference count, if the current token is a string@>;
2173    c--;
2174  };
2175  set_cur_cmd (s1);
2176  set_cur_mod (s2);
2177  set_cur_sym (s3);
2178  mp->OK_to_interrupt = true;
2179  help_ptr = 2;
2180  help_line[1] = "I have just deleted some text, as you asked.";
2181  help_line[0] = "You can now delete more, or insert, or whatever.";
2182  mp_show_context (mp);
2183  goto CONTINUE;
2184}
2185
2186
2187@ Some wriggling with |help_line| is done here to avoid giving no
2188information whatsoever, or presenting the same information twice
2189in a row.
2190
2191@<Print the help info...@>=
2192{
2193  if (mp->use_err_help) {
2194    @<Print the string |err_help|, possibly on several lines@>;
2195    mp->use_err_help = false;
2196  } else {
2197    if (help_ptr == 0) {
2198      help_ptr=2;
2199      help_line[1] = "Sorry, I don't know how to help in this situation.";
2200      help_line[0] = "Maybe you should try asking a human?";
2201    }
2202    do {
2203      decr (help_ptr);
2204      mp_print (mp, help_line[help_ptr]);
2205      mp_print_ln (mp);
2206    } while (help_ptr != 0);
2207  };
2208  help_ptr=4;
2209  help_line[3] = "Sorry, I already gave what help I could...";
2210  help_line[2] = "Maybe you should try asking a human?";
2211  help_line[1] = "An error might have occurred before I noticed any problems.";
2212  help_line[0] = "``If all else fails, read the instructions.''";
2213  goto CONTINUE;
2214}
2215
2216
2217@ @<Print the string |err_help|, possibly on several lines@>=
2218{
2219  size_t j = 0;
2220  while (j < mp->err_help->len) {
2221    if (*(mp->err_help->str + j) != '%')
2222      mp_print (mp, (const char *) (mp->err_help->str + j));
2223    else if (j + 1 == mp->err_help->len)
2224      mp_print_ln (mp);
2225    else if (*(mp->err_help->str + j) != '%')
2226      mp_print_ln (mp);
2227    else {
2228      j++;
2229      mp_print_char (mp, xord ('%'));
2230    };
2231    j++;
2232  }
2233}
2234
2235
2236@ @<Put help message on the transcript file@>=
2237if (mp->interaction > mp_batch_mode)
2238  decr (mp->selector);          /* avoid terminal output */
2239if (mp->use_err_help) {
2240  mp_print_nl (mp, "");
2241  @<Print the string |err_help|, possibly on several lines@>;
2242} else {
2243  while (help_ptr > 0) {
2244    decr (help_ptr);
2245    mp_print_nl (mp, help_line[help_ptr]);
2246  };
2247  mp_print_ln (mp);
2248  if (mp->interaction > mp_batch_mode)
2249    incr (mp->selector);        /* re-enable terminal output */
2250  mp_print_ln (mp);
2251}
2252
2253
2254@ In anomalous cases, the print selector might be in an unknown state;
2255the following subroutine is called to fix things just enough to keep
2256running a bit longer.
2257
2258@c
2259void mp_normalize_selector (MP mp) {
2260  if (mp->log_opened)
2261    mp->selector = term_and_log;
2262  else
2263    mp->selector = term_only;
2264  if (mp->job_name == NULL)
2265    mp_open_log_file (mp);
2266  if (mp->interaction == mp_batch_mode)
2267    decr (mp->selector);
2268}
2269
2270
2271@ The following procedure prints \MP's last words before dying.
2272
2273@<Error hand...@>=
2274void mp_fatal_error (MP mp, const char *s) {                               /* prints |s|, and that's it */
2275  const char *hlp[] = {s, NULL} ;
2276  mp_normalize_selector (mp);
2277  if ( mp->interaction==mp_error_stop_mode )
2278    mp->interaction=mp_scroll_mode; /* no more interaction */
2279  if ( mp->log_opened )
2280    mp_error(mp, "Emergency stop", hlp, true);
2281  mp->history=mp_fatal_error_stop;
2282  mp_jump_out(mp); /* irrecoverable error */
2283@.Emergency stop@>
2284}
2285
2286
2287@ @<Exported function ...@>=
2288extern void mp_fatal_error (MP mp, const char *s);
2289
2290
2291@ @<Internal library declarations@>=
2292void mp_overflow (MP mp, const char *s, integer n);
2293
2294
2295@ @<Error hand...@>=
2296void mp_overflow (MP mp, const char *s, integer n) {                               /* stop due to finiteness */
2297  char msg[256];
2298  const char *hlp[] = {
2299         "If you really absolutely need more capacity,",
2300         "you can ask a wizard to enlarge me.",
2301         NULL };
2302  mp_normalize_selector (mp);
2303  mp_snprintf (msg, 256, "MetaPost capacity exceeded, sorry [%s=%d]", s, (int) n);
2304@.MetaPost capacity exceeded ...@>;
2305  if ( mp->interaction==mp_error_stop_mode )
2306    mp->interaction=mp_scroll_mode; /* no more interaction */
2307  if ( mp->log_opened )
2308    mp_error(mp, msg, hlp, true);
2309  mp->history=mp_fatal_error_stop;
2310  mp_jump_out(mp); /* irrecoverable error */
2311}
2312
2313
2314@ The program might sometime run completely amok, at which point there is
2315no choice but to stop. If no previous error has been detected, that's bad
2316news; a message is printed that is really intended for the \MP\
2317maintenance person instead of the user (unless the user has been
2318particularly diabolical).  The index entries for `this can't happen' may
2319help to pinpoint the problem.
2320@^dry rot@>
2321
2322@<Internal library ...@>=
2323void mp_confusion (MP mp, const char *s);
2324
2325@ Consistency check violated; |s| tells where.
2326@<Error hand...@>=
2327void mp_confusion (MP mp, const char *s) {
2328  char msg[256];
2329  const char *hlp[] = {
2330           "One of your faux pas seems to have wounded me deeply...",
2331           "in fact, I'm barely conscious. Please fix it and try again.",
2332           NULL };
2333  mp_normalize_selector (mp);
2334  if (mp->history < mp_error_message_issued) {
2335    mp_snprintf (msg, 256, "This can't happen (%s)", s);
2336@.This can't happen@>;
2337    hlp[0] = "I'm broken. Please show this to someone who can fix can fix";
2338    hlp[1] = NULL;
2339  } else {
2340    mp_snprintf (msg, 256, "I can\'t go on meeting you like this");
2341@.I can't go on...@>;
2342  }
2343  if ( mp->interaction==mp_error_stop_mode )
2344    mp->interaction=mp_scroll_mode; /* no more interaction */
2345  if ( mp->log_opened )
2346    mp_error(mp, msg, hlp, true);
2347  mp->history=mp_fatal_error_stop;
2348  mp_jump_out(mp); /* irrecoverable error */
2349}
2350
2351
2352@ Users occasionally want to interrupt \MP\ while it's running.
2353If the runtime system allows this, one can implement
2354a routine that sets the global variable |interrupt| to some nonzero value
2355when such an interrupt is signaled. Otherwise there is probably at least
2356a way to make |interrupt| nonzero using the C debugger.
2357@^system dependencies@>
2358@^debugging@>
2359
2360@d check_interrupt { if ( mp->interrupt!=0 )
2361   mp_pause_for_instructions(mp); }
2362
2363@<Global...@>=
2364integer interrupt;      /* should \MP\ pause for instructions? */
2365boolean OK_to_interrupt;        /* should interrupts be observed? */
2366integer run_state;      /* are we processing input ? */
2367boolean finished;       /* set true by |close_files_and_terminate| */
2368boolean reading_preload;
2369
2370@ @<Allocate or ...@>=
2371mp->OK_to_interrupt = true;
2372mp->finished = false;
2373
2374@ When an interrupt has been detected, the program goes into its
2375highest interaction level and lets the user have the full flexibility of
2376the |error| routine.  \MP\ checks for interrupts only at times when it is
2377safe to do this.
2378
2379@c
2380static void mp_pause_for_instructions (MP mp) {
2381  const char *hlp[] = { "You rang?",
2382	           "Try to insert some instructions for me (e.g.,`I show x'),",
2383        	   "unless you just want to quit by typing `X'.",
2384	           NULL } ;
2385  if (mp->OK_to_interrupt) {
2386    mp->interaction = mp_error_stop_mode;
2387    if ((mp->selector == log_only) || (mp->selector == no_print))
2388      incr (mp->selector);
2389@.Interruption@>;
2390    mp_error (mp, "Interruption", hlp, false);
2391    mp->interrupt = 0;
2392  }
2393}
2394
2395
2396@* Arithmetic with scaled numbers.
2397The principal computations performed by \MP\ are done entirely in terms of
2398integers less than $2^{31}$ in magnitude; thus, the arithmetic specified in this
2399program can be carried out in exactly the same way on a wide variety of
2400computers, including some small ones.
2401@^small computers@>
2402
2403But C does not rigidly define the |/| operation in the case of negative
2404dividends; for example, the result of |(-2*n-1) / 2| is |-(n+1)| on some
2405computers and |-n| on others (is this true ?).  There are two principal
2406types of arithmetic: ``translation-preserving,'' in which the identity
2407|(a+q*b)/b=(a/b)+q| is valid; and ``negation-preserving,'' in which
2408|(-a)/b=-(a/b)|. This leads to two \MP s, which can produce
2409different results, although the differences should be negligible when the
2410language is being used properly.  The \TeX\ processor has been defined
2411carefully so that both varieties of arithmetic will produce identical
2412output, but it would be too inefficient to constrain \MP\ in a similar way.
2413
2414@d inf_t  ((math_data *)mp->math)->inf_t
2415
2416@ A single computation might use several subroutine calls, and it is
2417desirable to avoid producing multiple error messages in case of arithmetic
2418overflow. So the routines below set the global variable |arith_error| to |true|
2419instead of reporting errors directly to the user.
2420@^overflow in arithmetic@>
2421
2422@<Glob...@>=
2423boolean arith_error;    /* has arithmetic overflow occurred recently? */
2424
2425@ @<Allocate or ...@>=
2426mp->arith_error = false;
2427
2428@ At crucial points the program will say |check_arith|, to test if
2429an arithmetic error has been detected.
2430
2431@d check_arith() do {
2432  if ( mp->arith_error )
2433    mp_clear_arith(mp);
2434} while (0)
2435
2436@c
2437static void mp_clear_arith (MP mp) {
2438  const char *hlp[] = {
2439         "Uh, oh. A little while ago one of the quantities that I was",
2440         "computing got too large, so I'm afraid your answers will be",
2441         "somewhat askew. You'll probably have to adopt different",
2442         "tactics next time. But I shall try to carry on anyway.",
2443         NULL };
2444  mp_error (mp, "Arithmetic overflow", hlp, true);
2445@.Arithmetic overflow@>;
2446  mp->arith_error = false;
2447}
2448
2449
2450@ The definitions of these are set up by the math initialization.
2451
2452@d arc_tol_k ((math_data *)mp->math)->arc_tol_k
2453@d coef_bound_k ((math_data *)mp->math)->coef_bound_k
2454@d coef_bound_minus_1 ((math_data *)mp->math)->coef_bound_minus_1
2455@d sqrt_8_e_k ((math_data *)mp->math)->sqrt_8_e_k
2456@d twelve_ln_2_k ((math_data *)mp->math)->twelve_ln_2_k
2457@d twelvebits_3 ((math_data *)mp->math)->twelvebits_3
2458@d one_k  ((math_data *)mp->math)->one_k
2459@d epsilon_t  ((math_data *)mp->math)->epsilon_t
2460@d unity_t  ((math_data *)mp->math)->unity_t
2461@d zero_t  ((math_data *)mp->math)->zero_t
2462@d two_t ((math_data *)mp->math)->two_t
2463@d three_t  ((math_data *)mp->math)->three_t
2464@d half_unit_t ((math_data *)mp->math)->half_unit_t
2465@d three_quarter_unit_t ((math_data *)mp->math)->three_quarter_unit_t
2466@d twentysixbits_sqrt2_t ((math_data *)mp->math)->twentysixbits_sqrt2_t
2467@d twentyeightbits_d_t ((math_data *)mp->math)->twentyeightbits_d_t
2468@d twentysevenbits_sqrt2_d_t ((math_data *)mp->math)->twentysevenbits_sqrt2_d_t
2469@d warning_limit_t ((math_data *)mp->math)->warning_limit_t
2470@d precision_default  ((math_data *)mp->math)->precision_default
2471@d precision_max  ((math_data *)mp->math)->precision_max
2472@d precision_min  ((math_data *)mp->math)->precision_min
2473
2474@ In fact, the two sorts of scaling discussed above aren't quite
2475sufficient; \MP\ has yet another, used internally to keep track of angles.
2476
2477@ We often want to print two scaled quantities in parentheses,
2478separated by a comma.
2479
2480@<Basic printing...@>=
2481void mp_print_two (MP mp, mp_number x, mp_number y) {                               /* prints `|(x,y)|' */
2482  mp_print_char (mp, xord ('('));
2483  print_number (x);
2484  mp_print_char (mp, xord (','));
2485  print_number (y);
2486  mp_print_char (mp, xord (')'));
2487}
2488
2489
2490@
2491@d fraction_one_t ((math_data *)mp->math)->fraction_one_t
2492@d fraction_half_t ((math_data *)mp->math)->fraction_half_t
2493@d fraction_three_t ((math_data *)mp->math)->fraction_three_t
2494@d fraction_four_t ((math_data *)mp->math)->fraction_four_t
2495
2496@d one_eighty_deg_t ((math_data *)mp->math)->one_eighty_deg_t
2497@d three_sixty_deg_t ((math_data *)mp->math)->three_sixty_deg_t
2498
2499@ @<Local variables for initialization@>=
2500integer k;  /* all-purpose loop index */
2501
2502@ And now let's complete our collection of numeric utility routines
2503by considering random number generation.
2504\MP\ generates pseudo-random numbers with the additive scheme recommended
2505in Section 3.6 of {\sl The Art of Computer Programming}; however, the
2506results are random fractions between 0 and |fraction_one-1|, inclusive.
2507
2508There's an auxiliary array |randoms| that contains 55 pseudo-random
2509fractions. Using the recurrence $x_n=(x_{n-55}-x_{n-31})\bmod 2^{28}$,
2510we generate batches of 55 new $x_n$'s at a time by calling |new_randoms|.
2511The global variable |j_random| tells which element has most recently
2512been consumed.
2513The global variable |random_seed| was introduced in version 0.9,
2514for the sole reason of stressing the fact that the initial value of the
2515random seed is system-dependant. The initialization code below will initialize
2516this variable to |(internal[mp_time] div unity)+internal[mp_day]|, but this
2517is not good enough on modern fast machines that are capable of running
2518multiple MetaPost processes within the same second.
2519@^system dependencies@>
2520
2521@<Glob...@>=
2522mp_number randoms[55];   /* the last 55 random values generated */
2523int j_random;   /* the number of unused |randoms| */
2524
2525@ @<Option variables@>=
2526int random_seed;        /* the default random seed */
2527
2528@ @<Allocate or initialize ...@>=
2529mp->random_seed = opt->random_seed;
2530{
2531  int i;
2532  for (i=0;i<55;i++) {
2533     new_fraction (mp->randoms[i]);
2534  }
2535}
2536
2537@ @<Dealloc...@>=
2538{
2539  int i;
2540  for (i=0;i<55;i++) {
2541     free_number (mp->randoms[i]);
2542  }
2543}
2544
2545@ @<Internal library ...@>=
2546void mp_new_randoms (MP mp);
2547
2548@ @c
2549void mp_new_randoms (MP mp) {
2550  int k;        /* index into |randoms| */
2551  mp_number x;   /* accumulator */
2552  new_number (x);
2553  for (k = 0; k <= 23; k++) {
2554    set_number_from_substraction(x, mp->randoms[k], mp->randoms[k + 31]);
2555    if (number_negative(x))
2556      number_add (x, fraction_one_t);
2557    number_clone (mp->randoms[k], x);
2558  }
2559  for (k = 24; k <= 54; k++) {
2560    set_number_from_substraction(x, mp->randoms[k], mp->randoms[k - 24]);
2561    if (number_negative(x))
2562      number_add (x, fraction_one_t);
2563    number_clone (mp->randoms[k], x);
2564  }
2565  free_number (x);
2566  mp->j_random = 54;
2567}
2568
2569@ To consume a random fraction, the program below will say `|next_random|'.
2570
2571@c
2572static void mp_next_random (MP mp, mp_number *ret) {
2573  if ( mp->j_random==0 )
2574    mp_new_randoms(mp);
2575  else
2576    decr(mp->j_random);
2577  number_clone (*ret, mp->randoms[mp->j_random]);
2578}
2579
2580
2581@ To produce a uniform random number in the range |0<=u<x| or |0>=u>x|
2582or |0=u=x|, given a |scaled| value~|x|, we proceed as shown here.
2583
2584Note that the call of |take_fraction| will produce the values 0 and~|x|
2585with about half the probability that it will produce any other particular
2586values between 0 and~|x|, because it rounds its answers.
2587
2588@c
2589static void mp_unif_rand (MP mp, mp_number *ret, mp_number x_orig) {
2590  mp_number y;     /* trial value */
2591  mp_number x, abs_x;
2592  mp_number u;
2593  new_fraction (y);
2594  new_number (x);
2595  new_number (abs_x);
2596  new_number (u);
2597  number_clone (x, x_orig);
2598  number_clone (abs_x, x);
2599  number_abs (abs_x);
2600  mp_next_random(mp, &u);
2601  take_fraction (y, abs_x, u);
2602  free_number (u);
2603  if (number_equal(y, abs_x)) {
2604    set_number_to_zero(*ret);
2605  } else if (number_positive(x)) {
2606    number_clone (*ret, y);
2607  } else {
2608    number_clone (*ret, y);
2609    number_negate (*ret);
2610  }
2611  free_number (abs_x);
2612  free_number (x);
2613  free_number (y);
2614}
2615
2616
2617@ Finally, a normal deviate with mean zero and unit standard deviation
2618can readily be obtained with the ratio method (Algorithm 3.4.1R in
2619{\sl The Art of Computer Programming\/}). This is the original one,
2620that stays as reference:
2621Now each number system has its own implementation,
2622true to the original as much as possibile.
2623
2624
2625@c
2626/*  Unused.
2627static void mp_norm_rand (MP mp, mp_number *ret) {
2628  mp_number ab_vs_cd;
2629  mp_number abs_x;
2630  mp_number u;
2631  mp_number r;
2632  mp_number la, xa;
2633  new_number (ab_vs_cd);
2634  new_number (la);
2635  new_number (xa);
2636  new_number (abs_x);
2637  new_number (u);
2638  new_number (r);
2639  do {
2640    do {
2641      mp_number v;
2642      new_number (v);
2643      mp_next_random(mp, &v);
2644      number_substract (v, fraction_half_t);
2645      take_fraction (xa, sqrt_8_e_k, v);
2646      free_number (v);
2647      mp_next_random(mp, &u);
2648      number_clone (abs_x, xa);
2649      number_abs (abs_x);
2650    } while (number_greaterequal (abs_x, u));
2651    make_fraction (r, xa, u);
2652    number_clone (xa, r);
2653    m_log (la, u);
2654    set_number_from_substraction(la, twelve_ln_2_k, la);
2655    ab_vs_cd (ab_vs_cd, one_k, la, xa, xa);
2656  } while (number_negative(ab_vs_cd));
2657  number_clone (*ret, xa);
2658  free_number (ab_vs_cd);
2659  free_number (r);
2660  free_number (abs_x);
2661  free_number (la);
2662  free_number (xa);
2663  free_number (u);
2664}
2665*/
2666
2667
2668@* Packed data.
2669
2670@d max_quarterword 0x3FFF /* largest allowable value in a |quarterword| */
2671@d max_halfword 0xFFFFFFF /* largest allowable value in a |halfword| */
2672
2673@ The macros |qi| and |qo| are used for input to and output
2674from quarterwords. These are legacy macros.
2675@^system dependencies@>
2676
2677@d qo(A) (A) /* to read eight bits from a quarterword */
2678@d qi(A) (quarterword)(A) /* to store eight bits in a quarterword */
2679
2680@ The reader should study the following definitions closely:
2681@^system dependencies@>
2682
2683@<Types...@>=
2684typedef struct mp_value_node_data *mp_value_node;
2685typedef struct mp_node_data *mp_node;
2686typedef struct mp_symbol_entry *mp_sym;
2687typedef short quarterword;      /* 1/4 of a word */
2688typedef int halfword;   /* 1/2 of a word */
2689typedef struct {
2690  integer scale; /* only for |indep_scale|, used together with |serial| */
2691  integer serial; /* only for |indep_value|, used together with |scale| */
2692} mp_independent_data;
2693typedef struct {
2694  mp_independent_data indep;
2695  mp_number n;
2696  mp_string str;
2697  mp_sym sym;
2698  mp_node node;
2699  mp_knot p;
2700} mp_value_data;
2701typedef struct {
2702  mp_variable_type type;
2703  mp_value_data data;
2704} mp_value;
2705typedef struct {
2706  quarterword b0, b1, b2, b3;
2707} four_quarters;
2708typedef union {
2709  integer sc;
2710  four_quarters qqqq;
2711} font_data;
2712
2713
2714@ The global variable |math_mode| has four settings, representing the
2715math value type that will be used in this run.
2716
2717the typedef for |mp_number| is here because it has to come very early.
2718
2719@<Exported types@>=
2720typedef enum {
2721  mp_math_scaled_mode = 0,
2722  mp_math_double_mode = 1,
2723  mp_math_binary_mode = 2,
2724  mp_math_decimal_mode = 3
2725} mp_math_mode;
2726
2727@ @<Option variables@>=
2728int math_mode;               /* math mode */
2729
2730@ @<Allocate or initialize ...@>=
2731mp->math_mode = opt->math_mode;
2732
2733@
2734@d xfree(A) do { mp_xfree(A); A=NULL; } while (0)
2735@d xrealloc(P,A,B) mp_xrealloc(mp,P,(size_t)A,B)
2736@d xmalloc(A,B)  mp_xmalloc(mp,(size_t)A,B)
2737@d xstrdup(A)  mp_xstrdup(mp,A)
2738@d XREALLOC(a,b,c) a = xrealloc(a,(b+1),sizeof(c));
2739
2740@<Declare helpers@>=
2741extern void mp_xfree (void *x);
2742extern void *mp_xrealloc (MP mp, void *p, size_t nmem, size_t size);
2743extern void *mp_xmalloc (MP mp, size_t nmem, size_t size);
2744extern void mp_do_snprintf (char *str, int size, const char *fmt, ...);
2745extern void *do_alloc_node(MP mp, size_t size);
2746
2747@ This is an attempt to spend less time in |malloc()|:
2748
2749@d max_num_token_nodes 1000
2750@d max_num_pair_nodes 1000
2751@d max_num_knot_nodes 1000
2752@d max_num_value_nodes 1000
2753@d max_num_symbolic_nodes 1000
2754
2755@<Global ...@>=
2756mp_node token_nodes;
2757int num_token_nodes;
2758mp_node pair_nodes;
2759int num_pair_nodes;
2760mp_knot knot_nodes;
2761int num_knot_nodes;
2762mp_node value_nodes;
2763int num_value_nodes;
2764mp_node symbolic_nodes;
2765int num_symbolic_nodes;
2766
2767@ @<Allocate or initialize ...@>=
2768mp->token_nodes = NULL;
2769mp->num_token_nodes = 0;
2770mp->pair_nodes = NULL;
2771mp->num_pair_nodes = 0;
2772mp->knot_nodes = NULL;
2773mp->num_knot_nodes = 0;
2774mp->value_nodes = NULL;
2775mp->num_value_nodes = 0;
2776mp->symbolic_nodes = NULL;
2777mp->num_symbolic_nodes = 0;
2778
2779@ @<Dealloc ...@>=
2780while (mp->value_nodes) {
2781      mp_node p = mp->value_nodes;
2782      mp->value_nodes = p->link;
2783      mp_free_node(mp,p,value_node_size);
2784}
2785while (mp->symbolic_nodes) {
2786      mp_node p = mp->symbolic_nodes;
2787      mp->symbolic_nodes = p->link;
2788      mp_free_node(mp,p,symbolic_node_size);
2789}
2790while (mp->pair_nodes) {
2791      mp_node p = mp->pair_nodes;
2792      mp->pair_nodes = p->link;
2793      mp_free_node(mp,p,pair_node_size);
2794}
2795while (mp->token_nodes) {
2796      mp_node p = mp->token_nodes;
2797      mp->token_nodes = p->link;
2798      mp_free_node(mp,p,token_node_size);
2799}
2800while (mp->knot_nodes) {
2801      mp_knot p = mp->knot_nodes;
2802      mp->knot_nodes = p->next;
2803      mp_free_knot(mp,p);
2804}
2805
2806@ This is a nicer way of allocating nodes.
2807
2808@d malloc_node(A) do_alloc_node(mp,(A))
2809
2810@
2811@c
2812void *do_alloc_node (MP mp, size_t size) {
2813    void *p;
2814    p = xmalloc(1,size);
2815    add_var_used (size);
2816    ((mp_node)p)->link = NULL;
2817    ((mp_node)p)->has_number = 0;
2818    return p;
2819}
2820
2821
2822@ The |max_size_test| guards against overflow, on the assumption that
2823|size_t| is at least 31bits wide.
2824
2825@d max_size_test 0x7FFFFFFF
2826
2827@c
2828void mp_xfree (void *x) {
2829  if (x != NULL)
2830    free (x);
2831}
2832void *mp_xrealloc (MP mp, void *p, size_t nmem, size_t size) {
2833  void *w;
2834  if ((max_size_test / size) < nmem) {
2835    mp_fputs ("Memory size overflow!\n", mp->err_out);
2836    mp->history = mp_fatal_error_stop;
2837    mp_jump_out (mp);
2838  }
2839  w = realloc (p, (nmem * size));
2840  if (w == NULL) {
2841    mp_fputs ("Out of memory!\n", mp->err_out);
2842    mp->history = mp_system_error_stop;
2843    mp_jump_out (mp);
2844  }
2845  return w;
2846}
2847void *mp_xmalloc (MP mp, size_t nmem, size_t size) {
2848  void *w;
2849#if DEBUG
2850  if ((max_size_test / size) < nmem) {
2851    mp_fputs ("Memory size overflow!\n", mp->err_out);
2852    mp->history = mp_fatal_error_stop;
2853    mp_jump_out (mp);
2854  }
2855#endif
2856  w = malloc (nmem * size);
2857  if (w == NULL) {
2858    mp_fputs ("Out of memory!\n", mp->err_out);
2859    mp->history = mp_system_error_stop;
2860    mp_jump_out (mp);
2861  }
2862  return w;
2863}
2864
2865@ @<Internal library declarations@>=
2866#  define mp_snprintf (void)snprintf
2867
2868@* Dynamic memory allocation.
2869
2870The \MP\ system does nearly all of its own memory allocation, so that it
2871can readily be transported into environments that do not have automatic
2872facilities for strings, garbage collection, etc., and so that it can be in
2873control of what error messages the user receives.
2874
2875@d MP_VOID (mp_node)(1) /* |NULL+1|, a |NULL| pointer different from |NULL| */
2876
2877@d mp_link(A)      (A)->link /* the |link| field of a node */
2878@d set_mp_link(A,B) do {
2879   mp_node d = (B);
2880   /* |printf("set link    of %p to %p on line %d\n", (A), d, __LINE__);| */
2881   mp_link((A)) = d;
2882 } while (0)
2883@d mp_type(A)      (A)->type /* identifies what kind of value this is */
2884@d mp_name_type(A) (A)->name_type /* a clue to the name of this value */
2885
2886@ @<MPlib internal header stuff@>=
2887#define NODE_BODY                       \
2888  mp_variable_type type;                \
2889  mp_name_type_type name_type;          \
2890  unsigned short has_number;		\
2891  struct mp_node_data *link
2892typedef struct mp_node_data {
2893  NODE_BODY;
2894  mp_value_data data;
2895} mp_node_data;
2896typedef struct mp_node_data *mp_symbolic_node;
2897
2898@ Users who wish to study the memory requirements of particular applications can
2899can use the special features that keep track of current and maximum memory usage.
2900\MP\ will report these statistics when |mp_tracing_stats| is positive.
2901
2902@d add_var_used(a) do {
2903   mp->var_used+=(a);
2904   if (mp->var_used>mp->var_used_max) mp->var_used_max=mp->var_used;
2905} while (0)
2906
2907@<Glob...@>=
2908size_t var_used;        /* how much memory is in use */
2909size_t var_used_max;    /* how much memory was in use max */
2910
2911@ These redirect to function to aid in debugging.
2912
2913@c
2914#if DEBUG
2915#define mp_sym_info(A)       get_mp_sym_info(mp,(A))
2916#define set_mp_sym_info(A,B) do_set_mp_sym_info(mp,(A),(B))
2917#define mp_sym_sym(A)        get_mp_sym_sym(mp,(A))
2918#define set_mp_sym_sym(A,B)  do_set_mp_sym_sym(mp,(A),(mp_sym)(B))
2919static void do_set_mp_sym_info (MP mp, mp_node p, halfword v) {
2920  FUNCTION_TRACE3 ("do_set_mp_sym_info(%p,%d)\n", p, v);
2921  assert (p->type == mp_symbol_node);
2922  set_indep_value(p, v);
2923}
2924static halfword get_mp_sym_info (MP mp, mp_node p) {
2925  FUNCTION_TRACE3 ("%d = get_mp_sym_info(%p)\n", indep_value (p), p);
2926  assert (p->type == mp_symbol_node);
2927  return indep_value(p);
2928}
2929static void do_set_mp_sym_sym (MP mp, mp_node p, mp_sym v) {
2930  mp_symbolic_node pp = (mp_symbolic_node) p;
2931  FUNCTION_TRACE3 ("do_set_mp_sym_sym(%p,%p)\n", pp, v);
2932  assert (pp->type == mp_symbol_node);
2933  pp->data.sym = v;
2934}
2935static mp_sym get_mp_sym_sym (MP mp, mp_node p) {
2936  mp_symbolic_node pp = (mp_symbolic_node) p;
2937  FUNCTION_TRACE3 ("%p = get_mp_sym_sym(%p)\n", pp->data.sym, pp);
2938  assert (pp->type == mp_symbol_node);
2939  return pp->data.sym;
2940}
2941#else
2942#define mp_sym_info(A)        indep_value(A)
2943#define set_mp_sym_info(A,B)  set_indep_value(A, (B))
2944#define mp_sym_sym(A)        (A)->data.sym
2945#define set_mp_sym_sym(A,B)  (A)->data.sym = (mp_sym)(B)
2946#endif
2947
2948@ @<Declarations@>=
2949#if DEBUG
2950static void do_set_mp_sym_info (MP mp, mp_node A, halfword B);
2951static halfword get_mp_sym_info (MP mp, mp_node p);
2952static void do_set_mp_sym_sym (MP mp, mp_node A, mp_sym B);
2953static mp_sym get_mp_sym_sym (MP mp, mp_node p);
2954#endif
2955
2956@ The function |get_symbolic_node| returns a pointer to a new symbolic node whose
2957|link| field is null.
2958@^inner loop@>
2959
2960@d symbolic_node_size sizeof(mp_node_data)
2961@c
2962static mp_node mp_get_symbolic_node (MP mp) {
2963  mp_symbolic_node p;
2964  if (mp->symbolic_nodes) {
2965    p = (mp_symbolic_node)mp->symbolic_nodes;
2966    mp->symbolic_nodes = p->link;
2967    mp->num_symbolic_nodes--;
2968    p->link = NULL;
2969  } else {
2970    p = malloc_node (symbolic_node_size);
2971    new_number(p->data.n);
2972    p->has_number = 1;
2973  }
2974  p->type = mp_symbol_node;
2975  p->name_type = mp_normal_sym;
2976  FUNCTION_TRACE2 ("%p = mp_get_symbolic_node()\n", p);
2977  return (mp_node) p;
2978}
2979
2980
2981@ Conversely, when some node |p| of size |s| is no longer needed,
2982the operation |free_node(p,s)| will make its words available, by inserting
2983|p| as a new empty node just before where |rover| now points.
2984
2985A symbolic node is recycled by calling |free_symbolic_node|.
2986
2987@c
2988void mp_free_node (MP mp, mp_node p, size_t siz) {  /* node liberation */
2989  FUNCTION_TRACE3 ("mp_free_node(%p,%d)\n", p, (int)siz);
2990  if (!p) return;
2991  mp->var_used -= siz;
2992  if (mp->math_mode > mp_math_double_mode) {
2993    if (p->has_number >= 1 && is_number(((mp_symbolic_node)p)->data.n)) {
2994      free_number(((mp_symbolic_node)p)->data.n);
2995    }
2996    if (p->has_number == 2 && is_number(((mp_value_node)p)->subscript_)) {
2997      free_number(((mp_value_node)p)->subscript_);
2998    }
2999    /* There was a quite large |switch| here first, but the |mp_dash_node|
3000       case was the only one that did anything ... */
3001    if (mp_type (p) == mp_dash_node_type) {
3002      free_number(((mp_dash_node)p)->start_x);
3003      free_number(((mp_dash_node)p)->stop_x);
3004      free_number(((mp_dash_node)p)->dash_y);
3005    }
3006  }
3007  xfree (p);
3008}
3009void mp_free_symbolic_node (MP mp, mp_node p) {  /* node liberation */
3010  FUNCTION_TRACE2 ("mp_free_symbolic_node(%p)\n", p);
3011  if (!p) return;
3012  if (mp->num_symbolic_nodes < max_num_symbolic_nodes) {
3013    p->link = mp->symbolic_nodes;
3014    mp->symbolic_nodes = p;
3015    mp->num_symbolic_nodes++;
3016    return;
3017  }
3018  mp->var_used -= symbolic_node_size;
3019  xfree (p);
3020}
3021void mp_free_value_node (MP mp, mp_node p) {  /* node liberation */
3022  FUNCTION_TRACE2 ("mp_free_value_node(%p)\n", p);
3023  if (!p) return;
3024  if (mp->num_value_nodes < max_num_value_nodes) {
3025    p->link = mp->value_nodes;
3026    mp->value_nodes = p;
3027    mp->num_value_nodes++;
3028    return;
3029  }
3030  mp->var_used -= value_node_size;
3031  assert(p->has_number == 2);
3032  if (mp->math_mode > mp_math_double_mode) {
3033    free_number(((mp_value_node)p)->data.n);
3034    free_number(((mp_value_node)p)->subscript_);
3035  }
3036  xfree (p);
3037}
3038
3039
3040@ @<Internal library declarations@>=
3041void mp_free_node (MP mp, mp_node p, size_t siz);
3042void mp_free_symbolic_node (MP mp, mp_node p);
3043void mp_free_value_node (MP mp, mp_node p);
3044
3045@* Memory layout.
3046Some nodes are created statically, since static allocation is
3047more efficient than dynamic allocation when we can get away with it.
3048
3049@<Glob...@>=
3050mp_dash_node null_dash;
3051mp_value_node dep_head;
3052mp_node inf_val;
3053mp_node zero_val;
3054mp_node temp_val;
3055mp_node end_attr;
3056mp_node bad_vardef;
3057mp_node temp_head;
3058mp_node hold_head;
3059mp_node spec_head;
3060
3061@ The following code gets the memory off to a good start.
3062
3063@<Initialize table entries@>=
3064mp->spec_head = mp_get_symbolic_node (mp);
3065mp->last_pending = mp->spec_head;
3066mp->temp_head = mp_get_symbolic_node (mp);
3067mp->hold_head = mp_get_symbolic_node (mp);
3068
3069@ @<Free table entries@>=
3070mp_free_symbolic_node (mp, mp->spec_head);
3071mp_free_symbolic_node (mp, mp->temp_head);
3072mp_free_symbolic_node (mp, mp->hold_head);
3073
3074@ The procedure |flush_node_list(p)| frees an entire linked list of
3075nodes that starts at a given position, until coming to a |NULL| pointer.
3076@^inner loop@>
3077
3078@c
3079static void mp_flush_node_list (MP mp, mp_node p) {
3080  mp_node q;    /* the node being recycled */
3081  FUNCTION_TRACE2 ("mp_flush_node_list(%p)\n", p);
3082  while (p != NULL) {
3083    q = p;
3084    p = p->link;
3085    if (q->type != mp_symbol_node)
3086      mp_free_token_node (mp, q);
3087    else
3088      mp_free_symbolic_node (mp, q);
3089  }
3090}
3091
3092
3093@* The command codes.
3094Before we can go much further, we need to define symbolic names for the internal
3095code numbers that represent the various commands obeyed by \MP. These codes
3096are somewhat arbitrary, but not completely so. For example,
3097some codes have been made adjacent so that |case| statements in the
3098program need not consider cases that are widely spaced, or so that |case|
3099statements can be replaced by |if| statements. A command can begin an
3100expression if and only if its code lies between |min_primary_command| and
3101|max_primary_command|, inclusive. The first token of a statement that doesn't
3102begin with an expression has a command code between |min_command| and
3103|max_statement_command|, inclusive. Anything less than |min_command| is
3104eliminated during macro expansions, and anything no more than |max_pre_command|
3105is eliminated when expanding \TeX\ material.  Ranges such as
3106|min_secondary_command..max_secondary_command| are used when parsing
3107expressions, but the relative ordering within such a range is generally not
3108critical.
3109
3110The ordering of the highest-numbered commands
3111(|comma<semicolon<end_group<stop|) is crucial for the parsing and
3112error-recovery methods of this program as is the ordering |if_test<fi_or_else|
3113for the smallest two commands.  The ordering is also important in the ranges
3114|numeric_token..plus_or_minus| and |left_brace..ampersand|.
3115
3116At any rate, here is the list, for future reference.
3117
3118@d mp_max_command_code mp_stop
3119@d mp_max_pre_command mp_mpx_break
3120@d mp_min_command (mp_defined_macro+1)
3121@d mp_max_statement_command mp_type_name
3122@d mp_min_primary_command mp_type_name
3123@d mp_min_suffix_token mp_internal_quantity
3124@d mp_max_suffix_token mp_numeric_token
3125@d mp_max_primary_command mp_plus_or_minus /* should also be |numeric_token+1| */
3126@d mp_min_tertiary_command mp_plus_or_minus
3127@d mp_max_tertiary_command mp_tertiary_binary
3128@d mp_min_expression_command mp_left_brace
3129@d mp_max_expression_command mp_equals
3130@d mp_min_secondary_command mp_and_command
3131@d mp_max_secondary_command mp_secondary_binary
3132@d mp_end_of_statement (cur_cmd()>mp_comma)
3133
3134
3135@<Enumeration types@>=
3136typedef enum {
3137mp_start_tex=1, /* begin \TeX\ material (\&{btex}, \&{verbatimtex}) */
3138mp_etex_marker, /* end \TeX\ material (\&{etex}) */
3139mp_mpx_break, /* stop reading an \.{MPX} file (\&{mpxbreak}) */
3140mp_if_test, /* conditional text (\&{if}) */
3141mp_fi_or_else, /* delimiters for conditionals (\&{elseif}, \&{else}, \&{fi}) */
3142mp_input, /* input a source file (\&{input}, \&{endinput}) */
3143mp_iteration, /* iterate (\&{for}, \&{forsuffixes}, \&{forever}, \&{endfor}) */
3144mp_repeat_loop, /* special command substituted for \&{endfor} */
3145mp_exit_test, /* premature exit from a loop (\&{exitif}) */
3146mp_relax, /* do nothing (\.{\char`\\}) */
3147mp_scan_tokens, /* put a string into the input buffer */
3148mp_runscript, /* put a script result string into the input buffer */
3149mp_maketext, /* put a script result string into the input buffer */
3150mp_expand_after, /* look ahead one token */
3151mp_defined_macro, /* a macro defined by the user */
3152mp_save_command, /* save a list of tokens (\&{save}) */
3153mp_interim_command, /* save an internal quantity (\&{interim}) */
3154mp_let_command, /* redefine a symbolic token (\&{let}) */
3155mp_new_internal, /* define a new internal quantity (\&{newinternal}) */
3156mp_macro_def, /* define a macro (\&{def}, \&{vardef}, etc.) */
3157mp_ship_out_command, /* output a character (\&{shipout}) */
3158mp_add_to_command, /* add to edges (\&{addto}) */
3159mp_bounds_command,  /* add bounding path to edges (\&{setbounds}, \&{clip}) */
3160mp_tfm_command, /* command for font metric info (\&{ligtable}, etc.) */
3161mp_protection_command, /* set protection flag (\&{outer}, \&{inner}) */
3162mp_show_command, /* diagnostic output (\&{show}, \&{showvariable}, etc.) */
3163mp_mode_command, /* set interaction level (\&{batchmode}, etc.) */
3164mp_random_seed, /* initialize random number generator (\&{randomseed}) */
3165mp_message_command, /* communicate to user (\&{message}, \&{errmessage}) */
3166mp_every_job_command, /* designate a starting token (\&{everyjob}) */
3167mp_delimiters, /* define a pair of delimiters (\&{delimiters}) */
3168mp_special_command, /* output special info (\&{special})
3169                       or font map info (\&{fontmapfile}, \&{fontmapline}) */
3170mp_write_command, /* write text to a file (\&{write}) */
3171mp_type_name, /* declare a type (\&{numeric}, \&{pair}, etc.) */
3172mp_left_delimiter, /* the left delimiter of a matching pair */
3173mp_begin_group, /* beginning of a group (\&{begingroup}) */
3174mp_nullary, /* an operator without arguments (e.g., \&{normaldeviate}) */
3175mp_unary, /* an operator with one argument (e.g., \&{sqrt}) */
3176mp_str_op, /* convert a suffix to a string (\&{str}) */
3177mp_cycle, /* close a cyclic path (\&{cycle}) */
3178mp_primary_binary, /* binary operation taking `\&{of}' (e.g., \&{point}) */
3179mp_capsule_token, /* a value that has been put into a token list */
3180mp_string_token, /* a string constant (e.g., |"hello"|) */
3181mp_internal_quantity, /* internal numeric parameter (e.g., \&{pausing}) */
3182mp_tag_token, /* a symbolic token without a primitive meaning */
3183mp_numeric_token, /* a numeric constant (e.g., \.{3.14159}) */
3184mp_plus_or_minus, /* either `\.+' or `\.-' */
3185mp_tertiary_secondary_macro, /* a macro defined by \&{secondarydef} */
3186mp_tertiary_binary, /* an operator at the tertiary level (e.g., `\.{++}') */
3187mp_left_brace, /* the operator `\.{\char`\{}' */
3188mp_path_join, /* the operator `\.{..}' */
3189mp_ampersand, /* the operator `\.\&' */
3190mp_expression_tertiary_macro, /* a macro defined by \&{tertiarydef} */
3191mp_expression_binary, /* an operator at the expression level (e.g., `\.<') */
3192mp_equals, /* the operator `\.=' */
3193mp_and_command, /* the operator `\&{and}' */
3194mp_secondary_primary_macro, /* a macro defined by \&{primarydef} */
3195mp_slash, /* the operator `\./' */
3196mp_secondary_binary, /* an operator at the binary level (e.g., \&{shifted}) */
3197mp_param_type, /* type of parameter (\&{primary}, \&{expr}, \&{suffix}, etc.) */
3198mp_controls, /* specify control points explicitly (\&{controls}) */
3199mp_tension, /* specify tension between knots (\&{tension}) */
3200mp_at_least, /* bounded tension value (\&{atleast}) */
3201mp_curl_command, /* specify curl at an end knot (\&{curl}) */
3202mp_macro_special, /* special macro operators (\&{quote}, \.{\#\AT!}, etc.) */
3203mp_right_delimiter, /* the right delimiter of a matching pair */
3204mp_left_bracket, /* the operator `\.[' */
3205mp_right_bracket, /* the operator `\.]' */
3206mp_right_brace, /* the operator `\.{\char`\}}' */
3207mp_with_option, /* option for filling (\&{withpen}, \&{withweight}, etc.) */
3208mp_thing_to_add,
3209  /* variant of \&{addto} (\&{contour}, \&{doublepath}, \&{also}) */
3210mp_of_token, /* the operator `\&{of}' */
3211mp_to_token, /* the operator `\&{to}' */
3212mp_step_token, /* the operator `\&{step}' */
3213mp_until_token, /* the operator `\&{until}' */
3214mp_within_token, /* the operator `\&{within}' */
3215mp_lig_kern_token,
3216  /* the operators `\&{kern}' and `\.{=:}' and `\.{=:\char'174}', etc. */
3217mp_assignment, /* the operator `\.{:=}' */
3218mp_skip_to, /* the operation `\&{skipto}' */
3219mp_bchar_label, /* the operator `\.{\char'174\char'174:}' */
3220mp_double_colon, /* the operator `\.{::}' */
3221mp_colon, /* the operator `\.:' */
3222@#
3223mp_comma, /* the operator `\.,', must be |colon+1| */
3224mp_semicolon, /* the operator `\.;', must be |comma+1| */
3225mp_end_group, /* end a group (\&{endgroup}), must be |semicolon+1| */
3226mp_stop, /* end a job (\&{end}, \&{dump}), must be |end_group+1| */
3227mp_outer_tag, /* protection code added to command code */
3228mp_undefined_cs, /* protection code added to command code */
3229} mp_command_code;
3230
3231@ Variables and capsules in \MP\ have a variety of ``types,''
3232distinguished by the code numbers defined here. These numbers are also
3233not completely arbitrary.  Things that get expanded must have types
3234|>mp_independent|; a type remaining after expansion is numeric if and only if
3235its code number is at least |numeric_type|; objects containing numeric
3236parts must have types between |transform_type| and |pair_type|;
3237all other types must be smaller than |transform_type|; and among the types
3238that are not unknown or vacuous, the smallest two must be |boolean_type|
3239and |string_type| in that order.
3240
3241@d unknown_tag 1 /* this constant is added to certain type codes below */
3242@d unknown_types mp_unknown_boolean: case mp_unknown_string:
3243  case mp_unknown_pen: case mp_unknown_picture: case mp_unknown_path
3244
3245@<Enumeration types@>=
3246typedef enum {
3247  mp_undefined = 0,       /* no type has been declared */
3248  mp_vacuous,                   /* no expression was present */
3249  mp_boolean_type,              /* \&{boolean} with a known value */
3250  mp_unknown_boolean,
3251  mp_string_type,               /* \&{string} with a known value */
3252  mp_unknown_string,
3253  mp_pen_type,                  /* \&{pen} with a known value */
3254  mp_unknown_pen,
3255  mp_path_type,                 /* \&{path} with a known value */
3256  mp_unknown_path,
3257  mp_picture_type,              /* \&{picture} with a known value */
3258  mp_unknown_picture,
3259  mp_transform_type,            /* \&{transform} variable or capsule */
3260  mp_color_type,                /* \&{color} variable or capsule */
3261  mp_cmykcolor_type,            /* \&{cmykcolor} variable or capsule */
3262  mp_pair_type,                 /* \&{pair} variable or capsule */
3263  mp_numeric_type,              /* variable that has been declared \&{numeric} but not used */
3264  mp_known,                     /* \&{numeric} with a known value */
3265  mp_dependent,                 /* a linear combination with |fraction| coefficients */
3266  mp_proto_dependent,           /* a linear combination with |scaled| coefficients */
3267  mp_independent,               /* \&{numeric} with unknown value */
3268  mp_token_list,                /* variable name or suffix argument or text argument */
3269  mp_structured,                /* variable with subscripts and attributes */
3270  mp_unsuffixed_macro,          /* variable defined with \&{vardef} but no \.{\AT!\#} */
3271  mp_suffixed_macro,            /* variable defined with \&{vardef} and \.{\AT!\#} */
3272/* here are some generic node types */
3273  mp_symbol_node,
3274  mp_token_node_type,
3275  mp_value_node_type,
3276  mp_attr_node_type,
3277  mp_subscr_node_type,
3278  mp_pair_node_type,
3279  mp_transform_node_type,
3280  mp_color_node_type,
3281  mp_cmykcolor_node_type,
3282/* it is important that the next 7 items remain in this order, for export */
3283  mp_fill_node_type,
3284  mp_stroked_node_type,
3285  mp_text_node_type,
3286  mp_start_clip_node_type,
3287  mp_start_bounds_node_type,
3288  mp_stop_clip_node_type,
3289  mp_stop_bounds_node_type,
3290  mp_dash_node_type,
3291  mp_dep_node_type,
3292  mp_if_node_type,
3293  mp_edge_header_node_type,
3294} mp_variable_type;
3295
3296@ @<Declarations@>=
3297static void mp_print_type (MP mp, quarterword t);
3298
3299@ @<Basic printing procedures@>=
3300static const char *mp_type_string (quarterword t) {
3301  const char *s = NULL;
3302  switch (t) {
3303  case mp_undefined:
3304    s = "undefined";
3305    break;
3306  case mp_vacuous:
3307    s = "vacuous";
3308    break;
3309  case mp_boolean_type:
3310    s = "boolean";
3311    break;
3312  case mp_unknown_boolean:
3313    s = "unknown boolean";
3314    break;
3315  case mp_string_type:
3316    s = "string";
3317    break;
3318  case mp_unknown_string:
3319    s = "unknown string";
3320    break;
3321  case mp_pen_type:
3322    s = "pen";
3323    break;
3324  case mp_unknown_pen:
3325    s = "unknown pen";
3326    break;
3327  case mp_path_type:
3328    s = "path";
3329    break;
3330  case mp_unknown_path:
3331    s = "unknown path";
3332    break;
3333  case mp_picture_type:
3334    s = "picture";
3335    break;
3336  case mp_unknown_picture:
3337    s = "unknown picture";
3338    break;
3339  case mp_transform_type:
3340    s = "transform";
3341    break;
3342  case mp_color_type:
3343    s = "color";
3344    break;
3345  case mp_cmykcolor_type:
3346    s = "cmykcolor";
3347    break;
3348  case mp_pair_type:
3349    s = "pair";
3350    break;
3351  case mp_known:
3352    s = "known numeric";
3353    break;
3354  case mp_dependent:
3355    s = "dependent";
3356    break;
3357  case mp_proto_dependent:
3358    s = "proto-dependent";
3359    break;
3360  case mp_numeric_type:
3361    s = "numeric";
3362    break;
3363  case mp_independent:
3364    s = "independent";
3365    break;
3366  case mp_token_list:
3367    s = "token list";
3368    break;
3369  case mp_structured:
3370    s = "mp_structured";
3371    break;
3372  case mp_unsuffixed_macro:
3373    s = "unsuffixed macro";
3374    break;
3375  case mp_suffixed_macro:
3376    s = "suffixed macro";
3377    break;
3378  case mp_symbol_node:
3379    s = "symbol node";
3380    break;
3381  case mp_token_node_type:
3382    s = "token node";
3383    break;
3384  case mp_value_node_type:
3385    s = "value node";
3386    break;
3387  case mp_attr_node_type:
3388    s = "attribute node";
3389    break;
3390  case mp_subscr_node_type:
3391    s = "subscript node";
3392    break;
3393  case mp_pair_node_type:
3394    s = "pair node";
3395    break;
3396  case mp_transform_node_type:
3397    s = "transform node";
3398    break;
3399  case mp_color_node_type:
3400    s = "color node";
3401    break;
3402  case mp_cmykcolor_node_type:
3403    s = "cmykcolor node";
3404    break;
3405  case mp_fill_node_type:
3406    s = "fill node";
3407    break;
3408  case mp_stroked_node_type:
3409    s = "stroked node";
3410    break;
3411  case mp_text_node_type:
3412    s = "text node";
3413    break;
3414  case mp_start_clip_node_type:
3415    s = "start clip node";
3416    break;
3417  case mp_start_bounds_node_type:
3418    s = "start bounds node";
3419    break;
3420  case mp_stop_clip_node_type:
3421    s = "stop clip node";
3422    break;
3423  case mp_stop_bounds_node_type:
3424    s = "stop bounds node";
3425    break;
3426  case mp_dash_node_type:
3427    s = "dash node";
3428    break;
3429  case mp_dep_node_type:
3430    s = "dependency node";
3431    break;
3432  case mp_if_node_type:
3433    s = "if node";
3434    break;
3435  case mp_edge_header_node_type:
3436    s = "edge header node";
3437    break;
3438  default:
3439    {
3440        char ss[256];
3441    	mp_snprintf (ss, 256, "<unknown type %d>", t);
3442	s = strdup(ss);
3443    }
3444    break;
3445  }
3446  return s;
3447}
3448void mp_print_type (MP mp, quarterword t) {
3449  if (t >= 0 && t <= mp_edge_header_node_type)
3450    mp_print (mp, mp_type_string (t));
3451  else
3452    mp_print (mp, "unknown");
3453}
3454
3455
3456@ Values inside \MP\ are stored in non-symbolic nodes that have a |name_type|
3457as well as a |type|. The possibilities for |name_type| are defined
3458here; they will be explained in more detail later.
3459
3460@<Enumeration types...@>=
3461typedef enum {
3462  mp_root = 0,  /* |name_type| at the top level of a variable */
3463  mp_saved_root,                /* same, when the variable has been saved */
3464  mp_structured_root,           /* |name_type| where a |mp_structured| branch occurs */
3465  mp_subscr,                    /* |name_type| in a subscript node */
3466  mp_attr,                      /* |name_type| in an attribute node */
3467  mp_x_part_sector,             /* |name_type| in the \&{xpart} of a node */
3468  mp_y_part_sector,             /* |name_type| in the \&{ypart} of a node */
3469  mp_xx_part_sector,            /* |name_type| in the \&{xxpart} of a node */
3470  mp_xy_part_sector,            /* |name_type| in the \&{xypart} of a node */
3471  mp_yx_part_sector,            /* |name_type| in the \&{yxpart} of a node */
3472  mp_yy_part_sector,            /* |name_type| in the \&{yypart} of a node */
3473  mp_red_part_sector,           /* |name_type| in the \&{redpart} of a node */
3474  mp_green_part_sector,         /* |name_type| in the \&{greenpart} of a node */
3475  mp_blue_part_sector,          /* |name_type| in the \&{bluepart} of a node */
3476  mp_cyan_part_sector,          /* |name_type| in the \&{redpart} of a node */
3477  mp_magenta_part_sector,       /* |name_type| in the \&{greenpart} of a node */
3478  mp_yellow_part_sector,        /* |name_type| in the \&{bluepart} of a node */
3479  mp_black_part_sector,         /* |name_type| in the \&{greenpart} of a node */
3480  mp_grey_part_sector,          /* |name_type| in the \&{bluepart} of a node */
3481  mp_capsule,                   /* |name_type| in stashed-away subexpressions */
3482  mp_token,                     /* |name_type| in a numeric token or string token */
3483  /* Symbolic nodes also have |name_type|, which is a different enumeration */
3484  mp_normal_sym,
3485  mp_internal_sym,              /* for values of internals */
3486  mp_macro_sym,                 /* for macro names */
3487  mp_expr_sym,                  /* for macro parameters if type |expr| */
3488  mp_suffix_sym,                /* for macro parameters if type |suffix| */
3489  mp_text_sym,                  /* for macro parameters if type |text| */
3490  @<Operation codes@>
3491} mp_name_type_type;
3492
3493@ Primitive operations that produce values have a secondary identification
3494code in addition to their command code; it's something like genera and species.
3495For example, `\.*' has the command code |primary_binary|, and its
3496secondary identification is |times|. The secondary codes start such that
3497they don't overlap with the type codes; some type codes (e.g., |mp_string_type|)
3498are used as operators as well as type identifications.  The relative values
3499are not critical, except for |true_code..false_code|, |or_op..and_op|,
3500and |filled_op..bounded_op|.  The restrictions are that
3501|and_op-false_code=or_op-true_code|, that the ordering of
3502|x_part...blue_part| must match that of |x_part_sector..mp_blue_part_sector|,
3503and the ordering of |filled_op..bounded_op| must match that of the code
3504values they test for.
3505
3506@d mp_min_of mp_substring_of
3507
3508@<Operation codes@>=
3509mp_true_code, /* operation code for \.{true} */
3510mp_false_code, /* operation code for \.{false} */
3511mp_null_picture_code, /* operation code for \.{nullpicture} */
3512mp_null_pen_code, /* operation code for \.{nullpen} */
3513mp_read_string_op, /* operation code for \.{readstring} */
3514mp_pen_circle, /* operation code for \.{pencircle} */
3515mp_normal_deviate, /* operation code for \.{normaldeviate} */
3516mp_read_from_op, /* operation code for \.{readfrom} */
3517mp_close_from_op, /* operation code for \.{closefrom} */
3518mp_odd_op, /* operation code for \.{odd} */
3519mp_known_op, /* operation code for \.{known} */
3520mp_unknown_op, /* operation code for \.{unknown} */
3521mp_not_op, /* operation code for \.{not} */
3522mp_decimal, /* operation code for \.{decimal} */
3523mp_reverse, /* operation code for \.{reverse} */
3524mp_make_path_op, /* operation code for \.{makepath} */
3525mp_make_pen_op, /* operation code for \.{makepen} */
3526mp_oct_op, /* operation code for \.{oct} */
3527mp_hex_op, /* operation code for \.{hex} */
3528mp_ASCII_op, /* operation code for \.{ASCII} */
3529mp_char_op, /* operation code for \.{char} */
3530mp_length_op, /* operation code for \.{length} */
3531mp_turning_op, /* operation code for \.{turningnumber} */
3532mp_color_model_part, /* operation code for \.{colormodel} */
3533mp_x_part, /* operation code for \.{xpart} */
3534mp_y_part, /* operation code for \.{ypart} */
3535mp_xx_part, /* operation code for \.{xxpart} */
3536mp_xy_part, /* operation code for \.{xypart} */
3537mp_yx_part, /* operation code for \.{yxpart} */
3538mp_yy_part, /* operation code for \.{yypart} */
3539mp_red_part, /* operation code for \.{redpart} */
3540mp_green_part, /* operation code for \.{greenpart} */
3541mp_blue_part, /* operation code for \.{bluepart} */
3542mp_cyan_part, /* operation code for \.{cyanpart} */
3543mp_magenta_part, /* operation code for \.{magentapart} */
3544mp_yellow_part, /* operation code for \.{yellowpart} */
3545mp_black_part, /* operation code for \.{blackpart} */
3546mp_grey_part, /* operation code for \.{greypart} */
3547mp_font_part, /* operation code for \.{fontpart} */
3548mp_text_part, /* operation code for \.{textpart} */
3549mp_path_part, /* operation code for \.{pathpart} */
3550mp_pen_part, /* operation code for \.{penpart} */
3551mp_dash_part, /* operation code for \.{dashpart} */
3552mp_prescript_part, /* operation code for \.{prescriptpart} */
3553mp_postscript_part, /* operation code for \.{postscriptpart} */
3554mp_sqrt_op, /* operation code for \.{sqrt} */
3555mp_m_exp_op, /* operation code for \.{mexp} */
3556mp_m_log_op, /* operation code for \.{mlog} */
3557mp_sin_d_op, /* operation code for \.{sind} */
3558mp_cos_d_op, /* operation code for \.{cosd} */
3559mp_floor_op, /* operation code for \.{floor} */
3560mp_uniform_deviate, /* operation code for \.{uniformdeviate} */
3561mp_char_exists_op, /* operation code for \.{charexists} */
3562mp_font_size, /* operation code for \.{fontsize} */
3563mp_ll_corner_op, /* operation code for \.{llcorner} */
3564mp_lr_corner_op, /* operation code for \.{lrcorner} */
3565mp_ul_corner_op, /* operation code for \.{ulcorner} */
3566mp_ur_corner_op, /* operation code for \.{urcorner} */
3567mp_arc_length, /* operation code for \.{arclength} */
3568mp_angle_op, /* operation code for \.{angle} */
3569mp_cycle_op, /* operation code for \.{cycle} */
3570mp_filled_op, /* operation code for \.{filled} */
3571mp_stroked_op, /* operation code for \.{stroked} */
3572mp_textual_op, /* operation code for \.{textual} */
3573mp_clipped_op, /* operation code for \.{clipped} */
3574mp_bounded_op, /* operation code for \.{bounded} */
3575mp_plus, /* operation code for \.+ */
3576mp_minus, /* operation code for \.- */
3577mp_times, /* operation code for \.* */
3578mp_over, /* operation code for \./ */
3579mp_pythag_add, /* operation code for \.{++} */
3580mp_pythag_sub, /* operation code for \.{+-+} */
3581mp_or_op, /* operation code for \.{or} */
3582mp_and_op, /* operation code for \.{and} */
3583mp_less_than, /* operation code for \.< */
3584mp_less_or_equal, /* operation code for \.{<=} */
3585mp_greater_than, /* operation code for \.> */
3586mp_greater_or_equal, /* operation code for \.{>=} */
3587mp_equal_to, /* operation code for \.= */
3588mp_unequal_to, /* operation code for \.{<>} */
3589mp_concatenate, /* operation code for \.\& */
3590mp_rotated_by, /* operation code for \.{rotated} */
3591mp_slanted_by, /* operation code for \.{slanted} */
3592mp_scaled_by, /* operation code for \.{scaled} */
3593mp_shifted_by, /* operation code for \.{shifted} */
3594mp_transformed_by, /* operation code for \.{transformed} */
3595mp_x_scaled, /* operation code for \.{xscaled} */
3596mp_y_scaled, /* operation code for \.{yscaled} */
3597mp_z_scaled, /* operation code for \.{zscaled} */
3598mp_in_font, /* operation code for \.{infont} */
3599mp_intersect, /* operation code for \.{intersectiontimes} */
3600mp_double_dot, /* operation code for improper \.{..} */
3601mp_substring_of, /* operation code for \.{substring} */
3602mp_subpath_of, /* operation code for \.{subpath} */
3603mp_direction_time_of, /* operation code for \.{directiontime} */
3604mp_point_of, /* operation code for \.{point} */
3605mp_precontrol_of, /* operation code for \.{precontrol} */
3606mp_postcontrol_of, /* operation code for \.{postcontrol} */
3607mp_pen_offset_of, /* operation code for \.{penoffset} */
3608mp_arc_time_of, /* operation code for \.{arctime} */
3609mp_version, /* operation code for \.{mpversion} */
3610mp_envelope_of, /* operation code for \.{envelope} */
3611mp_glyph_infont, /* operation code for \.{glyph} */
3612mp_kern_flag /* operation code for \.{kern} */
3613
3614@ @c
3615static void mp_print_op (MP mp, quarterword c) {
3616  if (c <= mp_numeric_type) {
3617    mp_print_type (mp, c);
3618  } else {
3619    switch (c) {
3620    case mp_true_code:
3621      mp_print (mp, "true");
3622      break;
3623    case mp_false_code:
3624      mp_print (mp, "false");
3625      break;
3626    case mp_null_picture_code:
3627      mp_print (mp, "nullpicture");
3628      break;
3629    case mp_null_pen_code:
3630      mp_print (mp, "nullpen");
3631      break;
3632    case mp_read_string_op:
3633      mp_print (mp, "readstring");
3634      break;
3635    case mp_pen_circle:
3636      mp_print (mp, "pencircle");
3637      break;
3638    case mp_normal_deviate:
3639      mp_print (mp, "normaldeviate");
3640      break;
3641    case mp_read_from_op:
3642      mp_print (mp, "readfrom");
3643      break;
3644    case mp_close_from_op:
3645      mp_print (mp, "closefrom");
3646      break;
3647    case mp_odd_op:
3648      mp_print (mp, "odd");
3649      break;
3650    case mp_known_op:
3651      mp_print (mp, "known");
3652      break;
3653    case mp_unknown_op:
3654      mp_print (mp, "unknown");
3655      break;
3656    case mp_not_op:
3657      mp_print (mp, "not");
3658      break;
3659    case mp_decimal:
3660      mp_print (mp, "decimal");
3661      break;
3662    case mp_reverse:
3663      mp_print (mp, "reverse");
3664      break;
3665    case mp_make_path_op:
3666      mp_print (mp, "makepath");
3667      break;
3668    case mp_make_pen_op:
3669      mp_print (mp, "makepen");
3670      break;
3671    case mp_oct_op:
3672      mp_print (mp, "oct");
3673      break;
3674    case mp_hex_op:
3675      mp_print (mp, "hex");
3676      break;
3677    case mp_ASCII_op:
3678      mp_print (mp, "ASCII");
3679      break;
3680    case mp_char_op:
3681      mp_print (mp, "char");
3682      break;
3683    case mp_length_op:
3684      mp_print (mp, "length");
3685      break;
3686    case mp_turning_op:
3687      mp_print (mp, "turningnumber");
3688      break;
3689    case mp_x_part:
3690      mp_print (mp, "xpart");
3691      break;
3692    case mp_y_part:
3693      mp_print (mp, "ypart");
3694      break;
3695    case mp_xx_part:
3696      mp_print (mp, "xxpart");
3697      break;
3698    case mp_xy_part:
3699      mp_print (mp, "xypart");
3700      break;
3701    case mp_yx_part:
3702      mp_print (mp, "yxpart");
3703      break;
3704    case mp_yy_part:
3705      mp_print (mp, "yypart");
3706      break;
3707    case mp_red_part:
3708      mp_print (mp, "redpart");
3709      break;
3710    case mp_green_part:
3711      mp_print (mp, "greenpart");
3712      break;
3713    case mp_blue_part:
3714      mp_print (mp, "bluepart");
3715      break;
3716    case mp_cyan_part:
3717      mp_print (mp, "cyanpart");
3718      break;
3719    case mp_magenta_part:
3720      mp_print (mp, "magentapart");
3721      break;
3722    case mp_yellow_part:
3723      mp_print (mp, "yellowpart");
3724      break;
3725    case mp_black_part:
3726      mp_print (mp, "blackpart");
3727      break;
3728    case mp_grey_part:
3729      mp_print (mp, "greypart");
3730      break;
3731    case mp_color_model_part:
3732      mp_print (mp, "colormodel");
3733      break;
3734    case mp_font_part:
3735      mp_print (mp, "fontpart");
3736      break;
3737    case mp_text_part:
3738      mp_print (mp, "textpart");
3739      break;
3740    case mp_prescript_part:
3741      mp_print (mp, "prescriptpart");
3742      break;
3743    case mp_postscript_part:
3744      mp_print (mp, "postscriptpart");
3745      break;
3746    case mp_path_part:
3747      mp_print (mp, "pathpart");
3748      break;
3749    case mp_pen_part:
3750      mp_print (mp, "penpart");
3751      break;
3752    case mp_dash_part:
3753      mp_print (mp, "dashpart");
3754      break;
3755    case mp_sqrt_op:
3756      mp_print (mp, "sqrt");
3757      break;
3758    case mp_m_exp_op:
3759      mp_print (mp, "mexp");
3760      break;
3761    case mp_m_log_op:
3762      mp_print (mp, "mlog");
3763      break;
3764    case mp_sin_d_op:
3765      mp_print (mp, "sind");
3766      break;
3767    case mp_cos_d_op:
3768      mp_print (mp, "cosd");
3769      break;
3770    case mp_floor_op:
3771      mp_print (mp, "floor");
3772      break;
3773    case mp_uniform_deviate:
3774      mp_print (mp, "uniformdeviate");
3775      break;
3776    case mp_char_exists_op:
3777      mp_print (mp, "charexists");
3778      break;
3779    case mp_font_size:
3780      mp_print (mp, "fontsize");
3781      break;
3782    case mp_ll_corner_op:
3783      mp_print (mp, "llcorner");
3784      break;
3785    case mp_lr_corner_op:
3786      mp_print (mp, "lrcorner");
3787      break;
3788    case mp_ul_corner_op:
3789      mp_print (mp, "ulcorner");
3790      break;
3791    case mp_ur_corner_op:
3792      mp_print (mp, "urcorner");
3793      break;
3794    case mp_arc_length:
3795      mp_print (mp, "arclength");
3796      break;
3797    case mp_angle_op:
3798      mp_print (mp, "angle");
3799      break;
3800    case mp_cycle_op:
3801      mp_print (mp, "cycle");
3802      break;
3803    case mp_filled_op:
3804      mp_print (mp, "filled");
3805      break;
3806    case mp_stroked_op:
3807      mp_print (mp, "stroked");
3808      break;
3809    case mp_textual_op:
3810      mp_print (mp, "textual");
3811      break;
3812    case mp_clipped_op:
3813      mp_print (mp, "clipped");
3814      break;
3815    case mp_bounded_op:
3816      mp_print (mp, "bounded");
3817      break;
3818    case mp_plus:
3819      mp_print_char (mp, xord ('+'));
3820      break;
3821    case mp_minus:
3822      mp_print_char (mp, xord ('-'));
3823      break;
3824    case mp_times:
3825      mp_print_char (mp, xord ('*'));
3826      break;
3827    case mp_over:
3828      mp_print_char (mp, xord ('/'));
3829      break;
3830    case mp_pythag_add:
3831      mp_print (mp, "++");
3832      break;
3833    case mp_pythag_sub:
3834      mp_print (mp, "+-+");
3835      break;
3836    case mp_or_op:
3837      mp_print (mp, "or");
3838      break;
3839    case mp_and_op:
3840      mp_print (mp, "and");
3841      break;
3842    case mp_less_than:
3843      mp_print_char (mp, xord ('<'));
3844      break;
3845    case mp_less_or_equal:
3846      mp_print (mp, "<=");
3847      break;
3848    case mp_greater_than:
3849      mp_print_char (mp, xord ('>'));
3850      break;
3851    case mp_greater_or_equal:
3852      mp_print (mp, ">=");
3853      break;
3854    case mp_equal_to:
3855      mp_print_char (mp, xord ('='));
3856      break;
3857    case mp_unequal_to:
3858      mp_print (mp, "<>");
3859      break;
3860    case mp_concatenate:
3861      mp_print (mp, "&");
3862      break;
3863    case mp_rotated_by:
3864      mp_print (mp, "rotated");
3865      break;
3866    case mp_slanted_by:
3867      mp_print (mp, "slanted");
3868      break;
3869    case mp_scaled_by:
3870      mp_print (mp, "scaled");
3871      break;
3872    case mp_shifted_by:
3873      mp_print (mp, "shifted");
3874      break;
3875    case mp_transformed_by:
3876      mp_print (mp, "transformed");
3877      break;
3878    case mp_x_scaled:
3879      mp_print (mp, "xscaled");
3880      break;
3881    case mp_y_scaled:
3882      mp_print (mp, "yscaled");
3883      break;
3884    case mp_z_scaled:
3885      mp_print (mp, "zscaled");
3886      break;
3887    case mp_in_font:
3888      mp_print (mp, "infont");
3889      break;
3890    case mp_intersect:
3891      mp_print (mp, "intersectiontimes");
3892      break;
3893    case mp_substring_of:
3894      mp_print (mp, "substring");
3895      break;
3896    case mp_subpath_of:
3897      mp_print (mp, "subpath");
3898      break;
3899    case mp_direction_time_of:
3900      mp_print (mp, "directiontime");
3901      break;
3902    case mp_point_of:
3903      mp_print (mp, "point");
3904      break;
3905    case mp_precontrol_of:
3906      mp_print (mp, "precontrol");
3907      break;
3908    case mp_postcontrol_of:
3909      mp_print (mp, "postcontrol");
3910      break;
3911    case mp_pen_offset_of:
3912      mp_print (mp, "penoffset");
3913      break;
3914    case mp_arc_time_of:
3915      mp_print (mp, "arctime");
3916      break;
3917    case mp_version:
3918      mp_print (mp, "mpversion");
3919      break;
3920    case mp_envelope_of:
3921      mp_print (mp, "envelope");
3922      break;
3923    case mp_glyph_infont:
3924      mp_print (mp, "glyph");
3925      break;
3926    default:
3927      mp_print (mp, "..");
3928      break;
3929    }
3930  }
3931}
3932
3933
3934@ \MP\ also has a bunch of internal parameters that a user might want to
3935fuss with. Every such parameter has an identifying code number, defined here.
3936
3937@<Types...@>=
3938enum mp_given_internal {
3939  mp_output_template = 1,       /* a string set up by \&{outputtemplate} */
3940  mp_output_filename,           /* the output file name, accessible as \&{outputfilename} */
3941  mp_output_format,             /* the output format set up by \&{outputformat} */
3942  mp_output_format_options,     /* the output format options set up by \&{outputformatoptions} */
3943  mp_number_system,             /* the number system as set up by \&{numbersystem} */
3944  mp_number_precision,          /* the number system precision as set up by \&{numberprecision} */
3945  mp_job_name,                  /* the perceived jobname, as set up from the options stucture,
3946                                   the name of the input file, or by \&{jobname}  */
3947  mp_tracing_titles,            /* show titles online when they appear */
3948  mp_tracing_equations,         /* show each variable when it becomes known */
3949  mp_tracing_capsules,          /* show capsules too */
3950  mp_tracing_choices,           /* show the control points chosen for paths */
3951  mp_tracing_specs,             /* show path subdivision prior to filling with polygonal a pen */
3952  mp_tracing_commands,          /* show commands and operations before they are performed */
3953  mp_tracing_restores,          /* show when a variable or internal is restored */
3954  mp_tracing_macros,            /* show macros before they are expanded */
3955  mp_tracing_output,            /* show digitized edges as they are output */
3956  mp_tracing_stats,             /* show memory usage at end of job */
3957  mp_tracing_lost_chars,        /* show characters that aren't \&{infont} */
3958  mp_tracing_online,            /* show long diagnostics on terminal and in the log file */
3959  mp_year,                      /* the current year (e.g., 1984) */
3960  mp_month,                     /* the current month (e.g., 3 $\equiv$ March) */
3961  mp_day,                       /* the current day of the month */
3962  mp_time,                      /* the number of minutes past midnight when this job started */
3963  mp_hour,                      /* the number of hours past midnight when this job started */
3964  mp_minute,                    /* the number of minutes in that hour when this job started */
3965  mp_char_code,                 /* the number of the next character to be output */
3966  mp_char_ext,                  /* the extension code of the next character to be output */
3967  mp_char_wd,                   /* the width of the next character to be output */
3968  mp_char_ht,                   /* the height of the next character to be output */
3969  mp_char_dp,                   /* the depth of the next character to be output */
3970  mp_char_ic,                   /* the italic correction of the next character to be output */
3971  mp_design_size,               /* the unit of measure used for |mp_char_wd..mp_char_ic|, in points */
3972  mp_pausing,                   /* positive to display lines on the terminal before they are read */
3973  mp_showstopping,              /* positive to stop after each \&{show} command */
3974  mp_fontmaking,                /* positive if font metric output is to be produced */
3975  mp_linejoin,                  /* as in \ps: 0 for mitered, 1 for round, 2 for beveled */
3976  mp_linecap,                   /* as in \ps: 0 for butt, 1 for round, 2 for square */
3977  mp_miterlimit,                /* controls miter length as in \ps */
3978  mp_warning_check,             /* controls error message when variable value is large */
3979  mp_boundary_char,             /* the right boundary character for ligatures */
3980  mp_prologues,                 /* positive to output conforming PostScript using built-in fonts */
3981  mp_true_corners,              /* positive to make \&{llcorner} etc. ignore \&{setbounds} */
3982  mp_default_color_model,       /* the default color model for unspecified items */
3983  mp_restore_clip_color,
3984  mp_procset,                   /* wether or not create PostScript command shortcuts */
3985  mp_hppp,                      /* horizontal pixels per point (for png output) */
3986  mp_vppp,                      /* vertical pixels per point (for png output) */
3987  mp_gtroffmode,                /* whether the user specified |-troff| on the command line */
3988};
3989typedef struct {
3990  mp_value v;
3991  char *intname;
3992} mp_internal;
3993
3994
3995@ @<MPlib internal header stuff@>=
3996#define internal_value(A) mp->internal[(A)].v.data.n
3997#define set_internal_from_number(A,B) do { \
3998  number_clone (internal_value ((A)),(B));\
3999} while (0)
4000#define internal_string(A) (mp_string)mp->internal[(A)].v.data.str
4001#define set_internal_string(A,B) mp->internal[(A)].v.data.str=(B)
4002#define internal_name(A) mp->internal[(A)].intname
4003#define set_internal_name(A,B) mp->internal[(A)].intname=(B)
4004#define internal_type(A) (mp_variable_type)mp->internal[(A)].v.type
4005#define set_internal_type(A,B) mp->internal[(A)].v.type=(B)
4006#define set_internal_from_cur_exp(A) do { \
4007  if (internal_type ((A)) == mp_string_type) { \
4008      add_str_ref (cur_exp_str ()); \
4009      set_internal_string ((A), cur_exp_str ()); \
4010  } else { \
4011      set_internal_from_number ((A), cur_exp_value_number ()); \
4012  } \
4013} while (0)
4014
4015
4016
4017@
4018
4019@d max_given_internal mp_gtroffmode
4020
4021@<Glob...@>=
4022mp_internal *internal;  /* the values of internal quantities */
4023int int_ptr;    /* the maximum internal quantity defined so far */
4024int max_internal;       /* current maximum number of internal quantities */
4025
4026@ @<Option variables@>=
4027int troff_mode;
4028
4029@ @<Allocate or initialize ...@>=
4030mp->max_internal = 2 * max_given_internal;
4031mp->internal = xmalloc ((mp->max_internal + 1), sizeof (mp_internal));
4032memset (mp->internal, 0,
4033        (size_t) (mp->max_internal + 1) * sizeof (mp_internal));
4034{
4035  int i;
4036  for (i = 1; i <= mp->max_internal; i++) {
4037    new_number(mp->internal[i].v.data.n);
4038  }
4039  for (i = 1; i <= max_given_internal; i++) {
4040    set_internal_type (i, mp_known);
4041  }
4042}
4043set_internal_type (mp_output_format, mp_string_type);
4044set_internal_type (mp_output_filename, mp_string_type);
4045set_internal_type (mp_output_format_options, mp_string_type);
4046set_internal_type (mp_output_template, mp_string_type);
4047set_internal_type (mp_number_system, mp_string_type);
4048set_internal_type (mp_job_name, mp_string_type);
4049mp->troff_mode = (opt->troff_mode > 0 ? true : false);
4050
4051@ @<Exported function ...@>=
4052int mp_troff_mode (MP mp);
4053
4054@ @c
4055int mp_troff_mode (MP mp) {
4056  return mp->troff_mode;
4057}
4058
4059
4060@ @<Set initial ...@>=
4061mp->int_ptr = max_given_internal;
4062
4063@ The symbolic names for internal quantities are put into \MP's hash table
4064by using a routine called |primitive|, which will be defined later. Let us
4065enter them now, so that we don't have to list all those names again
4066anywhere else.
4067
4068@<Put each of \MP's primitives into the hash table@>=
4069mp_primitive (mp, "tracingtitles", mp_internal_quantity, mp_tracing_titles);
4070@:tracingtitles_}{\&{tracingtitles} primitive@>;
4071mp_primitive (mp, "tracingequations", mp_internal_quantity, mp_tracing_equations);
4072@:mp_tracing_equations_}{\&{tracingequations} primitive@>;
4073mp_primitive (mp, "tracingcapsules", mp_internal_quantity, mp_tracing_capsules);
4074@:mp_tracing_capsules_}{\&{tracingcapsules} primitive@>;
4075mp_primitive (mp, "tracingchoices", mp_internal_quantity, mp_tracing_choices);
4076@:mp_tracing_choices_}{\&{tracingchoices} primitive@>;
4077mp_primitive (mp, "tracingspecs", mp_internal_quantity, mp_tracing_specs);
4078@:mp_tracing_specs_}{\&{tracingspecs} primitive@>;
4079mp_primitive (mp, "tracingcommands", mp_internal_quantity, mp_tracing_commands);
4080@:mp_tracing_commands_}{\&{tracingcommands} primitive@>;
4081mp_primitive (mp, "tracingrestores", mp_internal_quantity, mp_tracing_restores);
4082@:mp_tracing_restores_}{\&{tracingrestores} primitive@>;
4083mp_primitive (mp, "tracingmacros", mp_internal_quantity, mp_tracing_macros);
4084@:mp_tracing_macros_}{\&{tracingmacros} primitive@>;
4085mp_primitive (mp, "tracingoutput", mp_internal_quantity, mp_tracing_output);
4086@:mp_tracing_output_}{\&{tracingoutput} primitive@>;
4087mp_primitive (mp, "tracingstats", mp_internal_quantity, mp_tracing_stats);
4088@:mp_tracing_stats_}{\&{tracingstats} primitive@>;
4089mp_primitive (mp, "tracinglostchars", mp_internal_quantity, mp_tracing_lost_chars);
4090@:mp_tracing_lost_chars_}{\&{tracinglostchars} primitive@>;
4091mp_primitive (mp, "tracingonline", mp_internal_quantity, mp_tracing_online);
4092@:mp_tracing_online_}{\&{tracingonline} primitive@>;
4093mp_primitive (mp, "year", mp_internal_quantity, mp_year);
4094@:mp_year_}{\&{year} primitive@>;
4095mp_primitive (mp, "month", mp_internal_quantity, mp_month);
4096@:mp_month_}{\&{month} primitive@>;
4097mp_primitive (mp, "day", mp_internal_quantity, mp_day);
4098@:mp_day_}{\&{day} primitive@>;
4099mp_primitive (mp, "time", mp_internal_quantity, mp_time);
4100@:time_}{\&{time} primitive@>;
4101mp_primitive (mp, "hour", mp_internal_quantity, mp_hour);
4102@:hour_}{\&{hour} primitive@>;
4103mp_primitive (mp, "minute", mp_internal_quantity, mp_minute);
4104@:minute_}{\&{minute} primitive@>;
4105mp_primitive (mp, "charcode", mp_internal_quantity, mp_char_code);
4106@:mp_char_code_}{\&{charcode} primitive@>;
4107mp_primitive (mp, "charext", mp_internal_quantity, mp_char_ext);
4108@:mp_char_ext_}{\&{charext} primitive@>;
4109mp_primitive (mp, "charwd", mp_internal_quantity, mp_char_wd);
4110@:mp_char_wd_}{\&{charwd} primitive@>;
4111mp_primitive (mp, "charht", mp_internal_quantity, mp_char_ht);
4112@:mp_char_ht_}{\&{charht} primitive@>;
4113mp_primitive (mp, "chardp", mp_internal_quantity, mp_char_dp);
4114@:mp_char_dp_}{\&{chardp} primitive@>;
4115mp_primitive (mp, "charic", mp_internal_quantity, mp_char_ic);
4116@:mp_char_ic_}{\&{charic} primitive@>;
4117mp_primitive (mp, "designsize", mp_internal_quantity, mp_design_size);
4118@:mp_design_size_}{\&{designsize} primitive@>;
4119mp_primitive (mp, "pausing", mp_internal_quantity, mp_pausing);
4120@:mp_pausing_}{\&{pausing} primitive@>;
4121mp_primitive (mp, "showstopping", mp_internal_quantity, mp_showstopping);
4122@:mp_showstopping_}{\&{showstopping} primitive@>;
4123mp_primitive (mp, "fontmaking", mp_internal_quantity, mp_fontmaking);
4124@:mp_fontmaking_}{\&{fontmaking} primitive@>;
4125mp_primitive (mp, "linejoin", mp_internal_quantity, mp_linejoin);
4126@:mp_linejoin_}{\&{linejoin} primitive@>;
4127mp_primitive (mp, "linecap", mp_internal_quantity, mp_linecap);
4128@:mp_linecap_}{\&{linecap} primitive@>;
4129mp_primitive (mp, "miterlimit", mp_internal_quantity, mp_miterlimit);
4130@:mp_miterlimit_}{\&{miterlimit} primitive@>;
4131mp_primitive (mp, "warningcheck", mp_internal_quantity, mp_warning_check);
4132@:mp_warning_check_}{\&{warningcheck} primitive@>;
4133mp_primitive (mp, "boundarychar", mp_internal_quantity, mp_boundary_char);
4134@:mp_boundary_char_}{\&{boundarychar} primitive@>;
4135mp_primitive (mp, "prologues", mp_internal_quantity, mp_prologues);
4136@:mp_prologues_}{\&{prologues} primitive@>;
4137mp_primitive (mp, "truecorners", mp_internal_quantity, mp_true_corners);
4138@:mp_true_corners_}{\&{truecorners} primitive@>;
4139mp_primitive (mp, "mpprocset", mp_internal_quantity, mp_procset);
4140@:mp_procset_}{\&{mpprocset} primitive@>;
4141mp_primitive (mp, "troffmode", mp_internal_quantity, mp_gtroffmode);
4142@:troffmode_}{\&{troffmode} primitive@>;
4143mp_primitive (mp, "defaultcolormodel", mp_internal_quantity,
4144              mp_default_color_model);
4145@:mp_default_color_model_}{\&{defaultcolormodel} primitive@>;
4146mp_primitive (mp, "restoreclipcolor", mp_internal_quantity, mp_restore_clip_color);
4147@:mp_restore_clip_color_}{\&{restoreclipcolor} primitive@>;
4148mp_primitive (mp, "outputtemplate", mp_internal_quantity, mp_output_template);
4149@:mp_output_template_}{\&{outputtemplate} primitive@>;
4150mp_primitive (mp, "outputfilename", mp_internal_quantity, mp_output_filename);
4151@:mp_output_filename_}{\&{outputfilename} primitive@>;
4152mp_primitive (mp, "numbersystem", mp_internal_quantity, mp_number_system);
4153@:mp_number_system_}{\&{numbersystem} primitive@>;
4154mp_primitive (mp, "numberprecision", mp_internal_quantity, mp_number_precision);
4155@:mp_number_precision_}{\&{numberprecision} primitive@>;
4156mp_primitive (mp, "outputformat", mp_internal_quantity, mp_output_format);
4157@:mp_output_format_}{\&{outputformat} primitive@>;
4158mp_primitive (mp, "outputformatoptions", mp_internal_quantity, mp_output_format_options);
4159@:mp_output_format_options_}{\&{outputformatoptions} primitive@>;
4160mp_primitive (mp, "jobname", mp_internal_quantity, mp_job_name);
4161@:mp_job_name_}{\&{jobname} primitive@>
4162mp_primitive (mp, "hppp", mp_internal_quantity, mp_hppp);
4163@:mp_hppp_}{\&{hppp} primitive@>;
4164mp_primitive (mp, "vppp", mp_internal_quantity, mp_vppp);
4165@:mp_vppp_}{\&{vppp} primitive@>;
4166
4167
4168@ Colors can be specified in four color models. In the special
4169case of |no_model|, MetaPost does not output any color operator to
4170the postscript output.
4171
4172Note: these values are passed directly on to |with_option|. This only
4173works because the other possible values passed to |with_option| are
41748 and 10 respectively (from |with_pen| and |with_picture|).
4175
4176There is a first state, that is only used for |gs_colormodel|. It flags
4177the fact that there has not been any kind of color specification by
4178the user so far in the game.
4179
4180@<MPlib header stuff@>=
4181enum mp_color_model {
4182  mp_no_model = 1,
4183  mp_grey_model = 3,
4184  mp_rgb_model = 5,
4185  mp_cmyk_model = 7,
4186  mp_uninitialized_model = 9
4187};
4188
4189
4190@ @<Initialize table entries@>=
4191set_internal_from_number (mp_default_color_model, unity_t);
4192number_multiply_int (internal_value (mp_default_color_model), mp_rgb_model);
4193number_clone (internal_value (mp_restore_clip_color), unity_t);
4194number_clone (internal_value (mp_hppp), unity_t);
4195number_clone (internal_value (mp_vppp), unity_t);
4196set_internal_string (mp_output_template, mp_intern (mp, "%j.%c"));
4197set_internal_string (mp_output_filename, mp_intern (mp, ""));
4198set_internal_string (mp_output_format, mp_intern (mp, "eps"));
4199set_internal_string (mp_output_format_options, mp_intern (mp, ""));
4200set_internal_string (mp_number_system, mp_intern (mp, "scaled"));
4201set_internal_from_number (mp_number_precision, precision_default);
4202#if DEBUG
4203number_clone (internal_value (mp_tracing_titles), three_t);
4204number_clone (internal_value (mp_tracing_equations), three_t);
4205number_clone (internal_value (mp_tracing_capsules), three_t);
4206number_clone (internal_value (mp_tracing_choices), three_t);
4207number_clone (internal_value (mp_tracing_specs), three_t);
4208number_clone (internal_value (mp_tracing_commands), three_t);
4209number_clone (internal_value (mp_tracing_restores), three_t);
4210number_clone (internal_value (mp_tracing_macros), three_t);
4211number_clone (internal_value (mp_tracing_output), three_t);
4212number_clone (internal_value (mp_tracing_stats), three_t);
4213number_clone (internal_value (mp_tracing_lost_chars), three_t);
4214number_clone (internal_value (mp_tracing_online), three_t);
4215#endif
4216
4217@ Well, we do have to list the names one more time, for use in symbolic
4218printouts.
4219
4220@<Initialize table...@>=
4221set_internal_name (mp_tracing_titles, xstrdup ("tracingtitles"));
4222set_internal_name (mp_tracing_equations, xstrdup ("tracingequations"));
4223set_internal_name (mp_tracing_capsules, xstrdup ("tracingcapsules"));
4224set_internal_name (mp_tracing_choices, xstrdup ("tracingchoices"));
4225set_internal_name (mp_tracing_specs, xstrdup ("tracingspecs"));
4226set_internal_name (mp_tracing_commands, xstrdup ("tracingcommands"));
4227set_internal_name (mp_tracing_restores, xstrdup ("tracingrestores"));
4228set_internal_name (mp_tracing_macros, xstrdup ("tracingmacros"));
4229set_internal_name (mp_tracing_output, xstrdup ("tracingoutput"));
4230set_internal_name (mp_tracing_stats, xstrdup ("tracingstats"));
4231set_internal_name (mp_tracing_lost_chars, xstrdup ("tracinglostchars"));
4232set_internal_name (mp_tracing_online, xstrdup ("tracingonline"));
4233set_internal_name (mp_year, xstrdup ("year"));
4234set_internal_name (mp_month, xstrdup ("month"));
4235set_internal_name (mp_day, xstrdup ("day"));
4236set_internal_name (mp_time, xstrdup ("time"));
4237set_internal_name (mp_hour, xstrdup ("hour"));
4238set_internal_name (mp_minute, xstrdup ("minute"));
4239set_internal_name (mp_char_code, xstrdup ("charcode"));
4240set_internal_name (mp_char_ext, xstrdup ("charext"));
4241set_internal_name (mp_char_wd, xstrdup ("charwd"));
4242set_internal_name (mp_char_ht, xstrdup ("charht"));
4243set_internal_name (mp_char_dp, xstrdup ("chardp"));
4244set_internal_name (mp_char_ic, xstrdup ("charic"));
4245set_internal_name (mp_design_size, xstrdup ("designsize"));
4246set_internal_name (mp_pausing, xstrdup ("pausing"));
4247set_internal_name (mp_showstopping, xstrdup ("showstopping"));
4248set_internal_name (mp_fontmaking, xstrdup ("fontmaking"));
4249set_internal_name (mp_linejoin, xstrdup ("linejoin"));
4250set_internal_name (mp_linecap, xstrdup ("linecap"));
4251set_internal_name (mp_miterlimit, xstrdup ("miterlimit"));
4252set_internal_name (mp_warning_check, xstrdup ("warningcheck"));
4253set_internal_name (mp_boundary_char, xstrdup ("boundarychar"));
4254set_internal_name (mp_prologues, xstrdup ("prologues"));
4255set_internal_name (mp_true_corners, xstrdup ("truecorners"));
4256set_internal_name (mp_default_color_model, xstrdup ("defaultcolormodel"));
4257set_internal_name (mp_procset, xstrdup ("mpprocset"));
4258set_internal_name (mp_gtroffmode, xstrdup ("troffmode"));
4259set_internal_name (mp_restore_clip_color, xstrdup ("restoreclipcolor"));
4260set_internal_name (mp_output_template, xstrdup ("outputtemplate"));
4261set_internal_name (mp_output_filename, xstrdup ("outputfilename"));
4262set_internal_name (mp_output_format, xstrdup ("outputformat"));
4263set_internal_name (mp_output_format_options, xstrdup ("outputformatoptions"));
4264set_internal_name (mp_job_name, xstrdup ("jobname"));
4265set_internal_name (mp_number_system, xstrdup ("numbersystem"));
4266set_internal_name (mp_number_precision, xstrdup ("numberprecision"));
4267set_internal_name (mp_hppp, xstrdup ("hppp"));
4268set_internal_name (mp_vppp, xstrdup ("vppp"));
4269
4270@ The following procedure, which is called just before \MP\ initializes its
4271input and output, establishes the initial values of the date and time.
4272@^system dependencies@>
4273
4274Note that the values are |scaled| integers. Hence \MP\ can no longer
4275be used after the year 32767.
4276
4277@c
4278static void mp_fix_date_and_time (MP mp) {
4279  time_t aclock = time ((time_t *) 0);
4280  struct tm *tmptr = localtime (&aclock);
4281  set_internal_from_number (mp_time, unity_t);
4282  number_multiply_int (internal_value(mp_time), (tmptr->tm_hour * 60 + tmptr->tm_min));
4283  set_internal_from_number (mp_hour, unity_t);
4284  number_multiply_int (internal_value(mp_hour), (tmptr->tm_hour));
4285  set_internal_from_number (mp_minute, unity_t);
4286  number_multiply_int (internal_value(mp_minute), (tmptr->tm_min));
4287  set_internal_from_number (mp_day, unity_t);
4288  number_multiply_int (internal_value(mp_day), (tmptr->tm_mday));
4289  set_internal_from_number (mp_month, unity_t);
4290  number_multiply_int (internal_value(mp_month), (tmptr->tm_mon + 1));
4291  set_internal_from_number (mp_year, unity_t);
4292  number_multiply_int (internal_value(mp_year), (tmptr->tm_year + 1900));
4293}
4294
4295
4296@ @<Declarations@>=
4297static void mp_fix_date_and_time (MP mp);
4298
4299@ \MP\ is occasionally supposed to print diagnostic information that
4300goes only into the transcript file, unless |mp_tracing_online| is positive.
4301Now that we have defined |mp_tracing_online| we can define
4302two routines that adjust the destination of print commands:
4303
4304@<Declarations@>=
4305static void mp_begin_diagnostic (MP mp);
4306static void mp_end_diagnostic (MP mp, boolean blank_line);
4307static void mp_print_diagnostic (MP mp, const char *s, const char *t,
4308                                 boolean nuline);
4309
4310@ @<Basic printing...@>=
4311void mp_begin_diagnostic (MP mp) {                               /* prepare to do some tracing */
4312  mp->old_setting = mp->selector;
4313  if (number_nonpositive(internal_value (mp_tracing_online))
4314      && (mp->selector == term_and_log)) {
4315    decr (mp->selector);
4316    if (mp->history == mp_spotless)
4317      mp->history = mp_warning_issued;
4318  }
4319}
4320@#
4321void mp_end_diagnostic (MP mp, boolean blank_line) {
4322  /* restore proper conditions after tracing */
4323  mp_print_nl (mp, "");
4324  if (blank_line)
4325    mp_print_ln (mp);
4326  mp->selector = mp->old_setting;
4327}
4328
4329
4330@
4331
4332@<Glob...@>=
4333unsigned int old_setting;
4334
4335@ We will occasionally use |begin_diagnostic| in connection with line-number
4336printing, as follows. (The parameter |s| is typically |"Path"| or
4337|"Cycle spec"|, etc.)
4338
4339@<Basic printing...@>=
4340void mp_print_diagnostic (MP mp, const char *s, const char *t, boolean nuline) {
4341  mp_begin_diagnostic (mp);
4342  if (nuline)
4343    mp_print_nl (mp, s);
4344  else
4345    mp_print (mp, s);
4346  mp_print (mp, " at line ");
4347  mp_print_int (mp, mp_true_line (mp));
4348  mp_print (mp, t);
4349  mp_print_char (mp, xord (':'));
4350}
4351
4352
4353@ The 256 |ASCII_code| characters are grouped into classes by means of
4354the |char_class| table. Individual class numbers have no semantic
4355or syntactic significance, except in a few instances defined here.
4356There's also |max_class|, which can be used as a basis for additional
4357class numbers in nonstandard extensions of \MP.
4358
4359@d digit_class 0 /* the class number of \.{0123456789} */
4360@d period_class 1 /* the class number of `\..' */
4361@d space_class 2 /* the class number of spaces and nonstandard characters */
4362@d percent_class 3 /* the class number of `\.\%' */
4363@d string_class 4 /* the class number of `\."' */
4364@d right_paren_class 8 /* the class number of `\.)' */
4365@d isolated_classes 5: case 6: case 7: case 8 /* characters that make length-one tokens only */
4366@d letter_class 9 /* letters and the underline character */
4367@d mp_left_bracket_class 17 /* `\.[' */
4368@d mp_right_bracket_class 18 /* `\.]' */
4369@d invalid_class 20 /* bad character in the input */
4370@d max_class 20 /* the largest class number */
4371
4372@<Glob...@>=
4373#define digit_class 0 /* the class number of \.{0123456789} */
4374int char_class[256];    /* the class numbers */
4375
4376@ If changes are made to accommodate non-ASCII character sets, they should
4377follow the guidelines in Appendix~C of {\sl The {\logos METAFONT\/}book}.
4378@:METAFONTbook}{\sl The {\logos METAFONT\/}book@>
4379@^system dependencies@>
4380
4381@<Set initial ...@>=
4382for (k = '0'; k <= '9'; k++)
4383  mp->char_class[k] = digit_class;
4384mp->char_class['.'] = period_class;
4385mp->char_class[' '] = space_class;
4386mp->char_class['%'] = percent_class;
4387mp->char_class['"'] = string_class;
4388mp->char_class[','] = 5;
4389mp->char_class[';'] = 6;
4390mp->char_class['('] = 7;
4391mp->char_class[')'] = right_paren_class;
4392for (k = 'A'; k <= 'Z'; k++)
4393  mp->char_class[k] = letter_class;
4394for (k = 'a'; k <= 'z'; k++)
4395  mp->char_class[k] = letter_class;
4396mp->char_class['_'] = letter_class;
4397mp->char_class['<'] = 10;
4398mp->char_class['='] = 10;
4399mp->char_class['>'] = 10;
4400mp->char_class[':'] = 10;
4401mp->char_class['|'] = 10;
4402mp->char_class['`'] = 11;
4403mp->char_class['\''] = 11;
4404mp->char_class['+'] = 12;
4405mp->char_class['-'] = 12;
4406mp->char_class['/'] = 13;
4407mp->char_class['*'] = 13;
4408mp->char_class['\\'] = 13;
4409mp->char_class['!'] = 14;
4410mp->char_class['?'] = 14;
4411mp->char_class['#'] = 15;
4412mp->char_class['&'] = 15;
4413mp->char_class['@@'] = 15;
4414mp->char_class['$'] = 15;
4415mp->char_class['^'] = 16;
4416mp->char_class['~'] = 16;
4417mp->char_class['['] = mp_left_bracket_class;
4418mp->char_class[']'] = mp_right_bracket_class;
4419mp->char_class['{'] = 19;
4420mp->char_class['}'] = 19;
4421for (k = 0; k < ' '; k++)
4422  mp->char_class[k] = invalid_class;
4423mp->char_class['\t'] = space_class;
4424mp->char_class['\f'] = space_class;
4425for (k = 127; k <= 255; k++)
4426  mp->char_class[k] = invalid_class;
4427
4428@* The hash table.
4429
4430Symbolic tokens are stored in and retrieved from an AVL tree. This
4431is not as fast as an actual hash table, but it is easily extensible.
4432
4433A symbolic token contains a pointer to the |mp_string| that
4434contains the string representation of the symbol, a |halfword|
4435that holds the current command value of the token, and an
4436|mp_value| for the associated equivalent.
4437
4438@d set_text(A)     do {
4439   FUNCTION_TRACE3 ("set_text(%p, %p)\n",(A),(B));
4440   (A)->text=(B) ;
4441} while (0)
4442
4443@d set_eq_type(A,B)  do {
4444   FUNCTION_TRACE3 ("set_eq_type(%p, %d)\n",(A),(B));
4445   (A)->type=(B) ;
4446} while (0)
4447
4448@d set_equiv(A,B)  do {
4449   FUNCTION_TRACE3 ("set_equiv(%p, %d)\n",(A),(B));
4450   (A)->v.data.node=NULL ;
4451   (A)->v.data.indep.serial=(B);
4452} while (0)
4453
4454@d set_equiv_node(A,B)  do {
4455   FUNCTION_TRACE3 ("set_equiv_node(%p, %p)\n",(A),(B));
4456   (A)->v.data.node=(B) ;
4457   (A)->v.data.indep.serial=0;
4458} while (0)
4459
4460@d set_equiv_sym(A,B)  do {
4461   FUNCTION_TRACE3 ("set_equiv_sym(%p, %p)\n",(A),(B));
4462   (A)->v.data.node=(mp_node)(B);
4463   (A)->v.data.indep.serial=0;
4464} while (0)
4465
4466@ @c
4467#if DEBUG
4468#define text(A)         do_get_text(mp, (A))
4469#define eq_type(A)      do_get_eq_type(mp, (A))
4470#define equiv(A)        do_get_equiv(mp, (A))
4471#define equiv_node(A)   do_get_equiv_node(mp, (A))
4472#define equiv_sym(A)    do_get_equiv_sym(mp, (A))
4473static mp_string do_get_text (MP mp, mp_sym A) {
4474  FUNCTION_TRACE3 ("%d = do_get_text(%p)\n",A->text,A);
4475  return A->text;
4476}
4477static halfword do_get_eq_type (MP mp, mp_sym A) {
4478  FUNCTION_TRACE3 ("%d = do_get_eq_type(%p)\n",A->type,A);
4479  return A->type;
4480}
4481static halfword do_get_equiv (MP mp, mp_sym A) {
4482  FUNCTION_TRACE3 ("%d = do_get_equiv(%p)\n",A->v.data.indep.serial,A);
4483  return A->v.data.indep.serial;
4484}
4485static mp_node do_get_equiv_node (MP mp, mp_sym A) {
4486  FUNCTION_TRACE3 ("%p = do_get_equiv_node(%p)\n",A->v.data.node,A);
4487  return A->v.data.node;
4488}
4489static mp_sym do_get_equiv_sym (MP mp, mp_sym A) {
4490  FUNCTION_TRACE3 ("%p = do_get_equiv_sym(%p)\n",A->v.data.node,A);
4491  return (mp_sym)A->v.data.node;
4492}
4493#else
4494#define text(A)         (A)->text
4495#define eq_type(A)      (A)->type
4496#define equiv(A)        (A)->v.data.indep.serial
4497#define equiv_node(A)   (A)->v.data.node
4498#define equiv_sym(A)    (mp_sym)(A)->v.data.node
4499#endif
4500
4501@ @<Declarations...@>=
4502#if DEBUG
4503static mp_string do_get_text (MP mp, mp_sym A);
4504static halfword do_get_eq_type (MP mp, mp_sym A);
4505static halfword do_get_equiv (MP mp, mp_sym A);
4506static mp_node do_get_equiv_node (MP mp, mp_sym A);
4507static mp_sym do_get_equiv_sym (MP mp, mp_sym A);
4508#endif
4509
4510@ @<Types...@>=
4511typedef struct mp_symbol_entry {
4512  halfword type;
4513  mp_value v;
4514  mp_string text;
4515  void *parent;
4516} mp_symbol_entry;
4517
4518@ @<Glob...@>=
4519integer st_count;       /* total number of known identifiers */
4520avl_tree symbols;       /* avl tree of symbolic tokens */
4521avl_tree frozen_symbols;        /* avl tree of frozen symbolic tokens */
4522mp_sym frozen_bad_vardef;
4523mp_sym frozen_colon;
4524mp_sym frozen_end_def;
4525mp_sym frozen_end_for;
4526mp_sym frozen_end_group;
4527mp_sym frozen_etex;
4528mp_sym frozen_fi;
4529mp_sym frozen_inaccessible;
4530mp_sym frozen_left_bracket;
4531mp_sym frozen_mpx_break;
4532mp_sym frozen_repeat_loop;
4533mp_sym frozen_right_delimiter;
4534mp_sym frozen_semicolon;
4535mp_sym frozen_slash;
4536mp_sym frozen_undefined;
4537mp_sym frozen_dump;
4538
4539
4540@ Here are the functions needed for the avl construction.
4541
4542@<Declarations@>=
4543static int comp_symbols_entry (void *p, const void *pa, const void *pb);
4544static void *copy_symbols_entry (const void *p);
4545static void *delete_symbols_entry (void *p);
4546
4547
4548@ The avl comparison function is a straightword version of |strcmp|,
4549except that checks for the string lengths first.
4550
4551@c
4552static int comp_symbols_entry (void *p, const void *pa, const void *pb) {
4553  const mp_symbol_entry *a = (const mp_symbol_entry *) pa;
4554  const mp_symbol_entry *b = (const mp_symbol_entry *) pb;
4555  (void) p;
4556  if (a->text->len != b->text->len) {
4557    return (a->text->len > b->text->len ? 1 : -1);
4558  }
4559  return strncmp ((const char *) a->text->str, (const char *) b->text->str,
4560                  a->text->len);
4561}
4562
4563
4564@ Copying a symbol happens when an item is inserted into an AVL tree.
4565The |text| and |mp_number| needs to be deep copied, every thing else
4566can be reassigned.
4567
4568@c
4569static void *copy_symbols_entry (const void *p) {
4570  MP mp;
4571  mp_sym ff;
4572  const mp_symbol_entry *fp;
4573  fp = (const mp_symbol_entry *) p;
4574  mp = (MP)fp->parent;
4575  ff = malloc (sizeof (mp_symbol_entry));
4576  if (ff == NULL)
4577    return NULL;
4578  ff->text = copy_strings_entry (fp->text);
4579  if (ff->text == NULL)
4580    return NULL;
4581  ff->v = fp->v;
4582  ff->type = fp->type;
4583  ff->parent = mp;
4584  new_number(ff->v.data.n);
4585  number_clone(ff->v.data.n, fp->v.data.n);
4586  return ff;
4587}
4588
4589
4590@ In the current implementation, symbols are not freed until the
4591end of the run.
4592
4593@c
4594static void *delete_symbols_entry (void *p) {
4595  MP mp;
4596  mp_sym ff = (mp_sym) p;
4597  mp = (MP)ff->parent;
4598  free_number(ff->v.data.n);
4599  mp_xfree (ff->text->str);
4600  mp_xfree (ff->text);
4601  mp_xfree (ff);
4602  return NULL;
4603}
4604
4605
4606@ @<Allocate or initialize ...@>=
4607mp->symbols = avl_create (comp_symbols_entry,
4608                          copy_symbols_entry,
4609                          delete_symbols_entry, malloc, free, NULL);
4610mp->frozen_symbols = avl_create (comp_symbols_entry,
4611                                 copy_symbols_entry,
4612                                 delete_symbols_entry, malloc, free, NULL);
4613
4614@ @<Dealloc variables@>=
4615if (mp->symbols != NULL)
4616  avl_destroy (mp->symbols);
4617if (mp->frozen_symbols != NULL)
4618  avl_destroy (mp->frozen_symbols);
4619
4620@ Actually creating symbols is done by |id_lookup|, but in order to
4621do so it needs a way to create a new, empty symbol structure.
4622
4623@<Declarations@>=
4624static mp_sym new_symbols_entry (MP mp, unsigned char *nam, size_t len);
4625
4626@ @c
4627static mp_sym new_symbols_entry (MP mp, unsigned char *nam, size_t len) {
4628  mp_sym ff;
4629  ff = mp_xmalloc (mp, 1, sizeof (mp_symbol_entry));
4630  memset (ff, 0, sizeof (mp_symbol_entry));
4631  ff->parent = mp;
4632  ff->text = mp_xmalloc (mp, 1, sizeof (mp_lstring));
4633  ff->text->str = nam;
4634  ff->text->len = len;
4635  ff->type = mp_tag_token;
4636  ff->v.type = mp_known;
4637  new_number(ff->v.data.n);
4638  FUNCTION_TRACE4 ("%p = new_symbols_entry(\"%s\",%d)\n", ff, nam, (int)len);
4639  return ff;
4640}
4641
4642
4643@ There is one global variable so that |id_lookup| does not always have to
4644create a new entry just for testing. This is not freed because it creates
4645a double-free thanks to the |NULL| init.
4646
4647@<Global ...@>=
4648mp_sym id_lookup_test;
4649
4650@ @<Initialize table entries@>=
4651mp->id_lookup_test = new_symbols_entry (mp, NULL, 0);
4652
4653@ Certain symbols are ``frozen'' and not redefinable, since they are
4654used
4655in error recovery.
4656
4657@<Initialize table entries@>=
4658mp->st_count = 0;
4659mp->frozen_bad_vardef = mp_frozen_primitive (mp, "a bad variable", mp_tag_token, 0);
4660mp->frozen_right_delimiter = mp_frozen_primitive (mp, ")", mp_right_delimiter, 0);
4661mp->frozen_inaccessible = mp_frozen_primitive (mp, " INACCESSIBLE", mp_tag_token, 0);
4662mp->frozen_undefined = mp_frozen_primitive (mp, " UNDEFINED", mp_tag_token, 0);
4663
4664@ Here is the subroutine that searches the avl tree for an identifier
4665that matches a given string of length~|l| appearing in |buffer[j..
4666(j+l-1)]|. If the identifier is not found, it is inserted if
4667|insert_new| is |true|, and the corresponding symbol will be returned.
4668
4669There are two variations on the lookup function: one for the normal
4670symbol table, and one for the table of error recovery symbols.
4671
4672@d mp_id_lookup(A,B,C,D) mp_do_id_lookup ((A), mp->symbols, (B), (C), (D))
4673
4674@c
4675static mp_sym mp_do_id_lookup (MP mp, avl_tree symbols, char *j,
4676                               size_t l, boolean insert_new) {
4677  /* search an avl tree */
4678  mp_sym str;
4679  mp->id_lookup_test->text->str = (unsigned char *)j;
4680  mp->id_lookup_test->text->len = l;
4681  str = (mp_sym) avl_find (mp->id_lookup_test, symbols);
4682  if (str == NULL && insert_new) {
4683    unsigned char *nam = (unsigned char *) mp_xstrldup (mp, j, l);
4684    mp_sym s = new_symbols_entry (mp, nam, l);
4685    mp->st_count++;
4686    assert (avl_ins (s, symbols, avl_false) > 0);
4687    str = (mp_sym) avl_find (s, symbols);
4688    delete_symbols_entry (s);
4689  }
4690  return str;
4691}
4692static mp_sym mp_frozen_id_lookup (MP mp, char *j, size_t l,
4693                                   boolean insert_new) {
4694  /* search the error recovery symbol table */
4695  return mp_do_id_lookup (mp, mp->frozen_symbols, j, l, insert_new);
4696}
4697
4698/* see mp_print_sym  (mp_sym sym) */
4699@ Get a numeric value from \MP\ is not easy. We have to consider
4700the macro and the loops, as also the internal type (this is a
4701first attempt, and more work is needed). If we are inside
4702a \&{for} loop, then the global |loop_ptr| is not null and the other loops
4703eventually nested are available by mean of |loop_ptr->link|.
4704The current numeric value is stored in |old_value|.
4705
4706@c
4707double mp_get_numeric_value (MP mp, const char *s, size_t l) {
4708    char *ss = mp_xstrdup(mp,s);
4709    if (ss) {
4710     mp_sym sym = mp_id_lookup(mp,ss,l,false);
4711     if (sym != NULL) {
4712        if (mp->loop_ptr != NULL) {
4713	   mp_loop_data *s;
4714           s = mp->loop_ptr;
4715           while (s != NULL && sym != s->var)
4716             s = mp->loop_ptr->link;
4717           if (s != NULL &&  sym == s->var ){
4718	     mp_xfree (ss);
4719             return number_to_double(s->old_value) ;
4720	   }
4721        }
4722        if (mp_type(sym) == mp_internal_quantity) {
4723          halfword qq = equiv(sym);
4724          mp_xfree (ss);
4725	  if (internal_type (qq) != mp_string_type)
4726            return number_to_double(internal_value(qq));
4727	  else
4728	    return 0;
4729        }
4730        if (sym->v.data.node != NULL && mp_type(sym->v.data.node) == mp_known) {
4731	    mp_xfree (ss);
4732            return number_to_double(sym->v.data.node->data.n) ;
4733        }
4734     }
4735    }
4736    mp_xfree (ss);
4737    return 0 ;
4738}
4739
4740int mp_get_boolean_value (MP mp, const char *s, size_t l) {
4741   char *ss = mp_xstrdup(mp,s);
4742   if (ss) {
4743    mp_sym sym = mp_id_lookup(mp,ss,l,false);
4744    if (sym != NULL) {
4745        if (mp_type(sym->v.data.node) == mp_boolean_type) {
4746            if (number_to_boolean (sym->v.data.node->data.n) == mp_true_code) {
4747 	        mp_xfree(ss);
4748                return 1 ;
4749            }
4750        }
4751     }
4752   }
4753   mp_xfree (ss);
4754   return 0;
4755}
4756
4757char *mp_get_string_value (MP mp, const char *s, size_t l) {
4758   char *ss = mp_xstrdup(mp,s);
4759   if (ss) {
4760    mp_sym sym = mp_id_lookup(mp,ss,l,false);
4761    if (sym != NULL) {
4762        if (mp_type(sym->v.data.node) == mp_string_type) {
4763	    mp_xfree (ss);
4764            return (char *) sym->v.data.node->data.str->str;
4765        }
4766    }
4767   }
4768   mp_xfree (ss);
4769   return NULL;
4770}
4771
4772@ @<Exported function headers@>=
4773double mp_get_numeric_value(MP mp,const char *s,size_t l);
4774int mp_get_boolean_value(MP mp,const char *s,size_t l);
4775char *mp_get_string_value(MP mp,const char *s,size_t l);
4776
4777@ We need to put \MP's ``primitive'' symbolic tokens into the hash
4778table, together with their command code (which will be the |eq_type|)
4779and an operand (which will be the |equiv|). The |primitive| procedure
4780does this, in a way that no \MP\ user can. The global value |cur_sym|
4781contains the new |eqtb| pointer after |primitive| has acted.
4782
4783@c
4784static void mp_primitive (MP mp, const char *ss, halfword c, halfword o) {
4785  char *s = mp_xstrdup (mp, ss);
4786  set_cur_sym (mp_id_lookup (mp, s, strlen (s), true));
4787  mp_xfree (s);
4788  set_eq_type (cur_sym(), c);
4789  set_equiv (cur_sym(), o);
4790}
4791
4792
4793@ Some other symbolic tokens only exist for error recovery.
4794
4795@c
4796static mp_sym mp_frozen_primitive (MP mp, const char *ss, halfword c,
4797                                   halfword o) {
4798  char *s = mp_xstrdup (mp, ss);
4799  mp_sym str = mp_frozen_id_lookup (mp, s, strlen (ss), true);
4800  mp_xfree (s);
4801  str->type = c;
4802  str->v.data.indep.serial = o;
4803  return str;
4804}
4805
4806
4807@ This routine returns |true| if the argument is an un-redefinable symbol
4808because it is one of the error recovery tokens (as explained elsewhere,
4809|frozen_inaccessible| actuall is redefinable).
4810
4811@c
4812static boolean mp_is_frozen (MP mp, mp_sym sym) {
4813  mp_sym temp = mp_frozen_id_lookup (mp, (char *) sym->text->str, sym->text->len, false);
4814  if (temp==mp->frozen_inaccessible)
4815    return false;
4816  return (temp == sym);
4817}
4818
4819
4820@ Many of \MP's primitives need no |equiv|, since they are identifiable
4821by their |eq_type| alone. These primitives are loaded into the hash table
4822as follows:
4823
4824@<Put each of \MP's primitives into the hash table@>=
4825mp_primitive (mp, "..", mp_path_join, 0);
4826@:.._}{\.{..} primitive@>;
4827mp_primitive (mp, "[", mp_left_bracket, 0);
4828mp->frozen_left_bracket = mp_frozen_primitive (mp, "[", mp_left_bracket, 0);
4829@:[ }{\.{[} primitive@>;
4830mp_primitive (mp, "]", mp_right_bracket, 0);
4831@:] }{\.{]} primitive@>;
4832mp_primitive (mp, "}", mp_right_brace, 0);
4833@:]]}{\.{\char`\}} primitive@>;
4834mp_primitive (mp, "{", mp_left_brace, 0);
4835@:][}{\.{\char`\{} primitive@>;
4836mp_primitive (mp, ":", mp_colon, 0);
4837mp->frozen_colon = mp_frozen_primitive (mp, ":", mp_colon, 0);
4838@:: }{\.{:} primitive@>;
4839mp_primitive (mp, "::", mp_double_colon, 0);
4840@::: }{\.{::} primitive@>;
4841mp_primitive (mp, "||:", mp_bchar_label, 0);
4842@:::: }{\.{\char'174\char'174:} primitive@>;
4843mp_primitive (mp, ":=", mp_assignment, 0);
4844@::=_}{\.{:=} primitive@>;
4845mp_primitive (mp, ",", mp_comma, 0);
4846@:, }{\., primitive@>;
4847mp_primitive (mp, ";", mp_semicolon, 0);
4848mp->frozen_semicolon = mp_frozen_primitive (mp, ";", mp_semicolon, 0);
4849@:; }{\.; primitive@>;
4850mp_primitive (mp, "\\", mp_relax, 0);
4851@:]]\\}{\.{\char`\\} primitive@>;
4852mp_primitive (mp, "addto", mp_add_to_command, 0);
4853@:add_to_}{\&{addto} primitive@>;
4854mp_primitive (mp, "atleast", mp_at_least, 0);
4855@:at_least_}{\&{atleast} primitive@>;
4856mp_primitive (mp, "begingroup", mp_begin_group, 0);
4857mp->bg_loc = cur_sym();
4858@:begin_group_}{\&{begingroup} primitive@>;
4859mp_primitive (mp, "controls", mp_controls, 0);
4860@:controls_}{\&{controls} primitive@>;
4861mp_primitive (mp, "curl", mp_curl_command, 0);
4862@:curl_}{\&{curl} primitive@>;
4863mp_primitive (mp, "delimiters", mp_delimiters, 0);
4864@:delimiters_}{\&{delimiters} primitive@>;
4865mp_primitive (mp, "endgroup", mp_end_group, 0);
4866mp->eg_loc = cur_sym();
4867mp->frozen_end_group = mp_frozen_primitive (mp, "endgroup", mp_end_group, 0);
4868@:endgroup_}{\&{endgroup} primitive@>;
4869mp_primitive (mp, "everyjob", mp_every_job_command, 0);
4870@:every_job_}{\&{everyjob} primitive@>;
4871mp_primitive (mp, "exitif", mp_exit_test, 0);
4872@:exit_if_}{\&{exitif} primitive@>;
4873mp_primitive (mp, "expandafter", mp_expand_after, 0);
4874@:expand_after_}{\&{expandafter} primitive@>;
4875mp_primitive (mp, "interim", mp_interim_command, 0);
4876@:interim_}{\&{interim} primitive@>;
4877mp_primitive (mp, "let", mp_let_command, 0);
4878@:let_}{\&{let} primitive@>;
4879mp_primitive (mp, "newinternal", mp_new_internal, 0);
4880@:new_internal_}{\&{newinternal} primitive@>;
4881mp_primitive (mp, "of", mp_of_token, 0);
4882@:of_}{\&{of} primitive@>;
4883mp_primitive (mp, "randomseed", mp_random_seed, 0);
4884@:mp_random_seed_}{\&{randomseed} primitive@>;
4885mp_primitive (mp, "save", mp_save_command, 0);
4886@:save_}{\&{save} primitive@>;
4887mp_primitive (mp, "scantokens", mp_scan_tokens, 0);
4888@:scan_tokens_}{\&{scantokens} primitive@>;
4889
4890mp_primitive (mp, "runscript", mp_runscript, 0);
4891@:run_script_}{\&{runscript} primitive@>;
4892mp_primitive (mp, "maketext", mp_maketext, 0);
4893@:make_text_}{\&{maketext} primitive@>;
4894
4895mp_primitive (mp, "shipout", mp_ship_out_command, 0);
4896@:ship_out_}{\&{shipout} primitive@>;
4897mp_primitive (mp, "skipto", mp_skip_to, 0);
4898@:skip_to_}{\&{skipto} primitive@>;
4899mp_primitive (mp, "special", mp_special_command, 0);
4900@:special}{\&{special} primitive@>;
4901mp_primitive (mp, "fontmapfile", mp_special_command, 1);
4902@:fontmapfile}{\&{fontmapfile} primitive@>;
4903mp_primitive (mp, "fontmapline", mp_special_command, 2);
4904@:fontmapline}{\&{fontmapline} primitive@>;
4905mp_primitive (mp, "step", mp_step_token, 0);
4906@:step_}{\&{step} primitive@>;
4907mp_primitive (mp, "str", mp_str_op, 0);
4908@:str_}{\&{str} primitive@>;
4909mp_primitive (mp, "tension", mp_tension, 0);
4910@:tension_}{\&{tension} primitive@>;
4911mp_primitive (mp, "to", mp_to_token, 0);
4912@:to_}{\&{to} primitive@>;
4913mp_primitive (mp, "until", mp_until_token, 0);
4914@:until_}{\&{until} primitive@>;
4915mp_primitive (mp, "within", mp_within_token, 0);
4916@:within_}{\&{within} primitive@>;
4917mp_primitive (mp, "write", mp_write_command, 0);
4918@:write_}{\&{write} primitive@>
4919
4920
4921@ Each primitive has a corresponding inverse, so that it is possible to
4922display the cryptic numeric contents of |eqtb| in symbolic form.
4923Every call of |primitive| in this program is therefore accompanied by some
4924straightforward code that forms part of the |print_cmd_mod| routine
4925explained below.
4926
4927@<Cases of |print_cmd_mod| for symbolic printing of primitives@>=
4928case mp_add_to_command:
4929mp_print (mp, "addto");
4930break;
4931case mp_assignment:
4932mp_print (mp, ":=");
4933break;
4934case mp_at_least:
4935mp_print (mp, "atleast");
4936break;
4937case mp_bchar_label:
4938mp_print (mp, "||:");
4939break;
4940case mp_begin_group:
4941mp_print (mp, "begingroup");
4942break;
4943case mp_colon:
4944mp_print (mp, ":");
4945break;
4946case mp_comma:
4947mp_print (mp, ",");
4948break;
4949case mp_controls:
4950mp_print (mp, "controls");
4951break;
4952case mp_curl_command:
4953mp_print (mp, "curl");
4954break;
4955case mp_delimiters:
4956mp_print (mp, "delimiters");
4957break;
4958case mp_double_colon:
4959mp_print (mp, "::");
4960break;
4961case mp_end_group:
4962mp_print (mp, "endgroup");
4963break;
4964case mp_every_job_command:
4965mp_print (mp, "everyjob");
4966break;
4967case mp_exit_test:
4968mp_print (mp, "exitif");
4969break;
4970case mp_expand_after:
4971mp_print (mp, "expandafter");
4972break;
4973case mp_interim_command:
4974mp_print (mp, "interim");
4975break;
4976case mp_left_brace:
4977mp_print (mp, "{");
4978break;
4979case mp_left_bracket:
4980mp_print (mp, "[");
4981break;
4982case mp_let_command:
4983mp_print (mp, "let");
4984break;
4985case mp_new_internal:
4986mp_print (mp, "newinternal");
4987break;
4988case mp_of_token:
4989mp_print (mp, "of");
4990break;
4991case mp_path_join:
4992mp_print (mp, "..");
4993break;
4994case mp_random_seed:
4995mp_print (mp, "randomseed");
4996break;
4997case mp_relax:
4998mp_print_char (mp, xord ('\\'));
4999break;
5000case mp_right_brace:
5001mp_print_char (mp, xord ('}'));
5002break;
5003case mp_right_bracket:
5004mp_print_char (mp, xord (']'));
5005break;
5006case mp_save_command:
5007mp_print (mp, "save");
5008break;
5009case mp_scan_tokens:
5010mp_print (mp, "scantokens");
5011break;
5012case mp_runscript:
5013mp_print (mp, "runscript");
5014break;
5015case mp_maketext:
5016mp_print (mp, "maketext");
5017break;
5018case mp_semicolon:
5019mp_print_char (mp, xord (';'));
5020break;
5021case mp_ship_out_command:
5022mp_print (mp, "shipout");
5023break;
5024case mp_skip_to:
5025mp_print (mp, "skipto");
5026break;
5027case mp_special_command:
5028if (m == 2)
5029  mp_print (mp, "fontmapline");
5030else if (m == 1)
5031  mp_print (mp, "fontmapfile");
5032else
5033  mp_print (mp, "special");
5034break;
5035case mp_step_token:
5036mp_print (mp, "step");
5037break;
5038case mp_str_op:
5039mp_print (mp, "str");
5040break;
5041case mp_tension:
5042mp_print (mp, "tension");
5043break;
5044case mp_to_token:
5045mp_print (mp, "to");
5046break;
5047case mp_until_token:
5048mp_print (mp, "until");
5049break;
5050case mp_within_token:
5051mp_print (mp, "within");
5052break;
5053case mp_write_command:
5054mp_print (mp, "write");
5055break;
5056
5057@ We will deal with the other primitives later, at some point in the program
5058where their |eq_type| and |equiv| values are more meaningful.  For example,
5059the primitives for macro definitions will be loaded when we consider the
5060routines that define macros. It is easy to find where each particular
5061primitive was treated by looking in the index at the end; for example, the
5062section where |"def"| entered |eqtb| is listed under `\&{def} primitive'.
5063
5064@* Token lists.
5065
5066A \MP\ token is either symbolic or numeric or a string, or it denotes a macro
5067parameter or capsule or an internal; so there are six corresponding ways to
5068encode it internally:
5069@^token@>
5070
5071(1)~A symbolic token for symbol |p| is represented by the pointer |p|,
5072in the |sym_sym| field of a symbolic node in~|mem|. The |type| field is |symbol_node|;
5073and it has a |name_type| to differentiate various subtypes of symbolic tokens,
5074which is usually |normal_sym|, but |macro_sym| for macro names.
5075
5076(2)~A numeric token whose |scaled| value is~|v| is
5077represented in a non-symbolic node of~|mem|; the |type| field is |known|,
5078the |name_type| field is |token|, and the |value| field holds~|v|.
5079
5080(3)~A string token is also represented in a non-symbolic node; the |type|
5081field is |mp_string_type|, the |name_type| field is |token|, and the
5082|value| field holds the corresponding |mp_string|.
5083
5084(4)~Capsules have |name_type=capsule|, and their |type| and |value| fields
5085represent arbitrary values, with |type| different from |symbol_node|
5086(in ways to be explained later).
5087
5088(5)~Macro parameters appear in |sym_info| fields of symbolic nodes.  The |type|
5089field is |symbol_node|; the $k$th parameter is represented by |k| in |sym_info|;
5090and |expr_sym| in |name_type|, if it is of type \&{expr}, or |suffix_sym| if it
5091is of type \&{suffix}, or by |text_sym| if it is of type \&{text}.
5092
5093(6)~The $k$th internal is also represented by |k| in |sym_info|; the |type| field is
5094|symbol_node| as for the other symbolic tokens; and |internal_sym| is its |name_type|;
5095
5096Actual values of the parameters and internals are kept in a separate
5097stack, as we will see later.
5098
5099Note that the `\\{type}' field of a node has nothing to do with ``type'' in a
5100printer's sense. It's curious that the same word is used in such different ways.
5101
5102@d token_node_size sizeof(mp_node_data) /* the number of words in a large token node */
5103
5104@d set_value_sym(A,B) do_set_value_sym(mp, (mp_token_node)(A), (B))
5105@d set_value_number(A,B) do_set_value_number(mp, (mp_token_node)(A), (B))
5106@d set_value_node(A,B) do_set_value_node(mp, (mp_token_node)(A), (B))
5107@d set_value_str(A,B) do_set_value_str(mp, (mp_token_node)(A), (B))
5108@d set_value_knot(A,B) do_set_value_knot(mp, (mp_token_node)A, (B))
5109
5110@d value_sym_NEW(A) (mp_sym)mp_link(A)
5111@d set_value_sym_NEW(A,B) set_mp_link(A,(mp_node)B)
5112
5113@<MPlib internal header stuff@>=
5114typedef struct mp_node_data *mp_token_node;
5115
5116@ @c
5117#if DEBUG
5118#define value_sym(A)    do_get_value_sym(mp,(mp_token_node)(A))
5119/* |#define value_number(A) do_get_value_number(mp,(mp_token_node)(A))| */
5120#define value_number(A) ((mp_token_node)(A))->data.n
5121#define value_node(A)   do_get_value_node(mp,(mp_token_node)(A))
5122#define value_str(A)    do_get_value_str(mp,(mp_token_node)(A))
5123#define value_knot(A)   do_get_value_knot(mp,(mp_token_node)(A))
5124#else
5125#define value_sym(A)    ((mp_token_node)(A))->data.sym
5126#define value_number(A) ((mp_token_node)(A))->data.n
5127#define value_node(A)   ((mp_token_node)(A))->data.node
5128#define value_str(A)    ((mp_token_node)(A))->data.str
5129#define value_knot(A)   ((mp_token_node)(A))->data.p
5130#endif
5131static void do_set_value_sym(MP mp, mp_token_node A, mp_sym B) {
5132   FUNCTION_TRACE3 ("set_value_sym(%p,%p)\n", (A),(B));
5133   A->data.sym=(B);
5134}
5135static void do_set_value_number(MP mp, mp_token_node A, mp_number B) {
5136   FUNCTION_TRACE3 ("set_value(%p,%s)\n", (A), number_tostring(B));
5137   A->data.p = NULL;
5138   A->data.str = NULL;
5139   A->data.node = NULL;
5140   number_clone (A->data.n, B);
5141}
5142static void do_set_value_str(MP mp, mp_token_node A, mp_string B) {
5143   FUNCTION_TRACE3 ("set_value_str(%p,%p)\n", (A),(B));
5144   assert (A->type != mp_structured);
5145   A->data.p = NULL;
5146   A->data.str = (B);
5147   add_str_ref((B));
5148   A->data.node = NULL;
5149   number_clone (A->data.n, zero_t);
5150}
5151static void do_set_value_node(MP mp, mp_token_node A, mp_node B) {
5152   /* store the value in a large token node */
5153   FUNCTION_TRACE3 ("set_value_node(%p,%p)\n", A,B);
5154   assert (A->type != mp_structured);
5155   A->data.p = NULL;
5156   A->data.str = NULL;
5157   A->data.node = B;
5158   number_clone (A->data.n, zero_t);
5159}
5160static void do_set_value_knot(MP mp, mp_token_node A, mp_knot B) {
5161   FUNCTION_TRACE3 ("set_value_knot(%p,%p)\n", (A),(B));
5162   assert (A->type != mp_structured);
5163   A->data.p = (B);
5164   A->data.str = NULL;
5165   A->data.node = NULL;
5166   number_clone (A->data.n, zero_t);
5167}
5168
5169
5170@ @c
5171#if DEBUG
5172static mp_sym do_get_value_sym (MP mp, mp_token_node A) {
5173  /* |A->type| can be structured in this case */
5174  FUNCTION_TRACE3 ("%p = get_value_sym(%p)\n", A->data.sym, A);
5175  return A->data.sym ;
5176}
5177static mp_node do_get_value_node (MP mp, mp_token_node A) {
5178  assert (A->type != mp_structured);
5179  FUNCTION_TRACE3 ("%p = get_value_node(%p)\n", A->data.node, A);
5180  return  A->data.node ;
5181}
5182static mp_string do_get_value_str (MP mp, mp_token_node A) {
5183  assert (A->type != mp_structured);
5184  FUNCTION_TRACE3 ("%p = get_value_str(%p)\n", A->data.str, A);
5185  return  A->data.str ;
5186}
5187static mp_knot do_get_value_knot (MP mp, mp_token_node A) {
5188  assert (A->type != mp_structured);
5189  FUNCTION_TRACE3 ("%p = get_value_knot(%p)\n", A->data.p, A);
5190  return  A->data.p ;
5191}
5192static mp_number do_get_value_number (MP mp, mp_token_node A) {
5193  assert (A->type != mp_structured);
5194  FUNCTION_TRACE3 ("%d = get_value_number(%p)\n", A->data.n.type, A);
5195  return  A->data.n ;
5196}
5197#endif
5198
5199@ @<Declarations@>=
5200#if DEBUG
5201static mp_number do_get_value_number (MP mp, mp_token_node A);
5202static mp_sym    do_get_value_sym    (MP mp, mp_token_node A);
5203static mp_node   do_get_value_node   (MP mp, mp_token_node A);
5204static mp_string do_get_value_str    (MP mp, mp_token_node A) ;
5205static mp_knot   do_get_value_knot   (MP mp, mp_token_node A) ;
5206#endif
5207static void do_set_value_sym    (MP mp, mp_token_node A, mp_sym B);
5208static void do_set_value_number (MP mp, mp_token_node A, mp_number B);
5209static void do_set_value_node   (MP mp, mp_token_node A, mp_node B);
5210static void do_set_value_str    (MP mp, mp_token_node A, mp_string B);
5211static void do_set_value_knot   (MP mp, mp_token_node A, mp_knot B);
5212
5213@
5214@c
5215static mp_node mp_get_token_node (MP mp) {
5216  mp_node p;
5217  if (mp->token_nodes) {
5218    p = mp->token_nodes;
5219    mp->token_nodes = p->link;
5220    mp->num_token_nodes--;
5221    p->link = NULL;
5222  } else {
5223    p = malloc_node (token_node_size);
5224    new_number(p->data.n);
5225    p->has_number = 1;
5226  }
5227  p->type = mp_token_node_type;
5228  FUNCTION_TRACE2 ("%p = mp_get_token_node()\n", p);
5229  return (mp_node) p;
5230}
5231
5232@ @c
5233static void mp_free_token_node (MP mp, mp_node p) {
5234  FUNCTION_TRACE2 ("mp_free_token_node(%p)\n", p);
5235  if (!p) return;
5236  if (mp->num_token_nodes < max_num_token_nodes) {
5237    p->link = mp->token_nodes;
5238    mp->token_nodes = p;
5239    mp->num_token_nodes++;
5240    return;
5241  }
5242  mp->var_used -= token_node_size;
5243  if (mp->math_mode > mp_math_double_mode) {
5244    free_number(((mp_value_node)p)->data.n);
5245  }
5246  xfree (p);
5247}
5248
5249@ @<Declarations@>=
5250static void mp_free_token_node (MP mp, mp_node p);
5251
5252@ A numeric token is created by the following trivial routine.
5253
5254@c
5255static mp_node mp_new_num_tok (MP mp, mp_number v) {
5256  mp_node p;    /* the new node */
5257  p = mp_get_token_node (mp);
5258  set_value_number (p, v);
5259  p->type = mp_known;
5260  p->name_type = mp_token;
5261  FUNCTION_TRACE3 ("%p = mp_new_num_tok(%p)\n", p, v);
5262  return p;
5263}
5264
5265
5266@ A token list is a singly linked list of nodes in |mem|, where
5267each node contains a token and a link.  Here's a subroutine that gets rid
5268of a token list when it is no longer needed.
5269
5270@c
5271static void mp_flush_token_list (MP mp, mp_node p) {
5272  mp_node q;    /* the node being recycled */
5273  FUNCTION_TRACE2 ("mp_flush_token_list(%p)\n", p);
5274  while (p != NULL) {
5275    q = p;
5276    p = mp_link (p);
5277    if (mp_type (q) == mp_symbol_node) {
5278      mp_free_symbolic_node (mp, q);
5279    } else {
5280      switch (mp_type (q)) {
5281      case mp_vacuous:
5282      case mp_boolean_type:
5283      case mp_known:
5284        break;
5285      case mp_string_type:
5286        delete_str_ref (value_str (q));
5287        break;
5288      case unknown_types:
5289      case mp_pen_type:
5290      case mp_path_type:
5291      case mp_picture_type:
5292      case mp_pair_type:
5293      case mp_color_type:
5294      case mp_cmykcolor_type:
5295      case mp_transform_type:
5296      case mp_dependent:
5297      case mp_proto_dependent:
5298      case mp_independent:
5299        mp_recycle_value (mp, q);
5300        break;
5301      default:
5302        mp_confusion (mp, "token");
5303@:this can't happen token}{\quad token@>;
5304      }
5305      mp_free_token_node (mp, q);
5306    }
5307  }
5308}
5309
5310
5311@ The procedure |show_token_list|, which prints a symbolic form of
5312the token list that starts at a given node |p|, illustrates these
5313conventions. The token list being displayed should not begin with a reference
5314count.
5315
5316An additional parameter |q| is also given; this parameter is either NULL
5317or it points to a node in the token list where a certain magic computation
5318takes place that will be explained later. (Basically, |q| is non-NULL when
5319we are printing the two-line context information at the time of an error
5320message; |q| marks the place corresponding to where the second line
5321should begin.)
5322
5323The generation will stop, and `\.{\char`\ ETC.}' will be printed, if the length
5324of printing exceeds a given limit~|l|; the length of printing upon entry is
5325assumed to be a given amount called |null_tally|. (Note that
5326|show_token_list| sometimes uses itself recursively to print
5327variable names within a capsule.)
5328@^recursion@>
5329
5330Unusual entries are printed in the form of all-caps tokens
5331preceded by a space, e.g., `\.{\char`\ BAD}'.
5332
5333@<Declarations@>=
5334static void mp_show_token_list (MP mp, mp_node p, mp_node q, integer l,
5335                                integer null_tally);
5336
5337@ @c
5338void mp_show_token_list (MP mp, mp_node p, mp_node q, integer l,
5339                         integer null_tally) {
5340  quarterword cclass, c; /* the |char_class| of previous and new tokens */
5341  cclass = percent_class;
5342  mp->tally = null_tally;
5343  while ((p != NULL) && (mp->tally < l)) {
5344    if (p == q) {
5345      set_trick_count();
5346    }
5347    /* Display token |p| and set |c| to its class; but |return| if there are problems */
5348    c = letter_class;               /* the default */
5349    if (mp_type (p) != mp_symbol_node) {
5350      /* Display non-symbolic token */
5351      if (mp_name_type (p) == mp_token) {
5352        if (mp_type (p) == mp_known) {
5353          /* Display a numeric token */
5354          if (cclass == digit_class)
5355            mp_print_char (mp, xord (' '));
5356          if (number_negative (value_number (p))) {
5357            if (cclass == mp_left_bracket_class)
5358              mp_print_char (mp, xord (' '));
5359            mp_print_char (mp, xord ('['));
5360            print_number (value_number (p));
5361            mp_print_char (mp, xord (']'));
5362            c = mp_right_bracket_class;
5363          } else {
5364            print_number (value_number (p));
5365            c = digit_class;
5366          }
5367
5368        } else if (mp_type (p) != mp_string_type) {
5369          mp_print (mp, " BAD");
5370        } else {
5371          mp_print_char (mp, xord ('"'));
5372          mp_print_str (mp, value_str (p));
5373          mp_print_char (mp, xord ('"'));
5374          c = string_class;
5375        }
5376      } else if ((mp_name_type (p) != mp_capsule) || (mp_type (p) < mp_vacuous)
5377                 || (mp_type (p) > mp_independent)) {
5378        mp_print (mp, " BAD");
5379      } else {
5380        mp_print_capsule (mp, p);
5381        c = right_paren_class;
5382      }
5383
5384    } else {
5385      if (mp_name_type (p) == mp_expr_sym ||
5386          mp_name_type (p) == mp_suffix_sym || mp_name_type (p) == mp_text_sym) {
5387        integer r; /* temporary register */
5388        r = mp_sym_info (p);
5389        if (mp_name_type (p) == mp_expr_sym) {
5390          mp_print (mp, "(EXPR");
5391        } else if (mp_name_type (p) == mp_suffix_sym) {
5392          mp_print (mp, "(SUFFIX");
5393        } else {
5394          mp_print (mp, "(TEXT");
5395        }
5396        mp_print_int (mp, r);
5397        mp_print_char (mp, xord (')'));
5398        c = right_paren_class;
5399      } else {
5400        mp_sym sr = mp_sym_sym (p);
5401        if (sr == collective_subscript) {
5402          /* Display a collective subscript */
5403          if (cclass == mp_left_bracket_class)
5404            mp_print_char (mp, xord (' '));
5405          mp_print (mp, "[]");
5406          c = mp_right_bracket_class;
5407
5408        } else {
5409          mp_string rr = text (sr);
5410          if (rr == NULL || rr->str == NULL) {
5411            mp_print (mp, " NONEXISTENT");
5412          } else {
5413            /* Print string |r| as a symbolic token and set |c| to its class */
5414            c = (quarterword) mp->char_class[(rr->str[0])];
5415            if (c == cclass) {
5416              switch (c) {
5417              case letter_class:
5418                mp_print_char (mp, xord ('.'));
5419                break;
5420              case isolated_classes:
5421                break;
5422              default:
5423                mp_print_char (mp, xord (' '));
5424                break;
5425              }
5426            }
5427            mp_print_str (mp, rr);
5428
5429          }
5430        }
5431      }
5432    }
5433
5434    cclass = c;
5435    p = mp_link (p);
5436  }
5437  if (p != NULL)
5438    mp_print (mp, " ETC.");
5439  return;
5440}
5441
5442
5443@ @<Declarations@>=
5444static void mp_print_capsule (MP mp, mp_node p);
5445
5446@ @<Declare miscellaneous procedures that were declared |forward|@>=
5447void mp_print_capsule (MP mp, mp_node p) {
5448  mp_print_char (mp, xord ('('));
5449  mp_print_exp (mp, p, 0);
5450  mp_print_char (mp, xord (')'));
5451}
5452
5453
5454@ Macro definitions are kept in \MP's memory in the form of token lists
5455that have a few extra symbolic nodes at the beginning.
5456
5457The first node contains a reference count that is used to tell when the
5458list is no longer needed. To emphasize the fact that a reference count is
5459present, we shall refer to the |sym_info| field of this special node as the
5460|ref_count| field.
5461@^reference counts@>
5462
5463The next node or nodes after the reference count serve to describe the
5464formal parameters. They consist of zero or more parameter tokens followed
5465by a code for the type of macro.
5466
5467/* reference count preceding a macro definition or picture header */
5468@d ref_count(A) indep_value(A)
5469@d set_ref_count(A,B) set_indep_value(A,B)
5470@d add_mac_ref(A)  set_ref_count((A),ref_count((A))+1) /* make a new reference to a macro list */
5471@d decr_mac_ref(A) set_ref_count((A),ref_count((A))-1) /* remove a reference to a macro list */
5472
5473@<Types...@>=
5474typedef enum {
5475 mp_general_macro, /* preface to a macro defined with a parameter list */
5476 mp_primary_macro, /* preface to a macro with a \&{primary} parameter */
5477 mp_secondary_macro, /* preface to a macro with a \&{secondary} parameter */
5478 mp_tertiary_macro, /* preface to a macro with a \&{tertiary} parameter */
5479 mp_expr_macro, /* preface to a macro with an undelimited \&{expr} parameter */
5480 mp_of_macro, /* preface to a macro with undelimited `\&{expr} |x| \&{of}~|y|' parameters */
5481 mp_suffix_macro, /* preface to a macro with an undelimited \&{suffix} parameter */
5482 mp_text_macro, /* preface to a macro with an undelimited \&{text} parameter */
5483 mp_expr_param, /* used by \.{expr} primitive */
5484 mp_suffix_param, /* used by \.{suffix} primitive */
5485 mp_text_param /* used by \.{text} primitive */
5486} mp_macro_info;
5487
5488@ @c
5489static void mp_delete_mac_ref (MP mp, mp_node p) {
5490  /* |p| points to the reference count of a macro list that is
5491     losing one reference */
5492  if (ref_count (p) == 0)
5493    mp_flush_token_list (mp, p);
5494  else
5495    decr_mac_ref (p);
5496}
5497
5498
5499@ The following subroutine displays a macro, given a pointer to its
5500reference count.
5501
5502@c
5503static void mp_show_macro (MP mp, mp_node p, mp_node q, integer l) {
5504  mp_node r;    /* temporary storage */
5505  p = mp_link (p);              /* bypass the reference count */
5506  while (mp_name_type (p) != mp_macro_sym) {
5507    r = mp_link (p);
5508    mp_link (p) = NULL;
5509    mp_show_token_list (mp, p, NULL, l, 0);
5510    mp_link (p) = r;
5511    p = r;
5512    if (l > 0)
5513      l = l - mp->tally;
5514    else
5515      return;
5516  }                             /* control printing of `\.{ETC.}' */
5517@.ETC@>;
5518  mp->tally = 0;
5519  switch (mp_sym_info (p)) {
5520  case mp_general_macro:
5521    mp_print (mp, "->");
5522    break;
5523@.->@>
5524  case mp_primary_macro:
5525  case mp_secondary_macro:
5526  case mp_tertiary_macro:
5527    mp_print_char (mp, xord ('<'));
5528    mp_print_cmd_mod (mp, mp_param_type, mp_sym_info (p));
5529    mp_print (mp, ">->");
5530    break;
5531  case mp_expr_macro:
5532    mp_print (mp, "<expr>->");
5533    break;
5534  case mp_of_macro:
5535    mp_print (mp, "<expr>of<primary>->");
5536    break;
5537  case mp_suffix_macro:
5538    mp_print (mp, "<suffix>->");
5539    break;
5540  case mp_text_macro:
5541    mp_print (mp, "<text>->");
5542    break;
5543  }                             /* there are no other cases */
5544  mp_show_token_list (mp, mp_link (p), q, l - mp->tally, 0);
5545}
5546
5547
5548@* Data structures for variables.
5549The variables of \MP\ programs can be simple, like `\.x', or they can
5550combine the structural properties of arrays and records, like `\.{x20a.b}'.
5551A \MP\ user assigns a type to a variable like \.{x20a.b} by saying, for
5552example, `\.{boolean} \.{x[]a.b}'. It's time for us to study how such
5553things are represented inside of the computer.
5554
5555Each variable value occupies two consecutive words, either in a non-symbolic
5556node called a value node, or as a non-symbolic subfield of a larger node.  One
5557of those two words is called the |value| field; it is an integer,
5558containing either a |scaled| numeric value or the representation of some
5559other type of quantity. (It might also be subdivided into halfwords, in
5560which case it is referred to by other names instead of |value|.) The other
5561word is broken into subfields called |type|, |name_type|, and |link|.  The
5562|type| field is a quarterword that specifies the variable's type, and
5563|name_type| is a quarterword from which \MP\ can reconstruct the
5564variable's name (sometimes by using the |link| field as well).  Thus, only
55651.25 words are actually devoted to the value itself; the other
5566three-quarters of a word are overhead, but they aren't wasted because they
5567allow \MP\ to deal with sparse arrays and to provide meaningful diagnostics.
5568
5569In this section we shall be concerned only with the structural aspects of
5570variables, not their values. Later parts of the program will change the
5571|type| and |value| fields, but we shall treat those fields as black boxes
5572whose contents should not be touched.
5573
5574However, if the |type| field is |mp_structured|, there is no |value| field,
5575and the second word is broken into two pointer fields called |attr_head|
5576and |subscr_head|. Those fields point to additional nodes that
5577contain structural information, as we shall see.
5578
5579TH Note: DEK and JDH had a nice theoretical split between |value|,
5580|attr| and |subscr| nodes, as documented above and further
5581below. However, all three types had a bad habit of transmuting into
5582each other in practice while pointers to them still lived on
5583elsewhere, so using three different C structures is simply not
5584workable. All three are now represented as a single C structure called
5585|mp_value_node|.
5586
5587There is a potential union in this structure in the interest of space
5588saving: |subscript_| and |hashloc_| are mutually exclusive.
5589
5590Actually, so are |attr_head_| + |subscr_head_| on one side and and
5591|value_| on the other, but because of all the access macros that are
5592used in the code base to get at values, those cannot be folded into a
5593union (yet); this would have required creating a similar union in
5594|mp_token_node| where it would only serve to confuse things.
5595
5596Finally, |parent_| only applies in |attr| nodes (the ones that have
5597|hashloc_|), but creating an extra substructure inside the union just
5598for that does not save space and the extra complication in the
5599structure is not worth the minimal extra code clarification.
5600
5601@d attr_head(A)   do_get_attr_head(mp,(mp_value_node)(A))
5602@d set_attr_head(A,B) do_set_attr_head(mp,(mp_value_node)(A),(mp_node)(B))
5603
5604@d subscr_head(A)   do_get_subscr_head(mp,(mp_value_node)(A))
5605@d set_subscr_head(A,B) do_set_subscr_head(mp,(mp_value_node)(A),(mp_node)(B))
5606
5607@<MPlib internal header stuff@>=
5608typedef struct mp_value_node_data {
5609  NODE_BODY;
5610  mp_value_data data;
5611  mp_number subscript_;
5612  mp_sym hashloc_;
5613  mp_node parent_;
5614  mp_node attr_head_;
5615  mp_node subscr_head_;
5616} mp_value_node_data;
5617
5618@ @c
5619static mp_node do_get_attr_head (MP mp, mp_value_node A) {
5620  assert (A->type == mp_structured);
5621  FUNCTION_TRACE3 ("%p = get_attr_head(%p)\n", A->attr_head_, A);
5622  return A->attr_head_;
5623}
5624static mp_node do_get_subscr_head (MP mp, mp_value_node A) {
5625  assert (A->type == mp_structured);
5626  FUNCTION_TRACE3 ("%p = get_subscr_head(%p)\n", A->subscr_head_, A);
5627  return A->subscr_head_;
5628}
5629static void do_set_attr_head (MP mp, mp_value_node A, mp_node d) {
5630   FUNCTION_TRACE4 ("set_attr_head(%p,%p) on line %d\n", (A), d, __LINE__);
5631   assert (A->type == mp_structured);
5632   A->attr_head_ = d;
5633}
5634static void do_set_subscr_head (MP mp, mp_value_node A, mp_node d) {
5635   FUNCTION_TRACE4 ("set_subscr_head(%p,%p) on line %d\n", (A), d, __LINE__);
5636   assert (A->type == mp_structured);
5637   A->subscr_head_ = d;
5638}
5639
5640@ @<Declarations@>=
5641static mp_node do_get_subscr_head (MP mp, mp_value_node A);
5642static mp_node do_get_attr_head (MP mp, mp_value_node A);
5643static void do_set_attr_head (MP mp, mp_value_node A, mp_node d);
5644static void do_set_subscr_head (MP mp, mp_value_node A, mp_node d);
5645
5646@ It would have been nicer to make |mp_get_value_node| return
5647|mp_value_node| variables, but with |eqtb| as it stands that
5648became messy: lots of typecasts. So, it returns a simple
5649|mp_node| for now.
5650
5651@d value_node_size sizeof(struct mp_value_node_data)
5652
5653@c
5654static mp_node mp_get_value_node (MP mp) {
5655  mp_value_node p;
5656  if (mp->value_nodes) {
5657    p = (mp_value_node)mp->value_nodes;
5658    mp->value_nodes = p->link;
5659    mp->num_value_nodes--;
5660    p->link = NULL;
5661  } else {
5662    p = malloc_node (value_node_size);
5663    new_number(p->data.n);
5664    new_number(p->subscript_);
5665    p->has_number = 2;
5666  }
5667  mp_type (p) = mp_value_node_type;
5668  FUNCTION_TRACE2 ("%p = mp_get_value_node()\n", p);
5669  return (mp_node)p;
5670}
5671#if DEBUG > 1
5672static void debug_dump_value_node (mp_node x) {
5673  mp_value_node qq = (mp_value_node)x;
5674  fprintf (stdout, "\nnode %p:\n", qq);
5675  fprintf (stdout, "  type=%s\n", mp_type_string(qq->type));
5676  fprintf (stdout, "  name_type=%d\n", qq->name_type);
5677  fprintf (stdout, "  link=%p\n", qq->link);
5678  fprintf (stdout, "  data.n=%d\n", qq->data.n.type);
5679  if (is_number(qq->data.n)) {
5680    fprintf (stdout, "    data.n.data.val=%d\n",  qq->data.n.data.val);
5681    fprintf (stdout, "    data.n.data.dval=%f\n", qq->data.n.data.dval);
5682  }
5683  fprintf (stdout, "  data.str=%p\n", qq->data.str);
5684  if (qq->data.str != NULL) {
5685    fprintf (stdout, "    data.str->len=%d\n", (int)qq->data.str->len);
5686    fprintf (stdout, "    data.str->str=%s\n", qq->data.str->str);
5687  }
5688  fprintf (stdout, "  data.indep.serial=%d\n  data.indep.scale=%d\n", qq->data.indep.serial,
5689                                                                       qq->data.indep.scale);
5690  fprintf (stdout, "  data.sym=%p\n", qq->data.sym);
5691  fprintf (stdout, "  data.p=%p\n", qq->data.p);
5692  fprintf (stdout, "  data.node=%p\n", qq->data.node);
5693  fprintf (stdout, "  subscript=%d\n", qq->subscript_.type);
5694  if (is_number(qq->subscript_)) {
5695    fprintf (stdout, "    subscript_.data.val=%d\n",  qq->subscript_.data.val);
5696    fprintf (stdout, "    subscript_.data.dval=%f\n", qq->subscript_.data.dval);
5697  }
5698  fprintf (stdout, "  hashloc=%p\n", qq->hashloc_);
5699  fprintf (stdout, "  parent=%p\n", qq->parent_);
5700  fprintf (stdout, "  attr_head=%p\n", qq->attr_head_);
5701  fprintf (stdout, "  subscr_head=%p\n\n", qq->subscr_head_);
5702}
5703#endif
5704
5705@ @<Declarations@>=
5706static mp_node mp_get_value_node (MP mp);
5707#if DEBUG > 1
5708static void debug_dump_value_node (mp_node x);
5709#endif
5710
5711@ An attribute node is three words long. Two of these words contain |type|
5712and |value| fields as described above, and the third word contains
5713additional information:  There is an |hashloc| field, which contains the
5714hash address of the token that names this attribute; and there's also a
5715|parent| field, which points to the value node of |mp_structured| type at the
5716next higher level (i.e., at the level to which this attribute is
5717subsidiary).  The |name_type| in an attribute node is `|attr|'.  The
5718|link| field points to the next attribute with the same parent; these are
5719arranged in increasing order, so that |hashloc(mp_link(p))>hashloc(p)|. The
5720final attribute node links to the constant |end_attr|, whose |hashloc|
5721field is greater than any legal hash address. The |attr_head| in the
5722parent points to a node whose |name_type| is |mp_structured_root|; this
5723node represents the NULL attribute, i.e., the variable that is relevant
5724when no attributes are attached to the parent. The |attr_head| node
5725has the fields of either
5726a value node, a subscript node, or an attribute node, depending on what
5727the parent would be if it were not structured; but the subscript and
5728attribute fields are ignored, so it effectively contains only the data of
5729a value node. The |link| field in this special node points to an attribute
5730node whose |hashloc| field is zero; the latter node represents a collective
5731subscript `\.{[]}' attached to the parent, and its |link| field points to
5732the first non-special attribute node (or to |end_attr| if there are none).
5733
5734A subscript node likewise occupies three words, with |type| and |value| fields
5735plus extra information; its |name_type| is |subscr|. In this case the
5736third word is called the |subscript| field, which is a |scaled| integer.
5737The |link| field points to the subscript node with the next larger
5738subscript, if any; otherwise the |link| points to the attribute node
5739for collective subscripts at this level. We have seen that the latter node
5740contains an upward pointer, so that the parent can be deduced.
5741
5742The |name_type| in a parent-less value node is |root|, and the |link|
5743is the hash address of the token that names this value.
5744
5745In other words, variables have a hierarchical structure that includes
5746enough threads running around so that the program is able to move easily
5747between siblings, parents, and children. An example should be helpful:
5748(The reader is advised to draw a picture while reading the following
5749description, since that will help to firm up the ideas.)
5750Suppose that `\.x' and `\.{x.a}' and `\.{x[]b}' and `\.{x5}'
5751and `\.{x20b}' have been mentioned in a user's program, where
5752\.{x[]b} has been declared to be of \&{boolean} type. Let |h(x)|, |h(a)|,
5753and |h(b)| be the hash addresses of \.x, \.a, and~\.b. Then
5754|eq_type(h(x))=name| and |equiv(h(x))=p|, where |p|~is a non-symbolic value
5755node with |mp_name_type(p)=root| and |mp_link(p)=h(x)|. We have |type(p)=mp_structured|,
5756|attr_head(p)=q|, and |subscr_head(p)=r|, where |q| points to a value
5757node and |r| to a subscript node. (Are you still following this? Use
5758a pencil to draw a diagram.) The lone variable `\.x' is represented by
5759|type(q)| and |value(q)|; furthermore
5760|mp_name_type(q)=mp_structured_root| and |mp_link(q)=q1|, where |q1| points
5761to an attribute node representing `\.{x[]}'. Thus |mp_name_type(q1)=attr|,
5762|hashloc(q1)=collective_subscript=0|, |parent(q1)=p|,
5763|type(q1)=mp_structured|, |attr_head(q1)=qq|, and |subscr_head(q1)=qq1|;
5764|qq| is a  three-word ``attribute-as-value'' node with |type(qq)=numeric_type|
5765(assuming that \.{x5} is numeric, because |qq| represents `\.{x[]}'
5766with no further attributes), |mp_name_type(qq)=structured_root|,
5767|hashloc(qq)=0|, |parent(qq)=p|, and
5768|mp_link(qq)=qq1|. (Now pay attention to the next part.) Node |qq1| is
5769an attribute node representing `\.{x[][]}', which has never yet
5770occurred; its |type| field is |undefined|, and its |value| field is
5771undefined. We have |mp_name_type(qq1)=attr|, |hashloc(qq1)=collective_subscript|,
5772|parent(qq1)=q1|, and |mp_link(qq1)=qq2|. Since |qq2| represents
5773`\.{x[]b}', |type(qq2)=mp_unknown_boolean|; also |hashloc(qq2)=h(b)|,
5774|parent(qq2)=q1|, |mp_name_type(qq2)=attr|, |mp_link(qq2)=end_attr|.
5775(Maybe colored lines will help untangle your picture.)
5776 Node |r| is a subscript node with |type| and |value|
5777representing `\.{x5}'; |mp_name_type(r)=subscr|, |subscript(r)=5.0|,
5778and |mp_link(r)=r1| is another subscript node. To complete the picture,
5779see if you can guess what |mp_link(r1)| is; give up? It's~|q1|.
5780Furthermore |subscript(r1)=20.0|, |mp_name_type(r1)=subscr|,
5781|type(r1)=mp_structured|, |attr_head(r1)=qqq|, |subscr_head(r1)=qqq1|,
5782and we finish things off with three more nodes
5783|qqq|, |qqq1|, and |qqq2| hung onto~|r1|. (Perhaps you should start again
5784with a larger sheet of paper.) The value of variable \.{x20b}
5785appears in node~|qqq2|, as you can well imagine.
5786
5787If the example in the previous paragraph doesn't make things crystal
5788clear, a glance at some of the simpler subroutines below will reveal how
5789things work out in practice.
5790
5791The only really unusual thing about these conventions is the use of
5792collective subscript attributes. The idea is to avoid repeating a lot of
5793type information when many elements of an array are identical macros
5794(for which distinct values need not be stored) or when they don't have
5795all of the possible attributes. Branches of the structure below collective
5796subscript attributes do not carry actual values except for macro identifiers;
5797branches of the structure below subscript nodes do not carry significant
5798information in their collective subscript attributes.
5799
5800
5801@c
5802#if DEBUG
5803#define hashloc(A)       do_get_hashloc(mp,(mp_value_node)(A))
5804#define set_hashloc(A,B) do_set_hashloc (mp,(mp_value_node)A, B)
5805#define parent(A)        do_get_parent(mp, A)
5806#define set_parent(A,B)  do_set_parent (mp,(mp_value_node)A, B)
5807static mp_sym do_get_hashloc (MP mp, mp_value_node A) {
5808  assert((A)->type == mp_attr_node_type || (A)->name_type == mp_attr);
5809  return (A)->hashloc_;
5810}
5811static void do_set_hashloc (MP mp, mp_value_node A, mp_sym B) {
5812  FUNCTION_TRACE4 ("set_hashloc(%p,%p) on line %d\n", (A), (B), __LINE__);
5813   assert((A)->type == mp_attr_node_type || (A)->name_type == mp_attr);
5814   A->hashloc_ = B;
5815}
5816static mp_node do_get_parent (MP mp, mp_value_node A) {
5817  assert((A)->type == mp_attr_node_type || (A)->name_type == mp_attr);
5818  return (A)->parent_; /* pointer to |mp_structured| variable */
5819}
5820static void do_set_parent (MP mp, mp_value_node A, mp_node d) {
5821   assert((A)->type == mp_attr_node_type || (A)->name_type == mp_attr);
5822   FUNCTION_TRACE4 ("set_parent(%p,%p) on line %d\n", (A), d, __LINE__);
5823   A->parent_ = d;
5824}
5825#else
5826#define hashloc(A)       ((mp_value_node)(A))->hashloc_
5827#define set_hashloc(A,B) ((mp_value_node)(A))->hashloc_ = B
5828#define parent(A)        ((mp_value_node)(A))->parent_
5829#define set_parent(A,B)  ((mp_value_node)(A))->parent_ = B
5830#endif
5831
5832@
5833@d mp_free_attr_node(a,b) do {
5834   assert((b)->type == mp_attr_node_type || (b)->name_type == mp_attr);
5835   mp_free_value_node(a,b);
5836} while (0)
5837
5838@c
5839static mp_value_node mp_get_attr_node (MP mp) {
5840  mp_value_node p = (mp_value_node) mp_get_value_node (mp);
5841  mp_type (p) = mp_attr_node_type;
5842  return p;
5843}
5844
5845
5846@ Setting the |hashloc| field of |end_attr| to a value greater than
5847any legal hash address is done by assigning $-1$ typecasted to
5848|mp_sym|, hopefully resulting in all bits being set. On systems that
5849support negative pointer values or where typecasting $-1$ does not
5850result in all bits in a pointer being set, something else needs to be done.
5851@^system dependencies@>
5852
5853@<Initialize table...@>=
5854mp->end_attr = (mp_node) mp_get_attr_node (mp);
5855set_hashloc (mp->end_attr, (mp_sym)-1);
5856set_parent ((mp_value_node) mp->end_attr, NULL);
5857
5858@ @<Free table...@>=
5859mp_free_attr_node (mp, mp->end_attr);
5860
5861@
5862@d collective_subscript (void *)0 /* code for the attribute `\.{[]}' */
5863@d subscript(A) ((mp_value_node)(A))->subscript_
5864@d set_subscript(A,B) do_set_subscript (mp, (mp_value_node)(A), B)
5865
5866@c
5867static void do_set_subscript (MP mp, mp_value_node A, mp_number B) {
5868  FUNCTION_TRACE3("set_subscript(%p,%p)\n", (A), (B));
5869  assert((A)->type == mp_subscr_node_type || (A)->name_type == mp_subscr);
5870  number_clone(A->subscript_,B); /* subscript of this variable */
5871}
5872
5873@
5874@c
5875static mp_value_node mp_get_subscr_node (MP mp) {
5876  mp_value_node p = (mp_value_node) mp_get_value_node (mp);
5877  mp_type (p) = mp_subscr_node_type;
5878  return p;
5879}
5880
5881
5882@ Variables of type \&{pair} will have values that point to four-word
5883nodes containing two numeric values. The first of these values has
5884|name_type=mp_x_part_sector| and the second has |name_type=mp_y_part_sector|;
5885the |link| in the first points back to the node whose |value| points
5886to this four-word node.
5887
5888@d x_part(A) ((mp_pair_node)(A))->x_part_ /* where the \&{xpart} is found in a pair node */
5889@d y_part(A) ((mp_pair_node)(A))->y_part_ /* where the \&{ypart} is found in a pair node */
5890
5891@<MPlib internal header stuff@>=
5892typedef struct mp_pair_node_data {
5893  NODE_BODY;
5894  mp_node x_part_;
5895  mp_node y_part_;
5896} mp_pair_node_data;
5897typedef struct mp_pair_node_data *mp_pair_node;
5898
5899@
5900@d pair_node_size sizeof(struct mp_pair_node_data) /* the number of words in a subscript node */
5901
5902@c
5903static mp_node mp_get_pair_node (MP mp) {
5904  mp_node p;
5905  if (mp->pair_nodes) {
5906    p = mp->pair_nodes;
5907    mp->pair_nodes = p->link;
5908    mp->num_pair_nodes--;
5909    p->link = NULL;
5910  } else {
5911    p = malloc_node (pair_node_size);
5912  }
5913  mp_type (p) = mp_pair_node_type;
5914  FUNCTION_TRACE2("get_pair_node(): %p\n", p);
5915  return (mp_node) p;
5916}
5917
5918@ @<Declarations@>=
5919void mp_free_pair_node (MP mp, mp_node p);
5920
5921@ @c
5922void mp_free_pair_node (MP mp, mp_node p) {
5923  FUNCTION_TRACE2 ("mp_free_pair_node(%p)\n", p);
5924  if (!p) return;
5925  if (mp->num_pair_nodes < max_num_pair_nodes) {
5926    p->link = mp->pair_nodes;
5927    mp->pair_nodes = p;
5928    mp->num_pair_nodes++;
5929    return;
5930  }
5931  mp->var_used -= pair_node_size;
5932  xfree (p);
5933}
5934
5935
5936@ If |type(p)=mp_pair_type| or if |value(p)=NULL|, the procedure call |init_pair_node(p)| will
5937allocate a pair node for~|p|.  The individual parts of such nodes are  initially of type
5938|mp_independent|.
5939
5940@c
5941static void mp_init_pair_node (MP mp, mp_node p) {
5942  mp_node q;    /* the new node */
5943  mp_type (p) = mp_pair_type;
5944  q = mp_get_pair_node (mp);
5945  y_part (q) = mp_get_value_node (mp);
5946  mp_new_indep (mp, y_part (q));   /* sets |type(q)| and |value(q)| */
5947  mp_name_type (y_part (q)) = (quarterword) (mp_y_part_sector);
5948  mp_link (y_part (q)) = p;
5949  x_part (q) = mp_get_value_node (mp);
5950  mp_new_indep (mp, x_part (q));   /* sets |type(q)| and |value(q)| */
5951  mp_name_type (x_part (q)) = (quarterword) (mp_x_part_sector);
5952  mp_link (x_part (q)) = p;
5953  set_value_node (p, q);
5954}
5955
5956
5957@
5958Variables of type \&{transform} are similar, but in this case their
5959|value| points to a 12-word node containing six values, identified by
5960|x_part_sector|, |y_part_sector|, |mp_xx_part_sector|, |mp_xy_part_sector|,
5961|mp_yx_part_sector|, and |mp_yy_part_sector|.
5962
5963@d tx_part(A) ((mp_transform_node)(A))->tx_part_ /* where the \&{xpart} is found in a transform node */
5964@d ty_part(A) ((mp_transform_node)(A))->ty_part_ /* where the \&{ypart} is found in a transform node */
5965@d xx_part(A) ((mp_transform_node)(A))->xx_part_ /* where the \&{xxpart} is found in a transform node */
5966@d xy_part(A) ((mp_transform_node)(A))->xy_part_ /* where the \&{xypart} is found in a transform node */
5967@d yx_part(A) ((mp_transform_node)(A))->yx_part_ /* where the \&{yxpart} is found in a transform node */
5968@d yy_part(A) ((mp_transform_node)(A))->yy_part_ /* where the \&{yypart} is found in a transform node */
5969
5970@<MPlib internal header stuff@>=
5971typedef struct mp_transform_node_data {
5972  NODE_BODY;
5973  mp_node tx_part_;
5974  mp_node ty_part_;
5975  mp_node xx_part_;
5976  mp_node yx_part_;
5977  mp_node xy_part_;
5978  mp_node yy_part_;
5979} mp_transform_node_data;
5980typedef struct mp_transform_node_data *mp_transform_node;
5981
5982@
5983@d transform_node_size sizeof(struct mp_transform_node_data) /* the number of words in a subscript node */
5984
5985@c
5986static mp_node mp_get_transform_node (MP mp) {
5987  mp_transform_node p = (mp_transform_node) malloc_node (transform_node_size);
5988  mp_type (p) = mp_transform_node_type;
5989  return (mp_node) p;
5990}
5991
5992
5993@ @c
5994static void mp_init_transform_node (MP mp, mp_node p) {
5995  mp_node q;    /* the new node */
5996  mp_type (p) = mp_transform_type;
5997  q = mp_get_transform_node (mp);       /* big node */
5998  yy_part (q) = mp_get_value_node (mp);
5999  mp_new_indep (mp, yy_part (q));  /* sets |type(q)| and |value(q)| */
6000  mp_name_type (yy_part (q)) = (quarterword) (mp_yy_part_sector);
6001  mp_link (yy_part (q)) = p;
6002  yx_part (q) = mp_get_value_node (mp);
6003  mp_new_indep (mp, yx_part (q));  /* sets |type(q)| and |value(q)| */
6004  mp_name_type (yx_part (q)) = (quarterword) (mp_yx_part_sector);
6005  mp_link (yx_part (q)) = p;
6006  xy_part (q) = mp_get_value_node (mp);
6007  mp_new_indep (mp, xy_part (q));  /* sets |type(q)| and |value(q)| */
6008  mp_name_type (xy_part (q)) = (quarterword) (mp_xy_part_sector);
6009  mp_link (xy_part (q)) = p;
6010  xx_part (q) = mp_get_value_node (mp);
6011  mp_new_indep (mp, xx_part (q));  /* sets |type(q)| and |value(q)| */
6012  mp_name_type (xx_part (q)) = (quarterword) (mp_xx_part_sector);
6013  mp_link (xx_part (q)) = p;
6014  ty_part (q) = mp_get_value_node (mp);
6015  mp_new_indep (mp, ty_part (q));  /* sets |type(q)| and |value(q)| */
6016  mp_name_type (ty_part (q)) = (quarterword) (mp_y_part_sector);
6017  mp_link (ty_part (q)) = p;
6018  tx_part (q) = mp_get_value_node (mp);
6019  mp_new_indep (mp, tx_part (q));  /* sets |type(q)| and |value(q)| */
6020  mp_name_type (tx_part (q)) = (quarterword) (mp_x_part_sector);
6021  mp_link (tx_part (q)) = p;
6022  set_value_node (p, q);
6023}
6024
6025
6026@
6027Variables of type \&{color} have 3~values in 6~words identified by |mp_red_part_sector|,
6028|mp_green_part_sector|, and |mp_blue_part_sector|.
6029
6030@d red_part(A) ((mp_color_node)(A))->red_part_ /* where the \&{redpart} is found in a color node */
6031@d green_part(A) ((mp_color_node)(A))->green_part_ /* where the \&{greenpart} is found in a color node */
6032@d blue_part(A) ((mp_color_node)(A))->blue_part_ /* where the \&{bluepart} is found in a color node */
6033
6034@d grey_part(A) red_part(A) /* where the \&{greypart} is found in a color node */
6035
6036@<MPlib internal header stuff@>=
6037typedef struct mp_color_node_data {
6038  NODE_BODY;
6039  mp_node red_part_;
6040  mp_node green_part_;
6041  mp_node blue_part_;
6042} mp_color_node_data;
6043typedef struct mp_color_node_data *mp_color_node;
6044
6045@
6046@d color_node_size sizeof(struct mp_color_node_data) /* the number of words in a subscript node */
6047
6048@c
6049static mp_node mp_get_color_node (MP mp) {
6050  mp_color_node p = (mp_color_node) malloc_node (color_node_size);
6051  mp_type (p) = mp_color_node_type;
6052  p->link = NULL;
6053  return (mp_node) p;
6054}
6055
6056
6057@
6058@c
6059static void mp_init_color_node (MP mp, mp_node p) {
6060  mp_node q;    /* the new node */
6061  mp_type (p) = mp_color_type;
6062  q = mp_get_color_node (mp);   /* big node */
6063  blue_part (q) = mp_get_value_node (mp);
6064  mp_new_indep (mp, blue_part (q));        /* sets |type(q)| and |value(q)| */
6065  mp_name_type (blue_part (q)) = (quarterword) (mp_blue_part_sector);
6066  mp_link (blue_part (q)) = p;
6067  green_part (q) = mp_get_value_node (mp);
6068  mp_new_indep (mp, green_part (q));       /* sets |type(q)| and |value(q)| */
6069  mp_name_type (y_part (q)) = (quarterword) (mp_green_part_sector);
6070  mp_link (green_part (q)) = p;
6071  red_part (q) = mp_get_value_node (mp);
6072  mp_new_indep (mp, red_part (q)); /* sets |type(q)| and |value(q)| */
6073  mp_name_type (red_part (q)) = (quarterword) (mp_red_part_sector);
6074  mp_link (red_part (q)) = p;
6075  set_value_node (p, q);
6076}
6077
6078
6079@ Finally, variables of type |cmykcolor|.
6080
6081@d cyan_part(A)    ((mp_cmykcolor_node)(A))->cyan_part_ /* where the \&{cyanpart} is found in a color node */
6082@d magenta_part(A) ((mp_cmykcolor_node)(A))->magenta_part_ /* where the \&{magentapart} is found in a color node */
6083@d yellow_part(A)  ((mp_cmykcolor_node)(A))->yellow_part_ /* where the \&{yellowpart} is found in a color node */
6084@d black_part(A)   ((mp_cmykcolor_node)(A))->black_part_ /* where the \&{blackpart} is found in a color node */
6085
6086@<MPlib internal header stuff@>=
6087typedef struct mp_cmykcolor_node_data {
6088  NODE_BODY;
6089  mp_node cyan_part_;
6090  mp_node magenta_part_;
6091  mp_node yellow_part_;
6092  mp_node black_part_;
6093} mp_cmykcolor_node_data;
6094typedef struct mp_cmykcolor_node_data *mp_cmykcolor_node;
6095
6096@
6097@d cmykcolor_node_size sizeof(struct mp_cmykcolor_node_data) /* the number of words in a subscript node */
6098
6099@c
6100static mp_node mp_get_cmykcolor_node (MP mp) {
6101  mp_cmykcolor_node p = (mp_cmykcolor_node) malloc_node (cmykcolor_node_size);
6102  mp_type (p) = mp_cmykcolor_node_type;
6103  p->link = NULL;
6104  return (mp_node) p;
6105}
6106
6107
6108@
6109@c
6110static void mp_init_cmykcolor_node (MP mp, mp_node p) {
6111  mp_node q;    /* the new node */
6112  mp_type (p) = mp_cmykcolor_type;
6113  q = mp_get_cmykcolor_node (mp);       /* big node */
6114  black_part (q) = mp_get_value_node (mp);
6115  mp_new_indep (mp, black_part (q));       /* sets |type(q)| and |value(q)| */
6116  mp_name_type (black_part (q)) = (quarterword) (mp_black_part_sector);
6117  mp_link (black_part (q)) = p;
6118  yellow_part (q) = mp_get_value_node (mp);
6119  mp_new_indep (mp, yellow_part (q));      /* sets |type(q)| and |value(q)| */
6120  mp_name_type (yellow_part (q)) = (quarterword) (mp_yellow_part_sector);
6121  mp_link (yellow_part (q)) = p;
6122  magenta_part (q) = mp_get_value_node (mp);
6123  mp_new_indep (mp, magenta_part (q));     /* sets |type(q)| and |value(q)| */
6124  mp_name_type (magenta_part (q)) = (quarterword) (mp_magenta_part_sector);
6125  mp_link (magenta_part (q)) = p;
6126  cyan_part (q) = mp_get_value_node (mp);
6127  mp_new_indep (mp, cyan_part (q));        /* sets |type(q)| and |value(q)| */
6128  mp_name_type (cyan_part (q)) = (quarterword) (mp_cyan_part_sector);
6129  mp_link (cyan_part (q)) = p;
6130  set_value_node (p, q);
6131}
6132
6133
6134@ When an entire structured variable is saved, the |root| indication
6135is temporarily replaced by |saved_root|.
6136
6137Some variables have no name; they just are used for temporary storage
6138while expressions are being evaluated. We call them {\sl capsules}.
6139
6140@ The |id_transform| function creates a capsule for the
6141identity transformation.
6142
6143@c
6144static mp_node mp_id_transform (MP mp) {
6145  mp_node p, q; /* list manipulation registers */
6146  p = mp_get_value_node (mp);
6147  mp_name_type (p) = mp_capsule;
6148  set_value_number (p, zero_t);             /* todo: this was |null| */
6149  mp_init_transform_node (mp, p);
6150  q = value_node (p);
6151  mp_type (tx_part (q)) = mp_known;
6152  set_value_number (tx_part (q), zero_t);
6153  mp_type (ty_part (q)) = mp_known;
6154  set_value_number (ty_part (q), zero_t);
6155  mp_type (xy_part (q)) = mp_known;
6156  set_value_number (xy_part (q), zero_t);
6157  mp_type (yx_part (q)) = mp_known;
6158  set_value_number (yx_part (q), zero_t);
6159  mp_type (xx_part (q)) = mp_known;
6160  set_value_number (xx_part (q), unity_t);
6161  mp_type (yy_part (q)) = mp_known;
6162  set_value_number (yy_part (q), unity_t);
6163  return p;
6164}
6165
6166
6167@ Tokens are of type |tag_token| when they first appear, but they point
6168to |NULL| until they are first used as the root of a variable.
6169The following subroutine establishes the root node on such grand occasions.
6170
6171@c
6172static void mp_new_root (MP mp, mp_sym x) {
6173  mp_node p;    /* the new node */
6174  p = mp_get_value_node (mp);
6175  mp_type (p) = mp_undefined;
6176  mp_name_type (p) = mp_root;
6177  set_value_sym (p, x);
6178  set_equiv_node (x, p);
6179}
6180
6181
6182@ These conventions for variable representation are illustrated by the
6183|print_variable_name| routine, which displays the full name of a
6184variable given only a pointer to its value.
6185
6186@<Declarations@>=
6187static void mp_print_variable_name (MP mp, mp_node p);
6188
6189@ @c
6190void mp_print_variable_name (MP mp, mp_node p) {
6191  mp_node q;    /* a token list that will name the variable's suffix */
6192  mp_node r;    /* temporary for token list creation */
6193  while (mp_name_type (p) >= mp_x_part_sector) {
6194    switch (mp_name_type (p)) {
6195    case mp_x_part_sector:      mp_print (mp, "xpart ");      break;
6196    case mp_y_part_sector:      mp_print (mp, "ypart ");      break;
6197    case mp_xx_part_sector:     mp_print (mp, "xxpart ");     break;
6198    case mp_xy_part_sector:     mp_print (mp, "xypart ");     break;
6199    case mp_yx_part_sector:     mp_print (mp, "yxpart ");     break;
6200    case mp_yy_part_sector:     mp_print (mp, "yypart ");     break;
6201    case mp_red_part_sector:    mp_print (mp, "redpart ");    break;
6202    case mp_green_part_sector:  mp_print (mp, "greenpart ");  break;
6203    case mp_blue_part_sector:   mp_print (mp, "bluepart ");   break;
6204    case mp_cyan_part_sector:   mp_print (mp, "cyanpart ");   break;
6205    case mp_magenta_part_sector:mp_print (mp, "magentapart ");break;
6206    case mp_yellow_part_sector: mp_print (mp, "yellowpart "); break;
6207    case mp_black_part_sector:  mp_print (mp, "blackpart ");  break;
6208    case mp_grey_part_sector:   mp_print (mp, "greypart ");   break;
6209    case mp_capsule:            mp_printf (mp, "%%CAPSULE%p",p); return; break;
6210    /* this is to please the compiler: the remaining cases are operation codes */
6211    default: break;
6212    }
6213    p = mp_link (p);
6214  }
6215  q = NULL;
6216  while (mp_name_type (p) > mp_saved_root) {
6217    /* Ascend one level, pushing a token onto list |q|
6218       and replacing |p| by its parent */
6219    if (mp_name_type (p) == mp_subscr) {
6220      r = mp_new_num_tok (mp, subscript (p));
6221      do {
6222        p = mp_link (p);
6223      } while (mp_name_type (p) != mp_attr);
6224    } else if (mp_name_type (p) == mp_structured_root) {
6225      p = mp_link (p);
6226      goto FOUND;
6227    } else {
6228      if (mp_name_type (p) != mp_attr)
6229        mp_confusion (mp, "var");
6230      r = mp_get_symbolic_node (mp);
6231      set_mp_sym_sym (r, hashloc (p)); /* the hash address */
6232    }
6233    set_mp_link (r, q);
6234    q = r;
6235  FOUND:
6236    p = parent ((mp_value_node) p);
6237
6238  }
6239  /* now |link(p)| is the hash address of |p|, and
6240     |name_type(p)| is either |root| or |saved_root|.
6241     Have to prepend a token to |q| for |show_token_list|. */
6242  r = mp_get_symbolic_node (mp);
6243  set_mp_sym_sym (r, value_sym (p));
6244  mp_link (r) = q;
6245  if (mp_name_type (p) == mp_saved_root)
6246    mp_print (mp, "(SAVED)");
6247  mp_show_token_list (mp, r, NULL, max_integer, mp->tally);
6248  mp_flush_token_list (mp, r);
6249}
6250
6251@ The |interesting| function returns |true| if a given variable is not
6252in a capsule, or if the user wants to trace capsules.
6253
6254@c
6255static boolean mp_interesting (MP mp, mp_node p) {
6256  mp_name_type_type t;        /* a |name_type| */
6257  if (number_positive(internal_value (mp_tracing_capsules))) {
6258    return true;
6259  } else {
6260    t = mp_name_type (p);
6261    if (t >= mp_x_part_sector && t != mp_capsule) {
6262      mp_node tt = value_node(mp_link(p));
6263      switch (t) {
6264      case mp_x_part_sector:
6265        t = mp_name_type (x_part (tt));
6266        break;
6267      case mp_y_part_sector:
6268        t = mp_name_type (y_part (tt));
6269        break;
6270      case mp_xx_part_sector:
6271        t = mp_name_type (xx_part (tt));
6272        break;
6273      case mp_xy_part_sector:
6274        t = mp_name_type (xy_part (tt));
6275        break;
6276      case mp_yx_part_sector:
6277        t = mp_name_type (yx_part (tt));
6278        break;
6279      case mp_yy_part_sector:
6280        t = mp_name_type (yy_part (tt));
6281        break;
6282      case mp_red_part_sector:
6283        t = mp_name_type (red_part (tt));
6284        break;
6285      case mp_green_part_sector:
6286        t = mp_name_type (green_part (tt));
6287        break;
6288      case mp_blue_part_sector:
6289        t = mp_name_type (blue_part (tt));
6290        break;
6291      case mp_cyan_part_sector:
6292        t = mp_name_type (cyan_part (tt));
6293        break;
6294      case mp_magenta_part_sector:
6295        t = mp_name_type (magenta_part (tt));
6296        break;
6297      case mp_yellow_part_sector:
6298        t = mp_name_type (yellow_part (tt));
6299        break;
6300      case mp_black_part_sector:
6301        t = mp_name_type (black_part (tt));
6302        break;
6303      case mp_grey_part_sector:
6304        t = mp_name_type (grey_part (tt));
6305        break;
6306      default:
6307        break;
6308      }
6309    }
6310  }
6311  return (t != mp_capsule);
6312}
6313
6314
6315@ Now here is a subroutine that converts an unstructured type into an
6316equivalent structured type, by inserting a |mp_structured| node that is
6317capable of growing. This operation is done only when |mp_name_type(p)=root|,
6318|subscr|, or |attr|.
6319
6320The procedure returns a pointer to the new node that has taken node~|p|'s
6321place in the structure. Node~|p| itself does not move, nor are its
6322|value| or |type| fields changed in any way.
6323
6324@c
6325static mp_node mp_new_structure (MP mp, mp_node p) {
6326  mp_node q, r = NULL;  /* list manipulation registers */
6327  mp_sym qq = NULL;
6328  switch (mp_name_type (p)) {
6329  case mp_root:
6330    {
6331      qq = value_sym (p);
6332      r = mp_get_value_node (mp);
6333      set_equiv_node (qq, r);
6334    }
6335    break;
6336  case mp_subscr:
6337    /* Link a new subscript node |r| in place of node |p| */
6338    {
6339      mp_node q_new;
6340      q = p;
6341      do {
6342        q = mp_link (q);
6343      } while (mp_name_type (q) != mp_attr);
6344      q = parent ((mp_value_node) q);
6345      r = mp->temp_head;
6346      set_mp_link (r, subscr_head (q));
6347      do {
6348        q_new = r;
6349        r = mp_link (r);
6350      } while (r != p);
6351      r = (mp_node) mp_get_subscr_node (mp);
6352      if (q_new == mp->temp_head) {
6353        set_subscr_head (q, r);
6354      } else {
6355        set_mp_link (q_new, r);
6356      }
6357      set_subscript (r, subscript (p));
6358    }
6359
6360    break;
6361  case mp_attr:
6362    /* Link a new attribute node |r| in place of node |p| */
6363    /* If the attribute is |collective_subscript|, there are two pointers to
6364       node~|p|, so we must change both of them. */
6365    {
6366      mp_value_node rr;
6367      q = parent ((mp_value_node) p);
6368      r = attr_head (q);
6369      do {
6370        q = r;
6371        r = mp_link (r);
6372      } while (r != p);
6373      rr = mp_get_attr_node (mp);
6374      r = (mp_node) rr;
6375      set_mp_link (q, (mp_node) rr);
6376      set_hashloc (rr, hashloc (p));
6377      set_parent (rr, parent ((mp_value_node) p));
6378      if (hashloc (p) == collective_subscript) {
6379        q = mp->temp_head;
6380        set_mp_link (q, subscr_head (parent ((mp_value_node) p)));
6381        while (mp_link (q) != p)
6382          q = mp_link (q);
6383        if (q == mp->temp_head)
6384          set_subscr_head (parent ((mp_value_node) p), (mp_node) rr);
6385        else
6386          set_mp_link (q, (mp_node) rr);
6387      }
6388    }
6389
6390    break;
6391  default:
6392    mp_confusion (mp, "struct");
6393    break;
6394  }
6395  set_mp_link (r, mp_link (p));
6396  set_value_sym (r, value_sym (p));
6397  mp_type (r) = mp_structured;
6398  mp_name_type (r) = mp_name_type (p);
6399  set_attr_head (r, p);
6400  mp_name_type (p) = mp_structured_root;
6401  {
6402    mp_value_node qqr = mp_get_attr_node (mp);
6403    set_mp_link (p, (mp_node) qqr);
6404    set_subscr_head (r, (mp_node) qqr);
6405    set_parent (qqr, r);
6406    mp_type (qqr) = mp_undefined;
6407    mp_name_type (qqr) = mp_attr;
6408    set_mp_link (qqr, mp->end_attr);
6409    set_hashloc (qqr, collective_subscript);
6410  }
6411  return r;
6412}
6413
6414@ The |find_variable| routine is given a pointer~|t| to a nonempty token
6415list of suffixes; it returns a pointer to the corresponding non-symbolic
6416value. For example, if |t| points to token \.x followed by a numeric
6417token containing the value~7, |find_variable| finds where the value of
6418\.{x7} is stored in memory. This may seem a simple task, and it
6419usually is, except when \.{x7} has never been referenced before.
6420Indeed, \.x may never have even been subscripted before; complexities
6421arise with respect to updating the collective subscript information.
6422
6423If a macro type is detected anywhere along path~|t|, or if the first
6424item on |t| isn't a |tag_token|, the value |NULL| is returned.
6425Otherwise |p| will be a non-NULL pointer to a node such that
6426|undefined<type(p)<mp_structured|.
6427
6428@c
6429static mp_node mp_find_variable (MP mp, mp_node t) {
6430  mp_node p, q, r, s;   /* nodes in the ``value'' line */
6431  mp_sym p_sym;
6432  mp_node pp, qq, rr, ss;       /* nodes in the ``collective'' line */
6433@^inner loop@>;
6434  p_sym = mp_sym_sym (t);
6435  t = mp_link (t);
6436  if ((eq_type (p_sym) % mp_outer_tag) != mp_tag_token)
6437    return NULL;
6438  if (equiv_node (p_sym) == NULL)
6439    mp_new_root (mp, p_sym);
6440  p = equiv_node (p_sym);
6441  pp = p;
6442  while (t != NULL) {
6443    /* Make sure that both nodes |p| and |pp| are of |mp_structured| type */
6444    /* Although |pp| and |p| begin together, they diverge when a subscript occurs;
6445       |pp|~stays in the collective line while |p|~goes through actual subscript
6446       values. */
6447    if (mp_type (pp) != mp_structured) {
6448      if (mp_type (pp) > mp_structured)
6449        return NULL;
6450      ss = mp_new_structure (mp, pp);
6451      if (p == pp)
6452        p = ss;
6453      pp = ss;
6454    }                             /* now |type(pp)=mp_structured| */
6455    if (mp_type (p) != mp_structured) {   /* it cannot be |>mp_structured| */
6456      p = mp_new_structure (mp, p);       /* now |type(p)=mp_structured| */
6457    }
6458
6459    if (mp_type (t) != mp_symbol_node) {
6460      /* Descend one level for the subscript |value(t)| */
6461      /* We want this part of the program to be reasonably fast, in case there are
6462        lots of subscripts at the same level of the data structure. Therefore
6463        we store an ``infinite'' value in the word that appears at the end of the
6464        subscript list, even though that word isn't part of a subscript node. */
6465      mp_number nn, save_subscript;      /* temporary storage */
6466      new_number (nn);
6467      new_number (save_subscript);
6468      number_clone (nn, value_number (t));
6469      pp = mp_link (attr_head (pp)); /* now |hashloc(pp)=collective_subscript| */
6470      q = mp_link (attr_head (p));
6471      number_clone (save_subscript, subscript (q));
6472      set_number_to_inf(subscript (q));
6473      s = mp->temp_head;
6474      set_mp_link (s, subscr_head (p));
6475      do {
6476        r = s;
6477        s = mp_link (s);
6478      } while (number_greater (nn, subscript (s)));
6479      if (number_equal(nn, subscript (s))) {
6480        p = s;
6481      } else {
6482        mp_value_node p1 = mp_get_subscr_node (mp);
6483        if (r == mp->temp_head)
6484          set_subscr_head (p, (mp_node) p1);
6485        else
6486          set_mp_link (r, (mp_node) p1);
6487        set_mp_link (p1, s);
6488        number_clone (subscript (p1), nn);
6489        mp_name_type (p1) = mp_subscr;
6490        mp_type (p1) = mp_undefined;
6491        p = (mp_node) p1;
6492      }
6493      number_clone (subscript (q), save_subscript);
6494      free_number (save_subscript);
6495      free_number (nn);
6496    } else {
6497      /* Descend one level for the attribute |mp_sym_info(t)| */
6498      mp_sym nn1 = mp_sym_sym (t);
6499      ss = attr_head (pp);
6500      do {
6501        rr = ss;
6502        ss = mp_link (ss);
6503      } while (nn1 > hashloc (ss));
6504      if (nn1 < hashloc (ss)) {
6505        qq = (mp_node) mp_get_attr_node (mp);
6506        set_mp_link (rr, qq);
6507        set_mp_link (qq, ss);
6508        set_hashloc (qq, nn1);
6509        mp_name_type (qq) = mp_attr;
6510        mp_type (qq) = mp_undefined;
6511        set_parent ((mp_value_node) qq, pp);
6512        ss = qq;
6513      }
6514      if (p == pp) {
6515        p = ss;
6516        pp = ss;
6517      } else {
6518        pp = ss;
6519        s = attr_head (p);
6520        do {
6521          r = s;
6522          s = mp_link (s);
6523        } while (nn1 > hashloc (s));
6524        if (nn1 == hashloc (s)) {
6525          p = s;
6526        } else {
6527          q = (mp_node) mp_get_attr_node (mp);
6528          set_mp_link (r, q);
6529          set_mp_link (q, s);
6530          set_hashloc (q, nn1);
6531          mp_name_type (q) = mp_attr;
6532          mp_type (q) = mp_undefined;
6533          set_parent ((mp_value_node) q, p);
6534          p = q;
6535        }
6536      }
6537    }
6538    t = mp_link (t);
6539  }
6540  if (mp_type (pp) >= mp_structured) {
6541    if (mp_type (pp) == mp_structured)
6542      pp = attr_head (pp);
6543    else
6544      return NULL;
6545  }
6546  if (mp_type (p) == mp_structured)
6547    p = attr_head (p);
6548  if (mp_type (p) == mp_undefined) {
6549    if (mp_type (pp) == mp_undefined) {
6550      mp_type (pp) = mp_numeric_type;
6551      set_value_number (pp, zero_t);
6552    }
6553    mp_type (p) = mp_type (pp);
6554    set_value_number (p, zero_t);
6555  }
6556  return p;
6557}
6558
6559
6560@ Variables lose their former values when they appear in a type declaration,
6561or when they are defined to be macros or \&{let} equal to something else.
6562A subroutine will be defined later that recycles the storage associated
6563with any particular |type| or |value|; our goal now is to study a higher
6564level process called |flush_variable|, which selectively frees parts of a
6565variable structure.
6566
6567This routine has some complexity because of examples such as
6568`\hbox{\tt numeric x[]a[]b}'
6569which recycles all variables of the form \.{x[i]a[j]b} (and no others), while
6570`\hbox{\tt vardef x[]a[]=...}'
6571discards all variables of the form \.{x[i]a[j]} followed by an arbitrary
6572suffix, except for the collective node \.{x[]a[]} itself. The obvious way
6573to handle such examples is to use recursion; so that's what we~do.
6574@^recursion@>
6575
6576Parameter |p| points to the root information of the variable;
6577parameter |t| points to a list of symbolic nodes that represent
6578suffixes, with |info=collective_subscript| for subscripts.
6579
6580@<Declarations@>=
6581void mp_flush_cur_exp (MP mp, mp_value v);
6582
6583@ @c
6584static void mp_flush_variable (MP mp, mp_node p, mp_node t,
6585                               boolean discard_suffixes) {
6586  mp_node q, r = NULL; /* list manipulation */
6587  mp_sym n;     /* attribute to match */
6588  while (t != NULL) {
6589    if (mp_type (p) != mp_structured) {
6590      return;
6591    }
6592    n = mp_sym_sym (t);
6593    t = mp_link (t);
6594    if (n == collective_subscript) {
6595      q = subscr_head (p);
6596      while (mp_name_type (q) == mp_subscr) {
6597        mp_flush_variable (mp, q, t, discard_suffixes);
6598        if (t == NULL) {
6599          if (mp_type (q) == mp_structured) {
6600            r = q;
6601          } else {
6602            if (r==NULL)
6603   	      set_subscr_head (p, mp_link (q));
6604            else
6605              set_mp_link (r, mp_link (q));
6606            mp_free_value_node (mp, q);
6607          }
6608        } else {
6609          r = q;
6610        }
6611        q = (r==NULL ? subscr_head (p) : mp_link (r));
6612      }
6613    }
6614    p = attr_head (p);
6615    do {
6616      p = mp_link (p);
6617    } while (hashloc (p) < n);
6618    if (hashloc (p) != n) {
6619      return;
6620    }
6621  }
6622  if (discard_suffixes) {
6623    mp_flush_below_variable (mp, p);
6624  } else {
6625    if (mp_type (p) == mp_structured) {
6626      p = attr_head (p);
6627    }
6628    mp_recycle_value (mp, p);
6629  }
6630}
6631
6632
6633@ The next procedure is simpler; it wipes out everything but |p| itself,
6634which becomes undefined.
6635
6636@<Declarations@>=
6637static void mp_flush_below_variable (MP mp, mp_node p);
6638
6639@ @c
6640void mp_flush_below_variable (MP mp, mp_node p) {
6641  mp_node q, r; /* list manipulation registers */
6642  FUNCTION_TRACE2 ("mp_flush_below_variable(%p)\n", p);
6643  if (mp_type (p) != mp_structured) {
6644    mp_recycle_value (mp, p);   /* this sets |type(p)=undefined| */
6645  } else {
6646    q = subscr_head (p);
6647    while (mp_name_type (q) == mp_subscr) {
6648      mp_flush_below_variable (mp, q);
6649      r = q;
6650      q = mp_link (q);
6651      mp_free_value_node (mp, r);
6652    }
6653    r = attr_head (p);
6654    q = mp_link (r);
6655    mp_recycle_value (mp, r);
6656    mp_free_value_node (mp, r);
6657    do {
6658      mp_flush_below_variable (mp, q);
6659      r = q;
6660      q = mp_link (q);
6661      mp_free_value_node (mp, r);
6662    } while (q != mp->end_attr);
6663    mp_type (p) = mp_undefined;
6664  }
6665}
6666
6667
6668@ Just before assigning a new value to a variable, we will recycle the
6669old value and make the old value undefined. The |und_type| routine
6670determines what type of undefined value should be given, based on
6671the current type before recycling.
6672
6673@c
6674static quarterword mp_und_type (MP mp, mp_node p) {
6675  (void) mp;
6676  switch (mp_type (p)) {
6677  case mp_vacuous:
6678    return mp_undefined;
6679  case mp_boolean_type:
6680  case mp_unknown_boolean:
6681    return mp_unknown_boolean;
6682  case mp_string_type:
6683  case mp_unknown_string:
6684    return mp_unknown_string;
6685  case mp_pen_type:
6686  case mp_unknown_pen:
6687    return mp_unknown_pen;
6688  case mp_path_type:
6689  case mp_unknown_path:
6690    return mp_unknown_path;
6691  case mp_picture_type:
6692  case mp_unknown_picture:
6693    return mp_unknown_picture;
6694  case mp_transform_type:
6695  case mp_color_type:
6696  case mp_cmykcolor_type:
6697  case mp_pair_type:
6698  case mp_numeric_type:
6699    return mp_type (p);
6700  case mp_known:
6701  case mp_dependent:
6702  case mp_proto_dependent:
6703  case mp_independent:
6704    return mp_numeric_type;
6705  default:                     /* there are no other valid cases, but please the compiler */
6706    return 0;
6707  }
6708  return 0;
6709}
6710
6711
6712@ The |clear_symbol| routine is used when we want to redefine the equivalent
6713of a symbolic token. It must remove any variable structure or macro
6714definition that is currently attached to that symbol. If the |saving|
6715parameter is true, a subsidiary structure is saved instead of destroyed.
6716
6717@c
6718static void mp_clear_symbol (MP mp, mp_sym p, boolean saving) {
6719  mp_node q;    /* |equiv(p)| */
6720  FUNCTION_TRACE3 ("mp_clear_symbol(%p,%d)\n", p, saving);
6721  q = equiv_node (p);
6722  switch (eq_type (p) % mp_outer_tag) {
6723  case mp_defined_macro:
6724  case mp_secondary_primary_macro:
6725  case mp_tertiary_secondary_macro:
6726  case mp_expression_tertiary_macro:
6727    if (!saving)
6728      mp_delete_mac_ref (mp, q);
6729    break;
6730  case mp_tag_token:
6731    if (q != NULL) {
6732      if (saving) {
6733        mp_name_type (q) = mp_saved_root;
6734      } else {
6735        mp_flush_below_variable (mp, q);
6736        mp_free_value_node (mp, q);
6737      }
6738    }
6739    break;
6740  default:
6741    break;
6742  }
6743  set_equiv (p, mp->frozen_undefined->v.data.indep.serial);
6744  set_eq_type (p, mp->frozen_undefined->type);
6745}
6746
6747
6748@* Saving and restoring equivalents.
6749The nested structure given by \&{begingroup} and \&{endgroup}
6750allows |eqtb| entries to be saved and restored, so that temporary changes
6751can be made without difficulty.  When the user requests a current value to
6752be saved, \MP\ puts that value into its ``save stack.'' An appearance of
6753\&{endgroup} ultimately causes the old values to be removed from the save
6754stack and put back in their former places.
6755
6756The save stack is a linked list containing three kinds of entries,
6757distinguished by their |type| fields. If |p| points to a saved item,
6758then
6759
6760\smallskip\hang
6761|p->type=0| stands for a group boundary; each \&{begingroup} contributes
6762such an item to the save stack and each \&{endgroup} cuts back the stack
6763until the most recent such entry has been removed.
6764
6765\smallskip\hang
6766|p->type=mp_normal_sym| means that |p->value| holds the former
6767contents of |eqtb[q]| (saved in the |knot| field of the value, which
6768is otherwise unused for variables). Such save stack entries are generated by \&{save}
6769commands.
6770
6771\smallskip\hang
6772|p->type=mp_internal_sym| means that |p->value| is a |mp_internal|
6773to be restored to internal parameter number~|q| (saved in the |serial| field of the value, which
6774is otherwise unused for internals). Such entries are generated by \&{interim} commands.
6775
6776\smallskip\noindent
6777The global variable |save_ptr| points to the top item on the save stack.
6778
6779@<Types...@>=
6780typedef struct mp_save_data {
6781  quarterword type;
6782  mp_internal value;
6783  struct mp_save_data *link;
6784} mp_save_data;
6785
6786@ @<Glob...@>=
6787mp_save_data *save_ptr; /* the most recently saved item */
6788
6789@ @<Set init...@>=
6790mp->save_ptr = NULL;
6791
6792@ Saving a boundary item
6793@c
6794static void mp_save_boundary (MP mp) {
6795  mp_save_data *p;      /* temporary register */
6796  FUNCTION_TRACE1 ("mp_save_boundary ()\n");
6797  p = xmalloc (1, sizeof (mp_save_data));
6798  p->type = 0;
6799  p->link = mp->save_ptr;
6800  mp->save_ptr = p;
6801}
6802
6803
6804@ The |save_variable| routine is given a hash address |q|; it salts this
6805address in the save stack, together with its current equivalent,
6806then makes token~|q| behave as though it were brand new.
6807
6808Nothing is stacked when |save_ptr=NULL|, however; there's no way to remove
6809things from the stack when the program is not inside a group, so there's
6810no point in wasting the space.
6811
6812@c
6813static void mp_save_variable (MP mp, mp_sym q) {
6814  mp_save_data *p;      /* temporary register */
6815  FUNCTION_TRACE2 ("mp_save_variable (%p)\n", q);
6816  if (mp->save_ptr != NULL) {
6817    p = xmalloc (1, sizeof (mp_save_data));
6818    p->type = mp_normal_sym;
6819    p->link = mp->save_ptr;
6820    p->value.v.data.indep.scale = eq_type (q);
6821    p->value.v.data.indep.serial = equiv(q);
6822    p->value.v.data.node = equiv_node(q);
6823    p->value.v.data.p = (mp_knot)q;
6824    mp->save_ptr = p;
6825  }
6826  mp_clear_symbol (mp, q, (mp->save_ptr != NULL));
6827}
6828static void mp_unsave_variable (MP mp) {
6829  mp_sym q = (mp_sym)mp->save_ptr->value.v.data.p;
6830  if (number_positive(internal_value (mp_tracing_restores))) {
6831    mp_begin_diagnostic (mp);
6832    mp_print_nl (mp, "{restoring ");
6833    mp_print_text (q);
6834    mp_print_char (mp, xord ('}'));
6835    mp_end_diagnostic (mp, false);
6836  }
6837  mp_clear_symbol (mp, q, false);
6838  set_eq_type(q, mp->save_ptr->value.v.data.indep.scale);
6839  set_equiv  (q,mp->save_ptr->value.v.data.indep.serial);
6840  q->v.data.node = mp->save_ptr->value.v.data.node;
6841  if (eq_type (q) % mp_outer_tag == mp_tag_token) {
6842    mp_node pp = q->v.data.node;
6843    if (pp != NULL)
6844      mp_name_type (pp) = mp_root;
6845  }
6846}
6847
6848@ Similarly, |save_internal| is given the location |q| of an internal
6849quantity like |mp_tracing_pens|. It creates a save stack entry of the
6850third kind.
6851
6852@c
6853static void mp_save_internal (MP mp, halfword q) {
6854  mp_save_data *p;      /* new item for the save stack */
6855  FUNCTION_TRACE2 ("mp_save_internal (%d)\n", q);
6856  if (mp->save_ptr != NULL) {
6857    p = xmalloc (1, sizeof (mp_save_data));
6858    p->type = mp_internal_sym;
6859    p->link = mp->save_ptr;
6860    p->value = mp->internal[q];
6861    p->value.v.data.indep.serial = q;
6862    new_number(p->value.v.data.n);
6863    number_clone(p->value.v.data.n, mp->internal[q].v.data.n);
6864    mp->save_ptr = p;
6865  }
6866}
6867
6868static void mp_unsave_internal (MP mp) {
6869  halfword q = mp->save_ptr->value.v.data.indep.serial;
6870  mp_internal saved = mp->save_ptr->value;
6871  if (number_positive(internal_value (mp_tracing_restores))) {
6872    mp_begin_diagnostic (mp);
6873    mp_print_nl (mp, "{restoring ");
6874    mp_print (mp, internal_name (q));
6875    mp_print_char (mp, xord ('='));
6876    if (internal_type (q) == mp_known) {
6877      print_number (saved.v.data.n);
6878    } else if (internal_type (q) == mp_string_type) {
6879      char *s = mp_str (mp, saved.v.data.str);
6880      mp_print (mp, s);
6881    } else {
6882      mp_confusion (mp, "internal_restore");
6883    }
6884    mp_print_char (mp, xord ('}'));
6885    mp_end_diagnostic (mp, false);
6886  }
6887  free_number (mp->internal[q].v.data.n);
6888  mp->internal[q] = saved;
6889}
6890
6891@ At the end of a group, the |unsave| routine restores all of the saved
6892equivalents in reverse order. This routine will be called only when there
6893is at least one boundary item on the save stack.
6894
6895@c
6896static void mp_unsave (MP mp) {
6897  mp_save_data *p;      /* saved item */
6898  FUNCTION_TRACE1 ("mp_unsave ()\n");
6899  while (mp->save_ptr->type != 0) {
6900    if (mp->save_ptr->type == mp_internal_sym) {
6901      mp_unsave_internal(mp);
6902    } else {
6903      mp_unsave_variable(mp);
6904    }
6905    p = mp->save_ptr->link;
6906    xfree (mp->save_ptr);
6907    mp->save_ptr = p;
6908  }
6909  p = mp->save_ptr->link;
6910  xfree (mp->save_ptr);
6911  mp->save_ptr = p;
6912}
6913
6914
6915@* Data structures for paths.
6916When a \MP\ user specifies a path, \MP\ will create a list of knots
6917and control points for the associated cubic spline curves. If the
6918knots are $z_0$, $z_1$, \dots, $z_n$, there are control points
6919$z_k^+$ and $z_{k+1}^-$ such that the cubic splines between knots
6920$z_k$ and $z_{k+1}$ are defined by B\'ezier's formula
6921@:Bezier}{B\'ezier, Pierre Etienne@>
6922$$\eqalign{z(t)&=B(z_k,z_k^+,z_{k+1}^-,z_{k+1};t)\cr
6923&=(1-t)^3z_k+3(1-t)^2tz_k^++3(1-t)t^2z_{k+1}^-+t^3z_{k+1}\cr}$$
6924for |0<=t<=1|.
6925
6926There is a 8-word node for each knot $z_k$, containing one word of
6927control information and six words for the |x| and |y| coordinates of
6928$z_k^-$ and $z_k$ and~$z_k^+$. The control information appears in the
6929|mp_left_type| and |mp_right_type| fields, which each occupy a quarter of
6930the first word in the node; they specify properties of the curve as it
6931enters and leaves the knot. There's also a halfword |link| field,
6932which points to the following knot, and a final supplementary word (of
6933which only a quarter is used).
6934
6935If the path is a closed contour, knots 0 and |n| are identical;
6936i.e., the |link| in knot |n-1| points to knot~0. But if the path
6937is not closed, the |mp_left_type| of knot~0 and the |mp_right_type| of knot~|n|
6938are equal to |endpoint|. In the latter case the |link| in knot~|n| points
6939to knot~0, and the control points $z_0^-$ and $z_n^+$ are not used.
6940
6941@d mp_next_knot(A)   (A)->next /* the next knot in this list */
6942@d mp_left_type(A)   (A)->data.types.left_type /* characterizes the path entering this knot */
6943@d mp_right_type(A)  (A)->data.types.right_type /* characterizes the path leaving this knot */
6944@d mp_prev_knot(A)   (A)->data.prev /* the previous knot in this list (only for pens) */
6945@d mp_knot_info(A)   (A)->data.info /* temporary info, used during splitting */
6946
6947@<Exported types...@>=
6948typedef struct mp_knot_data *mp_knot;
6949typedef struct mp_knot_data {
6950  mp_number x_coord; /* the |x| coordinate of this knot */
6951  mp_number y_coord; /* the |y| coordinate of this knot */
6952  mp_number left_x; /* the |x| coordinate of previous control point */
6953  mp_number left_y; /* the |y| coordinate of previous control point */
6954  mp_number right_x; /* the |x| coordinate of next control point */
6955  mp_number right_y; /* the |y| coordinate of next control point */
6956  mp_knot next;
6957  union {
6958    struct {
6959      unsigned short left_type;
6960      unsigned short right_type;
6961    } types;
6962    mp_knot prev;
6963    signed int info;
6964  } data;
6965  unsigned char originator;
6966} mp_knot_data;
6967
6968
6969@
6970@d mp_gr_next_knot(A)   (A)->next /* the next knot in this list */
6971
6972@<Exported types...@>=
6973typedef struct mp_gr_knot_data *mp_gr_knot;
6974typedef struct mp_gr_knot_data {
6975  double x_coord;
6976  double y_coord;
6977  double left_x;
6978  double left_y;
6979  double right_x;
6980  double right_y;
6981  mp_gr_knot next;
6982  union {
6983    struct {
6984      unsigned short left_type;
6985      unsigned short right_type;
6986    } types;
6987    mp_gr_knot prev;
6988    signed int info;
6989  } data;
6990  unsigned char originator;
6991} mp_gr_knot_data;
6992
6993
6994@ @<MPlib header stuff@>=
6995enum mp_knot_type {
6996  mp_endpoint = 0,      /* |mp_left_type| at path beginning and |mp_right_type| at path end */
6997  mp_explicit,                  /* |mp_left_type| or |mp_right_type| when control points are known */
6998  mp_given,                     /* |mp_left_type| or |mp_right_type| when a direction is given */
6999  mp_curl,                      /* |mp_left_type| or |mp_right_type| when a curl is desired */
7000  mp_open,                      /* |mp_left_type| or |mp_right_type| when \MP\ should choose the direction */
7001  mp_end_cycle
7002};
7003
7004@ Before the B\'ezier control points have been calculated, the memory
7005space they will ultimately occupy is taken up by information that can be
7006used to compute them. There are four cases:
7007
7008\yskip
7009\textindent{$\bullet$} If |mp_right_type=mp_open|, the curve should leave
7010the knot in the same direction it entered; \MP\ will figure out a
7011suitable direction.
7012
7013\yskip
7014\textindent{$\bullet$} If |mp_right_type=mp_curl|, the curve should leave the
7015knot in a direction depending on the angle at which it enters the next
7016knot and on the curl parameter stored in |right_curl|.
7017
7018\yskip
7019\textindent{$\bullet$} If |mp_right_type=mp_given|, the curve should leave the
7020knot in a nonzero direction stored as an |angle| in |right_given|.
7021
7022\yskip
7023\textindent{$\bullet$} If |mp_right_type=mp_explicit|, the B\'ezier control
7024point for leaving this knot has already been computed; it is in the
7025|mp_right_x| and |mp_right_y| fields.
7026
7027\yskip\noindent
7028The rules for |mp_left_type| are similar, but they refer to the curve entering
7029the knot, and to \\{left} fields instead of \\{right} fields.
7030
7031Non-|explicit| control points will be chosen based on ``tension'' parameters
7032in the |left_tension| and |right_tension| fields. The
7033`\&{atleast}' option is represented by negative tension values.
7034@:at_least_}{\&{atleast} primitive@>
7035
7036For example, the \MP\ path specification
7037$$\.{z0..z1..tension atleast 1..\{curl 2\}z2..z3\{-1,-2\}..tension
7038  3 and 4..p},$$
7039where \.p is the path `\.{z4..controls z45 and z54..z5}', will be represented
7040by the six knots
7041\def\lodash{\hbox to 1.1em{\thinspace\hrulefill\thinspace}}
7042$$\vbox{\halign{#\hfil&&\qquad#\hfil\cr
7043|mp_left_type|&\\{left} info&|x_coord,y_coord|&|mp_right_type|&\\{right} info\cr
7044\noalign{\yskip}
7045|endpoint|&\lodash$,\,$\lodash&$x_0,y_0$&|curl|&$1.0,1.0$\cr
7046|open|&\lodash$,1.0$&$x_1,y_1$&|open|&\lodash$,-1.0$\cr
7047|curl|&$2.0,-1.0$&$x_2,y_2$&|curl|&$2.0,1.0$\cr
7048|given|&$d,1.0$&$x_3,y_3$&|given|&$d,3.0$\cr
7049|open|&\lodash$,4.0$&$x_4,y_4$&|explicit|&$x_{45},y_{45}$\cr
7050|explicit|&$x_{54},y_{54}$&$x_5,y_5$&|endpoint|&\lodash$,\,$\lodash\cr}}$$
7051Here |d| is the |angle| obtained by calling |n_arg(-unity,-two)|.
7052Of course, this example is more complicated than anything a normal user
7053would ever write.
7054
7055These types must satisfy certain restrictions because of the form of \MP's
7056path syntax:
7057(i)~|open| type never appears in the same node together with |endpoint|,
7058|given|, or |curl|.
7059(ii)~The |mp_right_type| of a node is |explicit| if and only if the
7060|mp_left_type| of the following node is |explicit|.
7061(iii)~|endpoint| types occur only at the ends, as mentioned above.
7062
7063@d left_curl left_x /* curl information when entering this knot */
7064@d left_given left_x /* given direction when entering this knot */
7065@d left_tension left_y /* tension information when entering this knot */
7066@d right_curl right_x /* curl information when leaving this knot */
7067@d right_given right_x /* given direction when leaving this knot */
7068@d right_tension right_y /* tension information when leaving this knot */
7069
7070@ Knots can be user-supplied, or they can be created by program code,
7071like the |split_cubic| function, or |copy_path|. The distinction is
7072needed for the cleanup routine that runs after |split_cubic|, because
7073it should only delete knots it has previously inserted, and never
7074anything that was user-supplied. In order to be able to differentiate
7075one knot from another, we will set |originator(p):=mp_metapost_user| when
7076it appeared in the actual metapost program, and
7077|originator(p):=mp_program_code| in all other cases.
7078
7079@d mp_originator(A)   (A)->originator /* the creator of this knot */
7080
7081@<Exported types@>=
7082enum mp_knot_originator {
7083  mp_program_code = 0,  /* not created by a user */
7084  mp_metapost_user              /* created by a user */
7085};
7086
7087@ Here is a routine that prints a given knot list
7088in symbolic form. It illustrates the conventions discussed above,
7089and checks for anomalies that might arise while \MP\ is being debugged.
7090
7091@<Declarations@>=
7092static void mp_pr_path (MP mp, mp_knot h);
7093
7094@ @c
7095void mp_pr_path (MP mp, mp_knot h) {
7096  mp_knot p, q; /* for list traversal */
7097  p = h;
7098  do {
7099    q = mp_next_knot (p);
7100    if ((p == NULL) || (q == NULL)) {
7101      mp_print_nl (mp, "???");
7102      return;                   /* this won't happen */
7103@.???@>
7104    }
7105    @<Print information for adjacent knots |p| and |q|@>;
7106  DONE1:
7107    p = q;
7108    if (p && ((p != h) || (mp_left_type (h) != mp_endpoint))) {
7109      @<Print two dots, followed by |given| or |curl| if present@>;
7110    }
7111  } while (p != h);
7112  if (mp_left_type (h) != mp_endpoint)
7113    mp_print (mp, "cycle");
7114}
7115
7116
7117@ @<Print information for adjacent knots...@>=
7118mp_print_two (mp, p->x_coord, p->y_coord);
7119switch (mp_right_type (p)) {
7120case mp_endpoint:
7121  if (mp_left_type (p) == mp_open)
7122    mp_print (mp, "{open?}");   /* can't happen */
7123@.open?@>;
7124  if ((mp_left_type (q) != mp_endpoint) || (q != h))
7125    q = NULL;                   /* force an error */
7126  goto DONE1;
7127  break;
7128case mp_explicit:
7129  @<Print control points between |p| and |q|, then |goto done1|@>;
7130  break;
7131case mp_open:
7132  @<Print information for a curve that begins |open|@>;
7133  break;
7134case mp_curl:
7135case mp_given:
7136  @<Print information for a curve that begins |curl| or |given|@>;
7137  break;
7138default:
7139  mp_print (mp, "???");         /* can't happen */
7140@.???@>;
7141  break;
7142}
7143if (mp_left_type (q) <= mp_explicit) {
7144  mp_print (mp, "..control?");  /* can't happen */
7145@.control?@>
7146} else if ((!number_equal(p->right_tension, unity_t)) || (!number_equal(q->left_tension, unity_t))) {
7147  @<Print tension between |p| and |q|@>;
7148}
7149
7150@ Since |n_sin_cos| produces |fraction| results, which we will print as if they
7151were |scaled|, the magnitude of a |given| direction vector will be~4096.
7152
7153@<Print two dots...@>=
7154{
7155  mp_number n_sin, n_cos;
7156  new_fraction (n_sin);
7157  new_fraction (n_cos);
7158  mp_print_nl (mp, " ..");
7159  if (mp_left_type (p) == mp_given) {
7160    n_sin_cos (p->left_given, n_cos, n_sin);
7161    mp_print_char (mp, xord ('{'));
7162    print_number (n_cos);
7163    mp_print_char (mp, xord (','));
7164    print_number (n_sin);
7165    mp_print_char (mp, xord ('}'));
7166  } else if (mp_left_type (p) == mp_curl) {
7167    mp_print (mp, "{curl ");
7168    print_number (p->left_curl);
7169    mp_print_char (mp, xord ('}'));
7170  }
7171  free_number (n_sin);
7172  free_number (n_cos);
7173}
7174
7175
7176@ @<Print tension between |p| and |q|@>=
7177{
7178  mp_number v1;
7179  new_number (v1);
7180  mp_print (mp, "..tension ");
7181  if (number_negative(p->right_tension))
7182    mp_print (mp, "atleast");
7183  number_clone (v1, p->right_tension);
7184  number_abs (v1);
7185  print_number (v1);
7186  if (!number_equal(p->right_tension, q->left_tension)) {
7187    mp_print (mp, " and ");
7188    if (number_negative(q->left_tension))
7189      mp_print (mp, "atleast");
7190    number_clone (v1, p->left_tension);
7191    number_abs (v1);
7192    print_number (v1);
7193  }
7194  free_number (v1);
7195}
7196
7197
7198@ @<Print control points between |p| and |q|, then |goto done1|@>=
7199{
7200  mp_print (mp, "..controls ");
7201  mp_print_two (mp, p->right_x, p->right_y);
7202  mp_print (mp, " and ");
7203  if (mp_left_type (q) != mp_explicit) {
7204    mp_print (mp, "??");        /* can't happen */
7205@.??@>
7206  } else {
7207    mp_print_two (mp, q->left_x, q->left_y);
7208  }
7209  goto DONE1;
7210}
7211
7212
7213@ @<Print information for a curve that begins |open|@>=
7214if ((mp_left_type (p) != mp_explicit) && (mp_left_type (p) != mp_open)) {
7215  mp_print (mp, "{open?}");     /* can't happen */
7216@.open?@>
7217}
7218
7219@ A curl of 1 is shown explicitly, so that the user sees clearly that
7220\MP's default curl is present.
7221
7222@<Print information for a curve that begins |curl|...@>=
7223{
7224  if (mp_left_type (p) == mp_open)
7225    mp_print (mp, "??");        /* can't happen */
7226@.??@>;
7227  if (mp_right_type (p) == mp_curl) {
7228    mp_print (mp, "{curl ");
7229    print_number (p->right_curl);
7230  } else {
7231    mp_number n_sin, n_cos;
7232    new_fraction (n_sin);
7233    new_fraction (n_cos);
7234    n_sin_cos (p->right_given, n_cos, n_sin);
7235    mp_print_char (mp, xord ('{'));
7236    print_number (n_cos);
7237    mp_print_char (mp, xord (','));
7238    print_number (n_sin);
7239    free_number (n_sin);
7240    free_number (n_cos);
7241  }
7242  mp_print_char (mp, xord ('}'));
7243}
7244
7245
7246@ It is convenient to have another version of |pr_path| that prints the path
7247as a diagnostic message.
7248
7249@<Declarations@>=
7250static void mp_print_path (MP mp, mp_knot h, const char *s, boolean nuline);
7251
7252@ @c
7253void mp_print_path (MP mp, mp_knot h, const char *s, boolean nuline) {
7254  mp_print_diagnostic (mp, "Path", s, nuline);
7255  mp_print_ln (mp);
7256@.Path at line...@>;
7257  mp_pr_path (mp, h);
7258  mp_end_diagnostic (mp, true);
7259}
7260
7261
7262@ @<Declarations@>=
7263static mp_knot mp_new_knot (MP mp);
7264
7265@ @c
7266static mp_knot mp_new_knot (MP mp) {
7267  mp_knot q;
7268  if (mp->knot_nodes) {
7269    q = mp->knot_nodes;
7270    mp->knot_nodes = q->next;
7271    mp->num_knot_nodes--;
7272  } else {
7273    q = mp_xmalloc (mp, 1, sizeof (struct mp_knot_data));
7274  }
7275  memset(q,0,sizeof (struct mp_knot_data));
7276  new_number(q->x_coord);
7277  new_number(q->y_coord);
7278  new_number(q->left_x);
7279  new_number(q->left_y);
7280  new_number(q->right_x);
7281  new_number(q->right_y);
7282  return q;
7283}
7284
7285
7286@ @<Declarations@>=
7287static mp_gr_knot mp_gr_new_knot (MP mp);
7288
7289@ @c
7290static mp_gr_knot mp_gr_new_knot (MP mp) {
7291  mp_gr_knot q = mp_xmalloc (mp, 1, sizeof (struct mp_gr_knot_data));
7292  return q;
7293}
7294
7295
7296@ If we want to duplicate a knot node, we can say |copy_knot|:
7297
7298@c
7299static mp_knot mp_copy_knot (MP mp, mp_knot p) {
7300  mp_knot q;
7301  if (mp->knot_nodes) {
7302    q  = mp->knot_nodes;
7303    mp->knot_nodes = q->next;
7304    mp->num_knot_nodes--;
7305  } else {
7306    q = mp_xmalloc (mp, 1, sizeof (struct mp_knot_data));
7307  }
7308  memcpy (q, p, sizeof (struct mp_knot_data));
7309  if (mp->math_mode > mp_math_double_mode) {
7310    new_number(q->x_coord);
7311    new_number(q->y_coord);
7312    new_number(q->left_x);
7313    new_number(q->left_y);
7314    new_number(q->right_x);
7315    new_number(q->right_y);
7316    number_clone(q->x_coord, p->x_coord);
7317    number_clone(q->y_coord, p->y_coord);
7318    number_clone(q->left_x, p->left_x);
7319    number_clone(q->left_y, p->left_y);
7320    number_clone(q->right_x, p->right_x);
7321    number_clone(q->right_y, p->right_y);
7322  }
7323  mp_next_knot (q) = NULL;
7324  return q;
7325}
7326
7327@ If we want to export a knot node, we can say |export_knot|:
7328
7329@c
7330static mp_gr_knot mp_export_knot (MP mp, mp_knot p) {
7331  mp_gr_knot q;    /* the copy */
7332  q = mp_gr_new_knot (mp);
7333  q->x_coord = number_to_double(p->x_coord);
7334  q->y_coord = number_to_double(p->y_coord);
7335  q->left_x  = number_to_double(p->left_x);
7336  q->left_y  = number_to_double(p->left_y);
7337  q->right_x = number_to_double(p->right_x);
7338  q->right_y = number_to_double(p->right_y);
7339  q->data.types.left_type = mp_left_type(p);
7340  q->data.types.right_type = mp_left_type(p);
7341  q->data.info = mp_knot_info(p);
7342  mp_gr_next_knot (q) = NULL;
7343  return q;
7344}
7345
7346
7347@ The |copy_path| routine makes a clone of a given path.
7348
7349@c
7350static mp_knot mp_copy_path (MP mp, mp_knot p) {
7351  mp_knot q, pp, qq;    /* for list manipulation */
7352  if (p == NULL)
7353    return NULL;
7354  q = mp_copy_knot (mp, p);
7355  qq = q;
7356  pp = mp_next_knot (p);
7357  while (pp != p) {
7358    mp_next_knot (qq) = mp_copy_knot (mp, pp);
7359    qq = mp_next_knot (qq);
7360    pp = mp_next_knot (pp);
7361  }
7362  mp_next_knot (qq) = q;
7363  return q;
7364}
7365
7366@ The |export_path| routine makes a clone of a given path
7367and converts the |value|s therein to |double|s.
7368
7369@c
7370static mp_gr_knot mp_export_path (MP mp, mp_knot p) {
7371  mp_knot pp;    /* for list manipulation */
7372  mp_gr_knot q, qq;
7373  if (p == NULL)
7374    return NULL;
7375  q = mp_export_knot (mp, p);
7376  qq = q;
7377  pp = mp_next_knot (p);
7378  while (pp != p) {
7379    mp_gr_next_knot (qq) = mp_export_knot (mp, pp);
7380    qq = mp_gr_next_knot (qq);
7381    pp = mp_next_knot (pp);
7382  }
7383  mp_gr_next_knot (qq) = q;
7384  return q;
7385}
7386
7387@ If we want to import a knot node, we can say |import_knot|:
7388
7389@c
7390static mp_knot mp_import_knot (MP mp, mp_gr_knot p) {
7391  mp_knot q;    /* the copy */
7392  q = mp_new_knot (mp);
7393  set_number_from_double(q->x_coord, p->x_coord);
7394  set_number_from_double(q->y_coord, p->y_coord);
7395  set_number_from_double(q->left_x, p->left_x);
7396  set_number_from_double(q->left_y, p->left_y);
7397  set_number_from_double(q->right_x, p->right_x);
7398  set_number_from_double(q->right_y, p->right_y);
7399  mp_left_type(q) = p->data.types.left_type;
7400  mp_left_type(q) = p->data.types.right_type;
7401  mp_knot_info(q) = p->data.info;
7402  mp_next_knot (q) = NULL;
7403  return q;
7404}
7405
7406
7407@ The |import_path| routine makes a clone of a given path
7408and converts the |value|s therein to |scaled|s.
7409
7410@c
7411static mp_knot mp_import_path (MP mp, mp_gr_knot p) {
7412  mp_gr_knot pp;    /* for list manipulation */
7413  mp_knot q, qq;
7414  if (p == NULL)
7415    return NULL;
7416  q = mp_import_knot (mp, p);
7417  qq = q;
7418  pp = mp_gr_next_knot (p);
7419  while (pp != p) {
7420    mp_next_knot (qq) = mp_import_knot (mp, pp);
7421    qq = mp_next_knot (qq);
7422    pp = mp_gr_next_knot (pp);
7423  }
7424  mp_next_knot (qq) = q;
7425  return q;
7426}
7427
7428
7429@ Just before |ship_out|, knot lists are exported for printing.
7430
7431@ The |export_knot_list| routine therefore also makes a clone
7432of a given path.
7433
7434@c
7435static mp_gr_knot mp_export_knot_list (MP mp, mp_knot p) {
7436  mp_gr_knot q;    /* the exported copy */
7437  if (p == NULL)
7438    return NULL;
7439  q = mp_export_path (mp, p);
7440  return q;
7441}
7442static mp_knot mp_import_knot_list (MP mp, mp_gr_knot q) {
7443  mp_knot p;    /* the imported copy */
7444  if (q == NULL)
7445    return NULL;
7446  p = mp_import_path (mp, q);
7447  return p;
7448}
7449
7450@ Similarly, there's a way to copy the {\sl reverse\/} of a path. This procedure
7451returns a pointer to the first node of the copy, if the path is a cycle,
7452but to the final node of a non-cyclic copy. The global
7453variable |path_tail| will point to the final node of the original path;
7454this trick makes it easier to implement `\&{doublepath}'.
7455
7456All node types are assumed to be |endpoint| or |explicit| only.
7457
7458@c
7459static mp_knot mp_htap_ypoc (MP mp, mp_knot p) {
7460  mp_knot q, pp, qq, rr;        /* for list manipulation */
7461  q = mp_new_knot (mp);         /* this will correspond to |p| */
7462  qq = q;
7463  pp = p;
7464  while (1) {
7465    mp_right_type (qq) = mp_left_type (pp);
7466    mp_left_type (qq) = mp_right_type (pp);
7467    number_clone (qq->x_coord, pp->x_coord);
7468    number_clone (qq->y_coord, pp->y_coord);
7469    number_clone (qq->right_x, pp->left_x);
7470    number_clone (qq->right_y, pp->left_y);
7471    number_clone (qq->left_x, pp->right_x);
7472    number_clone (qq->left_y, pp->right_y);
7473    mp_originator (qq) = mp_originator (pp);
7474    if (mp_next_knot (pp) == p) {
7475      mp_next_knot (q) = qq;
7476      mp->path_tail = pp;
7477      return q;
7478    }
7479    rr = mp_new_knot (mp);
7480    mp_next_knot (rr) = qq;
7481    qq = rr;
7482    pp = mp_next_knot (pp);
7483  }
7484}
7485
7486
7487@ @<Glob...@>=
7488mp_knot path_tail;      /* the node that links to the beginning of a path */
7489
7490@ When a cyclic list of knot nodes is no longer needed, it can be recycled by
7491calling the following subroutine.
7492
7493@<Declarations@>=
7494static void mp_toss_knot_list (MP mp, mp_knot p);
7495static void mp_toss_knot (MP mp, mp_knot p);
7496static void mp_free_knot (MP mp, mp_knot p);
7497
7498@ @c
7499void mp_free_knot  (MP mp, mp_knot q) {
7500  free_number (q->x_coord);
7501  free_number (q->y_coord);
7502  free_number (q->left_x);
7503  free_number (q->left_y);
7504  free_number (q->right_x);
7505  free_number (q->right_y);
7506  mp_xfree (q);
7507}
7508void mp_toss_knot (MP mp, mp_knot q) {
7509  if (mp->num_knot_nodes < max_num_knot_nodes) {
7510    q->next = mp->knot_nodes;
7511    mp->knot_nodes = q;
7512    mp->num_knot_nodes++;
7513    return;
7514  }
7515  if (mp->math_mode > mp_math_double_mode) {
7516    mp_free_knot(mp,q);
7517  } else {
7518    mp_xfree (q);
7519  }
7520}
7521void mp_toss_knot_list (MP mp, mp_knot p) {
7522  mp_knot q;    /* the node being freed */
7523  mp_knot r;    /* the next node */
7524  if (p == NULL)
7525    return;
7526  q = p;
7527  if (mp->math_mode > mp_math_double_mode) {
7528    do {
7529      r = mp_next_knot (q);
7530      mp_toss_knot(mp, q);
7531      q = r;
7532    } while (q != p);
7533  } else {
7534    do {
7535      r = mp_next_knot (q);
7536      if (mp->num_knot_nodes < max_num_knot_nodes) {
7537        q->next = mp->knot_nodes;
7538	mp->knot_nodes = q;
7539	mp->num_knot_nodes++;
7540      } else {
7541        mp_xfree (q);
7542      }
7543      q = r;
7544    } while (q != p);
7545  }
7546}
7547
7548
7549@* Choosing control points.
7550Now we must actually delve into one of \MP's more difficult routines,
7551the |make_choices| procedure that chooses angles and control points for
7552the splines of a curve when the user has not specified them explicitly.
7553The parameter to |make_choices| points to a list of knots and
7554path information, as described above.
7555
7556A path decomposes into independent segments at ``breakpoint'' knots,
7557which are knots whose left and right angles are both prespecified in
7558some way (i.e., their |mp_left_type| and |mp_right_type| aren't both open).
7559
7560@c
7561void mp_make_choices (MP mp, mp_knot knots) {
7562  mp_knot h;    /* the first breakpoint */
7563  mp_knot p, q; /* consecutive breakpoints being processed */
7564  @<Other local variables for |make_choices|@>;
7565  FUNCTION_TRACE1 ("make_choices()\n");
7566  check_arith();                  /* make sure that |arith_error=false| */
7567  if (number_positive(internal_value (mp_tracing_choices)))
7568    mp_print_path (mp, knots, ", before choices", true);
7569  @<If consecutive knots are equal, join them explicitly@>;
7570  @<Find the first breakpoint, |h|, on the path;
7571    insert an artificial breakpoint if the path is an unbroken cycle@>;
7572  p = h;
7573  do {
7574    @<Fill in the control points between |p| and the next breakpoint,
7575      then advance |p| to that breakpoint@>;
7576  } while (p != h);
7577  if (number_positive(internal_value (mp_tracing_choices)))
7578    mp_print_path (mp, knots, ", after choices", true);
7579  if (mp->arith_error) {
7580    @<Report an unexpected problem during the choice-making@>;
7581  }
7582}
7583
7584@ @<Internal ...@>=
7585void mp_make_choices (MP mp, mp_knot knots);
7586
7587@ @<Report an unexpected problem during the choice...@>=
7588{
7589  const char *hlp[] = {
7590         "The path that I just computed is out of range.",
7591         "So it will probably look funny. Proceed, for a laugh.",
7592          NULL };
7593  mp_back_error (mp, "Some number got too big", hlp, true);
7594@.Some number got too big@>;
7595  mp_get_x_next (mp);
7596  mp->arith_error = false;
7597}
7598
7599
7600@ Two knots in a row with the same coordinates will always be joined
7601by an explicit ``curve'' whose control points are identical with the
7602knots.
7603
7604@<If consecutive knots are equal, join them explicitly@>=
7605p = knots;
7606do {
7607  q = mp_next_knot (p);
7608  if (number_equal (p->x_coord, q->x_coord) &&
7609      number_equal (p->y_coord, q->y_coord) &&
7610      mp_right_type (p) > mp_explicit) {
7611    mp_right_type (p) = mp_explicit;
7612    if (mp_left_type (p) == mp_open) {
7613      mp_left_type (p) = mp_curl;
7614      set_number_to_unity(p->left_curl);
7615    }
7616    mp_left_type (q) = mp_explicit;
7617    if (mp_right_type (q) == mp_open) {
7618      mp_right_type (q) = mp_curl;
7619      set_number_to_unity(q->right_curl);
7620    }
7621    number_clone (p->right_x, p->x_coord);
7622    number_clone (q->left_x, p->x_coord);
7623    number_clone (p->right_y, p->y_coord);
7624    number_clone (q->left_y, p->y_coord);
7625  }
7626  p = q;
7627} while (p != knots)
7628
7629@ If there are no breakpoints, it is necessary to compute the direction
7630angles around an entire cycle. In this case the |mp_left_type| of the first
7631node is temporarily changed to |end_cycle|.
7632
7633@<Find the first breakpoint, |h|, on the path...@>=
7634h = knots;
7635while (1) {
7636  if (mp_left_type (h) != mp_open)
7637    break;
7638  if (mp_right_type (h) != mp_open)
7639    break;
7640  h = mp_next_knot (h);
7641  if (h == knots) {
7642    mp_left_type (h) = mp_end_cycle;
7643    break;
7644  }
7645}
7646
7647
7648@ If |mp_right_type(p)<given| and |q=mp_link(p)|, we must have
7649|mp_right_type(p)=mp_left_type(q)=mp_explicit| or |endpoint|.
7650
7651@<Fill in the control points between |p| and the next breakpoint...@>=
7652q = mp_next_knot (p);
7653if (mp_right_type (p) >= mp_given) {
7654  while ((mp_left_type (q) == mp_open) && (mp_right_type (q) == mp_open)) {
7655    q = mp_next_knot (q);
7656  }
7657  @<Fill in the control information between consecutive breakpoints |p| and |q|@>;
7658} else if (mp_right_type (p) == mp_endpoint) {
7659  @<Give reasonable values for the unused control points between |p| and~|q|@>;
7660}
7661p = q
7662
7663@ This step makes it possible to transform an explicitly computed path without
7664checking the |mp_left_type| and |mp_right_type| fields.
7665
7666@<Give reasonable values for the unused control points between |p| and~|q|@>=
7667{
7668  number_clone (p->right_x, p->x_coord);
7669  number_clone (p->right_y, p->y_coord);
7670  number_clone (q->left_x, q->x_coord);
7671  number_clone (q->left_y, q->y_coord);
7672}
7673
7674
7675@ Before we can go further into the way choices are made, we need to
7676consider the underlying theory. The basic ideas implemented in |make_choices|
7677are due to John Hobby, who introduced the notion of ``mock curvature''
7678@^Hobby, John Douglas@>
7679at a knot. Angles are chosen so that they preserve mock curvature when
7680a knot is passed, and this has been found to produce excellent results.
7681
7682It is convenient to introduce some notations that simplify the necessary
7683formulas. Let $d_{k,k+1}=\vert z\k-z_k\vert$ be the (nonzero) distance
7684between knots |k| and |k+1|; and let
7685$${z\k-z_k\over z_k-z_{k-1}}={d_{k,k+1}\over d_{k-1,k}}e^{i\psi_k}$$
7686so that a polygonal line from $z_{k-1}$ to $z_k$ to $z\k$ turns left
7687through an angle of~$\psi_k$. We assume that $\vert\psi_k\vert\L180^\circ$.
7688The control points for the spline from $z_k$ to $z\k$ will be denoted by
7689$$\eqalign{z_k^+&=z_k+
7690  \textstyle{1\over3}\rho_k e^{i\theta_k}(z\k-z_k),\cr
7691 z\k^-&=z\k-
7692  \textstyle{1\over3}\sigma\k e^{-i\phi\k}(z\k-z_k),\cr}$$
7693where $\rho_k$ and $\sigma\k$ are nonnegative ``velocity ratios'' at the
7694beginning and end of the curve, while $\theta_k$ and $\phi\k$ are the
7695corresponding ``offset angles.'' These angles satisfy the condition
7696$$\theta_k+\phi_k+\psi_k=0,\eqno(*)$$
7697whenever the curve leaves an intermediate knot~|k| in the direction that
7698it enters.
7699
7700@ Let $\alpha_k$ and $\beta\k$ be the reciprocals of the ``tension'' of
7701the curve at its beginning and ending points. This means that
7702$\rho_k=\alpha_k f(\theta_k,\phi\k)$ and $\sigma\k=\beta\k f(\phi\k,\theta_k)$,
7703where $f(\theta,\phi)$ is \MP's standard velocity function defined in
7704the |velocity| subroutine. The cubic spline $B(z_k^{\phantom+},z_k^+,
7705z\k^-,z\k^{\phantom+};t)$
7706has curvature
7707@^curvature@>
7708$${2\sigma\k\sin(\theta_k+\phi\k)-6\sin\theta_k\over\rho_k^2d_{k,k+1}}
7709\qquad{\rm and}\qquad
7710{2\rho_k\sin(\theta_k+\phi\k)-6\sin\phi\k\over\sigma\k^2d_{k,k+1}}$$
7711at |t=0| and |t=1|, respectively. The mock curvature is the linear
7712@^mock curvature@>
7713approximation to this true curvature that arises in the limit for
7714small $\theta_k$ and~$\phi\k$, if second-order terms are discarded.
7715The standard velocity function satisfies
7716$$f(\theta,\phi)=1+O(\theta^2+\theta\phi+\phi^2);$$
7717hence the mock curvatures are respectively
7718$${2\beta\k(\theta_k+\phi\k)-6\theta_k\over\alpha_k^2d_{k,k+1}}
7719\qquad{\rm and}\qquad
7720{2\alpha_k(\theta_k+\phi\k)-6\phi\k\over\beta\k^2d_{k,k+1}}.\eqno(**)$$
7721
7722@ The turning angles $\psi_k$ are given, and equation $(*)$ above
7723determines $\phi_k$ when $\theta_k$ is known, so the task of
7724angle selection is essentially to choose appropriate values for each
7725$\theta_k$. When equation~$(*)$ is used to eliminate $\phi$~variables
7726from $(**)$, we obtain a system of linear equations of the form
7727$$A_k\theta_{k-1}+(B_k+C_k)\theta_k+D_k\theta\k=-B_k\psi_k-D_k\psi\k,$$
7728where
7729$$A_k={\alpha_{k-1}\over\beta_k^2d_{k-1,k}},
7730\qquad B_k={3-\alpha_{k-1}\over\beta_k^2d_{k-1,k}},
7731\qquad C_k={3-\beta\k\over\alpha_k^2d_{k,k+1}},
7732\qquad D_k={\beta\k\over\alpha_k^2d_{k,k+1}}.$$
7733The tensions are always $3\over4$ or more, hence each $\alpha$ and~$\beta$
7734will be at most $4\over3$. It follows that $B_k\G{5\over4}A_k$ and
7735$C_k\G{5\over4}D_k$; hence the equations are diagonally dominant;
7736hence they have a unique solution. Moreover, in most cases the tensions
7737are equal to~1, so that $B_k=2A_k$ and $C_k=2D_k$. This makes the
7738solution numerically stable, and there is an exponential damping
7739effect: The data at knot $k\pm j$ affects the angle at knot~$k$ by
7740a factor of~$O(2^{-j})$.
7741
7742@ However, we still must consider the angles at the starting and ending
7743knots of a non-cyclic path. These angles might be given explicitly, or
7744they might be specified implicitly in terms of an amount of ``curl.''
7745
7746Let's assume that angles need to be determined for a non-cyclic path
7747starting at $z_0$ and ending at~$z_n$. Then equations of the form
7748$$A_k\theta_{k-1}+(B_k+C_k)\theta_k+D_k\theta_{k+1}=R_k$$
7749have been given for $0<k<n$, and it will be convenient to introduce
7750equations of the same form for $k=0$ and $k=n$, where
7751$$A_0=B_0=C_n=D_n=0.$$
7752If $\theta_0$ is supposed to have a given value $E_0$, we simply
7753define $C_0=1$, $D_0=0$, and $R_0=E_0$. Otherwise a curl
7754parameter, $\gamma_0$, has been specified at~$z_0$; this means
7755that the mock curvature at $z_0$ should be $\gamma_0$ times the
7756mock curvature at $z_1$; i.e.,
7757$${2\beta_1(\theta_0+\phi_1)-6\theta_0\over\alpha_0^2d_{01}}
7758=\gamma_0{2\alpha_0(\theta_0+\phi_1)-6\phi_1\over\beta_1^2d_{01}}.$$
7759This equation simplifies to
7760$$(\alpha_0\chi_0+3-\beta_1)\theta_0+
7761 \bigl((3-\alpha_0)\chi_0+\beta_1\bigr)\theta_1=
7762 -\bigl((3-\alpha_0)\chi_0+\beta_1\bigr)\psi_1,$$
7763where $\chi_0=\alpha_0^2\gamma_0/\beta_1^2$; so we can set $C_0=
7764\chi_0\alpha_0+3-\beta_1$, $D_0=(3-\alpha_0)\chi_0+\beta_1$, $R_0=-D_0\psi_1$.
7765It can be shown that $C_0>0$ and $C_0B_1-A_1D_0>0$ when $\gamma_0\G0$,
7766hence the linear equations remain nonsingular.
7767
7768Similar considerations apply at the right end, when the final angle $\phi_n$
7769may or may not need to be determined. It is convenient to let $\psi_n=0$,
7770hence $\theta_n=-\phi_n$. We either have an explicit equation $\theta_n=E_n$,
7771or we have
7772$$\bigl((3-\beta_n)\chi_n+\alpha_{n-1}\bigr)\theta_{n-1}+
7773(\beta_n\chi_n+3-\alpha_{n-1})\theta_n=0,\qquad
7774  \chi_n={\beta_n^2\gamma_n\over\alpha_{n-1}^2}.$$
7775
7776When |make_choices| chooses angles, it must compute the coefficients of
7777these linear equations, then solve the equations. To compute the coefficients,
7778it is necessary to compute arctangents of the given turning angles~$\psi_k$.
7779When the equations are solved, the chosen directions $\theta_k$ are put
7780back into the form of control points by essentially computing sines and
7781cosines.
7782
7783@ OK, we are ready to make the hard choices of |make_choices|.
7784Most of the work is relegated to an auxiliary procedure
7785called |solve_choices|, which has been introduced to keep
7786|make_choices| from being extremely long.
7787
7788@<Fill in the control information between...@>=
7789@<Calculate the turning angles $\psi_k$ and the distances $d_{k,k+1}$;
7790  set $n$ to the length of the path@>;
7791@<Remove |open| types at the breakpoints@>;
7792mp_solve_choices (mp, p, q, n)
7793
7794
7795@ It's convenient to precompute quantities that will be needed several
7796times later. The values of |delta_x[k]| and |delta_y[k]| will be the
7797coordinates of $z\k-z_k$, and the magnitude of this vector will be
7798|delta[k]=@t$d_{k,k+1}$@>|. The path angle $\psi_k$ between $z_k-z_{k-1}$
7799and $z\k-z_k$ will be stored in |psi[k]|.
7800
7801@<Glob...@>=
7802int path_size;  /* maximum number of knots between breakpoints of a path */
7803mp_number *delta_x;
7804mp_number *delta_y;
7805mp_number *delta;  /* knot differences */
7806mp_number *psi;     /* turning angles */
7807
7808@ @<Dealloc variables@>=
7809{
7810  int k;
7811  for (k = 0; k<mp->path_size; k++) {
7812    free_number (mp->delta_x[k]);
7813    free_number (mp->delta_y[k]);
7814    free_number (mp->delta[k]);
7815    free_number (mp->psi[k]);
7816  }
7817  xfree (mp->delta_x);
7818  xfree (mp->delta_y);
7819  xfree (mp->delta);
7820  xfree (mp->psi);
7821}
7822
7823@ @<Other local variables for |make_choices|@>=
7824int k, n;       /* current and final knot numbers */
7825mp_knot s, t;   /* registers for list traversal */
7826
7827@ @<Calculate the turning angles...@>=
7828{
7829  mp_number sine, cosine;  /* trig functions of various angles */
7830  new_fraction (sine);
7831  new_fraction (cosine);
7832RESTART:
7833  k = 0;
7834  s = p;
7835  n = mp->path_size;
7836  do {
7837    t = mp_next_knot (s);
7838    set_number_from_substraction(mp->delta_x[k], t->x_coord, s->x_coord);
7839    set_number_from_substraction(mp->delta_y[k], t->y_coord, s->y_coord);
7840    pyth_add (mp->delta[k], mp->delta_x[k], mp->delta_y[k]);
7841    if (k > 0) {
7842      mp_number arg1, arg2, r1, r2;
7843      new_number (arg1);
7844      new_number (arg2);
7845      new_fraction (r1);
7846      new_fraction (r2);
7847      make_fraction (r1, mp->delta_y[k - 1], mp->delta[k - 1]);
7848      number_clone (sine, r1);
7849      make_fraction (r2, mp->delta_x[k - 1], mp->delta[k - 1]);
7850      number_clone (cosine, r2);
7851      take_fraction (r1, mp->delta_x[k], cosine);
7852      take_fraction (r2, mp->delta_y[k], sine);
7853      set_number_from_addition (arg1, r1, r2);
7854      take_fraction (r1, mp->delta_y[k], cosine);
7855      take_fraction (r2, mp->delta_x[k], sine);
7856      set_number_from_substraction (arg2, r1, r2);
7857      n_arg (mp->psi[k], arg1, arg2 );
7858      free_number (r1);
7859      free_number (r2);
7860      free_number (arg1);
7861      free_number (arg2);
7862    }
7863    incr (k);
7864    s = t;
7865    if (k == mp->path_size) {
7866      mp_reallocate_paths (mp, mp->path_size + (mp->path_size / 4));
7867      goto RESTART;             /* retry, loop size has changed */
7868    }
7869    if (s == q)
7870      n = k;
7871  } while (!((k >= n) && (mp_left_type (s) != mp_end_cycle)));
7872  if (k == n)
7873    set_number_to_zero(mp->psi[k]);
7874  else
7875    number_clone(mp->psi[k], mp->psi[1]);
7876  free_number (sine);
7877  free_number (cosine);
7878}
7879
7880
7881@ When we get to this point of the code, |mp_right_type(p)| is either
7882|given| or |curl| or |open|. If it is |open|, we must have
7883|mp_left_type(p)=mp_end_cycle| or |mp_left_type(p)=mp_explicit|. In the latter
7884case, the |open| type is converted to |given|; however, if the
7885velocity coming into this knot is zero, the |open| type is
7886converted to a |curl|, since we don't know the incoming direction.
7887
7888Similarly, |mp_left_type(q)| is either |given| or |curl| or |open| or
7889|mp_end_cycle|. The |open| possibility is reduced either to |given| or to |curl|.
7890
7891@<Remove |open| types at the breakpoints@>=
7892{
7893  mp_number delx, dely;      /* directions where |open| meets |explicit| */
7894  new_number(delx);
7895  new_number(dely);
7896  if (mp_left_type (q) == mp_open) {
7897    set_number_from_substraction(delx, q->right_x, q->x_coord);
7898    set_number_from_substraction(dely, q->right_y, q->y_coord);
7899    if (number_zero(delx) && number_zero(dely)) {
7900      mp_left_type (q) = mp_curl;
7901      set_number_to_unity(q->left_curl);
7902    } else {
7903      mp_left_type (q) = mp_given;
7904      n_arg (q->left_given, delx, dely);
7905    }
7906  }
7907  if ((mp_right_type (p) == mp_open) && (mp_left_type (p) == mp_explicit)) {
7908    set_number_from_substraction(delx, p->x_coord, p->left_x);
7909    set_number_from_substraction(dely, p->y_coord, p->left_y);
7910    if (number_zero(delx) && number_zero(dely)) {
7911      mp_right_type (p) = mp_curl;
7912      set_number_to_unity(p->right_curl);
7913    } else {
7914      mp_right_type (p) = mp_given;
7915      n_arg (p->right_given, delx, dely);
7916    }
7917  }
7918  free_number (delx);
7919  free_number (dely);
7920}
7921
7922@ Linear equations need to be solved whenever |n>1|; and also when |n=1|
7923and exactly one of the breakpoints involves a curl. The simplest case occurs
7924when |n=1| and there is a curl at both breakpoints; then we simply draw
7925a straight line.
7926
7927But before coding up the simple cases, we might as well face the general case,
7928since we must deal with it sooner or later, and since the general case
7929is likely to give some insight into the way simple cases can be handled best.
7930
7931When there is no cycle, the linear equations to be solved form a tridiagonal
7932system, and we can apply the standard technique of Gaussian elimination
7933to convert that system to a sequence of equations of the form
7934$$\theta_0+u_0\theta_1=v_0,\quad
7935\theta_1+u_1\theta_2=v_1,\quad\ldots,\quad
7936\theta_{n-1}+u_{n-1}\theta_n=v_{n-1},\quad
7937\theta_n=v_n.$$
7938It is possible to do this diagonalization while generating the equations.
7939Once $\theta_n$ is known, it is easy to determine $\theta_{n-1}$, \dots,
7940$\theta_1$, $\theta_0$; thus, the equations will be solved.
7941
7942The procedure is slightly more complex when there is a cycle, but the
7943basic idea will be nearly the same. In the cyclic case the right-hand
7944sides will be $v_k+w_k\theta_0$ instead of simply $v_k$, and we will start
7945the process off with $u_0=v_0=0$, $w_0=1$. The final equation will be not
7946$\theta_n=v_n$ but $\theta_n+u_n\theta_1=v_n+w_n\theta_0$; an appropriate
7947ending routine will take account of the fact that $\theta_n=\theta_0$ and
7948eliminate the $w$'s from the system, after which the solution can be
7949obtained as before.
7950
7951When $u_k$, $v_k$, and $w_k$ are being computed, the three pointer
7952variables |r|, |s|,~|t| will point respectively to knots |k-1|, |k|,
7953and~|k+1|. The $u$'s and $w$'s are scaled by $2^{28}$, i.e., they are
7954of type |fraction|; the $\theta$'s and $v$'s are of type |angle|.
7955
7956@<Glob...@>=
7957mp_number *theta;   /* values of $\theta_k$ */
7958mp_number *uu;   /* values of $u_k$ */
7959mp_number *vv;      /* values of $v_k$ */
7960mp_number *ww;   /* values of $w_k$ */
7961
7962@ @<Dealloc variables@>=
7963{
7964  int k;
7965  for (k = 0; k<mp->path_size; k++) {
7966    free_number (mp->theta[k]);
7967    free_number (mp->uu[k]);
7968    free_number (mp->vv[k]);
7969    free_number (mp->ww[k]);
7970  }
7971  xfree (mp->theta);
7972  xfree (mp->uu);
7973  xfree (mp->vv);
7974  xfree (mp->ww);
7975}
7976
7977@ @<Declarations@>=
7978static void mp_reallocate_paths (MP mp, int l);
7979
7980@ @c
7981void mp_reallocate_paths (MP mp, int l) {
7982  int k;
7983  XREALLOC (mp->delta_x, l, mp_number);
7984  XREALLOC (mp->delta_y, l, mp_number);
7985  XREALLOC (mp->delta, l, mp_number);
7986  XREALLOC (mp->psi, l, mp_number);
7987  XREALLOC (mp->theta, l, mp_number);
7988  XREALLOC (mp->uu, l, mp_number);
7989  XREALLOC (mp->vv, l, mp_number);
7990  XREALLOC (mp->ww, l, mp_number);
7991  for (k = mp->path_size; k<l; k++) {
7992    new_number (mp->delta_x[k]);
7993    new_number (mp->delta_y[k]);
7994    new_number (mp->delta[k]);
7995    new_angle (mp->psi[k]);
7996    new_angle (mp->theta[k]);
7997    new_fraction (mp->uu[k]);
7998    new_angle (mp->vv[k]);
7999    new_fraction (mp->ww[k]);
8000  }
8001  mp->path_size = l;
8002}
8003
8004
8005@ Our immediate problem is to get the ball rolling by setting up the
8006first equation or by realizing that no equations are needed, and to fit
8007this initialization into a framework suitable for the overall computation.
8008
8009@<Declarations@>=
8010static void mp_solve_choices (MP mp, mp_knot p, mp_knot q, halfword n);
8011
8012@ @c
8013void mp_solve_choices (MP mp, mp_knot p, mp_knot q, halfword n) {
8014  int k;        /* current knot number */
8015  mp_knot r, s, t;      /* registers for list traversal */
8016  mp_number ff;
8017  new_fraction (ff);
8018  FUNCTION_TRACE2 ("solve_choices(%d)\n", n);
8019  k = 0;
8020  s = p;
8021  r = 0;
8022  while (1) {
8023    t = mp_next_knot (s);
8024    if (k == 0) {
8025      @<Get the linear equations started; or |return|
8026        with the control points in place, if linear equations
8027        needn't be solved@>
8028    } else {
8029      switch (mp_left_type (s)) {
8030      case mp_end_cycle:
8031      case mp_open:
8032        @<Set up equation to match mock curvatures
8033          at $z_k$; then |goto found| with $\theta_n$
8034          adjusted to equal $\theta_0$, if a cycle has ended@>;
8035        break;
8036      case mp_curl:
8037        @<Set up equation for a curl at $\theta_n$
8038          and |goto found|@>;
8039        break;
8040      case mp_given:
8041        @<Calculate the given value of $\theta_n$
8042          and |goto found|@>;
8043        break;
8044      }                         /* there are no other cases */
8045    }
8046    r = s;
8047    s = t;
8048    incr (k);
8049  }
8050FOUND:
8051  @<Finish choosing angles and assigning control points@>;
8052  free_number (ff);
8053}
8054
8055
8056@ On the first time through the loop, we have |k=0| and |r| is not yet
8057defined. The first linear equation, if any, will have $A_0=B_0=0$.
8058
8059@<Get the linear equations started...@>=
8060switch (mp_right_type (s)) {
8061case mp_given:
8062  if (mp_left_type (t) == mp_given) {
8063    @<Reduce to simple case of two givens  and |return|@>
8064  } else {
8065    @<Set up the equation for a given value of $\theta_0$@>;
8066  }
8067  break;
8068case mp_curl:
8069  if (mp_left_type (t) == mp_curl) {
8070    @<Reduce to simple case of straight line and |return|@>
8071  } else {
8072    @<Set up the equation for a curl at $\theta_0$@>;
8073  }
8074  break;
8075case mp_open:
8076  set_number_to_zero(mp->uu[0]);
8077  set_number_to_zero(mp->vv[0]);
8078  number_clone(mp->ww[0], fraction_one_t);
8079  /* this begins a cycle */
8080  break;
8081}                               /* there are no other cases */
8082
8083
8084@ The general equation that specifies equality of mock curvature at $z_k$ is
8085$$A_k\theta_{k-1}+(B_k+C_k)\theta_k+D_k\theta\k=-B_k\psi_k-D_k\psi\k,$$
8086as derived above. We want to combine this with the already-derived equation
8087$\theta_{k-1}+u_{k-1}\theta_k=v_{k-1}+w_{k-1}\theta_0$ in order to obtain
8088a new equation
8089$\theta_k+u_k\theta\k=v_k+w_k\theta_0$. This can be done by dividing the
8090equation
8091$$(B_k-u_{k-1}A_k+C_k)\theta_k+D_k\theta\k=-B_k\psi_k-D_k\psi\k-A_kv_{k-1}
8092    -A_kw_{k-1}\theta_0$$
8093by $B_k-u_{k-1}A_k+C_k$. The trick is to do this carefully with
8094fixed-point arithmetic, avoiding the chance of overflow while retaining
8095suitable precision.
8096
8097The calculations will be performed in several registers that
8098provide temporary storage for intermediate quantities.
8099
8100@ @<Set up equation to match mock curvatures...@>=
8101{
8102  mp_number aa, bb, cc, acc;   /* temporary registers */
8103  mp_number dd, ee;  /* likewise, but |scaled| */
8104  new_fraction (aa);
8105  new_fraction (bb);
8106  new_fraction (cc);
8107  new_fraction (acc);
8108  new_number (dd);
8109  new_number (ee);
8110  @<Calculate the values $\\{aa}=A_k/B_k$, $\\{bb}=D_k/C_k$,
8111    $\\{dd}=(3-\alpha_{k-1})d_{k,k+1}$, $\\{ee}=(3-\beta\k)d_{k-1,k}$,
8112    and $\\{cc}=(B_k-u_{k-1}A_k)/B_k$@>;
8113  @<Calculate the ratio $\\{ff}=C_k/(C_k+B_k-u_{k-1}A_k)$@>;
8114  take_fraction (mp->uu[k], ff, bb);
8115  @<Calculate the values of $v_k$ and $w_k$@>;
8116  if (mp_left_type (s) == mp_end_cycle) {
8117    @<Adjust $\theta_n$ to equal $\theta_0$ and |goto found|@>;
8118  }
8119  free_number(aa);
8120  free_number(bb);
8121  free_number(cc);
8122  free_number(acc);
8123  free_number(dd);
8124  free_number(ee);
8125}
8126
8127
8128@ Since tension values are never less than 3/4, the values |aa| and
8129|bb| computed here are never more than 4/5.
8130
8131@<Calculate the values $\\{aa}=...@>=
8132{
8133  mp_number absval;
8134  new_number (absval);
8135  number_clone (absval, r->right_tension);
8136  number_abs (absval);
8137if (number_equal (absval, unity_t)) {
8138  number_clone (aa, fraction_half_t);
8139  number_clone (dd, mp->delta[k]);
8140  number_double (dd);
8141} else {
8142  mp_number arg1, arg2, ret;
8143  new_number (arg2);
8144  new_number (arg1);
8145  number_clone (arg2, r->right_tension);
8146  number_abs (arg2);
8147  number_multiply_int (arg2, 3);
8148  number_substract (arg2, unity_t);
8149  make_fraction (aa, unity_t, arg2);
8150  number_clone (arg2, r->right_tension);
8151  number_abs (arg2);
8152  new_fraction (ret);
8153  make_fraction (ret, unity_t, arg2);
8154  set_number_from_substraction (arg1, fraction_three_t, ret);
8155  take_fraction (arg2, mp->delta[k], arg1);
8156  number_clone (dd, arg2);
8157  free_number (ret);
8158  free_number (arg1);
8159  free_number (arg2);
8160}
8161  number_clone (absval, t->left_tension);
8162  number_abs (absval);
8163if (number_equal (absval, unity_t)) {
8164  number_clone (bb, fraction_half_t);
8165  number_clone (ee, mp->delta[k - 1]);
8166  number_double (ee);
8167} else {
8168  mp_number arg1, arg2, ret;
8169  new_number (arg1);
8170  new_number (arg2);
8171  number_clone (arg2, t->left_tension);
8172  number_abs (arg2);
8173  number_multiply_int (arg2, 3);
8174  number_substract (arg2, unity_t);
8175  make_fraction (bb, unity_t, arg2);
8176  number_clone (arg2, t->left_tension);
8177  number_abs (arg2);
8178  new_fraction(ret);
8179  make_fraction (ret, unity_t, arg2);
8180  set_number_from_substraction (arg1,fraction_three_t, ret);
8181  take_fraction (ee, mp->delta[k - 1], arg1);
8182  free_number (ret);
8183  free_number (arg1);
8184  free_number (arg2);
8185}
8186free_number (absval);
8187}
8188{
8189  mp_number r1;
8190  new_number (r1);
8191  take_fraction (r1, mp->uu[k - 1], aa);
8192  set_number_from_substraction (cc, fraction_one_t, r1);
8193  free_number (r1);
8194}
8195
8196@ The ratio to be calculated in this step can be written in the form
8197$$\beta_k^2\cdot\\{ee}\over\beta_k^2\cdot\\{ee}+\alpha_k^2\cdot
8198  \\{cc}\cdot\\{dd},$$
8199because of the quantities just calculated. The values of |dd| and |ee|
8200will not be needed after this step has been performed.
8201
8202@<Calculate the ratio $\\{ff}=C_k/(C_k+B_k-u_{k-1}A_k)$@>=
8203{
8204  mp_number rt, lt;
8205  mp_number arg2;
8206  new_number (arg2);
8207  number_clone (arg2, dd);
8208  take_fraction (dd, arg2, cc);
8209  new_number (lt);
8210  new_number (rt);
8211  number_clone (lt, s->left_tension);
8212  number_abs (lt);
8213  number_clone (rt, s->right_tension);
8214  number_abs (rt);
8215  if (!number_equal(lt, rt)) {                 /* $\beta_k^{-1}\ne\alpha_k^{-1}$ */
8216    mp_number r1;
8217    new_number (r1);
8218    if (number_less(lt, rt)) {
8219      make_fraction (r1, lt, rt);  /* $\alpha_k^2/\beta_k^2$ */
8220      take_fraction (ff, r1, r1);
8221      number_clone (r1, dd);
8222      take_fraction (dd, r1, ff);
8223    } else {
8224      make_fraction (r1, rt, lt);  /* $\beta_k^2/\alpha_k^2$ */
8225      take_fraction (ff, r1, r1);
8226      number_clone (r1, ee);
8227      take_fraction (ee, r1, ff);
8228    }
8229    free_number (r1);
8230  }
8231  free_number (rt);
8232  free_number (lt);
8233  set_number_from_addition (arg2, dd, ee);
8234  make_fraction (ff, ee, arg2);
8235  free_number (arg2);
8236}
8237
8238
8239@ The value of $u_{k-1}$ will be |<=1| except when $k=1$ and the previous
8240equation was specified by a curl. In that case we must use a special
8241method of computation to prevent overflow.
8242
8243Fortunately, the calculations turn out to be even simpler in this ``hard''
8244case. The curl equation makes $w_0=0$ and $v_0=-u_0\psi_1$, hence
8245$-B_1\psi_1-A_1v_0=-(B_1-u_0A_1)\psi_1=-\\{cc}\cdot B_1\psi_1$.
8246
8247@<Calculate the values of $v_k$ and $w_k$@>=
8248take_fraction (acc, mp->psi[k + 1], mp->uu[k]);
8249number_negate (acc);
8250if (mp_right_type (r) == mp_curl) {
8251  mp_number r1, arg2;
8252  new_fraction (r1);
8253  new_number (arg2);
8254  set_number_from_substraction (arg2, fraction_one_t, ff);
8255  take_fraction (r1, mp->psi[1], arg2);
8256  set_number_to_zero(mp->ww[k]);
8257  set_number_from_substraction(mp->vv[k], acc, r1);
8258  free_number (r1);
8259  free_number (arg2);
8260} else {
8261  mp_number arg1, r1;
8262  new_fraction (r1);
8263  new_number (arg1);
8264  set_number_from_substraction (arg1, fraction_one_t, ff);
8265  make_fraction (ff, arg1, cc);    /* this is $B_k/(C_k+B_k-u_{k-1}A_k)<5$ */
8266  free_number (arg1);
8267  take_fraction (r1, mp->psi[k], ff);
8268  number_substract (acc, r1);
8269  number_clone (r1, ff);
8270  take_fraction (ff, r1, aa);   /* this is $A_k/(C_k+B_k-u_{k-1}A_k)$ */
8271  take_fraction (r1, mp->vv[k - 1], ff);
8272  set_number_from_substraction(mp->vv[k], acc, r1 );
8273  if (number_zero(mp->ww[k - 1])) {
8274    set_number_to_zero(mp->ww[k]);
8275  } else {
8276    take_fraction (mp->ww[k], mp->ww[k - 1], ff);
8277    number_negate(mp->ww[k]);
8278  }
8279  free_number (r1);
8280}
8281
8282
8283@ When a complete cycle has been traversed, we have $\theta_k+u_k\theta\k=
8284v_k+w_k\theta_0$, for |1<=k<=n|. We would like to determine the value of
8285$\theta_n$ and reduce the system to the form $\theta_k+u_k\theta\k=v_k$
8286for |0<=k<n|, so that the cyclic case can be finished up just as if there
8287were no cycle.
8288
8289The idea in the following code is to observe that
8290$$\eqalign{\theta_n&=v_n+w_n\theta_0-u_n\theta_1=\cdots\cr
8291&=v_n+w_n\theta_0-u_n\bigl(v_1+w_1\theta_0-u_1(v_2+\cdots
8292  -u_{n-2}(v_{n-1}+w_{n-1}\theta_0-u_{n-1}\theta_0))\bigr),\cr}$$
8293so we can solve for $\theta_n=\theta_0$.
8294
8295@<Adjust $\theta_n$ to equal $\theta_0$ and |goto found|@>=
8296{
8297  mp_number arg2, r1;
8298  new_number (arg2);
8299  new_number (r1);
8300  set_number_to_zero (aa);
8301  number_clone (bb, fraction_one_t);            /* we have |k=n| */
8302  do {
8303    decr (k);
8304    if (k == 0)
8305      k = n;
8306    take_fraction (r1, aa, mp->uu[k]);
8307    set_number_from_substraction (aa, mp->vv[k], r1);
8308    take_fraction (r1, bb, mp->uu[k]);
8309    set_number_from_substraction (bb, mp->ww[k], r1);
8310  } while (k != n);             /* now $\theta_n=\\{aa}+\\{bb}\cdot\theta_n$ */
8311  set_number_from_substraction (arg2, fraction_one_t, bb);
8312  make_fraction (r1, aa, arg2);
8313  number_clone (aa, r1);
8314  number_clone(mp->theta[n], aa);
8315  number_clone(mp->vv[0], aa);
8316  for (k = 1; k < n; k++) {
8317    take_fraction (r1, aa, mp->ww[k]);
8318    number_add(mp->vv[k], r1);
8319  }
8320  free_number(arg2);
8321  free_number(r1);
8322  free_number(aa);
8323  free_number(bb);
8324  free_number(cc);
8325  free_number(acc);
8326  free_number(dd);
8327  free_number(ee);
8328  goto FOUND;
8329}
8330
8331
8332@ @c
8333void mp_reduce_angle (MP mp, mp_number *a) {
8334  mp_number abs_a;
8335  FUNCTION_TRACE2 ("reduce_angle(%f)\n", number_to_double(*a));
8336  new_number(abs_a);
8337  number_clone(abs_a, *a);
8338  number_abs(abs_a);
8339  if ( number_greater(abs_a, one_eighty_deg_t)) {
8340    if (number_positive(*a)) {
8341      number_substract(*a, three_sixty_deg_t);
8342    } else {
8343      number_add(*a, three_sixty_deg_t);
8344    }
8345  }
8346  free_number(abs_a);
8347}
8348
8349@ @<Declarations@>=
8350void mp_reduce_angle (MP mp, mp_number *a);
8351
8352
8353@ @<Calculate the given value of $\theta_n$...@>=
8354{
8355  mp_number narg;
8356  new_angle (narg);
8357  n_arg (narg, mp->delta_x[n - 1], mp->delta_y[n - 1]);
8358  set_number_from_substraction(mp->theta[n], s->left_given, narg);
8359  free_number (narg);
8360  mp_reduce_angle (mp, &mp->theta[n]);
8361  goto FOUND;
8362}
8363
8364
8365@ @<Set up the equation for a given value of $\theta_0$@>=
8366{
8367  mp_number narg;
8368  new_angle (narg);
8369  n_arg (narg, mp->delta_x[0], mp->delta_y[0]);
8370  set_number_from_substraction(mp->vv[0], s->right_given, narg);
8371  free_number (narg);
8372  mp_reduce_angle (mp, &mp->vv[0]);
8373  set_number_to_zero(mp->uu[0]);
8374  set_number_to_zero(mp->ww[0]);
8375}
8376
8377
8378@ @<Set up the equation for a curl at $\theta_0$@>=
8379{
8380  mp_number lt, rt, cc;  /* tension values */
8381  new_number (lt);
8382  new_number (rt);
8383  new_number (cc);
8384  number_clone (cc, s->right_curl);
8385  number_clone (lt, t->left_tension);
8386  number_abs(lt);
8387  number_clone (rt, s->right_tension);
8388  number_abs(rt);
8389  if (number_unity(rt) && number_unity(lt)) {
8390    mp_number arg1, arg2;
8391    new_number (arg1);
8392    new_number (arg2);
8393    number_clone (arg1, cc);
8394    number_double (arg1);
8395    number_add (arg1, unity_t);
8396    number_clone (arg2, cc);
8397    number_add (arg2, two_t);
8398    make_fraction (mp->uu[0], arg1, arg2);
8399    free_number (arg1);
8400    free_number (arg2);
8401  } else {
8402    mp_curl_ratio (mp, &mp->uu[0], cc, rt, lt);
8403  }
8404  take_fraction (mp->vv[0], mp->psi[1], mp->uu[0]);
8405  number_negate(mp->vv[0]);
8406  set_number_to_zero(mp->ww[0]);
8407  free_number (rt);
8408  free_number (lt);
8409  free_number (cc);
8410}
8411
8412
8413@ @<Set up equation for a curl at $\theta_n$...@>=
8414{
8415  mp_number lt, rt, cc;  /* tension values */
8416  new_number (lt);
8417  new_number (rt);
8418  new_number (cc);
8419  number_clone (cc, s->left_curl);
8420  number_clone (lt, s->left_tension);
8421  number_abs(lt);
8422  number_clone (rt, r->right_tension);
8423  number_abs(rt);
8424  if (number_unity(rt) && number_unity(lt)) {
8425    mp_number arg1, arg2;
8426    new_number (arg1);
8427    new_number (arg2);
8428    number_clone (arg1, cc);
8429    number_double (arg1);
8430    number_add (arg1, unity_t);
8431    number_clone (arg2, cc);
8432    number_add (arg2, two_t);
8433    make_fraction (ff, arg1, arg2);
8434    free_number (arg1);
8435    free_number (arg2);
8436  } else {
8437    mp_curl_ratio (mp, &ff, cc, lt, rt);
8438  }
8439  {
8440    mp_number arg1, arg2, r1;
8441    new_fraction (r1);
8442    new_fraction (arg1);
8443    new_number (arg2);
8444    take_fraction (arg1, mp->vv[n - 1], ff);
8445    take_fraction (r1, ff, mp->uu[n - 1]);
8446    set_number_from_substraction (arg2, fraction_one_t, r1);
8447    make_fraction (mp->theta[n], arg1, arg2);
8448    number_negate(mp->theta[n]);
8449    free_number (r1);
8450    free_number (arg1);
8451    free_number (arg2);
8452  }
8453  free_number (rt);
8454  free_number (lt);
8455  free_number (cc);
8456  goto FOUND;
8457}
8458
8459
8460@ The |curl_ratio| subroutine has three arguments, which our previous notation
8461encourages us to call $\gamma$, $\alpha^{-1}$, and $\beta^{-1}$. It is
8462a somewhat tedious program to calculate
8463$${(3-\alpha)\alpha^2\gamma+\beta^3\over
8464  \alpha^3\gamma+(3-\beta)\beta^2},$$
8465with the result reduced to 4 if it exceeds 4. (This reduction of curl
8466is necessary only if the curl and tension are both large.)
8467The values of $\alpha$ and $\beta$ will be at most~4/3.
8468
8469@<Declarations@>=
8470static void mp_curl_ratio (MP mp, mp_number *ret, mp_number gamma, mp_number a_tension,
8471                                 mp_number b_tension);
8472
8473@ @c
8474void mp_curl_ratio (MP mp, mp_number *ret, mp_number gamma_orig, mp_number a_tension, mp_number b_tension) {
8475  mp_number alpha, beta, gamma, num, denom, ff; /* registers */
8476  mp_number arg1;
8477  new_number (arg1);
8478  new_fraction (alpha);
8479  new_fraction (beta);
8480  new_fraction (gamma);
8481  new_fraction (ff);
8482  new_fraction (denom);
8483  new_fraction (num);
8484  make_fraction (alpha, unity_t, a_tension);
8485  make_fraction (beta, unity_t, b_tension);
8486  number_clone (gamma, gamma_orig);
8487  if (number_lessequal(alpha, beta)) {
8488    make_fraction (ff, alpha, beta);
8489    number_clone (arg1, ff);
8490    take_fraction (ff, arg1, arg1);
8491    number_clone (arg1, gamma);
8492    take_fraction (gamma, arg1, ff);
8493    convert_fraction_to_scaled (beta);
8494    take_fraction (denom, gamma, alpha);
8495    number_add (denom, three_t);
8496  } else {
8497    make_fraction (ff, beta, alpha);
8498    number_clone (arg1, ff);
8499    take_fraction (ff, arg1, arg1);
8500    take_fraction (arg1, beta, ff);
8501    convert_fraction_to_scaled (arg1);
8502    number_clone (beta, arg1);
8503    take_fraction (denom, gamma, alpha);
8504    set_number_from_div (arg1, ff, twelvebits_3);
8505    number_add (denom, arg1);
8506  }
8507  number_substract (denom, beta);
8508  set_number_from_substraction (arg1, fraction_three_t, alpha);
8509  take_fraction (num, gamma, arg1);
8510  number_add (num, beta);
8511  number_clone (arg1, denom);
8512  number_double (arg1);
8513  number_double (arg1); /* arg1 = 4*denom */
8514  if (number_greaterequal(num, arg1)) {
8515    number_clone(*ret, fraction_four_t);
8516  } else {
8517    make_fraction (*ret, num, denom);
8518  }
8519  free_number (alpha);
8520  free_number (beta);
8521  free_number (gamma);
8522  free_number (num);
8523  free_number (denom);
8524  free_number (ff);
8525  free_number (arg1);
8526}
8527
8528
8529@ We're in the home stretch now.
8530
8531@<Finish choosing angles and assigning control points@>=
8532{
8533  mp_number r1;
8534  new_number (r1);
8535  for (k = n - 1; k >= 0; k--) {
8536    take_fraction (r1, mp->theta[k + 1], mp->uu[k]);
8537    set_number_from_substraction(mp->theta[k], mp->vv[k], r1);
8538  }
8539  free_number (r1);
8540}
8541s = p;
8542k = 0;
8543{
8544mp_number arg;
8545new_number (arg);
8546do {
8547  t = mp_next_knot (s);
8548  n_sin_cos (mp->theta[k], mp->ct, mp->st);
8549  number_clone (arg, mp->psi[k + 1]);
8550  number_negate (arg);
8551  number_substract (arg, mp->theta[k + 1]);
8552  n_sin_cos (arg, mp->cf, mp->sf);
8553  mp_set_controls (mp, s, t, k);
8554  incr (k);
8555  s = t;
8556} while (k != n);
8557free_number (arg);
8558}
8559
8560
8561@ The |set_controls| routine actually puts the control points into
8562a pair of consecutive nodes |p| and~|q|. Global variables are used to
8563record the values of $\sin\theta$, $\cos\theta$, $\sin\phi$, and
8564$\cos\phi$ needed in this calculation.
8565
8566@<Glob...@>=
8567mp_number st;
8568mp_number ct;
8569mp_number sf;
8570mp_number cf;    /* sines and cosines */
8571
8572@ @<Initialize table...@>=
8573new_fraction (mp->st);
8574new_fraction (mp->ct);
8575new_fraction (mp->sf);
8576new_fraction (mp->cf);
8577
8578@ @<Dealloc ...@>=
8579free_number (mp->st);
8580free_number (mp->ct);
8581free_number (mp->sf);
8582free_number (mp->cf);
8583
8584
8585@ @<Declarations@>=
8586static void mp_set_controls (MP mp, mp_knot p, mp_knot q, integer k);
8587
8588@ @c
8589void mp_set_controls (MP mp, mp_knot p, mp_knot q, integer k) {
8590  mp_number rr, ss;      /* velocities, divided by thrice the tension */
8591  mp_number lt, rt;        /* tensions */
8592  mp_number sine;        /* $\sin(\theta+\phi)$ */
8593  mp_number tmp;
8594  mp_number r1, r2;
8595  new_number(tmp);
8596  new_number (lt);
8597  new_number (rt);
8598  new_number (r1);
8599  new_number (r2);
8600  number_clone(lt, q->left_tension);
8601  number_abs(lt);
8602  number_clone(rt, p->right_tension);
8603  number_abs(rt);
8604  new_fraction (sine);
8605  new_fraction (rr);
8606  new_fraction (ss);
8607  velocity (rr, mp->st, mp->ct, mp->sf, mp->cf, rt);
8608  velocity (ss, mp->sf, mp->cf, mp->st, mp->ct, lt);
8609  if (number_negative(p->right_tension) || number_negative(q->left_tension)) {
8610    @<Decrease the velocities,
8611      if necessary, to stay inside the bounding triangle@>;
8612  }
8613  take_fraction (r1, mp->delta_x [k], mp->ct);
8614  take_fraction (r2, mp->delta_y [k], mp->st);
8615  number_substract (r1, r2);
8616  take_fraction (tmp, r1, rr);
8617  set_number_from_addition (p->right_x, p->x_coord, tmp);
8618  take_fraction (r1, mp->delta_y[k], mp->ct);
8619  take_fraction (r2, mp->delta_x[k], mp->st);
8620  number_add (r1, r2);
8621  take_fraction (tmp, r1, rr);
8622  set_number_from_addition (p->right_y, p->y_coord, tmp);
8623  take_fraction (r1, mp->delta_x[k], mp->cf);
8624  take_fraction (r2, mp->delta_y[k], mp->sf);
8625  number_add (r1, r2);
8626  take_fraction (tmp, r1, ss);
8627  set_number_from_substraction (q->left_x, q->x_coord, tmp);
8628  take_fraction (r1, mp->delta_y[k], mp->cf);
8629  take_fraction (r2, mp->delta_x[k], mp->sf);
8630  number_substract (r1, r2);
8631  take_fraction (tmp, r1, ss);
8632  set_number_from_substraction(q->left_y, q->y_coord, tmp);
8633  mp_right_type (p) = mp_explicit;
8634  mp_left_type (q) = mp_explicit;
8635  free_number (tmp);
8636  free_number (r1);
8637  free_number (r2);
8638  free_number (lt);
8639  free_number (rt);
8640  free_number (rr);
8641  free_number (ss);
8642  free_number (sine);
8643}
8644
8645
8646@ The boundedness conditions $\\{rr}\L\sin\phi\,/\sin(\theta+\phi)$ and
8647$\\{ss}\L\sin\theta\,/\sin(\theta+\phi)$ are to be enforced if $\sin\theta$,
8648$\sin\phi$, and $\sin(\theta+\phi)$ all have the same sign. Otherwise
8649there is no ``bounding triangle.''
8650
8651@<Decrease the velocities, if necessary...@>=
8652if ((number_nonnegative(mp->st) && number_nonnegative(mp->sf)) || (number_nonpositive(mp->st) && number_nonpositive(mp->sf))) {
8653  mp_number r1, r2, arg1;
8654  mp_number ab_vs_cd;
8655  new_number (ab_vs_cd);
8656  new_fraction (r1);
8657  new_fraction (r2);
8658  new_number (arg1);
8659  number_clone (arg1, mp->st);
8660  number_abs (arg1);
8661  take_fraction (r1, arg1, mp->cf);
8662  number_clone (arg1, mp->sf);
8663  number_abs (arg1);
8664  take_fraction (r2, arg1, mp->ct);
8665  set_number_from_addition (sine, r1, r2);
8666  if (number_positive(sine)) {
8667    set_number_from_addition (arg1, fraction_one_t, unity_t);  /* safety factor */
8668    number_clone (r1, sine);
8669    take_fraction (sine, r1, arg1);
8670    if (number_negative(p->right_tension)) {
8671      number_clone (arg1, mp->sf);
8672      number_abs (arg1);
8673      ab_vs_cd (ab_vs_cd, arg1, fraction_one_t, rr, sine);
8674      if (number_negative(ab_vs_cd)) {
8675        number_clone (arg1, mp->sf);
8676        number_abs (arg1);
8677        make_fraction (rr, arg1, sine);
8678      }
8679    }
8680    if (number_negative(q->left_tension)) {
8681      number_clone (arg1, mp->st);
8682      number_abs (arg1);
8683      ab_vs_cd (ab_vs_cd, arg1, fraction_one_t, ss, sine);
8684      if (number_negative(ab_vs_cd)) {
8685        number_clone (arg1, mp->st);
8686        number_abs (arg1);
8687        make_fraction (ss, arg1, sine);
8688      }
8689    }
8690  }
8691  free_number (arg1);
8692  free_number (r1);
8693  free_number (r2);
8694  free_number (ab_vs_cd);
8695}
8696
8697@ Only the simple cases remain to be handled.
8698
8699@<Reduce to simple case of two givens and |return|@>=
8700{
8701  mp_number arg1;
8702  mp_number narg;
8703  new_angle (narg);
8704  n_arg (narg, mp->delta_x[0], mp->delta_y[0]);
8705  new_number (arg1);
8706  set_number_from_substraction (arg1, p->right_given, narg);
8707  n_sin_cos (arg1, mp->ct, mp->st);
8708  set_number_from_substraction (arg1, q->left_given, narg);
8709  n_sin_cos (arg1, mp->cf, mp->sf);
8710  number_negate (mp->sf);
8711  mp_set_controls (mp, p, q, 0);
8712  free_number (narg);
8713  free_number (arg1);
8714  free_number (ff);
8715  return;
8716}
8717
8718
8719@ @<Reduce to simple case of straight line and |return|@>=
8720{
8721  mp_number lt, rt;  /* tension values */
8722  mp_right_type (p) = mp_explicit;
8723  mp_left_type (q) = mp_explicit;
8724  new_number (lt);
8725  new_number (rt);
8726  number_clone (lt, q->left_tension);
8727  number_abs(lt);
8728  number_clone (rt, p->right_tension);
8729  number_abs(rt);
8730  if (number_unity(rt)) {
8731    mp_number arg2;
8732    new_number (arg2);
8733    if (number_nonnegative(mp->delta_x[0])) {
8734      set_number_from_addition (arg2, mp->delta_x[0], epsilon_t);
8735    } else {
8736      set_number_from_substraction (arg2, mp->delta_x[0], epsilon_t);
8737    }
8738    number_int_div (arg2, 3);
8739    set_number_from_addition (p->right_x, p->x_coord, arg2);
8740    if (number_nonnegative(mp->delta_y[0])) {
8741      set_number_from_addition (arg2, mp->delta_y[0], epsilon_t);
8742    } else {
8743      set_number_from_substraction (arg2, mp->delta_y[0], epsilon_t);
8744    }
8745    number_int_div (arg2, 3);
8746    set_number_from_addition (p->right_y, p->y_coord, arg2);
8747    free_number (arg2);
8748  } else {
8749    mp_number arg2, r1;
8750    new_fraction (r1);
8751    new_number (arg2);
8752    number_clone (arg2, rt);
8753    number_multiply_int (arg2, 3);
8754    make_fraction (ff, unity_t, arg2);  /* $\alpha/3$ */
8755    free_number (arg2);
8756    take_fraction (r1, mp->delta_x[0], ff);
8757    set_number_from_addition (p->right_x, p->x_coord, r1);
8758    take_fraction (r1, mp->delta_y[0], ff);
8759    set_number_from_addition (p->right_y, p->y_coord, r1);
8760  }
8761  if (number_unity(lt)) {
8762    mp_number arg2;
8763    new_number (arg2);
8764    if (number_nonnegative(mp->delta_x[0])) {
8765      set_number_from_addition (arg2, mp->delta_x[0], epsilon_t);
8766    } else {
8767      set_number_from_substraction (arg2, mp->delta_x[0], epsilon_t);
8768    }
8769    number_int_div (arg2, 3);
8770    set_number_from_substraction (q->left_x, q->x_coord, arg2);
8771    if (number_nonnegative(mp->delta_y[0])) {
8772      set_number_from_addition (arg2, mp->delta_y[0], epsilon_t);
8773    } else {
8774      set_number_from_substraction (arg2, mp->delta_y[0], epsilon_t);
8775    }
8776    number_int_div (arg2, 3);
8777    set_number_from_substraction (q->left_y, q->y_coord, arg2);
8778    free_number (arg2);
8779  } else {
8780    mp_number arg2, r1;
8781    new_fraction (r1);
8782    new_number (arg2);
8783    number_clone (arg2, lt);
8784    number_multiply_int (arg2, 3);
8785    make_fraction (ff, unity_t, arg2);  /* $\beta/3$ */
8786    free_number (arg2);
8787    take_fraction (r1, mp->delta_x[0], ff);
8788    set_number_from_substraction(q->left_x, q->x_coord, r1);
8789    take_fraction (r1, mp->delta_y[0], ff);
8790    set_number_from_substraction(q->left_y, q->y_coord, r1);
8791    free_number (r1);
8792  }
8793  free_number (ff);
8794  free_number (lt);
8795  free_number (rt);
8796  return;
8797}
8798
8799@ Various subroutines that are useful for the new (1.770) exported
8800api for solving path choices
8801
8802@c
8803#define TOO_LARGE(a) (fabs((a))>4096.0)
8804#define PI 3.1415926535897932384626433832795028841971
8805
8806static int out_of_range(MP mp, double a)
8807{
8808    mp_number t;
8809    new_number (t);
8810    set_number_from_double(t,fabs(a));
8811    if (number_greaterequal(t,inf_t)) {
8812       free_number (t);
8813       return 1;
8814    }
8815    free_number (t);
8816    return 0;
8817}
8818
8819static int mp_link_knotpair (MP mp, mp_knot p, mp_knot q);
8820static int mp_link_knotpair (MP mp, mp_knot p, mp_knot q)
8821{
8822    if (p==NULL ||q==NULL) return 0;
8823    p->next = q;
8824    set_number_from_double(p->right_tension, 1.0);
8825    if (mp_right_type(p)==mp_endpoint) {
8826	mp_right_type(p) = mp_open;
8827    }
8828    set_number_from_double(q->left_tension, 1.0);
8829    if (mp_left_type(q) == mp_endpoint) {
8830	mp_left_type(q) = mp_open;
8831    }
8832    return 1;
8833}
8834
8835int mp_close_path_cycle (MP mp, mp_knot p, mp_knot q)
8836{
8837    return mp_link_knotpair(mp,p,q);
8838}
8839
8840int mp_close_path (MP mp, mp_knot q, mp_knot first)
8841{
8842    if (q==NULL || first==NULL) return 0;
8843    q->next = first;
8844    mp_right_type(q) = mp_endpoint;
8845    set_number_from_double(q->right_tension, 1.0);
8846    mp_left_type(first) = mp_endpoint;
8847    set_number_from_double(first->left_tension, 1.0);
8848    return 1;
8849}
8850
8851mp_knot mp_create_knot (MP mp)
8852{
8853    mp_knot q = mp_new_knot(mp);
8854    mp_left_type(q) = mp_endpoint;
8855    mp_right_type(q) = mp_endpoint;
8856    return q;
8857}
8858
8859int mp_set_knot (MP mp, mp_knot p, double x, double y)
8860{
8861    if (out_of_range(mp, x)) return 0;
8862    if (out_of_range(mp, y)) return 0;
8863    if (p==NULL) return 0;
8864    set_number_from_double(p->x_coord, x);
8865    set_number_from_double(p->y_coord, y);
8866    return 1;
8867}
8868
8869mp_knot mp_append_knot (MP mp, mp_knot p, double x, double y)
8870{
8871    mp_knot q = mp_create_knot(mp);
8872    if (q==NULL) return NULL;
8873    if (!mp_set_knot(mp, q, x, y)) {
8874	free(q);
8875	return NULL;
8876    }
8877    if (p == NULL) return q;
8878    if (!mp_link_knotpair(mp, p,q)) {
8879	free(q);
8880	return NULL;
8881    }
8882    return q;
8883}
8884
8885int mp_set_knot_curl (MP mp, mp_knot q, double value) {
8886    if (q==NULL) return 0;
8887    if (TOO_LARGE(value)) return 0;
8888    mp_right_type(q)=mp_curl;
8889    set_number_from_double(q->right_curl, value);
8890    if (mp_left_type(q)==mp_open) {
8891	mp_left_type(q)=mp_curl;
8892	set_number_from_double(q->left_curl, value);
8893    }
8894    return 1;
8895}
8896
8897int mp_set_knot_left_curl (MP mp, mp_knot q, double value) {
8898    if (q==NULL) return 0;
8899    if (TOO_LARGE(value)) return 0;
8900    mp_left_type(q)=mp_curl;
8901    set_number_from_double(q->left_curl, value);
8902    if (mp_right_type(q)==mp_open) {
8903	mp_right_type(q)=mp_curl;
8904	set_number_from_double(q->right_curl, value);
8905    }
8906    return 1;
8907}
8908
8909int mp_set_knot_right_curl (MP mp, mp_knot q, double value) {
8910    if (q==NULL) return 0;
8911    if (TOO_LARGE(value)) return 0;
8912    mp_right_type(q)=mp_curl;
8913    set_number_from_double(q->right_curl, value);
8914    if (mp_left_type(q)==mp_open) {
8915	mp_left_type(q)=mp_curl;
8916	set_number_from_double(q->left_curl, value);
8917    }
8918    return 1;
8919}
8920
8921int mp_set_knotpair_curls (MP mp, mp_knot p, mp_knot q, double t1, double t2) {
8922    if (p==NULL || q==NULL) return 0;
8923    if (mp_set_knot_curl(mp, p, t1))
8924	return mp_set_knot_curl(mp, q, t2);
8925    return 0;
8926}
8927
8928int mp_set_knotpair_tensions (MP mp, mp_knot p, mp_knot q, double t1, double t2) {
8929    if (p==NULL || q==NULL) return 0;
8930    if (TOO_LARGE(t1)) return 0;
8931    if (TOO_LARGE(t2)) return 0;
8932    if ((fabs(t1)<0.75)) return 0;
8933    if ((fabs(t2)<0.75)) return 0;
8934    set_number_from_double(p->right_tension, t1);
8935    set_number_from_double(q->left_tension, t2);
8936    return 1;
8937}
8938
8939int mp_set_knot_left_tension (MP mp, mp_knot p, double t1) {
8940    if (p==NULL) return 0;
8941    if (TOO_LARGE(t1)) return 0;
8942    if ((fabs(t1)<0.75)) return 0;
8943    set_number_from_double(p->left_tension, t1);
8944    return 1;
8945}
8946
8947int mp_set_knot_right_tension (MP mp, mp_knot p, double t1) {
8948    if (p==NULL) return 0;
8949    if (TOO_LARGE(t1)) return 0;
8950    if ((fabs(t1)<0.75)) return 0;
8951    set_number_from_double(p->right_tension, t1);
8952    return 1;
8953}
8954
8955int mp_set_knotpair_controls (MP mp, mp_knot p, mp_knot q, double x1, double y1, double x2, double y2) {
8956    if (p==NULL || q==NULL) return 0;
8957    if (out_of_range(mp, x1)) return 0;
8958    if (out_of_range(mp, y1)) return 0;
8959    if (out_of_range(mp, x2)) return 0;
8960    if (out_of_range(mp, y2)) return 0;
8961    mp_right_type(p)=mp_explicit;
8962    set_number_from_double(p->right_x, x1);
8963    set_number_from_double(p->right_y, y1);
8964    mp_left_type(q)=mp_explicit;
8965    set_number_from_double(q->left_x, x2);
8966    set_number_from_double(q->left_y, y2);
8967    return 1;
8968}
8969
8970int mp_set_knot_left_control (MP mp, mp_knot p, double x1, double y1) {
8971    if (p==NULL) return 0;
8972    if (out_of_range(mp, x1)) return 0;
8973    if (out_of_range(mp, y1)) return 0;
8974    mp_left_type(p)=mp_explicit;
8975    set_number_from_double(p->left_x, x1);
8976    set_number_from_double(p->left_y, y1);
8977    return 1;
8978}
8979
8980int mp_set_knot_right_control (MP mp, mp_knot p, double x1, double y1) {
8981    if (p==NULL) return 0;
8982    if (out_of_range(mp, x1)) return 0;
8983    if (out_of_range(mp, y1)) return 0;
8984    mp_right_type(p)=mp_explicit;
8985    set_number_from_double(p->right_x, x1);
8986    set_number_from_double(p->right_y, y1);
8987    return 1;
8988}
8989
8990int mp_set_knot_direction (MP mp, mp_knot q, double x, double y) {
8991    double value = 0;
8992    if (q==NULL) return 0;
8993    if (TOO_LARGE(x)) return 0;
8994    if (TOO_LARGE(y)) return 0;
8995    if (!(x==0 && y == 0))
8996	value = atan2 (y, x) * (180.0 / PI)  * 16.0;
8997    mp_right_type(q)=mp_given;
8998    set_number_from_double(q->right_curl, value);
8999    if (mp_left_type(q)==mp_open) {
9000	mp_left_type(q)=mp_given;
9001	set_number_from_double(q->left_curl, value);
9002    }
9003    return 1;
9004}
9005
9006int mp_set_knotpair_directions (MP mp, mp_knot p, mp_knot q, double x1, double y1, double x2, double y2) {
9007    if (p==NULL || q==NULL) return 0;
9008    if (mp_set_knot_direction(mp,p, x1, y1))
9009	return mp_set_knot_direction(mp,q, x2, y2);
9010    return 0;
9011}
9012
9013@
9014@c
9015static int path_needs_fixing (mp_knot source);
9016static int path_needs_fixing (mp_knot source) {
9017    mp_knot sourcehead = source;
9018    do {
9019	source = source->next;
9020    } while (source && source != sourcehead);
9021    if (!source) {
9022	return 1;
9023    }
9024    return 0;
9025}
9026
9027int mp_solve_path (MP mp, mp_knot first)
9028{
9029    int saved_arith_error = mp->arith_error;
9030    jmp_buf *saved_jump_buf = mp->jump_buf;
9031    int retval = 1;
9032    if (first==NULL) return 0;
9033    if (path_needs_fixing(first)) return 0;
9034    mp->jump_buf = malloc(sizeof(jmp_buf));
9035    if (mp->jump_buf == NULL || setjmp(*(mp->jump_buf)) != 0) {
9036       return 0;
9037    }
9038    mp->arith_error = 0;
9039    mp_make_choices(mp, first);
9040    if (mp->arith_error)
9041 	retval = 0;
9042    mp->arith_error = saved_arith_error;
9043    free(mp->jump_buf);
9044    mp->jump_buf = saved_jump_buf;
9045    return retval;
9046}
9047
9048void mp_free_path (MP mp, mp_knot p) {
9049    mp_toss_knot_list(mp, p);
9050}
9051
9052@ @<Exported function headers@>=
9053int mp_close_path_cycle (MP mp, mp_knot p, mp_knot q);
9054int mp_close_path (MP mp, mp_knot q, mp_knot first);
9055mp_knot mp_create_knot (MP mp);
9056int mp_set_knot (MP mp, mp_knot p, double x, double y);
9057mp_knot mp_append_knot (MP mp, mp_knot p, double x, double y);
9058int mp_set_knot_curl (MP mp, mp_knot q, double value);
9059int mp_set_knot_left_curl (MP mp, mp_knot q, double value);
9060int mp_set_knot_right_curl (MP mp, mp_knot q, double value);
9061int mp_set_knotpair_curls (MP mp, mp_knot p, mp_knot q, double t1, double t2) ;
9062int mp_set_knotpair_tensions (MP mp, mp_knot p, mp_knot q, double t1, double t2) ;
9063int mp_set_knot_left_tension (MP mp, mp_knot p, double t1);
9064int mp_set_knot_right_tension (MP mp, mp_knot p, double t1);
9065int mp_set_knot_left_control (MP mp, mp_knot p, double t1, double t2);
9066int mp_set_knot_right_control (MP mp, mp_knot p, double t1, double t2);
9067int mp_set_knotpair_controls (MP mp, mp_knot p, mp_knot q, double x1, double y1, double x2, double y2) ;
9068int mp_set_knot_direction (MP mp, mp_knot q, double x, double y) ;
9069int mp_set_knotpair_directions (MP mp, mp_knot p, mp_knot q, double x1, double y1, double x2, double y2) ;
9070int mp_solve_path (MP mp, mp_knot first);
9071void mp_free_path (MP mp, mp_knot p);
9072
9073@ Simple accessors for |mp_knot|.
9074
9075@c
9076mp_number mp_knot_x_coord(MP mp, mp_knot p) { return p->x_coord; }
9077mp_number mp_knot_y_coord(MP mp, mp_knot p) { return p->y_coord; }
9078mp_number mp_knot_left_x (MP mp, mp_knot p) { return p->left_x;  }
9079mp_number mp_knot_left_y (MP mp, mp_knot p) { return p->left_y;  }
9080mp_number mp_knot_right_x(MP mp, mp_knot p) { return p->right_x;  }
9081mp_number mp_knot_right_y(MP mp, mp_knot p) { return p->right_y;  }
9082int mp_knot_right_type(MP mp, mp_knot p) { return mp_right_type(p);}
9083int mp_knot_left_type (MP mp, mp_knot p) { return mp_left_type(p);}
9084mp_knot mp_knot_next (MP mp, mp_knot p)  { return p->next; }
9085double mp_number_as_double(MP mp, mp_number n) {
9086  return number_to_double(n);
9087}
9088
9089@ @<Exported function headers@>=
9090#define mp_knot_left_curl mp_knot_left_x
9091#define mp_knot_left_given mp_knot_left_x
9092#define mp_knot_left_tension mp_knot_left_y
9093#define mp_knot_right_curl mp_knot_right_x
9094#define mp_knot_right_given mp_knot_right_x
9095#define mp_knot_right_tension mp_knot_right_y
9096mp_number mp_knot_x_coord(MP mp, mp_knot p);
9097mp_number mp_knot_y_coord(MP mp, mp_knot p);
9098mp_number mp_knot_left_x(MP mp, mp_knot p);
9099mp_number mp_knot_left_y(MP mp, mp_knot p);
9100mp_number mp_knot_right_x(MP mp, mp_knot p);
9101mp_number mp_knot_right_y(MP mp, mp_knot p);
9102int mp_knot_right_type(MP mp, mp_knot p);
9103int mp_knot_left_type(MP mp, mp_knot p);
9104mp_knot mp_knot_next(MP mp, mp_knot p);
9105double mp_number_as_double(MP mp, mp_number n);
9106
9107
9108@* Measuring paths.
9109\MP's \&{llcorner}, \&{lrcorner}, \&{ulcorner}, and \&{urcorner} operators
9110allow the user to measure the bounding box of anything that can go into a
9111picture.  It's easy to get rough bounds on the $x$ and $y$ extent of a path
9112by just finding the bounding box of the knots and the control points. We
9113need a more accurate version of the bounding box, but we can still use the
9114easy estimate to save time by focusing on the interesting parts of the path.
9115
9116@ Computing an accurate bounding box involves a theme that will come up again
9117and again. Given a Bernshte{\u\i}n polynomial
9118@^Bernshte{\u\i}n, Serge{\u\i} Natanovich@>
9119$$B(z_0,z_1,\ldots,z_n;t)=\sum_k{n\choose k}t^k(1-t)^{n-k}z_k,$$
9120we can conveniently bisect its range as follows:
9121
9122\smallskip
9123\textindent{1)} Let $z_k^{(0)}=z_k$, for |0<=k<=n|.
9124
9125\smallskip
9126\textindent{2)} Let $z_k^{(j+1)}={1\over2}(z_k^{(j)}+z\k^{(j)})$, for
9127|0<=k<n-j|, for |0<=j<n|.
9128
9129\smallskip\noindent
9130Then
9131$$B(z_0,z_1,\ldots,z_n;t)=B(z_0^{(0)},z_0^{(1)},\ldots,z_0^{(n)};2t)
9132 =B(z_0^{(n)},z_1^{(n-1)},\ldots,z_n^{(0)};2t-1).$$
9133This formula gives us the coefficients of polynomials to use over the ranges
9134$0\L t\L{1\over2}$ and ${1\over2}\L t\L1$.
9135
9136@ Here is a routine that computes the $x$ or $y$ coordinate of the point on
9137a cubic corresponding to the |fraction| value~|t|.
9138
9139@c
9140static void mp_eval_cubic (MP mp, mp_number *r, mp_knot p, mp_knot q, quarterword c,
9141                             mp_number t) {
9142  mp_number x1, x2, x3;    /* intermediate values */
9143  new_number(x1);
9144  new_number(x2);
9145  new_number(x3);
9146  if (c == mp_x_code) {
9147    set_number_from_of_the_way(x1, t, p->x_coord, p->right_x);
9148    set_number_from_of_the_way(x2, t, p->right_x, q->left_x);
9149    set_number_from_of_the_way(x3, t, q->left_x, q->x_coord);
9150  } else {
9151    set_number_from_of_the_way(x1, t, p->y_coord, p->right_y);
9152    set_number_from_of_the_way(x2, t, p->right_y, q->left_y);
9153    set_number_from_of_the_way(x3, t, q->left_y, q->y_coord);
9154  }
9155  set_number_from_of_the_way(x1, t, x1, x2);
9156  set_number_from_of_the_way(x2, t, x2, x3);
9157  set_number_from_of_the_way(*r, t, x1, x2);
9158  free_number (x1);
9159  free_number (x2);
9160  free_number (x3);
9161}
9162
9163
9164@ The actual bounding box information is stored in global variables.
9165Since it is convenient to address the $x$ and $y$ information
9166separately, we define arrays indexed by |x_code..y_code| and use
9167macros to give them more convenient names.
9168
9169@<Types...@>=
9170enum mp_bb_code {
9171  mp_x_code = 0,        /* index for |minx| and |maxx| */
9172  mp_y_code                     /* index for |miny| and |maxy| */
9173};
9174
9175@
9176@d mp_minx mp->bbmin[mp_x_code]
9177@d mp_maxx mp->bbmax[mp_x_code]
9178@d mp_miny mp->bbmin[mp_y_code]
9179@d mp_maxy mp->bbmax[mp_y_code]
9180
9181@<Glob...@>=
9182mp_number bbmin[mp_y_code + 1];
9183mp_number bbmax[mp_y_code + 1];
9184/* the result of procedures that compute bounding box information */
9185
9186@ @<Initialize table ...@>=
9187{
9188  int i;
9189  for (i=0;i<=mp_y_code;i++) {
9190    new_number(mp->bbmin[i]);
9191    new_number(mp->bbmax[i]);
9192  }
9193}
9194
9195@ @<Dealloc...@>=
9196{
9197  int i;
9198  for (i=0;i<=mp_y_code;i++) {
9199    free_number(mp->bbmin[i]);
9200    free_number(mp->bbmax[i]);
9201  }
9202}
9203
9204
9205@ Now we're ready for the key part of the bounding box computation.
9206The |bound_cubic| procedure updates |bbmin[c]| and |bbmax[c]| based on
9207$$B(\hbox{|knot_coord(p)|}, \hbox{|right_coord(p)|},
9208    \hbox{|left_coord(q)|}, \hbox{|knot_coord(q)|};t)
9209$$
9210for $0<t\le1$.  In other words, the procedure adjusts the bounds to
9211accommodate |knot_coord(q)| and any extremes over the range $0<t<1$.
9212The |c| parameter is |x_code| or |y_code|.
9213
9214@c
9215static void mp_bound_cubic (MP mp, mp_knot p, mp_knot q, quarterword c) {
9216  boolean wavy; /* whether we need to look for extremes */
9217  mp_number del1, del2, del3, del, dmax;  /* proportional to the control
9218                         points of a quadratic derived from a cubic */
9219  mp_number t, tt;       /* where a quadratic crosses zero */
9220  mp_number x;     /* a value that |bbmin[c]| and |bbmax[c]| must accommodate */
9221  new_number (x);
9222  new_fraction (t);
9223  new_fraction (tt);
9224  if (c == mp_x_code) {
9225    number_clone(x, q->x_coord);
9226  } else {
9227    number_clone(x, q->y_coord);
9228  }
9229  new_number(del1);
9230  new_number(del2);
9231  new_number(del3);
9232  new_number(del);
9233  new_number(dmax);
9234  @<Adjust |bbmin[c]| and |bbmax[c]| to accommodate |x|@>;
9235  @<Check the control points against the bounding box and set |wavy:=true|
9236    if any of them lie outside@>;
9237  if (wavy) {
9238    if (c == mp_x_code) {
9239      set_number_from_substraction(del1, p->right_x, p->x_coord);
9240      set_number_from_substraction(del2, q->left_x, p->right_x);
9241      set_number_from_substraction(del3, q->x_coord, q->left_x);
9242    } else {
9243      set_number_from_substraction(del1, p->right_y, p->y_coord);
9244      set_number_from_substraction(del2, q->left_y, p->right_y);
9245      set_number_from_substraction(del3, q->y_coord, q->left_y);
9246    }
9247    @<Scale up |del1|, |del2|, and |del3| for greater accuracy;
9248      also set |del| to the first nonzero element of |(del1,del2,del3)|@>;
9249    if (number_negative(del)) {
9250      number_negate (del1);
9251      number_negate (del2);
9252      number_negate (del3);
9253    }
9254    crossing_point (t, del1, del2, del3);
9255    if (number_less(t, fraction_one_t)) {
9256      @<Test the extremes of the cubic against the bounding box@>;
9257    }
9258  }
9259  free_number (del3);
9260  free_number (del2);
9261  free_number (del1);
9262  free_number (del);
9263  free_number (dmax);
9264  free_number (x);
9265  free_number (t);
9266  free_number (tt);
9267}
9268
9269
9270@ @<Adjust |bbmin[c]| and |bbmax[c]| to accommodate |x|@>=
9271if (number_less(x, mp->bbmin[c]))
9272  number_clone(mp->bbmin[c], x);
9273if (number_greater(x, mp->bbmax[c]))
9274  number_clone(mp->bbmax[c], x)
9275
9276@ @<Check the control points against the bounding box and set...@>=
9277wavy = true;
9278if (c == mp_x_code) {
9279  if (number_lessequal(mp->bbmin[c], p->right_x))
9280    if (number_lessequal (p->right_x, mp->bbmax[c]))
9281      if (number_lessequal(mp->bbmin[c], q->left_x))
9282        if (number_lessequal (q->left_x, mp->bbmax[c]))
9283          wavy = false;
9284} else {
9285  if (number_lessequal(mp->bbmin[c], p->right_y))
9286    if (number_lessequal (p->right_y, mp->bbmax[c]))
9287      if (number_lessequal(mp->bbmin[c], q->left_y))
9288        if (number_lessequal (q->left_y, mp->bbmax[c]))
9289          wavy = false;
9290}
9291
9292
9293@ If |del1=del2=del3=0|, it's impossible to obey the title of this
9294section. We just set |del=0| in that case.
9295
9296@<Scale up |del1|, |del2|, and |del3| for greater accuracy...@>=
9297if (number_nonzero(del1)) {
9298  number_clone (del, del1);
9299} else if (number_nonzero(del2)) {
9300  number_clone (del, del2);
9301} else {
9302  number_clone (del, del3);
9303}
9304if (number_nonzero(del)) {
9305  mp_number absval1;
9306  new_number(absval1);
9307  number_clone (dmax, del1);
9308  number_abs (dmax);
9309  number_clone (absval1, del2);
9310  number_abs(absval1);
9311  if (number_greater(absval1, dmax)) {
9312    number_clone(dmax, absval1);
9313  }
9314  number_clone (absval1, del3);
9315  number_abs(absval1);
9316  if (number_greater(absval1, dmax)) {
9317    number_clone(dmax, absval1);
9318  }
9319  while (number_less(dmax, fraction_half_t)) {
9320    number_double(dmax);
9321    number_double(del1);
9322    number_double(del2);
9323    number_double(del3);
9324  }
9325  free_number (absval1);
9326}
9327
9328@ Since |crossing_point| has tried to choose |t| so that
9329$B(|del1|,|del2|,|del3|;\tau)$ crosses zero at $\tau=|t|$ with negative
9330slope, the value of |del2| computed below should not be positive.
9331But rounding error could make it slightly positive in which case we
9332must cut it to zero to avoid confusion.
9333
9334@<Test the extremes of the cubic against the bounding box@>=
9335{
9336  mp_eval_cubic (mp, &x, p, q, c, t);
9337  @<Adjust |bbmin[c]| and |bbmax[c]| to accommodate |x|@>;
9338  set_number_from_of_the_way(del2, t, del2, del3);
9339  /* now |0,del2,del3| represent the derivative on the remaining interval */
9340  if (number_positive(del2))
9341    set_number_to_zero(del2);
9342  {
9343    mp_number arg2, arg3;
9344    new_number(arg2);
9345    new_number(arg3);
9346    number_clone(arg2, del2);
9347    number_negate(arg2);
9348    number_clone(arg3, del3);
9349    number_negate(arg3);
9350    crossing_point (tt, zero_t, arg2, arg3);
9351    free_number (arg2);
9352    free_number (arg3);
9353  }
9354  if (number_less(tt, fraction_one_t)) {
9355    @<Test the second extreme against the bounding box@>;
9356  }
9357}
9358
9359
9360@ @<Test the second extreme against the bounding box@>=
9361{
9362  mp_number arg;
9363  new_number (arg);
9364  set_number_from_of_the_way (arg, t, tt, fraction_one_t);
9365  mp_eval_cubic (mp, &x, p, q, c, arg);
9366  free_number (arg);
9367  @<Adjust |bbmin[c]| and |bbmax[c]| to accommodate |x|@>;
9368}
9369
9370
9371@ Finding the bounding box of a path is basically a matter of applying
9372|bound_cubic| twice for each pair of adjacent knots.
9373
9374@c
9375static void mp_path_bbox (MP mp, mp_knot h) {
9376  mp_knot p, q; /* a pair of adjacent knots */
9377  number_clone(mp_minx, h->x_coord);
9378  number_clone(mp_miny, h->y_coord);
9379  number_clone (mp_maxx, mp_minx);
9380  number_clone (mp_maxy, mp_miny);
9381  p = h;
9382  do {
9383    if (mp_right_type (p) == mp_endpoint)
9384      return;
9385    q = mp_next_knot (p);
9386    mp_bound_cubic (mp, p, q, mp_x_code);
9387    mp_bound_cubic (mp, p, q, mp_y_code);
9388    p = q;
9389  } while (p != h);
9390}
9391
9392
9393@ Another important way to measure a path is to find its arc length.  This
9394is best done by using the general bisection algorithm to subdivide the path
9395until obtaining ``well behaved'' subpaths whose arc lengths can be approximated
9396by simple means.
9397
9398Since the arc length is the integral with respect to time of the magnitude of
9399the velocity, it is natural to use Simpson's rule for the approximation.
9400@^Simpson's rule@>
9401If $\dot B(t)$ is the spline velocity, Simpson's rule gives
9402$$ \vb\dot B(0)\vb + 4\vb\dot B({1\over2})\vb + \vb\dot B(1)\vb \over 6 $$
9403for the arc length of a path of length~1.  For a cubic spline
9404$B(z_0,z_1,z_2,z_3;t)$, the time derivative $\dot B(t)$ is
9405$3B(dz_0,dz_1,dz_2;t)$, where $dz_i=z_{i+1}-z_i$.  Hence the arc length
9406approximation is
9407$$ {\vb dz_0\vb \over 2} + 2\vb dz_{02}\vb + {\vb dz_2\vb \over 2}, $$
9408where
9409$$ dz_{02}={1\over2}\left({dz_0+dz_1\over 2}+{dz_1+dz_2\over 2}\right)$$
9410is the result of the bisection algorithm.
9411
9412@ The remaining problem is how to decide when a subpath is ``well behaved.''
9413This could be done via the theoretical error bound for Simpson's rule,
9414@^Simpson's rule@>
9415but this is impractical because it requires an estimate of the fourth
9416derivative of the quantity being integrated.  It is much easier to just perform
9417a bisection step and see how much the arc length estimate changes.  Since the
9418error for Simpson's rule is proportional to the fourth power of the sample
9419spacing, the remaining error is typically about $1\over16$ of the amount of
9420the change.  We say ``typically'' because the error has a pseudo-random behavior
9421that could cause the two estimates to agree when each contain large errors.
9422
9423To protect against disasters such as undetected cusps, the bisection process
9424should always continue until all the $dz_i$ vectors belong to a single
9425$90^\circ$ sector.  This ensures that no point on the spline can have velocity
9426less than 70\% of the minimum of $\vb dz_0\vb$, $\vb dz_1\vb$ and $\vb dz_2\vb$.
9427If such a spline happens to produce an erroneous arc length estimate that
9428is little changed by bisection, the amount of the error is likely to be fairly
9429small.  We will try to arrange things so that freak accidents of this type do
9430not destroy the inverse relationship between the \&{arclength} and
9431\&{arctime} operations.
9432@:arclength_}{\&{arclength} primitive@>
9433@:arctime_}{\&{arctime} primitive@>
9434
9435@ The \&{arclength} and \&{arctime} operations are both based on a recursive
9436@^recursion@>
9437function that finds the arc length of a cubic spline given $dz_0$, $dz_1$,
9438$dz_2$. This |arc_test| routine also takes an arc length goal |a_goal| and
9439returns the time when the arc length reaches |a_goal| if there is such a time.
9440Thus the return value is either an arc length less than |a_goal| or, if the
9441arc length would be at least |a_goal|, it returns a time value decreased by
9442|two|.  This allows the caller to use the sign of the result to distinguish
9443between arc lengths and time values.  On certain types of overflow, it is
9444possible for |a_goal| and the result of |arc_test| both to be |EL_GORDO|.
9445Otherwise, the result is always less than |a_goal|.
9446
9447Rather than halving the control point coordinates on each recursive call to
9448|arc_test|, it is better to keep them proportional to velocity on the original
9449curve and halve the results instead.  This means that recursive calls can
9450potentially use larger error tolerances in their arc length estimates.  How
9451much larger depends on to what extent the errors behave as though they are
9452independent of each other.  To save computing time, we use optimistic assumptions
9453and increase the tolerance by a factor of about $\sqrt2$ for each recursive
9454call.
9455
9456In addition to the tolerance parameter, |arc_test| should also have parameters
9457for ${1\over3}\vb\dot B(0)\vb$, ${2\over3}\vb\dot B({1\over2})\vb$, and
9458${1\over3}\vb\dot B(1)\vb$.  These quantities are relatively expensive to compute
9459and they are needed in different instances of |arc_test|.
9460
9461@c
9462static void mp_arc_test (MP mp, mp_number *ret, mp_number dx0, mp_number dy0, mp_number dx1,
9463                           mp_number dy1, mp_number dx2, mp_number dy2, mp_number v0,
9464                           mp_number v02, mp_number v2, mp_number a_goal, mp_number tol_orig) {
9465  boolean simple;       /* are the control points confined to a $90^\circ$ sector? */
9466  mp_number dx01, dy01, dx12, dy12, dx02, dy02;    /* bisection results */
9467  mp_number v002, v022; /* twice the velocity magnitudes at $t={1\over4}$ and $t={3\over4}$ */
9468  mp_number arc;   /* best arc length estimate before recursion */
9469  mp_number arc1;    /* arc length estimate for the first half */
9470  mp_number simply;
9471  mp_number tol;
9472  new_number (arc );
9473  new_number (arc1);
9474  new_number (dx01);
9475  new_number (dy01);
9476  new_number (dx12);
9477  new_number (dy12);
9478  new_number (dx02);
9479  new_number (dy02);
9480  new_number (v002);
9481  new_number (v022);
9482  new_number (simply);
9483  new_number (tol);
9484  number_clone(tol, tol_orig);
9485  @<Bisect the B\'ezier quadratic given by |dx0|, |dy0|, |dx1|, |dy1|,
9486    |dx2|, |dy2|@>;
9487  @<Initialize |v002|, |v022|, and the arc length estimate |arc|; if it overflows
9488    set |arc_test| and |return|@>;
9489  @<Test if the control points are confined to one quadrant or rotating them
9490    $45^\circ$ would put them in one quadrant.  Then set |simple| appropriately@>;
9491
9492  set_number_from_addition(simply, v0, v2);
9493  number_halfp (simply);
9494  number_negate (simply);
9495  number_add (simply, arc);
9496  number_substract (simply, v02);
9497  number_abs (simply);
9498
9499 if (simple && number_lessequal(simply, tol)) {
9500    if (number_less(arc, a_goal)){
9501      number_clone(*ret, arc);
9502    } else {
9503      @<Estimate when the arc length reaches |a_goal| and set |arc_test| to
9504         that time minus |two|@>;
9505    }
9506  } else {
9507    @<Use one or two recursive calls to compute the |arc_test| function@>;
9508  }
9509DONE:
9510  free_number (arc);
9511  free_number (arc1);
9512  free_number (dx01);
9513  free_number (dy01);
9514  free_number (dx12);
9515  free_number (dy12);
9516  free_number (dx02);
9517  free_number (dy02);
9518  free_number (v002);
9519  free_number (v022);
9520  free_number (simply);
9521  free_number (tol);
9522}
9523
9524
9525@ The |tol| value should by multiplied by $\sqrt 2$ before making recursive
9526calls, but $1.5$ is an adequate approximation.  It is best to avoid using
9527|make_fraction| in this inner loop.
9528@^inner loop@>
9529
9530@<Use one or two recursive calls to compute the |arc_test| function@>=
9531{
9532  mp_number a_new, a_aux;    /* the sum of these gives the |a_goal| */
9533  mp_number a, b;    /* results of recursive calls */
9534  mp_number half_v02; /* |halfp(v02)|, a recursion argument */
9535  new_number(a_new);
9536  new_number(a_aux);
9537  new_number(half_v02);
9538  @<Set |a_new| and |a_aux| so their sum is |2*a_goal| and |a_new| is as
9539    large as possible@>;
9540  {
9541    mp_number halfp_tol;
9542    new_number(halfp_tol);
9543    number_clone (halfp_tol, tol);
9544    number_halfp (halfp_tol);
9545    number_add(tol, halfp_tol);
9546    free_number (halfp_tol);
9547  }
9548  number_clone(half_v02, v02);
9549  number_halfp(half_v02);
9550  new_number (a);
9551  mp_arc_test (mp, &a, dx0, dy0, dx01, dy01, dx02, dy02,
9552                              v0, v002, half_v02, a_new, tol);
9553  if (number_negative(a)) {
9554    set_number_to_unity(*ret);
9555    number_double(*ret); /* two */
9556    number_substract(*ret, a); /* two - a */
9557    number_halfp(*ret);
9558    number_negate(*ret); /* -halfp(two - a) */
9559  } else {
9560    @<Update |a_new| to reduce |a_new+a_aux| by |a|@>;
9561    new_number (b);
9562    mp_arc_test (mp, &b, dx02, dy02, dx12, dy12, dx2, dy2,
9563                         half_v02, v022, v2, a_new, tol);
9564    if (number_negative(b)) {
9565      mp_number tmp ;
9566      new_number (tmp);
9567      number_clone(tmp, b);
9568      number_negate(tmp);
9569      number_halfp(tmp);
9570      number_negate(tmp);
9571      number_clone(*ret, tmp);
9572      set_number_to_unity(tmp);
9573      number_halfp(tmp);
9574      number_substract(*ret, tmp); /* (-(halfp(-b)) - 1/2) */
9575      free_number (tmp);
9576    } else {
9577      set_number_from_substraction(*ret, b, a);
9578      number_half(*ret);
9579      set_number_from_addition(*ret, a, *ret); /* (a + half(b - a)) */
9580    }
9581    free_number (b);
9582  }
9583  free_number (half_v02);
9584  free_number (a_aux);
9585  free_number (a_new);
9586  free_number (a);
9587  goto DONE;
9588}
9589
9590
9591@ @<Set |a_new| and |a_aux| so their sum is |2*a_goal| and |a_new| is...@>=
9592set_number_to_inf(a_aux);
9593number_substract(a_aux, a_goal);
9594if (number_greater(a_goal, a_aux)) {
9595  set_number_from_substraction(a_aux, a_goal, a_aux);
9596  set_number_to_inf(a_new);
9597} else {
9598  set_number_from_addition(a_new, a_goal, a_goal);
9599  set_number_to_zero(a_aux);
9600}
9601
9602
9603@ There is no need to maintain |a_aux| at this point so we use it as a temporary
9604to force the additions and subtractions to be done in an order that avoids
9605overflow.
9606
9607@<Update |a_new| to reduce |a_new+a_aux| by |a|@>=
9608if (number_greater(a, a_aux)) {
9609  number_substract(a_aux, a);
9610  number_add(a_new, a_aux);
9611}
9612
9613@ This code assumes all {\it dx} and {\it dy} variables have magnitude less than
9614|fraction_four|.  To simplify the rest of the |arc_test| routine, we strengthen
9615this assumption by requiring the norm of each $({\it dx},{\it dy})$ pair to obey
9616this bound.  Note that recursive calls will maintain this invariant.
9617
9618@<Bisect the B\'ezier quadratic given by |dx0|, |dy0|, |dx1|, |dy1|,...@>=
9619set_number_from_addition(dx01, dx0, dx1);
9620number_half(dx01);
9621set_number_from_addition(dx12, dx1, dx2);
9622number_half(dx12);
9623set_number_from_addition(dx02, dx01, dx12);
9624number_half(dx02);
9625set_number_from_addition(dy01, dy0, dy1);
9626number_half(dy01);
9627set_number_from_addition(dy12, dy1, dy2);
9628number_half(dy12);
9629set_number_from_addition(dy02, dy01, dy12);
9630number_half(dy02);
9631
9632@ We should be careful to keep |arc<EL_GORDO| so that calling |arc_test| with
9633|a_goal=EL_GORDO| is guaranteed to yield the arc length.
9634
9635@<Initialize |v002|, |v022|, and the arc length estimate |arc|;...@>=
9636{
9637  mp_number tmp, arg1, arg2 ;
9638  new_number (tmp);
9639  new_number (arg1);
9640  new_number (arg2);
9641  set_number_from_addition(arg1, dx0, dx02);
9642  number_half(arg1);
9643  number_add(arg1, dx01);
9644  set_number_from_addition(arg2, dy0, dy02);
9645  number_half(arg2);
9646  number_add(arg2, dy01);
9647  pyth_add (v002, arg1, arg2);
9648
9649  set_number_from_addition(arg1, dx02, dx2);
9650  number_half(arg1);
9651  number_add(arg1, dx12);
9652  set_number_from_addition(arg2, dy02, dy2);
9653  number_half(arg2);
9654  number_add(arg2, dy12);
9655  pyth_add (v022, arg1, arg2);
9656  free_number(arg1);
9657  free_number(arg2);
9658
9659  number_clone (tmp, v02);
9660  number_add_scaled (tmp, 2);
9661  number_halfp (tmp);
9662
9663  set_number_from_addition(arc1, v0, tmp);
9664  number_halfp (arc1);
9665  number_substract (arc1, v002);
9666  number_half (arc1);
9667  set_number_from_addition(arc1, v002, arc1);
9668
9669  set_number_from_addition(arc, v2, tmp);
9670  number_halfp (arc);
9671  number_substract (arc, v022);
9672  number_half (arc);
9673  set_number_from_addition(arc, v022, arc);
9674
9675  /* reuse |tmp| for the next |if| test: */
9676  set_number_to_inf(tmp);
9677  number_substract(tmp,arc1);
9678  if (number_less(arc, tmp)) {
9679    free_number (tmp);
9680    number_add(arc, arc1);
9681  } else {
9682    free_number (tmp);
9683    mp->arith_error = true;
9684    if (number_infinite(a_goal)) {
9685      set_number_to_inf(*ret);
9686    } else {
9687      set_number_to_unity(*ret);
9688      number_double(*ret);
9689      number_negate(*ret); /* -two */
9690    }
9691    goto DONE;
9692  }
9693}
9694
9695
9696@ @<Test if the control points are confined to one quadrant or rotating...@>=
9697simple = ((number_nonnegative(dx0) && number_nonnegative(dx1) && number_nonnegative(dx2)) ||
9698          (number_nonpositive(dx0) && number_nonpositive(dx1) && number_nonpositive(dx2)));
9699if (simple) {
9700  simple = (number_nonnegative(dy0) && number_nonnegative(dy1) && number_nonnegative(dy2)) ||
9701           (number_nonpositive(dy0) && number_nonpositive(dy1) && number_nonpositive(dy2));
9702}
9703if (!simple) {
9704  simple = (number_greaterequal(dx0, dy0) && number_greaterequal(dx1, dy1) && number_greaterequal(dx2, dy2)) ||
9705           (number_lessequal(dx0, dy0) && number_lessequal(dx1, dy1) && number_lessequal(dx2, dy2));
9706  if (simple) {
9707    mp_number neg_dx0, neg_dx1, neg_dx2;
9708    new_number(neg_dx0);
9709    new_number(neg_dx1);
9710    new_number(neg_dx2);
9711    number_clone(neg_dx0, dx0);
9712    number_clone(neg_dx1, dx1);
9713    number_clone(neg_dx2, dx2);
9714    number_negate(neg_dx0);
9715    number_negate(neg_dx1);
9716    number_negate(neg_dx2);
9717    simple =
9718      (number_greaterequal(neg_dx0, dy0) && number_greaterequal(neg_dx1, dy1) && number_greaterequal(neg_dx2, dy2)) ||
9719      (number_lessequal(neg_dx0, dy0) && number_lessequal(neg_dx1, dy1) && number_lessequal(neg_dx2, dy2));
9720    free_number (neg_dx0);
9721    free_number (neg_dx1);
9722    free_number (neg_dx2);
9723  }
9724}
9725
9726@ Since Simpson's rule is based on approximating the integrand by a parabola,
9727@^Simpson's rule@>
9728it is appropriate to use the same approximation to decide when the integral
9729reaches the intermediate value |a_goal|.  At this point
9730$$\eqalign{
9731    {\vb\dot B(0)\vb\over 3} &= \hbox{|v0|}, \qquad
9732    {\vb\dot B({1\over4})\vb\over 3} = {\hbox{|v002|}\over 2}, \qquad
9733    {\vb\dot B({1\over2})\vb\over 3} = {\hbox{|v02|}\over 2}, \cr
9734    {\vb\dot B({3\over4})\vb\over 3} &= {\hbox{|v022|}\over 2}, \qquad
9735    {\vb\dot B(1)\vb\over 3} = \hbox{|v2|} \cr
9736}
9737$$
9738and
9739$$ {\vb\dot B(t)\vb\over 3} \approx
9740  \cases{B\left(\hbox{|v0|},
9741      \hbox{|v002|}-{1\over 2}\hbox{|v0|}-{1\over 4}\hbox{|v02|},
9742      {1\over 2}\hbox{|v02|}; 2t \right)&
9743    if $t\le{1\over 2}$\cr
9744  B\left({1\over 2}\hbox{|v02|},
9745      \hbox{|v022|}-{1\over 4}\hbox{|v02|}-{1\over 2}\hbox{|v2|},
9746      \hbox{|v2|}; 2t-1 \right)&
9747    if $t\ge{1\over 2}$.\cr}
9748 \eqno (*)
9749$$
9750We can integrate $\vb\dot B(t)\vb$ by using
9751$$\int 3B(a,b,c;\tau)\,dt =
9752  {B(0,a,a+b,a+b+c;\tau) + {\rm constant} \over {d\tau\over dt}}.
9753$$
9754
9755This construction allows us to find the time when the arc length reaches
9756|a_goal| by solving a cubic equation of the form
9757$$ B(0,a,a+b,a+b+c;\tau) = x, $$
9758where $\tau$ is $2t$ or $2t+1$, $x$ is |a_goal| or |a_goal-arc1|, and $a$, $b$,
9759and $c$ are the Bernshte{\u\i}n coefficients from $(*)$ divided by
9760@^Bernshte{\u\i}n, Serge{\u\i} Natanovich@>
9761$d\tau\over dt$.  We shall define a function |solve_rising_cubic| that finds
9762$\tau$ given $a$, $b$, $c$, and $x$.
9763
9764@<Estimate when the arc length reaches |a_goal| and set |arc_test| to...@>=
9765{
9766  mp_number tmp;
9767  mp_number tmp2;
9768  mp_number tmp3;
9769  mp_number tmp4;
9770  mp_number tmp5;
9771  new_number (tmp);
9772  new_number (tmp2);
9773  new_number (tmp3);
9774  new_number (tmp4);
9775  new_number (tmp5);
9776  number_clone(tmp, v02);
9777  number_add_scaled(tmp, 2);
9778  number_half(tmp);
9779  number_half(tmp); /* (v02+2) / 4 */
9780  if (number_lessequal(a_goal, arc1)) {
9781    number_clone(tmp2, v0);
9782    number_halfp(tmp2);
9783    set_number_from_substraction(tmp3, arc1, tmp2);
9784    number_substract(tmp3, tmp);
9785    mp_solve_rising_cubic (mp, &tmp5, tmp2, tmp3, tmp, a_goal);
9786    number_halfp (tmp5);
9787    set_number_to_unity(tmp3);
9788    number_substract(tmp5, tmp3);
9789    number_substract(tmp5, tmp3);
9790    number_clone(*ret, tmp5);
9791  } else {
9792    number_clone(tmp2, v2);
9793    number_halfp(tmp2);
9794    set_number_from_substraction(tmp3, arc, arc1);
9795    number_substract(tmp3, tmp);
9796    number_substract(tmp3, tmp2);
9797    set_number_from_substraction(tmp4, a_goal, arc1);
9798    mp_solve_rising_cubic (mp, &tmp5, tmp, tmp3, tmp2, tmp4);
9799    number_halfp(tmp5);
9800    set_number_to_unity(tmp2);
9801    set_number_to_unity(tmp3);
9802    number_half(tmp2);
9803    number_substract(tmp2, tmp3);
9804    number_substract(tmp2, tmp3);
9805    set_number_from_addition(*ret, tmp2, tmp5);
9806  }
9807  free_number (tmp);
9808  free_number (tmp2);
9809  free_number (tmp3);
9810  free_number (tmp4);
9811  free_number (tmp5);
9812  goto DONE;
9813}
9814
9815
9816@ Here is the |solve_rising_cubic| routine that finds the time~$t$ when
9817$$ B(0, a, a+b, a+b+c; t) = x. $$
9818This routine is based on |crossing_point| but is simplified by the
9819assumptions that $B(a,b,c;t)\ge0$ for $0\le t\le1$ and that |0<=x<=a+b+c|.
9820If rounding error causes this condition to be violated slightly, we just ignore
9821it and proceed with binary search.  This finds a time when the function value
9822reaches |x| and the slope is positive.
9823
9824@<Declarations@>=
9825static void mp_solve_rising_cubic (MP mp, mp_number *ret, mp_number a, mp_number b, mp_number c, mp_number x);
9826
9827@ @c
9828void mp_solve_rising_cubic (MP mp, mp_number *ret, mp_number a_orig, mp_number b_orig, mp_number c_orig, mp_number x_orig) {
9829  mp_number abc;
9830  mp_number a, b, c, x;      /* local versions of arguments */
9831  mp_number ab, bc, ac;    /* bisection results */
9832  mp_number t;    /* $2^k+q$ where unscaled answer is in $[q2^{-k},(q+1)2^{-k})$ */
9833  mp_number xx;   /* temporary for updating |x| */
9834  mp_number neg_x; /* temporary for an |if| */
9835  if (number_negative(a_orig) || number_negative(c_orig))
9836    mp_confusion (mp, "rising?");
9837@:this can't happen rising?}{\quad rising?@>;
9838  new_number (t);
9839  new_number (abc);
9840  new_number (a);
9841  new_number (b);
9842  new_number (c);
9843  new_number (x);
9844  number_clone(a, a_orig);
9845  number_clone(b, b_orig);
9846  number_clone(c, c_orig);
9847  number_clone(x, x_orig);
9848  new_number (ab);
9849  new_number (bc);
9850  new_number (ac);
9851  new_number (xx);
9852  new_number (neg_x);
9853  set_number_from_addition(abc, a, b);
9854  number_add(abc, c);
9855  if (number_nonpositive(x)) {
9856    set_number_to_zero(*ret);
9857  } else if (number_greaterequal(x, abc)) {
9858    set_number_to_unity(*ret);
9859  } else {
9860    number_clone (t, epsilon_t);
9861    @<Rescale if necessary to make sure |a|, |b|, and |c| are all less than
9862      |EL_GORDO div 3|@>;
9863    do {
9864      number_add (t, t);
9865      @<Subdivide the B\'ezier quadratic defined by |a|, |b|, |c|@>;
9866      number_clone(xx,x);
9867      number_substract(xx, a);
9868      number_substract(xx, ab);
9869      number_substract(xx, ac);
9870      number_clone(neg_x, x);
9871      number_negate(neg_x);
9872      if (number_less(xx, neg_x)) {
9873        number_double(x);
9874        number_clone(b, ab);
9875        number_clone(c, ac);
9876      } else {
9877        number_add(x, xx);
9878        number_clone(a, ac);
9879        number_clone(b, bc);
9880        number_add (t, epsilon_t);
9881      }
9882    } while (number_less (t, unity_t));
9883    set_number_from_substraction(*ret, t, unity_t);
9884  }
9885  free_number (abc);
9886  free_number (t);
9887  free_number (a);
9888  free_number (b);
9889  free_number (c);
9890  free_number (ab);
9891  free_number (bc);
9892  free_number (ac);
9893  free_number (xx);
9894  free_number (x);
9895  free_number (neg_x);
9896}
9897
9898
9899@ @<Subdivide the B\'ezier quadratic defined by |a|, |b|, |c|@>=
9900set_number_from_addition(ab, a, b);
9901number_half(ab);
9902set_number_from_addition(bc, b, c);
9903number_half(bc);
9904set_number_from_addition(ac, ab, bc);
9905number_half(ac);
9906
9907@ The upper bound on |a|, |b|, and |c|:
9908
9909@d one_third_inf_t  ((math_data *)mp->math)->one_third_inf_t
9910
9911@<Rescale if necessary to make sure |a|, |b|, and |c| are all less than...@>=
9912while (number_greater(a, one_third_inf_t) ||
9913       number_greater(b, one_third_inf_t) ||
9914       number_greater(c, one_third_inf_t)) {
9915  number_halfp(a);
9916  number_half(b);
9917  number_halfp(c);
9918  number_halfp(x);
9919}
9920
9921
9922@ It is convenient to have a simpler interface to |arc_test| that requires no
9923unnecessary arguments and ensures that each $({\it dx},{\it dy})$ pair has
9924length less than |fraction_four|.
9925
9926@c
9927static void mp_do_arc_test (MP mp, mp_number *ret, mp_number dx0, mp_number dy0, mp_number dx1,
9928                              mp_number dy1, mp_number dx2, mp_number dy2, mp_number a_goal) {
9929  mp_number v0, v1, v2;    /* length of each $({\it dx},{\it dy})$ pair */
9930  mp_number v02;   /* twice the norm of the quadratic at $t={1\over2}$ */
9931  new_number (v0);
9932  new_number (v1);
9933  new_number (v2);
9934  pyth_add (v0, dx0, dy0);
9935  pyth_add (v1, dx1, dy1);
9936  pyth_add (v2, dx2, dy2);
9937  if ((number_greaterequal(v0, fraction_four_t)) ||
9938      (number_greaterequal(v1, fraction_four_t)) ||
9939      (number_greaterequal(v2, fraction_four_t))) {
9940    mp->arith_error = true;
9941    if (number_infinite(a_goal)) {
9942      set_number_to_inf(*ret);
9943    } else {
9944      set_number_to_unity(*ret);
9945      number_double(*ret);
9946      number_negate(*ret);
9947    }
9948  } else {
9949    mp_number arg1, arg2;
9950    new_number (v02);
9951    new_number (arg1);
9952    new_number (arg2);
9953    set_number_from_addition(arg1, dx0, dx2);
9954    number_half(arg1);
9955    number_add(arg1, dx1);
9956    set_number_from_addition(arg2, dy0, dy2);
9957    number_half(arg2);
9958    number_add(arg2, dy1);
9959    pyth_add (v02, arg1, arg2);
9960    free_number(arg1);
9961    free_number(arg2);
9962    mp_arc_test (mp, ret, dx0, dy0, dx1, dy1, dx2, dy2, v0, v02, v2, a_goal, arc_tol_k);
9963    free_number (v02);
9964  }
9965  free_number (v0);
9966  free_number (v1);
9967  free_number (v2);
9968}
9969
9970
9971@ Now it is easy to find the arc length of an entire path.
9972
9973@c
9974static void mp_get_arc_length (MP mp, mp_number *ret, mp_knot h) {
9975  mp_knot p, q; /* for traversing the path */
9976  mp_number a;  /* current arc length */
9977  mp_number a_tot; /* total arc length */
9978  mp_number arg1, arg2, arg3, arg4, arg5, arg6;
9979  mp_number arcgoal;
9980  p = h;
9981  new_number (a_tot);
9982  new_number (arg1);
9983  new_number (arg2);
9984  new_number (arg3);
9985  new_number (arg4);
9986  new_number (arg5);
9987  new_number (arg6);
9988  new_number (a);
9989  new_number(arcgoal);
9990  set_number_to_inf(arcgoal);
9991  while (mp_right_type (p) != mp_endpoint) {
9992    q = mp_next_knot (p);
9993    set_number_from_substraction(arg1, p->right_x, p->x_coord);
9994    set_number_from_substraction(arg2, p->right_y, p->y_coord);
9995    set_number_from_substraction(arg3, q->left_x,  p->right_x);
9996    set_number_from_substraction(arg4, q->left_y,  p->right_y);
9997    set_number_from_substraction(arg5, q->x_coord, q->left_x);
9998    set_number_from_substraction(arg6, q->y_coord, q->left_y);
9999    mp_do_arc_test (mp, &a, arg1, arg2, arg3, arg4, arg5, arg6, arcgoal);
10000    slow_add (a_tot, a, a_tot);
10001    if (q == h)
10002      break;
10003    else
10004      p = q;
10005  }
10006  free_number (arcgoal);
10007  free_number (a);
10008  free_number (arg1);
10009  free_number (arg2);
10010  free_number (arg3);
10011  free_number (arg4);
10012  free_number (arg5);
10013  free_number (arg6);
10014  check_arith();
10015  number_clone (*ret, a_tot);
10016  free_number (a_tot);
10017}
10018
10019
10020@ The inverse operation of finding the time on a path~|h| when the arc length
10021reaches some value |arc0| can also be accomplished via |do_arc_test|.  Some care
10022is required to handle very large times or negative times on cyclic paths.  For
10023non-cyclic paths, |arc0| values that are negative or too large cause
10024|get_arc_time| to return 0 or the length of path~|h|.
10025
10026If |arc0| is greater than the arc length of a cyclic path~|h|, the result is a
10027time value greater than the length of the path.  Since it could be much greater,
10028we must be prepared to compute the arc length of path~|h| and divide this into
10029|arc0| to find how many multiples of the length of path~|h| to add.
10030
10031@c
10032static void mp_get_arc_time (MP mp, mp_number *ret, mp_knot h, mp_number arc0_orig) {
10033  mp_knot p, q; /* for traversing the path */
10034  mp_number t_tot; /* accumulator for the result */
10035  mp_number t;     /* the result of |do_arc_test| */
10036  mp_number arc, arc0;   /* portion of |arc0| not used up so far */
10037  mp_number arg1, arg2, arg3, arg4, arg5, arg6; /* |do_arc_test| arguments */
10038  if (number_negative(arc0_orig)) {
10039    @<Deal with a negative |arc0_orig| value and |return|@>;
10040  }
10041  new_number (t_tot);
10042  new_number (arc0);
10043  number_clone(arc0, arc0_orig);
10044  if (number_infinite(arc0)) {
10045    number_add_scaled (arc0, -1);
10046  }
10047  new_number (arc);
10048  number_clone(arc, arc0);
10049  p = h;
10050  new_number (arg1);
10051  new_number (arg2);
10052  new_number (arg3);
10053  new_number (arg4);
10054  new_number (arg5);
10055  new_number (arg6);
10056  new_number (t);
10057  while ((mp_right_type (p) != mp_endpoint) && number_positive(arc)) {
10058    q = mp_next_knot (p);
10059    set_number_from_substraction(arg1, p->right_x, p->x_coord);
10060    set_number_from_substraction(arg2, p->right_y, p->y_coord);
10061    set_number_from_substraction(arg3, q->left_x,  p->right_x);
10062    set_number_from_substraction(arg4, q->left_y,  p->right_y);
10063    set_number_from_substraction(arg5, q->x_coord, q->left_x);
10064    set_number_from_substraction(arg6, q->y_coord, q->left_y);
10065    mp_do_arc_test (mp, &t, arg1, arg2, arg3, arg4, arg5, arg6, arc);
10066    @<Update |arc| and |t_tot| after |do_arc_test| has just returned |t|@>;
10067    if (q == h) {
10068      @<Update |t_tot| and |arc| to avoid going around the cyclic
10069        path too many times but set |arith_error:=true| and |goto done| on
10070        overflow@>;
10071    }
10072    p = q;
10073  }
10074  check_arith();
10075  number_clone (*ret, t_tot);
10076RETURN:
10077  free_number (t_tot);
10078  free_number (t);
10079  free_number (arc);
10080  free_number (arc0);
10081  free_number (arg1);
10082  free_number (arg2);
10083  free_number (arg3);
10084  free_number (arg4);
10085  free_number (arg5);
10086  free_number (arg6);
10087}
10088
10089
10090@ @<Update |arc| and |t_tot| after |do_arc_test| has just returned |t|@>=
10091if (number_negative(t)) {
10092  number_add (t_tot, t);
10093  number_add (t_tot, two_t);
10094  set_number_to_zero(arc);
10095} else {
10096  number_add (t_tot, unity_t);
10097  number_substract(arc, t);
10098}
10099
10100
10101@ @<Deal with a negative |arc0_orig| value and |return|@>=
10102{
10103  if (mp_left_type (h) == mp_endpoint) {
10104    set_number_to_zero (*ret);
10105  } else {
10106    mp_number neg_arc0;
10107    p = mp_htap_ypoc (mp, h);
10108    new_number(neg_arc0);
10109    number_clone(neg_arc0, arc0_orig);
10110    number_negate(neg_arc0);
10111    mp_get_arc_time (mp, ret, p, neg_arc0);
10112    number_negate(*ret);
10113    mp_toss_knot_list (mp, p);
10114    free_number (neg_arc0);
10115  }
10116  check_arith();
10117  return;
10118}
10119
10120
10121@ @<Update |t_tot| and |arc| to avoid going around the cyclic...@>=
10122if (number_positive(arc)) {
10123  mp_number n, n1, d1, v1;
10124  new_number (n);
10125  new_number (n1);
10126  new_number (d1);
10127  new_number (v1);
10128
10129  set_number_from_substraction (d1, arc0, arc); /* d1 = arc0 - arc */
10130  set_number_from_div (n1, arc, d1); /* n1 = (arc / d1) */
10131  number_clone (n, n1);
10132  set_number_from_mul (n1, n1, d1); /* n1 = (n1 * d1) */
10133  number_substract (arc, n1); /* arc = arc - n1 */
10134
10135  number_clone (d1, inf_t);         /* reuse d1 */
10136  number_clone (v1, n);             /* v1 = n */
10137  number_add (v1, epsilon_t);       /* v1 = n1+1 */
10138  set_number_from_div (d1, d1, v1); /* |d1 = EL_GORDO / v1| */
10139  if (number_greater (t_tot, d1)) {
10140    mp->arith_error = true;
10141    check_arith();
10142    set_number_to_inf(*ret);
10143    free_number (n);
10144    free_number (n1);
10145    free_number (d1);
10146    free_number (v1);
10147    goto RETURN;
10148  }
10149  set_number_from_mul (t_tot, t_tot, v1);
10150  free_number (n);
10151  free_number (n1);
10152  free_number (d1);
10153  free_number (v1);
10154}
10155
10156@* Data structures for pens.
10157A Pen in \MP\ can be either elliptical or polygonal.  Elliptical pens result
10158in \ps\ \&{stroke} commands, while anything drawn with a polygonal pen is
10159@:stroke}{\&{stroke} command@>
10160converted into an area fill as described in the next part of this program.
10161The mathematics behind this process is based on simple aspects of the theory
10162of tracings developed by Leo Guibas, Lyle Ramshaw, and Jorge Stolfi
10163[``A kinematic framework for computational geometry,'' Proc.\ IEEE Symp.\
10164Foundations of Computer Science {\bf 24} (1983), 100--111].
10165
10166Polygonal pens are created from paths via \MP's \&{makepen} primitive.
10167@:makepen_}{\&{makepen} primitive@>
10168This path representation is almost sufficient for our purposes except that
10169a pen path should always be a convex polygon with the vertices in
10170counter-clockwise order.
10171Since we will need to scan pen polygons both forward and backward, a pen
10172should be represented as a doubly linked ring of knot nodes.  There is
10173room for the extra back pointer because we do not need the
10174|mp_left_type| or |mp_right_type| fields.  In fact, we don't need the |left_x|,
10175|left_y|, |right_x|, or |right_y| fields either but we leave these alone
10176so that certain procedures can operate on both pens and paths.  In particular,
10177pens can be copied using |copy_path| and recycled using |toss_knot_list|.
10178
10179@ The |make_pen| procedure turns a path into a pen by initializing
10180the |prev_knot| pointers and making sure the knots form a convex polygon.
10181Thus each cubic in the given path becomes a straight line and the control
10182points are ignored.  If the path is not cyclic, the ends are connected by a
10183straight line.
10184
10185@d copy_pen(A) mp_make_pen(mp, mp_copy_path(mp, (A)),false)
10186
10187@c
10188static mp_knot mp_make_pen (MP mp, mp_knot h, boolean need_hull) {
10189  mp_knot p, q; /* two consecutive knots */
10190  q = h;
10191  do {
10192    p = q;
10193    q = mp_next_knot (q);
10194    mp_prev_knot (q) = p;
10195  } while (q != h);
10196  if (need_hull) {
10197    h = mp_convex_hull (mp, h);
10198    @<Make sure |h| isn't confused with an elliptical pen@>;
10199  }
10200  return h;
10201}
10202
10203
10204@ The only information required about an elliptical pen is the overall
10205transformation that has been applied to the original \&{pencircle}.
10206@:pencircle_}{\&{pencircle} primitive@>
10207Since it suffices to keep track of how the three points $(0,0)$, $(1,0)$,
10208and $(0,1)$ are transformed, an elliptical pen can be stored in a single
10209knot node and transformed as if it were a path.
10210
10211@d pen_is_elliptical(A) ((A)==mp_next_knot((A)))
10212
10213@c
10214static mp_knot mp_get_pen_circle (MP mp, mp_number diam) {
10215  mp_knot h;    /* the knot node to return */
10216  h = mp_new_knot (mp);
10217  mp_next_knot (h) = h;
10218  mp_prev_knot (h) = h;
10219  mp_originator (h) = mp_program_code;
10220  set_number_to_zero(h->x_coord);
10221  set_number_to_zero(h->y_coord);
10222  number_clone(h->left_x, diam);
10223  set_number_to_zero(h->left_y);
10224  set_number_to_zero(h->right_x);
10225  number_clone(h->right_y, diam);
10226  return h;
10227}
10228
10229
10230@ If the polygon being returned by |make_pen| has only one vertex, it will
10231be interpreted as an elliptical pen.  This is no problem since a degenerate
10232polygon can equally well be thought of as a degenerate ellipse.  We need only
10233initialize the |left_x|, |left_y|, |right_x|, and |right_y| fields.
10234
10235@<Make sure |h| isn't confused with an elliptical pen@>=
10236if (pen_is_elliptical (h)) {
10237  number_clone(h->left_x, h->x_coord);
10238  number_clone(h->left_y, h->y_coord);
10239  number_clone(h->right_x, h->x_coord);
10240  number_clone(h->right_y, h->y_coord);
10241}
10242
10243@ Printing a polygonal pen is very much like printing a path
10244
10245@<Declarations@>=
10246static void mp_pr_pen (MP mp, mp_knot h);
10247
10248@ @c
10249void mp_pr_pen (MP mp, mp_knot h) {
10250  mp_knot p, q; /* for list traversal */
10251  if (pen_is_elliptical (h)) {
10252    @<Print the elliptical pen |h|@>;
10253  } else {
10254    p = h;
10255    do {
10256      mp_print_two (mp, p->x_coord, p->y_coord);
10257      mp_print_nl (mp, " .. ");
10258      @<Advance |p| making sure the links are OK and |return| if there is
10259        a problem@>;
10260    } while (p != h);
10261    mp_print (mp, "cycle");
10262  }
10263}
10264
10265
10266@ @<Advance |p| making sure the links are OK and |return| if there is...@>=
10267q = mp_next_knot (p);
10268if ((q == NULL) || (mp_prev_knot (q) != p)) {
10269  mp_print_nl (mp, "???");
10270  return;                       /* this won't happen */
10271@.???@>
10272}
10273p = q
10274
10275@ @<Print the elliptical pen |h|@>=
10276{
10277  mp_number v1;
10278  new_number (v1);
10279  mp_print (mp, "pencircle transformed (");
10280  print_number (h->x_coord);
10281  mp_print_char (mp, xord (','));
10282  print_number (h->y_coord);
10283  mp_print_char (mp, xord (','));
10284  set_number_from_substraction (v1, h->left_x, h->x_coord);
10285  print_number (v1);
10286  mp_print_char (mp, xord (','));
10287  set_number_from_substraction (v1, h->right_x, h->x_coord);
10288  print_number (v1);
10289  mp_print_char (mp, xord (','));
10290  set_number_from_substraction (v1, h->left_y, h->y_coord);
10291  print_number (v1);
10292  mp_print_char (mp, xord (','));
10293  set_number_from_substraction (v1, h->right_y, h->y_coord);
10294  print_number (v1);
10295  mp_print_char (mp, xord (')'));
10296  free_number (v1);
10297}
10298
10299
10300@ Here us another version of |pr_pen| that prints the pen as a diagnostic
10301message.
10302
10303@<Declarations@>=
10304static void mp_print_pen (MP mp, mp_knot h, const char *s, boolean nuline);
10305
10306@ @c
10307void mp_print_pen (MP mp, mp_knot h, const char *s, boolean nuline) {
10308  mp_print_diagnostic (mp, "Pen", s, nuline);
10309  mp_print_ln (mp);
10310@.Pen at line...@>;
10311  mp_pr_pen (mp, h);
10312  mp_end_diagnostic (mp, true);
10313}
10314
10315
10316@ Making a polygonal pen into a path involves restoring the |mp_left_type| and
10317|mp_right_type| fields and setting the control points so as to make a polygonal
10318path.
10319
10320@c
10321static void mp_make_path (MP mp, mp_knot h) {
10322  mp_knot p;    /* for traversing the knot list */
10323  quarterword k;        /* a loop counter */
10324  @<Other local variables in |make_path|@>;
10325  FUNCTION_TRACE1 ("make_path()\n");
10326  if (pen_is_elliptical (h)) {
10327    FUNCTION_TRACE1 ("make_path(elliptical)\n");
10328    @<Make the elliptical pen |h| into a path@>;
10329  } else {
10330    p = h;
10331    do {
10332      mp_left_type (p) = mp_explicit;
10333      mp_right_type (p) = mp_explicit;
10334      @<copy the coordinates of knot |p| into its control points@>;
10335      p = mp_next_knot (p);
10336    } while (p != h);
10337  }
10338}
10339
10340
10341@ @<copy the coordinates of knot |p| into its control points@>=
10342number_clone (p->left_x, p->x_coord);
10343number_clone (p->left_y, p->y_coord);
10344number_clone (p->right_x, p->x_coord);
10345number_clone (p->right_y, p->y_coord)
10346
10347
10348@ We need an eight knot path to get a good approximation to an ellipse.
10349
10350@<Make the elliptical pen |h| into a path@>=
10351{
10352  mp_number center_x, center_y;      /* translation parameters for an elliptical pen */
10353  mp_number width_x, width_y;        /* the effect of a unit change in $x$ */
10354  mp_number height_x, height_y;      /* the effect of a unit change in $y$ */
10355  mp_number dx, dy;  /* the vector from knot |p| to its right control point */
10356  new_number (center_x);
10357  new_number (center_y);
10358  new_number (width_x);
10359  new_number (width_y);
10360  new_number (height_x);
10361  new_number (height_y);
10362  new_number (dx);
10363  new_number (dy);
10364  @<Extract the transformation parameters from the elliptical pen~|h|@>;
10365  p = h;
10366  for (k = 0; k <= 7; k++) {
10367    @<Initialize |p| as the |k|th knot of a circle of unit diameter,
10368      transforming it appropriately@>;
10369    if (k == 7)
10370      mp_next_knot (p) = h;
10371    else
10372      mp_next_knot (p) = mp_new_knot (mp);
10373    p = mp_next_knot (p);
10374  }
10375  free_number (dx);
10376  free_number (dy);
10377  free_number (center_x);
10378  free_number (center_y);
10379  free_number (width_x);
10380  free_number (width_y);
10381  free_number (height_x);
10382  free_number (height_y);
10383}
10384
10385
10386@ @<Extract the transformation parameters from the elliptical pen~|h|@>=
10387number_clone (center_x, h->x_coord);
10388number_clone (center_y, h->y_coord);
10389set_number_from_substraction (width_x, h->left_x, center_x);
10390set_number_from_substraction (width_y, h->left_y, center_y);
10391set_number_from_substraction (height_x, h->right_x, center_x);
10392set_number_from_substraction (height_y, h->right_y, center_y);
10393
10394@ @<Other local variables in |make_path|@>=
10395integer kk;
10396  /* |k| advanced $270^\circ$ around the ring (cf. $\sin\theta=\cos(\theta+270)$) */
10397
10398@ The only tricky thing here are the tables |half_cos| and |d_cos| used to
10399find the point $k/8$ of the way around the circle and the direction vector
10400to use there.
10401
10402@<Initialize |p| as the |k|th knot of a circle of unit diameter,...@>=
10403kk = (k + 6) % 8;
10404{
10405  mp_number r1, r2;
10406  new_fraction (r1);
10407  new_fraction (r2);
10408  take_fraction (r1, mp->half_cos[k], width_x);
10409  take_fraction (r2, mp->half_cos[kk], height_x);
10410  number_add (r1, r2);
10411  set_number_from_addition (p->x_coord, center_x, r1);
10412  take_fraction (r1, mp->half_cos[k],  width_y);
10413  take_fraction (r2, mp->half_cos[kk], height_y);
10414  number_add (r1, r2);
10415  set_number_from_addition (p->y_coord, center_y, r1);
10416  take_fraction (r1, mp->d_cos[kk], width_x);
10417  take_fraction (r2, mp->d_cos[k], height_x);
10418  number_clone (dx, r1);
10419  number_negate (dx);
10420  number_add (dx, r2);
10421  take_fraction (r1, mp->d_cos[kk], width_y);
10422  take_fraction (r2, mp->d_cos[k], height_y);
10423  number_clone (dy, r1);
10424  number_negate (dy);
10425  number_add (dy, r2);
10426  set_number_from_addition (p->right_x, p->x_coord, dx);
10427  set_number_from_addition (p->right_y, p->y_coord, dy);
10428  set_number_from_substraction (p->left_x, p->x_coord, dx);
10429  set_number_from_substraction (p->left_y, p->y_coord, dy);
10430  free_number (r1);
10431  free_number (r2);
10432}
10433mp_left_type (p) = mp_explicit;
10434mp_right_type (p) = mp_explicit;
10435mp_originator (p) = mp_program_code
10436
10437@ @<Glob...@>=
10438mp_number half_cos[8];   /* ${1\over2}\cos(45k)$ */
10439mp_number d_cos[8];      /* a magic constant times $\cos(45k)$ */
10440
10441@ The magic constant for |d_cos| is the distance between $({1\over2},0)$ and
10442$({1\over4}\sqrt2,{1\over4}\sqrt2)$ times the result of the |velocity|
10443function for $\theta=\phi=22.5^\circ$.  This comes out to be
10444$$ d = {\sqrt{2-\sqrt2}\over 3+3\cos22.5^\circ}
10445  \approx 0.132608244919772.
10446$$
10447
10448@<Set init...@>=
10449for (k = 0; k <= 7; k++) {
10450  new_fraction (mp->half_cos[k]);
10451  new_fraction (mp->d_cos[k]);
10452}
10453number_clone (mp->half_cos[0], fraction_half_t);
10454number_clone (mp->half_cos[1], twentysixbits_sqrt2_t);
10455number_clone (mp->half_cos[2], zero_t);
10456number_clone (mp->d_cos[0], twentyeightbits_d_t);
10457number_clone (mp->d_cos[1], twentysevenbits_sqrt2_d_t);
10458number_clone (mp->d_cos[2], zero_t);
10459for (k = 3; k <= 4; k++) {
10460  number_clone (mp->half_cos[k], mp->half_cos[4 - k]);
10461  number_negate (mp->half_cos[k]);
10462  number_clone (mp->d_cos[k], mp->d_cos[4 - k]);
10463  number_negate (mp->d_cos[k]);
10464}
10465for (k = 5; k <= 7; k++) {
10466  number_clone (mp->half_cos[k], mp->half_cos[8 - k]);
10467  number_clone (mp->d_cos[k], mp->d_cos[8 - k]);
10468}
10469
10470@ @<Dealloc...@>=
10471for (k = 0; k <= 7; k++) {
10472  free_number (mp->half_cos[k]);
10473  free_number (mp->d_cos[k]);
10474}
10475
10476
10477@ The |convex_hull| function forces a pen polygon to be convex when it is
10478returned by |make_pen| and after any subsequent transformation where rounding
10479error might allow the convexity to be lost.
10480The convex hull algorithm used here is described by F.~P. Preparata and
10481M.~I. Shamos [{\sl Computational Geometry}, Springer-Verlag, 1985].
10482
10483@<Declarations@>=
10484static mp_knot mp_convex_hull (MP mp, mp_knot h);
10485
10486@ @c
10487mp_knot mp_convex_hull (MP mp, mp_knot h) {                               /* Make a polygonal pen convex */
10488  mp_knot l, r; /* the leftmost and rightmost knots */
10489  mp_knot p, q; /* knots being scanned */
10490  mp_knot s;    /* the starting point for an upcoming scan */
10491  mp_number dx, dy;        /* a temporary pointer */
10492  mp_knot ret;
10493  new_number (dx);
10494  new_number (dy);
10495  if (pen_is_elliptical (h)) {
10496    ret = h;
10497  } else {
10498    @<Set |l| to the leftmost knot in polygon~|h|@>;
10499    @<Set |r| to the rightmost knot in polygon~|h|@>;
10500    if (l != r) {
10501      s = mp_next_knot (r);
10502      @<Find any knots on the path from |l| to |r| above the |l|-|r| line and
10503        move them past~|r|@>;
10504      @<Find any knots on the path from |s| to |l| below the |l|-|r| line and
10505        move them past~|l|@>;
10506      @<Sort the path from |l| to |r| by increasing $x$@>;
10507      @<Sort the path from |r| to |l| by decreasing $x$@>;
10508    }
10509    if (l != mp_next_knot (l)) {
10510      @<Do a Gramm scan and remove vertices where there is no left turn@>;
10511    }
10512    ret = l;
10513  }
10514  free_number (dx);
10515  free_number (dy);
10516  return ret;
10517}
10518
10519
10520@ All comparisons are done primarily on $x$ and secondarily on $y$.
10521
10522@<Set |l| to the leftmost knot in polygon~|h|@>=
10523l = h;
10524p = mp_next_knot (h);
10525while (p != h) {
10526  if (number_lessequal (p->x_coord, l->x_coord))
10527    if ((number_less (p->x_coord, l->x_coord)) ||
10528        (number_less (p->y_coord, l->y_coord)))
10529      l = p;
10530  p = mp_next_knot (p);
10531}
10532
10533
10534@ @<Set |r| to the rightmost knot in polygon~|h|@>=
10535r = h;
10536p = mp_next_knot (h);
10537while (p != h) {
10538  if (number_greaterequal(p->x_coord, r->x_coord))
10539    if (number_greater (p->x_coord, r->x_coord) ||
10540        number_greater (p->y_coord, r->y_coord))
10541      r = p;
10542  p = mp_next_knot (p);
10543}
10544
10545
10546@ @<Find any knots on the path from |l| to |r| above the |l|-|r| line...@>=
10547{
10548  mp_number ab_vs_cd;
10549  mp_number arg1, arg2;
10550  new_number (arg1);
10551  new_number (arg2);
10552  new_number (ab_vs_cd);
10553  set_number_from_substraction (dx, r->x_coord, l->x_coord);
10554  set_number_from_substraction (dy, r->y_coord, l->y_coord);
10555  p = mp_next_knot (l);
10556  while (p != r) {
10557    q = mp_next_knot (p);
10558    set_number_from_substraction (arg1, p->y_coord, l->y_coord);
10559    set_number_from_substraction (arg2, p->x_coord, l->x_coord);
10560    ab_vs_cd (ab_vs_cd, dx, arg1, dy, arg2);
10561    if (number_positive(ab_vs_cd))
10562      mp_move_knot (mp, p, r);
10563    p = q;
10564  }
10565  free_number (ab_vs_cd);
10566  free_number (arg1);
10567  free_number (arg2);
10568}
10569
10570
10571@ The |move_knot| procedure removes |p| from a doubly linked list and inserts
10572it after |q|.
10573
10574@ @<Declarations@>=
10575static void mp_move_knot (MP mp, mp_knot p, mp_knot q);
10576
10577@ @c
10578void mp_move_knot (MP mp, mp_knot p, mp_knot q) {
10579  (void) mp;
10580  mp_next_knot (mp_prev_knot (p)) = mp_next_knot (p);
10581  mp_prev_knot (mp_next_knot (p)) = mp_prev_knot (p);
10582  mp_prev_knot (p) = q;
10583  mp_next_knot (p) = mp_next_knot (q);
10584  mp_next_knot (q) = p;
10585  mp_prev_knot (mp_next_knot (p)) = p;
10586}
10587
10588
10589@ @<Find any knots on the path from |s| to |l| below the |l|-|r| line...@>=
10590{
10591  mp_number ab_vs_cd;
10592  mp_number arg1, arg2;
10593  new_number (ab_vs_cd);
10594  new_number (arg1);
10595  new_number (arg2);
10596  p = s;
10597  while (p != l) {
10598    q = mp_next_knot (p);
10599    set_number_from_substraction (arg1, p->y_coord, l->y_coord);
10600    set_number_from_substraction (arg2, p->x_coord, l->x_coord);
10601    ab_vs_cd (ab_vs_cd, dx, arg1, dy, arg2);
10602    if (number_negative(ab_vs_cd))
10603      mp_move_knot (mp, p, l);
10604    p = q;
10605  }
10606  free_number (ab_vs_cd);
10607  free_number (arg1);
10608  free_number (arg2);
10609}
10610
10611
10612@ The list is likely to be in order already so we just do linear insertions.
10613Secondary comparisons on $y$ ensure that the sort is consistent with the
10614choice of |l| and |r|.
10615
10616@<Sort the path from |l| to |r| by increasing $x$@>=
10617p = mp_next_knot (l);
10618while (p != r) {
10619  q = mp_prev_knot (p);
10620  while (number_greater(q->x_coord, p->x_coord))
10621    q = mp_prev_knot (q);
10622  while (number_equal(q->x_coord, p->x_coord)) {
10623    if (number_greater(q->y_coord, p->y_coord))
10624      q = mp_prev_knot (q);
10625    else
10626      break;
10627  }
10628  if (q == mp_prev_knot (p)) {
10629    p = mp_next_knot (p);
10630  } else {
10631    p = mp_next_knot (p);
10632    mp_move_knot (mp, mp_prev_knot (p), q);
10633  }
10634}
10635
10636
10637@ @<Sort the path from |r| to |l| by decreasing $x$@>=
10638p = mp_next_knot (r);
10639while (p != l) {
10640  q = mp_prev_knot (p);
10641  while (number_less(q->x_coord, p->x_coord))
10642    q = mp_prev_knot (q);
10643  while (number_equal(q->x_coord, p->x_coord)) {
10644    if (number_less (q->y_coord, p->y_coord))
10645      q = mp_prev_knot (q);
10646    else
10647      break;
10648  }
10649  if (q == mp_prev_knot (p)) {
10650    p = mp_next_knot (p);
10651  } else {
10652    p = mp_next_knot (p);
10653    mp_move_knot (mp, mp_prev_knot (p), q);
10654  }
10655}
10656
10657
10658@ The condition involving |ab_vs_cd| tests if there is not a left turn
10659at knot |q|.  There usually will be a left turn so we streamline the case
10660where the |then| clause is not executed.
10661
10662@<Do a Gramm scan and remove vertices where there...@>=
10663{
10664  mp_number ab_vs_cd;
10665  mp_number arg1, arg2;
10666  new_number (arg1);
10667  new_number (arg2);
10668  new_number (ab_vs_cd);
10669  p = l;
10670  q = mp_next_knot (l);
10671  while (1) {
10672    set_number_from_substraction (dx, q->x_coord, p->x_coord);
10673    set_number_from_substraction (dy, q->y_coord, p->y_coord);
10674    p = q;
10675    q = mp_next_knot (q);
10676    if (p == l)
10677      break;
10678    if (p != r) {
10679      set_number_from_substraction (arg1, q->y_coord, p->y_coord);
10680      set_number_from_substraction (arg2, q->x_coord, p->x_coord);
10681      ab_vs_cd (ab_vs_cd, dx, arg1, dy, arg2);
10682      if (number_nonpositive(ab_vs_cd)) {
10683        @<Remove knot |p| and back up |p| and |q| but don't go past |l|@>;
10684      }
10685    }
10686  }
10687  free_number (ab_vs_cd);
10688  free_number (arg1);
10689  free_number (arg2);
10690}
10691
10692
10693@ @<Remove knot |p| and back up |p| and |q| but don't go past |l|@>=
10694{
10695  s = mp_prev_knot (p);
10696  mp_xfree (p);
10697  mp_next_knot (s) = q;
10698  mp_prev_knot (q) = s;
10699  if (s == l) {
10700    p = s;
10701  } else {
10702    p = mp_prev_knot (s);
10703    q = s;
10704  }
10705}
10706
10707
10708@ The |find_offset| procedure sets global variables |(cur_x,cur_y)| to the
10709offset associated with the given direction |(x,y)|.  If two different offsets
10710apply, it chooses one of them.
10711
10712@c
10713static void mp_find_offset (MP mp, mp_number x_orig, mp_number y_orig, mp_knot h) {
10714  mp_knot p, q; /* consecutive knots */
10715  if (pen_is_elliptical (h)) {
10716    mp_fraction xx, yy;      /* untransformed offset for an elliptical pen */
10717    mp_number wx, wy, hx, hy; /* the transformation matrix for an elliptical pen */
10718    mp_fraction d;   /* a temporary register */
10719    new_fraction(xx);
10720    new_fraction(yy);
10721    new_number(wx);
10722    new_number(wy);
10723    new_number(hx);
10724    new_number(hy);
10725    new_fraction(d);
10726    @<Find the offset for |(x,y)| on the elliptical pen~|h|@>
10727    free_number (xx);
10728    free_number (yy);
10729    free_number (wx);
10730    free_number (wy);
10731    free_number (hx);
10732    free_number (hy);
10733    free_number (d);
10734  } else {
10735    mp_number ab_vs_cd;
10736    mp_number arg1, arg2;
10737    new_number (arg1);
10738    new_number (arg2);
10739    new_number (ab_vs_cd);
10740    q = h;
10741    do {
10742      p = q;
10743      q = mp_next_knot (q);
10744      set_number_from_substraction (arg1, q->x_coord, p->x_coord);
10745      set_number_from_substraction (arg2, q->y_coord, p->y_coord);
10746      ab_vs_cd (ab_vs_cd, arg1, y_orig, arg2, x_orig);
10747    } while (number_negative(ab_vs_cd));
10748    do {
10749      p = q;
10750      q = mp_next_knot (q);
10751      set_number_from_substraction (arg1, q->x_coord, p->x_coord);
10752      set_number_from_substraction (arg2, q->y_coord, p->y_coord);
10753      ab_vs_cd (ab_vs_cd, arg1, y_orig, arg2, x_orig);
10754    } while (number_positive(ab_vs_cd));
10755    number_clone (mp->cur_x, p->x_coord);
10756    number_clone (mp->cur_y, p->y_coord);
10757    free_number (ab_vs_cd);
10758    free_number (arg1);
10759    free_number (arg2);
10760  }
10761}
10762
10763
10764@ @<Glob...@>=
10765mp_number cur_x;
10766mp_number cur_y;   /* all-purpose return value registers */
10767
10768@ @<Initialize table entries@>=
10769new_number (mp->cur_x);
10770new_number (mp->cur_y);
10771
10772@ @<Dealloc...@>=
10773free_number (mp->cur_x);
10774free_number (mp->cur_y);
10775
10776@ @<Find the offset for |(x,y)| on the elliptical pen~|h|@>=
10777if (number_zero(x_orig) && number_zero(y_orig)) {
10778  number_clone(mp->cur_x, h->x_coord);
10779  number_clone(mp->cur_y, h->y_coord);
10780} else {
10781  mp_number x, y, abs_x, abs_y;
10782  new_number(x);
10783  new_number(y);
10784  new_number(abs_x);
10785  new_number(abs_y);
10786  number_clone(x, x_orig);
10787  number_clone(y, y_orig);
10788  @<Find the non-constant part of the transformation for |h|@>;
10789  number_clone(abs_x, x);
10790  number_clone(abs_y, y);
10791  number_abs(abs_x);
10792  number_abs(abs_y);
10793  while (number_less(abs_x, fraction_half_t) && number_less(abs_y, fraction_half_t)) {
10794    number_double(x);
10795    number_double(y);
10796    number_clone(abs_x, x);
10797    number_clone(abs_y, y);
10798    number_abs(abs_x);
10799    number_abs(abs_y);
10800  }
10801  @<Make |(xx,yy)| the offset on the untransformed \&{pencircle} for the
10802    untransformed version of |(x,y)|@>;
10803  {
10804    mp_number r1, r2;
10805    new_fraction (r1);
10806    new_fraction (r2);
10807    take_fraction (r1, xx, wx);
10808    take_fraction (r2, yy, hx);
10809    number_add(r1, r2);
10810    set_number_from_addition(mp->cur_x, h->x_coord, r1);
10811    take_fraction (r1, xx, wy);
10812    take_fraction (r2, yy, hy);
10813    number_add(r1, r2);
10814    set_number_from_addition(mp->cur_y, h->y_coord, r1);
10815    free_number (r1);
10816    free_number (r2);
10817  }
10818  free_number(abs_x);
10819  free_number(abs_y);
10820  free_number(x);
10821  free_number(y);
10822}
10823
10824
10825@ @<Find the non-constant part of the transformation for |h|@>=
10826{
10827  set_number_from_substraction(wx, h->left_x,  h->x_coord);
10828  set_number_from_substraction(wy, h->left_y,  h->y_coord);
10829  set_number_from_substraction(hx, h->right_x, h->x_coord);
10830  set_number_from_substraction(hy, h->right_y, h->y_coord);
10831}
10832
10833
10834@ @<Make |(xx,yy)| the offset on the untransformed \&{pencircle} for the...@>=
10835{
10836  mp_number r1, r2, arg1;
10837  new_number (arg1);
10838  new_fraction (r1);
10839  new_fraction (r2);
10840  take_fraction (r1, x, hy);
10841  number_clone (arg1, hx);
10842  number_negate (arg1);
10843  take_fraction (r2, y, arg1);
10844  number_add (r1, r2);
10845  number_negate (r1);
10846  number_clone(yy, r1);
10847  number_clone (arg1, wy);
10848  number_negate (arg1);
10849  take_fraction (r1, x, arg1);
10850  take_fraction (r2, y, wx);
10851  number_add (r1, r2);
10852  number_clone(xx, r1);
10853  free_number (arg1);
10854  free_number (r1);
10855  free_number (r2);
10856}
10857pyth_add (d, xx, yy);
10858if (number_positive(d)) {
10859  mp_number ret;
10860  new_fraction (ret);
10861  make_fraction (ret, xx, d);
10862  number_half(ret);
10863  number_clone(xx, ret);
10864  make_fraction (ret, yy, d);
10865  number_half(ret);
10866  number_clone(yy, ret);
10867  free_number (ret);
10868}
10869
10870@ Finding the bounding box of a pen is easy except if the pen is elliptical.
10871But we can handle that case by just calling |find_offset| twice.  The answer
10872is stored in the global variables |minx|, |maxx|, |miny|, and |maxy|.
10873
10874@c
10875static void mp_pen_bbox (MP mp, mp_knot h) {
10876  mp_knot p;    /* for scanning the knot list */
10877  if (pen_is_elliptical (h)) {
10878    @<Find the bounding box of an elliptical pen@>;
10879  } else {
10880    number_clone (mp_minx, h->x_coord);
10881    number_clone (mp_maxx, mp_minx);
10882    number_clone (mp_miny, h->y_coord);
10883    number_clone (mp_maxy, mp_miny);
10884    p = mp_next_knot (h);
10885    while (p != h) {
10886      if (number_less (p->x_coord, mp_minx))
10887        number_clone (mp_minx, p->x_coord);
10888      if (number_less (p->y_coord, mp_miny))
10889        number_clone (mp_miny, p->y_coord);
10890      if (number_greater (p->x_coord, mp_maxx))
10891        number_clone (mp_maxx, p->x_coord);
10892      if (number_greater (p->y_coord, mp_maxy))
10893        number_clone (mp_maxy, p->y_coord);
10894      p = mp_next_knot (p);
10895    }
10896  }
10897}
10898
10899
10900@ @<Find the bounding box of an elliptical pen@>=
10901{
10902  mp_number arg1, arg2;
10903  new_number(arg1);
10904  new_fraction (arg2);
10905  number_clone(arg2, fraction_one_t);
10906  mp_find_offset (mp, arg1, arg2, h);
10907  number_clone (mp_maxx, mp->cur_x);
10908  number_clone (mp_minx, h->x_coord);
10909  number_double (mp_minx);
10910  number_substract (mp_minx, mp->cur_x);
10911  number_negate (arg2);
10912  mp_find_offset (mp, arg2, arg1, h);
10913  number_clone (mp_maxy, mp->cur_y);
10914  number_clone (mp_miny, h->y_coord);
10915  number_double (mp_miny);
10916  number_substract (mp_miny, mp->cur_y);
10917  free_number(arg1);
10918  free_number(arg2);
10919}
10920
10921
10922@* Numerical values.
10923
10924This first set goes into the header
10925
10926@<MPlib internal header stuff@>=
10927#define mp_fraction mp_number
10928#define mp_angle mp_number
10929#define new_number(A) (((math_data *)(mp->math))->allocate)(mp, &(A), mp_scaled_type)
10930#define new_fraction(A) (((math_data *)(mp->math))->allocate)(mp, &(A), mp_fraction_type)
10931#define new_angle(A) (((math_data *)(mp->math))->allocate)(mp, &(A), mp_angle_type)
10932#define free_number(A) (((math_data *)(mp->math))->free)(mp, &(A))
10933
10934@
10935@d set_precision()                     (((math_data *)(mp->math))->set_precision)(mp)
10936@d free_math()                         (((math_data *)(mp->math))->free_math)(mp)
10937@d scan_numeric_token(A)               (((math_data *)(mp->math))->scan_numeric)(mp, A)
10938@d scan_fractional_token(A)            (((math_data *)(mp->math))->scan_fractional)(mp, A)
10939@d set_number_from_of_the_way(A,t,B,C) (((math_data *)(mp->math))->from_oftheway)(mp, &(A),t,B,C)
10940@d set_number_from_int(A,B)	       (((math_data *)(mp->math))->from_int)(&(A),B)
10941@d set_number_from_scaled(A,B)	       (((math_data *)(mp->math))->from_scaled)(&(A),B)
10942@d set_number_from_boolean(A,B)	       (((math_data *)(mp->math))->from_boolean)(&(A),B)
10943@d set_number_from_double(A,B)	       (((math_data *)(mp->math))->from_double)(&(A),B)
10944@d set_number_from_addition(A,B,C)     (((math_data *)(mp->math))->from_addition)(&(A),B,C)
10945@d set_number_from_substraction(A,B,C) (((math_data *)(mp->math))->from_substraction)(&(A),B,C)
10946@d set_number_from_div(A,B,C)          (((math_data *)(mp->math))->from_div)(&(A),B,C)
10947@d set_number_from_mul(A,B,C)          (((math_data *)(mp->math))->from_mul)(&(A),B,C)
10948@d number_int_div(A,C)                 (((math_data *)(mp->math))->from_int_div)(&(A),A,C)
10949@d set_number_from_int_mul(A,B,C)      (((math_data *)(mp->math))->from_int_mul)(&(A),B,C)
10950@#
10951@d set_number_to_unity(A)	       (((math_data *)(mp->math))->clone)(&(A), unity_t)
10952@d set_number_to_zero(A)	       (((math_data *)(mp->math))->clone)(&(A), zero_t)
10953@d set_number_to_inf(A)		       (((math_data *)(mp->math))->clone)(&(A), inf_t)
10954@d set_number_to_neg_inf(A)	       do { set_number_to_inf(A); number_negate (A); } while (0)
10955@#
10956@d init_randoms(A)                     (((math_data *)(mp->math))->init_randoms)(mp,A)
10957@d print_number(A)                     (((math_data *)(mp->math))->print)(mp,A)
10958@d number_tostring(A)                  (((math_data *)(mp->math))->tostring)(mp,A)
10959@d make_scaled(R,A,B)                  (((math_data *)(mp->math))->make_scaled)(mp,&(R),A,B)
10960@d take_scaled(R,A,B)                  (((math_data *)(mp->math))->take_scaled)(mp,&(R),A,B)
10961@d make_fraction(R,A,B)                (((math_data *)(mp->math))->make_fraction)(mp,&(R),A,B)
10962@d take_fraction(R,A,B)                (((math_data *)(mp->math))->take_fraction)(mp,&(R),A,B)
10963@d pyth_add(R,A,B)                     (((math_data *)(mp->math))->pyth_add)(mp,&(R),A,B)
10964@d pyth_sub(R,A,B)                     (((math_data *)(mp->math))->pyth_sub)(mp,&(R),A,B)
10965@d n_arg(R,A,B)                        (((math_data *)(mp->math))->n_arg)(mp,&(R),A,B)
10966@d m_log(R,A)                          (((math_data *)(mp->math))->m_log)(mp,&(R),A)
10967@d m_exp(R,A)                          (((math_data *)(mp->math))->m_exp)(mp,&(R),A)
10968@d m_norm_rand(R)                      (((math_data *)(mp->math))->m_norm_rand)(mp,&(R))
10969@d velocity(R,A,B,C,D,E)               (((math_data *)(mp->math))->velocity)(mp,&(R),A,B,C,D,E)
10970@d ab_vs_cd(R,A,B,C,D)                 (((math_data *)(mp->math))->ab_vs_cd)(mp,&(R),A,B,C,D)
10971@d crossing_point(R,A,B,C)             (((math_data *)(mp->math))->crossing_point)(mp,&(R),A,B,C)
10972@d n_sin_cos(A,S,C)                    (((math_data *)(mp->math))->sin_cos)(mp,A,&(S),&(C))
10973@d square_rt(A,S)                      (((math_data *)(mp->math))->sqrt)(mp,&(A),S)
10974@d slow_add(R,A,B)                     (((math_data *)(mp->math))->slow_add)(mp,&(R),A,B)
10975@d round_unscaled(A)		       (((math_data *)(mp->math))->round_unscaled)(A)
10976@d floor_scaled(A)		       (((math_data *)(mp->math))->floor_scaled)(&(A))
10977@d fraction_to_round_scaled(A)         (((math_data *)(mp->math))->fraction_to_round_scaled)(&(A))
10978@d number_to_int(A)		       (((math_data *)(mp->math))->to_int)(A)
10979@d number_to_boolean(A)		       (((math_data *)(mp->math))->to_boolean)(A)
10980@d number_to_scaled(A)		       (((math_data *)(mp->math))->to_scaled)(A)
10981@d number_to_double(A)		       (((math_data *)(mp->math))->to_double)(A)
10982@d number_negate(A)		       (((math_data *)(mp->math))->negate)(&(A))
10983@d number_add(A,B)		       (((math_data *)(mp->math))->add)(&(A),B)
10984@d number_substract(A,B)	       (((math_data *)(mp->math))->substract)(&(A),B)
10985@d number_half(A)		       (((math_data *)(mp->math))->half)(&(A))
10986@d number_halfp(A)		       (((math_data *)(mp->math))->halfp)(&(A))
10987@d number_double(A)		       (((math_data *)(mp->math))->do_double)(&(A))
10988@d number_add_scaled(A,B)	       (((math_data *)(mp->math))->add_scaled)(&(A),B)
10989@d number_multiply_int(A,B)	       (((math_data *)(mp->math))->multiply_int)(&(A),B)
10990@d number_divide_int(A,B)	       (((math_data *)(mp->math))->divide_int)(&(A),B)
10991@d number_abs(A)		       (((math_data *)(mp->math))->abs)(&(A))
10992@d number_modulo(A,B)		       (((math_data *)(mp->math))->modulo)(&(A), B)
10993@d number_nonequalabs(A,B)	       (((math_data *)(mp->math))->nonequalabs)(A,B)
10994@d number_odd(A)		       (((math_data *)(mp->math))->odd)(A)
10995@d number_equal(A,B)		       (((math_data *)(mp->math))->equal)(A,B)
10996@d number_greater(A,B)		       (((math_data *)(mp->math))->greater)(A,B)
10997@d number_less(A,B)		       (((math_data *)(mp->math))->less)(A,B)
10998@d number_clone(A,B)		       (((math_data *)(mp->math))->clone)(&(A),B)
10999@d number_swap(A,B)		       (((math_data *)(mp->math))->swap)(&(A),&(B));
11000@d convert_scaled_to_angle(A)          (((math_data *)(mp->math))->scaled_to_angle)(&(A));
11001@d convert_angle_to_scaled(A)          (((math_data *)(mp->math))->angle_to_scaled)(&(A));
11002@d convert_fraction_to_scaled(A)       (((math_data *)(mp->math))->fraction_to_scaled)(&(A));
11003@d convert_scaled_to_fraction(A)       (((math_data *)(mp->math))->scaled_to_fraction)(&(A));
11004@#
11005@d number_zero(A)		       number_equal(A, zero_t)
11006@d number_infinite(A)		       number_equal(A, inf_t)
11007@d number_unity(A)		       number_equal(A, unity_t)
11008@d number_negative(A)		       number_less(A, zero_t)
11009@d number_nonnegative(A)	       (!number_negative(A))
11010@d number_positive(A)		       number_greater(A, zero_t)
11011@d number_nonpositive(A)	       (!number_positive(A))
11012@d number_nonzero(A)		       (!number_zero(A))
11013@d number_greaterequal(A,B)	       (!number_less(A,B))
11014@d number_lessequal(A,B)	       (!number_greater(A,B))
11015
11016@* Edge structures.
11017Now we come to \MP's internal scheme for representing pictures.
11018The representation is very different from \MF's edge structures
11019because \MP\ pictures contain \ps\ graphics objects instead of pixel
11020images.  However, the basic idea is somewhat similar in that shapes
11021are represented via their boundaries.
11022
11023The main purpose of edge structures is to keep track of graphical objects
11024until it is time to translate them into \ps.  Since \MP\ does not need to
11025know anything about an edge structure other than how to translate it into
11026\ps\ and how to find its bounding box, edge structures can be just linked
11027lists of graphical objects.  \MP\ has no easy way to determine whether
11028two such objects overlap, but it suffices to draw the first one first and
11029let the second one overwrite it if necessary.
11030
11031@<MPlib header stuff@>=
11032enum mp_graphical_object_code {
11033  @<Graphical object codes@>
11034  mp_final_graphic
11035};
11036
11037@ Let's consider the types of graphical objects one at a time.
11038First of all, a filled contour is represented by a eight-word node.  The first
11039word contains |type| and |link| fields, and the next six words contain a
11040pointer to a cyclic path and the value to use for \ps' \&{currentrgbcolor}
11041parameter.  If a pen is used for filling |pen_p|, |ljoin| and |miterlim|
11042give the relevant information.
11043
11044@d mp_path_p(A) (A)->path_p_  /* a pointer to the path that needs filling */
11045@d mp_pen_p(A) (A)->pen_p_  /* a pointer to the pen to fill or stroke with */
11046@d mp_color_model(A) ((mp_fill_node)(A))->color_model_ /*  the color model  */
11047@d cyan red
11048@d grey red
11049@d magenta green
11050@d yellow blue
11051@d mp_pre_script(A) ((mp_fill_node)(A))->pre_script_
11052@d mp_post_script(A) ((mp_fill_node)(A))->post_script_
11053
11054@<MPlib internal header stuff@>=
11055typedef struct mp_fill_node_data {
11056  NODE_BODY;
11057  halfword color_model_;
11058  mp_number red;
11059  mp_number green;
11060  mp_number blue;
11061  mp_number black;
11062  mp_string pre_script_;
11063  mp_string post_script_;
11064  mp_knot path_p_;
11065  mp_knot pen_p_;
11066  unsigned char ljoin;
11067  mp_number miterlim;
11068} mp_fill_node_data;
11069typedef struct mp_fill_node_data *mp_fill_node;
11070
11071@ @<Graphical object codes@>=
11072mp_fill_code = 1,
11073
11074@ Make a fill node for cyclic path |p| and color black.
11075
11076@d fill_node_size sizeof(struct mp_fill_node_data)
11077
11078@c
11079static mp_node mp_new_fill_node (MP mp, mp_knot p) {
11080  mp_fill_node t = malloc_node (fill_node_size);
11081  mp_type (t) = mp_fill_node_type;
11082  mp_path_p (t) = p;
11083  mp_pen_p (t) = NULL;          /* |NULL| means don't use a pen */
11084  new_number(t->red);
11085  new_number(t->green);
11086  new_number(t->blue);
11087  new_number(t->black);
11088  new_number(t->miterlim);
11089  clear_color (t);
11090  mp_color_model (t) = mp_uninitialized_model;
11091  mp_pre_script (t) = NULL;
11092  mp_post_script (t) = NULL;
11093  /* Set the |ljoin| and |miterlim| fields in object |t| */
11094  if (number_greater(internal_value (mp_linejoin), unity_t))
11095    t->ljoin = 2;
11096  else if (number_positive(internal_value (mp_linejoin)))
11097    t->ljoin = 1;
11098  else
11099    t->ljoin = 0;
11100  if (number_less(internal_value (mp_miterlimit), unity_t)) {
11101    set_number_to_unity(t->miterlim);
11102  } else {
11103    number_clone(t->miterlim,internal_value (mp_miterlimit));
11104  }
11105  return (mp_node) t;
11106}
11107
11108@ @c
11109static void mp_free_fill_node (MP mp, mp_fill_node p) {
11110  mp_toss_knot_list (mp, mp_path_p (p));
11111  if (mp_pen_p (p) != NULL)
11112    mp_toss_knot_list (mp, mp_pen_p (p));
11113  if (mp_pre_script (p) != NULL)
11114    delete_str_ref (mp_pre_script (p));
11115  if (mp_post_script (p) != NULL)
11116    delete_str_ref (mp_post_script (p));
11117  free_number(p->red);
11118  free_number(p->green);
11119  free_number(p->blue);
11120  free_number(p->black);
11121  free_number(p->miterlim);
11122  mp_free_node (mp, (mp_node)p, fill_node_size);
11123}
11124
11125
11126
11127@ A stroked path is represented by an eight-word node that is like a filled
11128contour node except that it contains the current \&{linecap} value, a scale
11129factor for the dash pattern, and a pointer that is non-NULL if the stroke
11130is to be dashed.  The purpose of the scale factor is to allow a picture to
11131be transformed without touching the picture that |dash_p| points to.
11132
11133@d mp_dash_p(A) ((mp_stroked_node)(A))->dash_p_  /* a pointer to the edge structure that gives the dash pattern */
11134
11135@<MPlib internal header stuff@>=
11136typedef struct mp_stroked_node_data {
11137  NODE_BODY;
11138  halfword color_model_;
11139  mp_number red;
11140  mp_number green;
11141  mp_number blue;
11142  mp_number black;
11143  mp_string pre_script_;
11144  mp_string post_script_;
11145  mp_knot path_p_;
11146  mp_knot pen_p_;
11147  unsigned char ljoin;
11148  mp_number miterlim;
11149  unsigned char lcap;
11150  mp_node dash_p_;
11151  mp_number dash_scale;
11152} mp_stroked_node_data;
11153typedef struct mp_stroked_node_data *mp_stroked_node;
11154
11155
11156@ @<Graphical object codes@>=
11157mp_stroked_code = 2,
11158
11159@  Make a stroked node for path |p| with |mp_pen_p(p)| temporarily |NULL|.
11160
11161@d stroked_node_size sizeof(struct mp_stroked_node_data)
11162
11163@c
11164static mp_node mp_new_stroked_node (MP mp, mp_knot p) {
11165  mp_stroked_node t = malloc_node (stroked_node_size);
11166  mp_type (t) = mp_stroked_node_type;
11167  mp_path_p (t) = p;
11168  mp_pen_p (t) = NULL;
11169  mp_dash_p (t) = NULL;
11170  new_number(t->dash_scale);
11171  set_number_to_unity(t->dash_scale);
11172  new_number(t->red);
11173  new_number(t->green);
11174  new_number(t->blue);
11175  new_number(t->black);
11176  new_number(t->miterlim);
11177  clear_color(t);
11178  mp_pre_script (t) = NULL;
11179  mp_post_script (t) = NULL;
11180  /* Set the |ljoin| and |miterlim| fields in object |t| */
11181  if (number_greater(internal_value (mp_linejoin), unity_t))
11182    t->ljoin = 2;
11183  else if (number_positive(internal_value (mp_linejoin)))
11184    t->ljoin = 1;
11185  else
11186    t->ljoin = 0;
11187  if (number_less(internal_value (mp_miterlimit), unity_t)) {
11188    set_number_to_unity(t->miterlim);
11189  } else {
11190    number_clone(t->miterlim,internal_value (mp_miterlimit));
11191  }
11192  if (number_greater(internal_value (mp_linecap), unity_t))
11193    t->lcap = 2;
11194  else if (number_positive(internal_value (mp_linecap)))
11195    t->lcap = 1;
11196  else
11197    t->lcap = 0;
11198  return (mp_node) t;
11199}
11200
11201@ @c
11202static mp_edge_header_node mp_free_stroked_node (MP mp, mp_stroked_node p) {
11203  mp_edge_header_node e = NULL;
11204  mp_toss_knot_list (mp, mp_path_p (p));
11205  if (mp_pen_p (p) != NULL)
11206    mp_toss_knot_list (mp, mp_pen_p (p));
11207  if (mp_pre_script (p) != NULL)
11208    delete_str_ref (mp_pre_script (p));
11209  if (mp_post_script (p) != NULL)
11210    delete_str_ref (mp_post_script (p));
11211  e = (mp_edge_header_node)mp_dash_p (p);
11212  free_number(p->dash_scale);
11213  free_number(p->red);
11214  free_number(p->green);
11215  free_number(p->blue);
11216  free_number(p->black);
11217  free_number(p->miterlim);
11218  mp_free_node (mp, (mp_node)p, stroked_node_size);
11219  return e;
11220}
11221
11222@ When a dashed line is computed in a transformed coordinate system, the dash
11223lengths get scaled like the pen shape and we need to compensate for this.  Since
11224there is no unique scale factor for an arbitrary transformation, we use the
11225the square root of the determinant.  The properties of the determinant make it
11226easier to maintain the |dash_scale|.  The computation is fairly straight-forward
11227except for the initialization of the scale factor |s|.  The factor of 64 is
11228needed because |square_rt| scales its result by $2^8$ while we need $2^{14}$
11229to counteract the effect of |take_fraction|.
11230
11231@ @c
11232void mp_sqrt_det (MP mp, mp_number *ret, mp_number a_orig, mp_number b_orig, mp_number c_orig, mp_number d_orig) {
11233  mp_number a,b,c,d;
11234  mp_number maxabs;        /* $max(|a|,|b|,|c|,|d|)$ */
11235  unsigned s;   /* amount by which the result of |square_rt| needs to be scaled */
11236  new_number(a);
11237  new_number(b);
11238  new_number(c);
11239  new_number(d);
11240  new_number(maxabs);
11241  number_clone(a, a_orig);
11242  number_clone(b, b_orig);
11243  number_clone(c, c_orig);
11244  number_clone(d, d_orig);
11245  /* Initialize |maxabs| */
11246  {
11247    mp_number tmp;
11248    new_number (tmp);
11249    number_clone(maxabs, a);
11250    number_abs(maxabs);
11251    number_clone(tmp, b);
11252    number_abs(tmp);
11253    if (number_greater(tmp, maxabs))
11254      number_clone(maxabs, tmp);
11255    number_clone(tmp, c);
11256    number_abs(tmp);
11257    if (number_greater(tmp, maxabs))
11258      number_clone(maxabs, tmp);
11259    number_clone(tmp, d);
11260    number_abs(tmp);
11261    if (number_greater(tmp, maxabs))
11262      number_clone(maxabs, tmp);
11263    free_number(tmp);
11264  }
11265
11266
11267  s = 64;
11268  while ((number_less(maxabs, fraction_one_t)) && (s > 1)) {
11269    number_double(a);
11270    number_double(b);
11271    number_double(c);
11272    number_double(d);
11273    number_double(maxabs);
11274    s = s/2;
11275  }
11276  {
11277    mp_number r1, r2;
11278    new_fraction (r1);
11279    new_fraction (r2);
11280    take_fraction (r1, a, d);
11281    take_fraction (r2, b, c);
11282    number_substract (r1, r2);
11283    number_abs (r1);
11284    square_rt(*ret, r1);
11285    number_multiply_int(*ret, s);
11286    free_number (r1);
11287    free_number (r2);
11288  }
11289  free_number(a);
11290  free_number(b);
11291  free_number(c);
11292  free_number(d);
11293  free_number(maxabs);
11294}
11295@#
11296static void mp_get_pen_scale (MP mp, mp_number *ret, mp_knot p) {
11297  if (p == NULL) {
11298    set_number_to_zero(*ret);
11299  } else {
11300    mp_number a,b,c,d;
11301    new_number(a);
11302    new_number(b);
11303    new_number(c);
11304    new_number(d);
11305    set_number_from_substraction(a, p->left_x, p->x_coord);
11306    set_number_from_substraction(b, p->right_x, p->x_coord);
11307    set_number_from_substraction(c, p->left_y,  p->y_coord);
11308    set_number_from_substraction(d, p->right_y, p->y_coord);
11309    mp_sqrt_det (mp, ret, a, b, c, d);
11310    free_number(a);
11311    free_number(b);
11312    free_number(c);
11313    free_number(d);
11314  }
11315}
11316
11317
11318@ @<Declarations@>=
11319static void mp_sqrt_det (MP mp, mp_number *ret, mp_number a, mp_number b, mp_number c, mp_number d);
11320
11321@ When a picture contains text, this is represented by a fourteen-word node
11322where the color information and |type| and |link| fields are augmented by
11323additional fields that describe the text and  how it is transformed.
11324The |path_p| and |mp_pen_p| pointers are replaced by a number that identifies
11325the font and a string number that gives the text to be displayed.
11326The |width|, |height|, and |depth| fields
11327give the dimensions of the text at its design size, and the remaining six
11328words give a transformation to be applied to the text.  The |new_text_node|
11329function initializes everything to default values so that the text comes out
11330black with its reference point at the origin.
11331
11332@d mp_text_p(A) ((mp_text_node)(A))->text_p_  /* a string pointer for the text to display */
11333@d mp_font_n(A) ((mp_text_node)(A))->font_n_ /* the font number */
11334
11335@<MPlib internal header stuff@>=
11336typedef struct mp_text_node_data {
11337  NODE_BODY;
11338  halfword color_model_;
11339  mp_number red;
11340  mp_number green;
11341  mp_number blue;
11342  mp_number black;
11343  mp_string pre_script_;
11344  mp_string post_script_;
11345  mp_string text_p_;
11346  halfword font_n_;
11347  mp_number width;
11348  mp_number height;
11349  mp_number depth;
11350  mp_number tx;
11351  mp_number ty;
11352  mp_number txx;
11353  mp_number txy;
11354  mp_number tyx;
11355  mp_number tyy;
11356} mp_text_node_data;
11357typedef struct mp_text_node_data *mp_text_node;
11358
11359@ @<Graphical object codes@>=
11360mp_text_code = 3,
11361
11362@  Make a text node for font |f| and text string |s|.
11363
11364@d text_node_size sizeof(struct mp_text_node_data)
11365
11366@c
11367static mp_node mp_new_text_node (MP mp, char *f, mp_string s) {
11368  mp_text_node t = malloc_node (text_node_size);
11369  mp_type (t) = mp_text_node_type;
11370  mp_text_p (t) = s;
11371  add_str_ref(s);
11372  mp_font_n (t) = (halfword) mp_find_font (mp, f);      /* this identifies the font */
11373  new_number(t->red);
11374  new_number(t->green);
11375  new_number(t->blue);
11376  new_number(t->black);
11377  new_number(t->width);
11378  new_number(t->height);
11379  new_number(t->depth);
11380  clear_color (t);
11381  mp_pre_script (t) = NULL;
11382  mp_post_script (t) = NULL;
11383  new_number(t->tx);
11384  new_number(t->ty);
11385  new_number(t->txx);
11386  new_number(t->txy);
11387  new_number(t->tyx);
11388  new_number(t->tyy);
11389  /* |tx_val (t) = 0; ty_val (t) = 0;| */
11390  /* |txy_val (t) = 0; tyx_val (t) = 0;| */
11391  set_number_to_unity(t->txx);
11392  set_number_to_unity(t->tyy);
11393  mp_set_text_box (mp, t);    /* this finds the bounding box */
11394  return (mp_node) t;
11395}
11396
11397@ @c
11398static void mp_free_text_node (MP mp, mp_text_node p) {
11399  /* |delete_str_ref (mp_text_p (p));| */ /* gives errors */
11400  if (mp_pre_script (p) != NULL)
11401    delete_str_ref (mp_pre_script (p));
11402  if (mp_post_script (p) != NULL)
11403    delete_str_ref (mp_post_script (p));
11404  free_number(p->red);
11405  free_number(p->green);
11406  free_number(p->blue);
11407  free_number(p->black);
11408  free_number(p->width);
11409  free_number(p->height);
11410  free_number(p->depth);
11411  free_number(p->tx);
11412  free_number(p->ty);
11413  free_number(p->txx);
11414  free_number(p->txy);
11415  free_number(p->tyx);
11416  free_number(p->tyy);
11417  mp_free_node (mp, (mp_node)p, text_node_size);
11418}
11419
11420@ The last two types of graphical objects that can occur in an edge structure
11421are clipping paths and \&{setbounds} paths.  These are slightly more difficult
11422@:set_bounds_}{\&{setbounds} primitive@>
11423to implement because we must keep track of exactly what is being clipped or
11424bounded when pictures get merged together.  For this reason, each clipping or
11425\&{setbounds} operation is represented by a pair of nodes:  first comes a
11426node whose |path_p| gives the relevant path, then there is the list
11427of objects to clip or bound followed by a closing node.
11428
11429@d has_color(A) (mp_type((A))<mp_start_clip_node_type)
11430  /* does a graphical object have color fields? */
11431@d has_pen(A) (mp_type((A))<mp_text_node_type)
11432  /* does a graphical object have a |mp_pen_p| field? */
11433@d is_start_or_stop(A) (mp_type((A))>=mp_start_clip_node_type)
11434@d is_stop(A) (mp_type((A))>=mp_stop_clip_node_type)
11435
11436@<MPlib internal header stuff@>=
11437typedef struct mp_start_clip_node_data {
11438  NODE_BODY;
11439  mp_knot path_p_;
11440} mp_start_clip_node_data;
11441typedef struct mp_start_clip_node_data *mp_start_clip_node;
11442typedef struct mp_start_bounds_node_data {
11443  NODE_BODY;
11444  mp_knot path_p_;
11445} mp_start_bounds_node_data;
11446typedef struct mp_start_bounds_node_data *mp_start_bounds_node;
11447typedef struct mp_stop_clip_node_data {
11448  NODE_BODY;
11449} mp_stop_clip_node_data;
11450typedef struct mp_stop_clip_node_data *mp_stop_clip_node;
11451typedef struct mp_stop_bounds_node_data {
11452  NODE_BODY;
11453} mp_stop_bounds_node_data;
11454typedef struct mp_stop_bounds_node_data *mp_stop_bounds_node;
11455
11456
11457@ @<Graphical object codes@>=
11458mp_start_clip_code = 4,         /* |type| of a node that starts clipping */
11459  mp_start_bounds_code = 5,     /* |type| of a node that gives a \&{setbounds} path */
11460  mp_stop_clip_code = 6,        /* |type| of a node that stops clipping */
11461  mp_stop_bounds_code = 7,      /* |type| of a node that stops \&{setbounds} */
11462
11463
11464@
11465
11466@d start_clip_size sizeof(struct mp_start_clip_node_data)
11467@d stop_clip_size sizeof(struct mp_stop_clip_node_data)
11468@d start_bounds_size sizeof(struct mp_start_bounds_node_data)
11469@d stop_bounds_size sizeof(struct mp_stop_bounds_node_data)
11470
11471@c
11472static mp_node mp_new_bounds_node (MP mp, mp_knot p, quarterword c) {
11473  /* make a node of type |c| where |p| is the clipping or \&{setbounds} path */
11474  if (c == mp_start_clip_node_type) {
11475    mp_start_clip_node t;       /* the new node */
11476    t = (mp_start_clip_node) malloc_node (start_clip_size);
11477    t->path_p_ = p;
11478    mp_type (t) = c;
11479    t->link = NULL;
11480    return (mp_node) t;
11481  } else if (c == mp_start_bounds_node_type) {
11482    mp_start_bounds_node t;     /* the new node */
11483    t = (mp_start_bounds_node) malloc_node (start_bounds_size);
11484    t->path_p_ = p;
11485    mp_type (t) = c;
11486    t->link = NULL;
11487    return (mp_node) t;
11488  } else if (c == mp_stop_clip_node_type) {
11489    mp_stop_clip_node t;        /* the new node */
11490    t = (mp_stop_clip_node) malloc_node (stop_clip_size);
11491    mp_type (t) = c;
11492    t->link = NULL;
11493    return (mp_node) t;
11494  } else if (c == mp_stop_bounds_node_type) {
11495    mp_stop_bounds_node t;      /* the new node */
11496    t = (mp_stop_bounds_node) malloc_node (stop_bounds_size);
11497    mp_type (t) = c;
11498    t->link = NULL;
11499    return (mp_node) t;
11500  } else {
11501    assert (0);
11502  }
11503  return NULL;
11504}
11505
11506
11507@ @c
11508static void mp_free_start_clip_node (MP mp, mp_start_clip_node p) {
11509  mp_toss_knot_list (mp, mp_path_p (p));
11510  mp_free_node (mp, (mp_node)p, start_clip_size);
11511}
11512static void mp_free_start_bounds_node (MP mp, mp_start_bounds_node p) {
11513  mp_toss_knot_list (mp, mp_path_p (p));
11514  mp_free_node (mp, (mp_node)p, start_bounds_size);
11515}
11516static void mp_free_stop_clip_node (MP mp, mp_stop_clip_node p) {
11517  mp_free_node (mp, (mp_node)p, stop_clip_size);
11518}
11519static void mp_free_stop_bounds_node (MP mp, mp_stop_bounds_node p) {
11520  mp_free_node (mp, (mp_node)p, stop_bounds_size);
11521}
11522
11523
11524@ All the essential information in an edge structure is encoded as a linked list
11525of graphical objects as we have just seen, but it is helpful to add some
11526redundant information.  A single edge structure might be used as a dash pattern
11527many times, and it would be nice to avoid scanning the same structure
11528repeatedly.  Thus, an edge structure known to be a suitable dash pattern
11529has a header that gives a list of dashes in a sorted order designed for rapid
11530translation into \ps.
11531
11532Each dash is represented by a three-word node containing the initial and final
11533$x$~coordinates as well as the usual |link| field.  The |link| fields points to
11534the dash node with the next higher $x$-coordinates and the final link points
11535to a special location called |null_dash|.  (There should be no overlap between
11536dashes).  Since the $y$~coordinate of the dash pattern is needed to determine
11537the period of repetition, this needs to be stored in the edge header along
11538with a pointer to the list of dash nodes.
11539
11540The |dash_info| is explained below.
11541
11542@d dash_list(A) (mp_dash_node)(((mp_dash_node)(A))->link)  /* in an edge header this points to the first dash node */
11543@d set_dash_list(A,B) ((mp_dash_node)(A))->link=(mp_node)((B))  /* in an edge header this points to the first dash node */
11544
11545@<MPlib internal header stuff@>=
11546typedef struct mp_dash_node_data {
11547  NODE_BODY;
11548  mp_number start_x; /* the starting $x$~coordinate in a dash node */
11549  mp_number stop_x; /* the ending $x$~coordinate in a dash node */
11550  mp_number dash_y; /* $y$ value for the dash list in an edge header */
11551  mp_node dash_info_;
11552} mp_dash_node_data;
11553
11554@ @<Types...@>=
11555typedef struct mp_dash_node_data *mp_dash_node;
11556
11557@ @<Initialize table entries@>=
11558mp->null_dash = mp_get_dash_node (mp);
11559
11560@ @<Free table entries@>=
11561mp_free_node (mp, (mp_node)mp->null_dash, dash_node_size);
11562
11563@
11564@d dash_node_size sizeof(struct mp_dash_node_data)
11565
11566@c
11567static mp_dash_node mp_get_dash_node (MP mp) {
11568  mp_dash_node p = (mp_dash_node) malloc_node (dash_node_size);
11569  p->has_number = 0;
11570  new_number(p->start_x);
11571  new_number(p->stop_x);
11572  new_number(p->dash_y);
11573  mp_type (p) = mp_dash_node_type;
11574  return p;
11575}
11576
11577
11578@ It is also convenient for an edge header to contain the bounding
11579box information needed by the \&{llcorner} and \&{urcorner} operators
11580so that this does not have to be recomputed unnecessarily.  This is done by
11581adding fields for the $x$~and $y$ extremes as well as a pointer that indicates
11582how far the bounding box computation has gotten.  Thus if the user asks for
11583the bounding box and then adds some more text to the picture before asking
11584for more bounding box information, the second computation need only look at
11585the additional text.
11586
11587When the bounding box has not been computed, the |bblast| pointer points
11588to a dummy link at the head of the graphical object list while the |minx_val|
11589and |miny_val| fields contain |EL_GORDO| and the |maxx_val| and |maxy_val|
11590fields contain |-EL_GORDO|.
11591
11592Since the bounding box of pictures containing objects of type
11593|mp_start_bounds_node| depends on the value of \&{truecorners}, the bounding box
11594@:mp_true_corners_}{\&{truecorners} primitive@>
11595data might not be valid for all values of this parameter.  Hence, the |bbtype|
11596field is needed to keep track of this.
11597
11598@d bblast(A) ((mp_edge_header_node)(A))->bblast_  /* last item considered in bounding box computation */
11599@d edge_list(A)  ((mp_edge_header_node)(A))->list_ /* where the object list begins in an edge header */
11600
11601@<MPlib internal header stuff@>=
11602typedef struct mp_edge_header_node_data {
11603  NODE_BODY;
11604  mp_number start_x;
11605  mp_number stop_x;
11606  mp_number dash_y;
11607  mp_node dash_info_;
11608  mp_number minx;
11609  mp_number miny;
11610  mp_number maxx;
11611  mp_number maxy;
11612  mp_node bblast_;
11613  int bbtype; /* tells how bounding box data depends on \&{truecorners} */
11614  mp_node list_;
11615  mp_node obj_tail_;    /* explained below */
11616  halfword ref_count_;  /* explained below */
11617} mp_edge_header_node_data;
11618typedef struct mp_edge_header_node_data *mp_edge_header_node;
11619
11620@
11621@d no_bounds 0  /* |bbtype| value when bounding box data is valid for all \&{truecorners} values */
11622@d bounds_set 1  /* |bbtype| value when bounding box data is for \&{truecorners}${}\le 0$ */
11623@d bounds_unset 2  /* |bbtype| value when bounding box data is for \&{truecorners}${}>0$ */
11624@c
11625static void mp_init_bbox (MP mp, mp_edge_header_node h) {
11626  /* Initialize the bounding box information in edge structure |h| */
11627  (void) mp;
11628  bblast (h) = edge_list (h);
11629  h->bbtype = no_bounds;
11630  set_number_to_inf(h->minx);
11631  set_number_to_inf(h->miny);
11632  set_number_to_neg_inf(h->maxx);
11633  set_number_to_neg_inf(h->maxy);
11634}
11635
11636
11637@ The only other entries in an edge header are a reference count in the first
11638word and a pointer to the tail of the object list in the last word.
11639
11640@d obj_tail(A) ((mp_edge_header_node)(A))->obj_tail_  /* points to the last entry in the object list */
11641@d edge_ref_count(A) ((mp_edge_header_node)(A))->ref_count_
11642
11643@d edge_header_size sizeof(struct mp_edge_header_node_data)
11644
11645@c
11646static mp_edge_header_node mp_get_edge_header_node (MP mp) {
11647  mp_edge_header_node p = (mp_edge_header_node) malloc_node (edge_header_size);
11648  mp_type (p) = mp_edge_header_node_type;
11649  new_number(p->start_x);
11650  new_number(p->stop_x);
11651  new_number(p->dash_y);
11652  new_number(p->minx);
11653  new_number(p->miny);
11654  new_number(p->maxx);
11655  new_number(p->maxy);
11656  p->list_ = mp_get_token_node (mp);   /* or whatever, just a need a link handle */
11657  return p;
11658}
11659static void mp_init_edges (MP mp, mp_edge_header_node h) {
11660  /* initialize an edge header to NULL values */
11661  set_dash_list (h, mp->null_dash);
11662  obj_tail (h) = edge_list (h);
11663  mp_link (edge_list (h)) = NULL;
11664  edge_ref_count (h) = 0;
11665  mp_init_bbox (mp, h);
11666}
11667
11668
11669@ Here is how edge structures are deleted.  The process can be recursive because
11670of the need to dereference edge structures that are used as dash patterns.
11671@^recursion@>
11672
11673@d add_edge_ref(A) incr(edge_ref_count((A)))
11674@d delete_edge_ref(A) {
11675   if ( edge_ref_count((A))==0 )
11676     mp_toss_edges(mp, (mp_edge_header_node)(A));
11677   else
11678     decr(edge_ref_count((A)));
11679   }
11680
11681@<Declarations@>=
11682static void mp_flush_dash_list (MP mp, mp_edge_header_node h);
11683static mp_edge_header_node mp_toss_gr_object (MP mp, mp_node p);
11684static void mp_toss_edges (MP mp, mp_edge_header_node h);
11685
11686@ @c
11687void mp_toss_edges (MP mp, mp_edge_header_node h) {
11688  mp_node p, q; /* pointers that scan the list being recycled */
11689  mp_edge_header_node r;    /* an edge structure that object |p| refers to */
11690  mp_flush_dash_list (mp, h);
11691  q = mp_link (edge_list (h));
11692  while ((q != NULL)) {
11693    p = q;
11694    q = mp_link (q);
11695    r = mp_toss_gr_object (mp, p);
11696    if (r != NULL)
11697      delete_edge_ref (r);
11698  }
11699  free_number(h->start_x);
11700  free_number(h->stop_x);
11701  free_number(h->dash_y);
11702  free_number(h->minx);
11703  free_number(h->miny);
11704  free_number(h->maxx);
11705  free_number(h->maxy);
11706  mp_free_token_node (mp, h->list_);
11707  mp_free_node (mp, (mp_node)h, edge_header_size);
11708}
11709void mp_flush_dash_list (MP mp, mp_edge_header_node h) {
11710  mp_dash_node p, q; /* pointers that scan the list being recycled */
11711  q = dash_list (h);
11712  while (q != mp->null_dash) { /* todo: NULL check should not be needed */
11713    p = q;
11714    q = (mp_dash_node)mp_link (q);
11715    mp_free_node (mp, (mp_node)p, dash_node_size);
11716  }
11717  set_dash_list (h,mp->null_dash);
11718}
11719mp_edge_header_node mp_toss_gr_object (MP mp, mp_node p) {
11720  /* returns an edge structure that needs to be dereferenced */
11721  mp_edge_header_node e = NULL;     /* the edge structure to return */
11722  switch (mp_type (p)) {
11723  case mp_fill_node_type:
11724    mp_free_fill_node (mp, (mp_fill_node)p);
11725    break;
11726  case mp_stroked_node_type:
11727    e = mp_free_stroked_node (mp, (mp_stroked_node)p);
11728    break;
11729  case mp_text_node_type:
11730    mp_free_text_node(mp, (mp_text_node)p);
11731    break;
11732  case mp_start_clip_node_type:
11733    mp_free_start_clip_node(mp, (mp_start_clip_node)p);
11734    break;
11735  case mp_start_bounds_node_type:
11736    mp_free_start_bounds_node(mp, (mp_start_bounds_node)p);
11737    break;
11738  case mp_stop_clip_node_type:
11739    mp_free_stop_clip_node(mp, (mp_stop_clip_node)p);
11740    break;
11741  case mp_stop_bounds_node_type:
11742    mp_free_stop_bounds_node(mp, (mp_stop_bounds_node)p);
11743    break;
11744  default:  /* there are no other valid cases, but please the compiler */
11745    break;
11746  }
11747  return e;
11748}
11749
11750
11751@ If we use |add_edge_ref| to ``copy'' edge structures, the real copying needs
11752to be done before making a significant change to an edge structure.  Much of
11753the work is done in a separate routine |copy_objects| that copies a list of
11754graphical objects into a new edge header.
11755
11756@c
11757static mp_edge_header_node mp_private_edges (MP mp, mp_edge_header_node h) {
11758  /* make a private copy of the edge structure headed by |h| */
11759  mp_edge_header_node hh;   /* the edge header for the new copy */
11760  mp_dash_node p, pp;        /* pointers for copying the dash list */
11761  assert (mp_type (h) == mp_edge_header_node_type);
11762  if (edge_ref_count (h) == 0) {
11763    return h;
11764  } else {
11765    decr (edge_ref_count (h));
11766    hh = (mp_edge_header_node)mp_copy_objects (mp, mp_link (edge_list (h)), NULL);
11767    @<Copy the dash list from |h| to |hh|@>;
11768    @<Copy the bounding box information from |h| to |hh| and make |bblast(hh)|
11769      point into the new object list@>;
11770    return hh;
11771  }
11772}
11773
11774
11775@ Here we use the fact that |dash_list(hh)=mp_link(hh)|.
11776@^data structure assumptions@>
11777
11778@<Copy the dash list from |h| to |hh|@>=
11779pp = (mp_dash_node)hh;
11780p = dash_list (h);
11781while ((p != mp->null_dash)) {
11782  mp_link (pp) = (mp_node)mp_get_dash_node (mp);
11783  pp = (mp_dash_node)mp_link (pp);
11784  number_clone(pp->start_x, p->start_x);
11785  number_clone(pp->stop_x, p->stop_x);
11786  p = (mp_dash_node)mp_link (p);
11787}
11788mp_link (pp) = (mp_node)mp->null_dash;
11789number_clone(hh->dash_y, h->dash_y )
11790
11791
11792@ |h| is an edge structure
11793
11794@c
11795static mp_dash_object *mp_export_dashes (MP mp, mp_stroked_node q, mp_number w) {
11796  mp_dash_object *d;
11797  mp_dash_node p, h;
11798  mp_number scf;   /* scale factor */
11799  mp_number dashoff;
11800  double *dashes = NULL;
11801  int num_dashes = 1;
11802  h = (mp_dash_node)mp_dash_p (q);
11803  if (h == NULL || dash_list (h) == mp->null_dash)
11804    return NULL;
11805  new_number (scf);
11806  p = dash_list (h);
11807  mp_get_pen_scale (mp, &scf, mp_pen_p (q));
11808  if (number_zero(scf)) {
11809    if (number_zero(w)) {
11810      number_clone(scf, q->dash_scale);
11811    } else {
11812      free_number(scf);
11813      return NULL;
11814    }
11815  } else {
11816    mp_number ret;
11817    new_number (ret);
11818    make_scaled (ret, w, scf);
11819    take_scaled (scf, ret, q->dash_scale);
11820    free_number (ret);
11821  }
11822  number_clone(w, scf);
11823  d = xmalloc (1, sizeof (mp_dash_object));
11824  add_var_used (sizeof (mp_dash_object));
11825  set_number_from_addition(mp->null_dash->start_x, p->start_x, h->dash_y);
11826  {
11827    mp_number ret, arg1;
11828    new_number (ret);
11829    new_number (arg1);
11830    new_number (dashoff);
11831    while (p != mp->null_dash) {
11832      dashes = xrealloc (dashes, (num_dashes + 2), sizeof (double));
11833      set_number_from_substraction (arg1, p->stop_x, p->start_x);
11834      take_scaled (ret, arg1, scf);
11835      dashes[(num_dashes - 1)] = number_to_double (ret);
11836      set_number_from_substraction (arg1, ((mp_dash_node)mp_link (p))->start_x, p->stop_x);
11837      take_scaled (ret, arg1, scf);
11838      dashes[(num_dashes)] = number_to_double (ret);
11839      dashes[(num_dashes + 1)] = -1.0;      /* terminus */
11840      num_dashes += 2;
11841      p = (mp_dash_node)mp_link (p);
11842    }
11843    d->array = dashes;
11844    mp_dash_offset (mp, &dashoff, h);
11845    take_scaled (ret, dashoff, scf);
11846    d->offset = number_to_double(ret);
11847    free_number (ret);
11848    free_number (arg1);
11849  }
11850  free_number (dashoff);
11851  free_number(scf);
11852  return d;
11853}
11854
11855
11856@ @<Copy the bounding box information from |h| to |hh|...@>=
11857number_clone(hh->minx, h->minx);
11858number_clone(hh->miny, h->miny);
11859number_clone(hh->maxx, h->maxx);
11860number_clone(hh->maxy, h->maxy);
11861hh->bbtype = h->bbtype;
11862p = (mp_dash_node)edge_list (h);
11863pp = (mp_dash_node)edge_list (hh);
11864while ((p != (mp_dash_node)bblast (h))) {
11865  if (p == NULL)
11866    mp_confusion (mp, "bblast");
11867@:this can't happen bblast}{\quad bblast@>;
11868  p = (mp_dash_node)mp_link (p);
11869  pp = (mp_dash_node)mp_link (pp);
11870}
11871bblast (hh) = (mp_node)pp
11872
11873@ Here is the promised routine for copying graphical objects into a new edge
11874structure.  It starts copying at object~|p| and stops just before object~|q|.
11875If |q| is NULL, it copies the entire sublist headed at |p|.  The resulting edge
11876structure requires further initialization by |init_bbox|.
11877
11878@<Declarations@>=
11879static mp_edge_header_node mp_copy_objects (MP mp, mp_node p, mp_node q);
11880
11881@ @c
11882mp_edge_header_node mp_copy_objects (MP mp, mp_node p, mp_node q) {
11883  mp_edge_header_node hh;   /* the new edge header */
11884  mp_node pp;   /* the last newly copied object */
11885  quarterword k = 0;  /* temporary register */
11886  hh = mp_get_edge_header_node (mp);
11887  set_dash_list (hh, mp->null_dash);
11888  edge_ref_count (hh) = 0;
11889  pp = edge_list (hh);
11890  while (p != q) {
11891    @<Make |mp_link(pp)| point to a copy of object |p|, and update |p| and |pp|@>;
11892  }
11893  obj_tail (hh) = pp;
11894  mp_link (pp) = NULL;
11895  return hh;
11896}
11897
11898
11899@ @<Make |mp_link(pp)| point to a copy of object |p|, and update |p| and |pp|@>=
11900{
11901  switch (mp_type (p)) {
11902  case mp_start_clip_node_type:
11903    k = start_clip_size;
11904    break;
11905  case mp_start_bounds_node_type:
11906    k = start_bounds_size;
11907    break;
11908  case mp_fill_node_type:
11909    k = fill_node_size;
11910    break;
11911  case mp_stroked_node_type:
11912    k = stroked_node_size;
11913    break;
11914  case mp_text_node_type:
11915    k = text_node_size;
11916    break;
11917  case mp_stop_clip_node_type:
11918    k = stop_clip_size;
11919    break;
11920  case mp_stop_bounds_node_type:
11921    k = stop_bounds_size;
11922    break;
11923  default:                     /* there are no other valid cases, but please the compiler */
11924    break;
11925  }
11926  mp_link (pp) = malloc_node ((size_t) k);       /* |gr_object| */
11927  pp = mp_link (pp);
11928  memcpy (pp, p, (size_t) k);
11929  pp->link = NULL;
11930  @<Fix anything in graphical object |pp| that should differ from the
11931    corresponding field in |p|@>;
11932  p = mp_link (p);
11933}
11934
11935
11936@ @<Fix anything in graphical object |pp| that should differ from the...@>=
11937switch (mp_type (p)) {
11938case mp_start_clip_node_type:
11939  {
11940    mp_start_clip_node tt = (mp_start_clip_node)pp;
11941    mp_start_clip_node t =  (mp_start_clip_node)p;
11942    mp_path_p (tt) = mp_copy_path (mp, mp_path_p (t));
11943  }
11944  break;
11945case mp_start_bounds_node_type:
11946  {
11947    mp_start_bounds_node tt = (mp_start_bounds_node)pp;
11948    mp_start_bounds_node t =  (mp_start_bounds_node)p;
11949    mp_path_p (tt) = mp_copy_path (mp, mp_path_p (t));
11950  }
11951  break;
11952case mp_fill_node_type:
11953  {
11954    mp_fill_node tt = (mp_fill_node)pp;
11955    mp_fill_node t =  (mp_fill_node)p;
11956    new_number(tt->red);    number_clone(tt->red,    t->red);
11957    new_number(tt->green);  number_clone(tt->green,  t->green);
11958    new_number(tt->blue);   number_clone(tt->blue,   t->blue);
11959    new_number(tt->black);  number_clone(tt->black,  t->black);
11960    new_number(tt->miterlim); number_clone(tt->miterlim,t->miterlim);
11961    mp_path_p (tt) =  mp_copy_path (mp, mp_path_p (t));
11962    if (mp_pre_script (p) != NULL)
11963      add_str_ref (mp_pre_script (p));
11964    if (mp_post_script (p) != NULL)
11965      add_str_ref (mp_post_script (p));
11966    if (mp_pen_p (t) != NULL)
11967      mp_pen_p (tt) = copy_pen (mp_pen_p (t));
11968  }
11969  break;
11970case mp_stroked_node_type:
11971  {
11972    mp_stroked_node tt = (mp_stroked_node)pp;
11973    mp_stroked_node t =  (mp_stroked_node)p;
11974    new_number(tt->red);        number_clone(tt->red,    t->red);
11975    new_number(tt->green);      number_clone(tt->green,  t->green);
11976    new_number(tt->blue);       number_clone(tt->blue,   t->blue);
11977    new_number(tt->black);      number_clone(tt->black,  t->black);
11978    new_number(tt->miterlim);   number_clone(tt->miterlim,t->miterlim);
11979    new_number(tt->dash_scale); number_clone(tt->dash_scale,t->dash_scale);
11980    if (mp_pre_script (p) != NULL)
11981      add_str_ref (mp_pre_script (p));
11982    if (mp_post_script (p) != NULL)
11983      add_str_ref (mp_post_script (p));
11984    mp_path_p (tt) =  mp_copy_path (mp, mp_path_p (t));
11985    mp_pen_p (tt) =  copy_pen (mp_pen_p (t));
11986    if (mp_dash_p (p) != NULL)
11987      add_edge_ref (mp_dash_p (pp));
11988  }
11989  break;
11990case mp_text_node_type:
11991  {
11992    mp_text_node tt = (mp_text_node)pp;
11993    mp_text_node t = (mp_text_node)p;
11994    new_number(tt->red);    number_clone(tt->red,    t->red);
11995    new_number(tt->green);  number_clone(tt->green,  t->green);
11996    new_number(tt->blue);   number_clone(tt->blue,   t->blue);
11997    new_number(tt->black);  number_clone(tt->black,  t->black);
11998    new_number(tt->width);  number_clone(tt->width,  t->width);
11999    new_number(tt->height); number_clone(tt->height, t->height);
12000    new_number(tt->depth);  number_clone(tt->depth,  t->depth);
12001    new_number(tt->tx);  number_clone(tt->tx,  t->tx);
12002    new_number(tt->ty);  number_clone(tt->ty,  t->ty);
12003    new_number(tt->txx); number_clone(tt->txx, t->txx);
12004    new_number(tt->tyx); number_clone(tt->tyx, t->tyx);
12005    new_number(tt->txy); number_clone(tt->txy, t->txy);
12006    new_number(tt->tyy); number_clone(tt->tyy, t->tyy);
12007    if (mp_pre_script (p) != NULL)
12008      add_str_ref (mp_pre_script (p));
12009    if (mp_post_script (p) != NULL)
12010      add_str_ref (mp_post_script (p));
12011    add_str_ref (mp_text_p (pp));
12012  }
12013  break;
12014case mp_stop_clip_node_type:
12015case mp_stop_bounds_node_type:
12016  break;
12017default:                       /* there are no other valid cases, but please the compiler */
12018  break;
12019}
12020
12021
12022@ Here is one way to find an acceptable value for the second argument to
12023|copy_objects|.  Given a non-NULL graphical object list, |skip_1component|
12024skips past one picture component, where a ``picture component'' is a single
12025graphical object, or a start bounds or start clip object and everything up
12026through the matching stop bounds or stop clip object.
12027
12028@c
12029static mp_node mp_skip_1component (MP mp, mp_node p) {
12030  integer lev;  /* current nesting level */
12031  lev = 0;
12032  (void) mp;
12033  do {
12034    if (is_start_or_stop (p)) {
12035      if (is_stop (p))
12036        decr (lev);
12037      else
12038        incr (lev);
12039    }
12040    p = mp_link (p);
12041  } while (lev != 0);
12042  return p;
12043}
12044
12045
12046@ Here is a diagnostic routine for printing an edge structure in symbolic form.
12047
12048@<Declarations@>=
12049static void mp_print_edges (MP mp, mp_node h, const char *s, boolean nuline);
12050
12051@ @c
12052void mp_print_edges (MP mp, mp_node h, const char *s, boolean nuline) {
12053  mp_node p;    /* a graphical object to be printed */
12054  mp_number scf;   /* a scale factor for the dash pattern */
12055  boolean ok_to_dash;   /* |false| for polygonal pen strokes */
12056  new_number (scf);
12057  mp_print_diagnostic (mp, "Edge structure", s, nuline);
12058  p = edge_list (h);
12059  while (mp_link (p) != NULL) {
12060    p = mp_link (p);
12061    mp_print_ln (mp);
12062    switch (mp_type (p)) {
12063      @<Cases for printing graphical object node |p|@>;
12064    default:
12065      mp_print (mp, "[unknown object type!]");
12066      break;
12067    }
12068  }
12069  mp_print_nl (mp, "End edges");
12070  if (p != obj_tail (h))
12071    mp_print (mp, "?");
12072@.End edges?@>;
12073  mp_end_diagnostic (mp, true);
12074  free_number (scf);
12075}
12076
12077
12078@ @<Cases for printing graphical object node |p|@>=
12079case mp_fill_node_type:
12080mp_print (mp, "Filled contour ");
12081mp_print_obj_color (mp, p);
12082mp_print_char (mp, xord (':'));
12083mp_print_ln (mp);
12084mp_pr_path (mp, mp_path_p ((mp_fill_node) p));
12085mp_print_ln (mp);
12086if ((mp_pen_p ((mp_fill_node) p) != NULL)) {
12087  @<Print join type for graphical object |p|@>;
12088  mp_print (mp, " with pen");
12089  mp_print_ln (mp);
12090  mp_pr_pen (mp, mp_pen_p ((mp_fill_node) p));
12091}
12092break;
12093
12094@ @<Print join type for graphical object |p|@>=
12095switch (((mp_stroked_node)p)->ljoin) {
12096case 0:
12097  mp_print (mp, "mitered joins limited ");
12098  print_number (((mp_stroked_node)p)->miterlim);
12099  break;
12100case 1:
12101  mp_print (mp, "round joins");
12102  break;
12103case 2:
12104  mp_print (mp, "beveled joins");
12105  break;
12106default:
12107  mp_print (mp, "?? joins");
12108@.??@>;
12109  break;
12110}
12111
12112
12113@ For stroked nodes, we need to print |lcap_val(p)| as well.
12114
12115@<Print join and cap types for stroked node |p|@>=
12116switch (((mp_stroked_node)p)->lcap ) {
12117case 0:
12118  mp_print (mp, "butt");
12119  break;
12120case 1:
12121  mp_print (mp, "round");
12122  break;
12123case 2:
12124  mp_print (mp, "square");
12125  break;
12126default:
12127  mp_print (mp, "??");
12128  break;
12129@.??@>
12130}
12131mp_print (mp, " ends, ");
12132@<Print join type for graphical object |p|@>
12133
12134
12135@ Here is a routine that prints the color of a graphical object if it isn't
12136black (the default color).
12137
12138@<Declarations@>=
12139static void mp_print_obj_color (MP mp, mp_node p);
12140
12141@ @c
12142void mp_print_obj_color (MP mp, mp_node p) {
12143  mp_stroked_node p0 = (mp_stroked_node) p;
12144  if (mp_color_model (p) == mp_grey_model) {
12145    if (number_positive(p0->grey)) {
12146      mp_print (mp, "greyed ");
12147      mp_print_char (mp, xord ('('));
12148      print_number (p0->grey);
12149      mp_print_char (mp, xord (')'));
12150    };
12151  } else if (mp_color_model (p) == mp_cmyk_model) {
12152    if (number_positive(p0->cyan) || number_positive(p0->magenta) ||
12153        number_positive(p0->yellow) || number_positive(p0->black)) {
12154      mp_print (mp, "processcolored ");
12155      mp_print_char (mp, xord ('('));
12156      print_number (p0->cyan);
12157      mp_print_char (mp, xord (','));
12158      print_number (p0->magenta);
12159      mp_print_char (mp, xord (','));
12160      print_number (p0->yellow);
12161      mp_print_char (mp, xord (','));
12162      print_number (p0->black);
12163      mp_print_char (mp, xord (')'));
12164    };
12165  } else if (mp_color_model (p) == mp_rgb_model) {
12166    if (number_positive(p0->red) || number_positive(p0->green) ||
12167	number_positive(p0->blue)) {
12168      mp_print (mp, "colored ");
12169      mp_print_char (mp, xord ('('));
12170      print_number (p0->red);
12171      mp_print_char (mp, xord (','));
12172      print_number (p0->green);
12173      mp_print_char (mp, xord (','));
12174      print_number (p0->blue);
12175      mp_print_char (mp, xord (')'));
12176    };
12177  }
12178}
12179
12180
12181@ @<Cases for printing graphical object node |p|@>=
12182case mp_stroked_node_type:
12183mp_print (mp, "Filled pen stroke ");
12184mp_print_obj_color (mp, p);
12185mp_print_char (mp, xord (':'));
12186mp_print_ln (mp);
12187mp_pr_path (mp, mp_path_p ((mp_stroked_node) p));
12188if (mp_dash_p (p) != NULL) {
12189  mp_print_nl (mp, "dashed (");
12190  @<Finish printing the dash pattern that |p| refers to@>;
12191}
12192mp_print_ln (mp);
12193@<Print join and cap types for stroked node |p|@>;
12194mp_print (mp, " with pen");
12195mp_print_ln (mp);
12196if (mp_pen_p ((mp_stroked_node) p) == NULL) {
12197  mp_print (mp, "???");         /* shouldn't happen */
12198@.???@>
12199} else {
12200  mp_pr_pen (mp, mp_pen_p ((mp_stroked_node) p));
12201}
12202break;
12203
12204@ Normally, the  |dash_list| field in an edge header is set to |null_dash|
12205when it is not known to define a suitable dash pattern.  This is disallowed
12206here because the |mp_dash_p| field should never point to such an edge header.
12207Note that memory is allocated for |start_x(null_dash)| and we are free to
12208give it any convenient value.
12209
12210@<Finish printing the dash pattern that |p| refers to@>=
12211{
12212mp_dash_node ppd, hhd;
12213ok_to_dash = pen_is_elliptical (mp_pen_p ((mp_stroked_node) p));
12214if (!ok_to_dash)
12215  set_number_to_unity (scf);
12216else
12217  number_clone(scf, ((mp_stroked_node) p)->dash_scale);
12218hhd = (mp_dash_node)mp_dash_p (p);
12219ppd = dash_list (hhd);
12220if ((ppd == mp->null_dash) || number_negative(hhd->dash_y)) {
12221  mp_print (mp, " ??");
12222} else {
12223  mp_number dashoff;
12224  mp_number ret, arg1;
12225  new_number (ret);
12226  new_number (arg1);
12227  new_number (dashoff);
12228  set_number_from_addition(mp->null_dash->start_x, ppd->start_x, hhd->dash_y );
12229  while (ppd != mp->null_dash) {
12230    mp_print (mp, "on ");
12231    set_number_from_substraction (arg1, ppd->stop_x, ppd->start_x);
12232    take_scaled (ret, arg1, scf);
12233    print_number ( ret);
12234    mp_print (mp, " off ");
12235    set_number_from_substraction (arg1, ((mp_dash_node)mp_link (ppd))->start_x, ppd->stop_x);
12236    take_scaled (ret, arg1, scf);
12237    print_number (ret);
12238    ppd = (mp_dash_node)mp_link (ppd);
12239    if (ppd != mp->null_dash)
12240      mp_print_char (mp, xord (' '));
12241  }
12242  mp_print (mp, ") shifted ");
12243  mp_dash_offset (mp, &dashoff, hhd);
12244  take_scaled (ret, dashoff, scf);
12245  number_negate (ret);
12246  print_number (ret);
12247  free_number (dashoff);
12248  free_number (ret);
12249  free_number (arg1);
12250  if (!ok_to_dash || number_zero(hhd->dash_y) )
12251    mp_print (mp, " (this will be ignored)");
12252}
12253}
12254
12255@ @<Declarations@>=
12256static void mp_dash_offset (MP mp, mp_number *x, mp_dash_node h);
12257
12258@ @c
12259void mp_dash_offset (MP mp, mp_number *x, mp_dash_node h) {
12260  if (dash_list (h) == mp->null_dash || number_negative(h->dash_y ))
12261    mp_confusion (mp, "dash0");
12262@:this can't happen dash0}{\quad dash0@>;
12263  if (number_zero(h->dash_y)) {
12264    set_number_to_zero(*x);
12265  } else {
12266    number_clone (*x, (dash_list (h))->start_x );
12267    number_modulo (*x, h->dash_y);
12268    number_negate (*x);
12269    if (number_negative(*x))
12270      number_add(*x, h->dash_y);
12271  }
12272}
12273
12274
12275@ @<Cases for printing graphical object node |p|@>=
12276case mp_text_node_type:
12277{
12278mp_text_node p0 = (mp_text_node)p;
12279mp_print_char (mp, xord ('"'));
12280mp_print_str (mp, mp_text_p (p));
12281mp_print (mp, "\" infont \"");
12282mp_print (mp, mp->font_name[mp_font_n (p)]);
12283mp_print_char (mp, xord ('"'));
12284mp_print_ln (mp);
12285mp_print_obj_color (mp, p);
12286mp_print (mp, "transformed ");
12287mp_print_char (mp, xord ('('));
12288print_number (p0->tx);
12289mp_print_char (mp, xord (','));
12290print_number (p0->ty);
12291mp_print_char (mp, xord (','));
12292print_number (p0->txx);
12293mp_print_char (mp, xord (','));
12294print_number (p0->txy);
12295mp_print_char (mp, xord (','));
12296print_number (p0->tyx);
12297mp_print_char (mp, xord (','));
12298print_number (p0->tyy);
12299mp_print_char (mp, xord (')'));
12300}
12301break;
12302
12303@ @<Cases for printing graphical object node |p|@>=
12304case mp_start_clip_node_type:
12305mp_print (mp, "clipping path:");
12306mp_print_ln (mp);
12307mp_pr_path (mp, mp_path_p ((mp_start_clip_node) p));
12308break;
12309case mp_stop_clip_node_type:
12310mp_print (mp, "stop clipping");
12311break;
12312
12313@ @<Cases for printing graphical object node |p|@>=
12314case mp_start_bounds_node_type:
12315mp_print (mp, "setbounds path:");
12316mp_print_ln (mp);
12317mp_pr_path (mp, mp_path_p ((mp_start_bounds_node) p));
12318break;
12319case mp_stop_bounds_node_type:
12320mp_print (mp, "end of setbounds");
12321break;
12322
12323@ To initialize the |dash_list| field in an edge header~|h|, we need a
12324subroutine that scans an edge structure and tries to interpret it as a dash
12325pattern.  This can only be done when there are no filled regions or clipping
12326paths and all the pen strokes have the same color.  The first step is to let
12327$y_0$ be the initial $y$~coordinate of the first pen stroke.  Then we implicitly
12328project all the pen stroke paths onto the line $y=y_0$ and require that there
12329be no retracing.  If the resulting paths cover a range of $x$~coordinates of
12330length $\Delta x$, we set |dash_y(h)| to the length of the dash pattern by
12331finding the maximum of $\Delta x$ and the absolute value of~$y_0$.
12332
12333@c
12334static mp_edge_header_node mp_make_dashes (MP mp, mp_edge_header_node h) { /* returns |h| or |NULL| */
12335  mp_node p;    /* this scans the stroked nodes in the object list */
12336  mp_node p0;   /* if not |NULL| this points to the first stroked node */
12337  mp_knot pp, qq, rr;   /* pointers into |mp_path_p(p)| */
12338  mp_dash_node d, dd;        /* pointers used to create the dash list */
12339  mp_number y0;
12340  @<Other local variables in |make_dashes|@>;
12341  if (dash_list (h) != mp->null_dash)
12342    return h;
12343  new_number (y0);                       /* the initial $y$ coordinate */
12344  p0 = NULL;
12345  p = mp_link (edge_list (h));
12346  while (p != NULL) {
12347    if (mp_type (p) != mp_stroked_node_type) {
12348      @<Compain that the edge structure contains a node of the wrong type
12349        and |goto not_found|@>;
12350    }
12351    pp = mp_path_p ((mp_stroked_node) p);
12352    if (p0 == NULL) {
12353      p0 = p;
12354      number_clone(y0, pp->y_coord);
12355    }
12356    @<Make |d| point to a new dash node created from stroke |p| and path |pp|
12357      or |goto not_found| if there is an error@>;
12358    @<Insert |d| into the dash list and |goto not_found| if there is an error@>;
12359    p = mp_link (p);
12360  }
12361  if (dash_list (h) == mp->null_dash)
12362    goto NOT_FOUND;             /* No error message */
12363  @<Scan |dash_list(h)| and deal with any dashes that are themselves dashed@>;
12364  @<Set |dash_y(h)| and merge the first and last dashes if necessary@>;
12365  free_number (y0);
12366  return h;
12367NOT_FOUND:
12368  free_number (y0);
12369  @<Flush the dash list, recycle |h| and return |NULL|@>;
12370}
12371
12372
12373@ @<Compain that the edge structure contains a node of the wrong type...@>=
12374{
12375  const char *hlp[] = {
12376         "When you say `dashed p', picture p should not contain any",
12377         "text, filled regions, or clipping paths.  This time it did",
12378         "so I'll just make it a solid line instead.",
12379         NULL };
12380  mp_back_error (mp, "Picture is too complicated to use as a dash pattern", hlp, true);
12381  mp_get_x_next (mp);
12382  goto NOT_FOUND;
12383}
12384
12385
12386@ A similar error occurs when monotonicity fails.
12387
12388@<Declarations@>=
12389static void mp_x_retrace_error (MP mp);
12390
12391@ @c
12392void mp_x_retrace_error (MP mp) {
12393  const char *hlp[] = {
12394         "When you say `dashed p', every path in p should be monotone",
12395         "in x and there must be no overlapping.  This failed",
12396         "so I'll just make it a solid line instead.",
12397         NULL };
12398  mp_back_error (mp, "Picture is too complicated to use as a dash pattern", hlp, true);
12399  mp_get_x_next (mp);
12400}
12401
12402
12403@ We stash |p| in |dash_info(d)| if |mp_dash_p(p)<>0| so that subsequent processing can
12404handle the case where the pen stroke |p| is itself dashed.
12405
12406@d dash_info(A) ((mp_dash_node)(A))->dash_info_  /* in an edge header this points to the first dash node */
12407
12408@<Make |d| point to a new dash node created from stroke |p| and path...@>=
12409@<Make sure |p| and |p0| are the same color and |goto not_found| if there is
12410  an error@>;
12411rr = pp;
12412if (mp_next_knot (pp) != pp) {
12413  do {
12414    qq = rr;
12415    rr = mp_next_knot (rr);
12416    @<Check for retracing between knots |qq| and |rr| and |goto not_found|
12417      if there is a problem@>;
12418  } while (mp_right_type (rr) != mp_endpoint);
12419}
12420d = (mp_dash_node)mp_get_dash_node (mp);
12421if (mp_dash_p (p) == NULL)
12422  dash_info (d) = NULL;
12423else
12424  dash_info (d) = p;
12425if (number_less (pp->x_coord, rr->x_coord)) {
12426  number_clone(d->start_x, pp->x_coord);
12427  number_clone(d->stop_x, rr->x_coord);
12428} else {
12429  number_clone(d->start_x, rr->x_coord);
12430  number_clone(d->stop_x, pp->x_coord);
12431}
12432
12433
12434@ We also need to check for the case where the segment from |qq| to |rr| is
12435monotone in $x$ but is reversed relative to the path from |pp| to |qq|.
12436
12437@<Check for retracing between knots |qq| and |rr| and |goto not_found|...@>=
12438{
12439  mp_number x0, x1, x2, x3;  /* $x$ coordinates of the segment from |qq| to |rr| */
12440  new_number(x0);
12441  new_number(x1);
12442  new_number(x2);
12443  new_number(x3);
12444  number_clone(x0, qq->x_coord);
12445  number_clone(x1, qq->right_x);
12446  number_clone(x2, rr->left_x);
12447  number_clone(x3, rr->x_coord);
12448  if (number_greater(x0, x1) || number_greater(x1, x2) || number_greater(x2, x3)) {
12449    if (number_less(x0, x1) || number_less(x1, x2) || number_less(x2, x3)) {
12450      mp_number a1, a2, a3, a4;
12451      mp_number test;
12452      new_number(test);
12453      new_number(a1);
12454      new_number(a2);
12455      new_number(a3);
12456      new_number(a4);
12457      set_number_from_substraction(a1, x2, x1);
12458      set_number_from_substraction(a2, x2, x1);
12459      set_number_from_substraction(a3, x1, x0);
12460      set_number_from_substraction(a4, x3, x2);
12461      ab_vs_cd (test, a1, a2, a3, a4);
12462      free_number(a1);
12463      free_number(a2);
12464      free_number(a3);
12465      free_number(a4);
12466      if (number_positive(test)) {
12467        mp_x_retrace_error (mp);
12468        free_number(x0);
12469        free_number(x1);
12470        free_number(x2);
12471        free_number(x3);
12472        free_number(test);
12473        goto NOT_FOUND;
12474      }
12475      free_number(test);
12476    }
12477  }
12478  if (number_greater(pp->x_coord, x0) || number_greater(x0, x3)) {
12479    if (number_less (pp->x_coord, x0) || number_less(x0, x3)) {
12480      mp_x_retrace_error (mp);
12481      free_number(x0);
12482      free_number(x1);
12483      free_number(x2);
12484      free_number(x3);
12485      goto NOT_FOUND;
12486    }
12487  }
12488  free_number(x0);
12489  free_number(x1);
12490  free_number(x2);
12491  free_number(x3);
12492}
12493
12494@ @<Make sure |p| and |p0| are the same color and |goto not_found|...@>=
12495if (!number_equal(((mp_stroked_node)p)->red, ((mp_stroked_node)p0)->red) ||
12496    !number_equal(((mp_stroked_node)p)->black, ((mp_stroked_node)p0)->black) ||
12497    !number_equal(((mp_stroked_node)p)->green, ((mp_stroked_node)p0)->green) ||
12498    !number_equal(((mp_stroked_node)p)->blue, ((mp_stroked_node)p0)->blue)
12499    ) {
12500  const char *hlp[] = {
12501         "When you say `dashed p', everything in picture p should",
12502         "be the same color.  I can\'t handle your color changes",
12503         "so I'll just make it a solid line instead.",
12504         NULL };
12505  mp_back_error (mp, "Picture is too complicated to use as a dash pattern", hlp, true);
12506  mp_get_x_next (mp);
12507  goto NOT_FOUND;
12508}
12509
12510@ @<Insert |d| into the dash list and |goto not_found| if there is an error@>=
12511number_clone(mp->null_dash->start_x, d->stop_x);
12512dd = (mp_dash_node)h;                         /* this makes |mp_link(dd)=dash_list(h)| */
12513while (number_less(((mp_dash_node)mp_link (dd))->start_x, d->stop_x ))
12514  dd = (mp_dash_node)mp_link (dd);
12515if (dd != (mp_dash_node)h) {
12516  if (number_greater(dd->stop_x, d->start_x)) {
12517    mp_x_retrace_error (mp);
12518    goto NOT_FOUND;
12519  };
12520}
12521mp_link (d) = mp_link (dd);
12522mp_link (dd) = (mp_node)d
12523
12524@ @<Set |dash_y(h)| and merge the first and last dashes if necessary@>=
12525d = dash_list (h);
12526while ((mp_link (d) != (mp_node)mp->null_dash))
12527  d = (mp_dash_node)mp_link (d);
12528dd = dash_list (h);
12529set_number_from_substraction(h->dash_y, d->stop_x, dd->start_x);
12530{
12531  mp_number absval;
12532  new_number (absval);
12533  number_clone (absval, y0);
12534  number_abs (absval);
12535  if (number_greater (absval, h->dash_y) ) {
12536    number_clone(h->dash_y, absval);
12537  } else if (d != dd) {
12538    set_dash_list (h, mp_link (dd));
12539    set_number_from_addition(d->stop_x, dd->stop_x, h->dash_y);
12540    mp_free_node (mp, (mp_node)dd, dash_node_size);
12541  }
12542  free_number (absval);
12543}
12544@ We get here when the argument is a NULL picture or when there is an error.
12545Recovering from an error involves making |dash_list(h)| empty to indicate
12546that |h| is not known to be a valid dash pattern.  We also dereference |h|
12547since it is not being used for the return value.
12548
12549@<Flush the dash list, recycle |h| and return |NULL|@>=
12550mp_flush_dash_list (mp, h);
12551delete_edge_ref (h);
12552return NULL
12553
12554@ Having carefully saved the dashed stroked nodes in the
12555corresponding dash nodes, we must be prepared to break up these dashes into
12556smaller dashes.
12557
12558@<Scan |dash_list(h)| and deal with any dashes that are themselves dashed@>=
12559{
12560mp_number hsf;     /* the dash pattern from |hh| gets scaled by this */
12561new_number (hsf);
12562d = (mp_dash_node)h;                          /* now |mp_link(d)=dash_list(h)| */
12563while (mp_link (d) != (mp_node)mp->null_dash) {
12564  ds = dash_info (mp_link (d));
12565  if (ds == NULL) {
12566    d = (mp_dash_node)mp_link (d);
12567  } else {
12568    hh = (mp_edge_header_node)mp_dash_p (ds);
12569    number_clone(hsf, ((mp_stroked_node)ds)->dash_scale);
12570    if (hh == NULL)
12571      mp_confusion (mp, "dash1");
12572@:this can't happen dash0}{\quad dash1@>;
12573    /* clang: dereference null pointer 'hh' */ assert(hh);
12574    if (number_zero(((mp_dash_node)hh)->dash_y )) {
12575      d = (mp_dash_node)mp_link (d);
12576    } else {
12577      if (dash_list (hh) == NULL)
12578        mp_confusion (mp, "dash1");
12579@:this can't happen dash0}{\quad dash1@>;
12580      @<Replace |mp_link(d)| by a dashed version as determined by edge header
12581          |hh| and scale factor |ds|@>;
12582    }
12583  }
12584}
12585free_number (hsf);
12586}
12587
12588@ @<Other local variables in |make_dashes|@>=
12589mp_dash_node dln;    /* |mp_link(d)| */
12590mp_edge_header_node hh;     /* an edge header that tells how to break up |dln| */
12591mp_node ds;     /* the stroked node from which |hh| and |hsf| are derived */
12592
12593@ @<Replace |mp_link(d)| by a dashed version as determined by edge header...@>=
12594{
12595  mp_number xoff;    /* added to $x$ values in |dash_list(hh)| to match |dln| */
12596  mp_number dashoff;
12597  mp_number r1, r2;
12598  new_number (r1);
12599  new_number (r2);
12600  dln = (mp_dash_node)mp_link (d);
12601  dd = dash_list (hh);
12602  /* clang: dereference null pointer 'dd' */ assert(dd);
12603  new_number (xoff);
12604  new_number (dashoff);
12605  mp_dash_offset (mp, &dashoff, (mp_dash_node)hh);
12606  take_scaled (r1, hsf, dd->start_x);
12607  take_scaled (r2, hsf, dashoff);
12608  number_add (r1, r2);
12609  set_number_from_substraction(xoff, dln->start_x, r1);
12610  free_number (dashoff);
12611  take_scaled (r1, hsf, dd->start_x);
12612  take_scaled (r2, hsf, hh->dash_y);
12613  set_number_from_addition(mp->null_dash->start_x, r1, r2);
12614  number_clone(mp->null_dash->stop_x, mp->null_dash->start_x);
12615  @<Advance |dd| until finding the first dash that overlaps |dln| when
12616    offset by |xoff|@>;
12617  while (number_lessequal(dln->start_x, dln->stop_x)) {
12618    @<If |dd| has `fallen off the end', back up to the beginning and fix |xoff|@>;
12619    @<Insert a dash between |d| and |dln| for the overlap with the offset version
12620      of |dd|@>;
12621    dd = (mp_dash_node)mp_link (dd);
12622    take_scaled (r1, hsf, dd->start_x);
12623    set_number_from_addition(dln->start_x , xoff, r1);
12624  }
12625  free_number(xoff);
12626  free_number (r1);
12627  free_number (r2);
12628  mp_link (d) = mp_link (dln);
12629  mp_free_node (mp, (mp_node)dln, dash_node_size);
12630}
12631
12632
12633@ The name of this module is a bit of a lie because we just find the
12634first |dd| where |take_scaled (hsf, stop_x(dd))| is large enough to make an
12635overlap possible.  It could be that the unoffset version of dash |dln| falls
12636in the gap between |dd| and its predecessor.
12637
12638@<Advance |dd| until finding the first dash that overlaps |dln| when...@>=
12639{
12640  mp_number r1;
12641  new_number (r1);
12642  take_scaled (r1, hsf, dd->stop_x);
12643  number_add (r1, xoff);
12644  while (number_less(r1, dln->start_x)) {
12645    dd = (mp_dash_node)mp_link (dd);
12646    take_scaled (r1, hsf, dd->stop_x);
12647    number_add (r1, xoff);
12648  }
12649  free_number (r1);
12650}
12651
12652@ @<If |dd| has `fallen off the end', back up to the beginning and fix...@>=
12653if (dd == mp->null_dash) {
12654  mp_number ret;
12655  new_number (ret);
12656  dd = dash_list (hh);
12657  take_scaled (ret, hsf, hh->dash_y);
12658  number_add(xoff, ret);
12659  free_number (ret);
12660}
12661
12662@ At this point we already know that |start_x(dln)<=xoff+take_scaled(hsf,stop_x(dd))|.
12663
12664@<Insert a dash between |d| and |dln| for the overlap with the offset...@>=
12665{
12666  mp_number r1;
12667  new_number (r1);
12668  take_scaled (r1, hsf, dd->start_x);
12669  number_add (r1, xoff);
12670  if (number_lessequal(r1, dln->stop_x)) {
12671    mp_link (d) = (mp_node)mp_get_dash_node (mp);
12672    d = (mp_dash_node)mp_link (d);
12673    mp_link (d) = (mp_node)dln;
12674    take_scaled (r1, hsf, dd->start_x );
12675    number_add (r1, xoff);
12676    if (number_greater(dln->start_x, r1))
12677      number_clone(d->start_x, dln->start_x);
12678    else {
12679      number_clone(d->start_x, r1);
12680    }
12681    take_scaled (r1, hsf, dd->stop_x);
12682    number_add (r1, xoff);
12683    if (number_less(dln->stop_x, r1))
12684      number_clone(d->stop_x, dln->stop_x );
12685    else {
12686      number_clone(d->stop_x, r1);
12687    }
12688  }
12689  free_number (r1);
12690}
12691
12692@ The next major task is to update the bounding box information in an edge
12693header~|h|. This is done via a procedure |adjust_bbox| that enlarges an edge
12694header's bounding box to accommodate the box computed by |path_bbox| or
12695|pen_bbox|. (This is stored in global variables |minx|, |miny|, |maxx|, and
12696|maxy|.)
12697
12698@c
12699static void mp_adjust_bbox (MP mp, mp_edge_header_node h) {
12700  if (number_less (mp_minx, h->minx))
12701    number_clone(h->minx, mp_minx);
12702  if (number_less (mp_miny, h->miny))
12703    number_clone(h->miny, mp_miny);
12704  if (number_greater (mp_maxx, h->maxx))
12705    number_clone(h->maxx, mp_maxx);
12706  if (number_greater (mp_maxy, h->maxy))
12707    number_clone(h->maxy, mp_maxy);
12708}
12709
12710
12711@ Here is a special routine for updating the bounding box information in
12712edge header~|h| to account for the squared-off ends of a non-cyclic path~|p|
12713that is to be stroked with the pen~|pp|.
12714
12715@c
12716static void mp_box_ends (MP mp, mp_knot p, mp_knot pp, mp_edge_header_node h) {
12717  mp_knot q;    /* a knot node adjacent to knot |p| */
12718  mp_fraction dx, dy;      /* a unit vector in the direction out of the path at~|p| */
12719  mp_number d;     /* a factor for adjusting the length of |(dx,dy)| */
12720  mp_number z;     /* a coordinate being tested against the bounding box */
12721  mp_number xx, yy;        /* the extreme pen vertex in the |(dx,dy)| direction */
12722  integer i;    /* a loop counter */
12723  new_fraction(dx);
12724  new_fraction(dy);
12725  new_number(xx);
12726  new_number(yy);
12727  new_number(z);
12728  new_number(d);
12729  if (mp_right_type (p) != mp_endpoint) {
12730    q = mp_next_knot (p);
12731    while (1) {
12732      @<Make |(dx,dy)| the final direction for the path segment from
12733        |q| to~|p|; set~|d|@>;
12734      pyth_add (d, dx, dy);
12735      if (number_positive(d)) {
12736        @<Normalize the direction |(dx,dy)| and find the pen offset |(xx,yy)|@>;
12737        for (i = 1; i <= 2; i++) {
12738          @<Use |(dx,dy)| to generate a vertex of the square end cap and
12739             update the bounding box to accommodate it@>;
12740          number_negate(dx);
12741          number_negate(dy);
12742        }
12743      }
12744      if (mp_right_type (p) == mp_endpoint) {
12745        goto DONE;
12746      } else {
12747        @<Advance |p| to the end of the path and make |q| the previous knot@>;
12748      }
12749    }
12750  }
12751DONE:
12752  free_number (dx);
12753  free_number (dy);
12754  free_number (xx);
12755  free_number (yy);
12756  free_number (z);
12757  free_number (d);
12758}
12759
12760
12761@ @<Make |(dx,dy)| the final direction for the path segment from...@>=
12762if (q == mp_next_knot (p)) {
12763  set_number_from_substraction(dx, p->x_coord, p->right_x);
12764  set_number_from_substraction(dy, p->y_coord, p->right_y);
12765  if (number_zero(dx) && number_zero(dy)) {
12766    set_number_from_substraction(dx, p->x_coord, q->left_x);
12767    set_number_from_substraction(dy, p->y_coord, q->left_y);
12768  }
12769} else {
12770  set_number_from_substraction(dx, p->x_coord, p->left_x);
12771  set_number_from_substraction(dy, p->y_coord, p->left_y);
12772  if (number_zero(dx) && number_zero(dy)) {
12773    set_number_from_substraction(dx, p->x_coord, q->right_x);
12774    set_number_from_substraction(dy, p->y_coord, q->right_y);
12775  }
12776}
12777set_number_from_substraction(dx, p->x_coord, q->x_coord);
12778set_number_from_substraction(dy, p->y_coord, q->y_coord);
12779
12780
12781@ @<Normalize the direction |(dx,dy)| and find the pen offset |(xx,yy)|@>=
12782{
12783  mp_number arg1, r;
12784  new_fraction (r);
12785  new_number(arg1);
12786  make_fraction (r, dx, d);
12787  number_clone(dx, r);
12788  make_fraction (r, dy, d);
12789  number_clone(dy, r);
12790  free_number (r);
12791  number_clone(arg1, dy);
12792  number_negate(arg1);
12793  mp_find_offset (mp, arg1, dx, pp);
12794  free_number(arg1);
12795  number_clone(xx, mp->cur_x);
12796  number_clone(yy, mp->cur_y);
12797}
12798
12799@ @<Use |(dx,dy)| to generate a vertex of the square end cap and...@>=
12800{
12801  mp_number r1, r2, arg1;
12802  new_number (arg1);
12803  new_fraction (r1);
12804  new_fraction (r2);
12805  mp_find_offset (mp, dx, dy, pp);
12806  set_number_from_substraction (arg1, xx, mp->cur_x);
12807  take_fraction (r1, arg1, dx);
12808  set_number_from_substraction (arg1, yy, mp->cur_y);
12809  take_fraction (r2, arg1, dy);
12810  set_number_from_addition(d, r1, r2);
12811  if ((number_negative(d) && (i == 1)) || (number_positive(d) && (i == 2)))
12812    mp_confusion (mp, "box_ends");
12813@:this can't happen box ends}{\quad\\{box\_ends}@>;
12814  take_fraction (r1, d, dx);
12815  set_number_from_addition(z, p->x_coord, mp->cur_x);
12816  number_add (z, r1);
12817  if (number_less(z, h->minx))
12818    number_clone(h->minx, z);
12819  if (number_greater(z, h->maxx))
12820    number_clone(h->maxx, z);
12821  take_fraction (r1, d, dy);
12822  set_number_from_addition(z, p->y_coord, mp->cur_y);
12823  number_add (z, r1);
12824  if (number_less(z, h->miny))
12825    number_clone(h->miny, z);
12826  if (number_greater(z, h->maxy))
12827    number_clone(h->maxy, z);
12828  free_number (r1);
12829  free_number (r2);
12830  free_number (arg1);
12831}
12832
12833@ @<Advance |p| to the end of the path and make |q| the previous knot@>=
12834do {
12835  q = p;
12836  p = mp_next_knot (p);
12837} while (mp_right_type (p) != mp_endpoint)
12838
12839@ The major difficulty in finding the bounding box of an edge structure is the
12840effect of clipping paths.  We treat them conservatively by only clipping to the
12841clipping path's bounding box, but this still
12842requires recursive calls to |set_bbox| in order to find the bounding box of
12843@^recursion@>
12844the objects to be clipped.  Such calls are distinguished by the fact that the
12845boolean parameter |top_level| is false.
12846
12847@c
12848void mp_set_bbox (MP mp, mp_edge_header_node h, boolean top_level) {
12849  mp_node p;    /* a graphical object being considered */
12850  integer lev;  /* nesting level for |mp_start_bounds_node| nodes */
12851  /* Wipe out any existing bounding box information if |bbtype(h)| is
12852     incompatible with |internal[mp_true_corners]| */
12853  switch (h->bbtype ) {
12854  case no_bounds:
12855    break;
12856  case bounds_set:
12857    if (number_positive(internal_value (mp_true_corners)))
12858      mp_init_bbox (mp, h);
12859    break;
12860  case bounds_unset:
12861    if (number_nonpositive(internal_value (mp_true_corners)))
12862      mp_init_bbox (mp, h);
12863    break;
12864  } /* there are no other cases */
12865
12866  while (mp_link (bblast (h)) != NULL) {
12867    p = mp_link (bblast (h));
12868    bblast (h) = p;
12869    switch (mp_type (p)) {
12870    case mp_stop_clip_node_type:
12871      if (top_level)
12872        mp_confusion (mp, "bbox");
12873      else
12874        return;
12875@:this can't happen bbox}{\quad bbox@>;
12876      break;
12877      @<Other cases for updating the bounding box based on the type of object |p|@>;
12878    default:                   /* there are no other valid cases, but please the compiler */
12879      break;
12880    }
12881  }
12882  if (!top_level)
12883    mp_confusion (mp, "bbox");
12884}
12885
12886
12887@ @<Declarations@>=
12888static void mp_set_bbox (MP mp, mp_edge_header_node h, boolean top_level);
12889
12890
12891@ @<Other cases for updating the bounding box...@>=
12892case mp_fill_node_type:
12893  mp_path_bbox (mp, mp_path_p ((mp_fill_node) p));
12894  if (mp_pen_p ((mp_fill_node) p) != NULL) {
12895    mp_number x0a, y0a, x1a, y1a;
12896    new_number (x0a);
12897    new_number (y0a);
12898    new_number (x1a);
12899    new_number (y1a);
12900    number_clone (x0a, mp_minx);
12901    number_clone (y0a, mp_miny);
12902    number_clone (x1a, mp_maxx);
12903    number_clone (y1a, mp_maxy);
12904    mp_pen_bbox (mp, mp_pen_p ((mp_fill_node) p));
12905    number_add (mp_minx, x0a);
12906    number_add (mp_miny, y0a);
12907    number_add (mp_maxx, x1a);
12908    number_add (mp_maxy, y1a);
12909    free_number (x0a);
12910    free_number (y0a);
12911    free_number (x1a);
12912    free_number (y1a);
12913  }
12914  mp_adjust_bbox (mp, h);
12915break;
12916
12917@ @<Other cases for updating the bounding box...@>=
12918case mp_start_bounds_node_type:
12919  if (number_positive (internal_value (mp_true_corners))) {
12920    h->bbtype = bounds_unset;
12921  } else {
12922    h->bbtype = bounds_set;
12923    mp_path_bbox (mp, mp_path_p ((mp_start_bounds_node) p));
12924    mp_adjust_bbox (mp, h);
12925    @<Scan to the matching |mp_stop_bounds_node| node and update |p| and
12926      |bblast(h)|@>;
12927  }
12928  break;
12929case mp_stop_bounds_node_type:
12930  if (number_nonpositive (internal_value (mp_true_corners)))
12931    mp_confusion (mp, "bbox2");
12932@:this can't happen bbox2}{\quad bbox2@>;
12933  break;
12934
12935@ @<Scan to the matching |mp_stop_bounds_node| node and update |p| and...@>=
12936lev = 1;
12937while (lev != 0) {
12938  if (mp_link (p) == NULL)
12939    mp_confusion (mp, "bbox2");
12940@:this can't happen bbox2}{\quad bbox2@>;
12941  /* clang: dereference null pointer */ assert(mp_link(p));
12942  p = mp_link (p);
12943  if (mp_type (p) == mp_start_bounds_node_type)
12944    incr (lev);
12945  else if (mp_type (p) == mp_stop_bounds_node_type)
12946    decr (lev);
12947}
12948bblast (h) = p
12949
12950@ It saves a lot of grief here to be slightly conservative and not account for
12951omitted parts of dashed lines.  We also don't worry about the material omitted
12952when using butt end caps.  The basic computation is for round end caps and
12953|box_ends| augments it for square end caps.
12954
12955@<Other cases for updating the bounding box...@>=
12956case mp_stroked_node_type:
12957mp_path_bbox (mp, mp_path_p ((mp_stroked_node) p));
12958{
12959    mp_number x0a, y0a, x1a, y1a;
12960    new_number (x0a);
12961    new_number (y0a);
12962    new_number (x1a);
12963    new_number (y1a);
12964    number_clone (x0a, mp_minx);
12965    number_clone (y0a, mp_miny);
12966    number_clone (x1a, mp_maxx);
12967    number_clone (y1a, mp_maxy);
12968    mp_pen_bbox (mp, mp_pen_p ((mp_stroked_node) p));
12969    number_add (mp_minx, x0a);
12970    number_add (mp_miny, y0a);
12971    number_add (mp_maxx, x1a);
12972    number_add (mp_maxy, y1a);
12973    free_number (x0a);
12974    free_number (y0a);
12975    free_number (x1a);
12976    free_number (y1a);
12977}
12978mp_adjust_bbox (mp, h);
12979if ((mp_left_type (mp_path_p ((mp_stroked_node) p)) == mp_endpoint)
12980    && (((mp_stroked_node) p)->lcap == 2))
12981  mp_box_ends (mp, mp_path_p ((mp_stroked_node) p),
12982             mp_pen_p ((mp_stroked_node) p), h);
12983break;
12984
12985@ The height width and depth information stored in a text node determines a
12986rectangle that needs to be transformed according to the transformation
12987parameters stored in the text node.
12988
12989@<Other cases for updating the bounding box...@>=
12990case mp_text_node_type:
12991{
12992  mp_number x0a, y0a, x1a, y1a, arg1;
12993  mp_text_node p0 = (mp_text_node)p;
12994  new_number (x0a);
12995  new_number (x1a);
12996  new_number (y0a);
12997  new_number (y1a);
12998  new_number (arg1);
12999  number_clone (arg1, p0->depth);
13000  number_negate (arg1);
13001  take_scaled (x1a, p0->txx, p0->width);
13002  take_scaled (y0a, p0->txy, arg1);
13003  take_scaled (y1a, p0->txy, p0->height);
13004  number_clone (mp_minx, p0->tx);
13005  number_clone (mp_maxx, mp_minx);
13006  if (number_less(y0a, y1a)) {
13007    number_add (mp_minx, y0a);
13008    number_add (mp_maxx, y1a);
13009  } else {
13010    number_add (mp_minx, y1a);
13011    number_add (mp_maxx, y0a);
13012  }
13013  if (number_negative(x1a))
13014    number_add (mp_minx, x1a);
13015  else
13016    number_add (mp_maxx, x1a);
13017  take_scaled (x1a, p0->tyx, p0->width);
13018  number_clone (arg1, p0->depth);
13019  number_negate (arg1);
13020  take_scaled (y0a, p0->tyy, arg1);
13021  take_scaled (y1a, p0->tyy, p0->height);
13022  number_clone (mp_miny, p0->ty);
13023  number_clone (mp_maxy, mp_miny);
13024  if (number_less (y0a, y1a)) {
13025    number_add (mp_miny, y0a);
13026    number_add (mp_maxy, y1a);
13027  } else {
13028    number_add (mp_miny, y1a);
13029    number_add (mp_maxy, y0a);
13030  }
13031  if (number_negative(x1a))
13032    number_add (mp_miny, x1a);
13033  else
13034    number_add (mp_maxy, x1a);
13035  mp_adjust_bbox (mp, h);
13036  free_number (x0a);
13037  free_number (y0a);
13038  free_number (x1a);
13039  free_number (y1a);
13040  free_number (arg1);
13041}
13042break;
13043
13044@ This case involves a recursive call that advances |bblast(h)| to the node of
13045type |mp_stop_clip_node| that matches |p|.
13046
13047@<Other cases for updating the bounding box...@>=
13048case mp_start_clip_node_type:
13049{
13050  mp_number sminx, sminy, smaxx, smaxy;
13051  /* for saving the bounding box during recursive calls */
13052  mp_number x0a, y0a, x1a, y1a;
13053    new_number (x0a);
13054    new_number (y0a);
13055    new_number (x1a);
13056    new_number (y1a);
13057    new_number (sminx);
13058    new_number (sminy);
13059    new_number (smaxx);
13060    new_number (smaxy);
13061mp_path_bbox (mp, mp_path_p ((mp_start_clip_node) p));
13062number_clone (x0a, mp_minx);
13063number_clone (y0a, mp_miny);
13064number_clone (x1a, mp_maxx);
13065number_clone (y1a, mp_maxy);
13066number_clone (sminx, h->minx);
13067number_clone (sminy, h->miny);
13068number_clone (smaxx, h->maxx);
13069number_clone (smaxy, h->maxy);
13070@<Reinitialize the bounding box in header |h| and call |set_bbox| recursively
13071    starting at |mp_link(p)|@>;
13072@<Clip the bounding box in |h| to the rectangle given by |x0a|, |x1a|,
13073    |y0a|, |y1a|@>;
13074number_clone (mp_minx, sminx);
13075number_clone (mp_miny, sminy);
13076number_clone (mp_maxx, smaxx);
13077number_clone (mp_maxy, smaxy);
13078mp_adjust_bbox (mp, h);
13079    free_number (sminx);
13080    free_number (sminy);
13081    free_number (smaxx);
13082    free_number (smaxy);
13083    free_number (x0a);
13084    free_number (y0a);
13085    free_number (x1a);
13086    free_number (y1a);
13087}
13088break;
13089
13090@ @<Reinitialize the bounding box in header |h| and call |set_bbox|...@>=
13091set_number_to_inf(h->minx);
13092set_number_to_inf(h->miny);
13093set_number_to_neg_inf(h->maxx);
13094set_number_to_neg_inf(h->maxy);
13095mp_set_bbox (mp, h, false)
13096
13097
13098@ @<Clip the bounding box in |h| to the rectangle given by |x0a|, |x1a|,...@>=
13099if (number_less(h->minx, x0a))
13100  number_clone(h->minx, x0a);
13101if (number_less(h->miny, y0a))
13102  number_clone(h->miny, y0a);
13103if (number_greater(h->maxx, x1a))
13104  number_clone(h->maxx, x1a);
13105if (number_greater(h->maxy, y1a))
13106  number_clone(h->maxy, y1a);
13107
13108@* Finding an envelope.
13109When \MP\ has a path and a polygonal pen, it needs to express the desired
13110shape in terms of things \ps\ can understand.  The present task is to compute
13111a new path that describes the region to be filled.  It is convenient to
13112define this as a two step process where the first step is determining what
13113offset to use for each segment of the path.
13114
13115@ Given a pointer |c| to a cyclic path,
13116and a pointer~|h| to the first knot of a pen polygon,
13117the |offset_prep| routine changes the path into cubics that are
13118associated with particular pen offsets. Thus if the cubic between |p|
13119and~|q| is associated with the |k|th offset and the cubic between |q| and~|r|
13120has offset |l| then |mp_info(q)=zero_off+l-k|. (The constant |zero_off| is added
13121to because |l-k| could be negative.)
13122
13123After overwriting the type information with offset differences, we no longer
13124have a true path so we refer to the knot list returned by |offset_prep| as an
13125``envelope spec.''
13126@^envelope spec@>
13127Since an envelope spec only determines relative changes in pen offsets,
13128|offset_prep| sets a global variable |spec_offset| to the relative change from
13129|h| to the first offset.
13130
13131@d zero_off 16384 /* added to offset changes to make them positive */
13132
13133@<Glob...@>=
13134integer spec_offset;    /* number of pen edges between |h| and the initial offset */
13135
13136@ @c
13137static mp_knot mp_offset_prep (MP mp, mp_knot c, mp_knot h) {
13138  int n;   /* the number of vertices in the pen polygon */
13139  mp_knot c0, p, q, q0, r, w, ww;       /* for list manipulation */
13140  int k_needed;     /* amount to be added to |mp_info(p)| when it is computed */
13141  mp_knot w0;   /* a pointer to pen offset to use just before |p| */
13142  mp_number dxin, dyin;    /* the direction into knot |p| */
13143  int turn_amt;     /* change in pen offsets for the current cubic */
13144  mp_number max_coef;       /* used while scaling */
13145  mp_number ss;
13146  @<Other local variables for |offset_prep|@>;
13147  new_number(max_coef);
13148  new_number(dxin);
13149  new_number(dyin);
13150  new_number(dx0);
13151  new_number(dy0);
13152  new_number(x0);
13153  new_number(y0);
13154  new_number(x1);
13155  new_number(y1);
13156  new_number(x2);
13157  new_number(y2);
13158  new_number(du);
13159  new_number(dv);
13160  new_number(dx);
13161  new_number(dy);
13162  new_number(x0a);
13163  new_number(y0a);
13164  new_number(x1a);
13165  new_number(y1a);
13166  new_number(x2a);
13167  new_number(y2a);
13168  new_number(t0);
13169  new_number(t1);
13170  new_number(t2);
13171  new_number(u0);
13172  new_number(u1);
13173  new_number(v0);
13174  new_number(v1);
13175  new_fraction (ss);
13176  new_fraction (s);
13177  new_fraction (t);
13178  @<Initialize the pen size~|n|@>;
13179  @<Initialize the incoming direction and pen offset at |c|@>;
13180  p = c;
13181  c0 = c;
13182  k_needed = 0;
13183  do {
13184    q = mp_next_knot (p);
13185    @<Split the cubic between |p| and |q|, if necessary, into cubics
13186      associated with single offsets, after which |q| should
13187      point to the end of the final such cubic@>;
13188  NOT_FOUND:
13189    @<Advance |p| to node |q|, removing any ``dead'' cubics that
13190      might have been introduced by the splitting process@>;
13191  } while (q != c);
13192  @<Fix the offset change in |mp_knot_info(c)| and set |c| to the return value of
13193    |offset_prep|@>;
13194  free_number (ss);
13195  free_number (s);
13196  free_number (dxin);
13197  free_number (dyin);
13198  free_number (dx0);
13199  free_number (dy0);
13200  free_number (x0);
13201  free_number (y0);
13202  free_number (x1);
13203  free_number (y1);
13204  free_number (x2);
13205  free_number (y2);
13206  free_number (max_coef);
13207  free_number (du);
13208  free_number (dv);
13209  free_number (dx);
13210  free_number (dy);
13211  free_number (x0a);
13212  free_number (y0a);
13213  free_number (x1a);
13214  free_number (y1a);
13215  free_number (x2a);
13216  free_number (y2a);
13217  free_number (t0);
13218  free_number (t1);
13219  free_number (t2);
13220  free_number (u0);
13221  free_number (u1);
13222  free_number (v0);
13223  free_number (v1);
13224  free_number (t);
13225  return c;
13226}
13227
13228
13229@ We shall want to keep track of where certain knots on the cyclic path
13230wind up in the envelope spec.  It doesn't suffice just to keep pointers to
13231knot nodes because some nodes are deleted while removing dead cubics.  Thus
13232|offset_prep| updates the following pointers
13233
13234@<Glob...@>=
13235mp_knot spec_p1;
13236mp_knot spec_p2;        /* pointers to distinguished knots */
13237
13238@ @<Set init...@>=
13239mp->spec_p1 = NULL;
13240mp->spec_p2 = NULL;
13241
13242@ @<Initialize the pen size~|n|@>=
13243n = 0;
13244p = h;
13245do {
13246  incr (n);
13247  p = mp_next_knot (p);
13248} while (p != h)
13249
13250@ Since the true incoming direction isn't known yet, we just pick a direction
13251consistent with the pen offset~|h|.  If this is wrong, it can be corrected
13252later.
13253
13254@<Initialize the incoming direction and pen offset at |c|@>=
13255{
13256  mp_knot hn = mp_next_knot (h);
13257  mp_knot hp = mp_prev_knot (h);
13258  set_number_from_substraction(dxin, hn->x_coord, hp->x_coord);
13259  set_number_from_substraction(dyin, hn->y_coord, hp->y_coord);
13260  if (number_zero(dxin) && number_zero(dyin)) {
13261    set_number_from_substraction(dxin, hp->y_coord, h->y_coord);
13262    set_number_from_substraction(dyin, h->x_coord, hp->x_coord);
13263  }
13264}
13265w0 = h
13266
13267@ We must be careful not to remove the only cubic in a cycle.
13268
13269But we must also be careful for another reason. If the user-supplied
13270path starts with a set of degenerate cubics, the target node |q| can
13271be collapsed to the initial node |p| which might be the same as the
13272initial node |c| of the curve. This would cause the |offset_prep| routine
13273to bail out too early, causing distress later on. (See for example
13274the testcase reported by Bogus\l{}aw Jackowski in tracker id 267, case 52c
13275on Sarovar.)
13276
13277@<Advance |p| to node |q|, removing any ``dead'' cubics...@>=
13278q0 = q;
13279do {
13280  r = mp_next_knot (p);
13281  if (number_equal (p->x_coord, p->right_x) &&
13282      number_equal (p->y_coord, p->right_y) &&
13283      number_equal (p->x_coord, r->left_x) &&
13284      number_equal (p->y_coord, r->left_y) &&
13285      number_equal (p->x_coord, r->x_coord) &&
13286      number_equal (p->y_coord, r->y_coord) &&
13287      r != p && r != q) {
13288    @<Remove the cubic following |p| and update the data structures
13289        to merge |r| into |p|@>;
13290  }
13291  p = r;
13292} while (p != q);
13293/* Check if we removed too much */
13294if ((q != q0) && (q != c || c == c0))
13295  q = mp_next_knot (q)
13296
13297
13298@ @<Remove the cubic following |p| and update the data structures...@>=
13299{
13300  k_needed = mp_knot_info (p) - zero_off;
13301  if (r == q) {
13302    q = p;
13303  } else {
13304    mp_knot_info (p) = k_needed + mp_knot_info (r);
13305    k_needed = 0;
13306  }
13307  if (r == c) {
13308    mp_knot_info (p) = mp_knot_info (c);
13309    c = p;
13310  }
13311  if (r == mp->spec_p1)
13312    mp->spec_p1 = p;
13313  if (r == mp->spec_p2)
13314    mp->spec_p2 = p;
13315  r = p;
13316  mp_remove_cubic (mp, p);
13317}
13318
13319
13320@ Not setting the |info| field of the newly created knot allows the splitting
13321routine to work for paths.
13322
13323@<Declarations@>=
13324static void mp_split_cubic (MP mp, mp_knot p, mp_number t);
13325
13326@ @c
13327void mp_split_cubic (MP mp, mp_knot p, mp_number t) {                               /* splits the cubic after |p| */
13328  mp_number v;     /* an intermediate value */
13329  mp_knot q, r; /* for list manipulation */
13330  q = mp_next_knot (p);
13331  r = mp_new_knot (mp);
13332  mp_next_knot (p) = r;
13333  mp_next_knot (r) = q;
13334  mp_originator (r) = mp_program_code;
13335  mp_left_type (r) = mp_explicit;
13336  mp_right_type (r) = mp_explicit;
13337  new_number(v);
13338  set_number_from_of_the_way (v,          t, p->right_x, q->left_x);
13339  set_number_from_of_the_way (p->right_x, t, p->x_coord, p->right_x);
13340  set_number_from_of_the_way (q->left_x,  t, q->left_x, q->x_coord);
13341  set_number_from_of_the_way (r->left_x,  t, p->right_x, v);
13342  set_number_from_of_the_way (r->right_x, t, v, q->left_x);
13343  set_number_from_of_the_way (r->x_coord, t, r->left_x, r->right_x);
13344  set_number_from_of_the_way (v,          t, p->right_y, q->left_y);
13345  set_number_from_of_the_way (p->right_y, t, p->y_coord, p->right_y);
13346  set_number_from_of_the_way (q->left_y,  t, q->left_y, q->y_coord);
13347  set_number_from_of_the_way (r->left_y,  t, p->right_y, v);
13348  set_number_from_of_the_way (r->right_y, t, v, q->left_y);
13349  set_number_from_of_the_way (r->y_coord, t, r->left_y, r->right_y);
13350  free_number (v);
13351}
13352
13353
13354@ This does not set |mp_knot_info(p)| or |mp_right_type(p)|.
13355
13356@<Declarations@>=
13357static void mp_remove_cubic (MP mp, mp_knot p);
13358
13359@ @c
13360void mp_remove_cubic (MP mp, mp_knot p) {                               /* removes the dead cubic following~|p| */
13361  mp_knot q;    /* the node that disappears */
13362  (void) mp;
13363  q = mp_next_knot (p);
13364  mp_next_knot (p) = mp_next_knot (q);
13365  number_clone (p->right_x, q->right_x);
13366  number_clone (p->right_y, q->right_y);
13367  mp_xfree (q);
13368}
13369
13370
13371@ Let $d\prec d'$ mean that the counter-clockwise angle from $d$ to~$d'$ is
13372strictly between zero and $180^\circ$.  Then we can define $d\preceq d'$ to
13373mean that the angle could be zero or $180^\circ$. If $w_k=(u_k,v_k)$ is the
13374$k$th pen offset, the $k$th pen edge direction is defined by the formula
13375$$d_k=(u\k-u_k,\,v\k-v_k).$$
13376When listed by increasing $k$, these directions occur in counter-clockwise
13377order so that $d_k\preceq d\k$ for all~$k$.
13378The goal of |offset_prep| is to find an offset index~|k| to associate with
13379each cubic, such that the direction $d(t)$ of the cubic satisfies
13380$$d_{k-1}\preceq d(t)\preceq d_k\qquad\hbox{for $0\le t\le 1$.}\eqno(*)$$
13381We may have to split a cubic into many pieces before each
13382piece corresponds to a unique offset.
13383
13384@<Split the cubic between |p| and |q|, if necessary, into cubics...@>=
13385mp_knot_info (p) = zero_off + k_needed;
13386k_needed = 0;
13387@<Prepare for derivative computations;
13388  |goto not_found| if the current cubic is dead@>;
13389@<Find the initial direction |(dx,dy)|@>;
13390@<Update |mp_knot_info(p)| and find the offset $w_k$ such that
13391  $d_{k-1}\preceq(\\{dx},\\{dy})\prec d_k$; also advance |w0| for
13392  the direction change at |p|@>;
13393@<Find the final direction |(dxin,dyin)|@>;
13394@<Decide on the net change in pen offsets and set |turn_amt|@>;
13395@<Complete the offset splitting process@>;
13396w0 = mp_pen_walk (mp, w0, turn_amt)
13397
13398
13399@ @<Declarations@>=
13400static mp_knot mp_pen_walk (MP mp, mp_knot w, integer k);
13401
13402@ @c
13403mp_knot mp_pen_walk (MP mp, mp_knot w, integer k) {
13404  /* walk |k| steps around a pen from |w| */
13405  (void) mp;
13406  while (k > 0) {
13407    w = mp_next_knot (w);
13408    decr (k);
13409  }
13410  while (k < 0) {
13411    w = mp_prev_knot (w);
13412    incr (k);
13413  }
13414  return w;
13415}
13416
13417
13418@ The direction of a cubic $B(z_0,z_1,z_2,z_3;t)=\bigl(x(t),y(t)\bigr)$ can be
13419calculated from the quadratic polynomials
13420${1\over3}x'(t)=B(x_1-x_0,x_2-x_1,x_3-x_2;t)$ and
13421${1\over3}y'(t)=B(y_1-y_0,y_2-y_1,y_3-y_2;t)$.
13422Since we may be calculating directions from several cubics
13423split from the current one, it is desirable to do these calculations
13424without losing too much precision. ``Scaled up'' values of the
13425derivatives, which will be less tainted by accumulated errors than
13426derivatives found from the cubics themselves, are maintained in
13427local variables |x0|, |x1|, and |x2|, representing $X_0=2^l(x_1-x_0)$,
13428$X_1=2^l(x_2-x_1)$, and $X_2=2^l(x_3-x_2)$; similarly |y0|, |y1|, and~|y2|
13429represent $Y_0=2^l(y_1-y_0)$, $Y_1=2^l(y_2-y_1)$, and $Y_2=2^l(y_3-y_2)$.
13430
13431@<Other local variables for |offset_prep|@>=
13432mp_number x0, x1, x2, y0, y1, y2; /* representatives of derivatives */
13433mp_number t0, t1, t2;     /* coefficients of polynomial for slope testing */
13434mp_number du, dv, dx, dy; /* for directions of the pen and the curve */
13435mp_number dx0, dy0;       /* initial direction for the first cubic in the curve */
13436mp_number x0a, x1a, x2a, y0a, y1a, y2a;   /* intermediate values */
13437mp_number t;     /* where the derivative passes through zero */
13438mp_number s;     /* a temporary value */
13439
13440@ @<Prepare for derivative computations...@>=
13441set_number_from_substraction(x0, p->right_x, p->x_coord);
13442set_number_from_substraction(x2, q->x_coord, q->left_x);
13443set_number_from_substraction(x1, q->left_x, p->right_x);
13444set_number_from_substraction(y0, p->right_y, p->y_coord);
13445set_number_from_substraction(y2, q->y_coord, q->left_y);
13446set_number_from_substraction(y1, q->left_y, p->right_y);
13447{
13448  mp_number absval;
13449  new_number (absval);
13450  number_clone(absval, x1);
13451  number_abs(absval);
13452  number_clone(max_coef, x0);
13453  number_abs (max_coef);
13454  if (number_greater(absval, max_coef)) {
13455    number_clone(max_coef, absval);
13456  }
13457  number_clone(absval, x2);
13458  number_abs(absval);
13459  if (number_greater(absval, max_coef)) {
13460    number_clone(max_coef, absval);
13461  }
13462  number_clone(absval, y0);
13463  number_abs(absval);
13464  if (number_greater(absval, max_coef)) {
13465    number_clone(max_coef, absval);
13466  }
13467  number_clone(absval, y1);
13468  number_abs(absval);
13469  if (number_greater(absval, max_coef)) {
13470    number_clone(max_coef, absval);
13471  }
13472  number_clone(absval, y2);
13473  number_abs(absval);
13474  if (number_greater(absval, max_coef)) {
13475    number_clone(max_coef, absval);
13476  }
13477  if (number_zero(max_coef)) {
13478    goto NOT_FOUND;
13479  }
13480  free_number (absval);
13481}
13482while (number_less(max_coef, fraction_half_t)) {
13483  number_double (max_coef);
13484  number_double (x0);
13485  number_double (x1);
13486  number_double (x2);
13487  number_double (y0);
13488  number_double (y1);
13489  number_double (y2);
13490}
13491
13492
13493@ Let us first solve a special case of the problem: Suppose we
13494know an index~$k$ such that either (i)~$d(t)\succeq d_{k-1}$ for all~$t$
13495and $d(0)\prec d_k$, or (ii)~$d(t)\preceq d_k$ for all~$t$ and
13496$d(0)\succ d_{k-1}$.
13497Then, in a sense, we're halfway done, since one of the two relations
13498in $(*)$ is satisfied, and the other couldn't be satisfied for
13499any other value of~|k|.
13500
13501Actually, the conditions can be relaxed somewhat since a relation such as
13502$d(t)\succeq d_{k-1}$ restricts $d(t)$ to a half plane when all that really
13503matters is whether $d(t)$ crosses the ray in the $d_{k-1}$ direction from
13504the origin.  The condition for case~(i) becomes $d_{k-1}\preceq d(0)\prec d_k$
13505and $d(t)$ never crosses the $d_{k-1}$ ray in the clockwise direction.
13506Case~(ii) is similar except $d(t)$ cannot cross the $d_k$ ray in the
13507counterclockwise direction.
13508
13509The |fin_offset_prep| subroutine solves the stated subproblem.
13510It has a parameter called |rise| that is |1| in
13511case~(i), |-1| in case~(ii). Parameters |x0| through |y2| represent
13512the derivative of the cubic following |p|.
13513The |w| parameter should point to offset~$w_k$ and |mp_info(p)| should already
13514be set properly.  The |turn_amt| parameter gives the absolute value of the
13515overall net change in pen offsets.
13516
13517@<Declarations@>=
13518static void mp_fin_offset_prep (MP mp, mp_knot p, mp_knot w, mp_number
13519                                x0, mp_number x1, mp_number x2, mp_number y0,
13520                                mp_number y1, mp_number y2, integer rise,
13521                                integer turn_amt);
13522
13523@ @c
13524void mp_fin_offset_prep (MP mp, mp_knot p, mp_knot w, mp_number
13525                         x0, mp_number x1, mp_number x2, mp_number y0, mp_number y1,
13526                         mp_number y2, integer rise, integer turn_amt) {
13527  mp_knot ww;   /* for list manipulation */
13528  mp_number du, dv;        /* for slope calculation */
13529  mp_number t0, t1, t2;   /* test coefficients */
13530  mp_number t;   /* place where the derivative passes a critical slope */
13531  mp_number s;   /* slope or reciprocal slope */
13532  mp_number v;    /* intermediate value for updating |x0..y2| */
13533  mp_knot q;    /* original |mp_next_knot(p)| */
13534  q = mp_next_knot (p);
13535  new_number(du);
13536  new_number(dv);
13537  new_number(v);
13538  new_number(t0);
13539  new_number(t1);
13540  new_number(t2);
13541  new_fraction(s);
13542  new_fraction(t);
13543  while (1) {
13544    if (rise > 0)
13545      ww = mp_next_knot (w);    /* a pointer to $w\k$ */
13546    else
13547      ww = mp_prev_knot (w);    /* a pointer to $w_{k-1}$ */
13548    @<Compute test coefficients |(t0,t1,t2)|
13549      for $d(t)$ versus $d_k$ or $d_{k-1}$@>;
13550    crossing_point (t, t0, t1, t2);
13551    if (number_greaterequal(t, fraction_one_t)) {
13552      if (turn_amt > 0)
13553        number_clone(t, fraction_one_t);
13554      else
13555        goto RETURN;
13556    }
13557    @<Split the cubic at $t$,
13558      and split off another cubic if the derivative crosses back@>;
13559    w = ww;
13560  }
13561RETURN:
13562  free_number (s);
13563  free_number (t);
13564  free_number (du);
13565  free_number (dv);
13566  free_number (v);
13567  free_number (t0);
13568  free_number (t1);
13569  free_number (t2);
13570}
13571
13572
13573@ We want $B(\\{t0},\\{t1},\\{t2};t)$ to be the dot product of $d(t)$ with a
13574$-90^\circ$ rotation of the vector from |w| to |ww|.  This makes the resulting
13575function cross from positive to negative when $d_{k-1}\preceq d(t)\preceq d_k$
13576begins to fail.
13577
13578@<Compute test coefficients |(t0,t1,t2)| for $d(t)$ versus...@>=
13579{
13580  mp_number abs_du, abs_dv;
13581  new_number (abs_du);
13582  new_number (abs_dv);
13583  set_number_from_substraction(du, ww->x_coord, w->x_coord);
13584  set_number_from_substraction(dv, ww->y_coord, w->y_coord);
13585  number_clone(abs_du, du);
13586  number_abs(abs_du);
13587  number_clone(abs_dv, dv);
13588  number_abs(abs_dv);
13589  if (number_greaterequal(abs_du, abs_dv)) {
13590    mp_number r1;
13591    new_fraction (r1);
13592    make_fraction (s, dv, du);
13593    take_fraction (r1, x0, s);
13594    set_number_from_substraction(t0, r1, y0);
13595    take_fraction (r1, x1, s);
13596    set_number_from_substraction(t1, r1, y1);
13597    take_fraction (r1, x2, s);
13598    set_number_from_substraction(t2, r1, y2);
13599    if (number_negative(du)) {
13600      number_negate (t0);
13601      number_negate (t1);
13602      number_negate (t2);
13603    }
13604    free_number (r1);
13605  } else {
13606    mp_number r1;
13607    new_fraction (r1);
13608    make_fraction (s, du, dv);
13609    take_fraction (r1, y0, s);
13610    set_number_from_substraction(t0, x0, r1);
13611    take_fraction (r1, y1, s);
13612    set_number_from_substraction(t1, x1, r1);
13613    take_fraction (r1, y2, s);
13614    set_number_from_substraction(t2, x2, r1);
13615    if (number_negative(dv)) {
13616      number_negate (t0);
13617      number_negate (t1);
13618      number_negate (t2);
13619    }
13620    free_number (r1);
13621  }
13622  free_number (abs_du);
13623  free_number (abs_dv);
13624  if (number_negative(t0))
13625    set_number_to_zero(t0); /* should be positive without rounding error */
13626}
13627
13628
13629@ The curve has crossed $d_k$ or $d_{k-1}$; its initial segment satisfies
13630$(*)$, and it might cross again and return towards $s_{k-1}$ or $s_k$,
13631respectively, yielding another solution of $(*)$.
13632
13633@<Split the cubic at $t$, and split off another...@>=
13634{
13635  mp_split_cubic (mp, p, t);
13636  p = mp_next_knot (p);
13637  mp_knot_info (p) = zero_off + rise;
13638  decr (turn_amt);
13639  set_number_from_of_the_way(v,  t, x0, x1);
13640  set_number_from_of_the_way(x1, t, x1, x2);
13641  set_number_from_of_the_way(x0, t, v, x1);
13642  set_number_from_of_the_way(v,  t, y0, y1);
13643  set_number_from_of_the_way(y1, t, y1, y2);
13644  set_number_from_of_the_way(y0, t, v, y1);
13645  if (turn_amt < 0) {
13646    mp_number arg1, arg2, arg3;
13647    new_number (arg1);
13648    new_number (arg2);
13649    new_number (arg3);
13650    set_number_from_of_the_way(t1, t, t1, t2);
13651    if (number_positive(t1))
13652      set_number_to_zero(t1);  /* without rounding error, |t1| would be |<=0| */
13653    number_clone(arg2, t1);
13654    number_negate(arg2);
13655    number_clone(arg3, t2);
13656    number_negate(arg3);
13657    crossing_point (t, arg1, arg2, arg3);
13658    free_number (arg1);
13659    free_number (arg2);
13660    free_number (arg3);
13661    if (number_greater(t, fraction_one_t))
13662      number_clone(t, fraction_one_t);
13663    incr (turn_amt);
13664    if (number_equal(t,fraction_one_t) && (mp_next_knot (p) != q)) {
13665      mp_knot_info (mp_next_knot (p)) = mp_knot_info (mp_next_knot (p)) - rise;
13666    } else {
13667      mp_split_cubic (mp, p, t);
13668      mp_knot_info (mp_next_knot (p)) = zero_off - rise;
13669      set_number_from_of_the_way(v,  t, x1, x2);
13670      set_number_from_of_the_way(x1, t, x0, x1);
13671      set_number_from_of_the_way(x2, t, x1, v);
13672      set_number_from_of_the_way(v,  t, y1, y2);
13673      set_number_from_of_the_way(y1, t, y0, y1);
13674      set_number_from_of_the_way(y2, t, y1, v);
13675    }
13676  }
13677}
13678
13679
13680@ Now we must consider the general problem of |offset_prep|, when
13681nothing is known about a given cubic. We start by finding its
13682direction in the vicinity of |t=0|.
13683
13684If $z'(t)=0$, the given cubic is numerically unstable but |offset_prep|
13685has not yet introduced any more numerical errors.  Thus we can compute
13686the true initial direction for the given cubic, even if it is almost
13687degenerate.
13688
13689@<Find the initial direction |(dx,dy)|@>=
13690number_clone(dx, x0);
13691number_clone(dy, y0);
13692if (number_zero(dx) && number_zero(dy)) {
13693  number_clone(dx, x1);
13694  number_clone(dy, y1);
13695  if (number_zero(dx) && number_zero(dy)) {
13696    number_clone(dx, x2);
13697    number_clone(dy, y2);
13698  }
13699}
13700if (p == c) {
13701  number_clone(dx0, dx);
13702  number_clone(dy0, dy);
13703}
13704
13705@ @<Find the final direction |(dxin,dyin)|@>=
13706number_clone(dxin, x2);
13707number_clone(dyin, y2);
13708if (number_zero(dxin) && number_zero(dyin)) {
13709  number_clone(dxin, x1);
13710  number_clone(dyin, y1);
13711  if (number_zero(dxin) && number_zero(dyin)) {
13712    number_clone(dxin, x0);
13713    number_clone(dyin, y0);
13714  }
13715}
13716
13717@ The next step is to bracket the initial direction between consecutive
13718edges of the pen polygon.  We must be careful to turn clockwise only if
13719this makes the turn less than $180^\circ$. (A $180^\circ$ turn must be
13720counter-clockwise in order to make \&{doublepath} envelopes come out
13721@:double_path_}{\&{doublepath} primitive@>
13722right.) This code depends on |w0| being the offset for |(dxin,dyin)|.
13723
13724@<Update |mp_knot_info(p)| and find the offset $w_k$ such that...@>=
13725{
13726  mp_number ab_vs_cd;
13727  new_number (ab_vs_cd);
13728  ab_vs_cd (ab_vs_cd, dy, dxin, dx, dyin);
13729  turn_amt = mp_get_turn_amt (mp, w0, dx, dy, number_nonnegative(ab_vs_cd));
13730  free_number (ab_vs_cd);
13731  w = mp_pen_walk (mp, w0, turn_amt);
13732  w0 = w;
13733  mp_knot_info (p) = mp_knot_info (p) + turn_amt;
13734}
13735
13736@ Decide how many pen offsets to go away from |w| in order to find the offset
13737for |(dx,dy)|, going counterclockwise if |ccw| is |true|.  This assumes that
13738|w| is the offset for some direction $(x',y')$ from which the angle to |(dx,dy)|
13739in the sense determined by |ccw| is less than or equal to $180^\circ$.
13740
13741If the pen polygon has only two edges, they could both be parallel
13742to |(dx,dy)|.  In this case, we must be careful to stop after crossing the first
13743such edge in order to avoid an infinite loop.
13744
13745@<Declarations@>=
13746static integer mp_get_turn_amt (MP mp, mp_knot w, mp_number dx,
13747                                mp_number dy, boolean ccw);
13748
13749@ @c
13750integer mp_get_turn_amt (MP mp, mp_knot w, mp_number dx, mp_number dy, boolean ccw) {
13751  mp_knot ww;   /* a neighbor of knot~|w| */
13752  integer s;    /* turn amount so far */
13753  mp_number t;    /* |ab_vs_cd| result */
13754  mp_number arg1, arg2;
13755  s = 0;
13756  new_number (arg1);
13757  new_number (arg2);
13758  new_number (t);
13759  if (ccw) {
13760    ww = mp_next_knot (w);
13761    do {
13762      set_number_from_substraction (arg1, ww->x_coord, w->x_coord);
13763      set_number_from_substraction (arg2, ww->y_coord, w->y_coord);
13764      ab_vs_cd (t, dy, arg1, dx, arg2);
13765      if (number_negative(t))
13766        break;
13767      incr (s);
13768      w = ww;
13769      ww = mp_next_knot (ww);
13770    } while (number_positive(t));
13771  } else {
13772    ww = mp_prev_knot (w);
13773    set_number_from_substraction (arg1, w->x_coord, ww->x_coord);
13774    set_number_from_substraction (arg2, w->y_coord, ww->y_coord);
13775    ab_vs_cd (t, dy, arg1, dx, arg2);
13776    while (number_negative(t)) {
13777      decr (s);
13778      w = ww;
13779      ww = mp_prev_knot (ww);
13780      set_number_from_substraction (arg1, w->x_coord, ww->x_coord);
13781      set_number_from_substraction (arg2, w->y_coord, ww->y_coord);
13782      ab_vs_cd (t, dy, arg1, dx, arg2);
13783    }
13784  }
13785  free_number (t);
13786  free_number (arg1);
13787  free_number (arg2);
13788  return s;
13789}
13790
13791
13792@ When we're all done, the final offset is |w0| and the final curve direction
13793is |(dxin,dyin)|.  With this knowledge of the incoming direction at |c|, we
13794can correct |mp_info(c)| which was erroneously based on an incoming offset
13795of~|h|.
13796
13797@d fix_by(A) mp_knot_info(c)=mp_knot_info(c)+(A)
13798
13799@<Fix the offset change in |mp_knot_info(c)| and set |c| to the return value of...@>=
13800mp->spec_offset = mp_knot_info (c) - zero_off;
13801if (mp_next_knot (c) == c) {
13802mp_knot_info (c) = zero_off + n;
13803} else {
13804  mp_number ab_vs_cd;
13805  new_number (ab_vs_cd);
13806  fix_by (k_needed);
13807  while (w0 != h) {
13808    fix_by (1);
13809    w0 = mp_next_knot (w0);
13810  }
13811  while (mp_knot_info (c) <= zero_off - n)
13812    fix_by (n);
13813  while (mp_knot_info (c) > zero_off)
13814    fix_by (-n);
13815  ab_vs_cd (ab_vs_cd, dy0, dxin, dx0, dyin);
13816  if ((mp_knot_info (c) != zero_off)   && number_nonnegative(ab_vs_cd))
13817    fix_by (n);
13818  free_number (ab_vs_cd);
13819}
13820
13821
13822@ Finally we want to reduce the general problem to situations that
13823|fin_offset_prep| can handle. We split the cubic into at most three parts
13824with respect to $d_{k-1}$, and apply |fin_offset_prep| to each part.
13825
13826@<Complete the offset splitting process@>=
13827ww = mp_prev_knot (w);
13828@<Compute test coeff...@>;
13829@<Find the first |t| where $d(t)$ crosses $d_{k-1}$ or set
13830  |t:=fraction_one+1|@>;
13831if (number_greater(t, fraction_one_t)) {
13832  mp_fin_offset_prep (mp, p, w, x0, x1, x2, y0, y1, y2, 1, turn_amt);
13833} else {
13834  mp_split_cubic (mp, p, t);
13835  r = mp_next_knot (p);
13836  set_number_from_of_the_way(x1a, t, x0, x1);
13837  set_number_from_of_the_way(x1,  t, x1, x2);
13838  set_number_from_of_the_way(x2a, t, x1a, x1);
13839  set_number_from_of_the_way(y1a, t, y0, y1);
13840  set_number_from_of_the_way(y1,  t, y1, y2);
13841  set_number_from_of_the_way(y2a, t, y1a, y1);
13842  mp_fin_offset_prep (mp, p, w, x0, x1a, x2a, y0, y1a, y2a, 1, 0);
13843  number_clone(x0, x2a);
13844  number_clone(y0, y2a);
13845  mp_knot_info (r) = zero_off - 1;
13846  if (turn_amt >= 0) {
13847    mp_number arg1, arg2, arg3;
13848    new_number(arg1);
13849    new_number(arg2);
13850    new_number(arg3);
13851    set_number_from_of_the_way(t1, t, t1, t2);
13852    if (number_positive(t1))
13853      set_number_to_zero(t1);
13854    number_clone(arg2, t1);
13855    number_negate(arg2);
13856    number_clone(arg3, t2);
13857    number_negate(arg3);
13858    crossing_point (t, arg1, arg2, arg3);
13859    free_number (arg1);
13860    free_number (arg2);
13861    free_number (arg3);
13862    if (number_greater(t, fraction_one_t))
13863      number_clone (t, fraction_one_t);
13864    @<Split off another rising cubic for |fin_offset_prep|@>;
13865    mp_fin_offset_prep (mp, r, ww, x0, x1, x2, y0, y1, y2, -1, 0);
13866  } else {
13867    mp_fin_offset_prep (mp, r, ww, x0, x1, x2, y0, y1, y2, -1, (-1 - turn_amt));
13868  }
13869}
13870
13871
13872@ @<Split off another rising cubic for |fin_offset_prep|@>=
13873mp_split_cubic (mp, r, t);
13874mp_knot_info (mp_next_knot (r)) = zero_off + 1;
13875set_number_from_of_the_way(x1a, t, x1, x2);
13876set_number_from_of_the_way(x1,  t, x0, x1);
13877set_number_from_of_the_way(x0a, t, x1, x1a);
13878set_number_from_of_the_way(y1a, t, y1, y2);
13879set_number_from_of_the_way(y1,  t, y0, y1);
13880set_number_from_of_the_way(y0a, t, y1, y1a);
13881mp_fin_offset_prep (mp, mp_next_knot (r), w, x0a, x1a, x2, y0a, y1a, y2, 1,  turn_amt);
13882number_clone(x2, x0a);
13883number_clone(y2, y0a)
13884
13885@ At this point, the direction of the incoming pen edge is |(-du,-dv)|.
13886When the component of $d(t)$ perpendicular to |(-du,-dv)| crosses zero, we
13887need to decide whether the directions are parallel or antiparallel.  We
13888can test this by finding the dot product of $d(t)$ and |(-du,-dv)|, but this
13889should be avoided when the value of |turn_amt| already determines the
13890answer.  If |t2<0|, there is one crossing and it is antiparallel only if
13891|turn_amt>=0|.  If |turn_amt<0|, there should always be at least one
13892crossing and the first crossing cannot be antiparallel.
13893
13894@<Find the first |t| where $d(t)$ crosses $d_{k-1}$ or set...@>=
13895crossing_point (t, t0, t1, t2);
13896if (turn_amt >= 0) {
13897  if (number_negative(t2)) {
13898    number_clone(t, fraction_one_t);
13899    number_add_scaled (t, 1);
13900  } else {
13901    mp_number tmp, arg1, r1;
13902    new_fraction (r1);
13903    new_number(tmp);
13904    new_number(arg1);
13905    set_number_from_of_the_way(u0, t, x0, x1);
13906    set_number_from_of_the_way(u1, t, x1, x2);
13907    set_number_from_of_the_way(tmp, t, u0, u1);
13908    number_clone (arg1, du);
13909    number_abs (arg1);
13910    take_fraction (ss, arg1, tmp);
13911    set_number_from_of_the_way(v0, t, y0, y1);
13912    set_number_from_of_the_way(v1, t, y1, y2);
13913    set_number_from_of_the_way(tmp, t, v0, v1);
13914    number_clone (arg1, dv);
13915    number_abs (arg1);
13916    take_fraction (r1, arg1, tmp);
13917    number_add (ss, r1);
13918    free_number (tmp);
13919    if (number_negative(ss)) {
13920      number_clone(t, fraction_one_t);
13921      number_add_scaled (t, 1);
13922    }
13923    free_number(arg1);
13924    free_number(r1);
13925  }
13926} else if (number_greater(t, fraction_one_t)) {
13927  number_clone (t, fraction_one_t);
13928}
13929
13930@ @<Other local variables for |offset_prep|@>=
13931mp_number u0, u1, v0, v1; /* intermediate values for $d(t)$ calculation */
13932int d_sign;     /* sign of overall change in direction for this cubic */
13933
13934@ If the cubic almost has a cusp, it is a numerically ill-conditioned
13935problem to decide which way it loops around but that's OK as long we're
13936consistent.  To make \&{doublepath} envelopes work properly, reversing
13937the path should always change the sign of |turn_amt|.
13938
13939@<Decide on the net change in pen offsets and set |turn_amt|@>=
13940{
13941  mp_number ab_vs_cd;
13942  new_number (ab_vs_cd);
13943  ab_vs_cd (ab_vs_cd, dx, dyin, dxin, dy);
13944  if (number_negative (ab_vs_cd))
13945    d_sign = -1;
13946  else if (number_zero (ab_vs_cd))
13947    d_sign = 0;
13948  else
13949    d_sign = 1;
13950  free_number (ab_vs_cd);
13951}
13952if (d_sign == 0) {
13953  @<Check rotation direction based on node position@>
13954}
13955if (d_sign == 0) {
13956  if (number_zero(dx)) {
13957    if (number_positive(dy))
13958      d_sign = 1;
13959    else
13960      d_sign = -1;
13961  } else {
13962    if (number_positive(dx))
13963      d_sign = 1;
13964    else
13965      d_sign = -1;
13966  }
13967}
13968@<Make |ss| negative if and only if the total change in direction is
13969  more than $180^\circ$@>;
13970turn_amt = mp_get_turn_amt (mp, w, dxin, dyin, (d_sign > 0));
13971if (number_negative(ss))
13972  turn_amt = turn_amt - d_sign * n
13973
13974@ We check rotation direction by looking at the vector connecting the current
13975node with the next. If its angle with incoming and outgoing tangents has the
13976same sign, we pick this as |d_sign|, since it means we have a flex, not a cusp.
13977Otherwise we proceed to the cusp code.
13978
13979@<Check rotation direction based on node position@>=
13980{
13981  mp_number ab_vs_cd1, ab_vs_cd2, t;
13982  new_number (ab_vs_cd1);
13983  new_number (ab_vs_cd2);
13984  new_number (t);
13985  set_number_from_substraction(u0, q->x_coord, p->x_coord);
13986  set_number_from_substraction(u1, q->y_coord, p->y_coord);
13987  ab_vs_cd (ab_vs_cd1, dx, u1, u0, dy);
13988  ab_vs_cd (ab_vs_cd2, u0, dyin, dxin, u1);
13989  set_number_from_addition (t, ab_vs_cd1, ab_vs_cd2);
13990  number_half (t);
13991  if (number_negative (t))
13992    d_sign = -1;
13993  else if (number_zero (t))
13994    d_sign = 0;
13995  else
13996    d_sign = 1;
13997  free_number (t);
13998  free_number (ab_vs_cd1);
13999  free_number (ab_vs_cd2);
14000}
14001
14002@ In order to be invariant under path reversal, the result of this computation
14003should not change when |x0|, |y0|, $\ldots$ are all negated and |(x0,y0)| is
14004then swapped with |(x2,y2)|.  We make use of the identities
14005|take_fraction(-a,-b)=take_fraction(a,b)| and
14006|t_of_the_way(-a,-b)=-(t_of_the_way(a,b))|.
14007
14008@<Make |ss| negative if and only if the total change in direction is...@>=
14009{
14010  mp_number r1, r2, arg1;
14011  new_number (arg1);
14012  new_fraction (r1);
14013  new_fraction (r2);
14014  take_fraction (r1, x0, y2);
14015  take_fraction (r2, x2, y0);
14016  number_half (r1);
14017  number_half (r2);
14018  set_number_from_substraction(t0, r1, r2);
14019  set_number_from_addition (arg1, y0, y2);
14020  take_fraction (r1, x1, arg1);
14021  set_number_from_addition (arg1, x0, x2);
14022  take_fraction (r1, y1, arg1);
14023  number_half (r1);
14024  number_half (r2);
14025  set_number_from_substraction(t1, r1, r2);
14026  free_number (arg1);
14027  free_number (r1);
14028  free_number (r2);
14029}
14030if (number_zero(t0))
14031  set_number_from_scaled(t0, d_sign);                  /* path reversal always negates |d_sign| */
14032if (number_positive(t0)) {
14033  mp_number arg3;
14034  new_number(arg3);
14035  number_clone(arg3, t0);
14036  number_negate(arg3);
14037  crossing_point (t, t0, t1, arg3);
14038  free_number (arg3);
14039  set_number_from_of_the_way(u0, t, x0, x1);
14040  set_number_from_of_the_way(u1, t, x1, x2);
14041  set_number_from_of_the_way(v0, t, y0, y1);
14042  set_number_from_of_the_way(v1, t, y1, y2);
14043} else {
14044  mp_number arg1;
14045  new_number(arg1);
14046  number_clone(arg1, t0);
14047  number_negate(arg1);
14048  crossing_point (t, arg1, t1, t0);
14049  free_number (arg1);
14050  set_number_from_of_the_way(u0, t, x2, x1);
14051  set_number_from_of_the_way(u1, t, x1, x0);
14052  set_number_from_of_the_way(v0, t, y2, y1);
14053  set_number_from_of_the_way(v1, t, y1, y0);
14054}
14055{
14056  mp_number tmp1, tmp2, r1, r2, arg1;
14057  new_fraction (r1);
14058  new_fraction (r2);
14059  new_number(arg1);
14060  new_number(tmp1);
14061  new_number(tmp2);
14062  set_number_from_of_the_way(tmp1, t, u0, u1);
14063  set_number_from_of_the_way(tmp2, t, v0, v1);
14064  set_number_from_addition(arg1, x0, x2);
14065  take_fraction (r1, arg1, tmp1);
14066  set_number_from_addition(arg1, y0, y2);
14067  take_fraction (r2, arg1, tmp2);
14068  set_number_from_addition (ss, r1, r2);
14069  free_number (arg1);
14070  free_number (r1);
14071  free_number (r2);
14072  free_number (tmp1);
14073  free_number (tmp2);
14074}
14075
14076
14077@ Here's a routine that prints an envelope spec in symbolic form.  It assumes
14078that the |cur_pen| has not been walked around to the first offset.
14079
14080@c
14081static void mp_print_spec (MP mp, mp_knot cur_spec, mp_knot cur_pen,
14082                           const char *s) {
14083  mp_knot p, q; /* list traversal */
14084  mp_knot w;    /* the current pen offset */
14085  mp_print_diagnostic (mp, "Envelope spec", s, true);
14086  p = cur_spec;
14087  w = mp_pen_walk (mp, cur_pen, mp->spec_offset);
14088  mp_print_ln (mp);
14089  mp_print_two (mp, cur_spec->x_coord, cur_spec->y_coord);
14090  mp_print (mp, " % beginning with offset ");
14091  mp_print_two (mp, w->x_coord, w->y_coord);
14092  do {
14093    while (1) {
14094      q = mp_next_knot (p);
14095      @<Print the cubic between |p| and |q|@>;
14096      p = q;
14097      if ((p == cur_spec) || (mp_knot_info (p) != zero_off))
14098        break;
14099    }
14100    if (mp_knot_info (p) != zero_off) {
14101      @<Update |w| as indicated by |mp_knot_info(p)| and print an explanation@>;
14102    }
14103  } while (p != cur_spec);
14104  mp_print_nl (mp, " & cycle");
14105  mp_end_diagnostic (mp, true);
14106}
14107
14108
14109@ @<Update |w| as indicated by |mp_knot_info(p)| and print an explanation@>=
14110{
14111  w = mp_pen_walk (mp, w, (mp_knot_info (p) - zero_off));
14112  mp_print (mp, " % ");
14113  if (mp_knot_info (p) > zero_off)
14114    mp_print (mp, "counter");
14115  mp_print (mp, "clockwise to offset ");
14116  mp_print_two (mp, w->x_coord, w->y_coord);
14117}
14118
14119
14120@ @<Print the cubic between |p| and |q|@>=
14121{
14122  mp_print_nl (mp, "   ..controls ");
14123  mp_print_two (mp, p->right_x,  p->right_y);
14124  mp_print (mp, " and ");
14125  mp_print_two (mp, q->left_x, q->left_y);
14126  mp_print_nl (mp, " ..");
14127  mp_print_two (mp, q->x_coord, q->y_coord);
14128}
14129
14130
14131@ Once we have an envelope spec, the remaining task to construct the actual
14132envelope by offsetting each cubic as determined by the |info| fields in
14133the knots.  First we use |offset_prep| to convert the |c| into an envelope
14134spec. Then we add the offsets so that |c| becomes a cyclic path that represents
14135the envelope.
14136
14137The |ljoin| and |miterlim| parameters control the treatment of points where the
14138pen offset changes, and |lcap| controls the endpoints of a \&{doublepath}.
14139The endpoints are easily located because |c| is given in undoubled form
14140and then doubled in this procedure.  We use |spec_p1| and |spec_p2| to keep
14141track of the endpoints and treat them like very sharp corners.
14142Butt end caps are treated like beveled joins; round end caps are treated like
14143round joins; and square end caps are achieved by setting |join_type:=3|.
14144
14145None of these parameters apply to inside joins where the convolution tracing
14146has retrograde lines.  In such cases we use a simple connect-the-endpoints
14147approach that is achieved by setting |join_type:=2|.
14148
14149@c
14150static mp_knot mp_make_envelope (MP mp, mp_knot c, mp_knot h, quarterword ljoin,
14151                                 quarterword lcap, mp_number miterlim) {
14152  mp_knot p, q, r, q0;  /* for manipulating the path */
14153  mp_knot w, w0;        /* the pen knot for the current offset */
14154  halfword k, k0;       /* controls pen edge insertion */
14155  mp_number qx, qy;        /* unshifted coordinates of |q| */
14156  mp_fraction dxin, dyin, dxout, dyout;      /* directions at |q| when square or mitered */
14157  int join_type = 0;    /* codes |0..3| for mitered, round, beveled, or square */
14158  @<Other local variables for |make_envelope|@>;
14159  new_number (max_ht);
14160  new_number (tmp);
14161  new_fraction(dxin);
14162  new_fraction(dyin);
14163  new_fraction(dxout);
14164  new_fraction(dyout);
14165  mp->spec_p1 = NULL;
14166  mp->spec_p2 = NULL;
14167  new_number(qx);
14168  new_number(qy);
14169  @<If endpoint, double the path |c|, and set |spec_p1| and |spec_p2|@>;
14170  @<Use |offset_prep| to compute the envelope spec then walk |h| around to
14171    the initial offset@>;
14172  w = h;
14173  p = c;
14174  do {
14175    q = mp_next_knot (p);
14176    q0 = q;
14177    number_clone (qx, q->x_coord);
14178    number_clone (qy, q->y_coord);
14179    k = mp_knot_info (q);
14180    k0 = k;
14181    w0 = w;
14182    if (k != zero_off) {
14183      @<Set |join_type| to indicate how to handle offset changes at~|q|@>;
14184    }
14185    @<Add offset |w| to the cubic from |p| to |q|@>;
14186    while (k != zero_off) {
14187      @<Step |w| and move |k| one step closer to |zero_off|@>;
14188      if ((join_type == 1) || (k == zero_off)) {
14189        mp_number xtot, ytot;
14190        new_number(xtot);
14191        new_number(ytot);
14192        set_number_from_addition (xtot, qx, w->x_coord);
14193        set_number_from_addition (ytot, qy, w->y_coord);
14194        q = mp_insert_knot (mp, q, xtot, ytot);
14195      }
14196    }
14197    if (q != mp_next_knot (p)) {
14198      @<Set |p=mp_link(p)| and add knots between |p| and |q| as
14199        required by |join_type|@>;
14200    }
14201    p = q;
14202  } while (q0 != c);
14203  free_number (max_ht);
14204  free_number (tmp);
14205  free_number (qx);
14206  free_number (qy);
14207  free_number (dxin);
14208  free_number (dyin);
14209  free_number (dxout);
14210  free_number (dyout);
14211  return c;
14212}
14213
14214
14215@ @<Use |offset_prep| to compute the envelope spec then walk |h| around to...@>=
14216c = mp_offset_prep (mp, c, h);
14217if (number_positive(internal_value (mp_tracing_specs)))
14218  mp_print_spec (mp, c, h, "");
14219h = mp_pen_walk (mp, h, mp->spec_offset)
14220
14221
14222@ Mitered and squared-off joins depend on path directions that are difficult to
14223compute for degenerate cubics.  The envelope spec computed by |offset_prep| can
14224have degenerate cubics only if the entire cycle collapses to a single
14225degenerate cubic.  Setting |join_type:=2| in this case makes the computed
14226envelope degenerate as well.
14227
14228@<Set |join_type| to indicate how to handle offset changes at~|q|@>=
14229if (k < zero_off) {
14230  join_type = 2;
14231} else {
14232  if ((q != mp->spec_p1) && (q != mp->spec_p2))
14233    join_type = ljoin;
14234  else if (lcap == 2)
14235    join_type = 3;
14236  else
14237    join_type = 2 - lcap;
14238  if ((join_type == 0) || (join_type == 3)) {
14239    @<Set the incoming and outgoing directions at |q|; in case of
14240      degeneracy set |join_type:=2|@>;
14241    if (join_type == 0) {
14242      @<If |miterlim| is less than the secant of half the angle at |q|
14243        then set |join_type:=2|@>;
14244    }
14245  }
14246}
14247
14248
14249@ @<If |miterlim| is less than the secant of half the angle at |q|...@>=
14250{
14251  mp_number r1, r2;
14252  new_fraction (r1);
14253  new_fraction (r2);
14254  take_fraction (r1, dxin, dxout);
14255  take_fraction (r2, dyin, dyout);
14256  number_add (r1, r2);
14257  number_half (r1);
14258  number_add (r1, fraction_half_t);
14259  take_fraction (tmp, miterlim, r1);
14260  if (number_less(tmp, unity_t)) {
14261    mp_number ret;
14262    new_number (ret);
14263    take_scaled (ret, miterlim, tmp);
14264    if (number_less(ret, unity_t))
14265      join_type = 2;
14266    free_number (ret);
14267  }
14268  free_number (r1);
14269  free_number (r2);
14270}
14271
14272
14273@ @<Other local variables for |make_envelope|@>=
14274mp_number tmp;     /* a temporary value */
14275
14276@ The coordinates of |p| have already been shifted unless |p| is the first
14277knot in which case they get shifted at the very end.
14278
14279@<Add offset |w| to the cubic from |p| to |q|@>=
14280number_add (p->right_x, w->x_coord);
14281number_add (p->right_y, w->y_coord);
14282number_add (q->left_x,  w->x_coord);
14283number_add (q->left_y,  w->y_coord);
14284number_add (q->x_coord, w->x_coord);
14285number_add (q->y_coord, w->y_coord);
14286mp_left_type (q) = mp_explicit;
14287mp_right_type (q) = mp_explicit
14288
14289@ @<Step |w| and move |k| one step closer to |zero_off|@>=
14290if (k > zero_off) {
14291  w = mp_next_knot (w);
14292  decr (k);
14293} else {
14294  w = mp_prev_knot (w);
14295  incr (k);
14296}
14297
14298
14299@ The cubic from |q| to the new knot at |(x,y)| becomes a line segment and
14300the |mp_right_x| and |mp_right_y| fields of |r| are set from |q|.  This is done in
14301case the cubic containing these control points is ``yet to be examined.''
14302
14303@<Declarations@>=
14304static mp_knot mp_insert_knot (MP mp, mp_knot q, mp_number x, mp_number y);
14305
14306@ @c
14307mp_knot mp_insert_knot (MP mp, mp_knot q, mp_number x, mp_number y) {
14308  /* returns the inserted knot */
14309  mp_knot r;    /* the new knot */
14310  r = mp_new_knot (mp);
14311  mp_next_knot (r) = mp_next_knot (q);
14312  mp_next_knot (q) = r;
14313  number_clone (r->right_x, q->right_x);
14314  number_clone (r->right_y, q->right_y);
14315  number_clone (r->x_coord, x);
14316  number_clone (r->y_coord, y);
14317  number_clone (q->right_x, q->x_coord);
14318  number_clone (q->right_y, q->y_coord);
14319  number_clone (r->left_x, r->x_coord);
14320  number_clone (r->left_y, r->y_coord);
14321  mp_left_type (r) = mp_explicit;
14322  mp_right_type (r) = mp_explicit;
14323  mp_originator (r) = mp_program_code;
14324  return r;
14325}
14326
14327
14328@ After setting |p:=mp_link(p)|, either |join_type=1| or |q=mp_link(p)|.
14329
14330@<Set |p=mp_link(p)| and add knots between |p| and |q| as...@>=
14331{
14332  p = mp_next_knot (p);
14333  if ((join_type == 0) || (join_type == 3)) {
14334    if (join_type == 0) {
14335      @<Insert a new knot |r| between |p| and |q| as required for a mitered join@>
14336    } else {
14337      @<Make |r| the last of two knots inserted between |p| and |q| to form a
14338        squared join@>;
14339    }
14340    if (r != NULL) {
14341      number_clone (r->right_x, r->x_coord);
14342      number_clone (r->right_y, r->y_coord);
14343    }
14344  }
14345}
14346
14347
14348@ For very small angles, adding a knot is unnecessary and would cause numerical
14349problems, so we just set |r:=NULL| in that case.
14350
14351@d near_zero_angle_k ((math_data *)mp->math)->near_zero_angle_t
14352
14353@<Insert a new knot |r| between |p| and |q| as required for a mitered join@>=
14354{
14355  mp_number det;   /* a determinant used for mitered join calculations */
14356  mp_number absdet;
14357  mp_number r1, r2;
14358  new_fraction (r1);
14359  new_fraction (r2);
14360  new_fraction (det);
14361  new_fraction (absdet);
14362  take_fraction (r1, dyout, dxin);
14363  take_fraction (r2, dxout, dyin);
14364  set_number_from_substraction(det, r1, r2);
14365  number_clone (absdet, det);
14366  number_abs (absdet);
14367  if (number_less (absdet, near_zero_angle_k)) {
14368    r = NULL;                   /* sine $<10^{-4}$ */
14369  } else {
14370    mp_number xtot, ytot, xsub, ysub;
14371    new_fraction(xsub);
14372    new_fraction(ysub);
14373    new_number(xtot);
14374    new_number(ytot);
14375    set_number_from_substraction (tmp, q->x_coord, p->x_coord);
14376    take_fraction (r1, tmp, dyout);
14377    set_number_from_substraction (tmp, q->y_coord, p->y_coord);
14378    take_fraction (r2, tmp, dxout);
14379    set_number_from_substraction (tmp, r1, r2);
14380    make_fraction (r1, tmp, det);
14381    number_clone (tmp, r1);
14382    take_fraction (xsub, tmp, dxin);
14383    take_fraction (ysub, tmp, dyin);
14384    set_number_from_addition(xtot, p->x_coord, xsub);
14385    set_number_from_addition(ytot, p->y_coord, ysub);
14386    r = mp_insert_knot (mp, p, xtot, ytot);
14387    free_number (xtot);
14388    free_number (ytot);
14389    free_number (xsub);
14390    free_number (ysub);
14391  }
14392  free_number (r1);
14393  free_number (r2);
14394  free_number (det);
14395  free_number (absdet);
14396}
14397
14398
14399@ @<Make |r| the last of two knots inserted between |p| and |q| to form a...@>=
14400{
14401  mp_number ht_x, ht_y;    /* perpendicular to the segment from |p| to |q| */
14402  mp_number ht_x_abs, ht_y_abs;    /* absolutes */
14403  mp_number xtot, ytot, xsub, ysub;
14404  new_fraction(xsub);
14405  new_fraction(ysub);
14406  new_number(xtot);
14407  new_number(ytot);
14408  new_fraction (ht_x);
14409  new_fraction (ht_y);
14410  new_fraction (ht_x_abs);
14411  new_fraction (ht_y_abs);
14412  set_number_from_substraction(ht_x, w->y_coord, w0->y_coord);
14413  set_number_from_substraction(ht_y, w0->x_coord, w->x_coord);
14414  number_clone (ht_x_abs, ht_x);
14415  number_clone (ht_y_abs, ht_y);
14416  number_abs (ht_x_abs);
14417  number_abs (ht_y_abs);
14418  while (number_less(ht_x_abs, fraction_half_t) && number_less(ht_y_abs, fraction_half_t)) {
14419    number_double(ht_x);
14420    number_double(ht_y);
14421    number_clone (ht_x_abs, ht_x);
14422    number_clone (ht_y_abs, ht_y);
14423    number_abs (ht_x_abs);
14424    number_abs (ht_y_abs);
14425  }
14426  @<Scan the pen polygon between |w0| and |w| and make |max_ht| the range dot
14427    product with |(ht_x,ht_y)|@>;
14428  {
14429    mp_number  r1 ,r2;
14430    new_fraction (r1);
14431    new_fraction (r2);
14432    take_fraction (r1, dxin, ht_x);
14433    take_fraction (r2, dyin, ht_y);
14434    number_add (r1, r2);
14435    make_fraction (tmp, max_ht, r1);
14436    free_number (r1);
14437    free_number (r2);
14438  }
14439  take_fraction (xsub, tmp, dxin);
14440  take_fraction (ysub, tmp, dyin);
14441  set_number_from_addition(xtot, p->x_coord, xsub);
14442  set_number_from_addition(ytot, p->y_coord, ysub);
14443  r = mp_insert_knot (mp, p, xtot, ytot);
14444  /* clang: value never read */ assert(r);
14445  {
14446    mp_number  r1 ,r2;
14447    new_fraction (r1);
14448    new_fraction (r2);
14449    take_fraction (r1, dxout, ht_x);
14450    take_fraction (r2, dyout, ht_y);
14451    number_add (r1, r2);
14452    make_fraction (tmp, max_ht, r1);
14453    free_number (r1);
14454    free_number (r2);
14455  }
14456  take_fraction (xsub, tmp, dxout);
14457  take_fraction (ysub, tmp, dyout);
14458  set_number_from_addition(xtot, q->x_coord, xsub);
14459  set_number_from_addition(ytot, q->y_coord, ysub);
14460  r = mp_insert_knot (mp, p, xtot, ytot);
14461  free_number (xsub);
14462  free_number (ysub);
14463  free_number (xtot);
14464  free_number (ytot);
14465  free_number (ht_x);
14466  free_number (ht_y);
14467  free_number (ht_x_abs);
14468  free_number (ht_y_abs);
14469}
14470
14471
14472@ @<Other local variables for |make_envelope|@>=
14473mp_number max_ht;  /* maximum height of the pen polygon above the |w0|-|w| line */
14474halfword kk;    /* keeps track of the pen vertices being scanned */
14475mp_knot ww;     /* the pen vertex being tested */
14476
14477@ The dot product of the vector from |w0| to |ww| with |(ht_x,ht_y)| ranges
14478from zero to |max_ht|.
14479
14480@<Scan the pen polygon between |w0| and |w| and make |max_ht| the range...@>=
14481set_number_to_zero (max_ht);
14482kk = zero_off;
14483ww = w;
14484while (1) {
14485  @<Step |ww| and move |kk| one step closer to |k0|@>;
14486  if (kk == k0)
14487    break;
14488  {
14489    mp_number r1, r2;
14490    new_fraction (r1);
14491    new_fraction (r2);
14492    set_number_from_substraction (tmp, ww->x_coord, w0->x_coord);
14493    take_fraction (r1, tmp, ht_x);
14494    set_number_from_substraction (tmp, ww->y_coord, w0->y_coord);
14495    take_fraction (r2, tmp, ht_y);
14496    set_number_from_addition (tmp, r1,  r2);
14497    free_number (r1);
14498    free_number (r2);
14499  }
14500  if (number_greater(tmp, max_ht))
14501    number_clone(max_ht, tmp);
14502}
14503
14504
14505@ @<Step |ww| and move |kk| one step closer to |k0|@>=
14506if (kk > k0) {
14507  ww = mp_next_knot (ww);
14508  decr (kk);
14509} else {
14510  ww = mp_prev_knot (ww);
14511  incr (kk);
14512}
14513
14514
14515@ @<If endpoint, double the path |c|, and set |spec_p1| and |spec_p2|@>=
14516if (mp_left_type (c) == mp_endpoint) {
14517  mp->spec_p1 = mp_htap_ypoc (mp, c);
14518  mp->spec_p2 = mp->path_tail;
14519  mp_originator (mp->spec_p1) = mp_program_code;
14520  mp_next_knot (mp->spec_p2) = mp_next_knot (mp->spec_p1);
14521  mp_next_knot (mp->spec_p1) = c;
14522  mp_remove_cubic (mp, mp->spec_p1);
14523  c = mp->spec_p1;
14524  if (c != mp_next_knot (c)) {
14525    mp_originator (mp->spec_p2) = mp_program_code;
14526    mp_remove_cubic (mp, mp->spec_p2);
14527  } else {
14528    @<Make |c| look like a cycle of length one@>;
14529  }
14530}
14531
14532@ @<Make |c| look like a cycle of length one@>=
14533{
14534  mp_left_type (c) = mp_explicit;
14535  mp_right_type (c) = mp_explicit;
14536  number_clone(c->left_x, c->x_coord);
14537  number_clone(c->left_y, c->y_coord);
14538  number_clone(c->right_x, c->x_coord);
14539  number_clone(c->right_y, c->y_coord);
14540}
14541
14542
14543@ In degenerate situations we might have to look at the knot preceding~|q|.
14544That knot is |p| but if |p<>c|, its coordinates have already been offset by |w|.
14545
14546@<Set the incoming and outgoing directions at |q|; in case of...@>=
14547{
14548  set_number_from_substraction(dxin, q->x_coord, q->left_x);
14549  set_number_from_substraction(dyin, q->y_coord, q->left_y);
14550  if (number_zero(dxin) && number_zero(dyin)) {
14551    set_number_from_substraction(dxin, q->x_coord, p->right_x);
14552    set_number_from_substraction(dyin, q->y_coord, p->right_y);
14553    if (number_zero(dxin) && number_zero(dyin)) {
14554      set_number_from_substraction(dxin, q->x_coord, p->x_coord);
14555      set_number_from_substraction(dyin, q->y_coord, p->y_coord);
14556      if (p != c) {                 /* the coordinates of |p| have been offset by |w| */
14557        number_add(dxin, w->x_coord);
14558        number_add(dyin, w->y_coord);
14559      }
14560    }
14561  }
14562  pyth_add (tmp, dxin, dyin);
14563  if (number_zero(tmp)) {
14564    join_type = 2;
14565  } else {
14566    mp_number r1;
14567    new_fraction (r1);
14568    make_fraction (r1, dxin, tmp);
14569    number_clone(dxin, r1);
14570    make_fraction (r1, dyin, tmp);
14571    number_clone(dyin, r1);
14572    free_number (r1);
14573    @<Set the outgoing direction at |q|@>;
14574  }
14575}
14576
14577
14578@ If |q=c| then the coordinates of |r| and the control points between |q|
14579and~|r| have already been offset by |h|.
14580
14581@<Set the outgoing direction at |q|@>=
14582{
14583  set_number_from_substraction(dxout, q->right_x, q->x_coord);
14584  set_number_from_substraction(dyout, q->right_y, q->y_coord);
14585  if (number_zero(dxout) && number_zero(dyout)) {
14586    r = mp_next_knot (q);
14587    set_number_from_substraction(dxout, r->left_x, q->x_coord);
14588    set_number_from_substraction(dyout, r->left_y, q->y_coord);
14589    if (number_zero(dxout) && number_zero(dyout)) {
14590      set_number_from_substraction(dxout, r->x_coord, q->x_coord);
14591      set_number_from_substraction(dyout, r->y_coord, q->y_coord);
14592    }
14593  }
14594  if (q == c) {
14595    number_substract(dxout, h->x_coord);
14596    number_substract(dyout, h->y_coord);
14597  }
14598  pyth_add (tmp, dxout, dyout);
14599  if (number_zero(tmp)) {
14600    /* |mp_confusion (mp, "degenerate spec");| */
14601@:this can't happen degerate spec}{\quad degenerate spec@>;
14602    /* But apparently, it actually can happen. The test case is this:
14603
14604  path p;
14605  linejoin := mitered;
14606  p:= (10,0)..(0,10)..(-10,0)..(0,-10)..cycle;
14607  addto currentpicture contour p withpen pensquare;
14608
14609  The reason for failure here is the addition of |r != q| in revision 1757
14610  in ``Advance |p| to node |q|, removing any ``dead'' cubics'', which itself
14611  was needed to fix a bug with disappearing knots in a path that was rotated
14612  exactly 45 degrees (luatex.org bug 530).
14613     */
14614  } else {
14615    mp_number r1;
14616    new_fraction (r1);
14617    make_fraction (r1, dxout, tmp);
14618    number_clone(dxout, r1);
14619    make_fraction (r1, dyout, tmp);
14620    number_clone(dyout, r1);
14621    free_number (r1);
14622  }
14623}
14624
14625
14626@* Direction and intersection times.
14627A path of length $n$ is defined parametrically by functions $x(t)$ and
14628$y(t)$, for |0<=t<=n|; we can regard $t$ as the ``time'' at which the path
14629reaches the point $\bigl(x(t),y(t)\bigr)$.  In this section of the program
14630we shall consider operations that determine special times associated with
14631given paths: the first time that a path travels in a given direction, and
14632a pair of times at which two paths cross each other.
14633
14634@ Let's start with the easier task. The function |find_direction_time| is
14635given a direction |(x,y)| and a path starting at~|h|. If the path never
14636travels in direction |(x,y)|, the direction time will be~|-1|; otherwise
14637it will be nonnegative.
14638
14639Certain anomalous cases can arise: If |(x,y)=(0,0)|, so that the given
14640direction is undefined, the direction time will be~0. If $\bigl(x'(t),
14641y'(t)\bigr)=(0,0)$, so that the path direction is undefined, it will be
14642assumed to match any given direction at time~|t|.
14643
14644The routine solves this problem in nondegenerate cases by rotating the path
14645and the given direction so that |(x,y)=(1,0)|; i.e., the main task will be
14646to find when a given path first travels ``due east.''
14647
14648@c
14649static void mp_find_direction_time (MP mp, mp_number *ret, mp_number x_orig, mp_number y_orig, mp_knot h) {
14650  mp_number max;   /* $\max\bigl(\vert x\vert,\vert y\vert\bigr)$ */
14651  mp_knot p, q; /* for list traversal */
14652  mp_number n;     /* the direction time at knot |p| */
14653  mp_number tt;    /* the direction time within a cubic */
14654  mp_number x, y;
14655  mp_number abs_x, abs_y;
14656  /* Other local variables for |find_direction_time| */
14657  mp_number x1, x2, x3, y1, y2, y3;  /* multiples of rotated derivatives */
14658  mp_number phi;       /* angles of exit and entry at a knot */
14659  mp_number t;     /* temp storage */
14660  mp_number ab_vs_cd;
14661  new_number(max);
14662  new_number(x1);
14663  new_number(x2);
14664  new_number(x3);
14665  new_number(y1);
14666  new_number(y2);
14667  new_number(y3);
14668  new_fraction(t);
14669  new_angle(phi);
14670  new_number (ab_vs_cd);
14671  set_number_to_zero (*ret); /* just in case */
14672  new_number (x);
14673  new_number (y);
14674  new_number (abs_x);
14675  new_number (abs_y);
14676  new_number (n);
14677  new_fraction (tt);
14678  number_clone (x, x_orig);
14679  number_clone (y, y_orig);
14680  number_clone (abs_x, x_orig);
14681  number_clone (abs_y, y_orig);
14682  number_abs (abs_x);
14683  number_abs (abs_y);
14684  /* Normalize the given direction for better accuracy;
14685     but |return| with zero result if it's zero */
14686  if (number_less(abs_x, abs_y)) {
14687    mp_number r1;
14688    new_fraction (r1);
14689    make_fraction (r1, x, abs_y);
14690    number_clone(x, r1);
14691    free_number (r1);
14692    if (number_positive(y)) {
14693      number_clone(y, fraction_one_t);
14694    } else {
14695      number_clone(y, fraction_one_t);
14696      number_negate(y);
14697    }
14698  } else if (number_zero(x)) {
14699    goto FREE;
14700  } else {
14701    mp_number r1;
14702    new_fraction (r1);
14703    make_fraction (r1, y, abs_x);
14704    number_clone(y, r1);
14705    free_number (r1);
14706    if (number_positive(x)) {
14707      number_clone(x, fraction_one_t);
14708    } else {
14709      number_clone(x, fraction_one_t);
14710      number_negate(x);
14711    }
14712  }
14713
14714  p = h;
14715  while (1) {
14716    if (mp_right_type (p) == mp_endpoint)
14717      break;
14718    q = mp_next_knot (p);
14719    @<Rotate the cubic between |p| and |q|; then
14720      |goto found| if the rotated cubic travels due east at some time |tt|;
14721      but |break| if an entire cyclic path has been traversed@>;
14722    p = q;
14723    number_add(n, unity_t);
14724  }
14725  set_number_to_unity (*ret);
14726  number_negate(*ret);
14727  goto FREE;
14728FOUND:
14729  set_number_from_addition (*ret, n, tt);
14730  goto FREE;
14731FREE:
14732  free_number (x);
14733  free_number (y);
14734  free_number (abs_x);
14735  free_number (abs_y);
14736  /* Free local variables for |find_direction_time| */
14737  free_number (x1);
14738  free_number (x2);
14739  free_number (x3);
14740  free_number (y1);
14741  free_number (y2);
14742  free_number (y3);
14743  free_number (t);
14744  free_number (phi);
14745  free_number (ab_vs_cd);
14746
14747  free_number (n);
14748  free_number (max);
14749  free_number (tt);
14750}
14751
14752
14753
14754@ Since we're interested in the tangent directions, we work with the
14755derivative $${1\over3}B'(x_0,x_1,x_2,x_3;t)=
14756B(x_1-x_0,x_2-x_1,x_3-x_2;t)$$ instead of
14757$B(x_0,x_1,x_2,x_3;t)$ itself. The derived coefficients are also scale-d up
14758in order to achieve better accuracy.
14759
14760The given path may turn abruptly at a knot, and it might pass the critical
14761tangent direction at such a time. Therefore we remember the direction |phi|
14762in which the previous rotated cubic was traveling. (The value of |phi| will be
14763undefined on the first cubic, i.e., when |n=0|.)
14764
14765@d we_found_it {
14766  number_clone (tt, t);
14767  fraction_to_round_scaled (tt);
14768  goto FOUND;
14769}
14770
14771@<Rotate the cubic between |p| and |q|; then...@>=
14772set_number_to_zero(tt);
14773/* Set local variables |x1,x2,x3| and |y1,y2,y3| to multiples of the control
14774   points of the rotated derivatives */
14775{
14776  mp_number absval;
14777  new_number (absval);
14778  set_number_from_substraction(x1, p->right_x, p->x_coord);
14779  set_number_from_substraction(x2, q->left_x,  p->right_x);
14780  set_number_from_substraction(x3, q->x_coord, q->left_x);
14781  set_number_from_substraction(y1, p->right_y, p->y_coord);
14782  set_number_from_substraction(y2, q->left_y,  p->right_y);
14783  set_number_from_substraction(y3, q->y_coord, q->left_y);
14784  number_clone(absval, x2);
14785  number_abs(absval);
14786  number_clone(max, x1);
14787  number_abs(max);
14788  if (number_greater(absval, max)) {
14789    number_clone(max, absval);
14790  }
14791  number_clone(absval, x3);
14792  number_abs(absval);
14793  if (number_greater(absval, max)) {
14794    number_clone(max, absval);
14795  }
14796  number_clone(absval, y1);
14797  number_abs(absval);
14798  if (number_greater(absval, max)) {
14799    number_clone(max, absval);
14800  }
14801  number_clone(absval, y2);
14802  number_abs(absval);
14803  if (number_greater(absval, max)) {
14804    number_clone(max, absval);
14805  }
14806  number_clone(absval, y3);
14807  number_abs(absval);
14808  if (number_greater(absval, max)) {
14809    number_clone(max, absval);
14810  }
14811  free_number (absval);
14812  if (number_zero(max))
14813    goto FOUND;
14814  while (number_less (max, fraction_half_t)) {
14815    number_double(max);
14816    number_double(x1);
14817    number_double(x2);
14818    number_double(x3);
14819    number_double(y1);
14820    number_double(y2);
14821    number_double(y3);
14822  }
14823  number_clone(t, x1);
14824  {
14825     mp_number r1, r2;
14826     new_fraction (r1);
14827     new_fraction (r2);
14828     take_fraction (r1, x1, x);
14829     take_fraction (r2, y1, y);
14830     set_number_from_addition(x1, r1, r2);
14831     take_fraction (r1, y1, x);
14832     take_fraction (r2, t, y);
14833     set_number_from_substraction(y1, r1, r2);
14834     number_clone(t, x2);
14835     take_fraction (r1, x2, x);
14836     take_fraction (r2, y2, y);
14837     set_number_from_addition(x2, r1, r2);
14838     take_fraction (r1, y2, x);
14839     take_fraction (r2, t, y);
14840     set_number_from_substraction(y2, r1, r2);
14841     number_clone(t, x3);
14842     take_fraction (r1, x3 ,x);
14843     take_fraction (r2, y3, y);
14844     set_number_from_addition(x3, r1, r2);
14845     take_fraction (r1, y3, x);
14846     take_fraction (r2, t, y);
14847     set_number_from_substraction(y3, r1, r2);
14848     free_number (r1);
14849     free_number (r2);
14850  }
14851}
14852if (number_zero(y1))
14853  if (number_zero(x1) || number_positive(x1))
14854    goto FOUND;
14855if (number_positive(n)) {
14856  /* Exit to |found| if an eastward direction occurs at knot |p| */
14857  mp_number theta;
14858  mp_number tmp;
14859  new_angle (theta);
14860  n_arg (theta, x1, y1);
14861  new_angle (tmp);
14862  set_number_from_substraction (tmp, theta, one_eighty_deg_t);
14863
14864  if (number_nonnegative(theta) && number_nonpositive(phi) && number_greaterequal(phi, tmp)) {
14865    free_number (tmp);
14866    free_number (theta);
14867    goto FOUND;
14868  }
14869  set_number_from_addition (tmp, theta, one_eighty_deg_t);
14870  if (number_nonpositive(theta) && number_nonnegative(phi) && number_lessequal(phi, tmp)) {
14871    free_number (tmp);
14872    free_number (theta);
14873    goto FOUND;
14874  }
14875  free_number (tmp);
14876  free_number (theta);
14877
14878  if (p == h)
14879    break;
14880}
14881if (number_nonzero(x3) || number_nonzero(y3)) {
14882  n_arg (phi, x3, y3);
14883}
14884/* Exit to |found| if the curve whose derivatives are specified by
14885   |x1,x2,x3,y1,y2,y3| travels eastward at some time~|tt| */
14886/* In this step we want to use the |crossing_point| routine to find the
14887roots of the quadratic equation $B(y_1,y_2,y_3;t)=0$.
14888Several complications arise: If the quadratic equation has a double root,
14889the curve never crosses zero, and |crossing_point| will find nothing;
14890this case occurs iff $y_1y_3=y_2^2$ and $y_1y_2<0$. If the quadratic
14891equation has simple roots, or only one root, we may have to negate it
14892so that $B(y_1,y_2,y_3;t)$ crosses from positive to negative at its first root.
14893And finally, we need to do special things if $B(y_1,y_2,y_3;t)$ is
14894identically zero. */
14895if (number_negative(x1))
14896  if (number_negative(x2))
14897    if (number_negative(x3))
14898      goto DONE;
14899{
14900  ab_vs_cd (ab_vs_cd, y1, y3, y2, y2);
14901  if (number_zero(ab_vs_cd)) {
14902    /* Handle the test for eastward directions when $y_1y_3=y_2^2$;
14903      either |goto found| or |goto done| */
14904{
14905  ab_vs_cd (ab_vs_cd, y1, y2, zero_t, zero_t);
14906  if (number_negative(ab_vs_cd)) {
14907    mp_number tmp, arg2;
14908    new_number(tmp);
14909    new_number(arg2);
14910    set_number_from_substraction (arg2, y1, y2);
14911    make_fraction (t, y1, arg2);
14912    free_number (arg2);
14913    set_number_from_of_the_way(x1, t, x1, x2);
14914    set_number_from_of_the_way(x2, t, x2, x3);
14915    set_number_from_of_the_way(tmp, t, x1, x2);
14916    if (number_zero(tmp) || number_positive(tmp)) {
14917      free_number (tmp);
14918      we_found_it;
14919    }
14920    free_number (tmp);
14921  } else if (number_zero(y3)) {
14922    if (number_zero(y1)) {
14923      /* Exit to |found| if the derivative $B(x_1,x_2,x_3;t)$ becomes |>=0| */
14924/* At this point we know that the derivative of |y(t)| is identically zero,
14925and that |x1<0|; but either |x2>=0| or |x3>=0|, so there's some hope of
14926traveling east. */
14927{
14928  mp_number arg1, arg2, arg3;
14929  new_number (arg1);
14930  new_number (arg2);
14931  new_number (arg3);
14932  number_clone(arg1, x1);
14933  number_negate(arg1);
14934  number_clone(arg2, x2);
14935  number_negate(arg2);
14936  number_clone(arg3, x3);
14937  number_negate(arg3);
14938  crossing_point (t, arg1, arg2, arg3);
14939  free_number (arg1);
14940  free_number (arg2);
14941  free_number (arg3);
14942  if (number_lessequal (t, fraction_one_t))
14943    we_found_it;
14944  ab_vs_cd (ab_vs_cd, x1, x3, x2, x2);
14945  if (number_nonpositive(ab_vs_cd)) {
14946    mp_number arg2;
14947    new_number (arg2);
14948    set_number_from_substraction (arg2, x1, x2);
14949    make_fraction (t, x1, arg2);
14950    free_number (arg2);
14951    we_found_it;
14952  }
14953}
14954
14955
14956
14957    } else if (number_zero(x3) || number_positive(x3)) {
14958      set_number_to_unity(tt);
14959      goto FOUND;
14960    }
14961  }
14962  goto DONE;
14963}
14964
14965
14966  }
14967}
14968if (number_zero(y1) || number_negative(y1)) {
14969  if (number_negative(y1)) {
14970    number_negate(y1);
14971    number_negate(y2);
14972    number_negate(y3);
14973  } else if (number_positive(y2)) {
14974    number_negate(y2);
14975    number_negate(y3);
14976  }
14977}
14978/* Check the places where $B(y_1,y_2,y_3;t)=0$ to see if
14979  $B(x_1,x_2,x_3;t)\ge0$ */
14980/* The quadratic polynomial $B(y_1,y_2,y_3;t)$ begins |>=0| and has at most
14981two roots, because we know that it isn't identically zero.
14982
14983It must be admitted that the |crossing_point| routine is not perfectly accurate;
14984rounding errors might cause it to find a root when $y_1y_3>y_2^2$, or to
14985miss the roots when $y_1y_3<y_2^2$. The rotation process is itself
14986subject to rounding errors. Yet this code optimistically tries to
14987do the right thing.
14988
14989*/
14990crossing_point (t, y1, y2, y3);
14991if (number_greater (t, fraction_one_t))
14992  goto DONE;
14993set_number_from_of_the_way(y2, t, y2, y3);
14994set_number_from_of_the_way(x1, t, x1, x2);
14995set_number_from_of_the_way(x2, t, x2, x3);
14996set_number_from_of_the_way(x1, t, x1, x2);
14997if (number_zero(x1) || number_positive(x1))
14998  we_found_it;
14999if (number_positive(y2))
15000  set_number_to_zero(y2);
15001number_clone(tt, t);
15002{
15003  mp_number arg1, arg2, arg3;
15004  new_number (arg1);
15005  new_number (arg2);
15006  new_number (arg3);
15007  number_clone(arg2, y2);
15008  number_negate(arg2);
15009  number_clone(arg3, y3);
15010  number_negate(arg3);
15011  crossing_point (t, arg1, arg2, arg3);
15012  free_number (arg1);
15013  free_number (arg2);
15014  free_number (arg3);
15015}
15016if (number_greater (t, fraction_one_t))
15017  goto DONE;
15018{
15019  mp_number tmp;
15020  new_number(tmp);
15021  set_number_from_of_the_way(x1, t, x1, x2);
15022  set_number_from_of_the_way(x2, t, x2, x3);
15023  set_number_from_of_the_way(tmp, t, x1, x2);
15024  if (number_nonnegative(tmp)) {
15025    free_number (tmp);
15026    set_number_from_of_the_way (t, t, tt, fraction_one_t);
15027    we_found_it;
15028  }
15029  free_number (tmp);
15030}
15031DONE:
15032
15033
15034@ The intersection of two cubics can be found by an interesting variant
15035of the general bisection scheme described in the introduction to
15036|crossing_point|.\
15037Given $w(t)=B(w_0,w_1,w_2,w_3;t)$ and $z(t)=B(z_0,z_1,z_2,z_3;t)$,
15038we wish to find a pair of times $(t_1,t_2)$ such that $w(t_1)=z(t_2)$,
15039if an intersection exists. First we find the smallest rectangle that
15040encloses the points $\{w_0,w_1,w_2,w_3\}$ and check that it overlaps
15041the smallest rectangle that encloses
15042$\{z_0,z_1,z_2,z_3\}$; if not, the cubics certainly don't intersect.
15043But if the rectangles do overlap, we bisect the intervals, getting
15044new cubics $w'$ and~$w''$, $z'$~and~$z''$; the intersection routine first
15045tries for an intersection between $w'$ and~$z'$, then (if unsuccessful)
15046between $w'$ and~$z''$, then (if still unsuccessful) between $w''$ and~$z'$,
15047finally (if thrice unsuccessful) between $w''$ and~$z''$. After $l$~successful
15048levels of bisection we will have determined the intersection times $t_1$
15049and~$t_2$ to $l$~bits of accuracy.
15050
15051\def\submin{_{\rm min}} \def\submax{_{\rm max}}
15052As before, it is better to work with the numbers $W_k=2^l(w_k-w_{k-1})$
15053and $Z_k=2^l(z_k-z_{k-1})$ rather than the coefficients $w_k$ and $z_k$
15054themselves. We also need one other quantity, $\Delta=2^l(w_0-z_0)$,
15055to determine when the enclosing rectangles overlap. Here's why:
15056The $x$~coordinates of~$w(t)$ are between $u\submin$ and $u\submax$,
15057and the $x$~coordinates of~$z(t)$ are between $x\submin$ and $x\submax$,
15058if we write $w_k=(u_k,v_k)$ and $z_k=(x_k,y_k)$ and $u\submin=
15059\min(u_0,u_1,u_2,u_3)$, etc. These intervals of $x$~coordinates
15060overlap if and only if $u\submin\L x\submax$ and
15061$x\submin\L u\submax$. Letting
15062$$U\submin=\min(0,U_1,U_1+U_2,U_1+U_2+U_3),\;
15063  U\submax=\max(0,U_1,U_1+U_2,U_1+U_2+U_3),$$
15064we have $2^lu\submin=2^lu_0+U\submin$, etc.; the condition for overlap
15065reduces to
15066$$X\submin-U\submax\L 2^l(u_0-x_0)\L X\submax-U\submin.$$
15067Thus we want to maintain the quantity $2^l(u_0-x_0)$; similarly,
15068the quantity $2^l(v_0-y_0)$ accounts for the $y$~coordinates. The
15069coordinates of $\Delta=2^l(w_0-z_0)$ must stay bounded as $l$ increases,
15070because of the overlap condition; i.e., we know that $X\submin$,
15071$X\submax$, and their relatives are bounded, hence $X\submax-
15072U\submin$ and $X\submin-U\submax$ are bounded.
15073
15074@ Incidentally, if the given cubics intersect more than once, the process
15075just sketched will not necessarily find the lexicographically smallest pair
15076$(t_1,t_2)$. The solution actually obtained will be smallest in ``shuffled
15077order''; i.e., if $t_1=(.a_1a_2\ldots a_{16})_2$ and
15078$t_2=(.b_1b_2\ldots b_{16})_2$, then we will minimize
15079$a_1b_1a_2b_2\ldots a_{16}b_{16}$, not
15080$a_1a_2\ldots a_{16}b_1b_2\ldots b_{16}$.
15081Shuffled order agrees with lexicographic order if all pairs of solutions
15082$(t_1,t_2)$ and $(t_1',t_2')$ have the property that $t_1<t_1'$ iff
15083$t_2<t_2'$; but in general, lexicographic order can be quite different,
15084and the bisection algorithm would be substantially less efficient if it were
15085constrained by lexicographic order.
15086
15087For example, suppose that an overlap has been found for $l=3$ and
15088$(t_1,t_2)= (.101,.011)$ in binary, but that no overlap is produced by
15089either of the alternatives $(.1010,.0110)$, $(.1010,.0111)$ at level~4.
15090Then there is probably an intersection in one of the subintervals
15091$(.1011,.011x)$; but lexicographic order would require us to explore
15092$(.1010,.1xxx)$ and $(.1011,.00xx)$ and $(.1011,.010x)$ first. We wouldn't
15093want to store all of the subdivision data for the second path, so the
15094subdivisions would have to be regenerated many times. Such inefficiencies
15095would be associated with every `1' in the binary representation of~$t_1$.
15096
15097@ The subdivision process introduces rounding errors, hence we need to
15098make a more liberal test for overlap. It is not hard to show that the
15099computed values of $U_i$ differ from the truth by at most~$l$, on
15100level~$l$, hence $U\submin$ and $U\submax$ will be at most $3l$ in error.
15101If $\beta$ is an upper bound on the absolute error in the computed
15102components of $\Delta=(|delx|,|dely|)$ on level~$l$, we will replace
15103the test `$X\submin-U\submax\L|delx|$' by the more liberal test
15104`$X\submin-U\submax\L|delx|+|tol|$', where $|tol|=6l+\beta$.
15105
15106More accuracy is obtained if we try the algorithm first with |tol=0|;
15107the more liberal tolerance is used only if an exact approach fails.
15108It is convenient to do this double-take by letting `3' in the preceding
15109paragraph be a parameter, which is first 0, then 3.
15110
15111@<Glob...@>=
15112unsigned int tol_step;  /* either 0 or 3, usually */
15113
15114@ We shall use an explicit stack to implement the recursive bisection
15115method described above. The |bisect_stack| array will contain numerous 5-word
15116packets like $(U_1,U_2,U_3,U\submin,U\submax)$, as well as 20-word packets
15117comprising the 5-word packets for $U$, $V$, $X$, and~$Y$.
15118
15119The following macros define the allocation of stack positions to
15120the quantities needed for bisection-intersection.
15121
15122@d stack_1(A) mp->bisect_stack[(A)] /* $U_1$, $V_1$, $X_1$, or $Y_1$ */
15123@d stack_2(A) mp->bisect_stack[(A)+1] /* $U_2$, $V_2$, $X_2$, or $Y_2$ */
15124@d stack_3(A) mp->bisect_stack[(A)+2] /* $U_3$, $V_3$, $X_3$, or $Y_3$ */
15125@d stack_min(A) mp->bisect_stack[(A)+3]
15126  /* $U\submin$, $V\submin$, $X\submin$, or $Y\submin$ */
15127@d stack_max(A) mp->bisect_stack[(A)+4]
15128  /* $U\submax$, $V\submax$, $X\submax$, or $Y\submax$ */
15129@d int_packets 20 /* number of words to represent $U_k$, $V_k$, $X_k$, and $Y_k$ */
15130@#
15131@d u_packet(A) ((A)-5)
15132@d v_packet(A) ((A)-10)
15133@d x_packet(A) ((A)-15)
15134@d y_packet(A) ((A)-20)
15135@d l_packets (mp->bisect_ptr-int_packets)
15136@d r_packets mp->bisect_ptr
15137@d ul_packet u_packet(l_packets) /* base of $U'_k$ variables */
15138@d vl_packet v_packet(l_packets) /* base of $V'_k$ variables */
15139@d xl_packet x_packet(l_packets) /* base of $X'_k$ variables */
15140@d yl_packet y_packet(l_packets) /* base of $Y'_k$ variables */
15141@d ur_packet u_packet(r_packets) /* base of $U''_k$ variables */
15142@d vr_packet v_packet(r_packets) /* base of $V''_k$ variables */
15143@d xr_packet x_packet(r_packets) /* base of $X''_k$ variables */
15144@d yr_packet y_packet(r_packets) /* base of $Y''_k$ variables */
15145@#
15146@d u1l stack_1(ul_packet) /* $U'_1$ */
15147@d u2l stack_2(ul_packet) /* $U'_2$ */
15148@d u3l stack_3(ul_packet) /* $U'_3$ */
15149@d v1l stack_1(vl_packet) /* $V'_1$ */
15150@d v2l stack_2(vl_packet) /* $V'_2$ */
15151@d v3l stack_3(vl_packet) /* $V'_3$ */
15152@d x1l stack_1(xl_packet) /* $X'_1$ */
15153@d x2l stack_2(xl_packet) /* $X'_2$ */
15154@d x3l stack_3(xl_packet) /* $X'_3$ */
15155@d y1l stack_1(yl_packet) /* $Y'_1$ */
15156@d y2l stack_2(yl_packet) /* $Y'_2$ */
15157@d y3l stack_3(yl_packet) /* $Y'_3$ */
15158@d u1r stack_1(ur_packet) /* $U''_1$ */
15159@d u2r stack_2(ur_packet) /* $U''_2$ */
15160@d u3r stack_3(ur_packet) /* $U''_3$ */
15161@d v1r stack_1(vr_packet) /* $V''_1$ */
15162@d v2r stack_2(vr_packet) /* $V''_2$ */
15163@d v3r stack_3(vr_packet) /* $V''_3$ */
15164@d x1r stack_1(xr_packet) /* $X''_1$ */
15165@d x2r stack_2(xr_packet) /* $X''_2$ */
15166@d x3r stack_3(xr_packet) /* $X''_3$ */
15167@d y1r stack_1(yr_packet) /* $Y''_1$ */
15168@d y2r stack_2(yr_packet) /* $Y''_2$ */
15169@d y3r stack_3(yr_packet) /* $Y''_3$ */
15170@#
15171@d stack_dx mp->bisect_stack[mp->bisect_ptr] /* stacked value of |delx| */
15172@d stack_dy mp->bisect_stack[mp->bisect_ptr+1] /* stacked value of |dely| */
15173@d stack_tol mp->bisect_stack[mp->bisect_ptr+2] /* stacked value of |tol| */
15174@d stack_uv mp->bisect_stack[mp->bisect_ptr+3] /* stacked value of |uv| */
15175@d stack_xy mp->bisect_stack[mp->bisect_ptr+4] /* stacked value of |xy| */
15176@d int_increment (int_packets+int_packets+5) /* number of stack words per level */
15177
15178@<Glob...@>=
15179mp_number *bisect_stack;
15180integer bisect_ptr;
15181
15182@ @<Allocate or initialize ...@>=
15183mp->bisect_stack = xmalloc ((bistack_size + 1), sizeof (mp_number));
15184{
15185  int i;
15186  for (i=0;i<bistack_size + 1;i++) {
15187    new_number (mp->bisect_stack[i]);
15188  }
15189}
15190
15191@ @<Dealloc variables@>=
15192{
15193  int i;
15194  for (i=0;i<bistack_size + 1;i++) {
15195    free_number (mp->bisect_stack[i]);
15196  }
15197}
15198xfree (mp->bisect_stack);
15199
15200@ @<Check the ``constant''...@>=
15201if (int_packets + 17 * int_increment > bistack_size)
15202  mp->bad = 19;
15203
15204@ Computation of the min and max is a tedious but fairly fast sequence of
15205instructions; exactly four comparisons are made in each branch.
15206
15207@d set_min_max(A)
15208  debug_number (stack_1(A));
15209  debug_number (stack_3(A));
15210  debug_number (stack_2(A));
15211  debug_number (stack_min(A));
15212  debug_number (stack_max(A));
15213  if ( number_negative(stack_1((A))) ) {
15214    if ( number_nonnegative (stack_3((A))) ) {
15215      if ( number_negative (stack_2((A))) )
15216        set_number_from_addition (stack_min((A)), stack_1((A)), stack_2((A)));
15217      else
15218        number_clone (stack_min((A)), stack_1((A)));
15219      set_number_from_addition (stack_max((A)), stack_1((A)), stack_2((A)));
15220      number_add (stack_max((A)), stack_3((A)));
15221      if ( number_negative (stack_max((A))) )
15222        set_number_to_zero (stack_max((A)));
15223    } else {
15224      set_number_from_addition (stack_min((A)), stack_1((A)), stack_2((A)));
15225      number_add (stack_min((A)), stack_3((A)));
15226      if ( number_greater (stack_min((A)), stack_1((A))))
15227        number_clone (stack_min((A)), stack_1((A)));
15228      set_number_from_addition (stack_max((A)), stack_1((A)), stack_2((A)));
15229      if ( number_negative (stack_max((A))) )
15230        set_number_to_zero (stack_max((A)));
15231    }
15232  } else if ( number_nonpositive (stack_3((A)))) {
15233    if ( number_positive (stack_2((A))) )
15234      set_number_from_addition (stack_max((A)), stack_1((A)), stack_2((A)));
15235    else
15236      number_clone (stack_max((A)), stack_1((A)));
15237    set_number_from_addition (stack_min((A)), stack_1((A)), stack_2((A)));
15238    number_add (stack_min((A)), stack_3((A)));
15239    if ( number_positive (stack_min((A))) )
15240      set_number_to_zero (stack_min((A)));
15241  } else  {
15242    set_number_from_addition (stack_max((A)), stack_1((A)), stack_2((A)));
15243    number_add (stack_max((A)), stack_3((A)));
15244    if ( number_less (stack_max((A)), stack_1((A))))
15245      number_clone (stack_max((A)), stack_1((A)));
15246    set_number_from_addition (stack_min((A)), stack_1((A)), stack_2((A)));
15247    if ( number_positive (stack_min((A))) )
15248      set_number_to_zero (stack_min((A)));
15249  }
15250
15251@ It's convenient to keep the current values of $l$, $t_1$, and $t_2$ in
15252the integer form $2^l+2^lt_1$ and $2^l+2^lt_2$. The |cubic_intersection|
15253routine uses global variables |cur_t| and |cur_tt| for this purpose;
15254after successful completion, |cur_t| and |cur_tt| will contain |unity|
15255plus the |scaled| values of $t_1$ and~$t_2$.
15256
15257The values of |cur_t| and |cur_tt| will be set to zero if |cubic_intersection|
15258finds no intersection. The routine gives up and gives an approximate answer
15259if it has backtracked
15260more than 5000 times (otherwise there are cases where several minutes
15261of fruitless computation would be possible).
15262
15263@d max_patience 5000
15264
15265@<Glob...@>=
15266mp_number cur_t;
15267mp_number cur_tt; /* controls and results of |cubic_intersection| */
15268integer time_to_go;     /* this many backtracks before giving up */
15269mp_number max_t;  /* maximum of $2^{l+1}$ so far achieved */
15270
15271@ @<Initialize table ...@>=
15272new_number (mp->cur_t);
15273new_number (mp->cur_tt);
15274new_number (mp->max_t);
15275
15276@ @<Dealloc ...@>=
15277free_number (mp->cur_t);
15278free_number (mp->cur_tt);
15279free_number (mp->max_t);
15280
15281@ The given cubics $B(w_0,w_1,w_2,w_3;t)$ and
15282$B(z_0,z_1,z_2,z_3;t)$ are specified in adjacent knot nodes |(p,mp_link(p))|
15283and |(pp,mp_link(pp))|, respectively.
15284
15285@d half(A) ((A)/2)
15286
15287@c
15288static void mp_cubic_intersection (MP mp, mp_knot p, mp_knot pp) {
15289  mp_knot q, qq;        /* |mp_link(p)|, |mp_link(pp)| */
15290  mp->time_to_go = max_patience;
15291  set_number_from_scaled (mp->max_t, 2);
15292  @<Initialize for intersections at level zero@>;
15293CONTINUE:
15294  while (1) {
15295    if (number_to_scaled (mp->delx) - mp->tol <=
15296        number_to_scaled (stack_max (x_packet (mp->xy))) - number_to_scaled (stack_min (u_packet (mp->uv))))
15297      if (number_to_scaled (mp->delx) + mp->tol >=
15298          number_to_scaled (stack_min (x_packet (mp->xy))) - number_to_scaled (stack_max (u_packet (mp->uv))))
15299        if (number_to_scaled (mp->dely) - mp->tol <=
15300            number_to_scaled (stack_max (y_packet (mp->xy))) - number_to_scaled (stack_min (v_packet (mp->uv))))
15301          if (number_to_scaled (mp->dely) + mp->tol >=
15302              number_to_scaled (stack_min (y_packet (mp->xy))) - number_to_scaled (stack_max (v_packet (mp->uv)))) {
15303            if (number_to_scaled (mp->cur_t) >= number_to_scaled (mp->max_t)) {
15304              if (number_equal(mp->max_t, two_t)) {   /* we've done 17 bisections */
15305                set_number_from_scaled (mp->cur_t, ((number_to_scaled (mp->cur_t) + 1)/2));
15306                set_number_from_scaled (mp->cur_tt, ((number_to_scaled (mp->cur_tt) + 1)/2));
15307                return;
15308              }
15309              number_double(mp->max_t);
15310              number_clone (mp->appr_t, mp->cur_t);
15311              number_clone (mp->appr_tt, mp->cur_tt);
15312            }
15313            @<Subdivide for a new level of intersection@>;
15314            goto CONTINUE;
15315          }
15316    if (mp->time_to_go > 0) {
15317      decr (mp->time_to_go);
15318    } else {
15319      while (number_less (mp->appr_t, unity_t)) {
15320        number_double(mp->appr_t);
15321        number_double(mp->appr_tt);
15322      }
15323      number_clone (mp->cur_t, mp->appr_t);
15324      number_clone (mp->cur_tt, mp->appr_tt);
15325      return;
15326    }
15327  NOT_FOUND:
15328    /* Advance to the next pair |(cur_t,cur_tt)| */
15329    if (odd (number_to_scaled (mp->cur_tt))) {
15330      if (odd (number_to_scaled (mp->cur_t))) {
15331        /* Descend to the previous level and |goto not_found| */
15332        {
15333          set_number_from_scaled (mp->cur_t, half (number_to_scaled (mp->cur_t)));
15334          set_number_from_scaled (mp->cur_tt, half (number_to_scaled (mp->cur_tt)));
15335          if (number_to_scaled (mp->cur_t) == 0)
15336            return;
15337          mp->bisect_ptr -= int_increment;
15338          mp->three_l -= (integer) mp->tol_step;
15339          number_clone (mp->delx, stack_dx);
15340          number_clone (mp->dely, stack_dy);
15341          mp->tol = number_to_scaled (stack_tol);
15342          mp->uv = number_to_scaled (stack_uv);
15343          mp->xy = number_to_scaled (stack_xy);
15344          goto NOT_FOUND;
15345        }
15346
15347      } else {
15348        set_number_from_scaled (mp->cur_t, number_to_scaled (mp->cur_t) + 1);
15349        number_add (mp->delx, stack_1 (u_packet (mp->uv)));
15350        number_add (mp->delx, stack_2 (u_packet (mp->uv)));
15351        number_add (mp->delx, stack_3 (u_packet (mp->uv)));
15352        number_add (mp->dely, stack_1 (v_packet (mp->uv)));
15353        number_add (mp->dely, stack_2 (v_packet (mp->uv)));
15354        number_add (mp->dely, stack_3 (v_packet (mp->uv)));
15355        mp->uv = mp->uv + int_packets;      /* switch from |l_packets| to |r_packets| */
15356        set_number_from_scaled (mp->cur_tt, number_to_scaled (mp->cur_tt) - 1);
15357        mp->xy = mp->xy - int_packets;
15358        number_add (mp->delx, stack_1 (x_packet (mp->xy)));
15359        number_add (mp->delx, stack_2 (x_packet (mp->xy)));
15360        number_add (mp->delx, stack_3 (x_packet (mp->xy)));
15361        number_add (mp->dely, stack_1 (y_packet (mp->xy)));
15362        number_add (mp->dely, stack_2 (y_packet (mp->xy)));
15363        number_add (mp->dely, stack_3 (y_packet (mp->xy)));
15364      }
15365    } else {
15366      set_number_from_scaled (mp->cur_tt, number_to_scaled (mp->cur_tt) + 1);
15367      mp->tol = mp->tol + mp->three_l;
15368      number_substract (mp->delx, stack_1 (x_packet (mp->xy)));
15369      number_substract (mp->delx, stack_2 (x_packet (mp->xy)));
15370      number_substract (mp->delx, stack_3 (x_packet (mp->xy)));
15371      number_substract (mp->dely, stack_1 (y_packet (mp->xy)));
15372      number_substract (mp->dely, stack_2 (y_packet (mp->xy)));
15373      number_substract (mp->dely, stack_3 (y_packet (mp->xy)));
15374      mp->xy = mp->xy + int_packets;        /* switch from |l_packets| to |r_packets| */
15375    }
15376  }
15377}
15378
15379
15380@ The following variables are global, although they are used only by
15381|cubic_intersection|, because it is necessary on some machines to
15382split |cubic_intersection| up into two procedures.
15383
15384@<Glob...@>=
15385mp_number delx;
15386mp_number dely;   /* the components of $\Delta=2^l(w_0-z_0)$ */
15387integer tol;    /* bound on the uncertainty in the overlap test */
15388integer uv;
15389integer xy;     /* pointers to the current packets of interest */
15390integer three_l;        /* |tol_step| times the bisection level */
15391mp_number appr_t;
15392mp_number appr_tt;        /* best approximations known to the answers */
15393
15394@ @<Initialize table ...@>=
15395new_number (mp->delx);
15396new_number (mp->dely);
15397new_number (mp->appr_t);
15398new_number (mp->appr_tt);
15399
15400@ @<Dealloc...@>=
15401free_number (mp->delx);
15402free_number (mp->dely);
15403free_number (mp->appr_t);
15404free_number (mp->appr_tt);
15405
15406@ We shall assume that the coordinates are sufficiently non-extreme that
15407integer overflow will not occur.
15408@^overflow in arithmetic@>
15409
15410@<Initialize for intersections at level zero@>=
15411q = mp_next_knot (p);
15412qq = mp_next_knot (pp);
15413mp->bisect_ptr = int_packets;
15414set_number_from_substraction (u1r, p->right_x, p->x_coord);
15415set_number_from_substraction (u2r, q->left_x, p->right_x);
15416set_number_from_substraction (u3r, q->x_coord, q->left_x);
15417set_min_max (ur_packet);
15418set_number_from_substraction (v1r, p->right_y, p->y_coord );
15419set_number_from_substraction (v2r, q->left_y, p->right_y);
15420set_number_from_substraction (v3r, q->y_coord, q->left_y );
15421set_min_max (vr_packet);
15422set_number_from_substraction (x1r, pp->right_x, pp->x_coord );
15423set_number_from_substraction (x2r, qq->left_x, pp->right_x );
15424set_number_from_substraction (x3r, qq->x_coord, qq->left_x );
15425set_min_max (xr_packet);
15426set_number_from_substraction (y1r, pp->right_y, pp->y_coord );
15427set_number_from_substraction (y2r, qq->left_y, pp->right_y);
15428set_number_from_substraction (y3r, qq->y_coord, qq->left_y);
15429set_min_max (yr_packet);
15430set_number_from_substraction (mp->delx, p->x_coord, pp->x_coord );
15431set_number_from_substraction (mp->dely, p->y_coord, pp->y_coord );
15432mp->tol = 0;
15433mp->uv = r_packets;
15434mp->xy = r_packets;
15435mp->three_l = 0;
15436set_number_from_scaled (mp->cur_t, 1);
15437set_number_from_scaled (mp->cur_tt, 1)
15438
15439@
15440
15441@<Subdivide for a new level of intersection@>=
15442number_clone (stack_dx, mp->delx);
15443number_clone (stack_dy, mp->dely);
15444set_number_from_scaled (stack_tol, mp->tol);
15445set_number_from_scaled (stack_uv, mp->uv);
15446set_number_from_scaled (stack_xy, mp->xy);
15447mp->bisect_ptr = mp->bisect_ptr + int_increment;
15448number_double (mp->cur_t);
15449number_double (mp->cur_tt);
15450number_clone (u1l, stack_1 (u_packet (mp->uv)));
15451number_clone (u3r, stack_3 (u_packet (mp->uv)));
15452set_number_from_addition (u2l, u1l, stack_2 (u_packet (mp->uv))); number_half (u2l);
15453set_number_from_addition (u2r, u3r, stack_2 (u_packet (mp->uv))); number_half (u2r);
15454set_number_from_addition (u3l, u2l, u2r); number_half (u3l);
15455number_clone (u1r, u3l);
15456set_min_max (ul_packet);
15457set_min_max (ur_packet);
15458number_clone (v1l, stack_1 (v_packet (mp->uv)));
15459number_clone (v3r, stack_3 (v_packet (mp->uv)));
15460set_number_from_addition (v2l, v1l, stack_2 (v_packet (mp->uv))); number_half(v2l);
15461set_number_from_addition (v2r, v3r, stack_2 (v_packet (mp->uv))); number_half(v2r);
15462set_number_from_addition (v3l, v2l, v2r); number_half(v3l);
15463number_clone (v1r, v3l);
15464set_min_max (vl_packet);
15465set_min_max (vr_packet);
15466number_clone (x1l, stack_1 (x_packet (mp->xy)));
15467number_clone (x3r, stack_3 (x_packet (mp->xy)));
15468set_number_from_addition (x2l, x1l, stack_2 (x_packet (mp->xy))); number_half(x2l);
15469set_number_from_addition (x2r, x3r, stack_2 (x_packet (mp->xy))); number_half(x2r);
15470set_number_from_addition (x3l, x2l, x2r); number_half(x3l);
15471number_clone (x1r, x3l);
15472set_min_max (xl_packet);
15473set_min_max (xr_packet);
15474number_clone (y1l, stack_1 (y_packet (mp->xy)));
15475number_clone (y3r, stack_3 (y_packet (mp->xy)));
15476set_number_from_addition (y2l, y1l, stack_2 (y_packet (mp->xy))); number_half (y2l);
15477set_number_from_addition (y2r, y3r, stack_2 (y_packet (mp->xy))); number_half (y2r);
15478set_number_from_addition (y3l, y2l, y2r); number_half (y3l);
15479number_clone (y1r, y3l);
15480set_min_max (yl_packet);
15481set_min_max (yr_packet);
15482mp->uv = l_packets;
15483mp->xy = l_packets;
15484number_double(mp->delx);
15485number_double(mp->dely);
15486mp->tol = mp->tol - mp->three_l + (integer) mp->tol_step;
15487mp->tol += mp->tol;
15488mp->three_l = mp->three_l + (integer) mp->tol_step
15489
15490@ The |path_intersection| procedure is much simpler.
15491It invokes |cubic_intersection| in lexicographic order until finding a
15492pair of cubics that intersect. The final intersection times are placed in
15493|cur_t| and~|cur_tt|.
15494
15495@c
15496static void mp_path_intersection (MP mp, mp_knot h, mp_knot hh) {
15497  mp_knot p, pp;        /* link registers that traverse the given paths */
15498  mp_number n, nn;        /* integer parts of intersection times, minus |unity| */
15499  @<Change one-point paths into dead cycles@>;
15500  new_number (n);
15501  new_number (nn);
15502  mp->tol_step = 0;
15503  do {
15504    set_number_to_unity(n);
15505    number_negate (n);
15506    p = h;
15507    do {
15508      if (mp_right_type (p) != mp_endpoint) {
15509        set_number_to_unity(nn);
15510        number_negate (nn);
15511        pp = hh;
15512        do {
15513          if (mp_right_type (pp) != mp_endpoint) {
15514            mp_cubic_intersection (mp, p, pp);
15515            if (number_positive (mp->cur_t)) {
15516              number_add (mp->cur_t, n);
15517              number_add (mp->cur_tt, nn);
15518              goto DONE;
15519            }
15520          }
15521          number_add(nn, unity_t);
15522          pp = mp_next_knot (pp);
15523        } while (pp != hh);
15524      }
15525      number_add(n, unity_t);
15526      p = mp_next_knot (p);
15527    } while (p != h);
15528    mp->tol_step = mp->tol_step + 3;
15529  } while (mp->tol_step <= 3);
15530  number_clone (mp->cur_t, unity_t);
15531  number_negate (mp->cur_t);
15532  number_clone (mp->cur_tt, unity_t);
15533  number_negate (mp->cur_tt);
15534DONE:
15535  free_number (n);
15536  free_number (nn);
15537}
15538
15539
15540@ @<Change one-point paths...@>=
15541if (mp_right_type (h) == mp_endpoint) {
15542  number_clone (h->right_x, h->x_coord);
15543  number_clone (h->left_x, h->x_coord);
15544  number_clone (h->right_y, h->y_coord);
15545  number_clone (h->left_y, h->y_coord);
15546  mp_right_type (h) = mp_explicit;
15547}
15548if (mp_right_type (hh) == mp_endpoint) {
15549  number_clone (hh->right_x, hh->x_coord);
15550  number_clone (hh->left_x, hh->x_coord);
15551  number_clone (hh->right_y, hh->y_coord);
15552  number_clone (hh->left_y, hh->y_coord);
15553  mp_right_type (hh) = mp_explicit;
15554}
15555
15556@* Dynamic linear equations.
15557\MP\ users define variables implicitly by stating equations that should be
15558satisfied; the computer is supposed to be smart enough to solve those equations.
15559And indeed, the computer tries valiantly to do so, by distinguishing five
15560different types of numeric values:
15561
15562\smallskip\hang
15563|type(p)=mp_known| is the nice case, when |value(p)| is the |scaled| value
15564of the variable whose address is~|p|.
15565
15566\smallskip\hang
15567|type(p)=mp_dependent| means that |value(p)| is not present, but |dep_list(p)|
15568points to a {\sl dependency list\/} that expresses the value of variable~|p|
15569as a |scaled| number plus a sum of independent variables with |fraction|
15570coefficients.
15571
15572\smallskip\hang
15573|type(p)=mp_independent| means that |indep_value(p)=s|, where |s>0| is a ``serial
15574number'' reflecting the time this variable was first used in an equation;
15575and there is an extra field |indep_scale(p)=m|, with |0<=m<64|, each dependent
15576variable that refers to this one is actually referring to the future value of
15577this variable times~$2^m$. (Usually |m=0|, but higher degrees of
15578scaling are sometimes needed to keep the coefficients in dependency lists
15579from getting too large. The value of~|m| will always be even.)
15580
15581\smallskip\hang
15582|type(p)=mp_numeric_type| means that variable |p| hasn't appeared in an
15583equation before, but it has been explicitly declared to be numeric.
15584
15585\smallskip\hang
15586|type(p)=undefined| means that variable |p| hasn't appeared before.
15587
15588\smallskip\noindent
15589We have actually discussed these five types in the reverse order of their
15590history during a computation: Once |known|, a variable never again
15591becomes |dependent|; once |dependent|, it almost never again becomes
15592|mp_independent|; once |mp_independent|, it never again becomes |mp_numeric_type|;
15593and once |mp_numeric_type|, it never again becomes |undefined| (except
15594of course when the user specifically decides to scrap the old value
15595and start again). A backward step may, however, take place: Sometimes
15596a |dependent| variable becomes |mp_independent| again, when one of the
15597independent variables it depends on is reverting to |undefined|.
15598
15599@d indep_scale(A) ((mp_value_node)(A))->data.indep.scale
15600@d set_indep_scale(A,B) ((mp_value_node)(A))->data.indep.scale=(B)
15601@d indep_value(A) ((mp_value_node)(A))->data.indep.serial
15602@d set_indep_value(A,B) ((mp_value_node)(A))->data.indep.serial=(B)
15603
15604
15605@c
15606void mp_new_indep(MP mp, mp_node p)  { /* create a new independent variable */
15607  if ( mp->serial_no>=max_integer ) {
15608    mp_fatal_error(mp, "variable instance identifiers exhausted");
15609  }
15610  mp_type(p)=mp_independent;
15611  mp->serial_no=mp->serial_no+1;
15612  set_indep_scale(p,0);
15613  set_indep_value(p,mp->serial_no);
15614}
15615
15616@ @<Declarations@>=
15617void mp_new_indep(MP mp, mp_node p);
15618
15619
15620@ @<Glob...@>=
15621integer serial_no;      /* the most recent serial number */
15622
15623@ But how are dependency lists represented? It's simple: The linear combination
15624$\alpha_1v_1+\cdots+\alpha_kv_k+\beta$ appears in |k+1| value nodes. If
15625|q=dep_list(p)| points to this list, and if |k>0|, then |dep_value(q)=
15626@t$\alpha_1$@>| (which is a |fraction|); |dep_info(q)| points to the location
15627of $\alpha_1$; and |mp_link(p)| points to the dependency list
15628$\alpha_2v_2+\cdots+\alpha_kv_k+\beta$. On the other hand if |k=0|,
15629then |dep_value(q)=@t$\beta$@>| (which is |scaled|) and |dep_info(q)=NULL|.
15630The independent variables $v_1$, \dots,~$v_k$ have been sorted so that
15631they appear in decreasing order of their |value| fields (i.e., of
15632their serial numbers). \ (It is convenient to use decreasing order,
15633since |value(NULL)=0|. If the independent variables were not sorted by
15634serial number but by some other criterion, such as their location in |mem|,
15635the equation-solving mechanism would be too system-dependent, because
15636the ordering can affect the computed results.)
15637
15638The |link| field in the node that contains the constant term $\beta$ is
15639called the {\sl final link\/} of the dependency list. \MP\ maintains
15640a doubly-linked master list of all dependency lists, in terms of a permanently
15641allocated node
15642in |mem| called |dep_head|. If there are no dependencies, we have
15643|mp_link(dep_head)=dep_head| and |prev_dep(dep_head)=dep_head|;
15644otherwise |mp_link(dep_head)| points to the first dependent variable, say~|p|,
15645and |prev_dep(p)=dep_head|. We have |type(p)=mp_dependent|, and |dep_list(p)|
15646points to its dependency list. If the final link of that dependency list
15647occurs in location~|q|, then |mp_link(q)| points to the next dependent
15648variable (say~|r|); and we have |prev_dep(r)=q|, etc.
15649
15650Dependency nodes sometimes mutate into value nodes and vice versa, so their
15651structures have to match.
15652
15653@d dep_value(A) ((mp_value_node)(A))->data.n
15654@d set_dep_value(A,B) do_set_dep_value(mp,(A),(B))
15655@d dep_info(A) get_dep_info(mp, (A))
15656@d set_dep_info(A,B) do {
15657   mp_value_node d = (mp_value_node)(B);
15658   FUNCTION_TRACE4("set_dep_info(%p,%p) on %d\n",(A),d,__LINE__);
15659  ((mp_value_node)(A))->parent_ = (mp_node)d;
15660} while (0)
15661@d dep_list(A) ((mp_value_node)(A))->attr_head_  /* half of the |value| field in a |dependent| variable */
15662@d set_dep_list(A,B) do {
15663   mp_value_node d = (mp_value_node)(B);
15664   FUNCTION_TRACE4("set_dep_list(%p,%p) on %d\n",(A),d,__LINE__);
15665   dep_list((A)) = (mp_node)d;
15666} while (0)
15667@d prev_dep(A) ((mp_value_node)(A))->subscr_head_ /* the other half; makes a doubly linked list */
15668@d set_prev_dep(A,B) do {
15669   mp_value_node d = (mp_value_node)(B);
15670   FUNCTION_TRACE4("set_prev_dep(%p,%p) on %d\n",(A),d,__LINE__);
15671   prev_dep((A)) = (mp_node)d;
15672} while (0)
15673
15674@c
15675static mp_node get_dep_info (MP mp, mp_value_node p) {
15676  mp_node d;
15677  d = p->parent_;               /* half of the |value| field in a |dependent| variable */
15678  FUNCTION_TRACE3 ("%p = dep_info(%p)\n", d, p);
15679  return d;
15680}
15681static void do_set_dep_value (MP mp, mp_value_node p, mp_number q) {
15682   number_clone (p->data.n, q);  /* half of the |value| field in a |dependent| variable */
15683   FUNCTION_TRACE3("set_dep_value(%p,%d)\n", p, q);
15684   p->attr_head_ = NULL;
15685   p->subscr_head_ = NULL;
15686}
15687
15688@ @<Declarations...@>=
15689static mp_node get_dep_info (MP mp, mp_value_node p);
15690
15691@
15692
15693@c
15694static mp_value_node mp_get_dep_node (MP mp) {
15695  mp_value_node p = (mp_value_node) mp_get_value_node (mp);
15696  mp_type (p) = mp_dep_node_type;
15697  return p;
15698}
15699static void mp_free_dep_node (MP mp, mp_value_node p) {
15700  mp_free_value_node (mp, (mp_node) p);
15701}
15702
15703
15704@ @<Declarations...@>=
15705static void mp_free_dep_node (MP mp, mp_value_node p);
15706
15707@ @<Initialize table entries@>=
15708mp->serial_no = 0;
15709mp->dep_head = mp_get_dep_node (mp);
15710set_mp_link (mp->dep_head, (mp_node) mp->dep_head);
15711set_prev_dep (mp->dep_head, (mp_node) mp->dep_head);
15712set_dep_info (mp->dep_head, NULL);
15713set_dep_list (mp->dep_head, NULL);
15714
15715@ @<Free table entries@>=
15716mp_free_dep_node (mp, mp->dep_head);
15717
15718@ Actually the description above contains a little white lie. There's
15719another kind of variable called |mp_proto_dependent|, which is
15720just like a |dependent| one except that the $\alpha$ coefficients
15721in its dependency list are |scaled| instead of being fractions.
15722Proto-dependency lists are mixed with dependency lists in the
15723nodes reachable from |dep_head|.
15724
15725@ Here is a procedure that prints a dependency list in symbolic form.
15726The second parameter should be either |dependent| or |mp_proto_dependent|,
15727to indicate the scaling of the coefficients.
15728
15729@<Declarations@>=
15730static void mp_print_dependency (MP mp, mp_value_node p, quarterword t);
15731
15732@ @c
15733void mp_print_dependency (MP mp, mp_value_node p, quarterword t) {
15734  mp_number v;    /* a coefficient */
15735  mp_value_node pp;     /* for list manipulation */
15736  mp_node q;
15737  pp = p;
15738  new_number (v);
15739  while (true) {
15740    number_clone (v, dep_value (p));
15741    number_abs (v);
15742    q = dep_info (p);
15743    if (q == NULL) {            /* the constant term */
15744      if (number_nonzero(v) || (p == pp)) {
15745        if (number_positive(dep_value (p)))
15746          if (p != pp)
15747            mp_print_char (mp, xord ('+'));
15748        print_number (dep_value (p));
15749      }
15750      return;
15751    }
15752    /* Print the coefficient, unless it's $\pm1.0$ */
15753    if (number_negative(dep_value (p)))
15754      mp_print_char (mp, xord ('-'));
15755    else if (p != pp)
15756      mp_print_char (mp, xord ('+'));
15757    if (t == mp_dependent) {
15758      fraction_to_round_scaled (v);
15759    }
15760    if (!number_equal (v, unity_t))
15761      print_number (v);
15762
15763    if (mp_type (q) != mp_independent)
15764      mp_confusion (mp, "dep");
15765    mp_print_variable_name (mp, q);
15766    set_number_from_scaled (v, indep_scale(q));
15767    while (number_positive (v)) {
15768      mp_print (mp, "*4");
15769      number_add_scaled (v, -2);
15770    }
15771    p = (mp_value_node) mp_link (p);
15772  }
15773}
15774
15775
15776
15777@ The maximum absolute value of a coefficient in a given dependency list
15778is returned by the following simple function.
15779
15780@c
15781static void mp_max_coef (MP mp, mp_number *x, mp_value_node p) {
15782  mp_number (absv);
15783  new_number (absv);
15784  set_number_to_zero (*x);
15785  while (dep_info (p) != NULL) {
15786    number_clone (absv, dep_value (p));
15787    number_abs (absv);
15788    if (number_greater (absv, *x)) {
15789      number_clone (*x, absv);
15790    }
15791    p = (mp_value_node) mp_link (p);
15792  }
15793  free_number (absv);
15794}
15795
15796
15797@ One of the main operations needed on dependency lists is to add a multiple
15798of one list to the other; we call this |p_plus_fq|, where |p| and~|q| point
15799to dependency lists and |f| is a fraction.
15800
15801If the coefficient of any independent variable becomes |coef_bound| or
15802more, in absolute value, this procedure changes the type of that variable
15803to `|independent_needing_fix|', and sets the global variable |fix_needed|
15804to~|true|. The value of $|coef_bound|=\mu$ is chosen so that
15805$\mu^2+\mu<8$; this means that the numbers we deal with won't
15806get too large. (Instead of the ``optimum'' $\mu=(\sqrt{33}-1)/2\approx
158072.3723$, the safer value 7/3 is taken as the threshold.)
15808
15809The changes mentioned in the preceding paragraph are actually done only if
15810the global variable |watch_coefs| is |true|. But it usually is; in fact,
15811it is |false| only when \MP\ is making a dependency list that will soon
15812be equated to zero.
15813
15814Several procedures that act on dependency lists, including |p_plus_fq|,
15815set the global variable |dep_final| to the final (constant term) node of
15816the dependency list that they produce.
15817
15818@d independent_needing_fix 0
15819
15820@<Glob...@>=
15821boolean fix_needed;     /* does at least one |independent| variable need scaling? */
15822boolean watch_coefs;    /* should we scale coefficients that exceed |coef_bound|? */
15823mp_value_node dep_final;        /* location of the constant term and final link */
15824
15825@ @<Set init...@>=
15826mp->fix_needed = false;
15827mp->watch_coefs = true;
15828
15829@ The |p_plus_fq| procedure has a fourth parameter, |t|, that should be
15830set to |mp_proto_dependent| if |p| is a proto-dependency list. In this
15831case |f| will be |scaled|, not a |fraction|. Similarly, the fifth parameter~|tt|
15832should be |mp_proto_dependent| if |q| is a proto-dependency list.
15833
15834List |q| is unchanged by the operation; but list |p| is totally destroyed.
15835
15836The final link of the dependency list or proto-dependency list returned
15837by |p_plus_fq| is the same as the original final link of~|p|. Indeed, the
15838constant term of the result will be located in the same |mem| location
15839as the original constant term of~|p|.
15840
15841Coefficients of the result are assumed to be zero if they are less than
15842a certain threshold. This compensates for inevitable rounding errors,
15843and tends to make more variables `|known|'. The threshold is approximately
15844$10^{-5}$ in the case of normal dependency lists, $10^{-4}$ for
15845proto-dependencies.
15846
15847@d fraction_threshold_k ((math_data *)mp->math)->fraction_threshold_t
15848@d half_fraction_threshold_k ((math_data *)mp->math)->half_fraction_threshold_t
15849@d scaled_threshold_k ((math_data *)mp->math)->scaled_threshold_t
15850@d half_scaled_threshold_k ((math_data *)mp->math)->half_scaled_threshold_t
15851
15852@<Declarations@>=
15853static mp_value_node mp_p_plus_fq (MP mp, mp_value_node p, mp_number f,
15854                                   mp_value_node q, mp_variable_type t,
15855                                   mp_variable_type tt);
15856
15857@ @c
15858static mp_value_node mp_p_plus_fq (MP mp, mp_value_node p, mp_number f,
15859                                   mp_value_node q, mp_variable_type t,
15860                                   mp_variable_type tt) {
15861  mp_node pp, qq;       /* |dep_info(p)| and |dep_info(q)|, respectively */
15862  mp_value_node r, s;   /* for list manipulation */
15863  mp_number threshold, half_threshold;    /* defines a neighborhood of zero */
15864  mp_number v, vv; /* temporary registers */
15865  new_number (v);
15866  new_number (vv);
15867  new_number (threshold);
15868  new_number (half_threshold);
15869  if (t == mp_dependent) {
15870    number_clone (threshold, fraction_threshold_k);
15871    number_clone (half_threshold, half_fraction_threshold_k);
15872  } else {
15873    number_clone (threshold, scaled_threshold_k);
15874    number_clone (half_threshold, half_scaled_threshold_k);
15875  }
15876  r = (mp_value_node) mp->temp_head;
15877  pp = dep_info (p);
15878  qq = dep_info (q);
15879  while (1) {
15880    if (pp == qq) {
15881      if (pp == NULL) {
15882        break;
15883      } else {
15884        /* Contribute a term from |p|, plus |f| times the
15885          corresponding term from |q| */
15886        mp_number r1;
15887        mp_number absv;
15888        new_fraction (r1);
15889        new_number (absv);
15890        if (tt == mp_dependent) {
15891          take_fraction (r1, f, dep_value (q));
15892        } else {
15893          take_scaled (r1, f, dep_value (q));
15894        }
15895        set_number_from_addition (v, dep_value (p), r1);
15896        free_number (r1);
15897        set_dep_value (p, v);
15898        s = p;
15899        p = (mp_value_node) mp_link (p);
15900        number_clone (absv, v);
15901        number_abs(absv);
15902        if (number_less (absv, threshold)) {
15903          mp_free_dep_node (mp, s);
15904        } else {
15905          if (number_greaterequal (absv, coef_bound_k) && mp->watch_coefs) {
15906            mp_type (qq) = independent_needing_fix;
15907	    /* If we set this , then we can drop (mp_type(pp) == independent_needing_fix && mp->fix_needed) later */
15908	    /* set_number_from_scaled (value_number (qq), indep_value(qq)); */
15909            mp->fix_needed = true;
15910          }
15911          set_mp_link (r, (mp_node) s);
15912          r = s;
15913        }
15914        free_number (absv);
15915        pp = dep_info (p);
15916        q = (mp_value_node) mp_link (q);
15917        qq = dep_info (q);
15918      }
15919
15920    } else {
15921      if (pp == NULL)
15922        set_number_to_neg_inf(v);
15923      else if (mp_type(pp) == mp_independent || (mp_type(pp) == independent_needing_fix && mp->fix_needed))
15924        set_number_from_scaled(v, indep_value(pp));
15925      else
15926        number_clone (v, value_number (pp));
15927      if (qq == NULL)
15928        set_number_to_neg_inf(vv);
15929      else if (mp_type(qq) == mp_independent || (mp_type(qq) == independent_needing_fix && mp->fix_needed))
15930        set_number_from_scaled(vv, indep_value(qq));
15931      else
15932        number_clone (vv, value_number (qq));
15933      if (number_less (v, vv)) {
15934        /* Contribute a term from |q|, multiplied by~|f| */
15935        mp_number absv;
15936        new_number (absv);
15937        {
15938          mp_number r1;
15939          mp_number arg1, arg2;
15940          new_fraction (r1);
15941          new_number (arg1);
15942          new_number (arg2);
15943          number_clone (arg1, f);
15944          number_clone (arg2, dep_value (q));
15945          if (tt == mp_dependent) {
15946            take_fraction (r1, arg1, arg2);
15947          } else {
15948            take_scaled (r1, arg1, arg2);
15949          }
15950          number_clone (v, r1);
15951          free_number (r1);
15952          free_number (arg1);
15953          free_number (arg2);
15954        }
15955        number_clone (absv, v);
15956        number_abs(absv);
15957        if (number_greater (absv, half_threshold)) {
15958          s = mp_get_dep_node (mp);
15959          set_dep_info (s, qq);
15960          set_dep_value (s, v);
15961          if (number_greaterequal(absv, coef_bound_k) && mp->watch_coefs) {
15962          /* clang:  dereference of a null pointer ('qq') */ assert(qq);
15963           mp_type (qq) = independent_needing_fix;
15964            mp->fix_needed = true;
15965          }
15966          set_mp_link (r, (mp_node) s);
15967          r = s;
15968        }
15969        q = (mp_value_node) mp_link (q);
15970        qq = dep_info (q);
15971        free_number (absv);
15972
15973      } else {
15974        set_mp_link (r, (mp_node) p);
15975        r = p;
15976        p = (mp_value_node) mp_link (p);
15977        pp = dep_info (p);
15978      }
15979    }
15980  }
15981  {
15982    mp_number r1;
15983    mp_number arg1, arg2;
15984    new_fraction (r1);
15985    new_number (arg1);
15986    new_number (arg2);
15987    number_clone (arg1, dep_value (q));
15988    number_clone (arg2, f);
15989    if (t == mp_dependent) {
15990      take_fraction (r1, arg1, arg2);
15991    } else {
15992      take_scaled (r1, arg1, arg2);
15993    }
15994    slow_add (arg1, dep_value (p), r1);
15995    set_dep_value (p, arg1);
15996    free_number (r1);
15997    free_number (arg1);
15998    free_number (arg2);
15999  }
16000  set_mp_link (r, (mp_node) p);
16001  mp->dep_final = p;
16002  free_number (threshold);
16003  free_number (half_threshold);
16004  free_number (v);
16005  free_number (vv);
16006  return (mp_value_node) mp_link (mp->temp_head);
16007}
16008
16009
16010@ It is convenient to have another subroutine for the special case
16011of |p_plus_fq| when |f=1.0|. In this routine lists |p| and |q| are
16012both of the same type~|t| (either |dependent| or |mp_proto_dependent|).
16013
16014@c
16015static mp_value_node mp_p_plus_q (MP mp, mp_value_node p, mp_value_node q,
16016                                  mp_variable_type t) {
16017  mp_node pp, qq;       /* |dep_info(p)| and |dep_info(q)|, respectively */
16018  mp_value_node s;      /* for list manipulation */
16019  mp_value_node r;      /* for list manipulation */
16020  mp_number threshold;    /* defines a neighborhood of zero */
16021  mp_number v, vv;        /* temporary register */
16022  new_number (v);
16023  new_number (vv);
16024  new_number (threshold);
16025  if (t == mp_dependent)
16026    number_clone (threshold, fraction_threshold_k);
16027  else
16028    number_clone (threshold, scaled_threshold_k);
16029  r = (mp_value_node) mp->temp_head;
16030  pp = dep_info (p);
16031  qq = dep_info (q);
16032  while (1) {
16033    if (pp == qq) {
16034      if (pp == NULL) {
16035        break;
16036      } else {
16037        /* Contribute a term from |p|, plus the corresponding term from |q| */
16038        mp_number test;
16039        new_number (test);
16040        set_number_from_addition (v, dep_value (p), dep_value (q));
16041        set_dep_value (p, v);
16042        s = p;
16043        p = (mp_value_node) mp_link (p);
16044        pp = dep_info (p);
16045        number_clone (test, v);
16046        number_abs(test);
16047        if (number_less (test, threshold)) {
16048          mp_free_dep_node (mp, s);
16049        } else {
16050          if (number_greaterequal(test, coef_bound_k) && mp->watch_coefs) {
16051            mp_type (qq) = independent_needing_fix;
16052	    /* If we set this , then we can drop (mp_type(pp) == independent_needing_fix && mp->fix_needed) later */
16053	    /* set_number_from_scaled (value_number (qq), indep_value(qq)); */
16054            mp->fix_needed = true;
16055          }
16056          set_mp_link (r, (mp_node) s);
16057          r = s;
16058        }
16059        free_number (test);
16060        q = (mp_value_node) mp_link (q);
16061        qq = dep_info (q);
16062      }
16063
16064    } else {
16065      if (pp == NULL)
16066        set_number_to_zero (v);
16067      else if (mp_type(pp) == mp_independent || (mp_type(pp) == independent_needing_fix && mp->fix_needed))
16068        set_number_from_scaled (v, indep_value(pp));
16069      else
16070        number_clone (v, value_number (pp));
16071      if (qq == NULL)
16072        set_number_to_zero (vv);
16073      else if (mp_type(qq) == mp_independent || (mp_type(qq) == independent_needing_fix && mp->fix_needed))
16074        set_number_from_scaled (vv, indep_value(qq));
16075      else
16076        number_clone (vv, value_number (qq));
16077      if (number_less (v, vv)) {
16078        s = mp_get_dep_node (mp);
16079        set_dep_info (s, qq);
16080        set_dep_value (s, dep_value (q));
16081        q = (mp_value_node) mp_link (q);
16082        qq = dep_info (q);
16083        set_mp_link (r, (mp_node) s);
16084        r = s;
16085      } else {
16086        set_mp_link (r, (mp_node) p);
16087        r = p;
16088        p = (mp_value_node) mp_link (p);
16089        pp = dep_info (p);
16090      }
16091    }
16092  }
16093  {
16094    mp_number r1;
16095    new_number (r1);
16096    slow_add (r1, dep_value (p), dep_value (q));
16097    set_dep_value (p, r1);
16098    free_number (r1);
16099  }
16100  set_mp_link (r, (mp_node) p);
16101  mp->dep_final = p;
16102  free_number (v);
16103  free_number (vv);
16104  free_number (threshold);
16105  return (mp_value_node) mp_link (mp->temp_head);
16106}
16107
16108@ A somewhat simpler routine will multiply a dependency list
16109by a given constant~|v|. The constant is either a |fraction| less than
16110|fraction_one|, or it is |scaled|. In the latter case we might be forced to
16111convert a dependency list to a proto-dependency list.
16112Parameters |t0| and |t1| are the list types before and after;
16113they should agree unless |t0=mp_dependent| and |t1=mp_proto_dependent|
16114and |v_is_scaled=true|.
16115
16116@c
16117static mp_value_node mp_p_times_v (MP mp, mp_value_node p, mp_number v,
16118                                   quarterword t0, quarterword t1,
16119                                   boolean v_is_scaled) {
16120  mp_value_node r, s;   /* for list manipulation */
16121  mp_number w;    /* tentative coefficient */
16122  mp_number threshold;
16123  boolean scaling_down;
16124  new_number (threshold);
16125  new_number (w);
16126  if (t0 != t1)
16127    scaling_down = true;
16128  else
16129    scaling_down = (!v_is_scaled);
16130  if (t1 == mp_dependent)
16131    number_clone (threshold, half_fraction_threshold_k);
16132  else
16133    number_clone (threshold, half_scaled_threshold_k);
16134  r = (mp_value_node) mp->temp_head;
16135  while (dep_info (p) != NULL) {
16136    mp_number test;
16137    new_number (test);
16138    if (scaling_down) {
16139      take_fraction (w, v, dep_value (p));
16140    } else {
16141      take_scaled (w, v, dep_value (p));
16142    }
16143    number_clone (test, w);
16144    number_abs(test);
16145    if (number_lessequal (test, threshold)) {
16146      s = (mp_value_node) mp_link (p);
16147      mp_free_dep_node (mp, p);
16148      p = s;
16149    } else {
16150      if (number_greaterequal(test, coef_bound_k)) {
16151        mp->fix_needed = true;
16152        mp_type (dep_info (p)) = independent_needing_fix;
16153      }
16154      set_mp_link (r, (mp_node) p);
16155      r = p;
16156      set_dep_value (p, w);
16157      p = (mp_value_node) mp_link (p);
16158    }
16159    free_number (test);
16160  }
16161  set_mp_link (r, (mp_node) p);
16162  {
16163    mp_number r1;
16164    new_number (r1);
16165    if (v_is_scaled) {
16166      take_scaled (r1, dep_value (p), v);
16167    } else {
16168      take_fraction (r1, dep_value (p), v);
16169    }
16170    set_dep_value (p, r1);
16171    free_number (r1);
16172  }
16173  free_number (w);
16174  free_number (threshold);
16175  return (mp_value_node) mp_link (mp->temp_head);
16176}
16177
16178
16179@ Similarly, we sometimes need to divide a dependency list
16180by a given |scaled| constant.
16181
16182@<Declarations@>=
16183static mp_value_node mp_p_over_v (MP mp, mp_value_node p, mp_number v, quarterword
16184                                  t0, quarterword t1);
16185
16186@
16187@d p_over_v_threshold_k ((math_data *)mp->math)->p_over_v_threshold_t
16188
16189@c
16190mp_value_node mp_p_over_v (MP mp, mp_value_node p, mp_number v_orig, quarterword
16191                           t0, quarterword t1) {
16192  mp_value_node r, s;   /* for list manipulation */
16193  mp_number w;    /* tentative coefficient */
16194  mp_number threshold;
16195  mp_number v;
16196  boolean scaling_down;
16197  new_number (v);
16198  new_number (w);
16199  new_number (threshold);
16200  number_clone (v, v_orig);
16201  if (t0 != t1)
16202    scaling_down = true;
16203  else
16204    scaling_down = false;
16205  if (t1 == mp_dependent)
16206    number_clone (threshold, half_fraction_threshold_k);
16207  else
16208    number_clone (threshold, half_scaled_threshold_k);
16209  r = (mp_value_node) mp->temp_head;
16210  while (dep_info (p) != NULL) {
16211    if (scaling_down) {
16212      mp_number x, absv;
16213      new_number (x);
16214      new_number (absv);
16215      number_clone (absv, v);
16216      number_abs (absv);
16217      if (number_less (absv, p_over_v_threshold_k)) {
16218        number_clone (x, v);
16219        convert_scaled_to_fraction (x);
16220        make_scaled (w, dep_value (p), x);
16221      } else {
16222        number_clone (x, dep_value (p));
16223        fraction_to_round_scaled (x);
16224        make_scaled (w, x, v);
16225      }
16226      free_number (x);
16227      free_number (absv);
16228    } else {
16229      make_scaled (w, dep_value (p), v);
16230    }
16231    {
16232    mp_number test;
16233    new_number (test);
16234    number_clone (test, w);
16235    number_abs(test);
16236    if (number_lessequal (test, threshold)) {
16237      s = (mp_value_node) mp_link (p);
16238      mp_free_dep_node (mp, p);
16239      p = s;
16240    } else {
16241      if (number_greaterequal (test, coef_bound_k)) {
16242        mp->fix_needed = true;
16243        mp_type (dep_info (p)) = independent_needing_fix;
16244      }
16245      set_mp_link (r, (mp_node) p);
16246      r = p;
16247      set_dep_value (p, w);
16248      p = (mp_value_node) mp_link (p);
16249    }
16250    free_number (test);
16251    }
16252  }
16253  set_mp_link (r, (mp_node) p);
16254  {
16255    mp_number ret;
16256    new_number (ret);
16257    make_scaled (ret, dep_value (p), v);
16258    set_dep_value (p, ret);
16259    free_number (ret);
16260  }
16261  free_number (v);
16262  free_number (w);
16263  free_number (threshold);
16264  return (mp_value_node) mp_link (mp->temp_head);
16265}
16266
16267
16268@ Here's another utility routine for dependency lists. When an independent
16269variable becomes dependent, we want to remove it from all existing
16270dependencies. The |p_with_x_becoming_q| function computes the
16271dependency list of~|p| after variable~|x| has been replaced by~|q|.
16272
16273This procedure has basically the same calling conventions as |p_plus_fq|:
16274List~|q| is unchanged; list~|p| is destroyed; the constant node and the
16275final link are inherited from~|p|; and the fourth parameter tells whether
16276or not |p| is |mp_proto_dependent|. However, the global variable |dep_final|
16277is not altered if |x| does not occur in list~|p|.
16278
16279@c
16280static mp_value_node mp_p_with_x_becoming_q (MP mp, mp_value_node p,
16281                                             mp_node x, mp_node q,
16282                                             quarterword t) {
16283  mp_value_node r, s;   /* for list manipulation */
16284  integer sx;   /* serial number of |x| */
16285  s = p;
16286  r = (mp_value_node) mp->temp_head;
16287  sx = indep_value (x);
16288  while (dep_info (s) != NULL && indep_value (dep_info (s)) > sx) {
16289    r = s;
16290    s = (mp_value_node) mp_link (s);
16291  }
16292  if (dep_info (s) == NULL || dep_info (s) != x) {
16293    return p;
16294  } else {
16295    mp_value_node ret;
16296    mp_number v1;
16297    new_number (v1);
16298    set_mp_link (mp->temp_head, (mp_node) p);
16299    set_mp_link (r, mp_link (s));
16300    number_clone (v1, dep_value (s));
16301    mp_free_dep_node (mp, s);
16302    ret = mp_p_plus_fq (mp, (mp_value_node) mp_link (mp->temp_head), v1,
16303                         (mp_value_node) q, t, mp_dependent);
16304    free_number (v1);
16305    return ret;
16306  }
16307}
16308
16309
16310@ Here's a simple procedure that reports an error when a variable
16311has just received a known value that's out of the required range.
16312
16313@<Declarations@>=
16314static void mp_val_too_big (MP mp, mp_number x);
16315
16316@ @c
16317static void mp_val_too_big (MP mp, mp_number x) {
16318  if (number_positive (internal_value (mp_warning_check))) {
16319    char msg[256];
16320    const char *hlp[] = {
16321           "The equation I just processed has given some variable a",
16322           "value outside of the safetyp range. Continue and I'll try",
16323           "to cope with that big value; but it might be dangerous.",
16324           "(Set warningcheck:=0 to suppress this message.)",
16325           NULL };
16326    mp_snprintf (msg, 256, "Value is too large (%s)", number_tostring(x));
16327    mp_error (mp, msg, hlp, true);
16328  }
16329}
16330
16331@ When a dependent variable becomes known, the following routine
16332removes its dependency list. Here |p| points to the variable, and
16333|q| points to the dependency list (which is one node long).
16334
16335@<Declarations@>=
16336static void mp_make_known (MP mp, mp_value_node p, mp_value_node q);
16337
16338@ @c
16339void mp_make_known (MP mp, mp_value_node p, mp_value_node q) {
16340  mp_variable_type t;   /* the previous type */
16341  mp_number absp;
16342  new_number (absp);
16343  set_prev_dep (mp_link (q), prev_dep (p));
16344  set_mp_link (prev_dep (p), mp_link (q));
16345  t = mp_type (p);
16346  mp_type (p) = mp_known;
16347  set_value_number (p, dep_value (q));
16348  mp_free_dep_node (mp, q);
16349  number_clone (absp, value_number (p));
16350  number_abs (absp);
16351  if (number_greaterequal (absp, warning_limit_t))
16352    mp_val_too_big (mp, value_number (p));
16353  if ((number_positive(internal_value (mp_tracing_equations)))
16354      && mp_interesting (mp, (mp_node) p)) {
16355    mp_begin_diagnostic (mp);
16356    mp_print_nl (mp, "#### ");
16357    mp_print_variable_name (mp, (mp_node) p);
16358    mp_print_char (mp, xord ('='));
16359    print_number (value_number (p));
16360    mp_end_diagnostic (mp, false);
16361  }
16362  if (cur_exp_node () == (mp_node) p && mp->cur_exp.type == t) {
16363    mp->cur_exp.type = mp_known;
16364    set_cur_exp_value_number (value_number (p));
16365    mp_free_value_node (mp, (mp_node) p);
16366  }
16367  free_number (absp);
16368}
16369
16370
16371@ The |fix_dependencies| routine is called into action when |fix_needed|
16372has been triggered. The program keeps a list~|s| of independent variables
16373whose coefficients must be divided by~4.
16374
16375In unusual cases, this fixup process might reduce one or more coefficients
16376to zero, so that a variable will become known more or less by default.
16377
16378@<Declarations@>=
16379static void mp_fix_dependencies (MP mp);
16380
16381@
16382@d independent_being_fixed 1 /* this variable already appears in |s| */
16383@c
16384static void mp_fix_dependencies (MP mp) {
16385  mp_value_node p, q, r, s, t;  /* list manipulation registers */
16386  mp_node x;    /* an independent variable */
16387  r = (mp_value_node) mp_link (mp->dep_head);
16388  s = NULL;
16389  while (r != mp->dep_head) {
16390    t = r;
16391    /* Run through the dependency list for variable |t|, fixing
16392      all nodes, and ending with final link~|q| */
16393    while (1) {
16394      if (t==r) {
16395        q = (mp_value_node) dep_list(t);
16396      } else {
16397        q = (mp_value_node) mp_link (r);
16398      }
16399      x = dep_info (q);
16400      if (x == NULL)
16401        break;
16402      if (mp_type (x) <= independent_being_fixed) {
16403        if (mp_type (x) < independent_being_fixed) {
16404          p = mp_get_dep_node (mp);
16405          set_mp_link (p, (mp_node) s);
16406          s = p;
16407          set_dep_info (s, x);
16408          mp_type (x) = independent_being_fixed;
16409        }
16410        set_dep_value (q, dep_value (q));
16411        number_divide_int (dep_value (q), 4);
16412        if (number_zero(dep_value (q))) {
16413          set_mp_link (r, mp_link (q));
16414          mp_free_dep_node (mp, q);
16415          q = r;
16416        }
16417      }
16418      r = q;
16419    }
16420
16421    r = (mp_value_node) mp_link (q);
16422    if (q == (mp_value_node) dep_list (t))
16423      mp_make_known (mp, t, q);
16424  }
16425  while (s != NULL) {
16426    p = (mp_value_node) mp_link (s);
16427    x = dep_info (s);
16428    mp_free_dep_node (mp, s);
16429    s = p;
16430    mp_type (x) = mp_independent;
16431    set_indep_scale (x, indep_scale (x) + 2);
16432  }
16433  mp->fix_needed = false;
16434}
16435
16436
16437@ The |new_dep| routine installs a dependency list~|p| based on the value node~|q|,
16438linking it into the list of all known dependencies. It replaces |q| with the new
16439dependency node. We assume that |dep_final| points to the final node of list~|p|.
16440
16441@c
16442static void mp_new_dep (MP mp, mp_node q, mp_variable_type newtype,
16443                        mp_value_node p) {
16444  mp_node r;    /* what used to be the first dependency */
16445  FUNCTION_TRACE4 ("mp_new_dep(%p,%d,%p)\n", q, newtype, p);
16446  mp_type (q) = newtype;
16447  set_dep_list (q, p);
16448  set_prev_dep (q, (mp_node) mp->dep_head);
16449  r = mp_link (mp->dep_head);
16450  set_mp_link (mp->dep_final, r);
16451  set_prev_dep (r, (mp_node) mp->dep_final);
16452  set_mp_link (mp->dep_head, q);
16453}
16454
16455
16456@ Here is one of the ways a dependency list gets started.
16457The |const_dependency| routine produces a list that has nothing but
16458a constant term.
16459
16460@c
16461static mp_value_node mp_const_dependency (MP mp, mp_number v) {
16462  mp->dep_final = mp_get_dep_node (mp);
16463  set_dep_value (mp->dep_final, v);
16464  set_dep_info (mp->dep_final, NULL);
16465  FUNCTION_TRACE3 ("%p = mp_const_dependency(%d)\n", mp->dep_final, number_to_scaled (v));
16466  return mp->dep_final;
16467}
16468
16469
16470@ And here's a more interesting way to start a dependency list from scratch:
16471The parameter to |single_dependency| is the location of an
16472independent variable~|x|, and the result is the simple dependency list
16473`|x+0|'.
16474
16475In the unlikely event that the given independent variable has been doubled so
16476often that we can't refer to it with a nonzero coefficient,
16477|single_dependency| returns the simple list `0'.  This case can be
16478recognized by testing that the returned list pointer is equal to
16479|dep_final|.
16480
16481@d two_to_the(A) (1<<(unsigned)(A))
16482
16483@c
16484static mp_value_node mp_single_dependency (MP mp, mp_node p) {
16485  mp_value_node q, rr;  /* the new dependency list */
16486  integer m;    /* the number of doublings */
16487  m = indep_scale (p);
16488  if (m > 28) {
16489    q = mp_const_dependency (mp, zero_t);
16490  } else {
16491    q = mp_get_dep_node (mp);
16492    set_dep_value (q, zero_t);
16493    set_number_from_scaled (dep_value (q), (integer) two_to_the (28 - m));
16494    set_dep_info (q, p);
16495    rr = mp_const_dependency (mp, zero_t);
16496    set_mp_link (q, (mp_node) rr);
16497  }
16498  FUNCTION_TRACE3 ("%p = mp_single_dependency(%p)\n", q, p);
16499  return q;
16500}
16501
16502
16503@ We sometimes need to make an exact copy of a dependency list.
16504
16505@c
16506static mp_value_node mp_copy_dep_list (MP mp, mp_value_node p) {
16507  mp_value_node q;      /* the new dependency list */
16508  FUNCTION_TRACE2 ("mp_copy_dep_list(%p)\n", p);
16509  q = mp_get_dep_node (mp);
16510  mp->dep_final = q;
16511  while (1) {
16512    set_dep_info (mp->dep_final, dep_info (p));
16513    set_dep_value (mp->dep_final, dep_value (p));
16514    if (dep_info (mp->dep_final) == NULL)
16515      break;
16516    set_mp_link (mp->dep_final, (mp_node) mp_get_dep_node (mp));
16517    mp->dep_final = (mp_value_node) mp_link (mp->dep_final);
16518    p = (mp_value_node) mp_link (p);
16519  }
16520  return q;
16521}
16522
16523
16524@ But how do variables normally become known? Ah, now we get to the heart of the
16525equation-solving mechanism. The |linear_eq| procedure is given a |dependent|
16526or |mp_proto_dependent| list,~|p|, in which at least one independent variable
16527appears. It equates this list to zero, by choosing an independent variable
16528with the largest coefficient and making it dependent on the others. The
16529newly dependent variable is eliminated from all current dependencies,
16530thereby possibly making other dependent variables known.
16531
16532The given list |p| is, of course, totally destroyed by all this processing.
16533
16534@c
16535static mp_value_node find_node_with_largest_coefficient(MP mp, mp_value_node p, mp_number *v);
16536static void display_new_dependency (MP mp, mp_value_node p, mp_node x, integer n);
16537static void change_to_known (MP mp, mp_value_node p, mp_node x, mp_value_node final_node, integer n);
16538static mp_value_node divide_p_by_minusv_removing_q (MP mp, mp_value_node p, mp_value_node q,
16539     				    mp_value_node *final_node, mp_number v, quarterword t);
16540static mp_value_node divide_p_by_2_n (MP mp, mp_value_node p, integer n);
16541static void mp_linear_eq (MP mp, mp_value_node p, quarterword t) {
16542  mp_value_node r;   /* for link manipulation */
16543  mp_node x;    /* the variable that loses its independence */
16544  integer n;    /* the number of times |x| had been halved */
16545  mp_number v;    /* the coefficient of |x| in list |p| */
16546  mp_value_node prev_r; /* lags one step behind |r| */
16547  mp_value_node final_node;     /* the constant term of the new dependency list */
16548  mp_value_node qq;
16549  new_number (v);
16550  FUNCTION_TRACE3 ("mp_linear_eq(%p,%d)\n", p, t);
16551  qq = find_node_with_largest_coefficient(mp, p, &v);
16552  x = dep_info (qq);
16553  n = indep_scale (x);
16554  p = divide_p_by_minusv_removing_q(mp, p, qq, &final_node, v, t);
16555  if (number_positive (internal_value (mp_tracing_equations))) {
16556    display_new_dependency(mp,p,(mp_node)x,n);
16557  }
16558  prev_r = (mp_value_node) mp->dep_head;
16559  r = (mp_value_node) mp_link (mp->dep_head);
16560  while (r != mp->dep_head) {
16561    mp_value_node s = (mp_value_node) dep_list (r);
16562    mp_value_node q = mp_p_with_x_becoming_q (mp, s, x, (mp_node) p, mp_type (r));
16563    if (dep_info (q) == NULL) {
16564      mp_make_known (mp, r, q);
16565    } else {
16566      set_dep_list (r, q);
16567      do {
16568        q = (mp_value_node) mp_link (q);
16569      } while (dep_info (q) != NULL);
16570      prev_r = q;
16571    }
16572    r = (mp_value_node) mp_link (prev_r);
16573  }
16574  if (n > 0) {
16575    p = divide_p_by_2_n(mp, p, n);
16576  }
16577  change_to_known(mp,p,(mp_node)x,final_node,n);
16578  if (mp->fix_needed)
16579    mp_fix_dependencies (mp);
16580  free_number (v);
16581}
16582
16583
16584@
16585@c
16586static mp_value_node find_node_with_largest_coefficient(MP mp, mp_value_node p, mp_number *v) {
16587  mp_number vabs; /* its absolute value of v*/
16588  mp_number rabs; /* the absolute value of |dep_value(r)| */
16589  mp_value_node q = p;
16590  mp_value_node r = (mp_value_node) mp_link (p);
16591  new_number (vabs);
16592  new_number (rabs);
16593  number_clone (*v, dep_value (q));
16594  while (dep_info (r) != NULL) {
16595     number_clone (vabs, *v);
16596     number_abs (vabs);
16597     number_clone (rabs, dep_value (r));
16598     number_abs (rabs);
16599     if (number_greater (rabs, vabs)) {
16600       q = r;
16601       number_clone (*v, dep_value (r));
16602    }
16603    r = (mp_value_node) mp_link (r);
16604  }
16605  free_number (vabs);
16606  free_number (rabs);
16607  return q;
16608}
16609
16610
16611@ Here we want to change the coefficients from |scaled| to |fraction|,
16612except in the constant term. In the common case of a trivial equation
16613like `\.{x=3.14}', we will have |v=-fraction_one|, |q=p|, and |t=mp_dependent|.
16614
16615@c
16616static mp_value_node divide_p_by_minusv_removing_q (MP mp, mp_value_node p, mp_value_node q,
16617       		     				   mp_value_node *final_node, mp_number v, quarterword t) {
16618  mp_value_node r;   /* for link manipulation */
16619  mp_value_node s;
16620  s = (mp_value_node) mp->temp_head;
16621  set_mp_link (s, (mp_node) p);
16622  r = p;
16623  do {
16624    if (r == q) {
16625      set_mp_link (s, mp_link (r));
16626      mp_free_dep_node (mp, r);
16627    } else {
16628      mp_number w;    /* a tentative coefficient */
16629      mp_number absw;
16630      new_number (w);
16631      new_number (absw);
16632      make_fraction (w, dep_value (r), v);
16633      number_clone (absw, w);
16634      number_abs (absw);
16635      if (number_lessequal (absw, half_fraction_threshold_k)) {
16636        set_mp_link (s, mp_link (r));
16637        mp_free_dep_node (mp, r);
16638      } else {
16639        number_negate (w);
16640        set_dep_value (r, w);
16641        s = r;
16642      }
16643      free_number(w);
16644      free_number (absw);
16645    }
16646    r = (mp_value_node) mp_link (s);
16647  } while (dep_info (r) != NULL);
16648
16649  if (t == mp_proto_dependent) {
16650    mp_number ret;
16651    new_number (ret);
16652    make_scaled (ret, dep_value (r), v);
16653    number_negate (ret);
16654    set_dep_value (r, ret);
16655    free_number (ret);
16656  } else if (number_to_scaled (v) != -number_to_scaled (fraction_one_t)) {
16657    mp_number ret;
16658    new_fraction (ret);
16659    make_fraction (ret, dep_value (r), v);
16660    number_negate (ret);
16661    set_dep_value (r, ret);
16662    free_number (ret);
16663  }
16664  *final_node = r;
16665  return (mp_value_node) mp_link (mp->temp_head);
16666}
16667
16668
16669@
16670@c
16671static void display_new_dependency (MP mp, mp_value_node p, mp_node x, integer n) {
16672  if (mp_interesting (mp, x)) {
16673    int w0;
16674    mp_begin_diagnostic (mp);
16675    mp_print_nl (mp, "## ");
16676    mp_print_variable_name (mp, x);
16677    w0 = n;
16678    while (w0 > 0) {
16679      mp_print (mp, "*4");
16680      w0 = w0 - 2;
16681    }
16682    mp_print_char (mp, xord ('='));
16683    mp_print_dependency (mp, p, mp_dependent);
16684    mp_end_diagnostic (mp, false);
16685  }
16686}
16687
16688@ The |n > 0| test is repeated here because it is of vital importance to the
16689function's functioning.
16690
16691@c
16692static mp_value_node divide_p_by_2_n (MP mp, mp_value_node p, integer n) {
16693  mp_value_node pp = NULL;
16694  if (n > 0) {
16695    /* Divide list |p| by $2^n$ */
16696    mp_value_node r;
16697    mp_value_node s;
16698    mp_number absw;
16699    mp_number w;    /* a tentative coefficient */
16700    new_number (w);
16701    new_number (absw);
16702    s = (mp_value_node) mp->temp_head;
16703    set_mp_link (mp->temp_head, (mp_node) p);
16704    r = p;
16705    do {
16706      if (n > 30) {
16707        set_number_to_zero (w);
16708      } else {
16709        number_clone (w, dep_value (r));
16710        number_divide_int (w, two_to_the (n));
16711      }
16712      number_clone (absw, w);
16713      number_abs (absw);
16714      if (number_lessequal(absw, half_fraction_threshold_k) && (dep_info (r) != NULL)) {
16715        set_mp_link (s, mp_link (r));
16716        mp_free_dep_node (mp, r);
16717      } else {
16718        set_dep_value (r, w);
16719        s = r;
16720      }
16721      r = (mp_value_node) mp_link (s);
16722    } while (dep_info (s) != NULL);
16723    pp = (mp_value_node) mp_link (mp->temp_head);
16724    free_number (absw);
16725    free_number (w);
16726  }
16727  return pp;
16728}
16729
16730@
16731@c
16732static void change_to_known (MP mp, mp_value_node p, mp_node x, mp_value_node final_node, integer n) {
16733  if (dep_info (p) == NULL) {
16734    mp_number absx;
16735    new_number (absx);
16736    mp_type (x) = mp_known;
16737    set_value_number (x, dep_value (p));
16738    number_clone (absx, value_number (x));
16739    number_abs (absx);
16740    if (number_greaterequal (absx, warning_limit_t))
16741      mp_val_too_big (mp, value_number (x));
16742    free_number (absx);
16743    mp_free_dep_node (mp, p);
16744    if (cur_exp_node () == x && mp->cur_exp.type == mp_independent) {
16745      set_cur_exp_value_number (value_number (x));
16746      mp->cur_exp.type = mp_known;
16747      mp_free_value_node (mp, x);
16748    }
16749  } else {
16750    mp->dep_final = final_node;
16751    mp_new_dep (mp, x, mp_dependent, p);
16752    if (cur_exp_node () == x && mp->cur_exp.type == mp_independent) {
16753      mp->cur_exp.type = mp_dependent;
16754    }
16755  }
16756}
16757
16758@* Dynamic nonlinear equations.
16759Variables of numeric type are maintained by the general scheme of
16760independent, dependent, and known values that we have just studied;
16761and the components of pair and transform variables are handled in the
16762same way. But \MP\ also has five other types of values: \&{boolean},
16763\&{string}, \&{pen}, \&{path}, and \&{picture}; what about them?
16764
16765Equations are allowed between nonlinear quantities, but only in a
16766simple form. Two variables that haven't yet been assigned values are
16767either equal to each other, or they're not.
16768
16769Before a boolean variable has received a value, its type is |mp_unknown_boolean|;
16770similarly, there are variables whose type is |mp_unknown_string|, |mp_unknown_pen|,
16771|mp_unknown_path|, and |mp_unknown_picture|. In such cases the value is either
16772|NULL| (which means that no other variables are equivalent to this one), or
16773it points to another variable of the same undefined type. The pointers in the
16774latter case form a cycle of nodes, which we shall call a ``ring.''
16775Rings of undefined variables may include capsules, which arise as
16776intermediate results within expressions or as \&{expr} parameters to macros.
16777
16778When one member of a ring receives a value, the same value is given to
16779all the other members. In the case of paths and pictures, this implies
16780making separate copies of a potentially large data structure; users should
16781restrain their enthusiasm for such generality, unless they have lots and
16782lots of memory space.
16783
16784@ The following procedure is called when a capsule node is being
16785added to a ring (e.g., when an unknown variable is mentioned in an expression).
16786
16787@c
16788static mp_node mp_new_ring_entry (MP mp, mp_node p) {
16789  mp_node q;    /* the new capsule node */
16790  q = mp_get_value_node (mp);
16791  mp_name_type (q) = mp_capsule;
16792  mp_type (q) = mp_type (p);
16793  if (value_node (p) == NULL)
16794    set_value_node (q, p);
16795  else
16796    set_value_node (q, value_node (p));
16797  set_value_node (p, q);
16798  return q;
16799}
16800
16801
16802@ Conversely, we might delete a capsule or a variable before it becomes known.
16803The following procedure simply detaches a quantity from its ring,
16804without recycling the storage.
16805
16806@<Declarations@>=
16807static void mp_ring_delete (MP mp, mp_node p);
16808
16809@ @c
16810void mp_ring_delete (MP mp, mp_node p) {
16811  mp_node q;
16812  (void) mp;
16813  q = value_node (p);
16814  if (q != NULL && q != p) {
16815    while (value_node (q) != p)
16816      q = value_node (q);
16817    set_value_node (q, value_node (p));
16818  }
16819}
16820
16821
16822@ Eventually there might be an equation that assigns values to all of the
16823variables in a ring. The |nonlinear_eq| subroutine does the necessary
16824propagation of values.
16825
16826If the parameter |flush_p| is |true|, node |p| itself needn't receive a
16827value, it will soon be recycled.
16828
16829@c
16830static void mp_nonlinear_eq (MP mp, mp_value v, mp_node p, boolean flush_p) {
16831  mp_variable_type t;   /* the type of ring |p| */
16832  mp_node q, r; /* link manipulation registers */
16833  t = (mp_type (p) - unknown_tag);
16834  q = value_node (p);
16835  if (flush_p)
16836    mp_type (p) = mp_vacuous;
16837  else
16838    p = q;
16839  do {
16840    r = value_node (q);
16841    mp_type (q) = t;
16842    switch (t) {
16843    case mp_boolean_type:
16844      set_value_number (q, v.data.n);
16845      break;
16846    case mp_string_type:
16847      set_value_str (q, v.data.str);
16848      add_str_ref (v.data.str);
16849      break;
16850    case mp_pen_type:
16851      set_value_knot (q, copy_pen (v.data.p));
16852      break;
16853    case mp_path_type:
16854      set_value_knot (q, mp_copy_path (mp, v.data.p));
16855      break;
16856    case mp_picture_type:
16857      set_value_node (q, v.data.node);
16858      add_edge_ref (v.data.node);
16859      break;
16860    default:
16861      break;
16862    }                           /* there ain't no more cases */
16863    q = r;
16864  } while (q != p);
16865}
16866
16867
16868@ If two members of rings are equated, and if they have the same type,
16869the |ring_merge| procedure is called on to make them equivalent.
16870
16871@c
16872static void mp_ring_merge (MP mp, mp_node p, mp_node q) {
16873  mp_node r;    /* traverses one list */
16874  r = value_node (p);
16875  while (r != p) {
16876    if (r == q) {
16877      exclaim_redundant_equation(mp);
16878      return;
16879    };
16880    r = value_node (r);
16881  }
16882  r = value_node (p);
16883  set_value_node (p, value_node (q));
16884  set_value_node (q, r);
16885}
16886
16887
16888@ @c
16889static void exclaim_redundant_equation (MP mp) {
16890  const char *hlp[] = {
16891         "I already knew that this equation was true.",
16892         "But perhaps no harm has been done; let's continue.",
16893         NULL };
16894  mp_back_error (mp, "Redundant equation", hlp, true);
16895  mp_get_x_next (mp);
16896}
16897
16898@ @<Declarations@>=
16899static void exclaim_redundant_equation (MP mp);
16900
16901@* Introduction to the syntactic routines.
16902Let's pause a moment now and try to look at the Big Picture.
16903The \MP\ program consists of three main parts: syntactic routines,
16904semantic routines, and output routines. The chief purpose of the
16905syntactic routines is to deliver the user's input to the semantic routines,
16906while parsing expressions and locating operators and operands. The
16907semantic routines act as an interpreter responding to these operators,
16908which may be regarded as commands. And the output routines are
16909periodically called on to produce compact font descriptions that can be
16910used for typesetting or for making interim proof drawings. We have
16911discussed the basic data structures and many of the details of semantic
16912operations, so we are good and ready to plunge into the part of \MP\ that
16913actually controls the activities.
16914
16915Our current goal is to come to grips with the |get_next| procedure,
16916which is the keystone of \MP's input mechanism. Each call of |get_next|
16917sets the value of three variables |cur_cmd|, |cur_mod|, and |cur_sym|,
16918representing the next input token.
16919$$\vbox{\halign{#\hfil\cr
16920  \hbox{|cur_cmd| denotes a command code from the long list of codes
16921   given earlier;}\cr
16922  \hbox{|cur_mod| denotes a modifier or operand of the command code;}\cr
16923  \hbox{|cur_sym| is the hash address of the symbolic token that was
16924   just scanned,}\cr
16925  \hbox{\qquad or zero in the case of a numeric or string
16926   or capsule token.}\cr}}$$
16927Underlying this external behavior of |get_next| is all the machinery
16928necessary to convert from character files to tokens. At a given time we
16929may be only partially finished with the reading of several files (for
16930which \&{input} was specified), and partially finished with the expansion
16931of some user-defined macros and/or some macro parameters, and partially
16932finished reading some text that the user has inserted online,
16933and so on. When reading a character file, the characters must be
16934converted to tokens; comments and blank spaces must
16935be removed, numeric and string tokens must be evaluated.
16936
16937To handle these situations, which might all be present simultaneously,
16938\MP\ uses various stacks that hold information about the incomplete
16939activities, and there is a finite state control for each level of the
16940input mechanism. These stacks record the current state of an implicitly
16941recursive process, but the |get_next| procedure is not recursive.
16942
16943@d cur_cmd() (unsigned)(mp->cur_mod_->type)
16944@d set_cur_cmd(A) mp->cur_mod_->type=(A)
16945@d cur_mod_int() number_to_int (mp->cur_mod_->data.n) /* operand of current command */
16946@d cur_mod() number_to_scaled (mp->cur_mod_->data.n) /* operand of current command */
16947@d cur_mod_number() mp->cur_mod_->data.n /* operand of current command */
16948@d set_cur_mod(A) set_number_from_scaled (mp->cur_mod_->data.n, (A))
16949@d set_cur_mod_number(A) number_clone (mp->cur_mod_->data.n, (A))
16950@d cur_mod_node() mp->cur_mod_->data.node
16951@d set_cur_mod_node(A) mp->cur_mod_->data.node=(A)
16952@d cur_mod_str() mp->cur_mod_->data.str
16953@d set_cur_mod_str(A) mp->cur_mod_->data.str=(A)
16954@d cur_sym() mp->cur_mod_->data.sym
16955@d set_cur_sym(A) mp->cur_mod_->data.sym=(A)
16956@d cur_sym_mod() mp->cur_mod_->name_type
16957@d set_cur_sym_mod(A) mp->cur_mod_->name_type=(A)
16958
16959@<Glob...@>=
16960mp_node cur_mod_;         /* current command, symbol, and its operands */
16961
16962@ @<Initialize table...@>=
16963mp->cur_mod_ = mp_get_symbolic_node(mp);
16964
16965@ @<Free table...@>=
16966mp_free_symbolic_node(mp, mp->cur_mod_);
16967
16968@ The |print_cmd_mod| routine prints a symbolic interpretation of a
16969command code and its modifier.
16970It consists of a rather tedious sequence of print
16971commands, and most of it is essentially an inverse to the |primitive|
16972routine that enters a \MP\ primitive into |hash| and |eqtb|. Therefore almost
16973all of this procedure appears elsewhere in the program, together with the
16974corresponding |primitive| calls.
16975
16976@<Declarations@>=
16977static void mp_print_cmd_mod (MP mp, integer c, integer m);
16978
16979@ @c
16980void mp_print_cmd_mod (MP mp, integer c, integer m) {
16981  switch (c) {
16982    @<Cases of |print_cmd_mod| for symbolic printing of primitives@>
16983  default:
16984    mp_print (mp, "[unknown command code!]");
16985    break;
16986  }
16987}
16988
16989
16990@ Here is a procedure that displays a given command in braces, in the
16991user's transcript file.
16992
16993@d show_cur_cmd_mod mp_show_cmd_mod(mp, cur_cmd(),cur_mod())
16994
16995@c
16996static void mp_show_cmd_mod (MP mp, integer c, integer m) {
16997  mp_begin_diagnostic (mp);
16998  mp_print_nl (mp, "{");
16999  mp_print_cmd_mod (mp, c, m);
17000  mp_print_char (mp, xord ('}'));
17001  mp_end_diagnostic (mp, false);
17002}
17003
17004
17005@* Input stacks and states.
17006The state of \MP's input mechanism appears in the input stack, whose
17007entries are records with five fields, called |index|, |start|, |loc|,
17008|limit|, and |name|. The top element of this stack is maintained in a
17009global variable for which no subscripting needs to be done; the other
17010elements of the stack appear in an array. Hence the stack is declared thus:
17011
17012@<Types...@>=
17013typedef struct {
17014  char *long_name_field;
17015  halfword start_field, loc_field, limit_field;
17016  mp_node nstart_field, nloc_field;
17017  mp_string name_field;
17018  quarterword index_field;
17019} in_state_record;
17020
17021@ @<Glob...@>=
17022in_state_record *input_stack;
17023integer input_ptr;      /* first unused location of |input_stack| */
17024integer max_in_stack;   /* largest value of |input_ptr| when pushing */
17025in_state_record cur_input;      /* the ``top'' input state */
17026int stack_size; /* maximum number of simultaneous input sources */
17027
17028@ @<Allocate or initialize ...@>=
17029mp->stack_size = 16;
17030mp->input_stack = xmalloc ((mp->stack_size + 1), sizeof (in_state_record));
17031
17032@ @<Dealloc variables@>=
17033xfree (mp->input_stack);
17034
17035@ We've already defined the special variable |loc==cur_input.loc_field|
17036in our discussion of basic input-output routines. The other components of
17037|cur_input| are defined in the same way:
17038
17039@d iindex mp->cur_input.index_field /* reference for buffer information */
17040@d start mp->cur_input.start_field /* starting position in |buffer| */
17041@d limit mp->cur_input.limit_field /* end of current line in |buffer| */
17042@d name mp->cur_input.name_field /* name of the current file */
17043
17044@ Let's look more closely now at the five control variables
17045(|index|,~|start|,~|loc|,~|limit|,~|name|),
17046assuming that \MP\ is reading a line of characters that have been input
17047from some file or from the user's terminal. There is an array called
17048|buffer| that acts as a stack of all lines of characters that are
17049currently being read from files, including all lines on subsidiary
17050levels of the input stack that are not yet completed. \MP\ will return to
17051the other lines when it is finished with the present input file.
17052
17053(Incidentally, on a machine with byte-oriented addressing, it would be
17054appropriate to combine |buffer| with the |str_pool| array,
17055letting the buffer entries grow downward from the top of the string pool
17056and checking that these two tables don't bump into each other.)
17057
17058The line we are currently working on begins in position |start| of the
17059buffer; the next character we are about to read is |buffer[loc]|; and
17060|limit| is the location of the last character present. We always have
17061|loc<=limit|. For convenience, |buffer[limit]| has been set to |"%"|, so
17062that the end of a line is easily sensed.
17063
17064The |name| variable is a string number that designates the name of
17065the current file, if we are reading an ordinary text file.  Special codes
17066|is_term..max_spec_src| indicate other sources of input text.
17067
17068@d is_term (mp_string)0 /* |name| value when reading from the terminal for normal input */
17069@d is_read (mp_string)1 /* |name| value when executing a \&{readstring} or \&{readfrom} */
17070@d is_scantok (mp_string)2 /* |name| value when reading text generated by \&{scantokens} */
17071@d max_spec_src is_scantok
17072
17073@ Additional information about the current line is available via the
17074|index| variable, which counts how many lines of characters are present
17075in the buffer below the current level. We have |index=0| when reading
17076from the terminal and prompting the user for each line; then if the user types,
17077e.g., `\.{input figs}', we will have |index=1| while reading
17078the file \.{figs.mp}. However, it does not follow that |index| is the
17079same as the input stack pointer, since many of the levels on the input
17080stack may come from token lists and some |index| values may correspond
17081to \.{MPX} files that are not currently on the stack.
17082
17083The global variable |in_open| is equal to the highest |index| value counting
17084\.{MPX} files but excluding token-list input levels.  Thus, the number of
17085partially read lines in the buffer is |in_open+1| and we have |in_open>=index|
17086when we are not reading a token list.
17087
17088If we are not currently reading from the terminal,
17089we are reading from the file variable |input_file[index]|. We use
17090the notation |terminal_input| as a convenient abbreviation for |name=is_term|,
17091and |cur_file| as an abbreviation for |input_file[index]|.
17092
17093When \MP\ is not reading from the terminal, the global variable |line| contains
17094the line number in the current file, for use in error messages. More precisely,
17095|line| is a macro for |line_stack[index]| and the |line_stack| array gives
17096the line number for each file in the |input_file| array.
17097
17098When an \.{MPX} file is opened the file name is stored in the |mpx_name|
17099array so that the name doesn't get lost when the file is temporarily removed
17100from the input stack.
17101Thus when |input_file[k]| is an \.{MPX} file, its name is |mpx_name[k]|
17102and it contains translated \TeX\ pictures for |input_file[k-1]|.
17103Since this is not an \.{MPX} file, we have
17104$$ \hbox{|mpx_name[k-1]<=absent|}. $$
17105This |name| field is set to |finished| when |input_file[k]| is completely
17106read.
17107
17108If more information about the input state is needed, it can be
17109included in small arrays like those shown here. For example,
17110the current page or segment number in the input file might be put
17111into a variable |page|, that is really a macro for the current entry
17112in `\ignorespaces|page_stack:array[0..max_in_open] of integer|\unskip'
17113by analogy with |line_stack|.
17114@^system dependencies@>
17115
17116@d terminal_input (name==is_term) /* are we reading from the terminal? */
17117@d cur_file mp->input_file[iindex] /* the current |void *| variable */
17118@d line mp->line_stack[iindex] /* current line number in the current source file */
17119@d in_ext mp->inext_stack[iindex] /* a string used to construct \.{MPX} file names */
17120@d in_name mp->iname_stack[iindex] /* a string used to construct \.{MPX} file names */
17121@d in_area mp->iarea_stack[iindex] /* another string for naming \.{MPX} files */
17122@d absent (mp_string)1 /* |name_field| value for unused |mpx_in_stack| entries */
17123@d mpx_reading (mp->mpx_name[iindex]>absent)
17124  /* when reading a file, is it an \.{MPX} file? */
17125@d mpx_finished 0
17126  /* |name_field| value when the corresponding \.{MPX} file is finished */
17127
17128@<Glob...@>=
17129integer in_open;        /* the number of lines in the buffer, less one */
17130integer in_open_max;    /* highest value of |in_open| ever seen */
17131unsigned int open_parens;       /* the number of open text files */
17132void **input_file;
17133integer *line_stack;    /* the line number for each file */
17134char **inext_stack;     /* used for naming \.{MPX} files */
17135char **iname_stack;     /* used for naming \.{MPX} files */
17136char **iarea_stack;     /* used for naming \.{MPX} files */
17137mp_string *mpx_name;
17138
17139@ @<Declarations@>=
17140static void mp_reallocate_input_stack (MP mp, int newsize);
17141
17142@ @c
17143static void mp_reallocate_input_stack (MP mp, int newsize) {
17144  int k;
17145  int n = newsize +1;
17146  XREALLOC (mp->input_file, n, void *);
17147  XREALLOC (mp->line_stack, n, integer);
17148  XREALLOC (mp->inext_stack, n, char *);
17149  XREALLOC (mp->iname_stack, n, char *);
17150  XREALLOC (mp->iarea_stack, n, char *);
17151  XREALLOC (mp->mpx_name, n, mp_string);
17152  for (k = mp->max_in_open; k <= n; k++) {
17153    mp->input_file[k] = NULL;
17154    mp->line_stack[k] = 0;
17155    mp->inext_stack[k] = NULL;
17156    mp->iname_stack[k] = NULL;
17157    mp->iarea_stack[k] = NULL;
17158    mp->mpx_name[k] = NULL;
17159  }
17160  mp->max_in_open = newsize;
17161}
17162
17163
17164@ This has to be more than |file_bottom|, so:
17165@<Allocate or ...@>=
17166mp_reallocate_input_stack (mp, file_bottom+4);
17167
17168@ @<Dealloc variables@>=
17169{
17170  int l;
17171  for (l = 0; l <= mp->max_in_open; l++) {
17172    xfree (mp->inext_stack[l]);
17173    xfree (mp->iname_stack[l]);
17174    xfree (mp->iarea_stack[l]);
17175  }
17176}
17177xfree (mp->input_file);
17178xfree (mp->line_stack);
17179xfree (mp->inext_stack);
17180xfree (mp->iname_stack);
17181xfree (mp->iarea_stack);
17182xfree (mp->mpx_name);
17183
17184
17185@ However, all this discussion about input state really applies only to the
17186case that we are inputting from a file. There is another important case,
17187namely when we are currently getting input from a token list. In this case
17188|iindex>max_in_open|, and the conventions about the other state variables
17189are different:
17190
17191\yskip\hang|nloc| is a pointer to the current node in the token list, i.e.,
17192the node that will be read next. If |nloc=NULL|, the token list has been
17193fully read.
17194
17195\yskip\hang|start| points to the first node of the token list; this node
17196may or may not contain a reference count, depending on the type of token
17197list involved.
17198
17199\yskip\hang|token_type|, which takes the place of |iindex| in the
17200discussion above, is a code number that explains what kind of token list
17201is being scanned.
17202
17203\yskip\hang|name| points to the |eqtb| address of the control sequence
17204being expanded, if the current token list is a macro not defined by
17205\&{vardef}. Macros defined by \&{vardef} have |name=NULL|; their name
17206can be deduced by looking at their first two parameters.
17207
17208\yskip\hang|param_start|, which takes the place of |limit|, tells where
17209the parameters of the current macro or loop text begin in the |param_stack|.
17210
17211\yskip\noindent The |token_type| can take several values, depending on
17212where the current token list came from:
17213
17214\yskip
17215\indent|forever_text|, if the token list being scanned is the body of
17216a \&{forever} loop;
17217
17218\indent|loop_text|, if the token list being scanned is the body of
17219a \&{for} or \&{forsuffixes} loop;
17220
17221\indent|parameter|, if a \&{text} or \&{suffix} parameter is being scanned;
17222
17223\indent|backed_up|, if the token list being scanned has been inserted as
17224`to be read again'.
17225
17226\indent|inserted|, if the token list being scanned has been inserted as
17227part of error recovery;
17228
17229\indent|macro|, if the expansion of a user-defined symbolic token is being
17230scanned.
17231
17232\yskip\noindent
17233The token list begins with a reference count if and only if |token_type=
17234macro|.
17235@^reference counts@>
17236
17237@d nloc mp->cur_input.nloc_field /* location of next node node */
17238@d nstart mp->cur_input.nstart_field /* location of next node node */
17239
17240@d token_type iindex /* type of current token list */
17241@d token_state (iindex<=macro) /* are we scanning a token list? */
17242@d file_state (iindex>macro) /* are we scanning a file line? */
17243@d param_start limit /* base of macro parameters in |param_stack| */
17244@d forever_text 0 /* |token_type| code for loop texts */
17245@d loop_text 1 /* |token_type| code for loop texts */
17246@d parameter 2 /* |token_type| code for parameter texts */
17247@d backed_up 3 /* |token_type| code for texts to be reread */
17248@d inserted 4 /* |token_type| code for inserted texts */
17249@d macro 5 /* |token_type| code for macro replacement texts */
17250@d file_bottom 6 /* lowest file code */
17251
17252@ The |param_stack| is an auxiliary array used to hold pointers to the token
17253lists for parameters at the current level and subsidiary levels of input.
17254This stack grows at a different rate from the others, and is dynamically reallocated
17255when needed.
17256
17257@<Glob...@>=
17258mp_node *param_stack;   /* token list pointers for parameters */
17259integer param_ptr;      /* first unused entry in |param_stack| */
17260integer max_param_stack;        /* largest value of |param_ptr| */
17261
17262@ @<Allocate or initialize ...@>=
17263mp->param_stack = xmalloc ((mp->param_size + 1), sizeof (mp_node));
17264
17265@ @c
17266static void mp_check_param_size (MP mp, int k) {
17267  while (k >= mp->param_size) {
17268    XREALLOC (mp->param_stack, (k + k / 4), mp_node);
17269    mp->param_size = k + k / 4;
17270  }
17271}
17272
17273
17274@ @<Dealloc variables@>=
17275xfree (mp->param_stack);
17276
17277@ Notice that the |line| isn't valid when |token_state| is true because it
17278depends on |iindex|.  If we really need to know the line number for the
17279topmost file in the iindex stack we use the following function.  If a page
17280number or other information is needed, this routine should be modified to
17281compute it as well.
17282@^system dependencies@>
17283
17284@<Declarations@>=
17285static integer mp_true_line (MP mp);
17286
17287@ @c
17288integer mp_true_line (MP mp) {
17289  int k;        /* an index into the input stack */
17290  if (file_state && (name > max_spec_src)) {
17291    return line;
17292  } else {
17293    k = mp->input_ptr;
17294    while ((k > 0) &&
17295           ((mp->input_stack[(k - 1)].index_field < file_bottom) ||
17296            (mp->input_stack[(k - 1)].name_field <= max_spec_src))) {
17297      decr (k);
17298    }
17299    return (k > 0 ? mp->line_stack[(k - 1) + file_bottom] : 0);
17300  }
17301}
17302
17303
17304@ Thus, the ``current input state'' can be very complicated indeed; there
17305can be many levels and each level can arise in a variety of ways. The
17306|show_context| procedure, which is used by \MP's error-reporting routine to
17307print out the current input state on all levels down to the most recent
17308line of characters from an input file, illustrates most of these conventions.
17309The global variable |file_ptr| contains the lowest level that was
17310displayed by this procedure.
17311
17312@<Glob...@>=
17313integer file_ptr;       /* shallowest level shown by |show_context| */
17314
17315@ The status at each level is indicated by printing two lines, where the first
17316line indicates what was read so far and the second line shows what remains
17317to be read. The context is cropped, if necessary, so that the first line
17318contains at most |half_error_line| characters, and the second contains
17319at most |error_line|. Non-current input levels whose |token_type| is
17320`|backed_up|' are shown only if they have not been fully read.
17321
17322@c
17323void mp_show_context (MP mp) {                               /* prints where the scanner is */
17324  unsigned old_setting; /* saved |selector| setting */
17325  @<Local variables for formatting calculations@>;
17326  mp->file_ptr = mp->input_ptr;
17327  mp->input_stack[mp->file_ptr] = mp->cur_input;
17328  /* store current state */
17329  while (1) {
17330    mp->cur_input = mp->input_stack[mp->file_ptr];      /* enter into the context */
17331    @<Display the current context@>;
17332    if (file_state)
17333      if ((name > max_spec_src) || (mp->file_ptr == 0))
17334        break;
17335    decr (mp->file_ptr);
17336  }
17337  mp->cur_input = mp->input_stack[mp->input_ptr];       /* restore original state */
17338}
17339
17340
17341@ @<Display the current context@>=
17342if ((mp->file_ptr == mp->input_ptr) || file_state ||
17343    (token_type != backed_up) || (nloc != NULL)) {
17344  /* we omit backed-up token lists that have already been read */
17345  mp->tally = 0;                /* get ready to count characters */
17346  old_setting = mp->selector;
17347  if (file_state) {
17348    @<Print location of current line@>;
17349    @<Pseudoprint the line@>;
17350  } else {
17351    @<Print type of token list@>;
17352    @<Pseudoprint the token list@>;
17353  }
17354  mp->selector = old_setting;   /* stop pseudoprinting */
17355  @<Print two lines using the tricky pseudoprinted information@>;
17356}
17357
17358@ This routine should be changed, if necessary, to give the best possible
17359indication of where the current line resides in the input file.
17360For example, on some systems it is best to print both a page and line number.
17361@^system dependencies@>
17362
17363@<Print location of current line@>=
17364if (name > max_spec_src) {
17365  mp_print_nl (mp, "l.");
17366  mp_print_int (mp, mp_true_line (mp));
17367} else if (terminal_input) {
17368  if (mp->file_ptr == 0)
17369    mp_print_nl (mp, "<*>");
17370  else
17371    mp_print_nl (mp, "<insert>");
17372} else if (name == is_scantok) {
17373  mp_print_nl (mp, "<scantokens>");
17374} else {
17375  mp_print_nl (mp, "<read>");
17376}
17377mp_print_char (mp, xord (' '))
17378
17379
17380@ Can't use case statement here because the |token_type| is not
17381a constant expression.
17382
17383@<Print type of token list@>=
17384{
17385  if (token_type == forever_text) {
17386    mp_print_nl (mp, "<forever> ");
17387  } else if (token_type == loop_text) {
17388    @<Print the current loop value@>;
17389  } else if (token_type == parameter) {
17390    mp_print_nl (mp, "<argument> ");
17391  } else if (token_type == backed_up) {
17392    if (nloc == NULL)
17393      mp_print_nl (mp, "<recently read> ");
17394    else
17395      mp_print_nl (mp, "<to be read again> ");
17396  } else if (token_type == inserted) {
17397    mp_print_nl (mp, "<inserted text> ");
17398  } else if (token_type == macro) {
17399    mp_print_ln (mp);
17400    if (name != NULL)
17401      mp_print_str (mp, name);
17402    else
17403      @<Print the name of a \&{vardef}'d macro@>;
17404    mp_print (mp, "->");
17405  } else {
17406    mp_print_nl (mp, "?");      /* this should never happen */
17407@.?\relax@>
17408  }
17409}
17410
17411
17412@ The parameter that corresponds to a loop text is either a token list
17413(in the case of \&{forsuffixes}) or a ``capsule'' (in the case of \&{for}).
17414We'll discuss capsules later; for now, all we need to know is that
17415the |link| field in a capsule parameter is |void| and that
17416|print_exp(p,0)| displays the value of capsule~|p| in abbreviated form.
17417
17418@<Print the current loop value@>=
17419{
17420  mp_node pp;
17421  mp_print_nl (mp, "<for(");
17422  pp = mp->param_stack[param_start];
17423  if (pp != NULL) {
17424    if (mp_link (pp) == MP_VOID)
17425      mp_print_exp (mp, pp, 0); /* we're in a \&{for} loop */
17426    else
17427      mp_show_token_list (mp, pp, NULL, 20, mp->tally);
17428  }
17429  mp_print (mp, ")> ");
17430}
17431
17432
17433@ The first two parameters of a macro defined by \&{vardef} will be token
17434lists representing the macro's prefix and ``at point.'' By putting these
17435together, we get the macro's full name.
17436
17437@<Print the name of a \&{vardef}'d macro@>=
17438{
17439  mp_node pp = mp->param_stack[param_start];
17440  if (pp == NULL) {
17441    mp_show_token_list (mp, mp->param_stack[param_start + 1], NULL, 20,
17442                        mp->tally);
17443  } else {
17444    mp_node qq = pp;
17445    while (mp_link (qq) != NULL)
17446      qq = mp_link (qq);
17447    mp_link (qq) = mp->param_stack[param_start + 1];
17448    mp_show_token_list (mp, pp, NULL, 20, mp->tally);
17449    mp_link (qq) = NULL;
17450  }
17451}
17452
17453
17454@ Now it is necessary to explain a little trick. We don't want to store a long
17455string that corresponds to a token list, because that string might take up
17456lots of memory; and we are printing during a time when an error message is
17457being given, so we dare not do anything that might overflow one of \MP's
17458tables. So `pseudoprinting' is the answer: We enter a mode of printing
17459that stores characters into a buffer of length |error_line|, where character
17460$k+1$ is placed into \hbox{|trick_buf[k mod error_line]|} if
17461|k<trick_count|, otherwise character |k| is dropped. Initially we set
17462|tally:=0| and |trick_count:=1000000|; then when we reach the
17463point where transition from line 1 to line 2 should occur, we
17464set |first_count:=tally| and |trick_count:=@tmax@>(error_line,
17465tally+1+error_line-half_error_line)|. At the end of the
17466pseudoprinting, the values of |first_count|, |tally|, and
17467|trick_count| give us all the information we need to print the two lines,
17468and all of the necessary text is in |trick_buf|.
17469
17470Namely, let |l| be the length of the descriptive information that appears
17471on the first line. The length of the context information gathered for that
17472line is |k=first_count|, and the length of the context information
17473gathered for line~2 is $m=\min(|tally|, |trick_count|)-k$. If |l+k<=h|,
17474where |h=half_error_line|, we print |trick_buf[0..k-1]| after the
17475descriptive information on line~1, and set |n:=l+k|; here |n| is the
17476length of line~1. If $l+k>h$, some cropping is necessary, so we set |n:=h|
17477and print `\.{...}' followed by
17478$$\hbox{|trick_buf[(l+k-h+3)..k-1]|,}$$
17479where subscripts of |trick_buf| are circular modulo |error_line|. The
17480second line consists of |n|~spaces followed by |trick_buf[k..(k+m-1)]|,
17481unless |n+m>error_line|; in the latter case, further cropping is done.
17482This is easier to program than to explain.
17483
17484@<Local variables for formatting...@>=
17485int i;  /* index into |buffer| */
17486integer l;      /* length of descriptive information on line 1 */
17487integer m;      /* context information gathered for line 2 */
17488int n;  /* length of line 1 */
17489integer p;      /* starting or ending place in |trick_buf| */
17490integer q;      /* temporary index */
17491
17492@ The following code tells the print routines to gather
17493the desired information.
17494
17495@d begin_pseudoprint {
17496  l=mp->tally; mp->tally=0; mp->selector=pseudo;
17497  mp->trick_count=1000000;
17498}
17499@d set_trick_count() {
17500  mp->first_count=mp->tally;
17501  mp->trick_count=mp->tally+1+mp->error_line-mp->half_error_line;
17502  if ( mp->trick_count<mp->error_line ) mp->trick_count=mp->error_line;
17503}
17504
17505@ And the following code uses the information after it has been gathered.
17506
17507@<Print two lines using the tricky pseudoprinted information@>=
17508if (mp->trick_count == 1000000)
17509  set_trick_count();
17510  /* |set_trick_count| must be performed */
17511if (mp->tally < mp->trick_count)
17512  m = mp->tally - mp->first_count;
17513else
17514  m = mp->trick_count - mp->first_count;        /* context on line 2 */
17515if (l + mp->first_count <= mp->half_error_line) {
17516  p = 0;
17517  n = l + mp->first_count;
17518} else {
17519  mp_print (mp, "...");
17520  p = l + mp->first_count - mp->half_error_line + 3;
17521  n = mp->half_error_line;
17522}
17523for (q = p; q <= mp->first_count - 1; q++) {
17524  mp_print_char (mp, mp->trick_buf[q % mp->error_line]);
17525}
17526mp_print_ln (mp);
17527for (q = 1; q <= n; q++) {
17528  mp_print_char (mp, xord (' '));       /* print |n| spaces to begin line~2 */
17529}
17530if (m + n <= mp->error_line)
17531  p = mp->first_count + m;
17532else
17533  p = mp->first_count + (mp->error_line - n - 3);
17534for (q = mp->first_count; q <= p - 1; q++) {
17535  mp_print_char (mp, mp->trick_buf[q % mp->error_line]);
17536}
17537if (m + n > mp->error_line)
17538  mp_print (mp, "...")
17539
17540
17541@ But the trick is distracting us from our current goal, which is to
17542understand the input state. So let's concentrate on the data structures that
17543are being pseudoprinted as we finish up the |show_context| procedure.
17544
17545@<Pseudoprint the line@>=
17546begin_pseudoprint;
17547if (limit > 0) {
17548  for (i = start; i <= limit - 1; i++) {
17549    if (i == loc)
17550      set_trick_count();
17551    mp_print_char (mp, mp->buffer[i]);
17552  }
17553}
17554
17555@ @<Pseudoprint the token list@>=
17556begin_pseudoprint;
17557if (token_type != macro)
17558  mp_show_token_list (mp, nstart, nloc, 100000, 0);
17559else
17560  mp_show_macro (mp, nstart, nloc, 100000)
17561
17562
17563@* Maintaining the input stacks.
17564The following subroutines change the input status in commonly needed ways.
17565
17566First comes |push_input|, which stores the current state and creates a
17567new level (having, initially, the same properties as the old).
17568
17569@d push_input  { /* enter a new input level, save the old */
17570  if ( mp->input_ptr>mp->max_in_stack ) {
17571    mp->max_in_stack=mp->input_ptr;
17572    if ( mp->input_ptr==mp->stack_size ) {
17573      int l = (mp->stack_size+(mp->stack_size/4));
17574      XREALLOC(mp->input_stack, l, in_state_record);
17575      mp->stack_size = l;
17576    }
17577  }
17578  mp->input_stack[mp->input_ptr]=mp->cur_input; /* stack the record */
17579  incr(mp->input_ptr);
17580}
17581
17582@ And of course what goes up must come down.
17583
17584@d pop_input { /* leave an input level, re-enter the old */
17585    decr(mp->input_ptr); mp->cur_input=mp->input_stack[mp->input_ptr];
17586  }
17587
17588@ Here is a procedure that starts a new level of token-list input, given
17589a token list |p| and its type |t|. If |t=macro|, the calling routine should
17590set |name|, reset~|loc|, and increase the macro's reference count.
17591
17592@d back_list(A) mp_begin_token_list(mp, (A), (quarterword)backed_up) /* backs up a simple token list */
17593
17594@c
17595static void mp_begin_token_list (MP mp, mp_node p, quarterword t) {
17596  push_input;
17597  nstart = p;
17598  token_type = t;
17599  param_start = mp->param_ptr;
17600  nloc = p;
17601}
17602
17603
17604@ When a token list has been fully scanned, the following computations
17605should be done as we leave that level of input.
17606@^inner loop@>
17607
17608@c
17609static void mp_end_token_list (MP mp) {                               /* leave a token-list input level */
17610  mp_node p;    /* temporary register */
17611  if (token_type >= backed_up) {        /* token list to be deleted */
17612    if (token_type <= inserted) {
17613      mp_flush_token_list (mp, nstart);
17614      goto DONE;
17615    } else {
17616      mp_delete_mac_ref (mp, nstart);   /* update reference count */
17617    }
17618  }
17619  while (mp->param_ptr > param_start) { /* parameters must be flushed */
17620    decr (mp->param_ptr);
17621    p = mp->param_stack[mp->param_ptr];
17622    if (p != NULL) {
17623      if (mp_link (p) == MP_VOID) {        /* it's an \&{expr} parameter */
17624        mp_recycle_value (mp, p);
17625        mp_free_value_node (mp, p);
17626      } else {
17627        mp_flush_token_list (mp, p);    /* it's a \&{suffix} or \&{text} parameter */
17628      }
17629    }
17630  }
17631DONE:
17632  pop_input;
17633  check_interrupt;
17634}
17635
17636
17637@ The contents of |cur_cmd,cur_mod,cur_sym| are placed into an equivalent
17638token by the |cur_tok| routine.
17639@^inner loop@>
17640
17641@c
17642@<Declare the procedure called |make_exp_copy|@>;
17643static mp_node mp_cur_tok (MP mp) {
17644  mp_node p;    /* a new token node */
17645  if (cur_sym() == NULL && (cur_sym_mod() == 0 || cur_sym_mod() == mp_normal_sym)) {
17646    if (cur_cmd() == mp_capsule_token) {
17647      mp_number save_exp_num; /* possible |cur_exp| numerical to be restored */
17648      mp_value save_exp = mp->cur_exp;  /* |cur_exp| to be restored */
17649      new_number (save_exp_num);
17650      number_clone (save_exp_num, cur_exp_value_number());
17651      mp_make_exp_copy (mp, cur_mod_node());
17652      p = mp_stash_cur_exp (mp);
17653      mp_link (p) = NULL;
17654      mp->cur_exp = save_exp;
17655      number_clone (mp->cur_exp.data.n, save_exp_num);
17656      free_number (save_exp_num);
17657    } else {
17658      p = mp_get_token_node (mp);
17659      mp_name_type (p) = mp_token;
17660      if (cur_cmd() == mp_numeric_token) {
17661        set_value_number (p, cur_mod_number());
17662        mp_type (p) = mp_known;
17663      } else {
17664        set_value_str (p, cur_mod_str());
17665        mp_type (p) = mp_string_type;
17666      }
17667    }
17668  } else {
17669    p = mp_get_symbolic_node (mp);
17670    set_mp_sym_sym (p, cur_sym());
17671    mp_name_type (p) = cur_sym_mod();
17672  }
17673  return p;
17674}
17675
17676
17677@ Sometimes \MP\ has read too far and wants to ``unscan'' what it has
17678seen. The |back_input| procedure takes care of this by putting the token
17679just scanned back into the input stream, ready to be read again.
17680If |cur_sym<>0|, the values of |cur_cmd| and |cur_mod| are irrelevant.
17681
17682@<Declarations@>=
17683static void mp_back_input (MP mp);
17684
17685@ @c
17686void mp_back_input (MP mp) {                               /* undoes one token of input */
17687  mp_node p;    /* a token list of length one */
17688  p = mp_cur_tok (mp);
17689  while (token_state && (nloc == NULL))
17690    mp_end_token_list (mp);     /* conserve stack space */
17691  back_list (p);
17692}
17693
17694
17695@ The |back_error| routine is used when we want to restore or replace an
17696offending token just before issuing an error message.  We disable interrupts
17697during the call of |back_input| so that the help message won't be lost.
17698
17699@<Declarations@>=
17700static void mp_back_error (MP mp, const char *msg, const char **hlp, boolean deletions_allowed) ;
17701
17702@ @c
17703static void mp_back_error (MP mp, const char *msg, const char **hlp, boolean deletions_allowed) {
17704  /* back up one token and call |error| */
17705  mp->OK_to_interrupt = false;
17706  mp_back_input (mp);
17707  mp->OK_to_interrupt = true;
17708  mp_error (mp, msg, hlp, deletions_allowed);
17709}
17710static void mp_ins_error (MP mp, const char *msg, const char **hlp, boolean deletions_allowed) {
17711  /* back up one inserted token and call |error| */
17712  mp->OK_to_interrupt = false;
17713  mp_back_input (mp);
17714  token_type = (quarterword) inserted;
17715  mp->OK_to_interrupt = true;
17716  mp_error (mp, msg, hlp, deletions_allowed);
17717}
17718
17719
17720@ The |begin_file_reading| procedure starts a new level of input for lines
17721of characters to be read from a file, or as an insertion from the
17722terminal. It does not take care of opening the file, nor does it set |loc|
17723or |limit| or |line|.
17724@^system dependencies@>
17725
17726@c
17727void mp_begin_file_reading (MP mp) {
17728  if (mp->in_open == (mp->max_in_open-1))
17729    mp_reallocate_input_stack (mp, (mp->max_in_open + mp->max_in_open / 4));
17730  if (mp->first == mp->buf_size)
17731    mp_reallocate_buffer (mp, (mp->buf_size + mp->buf_size / 4));
17732  mp->in_open++;
17733  push_input;
17734  iindex = (quarterword) mp->in_open;
17735  if (mp->in_open_max < mp->in_open)
17736    mp->in_open_max = mp->in_open;
17737  mp->mpx_name[iindex] = absent;
17738  start = (halfword) mp->first;
17739  name = is_term;               /* |terminal_input| is now |true| */
17740}
17741
17742
17743@ Conversely, the variables must be downdated when such a level of input
17744is finished.  Any associated \.{MPX} file must also be closed and popped
17745off the file stack. While finishing preloading, it is possible that the file
17746does not actually end with 'dump', so we capture that case here as well.
17747
17748@c
17749static void mp_end_file_reading (MP mp) {
17750  if (mp->reading_preload && mp->input_ptr == 0) {
17751      set_cur_sym(mp->frozen_dump);
17752      mp_back_input (mp);
17753      return;
17754  }
17755  if (mp->in_open > iindex) {
17756    if ((mp->mpx_name[mp->in_open] == absent) || (name <= max_spec_src)) {
17757      mp_confusion (mp, "endinput");
17758@:this can't happen endinput}{\quad endinput@>;
17759    } else {
17760      (mp->close_file) (mp, mp->input_file[mp->in_open]);       /* close an \.{MPX} file */
17761      delete_str_ref (mp->mpx_name[mp->in_open]);
17762      decr (mp->in_open);
17763    }
17764  }
17765  mp->first = (size_t) start;
17766  if (iindex != mp->in_open)
17767    mp_confusion (mp, "endinput");
17768  if (name > max_spec_src) {
17769    (mp->close_file) (mp, cur_file);
17770    xfree (in_ext);
17771    xfree (in_name);
17772    xfree (in_area);
17773  }
17774  pop_input;
17775  decr (mp->in_open);
17776}
17777
17778
17779@ Here is a function that tries to resume input from an \.{MPX} file already
17780associated with the current input file.  It returns |false| if this doesn't
17781work.
17782
17783@c
17784static boolean mp_begin_mpx_reading (MP mp) {
17785  if (mp->in_open != iindex + 1) {
17786    return false;
17787  } else {
17788    if (mp->mpx_name[mp->in_open] <= absent)
17789      mp_confusion (mp, "mpx");
17790    if (mp->first == mp->buf_size)
17791      mp_reallocate_buffer (mp, (mp->buf_size + (mp->buf_size / 4)));
17792    push_input;
17793    iindex = (quarterword) mp->in_open;
17794    start = (halfword) mp->first;
17795    name = mp->mpx_name[mp->in_open];
17796    add_str_ref (name);
17797    /* Put an empty line in the input buffer */
17798    /* We want to make it look as though we have just read a blank line
17799       without really doing so. */
17800    mp->last = mp->first;
17801    limit = (halfword) mp->last;
17802    /* simulate |input_ln| and |firm_up_the_line| */
17803    mp->buffer[limit] = xord ('%');
17804    mp->first = (size_t) (limit + 1);
17805    loc = start;
17806    return true;
17807  }
17808}
17809
17810
17811@ This procedure temporarily stops reading an \.{MPX} file.
17812
17813@c
17814static void mp_end_mpx_reading (MP mp) {
17815  if (mp->in_open != iindex)
17816    mp_confusion (mp, "mpx");
17817@:this can't happen mpx}{\quad mpx@>;
17818  if (loc < limit) {
17819    /* Complain that we are not at the end of a line in the \.{MPX} file */
17820    /* Here we enforce a restriction that simplifies the input stacks considerably.
17821       This should not inconvenience the user because \.{MPX} files are generated
17822       by an auxiliary program called \.{DVItoMP}. */
17823    const char *hlp[] = {
17824         "This file contains picture expressions for btex...etex",
17825         "blocks.  Such files are normally generated automatically",
17826         "but this one seems to be messed up.  I'm going to ignore",
17827         "the rest of this line.",
17828         NULL };
17829    mp_error (mp, "`mpxbreak' must be at the end of a line", hlp, true);
17830  }
17831  mp->first = (size_t) start;
17832  pop_input;
17833}
17834
17835@ In order to keep the stack from overflowing during a long sequence of
17836inserted `\.{show}' commands, the following routine removes completed
17837error-inserted lines from memory.
17838
17839@c
17840void mp_clear_for_error_prompt (MP mp) {
17841  while (file_state && terminal_input && (mp->input_ptr > 0) && (loc == limit))
17842    mp_end_file_reading (mp);
17843  mp_print_ln (mp);
17844  clear_terminal();
17845}
17846
17847
17848@ To get \MP's whole input mechanism going, we perform the following
17849actions.
17850
17851@<Initialize the input routines@>=
17852{
17853  mp->input_ptr = 0;
17854  mp->max_in_stack = file_bottom;
17855  mp->in_open = file_bottom;
17856  mp->open_parens = 0;
17857  mp->max_buf_stack = 0;
17858  mp->param_ptr = 0;
17859  mp->max_param_stack = 0;
17860  mp->first = 0;
17861  start = 0;
17862  iindex = file_bottom;
17863  line = 0;
17864  name = is_term;
17865  mp->mpx_name[file_bottom] = absent;
17866  mp->force_eof = false;
17867  if (!mp_init_terminal (mp))
17868    mp_jump_out (mp);
17869  limit = (halfword) mp->last;
17870  mp->first = mp->last + 1;
17871  /* |init_terminal| has set |loc| and |last| */
17872}
17873
17874
17875@* Getting the next token.
17876The heart of \MP's input mechanism is the |get_next| procedure, which
17877we shall develop in the next few sections of the program. Perhaps we
17878shouldn't actually call it the ``heart,'' however; it really acts as \MP's
17879eyes and mouth, reading the source files and gobbling them up. And it also
17880helps \MP\ to regurgitate stored token lists that are to be processed again.
17881
17882The main duty of |get_next| is to input one token and to set |cur_cmd|
17883and |cur_mod| to that token's command code and modifier. Furthermore, if
17884the input token is a symbolic token, that token's |hash| address
17885is stored in |cur_sym|; otherwise |cur_sym| is set to zero.
17886
17887Underlying this simple description is a certain amount of complexity
17888because of all the cases that need to be handled.
17889However, the inner loop of |get_next| is reasonably short and fast.
17890
17891@ Before getting into |get_next|, we need to consider a mechanism by which
17892\MP\ helps keep errors from propagating too far. Whenever the program goes
17893into a mode where it keeps calling |get_next| repeatedly until a certain
17894condition is met, it sets |scanner_status| to some value other than |normal|.
17895Then if an input file ends, or if an `\&{outer}' symbol appears,
17896an appropriate error recovery will be possible.
17897
17898The global variable |warning_info| helps in this error recovery by providing
17899additional information. For example, |warning_info| might indicate the
17900name of a macro whose replacement text is being scanned.
17901
17902@d normal 0 /* |scanner_status| at ``quiet times'' */
17903@d skipping 1 /* |scanner_status| when false conditional text is being skipped */
17904@d flushing 2 /* |scanner_status| when junk after a statement is being ignored */
17905@d absorbing 3 /* |scanner_status| when a \&{text} parameter is being scanned */
17906@d var_defining 4 /* |scanner_status| when a \&{vardef} is being scanned */
17907@d op_defining 5 /* |scanner_status| when a macro \&{def} is being scanned */
17908@d loop_defining 6 /* |scanner_status| when a \&{for} loop is being scanned */
17909
17910@<Glob...@>=
17911#define tex_flushing 7 /* |scanner_status| when skipping \TeX\ material */
17912integer scanner_status; /* are we scanning at high speed? */
17913mp_sym warning_info;    /* if so, what else do we need to know,
17914                           in case an error occurs? */
17915integer warning_line;
17916mp_node warning_info_node;
17917
17918@ @<Initialize the input routines@>=
17919mp->scanner_status = normal;
17920
17921@ The following subroutine
17922is called when an `\&{outer}' symbolic token has been scanned or
17923when the end of a file has been reached. These two cases are distinguished
17924by |cur_sym|, which is zero at the end of a file.
17925
17926@c
17927static boolean mp_check_outer_validity (MP mp) {
17928  mp_node p;    /* points to inserted token list */
17929  if (mp->scanner_status == normal) {
17930    return true;
17931  } else if (mp->scanner_status == tex_flushing) {
17932    @<Check if the file has ended while flushing \TeX\ material and set the
17933      result value for |check_outer_validity|@>;
17934  } else {
17935    @<Back up an outer symbolic token so that it can be reread@>;
17936    if (mp->scanner_status > skipping) {
17937      @<Tell the user what has run away and try to recover@>;
17938    } else {
17939      char msg[256];
17940      const char *hlp[] = {
17941             "A forbidden `outer' token occurred in skipped text.",
17942             "This kind of error happens when you say `if...' and forget",
17943             "the matching `fi'. I've inserted a `fi'; this might work.",
17944             NULL };
17945      mp_snprintf(msg, 256, "Incomplete if; all text was ignored after line %d", (int)mp->warning_line);
17946@.Incomplete if...@>;
17947      if (cur_sym() == NULL) {
17948        hlp[0] = "The file ended while I was skipping conditional text.";
17949      }
17950      set_cur_sym (mp->frozen_fi);
17951      mp_ins_error (mp, msg, hlp, false);
17952    }
17953    return false;
17954  }
17955}
17956
17957
17958@ @<Check if the file has ended while flushing \TeX\ material and set...@>=
17959if (cur_sym() != NULL) {
17960  return true;
17961} else {
17962  char msg[256];
17963  const char *hlp[] = {
17964         "The file ended while I was looking for the `etex' to",
17965         "finish this TeX material.  I've inserted `etex' now.",
17966          NULL };
17967  mp_snprintf(msg, 256, "TeX mode didn't end; all text was ignored after line %d", (int)mp->warning_line);
17968  set_cur_sym(mp->frozen_etex);
17969  mp_ins_error (mp, msg, hlp, false);
17970  return false;
17971}
17972
17973
17974@ @<Back up an outer symbolic token so that it can be reread@>=
17975if (cur_sym() != NULL) {
17976  p = mp_get_symbolic_node (mp);
17977  set_mp_sym_sym (p, cur_sym());
17978  mp_name_type (p) = cur_sym_mod();
17979  back_list (p);                /* prepare to read the symbolic token again */
17980}
17981
17982@ @<Tell the user what has run away...@>=
17983{
17984  char msg[256];
17985  const char *msg_start = NULL;
17986  const char *hlp[] = {
17987         "I suspect you have forgotten an `enddef',",
17988         "causing me to read past where you wanted me to stop.",
17989         "I'll try to recover; but if the error is serious,",
17990         "you'd better type `E' or `X' now and fix your file.",
17991         NULL };
17992  mp_runaway (mp);              /* print the definition-so-far */
17993  if (cur_sym() == NULL) {
17994    msg_start = "File ended while scanning";
17995@.File ended while scanning...@>
17996  } else {
17997    msg_start = "Forbidden token found while scanning";
17998@.Forbidden token found...@>
17999  }
18000  switch (mp->scanner_status) {
18001    @<Complete the error message,
18002      and set |cur_sym| to a token that might help recover from the error@>
18003  }                             /* there are no other cases */
18004  mp_ins_error (mp, msg, hlp, true);
18005}
18006
18007
18008@ As we consider various kinds of errors, it is also appropriate to
18009change the first line of the help message just given; |help_line[3]|
18010points to the string that might be changed.
18011
18012@<Complete the error message,...@>=
18013case flushing:
18014  mp_snprintf (msg, 256, "%s to the end of the statement", msg_start);
18015  hlp[0] = "A previous error seems to have propagated,";
18016  set_cur_sym(mp->frozen_semicolon);
18017  break;
18018case absorbing:
18019  mp_snprintf (msg, 256, "%s a text argument", msg_start);
18020  hlp[0] = "It seems that a right delimiter was left out,";
18021  if (mp->warning_info == NULL) {
18022    set_cur_sym(mp->frozen_end_group);
18023  } else {
18024    set_cur_sym(mp->frozen_right_delimiter);
18025    /* the next line makes sure that the inserted delimiter will
18026      match the delimiter that already was read. */
18027    set_equiv_sym (cur_sym(), mp->warning_info);
18028  }
18029  break;
18030case var_defining:
18031  {
18032    mp_string s;
18033    int old_setting = mp->selector;
18034    mp->selector = new_string;
18035    mp_print_variable_name (mp, mp->warning_info_node);
18036    s = mp_make_string (mp);
18037    mp->selector = old_setting;
18038    mp_snprintf (msg, 256, "%s the definition of %s", msg_start, s->str);
18039    delete_str_ref(s);
18040  }
18041  set_cur_sym(mp->frozen_end_def);
18042  break;
18043case op_defining:
18044  {
18045    char *s = mp_str(mp, text(mp->warning_info));
18046    mp_snprintf (msg, 256, "%s the definition of %s", msg_start, s);
18047  }
18048  set_cur_sym(mp->frozen_end_def);
18049  break;
18050case loop_defining:
18051  {
18052    char *s = mp_str(mp, text(mp->warning_info));
18053    mp_snprintf (msg, 256, "%s the text of a %s loop", msg_start, s);
18054  }
18055  hlp[0] = "I suspect you have forgotten an `endfor',";
18056  set_cur_sym(mp->frozen_end_for);
18057break;
18058
18059@ The |runaway| procedure displays the first part of the text that occurred
18060when \MP\ began its special |scanner_status|, if that text has been saved.
18061
18062@<Declarations@>=
18063static void mp_runaway (MP mp);
18064
18065@ @c
18066void mp_runaway (MP mp) {
18067  if (mp->scanner_status > flushing) {
18068    mp_print_nl (mp, "Runaway ");
18069    switch (mp->scanner_status) {
18070    case absorbing:
18071      mp_print (mp, "text?");
18072      break;
18073    case var_defining:
18074    case op_defining:
18075      mp_print (mp, "definition?");
18076      break;
18077    case loop_defining:
18078      mp_print (mp, "loop?");
18079      break;
18080    }                           /* there are no other cases */
18081    mp_print_ln (mp);
18082    mp_show_token_list (mp, mp_link (mp->hold_head), NULL, mp->error_line - 10,
18083                        0);
18084  }
18085}
18086
18087
18088@ We need to mention a procedure that may be called by |get_next|.
18089
18090@<Declarations@>=
18091static void mp_firm_up_the_line (MP mp);
18092
18093@ And now we're ready to take the plunge into |get_next| itself.
18094Note that the behavior depends on the |scanner_status| because percent signs
18095and double quotes need to be passed over when skipping TeX material.
18096
18097@c
18098void mp_get_next (MP mp) {
18099  /* sets |cur_cmd|, |cur_mod|, |cur_sym| to next token */
18100  mp_sym cur_sym_;    /* speed up access */
18101RESTART:
18102  set_cur_sym(NULL);
18103  set_cur_sym_mod(0);
18104  if (file_state) {
18105    int k;        /* an index into |buffer| */
18106    ASCII_code c; /* the current character in the buffer */
18107    int cclass;    /* its class number */
18108    /* Input from external file; |goto restart| if no input found,
18109       or |return| if a non-symbolic token is found */
18110    /* A percent sign appears in |buffer[limit]|; this makes it unnecessary
18111       to have a special test for end-of-line. */
18112  SWITCH:
18113    c = mp->buffer[loc];
18114    incr (loc);
18115    cclass = mp->char_class[c];
18116    switch (cclass) {
18117    case digit_class:
18118      scan_numeric_token((c - '0'));
18119      return;
18120      break;
18121    case period_class:
18122      cclass = mp->char_class[mp->buffer[loc]];
18123      if (cclass > period_class) {
18124        goto SWITCH;
18125      } else if (cclass < period_class) {  /* |class=digit_class| */
18126        scan_fractional_token(0);
18127        return;
18128      }
18129      break;
18130    case space_class:
18131      goto SWITCH;
18132      break;
18133    case percent_class:
18134      if (mp->scanner_status == tex_flushing) {
18135        if (loc < limit)
18136          goto SWITCH;
18137      }
18138      /* Move to next line of file, or |goto restart| if there is no next line */
18139      switch (move_to_next_line(mp)) {
18140      case 1:  goto RESTART;       break;
18141      case 2:  goto COMMON_ENDING; break;
18142      default: break;
18143      }
18144      check_interrupt;
18145      goto SWITCH;
18146      break;
18147    case string_class:
18148      if (mp->scanner_status == tex_flushing) {
18149        goto SWITCH;
18150      } else {
18151        if (mp->buffer[loc] == '"') {
18152          set_cur_mod_str(mp_rts(mp,""));
18153        } else {
18154          k = loc;
18155          mp->buffer[limit + 1] = xord ('"');
18156          do {
18157            incr (loc);
18158          } while (mp->buffer[loc] != '"');
18159          if (loc > limit) {
18160            /* Decry the missing string delimiter and |goto restart| */
18161            /* We go to |restart| after this error message, not to |SWITCH|,
18162               because the |clear_for_error_prompt| routine might have reinstated
18163               |token_state| after |error| has finished. */
18164            const char *hlp[] =  {
18165             "Strings should finish on the same line as they began.",
18166             "I've deleted the partial string; you might want to",
18167             "insert another by typing, e.g., `I\"new string\"'.",
18168             NULL };
18169            loc = limit;  /* the next character to be read on this line will be |"%"| */
18170            mp_error (mp, "Incomplete string token has been flushed", hlp, false);
18171            goto RESTART;
18172          }
18173          str_room ((size_t) (loc - k));
18174          do {
18175            append_char (mp->buffer[k]);
18176            incr (k);
18177          } while (k != loc);
18178          set_cur_mod_str(mp_make_string (mp));
18179        }
18180        incr (loc);
18181        set_cur_cmd((mp_variable_type)mp_string_token);
18182        return;
18183      }
18184      break;
18185    case isolated_classes:
18186      k = loc - 1;
18187      goto FOUND;
18188      break;
18189    case invalid_class:
18190      if (mp->scanner_status == tex_flushing) {
18191        goto SWITCH;
18192      } else {
18193        /* Decry the invalid character and |goto restart| */
18194        /* We go to |restart| instead of to |SWITCH|, because we might enter
18195        |token_state| after the error has been dealt with
18196        (cf.\ |clear_for_error_prompt|). */
18197        const char *hlp[] = {
18198           "A funny symbol that I can\'t read has just been input.",
18199           "Continue, and I'll forget that it ever happened.",
18200           NULL };
18201        mp_error(mp, "Text line contains an invalid character", hlp, false);
18202        goto RESTART;
18203      }
18204      break;
18205    default:
18206      break;                      /* letters, etc. */
18207    }
18208    k = loc - 1;
18209    while (mp->char_class[mp->buffer[loc]] == cclass)
18210      incr (loc);
18211  FOUND:
18212    set_cur_sym(mp_id_lookup (mp, (char *) (mp->buffer + k), (size_t) (loc - k), true));
18213
18214  } else {
18215    /* Input from token list; |goto restart| if end of list or
18216       if a parameter needs to be expanded,
18217       or |return| if a non-symbolic token is found */
18218    if (nloc != NULL && mp_type (nloc) == mp_symbol_node) { /* symbolic token */
18219      int cur_sym_mod_ = mp_name_type (nloc);
18220      halfword cur_info = mp_sym_info (nloc);
18221      set_cur_sym(mp_sym_sym (nloc));
18222      set_cur_sym_mod(cur_sym_mod_);
18223      nloc = mp_link (nloc);        /* move to next */
18224      if (cur_sym_mod_ == mp_expr_sym) {
18225        set_cur_cmd((mp_variable_type)mp_capsule_token);
18226        set_cur_mod_node(mp->param_stack[param_start + cur_info]);
18227        set_cur_sym_mod(0);
18228        set_cur_sym(NULL);
18229        return;
18230      } else if (cur_sym_mod_ == mp_suffix_sym || cur_sym_mod_ == mp_text_sym) {
18231        mp_begin_token_list (mp,
18232                             mp->param_stack[param_start + cur_info],
18233                             (quarterword) parameter);
18234        goto RESTART;
18235      }
18236    } else if (nloc != NULL) {
18237      /* Get a stored numeric or string or capsule token and |return| */
18238      if (mp_name_type (nloc) == mp_token) {
18239        if (mp_type (nloc) == mp_known) {
18240          set_cur_mod_number(value_number (nloc));
18241          set_cur_cmd((mp_variable_type)mp_numeric_token);
18242        } else {
18243          set_cur_mod_str(value_str (nloc));
18244          set_cur_cmd((mp_variable_type)mp_string_token);
18245          add_str_ref (cur_mod_str());
18246        }
18247      } else {
18248        set_cur_mod_node(nloc);
18249        set_cur_cmd((mp_variable_type)mp_capsule_token);
18250      }
18251      nloc = mp_link (nloc);
18252      return;
18253    } else {                        /* we are done with this token list */
18254      mp_end_token_list (mp);
18255      goto RESTART;                 /* resume previous level */
18256    }
18257  }
18258COMMON_ENDING:
18259  /* When a symbolic token is declared to be `\&{outer}', its command code
18260     is increased by |outer_tag|. */
18261  cur_sym_ = cur_sym();
18262  set_cur_cmd(eq_type (cur_sym_));
18263  set_cur_mod(equiv (cur_sym_));
18264  set_cur_mod_node(equiv_node (cur_sym_));
18265  if (cur_cmd() >= mp_outer_tag) {
18266    if (mp_check_outer_validity (mp))
18267      set_cur_cmd(cur_cmd() - mp_outer_tag);
18268    else
18269      goto RESTART;
18270  }
18271}
18272
18273@ The global variable |force_eof| is normally |false|; it is set |true|
18274by an \&{endinput} command.
18275
18276@<Glob...@>=
18277boolean force_eof;      /* should the next \&{input} be aborted early? */
18278
18279@ @<Declarations@>=
18280static int move_to_next_line (MP mp);
18281
18282@ @c
18283static int move_to_next_line (MP mp) {
18284  if (name > max_spec_src) {
18285    /* Read next line of file into |buffer|, or return 1
18286      (|goto restart|) if the file has ended */
18287    /* We must decrement |loc| in order to leave the buffer in a valid state
18288       when an error condition causes us to |goto restart| without calling
18289      |end_file_reading|. */
18290    {
18291      incr (line);
18292      mp->first = (size_t) start;
18293      if (!mp->force_eof) {
18294        if (mp_input_ln (mp, cur_file))     /* not end of file */
18295          mp_firm_up_the_line (mp); /* this sets |limit| */
18296        else
18297          mp->force_eof = true;
18298      };
18299      if (mp->force_eof) {
18300        mp->force_eof = false;
18301        decr (loc);
18302        if (mpx_reading) {
18303          /* Complain that the \.{MPX} file ended unexpectly; then set
18304            |cur_sym:=mp->frozen_mpx_break| and |goto comon_ending| */
18305	  /* We should never actually come to the end of an \.{MPX} file because such
18306             files should have an \&{mpxbreak} after the translation of the last
18307             \&{btex}$\,\ldots\,$\&{etex} block. */
18308          const char *hlp[] =  {"The file had too few picture expressions for btex...etex",
18309           "blocks.  Such files are normally generated automatically",
18310           "but this one got messed up.  You might want to insert a",
18311           "picture expression now.",
18312            NULL };
18313          mp->mpx_name[iindex] = mpx_finished;
18314          mp_error (mp, "mpx file ended unexpectedly", hlp, false);
18315          set_cur_sym(mp->frozen_mpx_break);
18316          return 2;
18317        } else {
18318          mp_print_char (mp, xord (')'));
18319          decr (mp->open_parens);
18320          update_terminal();          /* show user that file has been read */
18321          mp_end_file_reading (mp); /* resume previous level */
18322          if (mp_check_outer_validity (mp))
18323            return 1;
18324          else
18325            return 1;
18326        }
18327      }
18328      mp->buffer[limit] = xord ('%');
18329      mp->first = (size_t) (limit + 1);
18330      loc = start;                  /* ready to read */
18331    }
18332
18333
18334  } else {
18335    if (mp->input_ptr > 0) {
18336      /* text was inserted during error recovery or by \&{scantokens} */
18337      mp_end_file_reading (mp);
18338      /* goto RESTART */
18339      return 1;               /* resume previous level */
18340    }
18341    if (mp->job_name == NULL
18342        && (mp->selector < log_only || mp->selector >= write_file))
18343      mp_open_log_file (mp);
18344    if (mp->interaction > mp_nonstop_mode) {
18345      if (limit == start)         /* previous line was empty */
18346        mp_print_nl (mp, "(Please type a command or say `end')");
18347      mp_print_ln (mp);
18348      mp->first = (size_t) start;
18349      prompt_input ("*");         /* input on-line into |buffer| */
18350      limit = (halfword) mp->last;
18351      mp->buffer[limit] = xord ('%');
18352      mp->first = (size_t) (limit + 1);
18353      loc = start;
18354    } else {
18355      mp_fatal_error (mp, "*** (job aborted, no legal end found)");
18356      /* nonstop mode, which is intended for overnight batch processing,
18357         never waits for on-line input */
18358    }
18359  }
18360  return 0;
18361}
18362
18363
18364@ If the user has set the |mp_pausing| parameter to some positive value,
18365and if nonstop mode has not been selected, each line of input is displayed
18366on the terminal and the transcript file, followed by `\.{=>}'.
18367\MP\ waits for a response. If the response is NULL (i.e., if nothing is
18368typed except perhaps a few blank spaces), the original
18369line is accepted as it stands; otherwise the line typed is
18370used instead of the line in the file.
18371
18372@c
18373void mp_firm_up_the_line (MP mp) {
18374  size_t k;     /* an index into |buffer| */
18375  limit = (halfword) mp->last;
18376  if ((!mp->noninteractive)
18377      && (number_positive (internal_value (mp_pausing)))
18378      && (mp->interaction > mp_nonstop_mode)) {
18379    wake_up_terminal();
18380    mp_print_ln (mp);
18381    if (start < limit) {
18382      for (k = (size_t) start; k < (size_t) limit; k++) {
18383        mp_print_char (mp, mp->buffer[k]);
18384      }
18385    }
18386    mp->first = (size_t) limit;
18387    prompt_input ("=>");        /* wait for user response */
18388@.=>@>;
18389    if (mp->last > mp->first) {
18390      for (k = mp->first; k < mp->last; k++) {  /* move line down in buffer */
18391        mp->buffer[k + (size_t) start - mp->first] = mp->buffer[k];
18392      }
18393      limit = (halfword) ((size_t) start + mp->last - mp->first);
18394    }
18395  }
18396}
18397
18398
18399@* Dealing with \TeX\ material.
18400The \&{btex}$\,\ldots\,$\&{etex} and \&{verbatimtex}$\,\ldots\,$\&{etex}
18401features need to be implemented at a low level in the scanning process
18402so that \MP\ can stay in synch with the a preprocessor that treats
18403blocks of \TeX\ material as they occur in the input file without trying
18404to expand \MP\ macros.  Thus we need a special version of |get_next|
18405that does not expand macros and such but does handle \&{btex},
18406\&{verbatimtex}, etc.
18407
18408The special version of |get_next| is called |get_t_next|.  It works by flushing
18409\&{btex}$\,\ldots\,$\&{etex} and \&{verbatimtex}\allowbreak
18410$\,\ldots\,$\&{etex} blocks, switching to the \.{MPX} file when it sees
18411\&{btex}, and switching back when it sees \&{mpxbreak}.
18412
18413@d btex_code 0
18414@d verbatim_code 1
18415
18416@ @<Put each...@>=
18417mp_primitive (mp, "btex", mp_start_tex, btex_code);
18418@:btex_}{\&{btex} primitive@>;
18419mp_primitive (mp, "verbatimtex", mp_start_tex, verbatim_code);
18420@:verbatimtex_}{\&{verbatimtex} primitive@>;
18421mp_primitive (mp, "etex", mp_etex_marker, 0);
18422mp->frozen_etex = mp_frozen_primitive (mp, "etex", mp_etex_marker, 0);
18423@:etex_}{\&{etex} primitive@>;
18424mp_primitive (mp, "mpxbreak", mp_mpx_break, 0);
18425mp->frozen_mpx_break = mp_frozen_primitive (mp, "mpxbreak", mp_mpx_break, 0);
18426@:mpx_break_}{\&{mpxbreak} primitive@>
18427
18428
18429@ @<Cases of |print_cmd...@>=
18430case mp_start_tex:
18431if (m == btex_code)
18432  mp_print (mp, "btex");
18433else
18434  mp_print (mp, "verbatimtex");
18435break;
18436case mp_etex_marker:
18437mp_print (mp, "etex");
18438break;
18439case mp_mpx_break:
18440mp_print (mp, "mpxbreak");
18441break;
18442
18443@ Actually, |get_t_next| is a macro that avoids procedure overhead except
18444in the unusual case where \&{btex}, \&{verbatimtex}, \&{etex}, or \&{mpxbreak}
18445is encountered.
18446
18447@d get_t_next(a) do {
18448  mp_get_next (mp);
18449  if (cur_cmd() <= mp_max_pre_command)
18450    mp_t_next (mp);
18451} while (0)
18452
18453@c
18454@ @<Declarations@>=
18455static void mp_t_next (MP mp);
18456static void mp_start_mpx_input (MP mp);
18457
18458@ @c
18459static void mp_t_next (MP mp) {
18460  int old_status;       /* saves the |scanner_status| */
18461  integer old_info;     /* saves the |warning_info| */
18462
18463if ((mp->extensions == 1) && (cur_cmd() == mp_start_tex)) {
18464    @<Pass btex ... etex to script@>;
18465} else {
18466
18467    while (cur_cmd() <= mp_max_pre_command) {
18468        if (cur_cmd() == mp_mpx_break) {
18469          if (!file_state || (mp->mpx_name[iindex] == absent)) {
18470            @<Complain about a misplaced \&{mpxbreak}@>;
18471          } else {
18472            mp_end_mpx_reading (mp);
18473            goto TEX_FLUSH;
18474          }
18475        } else if (cur_cmd() == mp_start_tex) {
18476          if (token_state || (name <= max_spec_src)) {
18477            @<Complain that we are not reading a file@>;
18478          } else if (mpx_reading) {
18479            @<Complain that \.{MPX} files cannot contain \TeX\ material@>;
18480          } else if ((cur_mod() != verbatim_code) &&
18481                     (mp->mpx_name[iindex] != mpx_finished)) {
18482            if (!mp_begin_mpx_reading (mp))
18483              mp_start_mpx_input (mp);
18484          } else {
18485            goto TEX_FLUSH;
18486          }
18487        } else {
18488          @<Complain about a misplaced \&{etex}@>;
18489        }
18490        goto COMMON_ENDING;
18491      TEX_FLUSH:
18492        @<Flush the \TeX\ material@>;
18493      COMMON_ENDING:
18494        mp_get_next (mp);
18495    }
18496}
18497}
18498
18499
18500@ We could be in the middle of an operation such as skipping false conditional
18501text when \TeX\ material is encountered, so we must be careful to save the
18502|scanner_status|.
18503
18504@<Flush the \TeX\ material@>=
18505old_status = mp->scanner_status;
18506old_info = mp->warning_line;
18507mp->scanner_status = tex_flushing;
18508mp->warning_line = line;
18509do {
18510  mp_get_next (mp);
18511} while (cur_cmd() != mp_etex_marker);
18512mp->scanner_status = old_status;
18513mp->warning_line = old_info
18514
18515@ @<Complain that \.{MPX} files cannot contain \TeX\ material@>=
18516{
18517  const char *hlp[] = {
18518         "This file contains picture expressions for btex...etex",
18519         "blocks.  Such files are normally generated automatically",
18520         "but this one seems to be messed up.  I'll just keep going",
18521         "and hope for the best.",
18522         NULL };
18523  mp_error (mp, "An mpx file cannot contain btex or verbatimtex blocks", hlp, true);
18524}
18525
18526
18527@ @<Complain that we are not reading a file@>=
18528{
18529  const char *hlp[] = {
18530         "I'll have to ignore this preprocessor command because it",
18531         "only works when there is a file to preprocess.  You might",
18532         "want to delete everything up to the next `etex`.",
18533         NULL };
18534  mp_error (mp, "You can only use `btex' or `verbatimtex' in a file", hlp, true);
18535}
18536
18537
18538@ @<Complain about a misplaced \&{mpxbreak}@>=
18539{
18540  const char *hlp[] = {
18541         "I'll ignore this preprocessor command because it",
18542         "doesn't belong here",
18543         NULL };
18544  mp_error (mp, "Misplaced mpxbreak", hlp, true);
18545}
18546
18547
18548@ @<Complain about a misplaced \&{etex}@>=
18549{
18550  const char *hlp[] = {
18551         "There is no btex or verbatimtex for this to match",
18552          NULL };
18553  mp_error (mp, "Extra etex will be ignored", hlp, true);
18554}
18555
18556
18557@* Scanning macro definitions.
18558\MP\ has a variety of ways to tuck tokens away into token lists for later
18559use: Macros can be defined with \&{def}, \&{vardef}, \&{primarydef}, etc.;
18560repeatable code can be defined with \&{for}, \&{forever}, \&{forsuffixes}.
18561All such operations are handled by the routines in this part of the program.
18562
18563The modifier part of each command code is zero for the ``ending delimiters''
18564like \&{enddef} and \&{endfor}.
18565
18566@d start_def 1 /* command modifier for \&{def} */
18567@d var_def 2 /* command modifier for \&{vardef} */
18568@d end_def 0 /* command modifier for \&{enddef} */
18569@d start_forever 1 /* command modifier for \&{forever} */
18570@d start_for 2 /* command modifier for \&{forever} */
18571@d start_forsuffixes 3 /* command modifier for \&{forever} */
18572@d end_for 0 /* command modifier for \&{endfor} */
18573
18574@<Put each...@>=
18575mp_primitive (mp, "def", mp_macro_def, start_def);
18576@:def_}{\&{def} primitive@>;
18577mp_primitive (mp, "vardef", mp_macro_def, var_def);
18578@:var_def_}{\&{vardef} primitive@>;
18579mp_primitive (mp, "primarydef", mp_macro_def, mp_secondary_primary_macro);
18580@:primary_def_}{\&{primarydef} primitive@>;
18581mp_primitive (mp, "secondarydef", mp_macro_def, mp_tertiary_secondary_macro);
18582@:secondary_def_}{\&{secondarydef} primitive@>;
18583mp_primitive (mp, "tertiarydef", mp_macro_def, mp_expression_tertiary_macro);
18584@:tertiary_def_}{\&{tertiarydef} primitive@>;
18585mp_primitive (mp, "enddef", mp_macro_def, end_def);
18586mp->frozen_end_def = mp_frozen_primitive (mp, "enddef", mp_macro_def, end_def);
18587@:end_def_}{\&{enddef} primitive@>;
18588mp_primitive (mp, "for", mp_iteration, start_for);
18589@:for_}{\&{for} primitive@>;
18590mp_primitive (mp, "forsuffixes", mp_iteration, start_forsuffixes);
18591@:for_suffixes_}{\&{forsuffixes} primitive@>;
18592mp_primitive (mp, "forever", mp_iteration, start_forever);
18593@:forever_}{\&{forever} primitive@>;
18594mp_primitive (mp, "endfor", mp_iteration, end_for);
18595mp->frozen_end_for = mp_frozen_primitive (mp, "endfor", mp_iteration, end_for);
18596@:end_for_}{\&{endfor} primitive@>
18597
18598
18599@ @<Cases of |print_cmd...@>=
18600case mp_macro_def:
18601if (m <= var_def) {
18602  if (m == start_def)
18603    mp_print (mp, "def");
18604  else if (m < start_def)
18605    mp_print (mp, "enddef");
18606  else
18607    mp_print (mp, "vardef");
18608} else if (m == mp_secondary_primary_macro) {
18609  mp_print (mp, "primarydef");
18610} else if (m == mp_tertiary_secondary_macro) {
18611  mp_print (mp, "secondarydef");
18612} else {
18613  mp_print (mp, "tertiarydef");
18614}
18615break;
18616case mp_iteration:
18617if (m == start_forever)
18618  mp_print (mp, "forever");
18619else if (m == end_for)
18620  mp_print (mp, "endfor");
18621else if (m == start_for)
18622  mp_print (mp, "for");
18623else
18624  mp_print (mp, "forsuffixes");
18625break;
18626
18627@ Different macro-absorbing operations have different syntaxes, but they
18628also have a lot in common. There is a list of special symbols that are to
18629be replaced by parameter tokens; there is a special command code that
18630ends the definition; the quotation conventions are identical.  Therefore
18631it makes sense to have most of the work done by a single subroutine. That
18632subroutine is called |scan_toks|.
18633
18634The first parameter to |scan_toks| is the command code that will
18635terminate scanning (either |macro_def| or |iteration|).
18636
18637The second parameter, |subst_list|, points to a (possibly empty) list
18638of non-symbolic nodes whose |info| and |value| fields specify symbol tokens
18639before and after replacement. The list will be returned to free storage
18640by |scan_toks|.
18641
18642The third parameter is simply appended to the token list that is built.
18643And the final parameter tells how many of the special operations
18644\.{\#\AT!}, \.{\AT!}, and \.{\AT!\#} are to be replaced by suffix parameters.
18645When such parameters are present, they are called \.{(SUFFIX0)},
18646\.{(SUFFIX1)}, and \.{(SUFFIX2)}.
18647
18648@<Types...@>=
18649typedef struct mp_subst_list_item {
18650  mp_name_type_type info_mod;
18651  quarterword value_mod;
18652  mp_sym info;
18653  halfword value_data;
18654  struct mp_subst_list_item *link;
18655} mp_subst_list_item;
18656
18657@
18658@c
18659static mp_node mp_scan_toks (MP mp, mp_command_code terminator,
18660                             mp_subst_list_item * subst_list, mp_node tail_end,
18661                             quarterword suffix_count) {
18662  mp_node p;    /* tail of the token list being built */
18663  mp_subst_list_item *q = NULL; /* temporary for link management */
18664  integer balance;      /* left delimiters minus right delimiters */
18665  halfword cur_data;
18666  quarterword cur_data_mod = 0;
18667  p = mp->hold_head;
18668  balance = 1;
18669  mp_link (mp->hold_head) = NULL;
18670  while (1) {
18671    get_t_next (mp);
18672    cur_data = -1;
18673    if (cur_sym() != NULL) {
18674      @<Substitute for |cur_sym|, if it's on the |subst_list|@>;
18675      if (cur_cmd() == terminator) {
18676        @<Adjust the balance; |break| if it's zero@>;
18677      } else if (cur_cmd() == mp_macro_special) {
18678        /* Handle quoted symbols, \.{\#\AT!}, \.{\AT!}, or \.{\AT!\#} */
18679        if (cur_mod() == quote) {
18680          get_t_next (mp);
18681        } else if (cur_mod() <= suffix_count) {
18682          cur_data = cur_mod() - 1;
18683          cur_data_mod = mp_suffix_sym;
18684        }
18685      }
18686    }
18687    if (cur_data != -1) {
18688      mp_node pp = mp_get_symbolic_node (mp);
18689      set_mp_sym_info (pp, cur_data);
18690      mp_name_type (pp) = cur_data_mod;
18691      mp_link (p) = pp;
18692    } else {
18693      mp_link (p) = mp_cur_tok (mp);
18694    }
18695    p = mp_link (p);
18696  }
18697  mp_link (p) = tail_end;
18698  while (subst_list) {
18699    q = subst_list->link;
18700    xfree (subst_list);
18701    subst_list = q;
18702  }
18703  return mp_link (mp->hold_head);
18704}
18705
18706@
18707@c
18708void mp_print_sym  (mp_sym sym) {
18709  printf("{type = %d, v = {type = %d, data = {indep = {scale = %d, serial = %d}, n = %d, str = %p, sym = %p, node = %p, p = %p}}, text = %p}\n", sym->type, sym->v.type, (int)sym->v.data.indep.scale, (int)sym->v.data.indep.serial,
18710    sym->v.data.n.type, sym->v.data.str, sym->v.data.sym, sym->v.data.node, sym->v.data.p, sym->text);
18711  if (is_number(sym->v.data.n)) {
18712      mp_number n = sym->v.data.n;
18713      printf("{data = {dval = %f, val = %d}, type = %d}\n", n.data.dval, n.data.val, n.type);
18714  }
18715  if (sym->text != NULL) {
18716     mp_string t = sym->text;
18717     printf ("{str = %p \"%s\", len = %d, refs = %d}\n", t->str, t->str, (int)t->len, t->refs);
18718  }
18719}
18720
18721@
18722@<Declarations@>=
18723void mp_print_sym  (mp_sym sym) ;
18724
18725@ @<Substitute for |cur_sym|...@>=
18726{
18727  q = subst_list;
18728  while (q != NULL) {
18729    if (q->info == cur_sym() && q->info_mod == cur_sym_mod()) {
18730      cur_data = q->value_data;
18731      cur_data_mod = q->value_mod;
18732      set_cur_cmd((mp_variable_type)mp_relax);
18733      break;
18734    }
18735    q = q->link;
18736  }
18737}
18738
18739
18740@ @<Adjust the balance; |break| if it's zero@>=
18741if (cur_mod() > 0) {
18742  incr (balance);
18743} else {
18744  decr (balance);
18745  if (balance == 0)
18746    break;
18747}
18748
18749
18750@ Four commands are intended to be used only within macro texts: \&{quote},
18751\.{\#\AT!}, \.{\AT!}, and \.{\AT!\#}. They are variants of a single command
18752code called |macro_special|.
18753
18754@d quote 0 /* |macro_special| modifier for \&{quote} */
18755@d macro_prefix 1 /* |macro_special| modifier for \.{\#\AT!} */
18756@d macro_at 2 /* |macro_special| modifier for \.{\AT!} */
18757@d macro_suffix 3 /* |macro_special| modifier for \.{\AT!\#} */
18758
18759@<Put each...@>=
18760mp_primitive (mp, "quote", mp_macro_special, quote);
18761@:quote_}{\&{quote} primitive@>;
18762mp_primitive (mp, "#@@", mp_macro_special, macro_prefix);
18763@:]]]\#\AT!_}{\.{\#\AT!} primitive@>;
18764mp_primitive (mp, "@@", mp_macro_special, macro_at);
18765@:]]]\AT!_}{\.{\AT!} primitive@>;
18766mp_primitive (mp, "@@#", mp_macro_special, macro_suffix);
18767@:]]]\AT!\#_}{\.{\AT!\#} primitive@>
18768
18769
18770@ @<Cases of |print_cmd...@>=
18771case mp_macro_special:
18772switch (m) {
18773case macro_prefix:
18774  mp_print (mp, "#@@");
18775  break;
18776case macro_at:
18777  mp_print_char (mp, xord ('@@'));
18778  break;
18779case macro_suffix:
18780  mp_print (mp, "@@#");
18781  break;
18782default:
18783  mp_print (mp, "quote");
18784  break;
18785}
18786break;
18787
18788@ Here is a routine that's used whenever a token will be redefined. If
18789the user's token is unredefinable, the `|mp->frozen_inaccessible|' token is
18790substituted; the latter is redefinable but essentially impossible to use,
18791hence \MP's tables won't get fouled up.
18792
18793@c
18794static void mp_get_symbol (MP mp) {                               /* sets |cur_sym| to a safe symbol */
18795RESTART:
18796  get_t_next (mp);
18797  if ((cur_sym() == NULL) || mp_is_frozen(mp, cur_sym())) {
18798    const char *hlp[] = {
18799           "Sorry: You can\'t redefine a number, string, or expr.",
18800           "I've inserted an inaccessible symbol so that your",
18801           "definition will be completed without mixing me up too badly.",
18802           NULL };
18803    if (cur_sym() != NULL)
18804      hlp[0] = "Sorry: You can\'t redefine my error-recovery tokens.";
18805    else if (cur_cmd() == mp_string_token)
18806      delete_str_ref (cur_mod_str());
18807    set_cur_sym(mp->frozen_inaccessible);
18808    mp_ins_error (mp, "Missing symbolic token inserted", hlp, true);
18809@.Missing symbolic token...@>;
18810    goto RESTART;
18811  }
18812}
18813
18814
18815@ Before we actually redefine a symbolic token, we need to clear away its
18816former value, if it was a variable. The following stronger version of
18817|get_symbol| does that.
18818
18819@c
18820static void mp_get_clear_symbol (MP mp) {
18821  mp_get_symbol (mp);
18822  mp_clear_symbol (mp, cur_sym(), false);
18823}
18824
18825
18826@ Here's another little subroutine; it checks that an equals sign
18827or assignment sign comes along at the proper place in a macro definition.
18828
18829@c
18830static void mp_check_equals (MP mp) {
18831  if (cur_cmd() != mp_equals)
18832    if (cur_cmd() != mp_assignment) {
18833      const char *hlp[] = {
18834             "The next thing in this `def' should have been `=',",
18835             "because I've already looked at the definition heading.",
18836             "But don't worry; I'll pretend that an equals sign",
18837             "was present. Everything from here to `enddef'",
18838             "will be the replacement text of this macro.",
18839             NULL };
18840      mp_back_error (mp, "Missing `=' has been inserted", hlp, true);
18841@.Missing `='@>;
18842    }
18843}
18844
18845
18846@ A \&{primarydef}, \&{secondarydef}, or \&{tertiarydef} is rather easily
18847handled now that we have |scan_toks|.  In this case there are
18848two parameters, which will be \.{EXPR0} and \.{EXPR1}.
18849
18850@c
18851static void mp_make_op_def (MP mp) {
18852  mp_command_code m;       /* the type of definition */
18853  mp_node q, r; /* for list manipulation */
18854  mp_subst_list_item *qm = NULL, *qn = NULL;
18855  m = cur_mod();
18856  mp_get_symbol (mp);
18857  qm = xmalloc (1, sizeof (mp_subst_list_item));
18858  qm->link = NULL;
18859  qm->info = cur_sym();
18860  qm->info_mod = cur_sym_mod();
18861  qm->value_data = 0;
18862  qm->value_mod = mp_expr_sym;
18863  mp_get_clear_symbol (mp);
18864  mp->warning_info = cur_sym();
18865  mp_get_symbol (mp);
18866  qn = xmalloc (1, sizeof (mp_subst_list_item));
18867  qn->link = qm;
18868  qn->info = cur_sym();
18869  qn->info_mod = cur_sym_mod();
18870  qn->value_data = 1;
18871  qn->value_mod = mp_expr_sym;
18872  get_t_next (mp);
18873  mp_check_equals (mp);
18874  mp->scanner_status = op_defining;
18875  q = mp_get_symbolic_node (mp);
18876  set_ref_count (q, 0);
18877  r = mp_get_symbolic_node (mp);
18878  mp_link (q) = r;
18879  set_mp_sym_info (r, mp_general_macro);
18880  mp_name_type (r) = mp_macro_sym;
18881  mp_link (r) = mp_scan_toks (mp, mp_macro_def, qn, NULL, 0);
18882  mp->scanner_status = normal;
18883  set_eq_type (mp->warning_info, m);
18884  set_equiv_node (mp->warning_info, q);
18885  mp_get_x_next (mp);
18886}
18887
18888
18889@ Parameters to macros are introduced by the keywords \&{expr},
18890\&{suffix}, \&{text}, \&{primary}, \&{secondary}, and \&{tertiary}.
18891
18892@<Put each...@>=
18893mp_primitive (mp, "expr", mp_param_type, mp_expr_param);
18894@:expr_}{\&{expr} primitive@>;
18895mp_primitive (mp, "suffix", mp_param_type, mp_suffix_param);
18896@:suffix_}{\&{suffix} primitive@>;
18897mp_primitive (mp, "text", mp_param_type, mp_text_param);
18898@:text_}{\&{text} primitive@>;
18899mp_primitive (mp, "primary", mp_param_type, mp_primary_macro);
18900@:primary_}{\&{primary} primitive@>;
18901mp_primitive (mp, "secondary", mp_param_type, mp_secondary_macro);
18902@:secondary_}{\&{secondary} primitive@>;
18903mp_primitive (mp, "tertiary", mp_param_type, mp_tertiary_macro);
18904@:tertiary_}{\&{tertiary} primitive@>
18905
18906
18907@ @<Cases of |print_cmd...@>=
18908case mp_param_type:
18909if (m == mp_expr_param)
18910  mp_print (mp, "expr");
18911else if (m == mp_suffix_param)
18912  mp_print (mp, "suffix");
18913else if (m == mp_text_param)
18914  mp_print (mp, "text");
18915else if (m == mp_primary_macro)
18916  mp_print (mp, "primary");
18917else if (m == mp_secondary_macro)
18918  mp_print (mp, "secondary");
18919else
18920  mp_print (mp, "tertiary");
18921break;
18922
18923@ Let's turn next to the more complex processing associated with \&{def}
18924and \&{vardef}. When the following procedure is called, |cur_mod|
18925should be either |start_def| or |var_def|.
18926
18927Note that although the macro scanner allows |def = := enddef| and
18928|def := = enddef|; |def = = enddef| and |def := := enddef| will generate
18929an error because by the time the second of the two identical tokens is
18930seen, its meaning has already become undefined.
18931
18932@c
18933static void mp_scan_def (MP mp) {
18934  int m;        /* the type of definition */
18935  int n;        /* the number of special suffix parameters */
18936  int k;        /* the total number of parameters */
18937  int c;        /* the kind of macro we're defining */
18938  mp_subst_list_item *r = NULL, *rp = NULL;     /* parameter-substitution list */
18939  mp_node q;    /* tail of the macro token list */
18940  mp_node p;    /* temporary storage */
18941  quarterword sym_type; /* |expr_sym|, |suffix_sym|, or |text_sym| */
18942  mp_sym l_delim, r_delim;      /* matching delimiters */
18943  m = cur_mod();
18944  c = mp_general_macro;
18945  mp_link (mp->hold_head) = NULL;
18946  q = mp_get_symbolic_node (mp);
18947  set_ref_count (q, 0);
18948  r = NULL;
18949  /* Scan the token or variable to be defined;
18950    set |n|, |scanner_status|, and |warning_info| */
18951  if (m == start_def) {
18952    mp_get_clear_symbol (mp);
18953    mp->warning_info = cur_sym();
18954    get_t_next (mp);
18955    mp->scanner_status = op_defining;
18956    n = 0;
18957    set_eq_type (mp->warning_info, mp_defined_macro);
18958    set_equiv_node (mp->warning_info, q);
18959  } else { /* |var_def| */
18960    p = mp_scan_declared_variable (mp);
18961    mp_flush_variable (mp, equiv_node (mp_sym_sym (p)), mp_link (p), true);
18962    mp->warning_info_node = mp_find_variable (mp, p);
18963    mp_flush_node_list (mp, p);
18964    if (mp->warning_info_node == NULL) {
18965      /* Change to `\.{a bad variable}' */
18966      const char *hlp[] = {
18967         "After `vardef a' you can\'t say `vardef a.b'.",
18968         "So I'll have to discard this definition.",
18969         NULL };
18970      mp_error (mp, "This variable already starts with a macro", hlp, true);
18971      mp->warning_info_node = mp->bad_vardef;
18972    }
18973    mp->scanner_status = var_defining;
18974    n = 2;
18975    if (cur_cmd() == mp_macro_special && cur_mod() == macro_suffix) {    /* \.{\AT!\#} */
18976      n = 3;
18977      get_t_next (mp);
18978    }
18979    mp_type (mp->warning_info_node) = (quarterword) (mp_unsuffixed_macro - 2 + n);
18980    /* |mp_suffixed_macro=mp_unsuffixed_macro+1| */
18981    set_value_node (mp->warning_info_node, q);
18982  }
18983
18984  k = n;
18985  if (cur_cmd() == mp_left_delimiter) {
18986    /* Absorb delimited parameters, putting them into lists |q| and |r| */
18987    do {
18988      l_delim = cur_sym();
18989      r_delim = equiv_sym (cur_sym());
18990      get_t_next (mp);
18991      if ((cur_cmd() == mp_param_type) && (cur_mod() == mp_expr_param)) {
18992        sym_type = mp_expr_sym;
18993      } else if ((cur_cmd() == mp_param_type) && (cur_mod() == mp_suffix_param)) {
18994        sym_type = mp_suffix_sym;
18995      } else if ((cur_cmd() == mp_param_type) && (cur_mod() == mp_text_param)) {
18996        sym_type = mp_text_sym;
18997      } else {
18998        const char *hlp[] = { "You should've had `expr' or `suffix' or `text' here.", NULL };
18999        mp_back_error (mp, "Missing parameter type; `expr' will be assumed", hlp, true);
19000        sym_type = mp_expr_sym;
19001      }
19002      /* Absorb parameter tokens for type |sym_type| */
19003      do {
19004        mp_link (q) = mp_get_symbolic_node (mp);
19005        q = mp_link (q);
19006        mp_name_type (q) = sym_type;
19007        set_mp_sym_info (q, k);
19008        mp_get_symbol (mp);
19009        rp = xmalloc (1, sizeof (mp_subst_list_item));
19010        rp->link = NULL;
19011        rp->value_data = k;
19012        rp->value_mod = sym_type;
19013        rp->info = cur_sym();
19014        rp->info_mod = cur_sym_mod();
19015        mp_check_param_size (mp, k);
19016        incr (k);
19017        rp->link = r;
19018        r = rp;
19019        get_t_next (mp);
19020      } while (cur_cmd() == mp_comma);
19021
19022      mp_check_delimiter (mp, l_delim, r_delim);
19023      get_t_next (mp);
19024    } while (cur_cmd() == mp_left_delimiter);
19025
19026  }
19027  if (cur_cmd() == mp_param_type) {
19028    /* Absorb undelimited parameters, putting them into list |r| */
19029    rp = xmalloc (1, sizeof (mp_subst_list_item));
19030    rp->link = NULL;
19031    rp->value_data = k;
19032    if (cur_mod() == mp_expr_param) {
19033      rp->value_mod = mp_expr_sym;
19034      c = mp_expr_macro;
19035    } else if (cur_mod() == mp_suffix_param) {
19036      rp->value_mod = mp_suffix_sym;
19037      c = mp_suffix_macro;
19038    } else if (cur_mod() == mp_text_param) {
19039      rp->value_mod = mp_text_sym;
19040      c = mp_text_macro;
19041    } else {
19042      c = cur_mod();
19043      rp->value_mod = mp_expr_sym;
19044    }
19045    mp_check_param_size (mp, k);
19046    incr (k);
19047    mp_get_symbol (mp);
19048    rp->info = cur_sym();
19049    rp->info_mod = cur_sym_mod();
19050    rp->link = r;
19051    r = rp;
19052    get_t_next (mp);
19053    if (c == mp_expr_macro) {
19054      if (cur_cmd() == mp_of_token) {
19055        c = mp_of_macro;
19056        rp = xmalloc (1, sizeof (mp_subst_list_item));
19057        rp->link = NULL;
19058        mp_check_param_size (mp, k);
19059        rp->value_data = k;
19060        rp->value_mod = mp_expr_sym;
19061        mp_get_symbol (mp);
19062        rp->info = cur_sym();
19063        rp->info_mod = cur_sym_mod();
19064        rp->link = r;
19065        r = rp;
19066        get_t_next (mp);
19067      }
19068    }
19069  }
19070  mp_check_equals (mp);
19071  p = mp_get_symbolic_node (mp);
19072  set_mp_sym_info (p, c);
19073  mp_name_type (p) = mp_macro_sym;
19074  mp_link (q) = p;
19075  /* Attach the replacement text to the tail of node |p| */
19076  /* We don't put `|mp->frozen_end_group|' into the replacement text of
19077     a \&{vardef}, because the user may want to redefine `\.{endgroup}'. */
19078  if (m == start_def) {
19079    mp_link (p) = mp_scan_toks (mp, mp_macro_def, r, NULL, (quarterword) n);
19080  } else {
19081    mp_node qq = mp_get_symbolic_node (mp);
19082    set_mp_sym_sym (qq, mp->bg_loc);
19083    mp_link (p) = qq;
19084    p = mp_get_symbolic_node (mp);
19085    set_mp_sym_sym (p, mp->eg_loc);
19086    mp_link (qq) = mp_scan_toks (mp, mp_macro_def, r, p, (quarterword) n);
19087  }
19088  if (mp->warning_info_node == mp->bad_vardef)
19089    mp_flush_token_list (mp, value_node (mp->bad_vardef));
19090  mp->scanner_status = normal;
19091  mp_get_x_next (mp);
19092}
19093
19094@ @<Glob...@>=
19095mp_sym bg_loc;
19096mp_sym eg_loc;  /* hash addresses of `\.{begingroup}' and `\.{endgroup}' */
19097
19098@ @<Initialize table entries@>=
19099mp->bad_vardef = mp_get_value_node (mp);
19100mp_name_type (mp->bad_vardef) = mp_root;
19101set_value_sym (mp->bad_vardef, mp->frozen_bad_vardef);
19102
19103@ @<Free table entries@>=
19104mp_free_value_node (mp, mp->bad_vardef);
19105
19106
19107@* Expanding the next token.
19108Only a few command codes |<min_command| can possibly be returned by
19109|get_t_next|; in increasing order, they are
19110|if_test|, |fi_or_else|, |input|, |iteration|, |repeat_loop|,
19111|exit_test|, |relax|, |scan_tokens|, |run_script|, |expand_after|, and |defined_macro|.
19112
19113\MP\ usually gets the next token of input by saying |get_x_next|. This is
19114like |get_t_next| except that it keeps getting more tokens until
19115finding |cur_cmd>=min_command|. In other words, |get_x_next| expands
19116macros and removes conditionals or iterations or input instructions that
19117might be present.
19118
19119It follows that |get_x_next| might invoke itself recursively. In fact,
19120there is massive recursion, since macro expansion can involve the
19121scanning of arbitrarily complex expressions, which in turn involve
19122macro expansion and conditionals, etc.
19123@^recursion@>
19124
19125Therefore it's necessary to declare a whole bunch of |forward|
19126procedures at this point, and to insert some other procedures
19127that will be invoked by |get_x_next|.
19128
19129@<Declarations@>=
19130static void mp_scan_primary (MP mp);
19131static void mp_scan_secondary (MP mp);
19132static void mp_scan_tertiary (MP mp);
19133static void mp_scan_expression (MP mp);
19134static void mp_scan_suffix (MP mp);
19135static void mp_pass_text (MP mp);
19136static void mp_conditional (MP mp);
19137static void mp_start_input (MP mp);
19138static void mp_begin_iteration (MP mp);
19139static void mp_resume_iteration (MP mp);
19140static void mp_stop_iteration (MP mp);
19141
19142@ A recursion depth counter is used to discover infinite recursions.
19143(Near) infinite recursion is a problem because it translates into
19144C function calls that eat up the available call stack. A better solution
19145would be to depend on signal trapping, but that is problematic when
19146Metapost is used as a library.
19147
19148@<Global...@>=
19149int expand_depth_count; /* current expansion depth */
19150int expand_depth;       /* current expansion depth */
19151
19152@ The limit is set at |10000|, which should be enough to allow
19153normal usages of metapost while preventing the most obvious
19154crashes on most all operating systems, but the value can be
19155raised if the runtime system allows a larger C stack.
19156@^system dependencies@>
19157
19158@<Set initial...@>=
19159mp->expand_depth = 10000;
19160
19161@ Even better would be if the system allows discovery of the amount of
19162space available on the call stack.
19163@^system dependencies@>
19164
19165In any case, when the limit is crossed, that is a fatal error.
19166
19167@d check_expansion_depth()  if (++mp->expand_depth_count >= mp->expand_depth)
19168                              mp_expansion_depth_error(mp)
19169
19170@c
19171static void mp_expansion_depth_error (MP mp) {
19172    const char *hlp[] = {
19173         "Recursive macro expansion cannot be unlimited because of runtime",
19174         "stack constraints. The limit is 10000 recursion levels in total.",
19175         NULL };
19176    if ( mp->interaction==mp_error_stop_mode )
19177      mp->interaction=mp_scroll_mode; /* no more interaction */
19178    if ( mp->log_opened )
19179      mp_error(mp, "Maximum expansion depth reached", hlp, true);
19180    mp->history=mp_fatal_error_stop;
19181    mp_jump_out(mp);
19182}
19183
19184
19185@ An auxiliary subroutine called |expand| is used by |get_x_next|
19186when it has to do exotic expansion commands.
19187
19188@c
19189static void mp_expand (MP mp) {
19190  size_t k;     /* something that we hope is |<=buf_size| */
19191  size_t j;     /* index into |str_pool| */
19192  check_expansion_depth();
19193  if (number_greater (internal_value (mp_tracing_commands), unity_t))
19194    if (cur_cmd() != mp_defined_macro)
19195      show_cur_cmd_mod;
19196  switch (cur_cmd()) {
19197  case mp_if_test:
19198    mp_conditional (mp);        /* this procedure is discussed in Part 36 below */
19199    break;
19200  case mp_fi_or_else:
19201    @<Terminate the current conditional and skip to \&{fi}@>;
19202    break;
19203  case mp_input:
19204    @<Initiate or terminate input from a file@>;
19205    break;
19206  case mp_iteration:
19207    if (cur_mod() == end_for) {
19208      @<Scold the user for having an extra \&{endfor}@>;
19209    } else {
19210      mp_begin_iteration (mp);  /* this procedure is discussed in Part 37 below */
19211    }
19212    break;
19213  case mp_repeat_loop:
19214    @<Repeat a loop@>;
19215    break;
19216  case mp_exit_test:
19217    @<Exit a loop if the proper time has come@>;
19218    break;
19219  case mp_relax:
19220    break;
19221  case mp_expand_after:
19222    @<Expand the token after the next token@>;
19223    break;
19224  case mp_scan_tokens:
19225    @<Put a string into the input buffer@>;
19226    break;
19227  case mp_runscript:
19228    @<Put a script result string into the input buffer@>;
19229    break;
19230  case mp_maketext:
19231    @<Put a maketext result string into the input buffer@>;
19232    break;
19233  case mp_defined_macro:
19234    mp_macro_call (mp, cur_mod_node(), NULL, cur_sym());
19235    break;
19236  default:
19237    break; /* make the compiler happy */
19238  };                            /* there are no other cases */
19239  mp->expand_depth_count--;
19240}
19241
19242
19243@ @<Scold the user...@>=
19244{
19245  const char *hlp[] = {
19246         "I'm not currently working on a for loop,",
19247         "so I had better not try to end anything.",
19248         NULL };
19249  mp_error (mp, "Extra `endfor'", hlp, true);
19250@.Extra `endfor'@>;
19251}
19252
19253
19254@ The processing of \&{input} involves the |start_input| subroutine,
19255which will be declared later; the processing of \&{endinput} is trivial.
19256
19257@<Put each...@>=
19258mp_primitive (mp, "input", mp_input, 0);
19259@:input_}{\&{input} primitive@>;
19260mp_primitive (mp, "endinput", mp_input, 1);
19261@:end_input_}{\&{endinput} primitive@>
19262
19263
19264@ @<Cases of |print_cmd_mod|...@>=
19265case mp_input:
19266if (m == 0)
19267  mp_print (mp, "input");
19268else
19269  mp_print (mp, "endinput");
19270break;
19271
19272@ @<Initiate or terminate input...@>=
19273if (cur_mod() > 0)
19274  mp->force_eof = true;
19275else
19276  mp_start_input (mp)
19277
19278
19279@ We'll discuss the complicated parts of loop operations later. For now
19280it suffices to know that there's a global variable called |loop_ptr|
19281that will be |NULL| if no loop is in progress.
19282
19283@<Repeat a loop@>=
19284{
19285  while (token_state && (nloc == NULL))
19286    mp_end_token_list (mp);     /* conserve stack space */
19287  if (mp->loop_ptr == NULL) {
19288    const char *hlp[] = {
19289           "I'm confused; after exiting from a loop, I still seem",
19290           "to want to repeat it. I'll try to forget the problem.",
19291           NULL };
19292    mp_error (mp, "Lost loop", hlp, true);
19293@.Lost loop@>;
19294  } else {
19295    mp_resume_iteration (mp);   /* this procedure is in Part 37 below */
19296  }
19297}
19298
19299
19300@ @<Exit a loop if the proper time has come@>=
19301{
19302  mp_get_boolean (mp);
19303  if (number_greater (internal_value (mp_tracing_commands),  unity_t))
19304    mp_show_cmd_mod (mp, mp_nullary, cur_exp_value_boolean ());
19305  if (cur_exp_value_boolean () == mp_true_code) {
19306    if (mp->loop_ptr == NULL) {
19307      const char *hlp[] = {
19308          "Why say `exitif' when there's nothing to exit from?",
19309          NULL };
19310      if (cur_cmd() == mp_semicolon)
19311        mp_error (mp, "No loop is in progress", hlp, true);
19312      else
19313        mp_back_error (mp, "No loop is in progress", hlp, true);
19314@.No loop is in progress@>;
19315    } else {
19316      @<Exit prematurely from an iteration@>;
19317    }
19318  } else if (cur_cmd() != mp_semicolon) {
19319    const char *hlp[] = {
19320           "After `exitif <boolean exp>' I expect to see a semicolon.",
19321           "I shall pretend that one was there.",
19322           NULL };
19323    mp_back_error (mp, "Missing `;' has been inserted", hlp, true);
19324@.Missing `;'@>;
19325  }
19326}
19327
19328
19329@ Here we use the fact that |forever_text| is the only |token_type| that
19330is less than |loop_text|.
19331
19332@<Exit prematurely...@>=
19333{
19334  mp_node p = NULL;
19335  do {
19336    if (file_state) {
19337      mp_end_file_reading (mp);
19338    } else {
19339      if (token_type <= loop_text)
19340        p = nstart;
19341      mp_end_token_list (mp);
19342    }
19343  } while (p == NULL);
19344  if (p != mp->loop_ptr->info)
19345    mp_fatal_error (mp, "*** (loop confusion)");
19346@.loop confusion@>;
19347  mp_stop_iteration (mp);       /* this procedure is in Part 34 below */
19348}
19349
19350
19351@ @<Expand the token after the next token@>=
19352{
19353  mp_node p;
19354  get_t_next (mp);
19355  p = mp_cur_tok (mp);
19356  get_t_next (mp);
19357  if (cur_cmd() < mp_min_command)
19358    mp_expand (mp);
19359  else
19360    mp_back_input (mp);
19361  back_list (p);
19362}
19363
19364
19365@ @<Put a string into the input buffer@>=
19366{
19367  mp_get_x_next (mp);
19368  mp_scan_primary (mp);
19369  if (mp->cur_exp.type != mp_string_type) {
19370    mp_value new_expr;
19371    const char *hlp[] = {
19372           "I'm going to flush this expression, since",
19373           "scantokens should be followed by a known string.",
19374           NULL };
19375    memset(&new_expr,0,sizeof(mp_value));
19376    new_number(new_expr.data.n);
19377    mp_disp_err (mp, NULL);
19378    mp_back_error (mp, "Not a string", hlp, true);
19379@.Not a string@>;
19380    mp_get_x_next (mp);
19381    mp_flush_cur_exp (mp, new_expr);
19382  } else {
19383    mp_back_input (mp);
19384    if (cur_exp_str ()->len > 0)
19385      @<Pretend we're reading a new one-line file@>;
19386  }
19387}
19388
19389@ @<Run a script@>=
19390if (s != NULL) {
19391    int k ;
19392    size_t size = strlen(s);
19393    memset(&new_expr,0,sizeof(mp_value));
19394    new_number(new_expr.data.n);
19395    mp_begin_file_reading (mp);
19396    name = is_scantok;
19397    mp->last = mp->first;
19398    k = mp->first + size;
19399    if (k >= mp->max_buf_stack) {
19400        while (k >= mp->buf_size) {
19401            mp_reallocate_buffer (mp, (mp->buf_size + (mp->buf_size / 4)));
19402        }
19403        mp->max_buf_stack = k + 1;
19404    }
19405    limit = (halfword) k;
19406    (void) memcpy ((mp->buffer + mp->first), s, size);
19407    free(s);
19408    mp->buffer[limit] = xord ('%');
19409    mp->first = (size_t) (limit + 1);
19410    loc = start;
19411    mp_flush_cur_exp (mp, new_expr);
19412}
19413
19414@ @<Put a script result string into the input buffer@>=
19415{
19416
19417    if (mp->extensions == 0) {
19418        return ;
19419    }
19420    mp_get_x_next (mp);
19421    mp_scan_primary (mp);
19422    if (mp->cur_exp.type != mp_string_type) {
19423        mp_value new_expr;
19424        const char *hlp[] = {
19425           "I'm going to flush this expression, since",
19426           "runscript should be followed by a known string.",
19427           NULL };
19428        memset(&new_expr,0,sizeof(mp_value));
19429        new_number(new_expr.data.n);
19430        mp_disp_err (mp, NULL);
19431        mp_back_error (mp, "Not a string", hlp, true);
19432        @.Not a string@>;
19433        mp_get_x_next (mp);
19434        mp_flush_cur_exp (mp, new_expr);
19435    } else {
19436        mp_back_input (mp);
19437        if (cur_exp_str ()->len > 0) {
19438            mp_value new_expr;
19439            char *s = mp->run_script(mp,(const char*) cur_exp_str()->str) ;
19440            @<Run a script@>
19441        }
19442    }
19443}
19444
19445@ @<Pass btex ... etex to script@>=
19446{
19447    int first ;
19448    while ((loc < limit - 4) && (mp->buffer[loc] == ' ')) {
19449        incr(loc);
19450    }
19451    first = loc ;
19452    if (mp->buffer[loc-1] == ' ') {
19453        decr(loc);
19454    }
19455    while (loc < limit - 5) {
19456        if (mp->buffer[loc] == ' ') {
19457            incr(loc);
19458            if (mp->buffer[loc] == 'e') {
19459                incr(loc);
19460                if (mp->buffer[loc] == 't') {
19461                    incr(loc) ;
19462                    if (mp->buffer[loc] == 'e') {
19463                        incr(loc) ;
19464                        if (mp->buffer[loc] == 'x') {
19465                            /* start action */
19466                            char *s, *txt ;
19467                            int size ;
19468                            mp_value new_expr;
19469                            size = loc - first + 1 - 4 ;
19470                            if (size < 0) {
19471                                size = 0 ;
19472                            } else {
19473                                while ((size > 1) && (mp->buffer[first+size-1] == ' ')) {
19474                                    decr(size);
19475                                }
19476                            }
19477                            txt = malloc(size+1);
19478                            if (size > 0) {
19479                                (void) memcpy (txt, mp->buffer + first, size);
19480                            }
19481                            txt[size] = '\0';
19482                            incr(loc);
19483                            s = mp->make_text(mp,txt,(cur_mod() == verbatim_code)) ; /* we could pass the size */
19484                            @<Run a script@>
19485                            /* done */
19486                            free(txt);
19487                            break ;
19488                        } else {
19489                      //      decr(loc) ;
19490                        }
19491                    }
19492                }
19493            }
19494        } else {
19495            incr(loc);
19496        }
19497    }
19498}
19499
19500@ @<Put a maketext result string into the input buffer@>=
19501{
19502    if (mp->extensions == 0) {
19503        return ;
19504    }
19505    mp_get_x_next (mp);
19506    mp_scan_primary (mp);
19507    if (mp->cur_exp.type != mp_string_type) {
19508        mp_value new_expr;
19509        const char *hlp[] = {
19510           "I'm going to flush this expression, since",
19511           "makete should be followed by a known string.",
19512           NULL };
19513        memset(&new_expr,0,sizeof(mp_value));
19514        new_number(new_expr.data.n);
19515        mp_disp_err (mp, NULL);
19516        mp_back_error (mp, "Not a string", hlp, true);
19517        @.Not a string@>;
19518        mp_get_x_next (mp);
19519        mp_flush_cur_exp (mp, new_expr);
19520    } else {
19521        mp_back_input (mp);
19522        if (cur_exp_str ()->len > 0) {
19523            mp_value new_expr;
19524            char *s = mp->make_text(mp,(const char*) cur_exp_str()->str,0) ;
19525            @<Run a script@>
19526        }
19527    }
19528}
19529
19530@ @<Pretend we're reading a new one-line file@>=
19531{
19532  mp_value new_expr;
19533  memset(&new_expr,0,sizeof(mp_value));
19534  new_number(new_expr.data.n);
19535  mp_begin_file_reading (mp);
19536  name = is_scantok;
19537  k = mp->first + (size_t) cur_exp_str ()->len;
19538  if (k >= mp->max_buf_stack) {
19539    while (k >= mp->buf_size) {
19540      mp_reallocate_buffer (mp, (mp->buf_size + (mp->buf_size / 4)));
19541    }
19542    mp->max_buf_stack = k + 1;
19543  }
19544  j = 0;
19545  limit = (halfword) k;
19546  while (mp->first < (size_t) limit) {
19547    mp->buffer[mp->first] = *(cur_exp_str ()->str + j);
19548    j++;
19549    incr (mp->first);
19550  }
19551  mp->buffer[limit] = xord ('%');
19552  mp->first = (size_t) (limit + 1);
19553  loc = start;
19554  mp_flush_cur_exp (mp, new_expr);
19555}
19556
19557
19558@ Here finally is |get_x_next|.
19559
19560The expression scanning routines to be considered later
19561communicate via the global quantities |cur_type| and |cur_exp|;
19562we must be very careful to save and restore these quantities while
19563macros are being expanded.
19564@^inner loop@>
19565
19566@<Declarations@>=
19567static void mp_get_x_next (MP mp);
19568
19569@ @c
19570void mp_get_x_next (MP mp) {
19571  mp_node save_exp;     /* a capsule to save |cur_type| and |cur_exp| */
19572  get_t_next (mp);
19573  if (cur_cmd() < mp_min_command) {
19574    save_exp = mp_stash_cur_exp (mp);
19575    do {
19576      if (cur_cmd() == mp_defined_macro)
19577        mp_macro_call (mp, cur_mod_node(), NULL, cur_sym());
19578      else
19579        mp_expand (mp);
19580      get_t_next (mp);
19581    } while (cur_cmd() < mp_min_command);
19582    mp_unstash_cur_exp (mp, save_exp);  /* that restores |cur_type| and |cur_exp| */
19583  }
19584}
19585
19586
19587@ Now let's consider the |macro_call| procedure, which is used to start up
19588all user-defined macros. Since the arguments to a macro might be expressions,
19589|macro_call| is recursive.
19590@^recursion@>
19591
19592The first parameter to |macro_call| points to the reference count of the
19593token list that defines the macro. The second parameter contains any
19594arguments that have already been parsed (see below).  The third parameter
19595points to the symbolic token that names the macro. If the third parameter
19596is |NULL|, the macro was defined by \&{vardef}, so its name can be
19597reconstructed from the prefix and ``at'' arguments found within the
19598second parameter.
19599
19600What is this second parameter? It's simply a linked list of symbolic items,
19601whose |info| fields point to the arguments. In other words, if |arg_list=NULL|,
19602no arguments have been scanned yet; otherwise |mp_info(arg_list)| points to
19603the first scanned argument, and |mp_link(arg_list)| points to the list of
19604further arguments (if any).
19605
19606Arguments of type \&{expr} are so-called capsules, which we will
19607discuss later when we concentrate on expressions; they can be
19608recognized easily because their |link| field is |void|. Arguments of type
19609\&{suffix} and \&{text} are token lists without reference counts.
19610
19611@ After argument scanning is complete, the arguments are moved to the
19612|param_stack|. (They can't be put on that stack any sooner, because
19613the stack is growing and shrinking in unpredictable ways as more arguments
19614are being acquired.)  Then the macro body is fed to the scanner; i.e.,
19615the replacement text of the macro is placed at the top of the \MP's
19616input stack, so that |get_t_next| will proceed to read it next.
19617
19618@<Declarations@>=
19619static void mp_macro_call (MP mp, mp_node def_ref, mp_node arg_list,
19620                           mp_sym macro_name);
19621
19622@ @c
19623void mp_macro_call (MP mp, mp_node def_ref, mp_node arg_list, mp_sym macro_name) {
19624  /* invokes a user-defined control sequence */
19625  mp_node r;    /* current node in the macro's token list */
19626  mp_node p, q; /* for list manipulation */
19627  integer n;    /* the number of arguments */
19628  mp_node tail = 0;     /* tail of the argument list */
19629  mp_sym l_delim = NULL, r_delim = NULL;        /* a delimiter pair */
19630  r = mp_link (def_ref);
19631  add_mac_ref (def_ref);
19632  if (arg_list == NULL) {
19633    n = 0;
19634  } else {
19635    @<Determine the number |n| of arguments already supplied,
19636    and set |tail| to the tail of |arg_list|@>;
19637  }
19638  if (number_positive (internal_value (mp_tracing_macros))) {
19639    @<Show the text of the macro being expanded, and the existing arguments@>;
19640  }
19641  @<Scan the remaining arguments, if any; set |r| to the first token
19642    of the replacement text@>;
19643  @<Feed the arguments and replacement text to the scanner@>;
19644}
19645
19646
19647@ @<Show the text of the macro...@>=
19648mp_begin_diagnostic (mp);
19649mp_print_ln (mp);
19650mp_print_macro_name (mp, arg_list, macro_name);
19651if (n == 3)
19652  mp_print (mp, "@@#");         /* indicate a suffixed macro */
19653mp_show_macro (mp, def_ref, NULL, 100000);
19654if (arg_list != NULL) {
19655  n = 0;
19656  p = arg_list;
19657  do {
19658    q = (mp_node)mp_sym_sym (p);
19659    mp_print_arg (mp, q, n, 0, 0);
19660    incr (n);
19661    p = mp_link (p);
19662  } while (p != NULL);
19663}
19664mp_end_diagnostic (mp, false)
19665
19666
19667@ @<Declarations@>=
19668static void mp_print_macro_name (MP mp, mp_node a, mp_sym n);
19669
19670@ @c
19671void mp_print_macro_name (MP mp, mp_node a, mp_sym n) {
19672  mp_node p, q; /* they traverse the first part of |a| */
19673  if (n != NULL) {
19674    mp_print_text (n);
19675  } else {
19676    p = (mp_node)mp_sym_sym (a);
19677    if (p == NULL) {
19678      mp_print_text (mp_sym_sym ((mp_node)mp_sym_sym (mp_link (a))));
19679    } else {
19680      q = p;
19681      while (mp_link (q) != NULL)
19682        q = mp_link (q);
19683      mp_link (q) = (mp_node)mp_sym_sym (mp_link (a));
19684      mp_show_token_list (mp, p, NULL, 1000, 0);
19685      mp_link (q) = NULL;
19686    }
19687  }
19688}
19689
19690
19691@ @<Declarations@>=
19692static void mp_print_arg (MP mp, mp_node q, integer n, halfword b,
19693                          quarterword bb);
19694
19695@ @c
19696void mp_print_arg (MP mp, mp_node q, integer n, halfword b, quarterword bb) {
19697  if (q && mp_link (q) == MP_VOID) {
19698    mp_print_nl (mp, "(EXPR");
19699  } else {
19700    if ((bb < mp_text_sym) && (b != mp_text_macro))
19701      mp_print_nl (mp, "(SUFFIX");
19702    else
19703      mp_print_nl (mp, "(TEXT");
19704  }
19705  mp_print_int (mp, n);
19706  mp_print (mp, ")<-");
19707  if (q && mp_link (q) == MP_VOID)
19708    mp_print_exp (mp, q, 1);
19709  else
19710    mp_show_token_list (mp, q, NULL, 1000, 0);
19711}
19712
19713
19714@ @<Determine the number |n| of arguments already supplied...@>=
19715{
19716  n = 1;
19717  tail = arg_list;
19718  while (mp_link (tail) != NULL) {
19719    incr (n);
19720    tail = mp_link (tail);
19721  }
19722}
19723
19724
19725@ @<Scan the remaining arguments, if any; set |r|...@>=
19726set_cur_cmd(mp_comma + 1);        /* anything |<>comma| will do */
19727while (mp_name_type (r) == mp_expr_sym ||
19728       mp_name_type (r) == mp_suffix_sym || mp_name_type (r) == mp_text_sym) {
19729  @<Scan the delimited argument represented by |mp_sym_info(r)|@>;
19730  r = mp_link (r);
19731}
19732if (cur_cmd() == mp_comma) {
19733  char msg[256];
19734  const char *hlp[] = {
19735         "I'm going to assume that the comma I just read was a",
19736         "right delimiter, and then I'll begin expanding the macro.",
19737         "You might want to delete some tokens before continuing.",
19738         NULL };
19739  mp_string rname;
19740  int old_setting = mp->selector;
19741  mp->selector = new_string;
19742  mp_print_macro_name (mp, arg_list, macro_name);
19743  rname = mp_make_string(mp);
19744  mp->selector = old_setting;
19745  mp_snprintf (msg, 256, "Too many arguments to %s; Missing `%s' has been inserted",
19746	       mp_str(mp, rname), mp_str(mp, text(r_delim)));
19747  delete_str_ref(rname);
19748@.Too many arguments...@>;
19749@.Missing `)'...@>;
19750  mp_error (mp, msg, hlp, true);
19751}
19752if (mp_sym_info (r) != mp_general_macro) {
19753  @<Scan undelimited argument(s)@>;
19754}
19755r = mp_link (r)
19756
19757
19758@ At this point, the reader will find it advisable to review the explanation
19759of token list format that was presented earlier, paying special attention to
19760the conventions that apply only at the beginning of a macro's token list.
19761
19762On the other hand, the reader will have to take the expression-parsing
19763aspects of the following program on faith; we will explain |cur_type|
19764and |cur_exp| later. (Several things in this program depend on each other,
19765and it's necessary to jump into the circle somewhere.)
19766
19767@<Scan the delimited argument represented by |mp_sym_info(r)|@>=
19768if (cur_cmd() != mp_comma) {
19769  mp_get_x_next (mp);
19770  if (cur_cmd() != mp_left_delimiter) {
19771    char msg[256];
19772    const char *hlp[] = {
19773           "That macro has more parameters than you thought.",
19774           "I'll continue by pretending that each missing argument",
19775           "is either zero or null.",
19776           NULL };
19777    mp_string sname;
19778    int old_setting = mp->selector;
19779    mp->selector = new_string;
19780    mp_print_macro_name (mp, arg_list, macro_name);
19781    sname = mp_make_string(mp);
19782    mp->selector = old_setting;
19783    mp_snprintf (msg, 256, "Missing argument to %s", mp_str(mp, sname));
19784@.Missing argument...@>;
19785    delete_str_ref(sname);
19786    if (mp_name_type (r) == mp_suffix_sym || mp_name_type (r) == mp_text_sym) {
19787      set_cur_exp_value_number (zero_t);  /* todo: this was |null| */
19788      mp->cur_exp.type = mp_token_list;
19789    } else {
19790      set_cur_exp_value_number (zero_t);
19791      mp->cur_exp.type = mp_known;
19792    }
19793    mp_back_error (mp, msg, hlp, true);
19794    set_cur_cmd((mp_variable_type)mp_right_delimiter);
19795    goto FOUND;
19796  }
19797  l_delim = cur_sym();
19798  r_delim = equiv_sym (cur_sym());
19799}
19800@<Scan the argument represented by |mp_sym_info(r)|@>;
19801if (cur_cmd() != mp_comma)
19802  @<Check that the proper right delimiter was present@>;
19803FOUND:
19804@<Append the current expression to |arg_list|@>
19805
19806
19807@ @<Check that the proper right delim...@>=
19808if ((cur_cmd() != mp_right_delimiter) || (equiv_sym (cur_sym()) != l_delim)) {
19809  if (mp_name_type (mp_link (r)) == mp_expr_sym ||
19810      mp_name_type (mp_link (r)) == mp_suffix_sym ||
19811      mp_name_type (mp_link (r)) == mp_text_sym) {
19812    const char *hlp[] = {
19813           "I've finished reading a macro argument and am about to",
19814           "read another; the arguments weren't delimited correctly.",
19815           "You might want to delete some tokens before continuing.",
19816           NULL };
19817    mp_back_error (mp, "Missing `,' has been inserted", hlp, true);
19818@.Missing `,'@>;
19819    set_cur_cmd((mp_variable_type)mp_comma);
19820  } else {
19821    char msg[256];
19822    const char *hlp[] = {
19823           "I've gotten to the end of the macro parameter list.",
19824           "You might want to delete some tokens before continuing.",
19825           NULL };
19826    mp_snprintf(msg, 256, "Missing `%s' has been inserted", mp_str(mp, text(r_delim)));
19827@.Missing `)'@>;
19828    mp_back_error (mp, msg, hlp, true);
19829  }
19830}
19831
19832@ A \&{suffix} or \&{text} parameter will have been scanned as
19833a token list pointed to by |cur_exp|, in which case we will have
19834|cur_type=token_list|.
19835
19836@<Append the current expression to |arg_list|@>=
19837{
19838  p = mp_get_symbolic_node (mp);
19839  if (mp->cur_exp.type == mp_token_list)
19840    set_mp_sym_sym (p, mp->cur_exp.data.node);
19841  else
19842    set_mp_sym_sym (p, mp_stash_cur_exp (mp));
19843  if (number_positive (internal_value (mp_tracing_macros))) {
19844    mp_begin_diagnostic (mp);
19845    mp_print_arg (mp, (mp_node)mp_sym_sym (p), n, mp_sym_info (r), mp_name_type (r));
19846    mp_end_diagnostic (mp, false);
19847  }
19848  if (arg_list == NULL) {
19849    arg_list = p;
19850  } else {
19851    mp_link (tail) = p;
19852  }
19853  tail = p;
19854  incr (n);
19855}
19856
19857
19858@ @<Scan the argument represented by |mp_sym_info(r)|@>=
19859if (mp_name_type (r) == mp_text_sym) {
19860  mp_scan_text_arg (mp, l_delim, r_delim);
19861} else {
19862  mp_get_x_next (mp);
19863  if (mp_name_type (r) == mp_suffix_sym)
19864    mp_scan_suffix (mp);
19865  else
19866    mp_scan_expression (mp);
19867}
19868
19869
19870@ The parameters to |scan_text_arg| are either a pair of delimiters
19871or zero; the latter case is for undelimited text arguments, which
19872end with the first semicolon or \&{endgroup} or \&{end} that is not
19873contained in a group.
19874
19875@<Declarations@>=
19876static void mp_scan_text_arg (MP mp, mp_sym l_delim, mp_sym r_delim);
19877
19878@ @c
19879void mp_scan_text_arg (MP mp, mp_sym l_delim, mp_sym r_delim) {
19880  integer balance;      /* excess of |l_delim| over |r_delim| */
19881  mp_node p;    /* list tail */
19882  mp->warning_info = l_delim;
19883  mp->scanner_status = absorbing;
19884  p = mp->hold_head;
19885  balance = 1;
19886  mp_link (mp->hold_head) = NULL;
19887  while (1) {
19888    get_t_next (mp);
19889    if (l_delim == NULL) {
19890      @<Adjust the balance for an undelimited argument; |break| if done@>;
19891    } else {
19892      @<Adjust the balance for a delimited argument; |break| if done@>;
19893    }
19894    mp_link (p) = mp_cur_tok (mp);
19895    p = mp_link (p);
19896  }
19897  set_cur_exp_node (mp_link (mp->hold_head));
19898  mp->cur_exp.type = mp_token_list;
19899  mp->scanner_status = normal;
19900}
19901
19902
19903@ @<Adjust the balance for a delimited argument...@>=
19904if (cur_cmd() == mp_right_delimiter) {
19905  if (equiv_sym (cur_sym()) == l_delim) {
19906    decr (balance);
19907    if (balance == 0)
19908      break;
19909  }
19910} else if (cur_cmd() == mp_left_delimiter) {
19911  if (equiv_sym (cur_sym()) == r_delim)
19912    incr (balance);
19913}
19914
19915@ @<Adjust the balance for an undelimited...@>=
19916if (mp_end_of_statement) {         /* |cur_cmd=semicolon|, |end_group|, or |stop| */
19917  if (balance == 1) {
19918    break;
19919  } else {
19920    if (cur_cmd() == mp_end_group)
19921      decr (balance);
19922  }
19923} else if (cur_cmd() == mp_begin_group) {
19924  incr (balance);
19925}
19926
19927@ @<Scan undelimited argument(s)@>=
19928{
19929  if (mp_sym_info (r) < mp_text_macro) {
19930    mp_get_x_next (mp);
19931    if (mp_sym_info (r) != mp_suffix_macro) {
19932      if ((cur_cmd() == mp_equals) || (cur_cmd() == mp_assignment))
19933        mp_get_x_next (mp);
19934    }
19935  }
19936  switch (mp_sym_info (r)) {
19937  case mp_primary_macro:
19938    mp_scan_primary (mp);
19939    break;
19940  case mp_secondary_macro:
19941    mp_scan_secondary (mp);
19942    break;
19943  case mp_tertiary_macro:
19944    mp_scan_tertiary (mp);
19945    break;
19946  case mp_expr_macro:
19947    mp_scan_expression (mp);
19948    break;
19949  case mp_of_macro:
19950    @<Scan an expression followed by `\&{of} $\langle$primary$\rangle$'@>;
19951    break;
19952  case mp_suffix_macro:
19953    @<Scan a suffix with optional delimiters@>;
19954    break;
19955  case mp_text_macro:
19956    mp_scan_text_arg (mp, NULL, NULL);
19957    break;
19958  }                             /* there are no other cases */
19959  mp_back_input (mp);
19960  @<Append the current expression to |arg_list|@>;
19961}
19962
19963
19964@ @<Scan an expression followed by `\&{of} $\langle$primary$\rangle$'@>=
19965{
19966  mp_scan_expression (mp);
19967  p = mp_get_symbolic_node (mp);
19968  set_mp_sym_sym (p, mp_stash_cur_exp (mp));
19969  if (number_positive (internal_value (mp_tracing_macros))) {
19970    mp_begin_diagnostic (mp);
19971    mp_print_arg (mp, (mp_node)mp_sym_sym (p), n, 0, 0);
19972    mp_end_diagnostic (mp, false);
19973  }
19974  if (arg_list == NULL)
19975    arg_list = p;
19976  else
19977    mp_link (tail) = p;
19978  tail = p;
19979  incr (n);
19980  if (cur_cmd() != mp_of_token) {
19981    char msg[256];
19982    mp_string sname;
19983    const char *hlp[] = {
19984        "I've got the first argument; will look now for the other.",
19985        NULL };
19986    int old_setting = mp->selector;
19987    mp->selector = new_string;
19988    mp_print_macro_name (mp, arg_list, macro_name);
19989    sname = mp_make_string(mp);
19990    mp->selector = old_setting;
19991    mp_snprintf (msg, 256, "Missing `of' has been inserted for %s", mp_str(mp, sname));
19992    delete_str_ref(sname);
19993@.Missing `of'@>;
19994    mp_back_error (mp, msg, hlp, true);
19995  }
19996  mp_get_x_next (mp);
19997  mp_scan_primary (mp);
19998}
19999
20000
20001@ @<Scan a suffix with optional delimiters@>=
20002{
20003  if (cur_cmd() != mp_left_delimiter) {
20004    l_delim = NULL;
20005  } else {
20006    l_delim = cur_sym();
20007    r_delim = equiv_sym (cur_sym());
20008    mp_get_x_next (mp);
20009  }
20010  mp_scan_suffix (mp);
20011  if (l_delim != NULL) {
20012    if ((cur_cmd() != mp_right_delimiter) || (equiv_sym (cur_sym()) != l_delim)) {
20013      char msg[256];
20014      const char *hlp[] = {
20015        "I've gotten to the end of the macro parameter list.",
20016        "You might want to delete some tokens before continuing.",
20017        NULL };
20018      mp_snprintf(msg, 256, "Missing `%s' has been inserted", mp_str (mp, text (r_delim)));
20019@.Missing `)'@>;
20020      mp_back_error (mp, msg, hlp, true);
20021    }
20022    mp_get_x_next (mp);
20023  }
20024}
20025
20026
20027@ Before we put a new token list on the input stack, it is wise to clean off
20028all token lists that have recently been depleted. Then a user macro that ends
20029with a call to itself will not require unbounded stack space.
20030
20031@<Feed the arguments and replacement text to the scanner@>=
20032while (token_state && (nloc == NULL))
20033  mp_end_token_list (mp);       /* conserve stack space */
20034if (mp->param_ptr + n > mp->max_param_stack) {
20035  mp->max_param_stack = mp->param_ptr + n;
20036  mp_check_param_size (mp, mp->max_param_stack);
20037@:MetaPost capacity exceeded parameter stack size}{\quad parameter stack size@>
20038}
20039mp_begin_token_list (mp, def_ref, (quarterword) macro);
20040if (macro_name)
20041  name = text (macro_name);
20042else
20043  name = NULL;
20044nloc = r;
20045if (n > 0) {
20046  p = arg_list;
20047  do {
20048    mp->param_stack[mp->param_ptr] = (mp_node)mp_sym_sym (p);
20049    incr (mp->param_ptr);
20050    p = mp_link (p);
20051  } while (p != NULL);
20052  mp_flush_node_list (mp, arg_list);
20053}
20054
20055@ It's sometimes necessary to put a single argument onto |param_stack|.
20056The |stack_argument| subroutine does this.
20057
20058@c
20059static void mp_stack_argument (MP mp, mp_node p) {
20060  if (mp->param_ptr == mp->max_param_stack) {
20061    incr (mp->max_param_stack);
20062    mp_check_param_size (mp, mp->max_param_stack);
20063  }
20064  mp->param_stack[mp->param_ptr] = p;
20065  incr (mp->param_ptr);
20066}
20067
20068
20069@* Conditional processing.
20070Let's consider now the way \&{if} commands are handled.
20071
20072Conditions can be inside conditions, and this nesting has a stack
20073that is independent of other stacks.
20074Four global variables represent the top of the condition stack:
20075|cond_ptr| points to pushed-down entries, if~any; |cur_if| tells whether
20076we are processing \&{if} or \&{elseif}; |if_limit| specifies
20077the largest code of a |fi_or_else| command that is syntactically legal;
20078and |if_line| is the line number at which the current conditional began.
20079
20080If no conditions are currently in progress, the condition stack has the
20081special state |cond_ptr=NULL|, |if_limit=normal|, |cur_if=0|, |if_line=0|.
20082Otherwise |cond_ptr| points to a non-symbolic node; the |type|, |name_type|, and
20083|link| fields of the first word contain |if_limit|, |cur_if|, and
20084|cond_ptr| at the next level, and the second word contains the
20085corresponding |if_line|.
20086
20087@d if_line_field(A) ((mp_if_node)(A))->if_line_field_
20088@d if_code 1 /* code for \&{if} being evaluated */
20089@d fi_code 2 /* code for \&{fi} */
20090@d else_code 3 /* code for \&{else} */
20091@d else_if_code 4 /* code for \&{elseif} */
20092
20093@<MPlib internal header stuff@>=
20094typedef struct mp_if_node_data {
20095  NODE_BODY;
20096  int if_line_field_;
20097} mp_if_node_data;
20098typedef struct mp_if_node_data *mp_if_node;
20099
20100@
20101@d if_node_size sizeof(struct mp_if_node_data) /* number of words in stack entry for conditionals */
20102
20103@c
20104static mp_node mp_get_if_node (MP mp) {
20105  mp_if_node p = (mp_if_node) malloc_node (if_node_size);
20106  mp_type (p) = mp_if_node_type;
20107  return (mp_node) p;
20108}
20109
20110
20111@ @<Glob...@>=
20112mp_node cond_ptr;       /* top of the condition stack */
20113integer if_limit;       /* upper bound on |fi_or_else| codes */
20114quarterword cur_if;     /* type of conditional being worked on */
20115integer if_line;        /* line where that conditional began */
20116
20117@ @<Set init...@>=
20118mp->cond_ptr = NULL;
20119mp->if_limit = normal;
20120mp->cur_if = 0;
20121mp->if_line = 0;
20122
20123@ @<Put each...@>=
20124mp_primitive (mp, "if", mp_if_test, if_code);
20125@:if_}{\&{if} primitive@>;
20126mp_primitive (mp, "fi", mp_fi_or_else, fi_code);
20127mp->frozen_fi = mp_frozen_primitive (mp, "fi", mp_fi_or_else, fi_code);
20128@:fi_}{\&{fi} primitive@>;
20129mp_primitive (mp, "else", mp_fi_or_else, else_code);
20130@:else_}{\&{else} primitive@>;
20131mp_primitive (mp, "elseif", mp_fi_or_else, else_if_code);
20132@:else_if_}{\&{elseif} primitive@>
20133
20134
20135@ @<Cases of |print_cmd_mod|...@>=
20136case mp_if_test:
20137case mp_fi_or_else:
20138switch (m) {
20139case if_code:
20140  mp_print (mp, "if");
20141  break;
20142case fi_code:
20143  mp_print (mp, "fi");
20144  break;
20145case else_code:
20146  mp_print (mp, "else");
20147  break;
20148default:
20149  mp_print (mp, "elseif");
20150  break;
20151}
20152break;
20153
20154@ Here is a procedure that ignores text until coming to an \&{elseif},
20155\&{else}, or \&{fi} at level zero of $\&{if}\ldots\&{fi}$
20156nesting. After it has acted, |cur_mod| will indicate the token that
20157was found.
20158
20159\MP's smallest two command codes are |if_test| and |fi_or_else|; this
20160makes the skipping process a bit simpler.
20161
20162@c
20163void mp_pass_text (MP mp) {
20164  integer l = 0;
20165  mp->scanner_status = skipping;
20166  mp->warning_line = mp_true_line (mp);
20167  while (1) {
20168    get_t_next (mp);
20169    if (cur_cmd() <= mp_fi_or_else) {
20170      if (cur_cmd() < mp_fi_or_else) {
20171        incr (l);
20172      } else {
20173        if (l == 0)
20174          break;
20175        if (cur_mod() == fi_code)
20176          decr (l);
20177      }
20178    } else {
20179      @<Decrease the string reference count,
20180       if the current token is a string@>;
20181    }
20182  }
20183  mp->scanner_status = normal;
20184}
20185
20186
20187@ @<Decrease the string reference count...@>=
20188if (cur_cmd() == mp_string_token) {
20189  delete_str_ref (cur_mod_str());
20190}
20191
20192@ When we begin to process a new \&{if}, we set |if_limit:=if_code|; then
20193if \&{elseif} or \&{else} or \&{fi} occurs before the current \&{if}
20194condition has been evaluated, a colon will be inserted.
20195A construction like `\.{if fi}' would otherwise get \MP\ confused.
20196
20197@<Push the condition stack@>=
20198{
20199  p = mp_get_if_node (mp);
20200  mp_link (p) = mp->cond_ptr;
20201  mp_type (p) = (quarterword) mp->if_limit;
20202  mp_name_type (p) = mp->cur_if;
20203  if_line_field (p) = mp->if_line;
20204  mp->cond_ptr = p;
20205  mp->if_limit = if_code;
20206  mp->if_line = mp_true_line (mp);
20207  mp->cur_if = if_code;
20208}
20209
20210
20211@ @<Pop the condition stack@>=
20212{
20213  mp_node p = mp->cond_ptr;
20214  mp->if_line = if_line_field (p);
20215  mp->cur_if = mp_name_type (p);
20216  mp->if_limit = mp_type (p);
20217  mp->cond_ptr = mp_link (p);
20218  mp_free_node (mp, p, if_node_size);
20219}
20220
20221
20222@ Here's a procedure that changes the |if_limit| code corresponding to
20223a given value of |cond_ptr|.
20224
20225@c
20226static void mp_change_if_limit (MP mp, quarterword l, mp_node p) {
20227  mp_node q;
20228  if (p == mp->cond_ptr) {
20229    mp->if_limit = l;           /* that's the easy case */
20230  } else {
20231    q = mp->cond_ptr;
20232    while (1) {
20233      if (q == NULL)
20234        mp_confusion (mp, "if");
20235@:this can't happen if}{\quad if@>;
20236      /* clang: dereference of null pointer */ assert(q);
20237      if (mp_link (q) == p) {
20238        mp_type (q) = l;
20239        return;
20240      }
20241      q = mp_link (q);
20242    }
20243  }
20244}
20245
20246
20247@ The user is supposed to put colons into the proper parts of conditional
20248statements. Therefore, \MP\ has to check for their presence.
20249
20250@c
20251static void mp_check_colon (MP mp) {
20252  if (cur_cmd() != mp_colon) {
20253    const char *hlp[] = {
20254           "There should've been a colon after the condition.",
20255           "I shall pretend that one was there.",
20256           NULL };
20257    mp_back_error (mp, "Missing `:' has been inserted", hlp, true);
20258@.Missing `:'@>;
20259  }
20260}
20261
20262
20263@ A condition is started when the |get_x_next| procedure encounters
20264an |if_test| command; in that case |get_x_next| calls |conditional|,
20265which is a recursive procedure.
20266@^recursion@>
20267
20268@c
20269void mp_conditional (MP mp) {
20270  mp_node save_cond_ptr;        /* |cond_ptr| corresponding to this conditional */
20271  int new_if_limit;     /* future value of |if_limit| */
20272  mp_node p;    /* temporary register */
20273  @<Push the condition stack@>;
20274  save_cond_ptr = mp->cond_ptr;
20275RESWITCH:
20276  mp_get_boolean (mp);
20277  new_if_limit = else_if_code;
20278  if (number_greater (internal_value (mp_tracing_commands), unity_t)) {
20279    @<Display the boolean value of |cur_exp|@>;
20280  }
20281FOUND:
20282  mp_check_colon (mp);
20283  if (cur_exp_value_boolean () == mp_true_code) {
20284    mp_change_if_limit (mp, (quarterword) new_if_limit, save_cond_ptr);
20285    return;                     /* wait for \&{elseif}, \&{else}, or \&{fi} */
20286  };
20287  @<Skip to \&{elseif} or \&{else} or \&{fi}, then |goto done|@>;
20288DONE:
20289  mp->cur_if = (quarterword) cur_mod();
20290  mp->if_line = mp_true_line (mp);
20291  if (cur_mod() == fi_code) {
20292    @<Pop the condition stack@>
20293  } else if (cur_mod() == else_if_code) {
20294    goto RESWITCH;
20295  } else {
20296    set_cur_exp_value_boolean (mp_true_code);
20297    new_if_limit = fi_code;
20298    mp_get_x_next (mp);
20299    goto FOUND;
20300  }
20301}
20302
20303
20304@ In a construction like `\&{if} \&{if} \&{true}: $0=1$: \\{foo}
20305\&{else}: \\{bar} \&{fi}', the first \&{else}
20306that we come to after learning that the \&{if} is false is not the
20307\&{else} we're looking for. Hence the following curious logic is needed.
20308
20309@<Skip to \&{elseif}...@>=
20310while (1) {
20311  mp_pass_text (mp);
20312  if (mp->cond_ptr == save_cond_ptr)
20313    goto DONE;
20314  else if (cur_mod() == fi_code)
20315    @<Pop the condition stack@>;
20316}
20317
20318
20319@ @<Display the boolean value...@>=
20320{
20321  mp_begin_diagnostic (mp);
20322  if (cur_exp_value_boolean () == mp_true_code)
20323    mp_print (mp, "{true}");
20324  else
20325    mp_print (mp, "{false}");
20326  mp_end_diagnostic (mp, false);
20327}
20328
20329
20330@ The processing of conditionals is complete except for the following
20331code, which is actually part of |get_x_next|. It comes into play when
20332\&{elseif}, \&{else}, or \&{fi} is scanned.
20333
20334@<Terminate the current conditional and skip to \&{fi}@>=
20335if (cur_mod() > mp->if_limit) {
20336  if (mp->if_limit == if_code) {        /* condition not yet evaluated */
20337    const char *hlp[] = { "Something was missing here", NULL };
20338    mp_back_input (mp);
20339    set_cur_sym(mp->frozen_colon);
20340    mp_ins_error (mp, "Missing `:' has been inserted", hlp, true);
20341@.Missing `:'@>;
20342  } else {
20343    const char *hlp[] =  {"I'm ignoring this; it doesn't match any if.", NULL};
20344    if (cur_mod() == fi_code) {
20345       mp_error(mp, "Extra fi", hlp, true);
20346@.Extra fi@>;
20347    } else if (cur_mod() == else_code) {
20348       mp_error(mp, "Extra else", hlp, true);
20349@.Extra else@>
20350    } else {
20351       mp_error(mp, "Extra elseif", hlp, true);
20352@.Extra elseif@>
20353    }
20354  }
20355} else {
20356  while (cur_mod() != fi_code)
20357    mp_pass_text (mp);          /* skip to \&{fi} */
20358  @<Pop the condition stack@>;
20359}
20360
20361
20362@* Iterations.
20363To bring our treatment of |get_x_next| to a close, we need to consider what
20364\MP\ does when it sees \&{for}, \&{forsuffixes}, and \&{forever}.
20365
20366There's a global variable |loop_ptr| that keeps track of the \&{for} loops
20367that are currently active. If |loop_ptr=NULL|, no loops are in progress;
20368otherwise |loop_ptr.info| points to the iterative text of the current
20369(innermost) loop, and |loop_ptr.link| points to the data for any other
20370loops that enclose the current one.
20371
20372A loop-control node also has two other fields, called |type| and
20373|list|, whose contents depend on the type of loop:
20374
20375\yskip\indent|loop_ptr.type=NULL| means that the link of |loop_ptr.list|
20376points to a list of symbolic nodes whose |info| fields point to the
20377remaining argument values of a suffix list and expression list.
20378In this case, an extra field |loop_ptr.start_list| is needed to
20379make sure that |resume_operation| skips ahead.
20380
20381\yskip\indent|loop_ptr.type=MP_VOID| means that the current loop is
20382`\&{forever}'.
20383
20384\yskip\indent|loop_ptr.type=PROGRESSION_FLAG| means that
20385|loop_ptr.value|, |loop_ptr.step_size|, and |loop_ptr.final_value|
20386contain the data for an arithmetic progression.
20387
20388\yskip\indent|loop_ptr.type=p>PROGRESSION_FLAG| means that |p| points to an edge
20389header and |loop_ptr.list| points into the graphical object list for
20390that edge header.
20391
20392@d PROGRESSION_FLAG (mp_node)(2) /* |NULL+2| */
20393  /* |loop_type| value when |loop_list| points to a progression node */
20394
20395@<Types...@>=
20396typedef struct mp_loop_data {
20397  mp_sym  var ; /* the var of the loop */
20398  mp_node info; /* iterative text of this loop */
20399  mp_node type; /* the special type of this loop, or a pointer into
20400                   mem */
20401  mp_node list; /* the remaining list elements */
20402  mp_node list_start;   /* head fo the list of elements */
20403  mp_number old_value; /* previous value of current arithmetic value */
20404  mp_number value; /* current arithmetic value */
20405  mp_number step_size;     /* arithmetic step size */
20406  mp_number final_value;   /* end arithmetic value */
20407  struct mp_loop_data *link;    /* the enclosing loop, if any */
20408} mp_loop_data;
20409
20410@ @<Glob...@>=
20411mp_loop_data *loop_ptr; /* top of the loop-control-node stack */
20412
20413@ @<Set init...@>=
20414mp->loop_ptr = NULL;
20415
20416@ If the expressions that define an arithmetic progression in a
20417\&{for} loop don't have known numeric values, the |bad_for| subroutine
20418screams at the user.
20419
20420@c
20421static void mp_bad_for (MP mp, const char *s) {
20422  char msg[256];
20423  mp_value new_expr;
20424  const char *hlp[] = {"When you say `for x=a step b until c',",
20425         "the initial value `a' and the step size `b'",
20426         "and the final value `c' must have known numeric values.",
20427         "I'm zeroing this one. Proceed, with fingers crossed.",
20428         NULL };
20429  memset(&new_expr,0,sizeof(mp_value));
20430  new_number(new_expr.data.n);
20431  mp_disp_err (mp, NULL);
20432  /* show the bad expression above the message */
20433  mp_snprintf(msg, 256, "Improper %s has been replaced by 0", s);
20434@.Improper...replaced by 0@>;
20435  mp_back_error (mp, msg, hlp, true);
20436  mp_get_x_next (mp);
20437  mp_flush_cur_exp (mp, new_expr);
20438}
20439
20440
20441@ Here's what \MP\ does when \&{for}, \&{forsuffixes}, or \&{forever}
20442has just been scanned. (This code requires slight familiarity with
20443expression-parsing routines that we have not yet discussed; but it
20444seems to belong in the present part of the program, even though the
20445original author didn't write it until later. The reader may wish to
20446come back to it.)
20447
20448@c
20449void mp_begin_iteration (MP mp) {
20450  halfword m;   /* |start_for| (\&{for}) or |start_forsuffixes|
20451                   (\&{forsuffixes}) */
20452  mp_sym n;     /* hash address of the current symbol */
20453  mp_loop_data *s;      /* the new loop-control node */
20454  mp_subst_list_item *p = NULL; /* substitution list for |scan_toks|
20455                                 */
20456  mp_node q;    /* link manipulation register */
20457  m = cur_mod();
20458  n = cur_sym();
20459  s = xmalloc (1, sizeof (mp_loop_data));
20460  s->type = s->list = s->info = s->list_start = NULL;
20461  s->link = NULL; s->var = NULL;
20462  new_number (s->value);new_number (s->old_value);
20463  new_number (s->step_size);
20464  new_number (s->final_value);
20465  if (m == start_forever) {
20466    s->type = MP_VOID;
20467    p = NULL;
20468    mp_get_x_next (mp);
20469  } else {
20470    mp_get_symbol (mp);
20471    p = xmalloc (1, sizeof (mp_subst_list_item));
20472    p->link = NULL;
20473    p->info = cur_sym();
20474    s->var  = cur_sym();
20475    p->info_mod = cur_sym_mod();
20476    p->value_data = 0;
20477    if (m == start_for) {
20478      p->value_mod = mp_expr_sym;
20479    } else {                    /* |start_forsuffixes| */
20480      p->value_mod = mp_suffix_sym;
20481    }
20482    mp_get_x_next (mp);
20483    if (cur_cmd() == mp_within_token) {
20484      @<Set up a picture iteration@>;
20485    } else {
20486      @<Check for the assignment in a loop header@>;
20487      @<Scan the values to be used in the loop@>;
20488    }
20489  }
20490  @<Check for the presence of a colon@>;
20491  @<Scan the loop text and put it on the loop control stack@>;
20492  mp_resume_iteration (mp);
20493}
20494
20495
20496@ @<Check for the assignment in a loop header@>=
20497if ((cur_cmd() != mp_equals) && (cur_cmd() != mp_assignment)) {
20498  const char *hlp[] = {
20499         "The next thing in this loop should have been `=' or `:='.",
20500         "But don't worry; I'll pretend that an equals sign",
20501         "was present, and I'll look for the values next.",
20502         NULL };
20503  mp_back_error (mp, "Missing `=' has been inserted", hlp, true);
20504@.Missing `='@>;
20505}
20506
20507@ @<Check for the presence of a colon@>=
20508if (cur_cmd() != mp_colon) {
20509  const char *hlp[] = {
20510         "The next thing in this loop should have been a `:'.",
20511         "So I'll pretend that a colon was present;",
20512         "everything from here to `endfor' will be iterated.",
20513         NULL };
20514  mp_back_error (mp, "Missing `:' has been inserted", hlp, true);
20515@.Missing `:'@>;
20516}
20517
20518@ We append a special |mp->frozen_repeat_loop| token in place of the
20519`\&{endfor}' at the end of the loop. This will come through \MP's
20520scanner at the proper time to cause the loop to be repeated.
20521
20522(If the user tries some shenanigan like `\&{for} $\ldots$ \&{let}
20523\&{endfor}', he will be foiled by the |get_symbol| routine, which
20524keeps frozen tokens unchanged. Furthermore the
20525|mp->frozen_repeat_loop| is an \&{outer} token, so it won't be lost
20526accidentally.)
20527
20528@ @<Scan the loop text...@>=
20529q = mp_get_symbolic_node (mp);
20530set_mp_sym_sym (q, mp->frozen_repeat_loop);
20531mp->scanner_status = loop_defining;
20532mp->warning_info = n;
20533s->info = mp_scan_toks (mp, mp_iteration, p, q, 0);
20534mp->scanner_status = normal;
20535s->link = mp->loop_ptr;
20536mp->loop_ptr = s
20537
20538@ @<Initialize table...@>=
20539mp->frozen_repeat_loop =
20540mp_frozen_primitive (mp, " ENDFOR", mp_repeat_loop + mp_outer_tag, 0);
20541
20542@ The loop text is inserted into \MP's scanning apparatus by the
20543|resume_iteration| routine.
20544
20545@c
20546void mp_resume_iteration (MP mp) {
20547  mp_node p, q; /* link registers */
20548  p = mp->loop_ptr->type;
20549  if (p == PROGRESSION_FLAG) {
20550    set_cur_exp_value_number (mp->loop_ptr->value);
20551    if (@<The arithmetic progression has ended@>) {
20552      mp_stop_iteration (mp);
20553      return;
20554    }
20555    mp->cur_exp.type = mp_known;
20556    q = mp_stash_cur_exp (mp);  /* make |q| an \&{expr} argument */
20557    number_clone (mp->loop_ptr->old_value, cur_exp_value_number ());
20558    set_number_from_addition (mp->loop_ptr->value, cur_exp_value_number (), mp->loop_ptr->step_size);
20559                                                                       /* set |value(p)| for the next iteration */
20560    /* detect numeric overflow */
20561    if (number_positive(mp->loop_ptr->step_size) &&
20562        number_less(mp->loop_ptr->value,  cur_exp_value_number ())) {
20563      if (number_positive(mp->loop_ptr->final_value)) {
20564        number_clone (mp->loop_ptr->value, mp->loop_ptr->final_value);
20565        number_add_scaled (mp->loop_ptr->final_value, -1);
20566      } else {
20567        number_clone (mp->loop_ptr->value, mp->loop_ptr->final_value);
20568        number_add_scaled (mp->loop_ptr->value, 1);
20569      }
20570    } else if (number_negative(mp->loop_ptr->step_size) &&
20571              number_greater (mp->loop_ptr->value, cur_exp_value_number ())) {
20572      if (number_negative (mp->loop_ptr->final_value)) {
20573        number_clone (mp->loop_ptr->value, mp->loop_ptr->final_value);
20574        number_add_scaled (mp->loop_ptr->final_value, 1);
20575      } else {
20576        number_clone (mp->loop_ptr->value, mp->loop_ptr->final_value);
20577        number_add_scaled (mp->loop_ptr->value, -1);
20578      }
20579    }
20580  } else if (p == NULL) {
20581    p = mp->loop_ptr->list;
20582    if (p != NULL && p == mp->loop_ptr->list_start) {
20583      q = p;
20584      p = mp_link (p);
20585      mp_free_symbolic_node (mp, q);
20586      mp->loop_ptr->list = p;
20587    }
20588    if (p == NULL) {
20589      mp_stop_iteration (mp);
20590      return;
20591    }
20592    mp->loop_ptr->list = mp_link (p);
20593    q = (mp_node)mp_sym_sym (p);
20594    if (q)
20595     number_clone (mp->loop_ptr->old_value, q->data.n);
20596    mp_free_symbolic_node (mp, p);
20597  } else if (p == MP_VOID) {
20598    mp_begin_token_list (mp, mp->loop_ptr->info, (quarterword) forever_text);
20599    return;
20600  } else {
20601    @<Make |q| a capsule containing the next picture component from
20602      |loop_list(loop_ptr)| or |goto not_found|@>;
20603  }
20604  mp_begin_token_list (mp, mp->loop_ptr->info, (quarterword) loop_text);
20605  mp_stack_argument (mp, q);
20606  if (number_greater (internal_value (mp_tracing_commands), unity_t)) {
20607    @<Trace the start of a loop@>;
20608  }
20609  return;
20610NOT_FOUND:
20611  mp_stop_iteration (mp);
20612}
20613
20614
20615@ @<The arithmetic progression has ended@>=
20616(number_positive(mp->loop_ptr->step_size) && number_greater(cur_exp_value_number (), mp->loop_ptr->final_value))
20617||
20618(number_negative(mp->loop_ptr->step_size) && number_less(cur_exp_value_number (), mp->loop_ptr->final_value))
20619
20620
20621@ @<Trace the start of a loop@>=
20622{
20623  mp_begin_diagnostic (mp);
20624  mp_print_nl (mp, "{loop value=");
20625@.loop value=n@>;
20626  if ((q != NULL) && (mp_link (q) == MP_VOID))
20627    mp_print_exp (mp, q, 1);
20628  else
20629    mp_show_token_list (mp, q, NULL, 50, 0);
20630  mp_print_char (mp, xord ('}'));
20631  mp_end_diagnostic (mp, false);
20632}
20633
20634
20635@ @<Make |q| a capsule containing the next picture component
20636from...@>=
20637{
20638  q = mp->loop_ptr->list;
20639  if (q == NULL)
20640    goto NOT_FOUND;
20641    if ( ! is_start_or_stop(q) )
20642      q=mp_link(q);
20643    else if ( ! is_stop(q) )
20644      q=mp_skip_1component(mp, q);
20645    else
20646      goto NOT_FOUND;
20647
20648  set_cur_exp_node ((mp_node)mp_copy_objects (mp, mp->loop_ptr->list, q));
20649  mp_init_bbox (mp, (mp_edge_header_node)cur_exp_node ());
20650  mp->cur_exp.type = mp_picture_type;
20651  mp->loop_ptr->list = q;
20652  q = mp_stash_cur_exp (mp);
20653}
20654
20655
20656@ A level of loop control disappears when |resume_iteration| has
20657decided not to resume, or when an \&{exitif} construction has removed
20658the loop text from the input stack.
20659
20660@c
20661void mp_stop_iteration (MP mp) {
20662  mp_node p, q; /* the usual */
20663  mp_loop_data *tmp;    /* for free() */
20664  p = mp->loop_ptr->type;
20665  if (p == PROGRESSION_FLAG) {
20666    mp_free_symbolic_node (mp, mp->loop_ptr->list);
20667  } else if (p == NULL) {
20668    q = mp->loop_ptr->list;
20669    while (q != NULL) {
20670      p = (mp_node)mp_sym_sym (q);
20671      if (p != NULL) {
20672        if (mp_link (p) == MP_VOID) {      /* it's an \&{expr} parameter */
20673          mp_recycle_value (mp, p);
20674          mp_free_value_node (mp, p);
20675        } else {
20676          mp_flush_token_list (mp, p);  /* it's a \&{suffix} or \&{text}
20677                                           parameter */
20678        }
20679      }
20680      p = q;
20681      q = mp_link (q);
20682      mp_free_symbolic_node (mp, p);
20683    }
20684  } else if (p > PROGRESSION_FLAG) {
20685    delete_edge_ref (p);
20686  }
20687  tmp = mp->loop_ptr;
20688  mp->loop_ptr = tmp->link;
20689  mp_flush_token_list (mp, tmp->info);
20690  free_number (tmp->value);
20691  free_number (tmp->step_size);
20692  free_number (tmp->final_value);
20693  xfree (tmp);
20694}
20695
20696
20697@ Now that we know all about loop control, we can finish up the
20698missing portion of |begin_iteration| and we'll be done.
20699
20700The following code is performed after the `\.=' has been scanned in a
20701\&{for} construction (if |m=start_for|) or a \&{forsuffixes}
20702construction (if |m=start_forsuffixes|).
20703
20704@<Scan the values to be used in the loop@>=
20705s->type = NULL;
20706s->list = mp_get_symbolic_node (mp);
20707s->list_start = s->list;
20708q = s->list;
20709do {
20710  mp_get_x_next (mp);
20711  if (m != start_for) {
20712    mp_scan_suffix (mp);
20713  } else {
20714    if (cur_cmd() >= mp_colon)
20715      if (cur_cmd() <= mp_comma)
20716        goto CONTINUE;
20717    mp_scan_expression (mp);
20718    if (cur_cmd() == mp_step_token)
20719      if (q == s->list) {
20720        @<Prepare for step-until construction and |break|@>;
20721      }
20722    set_cur_exp_node (mp_stash_cur_exp (mp));
20723  }
20724  mp_link (q) = mp_get_symbolic_node (mp);
20725  q = mp_link (q);
20726  set_mp_sym_sym (q, mp->cur_exp.data.node);
20727  if (m == start_for)
20728    mp_name_type (q) = mp_expr_sym;
20729  else if (m == start_forsuffixes)
20730    mp_name_type (q) = mp_suffix_sym;
20731  mp->cur_exp.type = mp_vacuous;
20732CONTINUE:
20733  ;
20734} while (cur_cmd() == mp_comma)
20735
20736@ @<Prepare for step-until construction and |break|@>=
20737{
20738  if (mp->cur_exp.type != mp_known)
20739    mp_bad_for (mp, "initial value");
20740  number_clone (s->value, cur_exp_value_number ());
20741  number_clone (s->old_value, cur_exp_value_number ());
20742  mp_get_x_next (mp);
20743  mp_scan_expression (mp);
20744  if (mp->cur_exp.type != mp_known)
20745    mp_bad_for (mp, "step size");
20746  number_clone (s->step_size, cur_exp_value_number ());
20747  if (cur_cmd() != mp_until_token) {
20748    const char *hlp[] = {
20749           "I assume you meant to say `until' after `step'.",
20750           "So I'll look for the final value and colon next.",
20751           NULL };
20752    mp_back_error (mp, "Missing `until' has been inserted", hlp, true);
20753@.Missing `until'@>;
20754  }
20755  mp_get_x_next (mp);
20756  mp_scan_expression (mp);
20757  if (mp->cur_exp.type != mp_known)
20758    mp_bad_for (mp, "final value");
20759  number_clone (s->final_value, cur_exp_value_number ());
20760  s->type = PROGRESSION_FLAG;
20761  break;
20762}
20763
20764
20765@ The last case is when we have just seen ``\&{within}'', and we need to
20766parse a picture expression and prepare to iterate over it.
20767
20768@<Set up a picture iteration@>=
20769{
20770  mp_get_x_next (mp);
20771  mp_scan_expression (mp);
20772  @<Make sure the current expression is a known picture@>;
20773  s->type = mp->cur_exp.data.node;
20774  mp->cur_exp.type = mp_vacuous;
20775  q = mp_link (edge_list (mp->cur_exp.data.node));
20776  if (q != NULL)
20777    if (is_start_or_stop (q))
20778      if (mp_skip_1component (mp, q) == NULL)
20779        q = mp_link (q);
20780  s->list = q;
20781}
20782
20783
20784@ @<Make sure the current expression is a known picture@>=
20785if (mp->cur_exp.type != mp_picture_type) {
20786  mp_value new_expr;
20787  const char *hlp[] = { "When you say `for x in p', p must be a known picture.", NULL };
20788  memset(&new_expr,0,sizeof(mp_value));
20789  new_number(new_expr.data.n);
20790  new_expr.data.node = (mp_node)mp_get_edge_header_node (mp);
20791  mp_disp_err (mp, NULL);
20792  mp_back_error (mp,"Improper iteration spec has been replaced by nullpicture", hlp, true);
20793  mp_get_x_next (mp);
20794  mp_flush_cur_exp (mp, new_expr);
20795  mp_init_edges (mp, (mp_edge_header_node)mp->cur_exp.data.node);
20796  mp->cur_exp.type = mp_picture_type;
20797}
20798
20799@* File names.
20800It's time now to fret about file names.  Besides the fact that different
20801operating systems treat files in different ways, we must cope with the
20802fact that completely different naming conventions are used by different
20803groups of people. The following programs show what is required for one
20804particular operating system; similar routines for other systems are not
20805difficult to devise.
20806@^system dependencies@>
20807
20808\MP\ assumes that a file name has three parts: the name proper; its
20809``extension''; and a ``file area'' where it is found in an external file
20810system.  The extension of an input file is assumed to be
20811`\.{.mp}' unless otherwise specified; it is `\.{.log}' on the
20812transcript file that records each run of \MP; it is `\.{.tfm}' on the font
20813metric files that describe characters in any fonts created by \MP; it is
20814`\.{.ps}' or `.{\it nnn}' for some number {\it nnn} on the \ps\ output files.
20815The file area can be arbitrary on input files, but files are usually
20816output to the user's current area.  If an input file cannot be
20817found on the specified area, \MP\ will look for it on a special system
20818area; this special area is intended for commonly used input files.
20819
20820Simple uses of \MP\ refer only to file names that have no explicit
20821extension or area. For example, a person usually says `\.{input} \.{cmr10}'
20822instead of `\.{input} \.{cmr10.new}'. Simple file
20823names are best, because they make the \MP\ source files portable;
20824whenever a file name consists entirely of letters and digits, it should be
20825treated in the same way by all implementations of \MP. However, users
20826need the ability to refer to other files in their environment, especially
20827when responding to error messages concerning unopenable files; therefore
20828we want to let them use the syntax that appears in their favorite
20829operating system.
20830
20831@ \MP\ uses the same conventions that have proved to be satisfactory for
20832\TeX\ and \MF. In order to isolate the system-dependent aspects of file names,
20833@^system dependencies@>
20834the system-independent parts of \MP\ are expressed in terms
20835of three system-dependent
20836procedures called |begin_name|, |more_name|, and |end_name|. In
20837essence, if the user-specified characters of the file name are $c_1\ldots c_n$,
20838the system-independent driver program does the operations
20839$$|begin_name|;\,|more_name|(c_1);\,\ldots\,;\,|more_name|(c_n);
20840\,|end_name|.$$
20841These three procedures communicate with each other via global variables.
20842Afterwards the file name will appear in the string pool as three strings
20843called |cur_name|\penalty10000\hskip-.05em,
20844|cur_area|, and |cur_ext|; the latter two are NULL (i.e.,
20845|""|), unless they were explicitly specified by the user.
20846
20847Actually the situation is slightly more complicated, because \MP\ needs
20848to know when the file name ends. The |more_name| routine is a function
20849(with side effects) that returns |true| on the calls |more_name|$(c_1)$,
20850\dots, |more_name|$(c_{n-1})$. The final call |more_name|$(c_n)$
20851returns |false|; or, it returns |true| and $c_n$ is the last character
20852on the current input line. In other words,
20853|more_name| is supposed to return |true| unless it is sure that the
20854file name has been completely scanned; and |end_name| is supposed to be able
20855to finish the assembly of |cur_name|, |cur_area|, and |cur_ext| regardless of
20856whether $|more_name|(c_n)$ returned |true| or |false|.
20857
20858@<Glob...@>=
20859char *cur_name; /* name of file just scanned */
20860char *cur_area; /* file area just scanned, or \.{""} */
20861char *cur_ext;  /* file extension just scanned, or \.{""} */
20862
20863@ It is easier to maintain reference counts if we assign initial values.
20864
20865@<Set init...@>=
20866mp->cur_name = xstrdup ("");
20867mp->cur_area = xstrdup ("");
20868mp->cur_ext = xstrdup ("");
20869
20870@ @<Dealloc variables@>=
20871xfree (mp->cur_area);
20872xfree (mp->cur_name);
20873xfree (mp->cur_ext);
20874
20875@ The file names we shall deal with for illustrative purposes have the
20876following structure:  If the name contains `\.>' or `\.:', the file area
20877consists of all characters up to and including the final such character;
20878otherwise the file area is null.  If the remaining file name contains
20879`\..', the file extension consists of all such characters from the first
20880remaining `\..' to the end, otherwise the file extension is null.
20881@^system dependencies@>
20882
20883We can scan such file names easily by using two global variables that keep track
20884of the occurrences of area and extension delimiters.
20885
20886@<Glob...@>=
20887integer area_delimiter;
20888  /* most recent `\.>' or `\.:' relative to |str_start[str_ptr]| */
20889integer ext_delimiter;  /* the relevant `\..', if any */
20890boolean quoted_filename;        /* whether the filename is wrapped in " markers */
20891
20892@ Here now is the first of the system-dependent routines for file name scanning.
20893@^system dependencies@>
20894
20895@<Declarations@>=
20896static void mp_begin_name (MP mp);
20897static boolean mp_more_name (MP mp, ASCII_code c);
20898static void mp_end_name (MP mp);
20899
20900@ @c
20901void mp_begin_name (MP mp) {
20902  xfree (mp->cur_name);
20903  xfree (mp->cur_area);
20904  xfree (mp->cur_ext);
20905  mp->area_delimiter = -1;
20906  mp->ext_delimiter = -1;
20907  mp->quoted_filename = false;
20908}
20909
20910
20911@ And here's the second.
20912@^system dependencies@>
20913
20914@c
20915#ifndef IS_DIR_SEP
20916#define IS_DIR_SEP(c) (c=='/' || c=='\\')
20917#endif
20918boolean mp_more_name (MP mp, ASCII_code c) {
20919  if (c == '"') {
20920    mp->quoted_filename = !mp->quoted_filename;
20921  } else if ((c == ' ' || c == '\t') && (mp->quoted_filename == false)) {
20922    return false;
20923  } else {
20924    if (IS_DIR_SEP (c)) {
20925      mp->area_delimiter = (integer) mp->cur_length;
20926      mp->ext_delimiter = -1;
20927    } else if (c == '.') {
20928      mp->ext_delimiter = (integer) mp->cur_length;
20929    }
20930    append_char (c);            /* contribute |c| to the current string */
20931  }
20932  return true;
20933}
20934
20935
20936@ The third.
20937@^system dependencies@>
20938
20939@d copy_pool_segment(A,B,C) {
20940      A = xmalloc(C+1,sizeof(char));
20941      (void)memcpy(A,(char *)(mp->cur_string+B),C);
20942      A[C] = 0;}
20943
20944@c
20945void mp_end_name (MP mp) {
20946  size_t s = 0; /* length of area, name, and extension */
20947  size_t len;
20948  /* "my/w.mp" */
20949  if (mp->area_delimiter < 0) {
20950    mp->cur_area = xstrdup ("");
20951  } else {
20952    len = (size_t) mp->area_delimiter - s + 1;
20953    copy_pool_segment (mp->cur_area, s, len);
20954    s += len;
20955  }
20956  if (mp->ext_delimiter < 0) {
20957    mp->cur_ext = xstrdup ("");
20958    len = (unsigned) (mp->cur_length - s);
20959  } else {
20960    copy_pool_segment (mp->cur_ext, mp->ext_delimiter,
20961                       (mp->cur_length - (size_t) mp->ext_delimiter));
20962    len = (size_t) mp->ext_delimiter - s;
20963  }
20964  copy_pool_segment (mp->cur_name, s, len);
20965  mp_reset_cur_string (mp);
20966}
20967
20968
20969@ Conversely, here is a routine that takes three strings and prints a file
20970name that might have produced them. (The routine is system dependent, because
20971some operating systems put the file area last instead of first.)
20972@^system dependencies@>
20973
20974@<Basic printing...@>=
20975static void mp_print_file_name (MP mp, char *n, char *a, char *e) {
20976  boolean must_quote = false;
20977  if (((a != NULL) && (strchr (a, ' ') != NULL)) ||
20978      ((n != NULL) && (strchr (n, ' ') != NULL)) ||
20979      ((e != NULL) && (strchr (e, ' ') != NULL)))
20980    must_quote = true;
20981  if (must_quote)
20982    mp_print_char (mp, (ASCII_code) '"');
20983  mp_print (mp, a);
20984  mp_print (mp, n);
20985  mp_print (mp, e);
20986  if (must_quote)
20987    mp_print_char (mp, (ASCII_code) '"');
20988}
20989
20990
20991@ Another system-dependent routine is needed to convert three internal
20992\MP\ strings
20993to the |name_of_file| value that is used to open files. The present code
20994allows both lowercase and uppercase letters in the file name.
20995@^system dependencies@>
20996
20997@d append_to_name(A) { mp->name_of_file[k++]=(char)xchr(xord((ASCII_code)(A))); }
20998
20999@ @c
21000void mp_pack_file_name (MP mp, const char *n, const char *a, const char *e) {
21001  integer k;    /* number of positions filled in |name_of_file| */
21002  const char *j;        /* a character  index */
21003  size_t slen;
21004  k = 0;
21005  assert (n != NULL);
21006  xfree (mp->name_of_file);
21007  slen = strlen (n) + 1;
21008  if (a != NULL)
21009    slen += strlen (a);
21010  if (e != NULL)
21011    slen += strlen (e);
21012  mp->name_of_file = xmalloc (slen, 1);
21013  if (a != NULL) {
21014    for (j = a; *j != '\0'; j++) {
21015      append_to_name (*j);
21016    }
21017  }
21018  for (j = n; *j != '\0'; j++) {
21019    append_to_name (*j);
21020  }
21021  if (e != NULL) {
21022    for (j = e; *j != '\0'; j++) {
21023      append_to_name (*j);
21024    }
21025  }
21026  mp->name_of_file[k] = 0;
21027}
21028
21029
21030@ @<Internal library declarations@>=
21031void mp_pack_file_name (MP mp, const char *n, const char *a, const char *e);
21032
21033@ @<Option variables@>=
21034char *mem_name; /* for commandline */
21035
21036@ Stripping a |.mem| extension here is for backward compatibility.
21037
21038@<Find and load preload file, if required@>=
21039if (!opt->ini_version) {
21040  mp->mem_name = xstrdup (opt->mem_name);
21041  if (mp->mem_name) {
21042    size_t l = strlen (mp->mem_name);
21043    if (l > 4) {
21044      char *test = strstr (mp->mem_name, ".mem");
21045      if (test == mp->mem_name + l - 4) {
21046       *test = 0;
21047      }
21048    }
21049  }
21050  if (mp->mem_name != NULL) {
21051    if (!mp_open_mem_file (mp)) {
21052      mp->history = mp_fatal_error_stop;
21053      mp_jump_out (mp);
21054    }
21055  }
21056}
21057
21058
21059
21060@ @<Dealloc variables@>=
21061xfree (mp->mem_name);
21062
21063@ This part of the program becomes active when a ``virgin'' \MP\ is
21064trying to get going, just after the preliminary initialization.
21065The buffer contains the first line of input in |buffer[loc..(last-1)]|,
21066where |loc<last| and |buffer[loc]<>""|.
21067
21068@<Declarations@>=
21069static boolean mp_open_mem_name (MP mp);
21070static boolean mp_open_mem_file (MP mp);
21071
21072@ @c
21073boolean mp_open_mem_name (MP mp) {
21074  if (mp->mem_name != NULL) {
21075    size_t l = strlen (mp->mem_name);
21076    char *s = xstrdup (mp->mem_name);
21077    if (l > 4) {
21078      char *test = strstr (s, ".mp");
21079      if (test == NULL || test != s + l - 4) {
21080        s = xrealloc (s, l + 5, 1);
21081        strcat (s, ".mp");
21082      }
21083    } else {
21084      s = xrealloc (s, l + 5, 1);
21085      strcat (s, ".mp");
21086    }
21087    s = (mp->find_file) (mp, s, "r", mp_filetype_program);
21088    xfree(mp->name_of_file);
21089    if (s == NULL)
21090      return false;
21091    mp->name_of_file = xstrdup(s);
21092    mp->mem_file = (mp->open_file) (mp, s, "r", mp_filetype_program);
21093    free (s);
21094    if (mp->mem_file)
21095      return true;
21096  }
21097  return false;
21098}
21099boolean mp_open_mem_file (MP mp) {
21100  if (mp->mem_file != NULL)
21101    return true;
21102  if (mp_open_mem_name (mp))
21103    return true;
21104  if (mp_xstrcmp (mp->mem_name, "plain")) {
21105    wake_up_terminal();
21106    wterm ("Sorry, I can\'t find the '");
21107    wterm (mp->mem_name);
21108    wterm ("' preload file; will try 'plain'.");
21109    wterm_cr;
21110@.Sorry, I can't find...@>;
21111    update_terminal();
21112    /* now pull out all the stops: try for the system \.{plain} file */
21113    xfree (mp->mem_name);
21114    mp->mem_name = xstrdup ("plain");
21115    if (mp_open_mem_name (mp))
21116      return true;
21117  }
21118  wake_up_terminal();
21119  wterm_ln ("I can't find the 'plain' preload file!\n");
21120@.I can't find PLAIN...@>
21121@.plain@>;
21122  return false;
21123}
21124
21125
21126@ Operating systems often make it possible to determine the exact name (and
21127possible version number) of a file that has been opened. The following routine,
21128which simply makes a \MP\ string from the value of |name_of_file|, should
21129ideally be changed to deduce the full name of file~|f|, which is the file
21130most recently opened, if it is possible to do this.
21131@^system dependencies@>
21132
21133@ @c
21134static mp_string mp_make_name_string (MP mp) {
21135  int k;        /* index into |name_of_file| */
21136  int name_length = (int) strlen (mp->name_of_file);
21137  str_room (name_length);
21138  for (k = 0; k < name_length; k++) {
21139    append_char (xord ((ASCII_code) mp->name_of_file[k]));
21140  }
21141  return mp_make_string (mp);
21142}
21143
21144
21145@ Now let's consider the ``driver''
21146routines by which \MP\ deals with file names
21147in a system-independent manner.  First comes a procedure that looks for a
21148file name in the input by taking the information from the input buffer.
21149(We can't use |get_next|, because the conversion to tokens would
21150destroy necessary information.)
21151
21152This procedure doesn't allow semicolons or percent signs to be part of
21153file names, because of other conventions of \MP.
21154{\sl The {\logos METAFONT\/}book} doesn't
21155use semicolons or percents immediately after file names, but some users
21156no doubt will find it natural to do so; therefore system-dependent
21157changes to allow such characters in file names should probably
21158be made with reluctance, and only when an entire file name that
21159includes special characters is ``quoted'' somehow.
21160@^system dependencies@>
21161
21162@c
21163static void mp_scan_file_name (MP mp) {
21164  mp_begin_name (mp);
21165  while (mp->buffer[loc] == ' ')
21166    incr (loc);
21167  while (1) {
21168    if ((mp->buffer[loc] == ';') || (mp->buffer[loc] == '%'))
21169      break;
21170    if (!mp_more_name (mp, mp->buffer[loc]))
21171      break;
21172    incr (loc);
21173  }
21174  mp_end_name (mp);
21175}
21176
21177
21178@ Here is another version that takes its input from a string.
21179
21180@<Declare subroutines for parsing file names@>=
21181void mp_str_scan_file (MP mp, mp_string s);
21182
21183@ @c
21184void mp_str_scan_file (MP mp, mp_string s) {
21185  size_t p, q;  /* current position and stopping point */
21186  mp_begin_name (mp);
21187  p = 0;
21188  q = s->len;
21189  while (p < q) {
21190    if (!mp_more_name (mp, *(s->str + p)))
21191      break;
21192    incr (p);
21193  }
21194  mp_end_name (mp);
21195}
21196
21197
21198@ And one that reads from a |char*|.
21199
21200@<Declare subroutines for parsing file names@>=
21201extern void mp_ptr_scan_file (MP mp, char *s);
21202
21203@ @c
21204void mp_ptr_scan_file (MP mp, char *s) {
21205  char *p, *q;  /* current position and stopping point */
21206  mp_begin_name (mp);
21207  p = s;
21208  q = p + strlen (s);
21209  while (p < q) {
21210    if (!mp_more_name (mp, (ASCII_code) (*p)))
21211      break;
21212    p++;
21213  }
21214  mp_end_name (mp);
21215}
21216
21217
21218@ The option variable |job_name| contains the file name that was first
21219\&{input} by the user. This name is used to initialize the |job_name| global
21220as well as the |mp_job_name| internal, and is extended by `\.{.log}' and
21221`\.{ps}' and `\.{.mem}' and `\.{.tfm}' in order to make the names of \MP's
21222output files.
21223
21224@<Glob...@>=
21225boolean log_opened;     /* has the transcript file been opened? */
21226char *log_name; /* full name of the log file */
21227
21228@ @<Option variables@>=
21229char *job_name; /* principal file name */
21230
21231@ Initially |job_name=NULL|; it becomes nonzero as soon as the true name is known.
21232We have |job_name=NULL| if and only if the `\.{log}' file has not been opened,
21233except of course for a short time just after |job_name| has become nonzero.
21234
21235@<Allocate or ...@>=
21236mp->job_name = mp_xstrdup (mp, opt->job_name);
21237/*|
21238if (mp->job_name != NULL) {
21239  char *s = mp->job_name + strlen (mp->job_name);
21240  while (s > mp->job_name) {
21241    if (*s == '.') {
21242      *s = '\0';
21243    }
21244    s--;
21245  }
21246}
21247|*/
21248if (opt->noninteractive) {
21249  if (mp->job_name == NULL)
21250    mp->job_name = mp_xstrdup (mp, mp->mem_name);
21251}
21252mp->log_opened = false;
21253
21254@ Cannot do this earlier because at the |<Allocate or ...>|, the string
21255pool is not yet initialized.
21256
21257@<Fix up |mp->internal[mp_job_name]|@>=
21258if (mp->job_name != NULL) {
21259  if (internal_string (mp_job_name) != 0)
21260    delete_str_ref (internal_string (mp_job_name));
21261  set_internal_string (mp_job_name, mp_rts (mp, mp->job_name));
21262}
21263
21264@ @<Dealloc variables@>=
21265xfree (mp->job_name);
21266
21267@ Here is a routine that manufactures the output file names, assuming that
21268|job_name<>0|. It ignores and changes the current settings of |cur_area|
21269and |cur_ext|.
21270
21271@d pack_cur_name mp_pack_file_name(mp, mp->cur_name,mp->cur_area,mp->cur_ext)
21272
21273@<Internal library ...@>=
21274void mp_pack_job_name (MP mp, const char *s);
21275
21276@ @c
21277void mp_pack_job_name (MP mp, const char *s) {                               /* |s = ".log"|, |".mem"|, |".ps"|, or .\\{nnn} */
21278  xfree (mp->cur_name);
21279  mp->cur_name = xstrdup (mp->job_name);
21280  xfree (mp->cur_area);
21281  mp->cur_area = xstrdup ("");
21282  xfree (mp->cur_ext);
21283  mp->cur_ext = xstrdup (s);
21284  pack_cur_name;
21285}
21286
21287
21288@ If some trouble arises when \MP\ tries to open a file, the following
21289routine calls upon the user to supply another file name. Parameter~|s|
21290is used in the error message to identify the type of file; parameter~|e|
21291is the default extension if none is given. Upon exit from the routine,
21292variables |cur_name|, |cur_area|, |cur_ext|, and |name_of_file| are
21293ready for another attempt at file opening.
21294
21295@<Internal library ...@>=
21296void mp_prompt_file_name (MP mp, const char *s, const char *e);
21297
21298@ @c
21299void mp_prompt_file_name (MP mp, const char *s, const char *e) {
21300  size_t k;     /* index into |buffer| */
21301  char *saved_cur_name;
21302  if (mp->interaction == mp_scroll_mode)
21303    wake_up_terminal();
21304  if (strcmp (s, "input file name") == 0) {
21305    mp_print_err (mp, "I can\'t open file `");
21306@.I can't find file x@>
21307  } else {
21308    mp_print_err (mp, "I can\'t write on file `");
21309@.I can't write on file x@>
21310  }
21311  if (strcmp (s, "file name for output") == 0) {
21312    mp_print (mp, mp->name_of_file);
21313  } else {
21314    mp_print_file_name (mp, mp->cur_name, mp->cur_area, mp->cur_ext);
21315  }
21316  mp_print (mp, "'.");
21317  if (strcmp (e, "") == 0)
21318    mp_show_context (mp);
21319  mp_print_nl (mp, "Please type another ");
21320  mp_print (mp, s);
21321@.Please type...@>;
21322  if (mp->noninteractive || mp->interaction < mp_scroll_mode)
21323    mp_fatal_error (mp, "*** (job aborted, file error in nonstop mode)");
21324@.job aborted, file error...@>;
21325  saved_cur_name = xstrdup (mp->cur_name);
21326  clear_terminal();
21327  prompt_input (": ");
21328  @<Scan file name in the buffer@>;
21329  if (strcmp (mp->cur_ext, "") == 0)
21330    mp->cur_ext = xstrdup (e);
21331  if (strlen (mp->cur_name) == 0) {
21332    mp->cur_name = saved_cur_name;
21333  } else {
21334    xfree (saved_cur_name);
21335  }
21336  pack_cur_name;
21337}
21338
21339
21340@ @<Scan file name in the buffer@>=
21341{
21342  mp_begin_name (mp);
21343  k = mp->first;
21344  while ((mp->buffer[k] == ' ') && (k < mp->last))
21345    incr (k);
21346  while (1) {
21347    if (k == mp->last)
21348      break;
21349    if (!mp_more_name (mp, mp->buffer[k]))
21350      break;
21351    incr (k);
21352  }
21353  mp_end_name (mp);
21354}
21355
21356
21357@ The |open_log_file| routine is used to open the transcript file and to help
21358it catch up to what has previously been printed on the terminal.
21359
21360@c
21361void mp_open_log_file (MP mp) {
21362  unsigned old_setting; /* previous |selector| setting */
21363  int k;        /* index into |months| and |buffer| */
21364  int l;        /* end of first input line */
21365  integer m;    /* the current month */
21366  const char *months = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC";
21367  /* abbreviations of month names */
21368  if (mp->log_opened)
21369    return;
21370  old_setting = mp->selector;
21371  if (mp->job_name == NULL) {
21372    mp->job_name = xstrdup ("mpout");
21373    @<Fix up |mp->internal[mp_job_name]|@>;
21374  }
21375  mp_pack_job_name (mp, ".log");
21376  while (!mp_open_out (mp, &mp->log_file, mp_filetype_log)) {
21377    @<Try to get a different log file name@>;
21378  }
21379  mp->log_name = xstrdup (mp->name_of_file);
21380  mp->selector = log_only;
21381  mp->log_opened = true;
21382  @<Print the banner line, including the date and time@>;
21383  mp->input_stack[mp->input_ptr] = mp->cur_input;
21384  /* make sure bottom level is in memory */
21385  if (!mp->noninteractive) {
21386    mp_print_nl (mp, "**");
21387@.**@>;
21388    l = mp->input_stack[0].limit_field - 1;     /* last position of first line */
21389    for (k = 0; k <= l; k++)
21390      mp_print_char (mp, mp->buffer[k]);
21391    mp_print_ln (mp);           /* now the transcript file contains the first line of input */
21392  }
21393  mp->selector = old_setting + 2;       /* |log_only| or |term_and_log| */
21394}
21395
21396
21397@ @<Dealloc variables@>=
21398xfree (mp->log_name);
21399
21400@ Sometimes |open_log_file| is called at awkward moments when \MP\ is
21401unable to print error messages or even to |show_context|.
21402The |prompt_file_name| routine can result in a |fatal_error|, but the |error|
21403routine will not be invoked because |log_opened| will be false.
21404
21405The normal idea of |mp_batch_mode| is that nothing at all should be written
21406on the terminal. However, in the unusual case that
21407no log file could be opened, we make an exception and allow
21408an explanatory message to be seen.
21409
21410Incidentally, the program always refers to the log file as a `\.{transcript
21411file}', because some systems cannot use the extension `\.{.log}' for
21412this file.
21413
21414@<Try to get a different log file name@>=
21415{
21416  mp->selector = term_only;
21417  mp_prompt_file_name (mp, "transcript file name", ".log");
21418}
21419
21420
21421@ @<Print the banner...@>=
21422{
21423  wlog (mp->banner);
21424  mp_print (mp, "  ");
21425  mp_print_int (mp, round_unscaled (internal_value (mp_day)));
21426  mp_print_char (mp, xord (' '));
21427  m = round_unscaled (internal_value (mp_month));
21428  for (k = 3 * m - 3; k < 3 * m; k++) {
21429    wlog_chr ((unsigned char) months[k]);
21430  }
21431  mp_print_char (mp, xord (' '));
21432  mp_print_int (mp, round_unscaled (internal_value (mp_year)));
21433  mp_print_char (mp, xord (' '));
21434  mp_print_dd (mp, round_unscaled (internal_value (mp_hour)));
21435  mp_print_char (mp, xord (':'));
21436  mp_print_dd (mp, round_unscaled (internal_value (mp_minute)));
21437}
21438
21439
21440@ The |try_extension| function tries to open an input file determined by
21441|cur_name|, |cur_area|, and the argument |ext|.  It returns |false| if it
21442can't find the file in |cur_area| or the appropriate system area.
21443
21444@c
21445static boolean mp_try_extension (MP mp, const char *ext) {
21446  mp_pack_file_name (mp, mp->cur_name, mp->cur_area, ext);
21447  in_name = xstrdup (mp->cur_name);
21448  in_area = xstrdup (mp->cur_area);
21449  in_ext = xstrdup (ext);
21450  if (mp_open_in (mp, &cur_file, mp_filetype_program)) {
21451    return true;
21452  } else {
21453    mp_pack_file_name (mp, mp->cur_name, NULL, ext);
21454    return mp_open_in (mp, &cur_file, mp_filetype_program);
21455  }
21456}
21457
21458
21459@ Let's turn now to the procedure that is used to initiate file reading
21460when an `\.{input}' command is being processed.
21461
21462@c
21463void mp_start_input (MP mp) {                               /* \MP\ will \.{input} something */
21464  char *fname = NULL;
21465  @<Put the desired file name in |(cur_name,cur_ext,cur_area)|@>;
21466  while (1) {
21467    mp_begin_file_reading (mp); /* set up |cur_file| and new level of input */
21468    if (strlen (mp->cur_ext) == 0) {
21469      if (mp_try_extension (mp, ".mp"))
21470        break;
21471      else if (mp_try_extension (mp, ""))
21472        break;
21473      else if (mp_try_extension (mp, ".mf"))
21474        break;
21475    } else if (mp_try_extension (mp, mp->cur_ext)) {
21476      break;
21477    }
21478    mp_end_file_reading (mp);   /* remove the level that didn't work */
21479    mp_prompt_file_name (mp, "input file name", "");
21480  }
21481  name = mp_make_name_string (mp);
21482  fname = xstrdup (mp->name_of_file);
21483  if (mp->job_name == NULL) {
21484    mp->job_name = xstrdup (mp->cur_name);
21485    @<Fix up |mp->internal[mp_job_name]|@>;
21486  }
21487  if (!mp->log_opened) {
21488    mp_open_log_file (mp);
21489  }                             /* |open_log_file| doesn't |show_context|, so |limit|
21490                                   and |loc| needn't be set to meaningful values yet */
21491  if (((int) mp->term_offset + (int) strlen (fname)) > (mp->max_print_line - 2))
21492    mp_print_ln (mp);
21493  else if ((mp->term_offset > 0) || (mp->file_offset > 0))
21494    mp_print_char (mp, xord (' '));
21495  mp_print_char (mp, xord ('('));
21496  incr (mp->open_parens);
21497  mp_print (mp, fname);
21498  xfree (fname);
21499  update_terminal();
21500  @<Flush |name| and replace it with |cur_name| if it won't be needed@>;
21501  @<Read the first line of the new file@>;
21502}
21503
21504
21505@ This code should be omitted if |make_name_string| returns something other
21506than just a copy of its argument and the full file name is needed for opening
21507\.{MPX} files or implementing the switch-to-editor option.
21508@^system dependencies@>
21509
21510@<Flush |name| and replace it with |cur_name| if it won't be needed@>=
21511mp_flush_string (mp, name);
21512name = mp_rts (mp, mp->cur_name);
21513xfree (mp->cur_name)
21514
21515
21516@ If the file is empty, it is considered to contain a single blank line,
21517so there is no need to test the return value.
21518
21519@<Read the first line...@>=
21520{
21521  line = 1;
21522  (void) mp_input_ln (mp, cur_file);
21523  mp_firm_up_the_line (mp);
21524  mp->buffer[limit] = xord ('%');
21525  mp->first = (size_t) (limit + 1);
21526  loc = start;
21527}
21528
21529
21530@ @<Put the desired file name in |(cur_name,cur_ext,cur_area)|@>=
21531while (token_state && (nloc == NULL))
21532  mp_end_token_list (mp);
21533if (token_state) {
21534  const char *hlp[] = {
21535         "Sorry...I've converted what follows to tokens,",
21536         "possibly garbaging the name you gave.",
21537         "Please delete the tokens and insert the name again.",
21538         NULL };
21539  mp_error (mp, "File names can't appear within macros", hlp, true);
21540@.File names can't...@>;
21541}
21542if (file_state) {
21543  mp_scan_file_name (mp);
21544} else {
21545  xfree (mp->cur_name);
21546  mp->cur_name = xstrdup ("");
21547  xfree (mp->cur_ext);
21548  mp->cur_ext = xstrdup ("");
21549  xfree (mp->cur_area);
21550  mp->cur_area = xstrdup ("");
21551}
21552
21553
21554@ The following simple routine starts reading the \.{MPX} file associated
21555with the current input file.
21556
21557@c
21558void mp_start_mpx_input (MP mp) {
21559  char *origname = NULL;        /* a copy of nameoffile */
21560  mp_pack_file_name (mp, in_name, in_area, in_ext);
21561  origname = xstrdup (mp->name_of_file);
21562  mp_pack_file_name (mp, in_name, in_area, ".mpx");
21563  if (!(mp->run_make_mpx) (mp, origname, mp->name_of_file))
21564    goto NOT_FOUND;
21565  mp_begin_file_reading (mp);
21566  if (!mp_open_in (mp, &cur_file, mp_filetype_program)) {
21567    mp_end_file_reading (mp);
21568    goto NOT_FOUND;
21569  }
21570  name = mp_make_name_string (mp);
21571  mp->mpx_name[iindex] = name;
21572  add_str_ref (name);
21573  @<Read the first line of the new file@>;
21574  xfree (origname);
21575  return;
21576NOT_FOUND:
21577  @<Explain that the \.{MPX} file can't be read and |succumb|@>;
21578  xfree (origname);
21579}
21580
21581
21582@ This should ideally be changed to do whatever is necessary to create the
21583\.{MPX} file given by |name_of_file| if it does not exist or if it is out
21584of date.  This requires invoking \.{MPtoTeX} on the |origname| and passing
21585the results through \TeX\ and \.{DVItoMP}.  (It is possible to use a
21586completely different typesetting program if suitable postprocessor is
21587available to perform the function of \.{DVItoMP}.)
21588@^system dependencies@>
21589
21590@ @<Exported types@>=
21591typedef int (*mp_makempx_cmd) (MP mp, char *origname, char *mtxname);
21592
21593@ @<Option variables@>=
21594mp_makempx_cmd run_make_mpx;
21595
21596@ @<Allocate or initialize ...@>=
21597set_callback_option (run_make_mpx);
21598
21599@ @<Declarations@>=
21600static int mp_run_make_mpx (MP mp, char *origname, char *mtxname);
21601
21602@ The default does nothing.
21603@c
21604int mp_run_make_mpx (MP mp, char *origname, char *mtxname) {
21605  (void) mp;
21606  (void) origname;
21607  (void) mtxname;
21608  return false;
21609}
21610
21611
21612@ @<Explain that the \.{MPX} file can't be read and |succumb|@>=
21613{
21614  const char *hlp[] = {
21615       "The two files given above are one of your source files",
21616       "and an auxiliary file I need to read to find out what your",
21617       "btex..etex blocks mean. If you don't know why I had trouble,",
21618       "try running it manually through MPtoTeX, TeX, and DVItoMP",
21619        NULL };
21620  if (mp->interaction == mp_error_stop_mode)
21621    wake_up_terminal();
21622  mp_print_nl (mp, ">> ");
21623  mp_print (mp, origname);
21624  mp_print_nl (mp, ">> ");
21625  mp_print (mp, mp->name_of_file);
21626  xfree (origname);
21627  if ( mp->interaction==mp_error_stop_mode )
21628    mp->interaction=mp_scroll_mode; /* no more interaction */
21629  if ( mp->log_opened )
21630    mp_error(mp, "! Unable to read mpx file", hlp, true);
21631  mp->history=mp_fatal_error_stop;
21632  mp_jump_out(mp); /* irrecoverable error */
21633}
21634
21635@ The last file-opening commands are for files accessed via the \&{readfrom}
21636@:read_from_}{\&{readfrom} primitive@>
21637operator and the \&{write} command.  Such files are stored in separate arrays.
21638@:write_}{\&{write} primitive@>
21639
21640@<Types in the outer block@>=
21641typedef unsigned int readf_index;       /* |0..max_read_files| */
21642typedef unsigned int write_index;       /* |0..max_write_files| */
21643
21644@ @<Glob...@>=
21645readf_index max_read_files;     /* maximum number of simultaneously open \&{readfrom} files */
21646void **rd_file; /* \&{readfrom} files */
21647char **rd_fname;        /* corresponding file name or 0 if file not open */
21648readf_index read_files; /* number of valid entries in the above arrays */
21649write_index max_write_files;    /* maximum number of simultaneously open \&{write} */
21650void **wr_file; /* \&{write} files */
21651char **wr_fname;        /* corresponding file name or 0 if file not open */
21652write_index write_files;        /* number of valid entries in the above arrays */
21653
21654@ @<Allocate or initialize ...@>=
21655mp->max_read_files = 8;
21656mp->rd_file = xmalloc ((mp->max_read_files + 1), sizeof (void *));
21657mp->rd_fname = xmalloc ((mp->max_read_files + 1), sizeof (char *));
21658memset (mp->rd_fname, 0, sizeof (char *) * (mp->max_read_files + 1));
21659mp->max_write_files = 8;
21660mp->wr_file = xmalloc ((mp->max_write_files + 1), sizeof (void *));
21661mp->wr_fname = xmalloc ((mp->max_write_files + 1), sizeof (char *));
21662memset (mp->wr_fname, 0, sizeof (char *) * (mp->max_write_files + 1));
21663
21664
21665@ This routine starts reading the file named by string~|s| without setting
21666|loc|, |limit|, or |name|.  It returns |false| if the file is empty or cannot
21667be opened.  Otherwise it updates |rd_file[n]| and |rd_fname[n]|.
21668
21669@c
21670static boolean mp_start_read_input (MP mp, char *s, readf_index n) {
21671  mp_ptr_scan_file (mp, s);
21672  pack_cur_name;
21673  mp_begin_file_reading (mp);
21674  if (!mp_open_in (mp, &mp->rd_file[n], (int) (mp_filetype_text + n)))
21675    goto NOT_FOUND;
21676  if (!mp_input_ln (mp, mp->rd_file[n])) {
21677    (mp->close_file) (mp, mp->rd_file[n]);
21678    goto NOT_FOUND;
21679  }
21680  mp->rd_fname[n] = xstrdup (s);
21681  return true;
21682NOT_FOUND:
21683  mp_end_file_reading (mp);
21684  return false;
21685}
21686
21687
21688@ Open |wr_file[n]| using file name~|s| and update |wr_fname[n]|.
21689
21690@<Declarations@>=
21691static void mp_open_write_file (MP mp, char *s, readf_index n);
21692
21693@ @c
21694void mp_open_write_file (MP mp, char *s, readf_index n) {
21695  mp_ptr_scan_file (mp, s);
21696  pack_cur_name;
21697  while (!mp_open_out (mp, &mp->wr_file[n], (int) (mp_filetype_text + n)))
21698    mp_prompt_file_name (mp, "file name for write output", "");
21699  mp->wr_fname[n] = xstrdup (s);
21700}
21701
21702
21703@* Introduction to the parsing routines.
21704We come now to the central nervous system that sparks many of \MP's activities.
21705By evaluating expressions, from their primary constituents to ever larger
21706subexpressions, \MP\ builds the structures that ultimately define complete
21707pictures or fonts of type.
21708
21709Four mutually recursive subroutines are involved in this process: We call them
21710$$\hbox{|scan_primary|, |scan_secondary|, |scan_tertiary|,
21711and |scan_expression|.}$$
21712@^recursion@>
21713Each of them is parameterless and begins with the first token to be scanned
21714already represented in |cur_cmd|, |cur_mod|, and |cur_sym|. After execution,
21715the value of the primary or secondary or tertiary or expression that was
21716found will appear in the global variables |cur_type| and |cur_exp|. The
21717token following the expression will be represented in |cur_cmd|, |cur_mod|,
21718and |cur_sym|.
21719
21720Technically speaking, the parsing algorithms are ``LL(1),'' more or less;
21721backup mechanisms have been added in order to provide reasonable error
21722recovery.
21723
21724@d cur_exp_value_boolean() number_to_int (mp->cur_exp.data.n)
21725@d cur_exp_value_number() mp->cur_exp.data.n
21726@d cur_exp_node() mp->cur_exp.data.node
21727@d cur_exp_str() mp->cur_exp.data.str
21728@d cur_exp_knot() mp->cur_exp.data.p
21729
21730@d set_cur_exp_value_scaled(A) do {
21731    if (cur_exp_str()) {
21732        delete_str_ref(cur_exp_str());
21733    }
21734    set_number_from_scaled (mp->cur_exp.data.n, (A));
21735    cur_exp_node() = NULL;
21736    cur_exp_str() = NULL;
21737    cur_exp_knot() = NULL;
21738  } while (0)
21739@d set_cur_exp_value_boolean(A) do {
21740    if (cur_exp_str()) {
21741        delete_str_ref(cur_exp_str());
21742    }
21743    set_number_from_int (mp->cur_exp.data.n, (A));
21744    cur_exp_node() = NULL;
21745    cur_exp_str() = NULL;
21746    cur_exp_knot() = NULL;
21747  } while (0)
21748@d set_cur_exp_value_number(A) do {
21749    if (cur_exp_str()) {
21750        delete_str_ref(cur_exp_str());
21751    }
21752    number_clone (mp->cur_exp.data.n, (A));
21753    cur_exp_node() = NULL;
21754    cur_exp_str() = NULL;
21755    cur_exp_knot() = NULL;
21756  } while (0)
21757@d set_cur_exp_node(A) do {
21758    if (cur_exp_str()) {
21759        delete_str_ref(cur_exp_str());
21760    }
21761    cur_exp_node() = A;
21762    cur_exp_str() = NULL;
21763    cur_exp_knot() = NULL;
21764    set_number_to_zero (mp->cur_exp.data.n);
21765  } while (0)
21766@d set_cur_exp_str(A) do {
21767    if (cur_exp_str()) {
21768        delete_str_ref(cur_exp_str());
21769    }
21770    cur_exp_str() = A;
21771    add_str_ref(cur_exp_str());
21772    cur_exp_node() = NULL;
21773    cur_exp_knot() = NULL;
21774    set_number_to_zero (mp->cur_exp.data.n);
21775  } while (0)
21776@d set_cur_exp_knot(A) do {
21777    if (cur_exp_str()) {
21778        delete_str_ref(cur_exp_str());
21779    }
21780    cur_exp_knot() = A;
21781    cur_exp_node() = NULL;
21782    cur_exp_str() = NULL;
21783    set_number_to_zero (mp->cur_exp.data.n);
21784  } while (0)
21785
21786
21787@ @<Glob...@>=
21788mp_value cur_exp;       /* the value of the expression just found */
21789
21790@ @<Set init...@>=
21791memset (&mp->cur_exp.data, 0, sizeof (mp_value));
21792new_number(mp->cur_exp.data.n);
21793
21794@ @<Free table ...@>=
21795free_number(mp->cur_exp.data.n);
21796
21797@ Many different kinds of expressions are possible, so it is wise to have
21798precise descriptions of what |cur_type| and |cur_exp| mean in all cases:
21799
21800\smallskip\hang
21801|cur_type=mp_vacuous| means that this expression didn't turn out to have a
21802value at all, because it arose from a \&{begingroup}$\,\ldots\,$\&{endgroup}
21803construction in which there was no expression before the \&{endgroup}.
21804In this case |cur_exp| has some irrelevant value.
21805
21806\smallskip\hang
21807|cur_type=mp_boolean_type| means that |cur_exp| is either |true_code|
21808or |false_code|.
21809
21810\smallskip\hang
21811|cur_type=mp_unknown_boolean| means that |cur_exp| points to a capsule
21812node that is in
21813a ring of equivalent booleans whose value has not yet been defined.
21814
21815\smallskip\hang
21816|cur_type=mp_string_type| means that |cur_exp| is a string number (i.e., an
21817integer in the range |0<=cur_exp<str_ptr|). That string's reference count
21818includes this particular reference.
21819
21820\smallskip\hang
21821|cur_type=mp_unknown_string| means that |cur_exp| points to a capsule
21822node that is in
21823a ring of equivalent strings whose value has not yet been defined.
21824
21825\smallskip\hang
21826|cur_type=mp_pen_type| means that |cur_exp| points to a node in a pen.  Nobody
21827else points to any of the nodes in this pen.  The pen may be polygonal or
21828elliptical.
21829
21830\smallskip\hang
21831|cur_type=mp_unknown_pen| means that |cur_exp| points to a capsule
21832node that is in
21833a ring of equivalent pens whose value has not yet been defined.
21834
21835\smallskip\hang
21836|cur_type=mp_path_type| means that |cur_exp| points to a the first node of
21837a path; nobody else points to this particular path. The control points of
21838the path will have been chosen.
21839
21840\smallskip\hang
21841|cur_type=mp_unknown_path| means that |cur_exp| points to a capsule
21842node that is in
21843a ring of equivalent paths whose value has not yet been defined.
21844
21845\smallskip\hang
21846|cur_type=mp_picture_type| means that |cur_exp| points to an edge header node.
21847There may be other pointers to this particular set of edges.  The header node
21848contains a reference count that includes this particular reference.
21849
21850\smallskip\hang
21851|cur_type=mp_unknown_picture| means that |cur_exp| points to a capsule
21852node that is in
21853a ring of equivalent pictures whose value has not yet been defined.
21854
21855\smallskip\hang
21856|cur_type=mp_transform_type| means that |cur_exp| points to a |mp_transform_type|
21857capsule node. The |value| part of this capsule
21858points to a transform node that contains six numeric values,
21859each of which is |independent|, |dependent|, |mp_proto_dependent|, or |known|.
21860
21861\smallskip\hang
21862|cur_type=mp_color_type| means that |cur_exp| points to a |color_type|
21863capsule node. The |value| part of this capsule
21864points to a color node that contains three numeric values,
21865each of which is |independent|, |dependent|, |mp_proto_dependent|, or |known|.
21866
21867\smallskip\hang
21868|cur_type=mp_cmykcolor_type| means that |cur_exp| points to a |mp_cmykcolor_type|
21869capsule node. The |value| part of this capsule
21870points to a color node that contains four numeric values,
21871each of which is |independent|, |dependent|, |mp_proto_dependent|, or |known|.
21872
21873\smallskip\hang
21874|cur_type=mp_pair_type| means that |cur_exp| points to a capsule
21875node whose type is |mp_pair_type|. The |value| part of this capsule
21876points to a pair node that contains two numeric values,
21877each of which is |independent|, |dependent|, |mp_proto_dependent|, or |known|.
21878
21879\smallskip\hang
21880|cur_type=mp_known| means that |cur_exp| is a |scaled| value.
21881
21882\smallskip\hang
21883|cur_type=mp_dependent| means that |cur_exp| points to a capsule node whose type
21884is |dependent|. The |dep_list| field in this capsule points to the associated
21885dependency list.
21886
21887\smallskip\hang
21888|cur_type=mp_proto_dependent| means that |cur_exp| points to a |mp_proto_dependent|
21889capsule node. The |dep_list| field in this capsule
21890points to the associated dependency list.
21891
21892\smallskip\hang
21893|cur_type=independent| means that |cur_exp| points to a capsule node
21894whose type is |independent|. This somewhat unusual case can arise, for
21895example, in the expression
21896`$x+\&{begingroup}\penalty0\,\&{string}\,x; 0\,\&{endgroup}$'.
21897
21898\smallskip\hang
21899|cur_type=mp_token_list| means that |cur_exp| points to a linked list of
21900tokens.
21901
21902\smallskip\noindent
21903The possible settings of |cur_type| have been listed here in increasing
21904numerical order. Notice that |cur_type| will never be |mp_numeric_type| or
21905|suffixed_macro| or |mp_unsuffixed_macro|, although variables of those types
21906are allowed.  Conversely, \MP\ has no variables of type |mp_vacuous| or
21907|token_list|.
21908
21909@ Capsules are non-symbolic nodes that have a similar meaning
21910to |cur_type| and |cur_exp|. Such nodes have |name_type=capsule|,
21911and their |type| field is one of the possibilities for |cur_type| listed above.
21912Also |link<=void| in capsules that aren't part of a token list.
21913
21914The |value| field of a capsule is, in most cases, the value that
21915corresponds to its |type|, as |cur_exp| corresponds to |cur_type|.
21916However, when |cur_exp| would point to a capsule,
21917no extra layer of indirection is present; the |value|
21918field is what would have been called |value(cur_exp)| if it had not been
21919encapsulated.  Furthermore, if the type is |dependent| or
21920|mp_proto_dependent|, the |value| field of a capsule is replaced by
21921|dep_list| and |prev_dep| fields, since dependency lists in capsules are
21922always part of the general |dep_list| structure.
21923
21924The |get_x_next| routine is careful not to change the values of |cur_type|
21925and |cur_exp| when it gets an expanded token. However, |get_x_next| might
21926call a macro, which might parse an expression, which might execute lots of
21927commands in a group; hence it's possible that |cur_type| might change
21928from, say, |mp_unknown_boolean| to |mp_boolean_type|, or from |dependent| to
21929|known| or |independent|, during the time |get_x_next| is called. The
21930programs below are careful to stash sensitive intermediate results in
21931capsules, so that \MP's generality doesn't cause trouble.
21932
21933Here's a procedure that illustrates these conventions. It takes
21934the contents of $(|cur_type|\kern-.3pt,|cur_exp|\kern-.3pt)$
21935and stashes them away in a
21936capsule. It is not used when |cur_type=mp_token_list|.
21937After the operation, |cur_type=mp_vacuous|; hence there is no need to
21938copy path lists or to update reference counts, etc.
21939
21940The special link |MP_VOID| is put on the capsule returned by
21941|stash_cur_exp|, because this procedure is used to store macro parameters
21942that must be easily distinguishable from token lists.
21943
21944@<Declare the stashing/unstashing routines@>=
21945static mp_node mp_stash_cur_exp (MP mp) {
21946  mp_node p;    /* the capsule that will be returned */
21947  mp_variable_type exp_type = mp->cur_exp.type;
21948  switch (exp_type) {
21949  case unknown_types:
21950  case mp_transform_type:
21951  case mp_color_type:
21952  case mp_pair_type:
21953  case mp_dependent:
21954  case mp_proto_dependent:
21955  case mp_independent:
21956  case mp_cmykcolor_type:
21957    p = cur_exp_node ();
21958    break;
21959    /* |case mp_path_type: case mp_pen_type: case mp_string_type:| */
21960  default:
21961    p = mp_get_value_node (mp);
21962    mp_name_type (p) = mp_capsule;
21963    mp_type (p) = mp->cur_exp.type;
21964    set_value_number (p, cur_exp_value_number ());    /* this also resets the rest to 0/NULL */
21965    if (cur_exp_str ())  {
21966      set_value_str (p, cur_exp_str ());
21967    } else if (cur_exp_knot ()) {
21968      set_value_knot (p, cur_exp_knot ());
21969    } else if (cur_exp_node ()) {
21970      set_value_node (p, cur_exp_node ());
21971    }
21972    break;
21973  }
21974  mp->cur_exp.type = mp_vacuous;
21975  mp_link (p) = MP_VOID;
21976  return p;
21977}
21978
21979
21980@ The inverse of |stash_cur_exp| is the following procedure, which
21981deletes an unnecessary capsule and puts its contents into |cur_type|
21982and |cur_exp|.
21983
21984The program steps of \MP\ can be divided into two categories: those in
21985which |cur_type| and |cur_exp| are ``alive'' and those in which they are
21986``dead,'' in the sense that |cur_type| and |cur_exp| contain relevant
21987information or not. It's important not to ignore them when they're alive,
21988and it's important not to pay attention to them when they're dead.
21989
21990There's also an intermediate category: If |cur_type=mp_vacuous|, then
21991|cur_exp| is irrelevant, hence we can proceed without caring if |cur_type|
21992and |cur_exp| are alive or dead. In such cases we say that |cur_type|
21993and |cur_exp| are {\sl dormant}. It is permissible to call |get_x_next|
21994only when they are alive or dormant.
21995
21996The \\{stash} procedure above assumes that |cur_type| and |cur_exp|
21997are alive or dormant. The \\{unstash} procedure assumes that they are
21998dead or dormant; it resuscitates them.
21999
22000@<Declare the stashing/unstashing...@>=
22001static void mp_unstash_cur_exp (MP mp, mp_node p);
22002
22003@ @c
22004void mp_unstash_cur_exp (MP mp, mp_node p) {
22005  mp->cur_exp.type = mp_type (p);
22006  switch (mp->cur_exp.type) {
22007  case unknown_types:
22008  case mp_transform_type:
22009  case mp_color_type:
22010  case mp_pair_type:
22011  case mp_dependent:
22012  case mp_proto_dependent:
22013  case mp_independent:
22014  case mp_cmykcolor_type:
22015    set_cur_exp_node (p);
22016    break;
22017  case mp_token_list: /* this is how symbols are stashed */
22018    set_cur_exp_node (value_node(p));
22019    mp_free_value_node (mp, p);
22020    break;
22021  case mp_path_type:
22022  case mp_pen_type:
22023    set_cur_exp_knot (value_knot (p));
22024    mp_free_value_node (mp, p);
22025    break;
22026  case mp_string_type:
22027    set_cur_exp_str (value_str (p));
22028    mp_free_value_node (mp, p);
22029    break;
22030  case mp_picture_type:
22031    set_cur_exp_node (value_node (p));
22032    mp_free_value_node (mp, p);
22033    break;
22034  case mp_boolean_type:
22035  case mp_known:
22036    set_cur_exp_value_number (value_number (p));
22037    mp_free_value_node (mp, p);
22038    break;
22039  default:
22040    set_cur_exp_value_number (value_number (p));
22041    if (value_knot(p)) {
22042      set_cur_exp_knot (value_knot (p));
22043    } else if (value_node(p)) {
22044      set_cur_exp_node (value_node (p));
22045    } else if (value_str(p)) {
22046      set_cur_exp_str (value_str (p));
22047    }
22048    mp_free_value_node (mp, p);
22049    break;
22050  }
22051}
22052
22053
22054@ The following procedure prints the values of expressions in an
22055abbreviated format. If its first parameter |p| is NULL, the value of
22056|(cur_type,cur_exp)| is displayed; otherwise |p| should be a capsule
22057containing the desired value. The second parameter controls the amount of
22058output. If it is~0, dependency lists will be abbreviated to
22059`\.{linearform}' unless they consist of a single term.  If it is greater
22060than~1, complicated structures (pens, pictures, and paths) will be displayed
22061in full.
22062@.linearform@>
22063
22064@<Declarations@>=
22065@<Declare the procedure called |print_dp|@>;
22066@<Declare the stashing/unstashing routines@>;
22067static void mp_print_exp (MP mp, mp_node p, quarterword verbosity);
22068
22069@ @c
22070void mp_print_exp (MP mp, mp_node p, quarterword verbosity) {
22071  boolean restore_cur_exp;      /* should |cur_exp| be restored? */
22072  mp_variable_type t;   /* the type of the expression */
22073  mp_number vv;       /* the value of the expression */
22074  mp_node v = NULL;
22075  new_number (vv);
22076  if (p != NULL) {
22077    restore_cur_exp = false;
22078  } else {
22079    p = mp_stash_cur_exp (mp);
22080    restore_cur_exp = true;
22081  }
22082  t = mp_type (p);
22083  if (t < mp_dependent) {       /* no dep list, could be a capsule */
22084    if (t != mp_vacuous && t != mp_known && value_node (p) != NULL)
22085      v = value_node (p);
22086    else
22087      number_clone (vv, value_number (p));
22088  } else if (t < mp_independent) {
22089    v = (mp_node) dep_list ((mp_value_node) p);
22090  }
22091  @<Print an abbreviated value of |v| or |vv| with format depending on |t|@>;
22092  if (restore_cur_exp)
22093    mp_unstash_cur_exp (mp, p);
22094  free_number (vv);
22095}
22096
22097
22098@ @<Print an abbreviated value of |v| or |vv| with format depending on |t|@>=
22099switch (t) {
22100case mp_vacuous:
22101  mp_print (mp, "vacuous");
22102  break;
22103case mp_boolean_type:
22104  if (number_to_boolean (vv) == mp_true_code)
22105    mp_print (mp, "true");
22106  else
22107    mp_print (mp, "false");
22108  break;
22109case unknown_types:
22110case mp_numeric_type:
22111  @<Display a variable that's been declared but not defined@>;
22112  break;
22113case mp_string_type:
22114  mp_print_char (mp, xord ('"'));
22115  mp_print_str (mp, value_str (p));
22116  mp_print_char (mp, xord ('"'));
22117  break;
22118case mp_pen_type:
22119case mp_path_type:
22120case mp_picture_type:
22121  @<Display a complex type@>;
22122  break;
22123case mp_transform_type:
22124  if (number_zero (vv) && v == NULL)
22125    mp_print_type (mp, t);
22126  else
22127    @<Display a transform node@>;
22128  break;
22129case mp_color_type:
22130  if (number_zero (vv) && v == NULL)
22131    mp_print_type (mp, t);
22132  else
22133    @<Display a color node@>;
22134  break;
22135case mp_pair_type:
22136  if (number_zero (vv) && v == NULL)
22137    mp_print_type (mp, t);
22138  else
22139    @<Display a pair node@>;
22140  break;
22141case mp_cmykcolor_type:
22142  if (number_zero (vv) && v == NULL)
22143    mp_print_type (mp, t);
22144  else
22145    @<Display a cmykcolor node@>;
22146  break;
22147case mp_known:
22148  print_number (vv);
22149  break;
22150case mp_dependent:
22151case mp_proto_dependent:
22152  mp_print_dp (mp, t, (mp_value_node) v, verbosity);
22153  break;
22154case mp_independent:
22155  mp_print_variable_name (mp, p);
22156  break;
22157default:
22158  mp_confusion (mp, "exp");
22159  break;
22160@:this can't happen exp}{\quad exp@>
22161}
22162
22163
22164@ @<Display big node item |v|@>=
22165{
22166  if (mp_type (v) == mp_known)
22167    print_number (value_number (v));
22168  else if (mp_type (v) == mp_independent)
22169    mp_print_variable_name (mp, v);
22170  else
22171    mp_print_dp (mp, mp_type (v), (mp_value_node) dep_list ((mp_value_node) v),
22172                 verbosity);
22173}
22174
22175
22176@ In these cases, |v| starts as the big node.
22177
22178@<Display a pair node@>=
22179{
22180  mp_node vvv = v;
22181  mp_print_char (mp, xord ('('));
22182  /* clang: dereference of null pointer */ assert(vvv);
22183  v = x_part (vvv);
22184  @<Display big node item |v|@>;
22185  mp_print_char (mp, xord (','));
22186  v = y_part (vvv);
22187  @<Display big node item |v|@>;
22188  mp_print_char (mp, xord (')'));
22189}
22190
22191
22192@ @<Display a transform node@>=
22193{
22194  mp_node vvv = v;
22195  mp_print_char (mp, xord ('('));
22196  /* clang: dereference of null pointer */ assert(vvv);
22197  v = tx_part (vvv);
22198  @<Display big node item |v|@>;
22199  mp_print_char (mp, xord (','));
22200  v = ty_part (vvv);
22201  @<Display big node item |v|@>;
22202  mp_print_char (mp, xord (','));
22203  v = xx_part (vvv);
22204  @<Display big node item |v|@>;
22205  mp_print_char (mp, xord (','));
22206  v = xy_part (vvv);
22207  @<Display big node item |v|@>;
22208  mp_print_char (mp, xord (','));
22209  v = yx_part (vvv);
22210  @<Display big node item |v|@>;
22211  mp_print_char (mp, xord (','));
22212  v = yy_part (vvv);
22213  @<Display big node item |v|@>;
22214  mp_print_char (mp, xord (')'));
22215}
22216
22217
22218@ @<Display a color node@>=
22219{
22220  mp_node vvv = v;
22221  mp_print_char (mp, xord ('('));
22222  /* clang: dereference of null pointer */ assert(vvv);
22223  v = red_part (vvv);
22224  @<Display big node item |v|@>;
22225  mp_print_char (mp, xord (','));
22226  v = green_part (vvv);
22227  @<Display big node item |v|@>;
22228  mp_print_char (mp, xord (','));
22229  v = blue_part (vvv);
22230  @<Display big node item |v|@>;
22231  mp_print_char (mp, xord (')'));
22232}
22233
22234
22235@ @<Display a cmykcolor node@>=
22236{
22237  mp_node vvv = v;
22238  mp_print_char (mp, xord ('('));
22239  /* clang: dereference of null pointer */ assert(vvv);
22240  v = cyan_part (vvv);
22241  @<Display big node item |v|@>;
22242  mp_print_char (mp, xord (','));
22243  v = magenta_part (vvv);
22244  @<Display big node item |v|@>;
22245  mp_print_char (mp, xord (','));
22246  v = yellow_part (vvv);
22247  @<Display big node item |v|@>;
22248  mp_print_char (mp, xord (','));
22249  v = black_part (vvv);
22250  @<Display big node item |v|@>;
22251  mp_print_char (mp, xord (')'));
22252}
22253
22254
22255@ Values of type \&{picture}, \&{path}, and \&{pen} are displayed verbosely
22256in the log file only, unless the user has given a positive value to
22257\\{tracingonline}.
22258
22259@<Display a complex type@>=
22260if (verbosity <= 1) {
22261  mp_print_type (mp, t);
22262} else {
22263  if (mp->selector == term_and_log)
22264    if (number_nonpositive (internal_value (mp_tracing_online))) {
22265      mp->selector = term_only;
22266      mp_print_type (mp, t);
22267      mp_print (mp, " (see the transcript file)");
22268      mp->selector = term_and_log;
22269    };
22270  switch (t) {
22271  case mp_pen_type:
22272    mp_print_pen (mp, value_knot (p), "", false);
22273    break;
22274  case mp_path_type:
22275    mp_print_path (mp, value_knot (p), "", false);
22276    break;
22277  case mp_picture_type:
22278    mp_print_edges (mp, v, "", false);
22279    break;
22280  default:
22281    break;
22282  }
22283}
22284
22285
22286@ @<Declare the procedure called |print_dp|@>=
22287static void mp_print_dp (MP mp, quarterword t, mp_value_node p,
22288                         quarterword verbosity) {
22289  mp_value_node q;      /* the node following |p| */
22290  q = (mp_value_node) mp_link (p);
22291  if ((dep_info (q) == NULL) || (verbosity > 0))
22292    mp_print_dependency (mp, p, t);
22293  else
22294    mp_print (mp, "linearform");
22295}
22296
22297
22298@ The displayed name of a variable in a ring will not be a capsule unless
22299the ring consists entirely of capsules.
22300
22301@<Display a variable that's been declared but not defined@>=
22302{
22303  mp_print_type (mp, t);
22304  if (v != NULL) {
22305    mp_print_char (mp, xord (' '));
22306    while ((mp_name_type (v) == mp_capsule) && (v != p))
22307      v = value_node (v);
22308    mp_print_variable_name (mp, v);
22309  };
22310}
22311
22312
22313@ When errors are detected during parsing, it is often helpful to
22314display an expression just above the error message, using |disp_err|
22315just before |mp_error|.
22316
22317@<Declarations@>=
22318static void mp_disp_err (MP mp, mp_node p);
22319
22320@ @c
22321void mp_disp_err (MP mp, mp_node p) {
22322  if (mp->interaction == mp_error_stop_mode)
22323    wake_up_terminal();
22324  mp_print_nl (mp, ">> ");
22325@.>>@>;
22326  mp_print_exp (mp, p, 1);      /* ``medium verbose'' printing of the expression */
22327}
22328
22329
22330@ If |cur_type| and |cur_exp| contain relevant information that should
22331be recycled, we will use the following procedure, which changes |cur_type|
22332to |known| and stores a given value in |cur_exp|. We can think of |cur_type|
22333and |cur_exp| as either alive or dormant after this has been done,
22334because |cur_exp| will not contain a pointer value.
22335
22336@ @c
22337void mp_flush_cur_exp (MP mp, mp_value v) {
22338  if (is_number(mp->cur_exp.data.n)) {
22339    free_number(mp->cur_exp.data.n);
22340  }
22341  switch (mp->cur_exp.type) {
22342  case unknown_types:
22343  case mp_transform_type:
22344  case mp_color_type:
22345  case mp_pair_type:
22346  case mp_dependent:
22347  case mp_proto_dependent:
22348  case mp_independent:
22349  case mp_cmykcolor_type:
22350    mp_recycle_value (mp, cur_exp_node ());
22351    mp_free_value_node (mp, cur_exp_node ());
22352    break;
22353  case mp_string_type:
22354    delete_str_ref (cur_exp_str ());
22355    break;
22356  case mp_pen_type:
22357  case mp_path_type:
22358    mp_toss_knot_list (mp, cur_exp_knot ());
22359    break;
22360  case mp_picture_type:
22361    delete_edge_ref (cur_exp_node ());
22362    break;
22363  default:
22364    break;
22365  }
22366  mp->cur_exp = v;
22367  mp->cur_exp.type = mp_known;
22368}
22369
22370
22371@ There's a much more general procedure that is capable of releasing
22372the storage associated with any non-symbolic value packet.
22373
22374@<Declarations@>=
22375static void mp_recycle_value (MP mp, mp_node p);
22376
22377@ @c
22378static void mp_recycle_value (MP mp, mp_node p) {
22379  mp_variable_type t;   /* a type code */
22380  FUNCTION_TRACE2 ("mp_recycle_value(%p)\n", p);
22381  if (p==NULL || p==MP_VOID)
22382    return;
22383  t = mp_type (p);
22384  switch (t) {
22385  case mp_vacuous:
22386  case mp_boolean_type:
22387  case mp_known:
22388  case mp_numeric_type:
22389    break;
22390  case unknown_types:
22391    mp_ring_delete (mp, p);
22392    break;
22393  case mp_string_type:
22394    delete_str_ref (value_str (p));
22395    break;
22396  case mp_path_type:
22397  case mp_pen_type:
22398    mp_toss_knot_list (mp, value_knot (p));
22399    break;
22400  case mp_picture_type:
22401    delete_edge_ref (value_node (p));
22402    break;
22403  case mp_cmykcolor_type:
22404    if (value_node (p) != NULL) {
22405      mp_recycle_value (mp, cyan_part (value_node (p)));
22406      mp_recycle_value (mp, magenta_part (value_node (p)));
22407      mp_recycle_value (mp, yellow_part (value_node (p)));
22408      mp_recycle_value (mp, black_part (value_node (p)));
22409      mp_free_value_node (mp, cyan_part (value_node (p)));
22410      mp_free_value_node (mp, magenta_part (value_node (p)));
22411      mp_free_value_node (mp, black_part (value_node (p)));
22412      mp_free_value_node (mp, yellow_part (value_node (p)));
22413      mp_free_node (mp, value_node (p), cmykcolor_node_size);
22414    }
22415    break;
22416  case mp_pair_type:
22417    if (value_node (p) != NULL) {
22418      mp_recycle_value (mp, x_part (value_node (p)));
22419      mp_recycle_value (mp, y_part (value_node (p)));
22420      mp_free_value_node (mp, x_part (value_node (p)));
22421      mp_free_value_node (mp, y_part (value_node (p)));
22422      mp_free_pair_node (mp, value_node (p));
22423    }
22424    break;
22425  case mp_color_type:
22426    if (value_node (p) != NULL) {
22427      mp_recycle_value (mp, red_part (value_node (p)));
22428      mp_recycle_value (mp, green_part (value_node (p)));
22429      mp_recycle_value (mp, blue_part (value_node (p)));
22430      mp_free_value_node (mp, red_part (value_node (p)));
22431      mp_free_value_node (mp, green_part (value_node (p)));
22432      mp_free_value_node (mp, blue_part (value_node (p)));
22433      mp_free_node (mp, value_node (p), color_node_size);
22434    }
22435    break;
22436  case mp_transform_type:
22437    if (value_node (p) != NULL) {
22438      mp_recycle_value (mp, tx_part (value_node (p)));
22439      mp_recycle_value (mp, ty_part (value_node (p)));
22440      mp_recycle_value (mp, xx_part (value_node (p)));
22441      mp_recycle_value (mp, xy_part (value_node (p)));
22442      mp_recycle_value (mp, yx_part (value_node (p)));
22443      mp_recycle_value (mp, yy_part (value_node (p)));
22444      mp_free_value_node (mp, tx_part (value_node (p)));
22445      mp_free_value_node (mp, ty_part (value_node (p)));
22446      mp_free_value_node (mp, xx_part (value_node (p)));
22447      mp_free_value_node (mp, xy_part (value_node (p)));
22448      mp_free_value_node (mp, yx_part (value_node (p)));
22449      mp_free_value_node (mp, yy_part (value_node (p)));
22450      mp_free_node (mp, value_node (p), transform_node_size);
22451    }
22452    break;
22453  case mp_dependent:
22454  case mp_proto_dependent:
22455    /* Recycle a dependency list */
22456    {
22457      mp_value_node qq = (mp_value_node) dep_list ((mp_value_node) p);
22458      while (dep_info (qq) != NULL)
22459        qq = (mp_value_node) mp_link (qq);
22460      set_mp_link (prev_dep ((mp_value_node) p), mp_link (qq));
22461      set_prev_dep (mp_link (qq), prev_dep ((mp_value_node) p));
22462      set_mp_link (qq, NULL);
22463      mp_flush_node_list (mp, (mp_node) dep_list ((mp_value_node) p));
22464    }
22465    break;
22466  case mp_independent:
22467    @<Recycle an independent variable@>;
22468    break;
22469  case mp_token_list:
22470  case mp_structured:
22471    mp_confusion (mp, "recycle");
22472    break;
22473  case mp_unsuffixed_macro:
22474  case mp_suffixed_macro:
22475    mp_delete_mac_ref (mp, value_node (p));
22476    break;
22477  default: /* there are no other valid cases, but please the compiler */
22478    break;
22479  }
22480  mp_type (p) = mp_undefined;
22481}
22482
22483@ When an independent variable disappears, it simply fades away, unless
22484something depends on it. In the latter case, a dependent variable whose
22485coefficient of dependence is maximal will take its place.
22486The relevant algorithm is due to Ignacio~A. Zabala, who implemented it
22487as part of his Ph.n->data. thesis (Stanford University, December 1982).
22488@^Zabala Salelles, Ignacio Andr\'es@>
22489
22490For example, suppose that variable $x$ is being recycled, and that the
22491only variables depending on~$x$ are $y=2x+a$ and $z=x+b$. In this case
22492we want to make $y$ independent and $z=.5y-.5a+b$; no other variables
22493will depend on~$y$. If $\\{tracingequations}>0$ in this situation,
22494we will print `\.{\#\#\# -2x=-y+a}'.
22495
22496There's a slight complication, however: An independent variable $x$
22497can occur both in dependency lists and in proto-dependency lists.
22498This makes it necessary to be careful when deciding which coefficient
22499is maximal.
22500
22501Furthermore, this complication is not so slight when
22502a proto-dependent variable is chosen to become independent. For example,
22503suppose that $y=2x+100a$ is proto-dependent while $z=x+b$ is dependent;
22504then we must change $z=.5y-50a+b$ to a proto-dependency, because of the
22505large coefficient `50'.
22506
22507In order to deal with these complications without wasting too much time,
22508we shall link together the occurrences of~$x$ among all the linear
22509dependencies, maintaining separate lists for the dependent and
22510proto-dependent cases.
22511
22512@<Recycle an independent variable@>=
22513{
22514  mp_value_node q, r, s;
22515  mp_node pp;   /* link manipulation register */
22516  mp_number v ;        /* a value */
22517  mp_number test;      /* a temporary value */
22518  new_number (test);
22519  new_number (v);
22520  if (t < mp_dependent)
22521    number_clone (v, value_number (p));
22522  set_number_to_zero(mp->max_c[mp_dependent]);
22523  set_number_to_zero(mp->max_c[mp_proto_dependent]);
22524  mp->max_link[mp_dependent] = NULL;
22525  mp->max_link[mp_proto_dependent] = NULL;
22526  q = (mp_value_node) mp_link (mp->dep_head);
22527  while (q != mp->dep_head) {
22528    s = (mp_value_node) mp->temp_head;
22529    set_mp_link (s, dep_list (q));
22530    while (1) {
22531      r = (mp_value_node) mp_link (s);
22532      if (dep_info (r) == NULL)
22533        break;
22534      if (dep_info (r) != p) {
22535        s = r;
22536      } else {
22537        t = mp_type (q);
22538        if (mp_link (s) == dep_list (q)) {      /* reset the |dep_list| */
22539          set_dep_list (q, mp_link (r));
22540        }
22541        set_mp_link (s, mp_link (r));
22542        set_dep_info (r, (mp_node) q);
22543        number_clone (test, dep_value (r));
22544        number_abs (test);
22545        if (number_greater (test, mp->max_c[t])) {
22546          /* Record a new maximum coefficient of type |t| */
22547          if (number_positive(mp->max_c[t])) {
22548            set_mp_link (mp->max_ptr[t], (mp_node) mp->max_link[t]);
22549            mp->max_link[t] = mp->max_ptr[t];
22550          }
22551          number_clone (mp->max_c[t], test);
22552          mp->max_ptr[t] = r;
22553        } else {
22554          set_mp_link (r, (mp_node) mp->max_link[t]);
22555          mp->max_link[t] = r;
22556        }
22557      }
22558    }
22559    q = (mp_value_node) mp_link (r);
22560  }
22561  if (number_positive(mp->max_c[mp_dependent]) || number_positive(mp->max_c[mp_proto_dependent])) {
22562    /* Choose a dependent variable to take the place of the disappearing
22563       independent variable, and change all remaining dependencies
22564       accordingly */
22565    mp_number test, ret; /* temporary use */
22566    new_number (ret);
22567    new_number (test);
22568    number_clone (test, mp->max_c[mp_dependent]);
22569    number_divide_int (test, 4096);
22570    if (number_greaterequal(test, mp->max_c[mp_proto_dependent]))
22571      t = mp_dependent;
22572    else
22573      t = mp_proto_dependent;
22574
22575    /* Let |s=max_ptr[t]|. At this point we have $|value|(s)=\pm|max_c|[t]$,
22576       and |dep_info(s)| points to the dependent variable~|pp| of type~|t| from
22577       whose dependency list we have removed node~|s|. We must reinsert
22578       node~|s| into the dependency list, with coefficient $-1.0$, and with
22579       |pp| as the new independent variable. Since |pp| will have a larger serial
22580       number than any other variable, we can put node |s| at the head of the
22581       list. */
22582    /* Determine the dependency list |s| to substitute for the independent
22583       variable~|p| */
22584
22585    s = mp->max_ptr[t];
22586    pp = (mp_node) dep_info (s);
22587    number_clone (v, dep_value (s));
22588    if (t == mp_dependent) {
22589      set_dep_value (s, fraction_one_t);
22590    } else {
22591      set_dep_value (s, unity_t);
22592    }
22593    number_negate(dep_value(s));
22594    r = (mp_value_node) dep_list ((mp_value_node) pp);
22595    set_mp_link (s, (mp_node) r);
22596    while (dep_info (r) != NULL)
22597      r = (mp_value_node) mp_link (r);
22598    q = (mp_value_node) mp_link (r);
22599    set_mp_link (r, NULL);
22600    set_prev_dep (q, prev_dep ((mp_value_node) pp));
22601    set_mp_link (prev_dep ((mp_value_node) pp), (mp_node) q);
22602    mp_new_indep (mp, pp);
22603    if (cur_exp_node () == pp && mp->cur_exp.type == t)
22604      mp->cur_exp.type = mp_independent;
22605    if (number_positive (internal_value (mp_tracing_equations))) {
22606      /* Show the transformed dependency */
22607      if (mp_interesting (mp, p)) {
22608        mp_begin_diagnostic (mp);
22609        mp_show_transformed_dependency(mp, v, t, p);
22610        mp_print_dependency (mp, s, t);
22611        mp_end_diagnostic (mp, false);
22612      }
22613    }
22614
22615    t = (quarterword) (mp_dependent + mp_proto_dependent - t);    /* complement |t| */
22616    if (number_positive(mp->max_c[t])) {
22617      /* we need to pick up an unchosen dependency */
22618      set_mp_link (mp->max_ptr[t], (mp_node) mp->max_link[t]);
22619      mp->max_link[t] = mp->max_ptr[t];
22620    }
22621    /* Finally, there are dependent and proto-dependent variables whose
22622       dependency lists must be brought up to date. */
22623    if (t != mp_dependent) {
22624      /* Substitute new dependencies in place of |p| */
22625      for (t = mp_dependent; t <= mp_proto_dependent; t=t+1) {
22626        r = mp->max_link[t];
22627        while (r != NULL) {
22628          q = (mp_value_node) dep_info (r);
22629          number_clone (test, v);
22630          number_negate (test);
22631          make_fraction (ret, dep_value (r), test);
22632          set_dep_list (q, mp_p_plus_fq (mp, (mp_value_node) dep_list (q), ret, s, t, mp_dependent));
22633          if (dep_list (q) == (mp_node) mp->dep_final)
22634            mp_make_known (mp, q, mp->dep_final);
22635          q = r;
22636          r = (mp_value_node) mp_link (r);
22637          mp_free_dep_node (mp, q);
22638        }
22639      }
22640    } else {
22641      /* Substitute new proto-dependencies in place of |p| */
22642      for (t = mp_dependent; t <= mp_proto_dependent; t=t+1) {
22643        r = mp->max_link[t];
22644        while (r != NULL) {
22645          q = (mp_value_node) dep_info (r);
22646          if (t == mp_dependent) {    /* for safety's sake, we change |q| to |mp_proto_dependent| */
22647            if (cur_exp_node () == (mp_node) q && mp->cur_exp.type == mp_dependent)
22648              mp->cur_exp.type = mp_proto_dependent;
22649            set_dep_list (q, mp_p_over_v (mp, (mp_value_node) dep_list (q),
22650                                           unity_t, mp_dependent,
22651                                           mp_proto_dependent));
22652            mp_type (q) = mp_proto_dependent;
22653            fraction_to_round_scaled (dep_value (r));
22654          }
22655          number_clone (test, v);
22656          number_negate (test);
22657          make_scaled (ret, dep_value (r), test);
22658          set_dep_list (q, mp_p_plus_fq (mp, (mp_value_node) dep_list (q),
22659                                             ret, s,
22660                                             mp_proto_dependent,
22661                                             mp_proto_dependent));
22662          if (dep_list (q) == (mp_node) mp->dep_final)
22663            mp_make_known (mp, q, mp->dep_final);
22664          q = r;
22665          r = (mp_value_node) mp_link (r);
22666          mp_free_dep_node (mp, q);
22667        }
22668      }
22669    }
22670    mp_flush_node_list (mp, (mp_node) s);
22671    if (mp->fix_needed)
22672      mp_fix_dependencies (mp);
22673    check_arith();
22674    free_number (ret);
22675  }
22676  free_number (v);
22677  free_number(test);
22678}
22679
22680@ @<Declarations@>=
22681static void mp_show_transformed_dependency(MP mp, mp_number v, mp_variable_type t, mp_node p);
22682
22683@ @c
22684static void mp_show_transformed_dependency(MP mp, mp_number v, mp_variable_type t, mp_node p)
22685{
22686  mp_number vv;   /* for temp use */
22687  new_number (vv);
22688  mp_print_nl (mp, "### ");
22689  if (number_positive(v))
22690    mp_print_char (mp, xord ('-'));
22691  if (t == mp_dependent) {
22692    number_clone (vv, mp->max_c[mp_dependent]);
22693    fraction_to_round_scaled (vv);
22694  } else {
22695    number_clone (vv, mp->max_c[mp_proto_dependent]);
22696  }
22697  if (!number_equal(vv, unity_t)) {
22698    print_number (vv);
22699  }
22700  mp_print_variable_name (mp, p);
22701  while (indep_scale (p) > 0) {
22702    mp_print (mp, "*4");
22703    set_indep_scale(p, indep_scale(p)-2);
22704  }
22705  if (t == mp_dependent)
22706    mp_print_char (mp, xord ('='));
22707  else
22708    mp_print (mp, " = ");
22709  free_number (vv);
22710}
22711
22712
22713@ The code for independency removal makes use of three non-symbolic arrays.
22714
22715@<Glob...@>=
22716mp_number max_c[mp_proto_dependent + 1];  /* max coefficient magnitude */
22717mp_value_node max_ptr[mp_proto_dependent + 1];  /* where |p| occurs with |max_c| */
22718mp_value_node max_link[mp_proto_dependent + 1]; /* other occurrences of |p| */
22719
22720
22721@ @<Initialize table ... @>=
22722{
22723  int i;
22724  for (i=0;i<mp_proto_dependent + 1;i++) {
22725    new_number (mp->max_c[i]);
22726  }
22727}
22728
22729@ @<Dealloc...@>=
22730{
22731  int i;
22732  for (i=0;i<mp_proto_dependent + 1;i++) {
22733    free_number (mp->max_c[i]);
22734  }
22735}
22736
22737@ A global variable |var_flag| is set to a special command code
22738just before \MP\ calls |scan_expression|, if the expression should be
22739treated as a variable when this command code immediately follows. For
22740example, |var_flag| is set to |assignment| at the beginning of a
22741statement, because we want to know the {\sl location\/} of a variable at
22742the left of `\.{:=}', not the {\sl value\/} of that variable.
22743
22744The |scan_expression| subroutine calls |scan_tertiary|,
22745which calls |scan_secondary|, which calls |scan_primary|, which sets
22746|var_flag:=0|. In this way each of the scanning routines ``knows''
22747when it has been called with a special |var_flag|, but |var_flag| is
22748usually zero.
22749
22750A variable preceding a command that equals |var_flag| is converted to a
22751token list rather than a value. Furthermore, an `\.{=}' sign following an
22752expression with |var_flag=assignment| is not considered to be a relation
22753that produces boolean expressions.
22754
22755
22756@<Glob...@>=
22757int var_flag;   /* command that wants a variable */
22758
22759@ @<Set init...@>=
22760mp->var_flag = 0;
22761
22762@* Parsing primary expressions.
22763The first parsing routine, |scan_primary|, is also the most complicated one,
22764since it involves so many different cases. But each case---with one
22765exception---is fairly simple by itself.
22766
22767When |scan_primary| begins, the first token of the primary to be scanned
22768should already appear in |cur_cmd|, |cur_mod|, and |cur_sym|. The values
22769of |cur_type| and |cur_exp| should be either dead or dormant, as explained
22770earlier. If |cur_cmd| is not between |min_primary_command| and
22771|max_primary_command|, inclusive, a syntax error will be signaled.
22772
22773Later we'll come to procedures that perform actual operations like
22774addition, square root, and so on; our purpose now is to do the parsing.
22775But we might as well mention those future procedures now, so that the
22776suspense won't be too bad:
22777
22778\smallskip
22779|do_nullary(c)| does primitive operations that have no operands (e.g.,
22780`\&{true}' or `\&{pencircle}');
22781
22782\smallskip
22783|do_unary(c)| applies a primitive operation to the current expression;
22784
22785\smallskip
22786|do_binary(p,c)| applies a primitive operation to the capsule~|p|
22787and the current expression.
22788
22789@<Declare the basic parsing subroutines@>=
22790static void check_for_mediation (MP mp);
22791void mp_scan_primary (MP mp) {
22792  mp_command_code my_var_flag;      /* initial value of |my_var_flag| */
22793  my_var_flag = mp->var_flag;
22794  mp->var_flag = 0;
22795RESTART:
22796  check_arith();
22797  /* Supply diagnostic information, if requested */
22798  if (mp->interrupt != 0) {
22799    if (mp->OK_to_interrupt) {
22800      mp_back_input (mp);
22801      check_interrupt;
22802      mp_get_x_next (mp);
22803    }
22804  }
22805  switch (cur_cmd()) {
22806  case mp_left_delimiter:
22807  {
22808    /* Scan a delimited primary */
22809    mp_node p, q, r;      /* for list manipulation */
22810    mp_sym l_delim, r_delim;      /* hash addresses of a delimiter pair */
22811    l_delim = cur_sym();
22812    r_delim = equiv_sym (cur_sym());
22813    mp_get_x_next (mp);
22814    mp_scan_expression (mp);
22815    if ((cur_cmd() == mp_comma) && (mp->cur_exp.type >= mp_known)) {
22816      /* Scan the rest of a delimited set of numerics */
22817      /* This code uses the fact that |red_part| and |green_part|
22818         are synonymous with |x_part| and |y_part|. */
22819      p = mp_stash_cur_exp (mp);
22820      mp_get_x_next (mp);
22821      mp_scan_expression (mp);
22822      /* Make sure the second part of a pair or color has a numeric type */
22823      if (mp->cur_exp.type < mp_known) {
22824        const char *hlp[] = {
22825               "I've started to scan a pair `(a,b)' or a color `(a,b,c)';",
22826               "but after finding a nice `a' I found a `b' that isn't",
22827               "of numeric type. So I've changed that part to zero.",
22828               "(The b that I didn't like appears above the error message.)",
22829               NULL };
22830        mp_value new_expr;
22831        memset(&new_expr,0,sizeof(mp_value));
22832        mp_disp_err(mp, NULL);
22833        new_number(new_expr.data.n);
22834        set_number_to_zero(new_expr.data.n);
22835        mp_back_error (mp,"Nonnumeric ypart has been replaced by 0", hlp, true);
22836        mp_get_x_next (mp);
22837        mp_flush_cur_exp (mp, new_expr);
22838      }
22839
22840      q = mp_get_value_node (mp);
22841      mp_name_type (q) = mp_capsule;
22842      if (cur_cmd() == mp_comma) {
22843        mp_init_color_node (mp, q);
22844        r = value_node (q);
22845        mp_stash_in (mp, y_part (r));
22846        mp_unstash_cur_exp (mp, p);
22847        mp_stash_in (mp, x_part (r));
22848        /* Scan the last of a triplet of numerics */
22849        mp_get_x_next (mp);
22850        mp_scan_expression (mp);
22851        if (mp->cur_exp.type < mp_known) {
22852          mp_value new_expr;
22853          const char *hlp[] = {
22854              "I've just scanned a color `(a,b,c)' or cmykcolor(a,b,c,d); but the `c'",
22855              "isn't of numeric type. So I've changed that part to zero.",
22856              "(The c that I didn't like appears above the error message.)",
22857              NULL };
22858          memset(&new_expr,0,sizeof(mp_value));
22859          mp_disp_err(mp, NULL);
22860          new_number(new_expr.data.n);
22861          set_number_to_zero(new_expr.data.n);
22862          mp_back_error (mp,"Nonnumeric third part has been replaced by 0", hlp, true);
22863          mp_get_x_next (mp);
22864          mp_flush_cur_exp (mp, new_expr);
22865        }
22866        mp_stash_in (mp, blue_part (r));
22867
22868        if (cur_cmd() == mp_comma) {
22869          mp_node t;      /* a token */
22870          mp_init_cmykcolor_node (mp, q);
22871          t = value_node (q);
22872          mp_type (cyan_part (t)) = mp_type (red_part (r));
22873          set_value_number (cyan_part (t), value_number (red_part (r)));
22874          mp_type (magenta_part (t)) = mp_type (green_part (r));
22875          set_value_number (magenta_part (t), value_number (green_part (r)));
22876          mp_type (yellow_part (t)) = mp_type (blue_part (r));
22877          set_value_number (yellow_part (t), value_number (blue_part (r)));
22878          mp_recycle_value (mp, r);
22879          r = t;
22880          /* Scan the last of a quartet of numerics */
22881          mp_get_x_next (mp);
22882          mp_scan_expression (mp);
22883          if (mp->cur_exp.type < mp_known) {
22884            const char *hlp[] = {
22885                   "I've just scanned a cmykcolor `(c,m,y,k)'; but the `k' isn't",
22886                   "of numeric type. So I've changed that part to zero.",
22887                   "(The k that I didn't like appears above the error message.)",
22888                   NULL };
22889            mp_value new_expr;
22890            memset(&new_expr,0,sizeof(mp_value));
22891            new_number(new_expr.data.n);
22892            mp_disp_err(mp, NULL);
22893            set_number_to_zero(new_expr.data.n);
22894            mp_back_error (mp,"Nonnumeric blackpart has been replaced by 0", hlp, true);
22895            mp_get_x_next (mp);
22896            mp_flush_cur_exp (mp, new_expr);
22897          }
22898          mp_stash_in (mp, black_part (r));
22899
22900        }
22901      } else {
22902        mp_init_pair_node (mp, q);
22903        r = value_node (q);
22904        mp_stash_in (mp, y_part (r));
22905        mp_unstash_cur_exp (mp, p);
22906        mp_stash_in (mp, x_part (r));
22907      }
22908      mp_check_delimiter (mp, l_delim, r_delim);
22909      mp->cur_exp.type = mp_type (q);
22910      set_cur_exp_node (q);
22911
22912    } else {
22913      mp_check_delimiter (mp, l_delim, r_delim);
22914    }
22915  }
22916    break;
22917  case mp_begin_group:
22918    /* Scan a grouped primary */
22919    /* The local variable |group_line| keeps track of the line
22920       where a \&{begingroup} command occurred; this will be useful
22921       in an error message if the group doesn't actually end. */
22922    {
22923      integer group_line;     /* where a group began */
22924      group_line = mp_true_line (mp);
22925      if (number_positive (internal_value (mp_tracing_commands)))
22926        show_cur_cmd_mod;
22927      mp_save_boundary (mp);
22928      do {
22929        mp_do_statement (mp);       /* ends with |cur_cmd>=semicolon| */
22930      } while (cur_cmd() == mp_semicolon);
22931      if (cur_cmd() != mp_end_group) {
22932        char msg[256];
22933        const char *hlp[] = {
22934               "I saw a `begingroup' back there that hasn't been matched",
22935               "by `endgroup'. So I've inserted `endgroup' now.",
22936               NULL };
22937        mp_snprintf(msg, 256, "A group begun on line %d never ended", (int)group_line);
22938        mp_back_error (mp, msg, hlp, true);
22939        set_cur_cmd((mp_variable_type)mp_end_group);
22940      }
22941      mp_unsave (mp);
22942      /* this might change |cur_type|, if independent variables are recycled */
22943      if (number_positive (internal_value (mp_tracing_commands)))
22944        show_cur_cmd_mod;
22945    }
22946    break;
22947  case mp_string_token:
22948    /* Scan a string constant */
22949    mp->cur_exp.type = mp_string_type;
22950    set_cur_exp_str (cur_mod_str());
22951    break;
22952  case mp_numeric_token:
22953  {
22954    /* Scan a primary that starts with a numeric token */
22955    /* A numeric token might be a primary by itself, or it might be the
22956       numerator of a fraction composed solely of numeric tokens, or it might
22957       multiply the primary that follows (provided that the primary doesn't begin
22958       with a plus sign or a minus sign). The code here uses the facts that
22959       |max_primary_command=plus_or_minus| and
22960       |max_primary_command-1=numeric_token|. If a fraction is found that is less
22961       than unity, we try to retain higher precision when we use it in scalar
22962       multiplication. */
22963    mp_number num, denom;      /* for primaries that are fractions, like `1/2' */
22964    new_number (num);
22965    new_number (denom);
22966    set_cur_exp_value_number (cur_mod_number());
22967    mp->cur_exp.type = mp_known;
22968    mp_get_x_next (mp);
22969    if (cur_cmd() != mp_slash) {
22970      set_number_to_zero(num);
22971      set_number_to_zero(denom);
22972    } else {
22973      mp_get_x_next (mp);
22974      if (cur_cmd() != mp_numeric_token) {
22975        mp_back_input (mp);
22976        set_cur_cmd((mp_variable_type)mp_slash);
22977        set_cur_mod(mp_over);
22978        set_cur_sym(mp->frozen_slash);
22979        free_number (num);
22980        free_number (denom);
22981        goto DONE;
22982      }
22983      number_clone (num, cur_exp_value_number ());
22984      number_clone (denom, cur_mod_number());
22985      if (number_zero(denom)) {
22986        /* Protest division by zero */
22987        const char *hlp[] = { "I'll pretend that you meant to divide by 1.", NULL };
22988        mp_error (mp, "Division by zero", hlp, true);
22989      } else {
22990        mp_number ret;
22991        new_number (ret);
22992        make_scaled (ret, num, denom);
22993        set_cur_exp_value_number (ret);
22994        free_number (ret);
22995      }
22996      check_arith();
22997      mp_get_x_next (mp);
22998    }
22999    if (cur_cmd() >= mp_min_primary_command) {
23000      if (cur_cmd() < mp_numeric_token) {  /* in particular, |cur_cmd<>plus_or_minus| */
23001        mp_node p;      /* for list manipulation */
23002        mp_number absnum, absdenom;
23003        new_number (absnum);
23004        new_number (absdenom);
23005        p = mp_stash_cur_exp (mp);
23006        mp_scan_primary (mp);
23007        number_clone (absnum, num);
23008        number_abs (absnum);
23009        number_clone (absdenom, denom);
23010        number_abs (absdenom);
23011        if (number_greaterequal(absnum, absdenom) || (mp->cur_exp.type < mp_color_type)) {
23012          mp_do_binary (mp, p, mp_times);
23013        } else {
23014          mp_frac_mult (mp, num, denom);
23015          mp_free_value_node (mp, p);
23016        }
23017        free_number (absnum);
23018        free_number (absdenom);
23019      }
23020    }
23021    free_number (num);
23022    free_number (denom);
23023    goto DONE;
23024  }
23025    break;
23026  case mp_nullary:
23027    /* Scan a nullary operation */
23028    mp_do_nullary (mp, (quarterword) cur_mod());
23029    break;
23030  case mp_unary:
23031  case mp_type_name:
23032  case mp_cycle:
23033  case mp_plus_or_minus:
23034  {
23035    /* Scan a unary operation */
23036    quarterword c;        /* a primitive operation code */
23037    c = (quarterword) cur_mod();
23038    mp_get_x_next (mp);
23039    mp_scan_primary (mp);
23040    mp_do_unary (mp, c);
23041    goto DONE;
23042  }
23043    break;
23044  case mp_primary_binary:
23045  {
23046    /* Scan a binary operation with `\&{of}' between its operands */
23047    mp_node p;      /* for list manipulation */
23048    quarterword c;        /* a primitive operation code */
23049    c = (quarterword) cur_mod();
23050    mp_get_x_next (mp);
23051    mp_scan_expression (mp);
23052    if (cur_cmd() != mp_of_token) {
23053      char msg[256];
23054      mp_string sname;
23055      const char *hlp[] = {
23056          "I've got the first argument; will look now for the other.",
23057          NULL };
23058      int old_setting = mp->selector;
23059      mp->selector = new_string;
23060      mp_print_cmd_mod (mp, mp_primary_binary, c);
23061      mp->selector = old_setting;
23062      sname = mp_make_string(mp);
23063      mp_snprintf (msg, 256, "Missing `of' has been inserted for %s", mp_str(mp, sname));
23064      delete_str_ref(sname);
23065      mp_back_error (mp, msg, hlp, true);
23066    }
23067    p = mp_stash_cur_exp (mp);
23068    mp_get_x_next (mp);
23069    mp_scan_primary (mp);
23070    mp_do_binary (mp, p, c);
23071    goto DONE;
23072  }
23073    break;
23074  case mp_str_op:
23075    /* Convert a suffix to a string */
23076    mp_get_x_next (mp);
23077    mp_scan_suffix (mp);
23078    mp->old_setting = mp->selector;
23079    mp->selector = new_string;
23080    mp_show_token_list (mp, cur_exp_node (), NULL, 100000, 0);
23081    mp_flush_token_list (mp, cur_exp_node ());
23082    set_cur_exp_str (mp_make_string (mp));
23083    mp->selector = mp->old_setting;
23084    mp->cur_exp.type = mp_string_type;
23085    goto DONE;
23086    break;
23087  case mp_internal_quantity:
23088    /* Scan an internal numeric quantity */
23089    /* If an internal quantity appears all by itself on the left of an
23090       assignment, we return a token list of length one, containing the address
23091       of the internal quantity, with |name_type| equal to |mp_internal_sym|.
23092       (This accords with the conventions of the save stack, as described earlier.) */
23093    {
23094      halfword qq = cur_mod();
23095      if (my_var_flag == mp_assignment) {
23096        mp_get_x_next (mp);
23097        if (cur_cmd() == mp_assignment) {
23098          set_cur_exp_node (mp_get_symbolic_node (mp));
23099          set_mp_sym_info (cur_exp_node (), qq);
23100          mp_name_type (cur_exp_node ()) = mp_internal_sym;
23101          mp->cur_exp.type = mp_token_list;
23102          goto DONE;
23103        }
23104        mp_back_input (mp);
23105      }
23106      if (internal_type (qq) == mp_string_type) {
23107        set_cur_exp_str (internal_string (qq));
23108      } else {
23109        set_cur_exp_value_number (internal_value (qq));
23110      }
23111      mp->cur_exp.type = internal_type (qq);
23112    }
23113    break;
23114  case mp_capsule_token:
23115    mp_make_exp_copy (mp, cur_mod_node());
23116    break;
23117  case mp_tag_token:
23118    @<Scan a variable primary; |goto restart| if it turns out to be a macro@>;
23119    break;
23120  default:
23121    mp_bad_exp (mp, "A primary");
23122    goto RESTART;
23123    break;
23124  }
23125  mp_get_x_next (mp);           /* the routines |goto done| if they don't want this */
23126DONE:
23127  check_for_mediation (mp);
23128}
23129
23130@  Expressions of the form `\.{a[b,c]}' are converted into
23131`\.{b+a*(c-b)}', without checking the types of \.b~or~\.c,
23132provided that \.a is numeric.
23133
23134@<Declare the basic parsing subroutines@>=
23135static void check_for_mediation (MP mp) {
23136  mp_node p, q, r;      /* for list manipulation */
23137  if (cur_cmd() == mp_left_bracket) {
23138    if (mp->cur_exp.type >= mp_known) {
23139      /* Scan a mediation construction */
23140      p = mp_stash_cur_exp (mp);
23141      mp_get_x_next (mp);
23142      mp_scan_expression (mp);
23143      if (cur_cmd() != mp_comma) {
23144        /* Put the left bracket and the expression back to be rescanned */
23145        /* The left bracket that we thought was introducing a subscript might have
23146           actually been the left bracket in a mediation construction like `\.{x[a,b]}'.
23147           So we don't issue an error message at this point; but we do want to back up
23148           so as to avoid any embarrassment about our incorrect assumption. */
23149        mp_back_input (mp);           /* that was the token following the current expression */
23150        mp_back_expr (mp);
23151        set_cur_cmd((mp_variable_type)mp_left_bracket);
23152        set_cur_mod_number(zero_t);
23153        set_cur_sym(mp->frozen_left_bracket);
23154        mp_unstash_cur_exp (mp, p);
23155      } else {
23156        q = mp_stash_cur_exp (mp);
23157        mp_get_x_next (mp);
23158        mp_scan_expression (mp);
23159        if (cur_cmd() != mp_right_bracket) {
23160          const char *hlp[] = {
23161                 "I've scanned an expression of the form `a[b,c',",
23162                 "so a right bracket should have come next.",
23163                 "I shall pretend that one was there.",
23164                 NULL };
23165          mp_back_error (mp, "Missing `]' has been inserted", hlp, true);
23166        }
23167        r = mp_stash_cur_exp (mp);
23168        mp_make_exp_copy (mp, q);
23169        mp_do_binary (mp, r, mp_minus);
23170        mp_do_binary (mp, p, mp_times);
23171        mp_do_binary (mp, q, mp_plus);
23172        mp_get_x_next (mp);
23173      }
23174    }
23175  }
23176}
23177
23178
23179@ Errors at the beginning of expressions are flagged by |bad_exp|.
23180
23181@c
23182static void mp_bad_exp (MP mp, const char *s) {
23183  char msg[256];
23184  int save_flag;
23185  const char *hlp[] = {
23186         "I'm afraid I need some sort of value in order to continue,",
23187         "so I've tentatively inserted `0'. You may want to",
23188         "delete this zero and insert something else;",
23189         "see Chapter 27 of The METAFONTbook for an example.",
23190         NULL };
23191@:METAFONTbook}{\sl The {\logos METAFONT\/}book@>;
23192  {
23193     mp_string cm;
23194     int old_selector = mp->selector;
23195     mp->selector = new_string;
23196     mp_print_cmd_mod (mp, cur_cmd(), cur_mod());
23197     mp->selector = old_selector;
23198     cm = mp_make_string(mp);
23199     mp_snprintf(msg, 256, "%s expression can't begin with `%s'", s, mp_str(mp, cm));
23200     delete_str_ref(cm);
23201  }
23202  mp_back_input (mp);
23203  set_cur_sym(NULL);
23204  set_cur_cmd((mp_variable_type)mp_numeric_token);
23205  set_cur_mod_number (zero_t);
23206  mp_ins_error (mp, msg, hlp, true);
23207  save_flag = mp->var_flag;
23208  mp->var_flag = 0;
23209  mp_get_x_next (mp);
23210  mp->var_flag = save_flag;
23211}
23212
23213
23214@ The |stash_in| subroutine puts the current (numeric) expression into a field
23215within a ``big node.''
23216
23217@c
23218static void mp_stash_in (MP mp, mp_node p) {
23219  mp_value_node q;      /* temporary register */
23220  mp_type (p) = mp->cur_exp.type;
23221  if (mp->cur_exp.type == mp_known) {
23222    set_value_number (p, cur_exp_value_number ());
23223  } else {
23224    if (mp->cur_exp.type == mp_independent) {
23225      /* Stash an independent |cur_exp| into a big node */
23226      /* In rare cases the current expression can become |independent|. There
23227         may be many dependency lists pointing to such an independent capsule,
23228	 so we can't simply move it into place within a big node. Instead,
23229	 we copy it, then recycle it. */
23230      q = mp_single_dependency (mp, cur_exp_node ());
23231      if (q == mp->dep_final) {
23232        mp_type (p) = mp_known;
23233        set_value_number (p, zero_t);
23234        mp_free_dep_node (mp, q);
23235      } else {
23236        mp_new_dep (mp, p, mp_dependent, q);
23237      }
23238      mp_recycle_value (mp, cur_exp_node ());
23239      mp_free_value_node (mp, cur_exp_node ());
23240    } else {
23241      set_dep_list ((mp_value_node) p,
23242                    dep_list ((mp_value_node) cur_exp_node ()));
23243      set_prev_dep ((mp_value_node) p,
23244                    prev_dep ((mp_value_node) cur_exp_node ()));
23245      set_mp_link (prev_dep ((mp_value_node) p), p);
23246      mp_free_dep_node (mp, (mp_value_node) cur_exp_node ());
23247    }
23248  }
23249  mp->cur_exp.type = mp_vacuous;
23250}
23251
23252@ The most difficult part of |scan_primary| has been saved for last, since
23253it was necessary to build up some confidence first. We can now face the task
23254of scanning a variable.
23255
23256As we scan a variable, we build a token list containing the relevant
23257names and subscript values, simultaneously following along in the
23258``collective'' structure to see if we are actually dealing with a macro
23259instead of a value.
23260
23261The local variables |pre_head| and |post_head| will point to the beginning
23262of the prefix and suffix lists; |tail| will point to the end of the list
23263that is currently growing.
23264
23265Another local variable, |tt|, contains partial information about the
23266declared type of the variable-so-far. If |tt>=mp_unsuffixed_macro|, the
23267relation |tt=mp_type(q)| will always hold. If |tt=undefined|, the routine
23268doesn't bother to update its information about type. And if
23269|undefined<tt<mp_unsuffixed_macro|, the precise value of |tt| isn't critical.
23270
23271@ @<Scan a variable primary...@>=
23272{
23273  mp_node p, q;      /* for list manipulation */
23274  mp_node t;      /* a token */
23275  mp_node pre_head, post_head, tail; /* prefix and suffix list variables */
23276  quarterword tt; /* approximation to the type of the variable-so-far */
23277  mp_node macro_ref = 0;  /* reference count for a suffixed macro */
23278  pre_head = mp_get_symbolic_node (mp);
23279  tail = pre_head;
23280  post_head = NULL;
23281  tt = mp_vacuous;
23282  while (1) {
23283    t = mp_cur_tok (mp);
23284    mp_link (tail) = t;
23285    if (tt != mp_undefined) {
23286      /* Find the approximate type |tt| and corresponding~|q| */
23287      /* Every time we call |get_x_next|, there's a chance that the variable we've
23288         been looking at will disappear. Thus, we cannot safely keep |q| pointing
23289         into the variable structure; we need to start searching from the root each
23290         time. */
23291      mp_sym qq;
23292      p = mp_link (pre_head);
23293      qq = mp_sym_sym (p);
23294      tt = mp_undefined;
23295      if (eq_type (qq) % mp_outer_tag == mp_tag_token) {
23296        q = equiv_node (qq);
23297        if (q == NULL)
23298          goto DONE2;
23299        while (1) {
23300          p = mp_link (p);
23301          if (p == NULL) {
23302            tt = mp_type (q);
23303            goto DONE2;
23304          }
23305          if (mp_type (q) != mp_structured)
23306            goto DONE2;
23307          q = mp_link (attr_head (q));      /* the |collective_subscript| attribute */
23308          if (mp_type (p) == mp_symbol_node) {      /* it's not a subscript */
23309            do {
23310              q = mp_link (q);
23311            } while (!(hashloc (q) >= mp_sym_sym (p)));
23312            if (hashloc (q) > mp_sym_sym (p))
23313              goto DONE2;
23314          }
23315        }
23316      }
23317    DONE2:
23318
23319      if (tt >= mp_unsuffixed_macro) {
23320        /* Either begin an unsuffixed macro call or
23321          prepare for a suffixed one */
23322        mp_link (tail) = NULL;
23323        if (tt > mp_unsuffixed_macro) {       /* |tt=mp_suffixed_macro| */
23324          post_head = mp_get_symbolic_node (mp);
23325          tail = post_head;
23326          mp_link (tail) = t;
23327          tt = mp_undefined;
23328          macro_ref = value_node (q);
23329          add_mac_ref (macro_ref);
23330        } else {
23331          /* Set up unsuffixed macro call and |goto restart| */
23332          /* The only complication associated with macro calling is that the prefix
23333             and ``at'' parameters must be packaged in an appropriate list of lists. */
23334          p = mp_get_symbolic_node (mp);
23335          set_mp_sym_sym (pre_head, mp_link (pre_head));
23336          mp_link (pre_head) = p;
23337          set_mp_sym_sym (p, t);
23338          mp_macro_call (mp, value_node (q), pre_head, NULL);
23339          mp_get_x_next (mp);
23340          goto RESTART;
23341        }
23342      }
23343    }
23344    mp_get_x_next (mp);
23345    tail = t;
23346    if (cur_cmd() == mp_left_bracket) {
23347      /* Scan for a subscript; replace |cur_cmd| by |numeric_token| if found */
23348      mp_get_x_next (mp);
23349      mp_scan_expression (mp);
23350      if (cur_cmd() != mp_right_bracket) {
23351        /* Put the left bracket and the expression back to be rescanned */
23352        /* The left bracket that we thought was introducing a subscript might have
23353           actually been the left bracket in a mediation construction like `\.{x[a,b]}'.
23354           So we don't issue an error message at this point; but we do want to back up
23355           so as to avoid any embarrassment about our incorrect assumption. */
23356        mp_back_input (mp);           /* that was the token following the current expression */
23357        mp_back_expr (mp);
23358        set_cur_cmd((mp_variable_type)mp_left_bracket);
23359        set_cur_mod_number(zero_t);
23360        set_cur_sym(mp->frozen_left_bracket);
23361
23362      } else {
23363        if (mp->cur_exp.type != mp_known)
23364          mp_bad_subscript (mp);
23365        set_cur_cmd((mp_variable_type)mp_numeric_token);
23366        set_cur_mod_number(cur_exp_value_number ());
23367        set_cur_sym(NULL);
23368      }
23369    }
23370    if (cur_cmd() > mp_max_suffix_token)
23371      break;
23372    if (cur_cmd() < mp_min_suffix_token)
23373      break;
23374  } /* now |cur_cmd| is |internal_quantity|, |tag_token|, or |numeric_token| */
23375   /* Handle unusual cases that masquerade as variables, and |goto restart| or
23376      |goto done| if appropriate; otherwise make a copy of the variable and |goto done| */
23377   /* If the variable does exist, we also need to check
23378      for a few other special cases before deciding that a plain old ordinary
23379      variable has, indeed, been scanned. */
23380  if (post_head != NULL) {
23381    /* Set up suffixed macro call and |goto restart| */
23382    /* If the ``variable'' that turned out to be a suffixed macro no longer exists,
23383       we don't care, because we have reserved a pointer (|macro_ref|) to its
23384       token list. */
23385    mp_back_input (mp);
23386    p = mp_get_symbolic_node (mp);
23387    q = mp_link (post_head);
23388    set_mp_sym_sym (pre_head, mp_link (pre_head));
23389    mp_link (pre_head) = post_head;
23390    set_mp_sym_sym (post_head, q);
23391    mp_link (post_head) = p;
23392    set_mp_sym_sym (p, mp_link (q));
23393    mp_link (q) = NULL;
23394    mp_macro_call (mp, macro_ref, pre_head, NULL);
23395    decr_mac_ref (macro_ref);
23396    mp_get_x_next (mp);
23397    goto RESTART;
23398  }
23399  q = mp_link (pre_head);
23400  mp_free_symbolic_node (mp, pre_head);
23401  if (cur_cmd() == my_var_flag) {
23402    mp->cur_exp.type = mp_token_list;
23403    set_cur_exp_node (q);
23404    goto DONE;
23405  }
23406  p = mp_find_variable (mp, q);
23407  if (p != NULL) {
23408    mp_make_exp_copy (mp, p);
23409  } else {
23410    mp_value new_expr;
23411    const char *hlp[] = {
23412      "While I was evaluating the suffix of this variable,",
23413      "something was redefined, and it's no longer a variable!",
23414      "In order to get back on my feet, I've inserted `0' instead.",
23415      NULL };
23416    char *msg = mp_obliterated (mp, q);
23417    memset(&new_expr,0,sizeof(mp_value));
23418    new_number(new_expr.data.n);
23419    set_number_to_zero(new_expr.data.n);
23420    mp_back_error (mp, msg, hlp, true);
23421    free(msg);
23422    mp_get_x_next (mp);
23423    mp_flush_cur_exp (mp, new_expr);
23424  }
23425  mp_flush_node_list (mp, q);
23426  goto DONE;
23427}
23428
23429
23430@ Here's a routine that puts the current expression back to be read again.
23431
23432@c
23433static void mp_back_expr (MP mp) {
23434  mp_node p;    /* capsule token */
23435  p = mp_stash_cur_exp (mp);
23436  mp_link (p) = NULL;
23437  back_list (p);
23438}
23439
23440
23441@ Unknown subscripts lead to the following error message.
23442
23443@c
23444static void mp_bad_subscript (MP mp) {
23445  mp_value new_expr;
23446  const char *hlp[] = {
23447         "A bracketed subscript must have a known numeric value;",
23448         "unfortunately, what I found was the value that appears just",
23449         "above this error message. So I'll try a zero subscript.",
23450         NULL };
23451  memset(&new_expr,0,sizeof(mp_value));
23452  new_number(new_expr.data.n);
23453  mp_disp_err(mp, NULL);
23454  mp_error (mp, "Improper subscript has been replaced by zero", hlp, true);
23455@.Improper subscript...@>;
23456  mp_flush_cur_exp (mp, new_expr);
23457}
23458
23459
23460@ How do things stand now? Well, we have scanned an entire variable name,
23461including possible subscripts and/or attributes; |cur_cmd|, |cur_mod|, and
23462|cur_sym| represent the token that follows. If |post_head=NULL|, a
23463token list for this variable name starts at |mp_link(pre_head)|, with all
23464subscripts evaluated. But if |post_head<>NULL|, the variable turned out
23465to be a suffixed macro; |pre_head| is the head of the prefix list, while
23466|post_head| is the head of a token list containing both `\.{\AT!}' and
23467the suffix.
23468
23469Our immediate problem is to see if this variable still exists. (Variable
23470structures can change drastically whenever we call |get_x_next|; users
23471aren't supposed to do this, but the fact that it is possible means that
23472we must be cautious.)
23473
23474The following procedure creates an error message for when a variable
23475unexpectedly disappears.
23476
23477@c
23478static char *mp_obliterated (MP mp, mp_node q) {
23479  char msg[256];
23480  mp_string sname;
23481  int old_setting = mp->selector;
23482  mp->selector = new_string;
23483  mp_show_token_list (mp, q, NULL, 1000, 0);
23484  sname = mp_make_string(mp);
23485  mp->selector = old_setting;
23486  mp_snprintf(msg, 256, "Variable %s has been obliterated", mp_str(mp, sname));
23487@.Variable...obliterated@>;
23488  delete_str_ref(sname);
23489  return xstrdup(msg);
23490}
23491
23492
23493@ Our remaining job is simply to make a copy of the value that has been
23494found. Some cases are harder than others, but complexity arises solely
23495because of the multiplicity of possible cases.
23496
23497@<Declare the procedure called |make_exp_copy|@>=
23498@<Declare subroutines needed by |make_exp_copy|@>;
23499static void mp_make_exp_copy (MP mp, mp_node p) {
23500  mp_node t;    /* register(s) for list manipulation */
23501  mp_value_node q;
23502RESTART:
23503  mp->cur_exp.type = mp_type (p);
23504  switch (mp->cur_exp.type) {
23505  case mp_vacuous:
23506  case mp_boolean_type:
23507  case mp_known:
23508    set_cur_exp_value_number (value_number (p));
23509    break;
23510  case unknown_types:
23511    t = mp_new_ring_entry (mp, p);
23512    set_cur_exp_node (t);
23513    break;
23514  case mp_string_type:
23515    set_cur_exp_str (value_str (p));
23516    break;
23517  case mp_picture_type:
23518    set_cur_exp_node (value_node (p));
23519    add_edge_ref (cur_exp_node ());
23520    break;
23521  case mp_pen_type:
23522    set_cur_exp_knot (copy_pen (value_knot (p)));
23523    break;
23524  case mp_path_type:
23525    set_cur_exp_knot (mp_copy_path (mp, value_knot (p)));
23526    break;
23527  case mp_transform_type:
23528  case mp_color_type:
23529  case mp_cmykcolor_type:
23530  case mp_pair_type:
23531    /* Copy the big node |p| */
23532    /* The most tedious case arises when the user refers to a
23533       \&{pair}, \&{color}, or \&{transform} variable; we must copy several fields,
23534       each of which can be |independent|, |dependent|, |mp_proto_dependent|,
23535       or |known|. */
23536    if (value_node (p) == NULL) {
23537      switch (mp_type (p)) {
23538      case mp_pair_type:
23539        mp_init_pair_node (mp, p);
23540        break;
23541      case mp_color_type:
23542        mp_init_color_node (mp, p);
23543        break;
23544      case mp_cmykcolor_type:
23545        mp_init_cmykcolor_node (mp, p);
23546        break;
23547      case mp_transform_type:
23548        mp_init_transform_node (mp, p);
23549        break;
23550      default:                   /* there are no other valid cases, but please the compiler */
23551        break;
23552      }
23553    }
23554    t = mp_get_value_node (mp);
23555    mp_name_type (t) = mp_capsule;
23556    q = (mp_value_node)value_node (p);
23557    switch (mp->cur_exp.type) {
23558    case mp_pair_type:
23559      mp_init_pair_node (mp, t);
23560      mp_install (mp, y_part (value_node (t)), y_part (q));
23561      mp_install (mp, x_part (value_node (t)), x_part (q));
23562      break;
23563    case mp_color_type:
23564      mp_init_color_node (mp, t);
23565      mp_install (mp, blue_part (value_node (t)),  blue_part (q));
23566      mp_install (mp, green_part (value_node (t)), green_part (q));
23567      mp_install (mp, red_part (value_node (t)),   red_part (q));
23568      break;
23569    case mp_cmykcolor_type:
23570      mp_init_cmykcolor_node (mp, t);
23571      mp_install (mp, black_part (value_node (t)),   black_part (q));
23572      mp_install (mp, yellow_part (value_node (t)),  yellow_part (q));
23573      mp_install (mp, magenta_part (value_node (t)), magenta_part (q));
23574      mp_install (mp, cyan_part (value_node (t)),    cyan_part (q));
23575      break;
23576    case mp_transform_type:
23577      mp_init_transform_node (mp, t);
23578      mp_install (mp, yy_part (value_node (t)), yy_part (q));
23579      mp_install (mp, yx_part (value_node (t)), yx_part (q));
23580      mp_install (mp, xy_part (value_node (t)), xy_part (q));
23581      mp_install (mp, xx_part (value_node (t)), xx_part (q));
23582      mp_install (mp, ty_part (value_node (t)), ty_part (q));
23583      mp_install (mp, tx_part (value_node (t)), tx_part (q));
23584      break;
23585    default:  /* there are no other valid cases, but please the compiler */
23586      break;
23587    }
23588    set_cur_exp_node (t);
23589    break;
23590  case mp_dependent:
23591  case mp_proto_dependent:
23592    mp_encapsulate (mp,
23593                    mp_copy_dep_list (mp,
23594                                      (mp_value_node) dep_list ((mp_value_node)
23595                                                                p)));
23596    break;
23597  case mp_numeric_type:
23598    mp_new_indep (mp, p);
23599    goto RESTART;
23600    break;
23601  case mp_independent:
23602    q = mp_single_dependency (mp, p);
23603    if (q == mp->dep_final) {
23604      mp->cur_exp.type = mp_known;
23605      set_cur_exp_value_number (zero_t);
23606      mp_free_dep_node (mp, q);
23607    } else {
23608      mp->cur_exp.type = mp_dependent;
23609      mp_encapsulate (mp, q);
23610    }
23611    break;
23612  default:
23613    mp_confusion (mp, "copy");
23614@:this can't happen copy}{\quad copy@>;
23615    break;
23616  }
23617}
23618
23619
23620@ The |encapsulate| subroutine assumes that |dep_final| is the
23621tail of dependency list~|p|.
23622
23623@<Declare subroutines needed by |make_exp_copy|@>=
23624static void mp_encapsulate (MP mp, mp_value_node p) {
23625  mp_node q = mp_get_value_node (mp);
23626  FUNCTION_TRACE2 ("mp_encapsulate(%p)\n", p);
23627  mp_name_type (q) = mp_capsule;
23628  mp_new_dep (mp, q, mp->cur_exp.type, p);
23629  set_cur_exp_node (q);
23630}
23631
23632@ The |install| procedure copies a numeric field~|q| into field~|r| of
23633a big node that will be part of a capsule.
23634
23635@<Declare subroutines needed by |make_exp_copy|@>=
23636static void mp_install (MP mp, mp_node r, mp_node q) {
23637  mp_value_node p;      /* temporary register */
23638  if (mp_type (q) == mp_known) {
23639    mp_type (r) = mp_known;
23640    set_value_number (r, value_number (q));
23641  } else if (mp_type (q) == mp_independent) {
23642    p = mp_single_dependency (mp, q);
23643    if (p == mp->dep_final) {
23644      mp_type (r) = mp_known;
23645      set_value_number (r, zero_t);
23646      mp_free_dep_node (mp, p);
23647    } else {
23648      mp_new_dep (mp, r, mp_dependent, p);
23649    }
23650  } else {
23651    mp_new_dep (mp, r, mp_type (q),
23652                mp_copy_dep_list (mp, (mp_value_node) dep_list ((mp_value_node)
23653                                                                q)));
23654  }
23655}
23656
23657
23658@ Here is a comparatively simple routine that is used to scan the
23659\&{suffix} parameters of a macro.
23660
23661@<Declare the basic parsing subroutines@>=
23662static void mp_scan_suffix (MP mp) {
23663  mp_node h, t; /* head and tail of the list being built */
23664  mp_node p;    /* temporary register */
23665  h = mp_get_symbolic_node (mp);
23666  t = h;
23667  while (1) {
23668    if (cur_cmd() == mp_left_bracket) {
23669      /* Scan a bracketed subscript and set |cur_cmd:=numeric_token| */
23670      mp_get_x_next (mp);
23671      mp_scan_expression (mp);
23672      if (mp->cur_exp.type != mp_known)
23673        mp_bad_subscript (mp);
23674      if (cur_cmd() != mp_right_bracket) {
23675        const char *hlp[] = {
23676               "I've seen a `[' and a subscript value, in a suffix,",
23677               "so a right bracket should have come next.",
23678               "I shall pretend that one was there.",
23679               NULL };
23680        mp_back_error (mp, "Missing `]' has been inserted", hlp, true);
23681      }
23682      set_cur_cmd((mp_variable_type)mp_numeric_token);
23683      set_cur_mod_number(cur_exp_value_number ());
23684
23685    }
23686    if (cur_cmd() == mp_numeric_token) {
23687      mp_number arg1;
23688      new_number (arg1);
23689      number_clone (arg1, cur_mod_number());
23690      p = mp_new_num_tok (mp, arg1);
23691      free_number (arg1);
23692    } else if ((cur_cmd() == mp_tag_token) || (cur_cmd() == mp_internal_quantity)) {
23693      p = mp_get_symbolic_node (mp);
23694      set_mp_sym_sym (p, cur_sym());
23695      mp_name_type (p) = cur_sym_mod();
23696    } else {
23697      break;
23698    }
23699    mp_link (t) = p;
23700    t = p;
23701    mp_get_x_next (mp);
23702  }
23703  set_cur_exp_node (mp_link (h));
23704  mp_free_symbolic_node (mp, h);
23705  mp->cur_exp.type = mp_token_list;
23706}
23707
23708@* Parsing secondary and higher expressions.
23709
23710After the intricacies of |scan_primary|\kern-1pt,
23711the |scan_secondary| routine is
23712refreshingly simple. It's not trivial, but the operations are relatively
23713straightforward; the main difficulty is, again, that expressions and data
23714structures might change drastically every time we call |get_x_next|, so a
23715cautious approach is mandatory. For example, a macro defined by
23716\&{primarydef} might have disappeared by the time its second argument has
23717been scanned; we solve this by increasing the reference count of its token
23718list, so that the macro can be called even after it has been clobbered.
23719
23720@<Declare the basic parsing subroutines@>=
23721static void mp_scan_secondary (MP mp) {
23722  mp_node p;    /* for list manipulation */
23723  halfword c, d;        /* operation codes or modifiers */
23724  mp_node cc = NULL;
23725  mp_sym mac_name = NULL;      /* token defined with \&{primarydef} */
23726RESTART:
23727  if ((cur_cmd() < mp_min_primary_command) ||
23728      (cur_cmd() > mp_max_primary_command))
23729    mp_bad_exp (mp, "A secondary");
23730@.A secondary expression...@>;
23731  mp_scan_primary (mp);
23732CONTINUE:
23733  if (cur_cmd() <= mp_max_secondary_command &&
23734      cur_cmd() >= mp_min_secondary_command) {
23735    p = mp_stash_cur_exp (mp);
23736    d = cur_cmd();
23737    c = cur_mod();
23738    if (d == mp_secondary_primary_macro) {
23739      cc = cur_mod_node();
23740      mac_name = cur_sym();
23741      add_mac_ref (cc);
23742    }
23743    mp_get_x_next (mp);
23744    mp_scan_primary (mp);
23745    if (d != mp_secondary_primary_macro) {
23746      mp_do_binary (mp, p, c);
23747    } else {
23748      mp_back_input (mp);
23749      mp_binary_mac (mp, p, cc, mac_name);
23750      decr_mac_ref (cc);
23751      mp_get_x_next (mp);
23752      goto RESTART;
23753    }
23754    goto CONTINUE;
23755  }
23756}
23757
23758
23759@ The following procedure calls a macro that has two parameters,
23760|p| and |cur_exp|.
23761
23762@c
23763static void mp_binary_mac (MP mp, mp_node p, mp_node c, mp_sym n) {
23764  mp_node q, r; /* nodes in the parameter list */
23765  q = mp_get_symbolic_node (mp);
23766  r = mp_get_symbolic_node (mp);
23767  mp_link (q) = r;
23768  set_mp_sym_sym (q, p);
23769  set_mp_sym_sym (r, mp_stash_cur_exp (mp));
23770  mp_macro_call (mp, c, q, n);
23771}
23772
23773
23774@ The next procedure, |scan_tertiary|, is pretty much the same deal.
23775
23776@<Declare the basic parsing subroutines@>=
23777static void mp_scan_tertiary (MP mp) {
23778  mp_node p;    /* for list manipulation */
23779  halfword c, d;        /* operation codes or modifiers */
23780  mp_node cc = NULL;
23781  mp_sym mac_name = NULL;      /* token defined with \&{secondarydef} */
23782RESTART:
23783  if ((cur_cmd() < mp_min_primary_command) ||
23784      (cur_cmd() > mp_max_primary_command))
23785    mp_bad_exp (mp, "A tertiary");
23786@.A tertiary expression...@>;
23787  mp_scan_secondary (mp);
23788CONTINUE:
23789  if (cur_cmd() <= mp_max_tertiary_command) {
23790    if (cur_cmd() >= mp_min_tertiary_command) {
23791      p = mp_stash_cur_exp (mp);
23792      c = cur_mod();
23793      d = cur_cmd();
23794      if (d == mp_tertiary_secondary_macro) {
23795        cc = cur_mod_node();
23796        mac_name = cur_sym();
23797        add_mac_ref (cc);
23798      }
23799      mp_get_x_next (mp);
23800      mp_scan_secondary (mp);
23801      if (d != mp_tertiary_secondary_macro) {
23802        mp_do_binary (mp, p, c);
23803      } else {
23804        mp_back_input (mp);
23805        mp_binary_mac (mp, p, cc, mac_name);
23806        decr_mac_ref (cc);
23807        mp_get_x_next (mp);
23808        goto RESTART;
23809      }
23810      goto CONTINUE;
23811    }
23812  }
23813}
23814
23815
23816@ Finally we reach the deepest level in our quartet of parsing routines.
23817This one is much like the others; but it has an extra complication from
23818paths, which materialize here.
23819
23820@<Declare the basic parsing subroutines@>=
23821static int mp_scan_path (MP mp);
23822static void mp_scan_expression (MP mp) {
23823  int my_var_flag;      /* initial value of |var_flag| */
23824  my_var_flag = mp->var_flag;
23825  check_expansion_depth();
23826RESTART:
23827  if ((cur_cmd() < mp_min_primary_command) ||
23828      (cur_cmd() > mp_max_primary_command))
23829    mp_bad_exp (mp, "An");
23830@.An expression...@>;
23831  mp_scan_tertiary (mp);
23832CONTINUE:
23833  if (cur_cmd() <= mp_max_expression_command) {
23834    if (cur_cmd() >= mp_min_expression_command) {
23835      if ((cur_cmd() != mp_equals) || (my_var_flag != mp_assignment)) {
23836        mp_node p;    /* for list manipulation */
23837        mp_node cc = NULL;
23838        halfword c;
23839        halfword d;        /* operation codes or modifiers */
23840        mp_sym mac_name;      /* token defined with \&{tertiarydef} */
23841        mac_name = NULL;
23842        p = mp_stash_cur_exp (mp);
23843        d = cur_cmd();
23844        c = cur_mod();
23845        if (d == mp_expression_tertiary_macro) {
23846          cc = cur_mod_node();
23847          mac_name = cur_sym();
23848          add_mac_ref (cc);
23849        }
23850        if ((d < mp_ampersand) || ((d == mp_ampersand) &&
23851                                ((mp_type (p) == mp_pair_type)
23852                                 || (mp_type (p) == mp_path_type)))) {
23853          /* Scan a path construction operation;  but |return| if |p| has the wrong type */
23854
23855          mp_unstash_cur_exp (mp, p);
23856          if (!mp_scan_path(mp)) {
23857            mp->expand_depth_count--;
23858            return;
23859          }
23860        } else {
23861          mp_get_x_next (mp);
23862          mp_scan_tertiary (mp);
23863          if (d != mp_expression_tertiary_macro) {
23864            mp_do_binary (mp, p, c);
23865          } else {
23866            mp_back_input (mp);
23867            mp_binary_mac (mp, p, cc, mac_name);
23868            decr_mac_ref (cc);
23869            mp_get_x_next (mp);
23870            goto RESTART;
23871          }
23872        }
23873        goto CONTINUE;
23874      }
23875    }
23876  }
23877  mp->expand_depth_count--;
23878}
23879
23880
23881@ The reader should review the data structure conventions for paths before
23882hoping to understand the next part of this code.
23883
23884@d min_tension three_quarter_unit_t
23885
23886@<Declare the basic parsing subroutines@>=
23887static void force_valid_tension_setting(MP mp) {
23888  if ((mp->cur_exp.type != mp_known) || number_less(cur_exp_value_number (), min_tension)) {
23889    mp_value new_expr;
23890    const char *hlp[] = {
23891               "The expression above should have been a number >=3/4.",
23892                NULL };
23893    memset(&new_expr,0,sizeof(mp_value));
23894    new_number(new_expr.data.n);
23895    mp_disp_err(mp, NULL);
23896    number_clone(new_expr.data.n, unity_t);
23897    mp_back_error (mp, "Improper tension has been set to 1", hlp, true);
23898    mp_get_x_next (mp);
23899    mp_flush_cur_exp (mp, new_expr);
23900  }
23901}
23902static int mp_scan_path (MP mp) {
23903  mp_knot path_p, path_q, r;
23904  mp_knot pp, qq;
23905  halfword d;        /* operation code or modifier */
23906  boolean cycle_hit; /* did a path expression just end with `\&{cycle}'? */
23907  mp_number x, y;    /* explicit coordinates or tension at a path join */
23908  int t;             /* knot type following a path join */
23909  t = 0;
23910  cycle_hit = false;
23911  /* Convert the left operand, |p|, into a partial path ending at~|q|;
23912    but |return| if |p| doesn't have a suitable type */
23913  if (mp->cur_exp.type == mp_pair_type)
23914    path_p = mp_pair_to_knot (mp);
23915  else if (mp->cur_exp.type == mp_path_type)
23916    path_p = cur_exp_knot ();
23917  else
23918    return 0;
23919  path_q = path_p;
23920  while (mp_next_knot (path_q) != path_p)
23921    path_q = mp_next_knot (path_q);
23922  if (mp_left_type (path_p) != mp_endpoint) {   /* open up a cycle */
23923    r = mp_copy_knot (mp, path_p);
23924    mp_next_knot (path_q) = r;
23925    path_q = r;
23926  }
23927  mp_left_type (path_p) = mp_open;
23928  mp_right_type (path_q) = mp_open;
23929
23930  new_number (y);
23931  new_number (x);
23932
23933CONTINUE_PATH:
23934  /* Determine the path join parameters;
23935    but |goto finish_path| if there's only a direction specifier */
23936  /* At this point |cur_cmd| is either |ampersand|, |left_brace|, or |path_join|. */
23937
23938  if (cur_cmd() == mp_left_brace) {
23939    /* Put the pre-join direction information into node |q| */
23940    /* At this point |mp_right_type(q)| is usually |open|, but it may have been
23941       set to some other value by a previous operation. We must maintain
23942       the value of |mp_right_type(q)| in cases such as
23943       `\.{..\{curl2\}z\{0,0\}..}'. */
23944    t = mp_scan_direction (mp);
23945    if (t != mp_open) {
23946      mp_right_type (path_q) = (unsigned short) t;
23947      number_clone(path_q->right_given, cur_exp_value_number ());
23948      if (mp_left_type (path_q) == mp_open) {
23949        mp_left_type (path_q) = (unsigned short) t;
23950        number_clone(path_q->left_given, cur_exp_value_number ());
23951      }   /* note that |left_given(q)=left_curl(q)| */
23952    }
23953  }
23954  d = cur_cmd();
23955  if (d == mp_path_join) {
23956    /* Determine the tension and/or control points */
23957    mp_get_x_next (mp);
23958    if (cur_cmd() == mp_tension) {
23959      /* Set explicit tensions */
23960      mp_get_x_next (mp);
23961      set_number_from_scaled (y, cur_cmd());
23962      if (cur_cmd() == mp_at_least)
23963        mp_get_x_next (mp);
23964      mp_scan_primary (mp);
23965      force_valid_tension_setting(mp);
23966      if (number_to_scaled (y) == mp_at_least) {
23967       if (is_number(cur_exp_value_number()))
23968         number_negate (cur_exp_value_number());
23969      }
23970      number_clone(path_q->right_tension, cur_exp_value_number ());
23971      if (cur_cmd() == mp_and_command) {
23972        mp_get_x_next (mp);
23973        set_number_from_scaled (y, cur_cmd());
23974        if (cur_cmd() == mp_at_least)
23975          mp_get_x_next (mp);
23976        mp_scan_primary (mp);
23977        force_valid_tension_setting(mp);
23978        if (number_to_scaled (y) == mp_at_least) {
23979          if (is_number(cur_exp_value_number()))
23980            number_negate (cur_exp_value_number());
23981        }
23982      }
23983      number_clone (y, cur_exp_value_number ());
23984
23985    } else if (cur_cmd() == mp_controls) {
23986      /* Set explicit control points */
23987      mp_right_type (path_q) = mp_explicit;
23988      t = mp_explicit;
23989      mp_get_x_next (mp);
23990      mp_scan_primary (mp);
23991      mp_known_pair (mp);
23992      number_clone (path_q->right_x, mp->cur_x);
23993      number_clone (path_q->right_y, mp->cur_y);
23994      if (cur_cmd() != mp_and_command) {
23995        number_clone (x, path_q->right_x);
23996        number_clone (y, path_q->right_y);
23997      } else {
23998        mp_get_x_next (mp);
23999        mp_scan_primary (mp);
24000        mp_known_pair (mp);
24001        number_clone (x, mp->cur_x);
24002        number_clone (y, mp->cur_y);
24003      }
24004
24005    } else {
24006      set_number_to_unity(path_q->right_tension);
24007      set_number_to_unity(y);
24008      mp_back_input (mp);         /* default tension */
24009      goto DONE;
24010    };
24011    if (cur_cmd() != mp_path_join) {
24012      const char *hlp[] = { "A path join command should end with two dots.", NULL};
24013      mp_back_error (mp, "Missing `..' has been inserted", hlp, true);
24014    }
24015  DONE:
24016    ;
24017  } else if (d != mp_ampersand) {
24018    goto FINISH_PATH;
24019  }
24020  mp_get_x_next (mp);
24021  if (cur_cmd() == mp_left_brace) {
24022    /* Put the post-join direction information into |x| and |t| */
24023    /* Since |left_tension| and |mp_left_y| share the same position in knot nodes,
24024       and since |left_given| is similarly equivalent to |left_x|, we use
24025       |x| and |y| to hold the given direction and tension information when
24026       there are no explicit control points. */
24027    t = mp_scan_direction (mp);
24028    if (mp_right_type (path_q) != mp_explicit)
24029      number_clone (x, cur_exp_value_number ());
24030    else
24031      t = mp_explicit;            /* the direction information is superfluous */
24032
24033  } else if (mp_right_type (path_q) != mp_explicit) {
24034    t = mp_open;
24035    set_number_to_zero(x);
24036  }
24037
24038  if (cur_cmd() == mp_cycle) {
24039    /* Get ready to close a cycle */
24040    /* If a person tries to define an entire path by saying `\.{(x,y)\&cycle}',
24041       we silently change the specification to `\.{(x,y)..cycle}', since a cycle
24042       shouldn't have length zero. */
24043    cycle_hit = true;
24044    mp_get_x_next (mp);
24045    pp = path_p;
24046    qq = path_p;
24047    if (d == mp_ampersand) {
24048      if (path_p == path_q) {
24049        d = mp_path_join;
24050        set_number_to_unity(path_q->right_tension);
24051        set_number_to_unity(y);
24052      }
24053    }
24054  } else {
24055    mp_scan_tertiary (mp);
24056    /* Convert the right operand, |cur_exp|,
24057      into a partial path from |pp| to~|qq| */
24058    if (mp->cur_exp.type != mp_path_type)
24059      pp = mp_pair_to_knot (mp);
24060    else
24061      pp = cur_exp_knot ();
24062    qq = pp;
24063    while (mp_next_knot (qq) != pp)
24064      qq = mp_next_knot (qq);
24065    if (mp_left_type (pp) != mp_endpoint) {       /* open up a cycle */
24066      r = mp_copy_knot (mp, pp);
24067      mp_next_knot (qq) = r;
24068      qq = r;
24069    }
24070    mp_left_type (pp) = mp_open;
24071    mp_right_type (qq) = mp_open;
24072  }
24073  /* Join the partial paths and reset |p| and |q| to the head and tail
24074    of the result */
24075  if (d == mp_ampersand) {
24076    if (!(number_equal (path_q->x_coord, pp->x_coord)) ||
24077        !(number_equal (path_q->y_coord, pp->y_coord))) {
24078      const char *hlp[] = {
24079             "When you join paths `p&q', the ending point of p",
24080             "must be exactly equal to the starting point of q.",
24081             "So I'm going to pretend that you said `p..q' instead.",
24082             NULL };
24083      mp_back_error (mp, "Paths don't touch; `&' will be changed to `..'", hlp, true);
24084@.Paths don't touch@>;
24085      mp_get_x_next (mp);
24086      d = mp_path_join;
24087      set_number_to_unity (path_q->right_tension);
24088      set_number_to_unity (y);
24089    }
24090  }
24091  /* Plug an opening in |mp_right_type(pp)|, if possible */
24092  if (mp_right_type (pp) == mp_open) {
24093    if ((t == mp_curl) || (t == mp_given)) {
24094      mp_right_type (pp) = (unsigned short) t;
24095      number_clone (pp->right_given, x);
24096    }
24097  }
24098  if (d == mp_ampersand) {
24099    /* Splice independent paths together */
24100    if (mp_left_type (path_q) == mp_open)
24101      if (mp_right_type (path_q) == mp_open) {
24102        mp_left_type (path_q) = mp_curl;
24103        set_number_to_unity(path_q->left_curl);
24104      }
24105    if (mp_right_type (pp) == mp_open)
24106      if (t == mp_open) {
24107        mp_right_type (pp) = mp_curl;
24108        set_number_to_unity(pp->right_curl);
24109      }
24110    mp_right_type (path_q) = mp_right_type (pp);
24111    mp_next_knot (path_q) = mp_next_knot (pp);
24112    number_clone (path_q->right_x, pp->right_x);
24113    number_clone (path_q->right_y, pp->right_y);
24114    mp_xfree (pp);
24115    if (qq == pp)
24116      qq = path_q;
24117
24118  } else {
24119    /* Plug an opening in |mp_right_type(q)|, if possible */
24120    if (mp_right_type (path_q) == mp_open) {
24121      if ((mp_left_type (path_q) == mp_curl) || (mp_left_type (path_q) == mp_given)) {
24122        mp_right_type (path_q) = mp_left_type (path_q);
24123        number_clone(path_q->right_given, path_q->left_given);
24124      }
24125    }
24126
24127    mp_next_knot (path_q) = pp;
24128    number_clone (pp->left_y, y);
24129    if (t != mp_open) {
24130      number_clone (pp->left_x, x);
24131      mp_left_type (pp) = (unsigned short) t;
24132    };
24133  }
24134  path_q = qq;
24135
24136  if (cur_cmd() >= mp_min_expression_command)
24137    if (cur_cmd() <= mp_ampersand)
24138      if (!cycle_hit)
24139        goto CONTINUE_PATH;
24140FINISH_PATH:
24141  /* Choose control points for the path and put the result into |cur_exp| */
24142  if (cycle_hit) {
24143    if (d == mp_ampersand)
24144      path_p = path_q;
24145  } else {
24146    mp_left_type (path_p) = mp_endpoint;
24147    if (mp_right_type (path_p) == mp_open) {
24148      mp_right_type (path_p) = mp_curl;
24149      set_number_to_unity(path_p->right_curl);
24150    }
24151    mp_right_type (path_q) = mp_endpoint;
24152    if (mp_left_type (path_q) == mp_open) {
24153      mp_left_type (path_q) = mp_curl;
24154      set_number_to_unity(path_q->left_curl);
24155    }
24156    mp_next_knot (path_q) = path_p;
24157  }
24158  mp_make_choices (mp, path_p);
24159  mp->cur_exp.type = mp_path_type;
24160  set_cur_exp_knot (path_p);
24161
24162  free_number (x);
24163  free_number (y);
24164  return 1;
24165}
24166
24167
24168@ A pair of numeric values is changed into a knot node for a one-point path
24169when \MP\ discovers that the pair is part of a path.
24170
24171@c
24172static mp_knot mp_pair_to_knot (MP mp) {                               /* convert a pair to a knot with two endpoints */
24173  mp_knot q;    /* the new node */
24174  q = mp_new_knot(mp);
24175  mp_left_type (q) = mp_endpoint;
24176  mp_right_type (q) = mp_endpoint;
24177  mp_originator (q) = mp_metapost_user;
24178  mp_next_knot (q) = q;
24179  mp_known_pair (mp);
24180  number_clone (q->x_coord, mp->cur_x);
24181  number_clone (q->y_coord, mp->cur_y);
24182  return q;
24183}
24184
24185
24186@ The |known_pair| subroutine sets |cur_x| and |cur_y| to the components
24187of the current expression, assuming that the current expression is a
24188pair of known numerics. Unknown components are zeroed, and the
24189current expression is flushed.
24190
24191@<Declarations@>=
24192static void mp_known_pair (MP mp);
24193
24194@ @c
24195void mp_known_pair (MP mp) {
24196  mp_value new_expr;
24197  mp_node p;    /* the pair node */
24198  memset(&new_expr,0,sizeof(mp_value));
24199  new_number(new_expr.data.n);
24200  if (mp->cur_exp.type != mp_pair_type) {
24201    const char *hlp[] = {
24202           "I need x and y numbers for this part of the path.",
24203           "The value I found (see above) was no good;",
24204           "so I'll try to keep going by using zero instead.",
24205           "(Chapter 27 of The METAFONTbook explains that",
24206           "you might want to type `I ??" "?' now.)",
24207           NULL };
24208    mp_disp_err(mp, NULL);
24209    mp_back_error (mp, "Undefined coordinates have been replaced by (0,0)", hlp, true);
24210    mp_get_x_next (mp);
24211    mp_flush_cur_exp (mp, new_expr);
24212    set_number_to_zero(mp->cur_x);
24213    set_number_to_zero(mp->cur_y);
24214  } else {
24215    p = value_node (cur_exp_node ());
24216    /* Make sure that both |x| and |y| parts of |p| are known;
24217       copy them into |cur_x| and |cur_y| */
24218    if (mp_type (x_part (p)) == mp_known) {
24219      number_clone(mp->cur_x, value_number (x_part (p)));
24220    } else {
24221      const char *hlp[] = {
24222             "I need a `known' x value for this part of the path.",
24223             "The value I found (see above) was no good;",
24224             "so I'll try to keep going by using zero instead.",
24225             "(Chapter 27 of The METAFONTbook explains that",
24226             "you might want to type `I ??" "?' now.)",
24227             NULL };
24228      mp_disp_err (mp, x_part (p));
24229      mp_back_error (mp, "Undefined x coordinate has been replaced by 0", hlp, true);
24230      mp_get_x_next (mp);
24231      mp_recycle_value (mp, x_part (p));
24232      set_number_to_zero(mp->cur_x);
24233    }
24234    if (mp_type (y_part (p)) == mp_known) {
24235      number_clone(mp->cur_y, value_number (y_part (p)));
24236    } else {
24237      const char *hlp[] = {
24238             "I need a `known' y value for this part of the path.",
24239             "The value I found (see above) was no good;",
24240             "so I'll try to keep going by using zero instead.",
24241             "(Chapter 27 of The METAFONTbook explains that",
24242             "you might want to type `I ??" "?' now.)",
24243             NULL };
24244      mp_disp_err (mp, y_part (p));
24245      mp_back_error (mp, "Undefined y coordinate has been replaced by 0", hlp, true);
24246      mp_get_x_next (mp);
24247      mp_recycle_value (mp, y_part (p));
24248      set_number_to_zero(mp->cur_y);
24249    }
24250    mp_flush_cur_exp (mp, new_expr);
24251  }
24252}
24253
24254@ The |scan_direction| subroutine looks at the directional information
24255that is enclosed in braces, and also scans ahead to the following character.
24256A type code is returned, either |open| (if the direction was $(0,0)$),
24257or |curl| (if the direction was a curl of known value |cur_exp|), or
24258|given| (if the direction is given by the |angle| value that now
24259appears in |cur_exp|).
24260
24261There's nothing difficult about this subroutine, but the program is rather
24262lengthy because a variety of potential errors need to be nipped in the bud.
24263
24264@c
24265static quarterword mp_scan_direction (MP mp) {
24266  int t;        /* the type of information found */
24267  mp_get_x_next (mp);
24268  if (cur_cmd() == mp_curl_command) {
24269    /* Scan a curl specification */
24270    mp_get_x_next (mp);
24271    mp_scan_expression (mp);
24272    if ((mp->cur_exp.type != mp_known) || (number_negative(cur_exp_value_number ()))) {
24273      mp_value new_expr;
24274      const char *hlp[] = { "A curl must be a known, nonnegative number.", NULL };
24275      memset(&new_expr,0,sizeof(mp_value));
24276      new_number(new_expr.data.n);
24277      set_number_to_unity(new_expr.data.n);
24278      mp_disp_err(mp, NULL);
24279      mp_back_error (mp, "Improper curl has been replaced by 1", hlp, true);
24280      mp_get_x_next (mp);
24281      mp_flush_cur_exp (mp, new_expr);
24282    }
24283    t = mp_curl;
24284
24285  } else {
24286    /* Scan a given direction */
24287    mp_scan_expression (mp);
24288    if (mp->cur_exp.type > mp_pair_type) {
24289      /* Get given directions separated by commas */
24290      mp_number xx;
24291      new_number(xx);
24292      if (mp->cur_exp.type != mp_known) {
24293        mp_value new_expr;
24294        const char *hlp[] = {
24295               "I need a `known' x value for this part of the path.",
24296               "The value I found (see above) was no good;",
24297               "so I'll try to keep going by using zero instead.",
24298               "(Chapter 27 of The METAFONTbook explains that",
24299               "you might want to type `I ??" "?' now.)",
24300               NULL };
24301        memset(&new_expr,0,sizeof(mp_value));
24302        new_number(new_expr.data.n);
24303        set_number_to_zero(new_expr.data.n);
24304        mp_disp_err(mp, NULL);
24305        mp_back_error (mp, "Undefined x coordinate has been replaced by 0", hlp, true);
24306        mp_get_x_next (mp);
24307        mp_flush_cur_exp (mp, new_expr);
24308      }
24309      number_clone(xx, cur_exp_value_number ());
24310      if (cur_cmd() != mp_comma) {
24311        const char *hlp[] = {
24312               "I've got the x coordinate of a path direction;",
24313               "will look for the y coordinate next.",
24314               NULL };
24315        mp_back_error (mp, "Missing `,' has been inserted", hlp, true);
24316      }
24317      mp_get_x_next (mp);
24318      mp_scan_expression (mp);
24319      if (mp->cur_exp.type != mp_known) {
24320        mp_value new_expr;
24321        const char *hlp[] = {
24322               "I need a `known' y value for this part of the path.",
24323               "The value I found (see above) was no good;",
24324               "so I'll try to keep going by using zero instead.",
24325               "(Chapter 27 of The METAFONTbook explains that",
24326               "you might want to type `I ??" "?' now.)",
24327               NULL };
24328        memset(&new_expr,0,sizeof(mp_value));
24329        new_number(new_expr.data.n);
24330        set_number_to_zero(new_expr.data.n);
24331        mp_disp_err(mp, NULL);
24332        mp_back_error (mp, "Undefined y coordinate has been replaced by 0", hlp, true);
24333        mp_get_x_next (mp);
24334        mp_flush_cur_exp (mp, new_expr);
24335      }
24336      number_clone(mp->cur_y, cur_exp_value_number ());
24337      number_clone(mp->cur_x, xx);
24338      free_number(xx);
24339
24340    } else {
24341      mp_known_pair (mp);
24342    }
24343    if (number_zero(mp->cur_x) && number_zero(mp->cur_y))
24344      t = mp_open;
24345    else {
24346      mp_number narg;
24347      new_angle (narg);
24348      n_arg (narg, mp->cur_x, mp->cur_y);
24349      t = mp_given;
24350      set_cur_exp_value_number (narg);
24351      free_number (narg);
24352    }
24353  }
24354  if (cur_cmd() != mp_right_brace) {
24355    const char *hlp[] = {
24356           "I've scanned a direction spec for part of a path,",
24357           "so a right brace should have come next.",
24358           "I shall pretend that one was there.",
24359           NULL };
24360    mp_back_error (mp, "Missing `}' has been inserted", hlp, true);
24361  }
24362  mp_get_x_next (mp);
24363  return (quarterword) t;
24364}
24365
24366
24367@ Finally, we sometimes need to scan an expression whose value is
24368supposed to be either |true_code| or |false_code|.
24369
24370@d mp_get_boolean(mp) do {
24371  mp_get_x_next (mp);
24372  mp_scan_expression (mp);
24373  if (mp->cur_exp.type != mp_boolean_type) {
24374    do_boolean_error(mp);
24375  }
24376} while (0)
24377
24378@<Declare the basic parsing subroutines@>=
24379static void do_boolean_error (MP mp) {
24380  mp_value new_expr;
24381  const char *hlp[] = {
24382         "The expression shown above should have had a definite",
24383         "true-or-false value. I'm changing it to `false'.",
24384         NULL };
24385  memset(&new_expr,0,sizeof(mp_value));
24386  new_number(new_expr.data.n);
24387  mp_disp_err(mp, NULL);
24388  set_number_from_boolean (new_expr.data.n, mp_false_code);
24389  mp_back_error (mp, "Undefined condition will be treated as `false'", hlp, true);
24390  mp_get_x_next (mp);
24391  mp_flush_cur_exp (mp, new_expr);
24392  mp->cur_exp.type = mp_boolean_type;
24393}
24394
24395@ @<Declarations@>=
24396static void do_boolean_error (MP mp);
24397
24398@* Doing the operations.
24399The purpose of parsing is primarily to permit people to avoid piles of
24400parentheses. But the real work is done after the structure of an expression
24401has been recognized; that's when new expressions are generated. We
24402turn now to the guts of \MP, which handles individual operators that
24403have come through the parsing mechanism.
24404
24405We'll start with the easy ones that take no operands, then work our way
24406up to operators with one and ultimately two arguments. In other words,
24407we will write the three procedures |do_nullary|, |do_unary|, and |do_binary|
24408that are invoked periodically by the expression scanners.
24409
24410First let's make sure that all of the primitive operators are in the
24411hash table. Although |scan_primary| and its relatives made use of the
24412\\{cmd} code for these operators, the \\{do} routines base everything
24413on the \\{mod} code. For example, |do_binary| doesn't care whether the
24414operation it performs is a |primary_binary| or |secondary_binary|, etc.
24415
24416@<Put each...@>=
24417mp_primitive (mp, "true", mp_nullary, mp_true_code);
24418@:true_}{\&{true} primitive@>;
24419mp_primitive (mp, "false", mp_nullary, mp_false_code);
24420@:false_}{\&{false} primitive@>;
24421mp_primitive (mp, "nullpicture", mp_nullary, mp_null_picture_code);
24422@:null_picture_}{\&{nullpicture} primitive@>;
24423mp_primitive (mp, "nullpen", mp_nullary, mp_null_pen_code);
24424@:null_pen_}{\&{nullpen} primitive@>;
24425mp_primitive (mp, "readstring", mp_nullary, mp_read_string_op);
24426@:read_string_}{\&{readstring} primitive@>;
24427mp_primitive (mp, "pencircle", mp_nullary, mp_pen_circle);
24428@:pen_circle_}{\&{pencircle} primitive@>;
24429mp_primitive (mp, "normaldeviate", mp_nullary, mp_normal_deviate);
24430@:normal_deviate_}{\&{normaldeviate} primitive@>;
24431mp_primitive (mp, "readfrom", mp_unary, mp_read_from_op);
24432@:read_from_}{\&{readfrom} primitive@>;
24433mp_primitive (mp, "closefrom", mp_unary, mp_close_from_op);
24434@:close_from_}{\&{closefrom} primitive@>;
24435mp_primitive (mp, "odd", mp_unary, mp_odd_op);
24436@:odd_}{\&{odd} primitive@>;
24437mp_primitive (mp, "known", mp_unary, mp_known_op);
24438@:known_}{\&{known} primitive@>;
24439mp_primitive (mp, "unknown", mp_unary, mp_unknown_op);
24440@:unknown_}{\&{unknown} primitive@>;
24441mp_primitive (mp, "not", mp_unary, mp_not_op);
24442@:not_}{\&{not} primitive@>;
24443mp_primitive (mp, "decimal", mp_unary, mp_decimal);
24444@:decimal_}{\&{decimal} primitive@>;
24445mp_primitive (mp, "reverse", mp_unary, mp_reverse);
24446@:reverse_}{\&{reverse} primitive@>;
24447mp_primitive (mp, "makepath", mp_unary, mp_make_path_op);
24448@:make_path_}{\&{makepath} primitive@>;
24449mp_primitive (mp, "makepen", mp_unary, mp_make_pen_op);
24450@:make_pen_}{\&{makepen} primitive@>;
24451mp_primitive (mp, "oct", mp_unary, mp_oct_op);
24452@:oct_}{\&{oct} primitive@>;
24453mp_primitive (mp, "hex", mp_unary, mp_hex_op);
24454@:hex_}{\&{hex} primitive@>;
24455mp_primitive (mp, "ASCII", mp_unary, mp_ASCII_op);
24456@:ASCII_}{\&{ASCII} primitive@>;
24457mp_primitive (mp, "char", mp_unary, mp_char_op);
24458@:char_}{\&{char} primitive@>;
24459mp_primitive (mp, "length", mp_unary, mp_length_op);
24460@:length_}{\&{length} primitive@>;
24461mp_primitive (mp, "turningnumber", mp_unary, mp_turning_op);
24462@:turning_number_}{\&{turningnumber} primitive@>;
24463mp_primitive (mp, "xpart", mp_unary, mp_x_part);
24464@:x_part_}{\&{xpart} primitive@>;
24465mp_primitive (mp, "ypart", mp_unary, mp_y_part);
24466@:y_part_}{\&{ypart} primitive@>;
24467mp_primitive (mp, "xxpart", mp_unary, mp_xx_part);
24468@:xx_part_}{\&{xxpart} primitive@>;
24469mp_primitive (mp, "xypart", mp_unary, mp_xy_part);
24470@:xy_part_}{\&{xypart} primitive@>;
24471mp_primitive (mp, "yxpart", mp_unary, mp_yx_part);
24472@:yx_part_}{\&{yxpart} primitive@>;
24473mp_primitive (mp, "yypart", mp_unary, mp_yy_part);
24474@:yy_part_}{\&{yypart} primitive@>;
24475mp_primitive (mp, "redpart", mp_unary, mp_red_part);
24476@:red_part_}{\&{redpart} primitive@>;
24477mp_primitive (mp, "greenpart", mp_unary, mp_green_part);
24478@:green_part_}{\&{greenpart} primitive@>;
24479mp_primitive (mp, "bluepart", mp_unary, mp_blue_part);
24480@:blue_part_}{\&{bluepart} primitive@>;
24481mp_primitive (mp, "cyanpart", mp_unary, mp_cyan_part);
24482@:cyan_part_}{\&{cyanpart} primitive@>;
24483mp_primitive (mp, "magentapart", mp_unary, mp_magenta_part);
24484@:magenta_part_}{\&{magentapart} primitive@>;
24485mp_primitive (mp, "yellowpart", mp_unary, mp_yellow_part);
24486@:yellow_part_}{\&{yellowpart} primitive@>;
24487mp_primitive (mp, "blackpart", mp_unary, mp_black_part);
24488@:black_part_}{\&{blackpart} primitive@>;
24489mp_primitive (mp, "greypart", mp_unary, mp_grey_part);
24490@:grey_part_}{\&{greypart} primitive@>;
24491mp_primitive (mp, "colormodel", mp_unary, mp_color_model_part);
24492@:color_model_part_}{\&{colormodel} primitive@>;
24493mp_primitive (mp, "fontpart", mp_unary, mp_font_part);
24494@:font_part_}{\&{fontpart} primitive@>;
24495mp_primitive (mp, "textpart", mp_unary, mp_text_part);
24496@:text_part_}{\&{textpart} primitive@>;
24497mp_primitive (mp, "prescriptpart", mp_unary, mp_prescript_part);
24498@:prescript_part_}{\&{prescriptpart} primitive@>;
24499mp_primitive (mp, "postscriptpart", mp_unary, mp_postscript_part);
24500@:postscript_part_}{\&{postscriptpart} primitive@>;
24501mp_primitive (mp, "pathpart", mp_unary, mp_path_part);
24502@:path_part_}{\&{pathpart} primitive@>;
24503mp_primitive (mp, "penpart", mp_unary, mp_pen_part);
24504@:pen_part_}{\&{penpart} primitive@>;
24505mp_primitive (mp, "dashpart", mp_unary, mp_dash_part);
24506@:dash_part_}{\&{dashpart} primitive@>;
24507mp_primitive (mp, "sqrt", mp_unary, mp_sqrt_op);
24508@:sqrt_}{\&{sqrt} primitive@>;
24509mp_primitive (mp, "mexp", mp_unary, mp_m_exp_op);
24510@:m_exp_}{\&{mexp} primitive@>;
24511mp_primitive (mp, "mlog", mp_unary, mp_m_log_op);
24512@:m_log_}{\&{mlog} primitive@>;
24513mp_primitive (mp, "sind", mp_unary, mp_sin_d_op);
24514@:sin_d_}{\&{sind} primitive@>;
24515mp_primitive (mp, "cosd", mp_unary, mp_cos_d_op);
24516@:cos_d_}{\&{cosd} primitive@>;
24517mp_primitive (mp, "floor", mp_unary, mp_floor_op);
24518@:floor_}{\&{floor} primitive@>;
24519mp_primitive (mp, "uniformdeviate", mp_unary, mp_uniform_deviate);
24520@:uniform_deviate_}{\&{uniformdeviate} primitive@>;
24521mp_primitive (mp, "charexists", mp_unary, mp_char_exists_op);
24522@:char_exists_}{\&{charexists} primitive@>;
24523mp_primitive (mp, "fontsize", mp_unary, mp_font_size);
24524@:font_size_}{\&{fontsize} primitive@>;
24525mp_primitive (mp, "llcorner", mp_unary, mp_ll_corner_op);
24526@:ll_corner_}{\&{llcorner} primitive@>;
24527mp_primitive (mp, "lrcorner", mp_unary, mp_lr_corner_op);
24528@:lr_corner_}{\&{lrcorner} primitive@>;
24529mp_primitive (mp, "ulcorner", mp_unary, mp_ul_corner_op);
24530@:ul_corner_}{\&{ulcorner} primitive@>;
24531mp_primitive (mp, "urcorner", mp_unary, mp_ur_corner_op);
24532@:ur_corner_}{\&{urcorner} primitive@>;
24533mp_primitive (mp, "arclength", mp_unary, mp_arc_length);
24534@:arc_length_}{\&{arclength} primitive@>;
24535mp_primitive (mp, "angle", mp_unary, mp_angle_op);
24536@:angle_}{\&{angle} primitive@>;
24537mp_primitive (mp, "cycle", mp_cycle, mp_cycle_op);
24538@:cycle_}{\&{cycle} primitive@>;
24539mp_primitive (mp, "stroked", mp_unary, mp_stroked_op);
24540@:stroked_}{\&{stroked} primitive@>;
24541mp_primitive (mp, "filled", mp_unary, mp_filled_op);
24542@:filled_}{\&{filled} primitive@>;
24543mp_primitive (mp, "textual", mp_unary, mp_textual_op);
24544@:textual_}{\&{textual} primitive@>;
24545mp_primitive (mp, "clipped", mp_unary, mp_clipped_op);
24546@:clipped_}{\&{clipped} primitive@>;
24547mp_primitive (mp, "bounded", mp_unary, mp_bounded_op);
24548@:bounded_}{\&{bounded} primitive@>;
24549mp_primitive (mp, "+", mp_plus_or_minus, mp_plus);
24550@:+ }{\.{+} primitive@>;
24551mp_primitive (mp, "-", mp_plus_or_minus, mp_minus);
24552@:- }{\.{-} primitive@>;
24553mp_primitive (mp, "*", mp_secondary_binary, mp_times);
24554@:* }{\.{*} primitive@>;
24555mp_primitive (mp, "/", mp_slash, mp_over);
24556mp->frozen_slash = mp_frozen_primitive (mp, "/", mp_slash, mp_over);
24557@:/ }{\.{/} primitive@>;
24558mp_primitive (mp, "++", mp_tertiary_binary, mp_pythag_add);
24559@:++_}{\.{++} primitive@>;
24560mp_primitive (mp, "+-+", mp_tertiary_binary, mp_pythag_sub);
24561@:+-+_}{\.{+-+} primitive@>;
24562mp_primitive (mp, "or", mp_tertiary_binary, mp_or_op);
24563@:or_}{\&{or} primitive@>;
24564mp_primitive (mp, "and", mp_and_command, mp_and_op);
24565@:and_}{\&{and} primitive@>;
24566mp_primitive (mp, "<", mp_expression_binary, mp_less_than);
24567@:< }{\.{<} primitive@>;
24568mp_primitive (mp, "<=", mp_expression_binary, mp_less_or_equal);
24569@:<=_}{\.{<=} primitive@>;
24570mp_primitive (mp, ">", mp_expression_binary, mp_greater_than);
24571@:> }{\.{>} primitive@>;
24572mp_primitive (mp, ">=", mp_expression_binary, mp_greater_or_equal);
24573@:>=_}{\.{>=} primitive@>;
24574mp_primitive (mp, "=", mp_equals, mp_equal_to);
24575@:= }{\.{=} primitive@>;
24576mp_primitive (mp, "<>", mp_expression_binary, mp_unequal_to);
24577@:<>_}{\.{<>} primitive@>;
24578mp_primitive (mp, "substring", mp_primary_binary, mp_substring_of);
24579@:substring_}{\&{substring} primitive@>;
24580mp_primitive (mp, "subpath", mp_primary_binary, mp_subpath_of);
24581@:subpath_}{\&{subpath} primitive@>;
24582mp_primitive (mp, "directiontime", mp_primary_binary, mp_direction_time_of);
24583@:direction_time_}{\&{directiontime} primitive@>;
24584mp_primitive (mp, "point", mp_primary_binary, mp_point_of);
24585@:point_}{\&{point} primitive@>;
24586mp_primitive (mp, "precontrol", mp_primary_binary, mp_precontrol_of);
24587@:precontrol_}{\&{precontrol} primitive@>;
24588mp_primitive (mp, "postcontrol", mp_primary_binary, mp_postcontrol_of);
24589@:postcontrol_}{\&{postcontrol} primitive@>;
24590mp_primitive (mp, "penoffset", mp_primary_binary, mp_pen_offset_of);
24591@:pen_offset_}{\&{penoffset} primitive@>;
24592mp_primitive (mp, "arctime", mp_primary_binary, mp_arc_time_of);
24593@:arc_time_of_}{\&{arctime} primitive@>;
24594mp_primitive (mp, "mpversion", mp_nullary, mp_version);
24595@:mp_verison_}{\&{mpversion} primitive@>;
24596mp_primitive (mp, "&", mp_ampersand, mp_concatenate);
24597@:!!!}{\.{\&} primitive@>;
24598mp_primitive (mp, "rotated", mp_secondary_binary, mp_rotated_by);
24599@:rotated_}{\&{rotated} primitive@>;
24600mp_primitive (mp, "slanted", mp_secondary_binary, mp_slanted_by);
24601@:slanted_}{\&{slanted} primitive@>;
24602mp_primitive (mp, "scaled", mp_secondary_binary, mp_scaled_by);
24603@:scaled_}{\&{scaled} primitive@>;
24604mp_primitive (mp, "shifted", mp_secondary_binary, mp_shifted_by);
24605@:shifted_}{\&{shifted} primitive@>;
24606mp_primitive (mp, "transformed", mp_secondary_binary, mp_transformed_by);
24607@:transformed_}{\&{transformed} primitive@>;
24608mp_primitive (mp, "xscaled", mp_secondary_binary, mp_x_scaled);
24609@:x_scaled_}{\&{xscaled} primitive@>;
24610mp_primitive (mp, "yscaled", mp_secondary_binary, mp_y_scaled);
24611@:y_scaled_}{\&{yscaled} primitive@>;
24612mp_primitive (mp, "zscaled", mp_secondary_binary, mp_z_scaled);
24613@:z_scaled_}{\&{zscaled} primitive@>;
24614mp_primitive (mp, "infont", mp_secondary_binary, mp_in_font);
24615@:in_font_}{\&{infont} primitive@>;
24616mp_primitive (mp, "intersectiontimes", mp_tertiary_binary, mp_intersect);
24617@:intersection_times_}{\&{intersectiontimes} primitive@>;
24618mp_primitive (mp, "envelope", mp_primary_binary, mp_envelope_of);
24619@:envelope_}{\&{envelope} primitive@>;
24620mp_primitive (mp, "glyph", mp_primary_binary, mp_glyph_infont);
24621@:glyph_infont_}{\&{envelope} primitive@>
24622
24623
24624@ @<Cases of |print_cmd...@>=
24625case mp_nullary:
24626case mp_unary:
24627case mp_primary_binary:
24628case mp_secondary_binary:
24629case mp_tertiary_binary:
24630case mp_expression_binary:
24631case mp_cycle:
24632case mp_plus_or_minus:
24633case mp_slash:
24634case mp_ampersand:
24635case mp_equals:
24636case mp_and_command:
24637mp_print_op (mp, (quarterword) m);
24638break;
24639
24640@ OK, let's look at the simplest \\{do} procedure first.
24641
24642@c
24643@<Declare nullary action procedure@>;
24644static void mp_do_nullary (MP mp, quarterword c) {
24645  check_arith();
24646  if (number_greater (internal_value (mp_tracing_commands), two_t))
24647    mp_show_cmd_mod (mp, mp_nullary, c);
24648  switch (c) {
24649  case mp_true_code:
24650  case mp_false_code:
24651    mp->cur_exp.type = mp_boolean_type;
24652    set_cur_exp_value_boolean (c);
24653    break;
24654  case mp_null_picture_code:
24655    mp->cur_exp.type = mp_picture_type;
24656    set_cur_exp_node ((mp_node)mp_get_edge_header_node (mp));
24657    mp_init_edges (mp, (mp_edge_header_node)cur_exp_node ());
24658    break;
24659  case mp_null_pen_code:
24660    mp->cur_exp.type = mp_pen_type;
24661    set_cur_exp_knot (mp_get_pen_circle (mp, zero_t));
24662    break;
24663  case mp_normal_deviate:
24664    {
24665      mp_number r;
24666      new_number (r);
24667      /*mp_norm_rand (mp, &r);*/
24668      m_norm_rand (r);
24669      mp->cur_exp.type = mp_known;
24670      set_cur_exp_value_number (r);
24671      free_number (r);
24672    }
24673    break;
24674  case mp_pen_circle:
24675    mp->cur_exp.type = mp_pen_type;
24676    set_cur_exp_knot (mp_get_pen_circle (mp, unity_t));
24677    break;
24678  case mp_version:
24679    mp->cur_exp.type = mp_string_type;
24680    set_cur_exp_str (mp_intern (mp, metapost_version));
24681    break;
24682  case mp_read_string_op:
24683    /* Read a string from the terminal */
24684    if (mp->noninteractive || mp->interaction <= mp_nonstop_mode)
24685      mp_fatal_error (mp, "*** (cannot readstring in nonstop modes)");
24686    mp_begin_file_reading (mp);
24687    name = is_read;
24688    limit = start;
24689    prompt_input ("");
24690    mp_finish_read (mp);
24691    break;
24692  }                             /* there are no other cases */
24693  check_arith();
24694}
24695
24696
24697@ @<Declare nullary action procedure@>=
24698static void mp_finish_read (MP mp) {                               /* copy |buffer| line to |cur_exp| */
24699  size_t k;
24700  str_room (((int) mp->last - (int) start));
24701  for (k = (size_t) start; k < mp->last; k++) {
24702    append_char (mp->buffer[k]);
24703  }
24704  mp_end_file_reading (mp);
24705  mp->cur_exp.type = mp_string_type;
24706  set_cur_exp_str (mp_make_string (mp));
24707}
24708
24709
24710@ Things get a bit more interesting when there's an operand. The
24711operand to |do_unary| appears in |cur_type| and |cur_exp|.
24712
24713This complicated if test makes sure that any |bounds| or |clip|
24714picture objects that get passed into \&{within} do not raise an
24715error when queried using the color part primitives (this is needed
24716for backward compatibility) .
24717
24718@d cur_pic_item mp_link(edge_list(cur_exp_node()))
24719@d pict_color_type(A) ((cur_pic_item!=NULL) &&
24720         ((!has_color(cur_pic_item))
24721          ||
24722         (((mp_color_model(cur_pic_item)==A)
24723          ||
24724          ((mp_color_model(cur_pic_item)==mp_uninitialized_model) &&
24725           (number_to_scaled (internal_value(mp_default_color_model))/number_to_scaled (unity_t))==(A))))))
24726
24727@d boolean_reset(A) if ( (A) ) set_cur_exp_value_boolean(mp_true_code); else set_cur_exp_value_boolean(mp_false_code)
24728
24729@d type_range(A,B) {
24730  if ( (mp->cur_exp.type>=(A)) && (mp->cur_exp.type<=(B)) )
24731    set_number_from_boolean (new_expr.data.n, mp_true_code);
24732  else
24733    set_number_from_boolean (new_expr.data.n, mp_false_code);
24734  mp_flush_cur_exp(mp, new_expr);
24735  mp->cur_exp.type=mp_boolean_type;
24736  }
24737@d type_test(A) {
24738  if ( mp->cur_exp.type==(mp_variable_type)(A) )
24739    set_number_from_boolean (new_expr.data.n, mp_true_code);
24740  else
24741    set_number_from_boolean (new_expr.data.n, mp_false_code);
24742  mp_flush_cur_exp(mp, new_expr);
24743  mp->cur_exp.type=mp_boolean_type;
24744  }
24745
24746
24747@c
24748@<Declare unary action procedures@>;
24749static void mp_do_unary (MP mp, quarterword c) {
24750  mp_node p;      /* for list manipulation */
24751  mp_value new_expr;
24752  check_arith();
24753  if (number_greater (internal_value (mp_tracing_commands), two_t)) {
24754    /* Trace the current unary operation */
24755    mp_begin_diagnostic (mp);
24756    mp_print_nl (mp, "{");
24757    mp_print_op (mp, c);
24758    mp_print_char (mp, xord ('('));
24759    mp_print_exp (mp, NULL, 0);   /* show the operand, but not verbosely */
24760    mp_print (mp, ")}");
24761    mp_end_diagnostic (mp, false);
24762  }
24763  switch (c) {
24764  case mp_plus:
24765    if (mp->cur_exp.type < mp_color_type)
24766      mp_bad_unary (mp, mp_plus);
24767    break;
24768  case mp_minus:
24769    negate_cur_expr(mp);
24770    break;
24771  case mp_not_op:
24772    if (mp->cur_exp.type != mp_boolean_type) {
24773      mp_bad_unary (mp, mp_not_op);
24774    } else {
24775      halfword bb;
24776      if (cur_exp_value_boolean () == mp_true_code)
24777        bb = mp_false_code;
24778      else
24779        bb = mp_true_code;
24780      set_cur_exp_value_boolean (bb);
24781    }
24782    break;
24783  case mp_sqrt_op:
24784  case mp_m_exp_op:
24785  case mp_m_log_op:
24786  case mp_sin_d_op:
24787  case mp_cos_d_op:
24788  case mp_floor_op:
24789  case mp_uniform_deviate:
24790  case mp_odd_op:
24791  case mp_char_exists_op:
24792    if (mp->cur_exp.type != mp_known) {
24793      mp_bad_unary (mp, c);
24794    } else {
24795      switch (c) {
24796      case mp_sqrt_op:
24797        {
24798          mp_number r1;
24799          new_number (r1);
24800          square_rt (r1, cur_exp_value_number ());
24801          set_cur_exp_value_number  (r1);
24802          free_number (r1);
24803        }
24804        break;
24805      case mp_m_exp_op:
24806        {
24807          mp_number r1;
24808          new_number (r1);
24809          m_exp (r1, cur_exp_value_number ());
24810          set_cur_exp_value_number (r1);
24811          free_number (r1);
24812        }
24813        break;
24814      case mp_m_log_op:
24815        {
24816          mp_number r1;
24817          new_number (r1);
24818          m_log (r1, cur_exp_value_number ());
24819          set_cur_exp_value_number (r1);
24820          free_number (r1);
24821        }
24822        break;
24823      case mp_sin_d_op:
24824      case mp_cos_d_op:
24825        {
24826          mp_number n_sin, n_cos, arg1, arg2;
24827          new_number (arg1);
24828          new_number (arg2);
24829          new_fraction (n_sin);
24830          new_fraction (n_cos); /* results computed by |n_sin_cos| */
24831          number_clone (arg1, cur_exp_value_number());
24832          number_clone (arg2, unity_t);
24833          number_multiply_int (arg2, 360);
24834          number_modulo (arg1, arg2);
24835          convert_scaled_to_angle (arg1);
24836          n_sin_cos (arg1, n_cos, n_sin);
24837          if (c == mp_sin_d_op) {
24838            fraction_to_round_scaled (n_sin);
24839            set_cur_exp_value_number (n_sin);
24840          } else {
24841            fraction_to_round_scaled (n_cos);
24842            set_cur_exp_value_number (n_cos);
24843          }
24844          free_number (arg1);
24845          free_number (arg2);
24846          free_number (n_sin);
24847          free_number (n_cos);
24848        }
24849        break;
24850      case mp_floor_op:
24851        {
24852          mp_number vvx;
24853          new_number (vvx);
24854          number_clone (vvx, cur_exp_value_number ());
24855          floor_scaled (vvx);
24856          set_cur_exp_value_number (vvx);
24857          free_number (vvx);
24858        }
24859        break;
24860      case mp_uniform_deviate:
24861        {
24862          mp_number vvx;
24863          new_number (vvx);
24864          mp_unif_rand (mp, &vvx, cur_exp_value_number ());
24865          set_cur_exp_value_number (vvx);
24866          free_number (vvx);
24867        }
24868        break;
24869      case mp_odd_op:
24870        {
24871          integer vvx = odd (round_unscaled (cur_exp_value_number ()));
24872          boolean_reset (vvx);
24873          mp->cur_exp.type = mp_boolean_type;
24874        }
24875        break;
24876      case mp_char_exists_op:
24877        /* Determine if a character has been shipped out */
24878        set_cur_exp_value_scaled (round_unscaled (cur_exp_value_number ()) % 256);
24879        if (number_negative(cur_exp_value_number ())) {
24880          halfword vv = number_to_scaled(cur_exp_value_number ());
24881          set_cur_exp_value_scaled (vv + 256);
24882        }
24883        boolean_reset (mp->char_exists[number_to_scaled(cur_exp_value_number ())]);
24884        mp->cur_exp.type = mp_boolean_type;
24885        break;
24886      }                             /* there are no other cases */
24887    }
24888    break;
24889  case mp_angle_op:
24890    if (mp_nice_pair (mp, cur_exp_node (), mp->cur_exp.type)) {
24891      mp_number narg;
24892      memset(&new_expr,0,sizeof(mp_value));
24893      new_number(new_expr.data.n);
24894      new_angle (narg);
24895      p = value_node (cur_exp_node ());
24896      n_arg (narg, value_number (x_part (p)), value_number (y_part (p)));
24897      number_clone (new_expr.data.n, narg);
24898      convert_angle_to_scaled (new_expr.data.n);
24899      free_number (narg);
24900      mp_flush_cur_exp (mp, new_expr);
24901    } else {
24902      mp_bad_unary (mp, mp_angle_op);
24903    }
24904    break;
24905  case mp_x_part:
24906  case mp_y_part:
24907    if ((mp->cur_exp.type == mp_pair_type)
24908        || (mp->cur_exp.type == mp_transform_type))
24909      mp_take_part (mp, c);
24910    else if (mp->cur_exp.type == mp_picture_type)
24911      mp_take_pict_part (mp, c);
24912    else
24913      mp_bad_unary (mp, c);
24914    break;
24915  case mp_xx_part:
24916  case mp_xy_part:
24917  case mp_yx_part:
24918  case mp_yy_part:
24919    if (mp->cur_exp.type == mp_transform_type)
24920      mp_take_part (mp, c);
24921    else if (mp->cur_exp.type == mp_picture_type)
24922      mp_take_pict_part (mp, c);
24923    else
24924      mp_bad_unary (mp, c);
24925    break;
24926  case mp_red_part:
24927  case mp_green_part:
24928  case mp_blue_part:
24929    if (mp->cur_exp.type == mp_color_type)
24930      mp_take_part (mp, c);
24931    else if (mp->cur_exp.type == mp_picture_type) {
24932      if pict_color_type
24933        (mp_rgb_model) mp_take_pict_part (mp, c);
24934      else
24935        mp_bad_color_part (mp, c);
24936    } else
24937      mp_bad_unary (mp, c);
24938    break;
24939  case mp_cyan_part:
24940  case mp_magenta_part:
24941  case mp_yellow_part:
24942  case mp_black_part:
24943    if (mp->cur_exp.type == mp_cmykcolor_type)
24944      mp_take_part (mp, c);
24945    else if (mp->cur_exp.type == mp_picture_type) {
24946      if pict_color_type
24947        (mp_cmyk_model) mp_take_pict_part (mp, c);
24948      else
24949        mp_bad_color_part (mp, c);
24950    } else
24951      mp_bad_unary (mp, c);
24952    break;
24953  case mp_grey_part:
24954    if (mp->cur_exp.type == mp_known);
24955    else if (mp->cur_exp.type == mp_picture_type) {
24956      if pict_color_type
24957        (mp_grey_model) mp_take_pict_part (mp, c);
24958      else
24959        mp_bad_color_part (mp, c);
24960    } else
24961      mp_bad_unary (mp, c);
24962    break;
24963  case mp_color_model_part:
24964    if (mp->cur_exp.type == mp_picture_type)
24965      mp_take_pict_part (mp, c);
24966    else
24967      mp_bad_unary (mp, c);
24968    break;
24969  case mp_font_part:
24970  case mp_text_part:
24971  case mp_path_part:
24972  case mp_pen_part:
24973  case mp_dash_part:
24974  case mp_prescript_part:
24975  case mp_postscript_part:
24976    if (mp->cur_exp.type == mp_picture_type)
24977      mp_take_pict_part (mp, c);
24978    else
24979      mp_bad_unary (mp, c);
24980    break;
24981  case mp_char_op:
24982    if (mp->cur_exp.type != mp_known) {
24983      mp_bad_unary (mp, mp_char_op);
24984    } else {
24985      int vv = round_unscaled (cur_exp_value_number ()) % 256;
24986      set_cur_exp_value_scaled (vv);
24987      mp->cur_exp.type = mp_string_type;
24988      if (number_negative(cur_exp_value_number ())) {
24989        vv = number_to_scaled(cur_exp_value_number ()) + 256;
24990        set_cur_exp_value_scaled (vv);
24991      }
24992      {
24993        unsigned char ss[2];
24994        ss[0] = (unsigned char) number_to_scaled(cur_exp_value_number ());
24995        ss[1] = '\0';
24996        set_cur_exp_str (mp_rtsl (mp, (char *) ss, 1));
24997      }
24998    }
24999    break;
25000  case mp_decimal:
25001    if (mp->cur_exp.type != mp_known) {
25002      mp_bad_unary (mp, mp_decimal);
25003    } else {
25004      mp->old_setting = mp->selector;
25005      mp->selector = new_string;
25006      print_number (cur_exp_value_number ());
25007      set_cur_exp_str (mp_make_string (mp));
25008      mp->selector = mp->old_setting;
25009      mp->cur_exp.type = mp_string_type;
25010    }
25011    break;
25012  case mp_oct_op:
25013  case mp_hex_op:
25014  case mp_ASCII_op:
25015    if (mp->cur_exp.type != mp_string_type)
25016      mp_bad_unary (mp, c);
25017    else
25018      mp_str_to_num (mp, c);
25019    break;
25020  case mp_font_size:
25021    if (mp->cur_exp.type != mp_string_type) {
25022      mp_bad_unary (mp, mp_font_size);
25023    } else {
25024      /* Find the design size of the font whose name is |cur_exp| */
25025      /* One simple application of |find_font| is the implementation of the |font_size|
25026         operator that gets the design size for a given font name. */
25027      memset(&new_expr,0,sizeof(mp_value));
25028      new_number(new_expr.data.n);
25029      set_number_from_scaled (new_expr.data.n,
25030               (mp->font_dsize[mp_find_font (mp, mp_str (mp, cur_exp_str ()))] + 8) / 16);
25031      mp_flush_cur_exp (mp, new_expr);
25032    }
25033    break;
25034  case mp_length_op:
25035    /* The length operation is somewhat unusual in that it applies to a variety
25036       of different types of operands. */
25037    switch (mp->cur_exp.type) {
25038    case mp_string_type:
25039      memset(&new_expr,0,sizeof(mp_value));
25040      new_number(new_expr.data.n);
25041      number_clone (new_expr.data.n, unity_t);
25042      number_multiply_int(new_expr.data.n, cur_exp_str ()->len);
25043      mp_flush_cur_exp (mp, new_expr);
25044      break;
25045    case mp_path_type:
25046      memset(&new_expr,0,sizeof(mp_value));
25047      new_number(new_expr.data.n);
25048      mp_path_length (mp, &new_expr.data.n);
25049      mp_flush_cur_exp (mp, new_expr);
25050      break;
25051    case mp_known:
25052      set_cur_exp_value_number (cur_exp_value_number ());
25053      number_abs (cur_exp_value_number ());
25054      break;
25055    case mp_picture_type:
25056      memset(&new_expr,0,sizeof(mp_value));
25057      new_number(new_expr.data.n);
25058      mp_pict_length (mp, &new_expr.data.n);
25059      mp_flush_cur_exp (mp, new_expr);
25060      break;
25061    default:
25062      if (mp_nice_pair (mp, cur_exp_node (), mp->cur_exp.type)) {
25063        memset(&new_expr,0,sizeof(mp_value));
25064        new_number(new_expr.data.n);
25065        pyth_add (new_expr.data.n, value_number (x_part (value_node (cur_exp_node ()))),
25066                                   value_number (y_part (value_node (cur_exp_node ()))));
25067        mp_flush_cur_exp (mp, new_expr);
25068      } else
25069        mp_bad_unary (mp, c);
25070      break;
25071    }
25072    break;
25073  case mp_turning_op:
25074    if (mp->cur_exp.type == mp_pair_type) {
25075      memset(&new_expr,0,sizeof(mp_value));
25076      new_number(new_expr.data.n);
25077      set_number_to_zero(new_expr.data.n);
25078      mp_flush_cur_exp (mp, new_expr);
25079    } else if (mp->cur_exp.type != mp_path_type) {
25080      mp_bad_unary (mp, mp_turning_op);
25081    } else if (mp_left_type (cur_exp_knot ()) == mp_endpoint) {
25082      memset(&new_expr,0,sizeof(mp_value));
25083      new_number(new_expr.data.n);
25084      new_expr.data.p = NULL;
25085      mp_flush_cur_exp (mp, new_expr);      /* not a cyclic path */
25086    } else {
25087      memset(&new_expr,0,sizeof(mp_value));
25088      new_number(new_expr.data.n);
25089      mp_turn_cycles_wrapper (mp, &new_expr.data.n, cur_exp_knot ());
25090      mp_flush_cur_exp (mp, new_expr);
25091    }
25092    break;
25093  case mp_boolean_type:
25094    memset(&new_expr,0,sizeof(mp_value));
25095    new_number(new_expr.data.n);
25096    type_range (mp_boolean_type, mp_unknown_boolean);
25097    break;
25098  case mp_string_type:
25099    memset(&new_expr,0,sizeof(mp_value));
25100    new_number(new_expr.data.n);
25101    type_range (mp_string_type, mp_unknown_string);
25102    break;
25103  case mp_pen_type:
25104    memset(&new_expr,0,sizeof(mp_value));
25105    new_number(new_expr.data.n);
25106    type_range (mp_pen_type, mp_unknown_pen);
25107    break;
25108  case mp_path_type:
25109    memset(&new_expr,0,sizeof(mp_value));
25110    new_number(new_expr.data.n);
25111    type_range (mp_path_type, mp_unknown_path);
25112    break;
25113  case mp_picture_type:
25114    memset(&new_expr,0,sizeof(mp_value));
25115    new_number(new_expr.data.n);
25116    type_range (mp_picture_type, mp_unknown_picture);
25117    break;
25118  case mp_transform_type:
25119  case mp_color_type:
25120  case mp_cmykcolor_type:
25121  case mp_pair_type:
25122    memset(&new_expr,0,sizeof(mp_value));
25123    new_number(new_expr.data.n);
25124    type_test (c);
25125    break;
25126  case mp_numeric_type:
25127    memset(&new_expr,0,sizeof(mp_value));
25128    new_number(new_expr.data.n);
25129    type_range (mp_known, mp_independent);
25130    break;
25131  case mp_known_op:
25132  case mp_unknown_op:
25133    mp_test_known (mp, c);
25134    break;
25135  case mp_cycle_op:
25136    memset(&new_expr,0,sizeof(mp_value));
25137    new_number(new_expr.data.n);
25138    if (mp->cur_exp.type != mp_path_type)
25139      set_number_from_boolean (new_expr.data.n, mp_false_code);
25140    else if (mp_left_type (cur_exp_knot ()) != mp_endpoint)
25141      set_number_from_boolean (new_expr.data.n, mp_true_code);
25142    else
25143      set_number_from_boolean (new_expr.data.n, mp_false_code);
25144    mp_flush_cur_exp (mp, new_expr);
25145    mp->cur_exp.type = mp_boolean_type;
25146    break;
25147  case mp_arc_length:
25148    if (mp->cur_exp.type == mp_pair_type)
25149      mp_pair_to_path (mp);
25150    if (mp->cur_exp.type != mp_path_type) {
25151      mp_bad_unary (mp, mp_arc_length);
25152    } else {
25153      memset(&new_expr,0,sizeof(mp_value));
25154      new_number(new_expr.data.n);
25155      mp_get_arc_length (mp, &new_expr.data.n, cur_exp_knot ());
25156      mp_flush_cur_exp (mp, new_expr);
25157    }
25158    break;
25159  case mp_filled_op:
25160  case mp_stroked_op:
25161  case mp_textual_op:
25162  case mp_clipped_op:
25163  case mp_bounded_op:
25164    /* Here we use the fact that |c-filled_op+fill_code| is the desired graphical
25165    object |type|. */
25166@^data structure assumptions@>
25167    memset(&new_expr,0,sizeof(mp_value));
25168    new_number(new_expr.data.n);
25169    if (mp->cur_exp.type != mp_picture_type) {
25170      set_number_from_boolean (new_expr.data.n, mp_false_code);
25171    } else if (mp_link (edge_list (cur_exp_node ())) == NULL) {
25172      set_number_from_boolean (new_expr.data.n, mp_false_code);
25173    } else if (mp_type (mp_link (edge_list (cur_exp_node ()))) ==
25174               (mp_variable_type) (c + mp_fill_node_type - mp_filled_op)) {
25175      set_number_from_boolean (new_expr.data.n, mp_true_code);
25176    } else {
25177      set_number_from_boolean (new_expr.data.n, mp_false_code);
25178    }
25179    mp_flush_cur_exp (mp, new_expr);
25180    mp->cur_exp.type = mp_boolean_type;
25181    break;
25182  case mp_make_pen_op:
25183    if (mp->cur_exp.type == mp_pair_type)
25184      mp_pair_to_path (mp);
25185    if (mp->cur_exp.type != mp_path_type)
25186      mp_bad_unary (mp, mp_make_pen_op);
25187    else {
25188      mp->cur_exp.type = mp_pen_type;
25189      set_cur_exp_knot (mp_make_pen (mp, cur_exp_knot (), true));
25190    }
25191    break;
25192  case mp_make_path_op:
25193    if (mp->cur_exp.type != mp_pen_type) {
25194      mp_bad_unary (mp, mp_make_path_op);
25195    } else {
25196      mp->cur_exp.type = mp_path_type;
25197      mp_make_path (mp, cur_exp_knot ());
25198    }
25199    break;
25200  case mp_reverse:
25201    if (mp->cur_exp.type == mp_path_type) {
25202      mp_knot pk = mp_htap_ypoc (mp, cur_exp_knot ());
25203      if (mp_right_type (pk) == mp_endpoint)
25204        pk = mp_next_knot (pk);
25205      mp_toss_knot_list (mp, cur_exp_knot ());
25206      set_cur_exp_knot (pk);
25207    } else if (mp->cur_exp.type == mp_pair_type) {
25208      mp_pair_to_path (mp);
25209    } else {
25210      mp_bad_unary (mp, mp_reverse);
25211    }
25212    break;
25213  case mp_ll_corner_op:
25214    if (!mp_get_cur_bbox (mp))
25215      mp_bad_unary (mp, mp_ll_corner_op);
25216    else
25217      mp_pair_value (mp, mp_minx, mp_miny);
25218    break;
25219  case mp_lr_corner_op:
25220    if (!mp_get_cur_bbox (mp))
25221      mp_bad_unary (mp, mp_lr_corner_op);
25222    else
25223      mp_pair_value (mp,  mp_maxx, mp_miny);
25224    break;
25225  case mp_ul_corner_op:
25226    if (!mp_get_cur_bbox (mp))
25227      mp_bad_unary (mp, mp_ul_corner_op);
25228    else
25229      mp_pair_value (mp, mp_minx, mp_maxy);
25230    break;
25231  case mp_ur_corner_op:
25232    if (!mp_get_cur_bbox (mp))
25233      mp_bad_unary (mp, mp_ur_corner_op);
25234    else
25235      mp_pair_value (mp, mp_maxx,  mp_maxy);
25236    break;
25237  case mp_read_from_op:
25238  case mp_close_from_op:
25239    if (mp->cur_exp.type != mp_string_type)
25240      mp_bad_unary (mp, c);
25241    else
25242      mp_do_read_or_close (mp, c);
25243    break;
25244
25245  }                             /* there are no other cases */
25246  check_arith();
25247}
25248
25249
25250@ The |nice_pair| function returns |true| if both components of a pair
25251are known.
25252
25253@<Declare unary action procedures@>=
25254static boolean mp_nice_pair (MP mp, mp_node p, quarterword t) {
25255  (void) mp;
25256  if (t == mp_pair_type) {
25257    p = value_node (p);
25258    if (mp_type (x_part (p)) == mp_known)
25259      if (mp_type (y_part (p)) == mp_known)
25260        return true;
25261  }
25262  return false;
25263}
25264
25265
25266@ The |nice_color_or_pair| function is analogous except that it also accepts
25267fully known colors.
25268
25269@<Declare unary action procedures@>=
25270static boolean mp_nice_color_or_pair (MP mp, mp_node p, quarterword t) {
25271  mp_node q;
25272  (void) mp;
25273  switch (t) {
25274  case mp_pair_type:
25275    q = value_node (p);
25276    if (mp_type (x_part (q)) == mp_known)
25277      if (mp_type (y_part (q)) == mp_known)
25278        return true;
25279    break;
25280  case mp_color_type:
25281    q = value_node (p);
25282    if (mp_type (red_part (q)) == mp_known)
25283      if (mp_type (green_part (q)) == mp_known)
25284        if (mp_type (blue_part (q)) == mp_known)
25285          return true;
25286    break;
25287  case mp_cmykcolor_type:
25288    q = value_node (p);
25289    if (mp_type (cyan_part (q)) == mp_known)
25290      if (mp_type (magenta_part (q)) == mp_known)
25291        if (mp_type (yellow_part (q)) == mp_known)
25292          if (mp_type (black_part (q)) == mp_known)
25293            return true;
25294    break;
25295  }
25296  return false;
25297}
25298
25299
25300@ @<Declare unary action...@>=
25301static void mp_print_known_or_unknown_type (MP mp, quarterword t, mp_node v) {
25302  mp_print_char (mp, xord ('('));
25303  if (t > mp_known)
25304    mp_print (mp, "unknown numeric");
25305  else {
25306    if ((t == mp_pair_type) || (t == mp_color_type) || (t == mp_cmykcolor_type))
25307      if (!mp_nice_color_or_pair (mp, v, t))
25308        mp_print (mp, "unknown ");
25309    mp_print_type (mp, t);
25310  }
25311  mp_print_char (mp, xord (')'));
25312}
25313
25314
25315@ @<Declare unary action...@>=
25316static void mp_bad_unary (MP mp, quarterword c) {
25317  char msg[256];
25318  mp_string sname;
25319  int old_setting = mp->selector;
25320  const char *hlp[] = {
25321         "I'm afraid I don't know how to apply that operation to that",
25322         "particular type. Continue, and I'll simply return the",
25323         "argument (shown above) as the result of the operation.",
25324         NULL };
25325  mp->selector = new_string;
25326  mp_print_op (mp, c);
25327  mp_print_known_or_unknown_type (mp, mp->cur_exp.type, cur_exp_node ());
25328  sname = mp_make_string(mp);
25329  mp->selector = old_setting;
25330  mp_snprintf (msg, 256, "Not implemented: %s", mp_str(mp, sname));
25331  delete_str_ref(sname);
25332  mp_disp_err(mp, NULL);
25333  mp_back_error (mp, msg, hlp, true);
25334@.Not implemented...@>;
25335  mp_get_x_next (mp);
25336}
25337
25338
25339
25340@ Negation is easy except when the current expression
25341is of type |independent|, or when it is a pair with one or more
25342|independent| components.
25343
25344@<Declare unary action...@>=
25345static void mp_negate_dep_list (MP mp, mp_value_node p) {
25346  (void) mp;
25347  while (1) {
25348    number_negate (dep_value (p));
25349    if (dep_info (p) == NULL)
25350      return;
25351    p = (mp_value_node) mp_link (p);
25352  }
25353}
25354
25355
25356@ It is tempting to argue that the negative of an independent variable
25357is an independent variable, hence we don't have to do anything when
25358negating it. The fallacy is that other dependent variables pointing
25359to the current expression must change the sign of their
25360coefficients if we make no change to the current expression.
25361
25362Instead, we work around the problem by copying the current expression
25363and recycling it afterwards (cf.~the |stash_in| routine).
25364
25365@d negate_value(A) if (mp_type (A) == mp_known) {
25366        set_value_number(A, (value_number (A))); /* to clear the rest */
25367        number_negate (value_number (A));
25368      } else {
25369        mp_negate_dep_list (mp, (mp_value_node) dep_list ((mp_value_node) A));
25370      }
25371
25372@<Declare unary action...@>=
25373static void negate_cur_expr(MP mp) {
25374  mp_node p, q, r;      /* for list manipulation */
25375  switch (mp->cur_exp.type) {
25376  case mp_color_type:
25377  case mp_cmykcolor_type:
25378  case mp_pair_type:
25379  case mp_independent:
25380    q = cur_exp_node ();
25381    mp_make_exp_copy (mp, q);
25382    if (mp->cur_exp.type == mp_dependent) {
25383      mp_negate_dep_list (mp, (mp_value_node) dep_list ((mp_value_node)
25384                                                        cur_exp_node ()));
25385    } else if (mp->cur_exp.type <= mp_pair_type) {
25386      /* |mp_color_type| |mp_cmykcolor_type|, or |mp_pair_type| */
25387      p = value_node (cur_exp_node ());
25388      switch (mp->cur_exp.type) {
25389      case mp_pair_type:
25390        r = x_part (p);
25391        negate_value (r);
25392        r = y_part (p);
25393        negate_value (r);
25394        break;
25395      case mp_color_type:
25396        r = red_part (p);
25397        negate_value (r);
25398        r = green_part (p);
25399        negate_value (r);
25400        r = blue_part (p);
25401        negate_value (r);
25402        break;
25403      case mp_cmykcolor_type:
25404        r = cyan_part (p);
25405        negate_value (r);
25406        r = magenta_part (p);
25407        negate_value (r);
25408        r = yellow_part (p);
25409        negate_value (r);
25410        r = black_part (p);
25411        negate_value (r);
25412        break;
25413      default:                   /* there are no other valid cases, but please the compiler */
25414        break;
25415      }
25416    }                             /* if |cur_type=mp_known| then |cur_exp=0| */
25417    mp_recycle_value (mp, q);
25418    mp_free_value_node (mp, q);
25419    break;
25420  case mp_dependent:
25421  case mp_proto_dependent:
25422    mp_negate_dep_list (mp, (mp_value_node) dep_list ((mp_value_node)
25423                                                      cur_exp_node ()));
25424    break;
25425  case mp_known:
25426    if (is_number(cur_exp_value_number()))
25427      number_negate (cur_exp_value_number());
25428    break;
25429  default:
25430    mp_bad_unary (mp, mp_minus);
25431    break;
25432  }
25433}
25434
25435@ If the current expression is a pair, but the context wants it to
25436be a path, we call |pair_to_path|.
25437
25438@<Declare unary action...@>=
25439static void mp_pair_to_path (MP mp) {
25440  set_cur_exp_knot (mp_pair_to_knot (mp));
25441  mp->cur_exp.type = mp_path_type;
25442}
25443
25444
25445
25446@ @<Declarations@>=
25447static void mp_bad_color_part (MP mp, quarterword c);
25448
25449@ @c
25450static void mp_bad_color_part (MP mp, quarterword c) {
25451  mp_node p;    /* the big node */
25452  mp_value new_expr;
25453  char msg[256];
25454  int old_setting;
25455  mp_string sname;
25456  const char *hlp[] = {
25457     "You can only ask for the redpart, greenpart, bluepart of a rgb object,",
25458     "the cyanpart, magentapart, yellowpart or blackpart of a cmyk object, ",
25459     "or the greypart of a grey object. No mixing and matching, please.",
25460     NULL };
25461  memset(&new_expr,0,sizeof(mp_value));
25462  new_number(new_expr.data.n);
25463  p = mp_link (edge_list (cur_exp_node ()));
25464  mp_disp_err(mp, NULL);
25465  old_setting = mp->selector;
25466  mp->selector = new_string;
25467  mp_print_op (mp, c);
25468  sname = mp_make_string(mp);
25469  mp->selector = old_setting;
25470@.Wrong picture color model...@>;
25471  if (mp_color_model (p) == mp_grey_model)
25472    mp_snprintf (msg, 256, "Wrong picture color model: %s of grey object", mp_str(mp, sname));
25473  else if (mp_color_model (p) == mp_cmyk_model)
25474    mp_snprintf (msg, 256, "Wrong picture color model: %s of cmyk object", mp_str(mp, sname));
25475  else if (mp_color_model (p) == mp_rgb_model)
25476    mp_snprintf (msg, 256, "Wrong picture color model: %s of rgb object", mp_str(mp, sname));
25477  else if (mp_color_model (p) == mp_no_model)
25478    mp_snprintf (msg, 256, "Wrong picture color model: %s of marking object", mp_str(mp, sname));
25479  else
25480    mp_snprintf (msg, 256, "Wrong picture color model: %s of defaulted object", mp_str(mp, sname));
25481  delete_str_ref(sname);
25482  mp_error (mp, msg, hlp, true);
25483  if (c == mp_black_part)
25484    number_clone (new_expr.data.n, unity_t);
25485  else
25486    set_number_to_zero(new_expr.data.n);
25487  mp_flush_cur_exp (mp, new_expr);
25488}
25489
25490
25491@ In the following procedure, |cur_exp| points to a capsule, which points to
25492a big node. We want to delete all but one part of the big node.
25493
25494@<Declare unary action...@>=
25495static void mp_take_part (MP mp, quarterword c) {
25496  mp_node p;    /* the big node */
25497  p = value_node (cur_exp_node ());
25498  set_value_node (mp->temp_val, p);
25499  mp_type (mp->temp_val) = mp->cur_exp.type;
25500  mp_link (p) = mp->temp_val;
25501  mp_free_value_node (mp, cur_exp_node ());
25502  switch (c) {
25503  case mp_x_part:
25504    if (mp->cur_exp.type == mp_pair_type)
25505      mp_make_exp_copy (mp, x_part (p));
25506    else
25507      mp_make_exp_copy (mp, tx_part (p));
25508    break;
25509  case mp_y_part:
25510    if (mp->cur_exp.type == mp_pair_type)
25511      mp_make_exp_copy (mp, y_part (p));
25512    else
25513      mp_make_exp_copy (mp, ty_part (p));
25514    break;
25515  case mp_xx_part:
25516    mp_make_exp_copy (mp, xx_part (p));
25517    break;
25518  case mp_xy_part:
25519    mp_make_exp_copy (mp, xy_part (p));
25520    break;
25521  case mp_yx_part:
25522    mp_make_exp_copy (mp, yx_part (p));
25523    break;
25524  case mp_yy_part:
25525    mp_make_exp_copy (mp, yy_part (p));
25526    break;
25527  case mp_red_part:
25528    mp_make_exp_copy (mp, red_part (p));
25529    break;
25530  case mp_green_part:
25531    mp_make_exp_copy (mp, green_part (p));
25532    break;
25533  case mp_blue_part:
25534    mp_make_exp_copy (mp, blue_part (p));
25535    break;
25536  case mp_cyan_part:
25537    mp_make_exp_copy (mp, cyan_part (p));
25538    break;
25539  case mp_magenta_part:
25540    mp_make_exp_copy (mp, magenta_part (p));
25541    break;
25542  case mp_yellow_part:
25543    mp_make_exp_copy (mp, yellow_part (p));
25544    break;
25545  case mp_black_part:
25546    mp_make_exp_copy (mp, black_part (p));
25547    break;
25548  }
25549  mp_recycle_value (mp, mp->temp_val);
25550}
25551
25552
25553@ @<Initialize table entries@>=
25554mp->temp_val = mp_get_value_node (mp);
25555mp_name_type (mp->temp_val) = mp_capsule;
25556
25557@ @<Free table entries@>=
25558mp_free_value_node (mp, mp->temp_val);
25559
25560
25561@ @<Declarations@>=
25562static mp_edge_header_node mp_scale_edges (MP mp, mp_number se_sf, mp_edge_header_node se_pic);
25563
25564@ @<Declare unary action...@>=
25565static void mp_take_pict_part (MP mp, quarterword c) {
25566  mp_node p;    /* first graphical object in |cur_exp| */
25567  mp_value new_expr;
25568  memset(&new_expr,0,sizeof(mp_value));
25569  new_number(new_expr.data.n);
25570  p = mp_link (edge_list (cur_exp_node ()));
25571  if (p != NULL) {
25572    switch (c) {
25573    case mp_x_part:
25574    case mp_y_part:
25575    case mp_xx_part:
25576    case mp_xy_part:
25577    case mp_yx_part:
25578    case mp_yy_part:
25579      if (mp_type (p) == mp_text_node_type) {
25580        mp_text_node p0 = (mp_text_node)p;
25581        switch (c) {
25582        case mp_x_part:
25583          number_clone(new_expr.data.n, p0->tx);
25584          break;
25585        case mp_y_part:
25586          number_clone(new_expr.data.n, p0->ty);
25587          break;
25588        case mp_xx_part:
25589          number_clone(new_expr.data.n, p0->txx);
25590          break;
25591        case mp_xy_part:
25592          number_clone(new_expr.data.n, p0->txy);
25593          break;
25594        case mp_yx_part:
25595          number_clone(new_expr.data.n, p0->tyx);
25596          break;
25597        case mp_yy_part:
25598          number_clone(new_expr.data.n, p0->tyy);
25599          break;
25600        }
25601        mp_flush_cur_exp (mp, new_expr);
25602      } else
25603        goto NOT_FOUND;
25604      break;
25605    case mp_red_part:
25606    case mp_green_part:
25607    case mp_blue_part:
25608      if (has_color (p)) {
25609        switch (c) {
25610        case mp_red_part:
25611          number_clone(new_expr.data.n,((mp_stroked_node)p)->red);
25612          break;
25613        case mp_green_part:
25614          number_clone(new_expr.data.n,((mp_stroked_node)p)->green);
25615          break;
25616        case mp_blue_part:
25617          number_clone(new_expr.data.n,((mp_stroked_node)p)->blue);
25618          break;
25619        }
25620        mp_flush_cur_exp (mp, new_expr);
25621      } else
25622        goto NOT_FOUND;
25623      break;
25624    case mp_cyan_part:
25625    case mp_magenta_part:
25626    case mp_yellow_part:
25627    case mp_black_part:
25628      if (has_color (p)) {
25629        if (mp_color_model (p) == mp_uninitialized_model && c == mp_black_part) {
25630          set_number_to_unity(new_expr.data.n);
25631        } else {
25632          switch (c) {
25633          case mp_cyan_part:
25634            number_clone(new_expr.data.n,((mp_stroked_node)p)->cyan);
25635            break;
25636          case mp_magenta_part:
25637            number_clone(new_expr.data.n,((mp_stroked_node)p)->magenta);
25638            break;
25639          case mp_yellow_part:
25640            number_clone(new_expr.data.n,((mp_stroked_node)p)->yellow);
25641            break;
25642          case mp_black_part:
25643            number_clone(new_expr.data.n,((mp_stroked_node)p)->black);
25644            break;
25645          }
25646        }
25647        mp_flush_cur_exp (mp, new_expr);
25648      } else
25649        goto NOT_FOUND;
25650      break;
25651    case mp_grey_part:
25652      if (has_color (p)) {
25653        number_clone(new_expr.data.n,((mp_stroked_node)p)->grey);
25654        mp_flush_cur_exp (mp, new_expr);
25655      } else
25656        goto NOT_FOUND;
25657      break;
25658    case mp_color_model_part:
25659      if (has_color (p)) {
25660        if (mp_color_model (p) == mp_uninitialized_model) {
25661          number_clone (new_expr.data.n, internal_value (mp_default_color_model));
25662        } else {
25663          number_clone (new_expr.data.n, unity_t);
25664          number_multiply_int (new_expr.data.n, mp_color_model (p));
25665        }
25666        mp_flush_cur_exp (mp, new_expr);
25667      } else
25668        goto NOT_FOUND;
25669      break;
25670    case mp_text_part:
25671      if (mp_type (p) != mp_text_node_type)
25672        goto NOT_FOUND;
25673      else {
25674        new_expr.data.str = mp_text_p (p);
25675        add_str_ref (new_expr.data.str);
25676        mp_flush_cur_exp (mp, new_expr);
25677        mp->cur_exp.type = mp_string_type;
25678      };
25679      break;
25680    case mp_prescript_part:
25681      if (!has_color (p)) {
25682        goto NOT_FOUND;
25683      } else {
25684        if (mp_pre_script(p)) {
25685          new_expr.data.str = mp_pre_script(p);
25686          add_str_ref (new_expr.data.str);
25687        } else {
25688          new_expr.data.str = mp_rts(mp,"");
25689        }
25690        mp_flush_cur_exp (mp, new_expr);
25691        mp->cur_exp.type = mp_string_type;
25692      };
25693      break;
25694    case mp_postscript_part:
25695      if (!has_color (p)) {
25696        goto NOT_FOUND;
25697      } else {
25698        if (mp_post_script(p)) {
25699          new_expr.data.str = mp_post_script(p);
25700          add_str_ref (new_expr.data.str);
25701        } else {
25702          new_expr.data.str = mp_rts(mp,"");
25703        }
25704        mp_flush_cur_exp (mp, new_expr);
25705        mp->cur_exp.type = mp_string_type;
25706      };
25707      break;
25708    case mp_font_part:
25709      if (mp_type (p) != mp_text_node_type)
25710        goto NOT_FOUND;
25711      else {
25712        new_expr.data.str = mp_rts (mp, mp->font_name[mp_font_n (p)]);
25713        add_str_ref (new_expr.data.str);
25714        mp_flush_cur_exp (mp, new_expr);
25715        mp->cur_exp.type = mp_string_type;
25716      };
25717      break;
25718    case mp_path_part:
25719      if (mp_type (p) == mp_text_node_type) {
25720        goto NOT_FOUND;
25721      } else if (is_stop (p)) {
25722        mp_confusion (mp, "pict");
25723      } else {
25724        new_expr.data.node = NULL;
25725        switch (mp_type (p)) {
25726        case mp_fill_node_type:
25727          new_expr.data.p = mp_copy_path (mp, mp_path_p ((mp_fill_node) p));
25728          break;
25729        case mp_stroked_node_type:
25730          new_expr.data.p = mp_copy_path (mp, mp_path_p ((mp_stroked_node) p));
25731          break;
25732        case mp_start_bounds_node_type:
25733          new_expr.data.p = mp_copy_path (mp, mp_path_p ((mp_start_bounds_node) p));
25734          break;
25735        case mp_start_clip_node_type:
25736          new_expr.data.p = mp_copy_path (mp, mp_path_p ((mp_start_clip_node) p));
25737          break;
25738        default:
25739          assert (0);
25740          break;
25741        }
25742        mp_flush_cur_exp (mp, new_expr);
25743        mp->cur_exp.type = mp_path_type;
25744      }
25745      break;
25746    case mp_pen_part:
25747      if (!has_pen (p)) {
25748        goto NOT_FOUND;
25749      } else {
25750        switch (mp_type (p)) {
25751        case mp_fill_node_type:
25752          if (mp_pen_p ((mp_fill_node) p) == NULL)
25753            goto NOT_FOUND;
25754          else {
25755            new_expr.data.p = copy_pen (mp_pen_p ((mp_fill_node) p));
25756            mp_flush_cur_exp (mp, new_expr);
25757            mp->cur_exp.type = mp_pen_type;
25758          }
25759          break;
25760        case mp_stroked_node_type:
25761          if (mp_pen_p ((mp_stroked_node) p) == NULL)
25762            goto NOT_FOUND;
25763          else {
25764            new_expr.data.p = copy_pen (mp_pen_p ((mp_stroked_node) p));
25765            mp_flush_cur_exp (mp, new_expr);
25766            mp->cur_exp.type = mp_pen_type;
25767          }
25768          break;
25769        default:
25770          assert (0);
25771          break;
25772        }
25773      }
25774      break;
25775    case mp_dash_part:
25776      if (mp_type (p) != mp_stroked_node_type) {
25777        goto NOT_FOUND;
25778      } else {
25779        if (mp_dash_p (p) == NULL) {
25780          goto NOT_FOUND;
25781        } else {
25782          add_edge_ref (mp_dash_p (p));
25783          new_expr.data.node = (mp_node)mp_scale_edges (mp, ((mp_stroked_node)p)->dash_scale,
25784                                                            (mp_edge_header_node)mp_dash_p (p));
25785          mp_flush_cur_exp (mp, new_expr);
25786          mp->cur_exp.type = mp_picture_type;
25787        }
25788      }
25789      break;
25790    }                           /* all cases have been enumerated */
25791    return;
25792  };
25793NOT_FOUND:
25794  /* Convert the current expression to a NULL value appropriate for |c| */
25795  switch (c) {
25796  case mp_text_part:
25797  case mp_font_part:
25798  case mp_prescript_part:
25799  case mp_postscript_part:
25800    new_expr.data.str = mp_rts(mp,"");
25801    mp_flush_cur_exp (mp, new_expr);
25802    mp->cur_exp.type = mp_string_type;
25803    break;
25804  case mp_path_part:
25805    new_expr.data.p = mp_new_knot (mp);
25806    mp_flush_cur_exp (mp, new_expr);
25807    mp_left_type (cur_exp_knot ()) = mp_endpoint;
25808    mp_right_type (cur_exp_knot ()) = mp_endpoint;
25809    mp_next_knot (cur_exp_knot ()) = cur_exp_knot ();
25810    set_number_to_zero(cur_exp_knot ()->x_coord);
25811    set_number_to_zero(cur_exp_knot ()->y_coord);
25812    mp_originator (cur_exp_knot ()) = mp_metapost_user;
25813    mp->cur_exp.type = mp_path_type;
25814    break;
25815  case mp_pen_part:
25816    new_expr.data.p = mp_get_pen_circle (mp, zero_t);
25817    mp_flush_cur_exp (mp, new_expr);
25818    mp->cur_exp.type = mp_pen_type;
25819    break;
25820  case mp_dash_part:
25821    new_expr.data.node = (mp_node)mp_get_edge_header_node (mp);
25822    mp_flush_cur_exp (mp, new_expr);
25823    mp_init_edges (mp, (mp_edge_header_node)cur_exp_node ());
25824    mp->cur_exp.type = mp_picture_type;
25825    break;
25826  default:
25827    set_number_to_zero(new_expr.data.n);
25828    mp_flush_cur_exp (mp, new_expr);
25829    break;
25830  }
25831}
25832
25833@ @<Declare unary action...@>=
25834static void mp_str_to_num (MP mp, quarterword c) {  /* converts a string to a number */
25835  integer n;    /* accumulator */
25836  ASCII_code m; /* current character */
25837  unsigned k;   /* index into |str_pool| */
25838  int b;        /* radix of conversion */
25839  boolean bad_char;     /* did the string contain an invalid digit? */
25840  mp_value new_expr;
25841  memset(&new_expr,0,sizeof(mp_value));
25842  new_number(new_expr.data.n);
25843  if (c == mp_ASCII_op) {
25844    if (cur_exp_str ()->len == 0)
25845      n = -1;
25846    else
25847      n = cur_exp_str ()->str[0];
25848  } else {
25849    if (c == mp_oct_op)
25850      b = 8;
25851    else
25852      b = 16;
25853    n = 0;
25854    bad_char = false;
25855    for (k = 0; k < cur_exp_str ()->len; k++) {
25856      m = (ASCII_code) (*(cur_exp_str ()->str + k));
25857      if ((m >= '0') && (m <= '9'))
25858        m = (ASCII_code) (m - '0');
25859      else if ((m >= 'A') && (m <= 'F'))
25860        m = (ASCII_code) (m - 'A' + 10);
25861      else if ((m >= 'a') && (m <= 'f'))
25862        m = (ASCII_code) (m - 'a' + 10);
25863      else {
25864        bad_char = true;
25865        m = 0;
25866      };
25867      if ((int) m >= b) {
25868        bad_char = true;
25869        m = 0;
25870      };
25871      if (n < 32768 / b)
25872        n = n * b + m;
25873      else
25874        n = 32767;
25875    }
25876    /* Give error messages if |bad_char| or |n>=4096| */
25877    if (bad_char) {
25878      const char *hlp[] = {"I zeroed out characters that weren't hex digits.", NULL};
25879      if (c == mp_oct_op) {
25880        hlp[0] = "I zeroed out characters that weren't in the range 0..7.";
25881      }
25882      mp_disp_err(mp, NULL);
25883      mp_back_error (mp, "String contains illegal digits", hlp, true);
25884      mp_get_x_next (mp);
25885    }
25886    if ((n > 4095)) { /* todo, this is scaled specific */
25887      if (number_positive (internal_value (mp_warning_check))) {
25888        char msg[256];
25889        const char *hlp[] = {
25890               "I have trouble with numbers greater than 4095; watch out.",
25891               "(Set warningcheck:=0 to suppress this message.)",
25892               NULL };
25893        mp_snprintf (msg, 256,"Number too large (%d)", (int)n);
25894        mp_back_error (mp, msg, hlp, true);
25895        mp_get_x_next (mp);
25896      }
25897    }
25898  }
25899  number_clone (new_expr.data.n, unity_t);
25900  number_multiply_int(new_expr.data.n, n);
25901  mp_flush_cur_exp (mp, new_expr);
25902}
25903
25904@ @<Declare unary action...@>=
25905static void mp_path_length (MP mp, mp_number *n) {                               /* computes the length of the current path */
25906  mp_knot p;    /* traverser */
25907  set_number_to_zero (*n);
25908  p = cur_exp_knot ();
25909  if (mp_left_type (p) == mp_endpoint) {
25910    number_substract(*n, unity_t); /* -unity */
25911  }
25912  do {
25913    p = mp_next_knot (p);
25914    number_add(*n, unity_t);
25915  } while (p != cur_exp_knot ());
25916}
25917
25918
25919@ @<Declare unary action...@>=
25920static void mp_pict_length (MP mp, mp_number *n) {
25921  /* counts interior components in picture |cur_exp| */
25922  mp_node p;    /* traverser */
25923  set_number_to_zero (*n);
25924  p = mp_link (edge_list (cur_exp_node ()));
25925  if (p != NULL) {
25926    if (is_start_or_stop (p))
25927      if (mp_skip_1component (mp, p) == NULL)
25928        p = mp_link (p);
25929    while (p != NULL) {
25930      if ( ! is_start_or_stop(p) )
25931        p = mp_link(p);
25932      else if ( ! is_stop(p))
25933        p = mp_skip_1component(mp, p);
25934      else
25935        return;
25936      number_add(*n, unity_t);
25937    }
25938  }
25939}
25940
25941
25942@ The function |an_angle| returns the value of the |angle| primitive, or $0$ if the
25943argument is |origin|.
25944
25945@<Declare unary action...@>=
25946static void mp_an_angle (MP mp, mp_number *ret, mp_number xpar, mp_number ypar) {
25947  set_number_to_zero (*ret);
25948  if ((!(number_zero(xpar) && number_zero(ypar)))) {
25949    n_arg (*ret, xpar, ypar);
25950  }
25951}
25952
25953
25954@ The actual turning number is (for the moment) computed in a C function
25955that receives eight integers corresponding to the four controlling points,
25956and returns a single angle.  Besides those, we have to account for discrete
25957moves at the actual points.
25958
25959@d mp_floor(a) ((a)>=0 ? (int)(a) : -(int)(-(a)))
25960@d bezier_error (720*(256*256*16))+1
25961@d mp_sign(v) ((v)>0 ? 1 : ((v)<0 ? -1 : 0 ))
25962@d mp_out(A) (double)((A)/16)
25963
25964@<Declare unary action...@>=
25965static void mp_bezier_slope (MP mp, mp_number *ret, mp_number AX, mp_number AY, mp_number BX,
25966                              mp_number BY, mp_number CX, mp_number CY, mp_number DX,
25967                              mp_number DY);
25968
25969@ @c
25970static void mp_bezier_slope (MP mp, mp_number *ret, mp_number AX, mp_number AY, mp_number BX,
25971                              mp_number BY, mp_number CX, mp_number CY, mp_number DX,
25972                              mp_number DY) {
25973  double a, b, c;
25974  mp_number deltax, deltay;
25975  double ax, ay, bx, by, cx, cy, dx, dy;
25976  mp_number xi, xo, xm;
25977  double res = 0;
25978  ax = number_to_double (AX);
25979  ay = number_to_double (AY);
25980  bx = number_to_double (BX);
25981  by = number_to_double (BY);
25982  cx = number_to_double (CX);
25983  cy = number_to_double (CY);
25984  dx = number_to_double (DX);
25985  dy = number_to_double (DY);
25986  new_number (deltax);
25987  new_number (deltay);
25988  set_number_from_substraction(deltax, BX, AX);
25989  set_number_from_substraction(deltay, BY, AY);
25990  if (number_zero(deltax) && number_zero(deltay)) {
25991    set_number_from_substraction(deltax, CX, AX);
25992    set_number_from_substraction(deltay, CY, AY);
25993  }
25994  if (number_zero(deltax) && number_zero(deltay)) {
25995    set_number_from_substraction(deltax, DX, AX);
25996    set_number_from_substraction(deltay, DY, AY);
25997  }
25998  new_number (xi);
25999  new_number (xm);
26000  new_number (xo);
26001  mp_an_angle (mp, &xi, deltax, deltay);
26002  set_number_from_substraction(deltax, CX, BX);
26003  set_number_from_substraction(deltay, CY, BY);
26004  mp_an_angle (mp, &xm, deltax, deltay); /* !!! never used? */
26005  set_number_from_substraction(deltax, DX, CX);
26006  set_number_from_substraction(deltay, DY, CY);
26007  if (number_zero(deltax) && number_zero(deltay)) {
26008    set_number_from_substraction(deltax, DX, BX);
26009    set_number_from_substraction(deltay, DY, BY);
26010  }
26011  if (number_zero(deltax) && number_zero(deltay)) {
26012    set_number_from_substraction(deltax, DX, AX);
26013    set_number_from_substraction(deltay, DY, AY);
26014  }
26015  mp_an_angle (mp, &xo, deltax, deltay);
26016  a = (bx - ax) * (cy - by) - (cx - bx) * (by - ay);    /* a = (bp-ap)x(cp-bp); */
26017  b = (bx - ax) * (dy - cy) - (by - ay) * (dx - cx);;   /* b = (bp-ap)x(dp-cp); */
26018  c = (cx - bx) * (dy - cy) - (dx - cx) * (cy - by);    /* c = (cp-bp)x(dp-cp); */
26019  if ((a == 0) && (c == 0)) {
26020    res = (b == 0 ? 0 : (mp_out (number_to_double(xo)) - mp_out (number_to_double(xi))));
26021  } else if ((a == 0) || (c == 0)) {
26022    if ((mp_sign (b) == mp_sign (a)) || (mp_sign (b) == mp_sign (c))) {
26023      res = mp_out (number_to_double(xo)) - mp_out (number_to_double(xi));  /* ? */
26024      if (res < -180.0)
26025        res += 360.0;
26026      else if (res > 180.0)
26027        res -= 360.0;
26028    } else {
26029      res = mp_out (number_to_double(xo)) - mp_out (number_to_double(xi));  /* ? */
26030    }
26031  } else if ((mp_sign (a) * mp_sign (c)) < 0) {
26032    res = mp_out (number_to_double(xo)) - mp_out (number_to_double(xi));    /* ? */
26033    if (res < -180.0)
26034      res += 360.0;
26035    else if (res > 180.0)
26036      res -= 360.0;
26037  } else {
26038    if (mp_sign (a) == mp_sign (b)) {
26039      res = mp_out (number_to_double(xo)) - mp_out (number_to_double(xi));  /* ? */
26040      if (res < -180.0)
26041        res += 360.0;
26042      else if (res > 180.0)
26043        res -= 360.0;
26044    } else {
26045      if ((b * b) == (4 * a * c)) {
26046        res = (double) bezier_error;
26047      } else if ((b * b) < (4 * a * c)) {
26048        res = mp_out (number_to_double(xo)) - mp_out (number_to_double(xi));        /* ? */
26049        if (res <= 0.0 && res > -180.0)
26050          res += 360.0;
26051        else if (res >= 0.0 && res < 180.0)
26052          res -= 360.0;
26053      } else {
26054        res = mp_out (number_to_double(xo)) - mp_out (number_to_double(xi));
26055        if (res < -180.0)
26056          res += 360.0;
26057        else if (res > 180.0)
26058          res -= 360.0;
26059      }
26060    }
26061  }
26062  free_number (deltax);
26063  free_number (deltay);
26064  free_number (xi);
26065  free_number (xo);
26066  free_number (xm);
26067  set_number_from_double(*ret, res);
26068  convert_scaled_to_angle (*ret);
26069}
26070
26071
26072@
26073@d p_nextnext mp_next_knot(mp_next_knot(p))
26074@d p_next mp_next_knot(p)
26075
26076@<Declare unary action...@>=
26077static void mp_turn_cycles (MP mp, mp_number *turns, mp_knot c) {
26078  mp_angle res, ang;       /*  the angles of intermediate results  */
26079  mp_knot p;    /*  for running around the path  */
26080  mp_number xp, yp;       /*  coordinates of next point  */
26081  mp_number x, y; /*  helper coordinates  */
26082  mp_number arg1, arg2;
26083  mp_angle in_angle, out_angle;    /*  helper angles */
26084  mp_angle seven_twenty_deg_t, neg_one_eighty_deg_t;
26085  unsigned old_setting; /* saved |selector| setting */
26086  set_number_to_zero(*turns);
26087  new_number(arg1);
26088  new_number(arg2);
26089  new_number(xp);
26090  new_number(yp);
26091  new_number(x);
26092  new_number(y);
26093  new_angle(in_angle);
26094  new_angle(out_angle);
26095  new_angle(ang);
26096  new_angle(res);
26097  new_angle(seven_twenty_deg_t);
26098  new_angle(neg_one_eighty_deg_t);
26099  number_clone(seven_twenty_deg_t, three_sixty_deg_t);
26100  number_double(seven_twenty_deg_t);
26101  number_clone(neg_one_eighty_deg_t, one_eighty_deg_t);
26102  number_negate(neg_one_eighty_deg_t);
26103  p = c;
26104  old_setting = mp->selector;
26105  mp->selector = term_only;
26106  if (number_greater (internal_value (mp_tracing_commands), unity_t)) {
26107    mp_begin_diagnostic (mp);
26108    mp_print_nl (mp, "");
26109    mp_end_diagnostic (mp, false);
26110  }
26111  do {
26112    number_clone (xp, p_next->x_coord);
26113    number_clone (yp, p_next->y_coord);
26114    mp_bezier_slope (mp, &ang, p->x_coord,  p->y_coord, p->right_x, p->right_y,
26115                         p_next->left_x, p_next->left_y, xp, yp);
26116    if (number_greater(ang, seven_twenty_deg_t)) {
26117      mp_error (mp, "Strange path", NULL, true);
26118      mp->selector = old_setting;
26119      set_number_to_zero(*turns);
26120      goto DONE;
26121    }
26122    number_add(res, ang);
26123    if (number_greater(res, one_eighty_deg_t)) {
26124      number_substract(res, three_sixty_deg_t);
26125      number_add(*turns, unity_t);
26126    }
26127    if (number_lessequal(res, neg_one_eighty_deg_t)) {
26128      number_add(res, three_sixty_deg_t);
26129      number_substract(*turns, unity_t);
26130    }
26131    /*  incoming angle at next point  */
26132    number_clone (x, p_next->left_x);
26133    number_clone (y, p_next->left_y);
26134    if (number_equal(xp, x) && number_equal(yp, y)) {
26135      number_clone (x, p->right_x);
26136      number_clone (y, p->right_y);
26137    }
26138    if (number_equal(xp, x) && number_equal(yp, y)) {
26139      number_clone (x, p->x_coord);
26140      number_clone (y, p->y_coord);
26141    }
26142    set_number_from_substraction(arg1, xp, x);
26143    set_number_from_substraction(arg2, yp, y);
26144    mp_an_angle (mp, &in_angle, arg1, arg2);
26145    /*  outgoing angle at next point  */
26146    number_clone (x, p_next->right_x);
26147    number_clone (y, p_next->right_y);
26148    if (number_equal(xp, x) && number_equal(yp, y)) {
26149      number_clone (x, p_nextnext->left_x);
26150      number_clone (y, p_nextnext->left_y);
26151    }
26152    if (number_equal(xp, x) && number_equal(yp, y)) {
26153      number_clone (x, p_nextnext->x_coord);
26154      number_clone (y, p_nextnext->y_coord);
26155    }
26156    set_number_from_substraction(arg1, x, xp);
26157    set_number_from_substraction(arg2, y, yp);
26158    mp_an_angle (mp, &out_angle, arg1, arg2);
26159    set_number_from_substraction(ang, out_angle, in_angle);
26160    mp_reduce_angle (mp, &ang);
26161    if (number_nonzero(ang)) {
26162      number_add(res, ang);
26163      if (number_greaterequal(res, one_eighty_deg_t)) {
26164        number_substract(res, three_sixty_deg_t);
26165        number_add(*turns, unity_t);
26166      }
26167      if (number_lessequal(res, neg_one_eighty_deg_t)) {
26168        number_add(res, three_sixty_deg_t);
26169        number_substract(*turns, unity_t);
26170      }
26171    }
26172    p = mp_next_knot (p);
26173  } while (p != c);
26174  mp->selector = old_setting;
26175DONE:
26176  free_number(xp);
26177  free_number(yp);
26178  free_number(x);
26179  free_number(y);
26180  free_number(seven_twenty_deg_t);
26181  free_number(neg_one_eighty_deg_t);
26182  free_number(in_angle);
26183  free_number(out_angle);
26184  free_number(ang);
26185  free_number(res);
26186  free_number(arg1);
26187  free_number(arg2);
26188}
26189
26190@ @<Declare unary action...@>=
26191static void mp_turn_cycles_wrapper (MP mp, mp_number *ret, mp_knot c) {
26192  if (mp_next_knot (c) == c) {
26193    /* one-knot paths always have a turning number of 1 */
26194    set_number_to_unity(*ret);
26195  } else {
26196    mp_turn_cycles (mp, ret, c);
26197  }
26198}
26199
26200@ @<Declare unary action procedures@>=
26201static void mp_test_known (MP mp, quarterword c) {
26202  int b;        /* is the current expression known? */
26203  mp_node p;    /* location in a big node */
26204  mp_value new_expr;
26205  memset(&new_expr,0,sizeof(mp_value));
26206  new_number(new_expr.data.n);
26207  b = mp_false_code;
26208  switch (mp->cur_exp.type) {
26209  case mp_vacuous:
26210  case mp_boolean_type:
26211  case mp_string_type:
26212  case mp_pen_type:
26213  case mp_path_type:
26214  case mp_picture_type:
26215  case mp_known:
26216    b = mp_true_code;
26217    break;
26218  case mp_transform_type:
26219    p = value_node (cur_exp_node ());
26220    if (mp_type (tx_part (p)) != mp_known)
26221      break;
26222    if (mp_type (ty_part (p)) != mp_known)
26223      break;
26224    if (mp_type (xx_part (p)) != mp_known)
26225      break;
26226    if (mp_type (xy_part (p)) != mp_known)
26227      break;
26228    if (mp_type (yx_part (p)) != mp_known)
26229      break;
26230    if (mp_type (yy_part (p)) != mp_known)
26231      break;
26232    b = mp_true_code;
26233    break;
26234  case mp_color_type:
26235    p = value_node (cur_exp_node ());
26236    if (mp_type (red_part (p)) != mp_known)
26237      break;
26238    if (mp_type (green_part (p)) != mp_known)
26239      break;
26240    if (mp_type (blue_part (p)) != mp_known)
26241      break;
26242    b = mp_true_code;
26243    break;
26244  case mp_cmykcolor_type:
26245    p = value_node (cur_exp_node ());
26246    if (mp_type (cyan_part (p)) != mp_known)
26247      break;
26248    if (mp_type (magenta_part (p)) != mp_known)
26249      break;
26250    if (mp_type (yellow_part (p)) != mp_known)
26251      break;
26252    if (mp_type (black_part (p)) != mp_known)
26253      break;
26254    b = mp_true_code;
26255    break;
26256  case mp_pair_type:
26257    p = value_node (cur_exp_node ());
26258    if (mp_type (x_part (p)) != mp_known)
26259      break;
26260    if (mp_type (y_part (p)) != mp_known)
26261      break;
26262    b = mp_true_code;
26263    break;
26264  default:
26265    break;
26266  }
26267  if (c == mp_known_op) {
26268    set_number_from_boolean (new_expr.data.n, b);
26269  } else {
26270    if (b==mp_true_code) {
26271      set_number_from_boolean (new_expr.data.n, mp_false_code);
26272    } else {
26273      set_number_from_boolean (new_expr.data.n, mp_true_code);
26274    }
26275  }
26276  mp_flush_cur_exp (mp, new_expr);
26277  cur_exp_node() = NULL; /* !! do not replace with |set_cur_exp_node()| !! */
26278  mp->cur_exp.type = mp_boolean_type;
26279}
26280
26281@ The |pair_value| routine changes the current expression to a
26282given ordered pair of values.
26283
26284@<Declare unary action procedures@>=
26285static void mp_pair_value (MP mp, mp_number x, mp_number y) {
26286  mp_node p;    /* a pair node */
26287  mp_value new_expr;
26288  mp_number x1, y1;
26289  new_number(x1);
26290  new_number(y1);
26291  number_clone (x1, x);
26292  number_clone (y1, y);
26293  memset(&new_expr,0,sizeof(mp_value));
26294  new_number(new_expr.data.n);
26295  p = mp_get_value_node (mp);
26296  new_expr.type = mp_type (p);
26297  new_expr.data.node = p;
26298  mp_flush_cur_exp (mp, new_expr);
26299  mp->cur_exp.type = mp_pair_type;
26300  mp_name_type (p) = mp_capsule;
26301  mp_init_pair_node (mp, p);
26302  p = value_node (p);
26303  mp_type (x_part (p)) = mp_known;
26304  set_value_number (x_part (p), x1);
26305  mp_type (y_part (p)) = mp_known;
26306  set_value_number (y_part (p), y1);
26307  free_number(x1);
26308  free_number(y1);
26309}
26310
26311
26312@ Here is a function that sets |minx|, |maxx|, |miny|, |maxy| to the bounding
26313box of the current expression.  The boolean result is |false| if the expression
26314has the wrong type.
26315
26316@<Declare unary action procedures@>=
26317static boolean mp_get_cur_bbox (MP mp) {
26318  switch (mp->cur_exp.type) {
26319  case mp_picture_type:
26320  {
26321    mp_edge_header_node p0 = (mp_edge_header_node)cur_exp_node ();
26322    mp_set_bbox (mp, p0, true);
26323    if (number_greater(p0->minx, p0->maxx)) {
26324      set_number_to_zero(mp_minx);
26325      set_number_to_zero(mp_maxx);
26326      set_number_to_zero(mp_miny);
26327      set_number_to_zero(mp_maxy);
26328    } else {
26329      number_clone (mp_minx, p0->minx);
26330      number_clone (mp_maxx, p0->maxx);
26331      number_clone (mp_miny, p0->miny);
26332      number_clone (mp_maxy, p0->maxy);
26333    }
26334  }
26335    break;
26336  case mp_path_type:
26337    mp_path_bbox (mp, cur_exp_knot ());
26338    break;
26339  case mp_pen_type:
26340    mp_pen_bbox (mp, cur_exp_knot ());
26341    break;
26342  default:
26343    return false;
26344  }
26345  return true;
26346}
26347
26348
26349@ Here is a routine that interprets |cur_exp| as a file name and tries to read
26350a line from the file or to close the file.
26351
26352@<Declare unary action procedures@>=
26353static void mp_do_read_or_close (MP mp, quarterword c) {
26354  mp_value new_expr;
26355  readf_index n, n0;    /* indices for searching |rd_fname| */
26356  memset(&new_expr,0,sizeof(mp_value));
26357  new_number(new_expr.data.n);
26358  /* Find the |n| where |rd_fname[n]=cur_exp|; if |cur_exp| must be inserted,
26359     call |start_read_input| and |goto found| or |not_found| */
26360  /* Free slots in the |rd_file| and |rd_fname| arrays are marked with NULL's in
26361     |rd_fname|. */
26362  {
26363    char *fn;
26364    n = mp->read_files;
26365    n0 = mp->read_files;
26366    fn = mp_xstrdup (mp, mp_str (mp, cur_exp_str ()));
26367    while (mp_xstrcmp (fn, mp->rd_fname[n]) != 0) {
26368      if (n > 0) {
26369        decr (n);
26370      } else if (c == mp_close_from_op) {
26371        goto CLOSE_FILE;
26372      } else {
26373        if (n0 == mp->read_files) {
26374          if (mp->read_files < mp->max_read_files) {
26375            incr (mp->read_files);
26376          } else {
26377            void **rd_file;
26378            char **rd_fname;
26379            readf_index l, k;
26380            l = mp->max_read_files + (mp->max_read_files / 4);
26381            rd_file = xmalloc ((l + 1), sizeof (void *));
26382            rd_fname = xmalloc ((l + 1), sizeof (char *));
26383            for (k = 0; k <= l; k++) {
26384              if (k <= mp->max_read_files) {
26385                rd_file[k] = mp->rd_file[k];
26386                rd_fname[k] = mp->rd_fname[k];
26387              } else {
26388                rd_file[k] = 0;
26389                rd_fname[k] = NULL;
26390              }
26391            }
26392            xfree (mp->rd_file);
26393            xfree (mp->rd_fname);
26394            mp->max_read_files = l;
26395            mp->rd_file = rd_file;
26396            mp->rd_fname = rd_fname;
26397          }
26398        }
26399        n = n0;
26400        if (mp_start_read_input (mp, fn, n))
26401          goto FOUND;
26402        else
26403          goto NOT_FOUND;
26404      }
26405      if (mp->rd_fname[n] == NULL) {
26406        n0 = n;
26407      }
26408    }
26409    if (c == mp_close_from_op) {
26410      (mp->close_file) (mp, mp->rd_file[n]);
26411      goto NOT_FOUND;
26412    }
26413  }
26414  mp_begin_file_reading (mp);
26415  name = is_read;
26416  if (mp_input_ln (mp, mp->rd_file[n]))
26417    goto FOUND;
26418  mp_end_file_reading (mp);
26419NOT_FOUND:
26420  /* Record the end of file and set |cur_exp| to a dummy value  */
26421  xfree (mp->rd_fname[n]);
26422  mp->rd_fname[n] = NULL;
26423  if (n == mp->read_files - 1)
26424    mp->read_files = n;
26425  if (c == mp_close_from_op)
26426    goto CLOSE_FILE;
26427  new_expr.data.str = mp->eof_line;
26428  add_str_ref (new_expr.data.str);
26429  mp_flush_cur_exp (mp, new_expr);
26430  mp->cur_exp.type = mp_string_type;
26431  return;
26432CLOSE_FILE:
26433  mp_flush_cur_exp (mp, new_expr);
26434  mp->cur_exp.type = mp_vacuous;
26435  return;
26436FOUND:
26437  mp_flush_cur_exp (mp, new_expr);
26438  mp_finish_read (mp);
26439}
26440
26441@ The string denoting end-of-file is a one-byte string at position zero, by definition.
26442I have to cheat a little here because
26443
26444@<Glob...@>=
26445mp_string eof_line;
26446
26447@ @<Set init...@>=
26448mp->eof_line = mp_rtsl (mp, "\0", 1);
26449mp->eof_line->refs = MAX_STR_REF;
26450
26451@ Finally, we have the operations that combine a capsule~|p|
26452with the current expression.
26453
26454Several of the binary operations are potentially complicated by the
26455fact that |independent| values can sneak into capsules. For example,
26456we've seen an instance of this difficulty in the unary operation
26457of negation. In order to reduce the number of cases that need to be
26458handled, we first change the two operands (if necessary)
26459to rid them of |independent| components. The original operands are
26460put into capsules called |old_p| and |old_exp|, which will be
26461recycled after the binary operation has been safely carried out.
26462
26463@d binary_return  { mp_finish_binary(mp, old_p, old_exp); return; }
26464
26465@c
26466@<Declare binary action procedures@>;
26467static void mp_finish_binary (MP mp, mp_node old_p, mp_node old_exp) {
26468  check_arith();
26469  /* Recycle any sidestepped |independent| capsules */
26470  if (old_p != NULL) {
26471    mp_recycle_value (mp, old_p);
26472    mp_free_value_node (mp, old_p);
26473  }
26474  if (old_exp != NULL) {
26475    mp_recycle_value (mp, old_exp);
26476    mp_free_value_node (mp, old_exp);
26477  }
26478}
26479static void mp_do_binary (MP mp, mp_node p, integer c) {
26480  mp_node q, r, rr;     /* for list manipulation */
26481  mp_node old_p, old_exp;       /* capsules to recycle */
26482  mp_value new_expr;
26483  check_arith();
26484  if (number_greater (internal_value (mp_tracing_commands), two_t)) {
26485    /* Trace the current binary operation */
26486    mp_begin_diagnostic (mp);
26487    mp_print_nl (mp, "{(");
26488    mp_print_exp (mp, p, 0);      /* show the operand, but not verbosely */
26489    mp_print_char (mp, xord (')'));
26490    mp_print_op (mp, (quarterword) c);
26491    mp_print_char (mp, xord ('('));
26492    mp_print_exp (mp, NULL, 0);
26493    mp_print (mp, ")}");
26494    mp_end_diagnostic (mp, false);
26495  }
26496  /* Sidestep |independent| cases in capsule |p| */
26497  /* A big node is considered to be ``tarnished'' if it contains at least one
26498     independent component. We will define a simple function called `|tarnished|'
26499     that returns |NULL| if and only if its argument is not tarnished. */
26500  switch (mp_type (p)) {
26501  case mp_transform_type:
26502  case mp_color_type:
26503  case mp_cmykcolor_type:
26504  case mp_pair_type:
26505    old_p = mp_tarnished (mp, p);
26506    break;
26507  case mp_independent:
26508    old_p = MP_VOID;
26509    break;
26510  default:
26511    old_p = NULL;
26512    break;
26513  }
26514  if (old_p != NULL) {
26515    q = mp_stash_cur_exp (mp);
26516    old_p = p;
26517    mp_make_exp_copy (mp, old_p);
26518    p = mp_stash_cur_exp (mp);
26519    mp_unstash_cur_exp (mp, q);
26520  }
26521
26522  /* Sidestep |independent| cases in the current expression */
26523  switch (mp->cur_exp.type) {
26524  case mp_transform_type:
26525  case mp_color_type:
26526  case mp_cmykcolor_type:
26527  case mp_pair_type:
26528    old_exp = mp_tarnished (mp, cur_exp_node ());
26529    break;
26530  case mp_independent:
26531    old_exp = MP_VOID;
26532    break;
26533  default:
26534    old_exp = NULL;
26535    break;
26536  }
26537  if (old_exp != NULL) {
26538    old_exp = cur_exp_node ();
26539    mp_make_exp_copy (mp, old_exp);
26540  }
26541
26542  switch (c) {
26543  case mp_plus:
26544  case mp_minus:
26545    /* Add or subtract the current expression from |p| */
26546    if ((mp->cur_exp.type < mp_color_type) || (mp_type (p) < mp_color_type)) {
26547      mp_bad_binary (mp, p, (quarterword) c);
26548    } else {
26549      quarterword cc = (quarterword)c;
26550      if ((mp->cur_exp.type > mp_pair_type) && (mp_type (p) > mp_pair_type)) {
26551        mp_add_or_subtract (mp, p, NULL, cc);
26552      } else {
26553        if (mp->cur_exp.type != mp_type (p)) {
26554          mp_bad_binary (mp, p, cc);
26555        } else {
26556          q = value_node (p);
26557          r = value_node (cur_exp_node ());
26558          switch (mp->cur_exp.type) {
26559          case mp_pair_type:
26560            mp_add_or_subtract (mp, x_part (q), x_part (r),cc);
26561            mp_add_or_subtract (mp, y_part (q), y_part (r),cc);
26562            break;
26563          case mp_color_type:
26564            mp_add_or_subtract (mp, red_part (q), red_part (r),cc);
26565            mp_add_or_subtract (mp, green_part (q), green_part (r),cc);
26566            mp_add_or_subtract (mp, blue_part (q), blue_part (r),cc);
26567            break;
26568          case mp_cmykcolor_type:
26569            mp_add_or_subtract (mp, cyan_part (q), cyan_part (r),cc);
26570            mp_add_or_subtract (mp, magenta_part (q), magenta_part (r),cc);
26571            mp_add_or_subtract (mp, yellow_part (q), yellow_part (r),cc);
26572            mp_add_or_subtract (mp, black_part (q), black_part (r),cc);
26573            break;
26574          case mp_transform_type:
26575            mp_add_or_subtract (mp, tx_part (q), tx_part (r),cc);
26576            mp_add_or_subtract (mp, ty_part (q), ty_part (r),cc);
26577            mp_add_or_subtract (mp, xx_part (q), xx_part (r),cc);
26578            mp_add_or_subtract (mp, xy_part (q), xy_part (r),cc);
26579            mp_add_or_subtract (mp, yx_part (q), yx_part (r),cc);
26580            mp_add_or_subtract (mp, yy_part (q), yy_part (r),cc);
26581            break;
26582          default:                 /* there are no other valid cases, but please the compiler */
26583            break;
26584          }
26585        }
26586      }
26587    }
26588    break;
26589  case mp_less_than:
26590  case mp_less_or_equal:
26591  case mp_greater_than:
26592  case mp_greater_or_equal:
26593  case mp_equal_to:
26594  case mp_unequal_to:
26595    check_arith();                    /* at this point |arith_error| should be |false|? */
26596    if ((mp->cur_exp.type > mp_pair_type) && (mp_type (p) > mp_pair_type)) {
26597      mp_add_or_subtract (mp, p, NULL, mp_minus);      /* |cur_exp:=(p)-cur_exp| */
26598    } else if (mp->cur_exp.type != mp_type (p)) {
26599      mp_bad_binary (mp, p, (quarterword) c);
26600      goto DONE;
26601    } else if (mp->cur_exp.type == mp_string_type) {
26602      memset(&new_expr,0,sizeof(mp_value));
26603      new_number(new_expr.data.n);
26604      set_number_from_scaled (new_expr.data.n, mp_str_vs_str (mp, value_str (p), cur_exp_str ()));
26605      mp_flush_cur_exp (mp, new_expr);
26606    } else if ((mp->cur_exp.type == mp_unknown_string) ||
26607               (mp->cur_exp.type == mp_unknown_boolean)) {
26608      /* Check if unknowns have been equated */
26609      /* When two unknown strings are in the same ring, we know that they are
26610         equal. Otherwise, we don't know whether they are equal or not, so we
26611         make no change. */
26612      q = value_node (cur_exp_node ());
26613      while ((q != cur_exp_node ()) && (q != p))
26614        q = value_node (q);
26615      if (q == p) {
26616        memset(&new_expr,0,sizeof(mp_value));
26617        new_number(new_expr.data.n);
26618        set_cur_exp_node (NULL);
26619        mp_flush_cur_exp (mp, new_expr);
26620      }
26621
26622    } else if ((mp->cur_exp.type <= mp_pair_type)
26623               && (mp->cur_exp.type >= mp_transform_type)) {
26624      /* Reduce comparison of big nodes to comparison of scalars */
26625      /* In the following, the |while| loops exist just so that |break| can be used,
26626         each loop runs exactly once. */
26627      quarterword part_type;
26628      q = value_node (p);
26629      r = value_node (cur_exp_node ());
26630      part_type = 0;
26631      switch (mp->cur_exp.type) {
26632      case mp_pair_type:
26633        while (part_type==0) {
26634          rr = x_part (r);
26635          part_type = mp_x_part;
26636          mp_add_or_subtract (mp, x_part (q), rr, mp_minus);
26637          if (mp_type (rr) != mp_known || ! number_zero(value_number (rr)))
26638            break;
26639          rr = y_part (r);
26640          part_type = mp_y_part;
26641          mp_add_or_subtract (mp, y_part (q), rr, mp_minus);
26642          if (mp_type (rr) != mp_known || !number_zero(value_number (rr)))
26643            break;
26644        }
26645        mp_take_part (mp, part_type);
26646        break;
26647      case mp_color_type:
26648        while (part_type==0) {
26649          rr = red_part (r);
26650          part_type = mp_red_part;
26651          mp_add_or_subtract (mp, red_part (q), rr, mp_minus);
26652          if (mp_type (rr) != mp_known || ! number_zero(value_number (rr)))
26653            break;
26654          rr = green_part (r);
26655          part_type = mp_green_part;
26656          mp_add_or_subtract (mp, green_part (q), rr, mp_minus);
26657          if (mp_type (rr) != mp_known || !number_zero(value_number (rr)))
26658            break;
26659          rr = blue_part (r);
26660          part_type = mp_blue_part;
26661          mp_add_or_subtract (mp, blue_part (q), rr, mp_minus);
26662          if (mp_type (rr) != mp_known || !number_zero(value_number (rr)))
26663            break;
26664        }
26665        mp_take_part (mp, part_type);
26666        break;
26667      case mp_cmykcolor_type:
26668        while (part_type==0) {
26669          rr = cyan_part (r);
26670          part_type = mp_cyan_part;
26671          mp_add_or_subtract (mp, cyan_part (q), rr, mp_minus);
26672          if (mp_type (rr) != mp_known || !number_zero(value_number (rr)))
26673            break;
26674          rr = magenta_part (r);
26675          part_type = mp_magenta_part;
26676          mp_add_or_subtract (mp, magenta_part (q), rr, mp_minus);
26677          if (mp_type (rr) != mp_known || !number_zero(value_number (rr)))
26678            break;
26679          rr = yellow_part (r);
26680          part_type = mp_yellow_part;
26681          mp_add_or_subtract (mp, yellow_part (q), rr, mp_minus);
26682          if (mp_type (rr) != mp_known || !number_zero(value_number (rr)))
26683            break;
26684          rr = black_part (r);
26685          part_type = mp_black_part;
26686          mp_add_or_subtract (mp, black_part (q), rr, mp_minus);
26687          if (mp_type (rr) != mp_known || !number_zero(value_number (rr)))
26688            break;
26689        }
26690        mp_take_part (mp, part_type);
26691        break;
26692      case mp_transform_type:
26693        while (part_type==0) {
26694          rr = tx_part (r);
26695          part_type = mp_x_part;
26696          mp_add_or_subtract (mp, tx_part (q), rr, mp_minus);
26697          if (mp_type (rr) != mp_known || !number_zero(value_number (rr)))
26698            break;
26699          rr = ty_part (r);
26700          part_type = mp_y_part;
26701          mp_add_or_subtract (mp, ty_part (q), rr, mp_minus);
26702          if (mp_type (rr) != mp_known || !number_zero(value_number (rr)))
26703            break;
26704          rr = xx_part (r);
26705          part_type = mp_xx_part;
26706          mp_add_or_subtract (mp, xx_part (q), rr, mp_minus);
26707          if (mp_type (rr) != mp_known || !number_zero(value_number (rr)))
26708            break;
26709          rr = xy_part (r);
26710          part_type = mp_xy_part;
26711          mp_add_or_subtract (mp, xy_part (q), rr, mp_minus);
26712          if (mp_type (rr) != mp_known || !number_zero(value_number (rr)))
26713            break;
26714          rr = yx_part (r);
26715          part_type = mp_yx_part;
26716          mp_add_or_subtract (mp, yx_part (q), rr, mp_minus);
26717          if (mp_type (rr) != mp_known || !number_zero(value_number (rr)))
26718            break;
26719          rr = yy_part (r);
26720          part_type = mp_yy_part;
26721          mp_add_or_subtract (mp, yy_part (q), rr, mp_minus);
26722          if (mp_type (rr) != mp_known || !number_zero(value_number (rr)))
26723            break;
26724        }
26725        mp_take_part (mp, part_type);
26726        break;
26727      default:
26728        assert (0);                 /* todo: |mp->cur_exp.type>mp_transform_node_type| ? */
26729        break;
26730      }
26731
26732    } else if (mp->cur_exp.type == mp_boolean_type) {
26733      memset(&new_expr,0,sizeof(mp_value));
26734      new_number(new_expr.data.n);
26735      set_number_from_boolean (new_expr.data.n, number_to_scaled(cur_exp_value_number ()) -
26736                                                number_to_scaled (value_number (p)));
26737      mp_flush_cur_exp (mp, new_expr);
26738    } else {
26739      mp_bad_binary (mp, p, (quarterword) c);
26740      goto DONE;
26741    }
26742    /* Compare the current expression with zero */
26743    if (mp->cur_exp.type != mp_known) {
26744      const char *hlp[] = {
26745          "Oh dear. I can\'t decide if the expression above is positive,",
26746          "negative, or zero. So this comparison test won't be `true'.",
26747          NULL  };
26748      if (mp->cur_exp.type < mp_known) {
26749        mp_disp_err (mp, p);
26750        hlp[0]  = "The quantities shown above have not been equated.";
26751        hlp[1]  = NULL;
26752      }
26753      mp_disp_err(mp, NULL);
26754      memset(&new_expr,0,sizeof(mp_value));
26755      new_number(new_expr.data.n);
26756      set_number_from_boolean (new_expr.data.n, mp_false_code);
26757      mp_back_error (mp,"Unknown relation will be considered false", hlp, true);
26758    @.Unknown relation...@>;
26759      mp_get_x_next (mp);
26760      mp_flush_cur_exp (mp, new_expr);
26761    } else {
26762      switch (c) {
26763      case mp_less_than:
26764        boolean_reset (number_negative(cur_exp_value_number ()));
26765        break;
26766      case mp_less_or_equal:
26767        boolean_reset (number_nonpositive(cur_exp_value_number ()));
26768        break;
26769      case mp_greater_than:
26770        boolean_reset (number_positive(cur_exp_value_number ()));
26771        break;
26772      case mp_greater_or_equal:
26773        boolean_reset (number_nonnegative(cur_exp_value_number ()));
26774        break;
26775      case mp_equal_to:
26776        boolean_reset (number_zero(cur_exp_value_number ()));
26777        break;
26778      case mp_unequal_to:
26779        boolean_reset (number_nonzero(cur_exp_value_number ()));
26780        break;
26781      };                            /* there are no other cases */
26782    }
26783    mp->cur_exp.type = mp_boolean_type;
26784  DONE:
26785    mp->arith_error = false;        /* ignore overflow in comparisons */
26786    break;
26787  case mp_and_op:
26788  case mp_or_op:
26789    /* Here we use the sneaky fact that |and_op-false_code=or_op-true_code| */
26790    if ((mp_type (p) != mp_boolean_type) || (mp->cur_exp.type != mp_boolean_type))
26791      mp_bad_binary (mp, p, (quarterword) c);
26792    else if (number_to_boolean (p->data.n) == c + mp_false_code - mp_and_op) {
26793      set_cur_exp_value_boolean (number_to_boolean (p->data.n));
26794    }
26795    break;
26796  case mp_times:
26797    if ((mp->cur_exp.type < mp_color_type) || (mp_type (p) < mp_color_type)) {
26798      mp_bad_binary (mp, p, mp_times);
26799    } else if ((mp->cur_exp.type == mp_known) || (mp_type (p) == mp_known)) {
26800      /* Multiply when at least one operand is known */
26801      mp_number vv;
26802      new_fraction (vv);
26803      if (mp_type (p) == mp_known) {
26804        number_clone(vv, value_number (p));
26805        mp_free_value_node (mp, p);
26806      } else {
26807        number_clone(vv, cur_exp_value_number ());
26808        mp_unstash_cur_exp (mp, p);
26809      }
26810      if (mp->cur_exp.type == mp_known) {
26811        mp_number ret;
26812        new_number (ret);
26813        take_scaled (ret, cur_exp_value_number (), vv);
26814        set_cur_exp_value_number (ret);
26815        free_number (ret);
26816      } else if (mp->cur_exp.type == mp_pair_type) {
26817        mp_dep_mult (mp, (mp_value_node) x_part (value_node (cur_exp_node ())), vv, true);
26818        mp_dep_mult (mp, (mp_value_node) y_part (value_node (cur_exp_node ())), vv, true);
26819      } else if (mp->cur_exp.type == mp_color_type) {
26820        mp_dep_mult (mp, (mp_value_node) red_part (value_node (cur_exp_node ())), vv, true);
26821        mp_dep_mult (mp, (mp_value_node) green_part (value_node (cur_exp_node ())), vv, true);
26822        mp_dep_mult (mp, (mp_value_node) blue_part (value_node (cur_exp_node ())), vv, true);
26823      } else if (mp->cur_exp.type == mp_cmykcolor_type) {
26824        mp_dep_mult (mp, (mp_value_node) cyan_part (value_node (cur_exp_node ())), vv, true);
26825        mp_dep_mult (mp, (mp_value_node) magenta_part (value_node (cur_exp_node ())), vv, true);
26826        mp_dep_mult (mp, (mp_value_node) yellow_part (value_node (cur_exp_node ())), vv, true);
26827        mp_dep_mult (mp, (mp_value_node) black_part (value_node (cur_exp_node ())),  vv, true);
26828      } else {
26829        mp_dep_mult (mp, NULL, vv, true);
26830      }
26831      free_number (vv);
26832      binary_return;
26833
26834    } else if ((mp_nice_color_or_pair (mp, p, mp_type (p))
26835                && (mp->cur_exp.type > mp_pair_type))
26836               || (mp_nice_color_or_pair (mp, cur_exp_node (), mp->cur_exp.type)
26837                   && (mp_type (p) > mp_pair_type))) {
26838      mp_hard_times (mp, p);
26839      binary_return;
26840    } else {
26841      mp_bad_binary (mp, p, mp_times);
26842    }
26843    break;
26844  case mp_over:
26845    if ((mp->cur_exp.type != mp_known) || (mp_type (p) < mp_color_type)) {
26846      mp_bad_binary (mp, p, mp_over);
26847    } else {
26848      mp_number v_n;
26849      new_number (v_n);
26850      number_clone (v_n, cur_exp_value_number ());
26851      mp_unstash_cur_exp (mp, p);
26852      if (number_zero(v_n)) {
26853        /* Squeal about division by zero */
26854        const char *hlp[] = {
26855             "You're trying to divide the quantity shown above the error",
26856             "message by zero. I'm going to divide it by one instead.",
26857             NULL };
26858        mp_disp_err(mp, NULL);
26859        mp_back_error (mp, "Division by zero", hlp, true);
26860        mp_get_x_next (mp);
26861
26862      } else {
26863        if (mp->cur_exp.type == mp_known) {
26864          mp_number ret;
26865          new_number (ret);
26866          make_scaled (ret, cur_exp_value_number (), v_n);
26867          set_cur_exp_value_number (ret);
26868          free_number (ret);
26869        } else if (mp->cur_exp.type == mp_pair_type) {
26870          mp_dep_div (mp, (mp_value_node) x_part (value_node (cur_exp_node ())),
26871                      v_n);
26872          mp_dep_div (mp, (mp_value_node) y_part (value_node (cur_exp_node ())),
26873                      v_n);
26874        } else if (mp->cur_exp.type == mp_color_type) {
26875          mp_dep_div (mp,
26876                      (mp_value_node) red_part (value_node (cur_exp_node ())),
26877                      v_n);
26878          mp_dep_div (mp,
26879                      (mp_value_node) green_part (value_node (cur_exp_node ())),
26880                      v_n);
26881          mp_dep_div (mp,
26882                      (mp_value_node) blue_part (value_node (cur_exp_node ())),
26883                      v_n);
26884        } else if (mp->cur_exp.type == mp_cmykcolor_type) {
26885          mp_dep_div (mp,
26886                      (mp_value_node) cyan_part (value_node (cur_exp_node ())),
26887                      v_n);
26888          mp_dep_div (mp, (mp_value_node)
26889                      magenta_part (value_node (cur_exp_node ())), v_n);
26890          mp_dep_div (mp, (mp_value_node)
26891                      yellow_part (value_node (cur_exp_node ())), v_n);
26892          mp_dep_div (mp,
26893                      (mp_value_node) black_part (value_node (cur_exp_node ())),
26894                      v_n);
26895        } else {
26896          mp_dep_div (mp, NULL, v_n);
26897        }
26898      }
26899      free_number(v_n);
26900      binary_return;
26901    }
26902    break;
26903  case mp_pythag_add:
26904  case mp_pythag_sub:
26905    if ((mp->cur_exp.type == mp_known) && (mp_type (p) == mp_known)) {
26906      mp_number r;
26907      new_number (r);
26908      if (c == mp_pythag_add) {
26909        pyth_add (r, value_number (p), cur_exp_value_number ());
26910      } else {
26911        pyth_sub (r, value_number (p), cur_exp_value_number ());
26912      }
26913      set_cur_exp_value_number (r);
26914      free_number (r);
26915    } else
26916      mp_bad_binary (mp, p, (quarterword) c);
26917    break;
26918  case mp_rotated_by:
26919  case mp_slanted_by:
26920  case mp_scaled_by:
26921  case mp_shifted_by:
26922  case mp_transformed_by:
26923  case mp_x_scaled:
26924  case mp_y_scaled:
26925  case mp_z_scaled:
26926    /* The next few sections of the program deal with affine transformations
26927    of coordinate data. */
26928    if (mp_type (p) == mp_path_type) {
26929      path_trans ((quarterword) c, p);
26930      binary_return;
26931    } else if (mp_type (p) == mp_pen_type) {
26932      pen_trans ((quarterword) c, p);
26933      set_cur_exp_knot (mp_convex_hull (mp, cur_exp_knot ()));
26934      /* rounding error could destroy convexity */
26935      binary_return;
26936    } else if ((mp_type (p) == mp_pair_type) || (mp_type (p) == mp_transform_type)) {
26937      mp_big_trans (mp, p, (quarterword) c);
26938    } else if (mp_type (p) == mp_picture_type) {
26939      mp_do_edges_trans (mp, p, (quarterword) c);
26940      binary_return;
26941    } else {
26942      mp_bad_binary (mp, p, (quarterword) c);
26943    }
26944    break;
26945  case mp_concatenate:
26946    if ((mp->cur_exp.type == mp_string_type) && (mp_type (p) == mp_string_type)) {
26947      mp_string str = mp_cat (mp, value_str (p), cur_exp_str());
26948      delete_str_ref (cur_exp_str ()) ;
26949      set_cur_exp_str (str);
26950    } else
26951      mp_bad_binary (mp, p, mp_concatenate);
26952    break;
26953  case mp_substring_of:
26954    if (mp_nice_pair (mp, p, mp_type (p)) && (mp->cur_exp.type == mp_string_type)) {
26955      mp_string str = mp_chop_string (mp,
26956                          cur_exp_str (),
26957                          round_unscaled (value_number (x_part (value_node(p)))),
26958                          round_unscaled (value_number (y_part (value_node(p)))));
26959      delete_str_ref (cur_exp_str ()) ;
26960      set_cur_exp_str (str);
26961    } else
26962      mp_bad_binary (mp, p, mp_substring_of);
26963    break;
26964  case mp_subpath_of:
26965    if (mp->cur_exp.type == mp_pair_type)
26966      mp_pair_to_path (mp);
26967    if (mp_nice_pair (mp, p, mp_type (p)) && (mp->cur_exp.type == mp_path_type))
26968      mp_chop_path (mp, value_node (p));
26969    else
26970      mp_bad_binary (mp, p, mp_subpath_of);
26971    break;
26972  case mp_point_of:
26973  case mp_precontrol_of:
26974  case mp_postcontrol_of:
26975    if (mp->cur_exp.type == mp_pair_type)
26976      mp_pair_to_path (mp);
26977    if ((mp->cur_exp.type == mp_path_type) && (mp_type (p) == mp_known))
26978      mp_find_point (mp, value_number (p), (quarterword) c);
26979    else
26980      mp_bad_binary (mp, p, (quarterword) c);
26981    break;
26982  case mp_pen_offset_of:
26983    if ((mp->cur_exp.type == mp_pen_type) && mp_nice_pair (mp, p, mp_type (p)))
26984      mp_set_up_offset (mp, value_node (p));
26985    else
26986      mp_bad_binary (mp, p, mp_pen_offset_of);
26987    break;
26988  case mp_direction_time_of:
26989    if (mp->cur_exp.type == mp_pair_type)
26990      mp_pair_to_path (mp);
26991    if ((mp->cur_exp.type == mp_path_type) && mp_nice_pair (mp, p, mp_type (p)))
26992      mp_set_up_direction_time (mp, value_node (p));
26993    else
26994      mp_bad_binary (mp, p, mp_direction_time_of);
26995    break;
26996  case mp_envelope_of:
26997    if ((mp_type (p) != mp_pen_type) || (mp->cur_exp.type != mp_path_type))
26998      mp_bad_binary (mp, p, mp_envelope_of);
26999    else
27000      mp_set_up_envelope (mp, p);
27001    break;
27002  case mp_glyph_infont:
27003    if ((mp_type (p) != mp_string_type &&
27004         mp_type (p) != mp_known) || (mp->cur_exp.type != mp_string_type))
27005      mp_bad_binary (mp, p, mp_glyph_infont);
27006    else
27007      mp_set_up_glyph_infont (mp, p);
27008    break;
27009  case mp_arc_time_of:
27010    if (mp->cur_exp.type == mp_pair_type)
27011      mp_pair_to_path (mp);
27012    if ((mp->cur_exp.type == mp_path_type) && (mp_type (p) == mp_known)) {
27013      memset(&new_expr,0,sizeof(mp_value));
27014      new_number(new_expr.data.n);
27015      mp_get_arc_time (mp, &new_expr.data.n, cur_exp_knot (), value_number (p));
27016      mp_flush_cur_exp (mp, new_expr);
27017    } else {
27018      mp_bad_binary (mp, p, (quarterword) c);
27019    }
27020    break;
27021  case mp_intersect:
27022    if (mp_type (p) == mp_pair_type) {
27023      q = mp_stash_cur_exp (mp);
27024      mp_unstash_cur_exp (mp, p);
27025      mp_pair_to_path (mp);
27026      p = mp_stash_cur_exp (mp);
27027      mp_unstash_cur_exp (mp, q);
27028    }
27029    if (mp->cur_exp.type == mp_pair_type)
27030      mp_pair_to_path (mp);
27031    if ((mp->cur_exp.type == mp_path_type) && (mp_type (p) == mp_path_type)) {
27032      mp_number arg1, arg2;
27033      new_number (arg1);
27034      new_number (arg2);
27035      mp_path_intersection (mp, value_knot (p), cur_exp_knot ());
27036      number_clone (arg1, mp->cur_t);
27037      number_clone (arg2, mp->cur_tt);
27038      mp_pair_value (mp, arg1, arg2);
27039      free_number (arg1);
27040      free_number (arg2);
27041    } else {
27042      mp_bad_binary (mp, p, mp_intersect);
27043    }
27044    break;
27045  case mp_in_font:
27046    if ((mp->cur_exp.type != mp_string_type) || mp_type (p) != mp_string_type) {
27047      mp_bad_binary (mp, p, mp_in_font);
27048    } else {
27049      mp_do_infont (mp, p);
27050      binary_return;
27051    }
27052    break;
27053  }                            /* there are no other cases */
27054  mp_recycle_value (mp, p);
27055  mp_free_value_node (mp, p);        /* |return| to avoid this */
27056  mp_finish_binary (mp, old_p, old_exp);
27057}
27058
27059
27060@ @<Declare binary action...@>=
27061static void mp_bad_binary (MP mp, mp_node p, quarterword c) {
27062  char msg[256];
27063  mp_string sname;
27064  int old_setting = mp->selector;
27065  const char *hlp[] = {
27066         "I'm afraid I don't know how to apply that operation to that",
27067         "combination of types. Continue, and I'll return the second",
27068         "argument (see above) as the result of the operation.",
27069         NULL };
27070  mp->selector = new_string;
27071  if (c >= mp_min_of)
27072    mp_print_op (mp, c);
27073  mp_print_known_or_unknown_type (mp, mp_type (p), p);
27074  if (c >= mp_min_of)
27075    mp_print (mp, "of");
27076  else
27077    mp_print_op (mp, c);
27078  mp_print_known_or_unknown_type (mp, mp->cur_exp.type, cur_exp_node ());
27079  sname = mp_make_string(mp);
27080  mp->selector = old_setting;
27081  mp_snprintf (msg, 256, "Not implemented: %s", mp_str(mp, sname));
27082@.Not implemented...@>;
27083  delete_str_ref(sname);
27084  mp_disp_err (mp, p);
27085  mp_disp_err (mp, NULL);
27086  mp_back_error (mp, msg, hlp, true);
27087  mp_get_x_next (mp);
27088}
27089static void mp_bad_envelope_pen (MP mp) {
27090  const char *hlp[] = {
27091         "I'm afraid I don't know how to apply that operation to that",
27092         "combination of types. Continue, and I'll return the second",
27093         "argument (see above) as the result of the operation.",
27094         NULL };
27095  mp_disp_err (mp, NULL);
27096  mp_disp_err (mp, NULL);
27097  mp_back_error (mp, "Not implemented: envelope(elliptical pen)of(path)", hlp, true);
27098@.Not implemented...@>;
27099  mp_get_x_next (mp);
27100}
27101
27102@ @<Declare binary action...@>=
27103static mp_node mp_tarnished (MP mp, mp_node p) {
27104  mp_node q;    /* beginning of the big node */
27105  mp_node r;    /* moving value node pointer */
27106  (void) mp;
27107  q = value_node (p);
27108  switch (mp_type (p)) {
27109  case mp_pair_type:
27110    r = x_part (q);
27111    if (mp_type (r) == mp_independent)
27112      return MP_VOID;
27113    r = y_part (q);
27114    if (mp_type (r) == mp_independent)
27115      return MP_VOID;
27116    break;
27117  case mp_color_type:
27118    r = red_part (q);
27119    if (mp_type (r) == mp_independent)
27120      return MP_VOID;
27121    r = green_part (q);
27122    if (mp_type (r) == mp_independent)
27123      return MP_VOID;
27124    r = blue_part (q);
27125    if (mp_type (r) == mp_independent)
27126      return MP_VOID;
27127    break;
27128  case mp_cmykcolor_type:
27129    r = cyan_part (q);
27130    if (mp_type (r) == mp_independent)
27131      return MP_VOID;
27132    r = magenta_part (q);
27133    if (mp_type (r) == mp_independent)
27134      return MP_VOID;
27135    r = yellow_part (q);
27136    if (mp_type (r) == mp_independent)
27137      return MP_VOID;
27138    r = black_part (q);
27139    if (mp_type (r) == mp_independent)
27140      return MP_VOID;
27141    break;
27142  case mp_transform_type:
27143    r = tx_part (q);
27144    if (mp_type (r) == mp_independent)
27145      return MP_VOID;
27146    r = ty_part (q);
27147    if (mp_type (r) == mp_independent)
27148      return MP_VOID;
27149    r = xx_part (q);
27150    if (mp_type (r) == mp_independent)
27151      return MP_VOID;
27152    r = xy_part (q);
27153    if (mp_type (r) == mp_independent)
27154      return MP_VOID;
27155    r = yx_part (q);
27156    if (mp_type (r) == mp_independent)
27157      return MP_VOID;
27158    r = yy_part (q);
27159    if (mp_type (r) == mp_independent)
27160      return MP_VOID;
27161    break;
27162  default:                     /* there are no other valid cases, but please the compiler */
27163    break;
27164  }
27165  return NULL;
27166}
27167
27168@ The first argument to |add_or_subtract| is the location of a value node
27169in a capsule or pair node that will soon be recycled. The second argument
27170is either a location within a pair or transform node of |cur_exp|,
27171or it is NULL (which means that |cur_exp| itself should be the second
27172argument).  The third argument is either |plus| or |minus|.
27173
27174The sum or difference of the numeric quantities will replace the second
27175operand.  Arithmetic overflow may go undetected; users aren't supposed to
27176be monkeying around with really big values.
27177@^overflow in arithmetic@>
27178
27179@<Declare binary action...@>=
27180@<Declare the procedure called |dep_finish|@>;
27181static void mp_add_or_subtract (MP mp, mp_node p, mp_node q, quarterword c) {
27182  mp_variable_type s, t;        /* operand types */
27183  mp_value_node r;      /* dependency list traverser */
27184  mp_value_node v = NULL;       /* second operand value for dep lists */
27185  mp_number vv;       /* second operand value for known values */
27186  new_number (vv);
27187  if (q == NULL) {
27188    t = mp->cur_exp.type;
27189    if (t < mp_dependent)
27190      number_clone (vv, cur_exp_value_number ());
27191    else
27192      v = (mp_value_node) dep_list ((mp_value_node) cur_exp_node ());
27193  } else {
27194    t = mp_type (q);
27195    if (t < mp_dependent)
27196      number_clone (vv, value_number (q));
27197    else
27198      v = (mp_value_node) dep_list ((mp_value_node) q);
27199  }
27200  if (t == mp_known) {
27201    mp_value_node qq = (mp_value_node) q;
27202    if (c == mp_minus)
27203      number_negate (vv);
27204    if (mp_type (p) == mp_known) {
27205      slow_add (vv, value_number (p), vv);
27206      if (q == NULL)
27207        set_cur_exp_value_number (vv);
27208      else
27209        set_value_number (q, vv);
27210      free_number (vv);
27211      return;
27212    }
27213    /* Add a known value to the constant term of |dep_list(p)| */
27214    r = (mp_value_node) dep_list ((mp_value_node) p);
27215    while (dep_info (r) != NULL)
27216      r = (mp_value_node) mp_link (r);
27217    slow_add (vv, dep_value (r), vv);
27218    set_dep_value (r, vv);
27219    if (qq == NULL) {
27220      qq = mp_get_dep_node (mp);
27221      set_cur_exp_node ((mp_node) qq);
27222      mp->cur_exp.type = mp_type (p);
27223      mp_name_type (qq) = mp_capsule;
27224      /* clang: never read: |q = (mp_node) qq;| */
27225    }
27226    set_dep_list (qq, dep_list ((mp_value_node) p));
27227    mp_type (qq) = mp_type (p);
27228    set_prev_dep (qq, prev_dep ((mp_value_node) p));
27229    mp_link (prev_dep ((mp_value_node) p)) = (mp_node) qq;
27230    mp_type (p) = mp_known;     /* this will keep the recycler from collecting non-garbage */
27231  } else {
27232    if (c == mp_minus)
27233      mp_negate_dep_list (mp, v);
27234    /* Add operand |p| to the dependency list |v| */
27235    /* We prefer |dependent| lists to |mp_proto_dependent| ones, because it is
27236       nice to retain the extra accuracy of |fraction| coefficients.
27237       But we have to handle both kinds, and mixtures too. */
27238    if (mp_type (p) == mp_known) {
27239      /* Add the known |value(p)| to the constant term of |v| */
27240      while (dep_info (v) != NULL) {
27241        v = (mp_value_node) mp_link (v);
27242      }
27243      slow_add (vv, value_number (p), dep_value (v));
27244      set_dep_value (v, vv);
27245    } else {
27246      s = mp_type (p);
27247      r = (mp_value_node) dep_list ((mp_value_node) p);
27248      if (t == mp_dependent) {
27249        if (s == mp_dependent) {
27250          mp_number ret1, ret2;
27251          new_fraction (ret1);
27252          new_fraction (ret2);
27253          mp_max_coef (mp, &ret1, r);
27254          mp_max_coef (mp, &ret2, v);
27255          number_add (ret1, ret2);
27256          free_number (ret2);
27257          if (number_less (ret1, coef_bound_k)) {
27258            v = mp_p_plus_q (mp, v, r, mp_dependent);
27259            free_number (ret1);
27260            goto DONE;
27261          }
27262          free_number (ret1);
27263        }                           /* |fix_needed| will necessarily be false */
27264        t = mp_proto_dependent;
27265        v = mp_p_over_v (mp, v, unity_t, mp_dependent, mp_proto_dependent);
27266      }
27267      if (s == mp_proto_dependent)
27268        v = mp_p_plus_q (mp, v, r, mp_proto_dependent);
27269      else
27270        v = mp_p_plus_fq (mp, v, unity_t, r, mp_proto_dependent, mp_dependent);
27271    DONE:
27272      /* Output the answer, |v| (which might have become |known|) */
27273      if (q != NULL) {
27274        mp_dep_finish (mp, v, (mp_value_node) q, t);
27275      } else {
27276        mp->cur_exp.type = t;
27277        mp_dep_finish (mp, v, NULL, t);
27278      }
27279    }
27280  }
27281  free_number (vv);
27282}
27283
27284
27285@ Here's the current situation: The dependency list |v| of type |t|
27286should either be put into the current expression (if |q=NULL|) or
27287into location |q| within a pair node (otherwise). The destination (|cur_exp|
27288or |q|) formerly held a dependency list with the same
27289final pointer as the list |v|.
27290
27291@<Declare the procedure called |dep_finish|@>=
27292static void mp_dep_finish (MP mp, mp_value_node v, mp_value_node q,
27293                           quarterword t) {
27294  mp_value_node p;      /* the destination */
27295  if (q == NULL)
27296    p = (mp_value_node) cur_exp_node ();
27297  else
27298    p = q;
27299  set_dep_list (p, v);
27300  mp_type (p) = t;
27301  if (dep_info (v) == NULL) {
27302    mp_number vv;    /* the value, if it is |known| */
27303    new_number (vv);
27304    number_clone (vv, value_number (v));
27305    if (q == NULL) {
27306      mp_value new_expr;
27307      memset(&new_expr,0,sizeof(mp_value));
27308      new_number(new_expr.data.n);
27309      number_clone (new_expr.data.n, vv);
27310      mp_flush_cur_exp (mp, new_expr);
27311    } else {
27312      mp_recycle_value (mp, (mp_node) p);
27313      mp_type (q) = mp_known;
27314      set_value_number (q, vv);
27315    }
27316    free_number (vv);
27317  } else if (q == NULL) {
27318    mp->cur_exp.type = t;
27319  }
27320  if (mp->fix_needed)
27321    mp_fix_dependencies (mp);
27322}
27323
27324@ @<Declare binary action...@>=
27325static void mp_dep_mult (MP mp, mp_value_node p, mp_number v, boolean v_is_scaled) {
27326  mp_value_node q;      /* the dependency list being multiplied by |v| */
27327  quarterword s, t;     /* its type, before and after */
27328  if (p == NULL) {
27329    q = (mp_value_node) cur_exp_node ();
27330  } else if (mp_type (p) != mp_known) {
27331    q = p;
27332  } else {
27333    {
27334      mp_number r1, arg1;
27335      new_number (arg1);
27336      number_clone (arg1, dep_value (p));
27337      if (v_is_scaled) {
27338        new_number (r1);
27339        take_scaled (r1, arg1, v);
27340      } else {
27341        new_fraction (r1);
27342        take_fraction (r1, arg1, v);
27343      }
27344      set_dep_value (p, r1);
27345      free_number (r1);
27346      free_number (arg1);
27347    }
27348    return;
27349  }
27350  t = mp_type (q);
27351  q = (mp_value_node) dep_list (q);
27352  s = t;
27353  if (t == mp_dependent) {
27354    if (v_is_scaled) {
27355      mp_number ab_vs_cd;
27356      mp_number arg1, arg2;
27357      new_number (ab_vs_cd);
27358      new_number (arg2);
27359      new_fraction (arg1);
27360      mp_max_coef (mp, &arg1, q);
27361      number_clone (arg2, v);
27362      number_abs (arg2);
27363      ab_vs_cd (ab_vs_cd, arg1, arg2, coef_bound_minus_1, unity_t);
27364      free_number (arg1);
27365      free_number (arg2);
27366      if (number_nonnegative(ab_vs_cd)) {
27367        t = mp_proto_dependent;
27368      }
27369      free_number (ab_vs_cd);
27370    }
27371  }
27372  q = mp_p_times_v (mp, q, v, s, t, v_is_scaled);
27373  mp_dep_finish (mp, q, p, t);
27374}
27375
27376
27377@ Here is a routine that is similar to |times|; but it is invoked only
27378internally, when |v| is a |fraction| whose magnitude is at most~1,
27379and when |cur_type>=mp_color_type|.
27380
27381@c
27382static void mp_frac_mult (MP mp, mp_number n, mp_number d) {
27383  /* multiplies |cur_exp| by |n/d| */
27384  mp_node old_exp;      /* a capsule to recycle */
27385  mp_number v;   /* |n/d| */
27386  new_fraction (v);
27387  if (number_greater (internal_value (mp_tracing_commands), two_t)) {
27388    @<Trace the fraction multiplication@>;
27389  }
27390  switch (mp->cur_exp.type) {
27391  case mp_transform_type:
27392  case mp_color_type:
27393  case mp_cmykcolor_type:
27394  case mp_pair_type:
27395    old_exp = mp_tarnished (mp, cur_exp_node ());
27396    break;
27397  case mp_independent:
27398    old_exp = MP_VOID;
27399    break;
27400  default:
27401    old_exp = NULL;
27402    break;
27403  }
27404  if (old_exp != NULL) {
27405    old_exp = cur_exp_node ();
27406    mp_make_exp_copy (mp, old_exp);
27407  }
27408  make_fraction (v, n, d);
27409  if (mp->cur_exp.type == mp_known) {
27410    mp_number r1, arg1;
27411    new_fraction (r1);
27412    new_number (arg1);
27413    number_clone (arg1, cur_exp_value_number ());
27414    take_fraction (r1, arg1, v);
27415    set_cur_exp_value_number (r1);
27416    free_number (r1);
27417    free_number (arg1);
27418  } else if (mp->cur_exp.type == mp_pair_type) {
27419    mp_dep_mult (mp, (mp_value_node) x_part (value_node (cur_exp_node ())), v, false);
27420    mp_dep_mult (mp, (mp_value_node) y_part (value_node (cur_exp_node ())), v, false);
27421  } else if (mp->cur_exp.type == mp_color_type) {
27422    mp_dep_mult (mp, (mp_value_node) red_part (value_node (cur_exp_node ())), v, false);
27423    mp_dep_mult (mp, (mp_value_node) green_part (value_node (cur_exp_node ())), v, false);
27424    mp_dep_mult (mp, (mp_value_node) blue_part (value_node (cur_exp_node ())), v, false);
27425  } else if (mp->cur_exp.type == mp_cmykcolor_type) {
27426    mp_dep_mult (mp, (mp_value_node) cyan_part (value_node (cur_exp_node ())), v, false);
27427    mp_dep_mult (mp, (mp_value_node) magenta_part (value_node (cur_exp_node ())), v, false);
27428    mp_dep_mult (mp, (mp_value_node) yellow_part (value_node (cur_exp_node ())), v, false);
27429    mp_dep_mult (mp, (mp_value_node) black_part (value_node (cur_exp_node ())), v, false);
27430  } else {
27431    mp_dep_mult (mp, NULL, v, false);
27432  }
27433  if (old_exp != NULL) {
27434    mp_recycle_value (mp, old_exp);
27435    mp_free_value_node (mp, old_exp);
27436  }
27437  free_number (v);
27438}
27439
27440
27441@ @<Trace the fraction multiplication@>=
27442{
27443  mp_begin_diagnostic (mp);
27444  mp_print_nl (mp, "{(");
27445  print_number (n);
27446  mp_print_char (mp, xord ('/'));
27447  print_number (d);
27448  mp_print (mp, ")*(");
27449  mp_print_exp (mp, NULL, 0);
27450  mp_print (mp, ")}");
27451  mp_end_diagnostic (mp, false);
27452}
27453
27454
27455@ The |hard_times| routine multiplies a nice color or pair by a dependency list.
27456
27457@<Declare binary action procedures@>=
27458static void mp_hard_times (MP mp, mp_node p) {
27459  mp_value_node q;      /* a copy of the dependent variable |p| */
27460  mp_value_node pp;     /* for typecasting p */
27461  mp_node r;    /* a component of the big node for the nice color or pair */
27462  mp_number v;     /* the known value for |r| */
27463  new_number (v);
27464  if (mp_type (p) <= mp_pair_type) {
27465    q = (mp_value_node) mp_stash_cur_exp (mp);
27466    mp_unstash_cur_exp (mp, p);
27467    p = (mp_node) q;
27468  }                             /* now |cur_type=mp_pair_type| or |cur_type=mp_color_type| or |cur_type=mp_cmykcolor_type| */
27469  pp = (mp_value_node) p;
27470  if (mp->cur_exp.type == mp_pair_type) {
27471    r = x_part (value_node (cur_exp_node ()));
27472    number_clone(v, value_number (r));
27473    mp_new_dep (mp, r, mp_type (pp),
27474                mp_copy_dep_list (mp, (mp_value_node) dep_list (pp)));
27475    mp_dep_mult (mp, (mp_value_node) r, v, true);
27476    r = y_part (value_node (cur_exp_node ()));
27477    number_clone(v, value_number (r));
27478    mp_new_dep (mp, r, mp_type (pp),
27479                mp_copy_dep_list (mp, (mp_value_node) dep_list (pp)));
27480    mp_dep_mult (mp, (mp_value_node) r, v, true);
27481  } else if (mp->cur_exp.type == mp_color_type) {
27482    r = red_part (value_node (cur_exp_node ()));
27483    number_clone(v, value_number (r));
27484    mp_new_dep (mp, r, mp_type (pp),
27485                mp_copy_dep_list (mp, (mp_value_node) dep_list (pp)));
27486    mp_dep_mult (mp, (mp_value_node) r, v, true);
27487    r = green_part (value_node (cur_exp_node ()));
27488    number_clone(v, value_number (r));
27489    mp_new_dep (mp, r, mp_type (pp),
27490                mp_copy_dep_list (mp, (mp_value_node) dep_list (pp)));
27491    mp_dep_mult (mp, (mp_value_node) r, v, true);
27492    r = blue_part (value_node (cur_exp_node ()));
27493    number_clone(v, value_number (r));
27494    mp_new_dep (mp, r, mp_type (pp),
27495                mp_copy_dep_list (mp, (mp_value_node) dep_list (pp)));
27496    mp_dep_mult (mp, (mp_value_node) r, v, true);
27497  } else if (mp->cur_exp.type == mp_cmykcolor_type) {
27498    r = cyan_part (value_node (cur_exp_node ()));
27499    number_clone(v, value_number (r));
27500    mp_new_dep (mp, r, mp_type (pp),
27501                mp_copy_dep_list (mp, (mp_value_node) dep_list (pp)));
27502    mp_dep_mult (mp, (mp_value_node) r, v, true);
27503    r = yellow_part (value_node (cur_exp_node ()));
27504    number_clone(v, value_number (r));
27505    mp_new_dep (mp, r, mp_type (pp),
27506                mp_copy_dep_list (mp, (mp_value_node) dep_list (pp)));
27507    mp_dep_mult (mp, (mp_value_node) r, v, true);
27508    r = magenta_part (value_node (cur_exp_node ()));
27509    number_clone(v, value_number (r));
27510    mp_new_dep (mp, r, mp_type (pp),
27511                mp_copy_dep_list (mp, (mp_value_node) dep_list (pp)));
27512    mp_dep_mult (mp, (mp_value_node) r, v, true);
27513    r = black_part (value_node (cur_exp_node ()));
27514    number_clone(v, value_number (r));
27515    mp_new_dep (mp, r, mp_type (pp),
27516                mp_copy_dep_list (mp, (mp_value_node) dep_list (pp)));
27517    mp_dep_mult (mp, (mp_value_node) r, v, true);
27518  }
27519  free_number (v);
27520}
27521
27522@ @<Declare binary action...@>=
27523static void mp_dep_div (MP mp, mp_value_node p, mp_number v) {
27524  mp_value_node q;      /* the dependency list being divided by |v| */
27525  quarterword s, t;     /* its type, before and after */
27526  if (p == NULL)
27527    q = (mp_value_node) cur_exp_node ();
27528  else if (mp_type (p) != mp_known)
27529    q = p;
27530  else {
27531    mp_number ret;
27532    new_number (ret);
27533    make_scaled (ret, value_number (p), v);
27534    set_value_number (p, ret);
27535    free_number (ret);
27536    return;
27537  }
27538  t = mp_type (q);
27539  q = (mp_value_node) dep_list (q);
27540  s = t;
27541  if (t == mp_dependent) {
27542      mp_number ab_vs_cd;
27543      mp_number arg1, arg2;
27544      new_number (ab_vs_cd);
27545      new_number (arg2);
27546      new_fraction (arg1);
27547      mp_max_coef (mp, &arg1, q);
27548      number_clone (arg2, v);
27549      number_abs (arg2);
27550      ab_vs_cd (ab_vs_cd, arg1, unity_t, coef_bound_minus_1, arg2);
27551      free_number (arg1);
27552      free_number (arg2);
27553      if (number_nonnegative(ab_vs_cd)) {
27554        t = mp_proto_dependent;
27555      }
27556      free_number (ab_vs_cd);
27557  }
27558  q = mp_p_over_v (mp, q, v, s, t);
27559  mp_dep_finish (mp, q, p, t);
27560}
27561
27562@ Let |c| be one of the eight transform operators. The procedure call
27563|set_up_trans(c)| first changes |cur_exp| to a transform that corresponds to
27564|c| and the original value of |cur_exp|. (In particular, |cur_exp| doesn't
27565change at all if |c=transformed_by|.)
27566
27567Then, if all components of the resulting transform are |known|, they are
27568moved to the global variables |txx|, |txy|, |tyx|, |tyy|, |tx|, |ty|;
27569and |cur_exp| is changed to the known value zero.
27570
27571@<Declare binary action...@>=
27572static void mp_set_up_trans (MP mp, quarterword c) {
27573  mp_node p, q, r;      /* list manipulation registers */
27574  mp_value new_expr;
27575  memset(&new_expr,0,sizeof(mp_value));
27576  if ((c != mp_transformed_by) || (mp->cur_exp.type != mp_transform_type)) {
27577    /* Put the current transform into |cur_exp| */
27578    const char *hlp[] = {
27579           "The expression shown above has the wrong type,",
27580           "so I can\'t transform anything using it.",
27581           "Proceed, and I'll omit the transformation.",
27582           NULL };
27583    p = mp_stash_cur_exp (mp);
27584    set_cur_exp_node (mp_id_transform (mp));
27585    mp->cur_exp.type = mp_transform_type;
27586    q = value_node (cur_exp_node ());
27587    switch (c) {
27588      @<For each of the eight cases, change the relevant fields of |cur_exp|
27589      and |goto done|;
27590      but do nothing if capsule |p| doesn't have the appropriate type@>;
27591    };                            /* there are no other cases */
27592    mp_disp_err (mp, p);
27593    mp_back_error (mp, "Improper transformation argument", hlp, true);
27594    mp_get_x_next (mp);
27595  DONE:
27596    mp_recycle_value (mp, p);
27597    mp_free_value_node (mp, p);
27598
27599  }
27600  /* If the current transform is entirely known, stash it in global variables;
27601    otherwise |return| */
27602  q = value_node (cur_exp_node ());
27603  if (mp_type (tx_part (q)) != mp_known)
27604    return;
27605  if (mp_type (ty_part (q)) != mp_known)
27606    return;
27607  if (mp_type (xx_part (q)) != mp_known)
27608    return;
27609  if (mp_type (xy_part (q)) != mp_known)
27610    return;
27611  if (mp_type (yx_part (q)) != mp_known)
27612    return;
27613  if (mp_type (yy_part (q)) != mp_known)
27614    return;
27615  number_clone(mp->txx, value_number (xx_part (q)));
27616  number_clone(mp->txy, value_number (xy_part (q)));
27617  number_clone(mp->tyx, value_number (yx_part (q)));
27618  number_clone(mp->tyy, value_number (yy_part (q)));
27619  number_clone(mp->tx, value_number (tx_part (q)));
27620  number_clone(mp->ty, value_number (ty_part (q)));
27621  new_number(new_expr.data.n);
27622  set_number_to_zero (new_expr.data.n);
27623  mp_flush_cur_exp (mp, new_expr);
27624}
27625
27626
27627@ @<Glob...@>=
27628mp_number txx;
27629mp_number txy;
27630mp_number tyx;
27631mp_number tyy;
27632mp_number tx;
27633mp_number ty;      /* current transform coefficients */
27634
27635@ @<Initialize table...@>=
27636new_number(mp->txx);
27637new_number(mp->txy);
27638new_number(mp->tyx);
27639new_number(mp->tyy);
27640new_number(mp->tx);
27641new_number(mp->ty);
27642
27643@ @<Free table...@>=
27644free_number(mp->txx);
27645free_number(mp->txy);
27646free_number(mp->tyx);
27647free_number(mp->tyy);
27648free_number(mp->tx);
27649free_number(mp->ty);
27650
27651
27652@ @<For each of the eight cases...@>=
27653case mp_rotated_by:
27654if (mp_type (p) == mp_known)
27655  @<Install sines and cosines, then |goto done|@>;
27656break;
27657case mp_slanted_by:
27658if (mp_type (p) > mp_pair_type) {
27659  mp_install (mp, xy_part (q), p);
27660  goto DONE;
27661}
27662break;
27663case mp_scaled_by:
27664if (mp_type (p) > mp_pair_type) {
27665  mp_install (mp, xx_part (q), p);
27666  mp_install (mp, yy_part (q), p);
27667  goto DONE;
27668}
27669break;
27670case mp_shifted_by:
27671if (mp_type (p) == mp_pair_type) {
27672  r = value_node (p);
27673  mp_install (mp, tx_part (q), x_part (r));
27674  mp_install (mp, ty_part (q), y_part (r));
27675  goto DONE;
27676}
27677break;
27678case mp_x_scaled:
27679if (mp_type (p) > mp_pair_type) {
27680  mp_install (mp, xx_part (q), p);
27681  goto DONE;
27682}
27683break;
27684case mp_y_scaled:
27685if (mp_type (p) > mp_pair_type) {
27686  mp_install (mp, yy_part (q), p);
27687  goto DONE;
27688}
27689break;
27690case mp_z_scaled:
27691if (mp_type (p) == mp_pair_type)
27692  @<Install a complex multiplier, then |goto done|@>;
27693break;
27694case mp_transformed_by:
27695break;
27696
27697
27698@ @<Install sines and cosines, then |goto done|@>=
27699{
27700  mp_number n_sin, n_cos, arg1, arg2;
27701  new_number (arg1);
27702  new_number (arg2);
27703  new_fraction (n_sin);
27704  new_fraction (n_cos); /* results computed by |n_sin_cos| */
27705  number_clone (arg2, unity_t);
27706  number_clone (arg1, value_number (p));
27707  number_multiply_int (arg2, 360);
27708  number_modulo (arg1, arg2);
27709  convert_scaled_to_angle (arg1);
27710  n_sin_cos (arg1, n_cos, n_sin);
27711  fraction_to_round_scaled (n_sin);
27712  fraction_to_round_scaled (n_cos);
27713  set_value_number (xx_part (q), n_cos);
27714  set_value_number (yx_part (q), n_sin);
27715  set_value_number (xy_part (q), value_number (yx_part (q)));
27716  number_negate (value_number (xy_part (q)));
27717  set_value_number (yy_part (q), value_number (xx_part (q)));
27718  free_number (arg1);
27719  free_number (arg2);
27720  free_number (n_sin);
27721  free_number (n_cos);
27722  goto DONE;
27723}
27724
27725
27726@ @<Install a complex multiplier, then |goto done|@>=
27727{
27728  r = value_node (p);
27729  mp_install (mp, xx_part (q), x_part (r));
27730  mp_install (mp, yy_part (q), x_part (r));
27731  mp_install (mp, yx_part (q), y_part (r));
27732  if (mp_type (y_part (r)) == mp_known) {
27733    set_value_number (y_part (r), value_number (y_part (r)));
27734    number_negate (value_number (y_part (r)));
27735  } else {
27736    mp_negate_dep_list (mp, (mp_value_node) dep_list ((mp_value_node)
27737                                                      y_part (r)));
27738  }
27739  mp_install (mp, xy_part (q), y_part (r));
27740  goto DONE;
27741}
27742
27743
27744@ Procedure |set_up_known_trans| is like |set_up_trans|, but it
27745insists that the transformation be entirely known.
27746
27747@<Declare binary action...@>=
27748static void mp_set_up_known_trans (MP mp, quarterword c) {
27749  mp_set_up_trans (mp, c);
27750  if (mp->cur_exp.type != mp_known) {
27751    mp_value new_expr;
27752    const char *hlp[] = {
27753           "I'm unable to apply a partially specified transformation",
27754           "except to a fully known pair or transform.",
27755           "Proceed, and I'll omit the transformation.",
27756           NULL };
27757    memset(&new_expr,0,sizeof(mp_value));
27758    new_number(new_expr.data.n);
27759    mp_disp_err(mp, NULL);
27760    set_number_to_zero (new_expr.data.n);
27761    mp_back_error (mp,"Transform components aren't all known", hlp, true);
27762    mp_get_x_next (mp);
27763    mp_flush_cur_exp (mp, new_expr);
27764    set_number_to_unity(mp->txx);
27765    set_number_to_zero(mp->txy);
27766    set_number_to_zero(mp->tyx);
27767    set_number_to_unity(mp->tyy);
27768    set_number_to_zero(mp->tx);
27769    set_number_to_zero(mp->ty);
27770  }
27771}
27772
27773
27774@ Here's a procedure that applies the transform |txx..ty| to a pair of
27775coordinates in locations |p| and~|q|.
27776
27777@<Declare binary action...@>=
27778static void mp_number_trans (MP mp, mp_number *p, mp_number *q) {
27779  mp_number r1, r2, v;
27780  new_number (r1);
27781  new_number (r2);
27782  new_number (v);
27783  take_scaled (r1, *p, mp->txx);
27784  take_scaled (r2, *q, mp->txy);
27785  number_add (r1, r2);
27786  set_number_from_addition(v, r1, mp->tx);
27787  take_scaled (r1, *p, mp->tyx);
27788  take_scaled (r2, *q, mp->tyy);
27789  number_add (r1, r2);
27790  set_number_from_addition(*q, r1, mp->ty);
27791  number_clone(*p,v);
27792  free_number (r1);
27793  free_number (r2);
27794  free_number(v);
27795}
27796
27797
27798@ The simplest transformation procedure applies a transform to all
27799coordinates of a path.  The |path_trans(c)(p)| macro applies
27800a transformation defined by |cur_exp| and the transform operator |c|
27801to the path~|p|.
27802
27803@d path_trans(A,B) { mp_set_up_known_trans(mp, (A));
27804                     mp_unstash_cur_exp(mp, (B));
27805                     mp_do_path_trans(mp, cur_exp_knot()); }
27806
27807@<Declare binary action...@>=
27808static void mp_do_path_trans (MP mp, mp_knot p) {
27809  mp_knot q;    /* list traverser */
27810  q = p;
27811  do {
27812    if (mp_left_type (q) != mp_endpoint)
27813      mp_number_trans (mp, &q->left_x, &q->left_y);
27814    mp_number_trans (mp, &q->x_coord, &q->y_coord);
27815    if (mp_right_type (q) != mp_endpoint)
27816      mp_number_trans (mp, &q->right_x, &q->right_y);
27817    q = mp_next_knot (q);
27818  } while (q != p);
27819}
27820
27821
27822@ Transforming a pen is very similar, except that there are no |mp_left_type|
27823and |mp_right_type| fields.
27824
27825@d pen_trans(A,B) { mp_set_up_known_trans(mp, (A));
27826                    mp_unstash_cur_exp(mp, (B));
27827                    mp_do_pen_trans(mp, cur_exp_knot()); }
27828
27829@<Declare binary action...@>=
27830static void mp_do_pen_trans (MP mp, mp_knot p) {
27831  mp_knot q;    /* list traverser */
27832  if (pen_is_elliptical (p)) {
27833    mp_number_trans (mp, &p->left_x, &p->left_y);
27834    mp_number_trans (mp, &p->right_x, &p->right_y);
27835  }
27836  q = p;
27837  do {
27838    mp_number_trans (mp, &q->x_coord, &q->y_coord);
27839    q = mp_next_knot (q);
27840  } while (q != p);
27841}
27842
27843
27844@ The next transformation procedure applies to edge structures. It will do
27845any transformation, but the results may be substandard if the picture contains
27846text that uses downloaded bitmap fonts.  The binary action procedure is
27847|do_edges_trans|, but we also need a function that just scales a picture.
27848That routine is |scale_edges|.  Both it and the underlying routine |edges_trans|
27849should be thought of as procedures that update an edge structure |h|, except
27850that they have to return a (possibly new) structure because of the need to call
27851|private_edges|.
27852
27853@<Declare binary action...@>=
27854static mp_edge_header_node mp_edges_trans (MP mp, mp_edge_header_node h) {
27855  mp_node q;    /* the object being transformed */
27856  mp_dash_node r, s; /* for list manipulation */
27857  mp_number sx, sy;        /* saved transformation parameters */
27858  mp_number sqdet; /* square root of determinant for |dash_scale| */
27859  mp_number sgndet;       /* sign of the determinant */
27860  h = mp_private_edges (mp, h);
27861  new_number(sx);
27862  new_number(sy);
27863  new_number(sqdet);
27864  new_number(sgndet);
27865  mp_sqrt_det (mp, &sqdet, mp->txx, mp->txy, mp->tyx, mp->tyy);
27866  ab_vs_cd (sgndet, mp->txx, mp->tyy, mp->txy, mp->tyx);
27867  if (dash_list (h) != mp->null_dash) {
27868    @<Try to transform the dash list of |h|@>;
27869  }
27870  @<Make the bounding box of |h| unknown if it can't be updated properly
27871    without scanning the whole structure@>;
27872  q = mp_link (edge_list (h));
27873  while (q != NULL) {
27874    @<Transform graphical object |q|@>;
27875    q = mp_link (q);
27876  }
27877  free_number (sx);
27878  free_number (sy);
27879  free_number (sqdet);
27880  free_number(sgndet);
27881  return h;
27882}
27883static void mp_do_edges_trans (MP mp, mp_node p, quarterword c) {
27884  mp_set_up_known_trans (mp, c);
27885  set_value_node (p, (mp_node)mp_edges_trans (mp, (mp_edge_header_node)value_node (p)));
27886  mp_unstash_cur_exp (mp, p);
27887}
27888static mp_edge_header_node mp_scale_edges (MP mp, mp_number se_sf, mp_edge_header_node se_pic) {
27889  number_clone(mp->txx, se_sf);
27890  number_clone(mp->tyy, se_sf);
27891  set_number_to_zero(mp->txy);
27892  set_number_to_zero(mp->tyx);
27893  set_number_to_zero(mp->tx);
27894  set_number_to_zero(mp->ty);
27895  return mp_edges_trans (mp, se_pic);
27896}
27897
27898
27899@ @<Try to transform the dash list of |h|@>=
27900if (number_nonzero(mp->txy) || number_nonzero(mp->tyx) ||
27901    number_nonzero(mp->ty) || number_nonequalabs (mp->txx, mp->tyy)) {
27902  mp_flush_dash_list (mp, h);
27903} else {
27904  mp_number abs_tyy, ret;
27905  new_number (abs_tyy);
27906  if (number_negative(mp->txx)) {
27907    @<Reverse the dash list of |h|@>;
27908  }
27909  @<Scale the dash list by |txx| and shift it by |tx|@>;
27910  number_clone(abs_tyy, mp->tyy);
27911  number_abs (abs_tyy);
27912  new_number (ret);
27913  take_scaled (ret, h->dash_y, abs_tyy);
27914  number_clone(h->dash_y, ret);
27915  free_number (ret);
27916  free_number (abs_tyy);
27917}
27918
27919
27920@ @<Reverse the dash list of |h|@>=
27921{
27922  r = dash_list (h);
27923  set_dash_list (h, mp->null_dash);
27924  while (r != mp->null_dash) {
27925    s = r;
27926    r = (mp_dash_node)mp_link (r);
27927    number_swap(s->start_x, s->stop_x );
27928    mp_link (s) = (mp_node)dash_list (h);
27929    set_dash_list (h, s);
27930  }
27931}
27932
27933
27934@ @<Scale the dash list by |txx| and shift it by |tx|@>=
27935r = dash_list (h);
27936{
27937  mp_number arg1;
27938  new_number (arg1);
27939  while (r != mp->null_dash) {
27940    take_scaled (arg1, r->start_x, mp->txx);
27941    set_number_from_addition(r->start_x, arg1, mp->tx);
27942    take_scaled (arg1, r->stop_x, mp->txx);
27943    set_number_from_addition(r->stop_x, arg1, mp->tx);
27944    r = (mp_dash_node)mp_link (r);
27945  }
27946  free_number (arg1);
27947}
27948
27949
27950@ @<Make the bounding box of |h| unknown if it can't be updated properly...@>=
27951if (number_zero(mp->txx) && number_zero(mp->tyy)) {
27952  @<Swap the $x$ and $y$ parameters in the bounding box of |h|@>;
27953} else if (number_nonzero(mp->txy) || number_nonzero(mp->tyx)) {
27954  mp_init_bbox (mp, h);
27955  goto DONE1;
27956}
27957if (number_lessequal (h->minx, h->maxx)) {
27958  @<Scale the bounding box by |txx+txy| and |tyx+tyy|; then shift by
27959   |(tx,ty)|@>;
27960}
27961DONE1:
27962
27963
27964@ @<Swap the $x$ and $y$ parameters in the bounding box of |h|@>=
27965{
27966  number_swap(h->minx, h->miny);
27967  number_swap(h->maxx, h->maxy);
27968}
27969
27970
27971@ The sum ``|txx+txy|'' is whichever of |txx| or |txy| is nonzero.  The other
27972sum is similar.
27973
27974@<Scale the bounding box by |txx+txy| and |tyx+tyy|; then shift...@>=
27975{
27976  mp_number tot, ret;
27977  new_number(tot);
27978  new_number (ret);
27979  set_number_from_addition(tot,mp->txx,mp->txy);
27980  take_scaled (ret, h->minx, tot);
27981  set_number_from_addition(h->minx,ret, mp->tx);
27982  take_scaled (ret, h->maxx, tot);
27983  set_number_from_addition(h->maxx,ret, mp->tx);
27984
27985  set_number_from_addition(tot,mp->tyx,mp->tyy);
27986  take_scaled (ret, h->miny, tot);
27987  set_number_from_addition(h->miny, ret, mp->ty);
27988  take_scaled (ret, h->maxy, tot);
27989  set_number_from_addition(h->maxy, ret, mp->ty);
27990
27991  set_number_from_addition(tot, mp->txx, mp->txy);
27992  if (number_negative(tot)) {
27993    number_swap(h->minx, h->maxx);
27994  }
27995  set_number_from_addition(tot, mp->tyx, mp->tyy);
27996  if (number_negative(tot)) {
27997    number_swap(h->miny, h->maxy);
27998  }
27999  free_number (ret);
28000  free_number (tot);
28001}
28002
28003
28004@ Now we ready for the main task of transforming the graphical objects in edge
28005structure~|h|.
28006
28007@<Transform graphical object |q|@>=
28008switch (mp_type (q)) {
28009case mp_fill_node_type:
28010  {
28011    mp_fill_node qq = (mp_fill_node) q;
28012    mp_do_path_trans (mp, mp_path_p (qq));
28013    @<Transform |mp_pen_p(qq)|, making sure polygonal pens stay counter-clockwise@>;
28014  }
28015  break;
28016case mp_stroked_node_type:
28017  {
28018    mp_stroked_node qq = (mp_stroked_node) q;
28019    mp_do_path_trans (mp, mp_path_p (qq));
28020    @<Transform |mp_pen_p(qq)|, making sure polygonal pens stay counter-clockwise@>;
28021  }
28022  break;
28023case mp_start_clip_node_type:
28024  mp_do_path_trans (mp, mp_path_p ((mp_start_clip_node) q));
28025  break;
28026case mp_start_bounds_node_type:
28027  mp_do_path_trans (mp, mp_path_p ((mp_start_bounds_node) q));
28028  break;
28029case mp_text_node_type:
28030  @<Transform the compact transformation@>;
28031  break;
28032case mp_stop_clip_node_type:
28033case mp_stop_bounds_node_type:
28034  break;
28035default:                       /* there are no other valid cases, but please the compiler */
28036  break;
28037}
28038
28039
28040@ Note that the shift parameters |(tx,ty)| apply only to the path being stroked.
28041The |dash_scale| has to be adjusted  to scale the dash lengths in |mp_dash_p(q)|
28042since the \ps\ output procedures will try to compensate for the transformation
28043we are applying to |mp_pen_p(q)|.  Since this compensation is based on the square
28044root of the determinant, |sqdet| is the appropriate factor.
28045
28046We pass the mptrap test only if |dash_scale| is not adjusted, nowadays
28047(backend is changed?)
28048
28049@<Transform |mp_pen_p(qq)|, making sure...@>=
28050if (mp_pen_p (qq) != NULL) {
28051  number_clone(sx, mp->tx);
28052  number_clone(sy, mp->ty);
28053  set_number_to_zero(mp->tx);
28054  set_number_to_zero(mp->ty);
28055  mp_do_pen_trans (mp, mp_pen_p (qq));
28056  if (number_nonzero(sqdet)
28057      && ((mp_type (q) == mp_stroked_node_type) && (mp_dash_p (q) != NULL))) {
28058    mp_number ret;
28059    new_number (ret);
28060    take_scaled (ret, ((mp_stroked_node)q)->dash_scale, sqdet);
28061    number_clone(((mp_stroked_node)q)->dash_scale, ret);
28062    free_number (ret);
28063  }
28064  if (!pen_is_elliptical (mp_pen_p (qq)))
28065    if (number_negative(sgndet))
28066      mp_pen_p (qq) = mp_make_pen (mp, mp_copy_path (mp, mp_pen_p (qq)), true);
28067  /* this unreverses the pen */
28068  number_clone(mp->tx, sx);
28069  number_clone(mp->ty, sy);
28070}
28071
28072@ @<Transform the compact transformation@>=
28073mp_number_trans (mp, &((mp_text_node)q)->tx, &((mp_text_node)q)->ty);
28074number_clone(sx, mp->tx);
28075number_clone(sy, mp->ty);
28076set_number_to_zero(mp->tx);
28077set_number_to_zero(mp->ty);
28078mp_number_trans (mp, &((mp_text_node)q)->txx, &((mp_text_node)q)->tyx);
28079mp_number_trans (mp, &((mp_text_node)q)->txy, &((mp_text_node)q)->tyy);
28080number_clone(mp->tx, sx);
28081number_clone(mp->ty, sy)
28082
28083@ The hard cases of transformation occur when big nodes are involved,
28084and when some of their components are unknown.
28085
28086@<Declare binary action...@>=
28087@<Declare subroutines needed by |big_trans|@>;
28088static void mp_big_trans (MP mp, mp_node p, quarterword c) {
28089  mp_node q, r, pp, qq; /* list manipulation registers */
28090  q = value_node (p);
28091  if (mp_type (q) == mp_pair_node_type) {
28092    if (mp_type (x_part (q)) != mp_known ||
28093        mp_type (y_part (q)) != mp_known) {
28094      @<Transform an unknown big node and |return|@>;
28095    }
28096  } else {                      /* |mp_transform_type| */
28097    if (mp_type (tx_part (q)) != mp_known ||
28098        mp_type (ty_part (q)) != mp_known ||
28099        mp_type (xx_part (q)) != mp_known ||
28100        mp_type (xy_part (q)) != mp_known ||
28101        mp_type (yx_part (q)) != mp_known ||
28102        mp_type (yy_part (q)) != mp_known) {
28103      @<Transform an unknown big node and |return|@>;
28104    }
28105  }
28106  @<Transform a known big node@>;
28107}                               /* node |p| will now be recycled by |do_binary| */
28108
28109
28110@ @<Transform an unknown big node and |return|@>=
28111{
28112  mp_set_up_known_trans (mp, c);
28113  mp_make_exp_copy (mp, p);
28114  r = value_node (cur_exp_node ());
28115  if (mp->cur_exp.type == mp_transform_type) {
28116    mp_bilin1 (mp, yy_part (r), mp->tyy, xy_part (q), mp->tyx, zero_t);
28117    mp_bilin1 (mp, yx_part (r), mp->tyy, xx_part (q), mp->tyx, zero_t);
28118    mp_bilin1 (mp, xy_part (r), mp->txx, yy_part (q), mp->txy, zero_t);
28119    mp_bilin1 (mp, xx_part (r), mp->txx, yx_part (q), mp->txy, zero_t);
28120  }
28121  mp_bilin1 (mp, y_part (r), mp->tyy, x_part (q), mp->tyx, mp->ty);
28122  mp_bilin1 (mp, x_part (r), mp->txx, y_part (q), mp->txy, mp->tx);
28123  return;
28124}
28125
28126
28127@ Let |p| point to a value field inside a big node of |cur_exp|,
28128and let |q| point to a another value field. The |bilin1| procedure
28129replaces |p| by $p\cdot t+q\cdot u+\delta$.
28130
28131@<Declare subroutines needed by |big_trans|@>=
28132static void mp_bilin1 (MP mp, mp_node p, mp_number t, mp_node q,
28133                       mp_number u, mp_number delta_orig) {
28134  mp_number delta;
28135  new_number (delta);
28136  number_clone (delta, delta_orig);
28137  if (!number_equal(t, unity_t)) {
28138    mp_dep_mult (mp, (mp_value_node) p, t, true);
28139  }
28140  if (number_nonzero(u)) {
28141    if (mp_type (q) == mp_known) {
28142      mp_number tmp;
28143      new_number (tmp);
28144      take_scaled (tmp, value_number (q), u);
28145      number_add (delta, tmp);
28146      free_number (tmp);
28147    } else {
28148      /* Ensure that |type(p)=mp_proto_dependent| */
28149      if (mp_type (p) != mp_proto_dependent) {
28150        if (mp_type (p) == mp_known) {
28151          mp_new_dep (mp, p, mp_type (p), mp_const_dependency (mp, value_number (p)));
28152        } else {
28153          set_dep_list ((mp_value_node) p,
28154            mp_p_times_v (mp,
28155                                    (mp_value_node) dep_list ((mp_value_node)
28156                                                              p), unity_t,
28157                                    mp_dependent, mp_proto_dependent, true));
28158        }
28159        mp_type (p) = mp_proto_dependent;
28160      }
28161      set_dep_list ((mp_value_node) p,
28162        mp_p_plus_fq (mp,
28163                                (mp_value_node) dep_list ((mp_value_node) p), u,
28164                                (mp_value_node) dep_list ((mp_value_node) q),
28165                                mp_proto_dependent, mp_type (q)));
28166    }
28167  }
28168  if (mp_type (p) == mp_known) {
28169    set_value_number (p, value_number (p));
28170    number_add (value_number (p), delta);
28171  } else {
28172    mp_number tmp;
28173    mp_value_node r;    /* list traverser */
28174    new_number (tmp);
28175    r = (mp_value_node) dep_list ((mp_value_node) p);
28176    while (dep_info (r) != NULL)
28177      r = (mp_value_node) mp_link (r);
28178    number_clone (tmp, value_number(r));
28179    number_add (delta, tmp);
28180    if (r != (mp_value_node) dep_list ((mp_value_node) p))
28181      set_value_number (r, delta);
28182    else {
28183      mp_recycle_value (mp, p);
28184      mp_type (p) = mp_known;
28185      set_value_number (p, delta);
28186    }
28187    free_number (tmp);
28188  }
28189  if (mp->fix_needed)
28190    mp_fix_dependencies (mp);
28191  free_number (delta);
28192}
28193
28194
28195@ @<Transform a known big node@>=
28196mp_set_up_trans (mp, c);
28197if (mp->cur_exp.type == mp_known) {
28198  @<Transform known by known@>;
28199} else {
28200  pp = mp_stash_cur_exp (mp);
28201  qq = value_node (pp);
28202  mp_make_exp_copy (mp, p);
28203  r = value_node (cur_exp_node ());
28204  if (mp->cur_exp.type == mp_transform_type) {
28205    mp_bilin2 (mp, yy_part (r), yy_part (qq), value_number (xy_part (q)),
28206               yx_part (qq), NULL);
28207    mp_bilin2 (mp, yx_part (r), yy_part (qq), value_number (xx_part (q)),
28208               yx_part (qq), NULL);
28209    mp_bilin2 (mp, xy_part (r), xx_part (qq), value_number (yy_part (q)),
28210               xy_part (qq), NULL);
28211    mp_bilin2 (mp, xx_part (r), xx_part (qq), value_number (yx_part (q)),
28212               xy_part (qq), NULL);
28213  }
28214  mp_bilin2 (mp, y_part (r), yy_part (qq), value_number (x_part (q)),
28215             yx_part (qq), y_part (qq));
28216  mp_bilin2 (mp, x_part (r), xx_part (qq), value_number (y_part (q)),
28217             xy_part (qq), x_part (qq));
28218  mp_recycle_value (mp, pp);
28219  mp_free_value_node (mp, pp);
28220}
28221
28222
28223@ Let |p| be a |mp_proto_dependent| value whose dependency list ends
28224at |dep_final|. The following procedure adds |v| times another
28225numeric quantity to~|p|.
28226
28227@<Declare subroutines needed by |big_trans|@>=
28228static void mp_add_mult_dep (MP mp, mp_value_node p, mp_number v, mp_node r) {
28229  if (mp_type (r) == mp_known) {
28230    mp_number ret;
28231    new_number (ret);
28232    take_scaled (ret, value_number (r), v);
28233    set_dep_value (mp->dep_final, dep_value (mp->dep_final));
28234    number_add (dep_value (mp->dep_final), ret);
28235    free_number (ret);
28236  } else {
28237    set_dep_list (p,
28238      mp_p_plus_fq (mp, (mp_value_node) dep_list (p), v,
28239                              (mp_value_node) dep_list ((mp_value_node) r),
28240                              mp_proto_dependent, mp_type (r)));
28241    if (mp->fix_needed)
28242      mp_fix_dependencies (mp);
28243  }
28244}
28245
28246
28247@ The |bilin2| procedure is something like |bilin1|, but with known
28248and unknown quantities reversed. Parameter |p| points to a value field
28249within the big node for |cur_exp|; and |type(p)=mp_known|. Parameters
28250|t| and~|u| point to value fields elsewhere; so does parameter~|q|,
28251unless it is |NULL| (which stands for zero). Location~|p| will be
28252replaced by $p\cdot t+v\cdot u+q$.
28253
28254@<Declare subroutines needed by |big_trans|@>=
28255static void mp_bilin2 (MP mp, mp_node p, mp_node t, mp_number v,
28256                       mp_node u, mp_node q) {
28257  mp_number vv;    /* temporary storage for |value(p)| */
28258  new_number (vv);
28259  number_clone (vv, value_number (p));
28260  mp_new_dep (mp, p, mp_proto_dependent, mp_const_dependency (mp, zero_t));  /* this sets |dep_final| */
28261  if (number_nonzero(vv)) {
28262    mp_add_mult_dep (mp, (mp_value_node) p, vv, t);     /* |dep_final| doesn't change */
28263  }
28264  if (number_nonzero(v)) {
28265    mp_number arg1;
28266    new_number (arg1);
28267    number_clone (arg1, v);
28268    mp_add_mult_dep (mp, (mp_value_node) p, arg1, u);
28269    free_number (arg1);
28270  }
28271  if (q != NULL)
28272    mp_add_mult_dep (mp, (mp_value_node) p, unity_t, q);
28273  if (dep_list ((mp_value_node) p) == (mp_node) mp->dep_final) {
28274    number_clone (vv, dep_value (mp->dep_final));
28275    mp_recycle_value (mp, p);
28276    mp_type (p) = mp_known;
28277    set_value_number (p, vv);
28278  }
28279  free_number (vv);
28280}
28281
28282
28283@ @<Transform known by known@>=
28284{
28285  mp_make_exp_copy (mp, p);
28286  r = value_node (cur_exp_node ());
28287  if (mp->cur_exp.type == mp_transform_type) {
28288    mp_bilin3 (mp, yy_part (r), mp->tyy, value_number (xy_part (q)), mp->tyx, zero_t);
28289    mp_bilin3 (mp, yx_part (r), mp->tyy, value_number (xx_part (q)), mp->tyx, zero_t);
28290    mp_bilin3 (mp, xy_part (r), mp->txx, value_number (yy_part (q)), mp->txy, zero_t);
28291    mp_bilin3 (mp, xx_part (r), mp->txx, value_number (yx_part (q)), mp->txy, zero_t);
28292  }
28293  mp_bilin3 (mp, y_part (r), mp->tyy, value_number (x_part (q)), mp->tyx, mp->ty);
28294  mp_bilin3 (mp, x_part (r), mp->txx, value_number (y_part (q)), mp->txy, mp->tx);
28295}
28296
28297
28298@ Finally, in |bilin3| everything is |known|.
28299
28300@<Declare subroutines needed by |big_trans|@>=
28301static void mp_bilin3 (MP mp, mp_node p, mp_number t,
28302                       mp_number v, mp_number u, mp_number delta_orig) {
28303  mp_number delta;
28304  mp_number tmp;
28305  new_number (tmp);
28306  new_number (delta);
28307  number_clone (delta, delta_orig);
28308  if (!number_equal(t, unity_t)) {
28309    take_scaled (tmp, value_number (p), t);
28310  } else {
28311    number_clone (tmp, value_number (p));
28312  }
28313  number_add (delta, tmp);
28314  if (number_nonzero(u)) {
28315    mp_number ret;
28316    new_number (ret);
28317    take_scaled (ret, v, u);
28318    set_value_number (p, delta);
28319    number_add (value_number (p), ret);
28320    free_number (ret);
28321  } else
28322    set_value_number (p, delta);
28323  free_number (tmp);
28324  free_number (delta);
28325}
28326
28327
28328@ @<Declare binary action...@>=
28329static void mp_chop_path (MP mp, mp_node p) {
28330  mp_knot q;    /* a knot in the original path */
28331  mp_knot pp, qq, rr, ss;       /* link variables for copies of path nodes */
28332  mp_number a, b;    /* indices for chopping */
28333  mp_number l;
28334  boolean reversed;     /* was |a>b|? */
28335  new_number (a);
28336  new_number (b);
28337  new_number (l);
28338  mp_path_length (mp, &l);
28339  number_clone (a, value_number (x_part (p)));
28340  number_clone (b, value_number (y_part (p)));
28341  if (number_lessequal(a, b)) {
28342    reversed = false;
28343  } else {
28344    reversed = true;
28345    number_swap (a, b);
28346  }
28347  /* Dispense with the cases |a<0| and/or |b>l| */
28348  if (number_negative(a)) {
28349    if (mp_left_type (cur_exp_knot ()) == mp_endpoint) {
28350      set_number_to_zero(a);
28351      if (number_negative(b))
28352        set_number_to_zero(b);
28353    } else {
28354      do {
28355        number_add (a, l);
28356        number_add (b, l);
28357      } while (number_negative(a));            /* a cycle always has length |l>0| */
28358    }
28359  }
28360  if (number_greater (b, l)) {
28361    if (mp_left_type (cur_exp_knot ()) == mp_endpoint) {
28362      number_clone (b, l);
28363      if (number_greater (a, l))
28364        number_clone(a, l);
28365    } else {
28366      while (number_greaterequal (a, l)) {
28367        number_substract (a, l);
28368        number_substract (b, l);
28369      }
28370    }
28371  }
28372
28373  q = cur_exp_knot ();
28374  while (number_greaterequal(a, unity_t)) {
28375    q = mp_next_knot (q);
28376    number_substract(a, unity_t);
28377    number_substract(b, unity_t);
28378  }
28379  if (number_equal(b, a)) {
28380    /* Construct a path from |pp| to |qq| of length zero */
28381    if (number_positive (a)) {
28382      mp_number arg1;
28383      new_number (arg1);
28384      number_clone (arg1, a);
28385      convert_scaled_to_fraction (arg1);
28386      mp_split_cubic (mp, q, arg1);
28387      free_number (arg1);
28388      q = mp_next_knot (q);
28389    }
28390    pp = mp_copy_knot (mp, q);
28391    qq = pp;
28392
28393  } else {
28394    /* Construct a path from |pp| to |qq| of length $\lceil b\rceil$ */
28395    pp = mp_copy_knot (mp, q);
28396    qq = pp;
28397    do {
28398      q = mp_next_knot (q);
28399      rr = qq;
28400      qq = mp_copy_knot (mp, q);
28401      mp_next_knot (rr) = qq;
28402      number_substract (b, unity_t);
28403    } while (number_positive (b));
28404    if (number_positive (a)) {
28405      mp_number arg1;
28406      new_number (arg1);
28407      ss = pp;
28408      number_clone (arg1, a);
28409      convert_scaled_to_fraction (arg1);
28410      mp_split_cubic (mp, ss, arg1);
28411      free_number (arg1);
28412      pp = mp_next_knot (ss);
28413      mp_toss_knot (mp, ss);
28414      if (rr == ss) {
28415        mp_number arg1, arg2;
28416        new_number (arg1);
28417        new_number (arg2);
28418        set_number_from_substraction (arg1, unity_t, a);
28419        number_clone (arg2, b);
28420        make_scaled (b, arg2, arg1);
28421        free_number (arg1);
28422        free_number (arg2);
28423        rr = pp;
28424      }
28425    }
28426    if (number_negative (b)) {
28427      mp_number arg1;
28428      new_number (arg1);
28429      set_number_from_addition (arg1, b, unity_t);
28430      convert_scaled_to_fraction (arg1);
28431      mp_split_cubic (mp, rr, arg1);
28432      free_number (arg1);
28433      mp_toss_knot (mp, qq);
28434      qq = mp_next_knot (rr);
28435    }
28436
28437  }
28438  mp_left_type (pp) = mp_endpoint;
28439  mp_right_type (qq) = mp_endpoint;
28440  mp_next_knot (qq) = pp;
28441  mp_toss_knot_list (mp, cur_exp_knot ());
28442  if (reversed) {
28443    set_cur_exp_knot (mp_next_knot (mp_htap_ypoc (mp, pp)));
28444    mp_toss_knot_list (mp, pp);
28445  } else {
28446    set_cur_exp_knot (pp);
28447  }
28448  free_number (l);
28449  free_number (a);
28450  free_number (b);
28451}
28452
28453
28454@ @<Declare binary action...@>=
28455static void mp_set_up_offset (MP mp, mp_node p) {
28456  mp_find_offset (mp, value_number (x_part (p)), value_number (y_part (p)),
28457                  cur_exp_knot ());
28458  mp_pair_value (mp, mp->cur_x, mp->cur_y);
28459}
28460static void mp_set_up_direction_time (MP mp, mp_node p) {
28461  mp_value new_expr;
28462  memset(&new_expr,0,sizeof(mp_value));
28463  new_number (new_expr.data.n);
28464  mp_find_direction_time (mp, &new_expr.data.n, value_number (x_part (p)),
28465                                              value_number (y_part (p)),
28466                                              cur_exp_knot ());
28467  mp_flush_cur_exp (mp, new_expr);
28468}
28469static void mp_set_up_envelope (MP mp, mp_node p) {
28470  unsigned char ljoin, lcap;
28471  mp_number miterlim;
28472  mp_knot q = mp_copy_path (mp, cur_exp_knot ());       /* the original path */
28473  new_number(miterlim);
28474  /* TODO: accept elliptical pens for straight paths */
28475  if (pen_is_elliptical (value_knot (p))) {
28476    mp_bad_envelope_pen (mp);
28477    set_cur_exp_knot (q);
28478    mp->cur_exp.type = mp_path_type;
28479    return;
28480  }
28481  if (number_greater (internal_value (mp_linejoin), unity_t))
28482    ljoin = 2;
28483  else if (number_positive (internal_value (mp_linejoin)))
28484    ljoin = 1;
28485  else
28486    ljoin = 0;
28487  if (number_greater (internal_value (mp_linecap), unity_t))
28488    lcap = 2;
28489  else if (number_positive (internal_value (mp_linecap)))
28490    lcap = 1;
28491  else
28492    lcap = 0;
28493  if (number_less (internal_value (mp_miterlimit), unity_t))
28494    set_number_to_unity(miterlim);
28495  else
28496    number_clone(miterlim, internal_value (mp_miterlimit));
28497  set_cur_exp_knot (mp_make_envelope
28498                    (mp, q, value_knot (p), ljoin, lcap, miterlim));
28499  mp->cur_exp.type = mp_path_type;
28500}
28501
28502
28503@ This is pretty straightfoward. The one silly thing is that
28504the output of |mp_ps_do_font_charstring| has to be un-exported.
28505
28506@<Declare binary action...@>=
28507static void mp_set_up_glyph_infont (MP mp, mp_node p) {
28508  mp_edge_object *h = NULL;
28509  mp_ps_font *f = NULL;
28510  char *n = mp_str (mp, cur_exp_str ());
28511  f = mp_ps_font_parse (mp, (int) mp_find_font (mp, n));
28512  if (f != NULL) {
28513    if (mp_type (p) == mp_known) {
28514      int v = round_unscaled (value_number (p));
28515      if (v < 0 || v > 255) {
28516        char msg[256];
28517        mp_snprintf (msg, 256, "glyph index too high (%d)", v);
28518        mp_error (mp, msg, NULL, true);
28519      } else {
28520        h = mp_ps_font_charstring (mp, f, v);
28521      }
28522    } else {
28523      n = mp_str (mp, value_str (p));
28524      h = mp_ps_do_font_charstring (mp, f, n);
28525    }
28526    mp_ps_font_free (mp, f);
28527  }
28528  if (h != NULL) {
28529    set_cur_exp_node ((mp_node)mp_gr_import (mp, h));
28530  } else {
28531    set_cur_exp_node ((mp_node)mp_get_edge_header_node (mp));
28532    mp_init_edges (mp, (mp_edge_header_node)cur_exp_node ());
28533  }
28534  mp->cur_exp.type = mp_picture_type;
28535}
28536
28537
28538@ @<Declare binary action...@>=
28539static void mp_find_point (MP mp, mp_number v_orig, quarterword c) {
28540  mp_knot p;    /* the path */
28541  mp_number n;     /* its length */
28542  mp_number v;
28543  new_number (v);
28544  new_number (n);
28545  number_clone (v, v_orig);
28546  p = cur_exp_knot ();
28547  if (mp_left_type (p) == mp_endpoint) {
28548    set_number_to_unity (n);
28549    number_negate (n);
28550  } else {
28551    set_number_to_zero (n);
28552  }
28553  do {
28554    p = mp_next_knot (p);
28555    number_add (n, unity_t);
28556  } while (p != cur_exp_knot ());
28557  if (number_zero (n)) {
28558    set_number_to_zero(v);
28559  } else if (number_negative(v)) {
28560    if (mp_left_type (p) == mp_endpoint) {
28561      set_number_to_zero(v);
28562    } else  {
28563      /* |v = n - 1 - ((-v - 1) % n)
28564          == - ((-v - 1) % n) - 1 + n| */
28565      number_negate (v);
28566      number_add_scaled (v, -1);
28567      number_modulo (v, n);
28568      number_negate (v);
28569      number_add_scaled (v, -1);
28570      number_add (v, n);
28571    }
28572  } else if (number_greater(v, n)) {
28573    if (mp_left_type (p) == mp_endpoint)
28574      number_clone (v, n);
28575    else
28576      number_modulo (v, n);
28577  }
28578  p = cur_exp_knot ();
28579  while (number_greaterequal(v, unity_t)) {
28580    p = mp_next_knot (p);
28581    number_substract (v, unity_t);
28582  }
28583  if (number_nonzero(v)) {
28584    /* Insert a fractional node by splitting the cubic */
28585    convert_scaled_to_fraction (v);
28586    mp_split_cubic (mp, p, v);
28587    p = mp_next_knot (p);
28588  }
28589  /* Set the current expression to the desired path coordinates */
28590  switch (c) {
28591  case mp_point_of:
28592    mp_pair_value (mp, p->x_coord, p->y_coord);
28593    break;
28594  case mp_precontrol_of:
28595    if (mp_left_type (p) == mp_endpoint)
28596      mp_pair_value (mp, p->x_coord, p->y_coord);
28597    else
28598      mp_pair_value (mp, p->left_x, p->left_y);
28599    break;
28600  case mp_postcontrol_of:
28601    if (mp_right_type (p) == mp_endpoint)
28602      mp_pair_value (mp, p->x_coord, p->y_coord);
28603    else
28604      mp_pair_value (mp, p->right_x, p->right_y);
28605    break;
28606  }  /* there are no other cases */
28607  free_number (v);
28608  free_number (n);
28609}
28610
28611@ Function |new_text_node| owns the reference count for its second argument
28612(the text string) but not its first (the font name).
28613
28614@<Declare binary action...@>=
28615static void mp_do_infont (MP mp, mp_node p) {
28616  mp_edge_header_node q;
28617  mp_value new_expr;
28618  memset(&new_expr,0,sizeof(mp_value));
28619  new_number(new_expr.data.n);
28620  q = mp_get_edge_header_node (mp);
28621  mp_init_edges (mp, q);
28622  add_str_ref (cur_exp_str());
28623  mp_link (obj_tail (q)) =
28624    mp_new_text_node (mp, mp_str (mp, cur_exp_str ()), value_str (p));
28625  obj_tail (q) = mp_link (obj_tail (q));
28626  mp_free_value_node (mp, p);
28627  new_expr.data.node = (mp_node)q;
28628  mp_flush_cur_exp (mp, new_expr);
28629  mp->cur_exp.type = mp_picture_type;
28630}
28631
28632
28633@* Statements and commands.
28634The chief executive of \MP\ is the |do_statement| routine, which
28635contains the master switch that causes all the various pieces of \MP\
28636to do their things, in the right order.
28637
28638In a sense, this is the grand climax of the program: It applies all the
28639tools that we have worked so hard to construct. In another sense, this is
28640the messiest part of the program: It necessarily refers to other pieces
28641of code all over the place, so that a person can't fully understand what is
28642going on without paging back and forth to be reminded of conventions that
28643are defined elsewhere. We are now at the hub of the web.
28644
28645The structure of |do_statement| itself is quite simple.  The first token
28646of the statement is fetched using |get_x_next|.  If it can be the first
28647token of an expression, we look for an equation, an assignment, or a
28648title. Otherwise we use a \&{case} construction to branch at high speed to
28649the appropriate routine for various and sundry other types of commands,
28650each of which has an ``action procedure'' that does the necessary work.
28651
28652The program uses the fact that
28653$$\hbox{|min_primary_command=max_statement_command=type_name|}$$
28654to interpret a statement that starts with, e.g., `\&{string}',
28655as a type declaration rather than a boolean expression.
28656
28657@c
28658static void worry_about_bad_statement (MP mp);
28659static void flush_unparsable_junk_after_statement (MP mp);
28660void mp_do_statement (MP mp) {                               /* governs \MP's activities */
28661  mp->cur_exp.type = mp_vacuous;
28662  mp_get_x_next (mp);
28663  if (cur_cmd() > mp_max_primary_command) {
28664    worry_about_bad_statement (mp);
28665  } else if (cur_cmd() > mp_max_statement_command) {
28666    /* Do an equation, assignment, title, or
28667     `$\langle\,$expression$\,\rangle\,$\&{endgroup}'; */
28668    /* The most important statements begin with expressions */
28669    mp_value new_expr;
28670    mp->var_flag = mp_assignment;
28671    mp_scan_expression (mp);
28672    if (cur_cmd() < mp_end_group) {
28673      if (cur_cmd() == mp_equals)
28674        mp_do_equation (mp);
28675      else if (cur_cmd() == mp_assignment)
28676        mp_do_assignment (mp);
28677      else if (mp->cur_exp.type == mp_string_type) {
28678        /* Do a title */
28679        if (number_positive (internal_value (mp_tracing_titles))) {
28680          mp_print_nl (mp, "");
28681          mp_print_str (mp, cur_exp_str ());
28682          update_terminal();
28683        }
28684      } else if (mp->cur_exp.type != mp_vacuous) {
28685        const char *hlp[] = {
28686             "I couldn't find an `=' or `:=' after the",
28687             "expression that is shown above this error message,",
28688             "so I guess I'll just ignore it and carry on.",
28689             NULL };
28690        mp_disp_err(mp, NULL);
28691        mp_back_error (mp, "Isolated expression", hlp, true);
28692        mp_get_x_next (mp);
28693      }
28694      memset(&new_expr,0,sizeof(mp_value));
28695      new_number(new_expr.data.n);
28696      set_number_to_zero (new_expr.data.n);
28697      mp_flush_cur_exp (mp, new_expr);
28698      mp->cur_exp.type = mp_vacuous;
28699    }
28700  } else {
28701    /* Do a statement that doesn't begin with an expression */
28702    /* If |do_statement| ends with |cur_cmd=end_group|, we should have
28703       |cur_type=mp_vacuous| unless the statement was simply an expression;
28704       in the latter case, |cur_type| and |cur_exp| should represent that
28705       expression. */
28706    if (number_positive (internal_value (mp_tracing_commands)))
28707      show_cur_cmd_mod;
28708    switch (cur_cmd()) {
28709    case mp_type_name:
28710      mp_do_type_declaration (mp);
28711      break;
28712    case mp_macro_def:
28713      if (cur_mod() > var_def)
28714        mp_make_op_def (mp);
28715      else if (cur_mod() > end_def)
28716        mp_scan_def (mp);
28717      break;
28718    case mp_random_seed:
28719      mp_do_random_seed (mp);
28720      break;
28721    case mp_mode_command:
28722      mp_print_ln (mp);
28723      mp->interaction = cur_mod();
28724      initialize_print_selector();
28725      if (mp->log_opened)
28726        mp->selector = mp->selector + 2;
28727      mp_get_x_next (mp);
28728      break;
28729    case mp_protection_command:
28730      mp_do_protection (mp);
28731      break;
28732    case mp_delimiters:
28733      mp_def_delims (mp);
28734      break;
28735    case mp_save_command:
28736      do {
28737        mp_get_symbol (mp);
28738        mp_save_variable (mp, cur_sym());
28739        mp_get_x_next (mp);
28740      } while (cur_cmd() == mp_comma);
28741      break;
28742    case mp_interim_command:
28743      mp_do_interim (mp);
28744      break;
28745    case mp_let_command:
28746      mp_do_let (mp);
28747      break;
28748    case mp_new_internal:
28749      mp_do_new_internal (mp);
28750      break;
28751    case mp_show_command:
28752      mp_do_show_whatever (mp);
28753      break;
28754    case mp_add_to_command:
28755      mp_do_add_to (mp);
28756      break;
28757    case mp_bounds_command:
28758      mp_do_bounds (mp);
28759      break;
28760    case mp_ship_out_command:
28761      mp_do_ship_out (mp);
28762      break;
28763    case mp_every_job_command:
28764      mp_get_symbol (mp);
28765      mp->start_sym = cur_sym();
28766      mp_get_x_next (mp);
28767      break;
28768    case mp_message_command:
28769      mp_do_message (mp);
28770      break;
28771    case mp_write_command:
28772      mp_do_write (mp);
28773      break;
28774    case mp_tfm_command:
28775      mp_do_tfm_command (mp);
28776      break;
28777    case mp_special_command:
28778      if (cur_mod() == 0)
28779        mp_do_special (mp);
28780      else if (cur_mod() == 1)
28781        mp_do_mapfile (mp);
28782      else
28783        mp_do_mapline (mp);
28784      break;
28785    default:
28786      break; /* make the compiler happy */
28787    }
28788    mp->cur_exp.type = mp_vacuous;
28789  }
28790  if (cur_cmd() < mp_semicolon)
28791    flush_unparsable_junk_after_statement(mp);
28792  mp->error_count = 0;
28793}
28794
28795
28796@ @<Declarations@>=
28797@<Declare action procedures for use by |do_statement|@>
28798
28799
28800@ The only command codes |>max_primary_command| that can be present
28801at the beginning of a statement are |semicolon| and higher; these
28802occur when the statement is null.
28803
28804@c
28805static void worry_about_bad_statement (MP mp) {
28806  if (cur_cmd() < mp_semicolon) {
28807    char msg[256];
28808    mp_string sname;
28809    int old_setting = mp->selector;
28810    const char *hlp[] = {
28811           "I was looking for the beginning of a new statement.",
28812           "If you just proceed without changing anything, I'll ignore",
28813           "everything up to the next `;'. Please insert a semicolon",
28814           "now in front of anything that you don't want me to delete.",
28815           "(See Chapter 27 of The METAFONTbook for an example.)",
28816           NULL };
28817    mp->selector = new_string;
28818    mp_print_cmd_mod (mp, cur_cmd(), cur_mod());
28819    sname = mp_make_string(mp);
28820    mp->selector = old_setting;
28821    mp_snprintf (msg, 256, "A statement can't begin with `%s'", mp_str(mp, sname));
28822    delete_str_ref(sname);
28823    mp_back_error (mp, msg, hlp, true);
28824    mp_get_x_next (mp);
28825  }
28826}
28827
28828
28829@ The help message printed here says that everything is flushed up to
28830a semicolon, but actually the commands |end_group| and |stop| will
28831also terminate a statement.
28832
28833@c
28834static void flush_unparsable_junk_after_statement (MP mp)
28835{
28836  const char *hlp[] = {
28837         "I've just read as much of that statement as I could fathom,",
28838         "so a semicolon should have been next. It's very puzzling...",
28839         "but I'll try to get myself back together, by ignoring",
28840         "everything up to the next `;'. Please insert a semicolon",
28841         "now in front of anything that you don't want me to delete.",
28842         "(See Chapter 27 of The METAFONTbook for an example.)",
28843          NULL };
28844  mp_back_error (mp, "Extra tokens will be flushed", hlp, true);
28845  mp->scanner_status = flushing;
28846  do {
28847    get_t_next (mp);
28848    if (cur_cmd() == mp_string_token) {
28849      delete_str_ref (cur_mod_str());
28850    }
28851  } while (!mp_end_of_statement);  /* |cur_cmd=semicolon|, |end_group|, or |stop| */
28852  mp->scanner_status = normal;
28853}
28854
28855
28856
28857@ Equations and assignments are performed by the pair of mutually recursive
28858@^recursion@>
28859routines |do_equation| and |do_assignment|. These routines are called when
28860|cur_cmd=equals| and when |cur_cmd=assignment|, respectively; the left-hand
28861side is in |cur_type| and |cur_exp|, while the right-hand side is yet
28862to be scanned. After the routines are finished, |cur_type| and |cur_exp|
28863will be equal to the right-hand side (which will normally be equal
28864to the left-hand side).
28865
28866@<Declarations@>=
28867@<Declare the procedure called |make_eq|@>;
28868static void mp_do_equation (MP mp);
28869
28870@ @c
28871static void trace_equation (MP mp, mp_node lhs) {
28872  mp_begin_diagnostic (mp);
28873  mp_print_nl (mp, "{(");
28874  mp_print_exp (mp, lhs, 0);
28875  mp_print (mp, ")=(");
28876  mp_print_exp (mp, NULL, 0);
28877  mp_print (mp, ")}");
28878  mp_end_diagnostic (mp, false);
28879}
28880void mp_do_equation (MP mp) {
28881  mp_node lhs;  /* capsule for the left-hand side */
28882  lhs = mp_stash_cur_exp (mp);
28883  mp_get_x_next (mp);
28884  mp->var_flag = mp_assignment;
28885  mp_scan_expression (mp);
28886  if (cur_cmd() == mp_equals)
28887    mp_do_equation (mp);
28888  else if (cur_cmd() == mp_assignment)
28889    mp_do_assignment (mp);
28890  if (number_greater (internal_value (mp_tracing_commands), two_t)) {
28891    trace_equation(mp, lhs);
28892  }
28893  if (mp->cur_exp.type == mp_unknown_path) {
28894    if (mp_type (lhs) == mp_pair_type) {
28895      mp_node p;    /* temporary register */
28896      p = mp_stash_cur_exp (mp);
28897      mp_unstash_cur_exp (mp, lhs);
28898      lhs = p;
28899    }  /* in this case |make_eq| will change the pair to a path */
28900  }
28901  mp_make_eq (mp, lhs); /* equate |lhs| to |(cur_type,cur_exp)| */
28902}
28903
28904
28905@ And |do_assignment| is similar to |do_equation|:
28906
28907@<Declarations@>=
28908static void mp_do_assignment (MP mp);
28909
28910@ @c
28911static void bad_lhs (MP mp) {
28912  const char *hlp[] = {
28913         "I didn't find a variable name at the left of the `:=',",
28914         "so I'm going to pretend that you said `=' instead.",
28915         NULL };
28916  mp_disp_err(mp, NULL);
28917  mp_error (mp, "Improper `:=' will be changed to `='", hlp, true);
28918  mp_do_equation (mp);
28919}
28920static void bad_internal_assignment (MP mp, mp_node lhs) {
28921  char msg[256];
28922  const char *hlp[] = {
28923           "I can\'t set this internal quantity to anything but a known",
28924           "numeric value, so I'll have to ignore this assignment.",
28925           NULL };
28926  mp_disp_err(mp, NULL);
28927  if (internal_type (mp_sym_info (lhs)) == mp_known) {
28928    mp_snprintf (msg, 256, "Internal quantity `%s' must receive a known numeric value",
28929                 internal_name (mp_sym_info (lhs)));
28930  } else {
28931    mp_snprintf (msg, 256, "Internal quantity `%s' must receive a known string",
28932              internal_name (mp_sym_info (lhs)));
28933    hlp[1] = "string, so I'll have to ignore this assignment.";
28934  }
28935  mp_back_error (mp, msg, hlp, true);
28936  mp_get_x_next (mp);
28937}
28938static void forbidden_internal_assignment (MP mp, mp_node lhs) {
28939  char msg[256];
28940  const char *hlp[] = {
28941           "I can\'t set this internal quantity to anything just yet",
28942           "(it is read-only), so I'll have to ignore this assignment.",
28943           NULL };
28944  mp_snprintf (msg, 256, "Internal quantity `%s' is read-only",
28945               internal_name (mp_sym_info (lhs)));
28946  mp_back_error (mp, msg, hlp, true);
28947  mp_get_x_next (mp);
28948}
28949static void bad_internal_assignment_precision (MP mp, mp_node lhs, mp_number min, mp_number max) {
28950  char msg[256];
28951  char s[256];
28952  const char *hlp[] = {
28953       "Precision values are limited by the current numbersystem.",
28954       NULL,
28955       NULL } ;
28956  mp_snprintf (msg, 256, "Bad '%s' has been ignored", internal_name (mp_sym_info (lhs)));
28957  mp_snprintf (s, 256, "Currently I am using '%s'; the allowed precision range is [%s,%s].",
28958               mp_str (mp, internal_string (mp_number_system)), number_tostring(min), number_tostring(max));
28959  hlp[1] = s;
28960  mp_back_error (mp, msg, hlp, true);
28961  mp_get_x_next (mp);
28962}
28963static void bad_expression_assignment (MP mp, mp_node lhs) {
28964  const char *hlp[] = {
28965       "It seems you did a nasty thing---probably by accident,",
28966       "but nevertheless you nearly hornswoggled me...",
28967       "While I was evaluating the right-hand side of this",
28968       "command, something happened, and the left-hand side",
28969       "is no longer a variable! So I won't change anything.",
28970       NULL };
28971  char *msg = mp_obliterated (mp, lhs);
28972  mp_back_error (mp, msg, hlp, true);
28973  free(msg);
28974  mp_get_x_next (mp);
28975}
28976static void trace_assignment (MP mp, mp_node lhs) {
28977  mp_begin_diagnostic (mp);
28978  mp_print_nl (mp, "{");
28979  if (mp_name_type (lhs) == mp_internal_sym)
28980    mp_print (mp, internal_name (mp_sym_info (lhs)));
28981  else
28982    mp_show_token_list (mp, lhs, NULL, 1000, 0);
28983  mp_print (mp, ":=");
28984  mp_print_exp (mp, NULL, 0);
28985  mp_print_char (mp, xord ('}'));
28986  mp_end_diagnostic (mp, false);
28987}
28988void mp_do_assignment (MP mp) {
28989  if (mp->cur_exp.type != mp_token_list) {
28990    bad_lhs(mp);
28991  } else {
28992    mp_node lhs;  /* token list for the left-hand side */
28993    lhs = cur_exp_node ();
28994    mp->cur_exp.type = mp_vacuous;
28995    mp_get_x_next (mp);
28996    mp->var_flag = mp_assignment;
28997    mp_scan_expression (mp);
28998    if (cur_cmd() == mp_equals)
28999      mp_do_equation (mp);
29000    else if (cur_cmd() == mp_assignment)
29001      mp_do_assignment (mp);
29002    if (number_greater (internal_value (mp_tracing_commands), two_t)) {
29003      trace_assignment (mp, lhs);
29004    }
29005    if (mp_name_type (lhs) == mp_internal_sym) {
29006      /* Assign the current expression to an internal variable */
29007      if ((mp->cur_exp.type == mp_known || mp->cur_exp.type == mp_string_type)
29008          && (internal_type (mp_sym_info (lhs)) == mp->cur_exp.type)) {
29009	  if(mp_sym_info (lhs) == mp_number_system) {
29010             forbidden_internal_assignment (mp, lhs);
29011          } else if (mp_sym_info (lhs) == mp_number_precision) {
29012	     if (!(mp->cur_exp.type == mp_known &&
29013               (!number_less(cur_exp_value_number(), precision_min)) &&
29014               (!number_greater(cur_exp_value_number(), precision_max))
29015               )) {
29016	       bad_internal_assignment_precision(mp, lhs, precision_min, precision_max);
29017             } else {
29018	       set_internal_from_cur_exp(mp_sym_info (lhs));
29019               set_precision();
29020             }
29021          } else {
29022	     set_internal_from_cur_exp(mp_sym_info (lhs));
29023          }
29024      } else {
29025        bad_internal_assignment (mp, lhs);
29026      }
29027    } else {
29028      /* Assign the current expression to the variable |lhs| */
29029      mp_node p;    /* where the left-hand value is stored */
29030      mp_node q;    /* temporary capsule for the right-hand value */
29031      p = mp_find_variable (mp, lhs);
29032      if (p != NULL) {
29033        q = mp_stash_cur_exp (mp);
29034        mp->cur_exp.type = mp_und_type (mp, p);
29035        mp_recycle_value (mp, p);
29036        mp_type (p) = mp->cur_exp.type;
29037        set_value_number (p, zero_t);
29038        mp_make_exp_copy (mp, p);
29039        p = mp_stash_cur_exp (mp);
29040        mp_unstash_cur_exp (mp, q);
29041        mp_make_eq (mp, p);
29042      } else {
29043        bad_expression_assignment(mp, lhs);
29044      }
29045    }
29046    mp_flush_node_list (mp, lhs);
29047  }
29048}
29049
29050
29051@ And now we get to the nitty-gritty. The |make_eq| procedure is given
29052a pointer to a capsule that is to be equated to the current expression.
29053
29054@<Declare the procedure called |make_eq|@>=
29055static void mp_make_eq (MP mp, mp_node lhs);
29056
29057@
29058@c
29059static void announce_bad_equation (MP mp, mp_node lhs) {
29060  char msg[256];
29061  const char *hlp[] = {
29062       "I'm sorry, but I don't know how to make such things equal.",
29063       "(See the two expressions just above the error message.)",
29064       NULL  };
29065  mp_snprintf(msg, 256, "Equation cannot be performed (%s=%s)",
29066	(mp_type (lhs) <= mp_pair_type ? mp_type_string (mp_type (lhs)) : "numeric"),
29067	(mp->cur_exp.type <= mp_pair_type ? mp_type_string (mp->cur_exp.type) : "numeric"));
29068  mp_disp_err (mp, lhs);
29069  mp_disp_err(mp, NULL);
29070  mp_back_error (mp, msg, hlp, true);
29071  mp_get_x_next (mp);
29072}
29073static void exclaim_inconsistent_equation (MP mp) {
29074  const char *hlp[] = {
29075       "The equation I just read contradicts what was said before.",
29076       "But don't worry; continue and I'll just ignore it.",
29077        NULL };
29078  mp_back_error (mp,"Inconsistent equation", hlp, true);
29079  mp_get_x_next (mp);
29080}
29081static void exclaim_redundant_or_inconsistent_equation (MP mp) {
29082  const char *hlp[] = {
29083       "An equation between already-known quantities can't help.",
29084       "But don't worry; continue and I'll just ignore it.",
29085        NULL };
29086  mp_back_error (mp, "Redundant or inconsistent equation", hlp, true);
29087  mp_get_x_next (mp);
29088}
29089static void report_redundant_or_inconsistent_equation (MP mp, mp_node lhs, mp_number v) {
29090  if (mp->cur_exp.type <= mp_string_type) {
29091    if (mp->cur_exp.type == mp_string_type) {
29092      if (mp_str_vs_str (mp, value_str (lhs), cur_exp_str ()) != 0) {
29093        exclaim_inconsistent_equation(mp);
29094      } else {
29095        exclaim_redundant_equation(mp);
29096      }
29097    } else if (!number_equal (v, cur_exp_value_number ())) {
29098      exclaim_inconsistent_equation(mp);
29099    } else {
29100      exclaim_redundant_equation(mp);
29101    }
29102  } else {
29103    exclaim_redundant_or_inconsistent_equation (mp);
29104  }
29105}
29106
29107void mp_make_eq (MP mp, mp_node lhs) {
29108  mp_value new_expr;
29109  mp_variable_type t;   /* type of the left-hand side */
29110  mp_number v;        /* value of the left-hand side */
29111  memset(&new_expr,0,sizeof(mp_value));
29112  new_number (v);
29113RESTART:
29114  t = mp_type (lhs);
29115  if (t <= mp_pair_type)
29116    number_clone (v, value_number (lhs));
29117  /* For each type |t|, make an equation or complain if |cur_type|
29118     is incompatible with~|t| */
29119  switch (t) {
29120  case mp_boolean_type:
29121  case mp_string_type:
29122  case mp_pen_type:
29123  case mp_path_type:
29124  case mp_picture_type:
29125    if (mp->cur_exp.type == t + unknown_tag) {
29126      new_number(new_expr.data.n);
29127      if (t==mp_boolean_type) {
29128        number_clone (new_expr.data.n, v);
29129      } else if (t==mp_string_type) {
29130        new_expr.data.str = value_str(lhs);
29131      } else if (t==mp_picture_type) {
29132        new_expr.data.node = value_node(lhs);
29133      } else { /* pen or path */
29134        new_expr.data.p = value_knot(lhs);
29135      }
29136      mp_nonlinear_eq (mp, new_expr, cur_exp_node (), false);
29137      mp_unstash_cur_exp (mp, cur_exp_node ());
29138    } else if (mp->cur_exp.type == t) {
29139      report_redundant_or_inconsistent_equation(mp, lhs, v);
29140    } else {
29141      announce_bad_equation(mp, lhs);
29142    }
29143    break;
29144  case unknown_types:
29145    if (mp->cur_exp.type == t - unknown_tag) {
29146      mp_nonlinear_eq (mp, mp->cur_exp, lhs, true);
29147    } else if (mp->cur_exp.type == t) {
29148      mp_ring_merge (mp, lhs, cur_exp_node ());
29149    } else if (mp->cur_exp.type == mp_pair_type) {
29150      if (t == mp_unknown_path) {
29151        mp_pair_to_path (mp);
29152        goto RESTART;
29153      }
29154    } else {
29155      announce_bad_equation(mp, lhs);
29156    }
29157    break;
29158  case mp_transform_type:
29159  case mp_color_type:
29160  case mp_cmykcolor_type:
29161  case mp_pair_type:
29162    if (mp->cur_exp.type == t) {
29163      /* Do multiple equations */
29164      mp_node q = value_node (cur_exp_node ());
29165      mp_node p = value_node (lhs);
29166      switch (t) {
29167      case mp_transform_type:
29168        mp_try_eq (mp, yy_part (p), yy_part (q));
29169        mp_try_eq (mp, yx_part (p), yx_part (q));
29170        mp_try_eq (mp, xy_part (p), xy_part (q));
29171        mp_try_eq (mp, xx_part (p), xx_part (q));
29172        mp_try_eq (mp, ty_part (p), ty_part (q));
29173        mp_try_eq (mp, tx_part (p), tx_part (q));
29174        break;
29175      case mp_color_type:
29176        mp_try_eq (mp, blue_part (p),  blue_part (q));
29177        mp_try_eq (mp, green_part (p), green_part (q));
29178        mp_try_eq (mp, red_part (p),   red_part (q));
29179        break;
29180      case mp_cmykcolor_type:
29181        mp_try_eq (mp, black_part (p),   black_part (q));
29182        mp_try_eq (mp, yellow_part (p),  yellow_part (q));
29183        mp_try_eq (mp, magenta_part (p), magenta_part (q));
29184        mp_try_eq (mp, cyan_part (p),    cyan_part (q));
29185        break;
29186      case mp_pair_type:
29187        mp_try_eq (mp, y_part (p), y_part (q));
29188        mp_try_eq (mp, x_part (p), x_part (q));
29189        break;
29190      default:  /* there are no other valid cases, but please the compiler */
29191        break;
29192      }
29193    } else {
29194      announce_bad_equation(mp, lhs);
29195    }
29196    break;
29197  case mp_known:
29198  case mp_dependent:
29199  case mp_proto_dependent:
29200  case mp_independent:
29201    if (mp->cur_exp.type >= mp_known) {
29202      mp_try_eq (mp, lhs, NULL);
29203    } else {
29204      announce_bad_equation(mp, lhs);
29205    }
29206    break;
29207  case mp_vacuous:
29208    announce_bad_equation(mp, lhs);
29209    break;
29210  default: /* there are no other valid cases, but please the compiler */
29211    announce_bad_equation(mp, lhs);
29212    break;
29213  }
29214  check_arith();
29215  mp_recycle_value (mp, lhs);
29216  free_number (v);
29217  mp_free_value_node (mp, lhs);
29218}
29219
29220@ The first argument to |try_eq| is the location of a value node
29221in a capsule that will soon be recycled. The second argument is
29222either a location within a pair or transform node pointed to by
29223|cur_exp|, or it is |NULL| (which means that |cur_exp| itself
29224serves as the second argument). The idea is to leave |cur_exp| unchanged,
29225but to equate the two operands.
29226
29227@<Declarations@>=
29228static void mp_try_eq (MP mp, mp_node l, mp_node r);
29229
29230@
29231@d equation_threshold_k ((math_data *)mp->math)->equation_threshold_t
29232
29233@c
29234static void deal_with_redundant_or_inconsistent_equation(MP mp, mp_value_node p, mp_node r) {
29235  mp_number absp;
29236  new_number (absp);
29237  number_clone (absp, value_number (p));
29238  number_abs (absp);
29239  if (number_greater (absp, equation_threshold_k)) {   /* off by .001 or more */
29240    char msg[256];
29241    const char *hlp[] = {
29242           "The equation I just read contradicts what was said before.",
29243           "But don't worry; continue and I'll just ignore it.",
29244           NULL };
29245    mp_snprintf (msg, 256, "Inconsistent equation (off by %s)", number_tostring (value_number (p)));
29246    mp_back_error (mp, msg, hlp, true);
29247    mp_get_x_next (mp);
29248  } else if (r == NULL) {
29249    exclaim_redundant_equation(mp);
29250  }
29251  free_number (absp);
29252  mp_free_dep_node (mp, p);
29253}
29254
29255void mp_try_eq (MP mp, mp_node l, mp_node r) {
29256  mp_value_node p;      /* dependency list for right operand minus left operand */
29257  mp_variable_type t;   /* the type of list |p| */
29258  mp_value_node q;      /* the constant term of |p| is here */
29259  mp_value_node pp;     /* dependency list for right operand */
29260  mp_variable_type tt;  /* the type of list |pp| */
29261  boolean copied;       /* have we copied a list that ought to be recycled? */
29262  /* Remove the left operand from its container, negate it, and
29263     put it into dependency list~|p| with constant term~|q| */
29264  t = mp_type (l);
29265  if (t == mp_known) {
29266    mp_number arg1;
29267    new_number (arg1);
29268    number_clone (arg1, value_number(l));
29269    number_negate (arg1);
29270    t = mp_dependent;
29271    p = mp_const_dependency (mp, arg1);
29272    q = p;
29273    free_number (arg1);
29274  } else if (t == mp_independent) {
29275    t = mp_dependent;
29276    p = mp_single_dependency (mp, l);
29277    number_negate(dep_value (p));
29278    q = mp->dep_final;
29279  } else {
29280    mp_value_node ll = (mp_value_node) l;
29281    p = (mp_value_node) dep_list (ll);
29282    q = p;
29283    while (1) {
29284      number_negate(dep_value (q));
29285      if (dep_info (q) == NULL)
29286        break;
29287      q = (mp_value_node) mp_link (q);
29288    }
29289    mp_link (prev_dep (ll)) = mp_link (q);
29290    set_prev_dep ((mp_value_node) mp_link (q), prev_dep (ll));
29291    mp_type (ll) = mp_known;
29292  }
29293
29294  /* Add the right operand to list |p| */
29295  if (r == NULL) {
29296    if (mp->cur_exp.type == mp_known) {
29297      number_add (value_number (q), cur_exp_value_number ());
29298      goto DONE1;
29299    } else {
29300      tt = mp->cur_exp.type;
29301      if (tt == mp_independent)
29302        pp = mp_single_dependency (mp, cur_exp_node ());
29303      else
29304        pp = (mp_value_node) dep_list ((mp_value_node) cur_exp_node ());
29305    }
29306  } else {
29307    if (mp_type (r) == mp_known) {
29308      number_add (dep_value (q), value_number (r));
29309      goto DONE1;
29310    } else {
29311      tt = mp_type (r);
29312      if (tt == mp_independent)
29313        pp = mp_single_dependency (mp, r);
29314      else
29315        pp = (mp_value_node) dep_list ((mp_value_node) r);
29316    }
29317  }
29318  if (tt != mp_independent) {
29319    copied = false;
29320  } else {
29321    copied = true;
29322    tt = mp_dependent;
29323  }
29324  /* Add dependency list |pp| of type |tt| to dependency list~|p| of type~|t| */
29325  mp->watch_coefs = false;
29326  if (t == tt) {
29327    p = mp_p_plus_q (mp, p, pp, (quarterword) t);
29328  } else if (t == mp_proto_dependent) {
29329    p = mp_p_plus_fq (mp, p, unity_t, pp, mp_proto_dependent, mp_dependent);
29330  } else {
29331    mp_number x;
29332    new_number (x);
29333    q = p;
29334    while (dep_info (q) != NULL) {
29335      number_clone (x, dep_value (q));
29336      fraction_to_round_scaled (x);
29337      set_dep_value (q, x);
29338      q = (mp_value_node) mp_link (q);
29339    }
29340    free_number (x);
29341    t = mp_proto_dependent;
29342    p = mp_p_plus_q (mp, p, pp, (quarterword) t);
29343  }
29344  mp->watch_coefs = true;
29345
29346  if (copied)
29347    mp_flush_node_list (mp, (mp_node) pp);
29348  DONE1:
29349
29350  if (dep_info (p) == NULL) {
29351    deal_with_redundant_or_inconsistent_equation(mp, p, r);
29352  } else {
29353    mp_linear_eq (mp, p, (quarterword) t);
29354    if (r == NULL && mp->cur_exp.type != mp_known) {
29355      if (mp_type (cur_exp_node ()) == mp_known) {
29356        mp_node pp = cur_exp_node ();
29357        set_cur_exp_value_number (value_number (pp));
29358        mp->cur_exp.type = mp_known;
29359        mp_free_value_node (mp, pp);
29360      }
29361    }
29362  }
29363}
29364
29365@ Our next goal is to process type declarations. For this purpose it's
29366convenient to have a procedure that scans a $\langle\,$declared
29367variable$\,\rangle$ and returns the corresponding token list. After the
29368following procedure has acted, the token after the declared variable
29369will have been scanned, so it will appear in |cur_cmd|, |cur_mod|,
29370and~|cur_sym|.
29371
29372@<Declarations@>=
29373static mp_node mp_scan_declared_variable (MP mp);
29374
29375@ @c
29376mp_node mp_scan_declared_variable (MP mp) {
29377  mp_sym x;     /* hash address of the variable's root */
29378  mp_node h, t; /* head and tail of the token list to be returned */
29379  mp_get_symbol (mp);
29380  x = cur_sym();
29381  if (cur_cmd() != mp_tag_token)
29382    mp_clear_symbol (mp, x, false);
29383  h = mp_get_symbolic_node (mp);
29384  set_mp_sym_sym (h, x);
29385  t = h;
29386  while (1) {
29387    mp_get_x_next (mp);
29388    if (cur_sym() == NULL)
29389      break;
29390    if (cur_cmd() != mp_tag_token) {
29391      if (cur_cmd() != mp_internal_quantity) {
29392        if (cur_cmd() == mp_left_bracket) {
29393          /* Descend past a collective subscript */
29394	  /* If the subscript isn't collective, we don't accept it as part of the
29395  	     declared variable. */
29396	   mp_sym ll = cur_sym();      /* hash address of left bracket */
29397	   mp_get_x_next (mp);
29398	   if (cur_cmd() == mp_right_bracket) {
29399	     set_cur_sym(collective_subscript);
29400	   } else {
29401	     mp_back_input (mp);
29402	     set_cur_sym(ll);
29403	     set_cur_cmd((mp_variable_type)mp_left_bracket);
29404	     break;
29405	   }
29406        } else {
29407          break;
29408        }
29409      }
29410    }
29411    mp_link (t) = mp_get_symbolic_node (mp);
29412    t = mp_link (t);
29413    set_mp_sym_sym (t, cur_sym());
29414    mp_name_type (t) = cur_sym_mod();
29415  }
29416  if ((eq_type (x) % mp_outer_tag) != mp_tag_token)
29417    mp_clear_symbol (mp, x, false);
29418  if (equiv_node (x) == NULL)
29419    mp_new_root (mp, x);
29420  return h;
29421}
29422
29423
29424@ Type declarations are introduced by the following primitive operations.
29425
29426@<Put each...@>=
29427mp_primitive (mp, "numeric", mp_type_name, mp_numeric_type);
29428@:numeric_}{\&{numeric} primitive@>;
29429mp_primitive (mp, "string", mp_type_name, mp_string_type);
29430@:string_}{\&{string} primitive@>;
29431mp_primitive (mp, "boolean", mp_type_name, mp_boolean_type);
29432@:boolean_}{\&{boolean} primitive@>;
29433mp_primitive (mp, "path", mp_type_name, mp_path_type);
29434@:path_}{\&{path} primitive@>;
29435mp_primitive (mp, "pen", mp_type_name, mp_pen_type);
29436@:pen_}{\&{pen} primitive@>;
29437mp_primitive (mp, "picture", mp_type_name, mp_picture_type);
29438@:picture_}{\&{picture} primitive@>;
29439mp_primitive (mp, "transform", mp_type_name, mp_transform_type);
29440@:transform_}{\&{transform} primitive@>;
29441mp_primitive (mp, "color", mp_type_name, mp_color_type);
29442@:color_}{\&{color} primitive@>;
29443mp_primitive (mp, "rgbcolor", mp_type_name, mp_color_type);
29444@:color_}{\&{rgbcolor} primitive@>;
29445mp_primitive (mp, "cmykcolor", mp_type_name, mp_cmykcolor_type);
29446@:color_}{\&{cmykcolor} primitive@>;
29447mp_primitive (mp, "pair", mp_type_name, mp_pair_type);
29448@:pair_}{\&{pair} primitive@>
29449
29450
29451@ @<Cases of |print_cmd...@>=
29452case mp_type_name:
29453mp_print_type (mp, (quarterword) m);
29454break;
29455
29456@ Now we are ready to handle type declarations, assuming that a
29457|type_name| has just been scanned.
29458
29459@<Declare action procedures for use by |do_statement|@>=
29460static void mp_do_type_declaration (MP mp);
29461
29462@ @c
29463static void flush_spurious_symbols_after_declared_variable(MP mp);
29464void mp_do_type_declaration (MP mp) {
29465  integer t;        /* the type being declared */
29466  mp_node p;    /* token list for a declared variable */
29467  mp_node q;    /* value node for the variable */
29468  if (cur_mod() >= mp_transform_type)
29469    t = (quarterword) cur_mod();
29470  else
29471    t = (quarterword) (cur_mod() + unknown_tag);
29472  do {
29473    p = mp_scan_declared_variable (mp);
29474    mp_flush_variable (mp, equiv_node (mp_sym_sym (p)), mp_link (p), false);
29475    q = mp_find_variable (mp, p);
29476    if (q != NULL) {
29477      mp_type (q) = t;
29478      set_value_number (q, zero_t);         /* todo: this was |null| */
29479    } else {
29480      const char *hlp[] = {
29481             "You can't use, e.g., `numeric foo[]' after `vardef foo'.",
29482             "Proceed, and I'll ignore the illegal redeclaration.",
29483             NULL };
29484      mp_back_error (mp, "Declared variable conflicts with previous vardef", hlp, true);
29485      mp_get_x_next (mp);
29486    }
29487    mp_flush_node_list (mp, p);
29488    if (cur_cmd() < mp_comma) {
29489      flush_spurious_symbols_after_declared_variable(mp);
29490    }
29491  } while (!mp_end_of_statement);
29492}
29493
29494
29495@
29496@c
29497static void flush_spurious_symbols_after_declared_variable (MP mp)
29498{
29499  const char *hlp[] = {
29500         "Variables in declarations must consist entirely of",
29501         "names and collective subscripts, e.g., `x[]a'.",
29502         "Are you trying to use a reserved word in a variable name?",
29503         "I'm going to discard the junk I found here,",
29504         "up to the next comma or the end of the declaration.",
29505         NULL };
29506  if (cur_cmd() == mp_numeric_token)
29507    hlp[2] = "Explicit subscripts like `x15a' aren't permitted.";
29508  mp_back_error (mp, "Illegal suffix of declared variable will be flushed", hlp, true);
29509  mp_get_x_next (mp);
29510  mp->scanner_status = flushing;
29511  do {
29512    get_t_next (mp);
29513    @<Decrease the string reference count...@>;
29514  } while (cur_cmd() < mp_comma); /* break on either |end_of_statement| or |comma| */
29515  mp->scanner_status = normal;
29516}
29517
29518
29519@ \MP's |main_control| procedure just calls |do_statement| repeatedly
29520until coming to the end of the user's program.
29521Each execution of |do_statement| concludes with
29522|cur_cmd=semicolon|, |end_group|, or |stop|.
29523
29524@c
29525static void mp_main_control (MP mp) {
29526  do {
29527    mp_do_statement (mp);
29528    if (cur_cmd() == mp_end_group) {
29529      mp_value new_expr;
29530      const char *hlp[] = {
29531             "I'm not currently working on a `begingroup',",
29532             "so I had better not try to end anything.",
29533             NULL };
29534      memset(&new_expr,0,sizeof(mp_value));
29535      new_number(new_expr.data.n);
29536      mp_error (mp, "Extra `endgroup'", hlp, true);
29537      mp_flush_cur_exp (mp, new_expr);
29538    }
29539  } while (cur_cmd() != mp_stop);
29540}
29541int mp_run (MP mp) {
29542  if (mp->history < mp_fatal_error_stop) {
29543    xfree (mp->jump_buf);
29544    mp->jump_buf = malloc (sizeof (jmp_buf));
29545    if (mp->jump_buf == NULL || setjmp (*(mp->jump_buf)) != 0)
29546      return mp->history;
29547    mp_main_control (mp);       /* come to life */
29548    mp_final_cleanup (mp);      /* prepare for death */
29549    mp_close_files_and_terminate (mp);
29550  }
29551  return mp->history;
29552}
29553
29554
29555@ This function allows setting of internals from an external
29556source (like the command line or a controlling application).
29557
29558It accepts two |char *|'s, even for numeric assignments when
29559it calls |atoi| to get an integer from the start of the string.
29560
29561@c
29562void mp_set_internal (MP mp, char *n, char *v, int isstring) {
29563  size_t l = strlen (n);
29564  char err[256];
29565  const char *errid = NULL;
29566  if (l > 0) {
29567    mp_sym p = mp_id_lookup (mp, n, l, false);
29568    if (p == NULL) {
29569      errid = "variable does not exist";
29570    } else {
29571      if (eq_type (p) == mp_internal_quantity) {
29572        if ((internal_type (equiv (p)) == mp_string_type) && (isstring)) {
29573          set_internal_string (equiv (p), mp_rts (mp, v));
29574        } else if ((internal_type (equiv (p)) == mp_known) && (!isstring)) {
29575          int test = atoi (v);
29576          if (test > 16383 && mp->math_mode==mp_math_scaled_mode) {
29577            errid = "value is too large";
29578          } else if (test < -16383 && mp->math_mode==mp_math_scaled_mode) {
29579            errid = "value is too small";
29580          } else {
29581            set_internal_from_number (equiv (p), unity_t);
29582            number_multiply_int (internal_value(equiv (p)), test);
29583          }
29584        } else {
29585          errid = "value has the wrong type";
29586        }
29587      } else {
29588        errid = "variable is not an internal";
29589      }
29590    }
29591  }
29592  if (errid != NULL) {
29593    if (isstring) {
29594      mp_snprintf (err, 256, "%s=\"%s\": %s, assignment ignored.", n, v, errid);
29595    } else {
29596      mp_snprintf (err, 256, "%s=%d: %s, assignment ignored.", n, atoi (v),
29597                   errid);
29598    }
29599    mp_warn (mp, err);
29600  }
29601}
29602
29603
29604@ @<Exported function headers@>=
29605void mp_set_internal (MP mp, char *n, char *v, int isstring);
29606
29607@ For |mp_execute|, we need to define a structure to store the
29608redirected input and output. This structure holds the five relevant
29609streams: the three informational output streams, the PostScript
29610generation stream, and the input stream. These streams have many
29611things in common, so it makes sense to give them their own structure
29612definition.
29613
29614\item{fptr} is a virtual file pointer
29615\item{data} is the data this stream holds
29616\item{cur}  is a cursor pointing into |data|
29617\item{size} is the allocated length of the data stream
29618\item{used} is the actual length of the data stream
29619
29620There are small differences between input and output: |term_in| never
29621uses |used|, whereas the other four never use |cur|.
29622
29623The file |luatexdir/tex/texfileio.h| defines |term_in| as |stdin| and
29624|term_out| as |stdout|.  Moreover |stdio.h| for MinGW defines |stdin| as
29625|(&_iob[0])| and |stdout| as |(&_iob[1])|.  We must avoid all that.
29626
29627@<Exported types@>=
29628#undef term_in
29629#undef term_out
29630
29631typedef struct {
29632  void *fptr;
29633  char *data;
29634  char *cur;
29635  size_t size;
29636  size_t used;
29637} mp_stream;
29638typedef struct {
29639  mp_stream term_out;
29640  mp_stream error_out;
29641  mp_stream log_out;
29642  mp_stream ship_out;
29643  mp_stream term_in;
29644  struct mp_edge_object *edges;
29645} mp_run_data;
29646
29647@ We need a function to clear an output stream, this is called at the
29648beginning of |mp_execute|. We also need one for destroying an output
29649stream, this is called just before a stream is (re)opened.
29650
29651@c
29652static void mp_reset_stream (mp_stream * str) {
29653  xfree (str->data);
29654  str->cur = NULL;
29655  str->size = 0;
29656  str->used = 0;
29657}
29658static void mp_free_stream (mp_stream * str) {
29659  xfree (str->fptr);
29660  mp_reset_stream (str);
29661}
29662
29663
29664@ @<Declarations@>=
29665static void mp_reset_stream (mp_stream * str);
29666static void mp_free_stream (mp_stream * str);
29667
29668@ The global instance contains a pointer instead of the actual structure
29669even though it is essentially static, because that makes it is easier to move
29670the object around.
29671
29672@<Global ...@>=
29673mp_run_data run_data;
29674
29675@ Another type is needed: the indirection will overload some of the
29676file pointer objects in the instance (but not all). For clarity, an
29677indirect object is used that wraps a |FILE *|.
29678
29679@<Types ... @>=
29680typedef struct File {
29681  FILE *f;
29682} File;
29683
29684@ Here are all of the functions that need to be overloaded for |mp_execute|.
29685
29686@<Declarations@>=
29687static void *mplib_open_file (MP mp, const char *fname, const char *fmode,
29688                              int ftype);
29689static int mplib_get_char (void *f, mp_run_data * mplib_data);
29690static void mplib_unget_char (void *f, mp_run_data * mplib_data, int c);
29691static char *mplib_read_ascii_file (MP mp, void *ff, size_t * size);
29692static void mplib_write_ascii_file (MP mp, void *ff, const char *s);
29693static void mplib_read_binary_file (MP mp, void *ff, void **data,
29694                                    size_t * size);
29695static void mplib_write_binary_file (MP mp, void *ff, void *s, size_t size);
29696static void mplib_close_file (MP mp, void *ff);
29697static int mplib_eof_file (MP mp, void *ff);
29698static void mplib_flush_file (MP mp, void *ff);
29699static void mplib_shipout_backend (MP mp, void *h);
29700
29701@ The |xmalloc(1,1)| calls make sure the stored indirection values are unique.
29702
29703@d reset_stream(a)  do {
29704        mp_reset_stream(&(a));
29705        if (!ff->f) {
29706          ff->f = xmalloc(1,1);
29707          (a).fptr = ff->f;
29708        } } while (0)
29709
29710@c
29711static void *mplib_open_file (MP mp, const char *fname, const char *fmode,
29712                              int ftype) {
29713  File *ff = xmalloc (1, sizeof (File));
29714  mp_run_data *run = mp_rundata (mp);
29715  ff->f = NULL;
29716  if (ftype == mp_filetype_terminal) {
29717    if (fmode[0] == 'r') {
29718      if (!ff->f) {
29719        ff->f = xmalloc (1, 1);
29720        run->term_in.fptr = ff->f;
29721      }
29722    } else {
29723      reset_stream (run->term_out);
29724    }
29725  } else if (ftype == mp_filetype_error) {
29726    reset_stream (run->error_out);
29727  } else if (ftype == mp_filetype_log) {
29728    reset_stream (run->log_out);
29729  } else if (ftype == mp_filetype_postscript) {
29730    mp_free_stream (&(run->ship_out));
29731    ff->f = xmalloc (1, 1);
29732    run->ship_out.fptr = ff->f;
29733  } else if (ftype == mp_filetype_bitmap) {
29734    mp_free_stream (&(run->ship_out));
29735    ff->f = xmalloc (1, 1);
29736    run->ship_out.fptr = ff->f;
29737  } else {
29738    char realmode[3];
29739    char *f = (mp->find_file) (mp, fname, fmode, ftype);
29740    if (f == NULL)
29741      return NULL;
29742    realmode[0] = *fmode;
29743    realmode[1] = 'b';
29744    realmode[2] = 0;
29745    ff->f = fopen (f, realmode);
29746    free (f);
29747    if ((fmode[0] == 'r') && (ff->f == NULL)) {
29748      free (ff);
29749      return NULL;
29750    }
29751  }
29752  return ff;
29753}
29754static int mplib_get_char (void *f, mp_run_data * run) {
29755  int c;
29756  if (f == run->term_in.fptr && run->term_in.data != NULL) {
29757    if (run->term_in.size == 0) {
29758      if (run->term_in.cur != NULL) {
29759        run->term_in.cur = NULL;
29760      } else {
29761        xfree (run->term_in.data);
29762      }
29763      c = EOF;
29764    } else {
29765      run->term_in.size--;
29766      c = *(run->term_in.cur)++;
29767    }
29768  } else {
29769    c = fgetc (f);
29770  }
29771  return c;
29772}
29773static void mplib_unget_char (void *f, mp_run_data * run, int c) {
29774  if (f == run->term_in.fptr && run->term_in.cur != NULL) {
29775    run->term_in.size++;
29776    run->term_in.cur--;
29777  } else {
29778    ungetc (c, f);
29779  }
29780}
29781static char *mplib_read_ascii_file (MP mp, void *ff, size_t * size) {
29782  char *s = NULL;
29783  if (ff != NULL) {
29784    int c;
29785    size_t len = 0, lim = 128;
29786    mp_run_data *run = mp_rundata (mp);
29787    FILE *f = ((File *) ff)->f;
29788    if (f == NULL)
29789      return NULL;
29790    *size = 0;
29791    c = mplib_get_char (f, run);
29792    if (c == EOF)
29793      return NULL;
29794    s = malloc (lim);
29795    if (s == NULL)
29796      return NULL;
29797    while (c != EOF && c != '\n' && c != '\r') {
29798      if (len >= (lim - 1)) {
29799        s = xrealloc (s, (lim + (lim >> 2)), 1);
29800        if (s == NULL)
29801          return NULL;
29802        lim += (lim >> 2);
29803      }
29804      s[len++] = (char) c;
29805      c = mplib_get_char (f, run);
29806    }
29807    if (c == '\r') {
29808      c = mplib_get_char (f, run);
29809      if (c != EOF && c != '\n')
29810        mplib_unget_char (f, run, c);
29811    }
29812    s[len] = 0;
29813    *size = len;
29814  }
29815  return s;
29816}
29817static void mp_append_string (MP mp, mp_stream * a, const char *b) {
29818  size_t l = strlen (b) + 1; /* don't forget the trailing |'\0'| */
29819  if ((a->used + l) >= a->size) {
29820    a->size += 256 + (a->size) / 5 + l;
29821    a->data = xrealloc (a->data, a->size, 1);
29822  }
29823  memcpy (a->data + a->used, b, l);
29824  a->used += (l-1);
29825}
29826static void mp_append_data (MP mp, mp_stream * a, void *b, size_t l) {
29827  if ((a->used + l) >= a->size) {
29828    a->size += 256 + (a->size) / 5 + l;
29829    a->data = xrealloc (a->data, a->size, 1);
29830  }
29831  memcpy (a->data + a->used, b, l);
29832  a->used += l;
29833}
29834static void mplib_write_ascii_file (MP mp, void *ff, const char *s) {
29835  if (ff != NULL) {
29836    void *f = ((File *) ff)->f;
29837    mp_run_data *run = mp_rundata (mp);
29838    if (f != NULL) {
29839      if (f == run->term_out.fptr) {
29840        mp_append_string (mp, &(run->term_out), s);
29841      } else if (f == run->error_out.fptr) {
29842        mp_append_string (mp, &(run->error_out), s);
29843      } else if (f == run->log_out.fptr) {
29844        mp_append_string (mp, &(run->log_out), s);
29845      } else if (f == run->ship_out.fptr) {
29846        mp_append_string (mp, &(run->ship_out), s);
29847      } else {
29848        fprintf ((FILE *) f, "%s", s);
29849      }
29850    }
29851  }
29852}
29853static void mplib_read_binary_file (MP mp, void *ff, void **data, size_t * size) {
29854  (void) mp;
29855  if (ff != NULL) {
29856    size_t len = 0;
29857    FILE *f = ((File *) ff)->f;
29858    if (f != NULL)
29859      len = fread (*data, 1, *size, f);
29860    *size = len;
29861  }
29862}
29863static void mplib_write_binary_file (MP mp, void *ff, void *s, size_t size) {
29864  (void) mp;
29865  if (ff != NULL) {
29866    void *f = ((File *) ff)->f;
29867    mp_run_data *run = mp_rundata (mp);
29868    if (f != NULL) {
29869      if (f == run->ship_out.fptr) {
29870        mp_append_data (mp, &(run->ship_out), s, size);
29871      } else {
29872        (void) fwrite (s, size, 1, f);
29873      }
29874    }
29875  }
29876}
29877static void mplib_close_file (MP mp, void *ff) {
29878  if (ff != NULL) {
29879    mp_run_data *run = mp_rundata (mp);
29880    void *f = ((File *) ff)->f;
29881    if (f != NULL) {
29882      if (f != run->term_out.fptr
29883          && f != run->error_out.fptr
29884          && f != run->log_out.fptr
29885          && f != run->ship_out.fptr && f != run->term_in.fptr) {
29886        fclose (f);
29887      }
29888    }
29889    free (ff);
29890  }
29891}
29892static int mplib_eof_file (MP mp, void *ff) {
29893  if (ff != NULL) {
29894    mp_run_data *run = mp_rundata (mp);
29895    FILE *f = ((File *) ff)->f;
29896    if (f == NULL)
29897      return 1;
29898    if (f == run->term_in.fptr && run->term_in.data != NULL) {
29899      return (run->term_in.size == 0);
29900    }
29901    return feof (f);
29902  }
29903  return 1;
29904}
29905static void mplib_flush_file (MP mp, void *ff) {
29906  (void) mp;
29907  (void) ff;
29908  return;
29909}
29910static void mplib_shipout_backend (MP mp, void *voidh) {
29911  mp_edge_header_node h = (mp_edge_header_node) voidh;
29912  mp_edge_object *hh = mp_gr_export (mp, h);
29913  if (hh) {
29914    mp_run_data *run = mp_rundata (mp);
29915    if (run->edges == NULL) {
29916      run->edges = hh;
29917    } else {
29918      mp_edge_object *p = run->edges;
29919      while (p->next != NULL) {
29920        p = p->next;
29921      }
29922      p->next = hh;
29923    }
29924  }
29925}
29926
29927
29928@ This is where we fill them all in.
29929@<Prepare function pointers for non-interactive use@>=
29930{
29931  mp->open_file = mplib_open_file;
29932  mp->close_file = mplib_close_file;
29933  mp->eof_file = mplib_eof_file;
29934  mp->flush_file = mplib_flush_file;
29935  mp->write_ascii_file = mplib_write_ascii_file;
29936  mp->read_ascii_file = mplib_read_ascii_file;
29937  mp->write_binary_file = mplib_write_binary_file;
29938  mp->read_binary_file = mplib_read_binary_file;
29939  mp->shipout_backend = mplib_shipout_backend;
29940}
29941
29942
29943@ Perhaps this is the most important API function in the library.
29944
29945@<Exported function ...@>=
29946extern mp_run_data *mp_rundata (MP mp);
29947
29948@ @c
29949mp_run_data *mp_rundata (MP mp) {
29950  return &(mp->run_data);
29951}
29952
29953
29954@ @<Dealloc ...@>=
29955mp_free_stream (&(mp->run_data.term_in));
29956mp_free_stream (&(mp->run_data.term_out));
29957mp_free_stream (&(mp->run_data.log_out));
29958mp_free_stream (&(mp->run_data.error_out));
29959mp_free_stream (&(mp->run_data.ship_out));
29960
29961@ @<Finish non-interactive use@>=
29962xfree (mp->term_out);
29963xfree (mp->term_in);
29964xfree (mp->err_out);
29965
29966@ @<Start non-interactive work@>=
29967@<Initialize the output routines@>;
29968mp->input_ptr = 0;
29969mp->max_in_stack = file_bottom;
29970mp->in_open = file_bottom;
29971mp->open_parens = 0;
29972mp->max_buf_stack = 0;
29973mp->param_ptr = 0;
29974mp->max_param_stack = 0;
29975start = loc = 0;
29976iindex = file_bottom;
29977nloc = nstart = NULL;
29978mp->first = 0;
29979line = 0;
29980name = is_term;
29981mp->mpx_name[file_bottom] = absent;
29982mp->force_eof = false;
29983t_open_in();
29984mp->scanner_status = normal;
29985if (!mp->ini_version) {
29986  if (!mp_load_preload_file (mp)) {
29987    mp->history = mp_fatal_error_stop;
29988    return mp->history;
29989  }
29990}
29991mp_fix_date_and_time (mp);
29992if (mp->random_seed == 0)
29993  mp->random_seed =
29994    (number_to_scaled (internal_value (mp_time)) / number_to_scaled (unity_t)) + number_to_scaled (internal_value (mp_day));
29995init_randoms (mp->random_seed);
29996initialize_print_selector();
29997mp_open_log_file (mp);
29998mp_set_job_id (mp);
29999mp_init_map_file (mp, mp->troff_mode);
30000mp->history = mp_spotless;      /* ready to go! */
30001if (mp->troff_mode) {
30002  number_clone (internal_value(mp_gtroffmode), unity_t);
30003  number_clone (internal_value(mp_prologues), unity_t);
30004}
30005@<Fix up |mp->internal[mp_job_name]|@>;
30006if (mp->start_sym != NULL) {    /* insert the `\&{everyjob}' symbol */
30007  set_cur_sym(mp->start_sym);
30008  mp_back_input (mp);
30009}
30010
30011@ @c
30012int mp_execute (MP mp, char *s, size_t l) {
30013  mp_reset_stream (&(mp->run_data.term_out));
30014  mp_reset_stream (&(mp->run_data.log_out));
30015  mp_reset_stream (&(mp->run_data.error_out));
30016  mp_reset_stream (&(mp->run_data.ship_out));
30017  if (mp->finished) {
30018    return mp->history;
30019  } else if (!mp->noninteractive) {
30020    mp->history = mp_fatal_error_stop;
30021    return mp->history;
30022  }
30023  if (mp->history < mp_fatal_error_stop) {
30024    xfree (mp->jump_buf);
30025    mp->jump_buf = malloc (sizeof (jmp_buf));
30026    if (mp->jump_buf == NULL || setjmp (*(mp->jump_buf)) != 0) {
30027      return mp->history;
30028    }
30029    if (s == NULL) {            /* this signals EOF */
30030      mp_final_cleanup (mp);    /* prepare for death */
30031      mp_close_files_and_terminate (mp);
30032      return mp->history;
30033    }
30034    mp->tally = 0;
30035    mp->term_offset = 0;
30036    mp->file_offset = 0;
30037    /* Perhaps some sort of warning here when |data| is not
30038     * yet exhausted would be nice ...  this happens after errors
30039     */
30040    if (mp->run_data.term_in.data)
30041      xfree (mp->run_data.term_in.data);
30042    mp->run_data.term_in.data = xstrdup (s);
30043    mp->run_data.term_in.cur = mp->run_data.term_in.data;
30044    mp->run_data.term_in.size = l;
30045    if (mp->run_state == 0) {
30046      mp->selector = term_only;
30047      @<Start non-interactive work@>;
30048    }
30049    mp->run_state = 1;
30050    (void) mp_input_ln (mp, mp->term_in);
30051    mp_firm_up_the_line (mp);
30052    mp->buffer[limit] = xord ('%');
30053    mp->first = (size_t) (limit + 1);
30054    loc = start;
30055    do {
30056      mp_do_statement (mp);
30057    } while (cur_cmd() != mp_stop);
30058    mp_final_cleanup (mp);
30059    mp_close_files_and_terminate (mp);
30060  }
30061  return mp->history;
30062}
30063
30064
30065@ This function cleans up
30066@c
30067int mp_finish (MP mp) {
30068  int history = 0;
30069  if (mp->finished || mp->history >= mp_fatal_error_stop) {
30070    history = mp->history;
30071    mp_free (mp);
30072    return history;
30073  }
30074  xfree (mp->jump_buf);
30075  mp->jump_buf = malloc (sizeof (jmp_buf));
30076  if (mp->jump_buf == NULL || setjmp (*(mp->jump_buf)) != 0) {
30077    history = mp->history;
30078  } else {
30079    history = mp->history;
30080    mp_final_cleanup (mp);      /* prepare for death */
30081  }
30082  mp_close_files_and_terminate (mp);
30083  mp_free (mp);
30084  return history;
30085}
30086
30087
30088@ People may want to know the library version
30089@c
30090char *mp_metapost_version (void) {
30091  return mp_strdup (metapost_version);
30092}
30093void mp_show_library_versions (void) {
30094  fprintf(stdout, "Compiled with cairo %s; using %s\n", CAIRO_VERSION_STRING, cairo_version_string());
30095  fprintf(stdout, "Compiled with pixman %s; using %s\n", PIXMAN_VERSION_STRING, pixman_version_string());
30096  fprintf(stdout, "Compiled with libpng %s; using %s\n", PNG_LIBPNG_VER_STRING, png_libpng_ver);
30097  fprintf(stdout, "Compiled with zlib %s; using %s\n", ZLIB_VERSION, zlibVersion());
30098  fprintf(stdout, "Compiled with mpfr %s; using %s\n", MPFR_VERSION_STRING, mpfr_get_version());
30099  fprintf(stdout, "Compiled with gmp %d.%d.%d; using %s\n\n", __GNU_MP_VERSION, __GNU_MP_VERSION_MINOR, __GNU_MP_VERSION_PATCHLEVEL, gmp_version);
30100}
30101
30102@ @<Exported function headers@>=
30103int mp_run (MP mp);
30104int mp_execute (MP mp, char *s, size_t l);
30105int mp_finish (MP mp);
30106char *mp_metapost_version (void);void mp_show_library_versions (void);
30107
30108@ @<Put each...@>=
30109mp_primitive (mp, "end", mp_stop, 0);
30110@:end_}{\&{end} primitive@>;
30111mp_primitive (mp, "dump", mp_stop, 1);
30112mp->frozen_dump = mp_frozen_primitive (mp, "dump", mp_stop, 1);
30113@:dump_}{\&{dump} primitive@>
30114
30115
30116@ @<Cases of |print_cmd...@>=
30117case mp_stop:
30118if (cur_mod() == 0)
30119  mp_print (mp, "end");
30120else
30121  mp_print (mp, "dump");
30122break;
30123
30124@* Commands.
30125Let's turn now to statements that are classified as ``commands'' because
30126of their imperative nature. We'll begin with simple ones, so that it
30127will be clear how to hook command processing into the |do_statement| routine;
30128then we'll tackle the tougher commands.
30129
30130Here's one of the simplest:
30131
30132@ @<Declare action procedures for use by |do_statement|@>=
30133static void mp_do_random_seed (MP mp);
30134@ @c
30135void mp_do_random_seed (MP mp) {
30136  mp_value new_expr;
30137  memset(&new_expr,0,sizeof(mp_value));
30138  new_number(new_expr.data.n);
30139  mp_get_x_next (mp);
30140  if (cur_cmd() != mp_assignment) {
30141    const char *hlp[] = { "Always say `randomseed:=<numeric expression>'.", NULL };
30142    mp_back_error (mp, "Missing `:=' has been inserted", hlp, true);
30143@.Missing `:='@>;
30144  };
30145  mp_get_x_next (mp);
30146  mp_scan_expression (mp);
30147  if (mp->cur_exp.type != mp_known) {
30148    const char *hlp[] = {
30149           "Your expression was too random for me to handle,",
30150           "so I won't change the random seed just now.",
30151           NULL };
30152    mp_disp_err(mp, NULL);
30153    mp_back_error (mp, "Unknown value will be ignored", hlp, true);
30154@.Unknown value...ignored@>;
30155    mp_get_x_next (mp);
30156    mp_flush_cur_exp (mp, new_expr);
30157  } else {
30158    @<Initialize the random seed to |cur_exp|@>;
30159  }
30160}
30161
30162
30163@ @<Initialize the random seed to |cur_exp|@>=
30164{
30165  init_randoms (number_to_scaled(cur_exp_value_number ()));
30166  if (mp->selector >= log_only && mp->selector < write_file) {
30167    mp->old_setting = mp->selector;
30168    mp->selector = log_only;
30169    mp_print_nl (mp, "{randomseed:=");
30170    print_number (cur_exp_value_number ());
30171    mp_print_char (mp, xord ('}'));
30172    mp_print_nl (mp, "");
30173    mp->selector = mp->old_setting;
30174  }
30175}
30176
30177
30178@ And here's another simple one (somewhat different in flavor):
30179
30180@ @<Put each...@>=
30181mp_primitive (mp, "batchmode", mp_mode_command, mp_batch_mode);
30182@:mp_batch_mode_}{\&{batchmode} primitive@>;
30183mp_primitive (mp, "nonstopmode", mp_mode_command, mp_nonstop_mode);
30184@:mp_nonstop_mode_}{\&{nonstopmode} primitive@>;
30185mp_primitive (mp, "scrollmode", mp_mode_command, mp_scroll_mode);
30186@:mp_scroll_mode_}{\&{scrollmode} primitive@>;
30187mp_primitive (mp, "errorstopmode", mp_mode_command, mp_error_stop_mode);
30188@:mp_error_stop_mode_}{\&{errorstopmode} primitive@>
30189
30190
30191@ @<Cases of |print_cmd_mod|...@>=
30192case mp_mode_command:
30193switch (m) {
30194case mp_batch_mode:
30195  mp_print (mp, "batchmode");
30196  break;
30197case mp_nonstop_mode:
30198  mp_print (mp, "nonstopmode");
30199  break;
30200case mp_scroll_mode:
30201  mp_print (mp, "scrollmode");
30202  break;
30203default:
30204  mp_print (mp, "errorstopmode");
30205  break;
30206}
30207break;
30208
30209@ The `\&{inner}' and `\&{outer}' commands are only slightly harder.
30210
30211@ @<Put each...@>=
30212mp_primitive (mp, "inner", mp_protection_command, 0);
30213@:inner_}{\&{inner} primitive@>;
30214mp_primitive (mp, "outer", mp_protection_command, 1);
30215@:outer_}{\&{outer} primitive@>
30216
30217
30218@ @<Cases of |print_cmd...@>=
30219case mp_protection_command:
30220if (m == 0)
30221  mp_print (mp, "inner");
30222else
30223  mp_print (mp, "outer");
30224break;
30225
30226@ @<Declare action procedures for use by |do_statement|@>=
30227static void mp_do_protection (MP mp);
30228
30229@ @c
30230void mp_do_protection (MP mp) {
30231  int m;        /* 0 to unprotect, 1 to protect */
30232  halfword t;   /* the |eq_type| before we change it */
30233  m = cur_mod();
30234  do {
30235    mp_get_symbol (mp);
30236    t = eq_type (cur_sym());
30237    if (m == 0) {
30238      if (t >= mp_outer_tag)
30239        set_eq_type (cur_sym(), (t - mp_outer_tag));
30240    } else if (t < mp_outer_tag) {
30241      set_eq_type (cur_sym(), (t + mp_outer_tag));
30242    }
30243    mp_get_x_next (mp);
30244  } while (cur_cmd() == mp_comma);
30245}
30246
30247
30248@ \MP\ never defines the tokens `\.(' and `\.)' to be primitives, but
30249plain \MP\ begins with the declaration `\&{delimiters} \.{()}'. Such a
30250declaration assigns the command code |left_delimiter| to `\.{(}' and
30251|right_delimiter| to `\.{)}'; the |equiv| of each delimiter is the
30252hash address of its mate.
30253
30254@ @<Declare action procedures for use by |do_statement|@>=
30255static void mp_def_delims (MP mp);
30256
30257@ @c
30258void mp_def_delims (MP mp) {
30259  mp_sym l_delim, r_delim;      /* the new delimiter pair */
30260  mp_get_clear_symbol (mp);
30261  l_delim = cur_sym();
30262  mp_get_clear_symbol (mp);
30263  r_delim = cur_sym();
30264  set_eq_type (l_delim, mp_left_delimiter);
30265  set_equiv_sym (l_delim, r_delim);
30266  set_eq_type (r_delim, mp_right_delimiter);
30267  set_equiv_sym (r_delim, l_delim);
30268  mp_get_x_next (mp);
30269}
30270
30271
30272@ Here is a procedure that is called when \MP\ has reached a point
30273where some right delimiter is mandatory.
30274
30275@<Declarations@>=
30276static void mp_check_delimiter (MP mp, mp_sym l_delim, mp_sym r_delim);
30277
30278@ @c
30279void mp_check_delimiter (MP mp, mp_sym l_delim, mp_sym r_delim) {
30280  if (cur_cmd() == mp_right_delimiter)
30281    if (equiv_sym (cur_sym()) == l_delim)
30282      return;
30283  if (cur_sym() != r_delim) {
30284    char msg[256];
30285    const char *hlp[] = {
30286           "I found no right delimiter to match a left one. So I've",
30287           "put one in, behind the scenes; this may fix the problem.",
30288            NULL };
30289    mp_snprintf(msg, 256, "Missing `%s' has been inserted", mp_str (mp, text (r_delim)));
30290@.Missing `)'@>;
30291    mp_back_error (mp, msg, hlp, true);
30292  } else {
30293    char msg[256];
30294    const char *hlp[] = {
30295           "Strange: This token has lost its former meaning!",
30296           "I'll read it as a right delimiter this time;",
30297           "but watch out, I'll probably miss it later.",
30298           NULL };
30299    mp_snprintf(msg, 256, "The token `%s' is no longer a right delimiter", mp_str(mp, text (r_delim)));
30300@.The token...delimiter@>;
30301    mp_error (mp, msg, hlp, true);
30302  }
30303}
30304
30305
30306@ The next four commands save or change the values associated with tokens.
30307
30308@ @<Declare action procedures for use by |do_statement|@>=
30309static void mp_do_statement (MP mp);
30310static void mp_do_interim (MP mp);
30311
30312@ @c
30313void mp_do_interim (MP mp) {
30314  mp_get_x_next (mp);
30315  if (cur_cmd() != mp_internal_quantity) {
30316    char msg[256];
30317    const char *hlp[] = {
30318       "Something like `tracingonline' should follow `interim'.",
30319       NULL };
30320    mp_snprintf(msg, 256, "The token `%s' isn't an internal quantity",
30321      (cur_sym() == NULL ? "(%CAPSULE)" : mp_str(mp, text (cur_sym()))));
30322@.The token...quantity@>;
30323    mp_back_error (mp, msg, hlp, true);
30324  } else {
30325    mp_save_internal (mp, cur_mod());
30326    mp_back_input (mp);
30327  }
30328  mp_do_statement (mp);
30329}
30330
30331
30332@ The following procedure is careful not to undefine the left-hand symbol
30333too soon, lest commands like `{\tt let x=x}' have a surprising effect.
30334
30335@<Declare action procedures for use by |do_statement|@>=
30336static void mp_do_let (MP mp);
30337
30338@ @c
30339void mp_do_let (MP mp) {
30340  mp_sym l;     /* hash location of the left-hand symbol */
30341  mp_get_symbol (mp);
30342  l = cur_sym();
30343  mp_get_x_next (mp);
30344  if (cur_cmd() != mp_equals && cur_cmd() != mp_assignment) {
30345    const char *hlp[] = {
30346           "You should have said `let symbol = something'.",
30347           "But don't worry; I'll pretend that an equals sign",
30348           "was present. The next token I read will be `something'.",
30349           NULL };
30350    mp_back_error (mp, "Missing `=' has been inserted", hlp, true);
30351@.Missing `='@>;
30352  }
30353  mp_get_symbol (mp);
30354  switch (cur_cmd()) {
30355  case mp_defined_macro:
30356  case mp_secondary_primary_macro:
30357  case mp_tertiary_secondary_macro:
30358  case mp_expression_tertiary_macro:
30359    add_mac_ref (cur_mod_node());
30360    break;
30361  default:
30362    break;
30363  }
30364  mp_clear_symbol (mp, l, false);
30365  set_eq_type (l, cur_cmd());
30366  if (cur_cmd() == mp_tag_token)
30367    set_equiv (l, 0);              /* todo: this was |null| */
30368  else if (cur_cmd() == mp_defined_macro ||
30369           cur_cmd() == mp_secondary_primary_macro ||
30370           cur_cmd() == mp_tertiary_secondary_macro ||
30371           cur_cmd() == mp_expression_tertiary_macro)
30372    set_equiv_node (l, cur_mod_node());
30373  else if (cur_cmd() == mp_left_delimiter ||
30374           cur_cmd() ==  mp_right_delimiter)
30375    set_equiv_sym (l, equiv_sym (cur_sym()));
30376  else
30377    set_equiv (l, cur_mod());
30378  mp_get_x_next (mp);
30379}
30380
30381
30382@ @<Declarations@>=
30383static void mp_do_new_internal (MP mp);
30384
30385@ @<Internal library ...@>=
30386void mp_grow_internals (MP mp, int l);
30387
30388@ @c
30389void mp_grow_internals (MP mp, int l) {
30390  mp_internal *internal;
30391  int k;
30392  if (l > max_halfword) {
30393    mp_confusion (mp, "out of memory space");   /* can't be reached */
30394  }
30395  internal = xmalloc ((l + 1), sizeof (mp_internal));
30396  for (k = 0; k <= l; k++) {
30397    if (k <= mp->max_internal) {
30398      memcpy (internal + k, mp->internal + k, sizeof (mp_internal));
30399    } else {
30400      memset (internal + k, 0, sizeof (mp_internal));
30401      new_number(((mp_internal *)(internal + k))->v.data.n);
30402    }
30403  }
30404  xfree (mp->internal);
30405  mp->internal = internal;
30406  mp->max_internal = l;
30407}
30408void mp_do_new_internal (MP mp) {
30409  int the_type = mp_known;
30410  mp_get_x_next (mp);
30411  if (cur_cmd() == mp_type_name && cur_mod() == mp_string_type) {
30412    the_type = mp_string_type;
30413  } else {
30414    if (!(cur_cmd() == mp_type_name && cur_mod() == mp_numeric_type)) {
30415      mp_back_input (mp);
30416    }
30417  }
30418  do {
30419    if (mp->int_ptr == mp->max_internal) {
30420      mp_grow_internals (mp, (mp->max_internal + (mp->max_internal / 4)));
30421    }
30422    mp_get_clear_symbol (mp);
30423    incr (mp->int_ptr);
30424    set_eq_type (cur_sym(), mp_internal_quantity);
30425    set_equiv (cur_sym(), mp->int_ptr);
30426    if (internal_name (mp->int_ptr) != NULL)
30427      xfree (internal_name (mp->int_ptr));
30428    set_internal_name (mp->int_ptr,
30429      mp_xstrdup (mp, mp_str (mp, text (cur_sym()))));
30430    if (the_type == mp_string_type) {
30431      set_internal_string (mp->int_ptr, mp_rts(mp,""));
30432    } else {
30433      set_number_to_zero (internal_value (mp->int_ptr));
30434    }
30435    set_internal_type (mp->int_ptr, the_type);
30436    mp_get_x_next (mp);
30437  } while (cur_cmd() == mp_comma);
30438}
30439
30440
30441@ @<Dealloc variables@>=
30442for (k = 0; k <= mp->max_internal; k++) {
30443  free_number(mp->internal[k].v.data.n);
30444  xfree (internal_name (k));
30445}
30446xfree (mp->internal);
30447
30448
30449@ The various `\&{show}' commands are distinguished by modifier fields
30450in the usual way.
30451
30452@d show_token_code 0 /* show the meaning of a single token */
30453@d show_stats_code 1 /* show current memory and string usage */
30454@d show_code 2 /* show a list of expressions */
30455@d show_var_code 3 /* show a variable and its descendents */
30456@d show_dependencies_code 4 /* show dependent variables in terms of independents */
30457
30458@<Put each...@>=
30459mp_primitive (mp, "showtoken", mp_show_command, show_token_code);
30460@:show_token_}{\&{showtoken} primitive@>;
30461mp_primitive (mp, "showstats", mp_show_command, show_stats_code);
30462@:show_stats_}{\&{showstats} primitive@>;
30463mp_primitive (mp, "show", mp_show_command, show_code);
30464@:show_}{\&{show} primitive@>;
30465mp_primitive (mp, "showvariable", mp_show_command, show_var_code);
30466@:show_var_}{\&{showvariable} primitive@>;
30467mp_primitive (mp, "showdependencies", mp_show_command, show_dependencies_code);
30468@:show_dependencies_}{\&{showdependencies} primitive@>
30469
30470
30471@ @<Cases of |print_cmd...@>=
30472case mp_show_command:
30473switch (m) {
30474case show_token_code:
30475  mp_print (mp, "showtoken");
30476  break;
30477case show_stats_code:
30478  mp_print (mp, "showstats");
30479  break;
30480case show_code:
30481  mp_print (mp, "show");
30482  break;
30483case show_var_code:
30484  mp_print (mp, "showvariable");
30485  break;
30486default:
30487  mp_print (mp, "showdependencies");
30488  break;
30489}
30490break;
30491
30492@ The value of |cur_mod| controls the |verbosity| in the |print_exp| routine:
30493if it's |show_code|, complicated structures are abbreviated, otherwise
30494they aren't.
30495
30496@<Declare action procedures for use by |do_statement|@>=
30497static void mp_do_show (MP mp);
30498
30499@ @c
30500void mp_do_show (MP mp) {
30501  mp_value new_expr;
30502  do {
30503    memset(&new_expr,0,sizeof(mp_value));
30504    new_number(new_expr.data.n);
30505    mp_get_x_next (mp);
30506    mp_scan_expression (mp);
30507    mp_print_nl (mp, ">> ");
30508@.>>@>;
30509    mp_print_exp (mp, NULL, 2);
30510    mp_flush_cur_exp (mp, new_expr);
30511  } while (cur_cmd() == mp_comma);
30512}
30513
30514
30515@ @<Declare action procedures for use by |do_statement|@>=
30516static void mp_disp_token (MP mp);
30517
30518@ @c
30519void mp_disp_token (MP mp) {
30520  mp_print_nl (mp, "> ");
30521@.>\relax@>;
30522  if (cur_sym() == NULL) {
30523    @<Show a numeric or string or capsule token@>;
30524  } else {
30525    mp_print_text (cur_sym());
30526    mp_print_char (mp, xord ('='));
30527    if (eq_type (cur_sym()) >= mp_outer_tag)
30528      mp_print (mp, "(outer) ");
30529    mp_print_cmd_mod (mp, cur_cmd(), cur_mod());
30530    if (cur_cmd() == mp_defined_macro) {
30531      mp_print_ln (mp);
30532      mp_show_macro (mp, cur_mod_node(), NULL, 100000);
30533    }                           /* this avoids recursion between |show_macro| and |print_cmd_mod| */
30534@^recursion@>
30535  }
30536}
30537
30538
30539@ @<Show a numeric or string or capsule token@>=
30540{
30541  if (cur_cmd() == mp_numeric_token) {
30542    print_number (cur_mod_number());
30543  } else if (cur_cmd() == mp_capsule_token) {
30544    mp_print_capsule (mp, cur_mod_node());
30545  } else {
30546    mp_print_char (mp, xord ('"'));
30547    mp_print_str (mp, cur_mod_str());
30548    mp_print_char (mp, xord ('"'));
30549    delete_str_ref (cur_mod_str());
30550  }
30551}
30552
30553
30554@ The following cases of |print_cmd_mod| might arise in connection
30555with |disp_token|, although they don't necessarily correspond to
30556primitive tokens.
30557
30558@<Cases of |print_cmd_...@>=
30559case mp_left_delimiter:
30560case mp_right_delimiter:
30561if (c == mp_left_delimiter)
30562  mp_print (mp, "left");
30563else
30564  mp_print (mp, "right");
30565#if 0
30566mp_print (mp, " delimiter that matches ");
30567mp_print_text (m);
30568#else
30569mp_print (mp, " delimiter");
30570#endif
30571break;
30572case mp_tag_token:
30573if (m == 0)                     /* todo: this was |null| */
30574  mp_print (mp, "tag");
30575else
30576  mp_print (mp, "variable");
30577break;
30578case mp_defined_macro:
30579mp_print (mp, "macro:");
30580break;
30581case mp_secondary_primary_macro:
30582case mp_tertiary_secondary_macro:
30583case mp_expression_tertiary_macro:
30584  mp_print_cmd_mod(mp, mp_macro_def,c);
30585  mp_print(mp, "'d macro:");
30586  mp_print_ln(mp);
30587  mp_show_token_list(mp, mp_link(mp_link(cur_mod_node())),0,1000,0);
30588  break;
30589case mp_repeat_loop:
30590mp_print (mp, "[repeat the loop]");
30591break;
30592case mp_internal_quantity:
30593mp_print (mp, internal_name (m));
30594break;
30595
30596
30597@ @<Declare action procedures for use by |do_statement|@>=
30598static void mp_do_show_token (MP mp);
30599
30600@ @c
30601void mp_do_show_token (MP mp) {
30602  do {
30603    get_t_next (mp);
30604    mp_disp_token (mp);
30605    mp_get_x_next (mp);
30606  } while (cur_cmd() == mp_comma);
30607}
30608
30609
30610@ @<Declare action procedures for use by |do_statement|@>=
30611static void mp_do_show_stats (MP mp);
30612
30613@ @c
30614void mp_do_show_stats (MP mp) {
30615  mp_print_nl (mp, "Memory usage ");
30616@.Memory usage...@>;
30617  mp_print_int (mp, (integer) mp->var_used);
30618  mp_print_ln (mp);
30619  mp_print_nl (mp, "String usage ");
30620  mp_print_int (mp, (int) mp->strs_in_use);
30621  mp_print_char (mp, xord ('&'));
30622  mp_print_int (mp, (int) mp->pool_in_use);
30623  mp_print_ln (mp);
30624  mp_get_x_next (mp);
30625}
30626
30627
30628@ Here's a recursive procedure that gives an abbreviated account
30629of a variable, for use by |do_show_var|.
30630
30631@<Declare action procedures for use by |do_statement|@>=
30632static void mp_disp_var (MP mp, mp_node p);
30633
30634@ @c
30635void mp_disp_var (MP mp, mp_node p) {
30636  mp_node q;    /* traverses attributes and subscripts */
30637  int n;        /* amount of macro text to show */
30638  if (mp_type (p) == mp_structured) {
30639    @<Descend the structure@>;
30640  } else if (mp_type (p) >= mp_unsuffixed_macro) {
30641    @<Display a variable macro@>;
30642  } else if (mp_type (p) != mp_undefined) {
30643    mp_print_nl (mp, "");
30644    mp_print_variable_name (mp, p);
30645    mp_print_char (mp, xord ('='));
30646    mp_print_exp (mp, p, 0);
30647  }
30648}
30649
30650
30651@ @<Descend the structure@>=
30652{
30653  q = attr_head (p);
30654  do {
30655    mp_disp_var (mp, q);
30656    q = mp_link (q);
30657  } while (q != mp->end_attr);
30658  q = subscr_head (p);
30659  while (mp_name_type (q) == mp_subscr) {
30660    mp_disp_var (mp, q);
30661    q = mp_link (q);
30662  }
30663}
30664
30665
30666@ @<Display a variable macro@>=
30667{
30668  mp_print_nl (mp, "");
30669  mp_print_variable_name (mp, p);
30670  if (mp_type (p) > mp_unsuffixed_macro)
30671    mp_print (mp, "@@#");       /* |suffixed_macro| */
30672  mp_print (mp, "=macro:");
30673  if ((int) mp->file_offset >= mp->max_print_line - 20)
30674    n = 5;
30675  else
30676    n = mp->max_print_line - (int) mp->file_offset - 15;
30677  mp_show_macro (mp, value_node (p), NULL, n);
30678}
30679
30680
30681@ @<Declare action procedures for use by |do_statement|@>=
30682static void mp_do_show_var (MP mp);
30683
30684@ @c
30685void mp_do_show_var (MP mp) {
30686  do {
30687    get_t_next (mp);
30688    if (cur_sym() != NULL)
30689      if (cur_sym_mod() == 0)
30690        if (cur_cmd() == mp_tag_token)
30691          if (cur_mod() != 0 || cur_mod_node()!=NULL) {
30692            mp_disp_var (mp, cur_mod_node());
30693            goto DONE;
30694          }
30695    mp_disp_token (mp);
30696  DONE:
30697    mp_get_x_next (mp);
30698  } while (cur_cmd() == mp_comma);
30699}
30700
30701
30702@ @<Declare action procedures for use by |do_statement|@>=
30703static void mp_do_show_dependencies (MP mp);
30704
30705@ @c
30706void mp_do_show_dependencies (MP mp) {
30707  mp_value_node p;      /* link that runs through all dependencies */
30708  p = (mp_value_node) mp_link (mp->dep_head);
30709  while (p != mp->dep_head) {
30710    if (mp_interesting (mp, (mp_node) p)) {
30711      mp_print_nl (mp, "");
30712      mp_print_variable_name (mp, (mp_node) p);
30713      if (mp_type (p) == mp_dependent)
30714        mp_print_char (mp, xord ('='));
30715      else
30716        mp_print (mp, " = ");   /* extra spaces imply proto-dependency */
30717      mp_print_dependency (mp, (mp_value_node) dep_list (p), mp_type (p));
30718    }
30719    p = (mp_value_node) dep_list (p);
30720    while (dep_info (p) != NULL)
30721      p = (mp_value_node) mp_link (p);
30722    p = (mp_value_node) mp_link (p);
30723  }
30724  mp_get_x_next (mp);
30725}
30726
30727
30728@ Finally we are ready for the procedure that governs all of the
30729show commands.
30730
30731@<Declare action procedures for use by |do_statement|@>=
30732static void mp_do_show_whatever (MP mp);
30733
30734@ @c
30735void mp_do_show_whatever (MP mp) {
30736  if (mp->interaction == mp_error_stop_mode)
30737    wake_up_terminal();
30738  switch (cur_mod()) {
30739  case show_token_code:
30740    mp_do_show_token (mp);
30741    break;
30742  case show_stats_code:
30743    mp_do_show_stats (mp);
30744    break;
30745  case show_code:
30746    mp_do_show (mp);
30747    break;
30748  case show_var_code:
30749    mp_do_show_var (mp);
30750    break;
30751  case show_dependencies_code:
30752    mp_do_show_dependencies (mp);
30753    break;
30754  }                             /* there are no other cases */
30755  if (number_positive (internal_value (mp_showstopping))) {
30756    const char *hlp[] = {
30757         "This isn't an error message; I'm just showing something.",
30758         NULL };
30759    if (mp->interaction < mp_error_stop_mode) {
30760      hlp[0] = NULL;
30761      decr (mp->error_count);
30762    }
30763    if (cur_cmd() == mp_semicolon) {
30764      mp_error (mp, "OK", hlp, true);
30765    } else {
30766      mp_back_error (mp, "OK", hlp, true);
30767      mp_get_x_next (mp);
30768    }
30769@.OK@>;
30770  }
30771}
30772
30773
30774@ The `\&{addto}' command needs the following additional primitives:
30775
30776@d double_path_code 0 /* command modifier for `\&{doublepath}' */
30777@d contour_code 1 /* command modifier for `\&{contour}' */
30778@d also_code 2 /* command modifier for `\&{also}' */
30779
30780@ Pre and postscripts need two new identifiers:
30781
30782@d with_mp_pre_script 11
30783@d with_mp_post_script 13
30784
30785@<Put each...@>=
30786mp_primitive (mp, "doublepath", mp_thing_to_add, double_path_code);
30787@:double_path_}{\&{doublepath} primitive@>;
30788mp_primitive (mp, "contour", mp_thing_to_add, contour_code);
30789@:contour_}{\&{contour} primitive@>;
30790mp_primitive (mp, "also", mp_thing_to_add, also_code);
30791@:also_}{\&{also} primitive@>;
30792mp_primitive (mp, "withpen", mp_with_option, mp_pen_type);
30793@:with_pen_}{\&{withpen} primitive@>;
30794mp_primitive (mp, "dashed", mp_with_option, mp_picture_type);
30795@:dashed_}{\&{dashed} primitive@>;
30796mp_primitive (mp, "withprescript", mp_with_option, with_mp_pre_script);
30797@:with_mp_pre_script_}{\&{withprescript} primitive@>;
30798mp_primitive (mp, "withpostscript", mp_with_option, with_mp_post_script);
30799@:with_mp_post_script_}{\&{withpostscript} primitive@>;
30800mp_primitive (mp, "withoutcolor", mp_with_option, mp_no_model);
30801@:with_color_}{\&{withoutcolor} primitive@>;
30802mp_primitive (mp, "withgreyscale", mp_with_option, mp_grey_model);
30803@:with_color_}{\&{withgreyscale} primitive@>;
30804mp_primitive (mp, "withcolor", mp_with_option, mp_uninitialized_model);
30805@:with_color_}{\&{withcolor} primitive@>
30806/*  \&{withrgbcolor} is an alias for \&{withcolor} */
30807  mp_primitive (mp, "withrgbcolor", mp_with_option, mp_rgb_model);
30808@:with_color_}{\&{withrgbcolor} primitive@>;
30809mp_primitive (mp, "withcmykcolor", mp_with_option, mp_cmyk_model);
30810@:with_color_}{\&{withcmykcolor} primitive@>
30811
30812
30813@ @<Cases of |print_cmd...@>=
30814case mp_thing_to_add:
30815if (m == contour_code)
30816  mp_print (mp, "contour");
30817else if (m == double_path_code)
30818  mp_print (mp, "doublepath");
30819else
30820  mp_print (mp, "also");
30821break;
30822case mp_with_option:
30823if (m == mp_pen_type)
30824  mp_print (mp, "withpen");
30825else if (m == with_mp_pre_script)
30826  mp_print (mp, "withprescript");
30827else if (m == with_mp_post_script)
30828  mp_print (mp, "withpostscript");
30829else if (m == mp_no_model)
30830  mp_print (mp, "withoutcolor");
30831else if (m == mp_rgb_model)
30832  mp_print (mp, "withrgbcolor");
30833else if (m == mp_uninitialized_model)
30834  mp_print (mp, "withcolor");
30835else if (m == mp_cmyk_model)
30836  mp_print (mp, "withcmykcolor");
30837else if (m == mp_grey_model)
30838  mp_print (mp, "withgreyscale");
30839else
30840  mp_print (mp, "dashed");
30841break;
30842
30843@ The |scan_with_list| procedure parses a $\langle$with list$\rangle$ and
30844updates the list of graphical objects starting at |p|.  Each $\langle$with
30845clause$\rangle$ updates all graphical objects whose |type| is compatible.
30846Other objects are ignored.
30847
30848@<Declare action procedures for use by |do_statement|@>=
30849static void mp_scan_with_list (MP mp, mp_node p);
30850
30851@ Forcing the color to be between |0| and |unity| here guarantees that no
30852picture will ever contain a color outside the legal range for \ps\ graphics.
30853
30854@d make_cp_a_colored_object() do {
30855  cp = p;
30856  while (cp != NULL) {
30857    if (has_color (cp))
30858      break;
30859    cp = mp_link (cp);
30860  }
30861} while (0)
30862
30863@d clear_color(A) do {
30864  set_number_to_zero(((mp_stroked_node)(A))->cyan);
30865  set_number_to_zero(((mp_stroked_node)(A))->magenta);
30866  set_number_to_zero(((mp_stroked_node)(A))->yellow);
30867  set_number_to_zero(((mp_stroked_node)(A))->black);
30868  mp_color_model ((A)) = mp_uninitialized_model;
30869} while (0)
30870
30871@d set_color_val(A,B) do {
30872  number_clone(A, (B));
30873  if (number_negative(A))
30874    set_number_to_zero(A);
30875  if (number_greater(A,unity_t))
30876    set_number_to_unity(A);
30877} while (0)
30878
30879@c
30880static int is_invalid_with_list (MP mp, mp_variable_type t) {
30881  return ((t == with_mp_pre_script) && (mp->cur_exp.type != mp_string_type)) ||
30882        ((t == with_mp_post_script) && (mp->cur_exp.type != mp_string_type)) ||
30883        ((t == (mp_variable_type) mp_uninitialized_model) &&
30884         ((mp->cur_exp.type != mp_cmykcolor_type)
30885          && (mp->cur_exp.type != mp_color_type)
30886          && (mp->cur_exp.type != mp_known)
30887          && (mp->cur_exp.type != mp_boolean_type))) || ((t == (mp_variable_type) mp_cmyk_model)
30888                                                         && (mp->cur_exp.type !=
30889                                                             mp_cmykcolor_type))
30890        || ((t == (mp_variable_type) mp_rgb_model) && (mp->cur_exp.type != mp_color_type))
30891        || ((t == (mp_variable_type) mp_grey_model) && (mp->cur_exp.type != mp_known))
30892        || ((t == (mp_variable_type) mp_pen_type) && (mp->cur_exp.type != t))
30893        || ((t == (mp_variable_type) mp_picture_type) && (mp->cur_exp.type != t));
30894}
30895static void complain_invalid_with_list (MP mp, mp_variable_type t) {
30896   /* Complain about improper type */
30897   mp_value new_expr;
30898   const char *hlp[] = {
30899      "Next time say `withpen <known pen expression>';",
30900      "I'll ignore the bad `with' clause and look for another.",
30901      NULL };
30902   memset(&new_expr,0,sizeof(mp_value));
30903   new_number(new_expr.data.n);
30904   mp_disp_err(mp, NULL);
30905   if (t == with_mp_pre_script)
30906     hlp[0] = "Next time say `withprescript <known string expression>';";
30907   else if (t == with_mp_post_script)
30908     hlp[0] = "Next time say `withpostscript <known string expression>';";
30909   else if (t == mp_picture_type)
30910     hlp[0] = "Next time say `dashed <known picture expression>';";
30911   else if (t == (mp_variable_type) mp_uninitialized_model)
30912     hlp[0] = "Next time say `withcolor <known color expression>';";
30913   else if (t == (mp_variable_type) mp_rgb_model)
30914     hlp[0] = "Next time say `withrgbcolor <known color expression>';";
30915   else if (t == (mp_variable_type) mp_cmyk_model)
30916     hlp[0] = "Next time say `withcmykcolor <known cmykcolor expression>';";
30917   else if (t == (mp_variable_type) mp_grey_model)
30918     hlp[0] = "Next time say `withgreyscale <known numeric expression>';";;
30919   mp_back_error (mp, "Improper type", hlp, true);
30920   mp_get_x_next (mp);
30921   mp_flush_cur_exp (mp, new_expr);
30922}
30923
30924void mp_scan_with_list (MP mp, mp_node p) {
30925  mp_variable_type t;   /* |cur_mod| of the |with_option| (should match |cur_type|) */
30926  mp_node q;    /* for list manipulation */
30927  mp_node cp, pp, dp, ap, bp;
30928  /* objects being updated; |void| initially; |NULL| to suppress update */
30929  cp = MP_VOID;
30930  pp = MP_VOID;
30931  dp = MP_VOID;
30932  ap = MP_VOID;
30933  bp = MP_VOID;
30934  while (cur_cmd() == mp_with_option) {
30935    /* todo this is not very nice: the color models have their own enumeration */
30936    t = (mp_variable_type) cur_mod();
30937    mp_get_x_next (mp);
30938    if (t != (mp_variable_type) mp_no_model)
30939      mp_scan_expression (mp);
30940    if (is_invalid_with_list(mp, t)) {
30941      complain_invalid_with_list (mp, t);
30942      continue;
30943    }
30944    if (t == (mp_variable_type) mp_uninitialized_model) {
30945      mp_value new_expr;
30946      memset(&new_expr,0,sizeof(mp_value));
30947      new_number(new_expr.data.n);
30948      if (cp == MP_VOID)
30949        make_cp_a_colored_object();
30950      if (cp != NULL) {
30951        /* Transfer a color from the current expression to object~|cp| */
30952        if (mp->cur_exp.type == mp_color_type) {
30953          /* Transfer a rgbcolor from the current expression to object~|cp| */
30954          mp_stroked_node cp0 = (mp_stroked_node)cp;
30955          q = value_node (cur_exp_node ());
30956          clear_color(cp0);
30957          mp_color_model (cp) = mp_rgb_model;
30958          set_color_val (cp0->red, value_number (red_part (q)));
30959          set_color_val (cp0->green, value_number (green_part (q)));
30960          set_color_val (cp0->blue, value_number (blue_part (q)));
30961        } else if (mp->cur_exp.type == mp_cmykcolor_type) {
30962          /* Transfer a cmykcolor from the current expression to object~|cp| */
30963          mp_stroked_node cp0 = (mp_stroked_node)cp;
30964          q = value_node (cur_exp_node ());
30965          set_color_val (cp0->cyan, value_number (cyan_part (q)));
30966          set_color_val (cp0->magenta, value_number (magenta_part (q)));
30967          set_color_val (cp0->yellow, value_number (yellow_part (q)));
30968          set_color_val (cp0->black, value_number (black_part (q)));
30969          mp_color_model (cp) = mp_cmyk_model;
30970        } else if (mp->cur_exp.type == mp_known) {
30971          /* Transfer a greyscale from the current expression to object~|cp| */
30972          mp_number qq;
30973          mp_stroked_node cp0 = (mp_stroked_node)cp;
30974          new_number (qq);
30975          number_clone (qq, cur_exp_value_number ());
30976          clear_color (cp);
30977          mp_color_model (cp) = mp_grey_model;
30978          set_color_val (cp0->grey, qq);
30979          free_number (qq);
30980        } else if (cur_exp_value_boolean () == mp_false_code) {
30981          /* Transfer a noncolor from the current expression to object~|cp| */
30982          clear_color (cp);
30983          mp_color_model (cp) = mp_no_model;
30984        } else if (cur_exp_value_boolean () == mp_true_code) {
30985          /* Transfer no color from the current expression to object~|cp| */
30986          clear_color (cp);
30987          mp_color_model (cp) = mp_uninitialized_model;
30988        }
30989      }
30990      mp_flush_cur_exp (mp, new_expr);
30991    } else if (t == (mp_variable_type) mp_rgb_model) {
30992      mp_value new_expr;
30993      memset(&new_expr,0,sizeof(mp_value));
30994      new_number(new_expr.data.n);
30995      if (cp == MP_VOID)
30996        make_cp_a_colored_object();
30997      if (cp != NULL) {
30998        /* Transfer a rgbcolor from the current expression to object~|cp| */
30999        mp_stroked_node cp0 = (mp_stroked_node)cp;
31000        q = value_node (cur_exp_node ());
31001        clear_color(cp0);
31002        mp_color_model (cp) = mp_rgb_model;
31003        set_color_val (cp0->red, value_number (red_part (q)));
31004        set_color_val (cp0->green, value_number (green_part (q)));
31005        set_color_val (cp0->blue, value_number (blue_part (q)));
31006      }
31007      mp_flush_cur_exp (mp, new_expr);
31008    } else if (t == (mp_variable_type) mp_cmyk_model) {
31009      mp_value new_expr;
31010      memset(&new_expr,0,sizeof(mp_value));
31011      new_number(new_expr.data.n);
31012      if (cp == MP_VOID)
31013        make_cp_a_colored_object();
31014      if (cp != NULL) {
31015        /* Transfer a cmykcolor from the current expression to object~|cp| */
31016        mp_stroked_node cp0 = (mp_stroked_node)cp;
31017        q = value_node (cur_exp_node ());
31018        set_color_val (cp0->cyan, value_number (cyan_part (q)));
31019        set_color_val (cp0->magenta, value_number (magenta_part (q)));
31020        set_color_val (cp0->yellow, value_number (yellow_part (q)));
31021        set_color_val (cp0->black, value_number (black_part (q)));
31022        mp_color_model (cp) = mp_cmyk_model;
31023      }
31024      mp_flush_cur_exp (mp, new_expr);
31025    } else if (t == (mp_variable_type) mp_grey_model) {
31026      mp_value new_expr;
31027      memset(&new_expr,0,sizeof(mp_value));
31028      new_number(new_expr.data.n);
31029      if (cp == MP_VOID)
31030        make_cp_a_colored_object();
31031      if (cp != NULL) {
31032        /* Transfer a greyscale from the current expression to object~|cp| */
31033        mp_number qq;
31034        mp_stroked_node cp0 = (mp_stroked_node)cp;
31035        new_number (qq);
31036        number_clone (qq, cur_exp_value_number ());
31037        clear_color (cp);
31038        mp_color_model (cp) = mp_grey_model;
31039        set_color_val (cp0->grey, qq);
31040        free_number (qq);
31041      }
31042      mp_flush_cur_exp (mp, new_expr);
31043    } else if (t == (mp_variable_type) mp_no_model) {
31044      if (cp == MP_VOID)
31045        make_cp_a_colored_object();
31046      if (cp != NULL) {
31047        /* Transfer a noncolor from the current expression to object~|cp| */
31048        clear_color (cp);
31049        mp_color_model (cp) = mp_no_model;
31050      }
31051    } else if (t == mp_pen_type) {
31052      if (pp == MP_VOID) {
31053        /* Make |pp| an object in list~|p| that needs a pen */
31054        pp = p;
31055        while (pp != NULL) {
31056          if (has_pen (pp))
31057            break;
31058          pp = mp_link (pp);
31059        }
31060      }
31061
31062      if (pp != NULL) {
31063        switch (mp_type (pp)) {
31064        case mp_fill_node_type:
31065          if (mp_pen_p ((mp_fill_node) pp) != NULL)
31066            mp_toss_knot_list (mp, mp_pen_p ((mp_fill_node) pp));
31067          mp_pen_p ((mp_fill_node) pp) = cur_exp_knot ();
31068          break;
31069        case mp_stroked_node_type:
31070          if (mp_pen_p ((mp_stroked_node) pp) != NULL)
31071            mp_toss_knot_list (mp, mp_pen_p ((mp_stroked_node) pp));
31072          mp_pen_p ((mp_stroked_node) pp) = cur_exp_knot ();
31073          break;
31074        default:
31075          assert (0);
31076          break;
31077        }
31078        mp->cur_exp.type = mp_vacuous;
31079      }
31080    } else if (t == with_mp_pre_script) {
31081      if (cur_exp_str ()->len) {
31082        if (ap == MP_VOID)
31083          ap = p;
31084        while ((ap != NULL) && (!has_color (ap)))
31085          ap = mp_link (ap);
31086        if (ap != NULL) {
31087          if (mp_pre_script (ap) != NULL) {       /*  build a new,combined string  */
31088            unsigned old_setting; /* saved |selector| setting */
31089            mp_string s; /* for string cleanup after combining  */
31090            s = mp_pre_script (ap);
31091            old_setting = mp->selector;
31092            mp->selector = new_string;
31093            str_room (mp_pre_script (ap)->len + cur_exp_str ()->len + 2);
31094            mp_print_str (mp, cur_exp_str ());
31095            append_char (13);     /* a forced \ps\ newline  */
31096            mp_print_str (mp, mp_pre_script (ap));
31097            mp_pre_script (ap) = mp_make_string (mp);
31098            delete_str_ref (s);
31099            mp->selector = old_setting;
31100          } else {
31101            mp_pre_script (ap) = cur_exp_str ();
31102          }
31103          add_str_ref (mp_pre_script (ap));
31104          mp->cur_exp.type = mp_vacuous;
31105        }
31106      }
31107    } else if (t == with_mp_post_script) {
31108      if (cur_exp_str ()->len) {
31109        mp_node k = NULL;    /* for finding the near-last item in a list  */
31110        if (bp == MP_VOID)
31111          k = p;
31112        bp = k;
31113        while (k && mp_link (k) != NULL) { /* clang: dereference null pointer 'k' */
31114          k = mp_link (k);
31115          if (has_color (k))
31116            bp = k;
31117        }
31118        if (bp != NULL) {
31119          if (mp_post_script (bp) != NULL) {
31120            unsigned old_setting; /* saved |selector| setting */
31121            mp_string s; /* for string cleanup after combining  */
31122            s = mp_post_script (bp);
31123            old_setting = mp->selector;
31124            mp->selector = new_string;
31125            str_room (mp_post_script (bp)->len + cur_exp_str ()->len + 2);
31126            mp_print_str (mp, mp_post_script (bp));
31127            append_char (13);     /* a forced \ps\ newline  */
31128            mp_print_str (mp, cur_exp_str ());
31129            mp_post_script (bp) = mp_make_string (mp);
31130            delete_str_ref (s);
31131            mp->selector = old_setting;
31132          } else {
31133            mp_post_script (bp) = cur_exp_str ();
31134          }
31135          add_str_ref (mp_post_script (bp));
31136          mp->cur_exp.type = mp_vacuous;
31137        }
31138      }
31139    } else {
31140      if (dp == MP_VOID) {
31141        /* Make |dp| a stroked node in list~|p| */
31142        dp = p;
31143        while (dp != NULL) {
31144          if (mp_type (dp) == mp_stroked_node_type)
31145            break;
31146          dp = mp_link (dp);
31147        }
31148      }
31149      if (dp != NULL) {
31150        if (mp_dash_p (dp) != NULL)
31151          delete_edge_ref (mp_dash_p (dp));
31152        mp_dash_p (dp) = (mp_node)mp_make_dashes (mp, (mp_edge_header_node)cur_exp_node ());
31153        set_number_to_unity(((mp_stroked_node)dp)->dash_scale);
31154        mp->cur_exp.type = mp_vacuous;
31155      }
31156    }
31157  }
31158  /* Copy the information from objects |cp|, |pp|, and |dp| into the rest
31159    of the list */
31160  if (cp > MP_VOID) {
31161    /* Copy |cp|'s color into the colored objects linked to~|cp| */
31162    q = mp_link (cp);
31163    while (q != NULL) {
31164      if (has_color (q)) {
31165        mp_stroked_node q0 = (mp_stroked_node)q;
31166        mp_stroked_node cp0 = (mp_stroked_node)cp;
31167        number_clone(q0->red,   cp0->red);
31168        number_clone(q0->green, cp0->green);
31169        number_clone(q0->blue,  cp0->blue);
31170        number_clone(q0->black, cp0->black);
31171        mp_color_model (q) = mp_color_model (cp);
31172      }
31173      q = mp_link (q);
31174    }
31175  }
31176  if (pp > MP_VOID) {
31177    /* Copy |mp_pen_p(pp)| into stroked and filled nodes linked to |pp| */
31178    q = mp_link (pp);
31179    while (q != NULL) {
31180      if (has_pen (q)) {
31181        switch (mp_type (q)) {
31182        case mp_fill_node_type:
31183          if (mp_pen_p ((mp_fill_node) q) != NULL)
31184            mp_toss_knot_list (mp, mp_pen_p ((mp_fill_node) q));
31185          mp_pen_p ((mp_fill_node) q) = copy_pen (mp_pen_p ((mp_fill_node) pp));
31186          break;
31187        case mp_stroked_node_type:
31188          if (mp_pen_p ((mp_stroked_node) q) != NULL)
31189            mp_toss_knot_list (mp, mp_pen_p ((mp_stroked_node) q));
31190          mp_pen_p ((mp_stroked_node) q) =
31191            copy_pen (mp_pen_p ((mp_stroked_node) pp));
31192          break;
31193        default:
31194          assert (0);
31195          break;
31196        }
31197      }
31198      q = mp_link (q);
31199    }
31200  }
31201  if (dp > MP_VOID) {
31202    /* Make stroked nodes linked to |dp| refer to |mp_dash_p(dp)| */
31203    q = mp_link (dp);
31204    while (q != NULL) {
31205      if (mp_type (q) == mp_stroked_node_type) {
31206        if (mp_dash_p (q) != NULL)
31207          delete_edge_ref (mp_dash_p (q));
31208        mp_dash_p (q) = mp_dash_p (dp);
31209        set_number_to_unity(((mp_stroked_node)q)->dash_scale);
31210        if (mp_dash_p (q) != NULL)
31211          add_edge_ref (mp_dash_p (q));
31212      }
31213      q = mp_link (q);
31214    }
31215  }
31216}
31217
31218
31219@ One of the things we need to do when we've parsed an \&{addto} or
31220similar command is find the header of a supposed \&{picture} variable, given
31221a token list for that variable.  Since the edge structure is about to be
31222updated, we use |private_edges| to make sure that this is possible.
31223
31224@<Declare action procedures for use by |do_statement|@>=
31225static mp_edge_header_node mp_find_edges_var (MP mp, mp_node t);
31226
31227@ @c
31228mp_edge_header_node mp_find_edges_var (MP mp, mp_node t) {
31229  mp_node p;
31230  mp_edge_header_node cur_edges;    /* the return value */
31231  p = mp_find_variable (mp, t);
31232  cur_edges = NULL;
31233  if (p == NULL) {
31234    const char *hlp[] = {
31235         "It seems you did a nasty thing---probably by accident,",
31236         "but nevertheless you nearly hornswoggled me...",
31237         "While I was evaluating the right-hand side of this",
31238         "command, something happened, and the left-hand side",
31239         "is no longer a variable! So I won't change anything.",
31240         NULL };
31241    char *msg = mp_obliterated (mp, t);
31242    mp_back_error (mp, msg, hlp, true);
31243    free(msg);
31244    mp_get_x_next (mp);
31245  } else if (mp_type (p) != mp_picture_type) {
31246    char msg[256];
31247    mp_string sname;
31248    int old_setting = mp->selector;
31249    const char *hlp[] = {
31250           "I was looking for a \"known\" picture variable.",
31251           "So I'll not change anything just now.",
31252           NULL };
31253    mp->selector = new_string;
31254    mp_show_token_list (mp, t, NULL, 1000, 0);
31255    sname = mp_make_string(mp);
31256    mp->selector = old_setting;
31257    mp_snprintf (msg, 256, "Variable %s is the wrong type(%s)",
31258                 mp_str(mp, sname), mp_type_string(mp_type (p)));
31259@.Variable x is the wrong type@>;
31260    delete_str_ref(sname);
31261    mp_back_error (mp, msg, hlp, true);
31262    mp_get_x_next (mp);
31263  } else {
31264    set_value_node (p, (mp_node)mp_private_edges (mp, (mp_edge_header_node)value_node (p)));
31265    cur_edges = (mp_edge_header_node)value_node (p);
31266  }
31267  mp_flush_node_list (mp, t);
31268  return cur_edges;
31269}
31270
31271
31272@ @<Put each...@>=
31273mp_primitive (mp, "clip", mp_bounds_command, mp_start_clip_node_type);
31274@:clip_}{\&{clip} primitive@>;
31275mp_primitive (mp, "setbounds", mp_bounds_command, mp_start_bounds_node_type);
31276@:set_bounds_}{\&{setbounds} primitive@>
31277
31278
31279@ @<Cases of |print_cmd...@>=
31280case mp_bounds_command:
31281if (m == mp_start_clip_node_type)
31282  mp_print (mp, "clip");
31283else
31284  mp_print (mp, "setbounds");
31285break;
31286
31287@ The following function parses the beginning of an \&{addto} or \&{clip}
31288command: it expects a variable name followed by a token with |cur_cmd=sep|
31289and then an expression.  The function returns the token list for the variable
31290and stores the command modifier for the separator token in the global variable
31291|last_add_type|.  We must be careful because this variable might get overwritten
31292any time we call |get_x_next|.
31293
31294@<Glob...@>=
31295quarterword last_add_type;
31296  /* command modifier that identifies the last \&{addto} command */
31297
31298@ @<Declare action procedures for use by |do_statement|@>=
31299static mp_node mp_start_draw_cmd (MP mp, quarterword sep);
31300
31301@ @c
31302mp_node mp_start_draw_cmd (MP mp, quarterword sep) {
31303  mp_node lhv;  /* variable to add to left */
31304  quarterword add_type = 0;     /* value to be returned in |last_add_type| */
31305  lhv = NULL;
31306  mp_get_x_next (mp);
31307  mp->var_flag = sep;
31308  mp_scan_primary (mp);
31309  if (mp->cur_exp.type != mp_token_list) {
31310    /* Abandon edges command because there's no variable */
31311    mp_value new_expr;
31312    const char *hlp[] = {
31313         "At this point I needed to see the name of a picture variable.",
31314         "(Or perhaps you have indeed presented me with one; I might",
31315         "have missed it, if it wasn't followed by the proper token.)",
31316         "So I'll not change anything just now.",
31317         NULL };
31318    memset(&new_expr,0,sizeof(mp_value));
31319    new_number(new_expr.data.n);
31320    mp_disp_err(mp, NULL);
31321    set_number_to_zero (new_expr.data.n);
31322    mp_back_error (mp, "Not a suitable variable", hlp, true);
31323    mp_get_x_next (mp);
31324    mp_flush_cur_exp (mp, new_expr);
31325  } else {
31326    lhv = cur_exp_node ();
31327    add_type = (quarterword) cur_mod();
31328    mp->cur_exp.type = mp_vacuous;
31329    mp_get_x_next (mp);
31330    mp_scan_expression (mp);
31331  }
31332  mp->last_add_type = add_type;
31333  return lhv;
31334}
31335
31336@ Here is an example of how to use |start_draw_cmd|.
31337
31338@<Declare action procedures for use by |do_statement|@>=
31339static void mp_do_bounds (MP mp);
31340
31341@ @c
31342void mp_do_bounds (MP mp) {
31343  mp_node lhv;     /* variable on left, the corresponding edge structure */
31344  mp_edge_header_node lhe;
31345  mp_node p;    /* for list manipulation */
31346  integer m;    /* initial value of |cur_mod| */
31347  m = cur_mod();
31348  lhv = mp_start_draw_cmd (mp, mp_to_token);
31349  if (lhv != NULL) {
31350    mp_value new_expr;
31351    memset(&new_expr,0,sizeof(mp_value));
31352    lhe = mp_find_edges_var (mp, lhv);
31353    if (lhe == NULL) {
31354      new_number(new_expr.data.n);
31355      set_number_to_zero (new_expr.data.n);
31356      mp_flush_cur_exp (mp, new_expr);
31357    } else if (mp->cur_exp.type != mp_path_type) {
31358      const char *hlp[] ={
31359             "This expression should have specified a known path.",
31360             "So I'll not change anything just now.",
31361              NULL };
31362      mp_disp_err(mp, NULL);
31363      new_number(new_expr.data.n);
31364      set_number_to_zero (new_expr.data.n);
31365      mp_back_error (mp, "Improper `clip'", hlp, true);
31366      mp_get_x_next (mp);
31367      mp_flush_cur_exp (mp, new_expr);
31368    } else if (mp_left_type (cur_exp_knot ()) == mp_endpoint) {
31369      /* Complain about a non-cycle */
31370      const char *hlp[] = {
31371            "That contour should have ended with `..cycle' or `&cycle'.",
31372            "So I'll not change anything just now.",
31373            NULL };
31374      mp_back_error (mp, "Not a cycle" , hlp, true);
31375      mp_get_x_next (mp);
31376    } else {
31377      /* Make |cur_exp| into a \&{setbounds} or clipping path and add it to |lhe| */
31378      p = mp_new_bounds_node (mp, cur_exp_knot (), (quarterword) m);
31379      mp_link (p) = mp_link (edge_list (lhe));
31380      mp_link (edge_list (lhe)) = p;
31381      if (obj_tail (lhe) == edge_list (lhe))
31382        obj_tail (lhe) = p;
31383      if (m == mp_start_clip_node_type) {
31384        p = mp_new_bounds_node (mp, NULL, mp_stop_clip_node_type);
31385      } else if (m == mp_start_bounds_node_type) {
31386        p = mp_new_bounds_node (mp, NULL, mp_stop_bounds_node_type);
31387      }
31388      mp_link (obj_tail (lhe)) = p;
31389      obj_tail (lhe) = p;
31390      mp_init_bbox (mp, lhe);
31391    }
31392  }
31393}
31394
31395
31396@ The |do_add_to| procedure is a little like |do_clip| but there are a lot more
31397cases to deal with.
31398
31399@<Declare action procedures for use by |do_statement|@>=
31400static void mp_do_add_to (MP mp);
31401
31402@ @c
31403void mp_do_add_to (MP mp) {
31404  mp_node lhv;
31405  mp_edge_header_node lhe;     /* variable on left, the corresponding edge structure */
31406  mp_node p;    /* the graphical object or list for |scan_with_list| to update */
31407  mp_edge_header_node e;    /* an edge structure to be merged */
31408  quarterword add_type; /* |also_code|, |contour_code|, or |double_path_code| */
31409  lhv = mp_start_draw_cmd (mp, mp_thing_to_add);
31410  add_type = mp->last_add_type;
31411  if (lhv != NULL) {
31412    if (add_type == also_code) {
31413      /* Make sure the current expression is a suitable picture and set |e| and |p|
31414         appropriately */
31415      /* Setting |p:=NULL| causes the $\langle$with list$\rangle$ to be ignored;
31416         setting |e:=NULL| prevents anything from being added to |lhe|. */
31417      p = NULL;
31418      e = NULL;
31419      if (mp->cur_exp.type != mp_picture_type) {
31420        mp_value new_expr;
31421        const char *hlp[]= {
31422               "This expression should have specified a known picture.",
31423               "So I'll not change anything just now.",
31424               NULL };
31425        memset(&new_expr,0,sizeof(mp_value));
31426        new_number(new_expr.data.n);
31427        mp_disp_err(mp, NULL);
31428        set_number_to_zero (new_expr.data.n);
31429        mp_back_error (mp, "Improper `addto'", hlp, true);
31430        mp_get_x_next (mp);
31431        mp_flush_cur_exp (mp, new_expr);
31432      } else {
31433        e = mp_private_edges (mp, (mp_edge_header_node)cur_exp_node ());
31434        mp->cur_exp.type = mp_vacuous;
31435        p = mp_link (edge_list (e));
31436      }
31437
31438    } else {
31439      /* Create a graphical object |p| based on |add_type| and the current
31440         expression */
31441      /* In this case |add_type<>also_code| so setting |p:=NULL| suppresses future
31442         attempts to add to the edge structure. */
31443      e = NULL;
31444      p = NULL;
31445      if (mp->cur_exp.type == mp_pair_type)
31446        mp_pair_to_path (mp);
31447      if (mp->cur_exp.type != mp_path_type) {
31448        mp_value new_expr;
31449        const char *hlp[] = {
31450               "This expression should have specified a known path.",
31451               "So I'll not change anything just now.",
31452               NULL };
31453        memset(&new_expr,0,sizeof(mp_value));
31454        new_number(new_expr.data.n);
31455        mp_disp_err(mp, NULL);
31456        set_number_to_zero (new_expr.data.n);
31457        mp_back_error (mp, "Improper `addto'", hlp, true);
31458        mp_get_x_next (mp);
31459        mp_flush_cur_exp (mp, new_expr);
31460      } else if (add_type == contour_code) {
31461        if (mp_left_type (cur_exp_knot ()) == mp_endpoint) {
31462          /* Complain about a non-cycle */
31463          const char *hlp[] = {
31464                "That contour should have ended with `..cycle' or `&cycle'.",
31465                "So I'll not change anything just now.",
31466                NULL };
31467          mp_back_error (mp, "Not a cycle" , hlp, true);
31468          mp_get_x_next (mp);
31469
31470        } else {
31471          p = mp_new_fill_node (mp, cur_exp_knot ());
31472          mp->cur_exp.type = mp_vacuous;
31473        }
31474      } else {
31475        p = mp_new_stroked_node (mp, cur_exp_knot ());
31476        mp->cur_exp.type = mp_vacuous;
31477      }
31478
31479    }
31480    mp_scan_with_list (mp, p);
31481    /* Use |p|, |e|, and |add_type| to augment |lhv| as requested */
31482    lhe = mp_find_edges_var (mp, lhv);
31483    if (lhe == NULL) {
31484      if ((e == NULL) && (p != NULL))
31485        e = mp_toss_gr_object (mp, p);
31486      if (e != NULL)
31487        delete_edge_ref (e);
31488    } else if (add_type == also_code) {
31489      if (e != NULL) {
31490        /* Merge |e| into |lhe| and delete |e| */
31491        if (mp_link (edge_list (e)) != NULL) {
31492          mp_link (obj_tail (lhe)) = mp_link (edge_list (e));
31493          obj_tail (lhe) = obj_tail (e);
31494          obj_tail (e) = edge_list (e);
31495          mp_link (edge_list (e)) = NULL;
31496          mp_flush_dash_list (mp, lhe);
31497        }
31498        mp_toss_edges (mp, e);
31499      }
31500    } else if (p != NULL) {
31501      mp_link (obj_tail (lhe)) = p;
31502      obj_tail (lhe) = p;
31503      if (add_type == double_path_code) {
31504        if (mp_pen_p ((mp_stroked_node) p) == NULL) {
31505          mp_pen_p ((mp_stroked_node) p) = mp_get_pen_circle (mp, zero_t);
31506        }
31507      }
31508    }
31509  }
31510}
31511
31512@ @<Declare action procedures for use by |do_statement|@>=
31513@<Declare the \ps\ output procedures@>;
31514static void mp_do_ship_out (MP mp);
31515
31516@ @c
31517void mp_do_ship_out (MP mp) {
31518  integer c;    /* the character code */
31519  mp_value new_expr;
31520  memset(&new_expr,0,sizeof(mp_value));
31521  new_number(new_expr.data.n);
31522  mp_get_x_next (mp);
31523  mp_scan_expression (mp);
31524  if (mp->cur_exp.type != mp_picture_type) {
31525    @<Complain that it's not a known picture@>;
31526  } else {
31527    c = round_unscaled (internal_value (mp_char_code)) % 256;
31528    if (c < 0)
31529      c = c + 256;
31530    @<Store the width information for character code~|c|@>;
31531    mp_ship_out (mp, cur_exp_node ());
31532    set_number_to_zero (new_expr.data.n);
31533    mp_flush_cur_exp (mp, new_expr);
31534  }
31535}
31536
31537
31538@ @<Complain that it's not a known picture@>=
31539{
31540  const  char *hlp[] = { "I can only output known pictures.", NULL };
31541  mp_disp_err(mp, NULL);
31542  set_number_to_zero (new_expr.data.n);
31543  mp_back_error (mp, "Not a known picture", hlp, true);
31544  mp_get_x_next (mp);
31545  mp_flush_cur_exp (mp, new_expr);
31546}
31547
31548
31549@ The \&{everyjob} command simply assigns a nonzero value to the global variable
31550|start_sym|.
31551
31552
31553@ @<Glob...@>=
31554mp_sym start_sym;       /* a symbolic token to insert at beginning of job */
31555
31556@ @<Set init...@>=
31557mp->start_sym = NULL;
31558
31559@ Finally, we have only the ``message'' commands remaining.
31560
31561@d message_code 0
31562@d err_message_code 1
31563@d err_help_code 2
31564@d filename_template_code 3
31565@d print_with_leading_zeroes(A,B)  do {
31566              size_t g = mp->cur_length;
31567              size_t f = (size_t)(B);
31568              mp_print_int(mp, (A));
31569              g = mp->cur_length - g;
31570              if ( f>g ) {
31571                mp->cur_length = mp->cur_length - g;
31572                while ( f>g ) {
31573                  mp_print_char(mp, xord('0'));
31574                  decr(f);
31575                };
31576                mp_print_int(mp, (A));
31577              };
31578              f = 0;
31579          } while (0)
31580
31581@<Put each...@>=
31582mp_primitive (mp, "message", mp_message_command, message_code);
31583@:message_}{\&{message} primitive@>;
31584mp_primitive (mp, "errmessage", mp_message_command, err_message_code);
31585@:err_message_}{\&{errmessage} primitive@>;
31586mp_primitive (mp, "errhelp", mp_message_command, err_help_code);
31587@:err_help_}{\&{errhelp} primitive@>;
31588mp_primitive (mp, "filenametemplate", mp_message_command, filename_template_code);
31589@:filename_template_}{\&{filenametemplate} primitive@>
31590
31591
31592@ @<Cases of |print_cmd...@>=
31593case mp_message_command:
31594if (m < err_message_code)
31595  mp_print (mp, "message");
31596else if (m == err_message_code)
31597  mp_print (mp, "errmessage");
31598else if (m == filename_template_code)
31599  mp_print (mp, "filenametemplate");
31600else
31601  mp_print (mp, "errhelp");
31602break;
31603
31604@ @<Declare action procedures for use by |do_statement|@>=
31605@<Declare a procedure called |no_string_err|@>;
31606static void mp_do_message (MP mp);
31607
31608@
31609@c
31610void mp_do_message (MP mp) {
31611  int m;        /* the type of message */
31612  mp_value new_expr;
31613  m = cur_mod();
31614  memset(&new_expr,0,sizeof(mp_value));
31615  new_number(new_expr.data.n);
31616  mp_get_x_next (mp);
31617  mp_scan_expression (mp);
31618  if (mp->cur_exp.type != mp_string_type)
31619    mp_no_string_err (mp, "A message should be a known string expression.");
31620  else {
31621    switch (m) {
31622    case message_code:
31623      mp_print_nl (mp, "");
31624      mp_print_str (mp, cur_exp_str ());
31625      break;
31626    case err_message_code:
31627      @<Print string |cur_exp| as an error message@>;
31628      break;
31629    case err_help_code:
31630      @<Save string |cur_exp| as the |err_help|@>;
31631      break;
31632    case filename_template_code:
31633      @<Save the filename template@>;
31634      break;
31635    }                           /* there are no other cases */
31636  }
31637  set_number_to_zero (new_expr.data.n);
31638  mp_flush_cur_exp (mp, new_expr);
31639}
31640
31641
31642@ @<Save the filename template@>=
31643{
31644  delete_str_ref (internal_string (mp_output_template));
31645  if (cur_exp_str ()->len == 0) {
31646    set_internal_string (mp_output_template, mp_rts (mp, "%j.%c"));
31647  } else {
31648    set_internal_string (mp_output_template, cur_exp_str ());
31649    add_str_ref (internal_string (mp_output_template));
31650  }
31651}
31652
31653
31654@ @<Declare a procedure called |no_string_err|@>=
31655static void mp_no_string_err (MP mp, const char *s) {
31656  const char *hlp[] = {s, NULL};
31657  mp_disp_err(mp, NULL);
31658  mp_back_error (mp, "Not a string", hlp, true);
31659@.Not a string@>;
31660  mp_get_x_next (mp);
31661}
31662
31663
31664@ The global variable |err_help| is zero when the user has most recently
31665given an empty help string, or if none has ever been given.
31666
31667@<Save string |cur_exp| as the |err_help|@>=
31668{
31669  if (mp->err_help != NULL)
31670    delete_str_ref (mp->err_help);
31671  if (cur_exp_str ()->len == 0)
31672    mp->err_help = NULL;
31673  else {
31674    mp->err_help = cur_exp_str ();
31675    add_str_ref (mp->err_help);
31676  }
31677}
31678
31679
31680@ If \&{errmessage} occurs often in |mp_scroll_mode|, without user-defined
31681\&{errhelp}, we don't want to give a long help message each time. So we
31682give a verbose explanation only once.
31683
31684@<Glob...@>=
31685boolean long_help_seen; /* has the long \.{\\errmessage} help been used? */
31686
31687@ @<Set init...@>=
31688mp->long_help_seen = false;
31689
31690@ @<Print string |cur_exp| as an error message@>=
31691{
31692  char msg[256];
31693  mp_snprintf(msg, 256, "%s", mp_str(mp, cur_exp_str ()));
31694  if (mp->err_help != NULL) {
31695    mp->use_err_help = true;
31696    mp_back_error (mp, msg, NULL, true);
31697  } else if (mp->long_help_seen) {
31698    const char *hlp[] = { "(That was another `errmessage'.)", NULL };
31699    mp_back_error (mp, msg, hlp, true);
31700  } else {
31701    const char *hlp[] = {
31702           "This error message was generated by an `errmessage'",
31703           "command, so I can\'t give any explicit help.",
31704           "Pretend that you're Miss Marple: Examine all clues,",
31705           "and deduce the truth by inspired guesses.",
31706           NULL };
31707@^Marple, Jane@>
31708    if (mp->interaction < mp_error_stop_mode)
31709      mp->long_help_seen = true;
31710    mp_back_error (mp, msg, hlp, true);
31711  }
31712  mp_get_x_next (mp);
31713  mp->use_err_help = false;
31714}
31715
31716
31717@ @<Declare action procedures for use by |do_statement|@>=
31718static void mp_do_write (MP mp);
31719
31720@ @c
31721void mp_do_write (MP mp) {
31722  mp_string t; /* the line of text to be written */
31723  write_index n, n0;    /* for searching |wr_fname| and |wr_file| arrays */
31724  unsigned old_setting; /* for saving |selector| during output */
31725  mp_value new_expr;
31726  memset(&new_expr,0,sizeof(mp_value));
31727  new_number(new_expr.data.n);
31728  mp_get_x_next (mp);
31729  mp_scan_expression (mp);
31730  if (mp->cur_exp.type != mp_string_type) {
31731    mp_no_string_err (mp,
31732                      "The text to be written should be a known string expression");
31733  } else if (cur_cmd() != mp_to_token) {
31734    const char *hlp[] = { "A write command should end with `to <filename>'", NULL };
31735    mp_back_error (mp, "Missing `to' clause", hlp, true);
31736    mp_get_x_next (mp);
31737  } else {
31738    t = cur_exp_str ();
31739    mp->cur_exp.type = mp_vacuous;
31740    mp_get_x_next (mp);
31741    mp_scan_expression (mp);
31742    if (mp->cur_exp.type != mp_string_type)
31743      mp_no_string_err (mp,
31744                        "I can\'t write to that file name.  It isn't a known string");
31745    else {
31746      @<Write |t| to the file named by |cur_exp|@>;
31747    }
31748    /* |delete_str_ref(t);| *//* todo: is this right? */
31749  }
31750  set_number_to_zero (new_expr.data.n);
31751  mp_flush_cur_exp (mp, new_expr);
31752}
31753
31754
31755@ @<Write |t| to the file named by |cur_exp|@>=
31756{
31757  @<Find |n| where |wr_fname[n]=cur_exp| and call |open_write_file| if
31758    |cur_exp| must be inserted@>;
31759  if (mp_str_vs_str (mp, t, mp->eof_line) == 0) {
31760    @<Record the end of file on |wr_file[n]|@>;
31761  } else {
31762    old_setting = mp->selector;
31763    mp->selector = n + write_file;
31764    mp_print_str (mp, t);
31765    mp_print_ln (mp);
31766    mp->selector = old_setting;
31767  }
31768}
31769
31770
31771@ @<Find |n| where |wr_fname[n]=cur_exp| and call |open_write_file| if...@>=
31772{
31773  char *fn = mp_str (mp, cur_exp_str ());
31774  n = mp->write_files;
31775  n0 = mp->write_files;
31776  while (mp_xstrcmp (fn, mp->wr_fname[n]) != 0) {
31777    if (n == 0) {               /* bottom reached */
31778      if (n0 == mp->write_files) {
31779        if (mp->write_files < mp->max_write_files) {
31780          incr (mp->write_files);
31781        } else {
31782          void **wr_file;
31783          char **wr_fname;
31784          write_index l, k;
31785          l = mp->max_write_files + (mp->max_write_files / 4);
31786          wr_file = xmalloc ((l + 1), sizeof (void *));
31787          wr_fname = xmalloc ((l + 1), sizeof (char *));
31788          for (k = 0; k <= l; k++) {
31789            if (k <= mp->max_write_files) {
31790              wr_file[k] = mp->wr_file[k];
31791              wr_fname[k] = mp->wr_fname[k];
31792            } else {
31793              wr_file[k] = 0;
31794              wr_fname[k] = NULL;
31795            }
31796          }
31797          xfree (mp->wr_file);
31798          xfree (mp->wr_fname);
31799          mp->max_write_files = l;
31800          mp->wr_file = wr_file;
31801          mp->wr_fname = wr_fname;
31802        }
31803      }
31804      n = n0;
31805      mp_open_write_file (mp, fn, n);
31806    } else {
31807      decr (n);
31808      if (mp->wr_fname[n] == NULL)
31809        n0 = n;
31810    }
31811  }
31812}
31813
31814
31815@ @<Record the end of file on |wr_file[n]|@>=
31816{
31817  (mp->close_file) (mp, mp->wr_file[n]);
31818  xfree (mp->wr_fname[n]);
31819  if (n == mp->write_files - 1)
31820    mp->write_files = n;
31821}
31822
31823
31824@* Writing font metric data.
31825\TeX\ gets its knowledge about fonts from font metric files, also called
31826\.{TFM} files; the `\.T' in `\.{TFM}' stands for \TeX,
31827but other programs know about them too. One of \MP's duties is to
31828write \.{TFM} files so that the user's fonts can readily be
31829applied to typesetting.
31830@:TFM files}{\.{TFM} files@>
31831@^font metric files@>
31832
31833The information in a \.{TFM} file appears in a sequence of 8-bit bytes.
31834Since the number of bytes is always a multiple of~4, we could
31835also regard the file as a sequence of 32-bit words, but \MP\ uses the
31836byte interpretation. The format of \.{TFM} files was designed by
31837Lyle Ramshaw in 1980. The intent is to convey a lot of different kinds
31838@^Ramshaw, Lyle Harold@>
31839of information in a compact but useful form.
31840
31841@<Glob...@>=
31842void *tfm_file; /* the font metric output goes here */
31843char *metric_file_name; /* full name of the font metric file */
31844
31845@ The first 24 bytes (6 words) of a \.{TFM} file contain twelve 16-bit
31846integers that give the lengths of the various subsequent portions
31847of the file. These twelve integers are, in order:
31848$$\vbox{\halign{\hfil#&$\null=\null$#\hfil\cr
31849|lf|&length of the entire file, in words;\cr
31850|lh|&length of the header data, in words;\cr
31851|bc|&smallest character code in the font;\cr
31852|ec|&largest character code in the font;\cr
31853|nw|&number of words in the width table;\cr
31854|nh|&number of words in the height table;\cr
31855|nd|&number of words in the depth table;\cr
31856|ni|&number of words in the italic correction table;\cr
31857|nl|&number of words in the lig/kern table;\cr
31858|nk|&number of words in the kern table;\cr
31859|ne|&number of words in the extensible character table;\cr
31860|np|&number of font parameter words.\cr}}$$
31861They are all nonnegative and less than $2^{15}$. We must have |bc-1<=ec<=255|,
31862|ne<=256|, and
31863$$\hbox{|lf=6+lh+(ec-bc+1)+nw+nh+nd+ni+nl+nk+ne+np|.}$$
31864Note that a font may contain as many as 256 characters (if |bc=0| and |ec=255|),
31865and as few as 0 characters (if |bc=ec+1|).
31866
31867Incidentally, when two or more 8-bit bytes are combined to form an integer of
3186816 or more bits, the most significant bytes appear first in the file.
31869This is called BigEndian order.
31870@^BigEndian order@>
31871
31872@ The rest of the \.{TFM} file may be regarded as a sequence of ten data
31873arrays.
31874
31875The most important data type used here is a |fix_word|, which is
31876a 32-bit representation of a binary fraction. A |fix_word| is a signed
31877quantity, with the two's complement of the entire word used to represent
31878negation. Of the 32 bits in a |fix_word|, exactly 12 are to the left of the
31879binary point; thus, the largest |fix_word| value is $2048-2^{-20}$, and
31880the smallest is $-2048$. We will see below, however, that all but two of
31881the |fix_word| values must lie between $-16$ and $+16$.
31882
31883@ The first data array is a block of header information, which contains
31884general facts about the font. The header must contain at least two words,
31885|header[0]| and |header[1]|, whose meaning is explained below.  Additional
31886header information of use to other software routines might also be
31887included, and \MP\ will generate it if the \.{headerbyte} command occurs.
31888For example, 16 more words of header information are in use at the Xerox
31889Palo Alto Research Center; the first ten specify the character coding
31890scheme used (e.g., `\.{XEROX TEXT}' or `\.{TEX MATHSY}'), the next five
31891give the font family name (e.g., `\.{HELVETICA}' or `\.{CMSY}'), and the
31892last gives the ``face byte.''
31893
31894\yskip\hang|header[0]| is a 32-bit check sum that \MP\ will copy into
31895the \.{GF} output file. This helps ensure consistency between files,
31896since \TeX\ records the check sums from the \.{TFM}'s it reads, and these
31897should match the check sums on actual fonts that are used.  The actual
31898relation between this check sum and the rest of the \.{TFM} file is not
31899important; the check sum is simply an identification number with the
31900property that incompatible fonts almost always have distinct check sums.
31901@^check sum@>
31902
31903\yskip\hang|header[1]| is a |fix_word| containing the design size of the
31904font, in units of \TeX\ points. This number must be at least 1.0; it is
31905fairly arbitrary, but usually the design size is 10.0 for a ``10 point''
31906font, i.e., a font that was designed to look best at a 10-point size,
31907whatever that really means. When a \TeX\ user asks for a font `\.{at}
31908$\delta$ \.{pt}', the effect is to override the design size and replace it
31909by $\delta$, and to multiply the $x$ and~$y$ coordinates of the points in
31910the font image by a factor of $\delta$ divided by the design size.  {\sl
31911All other dimensions in the\/ \.{TFM} file are |fix_word|\kern-1pt\
31912numbers in design-size units.} Thus, for example, the value of |param[6]|,
31913which defines the \.{em} unit, is often the |fix_word| value $2^{20}=1.0$,
31914since many fonts have a design size equal to one em.  The other dimensions
31915must be less than 16 design-size units in absolute value; thus,
31916|header[1]| and |param[1]| are the only |fix_word| entries in the whole
31917\.{TFM} file whose first byte might be something besides 0 or 255.
31918@^design size@>
31919
31920@ Next comes the |char_info| array, which contains one |char_info_word|
31921per character. Each word in this part of the file contains six fields
31922packed into four bytes as follows.
31923
31924\yskip\hang first byte: |width_index| (8 bits)\par
31925\hang second byte: |height_index| (4 bits) times 16, plus |depth_index|
31926  (4~bits)\par
31927\hang third byte: |italic_index| (6 bits) times 4, plus |tag|
31928  (2~bits)\par
31929\hang fourth byte: |remainder| (8 bits)\par
31930\yskip\noindent
31931The actual width of a character is \\{width}|[width_index]|, in design-size
31932units; this is a device for compressing information, since many characters
31933have the same width. Since it is quite common for many characters
31934to have the same height, depth, or italic correction, the \.{TFM} format
31935imposes a limit of 16 different heights, 16 different depths, and
3193664 different italic corrections.
31937
31938Incidentally, the relation $\\{width}[0]=\\{height}[0]=\\{depth}[0]=
31939\\{italic}[0]=0$ should always hold, so that an index of zero implies a
31940value of zero.  The |width_index| should never be zero unless the
31941character does not exist in the font, since a character is valid if and
31942only if it lies between |bc| and |ec| and has a nonzero |width_index|.
31943
31944@ The |tag| field in a |char_info_word| has four values that explain how to
31945interpret the |remainder| field.
31946
31947\yskip\hang|tag=0| (|no_tag|) means that |remainder| is unused.\par
31948\hang|tag=1| (|lig_tag|) means that this character has a ligature/kerning
31949program starting at location |remainder| in the |lig_kern| array.\par
31950\hang|tag=2| (|list_tag|) means that this character is part of a chain of
31951characters of ascending sizes, and not the largest in the chain.  The
31952|remainder| field gives the character code of the next larger character.\par
31953\hang|tag=3| (|ext_tag|) means that this character code represents an
31954extensible character, i.e., a character that is built up of smaller pieces
31955so that it can be made arbitrarily large. The pieces are specified in
31956|exten[remainder]|.\par
31957\yskip\noindent
31958Characters with |tag=2| and |tag=3| are treated as characters with |tag=0|
31959unless they are used in special circumstances in math formulas. For example,
31960\TeX's \.{\\sum} operation looks for a |list_tag|, and the \.{\\left}
31961operation looks for both |list_tag| and |ext_tag|.
31962
31963@d no_tag 0 /* vanilla character */
31964@d lig_tag 1 /* character has a ligature/kerning program */
31965@d list_tag 2 /* character has a successor in a charlist */
31966@d ext_tag 3 /* character is extensible */
31967
31968@ The |lig_kern| array contains instructions in a simple programming language
31969that explains what to do for special letter pairs. Each word in this array is a
31970|lig_kern_command| of four bytes.
31971
31972\yskip\hang first byte: |skip_byte|, indicates that this is the final program
31973  step if the byte is 128 or more, otherwise the next step is obtained by
31974  skipping this number of intervening steps.\par
31975\hang second byte: |next_char|, ``if |next_char| follows the current character,
31976  then perform the operation and stop, otherwise continue.''\par
31977\hang third byte: |op_byte|, indicates a ligature step if less than~128,
31978  a kern step otherwise.\par
31979\hang fourth byte: |remainder|.\par
31980\yskip\noindent
31981In a kern step, an
31982additional space equal to |kern[256*(op_byte-128)+remainder]| is inserted
31983between the current character and |next_char|. This amount is
31984often negative, so that the characters are brought closer together
31985by kerning; but it might be positive.
31986
31987There are eight kinds of ligature steps, having |op_byte| codes $4a+2b+c$ where
31988$0\le a\le b+c$ and $0\le b,c\le1$. The character whose code is
31989|remainder| is inserted between the current character and |next_char|;
31990then the current character is deleted if $b=0$, and |next_char| is
31991deleted if $c=0$; then we pass over $a$~characters to reach the next
31992current character (which may have a ligature/kerning program of its own).
31993
31994If the very first instruction of the |lig_kern| array has |skip_byte=255|,
31995the |next_char| byte is the so-called right boundary character of this font;
31996the value of |next_char| need not lie between |bc| and~|ec|.
31997If the very last instruction of the |lig_kern| array has |skip_byte=255|,
31998there is a special ligature/kerning program for a left boundary character,
31999beginning at location |256*op_byte+remainder|.
32000The interpretation is that \TeX\ puts implicit boundary characters
32001before and after each consecutive string of characters from the same font.
32002These implicit characters do not appear in the output, but they can affect
32003ligatures and kerning.
32004
32005If the very first instruction of a character's |lig_kern| program has
32006|skip_byte>128|, the program actually begins in location
32007|256*op_byte+remainder|. This feature allows access to large |lig_kern|
32008arrays, because the first instruction must otherwise
32009appear in a location |<=255|.
32010
32011Any instruction with |skip_byte>128| in the |lig_kern| array must satisfy
32012the condition
32013$$\hbox{|256*op_byte+remainder<nl|.}$$
32014If such an instruction is encountered during
32015normal program execution, it denotes an unconditional halt; no ligature
32016command is performed.
32017
32018@d stop_flag (128)
32019  /* value indicating `\.{STOP}' in a lig/kern program */
32020@d kern_flag (128) /* op code for a kern step */
32021@d skip_byte(A) mp->lig_kern[(A)].b0
32022@d next_char(A) mp->lig_kern[(A)].b1
32023@d op_byte(A) mp->lig_kern[(A)].b2
32024@d rem_byte(A) mp->lig_kern[(A)].b3
32025
32026@ Extensible characters are specified by an |extensible_recipe|, which
32027consists of four bytes called |top|, |mid|, |bot|, and |rep| (in this
32028order). These bytes are the character codes of individual pieces used to
32029build up a large symbol.  If |top|, |mid|, or |bot| are zero, they are not
32030present in the built-up result. For example, an extensible vertical line is
32031like an extensible bracket, except that the top and bottom pieces are missing.
32032
32033Let $T$, $M$, $B$, and $R$ denote the respective pieces, or an empty box
32034if the piece isn't present. Then the extensible characters have the form
32035$TR^kMR^kB$ from top to bottom, for some |k>=0|, unless $M$ is absent;
32036in the latter case we can have $TR^kB$ for both even and odd values of~|k|.
32037The width of the extensible character is the width of $R$; and the
32038height-plus-depth is the sum of the individual height-plus-depths of the
32039components used, since the pieces are butted together in a vertical list.
32040
32041@d ext_top(A) mp->exten[(A)].b0 /* |top| piece in a recipe */
32042@d ext_mid(A) mp->exten[(A)].b1 /* |mid| piece in a recipe */
32043@d ext_bot(A) mp->exten[(A)].b2 /* |bot| piece in a recipe */
32044@d ext_rep(A) mp->exten[(A)].b3 /* |rep| piece in a recipe */
32045
32046@ The final portion of a \.{TFM} file is the |param| array, which is another
32047sequence of |fix_word| values.
32048
32049\yskip\hang|param[1]=slant| is the amount of italic slant, which is used
32050to help position accents. For example, |slant=.25| means that when you go
32051up one unit, you also go .25 units to the right. The |slant| is a pure
32052number; it is the only |fix_word| other than the design size itself that is
32053not scaled by the design size.
32054@^design size@>
32055
32056\hang|param[2]=space| is the normal spacing between words in text.
32057Note that character 040 in the font need not have anything to do with
32058blank spaces.
32059
32060\hang|param[3]=space_stretch| is the amount of glue stretching between words.
32061
32062\hang|param[4]=space_shrink| is the amount of glue shrinking between words.
32063
32064\hang|param[5]=x_height| is the size of one ex in the font; it is also
32065the height of letters for which accents don't have to be raised or lowered.
32066
32067\hang|param[6]=quad| is the size of one em in the font.
32068
32069\hang|param[7]=extra_space| is the amount added to |param[2]| at the
32070ends of sentences.
32071
32072\yskip\noindent
32073If fewer than seven parameters are present, \TeX\ sets the missing parameters
32074to zero.
32075
32076@d slant_code 1
32077@d space_code 2
32078@d space_stretch_code 3
32079@d space_shrink_code 4
32080@d x_height_code 5
32081@d quad_code 6
32082@d extra_space_code 7
32083
32084@ So that is what \.{TFM} files hold. One of \MP's duties is to output such
32085information, and it does this all at once at the end of a job.
32086In order to prepare for such frenetic activity, it squirrels away the
32087necessary facts in various arrays as information becomes available.
32088
32089Character dimensions (\&{charwd}, \&{charht}, \&{chardp}, and \&{charic})
32090are stored respectively in |tfm_width|, |tfm_height|, |tfm_depth|, and
32091|tfm_ital_corr|. Other information about a character (e.g., about
32092its ligatures or successors) is accessible via the |char_tag| and
32093|char_remainder| arrays. Other information about the font as a whole
32094is kept in additional arrays called |header_byte|, |lig_kern|,
32095|kern|, |exten|, and |param|.
32096
32097@d max_tfm_int 32510
32098@d undefined_label max_tfm_int /* an undefined local label */
32099
32100@<Glob...@>=
32101#define TFM_ITEMS 257
32102eight_bits bc;
32103eight_bits ec;  /* smallest and largest character codes shipped out */
32104mp_node tfm_width[TFM_ITEMS];   /* \&{charwd} values */
32105mp_node tfm_height[TFM_ITEMS];  /* \&{charht} values */
32106mp_node tfm_depth[TFM_ITEMS];   /* \&{chardp} values */
32107mp_node tfm_ital_corr[TFM_ITEMS];       /* \&{charic} values */
32108boolean char_exists[TFM_ITEMS]; /* has this code been shipped out? */
32109int char_tag[TFM_ITEMS];        /* |remainder| category */
32110int char_remainder[TFM_ITEMS];  /* the |remainder| byte */
32111char *header_byte;      /* bytes of the \.{TFM} header */
32112int header_last;        /* last initialized \.{TFM} header byte */
32113int header_size;        /* size of the \.{TFM} header */
32114four_quarters *lig_kern;        /* the ligature/kern table */
32115short nl;       /* the number of ligature/kern steps so far */
32116mp_number *kern;   /* distinct kerning amounts */
32117short nk;       /* the number of distinct kerns so far */
32118four_quarters exten[TFM_ITEMS]; /* extensible character recipes */
32119short ne;       /* the number of extensible characters so far */
32120mp_number *param;  /* \&{fontinfo} parameters */
32121short np;       /* the largest \&{fontinfo} parameter specified so far */
32122short nw;
32123short nh;
32124short nd;
32125short ni;       /* sizes of \.{TFM} subtables */
32126short skip_table[TFM_ITEMS];    /* local label status */
32127boolean lk_started;     /* has there been a lig/kern step in this command yet? */
32128integer bchar;  /* right boundary character */
32129short bch_label;        /* left boundary starting location */
32130short ll;
32131short lll;      /* registers used for lig/kern processing */
32132short label_loc[257];   /* lig/kern starting addresses */
32133eight_bits label_char[257];     /* characters for |label_loc| */
32134short label_ptr;        /* highest position occupied in |label_loc| */
32135
32136@ @<Allocate or initialize ...@>=
32137mp->header_last = 7;
32138mp->header_size = 128;          /* just for init */
32139mp->header_byte = xmalloc (mp->header_size, sizeof (char));
32140
32141@ @<Dealloc variables@>=
32142xfree (mp->header_byte);
32143xfree (mp->lig_kern);
32144if (mp->kern) {
32145  int i;
32146  for (i=0;i<(max_tfm_int + 1);i++) {
32147    free_number(mp->kern[i]);
32148  }
32149  xfree (mp->kern);
32150}
32151if (mp->param) {
32152  int i;
32153  for (i=0;i<(max_tfm_int + 1);i++) {
32154    free_number(mp->param[i]);
32155  }
32156  xfree (mp->param);
32157}
32158
32159@ @<Set init...@>=
32160for (k = 0; k <= 255; k++) {
32161  mp->tfm_width[k] = 0;
32162  mp->tfm_height[k] = 0;
32163  mp->tfm_depth[k] = 0;
32164  mp->tfm_ital_corr[k] = 0;
32165  mp->char_exists[k] = false;
32166  mp->char_tag[k] = no_tag;
32167  mp->char_remainder[k] = 0;
32168  mp->skip_table[k] = undefined_label;
32169}
32170memset (mp->header_byte, 0, (size_t) mp->header_size);
32171mp->bc = 255;
32172mp->ec = 0;
32173mp->nl = 0;
32174mp->nk = 0;
32175mp->ne = 0;
32176mp->np = 0;
32177set_internal_from_number (mp_boundary_char, unity_t);
32178number_negate (internal_value (mp_boundary_char));
32179mp->bch_label = undefined_label;
32180mp->label_loc[0] = -1;
32181mp->label_ptr = 0;
32182
32183@ @<Declarations@>=
32184static mp_node mp_tfm_check (MP mp, quarterword m);
32185
32186@ @c
32187static mp_node mp_tfm_check (MP mp, quarterword m) {
32188  mp_number absm;
32189  mp_node p = mp_get_value_node (mp);
32190  new_number (absm);
32191  number_clone (absm, internal_value (m));
32192  number_abs (absm);
32193  if (number_greaterequal (absm, fraction_half_t)) {
32194    char msg[256];
32195    const char *hlp[] = {
32196       "Font metric dimensions must be less than 2048pt.",
32197       NULL } ;
32198    mp_snprintf (msg, 256, "Enormous %s has been reduced", internal_name (m));
32199@.Enormous charwd...@>
32200@.Enormous chardp...@>
32201@.Enormous charht...@>
32202@.Enormous charic...@>
32203@.Enormous designsize...@>;
32204    mp_back_error (mp, msg, hlp, true);
32205    mp_get_x_next (mp);
32206    if (number_positive (internal_value (m))) {
32207      set_value_number (p, fraction_half_t);
32208      number_add_scaled (value_number (p), -1);
32209    } else {
32210      set_value_number (p, fraction_half_t);
32211      number_negate (value_number (p));
32212      number_add_scaled (value_number (p), 1);
32213    }
32214  } else {
32215    set_value_number (p, internal_value (m));
32216  }
32217  free_number (absm);
32218  return p;
32219}
32220
32221@ @<Store the width information for character code~|c|@>=
32222if (c < mp->bc)
32223  mp->bc = (eight_bits) c;
32224if (c > mp->ec)
32225  mp->ec = (eight_bits) c;
32226mp->char_exists[c] = true;
32227mp_free_value_node (mp, mp->tfm_width[c]);
32228mp->tfm_width[c] = mp_tfm_check (mp, mp_char_wd);
32229mp_free_value_node (mp, mp->tfm_height[c]);
32230mp->tfm_height[c] = mp_tfm_check (mp, mp_char_ht);
32231mp_free_value_node (mp, mp->tfm_depth[c]);
32232mp->tfm_depth[c] = mp_tfm_check (mp, mp_char_dp);
32233mp_free_value_node (mp, mp->tfm_ital_corr[c]);
32234mp->tfm_ital_corr[c] = mp_tfm_check (mp, mp_char_ic)
32235
32236
32237@ Now let's consider \MP's special \.{TFM}-oriented commands.
32238
32239
32240@ @d char_list_code 0
32241@d lig_table_code 1
32242@d extensible_code 2
32243@d header_byte_code 3
32244@d font_dimen_code 4
32245
32246@<Put each...@>=
32247mp_primitive (mp, "charlist", mp_tfm_command, char_list_code);
32248@:char_list_}{\&{charlist} primitive@>;
32249mp_primitive (mp, "ligtable", mp_tfm_command, lig_table_code);
32250@:lig_table_}{\&{ligtable} primitive@>;
32251mp_primitive (mp, "extensible", mp_tfm_command, extensible_code);
32252@:extensible_}{\&{extensible} primitive@>;
32253mp_primitive (mp, "headerbyte", mp_tfm_command, header_byte_code);
32254@:header_byte_}{\&{headerbyte} primitive@>;
32255mp_primitive (mp, "fontdimen", mp_tfm_command, font_dimen_code);
32256@:font_dimen_}{\&{fontdimen} primitive@>
32257
32258
32259@ @<Cases of |print_cmd...@>=
32260case mp_tfm_command:
32261switch (m) {
32262case char_list_code:
32263  mp_print (mp, "charlist");
32264  break;
32265case lig_table_code:
32266  mp_print (mp, "ligtable");
32267  break;
32268case extensible_code:
32269  mp_print (mp, "extensible");
32270  break;
32271case header_byte_code:
32272  mp_print (mp, "headerbyte");
32273  break;
32274default:
32275  mp_print (mp, "fontdimen");
32276  break;
32277}
32278break;
32279
32280@ @<Declare action procedures for use by |do_statement|@>=
32281static eight_bits mp_get_code (MP mp);
32282
32283@ @c
32284eight_bits mp_get_code (MP mp) {                               /* scans a character code value */
32285  integer c;    /* the code value found */
32286  mp_value new_expr;
32287  const char *hlp[] = {
32288         "I was looking for a number between 0 and 255, or for a",
32289         "string of length 1. Didn't find it; will use 0 instead.",
32290          NULL };
32291  memset(&new_expr,0,sizeof(mp_value));
32292  new_number(new_expr.data.n);
32293  mp_get_x_next (mp);
32294  mp_scan_expression (mp);
32295  if (mp->cur_exp.type == mp_known) {
32296    c = round_unscaled (cur_exp_value_number ());
32297    if (c >= 0)
32298      if (c < 256)
32299        return (eight_bits) c;
32300  } else if (mp->cur_exp.type == mp_string_type) {
32301    if (cur_exp_str ()->len == 1) {
32302      c = (integer) (*(cur_exp_str ()->str));
32303      return (eight_bits) c;
32304    }
32305  }
32306  mp_disp_err(mp, NULL);
32307  set_number_to_zero (new_expr.data.n);
32308  mp_back_error (mp, "Invalid code has been replaced by 0", hlp, true);
32309@.Invalid code...@>;
32310  mp_get_x_next (mp);
32311  mp_flush_cur_exp (mp, new_expr);
32312  c = 0;
32313  return (eight_bits) c;
32314}
32315
32316
32317@ @<Declare action procedures for use by |do_statement|@>=
32318static void mp_set_tag (MP mp, halfword c, quarterword t, halfword r);
32319
32320@ @c
32321void mp_set_tag (MP mp, halfword c, quarterword t, halfword r) {
32322  if (mp->char_tag[c] == no_tag) {
32323    mp->char_tag[c] = t;
32324    mp->char_remainder[c] = r;
32325    if (t == lig_tag) {
32326      mp->label_ptr++;
32327      mp->label_loc[mp->label_ptr] = (short) r;
32328      mp->label_char[mp->label_ptr] = (eight_bits) c;
32329    }
32330  } else {
32331    @<Complain about a character tag conflict@>;
32332  }
32333}
32334
32335
32336@ @<Complain about a character tag conflict@>=
32337{
32338  const char *xtra = NULL;
32339  char msg[256];
32340  const char *hlp[] = {
32341         "It's not legal to label a character more than once.",
32342         "So I'll not change anything just now.",
32343         NULL };
32344  switch (mp->char_tag[c]) {
32345  case lig_tag:  xtra = "in a ligtable";    break;
32346  case list_tag: xtra = "in a charlist";    break;
32347  case ext_tag:  xtra = "extensible";       break;
32348  default:       xtra = "";                 break;
32349  }
32350  if ((c > ' ') && (c < 127)) {
32351    mp_snprintf(msg, 256, "Character %c is already %s", xord(c), xtra);
32352  } else if (c == 256) {
32353    mp_snprintf(msg, 256, "Character || is already %s", xtra);
32354  } else {
32355    mp_snprintf(msg, 256, "Character code %d is already %s", c, xtra);
32356  }
32357@.Character c is already...@>;
32358  mp_back_error (mp, msg, hlp, true);
32359  mp_get_x_next (mp);
32360}
32361
32362
32363@ @<Declare action procedures for use by |do_statement|@>=
32364static void mp_do_tfm_command (MP mp);
32365
32366@ @c
32367void mp_do_tfm_command (MP mp) {
32368  int c, cc;    /* character codes */
32369  int k;        /* index into the |kern| array */
32370  int j;        /* index into |header_byte| or |param| */
32371  mp_value new_expr;
32372  memset(&new_expr,0,sizeof(mp_value));
32373  new_number(new_expr.data.n);
32374  switch (cur_mod()) {
32375  case char_list_code:
32376    c = mp_get_code (mp);
32377    /* we will store a list of character successors */
32378    while (cur_cmd() == mp_colon) {
32379      cc = mp_get_code (mp);
32380      mp_set_tag (mp, c, list_tag, cc);
32381      c = cc;
32382    };
32383    break;
32384  case lig_table_code:
32385    if (mp->lig_kern == NULL)
32386      mp->lig_kern = xmalloc ((max_tfm_int + 1), sizeof (four_quarters));
32387    if (mp->kern == NULL) {
32388      int i;
32389      mp->kern = xmalloc ((max_tfm_int + 1), sizeof (mp_number));
32390      for (i=0;i<(max_tfm_int + 1);i++)
32391         new_number (mp->kern[i]);
32392    }
32393    @<Store a list of ligature/kern steps@>;
32394    break;
32395  case extensible_code:
32396    @<Define an extensible recipe@>;
32397    break;
32398  case header_byte_code:
32399  case font_dimen_code:
32400    c = cur_mod();
32401    mp_get_x_next (mp);
32402    mp_scan_expression (mp);
32403    if ((mp->cur_exp.type != mp_known) || number_less(cur_exp_value_number (), half_unit_t)) {
32404      const char *hlp[] = {
32405             "I was looking for a known, positive number.",
32406             "For safety's sake I'll ignore the present command.",
32407             NULL };
32408      mp_disp_err(mp, NULL);
32409      mp_back_error (mp, "Improper location", hlp, true);
32410@.Improper location@>;
32411      mp_get_x_next (mp);
32412    } else {
32413      j = round_unscaled (cur_exp_value_number ());
32414      if (cur_cmd() != mp_colon) {
32415        const char *hlp[] = {
32416          "A colon should follow a headerbyte or fontinfo location.",
32417           NULL };
32418        mp_back_error (mp, "Missing `:' has been inserted", hlp, true);
32419@.Missing `:'@>;
32420      }
32421      if (c == header_byte_code) {
32422        @<Store a list of header bytes@>;
32423      } else {
32424        if (mp->param == NULL) {
32425          int i;
32426          mp->param = xmalloc ((max_tfm_int + 1), sizeof (mp_number));
32427          for (i=0;i<(max_tfm_int + 1);i++)
32428             new_number (mp->param[i]);
32429        }
32430        @<Store a list of font dimensions@>;
32431      }
32432    }
32433    break;
32434  }                             /* there are no other cases */
32435}
32436
32437
32438@ @<Store a list of ligature/kern steps@>=
32439{
32440  mp->lk_started = false;
32441CONTINUE:
32442  mp_get_x_next (mp);
32443  if ((cur_cmd() == mp_skip_to) && mp->lk_started)
32444    @<Process a |skip_to| command and |goto done|@>;
32445  if (cur_cmd() == mp_bchar_label) {
32446    c = 256;
32447    set_cur_cmd((mp_variable_type)mp_colon);
32448  } else {
32449    mp_back_input (mp);
32450    c = mp_get_code (mp);
32451  };
32452  if ((cur_cmd() == mp_colon) || (cur_cmd() == mp_double_colon)) {
32453    @<Record a label in a lig/kern subprogram and |goto continue|@>;
32454  }
32455  if (cur_cmd() == mp_lig_kern_token) {
32456    @<Compile a ligature/kern command@>;
32457  } else {
32458    const char *hlp[] = { "I was looking for `=:' or `kern' here.", NULL };
32459    mp_back_error (mp, "Illegal ligtable step", hlp, true);
32460@.Illegal ligtable step@>;
32461    next_char (mp->nl) = qi (0);
32462    op_byte (mp->nl) = qi (0);
32463    rem_byte (mp->nl) = qi (0);
32464    skip_byte (mp->nl) = stop_flag + 1; /* this specifies an unconditional stop */
32465  }
32466  if (mp->nl == max_tfm_int)
32467    mp_fatal_error (mp, "ligtable too large");
32468  mp->nl++;
32469  if (cur_cmd() == mp_comma)
32470    goto CONTINUE;
32471  if (skip_byte (mp->nl - 1) < stop_flag)
32472    skip_byte (mp->nl - 1) = stop_flag;
32473}
32474DONE:
32475
32476@ @<Put each...@>=
32477mp_primitive (mp, "=:", mp_lig_kern_token, 0);
32478@:=:_}{\.{=:} primitive@>;
32479mp_primitive (mp, "=:|", mp_lig_kern_token, 1);
32480@:=:/_}{\.{=:\char'174} primitive@>;
32481mp_primitive (mp, "=:|>", mp_lig_kern_token, 5);
32482@:=:/>_}{\.{=:\char'174>} primitive@>;
32483mp_primitive (mp, "|=:", mp_lig_kern_token, 2);
32484@:=:/_}{\.{\char'174=:} primitive@>;
32485mp_primitive (mp, "|=:>", mp_lig_kern_token, 6);
32486@:=:/>_}{\.{\char'174=:>} primitive@>;
32487mp_primitive (mp, "|=:|", mp_lig_kern_token, 3);
32488@:=:/_}{\.{\char'174=:\char'174} primitive@>;
32489mp_primitive (mp, "|=:|>", mp_lig_kern_token, 7);
32490@:=:/>_}{\.{\char'174=:\char'174>} primitive@>;
32491mp_primitive (mp, "|=:|>>", mp_lig_kern_token, 11);
32492@:=:/>_}{\.{\char'174=:\char'174>>} primitive@>;
32493mp_primitive (mp, "kern", mp_lig_kern_token, mp_kern_flag);
32494@:kern_}{\&{kern} primitive@>
32495
32496
32497@ @<Cases of |print_cmd...@>=
32498case mp_lig_kern_token:
32499switch (m) {
32500case 0:
32501  mp_print (mp, "=:");
32502  break;
32503case 1:
32504  mp_print (mp, "=:|");
32505  break;
32506case 2:
32507  mp_print (mp, "|=:");
32508  break;
32509case 3:
32510  mp_print (mp, "|=:|");
32511  break;
32512case 5:
32513  mp_print (mp, "=:|>");
32514  break;
32515case 6:
32516  mp_print (mp, "|=:>");
32517  break;
32518case 7:
32519  mp_print (mp, "|=:|>");
32520  break;
32521case 11:
32522  mp_print (mp, "|=:|>>");
32523  break;
32524default:
32525  mp_print (mp, "kern");
32526  break;
32527}
32528break;
32529
32530@ Local labels are implemented by maintaining the |skip_table| array,
32531where |skip_table[c]| is either |undefined_label| or the address of the
32532most recent lig/kern instruction that skips to local label~|c|. In the
32533latter case, the |skip_byte| in that instruction will (temporarily)
32534be zero if there were no prior skips to this label, or it will be the
32535distance to the prior skip.
32536
32537We may need to cancel skips that span more than 127 lig/kern steps.
32538
32539@d cancel_skips(A) mp->ll=(A);
32540  do {
32541    mp->lll=qo(skip_byte(mp->ll));
32542    skip_byte(mp->ll)=stop_flag; mp->ll=(short)(mp->ll-mp->lll);
32543  } while (mp->lll!=0)
32544
32545@d skip_error(A) {
32546  const char *hlp[] = { "At most 127 lig/kern steps can separate skipto1 from 1::.", NULL};
32547  mp_error(mp, "Too far to skip", hlp, true);
32548@.Too far to skip@>
32549  cancel_skips((A));
32550}
32551
32552@<Process a |skip_to| command and |goto done|@>=
32553{
32554  c = mp_get_code (mp);
32555  if (mp->nl - mp->skip_table[c] > 128) {
32556    skip_error (mp->skip_table[c]);
32557    mp->skip_table[c] = (short) undefined_label;
32558  }
32559  if (mp->skip_table[c] == undefined_label)
32560    skip_byte (mp->nl - 1) = qi (0);
32561  else
32562    skip_byte (mp->nl - 1) = qi (mp->nl - mp->skip_table[c] - 1);
32563  mp->skip_table[c] = (short) (mp->nl - 1);
32564  goto DONE;
32565}
32566
32567
32568@ @<Record a label in a lig/kern subprogram and |goto continue|@>=
32569{
32570  if (cur_cmd() == mp_colon) {
32571    if (c == 256)
32572      mp->bch_label = mp->nl;
32573    else
32574      mp_set_tag (mp, c, lig_tag, mp->nl);
32575  } else if (mp->skip_table[c] < undefined_label) {
32576    mp->ll = mp->skip_table[c];
32577    mp->skip_table[c] = undefined_label;
32578    do {
32579      mp->lll = qo (skip_byte (mp->ll));
32580      if (mp->nl - mp->ll > 128) {
32581        skip_error (mp->ll);
32582        goto CONTINUE;
32583      }
32584      skip_byte (mp->ll) = qi (mp->nl - mp->ll - 1);
32585      mp->ll = (short) (mp->ll - mp->lll);
32586    } while (mp->lll != 0);
32587  }
32588  goto CONTINUE;
32589}
32590
32591
32592@ @<Compile a ligature/kern...@>=
32593{
32594  next_char (mp->nl) = qi (c);
32595  skip_byte (mp->nl) = qi (0);
32596  if (cur_mod() < 128) {      /* ligature op */
32597    op_byte (mp->nl) = qi (cur_mod());
32598    rem_byte (mp->nl) = qi (mp_get_code (mp));
32599  } else {
32600    mp_get_x_next (mp);
32601    mp_scan_expression (mp);
32602    if (mp->cur_exp.type != mp_known) {
32603      const char *hlp[] =  {
32604             "The amount of kern should be a known numeric value.",
32605             "I'm zeroing this one. Proceed, with fingers crossed.",
32606             NULL };
32607      mp_disp_err(mp, NULL);
32608      set_number_to_zero (new_expr.data.n);
32609      mp_back_error (mp, "Improper kern", hlp, true);
32610@.Improper kern@>;
32611      mp_get_x_next (mp);
32612      mp_flush_cur_exp (mp, new_expr);
32613    }
32614    number_clone (mp->kern[mp->nk], cur_exp_value_number ());
32615    k = 0;
32616    while (!number_equal (mp->kern[k], cur_exp_value_number ()))
32617      incr (k);
32618    if (k == mp->nk) {
32619      if (mp->nk == max_tfm_int)
32620        mp_fatal_error (mp, "too many TFM kerns");
32621      mp->nk++;
32622    }
32623    op_byte (mp->nl) = qi (kern_flag + (k / 256));
32624    rem_byte (mp->nl) = qi ((k % 256));
32625  }
32626  mp->lk_started = true;
32627}
32628
32629
32630@ @d missing_extensible_punctuation(A)
32631  {
32632    char msg[256];
32633    const char *hlp[] = { "I'm processing `extensible c: t,m,b,r'.", NULL };
32634    mp_snprintf(msg, 256, "Missing %s has been inserted", (A));
32635    mp_back_error(mp, msg, hlp, true);
32636@.Missing `\char`\#'@>
32637  }
32638
32639@<Define an extensible recipe@>=
32640{
32641  if (mp->ne == 256)
32642    mp_fatal_error (mp, "too many extensible recipies");
32643  c = mp_get_code (mp);
32644  mp_set_tag (mp, c, ext_tag, mp->ne);
32645  if (cur_cmd() != mp_colon)
32646    missing_extensible_punctuation (":");
32647  ext_top (mp->ne) = qi (mp_get_code (mp));
32648  if (cur_cmd() != mp_comma)
32649    missing_extensible_punctuation (",");
32650  ext_mid (mp->ne) = qi (mp_get_code (mp));
32651  if (cur_cmd() != mp_comma)
32652    missing_extensible_punctuation (",");
32653  ext_bot (mp->ne) = qi (mp_get_code (mp));
32654  if (cur_cmd() != mp_comma)
32655    missing_extensible_punctuation (",");
32656  ext_rep (mp->ne) = qi (mp_get_code (mp));
32657  mp->ne++;
32658}
32659
32660
32661@ The header could contain ASCII zeroes, so can't use |strdup|.
32662
32663@<Store a list of header bytes@>=
32664j--;
32665do {
32666  if (j >= mp->header_size) {
32667    size_t l = (size_t) (mp->header_size + (mp->header_size / 4));
32668    char *t = xmalloc (l, 1);
32669    memset (t, 0, l);
32670    (void) memcpy (t, mp->header_byte, (size_t) mp->header_size);
32671    xfree (mp->header_byte);
32672    mp->header_byte = t;
32673    mp->header_size = (int) l;
32674  }
32675  mp->header_byte[j] = (char) mp_get_code (mp);
32676  incr (j);
32677  incr (mp->header_last);
32678} while (cur_cmd() == mp_comma)
32679
32680@ @<Store a list of font dimensions@>=
32681do {
32682  if (j > max_tfm_int)
32683    mp_fatal_error (mp, "too many fontdimens");
32684  while (j > mp->np) {
32685    mp->np++;
32686    set_number_to_zero(mp->param[mp->np]);
32687  };
32688  mp_get_x_next (mp);
32689  mp_scan_expression (mp);
32690  if (mp->cur_exp.type != mp_known) {
32691    const char *hlp[] = { "I'm zeroing this one. Proceed, with fingers crossed.", NULL };
32692    mp_disp_err(mp, NULL);
32693    set_number_to_zero (new_expr.data.n);
32694    mp_back_error (mp, "Improper font parameter", hlp, true);
32695@.Improper font parameter@>;
32696    mp_get_x_next (mp);
32697    mp_flush_cur_exp (mp, new_expr);
32698  }
32699  number_clone (mp->param[j], cur_exp_value_number ());
32700  incr (j);
32701} while (cur_cmd() == mp_comma)
32702
32703@ OK: We've stored all the data that is needed for the \.{TFM} file.
32704All that remains is to output it in the correct format.
32705
32706An interesting problem needs to be solved in this connection, because
32707the \.{TFM} format allows at most 256~widths, 16~heights, 16~depths,
32708and 64~italic corrections. If the data has more distinct values than
32709this, we want to meet the necessary restrictions by perturbing the
32710given values as little as possible.
32711
32712\MP\ solves this problem in two steps. First the values of a given
32713kind (widths, heights, depths, or italic corrections) are sorted;
32714then the list of sorted values is perturbed, if necessary.
32715
32716The sorting operation is facilitated by having a special node of
32717essentially infinite |value| at the end of the current list.
32718
32719@<Initialize table entries@>=
32720mp->inf_val = mp_get_value_node (mp);
32721set_value_number (mp->inf_val, fraction_four_t);
32722
32723@ @<Free table entries@>=
32724mp_free_value_node (mp, mp->inf_val);
32725
32726@ Straight linear insertion is good enough for sorting, since the lists
32727are usually not terribly long. As we work on the data, the current list
32728will start at |mp_link(temp_head)| and end at |inf_val|; the nodes in this
32729list will be in increasing order of their |value| fields.
32730
32731Given such a list, the |sort_in| function takes a value and returns a pointer
32732to where that value can be found in the list. The value is inserted in
32733the proper place, if necessary.
32734
32735At the time we need to do these operations, most of \MP's work has been
32736completed, so we will have plenty of memory to play with. The value nodes
32737that are allocated for sorting will never be returned to free storage.
32738
32739@d clear_the_list mp_link(mp->temp_head)=mp->inf_val
32740
32741@c
32742static mp_node mp_sort_in (MP mp, mp_number v) {
32743  mp_node p, q, r;      /* list manipulation registers */
32744  p = mp->temp_head;
32745  while (1) {
32746    q = mp_link (p);
32747    if (number_lessequal(v, value_number (q)))
32748      break;
32749    p = q;
32750  }
32751  if (number_less (v, value_number (q))) {
32752    r = mp_get_value_node (mp);
32753    set_value_number (r, v);
32754    mp_link (r) = q;
32755    mp_link (p) = r;
32756  }
32757  return mp_link (p);
32758}
32759
32760
32761@ Now we come to the interesting part, where we reduce the list if necessary
32762until it has the required size. The |min_cover| routine is basic to this
32763process; it computes the minimum number~|m| such that the values of the
32764current sorted list can be covered by |m|~intervals of width~|d|. It
32765also sets the global value |perturbation| to the smallest value $d'>d$
32766such that the covering found by this algorithm would be different.
32767
32768In particular, |min_cover(0)| returns the number of distinct values in the
32769current list and sets |perturbation| to the minimum distance between
32770adjacent values.
32771
32772@c
32773static integer mp_min_cover (MP mp, mp_number d) {
32774  mp_node p;    /* runs through the current list */
32775  mp_number l;     /* the least element covered by the current interval */
32776  mp_number test;
32777  integer m;    /* lower bound on the size of the minimum cover */
32778  m = 0;
32779  new_number  (l);
32780  new_number  (test);
32781  p = mp_link (mp->temp_head);
32782  set_number_to_inf(mp->perturbation);
32783  while (p != mp->inf_val) {
32784    incr (m);
32785    number_clone (l, value_number (p));
32786    do {
32787      p = mp_link (p);
32788      set_number_from_addition(test, l, d);
32789    } while (number_lessequal(value_number (p), test));
32790
32791    set_number_from_substraction(test, value_number (p), l);
32792    if (number_less (test, mp->perturbation)) {
32793      number_clone (mp->perturbation, value_number (p));
32794      number_substract (mp->perturbation, l);
32795    }
32796  }
32797  free_number  (test);
32798  free_number  (l);
32799  return m;
32800}
32801
32802
32803@ @<Glob...@>=
32804mp_number perturbation;    /* quantity related to \.{TFM} rounding */
32805integer excess; /* the list is this much too long */
32806
32807@ @<Initialize table...@>=
32808new_number (mp->perturbation);
32809
32810@ @<Dealloc...@>=
32811free_number (mp->perturbation);
32812
32813@ The smallest |d| such that a given list can be covered with |m| intervals
32814is determined by the |threshold| routine, which is sort of an inverse
32815to |min_cover|. The idea is to increase the interval size rapidly until
32816finding the range, then to go sequentially until the exact borderline has
32817been discovered.
32818
32819@c
32820static void mp_threshold (MP mp, mp_number ret, integer m) {
32821  mp_number d, arg1;     /* lower bound on the smallest interval size */
32822  new_number (d);
32823  new_number (arg1);
32824  mp->excess = mp_min_cover (mp, zero_t) - m;
32825  if (mp->excess <= 0) {
32826    number_clone (ret, zero_t);
32827  } else {
32828    do {
32829      number_clone (d, mp->perturbation);
32830      set_number_from_addition(arg1, d, d);
32831    } while (mp_min_cover (mp, arg1) > m);
32832    while (mp_min_cover (mp, d) > m) {
32833      number_clone (d, mp->perturbation);
32834    }
32835    number_clone (ret, d);
32836  }
32837  free_number (d);
32838  free_number (arg1);
32839}
32840
32841
32842@ The |skimp| procedure reduces the current list to at most |m| entries,
32843by changing values if necessary. It also sets |indep_value(p):=k| if |value(p)|
32844is the |k|th distinct value on the resulting list, and it sets
32845|perturbation| to the maximum amount by which a |value| field has
32846been changed. The size of the resulting list is returned as the
32847value of |skimp|.
32848
32849@c
32850static integer mp_skimp (MP mp, integer m) {
32851  mp_number d;     /* the size of intervals being coalesced */
32852  mp_node p, q, r;      /* list manipulation registers */
32853  mp_number l;     /* the least value in the current interval */
32854  mp_number v;     /* a compromise value */
32855  mp_number l_d;
32856  new_number (d);
32857  mp_threshold (mp, d, m);
32858  new_number (l);
32859  new_number (l_d);
32860  new_number (v);
32861  set_number_to_zero (mp->perturbation);
32862  q = mp->temp_head;
32863  m = 0;
32864  p = mp_link (mp->temp_head);
32865  while (p != mp->inf_val) {
32866    incr (m);
32867    number_clone (l, value_number (p));
32868    set_indep_value (p,m);
32869    set_number_from_addition (l_d, l, d);
32870    if (number_lessequal (value_number (mp_link (p)), l_d)) {
32871      @<Replace an interval of values by its midpoint@>;
32872    }
32873    q = p;
32874    p = mp_link (p);
32875  }
32876  free_number (l_d);
32877  free_number (d);
32878  free_number (l);
32879  free_number (v);
32880  return m;
32881}
32882
32883
32884@ @<Replace an interval...@>=
32885{
32886  mp_number test;
32887  new_number (test);
32888  do {
32889    p = mp_link (p);
32890    set_indep_value (p, m);
32891    decr (mp->excess);
32892    if (mp->excess == 0) {
32893       number_clone (l_d, l);
32894    }
32895  } while (number_lessequal(value_number (mp_link (p)), l_d));
32896  set_number_from_substraction (test, value_number (p), l);
32897  number_halfp(test);
32898  set_number_from_addition (v, l, test);
32899  set_number_from_substraction (test, value_number (p), v);
32900  if (number_greater (test, mp->perturbation))
32901    number_clone (mp->perturbation, test);
32902  r = q;
32903  do {
32904    r = mp_link (r);
32905    set_value_number (r, v);
32906  } while (r != p);
32907  mp_link (q) = p;              /* remove duplicate values from the current list */
32908  free_number (test);
32909}
32910
32911
32912@ A warning message is issued whenever something is perturbed by
32913more than 1/16\thinspace pt.
32914
32915@c
32916static void mp_tfm_warning (MP mp, quarterword m) {
32917  mp_print_nl (mp, "(some ");
32918  mp_print (mp, internal_name (m));
32919@.some charwds...@>
32920@.some chardps...@>
32921@.some charhts...@>
32922@.some charics...@>;
32923  mp_print (mp, " values had to be adjusted by as much as ");
32924  print_number (mp->perturbation);
32925  mp_print (mp, "pt)");
32926}
32927
32928
32929@ Here's an example of how we use these routines.
32930The width data needs to be perturbed only if there are 256 distinct
32931widths, but \MP\ must check for this case even though it is
32932highly unusual.
32933
32934An integer variable |k| will be defined when we use this code.
32935The |dimen_head| array will contain pointers to the sorted
32936lists of dimensions.
32937
32938@d tfm_warn_threshold_k  ((math_data *)mp->math)->tfm_warn_threshold_t
32939
32940@<Massage the \.{TFM} widths@>=
32941clear_the_list;
32942for (k = mp->bc; k <= mp->ec; k++) {
32943  if (mp->char_exists[k])
32944    mp->tfm_width[k] = mp_sort_in (mp, value_number (mp->tfm_width[k]));
32945}
32946mp->nw = (short) (mp_skimp (mp, 255) + 1);
32947mp->dimen_head[1] = mp_link (mp->temp_head);
32948if (number_greaterequal (mp->perturbation, tfm_warn_threshold_k))
32949  mp_tfm_warning (mp, mp_char_wd)
32950
32951
32952@ @<Glob...@>=
32953mp_node dimen_head[5];  /* lists of \.{TFM} dimensions */
32954
32955@ Heights, depths, and italic corrections are different from widths
32956not only because their list length is more severely restricted, but
32957also because zero values do not need to be put into the lists.
32958
32959@<Massage the \.{TFM} heights, depths, and italic corrections@>=
32960clear_the_list;
32961for (k = mp->bc; k <= mp->ec; k++) {
32962  if (mp->char_exists[k]) {
32963    if (mp->tfm_height[k] == 0)
32964      mp->tfm_height[k] = mp->zero_val;
32965    else
32966      mp->tfm_height[k] = mp_sort_in (mp, value_number (mp->tfm_height[k]));
32967  }
32968}
32969mp->nh = (short) (mp_skimp (mp, 15) + 1);
32970mp->dimen_head[2] = mp_link (mp->temp_head);
32971if (number_greaterequal (mp->perturbation, tfm_warn_threshold_k))
32972  mp_tfm_warning (mp, mp_char_ht);
32973clear_the_list;
32974for (k = mp->bc; k <= mp->ec; k++) {
32975  if (mp->char_exists[k]) {
32976    if (mp->tfm_depth[k] == 0)
32977      mp->tfm_depth[k] = mp->zero_val;
32978    else
32979      mp->tfm_depth[k] = mp_sort_in (mp, value_number (mp->tfm_depth[k]));
32980  }
32981}
32982mp->nd = (short) (mp_skimp (mp, 15) + 1);
32983mp->dimen_head[3] = mp_link (mp->temp_head);
32984if (number_greaterequal (mp->perturbation, tfm_warn_threshold_k))
32985  mp_tfm_warning (mp, mp_char_dp);
32986clear_the_list;
32987for (k = mp->bc; k <= mp->ec; k++) {
32988  if (mp->char_exists[k]) {
32989    if (mp->tfm_ital_corr[k] == 0)
32990      mp->tfm_ital_corr[k] = mp->zero_val;
32991    else
32992      mp->tfm_ital_corr[k] = mp_sort_in (mp, value_number (mp->tfm_ital_corr[k]));
32993  }
32994}
32995mp->ni = (short) (mp_skimp (mp, 63) + 1);
32996mp->dimen_head[4] = mp_link (mp->temp_head);
32997if (number_greaterequal (mp->perturbation, tfm_warn_threshold_k))
32998  mp_tfm_warning (mp, mp_char_ic)
32999
33000
33001@ @<Initialize table entries@>=
33002mp->zero_val = mp_get_value_node (mp);
33003set_value_number (mp->zero_val, zero_t);
33004
33005@ @<Free table entries@>=
33006mp_free_value_node (mp, mp->zero_val);
33007
33008@ Bytes 5--8 of the header are set to the design size, unless the user has
33009some crazy reason for specifying them differently.
33010@^design size@>
33011
33012Error messages are not allowed at the time this procedure is called,
33013so a warning is printed instead.
33014
33015The value of |max_tfm_dimen| is calculated so that
33016$$\hbox{|make_scaled(16*max_tfm_dimen,internal_value(mp_design_size))|}
33017 < \\{three\_bytes}.$$
33018
33019@d three_bytes 0100000000 /* $2^{24}$ */
33020
33021@c
33022static void mp_fix_design_size (MP mp) {
33023  mp_number d;     /* the design size */
33024  new_number (d);
33025  number_clone (d, internal_value (mp_design_size));
33026  if (number_less(d, unity_t) || number_greaterequal(d, fraction_half_t)) {
33027    if (!number_zero (d))
33028      mp_print_nl (mp, "(illegal design size has been changed to 128pt)");
33029@.illegal design size...@>;
33030    set_number_from_scaled (d, 040000000);
33031    number_clone (internal_value (mp_design_size), d);
33032  }
33033  if (mp->header_byte[4] == 0 && mp->header_byte[5] == 0 &&
33034      mp->header_byte[6] == 0 && mp->header_byte[7] == 0) {
33035    integer dd = number_to_scaled (d);
33036    mp->header_byte[4] = (char) (dd / 04000000);
33037    mp->header_byte[5] = (char) ((dd / 4096) % 256);
33038    mp->header_byte[6] = (char) ((dd / 16) % 256);
33039    mp->header_byte[7] = (char) ((dd % 16) * 16);
33040  }
33041  /* |mp->max_tfm_dimen = 16 * internal_value (mp_design_size) - 1 - internal_value (mp_design_size) / 010000000| */
33042  {
33043    mp_number secondpart;
33044    new_number (secondpart);
33045    number_clone (secondpart, internal_value (mp_design_size));
33046    number_clone (mp->max_tfm_dimen, secondpart);
33047    number_divide_int (secondpart, 010000000);
33048    number_multiply_int (mp->max_tfm_dimen, 16);
33049    number_add_scaled (mp->max_tfm_dimen, -1);
33050    number_substract (mp->max_tfm_dimen, secondpart);
33051    free_number (secondpart);
33052  }
33053  if (number_greaterequal (mp->max_tfm_dimen, fraction_half_t)) {
33054    number_clone (mp->max_tfm_dimen, fraction_half_t);
33055    number_add_scaled (mp->max_tfm_dimen, -1);
33056  }
33057  free_number (d);
33058}
33059
33060
33061@ The |dimen_out| procedure computes a |fix_word| relative to the
33062design size. If the data was out of range, it is corrected and the
33063global variable |tfm_changed| is increased by~one.
33064
33065@c
33066static integer mp_dimen_out (MP mp, mp_number x_orig) {
33067  integer ret;
33068  mp_number abs_x;
33069  mp_number x;
33070  new_number (abs_x);
33071  new_number (x);
33072  number_clone (x, x_orig);
33073  number_clone (abs_x, x_orig);
33074  number_abs (abs_x);
33075  if (number_greater (abs_x, mp->max_tfm_dimen)) {
33076    incr (mp->tfm_changed);
33077    if (number_positive(x))
33078      number_clone (x, mp->max_tfm_dimen);
33079    else {
33080      number_clone (x, mp->max_tfm_dimen);
33081      number_negate (x);
33082    }
33083  }
33084  {
33085    mp_number arg1;
33086    new_number (arg1);
33087    number_clone (arg1, x);
33088    number_multiply_int (arg1, 16);
33089    make_scaled (x, arg1, internal_value (mp_design_size));
33090    free_number (arg1);
33091  }
33092  free_number (abs_x);
33093  ret = number_to_scaled (x);
33094  free_number (x);
33095  return ret;
33096}
33097
33098
33099@ @<Glob...@>=
33100mp_number max_tfm_dimen;   /* bound on widths, heights, kerns, etc. */
33101integer tfm_changed;    /* the number of data entries that were out of bounds */
33102
33103@ @<Initialize table...@>=
33104new_number (mp->max_tfm_dimen);
33105
33106@ @<Dealloc...@>=
33107free_number (mp->max_tfm_dimen);
33108
33109
33110@ If the user has not specified any of the first four header bytes,
33111the |fix_check_sum| procedure replaces them by a ``check sum'' computed
33112from the |tfm_width| data relative to the design size.
33113@^check sum@>
33114
33115@c
33116static void mp_fix_check_sum (MP mp) {
33117  eight_bits k; /* runs through character codes */
33118  eight_bits B1, B2, B3, B4;    /* bytes of the check sum */
33119  integer x;    /* hash value used in check sum computation */
33120  if (mp->header_byte[0] == 0 && mp->header_byte[1] == 0 &&
33121      mp->header_byte[2] == 0 && mp->header_byte[3] == 0) {
33122    @<Compute a check sum in |(b1,b2,b3,b4)|@>;
33123    mp->header_byte[0] = (char) B1;
33124    mp->header_byte[1] = (char) B2;
33125    mp->header_byte[2] = (char) B3;
33126    mp->header_byte[3] = (char) B4;
33127    return;
33128  }
33129}
33130
33131
33132@ @<Compute a check sum in |(b1,b2,b3,b4)|@>=
33133B1 = mp->bc;
33134B2 = mp->ec;
33135B3 = mp->bc;
33136B4 = mp->ec;
33137mp->tfm_changed = 0;
33138for (k = mp->bc; k <= mp->ec; k++) {
33139  if (mp->char_exists[k]) {
33140    x = mp_dimen_out (mp, value_number (mp->tfm_width[k])) + (k + 4) * 020000000;      /* this is positive */
33141    B1 = (eight_bits) ((B1 + B1 + x) % 255);
33142    B2 = (eight_bits) ((B2 + B2 + x) % 253);
33143    B3 = (eight_bits) ((B3 + B3 + x) % 251);
33144    B4 = (eight_bits) ((B4 + B4 + x) % 247);
33145  }
33146  if (k == mp->ec)
33147    break;
33148}
33149
33150
33151@ Finally we're ready to actually write the \.{TFM} information.
33152Here are some utility routines for this purpose.
33153
33154@d tfm_out(A) do { /* output one byte to |tfm_file| */
33155  unsigned char s=(unsigned char)(A);
33156  (mp->write_binary_file)(mp,mp->tfm_file,(void *)&s,1);
33157  } while (0)
33158
33159@c
33160static void mp_tfm_two (MP mp, integer x) {                               /* output two bytes to |tfm_file| */
33161  tfm_out (x / 256);
33162  tfm_out (x % 256);
33163}
33164static void mp_tfm_four (MP mp, integer x) {                               /* output four bytes to |tfm_file| */
33165  if (x >= 0)
33166    tfm_out (x / three_bytes);
33167  else {
33168    x = x + 010000000000;       /* use two's complement for negative values */
33169    x = x + 010000000000;
33170    tfm_out ((x / three_bytes) + 128);
33171  };
33172  x = x % three_bytes;
33173  tfm_out (x / number_to_scaled (unity_t));
33174  x = x % number_to_scaled (unity_t);
33175  tfm_out (x / 0400);
33176  tfm_out (x % 0400);
33177}
33178static void mp_tfm_qqqq (MP mp, four_quarters x) {                               /* output four quarterwords to |tfm_file| */
33179  tfm_out (qo (x.b0));
33180  tfm_out (qo (x.b1));
33181  tfm_out (qo (x.b2));
33182  tfm_out (qo (x.b3));
33183}
33184
33185
33186@ @<Finish the \.{TFM} file@>=
33187if (mp->job_name == NULL)
33188  mp_open_log_file (mp);
33189mp_pack_job_name (mp, ".tfm");
33190while (!mp_open_out (mp, &mp->tfm_file, mp_filetype_metrics))
33191  mp_prompt_file_name (mp, "file name for font metrics", ".tfm");
33192mp->metric_file_name = xstrdup (mp->name_of_file);
33193@<Output the subfile sizes and header bytes@>;
33194@<Output the character information bytes, then
33195  output the dimensions themselves@>;
33196@<Output the ligature/kern program@>;
33197@<Output the extensible character recipes and the font metric parameters@>;
33198if (number_positive (internal_value (mp_tracing_stats)))
33199  @<Log the subfile sizes of the \.{TFM} file@>;
33200mp_print_nl (mp, "Font metrics written on ");
33201mp_print (mp, mp->metric_file_name);
33202mp_print_char (mp, xord ('.'));
33203@.Font metrics written...@>;
33204(mp->close_file) (mp, mp->tfm_file)
33205
33206
33207@ Integer variables |lh|, |k|, and |lk_offset| will be defined when we use
33208this code.
33209
33210@<Output the subfile sizes and header bytes@>=
33211k = mp->header_last;
33212LH = (k + 4) / 4;               /* this is the number of header words */
33213if (mp->bc > mp->ec)
33214  mp->bc = 1;                   /* if there are no characters, |ec=0| and |bc=1| */
33215@<Compute the ligature/kern program offset and implant the
33216  left boundary label@>;
33217mp_tfm_two (mp,
33218            6 + LH + (mp->ec - mp->bc + 1) + mp->nw + mp->nh + mp->nd + mp->ni +
33219            mp->nl + lk_offset + mp->nk + mp->ne + mp->np);
33220  /* this is the total number of file words that will be output */
33221mp_tfm_two (mp, LH);
33222mp_tfm_two (mp, mp->bc);
33223mp_tfm_two (mp, mp->ec);
33224mp_tfm_two (mp, mp->nw);
33225mp_tfm_two (mp, mp->nh);
33226mp_tfm_two (mp, mp->nd);
33227mp_tfm_two (mp, mp->ni);
33228mp_tfm_two (mp, mp->nl + lk_offset);
33229mp_tfm_two (mp, mp->nk);
33230mp_tfm_two (mp, mp->ne);
33231mp_tfm_two (mp, mp->np);
33232for (k = 0; k < 4 * LH; k++) {
33233  tfm_out (mp->header_byte[k]);
33234}
33235
33236
33237@ @<Output the character information bytes...@>=
33238for (k = mp->bc; k <= mp->ec; k++) {
33239  if (!mp->char_exists[k]) {
33240    mp_tfm_four (mp, 0);
33241  } else {
33242    tfm_out (indep_value (mp->tfm_width[k]));       /* the width index */
33243    tfm_out ((indep_value (mp->tfm_height[k])) * 16 + indep_value (mp->tfm_depth[k]));
33244    tfm_out ((indep_value (mp->tfm_ital_corr[k])) * 4 + mp->char_tag[k]);
33245    tfm_out (mp->char_remainder[k]);
33246  };
33247}
33248mp->tfm_changed = 0;
33249for (k = 1; k <= 4; k++) {
33250  mp_tfm_four (mp, 0);
33251  p = mp->dimen_head[k];
33252  while (p != mp->inf_val) {
33253    mp_tfm_four (mp, mp_dimen_out (mp, value_number (p)));
33254    p = mp_link (p);
33255  }
33256}
33257
33258
33259@ We need to output special instructions at the beginning of the
33260|lig_kern| array in order to specify the right boundary character
33261and/or to handle starting addresses that exceed 255. The |label_loc|
33262and |label_char| arrays have been set up to record all the
33263starting addresses; we have $-1=|label_loc|[0]<|label_loc|[1]\le\cdots
33264\le|label_loc|[|label_ptr]|$.
33265
33266@<Compute the ligature/kern program offset...@>=
33267mp->bchar = round_unscaled (internal_value (mp_boundary_char));
33268if ((mp->bchar < 0) || (mp->bchar > 255)) {
33269  mp->bchar = -1;
33270  mp->lk_started = false;
33271  lk_offset = 0;
33272} else {
33273  mp->lk_started = true;
33274  lk_offset = 1;
33275}
33276@<Find the minimum |lk_offset| and adjust all remainders@>;
33277if (mp->bch_label < undefined_label) {
33278  skip_byte (mp->nl) = qi (255);
33279  next_char (mp->nl) = qi (0);
33280  op_byte (mp->nl) = qi (((mp->bch_label + lk_offset) / 256));
33281  rem_byte (mp->nl) = qi (((mp->bch_label + lk_offset) % 256));
33282  mp->nl++;                     /* possibly |nl=lig_table_size+1| */
33283}
33284
33285@ @<Find the minimum |lk_offset|...@>=
33286k = mp->label_ptr;              /* pointer to the largest unallocated label */
33287if (mp->label_loc[k] + lk_offset > 255) {
33288  lk_offset = 0;
33289  mp->lk_started = false;       /* location 0 can do double duty */
33290  do {
33291    mp->char_remainder[mp->label_char[k]] = lk_offset;
33292    while (mp->label_loc[k - 1] == mp->label_loc[k]) {
33293      decr (k);
33294      mp->char_remainder[mp->label_char[k]] = lk_offset;
33295    }
33296    incr (lk_offset);
33297    decr (k);
33298  } while (!(lk_offset + mp->label_loc[k] < 256));
33299  /* N.B.: |lk_offset=256| satisfies this when |k=0| */
33300}
33301if (lk_offset > 0) {
33302  while (k > 0) {
33303    mp->char_remainder[mp->label_char[k]]
33304      = mp->char_remainder[mp->label_char[k]] + lk_offset;
33305    decr (k);
33306  }
33307}
33308
33309@ @<Output the ligature/kern program@>=
33310for (k = 0; k <= 255; k++) {
33311  if (mp->skip_table[k] < undefined_label) {
33312    mp_print_nl (mp, "(local label ");
33313    mp_print_int (mp, k);
33314    mp_print (mp, ":: was missing)");
33315@.local label l:: was missing@>;
33316    cancel_skips (mp->skip_table[k]);
33317  }
33318}
33319if (mp->lk_started) {           /* |lk_offset=1| for the special |bchar| */
33320  tfm_out (255);
33321  tfm_out (mp->bchar);
33322  mp_tfm_two (mp, 0);
33323} else {
33324  for (k = 1; k <= lk_offset; k++) {    /* output the redirection specs */
33325    mp->ll = mp->label_loc[mp->label_ptr];
33326    if (mp->bchar < 0) {
33327      tfm_out (254);
33328      tfm_out (0);
33329    } else {
33330      tfm_out (255);
33331      tfm_out (mp->bchar);
33332    };
33333    mp_tfm_two (mp, mp->ll + lk_offset);
33334    do {
33335      mp->label_ptr--;
33336    } while (!(mp->label_loc[mp->label_ptr] < mp->ll));
33337  }
33338}
33339for (k = 0; k < mp->nl; k++)
33340  mp_tfm_qqqq (mp, mp->lig_kern[k]);
33341{
33342  mp_number arg;
33343  new_number (arg);
33344  for (k = 0; k < mp->nk; k++) {
33345    number_clone (arg, mp->kern[k]);
33346    mp_tfm_four (mp, mp_dimen_out (mp, arg));
33347  }
33348  free_number (arg);
33349}
33350
33351@ @<Output the extensible character recipes...@>=
33352for (k = 0; k < mp->ne; k++)
33353  mp_tfm_qqqq (mp, mp->exten[k]);
33354{
33355mp_number arg;
33356new_number (arg);
33357for (k = 1; k <= mp->np; k++) {
33358  if (k == 1) {
33359    number_clone (arg, mp->param[1]);
33360    number_abs (arg);
33361    if (number_less(arg, fraction_half_t)) {
33362      mp_tfm_four (mp, number_to_scaled (mp->param[1]) * 16);
33363    } else {
33364      incr (mp->tfm_changed);
33365      if (number_positive(mp->param[1]))
33366        mp_tfm_four (mp, max_integer);
33367      else
33368        mp_tfm_four (mp, -max_integer);
33369    }
33370  } else {
33371    number_clone (arg, mp->param[k]);
33372    mp_tfm_four (mp, mp_dimen_out (mp, arg));
33373  }
33374}
33375free_number (arg);
33376}
33377if (mp->tfm_changed > 0) {
33378  if (mp->tfm_changed == 1) {
33379    mp_print_nl (mp, "(a font metric dimension");
33380@.a font metric dimension...@>
33381  } else {
33382    mp_print_nl (mp, "(");
33383    mp_print_int (mp, mp->tfm_changed);
33384@.font metric dimensions...@>;
33385    mp_print (mp, " font metric dimensions");
33386  }
33387  mp_print (mp, " had to be decreased)");
33388}
33389
33390@ @<Log the subfile sizes of the \.{TFM} file@>=
33391{
33392  char s[200];
33393  wlog_ln (" ");
33394  if (mp->bch_label < undefined_label)
33395    mp->nl--;
33396  mp_snprintf (s, 128,
33397               "(You used %iw,%ih,%id,%ii,%il,%ik,%ie,%ip metric file positions)",
33398               mp->nw, mp->nh, mp->nd, mp->ni, mp->nl, mp->nk, mp->ne, mp->np);
33399  wlog_ln (s);
33400}
33401
33402
33403@* Reading font metric data.
33404
33405\MP\ isn't a typesetting program but it does need to find the bounding box
33406of a sequence of typeset characters.  Thus it needs to read \.{TFM} files as
33407well as write them.
33408
33409@<Glob...@>=
33410void *tfm_infile;
33411
33412@ All the width, height, and depth information is stored in an array called
33413|font_info|.  This array is allocated sequentially and each font is stored
33414as a series of |char_info| words followed by the width, height, and depth
33415tables.  Since |font_name| entries are permanent, their |str_ref| values are
33416set to |MAX_STR_REF|.
33417
33418@<Types...@>=
33419typedef unsigned int font_number;       /* |0..font_max| */
33420
33421@ The |font_info| array is indexed via a group directory arrays.
33422For example, the |char_info| data for character~|c| in font~|f| will be
33423in |font_info[char_base[f]+c].qqqq|.
33424
33425@<Glob...@>=
33426font_number font_max;   /* maximum font number for included text fonts */
33427size_t font_mem_size;   /* number of words for \.{TFM} information for text fonts */
33428font_data *font_info;   /* height, width, and depth data */
33429char **font_enc_name;   /* encoding names, if any */
33430boolean *font_ps_name_fixed;    /* are the postscript names fixed already?  */
33431size_t next_fmem;       /* next unused entry in |font_info| */
33432font_number last_fnum;  /* last font number used so far */
33433integer *font_dsize;     /* 16 times the ``design'' size in \ps\ points */
33434char **font_name;       /* name as specified in the \&{infont} command */
33435char **font_ps_name;    /* PostScript name for use when |internal[mp_prologues]>0| */
33436font_number last_ps_fnum;       /* last valid |font_ps_name| index */
33437eight_bits *font_bc;
33438eight_bits *font_ec;    /* first and last character code */
33439int *char_base; /* base address for |char_info| */
33440int *width_base;        /* index for zeroth character width */
33441int *height_base;       /* index for zeroth character height */
33442int *depth_base;        /* index for zeroth character depth */
33443mp_node *font_sizes;
33444
33445@ @<Allocate or initialize ...@>=
33446mp->font_mem_size = 10000;
33447mp->font_info = xmalloc ((mp->font_mem_size + 1), sizeof (font_data));
33448memset (mp->font_info, 0, sizeof (font_data) * (mp->font_mem_size + 1));
33449mp->last_fnum = null_font;
33450
33451@ @<Dealloc variables@>=
33452for (k = 1; k <= (int) mp->last_fnum; k++) {
33453  xfree (mp->font_enc_name[k]);
33454  xfree (mp->font_name[k]);
33455  xfree (mp->font_ps_name[k]);
33456}
33457for (k = 0; k <= 255; k++) {
33458/* These are disabled for now following a bug-report about double free
33459   errors. TO BE FIXED, bug tracker id 831 */
33460/*|
33461  mp_free_value_node (mp, mp->tfm_width[k]);
33462  mp_free_value_node (mp, mp->tfm_height[k]);
33463  mp_free_value_node (mp, mp->tfm_depth[k]);
33464  mp_free_value_node (mp, mp->tfm_ital_corr[k]);
33465|*/
33466}
33467
33468xfree (mp->font_info);
33469xfree (mp->font_enc_name);
33470xfree (mp->font_ps_name_fixed);
33471xfree (mp->font_dsize);
33472xfree (mp->font_name);
33473xfree (mp->font_ps_name);
33474xfree (mp->font_bc);
33475xfree (mp->font_ec);
33476xfree (mp->char_base);
33477xfree (mp->width_base);
33478xfree (mp->height_base);
33479xfree (mp->depth_base);
33480xfree (mp->font_sizes);
33481
33482@
33483@c
33484void mp_reallocate_fonts (MP mp, font_number l) {
33485  font_number f;
33486  XREALLOC (mp->font_enc_name, l, char *);
33487  XREALLOC (mp->font_ps_name_fixed, l, boolean);
33488  XREALLOC (mp->font_dsize, l, integer);
33489  XREALLOC (mp->font_name, l, char *);
33490  XREALLOC (mp->font_ps_name, l, char *);
33491  XREALLOC (mp->font_bc, l, eight_bits);
33492  XREALLOC (mp->font_ec, l, eight_bits);
33493  XREALLOC (mp->char_base, l, int);
33494  XREALLOC (mp->width_base, l, int);
33495  XREALLOC (mp->height_base, l, int);
33496  XREALLOC (mp->depth_base, l, int);
33497  XREALLOC (mp->font_sizes, l, mp_node);
33498  for (f = (mp->last_fnum + 1); f <= l; f++) {
33499    mp->font_enc_name[f] = NULL;
33500    mp->font_ps_name_fixed[f] = false;
33501    mp->font_name[f] = NULL;
33502    mp->font_ps_name[f] = NULL;
33503    mp->font_sizes[f] = NULL;
33504  }
33505  mp->font_max = l;
33506}
33507
33508
33509@ @<Internal library declarations@>=
33510void mp_reallocate_fonts (MP mp, font_number l);
33511
33512
33513@ A |null_font| containing no characters is useful for error recovery.  Its
33514|font_name| entry starts out empty but is reset each time an erroneous font is
33515found.  This helps to cut down on the number of duplicate error messages without
33516wasting a lot of space.
33517
33518@d null_font 0 /* the |font_number| for an empty font */
33519
33520@<Set initial...@>=
33521mp->font_dsize[null_font] = 0;
33522mp->font_bc[null_font] = 1;
33523mp->font_ec[null_font] = 0;
33524mp->char_base[null_font] = 0;
33525mp->width_base[null_font] = 0;
33526mp->height_base[null_font] = 0;
33527mp->depth_base[null_font] = 0;
33528mp->next_fmem = 0;
33529mp->last_fnum = null_font;
33530mp->last_ps_fnum = null_font;
33531{
33532  static char nullfont_name[] = "nullfont";
33533  static char nullfont_psname[] = "";
33534  mp->font_name[null_font] = nullfont_name;
33535  mp->font_ps_name[null_font] = nullfont_psname;
33536}
33537mp->font_ps_name_fixed[null_font] = false;
33538mp->font_enc_name[null_font] = NULL;
33539mp->font_sizes[null_font] = NULL;
33540
33541@ Each |char_info| word is of type |four_quarters|.  The |b0| field contains
33542the |width index|; the |b1| field contains the height
33543index; the |b2| fields contains the depth index, and the |b3| field used only
33544for temporary storage. (It is used to keep track of which characters occur in
33545an edge structure that is being shipped out.)
33546The corresponding words in the width, height, and depth tables are stored as
33547|scaled| values in units of \ps\ points.
33548
33549With the macros below, the |char_info| word for character~|c| in font~|f| is
33550|char_mp_info(f,c)| and the width is
33551$$\hbox{|char_width(f,char_mp_info(f,c)).sc|.}$$
33552
33553@d char_mp_info(A,B) mp->font_info[mp->char_base[(A)]+(B)].qqqq
33554@d char_width(A,B) mp->font_info[mp->width_base[(A)]+(B).b0].sc
33555@d char_height(A,B) mp->font_info[mp->height_base[(A)]+(B).b1].sc
33556@d char_depth(A,B) mp->font_info[mp->depth_base[(A)]+(B).b2].sc
33557@d ichar_exists(A) ((A).b0>0)
33558
33559@ When we have a font name and we don't know whether it has been loaded yet,
33560we scan the |font_name| array before calling |read_font_info|.
33561
33562@<Declarations@>=
33563static font_number mp_find_font (MP mp, char *f);
33564
33565@ @c
33566font_number mp_find_font (MP mp, char *f) {
33567  font_number n;
33568  for (n = 0; n <= mp->last_fnum; n++) {
33569    if (mp_xstrcmp (f, mp->font_name[n]) == 0) {
33570      return n;
33571    }
33572  }
33573  n = mp_read_font_info (mp, f);
33574  return n;
33575}
33576
33577
33578@ This is an interface function for getting the width of character,
33579as a double in ps units
33580
33581@c
33582double mp_get_char_dimension (MP mp, char *fname, int c, int t) {
33583  unsigned n;
33584  four_quarters cc;
33585  font_number f = 0;
33586  double w = -1.0;
33587  for (n = 0; n <= mp->last_fnum; n++) {
33588    if (mp_xstrcmp (fname, mp->font_name[n]) == 0) {
33589      f = n;
33590      break;
33591    }
33592  }
33593  if (f == 0)
33594    return 0.0;
33595  cc = char_mp_info (f, c);
33596  if (!ichar_exists (cc))
33597    return 0.0;
33598  if (t == 'w')
33599    w = (double) char_width (f, cc);
33600  else if (t == 'h')
33601    w = (double) char_height (f, cc);
33602  else if (t == 'd')
33603    w = (double) char_depth (f, cc);
33604  return w / 655.35 * (72.27 / 72);
33605}
33606
33607
33608@ @<Exported function ...@>=
33609double mp_get_char_dimension (MP mp, char *fname, int n, int t);
33610
33611
33612@ If we discover that the font doesn't have a requested character, we omit it
33613from the bounding box computation and expect the \ps\ interpreter to drop it.
33614This routine issues a warning message if the user has asked for it.
33615
33616@<Declarations@>=
33617static void mp_lost_warning (MP mp, font_number f, int k);
33618
33619@ @c
33620void mp_lost_warning (MP mp, font_number f, int k) {
33621  if (number_positive (internal_value (mp_tracing_lost_chars))) {
33622    mp_begin_diagnostic (mp);
33623    if (mp->selector == log_only)
33624      incr (mp->selector);
33625    mp_print_nl (mp, "Missing character: There is no ");
33626@.Missing character@>;
33627    mp_print_int (mp, k);
33628    mp_print (mp, " in font ");
33629    mp_print (mp, mp->font_name[f]);
33630    mp_print_char (mp, xord ('!'));
33631    mp_end_diagnostic (mp, false);
33632  }
33633}
33634
33635
33636@ The whole purpose of saving the height, width, and depth information is to be
33637able to find the bounding box of an item of text in an edge structure.  The
33638|set_text_box| procedure takes a text node and adds this information.
33639
33640@<Declarations@>=
33641static void mp_set_text_box (MP mp, mp_text_node p);
33642
33643@ @c
33644void mp_set_text_box (MP mp, mp_text_node p) {
33645  font_number f;        /* |mp_font_n(p)| */
33646  ASCII_code bc, ec;    /* range of valid characters for font |f| */
33647  size_t k, kk; /* current character and character to stop at */
33648  four_quarters cc;     /* the |char_info| for the current character */
33649  mp_number h, d;  /* dimensions of the current character */
33650  new_number(h);
33651  new_number(d);
33652  set_number_to_zero(p->width);
33653  set_number_to_neg_inf(p->height);
33654  set_number_to_neg_inf(p->depth);
33655  f = (font_number) mp_font_n (p);
33656  bc = mp->font_bc[f];
33657  ec = mp->font_ec[f];
33658  kk = mp_text_p (p)->len;
33659  k = 0;
33660  while (k < kk) {
33661    @<Adjust |p|'s bounding box to contain |str_pool[k]|; advance |k|@>;
33662  }
33663  @<Set the height and depth to zero if the bounding box is empty@>;
33664  free_number (h);
33665  free_number (d);
33666}
33667
33668
33669@ @<Adjust |p|'s bounding box to contain |str_pool[k]|; advance |k|@>=
33670{
33671  if ((*(mp_text_p (p)->str + k) < bc) || (*(mp_text_p (p)->str + k) > ec)) {
33672    mp_lost_warning (mp, f, *(mp_text_p (p)->str + k));
33673  } else {
33674    cc = char_mp_info (f, *(mp_text_p (p)->str + k));
33675    if (!ichar_exists (cc)) {
33676      mp_lost_warning (mp, f, *(mp_text_p (p)->str + k));
33677    } else {
33678      set_number_from_scaled(p->width, number_to_scaled(p->width) + char_width (f, cc));
33679      set_number_from_scaled(h, char_height (f, cc));
33680      set_number_from_scaled(d, char_depth (f, cc));
33681      if (number_greater(h, p->height))
33682        number_clone(p->height, h);
33683      if (number_greater(d, p->depth))
33684        number_clone(p->depth, d);
33685    }
33686  }
33687  incr (k);
33688}
33689
33690
33691@ Let's hope modern compilers do comparisons correctly when the difference would
33692overflow.
33693
33694@<Set the height and depth to zero if the bounding box is empty@>=
33695if (number_to_scaled(p->height) < -number_to_scaled(p->depth)) {
33696  set_number_to_zero(p->height);
33697  set_number_to_zero(p->depth);
33698}
33699
33700@ The new primitives fontmapfile and fontmapline.
33701
33702@<Declare action procedures for use by |do_statement|@>=
33703static void mp_do_mapfile (MP mp);
33704static void mp_do_mapline (MP mp);
33705
33706@ @c
33707static void mp_do_mapfile (MP mp) {
33708  mp_get_x_next (mp);
33709  mp_scan_expression (mp);
33710  if (mp->cur_exp.type != mp_string_type) {
33711    @<Complain about improper map operation@>;
33712  } else {
33713    mp_map_file (mp, cur_exp_str ());
33714  }
33715}
33716static void mp_do_mapline (MP mp) {
33717  mp_get_x_next (mp);
33718  mp_scan_expression (mp);
33719  if (mp->cur_exp.type != mp_string_type) {
33720    @<Complain about improper map operation@>;
33721  } else {
33722    mp_map_line (mp, cur_exp_str ());
33723  }
33724}
33725
33726
33727@ @<Complain about improper map operation@>=
33728{
33729  const char *hlp[] = { "Only known strings can be map files or map lines.", NULL };
33730  mp_disp_err(mp, NULL);
33731  mp_back_error (mp, "Unsuitable expression", hlp, true);
33732  mp_get_x_next (mp);
33733}
33734
33735
33736@ To print |scaled| value to PDF output we need some subroutines to ensure
33737accurary.
33738
33739@d max_integer   0x7FFFFFFF /* $2^{31}-1$ */
33740
33741@<Glob...@>=
33742integer ten_pow[10];    /* $10^0..10^9$ */
33743integer scaled_out;     /* amount of |scaled| that was taken out in |divide_scaled| */
33744
33745@ @<Set init...@>=
33746mp->ten_pow[0] = 1;
33747for (i = 1; i <= 9; i++) {
33748  mp->ten_pow[i] = 10 * mp->ten_pow[i - 1];
33749}
33750
33751
33752@* Shipping pictures out.
33753The |ship_out| procedure, to be described below, is given a pointer to
33754an edge structure. Its mission is to output a file containing the \ps\
33755description of an edge structure.
33756
33757@ Each time an edge structure is shipped out we write a new \ps\ output
33758file named according to the current \&{charcode}.
33759@:char_code_}{\&{charcode} primitive@>
33760
33761This is the only backend function that remains in the main |mpost.w| file.
33762There are just too many variable accesses needed for status reporting
33763etcetera to make it worthwile to move the code to |psout.w|.
33764
33765@<Internal library declarations@>=
33766void mp_open_output_file (MP mp);
33767char *mp_get_output_file_name (MP mp);
33768char *mp_set_output_file_name (MP mp, integer c);
33769
33770@ @c
33771static void mp_append_to_template (MP mp, integer ff, integer c, boolean rounding) {
33772  if (internal_type (c) == mp_string_type) {
33773    char *ss = mp_str (mp, internal_string (c));
33774    mp_print (mp, ss);
33775  } else if (internal_type (c) == mp_known) {
33776    if (rounding) {
33777      int cc = round_unscaled (internal_value (c));
33778      print_with_leading_zeroes (cc, ff);
33779    } else {
33780      print_number (internal_value (c));
33781    }
33782  }
33783}
33784char *mp_set_output_file_name (MP mp, integer c) {
33785  char *ss = NULL;      /* filename extension proposal */
33786  char *nn = NULL;      /* temp string  for str() */
33787  unsigned old_setting; /* previous |selector| setting */
33788  size_t i;     /*  indexes into |filename_template|  */
33789  integer f;    /* field width */
33790  str_room (1024);
33791  if (mp->job_name == NULL)
33792    mp_open_log_file (mp);
33793  if (internal_string (mp_output_template) == NULL) {
33794    char *s;    /* a file extension derived from |c| */
33795    if (c < 0)
33796      s = xstrdup (".ps");
33797    else
33798      @<Use |c| to compute the file extension |s|@>;
33799    mp_pack_job_name (mp, s);
33800    free (s);
33801    ss = xstrdup (mp->name_of_file);
33802  } else {                      /* initializations */
33803    mp_string s, n, ftemplate;  /* a file extension derived from |c| */
33804    mp_number saved_char_code;
33805    new_number (saved_char_code);
33806    number_clone (saved_char_code, internal_value (mp_char_code));
33807    set_internal_from_number (mp_char_code, unity_t);
33808    number_multiply_int (internal_value (mp_char_code), c);
33809    if (internal_string (mp_job_name) == NULL) {
33810      if (mp->job_name == NULL) {
33811        mp->job_name = xstrdup ("mpout");
33812      }
33813      @<Fix up |mp->internal[mp_job_name]|@>;
33814    }
33815    old_setting = mp->selector;
33816    mp->selector = new_string;
33817    i = 0;
33818    n = mp_rts(mp,"");               /* initialize */
33819    ftemplate = internal_string (mp_output_template);
33820    while (i < ftemplate->len) {
33821      f = 0;
33822      if (*(ftemplate->str + i) == '%') {
33823      CONTINUE:
33824        incr (i);
33825        if (i < ftemplate->len) {
33826          switch (*(ftemplate->str + i)) {
33827          case 'j':
33828            mp_append_to_template (mp, f, mp_job_name, true);
33829            break;
33830          case 'c':
33831            if (number_negative (internal_value (mp_char_code))) {
33832              mp_print (mp, "ps");
33833            } else {
33834              mp_append_to_template (mp, f, mp_char_code, true);
33835            }
33836            break;
33837          case 'o':
33838            mp_append_to_template (mp, f, mp_output_format, true);
33839            break;
33840          case 'd':
33841            mp_append_to_template (mp, f, mp_day, true);
33842            break;
33843          case 'm':
33844            mp_append_to_template (mp, f, mp_month, true);
33845            break;
33846          case 'y':
33847            mp_append_to_template (mp, f, mp_year, true);
33848            break;
33849          case 'H':
33850            mp_append_to_template (mp, f, mp_hour, true);
33851            break;
33852          case 'M':
33853            mp_append_to_template (mp, f, mp_minute, true);
33854            break;
33855          case '{':
33856            {
33857              /* look up a name */
33858              size_t l = 0;
33859              size_t frst = i + 1;
33860              while (i < ftemplate->len) {
33861                i++;
33862                if (*(ftemplate->str + i) == '}')
33863                  break;
33864                l++;
33865              }
33866              if (l > 0) {
33867                mp_sym p =
33868                  mp_id_lookup (mp, (char *) (ftemplate->str + frst), l, false);
33869                char *id = xmalloc ((l + 1), 1);
33870                (void) memcpy (id, (char *) (ftemplate->str + frst), (size_t) l);
33871                *(id + l) = '\0';
33872                if (p == NULL) {
33873                  char err[256];
33874                  mp_snprintf (err, 256,
33875                               "requested identifier (%s) in outputtemplate not found.",
33876                               id);
33877                  mp_warn (mp, err);
33878                } else {
33879                  if (eq_type (p) == mp_internal_quantity) {
33880                    if (equiv (p) == mp_output_template) {
33881                      char err[256];
33882                      mp_snprintf (err, 256,
33883                                   "The appearance of outputtemplate inside outputtemplate is ignored.");
33884                      mp_warn (mp, err);
33885                    } else {
33886                      mp_append_to_template (mp, f, equiv (p), false);
33887                    }
33888                  } else {
33889                    char err[256];
33890                    mp_snprintf (err, 256,
33891                                 "requested identifier (%s) in outputtemplate is not an internal.",
33892                                 id);
33893                    mp_warn (mp, err);
33894                  }
33895                }
33896                free (id);
33897              }
33898            }
33899            break;
33900          case '0':
33901          case '1':
33902          case '2':
33903          case '3':
33904          case '4':
33905          case '5':
33906          case '6':
33907          case '7':
33908          case '8':
33909          case '9':
33910            if ((f < 10))
33911              f = (f * 10) + ftemplate->str[i] - '0';
33912            goto CONTINUE;
33913            break;
33914          case '%':
33915            mp_print_char (mp, '%');
33916            break;
33917          default:
33918            {
33919              char err[256];
33920              mp_snprintf (err, 256,
33921                           "requested format (%c) in outputtemplate is unknown.",
33922                           *(ftemplate->str + i));
33923              mp_warn (mp, err);
33924            }
33925            mp_print_char (mp, *(ftemplate->str + i));
33926          }
33927        }
33928      } else {
33929        if (*(ftemplate->str + i) == '.')
33930          if (n->len == 0)
33931            n = mp_make_string (mp);
33932        mp_print_char (mp, *(ftemplate->str + i));
33933      };
33934      incr (i);
33935    }
33936    s = mp_make_string (mp);
33937    number_clone (internal_value (mp_char_code), saved_char_code);
33938    free_number (saved_char_code);
33939    mp->selector = old_setting;
33940    if (n->len == 0) {
33941      n = s;
33942      s = mp_rts(mp,"");
33943    }
33944    ss = mp_str (mp, s);
33945    nn = mp_str (mp, n);
33946    mp_pack_file_name (mp, nn, "", ss);
33947    delete_str_ref (n);
33948    delete_str_ref (s);
33949  }
33950  return ss;
33951}
33952char *mp_get_output_file_name (MP mp) {
33953  char *f;
33954  char *saved_name;     /* saved |name_of_file| */
33955  saved_name = xstrdup (mp->name_of_file);
33956  (void) mp_set_output_file_name (mp, round_unscaled (internal_value(mp_char_code)));
33957  f = xstrdup (mp->name_of_file);
33958  mp_pack_file_name (mp, saved_name, NULL, NULL);
33959  free (saved_name);
33960  return f;
33961}
33962void mp_open_output_file (MP mp) {
33963  char *ss;     /* filename extension proposal */
33964  int c;    /* \&{charcode} rounded to the nearest integer */
33965  c = round_unscaled (internal_value (mp_char_code));
33966  ss = mp_set_output_file_name (mp, c);
33967  while (!mp_open_out (mp, (void *) &mp->output_file, mp_filetype_postscript))
33968    mp_prompt_file_name (mp, "file name for output", ss);
33969  mp_store_true_output_filename (mp, c);
33970}
33971
33972
33973@ The file extension created here could be up to five characters long in
33974extreme cases so it may have to be shortened on some systems.
33975@^system dependencies@>
33976
33977@<Use |c| to compute the file extension |s|@>=
33978{
33979  s = xmalloc (7, 1);
33980  mp_snprintf (s, 7, ".%i", (int) c);
33981}
33982
33983
33984@ The user won't want to see all the output file names so we only save the
33985first and last ones and a count of how many there were.  For this purpose
33986files are ordered primarily by \&{charcode} and secondarily by order of
33987creation.
33988@:char_code_}{\&{charcode} primitive@>
33989
33990@<Internal library ...@>=
33991void mp_store_true_output_filename (MP mp, int c);
33992
33993@ @c
33994void mp_store_true_output_filename (MP mp, int c)
33995{
33996  if ((c < mp->first_output_code) && (mp->first_output_code >= 0)) {
33997    mp->first_output_code = c;
33998    xfree (mp->first_file_name);
33999    mp->first_file_name = xstrdup (mp->name_of_file);
34000  }
34001  if (c >= mp->last_output_code) {
34002    mp->last_output_code = c;
34003    xfree (mp->last_file_name);
34004    mp->last_file_name = xstrdup (mp->name_of_file);
34005  }
34006  set_internal_string (mp_output_filename, mp_rts (mp, mp->name_of_file));
34007}
34008
34009@ @<Glob...@>=
34010char *first_file_name;
34011char *last_file_name;   /* full file names */
34012integer first_output_code;
34013integer last_output_code;       /* rounded \&{charcode} values */
34014@:char_code_}{\&{charcode} primitive@>
34015integer total_shipped;  /* total number of |ship_out| operations completed */
34016
34017@ @<Set init...@>=
34018mp->first_file_name = xstrdup ("");
34019mp->last_file_name = xstrdup ("");
34020mp->first_output_code = 32768;
34021mp->last_output_code = -32768;
34022mp->total_shipped = 0;
34023
34024@ @<Dealloc variables@>=
34025xfree (mp->first_file_name);
34026xfree (mp->last_file_name);
34027
34028@ @<Begin the progress report for the output of picture~|c|@>=
34029if ((int) mp->term_offset > mp->max_print_line - 6)
34030  mp_print_ln (mp);
34031else if ((mp->term_offset > 0) || (mp->file_offset > 0))
34032  mp_print_char (mp, xord (' '));
34033mp_print_char (mp, xord ('['));
34034if (c >= 0)
34035  mp_print_int (mp, c)
34036
34037
34038@ @<End progress report@>=
34039mp_print_char (mp, xord (']'));
34040update_terminal();
34041incr (mp->total_shipped)
34042
34043
34044@ @<Explain what output files were written@>=
34045if (mp->total_shipped > 0) {
34046  mp_print_nl (mp, "");
34047  mp_print_int (mp, mp->total_shipped);
34048  if (mp->noninteractive) {
34049    mp_print (mp, " figure");
34050    if (mp->total_shipped > 1)
34051      mp_print_char (mp, xord ('s'));
34052    mp_print (mp, " created.");
34053  } else {
34054    mp_print (mp, " output file");
34055    if (mp->total_shipped > 1)
34056      mp_print_char (mp, xord ('s'));
34057    mp_print (mp, " written: ");
34058    mp_print (mp, mp->first_file_name);
34059    if (mp->total_shipped > 1) {
34060      if (31 + strlen (mp->first_file_name) +
34061          strlen (mp->last_file_name) > (unsigned) mp->max_print_line)
34062        mp_print_ln (mp);
34063      mp_print (mp, " .. ");
34064      mp_print (mp, mp->last_file_name);
34065    }
34066    mp_print_nl (mp, "");
34067  }
34068}
34069
34070@ @<Internal library declarations@>=
34071boolean mp_has_font_size (MP mp, font_number f);
34072
34073@ @c
34074boolean mp_has_font_size (MP mp, font_number f) {
34075  return (mp->font_sizes[f] != NULL);
34076}
34077
34078
34079@ The \&{special} command saves up lines of text to be printed during the next
34080|ship_out| operation.  The saved items are stored as a list of capsule tokens.
34081
34082@<Glob...@>=
34083mp_node last_pending;   /* the last token in a list of pending specials */
34084
34085
34086@ @<Declare action procedures for use by |do_statement|@>=
34087static void mp_do_special (MP mp);
34088
34089@ @c
34090void mp_do_special (MP mp) {
34091  mp_get_x_next (mp);
34092  mp_scan_expression (mp);
34093  if (mp->cur_exp.type != mp_string_type) {
34094    @<Complain about improper special operation@>;
34095  } else {
34096    mp_link (mp->last_pending) = mp_stash_cur_exp (mp);
34097    mp->last_pending = mp_link (mp->last_pending);
34098    mp_link (mp->last_pending) = NULL;
34099  }
34100}
34101
34102
34103@ @<Complain about improper special operation@>=
34104{
34105  const char *hlp[] = { "Only known strings are allowed for output as specials.", NULL };
34106  mp_disp_err(mp, NULL);
34107  mp_back_error (mp, "Unsuitable expression", hlp, true);
34108  mp_get_x_next (mp);
34109}
34110
34111
34112@ On the export side, we need an extra object type for special strings.
34113
34114@<Graphical object codes@>=
34115mp_special_code = 8,
34116
34117@ @<Export pending specials@>=
34118p = mp_link (mp->spec_head);
34119while (p != NULL) {
34120  mp_special_object *tp;
34121  tp = (mp_special_object *) mp_new_graphic_object (mp, mp_special_code);
34122  gr_pre_script (tp) = mp_xstrdup(mp,mp_str (mp, value_str (p)));
34123  if (hh->body == NULL)
34124    hh->body = (mp_graphic_object *) tp;
34125  else
34126    gr_link (hp) = (mp_graphic_object *) tp;
34127  hp = (mp_graphic_object *) tp;
34128  p = mp_link (p);
34129}
34130mp_flush_token_list (mp, mp_link (mp->spec_head));
34131mp_link (mp->spec_head) = NULL;
34132mp->last_pending = mp->spec_head
34133
34134@ We are now ready for the main output procedure.  Note that the |selector|
34135setting is saved in a global variable so that |begin_diagnostic| can access it.
34136
34137@<Declare the \ps\ output procedures@>=
34138static void mp_ship_out (MP mp, mp_node h);
34139
34140@ Once again, the |gr_XXXX| macros are defined in |mppsout.h|
34141
34142@d export_color(q,p)
34143  if ( mp_color_model(p)==mp_uninitialized_model ) {
34144    gr_color_model(q)  = (unsigned char)(number_to_scaled (internal_value(mp_default_color_model))/65536);
34145    gr_cyan_val(q)     = 0;
34146	gr_magenta_val(q)  = 0;
34147	gr_yellow_val(q)   = 0;
34148	gr_black_val(q)    = ((gr_color_model(q)==mp_cmyk_model ? number_to_scaled (unity_t) : 0) / 65536.0);
34149  } else {
34150    gr_color_model(q)  = (unsigned char)mp_color_model(p);
34151    gr_cyan_val(q)     = number_to_double(p->cyan);
34152    gr_magenta_val(q)  = number_to_double(p->magenta);
34153    gr_yellow_val(q)   = number_to_double(p->yellow);
34154    gr_black_val(q)    = number_to_double(p->black);
34155  }
34156
34157@d export_scripts(q,p)
34158  if (mp_pre_script(p)!=NULL)  gr_pre_script(q)   = mp_xstrdup(mp, mp_str(mp,mp_pre_script(p)));
34159  if (mp_post_script(p)!=NULL) gr_post_script(q)  = mp_xstrdup(mp, mp_str(mp,mp_post_script(p)));
34160
34161@c
34162struct mp_edge_object *mp_gr_export (MP mp, mp_edge_header_node h) {
34163  mp_node p;    /* the current graphical object */
34164  integer t;    /* a temporary value */
34165  integer c;    /* a rounded charcode */
34166  mp_number d_width;       /* the current pen width */
34167  mp_edge_object *hh;   /* the first graphical object */
34168  mp_graphic_object *hq;        /* something |hp| points to  */
34169  mp_text_object *tt;
34170  mp_fill_object *tf;
34171  mp_stroked_object *ts;
34172  mp_clip_object *tc;
34173  mp_bounds_object *tb;
34174  mp_graphic_object *hp = NULL; /* the current graphical object */
34175  mp_set_bbox (mp, h, true);
34176  hh = xmalloc (1, sizeof (mp_edge_object));
34177  hh->body = NULL;
34178  hh->next = NULL;
34179  hh->parent = mp;
34180  hh->minx = number_to_double(h->minx);
34181  hh->minx = (fabs(hh->minx)<0.00001 ? 0 : hh->minx);
34182  hh->miny = number_to_double(h->miny);
34183  hh->miny = (fabs(hh->miny)<0.00001 ? 0 : hh->miny);
34184  hh->maxx = number_to_double(h->maxx);
34185  hh->maxx = (fabs(hh->maxx)<0.00001 ? 0 : hh->maxx);
34186  hh->maxy = number_to_double(h->maxy);
34187  hh->maxy = (fabs(hh->maxy)<0.00001 ? 0 : hh->maxy);
34188  hh->filename = mp_get_output_file_name (mp);
34189  c = round_unscaled (internal_value (mp_char_code));
34190  hh->charcode = c;
34191  hh->width = number_to_double (internal_value (mp_char_wd));
34192  hh->height = number_to_double (internal_value (mp_char_ht));
34193  hh->depth = number_to_double (internal_value (mp_char_dp));
34194  hh->ital_corr = number_to_double (internal_value (mp_char_ic));
34195  @<Export pending specials@>;
34196  p = mp_link (edge_list (h));
34197  while (p != NULL) {
34198    hq = mp_new_graphic_object (mp, (int) ((mp_type (p) - mp_fill_node_type) + 1));
34199    switch (mp_type (p)) {
34200    case mp_fill_node_type:
34201      {
34202      mp_fill_node p0 = (mp_fill_node)p;
34203      tf = (mp_fill_object *) hq;
34204      gr_pen_p (tf) = mp_export_knot_list (mp, mp_pen_p (p0));
34205      new_number (d_width);
34206      mp_get_pen_scale (mp, &d_width, mp_pen_p (p0)); /* whats the point ? */
34207      free_number (d_width);
34208      if ((mp_pen_p (p0) == NULL) || pen_is_elliptical (mp_pen_p (p0))) {
34209        gr_path_p (tf) = mp_export_knot_list (mp, mp_path_p (p0));
34210      } else {
34211        mp_knot pc, pp;
34212        pc = mp_copy_path (mp, mp_path_p (p0));
34213        pp = mp_make_envelope (mp, pc, mp_pen_p (p0), p0->ljoin, 0, p0->miterlim);
34214        gr_path_p (tf) = mp_export_knot_list (mp, pp);
34215        mp_toss_knot_list (mp, pp);
34216        pc = mp_htap_ypoc (mp, mp_path_p (p0));
34217        pp = mp_make_envelope (mp, pc, mp_pen_p ((mp_fill_node) p), p0->ljoin, 0, p0->miterlim);
34218        gr_htap_p (tf) = mp_export_knot_list (mp, pp);
34219        mp_toss_knot_list (mp, pp);
34220      }
34221      export_color (tf, p0);
34222      export_scripts (tf, p);
34223      gr_ljoin_val (tf) = p0->ljoin;
34224      gr_miterlim_val (tf) = number_to_double(p0->miterlim);
34225      }
34226      break;
34227    case mp_stroked_node_type:
34228      {
34229      mp_stroked_node p0 = (mp_stroked_node)p;
34230      ts = (mp_stroked_object *) hq;
34231      gr_pen_p (ts) = mp_export_knot_list (mp, mp_pen_p (p0));
34232      new_number (d_width);
34233      mp_get_pen_scale (mp, &d_width, mp_pen_p (p0));
34234      if (pen_is_elliptical (mp_pen_p (p0))) {
34235        gr_path_p (ts) = mp_export_knot_list (mp, mp_path_p (p0));
34236      } else {
34237        mp_knot pc;
34238        pc = mp_copy_path (mp, mp_path_p (p0));
34239        t = p0->lcap;
34240        if (mp_left_type (pc) != mp_endpoint) {
34241          mp_left_type (mp_insert_knot (mp, pc, pc->x_coord, pc->y_coord)) =  mp_endpoint;
34242          mp_right_type (pc) = mp_endpoint;
34243          pc = mp_next_knot (pc);
34244          t = 1;
34245        }
34246        pc =
34247          mp_make_envelope (mp, pc, mp_pen_p (p0),
34248                            p0->ljoin, (quarterword) t,
34249	                    p0->miterlim);
34250        gr_path_p (ts) = mp_export_knot_list (mp, pc);
34251        mp_toss_knot_list (mp, pc);
34252      }
34253      export_color (ts, p0);
34254      export_scripts (ts, p);
34255      gr_ljoin_val (ts) = p0->ljoin;
34256      gr_miterlim_val (ts) = number_to_double(p0->miterlim);
34257      gr_lcap_val (ts) = p0->lcap;
34258      gr_dash_p (ts) = mp_export_dashes (mp, p0, d_width);
34259      free_number (d_width);
34260      }
34261      break;
34262    case mp_text_node_type:
34263      {
34264      mp_text_node p0 = (mp_text_node)p;
34265      tt = (mp_text_object *) hq;
34266      gr_text_p (tt) = mp_xstrldup (mp, mp_str (mp, mp_text_p (p)),mp_text_p (p)->len);
34267      gr_text_l (tt) = (size_t) mp_text_p (p)->len;
34268      gr_font_n (tt) = (unsigned int) mp_font_n (p);
34269      gr_font_name (tt) = mp_xstrdup (mp, mp->font_name[mp_font_n (p)]);
34270      gr_font_dsize (tt) = mp->font_dsize[mp_font_n (p)] / 65536.0;
34271      export_color (tt, p0);
34272      export_scripts (tt, p);
34273      gr_width_val (tt) = number_to_double(p0->width);
34274      gr_height_val (tt) = number_to_double(p0->height);
34275      gr_depth_val (tt) = number_to_double(p0->depth);
34276      gr_tx_val (tt)  = number_to_double(p0->tx);
34277      gr_ty_val (tt)  = number_to_double(p0->ty);
34278      gr_txx_val (tt) = number_to_double(p0->txx);
34279      gr_txy_val (tt) = number_to_double(p0->txy);
34280      gr_tyx_val (tt) = number_to_double(p0->tyx);
34281      gr_tyy_val (tt) = number_to_double(p0->tyy);
34282      }
34283      break;
34284    case mp_start_clip_node_type:
34285      tc = (mp_clip_object *) hq;
34286      gr_path_p (tc) =
34287        mp_export_knot_list (mp, mp_path_p ((mp_start_clip_node) p));
34288      break;
34289    case mp_start_bounds_node_type:
34290      tb = (mp_bounds_object *) hq;
34291      gr_path_p (tb) =
34292        mp_export_knot_list (mp, mp_path_p ((mp_start_bounds_node) p));
34293      break;
34294    case mp_stop_clip_node_type:
34295    case mp_stop_bounds_node_type:
34296      /* nothing to do here */
34297      break;
34298    default:                   /* there are no other valid cases, but please the compiler */
34299      break;
34300    }
34301    if (hh->body == NULL)
34302      hh->body = hq;
34303    else
34304      gr_link (hp) = hq;
34305    hp = hq;
34306    p = mp_link (p);
34307  }
34308  return hh;
34309}
34310
34311
34312@ This function is only used for the |glyph| operator, so
34313it takes quite a few shortcuts for cases that cannot appear
34314in the output of |mp_ps_font_charstring|.
34315
34316@c
34317mp_edge_header_node mp_gr_import (MP mp, struct mp_edge_object *hh) {
34318  mp_edge_header_node h;    /* the edge object */
34319  mp_node ph, pn, pt;   /* for adding items */
34320  mp_graphic_object *p; /* the current graphical object */
34321  h = mp_get_edge_header_node (mp);
34322  mp_init_edges (mp, h);
34323  ph = edge_list (h);
34324  pt = ph;
34325  p = hh->body;
34326  set_number_from_double(h->minx, hh->minx);
34327  set_number_from_double(h->miny, hh->miny);
34328  set_number_from_double(h->maxx, hh->maxx);
34329  set_number_from_double(h->maxy, hh->maxy);
34330  while (p != NULL) {
34331    switch (gr_type (p)) {
34332    case mp_fill_code:
34333      if (gr_pen_p ((mp_fill_object *) p) == NULL) {
34334        mp_number turns;
34335        new_number (turns);
34336        pn = mp_new_fill_node (mp, NULL);
34337        mp_path_p ((mp_fill_node) pn) =
34338          mp_import_knot_list (mp, gr_path_p ((mp_fill_object *) p));
34339        mp_color_model (pn) = mp_grey_model;
34340        mp_turn_cycles (mp, &turns, mp_path_p ((mp_fill_node) pn));
34341        if (number_negative(turns)) {
34342          set_number_to_unity(((mp_fill_node) pn)->grey);
34343          mp_link (pt) = pn;
34344          pt = mp_link (pt);
34345        } else {
34346          set_number_to_zero(((mp_fill_node) pn)->grey);
34347          mp_link (pn) = mp_link (ph);
34348          mp_link (ph) = pn;
34349          if (ph == pt)
34350            pt = pn;
34351        }
34352        free_number (turns);
34353      }
34354      break;
34355    case mp_stroked_code:
34356    case mp_text_code:
34357    case mp_start_clip_code:
34358    case mp_stop_clip_code:
34359    case mp_start_bounds_code:
34360    case mp_stop_bounds_code:
34361    case mp_special_code:
34362      break;
34363    }                           /* all cases are enumerated */
34364    p = p->next;
34365  }
34366  mp_gr_toss_objects (hh);
34367  return h;
34368}
34369
34370
34371@ @<Declarations@>=
34372struct mp_edge_object *mp_gr_export (MP mp, mp_edge_header_node h);
34373mp_edge_header_node mp_gr_import (MP mp, struct mp_edge_object *h);
34374
34375@ This function is now nearly trivial.
34376
34377@c
34378void mp_ship_out (MP mp, mp_node h) {                               /* output edge structure |h| */
34379  int c;    /* \&{charcode} rounded to the nearest integer */
34380  c = round_unscaled (internal_value (mp_char_code));
34381  @<Begin the progress report for the output of picture~|c|@>;
34382  (mp->shipout_backend) (mp, h);
34383  @<End progress report@>;
34384  if (number_positive (internal_value (mp_tracing_output)))
34385    mp_print_edges (mp, h, " (just shipped out)", true);
34386}
34387
34388
34389@ @<Declarations@>=
34390static void mp_shipout_backend (MP mp, void *h);
34391
34392@
34393@c
34394void mp_shipout_backend (MP mp, void *voidh) {
34395  char *s;
34396  mp_edge_object *hh;   /* the first graphical object */
34397  mp_edge_header_node h = (mp_edge_header_node) voidh;
34398  hh = mp_gr_export (mp, h);
34399  s = NULL;
34400  if (internal_string (mp_output_format) != NULL)
34401    s = mp_str (mp, internal_string (mp_output_format));
34402  if (s && strcmp (s, "svg") == 0) {
34403    (void) mp_svg_gr_ship_out (hh,
34404                               (number_to_scaled (internal_value (mp_prologues)) / 65536), false);
34405  } else if (s && strcmp (s, "png") == 0) {
34406    (void) mp_png_gr_ship_out (hh, (const char *)((internal_string (mp_output_format_options))->str), false);
34407  } else {
34408    (void) mp_gr_ship_out (hh,
34409                           (number_to_scaled (internal_value (mp_prologues)) / 65536),
34410                           (number_to_scaled (internal_value (mp_procset)) / 65536), false);
34411  }
34412  mp_gr_toss_objects (hh);
34413}
34414
34415
34416@ @<Exported types@>=
34417typedef void (*mp_backend_writer) (MP, void *);
34418
34419@ @<Option variables@>=
34420mp_backend_writer shipout_backend;
34421
34422@ Now that we've finished |ship_out|, let's look at the other commands
34423by which a user can send things to the \.{GF} file.
34424
34425@ @<Glob...@>=
34426psout_data ps;
34427svgout_data svg;
34428pngout_data png;
34429
34430@ @<Allocate or initialize ...@>=
34431mp_ps_backend_initialize (mp);
34432mp_svg_backend_initialize (mp);
34433mp_png_backend_initialize (mp);
34434
34435@ @<Dealloc...@>=
34436mp_ps_backend_free (mp);
34437mp_svg_backend_free (mp);
34438mp_png_backend_free (mp);
34439
34440
34441@* Dumping and undumping the tables.
34442
34443When \.{MP} is started, it is possible to preload a macro file
34444containing definitions that will be usable in the main input
34445file. This action even takes place automatically, based on the
34446name of the executable (\.{mpost} will attempt to preload the
34447macros in the file \.{mpost.mp}). If such a preload is not
34448desired, the option variable |ini_version| has to be set |true|.
34449
34450The variable |mem_file| holds the open file pointer.
34451
34452@<Glob...@>=
34453void *mem_file; /* file for input or preloaded macros */
34454
34455@ @<Declarations@>=
34456extern boolean mp_load_preload_file (MP mp);
34457
34458@ Preloading a file is a lot like |mp_run| itself, except that
34459\MP\ should not exit and that a bit of trickery is needed with
34460the input buffer to make sure that the preloading does not
34461interfere with the actual job.
34462
34463@c
34464boolean mp_load_preload_file (MP mp) {
34465  size_t k;
34466  in_state_record old_state;
34467  integer old_in_open = mp->in_open;
34468  void *old_cur_file = cur_file;
34469  char *fname = xstrdup (mp->name_of_file);
34470  size_t l = strlen (fname);
34471  old_state = mp->cur_input;
34472  str_room (l);
34473  for (k = 0; k < l; k++) {
34474    append_char (*(fname + k));
34475  }
34476  name = mp_make_string (mp);
34477  if (!mp->log_opened) {
34478    mp_open_log_file (mp);
34479  }                             /* |open_log_file| doesn't |show_context|, so |limit|
34480                                   and |loc| needn't be set to meaningful values yet */
34481  if (((int) mp->term_offset + (int) strlen (fname)) > (mp->max_print_line - 2))
34482    mp_print_ln (mp);
34483  else if ((mp->term_offset > 0) || (mp->file_offset > 0))
34484    mp_print_char (mp, xord (' '));
34485  mp_print_char (mp, xord ('('));
34486  incr (mp->open_parens);
34487  mp_print (mp, fname);
34488  update_terminal();
34489  {
34490    line = 1;
34491    start = loc = limit + (mp->noninteractive ? 0 : 1);
34492    cur_file = mp->mem_file;
34493    (void) mp_input_ln (mp, cur_file);
34494    mp_firm_up_the_line (mp);
34495    mp->buffer[limit] = xord ('%');
34496    mp->first = (size_t) (limit + 1);
34497    loc = start;
34498  }
34499  mp->reading_preload = true;
34500  do {
34501    mp_do_statement (mp);
34502  } while (!(cur_cmd() == mp_stop));     /* "dump" or EOF */
34503  mp->reading_preload = false;
34504  mp_primitive (mp, "dump", mp_relax, 0); /* reset |dump| */
34505  while (mp->input_ptr > 0) {
34506    if (token_state)
34507      mp_end_token_list (mp);
34508    else
34509      mp_end_file_reading (mp);
34510  }
34511  while (mp->loop_ptr != NULL)
34512    mp_stop_iteration (mp);
34513  while (mp->open_parens > 0) {
34514    mp_print (mp, " )");
34515    decr (mp->open_parens);
34516  };
34517  while (mp->cond_ptr != NULL) {
34518    mp_print_nl (mp, "(dump occurred when ");
34519@.dump occurred...@>;
34520    mp_print_cmd_mod (mp, mp_fi_or_else, mp->cur_if);
34521    /* `\.{if}' or `\.{elseif}' or `\.{else}' */
34522    if (mp->if_line != 0) {
34523      mp_print (mp, " on line ");
34524      mp_print_int (mp, mp->if_line);
34525    }
34526    mp_print (mp, " was incomplete)");
34527    mp->if_line = if_line_field (mp->cond_ptr);
34528    mp->cur_if = mp_name_type (mp->cond_ptr);
34529    mp->cond_ptr = mp_link (mp->cond_ptr);
34530  }
34531
34532/*  |(mp->close_file) (mp, mp->mem_file);| */
34533  cur_file = old_cur_file;
34534  mp->cur_input  = old_state;
34535  mp->in_open = old_in_open;
34536  return true;
34537}
34538
34539
34540@* The main program.
34541This is it: the part of \MP\ that executes all those procedures we have
34542written.
34543
34544Well---almost. We haven't put the parsing subroutines into the
34545program yet; and we'd better leave space for a few more routines that may
34546have been forgotten.
34547
34548@c
34549@<Declare the basic parsing subroutines@>;
34550@<Declare miscellaneous procedures that were declared |forward|@>
34551
34552
34553@ Here we do whatever is needed to complete \MP's job gracefully on the
34554local operating system. The code here might come into play after a fatal
34555error; it must therefore consist entirely of ``safe'' operations that
34556cannot produce error messages. For example, it would be a mistake to call
34557|str_room| or |make_string| at this time, because a call on |overflow|
34558might lead to an infinite loop.
34559@^system dependencies@>
34560
34561@ @c
34562void mp_close_files_and_terminate (MP mp) {
34563  integer k;    /* all-purpose index */
34564  integer LH;   /* the length of the \.{TFM} header, in words */
34565  int lk_offset;        /* extra words inserted at beginning of |lig_kern| array */
34566  mp_node p;    /* runs through a list of \.{TFM} dimensions */
34567  if (mp->finished)
34568    return;
34569  @<Close all open files in the |rd_file| and |wr_file| arrays@>;
34570  if (number_positive (internal_value (mp_tracing_stats)))
34571    @<Output statistics about this job@>;
34572  wake_up_terminal();
34573  @<Do all the finishing work on the \.{TFM} file@>;
34574  @<Explain what output files were written@>;
34575  if (mp->log_opened && !mp->noninteractive) {
34576    wlog_cr;
34577    (mp->close_file) (mp, mp->log_file);
34578    mp->selector = mp->selector - 2;
34579    if (mp->selector == term_only) {
34580      mp_print_nl (mp, "Transcript written on ");
34581@.Transcript written...@>;
34582      mp_print (mp, mp->log_name);
34583      mp_print_char (mp, xord ('.'));
34584    }
34585  }
34586  mp_print_ln (mp);
34587  mp->finished = true;
34588}
34589
34590
34591@ @<Declarations@>=
34592static void mp_close_files_and_terminate (MP mp);
34593
34594@ @<Close all open files in the |rd_file| and |wr_file| arrays@>=
34595if (mp->rd_fname != NULL) {
34596  for (k = 0; k < (int) mp->read_files; k++) {
34597    if (mp->rd_fname[k] != NULL) {
34598      (mp->close_file) (mp, mp->rd_file[k]);
34599      xfree (mp->rd_fname[k]);
34600    }
34601  }
34602}
34603if (mp->wr_fname != NULL) {
34604  for (k = 0; k < (int) mp->write_files; k++) {
34605    if (mp->wr_fname[k] != NULL) {
34606      (mp->close_file) (mp, mp->wr_file[k]);
34607      xfree (mp->wr_fname[k]);
34608    }
34609  }
34610}
34611
34612@ @<Dealloc ...@>=
34613for (k = 0; k < (int) mp->max_read_files; k++) {
34614  if (mp->rd_fname[k] != NULL) {
34615    (mp->close_file) (mp, mp->rd_file[k]);
34616    xfree (mp->rd_fname[k]);
34617  }
34618}
34619xfree (mp->rd_file);
34620xfree (mp->rd_fname);
34621for (k = 0; k < (int) mp->max_write_files; k++) {
34622  if (mp->wr_fname[k] != NULL) {
34623    (mp->close_file) (mp, mp->wr_file[k]);
34624    xfree (mp->wr_fname[k]);
34625  }
34626}
34627xfree (mp->wr_file);
34628xfree (mp->wr_fname);
34629
34630
34631@ We want to produce a \.{TFM} file if and only if |mp_fontmaking| is positive.
34632
34633We reclaim all of the variable-size memory at this point, so that
34634there is no chance of another memory overflow after the memory capacity
34635has already been exceeded.
34636
34637@<Do all the finishing work on the \.{TFM} file@>=
34638if (number_positive (internal_value (mp_fontmaking))) {
34639  @<Massage the \.{TFM} widths@>;
34640  mp_fix_design_size (mp);
34641  mp_fix_check_sum (mp);
34642  @<Massage the \.{TFM} heights, depths, and italic corrections@>;
34643  set_number_to_zero (internal_value (mp_fontmaking));   /* avoid loop in case of fatal error */
34644  @<Finish the \.{TFM} file@>;
34645}
34646
34647@ The present section goes directly to the log file instead of using
34648|print| commands, because there's no need for these strings to take
34649up |str_pool| memory when a non-{\bf stat} version of \MP\ is being used.
34650
34651@<Output statistics...@>=
34652if (mp->log_opened) {
34653  char s[128];
34654  wlog_ln (" ");
34655  wlog_ln ("Here is how much of MetaPost's memory you used:");
34656@.Here is how much...@>;
34657  mp_snprintf (s, 128, " %i string%s using %i character%s",
34658               (int) mp->max_strs_used, (mp->max_strs_used != 1 ? "s" : ""),
34659               (int) mp->max_pl_used, (mp->max_pl_used != 1 ? "s" : ""));
34660  wlog_ln (s);
34661  mp_snprintf (s, 128, " %i bytes of node memory", (int) mp->var_used_max);
34662  wlog_ln (s);
34663  mp_snprintf (s, 128, " %i symbolic tokens", (int) mp->st_count);
34664  wlog_ln (s);
34665  mp_snprintf (s, 128,
34666               " %ii,%in,%ip,%ib,%if stack positions out of %ii,%in,%ip,%ib,%if",
34667               (int) mp->max_in_stack, (int) mp->int_ptr,
34668               (int) mp->max_param_stack, (int) mp->max_buf_stack + 1,
34669               (int) mp->in_open_max - file_bottom, (int) mp->stack_size,
34670               (int) mp->max_internal, (int) mp->param_size, (int) mp->buf_size,
34671               (int) mp->max_in_open - file_bottom);
34672  wlog_ln (s);
34673}
34674
34675@ It is nice to have have some of the stats available from the API.
34676
34677@<Exported function ...@>=
34678int mp_memory_usage (MP mp);
34679int mp_hash_usage (MP mp);
34680int mp_param_usage (MP mp);
34681int mp_open_usage (MP mp);
34682
34683@ @c
34684int mp_memory_usage (MP mp) {
34685  return (int) mp->var_used;
34686}
34687int mp_hash_usage (MP mp) {
34688  return (int) mp->st_count;
34689}
34690int mp_param_usage (MP mp) {
34691  return (int) mp->max_param_stack;
34692}
34693int mp_open_usage (MP mp) {
34694  return (int) mp->max_in_stack;
34695}
34696
34697
34698@ We get to the |final_cleanup| routine when \&{end} or \&{dump} has
34699been scanned.
34700
34701@c
34702void mp_final_cleanup (MP mp) {
34703  /* -Wunused: integer c; */   /* 0 for \&{end}, 1 for \&{dump} */
34704  /* clang: never read: |c = cur_mod();| */
34705  if (mp->job_name == NULL)
34706    mp_open_log_file (mp);
34707  while (mp->input_ptr > 0) {
34708    if (token_state)
34709      mp_end_token_list (mp);
34710    else
34711      mp_end_file_reading (mp);
34712  }
34713  while (mp->loop_ptr != NULL)
34714    mp_stop_iteration (mp);
34715  while (mp->open_parens > 0) {
34716    mp_print (mp, " )");
34717    decr (mp->open_parens);
34718  };
34719  while (mp->cond_ptr != NULL) {
34720    mp_print_nl (mp, "(end occurred when ");
34721@.end occurred...@>;
34722    mp_print_cmd_mod (mp, mp_fi_or_else, mp->cur_if);
34723    /* `\.{if}' or `\.{elseif}' or `\.{else}' */
34724    if (mp->if_line != 0) {
34725      mp_print (mp, " on line ");
34726      mp_print_int (mp, mp->if_line);
34727    }
34728    mp_print (mp, " was incomplete)");
34729    mp->if_line = if_line_field (mp->cond_ptr);
34730    mp->cur_if = mp_name_type (mp->cond_ptr);
34731    mp->cond_ptr = mp_link (mp->cond_ptr);
34732  }
34733  if (mp->history != mp_spotless)
34734    if (((mp->history == mp_warning_issued)
34735         || (mp->interaction < mp_error_stop_mode)))
34736      if (mp->selector == term_and_log) {
34737        mp->selector = term_only;
34738        mp_print_nl (mp,
34739                     "(see the transcript file for additional information)");
34740@.see the transcript file...@>;
34741        mp->selector = term_and_log;
34742      }
34743}
34744
34745
34746@ @<Declarations@>=
34747static void mp_final_cleanup (MP mp);
34748static void mp_init_prim (MP mp);
34749static void mp_init_tab (MP mp);
34750
34751@ @c
34752void mp_init_prim (MP mp) {                               /* initialize all the primitives */
34753  @<Put each...@>;
34754}
34755@#
34756void mp_init_tab (MP mp) {                               /* initialize other tables */
34757  @<Initialize table entries@>;
34758}
34759
34760
34761@ When we begin the following code, \MP's tables may still contain garbage;
34762thus we must proceed cautiously to get bootstrapped in.
34763
34764But when we finish this part of the program, \MP\ is ready to call on the
34765|main_control| routine to do its work.
34766
34767@<Get the first line...@>=
34768{
34769  @<Initialize the input routines@>;
34770  if (!mp->ini_version) {
34771    if (!mp_load_preload_file (mp)) {
34772      mp->history = mp_fatal_error_stop;
34773      return mp;
34774    }
34775  }
34776  @<Initializations following first line@>;
34777}
34778
34779
34780@ @<Initializations following first line@>=
34781mp->buffer[limit] = (ASCII_code) '%';
34782mp_fix_date_and_time (mp);
34783if (mp->random_seed == 0)
34784  mp->random_seed = (number_to_scaled (internal_value (mp_time)) / number_to_scaled (unity_t)) + number_to_scaled (internal_value (mp_day));
34785init_randoms (mp->random_seed);
34786initialize_print_selector();
34787mp_normalize_selector (mp);
34788if (loc < limit)
34789  if (mp->buffer[loc] != '\\')
34790    mp_start_input (mp);        /* \&{input} assumed */
34791
34792@* Debugging.
34793
34794
34795@* System-dependent changes.
34796This section should be replaced, if necessary, by any special
34797modification of the program
34798that are necessary to make \MP\ work at a particular installation.
34799It is usually best to design your change file so that all changes to
34800previous sections preserve the section numbering; then everybody's version
34801will be consistent with the published program. More extensive changes,
34802which introduce new sections, can be inserted here; then only the index
34803itself will get a new section number.
34804@^system dependencies@>
34805
34806@* Index.
34807Here is where you can find all uses of each identifier in the program,
34808with underlined entries pointing to where the identifier was defined.
34809If the identifier is only one letter long, however, you get to see only
34810the underlined entries. {\sl All references are to section numbers instead of
34811page numbers.}
34812
34813This index also lists error messages and other aspects of the program
34814that you might want to look up some day. For example, the entry
34815for ``system dependencies'' lists all sections that should receive
34816special attention from people who are installing \MP\ in a new
34817operating environment. A list of various things that can't happen appears
34818under ``this can't happen''.
34819Approximately 25 sections are listed under ``inner loop''; these account
34820for more than 60\pct! of \MP's running time, exclusive of input and output.
34821