home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 22 gnu / 22-gnu.zip / fweb153.zip / fweb-1.53 / web / common.web < prev    next >
Text File  |  1995-09-23  |  200KB  |  8,086 lines

  1. @z --- common.web ---
  2.  
  3. FWEB version 1.53 (September 23, 1995)
  4.  
  5. Based on version 0.5 of S. Levy's CWEB [copyright (C) 1987 Princeton University]
  6.  
  7. @x-----------------------------------------------------------------------------
  8.  
  9.  
  10. \Title{COMMON}
  11.  
  12. @c
  13.  
  14. @* INTRODUCTION.
  15. This file contains code common to both \.{TANGLE} and
  16. \.{WEAVE}, that roughly concerns the following problems: character
  17. uniformity, input routines, error handling and parsing of command line.  We
  18. have tried to concentrate in this file all the system dependencies, so as
  19. to maximize portability.
  20.  
  21. \FWEB\ has been written to work with a variety of C~compilers, not
  22. necessarily ANSI. In particular, the special macro tokens~'\.\#'
  23. and~'\.{\#\#}' are not used in any \.{@@d} commands; when such
  24. constructions have been needed, the \WEB\ macro definition~\.{@@m} has been
  25. used. The |enum| feature has been assumed to exist. \CWEB\ did not use
  26. |enum|, so there's a fair amount of inherited \CWEB\ code that should
  27. eventually be changed to |enum|, which makes debugging much easier. This
  28. will be done in future versions. One should study the header files
  29. \.{os.hweb}, \.{proto.hweb}, \.{includes.hweb}, and \.{custom.web} for
  30. further information about machine dependencies.
  31.  
  32. In the texts below we will sometimes use \.{WEB} to refer to either of the
  33. two component programs, if no confusion can arise.
  34.  
  35. @m _COMMON_
  36. @d _COMMON_h
  37. @d COMMON_FCNS_
  38.  
  39. @ Here is the overall appearance of this file:
  40.  
  41. @d VERSION "1.53"
  42. @d RELEASE "September 23, 1995"
  43.  
  44. @A
  45. @<Possibly split into parts@>@;
  46.  
  47. @<Include files@>@;
  48. @<Common code for \.{TANGLE} and \.{WEAVE}@>@;
  49. @<Typedef declarations@>@;
  50. @<Other definitions@>@;
  51. @<Global variables@>@;
  52.  
  53. /* For pc's, the file is split into two compilable parts using the
  54. compiler-line macro |part|, which must equal either~1 or~2. */
  55. #if(part != 2)
  56.     @<Part 1@>@;
  57. #endif /* Part 1 */
  58.  
  59. #if(part != 1)
  60.     @<Part 2@>@;
  61. #endif /* Part 2 */
  62.  
  63.  
  64. @I typedefs.hweb
  65.  
  66. @I xrefs.hweb
  67. @I tokens.hweb
  68. @I scraps.hweb
  69. @I stacks.hweb
  70.  
  71. @
  72. @<Include...@>=
  73. #include "map.h"
  74.  
  75. @ A dummy module for \.{scraps.hweb}.
  76.  
  77. @<Rest of |trans_plus| union@>=
  78.  
  79. @ For personal computers, we sometimes need to initialize the stack size.
  80.  
  81. @d STKLEN 20000U /* Borland needs this number; Microsoft requires it from
  82.             the compiler line. */
  83.  
  84. @<Common...@>=
  85.  
  86. #ifdef ibmpc /* \.{Machine-dependent}: For initializing the stack size. */
  87. #ifdef borland
  88.     extern unsigned _stklen = STKLEN;
  89. #endif
  90. #endif
  91.  
  92.  
  93. @* The CHARACTER SET.
  94. One of the main goals in the design of \.{WEB} has been to make it readily
  95. portable between a wide variety of computers. Yet \.{WEB} by its very
  96. nature must use a greater variety of characters than most computer
  97. programs deal with, and character encoding is one of the areas in which
  98. existing machines differ most widely from each other.
  99.  
  100. To resolve this problem, all input to \.{WEAVE} and \.{TANGLE} is converted
  101. to an internal seven-bit code that is essentially standard ASCII, the
  102. ``American Standard Code for Information Interchange.''  The conversion
  103. is done immediately when each character is read in. Conversely,
  104. characters are converted from ASCII to the user's external
  105. representation just before they are output.
  106.  
  107. Such an internal code can be accessed by users of \.{WEB} by means of
  108. constructions like \.{@@'A'}, which should be distinguished from \.{'A'}.
  109. The former is transformed by \.{TANGLE} into an integer that is the
  110. internal code of~\.A, but the latter, a |char| constant, is not touched by
  111. \.{WEB}, and will be interpreted by the compiler according to the machine's
  112. character set. 
  113. @^ASCII code@>
  114.  
  115. Here is a table of the standard visible ASCII codes (\.{\ } stands for
  116. a blank space):
  117. $$\def\:{\char\count255\global\advance\count255 by 1}
  118. \count255='40
  119. \vbox{
  120. \hbox{\hbox to 40pt{\it\hfill0\/\hfill}%
  121. \hbox to 40pt{\it\hfill1\/\hfill}%
  122. \hbox to 40pt{\it\hfill2\/\hfill}%
  123. \hbox to 40pt{\it\hfill3\/\hfill}%
  124. \hbox to 40pt{\it\hfill4\/\hfill}%
  125. \hbox to 40pt{\it\hfill5\/\hfill}%
  126. \hbox to 40pt{\it\hfill6\/\hfill}%
  127. \hbox to 40pt{\it\hfill7\/\hfill}}
  128. \vskip 4pt
  129. \hrule
  130. \def\^{\vrule height 10.5pt depth 4.5pt}
  131. \halign{\hbox to 0pt{\hskip -24pt\WO{\~#}\hfill}&\^
  132. \hbox to 40pt{\tt\hfill#\hfill\^}&
  133. &\hbox to 40pt{\tt\hfill#\hfill\^}\cr
  134. 04&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
  135. 05&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
  136. 06&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
  137. 07&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
  138. 10&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
  139. 11&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
  140. 12&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
  141. 13&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
  142. 14&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
  143. 15&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
  144. 16&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
  145. 17&\:&\:&\:&\:&\:&\:&\:\cr}
  146. \hrule width 280pt}$$
  147.  
  148. We introduce new types to distinguish between the transliterated characters
  149. and the characters in the outside world.  Let all ``interesting'' values
  150. that a |char| variable may take lie between |first_text_char| and
  151. |last_text_char|; for the ASCII code we can take |first_text_char=0| and
  152. |last_text_char=0177|. We will tell \.{WEB} to convert all input characters
  153. in this range to its own code, and balk at characters outside the range.
  154. We make two assumptions: |first_text_char>=0| and |char| has room for at
  155. least eight bits.
  156.  
  157. Following \CWEB, \FWEB\ deals with character constants by prefacing them
  158. with '\.{@@}' if they are to be interpreted as |ASCII|. In \CWEB, \TANGLE\
  159. translated such constants into octal, which makes the output hard to read
  160. while debugging. While this mechanism is necessary for a non-ASCII machine,
  161. it is redundant for an ASCII machine. Thus, for the latter by default
  162. \FWEB\ does not translate |ASCII| character constants. To force it to do
  163. so, use the `\.{-a}' command-line option. For non-ASCII machines, the
  164. translation will always be done.
  165.  
  166.  
  167. {\bf WARNING:} \FWEB\ has been tested only on an ASCII machine. It is 
  168. 100\% likely that some debugging will be necessary for any other kind of
  169. machine; the $|outer_char| \Leftrightarrow |ASCII|$ conversions are only
  170. about 75\% in place at this point. (Even this is a substantial improvement
  171. over \CWEB.)
  172.  
  173. @^system dependencies@>
  174.  
  175. @d first_text_char 0 // Lowest interesting value of an |outer_char|.
  176. @d last_text_char 0377 // Highest interesting value of an |outer_char|.
  177.  
  178. @ The \.{WEAVE} and \.{TANGLE} processors convert between ASCII code and
  179. the user's external character set by means of arrays~|xord| and~|xchr|
  180. that are analogous to PASCAL's |ord| and |chr| functions. Thus,
  181. $\hbox{internal\_WEB\_chr} = |xord[@texternal\_chr@>]|$ and
  182. $\hbox{external\_chr} = |xchr[@tinternal\_WEB\_chr@>]|$.
  183.  
  184. @<Common...@>=
  185.  
  186. IN_COMMON ASCII xord[last_text_char+1]; // Converts input chars.\ to |ASCII|.
  187. #ifdef scramble_ASCII
  188.     ASCII xxord[last_text_char+1];  // For scrambling.
  189. #endif
  190. IN_COMMON outer_char xchr[0200]; // Converts output chars.\ from |ASCII|. 
  191.  
  192. @ Every system supporting \cee\ must be able to read and write the
  193. 95~visible characters of standard ASCII above (although not necessarily
  194. using the ASCII codes to represent them).  Conversely, these characters,
  195. plus the newline, are sufficient to write any \cee\ program.  Other
  196. characters are desirable mainly in strings, and they can be referred to by
  197. means of escape sequences like~|'\t'|.
  198.  
  199. The basic implementation of \.{WEB}, then, only has to assign an~|xord| to
  200. these 95 characters (newlines are swallowed by the reading 
  201. routines).  The easiest way to do this is to assign the characters to
  202. their positions in~|xchr| and then invert the correspondence:
  203.  
  204. @<Part 1@>=@[
  205.  
  206. SRTN 
  207. common_init(VOID)
  208. {
  209. IN_RATFOR SRTN is_Rat_present PROTO((VOID));
  210. struct tm *t = the_localtime(); // Initialize the time.
  211.  
  212. #if(TRANSLATE_ASCII || DEBUG_XCHR)
  213.     translate_ASCII = YES;  // Force it for the cases that really need it.
  214. #endif
  215.  
  216.   banner(); // Start-up banner.
  217.  
  218.   is_Rat_present(); // Find out if the \Ratfor\ package was linked on.
  219.  
  220.   @<Set up character set translations@>@;
  221.   @<Initialize \TeX\ categories@>;
  222.   @<Initialize static pointers@>;
  223.   predefine_macros(); /* Must be done before parsing the command line, so
  224. the user can undefine or override stuff if desired. */
  225.   @<Initialize flags@>@;
  226.  
  227.   @<Scan arguments and open output files@>;
  228.  
  229. /* The next two have to be done after reading the command line. */
  230.   @<Allocate dynamic memory@>@; 
  231.   @<Initialize dynamic pointers@>; 
  232.   @<Execute deferred command-line options@>@;
  233.  
  234. more_includes(wt_style.Idir);
  235.  
  236. if(found_web)
  237.     read_aux(); // Process the \.{.aux} file.
  238. }
  239.  
  240. @
  241. @<Set up character set...@>=
  242. {
  243.   STRNCPY(xchr,"                                 !\"#$%&'()*+,-./0123456789\
  244. :;<=>?@@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ",
  245.     sizeof(xchr));
  246.  
  247. /* Interesting ``invisible'' things to output. */
  248.   xchr[tab_mark] = '\t';
  249.   xchr[@'\n'] = '\n';
  250.  
  251.   @<System-dependent parts of character set@>;
  252.   @<Invert |xchr| to get |xord|@>;
  253. }
  254.  
  255. @ The following system-independent code makes the |xord| array contain
  256. a suitable inverse to the information in |xchr|.
  257.  
  258. @<Invert |xchr|...@>= 
  259. @{
  260.   int i; /* to invert the correspondence */
  261.  
  262. @b
  263. /* Initialize to |ASCII| blanks. */
  264.   for (i=first_text_char; i<=last_text_char; i++) 
  265.     {
  266.     xord[i] = @' ';
  267. #ifdef scramble_ASCII
  268.     xxord[i] = @' ';
  269. #endif
  270.     }
  271.  
  272. /* The |NUL| and |DEL| characters are left alone! */
  273.   for (i=1; i<0177; i++) 
  274.     {
  275.     xord[(eight_bits)XCHR_[i]] = (ASCII)i;
  276. #ifdef scramble_ASCII
  277.     xxord[(eight_bits)wt_style.xchr[i]] = i;
  278. #endif
  279.     }
  280.  
  281. #ifdef scramble_ASCII
  282.   for(i=0177; i<=0377; i++)
  283.     xxord[(eight_bits)wt_style.xchr[i]] = i;
  284. #endif
  285.  
  286. #if(DEBUG_XCHR)
  287.   for(i=0177; i<=0377; i++)
  288.     xord[(eight_bits)XCHR_[i]] = (ASCII)i;
  289. #endif
  290. }
  291.  
  292. @ Now follow routines that translate |outer_char| strings into |ASCII|.
  293. First, to avoid messing with possibly read-only memory, we have a routine
  294. that allocates new storage for the string.  (Thanks to Thorsten Ohl for
  295. this one.) 
  296.  
  297. @<Part 1@>=@[
  298.  
  299. ASCII HUGE *
  300. x__to_ASCII FCN((p0))
  301.     CONST outer_char HUGE *p0 C1("Character string to translate.")@;
  302. {
  303.   if(translate_ASCII)
  304.     {
  305.       ASCII HUGE *buffer, HUGE *p;
  306.       CONST outer_char HUGE *q;
  307.  
  308.       buffer = GET_MEM("x__to_ASCII arg",STRLEN(p0)+1,ASCII);
  309.  
  310.       for (p = buffer, q = p0; *q; p++, q++)
  311.         *p = XORD(*q);
  312.       *p = '\0';
  313.  
  314.       return buffer;
  315.     }
  316.   else
  317.     return (ASCII HUGE *)p0;
  318. }
  319.  
  320. @ For dealing with arrays, we have to |realloc| the space.  (Again, thanks
  321. to Thorsten Ohl.) 
  322.  
  323. @<Part 1@>=@[
  324.  
  325. ASCII HUGE *
  326. x_to_ASCII FCN((p0))
  327.     CONST outer_char HUGE *p0 C1("Character string to translate.")@;
  328. {
  329.   static size_t bufsiz = 0;
  330.   static ASCII HUGE *buffer = NULL;
  331.  
  332.   if(translate_ASCII)
  333.     {
  334.       ASCII HUGE *p;
  335.       CONST outer_char HUGE *q;
  336.  
  337.       while (bufsiz <= STRLEN (p0) + 1)
  338.         {
  339.       if(buffer == NULL) 
  340.         buffer = GET_MEM("x_to_ASCII_buf",bufsiz=32,ASCII);
  341. /* Some systems don't like a |NULL| argument to |realloc|. */
  342.           else 
  343.         {
  344.         buffer = (ASCII HUGE *) REALLOC(buffer, bufsiz+32, bufsiz);
  345.         bufsiz += 32;
  346.         }
  347.  
  348.           if (buffer == NULL)
  349.             FATAL(C, "!! No more memory (x_to_ASCII)", "");
  350.         }
  351.  
  352.       for (p = buffer, q = p0; *q; p++, q++)
  353.         *p = XORD(*q);
  354.       *p = '\0';
  355.  
  356.       return buffer;
  357.     }
  358.   else
  359.     return (ASCII HUGE *)p0;
  360. }
  361.  
  362. @ Finally, this function converts in place.
  363.  
  364. @<Part 1@>=@[
  365.  
  366. ASCII HUGE *
  367. to_ASCII FCN((p0))
  368.     outer_char HUGE *p0 C1("Character string to translate.")@;
  369. {
  370. ASCII HUGE *p;
  371.  
  372. if(translate_ASCII)
  373.     for(p=(ASCII HUGE *)p0; *p; p++) *p = XORD(*p);
  374.  
  375. return (ASCII HUGE *)p0;
  376. }
  377.  
  378. @ Here is the inverse routine, converting in place |ASCII| to |outer_char|.
  379.  
  380. @<Part 1@>=@[
  381.  
  382. outer_char HUGE *
  383. to_outer FCN((p0))
  384.     ASCII HUGE *p0 C1("Internal string to translate to outer world.")@;
  385. {
  386. outer_char HUGE *p;
  387.  
  388. if(translate_ASCII)
  389.     for(p=(outer_char HUGE *)p0; *p; p++) *p = XCHR(*(ASCII HUGE *)p); 
  390.  
  391. return (outer_char HUGE *)p0;
  392. }
  393.  
  394. @ Some \cee\ compilers accept an extended character set, so that one can
  395. type things like~\.{\^\^Z} instead of~\.{!=}.  If that's the case in your
  396. system, you should change the relevant part of \.{typedefs.web}, assigning
  397. positions~|01| to~|037| in the most convenient way; for example, at MIT you
  398. can just say $$\hbox{|for (i=1; i<=037; i++) xchr[i]=i;|}$$ since \.{WEB}'s
  399. character set is essentially identical to MIT's, even with respect to
  400. characters less than |040| (see the definitions below).  If, however, the
  401. changes do not conform with these definitions you should change the
  402. definitions as well.  (See \.{typedefs.web} for definitions of |and_and|,
  403. etc.)
  404.  
  405. @^system dependencies@>
  406. @^notes to myself@>
  407.  
  408. @<System-dependent parts of character set@>= /* nothing needs to be done */
  409.  
  410. @* IDENTIFIERS.
  411. At the point during phase one where we're recognizing
  412. identifiers, we sometimes have to determine whether the identifier is a
  413. special one. This is a simple byte-by-byte comparison. However, things are
  414. complicated because by that time the input has been converted to |ASCII|.
  415. Therefore, the comparison tables must also be converted to |ASCII|.
  416.  
  417. @ Test whether an identifier is a |BUILT_IN|.
  418.  
  419. @<Part 1@>=@[
  420.  
  421. boolean 
  422. is_include_like(VOID)
  423. {
  424. /* Check for \.{m4} built-in. */
  425. if(m4 && is_in(incl_likes,id_first,id_loc)) return YES;
  426.  
  427. /* The \.{WEB} functions behave as built-ins for \.{WEAVE}, but as macros
  428. for \.{TANGLE}. */  
  429. if(program==tangle) return NO;
  430.  
  431. if(!(*id_first == @'$' || *id_first == @'_')) return NO; // Speed up |is_in|.
  432. return is_in(WEB_incl_likes,id_first,id_loc);
  433. }
  434.  
  435. @ Convert lists to |ASCII|.
  436. @<Part 1@>=@[
  437. SRTN conv_bi FCN((b))
  438.     BUILT_IN HUGE *b C1("")@;
  439. {
  440. for( ; b->n != 0; b++) 
  441.     b->name = x__to_ASCII((outer_char *)b->name);
  442. }
  443.  
  444. SRTN 
  445. conv_dot FCN((d))
  446.     DOTS HUGE *d C1("")@;
  447. {
  448. for( ;d->code != 0; d++) 
  449.     d->symbol = x__to_ASCII((outer_char *)d->symbol);
  450. }
  451.  
  452. @ Hunt through a |BUILT_IN| list.
  453.  
  454. @<Part 1@>=@[
  455.  
  456. boolean 
  457. is_in FCN((b0,p0,p1))
  458.     CONST BUILT_IN b0[] C0("Array of built-ins.")@;
  459.     CONST ASCII HUGE *p0 C0("Start of text.")@;
  460.     CONST ASCII HUGE *p1 C1("End of text.")@;
  461. {
  462. CONST BUILT_IN HUGE *b;
  463.  
  464. for(b=b0; b->n != 0; b++)
  465.     if(web_strcmp(b->name,b->name+b->n,p0,p1) == EQUAL)
  466.             return YES; 
  467.  
  468. return NO;
  469. }
  470.  
  471. @
  472. @<Common...@>=
  473.  
  474. extern DOTS dots0[],mcmds[]; /* These are put into \.{typedefs.web} so we can
  475.                 make use of the various \.{@@d}s. */
  476.  
  477. @ Dot constants are allocated dynamically. Because of \Fortran-90's ability
  478. to define new constants, we have to allow for expanding the size of the
  479. table at run time.
  480. @<Common...@>=
  481.  
  482. IN_COMMON BUF_SIZE delta_dots; // Expand the table in increments of this size.
  483. IN_COMMON BUF_SIZE ndots; // The current total size.
  484. IN_COMMON DOTS HUGE *dots,HUGE *dots_end,HUGE *next_dot; // The dynamic array.
  485.  
  486. @ Here we do the initial allocation of the dynamic |dots| table.
  487. @<Allocate dynamic...@>=
  488. {
  489. DOTS HUGE *d;
  490.  
  491. ALLOC(DOTS,dots,ABBREV(delta_dots),delta_dots,0);
  492. ndots = delta_dots; /* Initialize total size. */
  493. dots_end = dots + ndots;
  494. next_dot = dots; /* Current pointer. */
  495.  
  496. /* Fill the table with the initial values. */
  497. for(d=dots0; d->len != 0; d++)
  498.     {
  499.     if(dot_code(dots,d->symbol,d->symbol+d->len,d->code) != d->code)    
  500.         CONFUSION("dots allocation","Invalid dot code");
  501.     }
  502. }
  503.  
  504. @ Either case is allowed for the dot constants. We will convert everything
  505. between the dots in place to upper case.
  506.  
  507. @<Part 1@>=@[
  508.  
  509. ASCII HUGE *
  510. uppercase FCN((p,n))
  511.     ASCII HUGE *p C0("Start of text.")@;
  512.     int n C1("Number of bytes to convert in place.")@;
  513. {
  514. int k;
  515.  
  516. for(k=0; k<n; ++k)
  517.     p[k] = A_TO_UPPER(p[k]); /* Convert $n$~bytes in place. */
  518.  
  519. return p; /* Return beginning address of string. */
  520. }
  521.  
  522. @ The following routine simply determines whether a decimal point begins a dot
  523. constant; this is used in parsing potential decimal constants.
  524.  
  525. @<Part 1@>=@[
  526.  
  527. boolean 
  528. is_dot(VOID)
  529. {
  530. ASCII HUGE *p;
  531. int n;
  532. ASCII temp[MAX_DOT_LENGTH]; /* For converting the putative constant to
  533.                 upper case. */
  534.  
  535. /* Scan to the end of the dot constant (all of which are alphabetic). */
  536. for(p=loc,n=0; n<MAX_DOT_LENGTH; n++,p++)
  537.     if(*p==@'.' || !isAlpha(*p)) break;
  538.  
  539. if(*p != @'.' || p==loc) return NO; /* It wasn't stopped by a period, or
  540.     the scan didn't even get started, so it
  541.     can't be a dot constant. */
  542.     
  543. STRNCPY(temp,loc,n);
  544. return BOOLEAN(CHOICE(dot_code(dots,uppercase(temp,n),temp+n,dot_const),
  545.     YES,NO));
  546. }
  547.  
  548. @ Is it in a |DOTS| list? If so, return the associated code. If it is not,
  549. add it to the table. (This was required by \FORTRAN-90.) If necessary,
  550. enlarge the table.
  551.  
  552. @<Part 1@>=@[
  553.  
  554. eight_bits 
  555. dot_code FCN((d0,p0,p1,new_code))
  556.     DOTS HUGE *d0 C0("|DOTS| array.")@;
  557.     CONST ASCII HUGE *p0 C0("Start of text.")@;
  558.     CONST ASCII HUGE *p1 C0("End of text.")@;
  559.     eight_bits new_code C1("May we extend the table or not?")@;
  560. {
  561. DOTS HUGE *d;
  562.  
  563. re_dot:
  564.   for(d=d0; d < next_dot; d++)
  565.     if(web_strcmp(d->symbol,d->symbol+d->len,p0,p1) == EQUAL)
  566.         {
  567. /* Check for an overloaded operator. If so, remember the details. */
  568.         if(d->code == dot_const)
  569.             {
  570.             STRCPY(dot_op.name+1,d->symbol);
  571.             dot_op.cat = d->cat;
  572.             dot_op.num = (eight_bits)(d-dots);
  573.             }
  574.  
  575.         return d->code;
  576.         }
  577.  
  578. /* Didn't find the dot constant or operator in the table. Enlarge the table
  579. if necessary. */
  580. if(!new_code) return NO; /* One can add to |dots|, but not |mcmds|. */
  581.  
  582. if(next_dot == dots_end)
  583.     {
  584.     if( (dots=(DOTS HUGE *)REALLOC(dots,
  585.         (ndots+delta_dots)*sizeof(DOTS), ndots*sizeof(DOTS)))==NULL ) 
  586.             OVERFLW("dot operators",ABBREV(delta_dots));
  587.     
  588.     next_dot = dots + ndots; /* Next available position. */
  589.     ndots += delta_dots; /* New total length. */
  590.     dots_end = dots + ndots; /* Upper bound. */
  591.     }
  592.  
  593. /* Add operator to table. */
  594. next_dot->len = PTR_DIFF(short, p1, p0);
  595. next_dot->symbol = GET_MEM("next_dot->symbol",next_dot->len + 1,ASCII);
  596. STRNCPY(next_dot->symbol,p0,next_dot->len);
  597. next_dot->code = new_code; /* Mark as overloaded, or initialize. */
  598. next_dot->cat = 1; /* ??? */
  599. d0 = next_dot++;
  600. next_dot->len = 0;
  601. goto re_dot;
  602. }
  603.  
  604. @ Is it a macro preprocessor command in a |DOTS| list? 
  605.  
  606. @<Part 1@>=@[
  607.  
  608. eight_bits 
  609. is_mcmd FCN((d0,p0,p1))
  610.     DOTS HUGE *d0 C0("|DOTS| array.")@;
  611.     CONST ASCII HUGE *p0 C0("Start of text.")@;
  612.     CONST ASCII HUGE *p1 C1("End of text.")@;
  613. {
  614. DOTS HUGE *d;
  615.  
  616.   for(d=d0; d->code; d++)
  617.     if(web_strcmp(d->symbol,d->symbol+d->len,p0,p1) == EQUAL)
  618.         return d->code;
  619.  
  620. return 0; // Not a preprocessor command.
  621. }
  622.  
  623.  
  624. @* INPUT ROUTINES.
  625. The lowest level of input to the \.{WEB} programs is
  626. performed by |input_ln|, which must be told which file to read from.
  627. Unlike the original \WEB s, there is a separate input buffer for each
  628. possible open file, as well as associated parameters for each buffer. These
  629. parameters are collected in an array of structures of type \&{INPUT\_PRMS},
  630. which contains symmetric entries for both input files and change files.
  631. The parameters for the current file are in |*cur0_prms| and are set upon
  632. entry to |input_ln|. This change was necessary in order to accomodate
  633. \Fortran, which, since it reads ahead, leaves stuff in the tail end of the
  634. buffer, to be read on the next call to |input_ln|. If one doesn't have
  635. separate buffers, the change file mechanism won't work properly, nor will
  636. the include facility.
  637.  
  638. The return value of |input_ln| is |YES| if the read is successful and |NO| if
  639. not (generally this means the file has ended). The conventions
  640. of \TeX\ are followed; i.e., the characters of the next line of the file
  641. are translated to |ASCII| code and copied into the |cur_buffer| array,
  642. and the global variable |limit| is set to the first unoccupied position.
  643. Trailing blanks are ignored. The value of |limit| must be strictly less
  644. than |buf_size|, so that |cur_buffer[buf_size-1]| is never filled.
  645.  
  646. We assume that none of the |ASCII| values of |*j| for |cur_buffer<=j<limit|
  647. is equal to~0, |0177|, |line_feed|, |form_feed|, or |carriage_return|.
  648. Since |buf_size| is strictly less than |long_buf_size|,
  649. some of \.{WEB}'s routines use the fact that it is safe to refer to
  650. |*(limit+2)| without overstepping the bounds of the array.
  651.  
  652.  
  653. @m fp cur0_prms->File /* The current file pointer.  (It's~\.{@@m} instead
  654.     of~\.{@@d} because of a header conflict on the C370 system.) */
  655.  
  656. @<Common...@>=
  657.  
  658. IN_COMMON BUF_SIZE buf_size; // Holds input line.
  659.  
  660. IN_COMMON ASCII HUGE *loc; 
  661.     // Points to the next character to be read from the buffer.
  662.  
  663. typedef enum {START,END} DELIM_TYPE;
  664. typedef enum {NO_CMNT,SHORT_CMNT,LONG_CMNT} CMNT_TYPE;
  665.  
  666. typedef struct
  667.     {
  668.     CMNT_TYPE type;
  669.     DELIM_TYPE delim;
  670.     eight_bits len; // Length of the delimiter; either~$0$, $1$, or~$2$.
  671.     ASCII HUGE *pos; // Start of the delimiter.
  672.     } CMNT_DATA;
  673.  
  674. IN_COMMON CMNT_DATA posns[50], HUGE *pcmnt CSET(posns);
  675.  
  676. @*1 Reading an input line.
  677. In the unlikely event that your standard I/O
  678. library does not support |feof|, |getc| and |ungetc| you may have to change
  679. things here.  
  680. @^system dependencies@>
  681.  
  682. This function copies a line into |cur_buffer| or returns |NO| if it's
  683. gotten to the end-of-file.
  684.  
  685. @<Part 1@>=@[
  686.  
  687. boolean 
  688. input_ln FCN((p0))
  689.    INPUT_PRMS0 HUGE *p0 C1("Which structure describes the current file?")@;
  690. {
  691. cur0_prms = p0;    // Address of current parameters.
  692. limit = cur_buffer;    /* Initialize the top to the bottom. |limit| points to
  693.                 first unfilled position. */
  694.  
  695. cur_line++; /* A separate line count is kept for each file (including the
  696.         change file). */
  697.  
  698. /* |column_mode| and |parsing_mode| may be redundant. But don't mess with
  699. these. */
  700. if(!nuweb_mode && FORTRAN_LIKE(language) && column_mode && parsing_mode==OUTER)
  701.     switch(language)
  702.         {
  703.        case FORTRAN:
  704.        case FORTRAN_90:
  705.         if(!rd_Fortran())
  706.             return NO;
  707.         break;
  708.  
  709.        case RATFOR:
  710.        case RATFOR_90:
  711.         if(!rd_Ratfor())
  712.             return NO;
  713.  
  714.         if(limit==cur_buffer) 
  715.             goto empty_line;
  716.         break;
  717.  
  718.        default:
  719.         CONFUSION("input_ln","Invalid FORTRAN_LIKE language");
  720.         }
  721. else 
  722.     if(!rd_free_form())
  723.         return NO;
  724.  
  725. /* Take care of an empty line: kill the semicolon. (This is a bit shaky.) */
  726. if(column_mode && FORTRAN_LIKE(language) && 
  727.     PTR_DIFF(size_t,limit,cur_buffer)==2 &&    
  728.     ((!auto_semi && (cur_buffer[1]==@';' || cur_buffer[1]==@' ')) ||
  729.     (auto_semi && cur_buffer[1]==@';' && (cur_buffer[0]=='C' ||
  730.         cur_buffer[0]==@'c' || cur_buffer[0]==@'*'))))
  731.   empty_line:
  732.     limit = cur_buffer;
  733.     
  734. @#if 0
  735. fin_line:
  736.     ;
  737. @#endif /* For future use. */
  738.  
  739. #if(DEBUG)
  740.     @<Echo the line to be returned from |input_ln|@>;
  741. #endif
  742.  
  743. return YES;    /* Successful read */
  744. }
  745.  
  746. @ For debugging purposes, echo the line which will be returned. For the
  747. benefit of the user, replace the |begin_comment0| by~'\.{!}' and
  748. |begin_comment1| by~'\.?'. Turn on this echo with the command-line
  749. option~'\.{-l}'. A positive numeric argument means only start debugging at
  750. that line number; a negative one means also print the address of the
  751. buffer.
  752.  
  753. @<Other...@>=
  754.  
  755. IN_COMMON long start_line CSET(1); 
  756.     // Must be |long|; a negative value is used as a flag.
  757. IN_COMMON long end_line CSET(LONG_MAX);
  758.  
  759. @
  760. @<Echo the line...@>=
  761. {
  762. register ASCII HUGE *k;
  763.  
  764. if(prn_input_lines && cur_line >= (LINE_NUMBER)start_line
  765.         && cur_line < (LINE_NUMBER)end_line) 
  766.     {
  767.     if(prn_input_addresses)
  768.         printf("%lu->%lu (%u) ",
  769.             (unsigned long)cur_buffer,(unsigned long)limit,
  770.             PTR_DIFF(unsigned, limit, cur_buffer)); 
  771.  
  772.     printf("%c[%d%s:%u]: \"",
  773.         (language == global_language ? ' ' : *LANGUAGE_CODE(language)),
  774.         incl_depth,
  775.         (changing ? "*" :""),
  776.         cur_line); 
  777.  
  778.     for(k=cur_buffer; k<limit; ++k)
  779.         switch(*k)
  780.             {
  781.            case (ASCII)begin_comment0:
  782.             printf("</*>");
  783.             break;
  784.  
  785.            case (ASCII)begin_comment1:
  786.             printf("<//>");
  787.             break;
  788.  
  789.            case interior_semi:
  790.             printf("<;>");
  791.             break;
  792.  
  793.            default:
  794.             putchar(XCHR(*k));
  795.             break;
  796.             }
  797.  
  798.     puts("\"");
  799.     }
  800. }
  801.  
  802. @*1 Free-form input.
  803. The simplest, most straightforward thing to do is read
  804. completely free-form syntax. This is done for~C and for \RATFOR-77. Here is
  805. one of the clearest examples of when to prefix single-quoted characters
  806. with~'\.{@@}'.
  807.  
  808. @<Part 1@>=@[
  809.  
  810. boolean 
  811. rd_free_form(VOID)
  812. {
  813. register int c='\0'; // The character read.
  814. register ASCII HUGE *k;  // Where next character goes.
  815.  
  816. @<Read free-form syntax@>@;
  817.  
  818. return YES;
  819. }
  820.  
  821. @
  822. @<Read free...@>=
  823. {    
  824. if(num_in_buffer != 0) 
  825.     @<Flush \Fortran\ buffer.@>@;
  826. else
  827.     {
  828.     if (feof(fp)) 
  829.         return NO;  // We have hit end-of-file.
  830.  
  831.     @<Read by bytes@>;
  832. @#if(0)
  833.     @<Read by records@>;
  834. @#endif /* For speeding up input; not debugged. */
  835.     }
  836. }
  837.  
  838. @ This is the original byte-oriented input routine. It is ANSI-compatible.
  839. However, on some systems such as VAX/VMS, it may be slow.
  840.  
  841. @d TOO_LONG 
  842. {
  843. char temp[200];
  844. sprintf(temp, "please use the `-ybs...' option to increase buffer size from \
  845. its present value of %lu characters", buf_size);
  846. FATAL(C, "Input line too long; ", temp);
  847. }
  848.  
  849. @<Read by bytes@>=
  850. {
  851. k = cur_buffer;  /* beginning of buffer */
  852.  
  853. @#if 0
  854. if(column_mode && language==TEX)
  855.     { // Make each line a short comment.
  856.     *k++ = @'/'; @~ *k++ = @'/';
  857.     }
  858. @#endif
  859.  
  860. while (k<=buffer_end && (c=getc(fp)) != EOF && c!='\n')
  861.          if ((*(k++) = XORD(c)) != @' ') limit = k; /* Update first unfilled
  862. position; |k|~is now pointing to next position to fill, and the last one
  863. wasn't blank. */
  864.  
  865. if (k>buffer_end)
  866.     if ((c=getc(fp))!=EOF && c!='\n') 
  867.     {
  868.       ungetc(c,fp); loc=cur_buffer; TOO_LONG;
  869. @.Input line too long@>
  870.     }
  871.  
  872. if (c==EOF && limit==cur_buffer) 
  873.     return NO;  // There was nothing after the last newline.
  874. }
  875.  
  876. @ This is an experimental block to speed up the input. It is not complete.
  877. For VAX/VMS, it works only for files with variable length record formats,
  878. not with stream files.
  879. @<Read by records@>=
  880. @{
  881. int n;
  882.  
  883. @b
  884. for(k = cur_buffer; k < buffer_end &&
  885.     (n = fread(k,buffer_end-k,fp))!=0 &&
  886.     k[n-1]!='\n'; k += n); 
  887.  
  888. k += n;
  889.  
  890. if(k==buffer_end && *(k-1) != '\n')
  891.     {
  892.     loc = cur_buffer;
  893.     TOO_LONG;
  894.     }
  895.  
  896. #if(TRANSLATE_ASCII)
  897.     TERMINATE(cur_buffer,n);
  898.     to_ASCII(cur_buffer);
  899. #endif
  900.  
  901.  
  902. while(*(k-1)==@'\n' && k > cur_buffer) k--;
  903. while(*(k-1)==@' ' && k > cur_buffer) k--;
  904.  
  905. limit = k;
  906.  
  907. if(n==0 && limit==cur_buffer) return NO;
  908. }
  909.  
  910. @*1 \Fortran\ input.
  911. It's probably hopeless to explain the present version
  912. of the \FORTRAN\ input driver. It should be abandoned and rewritten from
  913. scratch. The best that can be said about \FORTRAN\ with \FWEB\ is that one
  914. should use \RATFOR\ instead.
  915.  
  916. The problem with \FORTRAN's fixed column format is that we must read ahead to 
  917. get any continuation lines which may be waiting. By the time we've figured
  918. that out, we've read too far. We leave that stuff waiting, signified by
  919. |num_in_buffer != 0|. On the next time into |input_ln|, the waiting stuff
  920. will first be moved to the beginning of the buffer; then we go on and read
  921. ahead some more.
  922.  
  923. In the following, |limit|~is the next available space in the buffer, |k|~is
  924. the present position; |k0|~marks the beginning of the last line read. Each
  925. line is actually 5~bytes longer than the actual number of characters read:
  926. 2~bytes for a possible |"*/"|, 3~for use by the scanning routines of
  927. \TANGLE, which may put things there during its scans.
  928.  
  929. @d N_WORK 3
  930. @d N_END (N_WORK+2)
  931.  
  932. @<Part 1@>=@[
  933.  
  934. boolean 
  935. rd_Fortran(VOID)
  936. {
  937. register int c='\0'; // The character read.
  938. register ASCII HUGE *k;  // Where next character goes.
  939. boolean first_line = YES;
  940.  
  941. /* We're starting afresh; initialize flags. */
  942. last_was_continued = NO;
  943. scanning_C_cmnt = NO;
  944. string_char = '\0';
  945. in_char_string = NO;
  946.  
  947. /* Keep reading lines until we sense a non-comment, non-continuation line.
  948. Leave the latter waiting in the buffer starting at~|k0|. */
  949. for(k= limit; ; k=k0 = (limit+=N_END),first_line=NO,cur_line++)
  950.     if(num_in_buffer == 0) 
  951.         @<Append to buffer.@>@;
  952.     else 
  953.         @<Fill buffer from previous read.@>@;
  954.  
  955. /* Except for new module, throw away all stuff after \.{@@}~command in
  956. column~1. */
  957. if(*cur_buffer==@'@@' && !(limit==cur_buffer+1 || *(cur_buffer+1)==@'*' ||
  958.         *(cur_buffer+1)==@' ')) 
  959.     for(k=cur_buffer+2; k<limit; ++k)
  960.         if(*k == @';')
  961.             {
  962. @#if(0)
  963.             limit = k;
  964. @#endif
  965.             *k = @' ';
  966.             break;
  967.             }
  968.  
  969. return YES;
  970. }
  971.  
  972. @ If |num_in_buffer == 0| on entry to |input_ln|, there's the beginning of
  973. a line already waiting. That line starts at~|k0|. Here, we move it to the
  974. beginning of the buffer.
  975.  
  976. @<Fill buffer from previous...@>=
  977. {
  978. @<Flush \Fortran\ buffer.@>;
  979.  
  980. /* If what was waiting was a control line, we must shift out of column mode. */
  981. if(found_at)
  982.     {
  983.     out_of_column_mode:
  984.     found_at = column_mode = last_was_empty = NO;
  985.     break; /* Break out of |for| loop which reads lines. The finish-read
  986.             material is moot. */
  987.     }
  988.  
  989. if(at_line) 
  990.     {
  991.     last_was_empty = NO;
  992.     break;
  993.     }
  994.  
  995. /* Finally, we give special treatment to empty lines. */
  996. last_was_empty = BOOLEAN(limit==cur_buffer);
  997. scan_for_cmnts();
  998. } /* Go back to the |for| loop which reads lines. */
  999.  
  1000. @ Here we move stuff waiting in the buffer to the beginning of the buffer,
  1001. and reset the end of the buffer.
  1002.  
  1003. @<Flush \Fortran\ buffer.@>=
  1004. {
  1005. STRNCPY(cur_buffer,k0,num_in_buffer); // Move line to beginning of buffer.
  1006. k0 = cur_buffer; // For |scan_for_cmnts|
  1007. limit = cur_buffer + num_in_buffer - N_END; // Reposition the end of buffer.
  1008. num_in_buffer = 0; // We've now cleaned out the buffer.
  1009. }
  1010.  
  1011. @ We get to here when |num_in_buffer == 0|. 
  1012. @<Append to buffer.@>=
  1013. @{
  1014. int i;
  1015.  
  1016. @b
  1017. if (feof(fp)) 
  1018.     {
  1019.     if(first_line) return NO;  // We have hit end-of-file.
  1020.     else goto concatenate_cmnts; //    Don't lose the last line.
  1021.     }
  1022.  
  1023. k0 = limit; // Set the start of this line to the end of the old one.
  1024.  
  1025. @<Read \Fortran\ line.@>; // |limit| is positioned after the last char.\ read.
  1026.  
  1027. /* We will allow two extra characters at the end, in case we need to put
  1028. end-of-comment markers there; we also need two bytes for Tangle's workspace. */
  1029. MEMSET(limit,@' ',N_END);
  1030. num_in_buffer = 0; // There's nothing waiting.
  1031.  
  1032. /* If we've read into a |new_module| command in column~1, 
  1033.     prepare to switch out of column mode. */
  1034. at_line = BOOLEAN(*k0==@'@@' && *(k0+1) != @'/');
  1035.  
  1036. if(at_line && (*(k0+1)==@'*' || *(k0+1)==@' ')) found_at = YES;
  1037.  
  1038. /* If we were appending, then we must delay shifting out of column mode until
  1039. we process the first part of the buffer. However, if we're actually at the
  1040. beginning of the buffer, then we must shift out of column mode immediately. */
  1041. if(found_at && k0==cur_buffer) goto out_of_column_mode;
  1042.  
  1043. if(first_line && at_line) break;
  1044.  
  1045. /* Now we look at the line we just put into the buffer. Usually, we should
  1046. look at that to see whether it's a continuation or a comment line, because if
  1047. so we keep on reading. However, if it's a command line, or if we're in the
  1048. middle of handling a trailing C~comment which didn't end on the previous
  1049. line, then this line should not be treated in column mode. */
  1050. if(scanning_C_cmnt)
  1051.     {
  1052.     scan_for_cmnts();
  1053.     continue;
  1054.     }
  1055.  
  1056. if(!(at_line || scanning_C_cmnt)) 
  1057.     @<Check for comment or continuation.@>@; 
  1058.  
  1059. /* If we get to here, it's neither a comment nor a continuation. If this line
  1060. was the first in the buffer, we must continue to read more.  (However,
  1061. if this line is null, then we do not read any more, because we don't want to
  1062. concatenate a null comment to the previous line.) Otherwise, we're
  1063. done reading and we should leave this line waiting in the buffer. */
  1064. neither_cmnt_nor_continuation:
  1065. if(first_line)
  1066.     if(limit==cur_buffer) /* Quit on null first line. */
  1067.         {
  1068.         comment_in_buffer = NO;
  1069.         num_in_buffer = 0;
  1070.         break;
  1071.         }
  1072.     else
  1073.         {
  1074.         scan_for_cmnts();
  1075.         continue;  /* We must read some    more to see if next is
  1076. continuation. */ 
  1077.         }
  1078.  
  1079. @<Finish reading lines.@>;
  1080. }
  1081.  
  1082. @ This is the end of this read. Replace the newline by a semicolon to
  1083. delimit the statements.
  1084.  
  1085. @<Finish read...@>=
  1086. {
  1087. last_of_read:
  1088.  if(!first_line)
  1089.   {
  1090.   num_in_buffer = PTR_DIFF(size_t,limit,k0) + N_END;
  1091.   limit = MAX(k0 - N_WORK,cur_buffer);
  1092.   }
  1093. else limit += N_WORK;
  1094.  
  1095. concatenate_cmnts:
  1096. anlz_cmnts(); // Concatenate comments appropriately, and set |scanning_C_cmnt|.
  1097.  
  1098. cur_line--;
  1099. break; /* Break out of |for| loop. */
  1100. }
  1101.  
  1102. @ Read characters for one line into buffer, translating them as we go.
  1103. Trailing blanks will be ignored. Tabs will be translated into 6~spaces.
  1104.  
  1105. @d is_point(c) (point_comments && c=='!')
  1106.  
  1107. @<Read \Fortran\ line.@>=
  1108. {
  1109. while(k<=buffer_end && (c=getc(fp)) != EOF && c!='\n')
  1110.     if(c=='\t' && k<=buffer_end-6) 
  1111.         for(i=0; i<6; ++i) *(k++) = @' ';/* Translate tab
  1112. character. (Don't update |limit|.) */
  1113.     else
  1114.         {
  1115.         boolean escaped_char = NO;
  1116.  
  1117.         if(c==';') c = XCHR(interior_semi);
  1118.         else if(point_comments && (*k0 != @'@@') && 
  1119.                 (escaped_char = BOOLEAN(c == '\\')))
  1120.             {
  1121.             c = getc(fp); // What's coming up next?
  1122.             
  1123. /* If it's not an escaped point, just copy the escape sequence. */
  1124.             if(c != '!') 
  1125.                 {
  1126.                 ungetc(c,fp);
  1127.                 c = '\\';
  1128.                 }
  1129.             }
  1130.  
  1131. /* A point that's not escaped begins a short comment. */
  1132.         if( (*(k++) = CHOICE(is_point(c) && !escaped_char,
  1133.                 (ASCII)begin_comment1,XORD(c))) != @' ') 
  1134.             limit = k; // Ignore trailing blanks.
  1135.         }
  1136.  
  1137. if (k>buffer_end)
  1138.     if ((c=getc(fp))!=EOF && c!='\n')
  1139.     {
  1140.       ungetc(c,fp); loc=cur_buffer; TOO_LONG;
  1141. @.Input line too long@>
  1142.     }
  1143.  
  1144. if (c==EOF && limit==cur_buffer) return NO;  /* there was nothing after
  1145.     the last newline */
  1146. }
  1147.  
  1148. @ Here we examine the line most recently read to see if it's a comment or
  1149. continuation. If that's true, then we continue to the bottom of the
  1150. enclosing |for| loop which reads lines.
  1151.  
  1152. @<Check for comment or cont...@>=
  1153. @{
  1154. boolean is_cmnt = NO, Fortran_cmnt = NO, C_cmnt = NO;
  1155.  
  1156. ASCII HUGE *p = k0; /* Points to the comment character. */
  1157.  
  1158. @b
  1159. /* Did we get a comment line?  */
  1160. switch(*k0)
  1161.     {
  1162.     case @'C':
  1163.     case @'c':
  1164.     case @'*':
  1165.     case (ASCII)begin_comment0:
  1166.     case (ASCII)begin_comment1:
  1167.         is_cmnt = Fortran_cmnt = YES;
  1168.         break;
  1169.  
  1170.     default:
  1171. /* Skip over white space at beginning of line. */
  1172.         for(; p<limit; p++) if(*p!=@' ') break;
  1173.  
  1174.         if( (*p==@'/' && (*(p+1)==@'*' 
  1175.                 || (*(p+1)==@'/' && Cpp_comments)))
  1176.             || *p == (ASCII)begin_comment0
  1177.             || *p == (ASCII)begin_comment1) is_cmnt = C_cmnt = YES;
  1178.         break;
  1179.     }
  1180.  
  1181. if(is_cmnt) @<Process comment line.@>@;
  1182.  
  1183. /* If we're this far, it's not a comment. Check for continuation line
  1184. (neither blank nor |'0'| in column~6). If
  1185. so, shift it over to abut it with previous stuff in buffer. */
  1186. if(limit-k0 >=5 && !(k0[5] == @' ' || k0[5]==@'0') )
  1187.     @<Process possible continuation line.@>@;
  1188. }
  1189.  
  1190.  
  1191. @ The line is a comment line. We give special treatment to an otherwise null
  1192. comment line, treating it as a line with nothing in it at all. If the last
  1193. line was a comment also, we concatenate this one with the previous one. 
  1194.  
  1195. @<Process comment line.@>=
  1196. {
  1197. if(limit == k0+1) 
  1198.     { /* Null comment line. */
  1199.     limit = k0;
  1200.     if(!first_line) goto last_of_read;
  1201.     else goto concatenate_cmnts;
  1202.     }
  1203.  
  1204. if(k0 > cur_buffer) MEMSET(k0-N_END,@' ',N_END);
  1205.  
  1206. if(Fortran_cmnt) *p = begin_comment1;
  1207.  
  1208. /* If the last line was empty, we don't want to read any further; leave this
  1209. comment waiting in the buffer. */
  1210. if(last_was_empty)
  1211.     {
  1212.     num_in_buffer = PTR_DIFF(size_t,limit,k0) + N_END;
  1213.     comment_in_buffer = YES;
  1214.     limit = cur_buffer;
  1215.      break; /* Break out of the |for| loop which reads lines. */
  1216.     }
  1217.  
  1218. scan_for_cmnts();
  1219. continue; /* Go back and read more lines. */
  1220. }
  1221.  
  1222.  
  1223. @ The line is a possible continuation line; namely, it has something in
  1224. column~6. However, if we're in the midst of a trailing C~comment or if an
  1225. alphanumeric label extends into column~6, then it's not a continuation. We
  1226. check for these possibilities by seeing whether there's anything in columns
  1227. 1--5. 
  1228.  
  1229. @<Process possible continuation...@>=
  1230. @{
  1231. register ASCII HUGE *l;
  1232. boolean stuff_in_1_5;
  1233.  
  1234. @b
  1235. if(first_line) 
  1236.     {/* Error: Continuation line not expected. */
  1237.     scan_for_cmnts();
  1238.     continue; 
  1239.     }
  1240.  
  1241. /* We don't allow anything in columns 1--5 for a continuation line. That's
  1242. a FORTRAN rule, and it helps us to catch a possible continuation of a
  1243. C-style comment. */
  1244. stuff_in_1_5 = NO;
  1245.  
  1246. for(l=k0;l<k0+5; ++l)
  1247.     if(*l != @' ')
  1248.         {
  1249.         stuff_in_1_5 = YES;
  1250.         break;
  1251.         }
  1252.  
  1253.  /*  If there's stuff in columns 1--5, it's not a continuation line.
  1254. Otherwise, it's a continuation line; abut it with the code in the previous
  1255. line. */
  1256. if(stuff_in_1_5) goto neither_cmnt_nor_continuation;
  1257.  
  1258. *l = @' '; // Delete the continuation symbol.
  1259. scan_for_cmnts();
  1260. continue; /* Keep on reading. */
  1261. }
  1262.  
  1263. @ The following function records the status and position of a comment
  1264. delimiter. 
  1265.  
  1266. @<Part 1@>=@[
  1267.  
  1268. SRTN 
  1269. mark_cmnt FCN((type0,delim0,length0,pos0))
  1270.     CMNT_TYPE type0 C0("")@;
  1271.     DELIM_TYPE delim0 C0("")@;
  1272.     eight_bits length0 C0("")@;
  1273.     ASCII HUGE *pos0 C1("")@;
  1274. {
  1275. pcmnt->type = type0;
  1276. pcmnt->delim = delim0;
  1277. pcmnt->len = length0;
  1278. pcmnt->pos = pos0;
  1279.  
  1280. if(type0 == LONG_CMNT) scanning_C_cmnt = BOOLEAN(delim0 == START);
  1281.  
  1282. pcmnt++;
  1283. pcmnt->type = NO_CMNT;
  1284. pcmnt->delim = START;
  1285. pcmnt->len = 0;
  1286. pcmnt->pos = limit;
  1287. }
  1288.  
  1289. @
  1290. @<Glob...@>=
  1291.  
  1292. IN_COMMON ASCII string_char;
  1293. IN_COMMON boolean in_char_string;
  1294.  
  1295. @
  1296.  
  1297. @d MARK(type,delim,len) mark_cmnt(type,delim,len,p)
  1298.  
  1299. @<Part 1@>=@[
  1300.  
  1301. SRTN 
  1302. scan_for_cmnts(VOID)
  1303. {
  1304. register ASCII HUGE *p;
  1305.  
  1306. for(p=k0; p<limit; p++)
  1307.     switch(*p)
  1308.         {
  1309.        case (ASCII)begin_comment0:
  1310.         if(scanning_C_cmnt) 
  1311.             break;
  1312.  
  1313.         MARK(LONG_CMNT,START,1);
  1314.         break;
  1315.  
  1316.        case (ASCII)begin_comment1:
  1317.         if(scanning_C_cmnt) 
  1318.             break;
  1319.  
  1320.         MARK(SHORT_CMNT,START,1);
  1321.         p = limit;
  1322.         MARK(SHORT_CMNT,END,0);
  1323.         break;
  1324.  
  1325.        case @'\'':
  1326.        case @'"':
  1327.         if(!scanning_C_cmnt)
  1328.             @<Check for \Fortran\ string@>@;
  1329.         break;
  1330.  
  1331.        case @'/':
  1332.         if(in_char_string || scanning_C_cmnt) 
  1333.             break;
  1334.  
  1335.         if(*(p+1) == @'/' && Cpp_comments)
  1336.             {
  1337.             MARK(SHORT_CMNT,START,2);
  1338.             p = limit;
  1339.             MARK(SHORT_CMNT,END,0);
  1340.             }
  1341.         else if(*(p+1) == @'*')
  1342.             {
  1343.             MARK(LONG_CMNT,START,2);
  1344.             p++;
  1345.             }
  1346.  
  1347.         break;
  1348.  
  1349.        case @'*':
  1350.         if(in_char_string)
  1351.             break;
  1352.  
  1353.         if(scanning_C_cmnt && *(p+1) == @'/')
  1354.             {
  1355.             MARK(LONG_CMNT,END,2);
  1356.             p++;
  1357.             }
  1358.  
  1359.         break;
  1360.         }
  1361. }
  1362.         
  1363. @
  1364. @<Check for \F...@>=
  1365. {
  1366. if(!in_char_string)
  1367.     {
  1368.     in_char_string = YES;
  1369.     string_char = *p;
  1370.     break;
  1371.     }
  1372.  
  1373. if(string_char == *p && p[1] == string_char)
  1374.     {
  1375.     p++;
  1376.     break;
  1377.     }
  1378.  
  1379. in_char_string = NO;
  1380. }
  1381.  
  1382. @
  1383.  
  1384. @d BLANK_OUT(d) MEMSET((d)->pos,@' ',(d)->len)
  1385.  
  1386. @<Part 1@>=@[
  1387.  
  1388. SRTN 
  1389. anlz_cmnts(VOID)
  1390. {
  1391. CMNT_DATA *d;
  1392. boolean found_text = NO;
  1393. ASCII HUGE *l;
  1394.  
  1395. pcmnt->pos = limit;
  1396.  
  1397. for(d=posns + 1; d<pcmnt; d+=2)
  1398.     {
  1399.     found_text = NO;
  1400.  
  1401.     for(l=d->pos+d->len; l<(d+1)->pos; l++)
  1402.         if(*l != @' ')
  1403.             {
  1404.             found_text = YES;
  1405.             break;
  1406.             }
  1407.  
  1408.     if(found_text)
  1409.         {
  1410.         if(d->type == SHORT_CMNT) 
  1411.             {
  1412.             BLANK_OUT(d-1);
  1413.             *((d-1)->pos) = begin_comment0;
  1414.             *(d->pos) = @'*';
  1415.             *(d->pos + 1) = @'/';
  1416.             }
  1417.         }
  1418.     else if(d->type == (d+1)->type)
  1419.         {
  1420.         BLANK_OUT(d);
  1421.         BLANK_OUT(d+1);
  1422.         d->type = (d+1)->type = NO_CMNT;
  1423.         }
  1424.     }
  1425.  
  1426. @<Insert an automatic semicolon@>@;
  1427.  
  1428. pcmnt = posns; // Reset.
  1429. }
  1430.  
  1431. @<Scan backwards over comments@>=
  1432. for(pk = k0-1; ;)
  1433.     if(!bscn_white() || !bscn_cmnt()) break;
  1434.  
  1435. @
  1436. @<Common...@>=
  1437.  
  1438. IN_COMMON ASCII HUGE *pk;
  1439.  
  1440. @<Unused@>=
  1441.  
  1442. boolean bscn_white(VOID)
  1443. {
  1444. for( ; pk>=cur_buffer; --pk)
  1445.     if(*pk != @' ') return YES;
  1446.  
  1447. return NO;
  1448. }
  1449.  
  1450. boolean bscn_cmnt(VOID)
  1451. {
  1452. /* Are we at end of comment? */
  1453. if( !(*pk==@'/' && *(pk-1) == @'*') ) 
  1454.     {
  1455.     ++pk; /* Copy the continuation line to here. */
  1456.     return NO; /* Not on white space and
  1457. not on end of comment; we're done. */
  1458.     }
  1459.  
  1460. /* Scan backwards to beginning of comment. Must allow for both kinds
  1461. of comment styles. */
  1462. for(pk -=N_END; pk >= cur_buffer; --pk)
  1463.     if(*pk==(ASCII)begin_comment0 || 
  1464.         *pk==(ASCII)begin_comment1 ||
  1465.         (*pk==@'/' && (*(pk+1) == @'*' || *(pk+1)==@'/')))
  1466.                     /* Found start of comment. */ 
  1467.             {
  1468.             --pk;
  1469.             return YES;
  1470.             }
  1471.  
  1472. return NO;
  1473. }
  1474.  
  1475. @ In Fortran mode, the last thing we do before returning the line is to
  1476. parse looking for comments. We want to propagate the final semicolon to
  1477. just before the last comment.  With this scheme, the innards of \WEAVE\ and
  1478. \TANGLE\ are presented with a uniform syntax.  
  1479.  
  1480. @<Common...@>=
  1481.  
  1482. IN_COMMON ASCII HUGE *lp, HUGE *semi_pos;
  1483. IN_COMMON boolean found_text,text,C_style_cmnt,short_cmnt,R_style_cmnt;
  1484.  
  1485. @<Insert an auto...@>=
  1486. {
  1487. boolean text_after = NO;
  1488.  
  1489. if(pcmnt > posns)
  1490.     { // There's at least one comment.
  1491.     if(!scanning_C_cmnt)
  1492.        for(l=limit-1; l>(pcmnt-1)->pos + (pcmnt-1)->len; l--)
  1493.         if(*l != @' ')
  1494.             { // There's text after the comment.
  1495.             text_after = YES;
  1496.             *(l+1) = @';';
  1497.             break;
  1498.             }
  1499.  
  1500.     if(!text_after)
  1501.        {
  1502.        pcmnt -= 2;
  1503.  
  1504.        if(pcmnt < posns)
  1505.         {
  1506.         ERR_PRINT(C, "Fortran comment didn't end; missing */?");
  1507.         pcmnt = posns;
  1508.         }
  1509.  
  1510.        while(pcmnt->type == NO_CMNT) 
  1511.         {
  1512.         pcmnt--;
  1513.  
  1514.         if(pcmnt < posns)
  1515.             CONFUSION("Insert an automatic semicolon", 
  1516. "Invalid comment structure");
  1517.         }
  1518.  
  1519.        for(l=pcmnt->pos - 1; l>=cur_buffer; l--)
  1520.         {
  1521.         if(*l != @' ')
  1522.             { // There's text before the comment.
  1523.             semi_pos = pcmnt->pos;
  1524.             @<Fill in semi@>@;
  1525.             break;
  1526.             }
  1527.         }
  1528.        }
  1529.     }
  1530. else 
  1531.     { // No comments at all.
  1532.     for(l=limit-1; l>=cur_buffer; l--)
  1533.         if(*l != @' ')
  1534.             {
  1535.             *(l+1) = @';';
  1536.             break;
  1537.             }
  1538.     }
  1539. }
  1540.  
  1541. @ Here we put in the semicolon. We have to watch out for a preceding
  1542. `\.{@@}' sign, which signifies a verbatim comment.
  1543. @<Fill in semi@>=
  1544. {
  1545. *(semi_pos+1) = (ASCII)(((pcmnt+1)->type==LONG_CMNT ? begin_comment0 :
  1546.             begin_comment1)); /* If the comment was started by 
  1547.     |begin_comment0| or |begin_comment1|, this overwrites the character
  1548.     after that. (We 
  1549.     have to make room for the semicolon.) If we want to cure this, we
  1550.     must do more copying. */
  1551.  
  1552. if(semi_pos > cur_buffer && *(semi_pos-1) == @'@@') /* A verbatim comment. */
  1553.     {
  1554.     *(semi_pos--) = @'@@';
  1555.     }
  1556.  
  1557. *semi_pos = @';'; /* This overwrites the beginning of the comment marker,
  1558.     or the previous~\.{@@} if there was one. */
  1559.  
  1560. @<Interchange line-break and semi@>;
  1561. }
  1562.  
  1563. @ As a kludge, if the Fortran line ends with an \.{@@/}, we interchange it
  1564. with the semicolon.
  1565. @<Interchange line-break...@>=
  1566.  
  1567. for(lp=semi_pos-1; lp>cur_buffer; lp--)
  1568.     {
  1569.     if(*lp == @' ') continue;
  1570.  
  1571.     if(*lp == @'/' && *(--lp) == @'@@')
  1572.         {
  1573.         *semi_pos = @' ';
  1574.         *lp = @';'; @~ *(lp+1) = @'@@'; @~ *(lp+2) = @'/';
  1575.         }
  1576.     break;
  1577.     }
  1578.  
  1579.  
  1580. @*1 \Ratfor\ input.
  1581.  
  1582. @<Part 1@>=@[
  1583.  
  1584. boolean 
  1585. rd_Ratfor(VOID)
  1586. {
  1587. register int c='\0'; // The character read.
  1588. register ASCII HUGE *k;  // Where next character goes.
  1589. boolean found_semi;
  1590.  
  1591. string_char = '\0';
  1592. in_char_string = NO;
  1593.  
  1594. @<Read free...@>@;
  1595.  
  1596. /* If we've read into a |new_module| command in column~1, 
  1597.     switch out of column mode. */
  1598. if(*cur_buffer == @'@@' && (limit==cur_buffer+1
  1599.         || *(cur_buffer+1)==@'*' || *(cur_buffer+1)==@' ')) 
  1600.     column_mode = NO;
  1601. else 
  1602.     @<Finish Ratfor line.@>;
  1603.  
  1604. return YES;
  1605. }
  1606.  
  1607. @
  1608. @<Common...@>=
  1609.  
  1610. IN_COMMON ASCII HUGE *last_pos;
  1611. IN_COMMON ASCII cmnt_char;
  1612.  
  1613. @ This section has to handle the completely free-form syntax as well as the
  1614. ``obviously continued'' syntax. The latter is more trouble than it's worth,
  1615. and isn't recommended.
  1616. @<Finish Ratfor line.@>=
  1617. {
  1618. last_was_continued = continuing_line;
  1619. continuing_line = NO;
  1620.  
  1621. if(limit==cur_buffer) 
  1622.     return YES;
  1623.  
  1624. limit[0] = limit[1] = limit[2] = limit[3] = @' ';
  1625.  
  1626. cmnt_char = @'#';
  1627.  
  1628. found_semi = found_text = text = NO; /* Start off assuming we're in comment
  1629. mode. */ 
  1630. semi_pos = NULL; /* |semi_pos| will record the position of a prospective
  1631. comment, to be begun by a semicolon. */
  1632.  
  1633. /* Scan the line. */
  1634. if(!free_form_input && !(*cur_buffer==@'@@' && *(cur_buffer+1)==@'#'))
  1635.    {
  1636.    for(lp=cur_buffer; ;)
  1637.     if(!skip_Rtext() || !skp_cmnt()) break;
  1638.  
  1639.     switch(*last_pos)
  1640.         {
  1641.         case @'+': case @'-': case @'*': case @'=':case @'{':case @'}':
  1642.         case @'^': case @'&': case @'|': case @'(': case @':':
  1643.         case @'>': case @'<': case @'[': case @',': case @'\\':
  1644.             continuing_line = YES;
  1645.             break; /* Line is continued. */
  1646.  
  1647. /* Ratfor's continuation character. */
  1648.         case @'_': 
  1649.                 if(last_pos > cur_buffer)
  1650.                     {
  1651.                     ASCII c;
  1652.                 
  1653.                     c = *(last_pos-1);
  1654.                     if(isAlpha(c) || isDigit(c) ||
  1655.                         c==@'_' || c==@'$') 
  1656.                             goto not_continuation;
  1657.                     }
  1658.                 *last_pos = @' '; 
  1659.                 continuing_line = YES;
  1660.                 break;
  1661.  
  1662.         default:
  1663.            not_continuation:
  1664.             if(!auto_semi) break;
  1665.  
  1666. /* If the last construction is a comment (|!text|) and there was actually
  1667. some text (|semi_pos!=NULL|),
  1668. replace ``\.{\slashstar}'' by |';'| and |begin_comment0| or
  1669. |begin_comment1|. */ 
  1670.            if(!text && semi_pos!=NULL) @<Fill in semi@>@;
  1671.            else if(found_text && limit > cur_buffer) *limit++ = @';';
  1672.                     /* Line ended. */ 
  1673.             break;
  1674.         }
  1675.    }
  1676. }
  1677.  
  1678. @ This nucleus skips over text in a line, stopping if it finds a comment. 
  1679.  
  1680. @d is_C_style_cmnt (C_style_cmnt = ((short_cmnt = Cpp_comments && *lp ==
  1681. @'/' && *(lp+1) == @'/')) || (*lp == @'/' && *(lp+1) == @'*') )
  1682.  
  1683. @d start_comment (is_C_style_cmnt || (*lp == (char)begin_comment0)
  1684.     || (*lp == (char)begin_comment1) ) 
  1685.  
  1686. @<Unused@>=
  1687.  
  1688. boolean skip_Ftext(VOID)
  1689. {
  1690. @<Begin skipping text@>;
  1691.  
  1692. /* If we haven't found a comment, set mode to text. */
  1693. if(!start_comment)
  1694.     {
  1695.     found_text = text = YES;
  1696.     ++lp;
  1697.     }
  1698.  
  1699. /* Scan to a comment. */
  1700. for(; lp<limit; ++lp)
  1701.     if(start_comment)
  1702.         if(++lp < limit) return YES;
  1703.  
  1704. return NO;
  1705. }
  1706.  
  1707. @ This nucleus skips over text in a line, stopping if it finds a comment. 
  1708.  
  1709. @d start_Rcomment ( (R_style_cmnt=BOOLEAN(*lp==cmnt_char && 
  1710.         *(lp+1)!=cmnt_char && *(lp-1)!=cmnt_char &&
  1711.         !(*(lp-1)==@'@@'))) || is_C_style_cmnt)
  1712.  
  1713. @<Part 1@>=@[
  1714. boolean skip_Rtext(VOID)
  1715. {
  1716. static ASCII blank = @' ';
  1717.  
  1718. @<Begin skipping text@>;
  1719.  
  1720. last_pos = ␣
  1721.  
  1722. /* If we haven't found a comment, set mode to text. */
  1723. if(!start_Rcomment)
  1724.     {
  1725.     found_text = text = YES;
  1726.     }
  1727.  
  1728. /* Scan to a comment. */
  1729. for(; lp<limit; lp++)
  1730.     {
  1731.     if(start_Rcomment)
  1732.         {
  1733.         if(R_style_cmnt)
  1734.             {
  1735.             *lp = cmnt_char = begin_comment0;
  1736.             *limit++ = @'*'; @~ *limit++ = @'/';
  1737.             }
  1738.         if(++lp < limit) return YES;
  1739.         }
  1740.  
  1741.     if(*lp != @' ' && *lp != tab_mark)
  1742.         last_pos = lp; /* Remember last non-blank position. */
  1743.     }
  1744.  
  1745. return NO;
  1746. }
  1747.  
  1748. @
  1749. @<Begin skipping ...@>=
  1750.  
  1751. if(scanning_C_cmnt) return YES; /* Make sure |skp_cmnt| is the first to be
  1752.                     executed. */
  1753.  
  1754. /* Start scanning at the current position. Skip over white space. */
  1755. for(; lp<limit; ++lp)
  1756.     if(!(*lp==@' ' || *lp==tab_mark)) break;
  1757.  
  1758. if(lp >= limit) return NO; /* The line was all white space. */
  1759.  
  1760. @ This nucleus skips over comments. */
  1761. @<Part 1@>=@[
  1762. boolean skp_cmnt(VOID)
  1763. {
  1764. /* If the last mode was text, remember the position for a possible semi. */
  1765. if(text) semi_pos = lp - 1;
  1766.  
  1767. text = NO;    /* We're in comment mode. */
  1768. scanning_C_cmnt = NO; /* Flag used to handle the next line properly if the
  1769.                 comment runs over. */
  1770.  
  1771. /* Scan to end of comment. */
  1772. if(C_style_cmnt)
  1773.     {
  1774.     if(short_cmnt)
  1775.         {
  1776.         lp = limit;
  1777.         return YES;
  1778.         }
  1779.  
  1780.     for(++lp; lp<limit; lp++)
  1781.         {
  1782.         if(*lp==(ASCII)begin_comment0 || *lp==(ASCII)begin_comment1)
  1783.             {
  1784.             *lp = @'c';
  1785.             continue;
  1786.             }
  1787.  
  1788.         if(*lp==@'*' && *(lp+1)==@'/')
  1789.             {
  1790.             lp += 2; /* Position after end of comment. */
  1791.             if(lp<=limit) return YES;
  1792.             }
  1793.         }
  1794.  
  1795.     scanning_C_cmnt = YES;
  1796.     }
  1797. else
  1798.     {
  1799.     for(++lp; lp<limit; lp++)
  1800.         if(*lp==(ASCII)begin_comment1)
  1801.              *lp = @'!';
  1802.  
  1803.     *(limit-2) = @'*'; @~ *(limit-1) = @'/';
  1804.     }
  1805.  
  1806. return NO;
  1807. }
  1808.         
  1809. @*1 Change files.
  1810. Now comes the problem of deciding which file to read from
  1811. next.  Recall that the actual text that \.{WEB} should process comes from
  1812. two streams: a |web_file|, which can contain possibly nested include
  1813. commands~`\.{@@i}', and a |change_file|, which should not contain includes.
  1814. The currently open files form a stack |prms|.  Each entry has two
  1815. components: |web| and |change|.  Thus, for any open web or include file,
  1816. there can be a separate change file is desired.  If no change file is
  1817. specified at any include level, the change file from the last level is used
  1818. instead.  Thus, one could have just one change file that changes entries in
  1819. both the |web_file| and all of the files included into the |web_file|.  The
  1820. boolean |changing| tells whether or not we're reading form the
  1821. |change_file|.
  1822.  
  1823. The line number of each open file is also kept for error reporting and
  1824. for the benefit of \.{TANGLE}, as are various other parameters.
  1825.  
  1826. |cur0_prms| points to the parameter component, either |web| or |change|,
  1827. being or just processed by |input_ln|. 
  1828.  
  1829. |cur_prms| points to the current stack level, including both |web| and
  1830. |change| components.
  1831.  
  1832. @d k0 cur0_prms->K0
  1833. @d found_at cur0_prms->Found_at
  1834. @d at_line cur0_prms->At_line
  1835.  
  1836. @ When |changing=NO|, the next line of |change_file| is kept in
  1837. |change_buffer|, for purposes of comparison with the next
  1838. line of |cur_file|. After the change file has been completely input, we
  1839. set |change_limit=change_buffer|, so that no further matches will be made.
  1840.  
  1841. Here's a shorthand expression for inequality between the two lines:
  1842.  
  1843. @d LINES_DONT_MATCH (change_limit-change_buffer != limit-cur_buffer ||
  1844.   STRNCMP(cur_buffer, change_buffer, PTR_DIFF(size_t,limit,cur_buffer)))
  1845.  
  1846. @d change_params prms[incl_depth].input_params
  1847.  
  1848. @ Procedure |prime_the_change_buffer| sets |change_buffer| in preparation
  1849. for the next matching operation. Since blank lines in the change file are
  1850. not used for matching, we have |(change_limit==NULL && !changing)| if and
  1851. only if the change file is exhausted. This procedure is called only
  1852. when |changing| is |YES|; hence error messages will be reported correctly.
  1853.  
  1854. @<Part 1@>=@[
  1855.  
  1856. SRTN 
  1857. prime_the_change_buffer(VOID)
  1858. {
  1859. INPUT_PARAMS input_params; // Saves incoming state.
  1860.  
  1861. input_params.Language = language;
  1862. input_params.Parsing_mode = parsing_mode;
  1863. input_params.Column_mode = column_mode;
  1864.  
  1865. language = change_params.Language;
  1866. parsing_mode = change_params.Parsing_mode;
  1867. column_mode = change_params.Column_mode;
  1868.  
  1869. change_limit = NULL; // This value will be used if the change file ends.
  1870.  
  1871.   @<Skip over comment lines in the change file; |goto done_priming@;| if end
  1872. of file@>; 
  1873.   @<Skip to the next nonblank line; |goto done_priming@;| if end of file@>;
  1874.  
  1875. done_priming:
  1876.     language = input_params.Language;
  1877.     parsing_mode = input_params.Parsing_mode;
  1878.     column_mode = input_params.Column_mode;
  1879.  
  1880. /* After we're done priming the change buffer, we will next read from the
  1881. input file. */
  1882. changing = NO;
  1883. cur0_prms = cur_prms.web;
  1884. }
  1885.  
  1886. @ While looking for a line that begins with~\.{@@x} in the change file,
  1887. we allow lines that begin with~\.{@@}, as long as they don't begin
  1888. with~\.{@@y} or~\.{@@z} (which would probably indicate that the change file is 
  1889. fouled up).
  1890.  
  1891. @<Skip over comment lines in the change file...@>=
  1892.  
  1893. WHILE() 
  1894.     {
  1895.     ASCII c;
  1896.  
  1897.     if (!input_ln(CHANGE_FILE)) 
  1898.         {
  1899.         change_limit = NULL;
  1900.         goto done_priming;
  1901.         }
  1902.  
  1903.     if (limit < cur_buffer+2) continue; 
  1904.         // There can't be an \.{@@}~command.
  1905.     if (cur_buffer[0] != @'@@') continue; 
  1906.         // Skip lines that don't start with~\.{@@}.
  1907.     @<Lowercasify |cur_buffer[1]|@>;
  1908.     @<Check for erroneous \.{@@i}@>;
  1909.     if ((c=cur_buffer[1])==@'x') break; // Found beginning of a change.
  1910.  
  1911.     if (c==@'y' || c==@'z') 
  1912.         {
  1913.         loc = cur_buffer+2; // Skip erroneous change.
  1914.         ERR_PRINT(C,"Where is the matching @@x?");
  1915. @.Where is the match...@>
  1916.         }
  1917.     else @<Process language-related change command@>@;
  1918.     }
  1919.  
  1920. @
  1921. @<Process language-related change...@>=
  1922. {
  1923. if(c==@'l') 
  1924.     if(limit == cur_buffer+2) 
  1925.         ERR_PRINT(C,"Missing language character after @@L");
  1926.     else c = cur_buffer[2];
  1927.  
  1928. switch(c)
  1929.     {
  1930.    @<|ASCII| cases for |C|@>:
  1931.     language = change_params.Language = 
  1932.         (cur_buffer[2] == @'+') ? C_PLUS_PLUS : C; 
  1933.     column_mode = change_params.Column_mode = NO;
  1934.     continue;
  1935.  
  1936.    @<|ASCII| cases for |RATFOR|@>:
  1937.     if(!RAT_OK("Language change ignored")) continue;
  1938.  
  1939.     language = change_params.Language = 
  1940.         (cur_buffer[2] == @'9') ? RATFOR_90 : RATFOR; 
  1941.     continue;
  1942.         
  1943.    @<|ASCII| cases for |FORTRAN|@>:
  1944.     language = change_params.Language = 
  1945.         (cur_buffer[2] == @'9') ? FORTRAN_90 : FORTRAN; 
  1946.     continue;
  1947.  
  1948.    @<|ASCII| cases for |LITERAL|@>:
  1949.     language = change_params.Language = LITERAL; continue;
  1950.  
  1951.    @<|ASCII| cases for |TEX|@>:
  1952.     language = change_params.Language = TEX; continue;
  1953.  
  1954.    case @'[': 
  1955.     column_mode = change_params.Column_mode = YES;continue;
  1956.  
  1957.    case @']':
  1958.     column_mode = change_params.Column_mode = NO;continue;
  1959.  
  1960.    default:
  1961.     loc = cur_buffer + 2;
  1962.     ERR_PRINT(C,"Invalid @@ command in change file");
  1963.     continue;
  1964.     }
  1965. }
  1966.  
  1967. @ This line of code makes~|"@@X"| equivalent to~|"@@x"| and so on.
  1968.  
  1969. @<Lowerc...@>=
  1970.  
  1971. if ((cur_buffer[1]>=@'X' && cur_buffer[1]<=@'Z') || cur_buffer[1]==@'I') 
  1972.     cur_buffer[1]+=@'z'-@'Z';
  1973.  
  1974. @ We do not allow includes in a change file, so as to avoid confusion.
  1975.  
  1976. @<Check for erron...@>=
  1977.  
  1978. if (cur_buffer[1]==@'i') 
  1979.     {
  1980.     loc = cur_buffer + 2;
  1981.     ERR_PRINT(C,"Sorry, no includes allowed in change file");
  1982. @.No includes allowed...@>
  1983.     }
  1984.  
  1985. @ Here we are looking at lines following the~\.{@@x}.
  1986.  
  1987. @<Skip to the next nonblank line...@>=
  1988.  
  1989. do 
  1990.     {
  1991.     if (!input_ln(CHANGE_FILE)) 
  1992.         {
  1993.         ERR_PRINT(C,"Change file ended after @@x");
  1994. @.Change file ended...@>
  1995.         change_limit = NULL;
  1996.         goto done_priming;
  1997.         }
  1998.     } 
  1999. while (limit==cur_buffer);
  2000.  
  2001. @ The following procedure is used to see if the next change entry should go
  2002. into effect; it is called only when |changing| is~|NO|.  The idea is to
  2003. test whether or not the current contents of |cur_buffer| matches the
  2004. current contents of |change_buffer|.  If not, there's nothing more to do;
  2005. but if so, a change is called for: All of the text down to the~\.{@@y} is
  2006. supposed to match. An error message is issued if any discrepancy is found.
  2007. Then the procedure prepares to read the next line from |change_file|.
  2008.  
  2009. @<Part 1@>=@[
  2010.  
  2011. SRTN 
  2012. chk_change(VOID) // Switches to |change_file| if the buffers match.
  2013. {
  2014. int n = 0; // The number of discrepancies found.
  2015.  
  2016. if(LINES_DONT_MATCH) return;  // Didn't match a change.
  2017.  
  2018. WHILE() 
  2019.     {
  2020.     changing = YES; prn_where = YES; 
  2021.  
  2022.     if (!input_ln(CHANGE_FILE)) 
  2023.         {
  2024.         ERR_PRINT(C,"Change file ended before @@y");
  2025. @.Change file ended...@>
  2026.         change_limit = NULL; changing = NO; prn_where = YES;
  2027.         return;
  2028.         }
  2029.  
  2030.     @<If the current line starts with \.{@@y},
  2031.           report any discrepancies and |return|@>;@/
  2032.     changing = NO; prn_where = YES; 
  2033.  
  2034.     while(!input_ln(INPUT_FILE)) 
  2035.         { /* Pop the stack or quit. */
  2036.         if (incl_depth==WEB_FILE) 
  2037.             {
  2038.                 ERR_PRINT(C,"WEB file ended during a change");
  2039. @.WEB file ended...@>
  2040.                 input_has_ended = YES; return;
  2041.                 }
  2042.  
  2043.         incl_depth--; prn_where = YES; 
  2044.         }
  2045.  
  2046.     if(LINES_DONT_MATCH) n++;
  2047.     }
  2048. }
  2049.  
  2050. @<If the current line starts with \.{@@y}...@>=
  2051.  
  2052. if (limit>cur_buffer+1 && cur_buffer[0]==@'@@') 
  2053.     {
  2054.     @<Lowerc...@>;
  2055.     @<Check for erron...@>;
  2056.  
  2057.     if (cur_buffer[1]==@'x' || cur_buffer[1]==@'z') 
  2058.         {
  2059.         loc=cur_buffer+2; 
  2060.         ERR_PRINT(C,"Where is the matching @@y?");
  2061. @.Where is the match...@>
  2062.         }
  2063.     else if (cur_buffer[1]==@'y') 
  2064.         {
  2065.         if (n>0) 
  2066.             {
  2067.             loc = cur_buffer + 2;
  2068.     ERR_PRINT(C,"Hmm... some of the preceding lines failed to match");
  2069. @.Hmm... some of the preceding...@>
  2070.             }
  2071.         return;
  2072.         }
  2073.     }
  2074.  
  2075. @ The |rst_input| procedure, which gets \.{WEAVE} ready to read the
  2076. user's \.{WEB} input, is used at the beginning of both phases one and two.
  2077.  
  2078. @d ABORT_ON_ERROR YES
  2079. @d DONT_ABORT_ON_ERROR NO
  2080.  
  2081. @d FCLOSE(file_ptr) if(file_ptr != stdin) fclose(file_ptr); file_ptr = NULL@;
  2082.  
  2083. @<Part 2@>=@[
  2084.  
  2085. SRTN 
  2086. rst_input(VOID)
  2087. {
  2088. FCLOSE(prms[WEB_FILE].web.File);
  2089. FCLOSE(prms[WEB_FILE].change.File);
  2090.  
  2091. ini_input_prms(WEB_FILE,(outer_char HUGE *)"",ABORT_ON_ERROR);
  2092. @<Reset cur\_buffer@>;
  2093.  
  2094. @<Initialize change buffer@>@;
  2095.  
  2096. incl_depth = WEB_FILE;
  2097. input_has_ended = NO;
  2098. }
  2099.  
  2100. @ Prepare to read from an input buffer.  It positions the~|loc|
  2101. after~|limit| so that a new line will be sure to be read next.
  2102.  
  2103. @<Reset cur\_buffer@>=
  2104.  
  2105. limit=cur_buffer; loc=cur_buffer+1; cur_buffer[0]=@' '@;
  2106.  
  2107. @
  2108. @<Glob...@>=
  2109.  
  2110. IN_COMMON INPUT_PARAMS change_params0
  2111. #if(part == 0 || part == 1)
  2112.      = {FORTRAN,OUTER,NO}
  2113. #endif
  2114.     ;
  2115.  
  2116. @
  2117. @<Initialize change...@>=
  2118. {
  2119.   cur0_prms = cur_prms.change;
  2120.   changing = YES;
  2121.   change_params = change_params0; // Reset to default values.
  2122.   prime_the_change_buffer(); 
  2123.   @<Reset cur\_buffer@>;
  2124. }
  2125.  
  2126. @*1 Initializing input files.
  2127. Whenever a new input file is opened, we must
  2128. allocate a buffer, and initialize parameters.  This routine initializes
  2129. both the \WEB\ file and the change file for a particular include level.
  2130. @^system dependencies@>
  2131.  
  2132. @
  2133. @<Initialize static...@>=
  2134. {
  2135. ALLOC(INPUT_PRMS, prms, ABBREV(max_include_depth), max_include_depth, 1);
  2136. }
  2137.  
  2138. @ Although the |prms| array had to be allocated before scanning the command
  2139. line (with the default |DFLT_MAX_INCLUDE_DEPTH|), the \.{-y}~option might
  2140. have requested a change in size.   If so, we reallocate the array.
  2141.  
  2142. @<Allocate dynamic...@>=
  2143. {
  2144. BUF_SIZE cur_prms_units;
  2145.  
  2146. alloc(OC("id"), &cur_prms_units, 0, -1); // What is current allocation?
  2147.  
  2148. if(cur_prms_units != DFLT_MAX_INCLUDE_DEPTH)
  2149.     prms = (INPUT_PRMS *)REALLOC(prms, 
  2150.         (cur_prms_units + 1)*sizeof(INPUT_PRMS),
  2151.         (DFLT_MAX_INCLUDE_DEPTH+1)*sizeof(INPUT_PRMS)); 
  2152. }
  2153.  
  2154. @
  2155. @<Part 2@>=@[
  2156.  
  2157. boolean 
  2158. ini_input_prms FCN((ilevel,path,quit_on_error))
  2159.     int ilevel C0("Present level of input files.")@;
  2160.     outer_char HUGE *path C0("Colon-delimited path list")@;
  2161.     boolean quit_on_error C1("Abort flag.")@;
  2162. {
  2163. INPUT_PRMS HUGE *p = &prms[ilevel];
  2164. CUR_PRMS old_prms;
  2165. boolean web_level = BOOLEAN(ilevel==WEB_FILE);
  2166.  
  2167. old_prms = cur_prms; // Save state in case of error.
  2168.  
  2169. /* Initialize this here in case of error messages. */
  2170. cur_prms.web = &p->web;
  2171. cur_prms.change = &p->change;
  2172.  
  2173. /* Initialize both input and change files; abort if error. */
  2174. if(!(ini0_input_prms(cur_prms.web,path,OC("input"),quit_on_error,
  2175.     (SEQUENCES *)(web_level ? wt_style.input_ext.web :
  2176.         wt_style.input_ext.hweb)) &&   
  2177.      ini0_input_prms(cur_prms.change,path,OC("change"),quit_on_error,
  2178.     (SEQUENCES *)(web_level ? wt_style.input_ext.change :
  2179.             wt_style.input_ext.hchange)))) 
  2180.         {
  2181.         cur_prms = old_prms; // Restore old state if error.
  2182.         return NO; 
  2183.         }
  2184.  
  2185. /* For definiteness we set |cur0_prms| to the web input file. */ 
  2186. cur0_prms = cur_prms.web;
  2187. loc = cur_buffer;
  2188.  
  2189. /* Remember the name of the include file, for use by \WEAVE. */
  2190. STRCPY(this_include_file,web_level ? 
  2191.     (CONST outer_char *)"" : p->web.File_name);
  2192.  
  2193. return YES;
  2194. }
  2195.  
  2196. @ This function initializes either a web file or a change file.
  2197. @<Part 2@>=@[
  2198.  
  2199. boolean 
  2200. ini0_input_prms FCN((p0,path,file_type,quit_on_error,extensions))
  2201.     INPUT_PRMS0 HUGE *p0 C0("")@;
  2202.     outer_char HUGE *path C0("")@;
  2203.     outer_char file_type[] C0("")@;
  2204.     boolean quit_on_error C0("")@;
  2205.     SEQUENCES *extensions C1("")@;
  2206. {
  2207. cur0_prms = p0; /* In case of error messages. */
  2208.  
  2209. /* If we've never opened this level before, allocate appropriate space. */
  2210. if(p0->Buffer == NULL)
  2211.     {
  2212.     ALLOC(ASCII,p0->Buffer,ABBREV(buf_size),buf_size,0);
  2213.     p0->Buffer_end = p0->Buffer + buf_size - 2;
  2214.     }
  2215.  
  2216. /* Initialize location pointers and flags. */
  2217. loc = p0->Limit = p0->Buffer;
  2218. p0->Line = 0;
  2219. p0->Num_in_buffer = 0;
  2220. p0->Scanning_C_cmnt = p0->Last_was_empty
  2221.   = p0->Continuing_line = p0->Last_was_continued = NO;
  2222.  
  2223. /* --- Open file (but not if it's already open) --- */
  2224. if(!p0->File && 
  2225.    (p0->File=xopen(p0->File_name,path,extensions,OC("r"))) == NULL)
  2226.     {
  2227.     if(quit_on_error)
  2228.         {
  2229.         char temp[100];
  2230.  
  2231.         sprintf(temp,"!! Can't open %s%s file ",
  2232.             *p0->File_name ? "" : "(null) ", (char *)file_type);
  2233.         FATAL(C, temp,*p0->File_name ? p0->File_name :
  2234.             wt_style.null_file_name); 
  2235. @.Can't open input file@>
  2236. @.Can't open change file@>
  2237.         }
  2238.     else 
  2239.         {
  2240.         err_print(C,"Can't open include %s file \"%s\"",
  2241.             file_type,p0->File_name);
  2242.         }
  2243.  
  2244.     return NO; // Couldn't open file.
  2245.     }
  2246.  
  2247. return YES; // Opened file successfully.
  2248. }
  2249.  
  2250. @ This general routine attempts to open a file by searching through a path.
  2251. @<Part 2@>=@[
  2252.  
  2253. FILE *
  2254. xopen FCN((file_name,path,extensions,iomode))
  2255.     outer_char *file_name C0("")@;
  2256.     outer_char HUGE *path C0("")@;
  2257.     SEQUENCES *extensions C0("")@;
  2258.     CONST outer_char *iomode C1("")@;
  2259. {
  2260. outer_char HUGE *p1;
  2261. FILE *file_ptr;
  2262. outer_char total_name[2*MAX_FILE_NAME_LENGTH];
  2263. outer_char prefix_end_str[2];
  2264.  
  2265. if(!*file_name)    
  2266.     return FOPEN(wt_style.null_file_name,iomode);
  2267.  
  2268. if(*iomode=='r' && STRCMP(file_name,"stdin")==0) 
  2269.     return stdin;
  2270. else if(*iomode=='w' && STRCMP(file_name,"stdout")==0) 
  2271.     return stdout;
  2272.  
  2273. /* If there's no path, we just try to look in the present directory. */
  2274. if(!(path && *path)) 
  2275.     return x0open(file_name,extensions,iomode);
  2276.  
  2277. prefix_end_str[0] = prefix_end_char;
  2278. prefix_end_str[1] = '\0';
  2279.  
  2280. /* Hunt through the path. */
  2281. while((p1=(outer_char HUGE *)STRCHR(path,':')) != NULL)
  2282.     {
  2283.     *p1 = '\0';
  2284.     STRCPY(total_name,path);
  2285.     *p1 = ':';
  2286.     if(*(p1-1) != prefix_end_char) 
  2287.         STRCAT(total_name,prefix_end_str);
  2288.     STRCAT(total_name,file_name);
  2289.     if((file_ptr=x0open(total_name,extensions,iomode)) != NULL) 
  2290.         return file_ptr;
  2291.     path = p1 + 1;
  2292.     }
  2293.  
  2294. return NULL; // Couldn't open any file.
  2295. }
  2296.  
  2297. @ Here we attempt to open a file with a specific file name.  When the
  2298. \.{-e}~option is in effect, we must scan through the list of 
  2299. possible extensions in order to ascertain whether one of the possible files
  2300. can be opened.  
  2301. @<Part 2@>=@[
  2302.  
  2303. FILE *
  2304. x0open FCN((file_name,extensions,iomode))
  2305.     outer_char *file_name C0("")@;
  2306.     SEQUENCES *extensions C0("")@;
  2307.     CONST outer_char *iomode C1("")@;
  2308. {
  2309. int k;
  2310.  
  2311. /* If it has a dot in it, it's always interpreted literally.  If it didn't
  2312. have a dot, then if the `\.{-e}'~option isn't in effect it will already
  2313. have had the `\.{.web}' extension added to it, so it will now have a dot.
  2314. (So presumably the last option of the \&{if} is redundant?) */
  2315. if(STRCHR(file_name,wt_style.ext_delimiter)!=NULL || !try_extensions) 
  2316.     return FOPEN(file_name,iomode);
  2317.  
  2318. /* At this point, the file name didn't have a dot and the `\.{-e}'~option
  2319. is in effect.  Make trial file names from the list of extensions and try to
  2320. open them one at a time until one matches. */
  2321. for(k=0; k<extensions->n; k++)
  2322.     {
  2323.     outer_char full_name[MAX_FILE_NAME_LENGTH];
  2324.     FILE *file_ptr;
  2325.  
  2326.     if(NSPRINTF(full_name,"%s%c%s",
  2327.         file_name,wt_style.ext_delimiter,extensions->string[k]) >= 
  2328.         MAX_FILE_NAME_LENGTH) OVERFLW("Extended file name","");
  2329.  
  2330.     file_ptr = FOPEN(full_name,iomode);
  2331.  
  2332.     if(file_ptr)
  2333.         {
  2334.         STRCPY(file_name,full_name); /* If we don't do this, the
  2335. information messages aren't as informative as they could be. */
  2336.         return file_ptr; // Opened a file successfully.
  2337.         }
  2338.     }
  2339.  
  2340. return NULL; // Failed to open the file.
  2341. }
  2342.  
  2343. @ The |get_line| procedure is called when |loc>limit|; it
  2344. puts the next line of merged input into the buffer and updates the other
  2345. variables appropriately. A space is placed at the right end of the line.
  2346. This procedure returns |!input_has_ended| because we often want to check
  2347. the value of that variable after calling the procedure.
  2348.  
  2349. If we've just changed from the |cur_file| to the |change_file|, or if
  2350. the |cur_file| has changed, we tell \.{TANGLE} to print this
  2351. information in the output file by means of the |prn_where| flag.
  2352.  
  2353. @<Common...@>=
  2354.  
  2355. IN_COMMON sixteen_bits module_count; // The current module number.
  2356.  
  2357. IN_COMMON BUF_SIZE max_modules; // Greater than the total number of modules.
  2358. IN_COMMON boolean HUGE *chngd_module; // Dynamic array: Is the module changed?
  2359.  
  2360. IN_COMMON boolean prn_where CSET(NO); 
  2361.     // Tells \.{TANGLE} to print line and file info
  2362. IN_COMMON boolean return_toggle CSET(NO); 
  2363.     /* Switch used for sending back an `\.{@@i}' when |toggle_includes|
  2364.     is on. */ 
  2365. IN_COMMON boolean toggling CSET(NO); // In the middle of a toggle include?
  2366. IN_COMMON boolean popped CSET(NO); // After popping the stack?
  2367. IN_COMMON boolean no_more_input CSET(NO); 
  2368.     // Temporary replacement for |input_has_ended|. 
  2369.  
  2370. IN_COMMON int tracing; // For debugging \.{WEAVE}.
  2371.  
  2372. @
  2373. @<Allocate dynamic...@>=
  2374.  
  2375. ALLOC(boolean,chngd_module,ABBREV(max_modules),max_modules,0);
  2376.  
  2377. @*1 Getting the next line.
  2378. Input the next line.
  2379.  
  2380. @<Part 2@>=@[
  2381.  
  2382. boolean 
  2383. get_line(VOID)
  2384. {
  2385. if(return_toggle) 
  2386.     @<Restore next line and |goto process_line@;|@>@;
  2387.  
  2388. restart:
  2389.   if (changing) 
  2390.     chngd_module[module_count] = YES;
  2391.   else 
  2392.     @<Read from |cur_file| and maybe turn on |changing|@>;
  2393.  
  2394. if (changing) 
  2395.     {
  2396.     @<Read from |change_file| and maybe turn off |changing|@>;
  2397.  
  2398.     if (!changing) 
  2399.         {
  2400.         chngd_module[module_count]=YES; goto restart;
  2401.         }
  2402.     }
  2403.  
  2404. process_line:
  2405.   loc = cur_buffer; *limit = @' ';
  2406.  
  2407. if(return_toggle) 
  2408.     @<Send back a toggle command@>@;
  2409.  
  2410. if (*cur_buffer==@'@@')
  2411.     switch(*(cur_buffer+1))
  2412.         {
  2413.        case @'i':
  2414.        case @'I':
  2415.         @<Process an \.{\AT!i} command@>@;
  2416.         break;
  2417.         }
  2418.  
  2419. return (boolean)(!input_has_ended);
  2420. }
  2421.  
  2422. @
  2423. @<Process an \.{\AT!i}...@>=
  2424.  
  2425. if( *(cur_buffer+1)==@'I' && (skip_includes ||
  2426.         (program==weave && toggle_includes && CUR_FILE==WEB_FILE)) )
  2427.     {
  2428.     if(skip_includes) 
  2429.         {
  2430.         loc = limit + 1; // Turn whole line into null.
  2431.         goto restart;
  2432.         }
  2433.     else if(toggle_includes && CUR_FILE==WEB_FILE)
  2434.         {
  2435.         toggling = YES;
  2436.         @<Send back a tog...@>@;
  2437.         }
  2438.     }
  2439. else @<Push stack and go to |restart|@>@;
  2440.  
  2441. @ Process an \.{\AT!o} command.
  2442. @<Unused@>=
  2443. {
  2444. outer_char new_file_name[MAX_FILE_NAME_LENGTH];
  2445.  
  2446. if(program==weave || phase==1) goto restart;
  2447.  
  2448. loc = cur_buffer + 2; // Position after \.{@@o}.
  2449.  
  2450. /* Look for the name of the new output file. */
  2451. if(!get_fname(new_file_name))
  2452.     ERR_PRINT(C,"Output file name not given");
  2453. @.Output file name not given@>
  2454. else
  2455.     {
  2456.     fclose(out_file);
  2457.     strcpy(output_file_name,new_file_name);
  2458.     open_out();
  2459.     }
  2460.  
  2461. goto restart;
  2462. }
  2463.  
  2464. @ An~`\.{@@i}'---|toggle_output|--- is returned before and after
  2465. an include line that is to be processed but not output (when
  2466. |toggle_includes == YES|). Before we send it back, we must save the line in
  2467. the buffer, because we must process it the next time we come back in for a
  2468. line. 
  2469.  
  2470. @<Send back a toggle...@>=
  2471. {
  2472. return_toggle = YES;
  2473. stored_line_length = PTR_DIFF(size_t,limit,cur_buffer);
  2474. stored_line = GET_MEM("stored_line",stored_line_length+1,ASCII); /* We
  2475.     allocate one extra so we don't have to handle the special case of a
  2476.     zero-length line, for which |FREE_MEM| would complain. */
  2477. STRNCPY(stored_line,cur_buffer,stored_line_length);
  2478. loc = limit = cur_buffer;
  2479. *limit++ = @'@@'; @~ *limit++ = '\001'; @~ *limit = @' '; 
  2480. return YES;
  2481. }
  2482.  
  2483. @ We need a place to hold that line.
  2484. @<Other def...@>=
  2485.  
  2486. IN_COMMON int stored_line_length;
  2487. IN_COMMON ASCII HUGE *stored_line; // Allocated dynamically.
  2488.  
  2489. @ When we come back after sending a |toggle_include| command, we must get
  2490. back the previous line that was in the buffer.
  2491.  
  2492. @<Restore next line...@>=
  2493. {
  2494. return_toggle = NO;
  2495. if(popped && no_more_input) {input_has_ended = YES; return NO;}
  2496. STRNCPY(cur_buffer,stored_line,stored_line_length);
  2497. FREE_MEM(stored_line,"stored_line",stored_line_length,ASCII);
  2498. limit = cur_buffer + stored_line_length;
  2499. *limit = @' ';
  2500. if(popped) 
  2501.     {
  2502.     popped = NO;
  2503.     goto popped_stack;
  2504.     }
  2505. else if(toggling) {goto push_stack;}
  2506. else goto process_line;
  2507. }
  2508.  
  2509. @ When an \.{@@i}~line is found in the |cur_file|, we must temporarily
  2510. stop reading it and start reading from the named include file.  The
  2511. \.{@@i}~line should give a complete file name with or without~\.{"..."};
  2512. \.{WEB} will not look for include files in standard directories as the
  2513. \cee\ preprocessor does when a \.{\#include <filename>} line is found.
  2514. Also, the file name should only contain visible ASCII characters,
  2515. since the characters are translated into ASCII and back again.
  2516.  
  2517. Optionally, one may also name a new change file.  As for the command line,
  2518. the format is \.{@@i include\_file [change\_file]}.
  2519.  
  2520. The next arrays hold the last and current names of the include file, for
  2521. use by \WEAVE\ in printing out what file goes with what section.
  2522.  
  2523. @<Other...@>=
  2524.  
  2525. IN_COMMON outer_char last_include_file[MAX_FILE_NAME_LENGTH] CSET(""), 
  2526.     this_include_file[MAX_FILE_NAME_LENGTH] CSET("");
  2527.  
  2528. @
  2529. @<Push stack and...@>= 
  2530. @{
  2531. boolean found_include;
  2532.  
  2533. @b
  2534. push_stack:
  2535.  loc = cur_buffer + 2; // Position after \.{@@i}.
  2536.   
  2537. if(++incl_depth >= (int)max_include_depth)
  2538.     {
  2539.     incl_depth--;
  2540.     err_print(C, "Too many nested includes; %d allowed.  \
  2541. Increase with `-yid'.", max_include_depth); 
  2542. @.Too many nested includes@>
  2543.     goto restart;
  2544.     }
  2545.  
  2546. /* Look for the name of a \WEB\ file to include. */
  2547. if(!(found_include=get_fname(cur_file_name))) 
  2548.     ERR_PRINT(C,"Include file name not given");
  2549. @.Include file name not given@>
  2550.   else
  2551.     { // Found a \WEB\ file name; now look for change file name.
  2552.     if(skip_ifiles)
  2553.         @<Skip an already-included file and |goto restart@;|@>@;
  2554.     
  2555.     if(!get_fname(change_file_name)) 
  2556.         { /* No change file name specified; obtain it from the
  2557. last level. */
  2558.         INPUT_PRMS *p_lower = &prms[incl_depth-1];
  2559.         INPUT_PRMS0 *p0_lower = &p_lower->change;
  2560.  
  2561.         STRCPY(change_file_name,p0_lower->File_name);
  2562.         change_file = p0_lower->File;
  2563.         change_params = p_lower->input_params;
  2564.         }
  2565.     }
  2566.  
  2567. if(found_include)
  2568.     {
  2569.     if(ini_input_prms(CUR_FILE,incl.list,DONT_ABORT_ON_ERROR))
  2570.         {
  2571.         if(cur_prms.change->File != prms[incl_depth-1].change.File)
  2572.             @<Initialize change...@>@; // New change file.
  2573.         else *cur_prms.change = prms[incl_depth-1].change;
  2574.             // Still using the old change file.
  2575.  
  2576.         cur_line = 0;
  2577.         prn_where = YES;
  2578.         CLR_PRINTF(include_file,(" (%s", (char *)cur_file_name)); 
  2579. /* Tell the terminal where we're reading from. */
  2580.         }
  2581.     else 
  2582.         { /* Failed to open include file. */
  2583.             incl_depth--;
  2584.         }
  2585.      }
  2586.  
  2587. goto restart;
  2588. }
  2589.  
  2590. @ When option \.{-j} is used, include files that have already been included
  2591. are not included again.
  2592.  
  2593. @<Glob...@>=
  2594.  
  2595. IN_COMMON BUF_SIZE max_ifiles;
  2596. IN_COMMON int num_ifiles;
  2597. IN_COMMON outer_char **ifiles; // Dynamic array of unique include file names.
  2598.  
  2599. @
  2600. @<Allocate dynamic...@>=
  2601. {
  2602. ALLOC(outer_char *, ifiles, ABBREV(max_ifiles), max_ifiles, 0);
  2603. }
  2604.  
  2605. @
  2606. @<Skip an already...@>=
  2607. {
  2608. int j;
  2609.  
  2610. for(j=0; j<num_ifiles; j++)
  2611.     if(STRCMP(ifiles[j], cur_file_name) == 0)
  2612.         {
  2613.         incl_depth--;
  2614.         goto restart;
  2615.         }
  2616.  
  2617. if(num_ifiles == (int)max_ifiles)
  2618.     OVERFLW("unique include file names", ABBREV(max_ifiles));
  2619.     
  2620. if(phase == 1)
  2621.  {
  2622.  ifiles[num_ifiles] = GET_MEM("ifile", STRLEN(cur_file_name) + 1, outer_char);
  2623.  STRCPY(ifiles[num_ifiles], cur_file_name);
  2624.  }
  2625.  
  2626. num_ifiles++;
  2627. }
  2628.  
  2629. @ Read a file name from an \.{@@i}~line.  One annoyance is the possibility
  2630. of embedded comments.
  2631. @<Part 2@>=@[
  2632.  
  2633. boolean 
  2634. get_fname FCN((file_name))
  2635.     outer_char HUGE *file_name C1("")@;
  2636. {
  2637. ASCII HUGE *j;
  2638. outer_char HUGE *k;
  2639.  
  2640. skip_white:
  2641.  
  2642. /* Skip over leading white space and optional quotes. */
  2643. while (loc<=limit && (*loc==@' '||*loc==@'\t'||*loc==@'"')) loc++;
  2644.  
  2645. /* If we've hit the end of the buffer, we're done, and we didn't find
  2646. anything. */ 
  2647. if(loc >= limit) return NO;
  2648.  
  2649. @<Process comment in include line@>@;
  2650.  
  2651. k = file_name; j=loc;
  2652.  
  2653. /* Terminate scan by white space or quotes. */
  2654. while (*loc!=@' '&&*loc!=@'\t'&&*loc!=@'"') *k++ = XCHR(*loc++);
  2655. TERMINATE(k,0);
  2656.  
  2657. /* On certain systems, underscores aren't allowed in file names. */
  2658. @#ifdef MVS
  2659. for (k = file_name; *k; k++)
  2660.    if (*k == '_')
  2661.      *k = '@@';
  2662. @#endif // |MVS|
  2663.  
  2664. add_prefix(file_name); // Attach a directory prefix.
  2665.  
  2666. return YES;
  2667. }
  2668.  
  2669. @
  2670. @<Process comment in include...@>=
  2671. {
  2672. if(*loc==@'/')
  2673.     {
  2674.     if(*(loc+1) == @'/')
  2675.         { /* Short comment */
  2676.         loc = limit+1;
  2677.         return NO;
  2678.         }
  2679.  
  2680.     if(*(loc+1) == @'*')
  2681.         { /* Long comment */
  2682.         for(loc += 2; loc <= limit; loc++)
  2683.             if(*loc==@'*' && *(loc+1)==@'/') 
  2684.                 {
  2685.                 loc += 2;
  2686.                 goto skip_white;
  2687.                 }
  2688.  
  2689.         ERR_PRINT(C,"Can't continue comments on @@i lines");
  2690.         }
  2691.     }
  2692. }
  2693.  
  2694. @ If the include file name has no directory prefix and there was a web file
  2695. prefix, then attach the latter to the former.
  2696.  
  2697. @<Part 2@>=@[
  2698.  
  2699. SRTN 
  2700. add_prefix FCN((file_name))
  2701.     outer_char HUGE *file_name C1("")@;
  2702. {
  2703. outer_char temp[MAX_FILE_NAME_LENGTH];
  2704.  
  2705. xpnd_file_name(file_name); // Expand possible environmental prefix.
  2706.  
  2707. if(*wbprefix && STRRCHR(file_name,prefix_end_char)==NULL)
  2708.     {
  2709.     if(NSPRINTF(temp,"%s%s",wbprefix,file_name) >=
  2710.         MAX_FILE_NAME_LENGTH) OVERFLW("Path/file_name","");
  2711.  
  2712.     STRCPY(file_name,temp);
  2713.     }
  2714. }
  2715.  
  2716. @ The following function emulates VMS' expansions of logical names. It's
  2717. assumed that the file names in an \.{@@i} command look like \.{ENV:name},
  2718. where \.{ENV}~is an environmental variable defined with \.{setenv}.
  2719. \.{ENV}~should expand to a subdirectory, without a trailing slash.
  2720. @<Part 2@>=@[
  2721.  
  2722. SRTN 
  2723. xpnd_file_name FCN((name))
  2724.     outer_char HUGE *name C1("")@;
  2725. {
  2726. outer_char temp[MAX_FILE_NAME_LENGTH],env_prefix[MAX_FILE_NAME_LENGTH];
  2727. outer_char *pc,*px,*qx;
  2728. CONST char *separator;
  2729. int n;
  2730. boolean expanded;
  2731.  
  2732. #ifdef vms
  2733.     return; // VAX/VMS does its own expansions of logical names.
  2734. #else
  2735.  
  2736. #if !HAVE_GETENV
  2737.    err_print(C,"Sorry, this machine doesn't support environment variables");
  2738.    return;
  2739. #else
  2740.  
  2741. /* Is there a logical name prefix? */
  2742. if((pc=OC(STRCHR(name,':'))) == NULL) return;
  2743.  
  2744. /* Isolate the prefix. */
  2745. STRNCPY(env_prefix,name,n=PTR_DIFF(int, pc, name));
  2746. TERMINATE(env_prefix,n);
  2747.  
  2748. /* Recursively expand the prefix. |px|~will have the last non-null name. */
  2749. expanded = NO;
  2750. for(px=env_prefix; px; px=qx)
  2751.     {
  2752.     if((qx=GETENV(px)) == NULL) break;
  2753.     if(STRCMP(qx,env_prefix) == 0)
  2754.         {
  2755.         err_print(C,"Infinite recursion in definition of \
  2756. environmental variable \"%s\"",px=env_prefix);
  2757.         expanded = NO;
  2758.         break;
  2759.         }
  2760.     expanded = YES; /* At least one expansion of the logical name was
  2761. made. */
  2762.     }
  2763.  
  2764. /* If the prefix wasn't expandable, or if it turned into an infinite
  2765. recursion, do nothing. */
  2766. if(!expanded) return;
  2767.  
  2768. /* Otherwise, |px|~has the last expansion of the name. */
  2769. separator = 
  2770. #ifdef ibmpc
  2771.     "\\"
  2772. #else
  2773.     "/"
  2774. #endif /* |ibmpc| */
  2775. ;
  2776. if(NSPRINTF(temp,"%s%s%s",px,separator,pc+1) >=
  2777.         MAX_FILE_NAME_LENGTH) 
  2778.     OVERFLW("Expandable file name","");
  2779. STRCPY(name,temp);
  2780.  
  2781. #endif // |HAVE_GETENV|
  2782. #endif // |vms|
  2783. }
  2784.  
  2785. @<Read from |cur_file|...@>= 
  2786. {
  2787. popped = NO;
  2788.  
  2789. if(cur_prms.web->Line == 0)
  2790.     if(ignored_cmnts()) goto fin_read;
  2791.     else goto pop_or_quit;
  2792.  
  2793. while (!input_ln(INPUT_FILE)) 
  2794.     { /* pop the stack or quit */
  2795.     pop_or_quit:
  2796.         prn_where=YES;
  2797.  
  2798.     if(incl_depth==WEB_FILE) {input_has_ended = YES; break;}
  2799.     else 
  2800.         { /* Tidy up the include level we're about to leave. */
  2801.         if(cur_prms.web->File != prms[incl_depth-1].web.File)
  2802.             {FCLOSE(cur_prms.web->File);}
  2803.  
  2804.         if(cur_prms.change->File != prms[incl_depth-1].change.File)
  2805.             {FCLOSE(cur_prms.change->File);}
  2806.         else prms[incl_depth-1].change = *cur_prms.change;
  2807.     
  2808.         cur0_prms->File = NULL; 
  2809.         CLR_PRINTF(include_file,(")")); /* Tell the terminal we're
  2810. finished with this web input file. */
  2811.  
  2812. /* Pop the stack here. */        
  2813.         incl_depth--;
  2814.         cur_prms.web = &prms[incl_depth].web;
  2815.         cur_prms.change = &prms[incl_depth].change;
  2816.  
  2817.         if(incl_depth == WEB_FILE && toggle_includes && toggling)
  2818.                 {
  2819.                 toggling = NO; 
  2820.                 return_toggle = YES;
  2821.                 }
  2822.  
  2823.         STRCPY(this_include_file,(incl_depth==WEB_FILE) ? 
  2824.             (CONST outer_char *)"" : cur_file_name);
  2825.  
  2826.         } 
  2827.       }
  2828.  
  2829. fin_read:
  2830. if(return_toggle)
  2831.     {
  2832.     no_more_input = input_has_ended;
  2833.     input_has_ended = NO;
  2834.     popped = YES;
  2835.     @<Send back a tog...@>@;
  2836.     }
  2837.  
  2838. popped_stack:
  2839.  if (!input_has_ended)
  2840.   if (limit==change_limit-change_buffer+cur_buffer)
  2841.     if (cur_buffer[0]==change_buffer[0])
  2842.       if (change_limit>change_buffer) chk_change();
  2843. }
  2844.  
  2845. @ Here we look at the beginning material in the include file.  If the file
  2846. begins with~`\.{@@z}', then everything up to and including a line beginning
  2847. with~`\.{@@x}' is skipped.  
  2848.  
  2849. @<Part 2@>=@[
  2850.  
  2851. boolean 
  2852. ignored_cmnts(VOID)
  2853. {
  2854. boolean limbo_material;
  2855.  
  2856. /* Read the first line of file. */
  2857. if(!input_ln(INPUT_FILE)) return NO;
  2858.  
  2859. /* Check for possible limbo material.     If so, skip all lines up to
  2860. an~`\.{@@x}'. */
  2861. limbo_material = BOOLEAN(cur_buffer[0] == @'@@' && cur_buffer[1] == @'z');
  2862. if(limbo_material)
  2863. do
  2864.     {
  2865.     if(!input_ln(INPUT_FILE))
  2866.         {
  2867.         CLR_PRINTF(warning,("\n! File ended during \
  2868. skip_limbo.  Limbo material that begins with @@z must be terminated by \
  2869. @@x."));
  2870.         return NO;
  2871.         }
  2872.     }
  2873. while(!(cur_buffer[0] == @'@@' && cur_buffer[1] == @'x'));
  2874.  
  2875. /* If there was limbo material, at this point the line beginning
  2876. with~`\.{@@x}' is in the buffer.  The next operation is to read another
  2877. line, which starts the proper part of the include file.  If there was no
  2878. limbo material, the first line of the file is already in the buffer. Thus,
  2879. after the following operation we'll always have the first proper line of
  2880. the file in the buffer. */
  2881. if(limbo_material && !input_ln(INPUT_FILE)) return NO; 
  2882.  
  2883. return YES; // Successfully skipped comments.
  2884. }
  2885.  
  2886. @<Read from |change_file|...@>= 
  2887. {
  2888.   if (!input_ln(CHANGE_FILE)) 
  2889.     {
  2890.         ERR_PRINT(C,"Change file ended without @@z");
  2891. @.Change file ended...@>
  2892.         cur_buffer[0]=@'@@'; cur_buffer[1]=@'z'; limit=cur_buffer+2;
  2893.       }
  2894.  
  2895.  if (limit>cur_buffer+1) /* check if the change has ended */
  2896.   if (cur_buffer[0]==@'@@') 
  2897.     {
  2898.         @<Lowerc...@>;
  2899.         @<Check for erron...@>;
  2900.  
  2901.         if (cur_buffer[1]==@'x' || cur_buffer[1]==@'y') 
  2902.         {
  2903.             loc=cur_buffer+2; ERR_PRINT(C,"Where is the matching @@z?");
  2904. @.Where is the match...@>
  2905.          }
  2906.         else if (cur_buffer[1]==@'z') 
  2907.         {
  2908.           prime_the_change_buffer(); prn_where=YES;
  2909.          }
  2910.       }
  2911. }
  2912.  
  2913. @ At the end of the program, we will tell the user if the change file
  2914. had a line that didn't match any relevant line in |web_file|.
  2915.  
  2916. @<Part 2@>=@[
  2917.  
  2918. SRTN 
  2919. chk_complete(VOID)
  2920. {
  2921.   if (change_limit != NULL) 
  2922.     { /* |changing| is |NO| */
  2923.     STRNCPY(cur_buffer,change_buffer,
  2924.     PTR_DIFF(size_t,change_limit,change_buffer)+1); 
  2925.     loc = limit = PTR_DIFF(size_t,change_limit,change_buffer) + cur_buffer;
  2926.     changing=YES; 
  2927.     ERR_PRINT(C,"Change file entry did not match");
  2928.   @.Change file entry did not match@>
  2929.       }
  2930. }
  2931.  
  2932. @* MEMORY ALLOCATION.
  2933. Almost all memory is allocated dynamically. This
  2934. allows one to customize the tables if necessary; default sizes can be
  2935. overridden by the command-line option~``\.{-y}'', which can also be used in
  2936. the ini file. Dynamic allocation also ensures that the pointer to the array
  2937. is |HUGE|, and that sometimes matters for personal computers.
  2938.  
  2939. @i mem.hweb
  2940.  
  2941. @ The memory allocation routine quits if it can't find anything.
  2942.  
  2943. @d MAX_SIZE_T ((BUF_SIZE)(size_t)(~(0L))) // Maximum argument to |calloc|.
  2944.  
  2945. @<Glob...@>=
  2946.  
  2947. IN_COMMON BUF_SIZE total_mem CSET(0); 
  2948.     // Current total dynamically allocated memory.
  2949. IN_COMMON BUF_SIZE max_mem CSET(0); // The maximum bytes that were allocated.
  2950.  
  2951. @
  2952. @<Part 2@>=@[
  2953.  
  2954. void HUGE *
  2955. get_mem0 FCN((why,nunits,nsize))
  2956.     CONST outer_char why[] C0("Reason for request")@;
  2957.     BUF_SIZE nunits C0("Number of units to allocate.")@;
  2958.     size_t nsize C1("Size of each unit.")@;
  2959. {
  2960. void HUGE *p; /* With certain compilers such as the |SGI|, |void| is
  2961.         translated into |char|; see \.{proto.hweb}. */
  2962. BUF_SIZE nbytes;
  2963.  
  2964. if(nunits==0) return NULL; /* Should this ever happen? */
  2965. if(nsize==0) CONFUSION("get_mem0","Nsize = 0 requested");
  2966.  
  2967. #if !NON_ANSI_CALLOC
  2968. /* For ANSI, the prototype for |calloc| is |void *calloc(size_t,size_t)|. */
  2969.     if(nunits > MAX_SIZE_T)
  2970.         {
  2971.     err_print(C,"get_mem0: Can't request %lu units; used max of %lu",
  2972.             nunits,MAX_SIZE_T);
  2973.         nunits = MAX_SIZE_T;
  2974.         }
  2975. #endif /* |ANSI_CALLOC| */
  2976.  
  2977. if( (p=(void HUGE *)CALLOC(nunits,nsize)) == NULL)
  2978.     {
  2979.     char temp[250];
  2980.  
  2981.     sprintf(temp,"\n!!! NO MORE MEMORY (\"%s\")!!! \
  2982. [Requesting %lu unit(s) of size %lu byte(s); allocated %lu bytes so far.]  \
  2983. Try using the -y option to reduce the sizes of internal tables.",
  2984.         (char *)why,nunits,(BUF_SIZE)nsize,total_mem);
  2985.     FREE(byte_mem); // Kludge to try to make it go out gracefully.
  2986.     FATAL(C, temp,"");
  2987.     }
  2988.  
  2989. /* Keep track of total allocation. */
  2990. nbytes = nunits*nsize;
  2991. total_mem += nbytes; 
  2992. max_mem = MAX(max_mem,total_mem);
  2993.  
  2994. #ifdef mac
  2995.     lmemset(p,0,nbytes); /* For |lmalloc|, must zero explicitly. */
  2996. #endif
  2997.  
  2998. if(show_mem && nbytes >= show_size)
  2999.     show_alloc('+',why,nunits,(BUF_SIZE)nsize,nbytes,p,total_mem);
  3000.  
  3001. return p; /* The allocated memory area. */
  3002. }
  3003.  
  3004. @ Here we display the details of the memory allocation.
  3005. @<Part 2@>=@[
  3006.  
  3007. SRTN 
  3008. show_alloc FCN((c,why,nunits,nsize,nbytes,p,total_mem))
  3009.     outer_char c C0("Either plus or minus")@;
  3010.     CONST outer_char why[] C0("Reason for request")@;
  3011.     BUF_SIZE nunits C0("Number of units")@;
  3012.     BUF_SIZE nsize C0("Size of each unit.")@;
  3013.     BUF_SIZE nbytes C0("Bytes allocated")@;
  3014.     void HUGE *p C0("Start of allocated area")@;
  3015.     BUF_SIZE total_mem C1("Total bytes currently allocated")@;
  3016. {
  3017. printf("\"%s\": %c(%lu x %lu) = %c%lu ",
  3018.         (char *)why, c, nunits, nsize, c, nbytes);
  3019. ptr_range(p,nbytes);
  3020. printf("(%lu total)\n",total_mem);
  3021. }
  3022.  
  3023. @ Because not all compilers understand the \.{\%p}~format command, and
  3024. since different users may wish to customize the form of the pointer range
  3025. for an allocation, we provide this function.
  3026.  
  3027. If one wanted, for the Sun etc.\ he could use |$TRANSLIT| to turn~\.p
  3028. into~\.d, for example.
  3029.  
  3030. @d PTR_BASE "[%p,%p) " /* To style file? */
  3031.  
  3032. @<Part 2@>=@[
  3033.  
  3034. SRTN 
  3035. ptr_range FCN((p,nbytes))
  3036.     void HUGE *p C0("Start of the allocated space.")@;
  3037.     BUF_SIZE nbytes C1("Length of the allocation")@;
  3038. {
  3039. static char ptr_string[] = 
  3040. #ifdef ibmpc
  3041.     PTR_BASE
  3042. #else
  3043.     ""
  3044. #endif
  3045. ;
  3046.  
  3047. printf(ptr_string,p,(char HUGE *)p + nbytes);
  3048. }
  3049.  
  3050. @ We free memory through an intermediate routine so we can keep track of
  3051. the total memory allocations.
  3052. @<Part 2@>=@[
  3053.  
  3054. SRTN 
  3055. free_mem0 FCN((p,why,nunits,nsize))
  3056.     void HUGE *p C0("Pointer to deallocate")@;
  3057.     CONST outer_char why[] C0("Reason for request")@;
  3058.      BUF_SIZE nunits C0("Number of units to deallocate.")@;
  3059.     size_t nsize C1("Size of each unit.")@;
  3060. {
  3061. BUF_SIZE nbytes;
  3062.  
  3063. /* If things are working correctly, |p|~should have been previously
  3064. allocated. */
  3065. if(p == NULL) CONFUSION("free_mem0","Attempting to deallocate NULL pointer");
  3066.  
  3067. /* Keep track of total allocation. */
  3068. nbytes = nunits*nsize;
  3069. total_mem -= nbytes; 
  3070.  
  3071. if(show_mem && nbytes >= show_size)
  3072.     show_alloc('-',why,nunits,(BUF_SIZE)nsize,nbytes,p,total_mem);
  3073.  
  3074. FREE(p);
  3075. }
  3076.  
  3077. @ For debugging, we have some variables that say whether to display the
  3078. dynamic memory allocations as they occur. To turn on the display, use the
  3079. command-line option~\.{-sm}. A numeric argument changes |show_size|---e.g.,
  3080. \.{-sm100} shows all memory allocations whose size is $\ge 100$ bytes.
  3081.  
  3082. @d SHOW_MEM 0    /* Don't display by default. */
  3083.  
  3084. @<Glob...@>=
  3085.  
  3086. IN_COMMON boolean show_mem CSET(SHOW_MEM);
  3087. IN_COMMON BUF_SIZE show_size CSET(10000); // Display for |nbytes >= show_size|.
  3088.  
  3089. @ Here we set up for dynamic memory allocation. The information about the
  3090. minimum allowed, maximum allowed, and default value for the length or value
  3091. of a variable is held in a |MEM| structure. The default value can be
  3092. overridden with the command-line option~\.{-y}. 
  3093.  
  3094. @m ID_FLAG 10240 /* \bf DO NOT MESS WITH THIS NUMBER!. */
  3095. @m ID_FLAG1 $EVAL(ID_FLAG-1)
  3096.  
  3097. @<Common...@>=
  3098.  
  3099. /* Ideally, the following numbers should be large enough to handle both
  3100. \FTANGLE\ and \FWEAVE. In the comments, we show some characteristic
  3101. statistics. */  
  3102.  
  3103. /* \.{Machine-dependent:} Machines with limited memory. */
  3104. #if SMALL_MEMORY
  3105.     #define DFLT_BYTES 10000 /* \.{fweave fweave} $\approx$ 23,300. */
  3106.     #define DFLT_BUF_SIZE 1500
  3107.     #define DFLT_C_BUF_SIZE 200
  3108.     #define DFLT_FMT_SIZE 200
  3109.     #define DFLT_MSG_SIZE 1000
  3110.     #define DFLT_DELTA_DOTS 20
  3111.     #define DFLT_MAX_LBLS 20
  3112.     #define DFLT_LINE_LENGTH 80
  3113.     #define DFLT_LONGEST_NAME 1000
  3114.     #define DFLT_MAX_EXPR_CHARS 500
  3115.     #define DFLT_MAX_IFILES 10
  3116.     #define DFLT_MAX_INCLUDE_DEPTH 10
  3117.     #define DFLT_MAX_MARGS 20
  3118.     #define DFLT_MAX_MODULES 400
  3119.     #define DFLT_MBUF_SIZE 32767
  3120.     #define DFLT_MAX_NAMES 1000 /* \.{fweave fweave} $\approx$ 3,300. */
  3121.     #define DFLT_MAX_REFS 2500 /* \.{fweave ftangle} $\approx$ 5,850. */
  3122.     #define DFLT_SBUF_LEN 300
  3123.     #define DFLT_STACK_SIZE_T 50
  3124.     #define DFLT_STACK_SIZE_W 210 /* \.{fweave fweave} $\approx$ 200. */
  3125.     #define DFLT_MAX_SCRAPS 1000 /* \.{fweave fweave} $\approx$ 900. */
  3126.     #define DFLT_MAX_TOKS_T 50000L // \.{ftangle fweave} $\approx$ 92,000.
  3127.     #define DFLT_MAX_TOKS_W 6000 /* \.{fweave fweave} $\approx$ 6,700. */
  3128.     #define DFLT_MAX_DTOKS 2500
  3129.     #define DFLT_MAX_TEXTS 2500 /* \.{fweave fweave} $\approx$ 4,500. */
  3130.     #define DFLT_MAX_DTEXTS 100
  3131.     #define DFLT_NUM_FILES 15
  3132.     #define DFLT_OP_ENTRIES $EVAL(128+PREDEFINED_DOTS+5)
  3133.     #define DFLT_X_BUF_SIZE 80
  3134. #else /* Large limits. */
  3135.     #define DFLT_BYTES 90000L
  3136.     #define DFLT_BUF_SIZE 1500
  3137.     #define DFLT_C_BUF_SIZE 255
  3138.     #define DFLT_FMT_SIZE 500
  3139.     #define DFLT_MSG_SIZE 2000
  3140.     #define DFLT_DELTA_DOTS 20
  3141.     #define DFLT_MAX_LBLS 20
  3142.     #define DFLT_LINE_LENGTH 80
  3143.     #define DFLT_LONGEST_NAME 10000
  3144.     #define DFLT_MAX_EXPR_CHARS 500
  3145.     #define DFLT_MAX_IFILES 100
  3146.     #define DFLT_MAX_INCLUDE_DEPTH 10
  3147.     #define DFLT_MAX_MARGS 20
  3148.     #define DFLT_MAX_MODULES 2000
  3149.     #define DFLT_MBUF_SIZE 50000L
  3150.     #define DFLT_MAX_NAMES 4000
  3151.     #define DFLT_MAX_REFS 20000
  3152.     #define DFLT_SBUF_LEN 300
  3153.     #define DFLT_STACK_SIZE_T 50
  3154.     #define DFLT_STACK_SIZE_W 400
  3155.     #define DFLT_MAX_SCRAPS 10000
  3156.     #define DFLT_MAX_TOKS_T 150000L
  3157.     #define DFLT_MAX_TOKS_W 25000
  3158.     #define DFLT_MAX_DTOKS 25000
  3159.     #define DFLT_MAX_TEXTS ID_FLAG1
  3160.     #define DFLT_MAX_DTEXTS 500
  3161.     #define DFLT_NUM_FILES 15
  3162.     #define DFLT_OP_ENTRIES $EVAL(128+PREDEFINED_DOTS+15)
  3163.     #define DFLT_X_BUF_SIZE 80
  3164. #endif /* Limited memory. */
  3165.  
  3166.  
  3167. @ The following list must be kept sorted by abbreviation!
  3168. @<Glob...@>=
  3169.  
  3170. IN_COMMON MEM mem[]
  3171.    #if(part == 0 || part == 1)
  3172.      = {
  3173.     {ABBREV(max_bytes),1,
  3174.         1000,DFLT_BYTES,ULONG_MAX}, /* |"b"|.  |max_bytes|: the
  3175. number of bytes in identifiers, index entries, and module names. */
  3176.     {ABBREV(buf_size),1,
  3177.         500,DFLT_BUF_SIZE,2000}, // |"bs"|.  |change_buffer| etc.
  3178.     {ABBREV(C_buf_size),1,
  3179.         10,DFLT_C_BUF_SIZE,1000},// |"cb"|. |C_buf_size|: see |C_putc|.
  3180.     {ABBREV(cmd_fsize),1,
  3181.           100,DFLT_FMT_SIZE,UL(65536L)}, // |"cf"|.|cmd_fsize|: see |out_cmd|.
  3182.     {ABBREV(cmd_size),1,
  3183.        100,DFLT_MSG_SIZE,UL(65536L)}, // |"cg"|. |cmd_size|: see |out_cmd|
  3184.     {ABBREV(delta_dots),sizeof(DOTS),
  3185.         20,DFLT_DELTA_DOTS,255}, /* |"d"|.  |delta_dots|: Number of
  3186. additional entries to reallocate for |dots| if necessary. */
  3187.     {ABBREV(max_dtoks),sizeof(eight_bits),
  3188.         1000,DFLT_MAX_DTOKS,ULONG_MAX}, // |"dt"|.  |max_dtoks|.
  3189.     {ABBREV(dtexts_max),sizeof(text),
  3190.         100,DFLT_MAX_DTEXTS,ID_FLAG1}, // |"dx"|.  |dtexts_max|.
  3191.     {ABBREV(max_include_depth), sizeof(INPUT_PRMS),
  3192.         1, DFLT_MAX_INCLUDE_DEPTH, 100}, // |"id"|.  |prms| (open files).
  3193.     {ABBREV(max_ifiles), sizeof(outer_char *),
  3194.         1, DFLT_MAX_IFILES, 10000}, // |"if"|.  |max_ifiles|.
  3195.     {ABBREV(stck_size_t),sizeof(output_state),
  3196.         10,DFLT_STACK_SIZE_T,100}, // |"kt"|.  Tangle's |stck_size|.
  3197.     {ABBREV(stck_size_w),sizeof(output_state),
  3198.         50,DFLT_STACK_SIZE_W,1000}, // |"kw"|.  Weave's |stck_size|.
  3199.     {ABBREV(max_lbls),sizeof(BEGUN),
  3200.         5,DFLT_MAX_LBLS,255}, // |"lb"|. |max_lbls|.
  3201.     {ABBREV(line_length),1,
  3202.        60,DFLT_LINE_LENGTH,255}, /* |"ll"|.  |line_length| for
  3203. \FWEAVE's output. */
  3204.     {ABBREV(longest_name),1,
  3205.         3*MAX_FILE_NAME_LENGTH, DFLT_LONGEST_NAME, UL(50000L)}, 
  3206. /* |"ln"|.  |longest_name|: module names and strings shouldn't be longer
  3207. than this. */   
  3208.     {ABBREV(max_expr_chars),1,
  3209.         25,DFLT_MAX_EXPR_CHARS,UL(65536L)}, // |"lx"|.  |max_expr_chars|.
  3210.     {ABBREV(max_modules),sizeof(boolean),
  3211.         100,DFLT_MAX_MODULES,ID_FLAG1}, /* |"m"|.  |max_modules|:
  3212. larger than the max number of modules. */ 
  3213.     {ABBREV(max_margs), sizeof(sixteen_bits),
  3214.         1, DFLT_MAX_MARGS, 255}, // |"ma"|.  |max_margs|.
  3215.     {ABBREV(mbuf_size),1,
  3216.         5000,DFLT_MBUF_SIZE,UL(65536L)}, // |"mb"|. |mbuf_size|.
  3217.     {ABBREV(max_names),sizeof(NAME_INFO)+sizeof(name_pointer),
  3218.         500,DFLT_MAX_NAMES,ID_FLAG1}, /* |"n"|.  |max_names|:
  3219. number of identifiers, strings, module names; must be less than 10240. */
  3220.     {ABBREV(num_files),sizeof(OPEN_FILE),
  3221.         1,DFLT_NUM_FILES,256}, // |"nf"|.  |num_files|.
  3222.     {ABBREV(op_entries),sizeof(OPERATOR),
  3223.        $EVAL(128+PREDEFINED_DOTS),DFLT_OP_ENTRIES,256}, /* |"op"|.
  3224. |op_entries| */
  3225.     {ABBREV(max_refs),sizeof(xref_info),
  3226.         500,DFLT_MAX_REFS,UL(65536L)}, /* |"r"|. |max_refs|: number of
  3227. cross-references; must be less than 65536. */ 
  3228.     {ABBREV(max_scraps),sizeof(scrap),
  3229.         1000,DFLT_MAX_SCRAPS,ULONG_MAX}, // |"s"|.  |max_scraps|.
  3230.     {ABBREV(sbuf_len),sizeof(outer_char),
  3231.         100,DFLT_SBUF_LEN,1000}, // |"sb"|.  |sbuf_len|.
  3232.     {ABBREV(max_toks_t),sizeof(eight_bits),
  3233.         10000,DFLT_MAX_TOKS_T,ULONG_MAX}, /* |"tt"|.  Tangle's
  3234. |max_toks|: Total for the entire code; can be big. */
  3235.     {ABBREV(max_toks_w),sizeof(sixteen_bits),1000,
  3236.         DFLT_MAX_TOKS_W,UL(65536L)}, /* |"tw"|.  Weave's |max_toks|:
  3237. number of symbols in code texts being parsed;  must be less than 65536. */ 
  3238.     {ABBREV(max_texts),sizeof(text),
  3239.         500,DFLT_MAX_TEXTS,ID_FLAG1}, // |"x"|.  |max_texts|.
  3240.     {ABBREV(X_buf_size),1,
  3241.         10,DFLT_X_BUF_SIZE,1000},// |"xb"|. |X_buf_size|: see |C_putc|.
  3242.     {"",0,0,0}
  3243.     }
  3244.     #endif // |part == 1|
  3245.     ;
  3246.  
  3247. IN_COMMON size_t sizeof_mem CSET(sizeof(mem));
  3248.  
  3249. @ This is the search routine used by the `\.{-y}'~option that matches an
  3250. abbreviation with the right element of the |MEM| structure and overrides
  3251. the default value. 
  3252.  
  3253. @<Part 2@>=@[
  3254.  
  3255. MEM HUGE *
  3256. msearch FCN((abbrev,nunits))
  3257.     CONST outer_char abbrev[] C0("Abbreviation for desired object.")@;
  3258.     BUF_SIZE nunits C1("Value.")@;
  3259. {
  3260. MEM HUGE *m;
  3261.  
  3262. for(m=mem; *m->abbrev; m++)
  3263.     if(STRCMP(m->abbrev,abbrev)==0)
  3264.         {
  3265.         if(nunits == 0L) return m; // Special case: just find it.
  3266.  
  3267.         if(nunits < m->min)
  3268.             {
  3269. CLR_PRINTF(warning,
  3270.     ("! Warning (msearch:%s): nunits (%lu) < min (%lu); min used.\n",
  3271.     (char *)abbrev,nunits,m->min));
  3272.             mark_harmless;
  3273.             nunits = m->min;
  3274.             }
  3275.         else if(nunits > m->max)
  3276.             {
  3277. CLR_PRINTF(warning,
  3278.     ("! Warning (msearch:%s): nunits (%lu) > max (%lu); max used\n",
  3279.     (char *)abbrev,nunits,m->max));
  3280.             mark_harmless;
  3281.             nunits = m->max;
  3282.             }
  3283.         
  3284.         m->nunits = nunits; /* Override the default value. */
  3285.         return m;
  3286.         }
  3287.  
  3288. CLR_PRINTF(warning,
  3289.     ("! Invalid alloc abbreviation \"%s[%lu]\"\n", (char *)abbrev,nunits));
  3290. mark_harmless;
  3291. return m;
  3292. }
  3293.  
  3294. @ Here is the allocation routine that works with the |ALLOC| macro. It
  3295. returns a pointer to allocated memory, and also sets an|unsigned long|
  3296. variable with the number of units (not bytes) allocated.
  3297.  
  3298. @<Part 2@>=@[
  3299.  
  3300. void HUGE *
  3301. alloc FCN((abbrev,pnunits,nsize,dn))
  3302.     CONST outer_char abbrev[] C0("Abbreviation for desired object.")@;
  3303.     BUF_SIZE HUGE *pnunits C0("Pointer to returned \# of units.")@;
  3304.     size_t nsize C0("Size of object.")@;
  3305.     int dn C1("Incremental number of units to allocate.")@;
  3306. {
  3307. MEM HUGE *m = find_abbrev(abbrev);
  3308.  
  3309. *pnunits = m->nunits;
  3310.  
  3311. /* |dn| is used as a flag; if it's negative, we just obtain the number of
  3312. units. */
  3313. if(dn < 0) 
  3314.     return NULL;
  3315. else 
  3316.     return get_mem0(abbrev, (*pnunits)+dn, nsize);
  3317. }
  3318.  
  3319. @ Search through the abbreviations list to get the element.
  3320. @<Part 2@>=@[
  3321. MEM HUGE *find_abbrev FCN((abbrev))
  3322.     CONST outer_char abbrev[] C1("")@;
  3323. {    
  3324. MEM HUGE *m;
  3325. outer_char temp[100];
  3326.  
  3327. for(m=mem; *m->abbrev; m++)
  3328.     if(STRCMP(m->abbrev,abbrev)==0) return m;
  3329.  
  3330. SPRINTF(100,temp,`"Bad alloc abbreviation \"%s\"",abbrev`);
  3331. CONFUSION("find_abbrev",temp);
  3332. return NULL;
  3333. }
  3334.  
  3335. @ Annoyingly, Microsoft doesn't supply a reallocation routine that works
  3336. with |halloc|, presumably because it doesn't do heap management with |huge|
  3337. stuff. So, we write our own. We simply allocate new space, copy the old to
  3338. the new, then free the old.
  3339.  
  3340. @<Part 2@>=@[
  3341. #ifdef mcc
  3342.  
  3343. void HUGE *
  3344. hrealloc FCN((old_ptr, new_size, old_size))
  3345.     void HUGE *old_ptr C0("Old buf previously allocated with |halloc|.")@;
  3346.     BUF_SIZE new_size C0("New size in bytes.")@;
  3347.     BUF_SIZE old_size C1("Old size in bytes.")@;
  3348. {
  3349. void HUGE *new_ptr;
  3350. BUF_SIZE num_to_copy;
  3351. size_t ncopied;
  3352.  
  3353. new_ptr = GET_MEM("hrealloc", new_size, char);
  3354.  
  3355. if(new_ptr == NULL)
  3356.     return NULL;
  3357.  
  3358. num_to_copy = MIN(old_size, new_size);
  3359. ncopied = (size_t)num_to_copy;
  3360.  
  3361. if((BUF_SIZE)ncopied != num_to_copy)
  3362.     CONFUSION("hrealloc", "trying to copy more than size_t");
  3363.  
  3364. memcpy(new_ptr, old_ptr, ncopied); 
  3365.  
  3366. FREE_MEM(old_ptr, "old_ptr", old_size, char);
  3367.  
  3368. return new_ptr;
  3369. }
  3370.  
  3371. #endif /* |mcc| */
  3372.  
  3373. @* STORAGE of IDENTIFIERS and MODULE NAMES.
  3374. Both \.{WEAVE} and \.{TANGLE} store identifiers, module names and
  3375. other strings in a large array of |ASCII|s, called |byte_mem|.
  3376. Information about the names is kept in the array |name_dir|, whose
  3377. elements are structures of type \&{NAME\_INFO}, containing a pointer into
  3378. the |byte_mem| array (the address where the name begins) and other data.
  3379. A \&{name\_pointer} variable is a pointer into |name_dir|.
  3380.  
  3381. @<Allocate dynamic memory@>=
  3382.  
  3383. ALLOC(ASCII,byte_mem,ABBREV(max_bytes),max_bytes,0); // Characters of names.
  3384. byte_end = byte_mem + max_bytes - 1; // End of |byte_mem|.
  3385.  
  3386. ALLOC(NAME_INFO,name_dir,ABBREV(max_names),max_names,0); 
  3387.     // Information about names.
  3388. name_end = name_dir + max_names - 1; // End of |name_dir|.
  3389.  
  3390. ALLOC(ASCII,mod_text,ABBREV(longest_name),longest_name,1);
  3391. mod_end = mod_text+longest_name; // End of |mod_text|.
  3392.  
  3393. @I trunc.hweb
  3394.  
  3395. @ The actual sequence of characters in the name pointed to by a |name_pointer
  3396. p| appears in positions |p->byte_start| to |(p+1)->byte_start|, inclusive.
  3397. The |prn_id| macro prints this text on the user's terminal.
  3398.  
  3399. @<Part 2@>=@[
  3400.  
  3401. unsigned 
  3402. prn_id FCN((np))
  3403.     name_pointer np C1("")@;
  3404. {
  3405. ASCII_write(*np->byte_start == BP_MARKER ? 
  3406.     ((BP *)np->byte_start)->Root->id : np->byte_start,Length(np));
  3407.  
  3408. return PTR_DIFF(unsigned, np, name_dir);
  3409. }
  3410.  
  3411. int 
  3412. Length FCN((np))
  3413.     name_pointer np C1("")@;
  3414. {
  3415. int n;
  3416. BP HUGE *bp;
  3417. TRUNC HUGE *s;
  3418. CONST ASCII HUGE *p0;
  3419. CONST ASCII HUGE *p1;
  3420.  
  3421. p0 = np->byte_start;
  3422. bp = (BP *)p0;
  3423.  
  3424. if(*(char *)bp == BP_MARKER)
  3425.     {
  3426.     s = bp->Root;
  3427.     n = PTR_DIFF(int, s->id_end, s->id);
  3428.     }
  3429. else
  3430.     {
  3431.     p1 = (np+1)->byte_start;
  3432.     PROPER_END(p1);
  3433.     n = PTR_DIFF(int, p1, p0);
  3434.     }
  3435.  
  3436. return n;
  3437. }
  3438.     
  3439. @ The first unused position in |byte_mem| and |name_dir| is
  3440. kept in |byte_ptr| and |name_ptr|, respectively.  Thus we
  3441. usually have |name_ptr->byte_start=byte_ptr|, and certainly
  3442. we want to keep |name_ptr<=name_end| and |byte_ptr<=byte_end|.
  3443.  
  3444. @<Common...@>=
  3445.  
  3446. IN_COMMON name_pointer name_ptr; // First unused position in |byte_start|.
  3447. IN_COMMON ASCII HUGE *byte_ptr; // First unused position in |byte_mem|.
  3448.  
  3449. @<Initialize dynamic...@>=
  3450.  
  3451. CAST(name_pointer,name_dir)->byte_start=byte_ptr=byte_mem; /* position zero
  3452.             in both arrays */ 
  3453. name_ptr=name_dir+1; /* |name_dir[0]| will not be used */
  3454. CAST(name_pointer,name_ptr)->byte_start=byte_mem; /* this makes name 0 of
  3455.             length zero */ 
  3456.  
  3457. @ The names of identifiers are found by computing a hash address |h| and
  3458. then looking at strings of bytes signified by the |name_pointer|s
  3459. |hash[h]|, |hash[h]->link|, |hash[h]->link->link|, \dots,
  3460. until either finding the desired name or encountering the null pointer.
  3461.  
  3462. The hash table itself consists of |hash_size| entries of type
  3463. |name_pointer|, and is updated by the |id_lookup| procedure, which finds a
  3464. given identifier and returns the appropriate |name_pointer|. The matching
  3465. is done by the function |names_match|, which is slightly different in
  3466. \.{WEAVE} and \.{TANGLE}.  If there is no match for the identifier, it is
  3467. inserted into the table.
  3468.  
  3469. @d hash_size 353 /* should be prime */
  3470.  
  3471. @ Initially all the hash lists are empty.
  3472.  
  3473. @<Initialize static...@>=
  3474.  
  3475. hash = GET_MEM("hash",hash_size,name_pointer);
  3476. hash_end = hash + hash_size - 1;
  3477.  
  3478. for (h=hash; h<=hash_end; *h++=NULL) ;
  3479.  
  3480. @ Here is the main procedure for finding identifiers. The type of
  3481. identifier being considered is held in the global variable |word_type|;
  3482. this comes into play when |last == NULL|.
  3483.  
  3484. @<Part 2@>=@[
  3485.  
  3486. name_pointer
  3487. id_lookup FCN((first,last,t))
  3488.     CONST ASCII HUGE *first C0("First character of string.")@;
  3489.     CONST ASCII HUGE *last C0("last character of string plus one.")@;
  3490.     eight_bits t C1("The |ilk|; used by \.{WEAVE} only.")@;
  3491. {
  3492.   CONST ASCII HUGE *i=first; /* position in |cur_buffer| */
  3493.   int h; /* hash code */
  3494.   int l; /* length of the given identifier */
  3495.   name_pointer p; /* where the identifier is being sought */
  3496.   WORD_TYPE word_type0 = ORDINARY_ID;
  3497.  
  3498. if(last == NULL)
  3499.     {
  3500.     word_type0 = word_type;
  3501.  
  3502.     for (last=first; *last!='\0'; last++)
  3503.         ; // Find end of identifier.
  3504.     }
  3505.  
  3506.   l = PTR_DIFF(int,last,first); /* compute the length */
  3507.   @<Compute the hash code |h|@>;
  3508.   @<Compute the name location |p|@>;
  3509.  
  3510.   if (p==name_ptr) 
  3511.     @<Enter a new name into the table at position |p|@>;
  3512.  
  3513.   if(!(p->Language & (boolean)language)) 
  3514.     p->ilk = t;
  3515.  
  3516.   p->Language |= (boolean)language; /* Record language usage. */
  3517.  
  3518.   switch(word_type0)
  3519.     {
  3520.     case RESERVED_WD:
  3521.         p->reserved_word |= (boolean)language;
  3522.         break;
  3523.  
  3524.     case INTRINSIC_FCN: 
  3525.         p->intrinsic_word |= (boolean)language;
  3526.         break;
  3527.  
  3528.     case KEYWD:
  3529.         p->keyword |= (boolean)language;
  3530.         break;
  3531.  
  3532.     default: ; /* Avoids warning about unused |enum|. */
  3533.     }
  3534.  
  3535.   return p;
  3536. }
  3537.  
  3538. @ The following two routines |find_n| and |find_s| are for rapid debugging
  3539. of the |name_dir|. They are to be called from the debugger. |find_n| prints
  3540. information about the $n$th~identifier; |find_s| prints information about
  3541. the identifier named~|s|.
  3542.  
  3543. @<Part 2@>=@[
  3544.  
  3545. sixteen_bits 
  3546. find_n FCN((n))
  3547.     sixteen_bits n C1("")@;
  3548. {
  3549. name_pointer np;
  3550. CONST ASCII HUGE *end;
  3551. outer_char HUGE *s;
  3552. size_t len;
  3553.  
  3554. np = name_dir + n;
  3555.  
  3556. if(np >= name_ptr) 
  3557.     {
  3558.     printf("!! n = %u exceeds nmax = %u.\n", n,
  3559.         PTR_DIFF(unsigned, name_ptr, name_dir) - 1);
  3560.     return 0;
  3561.     }
  3562.  
  3563. PROPER_END(end);
  3564.  
  3565. len = PTR_DIFF(size_t, end, np->byte_start); // Length of the $n$th identifier.
  3566. s = GET_MEM("find_n",len+1,outer_char); // Allocate temporary area.
  3567.  
  3568. STRNCPY(s,np->byte_start,len);
  3569. TERMINATE(s,len);
  3570.  
  3571. /* Convert from |ASCII| to the outer world; print info about id. */
  3572. to_outer((ASCII HUGE *)s);
  3573. id_info(np,(long)n,(CONST outer_char *)s, NULL);
  3574. FREE_MEM(s,"find_n",len+1,outer_char);
  3575.  
  3576. return n;
  3577. }
  3578.  
  3579. sixteen_bits 
  3580. find_s FCN((s))
  3581.     CONST outer_char *s C1("")@;
  3582. {
  3583. int h,l;
  3584. name_pointer p;
  3585. ASCII HUGE *a;
  3586. CONST ASCII HUGE *first, HUGE *last;
  3587. CONST ASCII HUGE *i;
  3588. sixteen_bits n = 0;
  3589.  
  3590. l = STRLEN(s);
  3591. a = GET_MEM("find_s",l+1,ASCII);
  3592. STRCPY(a,s);
  3593. i = first = to_ASCII((outer_char *)a);
  3594. last = first + l;
  3595.  
  3596. @<Compute the hash...@>@;
  3597. p=hash[h];
  3598. while (p && !(STRNCMP(first,p->byte_start,l) == 0)) p=p->link;
  3599.  
  3600. if(p == NULL) 
  3601.     {CLR_PRINTF(warning,("%cId \"%s\" is not in name_dir!\n", 
  3602.         beep(1), (char *)s));}
  3603. else 
  3604.     id_info(p,(long)(n=(sixteen_bits)(p-name_dir)),s, NULL);
  3605.  
  3606. FREE_MEM(a,"find_s",l+1,ASCII);
  3607.  
  3608. return n;
  3609. }
  3610.  
  3611. @I t_codes.hweb
  3612.  
  3613. @
  3614. @<Part 2@>=@[
  3615.  
  3616. sixteen_bits 
  3617. find_id FCN((a0,a1))
  3618.     eight_bits a0 C0("")@;
  3619.     eight_bits a1 C1("")@;
  3620. {
  3621. return find_n(IDENTIFIER(a0,a1));
  3622. }
  3623.  
  3624. @ The information utility used in |find_n| and |find_s|; also in |see_reserved|.
  3625. @<Part 2@>=@[
  3626.  
  3627. SRTN 
  3628. id_info FCN((np,n,s,prsrvd))
  3629.     name_pointer np C0("")@;
  3630.     long n C0("")@;
  3631.     CONST outer_char *s C0("")@;
  3632.     CONST RSRVD HUGE *prsrvd C1("")@;
  3633. {
  3634. if(n >= 0) 
  3635.     printf(_Xx("Id %ld (0x%lx):"),n,n);
  3636.  
  3637. if(!prsrvd 
  3638.    || (prsrvd->reserveds && np->reserved_word)
  3639.    || (prsrvd->intrinsics && np->intrinsic_word)
  3640.    || (prsrvd->keywords && np->keyword))
  3641.     printf(" \"%s\"\n", (char *)s);
  3642.  
  3643. if(!prsrvd)
  3644.     pr_lan("Language ",np->Language);
  3645.  
  3646. if(!prsrvd || prsrvd->reserveds)
  3647.     pr_lan("reserved ",np->reserved_word);
  3648.  
  3649. if(!prsrvd || prsrvd->intrinsics)
  3650.     pr_lan("intrinsic",np->intrinsic_word);
  3651.  
  3652. if(!prsrvd || prsrvd->keywords)
  3653.     pr_lan("keyword  ",np->keyword);
  3654. }
  3655.  
  3656. @
  3657. @<Glob...@>=
  3658.  
  3659. IN_COMMON boolean info_option CSET(NO);
  3660. IN_COMMON boolean see_rsrvd CSET(NO);
  3661. IN_COMMON RSRVD rsrvd
  3662.     #if(part == 0 || part == 1)
  3663.      = {NULL, NO, NO, NO}
  3664.     #endif // |part == 1|
  3665.     ;
  3666. IN_COMMON outer_char HUGE *style_args CSET(NULL);
  3667. IN_STYLE ASCII HUGE *at_codes;
  3668.  
  3669. @ Some info options can be executed only after dynamic memory is allocated.
  3670. After that, if no web file was specified on the command line we just
  3671. terminate gracefully.
  3672. @<Execute deferred...@>=
  3673. {
  3674. if(rsrvd.args) see_reserved(&rsrvd);
  3675. if(style_args) see_style(style_args, NO);
  3676.  
  3677. if(!found_web && info_option && !at_codes) 
  3678.     wrap_up();
  3679. }
  3680.  
  3681. @ Format the language dependence of a boolean variable, for debugging and
  3682. the \.{-D}~option.
  3683. @<Part 2@>=@[
  3684. SRTN 
  3685. pr_lan FCN((name,b))
  3686.     CONST char *name C0("")@;
  3687.     boolean b C1("")@;
  3688. {
  3689. unsigned k;
  3690. boolean first = YES;
  3691.  
  3692. if(b==0) return;
  3693.  
  3694. printf("   %s = { ",name);
  3695.  
  3696. for(k=1; k<=$EVAL(2^^8); k<<=1)
  3697.     if(k & b) 
  3698.         {
  3699.         printf("%s%s",first ? "" : ", ",
  3700.             languages[lan_num((LANGUAGE)k)]);
  3701.         first = NO;
  3702.         }
  3703.  
  3704. if(first) printf("(none)");
  3705. printf(_Xx(" } (0x%x)\n"),b);
  3706. }
  3707.  
  3708. @ More debugging:  Print out the entire |name_dir|.
  3709. @<Part 2@>=@[
  3710.  
  3711. BUF_SIZE 
  3712. prn_nd(VOID)
  3713. {
  3714. BUF_SIZE k, n = name_ptr - name_dir;
  3715.  
  3716. for(k=0; k<n; k++)
  3717.     {
  3718.     printf("[%lu]:  ", k);
  3719.     prn_id(name_dir + k);
  3720.     putchar('\n');
  3721.     }
  3722.  
  3723. return n;
  3724. }
  3725.  
  3726. @ A simple hash code is used: If the sequence of
  3727. ASCII codes is $c_1c_2\ldots c_n$, its hash value will be
  3728. $$(2^{n-1}c_1+2^{n-2}c_2+\cdots+c_n)\,\bmod\,|hash_size|.$$
  3729.  
  3730. @<Compute the hash...@>=
  3731.  
  3732. h=*i; 
  3733.  
  3734. while (++i<last) h=(h+h+*i) % hash_size;
  3735.  
  3736. @ If the identifier is new, it will be placed in position |p=name_ptr|,
  3737. otherwise |p| will point to its existing location.
  3738.  
  3739. @<Compute the name location...@>=
  3740. p=hash[h];
  3741.  
  3742. while (p && !names_match(p,first,l,t)) p=p->link;
  3743.  
  3744. if (p==NULL) 
  3745.     {
  3746.       p=name_ptr; /* the current identifier is new */
  3747.       p->link=hash[h]; hash[h]=p; /* insert |p| at beginning of hash
  3748.                         list */ 
  3749.     }
  3750.  
  3751. @ The information associated with a new identifier must be initialized
  3752. in a slightly different way in \.{WEAVE} than in \.{TANGLE}; hence the
  3753. |ini_p| procedure.
  3754.  
  3755. @<Enter a new name...@>= 
  3756. @{
  3757.   if (byte_ptr+l>byte_end) OVERFLW("bytes",ABBREV(max_bytes));
  3758.   if (name_ptr>=name_end) OVERFLW("names",ABBREV(max_names));
  3759.  
  3760.   STRNCPY(byte_ptr,first,l);
  3761.   (++name_ptr)->byte_start = byte_ptr += l;
  3762.  
  3763.   if (program==weave) 
  3764.     ini_p(p,t);
  3765. }
  3766.  
  3767. @ The names of modules are stored in |byte_mem| together
  3768. with the identifier names, but a hash table is not used for them because
  3769. \.{TANGLE} needs to be able to recognize a module name when given a prefix of
  3770. that name. A conventional binary seach tree is used to retrieve module names,
  3771. with fields called |llink| and |rlink| (where |llink| takes the place
  3772. of |link|).  The root of this tree is stored in |name_dir->rlink|;
  3773. this will be the only information in |name_dir[0]|.
  3774.  
  3775. Since the space used by |rlink| has a different function for
  3776. identifiers than for module names, we declare it as a |union|.
  3777.  
  3778. @  The binary search tree starts out with nothing in it.
  3779.  
  3780. @<Initialize dynamic...@>=
  3781.  
  3782. root = NULL;
  3783.  
  3784. @ The |mod_lookup| procedure finds a module name in the
  3785. search tree, after inserting it if necessary, and returns a pointer to
  3786. where it was found.
  3787.  
  3788. According to the rules of \.{WEB}, no module name should be a proper
  3789. prefix of another, so a ``clean'' comparison should occur between any
  3790. two names. The result of |mod_lookup| is |NULL| if this prefix condition
  3791. is violated. An error message is printed when such violations are detected.
  3792. (Note that the |LESS|, |EQUAL|, and |GREATER| conventions here are
  3793. different than those for |strcmp|; they are defined in \.{typedefs.hweb}.)
  3794.  
  3795. @<Part 2@>=@[
  3796.  
  3797. name_pointer
  3798. mod_lookup FCN((k,l))
  3799.     CONST ASCII HUGE *k C0("First character of name.")@;
  3800.     CONST ASCII HUGE *l C1("Last character of name.")@;
  3801. {
  3802.   LEXI c = GREATER; /* comparison between two names */
  3803.   name_pointer p = root; /* current node of the search tree */
  3804.   name_pointer q = name_dir; /* father of node |p| */
  3805.  
  3806.   while (p) 
  3807.     {
  3808.         c = web_strcmp(k,l+1,p->byte_start,(p+1)->byte_start);
  3809.         q=p;
  3810.  
  3811.         switch(c) 
  3812.         {
  3813.               case LESS: p=p->llink; continue;
  3814.               case GREATER: p=p->rlink; continue;
  3815.               case EQUAL: return p;
  3816.               default: ERR_PRINT(C,"Incompatible section names"); 
  3817.                 return NULL;
  3818. @.Incompatible section names@>
  3819.             }
  3820.       }
  3821.  
  3822.   return install_node(q,c,k,PTR_DIFF(int,l,k)+1);
  3823. }
  3824.  
  3825. @ This function is like |strcmp|, but it does not assume the strings
  3826. are null-terminated.
  3827.  
  3828. @<Part 2@>=@[
  3829.  
  3830. LEXI 
  3831. web_strcmp FCN((j,j1,k,k1)) /* fuller comparison than |strcmp| */
  3832.     CONST ASCII HUGE *j C0("Beginning of first string.")@;
  3833.     CONST ASCII HUGE *j1 C0("End of first string plus one.")@;
  3834.     CONST ASCII HUGE *k C0("Beginning of second string.")@;
  3835.     CONST ASCII HUGE *k1 C1("End of second string plus one.")@;
  3836. {
  3837.   while (k<k1 && j<j1 && *j==*k) k++, j++;
  3838.  
  3839.   if (k==k1) if (j==j1) return EQUAL;
  3840.     else return EXTENSION;
  3841.   else if (j==j1) return PREFIX;
  3842.   else if (*j<*k) return LESS;
  3843.   else return GREATER;
  3844. }
  3845.  
  3846. @ The reason we initialized |c| to |greater| is so that |name_pointer| will
  3847. make |name_dir->rlink| point to the root of the tree when |q=name_dir|,
  3848. that is, the first time it is called.
  3849.  
  3850. The information associated with a new node must be initialized in a
  3851. slightly different way in \.{WEAVE} than in \.{TANGLE}; hence the
  3852. |ini_node| procedure.
  3853.  
  3854. @<Part 2@>=@[
  3855.  
  3856. name_pointer
  3857. install_node FCN((parent,c,j,name_len)) /* install a new node in the tree */
  3858.     name_pointer parent C0("Parent of new node.")@;
  3859.     int c C0("Right or left?")@;
  3860.     CONST ASCII HUGE *j C0("Where replacement text starts.")@;
  3861.     int name_len C1("length of replacement text.")@;
  3862. {
  3863.   name_pointer node=name_ptr; /* new node */
  3864.   if (byte_ptr+name_len>byte_end) OVERFLW("bytes",ABBREV(max_bytes));
  3865.   if (name_ptr==name_end) OVERFLW("names",ABBREV(max_names));
  3866.  
  3867.   if (c==LESS) parent->llink=node; else parent->rlink=node;
  3868.  
  3869.   node->llink=node->rlink=NULL;
  3870.   ini_node(node);
  3871.   STRNCPY(byte_ptr,j,name_len);
  3872.   (++name_ptr)->byte_start=byte_ptr+=name_len;
  3873.   return node;
  3874. }
  3875.  
  3876. @ The |prefix_lookup| procedure is supposed to find exactly one module name
  3877. that has |k..l| as a prefix. Actually the algorithm silently accepts also
  3878. the situation that some module name is a prefix of |k..l|, because the user
  3879. who painstakingly typed in more than necessary probably doesn't want to be
  3880. told about the wasted effort.
  3881.  
  3882. @<Part 2@>=@[
  3883.  
  3884. name_pointer
  3885. prefix_lookup FCN((k,l)) /* finds module name given a prefix */
  3886.     CONST ASCII HUGE *k C0("First char of prefix.")@;
  3887.     CONST ASCII HUGE *l C1("Last char of prefix.")@;
  3888. {
  3889.   LEXI c = GREATER; /* comparison between two names */
  3890.   short count = 0; /* the number of hits */
  3891.   name_pointer p = root; /* current node of the search tree */
  3892.   name_pointer q = NULL;
  3893.     /* another place to resume the search after one is done */
  3894.   name_pointer r = NULL; /* extension found */
  3895.   while (p) {
  3896.     c=web_strcmp(k,l+1,p->byte_start,(p+1)->byte_start);
  3897.  
  3898.     switch(c) {
  3899.       case LESS: p=p->llink; break;
  3900.       case GREATER: p=p->rlink; break;
  3901.       default: r=p; count++; q=p->rlink; p=p->llink;
  3902.     }
  3903.  
  3904.     if (p==NULL) {
  3905.       p=q; q=NULL;
  3906.     }
  3907.   }
  3908.   if (count==0) ERR_PRINT(C,"Name does not match");
  3909. @.Name does not match@>
  3910.   else if (count>1) ERR_PRINT(C,"Ambiguous prefix");
  3911. @.Ambiguous prefix@>
  3912.  
  3913.   return r; /* the result will be |NULL| if there was no match */
  3914. }
  3915.  
  3916. @ Here's a routine to be called from the debugger; it returns the directory
  3917. number and the full name of a prefix.
  3918. @<Part 2@>=@[
  3919.  
  3920. unsigned 
  3921. find_prefix FCN((s))
  3922.     outer_char s[] C1("")@;
  3923. {
  3924. name_pointer r;
  3925.  
  3926. to_ASCII(s);
  3927. r = prefix_lookup((ASCII HUGE *)s,(ASCII HUGE *)(s+STRLEN(s)));
  3928.  
  3929. return r ? prn_id(r) : 0;
  3930. }
  3931.  
  3932. @ The last component of |name_info| is different for \.{TANGLE} and
  3933. \.{WEAVE}.  In \.{TANGLE}, if |p| is a pointer to a module name, |p->equiv|
  3934. is a pointer to its replacement text, an element of the array |text_info|.
  3935. In \.{WEAVE}, on the other hand, if |p| points to an identifier, |p->xref|
  3936. is a pointer to its list of cross-references, an element of the array
  3937. |xmem|.  The make-up of |text_info| and |xmem| is discussed in the
  3938. \.{TANGLE} and \.{WEAVE} source files, respectively; here we just declare a
  3939. common field |equiv_or_xref| as a pointer to an |ASCII|.
  3940.  
  3941. @* REPORTING ERRORS to the USER.
  3942. The command `|ERR_PRINT(origin,"Error message")|' will report a syntax
  3943. error to the user, by printing the error message at the beginning of a new
  3944. line and then giving an indication of where the error was spotted in the
  3945. source file.  Note that no period follows the error message, since the
  3946. error routine will automatically supply a period. The |origin| argument is
  3947. one of~|C|, |T|, |W|, |R|, or~|M|; these are translated by the macro into
  3948. things like |ERR_C|, an enumerated type that says where the error came
  3949. from.  More generally, one can say |err_print(origin,msg,args)|, where
  3950. |msg| can be formatted as for |printf| and |n|~specifies how many arguments
  3951. follow. Thus, |ERR_PRINT| is a special case of |err_print| with $n = 0$.
  3952.  
  3953. The actual error indications are provided by a procedure called |error|.
  3954. However, error messages are not actually reported during phase one, since
  3955. errors detected on the first pass will be detected again during the second.
  3956.  
  3957. @<Glob...@>=
  3958.  
  3959. IN_COMMON CONST char *origin_name[]
  3960.     #if(part == 0 || part == 1)
  3961.      = {"","COMMON","FTANGLE","FWEAVE","RATFOR","MACROS","STYLE"}
  3962.     #endif // |part == 1|
  3963.     ;
  3964.  
  3965. @
  3966. @<Part 2@>=@[
  3967.  
  3968. SRTN 
  3969. err0_print FCN(VA_ALIST((err_origin,fmt,n VA_ARGS)))
  3970.     VA_DCL(
  3971.     ERR_ORIGIN err_origin C0("Where the error came from")@;
  3972.     CONST outer_char *fmt C0("Format of error message.")@;
  3973.     int n C2("Number of arguments to the string.")@;)@;
  3974. {
  3975. VA_LIST(arg_ptr)@;
  3976. ASCII HUGE *k, HUGE *l; /* pointers into |cur_buffer| */
  3977. char temp[500];
  3978. boolean prn_input_buffer;
  3979. #if(NUM_VA_ARGS == 1)
  3980.     ERR_ORIGIN err_origin; 
  3981.     CONST outer_char *fmt; 
  3982.     int n;
  3983. #endif
  3984.  
  3985. VA_START(arg_ptr,n);
  3986.  
  3987. #if(NUM_VA_ARGS == 1)
  3988.     err_origin = va_arg(arg_ptr,ERR_ORIGIN);
  3989.     fmt = va_arg(arg_ptr,outer_char *);
  3990.     va_arg(arg_ptr,int);
  3991. #endif
  3992.  
  3993. prn_input_buffer = BOOLEAN(err_origin!=ERR_NULL);
  3994.  
  3995. if(prn_input_buffer && err_origin != ERR_S) putchar('\n');
  3996.  
  3997. set_color(RED);
  3998.  
  3999. if(prn_input_buffer)
  4000.     {
  4001.     sprintf(temp,"! (%s):  %s", origin_name[err_origin], (char *)fmt);
  4002.     vprintf(temp, arg_ptr);
  4003.     }
  4004. else 
  4005.     vprintf((char *)fmt, arg_ptr);
  4006.  
  4007. va_end(arg_ptr);
  4008.  
  4009. if(prn_input_buffer) 
  4010.     if(err_origin == ERR_S)
  4011.         @<Print error location based on style file buffer@>@;
  4012.     else
  4013.         @<Print error location based on input buffer@>@;
  4014.  
  4015. fflush(stdout); mark_error;
  4016.  
  4017. mfree(); // Free up any |mod_trans| allocations.
  4018. }
  4019.  
  4020. @ The error locations can be indicated by using the global variables |loc|,
  4021. |cur_line|, |cur_file_name| and |changing|, which tell respectively the
  4022. first unlooked-at position in |cur_buffer|, the current line number, the
  4023. current file, and whether the current line is from |change_file| or
  4024. |cur_file|.  This routine should be modified on systems whose standard text
  4025. editor has special line-numbering conventions.  @^system dependencies@>
  4026.  
  4027. @d MAX_ERR_BUF 160
  4028.  
  4029. @<Print error location based on input buffer@>=
  4030. {
  4031. outer_char last_char = fmt[STRLEN(fmt)-1];
  4032.  
  4033. if(*fmt && last_char != '?' && last_char != '!')
  4034.     printf("."); /* Sometimes the third argument to |fatal| is empty,
  4035.             so the period isn't necessary. */
  4036.  
  4037. if(cur0_prms)
  4038.  {
  4039. if (changing) 
  4040.     {
  4041.     printf(" (l. %u of %s file ", change_line, "change");
  4042.     CLR_PRINTF(in_file, ("%s)\n", (char *)change_file_name)); 
  4043.     }
  4044. else if (incl_depth==WEB_FILE) 
  4045.     printf(" (l. %u)\n", cur_line);
  4046. else 
  4047.     {
  4048.     printf(" (l. %u of %s file ",cur_line,"include");
  4049.     CLR_PRINTF(in_file,("%s)\n", (char *)cur_file_name));
  4050.     }
  4051.  
  4052. l = MIN(loc,limit);
  4053.  
  4054. if (l>cur_buffer && cur_buffer!=NULL) 
  4055.   {
  4056.   if((BUF_SIZE)(l-cur_buffer) > buf_size)
  4057.     {
  4058.     CLR_PRINTF(error,
  4059.         ("! TROUBLE IN ERROR ROUTINE.  PLEASE REPORT THIS!\n")); 
  4060.     mark_error;
  4061.  
  4062.     l = cur_buffer + MAX_ERR_BUF;
  4063.     }
  4064.  
  4065.   for (k=cur_buffer; k<l; k++)
  4066.     if (*k==@'\t') 
  4067.     putchar(' ');
  4068.     else 
  4069.     putchar(*k > 127 ? ' ' : XCHR(*k)); 
  4070.     /* print the characters already read */
  4071.  
  4072.   putchar('\n'); // Separate already read from not yet read.
  4073.  
  4074.   for (k=cur_buffer; k<l; k++) putchar(' '); /* Space out the next line. */
  4075.   }
  4076.  
  4077. if(l != NULL)
  4078.     {
  4079.     if((BUF_SIZE)(limit-l) > buf_size)
  4080.         {
  4081.         puts("! TROUBLE IN ERROR ROUTINE.  PLEASE REPORT THIS!");
  4082.         l = limit - MAX_ERR_BUF;
  4083.         }
  4084.  
  4085.      for (k=l; k<limit; k++) 
  4086.         putchar(*k > 127 ? ' ' : XCHR(*k)); 
  4087.             /* print the part not yet read */ 
  4088.     }
  4089. if(limit != NULL)
  4090.     if (*limit==@'|') putchar('|'); // End of code text in module names.
  4091.  
  4092. putchar(' '); // To separate the message from future asterisks.
  4093. err_happened = YES;
  4094.  }
  4095. }
  4096.  
  4097. @
  4098. @<Print error location based on style file buffer@>=
  4099. {
  4100. CONST outer_char HUGE *k, HUGE *l;
  4101. IN_STYLE boolean from_sprm;
  4102.  
  4103. printf(".  (l. %u of %s file ",s_line,"style");
  4104. CLR_PRINTF(in_file, ("%s)\n",
  4105.     from_sprm ? "parameter buffer" : (char *)style_file_name)); 
  4106.  
  4107. l = MIN(sloc,slimit);
  4108.  
  4109. for (k=sbuf; k<l; k++)
  4110.     putchar(*k > 127 ? ' ' : (*k == '\t' ? ' ' : *k)); 
  4111.     // Print the characters already read.
  4112.  
  4113. putchar('\n'); // Separate already read from not yet read.
  4114.  
  4115. for (k=sbuf; k<l; k++) 
  4116.     putchar(' '); // Space out the next line.
  4117.  
  4118. for (k=l; k<slimit; k++) 
  4119.     putchar(*k > 127 ? ' ' : *k); // Print the part not yet read.
  4120.  
  4121. putchar('\n');
  4122. }
  4123.  
  4124. @ When no recovery from some error has been provided, we have to wrap up
  4125. and quit as graciously as possible.  This is done by calling the function
  4126. |wrap_up| at the end of the code.
  4127.  
  4128. @<Part 2@>=@[
  4129.  
  4130. int 
  4131. fatal FCN((err_origin, s1, s2))
  4132.     ERR_ORIGIN err_origin C0("Where the error came from")@;
  4133.     CONST outer_char s1[] C0("Message 1.")@;
  4134.     CONST outer_char s2[] C1("Message 2.")@;
  4135. {
  4136. printf("\n");
  4137. if(err_origin != ERR_NULL)
  4138.     {
  4139.     CLR_PRINTF(fatal, ("! (%s):  ", origin_name[err_origin]));
  4140.     }
  4141. CLR_PRINTF(fatal,((char *)s1)); err0_print(ERR_NULL, s2, 0);
  4142. history = fatal_message; 
  4143.  
  4144. return wrap_up(); /* It never really returns, but the compiler doesn't
  4145.             know that. */
  4146. }
  4147.  
  4148. @ Sometimes the program's behavior is far different from what it should be,
  4149. and \.{WEB} prints an error message that is really for the \.{WEB}
  4150. maintainance person, not the user. In such cases the program says
  4151. |confusion("indication of where we are","what went wrong")|.
  4152.  
  4153. Note that this function doesn't really return.  It's coded this way so
  4154. compilers don't give spurious warnings.
  4155. @<Part 2@>=@[
  4156.  
  4157. int 
  4158. confusion FCN((where,why))
  4159.     CONST outer_char where[] C0("")@;
  4160.     CONST outer_char why[] C1("What went wrong.")@;
  4161. {
  4162. outer_char temp[100];
  4163.  
  4164. SPRINTF(100, temp, `"\n    At %s:  %s.", where, why`);
  4165.  
  4166. beep(3);
  4167.  
  4168. return FATAL(NULL, "\n!!! \
  4169. I'M CONFUSED; this shouldn't happen.  PLEASE REPORT THIS!!!  ", temp);
  4170. }
  4171. @.This shouldn't happen@>
  4172.  
  4173. @ An overflow stop occurs if \.{WEB}'s tables aren't large enough.
  4174. @<Part 2@>=@[
  4175.  
  4176. SRTN 
  4177. overflow FCN((s,abbrev))
  4178.     CONST outer_char s[] C0("Condition that caused overflow.")@;
  4179.     CONST outer_char abbrev[] C1("Abbreviation for buffer.")@;
  4180. {
  4181. char temp[1000];
  4182. MEM HUGE *m;
  4183.  
  4184. if(*abbrev == '\0')
  4185.     sprintf(temp,"%s contents too long.", (char *)s);
  4186. else
  4187.     {
  4188.     m = msearch(abbrev,0L);
  4189.     sprintf(temp,"Too many %s: current number is %lu, absolute max is \
  4190. %lu.  Use command-line option \"-y%s...\" to override default.",
  4191.         (char *)s, m->nunits, m->max, (char *)abbrev);
  4192.     }
  4193.  
  4194. FATAL(NULL, "!! Sorry, CAPACITY EXCEEDED:  ",temp);
  4195. }
  4196. @.Sorry, capacity exceeded@>
  4197.  
  4198. @ A special case of |overflow| is useful for style-file parameters.
  4199. @<Part 2@>=@[
  4200.  
  4201. SRTN 
  4202. new_sprm FCN((name, value))
  4203.     CONST outer_char name[] C0("Name of parameter.")@;
  4204.     int value C1("Integer value of parameters.")@;
  4205. {
  4206. char temp[100];
  4207.  
  4208. sprintf(temp, "Please increase style-file parameter `%s' from current value \
  4209. of %i;", name, value);
  4210. OVERFLW(temp, "");
  4211. }
  4212.  
  4213. @ Some implementations may wish to pass the |history| value to the
  4214. operating system so that it can be used to govern whether or not other
  4215. programs are started. Here, for instance, we pass the Unix operating system
  4216. a status of~ 0 if and only if only harmless messages were printed.
  4217. @^system dependencies@>
  4218.  
  4219. @<Part 2@>=@[
  4220.  
  4221. #if(VMS)
  4222.     #include stsdef // Return codes.
  4223. #endif /* |VMS| */
  4224.  
  4225. int 
  4226. wrap_up() 
  4227. {
  4228. putchar('\n');
  4229.  
  4230. #if TIMING
  4231.     prn_time();
  4232. #endif // |TIMING|
  4233.  
  4234. @<Print the job |history|@>@;
  4235. @<Close all open files@>@;
  4236.  
  4237. #if(VMS)
  4238.  switch(history)
  4239.     {
  4240.    case spotless: exit(STS$K_SUCCESS | STS$M_INHIB_MSG);
  4241.    case harmless_message: exit(STS$K_INFO | STS$M_INHIB_MSG);
  4242.    case error_message: exit(STS$K_ERROR | STS$M_INHIB_MSG);
  4243.    case fatal_message: exit(STS$K_SEVERE | STS$M_INHIB_MSG);
  4244.     }
  4245. #else 
  4246.  switch(history)
  4247.     {
  4248.    case spotless:
  4249.    case harmless_message:
  4250.     exit(0);
  4251.  
  4252.    default:
  4253.     exit(1);
  4254.     }
  4255. #endif // |VMS|
  4256.  
  4257. return -1; // Never really does this.
  4258. }
  4259.  
  4260. @ With some systems, it seems to be helpful to close the open files
  4261. explicitly, rather than letting |exit| do it.  I don't know why.
  4262.  
  4263. @<Close all open files@>=
  4264. {
  4265. if(cur_prms.web && cur_prms.web->File) 
  4266.     fclose(cur_prms.web->File);
  4267.  
  4268. if(cur_prms.change && cur_prms.change->File) 
  4269.     fclose(cur_prms.change->File);
  4270.  
  4271. cls_files(); // Do stuff specific to \FTANGLE\ or \FWEAVE.
  4272. }
  4273.  
  4274. @<Print the job |history|@>=
  4275. {
  4276. CLR_PRINTF(info,("[%s:",program==tangle ? TANGLE : WEAVE));
  4277. printf("  ");
  4278.  
  4279. switch (history) 
  4280.     {
  4281.    case spotless: 
  4282.     CLR_PRINTF(info,("No errors were found.")); break;
  4283.  
  4284.    case harmless_message:
  4285.     CLR_PRINTF(warning,
  4286.         ("%cDid you see the warning message(s) above?", beep(1)));
  4287.     break;
  4288.  
  4289.    case error_message:
  4290.     CLR_PRINTF(error,
  4291.          ("%cPardon me, but I think I spotted something wrong.", beep(1)));
  4292.     break;
  4293.  
  4294.    case fatal_message: 
  4295.     CLR_PRINTF(fatal,("%cThat was a fatal error, my friend.", beep(2))); 
  4296.     break;
  4297.     } // There are no other cases.
  4298.  
  4299. CLR_PRINTF(info,("]\n"));
  4300. set_color(NORMAL);
  4301. }
  4302.  
  4303. @ Certain errors beep the terminal.  The beeps can be turned off (and
  4304. replaced by an exclamation point) with the \.{-B} option.
  4305. @<Part 2@>=@[
  4306.  
  4307. char 
  4308. beep FCN((n))
  4309.     int n C1("")@;
  4310. {
  4311. if(beeps)
  4312.     {
  4313.     for(--n; n>0; n--)
  4314.         printf("\007");
  4315.         
  4316.     return '\007'; // (Some compilers didn't understand \.{'\\a'}.)
  4317.     }
  4318. else
  4319.     return '!';
  4320. }
  4321.  
  4322. @ The following error routine is called if there's problem with writing.
  4323. Usually, this means that you've run out of disk space.
  4324. @<Part 2@>=@[
  4325.  
  4326. SRTN 
  4327. out_error FCN((fcn))
  4328.     CONST outer_char fcn[] C1("")@;
  4329. {
  4330. FATAL(C, "!! Output error (no more quota?): ",fcn);
  4331. }
  4332.  
  4333.  
  4334. @* INITIALIZING FLAGS.
  4335.  
  4336. @<Initialize \TeX\ cat...@>=
  4337. {
  4338. int k;
  4339.  
  4340. for(k=0; k<128; k++)
  4341.     TeX[k] = TeX_other;
  4342.  
  4343. for(k=@'A'; k<=@'Z'; k++)
  4344.     TeX[k] = TeX_letter;
  4345.  
  4346. for(k=@'a'; k<=@'z'; k++)
  4347.     TeX[k] = TeX_letter;
  4348.  
  4349. TeX[@'\\'] = TeX_escape;
  4350. TeX[@'{'] = TeX_bgroup;
  4351. TeX[@'}'] = TeX_egroup;
  4352. TeX[@'$'] = TeX_math_shift;
  4353. TeX[@'&'] = TeX_alignment_tab;
  4354. TeX[@'\n'] = TeX_eol;
  4355. TeX[@'#'] = TeX_parameter;
  4356. TeX[@'^'] = TeX_superscript;
  4357. TeX[@'_'] = TeX_subscript;
  4358. TeX[0] = TeX_ignored;
  4359. TeX[@' '] = TeX[tab_mark] = TeX_space;
  4360. TeX[@'~'] = TeX_active;
  4361. TeX[@'%'] = TeX_comment;
  4362. TeX[0177] = TeX_invalid;
  4363. }
  4364.  
  4365. @ We don't initialize the flags statically because it's too tedious to get
  4366. the initialization list right if one adds a flag randomly.  
  4367.  
  4368. @<Initialize flags@>=
  4369. {
  4370. #define FSET(flag) = flag
  4371.  
  4372.     active_brackets FSET(ACTIVE_BRACKETS); // Special array handling?
  4373.     all_cmnts_verbatim FSET(ALL_CMNTS_VERBATIM); /* Should \.{TANGLE} copy
  4374.                     all comments? */ 
  4375.     all_includes FSET(ALL_INCLUDES); // Cross-reference all include files?
  4376.     auto_app_semi FSET(AUTO_APP_SEMI); /* For \.{WEAVE}, automatically
  4377. append a pseudo-semi to the end of \.{WEB} macro definitions? */
  4378.     auto_line FSET(AUTO_LINE); // Auto-insert line nums after \.{@@\%}?
  4379.  
  4380. /* Append automatic semicolon? */
  4381.     params.Auto_semi[LN(FORTRAN)] = YES;
  4382.  
  4383.     params.Auto_semi[LN(FORTRAN_90)]
  4384.         = params.Auto_semi[LN(RATFOR)]
  4385.         = params.Auto_semi[LN(RATFOR_90)]
  4386.         = params.Auto_semi[LN(C)]
  4387.         = params.Auto_semi[LN(C_PLUS_PLUS)]
  4388.         = params.Auto_semi[LN(LITERAL)]
  4389.         = params.Auto_semi[LN(TEX)]
  4390.         = NO;
  4391.  
  4392.     params.Beeps FSET(BEEPS);
  4393.  
  4394. /* Do we label |do|s and |if|s with the block number? */
  4395.     params.Block_nums[LN(C)] 
  4396.         = params.Block_nums[LN(C_PLUS_PLUS)]
  4397.         = params.Block_nums[LN(LITERAL)]
  4398.         = params.Block_nums[LN(TEX)]
  4399.         = NO;
  4400.  
  4401.     params.Block_nums[LN(FORTRAN)] 
  4402.         = params.Block_nums[LN(FORTRAN_90)]
  4403.         = params.Block_nums[LN(RATFOR)]
  4404.         = params.Block_nums[LN(RATFOR_90)] 
  4405.         = BLOCK_NUMS;
  4406.  
  4407.     bslash_continued_strings FSET(BSLASH_CONTINUED_STRINGS); /* Do the
  4408. continuations of strings require a starting backslash? */
  4409.     chk_ifelse FSET(CHK_IFELSE); // Protect parenthesized strings?
  4410.     chk_stmts FSET(CHK_STMTS); // Check statement syntax in \Ratfor?
  4411.     compare_outfiles FSET(COMPARE_OUTFILES);// Compare new vs.\ old output.
  4412.     compound_assignments FSET(COMPOUND_ASSIGNMENTS); /* Do we allow things
  4413.                             like \.{+=}? */
  4414.     Cpp FSET(NO); /* Do we recognize \.{C++}? */
  4415.  
  4416.     dbg_output FSET(DBG_OUTPUT); // Print output characters?
  4417.     deferred_macros FSET(DEFERRED_MACROS); // Allow deferred macros?
  4418.  
  4419.     defn_mask.outer_macros FSET(PRN_OUTER_MACROS);
  4420.     defn_mask.formats FSET(PRN_fORMATS);
  4421.     defn_mask.Formats FSET(PRN_FORMATS);
  4422.     defn_mask.limbo FSET(PRN_LIMBO);
  4423.     defn_mask.macros FSET(PRN_MACROS);
  4424.     defn_mask.v FSET(PRN_V);
  4425.     defn_mask.w FSET(PRN_W);
  4426.  
  4427.     dot_constants FSET(DOT_CONSTANTS); // Recognize dot constants?
  4428.  
  4429.     params.CPP_comments[LN(C)] 
  4430.         = params.CPP_comments[LN(C_PLUS_PLUS)]
  4431.         = params.CPP_comments[LN(LITERAL)]
  4432.         = params.CPP_comments[LN(TEX)]
  4433.         = CPP_COMMENTS;
  4434.  
  4435. /* For \Fortran\ and \Ratfor, the \.{//}~is preempted for concatenation.
  4436. Therefore, its use for short comments must be turned on explicitly. */
  4437.     params.CPP_comments[LN(FORTRAN)] 
  4438.         = params.CPP_comments[LN(FORTRAN_90)]
  4439.         = params.CPP_comments[LN(RATFOR)]
  4440.         = params.CPP_comments[LN(RATFOR_90)] 
  4441.         = NO;
  4442.  
  4443.     Fortran88 FSET(FORTRAN88); /* Turn on stuff for Fortran-88? */
  4444.     Fortran_label FSET(FORTRAN_LABEL); // Label on same line?
  4445.  
  4446.     params.Free_form_input[LN(FORTRAN)]
  4447.         = params.Free_form_input[LN(FORTRAN_90)]
  4448.         = params.Free_form_input[LN(LITERAL)]
  4449.         = params.Free_form_input[LN(TEX)]
  4450.         = NO;
  4451.     params.Free_form_input[LN(RATFOR)]
  4452.         = params.Free_form_input[LN(RATFOR_90)]
  4453.         = params.Free_form_input[LN(C)]
  4454.         = params.Free_form_input[LN(C_PLUS_PLUS)] 
  4455.         = YES; 
  4456.  
  4457.     keep_trailing_comments FSET(KEEP_TRAILING_COMMENTS); // For \TeX.
  4458.  
  4459.     params.In_escape[LN(FORTRAN)] 
  4460.         = params.In_escape[LN(FORTRAN_90)]
  4461.         = '&';
  4462.     params.In_escape[LN(RATFOR)] 
  4463.         = params.In_escape[LN(RATFOR_90)]
  4464.         = params.In_escape[LN(C)]
  4465.         = params.In_escape[LN(C_PLUS_PLUS)]
  4466.         = params.In_escape[LN(LITERAL)] 
  4467.         = params.In_escape[LN(TEX)] 
  4468.         = '\\';
  4469.  
  4470.     in_escape FSET(IN_ESCAPE); // Default continuation character.
  4471.     index_hidden FSET(INDEX_HIDDEN); // Index skipped includes files.
  4472.     input_macros FSET(INPUT_MACROS); /* Generate the default ``\.{\\input
  4473.                     fwebmac.tx}'' line? */
  4474.     lc_keywords FSET(LC_KEYWORDS); // Lower-case \Fortran\ keywords?
  4475.     line_info FSET(LINE_INFO); // Should \FTANGLE\ print out line info?
  4476.     lowercase_tokens FSET(LOWERCASE_TOKENS); // Output lower-case tokens?
  4477.     m4 FSET(M4_);
  4478.     no_xref FSET(NO_XREF); // Should \.{WEAVE} print an index?
  4479.     number_dos FSET(NUMBER_DOS);
  4480.     nuweb_mode FSET(NUWEB_MODE); // Emulate \.{nuweb}?
  4481.     overload_ops FSET(OVERLOAD_OPS); // Is operator overloading allowed?
  4482.  
  4483.     params.Point_comments[LN(C)] 
  4484.         = params.Point_comments[LN(C_PLUS_PLUS)]
  4485.         = params.Point_comments[LN(TEX)]
  4486.         = params.Point_comments[LN(LITERAL)] 
  4487.         = NO;
  4488.     params.Point_comments[LN(FORTRAN)]
  4489.         = params.Point_comments[LN(FORTRAN_90)]
  4490.         = params.Point_comments[LN(RATFOR)] 
  4491.         = params.Point_comments[LN(RATFOR_90)]
  4492.         = POINT_COMMENTS;  /* Does \.! signify a Fortran comment? */ 
  4493.  
  4494.     prn_contents FSET(PRN_CONTENTS); // Print the table of contents?
  4495.     prn_index FSET(PRN_INDEX); // Print the index?
  4496.     prn_input_lines FSET(NO); /* Used in |input_ln| to print out the
  4497. lines sent back. */
  4498.     prn_input_addresses FSET(NO); /* Print out the buffer addresses. */
  4499.     prn_modules FSET(PRN_MODULES); // Print the module list?
  4500.     prn_semis FSET(PRN_SEMIS); // Print semicolons in \Fortran\ output?
  4501.  
  4502.     quoted_includes FSET(QUOTED_INCLUDES); 
  4503.         // Cross-reference quoted include files?
  4504.     Ratfor77 FSET(RATFOR77); /* Expand \Ratfor\ code directly to
  4505. \Fortran-77? */  
  4506.     read_iformats FSET(READ_IFORMATS); // Read include files for formats.
  4507.     params.Reverse_indices[LN(C)]
  4508.         = params.Reverse_indices[LN(C_PLUS_PLUS)]
  4509.         = params.Reverse_indices[LN(TEX)]
  4510.         = params.Reverse_indices[LN(LITERAL)]
  4511.         = NO;
  4512.     params.Reverse_indices[LN(FORTRAN)]
  4513.         = params.Reverse_indices[LN(FORTRAN_90)]
  4514.         = params.Reverse_indices[LN(RATFOR)]
  4515.         = params.Reverse_indices[LN(RATFOR_90)]
  4516.         = REVERSE_INDICES; // C-style indexing in \Fortran?
  4517.     rmv_files FSET(RMV_FILES); // Remove temporary files related to \.{-H}?
  4518.  
  4519.     skip_ifiles FSET(SKIP_IFILES); // \.{-j} --- skip files already included.
  4520.     skip_includes FSET(SKIP_INCLUDES); /* For \.{WEAVE}, don't read in \.{@@I}
  4521.                     commands. (Uppercase '\.{I}' only.) */
  4522.     statistics FSET(STATISTICS); /* Print statistics about memory usage? */
  4523.     stop_the_scan FSET(YES); /* */
  4524.     subscript_fcns FSET(YES); // Module references on functions?
  4525.     suppress_cmds FSET(YES); /* ??? */
  4526.     toggle_includes FSET(TOGGLE_INCLUDES); /* For \.{WEAVE}, read in
  4527. \.{@@I} commands, but don't print them out. */
  4528.     top_version FSET(TOP_VERSION); /* Header info at top of output? */
  4529.     translate_ASCII FSET(TRANSLATE_ASCII0); /* Do we bother with the
  4530. |xchr|--|xord| conversions? */
  4531.     translate_brackets FSET(TRANSLATE_BRACKETS); /* Do we translate
  4532. brackets to parentheses in \Fortran? */
  4533.     truncate_ids FSET(NO); /* Shorten identifiers? */
  4534.     try_extensions FSET(TRY_EXTENSIONS); // Try various file names.
  4535.     TeX_processor FSET(TEX_PROCESSOR); // Default \TeX\ processor.
  4536.     xref_unnamed FSET(XREF_UNNAMED); // Xref unnamed stuff.
  4537. #undef FSET
  4538.  
  4539. #if(TRANSLATE_ASCII || DEBUG_XCHR)
  4540.     translate_ASCII = YES;  // Force it for the cases that really need it.
  4541. #endif
  4542.  
  4543. /* The following ensures that |ini_language| will work right.  (It
  4544. references |global_params|. */
  4545. global_params = params;
  4546. }
  4547.  
  4548. @ When we're scanning for identifiers, we have sometimes to do a
  4549. byte-by-byte comparison looking for special strings. These have to be
  4550. |ASCII|, not ordinary compiler strings. Thus, we initialize them here.
  4551.  
  4552. @<Common...@>=
  4553.  
  4554. IN_COMMON ASCII HUGE *pformat,HUGE *pdefault, HUGE *pdata,
  4555.     HUGE *pbp, HUGE *pinclude, HUGE *ppragma, HUGE *pcontains;
  4556.  
  4557. @ We must translate the |outer_char| strings into |ASCII|.
  4558.  
  4559. @<Initialize static...@>=
  4560.  
  4561. pformat = x__to_ASCII(OC("format"));
  4562. pdata = x__to_ASCII(OC("data"));
  4563. pdefault = x__to_ASCII(OC("default"));
  4564. pbp = x__to_ASCII(OC("_BP"));
  4565. pinclude = x__to_ASCII(OC("include"));
  4566. ppragma = x__to_ASCII(OC("pragma"));
  4567.  
  4568. conv_bi(incl_likes);
  4569. conv_bi(WEB_incl_likes);
  4570. conv_bi(non_labels);
  4571.  
  4572. conv_dot(dots0);
  4573. conv_dot(mcmds);
  4574.  
  4575.  
  4576. @* COMMAND-LINE ARGUMENTS.
  4577. The user calls \.{WEAVE} and \.{TANGLE} with arguments on the command line.
  4578. These are either file names or flags (beginning with |'-'|).
  4579. The following globals are for communicating the user's desires to the rest
  4580. of the program. The various file name variables contain strings with
  4581. the names of those files.
  4582.  
  4583. See the user manual for a detailed description of the various flags.
  4584.  
  4585.  
  4586. @<Common...@>=
  4587.  
  4588. IN_COMMON outer_char outp_buf[MAX_OUTPUT_LINE_LENGTH];    
  4589.     // FORTRAN's output buffer.
  4590. IN_COMMON int nbuf_length CSET(72);
  4591.  
  4592. IN_COMMON outer_char wbprefix[MAX_FILE_NAME_LENGTH]; 
  4593.     // Possible directory prefix for the web file name.
  4594.  
  4595.  /* Signifies the end of a directory prefix. */
  4596. IN_COMMON outer_char prefix_end_char CSET(PREFIX_END_CHAR);
  4597.  
  4598. IN_COMMON boolean all_std CSET(NO); // Do we route all output to |stdout|?
  4599.  
  4600. @ We now must look at the command line arguments and set the file names
  4601. accordingly.  At least one file name must be present: the \.{WEB}
  4602. file.  It may have an extension, or it may omit it to get |'.web'|
  4603. added.  The \TeX\ output file name is formed by replacing the \.{WEB}
  4604. file name extension by |'.tex'|, and the code output file name by replacing
  4605. the extension by~|'.c'|, |'.rat'|, or~|'.for'|.
  4606.  
  4607. If there is another file name present among the arguments, it is the
  4608. change file, again either with an extension or without one to get~|'.ch'|
  4609. An omitted change file argument means that \.{/dev/null} (for Unix),
  4610. \.{nl:} (for VMS), or \.{NUL} (for DOS) should be used, when no
  4611. changes are desired.  (These default extensions are in the style file, and
  4612. can be overridden.)
  4613. @^system dependencies@>
  4614.  
  4615. The following function should be superceded by ANSI's |strchr|.
  4616. @<Unused@>=
  4617.  
  4618. char HUGE *index FCN((s,c)) /* this is standard, but the name is not */
  4619.     char HUGE *s C0("String to be searched.")@;
  4620.     char c C1("Search for this character.")@;
  4621. {
  4622.   while (*s!=c && *s!='\0')
  4623.     s++;
  4624.   if (*s=='\0') return NULL;
  4625.   return s;
  4626. }
  4627.  
  4628. @*1 Scanning command-line arguments.
  4629. Here is the major routine that handles
  4630. the command-line arguments. If a command-line option doesn't begin with a
  4631. hyphen, it's a file name. If it just a hyphen, it means ``\.{stdin}''.
  4632. Otherwise, it's an option.
  4633.  
  4634. @<Glob...@>=
  4635.  
  4636. IN_COMMON boolean found_web,found_change; // Have these names been seen?
  4637. IN_COMMON outer_char wbflnm0[MAX_FILE_NAME_LENGTH]; 
  4638.     // The root name of the web file.
  4639.  
  4640. @
  4641. @<Part 2@>=@[
  4642.  
  4643. SRTN 
  4644. scan_args(VOID)
  4645. {
  4646. outer_char wbflnm[MAX_FILE_NAME_LENGTH];
  4647.  
  4648. /* Build the command line in a buffer. */
  4649. cmd_ln_buf = cmd_line(argc,argv);
  4650.  
  4651. /* Add the arguments from the ini file \.{.fweb}. This resets |argc| and
  4652. |argv|, and also ignores |argv[0]| from the command line. */
  4653. set_args(&argc,&argv);
  4654.  
  4655. /* Process all the arguments in order from left to right. */
  4656. do_args(YES,argc,argv,&found_web,wbflnm,&found_change);
  4657.  
  4658. if(statistics) 
  4659.     mem_avail(0); /* How much memory is available at start? */
  4660.  
  4661. @<Get the path and root file name@>@;
  4662.  
  4663. @<Read the style file@>@;
  4664.  
  4665. @<Make |web_file_name|, |tex_fname| and output file names@>;
  4666.  
  4667.   if (!(found_web || info_option)) usage("!! Missing webfile name.\n", YES);
  4668.   if (!found_change) *change_file_name = '\0'; // This means null file.
  4669. }
  4670.  
  4671. @ Here we allocate a buffer and build the command line into it.
  4672.  
  4673. @d WEAVE_LINE0 OC("{\"")
  4674. @d TANGLE_LINE0 OC("  COMMAND LINE: \"")
  4675.  
  4676. @d WEAVE_LINE1 OC("\"} ")
  4677. @d TANGLE_LINE1 OC("\"")
  4678.  
  4679. @d ESCAPE_LENGTH 256
  4680.  
  4681. @<Part 2@>=@[
  4682.  
  4683. outer_char HUGE *
  4684. cmd_line FCN((num_args,args))
  4685.     int num_args C0("Number of arguments on command line.")@;
  4686.     outer_char *args[] C1("Array of pointers to arguments.")@;
  4687. {
  4688. int k,len,l0,l1;
  4689. outer_char HUGE *buf,HUGE *b, HUGE *line0, HUGE *line1;
  4690. outer_char p[ESCAPE_LENGTH]; /* The escaped argument. */
  4691.  
  4692. if(program==tangle)
  4693.     {
  4694.     line0 = TANGLE_LINE0;
  4695.     line1 = TANGLE_LINE1;
  4696.     }
  4697. else
  4698.     {
  4699.     line0 = WEAVE_LINE0;
  4700.     line1 = WEAVE_LINE1;
  4701.     }
  4702.  
  4703. /* Determine required buffer length. */
  4704. for(k=0,len=0; k<num_args; len += STRLEN(args[k++]) + 1) ;
  4705.  
  4706. l0 = STRLEN(line0);
  4707. l1 = STRLEN(line1);
  4708. buf = GET_MEM("buf",2*(len+l0+l1),outer_char);
  4709. STRCPY(buf,line0);
  4710. b = buf + l0;
  4711.  
  4712. while(num_args-- > 0)
  4713.     {
  4714.     int n = esc_file_name(p,ESCAPE_LENGTH,*(args++));
  4715.  
  4716.     STRNCPY(b,p,n);
  4717.     b += n;
  4718.     *b++ = ' ';
  4719.     }
  4720.  
  4721. STRCPY(--b,line1); /* Kill off final blank. */
  4722. return buf;
  4723. }
  4724.  
  4725. @*1 Processing {\tt .fweb}. 
  4726. Here we handle the initialization file
  4727. \.{.fweb}. If it exists, it is opened. Arguments beginning with a '\.-' are
  4728. placed before the command-line arguments (\.+ also works for backward
  4729. compatibility); those beginning with a '\.\&' are placed after the
  4730. command-line arguments. 
  4731.  
  4732. @<Glob...@>=
  4733.  
  4734. IN_COMMON outer_char 
  4735.     HUGE *ini_args[MAX_INI_ARGS], // Arguments read from the ini file.
  4736.     HUGE * HUGE *new_args, /* The new list of all arguments; allocated
  4737. dynamically. */
  4738.     HUGE * HUGE *pn; // Pointer to the current new argument being processed.
  4739. IN_COMMON outer_char HUGE * HUGE *pn0; // Marks end of ini args.
  4740. IN_COMMON FILE *ini_file CSET(NULL); // The ini file \.{.fweb}.
  4741.  
  4742. @
  4743.  
  4744. @d MAX_INI_ARGS 100 // Max \# of arguments in the ini file.
  4745. @d T_SIZE 200 // Buffer length for |fgets|.
  4746. @d INI_CMNT_CHAR '%'
  4747.  
  4748. @d BEFORE_CHAR '-'
  4749. @d MIDDLE_CHAR '0'
  4750. @d AFTER_CHAR '&'
  4751.  
  4752. @<Part 2@>=@[
  4753.  
  4754. SRTN 
  4755. set_args FCN((pargc,pargv))
  4756.     int HUGE *pargc C0("Pointer to the arg count; new value returned.")@;
  4757.     outer_char HUGE * HUGE * *pargv C1("Ptr to the array of ptrs; returned.")@;
  4758. {
  4759. int k,n;
  4760. outer_char HUGE *buffer, HUGE *b, // Put one line from ini file into here.
  4761.     HUGE *temp, HUGE *t, // Extract the argument into here.
  4762.     HUGE * HUGE *a, // The current ini arg.
  4763.     HUGE *pc; // Pointer to possible comment character in ini line.
  4764. boolean blank;
  4765.  
  4766. /* Allocate temporary buffers. */
  4767. buffer = GET_MEM("buffer",T_SIZE,outer_char);
  4768. temp = GET_MEM("temp",T_SIZE,outer_char);
  4769.  
  4770. /* We start by assuming there is no ini file, hence no argument. */
  4771. k = 0;
  4772.  
  4773. @<Make ini file name and try to open it@>@;
  4774.  
  4775. /* If there is an ini file, then put the arguments into an array. */
  4776. if(ini_file)
  4777.   {
  4778.   for(a=ini_args; k<MAX_INI_ARGS; )
  4779.     {
  4780.     if(FGETS(buffer,T_SIZE,ini_file)==NULL) break; /* Read one line
  4781.         (including newline). */
  4782.  
  4783. /* Extract the string, disgarding the newline. If the line is blank, do
  4784. nothing. */  
  4785.     blank = YES;
  4786.     for(t=temp,b=buffer; *b; )
  4787.         {
  4788.         if(*b == '\n') break;
  4789.         if(!isspace(*b)) blank = NO;
  4790.         *t++ = *b++;
  4791.         }
  4792.     *t = '\0';
  4793.  
  4794.     if(blank) continue;
  4795.  
  4796. /* Kill off any trailing comment. */
  4797.     if((pc=OC(STRCHR(temp,INI_CMNT_CHAR))) != NULL) TERMINATE(pc,0);
  4798.     if(pc==temp) continue; /* The line was entirely a comment. */
  4799.  
  4800.     *a = GET_MEM("ini arg",STRLEN(temp)+1,outer_char); /* Space for the
  4801. argument. */ 
  4802.     STRCPY(*a++,temp); /* Store ini argument. */
  4803.     k++;
  4804.     }
  4805.  
  4806.   FCLOSE(ini_file);
  4807.   }
  4808.  
  4809. /* Allocate space for all the arguments, both ini and command-line. (We
  4810. don't count |argv[0]| from command line.) */
  4811. pn = new_args = GET_MEM("new_args",n=*pargc-1+k,outer_char HUGE *);
  4812.  
  4813. acopy(BEFORE_CHAR, ini_args,k); 
  4814.     // Put those starting with '\.+' or '\.-' first.
  4815. pn0 = pn; // Mark end of the ini stuff.
  4816.  
  4817. acopy(MIDDLE_CHAR, (outer_char HUGE **)(*pargv), *pargc); 
  4818.     // Then the command-line arguments.
  4819.  
  4820. acopy(AFTER_CHAR, ini_args, k); // Then the rest.
  4821.  
  4822. /* Return the new number and array of arguments. */
  4823. *pargc = n;
  4824. *pargv = new_args;
  4825.  
  4826. FREE_MEM(buffer,"buffer", T_SIZE, char);
  4827. FREE_MEM(temp,"temp", T_SIZE, char);
  4828. }
  4829.  
  4830. @ We will look for the ini file in the user's root directory, if possible.
  4831. We determine this by querying the environment with |getenv|.
  4832.  
  4833. @d HOME OC("HOME") // The Unix-standard name for the top-level directory.
  4834. @d ENV_INI OC("FWEB_INI") // Default root name to use.
  4835. @d ENV_INCLUDES OC("FWEB_INCLUDES") // The include path.
  4836.  
  4837. @<Make ini file...@>=
  4838. @{
  4839. outer_char ini_file_name[MAX_FILE_NAME_LENGTH];
  4840. outer_char *p_root, ini_root[MAX_FILE_NAME_LENGTH];
  4841.  
  4842. #if HAVE_GETENV /* \.{Machine-dependent}: Understands |getenv|. */
  4843.     if( (p_root=GETENV(ENV_INI)) == NULL) STRCPY(ini_root,OC(FWEB_INI));
  4844.     else STRCPY(ini_root,p_root);
  4845.  
  4846.     more_includes(GETENV(ENV_INCLUDES));
  4847. #else
  4848.     STRCPY(ini_root,OC(FWEB_INI));
  4849. #endif // |HAVE_GETENV|
  4850.  
  4851. ini_file = FOPEN(mk_fname(ini_file_name,MAX_FILE_NAME_LENGTH,
  4852.         HOME,YES,ini_root),"r");
  4853. }
  4854.  
  4855. @ The list of include paths is allocated only as necessary.
  4856. @<Glob...@>=
  4857.  
  4858. IN_COMMON INCL_PATHS incl;
  4859.  
  4860. @ This functions adds more entries to the include list.  The list is
  4861. constructed in the format \.{$\alpha$:$\beta$:$\gamma$:}; in other words,
  4862. entries are terminated by a colon.
  4863.  
  4864. @<Part 2@>=@[
  4865.  
  4866. SRTN 
  4867. more_includes FCN((incl_list0))
  4868.     outer_char *incl_list0 C1("")@;
  4869. {
  4870. unsigned n;
  4871. BUF_SIZE old_size;
  4872.  
  4873. if(!incl_list0)
  4874.     return;
  4875.  
  4876. /* Strip off useless opening colon. */
  4877. while(*incl_list0 == ':') 
  4878.     incl_list0++;
  4879.  
  4880. if((n = STRLEN(incl_list0)) == 0) 
  4881.     return;
  4882.  
  4883. old_size = incl.size;
  4884. incl.size += n + 2; // Allow for trailing colon and null byte.
  4885.  
  4886. if(!incl.list) 
  4887.     { /* List hasn't been allocated yet. */
  4888.     incl.list = GET_MEM("incl_list",incl.size,outer_char);
  4889.     STRCPY(incl.list,incl_list0);
  4890.     }
  4891. else 
  4892.     { /* Add more to the list. */
  4893.     incl.list = (outer_char HUGE *)REALLOC(incl.list, incl.size, old_size);
  4894.  
  4895.     if(incl.list == NULL)
  4896.         FATAL(NULL, "!! No more memory (more_includes)","");
  4897.  
  4898.     STRCAT(incl.list,incl_list0);
  4899.     }
  4900.  
  4901. /* Ensure list ends in a colon. */
  4902. if(incl.list[n-1] != ':') incl.list[n] = ':';
  4903. }    
  4904.  
  4905. @
  4906. @<Part 2@>=@[
  4907.  
  4908. char *
  4909. mk_fname FCN((buffer,buf_len,env_var,print_msg,file_name))
  4910.     outer_char *buffer C0("Put result here.")@;
  4911.     unsigned buf_len C0("Length of |buffer|.")@;
  4912.     CONST outer_char *env_var C0("Variable that holds directory name.")@;
  4913.     boolean print_msg C0("Flag for not recognizing |env_var|.")@;
  4914.     CONST outer_char *file_name C1("The raw file name.")@;
  4915. {
  4916. outer_char *directory; // Will hold result of |getenv|. 
  4917.  
  4918. #if HAVE_GETENV /* \.{Machine-dependent}: Understands |getenv|. */
  4919.     if( (directory=GETENV(env_var)) == NULL) 
  4920.         {
  4921.         if(print_msg)
  4922.           printf("! I can't find environment \
  4923. variable \"%s\", so I'll look for file \"%s\" \
  4924. in directory \"%s\".\n",
  4925.             (char *)env_var, (char *)file_name,
  4926.                 *wbprefix ? (char *)wbprefix : ".");
  4927.         if(buf_chk(file_name,buf_len,
  4928.                 STRLEN(file_name)+STRLEN(wbprefix)))
  4929.             return (char *)wt_style.null_file_name;
  4930.         STRCPY(buffer,wbprefix);
  4931.         STRCAT(buffer,file_name);
  4932.         }
  4933.     else
  4934.         { /* Got a directory from the environment variable. */
  4935.         if(buf_chk(file_name,buf_len,
  4936.             STRLEN(directory)+1+STRLEN(file_name)))
  4937.                 return (char *)wt_style.null_file_name;
  4938. /* For Unix, home is of the form
  4939. \.{/u/krommes}; for VMS, it's like \.{ux3:[krommes]}. */
  4940.    #if !UNIX_PATH
  4941.     SPRINTF(buf_len,buffer,`"%s%s",directory,file_name`);
  4942.    #else
  4943.     SPRINTF(buf_len,buffer,`"%s%c%s",directory,prefix_end_char,file_name`);
  4944.    #endif // |!UNIX_PATH|
  4945.         }
  4946. #else /* Doesn't understand |getenv|. */
  4947.     if(buf_chk(file_name,buf_len,STRLEN(file_name))) 
  4948.         return (char *)wt_style.null_file_name; 
  4949.     STRCPY(buffer,file_name);
  4950. #endif // |CAN_GETENV|
  4951.  
  4952. return (char *)buffer;
  4953. }
  4954.  
  4955. @ Make sure we don't overrun a file buffer.
  4956. @<Part 2@>=@[
  4957.  
  4958. boolean 
  4959. buf_chk FCN((file_name,buf_len,needed))
  4960.     CONST outer_char *file_name C0("Raw file name")@;
  4961.     unsigned buf_len C0("Max space available")@;
  4962.     unsigned needed C1("Requested")@;
  4963. {
  4964. if(needed >= buf_len)
  4965.     {
  4966.     printf("\n! Buffer too small to make complete file name from \
  4967. \"%s\"; opened \"%s\"\n", (char *)file_name, (char *)wt_style.null_file_name);
  4968.     return YES;
  4969.     }
  4970.  
  4971. return NO;
  4972. }
  4973.  
  4974. @ Here we copy from an old argument array to the new one. If the argument
  4975. begins with a~'\.+', that is changed to a~'\.-'.
  4976. @<Part 2@>=@[
  4977.  
  4978. SRTN 
  4979. acopy FCN((c,a,n))
  4980.     outer_char c C0("Starting symbol.")@;
  4981.     outer_char HUGE **a C0("Old array.")@;
  4982.     int n C1("Number of arguments to be looked at.")@;
  4983. {
  4984. boolean is_before = BOOLEAN(c==BEFORE_CHAR || c=='+');
  4985. boolean is_middle = BOOLEAN(c==MIDDLE_CHAR);
  4986. boolean is_after = BOOLEAN(c==AFTER_CHAR);
  4987. int k;
  4988.  
  4989. /* Disgard the zeroth argument from the command line. */
  4990. if(is_middle)
  4991.     {
  4992.     k = 1;
  4993.     a++;
  4994.     }
  4995. else 
  4996.     k = 0;
  4997.  
  4998. /* Look at all the arguments; copy them if appropriate. */
  4999. for(; k<n; k++,a++)
  5000.     {
  5001.     boolean before_entry = BOOLEAN((*a)[0] == BEFORE_CHAR || (*a)[0] == '+');
  5002.  
  5003. /* The following somewhat inscrutable logic puts file names and ``after''
  5004. entries from \.{.fweb} into the ``after'' list. */
  5005.     if( (!is_before && !before_entry) || (!is_after && before_entry) )
  5006.         {
  5007.         *pn = GET_MEM("*pn", STRLEN(*a)+1, outer_char);
  5008.         STRCPY(*pn, *a);
  5009.         if((*pn)[0] == '+' || (*pn)[0] == AFTER_CHAR) 
  5010.             (*pn)[0] = BEFORE_CHAR;
  5011.         pn++;
  5012.         }
  5013.     }
  5014. }
  5015.  
  5016. @*1 Making file names.
  5017. We use all of |pa=*argv| for the |web_file_name| if
  5018. there is a~|'.'| in it, otherwise add |'.web'|. \It{This has to be
  5019. augmented for VMS!} The other file names come from adding things after the
  5020. dot.  We must check that there is enough room in |web_file_name| and the
  5021. other arrays for the argument.
  5022.  
  5023. If the output file name was already set by~`\.{->}', we do nothing, except if
  5024. it contains a~`\.\#' we expand that into the |wbflnm0|.  If it was not set,
  5025. we add the extension determined from the style file.
  5026.  
  5027. @m SET_NAME(l,labbrev) set_name(l,wt_style.output_ext.labbrev##_)
  5028.  
  5029. @<Make |web_file_name...@>=
  5030. {
  5031. int k;
  5032.  
  5033. /* \WEAVE's output file. */
  5034. xpn_name(&tex_fname,MAX_FILE_NAME_LENGTH,tex_fname,wbflnm0);
  5035. if(*tex_fname == '\0') new_fname(&tex_fname,wbflnm0,OC("tex"));
  5036.  
  5037. /* \TANGLE's output files. */
  5038. for(k=0; k<NUM_LANGUAGES; k++)
  5039.     xpn_name(params.outp_nm+k,MAX_FILE_NAME_LENGTH,
  5040.         params.outp_nm[k],wbflnm0);
  5041.     
  5042. SET_NAME(C,C);
  5043. SET_NAME(C_PLUS_PLUS,Cpp);
  5044. SET_NAME(LITERAL,V);
  5045. SET_NAME(FORTRAN,N);
  5046. SET_NAME(FORTRAN_90,N90);
  5047. SET_NAME(RATFOR,R);
  5048. SET_NAME(RATFOR_90,R90);
  5049. SET_NAME(TEX,X);
  5050. }
  5051.  
  5052. @
  5053. @<Get the path...@>=
  5054. {
  5055. outer_char HUGE *dot_pos;
  5056. outer_char HUGE *p = NULL;
  5057.  
  5058. if (STRLEN(wbflnm) > MAX_FILE_NAME_LENGTH-5)
  5059.     too_long();
  5060.  
  5061. if(STRCMP(wbflnm,"stdin") == 0) STRCPY(web_file_name,wbflnm);
  5062. else 
  5063.     {
  5064.     p = OC(STRRCHR(wbflnm,prefix_end_char)); // Is there a path?
  5065.  
  5066.     if((dot_pos=OC(STRCHR(p ? p+1 : wbflnm,wt_style.ext_delimiter)))==NULL
  5067.              && !try_extensions)
  5068.         { /* Attach ``\.{.web}'' as a default. */
  5069.         SPRINTF(MAX_FILE_NAME_LENGTH,web_file_name,`"%s%cweb",
  5070.             wbflnm,wt_style.ext_delimiter`);
  5071.         }
  5072.       else { /* There's a dot, or we're trying extensions; treat the
  5073. name as is. */ 
  5074.         SPRINTF(MAX_FILE_NAME_LENGTH,web_file_name,`"%s",wbflnm`);
  5075.  
  5076.         if(!try_extensions)
  5077.             TERMINATE(dot_pos,0); /* string now ends where the
  5078. dot was */ 
  5079.         }
  5080.     }
  5081.  
  5082. @<Extract the basic file name and the directory prefix@>@;
  5083. }
  5084.  
  5085. @
  5086. @<Part 2@>=@[
  5087.  
  5088. SRTN 
  5089. set_name FCN((l,output_ext))
  5090.     LANGUAGE l C0("")@;
  5091.     outer_char *output_ext C1("")@;
  5092. {
  5093. int k;        
  5094.  
  5095. if(*params.outp_nm[k=lan_num(l)] == '\0')
  5096.     new_fname(params.outp_nm+k,wbflnm0,output_ext);
  5097. }
  5098.  
  5099. @ We will always write into the current directory. Thus, we strip off all
  5100. leading subdirectory information.
  5101. @<Extract...@>=
  5102. {
  5103. if(p)
  5104.     { /* Path was specified. */
  5105.     STRNCPY(wbprefix,wbflnm,
  5106.         PTR_DIFF(size_t,p,(outer_char HUGE *)wbflnm)+1); // Path.
  5107.     STRCPY(wbflnm0,p+1); // Root name.
  5108.     }
  5109. else
  5110.     { /* No path specification. */
  5111.     TERMINATE(wbprefix,0); // No path.
  5112.     STRCPY(wbflnm0,wbflnm); // Root name.
  5113.     }
  5114. }
  5115.  
  5116. @ Here we search for an embedded~`\.\#' and expand that into the web file
  5117. name.
  5118. @<Part 2@>=@[
  5119.  
  5120. outer_char HUGE *
  5121. xpn_name FCN((pout_name,buf_len,in_name,wbflnm0))
  5122.     outer_char HUGE * HUGE *pout_name C0("The expanded result")@;
  5123.     int buf_len C0("Length of above buffer")@;
  5124.     CONST outer_char *in_name C0("File name possibly having a \.\#.")@; 
  5125.     CONST outer_char wbflnm0[] C1("Basic name of the web file.")@;
  5126. {
  5127. size_t n; /* Length of the root name. */
  5128. outer_char HUGE *t,HUGE *t0=NULL;
  5129. boolean buffered = BOOLEAN(*pout_name == in_name);
  5130.  
  5131. if(buffered) t = t0 = GET_MEM("xpn buffer",buf_len,outer_char);
  5132. else t = *pout_name;
  5133.  
  5134. for(; *in_name; in_name++)
  5135.     if(*in_name == '#')
  5136.         {
  5137.         n = STRLEN(wbflnm0);
  5138.         STRNCPY(t,wbflnm0,n);
  5139.         t += n;
  5140.         }
  5141.     else *t++ = *in_name;
  5142.  
  5143. TERMINATE(t,0);
  5144.  
  5145. if(buffered)
  5146.     {
  5147.     new_fname(pout_name,t0,NULL);
  5148.     FREE_MEM(t0,"xpn buffer",buf_len,outer_char);
  5149.     }
  5150.  
  5151. return *pout_name;
  5152. }
  5153.  
  5154.  
  5155. @*1 Processing the {\tt .aux} file.
  5156. When |TeX_processor == LaTeX_p|, the \.{.aux} file is read if it is
  5157. present.  Information from the \.{\\Newlabel} commands is accumulated and
  5158. used to translate module numbers in error messages.
  5159.  
  5160. @d AUX_LEN 200
  5161. @d MAX_MOD_NAMES 3 // No more than this many names in one error message.
  5162.  
  5163. @<Typed...@>=
  5164.  
  5165. typedef struct
  5166.     {
  5167.     outer_char *secno; // The \LaTeX\ section number.
  5168.     sixteen_bits pageno, modno0;
  5169.     boolean subpage_flag; // |YES| if more than one section per page.
  5170.     } SECT_INFO;
  5171.  
  5172. IN_COMMON SECT_INFO HUGE *sect_info;
  5173.  
  5174. IN_COMMON outer_char HUGE * HUGE *mod_names;
  5175. IN_COMMON outer_char  HUGE * HUGE *next_mod_name, HUGE * HUGE *last_mod_name;
  5176.  
  5177. @
  5178. @<Allocate dynamic...@>=
  5179.  
  5180. ALLOC(SECT_INFO, sect_info, ABBREV(max_modules), max_modules, 0);
  5181.  
  5182. @ Open the \.{.aux} file, read and process each line of the file.  If the
  5183. line begins with ``\.{\\Newlabel}'', then the line is parsed.  The same
  5184. logic as in \.{fwebmac.web} is used to set the |subpage_flag| to |NO| if
  5185. there is only one section per page, or to |YES| if there are more than one.
  5186.  
  5187. @<Part 2@>=@[
  5188.  
  5189. SRTN 
  5190. read_aux(VOID)
  5191. {
  5192. outer_char aux_file_name[MAX_FILE_NAME_LENGTH];
  5193. FILE *fa;
  5194. outer_char buffer[AUX_LEN];
  5195. int aline = 0; // Counts line in the \.{.aux} file.
  5196. sixteen_bits modno, modno0;
  5197. outer_char smodno[10], smodno0[10], spageno[10], ssecno[100];
  5198. int n;
  5199. SECT_INFO *ps; // Points to current info structure.
  5200. int pmcount = 1; // Number of sections per page.
  5201. long modno_ref = -1; /* Last reference module number.  It's |long| to deal
  5202.     with the possibility of 16-bit integers, where |-1| would be the
  5203.     maximum module number. */
  5204.  
  5205. next_mod_name = mod_names = GET_MEM("mod_names", MAX_MOD_NAMES, 
  5206.     outer_char HUGE *@e); 
  5207. last_mod_name = mod_names + MAX_MOD_NAMES;
  5208.  
  5209. if(TeX_processor != LaTeX_p) 
  5210.     return;
  5211.  
  5212. @<Determine \.{aux} file name and try to open it@>@;
  5213.  
  5214. modno = 0; // In case there's nothing in the \.{.aux} file.
  5215.  
  5216. while(FGETS(buffer,AUX_LEN,fa))
  5217.     {
  5218.     aline++;
  5219.  
  5220.     if(STRNCMP(buffer,"\\Newlabel",9) != 0) 
  5221.         continue;
  5222.  
  5223.     @<Parse a \.{\\Newlabel} line@>@;
  5224.     }
  5225.  
  5226. sect_info[modno].subpage_flag = BOOLEAN(pmcount != 1);
  5227. fclose(fa);
  5228. }
  5229.  
  5230. @
  5231. @<Determine \.{aux}...@>=
  5232. {
  5233. outer_char *dot_pos; // For finding extension in file name.
  5234.  
  5235. dot_pos = OC(STRCHR(tex_fname, wt_style.ext_delimiter));
  5236.  
  5237. if(dot_pos == NULL)
  5238.     n = STRLEN(tex_fname);
  5239. else
  5240.     n = PTR_DIFF(int, dot_pos, tex_fname);
  5241.  
  5242. STRNCPY(aux_file_name, tex_fname, n);
  5243. SPRINTF(MAX_FILE_NAME_LENGTH, aux_file_name+n,`"%caux", 
  5244.     wt_style.ext_delimiter`);
  5245.  
  5246. fa = FOPEN(aux_file_name, "r");
  5247.  
  5248. if(!fa)
  5249.     {
  5250.     printf("(No %s file.)\n", (char *)aux_file_name);
  5251.     return;
  5252.     }
  5253. else
  5254.     reading(aux_file_name, YES);
  5255.  
  5256. }
  5257.  
  5258. @ The format of the \.{\\Newlabel} line is
  5259. ``\.{\\Newlabel\{12\}\{\{1.11\}\{5\}\}\{10\}}''.  We use the ANSI |sscanf|
  5260. \.{\%[\dots]} format command to read the contents of the braces as strings.
  5261.  
  5262. @<Parse a \.{\\New...@>=
  5263. {
  5264. #if(!ANSI_SSCANF)
  5265.     {
  5266.     ERR_PRINT(C,"Sorry, non-ANSI sscanf; can't read .aux file");
  5267.     return;
  5268.     }
  5269. #endif
  5270.  
  5271. n = sscanf((char *)(buffer+9),"%*c%[^}]%*3c%[^}]%*2c%[^}]%*3c%[^}]",
  5272.        (char *)smodno, (char *)ssecno, (char *)spageno, (char *)smodno0);
  5273.  
  5274. if(n != 4)
  5275.     {
  5276.     err_print(C,"Invalid \\Newlabel format in %s file, line %d",
  5277.         aux_file_name, aline);
  5278.     return;
  5279.     }
  5280.  
  5281. modno = (sixteen_bits)ATOI(smodno);
  5282.  
  5283. if(modno >= max_modules)
  5284.     OVERFLW("sections", ABBREV(max_modules));
  5285.  
  5286. ps = sect_info + modno;
  5287.  
  5288. ps->secno = GET_MEM("ps->secno",STRLEN(ssecno)+1,outer_char);
  5289. STRCPY(ps->secno, ssecno);
  5290.  
  5291. ps->pageno = (sixteen_bits)ATOI(spageno);
  5292. ps->modno0 = modno0 = (sixteen_bits)ATOI(smodno0);
  5293.  
  5294. /* Check to see whether there was more than one section on a page.  */
  5295. sect_info[modno-1].subpage_flag = YES;
  5296.  
  5297. if((long)modno0 == modno_ref)
  5298.       pmcount++; // More than one section on page.
  5299. else
  5300.       { /* Start of new page. */
  5301.     if(pmcount == 1)
  5302.       sect_info[modno-1].subpage_flag = NO;
  5303.     
  5304.     modno_ref = (long)modno0;
  5305.     pmcount = 1;
  5306.       }
  5307. }
  5308.  
  5309. @ Format a module number including section and page information.  (The
  5310. value returned from this function should be |FREE|d after use by calling
  5311. |mfree|.) 
  5312.  
  5313. @d NMOD_TEMP 200
  5314.  
  5315. @<Part 2@>=@[
  5316.  
  5317. outer_char HUGE *
  5318. mod_trans FCN((prefix,modno))
  5319.     outer_char *prefix C0("")@;
  5320.     sixteen_bits modno C1("")@;
  5321. {
  5322. outer_char HUGE *mod_temp = GET_MEM("mod_temp", NMOD_TEMP, outer_char);
  5323. SECT_INFO *ps = sect_info + modno;
  5324.  
  5325. if(ps->secno)
  5326.     {
  5327.     outer_char letter[2];
  5328.  
  5329.     letter[1] = '\0';
  5330.  
  5331.     letter[0] = (ps->subpage_flag ? 
  5332.         XCHR(modno - ps->modno0 + @'a' - 1) : '\0');
  5333.  
  5334.     if(letter[0] && XORD(letter[0]) > @'z')
  5335.         letter[0] = '?';
  5336.  
  5337.     SPRINTF(NMOD_TEMP, mod_temp, `"%s %d (sect. %s, p. %d%s)",
  5338.         prefix, modno, ps->secno, ps->pageno, letter`);
  5339.     }
  5340. else
  5341.     {
  5342.     SPRINTF(NMOD_TEMP, mod_temp, `"%s %d", prefix, modno`);
  5343.     }
  5344.  
  5345. if(next_mod_name == last_mod_name)
  5346.     CONFUSION("mod_trans","too many unfreed mod names");
  5347.  
  5348. return *(next_mod_name++) = mod_temp;
  5349. }
  5350.  
  5351. @ Undo storage areas allocated for |mod_trans|.
  5352. @<Part 2@>=@[
  5353.  
  5354. SRTN 
  5355. mfree(VOID)
  5356. {
  5357. if(!mod_names) return; // For errors happening during the command line.
  5358.  
  5359. for(--next_mod_name; next_mod_name >= mod_names; next_mod_name--)
  5360.     FREE(*next_mod_name);
  5361. }
  5362.  
  5363. @*1 Indexing languages.
  5364. Languages have both a number, name, and symbol; be
  5365. careful to keep these all in the same order. We provide two functions:
  5366. |lan_num| takes a language and returns an index number; |lan_enum| takes an
  5367. index number and returns a language.
  5368.  
  5369. @<Part 2@>=@[
  5370.  
  5371. int 
  5372. lan_num FCN((Language))
  5373.     LANGUAGE Language C1("")@;
  5374. {
  5375. proper_language:
  5376.   switch(Language)
  5377.     {
  5378.     case C:     return 0;
  5379.     case RATFOR:     return 1;
  5380.     case FORTRAN:     return 2;
  5381.     case TEX:     return 3;
  5382.     case LITERAL:     return 4;
  5383.  
  5384.     case C_PLUS_PLUS: return 5;
  5385.     case RATFOR_90: return 6;
  5386.     case FORTRAN_90: return 7;
  5387.  
  5388.     case NO_LANGUAGE: Language = GLOBAL_LANGUAGE; goto proper_language;
  5389.     default: Language = global_language; goto proper_language;
  5390.     }
  5391. }
  5392.  
  5393. LANGUAGE 
  5394. lan_enum FCN((num))
  5395.     int num C1("Integer index of the language.")@;
  5396. {
  5397. switch(num)
  5398.     {
  5399.     case 0: return C;
  5400.     case 1: return RATFOR;
  5401.     case 2: return FORTRAN;
  5402.     case 3: return TEX;
  5403.     case 4: return LITERAL;
  5404.  
  5405.     case 5: return C_PLUS_PLUS;
  5406.     case 6: return RATFOR_90;
  5407.     case 7: return FORTRAN_90;
  5408.  
  5409.     default: return NO_LANGUAGE;
  5410.     }
  5411. }
  5412.  
  5413. @ The function |lan_index| is like |lan_num|, but it compresses
  5414. |RATFOR| and |FORTRAN| onto the same index.
  5415.  
  5416. @<Part 2@>=@[
  5417. int 
  5418. lan_index FCN((Language))
  5419.     LANGUAGE Language C1("")@;
  5420. {
  5421. proper_language:
  5422.   switch(Language)
  5423.     {
  5424.     case C:     return 0;
  5425.  
  5426.     case RATFOR: 
  5427.     case FORTRAN:     return 1;
  5428.  
  5429.     case TEX:     return 2;
  5430.     case LITERAL:     return 3;
  5431.  
  5432.     case C_PLUS_PLUS: return 4;
  5433.  
  5434.     case RATFOR_90:
  5435.     case FORTRAN_90: return 5;
  5436.  
  5437.     case NO_LANGUAGE: Language = GLOBAL_LANGUAGE; goto proper_language;
  5438.     default: Language = global_language; goto proper_language;
  5439.     }
  5440. }
  5441.  
  5442. @*1 Processing {\tt fweb.sty}. 
  5443. Here we construct the name of the style file, and read that file.
  5444.  
  5445. @<Glob...@>=
  5446.  
  5447. IN_COMMON outer_char style_file_name[MAX_FILE_NAME_LENGTH] 
  5448.     CSET(STYLE_FILE_NAME);
  5449. IN_COMMON boolean renamed_style CSET(NO);
  5450.  
  5451. @
  5452. @<Read the style file@>=
  5453. {
  5454. read_sty(style_file_name,renamed_style); // See \.{style.web}.
  5455.  
  5456. @<Convert selected fields to |ASCII|@>@;
  5457. }
  5458.  
  5459. @ The style file is read as |outer_char|.  Some fields are best left that
  5460. way, while others need to be converted to |ASCII|.
  5461.  
  5462. @<Convert selected...@>=
  5463. {
  5464. DOT_DELIMITER HUGE *d = &wt_style.dot_delimiter;
  5465.  
  5466. d->begin = XORD(d->begin);
  5467. d->end = XORD(d->end);
  5468.  
  5469. if(program==tangle)
  5470.     {
  5471.     t_style.ASCII_fcn = x__to_ASCII((outer_char *)t_style.ASCII_fcn);
  5472.     }
  5473. }
  5474.  
  5475. @*1 Processing command-line arguments.
  5476. Processing arguments is done in a
  5477. separate function because it's called several times: once for the command
  5478. line, and (possibly) each time there's a language change.
  5479.  
  5480. @d THE_FILE_NAME (a_file_name ? pa : (outer_char HUGE *)"stdin")
  5481.  
  5482. @<Glob...@>=
  5483.  
  5484. IN_COMMON boolean doing_cmd_line; // Command line or optional arguments?
  5485. IN_COMMON boolean cmd_prms; // Stuff from ini file or from command line?
  5486.  
  5487. @
  5488. @<Part 2@>=@[
  5489.  
  5490. SRTN 
  5491. do_args FCN((doing_cmd_line0,argc,argv,pfound_web,wbflnm,pfound_change))
  5492.     boolean doing_cmd_line0 C0("")@;
  5493.     int argc C0("")@;
  5494.     outer_char HUGE *argv[] C0("")@;
  5495.     boolean HUGE *pfound_web C0("")@;
  5496.     outer_char wbflnm[] C0("")@;
  5497.     boolean HUGE *pfound_change C1("")@;
  5498. {
  5499. boolean a_file_name; // Is an actual file name supplied from the command line?
  5500. outer_char HUGE *dot_pos; /* Position of |'.'| (or more generally,
  5501.             |wt_style.ext_delimiter|) in the argument */
  5502. IN_STYLE outer_char HUGE *sprm_ptr0, HUGE *sprm_ptr;
  5503.  
  5504. doing_cmd_line = doing_cmd_line0;
  5505. cmd_prms = NO;
  5506.  
  5507. *pfound_web = *pfound_change = NO;
  5508.  
  5509. while (argc-- > 0) 
  5510.     {
  5511.     if(!cmd_prms && argv == pn0)
  5512.         { /* Note beginning of command-line parameters. */
  5513.         cmd_prms = YES;
  5514.         sprm_ptr0 = sprm_ptr;
  5515.         }
  5516.  
  5517.     pa = *(argv++); /* Beginning of current argument string. */
  5518.  
  5519. /* Arguments that don't begin with a hyphen, or that are just a single
  5520. hyphen with no trailing stuff, mean file names. */
  5521.     if ( (a_file_name= BOOLEAN(*pa != '-')) || *(pa+1)=='\0') 
  5522.     @<Process a file name@>@;
  5523.     else 
  5524.     @<Handle flag argument@>;
  5525.     }
  5526. }
  5527.  
  5528. @
  5529. @<Process a file name@>=
  5530. {
  5531. if(doing_cmd_line)
  5532.     {
  5533.         if (!*pfound_web) 
  5534.         {
  5535.         STRCPY(wbflnm,THE_FILE_NAME);
  5536.         *pfound_web = YES;
  5537.         }
  5538.         else if (!*pfound_change) 
  5539.         @<Make |change_file_name| from |fname|@>@;
  5540.         else 
  5541.         @<Print change file warning message and |continue|@>;
  5542.     }
  5543. else 
  5544.     {
  5545.     printf("\n! File name \"%s\" is not allowed as optional argument; \
  5546. ignored.\n", pa);
  5547.     mark_harmless;
  5548.     }
  5549. }     
  5550.  
  5551. @<Make |change_file_name|...@>=
  5552. {
  5553.   if (a_file_name && STRLEN(pa) > MAX_FILE_NAME_LENGTH-5)
  5554.     too_long();
  5555.  
  5556. /* For VMS, should skip over brackets here. */
  5557.  
  5558.   if (a_file_name && (dot_pos=OC(STRCHR(pa,wt_style.ext_delimiter)))==NULL
  5559.          && !try_extensions)
  5560.     {
  5561.     SPRINTF(MAX_FILE_NAME_LENGTH,change_file_name,`"%s%cch",
  5562.         pa,wt_style.ext_delimiter`);
  5563.     }
  5564.   else 
  5565.     {
  5566.     SPRINTF(MAX_FILE_NAME_LENGTH,change_file_name,`"%s",THE_FILE_NAME`);
  5567.     }
  5568.  
  5569.   *pfound_change=YES;
  5570. }
  5571.  
  5572. @ After a language command, there may be optional arguments that must also
  5573. be parsed.  
  5574.  
  5575. @d ARG_BUFFER_SIZE 500
  5576. @d COPY_TO_ARG_BUFFER(c) if(pa < arg_buffer_end) *pa++ = c;
  5577.             else OVERFLW("arg buffer bytes","")@;
  5578. @d MAX_ARGS 100
  5579.  
  5580. @<Part 2@>=@[
  5581.  
  5582. SRTN 
  5583. opt_args FCN((l))
  5584.     ASCII l C1("")@;
  5585. {
  5586. ASCII arg_buffer[ARG_BUFFER_SIZE],*pa = arg_buffer,
  5587.     *arg_buffer_end = arg_buffer + ARG_BUFFER_SIZE;
  5588. int argc;
  5589. outer_char HUGE *args[MAX_ARGS];
  5590.  
  5591. ini_language(l); /* Set language and initialize flags. */ 
  5592.  
  5593. if(!IS_WHITE(*loc))
  5594. {
  5595. if(*loc != @'[')
  5596.     { /* Unbracketed command, as in \.{@@c++}. */
  5597.     *pa++ = @'-';     // Make it an honest command.
  5598.     *pa++ = l;    // Don't forget the language.
  5599.  
  5600.     WHILE()
  5601.         {
  5602.         if(IS_WHITE(*loc) || *loc==@'[') break;
  5603.         if(*loc==@'|' && parsing_mode==INNER)
  5604.             {
  5605.             ERR_PRINT(C,"WARNING:  Code mode ended during \
  5606. unbracketed optional argument.  Should there be white space after \
  5607. language command?");
  5608.             break;
  5609.             }
  5610.         COPY_TO_ARG_BUFFER(*loc++);
  5611.         }
  5612.     }
  5613.  
  5614. /* We allow bracketed commands to follow unbracketed ones. */
  5615. if(*loc == @'[')
  5616.     {
  5617.     COPY_TO_ARG_BUFFER(@' ');
  5618.  
  5619.     for(loc++; ; )
  5620.         { // Skip the opening bracket; copy contents.
  5621.         if(*loc == @']') 
  5622.             {
  5623.             loc++; /* Skip the ending bracket. */
  5624.             break;
  5625.             }
  5626.         COPY_TO_ARG_BUFFER(*loc++);
  5627.         }
  5628.     }    
  5629.  
  5630. TERMINATE(pa,0); /* Terminate |arg_buffer|. */
  5631. argc = to_args(to_outer(arg_buffer),args,MAX_ARGS);
  5632. do_args(NO,argc,args,&found_web,(outer_char *)NULL,&found_change);
  5633. }
  5634.  
  5635. fin_language(); /* Do any further flag setup. */
  5636. }
  5637.  
  5638. @ Set the language, and also set up flags for particular language.
  5639. The input parameter is the letter corresponding to the basic language.
  5640. Subsidiary dialect flags such as~|Cpp| or~|Fortran88| are used to determine
  5641. the full language.
  5642. @<Part 2@>=@[
  5643.  
  5644. SRTN 
  5645. ini_language FCN((l))
  5646.     ASCII l C1("Basic language code")@;
  5647. {
  5648. /* First we set the language. */
  5649. switch(l)
  5650.     {
  5651.    @<|ASCII| cases for |C|@>:
  5652.     language = (Cpp ? C_PLUS_PLUS : C);
  5653.     break;
  5654.  
  5655.    @<|ASCII| cases for |RATFOR|@>:
  5656.     if(!RAT_OK("Invalid @@r command; language set to C")) language = C;
  5657.     else language = (Fortran88 ? RATFOR_90 : RATFOR);
  5658.     break;
  5659.  
  5660.    @<|ASCII| cases for |FORTRAN|@>:
  5661.     language = (Fortran88 ? FORTRAN_90 : FORTRAN);
  5662.     break;
  5663.  
  5664.    @<|ASCII| cases for |LITERAL|@>:
  5665.     language = LITERAL;
  5666.     break;
  5667.  
  5668.    @<|ASCII| cases for |TEX|@>:
  5669.     language = TEX;
  5670.     break;
  5671.     }
  5672.  
  5673. /* The following is for checking an override of the command-line language
  5674. in the limbo section. */
  5675. if(doing_cmd_line) cmd_language = language;
  5676.  
  5677. ini0_language();
  5678. }
  5679.  
  5680. @ Set parameters for each language. Note that certain flags, such as
  5681. |free_form_input|, are really arrays that can have a different value
  5682. depending on the language.
  5683.  
  5684. @<Part 2@>=@[
  5685.  
  5686. SRTN 
  5687. ini0_language(VOID)
  5688. {
  5689. /* Set the output language. */
  5690. out_language = OUT_LANGUAGE(language);
  5691.  
  5692. /* Storing these numbers cuts down on the overhead of function calls.  It
  5693. also enables us to use the macros in the following |switch| properly. */
  5694. language_index = (short)lan_index(language); // Maybe no longer used.
  5695. language_num = (short)lan_num(language);
  5696.  
  5697. index_flag = YES; // Most languages will cross-reference.
  5698.  
  5699. switch(language)
  5700.     {
  5701.    case C:
  5702.     Cpp = NO;
  5703.     break;
  5704.  
  5705.    case C_PLUS_PLUS:
  5706.     Cpp = YES; // Just a shorthand for |language==CPP|.
  5707.     break;
  5708.  
  5709.    case FORTRAN:
  5710.    case FORTRAN_90:
  5711.     free_form_input = global_params.Free_form_input[language_num];
  5712. /* This can be turned on by the \.{-n\\} or \.{-n\&} options. */
  5713.     auto_semi = global_params.Auto_semi[language_num];
  5714.     break;    
  5715.  
  5716.    case RATFOR:
  5717.    case RATFOR_90:
  5718.     if(nuweb_mode)
  5719.         err_print(C, "SORRY, but Ratfor doesn't work yet with the N \
  5720. mode!") ;
  5721.     auto_semi = global_params.Auto_semi[language_num];
  5722.     if(auto_semi) free_form_input = NO;
  5723.     break;
  5724.  
  5725.    case LITERAL:
  5726.     nuweb_mode = YES;
  5727.     line_info = NO;
  5728.     index_flag = NO;
  5729.     break;
  5730.  
  5731.    case TEX:
  5732.     break;
  5733.  
  5734.    default:
  5735.     break;
  5736.     }
  5737. }
  5738.  
  5739. @ Now handle any parameter modifications after optional arguments have been
  5740. read. 
  5741. @<Part 2@>=@[
  5742.  
  5743. SRTN 
  5744. fin_language(VOID)
  5745. {
  5746. ini_reserved(language);
  5747.  
  5748. switch(language)
  5749.     {
  5750.    case C:
  5751.     break;
  5752.  
  5753.    case RATFOR:
  5754.     break;
  5755.  
  5756.    case FORTRAN:
  5757.     break;
  5758.  
  5759.    case LITERAL:
  5760.     break;
  5761.  
  5762.    case TEX:
  5763.     break;
  5764.  
  5765.    default:
  5766.     break;
  5767.     }
  5768.  
  5769. frz_params(); // Make any local parameters.
  5770.  
  5771. /* Output file name.  For |program == weave|, |out_file == tex_file| by a
  5772. macro definition in \.{typedefs.hweb}. */
  5773. if(program == tangle)
  5774.     out_file = params.outp_file[lan_num(out_language)];
  5775. }
  5776.  
  5777. @ Call this routine whenever |params| is reset.
  5778. @<Part 2@>=@[
  5779.  
  5780. SRTN 
  5781. frz_params(VOID)
  5782. {
  5783. @#if 0
  5784.     if(auto_semi && !Fortran88) free_form_input = NO;
  5785. @#endif
  5786.  
  5787. cont_char = XORD(in_escape); // Current continuation character (in |ASCII|).
  5788. free_90 = BOOLEAN(Fortran88 && free_form_input);
  5789. free_Fortran = BOOLEAN((language==FORTRAN_90) && free_90);
  5790.  
  5791. if(is_FORTRAN_(language))
  5792.     auto_semi = BOOLEAN(!free_Fortran);
  5793.  
  5794. if(program==tangle && free_90)
  5795.     switch(language)
  5796.         {
  5797.        case FORTRAN_90:
  5798.        case RATFOR_90:
  5799.         begin_comment_char[lan_num(FORTRAN_90)] = 
  5800.             (outer_char)CHOICE(free_90,'!','C');
  5801.         break;
  5802.  
  5803.        default: ;
  5804.         }    
  5805.     
  5806. }
  5807.  
  5808. @ At this point the effective command line is in |outer_char| form in
  5809. |arg_buffer|. 
  5810. @<Part 2@>=@[
  5811.  
  5812. int 
  5813. to_args FCN((p,args,max_args))
  5814.     outer_char HUGE *p C0("")@;
  5815.     outer_char HUGE *args[] C0("")@;
  5816.     int max_args C1("")@;
  5817. {
  5818. int n;
  5819.  
  5820. for(n=0; *p; p++)
  5821.     {
  5822.     if(n >= max_args) 
  5823.         {
  5824.         err_print(C,"Too many command-line or optional \
  5825. arguments; must be < %d",max_args);
  5826.         break;
  5827.         }
  5828.     while(isspace(*p)) p++; // Skip leading white space.
  5829.     args[n++] = p; // Record start and count.
  5830.  
  5831.     for(p++; *p; p++)
  5832.         if(isspace(*p) || !*p) break; // Find end of argument.
  5833.  
  5834.     if(!*p) break; // Get out at end of line.
  5835.     TERMINATE(p,0); // Mark end of argument.
  5836.     }
  5837.  
  5838. return n; // Number of arguments found.
  5839. }
  5840.  
  5841. @
  5842. @<Initialize static...@>=
  5843.  
  5844. SET_VAL(mbuf_size,ABBREV(mbuf_size));
  5845.  
  5846. @ These are for automatic statement numbering.
  5847.  
  5848. @d STARTING_DO_NUM UL(90000L)
  5849.  
  5850. @<Common...@>=
  5851.  
  5852. IN_COMMON STMT_LBL max_stmt CSET(STARTING_DO_NUM);
  5853. IN_COMMON int not; // For the negations of options.
  5854.  
  5855. @ For many of the flags, a minus option, as \.{-flag}, means to do the
  5856. opposite of the default.  Furthermore, if the format is \.{--flag}, this is
  5857. the opposite of \.{-flag}.  Negating the usual flag value can be
  5858. accomplished by exclusive-oring it with |not|, where |not == YES| when we
  5859. have the extra hyphen, or |NO|~otherwise.
  5860.  
  5861. @d NOT(flag) BOOLEAN((!flag) ^ not)
  5862.  
  5863. @<Handle flag...@>=
  5864. @{
  5865. outer_char mc;
  5866. outer_char cmd_symbol; // The character after the hyphen.
  5867.  
  5868. @b
  5869. pa++; // |pa| now points to the character after the hyphen.
  5870.  
  5871. if(*pa == '-')
  5872.     {     // Second hyphen means negate.
  5873.     not = YES;
  5874.     pa++;     // Skip second hyphen.
  5875.     }
  5876. else 
  5877.     not = NO;
  5878.  
  5879. reswitch:
  5880.   switch(cmd_symbol= *pa++)
  5881.     { /* |pa| now positioned after command symbol---e.g., to optional
  5882. argument. */
  5883.    case '1':
  5884.    case '2':
  5885.     tracing = cmd_symbol - '0'; @~ break;
  5886.  
  5887.    case '@@':
  5888.     @<Option \.{-@@}: print control codes@>@;
  5889.     break;
  5890.  
  5891.    case 'a':
  5892.    case 'A': 
  5893.     @<Option \.{-A}: translations to |ASCII|@>@;
  5894.     break;
  5895.  
  5896.    case 'b':
  5897.     @<Option \.{-b}: block numbering@>@;
  5898.     break;
  5899.  
  5900.    case 'B':
  5901.     beeps = NOT(BEEPS);
  5902.     break;
  5903.  
  5904.    @<|outer_char| cases for |C|@>:
  5905.     @<Option \.{-c}:  C and \Cpp@>@;
  5906.     break;
  5907.  
  5908.    case 'D':
  5909.     @<Option \.{-D}:  see reserved words@>@;
  5910.     break;
  5911.    case 'd':
  5912.     number_dos = NOT(NUMBER_DOS); // NOTE:  falls through to next case!
  5913.  
  5914.    case ':':
  5915.     @<Option \.{-:}:  starting line number@>@;
  5916.     break;
  5917.  
  5918.    case 'e':
  5919.     try_extensions = NOT(TRY_EXTENSIONS); @~ break;
  5920.  
  5921.    case 'E':
  5922.     @<Option \.{-E}:  change extension delimiter@>@;
  5923.     break;
  5924.  
  5925.    case 'f':
  5926.     subscript_fcns = NOT(SUBSCRIPT_FCNS); @~ break;
  5927.  
  5928.    case 'F':
  5929.     compare_outfiles = NOT(COMPARE_OUTFILES); @~ break;
  5930.  
  5931.    case 'g':
  5932.     dbg_output = NOT(DBG_OUTPUT); @~ break;
  5933.  
  5934.    case 'h':
  5935.     @<Issue warning if this command is negated@>@;
  5936.     help(); 
  5937.     break;
  5938.  
  5939.    case 'H':
  5940.     @<Option \.{-H}:  scan include files@>@;
  5941.     break;
  5942.  
  5943.    case 'i':
  5944.     @<Option \.{-i}:  skip include files@>@;
  5945.     break;
  5946.  
  5947.    case 'I':
  5948.     more_includes(pa); // Add to include path list.
  5949.     break;
  5950.  
  5951.    case 'j':
  5952.     skip_ifiles = NOT(SKIP_IFILES); @~ break;
  5953.  
  5954.    case 'k':
  5955.     lc_keywords = NOT(LC_KEYWORDS);  @~ break;
  5956.  
  5957.    case 'l':
  5958.     @<Option \.{-l}:  print input lines for debugging@>@;
  5959.     break; 
  5960.  
  5961.    case 'L':
  5962.     @<Option \.{-L}:  generalized language switch@>@;
  5963.     break;
  5964.  
  5965.    case 'm':
  5966.    case 'M':
  5967.     @<Option \.{-m}:  macro definitions etc.@>@;
  5968.     break;            
  5969.  
  5970.    
  5971.    case 'N':
  5972.     nuweb_mode = NOT(NUWEB_MODE);
  5973.     break;
  5974.  
  5975.    case 'n':
  5976.     FR_args(FORTRAN);
  5977.     break;
  5978.  
  5979.    case 'o':
  5980.     overload_ops = NOT(OVERLOAD_OPS);
  5981.     break;
  5982.  
  5983.    case 'p':
  5984.     @<Option \.{-p}:  collect style-file parameter@>@;
  5985.     break;
  5986.  
  5987.    case 'P':
  5988.     @<Option \.{-P}:  specify the \TeX\ processor@>@;
  5989.     break;
  5990.  
  5991.    case 'q':
  5992.     free_form_input = NOT(FREE_FORM_INPUT);
  5993.     Ratfor77 = NOT(RATFOR77);
  5994.     break;
  5995.  
  5996.    @<|outer_char| cases for |RATFOR|@>:
  5997.     if(!RAT_OK("Language command ignored")) 
  5998.         break;
  5999.     FR_args(RATFOR);
  6000.     break;
  6001.  
  6002.    case 's':
  6003.     @<Option \.{-s}:  statistics@>@;
  6004.     break;
  6005.  
  6006.    case 't':
  6007.     @<Option \.{-t}:  truncate identifiers@>@;
  6008.     break;
  6009.  
  6010.    case 'T':
  6011.     @<Option \.{-T}:  various flags for \FTANGLE@>@;
  6012.     break;
  6013.  
  6014.    case 'u':
  6015.     @<Option \.{-u}:  undefine a macro@>@;
  6016.     break;
  6017.  
  6018.    case 'U':
  6019.     lowercase_tokens = NOT(LOWERCASE_TOKENS);
  6020.     break;
  6021.  
  6022.    case 'v':
  6023.     all_cmnts_verbatim = NOT(ALL_CMNTS_VERBATIM);
  6024.     break;
  6025.  
  6026.    case 'w':
  6027.     if(*pa)    
  6028.         new_fname(&fwebmac,pa,NULL);
  6029.     else 
  6030.         input_macros = NOT(INPUT_MACROS);
  6031.  
  6032.     break;
  6033.  
  6034.    case 'W':
  6035.     @<Option \.{-W}:  various flags for \FWEAVE@>@;
  6036.     break;
  6037.  
  6038.    case 'x':
  6039.     flags3(BOOLEAN(not));
  6040.     break;
  6041.  
  6042.    case 'X':
  6043.     flags3(BOOLEAN(!not));
  6044.     break;
  6045.         
  6046.    case 'y':
  6047.     @<Option \.{-y}:  process allocation command@>@;
  6048.     break;
  6049.  
  6050.    case 'z':
  6051.     @<Option \.{-z}@>@;
  6052.     break;
  6053.  
  6054.    case 'Z':
  6055.     prn_style_defaults = BOOLEAN(!not);
  6056.     style_args = pa;
  6057.     info_option = YES;
  6058.     break;
  6059.  
  6060.    case '.':
  6061.     dot_constants = NOT(DOT_CONSTANTS);
  6062.     break;
  6063.  
  6064.    case '\\':
  6065.     bslash_continued_strings = NOT(BSLASH_CONTINUED_STRINGS);
  6066.     break;
  6067.  
  6068.    case '?':
  6069.     chk_stmts = NOT(CHK_STMTS); 
  6070.     break;
  6071.  
  6072.    case '[':
  6073.     translate_brackets = NOT(TRANSLATE_BRACKETS); 
  6074.     break;
  6075.  
  6076.    case '(':
  6077.    case ')':
  6078.     chk_ifelse = NOT(CHK_IFELSE); @~break;
  6079.  
  6080.    case '>':
  6081.    case '=':
  6082.     @<Issue warning if this command is negated@>@;
  6083.     if(doing_cmd_line || phase==2) 
  6084.         redirect_output();
  6085.     break;
  6086.  
  6087.    case '/':
  6088.     params.CPP_comments[LN(FORTRAN)] 
  6089.         = params.CPP_comments[LN(FORTRAN_90)]
  6090.         = params.CPP_comments[LN(RATFOR)]
  6091.         = params.CPP_comments[LN(RATFOR_90)] 
  6092.         = BOOLEAN(YES ^ not);
  6093.     break;
  6094.  
  6095.    case '!':
  6096.     params.Point_comments[LN(FORTRAN)]
  6097.         = params.Point_comments[LN(FORTRAN_90)]
  6098.         = params.Point_comments[LN(RATFOR)] 
  6099.         = params.Point_comments[LN(RATFOR_90)]
  6100.         = BOOLEAN(YES ^ not);
  6101.     break;
  6102.  
  6103.    case '+':
  6104.     compound_assignments = NOT(COMPOUND_ASSIGNMENTS); @~ break;
  6105.  
  6106.    case '#':
  6107.     line_info = NOT(LINE_INFO); @~ break;
  6108.  
  6109.    default: 
  6110.     bad_option();
  6111.     break;
  6112.     }
  6113. }
  6114.  
  6115.  
  6116. @*1 Option {\tt -@@}.  
  6117.  
  6118. @<Option \.{-@@...@>=
  6119. {
  6120. if(!at_codes)
  6121.     {
  6122.     at_codes = GET_MEM("at_codes", 200, ASCII); // 200 is KLUDGE!
  6123.     at_codes[0] = '\0';
  6124.     }
  6125.  
  6126. STRCAT(at_codes, to_ASCII(pa));
  6127. info_option = YES;
  6128. }
  6129.  
  6130. @*1 Option {\tt -A}.
  6131. ASCII translations are off by default on |ASCII|
  6132. machines; they're turned on for the cases that really need.  Therefore,
  6133. this flag is for debugging.
  6134.  
  6135. @<Option \.{-A}...@>=
  6136. {
  6137. #if(TRANSLATE_ASCII || DEBUG_XCHR)
  6138.     translate_ASCII = YES;  // Force it for the cases that really need it.
  6139. #else
  6140.     translate_ASCII = NOT(TRANSLATE_ASCII0); 
  6141. #endif
  6142. }
  6143.  
  6144.  
  6145. @*1 Option {\tt -b}.
  6146.  
  6147. @<Option \.{-b}...@>=
  6148. {
  6149. params.Block_nums[LN(FORTRAN)] 
  6150.     = params.Block_nums[LN(FORTRAN_90)]
  6151.     = params.Block_nums[LN(RATFOR)]
  6152.     = params.Block_nums[LN(RATFOR_90)] 
  6153.     = NOT(BLOCK_NUMS);
  6154. }
  6155.  
  6156. @*1 Option {\tt -c}.
  6157. Options related to the C~language.
  6158.  
  6159. @<Option \.{-c}...@>=
  6160. {
  6161. @<Issue warning if this command is negated@>@;
  6162.  
  6163. Cpp = BOOLEAN(*pa == '+'); /* The commands \.{-c+} or \.{-c++} turn on
  6164.                 knowledge of \.{C++}. */ 
  6165. if(Cpp) while(*pa == '+') pa++; // Skip the pluses.
  6166.  
  6167. switch(*pa)
  6168.     {
  6169.    case '{':
  6170.     set_filter(Cpp ? C_PLUS_PLUS : C); 
  6171.     break;
  6172.  
  6173.    case '\0':
  6174.     ini_language(XORD(*LANGUAGE_CODE(C)));
  6175.     break;
  6176.  
  6177.    default:
  6178.     break;
  6179.     }
  6180. }
  6181.  
  6182.  
  6183. @*1 Option {\tt -D}.
  6184. See reserved words.  The form `\.{-D[ikr]word}' says to print only the
  6185. intrinsic functions (`\.i'), and/or the \Fortran/\Ratfor\ keywords (`\.k'),
  6186. and/or the reserved words (`\.r') beginning with `\.{word}'.
  6187.  
  6188. Note that we can't immediately execute |see_reserved| because dynamic
  6189. memory and the |name_dir| hasn't been set up yet.
  6190. @<Option \.{-D}...@>=
  6191. {
  6192. if(*pa == '[')
  6193.     @<Get the optional arguments to `\.{-D}'@>@;
  6194. else if(!(*pa=='\0' || *pa=='*' || isalpha(*pa)))
  6195.     {
  6196.     err_print(C, "Either nothing, a letter, '*', or '[' must follow `-D'; \
  6197. option ignored");
  6198.     break;
  6199.     }
  6200.  
  6201. rsrvd.args = pa;
  6202.  
  6203. if(!*pa)
  6204.     rsrvd.reserveds = YES;
  6205.  
  6206. info_option = YES;
  6207. }
  6208.  
  6209. @
  6210. @<Get the optional arguments to `\.{-D}'...@>=
  6211. {
  6212. for(++pa; *pa != ']'; pa++)
  6213.     switch(*pa)
  6214.         {
  6215.        case 'i':
  6216.        case 'I':
  6217.         rsrvd.intrinsics = YES;
  6218.         break;
  6219.  
  6220.        case 'k':
  6221.        case 'K':
  6222.         rsrvd.keywords = YES;
  6223.         break;
  6224.  
  6225.        case 'r':
  6226.        case 'R':
  6227.         rsrvd.reserveds = YES;
  6228.         break;
  6229.  
  6230.        case '\0':
  6231.         err_print(C, "Missing ']' inserted after optional argument \
  6232. list to `-D'");
  6233.         break;
  6234.  
  6235.        default:
  6236.         err_print(C, "Optional argument to `-D' must be one of \
  6237. 'i', 'k', or 'r'");
  6238.         break;
  6239.         }
  6240.  
  6241. pa++; // Advance past the \.{']'}.
  6242. }
  6243.  
  6244. @*1 Options {\tt -d} and {\tt -:}.
  6245. Process starting line number.
  6246.  
  6247. @<Option \.{-:}...@>=
  6248. {
  6249. @<Issue warning if this command is negated@>@;
  6250.  
  6251. max_stmt = STARTING_DO_NUM;
  6252.  
  6253. if(*pa)
  6254.     { // Optional starting line number specified.
  6255.     max_stmt = ATOL(pa);
  6256.  
  6257.     if(max_stmt > 99999L)
  6258. err_print(C,"Starting DO number must be < 99999; assuming %lu",
  6259.         max_stmt = STARTING_DO_NUM);
  6260.     }
  6261. }
  6262.  
  6263.  
  6264. @*1 Option {\tt -E}.
  6265. Change the extension delimiter from its default value of period.
  6266.  
  6267. @<Option \.{-E}...@>=
  6268. {
  6269. @<Issue warning if this command is negated@>@;
  6270.  
  6271. if(*pa)    wt_style.ext_delimiter = *pa;
  6272. else err_print(C,"Missing character after -E option; option ignored");
  6273. }
  6274.  
  6275. @*1 Option {\tt -h}.
  6276. Enter the help package with \.{-h}. (But it's not here yet!) 
  6277.  
  6278. @<Part 2@>=@[
  6279.  
  6280. SRTN 
  6281. help(VOID)
  6282. {
  6283. puts("! For help, please see the texinfo entry for FWEB or use options\n\ 
  6284.  '-@@' (query control codes),\n\
  6285.  '-D' (query reserved words),\n\
  6286.  '-y' (query memory allocations),\n\
  6287.  '-Z' (query style-file parameters)."); 
  6288.  
  6289. usage("", NO); // In lieu of anything better.
  6290. }
  6291.  
  6292.  
  6293. @*1 Option {\tt -H}.
  6294.  
  6295. @<Option \.{-H}...@>=
  6296. {
  6297. outer_char HUGE *p = pa;
  6298.  
  6299. more_H:
  6300. switch(*p++)
  6301.     {
  6302.    case 'r':
  6303.     rmv_files = NOT(RMV_FILES); // For debugging.
  6304.     goto more_H;
  6305.  
  6306.    case 'x':
  6307.     quoted_includes = NOT(QUOTED_INCLUDES);
  6308.     goto more_H;
  6309.  
  6310.    case 'X':
  6311.     all_includes = NOT(ALL_INCLUDES);
  6312.     goto more_H;
  6313.  
  6314.    case '\0':
  6315.     read_iformats = NOT(READ_IFORMATS); 
  6316.     break;
  6317.  
  6318.    default:
  6319.     bad_option();
  6320.     break;
  6321.     }
  6322. }
  6323.  
  6324. @*1 Option {\tt -i}.
  6325. When an include file is referenced by `\.{@@I}', then `\.{-i}' says for
  6326. \FWEAVE\ to not print the contents of the file.  `\.{-i!}' means to not
  6327. even read it (probably not useful).  By default, index entries are not made
  6328. for files that aren't printed; `\.{-ix}' says to index them anyway.
  6329.  
  6330. @<Option \.{-i}...@>=
  6331. {
  6332. switch(*pa)
  6333.     {
  6334.    case 'x':
  6335.     index_hidden = NOT(INDEX_HIDDEN); // Falls through.
  6336.  
  6337.    case '\0':
  6338.     toggle_includes = NOT(TOGGLE_INCLUDES); @~ break;
  6339.  
  6340.    case '!':
  6341.     skip_includes = NOT(SKIP_INCLUDES); @~ break;
  6342.  
  6343.    default:
  6344.     bad_option(); @~ break;
  6345.     }
  6346. }
  6347.  
  6348. @*1 Option {\tt -l}.
  6349. Print input lines for debugging; see |input_ln|.
  6350.  
  6351. @<Option \.{-l}...@>=
  6352. {
  6353. prn_input_lines = BOOLEAN(YES ^ not);
  6354.  
  6355. if(*pa) 
  6356.     if(isdigit(*pa) || *pa=='-')
  6357.         {
  6358.         outer_char *colon_pos;
  6359.  
  6360.         start_line = ATOL(pa);
  6361.         if((colon_pos=OC(STRCHR(pa,':'))) == NULL)
  6362.            end_line = LONG_MAX;
  6363.         else end_line = ATOL(colon_pos + 1);
  6364.         }
  6365.                 
  6366.     else
  6367.         {
  6368.         printf("! Option `-l' must be followed by integer.\n");
  6369.         mark_harmless;
  6370.         }
  6371.  
  6372. /* Negative line numbers are undocumented; they're for the developer. */
  6373. if(start_line < 0)
  6374.     {
  6375.     start_line = -start_line;
  6376.     prn_input_addresses = YES;
  6377.     }
  6378. }
  6379.  
  6380.  
  6381. @*1 Option {\tt -L}.
  6382. Generalized language switch.
  6383.  
  6384. @<Option \.{-L}...@>=
  6385. {
  6386. switch(*pa)
  6387.     {
  6388.    @<|outer_char| cases for |C|@>:
  6389.    @<|outer_char| cases for |FORTRAN|@>:
  6390.    @<|outer_char| cases for |RATFOR|@>:
  6391.     goto reswitch;
  6392.  
  6393.    @<|outer_char| cases for |LITERAL|@>:          
  6394.     @<Issue warning if this command is negated@>@;
  6395.     switch(*(pa+1))
  6396.         {
  6397.        case '{':
  6398.         set_filter(LITERAL);
  6399.         break;
  6400.  
  6401.        case '\0':
  6402.         ini_language(XORD(*LANGUAGE_CODE(LITERAL)));
  6403.         break;
  6404.  
  6405.        default:
  6406.         bad_lcmd("argument",pa-2);
  6407.         break;
  6408.         }
  6409.     break;
  6410.  
  6411.    @<|outer_char| cases for |TEX|@>:
  6412.     @<Issue warning...@>@;
  6413.     switch(*(pa+1))
  6414.         {
  6415.        case '{':
  6416.         set_filter(TEX);
  6417.         break;
  6418.  
  6419.        case '\0':
  6420.         ini_language(XORD(*LANGUAGE_CODE(TEX)));
  6421.         break;
  6422.  
  6423.        default:
  6424.         bad_lcmd("argument",pa-2);
  6425.         break;
  6426.         }
  6427.     break;
  6428.  
  6429.    default:
  6430.     bad_lcmd("command",pa-2);
  6431.     break;
  6432.     }
  6433. }
  6434.  
  6435. @ Not all options set the language.
  6436. @<Part 2@>=@[
  6437.  
  6438. SRTN 
  6439. FR_args FCN((Language))
  6440.     LANGUAGE Language C1("")@;
  6441. {
  6442. int lnum;
  6443.  
  6444. @<Issue warning if this command is negated@>@;
  6445.  
  6446. if(isdigit(*pa))
  6447.     switch(*pa++)
  6448.         { // Options \.{-n7} or \.{-n9}.
  6449.         case '7': 
  6450.             Fortran88 = NO; 
  6451.             ini_language(XORD(*LANGUAGE_CODE(Language)));
  6452.             break;
  6453.  
  6454.         case '9':
  6455.             Fortran88 = YES;
  6456.             ini_language(XORD(*LANGUAGE_CODE(Language)));
  6457.             break;
  6458.  
  6459.         default: 
  6460.             bad_loption(Language); @~ break;
  6461.         }
  6462. else 
  6463.   {
  6464.   lnum = lan_num(language); /* We need to set this so that we can
  6465. access the language arrays  properly. ??? |language| ??? Used to be |Language| */
  6466.  
  6467.   switch(*pa++)
  6468.     {
  6469.    case '\\':
  6470.    case '&':
  6471.      /* Set the continuation character; for \Fortran, turn on
  6472. free-form syntax. */
  6473.     if(is_FORTRAN_(Language)) 
  6474.         params.Free_form_input[lnum] = NOT(FREE_FORTRAN);
  6475.     params.In_escape[lnum] = *(pa-1); // Continuation character.
  6476.     break;
  6477.  
  6478.    case ';':
  6479.     params.Auto_semi[lnum] = BOOLEAN(YES ^ not);
  6480.     break;
  6481.  
  6482.    case ':':
  6483.     if(is_FORTRAN_(Language))
  6484.         Fortran_label = NOT(FORTRAN_LABEL);
  6485.     break;
  6486.  
  6487.    case '!':
  6488.     params.Point_comments[lnum] = BOOLEAN(YES ^ not);
  6489.     break;
  6490.  
  6491.    case '/':
  6492.     params.CPP_comments[lnum] = BOOLEAN(YES ^ not);
  6493.     break;
  6494.  
  6495.    case ')':
  6496.     params.Reverse_indices[lnum] = BOOLEAN(YES ^ not);
  6497.     break;
  6498.  
  6499.    case 'b':
  6500.    case 'B':
  6501.     params.Block_nums[lnum] = NOT(BLOCK_NUMS); @~ break;
  6502.  
  6503.    case 'g':
  6504.    case 'G':
  6505.     @<Option \.{-g}:  set |goto| parameters@>@;
  6506.     break;
  6507.  
  6508.    case 'k':
  6509.     suppress_cmds = BOOLEAN(YES ^ not);
  6510.     @<Copy abbreviation characters@>@;
  6511.     break;
  6512.  
  6513.    case 'K':
  6514.     suppress_cmds = BOOLEAN(NO ^ not);
  6515.     @<Copy abbrev...@>@;
  6516.     break;
  6517.  
  6518.    case 'p':
  6519.     if(is_FORTRAN_(Language)) prn_semis = NOT(PRN_SEMIS);
  6520.     break;
  6521.  
  6522.    case '{':
  6523.     pa--;
  6524.     set_filter(Language); 
  6525.     break;
  6526.  
  6527.    case '\0':
  6528.  // Specify the language.  Note that the other commands don't.
  6529.     ini_language(XORD(*LANGUAGE_CODE(Language)));
  6530.     break;
  6531.  
  6532.    default:
  6533.     bad_loption(Language);
  6534.     break;
  6535.     }
  6536.   }
  6537. }
  6538.  
  6539.  
  6540. @<Option \.{-g}...@>=
  6541. {
  6542. if(is_RATFOR_(Language))
  6543.     {
  6544.     if(!RAT_OK("'g' command ignored")) break;
  6545.     @<Scan the |goto| parameters@>;
  6546.     }
  6547. else bad_loption(Language);
  6548. }
  6549.  
  6550. @ Here we have some definitions for the parameters that control \Ratfor's
  6551. |goto|. (See the user manual.)
  6552.  
  6553. @d G_RATIO 2.0
  6554. @d MIN_G_RATIO 0.0 // This value definitively turns off the computed |goto|.
  6555.  
  6556. @d MARGINAL_CASES 5L
  6557. @d MIN_MARGINAL_CASES 1
  6558.  
  6559. @d MAX_SPREAD UL(128L)
  6560. @d MIN_MAX_SPREAD 1
  6561.  
  6562. @<Common...@>=
  6563.  
  6564. IN_COMMON double g_ratio CSET(G_RATIO);
  6565. IN_COMMON unsigned short marginal_cases CSET(MARGINAL_CASES);
  6566. IN_COMMON CASE_TYPE max_spread CSET(MAX_SPREAD);
  6567.  
  6568. @ We define an error macro to help ensure that valid parameters are passed
  6569. to the \.{-g}~option.
  6570.  
  6571. @m CHK_G(var,VAR,type) if(var < MIN_##VAR)
  6572.     {
  6573.     err_print(C,$IFELSE(type,int,"Invalid 'g' option: %s must be >= %d",
  6574.             "Invalid 'g' option: %s must be >= %.1f"),#var,
  6575.         MIN_##VAR);
  6576.     var = VAR;
  6577.     }
  6578.  
  6579. @<Scan the |goto|...@>=
  6580. {
  6581. while(*pa)
  6582.     if(isalpha(*pa))
  6583.         switch(*pa++)
  6584.             {
  6585.             case 'r':
  6586.             case 'R':
  6587.                 g_ratio = STRTOD(pa,&pa);
  6588.                 CHK_G(g_ratio,G_RATIO,double);
  6589.                 break;
  6590.             case 'm':
  6591.             case 'M':
  6592.                 marginal_cases = 
  6593.                     (unsigned short)STRTOL(pa,&pa,10); 
  6594.                 CHK_G(marginal_cases,MARGINAL_CASES,int);
  6595.                 break;
  6596.             case 's':
  6597.             case 'S':
  6598.                 max_spread = STRTOL(pa,&pa,10); 
  6599.                 CHK_G(max_spread,MAX_SPREAD,int);
  6600.                 break;
  6601.             default:
  6602.                 err_print(C,"Invalid 'g' option: \
  6603. parameter type '%c'",*(pa-1));
  6604.                 break;
  6605.             }
  6606. }
  6607.  
  6608. @*1 Option {\tt -k}.
  6609. If there are no abbreviation characters, we assume
  6610. that all are intended, so we insert an asterisk.
  6611.  
  6612. @<Copy abbrev...@>=
  6613. {
  6614. if(!*pa) STRCPY(abbrev_cmds,"*");
  6615. else STRNCPY(abbrev_cmds,pa,NUM_RATFOR_CMDS);
  6616. }
  6617.  
  6618.  
  6619. @*1 Option {\tt -p}.
  6620. Each use of this option collects its string argument
  6621. as a separate line in a style-file buffer.  That buffer is read before the
  6622. local style file.
  6623.  
  6624. @d SPRM_LEN 1500
  6625.  
  6626. @<Option \.{-p}...@>=
  6627. {
  6628. IN_STYLE outer_char HUGE *sprm_buf,HUGE *sprm_ptr,HUGE *sprm_end;
  6629. int n = STRLEN(pa);
  6630. size_t nbuf,nused;
  6631.  
  6632. if(!sprm_buf) 
  6633.     { /* Allocate the style-parameter buffer. */
  6634.     sprm_ptr = sprm_buf = GET_MEM("sprm_buf",SPRM_LEN,outer_char);
  6635.     sprm_end = sprm_buf + SPRM_LEN;
  6636.     nbuf = SPRM_LEN;
  6637.     }
  6638.  
  6639. if(sprm_ptr + (n+2) >= sprm_end) 
  6640.     { /* Reallocate a larger buffer. */
  6641.     BUF_SIZE old_size;
  6642.  
  6643.     nused = PTR_DIFF(size_t, sprm_ptr, sprm_buf);
  6644.     old_size = nbuf;
  6645.     nbuf = PTR_DIFF(size_t, sprm_end, sprm_buf) + SPRM_LEN;
  6646.  
  6647.     if( (sprm_buf = (outer_char HUGE *)REALLOC(sprm_buf,
  6648.         nbuf*sizeof(outer_char), old_size*sizeof(outer_char)))==NULL)
  6649.         OVERFLW("sprm_buf",0);
  6650.  
  6651.     sprm_ptr = sprm_buf + nused;
  6652.     sprm_end = sprm_ptr + nbuf;
  6653.     }
  6654.  
  6655. /* Add a line (terminated with space and newline). */
  6656. STRCPY(sprm_ptr, pa);
  6657. sprm_ptr += n;
  6658. *sprm_ptr++ = ' ';
  6659. *sprm_ptr++ = '\n';
  6660. }
  6661.  
  6662. @*1 Option {\tt -P}.
  6663. In a few cases it is useful to specify which processor,
  6664. \TeX\ or \LaTeX, will be used to process the output from \FWEAVE.  The
  6665. default is~`\.{-P}', which is equivalent to~`\.{-PL}'.
  6666.  
  6667. @<Option \.{-P}...@>=
  6668. {
  6669. switch(*pa)
  6670.     {
  6671.    default:
  6672.     err_print(C,"'%c' is invalid option for -X; must be either 'L' \
  6673. (LaTeX) or 'T' (TeX).  LaTeX is assumed", *pa); // Falls through to \LaTeX\ case.
  6674.  
  6675.    case '\0':
  6676.    case 'L':
  6677.     TeX_processor = LaTeX_p;
  6678.     break;
  6679.  
  6680.    case 'T':
  6681.     TeX_processor = TeX_p;
  6682.     break;
  6683.     }
  6684. }
  6685.  
  6686.  
  6687. @*1 Option {\tt -m}.
  6688. The \.{-m}~option takes care of several commands:
  6689. \.{-m4}~says to recognize \.{m4}~commands; \.{-m;}~means automatically
  6690. append as pseudo-semi to \WEB\ macro definitions; otherwise, it's a \WEB\
  6691. macro definition.
  6692.  
  6693. @<Option \.{-m}...@>=
  6694. {
  6695. switch(*pa)
  6696.     {
  6697.     case '4':
  6698. /* Recognize \.{m4} definitions. */
  6699.         @<Issue warning...@>@;
  6700. @#if 0
  6701.         R_ext = "m4"; N_ext = "n4";
  6702. @#endif
  6703.         m4 = !M4_;
  6704.         break;
  6705.  
  6706.     case ';':
  6707. /* Automatically append pseudo-semi to \.{WEB} macro definition. */
  6708.         auto_app_semi = NOT(AUTO_APP_SEMI);
  6709.         break;
  6710.  
  6711.     case '\0':
  6712.          ERR_PRINT(C,"Missing id for 'm' option");
  6713.         break;
  6714.  
  6715.     default:
  6716.         if(not) goto undefine_macro;
  6717.  
  6718.     define_macro:
  6719.         mc = *pa;
  6720.  
  6721.         if(!(isalpha(mc) || mc=='_' || mc=='$'))
  6722.             err_print(C,"Macro definition may not start with \
  6723. '%c'; -m option ignored",mc);
  6724.         else SAVE_MACRO(pa); /* Macro definition. */
  6725.  
  6726.         break;
  6727.     }
  6728. }
  6729.  
  6730. @ Build a macro definition in the macro buffer. This is used for
  6731. predefining macros as well as defining from the command line. (This
  6732. function modifies the text~|t|.)
  6733.  
  6734. @<Part 2@>=@[
  6735.  
  6736. SRTN 
  6737. save_macro FCN((cmd,t))
  6738.     CONST outer_char HUGE *cmd C0("Name of the ``at'' command to put first.")@;
  6739.     outer_char HUGE *t C1("Text of the definition.")@;
  6740. {
  6741. eight_bits HUGE *p;
  6742. boolean replace_equals = BOOLEAN(STRCMP(cmd,"m")==0); /* For macros, if an
  6743.     equals sign appears before a space, it will be turned into a space. */
  6744.     
  6745. if(program==weave) return;
  6746.  
  6747. p = mp;
  6748.  
  6749. /* Place command such as \.{@@m} into buffer. */
  6750. *mp++ = '@@'; 
  6751.  
  6752. while(*cmd)
  6753.     *mp++ = *cmd++; /* Copy text of command. */
  6754.  
  6755. *mp++ = ' ';
  6756.  
  6757. while(*t) 
  6758.     {
  6759.     if(replace_equals)
  6760.         if(*t == '=')
  6761.             {
  6762.             *t = ' ';
  6763.             replace_equals = NO;
  6764.             }
  6765.         else if(*t == ' ') replace_equals = NO;
  6766.     *mp++ = *t++;
  6767.     }
  6768.  
  6769. *mp++ = ' '; 
  6770. *mp = '@@'; @~ *(mp+1) = 'm';
  6771.  
  6772. /* After we've built it, convert to internal notation. */
  6773. for( ; p<mp+2; p++)
  6774.     *p = XORD(*p);
  6775. }
  6776.  
  6777. @ Divert the input stream to be from a buffer.
  6778. @<Glob...@>=
  6779.  
  6780. IN_COMMON ASCII HUGE *fbuffer, HUGE *flimit, HUGE *floc; /* Saved position
  6781.                 of current file. */ 
  6782.  
  6783. @
  6784. @<Part 2@>=@[
  6785.  
  6786. SRTN 
  6787. divert FCN((buffer,end,stop))
  6788.     ASCII HUGE *buffer C0("Start of the buffer.")@;
  6789.     ASCII HUGE *end C0("End of the buffer.")@;
  6790.     boolean stop C1("Stop when end is reached?")@;
  6791. {
  6792. stop_the_scan = stop;
  6793.  
  6794. fbuffer = cur_buffer;
  6795. flimit = limit;
  6796. floc = loc;
  6797.  
  6798. cur_buffer = loc = buffer;
  6799. limit = end; 
  6800.  
  6801. from_buffer = YES;
  6802. }
  6803.  
  6804. @ Undivert back to reading from files.
  6805. @<Part 2@>=@[
  6806. SRTN undivert(VOID)
  6807. {
  6808. cur_buffer = fbuffer;
  6809. limit = flimit;
  6810. loc = floc;
  6811.  
  6812. from_buffer = NO;
  6813. }
  6814.  
  6815.  
  6816. @*1 Option {\tt -s}.
  6817. Process statistics requests.
  6818.  
  6819. @<Option \.{-s}...@>=
  6820. {
  6821. statistics = NOT(STATISTICS);
  6822.  
  6823. /* The format ``\.{-sm100}'' says to display memory allocations for sizes
  6824. $>= 100$ bytes. */
  6825. if(*pa)
  6826.     if(*pa == 'm')
  6827.         { 
  6828.         pa++; // |pa| positioned to byte size.
  6829.         show_mem = NOT(SHOW_MEM);
  6830.         if(isdigit(*pa)) show_size = ATOL(pa);
  6831.         }
  6832.     else
  6833.         {
  6834.         printf("! Option \"%s\" is invalid.\n", (char *)(pa-2));
  6835.         mark_harmless;
  6836.         }
  6837. }
  6838.  
  6839. @ On the smaller computers, it's helpful to know where we stand regarding
  6840. available memory. The function |mem_avail| is invoked along with the other
  6841. statistics turned on by the \.{-s}~command-line option.
  6842.  
  6843. A global variable is used to capture the starting memory, so we don't print
  6844. rediculously large maximum numbers with the statistics.
  6845.  
  6846. @<Glob...@>=
  6847.  
  6848. IN_COMMON BUF_SIZE starting_memory CSET(ULONG_MAX); 
  6849.     // Set at first call to |mem_avail|.
  6850.  
  6851. @
  6852. @<Part 2@>=@[
  6853.  
  6854. SRTN 
  6855. mem_avail FCN((at_end))
  6856.     int at_end C1("0 for beginning, 1 for end")@;
  6857. {
  6858. if(at_end) 
  6859.     printf("\n Allocated maximum of %lu bytes dynamically; \
  6860. ended with %lu bytes.\n", max_mem, total_mem);
  6861.  
  6862. #if PRINT_AVAILABLE_MEMORY
  6863.     printf(" Available memory: %lu bytes.\n",CORE_LEFT);
  6864.     if(!at_end) 
  6865.         {
  6866.         starting_memory = CORE_LEFT; // Used in |smin0|.
  6867.         putchar('\n');
  6868.         }
  6869. #endif
  6870. }
  6871.  
  6872. @ Here is a common routine to print the statistics for a particular kind of
  6873. array. 
  6874. @<Part 2@>=@[
  6875.  
  6876. SRTN 
  6877. stat0 FCN((name,size,num,max_num,abs_max_num,abbrev,ctrl_chars))
  6878.     CONST outer_char *name C0("")@;
  6879.     size_t size C0("")@;
  6880.     BUF_SIZE num C0("")@;
  6881.     BUF_SIZE max_num C0("")@;
  6882.     BUF_SIZE abs_max_num C0("")@;
  6883.     CONST outer_char *abbrev C0("")@;
  6884.     CONST outer_char *ctrl_chars C1("")@;
  6885. {
  6886. printf("  %s [\"%s\"] x %u byte(s):  %lu [%lu byte(s)] of %lu <= %lu%s\n",
  6887.     (char *)name, (char *)abbrev, (unsigned)size, num, num*size, max_num,
  6888.         abs_max_num, (char *)ctrl_chars);
  6889. }
  6890.  
  6891. @ A simple function to return the maximum ``reasonable'' memory.
  6892. @<Part 2@>=@[
  6893.  
  6894. BUF_SIZE 
  6895. smin0 FCN((abs_max_num))
  6896.     BUF_SIZE abs_max_num C1("")@;
  6897. {
  6898. return MIN(starting_memory,abs_max_num);
  6899. }
  6900.  
  6901.  
  6902. @*1 Option {\tt -t}.
  6903. Truncate identifiers.
  6904.  
  6905. @<Option \.{-t}...@>=
  6906. {
  6907. @<Issue warning if this command is negated@>@;
  6908. ntrunc(); 
  6909. }
  6910.  
  6911. @ Parse the argument of the truncate (`\.{-t}') command.
  6912. @<Part 2@>=@[
  6913.  
  6914. SRTN 
  6915. ntrunc(VOID)
  6916. {
  6917. LANGUAGE l;
  6918.  
  6919. truncate_ids = NO;
  6920.  
  6921. if(!*pa) goto bad_t_option;
  6922.  
  6923. while(*pa)
  6924.     {
  6925.     if(isdigit(*pa)) 
  6926.         l = (language==NO_LANGUAGE) ? GLOBAL_LANGUAGE : language; 
  6927.     else switch(*pa++)
  6928.         {
  6929.        @<|outer_char| cases for |C|@>:       
  6930.         l = lcase(C,'\0',C_PLUS_PLUS,'+',&pa);
  6931.         break;
  6932.        @<|outer_char| cases for |RATFOR|@>:  
  6933.         l = lcase(RATFOR,'7',RATFOR_90,'9',&pa);
  6934.         break;
  6935.        @<|outer_char| cases for |FORTRAN|@>: 
  6936.         l = lcase(FORTRAN,'7',FORTRAN_90,'9',&pa);
  6937.         break;
  6938.        @<|outer_char| cases for |LITERAL|@>:    
  6939.         l = LITERAL; @~ break;
  6940.        @<|outer_char| cases for |TEX|@>:     
  6941.         l = TEX; @~ break;
  6942.        default: 
  6943.          bad_t_option:
  6944.         ERR_PRINT(C,"Invalid -t option.  Example:  `-tn=6{_}'"); 
  6945.         return;
  6946.         }
  6947.     
  6948. /* Skip over optional equals sign. */
  6949.     if(*pa == LANGUAGE_SEPARATOR) pa++;
  6950.  
  6951. /* Set the maximum identifier length for language~|l|. */
  6952.     truncate_ids = 
  6953.        BOOLEAN(CHOICE((tr_max[lan_num(l)] = (unsigned short)ATOI(pa)) != 0,
  6954.             YES,NO));
  6955.  
  6956.     while(isdigit(*pa)) pa++; // Skip over the integer.
  6957.  
  6958.     if(*pa == '{') set_filter(l);
  6959.     }
  6960. }
  6961.  
  6962. @ Return a language based on the current argument character.
  6963. @<Part 2@>=@[
  6964.  
  6965. LANGUAGE 
  6966. lcase FCN((l0,c0,l1,c1,pp))
  6967.     LANGUAGE l0 C0("")@;
  6968.     outer_char c0 C0("")@;
  6969.     LANGUAGE l1 C0("")@;
  6970.     outer_char c1 C0("")@;
  6971.     outer_char HUGE * HUGE *pp C1("")@;
  6972. {
  6973. if(c0 && **pp==c0)
  6974.     {
  6975.     while(**pp==c0) (*pp)++;
  6976.     return l0;
  6977.     }
  6978.  
  6979. if(c1 && **pp==c1)
  6980.     {
  6981.     while(**pp==c1) (*pp)++;
  6982.     return l1;
  6983.     }
  6984.  
  6985. return l0;
  6986. }
  6987.  
  6988. @ Set list of characters to strip from identifiers. The language command
  6989. `\.{-n\{abc\}}' sets the undesirable characters to `\.{abc}'.
  6990.  
  6991. @<Part 2@>=@[
  6992.  
  6993. SRTN 
  6994. set_filter FCN((Language))
  6995.     LANGUAGE Language C1("")@;
  6996. {
  6997. if(!*pa) return; // In case there's no argument following the language cmd.
  6998.  
  6999. if(*pa == '{') pa++;
  7000.  
  7001. filter_char[lan_num(Language)] = (char *)pa; /* Repoint to the string of
  7002.                 characters following this command. */
  7003.  
  7004. /* Remove the closing bracket from the list. */
  7005. for( ;*pa; pa++)
  7006.     if(*pa == '}')
  7007.         {
  7008.         *pa++ = '\0';
  7009.         return;
  7010.         }
  7011. }
  7012.  
  7013.  
  7014. @*1 Option {\tt -T}.
  7015. Various flags for \FTANGLE.
  7016.  
  7017. @<Option \.{-T}:  various flags for \FTANGLE@>=
  7018. {
  7019. outer_char HUGE *p = pa;
  7020.  
  7021. while(*p)
  7022.  switch(*p++)
  7023.     {
  7024.    case 'D':
  7025.     deferred_macros = NOT(DEFERRED_MACROS);
  7026.     break;
  7027.  
  7028.    case 'v':
  7029.     top_version = NOT(TOP_VERSION);
  7030.     break;
  7031.  
  7032.    case '%':
  7033.     keep_trailing_comments = NOT(KEEP_TRAILING_COMMENTS);
  7034.     break;
  7035.  
  7036.    case '#':
  7037.     auto_line = NOT(AUTO_LINE);
  7038.     break;
  7039.  
  7040.    default:
  7041.     bad_option();
  7042.     break;
  7043.     }
  7044. }
  7045.  
  7046. @*1 Option {\tt -u}.
  7047. Undefine a macro.
  7048.  
  7049. @<Option \.{-u}...@>=
  7050. {
  7051. if(not) 
  7052.     goto define_macro;
  7053.  
  7054. undefine_macro:
  7055.     if(*pa == '\0') 
  7056.         ERR_PRINT(C,"Missing identifier for `-u' option");
  7057.     else 
  7058.         save_macro(OC("#undef"),pa);
  7059. }
  7060.  
  7061.  
  7062. @*1 Option {\tt -W}.
  7063. Various flags for \FWEAVE.
  7064.  
  7065. @<Option \.{-W}:  various flags for \FWEAVE@>=
  7066. {
  7067. outer_char HUGE *p = pa;
  7068.  
  7069. while(*p)
  7070.  switch(*p++)
  7071.     {
  7072.    case '[':
  7073.     active_brackets = NOT(ACTIVE_BRACKETS);
  7074.     break;
  7075.  
  7076.    case 'd':
  7077.     defn_mask.outer_macros = NOT(PRN_OUTER_MACROS);
  7078.     break;
  7079.  
  7080.    case 'f':
  7081.     defn_mask.formats = NOT(PRN_fORMATS);
  7082.     break;
  7083.  
  7084.    case 'F':
  7085.     defn_mask.Formats = NOT(PRN_FORMATS);
  7086.     break;
  7087.  
  7088.    case 'l':
  7089.     defn_mask.limbo = NOT(PRN_LIMBO);
  7090.     break;
  7091.  
  7092.    case 'm':
  7093.     defn_mask.macros = NOT(PRN_MACROS);
  7094.     break;
  7095.  
  7096.    case 'v':
  7097.     defn_mask.v = NOT(PRN_V);
  7098.     break;
  7099.  
  7100.    case 'w':
  7101.    case 'W':
  7102.     defn_mask.w = NOT(PRN_W);
  7103.     break;
  7104.  
  7105.    default:
  7106.     bad_option();
  7107.     break;
  7108.     }
  7109. }
  7110.  
  7111. @*1 Option {\tt -x}.
  7112. The \.{-x}~option has the format `\.{-x\It{[*cimu]}}',
  7113. which controls the printing of the table of contents~(`\.{c}'),
  7114. index~(`\.{i}'), module list~(`\.{m}'), and unnamed
  7115. cross-references~(`\.{u}').  `\.{*}"~means print nothing. 
  7116. `\.{-xi}'~means don't print the index, but print everything else.  (More
  7117. precisely, `\.{*}'~is translated into the list~`\.{cim}'.  Each letter
  7118. negates the default action associated with it.)
  7119.  
  7120. @<Part 2@>=@[
  7121.  
  7122. SRTN 
  7123. flags3 FCN((not))
  7124.     boolean not C1("")@;
  7125. {
  7126. prn_contents = BOOLEAN(PRN_CONTENTS ^ not);
  7127. prn_index = BOOLEAN(PRN_INDEX ^ not);
  7128. prn_modules = BOOLEAN(PRN_MODULES ^ not);
  7129. xref_unnamed = BOOLEAN(XREF_UNNAMED ^ not);
  7130.  
  7131. if(!(*pa)) 
  7132.     { /* No argument.  Assume nothing is to be printed. */
  7133.     *pa = '*';
  7134.     *(pa+1) = '\0';
  7135.     }
  7136.  
  7137. for(; *pa; pa++)
  7138.     switch(*pa)
  7139.         {
  7140.        case '*':
  7141.        case '/':
  7142.         prn_contents = NOT(PRN_CONTENTS);
  7143.         prn_index = NOT(PRN_INDEX);
  7144.         prn_modules = NOT(PRN_MODULES);
  7145.         break;
  7146.  
  7147.        case 'C':
  7148.        case 'c':
  7149.         prn_contents = NOT(PRN_CONTENTS);
  7150.         break;
  7151.  
  7152.        case 'I':
  7153.        case 'i':
  7154.         prn_index = NOT(PRN_INDEX);
  7155.         break;
  7156.  
  7157.        case 'M':
  7158.        case 'm':
  7159.         prn_modules = NOT(PRN_MODULES);
  7160.         break;
  7161.  
  7162.        case 'u':
  7163.         xref_unnamed = NOT(XREF_UNNAMED);
  7164.         break;
  7165.  
  7166.        default:
  7167.         err_print(C,"'%c' is invalid argument for -x; must be \
  7168. element of set {*cim}",*pa);
  7169.         break;
  7170.         }
  7171.  
  7172. no_xref = BOOLEAN(!(prn_index || prn_modules));
  7173. }
  7174.  
  7175. @*1 Option {\tt -y}.
  7176. The `\.{-y}~command has two forms:  a query mode,
  7177. ``\.{-y$aa$?}'', or the setting mode, ``\.{-y$aa$\It{nnnnn}}''.  To query
  7178. all options, say ``\.{-y?}'' 
  7179.  
  7180. @<Option \.{-y}...@>=
  7181. @{
  7182. outer_char abbrev[80],HUGE *a;
  7183. BUF_SIZE nunits;
  7184. MEM HUGE *m,HUGE *mmin = NULL, HUGE *mmax = NULL;
  7185. boolean query_mode = NO;
  7186.  
  7187. @b
  7188. @<Issue warning if this command is negated@>@;
  7189.  
  7190. if(!*pa)
  7191.     { /* Query everything. */
  7192.     mmin = mem;
  7193.     mmax = mmin + (sizeof_mem/sizeof(MEM) - 1);
  7194.     query_mode = YES;
  7195.     puts("Default memory allocation parameters:");
  7196.     }
  7197. else
  7198.     { /* Abbreviation specified. */
  7199.     abbrev[0] = abbrev[1] = '\0'; /* Ensure the abbreviation gets
  7200.         terminated properly. */
  7201.  
  7202.     for(a=abbrev; isalpha(*pa); )
  7203.         *a++ = *pa++;
  7204.  
  7205.     TERMINATE(abbrev,2); // Max of two character abbreviation.
  7206.  
  7207.     if(!isdigit(*pa))
  7208.         { /* Assume it's a question. */
  7209.         mmin = msearch(abbrev,0L);
  7210.         mmax = mmin + 1;
  7211.         query_mode = YES;
  7212.         }
  7213.     }
  7214.  
  7215. if(query_mode)
  7216.     for(m=mmin; m<mmax; m++)
  7217.         printf(" \"%-s\"%s x %d byte(s):\
  7218.   min = %lu, current = %lu [%lu byte(s)], max = %lu.\n",
  7219.             (char *)m->abbrev, m->abbrev[1] ? "" : " ",
  7220.             m->bytes, 
  7221.             m->min,    m->nunits, (m->nunits)*(m->bytes), m->max);
  7222. else
  7223.     { /* Change setting. */
  7224.     nunits = ATOL(pa);
  7225.     msearch(abbrev,nunits); // Set the value.
  7226.     }
  7227.  
  7228. info_option = YES;
  7229. }
  7230.  
  7231. @*1 Option {\tt -z}.
  7232. Set name of style file.
  7233.  
  7234. @<Option \.{-z}@>=
  7235. {
  7236. @<Issue warning if this command is negated@>@;
  7237.  
  7238. if(STRLEN(pa) < MAX_FILE_NAME_LENGTH)
  7239.     {
  7240.     if(STRCMP(pa,".") == 0)
  7241.         { /* Read default file in current directory. */
  7242.         if(NSPRINTF(style_file_name,
  7243.             ".%c%s",PREFIX_END_CHAR,STYLE_FILE_NAME) >=
  7244. MAX_FILE_NAME_LENGTH) OVERFLW("./style_file_name","");
  7245.         }
  7246.     else
  7247.         STRCPY(style_file_name,pa);
  7248.  
  7249.     renamed_style = YES;
  7250.     }
  7251. else err_print(C,"Style file name too long; must be less \
  7252. than %d characters",MAX_FILE_NAME_LENGTH);
  7253. }
  7254.  
  7255.  
  7256. @*1 Options {\tt ->} and {\tt -=}.
  7257. Here we determine a new output file
  7258. name, with the command~\.{->}. If the argument is empty, everything goes to
  7259. |stdout|. Otherwise, the command must have the format \.{->l=name.ext}. If
  7260. the language~\.l is empty, all output is redirected to \.{name.ext}.  If
  7261. the name field is~'\.*', the name of the web file is used, but with the new
  7262. extension. Otherwise, both the new name and the new extension are used.
  7263.  
  7264. @d LANGUAGE_SEPARATOR '=' // Separates language from file name.
  7265.  
  7266. @<Part 2@>=@[
  7267.  
  7268. SRTN 
  7269. redirect_output(VOID)
  7270. {
  7271. int k;
  7272.  
  7273. if(*pa == '\0') 
  7274.     { /* Empty argument; send stuff to |stdout|. */
  7275.     new_fname(&tex_fname,OC("stdout"),NULL);
  7276.  
  7277.     for(k=0; k<NUM_LANGUAGES; k++)
  7278.         new_fname(params.outp_nm+k,OC("stdout"),NULL);
  7279.     }
  7280. else if(phase==2 && program==weave) 
  7281.     return;
  7282. else
  7283.     {
  7284.     outer_char c,HUGE *pname;
  7285.     outer_char HUGE *q = (outer_char HUGE *)STRCHR(pa,LANGUAGE_SEPARATOR); 
  7286.         // Is there an '='?
  7287.  
  7288.     if(q==pa)
  7289.         { /* ``\.{->=new\_name}'' */
  7290.         c = LANGUAGE_SEPARATOR;
  7291.         pname = q + 1; // Point to file name.
  7292.         }
  7293.     else if(q)
  7294.         { /* ``\.{->c=new\_name}'' */
  7295.         c = *pa++; /* Language symbol. */
  7296.         pname = q; // Point to \.=.
  7297.         }
  7298.     else
  7299.         { /* ``\.{->new\_name}'' */
  7300.         c = LANGUAGE_SEPARATOR;
  7301.         pname = pa; // Point to file name.
  7302.         }
  7303.  
  7304.     switch(c)
  7305.         {
  7306.        @<|outer_char| cases for |C|@>:
  7307.         set_fname(lcase(C,'\0',C_PLUS_PLUS,'+',&pa),pname,YES);
  7308.         break;
  7309.  
  7310.        @<|outer_char| cases for |RATFOR|@>:
  7311.         if(!RAT_OK("Redirection ignored")) break;
  7312.  
  7313.         set_fname(lcase(RATFOR,'7',RATFOR_90,'9',&pa),pname,YES);
  7314.         break;
  7315.  
  7316.        @<|outer_char| cases for |FORTRAN|@>:
  7317.         set_fname(lcase(FORTRAN,'7',FORTRAN_90,'9',&pa),pname,YES);
  7318.         break;
  7319.  
  7320.        @<|outer_char| cases for |LITERAL|@>:
  7321.         set_fname(LITERAL,pname,YES);
  7322.         break;
  7323.  
  7324.        @<|outer_char| cases for |TEX|@>:
  7325.         set_fname(TEX,pname,YES);
  7326.         break;
  7327.             
  7328.           case LANGUAGE_SEPARATOR:
  7329. #if 0 /* Don't want to do this; it screws up module labeling for \FWEAVE. */
  7330.         new_fname(&tex_fname,pname,NULL);
  7331. #endif
  7332.  
  7333.         for(k=0; k<NUM_LANGUAGES; k++)
  7334.             new_fname(params.outp_nm+k,pname,NULL);
  7335.  
  7336.         break;
  7337.  
  7338.        default:
  7339.         err_print(C,"Syntax error in output redirection \
  7340. command \"->\". Language must be one of 'c', 'r', 'n', `m', or `x',  not '%c'",
  7341. *(pa-1));
  7342.         break;
  7343.         }
  7344.     }
  7345. }
  7346.  
  7347. @ This function sets an alternative output file name. If the |check| field
  7348. is |YES|, we check to see that the string has the format ``\.{=stuff}''.
  7349.  
  7350. @<Part 2@>=@[
  7351.  
  7352. SRTN 
  7353. set_fname FCN((l,s,check))
  7354.     LANGUAGE l C0("Language")@;
  7355.     outer_char HUGE *s C0("Remainder of command-line argument to parse")@;
  7356.     boolean check C1("Do we check for beginning colon?")@;
  7357. {
  7358. if(program==weave)
  7359.     err_print(C,"Can't redirect FWEAVE's TeX output by individual \
  7360. languages. Use \"->\" or \"-%cname.ext\"",LANGUAGE_SEPARATOR);
  7361.  
  7362. if(check)
  7363.     if(*s++ != LANGUAGE_SEPARATOR)
  7364.         {
  7365.         err_print(C,"Expected '%c' after language in \"->\"; \
  7366. command ignored",LANGUAGE_SEPARATOR);
  7367.         return;
  7368.         }
  7369.  
  7370. /* If the name begins with a '\.\#', it's filled in later with |wbflnm0|. */
  7371. new_fname(params.outp_nm+lan_num(l),*s ? s : (outer_char HUGE *)"stdout",NULL);
  7372. }
  7373.  
  7374.  
  7375. @*1 Warnings.
  7376. Some of the commands aren't naturally negatable; issue a warning.
  7377.  
  7378. @<Issue warning...@>=
  7379. cant_negate(pa-2);
  7380.  
  7381. @
  7382. @<Part 2@>=@[
  7383.  
  7384. SRTN 
  7385. cant_negate FCN((s))
  7386.     outer_char HUGE *s C1("")@;
  7387. {
  7388. if(not) 
  7389.     {
  7390.     printf("! Can't negate option \"%s\"'; ignoring extra hyphen.\n",
  7391.         (char *)s);
  7392.     mark_harmless;
  7393.     }
  7394. }
  7395.  
  7396. @
  7397. @<Part 2@>=@[
  7398.  
  7399. SRTN 
  7400. bad_lcmd FCN((type,pa0))
  7401.     CONST char *type C0("")@;
  7402.     CONST outer_char HUGE *pa0 C1("")@;
  7403. {
  7404. err_print(C,"Invalid language %s \"%s\"",type,pa0);
  7405. }
  7406.  
  7407. @
  7408. @<Part 2@>=@[
  7409.  
  7410. SRTN 
  7411. bad_loption FCN((Language))
  7412.     LANGUAGE Language C1("")@;
  7413. {
  7414. printf("! Invalid language option \"%s\" for %s.\n",
  7415.     (char *)(pa-3), is_FORTRAN_(Language) ? "Fortran" : "Ratfor");
  7416. mark_harmless;
  7417. }
  7418.  
  7419. @<Part 2@>=@[
  7420.  
  7421. SRTN 
  7422. usage FCN((msg,is_fatal))
  7423.     CONST char *msg C0("")@;
  7424.     boolean is_fatal C1("Fatal?")@;
  7425. {
  7426. outer_char temp[200];
  7427.  
  7428. sprintf((char *)temp,
  7429.     "%s!! Usage:  %s webfile[%cweb] [changefile[%cch]] [-option...]\n",
  7430.     msg,
  7431.     program==tangle ? "ftangle" : "fweave",
  7432.         wt_style.ext_delimiter,wt_style.ext_delimiter);
  7433.  
  7434. if(is_fatal)
  7435.     FATAL(NULL, temp,"");
  7436. else
  7437.     wrap_up();
  7438. }
  7439.  
  7440. @
  7441. @<Print change file warning...@>=
  7442. {
  7443. printf("! Warning: Ignoring multiple change file \"%s\".\n",
  7444.     (char *)THE_FILE_NAME);
  7445. mark_harmless;
  7446. continue;
  7447. }
  7448.  
  7449. @<Part 2@>=@[
  7450.  
  7451. SRTN 
  7452. too_long(VOID)
  7453. {
  7454. FATAL(NULL, "!! Filename too long: ", (CONST outer_char *)pa);
  7455. }
  7456.  
  7457. @<Part 2@>=@[
  7458.  
  7459. SRTN 
  7460. bad_option(VOID)
  7461. {
  7462. printf("! Unknown option \"%s\" ignored.\n", (char *)(pa-2)); 
  7463. mark_harmless;
  7464. }
  7465.  
  7466. @ If a language was set on the command line, we issue a warning if the
  7467. limbo section overrode that.
  7468. @<Part 2@>=@[
  7469.  
  7470. SRTN 
  7471. chk_override FCN((language0))
  7472.     LANGUAGE language0 C1("")@;
  7473. {
  7474. if(language != language0 && cmd_language != NO_LANGUAGE && cmd_language !=
  7475.     language) 
  7476.  err_print(C,"WARNING:  Command-line language %s overridden in limbo by %s",
  7477.    languages[lan_num(cmd_language)],languages[lan_num(language)]); 
  7478. }
  7479.  
  7480. @* OUTPUT.
  7481. Here is the code that opens the output file:
  7482. @^system dependencies@>
  7483.  
  7484. @<Scan arguments and open output files@>=
  7485. {
  7486. @<Allocate initial file names@>@;
  7487. scan_args();
  7488.  
  7489. /* If there's no \WEB\ file, it must be an info option. */
  7490. if(found_web)
  7491.     {
  7492.     ini_input_prms(WEB_FILE,(outer_char HUGE *)"",ABORT_ON_ERROR); 
  7493.         // Opens both input and change file.
  7494.     @<Remember the |web_file_name| and |change_file_name|@>;
  7495.  
  7496.     if(program == weave) open_tex_file();
  7497.     }
  7498.  
  7499. /* In case no explicit language commands are given, either on the command
  7500. line or in the file, before encountering the first module, pick a default. */
  7501. if(language == NO_LANGUAGE) 
  7502.     ini_language(XORD(*LANGUAGE_CODE(GLOBAL_LANGUAGE)));
  7503.  
  7504. global_params = params;  // Freeze the params after arguments have been read.
  7505. }
  7506.  
  7507. @ Initially, all output file names are empty.
  7508. @<Allocate initial file names@>=
  7509. {
  7510. int k;
  7511.  
  7512. /* We initialize the \It{local} name list; it's set to global later after
  7513. everything else is set. */
  7514. new_fname(&tex_fname,NULL,NULL);
  7515. new_fname(&fwebmac,NULL,NULL);
  7516.  
  7517. for(k=0; k<NUM_LANGUAGES; k++)
  7518.     new_fname(params.outp_nm+k,NULL,NULL);
  7519. }
  7520.  
  7521. @ Make a new file name.
  7522.  
  7523. @d DFLT_FNAME_LENGTH 20
  7524.  
  7525. @<Part 2@>=@[
  7526.  
  7527. SRTN 
  7528. new_fname FCN((pname,name,ext))
  7529.     outer_char HUGE * HUGE *pname C0("")@;
  7530.     outer_char *name C0("")@;
  7531.     outer_char *ext C1("")@;
  7532. {
  7533. outer_char temp[MAX_FILE_NAME_LENGTH];
  7534.  
  7535. /* An empty |name| means allocate some space. */
  7536. if(!name)
  7537.     *pname = GET_MEM("pname",DFLT_FNAME_LENGTH+1,outer_char);
  7538. else
  7539.     { /* Otherwise, we possibly allocate the space and construct the
  7540. name. */ 
  7541.     char ext_delimiter[2];
  7542.     FILE *temp_file_ptr;
  7543.  
  7544.     ext_delimiter[0] = (char)wt_style.ext_delimiter;
  7545.     ext_delimiter[1] = '\0';
  7546.  
  7547.     SPRINTF(MAX_FILE_NAME_LENGTH,temp,`"%s%s%s",name,ext ?
  7548.             ext_delimiter : "", 
  7549.         ext ? ext : (outer_char *)""`);
  7550.  
  7551. /* Previously we used to reallocate the file name here.  However, that
  7552. leads to an egregious error, since if the block is actually moved the old
  7553. pointers are still around on the stack and in various other hiding places.
  7554. Now, we just allocate a new area if the name is new. */
  7555.     was_opened(temp,0,pname,&temp_file_ptr);
  7556.     }
  7557. }
  7558.  
  7559. @
  7560. @<Remember the |web_file_name| and |change_file_name|@>=
  7561. @{
  7562. outer_char temp1[100],temp2[1000],
  7563.     full_web_name[ESCAPE_LENGTH],esc_full_web_name[ESCAPE_LENGTH],
  7564.     full_change_name[ESCAPE_LENGTH],esc_full_change_name[ESCAPE_LENGTH];
  7565. int n1,n2;
  7566. BUF_SIZE old_size;
  7567.  
  7568. @b
  7569. /* Run time */
  7570. n1 = program==tangle ? NSPRINTF(temp1,
  7571.      "\n  RUN TIME: \"%s, %s at %s.\"",
  7572.             the_day(t),the_cdate(t),the_time(t))
  7573.         : NSPRINTF(temp1,"");
  7574.  
  7575. #if(VMS)
  7576.     fgetname(web_file,full_web_name);
  7577.     fgetname(change_file,full_change_name);
  7578. #else
  7579.     STRCPY(full_web_name,web_file_name);
  7580.     STRCPY(full_change_name,change_file_name);
  7581. #endif /* |VMS| */
  7582.  
  7583. esc_file_name(esc_full_web_name,ESCAPE_LENGTH,full_web_name);
  7584. esc_file_name(esc_full_change_name,ESCAPE_LENGTH,full_change_name);
  7585.  
  7586. n2 = NSPRINTF(temp2,
  7587.     program==tangle ?
  7588.         "\n  WEB FILE:    \"%s\"\n  CHANGE FILE: %c%s%c" :
  7589.         " {\"%s\"} {%c%s%c} ",
  7590.     esc_full_web_name,
  7591.     *esc_full_change_name ? '"' : '(',
  7592.     *esc_full_change_name ? esc_full_change_name : OC("none"),
  7593.     *esc_full_change_name ? '"' : ')');
  7594.  
  7595. old_size = STRLEN(cmd_ln_buf) + 1;
  7596. cmd_ln_buf = (outer_char HUGE *)REALLOC(cmd_ln_buf,old_size+n1+n2, old_size);
  7597. STRCAT(cmd_ln_buf,temp1);
  7598. STRCAT(cmd_ln_buf,temp2);
  7599. }
  7600.  
  7601. @ For some system such as DOS, the file names may have special characters
  7602. in them that need to be escaped:
  7603. @<Part 2@>=@[
  7604.  
  7605. unsigned 
  7606. esc_file_name FCN((esc_name,max_length,name))
  7607.     outer_char *esc_name C0("Put answer into here")@;
  7608.     unsigned max_length C0("Maximum length of |esc_name|.")@;
  7609.     outer_char *name C1("Put escapes into this name")@;
  7610. {
  7611. outer_char *pt,*pn;
  7612. unsigned n;
  7613.  
  7614. max_length--; /* Allow room for the final null. */
  7615.  
  7616. for(pt=esc_name,pn=name,n=0; *pn; )
  7617.     {
  7618.     if(program==weave)
  7619.        switch(*pn)
  7620.         {
  7621.         case '\\': case '$': case '#': case '%':
  7622.         case '^': case '~': case '&': case '_':
  7623.             if(n < max_length)
  7624.                 {
  7625.                 n++;
  7626.                 *pt++ = '\\';
  7627.                 }
  7628.         }
  7629.  
  7630.     if(n < max_length)
  7631.         {
  7632.         n++;
  7633.         *pt++ = *pn++;
  7634.         }
  7635.     }
  7636.  
  7637. TERMINATE(pt,0);
  7638. return n;
  7639. }
  7640.  
  7641. @ The |update_terminal| macro is used when we want
  7642. to make sure that everything we have output to the terminal so far has
  7643. actually left the computer's internal buffers and been sent.
  7644. @^system dependencies@>
  7645.  
  7646. @d UPDATE_TERMINAL fflush(stdout) // Empty the terminal output buffer.
  7647.  
  7648. @ For single-character output we use |putchar| (to the terminal) and |putc|
  7649. (to a file). When we have to translate from \.{WEB}'s code into the
  7650. external character code, we must say things like |putchar(xchr[c])|.  When
  7651. we just want to print strings, we use |printf|.  
  7652. @^system dependencies@>
  7653.  
  7654. @d new_line putchar('\n') @d putxchar putchar
  7655.  
  7656. @<Part 2@>=@[
  7657.  
  7658. SRTN 
  7659. ASCII_file_write FCN((file_ptr,p0,n))
  7660.     FILE *file_ptr C0("Standard file pointer.")@;
  7661.     CONST ASCII HUGE *p0 C0("Start of the buffer.")@;
  7662.     size_t n C1("Number of characters to write.")@;
  7663. {
  7664. CONST ASCII HUGE *p, HUGE *p1 = p0+n;
  7665. ASCII atemp[1000],HUGE *a,HUGE *a1;
  7666. size_t n_out;
  7667.  
  7668. if(n == 0) 
  7669.     return;
  7670.  
  7671. /* Translate any special characters into something readable. */
  7672. for(p=p0,a1=atemp; p<p1;a1++,p++)
  7673.     switch(*p)
  7674.         {
  7675.        case interior_semi:
  7676.         *a1 = @';';
  7677.         break;
  7678.  
  7679.        case MACRO_ARGUMENT:
  7680.         *a1++ = @'[';
  7681.         *a1++ = @'[';
  7682.         *a1++ = (ASCII)(@'0' + *(++p));
  7683.         *a1++ = @']';
  7684.         *a1 = @']';
  7685.         break;
  7686.  
  7687.        default:
  7688.         *a1 = *p;
  7689.         break;
  7690.         }
  7691.  
  7692. n = PTR_DIFF(size_t, a1, atemp); 
  7693.     // Might be bigger than incoming~|n| because of macro arg.
  7694.  
  7695. if(translate_ASCII)
  7696.     { /* Convert to the outer world. */
  7697.     outer_char temp[1000];
  7698.     outer_char HUGE *t;
  7699.  
  7700.     for(a=atemp,t=temp; a<a1;)
  7701.         *t++ = XCHR(*a++);
  7702.  
  7703.     n_out = FWRITE(temp,n,file_ptr);
  7704.     }
  7705. else 
  7706.     n_out = FWRITE(atemp,n,file_ptr);
  7707.  
  7708. if(n_out != 1)
  7709.     @<Diagnose a write error and give up@>@;
  7710. }
  7711.  
  7712. @
  7713. @<Diagnose...@>=
  7714. {
  7715. char temp[100];
  7716. int errno0 = errno;
  7717.  
  7718. sprintf(temp, "! WRITE ERROR: nbytes = %u, %u items written\n", 
  7719.     (unsigned)n, (unsigned)n_out);
  7720. FATAL(NULL, temp, strerror(errno0));
  7721. }
  7722.  
  7723. @ The error routine |strerror| isn't available on all systems.
  7724. @<Part 2@>=@[
  7725.  
  7726. #if !HAVE_STRERROR
  7727.  
  7728. char *
  7729. strerror FCN((errnum))
  7730.     int errnum C1("")@;
  7731. {
  7732. static char temp[50];
  7733.  
  7734. sprintf(temp, "(strerror is not available; errno = %d)", errnum);
  7735. return temp;
  7736. }
  7737.  
  7738. #endif // |!HAVE_STRERROR|
  7739.  
  7740. @ The |args| array used by the macro processor must be allocated after
  7741. the command line is parsed, because |max_margs| might have been changed by
  7742. the \.{-y}~option.
  7743.  
  7744. @<Allocate dyn...@>=
  7745. {
  7746. if(program == tangle)
  7747.     {
  7748.     ALLOC(sixteen_bits, args, ABBREV(max_margs), max_margs, 1);
  7749.     max_margs++; // So the user thinks this is really the max \#.
  7750.     }
  7751. }
  7752.  
  7753. @* DATE, TIME, and TIMING.
  7754. Here are declarations for time and date. We use
  7755. many of the ANSI routines. (However, some of the following could be
  7756. simplified if the compiler were fully ANSI. Timing seems to be one of the
  7757. areas that's most difficult to get right on the various machines.)
  7758.  
  7759. @<Common...@>=
  7760.  
  7761. IN_COMMON CONST char *day[] 
  7762.     #if(part == 0 || part == 1)
  7763.      = {"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"}
  7764.     #endif // |part == 1|
  7765.     ;
  7766.  
  7767. IN_COMMON CONST char *month[]
  7768.     #if(part == 0 || part == 1)
  7769.      = {"January","February","March","April","May","June","July",
  7770.         "August","September","October","November","December"}
  7771.     #endif // |part == 1|
  7772.     ;
  7773.  
  7774. @ Fill the |tm| structure and return a pointer.
  7775. @<Part 2@>=@[
  7776.  
  7777. struct tm *
  7778. the_localtime(VOID)
  7779. {
  7780. time_t the_tm;
  7781. time_t time PROTO((time_t *tp));
  7782.  
  7783. time(&the_tm);
  7784. return localtime(&the_tm);
  7785. }
  7786.  
  7787. @ Return strings containing the time and/or date.
  7788. @<Part 2@>=@[
  7789.  
  7790. CONST outer_char *
  7791. the_day FCN((t))
  7792.     CONST struct tm *t C1("")@;
  7793. {
  7794. return OC(day[t->tm_wday]);
  7795. }
  7796.  
  7797. #define NDATE 20
  7798.  
  7799. CONST outer_char *
  7800. the_cdate FCN((t))
  7801.     CONST struct tm *t C1("")@;
  7802. {
  7803. static outer_char date_buf[NDATE];
  7804.  
  7805. if(NSPRINTF(date_buf,"%s %d, %d",
  7806.         month[t->tm_mon],t->tm_mday,1900+t->tm_year) >= NDATE)
  7807.             OVERFLW("Date","");
  7808. return (CONST outer_char *)date_buf;
  7809. }
  7810. #undef NDATE
  7811.  
  7812. #define NTIME 8
  7813.  
  7814. CONST outer_char *
  7815. the_time FCN((t))
  7816.     CONST struct tm *t C1("")@;
  7817. {
  7818. static outer_char time_buf[NTIME];
  7819.  
  7820. if(NSPRINTF(time_buf,"%d:%02d",t->tm_hour,t->tm_min) >= NTIME)
  7821.     OVERFLW("Time","");
  7822.  
  7823. return (CONST outer_char *)time_buf;
  7824. }
  7825. #undef NTIME
  7826.  
  7827. @ Timing routines. In particular, sometimes we want timing more precise
  7828. than seconds, but the standard ANSI |difftime| doesn't always do that.
  7829. Thus, we sometimes supply our own.
  7830.  
  7831. @f TIME_T int
  7832.  
  7833. @<Common...@>=
  7834.  
  7835. #if TIMING
  7836.  
  7837. IN_COMMON clock_t clock0;
  7838. IN_COMMON TIME_T time0;
  7839.  
  7840. #if NEW_DIFFTIME
  7841. double diff_time FCN((t1,t0))
  7842.     TIME_T t1 C0("")@;
  7843.     TIME_T t0 C1("")@;
  7844. {
  7845. #if HAVE_GETTIMEOFDAY
  7846.     return (double)(t1.tv_sec - t0.tv_sec) 
  7847.         + 1.0e-6*(double)(t1.tv_usec - t0.tv_usec);
  7848. #else
  7849. #if HAVE_SYS_TIMEB_H
  7850.     return (double)t1.time - (double)t0.time 
  7851.         + 1.0e-3*((double)t1.millitm - (double)t0.millitm); /*
  7852. Without the casts, this line didn't work right on the DECstation. I think
  7853. it has to do with the |unsigned short| type of |millitm|; a compiler bug
  7854. with type promotion. */
  7855. #else
  7856.     return t1 - t0;
  7857. #endif // |HAVE_SYS_TIMEB_H|
  7858. #endif
  7859. }
  7860. #endif // |NEW_DIFFTIME|
  7861.  
  7862. #endif // |TIMING| 
  7863.  
  7864. @ This function is called at the beginning of both \FTANGLE\ and \FWEAVE.
  7865. @<Part 2@>=@[
  7866.  
  7867. #if TIMING
  7868.  
  7869. SRTN 
  7870. ini_timer(VOID)
  7871. {
  7872.     TIME(&time0);
  7873.     clock0 = clock();
  7874. }
  7875.  
  7876. #endif // |TIMING|
  7877.  
  7878. @ This one's called at the end.
  7879.  
  7880. @d TIMING_PREC 10 /* Check that |cpu <= wall| by multiplying seconds by
  7881.                 this number, then truncating. */
  7882.  
  7883. @<Part 2@>=@[
  7884.  
  7885. #if TIMING
  7886.  
  7887. SRTN 
  7888. prn_time(VOID)
  7889. {
  7890. TIME_T time1;
  7891. double cpu,wall;
  7892. char less_than = NO;
  7893. char format[100]; /* We build the format for the time output here. */
  7894.  
  7895. cpu = (clock()-clock0)/(double)(CLOCKS_PER_SEC);
  7896. TIME(&time1);
  7897.  
  7898. /* Because the real time is in units of seconds, we might get~0, so we'll
  7899. be fancy and print an inequality in that case. */
  7900. if( (wall = DIFFTIME(time1,time0)) == 0.0)
  7901.     {
  7902.     wall = 1.0;
  7903.     less_than = YES;
  7904.     }
  7905.  
  7906. SET_COLOR(timing);
  7907.  
  7908. /* --- CPU time --- */
  7909. sprintf(format,"CPU = %%.%df sec.",TIMING_WIDTH);
  7910. printf(format,cpu);
  7911. if(cpu >= 60.0) printf(" (%.2f min.)",cpu/60.0);
  7912.  
  7913. /* --- WALL CLOCK time --- */
  7914. sprintf(format,"; REAL %%s %%.%df sec.",TIMING_WIDTH);
  7915. printf(format,less_than ? "<=" : "=",wall);
  7916. if(wall >= 60.0) printf(" (%.2f min.).",wall/60.0);
  7917.  
  7918. /* --- PERCENTAGE CPU UTILIZATION --- */
  7919. printf("  CPU/REAL = %.1f%%.\n",100.0*(cpu/wall));
  7920.  
  7921. /* Debugging message: Check for compatibility. Because the timing routines
  7922. may not be perfectly compatible through all decimal places, we check only
  7923. that |cpu <= wall| to an accuracy of tenths of seconds. */
  7924. @#if 0
  7925.    if((unsigned long)(TIMING_PREC*cpu) > (unsigned long)(TIMING_PREC*wall))
  7926.     printf("! CPU (%.3f s.) > REAL (%.3f s.). \
  7927. Check for invalid CLOCKS_PER_SEC (%.3e) or type of clock().\n",
  7928.         cpu,wall,(double)CLOCKS_PER_SEC);
  7929. @#endif
  7930.  
  7931. SET_COLOR(ordinary);
  7932. }
  7933.  
  7934. #endif /* |TIMING| */
  7935.  
  7936. @* MISCELLANEOUS FUNCTIONS.
  7937. Here are various routines that didn't fit anywhere else.
  7938.  
  7939. @*1 Initializing |program|. 
  7940.  
  7941. @<Part 2@>=@[
  7942.  
  7943. SRTN 
  7944. ini_program FCN((program0))
  7945.     PROGRAM program0 C1("")@;
  7946. {
  7947. program = program0; // Set global flag.
  7948.  
  7949. the_system = OC(THE_SYSTEM); /* |THE_SYSTEM| is defined in \.{os.hweb}. */
  7950. local_banner = OC(LOCAL_BANNER); /* See \.{os.hweb} and \.{custom.web}. */
  7951.  
  7952. ini_style(); // Default style file initializations.
  7953. }
  7954.  
  7955. @*1 Printing the banner line.
  7956. The annoyance here is that if this is to be in
  7957. color, the style file needs to be read first.  Temporarily, we've turned
  7958. that off. 
  7959. @<Part 2@>=@[
  7960.  
  7961. SRTN 
  7962. banner(VOID)
  7963. {
  7964. @%SET_COLOR(ordinary);
  7965.  
  7966. printf("This is %s",DEBUG_XCHR ? "ScRaMbLeD " : "");
  7967.  
  7968. @%CLR_PRINTF(program_name,("%s",program==tangle ? "FTANGLE" : "FWEAVE"));
  7969. printf("%s",program==tangle ? "FTANGLE" : "FWEAVE");
  7970.  
  7971. printf(" [%s version %s (%s)]. %s\n",
  7972.     (char *)the_system, (char *)version, (char *)release_date,
  7973.     (char *)local_banner);
  7974. }
  7975.  
  7976. @*1 Announcing current file.
  7977. @<Part 2@>=@[
  7978.  
  7979. SRTN 
  7980. reading FCN((in_name,emit_newline))
  7981.     CONST outer_char *in_name C0("")@;
  7982.     boolean emit_newline C1("")@;
  7983. {
  7984. CLR_PRINTF(info,("Reading")); @~ putchar(' ');
  7985. CLR_PRINTF(in_file,("%s", (char *)in_name));
  7986. CLR_PRINTF(info,("..."));
  7987.  
  7988. if(emit_newline) 
  7989.     putchar('\n');
  7990. }
  7991.  
  7992. @
  7993. @<Part 2@>=@[
  7994.  
  7995. SRTN 
  7996. writing FCN((print_write,out_name))
  7997.     boolean print_write C0("")@;
  7998.     CONST outer_char *out_name C1("")@;
  7999. {
  8000. if(print_write) 
  8001.     {CLR_PRINTF(info,("\nWriting")); putchar(' ');}
  8002. CLR_PRINTF(out_file,("%s", (char *)out_name));
  8003. CLR_PRINTF(info,("..."));
  8004. fflush(stdout);
  8005. }
  8006.  
  8007. @* Printing the progress report for each starred module.
  8008. @<Part 2@>=@[
  8009.  
  8010. SRTN 
  8011. progress(VOID)
  8012. {
  8013. if (*(loc-1)==@'*')
  8014.     { /* Found a starred section. */
  8015.     if(err_happened)
  8016.         {
  8017.         putchar('\n');
  8018.         err_happened = NO;
  8019.         }
  8020.     else putchar(' ');
  8021.     CLR_PRINTF(module_num,("*%d",module_count));
  8022.     }
  8023.  
  8024. UPDATE_TERMINAL;
  8025. }
  8026.  
  8027. @*1 {\bf nsprintf}.
  8028. The ANSI |sprintf| returns the number of characters
  8029. written; however, not all libraries are ANSI. The following function
  8030. |nsprintf| forces the issue.
  8031.  
  8032. @<Part 2@>=@[
  8033.  
  8034. int 
  8035. nsprintf FCN(VA_ALIST((s,fmt,n VA_ARGS)))
  8036.     VA_DCL(
  8037.     outer_char *s C0("")@;
  8038.     CONST outer_char *fmt C0("")@;
  8039.     int n C2("")@;)@;
  8040. {
  8041. int num_char;
  8042. VA_LIST(arg_ptr)@;
  8043. #if(NUM_VA_ARGS == 1)
  8044.     outer_char *s;
  8045.     CONST outer_char *fmt;
  8046.     int n;
  8047. #endif
  8048.  
  8049. VA_START(arg_ptr,n);
  8050.  
  8051. #if(NUM_VA_ARGS == 1)
  8052.     s = va_arg(arg_ptr,outer_char *);
  8053.     fmt = va_arg(arg_ptr,outer_char *);
  8054.     va_arg(arg_ptr,int);
  8055. #endif
  8056.  
  8057. #if ANSI_SPRINTF
  8058.     num_char =
  8059. #endif
  8060.  
  8061. vsprintf((char *)s,(CONST char *)fmt,arg_ptr);
  8062.  
  8063. #if !ANSI_SPRINTF
  8064.     num_char = (int)STRLEN(s);
  8065. #endif
  8066.  
  8067. va_end(arg_ptr);
  8068.  
  8069. return num_char;
  8070. }
  8071.  
  8072. @* INDEX.
  8073.