*** ../cweb3.61/cweave.w Mon Jul 24 00:16:43 2000 --- ctwill.w Mon Jul 24 08:20:43 2000 *************** *** 1,10 **** ! % 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 3.61 --- July 2000 - % (essentially the same as version 3.6, which added - % recently introduced features of standard C++ to version 3.4) % Copyright (C) 1987,1990,1993,2000 Silvio Levy and Donald E. Knuth --- 1,6 ---- ! % This program by Don Knuth is based on CWEAVE by Levy and Knuth. ! % It's somewhat flaky, so you probably shouldn't try to use it. % Version 3.61 --- July 2000 % Copyright (C) 1987,1990,1993,2000 Silvio Levy and Donald E. Knuth *************** *** 17,23 **** % entire resulting derived work is given a different name and distributed % under the terms of a permission notice identical to this one. ! % 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 --- 13,19 ---- % entire resulting derived work is given a different name and distributed % under the terms of a permission notice identical to this one. ! % Here is TeX material that gets inserted after \input ctwimac \def\hang{\hangindent 3em\indent\ignorespaces} \def\pb{$\.|\ldots\.|$} % C brackets (|...|) \def\v{\char'174} % vertical (|) in typewriter font *************** *** 29,37 **** \def\skipxTeX{\\{skip\_\TEX/}} \def\copyxTeX{\\{copy\_\TEX/}} ! \def\title{CWEAVE (Version 3.61)} \def\topofcontents{\null\vfill ! \centerline{\titlefont The {\ttitlefont CWEAVE} processor} \vskip 15pt \centerline{(Version 3.61)} \vfill} --- 25,33 ---- \def\skipxTeX{\\{skip\_\TEX/}} \def\copyxTeX{\\{copy\_\TEX/}} ! \def\title{CTWILL (Version 3.61)} \def\topofcontents{\null\vfill ! \centerline{\titlefont The {\ttitlefont CTWILL} processor} \vskip 15pt \centerline{(Version 3.61)} \vfill} *************** *** 54,71 **** @s not_eq normal @q unreserve a C++ keyword @> @** Introduction. ! This is the \.{CWEAVE} program by Silvio Levy and Donald E. Knuth, ! based on \.{WEAVE} by Knuth. ! We are thankful to Steve Avery, ! Nelson Beebe, Hans-Hermann Bode (to whom the original \CPLUSPLUS/ adaptation ! is due), Klaus Guntermann, Norman Ramsey, Tomas Rokicki, Joachim Schnitter, ! Joachim Schrod, Lee Wittenberg, Saroj Mahapatra, Cesar Augusto Rorato ! Crusius, and others who have contributed improvements. ! The ``banner line'' defined here should be changed whenever \.{CWEAVE} ! is modified. ! @d banner "This is CWEAVE (Version 3.61)\n" @c @@/ @h --- 50,73 ---- @s not_eq normal @q unreserve a C++ keyword @> @** Introduction. ! This is the \.{CTWILL} program by D. E. Knuth, based ! on \.{CWEAVE} by Silvio Levy and D.~E. Knuth. It is also based on ! \.{TWILL}, a private \.{WEB} program that Knuth wrote to produce ! Volumes B and~D of {\sl Computers {\char`\&} Typesetting\/} in 1985. ! \.{CTWILL} was hacked together hastily in June, 1992, to generate pages for ! Knuth's book about the Stanford GraphBase, and updated even more hastily ! in March, 1993 to generate final copy for that book. The main idea was to ! extend \.{CWEAVE} so that ``mini-indexes'' could appear. ! No time was available to make \.{CTWILL} into a refined or complete system, ! nor even to fully update the program documentation below. Subsequent changes ! were made only to maintain compatibility with \.{CWEAVE}. Further information ! can be found in Knuth's article ``Mini-indexes for literate programs,'' ! reprinted in {\sl Digital Typography\/} (1999), 225--245. ! The ``banner line'' defined here should be changed whenever \.{CTWILL} is ! modified. The version number parallels the corresponding version of \.{CWEAVE}. ! @d banner "This is CTWILL (Version 3.61)\n" @c @@/ @h *************** *** 86,91 **** --- 88,253 ---- extern int strncmp(); /* compare up to $n$ string characters */ extern char* strncpy(); /* copy up to $n$ string characters */ + @ Here is a sort of user manual for \.{CTWILL}---which is exactly like + \.{CWEAVE} except that it produces much better documentation, for which you + must work harder. As with \.{CWEAVE}, input comes from a source file + \.{foo.w} and from an optional (but now almost mandatory) change file + \.{foo.ch}; output goes to \.{foo.tex}, \.{foo.idx}, and \.{foo.scn}. + Unlike \.{CWEAVE}, there is an additional output file, \.{foo.aux}, + which records all nonexternal definitions. The \.{.aux} file also + serves as an input file on subsequent runs. You should run \.{CTWILL} + twice, once to prime the pump and once to get decent answers. + + Moreover, you must run the output twice through \TeX. (This double duplicity + suggested the original name \.{TWILL}.) After `\.{tex} \.{foo}' you + will have output that looks like final pages except that the entries + of mini-indexes won't be alphabetized. \TeX\ will say `This is the first + pass', and it will produce a weird file called \.{foo.ref}. Say + $$\.{refsort < foo.ref > foo.sref}$$ + and then another `\.{tex} \.{foo}' will produce alphabetized output. + While \TeX\ runs it emits messages filled with numeric data, indicating how + much space is consumed by each program section. If you can decipher these + numbers (see \.{ctwimac.tex}), you can use them to fine-tune the page + layout. You might be tempted to do fine tuning by editing \.{foo.tex} + directly, but it's better to incorporate all changes into \.{foo.ch}. + + The mini-indexes list identifiers that are used but not defined on + each two-page spread. At the end of each section, \.{CTWILL} gives + \TeX\ a list of identifiers used in that section and information + about where they are defined. The macros in \.{ctwimac.tex} figure out + which identifiers should go in each mini-index, based on how the pages + break. (Yes, those macros are pretty hairy.) + + The information that \.{CTWILL} concocts from \.{foo.w} is not always + correct. Sometimes you'll use an identifier that you don't want + indexed; for example, your exposition might talk about |f(x)| when you + don't mean to refer to program variables |f| or |x|. Sometimes you'll + use an identifier that's defined in a header file, unknown to + \.{CTWILL}. Sometimes you'll define a single identifier in several + different places, and \.{CTWILL} won't know which definition to choose. + But all is not lost. \.{CTWILL} guesses right most of the time, and you can + give it the necessary hints in other places via your change file. + + If you think it's easy to write a completely automatic system that doesn't + make \.{CTWILL}'s mistakes and doesn't depend so much on change files, + please do so. + + \.{CTWILL} uses a very simple method to generate mini-index info. By + understanding this method, you will understand how to fix it when things + go wrong. Every identifier has a current ``meaning,'' consisting of its + abstract type and the number of the section in which it was most recently + defined. For example, if your \Cee\ program says `|char *s|' in section~3, + the meaning of~|s| gets changed to `\&{char} $*$, \S3' while \.{CTWILL} + is processing that section. If you refer to~|s| in section~10, and if + |s|~hasn't been redefined in the meantime, and if section~10 doesn't + wind up on the same two-page spread as section~3, the mini-index generated + by section~10 will say ``|s|: \&{char}~$*$, \S3.'' + + The current meaning of every identifier is initially `\.{\\uninitialized}'. + Then \.{CTWILL} reads the \.{.aux} file for your job, if any; this + \.{.aux} file contains all definitions of new meanings in the previous + run, so it tells \.{CTWILL} about definitions that will be occurring + in the future. If all identifiers have a unique definition, they will + have a unique and appropriate meaning in the mini-indexes. + + But some identifiers, like parameters to procedures, may be defined + several times. Others may not be defined at all, because they are + defined elsewhere and mentioned in header files included by the \Cee\ + preprocessor. To solve this problem, \.{CTWILL} provides mechanisms by which + the current meaning of an identifier can be temporarily or permanently + changed. + + For example, the operation + $$\.{@@\$s \{FOO\}3 \\\&\{char\} \$*\$@@>}$$ + changes the current meaning of |s| to the \TeX\ output of `\.{\\\&\{char\}} + \.{\$*\$}' in section~3 of program {\sc FOO}. All entries in the \.{.aux} + file are expressed in the form of this \.{@@\$} operator; therefore you + can use a text editor to paste such entries into a \.{.ch} file, whenever + you want to tell \.{CTWILL} about definitions that are out of order + or from other programs. + + Before reading the \.{.aux} file, \.{CTWILL} actually looks for a file + called \.{system.bux}, which will be read if present. And after + \.{foo.aux}, a third possibility is \.{foo.bux}. The general + convention is to put definitions of system procedures such as |printf| + into \.{system.bux}, and to put definitions found in specifically + foo-ish header files into \.{foo.bux}. Like the \.{.aux} + files, \.{.bux} files should contain only \.{@@\$} specifications; + this rule corresponds to the fact that `bux' is the plural of `\$'. + The \.{.bux} files may also contain \.{@@i} includes. + + A companion operation \.{@@\%} signifies that all \.{@@\$} + specifications from the present point to the beginning of the next + section will define {\it temporary\/} meanings instead of permanent + ones. Temporary meanings are placed into the + mini-index of the current section; the permanent (current) meaning of + the identifier will not be changed, nor will it appear in the + mini-index of the section. If several temporary meanings are assigned + to the same identifier in a section, all will appear in the mini-index. + Each \.{@@\%} toggles the temporary/permanent convention; thus, after + an even number of \.{@@\%} operations in a section, meanings specified + by \.{@@\$} are permanent. + + The operation \.{@@-} followed by an identifier followed by \.{@@>} + specifies that the identifier should not generate a mini-index entry + in the current section (unless, of course, a temporary meaning is assigned). + + If \.{@@-foo@@>} appears in a section where a new permanent meaning is + later defined by the semantics of~\Cee, the current meaning of \\{foo} + will not be redefined; moreover, this current meaning, which may have + been changed by \.{@@\$foo ...@@>}, will also be written to the + \.{.aux} file. Therefore you can control what \.{CTWILL} outputs; you + can keep it from repeatedly contaminating the \.{.aux} file with + things you don't like. + + The meaning specified by \.{@@\$...@@>} generally has four components: + an identifier (followed by space), a program name (enclosed in braces), + a section number (followed by space), and a \TeX\ part. The \TeX\ part + must have fewer than 50 characters. If the \TeX\ part starts + with `\.=', the mini-index entry will contain an equals sign instead + of a colon; for example, + $$\.{@@\$buf\_size \{PROG\}10 =\\T\{200\}@@>}$$ + generates either `$\\{buf\_size}=200$, \S10' or + `$\\{buf\_size}=200$, {\sc PROG}\S10', depending on whether + `{\sc PROG}' is or isn't the title of the current program. If the + \TeX\ part is `\.{\\zip}', the mini-index entry will contain neither + colon nor equals, just a comma. The program name and section number + can also be replaced by a string. For example, + $$\.{@@\$printf "" \\zip@@>}$$ + will generate a mini-index entry like `\\{printf}, \.{}.'. + + A special ``proofmode'' is provided so that you can check \.{CTWILL}'s + conclusions about cross-references. Run \.{CTWILL} with the + flag \.{+P}, and \TeX\ will produce a specially formatted document + ({\it without\/} mini-indexes) + in which you can check that your specifications are correct. + You should always do this before generating mini-indexes, because + mini-indexes can mask errors if page breaks are favorable but the + errors might reveal themselves later after your program has changed. + The proofmode output is much easier to check than the mini-indexes themselves. + + The control code \.{@@r} or \.{@@R} causes \.{CTWILL} to emit + the \TeX\ macro `\.{\\shortpage}' just before starting the next + section of the program. This causes the section to appear at the top of + a right-hand page, if it would ordinarily have appeared near the bottom + of a left-hand page and split across the pages. (The \.{\\shortpage} macro + is fragile and should be used only in cases where it will not mess up + the output; insert it only when fine-tuning a set of pages.) If the + next section is a starred section, the behavior is slightly different + (but still fragile): The starred section will either be postponed to + a left-hand page, if it normally would begin on a right-hand page, + or vice versa. In other words, \.{@@r@@*} inverts the left/right logic. + + \.{CTANGLE} does not recognize the operations \.{@@\$}, \.{@@\%}, \.{@@-}, + and \.{@@r}, which are unique to \.{CTWILL}. But that is no problem, + since you use them only in change files set up for book publishing, + which are quite different from the change files you set up for tangling. + + (End of user manual. We now resume the program for \.{CWEAVE}, with occasional + outbursts of new code.) + + @d max_tex_chars 50 /* limit on the \TeX\ part of a meaning */ + @ \.{CWEAVE} has a fairly straightforward outline. It operates in three phases: First it inputs the source file and stores cross-reference data, then it inputs the source once again and produces the \TEX/ output *************** *** 202,211 **** @ We keep track of the current section number in |section_count|, which is the total number of sections that have started. Sections which have been altered by a change file entry have their |changed_section| flag ! turned on during the first phase. ! ! @= ! boolean change_exists; /* has any section changed? */ @ The other large memory area in \.{CWEAVE} keeps the cross-reference data. All uses of the name |p| are recorded in a linked list beginning at --- 364,370 ---- @ We keep track of the current section number in |section_count|, which is the total number of sections that have started. Sections which have been altered by a change file entry have their |changed_section| flag ! turned on during the first phase---NOT! @ The other large memory area in \.{CWEAVE} keeps the cross-reference data. All uses of the name |p| are recorded in a linked list beginning at *************** *** 242,247 **** --- 401,517 ---- xref_pointer xref_ptr; /* the largest occupied position in |xmem| */ sixteen_bits xref_switch,section_xref_switch; /* either zero or |def_flag| */ + @ \.{CTWILL} also has special data structures to keep track of current + and temporary meanings. These structures were not designed for maximum + efficiency; they were designed to be easily grafted into \.{CWEAVE}'s + existing code without major surgery. + + @d max_meanings 100 /* max temporary meanings per section */ + @d max_titles 100 /* max distinct program or header names in meanings */ + + @= + typedef struct { + name_pointer id; /* identifier whose meaning is being recorded */ + sixteen_bits prog_no; /* title of program or header in which defined */ + sixteen_bits sec_no; /* section number in which defined */ + char tex_part[max_tex_chars]; /* \TeX\ part of meaning */ + } meaning_struct; + + @ @= + struct perm_meaning { + meaning_struct perm; /* current meaning of an identifier */ + int stamp; /* last section number in which further output suppressed */ + struct perm_meaning *link; /* another meaning to output in this section */ + } cur_meaning[max_names]; /* the current ``permanent'' meanings */ + struct perm_meaning *top_usage; /* first meaning to output in this section */ + meaning_struct temp_meaning_stack[max_meanings]; /* the current ``temporary'' meanings */ + meaning_struct *temp_meaning_ptr; /* first available slot in |temp_meaning_stack| */ + meaning_struct *max_temp_meaning_ptr; /* its maximum value so far */ + name_pointer title_code[max_titles]; /* program names seen so far */ + name_pointer *title_code_ptr; /* first available slot in |title_code| */ + char ministring_buf[max_tex_chars]; /* \TeX\ code being generated */ + char *ministring_ptr; /* first available slot in |ministring_buf| */ + boolean ms_mode; /* are we outputting to |ministring_buf|? */ + + @ @= + max_temp_meaning_ptr=temp_meaning_stack; + title_code_ptr=title_code; + ms_mode=0; + + @ Here's a routine that converts a program title from the buffer + into an internal number for the |prog_no| field of a meaning. + It advances |loc| past the title found. + + @c sixteen_bits title_lookup() + { + char *first,*last; /* boundaries */ + int balance; /* excess of left over right */ + register name_pointer *p; + first=loc; + if (*loc=='"') { + while (++loc<=limit && *loc!='"') if (*loc=='\\') loc++; + } else if (*loc=='{') { + balance=1; + while (++loc<=limit) { + if (*loc=='}' && --balance==0) break; + if (*loc=='{') balance++; + } + } else err_print("! Title should be enclosed in braces or doublequotes"); + last=++loc; + if (last>limit) err_print("! Title name didn't end"); + if (title_code_ptr==&title_code[max_titles]) overflow("titles"); + *title_code_ptr=id_lookup(first,last,title); + for (p=title_code;;p++) if (*p==*title_code_ptr) break; + if (p==title_code_ptr) title_code_ptr++; + return p-title_code; + } + + @ @= + if (title_code_ptr==title_code) { /* no \.{\\def\\title} found in limbo */ + char *saveloc=loc,*savelimit=limit; + loc=limit+1; limit=loc; + *limit++='{'; + strncpy(limit,tex_file_name,strlen(tex_file_name)-4); + limit+=strlen(tex_file_name)-4; + *limit++='}'; + title_lookup(); + loc=saveloc; limit=savelimit; + } + + @ The |new_meaning| routine changes the current ``permanent meaning'' + when an identifier is redeclared. It gets the |tex_part| from + |ministring_buf|. + + @c + void + new_meaning(p) + name_pointer p; + { + struct perm_meaning *q=p-name_dir+cur_meaning; + ms_mode=0; + if (q->stamp!=section_count) { + if (*(ministring_ptr-1)==' ') ministring_ptr--; + if (ministring_ptr>=&ministring_buf[max_tex_chars]) + strcpy(ministring_buf,"\\zip"); /* ignore |tex_part| if too long */ + @.\\zip@> + else *ministring_ptr='\0'; + q->perm.prog_no=0; /* |q->perm.id=p| */ + q->perm.sec_no=section_count; + strcpy(q->perm.tex_part,ministring_buf); + } + @; + } + + @ @= + {@+int n=q->perm.prog_no; + fprintf(aux_file,"@@$%.*s %.*s",@| + (p+1)->byte_start-p->byte_start,p->byte_start,@| + (title_code[n]+1)->byte_start-title_code[n]->byte_start, + title_code[n]->byte_start); + if (*(title_code[n]->byte_start)=='{') fprintf(aux_file,"%d",q->perm.sec_no); + fprintf(aux_file," %s@@>\n",q->perm.tex_part); + } + @ A section that is used for multi-file output (with the \.{@@(} feature) has a special first cross-reference whose |num| field is |file_flag|. *************** *** 384,390 **** --- 654,666 ---- name_pointer p; eight_bits t; { + struct perm_meaning *q=p-name_dir+cur_meaning; p->ilk=t; p->xref=(char*)xmem; + q->stamp=0; + q->link=NULL; + q->perm.id=p; + q->perm.prog_no=q->perm.sec_no=0; + strcpy(q->perm.tex_part,"\\uninitialized"); } void *************** *** 434,440 **** id_lookup("error",NULL,if_like); id_lookup("explicit",NULL,int_like); id_lookup("export",NULL,int_like); ! id_lookup("extern",NULL,int_like); id_lookup("FILE",NULL,raw_int); id_lookup("float",NULL,raw_int); id_lookup("for",NULL,for_like); --- 710,716 ---- id_lookup("error",NULL,if_like); id_lookup("explicit",NULL,int_like); id_lookup("export",NULL,int_like); ! ext_loc=id_lookup("extern",NULL,int_like)-name_dir; id_lookup("FILE",NULL,raw_int); id_lookup("float",NULL,raw_int); id_lookup("for",NULL,for_like); *************** *** 446,452 **** id_lookup("ifndef",NULL,if_like); id_lookup("include",NULL,if_like); id_lookup("inline",NULL,int_like); ! id_lookup("int",NULL,raw_int); id_lookup("jmp_buf",NULL,raw_int); id_lookup("ldiv_t",NULL,raw_int); id_lookup("line",NULL,if_like); --- 722,728 ---- id_lookup("ifndef",NULL,if_like); id_lookup("include",NULL,if_like); id_lookup("inline",NULL,int_like); ! int_loc=id_lookup("int",NULL,raw_int)-name_dir; id_lookup("jmp_buf",NULL,raw_int); id_lookup("ldiv_t",NULL,raw_int); id_lookup("line",NULL,if_like); *************** *** 503,508 **** --- 779,787 ---- id_lookup("TeX",NULL,custom); id_lookup("make_pair",NULL,func_template); + @ @= + sixteen_bits int_loc, ext_loc; /* locations of special reserved words */ + @* Lexical scanning. Let us now consider the subroutines that read the \.{CWEB} source file and break it into meaningful units. There are four such procedures: *************** *** 533,556 **** @d xref_typewriter 0205 /* control code for `\.{@@.}' */ @d TeX_string 0206 /* control code for `\.{@@t}' */ @f TeX_string TeX ! @d ord 0207 /* control code for `\.{@@'}' */ ! @d join 0210 /* control code for `\.{@@\&}' */ ! @d thin_space 0211 /* control code for `\.{@@,}' */ ! @d math_break 0212 /* control code for `\.{@@\v}' */ ! @d line_break 0213 /* control code for `\.{@@/}' */ ! @d big_line_break 0214 /* control code for `\.{@@\#}' */ ! @d no_line_break 0215 /* control code for `\.{@@+}' */ ! @d pseudo_semi 0216 /* control code for `\.{@@;}' */ ! @d macro_arg_open 0220 /* control code for `\.{@@[}' */ ! @d macro_arg_close 0221 /* control code for `\.{@@]}' */ ! @d trace 0222 /* control code for `\.{@@0}', `\.{@@1}' and `\.{@@2}' */ ! @d translit_code 0223 /* control code for `\.{@@l}' */ ! @d output_defs_code 0224 /* control code for `\.{@@h}' */ ! @d format_code 0225 /* control code for `\.{@@f}' and `\.{@@s}' */ ! @d definition 0226 /* control code for `\.{@@d}' */ ! @d begin_C 0227 /* control code for `\.{@@c}' */ ! @d section_name 0230 /* control code for `\.{@@<}' */ ! @d new_section 0231 /* control code for `\.{@@\ }' and `\.{@@*}' */ @ Control codes are converted to \.{CWEAVE}'s internal representation by means of the table |ccode|. --- 812,839 ---- @d xref_typewriter 0205 /* control code for `\.{@@.}' */ @d TeX_string 0206 /* control code for `\.{@@t}' */ @f TeX_string TeX ! @d meaning 0207 /* control code for `\.{@@\$}' */ ! @d suppress 0210 /* control code for `\.{@@-}' */ ! @d temp_meaning 0211 /* control code for `\.{@@\%}' */ ! @d right_start 0212 /* control code for `\.{@@r}' */ ! @d ord 0213 /* control code for `\.{@@'}' */ ! @d join 0214 /* control code for `\.{@@\&}' */ ! @d thin_space 0215 /* control code for `\.{@@,}' */ ! @d math_break 0216 /* control code for `\.{@@\v}' */ ! @d line_break 0217 /* control code for `\.{@@/}' */ ! @d big_line_break 0220 /* control code for `\.{@@\#}' */ ! @d no_line_break 0221 /* control code for `\.{@@+}' */ ! @d pseudo_semi 0222 /* control code for `\.{@@;}' */ ! @d macro_arg_open 0224 /* control code for `\.{@@[}' */ ! @d macro_arg_close 0225 /* control code for `\.{@@]}' */ ! @d trace 0226 /* control code for `\.{@@0}', `\.{@@1}' and `\.{@@2}' */ ! @d translit_code 0227 /* control code for `\.{@@l}' */ ! @d output_defs_code 0230 /* control code for `\.{@@h}' */ ! @d format_code 0231 /* control code for `\.{@@f}' and `\.{@@s}' */ ! @d definition 0232 /* control code for `\.{@@d}' */ ! @d begin_C 0233 /* control code for `\.{@@c}' */ ! @d section_name 0234 /* control code for `\.{@@<}' */ ! @d new_section 0235 /* control code for `\.{@@\ }' and `\.{@@*}' */ @ Control codes are converted to \.{CWEAVE}'s internal representation by means of the table |ccode|. *************** *** 578,583 **** --- 861,868 ---- ccode['+']=no_line_break; ccode[';']=pseudo_semi; ccode['[']=macro_arg_open; ccode[']']=macro_arg_close; ccode['\'']=ord; + ccode['$']=meaning; ccode['%']=temp_meaning; ccode['-']=suppress; + ccode['r']=ccode['R']=right_start; @@; @ Users can write *************** *** 599,609 **** @= void skip_limbo(); ! @ @c void skip_limbo() { while(1) { if (loc>limit && get_line()==0) return; *(limit+1)='@@'; while (*loc!='@@') loc++; /* look for '@@', then skip two chars */ if (loc++ <=limit) { int c=ccode[(eight_bits)*loc++]; --- 884,901 ---- @= void skip_limbo(); ! @ We look for a clue about the program's title, because this will become ! part of all meanings. ! ! @c void skip_limbo() { while(1) { if (loc>limit && get_line()==0) return; + if (loc==buffer && strncmp(buffer,"\\def\\title{",11)==0) { + loc=buffer+10; + title_lookup(); /* this program's title will be code zero */ + } *(limit+1)='@@'; while (*loc!='@@') loc++; /* look for '@@', then skip two chars */ if (loc++ <=limit) { int c=ccode[(eight_bits)*loc++]; *************** *** 659,664 **** --- 951,957 ---- |get_next| to take additional actions: \yskip\hang |xref_roman|, |xref_wildcard|, |xref_typewriter|, |TeX_string|, + |meaning|, |suppress|, |verbatim|: The values of |id_first| and |id_loc| will have been set to the beginning and ending-plus-one locations in the buffer. *************** *** 728,734 **** for |ord|, since |get_next| changes |ord| into a string. @d left_preproc ord /* begins a preprocessor command */ ! @d right_preproc 0217 /* ends a preprocessor command */ @= boolean preprocessing=0; /* are we scanning a preprocessor command? */ --- 1021,1027 ---- for |ord|, since |get_next| changes |ord| into a string. @d left_preproc ord /* begins a preprocessor command */ ! @d right_preproc 0223 /* ends a preprocessor command */ @= boolean preprocessing=0; /* are we scanning a preprocessor command? */ *************** *** 894,902 **** case translit_code: err_print("! Use @@l in limbo only"); continue; @.Use @@l in limbo...@> case underline: xref_switch=def_flag; continue; case trace: tracing=c-'0'; continue; case xref_roman: case xref_wildcard: case xref_typewriter: ! case noop: case TeX_string: c=ccode[c]; skip_restricted(); return(c); case section_name: @; case verbatim: @; --- 1187,1198 ---- case translit_code: err_print("! Use @@l in limbo only"); continue; @.Use @@l in limbo...@> case underline: xref_switch=def_flag; continue; + case temp_meaning: temp_switch=1-temp_switch; continue; + case right_start: right_start_switch=1; continue; case trace: tracing=c-'0'; continue; case xref_roman: case xref_wildcard: case xref_typewriter: ! case meaning: case suppress: ! case noop: case TeX_string: c=ccode[(eight_bits)c]; skip_restricted(); return(c); case section_name: @; case verbatim: @; *************** *** 975,981 **** @ @c void skip_restricted() ! { id_first=loc; *(limit+1)='@@'; false_alarm: while (*loc!='@@') loc++; --- 1271,1277 ---- @ @c void skip_restricted() ! { int c=ccode[(eight_bits)*(loc-1)]; id_first=loc; *(limit+1)='@@'; false_alarm: while (*loc!='@@') loc++; *************** *** 989,997 **** --- 1285,1346 ---- if (*loc++!='>') err_print("! Control codes are forbidden in control text"); @.Control codes are forbidden...@> + if (c==meaning && phase==2) @@; + else if (c==suppress && phase==2) @; } } + @ @= + { char *first=id_first,*last=id_loc; + while (xisspace(*first)) first++; + while (xisspace(*(last-1))) last--; + if (firststamp=section_count; /* this is what actually suppresses output */ + } + } + + @ @= + { char *first=id_first; + while (xisspace(*first)) first++; + loc=first; + while (xisalpha(*loc)||xisdigit(*loc)||*loc=='_') loc++; + if (*loc++!=' ') + err_print("! Identifier in meaning should be followed by space"); + else { name_pointer p=id_lookup(first,loc-1,normal); + sixteen_bits t; int n=0; + t=title_lookup(); + if (*(loc-1)=='}') + while (xisdigit(*loc)) n=10*n+(*loc++)-'0'; + if (*loc++!=' ') + err_print("! Location in meaning should be followed by space"); + else @; + } + loc=id_loc+2; + } + + @ @= + { meaning_struct *m; + struct perm_meaning *q=p-name_dir+cur_meaning; + if (temp_switch) { + m=temp_meaning_ptr++; + if (temp_meaning_ptr>max_temp_meaning_ptr) { + if (temp_meaning_ptr>&temp_meaning_stack[max_meanings]) + overflow("temp meanings"); + max_temp_meaning_ptr=temp_meaning_ptr; + } + } else m=&(q->perm); + m->id=p; + m->prog_no=t; + m->sec_no=n; + if (id_loc-loc>=max_tex_chars) strcpy(m->tex_part,"\\zip"); + @.\\zip@> + else { char *q=m->tex_part; + while (loc; - changed_section[section_count]=change_exists; - /* the index changes if anything does */ phase=2; /* prepare for second phase */ @; } --- 1378,1387 ---- void phase_one() { phase=1; reset_input(); section_count=0; ! skip_limbo(); ! @; while (!input_has_ended) @; phase=2; /* prepare for second phase */ @; } *************** *** 1041,1048 **** @ @= { if (++section_count==max_sections) overflow("section number"); - changed_section[section_count]=changing; - /* it will become 1 if any line changes */ if (*(loc-1)=='*' && show_progress) { printf("*%d",section_count); update_terminal; /* print a progress report */ --- 1389,1394 ---- *************** *** 1050,1056 **** @; @; @; - if (changed_section[section_count]) change_exists=1; } @ The |C_xref| subroutine stores references to identifiers in --- 1396,1401 ---- *************** *** 1138,1143 **** --- 1483,1489 ---- case trace: tracing=*(loc-1)-'0'; continue; case '|': C_xref(section_name); break; case xref_roman: case xref_wildcard: case xref_typewriter: + case meaning: case suppress: case noop: case section_name: loc-=2; next_control=get_next(); /* scan to \.{@@>} */ if (next_control>=xref_roman && next_control<=xref_typewriter) { *************** *** 1365,1375 **** @ In particular, the |finish_line| procedure is called near the very beginning of phase two. We initialize the output variables in a slightly tricky way so that the first line of the output file will be ! `\.{\\input cwebmac}'. @= out_ptr=out_buf+1; out_line=1; active_file=tex_file; ! *out_ptr='c'; tex_printf("\\input cwebma"); @ When we wish to append one character |c| to the output buffer, we write `|out(c)|'; this will cause the buffer to be emptied if it was already --- 1711,1725 ---- @ In particular, the |finish_line| procedure is called near the very beginning of phase two. We initialize the output variables in a slightly tricky way so that the first line of the output file will be ! `\.{\\input ctwimac}'. Or, if the user has specified proofing by ! saying \.{+P} on the command line, it's `\.{\\input proofmac}', ! a set of macros used when debugging mini-index entries. @= out_ptr=out_buf+1; out_line=1; active_file=tex_file; ! *out_ptr='c'; ! if (flags['P']) tex_printf("\\input proofma"); ! else tex_printf("\\input ctwima"); @ When we wish to append one character |c| to the output buffer, we write `|out(c)|'; this will cause the buffer to be emptied if it was already *************** *** 1379,1385 **** A line break will occur at a space or after a single-nonletter \TEX/ control sequence. ! @d out(c) {if (out_ptr>=out_buf_end) break_out(); *(++out_ptr)=c;} @c void --- 1729,1743 ---- A line break will occur at a space or after a single-nonletter \TEX/ control sequence. ! @d out(c) ! {if (ms_mode) { /* outputting to |ministring_buf| */ ! if (ministring_ptr<&ministring_buf[max_tex_chars]) ! *ministring_ptr++=c; ! } else { ! if (out_ptr>=out_buf_end) break_out(); ! *(++out_ptr)=c; ! } ! } @c void *************** *** 1435,1442 **** @ Here is a macro that outputs a section number in decimal notation. The number to be converted by |out_section| is known to be less than ! |def_flag|, so it cannot have more than five decimal digits. If ! the section is changed, we output `\.{\\*}' just after the number. @c void --- 1793,1799 ---- @ Here is a macro that outputs a section number in decimal notation. The number to be converted by |out_section| is known to be less than ! |def_flag|, so it cannot have more than five decimal digits. @c void *************** *** 1445,1452 **** { char s[6]; sprintf(s,"%d",n); out_str(s); - if(changed_section[n]) out_str ("\\*"); - @.\\*@> } @ The |out_name| procedure is used to output an identifier or index --- 1802,1807 ---- *************** *** 1502,1507 **** --- 1857,1863 ---- case format_code: if (get_next()==identifier) get_next(); if (loc>=limit) get_line(); /* avoid blank lines in output */ break; /* the operands of \.{@@s} are ignored on this pass */ + case right_start: right_start_switch=1; break; default: err_print("! Double @@ should be used in limbo"); @.Double @@ should be used...@> out('@@'); *************** *** 1711,1716 **** --- 2067,2073 ---- @d new_exp 60 /* \&{new} and a following type identifier */ @d begin_arg 61 /* \.{@@[} */ @d end_arg 62 /* \.{@@]} */ + @d title 63 /* program name or header name in a ``meaning'' */ @= char cat_name[256][12]; *************** *** 2101,2106 **** --- 2458,2464 ---- @= scrap scrap_info[max_scraps]; /* memory array for scraps */ + scrap null_scrap; /* a scrap with empty translation */ scrap_pointer scrap_info_end=scrap_info+max_scraps -1; /* end of |scrap_info| */ scrap_pointer pp; /* current position for reducing productions */ scrap_pointer scrap_base; /* beginning of the current scrap sequence */ *************** *** 2110,2115 **** --- 2468,2474 ---- scrap_pointer max_scr_ptr; /* largest value assumed by |scrap_ptr| */ @ @= + null_scrap.trans=&tok_start[0]; scrap_base=scrap_info+1; max_scr_ptr=scrap_ptr=scrap_info; *************** *** 2158,2165 **** default: @; } } ! fflush(stdout); } @ @= switch (r) { --- 2517,2528 ---- default: @; } } ! printf("|\n"); fflush(stdout); } + @# + void pr_txt(k) + int k; + { print_text(&tok_start[k]); } @ @= switch (r) { *************** *** 2444,2458 **** occurrence of the identifier that we're making reserved; hence the |for| loop below. @c void make_reserved(p) /* make the first identifier in |p->trans| like |int| */ scrap_pointer p; { sixteen_bits tok_value; /* the name of this identifier, plus its flag*/ ! token_pointer tok_loc; /* pointer to |tok_value| */ ! if ((tok_loc=find_first_ident(p->trans))<=operator_found) ! return; /* this should not happen */ tok_value=*tok_loc; for (;p<=scrap_ptr; p==lo_ptr? p=hi_ptr: p++) { if (p->cat==exp) { --- 2807,2823 ---- occurrence of the identifier that we're making reserved; hence the |for| loop below. + We use the fact that |make_underlined| has been called immediately preceding + |make_reserved|, hence |tok_loc| has been set. + @c + token_pointer tok_loc; /* where the first identifier appears */ void make_reserved(p) /* make the first identifier in |p->trans| like |int| */ scrap_pointer p; { sixteen_bits tok_value; /* the name of this identifier, plus its flag*/ ! if (tok_loc<=operator_found) return; /* this should not happen */ tok_value=*tok_loc; for (;p<=scrap_ptr; p==lo_ptr? p=hi_ptr: p++) { if (p->cat==exp) { *************** *** 2481,2487 **** /* underline the entry for the first identifier in |p->trans| */ scrap_pointer p; { - token_pointer tok_loc; /* where the first identifier appears */ if ((tok_loc=find_first_ident(p->trans))<=operator_found) return; /* this happens, for example, in |case found:| */ xref_switch=def_flag; --- 2846,2851 ---- *************** *** 2533,2538 **** --- 2897,3018 ---- while (r->xlink!=q) {r->num=r->xlink->num; r=r->xlink;} r->num=m; /* everything from |q| on is left undisturbed */ + @ \.{CTWILL} needs the following procedure, which appends tokens of a + translated text until coming to |tok_loc|, then suppresses text that may + appear between parentheses or brackets. The calling routine should set + |ident_seen=0| first. (This is admittedly tricky.) + + @c boolean ident_seen; + boolean app_supp(p) + text_pointer p; + { token_pointer j; + text_pointer q; + if (ident_seen && **p>=tok_flag) { + q=**p-tok_flag+tok_start; + if (**q=='(') { + app('(');@+app('\\');@+app(',');@+app(')'); goto catch14; + } + if (**q=='[') { + app('[');@+app('\\');@+app(',');@+app(']'); goto catch14; + } + } + for (j=*p;j<*(p+1);j++) { + if (*j=inner_tok_flag) confusion("inner"); + else if (app_supp(*j-tok_flag+tok_start)) goto catch14;; + } + return 0; + catch14: if (*(*(p+1)-1)=='9') return 1; /* production 14 was used */ + else return 0; + } + + @ The trickiest part of \.{CTWILL} is the procedure |make_ministring(l)|, + which tries to figure out a symbolic form of definition after + |make_underlined(pp+l)| has been called. We rely heavily on the + existing productions, which force the translated texts to have a + structure that's decodable even though the underlying |cat| and |mathness| + codes have disappeared. + + @c void + make_ministring(l) + int l; /* 0, 1, or 2 */ + { + text_pointer q,r; + name_pointer cn; + token t; + int ast_count; /* asterisks preceding the expression */ + boolean non_ast_seen; /* have we seen a non-asterisk? */ + if (tok_loc<=operator_found) return; + cn=((*tok_loc)%id_flag)+name_dir; + @; + null_scrap.mathness=(((pp+l)->mathness)%4)*5; big_app1(&null_scrap); + /* now we're ready for the mathness that follows (I think) */ + /* (without the mod 4 times 5, comments posed a problem) */ + /* (namely in cases like |int a(b,c)| followed by comment) */ + ident_seen=0;@+app_supp((pp+l)->trans); + null_scrap.mathness=10; big_app1(&null_scrap); + /* now |cur_mathness==no_math| */ + ms_mode=1; ministring_ptr=ministring_buf; + if (l==2) *ministring_ptr++='='; + make_output(); /* translate the current text into a ministring */ + tok_ptr=*(--text_ptr); /* delete that text */ + new_meaning(cn); + cur_mathness=maybe_math; /* restore it */ + } + + @ Here we use the fact that a |decl_head| comes from |int_like| only in + production~27, whose translation is fairly easy to recognize. (Well, + production 28 has been added for \CPLUSPLUS/, but we hope that doesn't + mess us up.) And we also use other similar facts. + + If an identifier is given an \&{extern} definition, we don't change + its current meaning, but we do suppress mini-index entries to its + current meaning in other sections. + + @= + if (l==0) { app(int_loc+res_flag); app(' '); cur_mathness=no_math; } + else { + q=(pp+l-1)->trans; + ast_count=0; + non_ast_seen=0; + while (1) { + if (*(q+1)==*q+1) { + r=q;@+break; /* e.g. \&{struct}; we're doing production 45 or 46 */ + } + if (**q=tok_flag && **(t-tok_flag+tok_start)=='*') { + /* production 34 */ + if (!non_ast_seen) ast_count++; /* count immediately preceding |*|'s */ + } else non_ast_seen=1; + if (*(*q+1)==' ' && *(q+1)==*q+2) break; /* production 27 */ + if (*(*q+1)=='{' && *(*q+2)=='}' && *(*q+3)=='$' && *(*q+4)==' '@| + && *(q+1)==*q+5) break; /* production 27 in disguise */ + q=r; + } + while (**r>=tok_flag) { + if (*(r+1)>*r+9 && *(*r+1)=='{' && *(*r+2)=='}' && *(*r+3)=='$' @| + && *(*r+4)==indent) q=**r-tok_flag+tok_start; /* production 49 */ + r=**r-tok_flag+tok_start; + } + if (**r==ext_loc+res_flag) return; /* \&{extern} gives no definition */ + @; + } + + @ @= + cur_mathness=no_math; /* it was |maybe_math| */ + if (*(q+1)==*q+8 && *(*q+1)==' ' && *(*q+3)==' ') { + app(**q);@+app(' ');@+app(*(*q+2)); /* production 46 */ + } else if ((t=*(*(q+1)-1))>=tok_flag && **(r=t-tok_flag+tok_start)=='\\' + && *(*r+1)=='{') app(**q); /* |struct_like| identifier */ + else app((q-tok_start)+tok_flag); + while (ast_count) { + big_app('{');@+app('*');@+app('}');@+ast_count--; + } + @ Now comes the code that tries to match each production starting with a particular type of scrap. Whenever a match is discovered, the |squash| or |reduce| macro will cause the appropriate action *************** *** 2540,2546 **** @= if (cat1==lbrace || cat1==int_like || cat1==decl) { ! make_underlined(pp); big_app1(pp); big_app(indent); app(indent); reduce(pp,1,fn_decl,0,1); } else if (cat1==unop) squash(pp,2,exp,-2,2); --- 3020,3028 ---- @= if (cat1==lbrace || cat1==int_like || cat1==decl) { ! make_underlined(pp); ! make_ministring(0); ! big_app1(pp); big_app(indent); app(indent); reduce(pp,1,fn_decl,0,1); } else if (cat1==unop) squash(pp,2,exp,-2,2); *************** *** 2554,2560 **** else if (cat1==cast && cat2==colon) squash(pp+2,1,base,0,5); else if (cat1==semi) squash(pp,2,stmt,-1,6); else if (cat1==colon) { ! make_underlined (pp); squash(pp,2,tag,-1,7); } else if (cat1==rbrace) squash(pp,1,stmt,-1,8); else if (cat1==lpar && cat2==rpar && (cat3==const_like || cat3==case_like)) { --- 3036,3048 ---- else if (cat1==cast && cat2==colon) squash(pp+2,1,base,0,5); else if (cat1==semi) squash(pp,2,stmt,-1,6); else if (cat1==colon) { ! make_underlined (pp); ! if (tok_loc>operator_found) { ! name_pointer cn=((*tok_loc)%id_flag)+name_dir; ! strcpy(ministring_buf,"label"); ! new_meaning(cn); ! } ! squash(pp,2,tag,-1,7); } else if (cat1==rbrace) squash(pp,1,stmt,-1,8); else if (cat1==lpar && cat2==rpar && (cat3==const_like || cat3==case_like)) { *************** *** 2647,2653 **** reduce(pp,2,decl_head,-1,34); } else if (cat1==exp && cat2!=lpar && cat2!=exp && cat2!=cast) { ! make_underlined(pp+1); squash(pp,2,decl_head,-1,35); } else if ((cat1==binop||cat1==colon) && cat2==exp && (cat3==comma || cat3==semi || cat3==rpar)) --- 3135,3143 ---- reduce(pp,2,decl_head,-1,34); } else if (cat1==exp && cat2!=lpar && cat2!=exp && cat2!=cast) { ! make_underlined(pp+1); ! make_ministring(1); ! squash(pp,2,decl_head,-1,35); } else if ((cat1==binop||cat1==colon) && cat2==exp && (cat3==comma || cat3==semi || cat3==rpar)) *************** *** 2687,2692 **** --- 3177,3183 ---- else if (cat1==exp||cat1==int_like) { if (cat2==lbrace || cat2==semi) { make_underlined(pp+1); make_reserved(pp+1); + make_ministring(1); big_app1(pp); big_app(' '); big_app1(pp+1); if (cat2==semi) reduce(pp,2,decl_head,0,45); else { *************** *** 2833,2839 **** big_app(' '); big_app1(pp); reduce(pp,1,stmt,-1,77); @ @= ! if (cat1==define_like) make_underlined(pp+2); if (cat1==else_like || cat1==if_like ||cat1==define_like) squash(pp,2,lproc,0,78); else if (cat1==rproc) { --- 3324,3336 ---- big_app(' '); big_app1(pp); reduce(pp,1,stmt,-1,77); @ @= ! if (cat1==define_like) { /* \.{\#define} is analogous to \&{extern} */ ! make_underlined(pp+2); ! if (tok_loc>operator_found) { ! /* no time to work out this case; I'll handle defines by brute force ! in the \.{aux} file, since they usually don't go in mini-index */ ! } ! } if (cat1==else_like || cat1==if_like ||cat1==define_like) squash(pp,2,lproc,0,78); else if (cat1==rproc) { *************** *** 2914,2925 **** squash(pp,1,exp,-2,99); @ @= ! if (cat1==prelangle) squash(pp+1,1,langle,1,100); ! else squash(pp,1,exp,-2,101); @ @= if (cat1==exp) { ! big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,else_like,-2,102); } @ @= --- 3411,3422 ---- squash(pp,1,exp,-2,99); @ @= ! if (cat1==prelangle) squash(pp+1,1,langle,1,121); ! else squash(pp,1,exp,-2,122); @ @= if (cat1==exp) { ! big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,else_like,-2,123); } @ @= *************** *** 2950,2973 **** else if (cat1==comma) squash(pp,2,exp,-2,113); else if (cat1!=raw_ubin) squash(pp,1,new_exp,0,114); ! @ @= ! if ((cat1==int_like || cat1==cast) && (cat2==comma || cat2==semi)) ! squash(pp+1,1,exp,-1,115); ! else if (cat1==int_like) { ! big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,typedef_like,0,116); ! } ! else if (cat1==exp && cat2!=lpar && cat2!=exp && cat2!=cast) { ! make_underlined(pp+1); make_reserved(pp+1); ! big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,typedef_like,0,117); ! } ! else if (cat1==comma) { ! big_app2(pp); big_app(' '); reduce(pp,2,typedef_like,0,118); ! } ! else if (cat1==semi) squash(pp,2,decl,-1,119); ! else if (cat1==ubinop && (cat2==ubinop || cat2==cast)) { ! big_app('{'); big_app1(pp+1); big_app('}'); big_app1(pp+2); ! reduce(pp+1,2,cat2,0,120); ! } @ @= if (cat1==lpar && cat2==rpar) { --- 3447,3470 ---- else if (cat1==comma) squash(pp,2,exp,-2,113); else if (cat1!=raw_ubin) squash(pp,1,new_exp,0,114); ! @ Here \.{CTWILL} deviates from the normal productions introduced in ! version 3.6, because those productions bypass |decl_head| (thereby ! confusing |make_ministring|, which depends on the |decl_head| productions ! to deduce the type). We revert to an older syntax that was ! less friendly to \CPLUSPLUS/ but good enough for me. ! ! @= ! if (cat1==decl_head) { ! if ((cat2==exp&&cat3!=lpar&&cat3!=exp)||cat2==int_like) { ! make_underlined(pp+2); make_reserved(pp+2); ! make_ministring(2); ! big_app2(pp+1); reduce(pp+1,2,decl_head,0,200); ! } ! else if (cat2==semi) { ! big_app1(pp); big_app(' '); big_app2(pp+1); reduce(pp,3,decl,-1,201); ! } ! } else if (cat1==int_like && cat2==raw_int && ! (cat3==semi || cat3==comma)) squash(pp+2,1,exp,1,202); @ @= if (cat1==lpar && cat2==rpar) { *************** *** 2978,2984 **** else if (cat1==exp) { big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,exp,-2,122); } ! @ @= if (cat1==exp && (cat2==colon || cat2==base)) { (pp+2)->mathness=5*yes_math; /* this colon should be in math mode */ --- 3475,3481 ---- else if (cat1==exp) { big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,exp,-2,122); } ! @ @= if (cat1==exp && (cat2==colon || cat2==base)) { (pp+2)->mathness=5*yes_math; /* this colon should be in math mode */ *************** *** 3249,3254 **** --- 3746,3752 ---- case '#': app_str("\\#"); app_scrap(ubinop,yes_math);@+break; @.\\\#@> case ignore: case xref_roman: case xref_wildcard: + case meaning: case suppress: case xref_typewriter: case noop:@+break; case '(': case '[': app(next_control); app_scrap(lpar,maybe_math);@+break; case ')': case ']': app(next_control); app_scrap(rpar,maybe_math);@+break; *************** *** 3440,3445 **** --- 3938,3944 ---- else app_scrap(p->ilk,maybe_math); } } + @; } @ When the `\.{\v}' that introduces \CEE/ text is sensed, a call on *************** *** 3470,3477 **** until |next_control>=format_code|. Thus, it takes care of embedded comments. The token list created from within `\pb' brackets is output as an argument ! to \.{\\PB}, if the user has invoked \.{CWEAVE} with the \.{+e} flag. ! Although \.{cwebmac} ignores \.{\\PB}, other macro packages might use it to localize the special meaning of the macros that mark up program text. --- 3969,3976 ---- until |next_control>=format_code|. Thus, it takes care of embedded comments. The token list created from within `\pb' brackets is output as an argument ! to \.{\\PB}, if the user has invoked \.{CTWILL} with the \.{+e} flag. ! Although \.{ctwimac} ignores \.{\\PB}, other macro packages might use it to localize the special meaning of the macros that mark up program text. *************** *** 3499,3504 **** --- 3998,4004 ---- p=text_ptr; freeze_text; q=C_translate(); /* at this point we have |tok_ptr+6<=max_toks| */ app(tok_flag+(int)(p-tok_start)); + app(inserted); if (make_pb) app_str("\\PB{"); @.\\PB@> app(inner_tok_flag+(int)(q-tok_start)); *************** *** 3957,3967 **** phase_two() { reset_input(); if (show_progress) printf("\nWriting the output file..."); @.Writing the output file...@> ! section_count=0; format_visible=1; copy_limbo(); finish_line(); flush_buffer(out_buf,0,0); /* insert a blank line, it looks nice */ while (!input_has_ended) @; } @ The output file will contain the control sequence \.{\\Y} between non-null sections of a section, e.g., between the \TEX/ and definition parts if both are nonempty. This puts a little white space between the parts when they are --- 4457,4497 ---- phase_two() { reset_input(); if (show_progress) printf("\nWriting the output file..."); @.Writing the output file...@> ! temp_switch=0; temp_meaning_ptr=temp_meaning_stack; ! @; ! section_count=0; format_visible=1; right_start_switch=0; copy_limbo(); finish_line(); flush_buffer(out_buf,0,0); /* insert a blank line, it looks nice */ while (!input_has_ended) @; } + @ @= + FILE *aux_file; + + @ We clobber |tex_file_name| to make the name of the \.{.aux} and + \.{.bux} files. Too bad if the user has specified an output file that + doesn't end with `\.{.tex}'. I would have used |alt_web_file_name|, + if \.{common.w} had make that public; but I resisted the temptation to + change \.{common.w}. + + @= + strcpy(tex_file_name+strlen(tex_file_name)-4,".bux"); + include_depth=1; /* we simulate \.{@@i} */ + strcpy(cur_file_name,tex_file_name); /* first in, third out */ + if ((cur_file=fopen(cur_file_name,"r"))) { cur_line=0; include_depth++; } + strcpy(tex_file_name+strlen(tex_file_name)-4,".aux"); + strcpy(cur_file_name,tex_file_name); /* second in, second out */ + if ((cur_file=fopen(cur_file_name,"r"))) { cur_line=0; include_depth++; } + strcpy(cur_file_name,"system.bux"); /* third in, first out */ + if ((cur_file=fopen(cur_file_name,"r"))) cur_line=0; + else include_depth--; + if (include_depth) { /* at least one new file was opened */ + while (get_next()==meaning) ; /* new meaning is digested */ + if (include_depth) err_print("! Only @@$ is allowed in aux and bux files"); + finish_line(); loc=buffer; /* now reading beginning of line 1 */ + } + if ((aux_file=fopen(tex_file_name,"w"))==NULL) + fatal("! Cannot open aux output file ",tex_file_name); + @ The output file will contain the control sequence \.{\\Y} between non-null sections of a section, e.g., between the \TEX/ and definition parts if both are nonempty. This puts a little white space between the parts when they are *************** *** 3984,3992 **** boolean format_visible; /* should the next format declaration be output? */ boolean doing_format=0; /* are we outputting a format declaration? */ boolean group_found=0; /* has a starred section occurred? */ ! @ @= { section_count++; @; save_position; @; --- 4514,4527 ---- boolean format_visible; /* should the next format declaration be output? */ boolean doing_format=0; /* are we outputting a format declaration? */ boolean group_found=0; /* has a starred section occurred? */ + boolean right_start_switch; /* has `\.{@@r}' occurred recently? */ + boolean temp_switch; /* has `\.{@@\%}' occurred recently? */ ! @ @d usage_sentinel (struct perm_meaning *)1 ! @= { section_count++; + temp_switch=0; temp_meaning_ptr=temp_meaning_stack; + top_usage=usage_sentinel; @; save_position; @; *************** *** 4004,4012 **** If the section has changed, we put \.{\\*} just after the section number. @= ! if (*(loc-1)!='*') out_str("\\M"); @.\\M@> ! else { while (*loc == ' ') loc++; if (*loc=='*') { /* ``top'' level */ sec_depth = -1; --- 4539,4552 ---- If the section has changed, we put \.{\\*} just after the section number. @= ! if (*(loc-1)!='*') { ! if (right_start_switch) { ! out_str("\\shortpage\n"); right_start_switch=0; ! @.\\shortpage@> ! } ! out_str("\\M"); @.\\M@> ! } else { while (*loc == ' ') loc++; if (*loc=='*') { /* ``top'' level */ sec_depth = -1; *************** *** 4020,4030 **** --- 4560,4575 ---- group_found=1; out_str("\\N"); @.\\N@> + if (right_start_switch) { + out_str("N"); right_start_switch=0; + @.\\NN@> + } {@+ char s[32];@+sprintf(s,"{%d}",sec_depth+1);@+out_str(s);@+} if (show_progress) printf("*%d",section_count); update_terminal; /* print a progress report */ } out_str("{");out_section(section_count); out_str("}"); + flush_buffer(out_ptr,0,0); @ In the \TEX/ part of a section, we simply copy the source text, except that index entries are not copied and \CEE/ text within \pb\ is translated. *************** *** 4034,4042 **** switch (next_control) { case '|': init_stack; output_C(); break; case '@@': out('@@'); break; case TeX_string: case noop: case xref_roman: case xref_wildcard: case xref_typewriter: ! case section_name: loc-=2; next_control=get_next(); /* skip to \.{@@>} */ if (next_control==TeX_string) err_print("! TeX string should be in C text only"); break; @.TeX string should be...@> --- 4579,4590 ---- switch (next_control) { case '|': init_stack; output_C(); break; case '@@': out('@@'); break; + case temp_meaning: temp_switch=1-temp_switch; break; + case right_start: right_start_switch=1; break; case TeX_string: case noop: case xref_roman: case xref_wildcard: case xref_typewriter: ! case meaning: case suppress: ! case section_name: loc-=2; next_control=get_next(); /* reprocess */ if (next_control==TeX_string) err_print("! TeX string should be in C text only"); break; @.TeX string should be...@> *************** *** 4058,4067 **** init_stack; if (next_control==definition) @@; else @; ! outer_parse(); finish_C(format_visible); format_visible=1; doing_format=0; } @ The |finish_C| procedure outputs the translation of the current scraps, preceded by the control sequence `\.{\\B}' and followed by the control sequence `\.{\\par}'. It also restores the token and scrap --- 4606,4622 ---- init_stack; if (next_control==definition) @@; else @; ! outer_parse(); ! if (is_macro) @; ! finish_C(format_visible); format_visible=1; doing_format=0; } + @ @= + boolean is_macro; /* it's a macro def, not a format def */ + int def_diff; /* 0 iff the current macro has parameters */ + name_pointer id_being_defined; /* the definee */ + @ The |finish_C| procedure outputs the translation of the current scraps, preceded by the control sequence `\.{\\B}' and followed by the control sequence `\.{\\par}'. It also restores the token and scrap *************** *** 4111,4116 **** --- 4666,4672 ---- it starts after we scan the matching `\.)'. @= { + is_macro=1; if (save_line!=out_line || save_place!=out_ptr || space_checked) app(backup); if(!space_checked){emit_space_if_needed;save_position;} app_str("\\D"); /* this will produce `\&{define }' */ *************** *** 4119,4125 **** --- 4675,4683 ---- err_print("! Improper macro definition"); @.Improper macro definition@> else { + id_being_defined=id_lookup(id_first,id_loc,normal); app('$'); app_cur_id(0); + def_diff=*loc-'('; if (*loc=='(') reswitch: switch (next_control=get_next()) { case '(': case ',': app(next_control); goto reswitch; *************** *** 4133,4140 **** --- 4691,4716 ---- } } + @ @= + { + ms_mode=1; ministring_ptr=ministring_buf; + *ministring_ptr++='='; + if (def_diff) { /* parameterless */ + scrap_pointer s=scrap_ptr; + text_pointer t; + token_pointer j; + while (s->cat==insert) s--; + if ((s-1)->cat==dead && s->cat==exp && **(t=s->trans)=='\\' + && *(*t+1)=='T') /* it's just a constant */ + for (j=*t;j<*(t+1);j++) *ministring_ptr++=*j; + else out_str("macro"); + } else out_str("macro (\\,)"); + new_meaning(id_being_defined); + } + @ @= { doing_format=1; + is_macro=0; if(*(loc-1)=='s' || *(loc-1)=='S') format_visible=0; if(!space_checked){emit_space_if_needed;save_position;} app_str("\\F"); /* this will produce `\&{format }' */ *************** *** 4280,4294 **** } @ @= ! out_str("\\fi"); finish_line(); ! @.\\fi@> flush_buffer(out_buf,0,0); /* insert a blank line, it looks nice */ @** Phase three processing. We are nearly finished! \.{CWEAVE}'s only remaining task is to write out the index, after sorting the identifiers and index entries. ! If the user has set the |no_xref| flag (the \.{-x} option on the command line), just finish off the page, omitting the index, section name list, and table of contents. --- 4856,4958 ---- } @ @= ! finish_line(); out_str("\\mini"); finish_line(); ! @.\\mini@> ! @; ! out_str("}\\FI"); finish_line(); ! @.\\FI@> flush_buffer(out_buf,0,0); /* insert a blank line, it looks nice */ + @ The following code is performed for each identifier parsed during + a section. Variable |top_usage| is always nonzero; it has the sentinel + value~1 initially, then it points to each variable scheduled for + possible citation. A variable is on this list if and only if its + |link| field is nonzero. All variables mentioned in the section are + placed on the list, unless they are reserved and their current + \TeX\ meaning is uninitialized. + + @ @= + { struct perm_meaning *q=p-name_dir+cur_meaning; + if (!(abnormal(p)) || strcmp(q->perm.tex_part,"\\uninitialized")!=0) + if (q->link==0) { + q->link=top_usage; + top_usage=q; + } + } + + @ @= + {@+struct perm_meaning *q; + while (temp_meaning_ptr>temp_meaning_stack) { + out_mini(--temp_meaning_ptr); + q=temp_meaning_ptr->id-name_dir+cur_meaning; + q->stamp=section_count; /* suppress output from ``permanent'' data */ + } + while (top_usage!=usage_sentinel) { + q=top_usage; + top_usage=q->link; + q->link=NULL; + if (q->stamp!=section_count) out_mini(&(q->perm)); + } + } + + @ @= + void out_mini(); + + @ @c void + out_mini(m) + meaning_struct *m; + { char s[60]; + name_pointer cur_name=m->id; + if (m->prog_no==0) { /* reference within current program */ + if (m->sec_no==section_count) return; /* defined in current section */ + sprintf(s,"\\[%d",m->sec_no); + } else { name_pointer n=title_code[m->prog_no]; + if (*(n->byte_start)=='{') + sprintf(s,"\\]%.*s%d",(n+1)->byte_start-n->byte_start,n->byte_start, + m->sec_no); + else sprintf(s,"\\]%.*s",(n+1)->byte_start-n->byte_start,n->byte_start); + } + out_str(s); out(' '); + @; + out(' '); out_str(m->tex_part); finish_line(); + } + + @ @= + switch (cur_name->ilk) { + case normal: case func_template: if (length(cur_name)==1) out_str("\\|"); + else {char *j; + for (j=cur_name->byte_start;j<(cur_name+1)->byte_start;j++) + if (xislower(*j)) goto lowcase; + out_str("\\."); break; + lowcase: out_str("\\\\"); + } + break; + @.\\|@> + @.\\.@> + @.\\\\@> + case roman: break; + case wildcard: out_str("\\9"); break; + @.\\9@> + case typewriter: out_str("\\."); break; + @.\\.@> + case custom: {char *j; out_str("$\\"); + for (j=cur_name->byte_start;j<(cur_name+1)->byte_start;j++) + out(*j=='_'? 'x': *j=='$'? 'X': *j); + out('$'); + goto name_done; + } + default: out_str("\\&"); + @.\\\&@> + } + out_name(cur_name,1); + name_done: + + @** Phase three processing. We are nearly finished! \.{CWEAVE}'s only remaining task is to write out the index, after sorting the identifiers and index entries. ! If the user has set the |no_xref| flag (the |-x| option on the command line), just finish off the page, omitting the index, section name list, and table of contents. *************** *** 4311,4319 **** if ((idx_file=fopen(idx_file_name,"w"))==NULL) fatal("! Cannot open index file ",idx_file_name); @.Cannot open index file@> - if (change_exists) { - @; finish_line(); finish_line(); - } out_str("\\inx"); finish_line(); @.\\inx@> active_file=idx_file; /* change active file to the index file */ --- 4975,4980 ---- *************** *** 4341,4365 **** } @ Just before the index comes a list of all the changed sections, including ! the index section itself. @= sixteen_bits k_section; /* runs through the sections */ - @ @= { - /* remember that the index is already marked as changed */ - k_section=0; - while (!changed_section[++k_section]); - out_str("\\ch "); - @.\\ch@> - out_section(k_section); - while (k_section= sixteen_bits k_section; /* runs through the sections */ @ A left-to-right radix sorting method is used, since this makes it easy to adjust the collating sequence and since the running time will be at worst proportional to the total length of all entries in the index. We put the *************** *** 4530,4538 **** --sort_ptr; } ! @ @= switch (cur_name->ilk) { ! case normal: case func_template: if (is_tiny(cur_name)) out_str("\\|"); else {char *j; for (j=cur_name->byte_start;j<(cur_name+1)->byte_start;j++) if (xislower(*j)) goto lowcase; --- 5177,5188 ---- --sort_ptr; } ! @ We don't format the index completely; the \.{twinx} program does the ! rest of the job. ! ! @= switch (cur_name->ilk) { ! case normal: if (is_tiny(cur_name)) out_str("\\|"); else {char *j; for (j=cur_name->byte_start;j<(cur_name+1)->byte_start;j++) if (xislower(*j)) goto lowcase; *************** *** 4543,4563 **** @.\\|@> @.\\.@> @.\\\\@> ! case wildcard: out_str("\\9");@+ goto not_an_identifier; @.\\9@> case typewriter: out_str("\\."); @.\\.@> ! case roman: not_an_identifier: out_name(cur_name,0); goto name_done; ! case custom: {char *j; out_str("$\\"); ! for (j=cur_name->byte_start;j<(cur_name+1)->byte_start;j++) ! out(*j=='_'? 'x': *j=='$'? 'X': *j); ! out('$'); ! goto name_done; ! } default: out_str("\\&"); @.\\\&@> } ! out_name(cur_name,1); name_done: @ Section numbers that are to be underlined are enclosed in --- 5193,5217 ---- @.\\|@> @.\\.@> @.\\\\@> ! case roman: out_str(" "); goto not_an_identifier; ! case wildcard: out_str("\\9"); goto not_an_identifier; @.\\9@> case typewriter: out_str("\\."); @.\\.@> ! not_an_identifier: out_name(cur_name,0); goto name_done; ! case custom: out_str("\\$"); break; ! @.\\\$@> default: out_str("\\&"); @.\\\&@> } ! if (flags['P']) out_name(cur_name,1); ! else { ! out('{'); ! {char *j; ! for (j=cur_name->byte_start;j<(cur_name+1)->byte_start;j++) out(*j); ! } ! out('}'); ! } name_done: @ Section numbers that are to be underlined are enclosed in *************** *** 4628,4633 **** --- 5282,5292 ---- (long)(xref_ptr-xmem),(long)max_refs); printf("%ld bytes (out of %ld)\n", (long)(byte_ptr-byte_mem),(long)max_bytes); + printf("%ld temp meanings (out of %ld)\n", + (long)(max_temp_meaning_ptr-temp_meaning_stack), + (long)max_meanings); + printf("%ld titles (out of %ld)\n", + (long)(title_code_ptr-title_code),(long)max_titles); printf("Parsing:\n"); printf("%ld scraps (out of %ld)\n", (long)(max_scr_ptr-scrap_info),(long)max_scraps);