summaryrefslogtreecommitdiff
path: root/Build/source/texk/web2c/cwebdir/ctangle.w
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/texk/web2c/cwebdir/ctangle.w')
-rw-r--r--Build/source/texk/web2c/cwebdir/ctangle.w1543
1 files changed, 1543 insertions, 0 deletions
diff --git a/Build/source/texk/web2c/cwebdir/ctangle.w b/Build/source/texk/web2c/cwebdir/ctangle.w
new file mode 100644
index 00000000000..e7e2d311fe5
--- /dev/null
+++ b/Build/source/texk/web2c/cwebdir/ctangle.w
@@ -0,0 +1,1543 @@
+% This file is part of CWEB.
+% This program by Silvio Levy and Donald E. Knuth
+% is based on a program by Knuth.
+% It is distributed WITHOUT ANY WARRANTY, express or implied.
+% Version 4.2 --- February 2021
+
+% Copyright (C) 1987,1990,1993,2000 Silvio Levy and Donald E. Knuth
+
+% Permission is granted to make and distribute verbatim copies of this
+% document provided that the copyright notice and this permission notice
+% are preserved on all copies.
+
+% Permission is granted to copy and distribute modified versions of this
+% document under the conditions for verbatim copying, provided that the
+% entire resulting derived work is given a different name and distributed
+% under the terms of a permission notice identical to this one.
+
+% Amendments to 'ctangle.w' resulting in this updated version were created
+% by numerous collaborators over the course of many years.
+
+% Please send comments, suggestions, etc. to tex-k@@tug.org.
+
+% Here is TeX material that gets inserted after \input cwebmac
+\def\hang{\hangindent 3em\indent\ignorespaces}
+\def\pb{$\.|\ldots\.|$} % C brackets (|...|)
+\def\v{\char'174} % vertical (|) in typewriter font
+\mathchardef\RA="3221 % right arrow
+\mathchardef\BA="3224 % double arrow
+
+\def\title{CTANGLE (Version 4.2)}
+\def\topofcontents{\null\vfill
+ \centerline{\titlefont The {\ttitlefont CTANGLE} processor}
+ \vskip 15pt
+ \centerline{(Version 4.2)}
+ \vfill}
+\def\botofcontents{\vfill
+\noindent
+Copyright \copyright\ 1987, 1990, 1993, 2000 Silvio Levy and Donald E. Knuth
+\bigskip\noindent
+Permission is granted to make and distribute verbatim copies of this
+document provided that the copyright notice and this permission notice
+are preserved on all copies.
+
+\smallskip\noindent
+Permission is granted to copy and distribute modified versions of this
+document under the conditions for verbatim copying, provided that the
+entire resulting derived work is given a different name and distributed
+under the terms of a permission notice identical to this one.
+}
+\pageno=\contentspagenumber \advance\pageno by 1
+\let\maybe=\iftrue
+
+@** Introduction.
+This is the \.{CTANGLE} program by Silvio Levy and Donald E. Knuth,
+based on \.{TANGLE} by Knuth.
+We are thankful to
+Nelson Beebe, Hans-Hermann Bode (to whom the \CPLUSPLUS/ adaptation is due),
+Klaus Guntermann, Norman Ramsey, Tomas Rokicki, Joachim Schnitter,
+Joachim Schrod, Lee Wittenberg, and others who have contributed improvements.
+
+The ``banner line'' defined here should be changed whenever \.{CTANGLE}
+is modified.
+
+@d banner "This is CTANGLE (Version 4.2)"
+
+@c
+@<Include files@>@/
+@h
+@<Common code for \.{CWEAVE} and \.{CTANGLE}@>@/
+@<Typedef declarations@>@/
+@<Private variables@>@/
+@<Predeclaration of procedures@>
+
+@ \.{CTANGLE} has a fairly straightforward outline. It operates in
+two phases: First it reads the source file, saving the \CEE/ code in
+compressed form; then it shuffles and outputs the code.
+
+Please read the documentation for \.{common}, the set of routines common
+to \.{CTANGLE} and \.{CWEAVE}, before proceeding further.
+
+@c
+int main (
+int ac,
+char **av)
+{
+ argc=ac; argv=av;
+ program=ctangle;
+ @<Set initial values@>@;
+ common_init();
+ if (show_banner) puts(banner); /* print a ``banner line'' */
+ phase_one(); /* read all the user's text and compress it into |tok_mem| */
+ phase_two(); /* output the contents of the compressed tables */
+ return wrap_up(); /* and exit gracefully */
+}
+
+@ The next few sections contain stuff from the file |"common.w"| that must
+be included in both |"ctangle.w"| and |"cweave.w"|. It appears in
+file |"common.h"|, which is also included in |"common.w"| to propagate
+possible changes from this \.{COMMON} interface consistently.
+
+@i common.h
+
+@* Data structures exclusive to {\tt CTANGLE}.
+We've already seen that the |byte_mem| array holds the names of identifiers,
+strings, and sections;
+the |tok_mem| array holds the replacement texts
+for sections. Allocation is sequential, since things are deleted only
+during Phase II, and only in a last-in-first-out manner.
+
+A \&{text} variable is a structure containing a pointer into
+|tok_mem|, which tells where the corresponding text starts, and an
+integer |text_link|, which, as we shall see later, is used to connect
+pieces of text that have the same name. All the \&{text}s are stored in
+the array |text_info|, and we use a \&{text\_pointer} variable to refer
+to them.
+
+The first position of |tok_mem| that is unoccupied by
+replacement text is called |tok_ptr|, and the first unused location of
+|text_info| is called |text_ptr|. Thus we usually have the identity
+|text_ptr->tok_start==tok_ptr|.
+
+@<Typed...@>=
+typedef struct {
+ eight_bits *tok_start; /* pointer into |tok_mem| */
+ sixteen_bits text_link; /* relates replacement texts */
+} text;
+typedef text *text_pointer;
+
+@ @<Private...@>=
+static text text_info[max_texts];
+static text_pointer text_info_end=text_info+max_texts-1;
+static text_pointer text_ptr; /* first unused position in |text_info| */
+static eight_bits tok_mem[max_toks];
+static eight_bits *tok_mem_end=tok_mem+max_toks-1;
+static eight_bits *tok_ptr; /* first unused position in |tok_mem| */
+
+@ @<Set init...@>=
+text_info->tok_start=tok_ptr=tok_mem;
+text_ptr=text_info+1; text_ptr->tok_start=tok_mem;
+ /* this makes replacement text 0 of length zero */
+
+@ If |p| is a pointer to a section name, |p->equiv| is a pointer to its
+replacement text, an element of the array |text_info|.
+
+@d equiv equiv_or_xref /* info corresponding to names */
+
+@ @<Set init...@>=
+init_node(name_dir); /* the undefined section has no replacement text */
+
+@ Here's the procedure that decides whether a name of length |l|
+starting at position |first| equals the identifier pointed to by |p|:
+
+@c
+boolean names_match(@t\1\1@>
+name_pointer p, /* points to the proposed match */
+const char *first, /* position of first character of string */
+size_t l, /* length of identifier */
+eight_bits t@t\2\2@>) /* not used by \.{TANGLE} */
+{@+(void)t;
+ if (length(p)!=l) return false;
+ return !strncmp(first,p->byte_start,l);
+}
+
+@ The common lookup routine refers to separate routines |init_node| and
+|init_p| when the data structure grows. Actually |init_p| is called only by
+\.{CWEAVE}, but we need to declare a dummy version so that
+the loader won't complain of its absence.
+
+@c
+void
+init_node(
+name_pointer node)
+{
+ node->equiv=(void *)text_info;
+}
+void
+init_p(name_pointer p,eight_bits t) {@+(void)p;@+(void)t;@+}
+
+@* Tokens.
+Replacement texts, which represent \CEE/ code in a compressed format,
+appear in |tok_mem| as mentioned above. The codes in
+these texts are called `tokens'; some tokens occupy two consecutive
+eight-bit byte positions, and the others take just one byte.
+
+If $p$ points to a replacement text, |p->tok_start| is the |tok_mem| position
+of the first eight-bit code of that text. If |p->text_link==0|,
+this is the replacement text for a macro, otherwise it is the replacement
+text for a section. In the latter case |p->text_link| is either equal to
+|section_flag|, which means that there is no further text for this section, or
+|p->text_link| points to a continuation of this replacement text; such
+links are created when several sections have \CEE/ texts with the same
+name, and they also tie together all the \CEE/ texts of unnamed sections.
+The replacement text pointer for the first unnamed section appears in
+|text_info->text_link|, and the most recent such pointer is |last_unnamed|.
+
+@d section_flag max_texts /* final |text_link| in section replacement texts */
+
+@<Private...@>=
+static text_pointer last_unnamed; /* most recent replacement text of unnamed section */
+
+@ @<Set init...@>= last_unnamed=text_info; text_info->text_link=0;
+
+@ If the first byte of a token is less than |0200|, the token occupies a
+single byte. Otherwise we make a sixteen-bit token by combining two consecutive
+bytes |a| and |b|. If |0200<=a<0250|, then |(a-0200)@t${}\times2^8$@>+b|
+points to an identifier; if |0250<=a<0320|, then
+|(a-0250)@t${}\times2^8$@>+b| points to a section name
+(or, if it has the special value |output_defs_flag|,
+to the area where the preprocessor definitions are stored); and if
+|0320<=a<0400|, then |(a-0320)@t${}\times2^8$@>+b| is the number of the section
+in which the current replacement text appears.
+
+Codes less than |0200| are 7-bit |char| codes that represent themselves.
+Some of the 7-bit codes will not be present, however, so we can
+use them for special purposes. The following symbolic names are used:
+
+\yskip \hang |join| denotes the concatenation of adjacent items with no
+space or line breaks allowed between them (the \.{@@\&} operation of \.{CWEB}).
+
+\hang |string| denotes the beginning or end of a string, verbatim
+construction or numerical constant.
+@^ASCII code dependencies@>
+
+@d string 02 /* takes the place of extended ASCII \.{\char2} */
+@d join 0177 /* takes the place of ASCII delete */
+@d output_defs_flag (2*024000-1)
+
+@ The following procedure is used to enter a two-byte value into
+|tok_mem| when a replacement text is being generated.
+
+@c
+static void
+store_two_bytes(
+sixteen_bits x)
+{
+ if (tok_ptr+2>tok_mem_end) overflow("token");
+ *tok_ptr++=x>>8; /* store high byte */
+ *tok_ptr++=x&0377; /* store low byte */
+}
+
+@ @<Predecl...@>=@+static void store_two_bytes(sixteen_bits);
+
+@** Stacks for output. The output process uses a stack to keep track
+of what is going on at different ``levels'' as the sections are being
+written out. Entries on this stack have five parts:
+
+\yskip\hang |end_field| is the |tok_mem| location where the replacement
+text of a particular level will end;
+
+\hang |byte_field| is the |tok_mem| location from which the next token
+on a particular level will be read;
+
+\hang |name_field| points to the name corresponding to a particular level;
+
+\hang |repl_field| points to the replacement text currently being read
+at a particular level;
+
+\hang |section_field| is the section number, or zero if this is a macro.
+
+\yskip\noindent The current values of these five quantities are referred to
+quite frequently, so they are stored in a separate place instead of in
+the |stack| array. We call the current values |cur_end|, |cur_byte|,
+|cur_name|, |cur_repl|, and |cur_section|.
+
+The global variable |stack_ptr| tells how many levels of output are
+currently in progress. The end of all output occurs when the stack is
+empty, i.e., when |stack_ptr==stack|.
+
+@<Typed...@>=
+typedef struct {
+ eight_bits *end_field; /* ending location of replacement text */
+ eight_bits *byte_field; /* present location within replacement text */
+ name_pointer name_field; /* |byte_start| index for text being output */
+ text_pointer repl_field; /* |tok_start| index for text being output */
+ sixteen_bits section_field; /* section number or zero if not a section */
+} output_state;
+typedef output_state *stack_pointer;
+
+@ @d cur_end cur_state.end_field /* current ending location in |tok_mem| */
+@d cur_byte cur_state.byte_field /* location of next output byte in |tok_mem|*/
+@d cur_name cur_state.name_field /* pointer to current name being expanded */
+@d cur_repl cur_state.repl_field /* pointer to current replacement text */
+@d cur_section cur_state.section_field /* current section number being expanded */
+
+@<Private...@>=
+static output_state cur_state; /* |cur_end|, |cur_byte|, |cur_name|, |cur_repl|,
+ and |cur_section| */
+static output_state stack[stack_size+1]; /* info for non-current levels */
+static stack_pointer stack_end=stack+stack_size; /* end of |stack| */
+static stack_pointer stack_ptr; /* first unused location in the output state stack */
+
+@ To get the output process started, we will perform the following
+initialization steps. We may assume that |text_info->text_link| is nonzero,
+since it points to the \CEE/ text in the first unnamed section that generates
+code; if there are no such sections, there is nothing to output, and an
+error message will have been generated before we do any of the initialization.
+
+@<Initialize the output stacks@>=
+stack_ptr=stack+1; cur_name=name_dir; cur_repl=text_info->text_link+text_info;
+cur_byte=cur_repl->tok_start; cur_end=(cur_repl+1)->tok_start; cur_section=0;
+
+@ When the replacement text for name |p| is to be inserted into the output,
+the following subroutine is called to save the old level of output and get
+the new one going.
+
+We assume that the \CEE/ compiler can copy structures.
+@^system dependencies@>
+
+@c
+static void
+push_level(@t\1\1@> /* suspends the current level */
+name_pointer p@t\2\2@>)
+{
+ if (stack_ptr==stack_end) overflow("stack");
+ *stack_ptr=cur_state;
+ stack_ptr++;
+ if (p!=NULL) { /* |p==NULL| means we are in |output_defs| */
+ cur_name=p; cur_repl=(text_pointer)p->equiv;
+ cur_byte=cur_repl->tok_start; cur_end=(cur_repl+1)->tok_start;
+ cur_section=0;
+ }
+}
+
+@ @<Predecl...@>=
+static void push_level(name_pointer);@/
+static void pop_level(boolean);
+
+@ When we come to the end of a replacement text, the |pop_level| subroutine
+does the right thing: It either moves to the continuation of this replacement
+text or returns the state to the most recently stacked level.
+
+@c
+static void
+pop_level(@t\1\1@> /* do this when |cur_byte| reaches |cur_end| */
+boolean flag@t\2\2@>) /* |flag==false| means we are in |output_defs| */
+{
+ if (flag && cur_repl->text_link<section_flag) { /* link to a continuation */
+ cur_repl=cur_repl->text_link+text_info; /* stay on the same level */
+ cur_byte=cur_repl->tok_start; cur_end=(cur_repl+1)->tok_start;
+ return;
+ }
+ stack_ptr--; /* go down to the previous level */
+ if (stack_ptr>stack) cur_state=*stack_ptr;
+}
+
+@ The heart of the output procedure is the function |get_output|,
+which produces the next token of output and sends it on to the lower-level
+function |out_char|. The main purpose of |get_output| is to handle the
+necessary stacking and unstacking. It sends the value |section_number|
+if the next output begins or ends the replacement text of some section,
+in which case |cur_val| is that section's number (if beginning) or the
+negative of that value (if ending). (A section number of 0 indicates
+not the beginning or ending of a section, but a \&{\#line} command.)
+And it sends the value |identifier|
+if the next output is an identifier, in which case
+|cur_val| points to that identifier name.
+
+@d section_number 0201 /* code returned by |get_output| for section numbers */
+@d identifier 0202 /* code returned by |get_output| for identifiers */
+
+@<Private...@>=
+static int cur_val; /* additional information corresponding to output token */
+
+@ If |get_output| finds that no more output remains, it returns with
+|stack_ptr==stack|.
+@^high-bit character handling@>
+
+@c
+static void
+get_output(void) /* sends next token to |out_char| */
+{
+ sixteen_bits a; /* value of current byte */
+ restart: if (stack_ptr==stack) return;
+ if (cur_byte==cur_end) {
+ cur_val=-((int)cur_section); /* cast needed because of sign extension */
+ pop_level(true);
+ if (cur_val==0) goto restart;
+ out_char(section_number); return;
+ }
+ a=*cur_byte++;
+ if (out_state==verbatim && a!=string && a!=constant && a!='\n')
+ C_putc(a); /* a high-bit character can occur in a string */
+ else if (a<0200) out_char(a); /* one-byte token */
+ else {
+ a=(a-0200)*0400+*cur_byte++;
+ switch (a/024000) { /* |024000==(0250-0200)*0400| */
+ case 0: cur_val=a; out_char(identifier); break;
+ case 1: if (a==output_defs_flag) output_defs();
+ else @<Expand section |a-024000|, |goto restart|@>@;
+ break;
+ default: cur_val=a-050000; if (cur_val>0) cur_section=cur_val;
+ out_char(section_number);
+ }
+ }
+}
+
+@ @<Predecl...@>=@+static void get_output(void);
+
+@ The user may have forgotten to give any \CEE/ text for a section name,
+or the \CEE/ text may have been associated with a different name by mistake.
+
+@<Expand section |a-...@>=
+{
+ a-=024000;
+ if ((a+name_dir)->equiv!=(void *)text_info) push_level(a+name_dir);
+ else if (a!=0) {
+ fputs("\n! Not present: <",stdout);
+ print_section_name(a+name_dir); err_print(">");
+@.Not present: <section name>@>
+ }
+ goto restart;
+}
+
+@* Producing the output.
+The |get_output| routine above handles most of the complexity of output
+generation, but there are two further considerations that have a nontrivial
+effect on \.{CTANGLE}'s algorithms.
+
+@ First,
+we want to make sure that the output has spaces and line breaks in
+the right places (e.g., not in the middle of a string or a constant or an
+identifier, not at a `\.{@@\&}' position
+where quantities are being joined together, and certainly after an \.=
+because the \CEE/ compiler thinks \.{=-} is ambiguous).
+
+The output process can be in one of following states:
+
+\yskip\hang |num_or_id| means that the last item in the buffer is a number or
+identifier, hence a blank space or line break must be inserted if the next
+item is also a number or identifier.
+
+\yskip\hang |unbreakable| means that the last item in the buffer was followed
+by the \.{@@\&} operation that inhibits spaces between it and the next item.
+
+\yskip\hang |verbatim| means we're copying only character tokens, and
+that they are to be output exactly as stored. This is the case during
+strings, verbatim constructions and numerical constants.
+
+\yskip\hang |post_slash| means we've just output a slash.
+
+\yskip\hang |normal| means none of the above.
+
+\yskip\noindent Furthermore, if the variable |protect| is |true|, newlines
+are preceded by a `\.\\'.
+
+@d normal 0 /* non-unusual state */
+@d num_or_id 1 /* state associated with numbers and identifiers */
+@d post_slash 2 /* state following a \./ */
+@d unbreakable 3 /* state associated with \.{@@\&} */
+@d verbatim 4 /* state in the middle of a string */
+
+@<Private...@>=
+static eight_bits out_state; /* current status of partial output */
+static boolean protect; /* should newline characters be quoted? */
+
+@ Here is a routine that is invoked when we want to output the current line.
+During the output process, |cur_line| equals the number of the next line
+to be output.
+
+@c
+static void
+flush_buffer(void) /* writes one line to output file */
+{
+ C_putc('\n');
+ if (cur_line % 100 == 0 && show_progress) {
+ printf(".");
+ if (cur_line % 500 == 0) printf("%d",cur_line);
+ update_terminal; /* progress report */
+ }
+ cur_line++;
+}
+
+@ @<Predecl...@>=@+static void flush_buffer(void);
+
+@ Second, we have modified the original \.{TANGLE} so that it will write output
+on multiple files.
+If a section name is introduced in at least one place by \.{@@(}
+instead of \.{@@<}, we treat it as the name of a file.
+All these special sections are saved on a stack, |output_files|.
+We write them out after we've done the unnamed section.
+
+@d max_files 256
+@<Private...@>=
+static name_pointer output_files[max_files];
+static name_pointer *cur_out_file, *end_output_files, *an_output_file;
+static char cur_section_name_char; /* is it |'<'| or |'('| */
+static char output_file_name[longest_name+1]; /* name of the file */
+
+@ We make |end_output_files| point just beyond the end of
+|output_files|. The stack pointer
+|cur_out_file| starts out there. Every time we see a new file, we
+decrement |cur_out_file| and then write it in.
+@<Set initial...@>=
+cur_out_file=end_output_files=output_files+max_files;
+
+@ @<If it's not there, add |cur_section_name| to the output file stack, or
+complain we're out of room@>=
+{
+ for (an_output_file=cur_out_file;
+ an_output_file<end_output_files; an_output_file++)
+ if (*an_output_file==cur_section_name) break;
+ if (an_output_file==end_output_files) {
+ if (cur_out_file>output_files)
+ *--cur_out_file=cur_section_name;
+ else {
+ overflow("output files");
+ }
+ }
+}
+
+@* The big output switch. Here then is the routine that does the
+output.
+
+@c
+static void
+phase_two (void) {
+ web_file_open=false;
+ cur_line=1;
+ @<Initialize the output stacks@>@;
+ @<Output macro definitions if appropriate@>@;
+ if (text_info->text_link==0 && cur_out_file==end_output_files) {
+ fputs("\n! No program text was specified.",stdout); mark_harmless;
+@.No program text...@>
+ }
+ else {
+ if(cur_out_file==end_output_files) {
+ if(show_progress)
+ printf("\nWriting the output file (%s):",C_file_name);
+ }
+ else {
+ if (show_progress) {
+ fputs("\nWriting the output files:",stdout);
+@.Writing the output...@>
+ printf(" (%s)",C_file_name);
+ update_terminal;
+ }
+ if (text_info->text_link==0) goto writeloop;
+ }
+ while (stack_ptr>stack) get_output();
+ flush_buffer();
+writeloop: @<Write all the named output files@>@;
+ if (show_happiness) {
+ if (show_progress) new_line;
+ fputs("Done.",stdout);
+ }
+ }
+}
+
+@ @<Predecl...@>=@+static void phase_two(void);
+
+@ To write the named output files, we proceed as for the unnamed
+section.
+The only subtlety is that we have to open each one.
+
+@<Write all the named output files@>=
+for (an_output_file=end_output_files; an_output_file>cur_out_file;) {
+ an_output_file--;
+ sprint_section_name(output_file_name,*an_output_file);
+ fclose(C_file);
+ C_file=fopen(output_file_name,"wb");
+ if (C_file ==0) fatal("! Cannot open output file ",output_file_name);
+@.Cannot open output file@>
+ if (show_progress) { printf("\n(%s)",output_file_name); update_terminal; }
+ cur_line=1;
+ stack_ptr=stack+1;
+ cur_name= (*an_output_file);
+ cur_repl= (text_pointer)cur_name->equiv;
+ cur_byte=cur_repl->tok_start;
+ cur_end=(cur_repl+1)->tok_start;
+ while (stack_ptr > stack) get_output();
+ flush_buffer();
+}
+
+@ If a \.{@@h} was not encountered in the input,
+we go through the list of replacement texts and copy the ones
+that refer to macros, preceded by the \.{\#define} preprocessor command.
+
+@<Output macro definitions if appropriate@>=
+ if (!output_defs_seen)
+ output_defs();
+
+@ @<Private...@>=
+static boolean output_defs_seen=false;
+
+@ @<Predecl...@>=
+static void output_defs(void);@/
+static void out_char(eight_bits);
+
+@ @c
+static void
+output_defs(void)
+{
+ sixteen_bits a;
+ push_level(NULL);
+ for (cur_text=text_info+1; cur_text<text_ptr; cur_text++)
+ if (cur_text->text_link==0) { /* |cur_text| is the text for a macro */
+ cur_byte=cur_text->tok_start;
+ cur_end=(cur_text+1)->tok_start;
+ C_printf("%s","#define ");
+ out_state=normal;
+ protect=true; /* newlines should be preceded by |'\\'| */
+ while (cur_byte<cur_end) {
+ a=*cur_byte++;
+ if (cur_byte==cur_end && a=='\n') break; /* disregard a final newline */
+ if (out_state==verbatim && a!=string && a!=constant && a!='\n')
+ C_putc(a); /* a high-bit character can occur in a string */
+@^high-bit character handling@>
+ else if (a<0200) out_char(a); /* one-byte token */
+ else {
+ a=(a-0200)*0400+*cur_byte++;
+ if (a<024000) { /* |024000==(0250-0200)*0400| */
+ cur_val=a; out_char(identifier);
+ }
+ else if (a<050000) { confusion("macro defs have strange char");}
+ else {
+ cur_val=a-050000; cur_section=cur_val; out_char(section_number);
+ }
+ /* no other cases */
+ }
+ }
+ protect=false;
+ flush_buffer();
+ }
+ pop_level(false);
+}
+
+@ A many-way switch is used to send the output. Note that this function
+is not called if |out_state==verbatim|, except perhaps with arguments
+|'\n'| (protect the newline), |string| (end the string), or |constant|
+(end the constant).
+
+@c
+static void
+out_char(
+eight_bits cur_char)
+{
+ char *j, *k; /* pointer into |byte_mem| */
+restart:
+ switch (cur_char) {
+ case '\n': if (protect && out_state!=verbatim) C_putc(' ');
+ if (protect || out_state==verbatim) C_putc('\\');
+ flush_buffer(); if (out_state!=verbatim) out_state=normal; break;
+ @/@t\4@>@<Case of an identifier@>@;
+ @/@t\4@>@<Case of a section number@>@;
+ @/@t\4@>@<Cases like \.{!=}@>@;
+ case '=': case '>': C_putc(cur_char); C_putc(' ');
+ out_state=normal; break;
+ case join: out_state=unbreakable; break;
+ case constant: if (out_state==verbatim) {
+ out_state=num_or_id; break;
+ }
+ if(out_state==num_or_id) C_putc(' '); out_state=verbatim; break;
+ case string: if (out_state==verbatim) out_state=normal;
+ else out_state=verbatim; break;
+ case '/': C_putc('/'); out_state=post_slash; break;
+ case '*': if (out_state==post_slash) C_putc(' ');
+ /* fall through */
+ default: C_putc(cur_char); out_state=normal; break;
+ }
+}
+
+@ @<Cases like \.{!=}@>=
+case plus_plus: C_putc('+');@+C_putc('+'); out_state=normal; break;
+case minus_minus: C_putc('-');@+C_putc('-'); out_state=normal; break;
+case minus_gt: C_putc('-');@+C_putc('>'); out_state=normal; break;
+case gt_gt: C_putc('>');@+C_putc('>'); out_state=normal; break;
+case eq_eq: C_putc('=');@+C_putc('='); out_state=normal; break;
+case lt_lt: C_putc('<');@+C_putc('<'); out_state=normal; break;
+case gt_eq: C_putc('>');@+C_putc('='); out_state=normal; break;
+case lt_eq: C_putc('<');@+C_putc('='); out_state=normal; break;
+case non_eq: C_putc('!');@+C_putc('='); out_state=normal; break;
+case and_and: C_putc('&');@+C_putc('&'); out_state=normal; break;
+case or_or: C_putc('|');@+C_putc('|'); out_state=normal; break;
+case dot_dot_dot: C_putc('.');@+C_putc('.');@+C_putc('.'); out_state=normal;
+ break;
+case colon_colon: C_putc(':');@+C_putc(':'); out_state=normal; break;
+case period_ast: C_putc('.');@+C_putc('*'); out_state=normal; break;
+case minus_gt_ast: C_putc('-');@+C_putc('>');@+C_putc('*'); out_state=normal;
+ break;
+
+@ When an identifier is output to the \CEE/ file, characters in the
+range 128--255 must be changed into something else, so the \CEE/
+compiler won't complain. By default, \.{CTANGLE} converts the
+character with code $16 x+y$ to the three characters `\.X$xy$', but
+a different transliteration table can be specified. Thus a German
+might want {\it gr\"un\/} to appear as a still readable \.{gruen}.
+This makes debugging a lot less confusing.
+
+@d translit_length 10
+
+@<Private...@>=
+static char translit[128][translit_length];
+
+@ @<Set init...@>=
+{
+ int i;
+ for (i=0;i<128;i++) sprintf(translit[i],"X%02X",(unsigned int)(128+i));
+}
+
+@ @<Case of an identifier@>=
+case identifier:
+ if (out_state==num_or_id) C_putc(' ');
+ j=(cur_val+name_dir)->byte_start;
+ k=(cur_val+name_dir+1)->byte_start;
+ while (j<k) {
+ if ((eight_bits)(*j)<0200) C_putc(*j);
+@^high-bit character handling@>
+ else C_printf("%s",translit[(eight_bits)(*j)-0200]);
+ j++;
+ }
+ out_state=num_or_id; break;
+
+@ @<Case of a sec...@>=
+case section_number:
+ if (cur_val>0) C_printf("/*%d:*/",cur_val);
+ else if(cur_val<0) C_printf("/*:%d*/",-cur_val);
+ else if (protect) {
+ cur_byte +=4; /* skip line number and file name */
+ cur_char = '\n';
+ goto restart;
+ } else {
+ sixteen_bits a;
+ a=0400* *cur_byte++;
+ a+=*cur_byte++; /* gets the line number */
+ C_printf("\n#line %d \"",a);
+@:line}{\.{\#line}@>
+ cur_val=*cur_byte++;
+ cur_val=0400*(cur_val-0200)+ *cur_byte++; /* points to the file name */
+ for (j=(cur_val+name_dir)->byte_start, k=(cur_val+name_dir+1)->byte_start;
+ j<k; j++) {
+ if (*j=='\\' || *j=='"') C_putc('\\');
+ C_putc(*j);
+ }
+ C_printf("%s","\"\n");
+ }
+ break;
+
+@** Introduction to the input phase.
+We have now seen that \.{CTANGLE} will be able to output the full
+\CEE/ program, if we can only get that program into the byte memory in
+the proper format. The input process is something like the output process
+in reverse, since we compress the text as we read it in and we expand it
+as we write it out.
+
+There are three main input routines. The most interesting is the one that gets
+the next token of a \CEE/ text; the other two are used to scan rapidly past
+\TEX/ text in the \.{CWEB} source code. One of the latter routines will jump to
+the next token that starts with `\.{@@}', and the other skips to the end
+of a \CEE/ comment.
+
+@ Control codes in \.{CWEB} begin with `\.{@@}', and the next character
+identifies the code. Some of these are of interest only to \.{CWEAVE},
+so \.{CTANGLE} ignores them; the others are converted by \.{CTANGLE} into
+internal code numbers by the |ccode| table below. The ordering
+of these internal code numbers has been chosen to simplify the program logic;
+larger numbers are given to the control codes that denote more significant
+milestones.
+
+@d ignore 0 /* control code of no interest to \.{CTANGLE} */
+@d ord 0302 /* control code for `\.{@@'}' */
+@d control_text 0303 /* control code for `\.{@@t}', `\.{@@\^}', etc. */
+@d translit_code 0304 /* control code for `\.{@@l}' */
+@d output_defs_code 0305 /* control code for `\.{@@h}' */
+@d format_code 0306 /* control code for `\.{@@f}' */
+@d definition 0307 /* control code for `\.{@@d}' */
+@d begin_C 0310 /* control code for `\.{@@c}' */
+@d section_name 0311 /* control code for `\.{@@<}' */
+@d new_section 0312 /* control code for `\.{@@\ }' and `\.{@@*}' */
+
+@<Private...@>=
+static eight_bits ccode[256]; /* meaning of a char following \.{@@} */
+
+@ @<Set ini...@>= {
+ int c; /* must be |int| so the |for| loop will end */
+ for (c=0; c<256; c++) ccode[c]=ignore;
+ ccode[' ']=ccode['\t']=ccode['\n']=ccode['\v']=ccode['\r']=ccode['\f']
+ =ccode['*']=new_section;
+ ccode['@@']='@@'; ccode['=']=string;
+ ccode['d']=ccode['D']=definition;
+ ccode['f']=ccode['F']=ccode['s']=ccode['S']=format_code;
+ ccode['c']=ccode['C']=ccode['p']=ccode['P']=begin_C;
+ ccode['^']=ccode[':']=ccode['.']=ccode['t']=ccode['T']=
+ ccode['q']=ccode['Q']=control_text;
+ ccode['h']=ccode['H']=output_defs_code;
+ ccode['l']=ccode['L']=translit_code;
+ ccode['&']=join;
+ ccode['<']=ccode['(']=section_name;
+ ccode['\'']=ord;
+}
+
+@ The |skip_ahead| procedure reads through the input at fairly high speed
+until finding the next non-ignorable control code, which it returns.
+
+@c
+static eight_bits
+skip_ahead(void) /* skip to next control code */
+{
+ eight_bits c; /* control code found */
+ while (true) {
+ if (loc>limit && (get_line()==false)) return new_section;
+ *(limit+1)='@@';
+ while (*loc!='@@') loc++;
+ if (loc<=limit) {
+ loc++; c=ccode[(eight_bits)*loc]; loc++;
+ if (c!=ignore || *(loc-1)=='>') return c;
+ }
+ }
+}
+
+@ @<Predecl...@>=
+static eight_bits skip_ahead(void);@/
+static boolean skip_comment(boolean);
+
+@ The |skip_comment| procedure reads through the input at somewhat high
+speed in order to pass over comments, which \.{CTANGLE} does not transmit
+to the output. If the comment is introduced by \.{/*}, |skip_comment|
+proceeds until finding the end-comment token \.{*/} or a newline; in the
+latter case |skip_comment| will be called again by |get_next|, since the
+comment is not finished. This is done so that each newline in the
+\CEE/ part of a section is copied to the output; otherwise the \&{\#line}
+commands inserted into the \CEE/ file by the output routines become useless.
+On the other hand, if the comment is introduced by \.{//} (i.e., if it
+is a \CPLUSPLUS/ ``short comment''), it always is simply delimited by the next
+newline. The boolean argument |is_long_comment| distinguishes between
+the two types of comments.
+
+If |skip_comment| comes to the end of the section, it prints an error message.
+No comment, long or short, is allowed to contain `\.{@@\ }' or `\.{@@*}'.
+
+@<Private...@>=
+static boolean comment_continues=false; /* are we scanning a comment? */
+
+@ @c
+static boolean skip_comment(@t\1\1@> /* skips over comments */
+boolean is_long_comment@t\2\2@>)
+{
+ char c; /* current character */
+ while (true) {
+ if (loc>limit) {
+ if (is_long_comment) {
+ if(get_line()) return comment_continues=true;
+ else{
+ err_print("! Input ended in mid-comment");
+@.Input ended in mid-comment@>
+ return comment_continues=false;
+ }
+ }
+ else return comment_continues=false;
+ }
+ c=*(loc++);
+ if (is_long_comment && c=='*' && *loc=='/') {
+ loc++; return comment_continues=false;
+ }
+ if (c=='@@') {
+ if (ccode[(eight_bits)*loc]==new_section) {
+ err_print("! Section name ended in mid-comment"); loc--;
+@.Section name ended in mid-comment@>
+ return comment_continues=false;
+ }
+ else loc++;
+ }
+ }
+}
+
+@* Inputting the next token.
+
+@d constant 03
+
+@<Private...@>=
+static name_pointer cur_section_name; /* name of section just scanned */
+static boolean no_where; /* suppress |print_where|? */
+
+@ As one might expect, |get_next| consists mostly of a big switch
+that branches to the various special cases that can arise.
+
+@d isxalpha(c) ((c)=='_' || (c)=='$')
+ /* non-alpha characters allowed in identifier */
+@d ishigh(c) ((eight_bits)(c)>0177)
+@^high-bit character handling@>
+
+@c
+static eight_bits
+get_next(void) /* produces the next input token */
+{
+ static int preprocessing=0;
+ eight_bits c; /* the current character */
+ while (true) {
+ if (loc>limit) {
+ if (preprocessing && *(limit-1)!='\\') preprocessing=0;
+ if (get_line()==false) return new_section;
+ else if (print_where && !no_where) {
+ print_where=false;
+ @<Insert the line number into |tok_mem|@>@;
+ }
+ else return '\n';
+ }
+ c=*loc;
+ if (comment_continues || (c=='/' && (*(loc+1)=='*' || *(loc+1)=='/'))) {
+ skip_comment(comment_continues||*(loc+1)=='*');
+ /* scan to end of comment or newline */
+ if (comment_continues) return '\n';
+ else continue;
+ }
+ loc++;
+ if (xisdigit(c) || c=='.') @<Get a constant@>@;
+ else if (c=='\'' || c=='"'@|
+ || ((c=='L' || c=='u' || c=='U')&&(*loc=='\'' || *loc=='"'))@|
+ || ((c=='u' && *loc=='8')&&(*(loc+1)=='\'' || *(loc+1)=='"')))
+ @<Get a string@>@;
+ else if (isalpha(c) || isxalpha(c) || ishigh(c))
+ @<Get an identifier@>@;
+ else if (c=='@@') @<Get control code and possible section name@>@;
+ else if (xisspace(c)) {
+ if (!preprocessing || loc>limit) continue;
+ /* we don't want a blank after a final backslash */
+ else return ' '; /* ignore spaces and tabs, unless preprocessing */
+ }
+ else if (c=='#' && loc==buffer+1) preprocessing=1;
+ mistake: @<Compress two-symbol operator@>@;
+ return c;
+ }
+}
+
+@ @<Predecl...@>=@+static eight_bits get_next(void);
+
+@ The following code assigns values to the combinations \.{++},
+\.{--}, \.{->}, \.{>=}, \.{<=}, \.{==}, \.{<<}, \.{>>}, \.{!=}, \.{||} and
+\.{\&\&}, and to the \CPLUSPLUS/
+combinations \.{...}, \.{::}, \.{.*} and \.{->*}.
+The compound assignment operators (e.g., \.{+=}) are
+treated as separate tokens.
+
+@d compress(c) if (loc++<=limit) return c
+
+@<Compress tw...@>=
+switch(c) {
+ case '+': if (*loc=='+') compress(plus_plus); break;
+ case '-': if (*loc=='-') {compress(minus_minus);}
+ else { if (*loc=='>') { if (*(loc+1)=='*') {loc++; compress(minus_gt_ast);}
+ else compress(minus_gt); } } break;
+ case '.': if (*loc=='*') {compress(period_ast);}
+ else if (*loc=='.' && *(loc+1)=='.') {
+ loc++; compress(dot_dot_dot);
+ }
+ break;
+ case ':': if (*loc==':') compress(colon_colon); break;
+ case '=': if (*loc=='=') compress(eq_eq); break;
+ case '>': if (*loc=='=') {compress(gt_eq);}
+ else if (*loc=='>') compress(gt_gt); break;
+ case '<': if (*loc=='=') {compress(lt_eq);}
+ else if (*loc=='<') compress(lt_lt); break;
+ case '&': if (*loc=='&') compress(and_and); break;
+ case '|': if (*loc=='|') compress(or_or); break;
+ case '!': if (*loc=='=') compress(non_eq); break;
+}
+
+@ @<Get an identifier@>= {
+ id_first=--loc;
+ while (isalpha((eight_bits)*++loc) || isdigit((eight_bits)*loc) @|
+ || isxalpha((eight_bits)*loc) || ishigh((eight_bits)*loc));
+ id_loc=loc; return identifier;
+}
+
+@ @<Get a constant@>= {
+ id_first=loc-1;
+ if (*id_first=='.' && !xisdigit(*loc)) goto mistake; /* not a constant */
+ if (*id_first=='0') {
+ if (*loc=='x' || *loc=='X') { /* hex constant */
+ loc++; while (xisxdigit(*loc)) loc++; goto found;
+ }
+ }
+ while (xisdigit(*loc)) loc++;
+ if (*loc=='.') {
+ loc++;
+ while (xisdigit(*loc)) loc++;
+ }
+ if (*loc=='e' || *loc=='E') { /* float constant */
+ if (*++loc=='+' || *loc=='-') loc++;
+ while (xisdigit(*loc)) loc++;
+ }
+ found: while (*loc=='u' || *loc=='U' || *loc=='l' || *loc=='L'
+ || *loc=='f' || *loc=='F') loc++;
+ id_loc=loc;
+ return constant;
+}
+
+@ \CEE/ strings and character constants, delimited by double and single
+quotes, respectively, can contain newlines or instances of their own
+delimiters if they are protected by a backslash. We follow this
+convention, but do not allow the string to be longer than |longest_name|.
+
+@<Get a string@>= {
+ char delim = c; /* what started the string */
+ id_first = section_text+1;
+ id_loc = section_text; *++id_loc=delim;
+ if (delim=='L' || delim=='u' || delim=='U') { /* wide character constant */
+ if (delim=='u' && *loc=='8') { *++id_loc=*loc++; }
+ delim=*loc++; *++id_loc=delim;
+ }
+ while (true) {
+ if (loc>=limit) {
+ if(*(limit-1)!='\\') {
+ err_print("! String didn't end"); loc=limit; break;
+@.String didn't end@>
+ }
+ if(get_line()==false) {
+ err_print("! Input ended in middle of string"); loc=buffer; break;
+@.Input ended in middle of string@>
+ }
+ else if (++id_loc<=section_text_end) *id_loc='\n'; /* will print as
+ \.{"\\\\\\n"} */
+ }
+ if ((c=*loc++)==delim) {
+ if (++id_loc<=section_text_end) *id_loc=c;
+ break;
+ }
+ if (c=='\\') {
+ if (loc>=limit) continue;
+ if (++id_loc<=section_text_end) *id_loc = '\\';
+ c=*loc++;
+ }
+ if (++id_loc<=section_text_end) *id_loc=c;
+ }
+ if (id_loc>=section_text_end) {
+ fputs("\n! String too long: ",stdout);
+@.String too long@>
+ term_write(section_text+1,25);
+ err_print("...");
+ }
+ id_loc++;
+ return string;
+}
+
+@ After an \.{@@} sign has been scanned, the next character tells us
+whether there is more work to do.
+
+@<Get control code and possible section name@>= {
+ c=ccode[(eight_bits)*loc++];
+ switch(c) {
+ case ignore: continue;
+ case translit_code: err_print("! Use @@l in limbo only"); continue;
+@.Use @@l in limbo...@>
+ case control_text: while ((c=skip_ahead())=='@@');
+ /* only \.{@@@@} and \.{@@>} are expected */
+ if (*(loc-1)!='>')
+ err_print("! Double @@ should be used in control text");
+@.Double @@ should be used...@>
+ continue;
+ case section_name:
+ cur_section_name_char=*(loc-1);
+ @<Scan the section name and make |cur_section_name| point to it@>@;
+ case string: @<Scan a verbatim string@>@;
+ case ord: @<Scan an ASCII constant@>@;
+ default: return c;
+ }
+}
+
+@ After scanning a valid ASCII constant that follows
+\.{@@'}, this code plows ahead until it finds the next single quote.
+(Special care is taken if the quote is part of the constant.)
+Anything after a valid ASCII constant is ignored;
+thus, \.{@@'\\nopq'} gives the same result as \.{@@'\\n'}.
+
+@<Scan an ASCII constant@>=
+ id_first=loc;
+ if (*loc=='\\') {
+ if (*++loc=='\'') loc++;
+ }
+ while (*loc!='\'') {
+ if (*loc=='@@') {
+ if (*(loc+1)!='@@')
+ err_print("! Double @@ should be used in ASCII constant");
+@.Double @@ should be used...@>
+ else loc++;
+ }
+ loc++;
+ if (loc>limit) {
+ err_print("! String didn't end"); loc=limit-1; break;
+@.String didn't end@>
+ }
+ }
+ loc++;
+ return ord;
+
+@ @<Scan the section name...@>= {
+ char *k; /* pointer into |section_text| */
+ @<Put section name into |section_text|@>@;
+ if (k-section_text>3 && strncmp(k-2,"...",3)==0)
+ cur_section_name=section_lookup(section_text+1,k-3,1); /* 1 means is a prefix */
+ else cur_section_name=section_lookup(section_text+1,k,0);
+ if (cur_section_name_char=='(')
+ @<If it's not there, add |cur_section_name| to the output file stack, or
+ complain we're out of room@>@;
+ return section_name;
+}
+
+@ Section names are placed into the |section_text| array with consecutive spaces,
+tabs, and carriage-returns replaced by single spaces. There will be no
+spaces at the beginning or the end. (We set |section_text[0]=' '| to facilitate
+this, since the |section_lookup| routine uses |section_text[1]| as the first
+character of the name.)
+
+@<Set init...@>=section_text[0]=' ';
+
+@ @<Put section name...@>=
+k=section_text;
+while (true) {
+ if (loc>limit && get_line()==false) {
+ err_print("! Input ended in section name");
+@.Input ended in section name@>
+ loc=buffer+1; break;
+ }
+ c=*loc;
+ @<If end of name or erroneous nesting, |break|@>@;
+ loc++; if (k<section_text_end) k++;
+ if (xisspace(c)) {
+ c=' '; if (*(k-1)==' ') k--;
+ }
+*k=c;
+}
+if (k>=section_text_end) {
+ fputs("\n! Section name too long: ",stdout);
+@.Section name too long@>
+ term_write(section_text+1,25);
+ printf("..."); mark_harmless;
+}
+if (*k==' ' && k>section_text) k--;
+
+@ @<If end of name or erroneous nesting,...@>=
+if (c=='@@') {
+ c=*(loc+1);
+ if (c=='>') {
+ loc+=2; break;
+ }
+ if (ccode[(eight_bits)c]==new_section) {
+ err_print("! Section name didn't end"); break;
+@.Section name didn't end@>
+ }
+ if (ccode[(eight_bits)c]==section_name) {
+ err_print("! Nesting of section names not allowed"); break;
+@.Nesting of section names...@>
+ }
+ *(++k)='@@'; loc++; /* now |c==*loc| again */
+}
+
+@ At the present point in the program we
+have |*(loc-1)==string|; we set |id_first| to the beginning
+of the string itself, and |id_loc| to its ending-plus-one location in the
+buffer. We also set |loc| to the position just after the ending delimiter.
+
+@<Scan a verbatim string@>= {
+ id_first=loc++; *(limit+1)='@@'; *(limit+2)='>';
+ while (*loc!='@@' || *(loc+1)!='>') loc++;
+ if (loc>=limit) err_print("! Verbatim string didn't end");
+@.Verbatim string didn't end@>
+ id_loc=loc; loc+=2;
+ return string;
+}
+
+@* Scanning a macro definition.
+The rules for generating the replacement texts corresponding to macros and
+\CEE/ texts of a section are almost identical; the only differences are that
+
+\yskip \item{a)}Section names are not allowed in macros;
+in fact, the appearance of a section name terminates such macros and denotes
+the name of the current section.
+
+\item{b)}The symbols \.{@@d} and \.{@@f} and \.{@@c} are not allowed after
+section names, while they terminate macro definitions.
+
+\item{c)}Spaces are inserted after right parentheses in macros, because the
+ANSI \CEE/ preprocessor sometimes requires it.
+
+\yskip Therefore there is a single procedure |scan_repl| whose parameter
+|t| specifies either |macro| or |section_name|. After |scan_repl| has
+acted, |cur_text| will point to the replacement text just generated, and
+|next_control| will contain the control code that terminated the activity.
+
+@d macro 0
+@d app_repl(c) {if (tok_ptr==tok_mem_end) overflow("token"); *tok_ptr++=c;}
+
+@<Private...@>=
+static text_pointer cur_text; /* replacement text formed by |scan_repl| */
+static eight_bits next_control;
+
+@ @c
+static void
+scan_repl(@t\1\1@> /* creates a replacement text */
+eight_bits t@t\2\2@>)
+{
+ sixteen_bits a; /* the current token */
+ if (t==section_name) {@<Insert the line number into |tok_mem|@>@;}
+ while (true) switch (a=get_next()) {
+ @<In cases that |a| is a non-|char| token (|identifier|,
+ |section_name|, etc.), either process it and change |a| to a byte
+ that should be stored, or |continue| if |a| should be ignored,
+ or |goto done| if |a| signals the end of this replacement text@>@;
+ case ')': app_repl(a);
+ if (t==macro) app_repl(' ');
+ break;
+ default: app_repl(a); /* store |a| in |tok_mem| */
+ }
+ done: next_control=(eight_bits) a;
+ if (text_ptr>text_info_end) overflow("text");
+ cur_text=text_ptr; (++text_ptr)->tok_start=tok_ptr;
+}
+
+@ @<Predecl...@>=@+static void scan_repl(eight_bits);
+
+@ Here is the code for the line number: first a |sixteen_bits| equal
+to |0150000|; then the numeric line number; then a pointer to the
+file name.
+
+@<Insert the line...@>=
+store_two_bytes(0150000);
+if (changing && include_depth==change_depth) { /* correction made Feb 2017 */
+ id_first=change_file_name;
+ store_two_bytes((sixteen_bits)change_line);
+}@+else {
+ id_first=cur_file_name;
+ store_two_bytes((sixteen_bits)cur_line);
+}
+id_loc=id_first+strlen(id_first);
+{int a_l=id_lookup(id_first,id_loc,0)-name_dir; app_repl((a_l / 0400)+0200);
+ app_repl(a_l % 0400);}
+
+@ @<In cases that |a| is...@>=
+case identifier: a=id_lookup(id_first,id_loc,0)-name_dir;
+ app_repl((a / 0400)+0200);
+ app_repl(a % 0400); break;
+case section_name: if (t!=section_name) goto done;
+ else {
+ @<Was an `\.{@@}' missed here?@>@;
+ a=cur_section_name-name_dir;
+ app_repl((a / 0400)+0250);
+ app_repl(a % 0400);
+ @<Insert the line number into |tok_mem|@>@; break;
+ }
+case output_defs_code: if (t!=section_name) err_print("! Misplaced @@h");
+@.Misplaced @@h@>
+ else {
+ output_defs_seen=true;
+ a=output_defs_flag;
+ app_repl((a / 0400)+0200);
+ app_repl(a % 0400);
+ @<Insert the line number into |tok_mem|@>@;
+ }
+ break;
+case constant: case string:
+ @<Copy a string or verbatim construction or numerical constant@>@;
+case ord:
+ @<Copy an ASCII constant@>@;
+case definition: case format_code: case begin_C: if (t!=section_name) goto done;
+ else {
+ err_print("! @@d, @@f and @@c are ignored in C text"); continue;
+@.@@d, @@f and @@c are ignored in C text@>
+ }
+case new_section: goto done;
+
+@ @<Was an `\.{@@}'...@>= {
+ char *try_loc=loc;
+ while (*try_loc==' ' && try_loc<limit) try_loc++;
+ if (*try_loc=='+' && try_loc<limit) try_loc++;
+ while (*try_loc==' ' && try_loc<limit) try_loc++;
+ if (*try_loc=='=') err_print ("! Missing `@@ ' before a named section");
+@.Missing `@@ '...@>
+ /* user who isn't defining a section should put newline after the name,
+ as explained in the manual */
+}
+
+@ @<Copy a string...@>=
+ app_repl(a); /* |string| or |constant| */
+ while (id_first < id_loc) { /* simplify \.{@@@@} pairs */
+ if (*id_first=='@@') {
+ if (*(id_first+1)=='@@') id_first++;
+ else err_print("! Double @@ should be used in string");
+@.Double @@ should be used...@>
+ }
+ app_repl(*id_first++);
+ }
+ app_repl(a); break;
+
+@ This section should be rewritten on machines that don't use ASCII
+code internally.
+@^ASCII code dependencies@>
+
+@<Copy an ASCII constant@>= {
+ int c=(eight_bits) *id_first;
+ if (c=='\\') {
+ c=*++id_first;
+ if (c>='0' && c<='7') {
+ c-='0';
+ if (*(id_first+1)>='0' && *(id_first+1)<='7') {
+ c=8*c+*(++id_first) - '0';
+ if (*(id_first+1)>='0' && *(id_first+1)<='7' && c<32)
+ c=8*c+*(++id_first)- '0';
+ }
+ }
+ else switch (c) {
+ case 't':c='\t';@+break;
+ case 'n':c='\n';@+break;
+ case 'b':c='\b';@+break;
+ case 'f':c='\f';@+break;
+ case 'v':c='\v';@+break;
+ case 'r':c='\r';@+break;
+ case 'a':c='\7';@+break;
+ case '?':c='?';@+break;
+ case 'x':
+ if (xisdigit(*(id_first+1))) c=*(++id_first)-'0';
+ else if (xisxdigit(*(id_first+1))) {
+ ++id_first;
+ c=toupper((eight_bits)*id_first)-'A'+10;
+ }
+ if (xisdigit(*(id_first+1))) c=16*c+*(++id_first)-'0';
+ else if (xisxdigit(*(id_first+1))) {
+ ++id_first;
+ c=16*c+toupper((eight_bits)*id_first)-'A'+10;
+ }
+ break;
+ case '\\':c='\\';@+break;
+ case '\'':c='\'';@+break;
+ case '\"':c='\"';@+break;
+ default: err_print("! Unrecognized escape sequence");
+@.Unrecognized escape sequence@>
+ }
+ }@/
+ /* at this point |c| should have been converted to its ASCII code number */
+ app_repl(constant);
+ if (c>=100) app_repl('0'+c/100);
+ if (c>=10) app_repl('0'+(c/10)%10);
+ app_repl('0'+c%10);
+ app_repl(constant);
+}
+break;
+
+@* Scanning a section.
+The |scan_section| procedure starts when `\.{@@\ }' or `\.{@@*}' has been
+sensed in the input, and it proceeds until the end of that section. It
+uses |section_count| to keep track of the current section number; with luck,
+\.{CWEAVE} and \.{CTANGLE} will both assign the same numbers to sections.
+
+The body of |scan_section| is a loop where we look for control codes
+that are significant to \.{CTANGLE}: those
+that delimit a definition, the \CEE/ part of a module, or a new module.
+
+@c
+static void
+scan_section(void)
+{
+ name_pointer p; /* section name for the current section */
+ text_pointer q; /* text for the current section */
+ sixteen_bits a; /* token for left-hand side of definition */
+ section_count++; @+ no_where=true;
+ if (*(loc-1)=='*' && show_progress) { /* starred section */
+ printf("*%d",section_count); update_terminal;
+ }
+ next_control=0;
+ while (true) {
+ @<Skip ahead until |next_control| corresponds to \.{@@d}, \.{@@<},
+ \.{@@\ } or the like@>@;
+ if (next_control == definition) { /* \.{@@d} */
+ @<Scan a definition@>@;
+ continue;
+ }
+ if (next_control == begin_C) { /* \.{@@c} or \.{@@p} */
+ p=name_dir; break;
+ }
+ if (next_control == section_name) { /* \.{@@<} or \.{@@(} */
+ p=cur_section_name;
+ @<If section is not being defined, |continue| @>@;
+ break;
+ }
+ return; /* \.{@@\ } or \.{@@*} */
+ }
+ no_where=print_where=false;
+ @<Scan the \CEE/ part of the current section@>@;
+}
+
+@ @<Predecl...@>=@+static void scan_section(void);
+
+@ At the top of this loop, if |next_control==section_name|, the
+section name has already been scanned (see |@<Get control code
+and...@>|). Thus, if we encounter |next_control==section_name| in the
+skip-ahead process, we should likewise scan the section name, so later
+processing will be the same in both cases.
+
+@<Skip ahead until |next_control| ...@>=
+while (next_control<definition)
+ /* |definition| is the lowest of the ``significant'' codes */
+ if((next_control=skip_ahead())==section_name){
+ loc-=2; next_control=get_next();
+ }
+
+@ @<Scan a definition@>= {
+ while ((next_control=get_next())=='\n'); /* allow newline before definition */
+ if (next_control!=identifier) {
+ err_print("! Definition flushed, must start with identifier");
+@.Definition flushed...@>
+ continue;
+ }
+ app_repl(((a=id_lookup(id_first,id_loc,0)-name_dir) / 0400)+0200);
+ /* append the lhs */
+ app_repl(a % 0400);
+ if (*loc!='(') { /* identifier must be separated from replacement text */
+ app_repl(string); app_repl(' '); app_repl(string);
+ }
+ scan_repl(macro);
+ cur_text->text_link=macro;
+}
+
+@ If the section name is not followed by \.{=} or \.{+=}, no \CEE/
+code is forthcoming: the section is being cited, not being
+defined. This use is illegal after the definition part of the
+current section has started, except inside a comment, but
+\.{CTANGLE} does not enforce this rule; it simply ignores the offending
+section name and everything following it, up to the next significant
+control code.
+
+@<If section is not being defined, |continue| @>=
+while ((next_control=get_next())=='+'); /* allow optional \.{+=} */
+if (next_control!='=' && next_control!=eq_eq)
+ continue;
+
+@ @<Scan the \CEE/...@>=
+@<Insert the section number into |tok_mem|@>@;
+scan_repl(section_name); /* now |cur_text| points to the replacement text */
+@<Update the data structure so that the replacement text is accessible@>@;
+
+@ @<Insert the section number...@>=
+store_two_bytes((sixteen_bits)(0150000+section_count));
+ /* |0150000==0320*0400| */
+
+@ @<Update the data...@>=
+if (p==name_dir||p==0) { /* unnamed section, or bad section name */
+ (last_unnamed)->text_link=cur_text-text_info; last_unnamed=cur_text;
+}
+else if (p->equiv==(void *)text_info) p->equiv=(void *)cur_text;
+ /* first section of this name */
+else {
+ q=(text_pointer)p->equiv;
+ while (q->text_link<section_flag)
+ q=q->text_link+text_info; /* find end of list */
+ q->text_link=cur_text-text_info;
+}
+cur_text->text_link=section_flag;
+ /* mark this replacement text as a nonmacro */
+
+@ @c
+static void
+phase_one(void) {
+ phase=1;
+ section_count=0;
+ reset_input();
+ skip_limbo();
+ while (!input_has_ended) scan_section();
+ check_complete();
+ phase=2;
+}
+
+@ @<Predec...@>=@+static void phase_one(void);
+
+@ Only a small subset of the control codes is legal in limbo, so limbo
+processing is straightforward.
+
+@c
+static void
+skip_limbo(void)
+{
+ char c;
+ while (true) {
+ if (loc>limit && get_line()==false) return;
+ *(limit+1)='@@';
+ while (*loc!='@@') loc++;
+ if (loc++<=limit) {
+ c=*loc++;
+ if (ccode[(eight_bits)c]==new_section) break;
+ switch (ccode[(eight_bits)c]) {
+ case translit_code: @<Read in transliteration of a character@>@; break;
+ case format_code: case '@@': break;
+ case control_text: if (c=='q' || c=='Q') {
+ while ((c=skip_ahead())=='@@');
+ if (*(loc-1)!='>')
+ err_print("! Double @@ should be used in control text");
+@.Double @@ should be used...@>
+ break;
+ } /* otherwise fall through */
+ default: err_print("! Double @@ should be used in limbo");
+@.Double @@ should be used...@>
+ }
+ }
+ }
+}
+
+@ @<Predecl...@>=@+static void skip_limbo(void);
+
+@ @<Read in transliteration of a character@>=
+ while(xisspace(*loc)&&loc<limit) loc++;
+ loc+=3;
+ if (loc>limit || !xisxdigit(*(loc-3)) || !xisxdigit(*(loc-2)) @|
+ || (*(loc-3)>='0' && *(loc-3)<='7') || !xisspace(*(loc-1)))
+ err_print("! Improper hex number following @@l");
+@.Improper hex number...@>
+ else {
+ unsigned int i;
+ char *beg;
+ sscanf(loc-3,"%x",&i);
+ while(xisspace(*loc)&&loc<limit) loc++;
+ beg=loc;
+ while(loc<limit&&(xisalpha(*loc)||xisdigit(*loc)||*loc=='_')) loc++;
+ if (loc-beg>=translit_length)
+ err_print("! Replacement string in @@l too long");
+@.Replacement string in @@l...@>
+ else{
+ strncpy(translit[i-0200],beg,(size_t)(loc-beg));
+ translit[i-0200][loc-beg]='\0';
+ }
+ }
+
+@ Because on some systems the difference between two pointers is a |ptrdiff_t|
+but not an |int|, we use \.{\%ld} to print these quantities.
+
+@c
+void
+print_stats(void) {
+ puts("\nMemory usage statistics:");
+ printf("%ld names (out of %ld)\n",
+ (ptrdiff_t)(name_ptr-name_dir),(long)max_names);
+ printf("%ld replacement texts (out of %ld)\n",
+ (ptrdiff_t)(text_ptr-text_info),(long)max_texts);
+ printf("%ld bytes (out of %ld)\n",
+ (ptrdiff_t)(byte_ptr-byte_mem),(long)max_bytes);
+ printf("%ld tokens (out of %ld)\n",
+ (ptrdiff_t)(tok_ptr-tok_mem),(long)max_toks);
+}
+
+@** Index.
+Here is a cross-reference table for \.{CTANGLE}.
+All sections in which an identifier is
+used are listed with that identifier, except that reserved words are
+indexed only when they appear in format definitions, and the appearances
+of identifiers in section names are not indexed. Underlined entries
+correspond to where the identifier was declared. Error messages and
+a few other things like ``ASCII code dependencies'' are indexed here too.