home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / unix / volume26 / funnlweb / part17 < prev    next >
Encoding:
Text File  |  1993-04-10  |  71.0 KB  |  1,873 lines

  1. Newsgroups: comp.sources.unix
  2. From: ross@spam.adelaide.edu.au (Ross Williams)
  3. Subject: v26i137: funnelweb - a tool for literate programming in C, Part17/20
  4. Sender: unix-sources-moderator@vix.com
  5. Approved: paul@vix.com
  6.  
  7. Submitted-By: ross@spam.adelaide.edu.au (Ross Williams)
  8. Posting-Number: Volume 26, Issue 137
  9. Archive-Name: funnelweb/part17
  10.  
  11. #! /bin/sh
  12. # This is a shell archive.  Remove anything before this line, then unpack
  13. # it by saving it into a file and typing "sh file".  To overwrite existing
  14. # files, type "sh file -c".  You can also feed this as standard input via
  15. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  16. # will see the following message at the end:
  17. #        "End of archive 17 (of 20)."
  18. # Contents:  sources/scanner.c
  19. # Wrapped by vixie@gw.home.vix.com on Sun Apr 11 11:00:33 1993
  20. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  21. if test -f 'sources/scanner.c' -a "${1}" != "-c" ; then 
  22.   echo shar: Will not clobber existing file \"'sources/scanner.c'\"
  23. else
  24. echo shar: Extracting \"'sources/scanner.c'\" \(69225 characters\)
  25. sed "s/^X//" >'sources/scanner.c' <<'END_OF_FILE'
  26. X/*##############################################################################
  27. X
  28. XFUNNNELWEB COPYRIGHT
  29. X====================
  30. XFunnelWeb is a literate-programming macro preprocessor.
  31. X
  32. Copyright (C) 1992 Ross N. Williams.
  33. X
  34. X   Ross N. Williams
  35. X   ross@spam.adelaide.edu.au
  36. X   16 Lerwick Avenue, Hazelwood Park 5066, Australia.
  37. X
  38. This program is free software; you can redistribute it and/or modify
  39. it under the terms of Version 2 of the GNU General Public License as
  40. published by the Free Software Foundation.
  41. X
  42. This program is distributed WITHOUT ANY WARRANTY; without even the implied
  43. warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  44. See Version 2 of the GNU General Public License for more details.
  45. X
  46. You should have received a copy of Version 2 of the GNU General Public
  47. License along with this program. If not, you can FTP the license from
  48. prep.ai.mit.edu/pub/gnu/COPYING-2 or write to the Free Software
  49. XFoundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  50. X
  51. Section 2a of the license requires that all changes to this file be
  52. recorded prominently in this file. Please record all changes here.
  53. X
  54. Programmers:
  55. X   RNW  Ross N. Williams  ross@spam.adelaide.edu.au
  56. X
  57. Changes:
  58. X   07-May-1992  RNW  Program prepared for release under GNU GPL V2.
  59. X
  60. X##############################################################################*/
  61. X
  62. X
  63. X/******************************************************************************/
  64. X/*                                   SCANNER.C                                */
  65. X/******************************************************************************/
  66. X/*                                                                            */
  67. X/* Introduction                                                               */
  68. X/* ------------                                                               */
  69. X/* The FunnelWeb scanner is a little messy because it deals with two          */
  70. X/* structures at the same time while attempting to be efficient. On the one   */
  71. X/* hand it is busy constructing the line list. This means that it has to keep */
  72. X/* an eye out for end of line characters ('\n'=EOL) so that it can add a line */
  73. X/* record whenever it sees one. On the other hand, it has to scan the input   */
  74. X/* file into a token stream consisting of text tokens and special tokens      */
  75. X/* which usually have no regard for end of lines. It is tempting to divide    */
  76. X/* these two functions up (into perhaps a LINER and a TOKENIZER). However,    */
  77. X/* the presence of the include file facility would make this messy. Also, the */
  78. X/* tokenizer has to count end of line markers so that it can generate         */
  79. X/* correctly positioned diagnostics.                                          */
  80. X/*                                                                            */
  81. X/* The long and short of it all is that the best way to do the scanning seems */
  82. X/* to be to run a liner and a tokenizer as parallel layers. The liner         */
  83. X/* extracts characters from the input file and hands them to the tokenizer.   */
  84. X/* It also keeps an eye out for newline characters, sending a line record off */
  85. X/* whenever it sees one, and counting lines. The tokenizer receives the       */
  86. X/* characters from the liner and performs the tokenize operation.             */
  87. X/*                                                                            */
  88. X/* Notes                                                                      */
  89. X/* -----                                                                      */
  90. X/* - Currently FunnelWeb recognises only two characters as whitespace.        */
  91. X/*   These are ' ' and EOL.                                                   */
  92. X/*                                                                            */
  93. X/******************************************************************************/
  94. X
  95. X#include <ctype.h>
  96. X#include <limits.h>
  97. X#include "style.h"
  98. X
  99. X#include "as.h"
  100. X#include "clock.h"
  101. X#include "data.h"
  102. X#include "dump.h"
  103. X#include "list.h"
  104. X#include "lister.h"
  105. X#include "machin.h"
  106. X#include "mapper.h"
  107. X#include "memory.h"
  108. X#include "misc.h"
  109. X#include "option.h"
  110. X#include "scanner.h"
  111. X
  112. X/******************************************************************************/
  113. X
  114. X/* The "special" character is the character that is used to introduce a       */
  115. X/* "special sequence". FunnelWeb allows the user to change this character so  */
  116. X/* as to cater for documents where the "default" character is common. This    */
  117. X/* definition defines what the default character is.                          */
  118. X#define CH_DSPE ('@')
  119. X
  120. X/* FunnelWeb allows include files which are handled by the scanner by placing */
  121. X/* recursive calls to scan_file. A maximum is placed on the level of nested   */
  122. X/* includes. This acts as a good sanity check as well as catching recursive   */
  123. X/* include files which are never a sensible construct in FunnelWeb as         */
  124. X/* FunnelWeb does not provide any conditional construct.                      */
  125. X#define MAX_INCL (10)
  126. X
  127. X/* FunnelWeb is very conservative about what characters it will allow in its  */
  128. X/* input and output files. Currently the only characters allowed are          */
  129. X/* printables and end of lines. When FunnelWeb does spot an illegal character */
  130. X/* it needs to be able to draw the user's attention to the character. The     */
  131. X/* best way to do this is to point to it in the listing file. However, if the */
  132. X/* character is banned, it cannot appear in the listing file! The problem is  */
  133. X/* solved by having the scanner replace all illegal characters in each mapped */
  134. X/* file by the following character. This eliminates further problems.         */
  135. X#define CENSORCH ('?')
  136. X
  137. X/* Following the Unix convention, mapped in files are not terminated with an  */
  138. X/* end-of-file character. However, the presence of such a character at the    */
  139. X/* end of the mapped file simplifies scanning and so we add one. This         */
  140. X/* definition defines what the character is to be. It doesn't matter what the */
  141. X/* character is, so long as it cannot legally appear in the file. A control   */
  142. X/* character is a good choice as these are filtered out by the liner (see     */
  143. X/* above).                                                                    */
  144. X/* We undef EOF (from <stdio.h>) because it is too dangerously close to EOFCH.*/
  145. X/* (EOF wasn't redefined as that might confuse readers used to <stdio.h>.     */
  146. X/* However, we still use EOF as an acronym for End Of File.                   */
  147. X#define EOFCH (26)
  148. X#undef EOF
  149. X
  150. X/* Tokens have a field for a general attribute which has meaning for some     */
  151. X/* token kinds. For other kinds, it has no meaning. This constant is used to  */
  152. X/* indicate a "don't care" value.                                             */
  153. X#define DONTCARE 0
  154. X
  155. X/* A nominal maximum value for the maximum length of an input line.           */
  156. X#define INMAXINF (ULONG_MAX)
  157. X
  158. X/******************************************************************************/
  159. X
  160. X/* The following type is used in the suite of pragma routines for parsing.    */
  161. typedef
  162. X   struct
  163. X     {
  164. X      ps_t  pt_ps;    /* Position of the start of this argument.              */
  165. X      char *pt_pstr;  /* Pointer to a string containing the argument.         */
  166. X      char *pt_pinl;  /* Pointer to first byte of the argument in commndline. */
  167. X     } pt_t;
  168. typedef pt_t *p_pt_t;
  169. X
  170. X/******************************************************************************/
  171. X
  172. X                        /* Variables Instantiated Over The Entire Scan        */
  173. X                        /* -------------------------------------------        */
  174. LOCVAR p_ck_t p_mapp;   /* Pointer to mapper's clock.                         */
  175. LOCVAR p_ck_t p_scan;   /* Pointer to scanner's clock.                        */
  176. LOCVAR ulong globalno;  /* Global line number of line being scanned.          */
  177. LOCVAR ulong inclevel;  /* Include level of current file. Top file is zero.   */
  178. LOCVAR bool  seenind;   /* TRUE iff we have seen an indentation pragma.       */
  179. LOCVAR ps_t  ps_ind;    /* seenind==TRUE => ps_ind is position of pragma.     */
  180. LOCVAR bool  seentyp;   /* TRUE iff we have seen a typesetter pragma.         */
  181. LOCVAR ps_t  ps_typ;    /* seentyp==TRUE => ps_typ is position of pragma.     */
  182. LOCVAR bool  seenlimo;  /* TRUE iff we have seen an out lin len limit pragma. */
  183. LOCVAR ps_t  ps_limo;   /* seenlimo==TRUE => ps_limo is position of pragma.   */
  184. X
  185. X                        /* Variables Instantiated Over The Current File       */
  186. X                        /* --------------------------------------------       */
  187. LOCVAR ulong inln_max;  /* Maximum permitted length of an input line.         */
  188. LOCVAR char  specialch; /* Current special (escape) character.                */
  189. LOCVAR char  *p_eof;    /* Pointer to EOFCH byte at the end of current file.  */
  190. LOCVAR ulong  localno;  /* Local line number of line being scanned.           */
  191. X
  192. X                        /* Variables Instantiated Over The Current Line       */
  193. X                        /* --------------------------------------------       */
  194. LOCVAR char  *p_sol;    /* Pointer to Start (first char) Of current Line.     */
  195. LOCVAR char  *p_ch;     /* Pointer to current character in current line.      */
  196. LOCVAR char   ch;       /* *p_ch.                                             */
  197. X
  198. X/******************************************************************************/
  199. X/*                          Line Processing Layer                             */
  200. X/******************************************************************************/
  201. X/*                                                                            */
  202. X/* This mini-section contains the two routines (prepline and NEXTCH) that     */
  203. X/* take care of the line based-scanning and feed characters to the            */
  204. X/* token-based scanner routines which have the top level of control.          */
  205. X/* After mapping in a file to be read, place a call to prepline passing the   */
  206. X/* address of the first byte of the mapped file as an argument. At that       */
  207. X/* point the current position will be the first byte on the first line and    */
  208. X/* the "variables instantiated over the current line" will be well defined.   */
  209. X/* Calls to NEXTCH then move the position through the mapped file one byte at */
  210. X/* a time, stopping at the end of file at which point calls will not move the */
  211. X/* marker which will point to the EOF character.                              */
  212. X/*                                                                            */
  213. X/******************************************************************************/
  214. X
  215. LOCAL void prepline P_((char *));
  216. LOCAL void prepline(p_line)
  217. X/* This function should be called at the end of each line to prepare the next */
  218. X/* line for scanning. The user of the liner mini-package should place a       */
  219. X/* single call to this function at the start of scanning a mapped file.       */
  220. X/* The user should then place calls to NEXTCH (which calls prepline when      */
  221. X/* necessary).                                                                */
  222. X/* This function serves two purposes:                                         */
  223. X/*    1. It looks at the next line and converts all non-printables into       */
  224. X/*       CENSORCH and issues errors for each non-printable.                   */
  225. X/*    2. It initializes the line scanning variables for the next line.        */
  226. X/* The argument is a pointer to the first byte of the next line.              */
  227. char *p_line;
  228. X{
  229. X char *p;  /* Scans through the line and winds up sitting on the EOL.         */
  230. X
  231. X /* Test to see if the "line" we have been given is the end of file marker.   */
  232. X /* We have to be careful here because the byte we are using to mark the end  */
  233. X /* of file could appear as an illegal unprintable. This is the reason for    */
  234. X /* the test p_line==p_eof.                                                   */
  235. X if (*p_line==EOFCH && p_line==p_eof)
  236. X   {
  237. X    /* The line we have to process is in fact the end of file marker. */
  238. X    p_sol = p_line;
  239. X    p_ch  = p_line;
  240. X    ch    = EOFCH;
  241. X    return;
  242. X   }
  243. X
  244. X /* At this point we know that we are faced with a run of bytes terminated by */
  245. X /* an EOL character (we know this cos we put an EOL before EOF earlier on).  */
  246. X /* We know that we have a line, so we can now bump up the line counters.     */
  247. X globalno++;
  248. X localno++;
  249. X
  250. X /* Run through the line checking for non-printables and issuing errors.      */
  251. X p = p_line;
  252. X while (*p != EOL)
  253. X    {
  254. X     /* The following test tests to see if the character is a printable in    */
  255. X     /* seven bit ascii. FunnelWeb is not currently designed to work with     */
  256. X     /* any character set other than seven-bit ascii and so we flag and       */
  257. X     /* convert all out-of-range characters here before they are exposed to   */
  258. X     /* the rest of the scanner code which assumes that each line that it is  */
  259. X     /* handed consists entirely of printables except for the EOL char on the */
  260. X     /* end and possibly an EOF char at the "Start" of a line.                */
  261. X     /* In particular, the NEXTCH macro will fail on machines with siged     */
  262. X     /* chars if non-printables are not removed. It goes into an infinite     */
  263. X     /* loop.                                                                 */
  264. X     /* Note: I don't use library function "isprint" here because on the vax  */
  265. X     /* it's definition is too loose (seems to accept characters with the top */
  266. X     /* bit set as printable).                                                */
  267. X     if (!isascprn(*p))  /* If not a printable character. */
  268. X       {
  269. X        ps_t ps;
  270. X        char c = *p;
  271. X        ubyte_ uc = *((ubyte_ *) p);
  272. X        ps.ps_line   = globalno;
  273. X        ps.ps_column = p-p_line+1;
  274. X        if (strlen(chabbrev(c))==0)
  275. X           sprintf(linet1,
  276. X              "Non printable character (Sym=<none>, Dec=%03u, Hex=%02X, Oct=%03o).",
  277. X              (unsigned) uc,(unsigned) uc,(unsigned) uc);
  278. X        else
  279. X           sprintf(linet1,
  280. X              "Non printable character (Sym=%s, Dec=%03u, Hex=%02X, Oct=%03o).",
  281. X                   chabbrev(c),(unsigned) uc,(unsigned) uc,(unsigned) uc);
  282. X        lr_err(&ps,linet1);
  283. X        *p=CENSORCH;
  284. X       }
  285. X     p++;
  286. X    }
  287. X /* Assert: p_line points to the start of the current line.     */
  288. X /* Assert: p points to the EOL at the end of the current line. */
  289. X
  290. X /* Check that the line is not too long. */
  291. X if ((p-p_line)>inln_max)
  292. X   {
  293. X    ps_t ps;
  294. X    ps.ps_line   = globalno;
  295. X    ps.ps_column = inln_max+1;
  296. X    lr_err(&ps,"Input line is too long (this character is the first offender).");
  297. X    sprintf(linet1,"Currently, the maximum allowable input line length is %lu.",
  298. X                   (unsigned long) inln_max);
  299. X    lr_mes(&ps,linet1);
  300. X    lr_mes(&ps,"Note: You can change this using a pragma directive (@p).");
  301. X   }
  302. X
  303. X /* Now check for trailing spaces. */
  304. X if ((p != p_line) && (*(p-1) == ' '))
  305. X   {
  306. X    ps_t ps;
  307. X    ps.ps_line   = globalno;
  308. X    ps.ps_column = p-p_line;
  309. X    lr_war(&ps,"Line has trailing spaces up to and including this space.");
  310. X   }
  311. X
  312. X /* Construct a line record and append the record to the line list. */
  313. X /* Note that the line scrap encompasses the trailing EOL.          */
  314. X {
  315. X  ln_t line;
  316. X  line.ln_global        = globalno;
  317. X  line.ln_local         = localno;
  318. X  line.ln_body.sc_first = p_line;
  319. X  line.ln_body.sc_last  = p;
  320. X  /* Note: We do not set sc_white as it is not used in lines. */
  321. X  ls_add(line_list,PV &line);
  322. X }
  323. X
  324. X /* Finally, set the line scanning variables to the start of the line. */
  325. X /* We can't do this earlier in case the start of the line was a       */
  326. X /* non-printable and got substituted (ch might pick it up).           */
  327. X p_sol =  p_line;
  328. X p_ch  =  p_line;
  329. X ch    = *p_line;
  330. X
  331. X} /* End of prepline. */
  332. X
  333. X/* NEXTCH can be called continuously after an initializing call to prepline.  */
  334. X/* After a call to NEXTCH, p_sol, p_ch, ch are all well-defined. p_sol points */
  335. X/* to the start of the current line, p_ch points to the current character,    */
  336. X/* and ch contains *p_ch. NEXTCH can be called repeatedly forever. When it    */
  337. X/* hits the EOF character, it sticks on it and returns it forever.            */
  338. X/* Note: The "ch<' '" is an optimized form of "(ch==EOL)||(ch=EOFCH)". Speed  */
  339. X/* is very important here as this macro is called in scanning tightloops.     */
  340. X/* This line of code is a little tricky so read it carefully.                 */
  341. X/* WARNING: The ch<' ' will cause an infinite loop if a character appears     */
  342. X/* that satisfies this condition without being EOF or EOL (e.g. a control     */
  343. X/* char (meant to be filtered out earlier) or a top-bit-set character on      */
  344. X/* machines with signed character type.                                       */
  345. X#define NEXTCH {if (ch<' ') {if (ch==EOL) prepline(p_ch+1);} else ch= *++p_ch;}
  346. X
  347. X/******************************************************************************/
  348. X/*                           Scanner Support Routines                         */
  349. X/******************************************************************************/
  350. X
  351. LOCAL ps_t *psofch P_((void));
  352. LOCAL ps_t *psofch()
  353. X/* Returns a pointer to an internal static ps structure holding the line and  */
  354. X/* column number of the current character ch.                                 */
  355. X{
  356. X STAVAR ps_t chps;
  357. X chps.ps_line   = globalno;
  358. X chps.ps_column = p_ch-p_sol+1;
  359. X return &chps;
  360. X}
  361. X
  362. X/******************************************************************************/
  363. X
  364. LOCAL void grabchps P_((p_ps_t));
  365. LOCAL void grabchps(p_ps)
  366. X/* Writes the position of the current ch into the argument position struct.   */
  367. p_ps_t p_ps;
  368. X{
  369. X p_ps->ps_line   = globalno;
  370. X p_ps->ps_column = p_ch-p_sol+1;
  371. X}
  372. X
  373. X/******************************************************************************/
  374. X
  375. LOCAL void sendspec P_((p_ps_t,tk_k_t,ubyte));
  376. LOCAL void sendspec(p_tkps,tk_kind,tk_gen)
  377. X/* Appends a non-text token of kind tk_kind to the end of the token list.     */
  378. X/* p_ps is a pointer to a position structure giving the position of the       */
  379. X/* first character of the token. tk_gen is the general token attribute.       */
  380. p_ps_t p_tkps;
  381. tk_k_t tk_kind;
  382. ubyte  tk_gen;
  383. X{
  384. X tk_t token;
  385. X token.tk_kind        = tk_kind;
  386. X ASSIGN(token.tk_ps,*p_tkps);
  387. X token.tk_sc.sc_first = NULL;
  388. X token.tk_sc.sc_last  = NULL;
  389. X token.tk_sc.sc_white = TRUE;
  390. X token.tk_gen         = tk_gen;
  391. X ls_add(token_list,PV &token);
  392. X}
  393. X
  394. X/******************************************************************************/
  395. X
  396. LOCAL void sendtext P_((p_ps_t,char *,char *,bool));
  397. LOCAL void sendtext(p_tkps,p_first,p_last,is_white)
  398. X/* Appends a text token to the end of the token list.                         */
  399. X/* IN: p_ps is a pointer to a position structure giving the position of the   */
  400. X/*     first character of the token.                                          */
  401. X/* IN: p_first and p_last point to the first and last byte of the text scrap. */
  402. X/* IN: is_white should be set to TRUE iff scrap is entirely whitespace.       */
  403. p_ps_t p_tkps;
  404. char  *p_first;
  405. char  *p_last;
  406. bool   is_white;
  407. X{
  408. X tk_t token;
  409. X
  410. X /* Empty text scraps should never be generated. */
  411. X as_cold(p_first<=p_last,"sendtext: Text scrap bounds are bad.");
  412. X
  413. X /* If ch=EOL then we should be scanning more text, not shipping it! */
  414. X as_cold(ch!=EOL,"senttext: Shipping text while still more to scan.");
  415. X
  416. X /* Send the text token. */
  417. X token.tk_kind        = TK_TEXT;
  418. X ASSIGN(token.tk_ps,*p_tkps);
  419. X token.tk_sc.sc_first = p_first;
  420. X token.tk_sc.sc_last  = p_last;
  421. X token.tk_sc.sc_white = is_white;
  422. X token.tk_gen         = DONTCARE;
  423. X ls_add(token_list,PV &token);
  424. X}
  425. X
  426. X/******************************************************************************/
  427. X
  428. LOCAL void add_eof P_((void));
  429. LOCAL void add_eof()
  430. X/* This function adds terminators to the line and token list.                 */
  431. X/*    1. It adds a TK_EOF token to the end of the token list.                 */
  432. X/*    2. It adds a visible <eof> line to the end of the line list.            */
  433. X/* This assists the parser by allowing it to point diagnostic messages to a   */
  434. X/* visible EOF marker rather than pointing vaguely to the end of the last     */
  435. X/* line of the input file which (by the way) may not even exist!              */
  436. X{
  437. X STAVAR char *eofstr = "<End-Of-File>\n";
  438. X ln_t line;
  439. X ps_t ps;
  440. X
  441. X /* When the liner mini package encounters an end of file marker, it stops    */
  442. X /* dead on the marker and returns EOFCH forever. scan_file() eventually gets */
  443. X /* the message and drops out. However, in all of this, the line numbers are  */
  444. X /* not incremented to indicate that we have moved to an EOF line. This is    */
  445. X /* intended, as we do not want EOFs to appear in the listing for include     */
  446. X /* files; only at the end of the main input file. Thus, here we effectively  */
  447. X /* perform the liner function of moving from the last line of the input file */
  448. X /* to the imaginary line containing the EOF marker. This is done by          */
  449. X /* incrementing the line numbers. Note that the fact that these line number  */
  450. X /* variables are incorrect from the point of detection of the final EOF to   */
  451. X /* here doesn't matter as no tokens or diagnostics are ever added after an   */
  452. X /* EOF is detected.                                                          */
  453. X globalno++;
  454. X localno++;
  455. X
  456. X /* Add a line to represent the EOF marker. */
  457. X line.ln_global        = globalno;
  458. X line.ln_local         = localno;
  459. X line.ln_body.sc_first = eofstr;
  460. X line.ln_body.sc_last  = eofstr+strlen(eofstr)-1;
  461. X /* Note: We do not set sc_white as it is not used in lines. */
  462. X ls_add(line_list,PV &line);
  463. X
  464. X /* Add a TK_EOF token to the end of the token list. */
  465. X ps.ps_line   = globalno;
  466. X ps.ps_column = 1;
  467. X sendspec(&ps,TK_EOF,DONTCARE);
  468. X}
  469. X
  470. X/******************************************************************************/
  471. X/*                              The Scanner Proper                            */
  472. X/******************************************************************************/
  473. X
  474. LOCAL void skiptoeol P_((void));
  475. LOCAL void skiptoeol()
  476. X{
  477. X while (ch != EOL)
  478. X    NEXTCH;
  479. X}
  480. X
  481. X/******************************************************************************/
  482. X
  483. X/* The incl_fil function calls this, so we have to declare it in advance. */
  484. LOCAL void scan_file P_((char *));
  485. X
  486. LOCAL void incl_fil P_((p_ps_t));
  487. LOCAL void incl_fil(p_ps)
  488. X/* Upon entry, the current character is the "i" of an "@i" sequence. Our task */
  489. X/* is first to see if the sequence occurred at the start of a line (the only  */
  490. X/* point at which it is legal) and issue an error if it isn't. If it is legal,*/
  491. X/* we have to read in the specified file and scan that. The included file     */
  492. X/* replaces exactly the line starting with the "@i" command and we return     */
  493. X/* to the "calling" file with the current position being the EOL character of */
  494. X/* the include line.                                                          */
  495. p_ps_t p_ps;
  496. X{
  497. X /* Complain if the include directive was not at the start of a line. */
  498. X if (p_ch-1 != p_sol)
  499. X   {
  500. X    lr_err(p_ps,"Include sequence must be at the beginning of a line.");
  501. X    lr_mes(p_ps,"Include ignored.");
  502. X    skiptoeol();
  503. X    return;
  504. X   }
  505. X
  506. X /* The include command should be followed by a blank. Get the next char. */
  507. X NEXTCH;
  508. X
  509. X /* Complain if the next character is not a blank. */
  510. X if (ch != ' ')
  511. X   {
  512. X    ps_t ps;
  513. X    ASSIGN(ps,*p_ps);
  514. X    ps.ps_column+=2;
  515. X    lr_err(&ps,"Include sequence (@i) must be followed by a blank.");
  516. X    lr_mes(&ps,"Example include: @i macros.fwi");
  517. X    lr_mes(&ps,"Include ignored.");
  518. X    skiptoeol();
  519. X    return;
  520. X   }
  521. X
  522. X /* Complain if the include level is too high. */
  523. X if (inclevel == MAX_INCL)
  524. X   {
  525. X    lr_err(p_ps,"This include file is nested too deeply. It's probably recursive.");
  526. X    sprintf(linet1,"The maximum level of nested includes is %u.",
  527. X                   (unsigned) MAX_INCL);
  528. X    lr_mes(p_ps,linet1);
  529. X    lr_mes(p_ps,"Include ignored.");
  530. X    skiptoeol();
  531. X    return;
  532. X   }
  533. X
  534. X {/* This construct does the work of the include.                 */
  535. X  /* Warning: The following variables MUST be declared automatic. */
  536. X  char   *p_filename;
  537. X  char   *p_tempname;
  538. X  ulong  length;
  539. X  char  *p;
  540. X  ulong xinln_max;
  541. X  char  xspecial;
  542. X  char  *xp_eof;
  543. X  ulong  xlocalno;
  544. X  char  *xp_sol;
  545. X  char  *xp_ch;
  546. X  char   xch;
  547. X
  548. X  /* We save stack space by sticking this filename in the heap. */
  549. X  p_filename=mm_temp((size_t) FILENAME_MAX+1+10); /* 10 is for paranoia. */
  550. X  p_tempname=mm_temp((size_t) FILENAME_MAX+1+10); /* 10 is for paranoia. */
  551. X
  552. X  /* The rest of the line is supposed to hold a filename. Copy it. */
  553. X  NEXTCH;
  554. X  p=p_tempname;
  555. X  length=0;
  556. X  while (ch!=EOL)
  557. X    {
  558. X     if (++length > FILENAME_MAX)
  559. X       {
  560. X        lr_err(p_ps,
  561. X        "This include command's file specification is too long.");
  562. X        if (option.op_b7_b)
  563. X           sprintf(linet1,"The maximum file name length is %s characters.",
  564. X                      SUPPVAL);
  565. X        else
  566. X           sprintf(linet1,"The maximum file name length is %u characters.",
  567. X                      (unsigned) FILENAME_MAX);
  568. X        lr_mes(p_ps,linet1);
  569. X        lr_mes(p_ps,"Include ignored.");
  570. X        skiptoeol();
  571. X        return;
  572. X       }
  573. X     *p++=ch;
  574. X     NEXTCH;
  575. X    }
  576. X  *p=EOS;
  577. X  /* Note: Current position is now on the EOL at the end of the @i line. */
  578. X
  579. X  /* Complain if the user has not specified a filename. */
  580. X  if (strlen(p_tempname) ==0)
  581. X    {
  582. X     lr_err(psofch(),"Expecting the name of a file to include.");
  583. X     return;
  584. X    }
  585. X
  586. X  /* Perform the necessary filename inheritance.                              */
  587. X  strcpy(p_filename,"");
  588. X  fn_ins(p_filename,option.op_f_s);
  589. X  fn_ins(p_filename,".fwi");
  590. X  fn_ins(p_filename,option.op_i_s);
  591. X  fn_ins(p_filename,p_tempname);
  592. X
  593. X  /* Include the included file by calling scan_file recursively. */
  594. X  /* Save and restore all variables in instantiation scope.      */
  595. X  xinln_max = inln_max;
  596. X  xspecial  = specialch;
  597. X  xp_eof    = p_eof;
  598. X  xlocalno  = localno;
  599. X  xp_sol    = p_sol;
  600. X  xp_ch     = p_ch;
  601. X  xch       = ch;
  602. X  inclevel++;
  603. X  scan_file(p_filename);
  604. X  inclevel--;
  605. X  ch        = xch;
  606. X  p_ch      = xp_ch;
  607. X  p_sol     = xp_sol;
  608. X  localno   = xlocalno;
  609. X  p_eof     = xp_eof;
  610. X  specialch = xspecial;
  611. X  inln_max  = xinln_max;
  612. X }
  613. X}
  614. X
  615. X/******************************************************************************/
  616. X
  617. LOCAL void do_ascii P_((p_ps_t));
  618. LOCAL void do_ascii(p_psspec)
  619. X/* Upon entry, the current character is the '^' of a @^ sequence. The task is */
  620. X/* to parse the following ascii code and generate a text token.               */
  621. p_ps_t p_psspec;
  622. X{
  623. X ubyte  base;             /* Base of the number we are scanning.              */
  624. X ubyte  digits;           /* Number of digits expected.                       */
  625. X uword  val;              /* Value of target character.                       */
  626. X ubyte  i;                /* Looping variable.                                */
  627. X STAVAR char alphab[256]; /* Static alphabet array to which to point scraps.  */
  628. X STAVAR bool init=FALSE;  /* Tells if alphab has been initialized.            */
  629. X
  630. X /* Establish an array containing the ascii character set. Later on we can    */
  631. X /* point the sc_first and sc_last pointers to particular characters.         */
  632. X if (!init) {uword i; for (i=0;i<256;i++) alphab[i]=(char) i; init=TRUE;}
  633. X
  634. X /* Make sure that the base character is legal. */
  635. X NEXTCH;
  636. X switch(toupper(ch))
  637. X   {
  638. X    case 'B': base= 2; digits=8; break;
  639. X    case 'O':
  640. X    case 'Q': base= 8; digits=3; break;
  641. X    case 'D': base=10; digits=3; break;
  642. X    case 'H':
  643. X    case 'X': base=16; digits=2; break;
  644. X    default : lr_err(psofch(),"Expecting one of 'B', 'Q', 'D', 'H'.");
  645. X              lr_mes(psofch(),"(For Binary, Octal, Decimal, and Hexadecimal).");
  646. X              base=10;
  647. X              goto trouble;
  648. X   }
  649. X
  650. X /* Parse opening parenthesis. */
  651. X NEXTCH;
  652. X if (ch!='(')
  653. X   {lr_err(psofch(),"Expecting '('.");goto trouble;}
  654. X
  655. X val=0;
  656. X for (i=0;i<digits;i++)
  657. X   {
  658. X    char uch;
  659. X    ubyte d;
  660. X
  661. X    NEXTCH;
  662. X    uch=toupper(ch);
  663. X    if (('0'<=uch) && (uch<='9'))
  664. X       d=uch-'0';
  665. X    else
  666. X       if ('A'<=uch && uch<='F')
  667. X          d=10+uch-'A';
  668. X       else
  669. X          d=100;
  670. X    if (d>=base)
  671. X      {lr_err(psofch(),"Illegal digit."); goto trouble;}
  672. X    val = base*val + d;
  673. X   }
  674. X
  675. X /* Parse closing parenthesis. */
  676. X NEXTCH;
  677. X if (ch!=')')
  678. X   {lr_err(psofch(),"Expecting ')'.");goto trouble;}
  679. X
  680. X /* Make sure that the number is not too big (this is possible in decimal). */
  681. X if (val>255)
  682. X   {
  683. X    lr_err(psofch(),"Character number is too large.");
  684. X    lr_mes(psofch(),"Character number must be in the range [0,255] (decimal).");
  685. X    goto trouble;
  686. X   }
  687. X
  688. X /* Success! Now we can parcel it up into a scrap! */
  689. X sendtext(p_psspec,&alphab[val],&alphab[val],ch==' ' || ch==EOL);
  690. X return;
  691. X
  692. X trouble:
  693. X /* Jump here after a specific diagnostic to give the user a reminder of */
  694. X /* how to specify an ascii character constant.                          */
  695. X switch (base)
  696. X    {
  697. X     case  2:
  698. X        lr_mes(psofch(),
  699. X        "A binary character representation takes the form \"@^B(dddddddd)\".");
  700. X        lr_mes(psofch(),
  701. X        "(exactly 8 digits) where each digit d is either 0 or 1.");
  702. X        break;
  703. X     case  8:
  704. X        lr_mes(psofch(),
  705. X        "An octal character representation takes the form \"@^Q(ddd)\" (or \"@^O(ddd)\").");
  706. X        lr_mes(psofch(),
  707. X        "(exactly 3 digits) where each digit d is in the range 0..7.");
  708. X        break;
  709. X     case 10:
  710. X        lr_mes(psofch(),
  711. X        "A decimal character representation takes the form \"@^D(ddd)\".");
  712. X        lr_mes(psofch(),
  713. X        "(exactly 3 digits) where each digit d is in the range 0..9.");
  714. X        break;
  715. X     case 16:
  716. X        lr_mes(psofch(),
  717. X        "A hexadecimal character representation takes the form \"@^X(dd)\" (or \"@^H(dd)\").");
  718. X        lr_mes(psofch(),
  719. X        "(exactly 2 digits) where each digit d is in the range 0..9,A..F.");
  720. X        break;
  721. X     default: as_bomb("do_ascii: trouble base switch defaulted.");
  722. X    }
  723. X}
  724. X
  725. X/******************************************************************************/
  726. X
  727. LOCAL void do_name P_((p_ps_t));
  728. LOCAL void do_name (p_psspec)
  729. X/* Upon entry, the current character is the # of a @#. The task is to parse   */
  730. X/* it and transmit a name token.                                              */
  731. p_ps_t p_psspec;
  732. X{
  733. X as_cold(ch=='#',"do_name: character is wrong.");
  734. X NEXTCH;
  735. X if ((ch==EOL) || (ch==' '))
  736. X    {lr_err(psofch(),"Expecting a printable character."); return;}
  737. X
  738. X /* Transmit a name token. */
  739. X sendspec(p_psspec,TK_NAME,(ubyte) ch);
  740. X}
  741. X
  742. X/******************************************************************************/
  743. X
  744. LOCAL void do_pgind P_((uword,p_pt_t));
  745. LOCAL void do_pgind(numarg,arg)
  746. X/* Parse an indentation pragma. */
  747. uword numarg;
  748. p_pt_t   arg;
  749. X{
  750. X bool pragind;
  751. X ps_t psprag;
  752. X
  753. X /* Make sure that there are exactly three arguments. */
  754. X if (numarg != 3)  /* "indentation"  "none|blank". */
  755. X   {
  756. X    lr_err(&arg[0].pt_ps,
  757. X           "This indentation pragma has the wrong number of arguments.");
  758. X    goto help;
  759. X   }
  760. X
  761. X /* Make sure that the second argument is an "=". */
  762. X if (0 != strcmp(arg[2].pt_pstr,"="))
  763. X   {
  764. X    lr_err(&arg[2].pt_ps,"Expecting \"=\".");
  765. X    goto help;
  766. X   }
  767. X
  768. X /* Check the third argument. */
  769. X      if (strcmp(arg[3].pt_pstr,"none" )==0) pragind=FALSE;
  770. X else if (strcmp(arg[3].pt_pstr,"blank")==0) pragind=TRUE;
  771. X else
  772. X   {
  773. X    lr_err(&arg[3].pt_ps,"Expecting either \"none\" or \"blank\".");
  774. X    goto help;
  775. X   }
  776. X
  777. X /* Construct a shorthand for the start of the pragma. */
  778. X ASSIGN(psprag,arg[0].pt_ps);
  779. X
  780. X /* Make sure that the pragma does not contradict an earlier pragma. */
  781. X if (seenind && (tgindent!=pragind))
  782. X   {
  783. X    sprintf(linet1,"This pragma is opposed by the pragma at line %lu.",
  784. X                   (unsigned long) psprag.ps_line);
  785. X    lr_mes(&ps_ind,linet1);
  786. X    sprintf(linet1,"This pragma opposes the pragma at line %lu.",
  787. X                   (unsigned long) ps_ind.ps_line);
  788. X    lr_err(&psprag,linet1);
  789. X    lr_mes(&psprag,"You can have as many indentation pragmas as you like,");
  790. X    lr_mes(&psprag,"but they all have to be the same!");
  791. X    lr_mes(&psprag,"Pragma ignored.");
  792. X    return;
  793. X   }
  794. X
  795. X /* Success: Record the pragma information. */
  796. X seenind  = TRUE;       /* Record that we have seen a pragma. */
  797. X tgindent = pragind;    /* Record what the pragma said.       */
  798. X ASSIGN(ps_ind,psprag); /* Record where the pragma was.       */
  799. X return;
  800. X
  801. X help:
  802. X    lr_mes(&arg[0].pt_ps,
  803. X           "The correct format is: \"@p indentation = none|blank\".");
  804. X    lr_mes(&arg[0].pt_ps,"Pragma ignored.");
  805. X    return;
  806. X}
  807. X
  808. X/******************************************************************************/
  809. X
  810. LOCAL void do_pginl P_((uword,p_pt_t));
  811. LOCAL void do_pginl(numarg,arg)
  812. X/* Parse a maximum input line length pragma. */
  813. uword numarg;
  814. p_pt_t   arg;
  815. X{
  816. X char  *numstr;
  817. X uword spn;
  818. X
  819. X /* Make sure that there are exactly three arguments. */
  820. X if (numarg != 3)  /* "max..length = <num>". */
  821. X   {
  822. X    lr_err(&arg[0].pt_ps,"This pragma has the wrong number of arguments.");
  823. X    goto help;
  824. X   }
  825. X
  826. X  /* Make sure that the second argument is "=". */
  827. X if (0 != strcmp(arg[2].pt_pstr,"="))
  828. X   {lr_err(&arg[2].pt_ps,"Expecting \"=\"."); goto help;}
  829. X
  830. X /* Set up an abbreviation. */
  831. X numstr=arg[3].pt_pstr;
  832. X
  833. X /* See if the value is "infinity". */
  834. X if (strcmp(numstr,"infinity")==0)
  835. X   {inln_max=INMAXINF; return;}
  836. X
  837. X /* Calculate length of longest prefix containing all decimal digits. */
  838. X /* Check that there are no illegal digits.                           */
  839. X spn=strspn(numstr,"0123456789");
  840. X if (spn != strlen(numstr))
  841. X   {
  842. X    ps_t ps;
  843. X    ASSIGN(ps,arg[3].pt_ps);
  844. X    ps.ps_column+=spn;
  845. X    lr_err(&ps,"Illegal digit. Value must consist entirely of decimal digits.");
  846. X    lr_mes(&ps,"You can also use the value \"infinity\".");
  847. X    lr_mes(&ps,"Pragma ignored.");
  848. X    return;
  849. X   }
  850. X
  851. X /* Check that the number is not too long. */
  852. X if (strlen(numstr)>8)
  853. X   {
  854. X    lr_err(&arg[3].pt_ps,"Too many digits. The maximum is eight.");
  855. X    lr_mes(&arg[3].pt_ps,"Pragma ignored.");
  856. X    return;
  857. X   }
  858. X
  859. X /* Convert the argument into an integer. */
  860. X {
  861. X  ulong val;
  862. X  int result;
  863. X  /* Note: Should really be %lu, but the Vax doesn't know about the %u */
  864. X  /* in sscanf and so we make do with %ld.                             */
  865. X  result=sscanf(numstr,"%ld",&val);
  866. X  as_cold(result==1,"do_pginl:sscanf failed.");
  867. X  inln_max=val;
  868. X }
  869. X return;
  870. X
  871. X help:
  872. X    lr_mes(&arg[0].pt_ps,
  873. X    "The correct format is: \"@p maximum_input_line_length = <num>|infinity\".");
  874. X    lr_mes(&arg[0].pt_ps,"Pragma ignored.");
  875. X    return;
  876. X}
  877. X
  878. X/******************************************************************************/
  879. X
  880. LOCAL void do_pgotl P_((uword,p_pt_t));
  881. LOCAL void do_pgotl(numarg,arg)
  882. X/* Parse a maximum product file line length pragma. */
  883. uword numarg;
  884. p_pt_t   arg;
  885. X{
  886. X char  *numstr;
  887. X uword spn;
  888. X ulong val;
  889. X ps_t psprag;
  890. X
  891. X /* Set up an abbreviation. */
  892. X ASSIGN(psprag,arg[0].pt_ps);
  893. X
  894. X /* Make sure that there are exactly three arguments. */
  895. X if (numarg != 3)  /* "max..length" "=" "value". */
  896. X   {
  897. X    lr_err(&arg[0].pt_ps,"This pragma has the wrong number of arguments.");
  898. X    goto help;
  899. X   }
  900. X
  901. X  /* Make sure that the second argument is an "=". */
  902. X if (0 != strcmp(arg[2].pt_pstr,"="))
  903. X   {lr_err(&arg[2].pt_ps,"Expecting \"=\"."); goto help;}
  904. X
  905. X /* Set up an abbreviation. */
  906. X numstr=arg[3].pt_pstr;
  907. X
  908. X /* See if the value is "infinity". */
  909. X if (strcmp(numstr,"infinity")==0)
  910. X   {val=TGMAXINF; goto gotvalue;}
  911. X
  912. X /* Calculate length of longest prefix containing all decimal digits. */
  913. X /* Check that there are no illegal digits.                           */
  914. X spn=strspn(numstr,"0123456789");
  915. X if (spn != strlen(numstr))
  916. X   {
  917. X    ps_t ps;
  918. X    ASSIGN(ps,arg[3].pt_ps);
  919. X    ps.ps_column+=spn;
  920. X    lr_err(&ps,"Illegal digit. Value must consist entirely of decimal digits.");
  921. X    lr_mes(&ps,"You can also use the value \"infinity\".");
  922. X    lr_mes(&ps,"Pragma ignored.");
  923. X    return;
  924. X   }
  925. X
  926. X /* Check that the number is not too long. */
  927. X if (strlen(numstr)>8)
  928. X   {
  929. X    lr_err(&arg[3].pt_ps,"Too many digits. The maximum is eight.");
  930. X    lr_mes(&arg[3].pt_ps,"Pragma ignored.");
  931. X    return;
  932. X   }
  933. X
  934. X /* Convert the argument into an integer. */
  935. X {
  936. X  int result=sscanf(numstr,"%ld",&val);
  937. X  as_cold(result==1,"do_pgotl:sscanf failed.");
  938. X }
  939. X
  940. X gotvalue:
  941. X /* Make sure that the pragma does not contradict an earlier pragma. */
  942. X if (seenlimo && (tglinmax!=val))
  943. X   {
  944. X    sprintf(linet1,"This pragma is opposed by the pragma at line %lu.",
  945. X                   (unsigned long) psprag.ps_line);
  946. X    lr_mes(&ps_limo,linet1);
  947. X    sprintf(linet1,"This pragma opposes the pragma at line %lu.",
  948. X                   (unsigned long) ps_limo.ps_line);
  949. X    lr_err(&psprag,linet1);
  950. X    lr_mes(&psprag,"You can have as many output line length pragmas");
  951. X    lr_mes(&psprag,"as you like, but they all have to be the same!");
  952. X    lr_mes(&psprag,"Pragma ignored.");
  953. X    return;
  954. X   }
  955. X
  956. X /* If we got this far then the pragma is just the same as an earlier one. */
  957. X /* We don't want to set the pragma position to the later pragma so we     */
  958. X /* return now.                                                            */
  959. X if (seenlimo) return;
  960. X
  961. X /* Success. Set the variables. */
  962. X tglinmax=val;
  963. X seenlimo=TRUE;
  964. X ASSIGN(ps_limo,psprag);
  965. X return;
  966. X
  967. X help:
  968. X    lr_mes(&arg[0].pt_ps,
  969. X    "The correct format is: \"@p maximum_output_line_length = <num>|infinity\".");
  970. X    lr_mes(&arg[0].pt_ps,"Pragma ignored.");
  971. X    return;
  972. X}
  973. X
  974. X/******************************************************************************/
  975. X
  976. LOCAL void do_pgnpg P_((uword,p_pt_t));
  977. LOCAL void do_pgnpg(numarg,arg)
  978. X/* Parse a newpage typesetter directive. */
  979. uword numarg;
  980. p_pt_t   arg;
  981. X{
  982. X /* Make sure that there is exactly one argument. */
  983. X if (numarg > 1)  /* "new_page" */
  984. X   {
  985. X    lr_err(&arg[2].pt_ps,"The new_page directive does not take arguments.");
  986. X    lr_mes(&arg[2].pt_ps,"Directive ignored.");
  987. X    return;
  988. X   }
  989. X sendspec(&arg[0].pt_ps,TK_NPAG,DONTCARE);
  990. X}
  991. X
  992. X/******************************************************************************/
  993. X
  994. LOCAL void do_pgtoc P_((uword,p_pt_t));
  995. LOCAL void do_pgtoc(numarg,arg)
  996. X/* Parse a table of contents typesetter directive. */
  997. uword numarg;  /* Number of arguments to table of contents directive.         */
  998. p_pt_t   arg;  /* Array describing arguments.                                 */
  999. X{
  1000. X /* Make sure that there is exactly one argument. */
  1001. X if (numarg > 1)  /* "table_of_contents" */
  1002. X   {
  1003. X    lr_err(&arg[2].pt_ps,
  1004. X           "The table_of_contents directive does not take arguments.");
  1005. X    lr_mes(&arg[2].pt_ps,"Directive ignored.");
  1006. X    return;
  1007. X   }
  1008. X sendspec(&arg[0].pt_ps,TK_TOCS,DONTCARE);
  1009. X}
  1010. X
  1011. X/******************************************************************************/
  1012. X
  1013. LOCAL void do_pgvsk P_((uword,p_pt_t));
  1014. LOCAL void do_pgvsk(numarg,arg)
  1015. X/* Parse a vskip typesetter directive. */
  1016. uword numarg;  /* Number of arguments to indentation directive.               */
  1017. p_pt_t   arg;  /* Array describing arguments.                                 */
  1018. X{
  1019. X char  *numstr;
  1020. X uword spn;
  1021. X
  1022. X /* Make sure that there are exactly three arguments. */
  1023. X if (numarg != 3)  /* "vskip" n "mm". */
  1024. X   {
  1025. X    lr_err(&arg[0].pt_ps,"This directive has the wrong number of arguments.");
  1026. X    goto help;
  1027. X   }
  1028. X
  1029. X  /* Make sure that the third argument is "mm". */
  1030. X if (0 != strcmp(arg[3].pt_pstr,"mm"))
  1031. X   {lr_err(&arg[3].pt_ps,"Expecting \"mm\"."); goto help;}
  1032. X
  1033. X /* Set up an abbreviation. */
  1034. X numstr=arg[2].pt_pstr;
  1035. X
  1036. X /* Calculate length of longest prefix containing all decimal digits. */
  1037. X /* Check that there are no illegal digits.                           */
  1038. X spn=strspn(numstr,"0123456789");
  1039. X if (spn != strlen(numstr))
  1040. X   {
  1041. X    ps_t ps;
  1042. X    ASSIGN(ps,arg[2].pt_ps);
  1043. X    ps.ps_column+=spn;
  1044. X    lr_err(&ps,"Illegal digit.");
  1045. X    lr_mes(&ps,"Value must consist entirely of decimal digits.");
  1046. X    lr_mes(&ps,"Directive ignored.");
  1047. X    return;
  1048. X   }
  1049. X
  1050. X /* Check that the number is not too long. */
  1051. X if (strlen(numstr)>3)
  1052. X   {
  1053. X    lr_err(&arg[2].pt_ps,"Too many digits. The maximum is three.");
  1054. X    lr_mes(&arg[2].pt_ps,"Directive ignored.");
  1055. X    return;
  1056. X   }
  1057. X
  1058. X /* Convert the argument into an integer. */
  1059. X {
  1060. X  ulong val;
  1061. X  int result;
  1062. X  result=sscanf(numstr,"%ld",&val);
  1063. X  as_cold(result==1,"do_pginl:sscanf failed.");
  1064. X  if (val>255)
  1065. X    {
  1066. X     lr_err(&arg[2].pt_ps,"Value too large. Maximum is 255.");
  1067. X     lr_mes(&arg[2].pt_ps,"Directive ignored.");
  1068. X     return;
  1069. X    }
  1070. X  sendspec(&arg[0].pt_ps,TK_SKIP,(ubyte) val);
  1071. X }
  1072. X return;
  1073. X
  1074. X help:
  1075. X    lr_mes(&arg[0].pt_ps,"The correct format is: \"@t vskip <num> mm\".");
  1076. X    lr_mes(&arg[0].pt_ps,"Directive ignored.");
  1077. X    return;
  1078. X}
  1079. X
  1080. X/******************************************************************************/
  1081. X
  1082. LOCAL void do_pgtit P_((uword,p_pt_t));
  1083. LOCAL void do_pgtit(numarg,arg)
  1084. X/* Parse a title typesetter directive. */
  1085. uword numarg;  /* Number of arguments to title directive.                     */
  1086. p_pt_t   arg;  /* Array describing arguments.                                 */
  1087. X{
  1088. X uword align;
  1089. X uword font;
  1090. X char *p_sot,*p_eot;
  1091. X
  1092. X /* Make sure that there are at least three arguments. */
  1093. X if (numarg < 4)  /* "title <font> <align> <text>". */
  1094. X   {lr_err(&arg[0].pt_ps,"This directive has too few arguments."); goto help;}
  1095. X
  1096. X /* Check the font argument. */
  1097. X      if (strcmp(arg[2].pt_pstr,"normalfont"    )==0) font=FT_NORM;
  1098. X else if (strcmp(arg[2].pt_pstr,"titlefont"     )==0) font=FT_TITL;
  1099. X else if (strcmp(arg[2].pt_pstr,"smalltitlefont")==0) font=FT_STIT;
  1100. X else
  1101. X   {
  1102. X    lr_err(&arg[2].pt_ps,
  1103. X    "Expecting one of {titlefont,smalltitlefont,normalfont}.");
  1104. X    lr_mes(&arg[2].pt_ps,"Directive ignored.");
  1105. X    return;
  1106. X   }
  1107. X
  1108. X /* Check the alignment argument. */
  1109. X      if (strcmp(arg[3].pt_pstr,"left"  )==0) align=LR_LEFT;
  1110. X else if (strcmp(arg[3].pt_pstr,"right" )==0) align=LR_RIGH;
  1111. X else if (strcmp(arg[3].pt_pstr,"centre")==0) align=LR_CENT;
  1112. X else
  1113. X   {
  1114. X    lr_err(&arg[3].pt_ps,"Expecting one of {left,right,centre}.");
  1115. X    if (strcmp(arg[3].pt_pstr,"center")==0)
  1116. X      {
  1117. X       lr_mes(&arg[3].pt_ps,"Note: Centre is spelt centRE, not centER.");
  1118. X       lr_mes(&arg[3].pt_ps,"      This is my revenge for years of getting error messages");
  1119. X       lr_mes(&arg[3].pt_ps,"      from TeX whenever I accidentally wrote \\centreline - Ross Williams.");
  1120. X      }
  1121. X    lr_mes(&arg[3].pt_ps,"Directive ignored.");
  1122. X    return;
  1123. X   }
  1124. X
  1125. X /* Now make sure that the remainder of the line is delimited by quotes. */
  1126. X p_sot=arg[4].pt_pinl;
  1127. X p_eot=p_sot+strlen(p_sot)-1;
  1128. X if (*p_sot!='"' || *p_eot!='"' || p_sot==p_eot)
  1129. X   {
  1130. X    lr_err(&arg[4].pt_ps,"Text argument must be delimited by double quotes.");
  1131. X    lr_mes(&arg[4].pt_ps,"Directive ignored.");
  1132. X    return;
  1133. X   }
  1134. X p_sot++; p_eot--;
  1135. X
  1136. X /* Ship out a token whose fields are all fully laden. */
  1137. X {
  1138. X  tk_t token;
  1139. X  token.tk_kind        = TK_TITL;
  1140. X  ASSIGN(token.tk_ps,arg[0].pt_ps);
  1141. X  token.tk_sc.sc_first = p_sol+ (3+(p_sot-arg[1].pt_pinl));
  1142. X  token.tk_sc.sc_last  = p_sol+ (3+(p_eot-arg[1].pt_pinl));
  1143. X  token.tk_sc.sc_white = FALSE;
  1144. X  token.tk_gen         = LRFT_PACK*font+align;
  1145. X  ls_add(token_list,PV &token);
  1146. X }
  1147. X return;
  1148. X
  1149. X help:
  1150. X    lr_mes(&arg[0].pt_ps,
  1151. X    "The correct format is: \"@t title <font> <align> <text>\".");
  1152. X    lr_mes(&arg[0].pt_ps,
  1153. X    "   where <font>  = titlefont | smalltitlefont | normalfont.");
  1154. X    lr_mes(&arg[0].pt_ps,
  1155. X    "   and   <align> = left | centre | right.");
  1156. X    lr_mes(&arg[0].pt_ps,
  1157. X    "   and   <text>  = text delimited by double quotes.");
  1158. X    lr_mes(&arg[0].pt_ps,"Directive ignored.");
  1159. X    return;
  1160. X}
  1161. X
  1162. X/******************************************************************************/
  1163. X
  1164. LOCAL void do_pgtyp P_((uword,p_pt_t));
  1165. LOCAL void do_pgtyp(numarg,arg)
  1166. X/* Parse a typesetter pragma. */
  1167. uword numarg;
  1168. p_pt_t   arg;
  1169. X{
  1170. X tr_k_t pragtyp;
  1171. X ps_t   psprag;
  1172. X
  1173. X /* Make sure that there are exactly three arguments. */
  1174. X if (numarg != 3)  /* "typesetter" "=" "name". */
  1175. X   {
  1176. X    lr_err(&arg[0].pt_ps,
  1177. X           "This typesetter pragma has the wrong number of arguments.");
  1178. X    goto help;
  1179. X   }
  1180. X
  1181. X /* Make sure that the second argument is "=". */
  1182. X if (0 != strcmp(arg[2].pt_pstr,"="))
  1183. X   {
  1184. X    lr_err(&arg[2].pt_ps,"Expecting \"=\".");
  1185. X    goto help;
  1186. X   }
  1187. X
  1188. X /* Check the third argument. */
  1189. X      if (strcmp(arg[3].pt_pstr,"none")==0) pragtyp=TR_NONE;
  1190. X else if (strcmp(arg[3].pt_pstr,"tex" )==0) pragtyp=TR_TEX;
  1191. X else
  1192. X   {
  1193. X    lr_err(&arg[3].pt_ps,"Expecting either \"none\" or \"tex\".");
  1194. X    goto help;
  1195. X   }
  1196. X
  1197. X /* Construct a shorthand for the start of the pragma. */
  1198. X ASSIGN(psprag,arg[0].pt_ps);
  1199. X
  1200. X /* Make sure that the pragma does not contradict an earlier pragma. */
  1201. X if (seentyp && (tr_codes != pragtyp))
  1202. X   {
  1203. X    sprintf(linet1,"This pragma is opposed by the pragma at line %lu.",
  1204. X                   (unsigned long) psprag.ps_line);
  1205. X    lr_mes(&ps_typ,linet1);
  1206. X    sprintf(linet1,"This pragma opposes the pragma at line %lu.",
  1207. X                   (unsigned long) ps_typ.ps_line);
  1208. X    lr_err(&psprag,linet1);
  1209. X    lr_mes(&psprag,"You can have as many typesetter pragmas as you like,");
  1210. X    lr_mes(&psprag,"but they all have to be the same!");
  1211. X    lr_mes(&psprag,"Pragma ignored.");
  1212. X    return;
  1213. X   }
  1214. X
  1215. X /* Success: Record the pragma information. */
  1216. X seentyp  = TRUE;       /* Record that we have seen a pragma. */
  1217. X tr_codes = pragtyp;    /* Record what the pragma said.       */
  1218. X ASSIGN(ps_typ,psprag); /* Record where the pragma was.       */
  1219. X return;
  1220. X
  1221. X help:
  1222. X    lr_mes(&arg[0].pt_ps,
  1223. X           "The correct format is: \"@p typesetter = none|tex\".");
  1224. X    lr_mes(&arg[0].pt_ps,"Pragma ignored.");
  1225. X    return;
  1226. X}
  1227. X
  1228. X/******************************************************************************/
  1229. X
  1230. LOCAL void do_pragma P_((p_ps_t,bool));
  1231. LOCAL void do_pragma(p_ps,is_typ)
  1232. X/* Upon entry, the current character is:                                      */
  1233. X/* is_typ=FALSE => The P of a @p.                                             */
  1234. X/* is_typ=TRUE  => The T of a @t.                                             */
  1235. X/* This function processes these contructs.                                   */
  1236. p_ps_t p_ps;
  1237. bool is_typ;
  1238. X{
  1239. X#define MAXPARG     10         /* Maximum recorded arguments to a pragma.     */
  1240. X#define PRAGMA_MAX 100         /* Maximum length of a pragma.                 */
  1241. X char  praglin[PRAGMA_MAX+1];  /* Array to hold pragma as a complete line.    */
  1242. X char  pragstr[PRAGMA_MAX+1];  /* Array to hold pragma as strings.            */
  1243. X pt_t  pragarg[MAXPARG+1];     /* Array of pragma arguments.                  */
  1244. X uword length;                 /* Helps prevent scanning overrun.             */
  1245. X char  *p,*q;                  /* Temporary.                                  */
  1246. X uword numarg,na;              /* Number of arguments seen so far.            */
  1247. X
  1248. X /* Complain if the pragma directive is not at the start of a line. */
  1249. X if (p_ch-1 != p_sol)
  1250. X   {
  1251. X    if (is_typ)
  1252. X      {
  1253. X       lr_err(p_ps,"Typesetter directive @t must be at the start of a line.");
  1254. X       lr_mes(p_ps,"The rest of this line will be ignored.");
  1255. X      }
  1256. X    else
  1257. X      {
  1258. X       lr_err(p_ps,"Pragma sequence @p must be at the start of a line.");
  1259. X       lr_mes(p_ps,"The rest of this line will be ignored.");
  1260. X      }
  1261. X    skiptoeol();
  1262. X    goto help;
  1263. X   }
  1264. X
  1265. X /* The include command should be followed by a blank. Get the next char. */
  1266. X NEXTCH;
  1267. X
  1268. X /* Complain if the next character is not a blank. */
  1269. X if (ch != ' ')
  1270. X   {
  1271. X    /* Note: If we position this error correctly, it gets put after the */
  1272. X    /*       help message!                                              */
  1273. X    if (is_typ)
  1274. X       lr_err(p_ps,"Typesetter directive @t must be followed by a blank.");
  1275. X    else
  1276. X       lr_err(p_ps,"Pragma sequence @p must be followed by a blank.");
  1277. X    skiptoeol();
  1278. X    goto help;
  1279. X   }
  1280. X
  1281. X /* Copy the rest of the line to the pragma arrays. */
  1282. X NEXTCH;
  1283. X p = &praglin[0];
  1284. X q = &pragstr[0];
  1285. X length=0;
  1286. X while (ch!=EOL)
  1287. X   {
  1288. X    if (++length > PRAGMA_MAX-3)  /* 3 is for "@p " or "@t " */
  1289. X      {
  1290. X       if (is_typ)
  1291. X         {
  1292. X          lr_err(p_ps,"This typestter directive line is too long.");
  1293. X          sprintf(linet1,"The maximum typesetter directive line length is %u characters.",
  1294. X                     (unsigned) PRAGMA_MAX);
  1295. X          lr_mes(p_ps,linet1);
  1296. X         }
  1297. X       else
  1298. X         {
  1299. X          lr_err(p_ps,"This pragma line is too long.");
  1300. X          sprintf(linet1,"The maximum pragma line length is %u characters.",
  1301. X                     (unsigned) PRAGMA_MAX);
  1302. X          lr_mes(p_ps,linet1);
  1303. X         }
  1304. X       skiptoeol();
  1305. X       goto help;
  1306. X      }
  1307. X    *p++=ch;
  1308. X    *q++=ch;
  1309. X    NEXTCH;
  1310. X   }
  1311. X *p=EOS;
  1312. X *q=EOS;
  1313. X /* Note: Current position is now on the EOL at the end of the @p line. */
  1314. X /* That is the way we want to leave it for the scanspec() routine.     */
  1315. X
  1316. X /* So far we have copied the body of the pragma line into two arrays. The    */
  1317. X /* next lump of code parses that line into a sequence of non-blank arguments.*/
  1318. X /* The result is an array of pt_t objects each of which contains the         */
  1319. X /* position of each argument, a pointer to the first character of each       */
  1320. X /* argument in praglin, and also a pointer to a string containing the arg.   */
  1321. X /* The string resides in the array pragstr which is the same as praglin      */
  1322. X /* except that some blanks have been replaced by EOSs so as to allow us to   */
  1323. X /* point into it to form strings. All this probably seems rather overdone    */
  1324. X /* for the analysis of a "simple" pragma, but I have found that pulling the  */
  1325. X /* different kinds of pragma lines apart separately is very messy. Far       */
  1326. X /* better to suffer here in what is at least reasonably neat code than       */
  1327. X /* later in the specific pragma routines.                                    */
  1328. X numarg=0;
  1329. X p= &praglin[0];
  1330. X q= &pragstr[0];
  1331. X while (TRUE)
  1332. X   {
  1333. X    /* Skip whitespace between arguments. */
  1334. X    while (*p==' ') {p++;q++;}
  1335. X
  1336. X    /* Exit if we have hit the end of the line. */
  1337. X    if ((numarg==MAXPARG) || (*p==EOS)) break;
  1338. X
  1339. X    /* We have found another argument! */
  1340. X    numarg++;
  1341. X
  1342. X    /* Record the argument. */
  1343. X    ASSIGN(pragarg[numarg].pt_ps,*p_ps);
  1344. X    pragarg[numarg].pt_ps.ps_column=4+(p-praglin);
  1345. X    pragarg[numarg].pt_pinl=p;
  1346. X    pragarg[numarg].pt_pstr=q;
  1347. X
  1348. X    /* Skip to the end of the argument. */
  1349. X    while (*p!=' ' && *p!=EOS) {p++;q++;}
  1350. X
  1351. X    /* Drop a null in the string array to complete string rep of argument. */
  1352. X    *q=EOS;
  1353. X   }
  1354. X
  1355. X /* At this point numarg is MIN(arguments,MAXPARG), and pragargs contains an  */
  1356. X /* entry for each of the numarg arguments.                                   */
  1357. X
  1358. X /* It is handy to have the position of the pragma itself handy. */
  1359. X ASSIGN(pragarg[0].pt_ps,*p_ps);
  1360. X
  1361. X /* CHECK: Make sure that the line and string arrays square up. */
  1362. X {
  1363. X  uword i;
  1364. X  for (i=1;i<=numarg;i++)
  1365. X    {
  1366. X     uword j;
  1367. X     uword t=strlen(pragarg[i].pt_pstr);
  1368. X     for (j=0;j<t;j++)
  1369. X       {
  1370. X        as_cold(pragarg[i].pt_pstr[j]==pragarg[i].pt_pinl[j],
  1371. X                "do_pragma: String and line arrays are not equal.");
  1372. X        as_cold((pragarg[i].pt_pstr-pragstr)==(pragarg[i].pt_pinl-praglin),
  1373. X                "do_pragma: String and line arrays are out of synch.");
  1374. X       }
  1375. X    }
  1376. X }
  1377. X
  1378. X /* Complain if there are no arguments at all. */
  1379. X if (numarg==0)
  1380. X   {
  1381. X    if (is_typ)
  1382. X       lr_err(p_ps,"Typesetter directive @t must be followed by a keyword.");
  1383. X    else
  1384. X       lr_err(p_ps,"Pragma sequence @p must be followed by a keyword.");
  1385. X    skiptoeol();
  1386. X    goto help;
  1387. X   }
  1388. X
  1389. X /* Branch off to specific routines based on the first argument. */
  1390. X p=pragarg[1].pt_pstr; na=numarg;
  1391. X if (is_typ)
  1392. X   {
  1393. X    if (0==strcmp(p,"new_page"          )) {do_pgnpg(na,pragarg);return;}
  1394. X    if (0==strcmp(p,"table_of_contents" )) {do_pgtoc(na,pragarg);return;}
  1395. X    if (0==strcmp(p,"title"             )) {do_pgtit(na,pragarg);return;}
  1396. X    if (0==strcmp(p,"vskip"             )) {do_pgvsk(na,pragarg);return;}
  1397. X   }
  1398. X else
  1399. X   {
  1400. X    if (0==strcmp(p,"indentation"               )) {do_pgind(na,pragarg);return;}
  1401. X    if (0==strcmp(p,"maximum_input_line_length" )) {do_pginl(na,pragarg);return;}
  1402. X    if (0==strcmp(p,"maximum_output_line_length")) {do_pgotl(na,pragarg);return;}
  1403. X    if (0==strcmp(p,"typesetter"                )) {do_pgtyp(na,pragarg);return;}
  1404. X   }
  1405. X
  1406. X help:
  1407. X if (is_typ)
  1408. X   {
  1409. X    lr_err(p_ps,"Unrecognised typesetter directive. Legal ones are:");
  1410. X    lr_mes(p_ps,"   @t new_page");
  1411. X    lr_mes(p_ps,"   @t table_of_contents");
  1412. X    lr_mes(p_ps,"   @t title <font> <align> <string>");
  1413. X    lr_mes(p_ps,"   @t vskip <num> mm");
  1414. X    lr_mes(p_ps,"The blanks between arguments are important.");
  1415. X    lr_mes(p_ps,"Typesetter directive ignored.");
  1416. X   }
  1417. X else
  1418. X   {
  1419. X    lr_err(p_ps,"Unrecognised pragma. Possible legal pragmas are:");
  1420. X    lr_mes(p_ps,"   @p indentation = none | blank");
  1421. X    lr_mes(p_ps,"   @p maximum_input_line_length  = <num>|infinity");
  1422. X    lr_mes(p_ps,"   @p maximum_output_line_length = <num>|infinity");
  1423. X    lr_mes(p_ps,"   @p typesetter = none | tex");
  1424. X    lr_mes(p_ps,"The blanks between arguments are important.");
  1425. X    lr_mes(p_ps,"Pragma ignored.");
  1426. X   }
  1427. X}
  1428. X
  1429. X/******************************************************************************/
  1430. X
  1431. LOCAL void chksol P_((void));
  1432. LOCAL void chksol()
  1433. X/* This function is called when the current character is the character after  */
  1434. X/* an @. The function checks to see if the @ was at the start of a line and   */
  1435. X/* issues a error message if it isn't.                                        */
  1436. X{
  1437. X ps_t ps;
  1438. X grabchps(&ps);
  1439. X if (ps.ps_column != 2)
  1440. X   {
  1441. X    ps.ps_column--;
  1442. X    sprintf(linet1,"@%c is legal only at the start of a line.",ch);
  1443. X    lr_err(&ps,linet1);
  1444. X   }
  1445. X}
  1446. X
  1447. X/******************************************************************************/
  1448. X
  1449. LOCAL void scanspec P_((void));
  1450. LOCAL void scanspec()
  1451. X/* Upon entry the current character is the special character (usually '@').   */
  1452. X/* The task is to scan the special sequence. Upon exit, the current character */
  1453. X/* is the character following the special sequence.                           */
  1454. X{
  1455. X ps_t  ps_spec;                   /* Position of start of special sequence.   */
  1456. X
  1457. X /* Make a note of where the special sequence starts. */
  1458. X grabchps(&ps_spec);
  1459. X
  1460. X /* Move onto the character following the special (escape) character. */
  1461. X NEXTCH;
  1462. X
  1463. X /* Now react to the character. In most cases, the special sequence is simply */
  1464. X /* a marker in the input and we can simply transmit it. The nasty special    */
  1465. X /* case sequences are left until the end of the switch statement.            */
  1466. X /* Purists will complain about how all the case options are hardwired and    */
  1467. X /* say that symbols should have been used. They once were, but were taken    */
  1468. X /* out when it was discovered that the symbols had cryptic names (because of */
  1469. X /* the portability eight-character rule) and were only used here anyway.     */
  1470. X switch (toupper(ch))
  1471. X   {
  1472. X    case '<': sendspec(&ps_spec,TK_ONAM,DONTCARE); break;
  1473. X    case '>': sendspec(&ps_spec,TK_CNAM,DONTCARE); break;
  1474. X    case '{': sendspec(&ps_spec,TK_ODEF,DONTCARE); break;
  1475. X    case '}': sendspec(&ps_spec,TK_CDEF,DONTCARE); break;
  1476. X    case '(': sendspec(&ps_spec,TK_OPAR,DONTCARE); break;
  1477. X    case ')': sendspec(&ps_spec,TK_CPAR,DONTCARE); break;
  1478. X    case ',': sendspec(&ps_spec,TK_COMA,DONTCARE); break;
  1479. X    case '"': sendspec(&ps_spec,TK_QUOT,DONTCARE); break;
  1480. X    case '/': sendspec(&ps_spec,TK_EMPH,DONTCARE); break;
  1481. X    case 'A': sendspec(&ps_spec,TK_NSEC,1); chksol(); break;
  1482. X    case 'B': sendspec(&ps_spec,TK_NSEC,2); chksol(); break;
  1483. X    case 'C': sendspec(&ps_spec,TK_NSEC,3); chksol(); break;
  1484. X    case 'D': sendspec(&ps_spec,TK_NSEC,4); chksol(); break;
  1485. X    case 'E': sendspec(&ps_spec,TK_NSEC,5); chksol(); break;
  1486. X    case '1': sendspec(&ps_spec,TK_PARM,1); break;
  1487. X    case '2': sendspec(&ps_spec,TK_PARM,2); break;
  1488. X    case '3': sendspec(&ps_spec,TK_PARM,3); break;
  1489. X    case '4': sendspec(&ps_spec,TK_PARM,4); break;
  1490. X    case '5': sendspec(&ps_spec,TK_PARM,5); break;
  1491. X    case '6': sendspec(&ps_spec,TK_PARM,6); break;
  1492. X    case '7': sendspec(&ps_spec,TK_PARM,7); break;
  1493. X    case '8': sendspec(&ps_spec,TK_PARM,8); break;
  1494. X    case '9': sendspec(&ps_spec,TK_PARM,9); break;
  1495. X    case 'M': sendspec(&ps_spec,TK_MANY,DONTCARE); break;
  1496. X    case 'Z': sendspec(&ps_spec,TK_ZERO,DONTCARE); break;
  1497. X    case 'O': sendspec(&ps_spec,TK_FDEF,DONTCARE); chksol(); break;
  1498. X    case '$': sendspec(&ps_spec,TK_MDEF,DONTCARE); chksol(); break;
  1499. X    case EOL:
  1500. X       lr_err(&ps_spec,"<special><endofline> is not a legal special sequence.");
  1501. X       break;
  1502. X    case ' ':
  1503. X       lr_err(&ps_spec,"<special><space> is not a legal special sequence.");
  1504. X       break;
  1505. X    case '@':
  1506. X       /* @ instructs FunnelWeb to replace the special construct with the */
  1507. X       /* special character. Luckily one appears just before the @ !!     */
  1508. X       /* Note: FALSE is OK because space is not a legal specialch.       */
  1509. X       sendtext(&ps_spec,p_ch-1,p_ch-1,FALSE);
  1510. X       break;
  1511. X    case '-':
  1512. X       /* - instructs FunnelWeb to suppress the following end of line. */
  1513. X       if (*(p_ch+1) == EOL)
  1514. X          NEXTCH
  1515. X       else
  1516. X          lr_err(&ps_spec,
  1517. X                 "Suppress EOL sequence is legal only at the end of a line.");
  1518. X       break;
  1519. X    case '+':
  1520. X       /* + instructs FunnelWeb to insert an EOL. We can't look to the end of */
  1521. X       /* the previous line to find an EOL as this might be the first line.   */
  1522. X       /* Running ahead to the end of the line is expensive, and having the   */
  1523. X       /* liner mini-package maintain a variable for it would be extra        */
  1524. X       /* housekeeping. Instead of all this, we just point to a static.       */
  1525. X       {STAVAR char stateol = EOL;
  1526. X        sendtext(&ps_spec,&stateol,&stateol,TRUE);}
  1527. X       break;
  1528. X    case '=':
  1529. X      /* = instructs FunnelWeb to change the special character to the         */
  1530. X      /* character following the <special>= sequence.                         */
  1531. X      NEXTCH;
  1532. X      if (ch == ' ')
  1533. X        {
  1534. X         lr_err(&ps_spec,"You cannot set the special character to <space>!");
  1535. X         lr_mes(&ps_spec,"Special sequence ignored.");
  1536. X        }
  1537. X      else if (ch == EOL)
  1538. X        {
  1539. X         lr_err(&ps_spec,
  1540. X                "You cannot set the special character to <endofline>!");
  1541. X         lr_mes(&ps_spec,"Special sequence ignored.");
  1542. X        }
  1543. X      else
  1544. X         specialch=ch;
  1545. X      break;
  1546. X    case '!':
  1547. X       /* ! instructs FunnelWeb to ignore the rest of the line (a comment). */
  1548. X       skiptoeol();
  1549. X       break;
  1550. X    case 'I':
  1551. X       /* i instructs FunnelWeb to include a file. */
  1552. X       incl_fil(&ps_spec);
  1553. X       break;
  1554. X    case '^':
  1555. X       /* ^ instructs FunnelWeb to insert a specific ascii character. */
  1556. X       do_ascii(&ps_spec);
  1557. X       break;
  1558. X    case '#':
  1559. X       /* # instructs FunnelWeb to transmit a two character name "#c". */
  1560. X       do_name(&ps_spec);
  1561. X       break;
  1562. X    case 'P':
  1563. X       /* P is used as a miscellaneous PRAGMA. */
  1564. X       do_pragma(&ps_spec,FALSE);
  1565. X       break;
  1566. X    case 'T':
  1567. X       /* T introduces a one-line typesetting directive. */
  1568. X       do_pragma(&ps_spec,TRUE);
  1569. X       break;
  1570. X    default:
  1571. X       lr_err(&ps_spec,"Unknown special sequence.");
  1572. X       break;
  1573. X   }
  1574. X
  1575. X /* The switch statment absorbs the special sequence and its effects.      */
  1576. X /* This NEXTCH places us on the character following the special sequence. */
  1577. X NEXTCH;
  1578. X}
  1579. X
  1580. X/******************************************************************************/
  1581. X
  1582. LOCAL void scantext P_((void));
  1583. LOCAL void scantext()
  1584. X/* Upon entry, we know that the current character is not EOF and that it is   */
  1585. X/* not the special character. Our task is to parse as much text as we can and */
  1586. X/* ship it off as a text token. The scanner will probably spend most of its   */
  1587. X/* time in the loops in this function so it is important that they be         */
  1588. X/* efficient. That is why two loops are used to deal with detecting           */
  1589. X/* whitespace rather than a flag.                                             */
  1590. X/* Upon return, the current character is the character following the text     */
  1591. X/* sequence. This is guaranteed to be the special character or an EOF.        */
  1592. X{
  1593. X ps_t ps_start;        /* Position of first character of text sequence.       */
  1594. X char *p_first = p_ch; /* Pointer  to first character of text sequence.       */
  1595. X
  1596. X /* Grab a copy of the position of this token. */
  1597. X grabchps(&ps_start);
  1598. X
  1599. X /* Scan whitespace. */
  1600. X while (ch==' ' || ch==EOL)
  1601. X    NEXTCH;
  1602. X
  1603. X /* If we hit something that ends a text token */
  1604. X /* then we can transmit a white text token.   */
  1605. X if (ch==specialch || ch==EOFCH)
  1606. X    {sendtext(&ps_start,p_first,p_ch-1,TRUE); return;}
  1607. X
  1608. X /* Otherwise we have some more (non-white) text to scan. */
  1609. X /* We can then send a non-white text token.              */
  1610. X while (ch!=specialch && ch!=EOFCH)
  1611. X    NEXTCH;
  1612. X sendtext(&ps_start,p_first,p_ch-1,FALSE);
  1613. X}
  1614. X
  1615. X/******************************************************************************/
  1616. X
  1617. LOCAL void scan_file(p_fname)
  1618. X/* This function scans a single file. It's argument is the name of the file   */
  1619. X/* to be scanned. scan_file calls the mapper to map in the file and then      */
  1620. X/* scans the text of the mapped file using the liner mini-package. The result */
  1621. X/* of the scan is additions to the line and token list, and diagnostics sent  */
  1622. X/* to the lister package. If an include directive is encountered, this        */
  1623. X/* function is called recursively.                                            */
  1624. char *p_fname;
  1625. X{
  1626. X char  *p_mapped;  /* Pointer to the mapped file.                             */
  1627. X ulong  length;    /* Number of bytes in the mapped file.                     */
  1628. X char  *errstr;    /* Error string returned by mapper.                        */
  1629. X bool   addedeol;  /* Did we have to add an EOL to the end of the last line?  */
  1630. X
  1631. X /* Check to see if the file exists. */
  1632. X if (!fexists(p_fname))
  1633. X   {
  1634. X    if (inclevel==0)
  1635. X      {
  1636. X       /* Failure to find the main file is a severe error. */
  1637. X       if (option.op_b7_b)
  1638. X          sprintf(linet1,"S: Error opening input file \"%s\".",SUPPNAME);
  1639. X       else
  1640. X          sprintf(linet1,"S: Error opening input file \"%s\".",p_fname);
  1641. X       wl_l(linet1);
  1642. X       /* Although strictly speaking we should suppress this error from the   */
  1643. X       /* screen stream unless option.op_s_b is set, absence of an input file */
  1644. X       /* is such an important error, that we write it out anyway.            */
  1645. X       /* if (option.op_s_b) */
  1646. X       wl_sj(linet1);
  1647. X       num_sev++;
  1648. X       return;
  1649. X      }
  1650. X    else
  1651. X      {
  1652. X       /* Failure to find an include file is an ordinary error. */
  1653. X       ps_t ps;
  1654. X       ps.ps_line   = globalno;
  1655. X       ps.ps_column = 4;
  1656. X       lr_err(&ps,"Error opening include file.");
  1657. X       if (option.op_b7_b)
  1658. X          sprintf(linet1,
  1659. X             "The include file's expanded name was \"%s\".",SUPPNAME);
  1660. X       else
  1661. X          sprintf(linet1,
  1662. X             "The include file's expanded name was \"%s\".",p_fname);
  1663. X       lr_mes(&ps,linet1);
  1664. X       return;
  1665. X      }
  1666. X   }
  1667. X
  1668. X /* Map the specified file into memory. We need to change from the scanner    */
  1669. X /* clock to the mapper clock to keep the time accounting correct here.       */
  1670. X ck_stop(p_scan);
  1671. X ck_start(p_mapp);
  1672. X errstr=map_file(p_fname,&p_mapped,&length);
  1673. X ck_stop(p_mapp);
  1674. X ck_start(p_scan);
  1675. X
  1676. X /* Abort if the mapping was not possible. */
  1677. X if (errstr != NULL)
  1678. X    if (inclevel==0)
  1679. X      {
  1680. X       /* Failure to map the main file is a severe error. */
  1681. X       if (option.op_b7_b)
  1682. X          sprintf(linet1,"S: Error reading input file \"%s\".",SUPPNAME);
  1683. X       else
  1684. X          sprintf(linet1,"S: Error reading input file \"%s\".",p_fname);
  1685. X       wl_l(linet1); if (option.op_s_b) wl_sj(linet1);
  1686. X       wl_l(errstr); if (option.op_s_b) wl_sj(errstr);
  1687. X       num_sev++;
  1688. X       return;
  1689. X      }
  1690. X    else
  1691. X      {
  1692. X       /* Failure to find an include file is an ordinary error. */
  1693. X       ps_t ps;
  1694. X       ps.ps_line   = globalno;
  1695. X       ps.ps_column = 4;
  1696. X       lr_err(&ps,"Error reading include file.");
  1697. X       lr_mes(&ps,errstr);
  1698. X       if (option.op_b7_b)
  1699. X          sprintf(linet1,"The include file's expanded name was \"%s\".",
  1700. X                  SUPPNAME);
  1701. X       else
  1702. X          sprintf(linet1,"The include file's expanded name was \"%s\".",
  1703. X                  p_fname);
  1704. X       lr_mes(&ps,linet1);
  1705. X       return;
  1706. X      }
  1707. X
  1708. X /* Dump the mapped file if requested. */
  1709. X if (option.op_b1_b)
  1710. X   {
  1711. X    if (option.op_b7_b)
  1712. X       sprintf(linet1,"Dump of mapped file \"%s\".",SUPPNAME);
  1713. X    else
  1714. X       sprintf(linet1,"Dump of mapped file \"%s\".",p_fname);
  1715. X    wl_l(linet1);
  1716. X    dm_mem(&f_l,p_mapped,length);
  1717. X   }
  1718. X
  1719. X /* If the file is absolutely empty, we have to warn the user. Also, this is  */
  1720. X /* a special case we can do without, and so we return here if file is empty. */
  1721. X if (length==0)
  1722. X   {
  1723. X    ps_t ps;
  1724. X    /* The empty file could be the main file or an include file.              */
  1725. X    /* If the empty file is the main file, we want the diagnostic to point to */
  1726. X    /*    the EOF marker which will appear as line 1.                         */
  1727. X    /* If the empty file is an include file, we wish to point the diagnostic  */
  1728. X    /*    to the line containing the include command. This is globalno.       */
  1729. X    /* In both cases, we want the diagnostic to point to column 1.            */
  1730. X    ps.ps_column=1;
  1731. X    if (inclevel==0)
  1732. X      {
  1733. X       ps.ps_line=1;
  1734. X       lr_war(&ps,"Input file is empty (not a byte in syte)!");
  1735. X      }
  1736. X    else
  1737. X      {
  1738. X       ps.ps_line=globalno;
  1739. X       lr_war(&ps,"Include file is empty (not a byte in syte)!");
  1740. X      }
  1741. X    return;
  1742. X   }
  1743. X
  1744. X /* Scanning is considerably simplified if we can guarantee that we will not  */
  1745. X /* run into an EOF without first hitting an EOL. The following code takes    */
  1746. X /* care of this by tacking one on the end if necessary and also adds an      */
  1747. X /* EOF character on the end, which also simplifies the scanning. We can get  */
  1748. X /* away with all this because the mapper purposefully leaves at least two    */
  1749. X /* bytes free for us at the end of the mapped file.                          */
  1750. X addedeol=FALSE;
  1751. X if (p_mapped[length-1] != EOL)
  1752. X    {p_mapped[length++]=EOL; addedeol=TRUE;}
  1753. X p_mapped[length]=EOFCH;
  1754. X
  1755. X /* Initialize the variables "instantiated over a single file". */
  1756. X inln_max  = 80;
  1757. X specialch = CH_DSPE;
  1758. X localno   = 0;
  1759. X p_eof     = &p_mapped[length];
  1760. X
  1761. X /* Crank up the line subscanner system with a call to prepline. */
  1762. X /* Then enter the main scanning loop.                           */
  1763. X /* All input consists of alternating special and text sequences */
  1764. X /* terminated by EOF.                                           */
  1765. X prepline(p_mapped);
  1766. X while (ch!=EOFCH)
  1767. X    if (ch==specialch)
  1768. X       scanspec();
  1769. X    else
  1770. X       scantext();
  1771. X
  1772. X /* Now that we are at the end of the scanned file and the scanning markers   */
  1773. X /* are all sitting on the end of the file, it is a good time to issue        */
  1774. X /* diagnostics about problems at the end of the file.                        */
  1775. X if (addedeol)
  1776. X   {
  1777. X    ps_t ps;
  1778. X    /* We want the diagnostic to point to the EOF line. Hence "global+1".     */
  1779. X    ps.ps_line   = globalno+1;
  1780. X    ps.ps_column = 1;
  1781. X    if (inclevel==0)
  1782. X       lr_war(&ps,"The last line of the input file was terminated by EOF.");
  1783. X    else
  1784. X       lr_war(&ps,"The last line of the include file was terminated by EOF.");
  1785. X    lr_mes(&ps,"An EOL was inserted at the end of the last line.");
  1786. X   }
  1787. X}
  1788. X
  1789. X/******************************************************************************/
  1790. X
  1791. XEXPORT void scanner(p_amapp,p_ascan)
  1792. X/* This is the scanner's main routine and the only exported function.         */
  1793. p_ck_t p_amapp; /* Mapper's clock (stopped).  */
  1794. p_ck_t p_ascan; /* Scanner's clock (running). */
  1795. X{
  1796. X /* Copy the arguments into globals where we can get at them. */
  1797. X p_mapp=p_amapp;
  1798. X p_scan=p_ascan;
  1799. X
  1800. X /* Apart from diagnostic messages sent to the lister, the only output of     */
  1801. X /* the scanner is two global lists holding a list of lines and a list of     */
  1802. X /* tokens. The scanner creates these lists simultaneously.                   */
  1803. X /* We have to initialize them here before we get into 'scan_file' which      */
  1804. X /* calls itself recursively if an include file command is encountered.       */
  1805. X line_list =ls_cre(sizeof(ln_t));
  1806. X token_list=ls_cre(sizeof(tk_t));
  1807. X
  1808. X /* Initialize all the variables instantiated throughout the entire scan.     */
  1809. X globalno  = 0;
  1810. X inclevel  = 0;
  1811. X seenind   = FALSE;
  1812. X seentyp   = FALSE;
  1813. X seenlimo  = FALSE;
  1814. X
  1815. X /* We also have to initialize localno in case the input file is empty and    */
  1816. X /* it never gets initialized before being sucked into being used as the      */
  1817. X /* local number for the end of file marker.                                  */
  1818. X localno=0;
  1819. X
  1820. X /* Initialize the global indentation flag to the default value. */
  1821. X tgindent=TRUE;
  1822. X
  1823. X /* Initialize the global product line length limit to the default value. */
  1824. X tglinmax=80;
  1825. X
  1826. X /* Initialize the global typesetter flag to the default value. */
  1827. X tr_codes=TR_NONE;
  1828. X
  1829. X /* Scan the top level file whose name is obtained from the command line.     */
  1830. X as_cold(option.op_f_b,"scanner: -F!!!!");
  1831. X
  1832. X /* Work out what the input file name should be. */
  1833. X {
  1834. X  fn_t fname;
  1835. X  strcpy(fname,"");              /* Start with an empty string.               */
  1836. X  fn_ins(fname,".fw");
  1837. X  fn_ins(fname,option.op_f_s);
  1838. X  scan_file(fname);
  1839. X }
  1840. X
  1841. X /* The scan_file function scans the main input file and all of its included  */
  1842. X /* files, but it does not append a TK_EOF token to the end. This call does   */
  1843. X /* this and also adds a line to the line list for EOF.                       */
  1844. X add_eof();
  1845. X}
  1846. X
  1847. X/******************************************************************************/
  1848. X/*                              End of SCANNER.C                              */
  1849. X/******************************************************************************/
  1850. END_OF_FILE
  1851. if test 69225 -ne `wc -c <'sources/scanner.c'`; then
  1852.     echo shar: \"'sources/scanner.c'\" unpacked with wrong size!
  1853. fi
  1854. # end of 'sources/scanner.c'
  1855. fi
  1856. echo shar: End of archive 17 \(of 20\).
  1857. cp /dev/null ark17isdone
  1858. MISSING=""
  1859. for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ; do
  1860.     if test ! -f ark${I}isdone ; then
  1861.     MISSING="${MISSING} ${I}"
  1862.     fi
  1863. done
  1864. if test "${MISSING}" = "" ; then
  1865.     echo You have unpacked all 20 archives.
  1866.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  1867. else
  1868.     echo You still need to unpack the following archives:
  1869.     echo "        " ${MISSING}
  1870. fi
  1871. ##  End of shell archive.
  1872. exit 0
  1873.