3
FWEB version 1.62 (September 25, 1998)
5
Based on version 0.5 of S. Levy's CWEB [copyright (C) 1987 Princeton University]
7
@x-----------------------------------------------------------------------------
15
This file contains code common to both \.{TANGLE} and
16
\.{WEAVE}, that roughly concerns the following problems: character
17
uniformity, input routines, error handling and parsing of command line. We
18
have tried to concentrate in this file all the system dependencies, so as
19
to maximize portability.
21
\FWEB\ has been written to work with a variety of C~compilers, not
22
necessarily ANSI. In particular, the special macro tokens~'\.\#'
23
and~'\.{\#\#}' are not used in any \.{@@d} commands; when such
24
constructions have been needed, the \WEB\ macro definition~\.{@@m} has been
25
used. The |enum| feature has been assumed to exist. \CWEB\ did not use
26
|enum|, so there's a fair amount of inherited \CWEB\ code that should
27
eventually be changed to |enum|, which makes debugging much easier. This
28
will be done in future versions. One should study the header files
29
\.{os.hweb}, \.{proto.hweb}, \.{includes.hweb}, and \.{custom.web} for
30
further information about machine dependencies.
32
In the texts below we will sometimes use \.{WEB} to refer to either of the
33
two component programs, if no confusion can arise.
39
@ Here is the overall appearance of this file:
41
@d VERSION "1.61" // For development, use |$REVISION| here.
42
@d RELEASE_DATE "September 23, 1998"
45
@<Possibly split into parts@>@;
48
@<Common code for \.{TANGLE} and \.{WEAVE}@>@;
49
@<Typedef declarations@>@;
50
@<Other definitions@>@;
51
@<Global variables@>@;
53
/* For pc's, the file is split into two compilable parts using the
54
compiler-line macro |part|, which must equal either~1 or~2. */
75
@ A dummy module for \.{scraps.hweb}.
77
@<Rest of |trans_plus| union@>=
79
@ For personal computers, we sometimes need to initialize the stack size.
81
@d STKLEN 20000U /* Borland needs this number; Microsoft requires it from
86
#ifdef ibmpc /* \.{Machine-dependent}: For initializing the stack size. */
88
extern unsigned _stklen = STKLEN;
94
One of the main goals in the design of \.{WEB} has been to make it readily
95
portable between a wide variety of computers. Yet \.{WEB} by its very
96
nature must use a greater variety of characters than most computer
97
programs deal with, and character encoding is one of the areas in which
98
existing machines differ most widely from each other.
100
To resolve this problem, all input to \.{WEAVE} and \.{TANGLE} is converted
101
to an internal seven-bit code that is essentially standard ASCII, the
102
``American Standard Code for Information Interchange.'' The conversion
103
is done immediately when each character is read in. Conversely,
104
characters are converted from ASCII to the user's external
105
representation just before they are output.
107
Such an internal code can be accessed by users of \.{WEB} by means of
108
constructions like \.{@@'A'}, which should be distinguished from \.{'A'}.
109
The former is transformed by \.{TANGLE} into an integer that is the
110
internal code of~\.A, but the latter, a |char| constant, is not touched by
111
\.{WEB}, and will be interpreted by the compiler according to the machine's
115
Here is a table of the standard visible ASCII codes (\.{\ } stands for
117
$$\def\:{\char\count255\global\advance\count255 by 1}
120
\hbox{\hbox to 40pt{\it\hfill0\/\hfill}%
121
\hbox to 40pt{\it\hfill1\/\hfill}%
122
\hbox to 40pt{\it\hfill2\/\hfill}%
123
\hbox to 40pt{\it\hfill3\/\hfill}%
124
\hbox to 40pt{\it\hfill4\/\hfill}%
125
\hbox to 40pt{\it\hfill5\/\hfill}%
126
\hbox to 40pt{\it\hfill6\/\hfill}%
127
\hbox to 40pt{\it\hfill7\/\hfill}}
130
\def\^{\vrule height 10.5pt depth 4.5pt}
131
\halign{\hbox to 0pt{\hskip -24pt\WO{\~#}\hfill}&\^
132
\hbox to 40pt{\tt\hfill#\hfill\^}&
133
&\hbox to 40pt{\tt\hfill#\hfill\^}\cr
134
04&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
135
05&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
136
06&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
137
07&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
138
10&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
139
11&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
140
12&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
141
13&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
142
14&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
143
15&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
144
16&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
145
17&\:&\:&\:&\:&\:&\:&\:\cr}
146
\hrule width 280pt}$$
148
We introduce new types to distinguish between the transliterated characters
149
and the characters in the outside world. Let all ``interesting'' values
150
that a |char| variable may take lie between |first_text_char| and
151
|last_text_char|; for the ASCII code we can take |first_text_char=0| and
152
|last_text_char=0177|. We will tell \.{WEB} to convert all input characters
153
in this range to its own code, and balk at characters outside the range.
154
We make two assumptions: |first_text_char>=0| and |char| has room for at
157
Following \CWEB, \FWEB\ deals with character constants by prefacing them
158
with '\.{@@}' if they are to be interpreted as |ASCII|. In \CWEB, \TANGLE\
159
translated such constants into octal, which makes the output hard to read
160
while debugging. While this mechanism is necessary for a non-ASCII machine,
161
it is redundant for an ASCII machine. Thus, for the latter by default
162
\FWEB\ does not translate |ASCII| character constants. To force it to do
163
so, use the `\.{-a}' command-line option. For non-ASCII machines, the
164
translation will always be done.
167
{\bf WARNING:} \FWEB\ has been tested only on an ASCII machine. It is
168
100\% likely that some debugging will be necessary for any other kind of
169
machine; the $|outer_char| \Leftrightarrow |ASCII|$ conversions are only
170
about 75\% in place at this point. (Even this is a substantial improvement
173
@^system dependencies@>
175
@d first_text_char 0 // Lowest interesting value of an |outer_char|.
176
@d last_text_char 0377 // Highest interesting value of an |outer_char|.
178
@ The \.{WEAVE} and \.{TANGLE} processors convert between ASCII code and
179
the user's external character set by means of arrays~|xord| and~|xchr|
180
that are analogous to PASCAL's |ord| and |chr| functions. Thus,
181
$\hbox{internal\_WEB\_chr} = |xord[@texternal\_chr@>]|$ and
182
$\hbox{external\_chr} = |xchr[@tinternal\_WEB\_chr@>]|$.
186
IN_COMMON ASCII xord[last_text_char+1]; // Converts input chars.\ to |ASCII|.
187
#ifdef scramble_ASCII
188
ASCII xxord[last_text_char+1]; // For scrambling.
190
IN_COMMON outer_char xchr[0200]; // Converts output chars.\ from |ASCII|.
192
@ Every system supporting \cee\ must be able to read and write the
193
95~visible characters of standard ASCII above (although not necessarily
194
using the ASCII codes to represent them). Conversely, these characters,
195
plus the newline, are sufficient to write any \cee\ program. Other
196
characters are desirable mainly in strings, and they can be referred to by
197
means of escape sequences like~|'\t'|.
199
The basic implementation of \.{WEB}, then, only has to assign an~|xord| to
200
these 95 characters (newlines are swallowed by the reading
201
routines). The easiest way to do this is to assign the characters to
202
their positions in~|xchr| and then invert the correspondence:
209
IN_RATFOR SRTN is_Rat_present PROTO((VOID));
210
struct tm *t = the_localtime(); // Initialize the time.
212
#if(TRANSLATE_ASCII || DEBUG_XCHR)
213
translate_ASCII = YES; // Force it for the cases that really need it.
216
is_Rat_present(); // Find out if the \Ratfor\ package was linked on.
218
@<Set up character set translations@>@;
219
@<Initialize \TeX\ categories@>;
220
@<Initialize static pointers@>;
221
predefine_macros(); /* Must be done before parsing the command line, so
222
the user can undefine or override stuff if desired. */
223
@<Initialize flags@>@;
225
@<Scan arguments and open output files@>;
228
banner(); // Start-up banner.
231
/* The next two have to be done after reading the command line. */
232
@<Allocate dynamic memory@>@;
233
@<Initialize dynamic pointers@>;
234
@<Execute deferred command-line options@>@;
236
more_includes(&incl, wt_style.Idir);
239
read_aux(); // Process the \.{.aux} file.
243
@<Set up character set...@>=
245
STRNCPY(xchr," !\"#$%&'()*+,-./0123456789\
246
:;<=>?@@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ",
249
/* Interesting ``invisible'' things to output. */
250
xchr[tab_mark] = '\t';
253
@<System-dependent parts of character set@>;
254
@<Invert |xchr| to get |xord|@>;
257
@ The following system-independent code makes the |xord| array contain
258
a suitable inverse to the information in |xchr|.
260
@<Invert |xchr|...@>=
262
int i; /* to invert the correspondence */
265
/* Initialize to |ASCII| blanks. */
266
for (i=first_text_char; i<=last_text_char; i++)
269
#ifdef scramble_ASCII
274
/* The |NUL| and |DEL| characters are left alone! */
275
for (i=1; i<0177; i++)
277
xord[(eight_bits)XCHR_[i]] = (ASCII)i;
278
#ifdef scramble_ASCII
279
xxord[(eight_bits)wt_style.xchr[i]] = i;
283
#ifdef scramble_ASCII
284
for(i=0177; i<=0377; i++)
285
xxord[(eight_bits)wt_style.xchr[i]] = i;
289
for(i=0177; i<=0377; i++)
290
xord[(eight_bits)XCHR_[i]] = (ASCII)i;
294
@ Now follow routines that translate |outer_char| strings into |ASCII|.
295
First, to avoid messing with possibly read-only memory, we have a routine
296
that allocates new storage for the string. (Thanks to Thorsten Ohl for
302
x__to_ASCII FCN((p0))
303
CONST outer_char HUGE *p0 C1("Character string to translate.")@;
307
ASCII HUGE *buffer, HUGE *p;
308
CONST outer_char HUGE *q;
310
buffer = GET_MEM("x__to_ASCII arg",STRLEN(p0)+1,ASCII);
312
for (p = buffer, q = p0; *q; p++, q++)
319
return (ASCII HUGE *)p0;
322
@ For dealing with arrays, we have to |realloc| the space. (Again, thanks
329
CONST outer_char HUGE *p0 C1("Character string to translate.")@;
331
static size_t bufsiz = 0;
332
static ASCII HUGE *buffer = NULL;
337
CONST outer_char HUGE *q;
339
while (bufsiz <= STRLEN (p0) + 1)
342
buffer = GET_MEM("x_to_ASCII_buf",bufsiz=32,ASCII);
343
/* Some systems don't like a |NULL| argument to |realloc|. */
346
buffer = (ASCII HUGE *) REALLOC(buffer, bufsiz+32, bufsiz);
351
FATAL(C, "!! No more memory (x_to_ASCII)", "");
354
for (p = buffer, q = p0; *q; p++, q++)
361
return (ASCII HUGE *)p0;
364
@ Finally, this function converts in place.
370
outer_char HUGE *p0 C1("Character string to translate.")@;
375
for(p=(ASCII HUGE *)p0; *p; p++)
378
return (ASCII HUGE *)p0;
381
@ Here is the inverse routine, converting in place |ASCII| to |outer_char|.
387
ASCII HUGE *p0 C1("Internal string to translate to outer world.")@;
392
for(p=(outer_char HUGE *)p0; *p; p++) *p = XCHR(*(ASCII HUGE *)p);
394
return (outer_char HUGE *)p0;
397
@ Some \cee\ compilers accept an extended character set, so that one can
398
type things like~\.{\^\^Z} instead of~\.{!=}. If that's the case in your
399
system, you should change the relevant part of \.{typedefs.web}, assigning
400
positions~|01| to~|037| in the most convenient way; for example, at MIT you
401
can just say $$\hbox{|for (i=1; i<=037; i++) xchr[i]=i;|}$$ since \.{WEB}'s
402
character set is essentially identical to MIT's, even with respect to
403
characters less than |040| (see the definitions below). If, however, the
404
changes do not conform with these definitions you should change the
405
definitions as well. (See \.{typedefs.web} for definitions of |and_and|,
408
@^system dependencies@>
411
@<System-dependent parts of character set@>= /* nothing needs to be done */
414
At the point during phase one where we're recognizing
415
identifiers, we sometimes have to determine whether the identifier is a
416
special one. This is a simple byte-by-byte comparison. However, things are
417
complicated because by that time the input has been converted to |ASCII|.
418
Therefore, the comparison tables must also be converted to |ASCII|.
420
@ Test whether an identifier is a |BUILT_IN|.
425
is_include_like(VOID)
427
/* Check for \.{m4} built-in. */
428
if(m4 && is_in(incl_likes,id_first,id_loc)) return YES;
430
/* The \.{WEB} functions behave as built-ins for \.{WEAVE}, but as macros
432
if(program==tangle) return NO;
434
if(!(*id_first == @'$' || *id_first == @'_')) return NO; // Speed up |is_in|.
435
return is_in(WEB_incl_likes,id_first,id_loc);
438
@ Convert lists to |ASCII|.
440
SRTN conv_bi FCN((b))
441
BUILT_IN HUGE *b C1("")@;
443
for( ; b->n != 0; b++)
444
b->name = x__to_ASCII((outer_char *)b->name);
449
DOTS HUGE *d C1("")@;
451
for( ;d->code != 0; d++)
452
d->symbol = x__to_ASCII((outer_char *)d->symbol);
455
@ Hunt through a |BUILT_IN| list.
460
is_in FCN((b0,p0,p1))
461
CONST BUILT_IN b0[] C0("Array of built-ins.")@;
462
CONST ASCII HUGE *p0 C0("Start of text.")@;
463
CONST ASCII HUGE *p1 C1("End of text.")@;
465
CONST BUILT_IN HUGE *b;
467
for(b=b0; b->n != 0; b++)
468
if(web_strcmp(b->name,b->name+b->n,p0,p1) == EQUAL)
477
extern DOTS dots0[],mcmds[]; /* These are put into \.{typedefs.web} so we can
478
make use of the various \.{@@d}s. */
480
@ Dot constants are allocated dynamically. Because of \Fortran-90's ability
481
to define new constants, we have to allow for expanding the size of the
485
IN_COMMON BUF_SIZE delta_dots; // Expand the table in increments of this size.
486
IN_COMMON BUF_SIZE ndots; // The current total size.
487
IN_COMMON DOTS HUGE *dots,HUGE *dots_end,HUGE *next_dot; // The dynamic array.
489
@ Here we do the initial allocation of the dynamic |dots| table.
490
@<Allocate dynamic...@>=
494
ALLOC(DOTS,dots,ABBREV(delta_dots),delta_dots,0);
495
ndots = delta_dots; /* Initialize total size. */
496
dots_end = dots + ndots;
497
next_dot = dots; /* Current pointer. */
499
/* Fill the table with the initial values. */
500
for(d=dots0; d->len != 0; d++)
502
if(dot_code(dots,d->symbol,d->symbol+d->len,d->code) != d->code)
503
CONFUSION("dots allocation","Invalid dot code");
507
@ Either case is allowed for the dot constants. We will convert everything
508
between the dots in place to upper case.
514
ASCII HUGE *p C0("Start of text.")@;
515
int n C1("Number of bytes to convert in place.")@;
520
p[k] = A_TO_UPPER(p[k]); /* Convert $n$~bytes in place. */
522
return p; /* Return beginning address of string. */
525
@ The following routine simply determines whether a decimal point begins a dot
526
constant; this is used in parsing potential decimal constants.
535
ASCII temp[MAX_DOT_LENGTH]; /* For converting the putative constant to
538
/* Scan to the end of the dot constant (all of which are alphabetic). */
539
for(p=loc,n=0; n<MAX_DOT_LENGTH; n++,p++)
540
if(*p==@'.' || !isAlpha(*p))
543
if(*p != @'.' || p==loc)
544
return NO; /* It wasn't stopped by a period, or
545
the scan didn't even get started, so it
546
can't be a dot constant. */
549
return BOOLEAN(CHOICE(dot_code(dots,uppercase(temp,n),temp+n,dot_const),
553
@ Is it in a |DOTS| list? If so, return the associated code. If it is not,
554
add it to the table. (This was required by \FORTRAN-90.) If necessary,
560
dot_code FCN((d0,p0,p1,new_code))
561
DOTS HUGE *d0 C0("|DOTS| array.")@;
562
CONST ASCII HUGE *p0 C0("Start of text.")@;
563
CONST ASCII HUGE *p1 C0("End of text.")@;
564
eight_bits new_code C1("May we extend the table or not?")@;
569
for(d=d0; d < next_dot; d++)
570
if(web_strcmp(d->symbol,d->symbol+d->len,p0,p1) == EQUAL)
572
/* Check for an overloaded operator. If so, remember the details. */
573
if(d->code == dot_const)
575
STRCPY(dot_op.name+1,d->symbol);
577
dot_op.num = (eight_bits)(d-dots);
583
/* Didn't find the dot constant or operator in the table. Enlarge the table
585
if(!new_code) return NO; /* One can add to |dots|, but not |mcmds|. */
587
if(next_dot == dots_end)
589
if( (dots=(DOTS HUGE *)REALLOC(dots,
590
(ndots+delta_dots)*sizeof(DOTS), ndots*sizeof(DOTS)))==NULL )
591
OVERFLW("dot operators",ABBREV(delta_dots));
593
next_dot = dots + ndots; /* Next available position. */
594
ndots += delta_dots; /* New total length. */
595
dots_end = dots + ndots; /* Upper bound. */
598
/* Add operator to table. */
599
next_dot->len = PTR_DIFF(short, p1, p0);
600
next_dot->symbol = GET_MEM("next_dot->symbol",next_dot->len + 1,ASCII);
601
STRNCPY(next_dot->symbol,p0,next_dot->len);
602
next_dot->code = new_code; /* Mark as overloaded, or initialize. */
603
next_dot->cat = 1; /* ??? */
609
@ Is it a macro preprocessor command in a |DOTS| list?
614
is_mcmd FCN((d0,p0,p1))
615
DOTS HUGE *d0 C0("|DOTS| array.")@;
616
CONST ASCII HUGE *p0 C0("Start of text.")@;
617
CONST ASCII HUGE *p1 C1("End of text.")@;
621
for(d=d0; d->code; d++)
622
if(web_strcmp(d->symbol,d->symbol+d->len,p0,p1) == EQUAL)
625
return 0; // Not a preprocessor command.
630
The lowest level of input to the \.{WEB} programs is
631
performed by |input_ln|, which must be told which file to read from.
632
Unlike the original \WEB s, there is a separate input buffer for each
633
possible open file, as well as associated parameters for each buffer. These
634
parameters are collected in an array of structures of type \&{INPUT\_PRMS},
635
which contains symmetric entries for both input files and change files.
636
The parameters for the current file are in |*cur0_prms| and are set upon
637
entry to |input_ln|. This change was necessary in order to accomodate
638
\Fortran, which, since it reads ahead, leaves stuff in the tail end of the
639
buffer, to be read on the next call to |input_ln|. If one doesn't have
640
separate buffers, the change file mechanism won't work properly, nor will
641
the include facility.
643
The return value of |input_ln| is |YES| if the read is successful and |NO| if
644
not (generally this means the file has ended). The conventions
645
of \TeX\ are followed; i.e., the characters of the next line of the file
646
are translated to |ASCII| code and copied into the |cur_buffer| array,
647
and the global variable |limit| is set to the first unoccupied position.
648
Trailing blanks are ignored. The value of |limit| must be strictly less
649
than |buf_size|, so that |cur_buffer[buf_size-1]| is never filled.
651
We assume that none of the |ASCII| values of |*j| for |cur_buffer<=j<limit|
652
is equal to~0, |0177|, |line_feed|, |form_feed|, or |carriage_return|.
653
Since |buf_size| is strictly less than |long_buf_size|,
654
some of \.{WEB}'s routines use the fact that it is safe to refer to
655
|*(limit+2)| without overstepping the bounds of the array.
658
@m fp cur0_prms->File /* The current file pointer. (It's~\.{@@m} instead
659
of~\.{@@d} because of a header conflict on the C370 system.) */
663
IN_COMMON BUF_SIZE buf_size; // Holds input line.
665
IN_COMMON ASCII HUGE *loc;
666
// Points to the next character to be read from the buffer.
668
typedef enum {START,END} DELIM_TYPE;
669
typedef enum {NO_CMNT,SHORT_CMNT,LONG_CMNT} CMNT_TYPE;
675
eight_bits len; // Length of the delimiter; either~$0$, $1$, or~$2$.
676
ASCII HUGE *pos; // Start of the delimiter.
679
#define CMNT_MARKS 50
681
IN_COMMON CMNT_DATA posns[CMNT_MARKS], HUGE *pcmnt CSET(posns),
682
HUGE *pcmnt_end CSET(posns+CMNT_MARKS);
684
@*1 Reading an input line.
685
In the unlikely event that your standard I/O library does not support
686
|feof|, |getc| and |ungetc| you may have to change things here.
687
@^system dependencies@>
689
The |input_ln| function copies a line into |cur_buffer| or returns |NO| if it's
690
gotten to the end-of-file.
696
INPUT_PRMS0 HUGE *p0 C1("Which structure describes the current file?")@;
698
cur0_prms = p0; // Address of current parameters.
699
limit = cur_buffer; /* Initialize the top to the bottom. |limit| points to
700
first unfilled position. */
702
cur_line++; /* A separate line count is kept for each file (including the
705
/* |column_mode| and |parsing_mode| may be redundant. But don't mess with
707
if(!nuweb_mode && FORTRAN_LIKE(language) && column_mode && parsing_mode==OUTER)
721
if(limit==cur_buffer)
726
CONFUSION("input_ln",
727
"Invalid FORTRAN_LIKE language %i", language);
733
/* Take care of an empty line: kill the semicolon. (This is a bit shaky.) */
734
if(column_mode && FORTRAN_LIKE(language) &&
735
PTR_DIFF(size_t,limit,cur_buffer)==2 &&
736
((!auto_semi && (cur_buffer[1]==@';' || cur_buffer[1]==@' ')) ||
737
(auto_semi && cur_buffer[1]==@';' && (cur_buffer[0]=='C' ||
738
cur_buffer[0]==@'c' || cur_buffer[0]==@'*'))))
745
@#endif /* For future use. */
748
@<Echo the line to be returned from |input_ln|@>;
751
return YES; /* Successful read */
754
@ For debugging purposes, echo the line which will be returned. For the
755
benefit of the user, replace the |begin_comment0| by~'\.{!}' and
756
|begin_comment1| by~'\.?'. Turn on this echo with the command-line
757
option~'\.{-l}'. A positive numeric argument means only start debugging at
758
that line number; a negative one means also print the address of the
763
IN_COMMON long start_line CSET(1);
764
// Must be |long|; a negative value is used as a flag.
765
IN_COMMON long end_line CSET(LONG_MAX);
768
@<Echo the line...@>=
770
register ASCII HUGE *k;
772
if(prn_input_lines && cur_line >= (LINE_NUMBER)start_line
773
&& cur_line < (LINE_NUMBER)end_line)
775
if(prn_input_addresses)
776
printf("%lu->%lu (%u) ",
777
(unsigned long)cur_buffer,(unsigned long)limit,
778
PTR_DIFF(unsigned, limit, cur_buffer));
780
printf("%c[%d%s:%u]: \"",
781
(language == global_language ? ' ' : *LANGUAGE_CODE(language)),
783
(changing ? "*" :""),
786
for(k=cur_buffer; k<limit; ++k)
789
case (ASCII)begin_comment0:
793
case (ASCII)begin_comment1:
811
The simplest, most straightforward thing to do is read
812
completely free-form syntax. This is done for~C and for \RATFOR-77. Here is
813
one of the clearest examples of when to prefix single-quoted characters
821
register int c='\0'; // The character read.
822
register ASCII HUGE *k; // Where next character goes.
824
@<Read free-form syntax@>@;
832
if(num_in_buffer != 0)
833
@<Flush \Fortran\ buffer.@>@;
837
return NO; // We have hit end-of-file.
842
@#endif /* For speeding up input; not debugged. */
846
@ This is the original byte-oriented input routine. It is ANSI-compatible.
847
However, on some systems such as VAX/VMS, it may be slow.
851
FATAL(C, "Input line too long; ",
852
"please use the `-ybs...' option to increase buffer size from \
853
its present value of %lu characters.",
859
k = cur_buffer; /* beginning of buffer */
862
if(column_mode && language==TEX)
863
{ // Make each line a short comment.
864
*k++ = @'/'; @~ *k++ = @'/';
868
while (k<=buffer_end && (c=getc(fp)) != EOF && c!='\n')
869
if ((*(k++) = XORD(c)) != @' ') limit = k; /* Update first unfilled
870
position; |k|~is now pointing to next position to fill, and the last one
874
if ((c=getc(fp))!=EOF && c!='\n')
876
ungetc(c,fp); loc=cur_buffer; TOO_LONG;
877
@.Input line too long@>
880
if (c==EOF && limit==cur_buffer)
881
return NO; // There was nothing after the last newline.
884
@ This is an experimental block to speed up the input. It is not complete.
885
For VAX/VMS, it works only for files with variable length record formats,
886
not with stream files.
892
for(k = cur_buffer; k < buffer_end &&
893
(n = fread(k,buffer_end-k,fp))!=0 &&
894
k[n-1]!='\n'; k += n);
898
if(k==buffer_end && *(k-1) != '\n')
905
TERMINATE(cur_buffer,n);
906
to_ASCII(cur_buffer);
910
while(*(k-1)==@'\n' && k > cur_buffer) k--;
911
while(*(k-1)==@' ' && k > cur_buffer) k--;
915
if(n==0 && limit==cur_buffer) return NO;
919
It's probably hopeless to explain the present version
920
of the \FORTRAN\ input driver. It should be abandoned and rewritten from
921
scratch. The best that can be said about \FORTRAN\ with \FWEB\ is that one
922
should use \RATFOR\ instead.
924
The problem with \FORTRAN's fixed column format is that we must read ahead to
925
get any continuation lines which may be waiting. By the time we've figured
926
that out, we've read too far. We leave that stuff waiting, signified by
927
|num_in_buffer != 0|. On the next time into |input_ln|, the waiting stuff
928
will first be moved to the beginning of the buffer; then we go on and read
931
In the following, |limit|~is the next available space in the buffer, |k|~is
932
the present position; |k0|~marks the beginning of the last line read. Each
933
line is actually 5~bytes longer than the actual number of characters read:
934
2~bytes for a possible |"*/"|, 3~for use by the scanning routines of
935
\TANGLE, which may put things there during its scans.
945
register int c='\0'; // The character read.
946
register ASCII HUGE *k; // Where next character goes.
947
boolean first_line = YES;
949
/* We're starting afresh; initialize flags. */
950
last_was_continued = NO;
951
scanning_C_cmnt = NO;
955
/* Keep reading lines until we sense a non-comment, non-continuation line.
956
Leave the latter waiting in the buffer starting at~|k0|. */
957
for(k= limit; ; k=k0 = (limit+=N_END),first_line=NO,cur_line++)
958
if(num_in_buffer == 0)
959
@<Append to buffer.@>@;
961
@<Fill buffer from previous read.@>@;
963
/* Except for new module, throw away all stuff after \.{@@}~command in
965
if(*cur_buffer==@'@@' && !(limit==cur_buffer+1 || *(cur_buffer+1)==@'*' ||
966
*(cur_buffer+1)==@' '))
967
for(k=cur_buffer+2; k<limit; ++k)
980
@ If |num_in_buffer == 0| on entry to |input_ln|, there's the beginning of
981
a line already waiting. That line starts at~|k0|. Here, we move it to the
982
beginning of the buffer.
984
@<Fill buffer from previous...@>=
986
@<Flush \Fortran\ buffer.@>;
988
/* If what was waiting was a control line, we must shift out of column mode. */
992
found_at = column_mode = last_was_empty = NO;
993
break; /* Break out of |for| loop which reads lines. The finish-read
1003
/* Finally, we give special treatment to empty lines. */
1004
last_was_empty = BOOLEAN(limit==cur_buffer);
1006
} /* Go back to the |for| loop which reads lines. */
1008
@ Here we move stuff waiting in the buffer to the beginning of the buffer,
1009
and reset the end of the buffer.
1011
@<Flush \Fortran\ buffer.@>=
1013
STRNCPY(cur_buffer,k0,num_in_buffer); // Move line to beginning of buffer.
1014
k0 = cur_buffer; // For |scan_for_cmnts|
1015
limit = cur_buffer + num_in_buffer - N_END; // Reposition the end of buffer.
1016
num_in_buffer = 0; // We've now cleaned out the buffer.
1019
@ We get to here when |num_in_buffer == 0|.
1020
@<Append to buffer.@>=
1028
return NO; // We have hit end-of-file.
1030
goto concatenate_cmnts; // Don't lose the last line.
1033
k0 = limit; // Set the start of this line to the end of the old one.
1035
@<Read \Fortran\ line.@>; // |limit| is positioned after the last char.\ read.
1037
/* We will allow two extra characters at the end, in case we need to put
1038
end-of-comment markers there; we also need two bytes for Tangle's workspace. */
1039
MEMSET(limit,@' ',N_END);
1040
num_in_buffer = 0; // There's nothing waiting.
1042
/* If we've read into a |new_module| command in column~1,
1043
prepare to switch out of column mode. */
1044
at_line = BOOLEAN(*k0==@'@@' && *(k0+1) != @'/');
1046
if(at_line && (*(k0+1)==@'*' || *(k0+1)==@' '))
1049
/* If we were appending, then we must delay shifting out of column mode until
1050
we process the first part of the buffer. However, if we're actually at the
1051
beginning of the buffer, then we must shift out of column mode immediately. */
1052
if(found_at && k0==cur_buffer)
1053
goto out_of_column_mode;
1055
if(first_line && at_line)
1058
/* Now we look at the line we just put into the buffer. Usually, we should
1059
look at that to see whether it's a continuation or a comment line, because if
1060
so we keep on reading. However, if it's a command line, or if we're in the
1061
middle of handling a trailing C~comment which didn't end on the previous
1062
line, then this line should not be treated in column mode. */
1069
if(!(at_line || scanning_C_cmnt))
1070
@<Check for comment or continuation.@>@;
1072
/* If we get to here, it's neither a comment nor a continuation. If this line
1073
was the first in the buffer, we must continue to read more. (However,
1074
if this line is null, then we do not read any more, because we don't want to
1075
concatenate a null comment to the previous line.) Otherwise, we're
1076
done reading and we should leave this line waiting in the buffer. */
1077
neither_cmnt_nor_continuation:
1079
if(limit==cur_buffer) /* Quit on null first line. */
1081
comment_in_buffer = NO;
1088
continue; /* We must read some more to see if next is
1092
@<Finish reading lines.@>;
1095
@ This is the end of this read. Replace the newline by a semicolon to
1096
delimit the statements.
1103
num_in_buffer = PTR_DIFF(size_t,limit,k0) + N_END;
1104
limit = MAX(k0 - N_WORK,cur_buffer);
1106
else limit += N_WORK;
1109
anlz_cmnts(); // Concatenate comments appropriately, and set |scanning_C_cmnt|.
1112
break; /* Break out of |for| loop. */
1115
@ Read characters for one line into buffer, translating them as we go.
1116
Trailing blanks will be ignored. Tabs will be translated into 6~spaces.
1119
(c=='!' && (point_comments || (c1=getc(fp),ungetc(c1,fp),c1=='!' )))
1121
@<Read \Fortran\ line.@>=
1124
@<Ignore single-line comments@>@;
1126
while(k<=buffer_end && (c=getc(fp)) != EOF && c!='\n')
1127
if(c=='\t' && k<=buffer_end-6)
1130
// Translate tab character. (Don't update |limit|.)
1133
boolean escaped_char = NO;
1134
outer_char c1; // For looking ahead for \.{!!}.
1137
c = XCHR(interior_semi);
1138
else if(point_comments && (*k0 != @'@@') &&
1139
(escaped_char = BOOLEAN(c == '\\')))
1141
c = getc(fp); // What's coming up next?
1143
/* If it's not an escaped point, just copy the escape sequence. */
1151
/* A point that's not escaped begins a short comment. */
1152
if( (*(k++) = CHOICE(IS_POINT(c) && !escaped_char,
1153
(ASCII)begin_comment1, XORD(c))) != @' ')
1154
limit = k; // Ignore trailing blanks.
1158
if ((c=getc(fp))!=EOF && c!='\n')
1160
ungetc(c,fp); loc=cur_buffer; TOO_LONG;
1161
@.Input line too long@>
1164
if (c==EOF && limit==cur_buffer) return NO; /* there was nothing after
1168
@ Here we examine the line most recently read to see if it's a comment or
1169
continuation. If that's true, then we continue to the bottom of the
1170
enclosing |for| loop which reads lines.
1172
@<Check for comment or cont...@>=
1174
boolean is_cmnt = NO, Fortran_cmnt = NO, C_cmnt = NO;
1176
ASCII HUGE *p = k0; // Points to the comment character.
1179
/* Did we get a comment line? */
1185
case (ASCII)begin_comment0:
1186
case (ASCII)begin_comment1:
1187
is_cmnt = Fortran_cmnt = YES;
1191
/* Skip over white space at beginning of line. */
1196
if( (*p==@'/' && (*(p+1)==@'*'
1197
|| (*(p+1)==@'/' && Cpp_comments && !ignore_C)))
1198
|| *p == (ASCII)begin_comment0
1199
|| *p == (ASCII)begin_comment1)
1200
is_cmnt = C_cmnt = YES;
1205
@<Process comment line.@>@;
1207
/* If we're this far, it's not a comment. Check for continuation line
1208
(neither blank nor |'0'| in column~6). If
1209
so, shift it over to abut it with previous stuff in buffer. */
1210
if(limit-k0 >=5 && !(k0[5] == @' ' || k0[5]==@'0') )
1211
@<Process possible continuation line.@>@;
1215
@ If the |ignore_C| flag is on (\.{-nC} option), we simple skip a
1216
single-line \Fortran\ comment.
1218
@<Ignore single-line comment...@>=
1221
c = getc(fp); // First character in line.
1235
while((c=getc(fp)) != EOF && c != '\n')
1246
@ The line is a comment line. We give special treatment to an otherwise null
1247
comment line, treating it as a line with nothing in it at all. If the last
1248
line was a comment also, we concatenate this one with the previous one.
1250
@<Process comment line.@>=
1253
{ /* Null comment line. */
1259
goto concatenate_cmnts;
1263
MEMSET(k0-N_END,@' ',N_END);
1266
*p = begin_comment1;
1268
/* If the last line was empty, we don't want to read any further; leave this
1269
comment waiting in the buffer. */
1272
num_in_buffer = PTR_DIFF(size_t,limit,k0) + N_END;
1273
comment_in_buffer = YES;
1275
break; // Break out of the |for| loop which reads lines.
1279
continue; // Go back and read more lines.
1283
@ The line is a possible continuation line; namely, it has something in
1284
column~6. However, if we're in the midst of a trailing C~comment or if an
1285
alphanumeric label extends into column~6, then it's not a continuation. We
1286
check for these possibilities by seeing whether there's anything in columns
1289
@<Process possible continuation...@>=
1291
register ASCII HUGE *l;
1292
boolean stuff_in_1_5;
1296
{/* Error: Continuation line not expected. */
1301
/* We don't allow anything in columns 1--5 for a continuation line. That's
1302
a FORTRAN rule, and it helps us to catch a possible continuation of a
1306
for(l=k0;l<k0+5; ++l)
1313
/* If there's stuff in columns 1--5, it's not a continuation line.
1314
Otherwise, it's a continuation line; abut it with the code in the previous
1316
if(stuff_in_1_5) goto neither_cmnt_nor_continuation;
1318
*l = @' '; // Delete the continuation symbol.
1320
continue; /* Keep on reading. */
1323
@ The following function records the status and position of a comment
1329
mark_cmnt FCN((type0,delim0,length0,pos0))
1330
CMNT_TYPE type0 C0("")@;
1331
DELIM_TYPE delim0 C0("")@;
1332
eight_bits length0 C0("")@;
1333
ASCII HUGE *pos0 C1("")@;
1335
pcmnt->type = type0;
1336
pcmnt->delim = delim0;
1337
pcmnt->len = length0;
1340
if(type0 == LONG_CMNT)
1341
scanning_C_cmnt = BOOLEAN(delim0 == START);
1345
if(pcmnt == pcmnt_end)
1346
FATAL(C, "Cmnt-buffer overflow.",
1347
" Please simplify long Fortran comment.");
1349
pcmnt->type = NO_CMNT;
1350
pcmnt->delim = START;
1358
IN_COMMON ASCII string_char;
1359
IN_COMMON boolean in_char_string;
1363
@d MARK(type,delim,len) mark_cmnt(type,delim,len,p)
1368
scan_for_cmnts(VOID)
1370
register ASCII HUGE *p;
1372
for(p=k0; p<limit; p++)
1375
case (ASCII)begin_comment0:
1379
MARK(LONG_CMNT,START,1);
1382
case (ASCII)begin_comment1:
1386
MARK(SHORT_CMNT,START,1);
1388
MARK(SHORT_CMNT,END,0);
1393
if(!scanning_C_cmnt)
1394
@<Check for \Fortran\ string@>@;
1398
if(in_char_string || scanning_C_cmnt)
1401
if(*(p+1) == @'/' && Cpp_comments && !ignore_C)
1403
MARK(SHORT_CMNT,START,2);
1405
MARK(SHORT_CMNT,END,0);
1407
else if(*(p+1) == @'*')
1409
MARK(LONG_CMNT,START,2);
1419
if(scanning_C_cmnt && *(p+1) == @'/')
1421
MARK(LONG_CMNT,END,2);
1430
@<Check for \F...@>=
1434
in_char_string = YES;
1439
if(string_char == *p && p[1] == string_char)
1445
in_char_string = NO;
1450
@d BLANK_OUT(d) MEMSET((d)->pos,@' ',(d)->len)
1458
boolean found_text = NO;
1463
for(d=posns + 1; d<pcmnt; d+=2)
1467
for(l=d->pos+d->len; l<(d+1)->pos; l++)
1476
if(d->type == SHORT_CMNT)
1479
*((d-1)->pos) = begin_comment0;
1481
*(d->pos + 1) = @'/';
1484
else if(d->type == (d+1)->type)
1488
d->type = (d+1)->type = NO_CMNT;
1492
@<Insert an automatic semicolon@>@;
1494
pcmnt = posns; // Reset.
1498
@<Scan backwards over comments@>=
1500
if(!bscn_white() || !bscn_cmnt())
1506
IN_COMMON ASCII HUGE *pk;
1511
boolean bscn_white(VOID)
1513
for( ; pk>=cur_buffer; --pk)
1514
if(*pk != @' ') return YES;
1519
boolean bscn_cmnt(VOID)
1521
/* Are we at end of comment? */
1522
if( !(*pk==@'/' && *(pk-1) == @'*') )
1524
++pk; /* Copy the continuation line to here. */
1525
return NO; /* Not on white space and
1526
not on end of comment; we're done. */
1529
/* Scan backwards to beginning of comment. Must allow for both kinds
1530
of comment styles. */
1531
for(pk -=N_END; pk >= cur_buffer; --pk)
1532
if(*pk==(ASCII)begin_comment0 ||
1533
*pk==(ASCII)begin_comment1 ||
1534
(*pk==@'/' && (*(pk+1) == @'*' || *(pk+1)==@'/')))
1535
/* Found start of comment. */
1544
@ In Fortran mode, the last thing we do before returning the line is to
1545
parse looking for comments. We want to propagate the final semicolon to
1546
just before the last comment. With this scheme, the innards of \WEAVE\ and
1547
\TANGLE\ are presented with a uniform syntax.
1551
IN_COMMON ASCII HUGE *lp, HUGE *semi_pos;
1552
IN_COMMON boolean found_text,text,C_style_cmnt,short_cmnt,R_style_cmnt;
1555
@<Insert an auto...@>=
1557
boolean text_after = NO;
1560
{ // There's at least one comment.
1561
if(!scanning_C_cmnt)
1562
for(l=limit-1; l>(pcmnt-1)->pos + (pcmnt-1)->len; l--)
1564
{ // There's text after the comment.
1576
ERR_PRINT(C, "Fortran comment didn't end; missing */?");
1580
while(pcmnt->type == NO_CMNT)
1585
CONFUSION("Insert an automatic semicolon",
1586
"Invalid comment structure");
1589
while(pcmnt >= posns)
1596
lmin = (pcmnt-1)->pos + (pcmnt-1)->len;
1598
for(l=pcmnt->pos - 1; l>=lmin; l--)
1601
{ // There's text before the comment.
1602
semi_pos = pcmnt->pos;
1604
goto done_auto_insert;
1613
{ // No comments at all.
1614
for(l=limit-1; l>=cur_buffer; l--)
1626
@ Here we put in the semicolon. We have to watch out for a preceding
1627
`\.{@@}' sign, which signifies a verbatim comment.
1630
*(semi_pos+1) = (ASCII)(((pcmnt+1)->type==LONG_CMNT ? begin_comment0 :
1631
begin_comment1)); /* If the comment was started by
1632
|begin_comment0| or |begin_comment1|, this overwrites the character
1634
have to make room for the semicolon.) If we want to cure this, we
1635
must do more copying. */
1637
if(semi_pos > cur_buffer && *(semi_pos-1) == @'@@') /* A verbatim comment. */
1639
*(semi_pos--) = @'@@';
1642
*semi_pos = @';'; /* This overwrites the beginning of the comment marker,
1643
or the previous~\.{@@} if there was one. */
1645
@<Interchange line-break and semi@>;
1648
@ As a kludge, if the Fortran line ends with an \.{@@/}, we interchange it
1650
@<Interchange line-break...@>=
1652
for(lp=semi_pos-1; lp>cur_buffer; lp--)
1654
if(*lp == @' ') continue;
1656
if(*lp == @'/' && *(--lp) == @'@@')
1659
*lp = @';'; @~ *(lp+1) = @'@@'; @~ *(lp+2) = @'/';
1672
register int c='\0'; // The character read.
1673
register ASCII HUGE *k; // Where next character goes.
1677
in_char_string = NO;
1681
/* If we've read into a |new_module| command in column~1,
1682
switch out of column mode. */
1683
if(*cur_buffer == @'@@' && (limit==cur_buffer+1
1684
|| *(cur_buffer+1)==@'*' || *(cur_buffer+1)==@' '))
1687
@<Finish Ratfor line.@>;
1695
IN_COMMON ASCII HUGE *last_pos;
1696
IN_COMMON ASCII cmnt_char;
1698
@ This section has to handle the completely free-form syntax as well as the
1699
``obviously continued'' syntax. The latter is more trouble than it's worth,
1700
and isn't recommended.
1702
@<Finish Ratfor line.@>=
1704
last_was_continued = continuing_line;
1705
continuing_line = NO;
1707
if(limit==cur_buffer)
1710
limit[0] = limit[1] = limit[2] = limit[3] = @' ';
1714
found_semi = found_text = text = NO; /* Start off assuming we're in comment
1716
semi_pos = NULL; /* |semi_pos| will record the position of a prospective
1717
comment, to be begun by a semicolon. */
1719
/* Scan the line. */
1720
if(!free_form_input && !(*cur_buffer==@'@@' && *(cur_buffer+1)==@'#'))
1722
for(lp=cur_buffer; ;)
1723
if(!skip_Rtext() || !skp_cmnt()) break;
1727
case @'+': case @'-': case @'*': case @'=':case @'{':case @'}':
1728
case @'^': case @'&': case @'|': case @'(': case @':':
1729
case @'>': case @'<': case @'[': case @',': case @'\\':
1730
continuing_line = YES;
1731
break; /* Line is continued. */
1733
/* Ratfor's continuation character. */
1735
if(last_pos > cur_buffer)
1740
if(isAlpha(c) || isDigit(c) ||
1742
goto not_continuation;
1745
continuing_line = YES;
1753
/* If the last construction is a comment (|!text|) and there was actually
1754
some text (|semi_pos!=NULL|),
1755
replace ``\.{\slashstar}'' by |';'| and |begin_comment0| or
1756
|begin_comment1|. */
1757
if(!text && semi_pos!=NULL) @<Fill in semi@>@;
1758
else if(found_text && limit > cur_buffer) *limit++ = @';';
1765
@ This nucleus skips over text in a line, stopping if it finds a comment.
1767
@d is_C_style_cmnt (C_style_cmnt = ((short_cmnt = Cpp_comments && *lp ==
1768
@'/' && *(lp+1) == @'/')) || (*lp == @'/' && *(lp+1) == @'*') )
1770
@d start_comment (is_C_style_cmnt || (*lp == (char)begin_comment0)
1771
|| (*lp == (char)begin_comment1) )
1775
boolean skip_Ftext(VOID)
1777
@<Begin skipping text@>;
1779
/* If we haven't found a comment, set mode to text. */
1782
found_text = text = YES;
1786
/* Scan to a comment. */
1787
for(; lp<limit; ++lp)
1789
if(++lp < limit) return YES;
1794
@ This nucleus skips over text in a line, stopping if it finds a comment.
1796
@d start_Rcomment ( (R_style_cmnt=BOOLEAN(*lp==cmnt_char &&
1797
*(lp+1)!=cmnt_char && *(lp-1)!=cmnt_char &&
1798
!(*(lp-1)==@'@@'))) || is_C_style_cmnt)
1801
boolean skip_Rtext(VOID)
1803
static ASCII blank = @' ';
1805
@<Begin skipping text@>;
1809
/* If we haven't found a comment, set mode to text. */
1812
found_text = text = YES;
1815
/* Scan to a comment. */
1816
for(; lp<limit; lp++)
1822
*lp = cmnt_char = begin_comment0;
1823
*limit++ = @'*'; @~ *limit++ = @'/';
1825
if(++lp < limit) return YES;
1828
if(*lp != @' ' && *lp != tab_mark)
1829
last_pos = lp; /* Remember last non-blank position. */
1836
@<Begin skipping ...@>=
1838
if(scanning_C_cmnt) return YES; /* Make sure |skp_cmnt| is the first to be
1841
/* Start scanning at the current position. Skip over white space. */
1842
for(; lp<limit; ++lp)
1843
if(!(*lp==@' ' || *lp==tab_mark)) break;
1845
if(lp >= limit) return NO; /* The line was all white space. */
1847
@ This nucleus skips over comments. */
1849
boolean skp_cmnt(VOID)
1851
/* If the last mode was text, remember the position for a possible semi. */
1852
if(text) semi_pos = lp - 1;
1854
text = NO; /* We're in comment mode. */
1855
scanning_C_cmnt = NO; /* Flag used to handle the next line properly if the
1856
comment runs over. */
1858
/* Scan to end of comment. */
1867
for(++lp; lp<limit; lp++)
1869
if(*lp==(ASCII)begin_comment0 || *lp==(ASCII)begin_comment1)
1875
if(*lp==@'*' && *(lp+1)==@'/')
1877
lp += 2; /* Position after end of comment. */
1878
if(lp<=limit) return YES;
1882
scanning_C_cmnt = YES;
1886
for(++lp; lp<limit; lp++)
1887
if(*lp==(ASCII)begin_comment1)
1890
*(limit-2) = @'*'; @~ *(limit-1) = @'/';
1897
Now comes the problem of deciding which file to read from
1898
next. Recall that the actual text that \.{WEB} should process comes from
1899
two streams: a |web_file|, which can contain possibly nested include
1900
commands~`\.{@@i}', and a |change_file|, which should not contain includes.
1901
The currently open files form a stack |prms|. Each entry has two
1902
components: |web| and |change|. Thus, for any open web or include file,
1903
there can be a separate change file is desired. If no change file is
1904
specified at any include level, the change file from the last level is used
1905
instead. Thus, one could have just one change file that changes entries in
1906
both the |web_file| and all of the files included into the |web_file|. The
1907
boolean |changing| tells whether or not we're reading form the
1910
The line number of each open file is also kept for error reporting and
1911
for the benefit of \.{TANGLE}, as are various other parameters.
1913
|cur0_prms| points to the parameter component, either |web| or |change|,
1914
being or just processed by |input_ln|.
1916
|cur_prms| points to the current stack level, including both |web| and
1917
|change| components.
1920
@d found_at cur0_prms->Found_at
1921
@d at_line cur0_prms->At_line
1923
@ When |changing=NO|, the next line of |change_file| is kept in
1924
|change_buffer|, for purposes of comparison with the next
1925
line of |cur_file|. After the change file has been completely input, we
1926
set |change_limit=change_buffer|, so that no further matches will be made.
1928
Here's a shorthand expression for inequality between the two lines:
1930
@d LINES_DONT_MATCH (change_limit-change_buffer != limit-cur_buffer ||
1931
STRNCMP(cur_buffer, change_buffer, PTR_DIFF(size_t,limit,cur_buffer)))
1933
@d change_params prms[incl_depth].input_params
1935
@ Procedure |prime_the_change_buffer| sets |change_buffer| in preparation
1936
for the next matching operation. Since blank lines in the change file are
1937
not used for matching, we have |(change_limit==NULL && !changing)| if and
1938
only if the change file is exhausted. This procedure is called only
1939
when |changing| is |YES|; hence error messages will be reported correctly.
1944
prime_the_change_buffer(VOID)
1946
INPUT_PARAMS input_params; // Saves incoming state.
1948
input_params.Language = language;
1949
input_params.Parsing_mode = parsing_mode;
1950
input_params.Column_mode = column_mode;
1952
language = change_params.Language;
1953
parsing_mode = change_params.Parsing_mode;
1954
column_mode = change_params.Column_mode;
1956
change_limit = NULL; // This value will be used if the change file ends.
1958
@<Skip over comment lines in the change file; |goto done_priming@;| if end
1960
@<Skip to the next nonblank line; |goto done_priming@;| if end of file@>;
1963
language = input_params.Language;
1964
parsing_mode = input_params.Parsing_mode;
1965
column_mode = input_params.Column_mode;
1967
/* After we're done priming the change buffer, we will next read from the
1970
cur0_prms = cur_prms.web;
1973
@ While looking for a line that begins with~\.{@@x} in the change file,
1974
we allow lines that begin with~\.{@@}, as long as they don't begin
1975
with~\.{@@y} or~\.{@@z} (which would probably indicate that the change file is
1978
@<Skip over comment lines in the change file...@>=
1984
if (!input_ln(CHANGE_FILE))
1986
change_limit = NULL;
1990
if (limit < cur_buffer+2) continue;
1991
// There can't be an \.{@@}~command.
1992
if (cur_buffer[0] != @'@@') continue;
1993
// Skip lines that don't start with~\.{@@}.
1994
@<Lowercasify |cur_buffer[1]|@>;
1995
@<Check for erroneous \.{@@i}@>;
1996
if ((c=cur_buffer[1])==@'x') break; // Found beginning of a change.
1998
if (c==@'y' || c==@'z')
2000
loc = cur_buffer+2; // Skip erroneous change.
2001
ERR_PRINT(C,"Where is the matching @@x?");
2002
@.Where is the match...@>
2004
else @<Process language-related change command@>@;
2008
@<Process language-related change...@>=
2011
if(limit == cur_buffer+2)
2012
ERR_PRINT(C,"Missing language character after @@L");
2013
else c = cur_buffer[2];
2017
@<|ASCII| cases for |C|@>:
2018
language = change_params.Language =
2019
(cur_buffer[2] == @'+') ? C_PLUS_PLUS : C;
2020
column_mode = change_params.Column_mode = NO;
2023
@<|ASCII| cases for |RATFOR|@>:
2024
if(!RAT_OK("Language change ignored")) continue;
2026
language = change_params.Language =
2027
(cur_buffer[2] == @'9') ? RATFOR_90 : RATFOR;
2030
@<|ASCII| cases for |FORTRAN|@>:
2031
language = change_params.Language =
2032
(cur_buffer[2] == @'9') ? FORTRAN_90 : FORTRAN;
2035
@<|ASCII| cases for |LITERAL|@>:
2036
language = change_params.Language = LITERAL; continue;
2038
@<|ASCII| cases for |TEX|@>:
2039
language = change_params.Language = TEX; continue;
2042
column_mode = change_params.Column_mode = YES;continue;
2045
column_mode = change_params.Column_mode = NO;continue;
2048
loc = cur_buffer + 2;
2049
ERR_PRINT(C,"Invalid @@ command in change file");
2054
@ This line of code makes~|"@@X"| equivalent to~|"@@x"| and so on.
2058
if ((cur_buffer[1]>=@'X' && cur_buffer[1]<=@'Z') || cur_buffer[1]==@'I')
2059
cur_buffer[1]+=@'z'-@'Z';
2061
@ We do not allow includes in a change file, so as to avoid confusion.
2063
@<Check for erron...@>=
2065
if (cur_buffer[1]==@'i')
2067
loc = cur_buffer + 2;
2068
ERR_PRINT(C,"Sorry, no includes allowed in change file");
2069
@.No includes allowed...@>
2072
@ Here we are looking at lines following the~\.{@@x}.
2074
@<Skip to the next nonblank line...@>=
2078
if (!input_ln(CHANGE_FILE))
2080
ERR_PRINT(C,"Change file ended after @@x");
2081
@.Change file ended...@>
2082
change_limit = NULL;
2086
while (limit==cur_buffer);
2088
@ The following procedure is used to see if the next change entry should go
2089
into effect; it is called only when |changing| is~|NO|. The idea is to
2090
test whether or not the current contents of |cur_buffer| matches the
2091
current contents of |change_buffer|. If not, there's nothing more to do;
2092
but if so, a change is called for: All of the text down to the~\.{@@y} is
2093
supposed to match. An error message is issued if any discrepancy is found.
2094
Then the procedure prepares to read the next line from |change_file|.
2099
chk_change(VOID) // Switches to |change_file| if the buffers match.
2101
int n = 0; // The number of discrepancies found.
2103
if(LINES_DONT_MATCH) return; // Didn't match a change.
2107
changing = YES; prn_where = YES;
2109
if (!input_ln(CHANGE_FILE))
2111
ERR_PRINT(C,"Change file ended before @@y");
2112
@.Change file ended...@>
2113
change_limit = NULL; changing = NO; prn_where = YES;
2117
@<If the current line starts with \.{@@y},
2118
report any discrepancies and |return|@>;@/
2119
changing = NO; prn_where = YES;
2121
while(!input_ln(INPUT_FILE))
2122
{ /* Pop the stack or quit. */
2123
if (incl_depth==WEB_FILE)
2125
ERR_PRINT(C,"WEB file ended during a change");
2126
@.WEB file ended...@>
2127
input_has_ended = YES; return;
2130
incl_depth--; prn_where = YES;
2133
if(LINES_DONT_MATCH) n++;
2138
@<If the current line starts with \.{@@y}...@>=
2140
if (limit>cur_buffer+1 && cur_buffer[0]==@'@@')
2143
@<Check for erron...@>;
2145
if (cur_buffer[1]==@'x' || cur_buffer[1]==@'z')
2148
ERR_PRINT(C,"Where is the matching @@y?");
2149
@.Where is the match...@>
2151
else if (cur_buffer[1]==@'y')
2155
loc = cur_buffer + 2;
2156
ERR_PRINT(C,"Hmm... some of the preceding lines failed to match");
2157
@.Hmm... some of the preceding...@>
2163
@ The |rst_input| procedure, which gets \.{WEAVE} ready to read the
2164
user's \.{WEB} input, is used at the beginning of both phases one and two.
2166
@d ABORT_ON_ERROR YES
2167
@d DONT_ABORT_ON_ERROR NO
2169
@d FCLOSE(file_ptr) if(file_ptr != stdin) fclose(file_ptr); file_ptr = NULL@;
2176
FCLOSE(prms[WEB_FILE].web.File);
2177
FCLOSE(prms[WEB_FILE].change.File);
2179
ini_input_prms(WEB_FILE,(outer_char HUGE *)"",ABORT_ON_ERROR);
2180
@<Reset cur\_buffer@>;
2182
@<Initialize change buffer@>@;
2184
incl_depth = WEB_FILE;
2185
input_has_ended = NO;
2188
@ Prepare to read from an input buffer. It positions the~|loc|
2189
after~|limit| so that a new line will be sure to be read next.
2191
@<Reset cur\_buffer@>=
2193
limit=cur_buffer; loc=cur_buffer+1; cur_buffer[0]=@' '@;
2198
IN_COMMON INPUT_PARAMS change_params0
2199
#if(part == 0 || part == 1)
2200
= {FORTRAN,OUTER,NO}
2205
@<Initialize change...@>=
2207
cur0_prms = cur_prms.change;
2209
change_params = change_params0; // Reset to default values.
2210
prime_the_change_buffer();
2211
@<Reset cur\_buffer@>;
2214
@*1 Initializing input files.
2215
Whenever a new input file is opened, we must
2216
allocate a buffer, and initialize parameters. This routine initializes
2217
both the \WEB\ file and the change file for a particular include level.
2218
@^system dependencies@>
2221
@<Initialize static...@>=
2223
ALLOC(INPUT_PRMS, prms, ABBREV(max_include_depth), max_include_depth, 1);
2226
@ Although the |prms| array had to be allocated before scanning the command
2227
line (with the default |DFLT_MAX_INCLUDE_DEPTH|), the \.{-y}~option might
2228
have requested a change in size. If so, we reallocate the array.
2230
@<Allocate dynamic...@>=
2232
BUF_SIZE cur_prms_units;
2234
alloc(OC("id"), &cur_prms_units, 0, -1); // What is current allocation?
2236
if(cur_prms_units != DFLT_MAX_INCLUDE_DEPTH)
2237
prms = (INPUT_PRMS *)REALLOC(prms,
2238
(cur_prms_units + 1)*sizeof(INPUT_PRMS),
2239
(DFLT_MAX_INCLUDE_DEPTH+1)*sizeof(INPUT_PRMS));
2246
ini_input_prms FCN((ilevel,path,quit_on_error))
2247
int ilevel C0("Present level of input files.")@;
2248
outer_char HUGE *path C0("Colon-delimited path list")@;
2249
boolean quit_on_error C1("Abort flag.")@;
2251
INPUT_PRMS HUGE *p = &prms[ilevel];
2253
boolean web_level = BOOLEAN(ilevel==WEB_FILE);
2255
old_prms = cur_prms; // Save state in case of error.
2257
/* Initialize this here in case of error messages. */
2258
cur_prms.web = &p->web;
2259
cur_prms.change = &p->change;
2261
/* Initialize both input and change files; abort if error. */
2262
if(!(ini0_input_prms(cur_prms.web,path,OC("input"),quit_on_error,
2263
(SEQUENCES *)(web_level ? wt_style.input_ext.web :
2264
wt_style.input_ext.hweb)) &&
2265
ini0_input_prms(cur_prms.change,path,OC("change"),quit_on_error,
2266
(SEQUENCES *)(web_level ? wt_style.input_ext.change :
2267
wt_style.input_ext.hchange))))
2269
cur_prms = old_prms; // Restore old state if error.
2273
/* For definiteness we set |cur0_prms| to the web input file. */
2274
cur0_prms = cur_prms.web;
2277
/* Remember the name of the include file, for use by \WEAVE. */
2278
STRCPY(this_include_file,web_level ?
2279
(CONST outer_char *)"" : p->web.File_name);
2284
@ This function initializes either a web file or a change file.
2288
ini0_input_prms FCN((p0,path,file_type,quit_on_error,extensions))
2289
INPUT_PRMS0 HUGE *p0 C0("")@;
2290
outer_char HUGE *path C0("")@;
2291
outer_char file_type[] C0("")@;
2292
boolean quit_on_error C0("")@;
2293
SEQUENCES *extensions C1("")@;
2295
cur0_prms = p0; /* In case of error messages. */
2297
/* If we've never opened this level before, allocate appropriate space. */
2298
if(p0->Buffer == NULL)
2300
ALLOC(ASCII,p0->Buffer,ABBREV(buf_size),buf_size,0);
2301
p0->Buffer_end = p0->Buffer + buf_size - 2;
2304
/* Initialize location pointers and flags. */
2305
loc = p0->Limit = p0->Buffer;
2307
p0->Num_in_buffer = 0;
2308
p0->Scanning_C_cmnt = p0->Last_was_empty
2309
= p0->Continuing_line = p0->Last_was_continued = NO;
2311
/* --- Open file (but not if it's already open) --- */
2313
(p0->File=xopen(p0->File_name,path,extensions,OC("r"))) == NULL)
2318
"Can't open %s%s file %s.",
2319
*p0->File_name ? "" : "(null) ",
2321
*p0->File_name ? p0->File_name : wt_style.null_file_name);
2322
@.Can't open input file@>
2323
@.Can't open change file@>
2327
err_print(C,"Can't open include %s file \"%s\"",
2328
file_type,p0->File_name);
2329
@.Can't open include file@>
2332
return NO; // Couldn't open file.
2335
return YES; // Opened file successfully.
2338
@ This general routine attempts to open a file by searching through a path.
2342
xopen FCN((file_name,path,extensions,iomode))
2343
outer_char *file_name C0("")@;
2344
outer_char HUGE *path C0("")@;
2345
SEQUENCES *extensions C0("")@;
2346
CONST outer_char *iomode C1("")@;
2348
outer_char HUGE *p1;
2350
outer_char total_name[2*MAX_FILE_NAME_LENGTH];
2351
outer_char prefix_end_str[2];
2354
return FOPEN(wt_style.null_file_name,iomode);
2356
if(*iomode=='r' && STRCMP(file_name,"stdin")==0)
2358
else if(*iomode=='w' && STRCMP(file_name,"stdout")==0)
2361
/* If at this point there's still a path attached to |file_name| (probably
2362
occurs only with the \.{-H} option), we just try to open the file.
2363
If there's no path, we just try to look in the present directory. */
2364
if(file_name[0] == prefix_end_char || !(path && *path))
2365
return x0open(file_name,extensions,iomode);
2367
prefix_end_str[0] = prefix_end_char;
2368
prefix_end_str[1] = '\0';
2370
/* Hunt through the path. */
2371
while((p1=(outer_char HUGE *)STRCHR(path,':')) != NULL)
2374
STRCPY(total_name,path);
2376
if(*(p1-1) != prefix_end_char)
2377
STRCAT(total_name,prefix_end_str);
2378
STRCAT(total_name,file_name);
2379
if((file_ptr=x0open(total_name,extensions,iomode)) != NULL)
2384
return NULL; // Couldn't open any file.
2387
@ Here we attempt to open a file with a specific file name. When the
2388
\.{-e}~option is in effect, we must scan through the list of
2389
possible extensions in order to ascertain whether one of the possible files
2394
x0open FCN((file_name,extensions,iomode))
2395
outer_char *file_name C0("")@;
2396
SEQUENCES *extensions C0("")@;
2397
CONST outer_char *iomode C1("")@;
2401
/* If it has a dot in it, it's always interpreted literally. If it didn't
2402
have a dot, then if the `\.{-e}'~option isn't in effect it will already
2403
have had the `\.{.web}' extension added to it, so it will now have a dot.
2404
(So presumably the last option of the \&{if} is redundant?) */
2405
if(STRCHR(file_name,wt_style.ext_delimiter)!=NULL || !try_extensions)
2406
return FOPEN(file_name,iomode);
2408
/* At this point, the file name didn't have a dot and the `\.{-e}'~option
2409
is in effect. Make trial file names from the list of extensions and try to
2410
open them one at a time until one matches. */
2411
for(k=0; k<extensions->n; k++)
2413
outer_char full_name[MAX_FILE_NAME_LENGTH];
2416
if(NSPRINTF(full_name,"%s%c%s",
2417
file_name,wt_style.ext_delimiter,extensions->string[k]) >=
2418
MAX_FILE_NAME_LENGTH) OVERFLW("Extended file name","");
2420
file_ptr = FOPEN(full_name,iomode);
2424
STRCPY(file_name,full_name); /* If we don't do this, the
2425
information messages aren't as informative as they could be. */
2426
return file_ptr; // Opened a file successfully.
2430
return NULL; // Failed to open the file.
2433
@ The |get_line| procedure is called when |loc>limit|; it
2434
puts the next line of merged input into the buffer and updates the other
2435
variables appropriately. A space is placed at the right end of the line.
2436
This procedure returns |!input_has_ended| because we often want to check
2437
the value of that variable after calling the procedure.
2439
If we've just changed from the |cur_file| to the |change_file|, or if
2440
the |cur_file| has changed, we tell \.{TANGLE} to print this
2441
information in the output file by means of the |prn_where| flag.
2445
IN_COMMON sixteen_bits module_count; // The current module number.
2447
IN_COMMON BUF_SIZE max_modules; // Greater than the total number of modules.
2448
IN_COMMON boolean HUGE *chngd_module; // Dynamic array: Is the module changed?
2450
IN_COMMON boolean prn_where CSET(NO);
2451
// Tells \.{TANGLE} to print line and file info
2452
IN_COMMON boolean return_toggle CSET(NO);
2453
/* Switch used for sending back an `\.{@@i}' when |toggle_includes|
2455
IN_COMMON boolean toggling CSET(NO); // In the middle of a toggle include?
2456
IN_COMMON boolean popped CSET(NO); // After popping the stack?
2457
IN_COMMON boolean no_more_input CSET(NO);
2458
// Temporary replacement for |input_has_ended|.
2462
@<Allocate dynamic...@>=
2464
ALLOC(boolean,chngd_module,ABBREV(max_modules),max_modules,0);
2466
@*1 Getting the next line.
2467
Input the next line.
2475
@<Restore next line and |goto process_line@;|@>@;
2479
chngd_module[module_count] = YES;
2481
@<Read from |cur_file| and maybe turn on |changing|@>;
2485
@<Read from |change_file| and maybe turn off |changing|@>;
2489
chngd_module[module_count]=YES; goto restart;
2494
loc = cur_buffer; *limit = @' ';
2497
@<Send back a toggle command@>@;
2499
if (*cur_buffer==@'@@')
2500
switch(*(cur_buffer+1))
2504
@<Process an \.{\AT!i} command@>@;
2508
return (boolean)(!input_has_ended);
2512
@<Process an \.{\AT!i}...@>=
2514
if( *(cur_buffer+1)==@'I' && (skip_includes ||
2515
(program==weave && toggle_includes && CUR_FILE==WEB_FILE)) )
2518
{ /* Include commands are being skipped altogether. */
2519
loc = limit + 1; // Turn whole line into null.
2522
else if(toggle_includes && CUR_FILE==WEB_FILE)
2523
{ /* Include files are read, but not printed. */
2525
@<Send back a tog...@>@;
2529
@<Push stack and go to |restart|@>@;
2531
@ Process an \.{\AT!o} command.
2534
outer_char new_file_name[MAX_FILE_NAME_LENGTH];
2536
if(program==weave || phase==1) goto restart;
2538
loc = cur_buffer + 2; // Position after \.{@@o}.
2540
/* Look for the name of the new output file. */
2541
if(!get_fname(new_file_name))
2542
ERR_PRINT(C,"Output file name not given");
2543
@.Output file name not given@>
2547
strcpy(output_file_name,new_file_name);
2554
@ An~`\.{@@i}'---|toggle_output|--- is returned before and after an include
2555
line that is to be processed but not output (when |toggle_includes == YES|).
2556
Before we send it back, we must save the line in the buffer, because
2557
we must process it the next time we come back in for a line.
2559
@<Send back a toggle...@>=
2561
return_toggle = YES;
2562
stored_line_length = PTR_DIFF(size_t, limit, cur_buffer);
2563
stored_line = GET_MEM("stored_line", stored_line_length+1, ASCII); /* We
2564
allocate one extra so we don't have to handle the special case of a
2565
zero-length line, for which |FREE_MEM| would complain. */
2566
STRNCPY(stored_line, cur_buffer, stored_line_length);
2567
loc = cur_buffer + 2;
2568
get_fname(prms[1].web.File_name); // For \.{@@I} bug.
2569
loc = limit = cur_buffer;
2570
*limit++ = @'@@'; @~ *limit++ = '\001'; @~ *limit = @' ';
2571
// |toggle_output| is \.{@@\\001}.
2575
@ We need a place to hold that line.
2578
IN_COMMON int stored_line_length;
2579
IN_COMMON ASCII HUGE *stored_line; // Allocated dynamically.
2581
@ When we come back after sending a |toggle_include| command, we must get
2582
back the previous line that was in the buffer.
2584
@<Restore next line...@>=
2588
if(popped && no_more_input)
2590
input_has_ended = YES;
2594
STRNCPY(cur_buffer, stored_line, stored_line_length);
2595
FREE_MEM(stored_line, "stored_line", stored_line_length, ASCII);
2596
limit = cur_buffer + stored_line_length;
2610
@ When an \.{@@i}~line is found in the |cur_file|, we must temporarily
2611
stop reading it and start reading from the named include file. The
2612
\.{@@i}~line should give a complete file name with or without~\.{"..."};
2613
\.{WEB} will not look for include files in standard directories as the
2614
\cee\ preprocessor does when a \.{\#include <filename>} line is found.
2615
Also, the file name should only contain visible ASCII characters,
2616
since the characters are translated into ASCII and back again.
2618
Optionally, one may also name a new change file. As for the command line,
2619
the format is \.{@@i include\_file [change\_file]}.
2621
The next arrays hold the last and current names of the include file, for
2622
use by \WEAVE\ in printing out what file goes with what section.
2626
IN_COMMON outer_char last_include_file[MAX_FILE_NAME_LENGTH] CSET(""),
2627
this_include_file[MAX_FILE_NAME_LENGTH] CSET("");
2630
@<Push stack and...@>=
2632
boolean found_include;
2636
loc = cur_buffer + 2; // Position after \.{@@i}.
2638
if(++incl_depth >= (int)max_include_depth)
2641
err_print(C, "Too many nested includes; %d allowed. \
2642
Increase with `-yid'.", max_include_depth);
2643
@.Too many nested includes@>
2647
/* Look for the name of a \WEB\ file to include. */
2648
if(!(found_include=get_fname(cur_file_name)))
2649
ERR_PRINT(C,"Include file name not given");
2650
@.Include file name not given@>
2652
{ // Found a \WEB\ file name; now look for change file name.
2654
@<Skip an already-included file and |goto restart@;|@>@;
2656
if(!get_fname(change_file_name))
2657
{ /* No change file name specified; obtain it from the
2659
INPUT_PRMS *p_lower = &prms[incl_depth-1];
2660
INPUT_PRMS0 *p0_lower = &p_lower->change;
2662
STRCPY(change_file_name,p0_lower->File_name);
2663
change_file = p0_lower->File;
2664
change_params = p_lower->input_params;
2670
if(ini_input_prms(CUR_FILE,incl.list,DONT_ABORT_ON_ERROR))
2672
if(cur_prms.change->File != prms[incl_depth-1].change.File)
2673
@<Initialize change...@>@; // New change file.
2674
else *cur_prms.change = prms[incl_depth-1].change;
2675
// Still using the old change file.
2679
CLR_PRINTF(SHORT_INFO, include_file,
2680
(" (%s", (char *)cur_file_name));
2681
/* Tell the terminal where we're reading from. */
2684
{ /* Failed to open include file. */
2692
@ When option \.{-j} is used, include files that have already been included
2693
are not included again.
2697
IN_COMMON BUF_SIZE max_ifiles;
2698
IN_COMMON outer_char **ifiles; // Dynamic array of unique include file names.
2699
IN_COMMON int num_ifiles; // Current number of include file names.
2702
@<Allocate dynamic...@>=
2704
ALLOC(outer_char *, ifiles, ABBREV(max_ifiles), max_ifiles, 0);
2708
@<Skip an already...@>=
2712
for(j=0; j<num_ifiles; j++)
2713
if(STRCMP(ifiles[j], cur_file_name) == 0)
2719
if(num_ifiles == (int)max_ifiles)
2720
OVERFLW("unique include file names", ABBREV(max_ifiles));
2724
ifiles[num_ifiles] = GET_MEM("ifile", STRLEN(cur_file_name) + 1, outer_char);
2725
STRCPY(ifiles[num_ifiles], cur_file_name);
2731
@ Read a file name from an \.{@@i}~line. One annoyance is the possibility
2732
of embedded comments.
2736
get_fname FCN((file_name))
2737
outer_char HUGE *file_name C1("")@;
2744
/* Skip over leading white space and optional quotes. */
2745
while (loc<=limit && (*loc==@' '||*loc==@'\t'||*loc==@'"')) loc++;
2747
/* If we've hit the end of the buffer, we're done, and we didn't find
2749
if(loc >= limit) return NO;
2751
@<Process comment in include line@>@;
2753
k = file_name; j=loc;
2755
/* Terminate scan by white space or quotes. */
2756
while (*loc!=@' '&&*loc!=@'\t'&&*loc!=@'"') *k++ = XCHR(*loc++);
2759
/* On certain systems, underscores aren't allowed in file names. */
2761
for (k = file_name; *k; k++)
2766
add_prefix(file_name); // Attach a directory prefix.
2772
@<Process comment in include...@>=
2776
if(*(loc+1) == @'/')
2777
{ /* Short comment */
2782
if(*(loc+1) == @'*')
2783
{ /* Long comment */
2784
for(loc += 2; loc <= limit; loc++)
2785
if(*loc==@'*' && *(loc+1)==@'/')
2791
ERR_PRINT(C,"Can't continue comments on @@i lines");
2796
@ If the include file name has no directory prefix and there was a web file
2797
prefix, then attach the latter to the former.
2802
add_prefix FCN((file_name))
2803
outer_char HUGE *file_name C1("")@;
2805
outer_char temp[MAX_FILE_NAME_LENGTH];
2807
xpnd_file_name(file_name); // Expand possible environmental prefix.
2809
if(*wbprefix && STRRCHR(file_name,prefix_end_char)==NULL)
2811
if(NSPRINTF(temp,"%s%s",wbprefix,file_name) >=
2812
MAX_FILE_NAME_LENGTH) OVERFLW("Path/file_name","");
2814
STRCPY(file_name,temp);
2818
@ The following function emulates VMS' expansions of logical names. It's
2819
assumed that the file names in an \.{@@i} command look like \.{ENV:name},
2820
where \.{ENV}~is an environmental variable defined with \.{setenv}.
2821
\.{ENV}~should expand to a subdirectory, without a trailing slash.
2825
xpnd_file_name FCN((name))
2826
outer_char HUGE *name C1("")@;
2828
outer_char temp[MAX_FILE_NAME_LENGTH],env_prefix[MAX_FILE_NAME_LENGTH];
2829
outer_char *pc,*px,*qx;
2830
CONST char *separator;
2835
return; // VAX/VMS does its own expansions of logical names.
2839
err_print(C,"Sorry, this machine doesn't support environment variables");
2843
/* Is there a logical name prefix? */
2844
if((pc=OC(STRCHR(name,':'))) == NULL) return;
2846
/* Isolate the prefix. */
2847
STRNCPY(env_prefix,name,n=PTR_DIFF(int, pc, name));
2848
TERMINATE(env_prefix,n);
2850
/* Recursively expand the prefix. |px|~will have the last non-null name. */
2852
for(px=env_prefix; px; px=qx)
2854
if((qx=GETENV(px)) == NULL) break;
2855
if(STRCMP(qx,env_prefix) == 0)
2857
err_print(C,"Infinite recursion in definition of \
2858
environmental variable \"%s\"",px=env_prefix);
2862
expanded = YES; /* At least one expansion of the logical name was
2866
/* If the prefix wasn't expandable, or if it turned into an infinite
2867
recursion, do nothing. */
2868
if(!expanded) return;
2870
/* Otherwise, |px|~has the last expansion of the name. */
2876
#endif /* |ibmpc| */
2878
if(NSPRINTF(temp,"%s%s%s",px,separator,pc+1) >=
2879
MAX_FILE_NAME_LENGTH)
2880
OVERFLW("Expandable file name","");
2883
#endif // |HAVE_GETENV|
2888
@<Read from |cur_file|...@>=
2892
if(cur_prms.web->Line == 0)
2900
while (!input_ln(INPUT_FILE))
2901
{ /* Pop the stack or quit. */
2905
if(incl_depth==WEB_FILE)
2906
{input_has_ended = YES; break;}
2908
{ /* Tidy up the include level we're about to leave. */
2909
if(cur_prms.web->File != prms[incl_depth-1].web.File)
2910
{FCLOSE(cur_prms.web->File);}
2912
if(cur_prms.change->File != prms[incl_depth-1].change.File)
2913
{FCLOSE(cur_prms.change->File);}
2914
else prms[incl_depth-1].change = *cur_prms.change;
2916
cur0_prms->File = NULL;
2917
CLR_PRINTF(SHORT_INFO, include_file, (")"));
2918
/* Tell the terminal we're finished with this web
2921
/* Pop the stack here. */
2923
cur_prms.web = &prms[incl_depth].web;
2924
cur_prms.change = &prms[incl_depth].change;
2926
if(incl_depth == WEB_FILE && toggle_includes && toggling)
2929
return_toggle = YES;
2932
STRCPY(this_include_file,(incl_depth==WEB_FILE) ?
2933
(CONST outer_char *)"" : cur_file_name);
2941
no_more_input = input_has_ended;
2942
input_has_ended = NO;
2944
@<Send back a tog...@>@;
2948
if (!input_has_ended)
2949
if (limit==change_limit-change_buffer+cur_buffer)
2950
if (cur_buffer[0]==change_buffer[0])
2951
if (change_limit>change_buffer) chk_change();
2954
@ Here we look at the beginning material in the include file. If the file
2955
begins with~`\.{@@z}', then everything up to and including a line beginning
2956
with~`\.{@@x}' is skipped.
2963
boolean limbo_material;
2965
/* Read the first line of file. */
2966
if(!input_ln(INPUT_FILE))
2969
/* Check for possible limbo material. If so, skip all lines up to
2971
limbo_material = BOOLEAN(cur_buffer[0] == @'@@' && cur_buffer[1] == @'z');
2976
if(!input_ln(INPUT_FILE))
2978
CLR_PRINTF(WARNINGS, warning, ("\n! File ended while \
2979
skiping ignorable commentary. Limbo material that begins with @@z must be \
2980
terminated by @@x."));
2985
find_keywords(cur_buffer, limit); /* The |WEB_FILE|
2986
constraint is a kludge. Eventually, one should integrate this into the
2987
stacking mechanism for include files. */
2989
while(!(cur_buffer[0] == @'@@' && cur_buffer[1] == @'x'));
2991
/* If there was limbo material, at this point the line beginning
2992
with~`\.{@@x}' is in the buffer. The next operation is to read another
2993
line, which starts the proper part of the include file. If there was no
2994
limbo material, the first line of the file is already in the buffer. Thus,
2995
after the following operation we'll always have the first proper line of
2996
the file in the buffer. */
2997
if(limbo_material && !input_ln(INPUT_FILE))
3000
return YES; // Successfully skipped comments.
3003
@ Here we search the line from~|p| to~|p_end| for the pattern
3004
`\.{\$keyword:\ text\ \$}'. If so, the text is associated with the
3005
built-in function \.{\$KEYWORD(keyword)}.
3010
find_keywords FCN((p, p_end))
3011
ASCII HUGE *p C0("Start of line to search")@;
3012
ASCII HUGE *p_end C1("End of line")@;
3014
ASCII HUGE *pk, HUGE *pk_end, HUGE *ptext, HUGE *ptext_end;
3018
pk = (ASCII HUGE *)STRCHR(p, @'$');
3021
return; // No starting \.{'\$'}.
3023
pk_end = (ASCII HUGE *)STRCHR(pk, @':');
3026
return; // No intermediate \.{':'}.
3028
if(pk_end[1] != @' ')
3029
return; // There must be at least one blank after the \.{':'}.
3031
ptext_end = (ASCII HUGE *)STRCHR(pk_end+1, @'$');
3034
return; // No ending \.{'\$'}.
3036
if(ptext_end[-1] != @' ')
3037
return; // There must be at least one blank before the \.{'\$'}.
3039
/* Strip blanks from start of text. */
3040
for(ptext = pk_end+2; *ptext == @' '; ptext++)
3043
/* Strip blanks from end of text. */
3044
for(ptext_end--; *ptext_end == @' '; ptext_end--)
3047
add_keyword(pk+1, pk_end, ptext, ptext_end+1);
3053
IN_COMMON BUF_SIZE max_keywords;
3054
IN_COMMON RCS HUGE *rcs; // Dynamic array of RCS-like keywords.
3055
IN_COMMON unsigned num_keywords; // Current number of RCS-like keywords.
3058
@<Allocate dynamic...@>=
3060
ALLOC(RCS, rcs, ABBREV(max_keywords), max_keywords, 0);
3067
add_keyword FCN((pk, pk_end, pt, pt_end))
3068
ASCII HUGE *pk C0("Start of keyword")@;
3069
ASCII HUGE *pk_end C0("End of keyword")@;
3070
ASCII HUGE *pt C0("Start of text")@;
3071
ASCII HUGE *pt_end C1("End of text")@;
3073
RCS_LIST HUGE *plist = &prms[CUR_FILE].rcs_list;
3077
*pk_end = *pt_end = '\0';
3079
for(prcs=plist->start; prcs && prcs->keyword; prcs=prcs->next)
3080
if(STRCMP(pk, prcs->keyword) == 0)
3082
outer_char temp[256];
3086
printf("\n! Duplicate RCS-like keyword `%s' ignored (l. %u).",
3093
plist->end = plist->start = GET_MEM("RCS", 1, RCS);
3095
plist->end = plist->end->next = GET_MEM("RCS", 1, RCS);
3099
prcs->keyword = GET_MEM("keyword", STRLEN(pk) + 1, ASCII);
3100
STRCPY(prcs->keyword, pk);
3102
prcs->txt = GET_MEM("RCS text", STRLEN(pt) + 1, ASCII);
3103
STRCPY(prcs->txt, pt);
3109
@<Read from |change_file|...@>=
3111
if (!input_ln(CHANGE_FILE))
3113
ERR_PRINT(C,"Change file ended without @@z");
3114
@.Change file ended...@>
3115
cur_buffer[0]=@'@@'; cur_buffer[1]=@'z'; limit=cur_buffer+2;
3118
if (limit>cur_buffer+1) /* check if the change has ended */
3119
if (cur_buffer[0]==@'@@')
3122
@<Check for erron...@>;
3124
if (cur_buffer[1]==@'x' || cur_buffer[1]==@'y')
3126
loc=cur_buffer+2; ERR_PRINT(C,"Where is the matching @@z?");
3127
@.Where is the match...@>
3129
else if (cur_buffer[1]==@'z')
3131
prime_the_change_buffer(); prn_where=YES;
3136
@ At the end of the program, we will tell the user if the change file
3137
had a line that didn't match any relevant line in |web_file|.
3144
if (change_limit != NULL)
3145
{ /* |changing| is |NO| */
3146
STRNCPY(cur_buffer,change_buffer,
3147
PTR_DIFF(size_t,change_limit,change_buffer)+1);
3148
loc = limit = PTR_DIFF(size_t,change_limit,change_buffer) + cur_buffer;
3150
ERR_PRINT(C,"Change file entry did not match");
3151
@.Change file entry did not match@>
3155
@* MEMORY ALLOCATION.
3156
Almost all memory is allocated dynamically. This
3157
allows one to customize the tables if necessary; default sizes can be
3158
overridden by the command-line option~``\.{-y}'', which can also be used in
3159
the ini file. Dynamic allocation also ensures that the pointer to the array
3160
is |HUGE|, and that sometimes matters for personal computers.
3164
@ The memory allocation routine quits if it can't find anything.
3166
@d MAX_SIZE_T ((BUF_SIZE)(size_t)(~(0L))) // Maximum argument to |calloc|.
3170
IN_COMMON BUF_SIZE total_mem CSET(0);
3171
// Current total dynamically allocated memory.
3172
IN_COMMON BUF_SIZE max_mem CSET(0); // The maximum bytes that were allocated.
3178
get_mem0 FCN((why,nunits,nsize))
3179
CONST outer_char why[] C0("Reason for request")@;
3180
BUF_SIZE nunits C0("Number of units to allocate.")@;
3181
size_t nsize C1("Size of each unit.")@;
3183
void HUGE *p; /* With certain compilers such as the |SGI|, |void| is
3184
translated into |char|; see \.{proto.hweb}. */
3188
return NULL; /* Should this ever happen? */
3191
CONFUSION("get_mem0","Nsize = 0 requested");
3193
#if !NON_ANSI_CALLOC
3194
/* For ANSI, the prototype for |calloc| is |void *calloc(size_t,size_t)|. */
3195
if(nunits > MAX_SIZE_T)
3197
err_print(C,"get_mem0: Can't request %lu units; used max of %lu",
3199
nunits = MAX_SIZE_T;
3201
#endif /* |ANSI_CALLOC| */
3203
if( (p=(void HUGE *)CALLOC(nunits,nsize)) == NULL)
3205
FREE(byte_mem); // Kludge to try to make it go out gracefully.
3207
FATAL(C, "\n!!! NO MORE MEMORY",
3209
[Requesting %lu unit(s) of size %lu byte(s); \
3210
allocated %lu bytes so far.] \
3211
Try using the -y option to reduce the sizes of internal tables.",
3212
(char *)why, nunits, (BUF_SIZE)nsize, total_mem);
3215
/* Keep track of total allocation. */
3216
nbytes = nunits*nsize;
3217
total_mem += nbytes;
3218
max_mem = MAX(max_mem,total_mem);
3221
lmemset(p,0,nbytes); /* For |lmalloc|, must zero explicitly. */
3224
if(show_mem && nbytes >= show_size)
3225
show_alloc('+',why,nunits,(BUF_SIZE)nsize,nbytes,p,total_mem);
3227
return p; /* The allocated memory area. */
3230
@ Here we display the details of the memory allocation.
3234
show_alloc FCN((c,why,nunits,nsize,nbytes,p,total_mem))
3235
outer_char c C0("Either plus or minus")@;
3236
CONST outer_char why[] C0("Reason for request")@;
3237
BUF_SIZE nunits C0("Number of units")@;
3238
BUF_SIZE nsize C0("Size of each unit.")@;
3239
BUF_SIZE nbytes C0("Bytes allocated")@;
3240
void HUGE *p C0("Start of allocated area")@;
3241
BUF_SIZE total_mem C1("Total bytes currently allocated")@;
3243
printf("\"%s\": %c(%s x %s) = %c%s ",
3244
(char *)why, c, commas(nunits), commas(nsize), c, commas(nbytes));
3245
ptr_range(p,nbytes);
3246
printf("(%s total)\n", commas(total_mem));
3250
@ Because not all compilers understand the \.{\%p}~format command, and
3251
since different users may wish to customize the form of the pointer range
3252
for an allocation, we provide this function.
3254
If one wanted, for the Sun etc.\ he could use |$TRANSLIT| to turn~\.p
3255
into~\.d, for example.
3257
@d PTR_BASE "[%p,%p) " /* To style file? */
3262
ptr_range FCN((p,nbytes))
3263
void HUGE *p C0("Start of the allocated space.")@;
3264
BUF_SIZE nbytes C1("Length of the allocation")@;
3266
static char ptr_string[] =
3274
printf(ptr_string,p,(char HUGE *)p + nbytes);
3277
@ We free memory through an intermediate routine so we can keep track of
3278
the total memory allocations.
3282
free_mem0 FCN((p,why,nunits,nsize))
3283
void HUGE *p C0("Pointer to deallocate")@;
3284
CONST outer_char why[] C0("Reason for request")@;
3285
BUF_SIZE nunits C0("Number of units to deallocate.")@;
3286
size_t nsize C1("Size of each unit.")@;
3290
/* If things are working correctly, |p|~should have been previously
3293
CONFUSION("free_mem0", "Attempting to deallocate NULL pointer");
3295
/* Keep track of total allocation. */
3296
nbytes = nunits*nsize;
3297
total_mem -= nbytes;
3299
if(show_mem && nbytes >= show_size)
3300
show_alloc('-',why,nunits,(BUF_SIZE)nsize,nbytes,p,total_mem);
3305
@ For debugging, we have some variables that say whether to display the
3306
dynamic memory allocations as they occur. To turn on the display, use the
3307
command-line option~\.{-sm}. A numeric argument changes |show_size|---e.g.,
3308
\.{-sm100} shows all memory allocations whose size is $\ge 100$ bytes.
3310
@d SHOW_MEM 0 /* Don't display by default. */
3314
IN_COMMON boolean show_mem CSET(SHOW_MEM);
3315
IN_COMMON BUF_SIZE show_size CSET(10000); // Display for |nbytes >= show_size|.
3317
@ Here we set up for dynamic memory allocation. The information about the
3318
minimum allowed, maximum allowed, and default value for the length or value
3319
of a variable is held in a |MEM| structure. The default value can be
3320
overridden with the command-line option~\.{-y}.
3322
@m ID_FLAG 10240 /* \bf DO NOT MESS WITH THIS NUMBER!. */
3323
@m ID_FLAG1 $EVAL(ID_FLAG-1)
3327
/* Ideally, the following numbers should be large enough to handle both
3328
\FTANGLE\ and \FWEAVE. In the comments, we show some characteristic
3331
/* \.{Machine-dependent:} Machines with limited memory. */
3333
#define DFLT_BYTES 10000 /* \.{fweave fweave} $\approx$ 23,300. */
3334
#define DFLT_BUF_SIZE 1500
3335
#define DFLT_C_BUF_SIZE 200
3336
#define DFLT_FMT_SIZE 200
3337
#define DFLT_MSG_SIZE 1000
3338
#define DFLT_DELTA_DOTS 20
3339
#define DFLT_MAX_LBLS 20
3340
#define DFLT_LINE_LENGTH 80
3341
#define DFLT_LONGEST_NAME 1000
3342
#define DFLT_MAX_EXPR_CHARS 500
3343
#define DFLT_MAX_IFILES 10
3344
#define DFLT_MAX_INCLUDE_DEPTH 10
3345
#define DFLT_MAX_KEYWORDS 15
3346
#define DFLT_MAX_MARGS 20
3347
#define DFLT_MAX_MODULES 400
3348
#define DFLT_MBUF_SIZE 32767
3349
#define DFLT_MAX_NAMES 1000 /* \.{fweave fweave} $\approx$ 3,300. */
3350
#define DFLT_MAX_REFS 2500 /* \.{fweave ftangle} $\approx$ 5,850. */
3351
#define DFLT_SBUF_LEN 300
3352
#define DFLT_STACK_SIZE_T 50
3353
#define DFLT_STACK_SIZE_W 210 /* \.{fweave fweave} $\approx$ 200. */
3354
#define DFLT_MAX_SCRAPS 1000 /* \.{fweave fweave} $\approx$ 900. */
3355
#define DFLT_MAX_TOKS_T 50000L // \.{ftangle fweave} $\approx$ 92,000.
3356
#define DFLT_MAX_TOKS_W 6000 /* \.{fweave fweave} $\approx$ 6,700. */
3357
#define DFLT_MAX_DTOKS 2500
3358
#define DFLT_MAX_TEXTS 2500 /* \.{fweave fweave} $\approx$ 4,500. */
3359
#define DFLT_MAX_DTEXTS 100
3360
#define DFLT_NUM_FILES 15
3361
#define DFLT_OP_ENTRIES $EVAL(128+PREDEFINED_DOTS+5)
3362
#define DFLT_X_BUF_SIZE 80
3363
#else /* Large limits. */
3364
#define DFLT_BYTES 90000L
3365
#define DFLT_BUF_SIZE 1500
3366
#define DFLT_C_BUF_SIZE 255
3367
#define DFLT_FMT_SIZE 500
3368
#define DFLT_MSG_SIZE 2000
3369
#define DFLT_DELTA_DOTS 20
3370
#define DFLT_MAX_LBLS 20
3371
#define DFLT_LINE_LENGTH 80
3372
#define DFLT_LONGEST_NAME 10000
3373
#define DFLT_MAX_EXPR_CHARS 500
3374
#define DFLT_MAX_IFILES 100
3375
#define DFLT_MAX_INCLUDE_DEPTH 10
3376
#define DFLT_MAX_KEYWORDS 20
3377
#define DFLT_MAX_MARGS 20
3378
#define DFLT_MAX_MODULES 2000
3379
#define DFLT_MBUF_SIZE 50000L
3380
#define DFLT_MAX_NAMES 4000
3381
#define DFLT_MAX_REFS 20000
3382
#define DFLT_SBUF_LEN 300
3383
#define DFLT_STACK_SIZE_T 50
3384
#define DFLT_STACK_SIZE_W 400
3385
#define DFLT_MAX_SCRAPS 10000
3386
#define DFLT_MAX_TOKS_T 150000L
3387
#define DFLT_MAX_TOKS_W 25000
3388
#define DFLT_MAX_DTOKS 25000
3389
#define DFLT_MAX_TEXTS ID_FLAG1
3390
#define DFLT_MAX_DTEXTS 500
3391
#define DFLT_NUM_FILES 15
3392
#define DFLT_OP_ENTRIES $EVAL(128+PREDEFINED_DOTS+15)
3393
#define DFLT_X_BUF_SIZE 80
3394
#endif /* Limited memory. */
3397
@ The following list must be kept sorted by abbreviation!
3401
#if(part == 0 || part == 1)
3403
{ABBREV(max_bytes),1,
3404
1000,DFLT_BYTES,ULONG_MAX}, /* |"b"|. |max_bytes|: the
3405
number of bytes in identifiers, index entries, and module names. */
3406
{ABBREV(buf_size),1,
3407
500,DFLT_BUF_SIZE,20000}, // |"bs"|. |change_buffer| etc.
3408
{ABBREV(C_buf_size),1,
3409
10,DFLT_C_BUF_SIZE,1000},// |"cb"|. |C_buf_size|: see |C_putc|.
3410
{ABBREV(cmd_fsize),1,
3411
100,DFLT_FMT_SIZE,UL(65536L)}, // |"cf"|.|cmd_fsize|: see |out_cmd|.
3412
{ABBREV(cmd_size),1,
3413
100,DFLT_MSG_SIZE,UL(65536L)}, // |"cg"|. |cmd_size|: see |out_cmd|
3414
{ABBREV(delta_dots),sizeof(DOTS),
3415
20,DFLT_DELTA_DOTS,255}, /* |"d"|. |delta_dots|: Number of
3416
additional entries to reallocate for |dots| if necessary. */
3417
{ABBREV(max_dtoks),sizeof(eight_bits),
3418
1000,DFLT_MAX_DTOKS,ULONG_MAX}, // |"dt"|. |max_dtoks|.
3419
{ABBREV(dtexts_max),sizeof(text),
3420
100,DFLT_MAX_DTEXTS,ID_FLAG1}, // |"dx"|. |dtexts_max|.
3421
{ABBREV(max_include_depth), sizeof(INPUT_PRMS),
3422
1, DFLT_MAX_INCLUDE_DEPTH, 100}, // |"id"|. |prms| (open files).
3423
{ABBREV(max_ifiles), sizeof(outer_char *),
3424
1, DFLT_MAX_IFILES, 10000}, // |"if"|. |max_ifiles|.
3425
{ABBREV(stck_size_t),sizeof(output_state),
3426
10,DFLT_STACK_SIZE_T,100}, // |"kt"|. Tangle's |stck_size|.
3427
{ABBREV(stck_size_w),sizeof(output_state),
3428
50,DFLT_STACK_SIZE_W,1000}, // |"kw"|. Weave's |stck_size|.
3429
{ABBREV(max_lbls),sizeof(BEGUN),
3430
5,DFLT_MAX_LBLS,255}, // |"lb"|. |max_lbls|.
3431
{ABBREV(line_length),1,
3432
60,DFLT_LINE_LENGTH,255}, /* |"ll"|. |line_length| for
3433
\FWEAVE's output. */
3434
{ABBREV(longest_name),1,
3435
3*MAX_FILE_NAME_LENGTH, DFLT_LONGEST_NAME, UL(50000L)},
3436
/* |"ln"|. |longest_name|: module names and strings shouldn't be longer
3438
{ABBREV(max_expr_chars),1,
3439
25,DFLT_MAX_EXPR_CHARS,UL(65536L)}, // |"lx"|. |max_expr_chars|.
3440
{ABBREV(max_modules),sizeof(boolean),
3441
100,DFLT_MAX_MODULES,ID_FLAG1}, /* |"m"|. |max_modules|:
3442
larger than the max number of modules. */
3443
{ABBREV(max_margs), sizeof(sixteen_bits),
3444
1, DFLT_MAX_MARGS, 255}, // |"ma"|. |max_margs|.
3445
{ABBREV(mbuf_size),1,
3446
5000,DFLT_MBUF_SIZE,ULONG_MAX}, // |"mb"|. |mbuf_size|.
3447
{ABBREV(max_names),sizeof(NAME_INFO)+sizeof(name_pointer),
3448
500,DFLT_MAX_NAMES,ID_FLAG1}, /* |"n"|. |max_names|:
3449
number of identifiers, strings, module names; must be less than 10240. */
3450
{ABBREV(num_files),sizeof(OPEN_FILE),
3451
1,DFLT_NUM_FILES,256}, // |"nf"|. |num_files|.
3452
{ABBREV(op_entries),sizeof(OPERATOR),
3453
$EVAL(128+PREDEFINED_DOTS),DFLT_OP_ENTRIES,256}, /* |"op"|.
3455
{ABBREV(max_refs),sizeof(xref_info),
3456
500,DFLT_MAX_REFS,UL(65536L)}, /* |"r"|. |max_refs|: number of
3457
cross-references; must be less than 65536. */
3458
{ABBREV(max_keywords), sizeof(RCS),
3459
11, DFLT_MAX_KEYWORDS, UL(65536L)}, // |"rk"|. |max_keywords|.
3460
{ABBREV(max_scraps),sizeof(scrap),
3461
1000,DFLT_MAX_SCRAPS,ULONG_MAX}, // |"s"|. |max_scraps|.
3462
{ABBREV(sbuf_len),sizeof(outer_char),
3463
100,DFLT_SBUF_LEN,ULONG_MAX}, // |"sb"|. |sbuf_len|.
3464
{ABBREV(max_toks_t),sizeof(eight_bits),
3465
10000,DFLT_MAX_TOKS_T,ULONG_MAX}, /* |"tt"|. Tangle's
3466
|max_toks|: Total for the entire code; can be big. */
3467
{ABBREV(max_toks_w),sizeof(sixteen_bits),1000,
3468
DFLT_MAX_TOKS_W,UL(65536L)}, /* |"tw"|. Weave's |max_toks|:
3469
number of symbols in code texts being parsed; must be less than 65536. */
3470
{ABBREV(max_texts),sizeof(text),
3471
500,DFLT_MAX_TEXTS,ID_FLAG1}, // |"x"|. |max_texts|.
3472
{ABBREV(X_buf_size),1,
3473
10,DFLT_X_BUF_SIZE,1000},// |"xb"|. |X_buf_size|: see |C_putc|.
3476
#endif // |part == 1|
3479
IN_COMMON size_t sizeof_mem CSET(sizeof(mem));
3481
@ This is the search routine used by the `\.{-y}'~option that matches an
3482
abbreviation with the right element of the |MEM| structure and overrides
3488
msearch FCN((abbrev,nunits))
3489
CONST outer_char abbrev[] C0("Abbreviation for desired object.")@;
3490
BUF_SIZE nunits C1("Value.")@;
3494
for(m=mem; *m->abbrev; m++)
3495
if(STRCMP(m->abbrev,abbrev)==0)
3497
if(nunits == 0L) return m; // Special case: just find it.
3501
CLR_PRINTF(WARNINGS, warning,
3502
("! Warning (msearch:%s): nunits (%lu) < min (%lu); min used.\n",
3503
(char *)abbrev,nunits,m->min));
3507
else if(nunits > m->max)
3509
CLR_PRINTF(WARNINGS, warning,
3510
("! Warning (dynamic buffer %s): nunits (%lu) > max (%lu); max used.\n",
3511
(char *)abbrev,nunits,m->max));
3516
m->nunits = nunits; /* Override the default value. */
3520
CLR_PRINTF(WARNINGS, warning,
3521
("! Invalid alloc abbreviation \"%s[%lu]\"\n", (char *)abbrev,nunits));
3526
@ Here is the allocation routine that works with the |ALLOC| macro. It
3527
returns a pointer to allocated memory, and also sets an|unsigned long|
3528
variable with the number of units (not bytes) allocated.
3533
alloc FCN((abbrev,pnunits,nsize,dn))
3534
CONST outer_char abbrev[] C0("Abbreviation for desired object.")@;
3535
BUF_SIZE HUGE *pnunits C0("Pointer to returned \# of units.")@;
3536
size_t nsize C0("Size of object.")@;
3537
int dn C1("Incremental number of units to allocate.")@;
3539
MEM HUGE *m = find_abbrev(abbrev);
3541
*pnunits = m->nunits;
3543
/* |dn| is used as a flag; if it's negative, we just obtain the number of
3548
return get_mem0(abbrev, (*pnunits)+dn, nsize);
3551
@ Search through the abbreviations list to get the element.
3554
find_abbrev FCN((abbrev))
3555
CONST outer_char abbrev[] C1("")@;
3559
for(m=mem; *m->abbrev; m++)
3560
if(STRCMP(m->abbrev,abbrev)==0)
3563
CONFUSION("find_abbrev", "Bad alloc abbreviation \"%s\"", abbrev);
3568
@ Annoyingly, Microsoft doesn't supply a reallocation routine that works
3569
with |halloc|, presumably because it doesn't do heap management with |huge|
3570
stuff. So, we write our own. We simply allocate new space, copy the old to
3571
the new, then free the old.
3577
hrealloc FCN((old_ptr, new_size, old_size))
3578
void HUGE *old_ptr C0("Old buf previously allocated with |halloc|.")@;
3579
BUF_SIZE new_size C0("New size in bytes.")@;
3580
BUF_SIZE old_size C1("Old size in bytes.")@;
3583
BUF_SIZE num_to_copy;
3586
new_ptr = GET_MEM("hrealloc", new_size, char);
3591
num_to_copy = MIN(old_size, new_size);
3592
ncopied = (size_t)num_to_copy;
3594
if((BUF_SIZE)ncopied != num_to_copy)
3595
CONFUSION("hrealloc",
3596
"trying to copy %lu bytes; more than size_t", num_to_copy);
3598
memcpy(new_ptr, old_ptr, ncopied);
3600
FREE_MEM(old_ptr, "old_ptr", old_size, char);
3607
@* STORAGE of IDENTIFIERS and MODULE NAMES.
3608
Both \.{WEAVE} and \.{TANGLE} store identifiers, module names and
3609
other strings in a large array of |ASCII|s, called |byte_mem|.
3610
Information about the names is kept in the array |name_dir|, whose
3611
elements are structures of type \&{NAME\_INFO}, containing a pointer into
3612
the |byte_mem| array (the address where the name begins) and other data.
3613
A \&{name\_pointer} variable is a pointer into |name_dir|.
3615
@<Allocate dynamic memory@>=
3617
ALLOC(ASCII,byte_mem,ABBREV(max_bytes),max_bytes,0); // Characters of names.
3618
byte_end = byte_mem + max_bytes - 1; // End of |byte_mem|.
3620
ALLOC(NAME_INFO,name_dir,ABBREV(max_names),max_names,0);
3621
// Information about names.
3622
name_end = name_dir + max_names - 1; // End of |name_dir|.
3624
ALLOC(ASCII,mod_text,ABBREV(longest_name),longest_name,1);
3625
mod_end = mod_text+longest_name; // End of |mod_text|.
3629
@ The actual sequence of characters in the name pointed to by a |name_pointer
3630
p| appears in positions |p->byte_start| to |(p+1)->byte_start|, inclusive.
3631
The |prn_id| macro prints this text on the user's terminal.
3637
name_pointer np C1("")@;
3639
ASCII_write(*np->byte_start == BP_MARKER ?
3640
((BP *)np->byte_start)->Root->id : np->byte_start,Length(np));
3642
return PTR_DIFF(unsigned, np, name_dir);
3647
name_pointer np C1("")@;
3652
CONST ASCII HUGE *p0;
3653
CONST ASCII HUGE *p1;
3655
p0 = np->byte_start;
3658
if(*(char *)bp == BP_MARKER)
3661
n = PTR_DIFF(int, s->id_end, s->id);
3665
p1 = (np+1)->byte_start;
3667
n = PTR_DIFF(int, p1, p0);
3673
@ The first unused position in |byte_mem| and |name_dir| is
3674
kept in |byte_ptr| and |name_ptr|, respectively. Thus we
3675
usually have |name_ptr->byte_start=byte_ptr|, and certainly
3676
we want to keep |name_ptr<=name_end| and |byte_ptr<=byte_end|.
3680
IN_COMMON name_pointer name_ptr; // First unused position in |byte_start|.
3681
IN_COMMON ASCII HUGE *byte_ptr; // First unused position in |byte_mem|.
3684
@<Initialize dynamic...@>=
3686
CAST(name_pointer,name_dir)->byte_start=byte_ptr=byte_mem; /* position zero
3688
name_ptr=name_dir+1; /* |name_dir[0]| will not be used */
3689
CAST(name_pointer,name_ptr)->byte_start=byte_mem; /* this makes name 0 of
3692
@ The names of identifiers are found by computing a hash address |h| and
3693
then looking at strings of bytes signified by the |name_pointer|s
3694
|hash[h]|, |hash[h]->link|, |hash[h]->link->link|, \dots,
3695
until either finding the desired name or encountering the null pointer.
3697
The hash table itself consists of |hash_size| entries of type
3698
|name_pointer|, and is updated by the |id_lookup| procedure, which finds a
3699
given identifier and returns the appropriate |name_pointer|. The matching
3700
is done by the function |names_match|, which is slightly different in
3701
\.{WEAVE} and \.{TANGLE}. If there is no match for the identifier, it is
3702
inserted into the table.
3704
@d hash_size 353 /* should be prime */
3706
@ Initially all the hash lists are empty.
3708
@<Initialize static...@>=
3710
hash = GET_MEM("hash",hash_size,name_pointer);
3711
hash_end = hash + hash_size - 1;
3713
for (h=hash; h<=hash_end; *h++=NULL) ;
3715
@ Here is the main procedure for finding identifiers. The type of
3716
identifier being considered is held in the global variable |word_type| (set
3717
in \.{reserved.web}, function |save_id|; this comes into play when |last == NULL|.
3722
id_lookup FCN((first,last,t))
3723
CONST ASCII HUGE *first C0("First character of string.")@;
3724
CONST ASCII HUGE *last C0("last character of string plus one.")@;
3725
eight_bits t C1("The |ilk|; used by \.{WEAVE} only.")@;
3727
CONST ASCII HUGE *i=first; /* position in |cur_buffer| */
3728
int h; /* hash code */
3729
int l; /* length of the given identifier */
3730
name_pointer p; /* where the identifier is being sought */
3731
WORD_TYPE word_type0 = ORDINARY_ID;
3735
word_type0 = word_type;
3737
for (last=first; *last!='\0'; last++)
3738
; // Find end of identifier.
3741
l = PTR_DIFF(int,last,first); /* compute the length */
3742
@<Compute the hash code |h|@>;
3743
@<Compute the name location |p|@>;
3746
@<Enter a new name into the table at position |p|@>;
3748
if(!(p->Language & (boolean)language))
3751
p->Language |= (boolean)language; /* Record language usage. This line is
3752
dangerous if one is using |id_lookup| for a query from a routine for a
3753
language other than the current one; see |ini_special_tokens|. */
3758
p->reserved_word |= (boolean)language;
3762
p->intrinsic_word |= (boolean)language;
3766
p->keyword |= (boolean)language;
3769
default: ; /* Avoids warning about unused |enum|. */
3775
@ The following two routines |find_n| and |find_s| are for rapid debugging
3776
of the |name_dir|. They are to be called from the debugger. |find_n| prints
3777
information about the $n$th~identifier; |find_s| prints information about
3778
the identifier named~|s|.
3784
sixteen_bits n C1("")@;
3787
CONST ASCII HUGE *end;
3795
printf("!! n = %u exceeds nmax = %u.\n", n,
3796
PTR_DIFF(unsigned, name_ptr, name_dir) - 1);
3802
len = PTR_DIFF(size_t, end, np->byte_start); // Length of the $n$th identifier.
3803
s = GET_MEM("find_n",len+1,outer_char); // Allocate temporary area.
3805
STRNCPY(s,np->byte_start,len);
3808
/* Convert from |ASCII| to the outer world; print info about id. */
3809
to_outer((ASCII HUGE *)s);
3810
id_info(np,(long)n,(CONST outer_char *)s, NULL);
3811
FREE_MEM(s,"find_n",len+1,outer_char);
3818
CONST outer_char *s C1("")@;
3823
CONST ASCII HUGE *first, HUGE *last;
3824
CONST ASCII HUGE *i;
3828
a = GET_MEM("find_s",l+1,ASCII);
3830
i = first = to_ASCII((outer_char *)a);
3833
@<Compute the hash...@>@;
3835
while (p && !(STRNCMP(first,p->byte_start,l) == 0))
3839
{CLR_PRINTF(WARNINGS, warning, ("%cId \"%s\" is not in name_dir!\n",
3840
beep(1), (char *)s));}
3842
id_info(p,(long)(n=(sixteen_bits)(p-name_dir)),s, NULL);
3844
FREE_MEM(a,"find_s",l+1,ASCII);
3855
find_id FCN((a0,a1))
3856
eight_bits a0 C0("")@;
3857
eight_bits a1 C1("")@;
3859
return find_n(IDENTIFIER(a0,a1));
3862
@ The information utility used in |find_n| and |find_s|; also in |see_reserved|.
3866
id_info FCN((np,n,s,prsrvd))
3867
name_pointer np C0("")@;
3869
CONST outer_char *s C0("")@;
3870
CONST RSRVD HUGE *prsrvd C1("")@;
3873
printf(_Xx("Id %ld (0x%lx):"),n,n);
3876
|| (prsrvd->reserveds && np->reserved_word)
3877
|| (prsrvd->intrinsics && np->intrinsic_word)
3878
|| (prsrvd->keywords && np->keyword))
3879
printf(" \"%s\"\n", (char *)s);
3882
pr_lan("Language ", np->Language);
3884
if(!prsrvd || prsrvd->reserveds)
3885
pr_lan("reserved ", np->reserved_word);
3887
if(!prsrvd || prsrvd->intrinsics)
3888
pr_lan("intrinsic", np->intrinsic_word);
3890
if(!prsrvd || prsrvd->keywords)
3891
pr_lan("keyword ", np->keyword);
3897
IN_COMMON boolean info_option CSET(NO);
3898
IN_COMMON boolean see_rsrvd CSET(NO);
3899
IN_COMMON RSRVD rsrvd
3900
#if(part == 0 || part == 1)
3901
= {NULL, NO, NO, NO}
3902
#endif // |part == 1|
3904
IN_COMMON outer_char HUGE *style_args CSET(NULL);
3905
IN_STYLE ASCII HUGE *at_codes;
3907
@ Some info options can be executed only after dynamic memory is allocated.
3908
After that, if no web file was specified on the command line we just
3909
terminate gracefully.
3910
@<Execute deferred...@>=
3912
if(rsrvd.args) see_reserved(&rsrvd);
3913
if(style_args) see_style(style_args, NO);
3915
if(!found_web && info_option && !at_codes)
3919
@ Format the language dependence of a boolean variable~|b|, for debugging and
3925
pr_lan FCN((name,b))
3926
CONST char *name C0("")@;
3929
unsigned k; // For scanning over the 8 possibilities.
3930
boolean first = YES;
3933
return; // Not turned on at all.
3935
printf(" %s = { ",name);
3937
for(k=1; k<=$EVAL(2^^8); k<<=1)
3940
printf("%s%s", first ? "" : ", ",
3941
languages[lan_num((LANGUAGE)k)]);
3950
@% printf(_Xx(" (0x%x)"), b);
3955
@ More debugging: Print out the entire |name_dir|.
3961
BUF_SIZE k, n = name_ptr - name_dir;
3965
printf("[%lu]: ", k);
3966
prn_id(name_dir + k);
3973
@ A simple hash code is used: If the sequence of
3974
ASCII codes is $c_1c_2\ldots c_n$, its hash value will be
3975
$$(2^{n-1}c_1+2^{n-2}c_2+\cdots+c_n)\,\bmod\,|hash_size|.$$
3977
@<Compute the hash...@>=
3981
while (++i<last) h=(h+h+*i) % hash_size;
3983
@ If the identifier is new, it will be placed in position |p=name_ptr|,
3984
otherwise |p| will point to its existing location.
3986
@<Compute the name location...@>=
3989
while (p && !names_match(p,first,l,t)) p=p->link;
3993
p=name_ptr; /* the current identifier is new */
3994
p->link=hash[h]; hash[h]=p; /* insert |p| at beginning of hash
3998
@ The information associated with a new identifier must be initialized
3999
in a slightly different way in \.{WEAVE} than in \.{TANGLE}; hence the
4002
@<Enter a new name...@>=
4004
if (byte_ptr+l>byte_end) OVERFLW("bytes",ABBREV(max_bytes));
4005
if (name_ptr>=name_end) OVERFLW("names",ABBREV(max_names));
4007
STRNCPY(byte_ptr,first,l);
4008
(++name_ptr)->byte_start = byte_ptr += l;
4014
@ The names of modules are stored in |byte_mem| together
4015
with the identifier names, but a hash table is not used for them because
4016
\.{TANGLE} needs to be able to recognize a module name when given a prefix of
4017
that name. A conventional binary seach tree is used to retrieve module names,
4018
with fields called |llink| and |rlink| (where |llink| takes the place
4019
of |link|). The root of this tree is stored in |name_dir->rlink|;
4020
this will be the only information in |name_dir[0]|.
4022
Since the space used by |rlink| has a different function for
4023
identifiers than for module names, we declare it as a |union|.
4025
@ The binary search tree starts out with nothing in it.
4027
@<Initialize dynamic...@>=
4031
@ The |mod_lookup| procedure finds a module name in the
4032
search tree, after inserting it if necessary, and returns a pointer to
4035
According to the rules of \.{WEB}, no module name should be a proper
4036
prefix of another, so a ``clean'' comparison should occur between any
4037
two names. The result of |mod_lookup| is |NULL| if this prefix condition
4038
is violated. An error message is printed when such violations are detected.
4039
(Note that the |LESS|, |EQUAL|, and |GREATER| conventions here are
4040
different than those for |strcmp|; they are defined in \.{typedefs.hweb}.)
4045
mod_lookup FCN((k,l))
4046
CONST ASCII HUGE *k C0("First character of name.")@;
4047
CONST ASCII HUGE *l C1("Last character of name.")@;
4049
LEXI c = GREATER; /* comparison between two names */
4050
name_pointer p = root; /* current node of the search tree */
4051
name_pointer q = name_dir; /* father of node |p| */
4055
c = web_strcmp(k,l+1,p->byte_start,(p+1)->byte_start);
4060
case LESS: p=p->llink; continue;
4061
case GREATER: p=p->rlink; continue;
4062
case EQUAL: return p;
4063
default: ERR_PRINT(C,"Incompatible section names");
4065
@.Incompatible section names@>
4069
return install_node(q,c,k,PTR_DIFF(int,l,k)+1);
4072
@ This function is like |strcmp|, but it does not assume the strings
4073
are null-terminated.
4078
web_strcmp FCN((j,j1,k,k1)) /* fuller comparison than |strcmp| */
4079
CONST ASCII HUGE *j C0("Beginning of first string.")@;
4080
CONST ASCII HUGE *j1 C0("End of first string plus one.")@;
4081
CONST ASCII HUGE *k C0("Beginning of second string.")@;
4082
CONST ASCII HUGE *k1 C1("End of second string plus one.")@;
4084
while (k<k1 && j<j1 && *j==*k) k++, j++;
4086
if (k==k1) if (j==j1) return EQUAL;
4087
else return EXTENSION;
4088
else if (j==j1) return PREFIX;
4089
else if (*j<*k) return LESS;
4090
else return GREATER;
4093
@ The reason we initialized |c| to |greater| is so that |name_pointer| will
4094
make |name_dir->rlink| point to the root of the tree when |q=name_dir|,
4095
that is, the first time it is called.
4097
The information associated with a new node must be initialized in a
4098
slightly different way in \.{WEAVE} than in \.{TANGLE}; hence the
4099
|ini_node| procedure.
4104
install_node FCN((parent,c,j,name_len)) /* install a new node in the tree */
4105
name_pointer parent C0("Parent of new node.")@;
4106
int c C0("Right or left?")@;
4107
CONST ASCII HUGE *j C0("Where replacement text starts.")@;
4108
int name_len C1("length of replacement text.")@;
4110
name_pointer node=name_ptr; /* new node */
4111
if (byte_ptr+name_len>byte_end) OVERFLW("bytes",ABBREV(max_bytes));
4112
if (name_ptr==name_end) OVERFLW("names",ABBREV(max_names));
4114
if (c==LESS) parent->llink=node; else parent->rlink=node;
4116
node->llink=node->rlink=NULL;
4118
STRNCPY(byte_ptr,j,name_len);
4119
(++name_ptr)->byte_start=byte_ptr+=name_len;
4123
@ The |prefix_lookup| procedure is supposed to find exactly one module name
4124
that has |k..l| as a prefix. Actually the algorithm silently accepts also
4125
the situation that some module name is a prefix of |k..l|, because the user
4126
who painstakingly typed in more than necessary probably doesn't want to be
4127
told about the wasted effort.
4132
prefix_lookup FCN((k,l)) /* finds module name given a prefix */
4133
CONST ASCII HUGE *k C0("First char of prefix.")@;
4134
CONST ASCII HUGE *l C1("Last char of prefix.")@;
4136
LEXI c = GREATER; /* comparison between two names */
4137
short count = 0; /* the number of hits */
4138
name_pointer p = root; /* current node of the search tree */
4139
name_pointer q = NULL;
4140
/* another place to resume the search after one is done */
4141
name_pointer r = NULL; /* extension found */
4143
c=web_strcmp(k,l+1,p->byte_start,(p+1)->byte_start);
4146
case LESS: p=p->llink; break;
4147
case GREATER: p=p->rlink; break;
4148
default: r=p; count++; q=p->rlink; p=p->llink;
4155
if (count==0) ERR_PRINT(C,"Name does not match");
4156
@.Name does not match@>
4157
else if (count>1) ERR_PRINT(C,"Ambiguous prefix");
4158
@.Ambiguous prefix@>
4160
return r; /* the result will be |NULL| if there was no match */
4163
@ Here's a routine to be called from the debugger; it returns the directory
4164
number and the full name of a prefix.
4168
find_prefix FCN((s))
4169
outer_char s[] C1("")@;
4174
r = prefix_lookup((ASCII HUGE *)s,(ASCII HUGE *)(s+STRLEN(s)));
4176
return r ? prn_id(r) : 0;
4179
@ The last component of |name_info| is different for \.{TANGLE} and
4180
\.{WEAVE}. In \.{TANGLE}, if |p| is a pointer to a module name, |p->equiv|
4181
is a pointer to its replacement text, an element of the array |text_info|.
4182
In \.{WEAVE}, on the other hand, if |p| points to an identifier, |p->xref|
4183
is a pointer to its list of cross-references, an element of the array
4184
|xmem|. The make-up of |text_info| and |xmem| is discussed in the
4185
\.{TANGLE} and \.{WEAVE} source files, respectively; here we just declare a
4186
common field |equiv_or_xref| as a pointer to an |ASCII|.
4188
@* REPORTING ERRORS to the USER.
4189
The command `|ERR_PRINT(origin,"Error message")|' will report a syntax
4190
error to the user, by printing the error message at the beginning of a new
4191
line and then giving an indication of where the error was spotted in the
4192
source file. Note that no period follows the error message, since the
4193
error routine will automatically supply a period. The |origin| argument is
4194
one of~|C|, |T|, |W|, |R|, or~|M|; these are translated by the macro into
4195
things like |ERR_C|, an enumerated type that says where the error came
4196
from. More generally, one can say |err_print(origin,msg,args)|, where
4197
|msg| can be formatted as for |printf| and |n|~specifies how many arguments
4198
follow. Thus, |ERR_PRINT| is a special case of |err_print| with $n = 0$.
4200
The actual error indications are provided by a procedure called |error|.
4201
However, error messages are not actually reported during phase one, since
4202
errors detected on the first pass will be detected again during the second.
4206
IN_COMMON CONST char *origin_name[]
4207
#if(part == 0 || part == 1)
4208
= {"","COMMON","FTANGLE","FWEAVE","RATFOR","MACROS","STYLE",".fweb"}
4209
#endif // |part == 1|
4216
err0_print FCN(VA_ALIST((err_origin,fmt,n VA_ARGS)))
4218
ERR_ORIGIN err_origin C0("Where the error came from")@;
4219
CONST outer_char *fmt C0("Format of error message.")@;
4220
int n C2("Number of arguments to the string.")@;)@;
4223
ASCII HUGE *k, HUGE *l; /* pointers into |cur_buffer| */
4225
boolean prn_input_buffer;
4226
boolean newline_first;
4227
#if(NUM_VA_ARGS == 1)
4228
ERR_ORIGIN err_origin;
4229
CONST outer_char *fmt;
4233
VA_START(arg_ptr, n);
4235
#if(NUM_VA_ARGS == 1)
4236
err_origin = va_arg(arg_ptr,ERR_ORIGIN);
4237
fmt = va_arg(arg_ptr,outer_char *);
4238
va_arg(arg_ptr,int);
4241
prn_input_buffer = BOOLEAN(err_origin!=ERR_NULL);
4249
newline_first = YES;
4251
if(prn_input_buffer && err_origin != ERR_S && newline_first)
4256
if(prn_input_buffer)
4259
CLR_PRINTF(ALWAYS, md_name, ("(%s)", origin_name[err_origin]));
4260
sprintf(temp,": %s", (char *)fmt);
4261
vprintf(temp, arg_ptr);
4264
vprintf((char *)fmt, arg_ptr);
4268
if(prn_input_buffer)
4270
outer_char last_char = fmt[STRLEN(fmt)-1];
4272
if(*fmt && last_char != '?' && last_char != '!')
4274
/* Sometimes the third argument to |fatal| is empty,
4275
so the period isn't necessary; or the phrase ends
4276
with a question mark or exclamation point. */
4278
if(err_origin == ERR_S)
4279
@<Print error location based on style file buffer@>@;
4281
@<Print error location based on input buffer@>@;
4284
fflush(stdout); mark_error;
4286
if(prn_input_buffer && err_origin != ERR_S && !newline_first)
4289
mfree(); // Free up any |mod_trans| allocations.
4291
free_buf(&sput_buf);
4295
@ The error locations can be indicated by using the global variables |loc|,
4296
|cur_line|, |cur_file_name| and |changing|, which tell respectively the
4297
first unlooked-at position in |cur_buffer|, the current line number, the
4298
current file, and whether the current line is from |change_file| or
4299
|cur_file|. This routine should be modified on systems whose standard text
4300
editor has special line-numbering conventions. @^system dependencies@>
4304
@<Print error location based on input buffer@>=
4310
CLR_PRINTF(ALWAYS, line_num,
4311
(" (l. %u of %s file ", change_line, "change"));
4312
CLR_PRINTF(ALWAYS, in_file, ("%s)\n", (char *)change_file_name));
4314
else if (incl_depth==WEB_FILE)
4316
CLR_PRINTF(ALWAYS, line_num, (" (l. %u)\n", cur_line));
4320
CLR_PRINTF(ALWAYS, line_num,
4321
(" (l. %u of %s file ",cur_line,"include"));
4322
CLR_PRINTF(ALWAYS, in_file,("%s)\n", (char *)cur_file_name));
4327
SET_COLOR(ordinary);
4329
if (l>cur_buffer && cur_buffer!=NULL)
4331
if((BUF_SIZE)(l-cur_buffer) > buf_size)
4333
CLR_PRINTF(ALWAYS, error,
4334
("! TROUBLE IN ERROR ROUTINE. PLEASE REPORT THIS!\n"));
4337
l = cur_buffer + MAX_ERR_BUF;
4340
for (k=cur_buffer; k<l; k++)
4342
putchar(' '); // Convert tab to space.
4344
putchar(*k > 127 ? ' ' : XCHR(*k));
4345
/* print the characters already read */
4347
putchar('\n'); // Separate already read from not yet read.
4349
multi_chars(' ', l - cur_buffer); // Space out the next line.
4354
if((BUF_SIZE)(limit-l) > buf_size)
4356
puts("! TROUBLE IN ERROR ROUTINE. PLEASE REPORT THIS!");
4357
l = limit - MAX_ERR_BUF;
4360
for (k=l; k<limit; k++)
4361
putchar(*k > 127 ? ' ' : XCHR(*k));
4362
/* print the part not yet read */
4365
if (*limit==@'|') putchar('|'); // End of code text in module names.
4367
putchar(' '); // To separate the message from future asterisks.
4372
@ Print a character multiple times.
4375
multi_chars FCN((c, n))
4376
outer_char c C0("Character to print")@;
4377
int n C1("Number of times to print")@;
4384
@<Print error location based on style file buffer@>=
4386
CONST outer_char HUGE *k, HUGE *l;
4387
IN_STYLE boolean from_sprm;
4389
printf(" (l. %u of %s file ", s_line, "style");
4390
CLR_PRINTF(ALWAYS, in_file, ("%s)\n",
4391
from_sprm ? "parameter buffer" : (char *)style_file_name));
4393
l = MIN(sloc,slimit);
4395
for (k=sbuf; k<l; k++)
4396
putchar(*k > 127 ? ' ' : (*k == '\t' ? ' ' : *k));
4397
// Print the characters already read.
4399
putchar('\n'); // Separate already read from not yet read.
4401
multi_chars(' ', l - sbuf); // Space out the next line.
4403
for (k=l; k<slimit; k++)
4404
putchar(*k > 127 ? ' ' : *k); // Print the part not yet read.
4409
@ When no recovery from some error has been provided, we have to wrap up
4410
and quit as graciously as possible. This is done by calling the function
4411
|wrap_up| at the end of the code.
4416
fatal FCN(VA_ALIST((err_origin, s1, fmt VA_ARGS)))
4418
ERR_ORIGIN err_origin C0("Where the error came from")@;
4419
CONST outer_char s1[] C0("Basic message.")@;
4420
CONST outer_char fmt[] C2("Elaboration.")@;)@;
4423
#if(NUM_VA_ARGS == 1)
4424
ERR_ORIGIN err_origin;
4425
CONST outer_char s1[];
4426
CONST outer_char fmt[];
4428
outer_char temp[500];
4430
VA_START(arg_ptr, fmt);
4432
#if(NUM_VA_ARGS == 1)
4433
err_origin = va_arg(arg_ptr, ERR_ORIGIN);
4434
s1 = va_arg(arg_ptr, outer_char *);
4435
fmt = va_arg(arg_ptr, outer_char *);
4439
if(err_origin != ERR_NULL)
4441
CLR_PRINTF(ALWAYS, fatal, ("! "));
4442
CLR_PRINTF(ALWAYS, md_name, ("(%s)", origin_name[err_origin]));
4443
CLR_PRINTF(ALWAYS, fatal, (": "));
4445
CLR_PRINTF(ALWAYS, fatal,((char *)s1));
4447
vsprintf((char *)temp, (CONST char *)fmt, arg_ptr);
4448
err0_print(ERR_NULL, temp, 0);
4450
history = FATAL_MESSAGE;
4454
return wrap_up(); /* It never really returns, but the compiler doesn't
4458
@ Sometimes the program's behavior is far different from what it should be,
4459
and \.{WEB} prints an error message that is really for the \.{WEB}
4460
maintainance person, not the user. In such cases the program says
4461
|confusion("indication of where we are","what went wrong")|. The second
4462
argument is actually a format in the style of |printf|, and there may
4463
optionally be additional arguments that are printed into the format.
4465
Note that this function doesn't really return. It's coded this way so
4466
compilers don't give spurious warnings.
4470
confusion FCN(VA_ALIST((where, fmt0 VA_ARGS)))
4472
CONST outer_char where[] C0("")@;
4473
CONST outer_char fmt0[] C2("")@;)@;
4475
outer_char fmt[200], temp[200];
4477
#if(NUM_VA_ARGS == 1)
4478
CONST outer_char where[];
4479
CONST outer_char fmt0[];
4482
VA_START(arg_ptr, fmt0);
4484
#if(NUM_VA_ARGS == 1)
4485
where = va_arg(arg_ptr, outer_char *);
4486
fmt0 = va_arg(arg_ptr, outer_char *);
4489
SPRINTF(200, fmt, `"\n At %s: %s.", where, fmt0`);
4490
vsprintf((char *)temp, (CONST char *)fmt, arg_ptr);
4494
return FATAL(NULL, "\n!!! \
4495
I'M CONFUSED; this shouldn't happen. PLEASE REPORT THIS!!! ", temp);
4497
@.This shouldn't happen@>
4499
@ An overflow stop occurs if \.{WEB}'s tables aren't large enough.
4503
overflow FCN((s,abbrev))
4504
CONST outer_char s[] C0("Condition that caused overflow.")@;
4505
CONST outer_char abbrev[] C1("Abbreviation for buffer.")@;
4510
"!!! Sorry, CAPACITY EXCEEDED: ",
4511
"%s contents too long.", (char *)s);
4515
MEM HUGE *m = msearch(abbrev,0L);
4518
"!!! Sorry, CAPACITY EXCEEDED: ",
4519
"Too many %s. Current number is %lu, absolute max is \
4520
%lu. Use command-line option \"-y%s...\" to override default.",
4521
(char *)s, m->nunits, m->max, (char *)abbrev);
4524
@.Sorry, capacity exceeded@>
4526
@ A special case of |overflow| is useful for style-file parameters.
4530
new_sprm FCN((name, value))
4531
CONST outer_char name[] C0("Name of parameter.")@;
4532
int value C1("Integer value of parameters.")@;
4536
sprintf(temp, "Please increase style-file parameter `%s' from current value \
4537
of %i;", name, value);
4541
@ Some implementations may wish to pass the |history| value to the
4542
operating system so that it can be used to govern whether or not other
4543
programs are started. Here, for instance, we pass the Unix operating system
4544
a status of~ 0 if and only if only harmless messages were printed.
4545
@^system dependencies@>
4550
#include stsdef // Return codes.
4556
/* --- Finish up --- */
4558
see_statistics(); /* Invoked by command-line option~\.{-s}.
4559
(\FTANGLE\ and \FWEAVE\ each has its own version of
4560
|see_statistics|.) */
4566
@<Print the job |history|@>@;
4567
@<Close all open files@>@;
4569
SET_COLOR(ordinary);
4574
case SPOTLESS: exit(STS$K_SUCCESS | STS$M_INHIB_MSG);
4575
case HARMLESS_MESSAGE: exit(STS$K_INFO | STS$M_INHIB_MSG);
4576
case ERROR_MESSAGE: exit(STS$K_ERROR | STS$M_INHIB_MSG);
4577
case FATAL_MESSAGE: exit(STS$K_SEVERE | STS$M_INHIB_MSG);
4583
case HARMLESS_MESSAGE:
4587
exit(1); // Some reasonably serious error occurred.
4591
return -1; // Never really does this.
4594
@ With some systems, it seems to be helpful to close the open files
4595
explicitly, rather than letting |exit| do it. I don't know why.
4597
@<Close all open files@>=
4599
if(cur_prms.web && cur_prms.web->File)
4600
fclose(cur_prms.web->File);
4602
if(cur_prms.change && cur_prms.change->File)
4603
fclose(cur_prms.change->File);
4605
cls_files(); // Do stuff specific to \FTANGLE\ or \FWEAVE.
4609
@<Print the job |history|@>=
4611
if(msg_level >= SHORT_INFO
4612
|| (msg_level >= WARNINGS && history >= HARMLESS_MESSAGE)
4613
|| (history >= ERROR_MESSAGE))
4616
CLR_PRINTF(ALWAYS, program_name,
4617
("[%s:",program==tangle ? TANGLE : WEAVE));
4624
CLR_PRINTF(SHORT_INFO, info,
4625
("No errors were found."));
4628
case HARMLESS_MESSAGE:
4629
CLR_PRINTF(WARNINGS, warning,
4630
("%cDid you see the warning message(s) above?", beep(1)));
4634
CLR_PRINTF(ALWAYS, error,
4635
("%cPardon me, but I think I spotted something wrong.", beep(1)));
4639
CLR_PRINTF(ALWAYS, fatal,
4640
("%cThat was a fatal error, my friend.", beep(2)));
4642
} // There are no other cases.
4644
if(msg_level >= SHORT_INFO
4645
|| (msg_level >= WARNINGS && history >= HARMLESS_MESSAGE)
4646
|| (history >= ERROR_MESSAGE))
4647
CLR_PRINTF(ALWAYS, program_name, ("]\n"));
4652
@ Certain errors beep the terminal. The beeps can be turned off (and
4653
replaced by an exclamation point) with the \.{-B} option.
4667
return '\007'; // (Some compilers didn't understand \.{'\\a'}.)
4673
@ The following error routine is called if there's problem with writing.
4674
Usually, this means that you've run out of disk space.
4678
out_error FCN((fcn))
4679
CONST outer_char fcn[] C1("")@;
4683
"from %s (quota exceeded?).",
4688
@ Here is a function that accepts either \.{yes} or \.{no} from the
4689
keyboard, then returns~1 or~0.
4694
verify FCN(VA_ALIST((fmt VA_ARGS)))
4696
CONST outer_char fmt[] C2("")@;)@;
4701
VA_START(arg_ptr, fmt);
4702
vprintf_((CONST char *)fmt, arg_ptr)@;
4706
CLR_PRINTF(ALWAYS, info, ("yes"));
4708
CLR_PRINTF(ALWAYS, error, ("no"));
4709
printf("]? "), fflush(stdout);
4713
SET_COLOR(ordinary);
4716
if(STRCMP(buf, "yes") == 0 || STRCMP(buf, "no") == 0)
4720
printf(">>> Please answer ``yes'' or ``no'': ");
4724
return (STRCMP(buf, "yes") == 0);
4727
@* INITIALIZING FLAGS.
4729
@<Initialize \TeX\ cat...@>=
4733
for(k=0; k<128; k++)
4736
for(k=@'A'; k<=@'Z'; k++)
4737
TeX[k] = TeX_letter;
4739
for(k=@'a'; k<=@'z'; k++)
4740
TeX[k] = TeX_letter;
4742
TeX[@'\\'] = TeX_escape;
4743
TeX[@'{'] = TeX_bgroup;
4744
TeX[@'}'] = TeX_egroup;
4745
TeX[@'$'] = TeX_math_shift;
4746
TeX[@'&'] = TeX_alignment_tab;
4747
TeX[@'\n'] = TeX_eol;
4748
TeX[@'#'] = TeX_parameter;
4749
TeX[@'^'] = TeX_superscript;
4750
TeX[@'_'] = TeX_subscript;
4751
TeX[0] = TeX_ignored;
4752
TeX[@' '] = TeX[tab_mark] = TeX_space;
4753
TeX[@'~'] = TeX_active;
4754
TeX[@'%'] = TeX_comment;
4755
TeX[0177] = TeX_invalid;
4758
@ We don't initialize the flags statically because it's too tedious to get
4759
the initialization list right if one adds a flag randomly.
4761
@<Initialize flags@>=
4763
params.uses = 0; // For safety.
4765
#define FSET(flag) = flag
4767
active_brackets FSET(ACTIVE_BRACKETS); // Special array handling?
4768
all_cmnts_verbatim FSET(ALL_CMNTS_VERBATIM); /* Should \.{TANGLE} copy
4770
all_includes FSET(ALL_INCLUDES); // Cross-reference all include files?
4771
auto_app_semi FSET(AUTO_APP_SEMI); /* For \.{WEAVE}, automatically
4772
append a pseudo-semi to the end of \.{WEB} macro definitions? */
4773
auto_line FSET(AUTO_LINE); // Auto-insert line nums after \.{@@\%}?
4775
/* Append automatic semicolon or pseudo-semicolon? */
4776
params.Auto_semi[LN(FORTRAN)]
4777
= params.Auto_semi[LN(FORTRAN_90)] = YES;
4778
params.Auto_semi[LN(RATFOR)]
4779
= params.Auto_semi[LN(RATFOR_90)]
4780
= params.Auto_semi[LN(C)]
4781
= params.Auto_semi[LN(C_PLUS_PLUS)]
4782
= params.Auto_semi[LN(LITERAL)]
4783
= params.Auto_semi[LN(TEX)]
4786
/* Which do we append: pseudo or actual? */
4787
params.Auto_pseudo_semis[LN(FORTRAN)]
4788
= params.Auto_pseudo_semis[LN(RATFOR)]
4789
= params.Auto_pseudo_semis[LN(RATFOR_90)]
4790
= params.Auto_pseudo_semis[LN(C)]
4791
= params.Auto_pseudo_semis[LN(C_PLUS_PLUS)]
4792
= params.Auto_pseudo_semis[LN(LITERAL)]
4793
= params.Auto_pseudo_semis[LN(TEX)]
4796
params.Auto_pseudo_semis[LN(FORTRAN_90)] FSET(AUTO_PSEUDO_SEMIS);
4797
// F90 code: append pseudo-semis, not semis.
4799
params.Beeps FSET(BEEPS);
4801
/* Do we label |do|s and |if|s with the block number? */
4802
params.Block_nums[LN(C)]
4803
= params.Block_nums[LN(C_PLUS_PLUS)]
4804
= params.Block_nums[LN(LITERAL)]
4805
= params.Block_nums[LN(TEX)]
4808
params.Block_nums[LN(FORTRAN)]
4809
= params.Block_nums[LN(FORTRAN_90)]
4810
= params.Block_nums[LN(RATFOR)]
4811
= params.Block_nums[LN(RATFOR_90)]
4814
bslash_continued_strings FSET(BSLASH_CONTINUED_STRINGS); /* Do the
4815
continuations of strings require a starting backslash? */
4816
chk_ifelse FSET(CHK_IFELSE); // Protect parenthesized strings?
4817
chk_stmts FSET(CHK_STMTS); // Check statement syntax in \Ratfor?
4818
compare_outfiles FSET(COMPARE_OUTFILES);// Compare new vs.\ old output.
4819
compound_assignments FSET(COMPOUND_ASSIGNMENTS); /* Do we allow things
4821
Cpp FSET(NO); /* Do we recognize \.{C++}? */
4823
dbg_output FSET(DBG_OUTPUT); // Print output characters?
4824
deferred_macros FSET(DEFERRED_MACROS); // Allow deferred macros?
4826
defn_mask.outer_macros FSET(PRN_OUTER_MACROS);
4827
defn_mask.formats FSET(PRN_fORMATS);
4828
defn_mask.Formats FSET(PRN_FORMATS);
4829
defn_mask.limbo FSET(PRN_LIMBO);
4830
defn_mask.macros FSET(PRN_MACROS);
4831
defn_mask.v FSET(PRN_V);
4832
defn_mask.w FSET(PRN_W);
4834
dot_constants FSET(DOT_CONSTANTS); // Recognize dot constants?
4836
params.CPP_comments[LN(C)]
4837
= params.CPP_comments[LN(C_PLUS_PLUS)]
4838
= params.CPP_comments[LN(LITERAL)]
4839
= params.CPP_comments[LN(TEX)]
4842
/* For \Fortran\ and \Ratfor, the \.{//}~is preempted for concatenation.
4843
Therefore, its use for short comments must be turned on explicitly. */
4844
params.CPP_comments[LN(FORTRAN)]
4845
= params.CPP_comments[LN(FORTRAN_90)]
4846
= params.CPP_comments[LN(RATFOR)]
4847
= params.CPP_comments[LN(RATFOR_90)]
4850
Fortran88 FSET(FORTRAN88); // Turn on stuff for Fortran-88?
4851
Fortran_label FSET(FORTRAN_LABEL); // Label on same line?
4853
params.Free_form_input[LN(FORTRAN)]
4854
= params.Free_form_input[LN(LITERAL)]
4855
= params.Free_form_input[LN(TEX)]
4857
params.Free_form_input[LN(RATFOR)]
4858
= params.Free_form_input[LN(RATFOR_90)]
4859
= params.Free_form_input[LN(C)]
4860
= params.Free_form_input[LN(C_PLUS_PLUS)]
4861
= params.Free_form_input[LN(FORTRAN_90)]
4864
keep_trailing_comments FSET(KEEP_TRAILING_COMMENTS); // For \TeX.
4865
ignore_C FSET(IGNORE_C); // Throw away single \Fortran\ comment lines.
4867
params.In_escape[LN(FORTRAN)]
4868
= params.In_escape[LN(FORTRAN_90)]
4870
params.In_escape[LN(RATFOR)]
4871
= params.In_escape[LN(RATFOR_90)]
4872
= params.In_escape[LN(C)]
4873
= params.In_escape[LN(C_PLUS_PLUS)]
4874
= params.In_escape[LN(LITERAL)]
4875
= params.In_escape[LN(TEX)]
4878
in_escape FSET(IN_ESCAPE); // Default continuation character.
4879
index_hidden FSET(INDEX_HIDDEN); // Index skipped includes files.
4880
index_one FSET(INDEX_ONE); // Index single-character identifiers?
4881
input_macros FSET(INPUT_MACROS); /* Generate the default ``\.{\\input
4882
fwebmac.tx}'' line? */
4883
lc_keywords FSET(LC_KEYWORDS); // Lower-case \Fortran\ keywords?
4884
line_info FSET(LINE_INFO); // Should \FTANGLE\ print out line info?
4885
lowercase_tokens FSET(LOWERCASE_TOKENS); // Output lower-case tokens?
4887
makeindex FSET(MAKEINDEX); // Output a \.{makeindex} file?
4888
no_xref FSET(NO_XREF); // Should \.{WEAVE} print an index?
4889
number_dos FSET(NUMBER_DOS);
4890
nuweb_mode FSET(NUWEB_MODE); // Emulate \.{nuweb}?
4891
overload_ops FSET(OVERLOAD_OPS); // Is operator overloading allowed?
4893
params.Point_comments[LN(C)]
4894
= params.Point_comments[LN(C_PLUS_PLUS)]
4895
= params.Point_comments[LN(TEX)]
4896
= params.Point_comments[LN(LITERAL)]
4898
params.Point_comments[LN(FORTRAN)]
4899
= params.Point_comments[LN(FORTRAN_90)]
4900
= params.Point_comments[LN(RATFOR)]
4901
= params.Point_comments[LN(RATFOR_90)]
4902
= POINT_COMMENTS; /* Does \.! signify a Fortran comment? */
4904
prn_contents FSET(PRN_CONTENTS); // Print the table of contents?
4905
prn_index FSET(PRN_INDEX); // Print the index?
4906
prn_input_lines FSET(NO); /* Used in |input_ln| to print out the
4908
prn_input_addresses FSET(NO); /* Print out the buffer addresses. */
4909
prn_modules FSET(PRN_MODULES); // Print the module list?
4910
prn_semis FSET(PRN_SEMIS); // Print semicolons in \Fortran\ output?
4912
quoted_includes FSET(QUOTED_INCLUDES);
4913
// Cross-reference quoted include files?
4914
Ratfor77 FSET(RATFOR77); /* Expand \Ratfor\ code directly to
4916
read_iformats FSET(READ_IFORMATS); // Read include files for formats.
4917
params.Reverse_indices[LN(C)]
4918
= params.Reverse_indices[LN(C_PLUS_PLUS)]
4919
= params.Reverse_indices[LN(TEX)]
4920
= params.Reverse_indices[LN(LITERAL)]
4922
params.Reverse_indices[LN(FORTRAN)]
4923
= params.Reverse_indices[LN(FORTRAN_90)]
4924
= params.Reverse_indices[LN(RATFOR)]
4925
= params.Reverse_indices[LN(RATFOR_90)]
4926
= REVERSE_INDICES; // C-style indexing in \Fortran?
4927
rmv_files FSET(RMV_FILES); // Remove temporary files related to \.{-H}?
4929
skip_ifiles FSET(SKIP_IFILES); // \.{-j} --- skip files already included.
4930
skip_includes FSET(SKIP_INCLUDES); /* For \.{WEAVE}, don't read in \.{@@I}
4931
commands. (Uppercase '\.{I}' only.) */
4932
statistics FSET(STATISTICS); /* Print statistics about memory usage? */
4933
stop_the_scan FSET(YES); /* */
4934
subscript_fcns FSET(YES); // Module references on functions?
4935
suppress_cmds FSET(YES); /* ??? */
4936
toggle_includes FSET(TOGGLE_INCLUDES); /* For \.{WEAVE}, read in
4937
\.{@@I} commands, but don't print them out. */
4938
top_version FSET(TOP_VERSION); /* Header info at top of output? */
4939
translate_ASCII FSET(TRANSLATE_ASCII0); /* Do we bother with the
4940
|xchr|--|xord| conversions? */
4941
translate_brackets FSET(TRANSLATE_BRACKETS); /* Do we translate
4942
brackets to parentheses in \Fortran? */
4943
truncate_ids FSET(NO); /* Shorten identifiers? */
4944
try_extensions FSET(TRY_EXTENSIONS); // Try various file names.
4945
TeX_processor FSET(TEX_PROCESSOR); // Default \TeX\ processor.
4946
xref_unnamed FSET(XREF_UNNAMED); // Xref unnamed stuff.
4949
#if(TRANSLATE_ASCII || DEBUG_XCHR)
4950
translate_ASCII = YES; // Force it for the cases that really need it.
4953
/* The following ensures that |ini_language| will work right. (It
4954
references |global_params|.) */
4955
global_params = params;
4958
@ When we're scanning for identifiers, we have sometimes to do a
4959
byte-by-byte comparison looking for special strings. These have to be
4960
|ASCII|, not ordinary compiler strings. Thus, we initialize them here.
4964
IN_COMMON ASCII HUGE *pformat,HUGE *pdefault, HUGE *pdata,
4965
HUGE *pbp, HUGE *pinclude, HUGE *ppragma, HUGE *pcontains;
4967
@ We must translate the |outer_char| strings into |ASCII|.
4969
@<Initialize static...@>=
4971
pformat = x__to_ASCII(OC("format"));
4972
pdata = x__to_ASCII(OC("data"));
4973
pdefault = x__to_ASCII(OC("default"));
4974
pbp = x__to_ASCII(OC("_BP"));
4975
pinclude = x__to_ASCII(OC("include"));
4976
ppragma = x__to_ASCII(OC("pragma"));
4978
conv_bi(incl_likes);
4979
conv_bi(WEB_incl_likes);
4980
conv_bi(non_labels);
4986
@* COMMAND-LINE ARGUMENTS.
4987
The user calls \.{WEAVE} and \.{TANGLE} with arguments on the command line.
4988
These are either file names or flags (beginning with |'-'|).
4989
The following globals are for communicating the user's desires to the rest
4990
of the program. The various file name variables contain strings with
4991
the names of those files.
4993
See the user manual for a detailed description of the various flags.
4998
IN_COMMON outer_char outp_buf[MAX_OUTPUT_LINE_LENGTH];
4999
// FORTRAN's output buffer.
5000
IN_COMMON int nbuf_length CSET(72);
5002
IN_COMMON outer_char wbprefix[MAX_FILE_NAME_LENGTH];
5003
// Possible directory prefix for the web file name.
5005
/* Signifies the end of a directory prefix. */
5006
IN_COMMON outer_char prefix_end_char CSET(PREFIX_END_CHAR);
5008
IN_COMMON boolean all_std CSET(NO); // Do we route all output to |stdout|?
5010
@ We now must look at the command line arguments and set the file names
5011
accordingly. At least one file name must be present: the \.{WEB}
5012
file. It may have an extension, or it may omit it to get |'.web'|
5013
added. The \TeX\ output file name is formed by replacing the \.{WEB}
5014
file name extension by |'.tex'|, and the code output file name by replacing
5015
the extension by~|'.c'|, |'.rat'|, or~|'.for'|.
5017
If there is another file name present among the arguments, it is the
5018
change file, again either with an extension or without one to get~|'.ch'|
5019
An omitted change file argument means that \.{/dev/null} (for Unix),
5020
\.{nl:} (for VMS), or \.{NUL} (for DOS) should be used, when no
5021
changes are desired. (These default extensions are in the style file, and
5023
@^system dependencies@>
5025
The following function should be superceded by ANSI's |strchr|.
5028
char HUGE *index FCN((s,c)) /* this is standard, but the name is not */
5029
char HUGE *s C0("String to be searched.")@;
5030
char c C1("Search for this character.")@;
5032
while (*s!=c && *s!='\0')
5034
if (*s=='\0') return NULL;
5038
@*1 Scanning command-line arguments.
5039
|scan_args| is the major routine that handles
5040
the command-line arguments. If a command-line option doesn't begin with a
5041
hyphen, it's a file name. If it just a hyphen, it means ``\.{stdin}''.
5042
Otherwise, it's an option.
5046
IN_COMMON boolean found_web,found_change; // Have these names been seen?
5047
IN_COMMON outer_char wbflnm0[MAX_FILE_NAME_LENGTH];
5048
// The root name of the web file.
5056
outer_char wbflnm[MAX_FILE_NAME_LENGTH];
5058
/* Build the command line in a buffer. */
5059
cmd_ln_buf = cmd_line(argc, argv);
5061
/* Add the arguments from the ini file \.{.fweb}. This resets |argc| and
5062
|argv|, and also ignores |argv[0]| from the command line. */
5063
set_args(&argc, &argv);
5065
/* Process all the arguments in order from left to right. */
5066
do_args(YES, argc, argv, &found_web, wbflnm, &found_change);
5068
banner(); // Start-up banner.
5071
mem_avail(0); /* How much memory is available at start? */
5073
@<Get the path and root file name@>@;
5075
@<Read the style file@>@;
5077
@<Make |web_file_name|, |tex_fname| and output file names@>;
5079
if (!(found_web || info_option))
5080
usage("!! Missing webfile name.\n", YES);
5083
*change_file_name = '\0'; // This means null file.
5086
@ Here we allocate a buffer and build the command line into it.
5088
@d WEAVE_LINE0 OC("{\"")
5089
@d TANGLE_LINE0 OC(" COMMAND LINE: \"")
5091
@d WEAVE_LINE1 OC("\"} ")
5092
@d TANGLE_LINE1 OC("\"")
5094
@d ESCAPE_LENGTH 256
5099
cmd_line FCN((num_args,args))
5100
int num_args C0("Number of arguments on command line.")@;
5101
outer_char *args[] C1("Array of pointers to arguments.")@;
5104
outer_char HUGE *buf,HUGE *b, HUGE *line0, HUGE *line1;
5105
outer_char p[ESCAPE_LENGTH]; /* The escaped argument. */
5109
line0 = TANGLE_LINE0;
5110
line1 = TANGLE_LINE1;
5114
line0 = WEAVE_LINE0;
5115
line1 = WEAVE_LINE1;
5118
/* Determine required buffer length. */
5119
for(k=0,len=0; k<num_args; len += STRLEN(args[k++]) + 1) ;
5123
buf = GET_MEM("buf", 2*(len+l0+l1), outer_char);
5127
while(num_args-- > 0)
5129
int n = esc_file_name(p, ESCAPE_LENGTH, *(args++));
5136
STRCPY(--b, line1); /* Kill off final blank. */
5140
@*1 Processing {\tt .fweb}.
5141
Here we handle the initialization file
5142
\.{.fweb}. If it exists, it is opened. Arguments beginning with a '\.-' are
5143
placed before the command-line arguments (\.+ also works for backward
5144
compatibility); those beginning with a '\.\&' are placed after the
5145
command-line arguments.
5149
IN_COMMON outer_char
5150
HUGE * HUGE *ini_args,
5151
// Arguments read from the ini file; allocated dynamically.
5152
HUGE * HUGE *new_args, /* The new list of all arguments; allocated
5154
HUGE * HUGE *pn; // Pointer to the current new argument being processed.
5155
IN_COMMON outer_char HUGE * HUGE *pn0; // Marks end of ini args.
5156
IN_COMMON outer_char HUGE * HUGE *pn1; // Marks end of cmd-line args.
5157
IN_COMMON FILE *ini_file CSET(NULL); // The ini file \.{.fweb}.
5161
@d MAX_INI_ARGS 100 // Max \# of arguments in the ini file.
5162
@d T_SIZE 200 // Buffer length for |fgets|.
5163
@d INI_CMNT_CHAR '%'
5172
set_args FCN((pargc,pargv))
5173
int HUGE *pargc C0("Pointer to the arg count; new value returned.")@;
5174
outer_char HUGE * HUGE * *pargv C1("Ptr to the array of ptrs; returned.")@;
5177
outer_char HUGE *buffer, // Put one line from ini file into here.
5178
HUGE *temp, HUGE *t, // Extract the argument into here.
5179
HUGE * HUGE *a, // The current ini arg.
5180
HUGE *pc; // Pointer to possible comment character in ini line.
5182
outer_char ini_file_name[MAX_FILE_NAME_LENGTH];
5184
/* Allocate temporary buffers. */
5185
buffer = GET_MEM("buffer",T_SIZE,outer_char);
5186
temp = GET_MEM("temp",T_SIZE,outer_char);
5188
/* We start by assuming there is no ini file, hence no argument. */
5189
k = 0; // Argument count.
5191
ini_args = GET_MEM("ini_args", MAX_INI_ARGS, outer_char HUGE *);
5193
@<Make ini file name and try to open it@>@;
5195
/* If there is an ini file, then put the arguments into an array. */
5197
@<Read \.{.fweb}@>@;
5199
/* Allocate space for all the arguments, both ini and command-line. (We
5200
don't count |argv[0]| from command line.) */
5201
pn = new_args = GET_MEM("new_args", n=*pargc-1+k, outer_char HUGE *);
5203
acopy(BEFORE_CHAR, ini_args, k);
5204
// Put those starting with '\.+' or '\.-' first.
5205
pn0 = pn; // Mark end of the ini stuff.
5207
acopy(MIDDLE_CHAR, (outer_char HUGE **)(*pargv), *pargc);
5208
// Then the command-line arguments.
5210
pn1 = pn; // Mark end of command-line arguments.
5212
acopy(AFTER_CHAR, ini_args, k); // Then the rest.
5214
/* Return the new number and array of arguments. */
5218
FREE_MEM(ini_args, "ini_args", MAX_INI_ARGS, outer_char HUGE *);
5219
FREE_MEM(buffer, "buffer", T_SIZE, char);
5220
FREE_MEM(temp, "temp", T_SIZE, char);
5223
@ We will look for the ini file in the user's root directory, if possible.
5224
We determine this by querying the environment with |getenv|.
5226
@d HOME OC("HOME") // The Unix-standard name for the top-level directory.
5227
@d ENV_INI OC("FWEB_INI") // Default root name to use.
5228
@d ENV_INCLUDES OC("FWEB_INCLUDES") // The include path for \FWEB\ files.
5229
@d ENV_HDR_INCLUDES OC("FWEB_HDR_INCLUDES") // \.{gcc -I} options.
5231
@<Make ini file...@>=
5233
outer_char *p_root, ini_root[MAX_FILE_NAME_LENGTH];
5235
#if HAVE_GETENV /* \.{Machine-dependent}: Understands |getenv|. */
5236
p_root = GETENV(ENV_INI);
5239
STRCPY(ini_root, p_root);
5241
STRCPY(ini_root, OC(FWEB_INI));
5243
more_includes(&incl, GETENV(ENV_INCLUDES)); // Included \FWEB\ files.
5244
more_includes(&hdr_incl, GETENV(ENV_HDR_INCLUDES));
5245
// Included C files to be scanned by preprocessor.
5247
STRCPY(ini_root, OC(FWEB_INI));
5248
#endif // |HAVE_GETENV|
5250
ini_file = FOPEN(mk_fname(ini_file_name, MAX_FILE_NAME_LENGTH,
5251
HOME, YES, ini_root), "r");
5254
@ Here we read the initialization file, if we could open it.
5258
outer_char CONST HUGE *b, HUGE *blimit;
5261
reading(ini_file_name, NO); // Can't print this here; msg level isn't set.
5264
for(a=ini_args; k<MAX_INI_ARGS; )
5266
if(FGETS(buffer, T_SIZE, ini_file)==NULL)
5267
break; // Read one line (including newline).
5269
blimit = buffer + STRLEN(buffer) - 1;
5270
// Position of end-of-line, excluding newline.
5272
/* Extract the string, disgarding the newline. If the line is blank, do
5276
for(t=temp,b=buffer; b < blimit; )
5285
@<Get ini string@>@;
5286
goto done; // Should check for trailing spurious text.
5290
for(b++; b<blimit; b++)
5291
if(*b != INI_CMNT_CHAR)
5293
err_print(F, "Garbage after option `%s' ignored", temp);
5301
*t++ = *b++; // Copy one character of argument.
5306
*t = '\0'; // Terminate the argument.
5312
continue; // The line was entirely a comment.
5314
*a = GET_MEM("ini arg", STRLEN(temp)+1, outer_char);
5315
// Space for the argument.
5316
STRCPY(*a++, temp); // Store ini argument.
5317
k++; // Count arguments found.
5323
if(msg_level >= SHORT_INFO)
5324
putchar('\n'); // End of reading message.
5331
outer_char delim = *b; // Opening and closing character of string.
5333
*t++ = *b++; // Store opening delimiter.
5339
err_print_(F, ".fweb: Missing quote inserted at end of string. \
5340
Did you mean to continue the line with '\\'? \
5341
(No white space is allowed after the '\\'.)");
5349
{ /* Line is continued. */
5350
FGETS(buffer, T_SIZE, ini_file);
5351
blimit = buffer + STRLEN(buffer);
5359
*t++ = *b++; // Ordinary character within string.
5362
*t++ = *b++; // Store terminating quote.
5365
@ The list of include paths is allocated only as necessary.
5368
IN_COMMON INCL_PATHS incl;
5369
IN_COMMON INCL_PATHS hdr_incl;
5371
@ This function adds more entries to the include list. The list is
5372
constructed in the format \.{$\alpha$:$\beta$:$\gamma$:}; in other words,
5373
entries are terminated by a colon.
5378
more_includes FCN((pincl, incl_list0))
5379
INCL_PATHS *pincl C0("Structure to fill")@;
5380
outer_char *incl_list0 C1("")@;
5388
/* Strip off useless opening colon. */
5389
while(*incl_list0 == ':')
5392
if((n = STRLEN(incl_list0)) == 0)
5395
old_size = incl.size;
5396
pincl->size += n + 2; // Allow for trailing colon and null byte.
5399
{ /* List hasn't been allocated yet. */
5400
pincl->list = GET_MEM("incl_list", pincl->size, outer_char);
5401
STRCPY(pincl->list, incl_list0);
5402
pincl->num = chr_cnt(pincl->list, ':'); // Count number of entries.
5405
{ /* Add more to the list. */
5406
pincl->list = (outer_char HUGE *)REALLOC(pincl->list, pincl->size,
5409
if(pincl->list == NULL)
5411
"!! NO MORE MEMORY",
5412
" (more_includes)");
5414
STRCAT(pincl->list, incl_list0);
5415
pincl->num += chr_cnt(incl_list0, ':');
5418
/* Ensure list ends in a colon. */
5419
if(pincl->list[n-1] != ':')
5421
pincl->list[n] = ':';
5431
outer_char *s C0("String to be searched for occurrences of |c|")@;
5432
outer_char c C1("Search character")@;
5448
mk_fname FCN((buffer,buf_len,env_var,print_msg,file_name))
5449
outer_char *buffer C0("Put result here.")@;
5450
unsigned buf_len C0("Length of |buffer|.")@;
5451
CONST outer_char *env_var C0("Variable that holds directory name.")@;
5452
boolean print_msg C0("Flag for not recognizing |env_var|.")@;
5453
CONST outer_char *file_name C1("The raw file name.")@;
5455
outer_char *directory; // Will hold result of |getenv|.
5457
#if HAVE_GETENV /* \.{Machine-dependent}: Understands |getenv|. */
5458
if( (directory=GETENV(env_var)) == NULL)
5461
printf("! I can't find environment \
5462
variable \"%s\", so I'll look for file \"%s\" \
5463
in directory \"%s\".\n",
5464
(char *)env_var, (char *)file_name,
5465
*wbprefix ? (char *)wbprefix : ".");
5466
if(buf_chk(file_name,buf_len,
5467
STRLEN(file_name)+STRLEN(wbprefix)))
5468
return (char *)wt_style.null_file_name;
5469
STRCPY(buffer,wbprefix);
5470
STRCAT(buffer,file_name);
5473
{ /* Got a directory from the environment variable. */
5474
if(buf_chk(file_name,buf_len,
5475
STRLEN(directory)+1+STRLEN(file_name)))
5476
return (char *)wt_style.null_file_name;
5477
/* For Unix, home is of the form
5478
\.{/u/krommes}; for VMS, it's like \.{ux3:[krommes]}. */
5480
SPRINTF(buf_len,buffer,`"%s%s",directory,file_name`);
5482
SPRINTF(buf_len,buffer,`"%s%c%s",directory,prefix_end_char,file_name`);
5483
#endif // |!UNIX_PATH|
5485
#else /* Doesn't understand |getenv|. */
5486
if(buf_chk(file_name,buf_len,STRLEN(file_name)))
5487
return (char *)wt_style.null_file_name;
5488
STRCPY(buffer,file_name);
5489
#endif // |CAN_GETENV|
5491
return (char *)buffer;
5494
@ Make sure we don't overrun a file buffer.
5498
buf_chk FCN((file_name,buf_len,needed))
5499
CONST outer_char *file_name C0("Raw file name")@;
5500
unsigned buf_len C0("Max space available")@;
5501
unsigned needed C1("Requested")@;
5503
if(needed >= buf_len)
5505
printf("\n! Buffer too small to make complete file name from \
5506
\"%s\"; opened \"%s\"\n", (char *)file_name, (char *)wt_style.null_file_name);
5513
@ Here we copy from an old argument array to the new one. If the argument
5514
begins with a~'\.+', that is changed to a~'\.-'.
5519
outer_char c C0("Starting symbol.")@;
5520
outer_char HUGE **a C0("Old array.")@;
5521
int n C1("Number of arguments to be looked at.")@;
5523
boolean is_before = BOOLEAN(c==BEFORE_CHAR || c=='+');
5524
boolean is_middle = BOOLEAN(c==MIDDLE_CHAR);
5525
boolean is_after = BOOLEAN(c==AFTER_CHAR);
5528
/* Disgard the zeroth argument from the command line. */
5537
/* Look at all the arguments; copy them if appropriate. */
5540
boolean before_entry = BOOLEAN((*a)[0] == BEFORE_CHAR || (*a)[0] == '+');
5542
/* The following somewhat inscrutable logic puts file names and ``after''
5543
entries from \.{.fweb} into the ``after'' list. */
5544
if( (!is_before && !before_entry) || (!is_after && before_entry) )
5546
*pn = GET_MEM("*pn", STRLEN(*a)+1, outer_char);
5548
if((*pn)[0] == '+' || (*pn)[0] == AFTER_CHAR)
5549
(*pn)[0] = BEFORE_CHAR;
5555
@*1 Making file names.
5556
We use all of |pa=*argv| for the |web_file_name| if
5557
there is a~|'.'| in it, otherwise add |'.web'|. \It{This has to be
5558
augmented for VMS!} The other file names come from adding things after the
5559
dot. We must check that there is enough room in |web_file_name| and the
5560
other arrays for the argument.
5562
If the output file name was already set by~`\.{->}', we do nothing, except if
5563
it contains a~`\.\#' we expand that into the |wbflnm0|. If it was not set,
5564
we add the extension determined from the style file.
5566
@m SET_NAME(l,labbrev) set_name(l,wt_style.output_ext.labbrev##_)
5568
@<Make |web_file_name...@>=
5572
/* \WEAVE's output file. */
5573
xpn_name(&tex_fname, MAX_FILE_NAME_LENGTH, tex_fname, wbflnm0);
5574
if(*tex_fname == '\0')
5575
new_fname(&tex_fname, wbflnm0, OC("tex"));
5577
/* \TANGLE's output files. */
5578
for(k=0; k<NUM_LANGUAGES; k++)
5579
xpn_name(params.outp_nm+k, MAX_FILE_NAME_LENGTH,
5580
params.outp_nm[k], wbflnm0);
5583
SET_NAME(C_PLUS_PLUS,Cpp);
5584
SET_NAME(LITERAL,V);
5585
SET_NAME(FORTRAN,N);
5586
SET_NAME(FORTRAN_90,N90);
5588
SET_NAME(RATFOR_90,R90);
5593
@<Get the path...@>=
5595
outer_char HUGE *dot_pos;
5596
outer_char HUGE *p = NULL;
5598
if (STRLEN(wbflnm) > MAX_FILE_NAME_LENGTH-5)
5601
if(STRCMP(wbflnm,"stdin") == 0) STRCPY(web_file_name,wbflnm);
5604
p = OC(STRRCHR(wbflnm,prefix_end_char)); // Is there a path?
5606
if((dot_pos=OC(STRCHR(p ? p+1 : wbflnm,wt_style.ext_delimiter)))==NULL
5608
{ /* Attach ``\.{.web}'' as a default. */
5609
SPRINTF(MAX_FILE_NAME_LENGTH,web_file_name,`"%s%cweb",
5610
wbflnm,wt_style.ext_delimiter`);
5612
else { /* There's a dot, or we're trying extensions; treat the
5614
SPRINTF(MAX_FILE_NAME_LENGTH,web_file_name,`"%s",wbflnm`);
5617
TERMINATE(dot_pos,0); /* string now ends where the
5622
@<Extract the basic file name and the directory prefix@>@;
5629
set_name FCN((l,output_ext))
5631
outer_char *output_ext C1("")@;
5635
if(*params.outp_nm[k=lan_num(l)] == '\0')
5636
new_fname(params.outp_nm+k,wbflnm0,output_ext);
5639
@ We will always write into the current directory. Thus, we strip off all
5640
leading subdirectory information.
5644
{ /* Path was specified. */
5645
STRNCPY(wbprefix,wbflnm,
5646
PTR_DIFF(size_t,p,(outer_char HUGE *)wbflnm)+1); // Path.
5647
STRCPY(wbflnm0,p+1); // Root name.
5650
{ /* No path specification. */
5651
TERMINATE(wbprefix,0); // No path.
5652
STRCPY(wbflnm0,wbflnm); // Root name.
5656
@ Here we search for an embedded~`\.\#' and expand that into the web file
5657
name. The construction `\.{\#\#}' is replaced by a single~`\.\#'.
5662
xpn_name FCN((pout_name, buf_len, in_name, wbflnm0))
5663
outer_char HUGE * HUGE *pout_name C0("The expanded result")@;
5664
int buf_len C0("Length of above buffer")@;
5665
CONST outer_char *in_name C0("File name possibly having a \.\#.")@;
5666
CONST outer_char wbflnm0[] C1("Basic name of the web file.")@;
5668
size_t n; /* Length of the root name. */
5669
outer_char HUGE *t,HUGE *t0=NULL;
5670
boolean buffered = BOOLEAN(*pout_name == in_name);
5673
t = t0 = GET_MEM("xpn buffer",buf_len,outer_char);
5677
for(; *in_name; in_name++)
5680
if(in_name[1] == '#')
5685
n = STRLEN(wbflnm0);
5686
STRNCPY(t,wbflnm0,n);
5697
new_fname(pout_name, t0, NULL);
5698
FREE_MEM(t0, "xpn buffer", buf_len,outer_char);
5705
@*1 Processing the {\tt .aux} file.
5706
When |TeX_processor == LaTeX_p|, the \.{.aux} file is read if it is
5707
present. Information from the \.{\\Newlabel} commands is accumulated and
5708
used to translate module numbers in error messages.
5711
@d MAX_MOD_NAMES 3 // No more than this many names in one error message.
5717
outer_char *secno; // The \LaTeX\ section number.
5718
sixteen_bits pageno, modno0;
5719
boolean subpage_flag; // |YES| if more than one section per page.
5722
IN_COMMON SECT_INFO HUGE *sect_info;
5724
IN_COMMON outer_char HUGE * HUGE *mod_names;
5725
IN_COMMON outer_char HUGE * HUGE *next_mod_name, HUGE * HUGE *last_mod_name;
5728
@<Allocate dynamic...@>=
5730
ALLOC(SECT_INFO, sect_info, ABBREV(max_modules), max_modules, 0);
5732
@ Open the \.{.aux} file, read and process each line of the file. If the
5733
line begins with ``\.{\\Newlabel}'', then the line is parsed. The same
5734
logic as in \.{fwebmac.web} is used to set the |subpage_flag| to |NO| if
5735
there is only one section per page, or to |YES| if there are more than one.
5742
outer_char aux_file_name[MAX_FILE_NAME_LENGTH];
5744
outer_char buffer[AUX_LEN];
5745
int aline = 0; // Counts line in the \.{.aux} file.
5746
sixteen_bits modno, modno0;
5747
outer_char smodno[100], smodno0[100], spageno[10], ssecno[100];
5749
SECT_INFO *ps; // Points to current info structure.
5750
int pmcount = 1; // Number of sections per page.
5751
long modno_ref = -1; /* Last reference module number. It's |long| to deal
5752
with the possibility of 16-bit integers, where |-1| would be the
5753
maximum module number. */
5755
next_mod_name = mod_names = GET_MEM("mod_names", MAX_MOD_NAMES,
5756
outer_char HUGE *@e);
5757
last_mod_name = mod_names + MAX_MOD_NAMES;
5759
if(TeX_processor != LaTeX_p)
5762
@<Determine \.{aux} file name and try to open it@>@;
5764
modno = 0; // In case there's nothing in the \.{.aux} file.
5766
while(FGETS(buffer,AUX_LEN,fa))
5770
if(STRNCMP(buffer,"\\Newlabel",9) != 0)
5773
@<Parse a \.{\\Newlabel} line@>@;
5776
sect_info[modno].subpage_flag = BOOLEAN(pmcount != 1);
5781
@<Determine \.{aux}...@>=
5783
outer_char *dot_pos; // For finding extension in file name.
5785
dot_pos = OC(STRCHR(tex_fname, wt_style.ext_delimiter));
5788
n = STRLEN(tex_fname);
5790
n = PTR_DIFF(int, dot_pos, tex_fname);
5792
STRNCPY(aux_file_name, tex_fname, n);
5793
SPRINTF(MAX_FILE_NAME_LENGTH, aux_file_name+n,`"%caux",
5794
wt_style.ext_delimiter`);
5796
fa = FOPEN(aux_file_name, "r");
5802
CLR_PRINTF(ALWAYS, in_file, ("%s", (char *)aux_file_name));
5803
printf(" file.)\n");
5807
reading(aux_file_name, YES);
5814
seea(int n, char *s)
5816
printf("Arg %i = \"%s\"\n", n, s);
5819
@ The format of the \.{\\Newlabel} line is
5820
``\.{\\Newlabel\{12\}\{\{1.11\}\{5\}\}\{10\}}''. In the original scheme,
5821
we used the ANSI |sscanf|
5822
\.{\%[\dots]} format command to read the contents of the braces as
5823
strings. However, that didn't work for complicated arguments that included
5824
nested braces. So now we do it right, taking account of brace levels.
5826
@<Parse a \.{\\New...@>=
5830
ERR_PRINT(C,"Sorry, non-ANSI sscanf; can't read .aux file");
5836
n = sscanf((char *)(buffer+9),"%*c%[^}]%*3c%[^}]%*2c%[^}]%*3c%[^}]",
5837
(char *)smodno, (char *)ssecno, (char *)spageno, (char *)smodno0);
5841
err_print(C,"Invalid \\Newlabel format in %s file, line %d",
5842
aux_file_name, aline);
5847
if(setjmp(top_of_get_arg) == 0)
5850
outer_char arg2[100];
5852
p = (outer_char *)STRCHR(buffer, '{');
5853
p = get_arg(smodno, p, '{', '}');
5858
p = get_arg(arg2, p, '{', '}');
5859
q = get_arg(ssecno, arg2, '{', '}');
5863
q = get_arg(spageno, q, '{', '}');
5868
p = get_arg(smodno0, p, '{', '}');
5875
err_print(C, "Invalid \\Newlabel format in %s file, line %d",
5876
aux_file_name, aline);
5880
modno = (sixteen_bits)ATOI(smodno);
5882
if(modno >= max_modules)
5883
OVERFLW("sections", ABBREV(max_modules));
5885
ps = sect_info + modno;
5887
ps->secno = GET_MEM("ps->secno",STRLEN(ssecno)+1,outer_char);
5888
STRCPY(ps->secno, ssecno);
5890
ps->pageno = (sixteen_bits)ATOI(spageno);
5891
ps->modno0 = modno0 = (sixteen_bits)ATOI(smodno0);
5893
/* Check to see whether there was more than one section on a page. */
5894
sect_info[modno-1].subpage_flag = YES;
5896
if((long)modno0 == modno_ref)
5897
pmcount++; // More than one section on page.
5899
{ /* Start of new page. */
5901
sect_info[modno-1].subpage_flag = NO;
5903
modno_ref = (long)modno0;
5908
@ The core routine for obtaining a delimited argument is |get_arg|. That
5909
can fail because of a variety of error conditions. If one is recognized,
5910
we bail out immediately with a |longjmp|.
5914
jmp_buf top_of_get_arg; // For premature bailout from get_arg.
5916
@ |get_arg| takes a string |s| and returns in |result| an argument
5917
delimited by |ldelim| and |rdelim|. The argument can itself contain
5922
get_arg FCN((result, s, ldelim, rdelim))
5923
outer_char *result C0("Resulting argument string")@;
5924
outer_char *s C0("Input argument")@;
5925
outer_char ldelim C0("Left argument delimiter")@;
5926
outer_char rdelim C1("Right argument delimiter")@;
5929
int blevel; // Brace level.
5933
err_print(C, "get_arg: Null arg pointer");
5934
longjmp(top_of_get_arg, 1);
5938
/* Skip white space at beginning of argument. */
5939
while(*s == ' ' || *s == '\t')
5944
err_print(C, "get_arg: Expected '%c' while processing \"%s\".\n",
5946
longjmp(top_of_get_arg, 2);
5949
s0 = s; // Remember beginning of argument.
5950
blevel = 1; // We've already found the left-hand delimiter.
5956
else if(*s == rdelim)
5962
err_print(C, "Too many '%c' while processing \"%s\"\n",
5964
longjmp(top_of_get_arg, 3);
5968
{ /* We've found the matching right-hand delimiter. */
5971
STRNCPY(result, s0, n = s-s0);
5982
err_print(C, "get_arg: Field \"%s\" ended while scanning for '%c'\n",
5984
longjmp(top_of_get_arg, 4);
5988
@ Format a module number including section and page information. (The
5989
value returned from this function should be |FREE|d after use by calling
5997
mod_trans FCN((prefix,modno))
5998
outer_char *prefix C0("")@;
5999
sixteen_bits modno C1("")@;
6001
outer_char HUGE *mod_temp = GET_MEM("mod_temp", NMOD_TEMP, outer_char);
6002
SECT_INFO *ps = sect_info + modno;
6006
outer_char c, letter[2];
6010
c = (ps->subpage_flag ?
6011
XCHR(modno - ps->modno0 + @'a' - 1) : '\0');
6023
SPRINTF(NMOD_TEMP, mod_temp, `"%s %d (sect. %s, p. %d%s)",
6024
prefix, modno, ps->secno, ps->pageno, letter`);
6028
SPRINTF(NMOD_TEMP, mod_temp, `"%s %d", prefix, modno`);
6031
if(next_mod_name == last_mod_name)
6032
CONFUSION("mod_trans", "too many unfreed mod names");
6034
return *(next_mod_name++) = mod_temp;
6037
@ Undo storage areas allocated for |mod_trans|.
6044
return; // For errors happening during the command line.
6046
/* Don't mess with this next construction; tricky on some systems. The form
6047
|for(--next_mod_name; next_mod_name >= mod_names; next_mod_name--)| doesn't
6049
while(next_mod_name > mod_names)
6052
FREE(*next_mod_name);
6056
@*1 Indexing languages.
6057
Languages have both a number, name, and symbol; be
6058
careful to keep these all in the same order. We provide two functions:
6059
|lan_num| takes a language and returns an index number; |lan_enum| takes an
6060
index number and returns a language.
6065
lan_num FCN((Language))
6066
LANGUAGE Language C1("")@;
6072
case RATFOR: return 1;
6073
case FORTRAN: return 2;
6075
case LITERAL: return 4;
6077
case C_PLUS_PLUS: return 5;
6078
case RATFOR_90: return 6;
6079
case FORTRAN_90: return 7;
6081
case NO_LANGUAGE: Language = GLOBAL_LANGUAGE; goto proper_language;
6082
default: Language = global_language; goto proper_language;
6088
int num C1("Integer index of the language.")@;
6093
case 1: return RATFOR;
6094
case 2: return FORTRAN;
6096
case 4: return LITERAL;
6098
case 5: return C_PLUS_PLUS;
6099
case 6: return RATFOR_90;
6100
case 7: return FORTRAN_90;
6102
default: return NO_LANGUAGE;
6106
@ The function |lan_index| is like |lan_num|, but it compresses
6107
|RATFOR| and |FORTRAN| onto the same index.
6111
lan_index FCN((Language))
6112
LANGUAGE Language C1("")@;
6120
case FORTRAN: return 1;
6123
case LITERAL: return 3;
6125
case C_PLUS_PLUS: return 4;
6128
case FORTRAN_90: return 5;
6130
case NO_LANGUAGE: Language = GLOBAL_LANGUAGE; goto proper_language;
6131
default: Language = global_language; goto proper_language;
6135
@*1 Processing {\tt fweb.sty}.
6136
Here we construct the name of the style file, and read that file.
6140
IN_COMMON outer_char style_file_name[MAX_FILE_NAME_LENGTH]
6141
CSET(STYLE_FILE_NAME);
6142
IN_COMMON boolean renamed_style CSET(NO);
6145
@<Read the style file@>=
6147
read_sty(style_file_name,renamed_style); // See \.{style.web}.
6149
@<Convert selected fields to |ASCII|@>@;
6152
@ The style file is read as |outer_char|. Some fields are best left that
6153
way, while others need to be converted to |ASCII|.
6155
@<Convert selected...@>=
6157
DOT_DELIMITER HUGE *d = &wt_style.dot_delimiter;
6159
d->begin = XORD(d->begin);
6160
d->end = XORD(d->end);
6164
t_style.ASCII_fcn = x__to_ASCII((outer_char *)t_style.ASCII_fcn);
6168
@*1 Processing command-line arguments.
6169
Processing arguments is done in a
6170
separate function because it's called several times: once for the command
6171
line, and (possibly) each time there's a language change.
6173
@d THE_FILE_NAME (a_file_name ? pa : (outer_char HUGE *)"stdin")
6177
IN_COMMON boolean doing_cmd_line; // Command line or optional arguments?
6178
IN_COMMON boolean cmd_prms; // Stuff from ini file or from command line?
6184
do_args FCN((doing_cmd_line0,argc,argv,pfound_web,wbflnm,pfound_change))
6185
boolean doing_cmd_line0 C0("")@;
6187
outer_char HUGE *argv[] C0("")@;
6188
boolean HUGE *pfound_web C0("")@;
6189
outer_char wbflnm[] C0("")@;
6190
boolean HUGE *pfound_change C1("")@;
6192
boolean a_file_name; // Is an actual file name supplied from the command line?
6193
outer_char HUGE *dot_pos; /* Position of |'.'| (or more generally,
6194
|wt_style.ext_delimiter|) in the argument */
6195
IN_STYLE outer_char HUGE *sprm_ptr0, HUGE *sprm_ptr;
6197
doing_cmd_line = doing_cmd_line0;
6200
*pfound_web = *pfound_change = NO;
6204
if(!cmd_prms && argv == pn0)
6205
{ /* Note beginning of command-line parameters. */
6207
sprm_ptr0 = sprm_ptr;
6210
if(cmd_prms && argv == pn1)
6211
{ /* Note beginning of more \.{.fweb} arguments. */
6215
pa = pa_begin = *(argv++); // Beginning of current argument string.
6217
/* Arguments that don't begin with a hyphen, or that are just a single
6218
hyphen with no trailing stuff, mean file names. */
6219
if ( (a_file_name= BOOLEAN(*pa != '-')) || *(pa+1)=='\0')
6220
@<Process a file name@>@;
6222
@<Handle flag argument@>;
6227
@<Process a file name@>=
6233
STRCPY(wbflnm,THE_FILE_NAME);
6236
else if (!*pfound_change)
6237
@<Make |change_file_name| from |fname|@>@;
6239
@<Print change file warning message and |continue|@>;
6243
printf("\n! File name \"%s\" is not allowed as optional argument; \
6250
@<Make |change_file_name|...@>=
6252
if (a_file_name && STRLEN(pa) > MAX_FILE_NAME_LENGTH-5)
6255
/* For VMS, should skip over brackets here. */
6257
if (a_file_name && (dot_pos=OC(STRCHR(pa,wt_style.ext_delimiter)))==NULL
6260
SPRINTF(MAX_FILE_NAME_LENGTH,change_file_name,`"%s%cch",
6261
pa,wt_style.ext_delimiter`);
6265
SPRINTF(MAX_FILE_NAME_LENGTH,change_file_name,`"%s",THE_FILE_NAME`);
6271
@ After a language command, there may be optional arguments that must also
6274
@d ARG_BUFFER_SIZE 500
6275
@d COPY_TO_ARG_BUFFER(c) if(pa < arg_buffer_end) *pa++ = c;
6276
else OVERFLW("arg buffer bytes","")@;
6285
ASCII arg_buffer[ARG_BUFFER_SIZE],*pa = arg_buffer,
6286
*arg_buffer_end = arg_buffer + ARG_BUFFER_SIZE;
6288
outer_char HUGE *args[MAX_ARGS];
6290
ini_language(l); /* Set language and initialize flags. */
6295
{ /* Unbracketed command, as in \.{@@c++}. */
6296
*pa++ = @'-'; // Make it an honest command.
6297
*pa++ = l; // Don't forget the language.
6301
if(IS_WHITE(*loc) || *loc==@'[') break;
6302
if(*loc==@'|' && parsing_mode==INNER)
6304
ERR_PRINT(C,"WARNING: Code mode ended during \
6305
unbracketed optional argument. Should there be white space after \
6306
language command?");
6309
COPY_TO_ARG_BUFFER(*loc++);
6313
/* We allow bracketed commands to follow unbracketed ones. */
6316
COPY_TO_ARG_BUFFER(@' ');
6319
{ // Skip the opening bracket; copy contents.
6322
loc++; /* Skip the ending bracket. */
6325
COPY_TO_ARG_BUFFER(*loc++);
6329
TERMINATE(pa,0); /* Terminate |arg_buffer|. */
6330
argc = to_args(to_outer(arg_buffer),args,MAX_ARGS);
6331
do_args(NO,argc,args,&found_web,(outer_char *)NULL,&found_change);
6334
fin_language(); /* Do any further flag setup. */
6337
@ Set the language, and also set up flags for particular language.
6338
The input parameter is the letter corresponding to the basic language.
6339
Subsidiary dialect flags such as~|Cpp| or~|Fortran88| are used to determine
6344
ini_language FCN((l))
6345
ASCII l C1("Basic language code")@;
6347
/* First we set the language. */
6348
l = A_TO_LOWER(l); // We now enforce that language commands are lower case.
6352
@<|ASCII| cases for |C|@>:
6353
language = (Cpp ? C_PLUS_PLUS : C);
6356
@<|ASCII| cases for |RATFOR|@>:
6357
if(!RAT_OK("Invalid @@r command; language set to C")) language = C;
6358
else language = (Fortran88 ? RATFOR_90 : RATFOR);
6361
@<|ASCII| cases for |FORTRAN|@>:
6362
language = (Fortran88 ? FORTRAN_90 : FORTRAN);
6365
@<|ASCII| cases for |LITERAL|@>:
6369
@<|ASCII| cases for |TEX|@>:
6374
/* The following is for checking an override of the command-line language
6375
in the limbo section. */
6377
cmd_language = language;
6382
@ Set parameters for each language. Note that certain flags, such as
6383
|free_form_input|, are really arrays that can have a different value
6384
depending on the language.
6391
/* Set the output language. */
6392
out_language = OUT_LANGUAGE(language);
6394
/* Storing these numbers cuts down on the overhead of function calls. It
6395
also enables us to use the macros in the following |switch| properly. */
6396
language_index = (short)lan_index(language); // Maybe no longer used.
6397
language_num = (short)lan_num(language);
6399
index_flag = YES; // Most languages will cross-reference.
6408
Cpp = YES; // Just a shorthand for |language==CPP|.
6413
free_form_input = global_params.Free_form_input[language_num];
6414
/* This can be turned on by the \.{-n\\} or \.{-n\&} options. */
6415
auto_semi = global_params.Auto_semi[language_num];
6421
err_print(C, "SORRY, but Ratfor doesn't work yet with the N \
6423
auto_semi = global_params.Auto_semi[language_num];
6425
free_form_input = NO;
6442
@ Now handle any parameter modifications after optional arguments have been
6449
ini_reserved(language);
6472
frz_params(); // Make any local parameters.
6474
/* Output file name. For |program == weave|, |out_file == tex_file| by a
6475
macro definition in \.{typedefs.hweb}. */
6476
if(program == tangle)
6477
out_file = params.outp_file[lan_num(out_language)];
6480
@ Call this routine whenever |params| is reset.
6487
if(auto_semi && !Fortran88) free_form_input = NO;
6490
cont_char = XORD(in_escape); // Current continuation character (in |ASCII|).
6491
free_90 = BOOLEAN(Fortran88 && free_form_input);
6492
free_Fortran = BOOLEAN((language==FORTRAN_90) && free_90);
6494
if(!prn_semis && free_Fortran && auto_pseudo_semis)
6498
/* Semicolons must be inserted explicitly for \Fortran-90! */
6499
if(is_FORTRAN_(language))
6500
auto_semi = BOOLEAN(!free_Fortran);
6503
if(program==tangle && free_90)
6508
begin_comment_char[lan_num(FORTRAN_90)] =
6509
(outer_char)CHOICE(free_90, '!', 'C');
6517
@ At this point the effective command line is in |outer_char| form in
6522
to_args FCN((p,args,max_args))
6523
outer_char HUGE *p C0("")@;
6524
outer_char HUGE *args[] C0("")@;
6525
int max_args C1("")@;
6533
err_print(C,"Too many command-line or optional \
6534
arguments; must be < %d",max_args);
6537
while(isspace(*p)) p++; // Skip leading white space.
6538
args[n++] = p; // Record start and count.
6541
if(isspace(*p) || !*p) break; // Find end of argument.
6543
if(!*p) break; // Get out at end of line.
6544
TERMINATE(p,0); // Mark end of argument.
6547
return n; // Number of arguments found.
6551
@<Initialize static...@>=
6553
SET_VAL(mbuf_size,ABBREV(mbuf_size));
6555
@ These are for automatic statement numbering.
6557
@d STARTING_DO_NUM UL(90000L)
6561
IN_COMMON STMT_LBL max_stmt CSET(STARTING_DO_NUM);
6562
IN_COMMON int not; // For the negations of options.
6564
@ For many of the flags, a minus option, as \.{-flag}, means to do the
6565
opposite of the default. Furthermore, if the format is \.{--flag}, this is
6566
the opposite of \.{-flag}. Negating the usual flag value can be
6567
accomplished by exclusive-oring it with |not|, where |not == YES| when we
6568
have the extra hyphen, or |NO|~otherwise.
6570
@d NOT(flag) BOOLEAN((!flag) ^ not)
6575
outer_char cmd_symbol; // The character after the hyphen.
6578
pa++; // |pa| now points to the character after the hyphen.
6581
{ // Second hyphen means negate.
6583
pa++; // Skip second hyphen.
6589
switch(cmd_symbol= *pa++)
6590
{ /* |pa| now positioned after command symbol---e.g., to optional
6601
@<Option \.{-@@}: print control codes@>@;
6606
@<Option \.{-A}: translations to |ASCII|@>@;
6610
@<Option \.{-b}: block numbering@>@;
6617
@<|outer_char| cases for |C|@>:
6618
@<Option \.{-c}: C and \Cpp@>@;
6624
break; // This was already processed in |ini_program|.
6627
@<Option \.{-D}: see reserved words@>@;
6631
number_dos = NOT(NUMBER_DOS); // NOTE: falls through to next case!
6634
@<Option \.{-:}: starting line number@>@;
6638
try_extensions = NOT(TRY_EXTENSIONS); @~ break;
6641
@<Option \.{-E}: change extension delimiter@>@;
6645
subscript_fcns = NOT(SUBSCRIPT_FCNS); @~ break;
6648
compare_outfiles = NOT(COMPARE_OUTFILES); @~ break;
6651
dbg_output = NOT(DBG_OUTPUT); @~ break;
6654
@<Issue warning if this command is negated@>@;
6659
@<Option \.{-H}: scan include files@>@;
6663
@<Option \.{-i}: skip include files@>@;
6667
@<Option \.{-I}: add to include path list@>@;
6671
skip_ifiles = NOT(SKIP_IFILES); @~ break;
6674
lc_keywords = NOT(LC_KEYWORDS); @~ break;
6677
@<Option \.{-L}: generalized language switch@>@;
6681
@<Option \.{-l}: print input lines for debugging@>@;
6685
@<Option \.{-M}: set output message level@>@;
6689
@<Option \.{-m}: macro definitions etc.@>@;
6693
nuweb_mode = NOT(NUWEB_MODE);
6701
overload_ops = NOT(OVERLOAD_OPS);
6705
@<Option \.{-p}: collect style-file parameter@>@;
6709
@<Option \.{-P}: specify the \TeX\ processor@>@;
6713
free_form_input = NOT(FREE_FORM_INPUT);
6714
Ratfor77 = NOT(RATFOR77);
6717
@<|outer_char| cases for |RATFOR|@>:
6718
if(!RAT_OK("Language command ignored"))
6724
@<Option \.{-s}: statistics@>@;
6728
@<Option \.{-t}: truncate identifiers@>@;
6732
@<Option \.{-T}: various flags for \FTANGLE@>@;
6736
@<Option \.{-u}: undefine a macro@>@;
6740
lowercase_tokens = NOT(LOWERCASE_TOKENS);
6744
prn_version = NOT(PRN_VERSION);
6748
all_cmnts_verbatim = NOT(ALL_CMNTS_VERBATIM);
6753
new_fname(&fwebmac, pa, NULL);
6755
input_macros = NOT(INPUT_MACROS);
6760
@<Option \.{-W}: various flags for \FWEAVE@>@;
6764
flags3(BOOLEAN(not));
6770
makeindex = NOT(MAKEINDEX);
6774
flags3(BOOLEAN(!not));
6778
@<Option \.{-y}: process allocation command@>@;
6786
prn_style_defaults = BOOLEAN(!not);
6792
dot_constants = NOT(DOT_CONSTANTS);
6796
bslash_continued_strings = NOT(BSLASH_CONTINUED_STRINGS);
6800
chk_stmts = NOT(CHK_STMTS);
6804
translate_brackets = NOT(TRANSLATE_BRACKETS);
6809
chk_ifelse = NOT(CHK_IFELSE); @~break;
6813
@<Issue warning if this command is negated@>@;
6814
if(doing_cmd_line || phase==2)
6819
params.CPP_comments[LN(FORTRAN)]
6820
= params.CPP_comments[LN(FORTRAN_90)]
6821
= params.CPP_comments[LN(RATFOR)]
6822
= params.CPP_comments[LN(RATFOR_90)]
6823
= BOOLEAN(YES ^ not);
6827
params.Point_comments[LN(FORTRAN)]
6828
= params.Point_comments[LN(FORTRAN_90)]
6829
= params.Point_comments[LN(RATFOR)]
6830
= params.Point_comments[LN(RATFOR_90)]
6831
= BOOLEAN(YES ^ not);
6835
compound_assignments = NOT(COMPOUND_ASSIGNMENTS); @~ break;
6838
line_info = NOT(LINE_INFO); @~ break;
6845
/* Check for trailing garbage. */
6851
@*1 Option {\tt -@@}.
6853
@<Option \.{-@@...@>=
6857
at_codes = GET_MEM("at_codes", 200, ASCII); // 200 is KLUDGE!
6861
STRCAT(at_codes, to_ASCII(pa));
6865
@*1 Option {\tt -A}.
6866
ASCII translations are off by default on |ASCII|
6867
machines; they're turned on for the cases that really need. Therefore,
6868
this flag is for debugging.
6870
@<Option \.{-A}...@>=
6872
#if(TRANSLATE_ASCII || DEBUG_XCHR)
6873
translate_ASCII = YES; // Force it for the cases that really need it.
6875
translate_ASCII = NOT(TRANSLATE_ASCII0);
6880
@*1 Option {\tt -b}.
6882
@<Option \.{-b}...@>=
6884
params.Block_nums[LN(FORTRAN)]
6885
= params.Block_nums[LN(FORTRAN_90)]
6886
= params.Block_nums[LN(RATFOR)]
6887
= params.Block_nums[LN(RATFOR_90)]
6891
@*1 Option {\tt -c}.
6892
Options related to the C~language.
6894
@<Option \.{-c}...@>=
6896
@<Issue warning if this command is negated@>@;
6898
Cpp = BOOLEAN(*pa == '+'); /* The commands \.{-c+} or \.{-c++} turn on
6899
knowledge of \.{C++}. */
6902
pa++; // Skip the pluses.
6907
set_filter(Cpp ? C_PLUS_PLUS : C);
6911
ini_language(XORD(*LANGUAGE_CODE(C)));
6920
@*1 Option {\tt -D}.
6921
See reserved words. The form `\.{-D[ikr]word}' says to print only the
6922
intrinsic functions (`\.i'), and/or the \Fortran/\Ratfor\ keywords (`\.k'),
6923
and/or the reserved words (`\.r') beginning with `\.{word}'.
6925
Note that we can't immediately execute |see_reserved| because dynamic
6926
memory and the |name_dir| hasn't been set up yet.
6928
@<Option \.{-D}...@>=
6931
@<Get the optional arguments to `\.{-D}'@>@;
6934
if(!(*pa=='\0' || *pa=='*' || isalpha(*pa)))
6936
err_print_(C, "Either nothing, a letter, '*', or '[' \
6937
must follow `-D'; option ignored");
6941
rsrvd.reserveds = rsrvd.intrinsics = rsrvd.keywords = YES;
6944
rsrvd.args = pa; // Points to `\.{*}' or letters to match.
6950
@<Get the optional arguments to `\.{-D}'...@>=
6952
for(++pa; *pa != ']'; pa++)
6957
rsrvd.intrinsics = YES;
6962
rsrvd.keywords = YES;
6967
rsrvd.reserveds = YES;
6971
err_print_(C, "Missing ']' inserted after optional argument \
6976
err_print_(C, "Optional argument to `-D' must be one of \
6981
pa++; // Advance past the \.{']'}.
6984
@*1 Options {\tt -d} and {\tt -:}.
6985
Process starting line number.
6987
@<Option \.{-:}...@>=
6989
@<Issue warning if this command is negated@>@;
6991
max_stmt = STARTING_DO_NUM;
6994
{ // Optional starting line number specified.
6995
max_stmt = ATOL(pa);
6997
if(max_stmt > 99999L)
6998
err_print_(C,"Starting DO number must be < 99999; assuming %lu",
6999
max_stmt = STARTING_DO_NUM);
7004
@*1 Option {\tt -E}.
7005
Change the extension delimiter from its default value of period.
7007
@<Option \.{-E}...@>=
7009
@<Issue warning if this command is negated@>@;
7012
wt_style.ext_delimiter = *pa++;
7014
err_print_(C,"Missing character after -E option; option ignored");
7017
@*1 Option {\tt -h}.
7018
Enter the help package with \.{-h}. Just typing \.{-h} gives a brief
7019
message about possible helpful options. Typing `\.{-h?}' or `\.{-h.}'
7020
enters `\.{info FWEB}' at top level. Typing `\.{-h}\emph{menu-list}' calls
7021
`\.{info FWEB {\it menu-list}}'.
7030
printf("! For help, please use options\n\n\
7031
`-@@' (query control codes),\n\
7032
`-D' (query reserved words),\n\
7033
`-y' (query memory allocations),\n\
7034
`-Z' (query style-file parameters)\n\n\
7035
or see the info(1) man-page entry for FWEB."), fflush(stdout);
7039
@<Enter the \.{info} package@>@;
7040
#endif // |HAVE_INFO|
7042
usage("", NO); // In lieu of anything better.
7047
@<Enter the \.{info}...@>=
7049
outer_char temp[500], buf[500], *pmenus;
7052
pmenus = pa; // There were command-line arguments.
7054
{ /* No command-line arguments. Offer to enter \.{info}. */
7055
printf("\nTo enter `info FWEB' now, type\n\n\
7056
`?', for the top-level FWEB directory;\n\
7057
`node-name' or `-n node-name', to go to a specific node like `Macros'; or\n\
7058
a space-separated list of hierarchal FWEB menu items,\n\
7059
like `concepts structure'.\n\n\
7060
Type `q' to exit info. To abort now, hit Enter: "), fflush(stdout);
7066
if(STRLEN(pmenus) > 0)
7068
if(*pmenus == '?' || *pmenus == '.')
7071
sprintf((char *)temp, "info -f FWEB %s", pmenus);
7072
system((char *)temp);
7076
@*1 Option {\tt -H}.
7078
@<Option \.{-H}...@>=
7080
outer_char HUGE *p = pa;
7086
rmv_files = NOT(RMV_FILES); // For debugging.
7090
quoted_includes = NOT(QUOTED_INCLUDES);
7094
all_includes = NOT(ALL_INCLUDES);
7098
read_iformats = NOT(READ_IFORMATS);
7108
@*1 Option {\tt -i}.
7109
When an include file is referenced by `\.{@@I}', then `\.{-i}' says for
7110
\FWEAVE\ to not print the contents of the file. `\.{-i!}' means to not
7111
even read it (probably not useful). By default, index entries are not made
7112
for files that aren't printed; `\.{-ix}' says to index them anyway.
7114
@<Option \.{-i}...@>=
7119
index_hidden = NOT(INDEX_HIDDEN); // Falls through.
7120
toggle_includes = NOT(TOGGLE_INCLUDES);
7124
toggle_includes = NOT(TOGGLE_INCLUDES);
7128
skip_includes = NOT(SKIP_INCLUDES); @~ break;
7136
@*1 Option {\tt -I}.
7137
The \.{-I}~option normally specifies additional files to add to the search
7138
list for \FWEB\ include files. However, any \.{-I}~options that follow
7139
a~\.{-H} add to the search list for C~include files; this list is passed
7140
to~\.{gcc} as the \.{-I}~option.
7142
@<Option \.{-I}...@>=
7144
more_includes(read_iformats ? &hdr_incl : &incl, pa);
7145
// Add to include path list.
7148
@*1 Option {\tt -L}.
7149
Generalized language switch.
7151
@<Option \.{-L}...@>=
7155
@<|outer_char| cases for |C|@>:
7156
@<|outer_char| cases for |FORTRAN|@>:
7157
@<|outer_char| cases for |RATFOR|@>:
7160
@<|outer_char| cases for |LITERAL|@>:
7161
@<Issue warning if this command is negated@>@;
7165
set_filter(LITERAL);
7169
ini_language(XORD(*LANGUAGE_CODE(LITERAL)));
7173
bad_lcmd("argument",pa-2);
7178
@<|outer_char| cases for |TEX|@>:
7179
@<Issue warning...@>@;
7187
ini_language(XORD(*LANGUAGE_CODE(TEX)));
7191
bad_lcmd("argument",pa-2);
7197
bad_lcmd("command",pa-2);
7202
@*1 Option {\tt -l}.
7203
Print input lines for debugging; see |input_ln|.
7205
@<Option \.{-l}...@>=
7207
prn_input_lines = BOOLEAN(YES ^ not);
7210
if(isdigit(*pa) || *pa=='-')
7212
outer_char *colon_pos;
7214
start_line = ATOL(pa);
7216
if((colon_pos=OC(STRCHR(pa,':'))) == NULL)
7217
end_line = LONG_MAX;
7219
end_line = ATOL(colon_pos + 1);
7224
printf("! Option `-l' must be followed by integer.\n");
7228
/* Negative line numbers are undocumented; they're for the developer. */
7231
start_line = -start_line;
7232
prn_input_addresses = YES;
7236
@ Here's code for command-line options for \Fortran\ and \Ratfor. Note
7237
that not all options set the language.
7242
FR_args FCN((Language))
7243
LANGUAGE Language C1("")@;
7249
@<Issue warning if this command is negated@>@;
7252
{ // Options \.{-n7} or \.{-n9}.
7257
ini_language(XORD(*LANGUAGE_CODE(Language)));
7264
ini_language(XORD(*LANGUAGE_CODE(Language)));
7268
bad_loption(Language);
7274
lnum = lan_num(language); /* We need to set this so that we can access
7275
the language arrays properly. Note use of |language| (here and in other
7276
statements below) rather than the function argument |Language|; the latter
7277
is only either |FORTRAN| or |RATFOR|. */
7283
/* Set the continuation character; for \Fortran-90, turn on
7284
free-form syntax. */
7287
if(FORTRAN90_LIKE(language))
7288
params.Free_form_input[lnum] = NOT(FREE_FORTRAN);
7289
params.In_escape[lnum] = *(pa-1); // Continuation character.
7299
params.Auto_semi[lnum] = BOOLEAN(YES ^ not);
7300
params.Auto_pseudo_semis[lnum] = YES;
7304
params.Auto_semi[lnum] = BOOLEAN(YES ^ not);
7305
params.Auto_pseudo_semis[lnum] = NO;
7309
if(is_FORTRAN_(Language))
7310
Fortran_label = NOT(FORTRAN_LABEL);
7314
params.Point_comments[lnum] = BOOLEAN(YES ^ not);
7318
params.CPP_comments[lnum] = BOOLEAN(YES ^ not);
7322
params.Reverse_indices[lnum] = BOOLEAN(YES ^ not);
7327
params.Block_nums[lnum] = NOT(BLOCK_NUMS);
7331
params.Ignore_C = NOT(IGNORE_C);
7338
@<Option \.{-g}: set |goto| parameters@>@;
7342
suppress_cmds = BOOLEAN(YES ^ not);
7343
@<Copy abbreviation characters@>@;
7347
suppress_cmds = BOOLEAN(NO ^ not);
7348
@<Copy abbrev...@>@;
7352
if(is_FORTRAN_(Language))
7353
prn_semis = NOT(PRN_SEMIS);
7359
set_filter(language);
7363
/* No argument at all: Specify the language. Note that the other
7366
ini_language(XORD(*LANGUAGE_CODE(Language)));
7370
bad_loption(Language);
7379
@ After many commands are processed, there shouldn't be any further
7380
trailing text; print a message if there is.
7382
@d SPURIOUS_MSG "Spurious trailing text ignored in option"
7387
outer_char HUGE *pa1 C1("Current position")@;
7389
printf("%c%s `%s'.\n", beep(1), SPURIOUS_MSG, pa_begin);
7390
multi_chars(' ', STRLEN(SPURIOUS_MSG) + 2 + pa1 - pa_begin);
7395
@<Option \.{-g}...@>=
7397
if(is_RATFOR_(Language))
7399
if(!RAT_OK("'g' command ignored"))
7401
@<Scan the |goto| parameters@>;
7404
bad_loption(Language);
7407
@ Here we have some definitions for the parameters that control \Ratfor's
7408
|goto|. (See the user manual.)
7411
@d MIN_G_RATIO 0.0 // This value definitively turns off the computed |goto|.
7413
@d MARGINAL_CASES 5L
7414
@d MIN_MARGINAL_CASES 1
7416
@d MAX_SPREAD UL(128L)
7421
IN_COMMON double g_ratio CSET(G_RATIO);
7422
IN_COMMON unsigned short marginal_cases CSET(MARGINAL_CASES);
7423
IN_COMMON CASE_TYPE max_spread CSET(MAX_SPREAD);
7425
@ We define an error macro to help ensure that valid parameters are passed
7426
to the \.{-g}~option.
7428
@m CHK_G(var,VAR,type) if(var < MIN_##VAR)
7430
err_print_(C,$IFELSE(type,int,"Invalid 'g' option: %s must be >= %d",
7431
"Invalid 'g' option: %s must be >= %.1f"),#var,
7436
@<Scan the |goto|...@>=
7444
g_ratio = STRTOD(pa,&pa);
7445
CHK_G(g_ratio,G_RATIO,double);
7450
(unsigned short)STRTOL(pa,&pa,10);
7451
CHK_G(marginal_cases,MARGINAL_CASES,int);
7455
max_spread = STRTOL(pa,&pa,10);
7456
CHK_G(max_spread,MAX_SPREAD,int);
7459
err_print_(C,"Invalid 'g' option: \
7460
parameter type '%c'",*(pa-1));
7465
@*1 Option {\tt -k}.
7466
If there are no abbreviation characters, we assume
7467
that all are intended, so we insert an asterisk.
7472
STRCPY(abbrev_cmds,"*");
7474
STRNCPY(abbrev_cmds, pa, NUM_RATFOR_CMDS); // Not fully error-checked.
7478
@*1 Option {\tt -p}.
7479
Each use of this option collects its string argument
7480
as a separate line in a style-file buffer. That buffer is read before the
7483
@<Option \.{-p}...@>=
7485
IN_STYLE outer_char HUGE *sprm_buf,HUGE *sprm_ptr,HUGE *sprm_end;
7490
{ /* Allocate the style-parameter buffer. */
7491
sprm_ptr = sprm_buf = GET_MEM("sprm_buf",SPRM_LEN,outer_char);
7492
sprm_end = sprm_buf + SPRM_LEN;
7496
if(sprm_ptr + (n+2) >= sprm_end)
7497
{ /* Reallocate a larger buffer. */
7500
nused = PTR_DIFF(size_t, sprm_ptr, sprm_buf);
7502
nbuf = PTR_DIFF(size_t, sprm_end, sprm_buf) + SPRM_LEN;
7504
if( (sprm_buf = (outer_char HUGE *)REALLOC(sprm_buf,
7505
nbuf*sizeof(outer_char), old_size*sizeof(outer_char)))==NULL)
7506
OVERFLW("sprm_buf",0);
7508
sprm_ptr = sprm_buf + nused;
7509
sprm_end = sprm_ptr + nbuf;
7512
/* Add a line (terminated with space and newline). */
7513
STRCPY(sprm_ptr, pa);
7519
@*1 Option {\tt -P}.
7520
In a few cases it is useful to specify which processor,
7521
\TeX\ or \LaTeX, will be used to process the output from \FWEAVE. The
7522
default is~`\.{-P}', which is equivalent to~`\.{-PL}'.
7524
@<Option \.{-P}...@>=
7529
err_print_(C,"'%c' is invalid option for -X; must be either 'L' \
7530
(LaTeX) or 'T' (TeX). Assuming LaTeX", *pa); // Falls through to \LaTeX\ case.
7531
TeX_processor = LaTeX_p;
7538
TeX_processor = LaTeX_p;
7542
err_print_(C, "Warning: Plain TeX (`-PT') is no longer supported. \
7543
Please use LaTeX (`-PL')");
7544
TeX_processor = TeX_p;
7552
@*1 Option {\tt -M}.
7553
The \.{-M}~option sets the message level (|msg_level|) for output, as
7556
\setcounter{enumi}{-1}
7559
(|NOTHING|): Like |ERRORS_ONLY|, but without the welcoming version number.
7562
(|ERRORS_ONLY|): Errors only.
7565
(|WARNINGS|): Errors and warnings.
7568
(|SHORT_INFO|): Errors, warnings, and all regular information output
7569
except for major module numbers and line numbers.
7572
(|EVERYTHING|): Errors, warnings, and all information output.
7576
@<Option \.{-M}...@>=
7579
msg_level = EVERYTHING;
7582
msg_level = ATOI(pa++);
7584
if(msg_level < NOTHING || msg_level > EVERYTHING)
7586
printf("! Argument to `-M' must be %i--%i or empty (==%i).\n",
7587
NOTHING, EVERYTHING, EVERYTHING);
7589
msg_level = EVERYTHING;
7595
@*1 Option {\tt -m}.
7596
The \.{-m}~option takes care of several commands:
7597
\.{-m4}~says to recognize \.{m4}~commands; \.{-m;}~means automatically
7598
append as pseudo-semi to \WEB\ macro definitions; otherwise, it's a \WEB\
7601
@<Option \.{-m}...@>=
7606
/* Recognize \.{m4} definitions. */
7607
@<Issue warning...@>@;
7609
R_ext = "m4"; N_ext = "n4";
7616
/* Automatically append pseudo-semi to \.{WEB} macro definition. */
7617
auto_app_semi = NOT(AUTO_APP_SEMI);
7622
err_print_(C,"Missing id for 'm' option");
7627
goto undefine_macro;
7632
if(!(isalpha(mc) || mc=='_' || mc=='$'))
7633
err_print_(C,"Macro definition may not start with \
7634
'%c'; -m option ignored",mc);
7636
save_macro(OC("m"), (outer_char *)pa);
7637
// Macro definition (unprotected; can't use |SAVE_MACRO|).
7643
@ Build a macro definition in the macro buffer. This is used for
7644
predefining macros as well as defining from the command line. (This
7645
function modifies the text~|t|.)
7650
save_macro FCN((cmd,t))
7651
CONST outer_char HUGE *cmd C0("Name of the ``at'' command to put first.")@;
7652
outer_char HUGE *t C1("Text of the definition.")@;
7655
boolean replace_equals = BOOLEAN(STRCMP(cmd,"m")==0); /* For macros, if an
7656
equals sign appears before a space, it will be turned into a space. */
7658
if(program==weave) return;
7662
/* Place command such as \.{@@m} into buffer. */
7666
*mp++ = *cmd++; /* Copy text of command. */
7676
replace_equals = NO;
7678
else if(*t == ' ') replace_equals = NO;
7683
*mp = '@@'; @~ *(mp+1) = 'm';
7685
/* After we've built it, convert to internal notation. */
7690
@ Divert the input stream to be from a buffer.
7693
IN_COMMON ASCII HUGE *fbuffer, HUGE *flimit, HUGE *floc; /* Saved position
7700
divert FCN((buffer,end,stop))
7701
ASCII HUGE *buffer C0("Start of the buffer.")@;
7702
ASCII HUGE *end C0("End of the buffer.")@;
7703
boolean stop C1("Stop when end is reached?")@;
7705
stop_the_scan = stop;
7707
fbuffer = cur_buffer;
7711
cur_buffer = loc = buffer;
7717
@ Undivert back to reading from files.
7722
cur_buffer = fbuffer;
7730
@*1 Option {\tt -s}.
7731
Process statistics requests.
7733
@<Option \.{-s}...@>=
7735
statistics = NOT(STATISTICS);
7737
/* The format ``\.{-sm100}'' says to display memory allocations for sizes
7742
pa++; // |pa| positioned to byte size.
7743
show_mem = NOT(SHOW_MEM);
7744
if(isdigit(*pa)) show_size = ATOL(pa);
7748
printf("! Option \"%s\" is invalid.\n", (char *)(pa-2));
7753
@ On the smaller computers, it's helpful to know where we stand regarding
7754
available memory. The function |mem_avail| is invoked along with the other
7755
statistics turned on by the \.{-s}~command-line option.
7757
A global variable is used to capture the starting memory, so we don't print
7758
rediculously large maximum numbers with the statistics.
7762
IN_COMMON BUF_SIZE starting_memory CSET(ULONG_MAX);
7763
// Set at first call to |mem_avail|.
7769
mem_avail FCN((at_end))
7770
int at_end C1("0 for beginning, 1 for end")@;
7775
printf("\n Allocated maximum of %s bytes dynamically; \
7776
ended with %s bytes.\n",
7777
commas(max_mem), commas(total_mem));
7779
#if PRINT_AVAILABLE_MEMORY
7780
printf(" Available memory: %s bytes.\n", commas(CORE_LEFT));
7783
starting_memory = CORE_LEFT; // Used in |smin0|.
7789
SET_COLOR(ordinary);
7792
@ Here is a common routine to print the statistics for a particular kind of
7797
stat0 FCN((name,size,num,max_num,abs_max_num,abbrev,ctrl_chars))
7798
CONST outer_char *name C0("")@;
7799
size_t size C0("")@;
7800
BUF_SIZE num C0("")@;
7801
BUF_SIZE max_num C0("")@;
7802
BUF_SIZE abs_max_num C0("")@;
7803
CONST outer_char *abbrev C0("")@;
7804
CONST outer_char *ctrl_chars C1("")@;
7806
SET_COLOR(ordinary);
7807
printf(" %s ", (char *)name);
7809
SET_COLOR(character);
7810
printf("[\"%s\"]", (char *)abbrev);
7812
SET_COLOR(ordinary);
7813
printf(" x %u byte(s): %lu [%lu byte(s)] of %lu <= %s%s\n",
7814
(unsigned)size, num, num*size, max_num,
7815
commas(abs_max_num), (char *)ctrl_chars);
7819
@ A simple function to return the maximum ``reasonable'' memory.
7823
smin0 FCN((abs_max_num))
7824
BUF_SIZE abs_max_num C1("")@;
7826
return MIN(starting_memory,abs_max_num);
7831
Here are routines to insert commas in integers.
7836
init_buf FCN((p, num))
7840
p->s = GET_MEM("buf array", num, outer_char *);
7846
push_buf FCN((p, s))
7848
outer_char *s C1("")@;
7853
return p->s[p->n++] = s;
7865
@<Initialize static...@>=
7867
init_buf(&sput_buf, 10);
7868
init_buf(&c_buf, 10);
7876
unsigned long l C1("")@;
7878
outer_char temp[50], *p;
7879
outer_char temp1[100], *p1;
7894
sprintf((char *)temp, "%lu", l);
7910
return push_buf(&c_buf, temp1);
7914
@*1 Option {\tt -t}.
7915
Truncate identifiers.
7917
@<Option \.{-t}...@>=
7919
@<Issue warning if this command is negated@>@;
7923
@ Parse the argument of the truncate (`\.{-t}') command.
7939
l = (language==NO_LANGUAGE) ? GLOBAL_LANGUAGE : language;
7942
@<|outer_char| cases for |C|@>:
7943
l = lcase(C,'\0',C_PLUS_PLUS,'+',&pa);
7945
@<|outer_char| cases for |RATFOR|@>:
7946
l = lcase(RATFOR,'7',RATFOR_90,'9',&pa);
7948
@<|outer_char| cases for |FORTRAN|@>:
7949
l = lcase(FORTRAN,'7',FORTRAN_90,'9',&pa);
7951
@<|outer_char| cases for |LITERAL|@>:
7952
l = LITERAL; @~ break;
7953
@<|outer_char| cases for |TEX|@>:
7957
err_print_(C,"Invalid -t option. Example: `-tn=6{_}'");
7961
/* Skip over optional equals sign. */
7962
if(*pa == LANGUAGE_SEPARATOR)
7965
/* Set the maximum identifier length for language~|l|. */
7967
BOOLEAN(CHOICE((tr_max[lan_num(l)] = (unsigned short)ATOI(pa)) != 0,
7970
while(isdigit(*pa)) pa++; // Skip over the integer.
7977
@ Return a language based on the current argument character.
7981
lcase FCN((l0,c0,l1,c1,pp))
7982
LANGUAGE l0 C0("")@;
7983
outer_char c0 C0("")@;
7984
LANGUAGE l1 C0("")@;
7985
outer_char c1 C0("")@;
7986
outer_char HUGE * HUGE *pp C1("")@;
7990
while(**pp==c0) (*pp)++;
7996
while(**pp==c1) (*pp)++;
8003
@ Set list of characters to strip from identifiers. The language command
8004
`\.{-n\{abc\}}' sets the undesirable characters to `\.{abc}'.
8009
set_filter FCN((Language))
8010
LANGUAGE Language C1("")@;
8013
return; // In case there's no argument following the language cmd.
8018
filter_char[lan_num(Language)] = (char *)pa; /* Repoint to the string of
8019
characters following this command. */
8021
/* Remove the closing bracket from the list. */
8031
@*1 Option {\tt -T}.
8032
Various flags for \FTANGLE.
8034
@<Option \.{-T}: various flags for \FTANGLE@>=
8036
outer_char HUGE *p = pa;
8040
no_arg('T', OC("Dv%#"));
8048
deferred_macros = NOT(DEFERRED_MACROS);
8052
redefine_builtins = NOT(REDEFINE_BUILTINS);
8056
redefine_macros = NOT(REDEFINE_MACROS);
8060
top_version = NOT(TOP_VERSION);
8064
keep_trailing_comments = NOT(KEEP_TRAILING_COMMENTS);
8068
auto_line = NOT(AUTO_LINE);
8077
@*1 Option {\tt -u}.
8080
@<Option \.{-u}...@>=
8087
err_print_(C,"Missing identifier for `-u' option");
8089
save_macro(OC("#undef"),pa);
8093
@*1 Option {\tt -W}.
8094
Various flags for \FWEAVE.
8098
IN_COMMON outer_char *extra_args;
8099
// Extra arguments for preprocessor; used in conjunction with \.{-H}.
8100
IN_COMMON boolean mod_warning_flag CSET(NEVER_USED);
8105
@d MULTIPLE_USES 0b10
8106
@d ALL_WARNINGS (NEVER_USED | MULTIPLE_USES)
8108
@<Option \.{-W}: various flags for \FWEAVE@>=
8110
outer_char HUGE *p = pa;
8115
no_arg('W', OC("@@1[HdFflmvWw"));
8124
mod_warning_flag = ALL_WARNINGS;
8126
mod_warning_flag = ATOI(p++);
8130
index_one = NOT(INDEX_ONE);
8134
active_brackets = NOT(ACTIVE_BRACKETS);
8139
n_old = STRLEN(extra_args);
8143
extra_args = GET_MEM("extra_args", n+1, outer_char);
8144
STRCPY(extra_args, p);
8148
if((extra_args = (outer_char *)REALLOC(extra_args,
8149
(n_old + n + 1)*sizeof(outer_char),
8150
n_old*sizeof(outer_char))) == NULL)
8151
OVERFLW("extra_args", 0);
8153
STRCAT(extra_args, p);
8160
defn_mask.outer_macros = NOT(PRN_OUTER_MACROS);
8164
defn_mask.formats = NOT(PRN_fORMATS);
8168
defn_mask.Formats = NOT(PRN_FORMATS);
8172
defn_mask.limbo = NOT(PRN_LIMBO);
8176
defn_mask.macros = NOT(PRN_MACROS);
8180
defn_mask.v = NOT(PRN_V);
8185
defn_mask.w = NOT(PRN_W);
8194
@*1 Option {\tt -x}.
8195
The \.{-x}~option has the format `\.{-x\It{[*cimu]}}',
8196
which controls the printing of the table of contents~(`\.{c}'),
8197
index~(`\.{i}'), module list~(`\.{m}'), and unnamed
8198
cross-references~(`\.{u}'). `\.{*}"~means print nothing.
8199
`\.{-xi}'~means don't print the index, but print everything else. (More
8200
precisely, `\.{*}'~is translated into the list~`\.{cim}'. Each letter
8201
negates the default action associated with it.)
8207
boolean not C1("")@;
8209
prn_contents = BOOLEAN(PRN_CONTENTS ^ not);
8210
prn_index = BOOLEAN(PRN_INDEX ^ not);
8211
prn_modules = BOOLEAN(PRN_MODULES ^ not);
8212
xref_unnamed = BOOLEAN(XREF_UNNAMED ^ not);
8215
{ /* No argument. Assume nothing is to be printed. */
8225
prn_contents = NOT(PRN_CONTENTS);
8226
prn_index = NOT(PRN_INDEX);
8227
prn_modules = NOT(PRN_MODULES);
8231
prn_contents = NOT(PRN_CONTENTS);
8235
prn_index = NOT(PRN_INDEX);
8239
prn_modules = NOT(PRN_MODULES);
8243
xref_unnamed = NOT(XREF_UNNAMED);
8247
err_print_(C,"'%c' is invalid argument for `-x' or `-X'; \
8248
must be element of set {*cim}",*pa);
8252
no_xref = BOOLEAN(!(prn_index || prn_modules));
8255
@*1 Option {\tt -y}.
8256
The `\.{-y}~command has two forms: a query mode,
8257
``\.{-y$aa$?}'', or the setting mode, ``\.{-y$aa$\It{nnnnn}}''. To query
8258
all options, say ``\.{-y?}''
8260
@<Option \.{-y}...@>=
8262
outer_char abbrev[80],HUGE *a;
8264
MEM HUGE *m,HUGE *mmin = NULL, HUGE *mmax = NULL;
8265
boolean query_mode = NO;
8268
@<Issue warning if this command is negated@>@;
8271
{ /* Query everything. */
8273
mmax = mmin + (sizeof_mem/sizeof(MEM) - 1);
8275
CLR_PRINTF(ALWAYS, info, ("Default memory allocation parameters:"));
8278
{ /* Abbreviation specified. */
8279
abbrev[0] = abbrev[1] = '\0'; /* Ensure the abbreviation gets
8280
terminated properly. */
8282
for(a=abbrev; isalpha(*pa); )
8285
TERMINATE(abbrev,2); // Max of two character abbreviation.
8288
{ /* Assume it's a question. */
8289
mmin = msearch(abbrev,0L);
8296
for(m=mmin; m<mmax; m++)
8298
SET_COLOR(character);
8299
printf(" \"%-s\"%s",
8300
(char *)m->abbrev, m->abbrev[1] ? "" : " ");
8302
SET_COLOR(ordinary);
8303
printf("x %d byte(s):\
8304
min = %s, cur = %s [%s byte(s)], max = %s.\n",
8308
commas((m->nunits)*(m->bytes)),
8312
{ /* Change setting. */
8314
msearch(abbrev,nunits); // Set the value.
8320
@*1 Option {\tt -z}.
8321
Set name of style file.
8325
@<Issue warning if this command is negated@>@;
8327
if(STRLEN(pa) < MAX_FILE_NAME_LENGTH)
8329
if(STRCMP(pa,".") == 0)
8330
{ /* Read default file in current directory. */
8331
if(NSPRINTF(style_file_name,
8332
".%c%s",PREFIX_END_CHAR,STYLE_FILE_NAME) >=
8333
MAX_FILE_NAME_LENGTH) OVERFLW("./style_file_name","");
8336
STRCPY(style_file_name,pa);
8338
renamed_style = YES;
8341
err_print_(C,"Style file name `%s' is too long; must be less \
8342
than %d characters",pa, MAX_FILE_NAME_LENGTH);
8346
@*1 Options {\tt ->} and {\tt -=}.
8347
Here we determine a new output file
8348
name, with the command~\.{->}. If the argument is empty, everything goes to
8349
|stdout|. Otherwise, the command must have the format \.{->l=name.ext}. If
8350
the language~\.l is empty, all output is redirected to \.{name.ext}. If
8351
the name field is~'\.*', the name of the web file is used, but with the new
8352
extension. Otherwise, both the new name and the new extension are used.
8354
@d LANGUAGE_SEPARATOR '=' // Separates language from file name.
8359
redirect_output(VOID)
8364
{ /* Empty argument; send stuff to |stdout|. */
8365
new_fname(&tex_fname,OC("stdout"),NULL);
8367
for(k=0; k<NUM_LANGUAGES; k++)
8368
new_fname(params.outp_nm+k,OC("stdout"),NULL);
8370
else if(phase==2 && program==weave)
8374
outer_char c,HUGE *pname;
8375
outer_char HUGE *q = (outer_char HUGE *)STRCHR(pa,LANGUAGE_SEPARATOR);
8379
{ /* ``\.{->=new\_name}'' */
8380
c = LANGUAGE_SEPARATOR;
8381
pname = q + 1; // Point to file name.
8384
{ /* ``\.{->c=new\_name}'' */
8385
c = *pa++; /* Language symbol. */
8386
pname = q; // Point to \.=.
8389
{ /* ``\.{->new\_name}'' */
8390
c = LANGUAGE_SEPARATOR;
8391
pname = pa; // Point to file name.
8396
@<|outer_char| cases for |C|@>:
8397
set_fname(lcase(C,'\0',C_PLUS_PLUS,'+',&pa),pname,YES);
8400
@<|outer_char| cases for |RATFOR|@>:
8401
if(!RAT_OK("Redirection ignored")) break;
8403
set_fname(lcase(RATFOR,'7',RATFOR_90,'9',&pa),pname,YES);
8406
@<|outer_char| cases for |FORTRAN|@>:
8407
set_fname(lcase(FORTRAN,'7',FORTRAN_90,'9',&pa),pname,YES);
8410
@<|outer_char| cases for |LITERAL|@>:
8411
set_fname(LITERAL,pname,YES);
8414
@<|outer_char| cases for |TEX|@>:
8415
set_fname(TEX,pname,YES);
8418
case LANGUAGE_SEPARATOR:
8419
#if 0 /* Don't want to do this; it screws up module labeling for \FWEAVE. */
8420
new_fname(&tex_fname,pname,NULL);
8423
for(k=0; k<NUM_LANGUAGES; k++)
8424
new_fname(params.outp_nm+k,pname,NULL);
8429
err_print_(C,"Syntax error in output redirection \
8430
command \"->\". Language must be one of 'c', 'r', 'n', `m', or `x', not '%c'",
8437
@ This function sets an alternative output file name. If the |check| field
8438
is |YES|, we check to see that the string has the format ``\.{=stuff}''.
8443
set_fname FCN((l,s,check))
8444
LANGUAGE l C0("Language")@;
8445
outer_char HUGE *s C0("Remainder of command-line argument to parse")@;
8446
boolean check C1("Do we check for beginning colon?")@;
8449
err_print_(C,"Can't redirect FWEAVE's TeX output by individual \
8450
languages. Use \"->\" or \"-%cname.ext\"",LANGUAGE_SEPARATOR);
8453
if(*s++ != LANGUAGE_SEPARATOR)
8455
err_print_(C,"Expected '%c' after language in \"->\"; \
8456
command ignored",LANGUAGE_SEPARATOR);
8460
/* If the name begins with a '\.\#', it's filled in later with |wbflnm0|. */
8461
new_fname(params.outp_nm+lan_num(l),*s ? s : (outer_char HUGE *)"stdout",NULL);
8466
Some of the commands aren't naturally negatable; issue a warning.
8468
@<Issue warning...@>=
8475
cant_negate FCN((s))
8476
outer_char HUGE *s C1("")@;
8480
printf("! Can't negate option \"%s\"'; ignoring extra hyphen.\n",
8490
bad_lcmd FCN((type,pa0))
8491
CONST char *type C0("")@;
8492
CONST outer_char HUGE *pa0 C1("")@;
8494
err_print_(C,"Invalid language %s \"%s\"",type,pa0);
8501
bad_loption FCN((Language))
8502
LANGUAGE Language C1("")@;
8504
printf("! Invalid language option \"%s\" for %s.\n",
8505
(char *)(pa-3), is_FORTRAN_(Language) ? "Fortran" : "Ratfor");
8513
no_arg FCN((option, letters))
8514
outer_char option C0("Letter of option")@;
8515
CONST outer_char *letters C1("Possibilities for args")@;
8517
err_print_(C, "Missing argument for `-%c' option; \
8518
should be one or more letters in set {%s}", option, letters);
8525
usage FCN((msg,is_fatal))
8526
CONST char *msg C0("")@;
8527
boolean is_fatal C1("Fatal?")@;
8531
char c = wt_style.ext_delimiter;
8535
"!! Usage: %s [-option...] webfile[%cweb] \
8536
[changefile[%cch]]\n",
8537
program==tangle ? "ftangle" : "fweave",
8545
@<Print change file warning...@>=
8547
printf("! Warning: Ignoring multiple change file \"%s\".\n",
8548
(char *)THE_FILE_NAME);
8560
"!! Filename too long: ",
8562
(CONST outer_char *)pa);
8571
printf("! (%s): Unknown option `%s' ignored.\n",
8572
cmd_prms ? "Command line" : ".fweb", (char *)pa_begin);
8576
@ If a language was set on the command line, we issue a warning if the
8577
limbo section overrode that.
8581
chk_override FCN((language0))
8582
LANGUAGE language0 C1("")@;
8584
if(language != language0 && cmd_language != NO_LANGUAGE && cmd_language !=
8586
err_print(C,"WARNING: Command-line language %s overridden in limbo by %s",
8587
languages[lan_num(cmd_language)],languages[lan_num(language)]);
8591
Here is the code that opens the output file:
8592
@^system dependencies@>
8594
@<Scan arguments and open output files@>=
8596
@<Allocate initial file names@>@;
8599
/* If there's no \WEB\ file, it must be an info option. */
8602
ini_input_prms(WEB_FILE,(outer_char HUGE *)"",ABORT_ON_ERROR);
8603
// Opens both input and change file.
8604
@<Remember the |web_file_name| and |change_file_name|@>;
8606
if(program == weave)
8610
/* In case no explicit language commands are given, either on the command
8611
line or in the file, before encountering the first module, pick a default. */
8612
if(language == NO_LANGUAGE)
8613
ini_language(XORD(*LANGUAGE_CODE(GLOBAL_LANGUAGE)));
8615
global_params = params; // Freeze the params after arguments have been read.
8618
@ Initially, all output file names are empty.
8619
@<Allocate initial file names@>=
8623
/* We initialize the \It{local} name list; it's set to global later after
8624
everything else is set. */
8625
new_fname(&tex_fname,NULL,NULL);
8626
new_fname(&fwebmac,NULL,NULL);
8628
for(k=0; k<NUM_LANGUAGES; k++)
8629
new_fname(params.outp_nm+k,NULL,NULL);
8632
@ Make a new file name.
8634
@d DFLT_FNAME_LENGTH 20
8639
new_fname FCN((pname,name,ext))
8640
outer_char HUGE * HUGE *pname C0("")@;
8641
outer_char *name C0("")@;
8642
outer_char *ext C1("")@;
8644
outer_char temp[MAX_FILE_NAME_LENGTH];
8646
/* An empty |name| means allocate some space. */
8648
*pname = GET_MEM("pname",DFLT_FNAME_LENGTH+1,outer_char);
8650
{ /* Otherwise, we possibly allocate the space and construct the
8652
char ext_delimiter[2];
8653
FILE *temp_file_ptr;
8655
ext_delimiter[0] = (char)wt_style.ext_delimiter;
8656
ext_delimiter[1] = '\0';
8658
SPRINTF(MAX_FILE_NAME_LENGTH,temp,`"%s%s%s",name,ext ?
8660
ext ? ext : (outer_char *)""`);
8662
/* Previously we used to reallocate the file name here. However, that
8663
leads to an egregious error, since if the block is actually moved the old
8664
pointers are still around on the stack and in various other hiding places.
8665
Now, we just allocate a new area if the name is new. */
8666
was_opened(temp,0,pname,&temp_file_ptr);
8671
@<Remember the |web_file_name| and |change_file_name|@>=
8673
outer_char temp1[100],temp2[1000],
8674
full_web_name[ESCAPE_LENGTH],esc_full_web_name[ESCAPE_LENGTH],
8675
full_change_name[ESCAPE_LENGTH],esc_full_change_name[ESCAPE_LENGTH];
8681
n1 = program==tangle ? NSPRINTF(temp1,
8682
"\n RUN TIME: \"%s, %s at %s.\"",
8683
the_day(t),the_cdate(t),the_time(t))
8684
: NSPRINTF(temp1,"");
8687
fgetname(web_file,full_web_name);
8688
fgetname(change_file,full_change_name);
8690
STRCPY(full_web_name,web_file_name);
8691
STRCPY(full_change_name,change_file_name);
8694
esc_file_name(esc_full_web_name,ESCAPE_LENGTH,full_web_name);
8695
esc_file_name(esc_full_change_name,ESCAPE_LENGTH,full_change_name);
8697
n2 = NSPRINTF(temp2,
8699
"\n WEB FILE: \"%s\"\n CHANGE FILE: %c%s%c" :
8700
" {\"%s\"} {%c%s%c} ",
8702
*esc_full_change_name ? '"' : '(',
8703
*esc_full_change_name ? esc_full_change_name : OC("none"),
8704
*esc_full_change_name ? '"' : ')');
8706
old_size = STRLEN(cmd_ln_buf) + 1;
8707
cmd_ln_buf = (outer_char HUGE *)REALLOC(cmd_ln_buf,old_size+n1+n2, old_size);
8708
STRCAT(cmd_ln_buf,temp1);
8709
STRCAT(cmd_ln_buf,temp2);
8712
@ For some system such as DOS, the file names may have special characters
8713
in them that need to be escaped:
8717
esc_file_name FCN((esc_name,max_length,name))
8718
outer_char *esc_name C0("Put answer into here")@;
8719
unsigned max_length C0("Maximum length of |esc_name|.")@;
8720
outer_char *name C1("Put escapes into this name")@;
8725
max_length--; /* Allow room for the final null. */
8727
for(pt=esc_name,pn=name,n=0; *pn; )
8732
case '\\': case '$': case '#': case '%':
8733
case '^': case '~': case '&': case '_':
8752
@ The |update_terminal| macro is used when we want
8753
to make sure that everything we have output to the terminal so far has
8754
actually left the computer's internal buffers and been sent.
8755
@^system dependencies@>
8757
@d UPDATE_TERMINAL fflush(stdout) // Empty the terminal output buffer.
8759
@ For single-character output we use |putchar| (to the terminal) and |putc|
8760
(to a file). When we have to translate from \.{WEB}'s code into the
8761
external character code, we must say things like |putchar(xchr[c])|. When
8762
we just want to print strings, we use |printf|.
8763
@^system dependencies@>
8765
@d new_line putchar('\n')
8771
ASCII_file_write FCN((file_ptr,p0,n))
8772
FILE *file_ptr C0("Standard file pointer.")@;
8773
CONST ASCII HUGE *p0 C0("Start of the buffer.")@;
8774
size_t n C1("Number of characters to write.")@;
8776
CONST ASCII HUGE *p, HUGE *p1 = p0+n;
8777
ASCII atemp[1000],HUGE *a,HUGE *a1;
8783
/* Translate any special characters into something readable. */
8784
for(p=p0,a1=atemp; p<p1;a1++,p++)
8791
case MACRO_ARGUMENT:
8794
*a1++ = (ASCII)(@'0' + *(++p));
8804
n = PTR_DIFF(size_t, a1, atemp);
8805
// Might be bigger than incoming~|n| because of macro arg.
8808
{ /* Convert to the outer world. */
8809
outer_char temp[1000];
8812
for(a=atemp,t=temp; a<a1;)
8815
n_out = FWRITE(temp,n,file_ptr);
8818
n_out = FWRITE(atemp,n,file_ptr);
8821
@<Diagnose a write error and give up@>@;
8829
"nbytes = %u, %u items written\n%s",
8830
(unsigned)n, (unsigned)n_out, strerror(errno));
8833
@ The error routine |strerror| isn't available on all systems.
8839
strerror FCN((errnum))
8842
static char temp[50];
8844
sprintf(temp, "(strerror() is not available; errno = %d).", errnum);
8848
#endif // |!HAVE_STRERROR|
8850
@ The |args| array used by the macro processor must be allocated after
8851
the command line is parsed, because |max_margs| might have been changed by
8854
@<Allocate dyn...@>=
8856
if(program == tangle)
8858
ALLOC(sixteen_bits, args, ABBREV(max_margs), max_margs, 1);
8859
max_margs++; // So the user thinks this is really the max \#.
8863
@* DATE, TIME, and TIMING.
8864
Here are declarations for time and date. We use
8865
many of the ANSI routines. (However, some of the following could be
8866
simplified if the compiler were fully ANSI. Timing seems to be one of the
8867
areas that's most difficult to get right on the various machines.)
8871
IN_COMMON CONST char *day[]
8872
#if(part == 0 || part == 1)
8873
= {"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"}
8874
#endif // |part == 1|
8877
IN_COMMON CONST char *month[]
8878
#if(part == 0 || part == 1)
8879
= {"January","February","March","April","May","June","July",
8880
"August","September","October","November","December"}
8881
#endif // |part == 1|
8884
@ Fill the |tm| structure and return a pointer.
8891
time_t time PROTO((time_t *tp));
8894
return localtime(&the_tm);
8897
@ Return strings containing the time and/or date.
8902
CONST struct tm *t C1("")@;
8904
return OC(day[t->tm_wday]);
8911
CONST struct tm *t C1("")@;
8913
static outer_char date_buf[NDATE];
8915
if(NSPRINTF(date_buf,"%s %d, %d",
8916
month[t->tm_mon],t->tm_mday,1900+t->tm_year) >= NDATE)
8918
return (CONST outer_char *)date_buf;
8926
CONST struct tm *t C1("")@;
8928
static outer_char time_buf[NTIME];
8930
if(NSPRINTF(time_buf,"%d:%02d",t->tm_hour,t->tm_min) >= NTIME)
8933
return (CONST outer_char *)time_buf;
8937
@ Timing routines. In particular, sometimes we want timing more precise
8938
than seconds, but the standard ANSI |difftime| doesn't always do that.
8939
Thus, we sometimes supply our own.
8947
IN_COMMON clock_t clock0;
8948
IN_COMMON TIME_T time0;
8951
double diff_time FCN((t1,t0))
8955
#if HAVE_GETTIMEOFDAY
8956
return (double)(t1.tv_sec - t0.tv_sec)
8957
+ 1.0e-6*(double)(t1.tv_usec - t0.tv_usec);
8959
#if HAVE_SYS_TIMEB_H
8960
return (double)t1.time - (double)t0.time
8961
+ 1.0e-3*((double)t1.millitm - (double)t0.millitm); /*
8962
Without the casts, this line didn't work right on the DECstation. I think
8963
it has to do with the |unsigned short| type of |millitm|; a compiler bug
8964
with type promotion. */
8967
#endif // |HAVE_SYS_TIMEB_H|
8970
#endif // |NEW_DIFFTIME|
8974
@ This function is called at the beginning of both \FTANGLE\ and \FWEAVE.
8988
@ This one's called at the end.
8990
@d TIMING_PREC 10 /* Check that |cpu <= wall| by multiplying seconds by
8991
this number, then truncating. */
9002
char less_than = NO;
9003
char format[100]; /* We build the format for the time output here. */
9005
cpu = (clock()-clock0)/(double)(CLOCKS_PER_SEC);
9008
/* Because the real time is in units of seconds, we might get~0, so we'll
9009
be fancy and print an inequality in that case. */
9010
if( (wall = DIFFTIME(time1,time0)) == 0.0)
9018
/* --- CPU time --- */
9019
sprintf(format, "\nCPU = %%.%df sec.", TIMING_WIDTH);
9020
printf(format, cpu);
9021
if(cpu >= 60.0) printf(" (%.2f min.)",cpu/60.0);
9023
/* --- WALL CLOCK time --- */
9024
sprintf(format,"; REAL %%s %%.%df sec.",TIMING_WIDTH);
9025
printf(format,less_than ? "<=" : "=",wall);
9026
if(wall >= 60.0) printf(" (%.2f min.).",wall/60.0);
9028
/* --- PERCENTAGE CPU UTILIZATION --- */
9029
printf(" CPU/REAL = %.1f%%.",100.0*(cpu/wall));
9031
/* Debugging message: Check for compatibility. Because the timing routines
9032
may not be perfectly compatible through all decimal places, we check only
9033
that |cpu <= wall| to an accuracy of tenths of seconds. */
9035
if((unsigned long)(TIMING_PREC*cpu) > (unsigned long)(TIMING_PREC*wall))
9036
printf("! CPU (%.3f s.) > REAL (%.3f s.). \
9037
Check for invalid CLOCKS_PER_SEC (%.3e) or type of clock().\n",
9038
cpu,wall,(double)CLOCKS_PER_SEC);
9041
SET_COLOR(ordinary);
9044
#endif /* |TIMING| */
9046
@* MISCELLANEOUS FUNCTIONS.
9047
Here are various routines that didn't fit anywhere else.
9049
@*1 Initializing |program|.
9054
ini_program FCN((program0))
9055
PROGRAM program0 C1("")@;
9057
program = program0; // Set global flag.
9059
the_system = OC(THE_SYSTEM); /* |THE_SYSTEM| is defined in \.{os.hweb}. */
9060
local_banner = OC(LOCAL_BANNER); /* See \.{os.hweb} and \.{custom.web}. */
9062
ini_style(); // Default style file initializations. Sets up initial color.
9065
@*1 Printing the banner line.
9072
if(msg_level == NOTHING && !prn_version)
9075
CLR_PRINTF(ALWAYS, info, ("This is "));
9076
SET_COLOR(program_name);
9077
printf("%s", DEBUG_XCHR ? "ScRaMbLeD " : "");
9080
CLR_PRINTF(ALWAYS, program_name,
9081
("%s",program==tangle ? "FTANGLE" : "FWEAVE"));
9084
printf("%s", program==tangle ? "FTANGLE" : "FWEAVE");
9088
printf(" [%s version %s (%s)%s%s]. %s\n",
9089
(char *)the_system, (char *)version, (char *)release_date,
9090
color_mode ? "; TERM=" : "",
9091
color_mode ? (char *)termcap : "",
9092
(char *)local_banner);
9094
SET_COLOR(ordinary);
9097
@*1 Announcing current file.
9101
reading FCN((in_name,emit_newline))
9102
CONST outer_char *in_name C0("")@;
9103
boolean emit_newline C1("")@;
9105
if(msg_level < SHORT_INFO)
9108
CLR_PRINTF(SHORT_INFO, info, ("Reading")); @~ putchar(' ');
9109
CLR_PRINTF(SHORT_INFO, in_file, ("%s", (char *)in_name));
9110
CLR_PRINTF(SHORT_INFO, info, ("..."));
9120
writing FCN((print_write,out_name))
9121
boolean print_write C0("")@;
9122
CONST outer_char *out_name C1("")@;
9124
if(msg_level < SHORT_INFO)
9129
CLR_PRINTF(SHORT_INFO, info, ("\nWriting"));
9133
CLR_PRINTF(SHORT_INFO, out_file, ("%s", (char *)out_name));
9134
CLR_PRINTF(SHORT_INFO, info, ("..."));
9138
@* Printing the progress report for each starred module.
9144
if(msg_level < EVERYTHING)
9147
if (loc[-1] == @'*')
9148
{ /* Found a starred section. */
9157
CLR_PRINTF(EVERYTHING, module_num, ("*%d",module_count));
9164
The ANSI |sprintf| returns the number of characters
9165
written; however, not all libraries are ANSI. The following function
9166
|nsprintf| forces the issue.
9171
nsprintf FCN(VA_ALIST((s,fmt,n VA_ARGS)))
9173
outer_char *s C0("")@;
9174
CONST outer_char *fmt C0("")@;
9179
#if(NUM_VA_ARGS == 1)
9181
CONST outer_char *fmt;
9185
VA_START(arg_ptr,n);
9187
#if(NUM_VA_ARGS == 1)
9188
s = va_arg(arg_ptr,outer_char *);
9189
fmt = va_arg(arg_ptr,outer_char *);
9190
va_arg(arg_ptr,int);
9197
vsprintf((char *)s,(CONST char *)fmt,arg_ptr);
9200
num_char = (int)STRLEN(s);
9208
@*1 {\bf x\_keyword}.
9209
Expand the text of an RCS-like keyword into a buffer, without |stringg|
9210
delimiters. This is called both from \.{macs}:|i_keyword_| (output phase)
9211
as well as directly from \FTANGLE\ during the input phase (when \.{\$LKWD})
9214
@d BCHECK(n, reason)
9215
if(mp + (n) > buf_end)
9216
OVERFLW("Keyword buffer", "")@;
9218
@d CUR_QUOTE ((eight_bits)(single_quote || (!double_quote && R77_or_F) ?
9223
IN_COMMON boolean single_quote CSET(NO), double_quote CSET(NO);
9229
x_keyword FCN((pmp, buf_end, p, p1, in_quotes, out_quotes, input_level))
9230
eight_bits HUGE * HUGE *pmp C0("")@;
9231
eight_bits HUGE *buf_end C0("")@;
9232
eight_bits HUGE *p C0("")@;
9233
eight_bits HUGE *p1 C0("")@;
9234
boolean in_quotes C0("")@;
9235
boolean out_quotes C0("")@;
9236
int input_level C1("")@;
9238
eight_bits HUGE *mp = *pmp;
9240
// Dynamic array of RCS-like keywords.
9241
ASCII temp[256]; // Should make this variable.
9243
eight_bits quote_char;
9246
len = p1 - p - 2*in_quotes; // The 2 takes care of quote characters.
9251
quote_char = CUR_QUOTE;
9255
BCHECK(1, "quote0");
9259
STRNCPY(temp, p, len);
9264
for(prcs=prms[input_level].rcs_list.start;
9265
prcs && prcs->keyword; prcs=prcs->next)
9266
if(STRCMP(temp, prcs->keyword) == 0)
9268
len = STRLEN(prcs->txt);
9269
BCHECK(len, "keyword");
9270
STRCPY(mp, prcs->txt);
9277
err_print(C, "Unrecognized %s RCS-like keyword `%s'",
9278
input_level == WEB_FILE ? "global" : "local",
9283
BCHECK(1, "quote1");
9287
*pmp = mp; // Update the buffer pointer in the caller.