% 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.3 --- May 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.3)}\def\topofcontents{\null\vfill \centerline{\titlefont The {\ttitlefont CTANGLE} processor} \vskip 15pt \centerline{(Version 4.3)} \vfill}\def\botofcontents{\vfill\noindentCopyright \copyright\ 1987, 1990, 1993, 2000 Silvio Levy and Donald E. Knuth\bigskip\noindentPermission is granted to make and distribute verbatim copies of thisdocument provided that the copyright notice and this permission noticeare preserved on all copies. \smallskip\noindentPermission is granted to copy and distribute modified versions of thisdocument under the conditions for verbatim copying, provided that theentire resulting derived work is given a different name and distributedunder 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 toNelson 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.3)" @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 intwo phases: First it reads the source file, saving the \CEE/ code incompressed form; then it shuffles and outputs the code. Please read the documentation for \.{common}, the set of routines commonto \.{CTANGLE} and \.{CWEAVE}, before proceeding further. @cint 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 mustbe included in both |"ctangle.w"| and |"cweave.w"|. It appears infile |"common.h"|, which is also included in |"common.w"| to propagatepossible 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 textsfor sections. Allocation is sequential, since things are deleted onlyduring 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 aninteger |text_link|, which, as we shall see later, is used to connectpieces of text that have the same name. All the \&{text}s are stored inthe array |text_info|, and we use a \&{text\_pointer} variable to referto them. The first position of |tok_mem| that is unoccupied byreplacement 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 itsreplacement 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|: @cboolean names_match(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) /* 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 thatthe loader won't complain of its absence. @cvoidinit_node(name_pointer node){ node->equiv=(void *)text_info;}voidinit_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 inthese texts are called `tokens'; some tokens occupy two consecutiveeight-bit byte positions, and the others take just one byte. If $p$ points to a replacement text, |p->tok_start| is the |tok_mem| positionof 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 replacementtext 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; suchlinks are created when several sections have \CEE/ texts with the samename, 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 macro 0@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=macro; @ If the first byte of a token is less than |0200|, the token occupies asingle byte. Otherwise we make a sixteen-bit token by combining two consecutivebytes |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 sectionin 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 canuse them for special purposes. The following symbolic names are used: \yskip \hang |string| denotes the beginning or end of a stringor a verbatim construction. \hang |constant| denotes a numerical constant. \hang |join| denotes the concatenation of adjacent items with no spaceor line breaks allowed between them (the \.{@@\&} operation of \.{CWEB}).@^ASCII code dependencies@> @d string 02 /* takes the place of ASCII \.{STX} */@d constant 03 /* takes the place of ASCII \.{ETX} */@d join 0177 /* takes the place of ASCII \.{DEL} */@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. @cstatic voidstore_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 trackof what is going on at different ``levels'' as the sections are beingwritten out. Entries on this stack have five parts: \yskip\hang |end_field| is the |tok_mem| location where the replacementtext of a particular level will end; \hang |byte_field| is the |tok_mem| location from which the next tokenon 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 readat 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 toquite frequently, so they are stored in a separate place instead of inthe |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 arecurrently in progress. The end of all output occurs when the stack isempty, 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 followinginitialization steps. We may assume that |text_info->text_link| is nonzero,since it points to the \CEE/ text in the first unnamed section that generatescode; if there are no such sections, there is nothing to output, and anerror 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 getthe new one going. We assume that the \CEE/ compiler can copy structures.@^system dependencies@> @cstatic voidpush_level( /* suspends the current level */name_pointer p){ 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| subroutinedoes the right thing: It either moves to the continuation of this replacementtext or returns the state to the most recently stacked level. @cstatic voidpop_level( /* do this when |cur_byte| reaches |cur_end| */boolean flag) /* |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-levelfunction |out_char|. The main purpose of |get_output| is to handle thenecessary 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 thenegative of that value (if ending). (A section number of 0 indicatesnot 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@> @cstatic voidget_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 outputgeneration, but there are two further considerations that have a nontrivialeffect on \.{CTANGLE}'s algorithms. @ First,we want to make sure that the output has spaces and line breaks inthe right places (e.g., not in the middle of a string or a constant or anidentifier, not at a `\.{@@\&}' positionwhere 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 oridentifier, hence a blank space or line break must be inserted if the nextitem is also a number or identifier. \yskip\hang |unbreakable| means that the last item in the buffer was followedby the \.{@@\&} operation that inhibits spaces between it and the next item. \yskip\hang |verbatim| means we're copying only character tokens, andthat they are to be output exactly as stored. This is the case duringstrings, 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|, newlinesare 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 lineto be output. @cstatic voidflush_buffer(void) /* writes one line to output file */{ C_putc('\n'); if (cur_line % 100 == 0 && show_progress) { putchar('.'); 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 outputon 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, wedecrement |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, orLines 1–500 of 1560. Load more
% 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.3 --- May 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.3)} \def\topofcontents{\null\vfill \centerline{\titlefont The {\ttitlefont CTANGLE} processor} \vskip 15pt \centerline{(Version 4.3)} \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.3)" @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( 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) /* 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 macro 0 @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=macro; @ 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 |string| denotes the beginning or end of a string or a verbatim construction. \hang |constant| denotes a numerical constant. \hang |join| denotes the concatenation of adjacent items with no space or line breaks allowed between them (the \.{@@\&} operation of \.{CWEB}). @^ASCII code dependencies@> @d string 02 /* takes the place of ASCII \.{STX} */ @d constant 03 /* takes the place of ASCII \.{ETX} */ @d join 0177 /* takes the place of ASCII \.{DEL} */ @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( /* suspends the current level */ name_pointer p) { 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( /* do this when |cur_byte| reaches |cur_end| */ boolean flag) /* |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) { putchar('.'); 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