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

  1. @z --- ftangle.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{FTANGLE.WEB} % The FTANGLE processor.
  11.  
  12. @c
  13.  
  14. @* INTRODUCTION.  \FTANGLE\ has a fairly straightforward outline.  It
  15. operates in two phases: first it reads the source file, saving the code in
  16. compressed form; then outputs the code, after shuffling it around.  It can
  17. be compiled with the optional flag |DEBUG|. (See \.{typedefs.hweb}.)
  18.  
  19. @m _FTANGLE_ // Identifies this module to the \.{*.hweb} header files.
  20. @d _FTANGLE_h 
  21. @d _FWEB_h
  22.  
  23. @A
  24. @<Possibly split into parts@>@;
  25.  
  26. @<Include files@>@;
  27. @<Typedef declarations@>@;
  28. @<Prototypes@>@;
  29. @<Global variables@>@;
  30.  
  31. /* For pc's, the file is split into three compilable parts using the
  32. compiler-line macro |part|, which must equal either~1, 2, or~3. */
  33. #if(part == 0 || part == 1)
  34.     @<Part 1@>@;
  35. #endif // |Part == 1|
  36.  
  37. #if(part == 0 || part == 2)
  38.     @<Part 2@>@;
  39. #endif // |part == 2|
  40.  
  41. #if(part == 0 || part == 3)
  42.     @<Part 3@>@;
  43. #endif // |part == 3|
  44.  
  45. @ Here is the main program.  See the user's manual for a detailed
  46. description of the command line.
  47. @<Part 1@>=@[
  48.  
  49. int main FCN((ac, av))
  50.     int ac C0("Number of arguments.")@;
  51.     outer_char **av C1("Argument list.")@;
  52. {
  53. #if TIMING
  54.     ini_timer(); 
  55.   // Timing statistics are printed at the end of the run; see \.{common.web}.
  56. #endif // |TIMING|
  57.  
  58. /* Remember the arguments to |main| in global variables. */
  59.   argc = ac; @+ argv = av;
  60.  
  61.   ini_program(tangle); // Set the |program| flag etc.; see \.{common.web}.
  62.  
  63. @<Initialize everything@>;
  64.  
  65.   phase1(); // Read all the user's text and compress it into |tok_mem|.
  66.   phase2(); // Output the contents of the compressed tables.
  67.  
  68.   if(statistics) see_tstatistics(); // Optional statistical info.
  69.  
  70. return wrap_up(); // We actually |exit| from here.
  71. }
  72.  
  73. @ Here are the initializations done before the beginning of phase~1.
  74. @<Initialize everything@>=
  75. {
  76. @<Allocate initial tables@>@; // Stuff that must be used for command line.
  77. common_init(); // Expand the command line here.
  78. @<Allocate dynamic memory@>@; // Local dynamic memory.
  79. @<Set initial values@>@;
  80. ini_internal_fcns(); // Internal built-in function macros.
  81. ini_Ratfor(); // Initialize \Ratfor.
  82. }
  83.  
  84. @I typedefs.hweb // Declarations common to both \FTANGLE\ and \FWEAVE.
  85.  
  86. @I val.hweb // Stuff for expression evaluation.
  87.  
  88. @I macs.hweb // Macros for macro processing.
  89.  
  90.  
  91. @ The function prototypes must appear before the global variables.
  92. @<Proto...@>=
  93.  
  94. #include "t_type.h" // Function prototypes for \FTANGLE.
  95.  
  96. @* CODE for OUTPUT. When |language != C|, we must remember the last fragment
  97. of code in order to implement |+=| and similar operators for \Fortran\ or
  98. \Ratfor. 
  99.  
  100. @d RST_LAST_EXPR {plast_char = last_char; last_xpr_overflowed = NO;}
  101.  
  102. @d INDENT_SIZE 2 /* Number of columns to indent for each level of
  103. beautified Ratfor output. (Put into style file?) */
  104.  
  105. @<Glob...@>=
  106.  
  107. EXTERN int indnt_size SET(INDENT_SIZE); 
  108.     // So we can interface to \.{rat77.web}.
  109.  
  110. EXTERN outer_char HUGE *last_char, HUGE *last_end; // Dynamic array.
  111. EXTERN outer_char HUGE *plast_char; // Current position in |last_char|.
  112. EXTERN BUF_SIZE max_expr_chars; // Allocated length of |last_char|.
  113.  
  114. EXTERN boolean last_xpr_overflowed SET(NO);
  115.  
  116. EXTERN int indent_level SET(0); // Current state of Ratfor output.
  117.  
  118. @ Allocate the |last_char| array.
  119. @<Allocate dynamic memory@>=
  120.  
  121. ALLOC(outer_char,last_char,ABBREV(max_expr_chars),max_expr_chars,0);
  122. last_end = last_char + max_expr_chars;
  123. plast_char = last_char;
  124.  
  125. @ An interface to \.{rat77.web}
  126. @<Part 1@>=@[
  127.  
  128. SRTN rst_last(VOID)@/
  129. RST_LAST_EXPR@;
  130.  
  131. @ For speed, we'll buffer up the C~output.  Characters are put temporarily
  132. into |C_buffer|.  That buffer is flushed whenever a newline is emitted.  If
  133. the buffer ever gets full, an attempt is made to split the buffer at a
  134. reasonable place.  To accomplish that, we have the array |str_start| of
  135. pointers to positions in |C_buffer|.  The odd elements in |str_start| are
  136. the positions at which |stringg| mode starts; the even elements are the
  137. positions after |stringg| mode ends.  The zeroth element is |C_buffer|
  138. itself, and the last position, which should always be odd, is |pC_buffer|,
  139. the current position in the buffer.  Thus, if there are no strings in the
  140. buffer, we have |str_start[0] == C_buffer|, |str_start[1] == pC_buffer|.
  141. The ranges allowed to be split are $[0,1)$, $[2,3)$, etc.
  142.  
  143. @<Glob...@>=
  144.  
  145. /* --- Output buffer for C --- */
  146. EXTERN outer_char HUGE *C_buffer, HUGE *pC_end; // Dynamically allocated.
  147. EXTERN outer_char HUGE *pC_buffer; // Current position.
  148. EXTERN BUF_SIZE C_buf_size; // Length of dynamic buffer array.
  149.  
  150. /* --- String positions in that buffer --- */
  151. EXTERN outer_char HUGE *split_pos; // Current position.
  152.  
  153. /* --- Output buffer for \TeX\ --- */
  154. EXTERN outer_char HUGE *X_buffer, HUGE *pX_end; // Dynamically allocated.
  155. EXTERN outer_char HUGE *pX_buffer; // Current position.
  156. EXTERN BUF_SIZE X_buf_size; // Length of dynamic buffer array.
  157.  
  158. @
  159. @<Allocate dyn...@>=
  160.  
  161. /* --- Allocate C output buffer --- */
  162. ALLOC(outer_char,C_buffer,ABBREV(C_buf_size),C_buf_size,0);
  163. pC_end = C_buffer + C_buf_size - 1; // Allow for extra backslash if necessary. 
  164. pC_buffer = C_buffer; // Initialize to beginning.
  165.  
  166. #if FANCY_SPLIT
  167.     @<Reset split position@>@;
  168. #endif /* |FANCY_SPLIT| */
  169.  
  170. /* --- Allocate \TeX\ output buffer --- */
  171. ALLOC(outer_char,X_buffer,ABBREV(X_buf_size),X_buf_size,0);
  172. pX_end = X_buffer + X_buf_size;
  173. pX_buffer = X_buffer; // Initialize to beginning.
  174.  
  175. @ The |split_C| routine is called whenever |C_buffer| fills.  If the flag
  176. |FANCY_SPLIT| is off (the ANSI case), we just continue everything with a
  177. backslash.  Otherwise, we do a fancy break, described below.
  178. @<Part 1@>=@[
  179.  
  180. SRTN split_C(VOID)
  181. {
  182. #if FANCY_SPLIT
  183.     @<Fancy split@>@;
  184. #else
  185.     @<Emit a backslash and newline@>@;
  186. #endif /* |FANCY_SPLIT| */
  187. }
  188.  
  189. @ For the fancy split, we do the following: If we are in a string at this
  190. moment, we emit a backslash and dump the whole buffer.  Otherwise, we hunt
  191. through the |str_start| array for allowable positions to break.  
  192.  
  193. @<Fancy split@>=
  194. @{
  195. /* Split strings, but not constants. */
  196. if(in_string && split_pos == C_buffer)
  197.     {
  198.     @<Emit a backslash and newline@>@;
  199.     return;
  200.     }
  201.  
  202. *pC_buffer = '\0';
  203. split0_C(split_pos);
  204. }
  205.  
  206. @ When we buffer stuff out in any way, e.g. with |C_out|, we must reset the
  207. split position.
  208.  
  209. @<Reset split position@>=
  210. {
  211. split_pos = C_buffer;
  212. }
  213.  
  214. @ Here is the bare-bones C~continuation.
  215.  
  216. @d NO_INDENT 0
  217. @d INDENT 2
  218.  
  219. @<Emit a backsl...@>=
  220. {
  221. if(!meta_mode)
  222.     *pC_buffer++ = '\\'; // There's always room for one more character.
  223.  
  224. C_out(C_buffer,pC_buffer,&pC_buffer,OC("\n"),OC(""),NO_INDENT); 
  225.     // Dump out all the way to the end.
  226. }
  227.  
  228. @ Here, given an allowable range we search for a split position.
  229. @<Part 1@>=
  230.  
  231. #if FANCY_SPLIT
  232. @[
  233. SRTN split0_C FCN((p))
  234.     outer_char *p C1("Position for the split")@;
  235. {
  236. int indent;
  237.  
  238. /* If no break has been found, force a break at the end. */
  239. if(p==C_buffer) 
  240.     {
  241.     *pC_buffer++ = '\\';
  242.     p = pC_buffer;
  243.     indent = NO_INDENT;
  244.     }
  245. else 
  246.     indent = INDENT;
  247.  
  248. C_out(C_buffer,p,&pC_buffer,OC("\n"),OC(""),indent);
  249.     // Output from |C_buffer| to~|p|.
  250.  
  251. @#if 0
  252.     UPDATE_TERMINAL;
  253.     printf("\nOutput line %u split\n",OUTPUT_LINE);
  254. @#endif
  255. }
  256. #endif // |FANCY_SPLIT|
  257.  
  258. @ Write out (part of) the |C_buffer|.
  259. @<Part 1@>=@[
  260.  
  261. SRTN C_out FCN((C_buffer,p,ppC_buffer,end_str,begin_str,indent))
  262.     outer_char HUGE *C_buffer C0("Buffer we're working with")@;
  263.     outer_char HUGE *p C0("End (next available pos)")@;
  264.     outer_char HUGE * HUGE *ppC_buffer C0("")@;
  265.     outer_char *end_str C0("")@;
  266.     CONST outer_char *begin_str C0("")@;
  267.     int indent C1("Should the next buffer be indented?")@;
  268. {
  269. int n = PTR_DIFF(int, *ppC_buffer, p); // How many left in buffer.
  270.  
  271. if(p > C_buffer)
  272.     WRITE1(C_buffer,p-C_buffer)@;
  273.  
  274. /* Add trailing characters if necessary. */
  275. if(*end_str) 
  276.     WRITE1(end_str,STRLEN(end_str))@;
  277.  
  278. /* Reset the pointer, then insert the beginning character if necessary. */
  279. *ppC_buffer = C_buffer;
  280.  
  281. while(*begin_str) 
  282.     *(*ppC_buffer)++ = *begin_str++;
  283.  
  284. /* Add optional indentation (i.e., fill with blanks). */ 
  285. while(indent--)
  286.     *(*ppC_buffer)++ = ' ';
  287.  
  288. /* If there's still stuff in the buffer, move it to the beginning. */
  289. if(n)
  290.     {
  291.     STRNCPY(*ppC_buffer,p,n);
  292.     *ppC_buffer += n; // Reset the current pointer if necessary.
  293.     }
  294.  
  295. flush0(); // Count the lines.
  296. @<Reset split...@>@;
  297. }
  298.  
  299. @ Here are output macros and routines for \FTANGLE. 
  300.  
  301. @d OUT_FILE outp_file[lan_num(out_language)] // Output of \FTANGLE.
  302.  
  303. /* The next may not be used. */
  304. @d C_printf(c,a) 
  305.     {
  306.     if(!out_file) open_out(OC(""),YES);
  307.     if(fprintf(out_file,c,a)<0) out_error(OC("fprintf"));
  308.     }
  309.  
  310. @<Part 1@>=@[
  311.  
  312. SRTN C_putc FCN((c))
  313.     outer_char c C1("Character to be sent to output.")@;
  314. {
  315. static CONST outer_char HUGE *prefx = OC("");
  316.  
  317. CHECK_OPEN; // Output files are opened only when necessary.
  318.  
  319. if(dbg_output) 
  320.     printf("c = '%c' (0x%x)\n",c,c);
  321.  
  322. if(at_beginning && meta_mode && !nuweb_mode && (in_string || in_version))
  323.     { /* Invoke |C_putc| recursively. */
  324.     at_beginning = NO; // Prevent infinite recursion.
  325.     out_pos = 0; // For \Fortran.
  326.     pmeta = &t_style.meta[lan_num(language)];
  327.     prefx = OUT_STR(in_version ? pmeta->hdr.prefx : pmeta->msg.prefx);
  328.     }
  329.  
  330. switch(language)
  331.     {
  332.  case RATFOR: 
  333.  case RATFOR_90:
  334.     if(!Ratfor77) 
  335.         {
  336.         RAT_out(c); /* Old-style \Ratfor. Modern \Ratfor\ falls
  337. through to \Fortran. */ 
  338.         break;
  339.         }
  340.  
  341.  case FORTRAN: 
  342.  case FORTRAN_90:
  343.     if(reverse_indices 
  344.          && ((pai > paren_level) || rparen || !(in_string || in_version)))
  345.         @<Reverse \Fortran\ indices@>@;
  346.     else
  347.         buffer_out(c);
  348.  
  349.     break;
  350.  
  351.  case LITERAL:
  352.  case TEX:
  353.     @<Buffer \TeX\ output@>@;
  354.     break;
  355.  
  356.  case C: 
  357.  case C_PLUS_PLUS:
  358.  default:
  359. #ifndef mac // \.{Machine-dependent}: Don't buffer C output.
  360.     @<Buffer C output@>@;
  361. /* If the above buffering (a relatively recent addition) doesn't work, use
  362. the following: */ 
  363. #else
  364.     if(c == '\n') 
  365.         flush0(); // Count the lines.
  366.     PUTC(c); 
  367. #endif /* |mac| */
  368.     break;
  369.     }
  370.  
  371. at_beginning = BOOLEAN(c=='\n');
  372. }
  373.  
  374. @ A recent addition to speed up the C~output.
  375. @<Buffer C output@>=
  376. @{
  377. *pC_buffer++ = c; // Add present character to buffer.
  378.  
  379. if(c == '\n') 
  380.     C_out(C_buffer,pC_buffer,&pC_buffer,OC(""),OC(""),NO_INDENT); 
  381.         // Output whole buffer.
  382. else if(pC_buffer == pC_end) 
  383.     split_C();
  384. }
  385.  
  386. @
  387. @<Buffer \TeX...@>=
  388. {
  389. *pX_buffer++ = c; // Add present character to buffer.
  390.  
  391. if(c == '\n') 
  392.     C_out(X_buffer,pX_buffer,&pX_buffer,OC(""),
  393.        (outer_char HUGE *)CHOICE(meta_mode && language==TEX,prefx,OC("")),
  394.        NO_INDENT);
  395. else if(pX_buffer == pX_end) 
  396.     split_X(prefx);
  397. }
  398.  
  399. @
  400. @<Part 1@>=@[
  401.  
  402. SRTN split_X FCN((prefx))
  403.     CONST outer_char HUGE *prefx C1("")@;
  404. {
  405. outer_char HUGE *p = pX_buffer - 1;
  406.  
  407. WHILE()
  408.     {
  409.     if(p==X_buffer) 
  410.         @<Print warning message about unsplittable \TeX\
  411.             line, break the line, and |return|@>@;
  412.  
  413.     if(*p == ' ')
  414.         {
  415.         C_out(X_buffer,p+1,&pX_buffer,OC("\n"),
  416.          (outer_char HUGE *)CHOICE(meta_mode && language==TEX, 
  417.           prefx, OC("")),
  418.          NO_INDENT);
  419.         return;
  420.         }
  421.  
  422.     if(*(p--) == '\\' && *p != '\\')
  423.         {
  424.         C_out(X_buffer,p+1,&pX_buffer,
  425.          language==TEX ? OC("%\n") : OC("\n"),
  426.          (outer_char HUGE *)CHOICE(meta_mode && language==TEX, 
  427.           prefx, OC("")),
  428.          NO_INDENT);
  429.         return;
  430.         }
  431.     }
  432. }
  433.  
  434. @
  435. @<Print warning message about unsplit...@>=
  436. {
  437. ERR_PRINT(T,"Line had to be broken");
  438. C_out(X_buffer,pX_buffer,&pX_buffer,
  439.     language==TEX ? OC("%\n") : OC("\n"),
  440.     OC(""),NO_INDENT);
  441. return;
  442. }
  443.  
  444. @ For~C, output of characters is very simple: use of |putc| suffices. For
  445. \Ratfor\ it's just slightly more complicated; we have to intercept the
  446. meta-comment characters. (We don't need to use metacomments in~C, since
  447. C~has its own preprocessor.) \Fortran\ is much more involved; we must
  448. buffer things up, then flush them out line by line in order to respect the
  449. 72~column restriction and emit continuation characters appropriately.
  450.  
  451. @d NOT_CONTINUATION 0
  452. @d CONTINUATION 1
  453.  
  454. @<Part 1@>=@[
  455.  
  456. static outer_char last_out = '\0'; // In \Fortran, the last character output.
  457.  
  458. /* Various flags help \Fortran\ out. */
  459. static boolean is_label = NO;
  460. static boolean should_continue = NO;
  461. static continuation_line = NOT_CONTINUATION;
  462.  
  463. static STMT_LBL stmt_num[50]; /* Archaic; for numbering
  464.             |do|s in \Fortran. Should use \Ratfor\ instead. */
  465. static short do_level = 0;
  466.  
  467. @ The following variables are needed in both parts~1 and~2.
  468. @<Glob...@>=
  469.  
  470. EXTERN int rst_pos SET(0); // The position immediately after resetting.
  471. EXTERN int out_pos SET(0); // Current position in \Fortran's output buffer.
  472. EXTERN boolean in_string SET(NO); // Faster version of the output state.
  473. EXTERN boolean in_constant SET(NO); // Ditto.
  474. EXTERN boolean started_vcmnt SET(NO);
  475. EXTERN boolean meta_mode SET(NO);
  476.  
  477. @ The function |C_putc| is used for all languages in order to send the
  478. character to the right place. Here is a routine which formats and prints
  479. out a string. It's used in printing out the line information.  Note use of
  480. the macro |vsprintf_| to take account of the different way that Sun-CC
  481. handles variable arguments.
  482.  
  483. @d N_STRBUF 150
  484.  
  485. @<Part 1@>=@[
  486. SRTN C_sprintf FCN(VA_ALIST((fmt,n VA_ARGS)))
  487.     VA_DCL(
  488.     CONST outer_char fmt[] C0("String to be printed.")@;
  489.     int n C2("Number of arguments to follow.")@;)@;
  490. {
  491. VA_LIST(arg_ptr)@;
  492. outer_char temp[N_STRBUF];
  493. outer_char HUGE *t;
  494.  
  495. VA_START(arg_ptr,n);
  496. vsprintf_((char *)temp,(CONST char *)fmt,arg_ptr)@;  // Length not checked now.
  497. va_end(arg_ptr);
  498.  
  499. for(t=temp; *t; ++t) C_putc(*t);
  500. }
  501.  
  502. @ Here is \Ratfor's output routine. All it does is intercept the
  503. meta-comment characters and makes the intervening text into a comment.
  504.  
  505. @d send_new_line RST_LAST_EXPR@; flush0(); PUTC('\n')@;
  506.  
  507. @<Part 1@>=@[
  508.  
  509. SRTN RAT_out FCN((c))
  510.     outer_char c C1("Output this character to \Ratfor.")@;
  511. {
  512. switch(c)
  513.     {
  514.     case end_meta:
  515.         send_new_line;
  516.         return;
  517.  
  518.     case begin_meta:
  519.         if(meta_mode) return; // The second in a row.
  520.         meta_mode = YES; // NOTE: FALLS THROUGH to next case.
  521.  
  522.     case '\n':
  523.         send_new_line;
  524.         if(meta_mode) PUTC('#'); // \Ratfor\ comment.
  525.         return;
  526.  
  527.     default:
  528.         PUTC(c);
  529.         return;
  530.     }
  531. }
  532.         
  533. @ \Fortran's output routine is much more complicated, because things have
  534. to be buffered up.
  535.  
  536. @<Glob...@>=
  537.  
  538. IN_COMMON outer_char outp_buf[];    // \Fortran's output buffer.
  539. IN_COMMON int nbuf_length; // Maximum of above, for breaking.
  540. EXTERN boolean out_at_beginning SET(YES); // Flag for the output buffer.
  541.  
  542. @ Send a character to \Fortran's output (buffered).  Possibly against the
  543. general philosophy of \WEB, here we make some attempt to make the output
  544. readable by indenting loop structures.  (It's not clear the indentation
  545. scheme has been adequately tested when the level is very deep.)
  546.  
  547. @<Part 1@>=@[
  548.  
  549. SRTN buffer_out FCN((c))
  550.     outer_char c C1("Output this character to the \Fortran\ buffer.")@;
  551. {
  552. outer_char *px; // For |in_string| |meta_mode| processing.
  553.  
  554. /* Remember the output character, since we may want to spit it out again
  555. later, as in |i *= expr| $\to$ |i = i*(expr)|. Turning off the
  556. |compound_assignments| flag by option \.{-+} will speed things up a bit. */
  557. if(compound_assignments && !send_rp)
  558.     if(plast_char >= last_end) 
  559.         last_xpr_overflowed = YES;
  560.     else 
  561.         *plast_char++ = c;
  562.  
  563. @<|switch| for single character output to \Fortran@>@;
  564.  
  565. /* When a statement label ends, skip to column~7. */
  566. if(is_label && !isdigit(c) )
  567.     {
  568.     is_label = NO;
  569.     out_pos = 6 + indent_level*INDENT_SIZE;
  570.     if(c==':' || c==' ') return; // Throw away the trailing colon.
  571.     }
  572.  
  573. @<Possibly number |do|s@>@;
  574.  
  575. /* Can't put it off any longer: Put the character into the buffer. */
  576. last_out = outp_buf[out_pos++] = c;
  577.  
  578. return;
  579. }
  580.  
  581. @ Not every character fired at |buffer_out| should actually be printed on
  582. the output file; some are special flags.
  583.  
  584. @<|switch| for single char...@>=
  585.  
  586. switch(c)
  587.     {
  588. case '\0': if(!in_string) return;  // In case a null sneaks in, ignore it.
  589.  
  590. /* Reset the verbatim comment mode. We have to remember whether we were in
  591. the middle of a line; if we were, we must resume continuation mode. */
  592.     if(in_string && started_vcmnt) 
  593.         {
  594.         NEWLINE_TO_FORTRAN(should_continue);
  595.         started_vcmnt = NO;
  596.         return;
  597.         }
  598.     break;
  599.  
  600. case '{':
  601. case '}':
  602. /* Filter out braces from \Ratfor. */
  603.     if(!in_string && xpn_Ratfor) return;
  604.     break;
  605.  
  606.  /* Ignore any blanks at beginning of line. */
  607. case ' ':
  608.     if(out_at_beginning) return;
  609.     break;
  610.  
  611. @t\4@>@<Case for newline@>@;
  612.  
  613. /* Semicolons not in strings mean emit a new line (except when they were
  614. earlier translated into |semi| during stringizing). */
  615. case ';':
  616.   if(!(in_string || in_constant))
  617.     {
  618.     NEWLINE_TO_FORTRAN(NOT_CONTINUATION);
  619.     return;
  620.     }
  621.    break;
  622.  
  623. case interior_semi:
  624. case semi:
  625.     c = ';'; @+ break;
  626.  
  627.  /* Handle meta-comments. */
  628. case begin_meta:
  629.     if(!meta_mode && last_out != '\n') flush_out(YES);
  630.     meta_mode = YES;
  631.     if(in_string)
  632.         { /* Start standard meta-comment. */
  633.         TO_BUFFER(top);
  634.         if(out_pos > 0) flush_out(YES);
  635.         }
  636.     rst_out(NOT_CONTINUATION);
  637.     return;
  638.  
  639. case end_meta:
  640.     if(in_string)
  641.         { /* Finish standard meta-comment. */
  642.         TO_BUFFER(bottom);
  643.         if(out_pos > 0) flush_out(YES);
  644.         started_vcmnt = NO;
  645.         }
  646.     else flush_out(YES);
  647.  
  648.     rst_out(NOT_CONTINUATION);
  649.     return;
  650.    }
  651.  
  652. /* If we're still going at column 73, emit a new line and make the next
  653. line a continuation line. */
  654. if(out_pos >= nbuf_length)
  655.     {
  656.     if(free_Fortran) outp_buf[out_pos++] = '&'; // Standard F--90 contin.
  657.     flush_out(YES);
  658.     rst_out(CONTINUATION);    /* Continuation. */
  659.  
  660.     if(in_string && started_vcmnt) @<Begin verbatim comment line@>;
  661.     }
  662.  
  663. if(out_at_beginning)
  664.     {
  665.     out_at_beginning = NO;
  666.  
  667. /* Statement labels require special treatment. When we sense one, we raise
  668. a special flag and put them into column~1. */
  669.     if(!in_string)
  670.            if(isdigit(c) && !is_label)
  671.         {
  672.         is_label = YES;
  673.         out_pos = 0;
  674.         }
  675.        else if(c=='#') 
  676.         { /* Place the \&{\#line} command in column~1. */
  677.         outp_buf[0] = t_style.line_char[lan_num(language)];
  678. // It's a comment. 
  679.         out_pos = 1;
  680.         return;
  681.         }
  682.     }
  683.  
  684. @ Processing a newline is somewhat annoying because of the need to handle
  685. verbatim comments. The logic could be cleaned up here, but since it
  686. permeates the entire code, don't try it.
  687.  
  688. @<Case for newline@>=
  689.  
  690. case '\n':
  691.     if(in_cdir) 
  692.         {
  693.         out_pos = 0;
  694.         }
  695.     else if(!in_string || (in_string && started_vcmnt) )
  696.         {
  697.         NEWLINE_TO_FORTRAN(NOT_CONTINUATION);
  698.  
  699.         if(in_string && started_vcmnt)@<Begin verbatim comment line@>@;
  700.         }
  701.     else if(!started_vcmnt)
  702.         { /* Remember if  there's stuff in the buffer. If so, when
  703. we terminate the verbatim comment we must continue. */
  704.         should_continue = BOOLEAN(out_pos > rst_pos); 
  705.  
  706. /* The next statement prevents overwriting the stuff already in the buffer. */
  707.         if(should_continue) {NEWLINE_TO_FORTRAN(NOT_CONTINUATION);}
  708.         should_continue = BOOLEAN((!free_Fortran) && should_continue);
  709.         @<Begin verbatim c...@>;
  710.         started_vcmnt = YES;
  711.         }
  712.  
  713.     return;
  714.  
  715.  
  716. @ The following stuff, which implements the \.{-d}~option, is kludgy and
  717. obsolete; use \Ratfor\ instead. 
  718.  
  719. @<Possibly number |do...@>=
  720.  
  721. if(number_dos && !continuation_line && (language==FORTRAN ||
  722.         language==FORTRAN_90 || R66) ) 
  723.     {
  724.     outer_char HUGE *do_pos;
  725.  
  726.     do_pos = outp_buf + 6;
  727.  
  728.     if(out_pos == 9)
  729.         {
  730.         if(STRNCMP(do_pos,"do ",3)==0 && !isdigit(c))
  731.             {
  732.             sprintf((char *)(do_pos+=3),"%lu ",
  733.                 stmt_num[do_level++] = max_stmt++);
  734.  
  735.             while(*do_pos++ != '\0') out_pos++;
  736.             }
  737.         }
  738.     else if( (out_pos==10 && STRNCMP(do_pos,"endd",4)==0) ||
  739.             (out_pos==11 && STRNCMP(do_pos,"end d",5)==0) )
  740.         {
  741.         if(do_level == 0)
  742.             {
  743.             ERR_PRINT(T,"Too many END DOs");
  744.             *outp_buf = 'C';
  745.             }
  746.         else
  747.             {
  748.             sprintf((char *)outp_buf,"%-5lu CONTINUE",
  749.                 stmt_num[--do_level]); 
  750.             out_pos = 14;
  751.             return;
  752.             }
  753.         }
  754.     }
  755.  
  756. @ Handle a newline when the output language is \Fortran.
  757.  
  758. @d NEWLINE_TO_FORTRAN(continuation_flag)
  759.     flush_out(YES); // Write out the buffer.
  760.     rst_out(continuation_flag)@; /* Reinitialize the buffer with no
  761. continuation character. */
  762.  
  763. @ The following is used during output of verbatim comments.
  764. @<Begin verbatim c...@>=
  765. {
  766. int k;
  767.  
  768. if(!meta_mode)
  769.     {
  770.     outp_buf[0] = begin_comment_char[lan_num(out_language)];
  771.  
  772.     for(out_pos = 1,k=spcs_after_cmnt; k; k--)
  773.         outp_buf[out_pos++] = ' ';
  774.     }
  775.  
  776. nbuf_length = MAX(t_style.output_line_length,80);
  777. out_at_beginning = NO; // Prevents stripping off blanks at beginning of cmnt.
  778. }
  779.  
  780. @ This routine writes out the current contents of \Fortran's output buffer.
  781. @<Part 1@>=@[
  782.  
  783. SRTN flush_out FCN((prn_new_line))
  784.     boolean prn_new_line C1("Do we print a newline?")@;
  785. {
  786. outp_buf[out_pos] = '\0'; // Terminate the buffer.
  787.  
  788. /* Dump it out, followed by a newline. */ 
  789. WRITE1(outp_buf,out_pos)@;
  790.  
  791. if(prn_new_line) 
  792.     {
  793.     last_out = '\n';
  794.     PUTC(last_out);
  795.     flush0();
  796.     }
  797. }
  798.  
  799. @ After we've flushed the buffer, we must prepare it for the next stuff.
  800.  
  801. @d TO_BUFFER(type)
  802.     if(!nuweb_mode)
  803.         {
  804.         px = t_style.meta[lan_num(language)].msg.type;
  805.         STRCPY(outp_buf,px);
  806.         out_pos = STRLEN(px);
  807.         }
  808.  
  809. @<Part 1@>=@[
  810.  
  811. int rst_out FCN((continuation))
  812.     boolean continuation C1("Is line a continuation?")@;
  813. {
  814. if(!continuation) RST_LAST_EXPR@; /* Reset the pointer so we can remember the
  815.                     upcoming expression. */
  816.  
  817. /* Blank out the comment and label field (first five columns). */
  818. for(out_pos=0; out_pos<5; ++out_pos)
  819.     outp_buf[out_pos] = ' ';
  820.  
  821. /* Deposit the continuation character. */
  822. outp_buf[out_pos++] = continuation ? t_style.cchar : (outer_char)' ';
  823. continuation_line = continuation;
  824. out_at_beginning = BOOLEAN(!continuation_line);
  825.  
  826. nbuf_length = t_style.output_line_length;
  827.  
  828. if(meta_mode) 
  829.     {
  830.     if(!in_string)
  831.         { /* Error message. */
  832.         outp_buf[0] = begin_comment_char[lan_num(out_language)];
  833.         if(!xpn_Ratfor) out_pos = 1 + spcs_after_cmnt;
  834.         }
  835.  
  836.     nbuf_length = MAX(nbuf_length,80);
  837.     }
  838.  
  839. /* If it's not a continuation line, mark the beginning. Also, if we're in a
  840. loop, indent appropriately. */
  841. if(out_at_beginning && xpn_Ratfor) blank_out(indent_level);
  842.  
  843. return rst_pos = out_pos;
  844. }
  845.  
  846. @ Blank out columns appropriate to |indent_level|.
  847. @<Part 1@>=@[
  848.  
  849. SRTN blank_out FCN((n))
  850.     int n C1("Number of levels to indent.")@;
  851. {
  852. outer_char HUGE *p;
  853. int i;
  854.  
  855. for(i=0,p=outp_buf+out_pos; i < n*INDENT_SIZE; i++) *p++ = ' ';
  856.  
  857. out_pos += i;
  858. rst_pos = out_pos;
  859. }
  860.  
  861. @*1 Index reversal.  For \Fortran\ programming, the \.{-)} option turns on
  862. \It{index reversal}.  It converts constructions of the form `\.{a(k)(i)}'
  863. to `\.{a(i,k)}', `\.{a(k(1)(2))(j)}' to `\.{a(j,k(2,1))}'.  As evidenced by
  864. this last example, the procedure must be recursive.  It works as follows.
  865. When a left parenthesis is recognized, the parenthesis level is advanced.
  866. Output tokens are copied into a temporary buffer.  If the combination
  867. `\.{)(}' is recognized, the buffer level is advanced and tokens are copied
  868. into the new buffer.  This continues until a right parenthesis is
  869. recognized.  Then the buffer levels are copied in reverse order to the
  870. buffer of the previous parenthesis level, with commas inbetween.
  871.  
  872. The annoyance is how to treak `\.{)(}'.  There's no room for more tokens;
  873. furthermore, the combination might be produced by macro processing.
  874. Therefore, what actually happens when a right paren is seen is that a flag
  875. |rparen| is set.  The buffers are not actually unwound at this time, but
  876. deferred until the next character, where it can be decided whether `\.{)(}'
  877. has occurred.  This is necessary because the output scheme cannot
  878. conveniently look ahead; bytes are sent to |C_PUTC| one at a time.  The
  879. disadvantage of this scheme is that white space sneaking inbetween the
  880. parens will prevent the `\.{)(}' from being recognized (with the current
  881. logic). 
  882.  
  883. @d CUR_BUF (pai->text_buf[pai->ilevel])
  884.  
  885. @<Typedef...@>=
  886.  
  887. /* We'll manage the buffers with a structure.  That way, we can use a
  888. standard routine |store| to add a byte. */
  889. typedef struct
  890.     {
  891.     outer_char HUGE *start, HUGE *pos, HUGE *end;
  892.     } TEXT_BUF;
  893.  
  894. /* One parenthesis level is described like this. */
  895. typedef struct
  896.     {
  897.     int ilevel;    // Current buffer (index) level.
  898.     TEXT_BUF HUGE * HUGE *text_buf;// Temporary storage for the index tokens.
  899.     TEXT_BUF HUGE *last_buf; // Buffer of the previous level.
  900.     } PAREN_LEVEL;
  901.  
  902. EXTERN PAREN_LEVEL HUGE *paren_level, HUGE *paren_level_end, HUGE *pai;
  903.  
  904. EXTERN int rparen TSET(NO); // Was the last character a right paren?
  905.  
  906.  
  907. @
  908. @<Allocate dyn...@>=
  909. {
  910. paren_level = GET_MEM("paren_level", t_style.paren.nest, PAREN_LEVEL);
  911. paren_level_end = paren_level + t_style.paren.nest;
  912.  
  913. /* Initialize each nesting level. */
  914. for(pai=paren_level; pai<paren_level_end; pai++)
  915.     pai->text_buf = GET_MEM("pai->text_buf", t_style.paren.num,
  916.         TEXT_BUF HUGE *);
  917.  
  918. pai = paren_level;
  919. pai->ilevel = 0;
  920. pai->text_buf[0] = pai->last_buf = calloc(1, sizeof(TEXT_BUF));
  921. }
  922.  
  923. @ Completed index levels are written into the appropriate |TEXT_BUF|, which
  924. is initialized if necessary.  If we're at parenthesis level~0, we don't
  925. store, but fire the byte at the \Fortran\ output buffer.
  926.  
  927. @<Part 1@>=@[
  928.  
  929. SRTN store FCN((t, c))
  930.     TEXT_BUF HUGE *t C0("")@;
  931.     outer_char c C1("")@;
  932. {
  933. if(pai == paren_level || t == paren_level[0].last_buf)
  934.     { /* Send directly to \Fortran's output buffer. */
  935.     buffer_out(c);
  936.     return;
  937.     }
  938.  
  939. /* Store in the indicated text buffer; initialize that if necessary. */
  940. if(t->start == NULL)
  941.     {
  942.     t->pos = t->start = GET_MEM("t->start", t_style.paren.len, outer_char);
  943.     t->end = t->start + t_style.paren.len;
  944.     }
  945.  
  946. if(t->pos == t->end)
  947.     {
  948.     size_t len = PTR_DIFF(size_t, t->end, t->start);
  949.  
  950.     t->start = (outer_char *)REALLOC(t->start,len + t_style.paren.len, len);
  951.     t->pos = t->start + len;
  952.     t->end = t->start + len    + t_style.paren.len;
  953.     }
  954.  
  955. *t->pos++ = c;
  956. }
  957.  
  958. @ Here we unwind the index entries in reverse order, interspersing them by
  959. commas.  Unwinding one buffer entry just means copying it into the
  960. |last_buf|. 
  961.  
  962. @<Part 1@>=@[
  963.  
  964. SRTN unwind(VOID)
  965. {
  966. int i;
  967. TEXT_BUF HUGE *t;
  968. outer_char HUGE *s1;
  969.  
  970. if(pai == paren_level)
  971.     {
  972.     ERR_PRINT(T, "Missing '('");
  973.     buffer_out(')');
  974.     return;
  975.     }
  976.  
  977. for(i=pai->ilevel; i >= 0; i--)
  978.     {
  979.     t = pai->text_buf[i];
  980.  
  981.     for(s1=t->start; s1<t->pos; s1++)
  982.         store(pai->last_buf, *s1);
  983.  
  984.     t->pos = t->start; // Reset the buffer.
  985.  
  986.     if(i > 0)
  987.         store(pai->last_buf, ',');
  988.     }
  989.  
  990. store(pai->last_buf, ')');
  991. pai--; // Decrement parenthesis level.
  992. }
  993.  
  994.  
  995. @ The following code is pressed into service with the `\.{-)}' flag (and
  996. when one is not inside a character string).
  997.  
  998. @<Reverse \Fortran\ indices@>=
  999. {
  1000. switch(c)
  1001.     {
  1002.    case '(':
  1003.     if(rparen)
  1004.         { /* The combination `\.{)(}' has occurred; advance the
  1005. buffer level. */
  1006.         pai->ilevel++;
  1007.  
  1008.         if(pai->ilevel == (int)t_style.paren.num)
  1009.             NEW_SPRM("paren.num", t_style.paren.num);
  1010.  
  1011.         @<Allocate |CUR_BUF| if necessary@>@;
  1012.             
  1013.         rparen = NO;
  1014.         }
  1015.     else
  1016.         { /* Time for a new parenthesis level.  Put the parenthesis
  1017. into the old level.  Remember where that was, then advance the level. */
  1018.         store(CUR_BUF, '(');
  1019.  
  1020.         (pai+1)->last_buf = CUR_BUF;
  1021.         pai++;
  1022.  
  1023.         if(pai == paren_level_end)
  1024.             NEW_SPRM("paren.nest", t_style.paren.nest);
  1025.  
  1026.         pai->ilevel = 0;
  1027.  
  1028.         @<Allocate |CUR_BUF|...@>@;
  1029.         }
  1030.  
  1031.     break;
  1032.  
  1033.    case ')':
  1034.     if(!rparen)
  1035.         rparen = YES;
  1036.     else
  1037.         unwind();
  1038.  
  1039.     break;
  1040.  
  1041.    default:
  1042.     if(rparen)
  1043.         {
  1044.         unwind();
  1045.         rparen = NO;
  1046.         }
  1047.  
  1048.     if(in_string && pai == paren_level)
  1049.         buffer_out(c);
  1050.     else
  1051.         store(CUR_BUF, c);
  1052.  
  1053.     break;
  1054.     }
  1055. }
  1056.  
  1057. @
  1058. @<Allocate |CUR_BUF|...@>=
  1059. {
  1060. if(!CUR_BUF)
  1061.     CUR_BUF = GET_MEM("CUR_BUF", 1, TEXT_BUF);
  1062. }
  1063.  
  1064. @i texts.hweb
  1065.  
  1066.  
  1067. @ Allocate the principal arrays.
  1068. @<Allocate dyn...@>=
  1069.  
  1070. alloc_Rat(); // Allocate \Ratfor\ arrays.
  1071.  
  1072. ALLOC(text,text_info,ABBREV(max_texts),max_texts,0);
  1073. text_end = text_info + max_texts - 1;
  1074.  
  1075. ALLOC(text,txt_dinfo,ABBREV(dtexts_max),dtexts_max,0);
  1076. textd_end = txt_dinfo + dtexts_max - 1;
  1077.  
  1078. ALLOC(eight_bits,tok_mem,ABBREV(max_toks_t),max_toks,0);
  1079. tok_m_end = tok_mem + max_toks - 1;
  1080.  
  1081. ALLOC(eight_bits,tok_dmem,ABBREV(max_dtoks),max_dtoks,0);
  1082. tokd_end = tok_dmem + max_dtoks - 1;
  1083.  
  1084. @ The convention is that the first entry, relating to the unnamed module,
  1085. has no replacement text. (The |CAST| operation was necessary to make the
  1086. Aztec compiler happy. Maybe it's not necessary anymore since we switched to
  1087. dynamic allocation.)
  1088. @<Set init...@>=
  1089.  
  1090. CAST(text_pointer,text_info)->tok_start = tok_ptr = tok_mem;
  1091. CAST(text_pointer,txt_dinfo)->tok_start = tok_dptr = tok_dmem;
  1092.  
  1093.   /* This makes replacement text 0 of length zero. */
  1094. text_ptr = text_info+1; text_ptr->tok_start = tok_mem;
  1095. txt_dptr = txt_dinfo + 1; txt_dptr->tok_start = tok_dmem;
  1096.  
  1097. @ If |p| is a pointer to a module name, |p->equiv| is a pointer to its
  1098. replacement text, an element of the array |text_info|.
  1099.  
  1100. @ The undefined module has no replacement text.
  1101.  
  1102. @<Set init...@>=
  1103.  
  1104. CAST(name_pointer,name_dir)->equiv = (EQUIV)text_info; 
  1105.  
  1106. @ Here's the procedure that decides whether a name of length |l|
  1107. starting at position |first| equals the identifier pointed to by |p|:
  1108.  
  1109. @<Part 1@>=@[
  1110.  
  1111. boolean names_match FCN((p,first,l,dummy))
  1112.     name_pointer p C0("Points to the proposed match.")@;
  1113.     CONST ASCII HUGE *first C0("Position of first character of string.")@;
  1114.     int l C0("length of identifier.")@;
  1115.     eight_bits dummy C1("Not used here")@;
  1116. {
  1117.   if (length(p)!=l) return NO;
  1118.   return (boolean)(!STRNCMP(first,p->byte_start,l));
  1119. }
  1120.  
  1121. @ The |ini_node| operation differs for \FTANGLE\ and \FWEAVE.
  1122. @<Part 1@>=@[
  1123.  
  1124. SRTN ini_node FCN((node))
  1125.     CONST name_pointer node C1("")@;
  1126. {
  1127. node->equiv=(EQUIV)text_info;
  1128. @<Initialize |mod_info| and |Language|@>@;
  1129. }
  1130.  
  1131. @ Several procedures are called only by \.{WEAVE}, but null routines need
  1132. to be here so the linker doesn't complain.
  1133.  
  1134. @<Part 1@>=@[
  1135.  
  1136. SRTN ini_p FCN((p,t))
  1137.     name_pointer p C0("")@;
  1138.     eight_bits t C1("")@;
  1139. {}
  1140.  
  1141. SRTN open_tex_file(VOID)
  1142. {}
  1143.  
  1144. @* TOKENS.  Replacement texts, which represent code in a compressed format,
  1145. appear in |tok_mem| as mentioned above. The codes in these texts are called
  1146. `tokens'; some tokens occupy two consecutive eight-bit byte positions, and
  1147. the others take just one byte.
  1148.  
  1149. If $p$ points to a replacement text, |p->tok_start| is the |tok_mem|
  1150. position of the first eight-bit code of that text. If |p->text_link=macro
  1151. == 0|, this is the replacement text for a macro, otherwise it is the
  1152. replacement text for a module. In the latter case |p->text_link| is either
  1153. equal to |module_flag|, which means that there is no further text for this
  1154. module, or |p->text_link| points to a continuation of this replacement
  1155. text; such links are created when several modules have texts with the same
  1156. name, and they also tie together all the texts of unnamed modules.  The
  1157. replacement text pointer for the first unnamed module appears in
  1158. |text_info->text_link|, and the most recent such pointer is |last_unnamed|.
  1159.  
  1160. @d module_flag (sixteen_bits)max_texts /* Final |text_link| in module
  1161.                         replacement texts. */ 
  1162.  
  1163. @<Glob...@>=
  1164.  
  1165. EXTERN text_pointer last_unnamed; /* Most recent replacement text of
  1166.                     unnamed module. */ 
  1167.  
  1168. @<Set init...@>=
  1169.  
  1170. last_unnamed = text_info; // Root of the unnamed module.
  1171. CAST(text_pointer,text_info)->text_link = 0; // No unnamed pieces yet.
  1172.  
  1173. @ The following procedure is used to enter a two-byte value into
  1174. |tok_mem| when a replacement text is being generated.
  1175.  
  1176. @<Part 1@>=@[
  1177.  
  1178. SRTN store_two_bytes FCN((x))
  1179.     sixteen_bits x C1("Two-byte token to be entered into |tok_mem|.")@;
  1180. {
  1181.   if (tok_ptr+2>tok_m_end) OVERFLW("tokens",ABBREV(max_toks_t));
  1182.  
  1183.   *tok_ptr++ = (eight_bits)(x >> 8); // Store high byte.
  1184.   *tok_ptr++ = (eight_bits)(x & 0377); // Store low byte.
  1185. }
  1186.  
  1187. @i stacks.hweb
  1188.  
  1189. @ Dynamically allocate the stack.
  1190. @<Allocate dyn...@>=
  1191.  
  1192. ALLOC(output_state,stack,ABBREV(stck_size_t),stck_size,1);
  1193. stck_end = stack + stck_size; // End of |stack|.
  1194.  
  1195. @ To get the output process started, we will perform the following
  1196. initialization steps. We may assume that |text_info->text_link| is nonzero,
  1197. since it points to the \cee\ text in the first unnamed module that generates
  1198. code; if there are no such modules, there is nothing to output, and an
  1199. error message will have been generated before we do any of the initialization.
  1200.  
  1201. @d UNNAMED_MODULE 0
  1202.  
  1203. @<Initialize the output stacks@>=
  1204.  
  1205. stck_ptr = stack+1; cur_name = name_dir; 
  1206. cur_repl = CAST(text_pointer,text_info)->text_link + text_info;
  1207. cur_byte = cur_repl->tok_start; cur_end = (cur_repl+1)->tok_start;
  1208. cur_mod = UNNAMED_MODULE; 
  1209. params = cur_params = cur_global_params = global_params;
  1210. frz_params();
  1211.  
  1212. @ When the replacement text for name~|p| is to be inserted into the output,
  1213. the following subroutine is called to save the old level of output and get
  1214. the new one going.
  1215.  
  1216. We assume that the C compiler can copy structures.  (Certainly true for ANSI.)
  1217. @^system dependencies@>
  1218.  
  1219. @<Part 1@>=@[
  1220.  
  1221. SRTN push_level FCN((p,b0,b1))
  1222.     name_pointer p C0("The new replacement text.")@;
  1223.     CONST eight_bits HUGE *b0 C0("If |p == NULL|, beginning of new \
  1224. stuff in memory.")@; 
  1225.     CONST eight_bits HUGE *b1 C1("If |p == NULL|, end of new stuff in \
  1226. memory.")@; 
  1227. {
  1228. if(stck_ptr==stck_end) OVERFLW("stack levels",ABBREV(stck_size_t));
  1229.  
  1230. *stck_ptr = cur_state; // Save old state.
  1231.  
  1232. /* Initialize new state. */
  1233. cur_name = p;
  1234.  
  1235. if(p != NULL)
  1236.     {
  1237.     cur_repl = (text_pointer)p->equiv;
  1238.  
  1239.     if(cur_repl == NULL) CONFUSION("push_level","cur_repl is NULL");
  1240.  
  1241.     cur_byte = cur_repl->tok_start; 
  1242.     cur_end = (cur_repl+1)->tok_start;
  1243.     }
  1244. else
  1245.     {
  1246.     cur_repl = NULL;
  1247.  
  1248.     cur_byte = (eight_bits HUGE *)b0; cur_end = (eight_bits HUGE *)b1;
  1249.     new_mbuf(); // Allocate new macro buffer. See \.{macs.web}.
  1250.     }
  1251.  
  1252. /* Get the language for this module. All modules start off in the global
  1253. language for that module. Also, the old state needs to recall the local
  1254. language switch. */
  1255. (stck_ptr++)->params = cur_params = cur_global_params =
  1256.     (p != NULL) ? params : params; /* ??? */
  1257. set_output_file(cur_language);
  1258. cur_mod = UNNAMED_MODULE; // Assume this until told otherwise.
  1259. }
  1260.  
  1261. @ When we come to the end of a replacement text, the |pop_level| subroutine
  1262. does the right thing: It either moves to the continuation of this replacement
  1263. text or returns the state to the most recently stacked level. If the pop
  1264. was successful---i.e., if there's more stuff to come---|YES| is returned.
  1265.  
  1266. @<Part 1@>=@[
  1267.  
  1268. boolean pop_level(VOID) /* do this when |cur_byte| reaches |cur_end| */
  1269. {
  1270. if(cur_repl != NULL && cur_repl->text_link < module_flag) 
  1271.     { /* Link to a continuation---i.e., the next in the chain of
  1272. replacement texts for this module. */
  1273.     cur_repl = cur_repl->text_link + text_info; // Stay on the same level.
  1274.     cur_byte = cur_repl->tok_start; 
  1275.     cur_end = (cur_repl+1)->tok_start;
  1276.  
  1277. /* In case we changed languages during the module, localize the change. */
  1278.     if(cur_repl->module_text)
  1279.         {
  1280.         params = cur_params = cur_global_params;
  1281.         frz_params();
  1282.         set_output_file(cur_language);
  1283.         }
  1284.  
  1285.     return YES;
  1286.     }
  1287.  
  1288. stck_ptr--; // Go down to the previous level.
  1289.  
  1290. if (stck_ptr>stack) 
  1291.     {
  1292.     cur_state = *stck_ptr; // Copy the current state structure.
  1293.     if(cur_language != language)
  1294.         flush_buffer();
  1295.     set_output_file(cur_language);
  1296.     return YES; // Successfully descended to a lower active level.
  1297.     }
  1298.  
  1299. return NO; // Already at lowest level (top of stack).
  1300. }
  1301.  
  1302. @ The heart of the output procedure is the |get_output| routine, which
  1303. produces the next token of output that is not a reference to a macro. This
  1304. procedure handles all the stacking and unstacking that is necessary.  It
  1305. returns the value |module_number| if the next output begins or ends the
  1306. replacement text of some module, in which case |cur_val| is that module's
  1307. number (if beginning) or the negative of that value (if ending). (A module
  1308. number of 0 indicates not the beginning or ending of a module, but a
  1309. \&{\#line} command.)  And it returns the value |identifier| if the next
  1310. output is an identifier of length two or more, in which case |cur_val|
  1311. points to that identifier name.
  1312.  
  1313. @<Global...@>=
  1314.  
  1315. /* These harmlessly redefine stuff in \.{typedefs.web}. It's a bit shaky, but
  1316. it seems to work. One was running out of lower-order tokens. */
  1317.  
  1318. #undef begin_format_stmt
  1319. #define begin_format_stmt OCTAL(14)
  1320.  
  1321. #undef end_format_stmt
  1322. #define end_format_stmt OCTAL(15)
  1323.  
  1324. EXTERN long cur_val; /* Additional information corresponding to output
  1325.     token. This must be \It{signed} (and capable of handling a full
  1326.     |sixteen_bits|) because of trickery involving output of the module
  1327.     numbers. */ 
  1328.  
  1329. @ If |get_output| finds that no more output remains, it returns the
  1330. value~|NO|. Otherwise, it returns the next token after macro expansion.
  1331.  
  1332. @<Part 1@>=@[
  1333. eight_bits get_output(VOID) 
  1334. {
  1335. sixteen_bits a; // Value of current byte.
  1336.  
  1337. restart: 
  1338.   if (stck_ptr==stack) 
  1339.     return NO; // At top of stack; nothing more.
  1340.  
  1341.   if (cur_byte==cur_end) 
  1342.     {
  1343.     cur_val = -((long)cur_mod); /* When we end a module, |cur_val| is
  1344. set to the negative of the module number.  The cast is needed because of
  1345. sign extension. */
  1346.  
  1347.     if(cur_val != ignore) 
  1348.         OUT_CHAR(module_number); /* Do this here so
  1349. it gets into the right file if we're changing languages. */
  1350.  
  1351.     pop_level();
  1352.  
  1353.     if (cur_val==ignore) 
  1354.         goto restart;
  1355.  
  1356.     return module_number;
  1357.     }
  1358.  
  1359. @<Expand output byte@>@;
  1360. }
  1361.  
  1362. @ To get the saved stuff out, we need a slightly different version of the
  1363. |get_output| routine.
  1364. @<Part 1@>=@[
  1365.  
  1366. eight_bits get_saved_output FCN((stck_ptr0))
  1367.     stack_pointer stck_ptr0 C1("")@;
  1368. {
  1369. sixteen_bits a;
  1370.  
  1371. restart:
  1372. if(stck_ptr == stack || stck_ptr != stck_ptr0) return NO;
  1373.  
  1374. if(DONE_LEVEL)
  1375.     { /* Hunt for end-of-tokens mark. */
  1376.     if(!pop_level()) CONFUSION("get_saved_output",
  1377.         "Shouldn't encounter top level here");
  1378.     return ignore;
  1379.     }
  1380.  
  1381. @<Expand output byte@>@;
  1382. }
  1383.  
  1384. @ We will recover the saved stuff by pushing the stack ``by hand''. When
  1385. |is_expr| is true, we reset the pointer used to save expressions that
  1386. implement the two-token operators like `\.{*=}'. We also allocate a new
  1387. macro buffer on the stack, and switch to it, so that if macros are expanded
  1388. during the |copy_out|, things don't get overwritten. (This last stuff is
  1389. done by |push_level|.)
  1390. @<Part 1@>=@[
  1391.  
  1392. SRTN copy_out FCN((p0,p1,is_expr))
  1393.     CONST eight_bits HUGE *p0 C0("Start of memory buffer.")@;
  1394.     CONST eight_bits HUGE *p1 C0("End of memory buffer.")@;
  1395.     boolean is_expr C1("Flag for resetting pointer to last expression.")@;
  1396. {
  1397. stack_pointer stck_ptr0;
  1398.  
  1399. /* If we're copying out an expression, reset the memory pointer. */
  1400. if(is_expr) rst_last();
  1401.  
  1402. push_level(NULL,p0,p1);
  1403. stck_ptr0 = stck_ptr;
  1404.  
  1405. while(get_saved_output(stck_ptr0))
  1406.     ;
  1407. }
  1408.  
  1409. @ The character sent by |send_single|, below.
  1410. @<Glob...@>=
  1411.  
  1412. EXTERN eight_bits sent;
  1413.  
  1414. @ Occasionally, the next byte contains useful information. That's put into
  1415. |cur_val|, which can be processed by |out_char|.
  1416.  
  1417. @<Send a single-byte token, handling escapes such as
  1418. |begin_language| or |dot_const|@>=
  1419. {
  1420. send_single(a);
  1421. }
  1422.  
  1423. @  A function so we can interface to \.{rat77.web}.
  1424. @<Part 1@>=@[
  1425.  
  1426. SRTN send_single FCN((a))
  1427.     sixteen_bits a C1("")@;
  1428. {
  1429. boolean scope;
  1430.  
  1431. switch(a)
  1432.     {
  1433.    case begin_language:
  1434. /* |begin_language| escapes the actual language, which follows next. */
  1435.     switch(sent = *cur_byte++)
  1436.         {
  1437.        case NO_LANGUAGE: // Serves double-duty for |new_output_file|.
  1438.         scope = *cur_byte++;
  1439.         a = *cur_byte++;
  1440.         a = IDENTIFIER(a,*cur_byte++);
  1441.         new_out(scope,a);
  1442.         sent = new_output_file;
  1443.         break;
  1444.  
  1445.        @t\4@>@<Cases for appending a language switch@>;
  1446.  
  1447.        case NUWEB_OFF:
  1448.        case NUWEB_ON:
  1449.         nuweb_mode = BOOLEAN(0x0F & sent);
  1450.         break;
  1451.  
  1452.        case no_mac_expand:
  1453.         mac_protected = no_expand = YES;
  1454.         break;
  1455.  
  1456.        case set_line_info:
  1457.         line_info = *cur_byte++;
  1458.         break;
  1459.         }
  1460.     break;
  1461.  
  1462.    case dot_const:
  1463.     cur_val = *cur_byte++; /* The relative number of the
  1464. operator is stored in the byte following |dot_const|. */
  1465.     sent = OUT_CHAR(a);
  1466.     break;
  1467.  
  1468.    default:
  1469.     sent = OUT_CHAR(a); // One-byte token.
  1470.     break;
  1471.     }
  1472. }
  1473.  
  1474. @ Open a new output file in response to an~\.{@@O} (global scope)
  1475. or~\.{@@o} (local scope) command.
  1476.  
  1477. @<Part 1@>=@[
  1478.  
  1479. #define TEMP_LEN (2*MAX_FILE_NAME_LENGTH)
  1480.  
  1481. SRTN new_out FCN((global_scope,a))
  1482.     boolean global_scope C0("0 for local, 1 for global")@;
  1483.     sixteen_bits a C1("")@;
  1484. {
  1485. name_pointer np = name_dir + a;
  1486. CONST ASCII HUGE *end;
  1487. size_t len;
  1488. outer_char temp_from[TEMP_LEN],temp_to[TEMP_LEN];
  1489. outer_char temp[MAX_FILE_NAME_LENGTH];
  1490.  
  1491. if(global_scope)
  1492.     {
  1493.     SPRINTF(TEMP_LEN,temp_from,
  1494.         `"\n\n  (This file was continued via @@O from %s.)",
  1495.         params.OUTPUT_FILE_NAME`);
  1496.     }
  1497. else 
  1498.     {
  1499.     SPRINTF(TEMP_LEN,temp_from," ");
  1500.     }
  1501.  
  1502. /* Extract the file name from the |name_dir|. */
  1503. PROPER_END(end);
  1504. len = PTR_DIFF(size_t, end, np->byte_start);
  1505. STRNCPY(temp,np->byte_start,len);
  1506. TERMINATE(temp,len);
  1507. to_outer((ASCII HUGE *)temp);
  1508. new_fname(¶ms.OUTPUT_FILE_NAME,temp,NULL);
  1509.  
  1510.  
  1511. if(global_scope)
  1512.     { /* Write a continuation message into the old file. */
  1513.     new_fname(&global_params.OUTPUT_FILE_NAME,temp,NULL);
  1514.     SPRINTF(TEMP_LEN,temp_to,`"  (Continued via @@O to %s.)",
  1515.         params.OUTPUT_FILE_NAME`);
  1516.     OUT_MSG(to_ASCII(temp_to),NULL);
  1517.     close_out(out_file);
  1518.     }
  1519. else
  1520.     fflush(out_file);
  1521.  
  1522. open_out(temp_from,global_scope);
  1523. }
  1524.  
  1525. #undef TEMP_LEN
  1526.  
  1527. @ The next fragment is used both here and in the \Ratfor-77 output routine.
  1528. @<Expand output byte@>=
  1529. {
  1530. a = *cur_byte++;
  1531.  
  1532. if(TOKEN1(a)) // |in_string|??
  1533.     {
  1534.     @<Send a single-byte token...@>;
  1535.     return sent;
  1536.     }
  1537. else 
  1538.     {
  1539.     a = IDENTIFIER(a,*cur_byte++);
  1540.  
  1541.     switch (a/MODULE_NAME) 
  1542.         { 
  1543.        case 0: 
  1544.         cur_val = a; 
  1545.         @<Check for wild \Ratfor\ scan@>@;
  1546.         return OUT_CHAR(identifier);
  1547.  
  1548.        case 1: 
  1549.         @<Expand module |a-MODULE_NAME|@>@;
  1550.         goto restart;
  1551.  
  1552.        default: 
  1553.         cur_val = a - MODULE_NUM; 
  1554.         if (cur_val>UNNAMED_MODULE) cur_mod = (sixteen_bits)cur_val;
  1555. /* Remember the current module so it can be used in
  1556. |out_char(module_number)| just after popping this level. */
  1557.         return OUT_CHAR(module_number); /* Module number at
  1558. beginning  of module. */
  1559.         }
  1560.     }
  1561. }
  1562.  
  1563. @ When checking for an out-of-control \Ratfor\ scan, we must look for the
  1564. following tokens:
  1565.  
  1566. @<Glob...@>=
  1567.  
  1568. IN_RATFOR sixteen_bits id_function,id_program,id_subroutine;
  1569.  
  1570. @ An errant \Ratfor\ scan can be stopped by looking for the beginning of
  1571. functions. 
  1572.  
  1573. @<Check for wild \Ratfor...@>=
  1574. {
  1575. IN_RATFOR boolean balanced;
  1576. IN_RATFOR ASCII cur_delim;
  1577.  
  1578. if(!balanced && language==RATFOR &&
  1579.         (a == id_function || a == id_program || a==id_subroutine))
  1580.     {
  1581.     RAT_ERROR(ERROR,"Inserted missing '%c' at beginning of function",
  1582.         1,XCHR(cur_delim));
  1583.     cur_byte -= 2; // Process the current identifier again.
  1584.     return OUT_CHAR(cur_delim); // Insert delimiter being searched for.
  1585.     }
  1586. }
  1587.  
  1588. @ When we expand a module, we remember the value for possible use in the
  1589. |_MODULE_NAME| macro.
  1590.  
  1591. @<Glob...@>=
  1592.  
  1593. EXTERN sixteen_bits cur_mod_no SET(0);
  1594.  
  1595. @ Implement the \.{\_MODULE\_NAME} built-in.
  1596.  
  1597. @<Define internal macros@>=
  1598.  
  1599. SAVE_MACRO("_MODULE_NAME $STRING($$MODULE_NAME)");
  1600. SAVE_MACRO("$MODULE_NAME $STRING($$MODULE_NAME)");
  1601.  
  1602. @
  1603. @d UNNAMED_MOD "unnamed"
  1604. @<Part 1@>=@[
  1605.  
  1606. SRTN i_mod_name_ FCN((n,pargs))
  1607.     int n C0("")@;
  1608.     PARGS pargs C1("")@;
  1609. {
  1610. int len;
  1611. name_pointer np = cur_name;
  1612. eight_bits HUGE *p;
  1613.  
  1614. CHK_ARGS("$MODULE_NAME",0);
  1615.  
  1616. if(cur_name) 
  1617.     cur_mod_no = (sixteen_bits)(np - name_dir);
  1618. else 
  1619.     cur_mod_no = 0;
  1620.  
  1621. len = cur_mod_no ? (int)length(np) : STRLEN(UNNAMED_MOD);
  1622. MCHECK(len,"current module name");
  1623.  
  1624. if(cur_mod_no)
  1625.     for(p=np->byte_start; p<(np+1)->byte_start; )
  1626.         *mp++ = *p++;
  1627. else
  1628.     {
  1629.     STRCPY(mp,UNNAMED_MOD);
  1630.     to_ASCII(mp);
  1631.     mp += len;
  1632.     }
  1633. }
  1634.  
  1635. @ Here's the number corresponding to the current module name.
  1636. @<Part 1@>=@[
  1637.  
  1638. SRTN i_sect_num_ FCN((n,pargs))
  1639.     int n C0("")@;
  1640.     PARGS pargs C1("")@;
  1641. {
  1642. num_to_mbuf(n,pargs,"$SECTION_NUM",0,"section number",cur_mod);
  1643. }
  1644.  
  1645. @ The user may have forgotten to give any code text for a module name,
  1646. or the code text may have been associated with a different name by mistake.
  1647.  
  1648. @<Expand module |a-...@>=
  1649. {
  1650. name_pointer np;
  1651.  
  1652. a -= MODULE_NAME;
  1653.  
  1654. np = name_dir + a;
  1655.  
  1656. if(np->equiv != (EQUIV)text_info) 
  1657.     push_level(np,NULL,NULL); 
  1658. else if(a != UNNAMED_MODULE) 
  1659.     { /* Module definition missing. */
  1660.     SET_COLOR(error);
  1661.     printf("\n! Not present: <"); prn_id(np); ERR_PRINT(NULL,">");
  1662. @.Not present: <section name>@>
  1663.     SET_COLOR(ordinary);
  1664.     @<Output a function call for debugging purposes@>@;
  1665.     }
  1666. }
  1667.  
  1668. @ When a missing module is detected, the command `\.{\$STUB(\It{name})}' is
  1669. inserted.  That macro expands by default to a function call appropriate to
  1670. the current language.
  1671.  
  1672. @<Define internal macros@>=
  1673.  
  1674. SAVE_MACRO("_STUB(s)$IFCASE($LANGUAGE_NUM,\
  1675. {missing_mod(#s);},{missing_mod(#s);},\
  1676. call nomod(#s),call nomod(#s),\
  1677. call nomod(#s),call nomod(#s),\
  1678. \\missingmod{s},\
  1679. %nomod(s),%nomod(s))");
  1680.  
  1681. SAVE_MACRO("$STUB(s)$IFCASE($LANGUAGE_NUM,\
  1682. {missing_mod(#s);},{missing_mod(#s);},\
  1683. call nomod(#s),call nomod(#s),\
  1684. call nomod(#s),call nomod(#s),\
  1685. \\missingmod{s},\
  1686. %nomod(s),%nomod(s))");
  1687.  
  1688. @ Here we build the tokenized text to make a call to a stub routine that
  1689. serves as the text of an undefined module.
  1690. @<Output a function call for debugging...@>=
  1691. {
  1692. #define TEMP_LEN 300
  1693.  
  1694. eight_bits temp[TEMP_LEN],temp1[TEMP_LEN];
  1695. sixteen_bits stub;
  1696. size_t n = (size_t)length(np);
  1697.  
  1698. id_first = x__to_ASCII(OC("$STUB"));
  1699. stub = ID_NUM(id_first,id_first+5);
  1700.  
  1701. STRNCPY(temp1,np->byte_start,n);
  1702. temp1[n] = '\0';
  1703.  
  1704. SPRINTF(TEMP_LEN,temp,`"%c%c%c%c%s%c%c",
  1705.     LEFT(stub,ID0),RIGHT(stub),@'(',stringg,temp1,stringg,@')'`);
  1706. push_level(NULL,temp,temp+STRLEN(temp));
  1707.  
  1708. #undef TEMP_LEN
  1709. }
  1710.  
  1711. @ Interface to \.{rat77.web}.
  1712. @<Part 1@>=@[
  1713.  
  1714. SRTN x_mod_a FCN((a))
  1715.     sixteen_bits a C1("")@;
  1716. {
  1717. @<Expand module |a...@>@;
  1718. }
  1719.  
  1720. @* PRODUCING the OUTPUT.  The |get_output| routine above handles most of
  1721. the complexity of output generation, but there is one further consideration
  1722. that has a nontrivial effect on \.{TANGLE}'s algorithms.  Namely, we want
  1723. to make sure that the output has spaces and line breaks in the right places
  1724. (e.g., not in the middle of a string or a constant or an identifier, not at
  1725. a `\.{@@\&}' position where quantities are being joined together, and, if
  1726. in the C~language, certainly after a `\.=' because the C compiler thinks
  1727. `\.{=-}' is ambiguous).
  1728.  
  1729. The output process can be in one of following states (which are |enum|ed in
  1730. \.{typedefs.web}): 
  1731.  
  1732. \yskip\hang |NUM_OR_ID| means that the last item in the buffer is a number or
  1733. identifier, hence a blank space or line break must be inserted if the next
  1734. item is also a number or identifier.
  1735.  
  1736. \yskip\hang |UNBREAKABLE| means that the last item in the buffer was followed
  1737. by the \.{@@\&}~operation that inhibits spaces between it and the next item.
  1738.  
  1739. \yskip\hang |VERBATIM| means we're copying only character tokens, and
  1740. that they are to be output exactly as stored.  This is the case during
  1741. strings, verbatim constructions and numerical constants.
  1742.  
  1743. \yskip\hang |MISCELLANEOUS| means none of the above.
  1744.  
  1745. \yskip Furthermore, if the variable |protect| is positive, new-lines
  1746. are preceded by the value of the style-file field |protect|.
  1747.  
  1748. @<Global...@>=
  1749.  
  1750. EXTERN OUTPUT_STATE out_state; // Current status of partial output.
  1751. EXTERN boolean protect; // Current status of partial output.
  1752. EXTERN boolean copying_macros SET(NO); // Outputting outer macros?
  1753. EXTERN boolean in_cdir SET(NO); // Inside a compiler directive?
  1754.  
  1755. @ Here is a routine that is invoked when we want to output the current line.
  1756. During the output process, |cur_line| equals the number of the next line
  1757. to be output. This variable counts the total number of lines that have been
  1758. output. However, this is not useful for error messages when more than one
  1759. file are open. Thus, we introduce an array |outp_line| of current lines
  1760. that keeps track of what's going on in each individual language.  The
  1761. output line number for the current language is accessed by the macro
  1762. |OUTPUT_LINE|. 
  1763.  
  1764. @d flush_buffer() C_putc('\n')
  1765.  
  1766. @<Part 1@>=@[
  1767.  
  1768. SRTN flush0()
  1769. {
  1770. /* This routine might be called during phase~1, because error messages use
  1771. the output buffering routines.  However, we don't want to update
  1772. |cur_line|, which counts the input lines during phase~1. */
  1773. if(phase==1) return;
  1774.  
  1775. /* Give some feedback to the terminal by printing a dot every so often, and
  1776. the line number somewhat less often. */
  1777. if (cur_line % 100 == 0) 
  1778.     {
  1779.     if (cur_line % 500 == 0) {CLR_PRINTF(line_num,("%u",cur_line));}
  1780.     else putchar('.');
  1781.  
  1782.     UPDATE_TERMINAL; // Progress report.
  1783.     }
  1784.  
  1785. cur_line++;
  1786. OUTPUT_LINE++;
  1787. }
  1788.  
  1789. @* The BIG OUTPUT SWITCH.  Here then is the routine that does the
  1790. output:
  1791.  
  1792. @<Part 1@>=@[
  1793.  
  1794. SRTN phase2(VOID) 
  1795. {
  1796. phase = 2;
  1797.  
  1798. params = global_params;
  1799. frz_params();
  1800. set_output_file(global_language);
  1801.  
  1802. /* Get the FORTRAN output buffer ready. */
  1803. rst_out(NOT_CONTINUATION);
  1804.  
  1805. CLR_PRINTF(info,("\nWriting the %soutput file(s):",
  1806.     compare_outfiles ? "temporary " : "")); 
  1807. printf("  ");
  1808. UPDATE_TERMINAL;
  1809.  
  1810. cur_line = 1;
  1811.  
  1812. if (CAST(text_pointer,text_info)->text_link==0)
  1813.     { /* There was no program text. */
  1814.     CLR_PRINTF(warning, ("\n! No program text was specified.")); 
  1815.     mark_harmless;
  1816. @.No output was specified@>
  1817.       }
  1818. else 
  1819.     { /* There is program text. */
  1820.     @<Truncate identifiers@>;
  1821.  
  1822.     @<Initialize the output stacks@>;
  1823.     @<Output macro definitions@>;
  1824.  
  1825.     @<Initialize the output stacks@>;
  1826.  
  1827.     while(get_output())
  1828.         ; // Process each character of the output.
  1829.  
  1830.     flush_buffer();
  1831.  
  1832.     if(compare_outfiles)
  1833.         cmp_outfiles(); // Compare tangled output against old files.
  1834.  
  1835.     CLR_PRINTF(info,("\nDone."));
  1836.     }
  1837. }
  1838.  
  1839. @ The command line is written out at the very beginning of the output file 
  1840. as a meta-comment.
  1841. @<Part 1@>=@[
  1842. SRTN out_version FCN((msg))
  1843.     CONST outer_char *msg C1("")@;
  1844. {
  1845. outer_char HUGE *temp = GET_MEM("version:temp",N_MSGBUF,outer_char);
  1846. boolean in_string0 = in_string;
  1847. OUTPUT_STATE out_state0 = out_state;
  1848.  
  1849. SPRINTF(N_MSGBUF,temp,
  1850.     `"  FTANGLE v%s, created with %s on \"%s, %s at %s.\" %s\n",
  1851.     $VERSION,the_system,$DAY,$DATE,$TIME,local_banner`);
  1852. STRCAT(temp,cmd_ln_buf);
  1853. STRCAT(temp,msg); // Possible \.{@@o} continuation message.i
  1854. in_version = YES;
  1855. OUT_MSG(to_ASCII(temp),NULL);
  1856. FREE_MEM(temp,"version:temp",N_MSGBUF,outer_char);
  1857.  
  1858. in_version = NO;
  1859. in_string = in_string0;
  1860. out_state = out_state0;
  1861.  
  1862. if(line_info) 
  1863.     out_pos = 0;
  1864. else 
  1865.     {
  1866.     started_vcmnt = NO;
  1867.     rst_out(NOT_CONTINUATION);
  1868.     }
  1869. }
  1870.  
  1871. @ The version number is defined as the string |version| in \.{common.web}.
  1872. @<Define internal macros@>=
  1873.  
  1874. SAVE_MACRO("_VERSION $STRING($$VERSION)");
  1875. SAVE_MACRO("$VERSION $STRING($$VERSION)");
  1876.  
  1877. @ This internal function just puts the version number into the |macro_buf|.
  1878. @<Part 1@>=@[
  1879.  
  1880. SRTN i_version_ FCN((n,pargs))
  1881.     int n C0("")@;
  1882.     PARGS pargs C1("")@;
  1883. {
  1884. CHK_ARGS("$VERSION",0);
  1885.  
  1886. mcopy(version);
  1887. }
  1888.  
  1889. @ Here are the various time and date macros.
  1890.  
  1891. @m __DAY 0
  1892. @m __DATE 1
  1893. @m __TIME 2
  1894.  
  1895. @m STORE_TIME(macro,i)STORE_TIME0(#!macro $TM(i))
  1896. @m STORE_TIME0(s)SAVE_MACRO(#s)
  1897.  
  1898. @<Define internal macros@>=
  1899.  
  1900. STORE_TIME(_DAY,__DAY);
  1901. STORE_TIME(_DATE,__DATE);
  1902. STORE_TIME(_TIME,__TIME);
  1903.  
  1904. STORE_TIME($DAY,__DAY);
  1905. STORE_TIME($DATE,__DATE);
  1906. STORE_TIME($TIME,__TIME);
  1907.  
  1908. SAVE_MACRO("_TM(i)$STRING($$TM(i))");
  1909. SAVE_MACRO("$TM(i)$STRING($$TM(i))");
  1910.  
  1911. @ The date and time functions use the ANSII standard routines.
  1912. @<Part 1@>=@[
  1913.  
  1914. SRTN i_tm_ FCN((n,pargs))
  1915.     int n C0("")@;
  1916.     PARGS pargs C1("")@;
  1917. {
  1918. eight_bits HUGE *p;
  1919. struct tm *t;
  1920.  
  1921. CHK_ARGS("$TM",1);
  1922.  
  1923. p = pargs[0] + 1; // Should point to a single-digit constant.
  1924.  
  1925. if(*p++ != constant)
  1926.     {
  1927.     MACRO_ERR("! Argument of $TM must be numerical constant",YES);
  1928.     return;
  1929.     }
  1930.  
  1931. t = the_localtime(); // Fill the |tm| structure and return a pointer.
  1932.  
  1933. switch(*p - @'0') 
  1934.     { /* Convert digit to integer and select routine. */
  1935.     case __DAY:
  1936.         mcopy(the_day(t));
  1937.         break;
  1938.  
  1939.     case __DATE:
  1940. /* The date needs to be protected because of the comma. */
  1941.         MCHECK(2,"the_cdate");
  1942.         *mp++ = @'`';
  1943.         mcopy(the_cdate(t));
  1944.         *mp++ = @'`';
  1945.         break;
  1946.  
  1947.     case __TIME:
  1948.         mcopy(the_time(t));
  1949.         break;
  1950.  
  1951.     default:
  1952.         MACRO_ERR("! Invalid case in _tm_",YES);
  1953.         break;
  1954.     }
  1955. }
  1956.  
  1957. @ Here is a simple routine that copies an |outer_char| string into the
  1958. |macro_buf|, converting to |ASCII| as it does so.
  1959. @<Part 1@>=@[
  1960.  
  1961. SRTN mcopy FCN((s))
  1962.     CONST outer_char *s C1("")@;
  1963. {
  1964. int n = STRLEN(s);
  1965.  
  1966. MCHECK(n,"mcopy");
  1967. STRCPY(mp,x_to_ASCII(s));
  1968. mp += n;
  1969. }
  1970.  
  1971. @ First we go through the list of replacement texts and copy to the output
  1972. the macros that were defined by~\.{@@d}. These will be preceded by the
  1973. preprocesor \.{define} command appropriate for the language of that macro.
  1974.  
  1975. For the future, we really ought to have a mechanism that starts this list
  1976. after some position in the file that may not be the top.  That way, 
  1977.  
  1978. @<Output macro def...@>= 
  1979. @{
  1980. sixteen_bits a;
  1981. text_pointer cur_text;
  1982. boolean is_def;
  1983.  
  1984. @b
  1985. copying_macros = YES;
  1986.  
  1987. for (cur_text=text_info+1; cur_text<text_ptr; cur_text++)
  1988.   if (cur_text->text_link==macro) 
  1989.     { /* |cur_text| is the text for a macro */
  1990.         cur_byte=cur_text->tok_start;
  1991.         cur_end=(cur_text+1)->tok_start;
  1992.  
  1993.     is_WEB_macro = 
  1994.         BOOLEAN(!((is_def=BOOLEAN(cur_text->nargs==OUTER_MACRO)) ||
  1995.             cur_text->nargs==OUTER_UNMACRO)); /* Check special
  1996. marker set on input. */
  1997.  
  1998.     if(is_WEB_macro)
  1999.         {
  2000. #if(0)
  2001.         see_macro(cur_byte,cur_end) /* For debugging. */
  2002. #endif
  2003.         ;}
  2004.     else 
  2005.         @<Copy outer macro.@>@;
  2006.     }
  2007.  
  2008. copying_macros = NO;
  2009. }
  2010.  
  2011. @ Here we copy the non-WEB ``outer'' macros to the output. At the moment,
  2012. these always go to the very top of the output. This is not always
  2013. convenient, and someday we'll generalize.
  2014.  
  2015. @<Copy outer...@>=
  2016. {
  2017. LANGUAGE language0;
  2018. T_OUTER *po = &t_style.outer_start[lan_num(language)];
  2019. outer_char *outer_macro;
  2020.  
  2021. out_state = MISCELLANEOUS;
  2022.  
  2023. set_output_file((LANGUAGE)cur_text->Language); /* Set the language for this
  2024.                         outer macro.  */
  2025.  
  2026. protect = YES; // New-lines should be preceded by the protection character.
  2027.  
  2028. outer_macro = OC(is_def ? po->def : po->undef);
  2029. language0 = language;
  2030.  
  2031. C_sprintf(outer_macro,0);
  2032.  
  2033. stck_ptr = stack;
  2034. push_level(NULL,cur_byte,cur_end);
  2035.  
  2036. WHILE()
  2037.     @<Write one outer macro@>@;
  2038.  
  2039. set_output_file(language0);
  2040.  
  2041. protect = NO;
  2042. flush_buffer();
  2043. }
  2044.  
  2045. @
  2046. @<Write one outer macro@>=
  2047. {
  2048. if(DONE_LEVEL && !pop_level()) break;
  2049.  
  2050. a = *cur_byte++;
  2051.  
  2052. if (cur_byte==cur_end && a==@'\n') 
  2053.     continue;    // disregard a final new-line
  2054.  
  2055. if(TOKEN1(a)) // |in_string|??
  2056.     @<Send a single-byte token...@>@;
  2057. else 
  2058.     {
  2059.     a = IDENTIFIER(a,*cur_byte++);
  2060.  
  2061.         if (a<MODULE_NAME) 
  2062.         {
  2063.             cur_val=a; 
  2064.         OUT_CHAR(identifier);// Outer macro text will be expanded here.
  2065.             }
  2066.         else if (a!=MODULE_NUM) 
  2067.         {
  2068.         CONFUSION("copy outer","Macros defs have strange char");
  2069.         }
  2070.     else 
  2071.         {
  2072.             cur_mod = (sixteen_bits)(a - MODULE_NUM);
  2073.         cur_val = (long)cur_mod;
  2074.         OUT_CHAR(module_number); 
  2075.             }
  2076.     /* no other cases */
  2077.         }
  2078. }
  2079.  
  2080. @ If the switch |truncate_ids| is on, then we go through the list of
  2081. identifiers, strip off selected characters, and maybe truncate them.
  2082. (The code for truncating identifiers isn't completed in version~1.)
  2083.  
  2084. @<Truncate id...@>=
  2085. {
  2086. name_pointer np;
  2087.  
  2088. npmax = name_ptr - 1; // Used in output routine.
  2089.  
  2090. if(truncate_ids)
  2091.     {
  2092.     unsigned n = 0; // Counts number of truncations.
  2093.  
  2094.     printf("\nTruncating %u identifiers...",
  2095.         PTR_DIFF(unsigned, name_ptr, name_dir));
  2096.     
  2097.     for(np=name_dir+1; np<name_ptr; np++)
  2098.         n += trunc_id(np);
  2099.  
  2100.     printf("\n%u truncation(s) performed.",n);
  2101.     }
  2102.  
  2103. not_unique(); // Print non-unique identifiers.
  2104. }
  2105.  
  2106. @ Check for duplicate identifiers.
  2107.  
  2108. @d NEWLINE puts("")
  2109.  
  2110. @f TRUNC int
  2111. @f BP int
  2112.  
  2113. @<Part 1@>=@[
  2114.  
  2115. SRTN not_unique(VOID)
  2116. {
  2117. TRUNC HUGE *s,HUGE * HUGE *ss,HUGE * HUGE *ss0,HUGE * HUGE *ss1;
  2118. LANGUAGE Language;
  2119. int l;
  2120. size_t n; // Counts number of non-unique variables.
  2121. size_t num_max; // Maximum \# of roots for any duplicate.
  2122. BP HUGE * HUGE *bb0;
  2123. boolean found_dup = NO;
  2124.  
  2125. for(l=0; l<NUM_LANGUAGES; l++)
  2126.     {
  2127.     Language = lan_enum(l);
  2128.  
  2129. /* Count the number of duplicate variables. */
  2130.     n = 0;
  2131.  
  2132.     for(s=&sh; s->next; s=s->next)
  2133.         {
  2134.         if(!((boolean)s->Language & (boolean)Language)) continue;
  2135.  
  2136.         if(s->num[l] > 1)
  2137.             {
  2138.             char temp[10];
  2139.             unsigned len = tr_max[l];
  2140.  
  2141.             sprintf(temp,len ? "%u" : "*",len);
  2142.  
  2143.             if(n==0) 
  2144.                 {
  2145.                 printf("\n\n%c! Non-unique \
  2146. %s variables (filtered with {%s}, truncated to length %s):",
  2147.                 beep(1),languages[l],filter_char[l],temp);
  2148.                 found_dup = YES;
  2149.                 }
  2150.             n++;
  2151.             }
  2152.         }
  2153.  
  2154.     if(n == 0) continue;
  2155.  
  2156. /* Store the pointers to the duplicates in an array. */
  2157.     ss1 = ss0 = ss = GET_MEM("ss",n,TRUNC HUGE *);
  2158.     num_max = 0;
  2159.  
  2160.     for(s=&sh; s->next; s=s->next)
  2161.         {
  2162.         if(!((boolean)s->Language & (boolean)Language)) continue;
  2163.  
  2164.         if(s->num[l] > 1)
  2165.             {
  2166.             *ss++ = s;
  2167.             num_max = MAX(num_max,s->num[l]);
  2168.             }
  2169.         }
  2170.  
  2171. /* Sort the array. */
  2172.     QSORT(ss0,n,sizeof(TRUNC HUGE *),cmpr_trunc);
  2173.  
  2174. /* Print out the sorted array. */
  2175.     bb0 = GET_MEM("bb",num_max,BP HUGE *);
  2176.  
  2177.     while(ss1 < ss)
  2178.         see_dup(*ss1++,Language,bb0);
  2179.  
  2180.     FREE_MEM(ss0,"ss",n,TRUNC HUGE *);
  2181.     FREE_MEM(bb0,"bb",num_max,BP HUGE *);
  2182.     }
  2183.  
  2184. if(found_dup)
  2185.     NEWLINE;
  2186. }
  2187.  
  2188. SRTN see_dup FCN((s,Language,bb0))
  2189.     CONST TRUNC HUGE *s C0("")@;
  2190.     LANGUAGE Language C0("")@;
  2191.     BP HUGE *HUGE *bb0 C1("")@;
  2192. {
  2193. BP HUGE *b, HUGE * HUGE *bb, HUGE * HUGE *bb1;
  2194. int n;
  2195.  
  2196. NEWLINE;
  2197. printf(" "); 
  2198. n = see(s->id,s->id_end); // The truncated id.
  2199.  
  2200. /* Space it out so it looks nicely lined up. */
  2201. for(n = tr_max[lan_num(Language)] + 1 - n; n > 0; n--) printf(" ");
  2202. printf("<=");
  2203.  
  2204. /* Print all back references to original variables. */
  2205. for(b=s->first,bb=bb0; b != NULL; b=b->next)
  2206.     {
  2207.     if(!((boolean)b->Language & (boolean)Language)) continue;
  2208.  
  2209.     *bb++ = b;
  2210.     }
  2211.  
  2212. QSORT(bb0,bb-bb0,sizeof(BP HUGE *),cmpr_bp);
  2213.  
  2214. for(bb1=bb0; bb1<bb; bb1++)
  2215.     {
  2216.     printf(" ");    
  2217.     see((*bb1)->byte_start,(*bb1)->byte_end);
  2218.     }
  2219. }
  2220.  
  2221. int see FCN((c0,c1))
  2222.     CONST ASCII HUGE *c0 C0("Beginning.")@;
  2223.     CONST ASCII HUGE *c1 C1("end.")@;
  2224. {
  2225. int n = PTR_DIFF(int, c1, c0);
  2226.  
  2227. while(c0 < c1) printf("%c",XCHR(*c0++));
  2228.  
  2229. return n; // Length of identifier.
  2230. }
  2231.  
  2232. @
  2233. @<Part 1@>=@[
  2234.  
  2235. int cmpr_trunc FCN((t0,t1))
  2236.     TRUNC HUGE **t0 C0("")@;
  2237.     TRUNC HUGE **t1 C1("")@;
  2238. {
  2239. switch(web_strcmp((*t0)->id,(*t0)->id_end,(*t1)->id,(*t1)->id_end))
  2240.     {
  2241.    case EQUAL:
  2242.     return 0;
  2243.  
  2244.    case LESS:
  2245.    case PREFIX:
  2246.     return -1;
  2247.  
  2248.    case GREATER:
  2249.    case EXTENSION:
  2250.     return 1;
  2251.     }
  2252.  
  2253. return 0;
  2254. }
  2255.  
  2256. int cmpr_bp FCN((bb0,bb1))
  2257.     BP HUGE **bb0 C0("")@;
  2258.     BP HUGE **bb1 C1("")@;
  2259. {
  2260. switch(web_strcmp((*bb0)->byte_start,(*bb0)->byte_end,
  2261.         (*bb1)->byte_start,(*bb1)->byte_end))
  2262.     {
  2263.    case EQUAL:
  2264.     return 0;
  2265.  
  2266.    case LESS:
  2267.    case PREFIX:
  2268.     return -1;
  2269.  
  2270.    case GREATER:
  2271.    case EXTENSION:
  2272.     return 1;
  2273.     }
  2274.  
  2275. return 0;
  2276. }
  2277.  
  2278. @i trunc.hweb
  2279.  
  2280. @ Define the first truncation structure.
  2281. @<Glob...@>=
  2282.  
  2283. EXTERN TRUNC sh;
  2284.  
  2285. @ Attach a back-pointer structure to a |TRUNC| structure.
  2286. @<Part 1@>=@[
  2287.  
  2288. BP HUGE *b_link FCN((s,Language,p0,p1))
  2289.     TRUNC HUGE *s C0("")@;
  2290.     LANGUAGE Language C0("")@;
  2291.     CONST ASCII HUGE *p0 C0("")@;
  2292.     CONST ASCII HUGE *p1 C1("")@;
  2293. {
  2294. BP HUGE *bp;
  2295.  
  2296. bp = GET_MEM("bp",1,BP); /* Get a back-pointer structure. */
  2297.  
  2298. bp->c = BP_MARKER;
  2299.  
  2300. /* Remember language of original variable. */
  2301. bp->Language = Language;
  2302.  
  2303. /* Record start and end of the original name. */
  2304. bp->byte_start = p0;
  2305. bp->byte_end = p1;
  2306.  
  2307. /* Link back to original |TRUNC| structure. */
  2308. bp->Root = s;
  2309. s->Language |= (boolean)Language;
  2310. s->num[lan_num(Language)]++; /* Count hits for this language. */
  2311.  
  2312. return bp;
  2313. }
  2314.  
  2315. @
  2316. Attach a |TRUNC| structure to the chain of truncated ids.
  2317. @<Part 1@>=@[
  2318.  
  2319. TRUNC HUGE *s_link FCN((s,id,len))
  2320.     TRUNC HUGE *s C0("Points to the current structure, to be \
  2321. filled with info.")@;
  2322.     CONST ASCII HUGE *id C0("Truncated identifier.")@;
  2323.     unsigned short len C1("Length of truncated identifier.")@;
  2324. {
  2325. /* Fill this structure with truncated variable name. */
  2326. s->id = GET_MEM("s->id",len,ASCII); // Space for name.
  2327. STRNCPY(s->id,id,len); // Copy over name.
  2328. s->id_end = s->id + len; // End of name.
  2329.  
  2330. /* Attach another (uninitialized) structure. */
  2331. s->next = GET_MEM("s->next",1,TRUNC);
  2332.  
  2333. return s; 
  2334. }
  2335.  
  2336. @ Search for identifier in table.
  2337. @<Part 1@>=@[
  2338.  
  2339. name_pointer id0_lookup FCN((start,end,l))
  2340.     CONST ASCII HUGE *start C0("Start of name.")@;
  2341.     CONST ASCII HUGE *end C0("end of name.")@;
  2342.     LANGUAGE l C1("")@;
  2343. {
  2344. name_pointer np;
  2345. CONST ASCII HUGE *p0, HUGE *p1;
  2346.  
  2347. for(np=name_dir+1; np<name_ptr; np++)
  2348.     {
  2349.     if(!(np->Language & (boolean)l) ||
  2350.         np->equiv != NULL || *(p0=np->byte_start) == BP_MARKER)
  2351.             continue; 
  2352.  
  2353.     PROPER_END(p1);
  2354.  
  2355.     if(web_strcmp(p0,p1,start,end) == EQUAL) 
  2356.         return np;
  2357.     }
  2358.  
  2359. return NULL;
  2360. }
  2361.  
  2362. @ Test if a character~|c| is valid for an identifier in the $l$th~language.
  2363. @<Unused@>=
  2364.  
  2365. boolean valid_char FCN((c,l))
  2366.     ASCII c C0("Character to be tested.")@;
  2367.     int l C1("Language index.")@;
  2368. {
  2369. return BOOLEAN(STRCHR(filter_char[l],(int)XCHR(c)) == NULL);
  2370. /* If the character isn't a filter character, we return |YES|. */
  2371. }
  2372.  
  2373. @ Truncate an identifier.
  2374. @<Part 1@>=@[
  2375.  
  2376. unsigned trunc_id FCN((np0))
  2377.     CONST name_pointer np0 C1("Points to current id structure.")@;
  2378. {
  2379. CONST ASCII HUGE *p, HUGE *p0, HUGE *p1; // For original identifier.
  2380. ASCII temp[N_IDBUF];
  2381. ASCII HUGE *t; // For truncated identifier.
  2382. unsigned short n; // Length of truncated identifier.
  2383. TRUNC HUGE *s;
  2384. name_pointer np;
  2385. unsigned short nmax; // Truncate to this length.
  2386. LANGUAGE Language;
  2387. int l;
  2388. unsigned count = 0;
  2389.  
  2390. if(np0->Language == (boolean)NO_LANGUAGE || np0->equiv != NULL) 
  2391.     return 0;
  2392.  
  2393. for(l=0; l<NUM_LANGUAGES; l++)
  2394.  {
  2395. Language = lan_enum(l);
  2396. np = np0;
  2397.  
  2398. /* Don't bother with it if there's no truncation specified for this
  2399. language, if it's not in use for this language, if it's a reserved word,
  2400. intrinsic word, or keyword, or if it's a \WEB\ macro. */
  2401.  if( (nmax = tr_max[l]) == 0 || !(np->Language & (boolean)Language)
  2402.     || (np->reserved_word & (boolean)Language)
  2403.     || (np->intrinsic_word & (boolean)Language)
  2404.     || (np->keyword & (boolean)Language)
  2405.     || (np->macro_type != NOT_DEFINED) )
  2406.         continue; 
  2407.  
  2408. /* The original name. */
  2409. p0 = np->byte_start;
  2410.  
  2411. if(*p0 == BP_MARKER) 
  2412.     continue; /* NEED MORE WORK HERE (variable already deflected). */
  2413.  
  2414. PROPER_END(p1);
  2415.  
  2416. /* Filter. */
  2417. for(p=p0,t=temp,n=0; p<p1 && n<nmax; p++)
  2418.     if(STRCHR(filter_char[l],(int)XCHR(*p)) == NULL)
  2419.         {
  2420.         n++;
  2421.         *t++ = *p;
  2422.         }
  2423.  
  2424. n = PTR_DIFF(unsigned short, t, temp); // Length of truncated identifier.
  2425.  
  2426. if(p1-p0 == (long)n) 
  2427.     continue; // Not truncated; nothing to do.
  2428.  
  2429. count++; // Count number of truncations for this identifier.
  2430.  
  2431. /* Is the truncated name already in the list? */
  2432. for(s= &sh; s->next != NULL; s=s->next)
  2433.     if(s->id_end - s->id == (long)n &&
  2434.             web_strcmp(s->id,s->id_end,temp,t) == EQUAL)
  2435.         {
  2436.     another_bp:
  2437.         s->last = s->last->next = b_link(s,Language,p0,p1); 
  2438. /* Remember the original variable by attaching another back reference. */
  2439.         np->byte_start = (ASCII *)s->last; // Deflect original ptr.
  2440.         goto next_language;
  2441.         }
  2442.  
  2443. /* Add a new name to the list. */
  2444. s = s_link(s,temp,n);
  2445. s->first = s->last = b_link(s,Language,p0,p1); // Attach first back reference.
  2446. np->byte_start = (ASCII *)s->first; // Deflect original ptr.
  2447.  
  2448. /* If the truncated name was in the original list, not previously truncated
  2449. from something else, put the original name into the truncated list. */
  2450. if( (np = id0_lookup(temp,t,(LANGUAGE)np->Language)) != NULL) 
  2451.     {
  2452.     p0 = np->byte_start; PROPER_END(p1);
  2453.     goto another_bp;
  2454.     }
  2455.  
  2456. next_language:;
  2457.  }
  2458.  
  2459. return count;
  2460. }
  2461.  
  2462. @ Here are some flags used in the output routine |out_char|.
  2463. @<Glob...@>=
  2464.  
  2465. EXTERN boolean mac_protected SET(NO); /* Are we between left quotes, so macros
  2466.                 shouldn't be expanded? */
  2467. EXTERN boolean send_rp SET(NO); /* Takes on a value only for |language ==
  2468.     RATFOR || language==FORTRAN|, when it's used to enclose the rhs of
  2469.     an operator like \.{*=}. */ 
  2470.  
  2471. EXTERN boolean in_version SET(NO); // For the initial header of output file.
  2472. EXTERN T_META *pmeta;
  2473.  
  2474. @ This fragment finishes off a~\.{*=} or similar operator by enclosing the
  2475. right-hand side expressions in parentheses.
  2476. @<Maybe send a right parenthesis@>=
  2477.  
  2478. if(send_rp)
  2479.     {
  2480.     C_putc(')'); // Not |buffer_out| because of \.{-)}.
  2481.     send_rp = NO; // Clear the flag.
  2482.     }
  2483.  
  2484. @ A many-way switch, |out_char()|, is used to send the output. Because of
  2485. macro expansion, this routine needs to be  recursive. It performs a variety
  2486. of actions, including inserting spaces at desired places (such as after
  2487. equals and between identifiers), translating internal codes to their
  2488. visible representations such as~\.{++}, etc.
  2489.  
  2490. @<Part 2@>=@[
  2491.  
  2492. eight_bits out_char FCN((cur_char))
  2493.     eight_bits cur_char C1("Token to control or be sent to the output.")@;
  2494. {
  2495. switch(cur_char) 
  2496.     {
  2497.    case ignore: 
  2498.     if(R77_or_F && started_vcmnt) C_putc(cur_char);
  2499.     return @' '; /* KLUDGE  to prevent |get_output| from being
  2500. terminated prematurely. */
  2501.  
  2502. /* In nuweb mode, tab is mapped to bell on input, and back again here. */
  2503.    case bell:
  2504.     return out_dflt(tab_mark);
  2505. @%   case bell: break; /* Bells go to the tty, but not the output file. */
  2506.  
  2507.    case @',':
  2508.     out_dflt(cur_char);
  2509.     @<Mark split position@>@;
  2510.     break;
  2511.  
  2512.    case interior_semi:
  2513.     if(!(Fortran88||in_string)) cur_char = @';'; 
  2514.         // Fall through to regular semicolon.
  2515.  
  2516.    case @';':
  2517.     @<Maybe send a right paren...@>;
  2518.     return out_dflt(cur_char);
  2519.  
  2520.    case cdir:
  2521.     in_cdir = BOOLEAN(!in_cdir);
  2522.  
  2523.     if(FORTRAN_LIKE(language))
  2524.         {
  2525.         in_string = NO;
  2526.         flush_buffer();
  2527.         in_string = YES;
  2528.         }
  2529.     break;
  2530.  
  2531.    case @'\n': 
  2532.     if((copying_macros || !nuweb_mode)
  2533.             && (protect || out_state==VERBATIM) ) 
  2534.         {
  2535. /* Outer macros are absorbed with no explicit backslash at end of line.
  2536. Furthermore, spaces are stripped from the start of the next line. 
  2537. Therefore, we will think of the end of line as a space.  Contrast this with
  2538. explicit \.{\#define}'s continued with a backslash, which just abuts the
  2539. last character of the line with the first character of the next one. */
  2540.         if(copying_macros && protect && !in_string)
  2541.             C_putc(' ');
  2542.  
  2543.         out_str(t_style.protect_chars[lan_num(language)]); 
  2544.             /* Backslash at end of line. */
  2545.         }
  2546.     @<Maybe send a right paren...@>;
  2547.     flush_buffer(); 
  2548.     if (out_state!=VERBATIM) 
  2549.         out_state = MISCELLANEOUS; 
  2550.     break;
  2551.  
  2552.    @t\4@>@<Case of an identifier@>;
  2553.    @t\4@>@<Case of a module number@>;
  2554.    @t\4@>@<Cases like \.{!=}@>;
  2555.    @t\4@>@<Cases like \.{+=}@>;
  2556.  
  2557.    case @'=': 
  2558.     C_putc('='); 
  2559.  
  2560.     if (out_state!=VERBATIM) 
  2561.         {
  2562.         if(C_LIKE(language) && !nuweb_mode) 
  2563.             C_putc(' '); // Space after ambiguous character.
  2564.  
  2565.         out_state = MISCELLANEOUS;
  2566.         }
  2567.  
  2568.     @<Mark split position@>@;
  2569.     break;
  2570.  
  2571.    case join: out_state = UNBREAKABLE; break; 
  2572.  
  2573.    case constant: 
  2574.     if (out_state==VERBATIM) 
  2575.         out_state= in_format ? MISCELLANEOUS : NUM_OR_ID; 
  2576.             // End of constant.
  2577.     else
  2578.         { /* Beginning of constant. */
  2579.         @<Mark split...@>@;
  2580.  
  2581.             if(out_state==NUM_OR_ID && !nuweb_mode) 
  2582.             C_putc(' '); 
  2583.  
  2584.         out_state = VERBATIM; 
  2585.         }
  2586.  
  2587.     in_constant = BOOLEAN(!in_constant);
  2588.     break;
  2589.  
  2590.    case stringg: 
  2591.     if(in_string) 
  2592.         out_state = MISCELLANEOUS; // End of string.
  2593.         else 
  2594.         { /* Begining of string. */
  2595.         @<Mark split...@>@;
  2596.  
  2597.         if(out_state == NUM_OR_ID && !nuweb_mode) 
  2598.             C_putc(' '); /* Strings after
  2599. identifiers can happen in macro definitions. */
  2600.  
  2601.         out_state = VERBATIM;
  2602.         }
  2603.  
  2604.     in_string = BOOLEAN(!in_string);
  2605.     break;
  2606.  
  2607.    case begin_meta: 
  2608. /* If there are two |begin_meta|s in a row, the second one means to turn
  2609. off the |xpn_Ratfor| flag, which among other things is used to control the
  2610. spacing after the comment character in \Fortran\ output. */
  2611.     pmeta = &t_style.meta[lan_num(language)];
  2612.  
  2613.     switch(language)
  2614.         {
  2615.         outer_char *t;
  2616.  
  2617.        case C:
  2618.        case C_PLUS_PLUS:
  2619.        case LITERAL:
  2620.        case TEX:
  2621.         if(meta_mode) 
  2622.             break;
  2623.  
  2624.         if(!nuweb_mode)
  2625.             {
  2626.             if(in_string && !in_version)
  2627.                 OUT_STR(t=pmeta->msg.top);
  2628.             else 
  2629.                 OUT_OP(t=pmeta->hdr.top);
  2630.  
  2631.             if(*t) 
  2632.                 OUT_STR("\n"); // Necessary????
  2633.             }
  2634.         meta_mode = YES;
  2635.         break;
  2636.  
  2637.        case RATFOR:
  2638.        case RATFOR_90:
  2639.        case FORTRAN:
  2640.        case FORTRAN_90:
  2641.         if(meta_mode) 
  2642.             xpn_Ratfor = NO;
  2643.          C_putc(cur_char);
  2644.         out_state = MISCELLANEOUS;
  2645.         break;
  2646.    
  2647.        default: 
  2648.         CONFUSION("out_char:begin_meta","Language not defined");
  2649.         }
  2650.     break;
  2651.  
  2652.    case end_meta:
  2653.     meta_mode = NO;
  2654.  
  2655.     switch(language)
  2656.         {
  2657.         outer_char *t;
  2658.  
  2659.        case C:
  2660.        case C_PLUS_PLUS:
  2661.        case LITERAL:
  2662.        case TEX:
  2663.         if(meta_mode) break;
  2664.  
  2665.         if(!nuweb_mode)
  2666.             {
  2667.             if(in_string && !in_version)
  2668.                 OUT_OP(t=pmeta->msg.bottom);
  2669.             else 
  2670.                 OUT_OP(t=pmeta->hdr.bottom);
  2671.  
  2672.             if(*t) OUT_OP("\n"); // Necessary????
  2673.             }
  2674.         break;
  2675.  
  2676.        case RATFOR:
  2677.        case RATFOR_90:
  2678.        case FORTRAN:
  2679.        case FORTRAN_90:
  2680.         xpn_Ratfor = YES;
  2681.         C_putc(cur_char);
  2682.         out_state = MISCELLANEOUS;
  2683.         break;
  2684.     
  2685.        default: 
  2686.         CONFUSION("out_char:end_meta","Language not defined");
  2687.         }
  2688.  
  2689.     break;
  2690.         
  2691.    case @'{':
  2692.     if(R77 && !in_string) 
  2693.         @<Copy function body@>@;
  2694.     else 
  2695.         {
  2696.         @<Mark split...@>@;
  2697.         return out_dflt(cur_char);
  2698.         }
  2699.     break;
  2700.  
  2701. /* The following doesn't work right when there's no |program| statement. */
  2702.    case @'}':
  2703.     {
  2704.     if(R77 && !in_string && brace_level==0) 
  2705.         RAT_ERROR(WARNING,"Spurious '}' ignored, \
  2706. or missing program, module, subroutine, or function statement",0); 
  2707.     else 
  2708.         {
  2709.         out_dflt(cur_char);
  2710.         @<Mark split...@>@;
  2711.         }
  2712.     }
  2713.  
  2714.     break;
  2715.  
  2716.    case @'[':
  2717.     out_bracket(cur_char,@'(');
  2718.     break;
  2719.  
  2720.    case @']':
  2721.     out_bracket(cur_char,@')');
  2722.     break;
  2723.  
  2724.  
  2725.    case @'`':
  2726.     if(!(in_string || language==LITERAL))
  2727.         {
  2728.         mac_protected = BOOLEAN(!mac_protected);
  2729.         break;
  2730.         }
  2731.     else 
  2732.         return out_dflt(cur_char);
  2733.  
  2734.    case @'&':
  2735.     if(C_LIKE(language) && out_state != VERBATIM
  2736.             && *(pC_buffer-1) == '&' && !nuweb_mode) 
  2737.         C_putc(' '); // Handle the situation |x & &y|.
  2738.     @<Mark split...@>@;
  2739.     return out_dflt(cur_char);
  2740.  
  2741.    case @'\\': 
  2742.     if(R66) 
  2743.         cur_char = @'$'; /* Change octal constant to \Ratfor's
  2744. argument token. This is kludgy and obsolete. */ 
  2745.  
  2746.    default: 
  2747.     return out_dflt(cur_char);
  2748.     }
  2749.  
  2750. return cur_char;
  2751. }
  2752.  
  2753. @
  2754. @<Part 2@>=@[
  2755.  
  2756. eight_bits out_bracket FCN((cur_char,new_char))
  2757.     eight_bits cur_char C0("")@;
  2758.     eight_bits new_char C1("")@;
  2759. {
  2760. if(out_state != VERBATIM && FORTRAN_LIKE(language) && translate_brackets) 
  2761.     cur_char = new_char;
  2762. return out_dflt(cur_char);
  2763. }
  2764.  
  2765. @
  2766. @<Mark split...@>=
  2767. {
  2768. #if FANCY_SPLIT
  2769.     if(C_LIKE(language) && out_state!=VERBATIM)
  2770.         split_pos = pC_buffer;
  2771. #endif /* |FANCY_SPLIT| */
  2772. }
  2773.  
  2774. @ In \Ratfor-77 mode, when we sense an opening brace, we copy everything
  2775. between matched braces.
  2776. @<Copy function body@>=
  2777. {
  2778. cp_fcn_body(); /* See \.{rat77.web}. */
  2779. cur_char = 01;
  2780. }
  2781.  
  2782. @ Send a single character to the output.
  2783. @<Part 2@>=@[
  2784.  
  2785. eight_bits out_dflt FCN((c))
  2786.     eight_bits c C1("")@;
  2787. {
  2788. C_putc(XCHR(c)); 
  2789.  
  2790. if (out_state != VERBATIM) 
  2791.     out_state = MISCELLANEOUS;
  2792.  
  2793. return c;
  2794. }
  2795.  
  2796. @
  2797. @<Cases for appending a lan...@>=
  2798.  
  2799. case C: opn_output_file(C); @+ break;
  2800. case C_PLUS_PLUS: opn_output_file(C_PLUS_PLUS); @+ break;
  2801. case RATFOR: 
  2802.     if(!RAT_OK("(send_single)")) 
  2803.         CONFUSION("output default","Ratfor command during output");
  2804.     opn_output_file(RATFOR); 
  2805.     break;
  2806. case RATFOR_90:
  2807.     if(!RAT_OK("(send_single)")) 
  2808.         CONFUSION("output default","Ratfor command during output");
  2809.     opn_output_file(RATFOR_90); 
  2810.     break;
  2811. case FORTRAN: opn_output_file(FORTRAN); @+ break;
  2812. case FORTRAN_90: opn_output_file(FORTRAN_90); @+ break;
  2813. case TEX: opn_output_file(TEX); @+ break;
  2814. case LITERAL: opn_output_file(LITERAL); @+ break@;
  2815.  
  2816. @ When we switch languages, we must select the appropriate output file, and
  2817. set up any relevant parameters.
  2818.  
  2819. @<Part 2@>=@[
  2820.  
  2821. LANGUAGE set_output_file FCN((language0))
  2822.     LANGUAGE language0 C1("")@;
  2823. {
  2824. language = language0; // Set the input language.
  2825. ini0_language(); // Set up parameters (include the |out_language|).
  2826. out_file = params.OUT_FILE; // Output of \.{TANGLE}.
  2827.  
  2828. return language; // Return the input language.
  2829. }
  2830.  
  2831. @ The |set_output_file| routine doesn't open a file.  The following
  2832. function does, in response to a |begin_language| seen by |send_single|
  2833. during the output phase.
  2834.  
  2835. @<Part 2@>=@[
  2836.  
  2837. LANGUAGE opn_output_file FCN((language0))
  2838.     LANGUAGE language0 C1("")@;
  2839. {
  2840. set_output_file(language0);
  2841. flush_buffer();
  2842. open_out(OC(""), LOCAL_SCOPE);
  2843.  
  2844. return language;
  2845. }
  2846.  
  2847. @ Output files are opened only when necessary, during phase~2.
  2848.  
  2849. @d GLOBAL_SCOPE YES
  2850. @d LOCAL_SCOPE NO
  2851.  
  2852. @d CHECK_OPEN if(!out_file) open_out(OC(""), GLOBAL_SCOPE)@;
  2853.  
  2854. @<Part 1@>=@[
  2855.  
  2856. SRTN open_out FCN((msg,global_scope))
  2857.     CONST outer_char *msg C0("")@;
  2858.     boolean global_scope C1("")@;
  2859. {
  2860. boolean is_stdout = BOOLEAN(STRCMP(params.OUTPUT_FILE_NAME,"stdout") == 0);
  2861. boolean already_opened = NO;
  2862.  
  2863. if(is_stdout) 
  2864.     out_file = params.OUT_FILE = stdout;
  2865. else 
  2866.     {
  2867.     already_opened = was_opened(params.OUTPUT_FILE_NAME, global_scope,
  2868.                     NULL, &out_file);
  2869.  
  2870.     params.OUT_FILE = out_file; // Local output file.
  2871.  
  2872. /* Write header info to the newly opened file. (We don't write it for
  2873. |stdout|, because it clutters up the screen.) */ 
  2874.     if(top_version && !(already_opened || compare_outfiles)) 
  2875.         out_version(msg); 
  2876.     }
  2877.  
  2878. /* The first time a file is opened for a particular language, its |FILE|
  2879. pointer must be made global so it can be restored at the beginning of each
  2880. module.  (The name was already made global in |common_init|.) */ 
  2881. if(global_scope) 
  2882.     cur_global_params.OUT_FILE = global_params.OUT_FILE = out_file;
  2883.  
  2884. /* The first time a file is opened, write its name to the screen. */
  2885. if(!already_opened)
  2886.  {
  2887.  CLR_PRINTF(out_file,("(%s)%s", (char *)params.OUTPUT_FILE_NAME,
  2888.     is_stdout ? "\n" : ""));
  2889.  UPDATE_TERMINAL;
  2890.  }
  2891. }
  2892.  
  2893. @ Information about previously opened files is stored in a dynamically
  2894. allocated list.
  2895.  
  2896. @<Glob...@>=
  2897.  
  2898. EXTERN OPEN_FILE HUGE *open_file, HUGE *open_file_end, HUGE *last_file;
  2899. EXTERN BUF_SIZE num_files; // Allocated length of |open_file|.
  2900.  
  2901. @ A list of |open_files| needs to be in place before the command line is
  2902. scanned.  The initial allocation gets the default value.
  2903.  
  2904. @<Allocate initial tables@>=
  2905. {
  2906. ALLOC(OPEN_FILE,open_file,ABBREV(num_files),num_files,0);
  2907. last_file = open_file;
  2908. open_file_end = open_file + num_files;
  2909. }
  2910.  
  2911. @ After the command line has been scanned, we may want to reallocate this
  2912. table. 
  2913.  
  2914. @<Allocate dyn...@>=
  2915. {
  2916. BUF_SIZE cur_num = last_file - open_file; // Current size of list.
  2917.  
  2918. /* Obtain the new allocation size. */
  2919. alloc((outer_char *)ABBREV(num_files),(BUF_SIZE HUGE *)&num_files,
  2920.     sizeof(*open_file),-1);
  2921.  
  2922. /* Reallocate and reset parameters. */
  2923. open_file = (OPEN_FILE *)REALLOC(open_file,
  2924.         num_files*sizeof(OPEN_FILE), cur_num*sizeof(OPEN_FILE));
  2925. last_file = open_file + cur_num;
  2926. open_file_end = open_file + num_files;
  2927. }
  2928.  
  2929. @ Here we check if the output file about to be opened has already been
  2930. previously opened.  If not, we put it into the list.
  2931.  
  2932. The variable |pname| is used as a flag.  If it's |NULL|, the file is opened
  2933. if necessary.  Otherwise, a pointer to the previously allocated storage
  2934. area for the name is returned.
  2935.  
  2936. @<Part 1@>=@[
  2937.  
  2938. boolean was_opened FCN((file_name,global_scope,pname,pfile_ptr))
  2939.     CONST outer_char HUGE *file_name C0("")@;
  2940.     boolean global_scope C0("")@;
  2941.     outer_char HUGE * HUGE *pname C0("")@;
  2942.     FILE **pfile_ptr C1("")@;
  2943. {
  2944. OPEN_FILE HUGE *f;
  2945.  
  2946. if(!*file_name)
  2947.     { /* Take care of special cases called by |xpn_name|. */
  2948.     *pname = (outer_char HUGE *)"";
  2949.     *pfile_ptr = NULL;
  2950.     return NO;
  2951.     }
  2952.  
  2953. /* Is file already in the list of previously opened? */
  2954. for(f=open_file; f<last_file; f++)
  2955.     if(STRCMP(f->name,file_name)==0) 
  2956.         {
  2957.         if(pname) 
  2958.             { /* Just return (to |new_fname|) some information. */
  2959.             *pname = f->name;
  2960.             *pfile_ptr = f->ptr;
  2961.             return f->previously_opened;
  2962.             }
  2963.         else 
  2964.             goto open_it;
  2965.         }
  2966.  
  2967. @<Add a new file to the list@>@;
  2968.  
  2969. if(pname) 
  2970.     { /* File wasn't previously opened, and has now been added to list
  2971. of file names. */
  2972.     *pname = f->name;
  2973.     f->ptr = NULL;
  2974.     f->previously_opened = NO;
  2975.     f->global_scope = global_scope;
  2976.     }
  2977. else
  2978.     @<Possibly open the file@>@;
  2979.  
  2980. *pfile_ptr = f->ptr;
  2981. return f->previously_opened;
  2982. }
  2983.  
  2984. @
  2985. @<Add a new file...@>=
  2986. {
  2987. /* File not in list; is there room for more? */
  2988. if(last_file==open_file_end) 
  2989.     {
  2990.     OVERFLW("previously opened files",ABBREV(num_files));
  2991.     }
  2992.  
  2993. last_file->name = GET_MEM("last_file",STRLEN(file_name)+1,outer_char);
  2994. STRCPY(last_file->name,file_name);
  2995. last_file++;
  2996. }
  2997.  
  2998. @ |f|~is now pointing to the proper entry in the list.  We're ready to open
  2999. the file.  If the file is already open, its file pointer is non-null, so we
  3000. do nothing except set the |previously_opened| flag.  (This might have
  3001. already been turned on when a file with local scope was closed.)  If it was
  3002. previously opened, but is now closed (|f->ptr == NULL|), we open it into
  3003. append mode.  Otherwise, it has never been opened and we must create a new
  3004. file name and open it into write mode.
  3005.  
  3006. @<Possibly open the file@>=
  3007. {
  3008. open_it:
  3009.   f->previously_opened = BOOLEAN(f->previously_opened || (f->ptr != NULL));
  3010.  
  3011. if(f->previously_opened)
  3012.     { /* It might have been once opened, but then closed. */
  3013.     if(f->ptr == NULL)
  3014.         f->ptr = FOPEN(compare_outfiles ? f->tmp_name : f->name, "a");
  3015.     }
  3016. else    
  3017.     { /* File wasn't ever opened. */
  3018.     if(compare_outfiles)
  3019.         @<Actually write into a temporary file@>@;
  3020.     else
  3021.         f->ptr = FOPEN(f->name, "w");
  3022.  
  3023.     if(!(f->ptr))
  3024.         { /* Should upgrade this message. */
  3025.         FATAL(T, "\n!! Can't open output file ", file_name);
  3026.         }
  3027.     }
  3028. }
  3029.  
  3030. @ We do the following when |compare_outfiles == YES|.
  3031.  
  3032. @<Actually write into a temp...@>=
  3033. {
  3034. char *buffer;
  3035. IN_COMMON outer_char wbprefix[MAX_FILE_NAME_LENGTH];
  3036.  
  3037. #if(HAVE_TEMPNAM)
  3038.     extern char *tempnam();
  3039.  
  3040.     if(!*wbprefix) 
  3041.         STRCPY(wbprefix,"./");
  3042.  
  3043.     buffer = tempnam((char *)wbprefix, "FTMP"); 
  3044.     // Non-|ANSI|, but more control over directory.
  3045. #else
  3046.     buffer = tmpnam(NULL); // |ANSI| routine.
  3047. #endif
  3048.  
  3049. f->tmp_name = GET_MEM("f->tmp_name",STRLEN(buffer)+1,outer_char); 
  3050.  
  3051. STRCPY(f->tmp_name, buffer);
  3052.  
  3053. f->ptr = FOPEN(f->tmp_name, "w");
  3054. }
  3055.  
  3056. @ Here we  close a file in response to an \.{@@O} command.
  3057. @<Part 1@>=@[
  3058.  
  3059. SRTN close_out FCN((fp))
  3060.     FILE *fp C1("")@;
  3061. {
  3062. OPEN_FILE *f;
  3063.  
  3064. for(f=open_file; f<last_file; f++)
  3065.     if(f->ptr == fp)
  3066.         {
  3067.         close0(f);
  3068.         return;
  3069.         }
  3070.  
  3071. CONFUSION("close_out", "Allegedly open file isn't in list");
  3072. }
  3073.  
  3074. @ Files with local scope are closed at the end of a section.
  3075. @<Part 1@>=@[
  3076.  
  3077. SRTN cls_local(VOID)
  3078. {
  3079. OPEN_FILE *f;
  3080.  
  3081. for(f=open_file; f<last_file; f++)
  3082.     if(f->ptr && !f->global_scope)
  3083.         close0(f);
  3084. }
  3085.  
  3086. @ Here's a nucleus for closing output files.
  3087. @<Part 1@>=@[
  3088. SRTN close0 FCN((f))
  3089.     OPEN_FILE *f C1("")@;
  3090. {
  3091. fclose(f->ptr);
  3092. f->ptr = NULL;
  3093. f->previously_opened = YES;
  3094. }
  3095.  
  3096. @ The following is called from |wrap_up()| in \.{common.web}.  Nothing
  3097. special needs to be done here.  (It's nontrivial in \FWEAVE.)
  3098.  
  3099. @<Part 1@>=@[
  3100.  
  3101. SRTN
  3102. cls_files(VOID)
  3103. {}
  3104.  
  3105. @ Here we go through the list of all potentially open files.  If it's open,
  3106. we compare the temporary file that was just written with what already
  3107. exists on disk.  If they're the same, the old one is kept; otherwise, the
  3108. temporary file is made the new one.
  3109.  
  3110. @<Part 1@>=@[
  3111. SRTN cmp_outfiles(VOID)
  3112. {
  3113. OPEN_FILE *f;
  3114. boolean renamed = NO;
  3115.  
  3116. printf("\nRenaming temporary file(s):  ");
  3117. UPDATE_TERMINAL;
  3118.  
  3119. for(f=open_file; f<last_file; f++)
  3120.     if(f->previously_opened || f->ptr)
  3121.         {
  3122.         FILE *old_ptr = FOPEN(f->name, "r");
  3123.  
  3124.         if(f->ptr)
  3125.             fflush(f->ptr);
  3126.  
  3127.         if(old_ptr)
  3128.             @<Compare file contents@>@;
  3129.         else
  3130.             @<Rename the temporary file@>@; // No old file at all.
  3131.         }
  3132.  
  3133. if(!renamed) 
  3134.     printf("[no changes]");
  3135. }
  3136.  
  3137. @ The following code is patterned after \.{nuweb}'s.  It compares the
  3138. contents of the new, temporary file and the old one.  If they're the same,
  3139. the temporary file is deleted; otherwise, it overwrites the old file.
  3140.  
  3141. @<Compare file contents@>=
  3142. {
  3143. int c_old, c_new;
  3144. FILE *new_ptr;
  3145.  
  3146. if(f->ptr)
  3147.     new_ptr = freopen((CONST char *)f->tmp_name, "r", f->ptr);
  3148. else
  3149.     new_ptr = FOPEN(f->tmp_name, "r");
  3150.  
  3151. if(!new_ptr) 
  3152.     FATAL(T, "\n!! Can't reopen temporary file ", f->tmp_name);
  3153.  
  3154. do
  3155.     {
  3156.     c_old = getc(old_ptr);
  3157.     c_new = getc(new_ptr);
  3158.     }
  3159. while(c_old == c_new && c_old != EOF);
  3160.  
  3161. fclose(old_ptr);
  3162. fclose(new_ptr);
  3163.  
  3164. if(c_old == c_new)
  3165.     remove((CONST char *)f->tmp_name); // Harmless if this doesn't work.
  3166. else
  3167.     @<Rename the temporary file@>@;
  3168. }
  3169.  
  3170. @ Since the behavior of |rename| is implementation-defined if the new file
  3171. exists, we explicitly remove it first.
  3172.  
  3173. @<Rename the temporary file@>=
  3174. {
  3175. /* Try to ensure that the following |rename| will succeed. */
  3176. remove((CONST char *)f->name); 
  3177.  
  3178. printf("(%s", (char *)f->name); // Echo to terminal.
  3179.  
  3180. if(rename((CONST char *)f->tmp_name, (CONST char *)f->name) != 0)
  3181.     { /* Rename didn't work.  Attempt to force the rename by issuing a
  3182. \.{mv} command.  The actual name of the command is obtained from the
  3183. preprocessor variable |MV|, which is defined on the command line and whose
  3184. value is ultimately defined in \.{defaults.mk}. */ 
  3185. #if ANSI_SYSTEM
  3186.     if(!system(NULL))
  3187.         { /* No command processor! */
  3188.         err_print(T, 
  3189. "Couldn't rename \"%s\" to \"%s\"", f->tmp_name, f->name);
  3190.         perror("");
  3191.         }
  3192.     else
  3193. #endif // |ANSI_SYSTEM|
  3194.         { 
  3195.         char temp[256];
  3196.  
  3197. /* We put the following here in case for some reason the make file can't
  3198. define |MV|.  This is the case with some versions of \.{nmake} on the PC. */
  3199. #ifndef MV
  3200.     #ifdef ibmpc
  3201.         #define MV "rename"
  3202.     #else
  3203.         #define MV "mv"
  3204.     #endif
  3205. #endif
  3206.         sprintf(temp, "%s %s %s", MV, (char *)f->tmp_name,
  3207.             (char *)f->name);
  3208.         system(temp);
  3209.         printf("*"); // Indicate a copy was done.
  3210.         }
  3211.     }
  3212.  
  3213. printf(")"); UPDATE_TERMINAL;
  3214.  
  3215. renamed = YES;
  3216. }
  3217.  
  3218. @ Here is a short-hand routine that expands the string equivalent of tokens
  3219. like |slash_slash| to the output.
  3220.  
  3221. @d OUT_OP(s) out_op(OC(s))
  3222. @d OUT_STR(s) out_str(OC(s))
  3223.  
  3224. @<Part 1@>=@[
  3225.  
  3226. SRTN out_op FCN((s))
  3227.     CONST outer_char HUGE *s C1("String to translate.")@;
  3228. {
  3229. out_str(s);
  3230.  
  3231. out_state = MISCELLANEOUS;
  3232. }
  3233.  
  3234. CONST outer_char HUGE *out_str FCN((s0))
  3235.     CONST outer_char HUGE *s0 C1("")@;
  3236. {
  3237. CONST outer_char HUGE *s;
  3238.  
  3239. for(s=s0; *s; s++)
  3240.     C_putc(*s);
  3241.  
  3242. return s0;
  3243. }
  3244.  
  3245. @ Here we translate internal code to their external representations.
  3246.  
  3247. @d F_OP(op77,op88) (Fortran88 ? op88 : op77)
  3248.  
  3249. @<Cases like \.{!=}@>=
  3250.  
  3251. case plus_plus: 
  3252.     if(FORTRAN_LIKE(language))
  3253.         {
  3254.         @<Output `\.=' and left-hand side@>;
  3255.         buffer_out('+'); @+ buffer_out('1');
  3256.         out_state = MISCELLANEOUS;
  3257.         }
  3258.     else 
  3259.         {
  3260.         if(*(pC_buffer-1) == '+' && !nuweb_mode) 
  3261.             C_putc(' '); // Watch out for |x + ++y|.
  3262.         OUT_OP("++");
  3263.         }
  3264.  
  3265.     @<Mark split...@>@;
  3266.     break;
  3267.  
  3268. case minus_minus: 
  3269.     if(FORTRAN_LIKE(language))
  3270.         {
  3271.         @<Output `\.=' and left-hand side@>;
  3272.         buffer_out('-'); @+ buffer_out('1');
  3273.         out_state = MISCELLANEOUS;
  3274.         }
  3275.     else 
  3276.         {
  3277.         if(*(pC_buffer-1) == '-' && !nuweb_mode) 
  3278.             C_putc(' '); // Watch out for |x - --y|.
  3279.         OUT_OP("--");
  3280.         }
  3281.  
  3282.     @<Mark split...@>@;
  3283.     break;
  3284.  
  3285. case minus_gt: OUT_OP(FORTRAN_LIKE(language) ? ".EQV." : "->"); @+ break;
  3286.  
  3287. case gt_gt:
  3288.     @<Mark split...@>@;
  3289.      OUT_OP(">>"); @+ break;
  3290.  
  3291. case eq_eq: 
  3292.     @<Mark split...@>@;
  3293.     OUT_OP(R77_or_F ? F_OP(".EQ.","==") : "=="); @+ break;
  3294.  
  3295. case lt_lt: 
  3296.     @<Mark split...@>@;
  3297.     OUT_OP("<<"); @+ break;
  3298.  
  3299. case @'>':
  3300.     if(in_string || in_format) 
  3301.         out_dflt(cur_char);
  3302.     else 
  3303.         {
  3304.         OUT_OP(R77_or_F ? F_OP(".GT.",">") : ">"); 
  3305.         if(language == C_PLUS_PLUS)
  3306.             C_putc(' '); // For protecting nested templates.
  3307.         }
  3308.  
  3309.     @<Mark split...@>@;
  3310.     break;
  3311.  
  3312. case gt_eq: 
  3313.     OUT_OP(R77_or_F ? F_OP(".GE.",">=") : ">=");  
  3314.     @<Mark split...@>@;
  3315.     break;
  3316.  
  3317. case @'<':
  3318.     if(in_string || in_format) 
  3319.         out_dflt(cur_char);
  3320.     else 
  3321.         OUT_OP(R77_or_F ? F_OP(".LT.","<") : "<"); 
  3322.  
  3323.     @<Mark split...@>@;
  3324.     break;
  3325.  
  3326. case lt_eq: 
  3327.     OUT_OP(R77_or_F ? F_OP(".LE.","<=") : "<="); 
  3328.     @<Mark split...@>@;
  3329.     break;
  3330.  
  3331. case not_eq: 
  3332.     OUT_OP(R77_or_F ? F_OP(".NE.","/=") : "!="); 
  3333.     @<Mark split...@>@;
  3334.     break;
  3335.  
  3336. case and_and: 
  3337.     OUT_OP(R77_or_F ? ".AND." : "&&"); 
  3338.     @<Mark split...@>@;
  3339.     break;
  3340.  
  3341. case or_or: 
  3342.     if(language==TEX) meta_mode = YES;
  3343.     else
  3344.         {
  3345.         OUT_OP(R77_or_F ? ".OR." : "||"); 
  3346.         @<Mark split...@>@;
  3347.         }
  3348.     break;
  3349.  
  3350. case star_star: 
  3351.     if(language==TEX) meta_mode = NO;
  3352.     else OUT_OP(C_LIKE(language) ? "^^" : "**"); 
  3353.     break;
  3354.  
  3355. case @'!': 
  3356.     @<Mark split...@>@;
  3357.     if(in_string) 
  3358.         return out_dflt(cur_char);
  3359.     else 
  3360.         OUT_OP(R77_or_F ? ".NOT." : "!"); 
  3361.     break; 
  3362.  
  3363. case slash_slash: OUT_OP("//"); @+ break;
  3364.  
  3365. case colon_colon: 
  3366.     if(in_string && !nuweb_mode) 
  3367.         return out_dflt(cur_char); /* The purpose of this clause
  3368. isn't clear.  Note |colon_colon == tab_mark|.  Presently, |colon_colon|
  3369. is active only for \Cpp. */
  3370.     else 
  3371.         OUT_OP("::");
  3372.     break;
  3373.  
  3374. case ellipsis: 
  3375.     OUT_OP(FORTRAN_LIKE(language) ? ".NEQV." : "...");
  3376.     @<Mark split...@>@;
  3377.     break;
  3378.  
  3379. case paste: OUT_OP("##"); @+ break;
  3380.  
  3381. case dot_const: 
  3382.     C_putc('.'); 
  3383.     STRCPY(dot_op.name+1,dots[cur_val].symbol);
  3384.     to_outer(dot_op.name+1);
  3385.     OUT_OP(OC(dot_op.name+1));
  3386.     C_putc('.');
  3387.     break;
  3388.  
  3389. @ Here we endow \Ratfor-77 with C's ability to handle powerful assignment
  3390. operators. Expressions like |i *= expr| get translated into |i = i*(expr)|.
  3391. @<Cases like \.{+=}@>=
  3392. case @'+':
  3393. case @'-':
  3394. case @'*':
  3395. case @'/':
  3396. /* These operators are handled in \Tangle\ as two adjacent tokens; we have
  3397. to check for that, and we dare not be in |VERBATIM| mode. */
  3398.     if(!FORTRAN_LIKE(language) || 
  3399.             cur_byte == cur_end || *cur_byte != @'=' ||  
  3400.             out_state == VERBATIM || !xpn_Ratfor)
  3401.         {
  3402.         if(cur_char==@'*' && C_LIKE(language) && out_state != VERBATIM
  3403.                 && *(pC_buffer-1) == '/' && !nuweb_mode) 
  3404.             C_putc(' '); // Watch out for |x/ *p|; not a comment.
  3405.         @<Mark split...@>@;
  3406.         return out_dflt(cur_char);
  3407.         }
  3408.  
  3409.     cur_byte++; /* Skip over the `\.='. */
  3410.     @<Output `\.=' and left-hand side@>;
  3411.     out_dflt(cur_char);
  3412.     send_rp = YES; /* The enclosing right paren will be output when the
  3413. next newline is encountered. */
  3414.     C_putc('('); // Not |buffer_out| because of \.{-)}.
  3415.     break;
  3416.  
  3417. @ This fragment is used both above and for the |++| and |--|~operators.
  3418. @<Output `\.=' and left-hand side@>=
  3419. @{
  3420. outer_char HUGE *l;
  3421.  
  3422. @b
  3423. /* The left-hand side has already been output. */
  3424. C_putc('='); // Not |buffer_out| because of \.{-)}.
  3425. plast_char--; // We don't want the '\.{=}' in the lhs buffer.
  3426. out_state = MISCELLANEOUS;
  3427.  
  3428. /* Now output the |i|~in the above
  3429. example again; however, in general, that could be subscripted etc. */
  3430. if(compound_assignments)
  3431.     {
  3432.     send_rp = YES;
  3433.  
  3434.     if(last_xpr_overflowed) 
  3435.         OVERFLW("last expression",ABBREV(max_expr_chars)); 
  3436.  
  3437.     for(l=last_char; isdigit(*l) || !isalpha(*l); l++)
  3438.         ;
  3439.  
  3440.     if(plast_char - l >= 3 && STRNCMP(last_char, "if(", 3) == 0)
  3441.         ERR_PRINT(T, "Sorry, can't expand compound assignment \
  3442. operators correctly after simple IF; use an IF...THEN construction");
  3443.  
  3444.     while(l < plast_char)
  3445.         buffer_out(*l++); // Echo the lhs.
  3446.  
  3447.     send_rp = NO;
  3448.     }
  3449. else 
  3450.     FATAL(T, "!! Operators ++, --, +=, -=, *=, and /= are not allowed; \
  3451. they were turned off by option \"-+\".","");
  3452. }
  3453.  
  3454. @ This important fragment translates the internal code for an identifier
  3455. into the actual name. Macro expansion and \Ratfor\ token translation is
  3456. done here.
  3457. @<Case of an identifier@>=
  3458. case end_format_stmt:
  3459.     in_format = NO;
  3460.     C_putc(';');
  3461.     out_state = NUM_OR_ID;
  3462.     break;
  3463.  
  3464. case begin_format_stmt:
  3465.     in_format = YES;
  3466.     OUT_OP(" format");
  3467.     out_state = MISCELLANEOUS;
  3468.     break;
  3469.  
  3470. case identifier:
  3471.     cur_char = x_identifier(cur_char);
  3472.     break;
  3473.  
  3474.  
  3475. @ This routine was inserted to attempt to cut down the function length.
  3476. @<Part 1@>=@[
  3477.  
  3478. eight_bits x_identifier FCN((cur_char))
  3479.     eight_bits cur_char C1("")@;
  3480. {
  3481. if(!in_cdir)
  3482.     @<Possibly expand special keyword@>;
  3483.  
  3484. if(is_deferred((sixteen_bits)cur_val)) 
  3485.     return cur_char;
  3486.  
  3487. /* |MAC_LOOKUP| determines whether this is a WEB macro. Eventually, this
  3488. routine will be called recursively to output the expansion. The |in_macro|
  3489. flag prevents us from checking the expanded tokens again, since everything
  3490. will already have been expanded. */
  3491.   if(!mac_protected && (macro_text=MAC_LOOKUP(cur_val)) != NULL)
  3492.     {
  3493.     @<Output a macro expansion@>@;
  3494.     return cur_char;
  3495.     }
  3496. else 
  3497.     { /* Not a macro. */
  3498.     @<Mark split...@>@;
  3499.  
  3500.     if (out_state==NUM_OR_ID && !nuweb_mode) 
  3501.         C_putc(' ');
  3502.  
  3503.     @<Output a possibly truncated identifier@>;
  3504.  
  3505.     if(no_expand)
  3506.         no_expand = mac_protected = NO;
  3507.     }
  3508.  
  3509. end_identifier:
  3510.   out_state = in_format ? MISCELLANEOUS : NUM_OR_ID; 
  3511.  
  3512. return cur_char;
  3513. }
  3514.  
  3515. @ It is easy to check whether an identifier is a deferred macro, because
  3516. the |macro_type| field was set when the deferred macro was stored in the
  3517. deferred pool.  If it is, the macro definition is executed and the macro is
  3518. now recorded as a regular (immediate) one.
  3519.  
  3520. @<Part 1@>=@[
  3521.  
  3522. boolean is_deferred FCN((cur_val))
  3523.     sixteen_bits cur_val C1("")@;
  3524. {
  3525. name_pointer np;
  3526.  
  3527. np = name_dir + cur_val;
  3528.  
  3529. if(np->macro_type == DEFERRED_MACRO)
  3530.     {
  3531.     text_pointer tp;
  3532.     eight_bits HUGE *p0;
  3533.     eight_bits a0;
  3534.  
  3535.     tp = (text_pointer)np->equiv; /* Position in the deferred pool. */
  3536.  
  3537. /* Copy the tokens of the definition over into the next text. */
  3538.     for(p0=tp->tok_start; p0 < (tp+1)->tok_start; ) 
  3539.         if(TOKEN1(a0= *p0++))
  3540.             if(a0 == @'#')
  3541.                    switch(*p0)
  3542.                 {
  3543.                 case @'!':
  3544.                     if(*(p0+1) == MACRO_ARGUMENT)
  3545.                         app_repl(a0)@; 
  3546.                 else 
  3547.                      @<Copy but don't expand deferred macro@>@;
  3548.                 break;
  3549.     
  3550.                 default:
  3551.                     app_repl(a0);
  3552.                     break;
  3553.                 }
  3554.             else app_repl(a0)@; /* Single token, not special. */
  3555.         else
  3556.             {
  3557.             app_repl(a0);
  3558.             app_repl(*p0++);
  3559.             }
  3560.  
  3561.     cur_text = text_ptr;
  3562.     cur_text->Language = (boolean)language;
  3563.     cur_text->nargs = tp->nargs;
  3564.     cur_text->moffset = tp->moffset;
  3565.     cur_text->var_args = tp->var_args;
  3566.     cur_text->recursive = NO;
  3567.     cur_text->text_link = macro;
  3568.  
  3569.     (++text_ptr)->tok_start = tok_ptr;
  3570.  
  3571.     np = name_dir + IDENTIFIER(tp->tok_start[0],tp->tok_start[1]);
  3572.     np->macro_type = IMMEDIATE_MACRO; // Now the defn's been executed.
  3573.     np->equiv = (EQUIV)cur_text;
  3574.  
  3575.     return YES; // It's a deferred macro.
  3576.     }
  3577.  
  3578. return NO; // Not a deferred macro.
  3579. }
  3580.     
  3581. @
  3582. @<Copy but don't expand deferred macro@>=
  3583. {
  3584. if(TOKEN1(*++p0)) 
  3585.     MACRO_ERR("! Macro token `#!' must be followed by identifier", YES); 
  3586. else
  3587.     {
  3588.     text_pointer m;
  3589.  
  3590.     if( (m=MAC_LOOKUP(IDENTIFIER(*p0,*(p0+1)))) == NULL)
  3591.         MACRO_ERR("! Expecting macro identifier after \"#!\"",YES);
  3592.     else 
  3593.         if(m->nargs > 0)
  3594.             MACRO_ERR("! Macro after \"#!\" can't have arguments",
  3595.                 YES);
  3596.         else @<Copy tokens of macro@>@;
  3597.  
  3598.     p0 += 2;
  3599.     }
  3600. }
  3601.  
  3602. @<Unused@>=
  3603. {
  3604. SPEC *s;
  3605.  
  3606. for(s=spec_tokens; s->len != 0; s++)
  3607.     if(cur_val == *s->pid && s->expand != NULL) 
  3608.         {
  3609.         boolean in_macro0 = in_macro;
  3610.  
  3611.         in_macro = NO; /* Don't suppress recursive expansion of
  3612. macros. */
  3613.         (*s->expand)();
  3614.         in_macro = in_macro0;
  3615.  
  3616.         goto end_identifier;
  3617.         }
  3618. }
  3619.  
  3620. @ Expand a \Ratfor\ token if necessary. 
  3621.  
  3622. @<Possibly expand spec...@>=
  3623. {
  3624. boolean in_macro0 = in_macro;
  3625. name_pointer np = name_dir + cur_val;
  3626. X_FCN (HUGE_FCN_PTR *pf)(VOID); // Fcn.\ associated with expandable keywords.
  3627.  
  3628. if(np->expandable & language)
  3629.     {
  3630. expand_special:
  3631.     in_macro = NO; // Don't suppress recursive expansion of macros.
  3632.  
  3633.     pf = np->x_translate[lan_num(language)];
  3634.  
  3635.     if(pf) 
  3636.         (*pf)(); // Expand keyword.
  3637.     else 
  3638.         CONFUSION("possibly expand special",
  3639.           "Allegedly expandable keyword has no associated function");
  3640.  
  3641.     in_macro = in_macro0;
  3642.  
  3643.     cur_char = id_keyword; // Helps \Ratfor\ know what happened.
  3644.     goto end_identifier;
  3645.     }
  3646. else if(R77 && Fortran88 && !checking_label) 
  3647.     switch(chk_lbl())
  3648.         {
  3649.        case YES: goto expand_special;
  3650.           case -1:  goto end_identifier;
  3651.        case NO: break;
  3652.         }
  3653. }
  3654.  
  3655. @ At this point in the output routine, we have identified an identifier as
  3656. a macro. Expand it, and output it recursively.
  3657. @<Output a macro exp...@>=
  3658. @{
  3659. eight_bits HUGE *p1;
  3660.  
  3661. @b
  3662. in_macro = YES; /* Used as a flag to prevent |MAC_LOOKUP| on
  3663.     recursive |out_char| output of the final translated macro. */
  3664.  
  3665. p1 = xmacro(macro_text,&cur_byte,cur_end,macrobuf); /* Expand this
  3666.     macro into the macro buffer. The final expansion will begin at |p1|
  3667.     and end at~|mp|. */
  3668.  
  3669. /* Output final translated text, which begins at the end~|p1| of the last
  3670. translation and ends at the current value of~|mp|. This calls |out_char|
  3671. recursively.  */
  3672. copy_out(p1,mp,macro);
  3673. in_macro = NO;
  3674. }
  3675.  
  3676. @ We want the speediest possible output routine, so we bypass extra stuff
  3677. if no variables were truncated.
  3678. @<Output a poss...@>=
  3679. @{
  3680. name_pointer np;
  3681.  
  3682. @b
  3683. np = name_dir + cur_val;
  3684.  
  3685. if(truncate_ids) 
  3686.     out_trunc(np);
  3687. else 
  3688.     see_id(np->byte_start,(np+1)->byte_start);
  3689. }
  3690.  
  3691. @ Interface to \.{rat77.web}.
  3692. @<Part 1@>=@[
  3693.  
  3694. SRTN out_ptrunc FCN((cur_val))
  3695.     sixteen_bits cur_val C1("")@;
  3696. {
  3697. @<Output a poss...@>@;
  3698. }
  3699.  
  3700. @ Write out an identifier, translating from internal |ASCII|.
  3701. @<Part 1@>=@[
  3702.  
  3703. SRTN see_id FCN((start,end))
  3704.     CONST ASCII HUGE *start C0("Beginning of identifier name.")@;
  3705.     CONST ASCII HUGE *end C1("End of identifier name.")@;
  3706. {
  3707. CONST ASCII HUGE *j;
  3708.  
  3709. for (j=start; j<end; j++) C_putc(XCHR(*j));
  3710. }
  3711.  
  3712. @ Print the $n$-th~identifier for debugging purposes. Call this routine
  3713. from the debugger.
  3714. @<Part 1@>=@[
  3715.  
  3716. int id FCN((n))
  3717.     int n C1("Identifier number.")@;
  3718. {
  3719. printf(_Xx("Id %d (0x%x): \"%s\"\n"), n, n, (char *)name_of((sixteen_bits)n));
  3720. return n;
  3721. }
  3722.  
  3723. @ This function translates internal text to the outer world, possibly
  3724. truncating it. 
  3725.  
  3726. @<Part 1@>=@[
  3727.  
  3728. outer_char HUGE *name_of FCN((id0))
  3729.     sixteen_bits id0 C1("Identifier token whose name is sought.")@;
  3730. {
  3731. static ASCII temp[MAX_ID_LENGTH];
  3732. int k,n;
  3733. name_pointer np;
  3734. CONST ASCII HUGE *end;
  3735.  
  3736. np = name_dir + id0;
  3737.  
  3738. /* Don't choke on bad id. */
  3739. if(np >= name_ptr)
  3740.     {
  3741.     STRCPY(temp,"???");
  3742.     return (outer_char HUGE *)temp;
  3743.     }
  3744.  
  3745. PROPER_END(end);
  3746.  
  3747. #if 0 /* This construction gives a compiler error on the IBM/6000. */
  3748. n = MIN(end - np->byte_start,MAX_ID_LENGTH-1);
  3749. #else
  3750. if(end - np->byte_start < MAX_ID_LENGTH - 1)
  3751.     n = PTR_DIFF(int, end, np->byte_start);
  3752. else
  3753.     n = MAX_ID_LENGTH - 1;
  3754. #endif
  3755.  
  3756. STRNCPY(temp,np->byte_start,n);
  3757.  
  3758. /* We must be careful when breakpointing; backslashes must be escaped. */ 
  3759. if(breakpoints)
  3760.    for(k=0; k<n; k++)
  3761.     if(temp[k] == @'\\') temp[k] = @'/';
  3762.  
  3763. temp[n] = '\0';
  3764.  
  3765. return to_outer(temp);
  3766. }
  3767.  
  3768. @ Spit out a possibly truncated identifier.
  3769.  
  3770. @<Part 1@>=@[
  3771.  
  3772. CONST ASCII HUGE *proper_end FCN((np))
  3773.     name_pointer np C1("")@;
  3774. {
  3775. CONST ASCII HUGE *end;
  3776.  
  3777. PROPER_END(end);
  3778. return end;
  3779. }
  3780.  
  3781. SRTN out_trunc FCN((np))
  3782.     CONST name_pointer np C1("")@;
  3783. {
  3784. TRUNC HUGE *s;
  3785. ASCII HUGE *pc;
  3786.  
  3787. pc = np->byte_start;
  3788.  
  3789. if(*pc != BP_MARKER)
  3790.         { /* Not truncated. */
  3791.         CONST ASCII HUGE *end;
  3792.  
  3793. /* If the next one was truncated, recover the proper end location. */
  3794.         PROPER_END(end);
  3795.         see_id((CONST ASCII HUGE *)pc,end);
  3796.         }
  3797.     else 
  3798.         { /* Truncated. */
  3799.         s = ((BP HUGE *)pc)->Root;
  3800.         see_id(s->id,s->id_end);
  3801.         }
  3802. }
  3803.  
  3804. @ Every time the line number is printed, it's remembered to help out with
  3805. error messages.
  3806. @<Glob...@>=
  3807.  
  3808. EXTERN LINE_NUMBER nearest_line SET(0);
  3809.  
  3810. @ Here we write out the module number info. If |cur_val > 0|, we're
  3811. beginning a module; if |cur_val < 0|, we're ending a module; if it's zero,
  3812. we print out the line number. The |line_info| flag kills off the output of
  3813. this information (although presently the information is still retained in
  3814. the file).
  3815.  
  3816. @<Case of a mod...@>=
  3817.  
  3818. case module_number:
  3819.     if (cur_val > 0) 
  3820.         prn_mod_num(OC("%c* %ld: *%c\n"),cur_val); // Beginning.
  3821.     else if(cur_val < 0) 
  3822.         prn_mod_num(OC("%c* :%ld *%c\n"),cur_val); // End.
  3823.     else 
  3824.         {// Print out the line number; remember it for error messages.
  3825.         if(line_info)
  3826.             {
  3827.             nearest_line = (LINE_NUMBER)(BASE2 * (*cur_byte++));
  3828.             nearest_line += *cur_byte++; // Gets the line number.
  3829.  
  3830.                 C_sprintf(OC("%cline %u \""),2,
  3831.                 language==TEX ? '%' : '#',nearest_line);
  3832.  
  3833. /* Get pointer to file name. */
  3834.                 cur_val = BASE2* (*cur_byte++); 
  3835.             cur_val += *cur_byte++;
  3836.  
  3837.             @<Output a possibly truncated identifier@>@;
  3838.             C_sprintf(OC("\"\n"),0);
  3839.             }
  3840.         else
  3841.             cur_byte += 4;
  3842.         }
  3843.  
  3844.     break;
  3845.  
  3846. @ The following function writes to the output file a comment about
  3847. beginning or ending a section (distinguished by the sign of~|cur_val|).
  3848. @<Part 1@>=@[
  3849.  
  3850. SRTN prn_mod_num FCN((fmt,val))
  3851.     outer_char *fmt C0("")@;
  3852.     long val C1("")@;    
  3853. {
  3854. int l;
  3855.  
  3856. if(line_info)
  3857.     {
  3858.     l = lan_num(R77_or_F && !free_90 ? FORTRAN : language);
  3859.  
  3860.     if(val < 0) 
  3861.         { /* Ending a section. */
  3862.         val = -val;
  3863. @%        C_putc('\n');
  3864.         }
  3865.  
  3866.     if(FORTRAN_LIKE(language)) 
  3867.         {
  3868.         if(out_pos > rst_pos) flush_out(YES);
  3869.         out_pos = 0;
  3870.         }
  3871.  
  3872.     C_sprintf(fmt,3,begin_comment_char[l],val,end_comment_char[l]);
  3873.     }
  3874. @#if 0
  3875.     switch(language)
  3876.         {
  3877.         case C:
  3878.         case C_PLUS_PLUS:
  3879.         case TEX:
  3880.         case LITERAL:
  3881.          C_sprintf(fmt,3,
  3882.             begin_comment_char[l],val,end_comment_char[l]);
  3883.         break;
  3884.  
  3885.         case RATFOR:
  3886.         case RATFOR_90:
  3887.         case FORTRAN:
  3888.         case FORTRAN_90:
  3889.         CHECK_OPEN;
  3890.         fprintf(out_file,fmt,
  3891.             begin_comment_char[l],val,end_comment_char[l]);
  3892.         break;
  3893.  
  3894.         default:
  3895.         ;
  3896.         }
  3897. @#endif
  3898. }
  3899.  
  3900. @* INTRODUCTION to the INPUT PHASE.  We have now seen that \.{TANGLE} will
  3901. be able to output the full \cee\ program, if we can only get that program
  3902. into the byte memory in the proper format. The input process is something
  3903. like the output process in reverse, since we compress the text as we read
  3904. it in and we expand it as we write it out.
  3905.  
  3906. There are three main input routines. The most interesting is |get_next|,
  3907. which gets the next token of a code text; the other two are used to scan
  3908. rapidly past \TeX\ text in the \.{WEB} source code. |skip_ahead| will jump
  3909. to the next token that starts with `\.{@@}'; |skip_comment| skips to the
  3910. end of a comment.
  3911.  
  3912. @i t_codes.hweb
  3913.  
  3914. @<Global...@>=
  3915.  
  3916. IN_STYLE eight_bits ccode[128]; // Meaning of a char following '\.{@@}'.
  3917.  
  3918. @  The control codes are assigned in \.{style.web}.
  3919.  
  3920. @m TANGLE_ONLY(d,c) INI_CCODE(d,c)
  3921. @m WEAVE_ONLY(d,c) INI_CCODE(d,USED_BY_OTHER)
  3922.  
  3923. @<Set ini...@>= 
  3924.  
  3925. zero_ccodes(); /* See \.{style.web}. */
  3926. ccode[@'/'] = begin_vcmnt; /* The commenting style is also fundamental, and
  3927.     for convenience the |line_break| command is also inviolate. (For
  3928.     \FTANGLE, this gets reassigned later.) */
  3929.  
  3930. @<Set the changable codes@>@;
  3931. @<Reassign certain codes for \FTANGLE@>@;
  3932. prn_codes();
  3933.  
  3934. @ Here are the default values for the things that are allowed to be
  3935. changed. Codes that are used only by
  3936. \FWEAVE\ get the special code~|ignore|; these are just skipped.  Codes
  3937. that are used by neither processor are initialized to~|'0xFF'|; that can be
  3938. used to trigger an error message.
  3939. Those things that must be reassigned for \FTANGLE\ are here
  3940. assigned the code for \FWEAVE; they're changed later by the |reassign|
  3941. function.  
  3942. @<Set the changable...@>= 
  3943. SAME_CCODE(" \t*",new_module);
  3944.  
  3945. SAME_CCODE("aA",begin_code);
  3946. SAME_CCODE("<",module_name);
  3947.  
  3948. SAME_CCODE("dD",definition);
  3949. SAME_CCODE("uU",undefinition);
  3950. SAME_CCODE("mM",WEB_definition);
  3951. SAME_CCODE("fF",formatt);
  3952.  
  3953. SAME_CCODE("'\"",ascii_constant);
  3954. REASSIGNABLE("=",verbatim);
  3955.  
  3956. REASSIGNABLE("tT",TeX_string);
  3957.  
  3958. SAME_CCODE("L",L_switch);
  3959. SAME_CCODE("cC",begin_C);
  3960. SAME_CCODE("rR",begin_RATFOR);
  3961. SAME_CCODE("n",begin_FORTRAN);
  3962. SAME_CCODE("N",begin_nuweb);
  3963.  
  3964. SAME_CCODE("&",join);
  3965.  
  3966. SAME_CCODE("?",Compiler_Directive);
  3967. SAME_CCODE("%",invisible_cmnt);
  3968.  
  3969. /* The next three must be reassigned to |control_text|. */
  3970. REASSIGNABLE("^",xref_roman);
  3971. REASSIGNABLE(".",xref_typewriter);
  3972. REASSIGNABLE("9",xref_wildcard);
  3973.  
  3974. SAME_CCODE("#",big_line_break);
  3975.  
  3976. SAME_CCODE("(",begin_meta);
  3977. SAME_CCODE(")",end_meta);
  3978.  
  3979. SAME_CCODE("l",limbo_text);
  3980. SAME_CCODE("vV",op_def);
  3981. SAME_CCODE("wW",macro_def);
  3982.  
  3983. TANGLE_ONLY("{",begin_bp);
  3984. TANGLE_ONLY("}bB",insert_bp);
  3985.  
  3986. TANGLE_ONLY("!",no_mac_expand);
  3987. TANGLE_ONLY("q", set_line_info);
  3988.  
  3989. SAME_CCODE("oO",new_output_file);
  3990.  
  3991. WEAVE_ONLY("\001",toggle_output); // This command is for internal use only!
  3992. WEAVE_ONLY("\\",line_break);
  3993. WEAVE_ONLY("_",underline);
  3994. WEAVE_ONLY("[",defd_at);
  3995. WEAVE_ONLY("`]",implicit_reserved);
  3996. WEAVE_ONLY("$",switch_math_flag);
  3997. {
  3998. char temp[3];
  3999.  
  4000. sprintf(temp,";%c",XCHR(interior_semi));
  4001. WEAVE_ONLY(temp,pseudo_semi);
  4002. }
  4003. WEAVE_ONLY("e",pseudo_expr);
  4004. WEAVE_ONLY(":",pseudo_colon);
  4005. WEAVE_ONLY(",",thin_space);
  4006. WEAVE_ONLY("|",math_break);
  4007. WEAVE_ONLY("~",no_line_break);
  4008. WEAVE_ONLY("-",no_index);
  4009. WEAVE_ONLY("+",yes_index);
  4010. WEAVE_ONLY("p", protect_code);
  4011. #if(DEBUG)
  4012.     WEAVE_ONLY("012",trace);
  4013. #endif /* |DEBUG| */
  4014. }
  4015.  
  4016. @ For \FTANGLE, certain codes must be reassigned (after they've possibly
  4017. been overridden by the style file).
  4018. @<Reassign...@>=
  4019. {
  4020. reassign(xref_roman,control_text);
  4021. reassign(xref_typewriter,control_text);
  4022. reassign(xref_wildcard,control_text);
  4023. reassign(TeX_string,control_text);
  4024.  
  4025. reassign(verbatim,stringg);
  4026. }
  4027.  
  4028. @ The |skip_ahead| procedure reads through the input at fairly high speed
  4029. until finding the next non-ignorable control code, which it returns.  There
  4030. is one special nuance. We don't want to process a language change between
  4031. vertical bars. Since during the high-speed scan we don't keep track of
  4032. balanced bars, we assume that the combination of bar followed by possible
  4033. spaces followed by a language command means the start of a barred section,
  4034. and we skip over the language command in that case.
  4035.  
  4036. @d MAYBE_SET_OUTPUT(l) if(last_char != @'|') set_output_file(l)@;
  4037.  
  4038. @<Part 1@>=@[
  4039.  
  4040. eight_bits skip_ahead FCN((last_control,skip_over_bars))
  4041.     eight_bits last_control C0("Last token that was seen.")@;
  4042.     boolean skip_over_bars C1("")@;
  4043. {
  4044. eight_bits cc; // Control code found.
  4045. int ncc = 0; /* A counter that counts the \.{@@}s;
  4046.         used to figure out whether to ignore section
  4047.         names immediately after \.{@@f}. */
  4048. ASCII last_char;
  4049. ASCII HUGE *lc;
  4050. ASCII HUGE *l1 = limit + 1;
  4051.  
  4052. WHILE()
  4053.     {
  4054.     if (loc>limit)
  4055.         {
  4056. another_line:
  4057.         if(from_buffer) 
  4058.             {
  4059.             undivert(); // Switch back to reading from files.
  4060.             return ignore;
  4061.             }
  4062.         else 
  4063.             {
  4064.             if(!get_line()) 
  4065.                 return new_module;
  4066.  
  4067.             l1 = limit + 1;
  4068.             }
  4069.         }
  4070.  
  4071.     *l1 = @'@@'; // Barrier to stop high-speed scan through line.
  4072.  
  4073. more_stuff:
  4074.     switch(*loc)
  4075.         {
  4076.        case @'@@':
  4077.         break;
  4078.  
  4079.        case @'|':
  4080.         if(skip_over_bars)
  4081.             {
  4082.             if(skip_bars() == new_module) return new_module;
  4083. /* It's now positioned after the bar. */
  4084.             continue;
  4085.             }
  4086.  
  4087. /* Otherwise, we're in limbo or scanning control text; just keep going. */
  4088.  
  4089.        default:
  4090.         loc++;
  4091.         if(loc > limit)
  4092.             {
  4093.             ncc = 2;
  4094.             goto another_line;
  4095.             }
  4096.         goto more_stuff;
  4097.         }
  4098.  
  4099.     *l1 = @' '; // Reset line terminator.
  4100.  
  4101.     if(loc > limit) ncc = 2;
  4102.     else @<Return the next non-ignorable control code@>@;
  4103.     }
  4104.  
  4105. DUMMY_RETURN(ignore);
  4106. }
  4107.  
  4108. @
  4109. @<Part 1@>=@[
  4110.  
  4111. eight_bits skip_bars(VOID)
  4112. {
  4113. PARAMS params0;
  4114. LANGUAGE language0 = language;
  4115. eight_bits ret_val;
  4116.  
  4117. params0 = params; // Save state.
  4118.  
  4119. loc++; // Advance past the opening bar.
  4120.  
  4121. WHILE()
  4122.     {
  4123.     if(loc > limit && !get_line()) 
  4124.         {
  4125.         err_print(T,"Reached end of file while skipping code text %s", 
  4126.             BTRANS);
  4127.         ret_val = new_module;
  4128.         goto done;
  4129.         }
  4130.  
  4131.     switch(next_control=get_next())
  4132.         {
  4133.        case begin_bp:
  4134.        case insert_bp:
  4135.        case begin_meta:
  4136.        case end_meta:
  4137.        case formatt:
  4138.        case limbo_text:
  4139.        case op_def:
  4140.        case macro_def:
  4141.        case definition:
  4142.        case undefinition:
  4143.        case WEB_definition:
  4144.        case m_ifdef:
  4145.        case m_ifndef:
  4146.        case m_else:
  4147.        case m_elif:
  4148.        case m_endif:
  4149.        case m_for:
  4150.        case m_endfor:
  4151.        case m_line:
  4152.        case m_undef:
  4153.        case begin_code:
  4154.         err_print(T,"Control code not allowed within |...|; \
  4155. inserted '|' in %s", MTRANS);
  4156.         loc -= 2;
  4157.         ret_val = @'|';
  4158.         goto done;
  4159.  
  4160.        case new_module:
  4161.         err_print(T,"Module%s ended while skipping code text; \
  4162. inserted '|'", MTRANS0); // Falls through to next case!
  4163.  
  4164.        case @'|':
  4165.         ret_val = next_control;
  4166.         goto done;
  4167.         }        
  4168.     }
  4169.  
  4170. done:
  4171.   params = params0;
  4172.   frz_params();
  4173.   set_output_file(language0);
  4174.  
  4175. return ret_val;
  4176. }
  4177.  
  4178. @ We get to here while skipping through a line at high speed.
  4179. @<Return the next non-ignorable...@>=
  4180. {
  4181. last_char = @' '; // Get the last non-blank character before this control code.
  4182.  
  4183. for(lc=loc-1; lc>=cur_buffer; lc--)
  4184.     if(*lc != @' ') 
  4185.         {
  4186.         last_char = *lc; // This might be a vertical bar.
  4187.         break;
  4188.         }
  4189.  
  4190. ++loc; // Position to after the~\.{@@}.
  4191. ++ncc; // Count the \.{@@}s.
  4192.  
  4193. switch(cc=ccode[*(loc++)])
  4194.     { /* Position to after \.{@@?}. */
  4195.    @<Specific language cases@>:
  4196.     loc--; /* Position to language letter; fall through. */
  4197.  
  4198.    case L_switch:
  4199.     {
  4200.     if(last_char != @'|')
  4201.         {
  4202.         @<Set |language|@>@;
  4203.         if(module_count == 0) global_params = params;
  4204.         set_output_file(language);
  4205.         }
  4206.     continue;
  4207.     }
  4208.     
  4209.    case begin_nuweb:
  4210.     nuweb_mode1 = nuweb_mode = !NUWEB_MODE;
  4211.  
  4212.     if(module_count == 0) 
  4213.         global_params = params;
  4214.  
  4215.     continue;
  4216.  
  4217.    case control_text:
  4218.     while ((c=skip_ahead(ignore,NO))==@'@@');
  4219.       /* only \.{@@@@} and \.{@@>} are expected */ /* Is |c| used?? */
  4220.  
  4221.       if (*(loc-1)!=@'>') ERR_PRINT(T,"Improper @@ within control text");
  4222. @.Improper \AT! within control text@>
  4223.     continue;
  4224.  
  4225.    case compiler_directive:
  4226.    case Compiler_Directive:
  4227.     if(scanning_TeX)
  4228.         ERR_PRINT(T,"Compiler directives are allowed only in code");
  4229.     loc = limit + 1;
  4230.     continue;
  4231.  
  4232.    case invisible_cmnt:
  4233.     loc = limit + 1;
  4234.     continue;
  4235.  
  4236.    case module_name:
  4237.     if(ncc==1 && last_control==formatt) 
  4238.         {
  4239.         loc -= 2;
  4240.         get_next(); // Scan module name to get it into table.
  4241.         continue;
  4242.         }
  4243.     break;
  4244.  
  4245.    case big_line_break: /* \.{@@\#} */
  4246.     if(loc >= limit) continue;
  4247.  
  4248.     @<Process possible preprocessor command@>; // (See \.{typedefs.web}.)
  4249.     continue;
  4250.  
  4251.    case USED_BY_NEITHER:
  4252.     err_print(T,"Invalid `@@%c' ignored",XCHR(*(loc-1)));
  4253.     continue;
  4254.     }
  4255.  
  4256. if (cc!=ignore || (*(loc-1)==@'>' && (ncc!=2) && last_control != formatt) ) 
  4257.     return cc; // \.{@@}~code or end of module name.
  4258. }
  4259.  
  4260. @ The |skip_comment| procedure reads through the input at somewhat high
  4261. speed until finding the end-comment token~`\.{*/}' or a new-line, in which
  4262. case |skip_comment| will be called again by |get_next|, since the comment
  4263. is not finished.  This is done so that the each newline in the code part of
  4264. a module is copied to the output; otherwise the \&{\#line} commands
  4265. inserted into the output file by the output routines become useless.  If it
  4266. comes to the end of the module it prints an error message.
  4267.  
  4268. @<Global...@>=
  4269.  
  4270. EXTERN boolean comment_continues SET(NO); // Are we scanning a comment?
  4271.  
  4272. @ Skip over comments.
  4273. @<Part 2@>=@[
  4274.  
  4275. boolean skip_comment(VOID)
  4276. {
  4277. ASCII c; /* current character */
  4278. PARSING_MODE outer_mode;
  4279.  
  4280. outer_mode = parsing_mode;
  4281. parsing_mode = OUTER;
  4282.  
  4283. if(comment_continues) loc--; /* We've already scanned over white space, so
  4284.   |loc| is presently one position beyond the first non-blank character on the
  4285.   continuation line. */
  4286. else if(*(loc-1) == @'/') loc++; /* If we're starting a comment, |loc|~is
  4287.   positioned on the star; move past that. */
  4288.  
  4289. WHILE()
  4290.     {
  4291.     if (loc>limit)
  4292.        if(!long_comment) @<Finish skipping comment and |break|@>@;
  4293.        else if(get_line()) 
  4294.         {
  4295.         comment_continues = YES; 
  4296.         break;
  4297.         }
  4298.           else
  4299.         {
  4300.             err_print(T,"Input ended in middle of comment %s", BTRANS);
  4301. @.Input ended in mid-comment@>
  4302.         comment_continues=NO;
  4303.         break;  /* We |break| out and return so |get_next| can
  4304. return a newline. */
  4305.         }
  4306.  
  4307.     c = *(loc++);
  4308.  
  4309.     if (c==@'*' && *loc==@'/') 
  4310.     { 
  4311.     loc++;
  4312.     @<Finish skipping comment...@>@;
  4313.     }
  4314.  
  4315.     if (c==@'@@') 
  4316.     {
  4317.           if (ccode[*loc]==new_module) /* `\.{@@\ }' or `\.{@@*}' */
  4318.          {
  4319.             err_print(T,"Section name ended in middle of comment %s", 
  4320.             BTRANS); 
  4321.         loc--;
  4322. @.Section name ended in mid-comment@>
  4323.         @<Finish skipping comment...@>@;
  4324.           }
  4325.           else loc++;
  4326.      }
  4327.     }
  4328.  
  4329. parsing_mode = outer_mode;
  4330. return comment_continues;
  4331. }
  4332.  
  4333. @ Ending the skip over comments is simple:
  4334. @<Finish skipping comment...@>=
  4335. {
  4336. comment_continues = NO; 
  4337. break;
  4338. }
  4339.  
  4340. @* INPUTTING the NEXT TOKEN.
  4341.  
  4342. @<Global...@>=
  4343.  
  4344. EXTERN name_pointer cur_module SET(NULL); /* name of module just scanned */
  4345. EXTERN ASCII c; /* the current character for |get_next| */
  4346. EXTERN boolean strt_cmnt;
  4347. EXTERN boolean strt_point_cmnt;
  4348. EXTERN boolean suppress_newline; // For scanning past invisible comments.
  4349. EXTERN boolean eat_blank_lines; // For `\.{@@\%\%}'.
  4350. EXTERN boolean no_expand SET(NO); // For use with `\.{@@\~}.
  4351. EXTERN boolean insrt_line SET(NO); // For inserting line number after \.{@@\%}.
  4352.  
  4353. @ As one might expect, |get_next| consists mostly of a big switch that
  4354. branches to the various special cases that can arise.  This function has
  4355. been broken into several function calls in order to fit it into personal
  4356. computers. 
  4357.  
  4358. When we return to token we obtained, we also store it using the |RETURN|
  4359. macro; this sometimes helps us parse the next object.
  4360.  
  4361. @d RETURN(pcode) return (eight_bits)pcode@;
  4362.  
  4363. @<Part 2@>=@[
  4364.  
  4365. eight_bits get_next(VOID) /* produces the next input token */
  4366. {
  4367. GOTO_CODE pcode; // Return code from the parse routines.
  4368.  
  4369. strt_point_cmnt = suppress_newline = NO;
  4370.  
  4371. WHILE()
  4372.     {
  4373.     @<Check if we're at the id part of a preprocessor command@>;
  4374.     @<Check if we're at the end of a preprocessor command@>;
  4375.  
  4376.     if (loc>limit) 
  4377.     @<Deal with end of line@>@;
  4378.     else 
  4379.     at_beginning = BOOLEAN(!preprocessing && (loc == cur_buffer));
  4380.  
  4381. if(preprocessing) 
  4382.     @<Compress string of blanks into one; if any found, return a space@>@;
  4383. else
  4384.     @<Skip white space at beginning of line@>@;
  4385.  
  4386. strt_cmnt = NO;
  4387.  
  4388. switch(language)
  4389.     {
  4390.    case TEX:
  4391.     if(!scanning_defn)
  4392.         {
  4393.         if((pcode=prs_TeX_code()) == MORE_PARSE) 
  4394.             break;
  4395.         else if(pcode < 0) 
  4396.             CONFUSION("prs_TEX_code","Negative pcode");
  4397.         else 
  4398.             RETURN(pcode);
  4399.         }
  4400.  
  4401.    default:
  4402.     if((pcode=prs_regular_code(MORE_PARSE)) == MORE_PARSE) 
  4403.         break;
  4404.     else if((int)pcode < 0)
  4405.         CONFUSION("prs_regular_code","Negative pcode");
  4406.     else 
  4407.         RETURN(pcode);
  4408.     }
  4409.    }
  4410.  
  4411. DUMMY_RETURN(ignore);
  4412. }
  4413.  
  4414. @ Since the preprocessor has different reserved words than C~itself, we
  4415. include the preprocessor token with the identifier if it's first on a
  4416. preprocessor line.
  4417.  
  4418. @<Check if we're at the id...@>=
  4419.  
  4420. if(preprocessing && at_beginning) 
  4421.     {
  4422.     at_beginning = NO;
  4423.  
  4424. /* Preprocessor directives can have white space between the '\.\#' and the
  4425. name. */
  4426.     for( ; loc < limit; loc++)
  4427.         if(!(*loc==@' ' || *loc==tab_mark)) break;
  4428.  
  4429.     *(loc-1) = @'#'; /* Now we're positioned on an identifier beginning
  4430. with |'#'|, with no intervening blanks. */
  4431.     return (eight_bits)prs_regular_code(GOTO_GET_IDENTIFIER);
  4432.     }
  4433.  
  4434. @ When we get to the end of a preprocessor line, we lower the flag and send
  4435. a code \\{right\_preproc}, unless the last character was the continuation
  4436. character'~\.\\'.
  4437.  
  4438. @<Check if we're at the end...@>=
  4439.  
  4440.   if(*loc==cont_char && loc==limit-1 && (preprocessing || free_Fortran))
  4441.     {
  4442.     loc += 2; /* Force it to read another line the next time through. */
  4443.     return (eight_bits)CHOICE(free_Fortran, @'&', cont_char); /* We
  4444. leave the format of the input file alone. Since we're using free-form
  4445. syntax, the compiler will continue the line correctly. */
  4446.     }
  4447.  
  4448. @ Here we are inside a C preprocessing statement.  A run of white space is
  4449. compressed into one blank.
  4450.  
  4451. @<Compress string of blanks...@>=
  4452. {
  4453. boolean found_white_space = NO;
  4454.  
  4455.     do
  4456.         {
  4457.         if((c=*loc++) != @' ' || c != tab_mark) 
  4458.             break;
  4459.  
  4460.         found_white_space = YES;
  4461.         }
  4462.     while(loc < limit);
  4463.  
  4464. @#if(0)
  4465.     if(c==cont_char && loc==limit)
  4466.         if(!get_line()) 
  4467.             return new_module;
  4468.         else 
  4469.             goto compress_blanks;
  4470. @#endif
  4471.         
  4472. if(found_white_space) 
  4473.     return @' ';
  4474. }
  4475.  
  4476. @ The following is called when |loc > limit|.
  4477.  
  4478. When debugging, it is useful to set a breakpoint at |undivert| and running
  4479. to there, before attempting to stop at |get_next| or |prs_regular_code|,
  4480. since the latter routines are called while storing macros (done before
  4481. |undivert|). 
  4482.  
  4483. @<Deal with end...@>=
  4484. {
  4485. if(from_buffer) 
  4486.     {
  4487.     undivert(); // Stop reading from buffer; go back to reading from files.
  4488.     if(stop_the_scan) 
  4489.         return WEB_definition;
  4490.     continue;
  4491.     }
  4492. else
  4493.     { /* Reading from file. */
  4494.       if (preprocessing && *(limit-1)!=cont_char) 
  4495.         {
  4496.         preprocessing = NO;
  4497.         if(in_cdir)
  4498.             {
  4499.             id_first = id_loc = mod_text + 1;
  4500.             *id_loc++ = cdir;
  4501.             *id_loc++ = '\0';
  4502.             in_cdir = NO;
  4503.             return stringg;
  4504.             }
  4505.         }
  4506.     if(stop_the_scan) 
  4507.         return WEB_definition;
  4508.       else if(!get_line()) 
  4509.         return new_module;
  4510.  
  4511.     if(eat_blank_lines)
  4512.         {
  4513.         eat_blank_lines = NO;
  4514.  
  4515.         while(loc >= limit)
  4516.             if(!get_line())
  4517.                 return new_module;
  4518.         }
  4519.  
  4520.     if(insrt_line)
  4521.         {
  4522.         ins_ln_no(0);
  4523.         insrt_line = NO;
  4524.         }
  4525.  
  4526.     at_beginning = BOOLEAN(!preprocessing);
  4527.  
  4528.       if(prn_where) 
  4529.         {
  4530.             prn_where=NO;
  4531.  
  4532.             if(!scanning_defn)
  4533.             {
  4534.             app_repl(@'\n'); 
  4535. // Ensure \&{\#line} command begins on new line.
  4536.             @<Insert the line number into |tok_mem|@>;
  4537.             }
  4538.             }
  4539.            else if(!suppress_newline &&
  4540.         (!R77_or_F || limit==cur_buffer || free_Fortran))
  4541.              return @'\n';
  4542.     
  4543.     suppress_newline = NO;
  4544.     }
  4545. }
  4546.  
  4547. @ Normally, white space at the beginning of line isn't significant---even
  4548. if the line ultimately starts with a preprocessor command.  Two exceptions
  4549. are \TeX\ mode and nuweb mode, since blanks or tabs could be significant
  4550. then.  However, in nuweb mode, white space in front of preprocessor
  4551. commands should be ignored.
  4552.  
  4553. @<Skip white space at beg...@>=
  4554. {
  4555. if(language==TEX) 
  4556.     c = *loc++;
  4557. else 
  4558.     {
  4559.     ASCII HUGE *loc0 = loc; // Remember starting point for nuweb mode.
  4560.  
  4561.     do
  4562.         { /* Skip beginning white space. */
  4563.         c = *loc++;
  4564.         }
  4565.     while(loc<=limit && (c==@' ' || c==tab_mark) );
  4566.  
  4567.     if(nuweb_mode || scanning_meta)
  4568.         {
  4569.         if(!(c == @'@@' && *loc == @'#'))
  4570.             { /* Go back to beginning. */
  4571.             loc = loc0;
  4572.             c = *loc++;
  4573.  
  4574.             if(loc > limit)
  4575.                 continue; // Prevent space at end of line.
  4576.             }
  4577.         }
  4578.     }
  4579. }
  4580.  
  4581. @ Parse \TeX\ code.
  4582. @<Part 2@>=@[
  4583. GOTO_CODE prs_TeX_code(VOID)
  4584. {
  4585. GOTO_CODE icode; // Return code from |get_control_code|.
  4586.  
  4587. if(loc>limit) 
  4588.     return MORE_PARSE;
  4589.  
  4590. if(TeX[c] == TeX_comment) 
  4591.     @<Handle \TeX\ comment@>@;
  4592.  
  4593. if (c==@'@@')
  4594.     {
  4595.     icode = get_control_code();
  4596.  
  4597.     if(icode == MORE_PARSE) 
  4598.         return icode;
  4599.  
  4600.     if((int)(icode) < 0) 
  4601.         return prs_regular_code(icode);
  4602.     else 
  4603.         return (eight_bits)icode;    
  4604.     }
  4605. else 
  4606.     @<Get \TeX\ string@>@;
  4607. }
  4608.  
  4609. @ Generally, comments are retained (|keep_trailing_comments==YES| by
  4610. default) if they don't start a line. 
  4611.  
  4612. @<Handle \TeX\ comment@>=
  4613. {
  4614. long_comment = NO;
  4615.  
  4616. if((all_cmnts_verbatim || (keep_trailing_comments && !at_beginning)) 
  4617.         && !(scanning_defn && is_WEB_macro))
  4618.     {
  4619.     strt_cmnt = YES;
  4620.     }
  4621. else
  4622.     {
  4623.     loc = limit + 1; // Skip rest of line.
  4624.     suppress_newline = YES; /* Blank lines inside macro def'ns, for
  4625.                     example, can cause problems. */
  4626.     return MORE_PARSE;
  4627.     }
  4628. }
  4629.  
  4630. @<Get \TeX\ string@>=
  4631. {
  4632. loc--;
  4633. id_first = id_loc = mod_text + 1;
  4634.  
  4635. if(strt_cmnt) 
  4636.     *id_loc++ = begin_Xmeta;
  4637.  
  4638. while(loc < limit)
  4639.     {
  4640.     if(*loc == @'@@')
  4641.         {
  4642.         if(*(loc+1)==@'@@') 
  4643.             *id_loc++ = *loc++;
  4644. @#if 0
  4645.         else break;
  4646. @#endif
  4647.         }
  4648.     else if(!strt_cmnt && TeX[*loc] == TeX_comment && *(loc-1) != @'\\') 
  4649.         break;
  4650.  
  4651.     *id_loc++ = *loc++;
  4652.     }
  4653.  
  4654. if(strt_cmnt) 
  4655.     *id_loc++ = end_Xmeta;
  4656.  
  4657. return stringg;
  4658. }
  4659.  
  4660. @ Parse all languages except \TeX.  Certain parts of this can be called by
  4661. means of the |iswitch| argument.
  4662. @<Part 2@>=@[
  4663. GOTO_CODE prs_regular_code FCN((iswitch))
  4664.     GOTO_CODE iswitch C1("")@;
  4665. {
  4666. GOTO_CODE icode; // Return code from |get_control_code|.
  4667.  
  4668. switch(iswitch)
  4669.     {
  4670.    case MORE_PARSE: break;
  4671.  
  4672.    case GOTO_MISTAKE: goto mistake;
  4673.    case GOTO_GET_IDENTIFIER: goto get_identifier;
  4674.    case GOTO_GET_A_STRING: goto get_a_string;
  4675.    case GOTO_SKIP_A_COMMENT: goto skip_a_comment;
  4676.     }
  4677.  
  4678. if(language != LITERAL)
  4679.     @<Check for ordinary comments@>@;
  4680.  
  4681. /* --- ELLIPSIS --- */
  4682. if(c==@'.' && *loc==@'.' && *(loc+1)==@'.')
  4683.     {
  4684.     ++loc;
  4685.     compress(ellipsis);
  4686.     }
  4687.  
  4688. /* --- DOT CONSTANT: `\.{.FALSE.}' --- */
  4689. else if(FORTRAN_LIKE(language) && dot_constants &&
  4690.         (c == wt_style.dot_delimiter.begin) && !isDigit(*loc))
  4691.  
  4692.     @<Try to identify a dot constant@>@;
  4693.  
  4694. /* --- CONSTANT: `\.{123}', `\.{.1}', or `\.{\\135}' --- */
  4695. else if (isDigit(c) || c==@'.' || (c==@'\\' && language != LITERAL) ) 
  4696.     @<Get a constant@>@;
  4697.  
  4698. /* --- IDENTIFIER --- */
  4699. else if(is_identifier(c)) 
  4700.     @<Get an identifier@>@;
  4701.  
  4702. /* --- STRING --- */
  4703. else if ( (c==@'\'' || c==@'"')
  4704.          || (is_RATFOR_(language) && sharp_include_line==YES && c==@'(') )
  4705.     {
  4706.     if(language == LITERAL)
  4707.         return c;
  4708.     else
  4709.         @<Get a string@>@;
  4710.     }
  4711.  
  4712. /* --- CONTROL CODE --- */
  4713. else if (c==@'@@') 
  4714.     @<Get a control code@>@;
  4715.  
  4716. /* --- WHITE SPACE --- */
  4717. else if (c==@' ' || c==tab_mark) 
  4718.     if(nuweb_mode || scanning_meta)
  4719.         return (c==tab_mark ? bell : c);
  4720.     else
  4721.         { /* Ignore spaces and tabs, unless preprocessing. */
  4722.         if (!preprocessing || loc>limit) 
  4723.             return MORE_PARSE;
  4724.           /* we don't want a blank after a final backslash */
  4725.         else 
  4726.             return @' '; // |preprocessing && loc <= limit|.
  4727.         }
  4728.  
  4729. /* --- C PREPROCESSOR COMMAND --- */
  4730. else if (c==@'#' && at_beginning && C_LIKE(language)) 
  4731.     {
  4732.     preprocessing = YES;
  4733.     return MORE_PARSE;
  4734.     }
  4735.  
  4736. /* --- END of |@r format| STATEMENT --- */
  4737. else if (in_format && c==@';') /* End a |@r format| statement. */
  4738.     {
  4739.     in_format = NO;
  4740.     return end_format_stmt;
  4741.     }
  4742.  
  4743. /* --- TWO-SYMBOL OPERATOR --- */
  4744. mistake: 
  4745.  if(language != LITERAL)
  4746.     @<Compress two-symbol operator@>@;
  4747.  
  4748. return (eight_bits)c;
  4749. }
  4750.  
  4751.  
  4752. @
  4753. @<Check for ordinary comments@>=
  4754. {
  4755. switch(c)
  4756.     {
  4757.    case (ASCII)begin_comment0:
  4758.     long_comment = strt_cmnt = YES;
  4759.     break;
  4760.  
  4761.    case (ASCII)begin_comment1:
  4762.     strt_cmnt = strt_point_cmnt = YES;
  4763.     long_comment = NO;
  4764.     break;
  4765.  
  4766.    case @'/':
  4767.     if(*loc==@'*')
  4768.         long_comment= strt_cmnt = YES;
  4769.     else if(*loc==@'/' && (C_LIKE(language) || (Cpp_comments &&
  4770. !in_format && FORTRAN_LIKE(language))))
  4771.         { // Short comments are recognized in both~C and \Cpp.
  4772.         long_comment = NO;
  4773.         strt_cmnt = YES;
  4774.         }
  4775.     break;
  4776.  
  4777.    case @'!':
  4778. /* \Fortran\ will handle the commenting style ``\.{! Comment}'' if
  4779. |point_comments| is on, or ``\.{!! Comment}'' always. */
  4780.     if((*loc==@'!' || point_comments) && FORTRAN_LIKE(language))
  4781.         {
  4782.         *(loc-1) = (ASCII)begin_comment1; /* This marker is
  4783. necessary so the verbatim comments don't get confused with \.{@@!}. */
  4784.         strt_cmnt = strt_point_cmnt = YES;
  4785.         long_comment = NO;
  4786.         }
  4787.     break;
  4788.     }
  4789.  
  4790. if(strt_cmnt && all_cmnts_verbatim && !(scanning_defn && is_WEB_macro))
  4791.     {
  4792.     loc--; /* Position on the '\./'. */
  4793.  
  4794.     @<Get a control code@>@;
  4795.     }
  4796. else if(strt_cmnt || comment_continues)
  4797.     {
  4798.     skip_a_comment:
  4799.           skip_comment(); /* scan to end of comment or newline */
  4800.  
  4801.           if ((comment_continues) && 
  4802.             !(scanning_defn && is_WEB_macro)) return @'\n';
  4803.           else return MORE_PARSE;
  4804.      }
  4805.  
  4806. if(loc==limit && c==cont_char && 
  4807.     (preprocessing || (auto_semi && R77)) ) return MORE_PARSE;
  4808. @#if(0)
  4809.     if(auto_semi && loc==limit && c==cont_char && R77) return MORE_PARSE;
  4810. @#endif
  4811. }
  4812.  
  4813. @ The following code assigns values to the combinations \.{++},
  4814. \.{--}, \.{->}, \.{>=}, \.{<=}, \.{==}, \.{<<}, \.{>>}, \.{!=}, \.{||} and
  4815. \.{\&\&}.  The compound assignment operators (e.g., \.{+=}) are 
  4816. separate tokens, according to the \ceeref. (They're not, according to ANSI.
  4817. Pragmatically, there's no more room in the table for more single-byte tokens.)
  4818.  
  4819. @d compress(c) if (loc++<=limit) return (eight_bits)(c)@;
  4820. @d Fcompress(c) if( is_FORTRAN_(language) && loc < limit) 
  4821.             return (eight_bits)(c)@; /* Not used. */ 
  4822. @<Compress two...@>=
  4823. switch(c) 
  4824.  {
  4825.   case @'\\':
  4826.     if(FORTRAN_LIKE(language) && !in_format && *loc == @'/')
  4827.         compress(slash_slash); /* \Fortran's concatenation
  4828. operator. Multiple slashes in |format| statements are just left alone. */
  4829.     break;
  4830.  
  4831.   case @'/':
  4832.     if(FORTRAN_LIKE(language) && !in_format)
  4833.         {
  4834.         if(*loc == @'/')
  4835.             {
  4836.             if(Cpp_comments) 
  4837.                 break; /* In this case, the
  4838. slashes are the \Cpp-style comments.  We'll always allow \.{\\/} as a
  4839. synonym for concatenation. */
  4840.  
  4841.             compress(slash_slash); /* \Fortran's concatenation
  4842. operator. Multiple slashes in |format| statements are just left alone. */
  4843.             }
  4844.         else if(*loc == @'=' && !compound_assignments)
  4845.             compress(not_eq);
  4846.         }
  4847.     break;
  4848.   case @'+': if (*loc==@'+') compress(plus_plus);  break;
  4849.  
  4850.   case @'-': if (*loc==@'-') {compress(minus_minus);}
  4851.     else if (*loc==@'>') compress(minus_gt);  break;
  4852.  
  4853.   case @'=': if (*loc==@'=') compress(eq_eq);  break;
  4854.  
  4855.   case @'>': if (*loc==@'=') {compress(gt_eq);}
  4856.     else if (*loc==@'>') {compress(gt_gt);}
  4857.     break;
  4858.  
  4859.   case @'<': if (*loc==@'=') {compress(lt_eq);}
  4860.     else if (*loc==@'<') {compress(lt_lt);}
  4861.     else if(*loc==@'>') {compress(not_eq);} /* \FORTRAN-88 */
  4862.     break;
  4863.  
  4864.   case @'&': if (*loc==@'&') compress(and_and);  break;
  4865.  
  4866.   case @'|': if (*loc==@'|') compress(or_or);  break;
  4867.  
  4868.   case @'!': if (*loc==@'=') {compress(not_eq);} break;
  4869.  
  4870.   case @'*': 
  4871.     if(FORTRAN_LIKE(language) && (*loc == @'*') )
  4872.         {compress(star_star);} /* Exponentiation. */
  4873.     break;
  4874.  
  4875.  case @'^': 
  4876.     if(*loc == @'^') {compress(star_star);}
  4877.     else if(FORTRAN_LIKE(language) && (loc < limit) ) return star_star;
  4878.     break; 
  4879.  
  4880.  case @'#': 
  4881.     if(*loc==@'#') {compress(paste);}
  4882.     else if(*loc==@'<') 
  4883.         {
  4884.         loc++;
  4885.         mac_mod_name = YES;
  4886.     @<Scan the module name, make |cur_module| point to it, and |return
  4887. module_name@;|@>@; 
  4888.         }
  4889.     break;
  4890.  
  4891.   case @':': if(*loc==@':' && language==C_PLUS_PLUS && !scanning_meta) 
  4892.     compress(colon_colon); @+ break;        
  4893.  
  4894. }
  4895.  
  4896.  
  4897. @ We need a few flags for processing constants.
  4898. @<Glob...@>=
  4899.  
  4900. EXTERN boolean starts_with_0, hex_constant, bin_constant, floating_constant;
  4901.  
  4902. @<Get a constant@>= 
  4903. @{
  4904. boolean decimal_point;
  4905.  
  4906. @b
  4907. if(loc==limit && c==cont_char)
  4908.     {
  4909.     if(preprocessing) loc++;
  4910.     return (eight_bits)c;
  4911.     }
  4912.  
  4913. starts_with_0 = hex_constant = bin_constant = floating_constant = NO;
  4914.  
  4915.   id_first = loc - 1;
  4916.  
  4917.   if (*id_first==@'.' && !isDigit(*loc)) goto mistake; /* not a constant */
  4918.  
  4919.   if (*id_first==@'\\') 
  4920.     {
  4921.     if(*loc == @'/') goto mistake;
  4922.     while (isOdigit(*loc)) loc++; /* octal constant */
  4923.     goto found;
  4924.     }
  4925.   else 
  4926.      {
  4927.      starts_with_0 = BOOLEAN(*id_first==@'0');
  4928.      if (starts_with_0) 
  4929.     {
  4930.     hex_constant = BOOLEAN(*loc==@'x' || *loc==@'X');
  4931.  
  4932.         if (hex_constant) 
  4933.         { /* hex constant---e.g, \.{0xA1} */
  4934.             loc++; while (isXdigit(*loc)) loc++; goto found;
  4935.         }
  4936.     else if( (bin_constant=BOOLEAN(*loc==@'b' || *loc==@'B')) != 0 )
  4937.         { /* Binary constant---e.g., |0b101|. */
  4938.         loc++;
  4939.         while(isBdigit(*loc)) loc++;
  4940.         goto found;
  4941.         }
  4942.     }
  4943.  
  4944. while(isDigit(*loc)) loc++; /* Skip over digits. */
  4945. decimal_point = BOOLEAN(*loc==@'.');
  4946. if(decimal_point) loc++; /* Check if decimal point. */
  4947. while(isDigit(*loc)) loc++; /* Skip over digits after decimal point. */
  4948.  
  4949.     if(FORTRAN_LIKE(language))
  4950.         if(*(loc-1)==@'.')
  4951.             {
  4952. /* If the constant doesn't end with a digit,
  4953. make sure the dot isn't the start of a dot constant. */
  4954.             if(is_dot())
  4955.                 {
  4956.                 loc--;
  4957.                 goto found;
  4958.                 }
  4959.             }
  4960.         else if(*loc == @'h' || *loc == @'H') 
  4961.             @<Get Hollerith string, |goto found@;|@>@;
  4962.  
  4963.     floating_constant = BOOLEAN(*loc==@'e' || *loc==@'E' ||
  4964.             (FORTRAN_LIKE(language) 
  4965.         && (*loc==@'d' || *loc==@'D' || *loc==@'q' || *loc==@'Q')));
  4966.  
  4967.     if(floating_constant)
  4968.         { /* float constant---e.g., \.{1.0e-5}  */
  4969.         if (*++loc==@'+' || *loc==@'-') loc++;
  4970.             while (isDigit(*loc)) loc++;
  4971.             }
  4972.  
  4973.     floating_constant |= decimal_point;
  4974.   }
  4975.  
  4976.   found: 
  4977.     if (C_LIKE(language))
  4978.         { /* Check for |unsigned|, |long|, or |float| suffix. */
  4979.         boolean its_long = NO, its_unsigned = NO, its_constant = NO;
  4980.  
  4981.         switch(*loc)
  4982.             {
  4983.            case @'l':
  4984.            case @'L':
  4985.             its_constant = its_long = YES;
  4986.             break;
  4987.  
  4988.            case @'u':
  4989.            case @'U':
  4990.             its_constant = its_unsigned = YES;
  4991.             break;
  4992.  
  4993.            case @'f':
  4994.            case @'F':
  4995.             its_constant = YES;
  4996.             break;
  4997.             }
  4998.  
  4999.         if(its_constant)
  5000.             { /* |long|, |float|, or |unsigned|
  5001. constant---e.g., \.{123L} */ 
  5002.             loc++; // Skip over suffix.  
  5003.         
  5004. /* Might be a second suffix. */
  5005.             if(its_long && (*loc == @'u' || *loc == @'U')) 
  5006.                 loc++; // |50LU|
  5007.             else if(its_unsigned && (*loc == @'l' || *loc ==@'L')) 
  5008.                 loc++; // |50UL|
  5009.             }
  5010.         }
  5011.     else if(Fortran88) @<Skip over optional kind parameter@>@;
  5012.  
  5013.   id_loc = loc;
  5014.   return constant;
  5015. }
  5016.  
  5017. @ For \Fortran-90.
  5018. @<Skip over optional kind...@>=
  5019. {
  5020. if(*loc == @'_')
  5021.     while(is_kind(*loc)) loc++;
  5022. }
  5023.  
  5024. @
  5025. @<Get Hollerith string...@>=
  5026. @{
  5027. int l,n;
  5028.  
  5029. @b
  5030. *loc++ = '\0'; /* Terminate string after the length
  5031.     (temporarily overwriting the 'H'); position to actual constant. */
  5032. n = ATOI(id_first); /* Length of constant. */
  5033. *(loc-1) = @'H'; /* Reconstruct the 'H'. */
  5034.  
  5035. for(l = 0; l<n; ++l) ++loc; /* Skip over the constant. */
  5036.  
  5037. goto found;
  5038. }
  5039.  
  5040. @
  5041. @<Try to identify a dot...@>=
  5042. @{
  5043. ASCII HUGE *p0;
  5044. int n;
  5045. eight_bits c;
  5046. ASCII dot_end = wt_style.dot_delimiter.end;
  5047.  
  5048. @b
  5049. /* At this point, |loc| is positioned to the first position after the dot. */
  5050. for(p0=loc, n=0; n<MAX_DOT_LENGTH; n++,loc++)
  5051.     if(*loc == dot_end || !isAlpha(*loc)) break; /* Found end of dot
  5052. constant. */ 
  5053.  
  5054. if(*loc != dot_end) /* Didn't find end. */
  5055.     {
  5056.     loc = p0; /* Reset position back to beginning. */
  5057.     goto mistake;
  5058.     }
  5059.  
  5060. c = dot_code(dots,uppercase(p0,n),loc++,dot_const);
  5061.  
  5062. if(c) return c;
  5063. else
  5064.     {
  5065.     loc = p0;
  5066.     goto mistake;
  5067.     }
  5068.  
  5069. }
  5070.  
  5071. @ Strings and character constants, delimited by double and single
  5072. quotes, respectively, can contain newlines or instances of their own
  5073. delimiters if they are protected by a backslash (for C---e.g., |"ab\"c"|)
  5074. or if they are 
  5075. repeated (for FORTRAN---e.g., |@r 'ab''c'|).  We follow this convention,
  5076. but do not allow the string to be longer than |longest_name|. 
  5077.  
  5078. @<Get a string@>= 
  5079. get_a_string:
  5080. {
  5081.   ASCII delim = c; /* what started the string */
  5082.   ASCII right_delim = c;
  5083.   int level;
  5084. boolean equal_delims;
  5085.  
  5086.   id_first = mod_text+1; /* Position of delimiter. */
  5087.   id_loc = mod_text; *++id_loc=delim;
  5088.  
  5089.   if(delim==@'(')
  5090.     {
  5091.      right_delim = @')'; /* For m4 |@r include|. */
  5092.     sharp_include_line = NO;
  5093.     }
  5094.  
  5095. level = 1;
  5096.  
  5097. equal_delims = BOOLEAN(right_delim==delim);
  5098.  
  5099. WHILE()
  5100. {
  5101.     if (loc>=limit) 
  5102.     {
  5103.       if( (equal_delims || chk_ifelse) && *(limit-1)!=cont_char) 
  5104.             /* Continuation after next line. */ 
  5105.         {
  5106.             err_print(T,"String %s with '%s%c' didn't end",
  5107.             BTRANS, delim==@'\'' ? "\\" : "", XCHR(delim)); 
  5108.         loc=limit; break;
  5109. @.String didn't end@>
  5110.           }
  5111.  
  5112.       if(!get_line()) 
  5113.         {
  5114.             err_print(T,"Input ended in middle of string \
  5115. %s with '%s%c'", BTRANS, delim==@'\'' ? "\\" : "", XCHR(delim)); 
  5116.         loc=cur_buffer; 
  5117.         break; 
  5118. @.Input ended in middle of string@>
  5119.           }
  5120.       else 
  5121.     {
  5122.     if (C_LIKE(language) && ++id_loc<=mod_end) *id_loc = @'\n'; 
  5123. /* More string to come; will print as \.{"\\\\\\n"} */
  5124.     
  5125. /* Now the continuation of the string is in the buffer.  If appropriate,
  5126. skip over beginning white space and backslash. */
  5127.     if(bslash_continued_strings)
  5128.         {
  5129.         for(; loc < limit; loc++)
  5130.             if(*loc != @' ' && *loc != tab_mark) break;
  5131.  
  5132.         if(*loc == cont_char) loc++; /* Move past the backslash. */
  5133.         else err_print(T,"Inserted '%c' at beginning of continued \
  5134. string",XCHR(cont_char));
  5135.         }
  5136.     }
  5137.     }
  5138.  
  5139.     if(!equal_delims) @<Skip over embedded comment@>;
  5140.  
  5141.     if ((c=*loc++)==delim) 
  5142.     {
  5143.     level++;
  5144.  
  5145.       if (++id_loc<=mod_end) *id_loc=c;
  5146.  
  5147.     if(!equal_delims) continue;
  5148.  
  5149.     if( *loc==delim && !(C_LIKE(language) ||
  5150.              (is_RATFOR_(language) && Ratfor77)) ) 
  5151.         ++loc; /* Copy over repeated delimiter. */
  5152.     else   break; /* Found end of string. */
  5153.         }
  5154.  
  5155.     if(c==right_delim)
  5156.         if(--level == 0)
  5157.             {
  5158.           if (++id_loc<=mod_end) *id_loc=c;
  5159.          break; /* Found end of string for unequal delims. */
  5160.             }
  5161.  
  5162. /* Double the quote. */
  5163.     if(R77 && c==@'\'')
  5164.         if(++id_loc <= mod_end) *id_loc = c;
  5165.  
  5166.     if (c==cont_char)
  5167.     {
  5168.       if (loc>=limit && (!is_FORTRAN_(language) || free_form_input)) 
  5169.         continue; /* Continuation of string; throw away the
  5170. continuation character. */
  5171.  
  5172.     if(!is_FORTRAN_(language))
  5173.     {
  5174.       c = *loc++; /* Character after backslash. */
  5175.     
  5176.     if(R77)
  5177.        switch(c)
  5178.         {
  5179. #if(0)
  5180. #define n c
  5181.         @<Convert escape characters@>@;
  5182. #undef n
  5183. #endif
  5184. /* Double the quote for the direct-to-Fortran output. */
  5185.         case @'\'':
  5186.             if(++id_loc <= mod_end) *id_loc = c;
  5187.             break;
  5188.         }
  5189.     else {if (++id_loc<=mod_end) *id_loc = @'\\';}
  5190.     }
  5191.         }
  5192.  
  5193.     if (++id_loc<=mod_end) *id_loc=c; /* Store the character. */
  5194.   }
  5195.  
  5196. found_string:
  5197.   if (id_loc>=mod_end) {
  5198.     SET_COLOR(error);
  5199.     printf("\n! String too long: ");
  5200. @.String too long@>
  5201.     ASCII_write(mod_text+1,25);
  5202.     printf("..."); mark_error;
  5203.   }
  5204.  
  5205.   id_loc++;
  5206.   return stringg;
  5207. }
  5208.  
  5209. @ For parenthesized strings, we shall eat embedded C-style comments.
  5210. @<Skip over embedded...@>=
  5211.  
  5212. if(*loc==@'/' && *(loc+1)==@'*')
  5213.     for(loc += 2; ; loc++)
  5214.         {
  5215.         if(loc >= limit)
  5216.             if(!get_line())
  5217.                 {
  5218.         err_print(T,"Input ended in middle of embedded comment %s",
  5219.             BTRANS);
  5220.                 loc = cur_buffer;
  5221.                 goto found_string;
  5222.                 }
  5223.  
  5224.         if(*loc==@'*' && *(loc+1)==@'/')
  5225.             {
  5226.             loc += 2;
  5227.             break;
  5228.             }
  5229.         }
  5230.  
  5231. @ After an \.{@@}~sign has been scanned, the next character tells us
  5232. whether there is more work to do.
  5233.  
  5234. @<Get a control code@>=
  5235. switch(icode=get_control_code())
  5236.     {
  5237.    case GOTO_MISTAKE: goto mistake;
  5238.    case GOTO_GET_A_STRING: goto get_a_string;
  5239.    case GOTO_GET_IDENTIFIER: goto get_identifier;
  5240.    case GOTO_SKIP_A_COMMENT: goto skip_a_comment;
  5241.  
  5242.    case m_line:
  5243.     ins_ln_no(1);
  5244.     suppress_newline = YES;
  5245.     return MORE_PARSE;
  5246.  
  5247.    case MORE_PARSE:
  5248.    default: return icode;
  5249.     }
  5250.  
  5251. @
  5252. @<Part 2@>=@[
  5253. GOTO_CODE get_control_code(VOID)
  5254. {
  5255. eight_bits cc; /* The |ccode| value. */
  5256.  
  5257. c = *loc++;
  5258. SET_CASE(c); // Set the |upper_case_code| flag.
  5259.  
  5260. if(c == (ASCII)begin_comment1 || c == (ASCII)begin_comment0)
  5261.     {
  5262.     c = *(loc-1) = @'/'; /* So we can handle this uniformly with C-style
  5263. comments. */
  5264.     strt_cmnt = YES;
  5265.     }
  5266.  
  5267. switch(cc=ccode[c]) 
  5268.     {
  5269.     case ignore: return MORE_PARSE; /* Undefined control code. */
  5270.  
  5271. /* Languages are stored, if necessary, in two parts: |begin_language|, and
  5272. the language itself. Here |set_output_file| sets the language, which can be
  5273. looked at when we're appending. */
  5274.    @<Specific language cases@>:
  5275.     loc--;
  5276.  
  5277.    case L_switch:
  5278.     {
  5279.     @<Set |language|@>@;
  5280.     set_output_file(language);
  5281.     return begin_language;
  5282.     }
  5283.  
  5284.     case control_text: while ((c=skip_ahead(ignore,NO))==@'@@');
  5285.       /* only \.{@@@@} and \.{@@>} are expected */ /* Is |c| used?? */
  5286.  
  5287.       if (*(loc-1)!=@'>') 
  5288.     err_print(T,"Improper @@ within control text %s", BTRANS);
  5289. @.Improper \AT! within control text@>
  5290.  
  5291.       return MORE_PARSE; /* To top of loop in |get_next|. */
  5292.  
  5293.     case module_name: /* \.{@@<} */
  5294.     mac_mod_name = NO; /* Used as a flag for macro processing. */
  5295.     @<Scan the module name, make |cur_module| point to it, and |return
  5296. module_name@;|@>@; 
  5297.  
  5298.     case stringg: /* \.{@@=} */
  5299.     @<Scan a verbatim string@>;
  5300.  
  5301.     case begin_vcmnt: 
  5302. /* Here the |strt_cmnt| handles all comments verbatim; the last two
  5303. cases handle~\.{@@\slashstar} or~\.{@@//}. */
  5304.     if(strt_cmnt || *loc==@'*' || *loc==@'/')
  5305.         if(!(scanning_defn && is_WEB_macro) && !deferred_macro)  
  5306.             {
  5307.             if(!strt_point_cmnt) long_comment =
  5308.                 BOOLEAN(!(*loc==@'/')); 
  5309.             @<Scan a verbatim comment@>@;  /* \.{@@\slashstar} */ 
  5310.             }
  5311.         else return GOTO_SKIP_A_COMMENT;
  5312.     else return MORE_PARSE; /* The line-break command \.{@@/} is ignored by
  5313. \TANGLE. */
  5314.  
  5315.     case invisible_cmnt:
  5316. /* When we sense an \.{@@\%}, we throw away everything to the end of line,
  5317. including the newline that is normally returned.  If the construction is
  5318. \.{@@\%\@}, we turn on the |eat_blank_lines| flags|, so we gobble up all
  5319. subsequent blank lines in a row. */
  5320.     if(*loc == @'%')
  5321.         eat_blank_lines = YES;
  5322.  
  5323. /* If the \.{@@\%} is beginning a line, put a \.{\#line} command in to help
  5324. out the debugger. */
  5325.     if(auto_line && !scanning_defn && loc == cur_buffer + 2)
  5326.         insrt_line = YES;
  5327.  
  5328.     loc = limit + 1; // Force the next line to be read.
  5329.     suppress_newline = YES;
  5330.     return MORE_PARSE;
  5331.  
  5332.     case compiler_directive:
  5333.         {
  5334.         int n;
  5335.         outer_char *s = t_style.cdir_start[language_num];
  5336.  
  5337.         id_first = id_loc = mod_text + 1;
  5338.  
  5339.         *id_loc++ = cdir;
  5340.  
  5341. /* Starting ``pragma'' string. */
  5342.         STRCPY(id_loc,s);
  5343.         to_ASCII((outer_char HUGE *)id_loc);
  5344.         id_loc += STRLEN(s);
  5345.         
  5346. /* Body. */
  5347.         STRNCPY(id_loc,loc,n = PTR_DIFF(int, limit, loc));
  5348.         id_loc += n;
  5349.  
  5350.         *id_loc++ = cdir;
  5351.         *id_loc++ = '\0';
  5352.  
  5353.         loc = limit + 1;
  5354.         return stringg;
  5355.         }
  5356.  
  5357.     case Compiler_Directive:
  5358.         {
  5359.         outer_char *s = t_style.cdir_start[language_num];
  5360.  
  5361.         id_first = id_loc = mod_text + 1;
  5362.  
  5363.         *id_loc++ = cdir;
  5364.         preprocessing = in_cdir = YES;
  5365.         at_beginning = NO;
  5366.  
  5367. /* Starting ``pragma'' string. */
  5368.         STRCPY(id_loc,s);
  5369.         to_ASCII((outer_char HUGE *)id_loc);
  5370.         id_loc += STRLEN(s);
  5371.  
  5372.         return stringg;
  5373.         }
  5374.  
  5375.     case new_output_file: // \.{@@o}
  5376.     @<Scan the output file name@>@;
  5377.     loc = limit + 1; // Skip rest of line.
  5378.     return cc;
  5379.  
  5380.     case ascii_constant: /* \.{@@'} or \.{@@"} */
  5381.     if(translate_ASCII) @<Scan an |ASCII| constant@>@;
  5382.     else 
  5383.         {
  5384.         c = *(loc-1); // The starting quote character.
  5385.         return GOTO_GET_A_STRING;
  5386.         }
  5387.  
  5388.     case big_line_break: /* \.{@@\#}. Serves double duty as line break or
  5389. preprocessor command. ??? GENERALIZE??? */
  5390.     if(loc >= limit) return MORE_PARSE;
  5391.  
  5392.     @<Process possible preprocessor command@>; 
  5393.     return MORE_PARSE;
  5394.  
  5395.     case set_line_info:
  5396.     @<Set the |line_info| flag@>@;
  5397.     return cc;
  5398.  
  5399.     case USED_BY_NEITHER:
  5400.     err_print(T,"Invalid `@@%c' ignored",XCHR(c));
  5401.     return ignore;
  5402.  
  5403.     default: return cc;
  5404.   }
  5405. }
  5406.  
  5407. @
  5408. @<Set the |line_info|...@>=
  5409. {
  5410. outer_char c = XCHR(*loc++);
  5411.  
  5412. if(!isdigit(c))
  5413.     {
  5414.     err_print(T, "You must say `@@Q0' or `@@Q1', not `@@Q%c'", c);
  5415.     loc--;
  5416.     }
  5417. else
  5418.     line_info = BOOLEAN((c != '0') && global_params.Line_info);
  5419. }
  5420.  
  5421. @ Here we copy over the contents of an |ASCII| constant or string.
  5422. @<Scan an |ASCII|...@>= 
  5423. {
  5424. ASCII delim = *(loc-1); // Character that started the string.
  5425.  
  5426.   id_first = loc - 1; // Include the delimiter for later reference.
  5427.  
  5428.   while(*loc != delim)
  5429.     {
  5430.     if (*loc == @'\\') 
  5431.         if(*++loc == delim) 
  5432.             { /* Skip over escape, and possibly escaped
  5433. delimiter. */ 
  5434.             loc++; 
  5435.             continue;
  5436.             }
  5437.  
  5438.     loc++;
  5439.  
  5440.     if (loc>limit) 
  5441.         {
  5442.             err_print(T,"ASCII string %s didn't end", BTRANS); 
  5443.         loc=limit-1; break;
  5444.         }
  5445.     }
  5446.  
  5447.   loc++; // Skip closing delimiter.
  5448.   return ascii_constant;
  5449. }
  5450.       
  5451. @ Process the stuff after~\.{@@<} or~\.{\#<}.
  5452. @<Scan the module name...@>= 
  5453. @{
  5454.   ASCII HUGE *k; /* pointer into |mod_text| */
  5455.   static ASCII ell[] = @"...";
  5456.  
  5457. @b
  5458.   @<Put module name into |mod_text|@>@;
  5459.  
  5460.   if (k-mod_text>3 && STRNCMP(k-2,ell,3)==0) 
  5461.     cur_module = prefix_lookup(mod_text+1,k-3);
  5462.   else 
  5463.     cur_module = mod_lookup(mod_text+1,k);
  5464.  
  5465. if(cur_module != NULL)
  5466.     {
  5467.     set_output_file(cur_module->mod_info->language); // Get current lang.
  5468.     }
  5469.  
  5470. return module_name;
  5471. }
  5472.  
  5473. @ Module names are placed into the |mod_text| array with consecutive spaces,
  5474. tabs, and carriage-returns replaced by single spaces. There will be no
  5475. spaces at the beginning or the end. (We set |mod_text[0]=' '| to facilitate
  5476. this, since the |mod_lookup| routine uses |mod_text[1]| as the first
  5477. character of the name.)
  5478.  
  5479. @<Set init...@>=
  5480.  
  5481. mod_text[0] = @' ';
  5482.  
  5483. @
  5484. @<Type...@>=
  5485.  
  5486. typedef struct
  5487.     {
  5488.     ASCII HUGE *start, HUGE *end;
  5489.     } TEMPLATE;
  5490.  
  5491. @<Put module name...@>=
  5492. {
  5493. int mlevel = 1; // For nested module names.
  5494. int arg_num = 0; // Counts template arguments.
  5495. TEMPLATE arg_ptr[10];
  5496.  
  5497. k = mod_text;
  5498.  
  5499. WHILE()
  5500.     {
  5501.     if (loc>limit && !get_line())
  5502.         {
  5503.         err_print(T,"Input ended in section name %s", BTRANS);
  5504. @.Input ended in section name@>
  5505.         loc=cur_buffer+1; 
  5506.         break;
  5507.         }
  5508.  
  5509.       c = *loc;
  5510.       @<If end of name, |break|@>;
  5511.       loc++; 
  5512.  
  5513.     if (k<mod_end) 
  5514.         k++; // Next available output position.
  5515.  
  5516.     switch(c)
  5517.         {
  5518.        case @' ':
  5519.        case tab_mark:
  5520.         c=@' '; 
  5521.         if (*(k-1)==@' ') 
  5522.             k--; // Compress white space.
  5523.         break;
  5524.  
  5525.        case @';':
  5526.         c = interior_semi;
  5527.         break;
  5528.  
  5529.        case @'[':
  5530. @#if 0
  5531.         if(*loc == @'[')
  5532.             {
  5533.             @<Add template argument to list@>@;
  5534.             continue;
  5535.             }
  5536. @#endif
  5537.         break;
  5538.         }
  5539.  
  5540.     *k = c; // Store the character.
  5541.     }
  5542.  
  5543. @#if 0
  5544. { /* Debugging */
  5545. int i;
  5546.  
  5547. if(arg_num > 0)
  5548.     puts("\nARGUMENTS:");
  5549.  
  5550. for(i=0; i<arg_num; i++)
  5551.     {
  5552.     int c = *arg_ptr[i].end;
  5553.  
  5554.     *arg_ptr[i].end = '\0';
  5555.     printf("[%i]:  \"%s\"\n", i, (char *)arg_ptr[i].start);
  5556.     *arg_ptr[i].end = c;
  5557.     }
  5558. trap();
  5559. }
  5560. @#endif
  5561.  
  5562. if (k>=mod_end) 
  5563.     {
  5564.     SET_COLOR(warning);
  5565.     printf("\n! Section name too long: ");
  5566. @.Section name too long@>
  5567.     ASCII_write(mod_text+1,25);
  5568.     printf("..."); mark_harmless;
  5569.     }
  5570.  
  5571. if (*k==@' ' && k>mod_text) 
  5572.     k--; // Trailing blanks.
  5573. }
  5574.  
  5575. @<If end of name,...@>=
  5576.  
  5577. if (c==@'@@') 
  5578.     {
  5579.     c = *(loc+1);
  5580.  
  5581.     if (c==@'>') 
  5582.         {
  5583.         if(--mlevel == 0)
  5584.             {
  5585.             loc+=2; 
  5586.             break; // Successful; position after \.{@@>}.
  5587.             }
  5588.         }
  5589.     else if(c==@'<') 
  5590.         mlevel++;
  5591.  
  5592.     if (ccode[c]==new_module) 
  5593.         {
  5594.         err_print(T,"Section name %s didn't end", BTRANS); 
  5595. @.Section name didn't end@>
  5596.         break;
  5597.         }
  5598.  
  5599.     *(++k) = @'@@'; 
  5600.     loc++; // Now |c==*loc| again.
  5601.     }
  5602.  
  5603. @
  5604. @<Add template argument to list@>=
  5605. {
  5606. arg_ptr[arg_num].start = ++loc;
  5607.  
  5608. while(loc < limit)
  5609.     if(*loc++ == @']' && *loc == @']')
  5610.         {
  5611.         loc++;
  5612.         break;
  5613.         }
  5614.  
  5615. arg_ptr[arg_num].end = loc - 2;
  5616.  
  5617. if(k < mod_end - 1)
  5618.     {
  5619.     *k++ = MACRO_ARGUMENT;
  5620.     *k = ++arg_num; // Zeroth-arg is recorded as~1.
  5621.     }
  5622. }
  5623.  
  5624. @ Verbatim comments (C-style comments preceded by~`\.{@@}'), are
  5625. essentially copied intact to the output. Here, we put the comment into the
  5626. |mod_text| buffer; we set |id_first| to the beginning, |id_loc| to the
  5627. end-plus-one, and |loc| to the position after the end-of-comment.
  5628.  
  5629. @<Scan a verbatim comment@>=
  5630. {
  5631. loc--; /* Position to the beginning slash or comment marker (which has been
  5632.         already read as part of~`\.{@@/}'). */
  5633.  
  5634. id_first = id_loc = mod_text + 1; /* A convenient place to put the verbatim
  5635.                     comment. */ 
  5636.  
  5637. if(!C_LIKE(language))
  5638.     {
  5639.     loc++;    /* Skip the opening \.*, for beauty. */
  5640.     @<Make newline and comment character@>;
  5641.     }
  5642.  
  5643. WHILE()
  5644.     {
  5645.     if(loc > limit)
  5646.       if(!long_comment) @<Finish comment and |break|@>@;
  5647.       else if(!get_line())
  5648.         {
  5649.         err_print(T,"Input ended in verbatim comment %s", BTRANS);
  5650. @.Input ended in verbatim comment@>
  5651.         loc = cur_buffer + 1;
  5652.         break;
  5653.         }
  5654.       else 
  5655.         {
  5656.         *id_loc++ = @'\n'; /* Retain line breaks in comments. */
  5657.  
  5658.         if(R66)
  5659.             {
  5660.             *id_loc++ = @'#'; /* Special comment line. */
  5661.             *id_loc++ = @' '; /* Space adds readability. */
  5662.             }
  5663.         }
  5664.  
  5665.     if(id_loc < mod_end - 3) 
  5666.         *id_loc++ = *loc++; /* Copy over the comment. */
  5667.     else
  5668.         {
  5669.         SET_COLOR(warning);
  5670.         printf("\n! Verbatim comment too long: ");
  5671. @.Verbatim comment too long@>
  5672.         ASCII_write(mod_text,25);
  5673.         printf("..."); mark_harmless;
  5674.  
  5675.         id_loc = mod_end - 3;
  5676.         *id_loc++ = @'*'; *id_loc++ = @'/'; /* Terminate the comment
  5677. (prematurely). */
  5678.         comment_continues = YES; /* This is so |get_next| can skip
  5679. the remainder of the comment. */
  5680.         goto finish_vcmnt;
  5681.         }
  5682.  
  5683. /* Check for end of verbatim comment. */
  5684.     if(long_comment && *loc == @'/' && *(loc-1)==@'*')
  5685.         {
  5686.         *id_loc++ = *loc++; /* Position after end of comment. */
  5687.         @<Finish comment and |break|@>@;
  5688.         }
  5689.     }
  5690.  
  5691. finish_vcmnt:
  5692.     if(!C_LIKE(language))
  5693.         {
  5694.         *id_loc++ = '\0';
  5695.         }
  5696.     return stringg; /* Complete comment copied. */
  5697. }
  5698.  
  5699.  
  5700. @
  5701. @<Finish comment...@>=
  5702. {
  5703. if(C_LIKE(language))
  5704.     { /* If we're not using \Cpp, we'll change short comments back to
  5705. standard form so they can be understood by the compiler. */
  5706.     if(!long_comment && !Cpp)
  5707.         {
  5708.         *id_loc++ = id_first[1] = @'*';
  5709.         *id_loc++ = id_first[0] = @'/';
  5710.         }
  5711.     }
  5712. else
  5713.     { /* In \Fortran, kill off the trailing terminator. */ 
  5714.     if(long_comment) id_loc -= 2; 
  5715.     }
  5716.  
  5717. break;
  5718. }
  5719.  
  5720. @ Verbatim comments not in~C must start on a new line, and must be prefixed
  5721. with a comment character.
  5722. @<Make newline and c...@>=
  5723. {
  5724. if(R66) *id_loc++ = @'#';
  5725. else *id_loc++ = @'\n';
  5726.  
  5727. }
  5728.  
  5729. @ At the present point in the program we have |*(loc-1)=stringg|; we set
  5730. |id_first| to the beginning of the string itself, and |id_loc| to its
  5731. ending-plus-one location in the buffer.  We also set |loc| to the position
  5732. just after the ending delimiter.
  5733.  
  5734. @<Scan a verbatim string@>= 
  5735. {
  5736. id_first = loc; /* This used to be |loc++|, but that doesn't handle null
  5737.         string correctly. */
  5738.  
  5739. *(limit+1) = @'@@'; *(limit+2) = @'>'; // Delimiters for verbatim string.
  5740.  
  5741. while (*loc != @'@@' || *(loc+1) != @'>') 
  5742.     loc++; // Verbatim string must end on same line.
  5743.  
  5744. if (loc >= limit) err_print(T,"Verbatim string %s didn't end", BTRANS);
  5745. @.Verbatim string didn't end@>
  5746.  
  5747. id_loc = loc; 
  5748. loc += 2; // Just after \.{@@>}.
  5749. return stringg;
  5750. }
  5751.  
  5752. @* GENERATING REPLACEMENT TEXTS.  The rules for generating the replacement
  5753. texts corresponding to macros and \cee\ texts of a module are almost
  5754. identical; the only differences are that
  5755. \begin{enumerate}
  5756.  \item Module names are not allowed in macros;
  5757. in fact, the appearance of a module name terminates such macros and denotes
  5758. the name of the current module.
  5759.  
  5760. \item The symbols \.{@@d}, \.{@@f}, and \.{@@a} are not allowed after
  5761. module names, while they terminate macro definitions.
  5762.  
  5763. \end{enumerate}
  5764.  
  5765. Therefore there is a single procedure |scan_repl| whose parameter
  5766. |t| specifies either |macro| or |module_name|. After |scan_repl| has acted,
  5767. |cur_text| will point to the replacement text just generated, and
  5768. |next_control| will contain the control code that terminated the activity.
  5769.  
  5770. /* In certain contexts, it is required to stop the scan at the end of the
  5771. current line. */
  5772. @d STOP (boolean)YES
  5773. @d DONT_STOP (boolean)NO
  5774.  
  5775. /* Add a token to |token_mem|. */
  5776. @d app_repl(c)  {if (tok_ptr==tok_m_end) 
  5777.                 OVERFLW("tokens",ABBREV(max_toks_t));
  5778.             *tok_ptr++= (eight_bits)(c);} 
  5779.  
  5780. @<Global...@>=
  5781.  
  5782. EXTERN text_pointer cur_text; // Replacement text just formed by |scan_repl|.
  5783.  
  5784. EXTERN eight_bits next_control;
  5785. EXTERN boolean scanning_meta SET(NO);
  5786.  
  5787. @ Creates a replacement text.
  5788. @<Part 3@>=@[
  5789.  
  5790. SRTN scan_repl FCN((t,stop))
  5791.     eight_bits t C0("Either |macro| or |module_name|.")@;
  5792.     boolean stop C1("IF |YES|, stops the scan at the end of current\
  5793. line.")@; 
  5794. {
  5795. eight_bits a0 = ignore;  /* the current token */
  5796. sixteen_bits a; /* An identifier number. */
  5797. LANGUAGE language0;
  5798. int ntoken = 2;
  5799. boolean auto_bp = YES; /* Breakpoints are inserted automatically, unless
  5800. the module starts off with \.{@@\lb}. */
  5801.  
  5802. scanning_meta = NO;
  5803. language0 = language; /* Save incoming language, in case while we're
  5804.             reading ahead we change it. */
  5805. stop_the_scan = stop;
  5806.  
  5807. if (t==module_name) 
  5808.     {
  5809.     ins_ln_no(column_mode);
  5810.  
  5811. /* Possibly turn on nuweb mode for output. */
  5812.     app_repl(begin_language);
  5813.     app_repl(NUWEB_OFF | nuweb_mode);
  5814.     }    
  5815. else if(stop) 
  5816.     @<Stop scan@>@;
  5817.  
  5818. WHILE()
  5819.     {
  5820.     if(stop)
  5821.         {
  5822.         while(loc <= limit) 
  5823.             if(*loc != @' ') break;
  5824.             else loc++;
  5825.  
  5826.         if(loc > limit) goto done;
  5827.         }
  5828.  
  5829. /* The |ntoken| counter starts out at~2. It is used to see whether the
  5830. first thing in the module is a left brace. If so, the |_BP| macro is
  5831. inserted after the brace for debugging purposes. */
  5832.     if(ntoken) 
  5833.         ntoken--;
  5834.  
  5835.     a0 = (ntoken && nuweb_mode && t==module_name) 
  5836.         ? begin_meta : get_next(); // !!!!!
  5837.  
  5838. reswitch:
  5839.     switch(a0)
  5840.         {
  5841.        case @'\\':
  5842.          if(loc==limit && language!=LITERAL)
  5843.             {
  5844.             if(!get_line())
  5845.                 FATAL(T, "!! Input ended while scanning \
  5846. WEB preprocessor statement","");
  5847.             @<Stop scan@>@;
  5848.             }
  5849.         else 
  5850.             {
  5851.             app_repl(a0);
  5852.  
  5853.             if(loc==limit && language == LITERAL) 
  5854.                 loc++; // Added |loc==limit|.  ???
  5855.             }
  5856.         break;
  5857.  
  5858.        case @'#': 
  5859.         if(t==macro && is_WEB_macro) 
  5860.             @<Possibly insert statement number@>@;
  5861.         else 
  5862.             {
  5863.             app_repl(a0);
  5864.             }
  5865.         break;
  5866.  
  5867.           @<In cases that |a0| is a non-ASCII token (|identifier|,
  5868.         |module_name|, etc.), either process it and change |a0| to a byte
  5869.         that should be stored, or |continue| if |a0| should be ignored,
  5870.         or |goto done@;| if |a0| signals the end of this replacement text@>@;
  5871.  
  5872. @#if(0)
  5873.     case @'\n':
  5874.         if(is_WEB_macro) continue;
  5875. @#endif
  5876.  
  5877.        case @'\n':
  5878. /* As far as checking whether a left brace begins a module, we don't care
  5879. about newlines. */
  5880.         if(ntoken) ntoken++;
  5881.         app_repl(a0);
  5882.         break;
  5883.  
  5884.        case @'{':
  5885.         app_repl(a0);
  5886.  
  5887.         if(ntoken && breakpoints && t==module_name&&auto_bp) 
  5888.             @<Insert the |_BP| macro for debugging@>@;
  5889.  
  5890.         break;
  5891.  
  5892.        case begin_bp:
  5893.         auto_bp = NO; // A manual insertion command is coming up.
  5894.         app_repl(@'{');
  5895.         break;
  5896.  
  5897.        case insert_bp:
  5898.         if(breakpoints) 
  5899.             @<Insert the |_BP|...@>@;
  5900.         break;
  5901.  
  5902.        default: 
  5903.         app_repl(a0); // Store |a0| in |tok_mem|.
  5904.         break;
  5905.         }
  5906.     }
  5907.  
  5908.   done: 
  5909.     if(stop_the_scan && !from_buffer)
  5910.         {
  5911.         stop_the_scan = NO;
  5912.         next_control = ignore;
  5913.         }
  5914.     else next_control = 
  5915.         (eight_bits)CHOICE((from_buffer && loc > limit) || stop,
  5916.                 ignore, a0);
  5917.  
  5918. if(t==module_name)
  5919.     {
  5920.     /* Reset nuweb mode. */
  5921.     if(scanning_meta)
  5922.         {
  5923.         if(!nuweb_mode)
  5924.             app_repl(end_meta);
  5925.  
  5926.         app_repl(stringg);
  5927.         scanning_meta = NO;
  5928.         }
  5929.     app_repl(begin_language);
  5930.     app_repl(NUWEB_OFF | nuweb_mode);
  5931.     }
  5932.  
  5933. @<Make |cur_text = text_ptr|; update |text_ptr|@>;
  5934. cur_text->Language = (boolean)language0; // Use the starting language.
  5935. }
  5936.  
  5937. @ For modules that start with a left brace, if the |_BP| macro has been
  5938. defined and/or we're in the debugging mode, then while we're reading things
  5939. in we insert a call to that macro, with arguments the module number and
  5940. module name. We build the call into the temporary buffer |bp_cmd|, then
  5941. divert the input stream into that buffer.
  5942.  
  5943. @d BP_BUF_SIZE (13 + MAX_ID_LENGTH) /* The print command below generates a
  5944.     string of the form ``\.{\_BP(99999,"\dots")}'', where the \dots\
  5945.     correspond to |name_of|, whose maximum length is |MAX_ID_LENGTH|. */
  5946.  
  5947. @<Insert the |_BP|...@>=
  5948. {
  5949. ASCII bp_cmd[BP_BUF_SIZE];
  5950.  
  5951. if(cur_module != NULL)
  5952.     {
  5953.     SPRINTF(BP_BUF_SIZE,bp_cmd,`"_BP(%d,\"%s\")",
  5954.         module_count,name_of((sixteen_bits)(cur_module-name_dir))`);
  5955.     to_ASCII(OC(bp_cmd));
  5956.     divert(bp_cmd,bp_cmd+STRLEN(bp_cmd),DONT_STOP);
  5957.     }
  5958. }
  5959.  
  5960. @ If the user has defined the macro |_BP| from the command line, then we
  5961. turn on the |breakpoints| flag so the macro can be inserted in front of
  5962. every module beginning with a left brace.
  5963. @<Glob...@>=
  5964.  
  5965. EXTERN boolean breakpoints;
  5966.  
  5967. @<Has the |_BP| macro been defined?@>=
  5968. @{
  5969. IN_COMMON ASCII HUGE *pbp;
  5970.  
  5971. @b
  5972. breakpoints = BOOLEAN(MAC_LOOKUP(ID_NUM(pbp,pbp+3)) != NULL);
  5973. }
  5974.  
  5975. @ (Sometimes used during debugging.)
  5976. @<Define internal...@>=
  5977.  
  5978. @#if(0)
  5979.     SAVE_MACRO("_BP(m,name)");
  5980. @#endif
  5981.  
  5982. @
  5983. @<Make |cur_text...@>=
  5984. {
  5985. if (text_ptr>text_end) OVERFLW("texts",ABBREV(max_texts));
  5986. cur_text = text_ptr; 
  5987. (++text_ptr)->tok_start = tok_ptr; /* The next start is the present end. */
  5988. }
  5989.  
  5990. @ Prevent macro scan for \.{@@\#if(...)} from overrunning end of line, by
  5991. inserting a |WEB_definition| command at the end.
  5992. @<Stop scan@>=
  5993. {
  5994. *limit = @' ';
  5995. *(limit+1) = @'@@';
  5996. *(limit+2) = @'m';
  5997. }
  5998.  
  5999. @ For inserting the line number, we use a function call to keep the code small.
  6000. @<Insert the line number into |tok_mem|@>=ins_ln_no(0)@;
  6001.  
  6002. @ Here is the code for the line number: first a |sixteen_bits| equal to
  6003. $|0150000| \equiv |LINE_NUM|$; then, if we're dealing with the change file,
  6004. the line number plus |0100000|; or, if we're dealing with the web file, the
  6005. line number; or, if we're dealing with an include file, the number 0, then
  6006. the line number, followed by the number of characters in the file name and
  6007. the file name.
  6008.  
  6009. @<Part 3@>=
  6010. SRTN ins_ln_no FCN((delta))
  6011.     int delta C1("Increment to line number")@;
  6012. {
  6013. name_pointer np;
  6014.  
  6015. store_two_bytes((sixteen_bits)LINE_NUM); // $\equiv$ a mod.\ \# of~0.
  6016.  
  6017. id_first = x_to_ASCII(changing ? change_file_name : cur_file_name);
  6018. id_loc = id_first + STRLEN(id_first);
  6019.  
  6020. store_two_bytes((sixteen_bits)((changing ? change_line : cur_line)+delta));
  6021.  
  6022. store_two_bytes(ID_NUM_ptr(np,id_first,id_loc));
  6023. np->Language = (boolean)NO_LANGUAGE; // \bfit Is this used???
  6024. }
  6025.  
  6026. @ This fragment stores away an identifier token returned from |id_lookup|.
  6027. @<Append identifier token@>=
  6028. @{
  6029. app_repl(LEFT(a,ID0));
  6030. app_repl(RIGHT(a));
  6031. }
  6032.  
  6033. @
  6034. @<Get and append an identifier token@>=
  6035.  
  6036. a = ID_NUM(id_first,id_loc);
  6037. @<Append identifier token@>@;
  6038.  
  6039. @<In cases that |a0| is...@>=
  6040.  
  6041. case identifier: 
  6042.     @<Get and append an identifier token@>@;
  6043.     break;
  6044.  
  6045. case module_name:
  6046. /* In a macro, the appearance of a module name beginning with
  6047.     \.{@@<} ends the macro and the definition section. On the other
  6048. hand, the construction \.{\#<\dots@@>} is OK in a macro. */
  6049.  if (t==macro && !mac_mod_name) 
  6050.     goto done;
  6051.  else 
  6052.   {
  6053.     @<Get optional arguments to module name@>@;
  6054.     @<Was an '@@' missed here?@>;
  6055.     a = (sixteen_bits)(cur_module - name_dir);
  6056.     app_repl(LEFT(a,MOD0));
  6057.     app_repl(RIGHT(a));
  6058.     ins_ln_no(0);
  6059.     if(nuweb_mode)
  6060.     { /* !!!!! */
  6061.     a0 = begin_meta;
  6062.     goto reswitch;
  6063.     }
  6064.     break;
  6065.   }
  6066.  
  6067. case constant: 
  6068. case stringg:
  6069.   @<Copy a string or verbatim construction or numerical constant@>;
  6070.  
  6071. case ascii_constant:
  6072.   cp_ASCII();
  6073.   break;
  6074.  
  6075. case begin_meta:
  6076.     @<Process |begin_meta|@>@;
  6077.     break;
  6078.  
  6079. case end_meta:
  6080.     @<Start column mode.@>;
  6081.     get_line();
  6082.     app_repl(end_meta);
  6083. @%    app_repl('\0');
  6084.     app_repl(stringg);
  6085.     scanning_meta = NO;
  6086.     break;
  6087.  
  6088. case dot_const:
  6089.     app_repl(a0);
  6090.     app_repl(dot_op.num); // |dot_op| was filled by |dot_code|.
  6091.     break;
  6092.  
  6093. case begin_language:
  6094.     switch(language)
  6095.         {
  6096.        case NO_LANGUAGE:
  6097.         CONFUSION("scan_repl:begin_language","Language isn't defined");
  6098.  
  6099.        case RATFOR:
  6100.        case RATFOR_90:
  6101.         if(!RAT_OK("(scan_repl)")) 
  6102.             CONFUSION("scan_repl:begin_language",
  6103.                 "Attempting to append @@Lr");
  6104.  
  6105.        case C:
  6106.        case C_PLUS_PLUS:
  6107.        case LITERAL:
  6108.         column_mode = NO;
  6109.         break;
  6110.  
  6111.        case FORTRAN:
  6112.        case FORTRAN_90:
  6113.        case TEX:
  6114.         if(!(scanning_defn || free_form_input)) 
  6115.             {
  6116.             @<Set up column mode@>@;
  6117.             }
  6118.         break;
  6119.  
  6120.        default:
  6121.         CONFUSION("app_id","Invalid language");
  6122.         }
  6123.  
  6124. /* We append the language in two parts: |begin_language|, and the language
  6125. itself. This is so we didn't have to tie up many non-printable |ASCII|
  6126. tokens. See the inverse code in |get_output|. */
  6127.     set_output_file(language);
  6128.     if(!scanning_defn) 
  6129.         {app_repl(a0);app_repl((eight_bits)language);}
  6130.     @<Insert the module number into |tok_mem|@>@;
  6131.     ins_ln_no(column_mode);
  6132.     break;
  6133.  
  6134. case no_mac_expand:
  6135.     app_repl(begin_language);
  6136.     app_repl(a0);
  6137.     break;
  6138.  
  6139. case set_line_info:
  6140.     app_repl(begin_language);
  6141.     app_repl(a0);
  6142.     app_repl(line_info);
  6143.     break;
  6144.  
  6145. case new_output_file:
  6146.     if(t == macro) 
  6147.         goto done;
  6148.     else
  6149.         {
  6150.         name_pointer np;
  6151.  
  6152.         app_repl(begin_language); // We piggy-back on |begin_language|.
  6153.         app_repl(NO_LANGUAGE);
  6154.         app_repl(upper_case_code); /* Scope of file name:
  6155. \.{@@o}~means local; \.{@@O}~means global. */
  6156.         a = ID_NUM_ptr(np, id_first, id_loc);
  6157.         @<Append identifier token@>@;
  6158.         np->macro_type = FILE_NAME; // To prevent truncations.
  6159.  
  6160.         if(nuweb_mode)
  6161.             {
  6162.             a0 = begin_meta;
  6163.             goto reswitch;
  6164.             }
  6165.         }
  6166.     break;
  6167.  
  6168. case WEB_definition:
  6169.     if(t == macro) 
  6170.         goto done;
  6171.     else 
  6172.         {
  6173.         @<Append a deferred macro@>;
  6174.         continue;
  6175.         }
  6176.  
  6177. case begin_nuweb:
  6178.     if(t != module_name)
  6179.         {
  6180.         nuweb_mode1 = nuweb_mode = !NUWEB_MODE;
  6181.         goto done;
  6182.         }
  6183.     else
  6184.         {
  6185.     ERR_PRINT(T,"@@N ignored; must appear before beginning of code part");
  6186.         continue;
  6187.         }
  6188.  
  6189. case formatt: 
  6190. case limbo_text: case op_def: case macro_def:
  6191. case definition: case undefinition:
  6192. case begin_code: 
  6193.   if (t!=module_name) 
  6194.     goto done;
  6195.   else 
  6196.     {
  6197.     ERR_PRINT(T,"@@d, @@l, @@v, @@w, @@u, @@f, and @@a \
  6198. are ignored in code text"); 
  6199.     continue; 
  6200. @.\AT!d, \AT!f and \AT!c are ignored in code text@>
  6201.     }
  6202.  
  6203. case end_of_buffer: 
  6204.     a0 = ignore;
  6205.  
  6206. case m_ifdef: case m_ifndef:
  6207. case m_if: case m_else: case m_elif: case m_endif: case m_undef: case m_line:
  6208. case m_for: case m_endfor:
  6209. case new_module: 
  6210.     goto done;
  6211.  
  6212. @
  6213. @<Process |begin_meta|@>=
  6214. {
  6215. app_repl(stringg);
  6216.  
  6217. if(!nuweb_mode)
  6218.     app_repl(a0); /* |begin_meta| inside strings means to insert the
  6219.         |meta| stuff from the style file. */
  6220.  
  6221. if(FORTRAN_LIKE(language)) 
  6222.     {
  6223.     column_mode = NO;
  6224.     app_repl(@'\n');
  6225.     }
  6226.  
  6227. scanning_meta = YES;
  6228.  
  6229. }
  6230.  
  6231. @
  6232. @<Unused@>=
  6233.     WHILE()
  6234.         {
  6235.         if(loc >= limit) // !!!!!!
  6236.             if(!get_line())
  6237.                 {
  6238.                 if(!nuweb_mode)
  6239.         err_print(T,"Input ended during meta-comment %s", BTRANS); 
  6240.                 break;
  6241.                 }
  6242.         
  6243.         while(loc < limit)
  6244.             {
  6245.             if(*loc == @'@@')
  6246.                 @<Check for end of meta-comment and |goto
  6247. done_meta@;| if necessary@>@; 
  6248.  
  6249.             if(is_identifier(*loc))
  6250.                 @<Append a meta-identifier@>@;
  6251.             else
  6252.                 app_repl(*loc++);
  6253.             }
  6254.         
  6255.         app_repl(@'\n');
  6256.         }
  6257.     
  6258.  
  6259. @
  6260. @<Append a meta-id...@>=
  6261. {
  6262. loc++;
  6263. @<Make |id_first|...@>@;
  6264. @<Get and append an identifier token@>@;
  6265. }
  6266.  
  6267. @
  6268. @<Check for end of meta-comment ...@>=
  6269. {
  6270. switch(ccode[*(loc+1)])
  6271.     {
  6272.    case ignore:
  6273.    case @'b':
  6274.    case @'{':
  6275.     if(nuweb_mode) 
  6276.         loc += 2;
  6277.     break;
  6278.  
  6279.    case end_meta:
  6280.     @<Start column mode.@>;
  6281.     get_line();
  6282.     goto done_meta;
  6283.  
  6284.    case new_module:
  6285.     goto done_meta; // !!!!!
  6286.  
  6287.    case @'@@':
  6288.     loc++;
  6289.     break;
  6290.  
  6291.    case invisible_cmnt:
  6292.     if(*(loc+2) == @'%')
  6293.         eat_blank_lines = YES;
  6294.  
  6295.     get_line();
  6296.  
  6297.     if(eat_blank_lines)
  6298.         {
  6299.         eat_blank_lines = NO;
  6300.  
  6301.         while(loc >= limit)
  6302.             if(!get_line())
  6303.                 goto done_meta;
  6304.         }
  6305.  
  6306.     continue;
  6307.     
  6308.    default:
  6309.     if(nuweb_mode)
  6310.         goto done_meta;  // !!!!!
  6311.  
  6312.       break;
  6313.     }
  6314. }
  6315.  
  6316. @ When |WEB_definition| is encountered in the code section, it signifies a
  6317. deferred macro. This has to be put into the special, deferred pool, not
  6318. into the current text being created.
  6319. @<Glob...@>=
  6320.  
  6321. EXTERN int n_unique SET(0);
  6322. EXTERN boolean deferred_macro SET(NO);
  6323.  
  6324. @ The deferred macro is referenced from the current text by creating a
  6325. special identifier of the form \.{@@}|n_unique|\.{name}, where |n_unique|
  6326. is incremented for each new reference to a deferred macro. The |equiv|
  6327. field in this identifier points to the deferred pool.
  6328.  
  6329. We must do some annoying copying in order to use the same routine
  6330. |app_macro|. This could be prettied up.
  6331. @<Append a deferred macro@>=
  6332. {
  6333. #define NAME_LEN 100
  6334.  
  6335. name_pointer np;
  6336. eight_bits HUGE *tok_ptr0, HUGE *tok_m_end0;
  6337. text_pointer text_ptr0,text_end0;
  6338. outer_char new_name[NAME_LEN];
  6339. ASCII HUGE *nn, HUGE *b;
  6340. sixteen_bits a;
  6341.  
  6342. if(!deferred_macros)
  6343.     {
  6344.     ERR_PRINT(T,"Sorry, deferred WEB macros (defined in code part) are \
  6345. prohibited; use option `-TD' to permit them");
  6346.     continue;
  6347.     }
  6348.  
  6349. tok_ptr0 = tok_ptr;
  6350. tok_m_end0 = tok_m_end;
  6351. text_ptr0 = text_ptr;
  6352. text_end0 = text_end;
  6353.  
  6354. tok_ptr = tok_dptr;
  6355. tok_m_end = tokd_end;
  6356. text_ptr = txt_dptr;
  6357. text_end = textd_end;
  6358.  
  6359. deferred_macro = YES;
  6360. np = app_macro(WEB_definition);
  6361. deferred_macro = NO;
  6362.  
  6363. tok_dptr = tok_ptr;
  6364. tok_ptr = tok_ptr0;
  6365. tok_m_end = tok_m_end0;
  6366.  
  6367. txt_dptr = text_ptr;
  6368. text_ptr = text_ptr0;
  6369. text_end = text_end0;
  6370.  
  6371. if(np == NULL) continue;
  6372.  
  6373. /* Create a unique name, beginning with '@@'. */
  6374. SPRINTF(NAME_LEN,new_name,`"@@%d",n_unique++`);
  6375. to_ASCII(new_name);
  6376. for(nn=(ASCII *)new_name+STRLEN(new_name),b=np->byte_start; 
  6377.         b<(np+1)->byte_start; )
  6378.     *nn++ = *b++;
  6379.  
  6380. a = ID_NUM_ptr(np,(ASCII *)new_name,nn);
  6381. @<Append identifier token@>;
  6382.  
  6383. np->macro_type = DEFERRED_MACRO;
  6384. np->equiv = (EQUIV)cur_text;
  6385.  
  6386. #undef NAME_LEN
  6387. }
  6388.  
  6389. @ Here we handle the cases in which `\.{\#}'~is expanded on \It{input}.
  6390. `\.{\#:0}'~expands into a unique statement number.
  6391. `\.{\#!}'~followed by a macro token means copy the definition of that
  6392. macro, but don't expand it. `\.{\#}'~followed by a macro token means
  6393. substitute the complete expansion of that macro.
  6394.  
  6395. @<Possibly insert statement...@>=
  6396. {
  6397. switch(*loc)
  6398.     {
  6399.    case @':':
  6400.     @<Possibly insert a unique statement label@>@; @+ break;
  6401.  
  6402.    case @'!':
  6403.     if(scanning_defn) @<Copy but don't expand macro@>@; 
  6404.     else app_repl(@'#');
  6405.     break;
  6406.  
  6407.    case @'\'':
  6408.    case @'"':
  6409.     app_repl(a0);
  6410.     app_repl(*loc++);
  6411.     break;
  6412.  
  6413.    default:
  6414.     @<Try to expand macro after \.{\#}'@>@; 
  6415.     break;
  6416.     }
  6417. }
  6418.  
  6419. @
  6420. @d N_IDBUF 100
  6421. @<Possibly insert a unique statement...@>=
  6422. @{
  6423. outer_char temp[N_IDBUF];
  6424. ASCII HUGE *t;
  6425.  
  6426. @b
  6427. loc++; /* Move past the colon. */
  6428.  
  6429. /* Check if it's '\.0'---immediate statement number. If not, pass it
  6430. through to the output phase. */
  6431. if(*loc != @'0')
  6432.     {
  6433.     app_repl(@'#');
  6434.     app_repl(@':');
  6435.     break;
  6436.     }
  6437.  
  6438. loc++; /* Move past the zero.*/
  6439. SPRINTF(N_IDBUF,temp,`"%lu",max_stmt++`); /* Make the number. */
  6440. to_ASCII(temp);
  6441.  
  6442. /* Append the number, bracketed by |constant|. */
  6443. app_repl(constant);
  6444.     
  6445. for(t=(ASCII *)temp; *t != '\0'; t++) app_repl(*t);
  6446.  
  6447. app_repl(constant);
  6448. }
  6449.  
  6450. @ We get to here when on input `\.{\#}' is not followed by `\.!' or `\.:'.
  6451. @<Try to expand macro after...@>=
  6452. @{
  6453. sixteen_bits a;
  6454.  
  6455. @b
  6456. if(isDigit(*loc) || *loc==@',' || *loc==@'&' || *loc==@'*' || *loc==@'.' ||
  6457.         *loc==@'[' || *loc==@'{') 
  6458. /* It's one of the forms `\.{\#}$nnn$', `\.{\#,}', `\.{\#\&}', `\.{\#*}',
  6459. or `\.{\#.}'; these are processed on output. */  
  6460.     {app_repl(@'#');} 
  6461. else if(get_next() != identifier) 
  6462.     MACRO_ERR("! '#' should be followed by identifier",YES);
  6463. else
  6464.     {
  6465.     a = ID_NUM(id_first,id_loc);
  6466.  
  6467. /* Check to see if the identifier is an already-defined macro; if not, it's
  6468. the stringizing operation, which is processed on output; just
  6469. append the identifier. */
  6470.     if( (MAC_LOOKUP(a)) == NULL)
  6471.         {
  6472.         app_repl(@'#');
  6473.         @<Append identifier token@>;
  6474.         break;
  6475.         }
  6476.  
  6477. /* Asking for immediate expansion of macro. */
  6478.     MACRO_ERR("! Immediate expansion of macro \"%s\" not implemented",
  6479.         YES,name_of(a));
  6480.     @<Append identifier token@>;
  6481.     }
  6482. }
  6483.  
  6484. @ If the construction `\.{\#!}' is followed by a macro id (without
  6485. arguments), then the token definition of that macro is substituted, without
  6486. expansion. 
  6487. @<Copy but don't expand macro@>=
  6488. @{
  6489. sixteen_bits a;
  6490.  
  6491. @b
  6492. loc++; /* Position to after `\.!'. */
  6493.  
  6494. if(get_next() != identifier) 
  6495.     ERR_PRINT(M,"Identifier must follow #!; command ignored");
  6496. else
  6497.     {
  6498.     text_pointer m;
  6499.  
  6500.  /* Add the identifier to the table if necessary. */
  6501.     a = ID_NUM(id_first,id_loc);
  6502.  
  6503. /* If it's an identifier but not a macro, it must be the construction
  6504. `\.{\#!}|arg|'; just append that for later processing. */
  6505.     if( (m=MAC_LOOKUP(a)) == NULL) 
  6506.         {
  6507.         app_repl(@'#');
  6508.         app_repl(@'!');
  6509.         @<Append identifier token@>;
  6510.         break;
  6511.         }
  6512.     else
  6513.         if(m->nargs > 0) 
  6514.             ERR_PRINT(M,"Macro after #! may not have arguments");
  6515.         else @<Copy tokens of macro@>@;
  6516.     }
  6517. }
  6518.  
  6519. @ Here we append the tokens of a macro definition, without expanding them.
  6520. @<Copy tokens of macro@>=
  6521. @{
  6522. eight_bits HUGE *q0, HUGE *q1;
  6523.  
  6524. @b
  6525. q0 = m->tok_start + m->moffset;
  6526. q1 = (m+1)->tok_start;
  6527.  
  6528. /* Just copy the definition without expanding. */
  6529. while(q0 < q1) app_repl(*q0++);
  6530. }
  6531.  
  6532. @
  6533. @<Get optional arg...@>=
  6534. {
  6535. }
  6536.  
  6537. @<Was an '@@'...@>= 
  6538. @{
  6539.   ASCII HUGE *try_loc = loc;
  6540.  
  6541. @b
  6542.   while (*try_loc==@' ' && try_loc<limit) 
  6543.     try_loc++;
  6544.  
  6545.   if (*try_loc==@'+' && try_loc<limit) 
  6546.     try_loc++;
  6547.  
  6548.   while (*try_loc==@' ' && try_loc<limit) 
  6549.     try_loc++;
  6550.  
  6551.   if (*try_loc == @'=') 
  6552.     ERR_PRINT(T,"Nested named modules.  Missing `@@*' or `@@ '?");
  6553. @.Nested named modules@>
  6554. }
  6555.  
  6556. @ We will {\it bracket} the string or constant with the id token.
  6557. @<Copy a string...@>=
  6558.  
  6559. if(C_LIKE(language))
  6560.     {
  6561.     if(bin_constant && a0==constant) 
  6562.         @<Convert binary constant@>@;
  6563.     else 
  6564.         copy_string(a0);
  6565.     }
  6566. else if(a0 == constant)
  6567.     {
  6568.     if(language == LITERAL)
  6569.         copy_string(a0);
  6570.     else if(hex_constant) 
  6571.         @<Convert hex constant@>@;
  6572.     else if(bin_constant) 
  6573.         @<Convert binary constant@>@;
  6574.     else if(starts_with_0 && !floating_constant) 
  6575.         @<Convert octal constant@>@; 
  6576.     else 
  6577.         copy_string(a0);
  6578.     }
  6579. else if(R77 && a0==stringg && !in_format)
  6580.     {
  6581.     if(*id_first==@'\'') 
  6582.         rdc_char_constant();
  6583.     else
  6584.         {
  6585. /* Replace the Ratfor double quote with Fortran's single quote. Watch out
  6586. for a verbatim comment that doesn't start with quote at all. */
  6587.         if(*id_first == @'"') *id_first = *(id_loc-1) = @'\'';
  6588.         copy_string(a0);
  6589.         }
  6590.     }
  6591. else 
  6592.     copy_string(a0);
  6593.  
  6594. break;
  6595.  
  6596. @
  6597. @<Part 3@>=@[
  6598. SRTN copy_string FCN((a0))
  6599.     eight_bits a0 C1("")@;
  6600. {
  6601. app_repl(a0); /* |stringg| or |constant| */
  6602.  
  6603. for(; id_first < id_loc; id_first++)
  6604.     {
  6605.     if (*id_first==@'@@') 
  6606.         @<Simplify \.{@@@@} pairs@>@;
  6607.  
  6608. @#if 0
  6609.     if(*id_first > 127)
  6610.         {
  6611.         err_print(T, "ASCII characters > 127 are prohibited inside \
  6612. strings; '%o' replaced by space", XCHR(*id_first));
  6613.         app_repl(@' ');
  6614.         }
  6615.     else
  6616. @#endif
  6617.         app_repl(*id_first);
  6618.     }
  6619.  
  6620. app_repl(a0); /* Bracket the string or constant with the id token. */
  6621. }
  6622.  
  6623. @ The following code changes doubled~\.{@@}'s to a single one.  It also
  6624. preserves any language commands, since these can appear inside vertical
  6625. bars.  Otherwise, it just deletes the character after the~`\.{@@}', thus
  6626. throwing away the entire `\.{@@}'~command.
  6627. @<Simplify \.{@@@@} pairs@>=
  6628.  
  6629. if(language == TEX && *(id_first+1) == @'@@') id_first++;
  6630. else
  6631.     {
  6632.     id_first++; // Character after the~`\.{@@}'.
  6633.  
  6634.     switch(ccode[*id_first])
  6635.         {
  6636.        case @'@@':
  6637.         break;  // The `\.{@@}'~will be stored.
  6638.  
  6639.        @<Specific language cases@>:
  6640.        case L_switch:
  6641.         app_repl(@'@@');
  6642.         break; // Retain the character.
  6643.  
  6644.        default:
  6645.         id_first++; // Discard character after~`\.{@@}'.
  6646.         continue;
  6647.         }
  6648.     }
  6649.  
  6650. @
  6651. @<Convert hex...@>=
  6652. @{
  6653. app_converted(xtoi(id_first,id_loc)); // Start after the \.{0x}.
  6654. }
  6655.  
  6656. @ A function that converts an alpha string to hex.
  6657. @<Part 3@>=@[
  6658.  
  6659. unsigned long xtoi FCN((b,b1))
  6660.     CONST ASCII HUGE *b C0("Beginning of string.")@;
  6661.     CONST ASCII HUGE *b1 C1("End of string.")@;
  6662. {
  6663. unsigned long n = 0;
  6664.  
  6665. for(b += 2; b<b1; b++)
  6666.     {
  6667.     n *= 16;
  6668.  
  6669.     if(isDigit(*b)) n += *b - @'0';
  6670.     else n += A_TO_UPPER(*b) - @'A' + 10;
  6671.     }
  6672.  
  6673. return n;
  6674. }
  6675.  
  6676. @
  6677. @<Part 3@>=@[
  6678. SRTN app_converted FCN((n))
  6679.     unsigned long n C1("")@;
  6680. {
  6681. ASCII temp[N_IDBUF];
  6682. ASCII HUGE *b;
  6683.  
  6684. SPRINTF(N_IDBUF,(outer_char *)(temp),`"%lu",n`);
  6685. to_ASCII((outer_char *)(temp));
  6686.  
  6687. app_repl(constant);
  6688.     for(b=temp; *b != '\0'; b++) app_repl(*b)@;
  6689. app_repl(constant);
  6690. }
  6691.  
  6692. @
  6693. @<Convert octal...@>=
  6694. {
  6695. app_converted(otoi(id_first,id_loc));
  6696. }
  6697.  
  6698. @ A function that converts an octal character string to integer.
  6699. @<Part 3@>=@[
  6700.  
  6701. unsigned long otoi FCN((b,b1))
  6702.     CONST ASCII HUGE *b C0("Beginning of string.")@;
  6703.     CONST ASCII HUGE *b1 C1("End of string.")@;
  6704. {
  6705. unsigned long n = 0;
  6706.  
  6707. for(b++; b<b1; b++)
  6708.     n = 8*n + *b - @'0';
  6709.  
  6710. return n;
  6711. }
  6712.  
  6713. @
  6714. @<Convert bin...@>=
  6715. {
  6716. app_converted(btoi(id_first,id_loc)); // Start after the \.{0x}.
  6717. }
  6718.  
  6719. @ A function that converts an binary character string to integer.
  6720. @<Part 3@>=@[
  6721.  
  6722. unsigned long btoi FCN((b,b1))
  6723.     CONST ASCII HUGE *b C0("Beginning of string.")@;
  6724.     CONST ASCII HUGE *b1 C1("End of string.")@;
  6725. {
  6726. unsigned long n = 0;
  6727.  
  6728. for(b+=2; b<b1; b++)
  6729.     n = 2*n + *b - @'0';
  6730.  
  6731. return n;
  6732. }
  6733.  
  6734. @ In \Ratfor-77 mode, character constants must be converted to integers. We
  6735. allow the standard ANSI escapes.
  6736. @<Part 3@>=@[
  6737. SRTN rdc_char_constant(VOID)
  6738. {
  6739. int n;
  6740.  
  6741. if(*++id_first == @'\\')
  6742.     switch(*++id_first)
  6743.         {
  6744.         @<Convert escape characters@>@;
  6745.         default:
  6746.             err_print(T,"Invalid escape sequence '\\%c' \
  6747. in Ratfor character constant; null assumed",XCHR(*id_first));
  6748.             n = 0;
  6749.             break;
  6750.         }
  6751. else n = *id_first;
  6752.  
  6753. if(*(id_first+1) != @'\'') ERR_PRINT(T,"Ratfor character constant longer \
  6754. than one byte; extra characters ignored");
  6755.  
  6756. app_converted(n);
  6757. }
  6758.  
  6759. @ Here are the standard ANSI escape sequences. The fragment isn't a
  6760. complete \&{switch} because we use it in several places, and the
  6761. \&{default} differs for each usage.
  6762.  
  6763. @<Convert escape char...@>=
  6764.         case @'0': n = '\0'; @+ break;
  6765.         case @'\\': n = @'\\'; @+ break;
  6766.         case @'\'': n = @'\''; @+ break;
  6767.         case @'"': n = @'\"'; @+ break;
  6768.         case @'?': n = @'\?'; @+ break; /* Microsoft doesn't like. */
  6769.         case @'a': n = @'\007'; @+ break; /* SGI didn't understand. */
  6770.         case @'b': n = @'\b'; @+ break;
  6771.         case @'f': n = @'\f'; @+ break;
  6772.         case @'n': n = @'\n'; @+ break;
  6773.         case @'r': n = @'\r'; @+ break;
  6774.         case @'t': n = @'\t'; @+ break;
  6775.         case @'v': n = @'\v'; @+ break;
  6776.  
  6777. @ At this point, we're positioned after the~`\.{@@}', on the starting
  6778. delimiter in a construction such as~`\.{@@'a'}', `\.{@@'\\n'}',
  6779. or~`\.{@@'\\007}'; or `\.{@@"abc"}'.
  6780.  
  6781. @<Part 3@>=
  6782. SRTN cp_ASCII(VOID)
  6783. {
  6784. if(*id_first++ == @'\'') 
  6785.     { /* Single |ASCII| character. */
  6786.     if(C_LIKE(language)) 
  6787.         app_aconst('o',YES); // Octal (leading zero).
  6788.     else 
  6789.         app_aconst('d',NO); // Decimal.
  6790.  
  6791.     if(*id_first != @'\'')
  6792.         @<Report single-quote error@>@;
  6793.     }
  6794. else
  6795.     { /* Do whole string. */
  6796.     if(C_LIKE(language))
  6797.         {
  6798.         app_repl(@'"');
  6799.  
  6800.         while(*id_first != @'"')
  6801.             {
  6802.             app_repl(@'\\');
  6803.             app_aconst('o',NO); // Octal, no leading zero.
  6804.             }
  6805.  
  6806.         app_repl(@'"');
  6807.         }
  6808.     else
  6809.         {
  6810.         sixteen_bits a;
  6811.         ASCII delim = (ASCII)(is_RATFOR_(language) ? @'"' : @'\'');
  6812.         int n = STRLEN(t_style.ASCII_fcn);
  6813.  
  6814. /*  Preface by function call from style file. */
  6815.         a = ID_NUM(t_style.ASCII_fcn,t_style.ASCII_fcn+n);
  6816.         @<Append identifier token@>@;
  6817.         app_repl(@'(');
  6818.         app_repl(delim);
  6819.         while(*id_first != @'"')
  6820.             app_repl(*id_first++);
  6821.         app_repl(delim);
  6822.         app_repl(@')');
  6823.         }
  6824.     }
  6825.  
  6826. #if(0) /* Keep around for compilers that can't handle the above. */
  6827. /* Do whole string, essentially converting to form
  6828. ``\.{@@'a',@@'b',@@'c'}''. */
  6829.     app_repl(@'{');
  6830.  
  6831.     while(*id_first != @'"')
  6832.         {
  6833.         app_aconst(YES);
  6834.         app_repl(@',');
  6835.         }
  6836.  
  6837.     app_repl(@'0'); // String terminator.
  6838.     app_repl(@'}');
  6839.     }        
  6840. #endif
  6841. }
  6842.  
  6843. @
  6844. @<Report single-q...@>=
  6845. {
  6846. ASCII temp[100], HUGE *t = temp;
  6847.  
  6848. id_first--;
  6849.  
  6850. if(id_first[-1] == @'\\')
  6851.     id_first--;
  6852.  
  6853. while(*id_first != @'\'')
  6854.     *t++ = *id_first++;
  6855.  
  6856. *t = '\0';
  6857.  
  6858. MACRO_ERR("! $A('%c') requires just one character between \
  6859. the single quotes; did you mean $A(\"%s\")?", NO, temp[0], temp);
  6860. }
  6861.  
  6862. @ Append the translation of an |ASCII| constant.
  6863. @<Part 3@>=
  6864.  
  6865. SRTN app_aconst FCN((fmt_char,leading_zero))
  6866.     outer_char fmt_char C0("Either 'o' (octal) or 'd' (decimal)")@;
  6867.     boolean leading_zero C1("For octal format")@;
  6868. {
  6869. eight_bits n; // Value of the constant.
  6870. outer_char value[10],*v;
  6871.  
  6872. if (*id_first==@'@@') 
  6873.     { /* The construction `\.{@@'@@@@'}'. */
  6874.     n = *id_first++; // Advance past first~`\.{@@}'.
  6875.  
  6876.     if (*id_first != @'@@') ERR_PRINT(T,"Should use double @@ within \
  6877. ASCII constant");
  6878.     else id_first++;
  6879.     }
  6880. else if (*id_first==@'\\') 
  6881.     { /* Something like `\.{@@'\\040'}' or~`\.{@@'\\n'}', or it could
  6882. be an escaped delimiter. */
  6883.     id_first++; // Advance past the escape character.
  6884.  
  6885.     n = esc_achar((CONST ASCII HUGE * HUGE *)&id_first);
  6886.  
  6887. @#if 0
  6888.     switch (*id_first) 
  6889.     {
  6890.     @<Convert escape char...@>@;
  6891.     default: err_print(T,"Invalid escape sequence '\\%c' \
  6892. in ASCII constant; null assumed",XCHR(*id_first));
  6893.         n = 0;
  6894.         break;
  6895.     }
  6896. @#endif
  6897.     }
  6898. else n = *id_first++; // ``Ordinary construction'' like `\.{@@'a'}'.
  6899.  
  6900. /* The following statement is for development while debugging the character
  6901. set translations.  From a normal \FTANGLE, \.{touch \{ftangle,common\}.web}
  6902. and run \.{make} with 
  6903. ``\.{DEBUGGING=-mscramble\_ASCII}''.  This adds some extra code to scramble
  6904. all the |ASCII| constants.  Then define |DEBUG_XCHR| in
  6905. \.{custom.h} and run \.{make} with ``\.{DEBUGGING=-a}''; this scrambles the
  6906. |ASCII| constants but also compiles using the new translation table.
  6907. Hopefully, it should work as before. */
  6908. #ifdef scramble_ASCII
  6909.     n = xxord[n];
  6910. #endif
  6911.  
  6912. #ifdef unscramble_ASCII
  6913.     n = XCHR(n);
  6914. #endif
  6915.  
  6916. /* Now |n|~has the numerical value of the |ASCII| constant; in octal, it's
  6917. something like~|0123|.  We now just append the octal representation as a
  6918. constant. */
  6919.   app_repl(constant);
  6920.  
  6921.   SPRINTF(10,value,`fmt_char=='o' ? "%s%o" : "%s%d",
  6922.     leading_zero ? "0" : "",n`);
  6923.  
  6924.   for(v=value; *v; v++)
  6925.     app_repl(XORD(*v));
  6926.  
  6927.   app_repl(constant);
  6928.  
  6929. #if(0) /* Kept around in case compiler can't understand \.{\%o}. */
  6930. int l;
  6931.  
  6932. if(leading_zero) app_repl(@'0'); // Beginning zero signifies octal constant.
  6933.  
  6934.   value[0] = @'0' + (n>>6); // Left-most digit.
  6935.   value[1] = @'0' + ((n-0100*(n>>6))>>3); // Center digit.
  6936.   value[2] = @'0' + (n-010*(n>>3)); // Right-most digit.
  6937.  
  6938.   for(l=0; l<3; l++)
  6939.     if(value[l] != @'0') break; // Kill off leading zeros for beauty.
  6940.  
  6941.   for( ; l<3; l++)
  6942.     app_repl(value[l]); // Nontrivial part.
  6943.  
  6944. #endif
  6945. }
  6946.  
  6947. @ Within macros, the \.{@'\dots'} constructions only works if the quote is
  6948. explicit, not if it's returned from another \WEB\ macro.  Therefore, we
  6949. introduce a built-in, to be used as `\.{\$A('\\321')}', which expands to its
  6950. argument if |translate_ASCII| is off or to `\.{0321}' if it's on.
  6951.  
  6952. @<Define internal macros@>=
  6953.  
  6954. SAVE_MACRO("_A(s)$$ASCII(s)");
  6955. SAVE_MACRO("$A(s)$$ASCII(s)");
  6956.  
  6957. SAVE_MACRO("_ASCII(s)$$ASCII(s)");
  6958. SAVE_MACRO("$ASCII(s)$$ASCII(s)");
  6959.  
  6960. @
  6961. @<Part 3@>=@[
  6962.  
  6963. SRTN i_ascii_ FCN((n,pargs))
  6964.     int n C0("")@;
  6965.     PARGS pargs C1("")@;
  6966. {
  6967. int len; // Length to copy.
  6968. eight_bits *start = pargs[0] + 1; // Starting address of argument.
  6969.  
  6970. CHK_ARGS("$A",1);
  6971.  
  6972. if(translate_ASCII)
  6973.     {
  6974.     eight_bits HUGE *tok_ptr0 = tok_ptr; // Save starting position.
  6975.  
  6976.     if(*start == stringg) 
  6977.         id_first = (ASCII HUGE *)(start + 1);
  6978.     else
  6979.         {
  6980.         err_print(T,"Argument of _A should be quoted; \
  6981. just returning argument");
  6982.         goto just_return;
  6983.         }
  6984.  
  6985.     cp_ASCII(); // Build result in the token area.
  6986.  
  6987.     len = PTR_DIFF(int, tok_ptr, tok_ptr0);
  6988.     MCHECK(len,"_ascii_");
  6989.     memcpy(mp,tok_ptr0,len);
  6990.     tok_ptr = tok_ptr0; // Reset position.
  6991.     }
  6992. else
  6993.     { /* Just return the string argument. */
  6994.   just_return:
  6995.     len = PTR_DIFF(int, pargs[1], start);
  6996.     MCHECK(len,"_ascii_");
  6997.     STRNCPY(mp,start,len);
  6998.     }
  6999.  
  7000. mp += len;
  7001. }
  7002.   
  7003. @* SCANNING a MODULE.
  7004. The |scan_module| procedure starts when~`\.{@@\ }' or~`\.{@@*}' has been
  7005. sensed in the input, and it proceeds until the end of that module.  It
  7006. uses |module_count| to keep track of the current module number; with luck,
  7007. \.{WEAVE} and \.{TANGLE} will both assign the same numbers to modules.
  7008.  
  7009. @ The top level of |scan_module| is trivial.
  7010. @<Part 3@>=@[
  7011.  
  7012. SRTN scan_module(VOID)
  7013. {
  7014. name_pointer p = NULL; /* module name for the current module */
  7015.  
  7016. module_count++;
  7017.  
  7018. params = global_params;
  7019. frz_params();
  7020. set_output_file(global_language);
  7021.  
  7022. progress();
  7023.  
  7024. @<Scan the definition part of the current module@>; // \TeX, \.{@@d}, \.{@@f}.
  7025. @<Scan the code part of the current module@>; // Code.
  7026. }
  7027.  
  7028. @<Glob...@>=
  7029.  
  7030. EXTERN boolean is_WEB_macro SET(NO);
  7031. EXTERN boolean scanning_defn; // Deflects verbatim comments from def'n section.
  7032. EXTERN boolean scanning_TeX; /* To help out |scan_text| with the handling
  7033.                 of vertical bars. */
  7034. EXTERN boolean nuweb_mode1; // In case \.{@@N} appears in defn section.
  7035.  
  7036. EXTERN int mlevel SET(0); // Level of preprocessor expansion.
  7037.  
  7038. @ Scan either to~\.{@@a} or~\.{@@<}. The one nuance here is that for the
  7039. very first module we must absorb the predefined macros, which are sitting
  7040. in the |macro_buf|.
  7041.  
  7042. @<Scan the definition part...@>=
  7043. {
  7044. parsing_mode = INNER;
  7045. nuweb_mode1 = nuweb_mode;
  7046.  
  7047. next_control=ignore;
  7048.  
  7049. if(module_count == 1)
  7050.     {
  7051.     *(mp-1) = @'@@';
  7052.     *mp = @'m';
  7053.     divert((ASCII HUGE *)macrobuf,(ASCII HUGE *)mp,STOP); /* Begin
  7054.         reading from 
  7055.         the macro buffer, when some macros were predefined with
  7056.         |save_macro|. |mp-1| is positioned to the blank after the
  7057.         last definition. */ 
  7058.     }
  7059.  
  7060. /* Skip \TeX\ stuff and expand the definition section. */
  7061. scanning_TeX = YES;
  7062.  scan_text(macro,p,EXPAND);
  7063. scanning_TeX = NO;
  7064.  
  7065. if(module_count == 1) @<Has the |_BP| macro been defined?@>;
  7066.  
  7067. if(mlevel != 0)
  7068.     {
  7069.     err_print(M,"Invalid preprocessor block structure (level %d). \
  7070. Missing @@#endif?",mlevel);
  7071.     mlevel = 0;
  7072.     }
  7073. }
  7074.  
  7075. @ A global flag for checking preprocessor commands.
  7076.  
  7077. @<Glob...@>=
  7078.  
  7079. EXTERN boolean found_else SET(NO);
  7080.  
  7081. @ The actual work is done in this recursive function.  Preprocessor
  7082. segments are treated as independent units, processed separately with
  7083. |scan_repl|, then linked together.
  7084.  
  7085. @d MAX_LEVEL 20
  7086.  
  7087. /* We have to tell |scan_text| whether or not to expand the text it is
  7088. reading. */
  7089. @d EXPAND YES
  7090.  
  7091. @<Part 3@>=@[
  7092.  
  7093. SRTN scan_text FCN((text_type,p,expand))
  7094.     int text_type C0("Either |macro| or |module_name|.")@;
  7095.     CONST name_pointer p C0("Module name.")@;
  7096.     boolean expand C1("Do we expand?")@;
  7097. {
  7098. boolean if_switch;
  7099. boolean scanned_if = NO;
  7100. boolean first_text = YES;
  7101. eight_bits HUGE *pp;
  7102. text_pointer q;
  7103.  
  7104. scanning_defn = BOOLEAN(text_type==macro);
  7105.  
  7106. if(++mlevel >= MAX_LEVEL) 
  7107.     FATAL(T, "!! Conditional nesting depth exceeded",""); /* Increment and
  7108. remember the incoming processing level. */  
  7109.  
  7110. WHILE()
  7111.     {
  7112.     if(scanning_defn && expand)
  7113.       {
  7114.       while(next_control<=ignore_defn)
  7115.         { // Skip \TeX\ stuff, \.{@@f}, \.{@@l}, \.{@@v}, and \.{@@W}.
  7116.          if((next_control=
  7117.             skip_ahead(next_control,YES))==module_name)  
  7118.             { /* scan the module name too */
  7119.              loc-=2; next_control=get_next();
  7120.             }
  7121.         }
  7122.       scanning_TeX = NO;
  7123.        }
  7124.     else /* Process incoming code text. */
  7125.        if(!expand) 
  7126.         {
  7127.         while( (next_control = 
  7128.               skip_ahead(next_control,YES)) == module_name)
  7129.              if( (next_control=skip_ahead(next_control,YES)) != ignore)
  7130.                 ERR_PRINT(T,"Expected @@> after @@<");
  7131.         }
  7132.        else
  7133.         { /* Process another complete fragment of code. */
  7134.         @<Insert the module number into |tok_mem|@>@;
  7135.  
  7136.         scan_repl(module_name,stop_the_scan); /* Now |cur_text|
  7137. points to the replacement text. */ 
  7138.  
  7139.         @<Argize the module@>@;
  7140.  
  7141.         @<Update the data structure so that the replacement text is
  7142. accessible@>@; 
  7143.         first_text = NO;
  7144.         }
  7145.  
  7146.    next_macro_token:
  7147.     switch(next_control)
  7148.         {
  7149.        @<Preprocessor cases@>@;
  7150.  
  7151.        case new_output_file:
  7152.         err_print(T,"@@O and @@o are allowed only in the code \
  7153. section; command ignored");
  7154.         next_control = ignore;
  7155.         loc = limit + 1;
  7156.         break;
  7157.  
  7158.        case definition: case undefinition:
  7159.        case WEB_definition:
  7160.         if(!expand) 
  7161.             next_control = ignore;
  7162.         else 
  7163.             {
  7164.             name_pointer np;
  7165.             eight_bits last_control;
  7166.  
  7167.             if((np=app_macro(last_control=next_control))
  7168.                  == NULL) continue; 
  7169.             else if(last_control==WEB_definition)
  7170.                 np->equiv = (EQUIV)cur_text;
  7171.             }
  7172.         break;
  7173.         
  7174.  
  7175.        default:
  7176.         if(next_control <= ignore_defn)
  7177.             break;
  7178.  
  7179.         mlevel--;
  7180.         return;
  7181.         }
  7182.     }    
  7183. }
  7184.         
  7185. @ The following macro implements either |m_ifdef| or |m_ifndef|. The
  7186. argument |compares| should be `|!=|' for |m_ifdef| or `|==|' for |m_ifndef|.
  7187. @d DEF_OR_NDEF(flag) 
  7188.     found_else = NO;
  7189.     if(!expand) 
  7190.         {
  7191.         to_endif(m_ifdef); 
  7192.         goto next_macro_token;
  7193.         }
  7194.     else
  7195.         {
  7196.         text_pointer m;
  7197.         if( (next_control=get_next()) != identifier)
  7198.             {
  7199.             ERR_PRINT(T,"Expected identifier after @@#ifdef \
  7200. or @@#ifndef; assuming not defined");
  7201.             if_switch = NO;
  7202.             }
  7203.         else if_switch =
  7204.           BOOLEAN(flag((m=MAC_LOOKUP(ID_NUM(id_first,id_loc)))!=NULL
  7205.             && !(m->built_in))); 
  7206.     /* Is the identifier defined as a WEB macro? */
  7207.         if(if_switch) 
  7208.             {
  7209.             GET_LINE; /* Skip possible comment at end of
  7210. \.{@@\#ifdef} or \.{@@\#ifndef}. */
  7211.             scan_text(text_type,p,if_switch);
  7212.             }
  7213.         else 
  7214.             {
  7215.             expand=NO; @+ to_else(); 
  7216.  
  7217.             if(next_control != m_endif) 
  7218.                 {
  7219.                 scanned_if = YES;
  7220.                 goto next_macro_token;
  7221.                 }
  7222.             else 
  7223.                 {
  7224.                 next_control = ignore;
  7225.                 expand = YES;
  7226.                 GET_LINE; /* Skip possible comment after
  7227. \.{@@\#endif}. */
  7228.                 break;
  7229.                 }
  7230.             }
  7231.         }
  7232.  
  7233. /* The following were changed from |TRUE| and |FALSE| to avoid difficulties
  7234. with the VAX' \.{cc}.  */
  7235. @d M_TRUE
  7236. @d M_FALSE !
  7237.  
  7238. @<Preprocessor cases@>=
  7239.  
  7240. case m_ifdef:
  7241.     DEF_OR_NDEF(M_TRUE);
  7242.     break;
  7243.  
  7244. case m_ifndef:
  7245.     DEF_OR_NDEF(M_FALSE);
  7246.     break;
  7247.  
  7248. case m_if:
  7249.     found_else = NO;
  7250.  
  7251.     if(!expand) 
  7252.         {
  7253.         to_endif(m_if); 
  7254.         goto next_macro_token;
  7255.         }
  7256.     else 
  7257.         @<Expand an |if| statement@>@;
  7258.  
  7259.     break;
  7260.                 
  7261. case m_elif:
  7262. /* The |elif| is essentially the inverse of |if|. If we were in the midst
  7263. of an expansion, everything else must be skipped until |endif|. This is
  7264. done via |to_endif|; we must process the |endif| again in order to return
  7265. properly from the recursive scan in progress.  If we were not in the midst
  7266. of an expansion, we got here via a |to_else|; we must now proceed just as
  7267. though this were an |if|. */
  7268.     next_control = ignore;
  7269.  
  7270.     if( (mlevel==1 && !scanned_if) || found_else)
  7271.         {
  7272.         OUT_OF_ORDER("elif");
  7273.         break;
  7274.         }
  7275.     
  7276.     scanned_if = NO;
  7277.  
  7278.     if(expand) 
  7279.         {
  7280.         to_endif(m_elif); 
  7281.         goto next_macro_token;
  7282.         }
  7283.     else 
  7284.         @<Expand an |if|...@>@;
  7285.  
  7286.     expand = YES;
  7287.     break;
  7288.  
  7289. case m_else:
  7290. /* When processing an |else|, we take action based on the opposite of the
  7291. |expand| flag currently in effect. If |expand == YES|, we must then skip
  7292. everything else until the |endif|. This is done with |to_end|; we must
  7293. process the |endif| again in order to return properly from the recursion in
  7294. progress when we got here.  If |expand == NO|, we got here via a |to_else|;
  7295. we must now expand everything until the |endif|. */
  7296.     next_control = ignore;
  7297.  
  7298.     if( (mlevel == 1 && !scanned_if) || found_else)
  7299.         {
  7300.         OUT_OF_ORDER("else");
  7301.         break;
  7302.         }
  7303.  
  7304.     found_else = YES;
  7305.     scanned_if = NO;
  7306.  
  7307.     expand = BOOLEAN(!expand);
  7308.  
  7309.     GET_LINE; // Skip possible comment after \.{@@\#else}.
  7310.  
  7311.     if(expand) 
  7312.         scan_text(text_type,p,expand);
  7313.     else 
  7314.         {
  7315.         to_endif(m_else); 
  7316.         expand = YES;
  7317.         goto next_macro_token;
  7318.         }
  7319.  
  7320.     break;
  7321.  
  7322. case m_endif:
  7323.     next_control = ignore;
  7324.  
  7325.     if(mlevel == 1)
  7326.         {
  7327.         OUT_OF_ORDER("endif");
  7328.         break;
  7329.         }
  7330.  
  7331.     found_else = NO;
  7332.     GET_LINE; // Skip possible comment after \.{@@\#endif}.
  7333.     mlevel--;
  7334.     return; // Ends recursion on |scan_text|.
  7335.  
  7336. case m_undef:
  7337.     if(!expand) 
  7338.         next_control = ignore;
  7339.     else
  7340.         {
  7341.         if( (next_control=get_next()) != identifier)
  7342.             ERR_PRINT(M,"Identifier must follow @@#undef");
  7343.         else 
  7344.             {
  7345.             undef(ID_NUM(id_first,id_loc), SILENT);  
  7346.             GET_LINE; /* Skip possible comment at end of
  7347. \.{@@\#undef}. */
  7348.             }
  7349.         }
  7350.     break;
  7351.  
  7352. case m_line:
  7353.     CONFUSION("preprocessor cases", "m_line shouldn't reach here");
  7354.  
  7355. case m_for:
  7356. case m_endfor:
  7357.     if(!expand) next_control = ignore;
  7358.     else
  7359.         {
  7360.           ERR_PRINT(M,"Sorry, preprocessor command isn't implemented yet");
  7361.         }
  7362.     break;
  7363.  
  7364. @
  7365.  
  7366. @d GET_LINE@/
  7367.      if(!from_buffer) 
  7368.         if(language!=TEX)
  7369.             get_line()@;
  7370.  
  7371. @<Expand an |if|...@>=
  7372. {
  7373. @<Evaluate conditional expression and set |if_switch|@>;
  7374. GET_LINE; // Skip possible comment at end of \.{@@\#if}.
  7375.  
  7376. if(if_switch) 
  7377.     scan_text(text_type,p,if_switch);
  7378. else 
  7379.     @<Skip to |else|, |elif|, or |endif|@>@;
  7380. }
  7381.  
  7382. @ We get here when an |if| evaluated to~0. We must skip everything until
  7383. the next |elif|, |else|, or~|endif|. If the |to_else| scan gets to an
  7384. |elif| or |else|, we  go back and evaluate that token again, thus
  7385. continuing the processing. However, if we get directly to an |endif|, this
  7386. was the case |if(0)|\dots|endif|. In this case |scan_text| wasn't called
  7387. recursively at all, so we mustn't return, but should |break| instead.
  7388.  
  7389. @<Skip to |else|...@>=
  7390. {
  7391. expand=NO; @+ to_else(); 
  7392.  
  7393. if(next_control != m_endif)
  7394.     {
  7395.     scanned_if = YES;
  7396.     goto next_macro_token;
  7397.     }
  7398. else 
  7399.     {
  7400.     next_control = ignore;
  7401.     expand = YES;
  7402.     GET_LINE; // Skip possible comment after \.{@@\#endif}
  7403.     break;
  7404.     }
  7405. }
  7406.  
  7407. @ An error message for out-of-order preprocessor commands.
  7408.  
  7409. @d OUT_OF_ORDER(cmd) out_of_order((outer_char *)cmd)
  7410.  
  7411. @<Part 3@>=@[
  7412.  
  7413. SRTN out_of_order FCN((cmd))
  7414.     CONST outer_char cmd[] C1("Name of bad preprocessor command.")@;
  7415. {
  7416. err_print(M,"Ignored out-of-order \"@@#%s\" (mlevel = %d)",cmd,mlevel);
  7417. }
  7418.  
  7419. @ We get here when we're not supposed to expand the stuff after an
  7420. \&{@@\#elif}.  We must scan without expanding to the next \&{@@\#elif},
  7421. \&{@@\#else}, or \&{@@\#endif}, taking into account the possibility of
  7422. further nested \&{@@\#if}\dots\&{@@\#endif} combinations.
  7423.  
  7424. @<Part 3@>=@[
  7425.  
  7426. SRTN to_else(VOID)
  7427. {
  7428. int elevel = 0,elifs[MAX_LEVEL],elses[MAX_LEVEL],k;
  7429.  
  7430. for(k=0; k<MAX_LEVEL; k++)
  7431.     elifs[k] = elses[k] = 0;
  7432.  
  7433. WHILE()
  7434.     switch(next_control=skip_ahead(next_control,NO))
  7435.         {
  7436.         case m_if:
  7437.         case m_ifdef:
  7438.         case m_ifndef:
  7439.             elevel++;
  7440.             break;
  7441.  
  7442.         case m_elif:
  7443.             if(elses[elevel]) 
  7444.                    ERR_PRINT(M,"Can't have @@#elif after @@#else");
  7445.             elifs[elevel]++;
  7446.             if(elevel==0) return;
  7447.             break;
  7448.  
  7449.         case m_else:
  7450.             if(elses[elevel]++) 
  7451.                 ERR_PRINT(M,"Only one @@#else allowed \
  7452. (scanning to @@else)");
  7453.             if(elevel==0) 
  7454.                 {
  7455.                 if(language==TEX && !get_line())
  7456.                     loc = limit + 1;
  7457.                 return;
  7458.                 }
  7459.             break;
  7460.  
  7461.         case m_endif:
  7462.             
  7463.             elifs[elevel] = elses[elevel] = 0;
  7464.  
  7465.             if(elevel-- == 0) 
  7466.                 {
  7467.                 found_else = NO;
  7468.                 if(language==TEX && !get_line())
  7469.                     loc = limit + 1;
  7470.                 return;
  7471.                 }
  7472.             break;
  7473.  
  7474.         case new_module:
  7475.             err_print(M,"Section ended during scan for \
  7476. \"@@#else\", \"@@#elif\", or \"@@#endif\". Inserted \"@@#endif\". \
  7477. (elevel = %d)",elevel);
  7478.             if(elevel == 0)    
  7479.                 found_else = NO;
  7480.  
  7481.             return;// The |new_module| is turned into an |m_endif|.
  7482.         }
  7483. }
  7484.  
  7485. @ The function is similar to |to_else|, but we're scanning to an \&{endif}.
  7486. @<Part 3@>=@[
  7487.  
  7488. SRTN to_endif FCN((m_case))
  7489.     int m_case C1("Case that called to_endif")@;
  7490. {
  7491. int elevel = 1,elifs[MAX_LEVEL],elses[MAX_LEVEL],k;
  7492.  
  7493. for(k=0; k<MAX_LEVEL; k++)
  7494.     elifs[k] = elses[k] = 0;
  7495.  
  7496. if(m_case==m_elif) 
  7497.     elifs[elevel] = 1;
  7498. else 
  7499.     {
  7500.     if(m_case==m_else) 
  7501.         elses[elevel] = 1;
  7502.     }
  7503.  
  7504. WHILE()
  7505.     switch(next_control=skip_ahead(next_control,NO))
  7506.         {
  7507.         case m_if:
  7508.         case m_ifdef:
  7509.         case m_ifndef:
  7510.             elevel++;
  7511.             break;
  7512.  
  7513.         case m_elif:
  7514.             if(elses[elevel]) 
  7515.                    ERR_PRINT(M,"Can't have @@#elif after @@#else");
  7516.             elifs[elevel]++;
  7517.             break;
  7518.  
  7519.         case m_else:
  7520.             if(elses[elevel]++) 
  7521.                 ERR_PRINT(M,"Only one @@#else allowed \
  7522. (scanning to @@endif)");
  7523.             break;
  7524.  
  7525.         case m_endif:
  7526.  
  7527.             elifs[elevel] = elses[elevel] = 0;
  7528.  
  7529.             if(--elevel == 0) 
  7530.                 {
  7531.                 found_else = NO;
  7532.                 if(language==TEX && !get_line())
  7533.                     loc = limit + 1;
  7534.                 return;
  7535.                 }
  7536.             break;
  7537.  
  7538.         case new_module:
  7539.             err_print(M,"Section ended during scan for \
  7540. \"endif\"; inserted \"endif\". (elevel = %d)",elevel);
  7541.             if(elevel == 0) 
  7542.                 found_else = NO;
  7543.             return;
  7544.         }
  7545. }
  7546.  
  7547. @ This fragment evaluates the argument to an \.{@@\#if} or \.{@@\#elif}.
  7548. @<Evaluate conditional...@>=
  7549. {
  7550. boolean scan0 = scanning_defn;
  7551.  
  7552. scanning_defn = YES;
  7553.     scan_repl(macro,STOP);
  7554. scanning_defn = scan0;
  7555.  
  7556. cur_text->nargs = UNDEFINED_MACRO;
  7557.  
  7558. pp = xmac_text(macrobuf,cur_text->tok_start,tok_ptr); // See \.{macs.web}.
  7559. if_switch = eval(pp,mp); // See \.{eval.web}.
  7560. }
  7561.  
  7562. @ Handle a macro definition.  \WEB\ macro definitions may have the special
  7563. forms~\.{@@m*} or~\.{@@m[\dots]}.  The asterisk indicates a recursive macro
  7564. (not implemented yet).  The \.{[\dots]} construction signifies automatic
  7565. insertion material; see the next module for more details.
  7566.  
  7567. @<Part 3@>=@[
  7568.  
  7569. name_pointer app_macro FCN((last_control))
  7570.     eight_bits last_control C1("Last token processed.")@;
  7571. {
  7572. sixteen_bits a;
  7573. name_pointer np = NULL;
  7574. boolean make_recursive = NO;
  7575. ASCII insert_type[6];
  7576. int insert_num = 0;
  7577. eight_bits temp[2]; // Holds the macro identifier.
  7578. boolean nuweb_mode0 = nuweb_mode;
  7579.  
  7580. nuweb_mode = NO; // Don't parse the beginning of macro defn's literally.
  7581.  
  7582. is_WEB_macro = BOOLEAN(last_control==WEB_definition);
  7583.  
  7584. if(is_WEB_macro || C_LIKE(language))
  7585.     {
  7586.       while ((next_control=get_next())==@'\n')
  7587.         ; // Allow definition to start on  next line.
  7588.  
  7589.     if(is_WEB_macro)
  7590.         if(next_control == MAKE_RECURSIVE) 
  7591.             {
  7592.             make_recursive = YES;
  7593.             next_control=get_next();
  7594.             }
  7595.         else if(next_control == AUTO_INSERT)
  7596.             @<Set up auto insertion@>@;
  7597.  
  7598.       if (next_control!= identifier)
  7599.         {
  7600.         err_print(M,"Definition flushed in %s; must start with \
  7601. identifier", MTRANS); 
  7602. @.Definition flushed...@>
  7603.         np = NULL;
  7604.         goto done_append;
  7605.         }
  7606.  
  7607.     a = ID_NUM_ptr(np,id_first,id_loc); // The identifier.
  7608.  
  7609. /* Process auto insertion. */
  7610.     temp[0] = LEFT(a,ID0); @+ temp[1] = RIGHT(a);
  7611.     @<Store auto insertion@>@;
  7612.  
  7613. /* Append the lhs. */
  7614.     app_repl(temp[0]);
  7615.     app_repl(temp[1]);
  7616.  
  7617.     np->macro_type = IMMEDIATE_MACRO;
  7618. /* Mark this name as a macro. |macro_type| isn't otherwise used by \Tangle. */
  7619.  
  7620.       if (*loc!=@'(') 
  7621.         {
  7622.         if(is_WEB_macro) 
  7623.             {app_repl(@' ');}
  7624.             else if(C_LIKE(language))
  7625.             { /* For outer macros, identifier must be separated
  7626. from replacement text */ 
  7627.                 app_repl(stringg); app_repl(@' '); app_repl(stringg);
  7628.             }
  7629.         }
  7630.     }
  7631.  
  7632. nuweb_mode = nuweb_mode0;
  7633. scan_repl((eight_bits)macro,(boolean)(!scanning_defn)); /* Copy stuff
  7634.             verbatim. (Also sets the language.) */ 
  7635.  
  7636. /* Finish off storing the macro. */
  7637. if(is_WEB_macro) 
  7638.     @<Argize a \.{WEB} macro@>@;
  7639. else 
  7640.     cur_text->nargs = (eight_bits)CHOICE(last_control==definition, 
  7641.         OUTER_MACRO, OUTER_UNMACRO); // Mark the outer macros.
  7642.  
  7643. cur_text->text_link = macro; // |text_link=0| characterizes a macro.
  7644.  
  7645. done_append:
  7646.     is_WEB_macro = NO;    // Reset.
  7647.     return np;
  7648. }
  7649.  
  7650. @ \WEB\ macro definitions may begin with the construction \.{@@m[*]} or
  7651. \.{@@m[pmsfbi]}, indicating that in \Ratfor\ this macro is to be output
  7652. automatically after one or more of the program units |program|, |module|,
  7653. |subroutine|, |function|, |blockdata|, and |interface|.
  7654. @<Set up auto insert...@>=
  7655. {
  7656. ASCII c;
  7657.  
  7658. while((c= *loc++)!=END_AUTO_INSERT)
  7659.     {
  7660.     if(*loc == @' ')
  7661.         {
  7662.         ERR_PRINT(M,"Found space instead of ']' after automatic \
  7663. insertion material");
  7664.         break;
  7665.         }
  7666.  
  7667.     if(loc == limit) break;
  7668.  
  7669.     if(insert_num >= 6) 
  7670.         {
  7671.         if(insert_num++ == 6)
  7672.            ERR_PRINT(M,"Can't have more than 6 types of automatic \
  7673. insertion material; remaining ignored");
  7674.         continue;
  7675.         }
  7676.  
  7677.     switch(c)
  7678.         {
  7679.        case @'*':
  7680.         STRNCPY(insert_type,"pmsfbi",insert_num=6);
  7681.         break;
  7682.  
  7683.        case @'p': case @'P':
  7684.        case @'m': case @'M':
  7685.        case @'s': case @'S':
  7686.        case @'f': case @'F':
  7687.        case @'b': case @'B':
  7688.        case @'i': case @'I':
  7689.         insert_type[insert_num++] = c;
  7690.         break;
  7691.  
  7692.        default:
  7693.         ERR_PRINT(M,"Auto insertion type must be one of \
  7694. \"ibfmps\"");
  7695.         continue;
  7696.         }
  7697.     }
  7698.  
  7699. next_control = get_next();
  7700. }
  7701.  
  7702. @ Here we save the macro identifier of automatic insertion material.  
  7703.  
  7704. @m SAVE_AUTO(type) if(insert.type.end > insert.type.start)
  7705.     err_print(M,"Overriding previous auto insertion type %s",#type);
  7706.     STRNCPY(insert.type.start,temp,2);
  7707.     insert.type.end = insert.type.start + 2@;
  7708.  
  7709. @<Store auto insert...@>=
  7710. {
  7711. while(insert_num-- > 0)
  7712.     switch(insert_type[insert_num])
  7713.         {
  7714.        case @'p': case @'P':
  7715.         SAVE_AUTO(program);
  7716.         break;
  7717.  
  7718.        case @'m': case @'M':
  7719.         SAVE_AUTO(module);
  7720.         break;
  7721.  
  7722.        case @'s': case @'S':
  7723.         SAVE_AUTO(subroutine);
  7724.         break;
  7725.  
  7726.        case @'f': case @'F':
  7727.         SAVE_AUTO(function);
  7728.         break;
  7729.  
  7730.        case @'b': case @'B':
  7731.         SAVE_AUTO(blockdata);
  7732.         break;
  7733.  
  7734.        case @'i': case @'I':
  7735.         SAVE_AUTO(interface);
  7736.         break;
  7737.         }
  7738. }
  7739.  
  7740. @ Put argument tokens into the token list for a WEB macro, and also strip
  7741. off newlines.
  7742. @<Argize a...@>=
  7743. {
  7744. text_ptr->tok_start = tok_ptr = argize(cur_text->tok_start,tok_ptr); /* Set
  7745.     new end by possibly stripping off newlines. */ 
  7746. cur_text->Language = (boolean)global_language; // This value shouldn't matter.
  7747. cur_text->recursive = make_recursive;
  7748. }
  7749.  
  7750. @ Similarly, in order to implement the built-in command |$DEFINE|, we need
  7751. to store a macro definition that has already been fully tokenized.
  7752. @<Part 3@>=@[
  7753.  
  7754. SRTN app_dmacro FCN((p,p1))
  7755.     CONST eight_bits HUGE *p C0("Start")@;
  7756.     CONST eight_bits HUGE *p1 C1("End.")@;
  7757. {
  7758. eight_bits a0,a1;
  7759. sixteen_bits a;
  7760. name_pointer np;
  7761. boolean make_recursive = NO;
  7762.  
  7763. if(*p == MAKE_RECURSIVE)
  7764.     {
  7765.     make_recursive = YES;
  7766.     p++;
  7767.     }
  7768.  
  7769. if(p+2 > p1)
  7770.     {
  7771.     MACRO_ERR("! Invalid argument to $DEFINE",YES);
  7772.     return;
  7773.     }
  7774.  
  7775. if(TOKEN1(a0 = *p++))
  7776.     {
  7777.     MACRO_ERR("! $DEFINE flushed; must start with identifier",YES);
  7778.     return;
  7779.     }
  7780.  
  7781. a = IDENTIFIER(a0,a1 = *p++);
  7782. app_repl(a0);
  7783. app_repl(a1);
  7784.  
  7785. np = name_dir + a;
  7786.  
  7787. np->macro_type = IMMEDIATE_MACRO;
  7788.  
  7789. if(*p == @'=') {p++; app_repl(@' ');} // Allow for zero-argument macro.
  7790.  
  7791. while(p < p1) {app_repl(*p++);}
  7792.  
  7793. @<Make |cur_text...@>;
  7794. @<Argize a...@>;
  7795. cur_text->text_link = macro;
  7796. np->equiv = (EQUIV)cur_text;
  7797. }
  7798.  
  7799. @ In terms of |app_dmacro|, we can also implement a built-in |$DEFINE|
  7800. command (|$M| for short).  It just appends a deferred macro.
  7801.  
  7802. @<Part 3@>=@[
  7803.  
  7804. SRTN i_define_ FCN((n,pargs))
  7805.     int n C0("")@;
  7806.     PARGS pargs C1("")@;
  7807. {
  7808. CHK_ARGS("$M",1);
  7809.  
  7810. app_dmacro(pargs[0]+1,pargs[1]);
  7811. }
  7812.  
  7813. @ We also need an |$UNDEF| command.
  7814.  
  7815. @<Part 3@>=@[
  7816.  
  7817. SRTN i_undef_ FCN((n,pargs))
  7818.     int n C0("")@;
  7819.     PARGS pargs C1("")@;
  7820. {
  7821. eight_bits a0;
  7822. eight_bits HUGE *p = pargs[0]+1;
  7823.  
  7824. CHK_ARGS("$UNDEF",1);
  7825.  
  7826. if(p+2 > pargs[1])
  7827.     {
  7828.     MACRO_ERR("! Invalid argument to $UNDEF(...)",YES);
  7829.     return;
  7830.     }
  7831.  
  7832. if(TOKEN1(a0 = *p++))
  7833.     {
  7834.     MACRO_ERR("! $UNDEF(...) flushed; must start with identifier",YES);
  7835.     return;
  7836.     }
  7837.  
  7838. undef(IDENTIFIER(a0,*p), NO);
  7839. }
  7840.  
  7841. @ We can now build in some simple arithmetic macros.
  7842. @<Define internal macros@>=
  7843.  
  7844. SAVE_MACRO("_INCR(N,...)$INCR0(#!N,$EVAL(N+$IFELSE(#0,0,1,#1)))");
  7845. SAVE_MACRO("$INCR(N,...)$INCR0(#!N,$EVAL(N+$IFELSE(#0,0,1,#1)))");
  7846.  
  7847. SAVE_MACRO("_DECR(N,...)$INCR0(#!N,$EVAL(N-$IFELSE(#0,0,1,#1)))");
  7848. SAVE_MACRO("$DECR(N,...)$INCR0(#!N,$EVAL(N-$IFELSE(#0,0,1,#1)))");
  7849.  
  7850. SAVE_MACRO("_INCR0(N,N1)$M(#!N N1)");
  7851. SAVE_MACRO("$INCR0(N,N1)$M(#!N N1)");
  7852.  
  7853. @<Scan the code...@>=
  7854. switch (next_control) 
  7855.     {
  7856.   case begin_code: /* \.{@@a} */
  7857.     {
  7858.     boolean nuweb_mode0 = nuweb_mode;
  7859.  
  7860.     params = global_params; // The unnamed module has the global state.
  7861.     nuweb_mode = nuweb_mode0;
  7862.     frz_params();
  7863.     set_output_file(global_language);
  7864.  
  7865.     p = name_dir; 
  7866.     @<Start column mode.@>;
  7867.     break;
  7868.     }
  7869.  
  7870.   case module_name: /* \.{@@<} */
  7871.     if(cur_module) 
  7872.         {
  7873.         p = cur_module;
  7874.         params = cur_module->mod_info->params; // Restore state.
  7875.         }
  7876.     else
  7877.         { // We get here if the module name was bad.
  7878. @#if 0
  7879.         ERR_PRINT(T,"Code placed into unnamed module");
  7880.         p = name_dir;
  7881.         params = global_params;
  7882. @#endif
  7883. /* The above wasn't a good idea.  It's better to flush the module. */
  7884.         while( (next_control=skip_ahead(ignore,NO)) != new_module)
  7885.             ;
  7886.         return;
  7887.         }
  7888.  
  7889.     @<Determine optional parameter list of module name@>@;
  7890.  
  7891.       @<Check that |=| or |==| follows this module name, otherwise |return|@>; 
  7892.     frz_params();
  7893.     @<Start column mode.@>;
  7894.     break;
  7895.  
  7896.   default: return;
  7897.     }
  7898.  
  7899. next_control = ignore;
  7900. scan_text(module_name,p,EXPAND); // Expand the code section.
  7901. column_mode = NO;
  7902.  
  7903.  
  7904. @ Module names may be followed by an optional parameter list, like \Cpp\
  7905. templates:  \.{@@< Name @@><$p_0$, $p_1$, \dots, $p_{n-1}$>}.  (Unfinished!!)
  7906.  
  7907. @<Determine optional parameter list of module name@>=
  7908. {
  7909. }
  7910.  
  7911. @
  7912. @<Argize the module@>=
  7913. {
  7914. }
  7915.  
  7916. @<Check that |=|...@>=
  7917. {
  7918. while ((next_control=get_next()) == @'+')
  7919.     ; // Allow optional `\.{+=}".
  7920.  
  7921. if (next_control != @'=' && next_control != eq_eq) 
  7922.     {
  7923.     err_print(T,"Code text of %s flushed; = sign is missing", MTRANS);
  7924. @.Code text flushed...@>
  7925.  
  7926.     while ((next_control=skip_ahead(ignore,NO)) != new_module)
  7927.         ;
  7928.  
  7929.     return;
  7930.     }
  7931. }
  7932.  
  7933. @ When starting a Fortran code section, skip everything after the equals
  7934. sign so we start off fresh in the column mode.
  7935. @<Start column mode.@>=
  7936.  
  7937. if(FORTRAN_LIKE(language) && !free_form_input)
  7938.     @<Set up column mode@>@;
  7939.  
  7940. @ Prepare for \Fortran's idiotic syntax.
  7941. @<Set up col...@>=
  7942. {
  7943. loc = limit+1;
  7944. column_mode = YES;
  7945. parsing_mode = OUTER;
  7946. }
  7947.  
  7948. @<Insert the module number...@>=
  7949. {
  7950. store_two_bytes((sixteen_bits)(LINE_NUM+module_count)); 
  7951. }
  7952.  
  7953. @<Update the data...@>=
  7954. {
  7955. if(p==name_dir || p==NULL) 
  7956.     { /* Unnamed module, or bad module name */
  7957.     cur_text->module_text = (first_text && mlevel==1);
  7958.  
  7959. /* The unnamed module begins in the global language.  However, subsequent
  7960. language changes within one section---e.g., by preprocessing---should be
  7961. retained. */
  7962.     if(cur_text->module_text)
  7963.         cur_text->Language = (boolean)global_language;
  7964.  
  7965.     last_unnamed->text_link = (sixteen_bits)(cur_text - text_info); 
  7966.          // Link the unnamed module together.
  7967.     last_unnamed = cur_text; // Present end of the unnamed module.
  7968.     }
  7969. else if (p->equiv==(EQUIV)text_info) 
  7970.     { /* First module of this name. */
  7971.     cur_text->module_text = YES;
  7972.     p->equiv = (EQUIV)cur_text;
  7973.     }
  7974. else 
  7975.     { /* Link on the |cur_text| to the linked list. */
  7976.     LANGUAGE language0;
  7977.  
  7978.     q = (text_pointer)p->equiv; // Start of the chain.
  7979.     language0 = (LANGUAGE)q->Language; // Global language of this module.
  7980.  
  7981. /* Each replacement text (for a module name) has the same language as the
  7982. first in the chain.  Thus language switching works very efficiently;
  7983. modules inherit the language of their superior.  On the other hand,
  7984. preprocessor fragments should retain the current language, as should the
  7985. fragment following a preprocessor fragment. */
  7986.     cur_text->module_text = (first_text && mlevel==1);
  7987.  
  7988.     if(cur_text->module_text) 
  7989.         cur_text->Language = (boolean)language0;
  7990.  
  7991. /* Find end of list, delimited by |module_flag|.  (There's nothing
  7992. comparable to |last_unnamed| to tell us where the end is.) */
  7993.     while (q->text_link < module_flag) q = q->text_link + text_info; 
  7994.  
  7995.     q->text_link = (sixteen_bits)(cur_text - text_info);
  7996.         // Append more stuff to this module by linking in |cur_text|.
  7997.     }
  7998.  
  7999. /* |cur_text| has now been linked to the end of the appropriate chain.  Use
  8000. |module_flag| as a special |text_link| to signify the end of the list. */
  8001. cur_text->text_link = module_flag;
  8002. }
  8003.  
  8004. @ In phase~1, we skip the limbo section, set the global language, then
  8005. process each module in turn. 
  8006. @<Part 3@>=@[
  8007.  
  8008. SRTN phase1(VOID) 
  8009. {
  8010. LANGUAGE language0=language;
  8011.  
  8012. phase = 1;
  8013. module_count = 0;
  8014. rst_input(); rst_out(NOT_CONTINUATION);
  8015. reading(web_file_name,NO);
  8016.  
  8017. while ((next_control=skip_ahead(ignore,NO))!=new_module)
  8018.     ; // Skip stuff before first module.  This may reset the language.
  8019.  
  8020. chk_override(language0);
  8021. fin_language(); /* Make sure flags are initialized properly. */
  8022. global_params = params; /* Remember the global parameters. */
  8023. set_output_file(global_language); /* Language in force at the
  8024.                 beginning of each module. */ 
  8025.  
  8026. while (!input_has_ended) 
  8027.     scan_module(); // Do each module one at a time.
  8028.  
  8029. chk_complete(); // Anything left in change file?
  8030. @<Count the distinct modules@>@;
  8031. }
  8032.  
  8033. @ Here we set a global variable to the number of distinct modules. This is
  8034. used later in the expansion of the built-in macro |$MODULES|. The total
  8035. number of sections is also remembered, for use in the built-in |$SECTIONS|.
  8036. @<Glob...@>=
  8037.  
  8038. EXTERN sixteen_bits num_distinct_modules SET(1); // Count the unnamed module.
  8039. EXTERN sixteen_bits num_modules;
  8040.  
  8041. @
  8042. @<Count the distinct...@>=
  8043. @{
  8044. name_pointer np;
  8045.  
  8046. @b
  8047. for(np=name_dir; np<name_ptr; np++)
  8048.        if(np->equiv != NULL && np->equiv != (EQUIV)text_info
  8049.         && np->macro_type==NOT_DEFINED)
  8050.             num_distinct_modules++; 
  8051.  
  8052. num_modules = module_count;
  8053. }
  8054.  
  8055. @ Here we  define a built-in macro that expands into the number of distinct
  8056. modules. 
  8057. @<Define internal...@>=
  8058.  
  8059. SAVE_MACRO("_MODULES $$MODULES(0)");
  8060. SAVE_MACRO("$MODULES $$MODULES(0)");
  8061.  
  8062. SAVE_MACRO("_SECTIONS $$MODULES(1)");
  8063. SAVE_MACRO("$SECTIONS $$MODULES(1)");
  8064.  
  8065. @
  8066. @<Part 3@>=@[
  8067.  
  8068. SRTN i_modules_ FCN((n,pargs))
  8069.     int n C0("")@;
  8070.     PARGS pargs C1("")@;
  8071. {
  8072. outer_char temp[50];
  8073. int m=NSPRINTF(temp,"%c%u%c",XCHR(constant),
  8074.     *(pargs[0]+2) == '0' ? num_distinct_modules : num_modules,
  8075.     XCHR(constant));
  8076.  
  8077. CHK_ARGS("$MODULES",1);
  8078.  
  8079. MCHECK(m,"_modules_");
  8080. STRCPY(mp,to_ASCII(temp));
  8081. mp += m;
  8082. }
  8083.  
  8084. @ Print statistics at end of \FTANGLE's run.
  8085. @<Part 3@>=
  8086.  
  8087. SRTN see_tstatistics(VOID)
  8088. {
  8089. CLR_PRINTF(info,("\n\nMEMORY USAGE STATISTICS:\n"));
  8090. STAT0("names",sizeof(*name_ptr),
  8091.     SUB_PTRS(name_ptr,name_dir),max_names,UPPER(max_names),",");
  8092.  
  8093. STAT0("replacement texts",sizeof(*text_ptr),
  8094.     SUB_PTRS(text_ptr,text_info),max_texts,UPPER(max_texts),",");
  8095.  
  8096. STAT0("deferred texts",sizeof(*txt_dptr),
  8097.     SUB_PTRS(txt_dptr,txt_dinfo),dtexts_max,UPPER(dtexts_max),";");
  8098.  
  8099. STAT0("bytes",sizeof(*byte_ptr),
  8100.     SUB_PTRS(byte_ptr,byte_mem),max_bytes,UPPER(max_bytes),",");
  8101.  
  8102. STAT0("tokens",sizeof(*tok_ptr),
  8103.     SUB_PTRS((mx_tok_ptr > tok_ptr ? mx_tok_ptr : tok_ptr),tok_mem),
  8104.         max_toks,UPPER(max_toks_t),",");
  8105.  
  8106. STAT0("deferred tokens",sizeof(*tok_dptr),
  8107.     SUB_PTRS((mx_dtok_ptr > tok_dptr ? mx_dtok_ptr : tok_dptr),tok_dmem),
  8108.         max_dtoks,UPPER(max_dtoks),".");
  8109.  
  8110. mem_avail(1); /* How much memory left at end of run. */
  8111. }
  8112.  
  8113. @ This is an interface to |predefine_macros| in \.{macs.web}.
  8114. @<Part 3@>=@[
  8115.  
  8116. SRTN t_macros(VOID)
  8117. {
  8118. @<Define internal...@>;
  8119. }
  8120.  
  8121. @ Send a commented message to the output file.
  8122.  
  8123. In some cases, the message we want to send might involve fragments of
  8124. code that have to be translated. Therefore, we first use |str_to_mb| to
  8125. detokenize the message, then we ship it out in the form of a meta-comment.
  8126.  
  8127. @d SPCS_AFTER_CMNT 1 // For beautification of the Ratfor error messages.
  8128.  
  8129. @<Glob...@>=
  8130.  
  8131. #if SMALL_MEMORY
  8132.     #define MSG_BUF_SIZE 5000
  8133. #else
  8134.     #define MSG_BUF_SIZE 50000L
  8135. #endif
  8136.  
  8137. @
  8138. @<Part 3@>=@[
  8139.  
  8140. SRTN out_msg FCN((msg,msg1))
  8141.     CONST ASCII *msg C0("Start of message.")@;
  8142.     CONST ASCII *msg1 C1("See the description below.")@;
  8143. {
  8144. eight_bits HUGE *temp;
  8145. eight_bits HUGE *mp0 = mp,
  8146.     HUGE *macrobuf0 = macrobuf, HUGE *macrobuf_end0 = macrobuf_end;
  8147. char HUGE *new_msg; // The translated message.
  8148. boolean nuweb_mode0,in_string0,meta_mode0;
  8149.  
  8150. /* Translate the message, which may contain identifiers, into the |macrobuf|.*/
  8151. mp = macrobuf = temp = GET_MEM("out_msg:temp",MSG_BUF_SIZE,eight_bits);
  8152. macrobuf_end = temp + MSG_BUF_SIZE;
  8153.  
  8154. /* If |msg1 != NULL|, then it denotes the end of the array. If it is
  8155. |NULL|, we assume it's an ordinary character string and determine the end. */
  8156. if(msg1==NULL) 
  8157.     msg1 = msg + STRLEN(msg);
  8158.  
  8159. new_msg = (char HUGE *)str_to_mb((eight_bits HUGE *)msg,
  8160.                 (eight_bits HUGE *)msg1,NO);
  8161.  
  8162. /* Ship it out in the form of a meta-comment. */
  8163. spcs_after_cmnt = SPCS_AFTER_CMNT;
  8164.  
  8165. /* We bracket the output message by a standard set of |nuweb_mode|,
  8166. |meta_mode|, and |in_string| in order that the top, bottom, and prefix
  8167. fields work correctly. */
  8168. nuweb_mode0 = nuweb_mode;
  8169. in_string0 = in_string;
  8170. meta_mode0 = meta_mode;
  8171.  
  8172. meta_mode = nuweb_mode = NO; 
  8173. @%in_string = YES;
  8174.  
  8175. OUT_CHAR(begin_meta); 
  8176. OUT_CHAR(begin_meta); // Second one turns off |xpn_Ratfor|.
  8177.       while(*new_msg) 
  8178.         OUT_CHAR(*new_msg++);
  8179. OUT_CHAR(end_meta);
  8180.  
  8181. nuweb_mode = nuweb_mode0;
  8182. in_string = in_string0;
  8183. meta_mode = meta_mode0;
  8184.  
  8185. spcs_after_cmnt = 0;
  8186.  
  8187. FREE_MEM(temp,"out_msg:temp",MSG_BUF_SIZE,eight_bits);
  8188. macrobuf = macrobuf0; @+ mp = mp0; @+ macrobuf_end = macrobuf_end0;
  8189. }
  8190.  
  8191. @
  8192. @<Part 3@>=
  8193.  
  8194. static sixteen_bits id_unroll;
  8195.  
  8196. SPEC univ_tokens[] = {
  8197.     {"_UNROLL",0,x_unroll,&id_unroll},
  8198.     {"$UNROLL",0,x_unroll,&id_unroll},
  8199.     {"",0,NULL,NULL}
  8200.     };
  8201.  
  8202. SRTN ini_univ_tokens FCN((language0))
  8203.     LANGUAGE language0 C1("")@;
  8204. {
  8205. ini_special_tokens(language0,univ_tokens);
  8206. }
  8207.  
  8208. @
  8209. @<Define internal macros@>=
  8210.  
  8211. SAVE_MACRO("$DO(k,kmin,kmax,...)$UNROLL(k,kmin,kmax,$IFCASE(#0,1,#.))");
  8212.  
  8213. @<Part 3@>=@[
  8214.  
  8215. SRTN ini_tokens FCN((language0))
  8216.     LANGUAGE language0 C1("")@;
  8217. {
  8218. switch(language0)
  8219.     {
  8220.    case C:
  8221.     break;
  8222.  
  8223.    case C_PLUS_PLUS:
  8224.     break;
  8225.  
  8226.    case FORTRAN:
  8227.     break;
  8228.  
  8229.    case FORTRAN_90:
  8230.     break;
  8231.  
  8232.    case TEX:
  8233.     break;
  8234.  
  8235.    default:
  8236.     break;
  8237.     }
  8238.  
  8239. ini_univ_tokens(language0);
  8240. }
  8241.  
  8242. @ Get the numerical value of a WEB |constant| string.
  8243. @<Part 3@>=@[
  8244.  
  8245. int get_constant FCN((e))
  8246.     eight_bits HUGE *e C1("")@;
  8247. {
  8248. boolean positive = YES;
  8249. int i = 1; // To prevent the increment from being~0 when an error occurs.
  8250.  
  8251. if(*e == @'-')
  8252.     {
  8253.     positive = NO;
  8254.     e++;
  8255.     }
  8256.  
  8257. if(*e++ != constant) 
  8258.     {
  8259.     ERR_PRINT(T,"Invalid loop constant");
  8260.     return i;
  8261.     }
  8262.  
  8263. to_outer(e);
  8264. i = ATOI(e);
  8265. return (positive) ? i : -i;
  8266. }
  8267.  
  8268.  
  8269.  
  8270. @* STYLE FILE. The style file is common to \FWEAVE\ and \FTANGLE. See
  8271. \.{style.web}. 
  8272.  
  8273. @<Include...@>=
  8274.  
  8275. #include "map.h" // Relations between style file keywords and internal arrays.
  8276.  
  8277. @* INDEX.  Here is a cross-reference table for the \.{TANGLE} processor.
  8278. All modules in which an identifier is used are listed with that identifier,
  8279. except that reserved words are indexed only when they appear in format
  8280. definitions, and the appearances of identifiers in module names are not
  8281. indexed. Underlined entries correspond to where the identifier was
  8282. declared. Error messages and a few other things like ``ASCII code'' are
  8283. indexed here too.
  8284.