diff options
author | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
commit | e0c6872cf40896c7be36b11dcc744620f10adf1d (patch) | |
tree | 60335e10d2f4354b0674ec22d7b53f0f8abee672 /web/c_cpp |
Initial commit
Diffstat (limited to 'web/c_cpp')
112 files changed, 57019 insertions, 0 deletions
diff --git a/web/c_cpp/c2cweb/COPYING b/web/c_cpp/c2cweb/COPYING new file mode 100644 index 0000000000..a43ea2126f --- /dev/null +++ b/web/c_cpp/c2cweb/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) 19yy <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/web/c_cpp/c2cweb/Makefile b/web/c_cpp/c2cweb/Makefile new file mode 100644 index 0000000000..616b2af16e --- /dev/null +++ b/web/c_cpp/c2cweb/Makefile @@ -0,0 +1,146 @@ +# This file is part of the c2cweb package Version 1.5 (10-Nov-1996) + +# GNU Makefile for c2cweb + +# you should use the CWEAVE program of this package + +.PHONY: default all debug documentation clean \ + do_all cweb +.CAUTIOUS: c2cweb.c + + +default: + @echo " say " + @echo ". " + @echo " make <TARGET> OS=<SYSTEM> " + @echo ". " + @echo " SYSTEM can be one of the following:" + @echo ". " + @echo " unix (gcc) " + @echo " os2 bound (emx) " + @echo " dos (djgpp) " + @echo ". " + @echo " TARGET can be one of the following:" + @echo ". " + @echo " all debug " + @echo " documentation " + @echo " clean " + + +ifdef OS + + ifeq ($(OS),unix) + CC = gcc -Wall -O + O = .o + LIB = + EXE = + CP = cp + RM = rm -f + endif + + ifeq ($(OS),os2) + CC = gcc -Wall -Zomf -Zmtd -O + O = .obj + LIB = c2cweb.def + EXE = .exe + CP = copy + RM = del + endif + + ifeq ($(OS),dos) + CC = gcc -Wall -O + O = .o + LIB = + EXE = .exe + CP = copy + RM = del + endif + + ifeq ($(OS),bound) + CC = gcc -Wall -O + O = .o + LIB = + EXE = .exe + CP = copy + RM = del + endif + + + ifeq ($(OS),unix) + define sub_make + cd cweb; make $@ + endef + else + define sub_make + cd cweb + make $@ + cd .. + endef + endif + + + OBJ = c2cweb$O + OUR_CWEAVE = cweb/cweave + + %$O: %.c + $(CC) $(CFLAGS) -c -o $@ $< + + %.c: %.w %.ch + $(CTANGLE) $^ $@ + %.c: %.w + $(CTANGLE) $< + + %.tex: %.w %.ch + $(OUR_CWEAVE) +ai $^ $@ + %.tex: %.w + $(OUR_CWEAVE) +ai $< + + + all: + $(MAKE) do_all CFLAGS=-s + + debug: + $(MAKE) do_all CFLAGS=-g + + # this builds a .dvi-file + # for .dvi files of cweave etc. please get the original CWEB package + documentation: c2cweb.dvi + + # remove the unnecessary files. + clean: + $(sub_make) + -$(RM) *$O + -$(RM) *.tex + -$(RM) *.scn + -$(RM) *.toc + -$(RM) *.idx + -$(RM) *.log + + + do_all: cweb c2cweb$(EXE) + + cweb: + $(sub_make) + + c2cweb$(EXE): $(OBJ) + ifeq ($(OS),dos) + $(CC) $(CFLAGS) -o $(basename $@) $^ + strip $(basename $@) + coff2exe $(basename $@) + -$(RM) $(basename $@) + else + $(CC) $(CFLAGS) -o $@ $^ $(LIB) + endif + + c2cweb.dvi: c2cweb.tex cweb/cwebmaca.tex + -$(RM) cwebmaca.tex + $(CP) cweb/cwebmaca.tex . + $(TEX) c2cweb + +else # ifdef OS + + all debug documentation clean: default + +endif + +# end of Makefile diff --git a/web/c_cpp/c2cweb/c2cweb.c b/web/c_cpp/c2cweb/c2cweb.c new file mode 100644 index 0000000000..f5869cf8b5 --- /dev/null +++ b/web/c_cpp/c2cweb/c2cweb.c @@ -0,0 +1,1195 @@ +#define banner "\nThis is c2cweb Version 1.5 (c) 1996 by Werner Lemberg\n\n" \ + \ + +#define FALSE 0 +#define TRUE 1 +#define DONE 2 +#define WAIT 3 \ + +#define DIR_LENGTH 80 +#define TITLE_LENGTH 100 +#define PATH_SEPARATOR '/' \ + +#define FILE_NAME_LENGTH 80 \ + +#define BUFFER_LENGTH 500 \ + +#define xisspace(c) (isspace(c) &&((unsigned char) c<0200) ) \ + +/*4:*/ +#line 120 "c2cweb.w" + +/*5:*/ +#line 155 "c2cweb.w" + +#include <ctype.h> +#include <getopt.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + + +/*:5*/ +#line 121 "c2cweb.w" +; +/*11:*/ +#line 370 "c2cweb.w" + +void open_files(char*); + +/*:11*//*14:*/ +#line 464 "c2cweb.w" + +void handle_input(void); + + +/*:14*//*33:*/ +#line 1145 "c2cweb.w" + +void usage(void); + +/*:33*//*35:*/ +#line 1171 "c2cweb.w" + +void modify_filename(char*); + + +/*:35*//*38:*/ +#line 1211 "c2cweb.w" + +char*get_line(void); + + +/*:38*//*41:*/ +#line 1250 "c2cweb.w" + +char*protect_underlines(char*); + + +/*:41*//*43:*/ +#line 1276 "c2cweb.w" + +#ifndef __EMX__ +char*_getname(char*); +#endif + + +/*:43*/ +#line 122 "c2cweb.w" +; +/*8:*/ +#line 249 "c2cweb.w" + +int tab_length= 4; +int verbatim= FALSE; +int user_linefeed= FALSE; +int one_side= FALSE; +char outdir[DIR_LENGTH+1]; +char include_file[FILE_NAME_LENGTH+1]; +char title[TITLE_LENGTH+1]; + +#ifdef __EMX__ +char optchar[]= "-/"; +char pathsepchar[]= "\\/"; +#else +char optchar[]= "-"; +char pathsepchar[]= "/"; +#endif + +char at_nmb[]= "@#"; + + + +/*:8*//*10:*/ +#line 365 "c2cweb.w" + +FILE*in,*out; + + +/*:10*//*15:*/ +#line 471 "c2cweb.w" + +char buffer[BUFFER_LENGTH+1]; + +/*:15*//*37:*/ +#line 1205 "c2cweb.w" + +int line_number= 0; +int column; + + +/*:37*//*40:*/ +#line 1245 "c2cweb.w" + +char tempbuf[2*FILE_NAME_LENGTH+1]; + + +/*:40*/ +#line 123 "c2cweb.w" +; + +void main(argc,argv) +int argc; +char*argv[]; + +{int i; +char buffer[DIR_LENGTH+FILE_NAME_LENGTH+1]; +char*p,*q; + + +printf(banner); + +#ifdef __EMX__ +_response(&argc,&argv); +_wildcard(&argc,&argv); +#endif + +/*9:*/ +#line 271 "c2cweb.w" + +{char c; +int i; + + +outdir[0]= '\0'; +include_file[0]= '\0'; +title[0]= '\0'; + +#ifdef __EMX__ +optswchar= optchar; +#endif + +strcpy(title,"c2cweb output"); + +while((c= getopt(argc,argv,"b:f:lo:t:v1"))!=EOF) +{switch(c) +{case'b': +if(strchr(optchar,optarg[0])) + +usage(); + +if(strlen(optarg)>=TITLE_LENGTH) +fprintf(stderr, +"\nTitle too long. Will use \"c2cweb output\".\n"); +else +strcpy(title,optarg); +break; +case'f': +if(strchr(optchar,optarg[0])) +usage(); + +if(strlen(optarg)>=FILE_NAME_LENGTH) +fprintf(stderr, +"\nInclude file name too long. Will be ignored.\n"); +else +strcpy(include_file,optarg); +break; +case'l': +user_linefeed= TRUE; +break; +case'o': +if(strchr(optchar,optarg[0])) +usage(); + +if((i= strlen(optarg))>=DIR_LENGTH) +fprintf(stderr, +"\nOutput directory name too long. Will be ignored.\n"); +else +{strcpy(outdir,optarg); +if(!strchr(pathsepchar,outdir[i-1])) + +{outdir[i]= PATH_SEPARATOR; +outdir[i+1]= '\0'; +} +} +break; +case't': +if(strchr(optchar,optarg[0])) +usage(); + +tab_length= atoi(optarg); +if(tab_length==0||tab_length>8) +tab_length= 4; +break; +case'v': +verbatim= TRUE; +break; +case'1': +one_side= TRUE; +break; +default: +usage(); +break; +} +} + +if(optind==argc) +usage(); +} + + +/*:9*/ +#line 141 "c2cweb.w" +; + +/*6:*/ +#line 164 "c2cweb.w" + +for(i= optind;i<argc-1;i++) +{printf(" processing %s\n",argv[i]); + +open_files(argv[i]); +q= protect_underlines(_getname(argv[i])); +if((p= strrchr(q,'.'))!=NULL) + + +fprintf(out, +"@*{%s\\ZZZ{\\setbox0=\\hbox{%s}\\hskip-\\wd0}}.\n" +"\\ind=2\n\n",q,p); +else +fprintf(out,"@*{%s}.\n" +"\\ind=2\n\n",q); + +handle_input(); +fclose(in); +fclose(out); +} + + +/*:6*/ +#line 143 "c2cweb.w" +; +/*7:*/ +#line 190 "c2cweb.w" + +printf(" processing %s\n",argv[i]); + +open_files(argv[i]); +/*13:*/ +#line 418 "c2cweb.w" + +fprintf(out, +"\\font\\symb=cmsy10\n" +"\\font\\math=cmmi10\n" +"\\def\\ob" +"{\\parskip=0pt\\parindent=0pt%%\n" +"\\let\\\\=\\BS\\let\\{=\\LB\\let\\}=\\RB\\let\\~=\\TL%%\n" +"\\let\\ =\\SP\\let\\_=\\UL\\let\\&=\\AM\\let\\^=\\CF%%\n" +"\\obeyspaces\\frenchspacing\\tt}\n" +"\n" +"\\def\\e{\\hfill\\break\\hbox{}}\n" +"\\def\\{{\\relax\\ifmmode\\lbrace\\else$\\lbrace$\\fi}\n" +"\\def\\}{\\relax\\ifmmode\\rbrace\\else$\\rbrace$\\fi}\n" +"\\def\\takenone#1{\\hskip-0.1em}\n" +"\\let\\ZZZ=\\relax\n" +"\n" +"%s" +"\n" +"\\pageno=\\contentspagenumber \\advance\\pageno by 1\n" +"\\let\\maybe=\\iftrue\n" +"\n" +"\\def\\title{%s}\n" +"\n" +"@i compiler.w\n" +"\n",one_side?"\\let\\lheader=\\rheader\n":"",title); + +if(*include_file) +fprintf(out, +"@i %s\n" +"\n",include_file); + +for(i= optind;i<argc-1;i++) +{strcpy(buffer,argv[i]); +modify_filename(buffer); + +fprintf(out,"@i %s\n",_getname(buffer)); +} + +fputc('\n',out); + + + +/*:13*/ +#line 194 "c2cweb.w" +; +q= protect_underlines(_getname(argv[i])); +if((p= strrchr(q,'.'))!=NULL) +fprintf(out, +"@*{%s\\ZZZ{\\setbox0=\\hbox{%s}\\hskip-\\wd0}}.\n" +"\\ind=2\n\n",q,p); +else +fprintf(out,"@*{%s}.\n" +"\\ind=2\n\n",q); + +handle_input(); +/*32:*/ +#line 1137 "c2cweb.w" + +fprintf(out, +"\n" +"@*Index.\n" +"\\let\\ZZZ=\\takenone\n"); + + +/*:32*/ +#line 205 "c2cweb.w" +; + +strcpy(buffer,argv[argc-1]); +modify_filename(buffer); + +printf( +"\n You must now call CWEAVE with %s%s\n" +" as the argument to get a TeX output",outdir,_getname(buffer)); +if(optind<argc-1) +printf(" of all processed files"); +printf("\n"); + + + +/*:7*/ +#line 144 "c2cweb.w" +; + +fclose(in); +fclose(out); +} + + +/*:4*//*12:*/ +#line 374 "c2cweb.w" + +void open_files(filename) +char*filename; +{char buffer[DIR_LENGTH+FILE_NAME_LENGTH+1]; + + +if(strlen(filename)>FILE_NAME_LENGTH-2) +{fprintf(stderr,"\n File name too long.\n"); +exit(-1); +} + +if((in= fopen(filename,"rt"))==NULL) +{fprintf(stderr,"\n Can't open input file %s\n",filename); +exit(-1); +} + +strcpy(buffer,outdir); +strcat(buffer,filename); +modify_filename(buffer); + +if((out= fopen(buffer,"wt"))==NULL) +{fprintf(stderr,"\n Can't open output file %s\n",buffer); +exit(-1); +} +} + + + +/*:12*//*16:*/ +#line 480 "c2cweb.w" + +void handle_input(void) +{char*buf_p; +char ch; +/*17:*/ +#line 571 "c2cweb.w" + +int any_input= FALSE; + +int brace_count= 0; +int blank_count= 0; + +int in_comment= FALSE; +int in_C= FALSE; +int in_string= FALSE; +int short_comment= FALSE; +int leading_blanks= TRUE; +int double_linefeed= FALSE; +int linefeed_comment= FALSE; + +int comment_slash= FALSE; +int comment_star= FALSE; +int escape_state= FALSE; + + +int before_TeX_text= FALSE; + +int function_blocks= FALSE; + + +/*:17*/ +#line 484 "c2cweb.w" +; + +line_number= 0; +while(get_line()) +{buf_p= buffer; + +do +{ch= *buf_p; + +/*18:*/ +#line 598 "c2cweb.w" + +/*19:*/ +#line 690 "c2cweb.w" + +if(buf_p==buffer) +{if(!(in_comment||in_string)) +{if(!strncmp(buf_p,"/""*@@*""/",6)) +{in_C= FALSE; +before_TeX_text= TRUE; +function_blocks= WAIT; +brace_count= 0; + +if(any_input) +fputs("\n@\n" +"\\ind=2\n\n",out); + + +any_input= FALSE; +*(buf_p--)= '\n'; + +goto end; +} +else if(!strncmp(buf_p,"/""*@*""/",5)) +{in_C= FALSE; +before_TeX_text= TRUE; +function_blocks= FALSE; + +if(any_input) +fputs("\n@\n" +"\\ind=2\n\n",out); + +any_input= FALSE; +*(buf_p--)= '\n'; +goto end; +} +else if(!strncmp(buf_p,"/""*{*""/",5)) +{brace_count++; +fputs("@{\n",out); + +ch= '\n'; +goto end; +} +else if(!strncmp(buf_p,"/""*}*""/",5)) +{brace_count--; +fputs("@}\n",out); + +if(!brace_count&&function_blocks) + +{in_C= FALSE; +before_TeX_text= TRUE; + +break; +} + +ch= '\n'; +goto end; +} +else if(!strncmp(buf_p,"/""*{}*""/",6)) +{fputs("@{@}\n",out); + +ch= '\n'; +goto end; +} +} +} + + +/*:19*/ +#line 599 "c2cweb.w" +; + +if(double_linefeed&&ch=='/') +linefeed_comment= TRUE; + +if(double_linefeed&&(ch==' '||ch=='\t')) +leading_blanks= TRUE; + +if(ch!='\n') +double_linefeed= FALSE; + +if(!xisspace(ch)) +{any_input= TRUE; + +if(before_TeX_text&&function_blocks) +{before_TeX_text= FALSE; + +if(function_blocks==WAIT) +function_blocks= TRUE; + +else +{fputs("@\n" +"\\ind=2\n\n",out); + + +if(leading_blanks) +{leading_blanks= FALSE; + +while(blank_count--) +fputc(' ',out); +blank_count= 0; +} +} +} + +if(in_comment&&leading_blanks) +{leading_blanks= FALSE; + +while(blank_count--) +fputc(' ',out); +blank_count= 0; +} +} + +if(!(ch=='/'||xisspace(ch))) + +{if(!(in_comment||in_C||comment_slash)) + +{in_C= TRUE; + +fputs("@c\n",out); + +if(leading_blanks) +{leading_blanks= FALSE; + +while(blank_count--) +fputc(' ',out); +blank_count= 0; +} +} + +if(!(in_comment||comment_slash)&&leading_blanks) +{leading_blanks= FALSE; + +while(blank_count--) +fputc(' ',out); +blank_count= 0; +} +} + +if(comment_slash&&!(ch=='*'||ch=='/')) + +{comment_slash= FALSE; +if(!in_comment) +linefeed_comment= FALSE; + +fputc('/',out); +} + +if(comment_star&&ch!='/') +{comment_star= FALSE; + +fputc('*',out); +} + +if(escape_state&&!(ch=='\"'||ch=='\n'||ch=='\\')) + +escape_state= FALSE; + + +/*:18*/ +#line 493 "c2cweb.w" +; + +switch(ch) +{case' ': +if(leading_blanks) +{blank_count++; +goto end; +} +break; + +case'\t': +{int i= tab_length-(column%tab_length); + +column+= i-1; + +if(leading_blanks) +{blank_count+= i; +goto end; +} + +while(i--) +fputc(' ',out); +goto end; +} +break; + +case'{': +/*20:*/ +#line 755 "c2cweb.w" + +if(in_comment) +fputc('\\',out); +else if(in_string) +break; +else if(function_blocks) +{brace_count++; +in_C= TRUE; +} + +/*:20*/ +#line 520 "c2cweb.w" +; +break; + +case'}': +/*25:*/ +#line 802 "c2cweb.w" + +if(in_comment) +fputc('\\',out); +else if(in_string) +break; +else if(function_blocks) +{brace_count--; +if(!brace_count) +{in_C= FALSE; + +before_TeX_text= TRUE; +break; +} +} + +/*:25*/ +#line 524 "c2cweb.w" +; +break; + +case'/': +/*26:*/ +#line 818 "c2cweb.w" + +if(comment_star) +{/*27:*/ +#line 832 "c2cweb.w" + +comment_star= FALSE; +leading_blanks= FALSE; + +if(!short_comment) +{in_comment= FALSE; + +if(!in_C) +{linefeed_comment= FALSE; + +if(verbatim) +fputs("*""/}",out); + +if(*(buf_p+1)=='\n') +fputs("\\e{}%",out); + +goto end; +} + +if(in_C&&verbatim) +{if(linefeed_comment) +{linefeed_comment= FALSE; + +fputs("*""/@>",out); +if(*(buf_p+1)=='\n'&&!user_linefeed) +fputs("@/",out); +goto end; +} +else +fputc('}',out); +} + +linefeed_comment= FALSE; + +if(in_C||verbatim) +fputc('*',out); +else +goto end; +} +else +fputc('*',out); + +/*:27*/ +#line 820 "c2cweb.w" +; +} +else if(comment_slash) +{/*28:*/ +#line 875 "c2cweb.w" + +comment_slash= FALSE; + +if(!short_comment) +{in_comment= TRUE; +short_comment= TRUE; + +if(!in_C&&verbatim) +{fputs("{\\ob{}",out); +if(leading_blanks) +{leading_blanks= FALSE; + +while(blank_count--) +fputc(' ',out); +blank_count= 0; +} +fputs("//",out); + +goto end; +} + +if(in_C&&verbatim) +{if(leading_blanks||linefeed_comment) +{linefeed_comment= TRUE; + +if(!user_linefeed) +fputs("@/",out); +fputs("@t}\\8{\\ob{}",out); + + +if(leading_blanks) +{leading_blanks= FALSE; + +while(blank_count--) +fputc(' ',out); +blank_count= 0; +} +fputs("//",out); +} +else +fputs("//{\\ob{}",out); + +goto end; +} + +if(in_C||verbatim) +fputc('/',out); +else +goto end; +} +else +fputc('/',out); + + +/*:28*/ +#line 823 "c2cweb.w" +; +} +else +{comment_slash= TRUE; + +goto end; +} + +/*:26*/ +#line 528 "c2cweb.w" +; +break; + +case'*': +/*29:*/ +#line 930 "c2cweb.w" + +if(comment_slash) +{comment_slash= FALSE; + +if(in_comment&&!short_comment) + + + + + + + +{fprintf(stderr, +" Error line %d: Nested comments not supported\n", +line_number); +exit(-1); +} + +if(!short_comment) +{in_comment= TRUE; + +if(!in_C&&verbatim) +{fputs("{\\ob{}",out); +if(leading_blanks) +{leading_blanks= FALSE; + +while(blank_count--) +fputc(' ',out); +blank_count= 0; +} +fputs("/""*",out); + +goto end; +} + +if(in_C&&verbatim) +{if(leading_blanks||linefeed_comment) +{linefeed_comment= TRUE; + +if(!user_linefeed) +fputs("@/",out); +fputs("@t}\\8{\\ob{}",out); +if(leading_blanks) +{leading_blanks= FALSE; + +while(blank_count--) +fputc(' ',out); +blank_count= 0; +} +fputs("/""*",out); +} +else +fputs("/""*{\\ob{}",out); + +goto end; +} + +if(in_C||verbatim) +fputc('/',out); +else +{fputs(" ",out); + +goto end; +} +} +else +fputc('/',out); +} +else +{comment_star= TRUE; + +goto end; +} + + +/*:29*/ +#line 532 "c2cweb.w" +; +break; + +case'\n': +/*30:*/ +#line 1006 "c2cweb.w" + +blank_count= 0; + +if(!in_comment&&in_C) +{if(double_linefeed==FALSE) +{double_linefeed= TRUE; +if(escape_state) +{escape_state= FALSE; + +leading_blanks= TRUE; + +if(in_string) +fputc('\n',out); +else +fputs("\n@/",out); +goto end; +} + +if(!leading_blanks&&user_linefeed) +fputs("@/",out); +} +else if(double_linefeed==TRUE) +{double_linefeed= DONE; + + +fputs(at_nmb,out); +} +} + +leading_blanks= TRUE; + +if(short_comment) +{short_comment= FALSE; +in_comment= FALSE; +double_linefeed= TRUE; + +if(verbatim) +{if(linefeed_comment&&in_C) +fputs("@>",out); +else +fputc('}',out); +} + +if(!in_C) +fputs("\\e{}%",out); +else if(linefeed_comment&&verbatim) +fputs("@/",out); + +linefeed_comment= FALSE; +} + +if(in_comment&&in_C&&verbatim&&linefeed_comment) +{ +fputs("@>@/\n@t}\\8{\\ob{}",out); + +goto end; +} + +if(in_comment&&verbatim) + + + +{fputs("\n\\e{}",out); +goto end; +} + + +/*:30*/ +#line 536 "c2cweb.w" +; +break; + +case'@': +/*21:*/ +#line 766 "c2cweb.w" + +if(in_comment) +{fputs("{\\char64}",out); +goto end; +} +else +fputc('@',out); + +/*:21*/ +#line 540 "c2cweb.w" +; +break; + +case'\'': +/*22:*/ +#line 775 "c2cweb.w" + +if(!in_comment) +{if(*(buf_p+1)=='\"'&&*(buf_p+2)=='\'') +escape_state= TRUE; +} + +/*:22*/ +#line 544 "c2cweb.w" +; +break; + +case'\"': +/*23:*/ +#line 782 "c2cweb.w" + +if(!in_comment) +{if(escape_state) +escape_state= FALSE; +else +in_string= TRUE-in_string; +} + +/*:23*/ +#line 548 "c2cweb.w" +; +break; + +case'\\': +/*24:*/ +#line 791 "c2cweb.w" + +if(in_comment) +{fputs("{\\symb\\char110}",out); +goto end; +} +else +escape_state= TRUE-escape_state; + + + +/*:24*/ +#line 552 "c2cweb.w" +; +break; + +default: +/*31:*/ +#line 1077 "c2cweb.w" + +if(in_comment) +{switch(ch) +{case'#': +fputs("{\\#}",out); +break; + +case'$': +fputs("{\\$}",out); +break; + +case'%': +fputs("{\\%}",out); +break; + +case'&': +fputs("{\\AM}",out); +break; + +case'_': +fputs("{\\_}",out); +break; + +case'^': +fputs("{\\^{}}",out); +break; + +case'\\': +fputs("{\\symb\\char110}",out); +break; + +case'~': +fputs("{\\~{}}",out); +break; + +case'|': +fputs("{\\symb\\char106}",out); +break; + +case'<': +fputs("{\\math\\char60}",out); +break; + +case'>': +fputs("{\\math\\char62}",out); +break; + +default: +fputc(ch,out); +break; +} + +goto end; +} + + +/*:31*/ +#line 556 "c2cweb.w" +; +break; +} + +fputc(ch,out); + +end: +buf_p++; +column++; +}while(ch!='\n'); +} +} + + +/*:16*//*34:*/ +#line 1149 "c2cweb.w" + +void usage(void) +{fprintf(stderr, +"Usage: c2cweb [switches] csourcefile(s) | @responsefile(s)" +"\n" +"\n possible switches:" +"\n" +"\n -b \"title\" set title" +"\n -l use input linefeeds" +"\n -o dirname set output directory (must already exist)" +"\n -f filename use this file as a format file" +"\n -t tablength set tabulator length (default 4)" +"\n -v verbatim mode" +"\n -1 one-sided output" +"\n" +"\n"); + +exit(-1); +} + + +/*:34*//*36:*/ +#line 1180 "c2cweb.w" + +void modify_filename(name) +char*name; +{char*p; + + +if((p= strrchr(name,'.'))!=NULL) +{p++; +if(*p&&*p!=' ') +p++; +if(*p&&*p!=' ') +p++; +if(*p!='w') +*p= 'w'; +else +*p= 'x'; +p++; +*p= '\0'; +} +else +strcat(name,".w"); +} + + +/*:36*//*39:*/ +#line 1218 "c2cweb.w" + +char*get_line(void) +{char*p; +int i= BUFFER_LENGTH; + + +if((p= fgets(buffer,BUFFER_LENGTH+1,in))!=NULL) +{while(i--) +{if(*(p++)=='\n') +break; +} + +p--; +p--; +while((*p==' '||*p=='\t')&&p>=buffer) +p--; +*(p+1)= '\n'; +*(p+2)= '\0'; + +line_number++; +column= 0; +} +return(p); +} + + +/*:39*//*42:*/ +#line 1257 "c2cweb.w" + +char*protect_underlines(p) +char*p; +{char*q; + + +q= tempbuf; + +do +{if(*p=='_') +*(q++)= '\\'; +*(q++)= *p; +}while(*(p++)); + +return tempbuf; +} + + +/*:42*//*44:*/ +#line 1287 "c2cweb.w" + +#ifndef __EMX__ +char*_getname(char*path) +{char*p; + +p= strrchr(path,'/'); +return p==NULL?path:(p+1); +} +#endif + + + +/*:44*/ diff --git a/web/c_cpp/c2cweb/c2cweb.def b/web/c_cpp/c2cweb/c2cweb.def new file mode 100644 index 0000000000..9fdd016403 --- /dev/null +++ b/web/c_cpp/c2cweb/c2cweb.def @@ -0,0 +1,3 @@ +NAME C2CWEB WINDOWCOMPAT +DESCRIPTION 'C2CWEB 1.5' +STACKSIZE 0x80000 diff --git a/web/c_cpp/c2cweb/c2cweb.dvi b/web/c_cpp/c2cweb/c2cweb.dvi Binary files differnew file mode 100644 index 0000000000..f0b0fcd989 --- /dev/null +++ b/web/c_cpp/c2cweb/c2cweb.dvi diff --git a/web/c_cpp/c2cweb/c2cweb.ger b/web/c_cpp/c2cweb/c2cweb.ger new file mode 100644 index 0000000000..895137a5f3 --- /dev/null +++ b/web/c_cpp/c2cweb/c2cweb.ger @@ -0,0 +1,17 @@ +% This file is part of the c2cweb package Version 1.5 + +% this is a small german `package' for c2cweb +% written by Werner Lemberg (a7971428@unet.univie.ac.at) 10-Nov-1996 + +\catcode`^^8e=\active \def ^^8e{\"A} +\catcode`^^99=\active \def ^^99{\"O} +\catcode`^^9a=\active \def ^^9a{\"U} +\catcode`^^84=\active \def ^^84{\"a} +\catcode`^^94=\active \def ^^94{\"o} +\catcode`^^81=\active \def ^^81{\"u} +\catcode`^^e1=\active \def ^^e1{\ss{}} + +\font\tentex=cmtt10 + + +% end of c2cweb.ger diff --git a/web/c_cpp/c2cweb/c2cweb.txt b/web/c_cpp/c2cweb/c2cweb.txt new file mode 100644 index 0000000000..9a7b64eb62 --- /dev/null +++ b/web/c_cpp/c2cweb/c2cweb.txt @@ -0,0 +1,479 @@ + +This is the c2cweb package Version 1.5 (10-Nov-1996) +===================================================== + +Copyright (C) 1996 Werner Lemberg + +[see below for a German introduction] + + +c2cweb will transform plain C or C++ code into a CWEB file to get a pretty +formatted output. A modified CWEAVE (which transforms the CWEB file into a +TeX file, see below) is included also. + +Some c2cweb highlights: + + variables and function names in italics + reserved words (int, char, return etc.) in bold face + strings in typewriter type + comments in typwriter type or roman + an index(!) of all variables, constants, function names etc. + each function gets a section + + and more + + +The c2cweb package contains the following files: + + c2cweb.txt this file + history.txt the history file + Makefile a makefile to compile c2cweb for UNIX, OS/2, and DOS + c2cweb.def definition file (needed for compiling under OS/2 + only) + COPYING the GNU Copying License + c2cweb.w a CWEB file of the program + you should say + make documentation (using this package's CWEAVE) + to produce a TeX-output + c2cweb.c extracted with CTANGLE from c2cweb.w + c2cweb.dvi the final .dvi-file + c2cweb.exe an executable for DOS and OS/2 + compiler.w compiler dependent formatting commands + (an output of c2cweb reads this file) + c2cweb.ger a small german `package' to demonstrate national + language support + example.h + example.c a C code example with inserted c2cweb control + commands + cweb/cweave.exe a modified executable (for DOS and OS/2) of CWEAVE + Ver. 3.2 with three important new features: + + o the switch +a causes CWEAVE to produce a + different output format, basically + + if(a) + {bla bla bla... + } + + instead of + + if(a) { + bla bla bla... + } + + o the switch +i causes CWEAVE to append () after a + function name in the index (this makes only + sense if all names are unique) + o the #define statement handling was basically + enhanced to allow macros with parameters + cweb/cweave.w this is the original CWEAVE source file of Levy's + CWEB package ver 3.2 (July 1994) + cweb/cweave.ch the change file for cweave.w + cweb/cweave.c extracted with CTANGLE from cweave.{w,ch} + cweb/common.h this is needed to build CWEAVE + (slightly different from the original) + cweb/common.w taken unaltered from CWEB 3.2 + cweb/common.ch change file for common.w + cweb/common.c extracted with CTANGLE from common.{w,ch} + cweb/Makefile Makefile for CWEAVE (UNIX, OS/2, and DOS) + cweb/cweave.def definition file (needed for compiling under OS/2 + only) + cweb/cwebmac.ori the standard cwebmac.tex file (slightly modified) + cweb/cwebmac.tex alternative format file + cweb/cwebmaca.tex this alternative format file is read by TeX if you + use the +a switch; similar to cwebmac.tex + cweb/prod-alt.w this file is an include file of cweave.ch; it + contains the alternative syntax rules of CWEAVE if + the switch a is on + +You should get the original CWEB package for further documentation; it +contains change files for different operating systems also. + + +Copying +------- + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License version 2 as published by +the Free Software Foundation. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +more details. + +You should have received a copy of the GNU General Public License along with +this program; if not, write to the Free Software Foundation, Inc., 675 Mass +Ave, Cambridge, MA 02139, USA. + + +Installation +------------ + +The c2cweb executable should be in the path. + +The CWEAVE executable should also be in the path; it needs the file +compiler.w (use the environment variable CWEBINPUTS to set the path). + +c2cweb.ger, cwebmac.tex and cwebmaca.tex must be found by TeX (the path is +usually controlled by the TEXINPUT environment variable). + +DOS and WINDOWS users must assure that emx.exe and rsx.exe can be found +(either in the path or set with the EMX and RSX environment variables). + +OS/2 users must assure that emx.dll and rsx.exe can be found (either in the +path or set with the EMX and RSX environment variables). + +[emx.exe, emx.dll, and rsx.exe can be found in the the emxrsx.zip package of + the emTeX distribution.] + + +Remarks for UNIX systems users +------------------------------ + +Because of the many different UNIX implementations, no executables are +included. The programs should compile without great problems if you use the +gcc compiler, the GNU C library and gnumake; you should say + + make all OS=unix + +in the c2cweb directory to build the binaries. + +Included in the package you will already find .c files extracted with +CTANGLE which can be directly used to compile. + + +How to use +---------- + +The only change the user has to do normally is to insert /*@@*/, starting an +empty line outside of a comment or string before the first function block +(normally main(){ ... }) in the C code. After this command c2cweb writes +each function block into a section. Before /*@@*/ all stuff is written into +a (possibly large) section. If you want to structure this further or if you +have structure definitions between functions, use /*@*/ to start a new +section without starting the function block algorithm (and of course /*@@*/ +before the next function). The function algorithm simply counts paired +braces; if none are open, a new section will begin. The file example.c has +these control codes inserted already. + + +The syntax is + + c2cweb [switches] csourcefile(s) | @responsefile(s) + +The possible switches are: + +-v: + all comments are written in typewriter type. You will need this if you + have already formatted your comments, for example + + /********************/ + /* example.c */ + /********************/. + +-t value: + all tabs will be expanded, and the -t switch defines the tab length + (default value is 4). + +-l: + causes all linefeeds inside of C text to be output explicitly by inserting + @/ (a CWEB control code) at the end of each code line. + +-o directory: + sets the output directory which must exist already. + +-f format_file: + include a format file (additionally to compiler.w). + +-b "titlestring": + defines a title with the titlestring enclosed in double quotes. This + string will be passed directly to \TeX. + +-1: + one-sided output (i.e. left header is the same as right header). + + +The last steps are calling CWEAVE with the transformed master file and then +calling TeX to get a printable .dvi-file. + + +An example: + + your input files are + header1.h, header2.h, file1.c, file2.c, file3.c + + you must call then + c2cweb [options] header*.h file*.c + + to get the *.*w files. c2cweb will now process your files and tells you + which file is the input file for CWEAVE (we'll assume file3.cw). + + After calling + cweave [options] file3.cw + + you get a .tex file (and some additional auxiliary files) which should + be processed further by PLAIN TeX (LaTeX is not supported): + + tex file3[.tex] + + +Please read the `Hints and Tricks' section about enhancements and +limitations of c2cweb in the file c2cweb.w (say `make documentation' to +build the .dvi file). + + +Author +------ + +Werner Lemberg (a7971428@unet.univie.ac.at) + +Please report any errors, comments or suggestions to this email-address. + + Werner Lemberg + Goldschlagstr. 52/14 + A-1150 Vienna/Austria + + + +-------------------------------------------------------------------------------- + + + +c2cweb Paket Version 1.5 (10-Nov-1996) +======================================= + +Copyright (C) 1996 Werner Lemberg + + +c2cweb verwandelt gewoehnlichen C - oder C++ - Quellcode in eine CWEB-Datei, +um einen gut formatierten Ausdruck zu erhalten. Dieses Paket enthaelt +ausserdem eine modifizierte Version von CWEAVE (welche die CWEB-Datei in +eine TeX-Datei transformiert). + +Besonderheiten u.a.: + + Variablen und Funktionsnamen in Kursivschrift + reservierte Woerter (int, char, return etc.) in Fettschrift + Strings in Schreibmaschinenschrift + Kommentare in Normal- oder Schreibmaschinenschrift + ein Index(!) aller Variablen, Konstanten, Funktionsnamen etc. + jede Funktion fuellt einen eigenen Abschnitt + + +Das c2cweb-Paket enthaelt folgende Dateien: + + c2cweb.txt diese Datei + history.txt Entstehungsgeschichte von c2cweb + Makefile ein Makefile fuer c2cweb (UNIX, OS/2 und DOS) + c2cweb.def Definitionsdatei (wird nur fuer die Kompilation + unter OS/2 gebraucht) + COPYING GNU Kopierlizenz + c2cweb.w die CWEB-Realisation dieses Programms + Um eine TeX .dvi-Datei zu erhalten sollte man + make documentation + eingeben (mit der CWEAVE-Version dieses Pakets) + c2cweb.c extrahiert mittels CTANGLE aus c2cweb.w + c2cweb.dvi die finale .dvi-Datei + c2cweb.exe ausfuehrbares Programm fuer DOS und OS/2 + compiler.w Compiler-abhaengige Formatierbefehle (von c2cweb + erzeugte Dateien inkludieren diese Datei) + c2cweb.ger ein kleines Deutsch-`Paket', um die Unterstuetzung + von nichtenglischen Sprachen zu demonstrieren. + example.h + example.c ein C-Code-Beispiel mit bereits eingefuegten + c2cweb-Kontrollbefehlen + cweb/cweave.exe eine modifizierte Version von CWEAVE Version 3.2 + (fuer DOS und OS/2) mit drei wichtigen neuen + Eigenschaften: + + o mit der Option +a formatiert CWEAVE den + Quellcode anders, und zwar im Prinzip + + if(a) + {bla bla bla... + } + + anstelle von + + if(a) { + bla bla bla... + } + + o die Option +i veranlasst CWEAVE, jedem + Funktionsnamen () im Index anzuhaengen (nur + sinnvoll, wenn alle Namen eindeutig sind) + o die Behandlung von Praeprozessorbefehlen + (#define etc.) wurde entscheidend verbessert, um + Makros mit Parametern zu ermoeglichen + cweb/cweave.w die originale CWEAVE Quelldatei aus Levy's + CWEB-Paket Version 3.2 (Juli 1994) + cweb/cweave.ch Change-Datei fuer cweave.w + cweb/cweave.c extrahiert aus cweave.{w,ch} mittels CTANGLE + cweb/common.h wird benoetigt fuer die Kompilation von cweave.exe + (Original wurde etwas modifiziert) + cweb/common.w unveraendert von CWEB 3.2 uebernommen + cweb/common.ch Change-Datei fuer common.w + cweb/common.c extrahiert aus common.{w,ch} mittels CTANGLE + cweb/Makefile Make-Datei fuer CWEAVE (UNIX, OS/2 und DOS) + cweb/cweave.def Definitionsfile (wird nur fuer die Kompilation unter + OS/2 gebraucht) + cweb/cwebmac.ori urspruengliche cwebmac.tex-Datei (leicht veraendert) + cweb/cwebmac.tex alternative Formatdatei + cweb/cwebmaca.tex diese alternative Formatdatei wird von TeX gelesen, + wenn die +a-Option angegeben ist; sonst gleich zu + cwebmac.tex + cweb/prod-alt.w diese Datei ist eine Include-Datei von cweave.ch; + sie enthaelt die alternativen Syntaxregeln, falls + Option +a verwendet wird + +Es ist empfehlenswert, sich das originale CWEB-Paket zu besorgen, welches +weitere Dokumentation enthaelt. Ausserdem sind Change-Dateien fuer andere +Betriebssysteme enthalten. + + +Installation +------------ + +c2cweb (c2cweb.exe fuer DOS und OS/2 Benutzer) sollte im Pfad sein. + +CWEAVE (cweave.exe fuer DOS und OS/2 Benutzer) sollte auch im Pfad sein; es +braucht die Datei compiler.w (die Umgebungsvariable CWEBINPUTS setzt den +Pfad). + +TeX muss die Dateien c2cweb.ger, cwebmac.tex und cwebmaca.tex finden koennen +(der Pfad wird normalerweise von der TEXINPUT-Umgebungsvariablen +kontrolliert). + +DOS und WINDOWS Benuetzer muessen sicherstellen, dass emx.exe und rsx.exe +gefunden werden koennen (entweder im Pfad oder durch die Umgebungsvariablen +EMX und RSX bestimmt). + +OS/2 Benuetzer muessen sicherstellen, dass emx.dll und rsx.exe gefunden +werden koennen (entweder im Pfad oder durch die Umgebungsvariablen EMX und +RSX bestimmt). + +[emx.dll, emx.exe und rsx.exe koennen im Paket exmrsx.zip gefunden werden, + das Bestandteil von emTeX ist.] + + +Bemerkungen fuer UNIX Benutzer +------------------------------ + +Aufgrund der vielen verschiedenen UNIX-Systeme sind keine ausfuehrbaren +Dateien inkludiert. Die Programme sollten ohne groessere Probleme +kompilierbar sein, wenn gcc, die GNU C-Bibliothek und gnumake verwendet +wird. + + make all OS=unix + +im c2cweb- und cweb-Verzeichnis erstellt die ausfuehrbaren Dateien. + +In diesem Paket befinden sich bereits mittels CTANGLE extrahiert .c Dateien, +die man direkt kompilieren kann. + + +Verwendung +---------- + +Die einzige Veraenderung, die der Anwender normalerweise machen muss, ist +das Einfuegen von /*@@*/ am Anfang einer leeren Zeile ausserhalb eines +Kommentars oder Strings vor dem ersten Funktionsblock (das ist in der Regel +main(){ ...}) im C-Quellcode. Nach diesem Befehl erzeugt c2cweb fuer jede +Funktion einen eigenen Abschnitt. Vor /*@@*/ wird alles in eine einzige +(u.U. grosse) Sektion geschrieben. Will man solche Abschnitte zusaetzlich +strukturieren oder hat man Struktur-Definitionen zwischen Funktionen, sollte +man /*@*/ verwenden, um einen neuen Abschnitt zu beginnen, ohne den +Funktionsalgorithmus einzuschalten (und natuerlich /*@@*/ vor der naechsten +Funktion einfuegen). Der Funktionsalgorithmus zaehlt einfach paarweise +geschwungene Klammern; falls keine mehr offen, wird ein neuer Abschnitt +begonnen. In der Beispieldatei example.c sind diese Kontrollcodes bereits +enthalten. + + +Aufruf: + + c2cweb [Optionen] C-Quelldatei(en) | @Response-Datei(en) + +Folgende Optionen sind moeglich: + +-v: + alle Kommentare werden in Schreibmaschinenschrift ausgegeben. Dieser + Schalter ist notwendig, falls die Kommentare bereits formatiert sind, zum + Beispiel + + /********************/ + /* example.c */ + /********************/. + +-t Wert: + all Tabulator-Stopps werden expandiert; -t Wert definiert die + Tabulatorweite (Grundeinstellung: Wert=4). + +-l: + Zeilenumbrueche in der Eingabe innerhalb von C-Text bleiben in der Ausgabe + erhalten durch explizites Anhaengen von @/ (einem CWEB Kontrollcode) an + jede Code-Zeile. + +-o Verzeichnis + definiert das Ausgabeverzeichis (muss bereits existieren). + +-o Format-Datei + Inkludiert (zusaetzlich zu compiler.w) eine Format-Datei + +-b "Titel": + +definiert einen Titel (in doppelten Anfuehrungszeichen). Dieser String wird +direkt an TeX weitergegeben. + +-1: + einseitige Ausgabe (d.h., linker Seitenkopf wird dem rechten Seitenkopf + gleichgesetzt). + + +Die letzten Schritte sind das Aufrufen von CWEAVE mit der transformierten +Hauptdatei und danach der Aufruf von TeX, um eine druckfaehige .dvi-Datei zu +erhalten. + + +Ein Beispiel: + + die Eingabedateien: + header1.h, header2.h, file1.c, file2.c, file3.c + + Man muss jetzt + c2cweb [Optionen] header*.h file*.c + + aufrufen, um die *.*w-Dateien zu erhalten. c2cweb bearbeitet jetzt alle + Eingabedateien und teilt mit, welche Datei die Eingabedatei fuer CWEAVE + ist (hier im Beispiel sei es file3.cw). + + Nach dem Aufruf + cweave [Optionen] file3.cw + + erhaelt man eine .tex-Datei (und einige zusaetzliche Hilfsdateien), die + mit PLAIN TeX weiterverarbeitet werden muss (LaTeX wird nicht + unterstuetzt): + + tex file3[.tex] + + +Im Abschnitt `Hints and Tricks' in der Datei c2cweb.w werden weitere +Verbesserungen und Beschraenkungen von c2cweb beschrieben (der Aufruf `make +documentation' erzeugt die .dvi-Datei). + + +Autor +----- + +Werner Lemberg (a7971428@unet.univie.ac.at) + +Bitte alle Fehler, Kommentare oder Bemerkungen an obige email-Adresse +schicken. + + Werner Lemberg + Goldschlagstr. 52/14 + A-1150 Vienna/Austria + + +--- end of c2cweb.txt --- diff --git a/web/c_cpp/c2cweb/c2cweb.w b/web/c_cpp/c2cweb/c2cweb.w new file mode 100644 index 0000000000..eb3b0e7691 --- /dev/null +++ b/web/c_cpp/c2cweb/c2cweb.w @@ -0,0 +1,1406 @@ +% This is the cweb file c2cweb Version 1.5 10-Nov-1996 +% +% You should process this file with +% +% cweave +ai c2cweb.w + +\pageno=\contentspagenumber \advance\pageno by 1 +\let\maybe=\iftrue +\fullpageheight=240mm +\pageheight=223mm +\pagewidth=158mm +\setpage +\frenchspacing +\def\in{\leavevmode\vrule width 0pt\nobreak\hskip 2em\hskip 0pt} % indentation +\font\sixrm=cmr6 +\def\tm{$^{\hbox{\sixrm TM}}$} % trademark + +\def\title{c2cweb (Version 1.5)} + +\def\topofcontents{% + \null\vfill + \centerline{\titlefont The {\ttitlefont c2cweb} program} + \vskip 20pt + \centerline{(Version 1.5)} + \vfill} + +\def\botofcontents{% + \vfill + \noindent + Copyright \copyright\ 1996 by Werner Lemberg + \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 distributed under the terms of a + permission notice identical to this one.} + + + +@* Introduction. +This is the \.{c2cweb} program by Werner Lemberg +(\.{a7971428@@unet.univie.ac.at}). + +The ``banner line'' defined here should be changed whenever \.{c2cweb} is +modified. + +@d banner "\nThis is c2cweb Version 1.5 (c) 1996 by Werner Lemberg\n\n" + + +@ +\.{c2cweb} will transform ordinary \CEE/ or \CPLUSPLUS/ source code into +\.{CWEB} formatted code. Three primary functions are performed: inserting +\.{@@}-commands between code blocks, transforming comments into \TeX-text, +and replacing offending characters like \.{\\}, \.{\_}, \.{\&} etc. with +commands \TeX\ (and \.{CWEB}) can understand. + +The only changes the user has to do normally is to insert `\.{/*@@@@*/}' or +`\.{/*@@*/}' \\{starting a line} outside of a comment or string (the rest of +these lines will be ignored): + +\advance\leftskip by 3em + + \item{$\bullet$} \.{/*@@@@*/} starts a new section and switches the + function block algorithm on (see below). + + \item{$\bullet$} \.{/*@@*/} starts a new section and switches the + function block algorithm off. This is the default when \.{c2cweb} + begins to scan a file. + +\advance\leftskip by -3em + +Both `commands' will be suppressed in the output (two additional commands +are described in the `Hints and Tricks'-section). + + +@ +Normal \CEE/ code consists of two parts: the code before function blocks +(\.{\#include} and \.{\#define} statements, prototypes, structure +definitions, global variables, etc.) and the function blocks (i.e. +\.{foo()\{ ... \}}) itselves (possibly mixed with global definitions of +variables, structures etc.). The main reason to separate them are memory +constraints of \.{CWEAVE}; inserting \.{/*@@@@*/} causes each function block +to be written into an own section. + +In header files, nothing is to do because there are no function blocks. In +\CEE/ code files, it's usually sufficient to insert \.{/*@@@@*/} once, but +you can structure your code further by inserting \.{/*@@*/} (and +\.{/*@@@@*/} if necessary). See also the example file delivered with this +package. + +You will need a special \.{CWEAVE} executable which has among others two +additional control codes sometimes needed. See also the section `Hints and +Tricks'. + +From now on, the words \.{CWEB} and \.{CWEAVE} will be used mutually in +spite of the fact that \.{CWEB} consists of both the programs \.{CWEAVE} and +\.{CTANGLE}---\.{c2cweb} is intended to create a well printed listing, and +the use of \.{CTANGLE} is of course possible but senseless. + + + +@* The program. +The use of |_response()|, |_wildcard()|, and |_getname()| is compiler +specific. If you don't use emx-gcc, it's likely that you have to use +different functions. If you use this program under DOS, consider the +\UNIX/-like behavior of the ``|*|''-wildcard character. + +After processing the options, the global variable |optind| (defined in +\.{getopt.h}) is the index to the first file name. + +@d FALSE 0 +@d TRUE 1 +@d DONE 2 +@d WAIT 3 + +@c +@<Include files@>; +@<Prototypes@>; +@<Global variables@>;@# + +void main(argc, argv) + int argc; + char *argv[]; + + {int i; + char buffer[DIR_LENGTH + FILE_NAME_LENGTH + 1]; + char *p, *q; + + + printf(banner);@# + +#ifdef __EMX__ + _response(&argc, &argv); /* to expand response files */ + _wildcard(&argc, &argv); /* to expand wildcards */ +#endif@# + + @<Get command switches@>;@# + + @<Processing all files except the last one@>; + @<Processing the last file@>;@# + + fclose(in); + fclose(out); + } + + +@ +\.{getopt.h} contains the \UNIX/-specific |getopt()|. If your system doesn't +support this function, get the GNU \CEE/-library for an implementation. + +@<Include files@>= +#include <ctype.h> +#include <getopt.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + + +@ +@<Processing all files except the last one@>= + for (i = optind; i < argc - 1; i++) + {printf(" processing %s\n", argv[i]);@# + + open_files(argv[i]); + q = protect_underlines(_getname(argv[i])); + if ((p = strrchr(q, '.')) != NULL) + /* the macro \.{\\ZZZ} suppresses the final dot after + the section title if the filename contains a dot */ + fprintf(out,@/ + "@@*{%s\\ZZZ{\\setbox0=\\hbox{%s}\\hskip-\\wd0}}.\n"@/ + "\\ind=2\n\n", q, p); + else + fprintf(out, "@@*{%s}.\n"@/ + "\\ind=2\n\n", q);@# + + handle_input(); + fclose(in); + fclose(out); + } + + +@ +The last file will be the `master' file; you have to call \.{CWEAVE} with +this file as the argument; all other files will be included. + +@<Processing the last file@>= + printf(" processing %s\n", argv[i]);@# + + open_files(argv[i]); + @<Write limbo@>; + q = protect_underlines(_getname(argv[i])); + if ((p = strrchr(q, '.')) != NULL) + fprintf(out,@/ + "@@*{%s\\ZZZ{\\setbox0=\\hbox{%s}\\hskip-\\wd0}}.\n"@/ + "\\ind=2\n\n", q, p); + else + fprintf(out, "@@*{%s}.\n"@/ + "\\ind=2\n\n", q);@# + + handle_input(); + @<Write index section@>;@# + + strcpy(buffer, argv[argc - 1]); + modify_filename(buffer);@# + + printf(@/ + "\n You must now call CWEAVE with %s%s\n"@/ + " as the argument to get a TeX output", outdir, _getname(buffer)); + if (optind < argc - 1) + printf(" of all processed files"); + printf("\n"); + + + +@*1 The input switches. +If the switch \.{-v} is set, all comments are written in typewriter type; +comments starting a line will also start a line in the output. + +All tabs will be expanded, and the \.{-t} switch defines the tab length +(default value is 4). + +The switch \.{-l} causes all linefeeds inside of \CEE/-text to be output +explicitly by inserting a \.{@@/} command at the end of each code line. + +The output directory will be set with the \.{-o} option; this directory must +already exist. Probably you have to adjust |PATH_SEPARATOR| and +|pathsepchar[]| to make it work correctly under your operating system. + +To get a title, use the \.{-b} switch with the titlestring enclosed in +double quotes; this string will be passed directly to \TeX. + +To include a format file (additionally to \.{compiler.w}), use the \.{-f} +switch. + +One-sided output is enabled with the option \.{-1} set. + +The global variable |optarg| (defined in \.{getopt.h}) points to the option +argument; the string |optswchar[]| is modified to allow |'-'| and |'/'| as +characters which start options (not under \UNIX/). + +@d DIR_LENGTH 80 +@d TITLE_LENGTH 100 +@d PATH_SEPARATOR '/' + +@<Global...@>= +int tab_length = 4; +int verbatim = FALSE; +int user_linefeed = FALSE; +int one_side = FALSE; +char outdir[DIR_LENGTH + 1]; +char include_file[FILE_NAME_LENGTH + 1]; +char title[TITLE_LENGTH + 1];@# + +#ifdef __EMX__ +char optchar[] = "-/"; +char pathsepchar[] = "\\/"; +#else +char optchar[] = "-"; +char pathsepchar[] = "/"; +#endif@# + +char at_nmb[] = "@@#"; + /* the \.{CWEB} command for inserting vertical space */ + + +@ +@<Get command switches@>= + {int c; + int i; + + + outdir[0] = '\0'; + include_file[0] = '\0'; + title[0] = '\0';@# + +#ifdef __EMX__ + optswchar = optchar; +#endif@# + + strcpy(title, "c2cweb output"); /* the default title */@# + + while ((c = getopt(argc, argv, "b:f:lo:t:v1")) != EOF) + {switch (c) + {case 'b': + if (strchr(optchar, optarg[0])) + /* check if argument is an option */ + usage();@# + + if (strlen(optarg) >= TITLE_LENGTH) + fprintf(stderr,@/ + "\nTitle too long. Will use \"c2cweb output\".\n"); + else + strcpy(title, optarg); + break; + case 'f': + if (strchr(optchar, optarg[0])) + usage();@# + + if (strlen(optarg) >= FILE_NAME_LENGTH) + fprintf(stderr,@/ + "\nInclude file name too long. Will be ignored.\n"); + else + strcpy(include_file, optarg); + break; + case 'l': + user_linefeed = TRUE; + break; + case 'o': + if (strchr(optchar, optarg[0])) + usage();@# + + if ((i = strlen(optarg)) >= DIR_LENGTH) + fprintf(stderr,@/ + "\nOutput directory name too long. Will be ignored.\n"); + else + {strcpy(outdir, optarg); + if (!strchr(pathsepchar, outdir[i - 1])) + /* check if last character is a path separator */ + {outdir[i] = PATH_SEPARATOR; + outdir[i + 1] = '\0'; + } + } + break; + case 't': + if (strchr(optchar, optarg[0])) + usage();@# + + tab_length = atoi(optarg); + if (tab_length == 0 || tab_length > 8) + tab_length = 4; /* default value */ + break; + case 'v': + verbatim = TRUE; + break; + case '1': + one_side = TRUE; + break; + default: + usage(); + break; + } + }@# + + if (optind == argc) + usage(); + } + + +@ +The output file has the same name as the input file but a different +extension: it will append a \.{w} or, if the extension is three characters +long, substitute the third character with a \.{w}, i.e. \.{.c} becomes +\.{.cw}, \.{.h} becomes \.{.hw}, \.{.cpp} will be replaced by \.{.cpw} and +so on (notice that for example \.{.cppp} also becomes \.{.cpw}). If the +character to be changed is a \.{w}, an \.{x} is used instead of. This will +be done by the function |modify_filename()|. No checking for overwriting +existing files is done. + +@d FILE_NAME_LENGTH 80 + +@<Global...@>= + FILE *in, *out; + + +@ +@<Prototypes@>= +void open_files(char *); + +@ +@c +void open_files(filename) + char *filename; + {char buffer[DIR_LENGTH + FILE_NAME_LENGTH + 1]; + + + if (strlen(filename) > FILE_NAME_LENGTH - 2) + {fprintf(stderr, "\n File name too long.\n"); + exit(-1); + }@# + + if ((in = fopen(filename, "rt")) == NULL) + {fprintf(stderr, "\n Can't open input file %s\n", filename); + exit(-1); + }@# + + strcpy(buffer, outdir); + strcat(buffer, filename); + modify_filename(buffer);@# + + if ((out = fopen(buffer, "wt")) == NULL) + {fprintf(stderr, "\n Can't open output file %s\n", buffer); + exit(-1); + } + } + + + +@*1 The output header. +This is the header of the last output file. You must call \.{CWEAVE} with +this file as an argument---all other processed files will be included. + +Additionally this `master' file will include the file \.{compiler.w}, which +should contain all system dependent definitions (like \.{va\_decl} or +\.{va\_arg}) not contained in the \.{CWEAVE} program. The syntax of +\.{compiler.w} is \.{CWEB} syntax; please read the documentation if you have +questions. You should set the environment variable |CWEBINPUTS| used by +\.{CWEAVE} (and \.{CTANGLE}) to the directory where \.{compiler.w} resides. + +The function |_getname()| will accept forward and backward slashes as path +separators if you compile under emx. However, options can be introduced with +|'-'| and |'/'|, therefore paths starting with a forward slash must be +enclosed in double quotes (not under \UNIX/). + +@<Write limbo@>= + fprintf(out,@/ + "\\font\\symb=cmsy10\n"@/ + "\\font\\math=cmmi10\n"@/ + "\\def\\ob"@/ + "{\\parskip=0pt\\parindent=0pt%%\n"@/ + "\\let\\\\=\\BS\\let\\{=\\LB\\let\\}=\\RB\\let\\~=\\TL%%\n"@/ + "\\let\\ =\\SP\\let\\_=\\UL\\let\\&=\\AM\\let\\^=\\CF%%\n"@/ + "\\obeyspaces\\frenchspacing\\tt}\n"@/ + "\n"@/ + "\\def\\e{\\hfill\\break\\hbox{}}\n"@/ + "\\def\\{{\\relax\\ifmmode\\lbrace\\else$\\lbrace$\\fi}\n"@/ + "\\def\\}{\\relax\\ifmmode\\rbrace\\else$\\rbrace$\\fi}\n"@/ + "\\def\\takenone#1{\\hskip-0.1em}\n"@/ + "\\let\\ZZZ=\\relax\n"@/ + "\n"@/ + "%s"@/ + "\n"@/ + "\\pageno=\\contentspagenumber \\advance\\pageno by 1\n"@/ + "\\let\\maybe=\\iftrue\n"@/ + "\n"@/ + "\\def\\title{%s}\n"@/ + "\n"@/ + "@@i compiler.w\n"@/ + "\n", one_side ? "\\let\\lheader=\\rheader\n" : "", title);@# + + if (*include_file) + fprintf(out,@/ + "@@i %s\n"@/ + "\n", include_file);@# + + for (i = optind; i < argc - 1; i++) + {strcpy(buffer, argv[i]); + modify_filename(buffer);@# + + fprintf(out, "@@i %s\n", _getname(buffer)); + }@# + + fputc('\n', out);@# + + + +@*1 Input Handling. + +@d BUFFER_LENGTH 500 + +@<Prototypes@>= +void handle_input(void); + + +@ +This is a wild hack. Perhaps in a future version I will improve it. + +@<Global...@>= +char buffer[BUFFER_LENGTH + 1]; + +@ +The main routine. Characters which will be treated in a special way by +\.{CWEB} and \TeX\ are handled within a great switch. + +@d xisspace(c) (isspace(c) && ((unsigned char)c < 0200)) + +@c +void handle_input(void) + {char *buf_p; + char ch; + @<Control flags@>; + + line_number = 0; + while (get_line()) + {buf_p = buffer;@# + + do + {ch = *buf_p;@# + + @<Special cases@>;@# + + switch (ch) + {case ' ': + if (leading_blanks) + {blank_count++; + goto end; + } + break;@# + + case '\t': + {int i = tab_length - (column % tab_length); + + column += i - 1; /* we'll say later |column++| */@# + + if (leading_blanks) + {blank_count += i; + goto end; + }@# + + while (i--) + fputc(' ', out); + goto end; + } + break;@# + + case '{': + @<Cases for |'{'|@>; + break;@# + + case '}': + @<Cases for |'}'|@>; + break;@# + + case '/': + @<Cases for |'/'|@>; + break;@# + + case '*': + @<Cases for |'*'|@>; + break;@# + + case '\n': + @<Cases for |'\n'|@>; + break;@# + + case '@@': + @<Cases for |'@@'|@>; + break;@# + + case '\'': + @<Cases for |'\''|@>; + break;@# + + case '\"': + @<Cases for |'\"'|@>; + break;@# + + case '\\': + @<Cases for |'\\'|@>; + break;@# + + default: + @<Cases for |default|@>; + break; + }@# + + fputc(ch, out);@# + +end: + buf_p++; + column++; + } while(ch != '\n'); + } + } + + +@ +@<Control flags@>= + int any_input = FALSE; + /* set after the first non blank character in a new section */ + int brace_count = 0; + int blank_count = 0;@# + + int in_comment = FALSE; + int in_C = FALSE; + int in_string = FALSE; + int short_comment = FALSE; + int leading_blanks = TRUE; + int double_linefeed = FALSE; /* set if last character was a linefeed */ + int linefeed_comment = FALSE; + /* set if a comment follows a linefeed immediately */ + int comment_slash = FALSE; /* set if last character was a slash */ + int comment_star = FALSE; /* set if last character was a star */ + int escape_state = FALSE; + /* needed to check whether in string or + at the end of a preprocessor line */ + int before_TeX_text = FALSE; + /* the state after leaving a function block */ + int function_blocks = FALSE; /* set if function block algorithm is on */ + + +@ +Here comes a bunch of |if|-statements. + +@<Special cases@>= + @<Check conditions while at start of a line@>;@# + + if (double_linefeed && ch == '/') + linefeed_comment = TRUE;@# + + if (double_linefeed && (ch == ' ' || ch == '\t')) + leading_blanks = TRUE;@# + + if (ch != '\n') /* multiple linefeed ? */ + double_linefeed = FALSE;@# + + if (!xisspace(ch)) /* whitespaces ? */ + {any_input = TRUE;@# + + if (before_TeX_text && function_blocks) + {before_TeX_text = FALSE;@# + + if (function_blocks == WAIT) + function_blocks = TRUE; + /* start at the second occurrence */ + else + {fputs("@@\n"@/ + "\\ind=2\n\n", out); + /* start a new text section */@# + + if (leading_blanks) + {leading_blanks = FALSE;@# + + while (blank_count--) + fputc(' ', out); + blank_count = 0; + } + } + }@# + + if (in_comment && leading_blanks) + {leading_blanks = FALSE;@# + + while (blank_count--) + fputc(' ', out); + blank_count = 0; + } + }@# + + if (!(ch == '/' || xisspace(ch))) + /* whitespace or possible start of comment ? */ + {if (!(in_comment || in_C || comment_slash)) + /* outside of code ? */ + {in_C = TRUE;@# + + fputs("@@c\n", out); /* start of a new code section */@# + + if (leading_blanks) + {leading_blanks = FALSE;@# + + while (blank_count--) + fputc(' ', out); + blank_count = 0; + } + }@# + + if (!(in_comment || comment_slash) && leading_blanks) + {leading_blanks = FALSE;@# + + while (blank_count--) + fputc(' ', out); + blank_count = 0; + } + }@# + + if (comment_slash && !(ch == '*' || ch == '/')) + /* start of a comment ? */ + {comment_slash = FALSE; + if (!in_comment) + linefeed_comment = FALSE;@# + + fputc('/', out); + }@# + + if (comment_star && ch != '/') /* end of a comment ? */ + {comment_star = FALSE;@# + + fputc('*', out); + }@# + + if (escape_state && !(ch == '\"' || ch == '\n' || ch == '\\')) + /* end of string or end of preprocessor line or backslash itself ? */ + escape_state = FALSE;@# + + +@ +@<Check conditions while at start of a line@>= + if (buf_p == buffer) + {if (!(in_comment || in_string)) + {if (!strncmp(buf_p, "/""*@@@@*""/", 6)) + {in_C = FALSE; + before_TeX_text = TRUE; + function_blocks = WAIT; /* switch on the algorithm */ + brace_count = 0;@# + + if (any_input) + fputs("\n@@\n"@/ + "\\ind=2\n\n", out); + /* start a new section */@# + + any_input = FALSE; + *(buf_p--) = '\n'; + /* the rest of the line will be ignored */ + goto end; + } + else if (!strncmp(buf_p, "/""*@@*""/", 5)) + {in_C = FALSE; + before_TeX_text = TRUE; + function_blocks = FALSE; /* switch off the algorithm */@# + + if (any_input) + fputs("\n@@\n"@/ + "\\ind=2\n\n", out);@# + + any_input = FALSE; + *(buf_p--) = '\n'; + goto end; + } + else if (!strncmp(buf_p, "/""*{*""/", 5)) + {brace_count++; /* a dummy opening brace */ + fputs("@@{\n", out);@# + + ch = '\n'; /* an end of line is simulated */ + goto end; + } + else if (!strncmp(buf_p, "/""*}*""/", 5)) + {brace_count--; /* a dummy closing brace */ + fputs("@@}\n", out);@# + + if (!brace_count && function_blocks) + /* end of function block reached ? */ + {in_C = FALSE; + before_TeX_text = TRUE;@# + + break; + }@# + + ch = '\n'; + goto end; + } + else if (!strncmp(buf_p, "/""*{}*""/", 6)) + {fputs("@@{@@}\n", out);@# /* a dummy function body */ + + ch = '\n'; + goto end; + } + } + } + + +@ +@<Cases for |'{'|@>= + if (in_comment) + fputc('\\', out); + else if (in_string) + break; + else if (function_blocks) + {brace_count++; + in_C = TRUE; + } + +@ +@<Cases for |'@@'|@>= + if (in_comment) + {fputs("{\\char64}", out); + goto end; + } + else /* \.{CWEB} needs \.{@@@@} to output \.{@@} */ + fputc('@@', out); + +@ +@<Cases for |'\''|@>= + if (!in_comment) + {if (*(buf_p + 1) == '\"' && *(buf_p + 2) == '\'') + escape_state = TRUE; /* this catches |'"'| */ + } + +@ +@<Cases for |'\"'|@>= + if (!in_comment) /* start or end of a string ? */ + {if (escape_state) + escape_state = FALSE; + else + in_string = TRUE - in_string; + } + +@ +@<Cases for |'\\'|@>= + if (in_comment) + {fputs("{\\symb\\char110}", out); + goto end; + } + else + escape_state = TRUE - escape_state; + /* continuation of preprocessor line or + an escape character */ + +@ +@<Cases for |'}'|@>= + if (in_comment) + fputc('\\', out); + else if (in_string) + break; + else if (function_blocks) + {brace_count--; + if (!brace_count) /* end of function block reached ? */ + {in_C = FALSE;@# + + before_TeX_text = TRUE; + break; + } + } + +@ +@<Cases for |'/'|@>= + if (comment_star) + {@<Cases for |'*/'|@>; + } + else if (comment_slash) + {@<Cases for |'//'|@>; + } + else + {comment_slash = TRUE;@# + + goto end; + } + +@ +@<Cases for |'*/'|@>= + comment_star = FALSE; + leading_blanks = FALSE;@# + + if (!short_comment) + {in_comment = FALSE; /* end of comment */@# + + if (!in_C) + {linefeed_comment = FALSE;@# + + if (verbatim) @q { @> + fputs("*""/}", out);@# + + if (*(buf_p + 1) == '\n') /* end of line ? */ + fputs("\\e{}%", out);@# + + goto end; + } + + if (in_C && verbatim) + {if (linefeed_comment) + {linefeed_comment = FALSE;@# + + fputs("*""/@@>", out); + if (*(buf_p + 1) == '\n' && !user_linefeed) + fputs("@@/", out); + goto end; + } + else @q { @> + fputc('}', out);@# + }@# + + linefeed_comment = FALSE;@# + + if (in_C || verbatim) + fputc('*', out); + else + goto end; + } + else + fputc('*', out); + +@ +@<Cases for |'//'|@>= + comment_slash = FALSE;@# + + if (!short_comment) + {in_comment = TRUE; + short_comment = TRUE; /* start of a short comment */@# + + if (!in_C && verbatim) + {fputs("{\\ob{}", out); @q } @> + if (leading_blanks) + {leading_blanks = FALSE;@# + + while (blank_count--) + fputc(' ', out); + blank_count = 0; + } + fputs("//", out);@# + + goto end; + }@# + + if (in_C && verbatim) + {if (leading_blanks || linefeed_comment) + {linefeed_comment = TRUE;@# + + if (!user_linefeed) + fputs("@@/", out); @q { @> + fputs("@@t}\\8{\\ob{}", out); @q } @> + /* this cryptic command starts a + comment line without indentation */ + if (leading_blanks) + {leading_blanks = FALSE;@# + + while (blank_count--) + fputc(' ', out); + blank_count = 0; + } + fputs("//", out); + } + else + fputs("//{\\ob{}", out);@# @q } @> + + goto end; + }@# + + if (in_C || verbatim) + fputc('/', out); + else + goto end; + } + else + fputc('/', out); + + +@ +@<Cases for |'*'|@>= + if (comment_slash) + {comment_slash = FALSE;@# + + if (in_comment && !short_comment) + /* uuh, ooh, for people who `comment + out' code with comments instead of + using \.{\#if 0} and \.{\#endif} for + example---this non-ANSI behaviour + would cause bad formatted code and is + therefore not supported; the program + exits */ + {fprintf(stderr, + " Error line %d: Nested comments not supported\n", + line_number); + exit(-1); + }@# + + if (!short_comment) + {in_comment = TRUE; /* start of comment */@# + + if (!in_C && verbatim) + {fputs("{\\ob{}", out); @q } @> + if (leading_blanks) + {leading_blanks = FALSE;@# + + while (blank_count--) + fputc(' ', out); + blank_count = 0; + } + fputs("/""*", out);@# + + goto end; + }@# + + if (in_C && verbatim) + {if (leading_blanks || linefeed_comment) + {linefeed_comment = TRUE; + + if (!user_linefeed) + fputs("@@/", out); @q { @> + fputs("@@t}\\8{\\ob{}", out); @q } @> + if (leading_blanks) + {leading_blanks = FALSE;@# + + while (blank_count--) + fputc(' ', out); + blank_count = 0; + } + fputs("/""*", out); + } + else + fputs("/""*{\\ob{}", out);@# @q } @> + + goto end; + }@# + + if (in_C || verbatim) + fputc('/', out); + else + {fputs(" ", out);@# + + goto end; + } + } + else + fputc('/', out); + } + else + {comment_star = TRUE;@# + + goto end; + } + + +@ +@<Cases for |'\n'|@>= + blank_count = 0;@# + + if (!in_comment && in_C) + {if (double_linefeed == FALSE) + {double_linefeed = TRUE; + if (escape_state) + {escape_state = FALSE; + /* continuation of a preprocessor line */ + leading_blanks = TRUE;@# + + if (in_string) + fputc('\n', out); + else + fputs("\n@@/", out); + goto end; + }@# + + if (!leading_blanks && user_linefeed) + fputs("@@/", out); + } + else if (double_linefeed == TRUE) + {double_linefeed = DONE; + /* blank lines in the input will be output as + little white space between code lines */@# + fputs(at_nmb, out); + } + }@# + + leading_blanks = TRUE;@# + + if (short_comment) + {short_comment = FALSE; + in_comment = FALSE; + double_linefeed = TRUE;@# + + if (verbatim) + {if (linefeed_comment && in_C) + fputs("@@>", out); + else @q { @> + fputc('}', out); + }@# + + if (!in_C) + fputs("\\e{}%", out); + else if (linefeed_comment && verbatim) + fputs("@@/", out);@# + + linefeed_comment = FALSE; + }@# + + if (in_comment && in_C && verbatim && linefeed_comment) + { @q { @> + fputs("@@>@@/\n@@t}\\8{\\ob{}", out); @q } @> + /* continuation of a comment line without indentation */ + goto end; + }@# + + if (in_comment && verbatim) + /* Both \.{CWEAVE} and \TeX\ need an input at the beginning of a line + to prevent leading blanks be swallowed while in verbatim mode; + this will be the macro \.{\\e} which causes a linebreak */ + {fputs("\n\\e{}", out); + goto end; + } + + +@ +All other special characters will be substituted with proper sequences \TeX\ +can understand. + +@<Cases for |default|@>= + if (in_comment) + {switch (ch) + {case '#': + fputs("{\\#}", out); + break;@# + + case '$': + fputs("{\\$}", out); + break;@# + + case '%': + fputs("{\\%}", out); + break;@# + + case '&': + fputs("{\\AM}", out); + break;@# + + case '_': + fputs("{\\_}", out); + break;@# + + case '^': + fputs("{\\^{}}", out); + break;@# + + case '\\': + fputs("{\\symb\\char110}", out); + break;@# + + case '~': + fputs("{\\~{}}", out); + break;@# + + case '|': + fputs("{\\symb\\char106}", out); + break;@# + + case '<': + fputs("{\\math\\char60}", out); + break;@# + + case '>': + fputs("{\\math\\char62}", out); + break;@# + + default: + fputc(ch, out); + break; + }@# + + goto end; + } + + +@ +Because the index macros defined in \.{cwebmac.tex} don't append a dot, we +have to redefine \.{\\ZZZ}. + +@<Write index section@>= + fprintf(out,@/ + "\n"@/ + "@@*Index.\n"@/ + "\\let\\ZZZ=\\takenone\n"); + + +@ +@<Prototypes@>= +void usage(void); + +@ +@c +void usage(void) + {fprintf(stderr,@/ + "Usage: c2cweb [switches] csourcefile(s) | @@responsefile(s)"@/ + "\n"@/ + "\n possible switches:"@/ + "\n"@/ + "\n -b \"title\" set title"@/ + "\n -l use input linefeeds"@/ + "\n -o dirname set output directory (must already exist)"@/ + "\n -f filename use this file as a format file"@/ + "\n -t tablength set tabulator length (default 4)"@/ + "\n -v verbatim mode"@/ + "\n -1 one-sided output"@/ + "\n"@/ + "\n");@# + + exit(-1); + } + + +@ +@<Prototypes@>= +void modify_filename(char *); + + +@ +If a response file is expanded, trailing blanks can occur which will be +ignored here. The same happens if a filename with trailing blanks is +enclosed in quotes. + +@c +void modify_filename(name) + char *name; + {char *p; + + + if ((p = strrchr(name, '.')) != NULL) + {p++; + if (*p && *p != ' ') + p++; + if (*p && *p != ' ') + p++; + if (*p != 'w') + *p = 'w'; + else + *p = 'x'; + p++; + *p = '\0'; + } + else + strcat(name, ".w"); + } + + +@ +@<Global...@>= +int line_number = 0; +int column; + + +@ +@<Prototypes@>= +char *get_line(void); + + +@ +|get_line()| gets the next line and removes all trailing blanks or tabs. + +@c +char *get_line(void) + {char *p; + int i = BUFFER_LENGTH; + + + if ((p = fgets(buffer, BUFFER_LENGTH + 1, in)) != NULL) + {while (i--) + {if (*(p++) == '\n') + break; + }@# + + p--; + p--; + while ((*p == ' ' || *p == '\t') && p >= buffer) + p--; + *(p + 1) = '\n'; + *(p + 2) = '\0';@# + + line_number++; + column = 0; + } + return(p); + } + + +@ +@<Global...@>= +char tempbuf[2 * FILE_NAME_LENGTH + 1]; + + +@ +@<Prototypes@>= +char *protect_underlines(char *); + + +@ +This function is needed for the names of named sections, i.e. file names. + +@c +char *protect_underlines(p) + char *p; + {char *q; + + + q = tempbuf;@# + + do + {if (*p == '_') + *(q++) = '\\'; + *(q++) = *p; + } while (*(p++));@# + + return tempbuf; + } + + +@ +@<Prototypes@>= +#ifndef __EMX__ +char *_getname(char *); +#endif + + +@ +For Linux and other \UNIX/-systems the function |_getname()| must be +defined. + + +@c +#ifndef __EMX__ +char *_getname(char *path) + {char *p; + + p = strrchr(path, '/'); + return p == NULL ? path : (p + 1); + } +#endif + + + +@* Hints and Tricks. +Some words are reserved by \.{CWEAVE} you would like to use. Here is a small +list of them (see the manual for a complete one): + + \in \.{error}, \.{line}, \.{try}, $\ldots$ + +To make a word unreserved write a line + + \in \.{@@s} \\{ident} \\{x} + +into \.{compiler.w} where \\{ident} is the reserved word. + +Some words you would expect \.{CWEAVE} knows actually are not in its memory: + + \in \.{va\_arg}, \.{va\_end}, \.{va\_start}, $\ldots$ + +To make a word \\{ident1} reserved you should write + + \in \.{@@s} \\{ident1} \\{ident2} + +into \.{compiler.w}. Now \\{ident1} will behave as \\{ident2}; for example + + \in \.{@@s va\_decl va\_dcl} + +causes \.{CWEAVE} to treat \.{va\_decl} as it treats \.{va\_dcl}. + +Bear always in mind that \.{CWEAVE} has been developed for use with \UNIX/ +and not for DOS or OS/2. Weird non-ANSI constructions like + + \in \&{void \_FAR} |*| \&{\_FARFUNC \_Cdecl} \\{memcpy} $\ldots$ + +will cause some, hmmm, troubles if you use the original \.{CWEAVE} program. +Until now it's also a bit difficult to program for Windows\tm\ or the +Presentation Manager\tm, because you have to include every structure and +constant you use in your program manually, which can be a tedious work. + +Do not use the same name for a structure and an instance of it. This means +that you must avoid things like this: + + \in \&{struct foo} \\{foo;} + +\.{CWEAVE} would be confused totally. The same applies to all identifiers +which will be used as reserved and as unreserved words at the same time (This +usually will not affect identical names of variables and functions. But in +this case the reader of your program will get confused). + +Most of the \CEE/ constants are written in upper case, and \.{CWEAVE} writes +them in typewriter type. But some constants like \.{\_Windows} or +\.{\_\_cplusplus} are mixed or lower case, and \.{CWEAVE} will use italic +type instead of. Look at \.{compiler.w} again to see a workaround how to get +a correct output (Note: underline characters are converted to `x' in the +\TeX\ control string). + +Nested comments are not supported; the program aborts with an error message. + +If you have unbalanced braces due to \.{\#ifdef foo} $\ldots$ \.{\#endif} +constructions, you can keep \.{c2cweb} and your editor happy if you write a +\.{/*\{*/} or \.{/*\}*/} command where needed; otherwise memory of \.{CWEAVE} +can overflow. \.{c2cweb} replaces these commands with the equal \.{CWEB} +constructions (only defined in this package's modified \.{CWEAVE} version!). + +Another trick with preprocessor conditionals: + + \in \.{/*@@*/} + + \in \&{\#ifdef} \.{foo} + + \in\in \&{int} \\{func1} $\{\,\ldots\,\}$ + + \in \&{\#endif} + + \in \.{/*@@@@*/} + +Without \.{/*@@*/} the \&{\#endif} instruction would be written into the next +section. + +The \.{/*\{\}*/} command proved to be useful in cases like this: + + \in \&{\#if} \.{\_\_STDC\_\_} + + \in\in \&{void} \\{func1}(\&{int} \\{a}) + + \in \.{/*\{\}*/} + + \in \&{\#else} + + \in\in \&{void} \\{func1}(\\{a}) + + \in\in\in \&{int} \\{a}; + + \in \&{\#endif} + + \in $\{\,\ldots\,\}$ + +It is essentially the same as + + \in \.{/*\{*/} + + \in \.{/*\}*/} + +but will not start a new section (Thus it is not necessary to insert a +\.{/*@@*/} before the actual function). + + +@* Index. + + +@q end of c2cweb.w @> diff --git a/web/c_cpp/c2cweb/compiler.w b/web/c_cpp/c2cweb/compiler.w new file mode 100644 index 0000000000..e03320c0f2 --- /dev/null +++ b/web/c_cpp/c2cweb/compiler.w @@ -0,0 +1,71 @@ +@q this file is part of the c2cweb package Version 1.5 @> + +@q this is the file compiler.w @> +@q written by Werner Lemberg (a7971428@@unet.univie.ac.at) 10-Nov-1996 @> + + +@q the format definitions below are just examples and not complete! @> + +@q definition strings @> + +% A4 paper format + +\fullpageheight=240mm +\pageheight=223mm +\pagewidth=158mm +\setpage + +\def\botofcontents{ + \vfill + \centerline{This document was produced with the \.{c2cweb} + program by Werner Lemberg}} + +\input c2cweb.ger @q uncomment this if not needed @> + +\def\xWindows{\.{\_Windows}} +\def\xxcplusplus{\.{\_\_cplusplus}} + + +@s error x @q if you use C++ 3.0, you must delete this line @> +@s line x @q if you want to use the #line preprocessor command, delete this line @> + + +@q most of the DOS C and C++ compilers need that stuff @> + +@s _cdecl const +@s cdecl const +@s _cs const +@s _ds const +@s _es const +@s _export const +@s _far const +@s far const +@s huge const +@s interrupt const +@s _loadds const +@s _near const +@s near const +@s _pascal const +@s pascal const +@s _saveregs const +@s _seg const +@s _ss const + +@s _Windows TeX +@s __cplusplus TeX + + +@q some GNU C directives @> + +@s asm const +@s inline const +@s const const +@s __asm__ const +@s __inline__ const +@s __const__ const +@s __label__ int +@s __attribute__ int +@s __alignof__ sizeof + + +@q end of compiler.w @> diff --git a/web/c_cpp/c2cweb/cweb/Makefile b/web/c_cpp/c2cweb/cweb/Makefile new file mode 100644 index 0000000000..68d0ca8e18 --- /dev/null +++ b/web/c_cpp/c2cweb/cweb/Makefile @@ -0,0 +1,106 @@ +# This file is part of the c2cweb package Version 1.5 (10-Nov-1996) + +# GNU Makefile for c2cweb's version of CWEAVE + + +.PHONY: default all debug clean \ + cweb +.CAUTIOUS: common.c cweave.c + + +default: + @echo " say " + @echo ". " + @echo " make <TARGET> OS=<SYSTEM> " + @echo ". " + @echo " SYSTEM can be one of the following: " + @echo ". " + @echo " unix (gcc) " + @echo " os2 bound (emx) " + @echo " dos (djgpp) " + @echo ". " + @echo " TARGET can be one of the following: " + @echo ". " + @echo " all debug clean " + + +ifdef OS + + ifeq ($(OS),unix) + CC = gcc -Wall -O + O = .o + LIB = + EXE = + RM = rm -f + endif + + ifeq ($(OS),os2) + CC = gcc -Wall -Zomf -Zmtd -O + O = .obj + LIB = cweave.def + EXE = .exe + RM = del + endif + + ifeq ($(OS),dos) + CC = gcc -Wall -O + O = .o + LIB = + EXE = .exe + RM = del + endif + + ifeq ($(OS),bound) + CC = gcc -Wall -O + O = .o + LIB = + EXE = .exe + RM = del + endif + + + OBJ = common$O cweave$O + + + %$O: %.c + $(CC) $(CFLAGS) -c -o $@ $< + + %.c: %.w %.ch + $(CTANGLE) $^ $@ + %.c: %.w + $(CTANGLE) $< + + + all: + $(MAKE) cweb CFLAGS=-s + + debug: + $(MAKE) cweb CFLAGS=-g + + # remove the unnecessary files. + clean: + -$(RM) *$O + + + cweb: cweave$(EXE) + + cweave$(EXE): $(OBJ) + ifeq ($(OS),dos) + $(CC) $(CFLAGS) -o $(basename $@) $^ + strip $(basename $@) + coff2exe $(basename $@) + -$(RM) $(basename $@) + else + $(CC) $(CFLAGS) -o $@ $^ $(LIB) + endif + + cweave.w: common.h + cweave.ch: prod-alt.w + +else # ifdef OS + + all debug clean: default + +endif + +# end of Makefile diff --git a/web/c_cpp/c2cweb/cweb/common.c b/web/c_cpp/c2cweb/cweb/common.c new file mode 100644 index 0000000000..963a20d9c1 --- /dev/null +++ b/web/c_cpp/c2cweb/cweb/common.c @@ -0,0 +1,1280 @@ +/*1:*/ +#line 57 "common.w" + +/*5:*/ +#line 101 "common.w" + +#include <ctype.h> + +/*:5*//*8:*/ +#line 163 "common.w" + +#include <stdio.h> + +/*:8*//*22:*/ +#line 468 "common.w" + +#include <stdlib.h> + +/*:22*/ +#line 58 "common.w" + +#define ctangle 0 +#define cweave 1 \ + +#define and_and 04 +#define lt_lt 020 +#define gt_gt 021 +#define plus_plus 013 +#define minus_minus 01 +#define minus_gt 031 +#define not_eq 032 +#define lt_eq 034 +#define gt_eq 035 +#define eq_eq 036 +#define or_or 037 +#define dot_dot_dot 016 +#define colon_colon 06 +#define period_ast 026 +#define minus_gt_ast 027 \ + +#define buf_size 400 +#define long_buf_size 500 +#define xisspace(c) (isspace(c) &&((unsigned char) c<0200) ) +#define xisupper(c) (isupper(c) &&((unsigned char) c<0200) ) \ + +#define max_include_depth 10 \ + +#define max_file_name_length 60 +#define cur_file file[include_depth] +#define cur_file_name file_name[include_depth] +#define cur_line line[include_depth] +#define web_file file[0] +#define web_file_name file_name[0] \ + +#define lines_dont_match (change_limit-change_buffer!=limit-buffer|| \ +strncmp(buffer,change_buffer,limit-buffer) ) \ + +#define if_section_start_make_pending(b) {*limit= '!'; \ +for(loc= buffer;xisspace(*loc) ;loc++) ; \ +*limit= ' '; \ +if(*loc=='@'&&(xisspace(*(loc+1) ) ||*(loc+1) =='*') ) change_pending= b; \ +} \ + +#define max_sections 2000 \ + \ + +#define too_long() {include_depth--; \ +err_print("! Include file name too long") ;goto restart;} \ + +#define max_bytes 225000 \ + +#define max_names 10000 \ + \ + +#define length(c) (c+1) ->byte_start-(c) ->byte_start +#define print_id(c) term_write((c) ->byte_start,length((c) ) ) \ + +#define hash_size 353 \ + +#define llink link +#define rlink dummy.Rlink +#define root name_dir->rlink \ + \ + +#define first_chunk(p) ((p) ->byte_start+2) +#define prefix_length(p) (int) ((unsigned char) *((p) ->byte_start) *256+ \ +(unsigned char) *((p) ->byte_start+1) ) +#define set_prefix_length(p,m) (*((p) ->byte_start) = (m) /256, \ +*((p) ->byte_start+1) = (m) %256) \ + +#define less 0 +#define equal 1 +#define greater 2 +#define prefix 3 +#define extension 4 \ + +#define bad_extension 5 \ + +#define spotless 0 +#define harmless_message 1 +#define error_message 2 +#define fatal_message 3 +#define mark_harmless {if(history==spotless) history= harmless_message;} +#define mark_error history= error_message \ + +#define confusion(s) fatal("! This can't happen: ",s) \ + \ + +#define show_banner flags['b'] +#define show_progress flags['p'] +#define show_stats flags['s'] +#define show_happiness flags['h'] \ + +#define update_terminal fflush(stdout) \ + +#define new_line putchar('\n') +#define putxchar putchar +#define term_write(a,b) fflush(stdout) ,fwrite(a,sizeof(char) ,b,stdout) +#define C_printf(c,a) fprintf(C_file,c,a) +#define C_putc(c) putc(c,C_file) \ + + +#line 59 "common.w" + +/*2:*/ +#line 72 "common.w" + +typedef short boolean; +boolean program; + +/*:2*//*7:*/ +#line 157 "common.w" + +char buffer[long_buf_size]; +char*buffer_end= buffer+buf_size-2; +char*limit= buffer; +char*loc= buffer; + +/*:7*//*10:*/ +#line 212 "common.w" + +int include_depth; +FILE*file[max_include_depth]; +FILE*change_file; +char file_name[max_include_depth][max_file_name_length]; + +char change_file_name[max_file_name_length]; +char alt_web_file_name[max_file_name_length]; +int line[max_include_depth]; +int change_line; +int change_depth; +boolean input_has_ended; +boolean changing; +boolean web_file_open= 0; + +/*:10*//*20:*/ +#line 416 "common.w" + +typedef unsigned short sixteen_bits; +sixteen_bits section_count; +boolean changed_section[max_sections]; +boolean change_pending; + +boolean print_where= 0; + +/*:20*//*27:*/ +#line 586 "common.w" + +typedef struct name_info{ +char*byte_start; +/*31:*/ +#line 623 "common.w" + +struct name_info*link; + +/*:31*//*40:*/ +#line 722 "common.w" + +union{ +struct name_info*Rlink; + +#line 31 "common.ch" +struct{ +char Ilk; +boolean Func_flag;}dummy1; +}dummy; +#line 728 "common.w" + +/*:40*//*55:*/ +#line 1054 "common.w" + +char*equiv_or_xref; + +/*:55*/ +#line 589 "common.w" + +}name_info; +typedef name_info*name_pointer; +char byte_mem[max_bytes]; +char*byte_mem_end= byte_mem+max_bytes-1; +name_info name_dir[max_names]; +name_pointer name_dir_end= name_dir+max_names-1; + +/*:27*//*29:*/ +#line 609 "common.w" + +name_pointer name_ptr; +char*byte_ptr; + +/*:29*//*32:*/ +#line 636 "common.w" + +typedef name_pointer*hash_pointer; +name_pointer hash[hash_size]; +hash_pointer hash_end= hash+hash_size-1; +hash_pointer h; + +/*:32*//*56:*/ +#line 1074 "common.w" + +int history= spotless; + +/*:56*//*67:*/ +#line 1212 "common.w" + +int argc; +char**argv; +char C_file_name[max_file_name_length]; +char tex_file_name[max_file_name_length]; +char idx_file_name[max_file_name_length]; +char scn_file_name[max_file_name_length]; +boolean flags[128]; + +/*:67*//*77:*/ +#line 1366 "common.w" + +FILE*C_file; +FILE*tex_file; +FILE*idx_file; +FILE*scn_file; +FILE*active_file; + +/*:77*/ +#line 60 "common.w" + +/*3:*/ +#line 82 "common.w" +int phase; + +/*:3*//*11:*/ +#line 238 "common.w" + +char change_buffer[buf_size]; +char*change_limit; + +/*:11*/ +#line 61 "common.w" + +/*33:*/ +#line 642 "common.w" + +extern int names_match(); + +/*:33*//*38:*/ +#line 695 "common.w" + +void init_p(); + +/*:38*//*46:*/ +#line 844 "common.w" + +extern void init_node(); + +/*:46*//*53:*/ +#line 1009 "common.w" + +int section_name_cmp(); + +/*:53*//*57:*/ +#line 1084 "common.w" + +void err_print(); + +/*:57*//*60:*/ +#line 1132 "common.w" + +int wrap_up(); +extern void print_stats(); + +/*:60*//*63:*/ +#line 1165 "common.w" + +void fatal(),overflow(); + +/*:63*//*69:*/ +#line 1243 "common.w" + +void scan_args(); + +/*:69*//*81:*/ +#line 1407 "common.w" + +extern int strlen(); +extern int strcmp(); +extern char*strcpy(); +extern int strncmp(); +extern char*strncpy(); + +/*:81*/ +#line 62 "common.w" + + +/*:1*//*4:*/ +#line 88 "common.w" + +void +common_init() +{ +/*30:*/ +#line 613 "common.w" + +name_dir->byte_start= byte_ptr= byte_mem; +name_ptr= name_dir+1; +name_ptr->byte_start= byte_mem; + +/*:30*//*34:*/ +#line 647 "common.w" + +for(h= hash;h<=hash_end;*h++= NULL); + +/*:34*//*41:*/ +#line 729 "common.w" + +root= NULL; + +/*:41*/ +#line 92 "common.w" +; +/*68:*/ +#line 1225 "common.w" + +show_banner= show_happiness= show_progress= 1; + +/*:68*/ +#line 93 "common.w" +; +/*78:*/ +#line 1373 "common.w" + +scan_args(); +if(program==ctangle){ +if((C_file= fopen(C_file_name,"w"))==NULL) +fatal("! Cannot open output file ",C_file_name); + +} +else{ +if((tex_file= fopen(tex_file_name,"w"))==NULL) +fatal("! Cannot open output file ",tex_file_name); +} + +/*:78*/ +#line 94 "common.w" +; +} + +/*:4*//*9:*/ +#line 170 "common.w" + +int input_ln(fp) +FILE*fp; +{ +register int c= EOF; +register char*k; +if(feof(fp))return(0); +limit= k= buffer; +while(k<=buffer_end&&(c= getc(fp))!=EOF&&c!='\n') +if((*(k++)= c)!=' ')limit= k; +if(k>buffer_end) +if((c= getc(fp))!=EOF&&c!='\n'){ +ungetc(c,fp);loc= buffer;err_print("! Input line too long"); + +} +if(c==EOF&&limit==buffer)return(0); + +return(1); +} + +/*:9*//*12:*/ +#line 249 "common.w" + +void +prime_the_change_buffer() +{ +change_limit= change_buffer; +/*13:*/ +#line 263 "common.w" + +while(1){ +change_line++; +if(!input_ln(change_file))return; +if(limit<buffer+2)continue; +if(buffer[0]!='@')continue; +if(xisupper(buffer[1]))buffer[1]= tolower(buffer[1]); +if(buffer[1]=='x')break; +if(buffer[1]=='y'||buffer[1]=='z'||buffer[1]=='i'){ +loc= buffer+2; +err_print("! Missing @x in change file"); + +} +} + +/*:13*/ +#line 254 "common.w" +; +/*14:*/ +#line 280 "common.w" + +do{ +change_line++; +if(!input_ln(change_file)){ +err_print("! Change file ended after @x"); + +return; +} +}while(limit==buffer); + +/*:14*/ +#line 255 "common.w" +; +/*15:*/ +#line 290 "common.w" + +{ +change_limit= change_buffer-buffer+limit; +strncpy(change_buffer,buffer,limit-buffer+1); +} + +/*:15*/ +#line 256 "common.w" +; +} + +/*:12*//*16:*/ +#line 318 "common.w" + +void +check_change() +{ +int n= 0; +if(lines_dont_match)return; +change_pending= 0; +if(!changed_section[section_count]){ +if_section_start_make_pending(1); +if(!change_pending)changed_section[section_count]= 1; +} +while(1){ +changing= 1;print_where= 1;change_line++; +if(!input_ln(change_file)){ +err_print("! Change file ended before @y"); + +change_limit= change_buffer;changing= 0; +return; +} +if(limit>buffer+1&&buffer[0]=='@'){ +if(xisupper(buffer[1]))buffer[1]= tolower(buffer[1]); +/*17:*/ +#line 356 "common.w" + +if(buffer[1]=='x'||buffer[1]=='z'){ +loc= buffer+2;err_print("! Where is the matching @y?"); + +} +else if(buffer[1]=='y'){ +if(n>0){ +loc= buffer+2; +printf("\n! Hmm... %d ",n); +err_print("of the preceding lines failed to match"); + +} +change_depth= include_depth; +return; +} + +/*:17*/ +#line 340 "common.w" +; +} +/*15:*/ +#line 290 "common.w" + +{ +change_limit= change_buffer-buffer+limit; +strncpy(change_buffer,buffer,limit-buffer+1); +} + +/*:15*/ +#line 342 "common.w" +; +changing= 0;cur_line++; +while(!input_ln(cur_file)){ +if(include_depth==0){ +err_print("! CWEB file ended during a change"); + +input_has_ended= 1;return; +} +include_depth--;cur_line++; +} +if(lines_dont_match)n++; +} +} + +/*:16*//*18:*/ +#line 376 "common.w" + +void +reset_input() +{ +limit= buffer;loc= buffer+1;buffer[0]= ' '; +/*19:*/ +#line 391 "common.w" + +if((web_file= fopen(web_file_name,"r"))==NULL){ +strcpy(web_file_name,alt_web_file_name); +if((web_file= fopen(web_file_name,"r"))==NULL) +fatal("! Cannot open input file ",web_file_name); +} + + +web_file_open= 1; +if((change_file= fopen(change_file_name,"r"))==NULL) +fatal("! Cannot open change file ",change_file_name); + +/*:19*/ +#line 381 "common.w" +; +include_depth= 0;cur_line= 0;change_line= 0; +change_depth= include_depth; +changing= 1;prime_the_change_buffer();changing= !changing; +limit= buffer;loc= buffer+1;buffer[0]= ' ';input_has_ended= 0; +} + +/*:18*//*21:*/ +#line 424 "common.w" + +int get_line() +{ +restart: +if(changing&&include_depth==change_depth) +/*25:*/ +#line 529 "common.w" +{ +change_line++; +if(!input_ln(change_file)){ +err_print("! Change file ended without @z"); + +buffer[0]= '@';buffer[1]= 'z';limit= buffer+2; +} +if(limit>buffer){ +if(change_pending){ +if_section_start_make_pending(0); +if(change_pending){ +changed_section[section_count]= 1;change_pending= 0; +} +} +*limit= ' '; +if(buffer[0]=='@'){ +if(xisupper(buffer[1]))buffer[1]= tolower(buffer[1]); +if(buffer[1]=='x'||buffer[1]=='y'){ +loc= buffer+2; +err_print("! Where is the matching @z?"); + +} +else if(buffer[1]=='z'){ +prime_the_change_buffer();changing= !changing;print_where= 1; +} +} +} +} + +/*:25*/ +#line 429 "common.w" +; +if(!changing||include_depth>change_depth){ +/*24:*/ +#line 512 "common.w" +{ +cur_line++; +while(!input_ln(cur_file)){ +print_where= 1; +if(include_depth==0){input_has_ended= 1;break;} +else{ +fclose(cur_file);include_depth--; +if(changing&&include_depth==change_depth)break; +cur_line++; +} +} +if(!changing&&!input_has_ended) +if(limit-buffer==change_limit-change_buffer) +if(buffer[0]==change_buffer[0]) +if(change_limit>change_buffer)check_change(); +} + +/*:24*/ +#line 431 "common.w" +; +if(changing&&include_depth==change_depth)goto restart; +} +loc= buffer;*limit= ' '; +if(*buffer=='@'&&(*(buffer+1)=='i'||*(buffer+1)=='I')){ +loc= buffer+2; +while(loc<=limit&&(*loc==' '||*loc=='\t'||*loc=='"'))loc++; +if(loc>=limit){ +err_print("! Include file name not given"); + +goto restart; +} +if(include_depth>=max_include_depth-1){ +err_print("! Too many nested includes"); + +goto restart; +} +include_depth++; +/*23:*/ +#line 471 "common.w" +{ +char temp_file_name[max_file_name_length]; +char*cur_file_name_end= cur_file_name+max_file_name_length-1; +char*k= cur_file_name,*kk; +int l; + +while(*loc!=' '&&*loc!='\t'&&*loc!='"'&&k<=cur_file_name_end)*k++= *loc++; +if(k>cur_file_name_end)too_long(); + +*k= '\0'; +if((cur_file= fopen(cur_file_name,"r"))!=NULL){ +cur_line= 0;print_where= 1; +goto restart; +} +kk= getenv("CWEBINPUTS"); +if(kk!=NULL){ +if((l= strlen(kk))>max_file_name_length-2)too_long(); +strcpy(temp_file_name,kk); +} +else{ +#ifdef CWEBINPUTS +if((l= strlen(CWEBINPUTS))>max_file_name_length-2)too_long(); +strcpy(temp_file_name,CWEBINPUTS); +#else +l= 0; +#endif +} +if(l>0){ +if(k+l+2>=cur_file_name_end)too_long(); + +for(;k>=cur_file_name;k--)*(k+l+1)= *k; +strcpy(cur_file_name,temp_file_name); +cur_file_name[l]= '/'; +if((cur_file= fopen(cur_file_name,"r"))!=NULL){ +cur_line= 0;print_where= 1; +goto restart; +} +} +include_depth--;err_print("! Cannot open include file");goto restart; +} + +/*:23*/ +#line 449 "common.w" +; +} +return(!input_has_ended); +} + +/*:21*//*26:*/ +#line 561 "common.w" + +void +check_complete(){ +if(change_limit!=change_buffer){ +strncpy(buffer,change_buffer,change_limit-change_buffer+1); +limit= buffer+(int)(change_limit-change_buffer); +changing= 1;change_depth= include_depth;loc= buffer; +err_print("! Change file entry did not match"); + +} +} + +/*:26*//*35:*/ +#line 652 "common.w" + +name_pointer +id_lookup(first,last,t) +char*first; +char*last; +char t; +{ +char*i= first; +int h; +int l; +name_pointer p; +if(last==NULL)for(last= first;*last!='\0';last++); +l= last-first; +/*36:*/ +#line 675 "common.w" + +h= (unsigned char)*i; +while(++i<last)h= (h+h+(int)((unsigned char)*i))%hash_size; + + +/*:36*/ +#line 665 "common.w" +; +/*37:*/ +#line 683 "common.w" + +p= hash[h]; +while(p&&!names_match(p,first,l,t))p= p->link; +if(p==NULL){ +p= name_ptr; +p->link= hash[h];hash[h]= p; +} + +/*:37*/ +#line 666 "common.w" +; +if(p==name_ptr)/*39:*/ +#line 698 "common.w" +{ +if(byte_ptr+l>byte_mem_end)overflow("byte memory"); +if(name_ptr>=name_dir_end)overflow("name"); +strncpy(byte_ptr,first,l); +(++name_ptr)->byte_start= byte_ptr+= l; +if(program==cweave)init_p(p,t); +} + +/*:39*/ +#line 667 "common.w" +; +return(p); +} + +/*:35*//*42:*/ +#line 756 "common.w" + +void +print_section_name(p) +name_pointer p; +{ +char*ss,*s= first_chunk(p); +name_pointer q= p+1; +while(p!=name_dir){ +ss= (p+1)->byte_start-1; +if(*ss==' '&&ss>=s){ +term_write(s,ss-s);p= q->link;q= p; +}else{ +term_write(s,ss+1-s);p= name_dir;q= NULL; +} +s= p->byte_start; +} +if(q)term_write("...",3); +} + +/*:42*//*43:*/ +#line 775 "common.w" + +void +sprint_section_name(dest,p) +char*dest; +name_pointer p; +{ +char*ss,*s= first_chunk(p); +name_pointer q= p+1; +while(p!=name_dir){ +ss= (p+1)->byte_start-1; +if(*ss==' '&&ss>=s){ +p= q->link;q= p; +}else{ +ss++;p= name_dir; +} +strncpy(dest,s,ss-s),dest+= ss-s; +s= p->byte_start; +} +*dest= '\0'; +} + +/*:43*//*44:*/ +#line 796 "common.w" + +void +print_prefix_name(p) +name_pointer p; +{ +char*s= first_chunk(p); +int l= prefix_length(p); +term_write(s,l); +if(s+l<(p+1)->byte_start)term_write("...",3); +} + +/*:44*//*45:*/ +#line 817 "common.w" + +int web_strcmp(j,j_len,k,k_len) +char*j,*k; +int j_len,k_len; +{ +char*j1= j+j_len,*k1= k+k_len; +while(k<k1&&j<j1&&*j==*k)k++,j++; +if(k==k1)if(j==j1)return equal; +else return extension; +else if(j==j1)return prefix; +else if(*j<*k)return less; +else return greater; +} + +/*:45*//*47:*/ +#line 847 "common.w" + +name_pointer +add_section_name(par,c,first,last,ispref) +name_pointer par; +int c; +char*first; +char*last; +int ispref; +{ +name_pointer p= name_ptr; +char*s= first_chunk(p); +int name_len= last-first+ispref; +if(s+name_len>byte_mem_end)overflow("byte memory"); +if(name_ptr+1>=name_dir_end)overflow("name"); +(++name_ptr)->byte_start= byte_ptr= s+name_len; +if(ispref){ +*(byte_ptr-1)= ' '; +name_len--; +name_ptr->link= name_dir; +(++name_ptr)->byte_start= byte_ptr; +} +set_prefix_length(p,name_len); +strncpy(s,first,name_len); +p->llink= NULL; +p->rlink= NULL; +init_node(p); +return par==NULL?(root= p):c==less?(par->llink= p):(par->rlink= p); +} + +/*:47*//*48:*/ +#line 876 "common.w" + +void +extend_section_name(p,first,last,ispref) +name_pointer p; +char*first; +char*last; +int ispref; +{ +char*s; +name_pointer q= p+1; +int name_len= last-first+ispref; +if(name_ptr>=name_dir_end)overflow("name"); +while(q->link!=name_dir)q= q->link; +q->link= name_ptr; +s= name_ptr->byte_start; +name_ptr->link= name_dir; +if(s+name_len>byte_mem_end)overflow("byte memory"); +(++name_ptr)->byte_start= byte_ptr= s+name_len; +strncpy(s,first,name_len); +if(ispref)*(byte_ptr-1)= ' '; +} + +/*:48*//*49:*/ +#line 904 "common.w" + +name_pointer +section_lookup(first,last,ispref) +char*first,*last; +int ispref; +{ +int c= 0; +name_pointer p= root; +name_pointer q= NULL; +name_pointer r= NULL; +name_pointer par= NULL; + +int name_len= last-first+1; +/*50:*/ +#line 928 "common.w" + +while(p){ +c= web_strcmp(first,name_len,first_chunk(p),prefix_length(p)); +if(c==less||c==greater){ +if(r==NULL) +par= p; +p= (c==less?p->llink:p->rlink); +}else{ +if(r!=NULL){ +printf("\n! Ambiguous prefix: matches <"); + +print_prefix_name(p); +printf(">\n and <"); +print_prefix_name(r); +err_print(">"); +return name_dir; +} +r= p; +p= p->llink; +q= r->rlink; +} +if(p==NULL) +p= q,q= NULL; +} + +/*:50*/ +#line 918 "common.w" +; +/*51:*/ +#line 953 "common.w" + +if(r==NULL) +return add_section_name(par,c,first,last+1,ispref); + +/*:51*/ +#line 919 "common.w" +; +/*52:*/ +#line 961 "common.w" + +switch(section_name_cmp(&first,name_len,r)){ + +case prefix: +if(!ispref){ +printf("\n! New name is a prefix of <"); + +print_section_name(r); +err_print(">"); +} +else if(name_len<prefix_length(r))set_prefix_length(r,name_len); + +case equal:return r; +case extension:if(!ispref||first<=last) +extend_section_name(r,first,last+1,ispref); +return r; +case bad_extension: +printf("\n! New name extends <"); + +print_section_name(r); +err_print(">"); +return r; +default: +printf("\n! Section name incompatible with <"); + +print_prefix_name(r); +printf(">,\n which abbreviates <"); +print_section_name(r); +err_print(">"); +return r; +} + +/*:52*/ +#line 920 "common.w" +; +} + +/*:49*//*54:*/ +#line 1012 "common.w" + +int section_name_cmp(pfirst,len,r) +char**pfirst; +int len; +name_pointer r; +{ +char*first= *pfirst; +name_pointer q= r+1; +char*ss,*s= first_chunk(r); +int c; +int ispref; +while(1){ +ss= (r+1)->byte_start-1; +if(*ss==' '&&ss>=r->byte_start)ispref= 1,q= q->link; +else ispref= 0,ss++,q= name_dir; +switch(c= web_strcmp(first,len,s,ss-s)){ +case equal:if(q==name_dir) +if(ispref){ +*pfirst= first+(ss-s); +return extension; +}else return equal; +else return(q->byte_start==(q+1)->byte_start)?equal:prefix; +case extension: +if(!ispref)return bad_extension; +first+= ss-s; +if(q!=name_dir){len-= ss-s;s= q->byte_start;r= q;continue;} +*pfirst= first;return extension; +default:return c; +} +} +} + +/*:54*//*58:*/ +#line 1087 "common.w" + +void +err_print(s) +char*s; +{ +char*k,*l; +printf(*s=='!'?"\n%s":"%s",s); +if(web_file_open)/*59:*/ +#line 1107 "common.w" + +{if(changing&&include_depth==change_depth) +printf(". (l. %d of change file)\n",change_line); +else if(include_depth==0)printf(". (l. %d)\n",cur_line); +else printf(". (l. %d of include file %s)\n",cur_line,cur_file_name); +l= (loc>=limit?limit:loc); +if(l>buffer){ +for(k= buffer;k<l;k++) +if(*k=='\t')putchar(' '); +else putchar(*k); +putchar('\n'); +for(k= buffer;k<l;k++)putchar(' '); +} +for(k= l;k<limit;k++)putchar(*k); +if(*limit=='|')putchar('|'); +putchar(' '); +} + +/*:59*/ +#line 1094 "common.w" +; +update_terminal;mark_error; +} + +/*:58*//*61:*/ +#line 1142 "common.w" + +int wrap_up(){ +putchar('\n'); +if(show_stats) +print_stats(); +/*62:*/ +#line 1152 "common.w" + +switch(history){ +case spotless:if(show_happiness)printf("(No errors were found.)\n");break; +case harmless_message: +printf("(Did you see the warning message above?)\n");break; +case error_message: +printf("(Pardon me, but I think I spotted something wrong.)\n");break; +case fatal_message:printf("(That was a fatal error, my friend.)\n"); +} + +/*:62*/ +#line 1147 "common.w" +; +if(history>harmless_message)return(1); +else return(0); +} + +/*:61*//*64:*/ +#line 1171 "common.w" +void +fatal(s,t) +char*s,*t; +{ +if(*s)printf(s); +err_print(t); +history= fatal_message;exit(wrap_up()); +} + +/*:64*//*65:*/ +#line 1182 "common.w" +void +overflow(t) +char*t; +{ +printf("\n! Sorry, %s capacity exceeded",t);fatal("",""); +} + + +/*:65*//*70:*/ +#line 1246 "common.w" + +void +scan_args() +{ +char*dot_pos; +char*name_pos; +register char*s; +boolean found_web= 0,found_change= 0,found_out= 0; + +boolean flag_change; + +while(--argc>0){ +if((**(++argv)=='-'||**argv=='+')&&*(*argv+1))/*74:*/ +#line 1340 "common.w" + +{ +if(**argv=='-')flag_change= 0; +else flag_change= 1; +for(dot_pos= *argv+1;*dot_pos>'\0';dot_pos++) +flags[*dot_pos]= flag_change; +} + +/*:74*/ +#line 1258 "common.w" + +else{ +s= name_pos= *argv;dot_pos= NULL; +while(*s){ +if(*s=='.')dot_pos= s++; +#line 47 "common.ch" +#ifdef __EMX__ +else if(*s==':'||*s=='\\'||*s=='/') +dot_pos= NULL,name_pos= ++s; +#else +else if(*s=='/')dot_pos= NULL,name_pos= ++s; +#endif +#line 1264 "common.w" + else s++; +} +if(!found_web)/*71:*/ +#line 1284 "common.w" + +{ +if(s-*argv>max_file_name_length-5) +/*76:*/ +#line 1360 "common.w" +fatal("! Filename too long\n",*argv); + + +/*:76*/ +#line 1287 "common.w" +; +if(dot_pos==NULL) +sprintf(web_file_name,"%s.w",*argv); +else{ +strcpy(web_file_name,*argv); +*dot_pos= 0; +} +sprintf(alt_web_file_name,"%s.web",*argv); +sprintf(tex_file_name,"%s.tex",name_pos); +sprintf(idx_file_name,"%s.idx",name_pos); +sprintf(scn_file_name,"%s.scn",name_pos); +sprintf(C_file_name,"%s.c",name_pos); +found_web= 1; +} + +/*:71*/ +#line 1267 "common.w" + +else if(!found_change)/*72:*/ +#line 1302 "common.w" + +{ +if(strcmp(*argv,"-")==0)found_change= -1; +else{ +if(s-*argv>max_file_name_length-4) +/*76:*/ +#line 1360 "common.w" +fatal("! Filename too long\n",*argv); + + +/*:76*/ +#line 1307 "common.w" +; +if(dot_pos==NULL) +sprintf(change_file_name,"%s.ch",*argv); +else strcpy(change_file_name,*argv); +found_change= 1; +} +} + +/*:72*/ +#line 1268 "common.w" + +else if(!found_out)/*73:*/ +#line 1315 "common.w" + +{ +if(s-*argv>max_file_name_length-5) +/*76:*/ +#line 1360 "common.w" +fatal("! Filename too long\n",*argv); + + +/*:76*/ +#line 1318 "common.w" +; +if(dot_pos==NULL){ +sprintf(tex_file_name,"%s.tex",*argv); +sprintf(idx_file_name,"%s.idx",*argv); +sprintf(scn_file_name,"%s.scn",*argv); +sprintf(C_file_name,"%s.c",*argv); +}else{ +strcpy(tex_file_name,*argv); +if(flags['x']){ +if(program==cweave&&strcmp(*argv+strlen(*argv)-4,".tex")!=0) +fatal("! Output file name should end with .tex\n",*argv); + +strcpy(idx_file_name,*argv); +strcpy(idx_file_name+strlen(*argv)-4,".idx"); +strcpy(scn_file_name,*argv); +strcpy(scn_file_name+strlen(*argv)-4,".scn"); +} +strcpy(C_file_name,*argv); +} +found_out= 1; +} + +/*:73*/ +#line 1269 "common.w" + +else/*75:*/ +#line 1348 "common.w" + +{ +if(program==ctangle) +fatal( +"! Usage: ctangle [options] webfile[.w] [{changefile[.ch]|-} [outfile[.c]]]\n" +,""); + +else fatal( +#line 69 "common.ch" +"! Usage: cweave [options] webfile[.w] [{changefile[.ch]|-} [outfile[.tex]]]\n" +"\n" +" possible options: (default values shown)\n" +"\n" +" -a alternative output format\n" +" +b show banner\n" +" +f force line breaks after C statements\n" +" +h print happy message if successful\n" +" -i append () after functions in index\n" +" +p give progress reports\n" +" -s show memory statistics\n" +" +x include indices and table of contents\n",""); +#line 1358 "common.w" +} + +/*:75*/ +#line 1270 "common.w" +; +} +} +if(!found_web)/*75:*/ +#line 1348 "common.w" + +{ +if(program==ctangle) +fatal( +"! Usage: ctangle [options] webfile[.w] [{changefile[.ch]|-} [outfile[.c]]]\n" +,""); + +else fatal( +#line 69 "common.ch" +"! Usage: cweave [options] webfile[.w] [{changefile[.ch]|-} [outfile[.tex]]]\n" +"\n" +" possible options: (default values shown)\n" +"\n" +" -a alternative output format\n" +" +b show banner\n" +" +f force line breaks after C statements\n" +" +h print happy message if successful\n" +" -i append () after functions in index\n" +" +p give progress reports\n" +" -s show memory statistics\n" +" +x include indices and table of contents\n",""); +#line 1358 "common.w" +} + +/*:75*/ +#line 1273 "common.w" +; +#line 58 "common.ch" +#ifdef __EMX__ +if(found_change<=0)strcpy(change_file_name,"NUL"); +#else +if(found_change<=0)strcpy(change_file_name,"/dev/null"); +#endif +#line 1275 "common.w" +} + +/*:70*/ diff --git a/web/c_cpp/c2cweb/cweb/common.ch b/web/c_cpp/c2cweb/cweb/common.ch new file mode 100644 index 0000000000..f591d774bf --- /dev/null +++ b/web/c_cpp/c2cweb/cweb/common.ch @@ -0,0 +1,83 @@ +This file is part of the c2cweb package Version 1.5 + +This is the change file for CWEB's COMMON (Ver. 3.2 July 1994) + +modified by Werner Lemberg (a7971428@@unet.univie.ac.at) 10-Nov-1996 + +=============================================================================== + +@x section 7 +@d buf_size 100 /* for \.{CWEAVE} and \.{CTANGLE} */ +@y +@d buf_size 400 /* for \.{CWEAVE} and \.{CTANGLE} */ +@z + +@x section 27 +@d max_bytes 90000 /* the number of bytes in identifiers, + index entries, and section names; must be less than $2^{24}$ */ +@d max_names 4000 /* number of identifiers, strings, section names; + must be less than 10240 */ +@y +@d max_bytes 225000 /* the number of bytes in identifiers, + index entries, and section names; must be less than $2^{24}$ */ +@d max_names 10000 /* number of identifiers, strings, section names; + must be less than 10240 */ +@z + +@x section 40 + char Ilk; /* used by identifiers in \.{CWEAVE} only */ +} dummy; +@y + struct { + char Ilk; /* used by identifiers in \.{CWEAVE} only */ + boolean Func_flag; } dummy1; +} dummy; +@z + +@x section 69 +An omitted change file argument means that |"/dev/null"| should be used, +@y +An omitted change file argument means that |"/dev/null"| (or |"NUL"| under DOS +and OS/2) should be used, +@z + +@x section 70 + else if (*s=='/') dot_pos=NULL,name_pos=++s; +@y +#ifdef __EMX__ + else if (*s == ':' || *s == '\\' || *s == '/') + dot_pos=NULL,name_pos=++s; +#else + else if (*s=='/') dot_pos=NULL,name_pos=++s; +#endif +@z + +@x section 70 + if (found_change<=0) strcpy(change_file_name,"/dev/null"); +@y +#ifdef __EMX__ + if (found_change<=0) strcpy(change_file_name,"NUL"); +#else + if (found_change<=0) strcpy(change_file_name,"/dev/null"); +#endif +@z + +@x section 75 +"! Usage: cweave [options] webfile[.w] [{changefile[.ch]|-} [outfile[.tex]]]\n" + ,""); +@y +"! Usage: cweave [options] webfile[.w] [{changefile[.ch]|-} [outfile[.tex]]]\n" +"\n" +" possible options: (default values shown)\n" +"\n" +" -a alternative output format\n" +" +b show banner\n" +" +f force line breaks after C statements\n" +" +h print happy message if successful\n" +" -i append () after functions in index\n" +" +p give progress reports\n" +" -s show memory statistics\n" +" +x include indices and table of contents\n", ""); +@z + +--- end of common.ch --- diff --git a/web/c_cpp/c2cweb/cweb/common.h b/web/c_cpp/c2cweb/cweb/common.h new file mode 100644 index 0000000000..200ec27358 --- /dev/null +++ b/web/c_cpp/c2cweb/cweb/common.h @@ -0,0 +1,201 @@ +% 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.0 --- June 1993 + +% Copyright (C) 1987,1990,1993 Silvio Levy and Donald E. Knuth + +% this file is part of the c2cweb package Version 1.5 +% modified by Werner Lemberg (a7971428@@unet.univie.ac.at) 10-Nov-1996 + +% 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 distributed under the terms of a +% permission notice identical to this one. + +% Please send comments, suggestions, etc. to levy@@geom.umn.edu. + +% The next few sections contain stuff from the file |"common.w"| that has +% to be included in both |"ctangle.w"| and |"cweave.w"|. It appears in this +% file |"common.h"|, which needs to be updated when |"common.w"| changes. + +First comes general stuff: + +@d ctangle 0 +@d cweave 1 + +@<Common code for \.{CWEAVE} and \.{CTANGLE}@>= +typedef short boolean; +typedef char unsigned eight_bits; +extern boolean program; /* \.{CWEAVE} or \.{CTANGLE}? */ +extern int phase; /* which phase are we in? */ + +@ @<Include files@>= +#include <stdio.h> + +@ Code related to the character set: +@^ASCII code dependencies@> + +@d and_and 04 /* `\.{\&\&}'\,; corresponds to MIT's {\tentex\char'4} */ +@d lt_lt 020 /* `\.{<<}'\,; corresponds to MIT's {\tentex\char'20} */ +@d gt_gt 021 /* `\.{>>}'\,; corresponds to MIT's {\tentex\char'21} */ +@d plus_plus 013 /* `\.{++}'\,; corresponds to MIT's {\tentex\char'13} */ +@d minus_minus 01 /* `\.{--}'\,; corresponds to MIT's {\tentex\char'1} */ +@d minus_gt 031 /* `\.{->}'\,; corresponds to MIT's {\tentex\char'31} */ +@d not_eq 032 /* `\.{!=}'\,; corresponds to MIT's {\tentex\char'32} */ +@d lt_eq 034 /* `\.{<=}'\,; corresponds to MIT's {\tentex\char'34} */ +@d gt_eq 035 /* `\.{>=}'\,; corresponds to MIT's {\tentex\char'35} */ +@d eq_eq 036 /* `\.{==}'\,; corresponds to MIT's {\tentex\char'36} */ +@d or_or 037 /* `\.{\v\v}'\,; corresponds to MIT's {\tentex\char'37} */ +@d dot_dot_dot 016 /* `\.{...}'\,; corresponds to MIT's {\tentex\char'16} */ +@d colon_colon 06 /* `\.{::}'\,; corresponds to MIT's {\tentex\char'6} */ +@d period_ast 026 /* `\.{.*}'\,; corresponds to MIT's {\tentex\char'26} */ +@d minus_gt_ast 027 /* `\.{->*}'\,; corresponds to MIT's {\tentex\char'27} */ + +@<Common code...@>= +char section_text[longest_name+1]; /* name being sought for */ +char *section_text_end = section_text+longest_name; /* end of |section_text| */ +char *id_first; /* where the current identifier begins in the buffer */ +char *id_loc; /* just after the current identifier in the buffer */ + +@ Code related to input routines: + +@d xisalpha(c) (isalpha(c)&&((eight_bits)c<0200)) +@d xisdigit(c) (isdigit(c)&&((eight_bits)c<0200)) +@d xisspace(c) (isspace(c)&&((eight_bits)c<0200)) +@d xislower(c) (islower(c)&&((eight_bits)c<0200)) +@d xisupper(c) (isupper(c)&&((eight_bits)c<0200)) +@d xisxdigit(c) (isxdigit(c)&&((eight_bits)c<0200)) + +@<Common code...@>= +extern char buffer[]; /* where each line of input goes */ +extern char *buffer_end; /* end of |buffer| */ +extern char *loc; /* points to the next character to be read from the buffer*/ +extern char *limit; /* points to the last character in the buffer */ + +@ Code related to identifier and section name storage: +@d length(c) (c+1)->byte_start-(c)->byte_start /* the length of a name */ +@d print_id(c) term_write((c)->byte_start,length((c))) /* print identifier */ +@d llink link /* left link in binary search tree for section names */ +@d rlink dummy.Rlink /* right link in binary search tree for section names */ +@d root name_dir->rlink /* the root of the binary search tree + for section names */ +@d chunk_marker 0 + +@<Common code...@>= +typedef struct name_info { + char *byte_start; /* beginning of the name in |byte_mem| */ + struct name_info *link; + union { + struct name_info *Rlink; /* right link in binary search tree for section + names */ + struct { + char Ilk; /* used by identifiers in \.{CWEAVE} only */ + boolean Func_flag; + } dummy1; + } dummy; + char *equiv_or_xref; /* info corresponding to names */ +} name_info; /* contains information about an identifier or section name */ +typedef name_info *name_pointer; /* pointer into array of \&{name\_info}s */ +typedef name_pointer *hash_pointer; +extern char byte_mem[]; /* characters of names */ +extern char *byte_mem_end; /* end of |byte_mem| */ +extern name_info name_dir[]; /* information about names */ +extern name_pointer name_dir_end; /* end of |name_dir| */ +extern name_pointer name_ptr; /* first unused position in |byte_start| */ +extern char *byte_ptr; /* first unused position in |byte_mem| */ +extern name_pointer hash[]; /* heads of hash lists */ +extern hash_pointer hash_end; /* end of |hash| */ +extern hash_pointer h; /* index into hash-head array */ +extern name_pointer id_lookup(); /* looks up a string in the identifier table */ +extern name_pointer section_lookup(); /* finds section name */ +extern void print_section_name(), sprint_section_name(); + +@ Code related to error handling: +@d spotless 0 /* |history| value for normal jobs */ +@d harmless_message 1 /* |history| value when non-serious info was printed */ +@d error_message 2 /* |history| value when an error was noted */ +@d fatal_message 3 /* |history| value when we had to stop prematurely */ +@d mark_harmless {if (history==spotless) history=harmless_message;} +@d mark_error history=error_message +@d confusion(s) fatal("! This can't happen: ",s) + +@<Common...@>= +extern history; /* indicates how bad this run was */ +extern err_print(); /* print error message and context */ +extern wrap_up(); /* indicate |history| and exit */ +extern void fatal(); /* issue error message and die */ +extern void overflow(); /* succumb because a table has overflowed */ + +@ Code related to file handling: +@f line x /* make |line| an unreserved word */ +@d max_file_name_length 60 +@d cur_file file[include_depth] /* current file */ +@d cur_file_name file_name[include_depth] /* current file name */ +@d web_file_name file_name[0] /* main source file name */ +@d cur_line line[include_depth] /* number of current line in current file */ + +@<Common code...@>= +extern include_depth; /* current level of nesting */ +extern FILE *file[]; /* stack of non-change files */ +extern FILE *change_file; /* change file */ +extern char C_file_name[]; /* name of |C_file| */ +extern char tex_file_name[]; /* name of |tex_file| */ +extern char idx_file_name[]; /* name of |idx_file| */ +extern char scn_file_name[]; /* name of |scn_file| */ +extern char file_name[][max_file_name_length]; + /* stack of non-change file names */ +extern char change_file_name[]; /* name of change file */ +extern line[]; /* number of current line in the stacked files */ +extern change_line; /* number of current line in change file */ +extern boolean input_has_ended; /* if there is no more input */ +extern boolean changing; /* if the current line is from |change_file| */ +extern boolean web_file_open; /* if the web file is being read */ +extern reset_input(); /* initialize to read the web file and change file */ +extern get_line(); /* inputs the next line */ +extern check_complete(); /* checks that all changes were picked up */ + +@ Code related to section numbers: +@<Common code...@>= +typedef unsigned short sixteen_bits; +extern sixteen_bits section_count; /* the current section number */ +extern boolean changed_section[]; /* is the section changed? */ +extern boolean change_pending; /* is a decision about change still unclear? */ +extern boolean print_where; /* tells \.{CTANGLE} to print line and file info */ + +@ Code related to command line arguments: +@d show_banner flags['b'] /* should the banner line be printed? */ +@d show_progress flags['p'] /* should progress reports be printed? */ +@d show_happiness flags['h'] /* should lack of errors be announced? */ + +@<Common code...@>= +extern int argc; /* copy of |ac| parameter to |main| */ +extern char **argv; /* copy of |av| parameter to |main| */ +extern boolean flags[]; /* an option for each 7-bit code */ + +@ Code relating to output: +@d update_terminal fflush(stdout) /* empty the terminal output buffer */ +@d new_line putchar('\n') @d putxchar putchar +@d term_write(a,b) fflush(stdout),fwrite(a,sizeof(char),b,stdout) +@d C_printf(c,a) fprintf(C_file,c,a) +@d C_putc(c) putc(c,C_file) + +@<Common code...@>= +extern FILE *C_file; /* where output of \.{CTANGLE} goes */ +extern FILE *tex_file; /* where output of \.{CWEAVE} goes */ +extern FILE *idx_file; /* where index from \.{CWEAVE} goes */ +extern FILE *scn_file; /* where list of sections from \.{CWEAVE} goes */ +extern FILE *active_file; /* currently active file for \.{CWEAVE} output */ + +@ The procedure that gets everything rolling: + +@<Common code...@>= +extern void common_init(); + + +@q end of common.h @> diff --git a/web/c_cpp/c2cweb/cweb/common.w b/web/c_cpp/c2cweb/cweb/common.w new file mode 100644 index 0000000000..48caf68e24 --- /dev/null +++ b/web/c_cpp/c2cweb/cweb/common.w @@ -0,0 +1,1415 @@ +% 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.3 --- December 1994 (works with later versions too) + +% Copyright (C) 1987,1990,1993 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. + +\def\v{\char'174} % vertical (|) in typewriter font + +\def\title{Common code for CTANGLE and CWEAVE (Version 3.3)} +\def\topofcontents{\null\vfill + \centerline{\titlefont Common code for {\ttitlefont CTANGLE} and + {\ttitlefont CWEAVE}} + \vskip 15pt + \centerline{(Version 3.3)} + \vfill} +\def\botofcontents{\vfill +\noindent +Copyright \copyright\ 1987, 1990, 1993 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 file contains code common +to both \.{CTANGLE} and \.{CWEAVE}, which roughly concerns the following +problems: character uniformity, input routines, error handling and +parsing of command line. We have tried to concentrate in this file +all the system dependencies, so as to maximize portability. + +In the texts below we will +sometimes use \.{CWEB} to refer to either of the two component +programs, if no confusion can arise. + +The file begins with a few basic definitions. + +@c +@<Include files@>@/ +@h +@<Definitions that should agree with \.{CTANGLE} and \.{CWEAVE}@>@/ +@<Other definitions@>@/ +@<Predeclaration of procedures@>@/ + +@ In certain cases \.{CTANGLE} and \.{CWEAVE} should do almost, but not +quite, the same thing. In these cases we've written common code for +both, differentiating between the two by means of the global variable +|program|. + +@d ctangle 0 +@d cweave 1 + +@<Definitions...@>= +typedef short boolean; +boolean program; /* \.{CWEAVE} or \.{CTANGLE}? */ + +@ \.{CWEAVE} 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 file, and finally it sorts and outputs the index. +Similarly, \.{CTANGLE} operates in two phases. +The global variable |phase| tells which phase we are in. + +@<Other...@>= int phase; /* which phase are we in? */ + +@ There's an initialization procedure that gets both \.{CTANGLE} and +\.{CWEAVE} off to a good start. We will fill in the details of this +procedure later. + +@c +void +common_init() +{ + @<Initialize pointers@>; + @<Set the default options common to \.{CTANGLE} and \.{CWEAVE}@>; + @<Scan arguments and open output files@>; +} + +@*1 The character set. +\.{CWEB} uses the conventions of \CEE/ programs found in the standard +\.{ctype.h} header file. + +@<Include files@>= +#include <ctype.h> + +@ A few character pairs are encoded internally as single characters, +using the definitions below. These definitions are consistent with +an extension of ASCII code originally developed at MIT and explained in +Appendix~C of {\sl The \TEX/book\/}; thus, users who have such a +character set can type things like \.{\char'32} and \.{\char'4} instead +of \.{!=} and \.{\&\&}. (However, their files will not be too portable +until more people adopt the extended code.) + +If the character set is not ASCII, the definitions given here may conflict +with existing characters; in such cases, other arbitrary codes should be +substituted. The indexes to \.{CTANGLE} and \.{CWEAVE} mention every +case where similar codes may have to be changed in order to +avoid character conflicts. Look for the entry ``ASCII code dependencies'' +in those indexes. + +@^ASCII code dependencies@> +@^system dependencies@> + +@d and_and 04 /* `\.{\&\&}'\,; corresponds to MIT's {\tentex\char'4} */ +@d lt_lt 020 /* `\.{<<}'\,; corresponds to MIT's {\tentex\char'20} */ +@d gt_gt 021 /* `\.{>>}'\,; corresponds to MIT's {\tentex\char'21} */ +@d plus_plus 013 /* `\.{++}'\,; corresponds to MIT's {\tentex\char'13} */ +@d minus_minus 01 /* `\.{--}'\,; corresponds to MIT's {\tentex\char'1} */ +@d minus_gt 031 /* `\.{->}'\,; corresponds to MIT's {\tentex\char'31} */ +@d not_eq 032 /* `\.{!=}'\,; corresponds to MIT's {\tentex\char'32} */ +@d lt_eq 034 /* `\.{<=}'\,; corresponds to MIT's {\tentex\char'34} */ +@d gt_eq 035 /* `\.{>=}'\,; corresponds to MIT's {\tentex\char'35} */ +@d eq_eq 036 /* `\.{==}'\,; corresponds to MIT's {\tentex\char'36} */ +@d or_or 037 /* `\.{\v\v}'\,; corresponds to MIT's {\tentex\char'37} */ +@d dot_dot_dot 016 /* `\.{...}'\,; corresponds to MIT's {\tentex\char'16} */ +@d colon_colon 06 /* `\.{::}'\,; corresponds to MIT's {\tentex\char'6} */ +@d period_ast 026 /* `\.{.*}'\,; corresponds to MIT's {\tentex\char'26} */ +@d minus_gt_ast 027 /* `\.{->*}'\,; corresponds to MIT's {\tentex\char'27} */ + +@** Input routines. The lowest level of input to the \.{CWEB} programs +is performed by |input_ln|, which must be told which file to read from. +The return value of |input_ln| is 1 if the read is successful and 0 if +not (generally this means the file has ended). The conventions +of \TEX/ are followed; i.e., the characters of the next line of the file +are copied into the |buffer| array, +and the global variable |limit| is set to the first unoccupied position. +Trailing blanks are ignored. The value of |limit| must be strictly less +than |buf_size|, so that |buffer[buf_size-1]| is never filled. + +Since |buf_size| is strictly less than |long_buf_size|, +some of \.{CWEB}'s routines use the fact that it is safe to refer to +|*(limit+2)| without overstepping the bounds of the array. + +@d buf_size 100 /* for \.{CWEAVE} and \.{CTANGLE} */ +@d longest_name 1000 +@d long_buf_size (buf_size+longest_name) /* for \.{CWEAVE} */ +@d xisspace(c) (isspace(c)&&((unsigned char)c<0200)) +@d xisupper(c) (isupper(c)&&((unsigned char)c<0200)) + +@<Definitions...@>= +char buffer[long_buf_size]; /* where each line of input goes */ +char *buffer_end=buffer+buf_size-2; /* end of |buffer| */ +char *limit=buffer; /* points to the last character in the buffer */ +char *loc=buffer; /* points to the next character to be read from the buffer */ + +@ @<Include files@>= +#include <stdio.h> + +@ In the unlikely event that your standard I/O library does not +support |feof|, |getc| and |ungetc| you may have to change things here. +@^system dependencies@> + +@c +int input_ln(fp) /* copies a line into |buffer| or returns 0 */ +FILE *fp; /* what file to read from */ +{ + register int c=EOF; /* character read; initialized so some compilers won't complain */ + register char *k; /* where next character goes */ + if (feof(fp)) return(0); /* we have hit end-of-file */ + limit = k = buffer; /* beginning of buffer */ + while (k<=buffer_end && (c=getc(fp)) != EOF && c!='\n') + if ((*(k++) = c) != ' ') limit = k; + if (k>buffer_end) + if ((c=getc(fp))!=EOF && c!='\n') { + ungetc(c,fp); loc=buffer; err_print("! Input line too long"); +@.Input line too long@> + } + if (c==EOF && limit==buffer) return(0); /* there was nothing after + the last newline */ + return(1); +} + +@ Now comes the problem of deciding which file to read from next. +Recall that the actual text that \.{CWEB} should process comes from two +streams: a |web_file|, which can contain possibly nested include +commands \.{@@i}, and a |change_file|, which might also contain +includes. The |web_file| together with the currently open include +files form a stack |file|, whose names are stored in a parallel stack +|file_name|. The boolean |changing| tells whether or not we're reading +from the |change_file|. + +The line number of each open file is also kept for error reporting and +for the benefit of \.{CTANGLE}. + +@f line x /* make |line| an unreserved word */ +@d max_include_depth 10 /* maximum number of source files open + simultaneously, not counting the change file */ +@d max_file_name_length 60 +@d cur_file file[include_depth] /* current file */ +@d cur_file_name file_name[include_depth] /* current file name */ +@d cur_line line[include_depth] /* number of current line in current file */ +@d web_file file[0] /* main source file */ +@d web_file_name file_name[0] /* main source file name */ + +@<Definitions...@>= +int include_depth; /* current level of nesting */ +FILE *file[max_include_depth]; /* stack of non-change files */ +FILE *change_file; /* change file */ +char file_name[max_include_depth][max_file_name_length]; + /* stack of non-change file names */ +char change_file_name[max_file_name_length]; /* name of change file */ +char alt_web_file_name[max_file_name_length]; /* alternate name to try */ +int line[max_include_depth]; /* number of current line in the stacked files */ +int change_line; /* number of current line in change file */ +int change_depth; /* where \.{@@y} originated during a change */ +boolean input_has_ended; /* if there is no more input */ +boolean changing; /* if the current line is from |change_file| */ +boolean web_file_open=0; /* if the web file is being read */ + +@ When |changing==0|, the next line of |change_file| is kept in +|change_buffer|, for purposes of comparison with the next +line of |cur_file|. After the change file has been completely input, we +set |change_limit=change_buffer|, +so that no further matches will be made. + +Here's a shorthand expression for inequality between the two lines: + +@d lines_dont_match (change_limit-change_buffer != limit-buffer || + strncmp(buffer, change_buffer, limit-buffer)) + +@<Other...@>= +char change_buffer[buf_size]; /* next line of |change_file| */ +char *change_limit; /* points to the last character in |change_buffer| */ + +@ Procedure |prime_the_change_buffer| +sets |change_buffer| in preparation for the next matching operation. +Since blank lines in the change file are not used for matching, we have +|(change_limit==change_buffer && !changing)| if and only if +the change file is exhausted. This procedure is called only when +|changing| is 1; hence error messages will be reported correctly. + +@c +void +prime_the_change_buffer() +{ + change_limit=change_buffer; /* this value is used if the change file ends */ + @<Skip over comment lines in the change file; |return| if end of file@>; + @<Skip to the next nonblank line; |return| if end of file@>; + @<Move |buffer| and |limit| to |change_buffer| and |change_limit|@>; +} + +@ While looking for a line that begins with \.{@@x} in the change file, we +allow lines that begin with \.{@@}, as long as they don't begin with \.{@@y}, +\.{@@z} or \.{@@i} (which would probably mean that the change file is fouled up). + +@<Skip over comment lines in the change file...@>= +while(1) { + change_line++; + if (!input_ln(change_file)) return; + if (limit<buffer+2) continue; + if (buffer[0]!='@@') continue; + if (xisupper(buffer[1])) buffer[1]=tolower(buffer[1]); + if (buffer[1]=='x') break; + if (buffer[1]=='y' || buffer[1]=='z' || buffer[1]=='i') { + loc=buffer+2; + err_print("! Missing @@x in change file"); +@.Missing @@x...@> + } +} + +@ Here we are looking at lines following the \.{@@x}. + +@<Skip to the next nonblank line...@>= +do { + change_line++; + if (!input_ln(change_file)) { + err_print("! Change file ended after @@x"); +@.Change file ended...@> + return; + } +} while (limit==buffer); + +@ @<Move |buffer| and |limit| to |change_buffer| and |change_limit|@>= +{ + change_limit=change_buffer-buffer+limit; + strncpy(change_buffer,buffer,limit-buffer+1); +} + +@ The following procedure is used to see if the next change entry should +go into effect; it is called only when |changing| is 0. +The idea is to test whether or not the current +contents of |buffer| matches the current contents of |change_buffer|. +If not, there's nothing more to do; but if so, a change is called for: +All of the text down to the \.{@@y} is supposed to match. An error +message is issued if any discrepancy is found. Then the procedure +prepares to read the next line from |change_file|. + +When a match is found, the current section is marked as changed unless +the first line after the \.{@@x} and after the \.{@@y} both start with +either |'@@*'| or |'@@ '| (possibly preceded by whitespace). + +This procedure is called only when |buffer<limit|, i.e., when the +current line is nonempty. + +@d if_section_start_make_pending(b) {@+*limit='!'; + for (loc=buffer;xisspace(*loc);loc++) ; + *limit=' '; + if (*loc=='@@' && (xisspace(*(loc+1)) || *(loc+1)=='*')) change_pending=b; +} + +@c +void +check_change() /* switches to |change_file| if the buffers match */ +{ + int n=0; /* the number of discrepancies found */ + if (lines_dont_match) return; + change_pending=0; + if (!changed_section[section_count]) { + if_section_start_make_pending(1); + if (!change_pending) changed_section[section_count]=1; + } + while (1) { + changing=1; print_where=1; change_line++; + if (!input_ln(change_file)) { + err_print("! Change file ended before @@y"); +@.Change file ended...@> + change_limit=change_buffer; changing=0; + return; + } + if (limit>buffer+1 && buffer[0]=='@@') { + if (xisupper(buffer[1])) buffer[1]=tolower(buffer[1]); + @<If the current line starts with \.{@@y}, + report any discrepancies and |return|@>; + } + @<Move |buffer| and |limit|...@>; + changing=0; cur_line++; + while (!input_ln(cur_file)) { /* pop the stack or quit */ + if (include_depth==0) { + err_print("! CWEB file ended during a change"); +@.CWEB file ended...@> + input_has_ended=1; return; + } + include_depth--; cur_line++; + } + if (lines_dont_match) n++; + } +} + +@ @<If the current line starts with \.{@@y}...@>= +if (buffer[1]=='x' || buffer[1]=='z') { + loc=buffer+2; err_print("! Where is the matching @@y?"); +@.Where is the match...@> + } +else if (buffer[1]=='y') { + if (n>0) { + loc=buffer+2; + printf("\n! Hmm... %d ",n); + err_print("of the preceding lines failed to match"); +@.Hmm... n of the preceding...@> + } + change_depth=include_depth; + return; +} + +@ The |reset_input| procedure, which gets \.{CWEB} ready to read the +user's \.{CWEB} input, is used at the beginning of phase one of \.{CTANGLE}, +phases one and two of \.{CWEAVE}. + +@c +void +reset_input() +{ + limit=buffer; loc=buffer+1; buffer[0]=' '; + @<Open input files@>; + include_depth=0; cur_line=0; change_line=0; + change_depth=include_depth; + changing=1; prime_the_change_buffer(); changing=!changing; + limit=buffer; loc=buffer+1; buffer[0]=' '; input_has_ended=0; +} + +@ The following code opens the input files. +@^system dependencies@> + +@<Open input files@>= +if ((web_file=fopen(web_file_name,"r"))==NULL) { + strcpy(web_file_name,alt_web_file_name); + if ((web_file=fopen(web_file_name,"r"))==NULL) + fatal("! Cannot open input file ", web_file_name); +} +@.Cannot open input file@> +@.Cannot open change file@> +web_file_open=1; +if ((change_file=fopen(change_file_name,"r"))==NULL) + fatal("! Cannot open change file ", change_file_name); + +@ The |get_line| procedure is called when |loc>limit|; it puts the next +line of merged input into the buffer and updates the other variables +appropriately. A space is placed at the right end of the line. +This procedure returns |!input_has_ended| because we often want to +check the value of that variable after calling the procedure. + +If we've just changed from the |cur_file| to the |change_file|, or if +the |cur_file| has changed, we tell \.{CTANGLE} to print this +information in the \CEE/ file by means of the |print_where| flag. + +@d max_sections 2000 /* number of identifiers, strings, section names; + must be less than 10240 */ + +@<Defin...@>= +typedef unsigned short sixteen_bits; +sixteen_bits section_count; /* the current section number */ +boolean changed_section[max_sections]; /* is the section changed? */ +boolean change_pending; /* if the current change is not yet recorded in + |changed_section[section_count]| */ +boolean print_where=0; /* should \.{CTANGLE} print line and file info? */ + +@ @c +int get_line() /* inputs the next line */ +{ + restart: + if (changing && include_depth==change_depth) + @<Read from |change_file| and maybe turn off |changing|@>; + if (! changing || include_depth>change_depth) { + @<Read from |cur_file| and maybe turn on |changing|@>; + if (changing && include_depth==change_depth) goto restart; + } + loc=buffer; *limit=' '; + if (*buffer=='@@' && (*(buffer+1)=='i' || *(buffer+1)=='I')) { + loc=buffer+2; + while (loc<=limit && (*loc==' '||*loc=='\t'||*loc=='"')) loc++; + if (loc>=limit) { + err_print("! Include file name not given"); +@.Include file name ...@> + goto restart; + } + if (include_depth>=max_include_depth-1) { + err_print("! Too many nested includes"); +@.Too many nested includes@> + goto restart; + } + include_depth++; /* push input stack */ + @<Try to open include file, abort push if unsuccessful, go to |restart|@>; + } + return (!input_has_ended); +} + +@ When an \.{@@i} line is found in the |cur_file|, we must temporarily +stop reading it and start reading from the named include file. The +\.{@@i} line should give a complete file name with or without +double quotes. +If the environment variable \.{CWEBINPUTS} is set, or if the compiler flag +of the same name was defined at compile time, +\.{CWEB} will look for include files in the directory thus named, if +it cannot find them in the current directory. +(Colon-separated paths are not supported.) +The remainder of the \.{@@i} line after the file name is ignored. + +@d too_long() {include_depth--; + err_print("! Include file name too long"); goto restart;} + +@<Include...@>= +#include <stdlib.h> /* declaration of |getenv| and |exit| */ + +@ @<Try to open...@>= { + char temp_file_name[max_file_name_length]; + char *cur_file_name_end=cur_file_name+max_file_name_length-1; + char *k=cur_file_name, *kk; + int l; /* length of file name */ + + while (*loc!=' '&&*loc!='\t'&&*loc!='"'&&k<=cur_file_name_end) *k++=*loc++; + if (k>cur_file_name_end) too_long(); +@.Include file name ...@> + *k='\0'; + if ((cur_file=fopen(cur_file_name,"r"))!=NULL) { + cur_line=0; print_where=1; + goto restart; /* success */ + } + kk=getenv("CWEBINPUTS"); + if (kk!=NULL) { + if ((l=strlen(kk))>max_file_name_length-2) too_long(); + strcpy(temp_file_name,kk); + } + else { +#ifdef CWEBINPUTS + if ((l=strlen(CWEBINPUTS))>max_file_name_length-2) too_long(); + strcpy(temp_file_name,CWEBINPUTS); +#else + l=0; +#endif /* |CWEBINPUTS| */ + } + if (l>0) { + if (k+l+2>=cur_file_name_end) too_long(); +@.Include file name ...@> + for (; k>= cur_file_name; k--) *(k+l+1)=*k; + strcpy(cur_file_name,temp_file_name); + cur_file_name[l]='/'; /* \UNIX/ pathname separator */ + if ((cur_file=fopen(cur_file_name,"r"))!=NULL) { + cur_line=0; print_where=1; + goto restart; /* success */ + } + } + include_depth--; err_print("! Cannot open include file"); goto restart; +} + +@ @<Read from |cur_file|...@>= { + cur_line++; + while (!input_ln(cur_file)) { /* pop the stack or quit */ + print_where=1; + if (include_depth==0) {input_has_ended=1; break;} + else { + fclose(cur_file); include_depth--; + if (changing && include_depth==change_depth) break; + cur_line++; + } + } + if (!changing && !input_has_ended) + if (limit-buffer==change_limit-change_buffer) + if (buffer[0]==change_buffer[0]) + if (change_limit>change_buffer) check_change(); +} + +@ @<Read from |change_file|...@>= { + change_line++; + if (!input_ln(change_file)) { + err_print("! Change file ended without @@z"); +@.Change file ended...@> + buffer[0]='@@'; buffer[1]='z'; limit=buffer+2; + } + if (limit>buffer) { /* check if the change has ended */ + if (change_pending) { + if_section_start_make_pending(0); + if (change_pending) { + changed_section[section_count]=1; change_pending=0; + } + } + *limit=' '; + if (buffer[0]=='@@') { + if (xisupper(buffer[1])) buffer[1]=tolower(buffer[1]); + if (buffer[1]=='x' || buffer[1]=='y') { + loc=buffer+2; + err_print("! Where is the matching @@z?"); +@.Where is the match...@> + } + else if (buffer[1]=='z') { + prime_the_change_buffer(); changing=!changing; print_where=1; + } + } + } +} + +@ At the end of the program, we will tell the user if the change file +had a line that didn't match any relevant line in |web_file|. + +@c +void +check_complete(){ + if (change_limit!=change_buffer) { /* |changing| is 0 */ + strncpy(buffer,change_buffer,change_limit-change_buffer+1); + limit=buffer+(int)(change_limit-change_buffer); + changing=1; change_depth=include_depth; loc=buffer; + err_print("! Change file entry did not match"); +@.Change file entry did not match@> + } +} + +@** Storage of names and strings. +Both \.{CWEAVE} and \.{CTANGLE} store identifiers, section names and +other strings in a large array of |char|s, called |byte_mem|. +Information about the names is kept in the array |name_dir|, whose +elements are structures of type |name_info|, containing a pointer into +the |byte_mem| array (the address where the name begins) and other data. +A |name_pointer| variable is a pointer into |name_dir|. + +@d max_bytes 90000 /* the number of bytes in identifiers, + index entries, and section names; must be less than $2^{24}$ */ +@d max_names 4000 /* number of identifiers, strings, section names; + must be less than 10240 */ + +@<Definitions that...@>= +typedef struct name_info { + char *byte_start; /* beginning of the name in |byte_mem| */ + @<More elements of |name_info| structure@>@; +} name_info; /* contains information about an identifier or section name */ +typedef name_info *name_pointer; /* pointer into array of |name_info|s */ +char byte_mem[max_bytes]; /* characters of names */ +char *byte_mem_end = byte_mem+max_bytes-1; /* end of |byte_mem| */ +name_info name_dir[max_names]; /* information about names */ +name_pointer name_dir_end = name_dir+max_names-1; /* end of |name_dir| */ + +@ The actual sequence of characters in the name pointed to by a |name_pointer +p| appears in positions |p->byte_start| to |(p+1)->byte_start-1|, inclusive. +The |print_id| macro prints this text on the user's terminal. + +@d length(c) (c+1)->byte_start-(c)->byte_start /* the length of a name */ +@d print_id(c) term_write((c)->byte_start,length((c))) /* print identifier */ + +@ The first unused position in |byte_mem| and |name_dir| is +kept in |byte_ptr| and |name_ptr|, respectively. Thus we +usually have |name_ptr->byte_start==byte_ptr|, and certainly +we want to keep |name_ptr<=name_dir_end| and |byte_ptr<=byte_mem_end|. + +@<Defini...@>= +name_pointer name_ptr; /* first unused position in |byte_start| */ +char *byte_ptr; /* first unused position in |byte_mem| */ + +@ @<Init...@>= +name_dir->byte_start=byte_ptr=byte_mem; /* position zero in both arrays */ +name_ptr=name_dir+1; /* |name_dir[0]| will be used only for error recovery */ +name_ptr->byte_start=byte_mem; /* this makes name 0 of length zero */ + +@ The names of identifiers are found by computing a hash address |h| and +then looking at strings of bytes signified by the |name_pointer|s +|hash[h]|, |hash[h]->link|, |hash[h]->link->link|, \dots, +until either finding the desired name or encountering the null pointer. + +@<More elements of |name...@>= +struct name_info *link; + +@ The hash table itself +consists of |hash_size| entries of type |name_pointer|, and is +updated by the |id_lookup| procedure, which finds a given identifier +and returns the appropriate |name_pointer|. The matching is done by the +function |names_match|, which is slightly different in +\.{CWEAVE} and \.{CTANGLE}. If there is no match for the identifier, +it is inserted into the table. + +@d hash_size 353 /* should be prime */ + +@<Defini...@>= +typedef name_pointer *hash_pointer; +name_pointer hash[hash_size]; /* heads of hash lists */ +hash_pointer hash_end = hash+hash_size-1; /* end of |hash| */ +hash_pointer h; /* index into hash-head array */ + +@ @<Predec...@>= +extern int names_match(); + +@ Initially all the hash lists are empty. + +@<Init...@>= +for (h=hash; h<=hash_end; *h++=NULL) ; + +@ Here is the main procedure for finding identifiers: + +@c +name_pointer +id_lookup(first,last,t) /* looks up a string in the identifier table */ +char *first; /* first character of string */ +char *last; /* last character of string plus one */ +char t; /* the |ilk|; used by \.{CWEAVE} only */ +{ + char *i=first; /* position in |buffer| */ + int h; /* hash code */ + int l; /* length of the given identifier */ + name_pointer p; /* where the identifier is being sought */ + if (last==NULL) for (last=first; *last!='\0'; last++); + l=last-first; /* compute the length */ + @<Compute the hash code |h|@>; + @<Compute the name location |p|@>; + if (p==name_ptr) @<Enter a new name into the table at position |p|@>; + return(p); +} + +@ A simple hash code is used: If the sequence of +character codes is $c_1c_2\ldots c_n$, its hash value will be +$$(2^{n-1}c_1+2^{n-2}c_2+\cdots+c_n)\,\bmod\,|hash_size|.$$ + +@<Compute the hash...@>= +h=(unsigned char)*i; +while (++i<last) h=(h+h+(int)((unsigned char)*i)) % hash_size; +@^high-bit character handling@> + +@ If the identifier is new, it will be placed in position |p=name_ptr|, +otherwise |p| will point to its existing location. + +@<Compute the name location...@>= +p=hash[h]; +while (p && !names_match(p,first,l,t)) p=p->link; +if (p==NULL) { + p=name_ptr; /* the current identifier is new */ + p->link=hash[h]; hash[h]=p; /* insert |p| at beginning of hash list */ +} + +@ The information associated with a new identifier must be initialized +in a slightly different way in \.{CWEAVE} than in \.{CTANGLE}; hence the +|init_p| procedure. + +@<Pred...@>= +void init_p(); + +@ @<Enter a new name...@>= { + if (byte_ptr+l>byte_mem_end) overflow("byte memory"); + if (name_ptr>=name_dir_end) overflow("name"); + strncpy(byte_ptr,first,l); + (++name_ptr)->byte_start=byte_ptr+=l; + if (program==cweave) init_p(p,t); +} + +@ The names of sections are stored in |byte_mem| together +with the identifier names, but a hash table is not used for them because +\.{CTANGLE} needs to be able to recognize a section name when given a prefix of +that name. A conventional binary search tree is used to retrieve section names, +with fields called |llink| and |rlink| (where |llink| takes the place +of |link|). The root of this tree is stored in |name_dir->rlink|; +this will be the only information in |name_dir[0]|. + +Since the space used by |rlink| has a different function for +identifiers than for section names, we declare it as a |union|. + +@d llink link /* left link in binary search tree for section names */ +@d rlink dummy.Rlink /* right link in binary search tree for section names */ +@d root name_dir->rlink /* the root of the binary search tree + for section names */ + +@<More elements of |name...@>= +union { + struct name_info *Rlink; /* right link in binary search tree for section + names */ + char Ilk; /* used by identifiers in \.{CWEAVE} only */ +} dummy; + +@ @<Init...@>= +root=NULL; /* the binary search tree starts out with nothing in it */ + +@ If |p| is a |name_pointer| variable, as we have seen, +|p->byte_start| is the beginning of the area where the name +corresponding to |p| is stored. However, if |p| refers to a section +name, the name may need to be stored in chunks, because it may +``grow'': a prefix of the section name may be encountered before +the full name. Furthermore we need to know the length of the shortest +prefix of the name that was ever encountered. + +We solve this problem by inserting two extra bytes at |p->byte_start|, +representing the length of the shortest prefix, when |p| is a +section name. Furthermore, the last byte of the name will be a blank +space if |p| is a prefix. In the latter case, the name pointer +|p+1| will allow us to access additional chunks of the name: +The second chunk will begin at the name pointer |(p+1)->link|, +and if it too is a prefix (ending with blank) its |link| will point +to additional chunks in the same way. Null links are represented by +|name_dir|. + +@d first_chunk(p) ((p)->byte_start+2) +@d prefix_length(p) (int)((unsigned char)*((p)->byte_start)*256 + + (unsigned char)*((p)->byte_start+1)) +@d set_prefix_length(p,m) (*((p)->byte_start)=(m)/256, + *((p)->byte_start+1)=(m)%256) + +@c +void +print_section_name(p) +name_pointer p; +{ + char *ss, *s = first_chunk(p); + name_pointer q = p+1; + while (p!=name_dir) { + ss = (p+1)->byte_start-1; + if (*ss==' ' && ss>=s) { + term_write(s,ss-s); p=q->link; q=p; + } else { + term_write(s,ss+1-s); p=name_dir; q=NULL; + } + s = p->byte_start; + } + if (q) term_write("...",3); /* complete name not yet known */ +} + +@ @c +void +sprint_section_name(dest,p) + char*dest; + name_pointer p; +{ + char *ss, *s = first_chunk(p); + name_pointer q = p+1; + while (p!=name_dir) { + ss = (p+1)->byte_start-1; + if (*ss==' ' && ss>=s) { + p=q->link; q=p; + } else { + ss++; p=name_dir; + } + strncpy(dest,s,ss-s), dest+=ss-s; + s = p->byte_start; + } + *dest='\0'; +} + +@ @c +void +print_prefix_name(p) +name_pointer p; +{ + char *s = first_chunk(p); + int l = prefix_length(p); + term_write(s,l); + if (s+l<(p+1)->byte_start) term_write("...",3); +} + +@ When we compare two section names, we'll need a function analogous to +|strcmp|. But we do not assume the strings +are null-terminated, and we keep an eye open for prefixes and extensions. + +@d less 0 /* the first name is lexicographically less than the second */ +@d equal 1 /* the first name is equal to the second */ +@d greater 2 /* the first name is lexicographically greater than the second */ +@d prefix 3 /* the first name is a proper prefix of the second */ +@d extension 4 /* the first name is a proper extension of the second */ + +@c +int web_strcmp(j,j_len,k,k_len) /* fuller comparison than |strcmp| */ + char *j, *k; /* beginning of first and second strings */ + int j_len, k_len; /* length of strings */ +{ + char *j1=j+j_len, *k1=k+k_len; + while (k<k1 && j<j1 && *j==*k) k++, j++; + if (k==k1) if (j==j1) return equal; + else return extension; + else if (j==j1) return prefix; + else if (*j<*k) return less; + else return greater; +} + +@ Adding a section name to the tree is straightforward if we know its +parent and whether it's the |rlink| or |llink| of the parent. As a +special case, when the name is the first section being added, we set the +``parent'' to |NULL|. When a section name is created, it has only one +chunk, which however may be just a prefix: the full name will +hopefully be unveiled later. Obviously, |prefix_length| starts +out as the length of the first chunk, though it may decrease later. + +The information associated with a new node must be initialized +differently in \.{CWEAVE} and \.{CTANGLE}; hence the +|init_node| procedure, which is defined differently in \.{cweave.w} +and \.{ctangle.w}. + +@<Prede...@>= +extern void init_node(); + +@ @c +name_pointer +add_section_name(par,c,first,last,ispref) /* install a new node in the tree */ +name_pointer par; /* parent of new node */ +int c; /* right or left? */ +char *first; /* first character of section name */ +char *last; /* last character of section name, plus one */ +int ispref; /* are we adding a prefix or a full name? */ +{ + name_pointer p=name_ptr; /* new node */ + char *s=first_chunk(p); + int name_len=last-first+ispref; /* length of section name */ + if (s+name_len>byte_mem_end) overflow("byte memory"); + if (name_ptr+1>=name_dir_end) overflow("name"); + (++name_ptr)->byte_start=byte_ptr=s+name_len; + if (ispref) { + *(byte_ptr-1)=' '; + name_len--; + name_ptr->link=name_dir; + (++name_ptr)->byte_start=byte_ptr; + } + set_prefix_length(p,name_len); + strncpy(s,first,name_len); + p->llink=NULL; + p->rlink=NULL; + init_node(p); + return par==NULL ? (root=p) : c==less ? (par->llink=p) : (par->rlink=p); +} + +@ @c +void +extend_section_name(p,first,last,ispref) +name_pointer p; /* name to be extended */ +char *first; /* beginning of extension text */ +char *last; /* one beyond end of extension text */ +int ispref; /* are we adding a prefix or a full name? */ +{ + char *s; + name_pointer q=p+1; + int name_len=last-first+ispref; + if (name_ptr>=name_dir_end) overflow("name"); + while (q->link!=name_dir) q=q->link; + q->link=name_ptr; + s=name_ptr->byte_start; + name_ptr->link=name_dir; + if (s+name_len>byte_mem_end) overflow("byte memory"); + (++name_ptr)->byte_start=byte_ptr=s+name_len; + strncpy(s,first,name_len); + if (ispref) *(byte_ptr-1)=' '; +} + +@ The |section_lookup| procedure is supposed to find a +section name that matches a new name, installing the new name if +it doesn't match an existing one. The new name is the string +between |first| and |last|; a ``match'' means that the new name +exactly equals or is a prefix or extension of a name in the tree. + +@c +name_pointer +section_lookup(first,last,ispref) /* find or install section name in tree */ +char *first, *last; /* first and last characters of new name */ +int ispref; /* is the new name a prefix or a full name? */ +{ + int c=0; /* comparison between two names; initialized so some compilers won't complain */ + name_pointer p=root; /* current node of the search tree */ + name_pointer q=NULL; /* another place to look in the tree */ + name_pointer r=NULL; /* where a match has been found */ + name_pointer par=NULL; /* parent of |p|, if |r| is |NULL|; + otherwise parent of |r| */ + int name_len=last-first+1; + @<Look for matches for new name among shortest prefixes, complaining + if more than one is found@>; + @<If no match found, add new name to tree@>; + @<If one match found, check for compatibility and return match@>; +} + +@ A legal new name matches an existing section name if and only if it +matches the shortest prefix of that section name. Therefore we can +limit our search for matches to shortest prefixes, which eliminates +the need for chunk-chasing at this stage. + +@<Look for matches for new name among...@>= +while (p) { /* compare shortest prefix of |p| with new name */ + c=web_strcmp(first,name_len,first_chunk(p),prefix_length(p)); + if (c==less || c==greater) { /* new name does not match |p| */ + if (r==NULL) /* no previous matches have been found */ + par=p; + p=(c==less?p->llink:p->rlink); + } else { /* new name matches |p| */ + if (r!=NULL) { /* and also |r|: illegal */ + printf("\n! Ambiguous prefix: matches <"); +@.Ambiguous prefix ... @> + print_prefix_name(p); + printf(">\n and <"); + print_prefix_name(r); + err_print(">"); + return name_dir; /* the unsection */ + } + r=p; /* remember match */ + p=p->llink; /* try another */ + q=r->rlink; /* we'll get back here if the new |p| doesn't match */ + } + if (p==NULL) + p=q, q=NULL; /* |q| held the other branch of |r| */ +} + +@ @<If no match ...@>= + if (r==NULL) /* no matches were found */ + return add_section_name(par,c,first,last+1,ispref); + +@ Although error messages are given in anomalous cases, we do return the +unique best match when a discrepancy is found, because users often +change a title in one place while forgetting to change it elsewhere. + +@<If one match found, check for compatibility and return match@>= +switch(section_name_cmp(&first,name_len,r)) { + /* compare all of |r| with new name */ + case prefix: + if (!ispref) { + printf("\n! New name is a prefix of <"); +@.New name is a prefix...@> + print_section_name(r); + err_print(">"); + } + else if (name_len<prefix_length(r)) set_prefix_length(r,name_len); + /* fall through */ + case equal: return r; + case extension: if (!ispref || first<=last) + extend_section_name(r,first,last+1,ispref); + return r; + case bad_extension: + printf("\n! New name extends <"); +@.New name extends...@> + print_section_name(r); + err_print(">"); + return r; + default: /* no match: illegal */ + printf("\n! Section name incompatible with <"); +@.Section name incompatible...@> + print_prefix_name(r); + printf(">,\n which abbreviates <"); + print_section_name(r); + err_print(">"); + return r; +} + +@ The return codes of |section_name_cmp|, which compares a string with +the full name of a section, are those of |web_strcmp| plus +|bad_extension|, used when the string is an extension of a +supposedly already complete section name. This function has a side +effect when the comparison string is an extension: it advances the +address of the first character of the string by an amount equal to +the length of the known part of the section name. + +The name \.{@@<foo...@@>} should be an acceptable ``abbreviation'' +for \.{@@<foo@@>}. If such an abbreviation comes after the complete +name, there's no trouble recognizing it. If it comes before the +complete name, we simply append a null chunk. This logic requires +us to regard \.{@@<foo...@@>} as an ``extension'' of itself. + +@d bad_extension 5 + +@<Predec...@>= +int section_name_cmp(); + +@ @c +int section_name_cmp(pfirst,len,r) +char **pfirst; /* pointer to beginning of comparison string */ +int len; /* length of string */ +name_pointer r; /* section name being compared */ +{ + char *first=*pfirst; /* beginning of comparison string */ + name_pointer q=r+1; /* access to subsequent chunks */ + char *ss, *s=first_chunk(r); + int c; /* comparison */ + int ispref; /* is chunk |r| a prefix? */ + while (1) { + ss=(r+1)->byte_start-1; + if (*ss==' ' && ss>=r->byte_start) ispref=1,q=q->link; + else ispref=0,ss++,q=name_dir; + switch(c=web_strcmp(first,len,s,ss-s)) { + case equal: if (q==name_dir) + if (ispref) { + *pfirst=first+(ss-s); + return extension; /* null extension */ + } else return equal; + else return (q->byte_start==(q+1)->byte_start)? equal: prefix; + case extension: + if (!ispref) return bad_extension; + first += ss-s; + if (q!=name_dir) {len -= ss-s; s=q->byte_start; r=q; continue;} + *pfirst=first; return extension; + default: return c; + } + } +} + +@ The last component of |name_info| is different for \.{CTANGLE} and +\.{CWEAVE}. In \.{CTANGLE}, 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|. In \.{CWEAVE}, on the other hand, if +|p| points to an identifier, |p->xref| is a pointer to its +list of cross-references, an element of the array |xmem|. The make-up +of |text_info| and |xmem| is discussed in the \.{CTANGLE} and \.{CWEAVE} +source files, respectively; here we just declare a common field +|equiv_or_xref| as a pointer to a |char|. + +@<More elements of |name...@>= +char *equiv_or_xref; /* info corresponding to names */ + +@** Reporting errors to the user. +A global variable called |history| will contain one of four values +at the end of every run: |spotless| means that no unusual messages were +printed; |harmless_message| means that a message of possible interest +was printed but no serious errors were detected; |error_message| means that +at least one error was found; |fatal_message| means that the program +terminated abnormally. The value of |history| does not influence the +behavior of the program; it is simply computed for the convenience +of systems that might want to use such information. + +@d spotless 0 /* |history| value for normal jobs */ +@d harmless_message 1 /* |history| value when non-serious info was printed */ +@d error_message 2 /* |history| value when an error was noted */ +@d fatal_message 3 /* |history| value when we had to stop prematurely */ +@d mark_harmless {if (history==spotless) history=harmless_message;} +@d mark_error history=error_message + +@<Definit...@>= +int history=spotless; /* indicates how bad this run was */ + +@ The command `|err_print("! Error message")|' will report a syntax error to +the user, by printing the error message at the beginning of a new line and +then giving an indication of where the error was spotted in the source file. +Note that no period follows the error message, since the error routine +will automatically supply a period. A newline is automatically supplied +if the string begins with |"!"|. + +@<Predecl...@>= +void err_print(); + +@ @c +void +err_print(s) /* prints `\..' and location of error message */ +char *s; +{ + char *k,*l; /* pointers into |buffer| */ + printf(*s=='!'? "\n%s" : "%s",s); + if(web_file_open) @<Print error location based on input buffer@>; + update_terminal; mark_error; +} + +@ The error locations can be indicated by using the global variables +|loc|, |cur_line|, |cur_file_name| and |changing|, +which tell respectively the first +unlooked-at position in |buffer|, the current line number, the current +file, and whether the current line is from |change_file| or |cur_file|. +This routine should be modified on systems whose standard text editor +has special line-numbering conventions. +@^system dependencies@> + +@<Print error location based on input buffer@>= +{if (changing && include_depth==change_depth) + printf(". (l. %d of change file)\n", change_line); +else if (include_depth==0) printf(". (l. %d)\n", cur_line); + else printf(". (l. %d of include file %s)\n", cur_line, cur_file_name); +l= (loc>=limit? limit: loc); +if (l>buffer) { + for (k=buffer; k<l; k++) + if (*k=='\t') putchar(' '); + else putchar(*k); /* print the characters already read */ + putchar('\n'); + for (k=buffer; k<l; k++) putchar(' '); /* space out the next line */ +} +for (k=l; k<limit; k++) putchar(*k); /* print the part not yet read */ +if (*limit=='|') putchar('|'); /* end of \CEE/ text in section names */ +putchar(' '); /* to separate the message from future asterisks */ +} + +@ When no recovery from some error has been provided, we have to wrap +up and quit as graciously as possible. This is done by calling the +function |wrap_up| at the end of the code. + +\.{CTANGLE} and \.{CWEAVE} have their own notions about how to +print the job statistics. + +@<Prede...@>= +int wrap_up(); +extern void print_stats(); + +@ Some implementations may wish to pass the |history| value to the +operating system so that it can be used to govern whether or not other +programs are started. Here, for instance, we pass the operating system +a status of 0 if and only if only harmless messages were printed. +@^system dependencies@> + +@c +int wrap_up() { + putchar('\n'); + if (show_stats) + print_stats(); /* print statistics about memory usage */ + @<Print the job |history|@>; + if (history > harmless_message) return(1); + else return(0); +} + +@ @<Print the job |history|@>= +switch (history) { +case spotless: if (show_happiness) printf("(No errors were found.)\n"); break; +case harmless_message: + printf("(Did you see the warning message above?)\n"); break; +case error_message: + printf("(Pardon me, but I think I spotted something wrong.)\n"); break; +case fatal_message: printf("(That was a fatal error, my friend.)\n"); +} /* there are no other cases */ + +@ When there is no way to recover from an error, the |fatal| subroutine is +invoked. This happens most often when |overflow| occurs. + +@<Predec...@>= +void fatal(), overflow(); + +@ The two parameters to |fatal| are strings that are essentially +concatenated to print the final error message. + +@c void +fatal(s,t) + char *s,*t; +{ + if (*s) printf(s); + err_print(t); + history=fatal_message; exit(wrap_up()); +} + +@ An overflow stop occurs if \.{CWEB}'s tables aren't large enough. + +@c void +overflow(t) + char *t; +{ + printf("\n! Sorry, %s capacity exceeded",t); fatal("",""); +} +@.Sorry, capacity exceeded@> + +@ Sometimes the program's behavior is far different from what it should be, +and \.{CWEB} prints an error message that is really for the \.{CWEB} +maintenance person, not the user. In such cases the program says +|confusion("indication of where we are")|. + +@d confusion(s) fatal("! This can't happen: ",s) +@.This can't happen@> + +@** Command line arguments. +The user calls \.{CWEAVE} and \.{CTANGLE} with arguments on the command line. +These are either file names or flags to be turned off (beginning with |"-"|) +or flags to be turned on (beginning with |"+"|). +The following globals are for communicating the user's desires to the rest +of the program. The various file name variables contain strings with +the names of those files. Most of the 128 flags are undefined but available +for future extensions. + +@d show_banner flags['b'] /* should the banner line be printed? */ +@d show_progress flags['p'] /* should progress reports be printed? */ +@d show_stats flags['s'] /* should statistics be printed at end of run? */ +@d show_happiness flags['h'] /* should lack of errors be announced? */ + +@<Defin...@>= +int argc; /* copy of |ac| parameter to |main| */ +char **argv; /* copy of |av| parameter to |main| */ +char C_file_name[max_file_name_length]; /* name of |C_file| */ +char tex_file_name[max_file_name_length]; /* name of |tex_file| */ +char idx_file_name[max_file_name_length]; /* name of |idx_file| */ +char scn_file_name[max_file_name_length]; /* name of |scn_file| */ +boolean flags[128]; /* an option for each 7-bit code */ + +@ The |flags| will be initially zero. Some of them are set to~1 before +scanning the arguments; if additional flags are 1 by default they +should be set before calling |common_init|. + +@<Set the default options common to \.{CTANGLE} and \.{CWEAVE}@>= +show_banner=show_happiness=show_progress=1; + +@ We now must look at the command line arguments and set the file names +accordingly. At least one file name must be present: the \.{CWEB} +file. It may have an extension, or it may omit the extension to get |".w"| or +|".web"| added. The \TEX/ output file name is formed by replacing the \.{CWEB} +file name extension by |".tex"|, and the \CEE/ file name by replacing +the extension by |".c"|, after removing the directory name (if any). + +If there is a second file name present among the arguments, it is the +change file, again either with an extension or without one to get |".ch"|. +An omitted change file argument means that |"/dev/null"| should be used, +when no changes are desired. +@^system dependencies@> + +If there's a third file name, it will be the output file. + +@<Pred...@>= +void scan_args(); + +@ @c +void +scan_args() +{ + char *dot_pos; /* position of |'.'| in the argument */ + char *name_pos; /* file name beginning, sans directory */ + register char *s; /* register for scanning strings */ + boolean found_web=0,found_change=0,found_out=0; + /* have these names have been seen? */ + boolean flag_change; + + while (--argc > 0) { + if ((**(++argv)=='-'||**argv=='+')&&*(*argv+1)) @<Handle flag argument@>@; + else { + s=name_pos=*argv;@+dot_pos=NULL; + while (*s) { + if (*s=='.') dot_pos=s++; + else if (*s=='/') dot_pos=NULL,name_pos=++s; + else s++; + } + if (!found_web) @<Make + |web_file_name|, |tex_file_name| and |C_file_name|@>@; + else if (!found_change) @<Make |change_file_name| from |fname|@>@; + else if (!found_out) @<Override |tex_file_name| and |C_file_name|@>@; + else @<Print usage error message and quit@>; + } + } + if (!found_web) @<Print usage error message and quit@>; + if (found_change<=0) strcpy(change_file_name,"/dev/null"); +} + +@ We use all of |*argv| for the |web_file_name| if there is a |'.'| in it, +otherwise we add |".w"|. If this file can't be opened, we prepare an +|alt_web_file_name| by adding |"web"| after the dot. +The other file names come from adding other things +after the dot. We must check that there is enough room in +|web_file_name| and the other arrays for the argument. + +@<Make |web_file_name|...@>= +{ + if (s-*argv > max_file_name_length-5) + @<Complain about argument length@>; + if (dot_pos==NULL) + sprintf(web_file_name,"%s.w",*argv); + else { + strcpy(web_file_name,*argv); + *dot_pos=0; /* string now ends where the dot was */ + } + sprintf(alt_web_file_name,"%s.web",*argv); + sprintf(tex_file_name,"%s.tex",name_pos); /* strip off directory name */ + sprintf(idx_file_name,"%s.idx",name_pos); + sprintf(scn_file_name,"%s.scn",name_pos); + sprintf(C_file_name,"%s.c",name_pos); + found_web=1; +} + +@ @<Make |change_file_name|...@>= +{ + if (strcmp(*argv,"-")==0) found_change=-1; + else { + if (s-*argv > max_file_name_length-4) + @<Complain about argument length@>; + if (dot_pos==NULL) + sprintf(change_file_name,"%s.ch",*argv); + else strcpy(change_file_name,*argv); + found_change=1; + } +} + +@ @<Override...@>= +{ + if (s-*argv > max_file_name_length-5) + @<Complain about argument length@>; + if (dot_pos==NULL) { + sprintf(tex_file_name,"%s.tex",*argv); + sprintf(idx_file_name,"%s.idx",*argv); + sprintf(scn_file_name,"%s.scn",*argv); + sprintf(C_file_name,"%s.c",*argv); + } else { + strcpy(tex_file_name,*argv); + if (flags['x']) { /* indexes will be generated */ + if (program==cweave && strcmp(*argv+strlen(*argv)-4,".tex")!=0) + fatal("! Output file name should end with .tex\n",*argv); +@.Output file name...tex@> + strcpy(idx_file_name,*argv); + strcpy(idx_file_name+strlen(*argv)-4,".idx"); + strcpy(scn_file_name,*argv); + strcpy(scn_file_name+strlen(*argv)-4,".scn"); + } + strcpy(C_file_name,*argv); + } + found_out=1; +} + +@ @<Handle flag...@>= +{ + if (**argv=='-') flag_change=0; + else flag_change=1; + for(dot_pos=*argv+1;*dot_pos>'\0';dot_pos++) + flags[*dot_pos]=flag_change; +} + +@ @<Print usage error message and quit@>= +{ +if (program==ctangle) + fatal( +"! Usage: ctangle [options] webfile[.w] [{changefile[.ch]|-} [outfile[.c]]]\n" + ,""); +@.Usage:@> +else fatal( +"! Usage: cweave [options] webfile[.w] [{changefile[.ch]|-} [outfile[.tex]]]\n" + ,""); +} + +@ @<Complain about arg...@>= fatal("! Filename too long\n", *argv); +@.Filename too long@> + +@** Output. Here is the code that opens the output file: +@^system dependencies@> + +@<Defin...@>= +FILE *C_file; /* where output of \.{CTANGLE} goes */ +FILE *tex_file; /* where output of \.{CWEAVE} goes */ +FILE *idx_file; /* where index from \.{CWEAVE} goes */ +FILE *scn_file; /* where list of sections from \.{CWEAVE} goes */ +FILE *active_file; /* currently active file for \.{CWEAVE} output */ + +@ @<Scan arguments and open output files@>= +scan_args(); +if (program==ctangle) { + if ((C_file=fopen(C_file_name,"w"))==NULL) + fatal("! Cannot open output file ", C_file_name); +@.Cannot open output file@> +} +else { + if ((tex_file=fopen(tex_file_name,"w"))==NULL) + fatal("! Cannot open output file ", tex_file_name); +} + +@ The |update_terminal| procedure is called when we want +to make sure that everything we have output to the terminal so far has +actually left the computer's internal buffers and been sent. +@^system dependencies@> + +@d update_terminal fflush(stdout) /* empty the terminal output buffer */ + +@ Terminal output uses |putchar| and |putc| when we have to +translate from \.{CWEB}'s code into the external character code, +and |printf| when we just want to print strings. +Several macros make other kinds of output convenient. +@^system dependencies@> +@d new_line putchar('\n') @d putxchar putchar +@d term_write(a,b) fflush(stdout),fwrite(a,sizeof(char),b,stdout) +@d C_printf(c,a) fprintf(C_file,c,a) +@d C_putc(c) putc(c,C_file) /* isn't \CEE/ wonderfully consistent? */ + +@ We predeclare several standard system functions here instead of including +their system header files, because the names of the header files are not as +standard as the names of the functions. (For example, some \CEE/ environments +have \.{<string.h>} where others have \.{<strings.h>}.) + +@<Predecl...@>= +extern int strlen(); /* length of string */ +extern int strcmp(); /* compare strings lexicographically */ +extern char* strcpy(); /* copy one string to another */ +extern int strncmp(); /* compare up to $n$ string characters */ +extern char* strncpy(); /* copy up to $n$ string characters */ + +@** Index. diff --git a/web/c_cpp/c2cweb/cweb/cweave.c b/web/c_cpp/c2cweb/cweb/cweave.c new file mode 100644 index 0000000000..bff742feb7 --- /dev/null +++ b/web/c_cpp/c2cweb/cweb/cweave.c @@ -0,0 +1,4543 @@ +/*1:*/ +#line 66 "cweave.w" +/*6:*/ +#line 38 "common.h" + +#include <stdio.h> + +/*:6*//*38:*/ +#line 644 "cweave.w" + +#include <ctype.h> +#include <stdlib.h> + +/*:38*/ +#line 66 "cweave.w" + +#define banner "This is CWEAVE (Version 3.4) modified by Werner Lemberg (V1.5)\n" \ + +#define max_bytes 225000 \ + +#define max_names 10000 \ + +#define max_sections 2000 +#define hash_size 353 +#define buf_size 400 +#define longest_name 1000 +#define long_buf_size (buf_size+longest_name) +#define line_length 100 \ + +#define max_refs 20000 +#define max_toks 65000 \ + +#define max_texts 10200 \ + +#define max_scraps 65000 +#define stack_size 10000 \ + +#define ctangle 0 +#define cweave 1 \ + +#define and_and 04 +#define lt_lt 020 +#define gt_gt 021 +#define plus_plus 013 +#define minus_minus 01 +#define minus_gt 031 +#define not_eq 032 +#define lt_eq 034 +#define gt_eq 035 +#define eq_eq 036 +#define or_or 037 +#define dot_dot_dot 016 +#define colon_colon 06 +#define period_ast 026 +#define minus_gt_ast 027 \ + +#define xisalpha(c) (isalpha(c) &&((eight_bits) c<0200) ) +#define xisdigit(c) (isdigit(c) &&((eight_bits) c<0200) ) +#define xisspace(c) (isspace(c) &&((eight_bits) c<0200) ) +#define xislower(c) (islower(c) &&((eight_bits) c<0200) ) +#define xisupper(c) (isupper(c) &&((eight_bits) c<0200) ) +#define xisxdigit(c) (isxdigit(c) &&((eight_bits) c<0200) ) \ + +#define length(c) (c+1) ->byte_start-(c) ->byte_start +#define print_id(c) term_write((c) ->byte_start,length((c) ) ) +#define llink link +#define rlink dummy.Rlink +#define root name_dir->rlink \ + +#define chunk_marker 0 \ + +#define spotless 0 +#define harmless_message 1 +#define error_message 2 +#define fatal_message 3 +#define mark_harmless {if(history==spotless) history= harmless_message;} +#define mark_error history= error_message +#define confusion(s) fatal("! This can't happen: ",s) \ + +#define max_file_name_length 60 +#define cur_file file[include_depth] +#define cur_file_name file_name[include_depth] +#define web_file_name file_name[0] +#define cur_line line[include_depth] \ + +#define show_banner flags['b'] +#define show_progress flags['p'] +#define show_happiness flags['h'] \ + +#define update_terminal fflush(stdout) +#define new_line putchar('\n') +#define putxchar putchar +#define term_write(a,b) fflush(stdout) ,fwrite(a,sizeof(char) ,b,stdout) +#define C_printf(c,a) fprintf(C_file,c,a) +#define C_putc(c) putc(c,C_file) \ + +#define ilk dummy.dummy1.Ilk +#define func_flag dummy.dummy1.Func_flag +#define normal 0 +#define roman 1 +#define wildcard 2 +#define typewriter 3 +#define abnormal(a) (a->ilk>typewriter) +#define custom 4 +#define unindexed(a) (a->ilk>custom) +#define quoted 5 +#define else_like 26 +#define public_like 40 +#define operator_like 41 +#define new_like 42 +#define catch_like 43 +#define for_like 45 +#define do_like 46 +#define if_like 47 +#define raw_rpar 48 +#define raw_unorbin 49 +#define const_like 50 +#define raw_int 51 +#define int_like 52 +#define case_like 53 +#define sizeof_like 54 +#define struct_like 55 +#define typedef_like 56 +#define define_like 57 \ + +#define file_flag (3*cite_flag) +#define def_flag (2*cite_flag) +#define cite_flag 10240 +#define xref equiv_or_xref \ + +#define append_xref(c) if(xref_ptr==xmem_end) overflow("cross-reference") ; \ +else(++xref_ptr) ->num= c; +#define no_xref (flags['x']==0) +#define make_xrefs flags['x'] +#define is_tiny(p) ((p+1) ->byte_start==(p) ->byte_start+1) \ + +#define ignore 00 +#define verbatim 02 +#define begin_short_comment 03 +#define begin_comment '\t' +#define underline '\n' +#define noop 0177 +#define xref_roman 0203 +#define xref_wildcard 0204 +#define xref_typewriter 0205 +#define TeX_string 0206 +#define ord 0207 +#define join 0210 +#define thin_space 0211 +#define math_break 0212 +#define line_break 0213 +#define big_line_break 0214 +#define no_line_break 0215 +#define pseudo_semi 0216 +#define macro_arg_open 0220 +#define macro_arg_close 0221 +#define trace 0222 +#define translit_code 0223 +#define output_defs_code 0224 +#define format_code 0225 +#define definition 0226 +#define begin_C 0227 +#define section_name 0230 +#define new_section 0231 +#define pseudo_lbrace 022 +#define pseudo_rbrace 023 \ + +#define constant 0200 +#define string 0201 +#define identifier 0202 \ + +#define isxalpha(c) ((c) =='_'||(c) =='$') \ + +#define ishigh(c) ((eight_bits) (c) >0177) \ + \ + +#define left_preproc ord +#define right_preproc 0217 +#define preproc_space '\b' +#define numb_numb '\f' \ + +#define compress(c) if(loc++<=limit) return(c) \ + +#define c_line_write(c) fflush(active_file) ,fwrite(out_buf+1,sizeof(char) ,c,active_file) +#define tex_putc(c) putc(c,active_file) +#define tex_new_line putc('\n',active_file) +#define tex_printf(c) fprintf(active_file,c) \ + +#define app_tok(c) {if(tok_ptr+2>tok_mem_end) overflow("token") ;*(tok_ptr++) = c;} \ + +#define exp 1 +#define unop 2 +#define binop 3 +#define unorbinop 4 \ + +#define cast 5 +#define question 6 +#define lbrace 7 +#define rbrace 8 +#define decl_head 9 +#define comma 10 +#define lpar 11 +#define rpar 12 +#define prelangle 13 +#define prerangle 14 +#define langle 15 +#define colcol 18 +#define base 19 +#define decl 20 +#define struct_head 21 +#define stmt 23 +#define function 24 +#define fn_decl 25 +#define semi 27 +#define colon 28 +#define tag 29 \ + +#define if_head 30 +#define else_head 31 +#define if_clause 32 +#define lproc 35 +#define rproc 36 +#define insert 37 +#define section_scrap 38 +#define dead 39 +#define begin_arg 58 +#define end_arg 59 +#define label 60 +#define do_head 61 +#define pp_space 62 \ + +#define math_rel 0206 +#define big_cancel 0210 +#define cancel 0211 +#define indent 0212 +#define outdent 0213 +#define opt 0214 +#define backup 0215 +#define break_space 0216 +#define force 0217 +#define big_force 0220 +#define preproc_line 0221 \ + +#define quoted_char 0222 \ + +#define end_translation 0223 +#define inserted 0224 \ + +#define trans trans_plus.Trans \ + +#define id_flag 10240 +#define res_flag 2*id_flag +#define section_flag 3*id_flag +#define tok_flag 4*id_flag +#define inner_tok_flag 5*id_flag \ + +#define no_math 2 +#define yes_math 1 +#define maybe_math 0 +#define big_app2(a) big_app1(a) ;big_app1(a+1) +#define big_app3(a) big_app2(a) ;big_app1(a+2) +#define big_app4(a) big_app3(a) ;big_app1(a+3) +#define app(a) *(tok_ptr++) = a +#define app1(a) *(tok_ptr++) = tok_flag+(int) ((a) ->trans-tok_start) \ + +#define cat1 (pp+1) ->cat +#define cat2 (pp+2) ->cat +#define cat3 (pp+3) ->cat +#define lhs_not_simple (pp->cat!=semi&&pp->cat!=raw_int&&pp->cat!=raw_unorbin \ +&&pp->cat!=raw_rpar&&pp->cat!=const_like) \ + +#define no_ident_found 0 \ + +#define force_lines flags['f'] +#define freeze_text *(++text_ptr) = tok_ptr \ + +#define safe_tok_incr 20 +#define safe_text_incr 10 +#define safe_scrap_incr 10 \ + +#define app_scrap(c,b) { \ +(++scrap_ptr) ->cat= (c) ;scrap_ptr->trans= text_ptr; \ +scrap_ptr->mathness= 5*(b) ; \ +freeze_text; \ +} \ + +#define alternative flags['a'] +#define func_index flags['i'] \ + \ + +#define inner 0 +#define outer 1 \ + +#define cur_end cur_state.end_field +#define cur_tok cur_state.tok_field +#define cur_mode cur_state.mode_field +#define init_stack stack_ptr= stack;cur_mode= outer \ + +#define res_word 0201 +#define section_code 0200 \ + +#define save_position save_line= out_line;save_place= out_ptr +#define emit_space_if_needed if(save_line!=out_line||save_place!=out_ptr) \ +out_str("\\Y") ; \ +space_checked= 1 \ + \ + +#define depth cat +#define head trans_plus.Head +#define sort_pointer scrap_pointer +#define sort_ptr scrap_ptr +#define max_sorts max_scraps \ + +#define infinity 255 \ + + +#line 67 "cweave.w" + +/*5:*/ +#line 32 "common.h" + +typedef short boolean; +typedef char unsigned eight_bits; +extern boolean program; +extern int phase; + +/*:5*//*7:*/ +#line 60 "common.h" + +char section_text[longest_name+1]; +char*section_text_end= section_text+longest_name; +char*id_first; +char*id_loc; + +/*:7*//*8:*/ +#line 75 "common.h" + +extern char buffer[]; +extern char*buffer_end; +extern char*loc; +extern char*limit; + +/*:8*//*9:*/ +#line 90 "common.h" + +typedef struct name_info{ +char*byte_start; +struct name_info*link; +union{ +struct name_info*Rlink; + +struct{ +char Ilk; +boolean Func_flag; +}dummy1; +}dummy; +char*equiv_or_xref; +}name_info; +typedef name_info*name_pointer; +typedef name_pointer*hash_pointer; +extern char byte_mem[]; +extern char*byte_mem_end; +extern name_info name_dir[]; +extern name_pointer name_dir_end; +extern name_pointer name_ptr; +extern char*byte_ptr; +extern name_pointer hash[]; +extern hash_pointer hash_end; +extern hash_pointer h; +extern name_pointer id_lookup(); +extern name_pointer section_lookup(); +extern void print_section_name(),sprint_section_name(); + +/*:9*//*10:*/ +#line 128 "common.h" + +extern history; +extern err_print(); +extern wrap_up(); +extern void fatal(); +extern void overflow(); + +/*:10*//*11:*/ +#line 143 "common.h" + +extern include_depth; +extern FILE*file[]; +extern FILE*change_file; +extern char C_file_name[]; +extern char tex_file_name[]; +extern char idx_file_name[]; +extern char scn_file_name[]; +extern char file_name[][max_file_name_length]; + +extern char change_file_name[]; +extern line[]; +extern change_line; +extern boolean input_has_ended; +extern boolean changing; +extern boolean web_file_open; +extern reset_input(); +extern get_line(); +extern check_complete(); + +/*:11*//*12:*/ +#line 164 "common.h" + +typedef unsigned short sixteen_bits; +extern sixteen_bits section_count; +extern boolean changed_section[]; +extern boolean change_pending; +extern boolean print_where; + +/*:12*//*13:*/ +#line 176 "common.h" + +extern int argc; +extern char**argv; +extern boolean flags[]; + +/*:13*//*14:*/ +#line 188 "common.h" + +extern FILE*C_file; +extern FILE*tex_file; +extern FILE*idx_file; +extern FILE*scn_file; +extern FILE*active_file; + +/*:14*//*15:*/ +#line 197 "common.h" + +extern void common_init(); + + + +#line 140 "cweave.w" + +/*:15*/ +#line 68 "cweave.w" + +/*18:*/ +#line 222 "cweave.w" + +typedef struct xref_info{ +sixteen_bits num; +struct xref_info*xlink; +}xref_info; +typedef xref_info*xref_pointer; + +/*:18*//*24:*/ +#line 332 "cweave.w" + +typedef sixteen_bits token; +typedef token*token_pointer; +typedef token_pointer*text_pointer; + +/*:24*//*103:*/ +#line 2012 "cweave.w" + +typedef struct{ +eight_bits cat; +eight_bits mathness; +union{ +text_pointer Trans; +/*231:*/ +#line 4257 "cweave.w" + +name_pointer Head; + +/*:231*/ +#line 2018 "cweave.w" + +}trans_plus; +}scrap; +typedef scrap*scrap_pointer; + +/*:103*//*185:*/ +#line 3412 "cweave.w" +typedef int mode; +typedef struct{ +token_pointer end_field; +token_pointer tok_field; +boolean mode_field; +}output_state; +typedef output_state*stack_pointer; + +/*:185*/ +#line 69 "cweave.w" + +/*17:*/ +#line 197 "cweave.w" + +boolean change_exists; + +/*:17*//*19:*/ +#line 229 "cweave.w" + +xref_info xmem[max_refs]; +xref_pointer xmem_end= xmem+max_refs-1; +xref_pointer xref_ptr; +sixteen_bits xref_switch,section_xref_switch; + +/*:19*//*25:*/ +#line 342 "cweave.w" + +token tok_mem[max_toks]; +token_pointer tok_mem_end= tok_mem+max_toks-1; +token_pointer tok_start[max_texts]; +token_pointer tok_ptr; +text_pointer text_ptr; +text_pointer tok_start_end= tok_start+max_texts-1; +token_pointer max_tok_ptr; +text_pointer max_text_ptr; + +/*:25*//*31:*/ +#line 521 "cweave.w" + +eight_bits ccode[256]; + +/*:31*//*37:*/ +#line 640 "cweave.w" + +name_pointer cur_section; +char cur_section_char; + +/*:37*//*41:*/ +#line 214 "cweave.ch" + +boolean preprocessing= 0; +int define_pos= 0; + +#line 695 "cweave.w" + +/*:41*//*43:*/ +#line 706 "cweave.w" + +boolean sharp_include_line= 0; + +/*:43*//*58:*/ +#line 980 "cweave.w" + +eight_bits next_control; + +/*:58*//*68:*/ +#line 1136 "cweave.w" + +name_pointer lhs,rhs; + +/*:68*//*73:*/ +#line 1219 "cweave.w" + +xref_pointer cur_xref; +boolean an_output; + +/*:73*//*77:*/ +#line 1261 "cweave.w" + +char out_buf[line_length+1]; +char*out_ptr; +char*out_buf_end= out_buf+line_length; +#line 268 "cweave.ch" +int out_line; + +boolean in_math= 0; +boolean escape= 0; +int math_brace_count= 0; +#line 1266 "cweave.w" + +/*:77*//*97:*/ +#line 1670 "cweave.w" + +char cat_name[256][12]; +eight_bits cat_index; + +/*:97*//*104:*/ +#line 2025 "cweave.w" + +scrap scrap_info[max_scraps]; +scrap_pointer scrap_info_end= scrap_info+max_scraps-1; +scrap_pointer pp; +scrap_pointer scrap_base; +scrap_pointer scrap_ptr; +scrap_pointer lo_ptr; +scrap_pointer hi_ptr; +scrap_pointer max_scr_ptr; + +/*:104*//*108:*/ +#line 2184 "cweave.w" + +int cur_mathness,init_mathness; + +/*:108*//*167:*/ +#line 2962 "cweave.w" + +int tracing; + +/*:167*//*186:*/ +#line 3425 "cweave.w" + +output_state cur_state; +output_state stack[stack_size]; +stack_pointer stack_ptr; +stack_pointer stack_end= stack+stack_size-1; +stack_pointer max_stack_ptr; + +/*:186*//*190:*/ +#line 3475 "cweave.w" + +name_pointer cur_name; + +/*:190*//*206:*/ +#line 3831 "cweave.w" + +int save_line; +char*save_place; +int sec_depth; +boolean space_checked; +boolean format_visible; +boolean doing_format= 0; +boolean group_found= 0; + +/*:206*//*215:*/ +#line 4014 "cweave.w" + +name_pointer this_section; + +/*:215*//*226:*/ +#line 4198 "cweave.w" + +sixteen_bits k_section; + +/*:226*//*228:*/ +#line 4224 "cweave.w" + +name_pointer bucket[256]; +name_pointer next_name; +name_pointer blink[max_names]; + +/*:228*//*232:*/ +#line 4267 "cweave.w" + +eight_bits cur_depth; +char*cur_byte; +sixteen_bits cur_val; +sort_pointer max_sort_ptr; + +/*:232*//*234:*/ +#line 4279 "cweave.w" + +eight_bits collate[102+128]; + + +/*:234*//*243:*/ +#line 4432 "cweave.w" + +xref_pointer next_xref,this_xref; + + +/*:243*/ +#line 70 "cweave.w" + +/*2:*/ +#line 78 "cweave.w" + +extern int strlen(); +extern int strcmp(); +extern char*strcpy(); +extern int strncmp(); +extern char*strncpy(); + +/*:2*//*34:*/ +#line 562 "cweave.w" + +void skip_limbo(); + +/*:34*//*39:*/ +#line 655 "cweave.w" + +eight_bits get_next(); + +/*:39*//*55:*/ +#line 933 "cweave.w" + +void skip_restricted(); + +/*:55*//*59:*/ +#line 986 "cweave.w" + +void phase_one(); + +/*:59*//*62:*/ +#line 1038 "cweave.w" + +void C_xref(); + +/*:62*//*64:*/ +#line 1066 "cweave.w" + +void outer_xref(); + +/*:64*//*74:*/ +#line 1227 "cweave.w" + +void section_check(); + +/*:74*//*83:*/ +#line 1363 "cweave.w" + +void break_out(); + +/*:83*//*91:*/ +#line 1506 "cweave.w" + +int copy_comment(); + +/*:91*//*114:*/ +#line 2388 "cweave.w" + +void underline_xref(); + +/*:114*//*180:*/ +#line 3289 "cweave.w" + +void app_cur_id(); + +/*:180*//*193:*/ +#line 3544 "cweave.w" + +void make_output(); + +/*:193*//*204:*/ +#line 3804 "cweave.w" + +void phase_two(); + +/*:204*//*211:*/ +#line 3927 "cweave.w" + +void finish_C(); + +/*:211*//*220:*/ +#line 4098 "cweave.w" + +void footnote(); + +/*:220*//*224:*/ +#line 4147 "cweave.w" + +void phase_three(); + +/*:224*//*236:*/ +#line 4327 "cweave.w" + +void unbucket(); + +/*:236*//*245:*/ +#line 4447 "cweave.w" + +void section_print(); + +/*:245*/ +#line 71 "cweave.w" + + +/*:1*//*3:*/ +#line 93 "cweave.w" + +int main(ac,av) +int ac; +char**av; +{ +argc= ac;argv= av; +program= cweave; +make_xrefs= force_lines= 1; +common_init(); +/*20:*/ +#line 243 "cweave.w" + +xref_ptr= xmem;name_dir->xref= (char*)xmem;xref_switch= 0;section_xref_switch= 0; +xmem->num= 0; + +/*:20*//*26:*/ +#line 352 "cweave.w" + +tok_ptr= tok_mem+1;text_ptr= tok_start+1;tok_start[0]= tok_mem+1; +tok_start[1]= tok_mem+1; +max_tok_ptr= tok_mem+1;max_text_ptr= tok_start+1; + +/*:26*//*32:*/ +#line 524 "cweave.w" + +{int c;for(c= 0;c<256;c++)ccode[c]= 0;} +ccode[' ']= ccode['\t']= ccode['\n']= ccode['\v']= ccode['\r']= ccode['\f'] += ccode['*']= new_section; +ccode['@']= '@'; +ccode['=']= verbatim; +ccode['d']= ccode['D']= definition; +ccode['f']= ccode['F']= ccode['s']= ccode['S']= format_code; +ccode['c']= ccode['C']= ccode['p']= ccode['P']= begin_C; +ccode['t']= ccode['T']= TeX_string; +ccode['l']= ccode['L']= translit_code; +ccode['q']= ccode['Q']= noop; +ccode['h']= ccode['H']= output_defs_code; +ccode['&']= join;ccode['<']= ccode['(']= section_name; +ccode['!']= underline;ccode['^']= xref_roman; +ccode[':']= xref_wildcard;ccode['.']= xref_typewriter;ccode[',']= thin_space; +ccode['|']= math_break;ccode['/']= line_break;ccode['#']= big_line_break; +#line 117 "cweave.ch" +ccode['+']= no_line_break;ccode[';']= pseudo_semi; +ccode['{']= pseudo_lbrace;ccode['}']= pseudo_rbrace; +ccode['[']= macro_arg_open;ccode[']']= macro_arg_close; +#line 543 "cweave.w" +ccode['\'']= ord; +/*33:*/ +#line 550 "cweave.w" + +ccode['0']= ccode['1']= ccode['2']= trace; + +/*:33*/ +#line 544 "cweave.w" + + +/*:32*//*52:*/ +#line 888 "cweave.w" +section_text[0]= ' '; + +/*:52*//*80:*/ +#line 1330 "cweave.w" + +#line 281 "cweave.ch" +out_ptr= out_buf+1;out_line= 1;active_file= tex_file; +if(alternative){ +*out_ptr= 'a'; +tex_printf("\\input cwebmac"); +}else{ +*out_ptr= 'c'; +tex_printf("\\input cwebma"); +} +#line 1333 "cweave.w" + +/*:80*//*82:*/ +#line 1356 "cweave.w" + +out_buf[0]= '\\'; + +/*:82*//*98:*/ +#line 1674 "cweave.w" + +for(cat_index= 0;cat_index<255;cat_index++) +strcpy(cat_name[cat_index],"UNKNOWN"); +strcpy(cat_name[exp],"exp"); +strcpy(cat_name[unop],"unop"); +strcpy(cat_name[binop],"binop"); +strcpy(cat_name[unorbinop],"unorbinop"); +strcpy(cat_name[cast],"cast"); +strcpy(cat_name[question],"?"); +strcpy(cat_name[lbrace],"{"); +strcpy(cat_name[rbrace],"}"); +strcpy(cat_name[decl_head],"decl_head"); +strcpy(cat_name[comma],","); +strcpy(cat_name[lpar],"("); +strcpy(cat_name[rpar],")"); +strcpy(cat_name[prelangle],"<"); +strcpy(cat_name[prerangle],">"); +strcpy(cat_name[langle],"\\<"); +strcpy(cat_name[colcol],"::"); +strcpy(cat_name[base],"\\:"); +strcpy(cat_name[decl],"decl"); +strcpy(cat_name[struct_head],"struct_head"); +strcpy(cat_name[stmt],"stmt"); +strcpy(cat_name[function],"function"); +strcpy(cat_name[fn_decl],"fn_decl"); +strcpy(cat_name[else_like],"else_like"); +strcpy(cat_name[semi],";"); +strcpy(cat_name[colon],":"); +strcpy(cat_name[tag],"tag"); +strcpy(cat_name[if_head],"if_head"); +strcpy(cat_name[else_head],"else_head"); +strcpy(cat_name[if_clause],"if()"); +strcpy(cat_name[lproc],"#{"); +strcpy(cat_name[rproc],"#}"); +strcpy(cat_name[insert],"insert"); +strcpy(cat_name[section_scrap],"section"); +strcpy(cat_name[dead],"@d"); +strcpy(cat_name[public_like],"public"); +strcpy(cat_name[operator_like],"operator"); +strcpy(cat_name[new_like],"new"); +strcpy(cat_name[catch_like],"catch"); +strcpy(cat_name[for_like],"for"); +strcpy(cat_name[do_like],"do"); +strcpy(cat_name[if_like],"if"); +strcpy(cat_name[raw_rpar],")?"); +strcpy(cat_name[raw_unorbin],"unorbinop?"); +strcpy(cat_name[const_like],"const"); +strcpy(cat_name[raw_int],"raw"); +strcpy(cat_name[int_like],"int"); +strcpy(cat_name[case_like],"case"); +strcpy(cat_name[sizeof_like],"sizeof"); +strcpy(cat_name[struct_like],"struct"); +strcpy(cat_name[typedef_like],"typedef"); +strcpy(cat_name[define_like],"define"); +strcpy(cat_name[begin_arg],"@["); +#line 362 "cweave.ch" +strcpy(cat_name[end_arg],"@]"); +strcpy(cat_name[label],"label"); +strcpy(cat_name[do_head],"do_head"); +strcpy(cat_name[pp_space],"#_"); +#line 1730 "cweave.w" +strcpy(cat_name[0],"zero"); + +/*:98*//*105:*/ +#line 2035 "cweave.w" + +scrap_base= scrap_info+1; +max_scr_ptr= scrap_ptr= scrap_info; + +/*:105*//*187:*/ +#line 3432 "cweave.w" + +max_stack_ptr= stack; + +/*:187*//*233:*/ +#line 4273 "cweave.w" + +max_sort_ptr= scrap_info; + +/*:233*//*235:*/ +#line 4292 "cweave.w" + +collate[0]= 0; +strcpy(collate+1," \1\2\3\4\5\6\7\10\11\12\13\14\15\16\17"); + +strcpy(collate+17,"\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37"); + +strcpy(collate+33,"!\42#$%&'()*+,-./:;<=>?@[\\]^`{|}~_"); + +strcpy(collate+65,"abcdefghijklmnopqrstuvwxyz0123456789"); + +strcpy(collate+101,"\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217"); + +strcpy(collate+117,"\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237"); + +strcpy(collate+133,"\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257"); + +strcpy(collate+149,"\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277"); + +strcpy(collate+165,"\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317"); + +strcpy(collate+181,"\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337"); + +strcpy(collate+197,"\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357"); + +strcpy(collate+213,"\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377"); + + +/*:235*/ +#line 102 "cweave.w" +; +if(show_banner)printf(banner); +/*28:*/ +#line 392 "cweave.w" + +id_lookup("asm",NULL,sizeof_like); +id_lookup("auto",NULL,int_like); +id_lookup("break",NULL,case_like); +id_lookup("case",NULL,case_like); +id_lookup("catch",NULL,catch_like); +id_lookup("char",NULL,raw_int); +id_lookup("class",NULL,struct_like); +id_lookup("clock_t",NULL,raw_int); +id_lookup("const",NULL,const_like); +id_lookup("continue",NULL,case_like); +id_lookup("default",NULL,case_like); +id_lookup("define",NULL,define_like); +id_lookup("defined",NULL,sizeof_like); +id_lookup("delete",NULL,sizeof_like); +id_lookup("div_t",NULL,raw_int); +id_lookup("do",NULL,do_like); +id_lookup("double",NULL,raw_int); +id_lookup("elif",NULL,if_like); +id_lookup("else",NULL,else_like); +id_lookup("endif",NULL,if_like); +id_lookup("enum",NULL,struct_like); +id_lookup("error",NULL,if_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); +id_lookup("fpos_t",NULL,raw_int); +id_lookup("friend",NULL,int_like); +id_lookup("goto",NULL,case_like); +id_lookup("if",NULL,if_like); +id_lookup("ifdef",NULL,if_like); +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); +id_lookup("long",NULL,raw_int); +id_lookup("new",NULL,new_like); +id_lookup("NULL",NULL,quoted); +id_lookup("offsetof",NULL,sizeof_like); +id_lookup("operator",NULL,operator_like); +id_lookup("pragma",NULL,if_like); +id_lookup("private",NULL,public_like); +id_lookup("protected",NULL,public_like); +id_lookup("ptrdiff_t",NULL,raw_int); +id_lookup("public",NULL,public_like); +id_lookup("register",NULL,int_like); +id_lookup("return",NULL,case_like); +id_lookup("short",NULL,raw_int); +id_lookup("sig_atomic_t",NULL,raw_int); +id_lookup("signed",NULL,raw_int); +id_lookup("size_t",NULL,raw_int); +id_lookup("sizeof",NULL,sizeof_like); +id_lookup("static",NULL,int_like); +id_lookup("struct",NULL,struct_like); +id_lookup("switch",NULL,for_like); +id_lookup("template",NULL,int_like); +id_lookup("TeX",NULL,custom); +id_lookup("this",NULL,quoted); +id_lookup("throw",NULL,case_like); +id_lookup("time_t",NULL,raw_int); +id_lookup("try",NULL,else_like); +id_lookup("typedef",NULL,typedef_like); +id_lookup("undef",NULL,if_like); +id_lookup("union",NULL,struct_like); +id_lookup("unsigned",NULL,raw_int); +id_lookup("va_dcl",NULL,decl); +id_lookup("va_list",NULL,raw_int); +id_lookup("virtual",NULL,int_like); +id_lookup("void",NULL,raw_int); +id_lookup("volatile",NULL,const_like); +id_lookup("wchar_t",NULL,raw_int); +id_lookup("while",NULL,for_like); + +/*:28*/ +#line 104 "cweave.w" +; +phase_one(); +phase_two(); +phase_three(); +return wrap_up(); +} + +/*:3*//*21:*/ +#line 262 "cweave.w" + +void +new_xref(p) +name_pointer p; +{ +xref_pointer q; +sixteen_bits m,n; +if(no_xref)return; +if((unindexed(p)||is_tiny(p))&&xref_switch==0)return; +m= section_count+xref_switch;xref_switch= 0;q= (xref_pointer)p->xref; +if(q!=xmem){ +n= q->num; +if(n==m||n==m+def_flag)return; +else if(m==n+def_flag){ +q->num= m;return; +} +} +append_xref(m);xref_ptr->xlink= q;p->xref= (char*)xref_ptr; +} + +/*:21*//*22:*/ +#line 293 "cweave.w" + +void +new_section_xref(p) +name_pointer p; +{ +xref_pointer q,r; +q= (xref_pointer)p->xref;r= xmem; +if(q>xmem) +while(q->num>section_xref_switch){r= q;q= q->xlink;} +if(r->num==section_count+section_xref_switch) +return; +append_xref(section_count+section_xref_switch); +xref_ptr->xlink= q;section_xref_switch= 0; +if(r==xmem)p->xref= (char*)xref_ptr; +else r->xlink= xref_ptr; +} + +/*:22*//*23:*/ +#line 313 "cweave.w" + +void +set_file_flag(p) +name_pointer p; +{ +xref_pointer q; +q= (xref_pointer)p->xref; +if(q->num==file_flag)return; +append_xref(file_flag); +xref_ptr->xlink= q; +p->xref= (char*)xref_ptr; +} + +/*:23*//*27:*/ +#line 358 "cweave.w" + +int names_match(p,first,l,t) +name_pointer p; +char*first; +int l; +eight_bits t; +{ +if(length(p)!=l)return 0; +if(p->ilk!=t&&!(t==normal&&abnormal(p)))return 0; +return!strncmp(first,p->byte_start,l); +} + +void +init_p(p,t) +name_pointer p; +eight_bits t; +{ +p->ilk= t;p->xref= (char*)xmem; +} + +void +init_node(p) +name_pointer p; +{ +p->xref= (char*)xmem; +} + +/*:27*//*35:*/ +#line 565 "cweave.w" + +void +skip_limbo(){ +while(1){ +if(loc>limit&&get_line()==0)return; +*(limit+1)= '@'; +while(*loc!='@')loc++; +if(loc++<=limit){int c= ccode[(eight_bits)*loc++]; +if(c==new_section)return; +if(c==noop)skip_restricted(); +else if(c==format_code)/*71:*/ +#line 1180 "cweave.w" + +{ +if(get_next()!=identifier) +err_print("! Missing left identifier of @s"); + +else{ +lhs= id_lookup(id_first,id_loc,normal); +if(get_next()!=identifier) +err_print("! Missing right identifier of @s"); + +else{ +rhs= id_lookup(id_first,id_loc,normal); +lhs->ilk= rhs->ilk; +} +} +} + +/*:71*/ +#line 575 "cweave.w" +; +} +} +} + +/*:35*//*36:*/ +#line 587 "cweave.w" + +unsigned +skip_TeX() +{ +while(1){ +if(loc>limit&&get_line()==0)return(new_section); +*(limit+1)= '@'; +while(*loc!='@'&&*loc!='|')loc++; +if(*loc++=='|')return('|'); +if(loc<=limit)return(ccode[(eight_bits)*(loc++)]); +} +} + +/*:36*//*40:*/ +#line 658 "cweave.w" + +#line 155 "cweave.ch" +eight_bits +get_next() +{eight_bits c; +int blank_count= 0; + +if(loc<limit&&preprocessing&&(define_pos==2||define_pos==4)&& +xisspace(*loc)){ +define_pos= 5; +return(preproc_space); +} + +while(1){ +/*45:*/ +#line 717 "cweave.w" + +while(loc==limit-1&&preprocessing&&*loc=='\\') +if(get_line()==0)return(new_section); +#line 228 "cweave.ch" +if(loc>=limit&&preprocessing){ +preprocessing= sharp_include_line= define_pos= 0; +return(right_preproc); +} +#line 724 "cweave.w" + +/*:45*/ +#line 167 "cweave.ch" +; +if(loc>limit&&get_line()==0)return(new_section); + +if(loc==buffer){ +blank_count= 0; +while((*loc==' '||*loc=='\t')&&loc<limit){ +blank_count++;loc++; +} +} + +c= *(loc++); +if(xisdigit(c)||c=='\\'||c=='.')/*48:*/ +#line 775 "cweave.w" +{ +id_first= id_loc= section_text+1; +if(*(loc-1)=='\\'){*id_loc++= '~'; +while(xisdigit(*loc))*id_loc++= *loc++;} +else if(*(loc-1)=='0'){ +if(*loc=='x'||*loc=='X'){*id_loc++= '^';loc++; +while(xisxdigit(*loc))*id_loc++= *loc++;} +else if(xisdigit(*loc)){*id_loc++= '~'; +while(xisdigit(*loc))*id_loc++= *loc++;} +else goto dec; +} +else{ +if(*(loc-1)=='.'&&!xisdigit(*loc))goto mistake; +dec:*id_loc++= *(loc-1); +while(xisdigit(*loc)||*loc=='.')*id_loc++= *loc++; +if(*loc=='e'||*loc=='E'){ +*id_loc++= '_';loc++; +if(*loc=='+'||*loc=='-')*id_loc++= *loc++; +while(xisdigit(*loc))*id_loc++= *loc++; +} +} +while(*loc=='u'||*loc=='U'||*loc=='l'||*loc=='L' +||*loc=='f'||*loc=='F'){ +*id_loc++= '$';*id_loc++= toupper(*loc);loc++; +} +return(constant); +} + +/*:48*/ +#line 178 "cweave.ch" + +else if(c=='\''||c=='"'||(c=='L'&&(*loc=='\''||*loc=='"')) +||(c=='<'&&sharp_include_line==1)) +/*49:*/ +#line 808 "cweave.w" +{ +char delim= c; +id_first= section_text+1; +id_loc= section_text; +if(delim=='\''&&*(loc-2)=='@'){*++id_loc= '@';*++id_loc= '@';} +*++id_loc= delim; +if(delim=='L'){ +delim= *loc++;*++id_loc= delim; +} +if(delim=='<')delim= '>'; +while(1){ +if(loc>=limit){ +if(*(limit-1)!='\\'){ +err_print("! String didn't end");loc= limit;break; + +} +if(get_line()==0){ +err_print("! Input ended in middle of string");loc= buffer;break; + +} +} +if((c= *loc++)==delim){ +if(++id_loc<=section_text_end)*id_loc= c; +break; +} +if(c=='\\')if(loc>=limit)continue; +else if(++id_loc<=section_text_end){ +*id_loc= '\\';c= *loc++; +} +if(++id_loc<=section_text_end)*id_loc= c; +} +if(id_loc>=section_text_end){ +printf("\n! String too long: "); + +term_write(section_text+1,25); +printf("...");mark_error; +} +id_loc++; +return(string); +} + +/*:49*/ +#line 181 "cweave.ch" + +else if(xisalpha(c)||isxalpha(c)||ishigh(c)) +/*47:*/ +#line 758 "cweave.w" +{ +#line 250 "cweave.ch" +id_first= --loc; + +if(preprocessing){ +if(define_pos==1) +define_pos= 2; +else if(strncmp(loc,"define",6)==0&&xisspace(*(loc+6))) +define_pos= 1; +} + +while(isalpha(*++loc)||isdigit(*loc)||isxalpha(*loc)||ishigh(*loc)); +id_loc= loc;return(identifier); +#line 762 "cweave.w" +} + +/*:47*/ +#line 183 "cweave.ch" + +else if(c=='@')/*50:*/ +#line 852 "cweave.w" +{ +c= *loc++; +switch(ccode[(eight_bits)c]){ +case translit_code:err_print("! Use @l in limbo only");continue; + +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: +/*51:*/ +#line 872 "cweave.w" +{ +char*k; +cur_section_char= *(loc-1); +/*53:*/ +#line 890 "cweave.w" + +k= section_text; +while(1){ +if(loc>limit&&get_line()==0){ +err_print("! Input ended in section name"); + +loc= buffer+1;break; +} +c= *loc; +/*54:*/ +#line 914 "cweave.w" + +if(c=='@'){ +c= *(loc+1); +if(c=='>'){ +loc+= 2;break; +} +if(ccode[(eight_bits)c]==new_section){ +err_print("! Section name didn't end");break; + +} +if(c!='@'){ +err_print("! Control codes are forbidden in section name");break; + +} +*(++k)= '@';loc++; +} + +/*:54*/ +#line 899 "cweave.w" +; +loc++;if(k<section_text_end)k++; +if(xisspace(c)){ +c= ' ';if(*(k-1)==' ')k--; +} +*k= c; +} +if(k>=section_text_end){ +printf("\n! Section name too long: "); + +term_write(section_text+1,25); +printf("...");mark_harmless; +} +if(*k==' '&&k>section_text)k--; + +/*:53*/ +#line 875 "cweave.w" +; +if(k-section_text>3&&strncmp(k-2,"...",3)==0) +cur_section= section_lookup(section_text+1,k-3,1); +else cur_section= section_lookup(section_text+1,k,0); +xref_switch= 0;return(section_name); +} + +/*:51*/ +#line 862 "cweave.w" +; +case verbatim:/*57:*/ +#line 961 "cweave.w" +{ +id_first= loc++;*(limit+1)= '@';*(limit+2)= '>'; +while(*loc!='@'||*(loc+1)!='>')loc++; +if(loc>=limit)err_print("! Verbatim string didn't end"); + +id_loc= loc;loc+= 2; +return(verbatim); +} + +/*:57*/ +#line 863 "cweave.w" +; +case ord:/*49:*/ +#line 808 "cweave.w" +{ +char delim= c; +id_first= section_text+1; +id_loc= section_text; +if(delim=='\''&&*(loc-2)=='@'){*++id_loc= '@';*++id_loc= '@';} +*++id_loc= delim; +if(delim=='L'){ +delim= *loc++;*++id_loc= delim; +} +if(delim=='<')delim= '>'; +while(1){ +if(loc>=limit){ +if(*(limit-1)!='\\'){ +err_print("! String didn't end");loc= limit;break; + +} +if(get_line()==0){ +err_print("! Input ended in middle of string");loc= buffer;break; + +} +} +if((c= *loc++)==delim){ +if(++id_loc<=section_text_end)*id_loc= c; +break; +} +if(c=='\\')if(loc>=limit)continue; +else if(++id_loc<=section_text_end){ +*id_loc= '\\';c= *loc++; +} +if(++id_loc<=section_text_end)*id_loc= c; +} +if(id_loc>=section_text_end){ +printf("\n! String too long: "); + +term_write(section_text+1,25); +printf("...");mark_error; +} +id_loc++; +return(string); +} + +/*:49*/ +#line 864 "cweave.w" +; +default:return(ccode[(eight_bits)c]); +} +} + +/*:50*/ +#line 184 "cweave.ch" + +else if(xisspace(c))continue; +if(c=='#'&&loc==buffer+1+blank_count&&!preprocessing) +/*42:*/ +#line 696 "cweave.w" +{ +preprocessing= 1; +/*44:*/ +#line 709 "cweave.w" + +while(loc<=buffer_end-7&&xisspace(*loc))loc++; +if(loc<=buffer_end-6&&strncmp(loc,"include",7)==0)sharp_include_line= 1; + +/*:44*/ +#line 698 "cweave.w" +; +return(left_preproc); +} + +/*:42*/ +#line 187 "cweave.ch" +; +mistake:/*46:*/ +#line 734 "cweave.w" + +switch(c){ +case'/':if(*loc=='*'){compress(begin_comment);} +else if(*loc=='/')compress(begin_short_comment);break; +case'+':if(*loc=='+')compress(plus_plus);break; +case'-':if(*loc=='-'){compress(minus_minus);} +else if(*loc=='>')if(*(loc+1)=='*'){loc++;compress(minus_gt_ast);} +else compress(minus_gt);break; +case'.':if(*loc=='*'){compress(period_ast);} +else if(*loc=='.'&&*(loc+1)=='.'){ +loc++;compress(dot_dot_dot); +} +break; +case':':if(*loc==':')compress(colon_colon);break; +case'=':if(*loc=='=')compress(eq_eq);break; +case'>':if(*loc=='='){compress(gt_eq);} +else if(*loc=='>')compress(gt_gt);break; +case'<':if(*loc=='='){compress(lt_eq);} +else if(*loc=='<')compress(lt_lt);break; +case'&':if(*loc=='&')compress(and_and);break; +case'|':if(*loc=='|')compress(or_or);break; +#line 239 "cweave.ch" +case'!':if(*loc=='=')compress(not_eq);break; +case'#':if(*loc=='#')compress(numb_numb);break; +#line 756 "cweave.w" +} + +/*:46*/ +#line 188 "cweave.ch" + +if(preprocessing){ +if(c=='('&&define_pos==2&&!xisspace(*(loc-2))) +define_pos= 3; +else if(c==')'&&define_pos==3) +define_pos= 4; +} +return(c); +} +} +#line 679 "cweave.w" + +/*:40*//*56:*/ +#line 936 "cweave.w" + +void +skip_restricted() +{ +id_first= loc;*(limit+1)= '@'; +false_alarm: +while(*loc!='@')loc++; +id_loc= loc; +if(loc++>limit){ +err_print("! Control text didn't end");loc= limit; + +} +else{ +if(*loc=='@'&&loc<=limit){loc++;goto false_alarm;} +if(*loc++!='>') +err_print("! Control codes are forbidden in control text"); + +} +} + +/*:56*//*60:*/ +#line 989 "cweave.w" + +void +phase_one(){ +phase= 1;reset_input();section_count= 0; +skip_limbo();change_exists= 0; +while(!input_has_ended) +/*61:*/ +#line 1002 "cweave.w" + +{ +if(++section_count==max_sections)overflow("section number"); +changed_section[section_count]= changing; + +if(*(loc-1)=='*'&&show_progress){ +printf("*%d",section_count); +update_terminal; +} +/*66:*/ +#line 1093 "cweave.w" + +while(1){ +switch(next_control= skip_TeX()){ +case translit_code:err_print("! Use @l in limbo only");continue; + +case underline:xref_switch= def_flag;continue; +case trace:tracing= *(loc-1)-'0';continue; +case'|':C_xref(section_name);break; +case xref_roman:case xref_wildcard:case xref_typewriter: +case noop:case section_name: +loc-= 2;next_control= get_next(); +if(next_control>=xref_roman&&next_control<=xref_typewriter){ +/*67:*/ +#line 1113 "cweave.w" + +{ +char*src= id_first,*dst= id_first; +while(src<id_loc){ +if(*src=='@')src++; +*dst++= *src++; +} +id_loc= dst; +while(dst<src)*dst++= ' '; +} + +/*:67*/ +#line 1105 "cweave.w" + +new_xref(id_lookup(id_first,id_loc,next_control-identifier)); +} +break; +} +if(next_control>=format_code)break; +} + +/*:66*/ +#line 1011 "cweave.w" +; +/*69:*/ +#line 1141 "cweave.w" + +while(next_control<=definition){ +if(next_control==definition){ +xref_switch= def_flag; +next_control= get_next(); +}else/*70:*/ +#line 1155 "cweave.w" +{ +next_control= get_next(); +if(next_control==identifier){ +lhs= id_lookup(id_first,id_loc,normal);lhs->ilk= normal; +if(xref_switch)new_xref(lhs); +next_control= get_next(); +if(next_control==identifier){ +rhs= id_lookup(id_first,id_loc,normal); +lhs->ilk= rhs->ilk; +if(unindexed(lhs)){ +xref_pointer q,r= NULL; +for(q= (xref_pointer)lhs->xref;q>xmem;q= q->xlink) +if(q->num<def_flag) +if(r)r->xlink= q->xlink; +else lhs->xref= (char*)q->xlink; +else r= q; +} +next_control= get_next(); +} +} +} + +/*:70*/ +#line 1146 "cweave.w" +; +outer_xref(); +} + +/*:69*/ +#line 1012 "cweave.w" +; +/*72:*/ +#line 1200 "cweave.w" + +if(next_control<=section_name){ +if(next_control==begin_C)section_xref_switch= 0; +else{ +section_xref_switch= def_flag; +if(cur_section_char=='('&&cur_section!=name_dir) +set_file_flag(cur_section); +} +do{ +if(next_control==section_name&&cur_section!=name_dir) +new_section_xref(cur_section); +next_control= get_next();outer_xref(); +}while(next_control<=section_name); +} + +/*:72*/ +#line 1013 "cweave.w" +; +if(changed_section[section_count])change_exists= 1; +} + +/*:61*/ +#line 995 "cweave.w" +; +changed_section[section_count]= change_exists; + +phase= 2; +/*76:*/ +#line 1253 "cweave.w" +section_check(root) + +/*:76*/ +#line 999 "cweave.w" +; +} + +/*:60*//*63:*/ +#line 1041 "cweave.w" + +void +C_xref(spec_ctrl) +eight_bits spec_ctrl; +{ +name_pointer p; +while(next_control<format_code||next_control==spec_ctrl){ +if(next_control>=identifier&&next_control<=xref_typewriter){ +if(next_control>identifier)/*67:*/ +#line 1113 "cweave.w" + +{ +char*src= id_first,*dst= id_first; +while(src<id_loc){ +if(*src=='@')src++; +*dst++= *src++; +} +id_loc= dst; +while(dst<src)*dst++= ' '; +} + +/*:67*/ +#line 1049 "cweave.w" + +p= id_lookup(id_first,id_loc,next_control-identifier);new_xref(p); +} +if(next_control==section_name){ +section_xref_switch= cite_flag; +new_section_xref(cur_section); +} +next_control= get_next(); +if(next_control=='|'||next_control==begin_comment|| +next_control==begin_short_comment)return; +} +} + +/*:63*//*65:*/ +#line 1069 "cweave.w" + +void +outer_xref() +{ +int bal; +while(next_control<format_code) +if(next_control!=begin_comment&&next_control!=begin_short_comment) +C_xref(ignore); +else{ +boolean is_long_comment= (next_control==begin_comment); +bal= copy_comment(is_long_comment,1);next_control= '|'; +while(bal>0){ +C_xref(section_name); +if(next_control=='|')bal= copy_comment(is_long_comment,bal); +else bal= 0; +} +} +} + +/*:65*//*75:*/ +#line 1230 "cweave.w" + +void +section_check(p) +name_pointer p; +{ +if(p){ +section_check(p->llink); +cur_xref= (xref_pointer)p->xref; +if(cur_xref->num==file_flag){an_output= 1;cur_xref= cur_xref->xlink;} +else an_output= 0; +if(cur_xref->num<def_flag){ +printf("\n! Never defined: <");print_section_name(p);putchar('>');mark_harmless; + +} +while(cur_xref->num>=cite_flag)cur_xref= cur_xref->xlink; +if(cur_xref==xmem&&!an_output){ +printf("\n! Never used: <");print_section_name(p);putchar('>');mark_harmless; + +} +section_check(p->rlink); +} +} + +/*:75*//*78:*/ +#line 1283 "cweave.w" + +void +flush_buffer(b,per_cent,carryover) +char*b; +boolean per_cent,carryover; +{ +char*j;j= b; +if(!per_cent) +while(j>out_buf&&*j==' ')j--; +c_line_write(j-out_buf); +if(per_cent)tex_putc('%'); +tex_new_line;out_line++; +if(carryover) +while(j>out_buf) +if(*j--=='%'&&(j==out_buf||*j!='\\')){ +*b--= '%';break; +} +if(b<out_ptr)strncpy(out_buf+1,b+1,out_ptr-b); +out_ptr-= b-out_buf; +} + +/*:78*//*79:*/ +#line 1312 "cweave.w" + +void +finish_line() +{ +char*k; +if(out_ptr>out_buf)flush_buffer(out_ptr,0,0); +else{ +for(k= buffer;k<=limit;k++) +if(!(xisspace(*k)))return; +flush_buffer(out_buf,0,0); +} +} + +/*:79*//*81:*/ +#line 298 "cweave.ch" + +void +out(c) +char c; +{ +if(out_ptr>=out_buf_end) +break_out(); + +if(!escape){ +if(in_math){ +if(c=='{') +math_brace_count++; +else if(c=='}'){ +math_brace_count--; +if(math_brace_count<0){ +math_brace_count= 0; +return; +} +} +} +if(c=='$'){ +while(math_brace_count>0){ +*(++out_ptr)= '}'; +math_brace_count--; +} +in_math= 1-in_math; +} +} + +if(c=='\\'&&!escape) +escape= 1; +else +escape= 0; + +*(++out_ptr)= c; +return; +} +#line 1345 "cweave.w" +void +out_str(s) +char*s; +{ +while(*s)out(*s++); +} + +/*:81*//*84:*/ +#line 1366 "cweave.w" + +void +break_out() +{ +char*k= out_ptr; +while(1){ +if(k==out_buf)/*85:*/ +#line 1387 "cweave.w" + +{ +printf("\n! Line had to be broken (output l. %d):\n",out_line); + +term_write(out_buf+1,out_ptr-out_buf-1); +new_line;mark_harmless; +flush_buffer(out_ptr-1,1,1);return; +} + +/*:85*/ +#line 1372 "cweave.w" +; +if(*k==' '){ +flush_buffer(k,0,1);return; +} +if(*(k--)=='\\'&&*k!='\\'){ +flush_buffer(k,1,1);return; +} +} +} + +/*:84*//*86:*/ +#line 1401 "cweave.w" + +void +out_section(n) +sixteen_bits n; +{ +char s[6]; +sprintf(s,"%d",n);out_str(s); +if(changed_section[n])out_str("\\*"); + +} + +/*:86*//*87:*/ +#line 1415 "cweave.w" + +void +out_name(p) +name_pointer p; +{ +char*k,*k_end= (p+1)->byte_start; +out('{'); +for(k= p->byte_start;k<k_end;k++){ +if(isxalpha(*k))out('\\'); +out(*k); +} +out('}'); +} + +/*:87*//*88:*/ +#line 1442 "cweave.w" + +void +copy_limbo() +{ +char c; +while(1){ +if(loc>limit&&(finish_line(),get_line()==0))return; +*(limit+1)= '@'; +while(*loc!='@')out(*(loc++)); +if(loc++<=limit){ +c= *loc++; +if(ccode[(eight_bits)c]==new_section)break; +switch(ccode[(eight_bits)c]){ +case translit_code:out_str("\\ATL");break; + +case'@':out('@');break; +case noop:skip_restricted();break; +case format_code:if(get_next()==identifier)get_next(); +if(loc>=limit)get_line(); +break; +default:err_print("! Double @ should be used in limbo"); + +out('@'); +} +} +} +} + +/*:88*//*90:*/ +#line 1477 "cweave.w" + +eight_bits +copy_TeX() +{ +char c; +while(1){ +if(loc>limit&&(finish_line(),get_line()==0))return(new_section); +*(limit+1)= '@'; +while((c= *(loc++))!='|'&&c!='@'){ +out(c); +if(out_ptr==out_buf+1&&(xisspace(c)))out_ptr--; +} +if(c=='|')return('|'); +if(loc<=limit)return(ccode[(eight_bits)*(loc++)]); +} +} + +/*:90*//*92:*/ +#line 1509 "cweave.w" + +int copy_comment(is_long_comment,bal) +boolean is_long_comment; +int bal; +{ +char c; +while(1){ +if(loc>limit){ +if(is_long_comment){ +if(get_line()==0){ +err_print("! Input ended in mid-comment"); + +loc= buffer+1;goto done; +} +} +else{ +if(bal>1)err_print("! Missing } in comment"); + +goto done; +} +} +c= *(loc++); +if(c=='|')return(bal); +if(is_long_comment)/*93:*/ +#line 1550 "cweave.w" + +if(c=='*'&&*loc=='/'){ +loc++; +if(bal>1)err_print("! Missing } in comment"); + +goto done; +} + +/*:93*/ +#line 1532 "cweave.w" +; +if(phase==2){ +if(ishigh(c))app_tok(quoted_char); +app_tok(c); +} +/*94:*/ +#line 1558 "cweave.w" + +if(c=='@'){ +if(*(loc++)!='@'){ +err_print("! Illegal use of @ in comment"); + +loc-= 2;if(phase==2)*(tok_ptr-1)= ' ';goto done; +} +} +else if(c=='\\'&&*loc!='@') +if(phase==2)app_tok(*(loc++))else loc++; + +/*:94*/ +#line 1537 "cweave.w" +; +if(c=='{')bal++; +else if(c=='}'){ +if(bal>1)bal--; +else{err_print("! Extra } in comment"); + +if(phase==2)tok_ptr--; +} +} +} +done:/*95:*/ +#line 1572 "cweave.w" + +if(phase==2)while(bal-->0)app_tok('}'); +return(0); + +/*:95*/ +#line 1547 "cweave.w" +; +} + +/*:92*//*99:*/ +#line 1734 "cweave.w" + +void +print_cat(c) +eight_bits c; +{ +printf(cat_name[c]); +} + +/*:99*//*106:*/ +#line 2062 "cweave.w" + +void +print_text(p) +text_pointer p; +{ +token_pointer j; +sixteen_bits r; +if(p>=text_ptr)printf("BAD"); +else for(j= *p;j<*(p+1);j++){ +r= *j%id_flag; +switch(*j/id_flag){ +case 1:printf("\\\\{");print_id((name_dir+r));printf("}"); +break; +case 2:printf("\\&{");print_id((name_dir+r));printf("}"); +break; +case 3:printf("<");print_section_name((name_dir+r));printf(">"); +break; +case 4:printf("[[%d]]",r);break; +case 5:printf("|[[%d]]|",r);break; +default:/*107:*/ +#line 2087 "cweave.w" + +switch(r){ +case math_rel:printf("\\mathrel{");break; +case big_cancel:printf("[ccancel]");break; +case cancel:printf("[cancel]");break; +case indent:printf("[indent]");break; +case outdent:printf("[outdent]");break; +case backup:printf("[backup]");break; +case opt:printf("[opt]");break; +case break_space:printf("[break]");break; +case force:printf("[force]");break; +case big_force:printf("[fforce]");break; +case preproc_line:printf("[preproc]");break; +case quoted_char:j++;printf("[%o]",(unsigned)*j);break; +case end_translation:printf("[quit]");break; +case inserted:printf("[inserted]");break; +default:putxchar(r); +} + +/*:107*/ +#line 2081 "cweave.w" +; +} +} +fflush(stdout); +} + +/*:106*//*109:*/ +#line 2187 "cweave.w" + +void +app_str(s) +char*s; +{ +while(*s)app_tok(*(s++)); +} + +void +big_app(a) +token a; +{ +if(a==' '||(a>=big_cancel&&a<=big_force)){ +if(cur_mathness==maybe_math)init_mathness= no_math; +else if(cur_mathness==yes_math)app_str("{}$"); +cur_mathness= no_math; +} +else{ +if(cur_mathness==maybe_math)init_mathness= yes_math; +else if(cur_mathness==no_math)app_str("${}"); +cur_mathness= yes_math; +} +app(a); +} + +void +big_app1(a) +scrap_pointer a; +{ +switch(a->mathness%4){ +case(no_math): +if(cur_mathness==maybe_math)init_mathness= no_math; +else if(cur_mathness==yes_math)app_str("{}$"); +cur_mathness= a->mathness/4; +break; +case(yes_math): +if(cur_mathness==maybe_math)init_mathness= yes_math; +else if(cur_mathness==no_math)app_str("${}"); +cur_mathness= a->mathness/4; +break; +case(maybe_math):break; +} +app(tok_flag+(int)((a)->trans-tok_start)); +} + +/*:109*//*111:*/ +#line 2312 "cweave.w" + +token_pointer +find_first_ident(p) +text_pointer p; +{ +token_pointer q; +token_pointer j; +sixteen_bits r; +if(p>=text_ptr)confusion("find_first_ident"); +for(j= *p;j<*(p+1);j++){ +r= *j%id_flag; +switch(*j/id_flag){ +case 1:case 2:return j; +case 4:case 5: +if((q= find_first_ident(tok_start+r))!=no_ident_found) +return q; +default:; +if(*j==inserted)return no_ident_found; +} +} +return no_ident_found; +} + +/*:111*//*112:*/ +#line 2339 "cweave.w" + +#line 421 "cweave.ch" +void +make_reserved(p,cat) +scrap_pointer p; +eight_bits cat; +{ +sixteen_bits tok_value; +token_pointer tok_loc; +if((tok_loc= find_first_ident(p->trans))==no_ident_found) +return; +tok_value= *tok_loc; +for(;p<=scrap_ptr;p==lo_ptr?p= hi_ptr:p++){ +if(p->cat==exp||p->cat==int_like||p->cat==const_like){ +if(**(p->trans)==tok_value){ +p->cat= cat; +**(p->trans)= tok_value%id_flag+res_flag; +} +} +} +(name_dir+(sixteen_bits)(tok_value%id_flag))->ilk= cat; +*tok_loc= tok_value%id_flag+res_flag; +} +#line 2360 "cweave.w" + +/*:112*//*113:*/ +#line 2370 "cweave.w" + +void +make_underlined(p) + +scrap_pointer p; +{ +token_pointer tok_loc; +if((tok_loc= find_first_ident(p->trans))==no_ident_found) +return; +xref_switch= def_flag; +underline_xref(*tok_loc%id_flag+name_dir); +} + +/*:113*//*115:*/ +#line 2391 "cweave.w" + +void +underline_xref(p) +name_pointer p; +{ +xref_pointer q= (xref_pointer)p->xref; +xref_pointer r; +sixteen_bits m; +sixteen_bits n; +if(no_xref)return; +m= section_count+xref_switch; +while(q!=xmem){ +n= q->num; +if(n==m)return; +else if(m==n+def_flag){ +q->num= m;return; +} +else if(n>=def_flag&&n<m)break; +q= q->xlink; +} +/*116:*/ +#line 2421 "cweave.w" + +append_xref(0); +xref_ptr->xlink= (xref_pointer)p->xref;r= xref_ptr; +p->xref= (char*)xref_ptr; +while(r->xlink!=q){r->num= r->xlink->num;r= r->xlink;} +r->num= m; + +/*:116*/ +#line 2411 "cweave.w" +; +} + +/*:115*//*162:*/ +#line 2868 "cweave.w" + +void +reduce(j,k,c,d,n) +scrap_pointer j; +eight_bits c; +short k,d,n; +{ +scrap_pointer i,i1; +j->cat= c;j->trans= text_ptr; +j->mathness= 4*cur_mathness+init_mathness; +freeze_text; +if(k>1){ +for(i= j+k,i1= j+1;i<=lo_ptr;i++,i1++){ +i1->cat= i->cat;i1->trans= i->trans; +i1->mathness= i->mathness; +} +lo_ptr= lo_ptr-k+1; +} +/*163:*/ +#line 2891 "cweave.w" + +if(pp+d>=scrap_base)pp= pp+d; +else pp= scrap_base; + +/*:163*/ +#line 2886 "cweave.w" +; +/*168:*/ +#line 2965 "cweave.w" + +{scrap_pointer k; +#line 806 "cweave.ch" +if(tracing==2){ +printf("\n%3d:",n); +#line 2969 "cweave.w" +for(k= scrap_base;k<=lo_ptr;k++){ +if(k==pp)putxchar('*');else putxchar(' '); +if(k->mathness%4==yes_math)putchar('+'); +else if(k->mathness%4==no_math)putchar('-'); +print_cat(k->cat); +if(k->mathness/4==yes_math)putchar('+'); +else if(k->mathness/4==no_math)putchar('-'); +} +if(hi_ptr<=scrap_ptr)printf("..."); +} +} + +/*:168*/ +#line 2887 "cweave.w" +; +pp--; +} + +/*:162*//*164:*/ +#line 2898 "cweave.w" + +void +squash(j,k,c,d,n) +scrap_pointer j; +eight_bits c; +short k,d,n; +{ +scrap_pointer i; +if(k==1){ +j->cat= c;/*163:*/ +#line 2891 "cweave.w" + +if(pp+d>=scrap_base)pp= pp+d; +else pp= scrap_base; + +/*:163*/ +#line 2907 "cweave.w" +; +/*168:*/ +#line 2965 "cweave.w" + +{scrap_pointer k; +#line 806 "cweave.ch" +if(tracing==2){ +printf("\n%3d:",n); +#line 2969 "cweave.w" +for(k= scrap_base;k<=lo_ptr;k++){ +if(k==pp)putxchar('*');else putxchar(' '); +if(k->mathness%4==yes_math)putchar('+'); +else if(k->mathness%4==no_math)putchar('-'); +print_cat(k->cat); +if(k->mathness/4==yes_math)putchar('+'); +else if(k->mathness/4==no_math)putchar('-'); +} +if(hi_ptr<=scrap_ptr)printf("..."); +} +} + +/*:168*/ +#line 2908 "cweave.w" +; +pp--; +return; +} +for(i= j;i<j+k;i++)big_app1(i); +reduce(j,k,c,d,n); +} + +/*:164*//*169:*/ +#line 2994 "cweave.w" + +text_pointer +translate() +{ +scrap_pointer i, +j; +pp= scrap_base;lo_ptr= pp-1;hi_ptr= pp; +/*172:*/ +#line 3033 "cweave.w" + +if(tracing==2){ +printf("\nTracing after l. %d:\n",cur_line);mark_harmless; + +if(loc>buffer+50){ +printf("..."); +term_write(loc-51,51); +} +else term_write(buffer,loc-buffer); +} + +/*:172*/ +#line 3001 "cweave.w" +; +/*165:*/ +#line 2929 "cweave.w" + +while(1){ +/*166:*/ +#line 2948 "cweave.w" + +if(lo_ptr<pp+3){ +while(hi_ptr<=scrap_ptr&&lo_ptr!=pp+3){ +(++lo_ptr)->cat= hi_ptr->cat;lo_ptr->mathness= (hi_ptr)->mathness; +lo_ptr->trans= (hi_ptr++)->trans; +} +for(i= lo_ptr+1;i<=pp+3;i++)i->cat= 0; +} + +/*:166*/ +#line 2931 "cweave.w" +; +if(tok_ptr+safe_tok_incr>tok_mem_end){ +if(tok_ptr>max_tok_ptr)max_tok_ptr= tok_ptr; +overflow("token"); +} +if(text_ptr+safe_text_incr>tok_start_end){ +if(text_ptr>max_text_ptr)max_text_ptr= text_ptr; +overflow("text"); +} +if(pp>lo_ptr)break; +init_mathness= cur_mathness= maybe_math; +/*110:*/ +#line 2243 "cweave.w" +{ +if(cat1==end_arg&&lhs_not_simple) +if(pp->cat==begin_arg)squash(pp,2,exp,-2,110); +else squash(pp,2,end_arg,-1,111); +else if(cat1==insert)squash(pp,2,pp->cat,-2,0); +else if(cat2==insert)squash(pp+1,2,(pp+1)->cat,-1,0); +else if(cat3==insert)squash(pp+2,2,(pp+2)->cat,0,0); +else +switch(pp->cat){ +case exp:/*117:*/ +#line 2433 "cweave.w" + +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); +else if((cat1==binop||cat1==unorbinop)&&cat2==exp) +squash(pp,3,exp,-2,3); +else if(cat1==comma&&cat2==exp){ +big_app2(pp); +app(opt);app('9');big_app1(pp+2);reduce(pp,3,exp,-2,4); +} +else if(cat1==exp||cat1==cast)squash(pp,2,exp,-2,5); +else if(cat1==semi)squash(pp,2,stmt,-1,6); +else if(cat1==colon){ +#line 449 "cweave.ch" +make_underlined(pp); +if(alternative) +squash(pp,2,label,0,7); +else +squash(pp,2,tag,0,7); +#line 2449 "cweave.w" +} +else if(cat1==base){ +if(cat2==int_like&&cat3==comma){ +big_app1(pp+1);big_app(' ');big_app2(pp+2); +app(opt);app('9');reduce(pp+1,3,base,0,8); +} +else if(cat2==int_like&&cat3==lbrace){ +big_app1(pp);big_app(' ');big_app1(pp+1);big_app(' ');big_app1(pp+2); +reduce(pp,3,exp,-1,9); +} +} +else if(cat1==rbrace)squash(pp,1,stmt,-1,10); + +/*:117*/ +#line 2252 "cweave.w" +;break; +case lpar:/*118:*/ +#line 2462 "cweave.w" + +if((cat1==exp||cat1==unorbinop)&&cat2==rpar)squash(pp,3,exp,-2,11); +else if(cat1==rpar){ +big_app1(pp);app('\\');app(',');big_app1(pp+1); + +reduce(pp,2,exp,-2,12); +} +else if(cat1==decl_head||cat1==int_like||cat1==exp){ +if(cat2==rpar)squash(pp,3,cast,-2,13); +else if(cat2==comma){ +big_app3(pp);app(opt);app('9');reduce(pp,3,lpar,0,14); +} +} +else if(cat1==stmt||cat1==decl){ +big_app2(pp);big_app(' ');reduce(pp,2,lpar,0,15); +} + +/*:118*/ +#line 2253 "cweave.w" +;break; +case question:/*119:*/ +#line 2479 "cweave.w" + +if(cat1==exp&&cat2==colon)squash(pp,3,binop,-2,16); + +/*:119*/ +#line 2254 "cweave.w" +;break; +case unop:/*120:*/ +#line 2482 "cweave.w" + +if(cat1==exp||cat1==int_like)squash(pp,2,cat1,-2,17); + +/*:120*/ +#line 2255 "cweave.w" +;break; +case unorbinop:/*121:*/ +#line 2485 "cweave.w" + +if(cat1==exp||cat1==int_like){ +big_app('{');big_app1(pp);big_app('}');big_app1(pp+1); +reduce(pp,2,cat1,-2,18); +} +else if(cat1==binop){ +big_app(math_rel);big_app1(pp);big_app('{');big_app1(pp+1);big_app('}'); +big_app('}');reduce(pp,2,binop,-1,19); +} + +/*:121*/ +#line 2256 "cweave.w" +;break; +case binop:/*122:*/ +#line 2495 "cweave.w" + +if(cat1==binop){ +big_app(math_rel);big_app('{');big_app1(pp);big_app('}'); +big_app('{');big_app1(pp+1);big_app('}'); +big_app('}');reduce(pp,2,binop,-1,20); +} + +/*:122*/ +#line 2257 "cweave.w" +;break; +case cast:/*123:*/ +#line 2502 "cweave.w" + +if(cat1==exp){ +big_app1(pp);big_app(' ');big_app1(pp+1);reduce(pp,2,exp,-2,21); +} +else if(cat1==semi)squash(pp,1,exp,-2,22); + +/*:123*/ +#line 2258 "cweave.w" +;break; +case sizeof_like:/*124:*/ +#line 2508 "cweave.w" + +if(cat1==cast)squash(pp,2,exp,-2,23); +else if(cat1==exp){ +big_app1(pp);big_app(' ');big_app1(pp+1);reduce(pp,2,exp,-2,24); +} + +/*:124*/ +#line 2259 "cweave.w" +;break; +case int_like:/*125:*/ +#line 2514 "cweave.w" + +if(cat1==int_like||cat1==struct_like){ +big_app1(pp);big_app(' ');big_app1(pp+1);reduce(pp,2,cat1,-2,25); +} +else if(cat1==exp&&(cat2==raw_int||cat2==struct_like)) +squash(pp,2,int_like,-2,26); +else if(cat1==exp||cat1==unorbinop||cat1==semi){ +big_app1(pp); +if(cat1!=semi)big_app(' '); +reduce(pp,1,decl_head,-1,27); +} +else if(cat1==colon){ +big_app1(pp);big_app(' ');reduce(pp,1,decl_head,0,28); +} +else if(cat1==prelangle)squash(pp+1,1,langle,1,29); +else if(cat1==colcol&&(cat2==exp||cat2==int_like))squash(pp,3,cat2,-2,30); +else if(cat1==cast){ +if(cat2==lbrace){ +big_app2(pp);big_app(indent);big_app(indent); +reduce(pp,2,fn_decl,1,31); +} +#line 462 "cweave.ch" +else squash(pp,2,int_like,-2,32); +} +else if(cat1==binop&&cat2==exp)squash(pp,3,int_like,-2,117); +#line 2537 "cweave.w" + +/*:125*/ +#line 2260 "cweave.w" +;break; +case decl_head:/*126:*/ +#line 2538 "cweave.w" + +if(cat1==comma){ +big_app2(pp);big_app(' ');reduce(pp,2,decl_head,-1,33); +} +else if(cat1==unorbinop){ +big_app1(pp);big_app('{');big_app1(pp+1);big_app('}'); +reduce(pp,2,decl_head,-1,34); +} +else if(cat1==exp&&cat2!=lpar&&cat2!=exp){ +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)) +squash(pp,3,decl_head,-1,36); +else if(cat1==cast)squash(pp,2,decl_head,-1,37); +else if(cat1==lbrace||(cat1==int_like&&cat2!=colcol)||cat1==decl){ +big_app1(pp);big_app(indent);app(indent);reduce(pp,1,fn_decl,0,38); +} +else if(cat1==semi)squash(pp,2,decl,-1,39); + +/*:126*/ +#line 2261 "cweave.w" +;break; +case decl:/*127:*/ +#line 2558 "cweave.w" + +if(cat1==decl){ +big_app1(pp);big_app(force);big_app1(pp+1); +reduce(pp,2,decl,-1,40); +} +else if(cat1==stmt||cat1==function){ +big_app1(pp);big_app(big_force); +big_app1(pp+1);reduce(pp,2,cat1,-1,41); +} + +/*:127*/ +#line 2262 "cweave.w" +;break; +case typedef_like:/*128:*/ +#line 2568 "cweave.w" + +#line 479 "cweave.ch" +if(cat1==decl_head){ +if((cat2==exp&&cat3!=lpar&&cat3!=exp)||cat2==int_like){ +make_underlined(pp+2);make_reserved(pp+2,raw_int); +big_app2(pp+1);reduce(pp+1,2,decl_head,0,42); +} +else if(cat2==semi){ +big_app1(pp);big_app(' ');big_app2(pp+1);reduce(pp,3,decl,-1,43); +} +} +else if(cat1==exp&&!preprocessing&&cat2!=lpar){ +make_reserved(pp+1,raw_int); +squash(pp+1,1,raw_int,-2,116); +} +#line 2577 "cweave.w" + +/*:128*/ +#line 2263 "cweave.w" +;break; +case struct_like:/*129:*/ +#line 2578 "cweave.w" + +#line 515 "cweave.ch" +if(cat1==lbrace){ +if(alternative){ +big_app1(pp);big_app(indent);big_app(force);big_app(backup); +big_app1(pp+1);reduce(pp,2,struct_head,0,44); +}else{ +big_app1(pp);big_app(' ');big_app1(pp+1); +reduce(pp,2,struct_head,0,44); +} +} +else if(cat1==exp||cat1==int_like){ +if(cat2==lbrace||cat2==semi){ +make_underlined(pp+1);make_reserved(pp+1,raw_int); +big_app1(pp);big_app(' ');big_app1(pp+1); +if(cat2==semi)reduce(pp,2,decl_head,-1,45); +else{ +if(alternative){ +big_app(indent);big_app(force);big_app(backup);big_app1(pp+2); +reduce(pp,3,struct_head,0,46); +}else{ +big_app(' ');big_app1(pp+2);reduce(pp,3,struct_head,0,46); +} +} +} +else if(cat2==colon)squash(pp+2,1,base,-1,47); +else if(!(cat2==base||cat2==raw_int|| +cat2==int_like||cat2==const_like)){ +big_app1(pp);big_app(' ');big_app1(pp+1);reduce(pp,2,int_like,-2,48); +} +} +#line 2596 "cweave.w" + +/*:129*/ +#line 2264 "cweave.w" +;break; +case struct_head:/*130:*/ +#line 2597 "cweave.w" + +if((cat1==decl||cat1==stmt||cat1==function)&&cat2==rbrace){ +#line 553 "cweave.ch" +if(alternative){ +big_app1(pp);big_app1(pp+1); +big_app(force);big_app(backup);big_app1(pp+2);big_app(' '); +big_app(outdent);reduce(pp,3,int_like,-2,49); +}else{ +big_app1(pp);big_app(indent);big_app(force);big_app1(pp+1); +big_app(outdent);big_app(force);big_app1(pp+2); +reduce(pp,3,int_like,-2,49); +} +#line 2602 "cweave.w" +} +else if(cat1==rbrace){ +big_app1(pp);app_str("\\,");big_app1(pp+1); + +reduce(pp,2,int_like,-2,50); +} + +/*:130*/ +#line 2265 "cweave.w" +;break; +case fn_decl:/*131:*/ +#line 2609 "cweave.w" + +if(cat1==decl){ +big_app1(pp);big_app(force);big_app1(pp+1);reduce(pp,2,fn_decl,0,51); +} +else if(cat1==stmt){ +big_app1(pp);app(outdent);app(outdent);big_app(force); +big_app1(pp+1);reduce(pp,2,function,-1,52); +} + +/*:131*/ +#line 2266 "cweave.w" +;break; +case function:/*132:*/ +#line 2618 "cweave.w" + +if(cat1==function||cat1==decl||cat1==stmt){ +big_app1(pp);big_app(big_force);big_app1(pp+1);reduce(pp,2,cat1,-1,53); +} + +/*:132*/ +#line 2267 "cweave.w" +;break; +case lbrace:/*133:*/ +#line 2623 "cweave.w" + +if(cat1==rbrace){ +big_app1(pp);app('\\');app(',');big_app1(pp+1); + +reduce(pp,2,stmt,-1,54); +} +else if((cat1==stmt||cat1==decl||cat1==function)&&cat2==rbrace){ +#line 571 "cweave.ch" +if(alternative){ +big_app(indent);big_app(force);big_app(backup);big_app1(pp); +app(noop);big_app(cancel);big_app1(pp+1);big_app(force);big_app(backup); +big_app1(pp+2);big_app(outdent);big_app(force);reduce(pp,3,stmt,-2,55); +}else{ +big_app(force);big_app1(pp);big_app(indent);big_app(force); +big_app1(pp+1);big_app(force);big_app(backup);big_app1(pp+2); +big_app(outdent);big_app(force);reduce(pp,3,stmt,-1,55); +} +#line 2633 "cweave.w" +} +else if(cat1==exp){ +if(cat2==rbrace)squash(pp,3,exp,-2,56); +else if(cat2==comma&&cat3==rbrace)squash(pp,4,exp,-2,56); +} + +/*:133*/ +#line 2268 "cweave.w" +;break; +case do_like:/*140:*/ +#line 2689 "cweave.w" + +#line 669 "cweave.ch" +if(alternative){ +if(cat1==lbrace)squash(pp,1,do_head,0,114); +else if(cat1==stmt&&cat2==else_like&&cat3==semi){ +big_app(force);big_app1(pp);big_app(indent);big_app(force); +big_app1(pp+1);big_app(cancel);app(noop); +big_app(break_space);big_app2(pp+2);big_app(outdent);big_app(force); +reduce(pp,4,stmt,-1,69); +} +}else{ +if(cat1==stmt&&cat2==else_like&&cat3==semi){ +big_app1(pp);big_app(break_space);app(noop);big_app(cancel); +big_app1(pp+1);big_app(cancel);app(noop);big_app(break_space); +big_app2(pp+2);reduce(pp,4,stmt,-1,69); +} +} +#line 2695 "cweave.w" + +/*:140*/ +#line 2269 "cweave.w" +;break; +case if_like:/*134:*/ +#line 2639 "cweave.w" + +if(cat1==exp){ +big_app1(pp);big_app(' ');big_app1(pp+1);reduce(pp,2,if_clause,0,57); +} + +/*:134*/ +#line 2270 "cweave.w" +;break; +case for_like:/*135:*/ +#line 2644 "cweave.w" + +if(cat1==exp){ +big_app1(pp);big_app(' ');big_app1(pp+1);reduce(pp,2,else_like,-2,58); +} + +/*:135*/ +#line 2271 "cweave.w" +;break; +case else_like:/*136:*/ +#line 2649 "cweave.w" + +if(cat1==lbrace)squash(pp,1,else_head,0,59); +else if(cat1==stmt){ +#line 589 "cweave.ch" +if(alternative){ +big_app(force);big_app1(pp);big_app(indent);big_app(force); +big_app1(pp+1);big_app(outdent);big_app(force); +reduce(pp,2,stmt,-1,60); +}else{ +big_app(force);big_app1(pp);big_app(indent);big_app(break_space); +big_app1(pp+1);big_app(outdent);big_app(force); +reduce(pp,2,stmt,-1,60); +} +#line 2655 "cweave.w" +} + +/*:136*/ +#line 2272 "cweave.w" +;break; +case if_clause:/*138:*/ +#line 2664 "cweave.w" + +if(cat1==lbrace)squash(pp,1,if_head,0,62); +else if(cat1==stmt){ +if(cat2==else_like){ +#line 627 "cweave.ch" +if(alternative){ +big_app(force);big_app1(pp);big_app(indent);big_app(force); +big_app1(pp+1);big_app(outdent);big_app(force);big_app1(pp+2); +}else{ +big_app(force);big_app1(pp);big_app(indent);big_app(break_space); +big_app1(pp+1);big_app(outdent);big_app(force);big_app1(pp+2); +} +if(cat3==if_like){ +big_app(' ');big_app1(pp+3);reduce(pp,4,if_like,0,63); +}else reduce(pp,3,else_like,0,64); +#line 2673 "cweave.w" +} +else squash(pp,1,else_like,0,65); +} + +/*:138*/ +#line 2273 "cweave.w" +;break; +case if_head:/*139:*/ +#line 2677 "cweave.w" + +if(cat1==stmt||cat1==exp){ +if(cat2==else_like){ +#line 648 "cweave.ch" +if(alternative){ +big_app(force);big_app1(pp);big_app(break_space); +big_app1(pp+1);big_app(force);big_app1(pp+2); +}else{ +big_app(force);big_app1(pp);big_app(break_space);app(noop); +big_app(cancel);big_app1(pp+1);big_app(force);big_app1(pp+2); +} +if(cat3==if_like){ +big_app(' ');big_app1(pp+3);reduce(pp,4,if_like,0,66); +}else reduce(pp,3,else_like,0,67); +#line 2685 "cweave.w" +} +else squash(pp,1,else_head,0,68); +} + +/*:139*/ +#line 2274 "cweave.w" +;break; +case else_head:/*137:*/ +#line 2657 "cweave.w" + +if(cat1==stmt||cat1==exp){ +#line 607 "cweave.ch" +if(alternative){ +big_app(force);big_app1(pp);big_app(break_space); +big_app1(pp+1);big_app(force); +reduce(pp,2,stmt,-1,61); +}else{ +big_app(force);big_app1(pp);big_app(break_space);app(noop); +big_app(cancel);big_app1(pp+1);big_app(force); +reduce(pp,2,stmt,-1,61); +} +#line 2662 "cweave.w" +} + +/*:137*/ +#line 2275 "cweave.w" +;break; +case case_like:/*141:*/ +#line 2696 "cweave.w" + +#line 692 "cweave.ch" +if(cat1==semi)squash(pp,2,stmt,-3,70); +else if(cat1==colon)squash(pp,2,tag,-2,71); +#line 2699 "cweave.w" +else if(cat1==exp){ +if(cat2==semi){ +big_app1(pp);big_app(' ');big_app1(pp+1);big_app1(pp+2); +reduce(pp,3,stmt,-1,72); +} +else if(cat2==colon){ +big_app1(pp);big_app(' ');big_app1(pp+1);big_app1(pp+2); +#line 701 "cweave.ch" +reduce(pp,3,tag,-2,73); +#line 2707 "cweave.w" +} +} + +/*:141*/ +#line 2276 "cweave.w" +;break; +case stmt:/*143:*/ +#line 2723 "cweave.w" + +if(cat1==stmt||cat1==decl||cat1==function){ +big_app1(pp); +if(cat1==function)big_app(big_force); +else if(cat1==decl)big_app(big_force); +else if(force_lines)big_app(force); +else big_app(break_space); +big_app1(pp+1);reduce(pp,2,cat1,-1,76); +} + +/*:143*/ +#line 2277 "cweave.w" +;break; +case tag:/*142:*/ +#line 2710 "cweave.w" + +#line 715 "cweave.ch" +if(alternative){ +if(cat1==tag){ +big_app1(pp);big_app(force);big_app1(pp+1);reduce(pp,2,tag,-1,74); +} +else if((cat1==stmt||cat1==decl||cat1==function)&& +(cat2==rbrace||cat2==tag)){ +big_app(force);big_app1(pp);big_app(indent);big_app(force); +big_app1(pp+1);big_app(outdent);reduce(pp,2,cat1,-1,75); +} +}else{ +if(cat1==tag){ +big_app1(pp);big_app(break_space);big_app1(pp+1);reduce(pp,2,tag,-1,74); +} +else if(cat1==stmt||cat1==decl||cat1==function){ +big_app(force);big_app(backup);big_app1(pp);big_app(break_space); +big_app1(pp+1);reduce(pp,2,cat1,-1,75); +} +} +#line 2718 "cweave.w" + +/*:142*/ +#line 2278 "cweave.w" +;break; +case semi:/*144:*/ +#line 2733 "cweave.w" + +big_app(' ');big_app1(pp);reduce(pp,1,stmt,-1,77); + +/*:144*/ +#line 2279 "cweave.w" +;break; +case lproc:/*145:*/ +#line 2736 "cweave.w" + +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){ +app(inserted);big_app2(pp);reduce(pp,2,insert,-1,79); +#line 750 "cweave.ch" +}else if((cat1==exp||cat1==function||cat1==int_like)&&cat2==rproc){ +app(inserted);big_app1(pp);big_app(' ');big_app2(pp+1); +reduce(pp,3,insert,-1,80); +}else if((cat1==exp||cat1==int_like||cat1==const_like)&& +cat2==pp_space){ +if(cat3==int_like||cat3==const_like||cat3==raw_int) +make_reserved(pp+1,(pp+3)->cat); +big_app1(pp);big_app(' ');big_app1(pp+1);app_str(" \\5"); + +reduce(pp,3,lproc,-1,119); +}else if(cat1==exp&&cat2==exp&&cat3==pp_space){ +big_app1(pp);big_app(' ');big_app2(pp+1);app_str(" \\5"); +reduce(pp,4,lproc,-1,120); +} +#line 2753 "cweave.w" + +/*:145*/ +#line 2280 "cweave.w" +;break; +case section_scrap:/*146:*/ +#line 2754 "cweave.w" + +if(cat1==semi){ +big_app2(pp);big_app(force);reduce(pp,2,stmt,-2,81); +} +else squash(pp,1,exp,-2,82); + +/*:146*/ +#line 2281 "cweave.w" +;break; +case insert:/*147:*/ +#line 2760 "cweave.w" + +if(cat1) +squash(pp,2,cat1,0,83); + +/*:147*/ +#line 2282 "cweave.w" +;break; +case prelangle:/*148:*/ +#line 2764 "cweave.w" + +init_mathness= cur_mathness= yes_math; +app('<');reduce(pp,1,binop,-2,84); + +/*:148*/ +#line 2283 "cweave.w" +;break; +case prerangle:/*149:*/ +#line 2768 "cweave.w" + +init_mathness= cur_mathness= yes_math; +app('>');reduce(pp,1,binop,-2,85); + +/*:149*/ +#line 2284 "cweave.w" +;break; +case langle:/*150:*/ +#line 2772 "cweave.w" + +if(cat1==exp&&cat2==prerangle)squash(pp,3,cast,-1,86); +else if(cat1==prerangle){ +big_app1(pp);app('\\');app(',');big_app1(pp+1); + +reduce(pp,2,cast,-1,87); +} +else if(cat1==decl_head||cat1==int_like){ +if(cat2==prerangle)squash(pp,3,cast,-1,88); +else if(cat2==comma){ +big_app3(pp);app(opt);app('9');reduce(pp,3,langle,0,89); +} +} + +/*:150*/ +#line 2285 "cweave.w" +;break; +case public_like:/*151:*/ +#line 2786 "cweave.w" + +#line 771 "cweave.ch" +if(cat1==colon)squash(pp,2,tag,-2,90); +#line 2788 "cweave.w" +else squash(pp,1,int_like,-2,91); + +/*:151*/ +#line 2286 "cweave.w" +;break; +case colcol:/*152:*/ +#line 2790 "cweave.w" + +if(cat1==exp||cat1==int_like)squash(pp,2,cat1,-2,92); + +/*:152*/ +#line 2287 "cweave.w" +;break; +case new_like:/*153:*/ +#line 2793 "cweave.w" + +if(cat1==exp||(cat1==raw_int&&cat2!=prelangle&&cat2!=langle)){ +big_app1(pp);big_app(' ');big_app1(pp+1);reduce(pp,2,new_like,0,93); +} +else if(cat1==raw_unorbin||cat1==colcol) +squash(pp,2,new_like,0,94); +else if(cat1==cast)squash(pp,2,exp,-2,95); +else if(cat1!=lpar&&cat1!=raw_int&&cat1!=struct_like) +squash(pp,1,exp,-2,96); + +/*:153*/ +#line 2288 "cweave.w" +;break; +case operator_like:/*154:*/ +#line 2803 "cweave.w" + +#line 786 "cweave.ch" +if(cat1==binop||cat1==unop||cat1==unorbinop||cat1==raw_unorbin){ +if(cat2==binop)break; +big_app1(pp);big_app('{');big_app1(pp+1);big_app('}'); +reduce(pp,2,exp,-2,97); +} +else if(cat1==new_like||cat1==sizeof_like){ +big_app1(pp);big_app(' ');big_app1(pp+1);reduce(pp,2,exp,-2,98); +} +else if(cat1==raw_int||cat1==int_like||cat1==const_like){ +big_app1(pp);big_app(' ');big_app1(pp+1); +reduce(pp,2,operator_like,-2,118); +} +#line 2812 "cweave.w" +else squash(pp,1,new_like,0,99); + +/*:154*/ +#line 2289 "cweave.w" +;break; +case catch_like:/*155:*/ +#line 2814 "cweave.w" + +if(cat1==cast||cat1==exp){ +big_app2(pp);big_app(indent);big_app(indent); +reduce(pp,2,fn_decl,0,100); +} + +/*:155*/ +#line 2290 "cweave.w" +;break; +case base:/*156:*/ +#line 2820 "cweave.w" + +if(cat1==public_like&&cat2==exp){ +if(cat3==comma){ +big_app2(pp);big_app(' ');big_app2(pp+2); +reduce(pp,4,base,0,101); +}else{ +big_app1(pp+1);big_app(' ');big_app1(pp+2); +reduce(pp+1,2,int_like,-1,102); +} +} + +/*:156*/ +#line 2291 "cweave.w" +;break; +case raw_rpar:/*157:*/ +#line 2831 "cweave.w" + +if(cat1==const_like&& +(cat2==semi||cat2==lbrace||cat2==comma||cat2==binop +||cat2==const_like)){ +big_app1(pp);big_app(' '); +big_app1(pp+1);reduce(pp,2,raw_rpar,0,103); +}else squash(pp,1,rpar,-3,104); + +/*:157*/ +#line 2292 "cweave.w" +;break; +case raw_unorbin:/*158:*/ +#line 2839 "cweave.w" + +if(cat1==const_like){ +big_app2(pp);app_str("\\ ");reduce(pp,2,raw_unorbin,0,105); + +}else squash(pp,1,unorbinop,-2,106); + +/*:158*/ +#line 2293 "cweave.w" +;break; +case const_like:/*159:*/ +#line 2845 "cweave.w" + +squash(pp,1,int_like,-2,107); + +/*:159*/ +#line 2294 "cweave.w" +;break; +#line 392 "cweave.ch" +case raw_int:/*160:*/ +#line 2848 "cweave.w" + +if(cat1==lpar)squash(pp,1,exp,-2,108); +else squash(pp,1,int_like,-3,109); + +/*:160*/ +#line 392 "cweave.ch" +;break; +case label:/*249:*/ +#line 1075 "cweave.ch" + +if(cat1==label){ +big_app1(pp);big_app(force);big_app1(pp+1);reduce(pp,2,label,-1,112); +} +else if(cat1==stmt||cat1==decl||cat1==function){ +big_app(cancel);app(noop); +big_app(outdent);big_app(outdent);big_app(force); +big_app1(pp);big_app(indent);big_app(indent);big_app(force); +big_app1(pp+1);reduce(pp,2,cat1,-1,113); +} + +/*:249*/ +#line 393 "cweave.ch" +;break; +case do_head:/*250:*/ +#line 1086 "cweave.ch" + +if(cat1==stmt&&cat2==else_like&&cat3==semi){ +big_app1(pp);big_app(break_space);big_app1(pp+1);big_app(cancel); +app(noop);big_app(break_space);big_app2(pp+2);reduce(pp,4,stmt,-1,115); +} + +/*:250*/ +#line 394 "cweave.ch" +;break; +#line 2296 "cweave.w" +} +pp++; +} + +/*:110*/ +#line 2942 "cweave.w" +; +} + +/*:165*/ +#line 3002 "cweave.w" +; +/*170:*/ +#line 3011 "cweave.w" +{ +/*171:*/ +#line 3023 "cweave.w" + +if(lo_ptr>scrap_base&&tracing==1){ +printf("\nIrreducible scrap sequence in section %d:",section_count); + +mark_harmless; +for(j= scrap_base;j<=lo_ptr;j++){ +printf(" ");print_cat(j->cat); +} +} + +/*:171*/ +#line 3012 "cweave.w" +; +for(j= scrap_base;j<=lo_ptr;j++){ +if(j!=scrap_base)app(' '); +if(j->mathness%4==yes_math)app('$'); +app1(j); +if(j->mathness/4==yes_math)app('$'); +if(tok_ptr+6>tok_mem_end)overflow("token"); +} +freeze_text;return(text_ptr-1); +} + +/*:170*/ +#line 3003 "cweave.w" +; +} + +/*:169*//*173:*/ +#line 3059 "cweave.w" + +void +C_parse(spec_ctrl) +eight_bits spec_ctrl; +{ +int count; +while(next_control<format_code||next_control==spec_ctrl){ +/*175:*/ +#line 821 "cweave.ch" + +/*176:*/ +#line 3156 "cweave.w" + +if(scrap_ptr+safe_scrap_incr>scrap_info_end|| +tok_ptr+safe_tok_incr>tok_mem_end|| +text_ptr+safe_text_incr>tok_start_end){ +if(scrap_ptr>max_scr_ptr)max_scr_ptr= scrap_ptr; +if(tok_ptr>max_tok_ptr)max_tok_ptr= tok_ptr; +if(text_ptr>max_text_ptr)max_text_ptr= text_ptr; +overflow("scrap/token/text"); +} + +/*:176*/ +#line 822 "cweave.ch" +; +#line 3084 "cweave.w" +switch(next_control){ +case section_name: +app(section_flag+(int)(cur_section-name_dir)); +app_scrap(section_scrap,maybe_math); +app_scrap(exp,yes_math);break; +case string:case constant:case verbatim:/*178:*/ +#line 3211 "cweave.w" + +count= -1; +if(next_control==constant)app_str("\\T{"); + +else if(next_control==string){ +count= 20;app_str("\\.{"); +} + +else app_str("\\vb{"); + +while(id_first<id_loc){ +if(count==0){ +app_str("}\\)\\.{");count= 20; + +} + +if((eight_bits)(*id_first)>0177){ +app_tok(quoted_char); +app_tok((eight_bits)(*id_first++)); +} +else{ +switch(*id_first){ +case' ':case'\\':case'#':case'%':case'$':case'^': +case'{':case'}':case'~':case'&':case'_':app('\\');break; + + + +#line 913 "cweave.ch" + + + +#line 3241 "cweave.w" + + + + + +case'@':if(*(id_first+1)=='@')id_first++; +else err_print("! Double @ should be used in strings"); + +} +app_tok(*id_first++); +} +count--; +} +app('}'); +app_scrap(exp,maybe_math); + +/*:178*/ +#line 3089 "cweave.w" +; +break; +case identifier:app_cur_id(1);break; +case TeX_string:/*179:*/ +#line 3272 "cweave.w" + +app_str("\\hbox{"); + +while(id_first<id_loc) +if((eight_bits)(*id_first)>0177){ +app_tok(quoted_char); +app_tok((eight_bits)(*id_first++)); +} +else{ +if(*id_first=='@')id_first++; +app_tok(*id_first++); +} +app('}'); + +/*:179*/ +#line 3092 "cweave.w" +;break; +case'/':case'.': +app(next_control);app_scrap(binop,yes_math);break; +case'<':app_str("\\langle");app_scrap(prelangle,yes_math);break; + +case'>':app_str("\\rangle");app_scrap(prerangle,yes_math);break; + +case'=':app_str("\\K");app_scrap(binop,yes_math);break; + +case'|':app_str("\\OR");app_scrap(binop,yes_math);break; + +case'^':app_str("\\XOR");app_scrap(binop,yes_math);break; + +case'%':app_str("\\MOD");app_scrap(binop,yes_math);break; + +case'!':app_str("\\R");app_scrap(unop,yes_math);break; + +case'~':app_str("\\CM");app_scrap(unop,yes_math);break; + +case'+':case'-':app(next_control);app_scrap(unorbinop,yes_math);break; +case'*':app(next_control);app_scrap(raw_unorbin,yes_math);break; +case'&':app_str("\\AND");app_scrap(raw_unorbin,yes_math);break; + +case'?':app_str("\\?");app_scrap(question,yes_math);break; + +case'#':app_str("\\#");app_scrap(unorbinop,yes_math);break; + +case ignore:case xref_roman:case xref_wildcard: +case xref_typewriter:case noop:break; +#line 835 "cweave.ch" +case'(':if(func_index) +if(*(tok_ptr-1)>=id_flag&&*(tok_ptr-1)<res_flag) + +(name_dir+*(tok_ptr-1)%10240)->func_flag= 1; +case'[':app(next_control);app_scrap(lpar,maybe_math);break; +case')':case']':app(next_control);app_scrap(raw_rpar,maybe_math);break; +case'{':if(alternative) +app_str("\\LBR"); +else +app_str("\\{"); +app_scrap(lbrace,yes_math);break; + +case'}':if(alternative) +app_str("\\RBR"); +else +app_str("\\}"); +app_scrap(rbrace,yes_math);break; + +#line 3127 "cweave.w" +case',':app(',');app_scrap(comma,yes_math);break; +case';':app(';');app_scrap(semi,maybe_math);break; +case':':app(':');app_scrap(colon,maybe_math);break; +/*177:*/ +#line 3170 "cweave.w" + +case not_eq:app_str("\\I");app_scrap(binop,yes_math);break; + +case lt_eq:app_str("\\Z");app_scrap(binop,yes_math);break; + +case gt_eq:app_str("\\G");app_scrap(binop,yes_math);break; + +case eq_eq:app_str("\\E");app_scrap(binop,yes_math);break; + +case and_and:app_str("\\W");app_scrap(binop,yes_math);break; + +case or_or:app_str("\\V");app_scrap(binop,yes_math);break; + +case plus_plus:app_str("\\PP");app_scrap(unop,yes_math);break; + +case minus_minus:app_str("\\MM");app_scrap(unop,yes_math);break; + +case minus_gt:app_str("\\MG");app_scrap(binop,yes_math);break; + +case gt_gt:app_str("\\GG");app_scrap(binop,yes_math);break; + +case lt_lt:app_str("\\LL");app_scrap(binop,yes_math);break; + +case dot_dot_dot:app_str("\\,\\ldots\\,");app_scrap(exp,yes_math);break; + + +case colon_colon:app_str("\\DC");app_scrap(colcol,maybe_math);break; + +case period_ast:app_str("\\PA");app_scrap(binop,yes_math);break; + +#line 900 "cweave.ch" +case minus_gt_ast:app_str("\\MGA");app_scrap(binop,yes_math);break; + +case numb_numb:app_str("\\NN");app_scrap(binop,yes_math);break; + +#line 3202 "cweave.w" + +/*:177*/ +#line 3130 "cweave.w" + +case thin_space:app_str("\\,");app_scrap(insert,maybe_math);break; + +case math_break:app(opt);app_str("0"); +app_scrap(insert,maybe_math);break; +case line_break:app(force);app_scrap(insert,no_math);break; +#line 863 "cweave.ch" +case left_preproc:app(force);app(preproc_line); +app_str("\\#");app_scrap(lproc,no_math);break; + +case preproc_space:app(' ');app_scrap(pp_space,maybe_math);break; +case right_preproc:app(force);app_scrap(rproc,no_math);break; +#line 3140 "cweave.w" +case big_line_break:app(big_force);app_scrap(insert,no_math);break; +case no_line_break:app(big_cancel);app(noop);app(break_space); +app(noop);app(big_cancel); +app_scrap(insert,no_math);break; +#line 875 "cweave.ch" +case pseudo_semi:app_scrap(semi,maybe_math);break; +case pseudo_lbrace:app_str("\\phantom{"); +if(alternative) +app_str("\\LBR"); +else +app_str("\\{"); +app_str("}"); +app_scrap(lbrace,yes_math); +break; +case pseudo_rbrace:app_str("\\phantom{"); +if(alternative) +app_str("\\RBR"); +else +app_str("\\}"); +app_str("}"); +app_scrap(rbrace,yes_math); +break; +#line 3145 "cweave.w" +case macro_arg_open:app_scrap(begin_arg,maybe_math);break; +case macro_arg_close:app_scrap(end_arg,maybe_math);break; +case join:app_str("\\J");app_scrap(insert,no_math);break; + +case output_defs_code:app(force);app_str("\\ATH");app(force); +app_scrap(insert,no_math);break; + +default:app(inserted);app(next_control); +app_scrap(insert,maybe_math);break; +} + +/*:175*/ +#line 3066 "cweave.w" +; +next_control= get_next(); +if(next_control=='|'||next_control==begin_comment|| +next_control==begin_short_comment)return; +} +} + +/*:173*//*181:*/ +#line 3292 "cweave.w" + +void +app_cur_id(scrapping) +boolean scrapping; +{ +name_pointer p= id_lookup(id_first,id_loc,normal); +if(p->ilk<=quoted){ +app(id_flag+(int)(p-name_dir)); +if(scrapping)app_scrap(exp,p->ilk>=custom?yes_math:maybe_math); + +}else{ +app(res_flag+(int)(p-name_dir)); +if(scrapping)app_scrap(p->ilk,maybe_math); +} +} + +/*:181*//*182:*/ +#line 3313 "cweave.w" + +text_pointer +C_translate() +{ +text_pointer p; +scrap_pointer save_base; +save_base= scrap_base;scrap_base= scrap_ptr+1; +C_parse(section_name); +if(next_control!='|')err_print("! Missing '|' after C text"); + +app_tok(cancel);app_scrap(insert,maybe_math); + +p= translate(); +if(scrap_ptr>max_scr_ptr)max_scr_ptr= scrap_ptr; +scrap_ptr= scrap_base-1;scrap_base= save_base; +return(p); +} + +/*:182*//*183:*/ +#line 3335 "cweave.w" + +void +outer_parse() +{ +int bal; +text_pointer p,q; +while(next_control<format_code) +if(next_control!=begin_comment&&next_control!=begin_short_comment) +C_parse(ignore); +else{ +boolean is_long_comment= (next_control==begin_comment); +/*176:*/ +#line 3156 "cweave.w" + +if(scrap_ptr+safe_scrap_incr>scrap_info_end|| +tok_ptr+safe_tok_incr>tok_mem_end|| +text_ptr+safe_text_incr>tok_start_end){ +if(scrap_ptr>max_scr_ptr)max_scr_ptr= scrap_ptr; +if(tok_ptr>max_tok_ptr)max_tok_ptr= tok_ptr; +if(text_ptr>max_text_ptr)max_text_ptr= text_ptr; +overflow("scrap/token/text"); +} + +/*:176*/ +#line 3346 "cweave.w" +; +app(cancel);app(inserted); +if(is_long_comment)app_str("\\C{"); + +else app_str("\\SHC{"); + +bal= copy_comment(is_long_comment,1);next_control= ignore; +while(bal>0){ +p= text_ptr;freeze_text;q= C_translate(); + +app(tok_flag+(int)(p-tok_start)); +app_str("\\PB{");app(inner_tok_flag+(int)(q-tok_start));app_tok('}'); + +if(next_control=='|'){ +bal= copy_comment(is_long_comment,bal); +next_control= ignore; +} +else bal= 0; +} +app(force);app_scrap(insert,no_math); + +} +} + +/*:183*//*188:*/ +#line 3439 "cweave.w" + +void +push_level(p) +text_pointer p; +{ +if(stack_ptr==stack_end)overflow("stack"); +if(stack_ptr>stack){ +stack_ptr->end_field= cur_end; +stack_ptr->tok_field= cur_tok; +stack_ptr->mode_field= cur_mode; +} +stack_ptr++; +if(stack_ptr>max_stack_ptr)max_stack_ptr= stack_ptr; +cur_tok= *p;cur_end= *(p+1); +} + +/*:188*//*189:*/ +#line 3459 "cweave.w" + +void +pop_level() +{ +cur_end= (--stack_ptr)->end_field; +cur_tok= stack_ptr->tok_field;cur_mode= stack_ptr->mode_field; +} + +/*:189*//*191:*/ +#line 3481 "cweave.w" + +eight_bits +get_output() +{ +sixteen_bits a; +restart:while(cur_tok==cur_end)pop_level(); +a= *(cur_tok++); +if(a>=0400){ +cur_name= a%id_flag+name_dir; +switch(a/id_flag){ +case 2:return(res_word); +case 3:return(section_code); +case 4:push_level(a%id_flag+tok_start);goto restart; + +case 5:push_level(a%id_flag+tok_start);cur_mode= inner;goto restart; + +default:return(identifier); +} +} +return(a); +} + +/*:191*//*192:*/ +#line 3523 "cweave.w" + +void +output_C() +{ +token_pointer save_tok_ptr; +text_pointer save_text_ptr; +sixteen_bits save_next_control; +text_pointer p; +save_tok_ptr= tok_ptr;save_text_ptr= text_ptr; +save_next_control= next_control;next_control= ignore;p= C_translate(); +app(inner_tok_flag+(int)(p-tok_start)); +out_str("\\PB{");make_output();out('}'); + +if(text_ptr>max_text_ptr)max_text_ptr= text_ptr; +if(tok_ptr>max_tok_ptr)max_tok_ptr= tok_ptr; +text_ptr= save_text_ptr;tok_ptr= save_tok_ptr; +next_control= save_next_control; +} + +/*:192*//*194:*/ +#line 3547 "cweave.w" + +void +make_output() +{ +eight_bits a, +b; +int c; +char scratch[longest_name]; +char*k,*k_limit; +char*j; +char*p; +char delim; +char*save_loc,*save_limit; +name_pointer cur_section_name; +boolean save_mode; +app(end_translation); +freeze_text;push_level(text_ptr-1); +while(1){ +a= get_output(); +reswitch:switch(a){ +case end_translation:return; +case identifier:case res_word:/*195:*/ +#line 3597 "cweave.w" + +out('\\'); +if(a==identifier){ +if(cur_name->ilk>=custom&&cur_name->ilk<=quoted&&!doing_format){ +for(p= cur_name->byte_start;p<(cur_name+1)->byte_start;p++) +out(isxalpha(*p)?'x':*p); +break; +#line 923 "cweave.ch" +}else if(is_tiny(cur_name))out('|'); +#line 3605 "cweave.w" + +else{delim= '.'; +for(p= cur_name->byte_start;p<(cur_name+1)->byte_start;p++) +if(xislower(*p)){ +delim= '\\';break; +} +out(delim); +} + + +} +#line 931 "cweave.ch" +else out('&'); +#line 3617 "cweave.w" + +if(is_tiny(cur_name)){ +if(isxalpha((cur_name->byte_start)[0])) +out('\\'); +out((cur_name->byte_start)[0]); +} +else out_name(cur_name); + +/*:195*/ +#line 3568 "cweave.w" +;break; +case section_code:/*199:*/ +#line 3695 "cweave.w" +{ +out_str("\\X"); + +cur_xref= (xref_pointer)cur_name->xref; +if(cur_xref->num==file_flag){an_output= 1;cur_xref= cur_xref->xlink;} +else an_output= 0; +if(cur_xref->num>=def_flag){ +out_section(cur_xref->num-def_flag); +if(phase==3){ +cur_xref= cur_xref->xlink; +while(cur_xref->num>=def_flag){ +out_str(", "); +out_section(cur_xref->num-def_flag); +cur_xref= cur_xref->xlink; +} +} +} +else out('0'); +out(':'); +if(an_output)out_str("\\.{"); + +/*200:*/ +#line 3721 "cweave.w" + +sprint_section_name(scratch,cur_name); +k= scratch; +k_limit= scratch+strlen(scratch); +cur_section_name= cur_name; +while(k<k_limit){ +b= *(k++); +if(b=='@')/*201:*/ +#line 3756 "cweave.w" + +if(*k++!='@'){ +printf("\n! Illegal control code in section name: <"); + +print_section_name(cur_section_name);printf("> ");mark_error; +} + +/*:201*/ +#line 3728 "cweave.w" +; +if(an_output) +switch(b){ +case' ':case'\\':case'#':case'%':case'$':case'^': +case'{':case'}':case'~':case'&':case'_': +out('\\'); + + + +#line 949 "cweave.ch" + + + +#line 3740 "cweave.w" + + + + + +default:out(b); +} +#line 959 "cweave.ch" +else if(b!='|')out(b); +#line 3748 "cweave.w" +else{ +/*202:*/ +#line 3769 "cweave.w" + +j= limit+1;*j= '|';delim= 0; +while(1){ +if(k>=k_limit){ +printf("\n! C text in section name didn't end: <"); + +print_section_name(cur_section_name);printf("> ");mark_error;break; +} +b= *(k++); +if(b=='@'||(b=='\\'&&delim!=0)) +/*203:*/ +#line 3792 "cweave.w" +{ +if(j>buffer+long_buf_size-4)overflow("buffer"); +*(++j)= b;*(++j)= *(k++); +} + +/*:203*/ +#line 3779 "cweave.w" + +else{ +if(b=='\''||b=='"') +if(delim==0)delim= b; +else if(delim==b)delim= 0; +if(b!='|'||delim!=0){ +if(j>buffer+long_buf_size-3)overflow("buffer"); +*(++j)= b; +} +else break; +} +} + +/*:202*/ +#line 3749 "cweave.w" +; +save_loc= loc;save_limit= limit;loc= limit+2;limit= j+1; +*limit= '|';output_C(); +loc= save_loc;limit= save_limit; +} +} + +/*:200*/ +#line 3716 "cweave.w" +; +if(an_output)out_str(" }"); +out_str("\\X"); +} + +/*:199*/ +#line 3569 "cweave.w" +;break; +case math_rel:out_str("\\MRL{"); + +case noop:case inserted:break; +case cancel:case big_cancel:c= 0;b= a; +while(1){ +a= get_output(); +if(a==inserted)continue; +if((a<indent&&!(b==big_cancel&&a==' '))||a>big_force)break; +if(a==indent)c++;else if(a==outdent)c--; +else if(a==opt)a= get_output(); +} +/*198:*/ +#line 3682 "cweave.w" + +for(;c>0;c--)out_str("\\1"); + +for(;c<0;c++)out_str("\\2"); + + +/*:198*/ +#line 3581 "cweave.w" +; +goto reswitch; +case indent:case outdent:case opt:case backup:case break_space: +case force:case big_force:case preproc_line:/*196:*/ +#line 3628 "cweave.w" + +if(a<break_space||a==preproc_line){ +if(cur_mode==outer){ +out('\\');out(a-cancel+'0'); + + + + + +if(a==opt){ +b= get_output(); +#line 939 "cweave.ch" +if(b!='0'||force_lines==0)out(b); +#line 3640 "cweave.w" +else out_str("{-1}"); +} +}else if(a==opt)b= get_output(); +} +else/*197:*/ +#line 3652 "cweave.w" +{ +b= a;save_mode= cur_mode;c= 0; +while(1){ +a= get_output(); +if(a==inserted)continue; +if(a==cancel||a==big_cancel){ +/*198:*/ +#line 3682 "cweave.w" + +for(;c>0;c--)out_str("\\1"); + +for(;c<0;c++)out_str("\\2"); + + +/*:198*/ +#line 3658 "cweave.w" +; +goto reswitch; +} +if((a!=' '&&a<indent)||a==backup||a>big_force){ +if(save_mode==outer){ +if(out_ptr>out_buf+3&&strncmp(out_ptr-3,"\\Y\\B",4)==0) +goto reswitch; +/*198:*/ +#line 3682 "cweave.w" + +for(;c>0;c--)out_str("\\1"); + +for(;c<0;c++)out_str("\\2"); + + +/*:198*/ +#line 3665 "cweave.w" +; +out('\\');out(b-cancel+'0'); + + + +if(a!=end_translation)finish_line(); +} +else if(a!=end_translation&&cur_mode==inner)out(' '); +goto reswitch; +} +if(a==indent)c++; +else if(a==outdent)c--; +else if(a==opt)a= get_output(); +else if(a>b)b= a; +} +} + +/*:197*/ +#line 3644 "cweave.w" + + +/*:196*/ +#line 3585 "cweave.w" +;break; +case quoted_char:out(*(cur_tok++));break; +default:out(a); +} +} +} + +/*:194*//*205:*/ +#line 3807 "cweave.w" + +void +phase_two(){ +reset_input();if(show_progress)printf("\nWriting the output file..."); + +section_count= 0;format_visible= 1;copy_limbo(); +finish_line();flush_buffer(out_buf,0,0); +while(!input_has_ended)/*207:*/ +#line 3840 "cweave.w" +{ +section_count++; +/*208:*/ +#line 3858 "cweave.w" + +if(*(loc-1)!='*')out_str("\\M"); + +else{ +while(*loc==' ')loc++; +if(*loc=='*'){ +sec_depth= -1; +loc++; +} +else{ +for(sec_depth= 0;xisdigit(*loc);loc++) +sec_depth= sec_depth*10+(*loc)-'0'; +} +while(*loc==' ')loc++; +group_found= 1; +out_str("\\N"); + +{char s[32];sprintf(s,"{%d}",sec_depth+1);out_str(s);} +if(show_progress) +printf("*%d",section_count);update_terminal; +} +out_str("{");out_section(section_count);out_str("}"); + +/*:208*/ +#line 3842 "cweave.w" +; +save_position; +/*209:*/ +#line 3884 "cweave.w" +do{ +next_control= copy_TeX(); +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(); +if(next_control==TeX_string) +err_print("! TeX string should be in C text only");break; + +#line 972 "cweave.ch" +case thin_space:case math_break:case ord: +case line_break:case big_line_break:case no_line_break:case join: +case pseudo_semi:case macro_arg_open:case macro_arg_close: +case pseudo_lbrace:case pseudo_rbrace: +case output_defs_code: +err_print("! You can't do that in TeX text");break; + +#line 3901 "cweave.w" +} +}while(next_control<format_code); + +/*:209*/ +#line 3844 "cweave.w" +; +/*210:*/ +#line 3907 "cweave.w" + +space_checked= 0; +while(next_control<=definition){ +init_stack; +if(next_control==definition)/*213:*/ +#line 3965 "cweave.w" +{ +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"); + +if((next_control= get_next())!=identifier) +err_print("! Improper macro definition"); + +else{ +app('$');app_cur_id(0); +#line 992 "cweave.ch" +if(*loc=='('){ +if(func_index) +(name_dir+*(tok_ptr-1)%10240)->func_flag= 1; +reswitch:switch(next_control= get_next()){ +case'(':case',':app(next_control);goto reswitch; +case identifier:app_cur_id(0);goto reswitch; +case')':app(next_control);next_control= get_next();break; +default:err_print("! Improper macro definition");break; +} +} +#line 3982 "cweave.w" +else next_control= get_next(); +app_str("$ ");app(break_space); +app_scrap(dead,no_math); +} +} + +/*:213*/ +#line 3911 "cweave.w" + +else/*214:*/ +#line 3988 "cweave.w" +{ +doing_format= 1; +if(*(loc-1)=='s'||*(loc-1)=='S')format_visible= 0; +if(!space_checked){emit_space_if_needed;save_position;} +app_str("\\F"); + +next_control= get_next(); +if(next_control==identifier){ +app(id_flag+(int)(id_lookup(id_first,id_loc,normal)-name_dir)); +app(' '); +app(break_space); +next_control= get_next(); +if(next_control==identifier){ +app(id_flag+(int)(id_lookup(id_first,id_loc,normal)-name_dir)); +app_scrap(exp,maybe_math);app_scrap(semi,maybe_math); +next_control= get_next(); +} +} +if(scrap_ptr!=scrap_info+2)err_print("! Improper format definition"); + +} + +/*:214*/ +#line 3912 "cweave.w" +; +outer_parse();finish_C(format_visible);format_visible= 1; +doing_format= 0; +} + +/*:210*/ +#line 3845 "cweave.w" +; +/*216:*/ +#line 4017 "cweave.w" + +this_section= name_dir; +if(next_control<=section_name){ +emit_space_if_needed;init_stack; +if(next_control==begin_C)next_control= get_next(); +else{ +this_section= cur_section; +/*217:*/ +#line 4037 "cweave.w" + +do next_control= get_next(); +while(next_control=='+'); +if(next_control!='='&&next_control!=eq_eq) +err_print("! You need an = sign after the section name"); + +else next_control= get_next(); +if(out_ptr>out_buf+1&&*out_ptr=='Y'&&*(out_ptr-1)=='\\')app(backup); + + +app(section_flag+(int)(this_section-name_dir)); +cur_xref= (xref_pointer)this_section->xref; +if(cur_xref->num==file_flag)cur_xref= cur_xref->xlink; +app_str("${}"); +if(cur_xref->num!=section_count+def_flag){ +app_str("\\mathrel+"); +this_section= name_dir; +} +app_str("\\E"); + +app_str("{}$"); +app(force);app_scrap(dead,no_math); + + +/*:217*/ +#line 4025 "cweave.w" +; +} +while(next_control<=section_name){ +outer_parse(); +/*218:*/ +#line 4061 "cweave.w" + +if(next_control<section_name){ +err_print("! You can't do that in C text"); + +next_control= get_next(); +} +else if(next_control==section_name){ +app(section_flag+(int)(cur_section-name_dir)); +app_scrap(section_scrap,maybe_math); +next_control= get_next(); +} + +/*:218*/ +#line 4029 "cweave.w" +; +} +finish_C(1); +} + +/*:216*/ +#line 3846 "cweave.w" +; +/*219:*/ +#line 4076 "cweave.w" + +if(this_section>name_dir){ +cur_xref= (xref_pointer)this_section->xref; +if(cur_xref->num==file_flag){an_output= 1;cur_xref= cur_xref->xlink;} +else an_output= 0; +if(cur_xref->num>def_flag) +cur_xref= cur_xref->xlink; +footnote(def_flag);footnote(cite_flag);footnote(0); +} + +/*:219*/ +#line 3847 "cweave.w" +; +/*223:*/ +#line 4134 "cweave.w" + +out_str("\\fi");finish_line(); + +flush_buffer(out_buf,0,0); + +/*:223*/ +#line 3848 "cweave.w" +; +} + +/*:207*/ +#line 3814 "cweave.w" +; +} + +/*:205*//*212:*/ +#line 3930 "cweave.w" + +void +finish_C(visible) +boolean visible; +{ +text_pointer p; +if(visible){ +out_str("\\B");app_tok(force);app_scrap(insert,no_math); +p= translate(); + +app(tok_flag+(int)(p-tok_start));make_output(); +if(out_ptr>out_buf+1) +if(*(out_ptr-1)=='\\') + + + +if(*out_ptr=='6')out_ptr-= 2; +else if(*out_ptr=='7')*out_ptr= 'Y'; +out_str("\\par");finish_line(); +} +if(text_ptr>max_text_ptr)max_text_ptr= text_ptr; +if(tok_ptr>max_tok_ptr)max_tok_ptr= tok_ptr; +if(scrap_ptr>max_scr_ptr)max_scr_ptr= scrap_ptr; +tok_ptr= tok_mem+1;text_ptr= tok_start+1;scrap_ptr= scrap_info; + +} + +/*:212*//*221:*/ +#line 4101 "cweave.w" + +void +footnote(flag) +sixteen_bits flag; +{ +xref_pointer q; +if(cur_xref->num<=flag)return; +finish_line();out('\\'); + + + +out(flag==0?'U':flag==cite_flag?'Q':'A'); +/*222:*/ +#line 4121 "cweave.w" + +q= cur_xref;if(q->xlink->num>flag)out('s'); +while(1){ +out_section(cur_xref->num-flag); +cur_xref= cur_xref->xlink; +if(cur_xref->num<=flag)break; +if(cur_xref->xlink->num>flag)out_str(", "); +else{out_str("\\ET"); + +if(cur_xref!=q->xlink)out('s'); +} +} + +/*:222*/ +#line 4113 "cweave.w" +; +out('.'); +} + +/*:221*//*225:*/ +#line 4150 "cweave.w" + +void +phase_three(){ +if(no_xref){ +finish_line(); +out_str("\\end"); + +finish_line(); +} +else{ +phase= 3;if(show_progress)printf("\nWriting the index..."); + +finish_line(); +if((idx_file= fopen(idx_file_name,"w"))==NULL) +fatal("! Cannot open index file ",idx_file_name); + +if(change_exists){ +/*227:*/ +#line 4201 "cweave.w" +{ + +k_section= 0; +while(!changed_section[++k_section]); +out_str("\\ch "); + +out_section(k_section); +while(k_section<section_count){ +while(!changed_section[++k_section]); +out_str(", ");out_section(k_section); +} +out('.'); +} + +/*:227*/ +#line 4167 "cweave.w" +;finish_line();finish_line(); +} +out_str("\\inx");finish_line(); + +active_file= idx_file; +/*229:*/ +#line 4232 "cweave.w" +{ +int c; +for(c= 0;c<=255;c++)bucket[c]= NULL; +for(h= hash;h<=hash_end;h++){ +next_name= *h; +while(next_name){ +cur_name= next_name;next_name= cur_name->link; +if(cur_name->xref!=(char*)xmem){ +c= (eight_bits)((cur_name->byte_start)[0]); +if(xisupper(c))c= tolower(c); +blink[cur_name-name_dir]= bucket[c];bucket[c]= cur_name; +} +} +} +} + +/*:229*/ +#line 4172 "cweave.w" +; +/*238:*/ +#line 4348 "cweave.w" + +sort_ptr= scrap_info;unbucket(1); +while(sort_ptr>scrap_info){ +cur_depth= sort_ptr->depth; +if(blink[sort_ptr->head-name_dir]==0||cur_depth==infinity) +/*240:*/ +#line 4373 "cweave.w" +{ +cur_name= sort_ptr->head; +do{ +out_str("\\I"); + +/*241:*/ +#line 4385 "cweave.w" + +#line 1036 "cweave.ch" +switch(cur_name->ilk){ +case normal: +if(is_tiny(cur_name)&&!cur_name->func_flag) +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; + +case typewriter:out_str("\\.");break; + +case custom:case quoted:{char*j;out_str("$\\"); +for(j= cur_name->byte_start;j<(cur_name+1)->byte_start;j++) +out(isxalpha(*j)?'x':*j); +out('$'); +goto name_done; +} +default:out_str("\\&"); + +} +out_name(cur_name); +name_done: +if(cur_name->func_flag) +out_str("(\\,)"); +#line 4414 "cweave.w" + +/*:241*/ +#line 4378 "cweave.w" +; +/*242:*/ +#line 4418 "cweave.w" + +/*244:*/ +#line 4436 "cweave.w" + +this_xref= (xref_pointer)cur_name->xref;cur_xref= xmem; +do{ +next_xref= this_xref->xlink;this_xref->xlink= cur_xref; +cur_xref= this_xref;this_xref= next_xref; +}while(this_xref!=xmem); + +/*:244*/ +#line 4419 "cweave.w" +; +do{ +out_str(", ");cur_val= cur_xref->num; +if(cur_val<def_flag)out_section(cur_val); +else{out_str("\\[");out_section(cur_val-def_flag);out(']');} + +cur_xref= cur_xref->xlink; +}while(cur_xref!=xmem); +out('.');finish_line(); + +/*:242*/ +#line 4379 "cweave.w" +; +cur_name= blink[cur_name-name_dir]; +}while(cur_name); +--sort_ptr; +} + +/*:240*/ +#line 4353 "cweave.w" + +else/*239:*/ +#line 4357 "cweave.w" +{ +eight_bits c; +next_name= sort_ptr->head; +do{ +cur_name= next_name;next_name= blink[cur_name-name_dir]; +cur_byte= cur_name->byte_start+cur_depth; +if(cur_byte==(cur_name+1)->byte_start)c= 0; +else{ +c= (eight_bits)*cur_byte; +if(xisupper(c))c= tolower(c); +} +blink[cur_name-name_dir]= bucket[c];bucket[c]= cur_name; +}while(next_name); +--sort_ptr;unbucket(cur_depth+1); +} + +/*:239*/ +#line 4354 "cweave.w" +; +} + +/*:238*/ +#line 4173 "cweave.w" +; +finish_line();fclose(active_file); +active_file= tex_file; +out_str("\\fin");finish_line(); + +if((scn_file= fopen(scn_file_name,"w"))==NULL) +fatal("! Cannot open section file ",scn_file_name); + +active_file= scn_file; +/*247:*/ +#line 4467 "cweave.w" +section_print(root) + +/*:247*/ +#line 4182 "cweave.w" +; +finish_line();fclose(active_file); +active_file= tex_file; +if(group_found)out_str("\\con");else out_str("\\end"); + + +finish_line(); +fclose(active_file); +} +if(show_happiness)printf("\nDone."); +check_complete(); +} + +/*:225*//*237:*/ +#line 4330 "cweave.w" + +void +unbucket(d) +eight_bits d; +{ +int c; + +for(c= 100+128;c>=0;c--)if(bucket[collate[c]]){ + +if(sort_ptr>=scrap_info_end)overflow("sorting"); +sort_ptr++; +if(sort_ptr>max_sort_ptr)max_sort_ptr= sort_ptr; +if(c==0)sort_ptr->depth= infinity; +else sort_ptr->depth= d; +sort_ptr->head= bucket[collate[c]];bucket[collate[c]]= NULL; +} +} + +/*:237*//*246:*/ +#line 4450 "cweave.w" + +void +section_print(p) +name_pointer p; +{ +if(p){ +section_print(p->llink);out_str("\\I"); + +tok_ptr= tok_mem+1;text_ptr= tok_start+1;scrap_ptr= scrap_info;init_stack; +app(p-name_dir+section_flag);make_output(); +footnote(cite_flag); +footnote(0); +finish_line(); +section_print(p->rlink); +} +} + +/*:246*//*248:*/ +#line 4472 "cweave.w" + +void +print_stats(){ +printf("\nMemory usage statistics:\n"); + +printf("%ld names (out of %ld)\n", +(long)(name_ptr-name_dir),(long)max_names); +printf("%ld cross-references (out of %ld)\n", +(long)(xref_ptr-xmem),(long)max_refs); +printf("%ld bytes (out of %ld)\n", +(long)(byte_ptr-byte_mem),(long)max_bytes); +printf("Parsing:\n"); +printf("%ld scraps (out of %ld)\n", +(long)(max_scr_ptr-scrap_info),(long)max_scraps); +printf("%ld texts (out of %ld)\n", +(long)(max_text_ptr-tok_start),(long)max_texts); +printf("%ld tokens (out of %ld)\n", +(long)(max_tok_ptr-tok_mem),(long)max_toks); +printf("%ld levels (out of %ld)\n", +(long)(max_stack_ptr-stack),(long)stack_size); +printf("Sorting:\n"); +printf("%ld levels (out of %ld)\n", +(long)(max_sort_ptr-scrap_info),(long)max_scraps); +} + +#line 1075 "cweave.ch" +/*:248*/ diff --git a/web/c_cpp/c2cweb/cweb/cweave.ch b/web/c_cpp/c2cweb/cweb/cweave.ch new file mode 100644 index 0000000000..035de5a5e3 --- /dev/null +++ b/web/c_cpp/c2cweb/cweb/cweave.ch @@ -0,0 +1,1095 @@ +This file is part of the c2cweb package Version 1.5 + +This is a change file for CWEB's CWEAVE (Ver. 3.4g June 1996) + +modified by Werner Lemberg (a7971428@@unet.univie.ac.at) 10-Nov-1996 + +=============================================================================== + +An exclamation mark (!) removes a bug in the original CWEAVE which caused +unbalanced braces sometimes. + +One star (*) marks changes of the productions if switch a is set -- +please look at an output to see what. Additionally cwebmaca.tex will be used +by TeX instead of cwebmac.tex . + +Two stars (**) mark changes to the index routines: function definitions will +now have () after the identifier if the i switch is on (this will work properly +only if you don't have function names identical to names of variables, +structures etc.). + +Three stars (***) change the debugging output slightly. + +A (+) enables the dollar sign `$' in identifiers; some compilers allow it, +and it doesn't harm in the output. + +Two plus (++) improve the processing of #define statements (weird things +like '#define short int'). Two new control codes are introduced: @{ and @} +(i.e. invisible braces). You will need this if you have unbalanced braces due +to #ifdef ... #endif statements. The concatenation operator ## will be now +recognized. + +Three plus (+++) implement exotic cases of C++ (look e.g. for "iostream.h"). + + +These changes are not perfect yet; if you don't like some things, remove the +specified sections. (Or better, improve them and send the new change file(s) +to me :-) + + +@x in limbo +\let\maybe=\iftrue +@y +\let\maybe=\iffalse +@z + +@x section 1 +@d banner "This is CWEAVE (Version 3.4)\n" +@y +@d banner "This is CWEAVE (Version 3.4) modified by Werner Lemberg (V1.5)\n" +@z + +@x section 4 +@d max_bytes 90000 /* the number of bytes in identifiers, + index entries, and section names */ +@d max_names 4000 /* number of identifiers, strings, section names; + must be less than 10240; used in |"common.w"| */ +@d max_sections 2000 /* greater than the total number of sections */ +@d hash_size 353 /* should be prime */ +@d buf_size 100 /* maximum length of input line, plus one */ +@d longest_name 1000 /* section names and strings shouldn't be longer than this */ +@d long_buf_size (buf_size+longest_name) +@d line_length 80 /* lines of \TEX/ output have at most this many characters; + should be less than 256 */ +@d max_refs 20000 /* number of cross-references; must be less than 65536 */ +@d max_toks 20000 /* number of symbols in \CEE/ texts being parsed; + must be less than 65536 */ +@d max_texts 4000 /* number of phrases in \CEE/ texts being parsed; + must be less than 10240 */ +@d max_scraps 2000 /* number of tokens in \CEE/ texts being parsed */ +@d stack_size 400 /* number of simultaneous output levels */ +@y +@d max_bytes 225000 /* the number of bytes in identifiers, + index entries, and section names */ +@d max_names 10000 /* number of identifiers, strings, section names; + must be less than 10240; used in |"common.w"| */ +@d max_sections 2000 /* greater than the total number of sections */ +@d hash_size 353 /* should be prime */ +@d buf_size 400 /* maximum length of input line, plus one */ +@d longest_name 1000 /* section names and strings shouldn't be longer than this */ +@d long_buf_size (buf_size+longest_name) +@d line_length 100 /* lines of \TEX/ output have at most this many characters; + should be less than 256 */ +@d max_refs 20000 /* number of cross-references; must be less than 65536 */ +@d max_toks 65000 /* number of symbols in \CEE/ texts being parsed; + must be less than 65536 */ +@d max_texts 10200 /* number of phrases in \CEE/ texts being parsed; + must be less than 10240 */ +@d max_scraps 65000 /* number of tokens in \CEE/ texts being parsed */ +@d stack_size 10000 /* number of simultaneous output levels */ +@z + +** + +@x section 16 +@d ilk dummy.Ilk +@y +@d ilk dummy.dummy1.Ilk +@d func_flag dummy.dummy1.Func_flag +@z + +++ + +@x section 30 +@d new_section 0231 /* control code for `\.{@@\ }' and `\.{@@*}' */ +@y +@d new_section 0231 /* control code for `\.{@@\ }' and `\.{@@*}' */ +@d pseudo_lbrace 022 /* control code for `\.{@@\{}' */ +@d pseudo_rbrace 023 /* control code for `\.{@@\}}' */ +@z + +++ + +@x section 32 +ccode['+']=no_line_break; ccode[';']=pseudo_semi; +ccode['[']=macro_arg_open; ccode[']']=macro_arg_close; +@y +ccode['+']=no_line_break; ccode[';']=pseudo_semi; +ccode['{']=pseudo_lbrace; ccode['}']=pseudo_rbrace; +ccode['[']=macro_arg_open; ccode[']']=macro_arg_close; +@z + ++ + +@x section 39 +@d isxalpha(c) ((c)=='_') /* non-alpha character allowed in identifier */ +@y +@d isxalpha(c) ((c)=='_' || (c)=='$') + /* non-alpha characters allowed in identifier */ +@z + +++ + +@x section 40 +eight_bits +get_next() /* produces the next input token */ +{@+eight_bits c; /* the current character */ + while (1) { + @<Check if we're at the end of a preprocessor command@>; + if (loc>limit && get_line()==0) return(new_section); + c=*(loc++); + if (xisdigit(c) || c=='\\' || c=='.') @<Get a constant@>@; + else if (c=='\'' || c=='"' || (c=='L'&&(*loc=='\'' || *loc=='"'))@| + || (c=='<' && sharp_include_line==1)) + @<Get a string@>@; + else if (xisalpha(c) || isxalpha(c) || ishigh(c)) + @<Get an identifier@>@; + else if (c=='@@') @<Get control code and possible section name@>@; + else if (xisspace(c)) continue; /* ignore spaces and tabs */ + if (c=='#' && loc==buffer+1) @<Raise preprocessor flag@>; + mistake: @<Compress two-symbol operator@>@; + return(c); + } +} +@y +eight_bits +get_next() /* produces the next input token */ +{@+eight_bits c; /* the current character */ + int blank_count=0; /* number of leading blanks */ + + if (loc<limit && preprocessing && (define_pos==2 || define_pos==4) && + xisspace(*loc)) { + define_pos=5; + return(preproc_space); + } + + while (1) { + @<Check if we're at the end of a preprocessor command@>; + if (loc>limit && get_line()==0) return(new_section); + + if(loc==buffer) { + blank_count=0; + while((*loc==' ' || *loc=='\t') && loc<limit) { + blank_count++; loc++; + } + } + + c=*(loc++); + if (xisdigit(c) || c=='\\' || c=='.') @<Get a constant@>@; + else if (c=='\'' || c=='"' || (c=='L'&&(*loc=='\'' || *loc=='"'))@| + || (c=='<' && sharp_include_line==1)) + @<Get a string@>@; + else if (xisalpha(c) || isxalpha(c) || ishigh(c)) + @<Get an identifier@>@; + else if (c=='@@') @<Get control code and possible section name@>@; + else if (xisspace(c)) continue; /* ignore spaces and tabs */ + if (c=='#' && loc==buffer+1+blank_count && !preprocessing) + @<Raise preprocessor flag@>; + mistake: @<Compress two-symbol operator@>@; + if(preprocessing) { + if (c=='(' && define_pos==2 && !xisspace(*(loc-2))) + define_pos=3; + else if (c==')' && define_pos==3) + define_pos=4; + } + return(c); + } +} +@z + +++ + +@x section 41 +@d left_preproc ord /* begins a preprocessor command */ +@d right_preproc 0217 /* ends a preprocessor command */ + +@<Glob...@>= +boolean preprocessing=0; /* are we scanning a preprocessor command? */ +@y +@d left_preproc ord /* begins a preprocessor command */ +@d right_preproc 0217 /* ends a preprocessor command */ +@d preproc_space '\b' /* emitted before the macro sustitution */ +@d numb_numb '\f' /* `\.{\#\#}'\,; the concatenation operator */ + +@<Glob...@>= +boolean preprocessing=0; /* are we scanning a preprocessor command? */ +int define_pos=0; + /* at which position we are while scanning a macro definition */ +@z + +++ + +@x section 45 + if (loc>=limit && preprocessing) { + preprocessing=sharp_include_line=0; + return(right_preproc); + } +@y + if (loc>=limit && preprocessing) { + preprocessing=sharp_include_line=define_pos=0; + return(right_preproc); + } +@z + +++ + +@x section 46 + case '!': if (*loc=='=') compress(not_eq); break; +@y + case '!': if (*loc=='=') compress(not_eq); break; + case '#': if (*loc=='#') compress(numb_numb); break; +@z + +++ + +@x section 47 + id_first=--loc; + while (isalpha(*++loc) || isdigit(*loc) || isxalpha(*loc) || ishigh(*loc)); + id_loc=loc; return(identifier); +@y + id_first=--loc; + + if (preprocessing) { + if (define_pos==1) + define_pos=2; + else if (strncmp(loc, "define", 6)==0 && xisspace(*(loc+6))) + define_pos=1; + } + + while (isalpha(*++loc) || isdigit(*loc) || isxalpha(*loc) || ishigh(*loc)); + id_loc=loc; return(identifier); +@z + +! + +@x section 77 +int out_line; /* number of next line to be output */ +@y +int out_line; /* number of next line to be output */ + +boolean in_math = 0; +boolean escape = 0; +int math_brace_count = 0; +@z + +* + +@x section 80 +out_ptr=out_buf+1; out_line=1; active_file=tex_file; +*out_ptr='c'; tex_printf("\\input cwebma"); +@y +out_ptr=out_buf+1; out_line=1; active_file=tex_file; +if(alternative) { + *out_ptr='a'; + tex_printf("\\input cwebmac"); +} else { + *out_ptr='c'; + tex_printf("\\input cwebma"); +} +@z + +! + +@x section 81 +@d out(c) {if (out_ptr>=out_buf_end) break_out(); *(++out_ptr)=c;} + +@c +@y +@c +void +out(c) +char c; +{ + if (out_ptr >= out_buf_end) + break_out(); + + if(!escape) { + if(in_math) { + if(c == '{') + math_brace_count++; + else if(c == '}') { + math_brace_count--; + if(math_brace_count < 0) { + math_brace_count = 0; + return; + } + } + } + if(c == '$') { + while(math_brace_count > 0) { + *(++out_ptr) = '}'; + math_brace_count--; + } + in_math = 1 - in_math; + } + } + + if(c == '\\' && !escape) + escape = 1; + else + escape = 0; + + *(++out_ptr) = c; + return; +} +@z + +* + +@x section 97 +@d tag 29 /* denotes a statement label */ +@y +@d tag 29 + /* denotes a statement label (if |alternative| is set, only a case label) */ +@z + +*, ++ + +@x section 97 +@d end_arg 59 /* \.{@@]} */ +@y +@d end_arg 59 /* \.{@@]} */ +@d label 60 /* denotes a label; used if |alternative| is set */ +@d do_head 61 /* used if |alternative| is set */ +@d pp_space 62 /* denotes the start point of a macro replacement text */ +@z + +*, ++ + +@x section 98 + strcpy(cat_name[end_arg],@q[@>"@@]"); +@y + strcpy(cat_name[end_arg],@q[@>"@@]"); + strcpy(cat_name[label],"label"); /* used if |alternative| is set */ + strcpy(cat_name[do_head], "do_head"); /* used if |alternative| is set */ + strcpy(cat_name[pp_space], "#_"); +@z + +++ section 101 + +@x +\.:&|colon|: \.:&maybe\cr +\.\# (within line)&|unorbinop|: \.{\\\#}&yes\cr +@y +\.:&|colon|: \.:&maybe\cr +\.{\#\#}&|binop|: \.{\\NN}&yes\cr +\.\# (within line)&|unorbinop|: \.{\\\#}&yes\cr +@z + +* + +@x section 101 +@i prod.w +@y +@i prod-alt.w +@z + +* + +@x section 110 + case raw_int: @<Cases for |raw_int|@>; @+break; +@y + case raw_int: @<Cases for |raw_int|@>; @+break; + case label: @<Cases for |label|@>; @+break; + case do_head: @<Cases for |do_head|@>; @+break; +@z + +++ + +@x section 112 +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))==no_ident_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) { + if (**(p->trans)==tok_value) { + p->cat=raw_int; + **(p->trans)=tok_value%id_flag+res_flag; + } + } + } + (name_dir+(sixteen_bits)(tok_value%id_flag))->ilk=raw_int; + *tok_loc=tok_value%id_flag+res_flag; +} +@y +void +make_reserved(p, cat) /* make the first identifier in |p->trans| like |cat| */ +scrap_pointer p; +eight_bits cat; +{ + 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))==no_ident_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 || p->cat==int_like || p->cat==const_like) { + if (**(p->trans)==tok_value) { + p->cat=cat; + **(p->trans)=tok_value%id_flag+res_flag; + } + } + } + (name_dir+(sixteen_bits)(tok_value%id_flag))->ilk=cat; + *tok_loc=tok_value%id_flag+res_flag; +} +@z + +* + +@x section 117 + make_underlined (pp); squash(pp,2,tag,0,7); +@y + make_underlined (pp); + if(alternative) + squash(pp,2,label,0,7); + else + squash(pp,2,tag,0,7); +@z + ++++ + +@x section 125 + else squash(pp,2,int_like,-2,32); +} +@y + else squash(pp,2,int_like,-2,32); +} +else if (cat1==binop && cat2==exp) squash(pp,3,int_like,-2,117); +@z + +++ + +@x section 128 +if (cat1==decl_head) + if ((cat2==exp&&cat3!=lpar&&cat3!=exp)||cat2==int_like) { + make_underlined(pp+2); make_reserved(pp+2); + big_app2(pp+1); reduce(pp+1,2,decl_head,0,42); + } + else if (cat2==semi) { + big_app1(pp); big_app(' '); big_app2(pp+1); reduce(pp,3,decl,-1,43); + } +@y +if (cat1==decl_head) { + if ((cat2==exp&&cat3!=lpar&&cat3!=exp)||cat2==int_like) { + make_underlined(pp+2); make_reserved(pp+2, raw_int); + big_app2(pp+1); reduce(pp+1,2,decl_head,0,42); + } + else if (cat2==semi) { + big_app1(pp); big_app(' '); big_app2(pp+1); reduce(pp,3,decl,-1,43); + } +} +else if (cat1==exp && !preprocessing && cat2!=lpar) { + make_reserved(pp+1, raw_int); + squash(pp+1,1,raw_int,-2,116); +} +@z + +* + +@x section 129 +if (cat1==lbrace) { + big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,struct_head,0,44); +} +else if (cat1==exp||cat1==int_like) { + if (cat2==lbrace || cat2==semi) { + make_underlined(pp+1); make_reserved(pp+1); + big_app1(pp); big_app(' '); big_app1(pp+1); + if (cat2==semi) reduce(pp,2,decl_head,0,45); + else { + big_app(' '); big_app1(pp+2);reduce(pp,3,struct_head,0,46); + } + } + else if (cat2==colon) squash(pp+2,1,base,-1,47); + else if (cat2!=base) { + big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,int_like,-2,48); + } +} +@y +if (cat1==lbrace) { + if(alternative) { + big_app1(pp); big_app(indent); big_app(force); big_app(backup); + big_app1(pp+1); reduce(pp,2,struct_head,0,44); + } else { + big_app1(pp); big_app(' '); big_app1(pp+1); + reduce(pp,2,struct_head,0,44); + } +} +else if (cat1==exp||cat1==int_like) { + if (cat2==lbrace || cat2==semi) { + make_underlined(pp+1); make_reserved(pp+1, raw_int); + big_app1(pp); big_app(' '); big_app1(pp+1); + if (cat2==semi) reduce(pp,2,decl_head,-1,45); + else { + if(alternative) { + big_app(indent); big_app(force); big_app(backup); big_app1(pp+2); + reduce(pp,3,struct_head,0,46); + } else { + big_app(' '); big_app1(pp+2); reduce(pp,3,struct_head,0,46); + } + } + } + else if (cat2==colon) squash(pp+2,1,base,-1,47); + else if (!(cat2==base || cat2==raw_int || + cat2==int_like || cat2==const_like)) { + big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,int_like,-2,48); + } +} +@z + +* + +@x section 130 + big_app1(pp); big_app(indent); big_app(force); big_app1(pp+1); + big_app(outdent); big_app(force); big_app1(pp+2); + reduce(pp,3,int_like,-2,49); +@y + if(alternative) { + big_app1(pp); big_app1(pp+1); + big_app(force); big_app(backup); big_app1(pp+2); big_app(' '); + big_app(outdent); reduce(pp,3,int_like,-2,49); + } else { + big_app1(pp); big_app(indent); big_app(force); big_app1(pp+1); + big_app(outdent); big_app(force); big_app1(pp+2); + reduce(pp,3,int_like,-2,49); + } +@z + +* + +@x section 133 + big_app(force); big_app1(pp); big_app(indent); big_app(force); + big_app1(pp+1); big_app(force); big_app(backup); big_app1(pp+2); + big_app(outdent); big_app(force); reduce(pp,3,stmt,-1,55); +@y + if(alternative) { + big_app(indent); big_app(force); big_app(backup); big_app1(pp); + app(noop); big_app(cancel); big_app1(pp+1); big_app(force); big_app(backup); + big_app1(pp+2); big_app(outdent); big_app(force); reduce(pp,3,stmt,-2,55); + } else { + big_app(force); big_app1(pp); big_app(indent); big_app(force); + big_app1(pp+1); big_app(force); big_app(backup); big_app1(pp+2); + big_app(outdent); big_app(force); reduce(pp,3,stmt,-1,55); + } +@z + +* + +@x section 136 + big_app(force); big_app1(pp); big_app(indent); big_app(break_space); + big_app1(pp+1); big_app(outdent); big_app(force); + reduce(pp,2,stmt,-1,60); +@y + if(alternative) { + big_app(force); big_app1(pp); big_app(indent); big_app(force); + big_app1(pp+1); big_app(outdent); big_app(force); + reduce(pp,2,stmt,-1,60); + } else { + big_app(force); big_app1(pp); big_app(indent); big_app(break_space); + big_app1(pp+1); big_app(outdent); big_app(force); + reduce(pp,2,stmt,-1,60); + } +@z + +* + +@x section 137 + big_app(force); big_app1(pp); big_app(break_space); app(noop); + big_app(cancel); big_app1(pp+1); big_app(force); + reduce(pp,2,stmt,-1,61); +@y + if(alternative) { + big_app(force); big_app1(pp); big_app(break_space); + big_app1(pp+1); big_app(force); + reduce(pp,2,stmt,-1,61); + } else { + big_app(force); big_app1(pp); big_app(break_space); app(noop); + big_app(cancel); big_app1(pp+1); big_app(force); + reduce(pp,2,stmt,-1,61); + } +@z + +* + +@x section 138 + big_app(force); big_app1(pp); big_app(indent); big_app(break_space); + big_app1(pp+1); big_app(outdent); big_app(force); big_app1(pp+2); + if (cat3==if_like) { + big_app(' '); big_app1(pp+3); reduce(pp,4,if_like,0,63); + }@+else reduce(pp,3,else_like,0,64); +@y + if(alternative) { + big_app(force); big_app1(pp); big_app(indent); big_app(force); + big_app1(pp+1); big_app(outdent); big_app(force); big_app1(pp+2); + } else { + big_app(force); big_app1(pp); big_app(indent); big_app(break_space); + big_app1(pp+1); big_app(outdent); big_app(force); big_app1(pp+2); + } + if (cat3==if_like) { + big_app(' '); big_app1(pp+3); reduce(pp,4,if_like,0,63); + }@+else reduce(pp,3,else_like,0,64); +@z + +* + +@x section 139 + big_app(force); big_app1(pp); big_app(break_space); app(noop); + big_app(cancel); big_app1(pp+1); big_app(force); big_app1(pp+2); + if (cat3==if_like) { + big_app(' '); big_app1(pp+3); reduce(pp,4,if_like,0,66); + }@+else reduce(pp,3,else_like,0,67); +@y + if(alternative) { + big_app(force); big_app1(pp); big_app(break_space); + big_app1(pp+1); big_app(force); big_app1(pp+2); + } else { + big_app(force); big_app1(pp); big_app(break_space); app(noop); + big_app(cancel); big_app1(pp+1); big_app(force); big_app1(pp+2); + } + if (cat3==if_like) { + big_app(' '); big_app1(pp+3); reduce(pp,4,if_like,0,66); + }@+else reduce(pp,3,else_like,0,67); +@z + +* + +@x section 140 +if (cat1==stmt && cat2==else_like && cat3==semi) { + big_app1(pp); big_app(break_space); app(noop); big_app(cancel); + big_app1(pp+1); big_app(cancel); app(noop); big_app(break_space); + big_app2(pp+2); reduce(pp,4,stmt,-1,69); +} +@y +if(alternative) { + if (cat1==lbrace) squash(pp,1,do_head,0,114); + else if (cat1==stmt && cat2==else_like && cat3==semi) { + big_app(force); big_app1(pp); big_app(indent); big_app(force); + big_app1(pp+1); big_app(cancel); app(noop); + big_app(break_space); big_app2(pp+2); big_app(outdent); big_app(force); + reduce(pp,4,stmt,-1,69); + } +} else { + if (cat1==stmt && cat2==else_like && cat3==semi) { + big_app1(pp); big_app(break_space); app(noop); big_app(cancel); + big_app1(pp+1); big_app(cancel); app(noop); big_app(break_space); + big_app2(pp+2); reduce(pp,4,stmt,-1,69); + } +} +@z + +* + +@x section 141 +if (cat1==semi) squash(pp,2,stmt,-1,70); +else if (cat1==colon) squash(pp,2,tag,-1,71); +@y +if (cat1==semi) squash(pp,2,stmt,-3,70); +else if (cat1==colon) squash(pp,2,tag,-2,71); +@z + +* + +@x section 141 + reduce(pp,3,tag,-1,73); +@y + reduce(pp,3,tag,-2,73); +@z + +* + +@x section 142 +if (cat1==tag) { + big_app1(pp); big_app(break_space); big_app1(pp+1); reduce(pp,2,tag,-1,74); +} +else if (cat1==stmt||cat1==decl||cat1==function) { + big_app(force); big_app(backup); big_app1(pp); big_app(break_space); + big_app1(pp+1); reduce(pp,2,cat1,-1,75); +} +@y +if(alternative) { + if (cat1==tag) { + big_app1(pp); big_app(force); big_app1(pp+1); reduce(pp,2,tag,-1,74); + } + else if ((cat1==stmt||cat1==decl||cat1==function) && + (cat2==rbrace||cat2==tag)) { + big_app(force); big_app1(pp); big_app(indent); big_app(force); + big_app1(pp+1); big_app(outdent); reduce(pp,2,cat1,-1,75); + } +} else { + if (cat1==tag) { + big_app1(pp); big_app(break_space); big_app1(pp+1); reduce(pp,2,tag,-1,74); + } + else if (cat1==stmt||cat1==decl||cat1==function) { + big_app(force); big_app(backup); big_app1(pp); big_app(break_space); + big_app1(pp+1); reduce(pp,2,cat1,-1,75); + } +} +@z + +++ + +@x section 145 +} else if (cat1==exp || cat1==function) { + if (cat2==rproc) { + app(inserted); big_app1(pp); big_app(' '); big_app2(pp+1); + reduce(pp,3,insert,-1,80); + } + else if (cat2==exp && cat3==rproc && cat1==exp) { + app(inserted); big_app1(pp); big_app(' '); big_app1(pp+1); app_str(" \\5"); +@.\\5@> + big_app2(pp+2); reduce(pp,4,insert,-1,80); + } +} +@y +} else if ((cat1==exp || cat1==function || cat1==int_like) && cat2==rproc) { + app(inserted); big_app1(pp); big_app(' '); big_app2(pp+1); + reduce(pp,3,insert,-1,80); +} else if ((cat1==exp || cat1==int_like || cat1==const_like) && + cat2==pp_space) { + if(cat3==int_like || cat3==const_like || cat3==raw_int) + make_reserved(pp+1, (pp+3)->cat); + big_app1(pp); big_app(' '); big_app1(pp+1); app_str(" \\5"); +@.\\5@> + reduce(pp,3,lproc,-1,119); +} else if (cat1==exp && cat2==exp && cat3==pp_space) { + big_app1(pp); big_app(' '); big_app2(pp+1); app_str(" \\5"); + reduce(pp,4,lproc,-1,120); +} +@z + +* + +@x section 151 +if (cat1==colon) squash(pp,2,tag,-1,90); +@y +if (cat1==colon) squash(pp,2,tag,-2,90); +@z + ++++ + +@x section 154 +if (cat1==binop || cat1==unop || cat1==unorbinop) { + if (cat2==binop) break; + big_app1(pp); big_app('{'); big_app1(pp+1); big_app('}'); + reduce(pp,2,exp,-2,97); +} +else if (cat1==new_like || cat1==sizeof_like) { + big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,exp,-2,98); +} +@y +if (cat1==binop || cat1==unop || cat1==unorbinop || cat1==raw_unorbin) { + if (cat2==binop) break; + big_app1(pp); big_app('{'); big_app1(pp+1); big_app('}'); + reduce(pp,2,exp,-2,97); +} +else if (cat1==new_like || cat1==sizeof_like) { + big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,exp,-2,98); +} +else if (cat1==raw_int || cat1==int_like || cat1==const_like) { + big_app1(pp); big_app(' '); big_app1(pp+1); + reduce(pp,2,operator_like,-2,118); +} +@z + +*** This makes the tracing info looking smoother + +@x section 168 + if (tracing==2) { + printf("\n%d:",n); +@y + if (tracing==2) { + printf("\n%3d:",n); +@z + +**, ++ + +@x section 175 +@ @<Append the scr...@>= +@<Make sure that there is room for the new scraps, tokens, and texts@>; +@y +@ +@d alternative flags['a'] /* if set, an alternative format is produced */ +@d func_index flags['i'] + /* should functions in the index have |()| appended? */ + +@<Append the scr...@>= +@<Make sure that there is room for the new scraps, tokens, and texts@>; +@z + +*, ** + +@x section 175 + case '(': case '[': app(next_control); app_scrap(lpar,maybe_math);@+break; + case ')': case ']': app(next_control); app_scrap(raw_rpar,maybe_math);@+break; + case '{': app_str("\\{"@q}@>); app_scrap(lbrace,yes_math);@+break; +@.\\\{@>@q}@> + case '}': app_str(@q{@>"\\}"); app_scrap(rbrace,yes_math);@+break; +@q{@>@.\\\}@> +@y + case '(': if(func_index) + if(*(tok_ptr-1)>=id_flag && *(tok_ptr-1)<res_flag) + /* an identifier? */ + (name_dir+ *(tok_ptr-1)%10240)->func_flag=1; + case '[': app(next_control); app_scrap(lpar,maybe_math);@+break; + case ')': case ']': app(next_control); app_scrap(raw_rpar,maybe_math);@+break; + case '{': if(alternative) + app_str("\\LBR"); + else + app_str("\\{"@q}@>); + app_scrap(lbrace,yes_math);@+break; +@.\\\{@>@q}@> + case '}': if(alternative) + app_str("\\RBR"); + else + app_str(@q{@>"\\}"); + app_scrap(rbrace,yes_math);@+break; +@q{@>@.\\\}@> +@z + +++ + +@x section 175 + case left_preproc: app(force); app(preproc_line); + app_str("\\#"); app_scrap(lproc,no_math);@+break; +@.\\\#@> + case right_preproc: app(force); app_scrap(rproc,no_math);@+break; +@y + case left_preproc: app(force); app(preproc_line); + app_str("\\#"); app_scrap(lproc,no_math);@+break; +@.\\\#@> + case preproc_space: app(' '); app_scrap(pp_space, maybe_math);@+break; + case right_preproc: app(force); app_scrap(rproc,no_math);@+break; +@z + +++ + +@x section 175 + case pseudo_semi: app_scrap(semi,maybe_math);@+break; +@y + case pseudo_semi: app_scrap(semi,maybe_math);@+break; + case pseudo_lbrace: app_str("\\phantom{"); + if(alternative) + app_str("\\LBR"); + else + app_str("\\{"@q}@>); + app_str("}"); + app_scrap(lbrace,yes_math); + break; + case pseudo_rbrace: app_str("\\phantom{"); + if(alternative) + app_str("\\RBR"); + else + app_str(@q{@>"\\}"); + app_str("}"); + app_scrap(rbrace,yes_math); + break; +@z + +++ + +@x section 177 +case minus_gt_ast: app_str("\\MGA");@+app_scrap(binop,yes_math);@+break; +@.\\MGA@> +@y +case minus_gt_ast: app_str("\\MGA");@+app_scrap(binop,yes_math);@+break; +@.\\MGA@> +case numb_numb: app_str("\\NN");@+app_scrap(binop,yes_math);@+break; +@.\\NN@> +@z + +** + +@x section 178 +@.\\\%@> +@.\\\$@> +@.\\\^@> +@y +@.\\\%@> +@.\\$@> @q CWEAVE quotes a dollar sign! @> +@.\\\^@> +@z + +! + +@x section 195 + } else if (is_tiny(cur_name)) out('|') +@y + } else if (is_tiny(cur_name)) out('|'); +@z + +! + +@x section 195 +else out('&') /* |a==res_word| */ +@y +else out('&'); /* |a==res_word| */ +@z + +! + +@x section 196 + if (b!='0' || force_lines==0) out(b)@; +@y + if (b!='0' || force_lines==0) out(b); +@z + +** + +@x section 200 +@.\\\%@> +@.\\\$@> +@.\\\^@> +@y +@.\\\%@> +@.\\$@> @q CWEAVE quotes a dollar symbol! @> +@.\\\^@> +@z + +! + +@x section 200 + else if (b!='|') out(b) +@y + else if (b!='|') out(b); +@z + +++ + +@x section 209 + case thin_space: case math_break: case ord: + case line_break: case big_line_break: case no_line_break: case join: + case pseudo_semi: case macro_arg_open: case macro_arg_close: + case output_defs_code: + err_print("! You can't do that in TeX text"); break; +@.You can't do that...@> +@y + case thin_space: case math_break: case ord: + case line_break: case big_line_break: case no_line_break: case join: + case pseudo_semi: case macro_arg_open: case macro_arg_close: + case pseudo_lbrace: case pseudo_rbrace: + case output_defs_code: + err_print("! You can't do that in TeX text"); break; +@.You can't do that...@> +@z + +** + +@x section 213 + if (*loc=='(') + reswitch: switch (next_control=get_next()) { + case '(': case ',': app(next_control); goto reswitch; + case identifier: app_cur_id(0); goto reswitch; + case ')': app(next_control); next_control=get_next(); break; + default: err_print("! Improper macro definition"); break; + } +@y + if (*loc=='(') { + if(func_index) + (name_dir+ *(tok_ptr-1)%10240)->func_flag=1; + reswitch: switch (next_control=get_next()) { + case '(': case ',': app(next_control); goto reswitch; + case identifier: app_cur_id(0); goto reswitch; + case ')': app(next_control); next_control=get_next(); break; + default: err_print("! Improper macro definition"); break; + } + } +@z + +** + +@x section 241 +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; + out_str("\\."); break; +lowcase: out_str("\\\\"); + } + break; +@.\\|@> +@.\\.@> +@.\\\\@> + case roman: break; + case wildcard: out_str("\\9"); break; +@.\\9@> + case typewriter: out_str("\\."); break; +@.\\.@> + case custom: case quoted: {char *j; out_str("$\\"); + for (j=cur_name->byte_start;j<(cur_name+1)->byte_start;j++) + out(isxalpha(*j)? 'x' : *j); + out('$'); + goto name_done; + } + default: out_str("\\&"); +@.\\\&@> +} +out_name(cur_name); +name_done: +@y +switch (cur_name->ilk) { + case normal: + if (is_tiny(cur_name) && !cur_name->func_flag) + 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: case quoted: {char *j; out_str("$\\"); + for (j=cur_name->byte_start;j<(cur_name+1)->byte_start;j++) + out(isxalpha(*j)? 'x' : *j); + out('$'); + goto name_done; + } + default: out_str("\\&"); +@.\\\&@> +} +out_name(cur_name); +name_done: + if(cur_name->func_flag) + out_str("(\\,)"); +@z + +* + +@x section 249 +@** Index. +@y +@ @<Cases for |label|@>= +if (cat1==label) { + big_app1(pp); big_app(force); big_app1(pp+1); reduce(pp,2,label,-1,112); +} +else if (cat1==stmt||cat1==decl||cat1==function) { + big_app(cancel); app(noop); + big_app(outdent); big_app(outdent); big_app(force); + big_app1(pp); big_app(indent); big_app(indent); big_app(force); + big_app1(pp+1); reduce(pp,2,cat1,-1,113); +} + +@ @<Cases for |do_head|@>= +if (cat1==stmt && cat2==else_like && cat3==semi) { + big_app1(pp); big_app(break_space); big_app1(pp+1); big_app(cancel); + app(noop); big_app(break_space); big_app2(pp+2); reduce(pp,4,stmt,-1,115); +} + +@** Index. +@z + +--- end of cweave.ch --- diff --git a/web/c_cpp/c2cweb/cweb/cweave.def b/web/c_cpp/c2cweb/cweb/cweave.def new file mode 100644 index 0000000000..71fd302fae --- /dev/null +++ b/web/c_cpp/c2cweb/cweb/cweave.def @@ -0,0 +1,3 @@ +NAME CWEAVE WINDOWCOMPAT +DESCRIPTION 'CWEAVE 3.2' +STACKSIZE 0x80000 diff --git a/web/c_cpp/c2cweb/cweb/cweave.w b/web/c_cpp/c2cweb/cweb/cweave.w new file mode 100644 index 0000000000..6c08b6bc80 --- /dev/null +++ b/web/c_cpp/c2cweb/cweb/cweave.w @@ -0,0 +1,4505 @@ +% 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.4 --- April 1995 + +% Copyright (C) 1987,1990,1993 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. + +% 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 +\def\dleft{[\![} \def\dright{]\!]} % double brackets +\mathchardef\RA="3221 % right arrow +\mathchardef\BA="3224 % double arrow +\def\({} % ) kludge for alphabetizing certain section names +\def\TeXxstring{\\{\TEX/\_string}} +\def\skipxTeX{\\{skip\_\TEX/}} +\def\copyxTeX{\\{copy\_\TEX/}} + +\def\title{CWEAVE (Version 3.4)} +\def\topofcontents{\null\vfill + \centerline{\titlefont The {\ttitlefont CWEAVE} processor} + \vskip 15pt + \centerline{(Version 3.4)} + \vfill} +\def\botofcontents{\vfill +\noindent +Copyright \copyright\ 1987, 1990, 1993 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 \.{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 \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 \.{CWEAVE} +is modified. + +@d banner "This is CWEAVE (Version 3.4)\n" + +@c @<Include files@>@/ +@h +@<Common code for \.{CWEAVE} and \.{CTANGLE}@>@/ +@<Typedef declarations@>@/ +@<Global variables@>@/ +@<Predeclaration of procedures@> + +@ We predeclare several standard system functions here instead of including +their system header files, because the names of the header files are not as +standard as the names of the functions. (For example, some \CEE/ environments +have \.{<string.h>} where others have \.{<strings.h>}.) + +@<Predecl...@>= +extern int strlen(); /* length of string */ +extern int strcmp(); /* compare strings lexicographically */ +extern char* strcpy(); /* copy one string to another */ +extern int strncmp(); /* compare up to $n$ string characters */ +extern char* strncpy(); /* copy up to $n$ string characters */ + +@ \.{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 +file, finally it sorts and outputs the index. + +Please read the documentation for \.{common}, the set of routines common +to \.{CTANGLE} and \.{CWEAVE}, before proceeding further. + +@c +int main (ac, av) +int ac; /* argument count */ +char **av; /* argument values */ +{ + argc=ac; argv=av; + program=cweave; + make_xrefs=force_lines=1; /* controlled by command-line options */ + common_init(); + @<Set initial values@>; + if (show_banner) printf(banner); /* print a ``banner line'' */ + @<Store all the reserved words@>; + phase_one(); /* read all the user's text and store the cross-references */ + phase_two(); /* read all the text again and translate it to \TEX/ form */ + phase_three(); /* output the cross-reference index */ + return wrap_up(); /* and exit gracefully */ +} + +@ The following parameters were sufficient in the original \.{WEAVE} to +handle \TEX/, so they should be sufficient for most applications of \.{CWEAVE}. +If you change |max_bytes|, |max_names|, |hash_size| or |buf_size| +you have to change them also in the file |"common.w"|. + +@d max_bytes 90000 /* the number of bytes in identifiers, + index entries, and section names */ +@d max_names 4000 /* number of identifiers, strings, section names; + must be less than 10240; used in |"common.w"| */ +@d max_sections 2000 /* greater than the total number of sections */ +@d hash_size 353 /* should be prime */ +@d buf_size 100 /* maximum length of input line, plus one */ +@d longest_name 1000 /* section names and strings shouldn't be longer than this */ +@d long_buf_size (buf_size+longest_name) +@d line_length 80 /* lines of \TEX/ output have at most this many characters; + should be less than 256 */ +@d max_refs 20000 /* number of cross-references; must be less than 65536 */ +@d max_toks 20000 /* number of symbols in \CEE/ texts being parsed; + must be less than 65536 */ +@d max_texts 4000 /* number of phrases in \CEE/ texts being parsed; + must be less than 10240 */ +@d max_scraps 2000 /* number of tokens in \CEE/ texts being parsed */ +@d stack_size 400 /* number of simultaneous output levels */ + +@ 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 needs to be updated when |"common.w"| changes. + +@i common.h + +@* Data structures exclusive to {\tt CWEAVE}. +As explained in \.{common.w}, the field of a |name_info| structure +that contains the |rlink| of a section name is used for a completely +different purpose in the case of identifiers. It is then called the +|ilk| of the identifier, and it is used to +distinguish between various types of identifiers, as follows: + +\yskip\hang |normal| identifiers are part of the \CEE/ program and +will appear in italic type. + +\yskip\hang |roman| identifiers are index entries that appear after +\.{@@\^} in the \.{CWEB} file. + +\yskip\hang |wildcard| identifiers are index entries that appear after +\.{@@:} in the \.{CWEB} file. + +\yskip\hang |typewriter| identifiers are index entries that appear after +\.{@@.} in the \.{CWEB} file. + +\yskip\hang |else_like|, \dots, |typedef_like| +identifiers are \CEE/ reserved words whose |ilk| explains how they are +to be treated when \CEE/ code is being formatted. + +@d ilk dummy.Ilk +@d normal 0 /* ordinary identifiers have |normal| ilk */ +@d roman 1 /* normal index entries have |roman| ilk */ +@d wildcard 2 /* user-formatted index entries have |wildcard| ilk */ +@d typewriter 3 /* `typewriter type' entries have |typewriter| ilk */ +@d abnormal(a) (a->ilk>typewriter) /* tells if a name is special */ +@d custom 4 /* identifiers with user-given control sequence */ +@d unindexed(a) (a->ilk>custom) /* tells if uses of a name are to be indexed */ +@d quoted 5 /* \.{NULL} */ +@d else_like 26 /* \&{else} */ +@d public_like 40 /* \&{public}, \&{private}, \&{protected} */ +@d operator_like 41 /* \&{operator} */ +@d new_like 42 /* \&{new} */ +@d catch_like 43 /* \&{catch} */ +@d for_like 45 /* \.{for}, \&{switch}, \&{while} */ +@d do_like 46 /* \&{do} */ +@d if_like 47 /* \&{if}, \&{ifdef}, \&{endif}, \&{pragma}, \dots */ +@d raw_rpar 48 /* `\.)' or `\.]' when looking for \&{const} following */ +@d raw_unorbin 49 /* `\.\&' or `\.*' when looking for \&{const} following */ +@d const_like 50 /* \&{const}, \&{volatile} */ +@d raw_int 51 /* \&{int}, \&{char}, \&{extern}, \dots */ +@d int_like 52 /* same, when not followed by left parenthesis */ +@d case_like 53 /* \&{case}, \&{return}, \&{goto}, \&{break}, \&{continue} */ +@d sizeof_like 54 /* \&{sizeof} */ +@d struct_like 55 /* \&{struct}, \&{union}, \&{enum}, \&{class} */ +@d typedef_like 56 /* \&{typedef} */ +@d define_like 57 /* \&{define} */ + +@ 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. + +@<Global...@>= +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 +|p->xref|, which points into the |xmem| array. The elements of |xmem| +are structures consisting of an integer, |num|, and a pointer |xlink| +to another element of |xmem|. If |x=p->xref| is a pointer into |xmem|, +the value of |x->num| is either a section number where |p| is used, +or |cite_flag| plus a section number where |p| is mentioned, +or |def_flag| plus a section number where |p| is defined; +and |x->xlink| points to the next such cross-reference for |p|, +if any. This list of cross-references is in decreasing order by +section number. The next unused slot in |xmem| is |xref_ptr|. +The linked list ends at |&xmem[0]|. + +The global variable |xref_switch| is set either to |def_flag| or to zero, +depending on whether the next cross-reference to an identifier is to be +underlined or not in the index. This switch is set to |def_flag| when +\.{@@!} or \.{@@d} is scanned, and it is cleared to zero when +the next identifier or index entry cross-reference has been made. +Similarly, the global variable |section_xref_switch| is either +|def_flag| or |cite_flag| or zero, depending +on whether a section name is being defined, cited or used in \CEE/ text. + +@<Type...@>= +typedef struct xref_info { + sixteen_bits num; /* section number plus zero or |def_flag| */ + struct xref_info *xlink; /* pointer to the previous cross-reference */ +} xref_info; +typedef xref_info *xref_pointer; + +@ @<Global...@>= +xref_info xmem[max_refs]; /* contains cross-reference information */ +xref_pointer xmem_end = xmem+max_refs-1; +xref_pointer xref_ptr; /* the largest occupied position in |xmem| */ +sixteen_bits xref_switch,section_xref_switch; /* either zero or |def_flag| */ + +@ A section that is used for multi-file output (with the \.{@@(} feature) +has a special first cross-reference whose |num| field is |file_flag|. + +@d file_flag (3*cite_flag) +@d def_flag (2*cite_flag) +@d cite_flag 10240 /* must be strictly larger than |max_sections| */ +@d xref equiv_or_xref + +@<Set init...@>= +xref_ptr=xmem; name_dir->xref=(char*)xmem; xref_switch=0; section_xref_switch=0; +xmem->num=0; /* sentinel value */ + +@ A new cross-reference for an identifier is formed by calling |new_xref|, +which discards duplicate entries and ignores non-underlined references +to one-letter identifiers or \CEE/'s reserved words. + +If the user has sent the |no_xref| flag (the \.{-x} option of the command line), +it is unnecessary to keep track of cross-references for identifiers. +If one were careful, one could probably make more changes around section +100 to avoid a lot of identifier looking up. + +@d append_xref(c) if (xref_ptr==xmem_end) overflow("cross-reference"); + else (++xref_ptr)->num=c; +@d no_xref (flags['x']==0) +@d make_xrefs flags['x'] /* should cross references be output? */ +@d is_tiny(p) ((p+1)->byte_start==(p)->byte_start+1) + +@c +void +new_xref(p) +name_pointer p; +{ + xref_pointer q; /* pointer to previous cross-reference */ + sixteen_bits m, n; /* new and previous cross-reference value */ + if (no_xref) return; + if ((unindexed(p) || is_tiny(p)) && xref_switch==0) return; + m=section_count+xref_switch; xref_switch=0; q=(xref_pointer)p->xref; + if (q != xmem) { + n=q->num; + if (n==m || n==m+def_flag) return; + else if (m==n+def_flag) { + q->num=m; return; + } + } + append_xref(m); xref_ptr->xlink=q; p->xref=(char*)xref_ptr; +} + +@ The cross-reference lists for section names are slightly different. +Suppose that a section name is defined in sections $m_1$, \dots, +$m_k$, cited in sections $n_1$, \dots, $n_l$, and used in sections +$p_1$, \dots, $p_j$. Then its list will contain $m_1+|def_flag|$, +\dots, $m_k+|def_flag|$, $n_1+|cite_flag|$, \dots, +$n_l+|cite_flag|$, $p_1$, \dots, $p_j$, in this order. + +Although this method of storage take quadratic time on the length of +the list, under foreseeable uses of \.{CWEAVE} this inefficiency is +insignificant. + +@c +void +new_section_xref(p) +name_pointer p; +{ + xref_pointer q,r; /* pointers to previous cross-references */ + q=(xref_pointer)p->xref; r=xmem; + if (q>xmem) + while (q->num>section_xref_switch) {r=q; q=q->xlink;} + if (r->num==section_count+section_xref_switch) + return; /* don't duplicate entries */ + append_xref(section_count+section_xref_switch); + xref_ptr->xlink=q; section_xref_switch=0; + if (r==xmem) p->xref=(char*)xref_ptr; + else r->xlink=xref_ptr; +} + +@ The cross-reference list for a section name may also begin with +|file_flag|. Here's how that flag gets put~in. + +@c +void +set_file_flag(p) +name_pointer p; +{ + xref_pointer q; + q=(xref_pointer)p->xref; + if (q->num==file_flag) return; + append_xref(file_flag); + xref_ptr->xlink = q; + p->xref = (char *)xref_ptr; +} + +@ A third large area of memory is used for sixteen-bit `tokens', which appear +in short lists similar to the strings of characters in |byte_mem|. Token lists +are used to contain the result of \CEE/ code translated into \TEX/ form; +further details about them will be explained later. A |text_pointer| variable +is an index into |tok_start|. + +@<Typed...@>= +typedef sixteen_bits token; +typedef token *token_pointer; +typedef token_pointer *text_pointer; + +@ The first position of |tok_mem| +that is unoccupied by replacement text is called |tok_ptr|, and the first +unused location of |tok_start| is called |text_ptr|. +Thus, we usually have |*text_ptr==tok_ptr|. + +@<Global...@>= +token tok_mem[max_toks]; /* tokens */ +token_pointer tok_mem_end = tok_mem+max_toks-1; /* end of |tok_mem| */ +token_pointer tok_start[max_texts]; /* directory into |tok_mem| */ +token_pointer tok_ptr; /* first unused position in |tok_mem| */ +text_pointer text_ptr; /* first unused position in |tok_start| */ +text_pointer tok_start_end = tok_start+max_texts-1; /* end of |tok_start| */ +token_pointer max_tok_ptr; /* largest value of |tok_ptr| */ +text_pointer max_text_ptr; /* largest value of |text_ptr| */ + +@ @<Set init...@>= +tok_ptr=tok_mem+1; text_ptr=tok_start+1; tok_start[0]=tok_mem+1; +tok_start[1]=tok_mem+1; +max_tok_ptr=tok_mem+1; max_text_ptr=tok_start+1; + +@ Here are the three procedures needed to complete |id_lookup|: +@c +int names_match(p,first,l,t) +name_pointer p; /* points to the proposed match */ +char *first; /* position of first character of string */ +int l; /* length of identifier */ +eight_bits t; /* desired ilk */ +{ + if (length(p)!=l) return 0; + if (p->ilk!=t && !(t==normal && abnormal(p))) return 0; + return !strncmp(first,p->byte_start,l); +} + +void +init_p(p,t) +name_pointer p; +eight_bits t; +{ + p->ilk=t; p->xref=(char*)xmem; +} + +void +init_node(p) +name_pointer p; +{ + p->xref=(char*)xmem; +} + +@ We have to get \CEE/'s +reserved words into the hash table, and the simplest way to do this is +to insert them every time \.{CWEAVE} is run. Fortunately there are relatively +few reserved words. (Some of these are not strictly ``reserved,'' but +are defined in header files of the ISO Standard \CEE/ Library.) +@^reserved words@> + +@<Store all the reserved words@>= +id_lookup("asm",NULL,sizeof_like); +id_lookup("auto",NULL,int_like); +id_lookup("break",NULL,case_like); +id_lookup("case",NULL,case_like); +id_lookup("catch",NULL,catch_like); +id_lookup("char",NULL,raw_int); +id_lookup("class",NULL,struct_like); +id_lookup("clock_t",NULL,raw_int); +id_lookup("const",NULL,const_like); +id_lookup("continue",NULL,case_like); +id_lookup("default",NULL,case_like); +id_lookup("define",NULL,define_like); +id_lookup("defined",NULL,sizeof_like); +id_lookup("delete",NULL,sizeof_like); +id_lookup("div_t",NULL,raw_int); +id_lookup("do",NULL,do_like); +id_lookup("double",NULL,raw_int); +id_lookup("elif",NULL,if_like); +id_lookup("else",NULL,else_like); +id_lookup("endif",NULL,if_like); +id_lookup("enum",NULL,struct_like); +id_lookup("error",NULL,if_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); +id_lookup("fpos_t",NULL,raw_int); +id_lookup("friend",NULL,int_like); +id_lookup("goto",NULL,case_like); +id_lookup("if",NULL,if_like); +id_lookup("ifdef",NULL,if_like); +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); +id_lookup("long",NULL,raw_int); +id_lookup("new",NULL,new_like); +id_lookup("NULL",NULL,quoted); +id_lookup("offsetof",NULL,sizeof_like); +id_lookup("operator",NULL,operator_like); +id_lookup("pragma",NULL,if_like); +id_lookup("private",NULL,public_like); +id_lookup("protected",NULL,public_like); +id_lookup("ptrdiff_t",NULL,raw_int); +id_lookup("public",NULL,public_like); +id_lookup("register",NULL,int_like); +id_lookup("return",NULL,case_like); +id_lookup("short",NULL,raw_int); +id_lookup("sig_atomic_t",NULL,raw_int); +id_lookup("signed",NULL,raw_int); +id_lookup("size_t",NULL,raw_int); +id_lookup("sizeof",NULL,sizeof_like); +id_lookup("static",NULL,int_like); +id_lookup("struct",NULL,struct_like); +id_lookup("switch",NULL,for_like); +id_lookup("template",NULL,int_like); +id_lookup("TeX",NULL,custom); +id_lookup("this",NULL,quoted); +id_lookup("throw",NULL,case_like); +id_lookup("time_t",NULL,raw_int); +id_lookup("try",NULL,else_like); +id_lookup("typedef",NULL,typedef_like); +id_lookup("undef",NULL,if_like); +id_lookup("union",NULL,struct_like); +id_lookup("unsigned",NULL,raw_int); +id_lookup("va_dcl",NULL,decl); /* Berkeley's variable-arg-list convention */ +id_lookup("va_list",NULL,raw_int); /* ditto */ +id_lookup("virtual",NULL,int_like); +id_lookup("void",NULL,raw_int); +id_lookup("volatile",NULL,const_like); +id_lookup("wchar_t",NULL,raw_int); +id_lookup("while",NULL,for_like); + +@* 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: +One simply skips to the next `\.{@@\ }' or `\.{@@*}' that begins a +section; another passes over the \TEX/ text at the beginning of a +section; the third passes over the \TEX/ text in a \CEE/ comment; +and the last, which is the most interesting, gets the next token of +a \CEE/ text. They all use the pointers |limit| and |loc| into +the line of input currently being studied. + +@ Control codes in \.{CWEB}, which begin with `\.{@@}', are converted +into a numeric code designed to simplify \.{CWEAVE}'s logic; for example, +larger numbers are given to the control codes that denote more significant +milestones, and the code of |new_section| should be the largest of +all. Some of these numeric control codes take the place of |char| +control codes that will not otherwise appear in the output of the +scanning routines. +@^ASCII code dependencies@> + +@d ignore 00 /* control code of no interest to \.{CWEAVE} */ +@d verbatim 02 /* takes the place of extended ASCII \.{\char2} */ +@d begin_short_comment 03 /* \CPLUSPLUS/ short comment */ +@d begin_comment '\t' /* tab marks will not appear */ +@d underline '\n' /* this code will be intercepted without confusion */ +@d noop 0177 /* takes the place of ASCII delete */ +@d xref_roman 0203 /* control code for `\.{@@\^}' */ +@d xref_wildcard 0204 /* control code for `\.{@@:}' */ +@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|. + +@<Global...@>= +eight_bits ccode[256]; /* meaning of a char following \.{@@} */ + +@ @<Set ini...@>= +{int c; for (c=0; c<256; c++) ccode[c]=0;} +ccode[' ']=ccode['\t']=ccode['\n']=ccode['\v']=ccode['\r']=ccode['\f'] + =ccode['*']=new_section; +ccode['@@']='@@'; /* `quoted' at sign */ +ccode['=']=verbatim; +ccode['d']=ccode['D']=definition; +ccode['f']=ccode['F']=ccode['s']=ccode['S']=format_code; +ccode['c']=ccode['C']=ccode['p']=ccode['P']=begin_C; +ccode['t']=ccode['T']=TeX_string; +ccode['l']=ccode['L']=translit_code; +ccode['q']=ccode['Q']=noop; +ccode['h']=ccode['H']=output_defs_code; +ccode['&']=join; ccode['<']=ccode['(']=section_name; +ccode['!']=underline; ccode['^']=xref_roman; +ccode[':']=xref_wildcard; ccode['.']=xref_typewriter; ccode[',']=thin_space; +ccode['|']=math_break; ccode['/']=line_break; ccode['#']=big_line_break; +ccode['+']=no_line_break; ccode[';']=pseudo_semi; +ccode['[']=macro_arg_open; ccode[']']=macro_arg_close; +ccode['\'']=ord; +@<Special control codes for debugging@>@; + +@ Users can write +\.{@@2}, \.{@@1}, and \.{@@0} to turn tracing fully on, partly on, +and off, respectively. + +@<Special control codes...@>= +ccode['0']=ccode['1']=ccode['2']=trace; + +@ The |skip_limbo| routine is used on the first pass to skip through +portions of the input that are not in any sections, i.e., that precede +the first section. After this procedure has been called, the value of +|input_has_ended| will tell whether or not a section has actually been found. + +There's a complication that we will postpone until later: If the \.{@@s} +operation appears in limbo, we want to use it to adjust the default +interpretation of identifiers. + +@<Predec...@>= +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++]; + if (c==new_section) return; + if (c==noop) skip_restricted(); + else if (c==format_code) @<Process simple format in limbo@>; + } + } +} + +@ The |skip_TeX| routine is used on the first pass to skip through +the \TEX/ code at the beginning of a section. It returns the next +control code or `\.{\v}' found in the input. A |new_section| is +assumed to exist at the very end of the file. + +@f skip_TeX TeX + +@c +unsigned +skip_TeX() /* skip past pure \TEX/ code */ +{ + while (1) { + if (loc>limit && get_line()==0) return(new_section); + *(limit+1)='@@'; + while (*loc!='@@' && *loc!='|') loc++; + if (*loc++ =='|') return('|'); + if (loc<=limit) return(ccode[(eight_bits)*(loc++)]); + } +} + +@*1 Inputting the next token. +As stated above, \.{CWEAVE}'s most interesting lexical scanning routine is the +|get_next| function that inputs the next token of \CEE/ input. However, +|get_next| is not especially complicated. + +The result of |get_next| is either a |char| code for some special character, +or it is a special code representing a pair of characters (e.g., `\.{!=}'), +or it is the numeric value computed by the |ccode| +table, or it is one of the following special codes: + +\yskip\hang |identifier|: In this case the global variables |id_first| and +|id_loc| will have been set to the beginning and ending-plus-one locations +in the buffer, as required by the |id_lookup| routine. + +\yskip\hang |string|: The string will have been copied into the array +|section_text|; |id_first| and |id_loc| are set as above (now they are +pointers into |section_text|). + +\yskip\hang |constant|: The constant is copied into |section_text|, with +slight modifications; |id_first| and |id_loc| are set. + +\yskip\noindent Furthermore, some of the control codes cause +|get_next| to take additional actions: + +\yskip\hang |xref_roman|, |xref_wildcard|, |xref_typewriter|, |TeX_string|, +|verbatim|: The values of |id_first| and |id_loc| will have been set to +the beginning and ending-plus-one locations in the buffer. + +\yskip\hang |section_name|: In this case the global variable |cur_section| will +point to the |byte_start| entry for the section name that has just been scanned. +The value of |cur_section_char| will be |'('| if the section name was +preceded by \.{@@(} instead of \.{@@<}. + +\yskip\noindent If |get_next| sees `\.{@@!}' +it sets |xref_switch| to |def_flag| and goes on to the next token. + +@d constant 0200 /* \CEE/ constant */ +@d string 0201 /* \CEE/ string */ +@d identifier 0202 /* \CEE/ identifier or reserved word */ + +@<Global...@>= +name_pointer cur_section; /* name of section just scanned */ +char cur_section_char; /* the character just before that name */ + +@ @<Include...@>= +#include <ctype.h> /* definition of |isalpha|, |isdigit| and so on */ +#include <stdlib.h> /* definition of |exit| */ + +@ As one might expect, |get_next| consists mostly of a big switch +that branches to the various special cases that can arise. + +@d isxalpha(c) ((c)=='_') /* non-alpha character allowed in identifier */ +@d ishigh(c) ((eight_bits)(c)>0177) +@^high-bit character handling@> + +@<Predecl...@>= +eight_bits get_next(); + +@ @c +eight_bits +get_next() /* produces the next input token */ +{@+eight_bits c; /* the current character */ + while (1) { + @<Check if we're at the end of a preprocessor command@>; + if (loc>limit && get_line()==0) return(new_section); + c=*(loc++); + if (xisdigit(c) || c=='\\' || c=='.') @<Get a constant@>@; + else if (c=='\'' || c=='"' || (c=='L'&&(*loc=='\'' || *loc=='"'))@| + || (c=='<' && sharp_include_line==1)) + @<Get a string@>@; + else if (xisalpha(c) || isxalpha(c) || ishigh(c)) + @<Get an identifier@>@; + else if (c=='@@') @<Get control code and possible section name@>@; + else if (xisspace(c)) continue; /* ignore spaces and tabs */ + if (c=='#' && loc==buffer+1) @<Raise preprocessor flag@>; + mistake: @<Compress two-symbol operator@>@; + return(c); + } +} + +@ Because preprocessor commands do not fit in with the rest of the syntax +of \CEE/, +we have to deal with them separately. One solution is to enclose such +commands between special markers. Thus, when a \.\# is seen as the +first character of a line, |get_next| returns a special code +|left_preproc| and raises a flag |preprocessing|. + +We can use the same internal code number for |left_preproc| as we do +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 */ + +@<Glob...@>= +boolean preprocessing=0; /* are we scanning a preprocessor command? */ + +@ @<Raise prep...@>= { + preprocessing=1; + @<Check if next token is |include|@>; + return (left_preproc); +} + +@ An additional complication is the freakish use of \.< and \.> to delimit +a file name in lines that start with \.{\#include}. We must treat this file +name as a string. + +@<Glob...@>= +boolean sharp_include_line=0; /* are we scanning a |#include| line? */ + +@ @<Check if next token is |include|@>= +while (loc<=buffer_end-7 && xisspace(*loc)) loc++; +if (loc<=buffer_end-6 && strncmp(loc,"include",7)==0) sharp_include_line=1; + +@ When we get to the end of a preprocessor line, +we lower the flag and send a code |right_preproc|, unless +the last character was a \.\\. + +@<Check if we're at...@>= + while (loc==limit-1 && preprocessing && *loc=='\\') + if (get_line()==0) return(new_section); /* still in preprocessor mode */ + if (loc>=limit && preprocessing) { + preprocessing=sharp_include_line=0; + return(right_preproc); + } + +@ The following code assigns values to the combinations \.{++}, +\.{--}, \.{->}, \.{>=}, \.{<=}, \.{==}, \.{<<}, \.{>>}, \.{!=}, \.{\v\v}, and +\.{\&\&}, and to the \CPLUSPLUS/ +combinations \.{...}, \.{::}, \.{.*} and \.{->*}. +The compound assignment operators (e.g., \.{+=}) are +treated as separate tokens. + +@d compress(c) if (loc++<=limit) return(c) + +@<Compress tw...@>= +switch(c) { + case '/': if (*loc=='*') {compress(begin_comment);} + else if (*loc=='/') compress(begin_short_comment); break; + case '+': if (*loc=='+') compress(plus_plus); break; + case '-': if (*loc=='-') {compress(minus_minus);} + else if (*loc=='>') if (*(loc+1)=='*') {loc++; compress(minus_gt_ast);} + else compress(minus_gt); break; + case '.': if (*loc=='*') {compress(period_ast);} + else if (*loc=='.' && *(loc+1)=='.') { + loc++; compress(dot_dot_dot); + } + break; + case ':': if (*loc==':') compress(colon_colon); break; + case '=': if (*loc=='=') compress(eq_eq); break; + case '>': if (*loc=='=') {compress(gt_eq);} + else if (*loc=='>') compress(gt_gt); break; + case '<': if (*loc=='=') {compress(lt_eq);} + else if (*loc=='<') compress(lt_lt); break; + case '&': if (*loc=='&') compress(and_and); break; + case '|': if (*loc=='|') compress(or_or); break; + case '!': if (*loc=='=') compress(not_eq); break; +} + +@ @<Get an identifier@>= { + id_first=--loc; + while (isalpha(*++loc) || isdigit(*loc) || isxalpha(*loc) || ishigh(*loc)); + id_loc=loc; return(identifier); +} + +@ Different conventions are followed by \TEX/ and \CEE/ to express octal +and hexadecimal numbers; it is reasonable to stick to each convention +within its realm. Thus the \CEE/ part of a \.{CWEB} file has octals +introduced by \.0 and hexadecimals by \.{0x}, but \.{CWEAVE} will print +in italics or typewriter font, respectively, and introduced by single +or double quotes. In order to simplify the \TEX/ macro used to print +such constants, we replace some of the characters. + +Notice that in this section and the next, |id_first| and |id_loc| +are pointers into the array |section_text|, not into |buffer|. + +@<Get a constant@>= { + id_first=id_loc=section_text+1; + if (*(loc-1)=='\\') {*id_loc++='~'; + while (xisdigit(*loc)) *id_loc++=*loc++;} /* octal constant */ + else if (*(loc-1)=='0') { + if (*loc=='x' || *loc=='X') {*id_loc++='^'; loc++; + while (xisxdigit(*loc)) *id_loc++=*loc++;} /* hex constant */ + else if (xisdigit(*loc)) {*id_loc++='~'; + while (xisdigit(*loc)) *id_loc++=*loc++;} /* octal constant */ + else goto dec; /* decimal constant */ + } + else { /* decimal constant */ + if (*(loc-1)=='.' && !xisdigit(*loc)) goto mistake; /* not a constant */ + dec: *id_loc++=*(loc-1); + while (xisdigit(*loc) || *loc=='.') *id_loc++=*loc++; + if (*loc=='e' || *loc=='E') { /* float constant */ + *id_loc++='_'; loc++; + if (*loc=='+' || *loc=='-') *id_loc++=*loc++; + while (xisdigit(*loc)) *id_loc++=*loc++; + } + } + while (*loc=='u' || *loc=='U' || *loc=='l' || *loc=='L' + || *loc=='f' || *loc=='F') { + *id_loc++='$'; *id_loc++=toupper(*loc); loc++; + } + return(constant); +} + +@ \CEE/ strings and character constants, delimited by double and single +quotes, respectively, can contain newlines or instances of their own +delimiters if they are protected by a backslash. We follow this +convention, but do not allow the string to be longer than |longest_name|. + +@<Get a string@>= { + char delim = c; /* what started the string */ + id_first = section_text+1; + id_loc = section_text; + if (delim=='\'' && *(loc-2)=='@@') {*++id_loc='@@'; *++id_loc='@@';} + *++id_loc=delim; + if (delim=='L') { /* wide character constant */ + delim=*loc++; *++id_loc=delim; + } + if (delim=='<') delim='>'; /* for file names in |#include| lines */ + while (1) { + if (loc>=limit) { + if(*(limit-1)!='\\') { + err_print("! String didn't end"); loc=limit; break; +@.String didn't end@> + } + if(get_line()==0) { + err_print("! Input ended in middle of string"); loc=buffer; break; +@.Input ended in middle of string@> + } + } + if ((c=*loc++)==delim) { + if (++id_loc<=section_text_end) *id_loc=c; + break; + } + if (c=='\\') if (loc>=limit) continue; + else if (++id_loc<=section_text_end) { + *id_loc = '\\'; c=*loc++; + } + if (++id_loc<=section_text_end) *id_loc=c; + } + if (id_loc>=section_text_end) { + printf("\n! String too long: "); +@.String too long@> + term_write(section_text+1,25); + printf("..."); mark_error; + } + id_loc++; + return(string); +} + +@ After an \.{@@} sign has been scanned, the next character tells us +whether there is more work to do. + +@<Get control code and possible section name@>= { + c=*loc++; + switch(ccode[(eight_bits)c]) { + 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: + @<Scan the section name and make |cur_section| point to it@>; + case verbatim: @<Scan a verbatim string@>; + case ord: @<Get a string@>; + default: return(ccode[(eight_bits)c]); + } +} + +@ The occurrence of a section name sets |xref_switch| to zero, +because the section name might (for example) follow \&{int}. + +@<Scan the section name...@>= { + char *k; /* pointer into |section_text| */ + cur_section_char=*(loc-1); + @<Put section name into |section_text|@>; + if (k-section_text>3 && strncmp(k-2,"...",3)==0) + cur_section=section_lookup(section_text+1,k-3,1); /* 1 indicates a prefix */ + else cur_section=section_lookup(section_text+1,k,0); + xref_switch=0; return(section_name); +} + +@ Section names are placed into the |section_text| array with consecutive spaces, +tabs, and carriage-returns replaced by single spaces. There will be no +spaces at the beginning or the end. (We set |section_text[0]=' '| to facilitate +this, since the |section_lookup| routine uses |section_text[1]| as the first +character of the name.) + +@<Set init...@>=section_text[0]=' '; + +@ @<Put section name...@>= +k=section_text; +while (1) { + if (loc>limit && get_line()==0) { + err_print("! Input ended in section name"); +@.Input ended in section name@> + loc=buffer+1; break; + } + c=*loc; + @<If end of name or erroneous control code, |break|@>; + loc++; if (k<section_text_end) k++; + if (xisspace(c)) { + c=' '; if (*(k-1)==' ') k--; + } +*k=c; +} +if (k>=section_text_end) { + printf("\n! Section name too long: "); +@.Section name too long@> + term_write(section_text+1,25); + printf("..."); mark_harmless; +} +if (*k==' ' && k>section_text) k--; + +@ @<If end of name...@>= +if (c=='@@') { + c=*(loc+1); + if (c=='>') { + loc+=2; break; + } + if (ccode[(eight_bits)c]==new_section) { + err_print("! Section name didn't end"); break; +@.Section name didn't end@> + } + if (c!='@@') { + err_print("! Control codes are forbidden in section name"); break; +@.Control codes are forbidden...@> + } + *(++k)='@@'; loc++; /* now |c==*loc| again */ +} + +@ This function skips over a restricted context at relatively high speed. + +@<Predecl...@>= +void skip_restricted(); + +@ @c +void +skip_restricted() +{ + id_first=loc; *(limit+1)='@@'; +false_alarm: + while (*loc!='@@') loc++; + id_loc=loc; + if (loc++>limit) { + err_print("! Control text didn't end"); loc=limit; +@.Control text didn't end@> + } + else { + if (*loc=='@@'&&loc<=limit) {loc++; goto false_alarm;} + if (*loc++!='>') + err_print("! Control codes are forbidden in control text"); +@.Control codes are forbidden...@> + } +} + +@ At the present point in the program we +have |*(loc-1)==verbatim|; we set |id_first| to the beginning +of the string itself, and |id_loc| to its ending-plus-one location in the +buffer. We also set |loc| to the position just after the ending delimiter. + +@<Scan a verbatim string@>= { + id_first=loc++; *(limit+1)='@@'; *(limit+2)='>'; + while (*loc!='@@' || *(loc+1)!='>') loc++; + if (loc>=limit) err_print("! Verbatim string didn't end"); +@.Verbatim string didn't end@> + id_loc=loc; loc+=2; + return (verbatim); +} + +@** Phase one processing. +We now have accumulated enough subroutines to make it possible to carry out +\.{CWEAVE}'s first pass over the source file. If everything works right, +both phase one and phase two of \.{CWEAVE} will assign the same numbers to +sections, and these numbers will agree with what \.{CTANGLE} does. + +The global variable |next_control| often contains the most recent output of +|get_next|; in interesting cases, this will be the control code that +ended a section or part of a section. + +@<Global...@>= +eight_bits next_control; /* control code waiting to be acting upon */ + +@ The overall processing strategy in phase one has the following +straightforward outline. + +@<Predecl...@>= +void phase_one(); + +@ @c +void +phase_one() { + phase=1; reset_input(); section_count=0; + skip_limbo(); change_exists=0; + while (!input_has_ended) + @<Store cross-reference data for the current section@>; + changed_section[section_count]=change_exists; + /* the index changes if anything does */ + phase=2; /* prepare for second phase */ + @<Print error messages about unused or undefined section names@>; +} + +@ @<Store cross-reference data...@>= +{ + 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 */ + } + @<Store cross-references in the \TEX/ part of a section@>; + @<Store cross-references in the definition part of a section@>; + @<Store cross-references in the \CEE/ part of a section@>; + if (changed_section[section_count]) change_exists=1; +} + +@ The |C_xref| subroutine stores references to identifiers in +\CEE/ text material beginning with the current value of |next_control| +and continuing until |next_control| is `\.\{' or `\.{\v}', or until the next +``milestone'' is passed (i.e., |next_control>=format_code|). If +|next_control>=format_code| when |C_xref| is called, nothing will happen; +but if |next_control=='|'| upon entry, the procedure assumes that this is +the `\.{\v}' preceding \CEE/ text that is to be processed. + +The parameter |spec_ctrl| is used to change this behavior. In most cases +|C_xref| is called with |spec_ctrl==ignore|, which triggers the default +processing described above. If |spec_ctrl==section_name|, section names will +be gobbled. This is used when \CEE/ text in the \TEX/ part or inside comments +is parsed: It allows for section names to appear in \pb, but these +strings will not be entered into the cross reference lists since they are not +definitions of section names. + +The program uses the fact that our internal code numbers satisfy +the relations |xref_roman==identifier+roman| and |xref_wildcard==identifier ++wildcard| and |xref_typewriter==identifier+typewriter|, +as well as |normal==0|. + +@<Predecl...@>= +void C_xref(); + +@ @c +void +C_xref( spec_ctrl ) /* makes cross-references for \CEE/ identifiers */ + eight_bits spec_ctrl; +{ + name_pointer p; /* a referenced name */ + while (next_control<format_code || next_control==spec_ctrl) { + if (next_control>=identifier && next_control<=xref_typewriter) { + if (next_control>identifier) @<Replace |"@@@@"| by |"@@"| @>@; + p=id_lookup(id_first, id_loc,next_control-identifier); new_xref(p); + } + if (next_control==section_name) { + section_xref_switch=cite_flag; + new_section_xref(cur_section); + } + next_control=get_next(); + if (next_control=='|' || next_control==begin_comment || + next_control==begin_short_comment) return; + } +} + +@ The |outer_xref| subroutine is like |C_xref| except that it begins +with |next_control!='|'| and ends with |next_control>=format_code|. Thus, it +handles \CEE/ text with embedded comments. + +@<Predecl...@>= +void outer_xref(); + +@ @c +void +outer_xref() /* extension of |C_xref| */ +{ + int bal; /* brace level in comment */ + while (next_control<format_code) + if (next_control!=begin_comment && next_control!=begin_short_comment) + C_xref(ignore); + else { + boolean is_long_comment=(next_control==begin_comment); + bal=copy_comment(is_long_comment,1); next_control='|'; + while (bal>0) { + C_xref(section_name); /* do not reference section names in comments */ + if (next_control=='|') bal=copy_comment(is_long_comment,bal); + else bal=0; /* an error message will occur in phase two */ + } + } +} + +@ In the \TEX/ part of a section, cross-reference entries are made only for +the identifiers in \CEE/ texts enclosed in \pb, or for control texts +enclosed in \.{@@\^}$\,\ldots\,$\.{@@>} or \.{@@.}$\,\ldots\,$\.{@@>} +or \.{@@:}$\,\ldots\,$\.{@@>}. + +@<Store cross-references in the \T...@>= +while (1) { + switch (next_control=skip_TeX()) { + 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=*(loc-1)-'0'; continue; + case '|': C_xref(section_name); break; + case xref_roman: case xref_wildcard: case xref_typewriter: + case noop: case section_name: + loc-=2; next_control=get_next(); /* scan to \.{@@>} */ + if (next_control>=xref_roman && next_control<=xref_typewriter) { + @<Replace |"@@@@"| by |"@@"| @>@; + new_xref(id_lookup(id_first, id_loc,next_control-identifier)); + } + break; + } + if (next_control>=format_code) break; +} + +@ @<Replace |"@@@@"| by |"@@"| @>= +{ + char *src=id_first,*dst=id_first; + while(src<id_loc){ + if(*src=='@@') src++; + *dst++=*src++; + } + id_loc=dst; + while (dst<src) *dst++=' '; /* clean up in case of error message display */ +} + +@ During the definition and \CEE/ parts of a section, cross-references +are made for all identifiers except reserved words. However, the right +identifier in a format definition is not referenced, and the left +identifier is referenced only if it has been explicitly +underlined (preceded by \.{@@!}). +The \TEX/ code in comments is, of course, ignored, except for +\CEE/ portions enclosed in \pb; the text of a section name is skipped +entirely, even if it contains \pb\ constructions. + +The variables |lhs| and |rhs| point to the respective identifiers involved +in a format definition. + +@<Global...@>= +name_pointer lhs, rhs; /* pointers to |byte_start| for format identifiers */ + +@ When we get to the following code we have |next_control>=format_code|. + +@<Store cross-references in the d...@>= +while (next_control<=definition) { /* |format_code| or |definition| */ + if (next_control==definition) { + xref_switch=def_flag; /* implied \.{@@!} */ + next_control=get_next(); + } else @<Process a format definition@>; + outer_xref(); +} + +@ Error messages for improper format definitions will be issued in phase +two. Our job in phase one is to define the |ilk| of a properly formatted +identifier, and to remove cross-references to identifiers that we now +discover should be unindexed. + +@<Process a form...@>= { + next_control=get_next(); + if (next_control==identifier) { + lhs=id_lookup(id_first, id_loc,normal); lhs->ilk=normal; + if (xref_switch) new_xref(lhs); + next_control=get_next(); + if (next_control==identifier) { + rhs=id_lookup(id_first, id_loc,normal); + lhs->ilk=rhs->ilk; + if (unindexed(lhs)) { /* retain only underlined entries */ + xref_pointer q,r=NULL; + for (q=(xref_pointer)lhs->xref;q>xmem;q=q->xlink) + if (q->num<def_flag) + if (r) r->xlink=q->xlink; + else lhs->xref=(char*)q->xlink; + else r=q; + } + next_control=get_next(); + } + } +} + +@ A much simpler processing of format definitions occurs when the +definition is found in limbo. + +@<Process simple format in limbo@>= +{ + if (get_next()!=identifier) + err_print("! Missing left identifier of @@s"); +@.Missing left identifier...@> + else { + lhs=id_lookup(id_first,id_loc,normal); + if (get_next()!=identifier) + err_print("! Missing right identifier of @@s"); +@.Missing right identifier...@> + else { + rhs=id_lookup(id_first,id_loc,normal); + lhs->ilk=rhs->ilk; + } + } +} + +@ Finally, when the \TEX/ and definition parts have been treated, we have +|next_control>=begin_C|. + +@<Store cross-references in the \CEE/...@>= +if (next_control<=section_name) { /* |begin_C| or |section_name| */ + if (next_control==begin_C) section_xref_switch=0; + else { + section_xref_switch=def_flag; + if(cur_section_char=='(' && cur_section!=name_dir) + set_file_flag(cur_section); + } + do { + if (next_control==section_name && cur_section!=name_dir) + new_section_xref(cur_section); + next_control=get_next(); outer_xref(); + } while ( next_control<=section_name); +} + +@ After phase one has looked at everything, we want to check that each +section name was both defined and used. The variable |cur_xref| will point +to cross-references for the current section name of interest. + +@<Global...@>= +xref_pointer cur_xref; /* temporary cross-reference pointer */ +boolean an_output; /* did |file_flag| precede |cur_xref|? */ + +@ The following recursive procedure +walks through the tree of section names and prints out anomalies. +@^recursion@> + +@<Predecl...@>= +void section_check(); + +@ @c +void +section_check(p) +name_pointer p; /* print anomalies in subtree |p| */ +{ + if (p) { + section_check(p->llink); + cur_xref=(xref_pointer)p->xref; + if (cur_xref->num==file_flag) {an_output=1; cur_xref=cur_xref->xlink;} + else an_output=0; + if (cur_xref->num <def_flag) { + printf("\n! Never defined: <"); print_section_name(p); putchar('>'); mark_harmless; +@.Never defined: <section name>@> + } + while (cur_xref->num >=cite_flag) cur_xref=cur_xref->xlink; + if (cur_xref==xmem && !an_output) { + printf("\n! Never used: <"); print_section_name(p); putchar('>'); mark_harmless; +@.Never used: <section name>@> + } + section_check(p->rlink); + } +} + +@ @<Print error messages about un...@>=section_check(root) + +@* Low-level output routines. +The \TEX/ output is supposed to appear in lines at most |line_length| +characters long, so we place it into an output buffer. During the output +process, |out_line| will hold the current line number of the line about to +be output. + +@<Global...@>= +char out_buf[line_length+1]; /* assembled characters */ +char *out_ptr; /* just after last character in |out_buf| */ +char *out_buf_end = out_buf+line_length; /* end of |out_buf| */ +int out_line; /* number of next line to be output */ + +@ The |flush_buffer| routine empties the buffer up to a given breakpoint, +and moves any remaining characters to the beginning of the next line. +If the |per_cent| parameter is 1 a |'%'| is appended to the line +that is being output; in this case the breakpoint |b| should be strictly +less than |out_buf_end|. If the |per_cent| parameter is |0|, +trailing blanks are suppressed. +The characters emptied from the buffer form a new line of output; +if the |carryover| parameter is true, a |"%"| in that line will be +carried over to the next line (so that \TEX/ will ignore the completion +of commented-out text). + +@d c_line_write(c) fflush(active_file),fwrite(out_buf+1,sizeof(char),c,active_file) +@d tex_putc(c) putc(c,active_file) +@d tex_new_line putc('\n',active_file) +@d tex_printf(c) fprintf(active_file,c) + +@c +void +flush_buffer(b,per_cent,carryover) +char *b; /* outputs from |out_buf+1| to |b|,where |b<=out_ptr| */ +boolean per_cent,carryover; +{ + char *j; j=b; /* pointer into |out_buf| */ + if (! per_cent) /* remove trailing blanks */ + while (j>out_buf && *j==' ') j--; + c_line_write(j-out_buf); + if (per_cent) tex_putc('%'); + tex_new_line; out_line++; + if (carryover) + while (j>out_buf) + if (*j--=='%' && (j==out_buf || *j!='\\')) { + *b--='%'; break; + } + if (b<out_ptr) strncpy(out_buf+1,b+1,out_ptr-b); + out_ptr-=b-out_buf; +} + +@ When we are copying \TEX/ source material, we retain line breaks +that occur in the input, except that an empty line is not +output when the \TEX/ source line was nonempty. For example, a line +of the \TEX/ file that contains only an index cross-reference entry +will not be copied. The |finish_line| routine is called just before +|get_line| inputs a new line, and just after a line break token has +been emitted during the output of translated \CEE/ text. + +@c +void +finish_line() /* do this at the end of a line */ +{ + char *k; /* pointer into |buffer| */ + if (out_ptr>out_buf) flush_buffer(out_ptr,0,0); + else { + for (k=buffer; k<=limit; k++) + if (!(xisspace(*k))) return; + flush_buffer(out_buf,0,0); + } +} + +@ 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}'. + +@<Set init...@>= +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 +full. If we want to append more than one character at once, we say +|out_str(s)|, where |s| is a string containing the characters. + +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 +out_str(s) /* output characters from |s| to end of string */ +char *s; +{ + while (*s) out(*s++); +} + +@ The |break_out| routine is called just before the output buffer is about +to overflow. To make this routine a little faster, we initialize position +0 of the output buffer to `\.\\'; this character isn't really output. + +@<Set init...@>= +out_buf[0]='\\'; + +@ A long line is broken at a blank space or just before a backslash that isn't +preceded by another backslash. In the latter case, a |'%'| is output at +the break. + +@<Predecl...@>= +void break_out(); + +@ @c +void +break_out() /* finds a way to break the output line */ +{ + char *k=out_ptr; /* pointer into |out_buf| */ + while (1) { + if (k==out_buf) @<Print warning message, break the line, |return|@>; + if (*k==' ') { + flush_buffer(k,0,1); return; + } + if (*(k--)=='\\' && *k!='\\') { /* we've decreased |k| */ + flush_buffer(k,1,1); return; + } + } +} + +@ We get to this section only in the unusual case that the entire output line +consists of a string of backslashes followed by a string of nonblank +non-backslashes. In such cases it is almost always safe to break the +line by putting a |'%'| just before the last character. + +@<Print warning message...@>= +{ + printf("\n! Line had to be broken (output l. %d):\n",out_line); +@.Line had to be broken@> + term_write(out_buf+1, out_ptr-out_buf-1); + new_line; mark_harmless; + flush_buffer(out_ptr-1,1,1); return; +} + +@ 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 +out_section(n) +sixteen_bits n; +{ + 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 +entry, enclosing it in braces. + +@c +void +out_name(p) +name_pointer p; +{ + char *k, *k_end=(p+1)->byte_start; /* pointers into |byte_mem| */ + out('{'); + for (k=p->byte_start; k<k_end; k++) { + if (isxalpha(*k)) out('\\'); + out(*k); + } + out('}'); +} + +@* Routines that copy \TEX/ material. +During phase two, we use subroutines |copy_limbo|, |copy_TeX|, and +|copy_comment| in place of the analogous |skip_limbo|, |skip_TeX|, and +|skip_comment| that were used in phase one. (Well, |copy_comment| +was actually written in such a way that it functions as |skip_comment| +in phase one.) + +The |copy_limbo| routine, for example, takes \TEX/ material that is not +part of any section and transcribes it almost verbatim to the output file. +The use of `\.{@@}' signs is severely restricted in such material: +`\.{@@@@}' pairs are replaced by singletons; `\.{@@l}' and `\.{@@q}' and +`\.{@@s}' are interpreted. + +@c +void +copy_limbo() +{ + char c; + while (1) { + if (loc>limit && (finish_line(), get_line()==0)) return; + *(limit+1)='@@'; + while (*loc!='@@') out(*(loc++)); + if (loc++<=limit) { + c=*loc++; + if (ccode[(eight_bits)c]==new_section) break; + switch (ccode[(eight_bits)c]) { + case translit_code: out_str("\\ATL"); break; +@.\\ATL@> + case '@@': out('@@'); break; + case noop: skip_restricted(); break; + 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 */ + default: err_print("! Double @@ should be used in limbo"); +@.Double @@ should be used...@> + out('@@'); + } + } + } +} + +@ The |copy_TeX| routine processes the \TEX/ code at the beginning of a +section; for example, the words you are now reading were copied in this +way. It returns the next control code or `\.{\v}' found in the input. +We don't copy spaces or tab marks into the beginning of a line. This +makes the test for empty lines in |finish_line| work. + +@ @f copy_TeX TeX +@c +eight_bits +copy_TeX() +{ + char c; /* current character being copied */ + while (1) { + if (loc>limit && (finish_line(), get_line()==0)) return(new_section); + *(limit+1)='@@'; + while ((c=*(loc++))!='|' && c!='@@') { + out(c); + if (out_ptr==out_buf+1 && (xisspace(c))) out_ptr--; + } + if (c=='|') return('|'); + if (loc<=limit) return(ccode[(eight_bits)*(loc++)]); + } +} + +@ The |copy_comment| function issues a warning if more braces are opened than +closed, and in the case of a more serious error it supplies enough +braces to keep \TEX/ from complaining about unbalanced braces. +Instead of copying the \TEX/ material +into the output buffer, this function copies it into the token memory +(in phase two only). +The abbreviation |app_tok(t)| is used to append token |t| to the current +token list, and it also makes sure that it is possible to append at least +one further token without overflow. + +@d app_tok(c) {if (tok_ptr+2>tok_mem_end) overflow("token"); *(tok_ptr++)=c;} + +@<Predec...@>= +int copy_comment(); + +@ @c +int copy_comment(is_long_comment,bal) /* copies \TEX/ code in comments */ +boolean is_long_comment; /* is this a traditional \CEE/ comment? */ +int bal; /* brace balance */ +{ + char c; /* current character being copied */ + while (1) { + if (loc>limit) { + if (is_long_comment) { + if (get_line()==0) { + err_print("! Input ended in mid-comment"); +@.Input ended in mid-comment@> + loc=buffer+1; goto done; + } + } + else { + if (bal>1) err_print("! Missing } in comment"); +@.Missing \} in comment@> + goto done; + } + } + c=*(loc++); + if (c=='|') return(bal); + if (is_long_comment) @<Check for end of comment@>; + if (phase==2) { + if (ishigh(c)) app_tok(quoted_char); + app_tok(c); + } + @<Copy special things when |c=='@@', '\\'|@>; + if (c=='{') bal++; + else if (c=='}') { + if(bal>1) bal--; + else {err_print("! Extra } in comment"); +@.Extra \} in comment@> + if (phase==2) tok_ptr--; + } + } + } +done:@<Clear |bal| and |return|@>; +} + +@ @<Check for end of comment@>= +if (c=='*' && *loc=='/') { + loc++; + if (bal>1) err_print("! Missing } in comment"); +@.Missing \} in comment@> + goto done; +} + +@ @<Copy special things when |c=='@@'...@>= +if (c=='@@') { + if (*(loc++)!='@@') { + err_print("! Illegal use of @@ in comment"); +@.Illegal use of @@...@> + loc-=2; if (phase==2) *(tok_ptr-1)=' '; goto done; + } +} +else if (c=='\\' && *loc!='@@') + if (phase==2) app_tok(*(loc++)) else loc++; + +@ We output +enough right braces to keep \TEX/ happy. + +@<Clear |bal|...@>= +if (phase==2) while (bal-- >0) app_tok('}'); +return(0); + +@** Parsing. +The most intricate part of \.{CWEAVE} is its mechanism for converting +\CEE/-like code into \TEX/ code, and we might as well plunge into this +aspect of the program now. A ``bottom up'' approach is used to parse the +\CEE/-like material, since \.{CWEAVE} must deal with fragmentary +constructions whose overall ``part of speech'' is not known. + +At the lowest level, the input is represented as a sequence of entities +that we shall call {\it scraps}, where each scrap of information consists +of two parts, its {\it category} and its {\it translation}. The category +is essentially a syntactic class, and the translation is a token list that +represents \TEX/ code. Rules of syntax and semantics tell us how to +combine adjacent scraps into larger ones, and if we are lucky an entire +\CEE/ text that starts out as hundreds of small scraps will join +together into one gigantic scrap whose translation is the desired \TEX/ +code. If we are unlucky, we will be left with several scraps that don't +combine; their translations will simply be output, one by one. + +The combination rules are given as context-sensitive productions that are +applied from left to right. Suppose that we are currently working on the +sequence of scraps $s_1\,s_2\ldots s_n$. We try first to find the longest +production that applies to an initial substring $s_1\,s_2\ldots\,$; but if +no such productions exist, we try to find the longest production +applicable to the next substring $s_2\,s_3\ldots\,$; and if that fails, we +try to match $s_3\,s_4\ldots\,$, etc. + +A production applies if the category codes have a given pattern. For +example, one of the productions (see rule~3) is +$$\hbox{|exp| }\left\{\matrix{\hbox{|binop|}\cr\hbox{|unorbinop|}}\right\} +\hbox{ |exp| }\RA\hbox{ |exp|}$$ +and it means that three consecutive scraps whose respective categories are +|exp|, |binop| (or |unorbinop|), +and |exp| are converted to one scrap whose category +is |exp|. The translations of the original +scraps are simply concatenated. The case of +$$\hbox{|exp| |comma| |exp| $\RA$ |exp|} \hskip4emE_1C\,\\{opt}9\,E_2$$ +(rule 4) is only slightly more complicated: +Here the resulting |exp| translation +consists not only of the three original translations, but also of the +tokens |opt| and 9 between the translations of the +|comma| and the following |exp|. +In the \TEX/ file, this will specify an optional line break after the +comma, with penalty 90. + +At each opportunity the longest possible production is applied. For +example, if the current sequence of scraps is |int_like| |cast| +|lbrace|, rule 31 is applied; but if the sequence is |int_like| |cast| +followed by anything other than |lbrace|, rule 32 takes effect. + +Translation rules such as `$E_1C\,\\{opt}9\,E_2$' above use subscripts +to distinguish between translations of scraps whose categories have the +same initial letter; these subscripts are assigned from left to right. + +@ Here is a list of the category codes that scraps can have. +(A few others, like |int_like|, have already been defined; the +|cat_name| array contains a complete list.) + +@d exp 1 /* denotes an expression, including perhaps a single identifier */ +@d unop 2 /* denotes a unary operator */ +@d binop 3 /* denotes a binary operator */ +@d unorbinop 4 + /* denotes an operator that can be unary or binary, depending on context */ +@d cast 5 /* denotes a cast */ +@d question 6 /* denotes a question mark and possibly the expressions flanking it */ +@d lbrace 7 /* denotes a left brace */ +@d rbrace 8 /* denotes a right brace */ +@d decl_head 9 /* denotes an incomplete declaration */ +@d comma 10 /* denotes a comma */ +@d lpar 11 /* denotes a left parenthesis or left bracket */ +@d rpar 12 /* denotes a right parenthesis or right bracket */ +@d prelangle 13 /* denotes `$<$' before we know what it is */ +@d prerangle 14 /* denotes `$>$' before we know what it is */ +@d langle 15 /* denotes `$<$' when it's used as angle bracket in a template */ +@d colcol 18 /* denotes `::' */ +@d base 19 /* denotes a colon that introduces a base specifier */ +@d decl 20 /* denotes a complete declaration */ +@d struct_head 21 /* denotes the beginning of a structure specifier */ +@d stmt 23 /* denotes a complete statement */ +@d function 24 /* denotes a complete function */ +@d fn_decl 25 /* denotes a function declarator */ +@d semi 27 /* denotes a semicolon */ +@d colon 28 /* denotes a colon */ +@d tag 29 /* denotes a statement label */ +@d if_head 30 /* denotes the beginning of a compound conditional */ +@d else_head 31 /* denotes a prefix for a compound statement */ +@d if_clause 32 /* pending \.{if} together with a condition */ +@d lproc 35 /* begins a preprocessor command */ +@d rproc 36 /* ends a preprocessor command */ +@d insert 37 /* a scrap that gets combined with its neighbor */ +@d section_scrap 38 /* section name */ +@d dead 39 /* scrap that won't combine */ +@d begin_arg 58 /* \.{@@[} */ +@d end_arg 59 /* \.{@@]} */ + +@<Glo...@>= +char cat_name[256][12]; +eight_bits cat_index; + +@ @<Set in...@>= + for (cat_index=0;cat_index<255;cat_index++) + strcpy(cat_name[cat_index],"UNKNOWN"); + strcpy(cat_name[exp],"exp"); + strcpy(cat_name[unop],"unop"); + strcpy(cat_name[binop],"binop"); + strcpy(cat_name[unorbinop],"unorbinop"); + strcpy(cat_name[cast],"cast"); + strcpy(cat_name[question],"?"); + strcpy(cat_name[lbrace],"{"@q}@>); + strcpy(cat_name[rbrace],@q{@>"}"); + strcpy(cat_name[decl_head],"decl_head"); + strcpy(cat_name[comma],","); + strcpy(cat_name[lpar],"("); + strcpy(cat_name[rpar],")"); + strcpy(cat_name[prelangle],"<"); + strcpy(cat_name[prerangle],">"); + strcpy(cat_name[langle],"\\<"); + strcpy(cat_name[colcol],"::"); + strcpy(cat_name[base],"\\:"); + strcpy(cat_name[decl],"decl"); + strcpy(cat_name[struct_head],"struct_head"); + strcpy(cat_name[stmt],"stmt"); + strcpy(cat_name[function],"function"); + strcpy(cat_name[fn_decl],"fn_decl"); + strcpy(cat_name[else_like],"else_like"); + strcpy(cat_name[semi],";"); + strcpy(cat_name[colon],":"); + strcpy(cat_name[tag],"tag"); + strcpy(cat_name[if_head],"if_head"); + strcpy(cat_name[else_head],"else_head"); + strcpy(cat_name[if_clause],"if()"); + strcpy(cat_name[lproc],"#{"@q}@>); + strcpy(cat_name[rproc],@q{@>"#}"); + strcpy(cat_name[insert],"insert"); + strcpy(cat_name[section_scrap],"section"); + strcpy(cat_name[dead],"@@d"); + strcpy(cat_name[public_like],"public"); + strcpy(cat_name[operator_like],"operator"); + strcpy(cat_name[new_like],"new"); + strcpy(cat_name[catch_like],"catch"); + strcpy(cat_name[for_like],"for"); + strcpy(cat_name[do_like],"do"); + strcpy(cat_name[if_like],"if"); + strcpy(cat_name[raw_rpar],")?"); + strcpy(cat_name[raw_unorbin],"unorbinop?"); + strcpy(cat_name[const_like],"const"); + strcpy(cat_name[raw_int],"raw"); + strcpy(cat_name[int_like],"int"); + strcpy(cat_name[case_like],"case"); + strcpy(cat_name[sizeof_like],"sizeof"); + strcpy(cat_name[struct_like],"struct"); + strcpy(cat_name[typedef_like],"typedef"); + strcpy(cat_name[define_like],"define"); + strcpy(cat_name[begin_arg],"@@["@q]@>); + strcpy(cat_name[end_arg],@q[@>"@@]"); + strcpy(cat_name[0],"zero"); + +@ This code allows \.{CWEAVE} to display its parsing steps. + +@c +void +print_cat(c) /* symbolic printout of a category */ +eight_bits c; +{ + printf(cat_name[c]); +} + +@ The token lists for translated \TEX/ output contain some special control +symbols as well as ordinary characters. These control symbols are +interpreted by \.{CWEAVE} before they are written to the output file. + +\yskip\hang |break_space| denotes an optional line break or an en space; + +\yskip\hang |force| denotes a line break; + +\yskip\hang |big_force| denotes a line break with additional vertical space; + +\yskip\hang |preproc_line| denotes that the line will be printed flush left; + +\yskip\hang |opt| denotes an optional line break (with the continuation +line indented two ems with respect to the normal starting position)---this +code is followed by an integer |n|, and the break will occur with penalty +$10n$; + +\yskip\hang |backup| denotes a backspace of one em; + +\yskip\hang |cancel| obliterates any |break_space|, |opt|, |force|, or +|big_force| tokens that immediately precede or follow it and also cancels any +|backup| tokens that follow it; + +\yskip\hang |indent| causes future lines to be indented one more em; + +\yskip\hang |outdent| causes future lines to be indented one less em. + +\yskip\noindent All of these tokens are removed from the \TEX/ output that +comes from \CEE/ text between \pb\ signs; |break_space| and |force| and +|big_force| become single spaces in this mode. The translation of other +\CEE/ texts results in \TEX/ control sequences \.{\\1}, \.{\\2}, +\.{\\3}, \.{\\4}, \.{\\5}, \.{\\6}, \.{\\7}, \.{\\8} +corresponding respectively to +|indent|, |outdent|, |opt|, |backup|, |break_space|, |force|, +|big_force| and |preproc_line|. +However, a sequence of consecutive `\.\ ', |break_space|, +|force|, and/or |big_force| tokens is first replaced by a single token +(the maximum of the given ones). + +The token |math_rel| will be translated into +\.{\\MRL\{}, and it will get a matching \.\} later. +Other control sequences in the \TEX/ output will be +`\.{\\\\\{}$\,\ldots\,$\.\}' +surrounding identifiers, `\.{\\\&\{}$\,\ldots\,$\.\}' surrounding +reserved words, `\.{\\.\{}$\,\ldots\,$\.\}' surrounding strings, +`\.{\\C\{}$\,\ldots\,$\.\}$\,$|force|' surrounding comments, and +`\.{\\X$n$:}$\,\ldots\,$\.{\\X}' surrounding section names, where +|n| is the section number. + +@d math_rel 0206 +@d big_cancel 0210 /* like |cancel|, also overrides spaces */ +@d cancel 0211 /* overrides |backup|, |break_space|, |force|, |big_force| */ +@d indent 0212 /* one more tab (\.{\\1}) */ +@d outdent 0213 /* one less tab (\.{\\2}) */ +@d opt 0214 /* optional break in mid-statement (\.{\\3}) */ +@d backup 0215 /* stick out one unit to the left (\.{\\4}) */ +@d break_space 0216 /* optional break between statements (\.{\\5}) */ +@d force 0217 /* forced break between statements (\.{\\6}) */ +@d big_force 0220 /* forced break with additional space (\.{\\7}) */ +@d preproc_line 0221 /* begin line without indentation (\.{\\8}) */ +@^high-bit character handling@> +@d quoted_char 0222 + /* introduces a character token in the range |0200|--|0377| */ +@d end_translation 0223 /* special sentinel token at end of list */ +@d inserted 0224 /* sentinel to mark translations of inserts */ + +@ The raw input is converted into scraps according to the following table, +which gives category codes followed by the translations. +\def\stars {\.{**}}% +The symbol `\stars' stands for `\.{\\\&\{{\rm identifier}\}}', +i.e., the identifier itself treated as a reserved word. +The right-hand column is the so-called |mathness|, which is explained +further below. + +An identifier |c| of length 1 is translated as \.{\\\v c} instead of +as \.{\\\\\{c\}}. An identifier \.{CAPS} in all caps is translated as +\.{\\.\{CAPS\}} instead of as \.{\\\\\{CAPS\}}. An identifier that has +become a reserved word via |typedef| is translated with \.{\\\&} replacing +\.{\\\\} and |raw_int| replacing |exp|. + +A string of length greater than 20 is broken into pieces of size at most~20 +with discretionary breaks in between. + +\yskip\halign{\quad#\hfil&\quad#\hfil&\quad\hfil#\hfil\cr +\.{!=}&|binop|: \.{\\I}&yes\cr +\.{<=}&|binop|: \.{\\Z}&yes\cr +\.{>=}&|binop|: \.{\\G}&yes\cr +\.{==}&|binop|: \.{\\E}&yes\cr +\.{\&\&}&|binop|: \.{\\W}&yes\cr +\.{\v\v}&|binop|: \.{\\V}&yes\cr +\.{++}&|binop|: \.{\\PP}&yes\cr +\.{--}&|binop|: \.{\\MM}&yes\cr +\.{->}&|binop|: \.{\\MG}&yes\cr +\.{>>}&|binop|: \.{\\GG}&yes\cr +\.{<<}&|binop|: \.{\\LL}&yes\cr +\.{::}&|colcol|: \.{\\DC}&maybe\cr +\.{.*}&|binop|: \.{\\PA}&yes\cr +\.{->*}&|binop|: \.{\\MGA}&yes\cr +\.{...}&|exp|: \.{\\,\\ldots\\,}&yes\cr +\."string\."&|exp|: \.{\\.\{}string with special characters quoted\.\}&maybe\cr +\.{@@=}string\.{@@>}&|exp|: \.{\\vb\{}string with special characters + quoted\.\}&maybe\cr +\.{@@'7'}&|exp|: \.{\\.\{@@'7'\}}&maybe\cr +\.{077} or \.{\\77}&|exp|: \.{\\T\{\\\~77\}}&maybe\cr +\.{0x7f}&|exp|: \.{\\T\{\\\^7f\}}&maybe\cr +\.{77}&|exp|: \.{\\T\{77\}}&maybe\cr +\.{77L}&|exp|: \.{\\T\{77\\\$L\}}&maybe\cr +\.{0.1E5}&|exp|: \.{\\T\{0.1\\\_5\}}&maybe\cr +\.+&|unorbinop|: \.+&yes\cr +\.-&|unorbinop|: \.-&yes\cr +\.*&|raw_unorbin|: \.*&yes\cr +\./&|binop|: \./&yes\cr +\.<&|prelangle|: \.{\\langle}&yes\cr +\.=&|binop|: \.{\\K}&yes\cr +\.>&|prerangle|: \.{\\rangle}&yes\cr +\..&|binop|: \..&yes\cr +\.{\v}&|binop|: \.{\\OR}&yes\cr +\.\^&|binop|: \.{\\XOR}&yes\cr +\.\%&|binop|: \.{\\MOD}&yes\cr +\.?&|question|: \.{\\?}&yes\cr +\.!&|unop|: \.{\\R}&yes\cr +\.\~&|unop|: \.{\\CM}&yes\cr +\.\&&|raw_unorbin|: \.{\\AND}&yes\cr +\.(&|lpar|: \.(&maybe\cr +\.[&|lpar|: \.[&maybe\cr +\.)&|raw_rpar|: \.)&maybe\cr +\.]&|raw_rpar|: \.]&maybe\cr +\.\{&|lbrace|: \.\{&yes\cr +\.\}&|lbrace|: \.\}&yes\cr +\.,&|comma|: \.,&yes\cr +\.;&|semi|: \.;&maybe\cr +\.:&|colon|: \.:&maybe\cr +\.\# (within line)&|unorbinop|: \.{\\\#}&yes\cr +\.\# (at beginning)&|lproc|: |force| |preproc_line| \.{\\\#}&no\cr +end of \.\# line&|rproc|: |force|&no\cr +identifier&|exp|: \.{\\\\\{}identifier with underlines quoted\.\}&maybe\cr +\.{asm}&|sizeof_like|: \stars&maybe\cr +\.{auto}&|int_like|: \stars&maybe\cr +\.{break}&|case_like|: \stars&maybe\cr +\.{case}&|case_like|: \stars&maybe\cr +\.{catch}&|catch_like|: \stars&maybe\cr +\.{char}&|raw_int|: \stars&maybe\cr +\.{class}&|struct_like|: \stars&maybe\cr +\.{clock\_t}&|raw_int|: \stars&maybe\cr +\.{const}&|const_like|: \stars&maybe\cr +\.{continue}&|case_like|: \stars&maybe\cr +\.{default}&|case_like|: \stars&maybe\cr +\.{define}&|define_like|: \stars&maybe\cr +\.{defined}&|sizeof_like|: \stars&maybe\cr +\.{delete}&|sizeof_like|: \stars&maybe\cr +\.{div\_t}&|raw_int|: \stars&maybe\cr +\.{do}&|do_like|: \stars&maybe\cr +\.{double}&|raw_int|: \stars&maybe\cr +\.{elif}&|if_like|: \stars&maybe\cr +\.{else}&|else_like|: \stars&maybe\cr +\.{endif}&|if_like|: \stars&maybe\cr +\.{enum}&|struct_like|: \stars&maybe\cr +\.{error}&|if_like|: \stars&maybe\cr +\.{extern}&|int_like|: \stars&maybe\cr +\.{FILE}&|raw_int|: \stars&maybe\cr +\.{float}&|raw_int|: \stars&maybe\cr +\.{for}&|for_like|: \stars&maybe\cr +\.{fpos\_t}&|raw_int|: \stars&maybe\cr +\.{friend}&|int_like|: \stars&maybe\cr +\.{goto}&|case_like|: \stars&maybe\cr +\.{if}&|if_like|: \stars&maybe\cr +\.{ifdef}&|if_like|: \stars&maybe\cr +\.{ifndef}&|if_like|: \stars&maybe\cr +\.{include}&|if_like|: \stars&maybe\cr +\.{inline}&|int_like|: \stars&maybe\cr +\.{int}&|raw_int|: \stars&maybe\cr +\.{jmp\_buf}&|raw_int|: \stars&maybe\cr +\.{ldiv\_t}&|raw_int|: \stars&maybe\cr +\.{line}&|if_like|: \stars&maybe\cr +\.{long}&|raw_int|: \stars&maybe\cr +\.{new}&|new_like|: \stars&maybe\cr +\.{NULL}&|exp|: \.{\\NULL}&yes\cr +\.{offsetof}&|sizeof_like|: \stars&maybe\cr +\.{operator}&|operator_like|: \stars&maybe\cr +\.{pragma}&|if_like|: \stars&maybe\cr +\.{private}&|public_like|: \stars&maybe\cr +\.{protected}&|public_like|: \stars&maybe\cr +\.{ptrdiff\_t}&|raw_int|: \stars&maybe\cr +\.{public}&|public_like|: \stars&maybe\cr +\.{register}&|int_like|: \stars&maybe\cr +\.{return}&|case_like|: \stars&maybe\cr +\.{short}&|raw_int|: \stars&maybe\cr +\.{sig\_atomic\_t}&|raw_int|: \stars&maybe\cr +\.{signed}&|raw_int|: \stars&maybe\cr +\.{size\_t}&|raw_int|: \stars&maybe\cr +\.{sizeof}&|sizeof_like|: \stars&maybe\cr +\.{static}&|int_like|: \stars&maybe\cr +\.{struct}&|struct_like|: \stars&maybe\cr +\.{switch}&|if_like|: \stars&maybe\cr +\.{template}&|int_like|: \stars&maybe\cr +\.{TeX}&|exp|: \.{\\TeX}&yes\cr +\.{this}&|exp|: \.{\\this}&yes\cr +\.{throw}&|case_like|: \stars&maybe\cr +\.{time\_t}&|raw_int|: \stars&maybe\cr +\.{try}&|else_like|: \stars&maybe\cr +\.{typedef}&|typedef_like|: \stars&maybe\cr +\.{undef}&|if_like|: \stars&maybe\cr +\.{union}&|struct_like|: \stars&maybe\cr +\.{unsigned}&|raw_int|: \stars&maybe\cr +\.{va\_dcl}&|decl|: \stars&maybe\cr +\.{va\_list}&|raw_int|: \stars&maybe\cr +\.{virtual}&|int_like|: \stars&maybe\cr +\.{void}&|raw_int|: \stars&maybe\cr +\.{volatile}&|const_like|: \stars&maybe\cr +\.{wchar\_t}&|raw_int|: \stars&maybe\cr +\.{while}&|if_like|: \stars&maybe\cr +\.{@@,}&|insert|: \.{\\,}&maybe\cr +\.{@@\v}&|insert|: |opt| \.0&maybe\cr +\.{@@/}&|insert|: |force|&no\cr +\.{@@\#}&|insert|: |big_force|&no\cr +\.{@@+}&|insert|: |big_cancel| \.{\{\}} |break_space| + \.{\{\}} |big_cancel|&no\cr +\.{@@;}&|semi|: &maybe\cr +\.{@@[@q]@>}&|begin_arg|: &maybe\cr +\.{@q[@>@@]}&|end_arg|: &maybe\cr +\.{@@\&}&|insert|: \.{\\J}&maybe\cr +\.{@@h}&|insert|: |force| \.{\\ATH} |force|&no\cr +\.{@@<}\thinspace section name\thinspace\.{@@>}&|section_scrap|: + \.{\\X}$n$\.:translated section name\.{\\X}&maybe\cr +\.{@@(@q)@>}\thinspace section name\thinspace\.{@@>}&|section_scrap|: + \.{\\X}$n$\.{:\\.\{}section name with special characters + quoted\.{\ \}\\X}&maybe\cr +\.{/*}comment\.{*/}&|insert|: |cancel| + \.{\\C\{}translated comment\.\} |force|&no\cr +\.{//}comment&|insert|: |cancel| + \.{\\SHC\{}translated comment\.\} |force|&no\cr +} + +The construction \.{@@t}\thinspace stuff\/\thinspace\.{@@>} contributes +\.{\\hbox\{}\thinspace stuff\/\thinspace\.\} to the following scrap. + +@i prod.w + +@* Implementing the productions. +More specifically, a scrap is a structure consisting of a category +|cat| and a |text_pointer| |trans|, which points to the translation in +|tok_start|. When \CEE/ text is to be processed with the grammar above, +we form an array |scrap_info| containing the initial scraps. +Our production rules have the nice property that the right-hand side is never +longer than the left-hand side. Therefore it is convenient to use sequential +allocation for the current sequence of scraps. Five pointers are used to +manage the parsing: + +\yskip\hang |pp| is a pointer into |scrap_info|. We will try to match +the category codes |pp->cat,@,@,(pp+1)->cat|$,\,\,\ldots\,$ +to the left-hand sides of productions. + +\yskip\hang |scrap_base|, |lo_ptr|, |hi_ptr|, and |scrap_ptr| are such that +the current sequence of scraps appears in positions |scrap_base| through +|lo_ptr| and |hi_ptr| through |scrap_ptr|, inclusive, in the |cat| and +|trans| arrays. Scraps located between |scrap_base| and |lo_ptr| have +been examined, while those in positions |>=hi_ptr| have not yet been +looked at by the parsing process. + +\yskip\noindent Initially |scrap_ptr| is set to the position of the final +scrap to be parsed, and it doesn't change its value. The parsing process +makes sure that |lo_ptr>=pp+3|, since productions have as many as four terms, +by moving scraps from |hi_ptr| to |lo_ptr|. If there are +fewer than |pp+3| scraps left, the positions up to |pp+3| are filled with +blanks that will not match in any productions. Parsing stops when +|pp==lo_ptr+1| and |hi_ptr==scrap_ptr+1|. + +Since the |scrap| structure will later be used for other purposes, we +declare its second element as unions. + +@<Type...@>= +typedef struct { + eight_bits cat; + eight_bits mathness; + union { + text_pointer Trans; + @<Rest of |trans_plus| union@>@; + } trans_plus; +} scrap; +typedef scrap *scrap_pointer; + +@ @d trans trans_plus.Trans /* translation texts of scraps */ + +@<Global...@>= +scrap scrap_info[max_scraps]; /* memory array for scraps */ +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 */ +scrap_pointer scrap_ptr; /* ending of the current scrap sequence */ +scrap_pointer lo_ptr; /* last scrap that has been examined */ +scrap_pointer hi_ptr; /* first scrap that has not been examined */ +scrap_pointer max_scr_ptr; /* largest value assumed by |scrap_ptr| */ + +@ @<Set init...@>= +scrap_base=scrap_info+1; +max_scr_ptr=scrap_ptr=scrap_info; + +@ Token lists in |@!tok_mem| are composed of the following kinds of +items for \TEX/ output. + +\yskip\item{$\bullet$}Character codes and special codes like |force| and +|math_rel| represent themselves; + +\item{$\bullet$}|id_flag+p| represents \.{\\\\\{{\rm identifier $p$}\}}; + +\item{$\bullet$}|res_flag+p| represents \.{\\\&\{{\rm identifier $p$}\}}; + +\item{$\bullet$}|section_flag+p| represents section name |p|; + +\item{$\bullet$}|tok_flag+p| represents token list number |p|; + +\item{$\bullet$}|inner_tok_flag+p| represents token list number |p|, to be +translated without line-break controls. + +@d id_flag 10240 /* signifies an identifier */ +@d res_flag 2*id_flag /* signifies a reserved word */ +@d section_flag 3*id_flag /* signifies a section name */ +@d tok_flag 4*id_flag /* signifies a token list */ +@d inner_tok_flag 5*id_flag /* signifies a token list in `\pb' */ + +@c +void +print_text(p) /* prints a token list for debugging; not used in |main| */ +text_pointer p; +{ + token_pointer j; /* index into |tok_mem| */ + sixteen_bits r; /* remainder of token after the flag has been stripped off */ + if (p>=text_ptr) printf("BAD"); + else for (j=*p; j<*(p+1); j++) { + r=*j%id_flag; + switch (*j/id_flag) { + case 1: printf("\\\\{"@q}@>); print_id((name_dir+r)); printf(@q{@>"}"); + break; /* |id_flag| */ + case 2: printf("\\&{"@q}@>); print_id((name_dir+r)); printf(@q{@>"}"); + break; /* |res_flag| */ + case 3: printf("<"); print_section_name((name_dir+r)); printf(">"); + break; /* |section_flag| */ + case 4: printf("[[%d]]",r); break; /* |tok_flag| */ + case 5: printf("|[[%d]]|",r); break; /* |inner_tok_flag| */ + default: @<Print token |r| in symbolic form@>; + } + } + fflush(stdout); +} + +@ @<Print token |r|...@>= +switch (r) { + case math_rel: printf("\\mathrel{"@q}@>); break; + case big_cancel: printf("[ccancel]"); break; + case cancel: printf("[cancel]"); break; + case indent: printf("[indent]"); break; + case outdent: printf("[outdent]"); break; + case backup: printf("[backup]"); break; + case opt: printf("[opt]"); break; + case break_space: printf("[break]"); break; + case force: printf("[force]"); break; + case big_force: printf("[fforce]"); break; + case preproc_line: printf("[preproc]"); break; + case quoted_char: j++; printf("[%o]",(unsigned)*j); break; + case end_translation: printf("[quit]"); break; + case inserted: printf("[inserted]"); break; + default: putxchar(r); +} + +@ The production rules listed above are embedded directly into \.{CWEAVE}, +since it is easier to do this than to write an interpretive system +that would handle production systems in general. Several macros are defined +here so that the program for each production is fairly short. + +All of our productions conform to the general notion that some |k| +consecutive scraps starting at some position |j| are to be replaced by a +single scrap of some category |c| whose translation is composed from the +translations of the disappearing scraps. After this production has been +applied, the production pointer |pp| should change by an amount |d|. Such +a production can be represented by the quadruple |(j,k,c,d)|. For example, +the production `|exp@,comma@,exp| $\RA$ |exp|' would be represented by +`|(pp,3,exp,-2)|'; in this case the pointer |pp| should decrease by 2 +after the production has been applied, because some productions with +|exp| in their second or third positions might now match, +but no productions have +|exp| in the fourth position of their left-hand sides. Note that +the value of |d| is determined by the whole collection of productions, not +by an individual one. +The determination of |d| has been +done by hand in each case, based on the full set of productions but not on +the grammar of \CEE/ or on the rules for constructing the initial +scraps. + +We also attach a serial number to each production, so that additional +information is available when debugging. For example, the program below +contains the statement `|reduce(pp,3,exp,-2,4)|' when it implements +the production just mentioned. + +Before calling |reduce|, the program should have appended the tokens of +the new translation to the |tok_mem| array. We commonly want to append +copies of several existing translations, and macros are defined to +simplify these common cases. For example, \\{app2}|(pp)| will append the +translations of two consecutive scraps, |pp->trans| and |(pp+1)->trans|, to +the current token list. If the entire new translation is formed in this +way, we write `|squash(j,k,c,d,n)|' instead of `|reduce(j,k,c,d,n)|'. For +example, `|squash(pp,3,exp,-2,3)|' is an abbreviation for `\\{app3}|(pp); +reduce(pp,3,exp,-2,3)|'. + +A couple more words of explanation: +Both |big_app| and |app| append a token (while |big_app1| to |big_app4| +append the specified number of scrap translations) to the current token list. +The difference between |big_app| and |app| is simply that |big_app| +checks whether there can be a conflict between math and non-math +tokens, and intercalates a `\.{\$}' token if necessary. When in +doubt what to use, use |big_app|. + +The |mathness| is an attribute of scraps that says whether they are +to be printed in a math mode context or not. It is separate from the +``part of speech'' (the |cat|) because to make each |cat| have +a fixed |mathness| (as in the original \.{WEAVE}) would multiply the +number of necessary production rules. + +The low two bits (i.e. |mathness % 4|) control the left boundary. +(We need two bits because we allow cases |yes_math|, |no_math| and +|maybe_math|, which can go either way.) +The next two bits (i.e. |mathness / 4|) control the right boundary. +If we combine two scraps and the right boundary of the first has +a different mathness from the left boundary of the second, we +insert a \.{\$} in between. Similarly, if at printing time some +irreducible scrap has a |yes_math| boundary the scrap gets preceded +or followed by a \.{\$}. The left boundary is |maybe_math| if and +only if the right boundary is. + +The code below is an exact translation of the production rules into +\CEE/, using such macros, and the reader should have no difficulty +understanding the format by comparing the code with the symbolic +productions as they were listed earlier. + +@d no_math 2 /* should be in horizontal mode */ +@d yes_math 1 /* should be in math mode */ +@d maybe_math 0 /* works in either horizontal or math mode */ +@d big_app2(a) big_app1(a);big_app1(a+1) +@d big_app3(a) big_app2(a);big_app1(a+2) +@d big_app4(a) big_app3(a);big_app1(a+3) +@d app(a) *(tok_ptr++)=a +@d app1(a) *(tok_ptr++)=tok_flag+(int)((a)->trans-tok_start) + +@<Global...@>= +int cur_mathness, init_mathness; + +@ @c +void +app_str(s) +char *s; +{ + while (*s) app_tok(*(s++)); +} + +void +big_app(a) +token a; +{ + if (a==' ' || (a>=big_cancel && a<=big_force)) /* non-math token */ { + if (cur_mathness==maybe_math) init_mathness=no_math; + else if (cur_mathness==yes_math) app_str("{}$"); + cur_mathness=no_math; + } + else { + if (cur_mathness==maybe_math) init_mathness=yes_math; + else if (cur_mathness==no_math) app_str("${}"); + cur_mathness=yes_math; + } + app(a); +} + +void +big_app1(a) +scrap_pointer a; +{ + switch (a->mathness % 4) { /* left boundary */ + case (no_math): + if (cur_mathness==maybe_math) init_mathness=no_math; + else if (cur_mathness==yes_math) app_str("{}$"); + cur_mathness=a->mathness / 4; /* right boundary */ + break; + case (yes_math): + if (cur_mathness==maybe_math) init_mathness=yes_math; + else if (cur_mathness==no_math) app_str("${}"); + cur_mathness=a->mathness / 4; /* right boundary */ + break; + case (maybe_math): /* no changes */ break; + } + app(tok_flag+(int)((a)->trans-tok_start)); +} + +@ Let us consider the big switch for productions now, before looking +at its context. We want to design the program so that this switch +works, so we might as well not keep ourselves in suspense about exactly what +code needs to be provided with a proper environment. + +@d cat1 (pp+1)->cat +@d cat2 (pp+2)->cat +@d cat3 (pp+3)->cat +@d lhs_not_simple (pp->cat!=semi && pp->cat!=raw_int && pp->cat!=raw_unorbin + && pp->cat!=raw_rpar && pp->cat!=const_like) + +@<Match a production at |pp|, or increase |pp| if there is no match@>= { + if (cat1==end_arg && lhs_not_simple) + if (pp->cat==begin_arg) squash(pp,2,exp,-2,110); + else squash(pp,2,end_arg,-1,111); + else if (cat1==insert) squash(pp,2,pp->cat,-2,0); + else if (cat2==insert) squash(pp+1,2,(pp+1)->cat,-1,0); + else if (cat3==insert) squash(pp+2,2,(pp+2)->cat,0,0); + else + switch (pp->cat) { + case exp: @<Cases for |exp|@>; @+break; + case lpar: @<Cases for |lpar|@>; @+break; + case question: @<Cases for |question|@>; @+break; + case unop: @<Cases for |unop|@>; @+break; + case unorbinop: @<Cases for |unorbinop|@>; @+break; + case binop: @<Cases for |binop|@>; @+break; + case cast: @<Cases for |cast|@>; @+break; + case sizeof_like: @<Cases for |sizeof_like|@>; @+break; + case int_like: @<Cases for |int_like|@>; @+break; + case decl_head: @<Cases for |decl_head|@>; @+break; + case decl: @<Cases for |decl|@>; @+break; + case typedef_like: @<Cases for |typedef_like|@>; @+break; + case struct_like: @<Cases for |struct_like|@>; @+break; + case struct_head: @<Cases for |struct_head|@>; @+break; + case fn_decl: @<Cases for |fn_decl|@>; @+break; + case function: @<Cases for |function|@>; @+break; + case lbrace: @<Cases for |lbrace|@>; @+break; + case do_like: @<Cases for |do_like|@>; @+break; + case if_like: @<Cases for |if_like|@>; @+break; + case for_like: @<Cases for |for_like|@>; @+break; + case else_like: @<Cases for |else_like|@>; @+break; + case if_clause: @<Cases for |if_clause|@>; @+break; + case if_head: @<Cases for |if_head|@>; @+break; + case else_head: @<Cases for |else_head|@>; @+break; + case case_like: @<Cases for |case_like|@>; @+break; + case stmt: @<Cases for |stmt|@>; @+break; + case tag: @<Cases for |tag|@>; @+break; + case semi: @<Cases for |semi|@>; @+break; + case lproc: @<Cases for |lproc|@>; @+break; + case section_scrap: @<Cases for |section_scrap|@>; @+break; + case insert: @<Cases for |insert|@>; @+break; + case prelangle: @<Cases for |prelangle|@>; @+break; + case prerangle: @<Cases for |prerangle|@>; @+break; + case langle: @<Cases for |langle|@>; @+break; + case public_like: @<Cases for |public_like|@>; @+break; + case colcol: @<Cases for |colcol|@>; @+break; + case new_like: @<Cases for |new_like|@>; @+break; + case operator_like: @<Cases for |operator_like|@>; @+break; + case catch_like: @<Cases for |catch_like|@>; @+break; + case base: @<Cases for |base|@>; @+break; + case raw_rpar: @<Cases for |raw_rpar|@>; @+break; + case raw_unorbin: @<Cases for |raw_unorbin|@>; @+break; + case const_like: @<Cases for |const_like|@>; @+break; + case raw_int: @<Cases for |raw_int|@>; @+break; + } + pp++; /* if no match was found, we move to the right */ +} + +@ In \CEE/, new specifier names can be defined via |typedef|, and we want +to make the parser recognize future occurrences of the identifier thus +defined as specifiers. This is done by the procedure |make_reserved|, +which changes the |ilk| of the relevant identifier. + +We first need a procedure to recursively seek the first +identifier in a token list, because the identifier might +be enclosed in parentheses, as when one defines a function +returning a pointer. + +@d no_ident_found 0 /* distinct from any identifier token */ + +@c +token_pointer +find_first_ident(p) +text_pointer p; +{ + token_pointer q; /* token to be returned */ + token_pointer j; /* token being looked at */ + sixteen_bits r; /* remainder of token after the flag has been stripped off */ + if (p>=text_ptr) confusion("find_first_ident"); + for (j=*p; j<*(p+1); j++) { + r=*j%id_flag; + switch (*j/id_flag) { + case 1: case 2: return j; + case 4: case 5: /* |tok_flag| or |inner_tok_flag| */ + if ((q=find_first_ident(tok_start+r))!=no_ident_found) + return q; + default: ; /* char, |section_flag|, fall thru: move on to next token */ + if (*j==inserted) return no_ident_found; /* ignore inserts */ + } + } + return no_ident_found; +} + +@ The scraps currently being parsed must be inspected for any +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))==no_ident_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) { + if (**(p->trans)==tok_value) { + p->cat=raw_int; + **(p->trans)=tok_value%id_flag+res_flag; + } + } + } + (name_dir+(sixteen_bits)(tok_value%id_flag))->ilk=raw_int; + *tok_loc=tok_value%id_flag+res_flag; +} + +@ In the following situations we want to mark the occurrence of +an identifier as a definition: when |make_reserved| is just about to be +used; after a specifier, as in |char **argv|; +before a colon, as in \\{found}:; and in the declaration of a function, +as in \\{main}()$\{\ldots;\}$. This is accomplished by the invocation +of |make_underlined| at appropriate times. Notice that, in the declaration +of a function, we only find out that the identifier is being defined after +it has been swallowed up by an |exp|. + +@c +void +make_underlined(p) +/* 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))==no_ident_found) + return; /* this happens after parsing the |()| in |double f();| */ + xref_switch=def_flag; + underline_xref(*tok_loc%id_flag+name_dir); +} + +@ We cannot use |new_xref| to underline a cross-reference at this point +because this would just make a new cross-reference at the end of the list. +We actually have to search through the list for the existing +cross-reference. + +@<Predecl...@>= +void underline_xref(); + +@ @c +void +underline_xref(p) +name_pointer p; +{ + xref_pointer q=(xref_pointer)p->xref; /* pointer to cross-reference being examined */ + xref_pointer r; /* temporary pointer for permuting cross-references */ + sixteen_bits m; /* cross-reference value to be installed */ + sixteen_bits n; /* cross-reference value being examined */ + if (no_xref) return; + m=section_count+xref_switch; + while (q != xmem) { + n=q->num; + if (n==m) return; + else if (m==n+def_flag) { + q->num=m; return; + } + else if (n>=def_flag && n<m) break; + q=q->xlink; + } + @<Insert new cross-reference at |q|, not at beginning of list@>; +} + +@ We get to this section only when the identifier is one letter long, +so it didn't get a non-underlined entry during phase one. But it may +have got some explicitly underlined entries in later sections, so in order +to preserve the numerical order of the entries in the index, we have +to insert the new cross-reference not at the beginning of the list +(namely, at |p->xref|), but rather right before |q|. + +@<Insert new cross-reference at |q|...@>= + append_xref(0); /* this number doesn't matter */ + xref_ptr->xlink=(xref_pointer)p->xref; r=xref_ptr; + p->xref=(char*)xref_ptr; + while (r->xlink!=q) {r->num=r->xlink->num; r=r->xlink;} + r->num=m; /* everything from |q| on is left undisturbed */ + +@ 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 +to be performed, followed by |goto found|. + +@<Cases for |exp|@>= +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); +else if ((cat1==binop || cat1==unorbinop) && cat2==exp) + squash(pp,3,exp,-2,3); +else if (cat1==comma && cat2==exp) { + big_app2(pp); + app(opt); app('9'); big_app1(pp+2); reduce(pp,3,exp,-2,4); +} +else if (cat1==exp || cat1==cast) squash(pp,2,exp,-2,5); +else if (cat1==semi) squash(pp,2,stmt,-1,6); +else if (cat1==colon) { + make_underlined (pp); squash(pp,2,tag,0,7); +} +else if (cat1==base) { + if (cat2==int_like && cat3==comma) { + big_app1(pp+1); big_app(' '); big_app2(pp+2); + app(opt); app('9'); reduce(pp+1,3,base,0,8); + } + else if (cat2==int_like && cat3==lbrace) { + big_app1(pp); big_app(' '); big_app1(pp+1); big_app(' '); big_app1(pp+2); + reduce(pp,3,exp,-1,9); + } +} +else if (cat1==rbrace) squash(pp,1,stmt,-1,10); + +@ @<Cases for |lpar|@>= +if ((cat1==exp||cat1==unorbinop) && cat2==rpar) squash(pp,3,exp,-2,11); +else if (cat1==rpar) { + big_app1(pp); app('\\'); app(','); big_app1(pp+1); +@.\\,@> + reduce(pp,2,exp,-2,12); +} +else if (cat1==decl_head || cat1==int_like || cat1==exp) { + if (cat2==rpar) squash(pp,3,cast,-2,13); + else if (cat2==comma) { + big_app3(pp); app(opt); app('9'); reduce(pp,3,lpar,0,14); + } +} +else if (cat1==stmt || cat1==decl) { + big_app2(pp); big_app(' '); reduce(pp,2,lpar,0,15); +} + +@ @<Cases for |question|@>= +if (cat1==exp && cat2==colon) squash(pp,3,binop,-2,16); + +@ @<Cases for |unop|@>= +if (cat1==exp || cat1==int_like) squash(pp,2,cat1,-2,17); + +@ @<Cases for |unorbinop|@>= +if (cat1==exp || cat1==int_like) { + big_app('{'); big_app1(pp); big_app('}'); big_app1(pp+1); + reduce(pp,2,cat1,-2,18); +} +else if (cat1==binop) { + big_app(math_rel); big_app1(pp); big_app('{'); big_app1(pp+1); big_app('}'); + big_app('}'); reduce(pp,2,binop,-1,19); +} + +@ @<Cases for |binop|@>= +if (cat1==binop) { + big_app(math_rel); big_app('{'); big_app1(pp); big_app('}'); + big_app('{'); big_app1(pp+1); big_app('}'); + big_app('}'); reduce(pp,2,binop,-1,20); +} + +@ @<Cases for |cast|@>= +if (cat1==exp) { + big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,exp,-2,21); +} +else if (cat1==semi) squash(pp,1,exp,-2,22); + +@ @<Cases for |sizeof_like|@>= +if (cat1==cast) squash(pp,2,exp,-2,23); +else if (cat1==exp) { + big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,exp,-2,24); +} + +@ @<Cases for |int_like|@>= +if (cat1==int_like|| cat1==struct_like) { + big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,cat1,-2,25); +} +else if (cat1==exp && (cat2==raw_int||cat2==struct_like)) + squash(pp,2,int_like,-2,26); +else if (cat1==exp || cat1==unorbinop || cat1==semi) { + big_app1(pp); + if (cat1!=semi) big_app(' '); + reduce(pp,1,decl_head,-1,27); +} +else if (cat1==colon) { + big_app1(pp); big_app(' '); reduce(pp,1,decl_head,0,28); +} +else if (cat1==prelangle) squash(pp+1,1,langle,1,29); +else if (cat1==colcol && (cat2==exp||cat2==int_like)) squash(pp,3,cat2,-2,30); +else if (cat1==cast) { + if (cat2==lbrace) { + big_app2(pp); big_app(indent); big_app(indent); + reduce(pp,2,fn_decl,1,31); + } + else squash(pp,2,int_like,-2,32); +} + +@ @<Cases for |decl_head|@>= +if (cat1==comma) { + big_app2(pp); big_app(' '); reduce(pp,2,decl_head,-1,33); +} +else if (cat1==unorbinop) { + big_app1(pp); big_app('{'); big_app1(pp+1); big_app('}'); + reduce(pp,2,decl_head,-1,34); +} +else if (cat1==exp && cat2!=lpar && cat2!=exp) { + 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)) + squash(pp,3,decl_head,-1,36); +else if (cat1==cast) squash(pp,2,decl_head,-1,37); +else if (cat1==lbrace || (cat1==int_like&&cat2!=colcol) || cat1==decl) { + big_app1(pp); big_app(indent); app(indent); reduce(pp,1,fn_decl,0,38); +} +else if (cat1==semi) squash(pp,2,decl,-1,39); + +@ @<Cases for |decl|@>= +if (cat1==decl) { + big_app1(pp); big_app(force); big_app1(pp+1); + reduce(pp,2,decl,-1,40); +} +else if (cat1==stmt || cat1==function) { + big_app1(pp); big_app(big_force); + big_app1(pp+1); reduce(pp,2,cat1,-1,41); +} + +@ @<Cases for |typedef_like|@>= +if (cat1==decl_head) + if ((cat2==exp&&cat3!=lpar&&cat3!=exp)||cat2==int_like) { + make_underlined(pp+2); make_reserved(pp+2); + big_app2(pp+1); reduce(pp+1,2,decl_head,0,42); + } + else if (cat2==semi) { + big_app1(pp); big_app(' '); big_app2(pp+1); reduce(pp,3,decl,-1,43); + } + +@ @<Cases for |struct_like|@>= +if (cat1==lbrace) { + big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,struct_head,0,44); +} +else if (cat1==exp||cat1==int_like) { + if (cat2==lbrace || cat2==semi) { + make_underlined(pp+1); make_reserved(pp+1); + big_app1(pp); big_app(' '); big_app1(pp+1); + if (cat2==semi) reduce(pp,2,decl_head,0,45); + else { + big_app(' '); big_app1(pp+2);reduce(pp,3,struct_head,0,46); + } + } + else if (cat2==colon) squash(pp+2,1,base,-1,47); + else if (cat2!=base) { + big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,int_like,-2,48); + } +} + +@ @<Cases for |struct_head|@>= +if ((cat1==decl || cat1==stmt || cat1==function) && cat2==rbrace) { + big_app1(pp); big_app(indent); big_app(force); big_app1(pp+1); + big_app(outdent); big_app(force); big_app1(pp+2); + reduce(pp,3,int_like,-2,49); +} +else if (cat1==rbrace) { + big_app1(pp); app_str("\\,"); big_app1(pp+1); +@.\\,@> + reduce(pp,2,int_like,-2,50); +} + +@ @<Cases for |fn_decl|@>= +if (cat1==decl) { + big_app1(pp); big_app(force); big_app1(pp+1); reduce(pp,2,fn_decl,0,51); +} +else if (cat1==stmt) { + big_app1(pp); app(outdent); app(outdent); big_app(force); + big_app1(pp+1); reduce(pp,2,function,-1,52); +} + +@ @<Cases for |function|@>= +if (cat1==function || cat1==decl || cat1==stmt) { + big_app1(pp); big_app(big_force); big_app1(pp+1); reduce(pp,2,cat1,-1,53); +} + +@ @<Cases for |lbrace|@>= +if (cat1==rbrace) { + big_app1(pp); app('\\'); app(','); big_app1(pp+1); +@.\\,@> + reduce(pp,2,stmt,-1,54); +} +else if ((cat1==stmt||cat1==decl||cat1==function) && cat2==rbrace) { + big_app(force); big_app1(pp); big_app(indent); big_app(force); + big_app1(pp+1); big_app(force); big_app(backup); big_app1(pp+2); + big_app(outdent); big_app(force); reduce(pp,3,stmt,-1,55); +} +else if (cat1==exp) { + if (cat2==rbrace) squash(pp,3,exp,-2,56); + else if (cat2==comma && cat3==rbrace) squash(pp,4,exp,-2,56); +} + +@ @<Cases for |if_like|@>= +if (cat1==exp) { + big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,if_clause,0,57); +} + +@ @<Cases for |for_like|@>= +if (cat1==exp) { + big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,else_like,-2,58); +} + +@ @<Cases for |else_like|@>= +if (cat1==lbrace) squash(pp,1,else_head,0,59); +else if (cat1==stmt) { + big_app(force); big_app1(pp); big_app(indent); big_app(break_space); + big_app1(pp+1); big_app(outdent); big_app(force); + reduce(pp,2,stmt,-1,60); +} + +@ @<Cases for |else_head|@>= +if (cat1==stmt || cat1==exp) { + big_app(force); big_app1(pp); big_app(break_space); app(noop); + big_app(cancel); big_app1(pp+1); big_app(force); + reduce(pp,2,stmt,-1,61); +} + +@ @<Cases for |if_clause|@>= +if (cat1==lbrace) squash(pp,1,if_head,0,62); +else if (cat1==stmt) { + if (cat2==else_like) { + big_app(force); big_app1(pp); big_app(indent); big_app(break_space); + big_app1(pp+1); big_app(outdent); big_app(force); big_app1(pp+2); + if (cat3==if_like) { + big_app(' '); big_app1(pp+3); reduce(pp,4,if_like,0,63); + }@+else reduce(pp,3,else_like,0,64); + } + else squash(pp,1,else_like,0,65); +} + +@ @<Cases for |if_head|@>= +if (cat1==stmt || cat1==exp) { + if (cat2==else_like) { + big_app(force); big_app1(pp); big_app(break_space); app(noop); + big_app(cancel); big_app1(pp+1); big_app(force); big_app1(pp+2); + if (cat3==if_like) { + big_app(' '); big_app1(pp+3); reduce(pp,4,if_like,0,66); + }@+else reduce(pp,3,else_like,0,67); + } + else squash(pp,1,else_head,0,68); +} + +@ @<Cases for |do_like|@>= +if (cat1==stmt && cat2==else_like && cat3==semi) { + big_app1(pp); big_app(break_space); app(noop); big_app(cancel); + big_app1(pp+1); big_app(cancel); app(noop); big_app(break_space); + big_app2(pp+2); reduce(pp,4,stmt,-1,69); +} + +@ @<Cases for |case_like|@>= +if (cat1==semi) squash(pp,2,stmt,-1,70); +else if (cat1==colon) squash(pp,2,tag,-1,71); +else if (cat1==exp) { + if (cat2==semi) { + big_app1(pp); big_app(' '); big_app1(pp+1); big_app1(pp+2); + reduce(pp,3,stmt,-1,72); + } + else if (cat2==colon) { + big_app1(pp); big_app(' '); big_app1(pp+1); big_app1(pp+2); + reduce(pp,3,tag,-1,73); + } +} + +@ @<Cases for |tag|@>= +if (cat1==tag) { + big_app1(pp); big_app(break_space); big_app1(pp+1); reduce(pp,2,tag,-1,74); +} +else if (cat1==stmt||cat1==decl||cat1==function) { + big_app(force); big_app(backup); big_app1(pp); big_app(break_space); + big_app1(pp+1); reduce(pp,2,cat1,-1,75); +} + +@ The user can decide at run-time whether short statements should be +grouped together on the same line. + +@d force_lines flags['f'] /* should each statement be on its own line? */ +@<Cases for |stmt|@>= +if (cat1==stmt||cat1==decl||cat1==function) { + big_app1(pp); + if (cat1==function) big_app(big_force); + else if (cat1==decl) big_app(big_force); + else if (force_lines) big_app(force); + else big_app(break_space); + big_app1(pp+1); reduce(pp,2,cat1,-1,76); +} + +@ @<Cases for |semi|@>= +big_app(' '); big_app1(pp); reduce(pp,1,stmt,-1,77); + +@ @<Cases for |lproc|@>= +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) { + app(inserted); big_app2(pp); reduce(pp,2,insert,-1,79); +} else if (cat1==exp || cat1==function) { + if (cat2==rproc) { + app(inserted); big_app1(pp); big_app(' '); big_app2(pp+1); + reduce(pp,3,insert,-1,80); + } + else if (cat2==exp && cat3==rproc && cat1==exp) { + app(inserted); big_app1(pp); big_app(' '); big_app1(pp+1); app_str(" \\5"); +@.\\5@> + big_app2(pp+2); reduce(pp,4,insert,-1,80); + } +} + +@ @<Cases for |section_scrap|@>= +if (cat1==semi) { + big_app2(pp); big_app(force); reduce(pp,2,stmt,-2,81); +} +else squash(pp,1,exp,-2,82); + +@ @<Cases for |insert|@>= +if (cat1) + squash(pp,2,cat1,0,83); + +@ @<Cases for |prelangle|@>= +init_mathness=cur_mathness=yes_math; +app('<'); reduce(pp,1,binop,-2,84); + +@ @<Cases for |prerangle|@>= +init_mathness=cur_mathness=yes_math; +app('>'); reduce(pp,1,binop,-2,85); + +@ @<Cases for |langle|@>= +if (cat1==exp && cat2==prerangle) squash(pp,3,cast,-1,86); +else if (cat1==prerangle) { + big_app1(pp); app('\\'); app(','); big_app1(pp+1); +@.\\,@> + reduce(pp,2,cast,-1,87); +} +else if (cat1==decl_head || cat1==int_like) { + if (cat2==prerangle) squash(pp,3,cast,-1,88); + else if (cat2==comma) { + big_app3(pp); app(opt); app('9'); reduce(pp,3,langle,0,89); + } +} + +@ @<Cases for |public_like|@>= +if (cat1==colon) squash(pp,2,tag,-1,90); +else squash(pp,1,int_like,-2,91); + +@ @<Cases for |colcol|@>= +if (cat1==exp||cat1==int_like) squash(pp,2,cat1,-2,92); + +@ @<Cases for |new_like|@>= +if (cat1==exp || (cat1==raw_int&&cat2!=prelangle&&cat2!=langle)) { + big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,new_like,0,93); +} +else if (cat1==raw_unorbin || cat1==colcol) + squash(pp,2,new_like,0,94); +else if (cat1==cast) squash(pp,2,exp,-2,95); +else if (cat1!=lpar && cat1!=raw_int && cat1!=struct_like) + squash(pp,1,exp,-2,96); + +@ @<Cases for |operator_like|@>= +if (cat1==binop || cat1==unop || cat1==unorbinop) { + if (cat2==binop) break; + big_app1(pp); big_app('{'); big_app1(pp+1); big_app('}'); + reduce(pp,2,exp,-2,97); +} +else if (cat1==new_like || cat1==sizeof_like) { + big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,exp,-2,98); +} +else squash(pp,1,new_like,0,99); + +@ @<Cases for |catch_like|@>= +if (cat1==cast || cat1==exp) { + big_app2(pp); big_app(indent); big_app(indent); + reduce(pp,2,fn_decl,0,100); +} + +@ @<Cases for |base|@>= +if (cat1==public_like && cat2==exp) { + if (cat3==comma) { + big_app2(pp); big_app(' '); big_app2(pp+2); + reduce(pp,4,base,0,101); + } else { + big_app1(pp+1); big_app(' '); big_app1(pp+2); + reduce(pp+1,2,int_like,-1,102); + } +} + +@ @<Cases for |raw_rpar|@>= +if (cat1==const_like && @| + (cat2==semi || cat2==lbrace || cat2==comma || cat2==binop + || cat2==const_like)) { + big_app1(pp); big_app(' '); + big_app1(pp+1); reduce(pp,2,raw_rpar,0,103); +} else squash(pp,1,rpar,-3,104); + +@ @<Cases for |raw_unorbin|@>= +if (cat1==const_like) { + big_app2(pp); app_str("\\ "); reduce(pp,2,raw_unorbin,0,105); +@.\\ @> +} else squash(pp,1,unorbinop,-2,106); + +@ @<Cases for |const_like|@>= +squash(pp,1,int_like,-2,107); + +@ @<Cases for |raw_int|@>= +if (cat1==lpar) squash(pp,1,exp,-2,108); +else squash(pp,1,int_like,-3,109); + +@ The `|freeze_text|' macro is used to give official status to a token list. +Before saying |freeze_text|, items are appended to the current token list, +and we know that the eventual number of this token list will be the current +value of |text_ptr|. But no list of that number really exists as yet, +because no ending point for the current list has been +stored in the |tok_start| array. After saying |freeze_text|, the +old current token list becomes legitimate, and its number is the current +value of |text_ptr-1| since |text_ptr| has been increased. The new +current token list is empty and ready to be appended to. +Note that |freeze_text| does not check to see that |text_ptr| hasn't gotten +too large, since it is assumed that this test was done beforehand. + +@d freeze_text *(++text_ptr)=tok_ptr + +@ Here's the |reduce| procedure used in our code for productions: + +@c +void +reduce(j,k,c,d,n) +scrap_pointer j; +eight_bits c; +short k, d, n; +{ + scrap_pointer i, i1; /* pointers into scrap memory */ + j->cat=c; j->trans=text_ptr; + j->mathness=4*cur_mathness+init_mathness; + freeze_text; + if (k>1) { + for (i=j+k, i1=j+1; i<=lo_ptr; i++, i1++) { + i1->cat=i->cat; i1->trans=i->trans; + i1->mathness=i->mathness; + } + lo_ptr=lo_ptr-k+1; + } + @<Change |pp| to $\max(|scrap_base|,|pp|+d)$@>; + @<Print a snapshot of the scrap list if debugging @>; + pp--; /* we next say |pp++| */ +} + +@ @<Change |pp| to $\max...@>= +if (pp+d>=scrap_base) pp=pp+d; +else pp=scrap_base; + +@ Here's the |squash| procedure, which +takes advantage of the simplification that occurs when |k==1|. + +@c +void +squash(j,k,c,d,n) +scrap_pointer j; +eight_bits c; +short k, d, n; +{ + scrap_pointer i; /* pointers into scrap memory */ + if (k==1) { + j->cat=c; @<Change |pp|...@>; + @<Print a snapshot...@>; + pp--; /* we next say |pp++| */ + return; + } + for (i=j; i<j+k; i++) big_app1(i); + reduce(j,k,c,d,n); +} + +@ Here now is the code that applies productions as long as possible. +Before applying the production mechanism, we must make sure +it has good input (at least four scraps, the length of the lhs of the +longest rules), and that there is enough room in the memory arrays +to hold the appended tokens and texts. Here we use a very +conservative test: it's more important to make sure the program +will still work if we change the production rules (within reason) +than to squeeze the last bit of space from the memory arrays. + +@d safe_tok_incr 20 +@d safe_text_incr 10 +@d safe_scrap_incr 10 + +@<Reduce the scraps using the productions until no more rules apply@>= +while (1) { + @<Make sure the entries |pp| through |pp+3| of |cat| are defined@>; + if (tok_ptr+safe_tok_incr>tok_mem_end) { + if (tok_ptr>max_tok_ptr) max_tok_ptr=tok_ptr; + overflow("token"); + } + if (text_ptr+safe_text_incr>tok_start_end) { + if (text_ptr>max_text_ptr) max_text_ptr=text_ptr; + overflow("text"); + } + if (pp>lo_ptr) break; + init_mathness=cur_mathness=maybe_math; + @<Match a production...@>; +} + +@ If we get to the end of the scrap list, category codes equal to zero are +stored, since zero does not match anything in a production. + +@<Make sure the entries...@>= +if (lo_ptr<pp+3) { + while (hi_ptr<=scrap_ptr && lo_ptr!=pp+3) { + (++lo_ptr)->cat=hi_ptr->cat; lo_ptr->mathness=(hi_ptr)->mathness; + lo_ptr->trans=(hi_ptr++)->trans; + } + for (i=lo_ptr+1;i<=pp+3;i++) i->cat=0; +} + +@ If \.{CWEAVE} is being run in debugging mode, the production numbers and +current stack categories will be printed out when |tracing| is set to 2; +a sequence of two or more irreducible scraps will be printed out when +|tracing| is set to 1. + +@<Global...@>= +int tracing; /* can be used to show parsing details */ + +@ @<Print a snapsh...@>= +{ scrap_pointer k; /* pointer into |scrap_info| */ + if (tracing==2) { + printf("\n%d:",n); + for (k=scrap_base; k<=lo_ptr; k++) { + if (k==pp) putxchar('*'); else putxchar(' '); + if (k->mathness %4 == yes_math) putchar('+'); + else if (k->mathness %4 == no_math) putchar('-'); + print_cat(k->cat); + if (k->mathness /4 == yes_math) putchar('+'); + else if (k->mathness /4 == no_math) putchar('-'); + } + if (hi_ptr<=scrap_ptr) printf("..."); /* indicate that more is coming */ + } +} + +@ The |translate| function assumes that scraps have been stored in +positions |scrap_base| through |scrap_ptr| of |cat| and |trans|. It +applies productions as much as +possible. The result is a token list containing the translation of +the given sequence of scraps. + +After calling |translate|, we will have |text_ptr+3<=max_texts| and +|tok_ptr+6<=max_toks|, so it will be possible to create up to three token +lists with up to six tokens without checking for overflow. Before calling +|translate|, we should have |text_ptr<max_texts| and |scrap_ptr<max_scraps|, +since |translate| might add a new text and a new scrap before it checks +for overflow. + +@c +text_pointer +translate() /* converts a sequence of scraps */ +{ + scrap_pointer i, /* index into |cat| */ + j; /* runs through final scraps */ + pp=scrap_base; lo_ptr=pp-1; hi_ptr=pp; + @<If tracing, print an indication of where we are@>; + @<Reduce the scraps...@>; + @<Combine the irreducible scraps that remain@>; +} + +@ If the initial sequence of scraps does not reduce to a single scrap, +we concatenate the translations of all remaining scraps, separated by +blank spaces, with dollar signs surrounding the translations of scraps +where appropriate. + +@<Combine the irreducible...@>= { + @<If semi-tracing, show the irreducible scraps@>; + for (j=scrap_base; j<=lo_ptr; j++) { + if (j!=scrap_base) app(' '); + if (j->mathness % 4 == yes_math) app('$'); + app1(j); + if (j->mathness / 4 == yes_math) app('$'); + if (tok_ptr+6>tok_mem_end) overflow("token"); + } + freeze_text; return(text_ptr-1); +} + +@ @<If semi-tracing, show the irreducible scraps@>= +if (lo_ptr>scrap_base && tracing==1) { + printf("\nIrreducible scrap sequence in section %d:",section_count); +@.Irreducible scrap sequence...@> + mark_harmless; + for (j=scrap_base; j<=lo_ptr; j++) { + printf(" "); print_cat(j->cat); + } +} + +@ @<If tracing,...@>= +if (tracing==2) { + printf("\nTracing after l. %d:\n",cur_line); mark_harmless; +@.Tracing after...@> + if (loc>buffer+50) { + printf("..."); + term_write(loc-51,51); + } + else term_write(buffer,loc-buffer); +} + +@* Initializing the scraps. +If we are going to use the powerful production mechanism just developed, we +must get the scraps set up in the first place, given a \CEE/ text. A table +of the initial scraps corresponding to \CEE/ tokens appeared above in the +section on parsing; our goal now is to implement that table. We shall do this +by implementing a subroutine called |C_parse| that is analogous to the +|C_xref| routine used during phase one. + +Like |C_xref|, the |C_parse| procedure starts with the current +value of |next_control| and it uses the operation |next_control=get_next()| +repeatedly to read \CEE/ text until encountering the next `\.{\v}' or +`\.{/*}', or until |next_control>=format_code|. The scraps corresponding to +what it reads are appended into the |cat| and |trans| arrays, and |scrap_ptr| +is advanced. + +@c +void +C_parse(spec_ctrl) /* creates scraps from \CEE/ tokens */ + eight_bits spec_ctrl; +{ + int count; /* characters remaining before string break */ + while (next_control<format_code || next_control==spec_ctrl) { + @<Append the scrap appropriate to |next_control|@>; + next_control=get_next(); + if (next_control=='|' || next_control==begin_comment || + next_control==begin_short_comment) return; + } +} + +@ The following macro is used to append a scrap whose tokens have just +been appended: + +@d app_scrap(c,b) { + (++scrap_ptr)->cat=(c); scrap_ptr->trans=text_ptr; + scrap_ptr->mathness=5*(b); /* no no, yes yes, or maybe maybe */ + freeze_text; +} + +@ @<Append the scr...@>= +@<Make sure that there is room for the new scraps, tokens, and texts@>; +switch (next_control) { + case section_name: + app(section_flag+(int)(cur_section-name_dir)); + app_scrap(section_scrap,maybe_math); + app_scrap(exp,yes_math);@+break; + case string: case constant: case verbatim: @<Append a string or constant@>; + @+break; + case identifier: app_cur_id(1);@+break; + case TeX_string: @<Append a \TEX/ string, without forming a scrap@>;@+break; + case '/': case '.': + app(next_control); app_scrap(binop,yes_math);@+break; + case '<': app_str("\\langle");@+app_scrap(prelangle,yes_math);@+break; +@.\\langle@> + case '>': app_str("\\rangle");@+app_scrap(prerangle,yes_math);@+break; +@.\\rangle@> + case '=': app_str("\\K"); app_scrap(binop,yes_math);@+break; +@.\\K@> + case '|': app_str("\\OR"); app_scrap(binop,yes_math);@+break; +@.\\OR@> + case '^': app_str("\\XOR"); app_scrap(binop,yes_math);@+break; +@.\\XOR@> + case '%': app_str("\\MOD"); app_scrap(binop,yes_math);@+break; +@.\\MOD@> + case '!': app_str("\\R"); app_scrap(unop,yes_math);@+break; +@.\\R@> + case '~': app_str("\\CM"); app_scrap(unop,yes_math);@+break; +@.\\CM@> + case '+': case '-': app(next_control); app_scrap(unorbinop,yes_math);@+break; + case '*': app(next_control); app_scrap(raw_unorbin,yes_math);@+break; + case '&': app_str("\\AND"); app_scrap(raw_unorbin,yes_math);@+break; +@.\\AND@> + case '?': app_str("\\?"); app_scrap(question,yes_math);@+break; +@.\\?@> + case '#': app_str("\\#"); app_scrap(unorbinop,yes_math);@+break; +@.\\\#@> + case ignore: case xref_roman: case xref_wildcard: + case xref_typewriter: case noop:@+break; + case '(': case '[': app(next_control); app_scrap(lpar,maybe_math);@+break; + case ')': case ']': app(next_control); app_scrap(raw_rpar,maybe_math);@+break; + case '{': app_str("\\{"@q}@>); app_scrap(lbrace,yes_math);@+break; +@.\\\{@>@q}@> + case '}': app_str(@q{@>"\\}"); app_scrap(rbrace,yes_math);@+break; +@q{@>@.\\\}@> + case ',': app(','); app_scrap(comma,yes_math);@+break; + case ';': app(';'); app_scrap(semi,maybe_math);@+break; + case ':': app(':'); app_scrap(colon,maybe_math);@+break;@/ + @t\4@> @<Cases involving nonstandard characters@>@; + case thin_space: app_str("\\,"); app_scrap(insert,maybe_math);@+break; +@.\\,@> + case math_break: app(opt); app_str("0"); + app_scrap(insert,maybe_math);@+break; + case line_break: app(force); app_scrap(insert,no_math);@+break; + case left_preproc: app(force); app(preproc_line); + app_str("\\#"); app_scrap(lproc,no_math);@+break; +@.\\\#@> + case right_preproc: app(force); app_scrap(rproc,no_math);@+break; + case big_line_break: app(big_force); app_scrap(insert,no_math);@+break; + case no_line_break: app(big_cancel); app(noop); app(break_space); + app(noop); app(big_cancel); + app_scrap(insert,no_math);@+break; + case pseudo_semi: app_scrap(semi,maybe_math);@+break; + case macro_arg_open: app_scrap(begin_arg,maybe_math);@+break; + case macro_arg_close: app_scrap(end_arg,maybe_math);@+break; + case join: app_str("\\J"); app_scrap(insert,no_math);@+break; +@.\\J@> + case output_defs_code: app(force); app_str("\\ATH"); app(force); + app_scrap(insert,no_math);@+break; +@.\\ATH@> + default: app(inserted); app(next_control); + app_scrap(insert,maybe_math);@+break; +} + +@ @<Make sure that there is room for the new...@>= +if (scrap_ptr+safe_scrap_incr>scrap_info_end || + tok_ptr+safe_tok_incr>tok_mem_end @| || + text_ptr+safe_text_incr>tok_start_end) { + if (scrap_ptr>max_scr_ptr) max_scr_ptr=scrap_ptr; + if (tok_ptr>max_tok_ptr) max_tok_ptr=tok_ptr; + if (text_ptr>max_text_ptr) max_text_ptr=text_ptr; + overflow("scrap/token/text"); +} + +@ Some nonstandard characters may have entered \.{CWEAVE} by means of +standard ones. They are converted to \TEX/ control sequences so that it is +possible to keep \.{CWEAVE} from outputting unusual |char| codes. + +@<Cases involving nonstandard...@>= +case not_eq: app_str("\\I");@+app_scrap(binop,yes_math);@+break; +@.\\I@> +case lt_eq: app_str("\\Z");@+app_scrap(binop,yes_math);@+break; +@.\\Z@> +case gt_eq: app_str("\\G");@+app_scrap(binop,yes_math);@+break; +@.\\G@> +case eq_eq: app_str("\\E");@+app_scrap(binop,yes_math);@+break; +@.\\E@> +case and_and: app_str("\\W");@+app_scrap(binop,yes_math);@+break; +@.\\W@> +case or_or: app_str("\\V");@+app_scrap(binop,yes_math);@+break; +@.\\V@> +case plus_plus: app_str("\\PP");@+app_scrap(unop,yes_math);@+break; +@.\\PP@> +case minus_minus: app_str("\\MM");@+app_scrap(unop,yes_math);@+break; +@.\\MM@> +case minus_gt: app_str("\\MG");@+app_scrap(binop,yes_math);@+break; +@.\\MG@> +case gt_gt: app_str("\\GG");@+app_scrap(binop,yes_math);@+break; +@.\\GG@> +case lt_lt: app_str("\\LL");@+app_scrap(binop,yes_math);@+break; +@.\\LL@> +case dot_dot_dot: app_str("\\,\\ldots\\,");@+app_scrap(exp,yes_math);@+break; +@.\\,@> +@.\\ldots@> +case colon_colon: app_str("\\DC");@+app_scrap(colcol,maybe_math);@+break; +@.\\DC@> +case period_ast: app_str("\\PA");@+app_scrap(binop,yes_math);@+break; +@.\\PA@> +case minus_gt_ast: app_str("\\MGA");@+app_scrap(binop,yes_math);@+break; +@.\\MGA@> + +@ The following code must use |app_tok| instead of |app| in order to +protect against overflow. Note that |tok_ptr+1<=max_toks| after |app_tok| +has been used, so another |app| is legitimate before testing again. + +Many of the special characters in a string must be prefixed by `\.\\' so that +\TEX/ will print them properly. +@^special string characters@> + +@<Append a string or...@>= +count= -1; +if (next_control==constant) app_str("\\T{"@q}@>); +@.\\T@> +else if (next_control==string) { + count=20; app_str("\\.{"@q}@>); +} +@.\\.@> +else app_str("\\vb{"@q}@>); +@.\\vb@> +while (id_first<id_loc) { + if (count==0) { /* insert a discretionary break in a long string */ + app_str(@q(@>@q{@>"}\\)\\.{"@q}@>); count=20; +@q(@>@.\\)@> + } +@^high-bit character handling@> + if((eight_bits)(*id_first)>0177) { + app_tok(quoted_char); + app_tok((eight_bits)(*id_first++)); + } + else { + switch (*id_first) { + case ' ':case '\\':case '#':case '%':case '$':case '^': + case '{': case '}': case '~': case '&': case '_': app('\\'); break; +@.\\\ @> +@.\\\\@> +@.\\\#@> +@.\\\%@> +@.\\\$@> +@.\\\^@> +@.\\\{@>@q}@> +@q{@>@.\\\}@> +@.\\\~@> +@.\\\&@> +@.\\_@> @q CWEAVE does quote an underscore! @> + case '@@': if (*(id_first+1)=='@@') id_first++; + else err_print("! Double @@ should be used in strings"); +@.Double @@ should be used...@> + } + app_tok(*id_first++); + } + count--; +} +app(@q{@>'}'); +app_scrap(exp,maybe_math); + +@ We do not make the \TEX/ string into a scrap, because there is no +telling what the user will be putting into it; instead we leave it +open, to be picked up by the next scrap. If it comes at the end of a +section, it will be made into a scrap when |finish_C| is called. + +There's a known bug here, in cases where an adjacent scrap is +|prelangle| or |prerangle|. Then the \TEX/ string can disappear +when the \.{\\langle} or \.{\\rangle} becomes \.{<} or \.{>}. +For example, if the user writes \.{\v x<@@ty@@>\v}, the \TEX/ string +\.{\\hbox\{y\}} eventually becomes part of an |insert| scrap, which is combined +with a |prelangle| scrap and eventually lost. The best way to work around +this bug is probably to enclose the \.{@@t...@@>} in \.{@@[...@@]} so that +the \TEX/ string is treated as an expression. +@^bug, known@> + +@<Append a \TEX/ string, without forming a scrap@>= +app_str("\\hbox{"@q}@>); +@^high-bit character handling@> +while (id_first<id_loc) + if((eight_bits)(*id_first)>0177) { + app_tok(quoted_char); + app_tok((eight_bits)(*id_first++)); + } + else { + if (*id_first=='@@') id_first++; + app_tok(*id_first++); + } +app(@q{@>'}'); + +@ The function |app_cur_id| appends the current identifier to the +token list; it also builds a new scrap if |scrapping==1|. + +@<Predec...@>= +void app_cur_id(); + +@ @c +void +app_cur_id(scrapping) +boolean scrapping; /* are we making this into a scrap? */ +{ + name_pointer p=id_lookup(id_first,id_loc,normal); + if (p->ilk<=quoted) { /* not a reserved word */ + app(id_flag+(int)(p-name_dir)); + if (scrapping) app_scrap(exp,p->ilk>=custom? yes_math: maybe_math); +@.\\NULL@> + } else { + app(res_flag+(int)(p-name_dir)); + if (scrapping) app_scrap(p->ilk,maybe_math); + } +} + +@ When the `\.{\v}' that introduces \CEE/ text is sensed, a call on +|C_translate| will return a pointer to the \TEX/ translation of +that text. If scraps exist in |scrap_info|, they are +unaffected by this translation process. + +@c +text_pointer +C_translate() +{ + text_pointer p; /* points to the translation */ + scrap_pointer save_base; /* holds original value of |scrap_base| */ + save_base=scrap_base; scrap_base=scrap_ptr+1; + C_parse(section_name); /* get the scraps together */ + if (next_control!='|') err_print("! Missing '|' after C text"); +@.Missing '|'...@> + app_tok(cancel); app_scrap(insert,maybe_math); + /* place a |cancel| token as a final ``comment'' */ + p=translate(); /* make the translation */ + if (scrap_ptr>max_scr_ptr) max_scr_ptr=scrap_ptr; + scrap_ptr=scrap_base-1; scrap_base=save_base; /* scrap the scraps */ + return(p); +} + +@ The |outer_parse| routine is to |C_parse| as |outer_xref| +is to |C_xref|: it constructs a sequence of scraps for \CEE/ text +until |next_control>=format_code|. Thus, it takes care of embedded comments. + +@c +void +outer_parse() /* makes scraps from \CEE/ tokens and comments */ +{ + int bal; /* brace level in comment */ + text_pointer p, q; /* partial comments */ + while (next_control<format_code) + if (next_control!=begin_comment && next_control!=begin_short_comment) + C_parse(ignore); + else { + boolean is_long_comment=(next_control==begin_comment); + @<Make sure that there is room for the new...@>; + app(cancel); app(inserted); + if (is_long_comment) app_str("\\C{"@q}@>); +@.\\C@> + else app_str("\\SHC{"@q}@>); +@.\\SHC@> + bal=copy_comment(is_long_comment,1); next_control=ignore; + while (bal>0) { + 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_str("\\PB{"); app(inner_tok_flag+(int)(q-tok_start)); app_tok('}'); +@.\\PB@> + if (next_control=='|') { + bal=copy_comment(is_long_comment,bal); + next_control=ignore; + } + else bal=0; /* an error has been reported */ + } + app(force); app_scrap(insert,no_math); + /* the full comment becomes a scrap */ + } +} + +@* Output of tokens. +So far our programs have only built up multi-layered token lists in +\.{CWEAVE}'s internal memory; we have to figure out how to get them into +the desired final form. The job of converting token lists to characters in +the \TEX/ output file is not difficult, although it is an implicitly +recursive process. Four main considerations had to be kept in mind when +this part of \.{CWEAVE} was designed. (a) There are two modes of output: +|outer| mode, which translates tokens like |force| into line-breaking +control sequences, and |inner| mode, which ignores them except that blank +spaces take the place of line breaks. (b) The |cancel| instruction applies +to adjacent token or tokens that are output, and this cuts across levels +of recursion since `|cancel|' occurs at the beginning or end of a token +list on one level. (c) The \TEX/ output file will be semi-readable if line +breaks are inserted after the result of tokens like |break_space| and +|force|. (d) The final line break should be suppressed, and there should +be no |force| token output immediately after `\.{\\Y\\B}'. + +@ The output process uses a stack to keep track of what is going on at +different ``levels'' as the token lists are being written out. Entries on +this stack have three parts: + +\yskip\hang |end_field| is the |tok_mem| location where the token list of a +particular level will end; + +\yskip\hang |tok_field| is the |tok_mem| location from which the next token +on a particular level will be read; + +\yskip\hang |mode_field| is the current mode, either |inner| or |outer|. + +\yskip\noindent The current values of these 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_tok|, and +|cur_mode|. + +The global variable |stack_ptr| tells how many levels of output are +currently in progress. The end of output occurs when an |end_translation| +token is found, so the stack is never empty except when we first begin the +output process. + +@d inner 0 /* value of |mode| for \CEE/ texts within \TEX/ texts */ +@d outer 1 /* value of |mode| for \CEE/ texts in sections */ + +@<Typed...@>= typedef int mode; +typedef struct { + token_pointer end_field; /* ending location of token list */ + token_pointer tok_field; /* present location within token list */ + boolean mode_field; /* interpretation of control tokens */ +} output_state; +typedef output_state *stack_pointer; + +@ @d cur_end cur_state.end_field /* current ending location in |tok_mem| */ +@d cur_tok cur_state.tok_field /* location of next output token in |tok_mem| */ +@d cur_mode cur_state.mode_field /* current mode of interpretation */ +@d init_stack stack_ptr=stack;cur_mode=outer /* initialize the stack */ + +@<Global...@>= +output_state cur_state; /* |cur_end|, |cur_tok|, |cur_mode| */ +output_state stack[stack_size]; /* info for non-current levels */ +stack_pointer stack_ptr; /* first unused location in the output state stack */ +stack_pointer stack_end=stack+stack_size-1; /* end of |stack| */ +stack_pointer max_stack_ptr; /* largest value assumed by |stack_ptr| */ + +@ @<Set init...@>= +max_stack_ptr=stack; + +@ To insert token-list |p| into the output, the |push_level| subroutine +is called; it saves the old level of output and gets a new one going. +The value of |cur_mode| is not changed. + +@c +void +push_level(p) /* suspends the current level */ +text_pointer p; +{ + if (stack_ptr==stack_end) overflow("stack"); + if (stack_ptr>stack) { /* save current state */ + stack_ptr->end_field=cur_end; + stack_ptr->tok_field=cur_tok; + stack_ptr->mode_field=cur_mode; + } + stack_ptr++; + if (stack_ptr>max_stack_ptr) max_stack_ptr=stack_ptr; + cur_tok=*p; cur_end=*(p+1); +} + +@ Conversely, the |pop_level| routine restores the conditions that were in +force when the current level was begun. This subroutine will never be +called when |stack_ptr==1|. + +@c +void +pop_level() +{ + cur_end=(--stack_ptr)->end_field; + cur_tok=stack_ptr->tok_field; cur_mode=stack_ptr->mode_field; +} + +@ The |get_output| function returns the next byte of output that is not a +reference to a token list. It returns the values |identifier| or |res_word| +or |section_code| if the next token is to be an identifier (typeset in +italics), a reserved word (typeset in boldface) or a section name (typeset +by a complex routine that might generate additional levels of output). +In these cases |cur_name| points to the identifier or section name in +question. + +@<Global...@>= +name_pointer cur_name; + +@ @d res_word 0201 /* returned by |get_output| for reserved words */ +@d section_code 0200 /* returned by |get_output| for section names */ + +@c +eight_bits +get_output() /* returns the next token of output */ +{ + sixteen_bits a; /* current item read from |tok_mem| */ + restart: while (cur_tok==cur_end) pop_level(); + a=*(cur_tok++); + if (a>=0400) { + cur_name=a % id_flag + name_dir; + switch (a / id_flag) { + case 2: return(res_word); /* |a==res_flag+cur_name| */ + case 3: return(section_code); /* |a==section_flag+cur_name| */ + case 4: push_level(a % id_flag + tok_start); goto restart; + /* |a==tok_flag+cur_name| */ + case 5: push_level(a % id_flag + tok_start); cur_mode=inner; goto restart; + /* |a==inner_tok_flag+cur_name| */ + default: return(identifier); /* |a==id_flag+cur_name| */ + } + } + return(a); +} + +@ The real work associated with token output is done by |make_output|. +This procedure appends an |end_translation| token to the current token list, +and then it repeatedly calls |get_output| and feeds characters to the output +buffer until reaching the |end_translation| sentinel. It is possible for +|make_output| to be called recursively, since a section name may include +embedded \CEE/ text; however, the depth of recursion never exceeds one +level, since section names cannot be inside of section names. + +A procedure called |output_C| does the scanning, translation, and +output of \CEE/ text within `\pb' brackets, and this procedure uses +|make_output| to output the current token list. Thus, the recursive call +of |make_output| actually occurs when |make_output| calls |output_C| +while outputting the name of a section. +@^recursion@> + +The token list created from within `\pb' brackets is output as an argument +to \.{\\PB}. Although \.{cwebmac} ignores \.{\\PB}, other macro packages +might use it to localize the special meaning of the macros that mark up +program text. + +@c +void +output_C() /* outputs the current token list */ +{ + token_pointer save_tok_ptr; + text_pointer save_text_ptr; + sixteen_bits save_next_control; /* values to be restored */ + text_pointer p; /* translation of the \CEE/ text */ + save_tok_ptr=tok_ptr; save_text_ptr=text_ptr; + save_next_control=next_control; next_control=ignore; p=C_translate(); + app(inner_tok_flag+(int)(p-tok_start)); + out_str("\\PB{"); make_output(); out('}'); /* output the list */ +@.\\PB@> + if (text_ptr>max_text_ptr) max_text_ptr=text_ptr; + if (tok_ptr>max_tok_ptr) max_tok_ptr=tok_ptr; + text_ptr=save_text_ptr; tok_ptr=save_tok_ptr; /* forget the tokens */ + next_control=save_next_control; /* restore |next_control| to original state */ +} + +@ Here is \.{CWEAVE}'s major output handler. + +@<Predecl...@>= +void make_output(); + +@ @c +void +make_output() /* outputs the equivalents of tokens */ +{ + eight_bits a, /* current output byte */ + b; /* next output byte */ + int c; /* count of |indent| and |outdent| tokens */ + char scratch[longest_name]; /* scratch area for section names */ + char *k, *k_limit; /* indices into |scratch| */ + char *j; /* index into |buffer| */ + char *p; /* index into |byte_mem| */ + char delim; /* first and last character of string being copied */ + char *save_loc, *save_limit; /* |loc| and |limit| to be restored */ + name_pointer cur_section_name; /* name of section being output */ + boolean save_mode; /* value of |cur_mode| before a sequence of breaks */ + app(end_translation); /* append a sentinel */ + freeze_text; push_level(text_ptr-1); + while (1) { + a=get_output(); + reswitch: switch(a) { + case end_translation: return; + case identifier: case res_word: @<Output an identifier@>; break; + case section_code: @<Output a section name@>; break; + case math_rel: out_str("\\MRL{"@q}@>); +@.\\MRL@> + case noop: case inserted: break; + case cancel: case big_cancel: c=0; b=a; + while (1) { + a=get_output(); + if (a==inserted) continue; + if ((a<indent && !(b==big_cancel&&a==' ')) || a>big_force) break; + if (a==indent) c++; else if (a==outdent) c--; + else if (a==opt) a=get_output(); + } + @<Output saved |indent| or |outdent| tokens@>; + goto reswitch; + case indent: case outdent: case opt: case backup: case break_space: + case force: case big_force: case preproc_line: @<Output a control, + look ahead in case of line breaks, possibly |goto reswitch|@>; break; + case quoted_char: out(*(cur_tok++)); break; + default: out(a); /* otherwise |a| is an ordinary character */ + } + } +} + +@ An identifier of length one does not have to be enclosed in braces, and it +looks slightly better if set in a math-italic font instead of a (slightly +narrower) text-italic font. Thus we output `\.{\\\v}\.{a}' but +`\.{\\\\\{aa\}}'. + +@<Output an identifier@>= +out('\\'); +if (a==identifier) { + if (cur_name->ilk>=custom && cur_name->ilk<=quoted && !doing_format) { + for (p=cur_name->byte_start;p<(cur_name+1)->byte_start;p++) + out(isxalpha(*p)? 'x':*p); + break; + } else if (is_tiny(cur_name)) out('|') +@.\\|@> + else { delim='.'; + for (p=cur_name->byte_start;p<(cur_name+1)->byte_start;p++) + if (xislower(*p)) { /* not entirely uppercase */ + delim='\\'; break; + } + out(delim); + } +@.\\\\@> +@.\\.@> +} +else out('&') /* |a==res_word| */ +@.\\\&@> +if (is_tiny(cur_name)) { + if (isxalpha((cur_name->byte_start)[0])) + out('\\'); + out((cur_name->byte_start)[0]); +} +else out_name(cur_name); + +@ The current mode does not affect the behavior of \.{CWEAVE}'s output routine +except when we are outputting control tokens. + +@<Output a control...@>= +if (a<break_space || a==preproc_line) { + if (cur_mode==outer) { + out('\\'); out(a-cancel+'0'); +@.\\1@> +@.\\2@> +@.\\3@> +@.\\4@> +@.\\8@> + if (a==opt) { + b=get_output(); /* |opt| is followed by a digit */ + if (b!='0' || force_lines==0) out(b)@; + else out_str("{-1}"); /* |force_lines| encourages more \.{@@\v} breaks */ + } + } else if (a==opt) b=get_output(); /* ignore digit following |opt| */ + } +else @<Look ahead for strongest line break, |goto reswitch|@> + +@ If several of the tokens |break_space|, |force|, |big_force| occur in a +row, possibly mixed with blank spaces (which are ignored), +the largest one is used. A line break also occurs in the output file, +except at the very end of the translation. The very first line break +is suppressed (i.e., a line break that follows `\.{\\Y\\B}'). + +@<Look ahead for st...@>= { + b=a; save_mode=cur_mode; c=0; + while (1) { + a=get_output(); + if (a==inserted) continue; + if (a==cancel || a==big_cancel) { + @<Output saved |indent| or |outdent| tokens@>; + goto reswitch; /* |cancel| overrides everything */ + } + if ((a!=' ' && a<indent) || a==backup || a>big_force) { + if (save_mode==outer) { + if (out_ptr>out_buf+3 && strncmp(out_ptr-3,"\\Y\\B",4)==0) + goto reswitch; + @<Output saved |indent| or |outdent| tokens@>; + out('\\'); out(b-cancel+'0'); +@.\\5@> +@.\\6@> +@.\\7@> + if (a!=end_translation) finish_line(); + } + else if (a!=end_translation && cur_mode==inner) out(' '); + goto reswitch; + } + if (a==indent) c++; + else if (a==outdent) c--; + else if (a==opt) a=get_output(); + else if (a>b) b=a; /* if |a==' '| we have |a<b| */ + } +} + +@ @<Output saved...@>= + for (;c>0;c--) out_str("\\1"); +@.\\1@> + for (;c<0;c++) out_str("\\2"); +@.\\2@> + +@ The remaining part of |make_output| is somewhat more complicated. When we +output a section name, we may need to enter the parsing and translation +routines, since the name may contain \CEE/ code embedded in +\pb\ constructions. This \CEE/ code is placed at the end of the active +input buffer and the translation process uses the end of the active +|tok_mem| area. + +@<Output a section name@>= { + out_str("\\X"); +@.\\X@> + cur_xref=(xref_pointer)cur_name->xref; + if (cur_xref->num==file_flag) {an_output=1; cur_xref=cur_xref->xlink;} + else an_output=0; + if (cur_xref->num>=def_flag) { + out_section(cur_xref->num-def_flag); + if (phase==3) { + cur_xref=cur_xref->xlink; + while (cur_xref->num>=def_flag) { + out_str(", "); + out_section(cur_xref->num-def_flag); + cur_xref=cur_xref->xlink; + } + } + } + else out('0'); /* output the section number, or zero if it was undefined */ + out(':'); + if (an_output) out_str("\\.{"@q}@>); +@.\\.@> + @<Output the text of the section name@>; + if (an_output) out_str(@q{@>" }"); + out_str("\\X"); +} + +@ @<Output the text...@>= +sprint_section_name(scratch,cur_name); +k=scratch; +k_limit=scratch+strlen(scratch); +cur_section_name=cur_name; +while (k<k_limit) { + b=*(k++); + if (b=='@@') @<Skip next character, give error if not `\.{@@}'@>; + if (an_output) + switch (b) { + case ' ':case '\\':case '#':case '%':case '$':case '^': + case '{': case '}': case '~': case '&': case '_': + out('\\'); /* falls through */ +@.\\\ @> +@.\\\\@> +@.\\\#@> +@.\\\%@> +@.\\\$@> +@.\\\^@> +@.\\\{@>@q}@> +@q{@>@.\\\}@> +@.\\\~@> +@.\\\&@> +@.\\_@> @q CWEAVE does quote an underscore! @> + default: out(b); + } + else if (b!='|') out(b) + else { + @<Copy the \CEE/ text into the |buffer| array@>; + save_loc=loc; save_limit=limit; loc=limit+2; limit=j+1; + *limit='|'; output_C(); + loc=save_loc; limit=save_limit; + } +} + +@ @<Skip next char...@>= +if (*k++!='@@') { + printf("\n! Illegal control code in section name: <"); +@.Illegal control code...@> + print_section_name(cur_section_name); printf("> "); mark_error; +} + +@ The \CEE/ text enclosed in \pb\ should not contain `\.{\v}' characters, +except within strings. We put a `\.{\v}' at the front of the buffer, so that an +error message that displays the whole buffer will look a little bit sensible. +The variable |delim| is zero outside of strings, otherwise it +equals the delimiter that began the string being copied. + +@<Copy the \CEE/ text into...@>= +j=limit+1; *j='|'; delim=0; +while (1) { + if (k>=k_limit) { + printf("\n! C text in section name didn't end: <"); +@.C text...didn't end@> + print_section_name(cur_section_name); printf("> "); mark_error; break; + } + b=*(k++); + if (b=='@@' || (b=='\\' && delim!=0)) + @<Copy a quoted character into the buffer@> + else { + if (b=='\'' || b=='"') + if (delim==0) delim=b; + else if (delim==b) delim=0; + if (b!='|' || delim!=0) { + if (j>buffer+long_buf_size-3) overflow("buffer"); + *(++j)=b; + } + else break; + } +} + +@ @<Copy a quoted char...@>= { + if (j>buffer+long_buf_size-4) overflow("buffer"); + *(++j)=b; *(++j)=*(k++); +} + +@** Phase two processing. +We have assembled enough pieces of the puzzle in order to be ready to specify +the processing in \.{CWEAVE}'s main pass over the source file. Phase two +is analogous to phase one, except that more work is involved because we must +actually output the \TEX/ material instead of merely looking at the +\.{CWEB} specifications. + +@<Predecl...@>= +void phase_two(); + +@ @c +void +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) @<Translate the current section@>; +} + +@ 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 +printed. However, we don't want \.{\\Y} to occur between two definitions +within a single section. The variables |out_line| or |out_ptr| will +change if a section is non-null, so the following macros `|save_position|' +and `|emit_space_if_needed|' are able to handle the situation: + +@d save_position save_line=out_line; save_place=out_ptr +@d emit_space_if_needed if (save_line!=out_line || save_place!=out_ptr) + out_str("\\Y"); + space_checked=1 +@.\\Y@> + +@<Global...@>= +int save_line; /* former value of |out_line| */ +char *save_place; /* former value of |out_ptr| */ +int sec_depth; /* the integer, if any, following \.{@@*} */ +boolean space_checked; /* have we done |emit_space_if_needed|? */ +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? */ + +@ @<Translate the current section@>= { + section_count++; + @<Output the code for the beginning of a new section@>; + save_position; + @<Translate the \TEX/ part of the current section@>; + @<Translate the definition part of the current section@>; + @<Translate the \CEE/ part of the current section@>; + @<Show cross-references to this section@>; + @<Output the code for the end of a section@>; +} + +@ Sections beginning with the \.{CWEB} control sequence `\.{@@\ }' start in the +output with the \TEX/ control sequence `\.{\\M}', followed by the section +number. Similarly, `\.{@@*}' sections lead to the control sequence `\.{\\N}'. +In this case there's an additional parameter, representing one plus the +specified depth, immediately after the \.{\\N}. +If the section has changed, we put \.{\\*} just after the section number. + +@<Output the code for the beginning...@>= +if (*(loc-1)!='*') out_str("\\M"); +@.\\M@> +else { + while (*loc == ' ') loc++; + if (*loc=='*') { /* ``top'' level */ + sec_depth = -1; + loc++; + } + else { + for (sec_depth=0; xisdigit(*loc);loc++) + sec_depth = sec_depth*10 + (*loc) -'0'; + } + while (*loc == ' ') loc++; /* remove spaces before group title */ + group_found=1; + out_str("\\N"); +@.\\N@> + {@+ 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("}"); + +@ 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. + +@<Translate the \T...@>= do { + next_control=copy_TeX(); + 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...@> + case thin_space: case math_break: case ord: + case line_break: case big_line_break: case no_line_break: case join: + case pseudo_semi: case macro_arg_open: case macro_arg_close: + case output_defs_code: + err_print("! You can't do that in TeX text"); break; +@.You can't do that...@> + } +} while (next_control<format_code); + +@ When we get to the following code we have |next_control>=format_code|, and +the token memory is in its initial empty state. + +@<Translate the d...@>= +space_checked=0; +while (next_control<=definition) { /* |format_code| or |definition| */ + init_stack; + if (next_control==definition) @<Start a macro definition@>@; + else @<Start a format definition@>; + 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 +memories to their initial empty state. + +A |force| token is appended to the current scraps before translation +takes place, so that the translation will normally end with \.{\\6} or +\.{\\7} (the \TEX/ macros for |force| and |big_force|). This \.{\\6} or +\.{\\7} is replaced by the concluding \.{\\par} or by \.{\\Y\\par}. + +@<Predecl...@>= +void finish_C(); + +@ @c +void +finish_C(visible) /* finishes a definition or a \CEE/ part */ + boolean visible; /* nonzero if we should produce \TEX/ output */ +{ + text_pointer p; /* translation of the scraps */ + if (visible) { + out_str("\\B"); app_tok(force); app_scrap(insert,no_math); + p=translate(); +@.\\B@> + app(tok_flag+(int)(p-tok_start)); make_output(); /* output the list */ + if (out_ptr>out_buf+1) + if (*(out_ptr-1)=='\\') +@.\\6@> +@.\\7@> +@.\\Y@> + if (*out_ptr=='6') out_ptr-=2; + else if (*out_ptr=='7') *out_ptr='Y'; + out_str("\\par"); finish_line(); + } + if (text_ptr>max_text_ptr) max_text_ptr=text_ptr; + if (tok_ptr>max_tok_ptr) max_tok_ptr=tok_ptr; + if (scrap_ptr>max_scr_ptr) max_scr_ptr=scrap_ptr; + tok_ptr=tok_mem+1; text_ptr=tok_start+1; scrap_ptr=scrap_info; + /* forget the tokens and the scraps */ +} + +@ Keeping in line with the conventions of the \CEE/ preprocessor (and +otherwise contrary to the rules of \.{CWEB}) we distinguish here +between the case that `\.(' immediately follows an identifier and the +case that the two are separated by a space. In the latter case, and +if the identifier is not followed by `\.(' at all, the replacement +text starts immediately after the identifier. In the former case, +it starts after we scan the matching `\.)'. + +@<Start a macro...@>= { + 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 }' */ +@.\\D@> + if ((next_control=get_next())!=identifier) + err_print("! Improper macro definition"); +@.Improper macro definition@> + else { + app('$'); app_cur_id(0); + if (*loc=='(') + reswitch: switch (next_control=get_next()) { + case '(': case ',': app(next_control); goto reswitch; + case identifier: app_cur_id(0); goto reswitch; + case ')': app(next_control); next_control=get_next(); break; + default: err_print("! Improper macro definition"); break; + } + else next_control=get_next(); + app_str("$ "); app(break_space); + app_scrap(dead,no_math); /* scrap won't take part in the parsing */ + } +} + +@ @<Start a format...@>= { + doing_format=1; + 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 }' */ +@.\\F@> + next_control=get_next(); + if (next_control==identifier) { + app(id_flag+(int)(id_lookup(id_first, id_loc,normal)-name_dir)); + app(' '); + app(break_space); /* this is syntactically separate from what follows */ + next_control=get_next(); + if (next_control==identifier) { + app(id_flag+(int)(id_lookup(id_first, id_loc,normal)-name_dir)); + app_scrap(exp,maybe_math); app_scrap(semi,maybe_math); + next_control=get_next(); + } + } + if (scrap_ptr!=scrap_info+2) err_print("! Improper format definition"); +@.Improper format definition@> +} + +@ Finally, when the \TEX/ and definition parts have been treated, we have +|next_control>=begin_C|. We will make the global variable |this_section| +point to the current section name, if it has a name. + +@<Global...@>= +name_pointer this_section; /* the current section name, or zero */ + +@ @<Translate the \CEE/...@>= +this_section=name_dir; +if (next_control<=section_name) { + emit_space_if_needed; init_stack; + if (next_control==begin_C) next_control=get_next(); + else { + this_section=cur_section; + @<Check that '=' or '==' follows this section name, and + emit the scraps to start the section definition@>; + } + while (next_control<=section_name) { + outer_parse(); + @<Emit the scrap for a section name if present@>; + } + finish_C(1); +} + +@ The title of the section and an $\E$ or $\mathrel+\E$ are made +into a scrap that should not take part in the parsing. + +@<Check that '='...@>= +do next_control=get_next(); + while (next_control=='+'); /* allow optional `\.{+=}' */ +if (next_control!='=' && next_control!=eq_eq) + err_print("! You need an = sign after the section name"); +@.You need an = sign...@> + else next_control=get_next(); +if (out_ptr>out_buf+1 && *out_ptr=='Y' && *(out_ptr-1)=='\\') app(backup); + /* the section name will be flush left */ +@.\\Y@> +app(section_flag+(int)(this_section-name_dir)); +cur_xref=(xref_pointer)this_section->xref; +if(cur_xref->num==file_flag) cur_xref=cur_xref->xlink; +app_str("${}"); +if (cur_xref->num!=section_count+def_flag) { + app_str("\\mathrel+"); /*section name is multiply defined*/ + this_section=name_dir; /*so we won't give cross-reference info here*/ +} +app_str("\\E"); /* output an equivalence sign */ +@.\\E@> +app_str("{}$"); +app(force); app_scrap(dead,no_math); + /* this forces a line break unless `\.{@@+}' follows */ + +@ @<Emit the scrap...@>= +if (next_control<section_name) { + err_print("! You can't do that in C text"); +@.You can't do that...@> + next_control=get_next(); +} +else if (next_control==section_name) { + app(section_flag+(int)(cur_section-name_dir)); + app_scrap(section_scrap,maybe_math); + next_control=get_next(); +} + +@ Cross references relating to a named section are given +after the section ends. + +@<Show cross...@>= +if (this_section>name_dir) { + cur_xref=(xref_pointer)this_section->xref; + if (cur_xref->num==file_flag){an_output=1;cur_xref=cur_xref->xlink;} + else an_output=0; + if (cur_xref->num>def_flag) + cur_xref=cur_xref->xlink; /* bypass current section number */ + footnote(def_flag); footnote(cite_flag); footnote(0); +} + +@ The |footnote| procedure gives cross-reference information about +multiply defined section names (if the |flag| parameter is +|def_flag|), or about references to a section name +(if |flag==cite_flag|), or to its uses (if |flag==0|). It assumes that +|cur_xref| points to the first cross-reference entry of interest, and it +leaves |cur_xref| pointing to the first element not printed. Typical outputs: +`\.{\\A101.}'; `\.{\\Us 370\\ET1009.}'; +`\.{\\As 8, 27\\*\\ETs64.}'. + +Note that the output of \.{CWEAVE} is not English-specific; users may +supply new definitions for the macros \.{\\A}, \.{\\As}, etc. + +@<Predecl...@>= +void footnote(); + +@ @c +void +footnote(flag) /* outputs section cross-references */ +sixteen_bits flag; +{ + xref_pointer q; /* cross-reference pointer variable */ + if (cur_xref->num<=flag) return; + finish_line(); out('\\'); +@.\\A@> +@.\\Q@> +@.\\U@> + out(flag==0? 'U': flag==cite_flag? 'Q': 'A'); + @<Output all the section numbers on the reference list |cur_xref|@>; + out('.'); +} + +@ The following code distinguishes three cases, according as the number +of cross-references is one, two, or more than two. Variable |q| points +to the first cross-reference, and the last link is a zero. + +@<Output all the section numbers...@>= +q=cur_xref; if (q->xlink->num>flag) out('s'); /* plural */ +while (1) { + out_section(cur_xref->num-flag); + cur_xref=cur_xref->xlink; /* point to the next cross-reference to output */ + if (cur_xref->num<=flag) break; + if (cur_xref->xlink->num>flag) out_str(", "); /* not the last */ + else {out_str("\\ET"); /* the last */ +@.\\ET@> + if (cur_xref != q->xlink) out('s'); /* the last of more than two */ + } +} + +@ @<Output the code for the end of a section@>= +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. + +@<Predecl...@>= +void phase_three(); + +@ @c +void +phase_three() { +if (no_xref) { + finish_line(); + out_str("\\end"); +@.\\end@> + finish_line(); +} +else { + phase=3; if (show_progress) printf("\nWriting the index..."); +@.Writing the index...@> + finish_line(); + if ((idx_file=fopen(idx_file_name,"w"))==NULL) + fatal("! Cannot open index file ",idx_file_name); +@.Cannot open index file@> + if (change_exists) { + @<Tell about changed sections@>; finish_line(); finish_line(); + } + out_str("\\inx"); finish_line(); +@.\\inx@> + active_file=idx_file; /* change active file to the index file */ + @<Do the first pass of sorting@>; + @<Sort and output the index@>; + finish_line(); fclose(active_file); /* finished with |idx_file| */ + active_file=tex_file; /* switch back to |tex_file| for a tic */ + out_str("\\fin"); finish_line(); +@.\\fin@> + if ((scn_file=fopen(scn_file_name,"w"))==NULL) + fatal("! Cannot open section file ",scn_file_name); +@.Cannot open section file@> + active_file=scn_file; /* change active file to section listing file */ + @<Output all the section names@>; + finish_line(); fclose(active_file); /* finished with |scn_file| */ + active_file=tex_file; + if (group_found) out_str("\\con");@+else out_str("\\end"); +@.\\con@> +@.\\end@> + finish_line(); + fclose(active_file); +} +if (show_happiness) printf("\nDone."); +check_complete(); /* was all of the change file used? */ +} + +@ Just before the index comes a list of all the changed sections, including +the index section itself. + +@<Global...@>= +sixteen_bits k_section; /* runs through the sections */ + +@ @<Tell about changed 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<section_count) { + while (!changed_section[++k_section]); + out_str(", "); out_section(k_section); + } + out('.'); +} + +@ 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 +identifiers into 102 different lists based on their first characters. +(Uppercase letters are put into the same list as the corresponding lowercase +letters, since we want to have `$t<\\{TeX}<\&{to}$'.) The +list for character |c| begins at location |bucket[c]| and continues through +the |blink| array. + +@<Global...@>= +name_pointer bucket[256]; +name_pointer next_name; /* successor of |cur_name| when sorting */ +name_pointer blink[max_names]; /* links in the buckets */ + +@ To begin the sorting, we go through all the hash lists and put each entry +having a nonempty cross-reference list into the proper bucket. + +@<Do the first pass...@>= { +int c; +for (c=0; c<=255; c++) bucket[c]=NULL; +for (h=hash; h<=hash_end; h++) { + next_name=*h; + while (next_name) { + cur_name=next_name; next_name=cur_name->link; + if (cur_name->xref!=(char*)xmem) { + c=(eight_bits)((cur_name->byte_start)[0]); + if (xisupper(c)) c=tolower(c); + blink[cur_name-name_dir]=bucket[c]; bucket[c]=cur_name; + } + } +} +} + +@ During the sorting phase we shall use the |cat| and |trans| arrays from +\.{CWEAVE}'s parsing algorithm and rename them |depth| and |head|. They now +represent a stack of identifier lists for all the index entries that have +not yet been output. The variable |sort_ptr| tells how many such lists are +present; the lists are output in reverse order (first |sort_ptr|, then +|sort_ptr-1|, etc.). The |j|th list starts at |head[j]|, and if the first +|k| characters of all entries on this list are known to be equal we have +|depth[j]==k|. + +@ @<Rest of |trans_plus| union@>= +name_pointer Head; + +@ @d depth cat /* reclaims memory that is no longer needed for parsing */ +@d head trans_plus.Head /* ditto */ +@f sort_pointer int +@d sort_pointer scrap_pointer /* ditto */ +@d sort_ptr scrap_ptr /* ditto */ +@d max_sorts max_scraps /* ditto */ + +@<Global...@>= +eight_bits cur_depth; /* depth of current buckets */ +char *cur_byte; /* index into |byte_mem| */ +sixteen_bits cur_val; /* current cross-reference number */ +sort_pointer max_sort_ptr; /* largest value of |sort_ptr| */ + +@ @<Set init...@>= +max_sort_ptr=scrap_info; + +@ The desired alphabetic order is specified by the |collate| array; namely, +$|collate|[0]<|collate|[1]<\cdots<|collate|[100]$. + +@<Global...@>= +eight_bits collate[102+128]; /* collation order */ +@^high-bit character handling@> + +@ We use the order $\hbox{null}<\.\ <\hbox{other characters}<{}$\.\_${}< +\.A=\.a<\cdots<\.Z=\.z<\.0<\cdots<\.9.$ Warning: The collation mapping +needs to be changed if ASCII code is not being used. +@^ASCII code dependencies@> +@^high-bit character handling@> + +We initialize |collate| by copying a few characters at a time, because +some \CEE/ compilers choke on long strings. + +@<Set init...@>= +collate[0]=0; +strcpy(collate+1," \1\2\3\4\5\6\7\10\11\12\13\14\15\16\17"); +/* 16 characters + 1 = 17 */ +strcpy(collate+17,"\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37"); +/* 16 characters + 17 = 33 */ +strcpy(collate+33,"!\42#$%&'()*+,-./:;<=>?@@[\\]^`{|}~_"); +/* 32 characters + 33 = 65 */ +strcpy(collate+65,"abcdefghijklmnopqrstuvwxyz0123456789"); +/* (26 + 10) characters + 65 = 101 */ +strcpy(collate+101,"\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217"); +/* 16 characters + 101 = 117 */ +strcpy(collate+117,"\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237"); +/* 16 characters + 117 = 133 */ +strcpy(collate+133,"\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257"); +/* 16 characters + 133 = 149 */ +strcpy(collate+149,"\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277"); +/* 16 characters + 149 = 165 */ +strcpy(collate+165,"\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317"); +/* 16 characters + 165 = 181 */ +strcpy(collate+181,"\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337"); +/* 16 characters + 181 = 197 */ +strcpy(collate+197,"\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357"); +/* 16 characters + 197 = 213 */ +strcpy(collate+213,"\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377"); +/* 16 characters + 213 = 229 */ + +@ Procedure |unbucket| goes through the buckets and adds nonempty lists +to the stack, using the collating sequence specified in the |collate| array. +The parameter to |unbucket| tells the current depth in the buckets. +Any two sequences that agree in their first 255 character positions are +regarded as identical. + +@d infinity 255 /* $\infty$ (approximately) */ + +@<Predecl...@>= +void unbucket(); + +@ @c +void +unbucket(d) /* empties buckets having depth |d| */ +eight_bits d; +{ + int c; /* index into |bucket|; cannot be a simple |char| because of sign + comparison below*/ + for (c=100+128; c>= 0; c--) if (bucket[collate[c]]) { +@^high-bit character handling@> + if (sort_ptr>=scrap_info_end) overflow("sorting"); + sort_ptr++; + if (sort_ptr>max_sort_ptr) max_sort_ptr=sort_ptr; + if (c==0) sort_ptr->depth=infinity; + else sort_ptr->depth=d; + sort_ptr->head=bucket[collate[c]]; bucket[collate[c]]=NULL; + } +} + +@ @<Sort and output...@>= +sort_ptr=scrap_info; unbucket(1); +while (sort_ptr>scrap_info) { + cur_depth=sort_ptr->depth; + if (blink[sort_ptr->head-name_dir]==0 || cur_depth==infinity) + @<Output index entries for the list at |sort_ptr|@>@; + else @<Split the list at |sort_ptr| into further lists@>; +} + +@ @<Split the list...@>= { + eight_bits c; + next_name=sort_ptr->head; + do { + cur_name=next_name; next_name=blink[cur_name-name_dir]; + cur_byte=cur_name->byte_start+cur_depth; + if (cur_byte==(cur_name+1)->byte_start) c=0; /* hit end of the name */ + else { + c=(eight_bits) *cur_byte; + if (xisupper(c)) c=tolower(c); + } + blink[cur_name-name_dir]=bucket[c]; bucket[c]=cur_name; + } while (next_name); + --sort_ptr; unbucket(cur_depth+1); +} + +@ @<Output index...@>= { + cur_name=sort_ptr->head; + do { + out_str("\\I"); +@.\\I@> + @<Output the name at |cur_name|@>; + @<Output the cross-references at |cur_name|@>; + cur_name=blink[cur_name-name_dir]; + } while (cur_name); + --sort_ptr; +} + +@ @<Output the name...@>= +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; + out_str("\\."); break; +lowcase: out_str("\\\\"); + } + break; +@.\\|@> +@.\\.@> +@.\\\\@> + case roman: break; + case wildcard: out_str("\\9"); break; +@.\\9@> + case typewriter: out_str("\\."); break; +@.\\.@> + case custom: case quoted: {char *j; out_str("$\\"); + for (j=cur_name->byte_start;j<(cur_name+1)->byte_start;j++) + out(isxalpha(*j)? 'x' : *j); + out('$'); + goto name_done; + } + default: out_str("\\&"); +@.\\\&@> +} +out_name(cur_name); +name_done: + +@ Section numbers that are to be underlined are enclosed in +`\.{\\[}$\,\ldots\,$\.]'. + +@<Output the cross-references...@>= +@<Invert the cross-reference list at |cur_name|, making |cur_xref| the head@>; +do { + out_str(", "); cur_val=cur_xref->num; + if (cur_val<def_flag) out_section(cur_val); + else {out_str("\\["); out_section(cur_val-def_flag); out(']');} +@.\\[@> + cur_xref=cur_xref->xlink; +} while (cur_xref!=xmem); +out('.'); finish_line(); + +@ List inversion is best thought of as popping elements off one stack and +pushing them onto another. In this case |cur_xref| will be the head of +the stack that we push things onto. +@<Global...@>= +xref_pointer next_xref, this_xref; + /* pointer variables for rearranging a list */ + +@ @<Invert the cross-reference list at |cur_name|, making |cur_xref| the head@>= +this_xref=(xref_pointer)cur_name->xref; cur_xref=xmem; +do { + next_xref=this_xref->xlink; this_xref->xlink=cur_xref; + cur_xref=this_xref; this_xref=next_xref; +} while (this_xref!=xmem); + +@ The following recursive procedure walks through the tree of section names and +prints them. +@^recursion@> + +@<Predecl...@>= +void section_print(); + +@ @c +void +section_print(p) /* print all section names in subtree |p| */ +name_pointer p; +{ + if (p) { + section_print(p->llink); out_str("\\I"); +@.\\I@> + tok_ptr=tok_mem+1; text_ptr=tok_start+1; scrap_ptr=scrap_info; init_stack; + app(p-name_dir+section_flag); make_output(); + footnote(cite_flag); + footnote(0); /* |cur_xref| was set by |make_output| */ + finish_line();@/ + section_print(p->rlink); + } +} + +@ @<Output all the section names@>=section_print(root) + +@ Because on some systems the difference between two pointers is a |long| +rather than an |int|, we use \.{\%ld} to print these quantities. + +@c +void +print_stats() { + printf("\nMemory usage statistics:\n"); +@.Memory usage statistics:@> + printf("%ld names (out of %ld)\n", + (long)(name_ptr-name_dir),(long)max_names); + printf("%ld cross-references (out of %ld)\n", + (long)(xref_ptr-xmem),(long)max_refs); + printf("%ld bytes (out of %ld)\n", + (long)(byte_ptr-byte_mem),(long)max_bytes); + printf("Parsing:\n"); + printf("%ld scraps (out of %ld)\n", + (long)(max_scr_ptr-scrap_info),(long)max_scraps); + printf("%ld texts (out of %ld)\n", + (long)(max_text_ptr-tok_start),(long)max_texts); + printf("%ld tokens (out of %ld)\n", + (long)(max_tok_ptr-tok_mem),(long)max_toks); + printf("%ld levels (out of %ld)\n", + (long)(max_stack_ptr-stack),(long)stack_size); + printf("Sorting:\n"); + printf("%ld levels (out of %ld)\n", + (long)(max_sort_ptr-scrap_info),(long)max_scraps); +} + +@** Index. +If you have read and understood the code for Phase III above, you know what +is in this index and how it got here. All sections in which an identifier is +used are listed with that identifier, except that reserved words are +indexed only when they appear in format definitions, and the appearances +of identifiers in section names are not indexed. Underlined entries +correspond to where the identifier was declared. Error messages, control +sequences put into the output, and a few +other things like ``recursion'' are indexed here too. diff --git a/web/c_cpp/c2cweb/cweb/cwebmac.ori b/web/c_cpp/c2cweb/cweb/cwebmac.ori new file mode 100644 index 0000000000..331a2eff46 --- /dev/null +++ b/web/c_cpp/c2cweb/cweb/cwebmac.ori @@ -0,0 +1,346 @@ +% This file is part of c2cweb package Version 1.5 + +% standard macros for CWEB listings (in addition to plain.tex) +% Version 3.0 --- June 1993 + +% modified by Werner Lemberg (a7971428@unet.univie.ac.at) 10-Nov-1996 + +\ifx\documentstyle\undefined\else\endinput\fi % LaTeX will use other macros +\xdef\fmtversion{\fmtversion+CWEB3.0} + +\let\:=\. % preserve a way to get the dot accent + % (all other accents will still work as usual) + +\parskip 0pt % no stretch between paragraphs +\parindent 1em % for paragraphs and for the first line of C text + +\font\ninerm=cmr9 +\let\mc=\ninerm % medium caps +\def\CEE/{{\mc C\spacefactor1000}} +\def\UNIX/{{\mc U\kern-.05emNIX\spacefactor1000}} +\def\TEX/{\TeX} +\def\CPLUSPLUS/{{\mc C\PP\spacefactor1000}} +\def\Cee{\CEE/} % for backward compatibility +\def\9#1{} +\font\eightrm=cmr8 +\let\sc=\eightrm % small caps (NOT a caps-and-small-caps font) +\let\mainfont=\tenrm +\let\cmntfont\tenrm +%\font\tenss=cmss10 \let\cmntfont\tenss % alternative comment font +\font\titlefont=cmr7 scaled\magstep4 % title on the contents page +\font\ttitlefont=cmtt10 scaled\magstep2 % typewriter type in title +\font\tentex=cmtex10 % TeX extended character set (used in strings) +\fontdimen7\tentex=0pt % no double space after sentences + +\def\${\ifmmode \hbox{\sl \char`\$} + \else \hbox{\ifdim \fontdimen1\font > 0pt \sl \fi \char`\$}\fi} + +\def\\#1{\leavevmode\hbox{\it#1\/\kern.05em}} % italic type for identifiers +\def\|#1{\leavevmode\hbox{$#1$}} % one-letter identifiers look better this way +\def\{\leavevmode\hbox{\bf + \def\_{\kern.04em\vbox{\hrule width.3em height .6pt}\kern.08em}% + #1\/\kern.05em}} % boldface type for reserved words +\def\.#1{\leavevmode\hbox{\tentex % typewriter type for strings + \let\\=\BS % backslash in a string + \let\{=\LB % left brace in a string + \let\}=\RB % right brace in a string + \let\~=\TL % tilde in a string + \let\ =\SP % space in a string + \let\_=\UL % underline in a string + \let\&=\AM % ampersand in a string + \let\^=\CF % circumflex in a string + #1\kern.05em}} +\def\){\discretionary{\hbox{\tentex\BS}}{}{}} +\def\AT{@} % at sign for control text (not needed in versions >= 2.9) +\def\ATL{\par\noindent\bgroup\catcode`\_=12 \postATL} % print @l in limbo +\def\postATL#1 #2 {\bf letter \\{\uppercase{\char"#1}} + tangles as \tentex "#2"\egroup\par} +\def\noATL#1 #2 {} +\def\noatl{\let\ATL=\noATL} % suppress output from @l +\def\ATH{\X\kern-.5em:Preprocessor definitions\X} +\let\PB=\relax % hook for program brackets |...| in TeX part or section name + +\chardef\AM=`\& % ampersand character in a string +\chardef\BS=`\\ % backslash in a string +\chardef\LB=`\{ % left brace in a string +\chardef\RB=`\} % right brace in a string +\def\SP{{\tt\char`\ }} % (visible) space in a string +\chardef\TL=`\~ % tilde in a string +\chardef\UL=`\_ % underline character in a string +\chardef\CF=`\^ % circumflex character in a string + +\newbox\PPbox % symbol for ++ +\setbox\PPbox=\hbox{\kern.5pt\raise1pt\hbox{\sevenrm+\kern-1pt+}\kern.5pt} +\def\PP{\copy\PPbox} +\newbox\MMbox \setbox\MMbox=\hbox{\kern.5pt\raise1pt\hbox{\sevensy\char0 + \kern-1pt\char0}\kern.5pt} +\def\MM{\copy\MMbox} +\newbox\MGbox % symbol for -> +\setbox\MGbox=\hbox{\kern-2pt\lower3pt\hbox{\teni\char'176}\kern1pt} +\def\MG{\copy\MGbox} +\newbox\NNbox % symbol for ## +\setbox\NNbox=\hbox{\kern.5pt\raise2pt\hbox{\#}\kern-4pt + \lower2pt\hbox{\#}\kern.5pt} +\def\NN{\copy\NNbox} +\def\MRL#1{\mathrel{\let\K==#1}} +%\def\MRL#1{\KK#1}\def\KK#1#2{\buildrel\;#1\over{#2}} +\let\GG=\gg +\let\LL=\ll +\let\NULL=\Lambda +\mathchardef\AND="2026 % bitwise and; also \& (unary operator) +\let\OR=\mid % bitwise or +\let\XOR=\oplus % bitwise exclusive or +\def\CM{{\sim}} % bitwise complement +\newbox\MODbox \setbox\MODbox=\hbox{\eightrm\%} +\def\MOD{\mathbin{\copy\MODbox}} +\def\DC{\kern.1em{::}\kern.1em} % symbol for :: +\def\PA{\mathbin{.*}} % symbol for .* +\def\MGA{\mathbin{\MG*}} % symbol for ->* +\def\this{\&{this}} + +\newbox\bak \setbox\bak=\hbox to -1em{} % backspace one em +\newbox\bakk\setbox\bakk=\hbox to -2em{} % backspace two ems + +\newcount\ind % current indentation in ems +\def\1{\global\advance\ind by1\hangindent\ind em} % indent one more notch +\def\2{\global\advance\ind by-1} % indent one less notch +\def\3#1{\hfil\penalty#10\hfilneg} % optional break within a statement +\def\4{\copy\bak} % backspace one notch +\def\5{\hfil\penalty-1\hfilneg\kern2.5em\copy\bakk\ignorespaces}% optional break +\def\6{\ifmmode\else\par % forced break + \hangindent\ind em\noindent\kern\ind em\copy\bakk\ignorespaces\fi} +\def\7{\Y\6} % forced break and a little extra space +\def\8{\hskip-\ind em\hskip 2em} % no indentation + +\newcount\gdepth % depth of current major group, plus one +\newcount\secpagedepth +\secpagedepth=3 % page breaks will occur for depths -1, 0, and 1 +\newtoks\gtitle % title of current major group +\newskip\intersecskip \intersecskip=12pt minus 3pt % space between sections +\let\yskip=\smallskip +\def\?{\mathrel?} +\def\note#1#2.{\Y\noindent{\hangindent2em\baselineskip10pt\eightrm#1~#2.\par}} +\def\lapstar{\rlap{*}} +\def\stsec{\rightskip=0pt % get out of C mode (cf. \B) + \sfcode`;=1500 \pretolerance 200 \hyphenpenalty 50 \exhyphenpenalty 50 + \noindent{\let\*=\lapstar\bf\secstar.\quad}} +\let\startsection=\stsec +\def\defin#1{\global\advance\ind by 2 \1\&{#1 } } % begin `define' or `format' +\def\A{\note{See also section}} % xref for doubly defined section name +\def\As{\note{See also sections}} % xref for multiply defined section name +\def\B{\rightskip=0pt plus 100pt minus 10pt % go into C mode + \sfcode`;=3000 + \pretolerance 10000 + \hyphenpenalty 9999 % so strings can be broken (discretionary \ is inserted) + \exhyphenpenalty 10000 + \global\ind=2 \1\ \unskip} +\def\C#1{\5\5\quad$/\ast\,${\cmntfont #1}$\,\ast/$} +\let\SHC\C % "// short comments" treated like "/* ordinary comments */" +%\def\C#1{\5\5\quad$\triangleright\,${\cmntfont#1}$\,\triangleleft$} +%\def\SHC#1{\5\5\quad$\diamond\,${\cmntfont#1}} +\def\D{\defin{\#define}} % macro definition +\let\E=\equiv % equivalence sign +\def\ET{ and~} % conjunction between two section numbers +\def\ETs{, and~} % conjunction between the last two of several section numbers +\def\F{\defin{format}} % format definition +\let\G=\ge % greater than or equal sign +% \H is long Hungarian umlaut accent +\let\I=\ne % unequal sign +\def\J{\.{@\&}} % TANGLE's join operation +\let\K== % assignment operator +%\let\K=\leftarrow % "honest" alternative to standard assignment operator +% \L is Polish letter suppressed-L +\outer\def\M#1{\MN{#1}\ifon\vfil\penalty-100\vfilneg % beginning of section + \vskip\intersecskip\startsection\ignorespaces} +\outer\def\N#1#2#3.{\gdepth=#1\gtitle={#3}\MN{#2}% beginning of starred section + \ifon\ifnum#1<\secpagedepth \vfil\eject % force page break if depth is small + \else\vfil\penalty-100\vfilneg\vskip\intersecskip\fi\fi + \message{*\secno} % progress report + \edef\next{\write\cont{\ZZ{#3}{#1}{\secno}% write to contents file + {\noexpand\the\pageno}}}\next % \ZZ{title}{depth}{sec}{page} + \ifon\startsection{\bf#3.\quad}\ignorespaces} +\def\MN#1{\par % common code for \M, \N + {\xdef\secstar{#1}\let\*=\empty\xdef\secno{#1}}% remove \* from section name + \ifx\secno\secstar \onmaybe \else\ontrue \fi + \mark{{{\tensy x}\secno}{\the\gdepth}{\the\gtitle}}} +% each \mark is {section reference or null}{depth plus 1}{group title} +% \O is Scandinavian letter O-with-slash +% \P is paragraph sign +\def\Q{\note{This code is cited in section}} % xref for mention of a section +\def\Qs{\note{This code is cited in sections}} % xref for mentions of a section +\let\R=\lnot % logical not +% \S is section sign +\def\T#1{\leavevmode % octal, hex or decimal constant + \hbox{$\def\?{\kern.2em}% + \def\$##1{\egroup_{\,\rm##1}\bgroup}% suffix to constant + \def\_{\cdot 10^{\aftergroup}}% power of ten (via dirty trick) + \let\~=\oct \let\^=\hex {#1}$}} +\def\U{\note{This code is used in section}} % xref for use of a section +\def\Us{\note{This code is used in sections}} % xref for uses of a section +\let\V=\lor % logical or +\let\W=\land % logical and +\def\X#1:#2\X{\ifmmode\gdef\XX{\null$\null}\else\gdef\XX{}\fi % section name + \XX$\langle\,${#2\eightrm\kern.5em#1}$\,\rangle$\XX} +\def\Y{\par\yskip} +\let\Z=\le +\let\ZZ=\let % now you can \write the control sequence \ZZ +\let\*=* + +%\def\oct{\hbox{\rm\char'23\kern-.2em\it\aftergroup\?\aftergroup}} % WEB style +%\def\hex{\hbox{\rm\char"7D\tt\aftergroup}} % WEB style +\def\oct{\hbox{$^\circ$\kern-.1em\it\aftergroup\?\aftergroup}}% CWEB style +\def\hex{\hbox{$^{\scriptscriptstyle\#}$\tt\aftergroup}} % CWEB style +\def\vb#1{\leavevmode\hbox{\kern2pt\vrule\vtop{\vbox{\hrule + \hbox{\strut\kern2pt\.{#1}\kern2pt}} + \hrule}\vrule\kern2pt}} % verbatim string + +\def\onmaybe{\let\ifon=\maybe} \let\maybe=\iftrue +\newif\ifon \newif\iftitle \newif\ifpagesaved + +\def\lheader{\mainfont\the\pageno\eightrm\qquad\grouptitle\hfill\title\qquad + \mainfont\topsecno} % top line on left-hand pages +\def\rheader{\mainfont\topsecno\eightrm\qquad\title\hfill\grouptitle + \qquad\mainfont\the\pageno} % top line on right-hand pages +\def\grouptitle{\let\i=I\let\j=J\uppercase\expandafter{\expandafter + \takethree\topmark}} +\def\topsecno{\expandafter\takeone\topmark} +\def\takeone#1#2#3{#1} +\def\taketwo#1#2#3{#2} +\def\takethree#1#2#3{#3} +\def\nullsec{\eightrm\kern-2em} % the \kern-2em cancels \qquad in headers + +\let\page=\pagebody \raggedbottom +% \def\page{\box255 }\normalbottom % faster, but loses plain TeX footnotes +\def\normaloutput#1#2#3{\ifodd\pageno\hoffset=\pageshift\fi + \shipout\vbox{ + \vbox to\fullpageheight{ + \iftitle\global\titlefalse + \else\hbox to\pagewidth{\vbox to10pt{}\ifodd\pageno #3\else#2\fi}\fi + \vfill#1}} % parameter #1 is the page itself + \global\advance\pageno by1} + +\gtitle={\.{CWEB} output} % this running head is reset by starred sections +\mark{\noexpand\nullsec0{\the\gtitle}} +\def\title{\expandafter\uppercase\expandafter{\jobname}} +\def\topofcontents{\centerline{\titlefont\title}\vskip.7in + \vfill} % this material will start the table of contents page +\def\botofcontents{\vfill + \centerline{\covernote}} % this material will end the table of contents page +\def\covernote{} +\def\contentspagenumber{0} % default page number for table of contents +\newdimen\pagewidth \pagewidth=6.5in % the width of each page +\newdimen\pageheight \pageheight=8.7in % the height of each page +\newdimen\fullpageheight \fullpageheight=9in % page height including headlines +\newdimen\pageshift \pageshift=0in % shift righthand pages wrt lefthand ones +\def\magnify#1{\mag=#1\pagewidth=6.5truein\pageheight=8.7truein + \fullpageheight=9truein\setpage} +\def\setpage{\hsize\pagewidth\vsize\pageheight} % use after changing page size +\def\contentsfile{\jobname.toc} % file that gets table of contents info +\def\readcontents{\input \contentsfile} +\def\readindex{\input \jobname.idx} +\def\readsections{\input \jobname.scn} + +\newwrite\cont +\output{\setbox0=\page % the first page is garbage + \openout\cont=\contentsfile + \write\cont{\catcode `\noexpand\@=11\relax} % \makeatletter + \global\output{\normaloutput\page\lheader\rheader}} +\setpage +\vbox to \vsize{} % the first \topmark won't be null + +\def\ch{\note{The following sections were changed by the change file:} + \let\*=\relax} +\newbox\sbox % saved box preceding the index +\newbox\lbox % lefthand column in the index +\def\inx{\par\vskip6pt plus 1fil % we are beginning the index + \def\page{\box255 } \normalbottom + \write\cont{} % ensure that the contents file isn't empty + \write\cont{\catcode `\noexpand\@=12\relax} % \makeatother + \closeout\cont % the contents information has been fully gathered + \output{\ifpagesaved\normaloutput{\box\sbox}\lheader\rheader\fi + \global\setbox\sbox=\page \global\pagesavedtrue} + \pagesavedfalse \eject % eject the page-so-far and predecessors + \setbox\sbox\vbox{\unvbox\sbox} % take it out of its box + \vsize=\pageheight \advance\vsize by -\ht\sbox % the remaining height + \hsize=.5\pagewidth \advance\hsize by -10pt + % column width for the index (20pt between cols) + \parfillskip 0pt plus .6\hsize % try to avoid almost empty lines + \def\lr{L} % this tells whether the left or right column is next + \output{\if L\lr\global\setbox\lbox=\page \gdef\lr{R} + \else\normaloutput{\vbox to\pageheight{\box\sbox\vss + \hbox to\pagewidth{\box\lbox\hfil\page}}}\lheader\rheader + \global\vsize\pageheight\gdef\lr{L}\global\pagesavedfalse\fi} + \message{Index:} + \parskip 0pt plus .5pt + \outer\def\I##1, {\par\hangindent2em\noindent##1:\kern1em} % index entry + \def\[##1]{$\underline{##1}$} % underlined index item + \rm \rightskip0pt plus 2.5em \tolerance 10000 \let\*=\lapstar + \hyphenpenalty 10000 \parindent0pt + \readindex} +\def\fin{\par\vfill\eject % this is done when we are ending the index + \ifpagesaved\null\vfill\eject\fi % output a null index column + \if L\lr\else\null\vfill\eject\fi % finish the current page + \parfillskip 0pt plus 1fil + \def\grouptitle{NAMES OF THE SECTIONS} + \let\topsecno=\nullsec + \message{Section names:} + \output={\normaloutput\page\lheader\rheader} + \setpage + \def\note##1##2.{\quad{\eightrm##1~##2.}} + \def\Q{\note{Cited in section}} % crossref for mention of a section + \def\Qs{\note{Cited in sections}} % crossref for mentions of a section + \def\U{\note{Used in section}} % crossref for use of a section + \def\Us{\note{Used in sections}} % crossref for uses of a section + \def\I{\par\hangindent 2em}\let\*=* + \readsections} +\def\con{\par\vfill\eject % finish the section names +% \ifodd\pageno\else\titletrue\null\vfill\eject\fi % for duplex printers + \rightskip 0pt \hyphenpenalty 50 \tolerance 200 + \setpage \output={\normaloutput\page\lheader\rheader} + \titletrue % prepare to output the table of contents + \pageno=\contentspagenumber + \def\grouptitle{TABLE OF CONTENTS} + \message{Table of contents:} + \topofcontents + \line{\hfil Section\hbox to3em{\hss Page}} + \let\ZZ=\contentsline + \readcontents\relax % read the contents info + \botofcontents \end} % print the contents page(s) and terminate +\def\contentsline#1#2#3#4{\ifnum#2=0 \smallbreak\fi + \line{\consetup{#2}#1 + \rm\leaders\hbox to .5em{.\hfil}\hfil\ #3\hbox to3em{\hss#4}}} +\def\consetup#1{\ifcase#1 \bf % depth -1 (@**) + \or % depth 0 (@*) + \or \hskip2em % depth 1 (@*1) + \or \hskip4em % depth 2 (@*2) + \or \hskip6em % depth 3 (@*3) + \or \hskip8em % depth 4 (@*4) + \or \hskip10em % depth 5 (@*5) + \else \hskip12em \fi} % depth 6 or more +\def\noinx{\let\inx=\end} % no indexes or table of contents +\def\nosecs{\let\FIN=\fin \def\fin{\let\parfillskip=\end \FIN}} + % no index of section names or table of contents +\def\nocon{\let\con=\end} % no table of contents +\def\today{\ifcase\month\or + January\or February\or March\or April\or May\or June\or + July\or August\or September\or October\or November\or December\fi + \space\number\day, \number\year} +\newcount\twodigits +\def\hours{\twodigits=\time \divide\twodigits by 60 \printtwodigits + \multiply\twodigits by-60 \advance\twodigits by\time :\printtwodigits} +\def\gobbleone1{} +\def\printtwodigits{\advance\twodigits100 + \expandafter\gobbleone\number\twodigits + \advance\twodigits-100 } +\def\TeX{{\ifmmode\it\fi + \leavevmode\hbox{T\kern-.1667em\lower.424ex\hbox{E}\hskip-.125em X}}} +\def\,{\relax\ifmmode\mskip\thinmuskip\else\thinspace\fi} +\def\datethis{\def\startsection{\leftline{\sc\today\ at \hours}\bigskip + \let\startsection=\stsec\stsec}} + % say `\datethis' in limbo, to get your listing timestamped before section 1 +\def\datecontentspage{% + \def\topofcontents{\leftline{\sc\today\ at \hours}\bigskip + \centerline{\titlefont\title}\vfill}} % timestamps the contents page + + +% end of cwebmac.tex diff --git a/web/c_cpp/c2cweb/cweb/cwebmac.tex b/web/c_cpp/c2cweb/cweb/cwebmac.tex new file mode 100644 index 0000000000..8682fb7372 --- /dev/null +++ b/web/c_cpp/c2cweb/cweb/cwebmac.tex @@ -0,0 +1,375 @@ +% This file is part of c2cweb package Version 1.5 + +% standard macros for CWEB listings (in addition to plain.tex) +% Version 3.0 --- June 1993 + +% modified by Werner Lemberg (a7971428@unet.univie.ac.at) 10-Nov-1996 + +\ifx\documentstyle\undefined\else\endinput\fi % LaTeX will use other macros +\xdef\fmtversion{\fmtversion+CWEB3.2} + +\let\:=\. % preserve a way to get the dot accent + % (all other accents will still work as usual) + +%\parskip 0pt % no stretch between paragraphs +%\parindent 1em % for paragraphs and for the first line of C text + +\font\ninerm=cmr9 +\let\mc=\ninerm % medium caps +\def\CEE/{{\mc C\spacefactor1000}} +\def\UNIX/{{\mc U\kern-.05emNIX\spacefactor1000}} +\def\TEX/{\TeX} +\def\CPLUSPLUS/{{\mc C\PP\spacefactor1000}} +\def\Cee{\CEE/} % for backward compatibility +\def\9#1{} +\font\eightrm=cmr8 +\let\sc=\eightrm % small caps (NOT a caps-and-small-caps font) +\let\mainfont=\tenrm +\let\cmntfont\tenrm +%\font\tenss=cmss10 \let\cmntfont\tenss % alternative comment font +%\font\titlefont=cmr7 scaled\magstep4 % title on the contents page +\font\titlefont=cmssbx10 scaled\magstep2 % title on the contents page +\font\ttitlefont=cmtt10 scaled\magstep2 % typewriter type in title +\font\tentex=cmtex10 % TeX extended character set (used in strings) +\fontdimen7\tentex=0pt % no double space after sentences + +\font\sectionfont=cmssbx10 scaled\magstephalf + +\def\${\ifmmode \hbox{\sl \char`\$} + \else \hbox{\ifdim \fontdimen1\font > 0pt \sl \fi \char`\$}\fi} + +\def\\#1{\leavevmode\hbox{\it#1\/\kern.05em}} % italic type for identifiers +\def\|#1{\leavevmode\hbox{$#1$}} % one-letter identifiers look better this way +\def\{\leavevmode\hbox{\bf + \def\_{\kern.04em\vbox{\hrule width.3em height .6pt}\kern.08em}% + #1\/\kern.05em}} % boldface type for reserved words +\def\.#1{\leavevmode\hbox{\tentex % typewriter type for strings + \let\\=\BS % backslash in a string + \let\{=\LB % left brace in a string + \let\}=\RB % right brace in a string + \let\~=\TL % tilde in a string + \let\ =\SP % space in a string + \let\_=\UL % underline in a string + \let\&=\AM % ampersand in a string + \let\^=\CF % circumflex in a string + #1\kern.05em}} +\def\){\discretionary{\hbox{\tentex\BS}}{}{}} +\def\AT{@} % at sign for control text (not needed in versions >= 2.9) +\def\ATL{\par\noindent\bgroup\catcode`\_=12 \postATL} % print @l in limbo +\def\postATL#1 #2 {\bf letter \\{\uppercase{\char"#1}} + tangles as \tentex "#2"\egroup\par} +\def\noATL#1 #2 {} +\def\noatl{\let\ATL=\noATL} % suppress output from @l +\def\ATH{\X\kern-.5em:Preprocessor definitions\X} +\let\PB=\relax % hook for program brackets |...| in TeX part or section name + +\chardef\AM=`\& % ampersand character in a string +\chardef\BS=`\\ % backslash in a string +\chardef\LB=`\{ % left brace in a string +\chardef\RB=`\} % right brace in a string +\def\SP{{\tt\char`\ }} % (visible) space in a string +\chardef\TL=`\~ % tilde in a string +\chardef\UL=`\_ % underline character in a string +\chardef\CF=`\^ % circumflex character in a string + +\newbox\PPbox % symbol for ++ +\setbox\PPbox=\hbox{\kern.5pt\raise1pt\hbox{\sevenrm+\kern-1pt+}\kern.5pt} +\def\PP{\copy\PPbox} +\newbox\MMbox \setbox\MMbox=\hbox{\kern.5pt\raise1pt\hbox{\sevensy\char0 + \kern-1pt\char0}\kern.5pt} +\def\MM{\copy\MMbox} +\newbox\MGbox % symbol for -> +\setbox\MGbox=\hbox{\kern-2pt\lower3pt\hbox{\teni\char'176}\kern1pt} +\def\MG{\copy\MGbox} +\newbox\NNbox % symbol for ## +\setbox\NNbox=\hbox{\kern.5pt\raise2pt\hbox{\#}\kern-4pt + \lower2pt\hbox{\#}\kern.5pt} +\def\NN{\copy\NNbox} +\def\MRL#1{\mathrel{\let\K==#1}} +%\def\MRL#1{\KK#1}\def\KK#1#2{\buildrel\;#1\over{#2}} +\let\GG=\gg +\let\LL=\ll +\let\NULL=\Lambda +\mathchardef\AND="2026 % bitwise and; also \& (unary operator) +\let\OR=\mid % bitwise or +\let\XOR=\oplus % bitwise exclusive or +\def\CM{{\sim}} % bitwise complement +\newbox\MODbox \setbox\MODbox=\hbox{\eightrm\%} +\def\MOD{\mathbin{\copy\MODbox}} +\def\DC{\kern.1em{::}\kern.1em} % symbol for :: +\def\PA{\mathbin{.*}} % symbol for .* +\def\MGA{\mathbin{\MG*}} % symbol for ->* +\def\this{\&{this}} + +\newbox\bak \setbox\bak=\hbox to -1em{} % backspace one em +\newbox\bakk\setbox\bakk=\hbox to -2em{} % backspace two ems + +\newcount\ind % current indentation in ems +\def\1{\global\advance\ind by1\hangindent\ind em} % indent one more notch +\def\2{\global\advance\ind by-1} % indent one less notch +\def\3#1{\hfil\penalty#10\hfilneg} % optional break within a statement +\def\4{\copy\bak} % backspace one notch +\def\5{\hfil\penalty-1\hfilneg\kern2.5em\copy\bakk\ignorespaces}% optional break +\def\6{\ifmmode\else\par % forced break + \hangindent\ind em\noindent\kern\ind em\copy\bakk\ignorespaces\fi} +%\def\7{\Y\6} % forced break and a little extra space +\def\7{\Y\penalty-150\6} % forced break and a little extra space +\def\8{\hskip-\ind em\hskip 2em} % no indentation + +\newcount\gdepth % depth of current major group, plus one +\newcount\secpagedepth +\secpagedepth=3 % page breaks will occur for depths -1, 0, and 1 +\newtoks\gtitle % title of current major group +\newskip\intersecskip \intersecskip=12pt minus 3pt % space between sections +%\let\yskip=\smallskip +\def\yskip{\vskip 5pt plus 1pt minus 1pt} +\def\?{\mathrel?} +\def\note#1#2.{\Y\noindent{\hangindent2em\baselineskip10pt\eightrm#1~#2.\par}} +\def\lapstar{\rlap{*}} +%\def\stsec{\rightskip=0pt % get out of C mode (cf. \B) +% \sfcode`;=1500 \pretolerance 200 \hyphenpenalty 50 \exhyphenpenalty 50 +% \noindent{\let\*=\lapstar\bf\secstar.\quad}} +\def\stsec{\rightskip=0pt % get out of C mode (cf. \B) + \parskip=4pt \parindent=0pt + \sfcode`;=1500 \pretolerance 200 \hyphenpenalty 50 \exhyphenpenalty 50 + \noindent{\let\*=\lapstar\sectionfont\secstar.\quad}} +\let\startsection=\stsec +\def\defin#1{\global\advance\ind by 2 \1\&{#1 } } % begin `define' or `format' +\def\A{\note{See also section}} % xref for doubly defined section name +\def\As{\note{See also sections}} % xref for multiply defined section name +%\def\B{\rightskip=0pt plus 100pt minus 10pt % go into C mode +% \sfcode`;=3000 +% \pretolerance 10000 +% \hyphenpenalty 9999 % so strings can be broken (discretionary \ is inserted) +% \exhyphenpenalty 10000 +% \global\ind=2 \1\ \unskip} +\def\B{\rightskip=0pt plus 100pt minus 10pt % go into C mode + \parskip=0pt \parindent=1em + \sfcode`;=3000 + \pretolerance 10000 + \hyphenpenalty 9999 % so strings can be broken (discretionary \ is inserted) + \exhyphenpenalty 10000 + \global\ind=2 \1\ \unskip} +%\def\C#1{\5\5\quad$/\ast\,${\cmntfont #1}$\,\ast/$} +\def\C#1{\5\5\hfill$/\ast\,${\cmntfont #1}$\,\ast/$} +\let\SHC\C % "// short comments" treated like "/* ordinary comments */" +%\def\C#1{\5\5\quad$\triangleright\,${\cmntfont#1}$\,\triangleleft$} +%\def\SHC#1{\5\5\quad$\diamond\,${\cmntfont#1}} +\def\D{\defin{\#define}} % macro definition +\let\E=\equiv % equivalence sign +\def\ET{ and~} % conjunction between two section numbers +\def\ETs{, and~} % conjunction between the last two of several section numbers +\def\F{\defin{format}} % format definition +\let\G=\ge % greater than or equal sign +% \H is long Hungarian umlaut accent +\let\I=\ne % unequal sign +\def\J{\.{@\&}} % TANGLE's join operation +\let\K== % assignment operator +%\let\K=\leftarrow % "honest" alternative to standard assignment operator +% \L is Polish letter suppressed-L +%\outer\def\M#1{\MN{#1}\ifon\vfil\penalty-100\vfilneg % beginning of section +% \vskip\intersecskip\startsection\ignorespaces} +\outer\def\M#1{\MN{#1}\ifon\vfil\penalty-100\vfilneg % beginning of section + \vskip\intersecskip\startsection\smallskip\noindent\ignorespaces} +%\outer\def\N#1#2#3.{\gdepth=#1\gtitle={#3}\MN{#2}% beginning of starred section +% \ifon\ifnum#1<\secpagedepth \vfil\eject % force page break if depth is small +% \else\vfil\penalty-100\vfilneg\vskip\intersecskip\fi\fi +% \message{*\secno} % progress report +% \edef\next{\write\cont{\ZZ{#3}{#1}{\secno}% write to contents file +% {\noexpand\the\pageno}}}\next % \ZZ{title}{depth}{sec}{page} +% \ifon\startsection{\bf#3.\quad}\ignorespaces} +\outer\def\N#1#2#3.{\gdepth=#1\gtitle={#3}\MN{#2}% beginning of starred section + \ifon\ifnum#1<\secpagedepth \vfil\eject % force page break if depth is small + \else\vfil\penalty-100\vfilneg\vskip\intersecskip\fi\fi + \message{*\secno} % progress report + \edef\next{\write\cont{\ZZ{#3}{#1}{\secno}% write to contents file + {\noexpand\the\pageno}}}\next % \ZZ{title}{depth}{sec}{page} + \ifon\startsection{\sectionfont#3.}\bigskip\noindent\ignorespaces} +\def\MN#1{\par % common code for \M, \N + {\xdef\secstar{#1}\let\*=\empty\xdef\secno{#1}}% remove \* from section name + \ifx\secno\secstar \onmaybe \else\ontrue \fi + \mark{{{\tensy x}\secno}{\the\gdepth}{\the\gtitle}}} +% each \mark is {section reference or null}{depth plus 1}{group title} +% \O is Scandinavian letter O-with-slash +% \P is paragraph sign +\def\Q{\note{This code is cited in section}} % xref for mention of a section +\def\Qs{\note{This code is cited in sections}} % xref for mentions of a section +%\let\R=\lnot % logical not +\def\R{\mathord ! \,} % logical not +% \S is section sign +\def\T#1{\leavevmode % octal, hex or decimal constant + \hbox{$\def\?{\kern.2em}% + \def\$##1{\egroup_{\,\rm##1}\bgroup}% suffix to constant + \def\_{\cdot 10^{\aftergroup}}% power of ten (via dirty trick) + \let\~=\oct \let\^=\hex {#1}$}} +\def\U{\note{This code is used in section}} % xref for use of a section +\def\Us{\note{This code is used in sections}} % xref for uses of a section +\let\V=\lor % logical or +\let\W=\land % logical and +\def\X#1:#2\X{\ifmmode\gdef\XX{\null$\null}\else\gdef\XX{}\fi % section name + \XX$\langle\,${#2\eightrm\kern.5em#1}$\,\rangle$\XX} +\def\Y{\par\yskip} +\let\Z=\le +\let\ZZ=\let % now you can \write the control sequence \ZZ +\let\*=* + +%\def\oct{\hbox{\rm\char'23\kern-.2em\it\aftergroup\?\aftergroup}} % WEB style +%\def\hex{\hbox{\rm\char"7D\tt\aftergroup}} % WEB style +\def\oct{\hbox{$^\circ$\kern-.1em\it\aftergroup\?\aftergroup}}% CWEB style +\def\hex{\hbox{$^{\scriptscriptstyle\#}$\tt\aftergroup}} % CWEB style +\def\vb#1{\leavevmode\hbox{\kern2pt\vrule\vtop{\vbox{\hrule + \hbox{\strut\kern2pt\.{#1}\kern2pt}} + \hrule}\vrule\kern2pt}} % verbatim string + +\def\onmaybe{\let\ifon=\maybe} \let\maybe=\iftrue +\newif\ifon \newif\iftitle \newif\ifpagesaved + +\def\lheader{\mainfont\the\pageno\eightrm\qquad\grouptitle\hfill\title\qquad + \mainfont\topsecno} % top line on left-hand pages +\def\rheader{\mainfont\topsecno\eightrm\qquad\title\hfill\grouptitle + \qquad\mainfont\the\pageno} % top line on right-hand pages +\def\grouptitle{\let\i=I\let\j=J\uppercase\expandafter{\expandafter + \takethree\topmark}} +\def\topsecno{\expandafter\takeone\topmark} +\def\takeone#1#2#3{#1} +\def\taketwo#1#2#3{#2} +\def\takethree#1#2#3{#3} +\def\nullsec{\eightrm\kern-2em} % the \kern-2em cancels \qquad in headers + +\let\page=\pagebody \raggedbottom +% \def\page{\box255 }\normalbottom % faster, but loses plain TeX footnotes +\def\normaloutput#1#2#3{\ifodd\pageno\hoffset=\pageshift\fi + \shipout\vbox{ + \vbox to\fullpageheight{ + \iftitle\global\titlefalse + \else\hbox to\pagewidth{\vbox to10pt{}\ifodd\pageno #3\else#2\fi}\fi + \vfill#1}} % parameter #1 is the page itself + \global\advance\pageno by1} + +\gtitle={\.{CWEB} output} % this running head is reset by starred sections +\mark{\noexpand\nullsec0{\the\gtitle}} +\def\title{\expandafter\uppercase\expandafter{\jobname}} +\def\topofcontents{\centerline{\titlefont\title}\vskip.7in + \vfill} % this material will start the table of contents page +\def\botofcontents{\vfill + \centerline{\covernote}} % this material will end the table of contents page +\def\covernote{} +\def\contentspagenumber{0} % default page number for table of contents +\newdimen\pagewidth \pagewidth=6.5in % the width of each page +\newdimen\pageheight \pageheight=8.7in % the height of each page +\newdimen\fullpageheight \fullpageheight=9in % page height including headlines +\newdimen\pageshift \pageshift=0in % shift righthand pages wrt lefthand ones +\def\magnify#1{\mag=#1\pagewidth=6.5truein\pageheight=8.7truein + \fullpageheight=9truein\setpage} +\def\setpage{\hsize\pagewidth\vsize\pageheight} % use after changing page size +\def\contentsfile{\jobname.toc} % file that gets table of contents info +\def\readcontents{\input \contentsfile} +\def\readindex{\input \jobname.idx} +\def\readsections{\input \jobname.scn} + +\newwrite\cont +\output{\setbox0=\page % the first page is garbage + \openout\cont=\contentsfile + \write\cont{\catcode `\noexpand\@=11\relax} % \makeatletter + \global\output{\normaloutput\page\lheader\rheader}} +\setpage +\vbox to \vsize{} % the first \topmark won't be null + +\def\ch{\note{The following sections were changed by the change file:} + \let\*=\relax} +\newbox\sbox % saved box preceding the index +\newbox\lbox % lefthand column in the index +\def\inx{\par\vskip6pt plus 1fil % we are beginning the index + \def\page{\box255 } \normalbottom + \write\cont{} % ensure that the contents file isn't empty + \write\cont{\catcode `\noexpand\@=12\relax} % \makeatother + \closeout\cont % the contents information has been fully gathered + \output{\ifpagesaved\normaloutput{\box\sbox}\lheader\rheader\fi + \global\setbox\sbox=\page \global\pagesavedtrue} + \pagesavedfalse \eject % eject the page-so-far and predecessors + \setbox\sbox\vbox{\unvbox\sbox} % take it out of its box + \vsize=\pageheight \advance\vsize by -\ht\sbox % the remaining height + \hsize=.5\pagewidth \advance\hsize by -10pt + % column width for the index (20pt between cols) + \parfillskip 0pt plus .6\hsize % try to avoid almost empty lines + \def\lr{L} % this tells whether the left or right column is next + \output{\if L\lr\global\setbox\lbox=\page \gdef\lr{R} + \else\normaloutput{\vbox to\pageheight{\box\sbox\vss + \hbox to\pagewidth{\box\lbox\hfil\page}}}\lheader\rheader + \global\vsize\pageheight\gdef\lr{L}\global\pagesavedfalse\fi} + \message{Index:} + \parskip 0pt plus .5pt + \outer\def\I##1, {\par\hangindent2em\noindent##1:\kern1em} % index entry + \def\[##1]{$\underline{##1}$} % underlined index item + \rm \rightskip0pt plus 2.5em \tolerance 10000 \let\*=\lapstar + \hyphenpenalty 10000 \parindent0pt + \readindex} +\def\fin{\par\vfill\eject % this is done when we are ending the index + \ifpagesaved\null\vfill\eject\fi % output a null index column + \if L\lr\else\null\vfill\eject\fi % finish the current page + \parfillskip 0pt plus 1fil + \def\grouptitle{NAMES OF THE SECTIONS} + \let\topsecno=\nullsec + \message{Section names:} + \output={\normaloutput\page\lheader\rheader} + \setpage + \def\note##1##2.{\quad{\eightrm##1~##2.}} + \def\Q{\note{Cited in section}} % crossref for mention of a section + \def\Qs{\note{Cited in sections}} % crossref for mentions of a section + \def\U{\note{Used in section}} % crossref for use of a section + \def\Us{\note{Used in sections}} % crossref for uses of a section + \def\I{\par\hangindent 2em}\let\*=* + \readsections} +\def\con{\par\vfill\eject % finish the section names +% \ifodd\pageno\else\titletrue\null\vfill\eject\fi % for duplex printers + \rightskip 0pt \hyphenpenalty 50 \tolerance 200 + \setpage \output={\normaloutput\page\lheader\rheader} + \titletrue % prepare to output the table of contents + \pageno=\contentspagenumber + \def\grouptitle{TABLE OF CONTENTS} + \message{Table of contents:} + \topofcontents + \line{\hfil Section\hbox to3em{\hss Page}} + \let\ZZ=\contentsline + \readcontents\relax % read the contents info + \botofcontents \end} % print the contents page(s) and terminate +\def\contentsline#1#2#3#4{\ifnum#2=0 \smallbreak\fi + \line{\consetup{#2}#1 + \rm\leaders\hbox to .5em{.\hfil}\hfil\ #3\hbox to3em{\hss#4}}} +\def\consetup#1{\ifcase#1 \bf % depth -1 (@**) + \or % depth 0 (@*) + \or \hskip2em % depth 1 (@*1) + \or \hskip4em % depth 2 (@*2) + \or \hskip6em % depth 3 (@*3) + \or \hskip8em % depth 4 (@*4) + \or \hskip10em % depth 5 (@*5) + \else \hskip12em \fi} % depth 6 or more +\def\noinx{\let\inx=\end} % no indexes or table of contents +\def\nosecs{\let\FIN=\fin \def\fin{\let\parfillskip=\end \FIN}} + % no index of section names or table of contents +\def\nocon{\let\con=\end} % no table of contents +\def\today{\ifcase\month\or + January\or February\or March\or April\or May\or June\or + July\or August\or September\or October\or November\or December\fi + \space\number\day, \number\year} +\newcount\twodigits +\def\hours{\twodigits=\time \divide\twodigits by 60 \printtwodigits + \multiply\twodigits by-60 \advance\twodigits by\time :\printtwodigits} +\def\gobbleone1{} +\def\printtwodigits{\advance\twodigits100 + \expandafter\gobbleone\number\twodigits + \advance\twodigits-100 } +\def\TeX{{\ifmmode\it\fi + \leavevmode\hbox{T\kern-.1667em\lower.424ex\hbox{E}\hskip-.125em X}}} +\def\,{\relax\ifmmode\mskip\thinmuskip\else\thinspace\fi} +\def\datethis{\def\startsection{\leftline{\sc\today\ at \hours}\bigskip + \let\startsection=\stsec\stsec}} + % say `\datethis' in limbo, to get your listing timestamped before section 1 +\def\datecontentspage{% + \def\topofcontents{\leftline{\sc\today\ at \hours}\bigskip + \centerline{\titlefont\title}\vfill}} % timestamps the contents page + +\endinput + + +% end of cwebmac.tex diff --git a/web/c_cpp/c2cweb/cweb/cwebmaca.tex b/web/c_cpp/c2cweb/cweb/cwebmaca.tex new file mode 100644 index 0000000000..307063f863 --- /dev/null +++ b/web/c_cpp/c2cweb/cweb/cwebmaca.tex @@ -0,0 +1,381 @@ +% This file is part of c2cweb package Version 1.5 + +% standard macros for CWEB listings (in addition to plain.tex) +% Version 3.0 --- June 1993 + +% modified by Werner Lemberg (a7971428@unet.univie.ac.at) 10-Nov-1996 +% to use with CWEAVE +a + +\ifx\documentstyle\undefined\else\endinput\fi % LaTeX will use other macros +\xdef\fmtversion{\fmtversion+CWEB3.2} + +\let\:=\. % preserve a way to get the dot accent + % (all other accents will still work as usual) + +%\parskip 0pt % no stretch between paragraphs +%\parindent 1em % for paragraphs and for the first line of C text + +\font\ninerm=cmr9 +\let\mc=\ninerm % medium caps +\def\CEE/{{\mc C\spacefactor1000}} +\def\UNIX/{{\mc U\kern-.05emNIX\spacefactor1000}} +\def\TEX/{\TeX} +\def\CPLUSPLUS/{{\mc C\PP\spacefactor1000}} +\def\Cee{\CEE/} % for backward compatibility +\def\9#1{} +\font\eightrm=cmr8 +\let\sc=\eightrm % small caps (NOT a caps-and-small-caps font) +\let\mainfont=\tenrm +\let\cmntfont\tenrm +%\font\tenss=cmss10 \let\cmntfont\tenss % alternative comment font +%\font\titlefont=cmr7 scaled\magstep4 % title on the contents page +\font\titlefont=cmssbx10 scaled\magstep2 % title on the contents page +\font\ttitlefont=cmtt10 scaled\magstep2 % typewriter type in title +\font\tentex=cmtex10 % TeX extended character set (used in strings) +\fontdimen7\tentex=0pt % no double space after sentences + +\font\sectionfont=cmssbx10 scaled\magstephalf + +\def\${\ifmmode \hbox{\sl \char`\$} + \else \hbox{\ifdim \fontdimen1\font > 0pt \sl \fi \char`\$}\fi} + +\def\\#1{\leavevmode\hbox{\it#1\/\kern.05em}} % italic type for identifiers +\def\|#1{\leavevmode\hbox{$#1$}} % one-letter identifiers look better this way +\def\{\leavevmode\hbox{\bf + \def\_{\kern.04em\vbox{\hrule width.3em height .6pt}\kern.08em}% + #1\/\kern.05em}} % boldface type for reserved words +\def\.#1{\leavevmode\hbox{\tentex % typewriter type for strings + \let\\=\BS % backslash in a string + \let\{=\LB % left brace in a string + \let\}=\RB % right brace in a string + \let\~=\TL % tilde in a string + \let\ =\SP % space in a string + \let\_=\UL % underline in a string + \let\&=\AM % ampersand in a string + \let\^=\CF % circumflex in a string + #1\kern.05em}} +\def\){\discretionary{\hbox{\tentex\BS}}{}{}} +\def\AT{@} % at sign for control text (not needed in versions >= 2.9) +\def\ATL{\par\noindent\bgroup\catcode`\_=12 \postATL} % print @l in limbo +\def\postATL#1 #2 {\bf letter \\{\uppercase{\char"#1}} + tangles as \tentex "#2"\egroup\par} +\def\noATL#1 #2 {} +\def\noatl{\let\ATL=\noATL} % suppress output from @l +\def\ATH{\X\kern-.5em:Preprocessor definitions\X} +%\let\PB=\relax % hook for program brackets |...| in TeX part or section name +\def\PB#1{{\def\LBR{\LB}\def\RBR{\RB}#1}} + +\chardef\AM=`\& % ampersand character in a string +\chardef\BS=`\\ % backslash in a string +\chardef\LB=`\{ % left brace in a string +\chardef\RB=`\} % right brace in a string +\def\SP{{\tt\char`\ }} % (visible) space in a string +\chardef\TL=`\~ % tilde in a string +\chardef\UL=`\_ % underline character in a string +\chardef\CF=`\^ % circumflex character in a string + +\newbox\PPbox % symbol for ++ +\setbox\PPbox=\hbox{\kern.5pt\raise1pt\hbox{\sevenrm+\kern-1pt+}\kern.5pt} +\def\PP{\copy\PPbox} +\newbox\MMbox \setbox\MMbox=\hbox{\kern.5pt\raise1pt\hbox{\sevensy\char0 + \kern-1pt\char0}\kern.5pt} +\def\MM{\copy\MMbox} +\newbox\MGbox % symbol for -> +\setbox\MGbox=\hbox{\kern-2pt\lower3pt\hbox{\teni\char'176}\kern1pt} +\def\MG{\copy\MGbox} +\newbox\NNbox % symbol for ## +\setbox\NNbox=\hbox{\kern.5pt\raise2pt\hbox{\#}\kern-4pt + \lower2pt\hbox{\#}\kern.5pt} +\def\NN{\copy\NNbox} +\def\MRL#1{\mathrel{\let\K==#1}} +%\def\MRL#1{\KK#1}\def\KK#1#2{\buildrel\;#1\over{#2}} +\let\GG=\gg +\let\LL=\ll +\let\NULL=\Lambda +\mathchardef\AND="2026 % bitwise and; also \& (unary operator) +\let\OR=\mid % bitwise or +\let\XOR=\oplus % bitwise exclusive or +\def\CM{{\sim}} % bitwise complement +\newbox\MODbox \setbox\MODbox=\hbox{\eightrm\%} +\def\MOD{\mathbin{\copy\MODbox}} +\def\DC{\kern.1em{::}\kern.1em} % symbol for :: +\def\PA{\mathbin{.*}} % symbol for .* +\def\MGA{\mathbin{\MG*}} % symbol for ->* +\def\this{\&{this}} + +\newbox\bak \setbox\bak=\hbox to -1em{} % backspace one em +\newbox\bakk\setbox\bakk=\hbox to -2em{} % backspace two ems + +\newbox\LBRbox \setbox\LBRbox=\hbox to 1em{\hss$\{$} +\newbox\RBRbox \setbox\RBRbox=\hbox to 1em{\hss$\}$} +\def\LBR{\copy\LBRbox} +\def\RBR{\copy\RBRbox} + +\newcount\ind % current indentation in ems +\def\1{\global\advance\ind by1\hangindent\ind em} % indent one more notch +\def\2{\global\advance\ind by-1} % indent one less notch +\def\3#1{\hfil\penalty#10\hfilneg} % optional break within a statement +\def\4{\copy\bak} % backspace one notch +\def\5{\hfil\penalty-1\hfilneg\kern2.5em\copy\bakk\ignorespaces}% optional break +\def\6{\ifmmode\else\par % forced break + \hangindent\ind em\noindent\kern\ind em\copy\bakk\ignorespaces\fi} +%\def\7{\Y\6} % forced break and a little extra space +\def\7{\Y\penalty-150\6} % forced break and a little extra space +\def\8{\hskip-\ind em\hskip 2em} % no indentation + +\newcount\gdepth % depth of current major group, plus one +\newcount\secpagedepth +\secpagedepth=3 % page breaks will occur for depths -1, 0, and 1 +\newtoks\gtitle % title of current major group +\newskip\intersecskip \intersecskip=12pt minus 3pt % space between sections +%\let\yskip=\smallskip +\def\yskip{\vskip 5pt plus 1pt minus 1pt} +\def\?{\mathrel?} +\def\note#1#2.{\Y\noindent{\hangindent2em\baselineskip10pt\eightrm#1~#2.\par}} +\def\lapstar{\rlap{*}} +%\def\stsec{\rightskip=0pt % get out of C mode (cf. \B) +% \sfcode`;=1500 \pretolerance 200 \hyphenpenalty 50 \exhyphenpenalty 50 +% \noindent{\let\*=\lapstar\bf\secstar.\quad}} +\def\stsec{\rightskip=0pt % get out of C mode (cf. \B) + \parskip=4pt \parindent=0pt + \sfcode`;=1500 \pretolerance 200 \hyphenpenalty 50 \exhyphenpenalty 50 + \noindent{\let\*=\lapstar\sectionfont\secstar.\quad}} +\let\startsection=\stsec +\def\defin#1{\global\advance\ind by 2 \1\&{#1 } } % begin `define' or `format' +\def\A{\note{See also section}} % xref for doubly defined section name +\def\As{\note{See also sections}} % xref for multiply defined section name +%\def\B{\rightskip=0pt plus 100pt minus 10pt % go into C mode +% \sfcode`;=3000 +% \pretolerance 10000 +% \hyphenpenalty 9999 % so strings can be broken (discretionary \ is inserted) +% \exhyphenpenalty 10000 +% \global\ind=2 \1\ \unskip} +\def\B{\rightskip=0pt plus 100pt minus 10pt % go into C mode + \parskip=0pt \parindent=1em + \sfcode`;=3000 + \pretolerance 10000 + \hyphenpenalty 9999 % so strings can be broken (discretionary \ is inserted) + \exhyphenpenalty 10000 + \global\ind=2 \1\ \unskip} +%\def\C#1{\5\5\quad$/\ast\,${\cmntfont #1}$\,\ast/$} +\def\C#1{\5\5\hfill$/\ast\,${\cmntfont #1}$\,\ast/$} +\let\SHC\C % "// short comments" treated like "/* ordinary comments */" +%\def\C#1{\5\5\quad$\triangleright\,${\cmntfont#1}$\,\triangleleft$} +%\def\SHC#1{\5\5\quad$\diamond\,${\cmntfont#1}} +\def\D{\defin{\#define}} % macro definition +\let\E=\equiv % equivalence sign +\def\ET{ and~} % conjunction between two section numbers +\def\ETs{, and~} % conjunction between the last two of several section numbers +\def\F{\defin{format}} % format definition +\let\G=\ge % greater than or equal sign +% \H is long Hungarian umlaut accent +\let\I=\ne % unequal sign +\def\J{\.{@\&}} % TANGLE's join operation +\let\K== % assignment operator +%\let\K=\leftarrow % "honest" alternative to standard assignment operator +% \L is Polish letter suppressed-L +%\outer\def\M#1{\MN{#1}\ifon\vfil\penalty-100\vfilneg % beginning of section +% \vskip\intersecskip\startsection\ignorespaces} +\outer\def\M#1{\MN{#1}\ifon\vfil\penalty-100\vfilneg % beginning of section + \vskip\intersecskip\startsection\smallskip\noindent\ignorespaces} +%\outer\def\N#1#2#3.{\gdepth=#1\gtitle={#3}\MN{#2}% beginning of starred section +% \ifon\ifnum#1<\secpagedepth \vfil\eject % force page break if depth is small +% \else\vfil\penalty-100\vfilneg\vskip\intersecskip\fi\fi +% \message{*\secno} % progress report +% \edef\next{\write\cont{\ZZ{#3}{#1}{\secno}% write to contents file +% {\noexpand\the\pageno}}}\next % \ZZ{title}{depth}{sec}{page} +% \ifon\startsection{\bf#3.\quad}\ignorespaces} +\outer\def\N#1#2#3.{\gdepth=#1\gtitle={#3}\MN{#2}% beginning of starred section + \ifon\ifnum#1<\secpagedepth \vfil\eject % force page break if depth is small + \else\vfil\penalty-100\vfilneg\vskip\intersecskip\fi\fi + \message{*\secno} % progress report + \edef\next{\write\cont{\ZZ{#3}{#1}{\secno}% write to contents file + {\noexpand\the\pageno}}}\next % \ZZ{title}{depth}{sec}{page} + \ifon\startsection{\sectionfont#3.}\bigskip\noindent\ignorespaces} +\def\MN#1{\par % common code for \M, \N + {\xdef\secstar{#1}\let\*=\empty\xdef\secno{#1}}% remove \* from section name + \ifx\secno\secstar \onmaybe \else\ontrue \fi + \mark{{{\tensy x}\secno}{\the\gdepth}{\the\gtitle}}} +% each \mark is {section reference or null}{depth plus 1}{group title} +% \O is Scandinavian letter O-with-slash +% \P is paragraph sign +\def\Q{\note{This code is cited in section}} % xref for mention of a section +\def\Qs{\note{This code is cited in sections}} % xref for mentions of a section +%\let\R=\lnot % logical not +\def\R{\mathord ! \,} % logical not +% \S is section sign +\def\T#1{\leavevmode % octal, hex or decimal constant + \hbox{$\def\?{\kern.2em}% + \def\$##1{\egroup_{\,\rm##1}\bgroup}% suffix to constant + \def\_{\cdot 10^{\aftergroup}}% power of ten (via dirty trick) + \let\~=\oct \let\^=\hex {#1}$}} +\def\U{\note{This code is used in section}} % xref for use of a section +\def\Us{\note{This code is used in sections}} % xref for uses of a section +\let\V=\lor % logical or +\let\W=\land % logical and +\def\X#1:#2\X{\ifmmode\gdef\XX{\null$\null}\else\gdef\XX{}\fi % section name + \XX$\langle\,${#2\eightrm\kern.5em#1}$\,\rangle$\XX} +\def\Y{\par\yskip} +\let\Z=\le +\let\ZZ=\let % now you can \write the control sequence \ZZ +\let\*=* + +%\def\oct{\hbox{\rm\char'23\kern-.2em\it\aftergroup\?\aftergroup}} % WEB style +%\def\hex{\hbox{\rm\char"7D\tt\aftergroup}} % WEB style +\def\oct{\hbox{$^\circ$\kern-.1em\it\aftergroup\?\aftergroup}}% CWEB style +\def\hex{\hbox{$^{\scriptscriptstyle\#}$\tt\aftergroup}} % CWEB style +\def\vb#1{\leavevmode\hbox{\kern2pt\vrule\vtop{\vbox{\hrule + \hbox{\strut\kern2pt\.{#1}\kern2pt}} + \hrule}\vrule\kern2pt}} % verbatim string + +\def\onmaybe{\let\ifon=\maybe} \let\maybe=\iftrue +\newif\ifon \newif\iftitle \newif\ifpagesaved + +\def\lheader{\mainfont\the\pageno\eightrm\qquad\grouptitle\hfill\title\qquad + \mainfont\topsecno} % top line on left-hand pages +\def\rheader{\mainfont\topsecno\eightrm\qquad\title\hfill\grouptitle + \qquad\mainfont\the\pageno} % top line on right-hand pages +\def\grouptitle{\let\i=I\let\j=J\uppercase\expandafter{\expandafter + \takethree\topmark}} +\def\topsecno{\expandafter\takeone\topmark} +\def\takeone#1#2#3{#1} +\def\taketwo#1#2#3{#2} +\def\takethree#1#2#3{#3} +\def\nullsec{\eightrm\kern-2em} % the \kern-2em cancels \qquad in headers + +\let\page=\pagebody \raggedbottom +% \def\page{\box255 }\normalbottom % faster, but loses plain TeX footnotes +\def\normaloutput#1#2#3{\ifodd\pageno\hoffset=\pageshift\fi + \shipout\vbox{ + \vbox to\fullpageheight{ + \iftitle\global\titlefalse + \else\hbox to\pagewidth{\vbox to10pt{}\ifodd\pageno #3\else#2\fi}\fi + \vfill#1}} % parameter #1 is the page itself + \global\advance\pageno by1} + +\gtitle={\.{CWEB} output} % this running head is reset by starred sections +\mark{\noexpand\nullsec0{\the\gtitle}} +\def\title{\expandafter\uppercase\expandafter{\jobname}} +\def\topofcontents{\centerline{\titlefont\title}\vskip.7in + \vfill} % this material will start the table of contents page +\def\botofcontents{\vfill + \centerline{\covernote}} % this material will end the table of contents page +\def\covernote{} +\def\contentspagenumber{0} % default page number for table of contents +\newdimen\pagewidth \pagewidth=6.5in % the width of each page +\newdimen\pageheight \pageheight=8.7in % the height of each page +\newdimen\fullpageheight \fullpageheight=9in % page height including headlines +\newdimen\pageshift \pageshift=0in % shift righthand pages wrt lefthand ones +\def\magnify#1{\mag=#1\pagewidth=6.5truein\pageheight=8.7truein + \fullpageheight=9truein\setpage} +\def\setpage{\hsize\pagewidth\vsize\pageheight} % use after changing page size +\def\contentsfile{\jobname.toc} % file that gets table of contents info +\def\readcontents{\input \contentsfile} +\def\readindex{\input \jobname.idx} +\def\readsections{\input \jobname.scn} + +\newwrite\cont +\output{\setbox0=\page % the first page is garbage + \openout\cont=\contentsfile + \write\cont{\catcode `\noexpand\@=11\relax} % \makeatletter + \global\output{\normaloutput\page\lheader\rheader}} +\setpage +\vbox to \vsize{} % the first \topmark won't be null + +\def\ch{\note{The following sections were changed by the change file:} + \let\*=\relax} +\newbox\sbox % saved box preceding the index +\newbox\lbox % lefthand column in the index +\def\inx{\par\vskip6pt plus 1fil % we are beginning the index + \def\page{\box255 } \normalbottom + \write\cont{} % ensure that the contents file isn't empty + \write\cont{\catcode `\noexpand\@=12\relax} % \makeatother + \closeout\cont % the contents information has been fully gathered + \output{\ifpagesaved\normaloutput{\box\sbox}\lheader\rheader\fi + \global\setbox\sbox=\page \global\pagesavedtrue} + \pagesavedfalse \eject % eject the page-so-far and predecessors + \setbox\sbox\vbox{\unvbox\sbox} % take it out of its box + \vsize=\pageheight \advance\vsize by -\ht\sbox % the remaining height + \hsize=.5\pagewidth \advance\hsize by -10pt + % column width for the index (20pt between cols) + \parfillskip 0pt plus .6\hsize % try to avoid almost empty lines + \def\lr{L} % this tells whether the left or right column is next + \output{\if L\lr\global\setbox\lbox=\page \gdef\lr{R} + \else\normaloutput{\vbox to\pageheight{\box\sbox\vss + \hbox to\pagewidth{\box\lbox\hfil\page}}}\lheader\rheader + \global\vsize\pageheight\gdef\lr{L}\global\pagesavedfalse\fi} + \message{Index:} + \parskip 0pt plus .5pt + \outer\def\I##1, {\par\hangindent2em\noindent##1:\kern1em} % index entry + \def\[##1]{$\underline{##1}$} % underlined index item + \rm \rightskip0pt plus 2.5em \tolerance 10000 \let\*=\lapstar + \hyphenpenalty 10000 \parindent0pt + \readindex} +\def\fin{\par\vfill\eject % this is done when we are ending the index + \ifpagesaved\null\vfill\eject\fi % output a null index column + \if L\lr\else\null\vfill\eject\fi % finish the current page + \parfillskip 0pt plus 1fil + \def\grouptitle{NAMES OF THE SECTIONS} + \let\topsecno=\nullsec + \message{Section names:} + \output={\normaloutput\page\lheader\rheader} + \setpage + \def\note##1##2.{\quad{\eightrm##1~##2.}} + \def\Q{\note{Cited in section}} % crossref for mention of a section + \def\Qs{\note{Cited in sections}} % crossref for mentions of a section + \def\U{\note{Used in section}} % crossref for use of a section + \def\Us{\note{Used in sections}} % crossref for uses of a section + \def\I{\par\hangindent 2em}\let\*=* + \readsections} +\def\con{\par\vfill\eject % finish the section names +% \ifodd\pageno\else\titletrue\null\vfill\eject\fi % for duplex printers + \rightskip 0pt \hyphenpenalty 50 \tolerance 200 + \setpage \output={\normaloutput\page\lheader\rheader} + \titletrue % prepare to output the table of contents + \pageno=\contentspagenumber + \def\grouptitle{TABLE OF CONTENTS} + \message{Table of contents:} + \topofcontents + \line{\hfil Section\hbox to3em{\hss Page}} + \let\ZZ=\contentsline + \readcontents\relax % read the contents info + \botofcontents \end} % print the contents page(s) and terminate +\def\contentsline#1#2#3#4{\ifnum#2=0 \smallbreak\fi + \line{\consetup{#2}#1 + \rm\leaders\hbox to .5em{.\hfil}\hfil\ #3\hbox to3em{\hss#4}}} +\def\consetup#1{\ifcase#1 \bf % depth -1 (@**) + \or % depth 0 (@*) + \or \hskip2em % depth 1 (@*1) + \or \hskip4em % depth 2 (@*2) + \or \hskip6em % depth 3 (@*3) + \or \hskip8em % depth 4 (@*4) + \or \hskip10em % depth 5 (@*5) + \else \hskip12em \fi} % depth 6 or more +\def\noinx{\let\inx=\end} % no indexes or table of contents +\def\nosecs{\let\FIN=\fin \def\fin{\let\parfillskip=\end \FIN}} + % no index of section names or table of contents +\def\nocon{\let\con=\end} % no table of contents +\def\today{\ifcase\month\or + January\or February\or March\or April\or May\or June\or + July\or August\or September\or October\or November\or December\fi + \space\number\day, \number\year} +\newcount\twodigits +\def\hours{\twodigits=\time \divide\twodigits by 60 \printtwodigits + \multiply\twodigits by-60 \advance\twodigits by\time :\printtwodigits} +\def\gobbleone1{} +\def\printtwodigits{\advance\twodigits100 + \expandafter\gobbleone\number\twodigits + \advance\twodigits-100 } +\def\TeX{{\ifmmode\it\fi + \leavevmode\hbox{T\kern-.1667em\lower.424ex\hbox{E}\hskip-.125em X}}} +\def\,{\relax\ifmmode\mskip\thinmuskip\else\thinspace\fi} +\def\datethis{\def\startsection{\leftline{\sc\today\ at \hours}\bigskip + \let\startsection=\stsec\stsec}} + % say `\datethis' in limbo, to get your listing timestamped before section 1 +\def\datecontentspage{% + \def\topofcontents{\leftline{\sc\today\ at \hours}\bigskip + \centerline{\titlefont\title}\vfill}} % timestamps the contents page + +\endinput + +% end of cwebmaca.tex diff --git a/web/c_cpp/c2cweb/cweb/prod-alt.w b/web/c_cpp/c2cweb/cweb/prod-alt.w new file mode 100644 index 0000000000..f7849d830a --- /dev/null +++ b/web/c_cpp/c2cweb/cweb/prod-alt.w @@ -0,0 +1,303 @@ +% 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.0 --- June 1993 + +% this file is part of the c2cweb package Version 1.5 +% modified by Werner Lemberg (a7971428@@unet.univie.ac.at) 10-Nov-1996 + +@ +\def\v{\char'174} +\mathchardef\RA="3221 % right arrow +\mathchardef\BA="3224 % double arrow +Here is a table of all the productions if you use the \.{a} switch. +Each production that combines two or more consecutive scraps implicitly +inserts a {\tt \$} where necessary, that is, between scraps whose abutting +boundaries have different |mathness|. In this way we never get double +{\tt\$\$}. + +A translation is provided when the resulting scrap is not merely a +juxtaposition of the scraps it comes from. An asterisk$^*$ next to a scrap +means that its first identifier gets an underlined entry in the index, +via the function |make_underlined|. Two asterisks$^{**}$ means that both +|make_underlined| and |make_reserved| are called; that is, the +identifier's ilk becomes |raw_int|. A dagger \dag\ before the +production number refers to the notes at the end of this section, +which deal with various exceptional cases. + +We use \\{in}, \\{out}, \\{back} and +\\{bsp} as shorthands for |indent|, |outdent|, |backup| and +|break_space|, respectively. + +\begingroup \lineskip=4pt +\def\alt #1 #2 +{$\displaystyle\Bigl\{\!\matrix{\strut\hbox{#1}\cr + \strut\hbox{#2}\cr}\!\Bigr\}$ } +\def\altt #1 #2 #3 +{$\displaystyle\Biggl\{\!\matrix{\strut\hbox{#1}\cr\hbox{#2}\cr + \strut\hbox{#3}\cr}\!\Biggr\}$ } +\def\altq #1 #2 #3 #4 +{$\displaystyle\left\{\!\matrix{\strut\hbox{#1}\cr\hbox{#2}\cr + \hbox{#3}\cr\strut\hbox{#4}\cr}\!\right\}$ } +\def\malt #1 #2 +{$\displaystyle\matrix{\strut\hbox{#1}\hfill\cr\strut\hbox{#2}\hfill\cr}$} +\def\maltt #1 #2 #3 +{$\displaystyle\matrix{\strut\hbox{#1}\hfill\cr\hbox{#2}\hfill\cr + \strut\hbox{#3}\hfill\cr}$} +\yskip\newcount\prodno\prodno=0 +\newdimen\midcol \midcol=2.5in +\def\theprodno{\number\prodno \global\advance\prodno by1\enspace} +\def\+#1\cr{\def\next{#1}% + \line{\hbox to 2em{\hss + \ifx\next\empty\theprodno\else\next\fi}\strut + \ignorespaces#2\hfil\hbox to\midcol{$\RA$ + \ignorespaces#3\hfil}\quad \hbox to1.45in{\ignorespaces#4\hfil}}} +\+\relax & LHS & RHS \hfill Translation & Example\cr +\yskip +\+& \altt\\{any} {\\{any} \\{any}} {\\{any} \\{any} \\{any}} +|insert| & \altt\\{any} {\\{any} \\{any}} {\\{any} \\{any} \\{any}} +& stmt; \ /$\ast\,$comment$\,*$/\cr +\+& |exp| \altt|lbrace| |int_like| |decl| + & |fn_decl| \altt|lbrace| |int_like| |decl| + \hfill $F=E^*\,|in|\,|in|$ & \malt {\\{main}()$\{$} + {\\{main}$(\\{ac},\\{av})$ \&{int} \\{ac};} \cr +\+& |exp| |unop| & |exp| & |x++|\cr +\+& |exp| \alt |binop| |unorbinop| |exp| & |exp| & \malt {|x/y|} {|x+y|} \cr +\+& |exp| |comma| |exp| & |exp| \hfill $EC\,|opt|9\,E$& |f(x,y)|\cr +\+& |exp| \alt |exp| |cast| & |exp| & |time()|\cr +\+& |exp| |semi| & |stmt| & |x=0;|\cr +\+& |exp| |colon| & |label| \hfill $E^*C$ & |found:|\cr +\+& |exp| |base| |int_like| |comma| & |base| \hfill $B\.\ IC$\,|opt|9 + & \&D : \&C,\cr +\+& |exp| |base| |int_like| |lbrace| & |exp| |lbrace| \hfill + $E=E\.\ B\.\ I$ & \&D : \&C $\{$\cr +\+& |exp| |rbrace| & |stmt| |rbrace| & end of \&{enum} list\cr +\+& |lpar| \alt |exp| |unorbinop| |rpar| & |exp| & \malt{|(x)|} {|(*)|} \cr +\+& |lpar| |rpar| & |exp| \hfill $L\.{\\,}R$ & functions, declarations\cr +\+& |lpar| \alt |decl_head| |int_like| |rpar| & |cast| & |(char*)|\cr +\+& |lpar| \altt |decl_head| |int_like| |exp| |comma| & |lpar| \hfill + L\,\altt $D$ $I$ $E$ C\,|opt|9 & |(int,|\cr +\+& |lpar| \alt |stmt| |decl| & |lpar| \hfill \alt {$LS\.\ $} {$LD\.\ $} & + \alt {|(k=5;|} {|(int k=5;|} \cr +\+& |question| |exp| |colon| & |binop| & |?x:|\cr +\+& |unop| \alt |exp| |int_like| & \alt |exp| |int_like| & + \malt |!x| |~|\&C \cr +\+& |unorbinop| \alt|exp| |int_like| & \alt|exp| |int_like| \hfill + $\.\{U\.\}E$ & |*x|\cr +\+& |unorbinop| |binop| & |binop| \hfill $|math_rel|\,U\.\{B\.\}\.\}$ & |*=|\cr +\+& |binop| |binop| & |binop| \hfill + $|math_rel|\,\.\{B_1\.\}\.\{B_2\.\}\.\}$ & |>>=|\cr +\+& |cast| |exp| & |exp| \hfill $C\.\ E$ & |(double)x|\cr +\+& |cast| |semi| & |exp| |semi| & |(int);|\cr +\+& |sizeof_like| |cast| & |exp| & |sizeof (double)|\cr +\+& |sizeof_like| |exp| & |exp| \hfill $S\.\ E$ & |sizeof x|\cr +\+& |int_like| \alt|int_like| |struct_like| & + \alt|int_like| |struct_like| \hfill $I\.\ $\alt $I$ $S$ + \unskip& |extern char|\cr +\+& |int_like| |exp| \alt|raw_int| |struct_like| & + |int_like| \alt|int_like| |struct_like| & |extern "Ada" int|\cr +\+& |int_like| \altt|exp| |unorbinop| |semi| & |decl_head| + \altt|exp| |unorbinop| |semi| \hfill + $D=I$\altt{\.\ } {\.\ } {} \unskip & |int x|\cr +\+& |int_like| |colon| & |decl_head| |colon| \hfill $D=I\.\ $ & |unsigned:|\cr +\+& |int_like| |prelangle| & |int_like| |langle| & \&C$\langle$\cr +\+& |int_like| |colcol| \alt |exp| |int_like| & \alt |exp| |int_like| & + \malt {\&C\DC$x$} {\&C\DC\&B} \cr +\+& |int_like| |cast| |lbrace| & |fn_decl| |lbrace| \hfill $IC\,|in|\,|in|$& + \&C$\langle\&{void}\ast\rangle\{$\cr +\+& |int_like| |cast| & |int_like| & \&C$\langle\&{class}\ \&T\rangle$\cr +\+& |decl_head| |comma| & |decl_head| \hfill $DC\.\ $ & |int x,|\cr +\+& |decl_head| |unorbinop| & |decl_head| \hfill $D\.\{U\.\}$ & |int *|\cr +\+\dag\theprodno& |decl_head| |exp| & |decl_head| \hfill $DE^*$ & |int x|\cr +\+& |decl_head| \alt|binop| |colon| |exp| \altt|comma| |semi| |rpar| & + |decl_head| \altt|comma| |semi| |rpar| \hfill + $D=D$\alt $B$ $C$ \unskip$E$ & \maltt initialization {fields or} + {default argument} \cr +\+& |decl_head| |cast| & |decl_head| & |int f(int)|\cr +\+\dag\theprodno& |decl_head| \altt|int_like| |lbrace| |decl| & |fn_decl| + \altt|int_like| |lbrace| |decl| \hfill $F=D\,|in|\,|in|$ + & |long time () {|\cr +\+& |decl_head| |semi| & |decl| & |int n;|\cr +\+& |decl| |decl| & |decl| \hfill $D\,|force|\,D$ & |int n;double x;|\cr +\+& |decl| \alt|stmt| |function| & \alt|stmt| |function| + \hfill $D\,|big_force|\,$\alt $S$ $F$ \unskip& \&{extern} $n$; + \\{main} ()|{}|\cr +\+\dag\theprodno& |typedef_like| |decl_head| \alt|exp| |int_like| & + |typedef_like| |decl_head| \hfill $D=D$\alt $E^{**}$ $I^{**}$ \unskip & + \&{typedef} \&{char} \&{ch};\cr +\+& |typedef_like| |decl_head| |semi| & |decl| \hfill $T\.\ D$ & + \&{typedef} \&{int} $\&x,\&y$;\cr +\+& |struct_like| |lbrace| & |struct_head| \hfill + $S\,\\{in}\,|force|\\{back}\,L$ & |struct {|\cr +\+& |struct_like| \alt|exp| |int_like| |semi| & |decl_head| + \hfill $S\.\ $\alt $E^{**}$ $I^{**}$ & \&{struct} \&{forward};\cr +\+& |struct_like| \alt|exp| |int_like| |lbrace| & |struct_head| \hfill + $S\.\ $\alt $E^{**}$ $I^{**}$ \unskip $\\{in}\,|force|\,\\{back}\,L$ & + \&{struct} \&{name\_info} $\{$\cr +\+& |struct_like| \alt|exp| |int_like| |colon| & + |struct_like| \alt|exp| |int_like| |base| & |class| \&C :\cr +\+\dag\theprodno& |struct_like| \alt|exp| |int_like| & |int_like| + \hfill $S\.\ $\alt$E$ $I$ & \&{struct} \&{name\_info} $z$;\cr +\+& |struct_head| \altt|decl| |stmt| |function| |rbrace| & |int_like|\hfill + $SD\,|force|\,\\{back}\,R\,\.\ |out|$ & + |struct {| declaration |}|\cr +\+& |struct_head| |rbrace| & |int_like|\hfill $S\.{\\,}R$ & |class C{}|\cr +\+& |fn_decl| |decl| & |fn_decl| \hfill $F\,|force|\,D$ + & $f(z)$ \&{double} $z$; \cr +\+& |fn_decl| |stmt| & |function| \hfill $F\,|out|\,|out|\,|force|\,S$ + & \\{main}() {\dots}\cr +\+& |function| \altt|stmt| |decl| |function| & \altt |stmt| |decl| |function| + \hfill $F\,|big_force|\,$\altt $S$ $D$ $F$ & outer block\cr +\+& |lbrace| |rbrace| & |stmt| \hfill $L\.{\\,}R$ & empty statement\cr +\advance\midcol15pt +\+& |lbrace| \altt|stmt| |decl| |function| |rbrace| & |stmt| \hfill + $\matrix{\\{in}\,|force|\,\\{back}\,L\,|noop|\,|cancel|\,S\cr + |force|\,\\{back}\,R\,\\{out}\,|force|}$ & compound statement\cr +\+& |lbrace| |exp| [|comma|] |rbrace| & |exp| & initializer\cr +\+& |if_like| |exp| & |if_clause| \hfill $I\.{\ }E$ & |if (z)|\cr +\+& |for_like| |exp| & |else_like| \hfill $F\.{\ }E$ & |while (1)|\cr +\+& |else_like| |lbrace| & |else_head| |lbrace| & \&{else} $\{$\cr +\+& |else_like| |stmt| & |stmt| \hfill + $|force|\,E\,\\{in}\,|force|\,S\,\\{out}\,|force|$ & |else x=0;|\cr +\+& |else_head| \alt|stmt| |exp| & |stmt| \hfill + $|force|\,E\,\\{bsp}\,S\,\\{force}$ & |else{x=0;}|\cr +\+& |if_clause| |lbrace| & |if_head| |lbrace| & |if (x) {|\cr +\+& |if_clause| |stmt| |else_like| |if_like| & |if_like| \hfill + $|force|\,I\,\\{in}\,|force|\,S\,\\{out}\,|force|\,E\,\.\ I$ & + |if (x) y; else if|\cr +\+& |if_clause| |stmt| |else_like| & |else_like| \hfill + $|force|\,I\,\\{in}\,|force|\,S\,\\{out}\,|force|\,E$ & + |if (x) y; else|\cr +\+& |if_clause| |stmt| & |else_like| |stmt| & |if (x)|\cr +\+& |if_head| \alt|stmt| |exp| |else_like| |if_like| & |if_like| \hfill + $|force|\,I\,\\{bsp}\,S\,|force|\,E\,\.\ I$ & + |if (x){y;}else if|\cr +\+& |if_head| \alt|stmt| |exp| |else_like| & |else_like| \hfill + $|force|\,I\,\\{bsp}\,S\,|force|\,E$ & + |if (x){y;}else|\cr +\+& |if_head| \alt|stmt| |exp| & |else_head| \alt|stmt| |exp| & |if (x){y;}|\cr +\+& |do_like| |stmt| |else_like| |semi| & |stmt| \hfill + $\matrix{|force|\,D\,\\{in}\,|force|\,S\,|cancel|\,|noop|\cr + \,\\{bsp}\,ES\,\\{out}\,|force|}$ & |do f(x); while (g(x));|\cr +\+& |case_like| |semi| & |stmt| & |return;|\cr +\+& |case_like| |colon| & |tag| & |default:|\cr +\+& |case_like| |exp| |semi| & |stmt| \hfill $C\.\ ES$ & |return 0;|\cr +\+& |case_like| |exp| |colon| & |tag| \hfill $C\.\ EC$ & |case 0:|\cr +\+& |tag| |tag| & |tag| \hfill $T_1\,|force|\,T_2$ & |case 0: case 1:|\cr +\advance\midcol20pt +\+& |tag| \altt|stmt| |decl| |function| \alt|rbrace| |tag| + & \altt|stmt| |decl| |function| \alt|rbrace| |tag| + \hfill $|force|\,T\,\\{in}\,|force|\,S\,\\{out}$ & |case 0: z=0;|\cr +\advance\midcol-20pt +\+\dag\theprodno& |stmt| \altt|stmt| |decl| |function| & + \altt|stmt| |decl| |function| + \hfill $S\,$\altt$|force|\,S$ $|big_force|\,D$ $|big_force|\,F$ & + |x=1;y=2;|\cr +\+& |semi| & |stmt| \hfill \.\ $S$& empty statement\cr +\+\dag\theprodno& |lproc| \altt |if_like| |else_like| |define_like| & |lproc| & + \maltt {{\bf \#include}} {\bf\#else} {\bf\#define} \cr +\+& |lproc| |rproc| & |insert| & {\bf\#endif} \cr +\+\dag\theprodno& |lproc| \altt|exp| |function| |int_like| |rproc| & + |insert| \hfill + $I$\.\ \altt {$E$} {$F$} {$I$} & {\bf\#define} $a$\enspace 1 \cr +\+& |section_scrap| |semi| & |stmt|\hfill $MS$ |force| + &$\langle\,$section name$\,\rangle$;\cr +\+& |section_scrap| & |exp| &$\langle\,$section name$\,\rangle$\cr +\+& |insert| |any| & |any| & \.{\v\#include\v}\cr +\+& |prelangle| & |binop| \hfill $<$ & $<$ not in template\cr +\+& |prerangle| & |binop| \hfill $>$ & $>$ not in template\cr +\+& |langle| |exp| |prerangle| & |cast| & $\langle\,0\,\rangle$\cr +\+& |langle| |prerangle| & |cast| \hfill $L\.{\\,}P$ & $\langle\,\rangle$\cr +\+& |langle| \alt|decl_head| |int_like| |prerangle| & |cast| & + $\langle\&{class}\,\&C\rangle$\cr +\+& |langle| \alt|decl_head| |int_like| |comma| & |langle| \hfill + L\,\alt $D$ $I$ C\,|opt|9 & $\langle\&{class}\,\&C,$\cr +\+& |public_like| |colon| & |tag| & \&{private}:\cr +\+& |public_like| & |int_like| & \&{private}\cr +\+& |colcol| \alt|exp| |int_like| & \alt|exp| |int_like| & |::x|\cr +\+\dag\theprodno& + |new_like| \alt|exp| |raw_int| & |new_like| \hfill $N\.\ E$ & |new(1)|\cr +\+& |new_like| \alt|raw_unorbin| |colcol| & |new_like| & |new ::*|\cr +\+& |new_like| |cast| & |exp| & |new(*)|\cr +\+\dag\theprodno& |new_like| & |exp| & |new|\cr +\+\dag\theprodno& |operator_like| \altq|binop| |unop| |unorbinop| |raw_unorbin| + & |exp| \hfill $O$\.\{\altq $B$ $U$ $U$ $R$ \unskip \.\} & |operator+|\cr +\+& |operator_like| \alt|new_like| |sizeof_like| & |exp| \hfill $O\.\ N$ + & |operator delete|\cr +\+& |operator_like| & |new_like| & conversion operator\cr +\+& |catch_like| \alt|cast| |exp| & |fn_decl| \hfill $CE\,\\{in}\,\\{in}$ & + |catch (...)|\cr +\+& |base| |public_like| |exp| |comma| & |base| \hfill $BP\.\ EC$ & + : \&{public} $a$,\cr +\+& |base| |public_like| |exp| & |base| |int_like| \hfill $I=P\.\ E$ & + : \&{public} $a$\cr +\+& |raw_rpar| |const_like| & |raw_rpar| \hfill $R\.\ C$ & ) \&{const};\cr +\+& |raw_rpar| & |rpar| & );\cr +\+& |raw_unorbin| |const_like| & |raw_unorbin| \hfill $RC$\.{\\\ } + & $*$\&{const} |x|\cr +\+& |raw_unorbin| & |unorbinop| & $*$ |x|\cr +\+& |const_like| & |int_like| & \&{const} |x|\cr +\+& |raw_int| |lpar| & |exp| |lpar| & \&{complex}$(x,y)$\cr +\+& |raw_int| & |int_like| & \&{complex} |z|\cr +\+& |begin_arg| |end_arg| & |exp| & \.{@@[}\&{char}$*$\.{@@]}\cr +\+& |any_other| |end_arg| & |end_arg| & \&{char}$*$\.{@@]}\cr +\+& |label| |label| & |label| \hfill $L_1\,|force|\,L_2$ & cf. tags\cr +\+& |label| \altt|stmt| |decl| |function| & \altt|stmt| |decl| |function| + \hfill $\matrix{|cancel|\,|noop|\,\\{out}\,\\{out}\,|force|\cr + L\,\\{in}\,\\{in}\,|force|\,S}$ & cf. tags\cr +\+& |do_like| |lbrace| & |do_head| |lbrace| & \&{do} $\{$ \cr +\+& |do_head| |stmt| |else_like| |semi| & |stmt| \hfill + $D\,\\{bsp}\,S\,|cancel|\,|noop|\,\\{bsp}\,ES$ & |do {x;} while(y);|\cr +\+\dag\theprodno& |typedef_like| |exp| & |typedef_like| |raw_int| & + \&{typedef} \&{?} \&{type} \cr +\+& |int_like| |binop| |exp| & |int_like| & ? (\CPLUSPLUS/)\cr +\+& |operator_like| \altt|raw_int| |int_like| |const_like| & + |operator_like| \hfill $O$\.{\ }$R$ & \&{operator} \&{name\_info} \cr +\+\dag\theprodno& |lproc| \altt|exp| |int_like| |const_like| |pp_space| & + |lproc| \hfill $I\.\ E\.{\ \\5}$ & {\bf\#define} $a$ $\ldots$ \cr +\+& |lproc| |exp| |exp| |pp_space| & |lproc| \hfill $I\.\ E_1 E_2\.{\ \\5}$ + & {\bf\#define} $a(b)$ $\ldots$\cr +\yskip +\yskip +\yskip +\parindent=0pt +\dag{\bf Notes} +\yskip +Rule 35: The |exp| must not be immediately followed by |lpar| or~|exp|. + +Rule 38: The |int_like| must not be immediately followed by |colcol|. + +Rule 42: The |exp| must not be immediately followed by |lpar| or~|exp|. + +Rule 48: The |exp| or |int_like| must not be immediately followed by |base|, +|raw_int|, |int_like|, or |const_like|. + +Rule 76: The |force| in the |stmt| line becomes \\{bsp} if \.{CWEAVE} has +been invoked with the \.{-f} option. + +Rule 78: The |define_like| case calls |make_underlined| on the following scrap. + +Rule 80: Makes the scrap following |lproc| reserved without changing the +category. + +Rule 93: The |raw_int| must not be immediately followed by +|prelangle| or |langle|. + +Rule 96: The |new_like| must not be immediately followed by |lpar|, +|raw_int|, or |struct_like|. + +Rule 97: The operator after |operator_like| +must not be immediately followed by a |binop|. + +Rule 116: This rule only holds outside of {\bf\#define} lines; |exp| must not +be followed by a |lpar|. + +Rule 119: Makes the scrap following |lproc| reserved using the category +of the scrap following |pp_space| if this scrap is |int_like|, |const_like|, +or |raw_int|. + +\endgroup + + +@q end of prod-alt.w @> diff --git a/web/c_cpp/c2cweb/example.c b/web/c_cpp/c2cweb/example.c new file mode 100644 index 0000000000..5d3529fa33 --- /dev/null +++ b/web/c_cpp/c2cweb/example.c @@ -0,0 +1,98 @@ +/* This file is part of the c2cweb package Version 1.5 */ +/* written by Werner Lemberg (a7971428@unet.univie.ac.at) 10-Nov-1996 */ + +/* Grá Gott! */ + +/* this is an artificial example C-file to demonstrate how c2cweb works. Please + note the insertions of @ and @@ `commands' into the C code. You should try + the various switches to see how they influence the output */ + +/* Say + + c2cweb [options] example.h example.c + + to process this example. */ + + +#include <stdio.h> +#include <stdlib.h> +#include "example.h" + +/*@*/ + +#define MAX_ARRAY 123 +#define test_function(a, b, c) (a+b+c) + +int dummy1; /* two dummy variables */ +float dummy2; + +/*@*/ + +/* Two stupid functions */ + +#ifdef HELLO +void say_hello(void); +#else +void say_goodbye(void); +#endif + +/*@@*/ + + +void main(void) + {int i=10000; + struct Test test; + + while(i--) + test.array[i]=i; + + do_nothing(1000); + +#ifdef HELLO + say_hello(); +#else + say_goodbye(); +#endif + } + +/*@*/ + +union { + float another_dummy1; + int another_dummy2; +} AnotherTest; + +/*@*/ + +#ifdef HELLO +void say_hello(void) + {printf("\nHello\n"); + +// look at the unbalanced braces! +// without insertion of /*}*/ right here c2cweb and CWEAVE would be confused +/*}*/ +#else +void say_goodbye(void) + {printf("\nGoodbye!\n"); +#endif + } + +/*@*/ + +// the following piece of code can often be found in source files written for +// multiple compilers. Before the |#else| statement a /*{}*/ command is +// inserted + +#ifdef __STDC__ +void do_nothing(int count) +/*{}*/ +#else +void do_nothing(count) + int count; +#endif + {while(count--) + ; + } + + +/* end of example.c */ diff --git a/web/c_cpp/c2cweb/example.h b/web/c_cpp/c2cweb/example.h new file mode 100644 index 0000000000..8ab55c02a3 --- /dev/null +++ b/web/c_cpp/c2cweb/example.h @@ -0,0 +1,20 @@ +/* This file is part of the c2cweb package Version 1.5 */ +/* written by Werner Lemberg (a7971428@unet.univie.ac.at) 10-Nov-1996 */ + +// Grá Gott! +// Hello! + +/* this is an example header file to demonstrate how c2cweb works. */ + +/* Say + + c2cweb [options] example.h example.c + + to process this example. */ + + +struct Test {char *pointer; + int array[MAX_ARRAY];}; /* a dummy structure */ + + +/* end of example.h */ diff --git a/web/c_cpp/c2cweb/history.txt b/web/c_cpp/c2cweb/history.txt new file mode 100644 index 0000000000..0fc04f18d5 --- /dev/null +++ b/web/c_cpp/c2cweb/history.txt @@ -0,0 +1,122 @@ +This file is part of the c2cweb package 1.5 +written by Werner Lemberg (a7971428@unet.univie.ac.at) 10-Nov-1996 + + + +History of the c2cweb package +============================= + + +Version 1.0 : +1-Mar-1994 only two persons got it :-) + +Version 1.1 : +8-Mar-1994 initial release + + new: + $ in identifiers are now allowed (CWEAVE) + + whitespaces before preprocessor commands are now allowed + (CWEAVE) + + the concatenation operator ## added (CWEAVE) + + option -1 added (one-sided output) (c2cweb) + + minor changes affecting documentation only + + errors: + Ver 1.0 erroneously reported `@' outside of a string + (c2cweb) + + /*}*/ started a new code section at function block level + if in /*@*/-mode (c2cweb) + + tabs were not handled correctly (c2cweb) + +Version 1.2: +13-Apr-1994 new: + c2cweb and cweave should now compile smoothly under UNIX + (tested with Linux) + + paper format definition moved from c2cweb to compiler.w + + some files renamed; makefile for c2cweb added + errors: + A serious bug introduced in Ver. 1.1 caused whitespaces + be swallowed (c2cweb) + +Version 1.3: +20-June-1994: new: + Makefiles enhanced + + new patchlevel of emx.exe + + c2cweb.dvi added + + c2cweb.txt updated + errors: + '"' couldn't be used; corrected (c2cweb) + + sometimes the macro `\e' was concatenated with the next + word; '\e ' now written (c2cweb) + + wrong index entry for dollar sign corrected (CWEAVE) + + bug in original CWEAVE corrected: + you couldn't write e.g. @<Cases for |'\''|@> + +Version 1.4: +20-Aug-1994: new: + based now on CWEB 3.2 (July 1994) + + original CWEB source files for CWEAVE added + + additional cwebmac.tex style; original cwebmac.tex + renamed in cwebmac.ori + + c2cweb.txt has now an installation section + + C-code example modified + errors: + \ at the end of the line in multi-line strings will now + be treated correctly (c2cweb) + + from 1.3 : '\e ' is of course wrong. It must be '\e{}' + (and sometimes '\e{}%') (c2cweb) + + //-comment handling was buggy (c2cweb) + + braces in comments could cause an error (c2cweb) + + email address changed (same host; old address became + unreachable) + +Version 1.5: +10-Nov-1996: new: + now based on CWEB 3.4g (June 1996) + + option -f (to include a format file) added + + new command /*{}*/ added + + Makefiles enhanced (a compiling run under djgpp is + likely to fail since it's untested. Additionally + wildcard expansion is missing---any volunteers to add + it are welcome) + + c2cweb.w more structured + + new email address + changed: + runtime binaries has been removed. You should get the + emxrsx.zip package of the emTeX distribution instead. + + minor optical corrections. + +Version 1.5.1: +28-Jun-1997: errors: + a `char' variable corrected to `int'. + + +--- end of history.txt --- diff --git a/web/c_cpp/cnoweb/README b/web/c_cpp/cnoweb/README new file mode 100644 index 0000000000..d78ceb03be --- /dev/null +++ b/web/c_cpp/cnoweb/README @@ -0,0 +1,4 @@ +This is the cnoweb `literate programming' +demonstration program (pf.c) and macro package (cnoweb.tex). + +Comments to Jim Fox, fox@cac.washington.edu diff --git a/web/c_cpp/cnoweb/cnoweb.tex b/web/c_cpp/cnoweb/cnoweb.tex new file mode 100644 index 0000000000..c9de58413e --- /dev/null +++ b/web/c_cpp/cnoweb/cnoweb.tex @@ -0,0 +1,511 @@ +% c-NO_WEB (a c program pretty printer) +% by Jim Fox, February 1987 +% version 1.1, August 10, 1989 +% version 1.2, October 30, 1989 +% fixed spacing after ?, :, etc. +% version 1.3, June 28, 1990 +% added OOdent and IIdent, added \|typewriter| +% version 1.4, January 4, 1991 +% allow /* and */ in strings +% version 1.5, December 11, 1991 +% page offset shift for two-sided page print alignment +% nicer setting of too-long code lines +% add \listfile and \includefile macros +% version 1.6, March 17, 1992 +% Restore plain-tex meaning of right quote in math +% +%------------------------------------------------------------------ +% +% The TeX input file is a c source program. It is assumed to have +% this structure: +% +% /* comments ignored by c-no-web +% \input cnoweb +% \title{A one line title of the program (optional) } +% \synopsis{Synopsis of the program (optional) } +% \section{Name of the first section} +% rest of comments */ +% +% The rest of the file is normal c code. +% Within the title a \\ may be used to separate lines. +% +% Each major section begins with +% /* \section{name of the section} .... */ +% The section names are printed boldface, marked for reference in +% the page headline, and written to the table of contents. +% +% Each minor section begins with +% /* \subsection{name of the subsection} .... */ +% The subsection names are written to the table of contents. +% +% A new page can be forced by +% /* \newpage */ +% +% Code and other text containing special characters may be +% typed in text mode if it is bracked by *< ... >* +% e.g. /* *<inactive c-code>* */ +% but this may not be used in a macro argument. +% +% You can print in bold by enclosing text in double quotes: \"text". +% You can print in typewriter by enclosing text in vertical bars: \|text|. +% You can print in italics by enclosing text in single quotes: \'text'. +% +% You can include a verbatim listing of a file with \listfile, e.g., +% ... \listfile{program.h} ... +% You can include a c file to be formatted with \includefile, e.g., +% ... \includefile{program.h} ... +% +% The file must end with \endc in a comment. e.g., +% ... last c code statement ... +% /* \endc */ +% +%----------------------------------------------------------------- +% +% Make sure macros loaded only once +% +\expandafter\ifx\csname cnoweb loaded\endcsname\relax + \expandafter\let\csname cnoweb loaded\endcsname* + \else\endinput\fi +% +\catcode`\@=11 +% +\message{[c-no-web vers 1.6]} +% +% +% page parameters -- most of these can be changed without harm +% The default setup is for 12 point type on an 8.5 x 11 inch page. +% Two side printing is assumed, 1-inch margins on the inside. +% +\vsize=55pc +\hsize=37pc +\topskip=2pc +\parindent=0pt +\parskip=6pt +\overfullrule=0pt +\newdimen\oddsideshift \oddsideshift=0pc % adjustment to odd, left margins +\newdimen\evensideshift \evensideshift=2pc % adjustment to even, left margins +\newdimen\rightmarginslop \rightmarginslop=3pc % right margin slop in code +\newdimen\blockindent \blockindent=18pt % '{' block indentation +\newdimen\longblockindent \longblockindent=30pt % '(' indentation +\newskip\sectionskip \sectionskip2pc +% +% fonts +% +\font\titles=cmr10 scaled\magstep3 % for titles +% +% normal commentary size is 12 point +% +\font\twelverm=cmr12 % roman text +\font\twelvei=cmmi12 % math italic +\font\twelvesy=cmsy10 scaled\magstep1 % math symbols +\font\twelvebf=cmbx12 % boldface extended +\font\twelvett=cmtt12 % typewriter +\font\twelvesl=cmsl12 % slanted roman +\font\twelveit=cmti12 % text italic +\font\twelveex=cmex10 scaled\magstep1 % math symbols +% +\font\ninerm=cmr9 % roman text +\font\ninei=cmmi9 % math italic +\font\ninesy=cmsy9 % math symbols +% +\def\normaltype{% + \def\rm{\fam0\twelverm}% + \textfont0=\twelverm \scriptfont0=\ninerm \scriptscriptfont0=\sevenrm + \textfont1=\twelvei \scriptfont1=\ninei \scriptscriptfont1=\seveni + \textfont2=\twelvesy \scriptfont2=\ninesy \scriptscriptfont2=\sevensy + \textfont3=\twelveex \scriptfont3=\twelveex \scriptscriptfont3=\twelveex + \textfont\itfam=\twelveit \def\it{\fam\itfam\twelveit}% + \textfont\slfam=\twelvesl \def\sl{\fam\slfam\twelvesl}% + \textfont\bffam=\twelvebf \def\bf{\fam\bffam\twelvebf}% + \textfont\ttfam=\twelvett \def\tt{\fam\ttfam\twelvett}% + \normalbaselineskip 14\p@ + \setbox\strutbox=\hbox{\vrule height10\p@ depth4\p@ + width0\p@}% + \normalbaselines\rm} +\normaltype +% +% normal code size is 11 point +% +\font\elevenrm=cmr10 scaled\magstephalf % roman text +\font\eleveni=cmmi10 scaled\magstephalf % math italic +\font\elevensy=cmsy10 scaled\magstephalf % math symbols +\font\elevenbf=cmbx10 scaled\magstephalf % boldface extended +\font\eleventt=cmtt10 scaled\magstephalf % typewriter +\font\elevensl=cmsl10 scaled\magstephalf % slanted roman +\font\elevenit=cmti10 scaled\magstephalf % text italic +% +\def\codetype{% + \def\rm{\fam0\elevenrm}% + \textfont0=\elevenrm \scriptfont0=\sevenrm \scriptscriptfont0=\fiverm + \textfont1=\eleveni \scriptfont1=\seveni \scriptscriptfont1=\fivei + \textfont2=\elevensy \scriptfont2=\sevensy \scriptscriptfont2=\fivesy + \textfont3=\tenex \scriptfont3=\tenex \scriptscriptfont3=\tenex + \textfont\itfam=\elevenit \def\it{\fam\itfam\elevenit}% + \textfont\slfam=\elevensl \def\sl{\fam\slfam\elevensl}% + \textfont\bffam=\elevenbf \def\bf{\fam\bffam\elevenbf}% + \textfont\ttfam=\eleventt \def\tt{\fam\ttfam\eleventt}% + \normalbaselineskip 13\p@ + \setbox\strutbox=\hbox{\vrule height9.5\p@ depth3.5\p@ width0\p@}% + \normalbaselines\rm} +% +% restricted code size is 10 point +% +\def\rcodetype{% + \def\rm{\fam0\tenrm}% + \textfont0=\tenrm \scriptfont0=\sevenrm \scriptscriptfont0=\fiverm + \textfont1=\teni \scriptfont1=\seveni \scriptscriptfont1=\fivei + \textfont2=\tensy \scriptfont2=\sevensy \scriptscriptfont2=\fivesy + \textfont3=\tenex \scriptfont3=\tenex \scriptscriptfont3=\tenex + \textfont\itfam=\tenit \def\it{\fam\itfam\tenit}% + \textfont\slfam=\tensl \def\sl{\fam\slfam\tensl}% + \textfont\bffam=\tenbf \def\bf{\fam\bffam\tenbf}% + \textfont\ttfam=\tentt \def\tt{\fam\ttfam\tentt}% + \normalbaselineskip 13\p@ + \setbox\strutbox=\hbox{\vrule height9.5\p@ depth3.5\p@ width0\p@}% + \normalbaselines\rm} +% +% try using 11pt for text also +% +\let\normaltype\codetype +\let\smalltype\rcodetype +\normaltype +% +% allow underscore as char in non-math text +\def\oldun{_} +\def\specialunderscore{\ifmmode_\else{\tt\char"5F}\fi} +\catcode`\_=\active +\let_\specialunderscore +% +\def\\{{\tt\char"5C}} % for \ in comments +\def\section#1{\mark{#1}% + {\let\folio=\relax\let_=\oldun \edef\\{\write\cfile{\endgraf + \tocnormaltype#1\cdots\cpageno\endgraf}}\\}% + {\bf#1.\ }} +\def\subsection#1{% + {\let\folio=\relax\let_=\oldun \edef\\{\write\cfile{\endgraf + \toccodetype\hskip12pt#1\cdots\cpageno\endgraf}}\\}% + {#1.\ }} +\def\subsubsection#1{% + {\let\folio=\relax\let_=\oldun \edef\\{\write\cfile{\endgraf + \tocrcodetype\hskip18pt#1\cdots\cpageno\endgraf}}\\}% + {#1.\ }} +% +% +% headline contains (title, page number, section) +% +\def\makeheadline{\vbox to0pt{\vskip-30pt + \ifnum\pageno=-1\else\line{\strut\normaltype + \ifodd\pageno\firstmark\hfill\bf\the\job\rlap{\ -- \folio}% + \else\bf\llap{\folio\ -- }\the\job\rm\hfill\firstmark\fi}\fi + \vss}\nointerlineskip} +\def\makefootline{\line{\tenrm\vrule height30pt depth0pt width0pt + \ifodd\pageno\today\ @\rightnow\ by \cnoweb\hfill + \else\hfill\today\ @\rightnow\ by \cnoweb\fi}} +% +\def\plainoutput{\shipout\vbox{% + \ifodd\pageno\moveright\oddsideshift\else\moveright\evensideshift\fi + \vbox{\makeheadline\pagebody\makefootline}}% + \advancepageno + \ifnum\outputpenalty>-\@MM \else\dosupereject\fi} + +\let\oldpagebody\pagebody +\def\toprules{\vrule height4pt width.4pt\kern-.4pt\hskip\blockindent} +\def\pagebody{\ifnum\pageno>0\vbox to0pt{\hrule height.3pt width\hsize + \leftline{\leaders\hbox{\toprules}}% + \vss}\nointerlineskip + \hbox{\llap{\vrule height\vsize width.3pt\hskip3pt}% + \oldpagebody}% + \else\oldpagebody\fi} + +% +% table of contents +% +\newwrite\cfile +\immediate\openout\cfile=\jobname.toc +\def\cdots{\nobreak\leaders\hbox to1pc{\hss.\hss} + \hskip24pt plus1fill} +\def\cpageno{\rlap{\hbox to18pt{\hfill\folio}}} +% +% these set the type size in toc entries +% +\let\tocnormaltype\relax % de-activate +\let\toccodetype\relax +\let\tocrcodetype\relax +% +% +%------------------------------------------------------------------- +% +% +% All comment fields are formatted normally (plain TeX style). +% All c code is scanned and formatted verbatim. +% +% '/*' triggers TeX mode, '*/' triggers verbatim mode +% +\let\star=* % a catcode 11 star +\let\slash=/ % a catcode 11 slash +\let\rarrow=> % a catcode 11 right arrow +% +% --------------------------------------------------------------- +% +% In TeX mode the * is active and looks for */ or *< +% \setex sets TeX mode +% +\newif\ifrestricted\restrictedfalse +\def\setex{% begins TeX mode + \ifrestricted\rcodetype + \else\ifvmode\normaltype\filbreak\else\codetype\fi\fi + \catcode`\*=\active + {\tt/* }\ignorespaces} +% +{\catcode`\*=\active + \gdef*{\futurelet\next\checkslash} + } +\def\checkslash{% + \ifx\next/\ifrestricted\let\next\resetrcc\else\let\next\setcc\fi + \else\ifx\next<\let\next\setrcc + \else\let\next\star\fi\fi\next} +% +% +% In code mode / is active and looks for /* +% \setcc sets the active code mode +% +\def\setcc/{\@setcc} +\def\@setcc{% set code mode + \begingroup\ccverbatim\catcode`\/=\active + */ + \parskip=0pt} +% +{\catcode`\/=\active + \gdef/{\futurelet\next\checkstar} + } +\def\checkstar{\ifx\next*\let\next\endcc + \else\let\next\slash\fi\next} +\def\endcc*{\@endcc} +\def\@endcc{\endgroup\ifvmode\filbreak\fi\setex} +% +% +% In restricted code mode > is active and looks for >* +% \setrcc sets the restricted code mode +% +\def\setrcc<{% set little code mode + \begingroup\restrictedtrue + \begingroup\ccverbatim\rccverbatim\tt\catcode`\>=\active + \catcode`\/=\active + \parskip=0pt} +% +\def\resetrcc/{% reset little code mode + \begingroup\ccverbatim\rccverbatim\tt\catcode`\>=\active + \catcode`\/=\active + */ + \parskip=0pt} +% +{\catcode`\>=\active + \gdef>{\futurelet\next\checkrstar} + } +\def\checkrstar{\ifx\next*\let\next\endrcc + \else\let\next\rarrow\fi\next} +\def\endrcc*{\endgroup\endgroup} +% +% +% --------------------------------------------------------------- +% +% \leftskip is controlled by the presence of '{', '}', +% '(', and ')' in the code text. +% +% \Ident increases \leftskip and takes effect after the current line +% \Odent decreases \leftskip and takes effect on the current line +% \IIdent and \OOdent do the same but use the long indentation +% +\newdimen\leftadjust % leftskip adjustment at next par +\def\Ident{\global\advance\leftadjust\blockindent} +\def\Odent{\global\advance\leftadjust-\blockindent\doOdent} +\def\IIdent{\global\advance\leftadjust\longblockindent} +\def\OOdent{\global\advance\leftadjust-\longblockindent\doOdent} +\def\doOdent{% + \ifvmode\ifdim\leftadjust<\z@\global\advance\leftskip\leftadjust + \global\leftadjust0pt\fi\fi} +% +\def\codepar{% this is \par in code mode + \ifvmode\bigskip % this way blank lines are ignored at page tops + \else\endgraf + \ifdim\leftadjust=\z@\else\global\advance\leftskip\leftadjust + \global\leftadjust0pt\fi\fi} +% +% --------------------------------------------------------------- +% +% Here are the catcodes and definitions for c-code verbatim +% +\chardef\other=12 +\def\ccverbatim{% set all chars to type `other' or `active' + \catcode`\\=\other + \catcode`\{=\active + \catcode`\}=\active + \catcode`\(=\active + \catcode`\)=\active + \catcode`\$=\other + \catcode`\*=\other + \catcode`\&=\other + \catcode`\#=\active + \catcode`\%=\other + \catcode`\~=\other + \catcode`\_=\other + \catcode`\^=\other + \catcode`\|=\other + \catcode`\`=\active + \catcode`\'=\active + \catcode`\"=\active + \let\par\codepar + \obeyspaces \obeylines \frenchspacing \codetype + \pretolerance10000 % don't hyphenate variable names, etc. + \everypar{\hangindent\longblockindent\hangafter1}% indent wrapped + \rightskip0pt minus\rightmarginslop % allow some extension + \tt} +\def\rccverbatim{% additional \other chars for restricted mode + \catcode`\{=\other + \catcode`\}=\other + \catcode`\(=\other + \catcode`\)=\other + \catcode`\#=\other + \catcode`\'=\other + \catcode`\"=\other + \rcodetype \tt} +% +% And here are the definitions for the c-code active characters. +% These perform the little formatting we do on c-code. +% {, }, (, ), and # adjust the indentation +% `, ', ", and / trigger more complete verbatim +% +% Note that the right quote already has a plain-tex definition +% +{ \catcode`\<=1\catcode`\>=2 + \catcode`\{=\active \gdef{<<\Ident\char`\{>> + \catcode`\}=\active \gdef}<<\Odent\char`\}>> + \catcode`\(=\active \gdef(<<\IIdent\char`\(>> + \catcode`\)=\active \gdef)<<\OOdent\char`\)>> + \catcode`\|=0 + \catcode`\\=\active + |catcode`|"=|active + |gdef"<|char`|"|bgroup + |catcode`|{=|other |catcode`|}=|other + |catcode`|(=|other |catcode`|)=|other + |catcode`|'=|other |catcode`|/=|other + |catcode`|\=|active |let\|verbslash + |def"<<|char`|"|egroup>>> + |catcode`|'=|active + |global|let|plain@rquote' + |gdef|cnoweb@rquote<|char`|'|bgroup + |catcode`|{=|other |catcode`|}=|other + |catcode`|(=|other |catcode`|)=|other + |catcode`|"=|other |catcode`|/=|other + |catcode`|\=|active |let\|verbslash + |def'<<|char`|'|egroup>>> + |gdef'<|ifmmode|let|next|plain@rquote + |else|let|next|cnoweb@rquote|fi|next> + > +\def\verbslash{\begingroup\catcode`\'=\other\catcode`\"=\other\doverbslash} +\def\doverbslash#1{\\#1\endgroup} +{\catcode`\`=\active\gdef`{\relax\lq}} +{\catcode`\#=\active\gdef#{\ifvmode\ifdim\leftskip>\z@ + \noindent\llap{\hbox to\leftskip{\#\hss}}\ignorespaces + \else\#\fi\else\#\fi}} +% +% +% --------------------------------------------------------------- +% +% \listfile produces a verbatim listing of a file +% \includefile formats the file (assumed to be c-code) +% +{\obeyspaces\gdef\hardspaces{\global\let =\ }} +\def\listfile#1{\par\begingroup\leftskip\blockindent\parskip0pt + \ccverbatim\rccverbatim\hardspaces\input#1\par\endgroup} +\def\includefile#1{\@setcc\par\input#1\par\@endcc} +% +% --------------------------------------------------------------- +% +% \endc signals end of the c program +% +\def\endc{{\it end~\tt*/}\vfill\eject\dotitle\docontents\end} +% +% --------------------------------------------------------------- +% +% /* \newpage */ should give nice new page +\def\newpage{\vadjust{\vfill\eject}} +% +% some ebcdic systems get 05 for tabs +\catcode`\^^E=10 % make them spaces +% +% \"bold text" and \|typewriter text| and \'italic text' +% +\def\"#1"{{\bf#1}} +\def\|#1|{{\tt#1}} +\def\'#1'{{\it#1}} +% +% fix \item, etc. to work without \parindent +% +\newdimen\itemindent\itemindent2\blockindent +\def\hang{\hangindent\itemindent} +\def\textindent#1{\indent\hbox to\itemindent{\hss#1\enspace}\ignorespaces} +\def\item{\par\hang\textindent} +\def\textiindent#1{\indent\hbox to2\itemindent{\hss#1\enspace}\ignorespaces} +\def\itemitem{\par\indent\hangindent2\itemindent\textiindent} +\def\narrower{\advance\leftskip\itemindent + \advance\rightskip\itemindent} +% +% c-no-web name +% +\def\cnoweb{\hbox{c-\setbox0=\hbox{web}% + \vrule depth-.25\ht0 height.33\ht0 width\wd0\kern-\wd0\box0}} +% +% print the title and table of contents +% +\newtoks\job\job={\jobname} +\newtoks\title\title={} +\newtoks\synopsis\synopsis={} +\def\dotitle{ + \ifodd\pageno\else + \message{page alignment}Intentionally blank \vfill\eject\fi + \pageno=-1 + \vglue2pc + \centerline{\titles\the\job} + \vskip18pt + \centerline{\bf\the\title} + {\openup3pt\narrower\noindent\the\synopsis\par} + \vfill} +\def\docontents{% print table of contents + \mark{Contents} + \immediate\closeout\cfile + \begingroup + \catcode`\@=11 + \leftskip=18pt\rightskip=\leftskip + \everypar={\hang} + \rightline{Page} + \medskip + \let\tocnormaltype\normaltype % activate these type size macros + \let\toccodetype\codetype + \let\tocrcodetype\rcodetype + \input \jobname.toc + \catcode`\@=12 + \endgroup + \vfill + \eject} +\def\today{\ifcase\month\or + January\or February\or March\or April\or May\or June\or + July\or August\or September\or October\or November\or December\fi + \space\number\day, \number\year} +\def\today{\ifcase\month\or + Jan\or Feb\or Mar\or Apr\or May\or Jun\or + Jul\or Aug\or Sep\or Oct\or Nov\or Dec\fi + \space\number\day, \number\year} +\def\rightnow{\count0=\time\count1=\count0 \divide\count0by60 + \ifnum\count0<10 0\fi\number\count0:% hours + \multiply\count0by60 \advance\count1by-\count0 + \ifnum\count1<10 0\fi\number\count1% minutes + } +% +\catcode`\@=11 +% +{\hsize\maxdimen + \output={\setbox0=\box255}\eject} % delete all before \input cnoweb +\setex % begin with TeX mode + +% end of cnoweb.tex diff --git a/web/c_cpp/cnoweb/pf.c b/web/c_cpp/cnoweb/pf.c new file mode 100644 index 0000000000..5fb0af98de --- /dev/null +++ b/web/c_cpp/cnoweb/pf.c @@ -0,0 +1,371 @@ +/* + pf.c in c-no-web format. (8/10/89) + by Jim Fox, University of Washington + + \input cnoweb % load the c-no-web system +% +% \nweb ... \bewn = \cnoweb commentary + \def\nweb{\leavevmode\begingroup\codetype$\lbrack\!\lbrack\;$} + \def\bewn{$\;\rbrack\!\rbrack$\ \endgroup} +% + \title{Printing factorials: a demonstration of \cnoweb} + \synopsis{You are reading a program listing that was formatted + with \par\item{}{\tt \% tex pf.c}\par + The same program is compiled + with \par\item{}{\tt \% cc pf.c}\par + The key to this dual function source file is a \TeX\ macro + package called \cnoweb\ that treats all comments as `\TeX-text' + and all else as `verbatim-text.' The C compiler naturally + does the opposite and interprets only the text outside the comments. + + \nweb In this program comments about + \cnoweb\ itself look like this.\bewn} + \section{Introduction} + + This program prints factorials of positive integers. + It uses this definition of $n!$ + $$ n! = \prod_{i=1}^n i$$ + + {\narrower + \nweb \"pf.c" begins with a comment which + is treated as \TeX-text by \cnoweb. + + The first comment contains the + string *<\input cnoweb>*. This begins the \cnoweb\ listing. + Included also in this leading comment section is + *<\title{Printing ...}>*, + *<\synopsis{You are reading ...}>*, + and + *<\section{Introduction}>*.\bewn\par} + + The program is invoked with the command + \item{}\"pf $[$-l$]$ $[$-e$]$ $n$" + + where + \item{\bf -l} prints the factorials of all numbers + up to and including the limit $n$. + \item{\bf -e} includes an exponential notation approximation. + \item{$n$} is the number whose factorial will be printed. + */ + +#define GOODEXIT 0 /* exit with this if factorial OK */ +#define BADEXIT 1 /* exit with this if error */ + +#include <stdio.h> /* standard io stuff */ +#include <math.h> /* math stuff */ +char *malloc(); /* memory allocator */ + +/* \section{Big-integer routines} + + \"pf" would be trivial were it not for the rapid + growth of $n!$, which is much greater than exponential + and quickly surpasses even the real number capacity of most machines. + We therefore define a new data type (\"Int") to hold big integers. + */ + + typedef struct Int_ { + int radix; /* $2^1 \le \hbox{radix} \le 2^8$ */ + char *digits; /* the \"Int"'s digits */ + int length; /* number of digits available */ + int limit; /* number of digits in use */ + } Int; + +/* We do not need many operations on \"Int"s to compute factorials: + only `set' and `multiply'. + */ + +/* \subsection{\"Set(I,\ i)"} + Return \"I" initialized to \"i". */ + +Set(I,i) +Int *I; +int i; +{ + int d; + I->digits[0] = (i?1:0); + for (d=1; d<I->length; I->digits[d++]=0); + I->limit = 0; + if ((i!=0)&&(i!=1)) Product(I,i); + return (0); +} + +/* \subsection{\"Product(I,\ i)"} + Return \"I" multiplied by \"i". + Returns true if the product is OK. */ + +Product(I,i) +Int *I; +int i; +{ + int d,n; + int l = I->limit+1; + int r = I->radix; + int carry = 0; + + for (d=0; ((d<l)||(carry!=0)); d++) { + if (d>=I->length) return (0); /* not enough digits */ + n = I->digits[d] * i + carry; + if ((I->digits[d] = n % r)>0) I->limit = d; + carry = n / r; + } + return (1); +} + + + +/* \section{\"main()"} Print the factorial of the integer + found on the command line. + + \item{1.} Parse command line arguments. + \item{2.} Compute number of digits needed and allocate space. + \item{3.} Calculate the factorial. + \item{4.} Print the factorial. + + \nweb \cnoweb\ breaks pages only before comments.\hfill\break + You can control pagination with this knowledge.\bewn + + The higher the radix the fewer digits needed, but + we use an internal radix of 10 + to save a lot of division when printing the number. + Also, we already have a log$_{10}$ function. + + */ + +#define RADIX 10 + +Int nfactorial; /* place for the factorial */ +Int *nf = &nfactorial; + +/* command line parameters */ + + int limit_mode = 0; /* true if printing all factorials */ + int expon_mode = 0; /* true if printing exponential notation */ + +/* start of the program */ + +main(argc,argv) +int argc; +char *argv[]; +{ + int n; + int i; + + /* 1. Parse the command line */ + + n = parse_args(argc,argv); /* we will print $n!$ */ + + /* 2. Compute digits needed and allocate space */ + + nf->radix = RADIX; + nf->length = digits(n,RADIX); + + if ((nf->digits = malloc(nf->length))==NULL) { + printf("Sorry, cannot allocate space for %d digits\n", + nf->length); + exit (BADEXIT); + } + + /* 3. Calculate the factorial. */ + + Set(nf,1); /* initialize \"nf" */ + + for (i=1; i<=n; i++) { + if (Product(nf,i)==0) { + printf("Sorry, miscalculated the number of digits.\n"); + exit (BADEXIT); + } + if (limit_mode) print_Int(nf,i); + } + + /* 4. Print the result, if it hasn't been printed yet. */ + + if (!limit_mode) print_Int(nf,n); + exit (GOODEXIT); +} + +/* \subsection{\"parse_args(argc,argv)"} + + This is a standard UNIX idiom. + See Kernighan \& Ritchie. + + \nweb \cnoweb\ automatically indents after + {\tt\char`\{} and {\tt\char`\(}.\bewn */ + +parse_args(argc,argv) +int argc; +char *argv[]; +{ + int n = (-1); + + while (--argc > 0) { + argv++; + if (argv[0][0]=='-') { /* is an option flag */ + switch (argv[0][1]) { + case 'l': { + limit_mode = 1; + break; + } + case 'e': { + expon_mode = 1; + break; + } + default: show_usage(); + } + } else { /* is the number */ + sscanf(argv[0],"%d",&n); + } + } + + if (n<=0) show_usage(); + return (n); +} + +/* \subsection{\"digits(n,r)"} Returns number of digits in \"n" + using a radix of \"r". + + A factorial can be approximated by the Sterling formula + $$ n! \approx e^{-n} n^{n} \sqrt{2\pi n}$$ + What we want is the number of digits, which is + $$ \hbox{\# digits} \approx \log_r(n!)=\log(n!)/\log(r) \approx + (-n \log e + n \log n + {1\over2}\log (2\pi n))/\log(r)$$ + + We will add a couple of digits to this value + to allow for the approximations and assure that we + have enough. + */ + +#define PI 3.141592653589793238462643 /* $\pi$ */ +#define E 2.718281828459045235360287 /* $e$ */ + +digits(n,r) +int n; +int r; +{ + double dn = n; + int nd; + + nd = (int)((-dn * log10(E)) + + (dn * log10(dn)) + (.5 * log10(2*PI*dn)))/log10((double)r) + 2; + /* *<printf("requires %d digits\n",nd);>* */ + + /* \nweb The + `Commented out' code above was typed: + {\tt/{}* *{}<printf...;>{}* *{}/}.\bewn */ + + return (nd); +} + +/* \subsection{\"print_Int(I,\ i)"} + Print {\tt{\it value(}i{\it)} = {\it value(}I{\it)}}. + The internal radix that matches the display radix + clearly makes this procedure easier. + + This display uses the convention that separates each + three-digit set with commas. +*/ + +#define MAX_WIDTH 80 /* maximum width of the output */ + +print_Int(I,i) +Int *I; +int i; +{ + int d; + int lp,bp; + char line[MAX_WIDTH]; + + sprintf(line,"%d! = ",i); + bp = lp = strlen(line); + for (d=I->limit; d>=0; d--) { + line[lp++] = I->digits[d] + '0'; /* assumes radix $\le 10$ */ + if ((d%3)==0) { + line[lp++] = (d?',':'.'); + if ((d==0) || (lp>MAX_WIDTH-5)) { + line[lp] = '\0'; + printf("%s\n",line); + for (lp=0; lp<bp; line[lp++]=' '); + } + } + } + + /* If the exponential notation was desired, print it now. + */ + + if (expon_mode) { + int d = I->limit; /* the most significant digit */ + sprintf(line+bp,"approximately = %d.%d x 10e%d", + I->digits[d],d?I->digits[d-1]:0,d); + printf("%s\n",line); + } + return (0); +} + +/* \section{\"show_usage()"} invocation syntax error---show + correct usage */ + +int show_usage() +{ + printf("usage: pf [-l] [-e] positive_integer\n"); + exit (BADEXIT); +} + +/* \section{Summary of \cnoweb\ commands} + + These control sequences are defined in the \cnoweb\ macro package. + + \begingroup + \def\{{{\tt\char`\{}} + \def\}{{\tt\char`\}}} + \def\cs#1{\smallskip\noindent\hangindent7\blockindent\hangafter1 + \hbox to7\blockindent{\bf#1\qquad\hss}\ignorespaces} + + \cs{\\title\{ ... \}} Titles the program. + \cs{\\job\{ ... \}} Another title area. Defaults to input filename. + \cs{\\section\{ ... \}} Begins a section. The section title + is also included in the table of contents and + in the page header. + \cs{\\subsection\{ ... \}} Begins a subsection. The subsection title + is also included in the table of contents. + \cs{\\subsubsection\{ ... \}} Begins a subsubsection. + The subsubsection title + is also included in the table of contents. + \cs{\\newpage} Causes a page eject after the current line. This + is usually used in a comment by itself, e.g., + \hbox{\tt /{}* \\newpage *{}/}. + \cs{\\endc} Ends the \cnoweb\ listing. This + is usually the last line in the file, e.g., + \hbox{\tt /{}* \\endc *{}/}. + \cs{\\{\tt"} ... {\tt"}} Prints {\bf bold} text. + \cs{\\{\tt'} ... {\tt'}} Prints {\it italic} text. + \cs{\\{\tt|} ... {\tt|}} Prints {\tt typewriter} text. + \cs{*{\tt<} ... {\tt>}*} Allows C code to be included in comments. + You can nest comments within the `commented out' C code, e.g., + \hfill\break + {\tt/{}* comment out this section *{}<} \hfill\break + \hglue\blockindent{\tt i = 0; /{}* initialize \"i" *{}/} \hfill\break + \hglue\blockindent{\tt ...} \hfill\break + \hglue\blockindent{\tt >{}* end of the commented out section *{}/} + + \cs{\\item, \\hang, {\rm etc.}} Work as you hope they would. + \endgroup + + */ + +/* \section{How to obtain \cnoweb} + + You may obtain \cnoweb\ by anonymous ftp to + {\tt u.washington.edu}. + + It is in the directory: {\tt pub/tex/cnoweb} + + \bigskip + \rightline{--- Jim Fox, + University of Washington} + \rightline{\strut \tt fox@cac.washington.edu} + + \medskip + + The file ends with the comment + `{\tt /{}* \\endc *{}/}' */ + +/* \endc */ diff --git a/web/c_cpp/ctie/ChangeLog b/web/c_cpp/ctie/ChangeLog new file mode 100644 index 0000000000..1f78f20ebd --- /dev/null +++ b/web/c_cpp/ctie/ChangeLog @@ -0,0 +1,25 @@ +2003-04-20 Julian Gilbey <jdg@debian.org> + + * Remove documented need for any change files - they're not + actually necessary (thanks to Hartmut Henkel + <hartmut_henkel@gmx.de> for this observation) + + * Use kpathsea 3.4.5 modified functions, losing some backward + compatibility if the kpathsea library is used + + * Match current Web2C/Kpathsea modifications to cweb, and modify + to more closely match cweb 3.64 behaviour + + * Release as version 1.1 + +2002-02-03 Julian Gilbey <jdg@debian.org> + + * Make success message appear on a new line at end of run + + * Improve formatted output for ctie-k.ch + + * Release as version 1.0 + +2002-01-13 Julian Gilbey <jdg@debian.org> + + * Initial release (version 0.9) diff --git a/web/c_cpp/ctie/Makefile b/web/c_cpp/ctie/Makefile new file mode 100644 index 0000000000..8edc24225c --- /dev/null +++ b/web/c_cpp/ctie/Makefile @@ -0,0 +1,51 @@ +# +# Makefile for ctie distribution - 21 Dec 92 +# +# This file makes CTIE and needs ctangle from cweb distribution. +# + +CTANGLE=ctangle +BINDIR=. +CTIE=$(BINDIR)/ctie + +# directory for CWEB inputs in @i files +CWEBINPUTS= /usr/local/lib/cweb + +CFLAGS= +LDFLAGS= +KPATHSEAFLAGS=-lkpathsea + +default: ctie + + +# CTIE +ctie.o: ctie.c + $(CC) $(LDFLAGS) $(CFLAGS) -DCWEBINPUTS=\"$(CWEBINPUTS)\" -c ctie.c + +ctie: ctie.o + $(CC) $(LDFLAGS) $(CFLAGS) -o $(CTIE) ctie.o + +ctie.c: ctie.w + $(CTANGLE) ctie + +ctie-k.o: ctie-k.c + +ctie-k: ctie-k.o + $(CC) $(LDFLAGS) $(CFLAGS) -o $(BINDIR)/ctie-k ctie-k.o $(KPATHSEAFLAGS) + +ctie-k.c: ctie.w ctie-k.ch + ctangle ctie.w ctie-k.ch ctie-k.c + +clean: + rm -f ctie.o ctie-k.o ctie.outc ctie.outm ctie.tex + rm -f ctie.log ctie.toc ctie.scn ctie.idx + rm -f ctiedoc.aux ctiedoc.log + +veryclean: clean + rm -f $(BINDIR)/ctie $(BINDIR)/ctie-k ctie.dvi ctiedoc.dvi + +ttest: $(CTIE) + $(CTIE) -c ctie.outc ctie.tie ctie.cf? + diff ctie.outc ctie.chf.gen + $(CTIE) -m ctie.outm ctie.tie ctie.cf? + diff ctie.outm ctie.master.gen diff --git a/web/c_cpp/ctie/README b/web/c_cpp/ctie/README new file mode 100644 index 0000000000..48aab880ee --- /dev/null +++ b/web/c_cpp/ctie/README @@ -0,0 +1,75 @@ +This is the README file of the `CTIE' distribution, version 1.1 +--------------------------------------------------------------- + +CTIE is based on the `TIE' program, a program which was first +developed to allow WEB programmers to use more than one change file +with their WEB programs. + +CWEB introduced a new command, @i, to handle "include files". The +original TIE program was not designed to handle this situation, and so +could not be used for all CWEB programs. CTIE behaves essentially +identically to the original TIE, and identical results will be +generated when TIE is applied to traditional WEB files with changes, +as long as there are no errors. (Errors may lead to different output, +but then the errors need correcting, anyway.) + +CTIE can be used to either create a new version of the (C)WEB file +which incorporates the changes, or to merge multiple change files into +a single change file. This program does not depend in an essential +way on any special WEB dialect, nor on using WEB at all, as the only +controls to be interpreted are the change requests in the change files +(delimited by @x, @y and @z) and file include commands (indicated by @i). +This makes `CTIE' also usable for any other language, or even +data files. See the documentation ctiedoc.tex and the example file +ctie.tie for more information. + + +REQUIREMENTS: + +This version of `CTIE' is written in CWEB. To preprocess and compile +the source you will need a working `ctangle'. A pretangled ctie.c is +supplied if you do not have a working ctangle available. + +Please note, that the CWEB by Silvio Levy and Donald E. Knuth must +probably be at least at version 2.8, to process and compile `ctie' +without problems. + +Furthermore, a ctie-k.ch file is provided which links ctie against +Karl Berry's kpathsea library. A tangled version of this file is +provided as ctie-k.c. To use this, you will need to link against +version 3.4.5 or higher of the kpathsea library. This version +searches for files, both from the command line and included (@i) files +using the CWEBINPUTS search path. + + +DISTRIBUTION: + +This package has been uploaded to CTAN, and appears in the directory +web/c_cpp/ctie/. + + +LICENCE: + +This program is based on tie.w and common.w (part of CWEB). It is +distributed with the following licence. + +Copyright (c) 2002,2003 by Julian Gilbey +All rights reserved. + +This program is distributed WITHOUT ANY WARRANTY, express or implied. + +Permission is granted to make and distribute verbatim copies of this +program 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 +program under the conditions for verbatim copying, provided that the +entire resulting derived work is distributed under the terms of a +permission notice identical to this one. + + +BUGS/ENHANCEMENTS: + +If you detect bugs in the implementation of CTIE or have suggestions +to enhance functionality or improve portability, please send a message +to Julian Gilbey <jdg@debian.org>. diff --git a/web/c_cpp/ctie/ctie-k.c b/web/c_cpp/ctie/ctie-k.c new file mode 100644 index 0000000000..bfa74483d5 --- /dev/null +++ b/web/c_cpp/ctie/ctie-k.c @@ -0,0 +1,1166 @@ +#define version_number "1.1" +#define banner "This is CTIE, Version 1.1" +#define copyright \ +"Copyright 2002,2003 Julian Gilbey. All rights reserved. There is no warranty.\n\ +Run with the --version option for other important information." \ + \ + +#define kpse_find_cweb(name) kpse_find_file(name,kpse_cweb_format,true) \ + +#define buf_size 1024 +#define max_file_index 32 \ + +#define xisupper(c) (isupper(c) &&((unsigned char) c<0200) ) \ + \ + +#define spotless 0 +#define troublesome 1 +#define fatal 2 \ + +#define max_include_files 20 \ + +#define max_file_name_length 1024 \ + +#define too_long() {total_include_files--;free(new_inc) ; \ +err_print(i,"! Include file name too long") ;goto restart;} \ + +#define fatal_error(i,s,t) { \ +fprintf(stderr,"\n%s",s) ; \ +err_print(i,t) ; \ +history= fatal; \ +exit(wrap_up() ) ; \ +} \ + \ + +#define none (-1) \ + +/*2:*/ +#line 89 "./ctie.w" + +/*10:*/ +#line 172 "./ctie.w" + +#include <stdio.h> + + +#line 126 "./ctie-k.ch" +/*:10*//*39:*/ +#line 715 "./ctie.w" + +#include <errno.h> + + +/*:39*//*72:*/ +#line 454 "./ctie-k.ch" + +#include <kpathsea/kpathsea.h> + +/*:72*/ +#line 90 "./ctie.w" + +/*12:*/ +#line 211 "./ctie.w" + +#define search 0 +#define test 1 +#define reading 2 +#define ignore 3 +typedef int in_file_modes; +#define unknown 0 +#define master 1 +#define chf 2 +typedef int file_types; + + +/*:12*//*13:*/ +#line 229 "./ctie.w" + +#define normal 0 +#define pre 1 +#define post 2 +typedef int out_md_type; + + +/*:13*//*14:*/ +#line 239 "./ctie.w" + +typedef int file_index; + + +/*:14*//*15:*/ +#line 246 "./ctie.w" + +typedef struct _indsc{ +char file_name[max_file_name_length]; +long line; +FILE*the_file; +struct _indsc*parent; +}include_description; + + +/*:15*//*16:*/ +#line 259 "./ctie.w" + +typedef struct _idsc{ +string file_name; +char buffer[buf_size]; +in_file_modes mode; +long line; +file_types type_of_file; +include_description*current_include; +char*buffer_end; +char*limit; +char*loc; +FILE*the_file; +int dont_match; +}input_description; + + +/*:16*/ +#line 91 "./ctie.w" + +/*30:*/ +#line 584 "./ctie.w" + +#line 223 "./ctie-k.ch" +void err_print P2H(file_index,char*); +#line 586 "./ctie.w" + +/*:30*//*35:*/ +#line 673 "./ctie.w" + +#line 243 "./ctie-k.ch" +int wrap_up P1H(void); +#line 675 "./ctie.w" + +/*:35*//*37:*/ +#line 696 "./ctie.w" + +#line 249 "./ctie-k.ch" +void pfatal_error P2H(char*,char*); +#line 698 "./ctie.w" + +/*:37*//*69:*/ +#line 1232 "./ctie.w" + +#line 392 "./ctie-k.ch" +void usage_help P1H(void); +void print_version_and_exit P2H(string,string); +#line 1235 "./ctie.w" + + +/*:69*/ +#line 92 "./ctie.w" + +/*9:*/ +#line 162 "./ctie.w" + +int history= spotless; + + +/*:9*//*17:*/ +#line 286 "./ctie.w" + +int total_include_files= 0; + +/*:17*//*18:*/ +#line 292 "./ctie.w" + +file_index actual_input,test_input,no_ch; +file_types prod_chf= unknown; +out_md_type out_mode; + +/*:18*//*20:*/ +#line 309 "./ctie.w" + +input_description*input_organisation[max_file_index+1]; + + +/*:20*//*24:*/ +#line 406 "./ctie.w" + +boolean input_has_ended= false; + + +/*:24*//*41:*/ +#line 742 "./ctie.w" + +FILE*out_file; +string out_name; + + +#line 273 "./ctie-k.ch" +/*:41*//*68:*/ +#line 1217 "./ctie.w" + +string CTIEHELP[]= { +"Usage: ctie -[mc] outfile master changefile(s)", +" Create a new master file or change file from the given", +" master (C)WEB file and changefiles.", +" All filenames are taken literally; no suffixes are added.", +"", +"-m create a new master file from original (C)WEB and change file(s)", +"-c create a master change file for original (C)WEB file from changefile(s)", +"--help display this help and exit", +"--version display version information and exit", +NULL +}; + + +/*:68*//*73:*/ +#line 459 "./ctie-k.ch" + +string file_open_mode; +#line 1278 "./ctie.w" + + +/*:73*/ +#line 93 "./ctie.w" + +/*31:*/ +#line 589 "./ctie.w" + +#line 230 "./ctie-k.ch" +void err_print P2C(file_index,i,char*,s) + +#line 592 "./ctie.w" +{ +char*k,*l; +fprintf(stderr,*s=='!'?"\n%s":"%s",s); +if(i>=0)/*32:*/ +#line 611 "./ctie.w" + +{ +register input_description*inp_desc= input_organisation[i]; +register include_description*inc_desc= inp_desc->current_include; + +if(inc_desc!=NULL){ +fprintf(stderr,". (l. %ld of include file %s",inc_desc->line, +inc_desc->file_name); +fprintf(stderr," included from l. %ld of %s file %s)\n", +inp_desc->line, +inp_desc->type_of_file==master?"master":"change", +inp_desc->file_name); +} +else +fprintf(stderr,". (l. %ld of %s file %s)\n",inp_desc->line, +inp_desc->type_of_file==master?"master":"change", +inp_desc->file_name); +l= (inp_desc->loc>=inp_desc->limit?inp_desc->limit:inp_desc->loc); +if(l> inp_desc->buffer){ +for(k= inp_desc->buffer;k<l;k++) +if(*k=='\t')putc(' ',stderr); +else putc(*k,stderr); +putc('\n',stderr); +for(k= inp_desc->buffer;k<l;k++) +putc(' ',stderr); +} +for(k= l;k<inp_desc->limit;k++) +putc(*k,stderr); +putc('\n',stderr); +} + + +/*:32*/ +#line 595 "./ctie.w" + +else putc('\n',stderr); +fflush(stderr); +history= troublesome; +} + +/*:31*//*38:*/ +#line 699 "./ctie.w" + +#line 256 "./ctie-k.ch" +void pfatal_error P2C(char*,s,char*,t) +#line 702 "./ctie.w" +{ +char*strerr= strerror(errno); + +fprintf(stderr,"\n%s%s",s,t); +if(strerr)fprintf(stderr," (%s)\n",strerr); +else fprintf(stderr,"\n"); +history= fatal; +exit(wrap_up()); +} + + +/*:38*/ +#line 94 "./ctie.w" + +/*21:*/ +#line 328 "./ctie.w" + +#line 143 "./ctie-k.ch" +boolean get_line P2C(file_index,i,boolean,do_includes) +#line 331 "./ctie.w" +{ +register input_description*inp_desc= input_organisation[i]; +register FILE*fp; + +if(inp_desc->mode==ignore)return false; + +restart: +if(inp_desc->current_include!=NULL){ +register include_description*inc_desc= inp_desc->current_include; + +fp= inc_desc->the_file; +/*26:*/ +#line 426 "./ctie.w" + +{ +register int c; +register char*k; + +if(feof(fp)) +/*27:*/ +#line 452 "./ctie.w" + +{ +include_description*temp= inc_desc->parent; + +fclose(fp); +free(inc_desc); +total_include_files--; +inp_desc->current_include= temp; +goto restart; +} + + +/*:27*/ +#line 432 "./ctie.w" + + +inp_desc->limit= k= inp_desc->buffer; +while(k<=inp_desc->buffer_end&&(c= getc(fp))!=EOF&&c!='\n') +if((*(k++)= c)!=' ')inp_desc->limit= k; +if(k> inp_desc->buffer_end) +if((c= getc(fp))!=EOF&&c!='\n'){ +ungetc(c,fp);inp_desc->loc= inp_desc->buffer; +err_print(i,"! Input line too long"); + +} +if(c==EOF&&inp_desc->limit==inp_desc->buffer) +/*27:*/ +#line 452 "./ctie.w" + +{ +include_description*temp= inc_desc->parent; + +fclose(fp); +free(inc_desc); +total_include_files--; +inp_desc->current_include= temp; +goto restart; +} + + +/*:27*/ +#line 444 "./ctie.w" + + +inc_desc->line++; +} + +/*:26*/ +#line 342 "./ctie.w" + +} +else{ +fp= inp_desc->the_file; +/*22:*/ +#line 366 "./ctie.w" + +{ +register int c; +register char*k; + +if(feof(fp)) +/*23:*/ +#line 394 "./ctie.w" + +{ +inp_desc->mode= ignore; +inp_desc->limit= NULL; +if(inp_desc->type_of_file==master)input_has_ended= true; +fclose(fp); +return false; +} + + +/*:23*/ +#line 372 "./ctie.w" + + +inp_desc->limit= k= inp_desc->buffer; +while(k<=inp_desc->buffer_end&&(c= getc(fp))!=EOF&&c!='\n') +if((*(k++)= c)!=' ')inp_desc->limit= k; +if(k> inp_desc->buffer_end) +if((c= getc(fp))!=EOF&&c!='\n'){ +ungetc(c,fp);inp_desc->loc= inp_desc->buffer; +err_print(i,"! Input line too long"); + +} +if(c==EOF&&inp_desc->limit==inp_desc->buffer) +/*23:*/ +#line 394 "./ctie.w" + +{ +inp_desc->mode= ignore; +inp_desc->limit= NULL; +if(inp_desc->type_of_file==master)input_has_ended= true; +fclose(fp); +return false; +} + + +/*:23*/ +#line 384 "./ctie.w" + + +/*25:*/ +#line 414 "./ctie.w" + +inp_desc->line++; +if(inp_desc->type_of_file==master&&inp_desc->line%100==0){ +if(inp_desc->line%500==0)printf("%ld",inp_desc->line); +else putchar('.'); +fflush(stdout); +} + + +/*:25*/ +#line 387 "./ctie.w" + +} + + +/*:22*/ +#line 346 "./ctie.w" + +} + +if(do_includes) +/*28:*/ +#line 467 "./ctie.w" + +{ +inp_desc->loc= inp_desc->buffer; +*inp_desc->limit= ' '; +if(*inp_desc->buffer=='@'&& +(inp_desc->buffer[1]=='i'||inp_desc->buffer[1]=='I')){ +inp_desc->loc= inp_desc->buffer+2; +*inp_desc->limit= '"'; +while(*inp_desc->loc==' '||*inp_desc->loc=='\t') +inp_desc->loc++; +if(inp_desc->loc>=inp_desc->limit){ +err_print(i,"! Include file name not given"); + +goto restart; +} +if(total_include_files>=max_include_files){ +err_print(i,"! Too many nested includes"); + +goto restart; +} +total_include_files++; +/*29:*/ +#line 507 "./ctie.w" + +{ +include_description*new_inc; +#line 166 "./ctie-k.ch" +char*file_name_end; +string fullname; +char*k; +#line 514 "./ctie.w" + +new_inc= (include_description*)malloc(sizeof(include_description)); +if(new_inc==NULL) +fatal_error(i,"! No memory for new include descriptor",""); +new_inc->line= 0; +k= new_inc->file_name; +file_name_end= k+max_file_name_length-1; + +if(*inp_desc->loc=='"'){ +inp_desc->loc++; +while(*inp_desc->loc!='"'&&k<=file_name_end) +*k++= *inp_desc->loc++; +if(inp_desc->loc==inp_desc->limit) +k= file_name_end+1; +}else +while(*inp_desc->loc!=' '&&*inp_desc->loc!='\t'&& +*inp_desc->loc!='"'&&k<=file_name_end)*k++= *inp_desc->loc++; +if(k> file_name_end)too_long(); + +*k= '\0'; +#line 174 "./ctie-k.ch" +fullname= kpse_find_cweb(new_inc->file_name); +if(fullname) +new_inc->the_file= fopen(fullname,"r"); +if(fullname!=NULL&&new_inc->the_file!=NULL){ +free(fullname); +#line 535 "./ctie.w" +new_inc->parent= inp_desc->current_include; +inp_desc->current_include= new_inc; +goto restart; +} +#line 563 "./ctie.w" +total_include_files--; +free(new_inc); +#line 212 "./ctie-k.ch" +if(fullname){ +free(fullname); +err_print(i,"! Cannot open include file"); +}else +err_print(i,"! Cannot find include file"); +#line 566 "./ctie.w" + +goto restart; +} + + + +/*:29*/ +#line 488 "./ctie.w" +; +} +} + + +/*:28*/ +#line 351 "./ctie.w" + +return true; +} + + +/*:21*//*34:*/ +#line 663 "./ctie.w" + +#line 237 "./ctie-k.ch" +int wrap_up P1H(void) +#line 665 "./ctie.w" +{ +/*36:*/ +#line 682 "./ctie.w" + +switch(history){ +case spotless: +printf("\n(No errors were found.)\n");break; +case troublesome: +printf("\n(Pardon me, but I think I spotted something wrong.)\n");break; +case fatal:printf("(That was a fatal error, my friend.)\n"); +} + + +/*:36*/ +#line 666 "./ctie.w" +; +if(history> spotless)return 1; +else return 0; +} + +/*:34*//*44:*/ +#line 791 "./ctie.w" + +#line 342 "./ctie-k.ch" +boolean lines_dont_match P2C(file_index,i,file_index,j) +#line 794 "./ctie.w" +{ +register input_description*iptr= input_organisation[i], +*jptr= input_organisation[j]; + +if(iptr->limit-iptr->buffer!=jptr->limit-jptr->buffer) +return true; +return strncmp(iptr->buffer,jptr->buffer,iptr->limit-iptr->buffer); +} + + +/*:44*//*45:*/ +#line 808 "./ctie.w" + +#line 349 "./ctie-k.ch" +void init_change_file P1C(file_index,i) +#line 811 "./ctie.w" +{ +register input_description*inp_desc= input_organisation[i]; +char ccode; + +inp_desc->limit= inp_desc->buffer; +/*46:*/ +#line 827 "./ctie.w" + +while(1){ +if(!get_line(i,false))return; +if(inp_desc->limit<inp_desc->buffer+2)continue; +if(inp_desc->buffer[0]!='@')continue; +ccode= inp_desc->buffer[1]; +if(xisupper(ccode))ccode= tolower(ccode); +if(ccode=='x')break; +if(ccode=='y'||ccode=='z'||ccode=='i'){ +inp_desc->loc= inp_desc->buffer+2; +err_print(i,"! Missing @x in change file"); + +} +} + +/*:46*/ +#line 816 "./ctie.w" + +/*47:*/ +#line 844 "./ctie.w" + +do{ +if(!get_line(i,true)){ +err_print(i,"! Change file ended after @x"); + +return; +} +}while(inp_desc->limit==inp_desc->buffer); + + +/*:47*/ +#line 817 "./ctie.w" + +inp_desc->dont_match= 0; +} + + +/*:45*//*48:*/ +#line 857 "./ctie.w" + +#line 356 "./ctie-k.ch" +void put_line P1C(file_index,j) +#line 860 "./ctie.w" +{ +char*ptr= input_organisation[j]->buffer; +char*lmt= input_organisation[j]->limit; + +while(ptr<lmt)putc(*ptr++,out_file); +putc('\n',out_file); +} + + +/*:48*//*49:*/ +#line 872 "./ctie.w" + +#line 363 "./ctie-k.ch" +boolean e_of_ch_module P1C(file_index,i) +#line 875 "./ctie.w" +{ +register input_description*inp_desc= input_organisation[i]; + +if(inp_desc->limit==NULL){ +err_print(i,"! Change file ended without @z"); + +return true; +}else if(inp_desc->limit>=inp_desc->buffer+2) +if(inp_desc->buffer[0]=='@'&& +(inp_desc->buffer[1]=='Z'||inp_desc->buffer[1]=='z')) +return true; +return false; +} + + +/*:49*//*50:*/ +#line 893 "./ctie.w" + +#line 370 "./ctie-k.ch" +boolean e_of_ch_preamble P1C(file_index,i) +#line 896 "./ctie.w" +{ +register input_description*inp_desc= input_organisation[i]; + +if(inp_desc->limit>=inp_desc->buffer+2&&inp_desc->buffer[0]=='@') +if(inp_desc->buffer[1]=='Y'||inp_desc->buffer[1]=='y'){ +if(inp_desc->dont_match> 0){ +inp_desc->loc= inp_desc->buffer+2; +fprintf(stderr,"\n! Hmm... %d ",inp_desc->dont_match); +err_print(i,"of the preceding lines failed to match"); +} +return true; +} +return false; +} + + + +/*:50*//*61:*/ +#line 1105 "./ctie.w" + +#line 376 "./ctie-k.ch" +void usage_error P1H(void) +#line 1107 "./ctie.w" +{ +/*62:*/ +#line 1118 "./ctie.w" + +#line 382 "./ctie-k.ch" +{ +extern KPSEDLL string kpathsea_version_string; +printf("%s (%s)\n",banner,kpathsea_version_string); +} +#line 1120 "./ctie.w" +printf("%s\n",copyright); + + +/*:62*/ +#line 1108 "./ctie.w" +; +fprintf(stderr,"Usage: ctie -[mc] outfile master changefile(s)\n"); +fprintf(stderr,"Type ctie --help for more information\n"); +exit(1); +} + + +/*:61*/ +#line 95 "./ctie.w" + +/*3:*/ +#line 101 "./ctie.w" + +#line 32 "./ctie-k.ch" +int main P2C(int,argc,string*,argv) +#line 104 "./ctie.w" +{ +#line 38 "./ctie-k.ch" +/*5:*/ +#line 84 "./ctie-k.ch" + +kpse_set_program_name(argv[0],"ctie"); + +/*:5*/ +#line 38 "./ctie-k.ch" +; +/*19:*/ +#line 300 "./ctie.w" + +actual_input= 0; +out_mode= normal; + +/*:19*/ +#line 39 "./ctie-k.ch" +; +#line 106 "./ctie.w" +/*63:*/ +#line 1135 "./ctie.w" + +{ +if(argc> max_file_index+5-1)usage_error(); +no_ch= -1; +while(--argc> 0){ +argv++; +if(strcmp("-help",*argv)==0||strcmp("--help",*argv)==0) +/*66:*/ +#line 1202 "./ctie.w" + +usage_help(); + + + +/*:66*/ +#line 1142 "./ctie.w" +; +if(strcmp("-version",*argv)==0||strcmp("--version",*argv)==0) +/*67:*/ +#line 1208 "./ctie.w" + +{ +print_version_and_exit("CTIE",version_number); + +} + + +/*:67*/ +#line 1144 "./ctie.w" +; +if(**argv=='-')/*64:*/ +#line 1158 "./ctie.w" + +if(prod_chf!=unknown)usage_error(); +else +switch(*(*argv+1)){ +case'c':case'C':prod_chf= chf;break; +case'm':case'M':prod_chf= master;break; +default:usage_error(); +} + + +/*:64*/ +#line 1145 "./ctie.w" + +else/*65:*/ +#line 1172 "./ctie.w" + +{ +if(no_ch==(-1)){ +out_name= *argv; +}else{ +register input_description*inp_desc; + +inp_desc= (input_description*)malloc(sizeof(input_description)); +if(inp_desc==NULL) +fatal_error(-1,"! No memory for input descriptor",""); + +inp_desc->mode= search; +inp_desc->line= 0; +inp_desc->type_of_file= chf; +inp_desc->limit= inp_desc->buffer; +inp_desc->buffer[0]= ' '; +inp_desc->loc= inp_desc->buffer+1; +inp_desc->buffer_end= inp_desc->buffer+buf_size-2; +inp_desc->file_name= *argv; +inp_desc->current_include= NULL; +input_organisation[no_ch]= inp_desc; +} +no_ch++; +} + + +/*:65*/ +#line 1146 "./ctie.w" + +} +if(no_ch<=0||prod_chf==unknown)usage_error(); +} + + +/*:63*/ +#line 106 "./ctie.w" + +/*62:*/ +#line 1118 "./ctie.w" + +#line 382 "./ctie-k.ch" +{ +extern KPSEDLL string kpathsea_version_string; +printf("%s (%s)\n",banner,kpathsea_version_string); +} +#line 1120 "./ctie.w" +printf("%s\n",copyright); + + +/*:62*/ +#line 107 "./ctie.w" +; +/*42:*/ +#line 277 "./ctie-k.ch" + +{ +string fullname; + +fullname= kpse_find_cweb(input_organisation[0]->file_name); +if(fullname) +input_organisation[0]->the_file= fopen(fullname,"r"); + +if(fullname==NULL||input_organisation[0]->the_file==NULL){ +if(fullname){ +pfatal_error("! Cannot open master file ", +input_organisation[0]->file_name); +}else{ +fatal_error(-1,"! Cannot find master file ", +input_organisation[0]->file_name); +} +} +else free(fullname); + + +#line 759 "./ctie.w" +printf("(%s)\n",input_organisation[0]->file_name); +input_organisation[0]->type_of_file= master; +get_line(0,true); +} + + +/*:42*/ +#line 108 "./ctie.w" + +/*43:*/ +#line 313 "./ctie-k.ch" + +{ +file_index i; +string fullname; + +i= 1; +while(i<no_ch){ +fullname= kpse_find_cweb(input_organisation[i]->file_name); +if(fullname) +input_organisation[i]->the_file= fopen(fullname,"r"); + +if(fullname==NULL||input_organisation[i]->the_file==NULL){ +if(fullname){ +pfatal_error("! Cannot open change file ", +input_organisation[i]->file_name); +}else{ +fatal_error(-1,"! Cannot find change file ", +input_organisation[i]->file_name); +} +} +else free(fullname); + + +#line 780 "./ctie.w" +printf("(%s)\n",input_organisation[i]->file_name); +init_change_file(i); +i++; +} +} + + +/*:43*/ +#line 109 "./ctie.w" + +/*40:*/ +#line 729 "./ctie.w" + +{ +out_file= fopen(out_name,"w"); +if(out_file==NULL){ +pfatal_error("! Cannot open/create output file",""); + +} +} + + +/*:40*/ +#line 110 "./ctie.w" + +/*59:*/ +#line 1074 "./ctie.w" + +actual_input= 0; +input_has_ended= false; +while(input_has_ended==false||actual_input!=0) +/*51:*/ +#line 917 "./ctie.w" + +{ +file_index test_file; + +/*52:*/ +#line 934 "./ctie.w" + +{ +register input_description*inp_desc; +while(actual_input> 0&&e_of_ch_module(actual_input)){ +inp_desc= input_organisation[actual_input]; +if(inp_desc->type_of_file==master){ + +fatal_error(-1,"! This can't happen: change file is master file",""); + +} +inp_desc->mode= search; +init_change_file(actual_input); +while((input_organisation[actual_input]->mode!=reading +&&actual_input> 0)) +actual_input--; +} +} + + +/*:52*/ +#line 921 "./ctie.w" + +if(input_has_ended&&actual_input==0)break; +/*53:*/ +#line 960 "./ctie.w" + +test_input= none; +test_file= actual_input; +while(test_input==none&&test_file<no_ch-1){ +test_file++; +switch(input_organisation[test_file]->mode){ +case search: +if(lines_dont_match(actual_input,test_file)==false){ +input_organisation[test_file]->mode= test; +test_input= test_file; +} +break; +case test: +if(lines_dont_match(actual_input,test_file)){ + +input_organisation[test_file]->dont_match++; +} +test_input= test_file; +break; +case reading: +break; +case ignore: +break; +} +} + + +/*:53*/ +#line 923 "./ctie.w" + +/*54:*/ +#line 993 "./ctie.w" + +if(prod_chf==chf){ +while(1){ +/*55:*/ +#line 1007 "./ctie.w" + +if(out_mode==normal){ +if(test_input!=none){ +fprintf(out_file,"@x\n"); +out_mode= pre; +}else break; +} + + +/*:55*/ +#line 996 "./ctie.w" + +/*56:*/ +#line 1021 "./ctie.w" + +if(out_mode==pre){ +if(test_input==none){ +fprintf(out_file,"@y\n"); +out_mode= post; +}else{ +if(input_organisation[actual_input]->type_of_file==master) +put_line(actual_input); +break; +} +} + + +/*:56*/ +#line 997 "./ctie.w" + +/*57:*/ +#line 1040 "./ctie.w" + +if(out_mode==post){ +if(input_organisation[actual_input]->type_of_file==chf){ +if(test_input==none)put_line(actual_input); +break; +}else{ +fprintf(out_file,"@z\n\n"); +out_mode= normal; +} +} + + +/*:57*/ +#line 998 "./ctie.w" + +} +}else +if(test_input==none)put_line(actual_input); + + +/*:54*/ +#line 924 "./ctie.w" + +/*58:*/ +#line 1055 "./ctie.w" + +get_line(actual_input,true); +if(test_input!=none){ +get_line(test_input,true); +if(e_of_ch_preamble(test_input)==true){ +get_line(test_input,true); +input_organisation[test_input]->mode= reading; +actual_input= test_input; +test_input= none; +} +} + + +/*:58*/ +#line 925 "./ctie.w" + +} + + +/*:51*/ +#line 1078 "./ctie.w" + +if(out_mode==post) +fprintf(out_file,"@z\n"); + + +/*:59*/ +#line 111 "./ctie.w" + +/*60:*/ +#line 1087 "./ctie.w" + +{ +file_index i; + +for(i= 1;i<no_ch;i++){ +if(input_organisation[i]->mode!=ignore){ +input_organisation[i]->loc= input_organisation[i]->buffer; +err_print(i,"! Change file entry did not match"); + +} +} +} + + +/*:60*/ +#line 112 "./ctie.w" + +exit(wrap_up()); +} + +#line 57 "./ctie-k.ch" +/*:3*/ +#line 96 "./ctie.w" + + +/*:2*//*70:*/ +#line 1237 "./ctie.w" + +#line 399 "./ctie-k.ch" +void usage_help P1H(void) +#line 1239 "./ctie.w" +{ +string*message= CTIEHELP; + +while(*message){ +fputs(*message,stdout); +putchar('\n'); +++message; +} +putchar('\n'); +exit(0); +} + + +/*:70*//*71:*/ +#line 1252 "./ctie.w" + +#line 416 "./ctie-k.ch" +void print_version_and_exit P2C(string,name,string,version) +{ +extern KPSEDLL string kpathsea_version_string; +printf("%s %s\n",name,version); +puts(kpathsea_version_string); + +puts("Copyright (C) 2002,2003 Julian Gilbey."); +puts("Kpathsea is copyright (C) 1999 Free Software Foundation, Inc."); + +puts("There is NO warranty. This is free software."); +puts("Redistribution of this software is covered by the terms of"); +puts("both the CTIE copyright and the GNU General Public Licence."); +puts("For more information about these matters, see the files"); +puts("named COPYING and the CTIE source."); +puts("Primary authors of CTIE: Julian Gilbey."); +puts("Kpathsea written by Karl Berry and others.\n"); + +exit(0); +} +#line 1265 "./ctie.w" + + +#line 450 "./ctie-k.ch" +/*:71*/ diff --git a/web/c_cpp/ctie/ctie-k.ch b/web/c_cpp/ctie/ctie-k.ch new file mode 100644 index 0000000000..88e8d840b5 --- /dev/null +++ b/web/c_cpp/ctie/ctie-k.ch @@ -0,0 +1,461 @@ +% Changes to adapt CTIE to web2c. +% Copyright 2002,2003 Julian Gilbey +% All rights reserved. +% +% This file is distributed WITHOUT ANY WARRANTY, express or implied. +% +% Permission is granted to make and distribute verbatim copies of this +% file 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 +% file under the conditions for verbatim copying, provided that the +% entire resulting derived work is distributed under the terms of a +% permission notice identical to this one. +% +% This file is based heavily on tie.ch by Olaf Weber to adapt tie.w to +% the web2c system and on comm-w2c.ch from the Web2C 7.4.5 distribution +% by Wlodek Bzyl and Olaf Weber. + +@x l.19 Add macro definitions +\def\title{The CTIE processor} +@y +\def\Kpathsea/{{\mc KPATHSEA\spacefactor1000}} + +\def\title{The CTIE processor} +@z + +@x l.102 +main(argc, argv) + int argc; string *argv; +@y +int main @,P2C(int, argc, string *, argv) +@z + +@x l.105 Set up kpathsea stuff + @<Initialise parameters@>; +@y + @<Set up |PROGNAME| feature and initialize the search path mechanism@>; + @<Initialise parameters@>; +@z + +These are defined by kpathsea; we replace this by the path-searching +initialisation code taken almost verbatim from comm-w2c.ch. +@x l.116 +@ We include the additional types |boolean| and |string|. \.{CTIE} +replaces the complex \.{TIE} character set handling (based on that of +the original \.{WEB} system) with the standard \.{CWEB} behaviour, and +so uses the |char| type for input and output. + +@d false 0 +@d true 1 + +@<Global types@>= +typedef int boolean; +typedef char* string; +@y +@ The \.{ctie} program from the original \.{CTIE} package uses the +compile-time default directory or the value of the environment +variable \.{CWEBINPUTS} as an alternative place to be searched for +files, if they could not be found in the current directory. + +This version uses the \Kpathsea/ mechanism for searching files. +The directories to be searched for come from three sources: + + (a)~a user-set environment variable \.{CWEBINPUTS} + (overriden by \.{CWEBINPUTS\_ctie});\par + (b)~a line in \Kpathsea/ configuration file \.{texmf.cnf},\hfil\break + e.g. \.{CWEBINPUTS=.:$TEXMF/texmf/cweb//} + or \.{CWEBINPUTS.ctie=.:$TEXMF/texmf/cweb//};\hangindent=2\parindent\par + (c)~compile-time default directories \.{.:$TEXMF/texmf/cweb//} + (specified in \.{texmf.in}). + +@d kpse_find_cweb(name) kpse_find_file(name, kpse_cweb_format, true) + +@ The simple file searching is replaced by the `path searching' +mechanism that the \Kpathsea/ library provides. + +We set |kpse_program_name| to |"ctie"|. This means if the variable +|CWEBINPUTS.ctie| is present in \.{texmf.cnf} (or |CWEBINPUTS_ctie| in +the environment) its value will be used as the search path for +filenames. This allows different flawors of \.{CTIE} to have +different search paths. + +@<Set up |PROGNAME| feature and initialize the search path mechanism@>= +kpse_set_program_name(argv[0], "ctie"); + +@ We include the additional types |boolean| and |string|. \.{CTIE} +replaces the complex \.{TIE} character set handling (based on that of +the original \.{WEB} system) with the standard \.{CWEB} behaviour, and +so uses the |char| type for input and output. + +The |kpathsea| library (version 3.4.5) defines the |true|, |false|, +|boolean| and |string| types in \.{kpathsea/types.h}, so we do not +actually need to define them here. +@z + +@x l.129 The kpathsea include files find the right header file for these. +@ We predeclare some standard string-handling functions here instead of +including their system header files, because the names of the header files +are not as standard as the names of the functions. (There's confusion +between \.{<string.h>} and \.{<strings.h>}.) + +@<Predecl...@>= +extern int strlen(); /* length of string */ +extern char* strcpy(); /* copy one string to another */ +extern int strncmp(); /* compare up to $n$ string characters */ +extern char* strncpy(); /* copy up to $n$ string characters */ +extern char *strerror(); +@y +@ We don't need to predeclare any string handling functions here, as +the \.{kpathsea} headers do the right thing. +@z + +@x l.176 And this. +@ And we need dynamic memory allocation. +This should cause no trouble in any \CEE/ program. +@^system dependencies@> + +@<Global \&{\#include}s@>= +#ifdef __STDC__ +#include <stdlib.h> +#else +#include <malloc.h> +#endif +@y +@ And we need dynamic memory allocation. +This should cause no trouble in any \CEE/ program. +The \.{kpathsea} include files handle the definition of |malloc()|, +too. +@^system dependencies@> +@z + +@x l.284 way too short! +@d max_file_name_length 60 +@y +@d max_file_name_length 1024 +@z + +@x l.329 +boolean get_line(i, do_includes) + file_index i; boolean do_includes; +@y +boolean get_line @,P2C(file_index, i, boolean, do_includes) +@z + +The next piece is simplified using the kpathsea kpse_find_file +function. + +@x l.497 +If the environment variable \.{CWEBINPUTS} is set, or if the compiler flag +of the same name was defined at compile time, +\.{CWEB} will look for include files in the directory thus named, if +it cannot find them in the current directory. +(Colon-separated paths are not supported.) +@y +We use the \Kpathsea/ library (in particular, the \.{CWEBINPUTS} +variable) to search for this file. +@z + +@x l.510 Don't need the same variables any longer + char temp_file_name[max_file_name_length]; + char *file_name_end; + char *k, *kk; + int l; /* length of file name */ +@y + char *file_name_end; + string fullname; + char *k; +@z + +@x l.534 Replace with kpse_find_file + if ((new_inc->the_file=fopen(new_inc->file_name, "r"))!=NULL) { +@y + fullname=kpse_find_cweb(new_inc->file_name); + if (fullname) + new_inc->the_file=fopen(fullname, "r"); + if (fullname!=NULL && new_inc->the_file!=NULL) { + free(fullname); +@z + +@x l.539 And this part is replaced by kpse_find_file + kk=getenv("CWEBINPUTS"); + if (kk!=NULL) { + if ((l=strlen(kk))>max_file_name_length-2) too_long(); + strcpy(temp_file_name, kk); + } + else { +#ifdef CWEBINPUTS + if ((l=strlen(CWEBINPUTS))>max_file_name_length-2) too_long(); + strcpy(temp_file_name, CWEBINPUTS); +#else + l=0; +#endif /* |CWEBINPUTS| */ + } + if (l>0) { + if (k+l+2>=file_name_end) too_long(); + for (; k>= new_inc->file_name; k--) *(k+l+1)=*k; + strcpy(new_inc->file_name, temp_file_name); + new_inc->file_name[l]='/'; /* \UNIX/ pathname separator */ + if ((new_inc->the_file=fopen(new_inc->file_name, "r"))!=NULL) { + new_inc->parent=inp_desc->current_include; /* link it in */ + inp_desc->current_include=new_inc; + goto restart; /* success */ + } + } +@y +@z + +@x l.565 slightly more useful error message + err_print(i, "! Cannot open include file"); +@y + if (fullname) { + free(fullname); + err_print(i, "! Cannot open include file"); + } else + err_print(i, "! Cannot find include file"); +@z + + +@x l.585 +void err_print(); +@y +void err_print @,P2H(file_index, char *); +@z + +@x l.590 +void err_print(i, s) /* prints `\..' and location of error message */ +file_index i; char *s; +@y +void err_print @,P2C(file_index, i, char *, s) +/* prints `\..' and location of error message */ +@z + +@x l.664 +int wrap_up() +@y +int wrap_up @,P1H(void) +@z + +@x l.674 +int wrap_up(); +@y +int wrap_up @,P1H(void); +@z + +@x l.697 +void pfatal_error(); +@y +void pfatal_error @,P2H(char *, char *); +@z + +@x l.700 +void pfatal_error(s, t) +char *s, *t; +@y +void pfatal_error @,P2C(char *, s, char *, t) +@z + +@x l.747 Use the kpathsea library to do this +@ For the master file we start by reading its first line into the +buffer, if we could open it. + +@<Get the master file started@>= +{ + input_organisation[0]->the_file= + fopen(input_organisation[0]->file_name, "r"); + + if (input_organisation[0]->the_file==NULL) + pfatal_error("! Cannot open master file ", + input_organisation[0]->file_name); +@.Cannot open master file@> +@y +@ For the master file we start by reading its first line into the +buffer, if we could open it. We use the \.{kpathsea} library to find +the file. + +@<Get the master file started@>= +{ + string fullname; + + fullname = kpse_find_cweb(input_organisation[0]->file_name); + if (fullname) + input_organisation[0]->the_file = fopen(fullname, "r"); + + if (fullname==NULL || input_organisation[0]->the_file==NULL) { + if (fullname) { + pfatal_error("! Cannot open master file ", + input_organisation[0]->file_name); + } else { + fatal_error(-1, "! Cannot find master file ", + input_organisation[0]->file_name); + } + } + else free(fullname); +@.Cannot open master file@> +@.Cannot find master file@> +@z + +@x l.768 And this +@<Prepare the change files@>= +{ + file_index i; + + i=1; + while (i<no_ch) { + input_organisation[i]->the_file= + fopen(input_organisation[i]->file_name, "r"); + if (input_organisation[i]->the_file==NULL) + pfatal_error("! Cannot open change file ", + input_organisation[i]->file_name); +@.Cannot open change file@> +@y +@<Prepare the change files@>= +{ + file_index i; + string fullname; + + i=1; + while (i<no_ch) { + fullname = kpse_find_cweb(input_organisation[i]->file_name); + if (fullname) + input_organisation[i]->the_file = fopen(fullname, "r"); + + if (fullname==NULL || input_organisation[i]->the_file==NULL) { + if (fullname) { + pfatal_error("! Cannot open change file ", + input_organisation[i]->file_name); + } else { + fatal_error(-1, "! Cannot find change file ", + input_organisation[i]->file_name); + } + } + else free(fullname); +@.Cannot open change file@> +@.Cannot find change file@> +@z + +@x l.792 +boolean lines_dont_match(i, j) + file_index i, j; +@y +boolean lines_dont_match @,P2C(file_index, i, file_index, j) +@z + +@x l.809 +void init_change_file(i) + file_index i; +@y +void init_change_file @,P1C(file_index, i) +@z + +@x l.858 +void put_line(j) + file_index j; +@y +void put_line @,P1C(file_index, j) +@z + +@x l.873 +boolean e_of_ch_module(i) + file_index i; +@y +boolean e_of_ch_module @,P1C(file_index, i) +@z + +@x l.894 +boolean e_of_ch_preamble(i) + file_index i; +@y +boolean e_of_ch_preamble @,P1C(file_index, i) +@z + +@x l.1106 +void usage_error() +@y +void usage_error @,P1H(void) +@z + +@x l.1119 Add Web2C version to banner string +printf("%s\n", banner); /* print a ``banner line'' */ +@y +{ + extern KPSEDLL string kpathsea_version_string; /* from kpathsea/version.c */ + printf("%s (%s)\n", banner, kpathsea_version_string); /* print a ``banner line'' */ +} +@z + +@x l.1233 +void usage_help(); +void print_version_and_exit(); +@y +void usage_help @,P1H(void); +void print_version_and_exit @,P2H(string, string); +@z + +@x l.1238 +void usage_help() +@y +void usage_help @,P1H(void) +@z + +@x l.1253 +void print_version_and_exit(name, version) + string name, version; +{ + printf ("%s %s\n", name, version); + + puts ("Copyright (C) 2002,2003 Julian Gilbey."); + + puts ("There is NO warranty. This is free software. See the source"); + puts ("code of CTIE for redistribution conditions."); + + exit (0); +} +@y +void print_version_and_exit @,P2C(string, name, string, version) +{ + extern KPSEDLL string kpathsea_version_string; /* from kpathsea/version.c */ + printf ("%s %s\n", name, version); + puts (kpathsea_version_string); + + puts ("Copyright (C) 2002,2003 Julian Gilbey."); + puts ("Kpathsea is copyright (C) 1999 Free Software Foundation, Inc."); + + puts ("There is NO warranty. This is free software."); + puts ("Redistribution of this software is covered by the terms of"); + puts ("both the CTIE copyright and the GNU General Public Licence."); + puts ("For more information about these matters, see the files"); + puts ("named COPYING and the CTIE source."); + puts ("Primary authors of CTIE: Julian Gilbey."); + puts ("Kpathsea written by Karl Berry and others.\n"); + + exit (0); +} +@z + +@x l.1267 +@* System-dependent changes. +This section should be replaced, if necessary, by +changes to the program that are necessary to make \.{CTIE} +work at a particular installation. It is usually best to +design your change file so that all changes to previous +modules preserve the module numbering; then everybody's +version will be consistent with the printed program. More +extensive changes, which introduce new modules, can be +inserted here; then only the index itself will get a new +module number. +@^system dependencies@> +@y +@* System-dependent changes. +We modify the program to use the \.{kpathsea} library. +We need to include the headers first. + +@<Global \&{\#include}s@>= +#include <kpathsea/kpathsea.h> + +@ A global variable for the file read mode. + +@<Global variables@>= +string file_open_mode; +@z diff --git a/web/c_cpp/ctie/ctie.1 b/web/c_cpp/ctie/ctie.1 new file mode 100644 index 0000000000..17f7f1e8c8 --- /dev/null +++ b/web/c_cpp/ctie/ctie.1 @@ -0,0 +1,58 @@ +.TH TIE 1 20/04/2003 +.SH NAME +ctie \- merge or apply CWEB change files +.SH SYNOPSIS +.B ctie +.B \-c|\-m +.I outputfile masterfile changefile1 +[ +.I ... changefile32 +] + +.SH DESCRIPTION +The +.I CTIE +program is used to process multiple change files used with the CWEB +system for literate programming. The CWEB programs allow one to apply +patches to a source file (the +.IR masterfile ) +by means of a single change file. +.I CTIE +can be used to either merge up to 32 change files into a single one or +to apply the changes to the +.I masterfile +in order to create a new master file. Output of either the new change +file or the patched master file goes into +.IR outputfile . +Any include files (indicated using the CWEB +.I @i +command) are fully expanded during +.I CTIE +processing, which means that the change files are able to modify the +contents of any included files. Also, if this program has been +compiled using the +.I kpathsea +library, then files are searched for using the +.I CWEBINPUTS +environment variable, as described in the +.I kpathsea +documentation. +.SH OPTIONS +.TP +.B \-c +Create a single change file from the given +.IR changefiles . +.TP +.B \-m +Create a new master file by applying the given +.IR changefiles . +.SH "SEE ALSO" +.BR cweb (1), +.IR ctiedoc.tex , +.BR tie (1), +.BR web (1). +.SH "AUTHOR" +Julian Gilbey, jdg@debian.org. The original TIE program was by Klaus +Guntermann, TH Darmstadt, Institut f. Theoretische Informatik. The +TIE man page was by Joachim Schnitter, TH Darmstadt, Phys. Chemie I, +and was modified for CTIE by Julian Gilbey. diff --git a/web/c_cpp/ctie/ctie.c b/web/c_cpp/ctie/ctie.c new file mode 100644 index 0000000000..840c780014 --- /dev/null +++ b/web/c_cpp/ctie/ctie.c @@ -0,0 +1,1111 @@ +#define version_number "1.1" +#define banner "This is CTIE, Version 1.1" +#define copyright \ +"Copyright 2002,2003 Julian Gilbey. All rights reserved. There is no warranty.\n\ +Run with the --version option for other important information." \ + \ + +#define false 0 +#define true 1 \ + +#define buf_size 1024 +#define max_file_index 32 \ + +#define xisupper(c) (isupper(c) &&((unsigned char) c<0200) ) \ + \ + +#define spotless 0 +#define troublesome 1 +#define fatal 2 \ + +#define max_include_files 20 \ + +#define max_file_name_length 60 \ + +#define too_long() {total_include_files--;free(new_inc) ; \ +err_print(i,"! Include file name too long") ;goto restart;} \ + +#define fatal_error(i,s,t) { \ +fprintf(stderr,"\n%s",s) ; \ +err_print(i,t) ; \ +history= fatal; \ +exit(wrap_up() ) ; \ +} \ + \ + +#define none (-1) \ + +/*2:*/ +#line 89 "./ctie.w" + +/*8:*/ +#line 172 "./ctie.w" + +#include <stdio.h> + + +/*:8*//*9:*/ +#line 180 "./ctie.w" + +#ifdef __STDC__ +#include <stdlib.h> +#else +#include <malloc.h> +#endif + + +/*:9*//*37:*/ +#line 715 "./ctie.w" + +#include <errno.h> + + +/*:37*/ +#line 90 "./ctie.w" + +/*4:*/ +#line 124 "./ctie.w" + +typedef int boolean; +typedef char*string; + + +/*:4*//*10:*/ +#line 211 "./ctie.w" + +#define search 0 +#define test 1 +#define reading 2 +#define ignore 3 +typedef int in_file_modes; +#define unknown 0 +#define master 1 +#define chf 2 +typedef int file_types; + + +/*:10*//*11:*/ +#line 229 "./ctie.w" + +#define normal 0 +#define pre 1 +#define post 2 +typedef int out_md_type; + + +/*:11*//*12:*/ +#line 239 "./ctie.w" + +typedef int file_index; + + +/*:12*//*13:*/ +#line 246 "./ctie.w" + +typedef struct _indsc{ +char file_name[max_file_name_length]; +long line; +FILE*the_file; +struct _indsc*parent; +}include_description; + + +/*:13*//*14:*/ +#line 259 "./ctie.w" + +typedef struct _idsc{ +string file_name; +char buffer[buf_size]; +in_file_modes mode; +long line; +file_types type_of_file; +include_description*current_include; +char*buffer_end; +char*limit; +char*loc; +FILE*the_file; +int dont_match; +}input_description; + + +/*:14*/ +#line 91 "./ctie.w" + +/*5:*/ +#line 134 "./ctie.w" + +extern int strlen(); +extern char*strcpy(); +extern int strncmp(); +extern char*strncpy(); +extern char*strerror(); + + +/*:5*//*28:*/ +#line 584 "./ctie.w" + +void err_print(); + +/*:28*//*33:*/ +#line 673 "./ctie.w" + +int wrap_up(); + +/*:33*//*35:*/ +#line 696 "./ctie.w" + +void pfatal_error(); + +/*:35*//*67:*/ +#line 1232 "./ctie.w" + +void usage_help(); +void print_version_and_exit(); + + +/*:67*/ +#line 92 "./ctie.w" + +/*7:*/ +#line 162 "./ctie.w" + +int history= spotless; + + +/*:7*//*15:*/ +#line 286 "./ctie.w" + +int total_include_files= 0; + +/*:15*//*16:*/ +#line 292 "./ctie.w" + +file_index actual_input,test_input,no_ch; +file_types prod_chf= unknown; +out_md_type out_mode; + +/*:16*//*18:*/ +#line 309 "./ctie.w" + +input_description*input_organisation[max_file_index+1]; + + +/*:18*//*22:*/ +#line 406 "./ctie.w" + +boolean input_has_ended= false; + + +/*:22*//*39:*/ +#line 742 "./ctie.w" + +FILE*out_file; +string out_name; + + +/*:39*//*66:*/ +#line 1217 "./ctie.w" + +string CTIEHELP[]= { +"Usage: ctie -[mc] outfile master changefile(s)", +" Create a new master file or change file from the given", +" master (C)WEB file and changefiles.", +" All filenames are taken literally; no suffixes are added.", +"", +"-m create a new master file from original (C)WEB and change file(s)", +"-c create a master change file for original (C)WEB file from changefile(s)", +"--help display this help and exit", +"--version display version information and exit", +NULL +}; + + +/*:66*/ +#line 93 "./ctie.w" + +/*29:*/ +#line 589 "./ctie.w" + +void err_print(i,s) +file_index i;char*s; +{ +char*k,*l; +fprintf(stderr,*s=='!'?"\n%s":"%s",s); +if(i>=0)/*30:*/ +#line 611 "./ctie.w" + +{ +register input_description*inp_desc= input_organisation[i]; +register include_description*inc_desc= inp_desc->current_include; + +if(inc_desc!=NULL){ +fprintf(stderr,". (l. %ld of include file %s",inc_desc->line, +inc_desc->file_name); +fprintf(stderr," included from l. %ld of %s file %s)\n", +inp_desc->line, +inp_desc->type_of_file==master?"master":"change", +inp_desc->file_name); +} +else +fprintf(stderr,". (l. %ld of %s file %s)\n",inp_desc->line, +inp_desc->type_of_file==master?"master":"change", +inp_desc->file_name); +l= (inp_desc->loc>=inp_desc->limit?inp_desc->limit:inp_desc->loc); +if(l> inp_desc->buffer){ +for(k= inp_desc->buffer;k<l;k++) +if(*k=='\t')putc(' ',stderr); +else putc(*k,stderr); +putc('\n',stderr); +for(k= inp_desc->buffer;k<l;k++) +putc(' ',stderr); +} +for(k= l;k<inp_desc->limit;k++) +putc(*k,stderr); +putc('\n',stderr); +} + + +/*:30*/ +#line 595 "./ctie.w" + +else putc('\n',stderr); +fflush(stderr); +history= troublesome; +} + +/*:29*//*36:*/ +#line 699 "./ctie.w" + +void pfatal_error(s,t) +char*s,*t; +{ +char*strerr= strerror(errno); + +fprintf(stderr,"\n%s%s",s,t); +if(strerr)fprintf(stderr," (%s)\n",strerr); +else fprintf(stderr,"\n"); +history= fatal; +exit(wrap_up()); +} + + +/*:36*/ +#line 94 "./ctie.w" + +/*19:*/ +#line 328 "./ctie.w" + +boolean get_line(i,do_includes) +file_index i;boolean do_includes; +{ +register input_description*inp_desc= input_organisation[i]; +register FILE*fp; + +if(inp_desc->mode==ignore)return false; + +restart: +if(inp_desc->current_include!=NULL){ +register include_description*inc_desc= inp_desc->current_include; + +fp= inc_desc->the_file; +/*24:*/ +#line 426 "./ctie.w" + +{ +register int c; +register char*k; + +if(feof(fp)) +/*25:*/ +#line 452 "./ctie.w" + +{ +include_description*temp= inc_desc->parent; + +fclose(fp); +free(inc_desc); +total_include_files--; +inp_desc->current_include= temp; +goto restart; +} + + +/*:25*/ +#line 432 "./ctie.w" + + +inp_desc->limit= k= inp_desc->buffer; +while(k<=inp_desc->buffer_end&&(c= getc(fp))!=EOF&&c!='\n') +if((*(k++)= c)!=' ')inp_desc->limit= k; +if(k> inp_desc->buffer_end) +if((c= getc(fp))!=EOF&&c!='\n'){ +ungetc(c,fp);inp_desc->loc= inp_desc->buffer; +err_print(i,"! Input line too long"); + +} +if(c==EOF&&inp_desc->limit==inp_desc->buffer) +/*25:*/ +#line 452 "./ctie.w" + +{ +include_description*temp= inc_desc->parent; + +fclose(fp); +free(inc_desc); +total_include_files--; +inp_desc->current_include= temp; +goto restart; +} + + +/*:25*/ +#line 444 "./ctie.w" + + +inc_desc->line++; +} + +/*:24*/ +#line 342 "./ctie.w" + +} +else{ +fp= inp_desc->the_file; +/*20:*/ +#line 366 "./ctie.w" + +{ +register int c; +register char*k; + +if(feof(fp)) +/*21:*/ +#line 394 "./ctie.w" + +{ +inp_desc->mode= ignore; +inp_desc->limit= NULL; +if(inp_desc->type_of_file==master)input_has_ended= true; +fclose(fp); +return false; +} + + +/*:21*/ +#line 372 "./ctie.w" + + +inp_desc->limit= k= inp_desc->buffer; +while(k<=inp_desc->buffer_end&&(c= getc(fp))!=EOF&&c!='\n') +if((*(k++)= c)!=' ')inp_desc->limit= k; +if(k> inp_desc->buffer_end) +if((c= getc(fp))!=EOF&&c!='\n'){ +ungetc(c,fp);inp_desc->loc= inp_desc->buffer; +err_print(i,"! Input line too long"); + +} +if(c==EOF&&inp_desc->limit==inp_desc->buffer) +/*21:*/ +#line 394 "./ctie.w" + +{ +inp_desc->mode= ignore; +inp_desc->limit= NULL; +if(inp_desc->type_of_file==master)input_has_ended= true; +fclose(fp); +return false; +} + + +/*:21*/ +#line 384 "./ctie.w" + + +/*23:*/ +#line 414 "./ctie.w" + +inp_desc->line++; +if(inp_desc->type_of_file==master&&inp_desc->line%100==0){ +if(inp_desc->line%500==0)printf("%ld",inp_desc->line); +else putchar('.'); +fflush(stdout); +} + + +/*:23*/ +#line 387 "./ctie.w" + +} + + +/*:20*/ +#line 346 "./ctie.w" + +} + +if(do_includes) +/*26:*/ +#line 467 "./ctie.w" + +{ +inp_desc->loc= inp_desc->buffer; +*inp_desc->limit= ' '; +if(*inp_desc->buffer=='@'&& +(inp_desc->buffer[1]=='i'||inp_desc->buffer[1]=='I')){ +inp_desc->loc= inp_desc->buffer+2; +*inp_desc->limit= '"'; +while(*inp_desc->loc==' '||*inp_desc->loc=='\t') +inp_desc->loc++; +if(inp_desc->loc>=inp_desc->limit){ +err_print(i,"! Include file name not given"); + +goto restart; +} +if(total_include_files>=max_include_files){ +err_print(i,"! Too many nested includes"); + +goto restart; +} +total_include_files++; +/*27:*/ +#line 507 "./ctie.w" + +{ +include_description*new_inc; +char temp_file_name[max_file_name_length]; +char*file_name_end; +char*k,*kk; +int l; + +new_inc= (include_description*)malloc(sizeof(include_description)); +if(new_inc==NULL) +fatal_error(i,"! No memory for new include descriptor",""); +new_inc->line= 0; +k= new_inc->file_name; +file_name_end= k+max_file_name_length-1; + +if(*inp_desc->loc=='"'){ +inp_desc->loc++; +while(*inp_desc->loc!='"'&&k<=file_name_end) +*k++= *inp_desc->loc++; +if(inp_desc->loc==inp_desc->limit) +k= file_name_end+1; +}else +while(*inp_desc->loc!=' '&&*inp_desc->loc!='\t'&& +*inp_desc->loc!='"'&&k<=file_name_end)*k++= *inp_desc->loc++; +if(k> file_name_end)too_long(); + +*k= '\0'; +if((new_inc->the_file= fopen(new_inc->file_name,"r"))!=NULL){ +new_inc->parent= inp_desc->current_include; +inp_desc->current_include= new_inc; +goto restart; +} +kk= getenv("CWEBINPUTS"); +if(kk!=NULL){ +if((l= strlen(kk))> max_file_name_length-2)too_long(); +strcpy(temp_file_name,kk); +} +else{ +#ifdef CWEBINPUTS +if((l= strlen(CWEBINPUTS))> max_file_name_length-2)too_long(); +strcpy(temp_file_name,CWEBINPUTS); +#else +l= 0; +#endif +} +if(l> 0){ +if(k+l+2>=file_name_end)too_long(); +for(;k>=new_inc->file_name;k--)*(k+l+1)= *k; +strcpy(new_inc->file_name,temp_file_name); +new_inc->file_name[l]= '/'; +if((new_inc->the_file= fopen(new_inc->file_name,"r"))!=NULL){ +new_inc->parent= inp_desc->current_include; +inp_desc->current_include= new_inc; +goto restart; +} +} +total_include_files--; +free(new_inc); +err_print(i,"! Cannot open include file"); + +goto restart; +} + + + +/*:27*/ +#line 488 "./ctie.w" +; +} +} + + +/*:26*/ +#line 351 "./ctie.w" + +return true; +} + + +/*:19*//*32:*/ +#line 663 "./ctie.w" + +int wrap_up() +{ +/*34:*/ +#line 682 "./ctie.w" + +switch(history){ +case spotless: +printf("\n(No errors were found.)\n");break; +case troublesome: +printf("\n(Pardon me, but I think I spotted something wrong.)\n");break; +case fatal:printf("(That was a fatal error, my friend.)\n"); +} + + +/*:34*/ +#line 666 "./ctie.w" +; +if(history> spotless)return 1; +else return 0; +} + +/*:32*//*42:*/ +#line 791 "./ctie.w" + +boolean lines_dont_match(i,j) +file_index i,j; +{ +register input_description*iptr= input_organisation[i], +*jptr= input_organisation[j]; + +if(iptr->limit-iptr->buffer!=jptr->limit-jptr->buffer) +return true; +return strncmp(iptr->buffer,jptr->buffer,iptr->limit-iptr->buffer); +} + + +/*:42*//*43:*/ +#line 808 "./ctie.w" + +void init_change_file(i) +file_index i; +{ +register input_description*inp_desc= input_organisation[i]; +char ccode; + +inp_desc->limit= inp_desc->buffer; +/*44:*/ +#line 827 "./ctie.w" + +while(1){ +if(!get_line(i,false))return; +if(inp_desc->limit<inp_desc->buffer+2)continue; +if(inp_desc->buffer[0]!='@')continue; +ccode= inp_desc->buffer[1]; +if(xisupper(ccode))ccode= tolower(ccode); +if(ccode=='x')break; +if(ccode=='y'||ccode=='z'||ccode=='i'){ +inp_desc->loc= inp_desc->buffer+2; +err_print(i,"! Missing @x in change file"); + +} +} + +/*:44*/ +#line 816 "./ctie.w" + +/*45:*/ +#line 844 "./ctie.w" + +do{ +if(!get_line(i,true)){ +err_print(i,"! Change file ended after @x"); + +return; +} +}while(inp_desc->limit==inp_desc->buffer); + + +/*:45*/ +#line 817 "./ctie.w" + +inp_desc->dont_match= 0; +} + + +/*:43*//*46:*/ +#line 857 "./ctie.w" + +void put_line(j) +file_index j; +{ +char*ptr= input_organisation[j]->buffer; +char*lmt= input_organisation[j]->limit; + +while(ptr<lmt)putc(*ptr++,out_file); +putc('\n',out_file); +} + + +/*:46*//*47:*/ +#line 872 "./ctie.w" + +boolean e_of_ch_module(i) +file_index i; +{ +register input_description*inp_desc= input_organisation[i]; + +if(inp_desc->limit==NULL){ +err_print(i,"! Change file ended without @z"); + +return true; +}else if(inp_desc->limit>=inp_desc->buffer+2) +if(inp_desc->buffer[0]=='@'&& +(inp_desc->buffer[1]=='Z'||inp_desc->buffer[1]=='z')) +return true; +return false; +} + + +/*:47*//*48:*/ +#line 893 "./ctie.w" + +boolean e_of_ch_preamble(i) +file_index i; +{ +register input_description*inp_desc= input_organisation[i]; + +if(inp_desc->limit>=inp_desc->buffer+2&&inp_desc->buffer[0]=='@') +if(inp_desc->buffer[1]=='Y'||inp_desc->buffer[1]=='y'){ +if(inp_desc->dont_match> 0){ +inp_desc->loc= inp_desc->buffer+2; +fprintf(stderr,"\n! Hmm... %d ",inp_desc->dont_match); +err_print(i,"of the preceding lines failed to match"); +} +return true; +} +return false; +} + + + +/*:48*//*59:*/ +#line 1105 "./ctie.w" + +void usage_error() +{ +/*60:*/ +#line 1118 "./ctie.w" + +printf("%s\n",banner); +printf("%s\n",copyright); + + +/*:60*/ +#line 1108 "./ctie.w" +; +fprintf(stderr,"Usage: ctie -[mc] outfile master changefile(s)\n"); +fprintf(stderr,"Type ctie --help for more information\n"); +exit(1); +} + + +/*:59*/ +#line 95 "./ctie.w" + +/*3:*/ +#line 101 "./ctie.w" + +main(argc,argv) +int argc;string*argv; +{ +/*17:*/ +#line 300 "./ctie.w" + +actual_input= 0; +out_mode= normal; + +/*:17*/ +#line 105 "./ctie.w" +; +/*61:*/ +#line 1135 "./ctie.w" + +{ +if(argc> max_file_index+5-1)usage_error(); +no_ch= -1; +while(--argc> 0){ +argv++; +if(strcmp("-help",*argv)==0||strcmp("--help",*argv)==0) +/*64:*/ +#line 1202 "./ctie.w" + +usage_help(); + + + +/*:64*/ +#line 1142 "./ctie.w" +; +if(strcmp("-version",*argv)==0||strcmp("--version",*argv)==0) +/*65:*/ +#line 1208 "./ctie.w" + +{ +print_version_and_exit("CTIE",version_number); + +} + + +/*:65*/ +#line 1144 "./ctie.w" +; +if(**argv=='-')/*62:*/ +#line 1158 "./ctie.w" + +if(prod_chf!=unknown)usage_error(); +else +switch(*(*argv+1)){ +case'c':case'C':prod_chf= chf;break; +case'm':case'M':prod_chf= master;break; +default:usage_error(); +} + + +/*:62*/ +#line 1145 "./ctie.w" + +else/*63:*/ +#line 1172 "./ctie.w" + +{ +if(no_ch==(-1)){ +out_name= *argv; +}else{ +register input_description*inp_desc; + +inp_desc= (input_description*)malloc(sizeof(input_description)); +if(inp_desc==NULL) +fatal_error(-1,"! No memory for input descriptor",""); + +inp_desc->mode= search; +inp_desc->line= 0; +inp_desc->type_of_file= chf; +inp_desc->limit= inp_desc->buffer; +inp_desc->buffer[0]= ' '; +inp_desc->loc= inp_desc->buffer+1; +inp_desc->buffer_end= inp_desc->buffer+buf_size-2; +inp_desc->file_name= *argv; +inp_desc->current_include= NULL; +input_organisation[no_ch]= inp_desc; +} +no_ch++; +} + + +/*:63*/ +#line 1146 "./ctie.w" + +} +if(no_ch<=0||prod_chf==unknown)usage_error(); +} + + +/*:61*/ +#line 106 "./ctie.w" + +/*60:*/ +#line 1118 "./ctie.w" + +printf("%s\n",banner); +printf("%s\n",copyright); + + +/*:60*/ +#line 107 "./ctie.w" +; +/*40:*/ +#line 750 "./ctie.w" + +{ +input_organisation[0]->the_file= +fopen(input_organisation[0]->file_name,"r"); + +if(input_organisation[0]->the_file==NULL) +pfatal_error("! Cannot open master file ", +input_organisation[0]->file_name); + +printf("(%s)\n",input_organisation[0]->file_name); +input_organisation[0]->type_of_file= master; +get_line(0,true); +} + + +/*:40*/ +#line 108 "./ctie.w" + +/*41:*/ +#line 768 "./ctie.w" + +{ +file_index i; + +i= 1; +while(i<no_ch){ +input_organisation[i]->the_file= +fopen(input_organisation[i]->file_name,"r"); +if(input_organisation[i]->the_file==NULL) +pfatal_error("! Cannot open change file ", +input_organisation[i]->file_name); + +printf("(%s)\n",input_organisation[i]->file_name); +init_change_file(i); +i++; +} +} + + +/*:41*/ +#line 109 "./ctie.w" + +/*38:*/ +#line 729 "./ctie.w" + +{ +out_file= fopen(out_name,"w"); +if(out_file==NULL){ +pfatal_error("! Cannot open/create output file",""); + +} +} + + +/*:38*/ +#line 110 "./ctie.w" + +/*57:*/ +#line 1074 "./ctie.w" + +actual_input= 0; +input_has_ended= false; +while(input_has_ended==false||actual_input!=0) +/*49:*/ +#line 917 "./ctie.w" + +{ +file_index test_file; + +/*50:*/ +#line 934 "./ctie.w" + +{ +register input_description*inp_desc; +while(actual_input> 0&&e_of_ch_module(actual_input)){ +inp_desc= input_organisation[actual_input]; +if(inp_desc->type_of_file==master){ + +fatal_error(-1,"! This can't happen: change file is master file",""); + +} +inp_desc->mode= search; +init_change_file(actual_input); +while((input_organisation[actual_input]->mode!=reading +&&actual_input> 0)) +actual_input--; +} +} + + +/*:50*/ +#line 921 "./ctie.w" + +if(input_has_ended&&actual_input==0)break; +/*51:*/ +#line 960 "./ctie.w" + +test_input= none; +test_file= actual_input; +while(test_input==none&&test_file<no_ch-1){ +test_file++; +switch(input_organisation[test_file]->mode){ +case search: +if(lines_dont_match(actual_input,test_file)==false){ +input_organisation[test_file]->mode= test; +test_input= test_file; +} +break; +case test: +if(lines_dont_match(actual_input,test_file)){ + +input_organisation[test_file]->dont_match++; +} +test_input= test_file; +break; +case reading: +break; +case ignore: +break; +} +} + + +/*:51*/ +#line 923 "./ctie.w" + +/*52:*/ +#line 993 "./ctie.w" + +if(prod_chf==chf){ +while(1){ +/*53:*/ +#line 1007 "./ctie.w" + +if(out_mode==normal){ +if(test_input!=none){ +fprintf(out_file,"@x\n"); +out_mode= pre; +}else break; +} + + +/*:53*/ +#line 996 "./ctie.w" + +/*54:*/ +#line 1021 "./ctie.w" + +if(out_mode==pre){ +if(test_input==none){ +fprintf(out_file,"@y\n"); +out_mode= post; +}else{ +if(input_organisation[actual_input]->type_of_file==master) +put_line(actual_input); +break; +} +} + + +/*:54*/ +#line 997 "./ctie.w" + +/*55:*/ +#line 1040 "./ctie.w" + +if(out_mode==post){ +if(input_organisation[actual_input]->type_of_file==chf){ +if(test_input==none)put_line(actual_input); +break; +}else{ +fprintf(out_file,"@z\n\n"); +out_mode= normal; +} +} + + +/*:55*/ +#line 998 "./ctie.w" + +} +}else +if(test_input==none)put_line(actual_input); + + +/*:52*/ +#line 924 "./ctie.w" + +/*56:*/ +#line 1055 "./ctie.w" + +get_line(actual_input,true); +if(test_input!=none){ +get_line(test_input,true); +if(e_of_ch_preamble(test_input)==true){ +get_line(test_input,true); +input_organisation[test_input]->mode= reading; +actual_input= test_input; +test_input= none; +} +} + + +/*:56*/ +#line 925 "./ctie.w" + +} + + +/*:49*/ +#line 1078 "./ctie.w" + +if(out_mode==post) +fprintf(out_file,"@z\n"); + + +/*:57*/ +#line 111 "./ctie.w" + +/*58:*/ +#line 1087 "./ctie.w" + +{ +file_index i; + +for(i= 1;i<no_ch;i++){ +if(input_organisation[i]->mode!=ignore){ +input_organisation[i]->loc= input_organisation[i]->buffer; +err_print(i,"! Change file entry did not match"); + +} +} +} + + +/*:58*/ +#line 112 "./ctie.w" + +exit(wrap_up()); +} + +/*:3*/ +#line 96 "./ctie.w" + + +/*:2*//*68:*/ +#line 1237 "./ctie.w" + +void usage_help() +{ +string*message= CTIEHELP; + +while(*message){ +fputs(*message,stdout); +putchar('\n'); +++message; +} +putchar('\n'); +exit(0); +} + + +/*:68*//*69:*/ +#line 1252 "./ctie.w" + +void print_version_and_exit(name,version) +string name,version; +{ +printf("%s %s\n",name,version); + +puts("Copyright (C) 2002,2003 Julian Gilbey."); + +puts("There is NO warranty. This is free software. See the source"); +puts("code of CTIE for redistribution conditions."); + +exit(0); +} + + +/*:69*/ diff --git a/web/c_cpp/ctie/ctie.cf1 b/web/c_cpp/ctie/ctie.cf1 new file mode 100644 index 0000000000..5b1165435c --- /dev/null +++ b/web/c_cpp/ctie/ctie.cf1 @@ -0,0 +1,14 @@ +@x +line 2 +line 3 +@y +changed line 2 +changed line 3 +inserted line +@z + +@x +line 7 +@y +changed line 7 +@z diff --git a/web/c_cpp/ctie/ctie.cf2 b/web/c_cpp/ctie/ctie.cf2 new file mode 100644 index 0000000000..d9a5fbaf67 --- /dev/null +++ b/web/c_cpp/ctie/ctie.cf2 @@ -0,0 +1,26 @@ +@x +line 1 +changed line 2 +changed line 3 +inserted line +line 4 +@y +final line 2 +final line 3 +changed line 4 +@z + +@x +include line 1 +@y +changed include line 1 +inserted include line +@z + +@x +changed line 7 +line 8 +@y +final line 7 +final line 8 +@z diff --git a/web/c_cpp/ctie/ctie.cf3 b/web/c_cpp/ctie/ctie.cf3 new file mode 100644 index 0000000000..95c7ff6353 --- /dev/null +++ b/web/c_cpp/ctie/ctie.cf3 @@ -0,0 +1,13 @@ +@x +changed line 4 +@y +final line 4 +@z + +@x +changed include line 1 +inserted include line +@y +final include line 1 +@i ctie.inc2 +@z diff --git a/web/c_cpp/ctie/ctie.chf.gen b/web/c_cpp/ctie/ctie.chf.gen new file mode 100644 index 0000000000..d07e9280c0 --- /dev/null +++ b/web/c_cpp/ctie/ctie.chf.gen @@ -0,0 +1,26 @@ +@x +line 1 +line 2 +line 3 +line 4 +@y +final line 2 +final line 3 +final line 4 +@z + +@x +include line 1 +@y +final include line 1 +final inserted include line +@z + +@x +line 7 +line 8 +@y +final line 7 +final line 8 +@z + diff --git a/web/c_cpp/ctie/ctie.inc b/web/c_cpp/ctie/ctie.inc new file mode 100644 index 0000000000..435c17d8a1 --- /dev/null +++ b/web/c_cpp/ctie/ctie.inc @@ -0,0 +1,4 @@ +include line 1 +include line 2 +@i ctie.inc1 +include line 3 diff --git a/web/c_cpp/ctie/ctie.inc1 b/web/c_cpp/ctie/ctie.inc1 new file mode 100644 index 0000000000..79b0114e1e --- /dev/null +++ b/web/c_cpp/ctie/ctie.inc1 @@ -0,0 +1 @@ +included include line diff --git a/web/c_cpp/ctie/ctie.inc2 b/web/c_cpp/ctie/ctie.inc2 new file mode 100644 index 0000000000..bb83f8a671 --- /dev/null +++ b/web/c_cpp/ctie/ctie.inc2 @@ -0,0 +1 @@ +final inserted include line diff --git a/web/c_cpp/ctie/ctie.master.gen b/web/c_cpp/ctie/ctie.master.gen new file mode 100644 index 0000000000..4c00f38314 --- /dev/null +++ b/web/c_cpp/ctie/ctie.master.gen @@ -0,0 +1,19 @@ +final line 2 +final line 3 +final line 4 +line 5 +final include line 1 +final inserted include line +include line 2 +included include line +include line 3 +line 6 +final line 7 +final line 8 +line 9 +line 10 +The character set: +abcdefghijklmnopqrstuvwxyz +ABCDEFGHIJKLMNOPQRSTUVWXYZ +=!"#$%&/()?'`^~|-+[]{}@\;: + 0123456789<>.,*_ diff --git a/web/c_cpp/ctie/ctie.tie b/web/c_cpp/ctie/ctie.tie new file mode 100644 index 0000000000..649b26d551 --- /dev/null +++ b/web/c_cpp/ctie/ctie.tie @@ -0,0 +1,16 @@ +line 1 +line 2 +line 3 +line 4 +line 5 +@i "ctie.inc" +line 6 +line 7 +line 8 +line 9 +line 10 +The character set: +abcdefghijklmnopqrstuvwxyz +ABCDEFGHIJKLMNOPQRSTUVWXYZ +=!"#$%&/()?'`^~|-+[]{}@\;: + 0123456789<>.,*_ diff --git a/web/c_cpp/ctie/ctie.w b/web/c_cpp/ctie/ctie.w new file mode 100644 index 0000000000..33abf6312c --- /dev/null +++ b/web/c_cpp/ctie/ctie.w @@ -0,0 +1,1280 @@ +% This is ctie.w +% Copyright (c) 2002,2003 by Julian Gilbey +% All rights reserved. +% +% This program is distributed WITHOUT ANY WARRANTY, express or implied. +% +% Permission is granted to make and distribute verbatim copies of this +% program 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 +% program under the conditions for verbatim copying, provided that the +% entire resulting derived work is distributed under the terms of a +% permission notice identical to this one. +% +% This program is based heavily on tie.w and common.w, part of the +% CWEB source. + +\def\title{The CTIE processor} +\def\botofcontents{% +\vfill +$\copyright$ 2002,2003 Julian Gilbey + +All rights reserved. + +This program is distributed WITHOUT ANY WARRANTY, express or implied. + +Permission is granted to make and distribute verbatim copies of this +program 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 +program under the conditions for verbatim copying, provided that the +entire resulting derived work is distributed under the terms of a +permission notice identical to this one. +} + + +@* Introduction. +Whenever a programmer wants to change a given \.{WEB} or \.{CWEB} +program (referred to as a \.{WEB} program throughout this program) +because of system dependencies, she or he will create a new change +file. In addition there may be a second change file to modify system +independent modules of the program. But the \.{WEB} file cannot be +tangled and weaved with more than one change file simultaneously. +The \.{TIE} program was designed to merge a \.{WEB} file +and several change files producing a new \.{WEB} file, and since the +input files are tied together, the program was called \.{TIE}. +Furthermore, the program could be used to merge several change files +giving a new single change file. This method seems to be more +important because it doesn't modify the original source file. + +However, the introduction of \.{CWEB} has meant that \.{TIE} is not +quite able to perform its task correctly any longer: \.{CWEB} +introduced the idea of include files, which are input into \.{CWEB} +files using the \.{@@i} command, and \.{TIE} is unable to handle such +constructs if the change files modify lines included in those files. +The present program, \.{CTIE}, is designed to overcome this lack. +Like \.{TIE}, upon which it is based, it can either output a single +master \.{WEB} file or a master change file. However, in both cases, +any include commands will be totally expanded and the files included +in the output rather than the \.{@@i} commands being left; this makes +this code feasible, which it would not necessarily be otherwise. +Other than this difference, \.{CTIE} should function identically to +\.{TIE} on files which do not involve any \.{CWEB} include commands. + +The algorithm used is essentially the same as that of \.{TIE}, with +modifications to check for and handle \.{@@i} commands. Thus, as with +\.{TIE}, the method used only needs one buffer line for each input +file. Thus the storage requirement of \.{CTIE} does not depend on the +sizes of the input files but only on their number. + +The program is written in \CEE/ and has few system dependencies. + +The ``banner line'' defined here should be changed whenever +\.{CTIE} is modified. We also keep the version number here separately +for ease; it is used below. + +@d version_number "1.1" +@d banner "This is CTIE, Version 1.1" +@d copyright + "Copyright 2002,2003 Julian Gilbey. All rights reserved. There is no warranty.\n\ +Run with the --version option for other important information." + + +@ The main outline of the program is now given. +This can be used more or less for any \CEE/ program. + +@c +@<Global \&{\#include}s@>@; +@<Global types@>@; +@<Predeclaration of functions@>@; +@<Global variables@>@; +@<Error handling functions@>@; +@<Internal functions@>@; +@<The main function@>@; + +@ And this is the structure of the |main| function: this is where +\.{CTIE} starts, and where it ends. + +@<The main function@>= +main(argc, argv) + int argc; string *argv; +{ + @<Initialise parameters@>; + @<Scan the parameters@>@; + @<Print the banners@>; + @<Get the master file started@>@; + @<Prepare the change files@>@; + @<Prepare the output file@>@; + @<Process the input@>@; + @<Check that all changes have been read@>@; + exit(wrap_up()); +} + +@ We include the additional types |boolean| and |string|. \.{CTIE} +replaces the complex \.{TIE} character set handling (based on that of +the original \.{WEB} system) with the standard \.{CWEB} behaviour, and +so uses the |char| type for input and output. + +@d false 0 +@d true 1 + +@<Global types@>= +typedef int boolean; +typedef char* string; + + +@ We predeclare some standard string-handling functions here instead of +including their system header files, because the names of the header files +are not as standard as the names of the functions. (There's confusion +between \.{<string.h>} and \.{<strings.h>}.) + +@<Predecl...@>= +extern int strlen(); /* length of string */ +extern char* strcpy(); /* copy one string to another */ +extern int strncmp(); /* compare up to $n$ string characters */ +extern char* strncpy(); /* copy up to $n$ string characters */ +extern char *strerror(); + + +@ The following parameters should be sufficient for most +applications of \.{CTIE}. +@^system dependencies@> + +@d buf_size 1024 /* maximum length of one input line */ +@d max_file_index 32 +/* we don't think that anyone needs more than 32 change files */ +@d xisupper(c) (isupper(c)&&((unsigned char)c<0200)) + + +@ We introduce a history variable that allows us to set a +return code if the operating system can use it. +First we introduce the coded values for the history. +This variable must be initialized. +(We do this even if the value given may be the default for +variables, just to document the need for the initial value.) +@d spotless 0 +@d troublesome 1 +@d fatal 2 + +@<Global variables@>= +int history=spotless; + + +@* Input and output. +Standard output for the user is done by writing on |stdout|. Error +messages are written to |stderr|. Terminal input is not needed in +this version of \.{CTIE}. |stdin|, |stdout| and |stderr| are +predefined as we include the \.{stdio.h} definitions. + +@<Global \&{\#include}s@>= +#include <stdio.h> + + +@ And we need dynamic memory allocation. +This should cause no trouble in any \CEE/ program. +@^system dependencies@> + +@<Global \&{\#include}s@>= +#ifdef __STDC__ +#include <stdlib.h> +#else +#include <malloc.h> +#endif + + +@* Data structures. +The multiple primary input files (master file and change +files) are treated the same way. To organize the +simultaneous usage of several input files, we introduce the +data type \&{in\_file\_modes}. + +The mode |search| indicates that \.{CTIE} searches for a +match of the input line with any line of an input file in +|reading| mode. |test| is used whenever a match is found +and it has to be tested if the next input lines do match +also. |reading| describes that the lines can be read without +any check for matching other lines. |ignore| denotes +that the file cannot be used. This may happen because an +error has been detected or because the end of the file has +been found. + +\leavevmode |file_types| is used to describe whether a file +is a master file or a change file. The value |unknown| is added +to this type to set an initial mode for the output file. +This enables us to check whether any option was used to select +the kind of output. (this would even be necessary if we +would assume a default action for missing options.) + +@<Global types@>= +#define search 0 +#define test 1 +#define reading 2 +#define ignore 3 +typedef int in_file_modes; /* should be |enum(search, test, reading, ignore)| */ +#define unknown 0 +#define master 1 +#define chf 2 +typedef int file_types; /* should be |enum(unknown, master, chf)| */ + + +@ A variable of type |out_md_type| will tell us in what state the output +change file is during processing. |normal| will be the state, when we +did not yet start a change, |pre| will be set when we write the lines +to be changes and |post| will indicate that the replacement lines are +written. + +@<Global types@>= +#define normal 0 +#define pre 1 +#define post 2 +typedef int out_md_type; /* should be |enum(normal, pre, post)| */ + + +@ The next type will indicate variables used as an index into the file +table. + +@<Global types@>= +typedef int file_index; /* |-1..max_file_index+1| */ + + +@ This is the data structure in which we collect information about +each include file. + +@<Global types@>= +typedef struct _indsc { + char file_name[max_file_name_length]; + long line; + FILE *the_file; + struct _indsc *parent; +} include_description; + + +@ The following data structure contains all of the information needed +to use these input files. +%`line' is a normal identifier throughout this program +@f line dummy +@<Global types@>= +typedef struct _idsc{ + string file_name; + char buffer[buf_size]; + in_file_modes mode; + long line; + file_types type_of_file; + include_description *current_include; + char *buffer_end; + char *limit; + char *loc; + FILE *the_file; + int dont_match; + } input_description; + + +@ Every one of the primary input files might include another file +using the \.{@@i} include mechanism. In turn, each of these might +include other files, and so on. We allow a limited number of these +files to be opened simultaneously, and we store information about the +currently open include files as a linked list attached to each primary +file. + +@d max_include_files 20 + /* maximum number of include files open simultaneously */ +@d max_file_name_length 60 + +@<Global variables@>= +int total_include_files = 0; /* count 'em */ + +@ The following variables refer to the files in action, the number of +change files, the mode of operation and the current output state. + +@<Global variables@>= +file_index actual_input, test_input, no_ch; +file_types prod_chf=unknown; +out_md_type out_mode; + +@ And the |actual_input| and |out_mode| variables need to be +initialised sensibly. + +@<Initialise parameters@>= +actual_input=0; +out_mode=normal; + +@ All primary input files (including the master file) are recorded +in the following structure. +The components are usually accessed through a local pointer variable, +requiring only a one-time-computation of the index expression. + +@<Global variables@>= +input_description *input_organisation[max_file_index+1]; + + +@* File I/O. +The basic function |get_line| can be used to get a line from +an input file. The line is stored in the |buffer| part of the +descriptor. The components |limit| and |line| are updated. If the +end of the file is reached |mode| is set to |ignore|. On some systems +it might be useful to replace tab characters by a proper number of +spaces since several editors used to create change files insert tab +characters into a source file not under control of the user. So it +might be a problem to create a matching change file. +@^tab character expansion@> + +We define |get_line| to read a line from a file specified by the +corresponding file descriptor. This function returns |true| if it is +successful and |false| if the end of the file has been reached. + +@<Internal functions@>= +boolean get_line(i, do_includes) + file_index i; boolean do_includes; +{ + register input_description *inp_desc=input_organisation[i]; + register FILE *fp; + + if (inp_desc->mode==ignore) return false; + + restart: + if (inp_desc->current_include != NULL) { + register include_description *inc_desc=inp_desc->current_include; + + fp=inc_desc->the_file; + @<Get include line into buffer or |goto restart| if end of file@>@; + } + else { + fp=inp_desc->the_file; + @<Get line into buffer, |return false| if end of file@>@; + } + + if (do_includes) + @<Check for \.{@@i} in newly read line, |goto restart| if + include fails@>@; + return true; +} + + +@ Lines must fit into the buffer completely. +We read all characters sequentially until an end of line is found +(but do not forget to check for |EOF|!). +Too long input lines will be truncated. +This will result in a damaged output if they occur in the +replacement part of a change file, or in an incomplerte check if the +matching part is concerned. +Tab character expansion might be done here. +@^tab character expansion@> + +@<Get line into buffer,...@>= +{ + register int c; /* the actual character read */ + register char *k; /* where the next character goes */ + + if (feof(fp)) + @<Handle end of file and return@>@; + + inp_desc->limit = k = inp_desc->buffer; /* beginning of buffer */ + while (k<=inp_desc->buffer_end && (c=getc(fp)) != EOF && c!='\n') + if ((*(k++) = c) != ' ') inp_desc->limit = k; + if (k>inp_desc->buffer_end) + if ((c=getc(fp))!=EOF && c!='\n') { + ungetc(c, fp); inp_desc->loc=inp_desc->buffer; + err_print(i, "! Input line too long"); +@.Input line too long@> + } + if (c==EOF && inp_desc->limit==inp_desc->buffer) + @<Handle end of file...@>@; + + @<Increment the line number and print a progess report at + certain times@>@; +} + + +@ End of file is special if this file is the master file. +Then we set the global flag variable |input_has_ended|. + +@<Handle end of file ...@>= +{ + inp_desc->mode=ignore; + inp_desc->limit=NULL; /* mark end-of-file */ + if (inp_desc->type_of_file==master) input_has_ended=true; + fclose(fp); + return false; +} + + +@ This variable must be declared for global access. + +@<Global variables@>= +boolean input_has_ended=false; + + +@ This section does what its name says. Every 100 lines +in the master file we print a dot, every 500 lines the number +of lines is shown. + +@<Increment the line number and print ...@>= +inp_desc->line++; +if (inp_desc->type_of_file==master && inp_desc->line % 100==0) { + if (inp_desc->line % 500 == 0) printf("%ld", inp_desc->line); + else putchar('.'); + fflush(stdout); +} + + +@ The following is very similar to the above, but for the case where +we are reading from an include file. + +@<Get include line into buffer or |goto restart| if end of file@>= +{ + register int c; /* the actual character read */ + register char *k; /* where the next character goes */ + + if (feof(fp)) + @<Handle end of include file and |goto restart|@>@; + + inp_desc->limit = k = inp_desc->buffer; /* beginning of buffer */ + while (k<=inp_desc->buffer_end && (c=getc(fp)) != EOF && c!='\n') + if ((*(k++) = c) != ' ') inp_desc->limit = k; + if (k>inp_desc->buffer_end) + if ((c=getc(fp))!=EOF && c!='\n') { + ungetc(c, fp); inp_desc->loc=inp_desc->buffer; + err_print(i, "! Input line too long"); +@.Input line too long@> + } + if (c==EOF && inp_desc->limit==inp_desc->buffer) + @<Handle end of include file...@>@; + + inc_desc->line++; +} + +@ We don't bail out if we find the end of an include file, we just +return to the parent file. + +@<Handle end of include file and |goto restart|@>= +{ + include_description *temp=inc_desc->parent; + + fclose(fp); + free(inc_desc); + total_include_files--; + inp_desc->current_include=temp; + goto restart; +} + + +@ Usually, we have to check the line we have just read to see whether +it begins with \.{@@i} and therefore needs expanding. + +@<Check for \.{@@i} in newly read line...@>= +{ + inp_desc->loc=inp_desc->buffer; + *inp_desc->limit=' '; + if (*inp_desc->buffer=='@@' && + (inp_desc->buffer[1]=='i' || inp_desc->buffer[1]=='I')) { + inp_desc->loc=inp_desc->buffer+2; + *inp_desc->limit='"'; /* this will terminate the search in all cases */ + while (*inp_desc->loc==' '||*inp_desc->loc=='\t') + inp_desc->loc++; + if (inp_desc->loc>=inp_desc->limit) { + err_print(i, "! Include file name not given"); +@.Include file name ...@> + goto restart; + } + if (total_include_files>=max_include_files) { + err_print(i, "! Too many nested includes"); +@.Too many nested includes@> + goto restart; + } + total_include_files++; /* push input stack */ + @<Try to open include file, abort push if unsuccessful, go to |restart|@>; + } +} + + +@ When an \.{@@i} line is found in the file, we must temporarily +stop reading it and start reading from the named include file. The +\.{@@i} line should give a complete file name with or without +double quotes. +If the environment variable \.{CWEBINPUTS} is set, or if the compiler flag +of the same name was defined at compile time, +\.{CWEB} will look for include files in the directory thus named, if +it cannot find them in the current directory. +(Colon-separated paths are not supported.) +The remainder of the \.{@@i} line after the file name is ignored. + +@d too_long() {total_include_files--; free(new_inc); + err_print(i, "! Include file name too long"); goto restart;} + +@<Try to open...@>= +{ + include_description *new_inc; + char temp_file_name[max_file_name_length]; + char *file_name_end; + char *k, *kk; + int l; /* length of file name */ + + new_inc=(include_description *) malloc(sizeof(include_description)); + if (new_inc==NULL) + fatal_error(i, "! No memory for new include descriptor", ""); + new_inc->line=0; + k=new_inc->file_name; + file_name_end=k+max_file_name_length-1; + + if (*inp_desc->loc=='"') { + inp_desc->loc++; + while (*inp_desc->loc!='"' && k<=file_name_end) + *k++=*inp_desc->loc++; + if (inp_desc->loc==inp_desc->limit) + k=file_name_end+1; /* unmatched quote is `too long' */ + } else + while (*inp_desc->loc!=' '&&*inp_desc->loc!='\t'&& + *inp_desc->loc!='"'&&k<=file_name_end) *k++=*inp_desc->loc++; + if (k>file_name_end) too_long(); +@.Include file name ...@> + *k='\0'; + if ((new_inc->the_file=fopen(new_inc->file_name, "r"))!=NULL) { + new_inc->parent=inp_desc->current_include; /* link it in */ + inp_desc->current_include=new_inc; + goto restart; /* success */ + } + kk=getenv("CWEBINPUTS"); + if (kk!=NULL) { + if ((l=strlen(kk))>max_file_name_length-2) too_long(); + strcpy(temp_file_name, kk); + } + else { +#ifdef CWEBINPUTS + if ((l=strlen(CWEBINPUTS))>max_file_name_length-2) too_long(); + strcpy(temp_file_name, CWEBINPUTS); +#else + l=0; +#endif /* |CWEBINPUTS| */ + } + if (l>0) { + if (k+l+2>=file_name_end) too_long(); + for (; k>= new_inc->file_name; k--) *(k+l+1)=*k; + strcpy(new_inc->file_name, temp_file_name); + new_inc->file_name[l]='/'; /* \UNIX/ pathname separator */ + if ((new_inc->the_file=fopen(new_inc->file_name, "r"))!=NULL) { + new_inc->parent=inp_desc->current_include; /* link it in */ + inp_desc->current_include=new_inc; + goto restart; /* success */ + } + } + total_include_files--; + free(new_inc); + err_print(i, "! Cannot open include file"); +@.Cannot open include file@> + goto restart; +} + + + +@* Reporting errors to the user. +There may be errors if a line in a given change +file does not match a line in the master file or a +replacement in a previous change file. Such errors are +reported to the user by saying +$$ + \hbox{|err_print(file_no, "! Error message")|;} +$$ +where |file_no| is the number of the file which is concerned by the +error. Please note that no trailing dot is supplied in the error +message because it is appended by |err_print|. + +@<Predecl...@>= +void err_print(); + +@ Here is the outline of the |err_print| function. + +@<Error handling...@>= +void err_print(i, s) /* prints `\..' and location of error message */ +file_index i; char *s; +{ + char *k, *l; /* pointers into an appropriate |buffer| */ + fprintf(stderr, *s=='!'? "\n%s" : "%s", s); + if(i>=0) @<Print error location based on input buffer@>@; + else putc('\n', stderr); + fflush(stderr); + history=troublesome; +} + +@ The error locations can be indicated by using the variables +|loc|, |line| and |file_name| within the appropriate file description +structures, which tell respectively the first +unlooked-at position in the |buffer|, the current line number and the +current file. We can determine whether we are looking at an included +file or not by examining the |current_include| variable. +This routine should be modified on systems whose standard text editor +has special line-numbering conventions. +@^system dependencies@> + +@<Print error location based on input buffer@>= +{ + register input_description *inp_desc=input_organisation[i]; + register include_description *inc_desc=inp_desc->current_include; + + if (inc_desc!=NULL) { + fprintf(stderr, ". (l. %ld of include file %s", inc_desc->line, + inc_desc->file_name); + fprintf(stderr, " included from l. %ld of %s file %s)\n", + inp_desc->line, + inp_desc->type_of_file==master?"master":"change", + inp_desc->file_name); + } + else + fprintf(stderr, ". (l. %ld of %s file %s)\n", inp_desc->line, + inp_desc->type_of_file==master?"master":"change", + inp_desc->file_name); + l=(inp_desc->loc>=inp_desc->limit? inp_desc->limit: inp_desc->loc); + if (l>inp_desc->buffer) { + for (k=inp_desc->buffer; k<l; k++) + if (*k=='\t') putc(' ', stderr); + else putc(*k, stderr); /* print the characters already read */ + putc('\n', stderr); + for (k=inp_desc->buffer; k<l; k++) + putc(' ', stderr); /* space out the next line */ + } + for (k=l; k<inp_desc->limit; k++) + putc(*k, stderr); /* print the part not yet read */ + putc('\n', stderr); +} + + +@ Non recoverable errors are handled by calling |fatal_error| that +outputs a message and then calls `|wrap_up|' and exits. |err_print| +will print the error message followed by an indication of where the +error was spotted in the source files. |fatal_error| cannot state any +files because the problem is usually to access these. + +@d fatal_error(i, s, t) { + fprintf(stderr, "\n%s", s); + err_print(i, t); + history=fatal; + exit(wrap_up()); + } + + +@ Some implementations may wish to pass the |history| value to the +operating system so that it can be used to govern whether or not other +programs are started. Here, for instance, we pass the operating system +a status of~0 if and only if only harmless messages were printed. +@^system dependencies@> + +@<Internal func...@>= +int wrap_up() +{ + @<Print the job |history|@>; + if (history > spotless) return 1; + else return 0; +} + +@ Always good to prototype. + +@<Predecl...@>= +int wrap_up(); + +@ We report the history to the user, although this may not +be ``\UNIX/'' style---but we are in good company: \.{WEB} and +\TEX/ do the same. We put this on |stdout| rather than |stderr|, so +that users can easily filter this away if they wish. +@^system dependencies@> + +@<Print the job |history|@>= +switch (history) { + case spotless: + printf("\n(No errors were found.)\n"); break; + case troublesome: + printf("\n(Pardon me, but I think I spotted something wrong.)\n"); break; + case fatal: printf("(That was a fatal error, my friend.)\n"); +} /* there are no other cases */ + + +@ If there's a system error, we may be able to give the user more +information with the |pfatal_error| function. This prints out system +error information if it is available. + +@<Predecl...@>= +void pfatal_error(); + +@ @<Error handling...@>= +void pfatal_error(s, t) +char *s, *t; +{ + char *strerr=strerror(errno); + + fprintf(stderr, "\n%s%s", s, t); + if (strerr) fprintf(stderr, " (%s)\n", strerr); + else fprintf(stderr, "\n"); + history=fatal; + exit(wrap_up()); +} + + +@ We need an include file for the above. + +@<Global \&{\#include}s@>= +#include <errno.h> + + +@* Handling multiple change files. +In the standard version we take the name of the +files from the command line. +It is assumed that filenames can be used as given in the +command line without changes. + +First there are some sections to open all files. +If a file is not accessible, the run will be aborted. +Otherwise the name of the open file will be displayed. + +@<Prepare the output file@>= +{ + out_file=fopen(out_name, "w"); + if (out_file==NULL) { + pfatal_error("! Cannot open/create output file", ""); +@.Cannot open/create output file@> + } +} + + +@ The name of the file and the file desciptor are stored in +global variables. + +@<Global variables@>= +FILE *out_file; +string out_name; + + +@ For the master file we start by reading its first line into the +buffer, if we could open it. + +@<Get the master file started@>= +{ + input_organisation[0]->the_file= + fopen(input_organisation[0]->file_name, "r"); + + if (input_organisation[0]->the_file==NULL) + pfatal_error("! Cannot open master file ", + input_organisation[0]->file_name); +@.Cannot open master file@> + printf("(%s)\n", input_organisation[0]->file_name); + input_organisation[0]->type_of_file=master; + get_line(0, true); +} + + +@ For the change files we must skip any comment part and see whether +there are any changes in it. This is done by |init_change_file|. + +@<Prepare the change files@>= +{ + file_index i; + + i=1; + while (i<no_ch) { + input_organisation[i]->the_file= + fopen(input_organisation[i]->file_name, "r"); + if (input_organisation[i]->the_file==NULL) + pfatal_error("! Cannot open change file ", + input_organisation[i]->file_name); +@.Cannot open change file@> + printf("(%s)\n", input_organisation[i]->file_name); + init_change_file(i); + i++; + } +} + + +@*Input/output organisation. +Here's a simple function that checks if two lines +are different. + +@<Internal functions@>= +boolean lines_dont_match(i, j) + file_index i, j; +{ + register input_description *iptr=input_organisation[i], + *jptr=input_organisation[j]; + + if (iptr->limit-iptr->buffer != jptr->limit-jptr->buffer) + return true; + return strncmp(iptr->buffer, jptr->buffer, iptr->limit-iptr->buffer); +} + + +@ Function |init_change_file(i)| is used to ignore all +lines of the input file with index~|i| until the next change +module is found. + +@<Internal functions@>= +void init_change_file(i) + file_index i; +{ + register input_description *inp_desc=input_organisation[i]; + char ccode; + + inp_desc->limit=inp_desc->buffer; + @<Skip over comment lines; |return| if end of file@>@; + @<Skip to the next nonblank line; |return| if end of file@>@; + inp_desc->dont_match=0; +} + + +@ While looking for a line that begins with \.{@@x} in the change +file, we allow lines that begin with \.{@@}, as long as they don't +begin with \.{@@y}, \.{@@z} or \.{@@i} (which would probably mean that +the change file is fouled up). + +@<Skip over comment lines...@>= +while(1) { + if (!get_line(i, false)) return; /* end of file reached */ + if (inp_desc->limit<inp_desc->buffer+2) continue; + if (inp_desc->buffer[0]!='@@') continue; + ccode=inp_desc->buffer[1]; + if (xisupper(ccode)) ccode=tolower(ccode); + if (ccode=='x') break; + if (ccode=='y' || ccode=='z' || ccode=='i') { + inp_desc->loc=inp_desc->buffer+2; + err_print(i, "! Missing @@x in change file"); +@.Missing @@x...@> + } +} + +@ Here we are looking at lines following the \.{@@x}. + +@<Skip to the next nonblank line...@>= +do { + if (!get_line(i, true)) { + err_print(i, "! Change file ended after @@x"); +@.Change file ended...@> + return; + } +} while (inp_desc->limit==inp_desc->buffer); + + +@ The |put_line| function is used to write a line from +input buffer |j| to the output file. + +@<Internal functions@>= +void put_line(j) + file_index j; +{ + char *ptr=input_organisation[j]->buffer; + char *lmt=input_organisation[j]->limit; + + while(ptr<lmt) putc(*ptr++, out_file); + putc('\n', out_file); +} + + +@ The function |e_of_ch_module| returns true if the input +line from file |i| starts with \.{@@z}. + +@<Internal functions@>= +boolean e_of_ch_module(i) + file_index i; +{ + register input_description *inp_desc=input_organisation[i]; + + if (inp_desc->limit==NULL) { + err_print(i, "! Change file ended without @@z"); +@.Change file ended without @@z@> + return true; + } else if (inp_desc->limit>=inp_desc->buffer+2) + if (inp_desc->buffer[0]=='@@' && + (inp_desc->buffer[1]=='Z' || inp_desc->buffer[1]=='z')) + return true; + return false; +} + + +@ The function |e_of_ch_preamble| returns |true| if the input +line from file~|i| starts with \.{@@y}. + +@<Internal functions@>= +boolean e_of_ch_preamble(i) + file_index i; +{ + register input_description *inp_desc=input_organisation[i]; + + if (inp_desc->limit>=inp_desc->buffer+2 && inp_desc->buffer[0]=='@@') + if (inp_desc->buffer[1]=='Y'||inp_desc->buffer[1]=='y') { + if (inp_desc->dont_match>0) { + inp_desc->loc=inp_desc->buffer+2; + fprintf(stderr, "\n! Hmm... %d ", inp_desc->dont_match); + err_print(i, "of the preceding lines failed to match"); + } + return true; + } + return false; +} + + + +@ To process the input file the next section reads a line of the +current (actual) input file and updates the |input_organisation| for +all files with index greater than |actual_input|. + +@<Process a line, |break| when end of source reached@>= +{ + file_index test_file; + + @<Check the current files for any ends of changes@>@; + if (input_has_ended && actual_input==0) break; /* all done */ + @<Scan all other files for changes to be done@>@; + @<Handle output@>@; + @<Step to next line@>@; +} + + +@ Any of the current change files may have reached the end of the +current change. In such a case, intermediate lines must be skipped +and the next start of change is to be found. This may make a change +file become inactive if the end of the file is reached. + +@<Check the...@>= +{ + register input_description *inp_desc; + while (actual_input>0 && e_of_ch_module(actual_input)) { + inp_desc=input_organisation[actual_input]; + if (inp_desc->type_of_file==master) { + /* emergency exit, everything mixed up!*/ + fatal_error(-1, "! This can't happen: change file is master file", ""); +@.This can't happen...@> + } + inp_desc->mode=search; + init_change_file(actual_input); + while ((input_organisation[actual_input]->mode!=reading + && actual_input>0)) + actual_input--; + } +} + + +@ Now we will set |test_input| to the first change file that is being +tested against the current line. If no other file is testing, then +|actual_input| refers to a line to write and |test_input| is set to +|none|. + +@d none (-1) + +@<Scan all other files...@>= +test_input=none; +test_file=actual_input; +while (test_input==none && test_file<no_ch-1){ + test_file++; + switch (input_organisation[test_file]->mode) { + case search: + if (lines_dont_match(actual_input, test_file)==false) { + input_organisation[test_file]->mode=test; + test_input=test_file; + } + break; + case test: + if (lines_dont_match(actual_input, test_file)) { + /* error, sections do not match; just note at this point */ + input_organisation[test_file]->dont_match++; + } + test_input=test_file; + break; + case reading: /* this can't happen */ + break; + case ignore: /* nothing to do */ + break; + } +} + + +@ For the output we must distinguish between whether we are creating a +new change file or a new master file. Change file creation requires +closer inspection because we may be before a change, in the pattern +(match) part or in the replacement part. For master file creation, we +simply have to write the line from the current (actual) input. + +@<Handle output@>= +if (prod_chf==chf) { + while(1) { + @<Test for |normal|, |break| when done@>@; + @<Test for |pre|, |break| when done@>@; + @<Test for |post|, |break| when done@>@; + } +} else + if (test_input==none) put_line(actual_input); + + +@ Check whether we have to start a change file entry. +Without a match nothing needs to be done. + +@<Test for |normal|...@>= +if (out_mode==normal) { + if (test_input!=none) { + fprintf(out_file, "@@x\n"); + out_mode=pre; + } else break; +} + + +@ Check whether we have to start the replacement text. This is the +case when we are in |pre| mode but have no more matching lines. +Otherwise the master file source line must be copied to the change +file. + +@<Test for |pre|...@>= +if (out_mode==pre) { + if (test_input==none) { + fprintf(out_file, "@@y\n"); + out_mode=post; + } else { + if (input_organisation[actual_input]->type_of_file==master) + put_line(actual_input); + break; + } +} + + +@ Check whether an entry from a change file is complete. If the +current input is from a change file which is not being tested against +a later change file, then this change file line must be written. If +the actual input has been reset to the master file, we can finish this +change. + +@<Test for |post|...@>= +if (out_mode==post) { + if (input_organisation[actual_input]->type_of_file==chf) { + if (test_input==none) put_line(actual_input); + break; + } else { + fprintf(out_file, "@@z\n\n"); + out_mode=normal; + } +} + + +@ If we had a change, we must proceed in the actual file +to be changed and in the change file in effect. + +@<Step to next line@>= +get_line(actual_input, true); +if (test_input!=none) { + get_line(test_input, true); + if (e_of_ch_preamble(test_input)==true) { + get_line(test_input, true); /* update current changing file */ + input_organisation[test_input]->mode=reading; + actual_input=test_input; + test_input=none; + } +} + + +@ To create the new output file we have to scan the whole +master file and all changes in effect when it ends. +At the very end it is wise to check for all changes +to have completed, in case the last line of the master file +was to be changed. + +@<Process the input@>= +actual_input=0; +input_has_ended=false; +while (input_has_ended==false||actual_input!=0) + @<Process a line...@>@; +if (out_mode==post) /* last line has been changed */ + fprintf(out_file, "@@z\n"); + + +@ At the end of the program, we will tell the user if the +change file had a line that didn't match any relevant line +in the master file or any of the change files. + +@<Check that all changes have been read@>= +{ + file_index i; + + for (i=1;i<no_ch;i++) { /* all change files */ + if (input_organisation[i]->mode!=ignore) { + input_organisation[i]->loc=input_organisation[i]->buffer; + err_print(i, "! Change file entry did not match"); +@.Change file entry ...@> + } + } +} + + +@ We want to tell the user about our command line options if they made +a mistake. This is done by the |usage_error()| function. It contains +merely the necessary print statements and exits afterwards. + +@<Intern...@>= +void usage_error() +{ + @<Print the banners@>; + fprintf(stderr, "Usage: ctie -[mc] outfile master changefile(s)\n"); + fprintf(stderr, "Type ctie --help for more information\n"); + exit(1); +} + + +@ Printing our welcome banners; we only do this if we are not asked +for version or help information. + +@<Print the banners@>= +printf("%s\n", banner); /* print a ``banner line'' */ +printf("%s\n", copyright); /* include the copyright notice */ + + +@ We must scan through the list of parameters, given in |argv|. The +number is in |argc|. We must pay attention to the flag parameter. We +need at least 3~parameters (\.{-m} or \.{-c}, an output file and a +master file) and can handle up to |max_file_index| change files. The +names of the file parameters will be inserted into the structure of +|input_organisation|. The first file is special. It indicates the +output file. When we allow flags at any position, we must find out +which name is for what purpose. The master file is already part of +the |input_organisation| structure (index~0). As long as the number +of files found (counted in |no_ch|) is |-1| we have not yet found the +output file name. + +@<Scan the parameters@>= +{ + if (argc>max_file_index+5-1) usage_error(); + no_ch = -1; /* fill this part of |input_organisation| */ + while (--argc>0) { + argv++; + if (strcmp("-help", *argv)==0 || strcmp("--help", *argv)==0) + @<Display help message and exit@>; + if (strcmp("-version", *argv)==0 || strcmp("--version", *argv)==0) + @<Display version information and exit@>; + if (**argv=='-') @<Set a flag@>@; + else @<Get a file name@>@; + } + if (no_ch<=0|| prod_chf==unknown) usage_error(); +} + + +@ The flag is about to determine the processing mode. +We must make sure that this flag has not been set before. +Further flags might be introduced to avoid/force overwriting of +output files. +Currently we just have to set the processing flag properly. + +@<Set a flag@>= +if (prod_chf!=unknown) usage_error(); +else + switch (*(*argv+1)) { + case 'c': case 'C': prod_chf=chf; break; + case 'm': case 'M': prod_chf=master; break; + default: usage_error(); + } + + +@ We have to distinguish whether this is the very first file name +(which is the case if |no_ch==(-1)|) or if the next element of +|input_organisation| must be filled. + +@<Get a file name@>= +{ + if (no_ch==(-1)) { + out_name=*argv; + } else { + register input_description *inp_desc; + + inp_desc=(input_description *) malloc(sizeof(input_description)); + if (inp_desc==NULL) + fatal_error(-1, "! No memory for input descriptor", ""); +@.No memory for descriptor@> + inp_desc->mode=search; + inp_desc->line=0; + inp_desc->type_of_file=chf; + inp_desc->limit=inp_desc->buffer; + inp_desc->buffer[0]=' '; + inp_desc->loc=inp_desc->buffer+1; + inp_desc->buffer_end=inp_desc->buffer+buf_size-2; + inp_desc->file_name=*argv; + inp_desc->current_include=NULL; + input_organisation[no_ch]=inp_desc; + } + no_ch++; +} + + +@ Modules for dealing with help messages and version info. We follow +the \.{kpathsea} standard code here, so that we can easily adapt this +to work with \.{kpathsea}. + +@<Display help message and exit@>= +usage_help(); +@.--help@> + + +@ +@<Display version information and exit@>= +{ + print_version_and_exit("CTIE", version_number); +@.--version@> +} + + +@ Here is the usage information for \.{--help}. + +@<Global variables@>= +string CTIEHELP[] = { + "Usage: ctie -[mc] outfile master changefile(s)", + " Create a new master file or change file from the given", + " master (C)WEB file and changefiles.", + " All filenames are taken literally; no suffixes are added.", + "", + "-m create a new master file from original (C)WEB and change file(s)", + "-c create a master change file for original (C)WEB file from changefile(s)", + "--help display this help and exit", + "--version display version information and exit", + NULL +}; + + +@ @<Predec...@>= +void usage_help(); +void print_version_and_exit(); + + +@ @c +void usage_help() +{ + string *message=CTIEHELP; + + while (*message) { + fputs(*message, stdout); + putchar('\n'); + ++message; + } + putchar('\n'); + exit(0); +} + + +@ @c +void print_version_and_exit(name, version) + string name, version; +{ + printf ("%s %s\n", name, version); + + puts ("Copyright (C) 2002,2003 Julian Gilbey."); + + puts ("There is NO warranty. This is free software. See the source"); + puts ("code of CTIE for redistribution conditions."); + + exit (0); +} + + +@* System-dependent changes. +This section should be replaced, if necessary, by +changes to the program that are necessary to make \.{CTIE} +work at a particular installation. It is usually best to +design your change file so that all changes to previous +modules preserve the module numbering; then everybody's +version will be consistent with the printed program. More +extensive changes, which introduce new modules, can be +inserted here; then only the index itself will get a new +module number. +@^system dependencies@> + + +@* Index. diff --git a/web/c_cpp/ctie/ctiedoc.tex b/web/c_cpp/ctie/ctiedoc.tex new file mode 100644 index 0000000000..972bfff44d --- /dev/null +++ b/web/c_cpp/ctie/ctiedoc.tex @@ -0,0 +1,242 @@ +% ctiedoc.tex Jan 2002, last updated Apr 2003 +%----------------------------------------------- + +% A minor rewrite of tiedoc.tex for the CTIE changes. + + +\documentclass{article} + +\let\mc=\small +\def\CEE/{{\mc C\spacefactor1000}} +\let\.=\texttt + +\begin{document} + +\begin{center} +\Large The \.{CTIE} Processor + +\bigskip +\large +Julian Gilbey\\ +\.{jdg@debian.org} + +\bigskip +Based on the original \.{TIE} documentation by:\\ +Klaus Guntermann\\ +TH Darmstadt\\ +Fachbereich Informatik\\ +Institut f\"ur Theoretische Informatik + +\bigskip +January 2002, updated April 2003 + +\end{center} + +\bigskip + +\section{Introduction} + +When installing a program on a particular computer system, certain +system dependent changes are often needed. The original \.{WEB} +programs (\.{WEAVE} and \.{TANGLE}) handled this problem by using +\textit{change files}, which work very much like a patch file and are +read by \.{WEAVE} and \.{TANGLE} along with the main \.{WEB} file. +This allows the original \.{WEB} file to remain intact and the +particular system dependencies to be isolated. + +Sometimes it is good practice to develop a set of multiple change +files for a given \.{WEB} file to distinguish between different +changes such as program enhancements, bug fixes, system dependent +changes and output device dependent modifications. Additionally this +allows combinations of changes that can be used with a set of programs +that share some common features, such as \.{WEAVE} and \.{TANGLE} +themselves. + +Unfortunately, the processors \.{TANGLE} and \.{WEAVE} can handle only +one change file. Rather than modifying these processors to handle +multiple change files, it is easier to combine them in a preprocessing +step. The \.{TIE} processor was written to perform this task. It is +able to create either a new master file or a single change file that +comprises the effect of all change files. + +The \.{CWEB} system was later developed in order to open up the +\.{WEB} system of literate programming to \CEE/ programmers. It +functions in an essentially identical way to the original Pascal +\.{WEB} system, and the syntax of change files is the same. So it +should be possible to use the \.{TIE} processor for \.{CWEB} source +files. However, one significant enhancement was made to \.{CWEB} +which makes this sometimes impossible. \.{CWEB} supports +\textit{include files}, which are introduced by a line beginning +`\.{@i} \textit{filename}', which includes the \.{CWEB} file +specified, just like the \CEE/ preprocessor command \.{\#include}. As +these include commands are effectively expanded before the change file +is applied, both in the master file and the change file, it is clear +that \.{TIE} needs to interpret \.{@i} commands in order to work for +all \.{CWEB} files. Thus \.{CTIE} was born: it essentially combines +the traditional behaviour of \.{TIE} with the ability to handle +\.{CWEB} include files. + + +\section{Application} + +The current version of \.{CTIE} accepts a master file and up to +32~change files. In general it is important to use the change files +in the correct sequence, as later change files may modify the results +of applying earlier changes. Thus the order of the change files on +the command line is taken to specify the order in which the change +files should be applied. Conceptually, each change file is applied to +the master file in turn to create a new master file, to which the next +change file is applied; in practice, though, a more space-efficient +algorithm is used which only requires storage space for one line per +file, so the lengths of the individual files or changes are irrelevant +for storage requirements. + +Note that \.{CTIE} will expand all \.{@i} include lines, including the +specified files whether or not they are modified by any change files. +So another application of \.{CTIE} is to simply expand all of these +include files, producing an expanded master file. (Thanks to Hartmut +Henkel \.{<hartmut\_henkel@gmx.de>} for this observation.) + +\.{CTIE} must be called with at least 3~parameters as follows: +$$\texttt{ctie -[cm] outfile master changefile(s)}$$ +where the parameters are (in order): +\begin{enumerate} + +\item Either the option \.{-c} or \.{-m}. This option determines + whether an amalgamated change file or a new master file is created. + +\item The name of the output file. + +\item The name of the master file. + +\item The name(s) of the change file(s), if any. + +\end{enumerate} + +Unlike the \.{CWEB} programs \.{CWEAVE} and \.{CTANGLE}, no attempt to +add default suffixes such as \.{.w} or \.{.ch} is made; the filenames +must be the full filenames. If \.{CTIE} has been compiled with +\.{kpathsea} support, then the \.{kpathsea} library will be used to +search for the files using the \.{CWEBINPUTS} variable, otherwise just +the current directory and the directory specified by the +\.{CWEBINPUTS} environment variable will be searched, in that order. + +Also, \.{CTIE} can be called with the single argument \.{--help} or +\.{--version} to get basic help or version information. + +\clearpage + + +\def\eof{$\langle$\textrm{\mc EOF}$\rangle$} +\tabcolsep=1em +\parindent=0pt + +\section{Example} + +To illustrate the actions \.{CTIE} performs you may inspect the +following example that exercises some of the borderline cases. + +\begin{center} +\ttfamily +\begin{tabular}{llll} +\multicolumn1{c}{\textbf{\textrm{ctie.tie}}}& + \multicolumn1{c}{\textbf{\textrm{ctie.cf1}}}& + \multicolumn1{c}{\textbf{\textrm{ctie.cf2}}}& + \multicolumn1{c}{\textbf{\textrm{ctie.cf3}}}\\[1pt] +\hline +line 1 &@x &@x &@x\\ +line 2 &line 2 &line 1 &changed line 4\\ +line 3 &line 3 &changed line 2 &@y\\ +line 4 &@y &changed line 3 &final line 4\\ +line 5 &changed line 2 &inserted line &@z\\ +@i ctie.inc &changed line 3 &line 4\\ +line 6 &inserted line &@y &@x\\ +line 7 &@z &final line 2 &changed inc line 1\\ +line 8 & &final line 3 &inserted inc line\\ +line 9 &@x &changed line 4 &@y\\ +line 10 &line 7 &@z &final inc line 1\\ +\eof &@y & &@i "ctie.inc2"\\ + &changed line 7 &@x &@z\\ + &@z &inc line 1 &\eof\\ + &\eof &@y\\ + & &changed inc line 1\\ + & &inserted inc line\\ + & &@z\\ + & &\\ + & &@x\\ + & &changed line 7\\ + & &line 8\\ + & &@y\\ + & &final line 7\\ + & &final line 8\\ + & &@z\\ + & &\eof\\ +\end{tabular} +\end{center} + +The included files are as follows: + +\begin{center} +\ttfamily +\begin{tabular}{lll} +\multicolumn1{c}{\textbf{\textrm{ctie.inc}}}& + \multicolumn1{c}{\textbf{\textrm{ctie.inc1}}}& + \multicolumn1{c}{\textbf{\textrm{ctie.inc2}}}\\[1pt] +\hline +inc line 1 &included inc line &final inserted inc line\\ +inc line 2 &\eof &\eof\\ +@i ctie.inc1\\ +inc line 3\\ +\eof\\ +\end{tabular} +\end{center} + +\clearpage + +Using these input files and running \.{CTIE} with the following +commands to create a new master file and a new change file will result +in the following output files. + +\begin{tabbing} + \quad For the master file:\qquad\=\texttt{ctie -m ctie.outm ctie.cf1 + ctie.cf2 ctie.cf3}\\ + \quad For the change file:\>\texttt{ctie -c ctie.outc ctie.cf1 + ctie.cf2 ctie.cf3} +\end{tabbing} + +\begin{center} +\ttfamily +\begin{tabular}{ll} +\multicolumn1{c}{\textbf{\textrm{ctie.outm}}}& + \multicolumn1{c}{\textbf{\textrm{ctie.outc}}}\\[1pt] +\hline +final line 2 &@x\\ +final line 3 &line 1\\ +final line 4 &line 2\\ +line 5 &line 3\\ +final inc line 1 &line 4\\ +final inserted inc line &@y\\ +inc line 2 &final line 2\\ +included inc line &final line 3\\ +inc line 3 &final line 4\\ +line 6 &@z\\ +final line 7 &\\ +final line 8 &@x\\ +line 9 &inc line 1\\ +line 10 &@y\\ + &final inc line 1\\ + &final inserted inc line\\ + &@z\\ + &\\ + &@x\\ + &line 7\\ + &line 8\\ + &@y\\ + &final line 7\\ + &final line 8\\ + &@z\\ +\end{tabular} +\end{center} + + +\end{document} diff --git a/web/c_cpp/cweb/Makefile b/web/c_cpp/cweb/Makefile new file mode 100644 index 0000000000..1367d00892 --- /dev/null +++ b/web/c_cpp/cweb/Makefile @@ -0,0 +1,197 @@ +# This file is part of CWEB. +# It is distributed WITHOUT ANY WARRANTY, express or implied. +# Version 3.64 --- February 2002 + +# 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. + +# +# Read the README file, then edit this file to reflect local conditions +# + +# directory for TeX inputs (cwebmac.tex goes here) +MACROSDIR= /usr/share/texmf/tex/generic + +# directory for CWEB inputs in @i files +CWEBINPUTS= /usr/local/lib/cweb + +# extension for manual pages ("l" distinguishes local from system stuff) +MANEXT= l +#MANEXT= 1 + +# directory for manual pages (cweb.1 goes here) +MANDIR= /usr/share/man/man$(MANEXT) + +# destination directory for executables; must end in / +DESTDIR= /usr/local/bin/ + +# directory for GNU EMACS Lisp code (cweb.el goes here) +EMACSDIR= /usr/share/emacs/site-lisp + +# Set DESTPREF to null if you want to call the executables "tangle" and "weave" +# (probably NOT a good idea; we recommend leaving DESTPREF=c) +DESTPREF=c + +# Set CCHANGES to comm-foo.ch if you need changes to common.w +CCHANGES= + +# Set TCHANGES to ctang-foo.ch if you need changes to ctangle.w +TCHANGES= + +# Set WCHANGES to cweav-foo.ch if you need changes to cweave.w +WCHANGES= + +# We keep debugging info around, for fun, but most users don't need it +CFLAGS = -g +#CFLAGS = -O +LINKFLAGS = -g +#LINKFLAGS = -s # for smaller (stripped) executables on many UNIX systems + +# What C compiler are you using? +CC = cc + +# RM and CP are used below in case rm and cp are aliased +RM= /bin/rm +CP= /bin/cp + +# uncomment the second line if you use pdftex to bypass .dvi files +PDFTEX = dvipdfm +#PDFTEX = pdftex + +########## You shouldn't have to change anything after this point ####### + +CWEAVE = ./cweave +CTANGLE = ./ctangle +SOURCES = cweave.w common.w ctangle.w +ALL = common.w ctangle.w cweave.w prod.w \ + Makefile common.c common.h ctangle.c \ + cwebman.tex cwebmac.tex comm-vms.ch ctang-vms.ch \ + cweav-vms.ch comm-man.ch ctang-man.ch cweav-man.ch \ + comm-pc.ch ctang-pc.ch cweav-pc.ch comm-amiga.ch \ + comm-bs.ch ctang-bs.ch cweav-bs.ch makefile.bs \ + comm-ql.ch ctang-ql.ch cweav-ql.ch readme.ql \ + comm-w32.ch ctang-w32.ch cweav-w32.ch \ + comm-os2.ch comm-mac.ch cweb.1 cweb.el c++lib.w README + +.SUFFIXES: .dvi .tex .w .pdf + +.w.tex: + $(CWEAVE) $* + +.tex.dvi: + tex $< + +.w.dvi: + make $*.tex + make $*.dvi + +.w.c: + $(CTANGLE) $* + +.w.o: + make $*.c + make $*.o + +.w.pdf: + make $*.tex + case "$(PDFTEX)" in \ + dvipdfm ) tex "\let\pdf+ \input $*"; dvipdfm $* ;; \ + pdftex ) pdftex $* ;; \ + esac + +all: ctangle cweave + +cautiously: ctangle + $(CP) common.c SAVEcommon.c + ./ctangle common $(CCHANGES) + diff common.c SAVEcommon.c + $(RM) SAVEcommon.c + $(CP) ctangle.c SAVEctangle.c + ./ctangle ctangle $(TCHANGES) + diff ctangle.c SAVEctangle.c + $(RM) SAVEctangle.c + +SAVEctangle.c: + $(CP) ctangle.c SAVEctangle.c + +SAVEcommon.c: + $(CP) common.c SAVEcommon.c + +common.c: common.w $(CCHANGES) + $(CTANGLE) common $(CCHANGES) + +common.o: common.c + $(CC) $(CFLAGS) -DCWEBINPUTS=\"$(CWEBINPUTS)\" -c common.c + +ctangle: ctangle.o common.o + $(CC) $(LINKFLAGS) -o ctangle ctangle.o common.o + +ctangle.c: ctangle.w $(TCHANGES) + $(CTANGLE) ctangle $(TCHANGES) + +cweave: cweave.o common.o + $(CC) $(LINKFLAGS) -o cweave cweave.o common.o + +cweave.c: cweave.w $(WCHANGES) + $(CTANGLE) cweave $(WCHANGES) + +doc: $(SOURCES:.w=.dvi) + +usermanual: cwebman.tex cwebmac.tex + tex cwebman + +fullmanual: usermanual $(SOURCES) comm-man.ch ctang-man.ch cweav-man.ch + make cweave + ./cweave common.w comm-man.ch + tex common.tex + ./cweave ctangle.w ctang-man.ch + tex ctangle.tex + ./cweave cweave.w cweav-man.ch + tex cweave.tex + +# be sure to leave ctangle.c and common.c for bootstrapping +clean: + $(RM) -f -r *~ *.o common.tex cweave.tex cweave.c ctangle.tex \ + *.log *.dvi *.toc *.idx *.scn *.pdf core cweave ctangle + +install: all + - mkdir $(DESTDIR) + $(CP) cweave $(DESTDIR)$(DESTPREF)weave + chmod 755 $(DESTDIR)$(DESTPREF)weave + $(CP) ctangle $(DESTDIR)$(DESTPREF)tangle + chmod 755 $(DESTDIR)$(DESTPREF)tangle + - mkdir $(MANDIR) + $(CP) cweb.1 $(MANDIR)/cweb.$(MANEXT) + chmod 644 $(MANDIR)/cweb.$(MANEXT) + - mkdir $(MACROSDIR) + $(CP) cwebmac.tex $(MACROSDIR) + chmod 644 $(MACROSDIR)/cwebmac.tex + - mkdir $(EMACSDIR) + $(CP) cweb.el $(EMACSDIR) + chmod 644 $(EMACSDIR)/cweb.el + - mkdir $(CWEBINPUTS) + $(CP) c++lib.w $(CWEBINPUTS) + chmod 644 $(CWEBINPUTS)/c++lib.w + +floppy: $(ALL) examples + bar cvhf /dev/rfd0 $(ALL) examples + bar tvf /dev/rfd0 + eject + +tags: $(ALL) + etags -lnone $(ALL) + +tarfile: $(ALL) examples + tar cvhf /tmp/cweb.tar $(ALL) examples + gzip -9 /tmp/cweb.tar + +tarball: + tar zcvhf /tmp/cweb.tgz $(ALL) examples diff --git a/web/c_cpp/cweb/README b/web/c_cpp/cweb/README new file mode 100644 index 0000000000..6429dabfa8 --- /dev/null +++ b/web/c_cpp/cweb/README @@ -0,0 +1,110 @@ +% This file is part of CWEB. +% The CWEB programs by Silvio Levy are based on programs by D. E. Knuth. +% They are distributed WITHOUT ANY WARRANTY, express or implied. +% This README file last updated January 2016 by Don Knuth + +% Copyright (C) 1987,1990,1993,2000,2016 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. + +% Please send comments, suggestions, etc. to tex-k@tug.org, and people +% there will verify and forward bug reports. +% +% DEK takes no responsibility for the changefiles; they should be +% maintained independently. His job is to correct errors in +% ctangle.w, cweave.w, prod.w, common.w, cwebmac.tex, cwebman.tex, +% and in the files of examples/*.w, nothing more. + +This directory contains the following files: + +Makefile +README +comm-amiga.ch +comm-bs.ch +comm-mac.ch +comm-man.ch +comm-pc.ch +comm-ql.ch +comm-vms.ch +comm-w32.ch +common.c +common.h +common.w +ctang-bs.ch +ctang-man.ch +ctang-pc.ch +ctang-ql.ch +ctang-vms.ch +ctang-w32.ch +ctangle.c +ctangle.w +cweav-bs.ch +cweav-man.ch +cweav-pc.ch +cweav-ql.ch +cweav-vms.ch +cweave-w32.ch +cweave.w +cweb.1 +cweb.el +cwebmac.tex +cwebman.tex +examples/ +makefile.bs +prod.w +readme.ql +c++lib.w + +The file cwebman.tex is the user manual. +The examples directory contains additional examples of the use of CWEB. +The files common.c and ctangle.c are used for bootstrapping. +The file cweb.1 is a manual page. +The file cweb.el is suggested for GNU-Emacs users. +The file c++lib.w is for C++ users (say `@i c++lib.w' at beginning of program). +The files *-man.ch are used if you want to make the full 239-page CWEB manual. +The files *-bs.ch are used instead of *-pc.ch if you are doing BIG programs. +You can use makefile.bs to make CWEB with *-bs.ch. +The files *-ql.ch are for QDOS/SMSQ systems; see readme.ql for further info. +The files *-w32.ch use __fastcall conventions on win32 systems. +The file comm-mac.ch is for Macintosh conventions. +The other files named *.ch are sample change files for local customization. + +IMPORTANT: Please touch *.c before proceeding. +Then edit the opening lines of Makefile so that it has the proper +directory information for your local system. + +To make ctangle and cweave say `make all'; this should produce roughly +the following actions (possibly with harmless warning messages from cc): + +cc -g -w -c ctangle.c +cc -g -w -DCWEBINPUTS=\"/usr/local/lib/cweb\" -c common.c +cc -g -o ctangle ctangle.o common.o +./ctangle cweave +cc -g -w -c cweave.c +cc -g -w -o cweave cweave.o common.o + +To get some reassurance that things are OK, you can say `make cautiously', +which ensures that CTANGLE will reproduce itself. (Otherwise the source +files common.w and ctangle.w won't actually have been used.) + +To install cweave and ctangle say `make install'. You probably need to +be superuser to do this; but it's wise to `make all' first, BEFORE +becoming superuser and saying `make install'. + +Note that change files for VMS, AMIGA, MAC, and PCs are provided. When you +are first bootstrapping to a new system, you may need to edit common.c and +ctangle.c by hand, but the vast majority of the change-file changes are +minor refinements that are not necessary for a rudimentary ctangle. + +----------------------------------------- +These archival sources are maintained only to the extent of fixing +significant bugs that were unintended at the time of writing. +A multi-decade ongoing project to improve and extend CWEB can be found at + https://github.com/ascherer/cwebbin diff --git a/web/c_cpp/cweb/c++lib.w b/web/c_cpp/cweb/c++lib.w new file mode 100644 index 0000000000..15e59cc885 --- /dev/null +++ b/web/c_cpp/cweb/c++lib.w @@ -0,0 +1,85 @@ +@q This file defines standard C++ namespaces and classes @> +@q Please send corrections to saroj-tamasa@@worldnet.att.net @> +@s std int +@s rel_ops int +@s bitset int +@s char_traits int +@s deque int +@s list int +@s map int +@s multimap int +@s multiset int +@s pair int +@s set int +@s stack int +@s exception int +@s logic_error int +@s runtime_error int +@s domain_error int +@s invalid_argument int +@s length_error int +@s out_of_range int +@s range_error int +@s overflow_error int +@s underflow_error int +@s back_insert_iterator int +@s front_insert_iterator int +@s insert_iterator int +@s reverse_iterator int +@s istream_iterator int +@s ostream_iterator int +@s istreambuf_iterator int +@s ostreambuf_iterator int +@s iterator_traits int +@s queue int +@s vector int +@s basic_string int +@s string int +@s auto_ptr int +@s valarray int +@s ios_base int +@s basic_ios int +@s basic_streambuf int +@s basic_istream int +@s basic_ostream int +@s basic_iostream int +@s basic_stringbuf int +@s basic_istringstream int +@s basic_ostringstream int +@s basic_stringstream int +@s basic_filebuf int +@s basic_ifstream int +@s basic_ofstream int +@s basic_fstream int +@s ctype int +@s collate int +@s collate_byname int +@s streambuf int +@s istream int +@s ostream int +@s iostream int +@s stringbuf int +@s istringstream int +@s ostringstream int +@s stringstream int +@s filebuf int +@s ifstream int +@s ofstream int +@s fstream int +@s wstreambuf int +@s wistream int +@s wostream int +@s wiostram int +@s wstringbuf int +@s wistringstream int +@s wostringstream int +@s wstringstream int +@s wfilebuf int +@s wifstream int +@s wofstream int +@s wfstream int +@s streamoff int +@s streamsize int +@s fpos int +@s streampos int +@s wstreampos int diff --git a/web/c_cpp/cweb/comm-amiga.ch b/web/c_cpp/cweb/comm-amiga.ch new file mode 100644 index 0000000000..57e65d1ccd --- /dev/null +++ b/web/c_cpp/cweb/comm-amiga.ch @@ -0,0 +1,17 @@ +This is the change file for CWEB's COMMON on the Amiga +(Contributed by Tomas Rokicki, June 1993) + +With Lattice C 5.1, use compilation switches -b0 -r0, and ignore the +compiler warnings. With SAS 6.0, use compilation switches Code=far Data=far. + +@x section 69 +An omitted change file argument means that |"/dev/null"| should be used, +@y +An omitted change file argument means that |"nil:"| should be used, +@z + +@x section 70 + if (found_change<=0) strcpy(change_file_name,"/dev/null"); +@y + if (found_change<=0) strcpy(change_file_name,"nil:"); +@z diff --git a/web/c_cpp/cweb/comm-bs.ch b/web/c_cpp/cweb/comm-bs.ch new file mode 100644 index 0000000000..26f79b160d --- /dev/null +++ b/web/c_cpp/cweb/comm-bs.ch @@ -0,0 +1,229 @@ +Changes for code common to CTANGLE and CWEAVE, for MSDOS +and Borland C++ 3.1 using the following options (and perhaps others): + + -mc -w-pro -Ff=5000 -Z- -O-p + +The options -Z- and -O-p explicitly turn off optimizations that seem to be +dangerous for the style of code in the CWEB sources. (See makefile.bs.) + +The main purpose of these changes is to support MSDOS with full-size arrays +by using "huge" pointers. + +(This file contributed by Barry Schwartz, trashman@crud.mn.org, 28 Jun 94; + revised 24 Jul 94.) + + +@x Section 23. + cur_file_name[l]='/'; /* \UNIX/ pathname separator */ +@y + cur_file_name[l]='/'; /* A valid {\mc MSDOS} pathname separator */ +@z + + +@x Section 27. +@d max_names 4000 /* number of identifiers, strings, section names; + must be less than 10240 */ + +@<Definitions that...@>= +typedef struct name_info { + char *byte_start; /* beginning of the name in |byte_mem| */ + @<More elements of |name_info| structure@>@; +} name_info; /* contains information about an identifier or section name */ +typedef name_info *name_pointer; /* pointer into array of |name_info|s */ +char byte_mem[max_bytes]; /* characters of names */ +char *byte_mem_end = byte_mem+max_bytes-1; /* end of |byte_mem| */ +name_info name_dir[max_names]; /* information about names */ +name_pointer name_dir_end = name_dir+max_names-1; /* end of |name_dir| */ +@y +@d max_names 4000 /* number of identifiers, strings, section names; + must be less than 10240 */ + +@f huge extern + +@<Definitions that...@>= +typedef struct name_info { + char huge* byte_start; /* beginning of the name in |byte_mem| */ + @<More elements of |name_info| structure@>@; +} name_info; /* contains information about an identifier or section name */ +typedef name_info *name_pointer; /* pointer into array of |name_info|s */ +char huge byte_mem[max_bytes]; /* characters of names */ +char huge* byte_mem_end; /* end of |byte_mem| */ +name_info name_dir[max_names]; /* information about names */ +name_pointer name_dir_end = name_dir+max_names-1; +@z + + +@x Section 29. +char *byte_ptr; /* first unused position in |byte_mem| */ +@y +char huge* byte_ptr; /* first unused position in |byte_mem| */ +@z + + +@x Section 30. +@ @<Init...@>= +name_dir->byte_start=byte_ptr=byte_mem; /* position zero in both arrays */ +name_ptr=name_dir+1; /* |name_dir[0]| will be used only for error recovery */ +name_ptr->byte_start=byte_mem; /* this makes name 0 of length zero */ +@y +@ @<Init...@>= +name_dir->byte_start=byte_ptr=byte_mem; /* position zero in both arrays */ +name_ptr=name_dir+1; /* |name_dir[0]| will be used only for error recovery */ +name_ptr->byte_start=byte_mem; /* this makes name 0 of length zero */ +byte_mem_end = byte_mem+max_bytes-1; +@z + + +@x Section 42. +void +print_section_name(p) +name_pointer p; +{ + char *ss, *s = first_chunk(p); +@y +void +print_section_name(p) +name_pointer p; +{ + char huge* ss, huge* s = first_chunk(p); +@z + + +@x Section 43. +void +sprint_section_name(dest,p) + char*dest; + name_pointer p; +{ + char *ss, *s = first_chunk(p); +@y +void +sprint_section_name(dest,p) + char*dest; + name_pointer p; +{ + char huge* ss, huge* s = first_chunk(p); +@z + + +@x Section 44. +void +print_prefix_name(p) +name_pointer p; +{ + char *s = first_chunk(p); +@y +void +print_prefix_name(p) +name_pointer p; +{ + char huge* s = first_chunk(p); +@z + + +@x Section 47. +name_pointer +add_section_name(par,c,first,last,ispref) /* install a new node in the tree */ +name_pointer par; /* parent of new node */ +int c; /* right or left? */ +char *first; /* first character of section name */ +char *last; /* last character of section name, plus one */ +int ispref; /* are we adding a prefix or a full name? */ +{ + name_pointer p=name_ptr; /* new node */ + char *s=first_chunk(p); +@y +name_pointer +add_section_name(par,c,first,last,ispref) /* install a new node in the tree */ +name_pointer par; /* parent of new node */ +int c; /* right or left? */ +char *first; /* first character of section name */ +char *last; /* last character of section name, plus one */ +int ispref; /* are we adding a prefix or a full name? */ +{ + name_pointer p=name_ptr; /* new node */ + char huge* s=first_chunk(p); +@z + + +@x Section 48. +void +extend_section_name(p,first,last,ispref) +name_pointer p; /* name to be extended */ +char *first; /* beginning of extension text */ +char *last; /* one beyond end of extension text */ +int ispref; /* are we adding a prefix or a full name? */ +{ + char *s; +@y +void +extend_section_name(p,first,last,ispref) +name_pointer p; /* name to be extended */ +char *first; /* beginning of extension text */ +char *last; /* one beyond end of extension text */ +int ispref; /* are we adding a prefix or a full name? */ +{ + char huge* s; +@z + + +@x Section 54. +int section_name_cmp(pfirst,len,r) +char **pfirst; /* pointer to beginning of comparison string */ +int len; /* length of string */ +name_pointer r; /* section name being compared */ +{ + char *first=*pfirst; /* beginning of comparison string */ + name_pointer q=r+1; /* access to subsequent chunks */ + char *ss, *s=first_chunk(r); +@y +int section_name_cmp(pfirst,len,r) +char **pfirst; /* pointer to beginning of comparison string */ +int len; /* length of string */ +name_pointer r; /* section name being compared */ +{ + char *first=*pfirst; /* beginning of comparison string */ + name_pointer q=r+1; /* access to subsequent chunks */ + char huge* ss, huge* s=first_chunk(r); +@z + + +@x Section 55. +source files, respectively; here we just declare a common field +|equiv_or_xref| as a pointer to a |char|. + +@<More elements of |name...@>= +char *equiv_or_xref; /* info corresponding to names */ +@y +source files, respectively. Here we just declare a common field +|ptr_union| as a union of pointers to |char|, which happen to have +different addressing attributes. + +@<More elements of |name...@>= +union { + char *equiv_member; + char huge* xref_member; +} ptr_union; /* info corresponding to names */ +@z + + +@x Section 69. +An omitted change file argument means that |"/dev/null"| should be used, +@y +An omitted change file argument means that |"NUL"| should be used, +@z + + +@x Section 70. + else if (*s=='/') dot_pos=NULL,name_pos=++s; +@y + else if (*s == ':' || *s == '\\' || *s == '/') + dot_pos=NULL,name_pos=++s; +@z + + +@x Section 70. + if (found_change<=0) strcpy(change_file_name,"/dev/null"); +@y + if (found_change<=0) strcpy(change_file_name,"NUL"); +@z diff --git a/web/c_cpp/cweb/comm-mac.ch b/web/c_cpp/cweb/comm-mac.ch new file mode 100644 index 0000000000..b9bd408655 --- /dev/null +++ b/web/c_cpp/cweb/comm-mac.ch @@ -0,0 +1,150 @@ +This is the change file to CWEB's COMMON for porting to Macintoshes. +No changes to CTANGLE or CWEAVE are needed. + +(Contributed 13 Oct 2000 by AndPio@aol.com; slightly edited by Don Knuth) + +@x in limbo, change the title page document to specify Mac version + \centerline{(Version 3.64)} +@y + \centerline{(Version 3.64 for MacOS)} +@z + +@x section 9: Make input_ln accept \n, \r, \n\r, or \r\n as line endings +@ In the unlikely event that your standard I/O library does not +support |feof|, |getc|, and |ungetc| you may have to change things here. +@^system dependencies@> + +@c +int input_ln(fp) /* copies a line into |buffer| or returns 0 */ +FILE *fp; /* what file to read from */ +{ + register int c=EOF; /* character read; initialized so some compilers won't complain */ + register char *k; /* where next character goes */ + if (feof(fp)) return(0); /* we have hit end-of-file */ + limit = k = buffer; /* beginning of buffer */ + while (k<=buffer_end && (c=getc(fp)) != EOF && c!='\n') + if ((*(k++) = c) != ' ') limit = k; + if (k>buffer_end) + if ((c=getc(fp))!=EOF && c!='\n') { + ungetc(c,fp); loc=buffer; err_print("! Input line too long"); +@.Input line too long@> + } + if (c==EOF && limit==buffer) return(0); /* there was nothing after + the last newline */ + return(1); +} +@y +@ In the unlikely event that your standard I/O library does not +support |feof|, |getc|, and |ungetc| you may have to change things here. + +This |input_ln| function accepts |"\n"|, |"\r"|, |"\n\r"| and |"\r\n"| as +line endings, so that \.{CWEB} will works with ASCII files stored in +\UNIX/, {\mc DOS} or {\mc MAC} format. +@^system dependencies@> + +@c +int input_ln(fp) /* copies a line into |buffer| or returns 0 */ +FILE *fp; /* what file to read from */ +{ + register int c=EOF; /* character read; initialized so some compilers won't complain */ + register char *k; /* where next character goes */ + if (feof(fp)) return(0); /* we have hit end-of-file */ + limit = k = buffer; /* beginning of buffer */ + while (1) { + c = getc(fp); + if (c==EOF) return (limit!=buffer); /* 0, if there was nothing after + the last newline */ + else if (c=='\n' || c=='\r') { /* we have hit end-of-line */ + int d = getc(fp); + if (c+d!='\n'+'\r') /* no combination |"\n\r"| or |"\r\n"| */ + ungetc(d,fp); + return (1); + } + else if (k>buffer_end) { + ungetc(c,fp); loc=buffer; err_print("! Input line too long"); + return (1); +@.Input line too long@> + } + else + if ((*(k++)=c) != ' ') limit = k; + } +} +@z + +@x section 12, simply return if no change file was specified + change_limit=change_buffer; /* this value is used if the change file ends */ + @<Skip over comment lines in the change file; |return| if end of file@>; +@y + change_limit=change_buffer; /* this value is used if the change file ends */ + if (change_file_name[0] == '\0') /* no change file specified */ + return; /* so we have reached the end of that file */ + @<Skip over comment lines in the change file; |return| if end of file@>; +@z + +@x section 19, don't try to open a change file if none was specified +if ((change_file=fopen(change_file_name,"r"))==NULL) + fatal("! Cannot open change file ", change_file_name); +@y +if (change_file_name[0] == '\0') /* no change file specified */ + change_file = NULL; /* reset at least the |change_file| */ +else if ((change_file=fopen(change_file_name,"r"))==NULL) + fatal("! Cannot open change file ", change_file_name); +@z + +@x section 22, declare colon as Mac's path separator +(Colon-separated paths are not supported.) +The remainder of the \.{@@i} line after the file name is ignored. + +@y +(Colon-separated path alternatives in the style of \UNIX/ or Kpathsea +are not supported. On a Macintosh, colons are used to separate the names on +different levels of a path.) +The remainder of the \.{@@i} line after the file name is ignored. + +@d PATH_SEP ':' /* MacOS pathname separator */ +@^system dependencies@> +@z + +@x section 23, use the path separator constant + cur_file_name[l]='/'; /* \UNIX/ pathname separator */ +@y + cur_file_name[l]=PATH_SEP; /* pathname separator */ +@z + +@x section 69, explain the convention for omitted change files +An omitted change file argument means that |"/dev/null"| should be used, +@y +An omitted change file argument means that no change file should be used, +@z + +@x section 70, use the Metrowerks |ccommand| to access command lines + while (--argc > 0) { +@y + argc = ccommand (&argv); /* use Mac interface to command line */ +@^system dependencies@> + while (--argc > 0) { +@z +@x section 70, use the path separator constant + else if (*s=='/') dot_pos=NULL,name_pos=++s; +@y + else if (*s==PATH_SEP) dot_pos=NULL,name_pos=++s; +@z +@x section 70, make change file name empty when it is unspecified + if (found_change<=0) strcpy(change_file_name,"/dev/null"); +@y + if (found_change<=0) change_file_name[0]='\0'; /* empty string */ +@z + +@x section 82, insert an extra module before the index +@** Index. +@y by putting the new module here, we preserve all the previous section numbers +@ We assume an interface to \CEE/ command-line emulation as supplied by +the |ccommand| function of Metrowerks CodeWarrior, as defined in +the header file \.{console.h}. + +@<Include files@>= +#include <console.h> +@^system dependencies@> + +@** Index. +@z diff --git a/web/c_cpp/cweb/comm-man.ch b/web/c_cpp/cweb/comm-man.ch new file mode 100644 index 0000000000..c922c03631 --- /dev/null +++ b/web/c_cpp/cweb/comm-man.ch @@ -0,0 +1,22 @@ +Change file to help make Appendix D of the full CWEB manual +@x +\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. +} +@y +\def\botofcontents{\vfill\titlefalse} +\def\contentspagenumber{28} +\def\title{APPENDIX D: COMMON} +\let\K=\leftarrow +@z diff --git a/web/c_cpp/cweb/comm-os2.ch b/web/c_cpp/cweb/comm-os2.ch new file mode 100644 index 0000000000..5e20a6706c --- /dev/null +++ b/web/c_cpp/cweb/comm-os2.ch @@ -0,0 +1,24 @@ +This is the change file for CWEB's COMMON under OS/2 +(Contributed by Jim Nutt, jnutt@PrimeNet.Com, May 1995) + +These changes tested with WATCOM C v10.0a. +No changes to CTANGLE or CWEAVE are needed with OS/2. + +@x section 69 +An omitted change file argument means that |"/dev/null"| should be used, +@y +An omitted change file argument means that |"NUL"| should be used, +@z + +@x section 70 (this change copied from comm-pc.ch) + else if (*s=='/') dot_pos=NULL,name_pos=++s; +@y + else if (*s == ':' || *s == '\\' || *s == '/') + dot_pos=NULL,name_pos=++s; +@z + +@x section 70 + if (found_change<=0) strcpy(change_file_name,"/dev/null"); +@y + if (found_change<=0) strcpy(change_file_name,"NUL"); +@z diff --git a/web/c_cpp/cweb/comm-pc.ch b/web/c_cpp/cweb/comm-pc.ch new file mode 100644 index 0000000000..e69bafb524 --- /dev/null +++ b/web/c_cpp/cweb/comm-pc.ch @@ -0,0 +1,33 @@ +This is the change file for CWEB's COMMON under DOS +(Contributed by Lee Wittenberg, March 1993) + +Changes necessary for compiling with Borland C/C++ +Use compilation switches -mc -w-pro -Ff=5000 + +Note: The change to section 27 is not necessary if using a compiler +that allows >64K arrays. + +@x section 27 +@d max_bytes 90000 /* the number of bytes in identifiers, +@y +@d max_bytes (unsigned)60000 /* the number of bytes in identifiers, +@z + +@x section 69 +An omitted change file argument means that |"/dev/null"| should be used, +@y +An omitted change file argument means that |"NUL"| should be used, +@z + +@x section 70 (this change copied from comm-bs.ch, July 94) + else if (*s=='/') dot_pos=NULL,name_pos=++s; +@y + else if (*s == ':' || *s == '\\' || *s == '/') + dot_pos=NULL,name_pos=++s; +@z + +@x section 70 + if (found_change<=0) strcpy(change_file_name,"/dev/null"); +@y + if (found_change<=0) strcpy(change_file_name,"NUL"); +@z diff --git a/web/c_cpp/cweb/comm-ql.ch b/web/c_cpp/cweb/comm-ql.ch new file mode 100644 index 0000000000..8859e215b2 --- /dev/null +++ b/web/c_cpp/cweb/comm-ql.ch @@ -0,0 +1,332 @@ +This is the change file for CWEB's COMMON under QSOD/SMSQ +(Contributed by Robert H. Klein, September 1994) + +This change file is intended for use with C68 v4.13 (or later). +compile with +ex <dev_>cc;'-v -h -c -=500000 -DCWEBINPUTS=flp2_ common_c' + + + +@x +\def\v{\char'174} % vertical (|) in typewriter font + +\def\title{Common code for CTANGLE and CWEAVE (Version 3.64)} +\def\topofcontents{\null\vfill + \centerline{\titlefont Common code for {\ttitlefont CTANGLE} and + {\ttitlefont CWEAVE}} + \vskip 15pt + \centerline{(Version 3.64)} + \vfill} +\def\botofcontents{\vfill +\noindent +@y +\def\v{\char'174} % vertical (|) in typewriter font + +\def\title{Common code for CTANGLE and CWEAVE (QL Version 3.64)} +\def\topofcontents{\null\vfill + \centerline{\titlefont Common code for {\ttitlefont CTANGLE} and + {\ttitlefont CWEAVE}} + \vskip 15pt + \centerline{(Version 3.64)} + \vfill} +\def\botofcontents{\vfill +\noindent +@z + +@x +The line number of each open file is also kept for error reporting and +for the benefit of \.{CTANGLE}. + +@f line x /* make |line| an unreserved word */ +@d max_include_depth 10 /* maximum number of source files open + simultaneously, not counting the change file */ +@d max_file_name_length 60 +@d cur_file file[include_depth] /* current file */ +@d cur_file_name file_name[include_depth] /* current file name */ +@d cur_line line[include_depth] /* number of current line in current file */ +@d web_file file[0] /* main source file */ +@d web_file_name file_name[0] /* main source file name */ +@y +The line number of each open file is also kept for error reporting and +for the benefit of \.{CTANGLE}. + +For use on QDOS/SMSQ systems the variable |max_file_name_length| is shortened +to 42 characters, i.e. 5 characters for the device name, 36 characters +for the file name plus one character as string terminator. (Note that +(current) QDOS/SMSQ file systems have a limitation of 36 characters as +maximum length for a file name. + +@f line x /* make |line| an unreserved word */ +@d max_include_depth 10 /* maximum number of source files open + simultaneously, not counting the change file */ +@d max_file_name_length 42 +@d cur_file file[include_depth] /* current file */ +@d cur_file_name file_name[include_depth] /* current file name */ +@d cur_line line[include_depth] /* number of current line in current file */ +@d web_file file[0] /* main source file */ +@d web_file_name file_name[0] /* main source file name */ +@z + + +@x + cur_file_name[l]='/'; /* \UNIX/ pathname separator */ +@y + cur_file_name[l]='_'; /* QDOS/SMSQ pathname separator */ +@z + + +@x +@ We now must look at the command line arguments and set the file names +accordingly. At least one file name must be present: the \.{CWEB} +file. It may have an extension, or it may omit the extension to get |".w"| or +|".web"| added. The \TEX/ output file name is formed by replacing the \.{CWEB} +file name extension by |".tex"|, and the \CEE/ file name by replacing +the extension by |".c"|, after removing the directory name (if any). + +If there is a second file name present among the arguments, it is the +change file, again either with an extension or without one to get |".ch"|. +An omitted change file argument means that |"/dev/null"| should be used, +when no changes are desired. +@^system dependencies@> + +If there's a third file name, it will be the output file. + +@<Pred...@>= +void scan_args(); + +@ @c +void +scan_args() +{ + char *dot_pos; /* position of |'.'| in the argument */ + char *name_pos; /* file name beginning, sans directory */ + register char *s; /* register for scanning strings */ + boolean found_web=0,found_change=0,found_out=0; + /* have these names have been seen? */ + boolean flag_change; + + while (--argc > 0) { + if ((**(++argv)=='-'||**argv=='+')&&*(*argv+1)) @<Handle flag argument@>@; + else { + s=name_pos=*argv;@+dot_pos=NULL; + while (*s) { + if (*s=='.') dot_pos=s++; + else if (*s=='/') dot_pos=NULL,name_pos=++s; + else s++; + } + if (!found_web) @<Make + |web_file_name|, |tex_file_name| and |C_file_name|@>@; + else if (!found_change) @<Make |change_file_name| from |fname|@>@; + else if (!found_out) @<Override |tex_file_name| and |C_file_name|@>@; + else @<Print usage error message and quit@>; + } + } + if (!found_web) @<Print usage error message and quit@>; + if (found_change<=0) strcpy(change_file_name,"/dev/null"); +} + +@ We use all of |*argv| for the |web_file_name| if there is a |'.'| in it, +otherwise we add |".w"|. If this file can't be opened, we prepare an +|alt_web_file_name| by adding |"web"| after the dot. +The other file names come from adding other things +after the dot. We must check that there is enough room in +|web_file_name| and the other arrays for the argument. + +@<Make |web_file_name|...@>= +{ + if (s-*argv > max_file_name_length-5) + @<Complain about argument length@>; + if (dot_pos==NULL) + sprintf(web_file_name,"%s.w",*argv); + else { + strcpy(web_file_name,*argv); + *dot_pos=0; /* string now ends where the dot was */ + } + sprintf(alt_web_file_name,"%s.web",*argv); + sprintf(tex_file_name,"%s.tex",name_pos); /* strip off directory name */ + sprintf(idx_file_name,"%s.idx",name_pos); + sprintf(scn_file_name,"%s.scn",name_pos); + sprintf(C_file_name,"%s.c",name_pos); + found_web=1; +} + +@ @<Make |change_file_name|...@>= +{ + if (strcmp(*argv,"-")==0) found_change=-1; + else { + if (s-*argv > max_file_name_length-4) + @<Complain about argument length@>; + if (dot_pos==NULL) + sprintf(change_file_name,"%s.ch",*argv); + else strcpy(change_file_name,*argv); + found_change=1; + } +} + +@ @<Override...@>= +{ + if (s-*argv > max_file_name_length-5) + @<Complain about argument length@>; + if (dot_pos==NULL) { + sprintf(tex_file_name,"%s.tex",*argv); + sprintf(idx_file_name,"%s.idx",*argv); + sprintf(scn_file_name,"%s.scn",*argv); + sprintf(C_file_name,"%s.c",*argv); + } else { + strcpy(tex_file_name,*argv); + strcpy(C_file_name,*argv); + if (flags['x']) { /* indexes will be generated */ + *dot_pos=0; + sprintf(idx_file_name,"%s.idx",*argv); + sprintf(scn_file_name,"%s.scn",*argv); + } + } + found_out=1; +} +@y +@ We now must look at the command line arguments and set the file names +accordingly. At least one file name must be present: the \.{CWEB} +file. It may have an extension, or it may omit the extension to get |"_w"| or +|"_web"| added. The \TEX/ output file name is formed by replacing the \.{CWEB} +file name extension by |"_tex"|, and the \CEE/ file name by replacing +the extension by |"_c"|, after removing the directory name (if any). + +If there is a second file name present among the arguments, it is the +change file, again either with an extension or without one to get |"_ch"|. +An omitted change file argument means that |"NUL"| should be used, +when no changes are desired. +@^system dependencies@> + +If there's a third file name, it will be the output file. + +Because |"_"| is a directory {\em and} extension separator, \.{CWEB} will +always use the {\em full} name (i.e. with full path). Fortunately the +routine has been written to search for the last ``dot'', so the altered +version will search for the last |"_"| (including directory separators), +so we have what we want. + +@<Pred...@>= +void scan_args(); + +@ @c +void +scan_args() +{ + char *dot_pos; /* position of |'_'| in the argument */ + char *name_pos; /* file name beginning, sans directory */ + register char *s; /* register for scanning strings */ + boolean found_web=0,found_change=0,found_out=0; + /* have these names have been seen? */ + boolean flag_change; + + while (--argc > 0) { + if ((**(++argv)=='-'||**argv=='+')&&*(*argv+1)) @<Handle flag argument@>@; + else { + s=name_pos=*argv;@+dot_pos=NULL; + while (*s) { + if (*s=='_') dot_pos=s++; + else if (*s=='_') dot_pos=NULL,name_pos=++s; + else s++; + } + if (!found_web) @<Make + |web_file_name|, |tex_file_name| and |C_file_name|@>@; + else if (!found_change) @<Make |change_file_name| from |fname|@>@; + else if (!found_out) @<Override |tex_file_name| and |C_file_name|@>@; + else @<Print usage error message and quit@>; + } + } + if (!found_web) @<Print usage error message and quit@>; + if (found_change<=0) strcpy(change_file_name,"NUL"); +} + +@ We use all of |*argv| for the |web_file_name| if there is a |'_'| in it, +otherwise we add |"_w"|. If this file can't be opened, we prepare an +|alt_web_file_name| by adding |"web"| after the dot. +The other file names come from adding other things +after the dot. We must check that there is enough room in +|web_file_name| and the other arrays for the argument. + +If you've read the section before carefully you'll have noticed, that +QDOS/SMSQ file names contain almost a |"_"|, so this routine will not work, +i.e. you'll {\em have} to add the |"_w"| always. Nevertheless I adapted +the routine as if it would work. + +@<Make |web_file_name|...@>= +{ + if (s-*argv > max_file_name_length-5) + @<Complain about argument length@>; + if (dot_pos==NULL) + sprintf(web_file_name,"%s_w",*argv); + else { + strcpy(web_file_name,*argv); + *dot_pos=0; /* string now ends where the dot was */ + } + sprintf(alt_web_file_name,"%s_web",*argv); + sprintf(tex_file_name,"%s_tex",name_pos); /* strip off directory name */ + sprintf(idx_file_name,"%s_idx",name_pos); + sprintf(scn_file_name,"%s_scn",name_pos); + sprintf(C_file_name,"%s_c",name_pos); + found_web=1; +} + +@ @<Make |change_file_name|...@>= +{ + if (strcmp(*argv,"-")==0) found_change=-1; + else { + if (s-*argv > max_file_name_length-4) + @<Complain about argument length@>; + if (dot_pos==NULL) + sprintf(change_file_name,"%s_ch",*argv); + else strcpy(change_file_name,*argv); + found_change=1; + } +} + +@ @<Override...@>= +{ + if (s-*argv > max_file_name_length-5) + @<Complain about argument length@>; + if (dot_pos==NULL) { + sprintf(tex_file_name,"%s_tex",*argv); + sprintf(idx_file_name,"%s_idx",*argv); + sprintf(scn_file_name,"%s_scn",*argv); + sprintf(C_file_name,"%s_c",*argv); + } else { + strcpy(tex_file_name,*argv); + strcpy(C_file_name,*argv); + if (flags['x']) { /* indexes will be generated */ + *dot_pos=0; + sprintf(idx_file_name,"%s_idx",*argv); + sprintf(scn_file_name,"%s_scn",*argv); + } + } + found_out=1; +} +@z + + +@x +@ @<Print usage error message and quit@>= +{ +if (program==ctangle) + fatal( +"! Usage: ctangle [options] webfile[.w] [{changefile[.ch]|-} [outfile[.c]]]\n" + ,""); +@.Usage:@> +else fatal( +"! Usage: cweave [options] webfile[.w] [{changefile[.ch]|-} [outfile[.tex]]]\n" + ,""); +} +@y +@ @<Print usage error message and quit@>= +{ +if (program==ctangle) + fatal( +"! Usage: ctangle [options] webfile[_w] [{changefile[_ch]|-} [outfile[_c]]]\n" + ,""); +@.Usage:@> +else fatal( +"! Usage: cweave [options] webfile[_w] [{changefile[_ch]|-} [outfile[_tex]]]\n" + ,""); +} +@z diff --git a/web/c_cpp/cweb/comm-vms.ch b/web/c_cpp/cweb/comm-vms.ch new file mode 100644 index 0000000000..cd5fe52439 --- /dev/null +++ b/web/c_cpp/cweb/comm-vms.ch @@ -0,0 +1,81 @@ +This is the change file for CWEB's COMMON under VAX/VMS. + +created: + 1987 BL (Bjorn Larsen, University of Oslo) + +modified: + 01-FEB-1992 ST (Stephan Trebels <trebels@ams02.dnet.gwdg.de>) + > include ctype,stdio,ssdef from textlibrary SYS$SHARE:VAXCDEF.TLB + > change error return code to SS$_ABORT + (perhaps better than %NONAME-E-NOMSG, Message number 2 ) + +(also modified by Don Knuth to match changes in the master file) +(only the two changes by BL are necessary for initial bootstrapping + via hand-editing of common.c) + +@x section 5 (01-FEB-1992 ST) +#include <ctype.h> +@y +#include ctype /* VMS searches Textlibraries faster */ +@z + +@x section 8 (01-FEB-1992 ST) +#include <stdio.h> +@y +#include stdio /* VMS searches Textlibraries faster */ +@z + +@x section 61 (01-FEB-1992 ST) +programs are started. Here, for instance, we pass the operating system +a status of 0 if and only if only harmless messages were printed. +@y +programs are started. Here, for instance, we pass VAX/VMS +a status of |SS$_NORMAL| if and only if only harmless +messages have been printed. +A suitable status to signal an error condition to VAX/VMS could be +|SS$_ABORT|, telling the operating system to +print |"%SYSTEM-F-ABORT, abort"|, if |history > harmless_message|. +@z + +@x section 61 (1987 BL) (01-FEB-1992 ST) (11-JAN-1993 DEK) + if (history > harmless_message) return(1); + else return(0); +@y +/* + VAX/VMS and UNIX have different error status conventions. + VAX/VMS uses odd values (for example |SS$_NORMAL|) to indicate success, + even values indicate errors, resulting in messages displayed + on the screen. |SS$_ABORT| has been chosen, to indicate an + error and display something that's not complete nonsense. +*/ + if (history > harmless_message) exit(SS$_ABORT); + else exit(SS$_NORMAL); +@z + +@x section 69 (01-FEB-1992 ST) +An omitted change file argument means that |"/dev/null"| should be used, +when no changes are desired. +@y +An omitted change file argument means that the +null device |"NL:"| should be used, when no changes are desired. +@z + +@x section 70 (1987 BL) (01-FEB-1992 ST) (05-APR-1992 DEK) + if (found_change<=0) strcpy(change_file_name,"/dev/null"); +@y + if (found_change<=) strcpy(change_file_name,"NL:"); + /* {\tt NL:} is the VAX/VMS notation for {\tt /dev/null} */ +@z + +@x section 82 (01-FEB-1992 ST) +@** Index. +@y +@* VAX/VMS specific code. +We have used |SS$_NORMAL| and |SS$_ABORT| as return codes, +so we have to include the system message codes. + +@<Include files@>= +#include ssdef /* we need VAX/VMS system messages */ + +@** Index. +@z diff --git a/web/c_cpp/cweb/comm-w32.ch b/web/c_cpp/cweb/comm-w32.ch new file mode 100644 index 0000000000..b300f496d2 --- /dev/null +++ b/web/c_cpp/cweb/comm-w32.ch @@ -0,0 +1,94 @@ +This is the change file for CWEB's COMMON under Win32 +(Contributed by Fabrice Popineau, February 2002 <Fabrice.Popineau@supelec.fr>) + +The Microsoft C compiler included in Visual Studio allows for different +parameter passing conventions than the standard one. This is actually +specified with the Intel IA32 architecture. There exist three +calling conventions under the IA32 architecture as defined by Intel. + +One of them is the standard C calling convention: Parameters are passed on +the stack, calling function is responsible to unstack arguments. Names are +decorated with a prefixed underscore. It is called the __cdecl convention. + +Another calling convention is the __fastcall convention. The first two 32bits +arguments are passed on the stack. The called function is responsible for +stack maintenance. Names are decorated with an @ sign as prefix, and another +@ sign followed by the size of the arguments as suffix. + +The third one is the __stdcall convention and is basically the Pascal calling +convention. + +Using the __fastcall convention is usually faster on Intel architecture. Names +are decorated because obviously a __cdecl function can't behave the same way +as a __fastcall one of the same prototype. So the new full prototype includes +the keyword __cdecl or __fastcall. A compiler option allows to compile all +functions using one or the other calling convention. If you compile using +__fastcall, then you are bound to explicitly declare a few functions as +__cdecl : the ones that are passed to the C library (like function pointers +for qsort() or signal()) or functions that replace functions from the C +library, because the standard headers have a __cdecl prototype for these +functions. Also, you cannot declare a library function without giving it the +exact prototype used in the library headers files. So you'd better use the +header files to be safe. + +Admittedly, this is not vital for cweb, except that I build the whole texlive +set of programs using the __fastcall convention. + +@x +@<Include files@>= +#include <ctype.h> +@y +@<Include files@>= +#include <ctype.h> +#include <string.h> +@z + +@x +@ @<Predec...@>= +extern int names_match(); +@y +@ @<Predec...@>= +extern int __cdecl names_match(); +@z + +@x +@<Pred...@>= +void init_p(); +@y +@<Pred...@>= +void __cdecl init_p(); +@z + +@x section 69 +An omitted change file argument means that |"/dev/null"| should be used, +@y +An omitted change file argument means that |"NUL"| should be used, +@z + +@x section 70 (this change copied from comm-bs.ch, July 94) + else if (*s=='/') dot_pos=NULL,name_pos=++s; +@y + else if (*s == ':' || *s == '\\' || *s == '/') + dot_pos=NULL,name_pos=++s; +@z + +@x section 70 + if (found_change<=0) strcpy(change_file_name,"/dev/null"); +@y + if (found_change<=0) strcpy(change_file_name,"NUL"); +@z + +@x +@ We predeclare several standard system functions here instead of including +their system header files, because the names of the header files are not as +standard as the names of the functions. (For example, some \CEE/ environments +have \.{<string.h>} where others have \.{<strings.h>}.) + +@<Predecl...@>= +extern int strlen(); /* length of string */ +extern int strcmp(); /* compare strings lexicographically */ +extern char* strcpy(); /* copy one string to another */ +extern int strncmp(); /* compare up to $n$ string characters */ +extern char* strncpy(); /* copy up to $n$ string characters */ +@y +@z diff --git a/web/c_cpp/cweb/common.c b/web/c_cpp/cweb/common.c new file mode 100644 index 0000000000..2bf7a9d4a8 --- /dev/null +++ b/web/c_cpp/cweb/common.c @@ -0,0 +1,1242 @@ +/*1:*/ +#line 58 "common.w" + +/*5:*/ +#line 102 "common.w" + +#include <ctype.h> + +/*:5*//*8:*/ +#line 165 "common.w" + +#include <stdio.h> + +/*:8*//*22:*/ +#line 471 "common.w" + +#include <stdlib.h> + +/*:22*/ +#line 59 "common.w" + +#define ctangle 0 +#define cweave 1 \ + +#define and_and 04 +#define lt_lt 020 +#define gt_gt 021 +#define plus_plus 013 +#define minus_minus 01 +#define minus_gt 031 +#define not_eq 032 +#define lt_eq 034 +#define gt_eq 035 +#define eq_eq 036 +#define or_or 037 +#define dot_dot_dot 016 +#define colon_colon 06 +#define period_ast 026 +#define minus_gt_ast 027 \ + +#define buf_size 100 +#define longest_name 10000 +#define long_buf_size (buf_size+longest_name) +#define xisspace(c) (isspace(c) &&((unsigned char) c<0200) ) +#define xisupper(c) (isupper(c) &&((unsigned char) c<0200) ) \ + +#define max_include_depth 10 \ + +#define max_file_name_length 60 +#define cur_file file[include_depth] +#define cur_file_name file_name[include_depth] +#define cur_line line[include_depth] +#define web_file file[0] +#define web_file_name file_name[0] \ + +#define lines_dont_match (change_limit-change_buffer!=limit-buffer|| \ +strncmp(buffer,change_buffer,limit-buffer) ) \ + +#define if_section_start_make_pending(b) {*limit= '!'; \ +for(loc= buffer;xisspace(*loc) ;loc++) ; \ +*limit= ' '; \ +if(*loc=='@'&&(xisspace(*(loc+1) ) ||*(loc+1) =='*') ) change_pending= b; \ +} \ + +#define max_sections 2000 \ + \ + +#define too_long() {include_depth--; \ +err_print("! Include file name too long") ;goto restart;} \ + +#define max_bytes 90000 \ + +#define max_names 4000 \ + \ + +#define length(c) (c+1) ->byte_start-(c) ->byte_start +#define print_id(c) term_write((c) ->byte_start,length((c) ) ) \ + +#define hash_size 353 \ + +#define llink link +#define rlink dummy.Rlink +#define root name_dir->rlink \ + \ + +#define first_chunk(p) ((p) ->byte_start+2) +#define prefix_length(p) (int) ((unsigned char) *((p) ->byte_start) *256+ \ +(unsigned char) *((p) ->byte_start+1) ) +#define set_prefix_length(p,m) (*((p) ->byte_start) = (m) /256, \ +*((p) ->byte_start+1) = (m) %256) \ + +#define less 0 +#define equal 1 +#define greater 2 +#define prefix 3 +#define extension 4 \ + +#define bad_extension 5 \ + +#define spotless 0 +#define harmless_message 1 +#define error_message 2 +#define fatal_message 3 +#define mark_harmless {if(history==spotless) history= harmless_message;} +#define mark_error history= error_message \ + +#define confusion(s) fatal("! This can't happen: ",s) \ + \ + +#define show_banner flags['b'] +#define show_progress flags['p'] +#define show_stats flags['s'] +#define show_happiness flags['h'] \ + +#define update_terminal fflush(stdout) \ + +#define new_line putchar('\n') +#define putxchar putchar +#define term_write(a,b) fflush(stdout) ,fwrite(a,sizeof(char) ,b,stdout) +#define C_printf(c,a) fprintf(C_file,c,a) +#define C_putc(c) putc(c,C_file) \ + + +#line 60 "common.w" + +/*2:*/ +#line 73 "common.w" + +typedef short boolean; +boolean program; + +/*:2*//*7:*/ +#line 159 "common.w" + +char buffer[long_buf_size]; +char*buffer_end= buffer+buf_size-2; +char*limit= buffer; +char*loc= buffer; + +/*:7*//*10:*/ +#line 214 "common.w" + +int include_depth; +FILE*file[max_include_depth]; +FILE*change_file; +char file_name[max_include_depth][max_file_name_length]; + +char change_file_name[max_file_name_length]; +char alt_web_file_name[max_file_name_length]; +int line[max_include_depth]; +int change_line; +int change_depth; +boolean input_has_ended; +boolean changing; +boolean web_file_open= 0; + +/*:10*//*20:*/ +#line 418 "common.w" + +typedef unsigned short sixteen_bits; +sixteen_bits section_count; +boolean changed_section[max_sections]; +boolean change_pending; + +boolean print_where= 0; + +/*:20*//*27:*/ +#line 594 "common.w" + +typedef struct name_info{ +char*byte_start; +/*31:*/ +#line 631 "common.w" + +struct name_info*link; + +/*:31*//*40:*/ +#line 730 "common.w" + +union{ +struct name_info*Rlink; + +char Ilk; +}dummy; + +/*:40*//*55:*/ +#line 1062 "common.w" + +char*equiv_or_xref; + +/*:55*/ +#line 597 "common.w" + +}name_info; +typedef name_info*name_pointer; +char byte_mem[max_bytes]; +char*byte_mem_end= byte_mem+max_bytes-1; +name_info name_dir[max_names]; +name_pointer name_dir_end= name_dir+max_names-1; + +/*:27*//*29:*/ +#line 617 "common.w" + +name_pointer name_ptr; +char*byte_ptr; + +/*:29*//*32:*/ +#line 644 "common.w" + +typedef name_pointer*hash_pointer; +name_pointer hash[hash_size]; +hash_pointer hash_end= hash+hash_size-1; +hash_pointer h; + +/*:32*//*56:*/ +#line 1082 "common.w" + +int history= spotless; + +/*:56*//*67:*/ +#line 1220 "common.w" + +int argc; +char**argv; +char C_file_name[max_file_name_length]; +char tex_file_name[max_file_name_length]; +char idx_file_name[max_file_name_length]; +char scn_file_name[max_file_name_length]; +boolean flags[128]; + +/*:67*//*77:*/ +#line 1370 "common.w" + +FILE*C_file; +FILE*tex_file; +FILE*idx_file; +FILE*scn_file; +FILE*active_file; + +/*:77*/ +#line 61 "common.w" + +/*3:*/ +#line 83 "common.w" +int phase; + +/*:3*//*11:*/ +#line 240 "common.w" + +char change_buffer[buf_size]; +char*change_limit; + +/*:11*/ +#line 62 "common.w" + +/*33:*/ +#line 650 "common.w" + +extern int names_match(); + +/*:33*//*38:*/ +#line 703 "common.w" + +void init_p(); + +/*:38*//*46:*/ +#line 852 "common.w" + +extern void init_node(); + +/*:46*//*53:*/ +#line 1017 "common.w" + +int section_name_cmp(); + +/*:53*//*57:*/ +#line 1092 "common.w" + +void err_print(); + +/*:57*//*60:*/ +#line 1140 "common.w" + +int wrap_up(); +extern void print_stats(); + +/*:60*//*63:*/ +#line 1173 "common.w" + +void fatal(),overflow(); + +/*:63*//*69:*/ +#line 1251 "common.w" + +void scan_args(); + +/*:69*//*81:*/ +#line 1411 "common.w" + +extern int strlen(); +extern int strcmp(); +extern char*strcpy(); +extern int strncmp(); +extern char*strncpy(); + +/*:81*/ +#line 63 "common.w" + + +/*:1*//*4:*/ +#line 89 "common.w" + +void +common_init() +{ +/*30:*/ +#line 621 "common.w" + +name_dir->byte_start= byte_ptr= byte_mem; +name_ptr= name_dir+1; +name_ptr->byte_start= byte_mem; + +/*:30*//*34:*/ +#line 655 "common.w" + +for(h= hash;h<=hash_end;*h++= NULL); + +/*:34*//*41:*/ +#line 737 "common.w" + +root= NULL; + +/*:41*/ +#line 93 "common.w" +; +/*68:*/ +#line 1233 "common.w" + +show_banner= show_happiness= show_progress= 1; + +/*:68*/ +#line 94 "common.w" +; +/*78:*/ +#line 1377 "common.w" + +scan_args(); +if(program==ctangle){ +if((C_file= fopen(C_file_name,"w"))==NULL) +fatal("! Cannot open output file ",C_file_name); + +} +else{ +if((tex_file= fopen(tex_file_name,"w"))==NULL) +fatal("! Cannot open output file ",tex_file_name); +} + +/*:78*/ +#line 95 "common.w" +; +} + +/*:4*//*9:*/ +#line 172 "common.w" + +int input_ln(fp) +FILE*fp; +{ +register int c= EOF; +register char*k; +if(feof(fp))return(0); +limit= k= buffer; +while(k<=buffer_end&&(c= getc(fp))!=EOF&&c!='\n') +if((*(k++)= c)!=' ')limit= k; +if(k> buffer_end) +if((c= getc(fp))!=EOF&&c!='\n'){ +ungetc(c,fp);loc= buffer;err_print("! Input line too long"); + +} +if(c==EOF&&limit==buffer)return(0); + +return(1); +} + +/*:9*//*12:*/ +#line 251 "common.w" + +void +prime_the_change_buffer() +{ +change_limit= change_buffer; +/*13:*/ +#line 265 "common.w" + +while(1){ +change_line++; +if(!input_ln(change_file))return; +if(limit<buffer+2)continue; +if(buffer[0]!='@')continue; +if(xisupper(buffer[1]))buffer[1]= tolower(buffer[1]); +if(buffer[1]=='x')break; +if(buffer[1]=='y'||buffer[1]=='z'||buffer[1]=='i'){ +loc= buffer+2; +err_print("! Missing @x in change file"); + +} +} + +/*:13*/ +#line 256 "common.w" +; +/*14:*/ +#line 282 "common.w" + +do{ +change_line++; +if(!input_ln(change_file)){ +err_print("! Change file ended after @x"); + +return; +} +}while(limit==buffer); + +/*:14*/ +#line 257 "common.w" +; +/*15:*/ +#line 292 "common.w" + +{ +change_limit= change_buffer+(limit-buffer); +strncpy(change_buffer,buffer,limit-buffer+1); +} + +/*:15*/ +#line 258 "common.w" +; +} + +/*:12*//*16:*/ +#line 320 "common.w" + +void +check_change() +{ +int n= 0; +if(lines_dont_match)return; +change_pending= 0; +if(!changed_section[section_count]){ +if_section_start_make_pending(1); +if(!change_pending)changed_section[section_count]= 1; +} +while(1){ +changing= 1;print_where= 1;change_line++; +if(!input_ln(change_file)){ +err_print("! Change file ended before @y"); + +change_limit= change_buffer;changing= 0; +return; +} +if(limit> buffer+1&&buffer[0]=='@'){ +char xyz_code= xisupper(buffer[1])?tolower(buffer[1]):buffer[1]; +/*17:*/ +#line 358 "common.w" + +if(xyz_code=='x'||xyz_code=='z'){ +loc= buffer+2;err_print("! Where is the matching @y?"); + +} +else if(xyz_code=='y'){ +if(n> 0){ +loc= buffer+2; +printf("\n! Hmm... %d ",n); +err_print("of the preceding lines failed to match"); + +} +change_depth= include_depth; +return; +} + +/*:17*/ +#line 342 "common.w" +; +} +/*15:*/ +#line 292 "common.w" + +{ +change_limit= change_buffer+(limit-buffer); +strncpy(change_buffer,buffer,limit-buffer+1); +} + +/*:15*/ +#line 344 "common.w" +; +changing= 0;cur_line++; +while(!input_ln(cur_file)){ +if(include_depth==0){ +err_print("! CWEB file ended during a change"); + +input_has_ended= 1;return; +} +include_depth--;cur_line++; +} +if(lines_dont_match)n++; +} +} + +/*:16*//*18:*/ +#line 378 "common.w" + +void +reset_input() +{ +limit= buffer;loc= buffer+1;buffer[0]= ' '; +/*19:*/ +#line 393 "common.w" + +if((web_file= fopen(web_file_name,"r"))==NULL){ +strcpy(web_file_name,alt_web_file_name); +if((web_file= fopen(web_file_name,"r"))==NULL) +fatal("! Cannot open input file ",web_file_name); +} + + +web_file_open= 1; +if((change_file= fopen(change_file_name,"r"))==NULL) +fatal("! Cannot open change file ",change_file_name); + +/*:19*/ +#line 383 "common.w" +; +include_depth= 0;cur_line= 0;change_line= 0; +change_depth= include_depth; +changing= 1;prime_the_change_buffer();changing= !changing; +limit= buffer;loc= buffer+1;buffer[0]= ' ';input_has_ended= 0; +} + +/*:18*//*21:*/ +#line 426 "common.w" + +int get_line() +{ +restart: +if(changing&&include_depth==change_depth) +/*25:*/ +#line 537 "common.w" +{ +change_line++; +if(!input_ln(change_file)){ +err_print("! Change file ended without @z"); + +buffer[0]= '@';buffer[1]= 'z';limit= buffer+2; +} +if(limit> buffer){ +if(change_pending){ +if_section_start_make_pending(0); +if(change_pending){ +changed_section[section_count]= 1;change_pending= 0; +} +} +*limit= ' '; +if(buffer[0]=='@'){ +if(xisupper(buffer[1]))buffer[1]= tolower(buffer[1]); +if(buffer[1]=='x'||buffer[1]=='y'){ +loc= buffer+2; +err_print("! Where is the matching @z?"); + +} +else if(buffer[1]=='z'){ +prime_the_change_buffer();changing= !changing;print_where= 1; +} +} +} +} + +/*:25*/ +#line 431 "common.w" +; +if(!changing||include_depth> change_depth){ +/*24:*/ +#line 520 "common.w" +{ +cur_line++; +while(!input_ln(cur_file)){ +print_where= 1; +if(include_depth==0){input_has_ended= 1;break;} +else{ +fclose(cur_file);include_depth--; +if(changing&&include_depth==change_depth)break; +cur_line++; +} +} +if(!changing&&!input_has_ended) +if(limit-buffer==change_limit-change_buffer) +if(buffer[0]==change_buffer[0]) +if(change_limit> change_buffer)check_change(); +} + +/*:24*/ +#line 433 "common.w" +; +if(changing&&include_depth==change_depth)goto restart; +} +if(input_has_ended)return 0; +loc= buffer;*limit= ' '; +if(buffer[0]=='@'&&(buffer[1]=='i'||buffer[1]=='I')){ +loc= buffer+2;*limit= '"'; +while(*loc==' '||*loc=='\t')loc++; +if(loc>=limit){ +err_print("! Include file name not given"); + +goto restart; +} +if(include_depth>=max_include_depth-1){ +err_print("! Too many nested includes"); + +goto restart; +} +include_depth++; +/*23:*/ +#line 474 "common.w" +{ +char temp_file_name[max_file_name_length]; +char*cur_file_name_end= cur_file_name+max_file_name_length-1; +char*k= cur_file_name,*kk; +int l; + +if(*loc=='"'){ +loc++; +while(*loc!='"'&&k<=cur_file_name_end)*k++= *loc++; +if(loc==limit)k= cur_file_name_end+1; +}else +while(*loc!=' '&&*loc!='\t'&&*loc!='"'&&k<=cur_file_name_end)*k++= *loc++; +if(k> cur_file_name_end)too_long(); + +*k= '\0'; +if((cur_file= fopen(cur_file_name,"r"))!=NULL){ +cur_line= 0;print_where= 1; +goto restart; +} +kk= getenv("CWEBINPUTS"); +if(kk!=NULL){ +if((l= strlen(kk))> max_file_name_length-2)too_long(); +strcpy(temp_file_name,kk); +} +else{ +#ifdef CWEBINPUTS +if((l= strlen(CWEBINPUTS))> max_file_name_length-2)too_long(); +strcpy(temp_file_name,CWEBINPUTS); +#else +l= 0; +#endif +} +if(l> 0){ +if(k+l+2>=cur_file_name_end)too_long(); + +for(;k>=cur_file_name;k--)*(k+l+1)= *k; +strcpy(cur_file_name,temp_file_name); +cur_file_name[l]= '/'; +if((cur_file= fopen(cur_file_name,"r"))!=NULL){ +cur_line= 0;print_where= 1; +goto restart; +} +} +include_depth--;err_print("! Cannot open include file");goto restart; +} + +/*:23*/ +#line 452 "common.w" +; +} +return 1; +} + +/*:21*//*26:*/ +#line 569 "common.w" + +void +check_complete(){ +if(change_limit!=change_buffer){ +strncpy(buffer,change_buffer,change_limit-change_buffer+1); +limit= buffer+(int)(change_limit-change_buffer); +changing= 1;change_depth= include_depth;loc= buffer; +err_print("! Change file entry did not match"); + +} +} + +/*:26*//*35:*/ +#line 660 "common.w" + +name_pointer +id_lookup(first,last,t) +char*first; +char*last; +char t; +{ +char*i= first; +int h; +int l; +name_pointer p; +if(last==NULL)for(last= first;*last!='\0';last++); +l= last-first; +/*36:*/ +#line 683 "common.w" + +h= (unsigned char)*i; +while(++i<last)h= (h+h+(int)((unsigned char)*i))%hash_size; + + +/*:36*/ +#line 673 "common.w" +; +/*37:*/ +#line 691 "common.w" + +p= hash[h]; +while(p&&!names_match(p,first,l,t))p= p->link; +if(p==NULL){ +p= name_ptr; +p->link= hash[h];hash[h]= p; +} + +/*:37*/ +#line 674 "common.w" +; +if(p==name_ptr)/*39:*/ +#line 706 "common.w" +{ +if(byte_ptr+l> byte_mem_end)overflow("byte memory"); +if(name_ptr>=name_dir_end)overflow("name"); +strncpy(byte_ptr,first,l); +(++name_ptr)->byte_start= byte_ptr+= l; +if(program==cweave)init_p(p,t); +} + +/*:39*/ +#line 675 "common.w" +; +return(p); +} + +/*:35*//*42:*/ +#line 764 "common.w" + +void +print_section_name(p) +name_pointer p; +{ +char*ss,*s= first_chunk(p); +name_pointer q= p+1; +while(p!=name_dir){ +ss= (p+1)->byte_start-1; +if(*ss==' '&&ss>=s){ +term_write(s,ss-s);p= q->link;q= p; +}else{ +term_write(s,ss+1-s);p= name_dir;q= NULL; +} +s= p->byte_start; +} +if(q)term_write("...",3); +} + +/*:42*//*43:*/ +#line 783 "common.w" + +void +sprint_section_name(dest,p) +char*dest; +name_pointer p; +{ +char*ss,*s= first_chunk(p); +name_pointer q= p+1; +while(p!=name_dir){ +ss= (p+1)->byte_start-1; +if(*ss==' '&&ss>=s){ +p= q->link;q= p; +}else{ +ss++;p= name_dir; +} +strncpy(dest,s,ss-s),dest+= ss-s; +s= p->byte_start; +} +*dest= '\0'; +} + +/*:43*//*44:*/ +#line 804 "common.w" + +void +print_prefix_name(p) +name_pointer p; +{ +char*s= first_chunk(p); +int l= prefix_length(p); +term_write(s,l); +if(s+l<(p+1)->byte_start)term_write("...",3); +} + +/*:44*//*45:*/ +#line 825 "common.w" + +int web_strcmp(j,j_len,k,k_len) +char*j,*k; +int j_len,k_len; +{ +char*j1= j+j_len,*k1= k+k_len; +while(k<k1&&j<j1&&*j==*k)k++,j++; +if(k==k1)if(j==j1)return equal; +else return extension; +else if(j==j1)return prefix; +else if(*j<*k)return less; +else return greater; +} + +/*:45*//*47:*/ +#line 855 "common.w" + +name_pointer +add_section_name(par,c,first,last,ispref) +name_pointer par; +int c; +char*first; +char*last; +int ispref; +{ +name_pointer p= name_ptr; +char*s= first_chunk(p); +int name_len= last-first+ispref; +if(s+name_len> byte_mem_end)overflow("byte memory"); +if(name_ptr+1>=name_dir_end)overflow("name"); +(++name_ptr)->byte_start= byte_ptr= s+name_len; +if(ispref){ +*(byte_ptr-1)= ' '; +name_len--; +name_ptr->link= name_dir; +(++name_ptr)->byte_start= byte_ptr; +} +set_prefix_length(p,name_len); +strncpy(s,first,name_len); +p->llink= NULL; +p->rlink= NULL; +init_node(p); +return par==NULL?(root= p):c==less?(par->llink= p):(par->rlink= p); +} + +/*:47*//*48:*/ +#line 884 "common.w" + +void +extend_section_name(p,first,last,ispref) +name_pointer p; +char*first; +char*last; +int ispref; +{ +char*s; +name_pointer q= p+1; +int name_len= last-first+ispref; +if(name_ptr>=name_dir_end)overflow("name"); +while(q->link!=name_dir)q= q->link; +q->link= name_ptr; +s= name_ptr->byte_start; +name_ptr->link= name_dir; +if(s+name_len> byte_mem_end)overflow("byte memory"); +(++name_ptr)->byte_start= byte_ptr= s+name_len; +strncpy(s,first,name_len); +if(ispref)*(byte_ptr-1)= ' '; +} + +/*:48*//*49:*/ +#line 912 "common.w" + +name_pointer +section_lookup(first,last,ispref) +char*first,*last; +int ispref; +{ +int c= 0; +name_pointer p= root; +name_pointer q= NULL; +name_pointer r= NULL; +name_pointer par= NULL; + +int name_len= last-first+1; +/*50:*/ +#line 936 "common.w" + +while(p){ +c= web_strcmp(first,name_len,first_chunk(p),prefix_length(p)); +if(c==less||c==greater){ +if(r==NULL) +par= p; +p= (c==less?p->llink:p->rlink); +}else{ +if(r!=NULL){ +printf("\n! Ambiguous prefix: matches <"); + +print_prefix_name(p); +printf(">\n and <"); +print_prefix_name(r); +err_print(">"); +return name_dir; +} +r= p; +p= p->llink; +q= r->rlink; +} +if(p==NULL) +p= q,q= NULL; +} + +/*:50*/ +#line 926 "common.w" +; +/*51:*/ +#line 961 "common.w" + +if(r==NULL) +return add_section_name(par,c,first,last+1,ispref); + +/*:51*/ +#line 927 "common.w" +; +/*52:*/ +#line 969 "common.w" + +switch(section_name_cmp(&first,name_len,r)){ + +case prefix: +if(!ispref){ +printf("\n! New name is a prefix of <"); + +print_section_name(r); +err_print(">"); +} +else if(name_len<prefix_length(r))set_prefix_length(r,name_len); + +case equal:return r; +case extension:if(!ispref||first<=last) +extend_section_name(r,first,last+1,ispref); +return r; +case bad_extension: +printf("\n! New name extends <"); + +print_section_name(r); +err_print(">"); +return r; +default: +printf("\n! Section name incompatible with <"); + +print_prefix_name(r); +printf(">,\n which abbreviates <"); +print_section_name(r); +err_print(">"); +return r; +} + +/*:52*/ +#line 928 "common.w" +; +} + +/*:49*//*54:*/ +#line 1020 "common.w" + +int section_name_cmp(pfirst,len,r) +char**pfirst; +int len; +name_pointer r; +{ +char*first= *pfirst; +name_pointer q= r+1; +char*ss,*s= first_chunk(r); +int c; +int ispref; +while(1){ +ss= (r+1)->byte_start-1; +if(*ss==' '&&ss>=r->byte_start)ispref= 1,q= q->link; +else ispref= 0,ss++,q= name_dir; +switch(c= web_strcmp(first,len,s,ss-s)){ +case equal:if(q==name_dir) +if(ispref){ +*pfirst= first+(ss-s); +return extension; +}else return equal; +else return(q->byte_start==(q+1)->byte_start)?equal:prefix; +case extension: +if(!ispref)return bad_extension; +first+= ss-s; +if(q!=name_dir){len-= ss-s;s= q->byte_start;r= q;continue;} +*pfirst= first;return extension; +default:return c; +} +} +} + +/*:54*//*58:*/ +#line 1095 "common.w" + +void +err_print(s) +char*s; +{ +char*k,*l; +printf(*s=='!'?"\n%s":"%s",s); +if(web_file_open)/*59:*/ +#line 1115 "common.w" + +{if(changing&&include_depth==change_depth) +printf(". (l. %d of change file)\n",change_line); +else if(include_depth==0)printf(". (l. %d)\n",cur_line); +else printf(". (l. %d of include file %s)\n",cur_line,cur_file_name); +l= (loc>=limit?limit:loc); +if(l> buffer){ +for(k= buffer;k<l;k++) +if(*k=='\t')putchar(' '); +else putchar(*k); +putchar('\n'); +for(k= buffer;k<l;k++)putchar(' '); +} +for(k= l;k<limit;k++)putchar(*k); +if(*limit=='|')putchar('|'); +putchar(' '); +} + +/*:59*/ +#line 1102 "common.w" +; +update_terminal;mark_error; +} + +/*:58*//*61:*/ +#line 1150 "common.w" + +int wrap_up(){ +putchar('\n'); +if(show_stats) +print_stats(); +/*62:*/ +#line 1160 "common.w" + +switch(history){ +case spotless:if(show_happiness)printf("(No errors were found.)\n");break; +case harmless_message: +printf("(Did you see the warning message above?)\n");break; +case error_message: +printf("(Pardon me, but I think I spotted something wrong.)\n");break; +case fatal_message:printf("(That was a fatal error, my friend.)\n"); +} + +/*:62*/ +#line 1155 "common.w" +; +if(history> harmless_message)return(1); +else return(0); +} + +/*:61*//*64:*/ +#line 1179 "common.w" +void +fatal(s,t) +char*s,*t; +{ +if(*s)printf(s); +err_print(t); +history= fatal_message;exit(wrap_up()); +} + +/*:64*//*65:*/ +#line 1190 "common.w" +void +overflow(t) +char*t; +{ +printf("\n! Sorry, %s capacity exceeded",t);fatal("",""); +} + + +/*:65*//*70:*/ +#line 1254 "common.w" + +void +scan_args() +{ +char*dot_pos; +char*name_pos; +register char*s; +boolean found_web= 0,found_change= 0,found_out= 0; + +boolean flag_change; + +while(--argc> 0){ +if((**(++argv)=='-'||**argv=='+')&&*(*argv+1))/*74:*/ +#line 1344 "common.w" + +{ +if(**argv=='-')flag_change= 0; +else flag_change= 1; +for(dot_pos= *argv+1;*dot_pos> '\0';dot_pos++) +flags[*dot_pos]= flag_change; +} + +/*:74*/ +#line 1266 "common.w" + +else{ +s= name_pos= *argv;dot_pos= NULL; +while(*s){ +if(*s=='.')dot_pos= s++; +else if(*s=='/')dot_pos= NULL,name_pos= ++s; +else s++; +} +if(!found_web)/*71:*/ +#line 1292 "common.w" + +{ +if(s-*argv> max_file_name_length-5) +/*76:*/ +#line 1364 "common.w" +fatal("! Filename too long\n",*argv); + + +/*:76*/ +#line 1295 "common.w" +; +if(dot_pos==NULL) +sprintf(web_file_name,"%s.w",*argv); +else{ +strcpy(web_file_name,*argv); +*dot_pos= 0; +} +sprintf(alt_web_file_name,"%s.web",*argv); +sprintf(tex_file_name,"%s.tex",name_pos); +sprintf(idx_file_name,"%s.idx",name_pos); +sprintf(scn_file_name,"%s.scn",name_pos); +sprintf(C_file_name,"%s.c",name_pos); +found_web= 1; +} + +/*:71*/ +#line 1275 "common.w" + +else if(!found_change)/*72:*/ +#line 1310 "common.w" + +{ +if(strcmp(*argv,"-")==0)found_change= -1; +else{ +if(s-*argv> max_file_name_length-4) +/*76:*/ +#line 1364 "common.w" +fatal("! Filename too long\n",*argv); + + +/*:76*/ +#line 1315 "common.w" +; +if(dot_pos==NULL) +sprintf(change_file_name,"%s.ch",*argv); +else strcpy(change_file_name,*argv); +found_change= 1; +} +} + +/*:72*/ +#line 1276 "common.w" + +else if(!found_out)/*73:*/ +#line 1323 "common.w" + +{ +if(s-*argv> max_file_name_length-5) +/*76:*/ +#line 1364 "common.w" +fatal("! Filename too long\n",*argv); + + +/*:76*/ +#line 1326 "common.w" +; +if(dot_pos==NULL){ +sprintf(tex_file_name,"%s.tex",*argv); +sprintf(idx_file_name,"%s.idx",*argv); +sprintf(scn_file_name,"%s.scn",*argv); +sprintf(C_file_name,"%s.c",*argv); +}else{ +strcpy(tex_file_name,*argv); +strcpy(C_file_name,*argv); +if(flags['x']){ +*dot_pos= 0; +sprintf(idx_file_name,"%s.idx",*argv); +sprintf(scn_file_name,"%s.scn",*argv); +} +} +found_out= 1; +} + +/*:73*/ +#line 1277 "common.w" + +else/*75:*/ +#line 1352 "common.w" + +{ +if(program==ctangle) +fatal( +"! Usage: ctangle [options] webfile[.w] [{changefile[.ch]|-} [outfile[.c]]]\n" +,""); + +else fatal( +"! Usage: cweave [options] webfile[.w] [{changefile[.ch]|-} [outfile[.tex]]]\n" +,""); +} + +/*:75*/ +#line 1278 "common.w" +; +} +} +if(!found_web)/*75:*/ +#line 1352 "common.w" + +{ +if(program==ctangle) +fatal( +"! Usage: ctangle [options] webfile[.w] [{changefile[.ch]|-} [outfile[.c]]]\n" +,""); + +else fatal( +"! Usage: cweave [options] webfile[.w] [{changefile[.ch]|-} [outfile[.tex]]]\n" +,""); +} + +/*:75*/ +#line 1281 "common.w" +; +if(found_change<=0)strcpy(change_file_name,"/dev/null"); +} + +/*:70*/ diff --git a/web/c_cpp/cweb/common.h b/web/c_cpp/cweb/common.h new file mode 100644 index 0000000000..d6c0bfb601 --- /dev/null +++ b/web/c_cpp/cweb/common.h @@ -0,0 +1,193 @@ +% 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.64 --- February 2017 (works also with later versions) + +% Copyright (C) 1987,1990,1993 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 distributed under the terms of a +% permission notice identical to this one. + +% Please send comments, suggestions, etc. to levy@@math.berkeley.edu. + +% The next few sections contain stuff from the file |"common.w"| that has +% to be included in both |"ctangle.w"| and |"cweave.w"|. It appears in this +% file |"common.h"|, which needs to be updated when |"common.w"| changes. + +First comes general stuff: + +@d ctangle 0 +@d cweave 1 + +@<Common code for \.{CWEAVE} and \.{CTANGLE}@>= +typedef short boolean; +typedef char unsigned eight_bits; +extern boolean program; /* \.{CWEAVE} or \.{CTANGLE}? */ +extern int phase; /* which phase are we in? */ + +@ @<Include files@>= +#include <stdio.h> + +@ Code related to the character set: +@^ASCII code dependencies@> + +@d and_and 04 /* `\.{\&\&}'\,; corresponds to MIT's {\tentex\char'4} */ +@d lt_lt 020 /* `\.{<<}'\,; corresponds to MIT's {\tentex\char'20} */ +@d gt_gt 021 /* `\.{>>}'\,; corresponds to MIT's {\tentex\char'21} */ +@d plus_plus 013 /* `\.{++}'\,; corresponds to MIT's {\tentex\char'13} */ +@d minus_minus 01 /* `\.{--}'\,; corresponds to MIT's {\tentex\char'1} */ +@d minus_gt 031 /* `\.{->}'\,; corresponds to MIT's {\tentex\char'31} */ +@d not_eq 032 /* `\.{!=}'\,; corresponds to MIT's {\tentex\char'32} */ +@d lt_eq 034 /* `\.{<=}'\,; corresponds to MIT's {\tentex\char'34} */ +@d gt_eq 035 /* `\.{>=}'\,; corresponds to MIT's {\tentex\char'35} */ +@d eq_eq 036 /* `\.{==}'\,; corresponds to MIT's {\tentex\char'36} */ +@d or_or 037 /* `\.{\v\v}'\,; corresponds to MIT's {\tentex\char'37} */ +@d dot_dot_dot 016 /* `\.{...}'\,; corresponds to MIT's {\tentex\char'16} */ +@d colon_colon 06 /* `\.{::}'\,; corresponds to MIT's {\tentex\char'6} */ +@d period_ast 026 /* `\.{.*}'\,; corresponds to MIT's {\tentex\char'26} */ +@d minus_gt_ast 027 /* `\.{->*}'\,; corresponds to MIT's {\tentex\char'27} */ + +@<Common code...@>= +char section_text[longest_name+1]; /* name being sought for */ +char *section_text_end = section_text+longest_name; /* end of |section_text| */ +char *id_first; /* where the current identifier begins in the buffer */ +char *id_loc; /* just after the current identifier in the buffer */ + +@ Code related to input routines: + +@d xisalpha(c) (isalpha(c)&&((eight_bits)c<0200)) +@d xisdigit(c) (isdigit(c)&&((eight_bits)c<0200)) +@d xisspace(c) (isspace(c)&&((eight_bits)c<0200)) +@d xislower(c) (islower(c)&&((eight_bits)c<0200)) +@d xisupper(c) (isupper(c)&&((eight_bits)c<0200)) +@d xisxdigit(c) (isxdigit(c)&&((eight_bits)c<0200)) + +@<Common code...@>= +extern char buffer[]; /* where each line of input goes */ +extern char *buffer_end; /* end of |buffer| */ +extern char *loc; /* points to the next character to be read from the buffer*/ +extern char *limit; /* points to the last character in the buffer */ + +@ Code related to identifier and section name storage: +@d length(c) (c+1)->byte_start-(c)->byte_start /* the length of a name */ +@d print_id(c) term_write((c)->byte_start,length((c))) /* print identifier */ +@d llink link /* left link in binary search tree for section names */ +@d rlink dummy.Rlink /* right link in binary search tree for section names */ +@d root name_dir->rlink /* the root of the binary search tree + for section names */ +@d chunk_marker 0 + +@<Common code...@>= +typedef struct name_info { + char *byte_start; /* beginning of the name in |byte_mem| */ + struct name_info *link; + union { + struct name_info *Rlink; /* right link in binary search tree for section + names */ + char Ilk; /* used by identifiers in \.{CWEAVE} only */ + } dummy; + char *equiv_or_xref; /* info corresponding to names */ +} name_info; /* contains information about an identifier or section name */ +typedef name_info *name_pointer; /* pointer into array of \&{name\_info}s */ +typedef name_pointer *hash_pointer; +extern char byte_mem[]; /* characters of names */ +extern char *byte_mem_end; /* end of |byte_mem| */ +extern name_info name_dir[]; /* information about names */ +extern name_pointer name_dir_end; /* end of |name_dir| */ +extern name_pointer name_ptr; /* first unused position in |name_dir| */ +extern char *byte_ptr; /* first unused position in |byte_mem| */ +extern name_pointer hash[]; /* heads of hash lists */ +extern hash_pointer hash_end; /* end of |hash| */ +extern hash_pointer h; /* index into hash-head array */ +extern name_pointer id_lookup(); /* looks up a string in the identifier table */ +extern name_pointer section_lookup(); /* finds section name */ +extern void print_section_name(), sprint_section_name(); + +@ Code related to error handling: +@d spotless 0 /* |history| value for normal jobs */ +@d harmless_message 1 /* |history| value when non-serious info was printed */ +@d error_message 2 /* |history| value when an error was noted */ +@d fatal_message 3 /* |history| value when we had to stop prematurely */ +@d mark_harmless {if (history==spotless) history=harmless_message;} +@d mark_error history=error_message +@d confusion(s) fatal("! This can't happen: ",s) + +@<Common...@>= +extern history; /* indicates how bad this run was */ +extern err_print(); /* print error message and context */ +extern wrap_up(); /* indicate |history| and exit */ +extern void fatal(); /* issue error message and die */ +extern void overflow(); /* succumb because a table has overflowed */ + +@ Code related to file handling: +@f line x /* make |line| an unreserved word */ +@d max_file_name_length 60 +@d cur_file file[include_depth] /* current file */ +@d cur_file_name file_name[include_depth] /* current file name */ +@d web_file_name file_name[0] /* main source file name */ +@d cur_line line[include_depth] /* number of current line in current file */ + +@<Common code...@>= +extern include_depth; /* current level of nesting */ +extern FILE *file[]; /* stack of non-change files */ +extern FILE *change_file; /* change file */ +extern char C_file_name[]; /* name of |C_file| */ +extern char tex_file_name[]; /* name of |tex_file| */ +extern char idx_file_name[]; /* name of |idx_file| */ +extern char scn_file_name[]; /* name of |scn_file| */ +extern char file_name[][max_file_name_length]; + /* stack of non-change file names */ +extern char change_file_name[]; /* name of change file */ +extern line[]; /* number of current line in the stacked files */ +extern change_line; /* number of current line in change file */ +extern change_depth; /* where \.{@@y} originated during a change */ +extern boolean input_has_ended; /* if there is no more input */ +extern boolean changing; /* if the current line is from |change_file| */ +extern boolean web_file_open; /* if the web file is being read */ +extern reset_input(); /* initialize to read the web file and change file */ +extern get_line(); /* inputs the next line */ +extern check_complete(); /* checks that all changes were picked up */ + +@ Code related to section numbers: +@<Common code...@>= +typedef unsigned short sixteen_bits; +extern sixteen_bits section_count; /* the current section number */ +extern boolean changed_section[]; /* is the section changed? */ +extern boolean change_pending; /* is a decision about change still unclear? */ +extern boolean print_where; /* tells \.{CTANGLE} to print line and file info */ + +@ Code related to command line arguments: +@d show_banner flags['b'] /* should the banner line be printed? */ +@d show_progress flags['p'] /* should progress reports be printed? */ +@d show_happiness flags['h'] /* should lack of errors be announced? */ + +@<Common code...@>= +extern int argc; /* copy of |ac| parameter to |main| */ +extern char **argv; /* copy of |av| parameter to |main| */ +extern boolean flags[]; /* an option for each 7-bit code */ + +@ Code relating to output: +@d update_terminal fflush(stdout) /* empty the terminal output buffer */ +@d new_line putchar('\n') @d putxchar putchar +@d term_write(a,b) fflush(stdout),fwrite(a,sizeof(char),b,stdout) +@d C_printf(c,a) fprintf(C_file,c,a) +@d C_putc(c) putc(c,C_file) + +@<Common code...@>= +extern FILE *C_file; /* where output of \.{CTANGLE} goes */ +extern FILE *tex_file; /* where output of \.{CWEAVE} goes */ +extern FILE *idx_file; /* where index from \.{CWEAVE} goes */ +extern FILE *scn_file; /* where list of sections from \.{CWEAVE} goes */ +extern FILE *active_file; /* currently active file for \.{CWEAVE} output */ + +@ The procedure that gets everything rolling: + +@<Common code...@>= +extern void common_init(); diff --git a/web/c_cpp/cweb/common.w b/web/c_cpp/cweb/common.w new file mode 100644 index 0000000000..d8aacfb7b9 --- /dev/null +++ b/web/c_cpp/cweb/common.w @@ -0,0 +1,1418 @@ +% 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.64 --- January 2002 + +% 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. + +\def\v{\char'174} % vertical (|) in typewriter font + +\def\title{Common code for CTANGLE and CWEAVE (Version 3.64)} +\def\topofcontents{\null\vfill + \centerline{\titlefont Common code for {\ttitlefont CTANGLE} and + {\ttitlefont CWEAVE}} + \vskip 15pt + \centerline{(Version 3.64)} + \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 +@s not_eq normal @q unreserve a C++ keyword @> + +@** Introduction. This file contains code common +to both \.{CTANGLE} and \.{CWEAVE}, which roughly concerns the following +problems: character uniformity, input routines, error handling and +parsing of command line. We have tried to concentrate in this file +all the system dependencies, so as to maximize portability. + +In the texts below we will +sometimes use \.{CWEB} to refer to either of the two component +programs, if no confusion can arise. + +The file begins with a few basic definitions. + +@c +@<Include files@>@/ +@h +@<Definitions that should agree with \.{CTANGLE} and \.{CWEAVE}@>@/ +@<Other definitions@>@/ +@<Predeclaration of procedures@>@/ + +@ In certain cases \.{CTANGLE} and \.{CWEAVE} should do almost, but not +quite, the same thing. In these cases we've written common code for +both, differentiating between the two by means of the global variable +|program|. + +@d ctangle 0 +@d cweave 1 + +@<Definitions...@>= +typedef short boolean; +boolean program; /* \.{CWEAVE} or \.{CTANGLE}? */ + +@ \.{CWEAVE} 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 file, and finally it sorts and outputs the index. +Similarly, \.{CTANGLE} operates in two phases. +The global variable |phase| tells which phase we are in. + +@<Other...@>= int phase; /* which phase are we in? */ + +@ There's an initialization procedure that gets both \.{CTANGLE} and +\.{CWEAVE} off to a good start. We will fill in the details of this +procedure later. + +@c +void +common_init() +{ + @<Initialize pointers@>; + @<Set the default options common to \.{CTANGLE} and \.{CWEAVE}@>; + @<Scan arguments and open output files@>; +} + +@* The character set. +\.{CWEB} uses the conventions of \CEE/ programs found in the standard +\.{ctype.h} header file. + +@<Include files@>= +#include <ctype.h> + +@ A few character pairs are encoded internally as single characters, +using the definitions below. These definitions are consistent with +an extension of ASCII code originally developed at MIT and explained in +Appendix~C of {\sl The \TEX/book\/}; thus, users who have such a +character set can type things like \.{\char'32} and \.{\char'4} instead +of \.{!=} and \.{\&\&}. (However, their files will not be too portable +until more people adopt the extended code.) + +If the character set is not ASCII, the definitions given here may conflict +with existing characters; in such cases, other arbitrary codes should be +substituted. The indexes to \.{CTANGLE} and \.{CWEAVE} mention every +case where similar codes may have to be changed in order to +avoid character conflicts. Look for the entry ``ASCII code dependencies'' +in those indexes. + +@^ASCII code dependencies@> +@^system dependencies@> + +@d and_and 04 /* `\.{\&\&}'\,; corresponds to MIT's {\tentex\char'4} */ +@d lt_lt 020 /* `\.{<<}'\,; corresponds to MIT's {\tentex\char'20} */ +@d gt_gt 021 /* `\.{>>}'\,; corresponds to MIT's {\tentex\char'21} */ +@d plus_plus 013 /* `\.{++}'\,; corresponds to MIT's {\tentex\char'13} */ +@d minus_minus 01 /* `\.{--}'\,; corresponds to MIT's {\tentex\char'1} */ +@d minus_gt 031 /* `\.{->}'\,; corresponds to MIT's {\tentex\char'31} */ +@d not_eq 032 /* `\.{!=}'\,; corresponds to MIT's {\tentex\char'32} */ +@d lt_eq 034 /* `\.{<=}'\,; corresponds to MIT's {\tentex\char'34} */ +@d gt_eq 035 /* `\.{>=}'\,; corresponds to MIT's {\tentex\char'35} */ +@d eq_eq 036 /* `\.{==}'\,; corresponds to MIT's {\tentex\char'36} */ +@d or_or 037 /* `\.{\v\v}'\,; corresponds to MIT's {\tentex\char'37} */ +@d dot_dot_dot 016 /* `\.{...}'\,; corresponds to MIT's {\tentex\char'16} */ +@d colon_colon 06 /* `\.{::}'\,; corresponds to MIT's {\tentex\char'6} */ +@d period_ast 026 /* `\.{.*}'\,; corresponds to MIT's {\tentex\char'26} */ +@d minus_gt_ast 027 /* `\.{->*}'\,; corresponds to MIT's {\tentex\char'27} */ + +@** Input routines. The lowest level of input to the \.{CWEB} programs +is performed by |input_ln|, which must be told which file to read from. +The return value of |input_ln| is 1 if the read is successful and 0 if +not (generally this means the file has ended). The conventions +of \TEX/ are followed; i.e., the characters of the next line of the file +are copied into the |buffer| array, +and the global variable |limit| is set to the first unoccupied position. +Trailing blanks are ignored. The value of |limit| must be strictly less +than |buf_size|, so that |buffer[buf_size-1]| is never filled. + +Since |buf_size| is strictly less than |long_buf_size|, +some of \.{CWEB}'s routines use the fact that it is safe to refer to +|*(limit+2)| without overstepping the bounds of the array. + +@d buf_size 100 /* for \.{CWEAVE} and \.{CTANGLE} */ +@d longest_name 10000 +@d long_buf_size (buf_size+longest_name) /* for \.{CWEAVE} */ +@d xisspace(c) (isspace(c)&&((unsigned char)c<0200)) +@d xisupper(c) (isupper(c)&&((unsigned char)c<0200)) + +@<Definitions...@>= +char buffer[long_buf_size]; /* where each line of input goes */ +char *buffer_end=buffer+buf_size-2; /* end of |buffer| */ +char *limit=buffer; /* points to the last character in the buffer */ +char *loc=buffer; /* points to the next character to be read from the buffer */ + +@ @<Include files@>= +#include <stdio.h> + +@ In the unlikely event that your standard I/O library does not +support |feof|, |getc|, and |ungetc| you may have to change things here. +@^system dependencies@> + +@c +int input_ln(fp) /* copies a line into |buffer| or returns 0 */ +FILE *fp; /* what file to read from */ +{ + register int c=EOF; /* character read; initialized so some compilers won't complain */ + register char *k; /* where next character goes */ + if (feof(fp)) return(0); /* we have hit end-of-file */ + limit = k = buffer; /* beginning of buffer */ + while (k<=buffer_end && (c=getc(fp)) != EOF && c!='\n') + if ((*(k++) = c) != ' ') limit = k; + if (k>buffer_end) + if ((c=getc(fp))!=EOF && c!='\n') { + ungetc(c,fp); loc=buffer; err_print("! Input line too long"); +@.Input line too long@> + } + if (c==EOF && limit==buffer) return(0); /* there was nothing after + the last newline */ + return(1); +} + +@ Now comes the problem of deciding which file to read from next. +Recall that the actual text that \.{CWEB} should process comes from two +streams: a |web_file|, which can contain possibly nested include +commands \.{@@i}, and a |change_file|, which might also contain +includes. The |web_file| together with the currently open include +files form a stack |file|, whose names are stored in a parallel stack +|file_name|. The boolean |changing| tells whether or not we're reading +from the |change_file|. + +The line number of each open file is also kept for error reporting and +for the benefit of \.{CTANGLE}. + +@f line x /* make |line| an unreserved word */ +@d max_include_depth 10 /* maximum number of source files open + simultaneously, not counting the change file */ +@d max_file_name_length 60 +@d cur_file file[include_depth] /* current file */ +@d cur_file_name file_name[include_depth] /* current file name */ +@d cur_line line[include_depth] /* number of current line in current file */ +@d web_file file[0] /* main source file */ +@d web_file_name file_name[0] /* main source file name */ + +@<Definitions...@>= +int include_depth; /* current level of nesting */ +FILE *file[max_include_depth]; /* stack of non-change files */ +FILE *change_file; /* change file */ +char file_name[max_include_depth][max_file_name_length]; + /* stack of non-change file names */ +char change_file_name[max_file_name_length]; /* name of change file */ +char alt_web_file_name[max_file_name_length]; /* alternate name to try */ +int line[max_include_depth]; /* number of current line in the stacked files */ +int change_line; /* number of current line in change file */ +int change_depth; /* where \.{@@y} originated during a change */ +boolean input_has_ended; /* if there is no more input */ +boolean changing; /* if the current line is from |change_file| */ +boolean web_file_open=0; /* if the web file is being read */ + +@ When |changing==0|, the next line of |change_file| is kept in +|change_buffer|, for purposes of comparison with the next +line of |cur_file|. After the change file has been completely input, we +set |change_limit=change_buffer|, +so that no further matches will be made. + +Here's a shorthand expression for inequality between the two lines: + +@d lines_dont_match (change_limit-change_buffer != limit-buffer || + strncmp(buffer, change_buffer, limit-buffer)) + +@<Other...@>= +char change_buffer[buf_size]; /* next line of |change_file| */ +char *change_limit; /* points to the last character in |change_buffer| */ + +@ Procedure |prime_the_change_buffer| +sets |change_buffer| in preparation for the next matching operation. +Since blank lines in the change file are not used for matching, we have +|(change_limit==change_buffer && !changing)| if and only if +the change file is exhausted. This procedure is called only when +|changing| is 1; hence error messages will be reported correctly. + +@c +void +prime_the_change_buffer() +{ + change_limit=change_buffer; /* this value is used if the change file ends */ + @<Skip over comment lines in the change file; |return| if end of file@>; + @<Skip to the next nonblank line; |return| if end of file@>; + @<Move |buffer| and |limit| to |change_buffer| and |change_limit|@>; +} + +@ While looking for a line that begins with \.{@@x} in the change file, we +allow lines that begin with \.{@@}, as long as they don't begin with \.{@@y}, +\.{@@z}, or \.{@@i} (which would probably mean that the change file is fouled up). + +@<Skip over comment lines in the change file...@>= +while(1) { + change_line++; + if (!input_ln(change_file)) return; + if (limit<buffer+2) continue; + if (buffer[0]!='@@') continue; + if (xisupper(buffer[1])) buffer[1]=tolower(buffer[1]); + if (buffer[1]=='x') break; + if (buffer[1]=='y' || buffer[1]=='z' || buffer[1]=='i') { + loc=buffer+2; + err_print("! Missing @@x in change file"); +@.Missing @@x...@> + } +} + +@ Here we are looking at lines following the \.{@@x}. + +@<Skip to the next nonblank line...@>= +do { + change_line++; + if (!input_ln(change_file)) { + err_print("! Change file ended after @@x"); +@.Change file ended...@> + return; + } +} while (limit==buffer); + +@ @<Move |buffer| and |limit| to |change_buffer| and |change_limit|@>= +{ + change_limit=change_buffer+(limit-buffer); + strncpy(change_buffer,buffer,limit-buffer+1); +} + +@ The following procedure is used to see if the next change entry should +go into effect; it is called only when |changing| is 0. +The idea is to test whether or not the current +contents of |buffer| matches the current contents of |change_buffer|. +If not, there's nothing more to do; but if so, a change is called for: +All of the text down to the \.{@@y} is supposed to match. An error +message is issued if any discrepancy is found. Then the procedure +prepares to read the next line from |change_file|. + +When a match is found, the current section is marked as changed unless +the first line after the \.{@@x} and after the \.{@@y} both start with +either |'@@*'| or |'@@ '| (possibly preceded by whitespace). + +This procedure is called only when |buffer<limit|, i.e., when the +current line is nonempty. + +@d if_section_start_make_pending(b) {@+*limit='!'; + for (loc=buffer;xisspace(*loc);loc++) ; + *limit=' '; + if (*loc=='@@' && (xisspace(*(loc+1)) || *(loc+1)=='*')) change_pending=b; +} + +@c +void +check_change() /* switches to |change_file| if the buffers match */ +{ + int n=0; /* the number of discrepancies found */ + if (lines_dont_match) return; + change_pending=0; + if (!changed_section[section_count]) { + if_section_start_make_pending(1); + if (!change_pending) changed_section[section_count]=1; + } + while (1) { + changing=1; print_where=1; change_line++; + if (!input_ln(change_file)) { + err_print("! Change file ended before @@y"); +@.Change file ended...@> + change_limit=change_buffer; changing=0; + return; + } + if (limit>buffer+1 && buffer[0]=='@@') { + char xyz_code=xisupper(buffer[1])? tolower(buffer[1]): buffer[1]; + @<If the current line starts with \.{@@y}, + report any discrepancies and |return|@>; + } + @<Move |buffer| and |limit|...@>; + changing=0; cur_line++; + while (!input_ln(cur_file)) { /* pop the stack or quit */ + if (include_depth==0) { + err_print("! CWEB file ended during a change"); +@.CWEB file ended...@> + input_has_ended=1; return; + } + include_depth--; cur_line++; + } + if (lines_dont_match) n++; + } +} + +@ @<If the current line starts with \.{@@y}...@>= +if (xyz_code=='x' || xyz_code=='z') { + loc=buffer+2; err_print("! Where is the matching @@y?"); +@.Where is the match...@> + } +else if (xyz_code=='y') { + if (n>0) { + loc=buffer+2; + printf("\n! Hmm... %d ",n); + err_print("of the preceding lines failed to match"); +@.Hmm... n of the preceding...@> + } + change_depth=include_depth; + return; +} + +@ The |reset_input| procedure, which gets \.{CWEB} ready to read the +user's \.{CWEB} input, is used at the beginning of phase one of \.{CTANGLE}, +phases one and two of \.{CWEAVE}. + +@c +void +reset_input() +{ + limit=buffer; loc=buffer+1; buffer[0]=' '; + @<Open input files@>; + include_depth=0; cur_line=0; change_line=0; + change_depth=include_depth; + changing=1; prime_the_change_buffer(); changing=!changing; + limit=buffer; loc=buffer+1; buffer[0]=' '; input_has_ended=0; +} + +@ The following code opens the input files. +@^system dependencies@> + +@<Open input files@>= +if ((web_file=fopen(web_file_name,"r"))==NULL) { + strcpy(web_file_name,alt_web_file_name); + if ((web_file=fopen(web_file_name,"r"))==NULL) + fatal("! Cannot open input file ", web_file_name); +} +@.Cannot open input file@> +@.Cannot open change file@> +web_file_open=1; +if ((change_file=fopen(change_file_name,"r"))==NULL) + fatal("! Cannot open change file ", change_file_name); + +@ The |get_line| procedure is called when |loc>limit|; it puts the next +line of merged input into the buffer and updates the other variables +appropriately. A space is placed at the right end of the line. +This procedure returns |!input_has_ended| because we often want to +check the value of that variable after calling the procedure. + +If we've just changed from the |cur_file| to the |change_file|, or if +the |cur_file| has changed, we tell \.{CTANGLE} to print this +information in the \CEE/ file by means of the |print_where| flag. + +@d max_sections 2000 /* number of identifiers, strings, section names; + must be less than 10240 */ + +@<Defin...@>= +typedef unsigned short sixteen_bits; +sixteen_bits section_count; /* the current section number */ +boolean changed_section[max_sections]; /* is the section changed? */ +boolean change_pending; /* if the current change is not yet recorded in + |changed_section[section_count]| */ +boolean print_where=0; /* should \.{CTANGLE} print line and file info? */ + +@ @c +int get_line() /* inputs the next line */ +{ + restart: + if (changing && include_depth==change_depth) + @<Read from |change_file| and maybe turn off |changing|@>; + if (! changing || include_depth>change_depth) { + @<Read from |cur_file| and maybe turn on |changing|@>; + if (changing && include_depth==change_depth) goto restart; + } + if (input_has_ended) return 0; + loc=buffer; *limit=' '; + if (buffer[0]=='@@' && (buffer[1]=='i' || buffer[1]=='I')) { + loc=buffer+2; *limit='"'; + while (*loc==' '||*loc=='\t') loc++; + if (loc>=limit) { + err_print("! Include file name not given"); +@.Include file name ...@> + goto restart; + } + if (include_depth>=max_include_depth-1) { + err_print("! Too many nested includes"); +@.Too many nested includes@> + goto restart; + } + include_depth++; /* push input stack */ + @<Try to open include file, abort push if unsuccessful, go to |restart|@>; + } + return 1; +} + +@ When an \.{@@i} line is found in the |cur_file|, we must temporarily +stop reading it and start reading from the named include file. The +\.{@@i} line should give a complete file name with or without +double quotes. +If the environment variable \.{CWEBINPUTS} is set, or if the compiler flag +of the same name was defined at compile time, +\.{CWEB} will look for include files in the directory thus named, if +it cannot find them in the current directory. +(Colon-separated paths are not supported.) +The remainder of the \.{@@i} line after the file name is ignored. + +@d too_long() {include_depth--; + err_print("! Include file name too long"); goto restart;} + +@<Include...@>= +#include <stdlib.h> /* declaration of |getenv| and |exit| */ + +@ @<Try to open...@>= { + char temp_file_name[max_file_name_length]; + char *cur_file_name_end=cur_file_name+max_file_name_length-1; + char *k=cur_file_name, *kk; + int l; /* length of file name */ + + if (*loc=='"') { + loc++; + while (*loc!='"' && k<=cur_file_name_end) *k++=*loc++; + if (loc==limit) k=cur_file_name_end+1; /* unmatched quote is `too long' */ + } else + while (*loc!=' '&&*loc!='\t'&&*loc!='"'&&k<=cur_file_name_end) *k++=*loc++; + if (k>cur_file_name_end) too_long(); +@.Include file name ...@> + *k='\0'; + if ((cur_file=fopen(cur_file_name,"r"))!=NULL) { + cur_line=0; print_where=1; + goto restart; /* success */ + } + kk=getenv("CWEBINPUTS"); + if (kk!=NULL) { + if ((l=strlen(kk))>max_file_name_length-2) too_long(); + strcpy(temp_file_name,kk); + } + else { +#ifdef CWEBINPUTS + if ((l=strlen(CWEBINPUTS))>max_file_name_length-2) too_long(); + strcpy(temp_file_name,CWEBINPUTS); +#else + l=0; +#endif /* |CWEBINPUTS| */ + } + if (l>0) { + if (k+l+2>=cur_file_name_end) too_long(); +@.Include file name ...@> + for (; k>= cur_file_name; k--) *(k+l+1)=*k; + strcpy(cur_file_name,temp_file_name); + cur_file_name[l]='/'; /* \UNIX/ pathname separator */ + if ((cur_file=fopen(cur_file_name,"r"))!=NULL) { + cur_line=0; print_where=1; + goto restart; /* success */ + } + } + include_depth--; err_print("! Cannot open include file"); goto restart; +} + +@ @<Read from |cur_file|...@>= { + cur_line++; + while (!input_ln(cur_file)) { /* pop the stack or quit */ + print_where=1; + if (include_depth==0) {input_has_ended=1; break;} + else { + fclose(cur_file); include_depth--; + if (changing && include_depth==change_depth) break; + cur_line++; + } + } + if (!changing && !input_has_ended) + if (limit-buffer==change_limit-change_buffer) + if (buffer[0]==change_buffer[0]) + if (change_limit>change_buffer) check_change(); +} + +@ @<Read from |change_file|...@>= { + change_line++; + if (!input_ln(change_file)) { + err_print("! Change file ended without @@z"); +@.Change file ended...@> + buffer[0]='@@'; buffer[1]='z'; limit=buffer+2; + } + if (limit>buffer) { /* check if the change has ended */ + if (change_pending) { + if_section_start_make_pending(0); + if (change_pending) { + changed_section[section_count]=1; change_pending=0; + } + } + *limit=' '; + if (buffer[0]=='@@') { + if (xisupper(buffer[1])) buffer[1]=tolower(buffer[1]); + if (buffer[1]=='x' || buffer[1]=='y') { + loc=buffer+2; + err_print("! Where is the matching @@z?"); +@.Where is the match...@> + } + else if (buffer[1]=='z') { + prime_the_change_buffer(); changing=!changing; print_where=1; + } + } + } +} + +@ At the end of the program, we will tell the user if the change file +had a line that didn't match any relevant line in |web_file|. + +@c +void +check_complete(){ + if (change_limit!=change_buffer) { /* |changing| is 0 */ + strncpy(buffer,change_buffer,change_limit-change_buffer+1); + limit=buffer+(int)(change_limit-change_buffer); + changing=1; change_depth=include_depth; loc=buffer; + err_print("! Change file entry did not match"); +@.Change file entry did not match@> + } +} + +@** Storage of names and strings. +Both \.{CWEAVE} and \.{CTANGLE} store identifiers, section names and +other strings in a large array of |char|s, called |byte_mem|. +Information about the names is kept in the array |name_dir|, whose +elements are structures of type |name_info|, containing a pointer into +the |byte_mem| array (the address where the name begins) and other data. +A |name_pointer| variable is a pointer into |name_dir|. + +@d max_bytes 90000 /* the number of bytes in identifiers, + index entries, and section names; must be less than $2^{24}$ */ +@d max_names 4000 /* number of identifiers, strings, section names; + must be less than 10240 */ + +@<Definitions that...@>= +typedef struct name_info { + char *byte_start; /* beginning of the name in |byte_mem| */ + @<More elements of |name_info| structure@>@; +} name_info; /* contains information about an identifier or section name */ +typedef name_info *name_pointer; /* pointer into array of |name_info|s */ +char byte_mem[max_bytes]; /* characters of names */ +char *byte_mem_end = byte_mem+max_bytes-1; /* end of |byte_mem| */ +name_info name_dir[max_names]; /* information about names */ +name_pointer name_dir_end = name_dir+max_names-1; /* end of |name_dir| */ + +@ The actual sequence of characters in the name pointed to by a |name_pointer +p| appears in positions |p->byte_start| to |(p+1)->byte_start-1|, inclusive. +The |print_id| macro prints this text on the user's terminal. + +@d length(c) (c+1)->byte_start-(c)->byte_start /* the length of a name */ +@d print_id(c) term_write((c)->byte_start,length((c))) /* print identifier */ + +@ The first unused position in |byte_mem| and |name_dir| is +kept in |byte_ptr| and |name_ptr|, respectively. Thus we +usually have |name_ptr->byte_start==byte_ptr|, and certainly +we want to keep |name_ptr<=name_dir_end| and |byte_ptr<=byte_mem_end|. + +@<Defini...@>= +name_pointer name_ptr; /* first unused position in |name_dir| */ +char *byte_ptr; /* first unused position in |byte_mem| */ + +@ @<Init...@>= +name_dir->byte_start=byte_ptr=byte_mem; /* position zero in both arrays */ +name_ptr=name_dir+1; /* |name_dir[0]| will be used only for error recovery */ +name_ptr->byte_start=byte_mem; /* this makes name 0 of length zero */ + +@ The names of identifiers are found by computing a hash address |h| and +then looking at strings of bytes signified by the |name_pointer|s +|hash[h]|, |hash[h]->link|, |hash[h]->link->link|, \dots, +until either finding the desired name or encountering the null pointer. + +@<More elements of |name...@>= +struct name_info *link; + +@ The hash table itself +consists of |hash_size| entries of type |name_pointer|, and is +updated by the |id_lookup| procedure, which finds a given identifier +and returns the appropriate |name_pointer|. The matching is done by the +function |names_match|, which is slightly different in +\.{CWEAVE} and \.{CTANGLE}. If there is no match for the identifier, +it is inserted into the table. + +@d hash_size 353 /* should be prime */ + +@<Defini...@>= +typedef name_pointer *hash_pointer; +name_pointer hash[hash_size]; /* heads of hash lists */ +hash_pointer hash_end = hash+hash_size-1; /* end of |hash| */ +hash_pointer h; /* index into hash-head array */ + +@ @<Predec...@>= +extern int names_match(); + +@ Initially all the hash lists are empty. + +@<Init...@>= +for (h=hash; h<=hash_end; *h++=NULL) ; + +@ Here is the main procedure for finding identifiers: + +@c +name_pointer +id_lookup(first,last,t) /* looks up a string in the identifier table */ +char *first; /* first character of string */ +char *last; /* last character of string plus one */ +char t; /* the |ilk|; used by \.{CWEAVE} only */ +{ + char *i=first; /* position in |buffer| */ + int h; /* hash code */ + int l; /* length of the given identifier */ + name_pointer p; /* where the identifier is being sought */ + if (last==NULL) for (last=first; *last!='\0'; last++); + l=last-first; /* compute the length */ + @<Compute the hash code |h|@>; + @<Compute the name location |p|@>; + if (p==name_ptr) @<Enter a new name into the table at position |p|@>; + return(p); +} + +@ A simple hash code is used: If the sequence of +character codes is $c_1c_2\ldots c_n$, its hash value will be +$$(2^{n-1}c_1+2^{n-2}c_2+\cdots+c_n)\,\bmod\,|hash_size|.$$ + +@<Compute the hash...@>= +h=(unsigned char)*i; +while (++i<last) h=(h+h+(int)((unsigned char)*i)) % hash_size; +@^high-bit character handling@> + +@ If the identifier is new, it will be placed in position |p=name_ptr|, +otherwise |p| will point to its existing location. + +@<Compute the name location...@>= +p=hash[h]; +while (p && !names_match(p,first,l,t)) p=p->link; +if (p==NULL) { + p=name_ptr; /* the current identifier is new */ + p->link=hash[h]; hash[h]=p; /* insert |p| at beginning of hash list */ +} + +@ The information associated with a new identifier must be initialized +in a slightly different way in \.{CWEAVE} than in \.{CTANGLE}; hence the +|init_p| procedure. + +@<Pred...@>= +void init_p(); + +@ @<Enter a new name...@>= { + if (byte_ptr+l>byte_mem_end) overflow("byte memory"); + if (name_ptr>=name_dir_end) overflow("name"); + strncpy(byte_ptr,first,l); + (++name_ptr)->byte_start=byte_ptr+=l; + if (program==cweave) init_p(p,t); +} + +@ The names of sections are stored in |byte_mem| together +with the identifier names, but a hash table is not used for them because +\.{CTANGLE} needs to be able to recognize a section name when given a prefix of +that name. A conventional binary search tree is used to retrieve section names, +with fields called |llink| and |rlink| (where |llink| takes the place +of |link|). The root of this tree is stored in |name_dir->rlink|; +this will be the only information in |name_dir[0]|. + +Since the space used by |rlink| has a different function for +identifiers than for section names, we declare it as a |union|. + +@d llink link /* left link in binary search tree for section names */ +@d rlink dummy.Rlink /* right link in binary search tree for section names */ +@d root name_dir->rlink /* the root of the binary search tree + for section names */ + +@<More elements of |name...@>= +union { + struct name_info *Rlink; /* right link in binary search tree for section + names */ + char Ilk; /* used by identifiers in \.{CWEAVE} only */ +} dummy; + +@ @<Init...@>= +root=NULL; /* the binary search tree starts out with nothing in it */ + +@ If |p| is a |name_pointer| variable, as we have seen, +|p->byte_start| is the beginning of the area where the name +corresponding to |p| is stored. However, if |p| refers to a section +name, the name may need to be stored in chunks, because it may +``grow'': a prefix of the section name may be encountered before +the full name. Furthermore we need to know the length of the shortest +prefix of the name that was ever encountered. + +We solve this problem by inserting two extra bytes at |p->byte_start|, +representing the length of the shortest prefix, when |p| is a +section name. Furthermore, the last byte of the name will be a blank +space if |p| is a prefix. In the latter case, the name pointer +|p+1| will allow us to access additional chunks of the name: +The second chunk will begin at the name pointer |(p+1)->link|, +and if it too is a prefix (ending with blank) its |link| will point +to additional chunks in the same way. Null links are represented by +|name_dir|. + +@d first_chunk(p) ((p)->byte_start+2) +@d prefix_length(p) (int)((unsigned char)*((p)->byte_start)*256 + + (unsigned char)*((p)->byte_start+1)) +@d set_prefix_length(p,m) (*((p)->byte_start)=(m)/256, + *((p)->byte_start+1)=(m)%256) + +@c +void +print_section_name(p) +name_pointer p; +{ + char *ss, *s = first_chunk(p); + name_pointer q = p+1; + while (p!=name_dir) { + ss = (p+1)->byte_start-1; + if (*ss==' ' && ss>=s) { + term_write(s,ss-s); p=q->link; q=p; + } else { + term_write(s,ss+1-s); p=name_dir; q=NULL; + } + s = p->byte_start; + } + if (q) term_write("...",3); /* complete name not yet known */ +} + +@ @c +void +sprint_section_name(dest,p) + char*dest; + name_pointer p; +{ + char *ss, *s = first_chunk(p); + name_pointer q = p+1; + while (p!=name_dir) { + ss = (p+1)->byte_start-1; + if (*ss==' ' && ss>=s) { + p=q->link; q=p; + } else { + ss++; p=name_dir; + } + strncpy(dest,s,ss-s), dest+=ss-s; + s = p->byte_start; + } + *dest='\0'; +} + +@ @c +void +print_prefix_name(p) +name_pointer p; +{ + char *s = first_chunk(p); + int l = prefix_length(p); + term_write(s,l); + if (s+l<(p+1)->byte_start) term_write("...",3); +} + +@ When we compare two section names, we'll need a function analogous to +|strcmp|. But we do not assume the strings +are null-terminated, and we keep an eye open for prefixes and extensions. + +@d less 0 /* the first name is lexicographically less than the second */ +@d equal 1 /* the first name is equal to the second */ +@d greater 2 /* the first name is lexicographically greater than the second */ +@d prefix 3 /* the first name is a proper prefix of the second */ +@d extension 4 /* the first name is a proper extension of the second */ + +@c +int web_strcmp(j,j_len,k,k_len) /* fuller comparison than |strcmp| */ + char *j, *k; /* beginning of first and second strings */ + int j_len, k_len; /* length of strings */ +{ + char *j1=j+j_len, *k1=k+k_len; + while (k<k1 && j<j1 && *j==*k) k++, j++; + if (k==k1) if (j==j1) return equal; + else return extension; + else if (j==j1) return prefix; + else if (*j<*k) return less; + else return greater; +} + +@ Adding a section name to the tree is straightforward if we know its +parent and whether it's the |rlink| or |llink| of the parent. As a +special case, when the name is the first section being added, we set the +``parent'' to |NULL|. When a section name is created, it has only one +chunk, which however may be just a prefix; the full name will +hopefully be unveiled later. Obviously, |prefix_length| starts +out as the length of the first chunk, though it may decrease later. + +The information associated with a new node must be initialized +differently in \.{CWEAVE} and \.{CTANGLE}; hence the +|init_node| procedure, which is defined differently in \.{cweave.w} +and \.{ctangle.w}. + +@<Prede...@>= +extern void init_node(); + +@ @c +name_pointer +add_section_name(par,c,first,last,ispref) /* install a new node in the tree */ +name_pointer par; /* parent of new node */ +int c; /* right or left? */ +char *first; /* first character of section name */ +char *last; /* last character of section name, plus one */ +int ispref; /* are we adding a prefix or a full name? */ +{ + name_pointer p=name_ptr; /* new node */ + char *s=first_chunk(p); + int name_len=last-first+ispref; /* length of section name */ + if (s+name_len>byte_mem_end) overflow("byte memory"); + if (name_ptr+1>=name_dir_end) overflow("name"); + (++name_ptr)->byte_start=byte_ptr=s+name_len; + if (ispref) { + *(byte_ptr-1)=' '; + name_len--; + name_ptr->link=name_dir; + (++name_ptr)->byte_start=byte_ptr; + } + set_prefix_length(p,name_len); + strncpy(s,first,name_len); + p->llink=NULL; + p->rlink=NULL; + init_node(p); + return par==NULL ? (root=p) : c==less ? (par->llink=p) : (par->rlink=p); +} + +@ @c +void +extend_section_name(p,first,last,ispref) +name_pointer p; /* name to be extended */ +char *first; /* beginning of extension text */ +char *last; /* one beyond end of extension text */ +int ispref; /* are we adding a prefix or a full name? */ +{ + char *s; + name_pointer q=p+1; + int name_len=last-first+ispref; + if (name_ptr>=name_dir_end) overflow("name"); + while (q->link!=name_dir) q=q->link; + q->link=name_ptr; + s=name_ptr->byte_start; + name_ptr->link=name_dir; + if (s+name_len>byte_mem_end) overflow("byte memory"); + (++name_ptr)->byte_start=byte_ptr=s+name_len; + strncpy(s,first,name_len); + if (ispref) *(byte_ptr-1)=' '; +} + +@ The |section_lookup| procedure is supposed to find a +section name that matches a new name, installing the new name if +it doesn't match an existing one. The new name is the string +between |first| and |last|; a ``match'' means that the new name +exactly equals or is a prefix or extension of a name in the tree. + +@c +name_pointer +section_lookup(first,last,ispref) /* find or install section name in tree */ +char *first, *last; /* first and last characters of new name */ +int ispref; /* is the new name a prefix or a full name? */ +{ + int c=0; /* comparison between two names; initialized so some compilers won't complain */ + name_pointer p=root; /* current node of the search tree */ + name_pointer q=NULL; /* another place to look in the tree */ + name_pointer r=NULL; /* where a match has been found */ + name_pointer par=NULL; /* parent of |p|, if |r| is |NULL|; + otherwise parent of |r| */ + int name_len=last-first+1; + @<Look for matches for new name among shortest prefixes, complaining + if more than one is found@>; + @<If no match found, add new name to tree@>; + @<If one match found, check for compatibility and return match@>; +} + +@ A legal new name matches an existing section name if and only if it +matches the shortest prefix of that section name. Therefore we can +limit our search for matches to shortest prefixes, which eliminates +the need for chunk-chasing at this stage. + +@<Look for matches for new name among...@>= +while (p) { /* compare shortest prefix of |p| with new name */ + c=web_strcmp(first,name_len,first_chunk(p),prefix_length(p)); + if (c==less || c==greater) { /* new name does not match |p| */ + if (r==NULL) /* no previous matches have been found */ + par=p; + p=(c==less?p->llink:p->rlink); + } else { /* new name matches |p| */ + if (r!=NULL) { /* and also |r|: illegal */ + printf("\n! Ambiguous prefix: matches <"); +@.Ambiguous prefix ... @> + print_prefix_name(p); + printf(">\n and <"); + print_prefix_name(r); + err_print(">"); + return name_dir; /* the unsection */ + } + r=p; /* remember match */ + p=p->llink; /* try another */ + q=r->rlink; /* we'll get back here if the new |p| doesn't match */ + } + if (p==NULL) + p=q, q=NULL; /* |q| held the other branch of |r| */ +} + +@ @<If no match ...@>= + if (r==NULL) /* no matches were found */ + return add_section_name(par,c,first,last+1,ispref); + +@ Although error messages are given in anomalous cases, we do return the +unique best match when a discrepancy is found, because users often +change a title in one place while forgetting to change it elsewhere. + +@<If one match found, check for compatibility and return match@>= +switch(section_name_cmp(&first,name_len,r)) { + /* compare all of |r| with new name */ + case prefix: + if (!ispref) { + printf("\n! New name is a prefix of <"); +@.New name is a prefix...@> + print_section_name(r); + err_print(">"); + } + else if (name_len<prefix_length(r)) set_prefix_length(r,name_len); + /* fall through */ + case equal: return r; + case extension: if (!ispref || first<=last) + extend_section_name(r,first,last+1,ispref); + return r; + case bad_extension: + printf("\n! New name extends <"); +@.New name extends...@> + print_section_name(r); + err_print(">"); + return r; + default: /* no match: illegal */ + printf("\n! Section name incompatible with <"); +@.Section name incompatible...@> + print_prefix_name(r); + printf(">,\n which abbreviates <"); + print_section_name(r); + err_print(">"); + return r; +} + +@ The return codes of |section_name_cmp|, which compares a string with +the full name of a section, are those of |web_strcmp| plus +|bad_extension|, used when the string is an extension of a +supposedly already complete section name. This function has a side +effect when the comparison string is an extension: It advances the +address of the first character of the string by an amount equal to +the length of the known part of the section name. + +The name \.{@@<foo...@@>} should be an acceptable ``abbreviation'' +for \.{@@<foo@@>}. If such an abbreviation comes after the complete +name, there's no trouble recognizing it. If it comes before the +complete name, we simply append a null chunk. This logic requires +us to regard \.{@@<foo...@@>} as an ``extension'' of itself. + +@d bad_extension 5 + +@<Predec...@>= +int section_name_cmp(); + +@ @c +int section_name_cmp(pfirst,len,r) +char **pfirst; /* pointer to beginning of comparison string */ +int len; /* length of string */ +name_pointer r; /* section name being compared */ +{ + char *first=*pfirst; /* beginning of comparison string */ + name_pointer q=r+1; /* access to subsequent chunks */ + char *ss, *s=first_chunk(r); + int c; /* comparison */ + int ispref; /* is chunk |r| a prefix? */ + while (1) { + ss=(r+1)->byte_start-1; + if (*ss==' ' && ss>=r->byte_start) ispref=1,q=q->link; + else ispref=0,ss++,q=name_dir; + switch(c=web_strcmp(first,len,s,ss-s)) { + case equal: if (q==name_dir) + if (ispref) { + *pfirst=first+(ss-s); + return extension; /* null extension */ + } else return equal; + else return (q->byte_start==(q+1)->byte_start)? equal: prefix; + case extension: + if (!ispref) return bad_extension; + first += ss-s; + if (q!=name_dir) {len -= ss-s; s=q->byte_start; r=q; continue;} + *pfirst=first; return extension; + default: return c; + } + } +} + +@ The last component of |name_info| is different for \.{CTANGLE} and +\.{CWEAVE}. In \.{CTANGLE}, 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|. In \.{CWEAVE}, on the other hand, if +|p| points to an identifier, |p->xref| is a pointer to its +list of cross-references, an element of the array |xmem|. The make-up +of |text_info| and |xmem| is discussed in the \.{CTANGLE} and \.{CWEAVE} +source files, respectively; here we just declare a common field +|equiv_or_xref| as a pointer to a |char|. + +@<More elements of |name...@>= +char *equiv_or_xref; /* info corresponding to names */ + +@** Reporting errors to the user. +A global variable called |history| will contain one of four values +at the end of every run: |spotless| means that no unusual messages were +printed; |harmless_message| means that a message of possible interest +was printed but no serious errors were detected; |error_message| means that +at least one error was found; |fatal_message| means that the program +terminated abnormally. The value of |history| does not influence the +behavior of the program; it is simply computed for the convenience +of systems that might want to use such information. + +@d spotless 0 /* |history| value for normal jobs */ +@d harmless_message 1 /* |history| value when non-serious info was printed */ +@d error_message 2 /* |history| value when an error was noted */ +@d fatal_message 3 /* |history| value when we had to stop prematurely */ +@d mark_harmless {if (history==spotless) history=harmless_message;} +@d mark_error history=error_message + +@<Definit...@>= +int history=spotless; /* indicates how bad this run was */ + +@ The command `|err_print("! Error message")|' will report a syntax error to +the user, by printing the error message at the beginning of a new line and +then giving an indication of where the error was spotted in the source file. +Note that no period follows the error message, since the error routine +will automatically supply a period. A newline is automatically supplied +if the string begins with |"!"|. + +@<Predecl...@>= +void err_print(); + +@ @c +void +err_print(s) /* prints `\..' and location of error message */ +char *s; +{ + char *k,*l; /* pointers into |buffer| */ + printf(*s=='!'? "\n%s" : "%s",s); + if(web_file_open) @<Print error location based on input buffer@>; + update_terminal; mark_error; +} + +@ The error locations can be indicated by using the global variables +|loc|, |cur_line|, |cur_file_name| and |changing|, +which tell respectively the first +unlooked-at position in |buffer|, the current line number, the current +file, and whether the current line is from |change_file| or |cur_file|. +This routine should be modified on systems whose standard text editor +has special line-numbering conventions. +@^system dependencies@> + +@<Print error location based on input buffer@>= +{if (changing && include_depth==change_depth) + printf(". (l. %d of change file)\n", change_line); +else if (include_depth==0) printf(". (l. %d)\n", cur_line); + else printf(". (l. %d of include file %s)\n", cur_line, cur_file_name); +l= (loc>=limit? limit: loc); +if (l>buffer) { + for (k=buffer; k<l; k++) + if (*k=='\t') putchar(' '); + else putchar(*k); /* print the characters already read */ + putchar('\n'); + for (k=buffer; k<l; k++) putchar(' '); /* space out the next line */ +} +for (k=l; k<limit; k++) putchar(*k); /* print the part not yet read */ +if (*limit=='|') putchar('|'); /* end of \CEE/ text in section names */ +putchar(' '); /* to separate the message from future asterisks */ +} + +@ When no recovery from some error has been provided, we have to wrap +up and quit as graciously as possible. This is done by calling the +function |wrap_up| at the end of the code. + +\.{CTANGLE} and \.{CWEAVE} have their own notions about how to +print the job statistics. + +@<Prede...@>= +int wrap_up(); +extern void print_stats(); + +@ Some implementations may wish to pass the |history| value to the +operating system so that it can be used to govern whether or not other +programs are started. Here, for instance, we pass the operating system +a status of 0 if and only if only harmless messages were printed. +@^system dependencies@> + +@c +int wrap_up() { + putchar('\n'); + if (show_stats) + print_stats(); /* print statistics about memory usage */ + @<Print the job |history|@>; + if (history > harmless_message) return(1); + else return(0); +} + +@ @<Print the job |history|@>= +switch (history) { +case spotless: if (show_happiness) printf("(No errors were found.)\n"); break; +case harmless_message: + printf("(Did you see the warning message above?)\n"); break; +case error_message: + printf("(Pardon me, but I think I spotted something wrong.)\n"); break; +case fatal_message: printf("(That was a fatal error, my friend.)\n"); +} /* there are no other cases */ + +@ When there is no way to recover from an error, the |fatal| subroutine is +invoked. This happens most often when |overflow| occurs. + +@<Predec...@>= +void fatal(), overflow(); + +@ The two parameters to |fatal| are strings that are essentially +concatenated to print the final error message. + +@c void +fatal(s,t) + char *s,*t; +{ + if (*s) printf(s); + err_print(t); + history=fatal_message; exit(wrap_up()); +} + +@ An overflow stop occurs if \.{CWEB}'s tables aren't large enough. + +@c void +overflow(t) + char *t; +{ + printf("\n! Sorry, %s capacity exceeded",t); fatal("",""); +} +@.Sorry, capacity exceeded@> + +@ Sometimes the program's behavior is far different from what it should be, +and \.{CWEB} prints an error message that is really for the \.{CWEB} +maintenance person, not the user. In such cases the program says +|confusion("indication of where we are")|. + +@d confusion(s) fatal("! This can't happen: ",s) +@.This can't happen@> + +@** Command line arguments. +The user calls \.{CWEAVE} and \.{CTANGLE} with arguments on the command line. +These are either file names or flags to be turned off (beginning with |"-"|) +or flags to be turned on (beginning with |"+"|). +The following globals are for communicating the user's desires to the rest +of the program. The various file name variables contain strings with +the names of those files. Most of the 128 flags are undefined but available +for future extensions. + +@d show_banner flags['b'] /* should the banner line be printed? */ +@d show_progress flags['p'] /* should progress reports be printed? */ +@d show_stats flags['s'] /* should statistics be printed at end of run? */ +@d show_happiness flags['h'] /* should lack of errors be announced? */ + +@<Defin...@>= +int argc; /* copy of |ac| parameter to |main| */ +char **argv; /* copy of |av| parameter to |main| */ +char C_file_name[max_file_name_length]; /* name of |C_file| */ +char tex_file_name[max_file_name_length]; /* name of |tex_file| */ +char idx_file_name[max_file_name_length]; /* name of |idx_file| */ +char scn_file_name[max_file_name_length]; /* name of |scn_file| */ +boolean flags[128]; /* an option for each 7-bit code */ + +@ The |flags| will be initially zero. Some of them are set to~1 before +scanning the arguments; if additional flags are 1 by default they +should be set before calling |common_init|. + +@<Set the default options common to \.{CTANGLE} and \.{CWEAVE}@>= +show_banner=show_happiness=show_progress=1; + +@ We now must look at the command line arguments and set the file names +accordingly. At least one file name must be present: the \.{CWEB} +file. It may have an extension, or it may omit the extension to get |".w"| or +|".web"| added. The \TEX/ output file name is formed by replacing the \.{CWEB} +file name extension by |".tex"|, and the \CEE/ file name by replacing +the extension by |".c"|, after removing the directory name (if any). + +If there is a second file name present among the arguments, it is the +change file, again either with an extension or without one to get |".ch"|. +An omitted change file argument means that |"/dev/null"| should be used, +when no changes are desired. +@^system dependencies@> + +If there's a third file name, it will be the output file. + +@<Pred...@>= +void scan_args(); + +@ @c +void +scan_args() +{ + char *dot_pos; /* position of |'.'| in the argument */ + char *name_pos; /* file name beginning, sans directory */ + register char *s; /* register for scanning strings */ + boolean found_web=0,found_change=0,found_out=0; + /* have these names been seen? */ + boolean flag_change; + + while (--argc > 0) { + if ((**(++argv)=='-'||**argv=='+')&&*(*argv+1)) @<Handle flag argument@>@; + else { + s=name_pos=*argv;@+dot_pos=NULL; + while (*s) { + if (*s=='.') dot_pos=s++; + else if (*s=='/') dot_pos=NULL,name_pos=++s; + else s++; + } + if (!found_web) @<Make + |web_file_name|, |tex_file_name|, and |C_file_name|@>@; + else if (!found_change) @<Make |change_file_name| from |fname|@>@; + else if (!found_out) @<Override |tex_file_name| and |C_file_name|@>@; + else @<Print usage error message and quit@>; + } + } + if (!found_web) @<Print usage error message and quit@>; + if (found_change<=0) strcpy(change_file_name,"/dev/null"); +} + +@ We use all of |*argv| for the |web_file_name| if there is a |'.'| in it, +otherwise we add |".w"|. If this file can't be opened, we prepare an +|alt_web_file_name| by adding |"web"| after the dot. +The other file names come from adding other things +after the dot. We must check that there is enough room in +|web_file_name| and the other arrays for the argument. + +@<Make |web_file_name|...@>= +{ + if (s-*argv > max_file_name_length-5) + @<Complain about argument length@>; + if (dot_pos==NULL) + sprintf(web_file_name,"%s.w",*argv); + else { + strcpy(web_file_name,*argv); + *dot_pos=0; /* string now ends where the dot was */ + } + sprintf(alt_web_file_name,"%s.web",*argv); + sprintf(tex_file_name,"%s.tex",name_pos); /* strip off directory name */ + sprintf(idx_file_name,"%s.idx",name_pos); + sprintf(scn_file_name,"%s.scn",name_pos); + sprintf(C_file_name,"%s.c",name_pos); + found_web=1; +} + +@ @<Make |change_file_name|...@>= +{ + if (strcmp(*argv,"-")==0) found_change=-1; + else { + if (s-*argv > max_file_name_length-4) + @<Complain about argument length@>; + if (dot_pos==NULL) + sprintf(change_file_name,"%s.ch",*argv); + else strcpy(change_file_name,*argv); + found_change=1; + } +} + +@ @<Override...@>= +{ + if (s-*argv > max_file_name_length-5) + @<Complain about argument length@>; + if (dot_pos==NULL) { + sprintf(tex_file_name,"%s.tex",*argv); + sprintf(idx_file_name,"%s.idx",*argv); + sprintf(scn_file_name,"%s.scn",*argv); + sprintf(C_file_name,"%s.c",*argv); + } else { + strcpy(tex_file_name,*argv); + strcpy(C_file_name,*argv); + if (flags['x']) { /* indexes will be generated */ + *dot_pos=0; + sprintf(idx_file_name,"%s.idx",*argv); + sprintf(scn_file_name,"%s.scn",*argv); + } + } + found_out=1; +} + +@ @<Handle flag...@>= +{ + if (**argv=='-') flag_change=0; + else flag_change=1; + for(dot_pos=*argv+1;*dot_pos>'\0';dot_pos++) + flags[*dot_pos]=flag_change; +} + +@ @<Print usage error message and quit@>= +{ +if (program==ctangle) + fatal( +"! Usage: ctangle [options] webfile[.w] [{changefile[.ch]|-} [outfile[.c]]]\n" + ,""); +@.Usage:@> +else fatal( +"! Usage: cweave [options] webfile[.w] [{changefile[.ch]|-} [outfile[.tex]]]\n" + ,""); +} + +@ @<Complain about arg...@>= fatal("! Filename too long\n", *argv); +@.Filename too long@> + +@** Output. Here is the code that opens the output file: +@^system dependencies@> + +@<Defin...@>= +FILE *C_file; /* where output of \.{CTANGLE} goes */ +FILE *tex_file; /* where output of \.{CWEAVE} goes */ +FILE *idx_file; /* where index from \.{CWEAVE} goes */ +FILE *scn_file; /* where list of sections from \.{CWEAVE} goes */ +FILE *active_file; /* currently active file for \.{CWEAVE} output */ + +@ @<Scan arguments and open output files@>= +scan_args(); +if (program==ctangle) { + if ((C_file=fopen(C_file_name,"w"))==NULL) + fatal("! Cannot open output file ", C_file_name); +@.Cannot open output file@> +} +else { + if ((tex_file=fopen(tex_file_name,"w"))==NULL) + fatal("! Cannot open output file ", tex_file_name); +} + +@ The |update_terminal| procedure is called when we want +to make sure that everything we have output to the terminal so far has +actually left the computer's internal buffers and been sent. +@^system dependencies@> + +@d update_terminal fflush(stdout) /* empty the terminal output buffer */ + +@ Terminal output uses |putchar| and |putc| when we have to +translate from \.{CWEB}'s code into the external character code, +and |printf| when we just want to print strings. +Several macros make other kinds of output convenient. +@^system dependencies@> +@d new_line putchar('\n') @d putxchar putchar +@d term_write(a,b) fflush(stdout),fwrite(a,sizeof(char),b,stdout) +@d C_printf(c,a) fprintf(C_file,c,a) +@d C_putc(c) putc(c,C_file) /* isn't \CEE/ wonderfully consistent? */ + +@ We predeclare several standard system functions here instead of including +their system header files, because the names of the header files are not as +standard as the names of the functions. (For example, some \CEE/ environments +have \.{<string.h>} where others have \.{<strings.h>}.) + +@<Predecl...@>= +extern int strlen(); /* length of string */ +extern int strcmp(); /* compare strings lexicographically */ +extern char* strcpy(); /* copy one string to another */ +extern int strncmp(); /* compare up to $n$ string characters */ +extern char* strncpy(); /* copy up to $n$ string characters */ + +@** Index. diff --git a/web/c_cpp/cweb/ctang-bs.ch b/web/c_cpp/cweb/ctang-bs.ch new file mode 100644 index 0000000000..c4062c4064 --- /dev/null +++ b/web/c_cpp/cweb/ctang-bs.ch @@ -0,0 +1,202 @@ +Changes for CTANGLE for MSDOS and Borland C++ 3.1 using the following +options (and perhaps others): + + -mc -w-pro -Ff=5000 -Z- -O-p + +The options -Z- and -O-p explicitly turn off optimizations that break +the code. (See makefile.bs.) + +The main purpose of these changes is to support MSDOS with full-size arrays +by using "huge" pointers. + +(This file contributed by Barry Schwartz, trashman@crud.mn.org, 28 Jun 94; + revised 24 Jul 94.) + + +@x Section 1. +The ``banner line'' defined here should be changed whenever \.{CTANGLE} +is modified. + +@d banner "This is CTANGLE (Version 3.64)\n" +@y +The ``banner line'' defined here should be changed whenever \.{CTANGLE} +is modified. + +@d banner "This is CTANGLE (Version 3.64pc/big)\n" +@z + + +@x Section 4. +@ The following parameters were sufficient in the original \.{TANGLE} to +handle \TEX/, +so they should be sufficient for most applications of \.{CTANGLE}. +If you change |max_bytes|, |max_names| or |hash_size| you should also +change them in the file |"common.w"|. + +@d max_bytes 90000 /* the number of bytes in identifiers, + index entries, and section names; used in |"common.w"| */ +@d max_toks 270000 /* number of bytes in compressed \CEE/ code */ +@d max_names 4000 /* number of identifiers, strings, section names; + must be less than 10240; used in |"common.w"| */ +@d max_texts 2500 /* number of replacement texts, must be less than 10240 */ +@d hash_size 353 /* should be prime; used in |"common.w"| */ +@d longest_name 10000 /* section names shouldn't be longer than this */ +@d stack_size 50 /* number of simultaneous levels of macro expansion */ +@d buf_size 100 /* for \.{CWEAVE} and \.{CTANGLE} */ +@y +@ The following parameters were sufficient in the original \.{TANGLE} to +handle \TEX/, +so they should be sufficient for most applications of \.{CTANGLE}. +If you change |max_bytes|, |max_names| or |hash_size| you should also +change them in the file |"common.w"|. + +(This is a modified version of \.{CTANGLE}, and in fact one of the parameters +has been reduced in value. The parameter |max_toks| +has been reduced from 270000 [which +was sufficient to handle \TEX/] to +170000, so that \.{CTANGLE} +may be run on {\mc MSDOS} +systems that are tight on memory. Consider, for +instance, an 80286-based machine with several TSRs and drivers, trying +to run \.{CTANGLE} from a makefile.) + +@d max_bytes 90000 /* the number of bytes in identifiers, + index entries, and section names; used in |"common.w"| */ +@d max_toks 170000 /* number of bytes in compressed \CEE/ code */ +@d max_names 4000 /* number of identifiers, strings, section names; + must be less than 10240; used in |"common.w"| */ +@d max_texts 2500 /* number of replacement texts, must be less than 10240 */ +@d hash_size 353 /* should be prime; used in |"common.w"| */ +@d longest_name 10000 /* section names shouldn't be longer than this */ +@d stack_size 50 /* number of simultaneous levels of macro expansion */ +@d buf_size 100 /* for \.{CWEAVE} and \.{CTANGLE} */ +@z + + +@x Section 9. +@d chunk_marker 0 + +@<Common code...@>= +typedef struct name_info { + char *byte_start; /* beginning of the name in |byte_mem| */ + struct name_info *link; + union { + struct name_info *Rlink; /* right link in binary search tree for section + names */ + char Ilk; /* used by identifiers in \.{CWEAVE} only */ + } dummy; + char *equiv_or_xref; /* info corresponding to names */ +} name_info; /* contains information about an identifier or section name */ +typedef name_info *name_pointer; /* pointer into array of \&{name\_info}s */ +typedef name_pointer *hash_pointer; +extern char byte_mem[]; /* characters of names */ +extern char *byte_mem_end; /* end of |byte_mem| */ +extern name_info name_dir[]; /* information about names */ +extern name_pointer name_dir_end; /* end of |name_dir| */ +extern name_pointer name_ptr; /* first unused position in |name_dir| */ +extern char *byte_ptr; /* first unused position in |byte_mem| */ +extern name_pointer hash[]; /* heads of hash lists */ +extern hash_pointer hash_end; /* end of |hash| */ +extern hash_pointer h; /* index into hash-head array */ +extern name_pointer id_lookup(); /* looks up a string in the identifier table */ +extern name_pointer section_lookup(); /* finds section name */ +extern void print_section_name(), sprint_section_name(); +@y +@d chunk_marker 0 + +@f huge extern + +@<Common code...@>= +typedef struct name_info { + char huge* byte_start; /* beginning of the name in |byte_mem| */ + struct name_info *link; + union { + struct name_info *Rlink; /* right link in binary search tree for section + names */ + char Ilk; /* used by identifiers in \.{CWEAVE} only */ + } dummy; + union { + char *equiv_member; + char huge* xref_member; + } ptr_union; /* info corresponding to names */ +} name_info; /* contains information about an identifier or section name */ +typedef name_info *name_pointer; /* pointer into array of \&{name\_info}s */ +typedef name_pointer *hash_pointer; +extern char huge byte_mem[]; /* characters of names */ +extern char huge* byte_mem_end; /* end of |byte_mem| */ +extern name_info name_dir[]; /* information about names */ +extern name_pointer name_dir_end; /* end of |name_dir| */ +extern name_pointer name_ptr; /* first unused position in |name_dir| */ +extern char huge* byte_ptr; /* first unused position in |byte_mem| */ +extern name_pointer hash[]; /* heads of hash lists */ +extern hash_pointer hash_end; /* end of |hash| */ +extern hash_pointer h; /* index into hash-head array */ +extern name_pointer id_lookup(); /* looks up a string in the identifier table */ +extern name_pointer section_lookup(); /* finds section name */ +extern void print_section_name(), sprint_section_name(); +@z + + +@x Section 16. + eight_bits *tok_start; /* pointer into |tok_mem| */ + sixteen_bits text_link; /* relates replacement texts */ +} text; +typedef text *text_pointer; +@y + eight_bits huge* tok_start; /* pointer into |tok_mem| */ + sixteen_bits text_link; /* relates replacement texts */ +} text; +typedef text *text_pointer; +@z + + +@x Section 17. +eight_bits tok_mem[max_toks]; +eight_bits *tok_mem_end=tok_mem+max_toks-1; +eight_bits *tok_ptr; /* first unused position in |tok_mem| */ +@y +eight_bits huge tok_mem[max_toks]; +eight_bits huge* tok_mem_end; +eight_bits huge* tok_ptr; /* first unused position in |tok_mem| */ +@z + + +@x Section 18. +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 */ +@y +tok_mem_end=tok_mem+max_toks-1; +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 */ +@z + + +@x Section 19. +@d equiv equiv_or_xref /* info corresponding to names */ +@y +@d equiv ptr_union.equiv_member /* info corresponding to names */ +@z + + +@x Section 27. + eight_bits *end_field; /* ending location of replacement text */ + eight_bits *byte_field; /* present location within replacement text */ +@y + eight_bits huge* end_field; /* ending location of replacement text */ + eight_bits huge* byte_field; /* present location within replacement text */ +@z + + +@x Section 49. +out_char(cur_char) +eight_bits cur_char; +{ + char *j, *k; /* pointer into |byte_mem| */ +@y +out_char(cur_char) +eight_bits cur_char; +{ + char huge* j, huge* k; /* pointer into |byte_mem| */ +@z diff --git a/web/c_cpp/cweb/ctang-man.ch b/web/c_cpp/cweb/ctang-man.ch new file mode 100644 index 0000000000..12e9d3248e --- /dev/null +++ b/web/c_cpp/cweb/ctang-man.ch @@ -0,0 +1,22 @@ +Change file to help make Appendix E of the full CWEB manual +@x +\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. +} +@y +\def\botofcontents{\vfill\titlefalse} +\def\contentspagenumber{63} +\def\title{APPENDIX E: CTANGLE} +\let\K=\leftarrow +@z diff --git a/web/c_cpp/cweb/ctang-pc.ch b/web/c_cpp/cweb/ctang-pc.ch new file mode 100644 index 0000000000..67f32fae0c --- /dev/null +++ b/web/c_cpp/cweb/ctang-pc.ch @@ -0,0 +1,24 @@ +This is the change file for CWEB's CTANGLE under DOS +(Contributed by Lee Wittenberg, March 1993) + +Changes necessary for compiling with Borland C/C++ +Use compilation switches -mc -w-pro -Ff=5000 + +Note: The changes to section 4 are not necessary if using a compiler +that allows >64K arrays. If you need lots of bytes and toks, try the +alternate change files with -bs suffix instead of -pc. + +@x section 1 +@d banner "This is CTANGLE (Version 3.64)\n" +@y +@d banner "This is CTANGLE (Version 3.64pc)\n" +@z +@x section 4 +@d max_bytes 90000 /* the number of bytes in identifiers, + index entries, and section names; used in |"common.w"| */ +@d max_toks 270000 /* number of bytes in compressed \CEE/ code */ +@y (note that CWEAVE itself needs only about 42K toks) +@d max_bytes (unsigned)60000 /* the number of bytes in identifiers, + index entries, and section names; used in |"common.w"| */ +@d max_toks (unsigned)60000 /* number of bytes in compressed \CEE/ code */ +@z diff --git a/web/c_cpp/cweb/ctang-ql.ch b/web/c_cpp/cweb/ctang-ql.ch new file mode 100644 index 0000000000..a0920243a9 --- /dev/null +++ b/web/c_cpp/cweb/ctang-ql.ch @@ -0,0 +1,25 @@ +This is the change file for CWEB's CTANGLE under QDOS/SMSQ +(Contributed by Robert H. Klein, September 1994) + +This change file is intended for use with C68 v4.13 (or later). +compile with +ex <dev_>cc;"-v -h -c -=500000 ctangle_c" + + +@x +\def\title{CTANGLE (Version 3.64)} +@y +\def\title{CTANGLE (QL Version 3.64)} +@z + +@x section 1 +@d banner "This is CTANGLE (Version 3.64)\n" +@y +@d banner "This is CTANGLE (QL Version 3.64)\n" +@z + +@x +@i common.h +@y +@i common_h +@z diff --git a/web/c_cpp/cweb/ctang-vms.ch b/web/c_cpp/cweb/ctang-vms.ch new file mode 100644 index 0000000000..7e538abc27 --- /dev/null +++ b/web/c_cpp/cweb/ctang-vms.ch @@ -0,0 +1,35 @@ +This is the change file for CWEB's CTANGLE for VAX/VMS. + +created: + 01-FEB-1992 ST (Stephan Trebels <trebels@ams02.dnet.gwdg.de>) + > include ctype,stdio from textlibrary SYS$SHARE:VAXCDEF.TLB + > change banner line to include (VAX/VMS) + ? will someone make a CLD interface? (should be easy) + +(also modified by Don Knuth to keep version numbers uptodate) +(these changes not necessary for initial bootstrapping) + +@x section 1 (01-FEB-1992 ST) +@d banner "This is CTANGLE (Version 3.64)\n" +@y +@d banner "This is CTANGLE (VAX/VMS Version 3.64)\n" +@z + +@x section 6 (from common.h) (01-FEB-1992 ST) +#include <stdio.h> +@y +#include stdio /* VMS searches Textlibraries faster */ +@z + +@x section 62 (01-FEB-1992 ST) +#include <ctype.h> /* definition of |isalpha|, |isdigit| and so on */ +@y +#include ctype /* definition of |isalpha|, |isdigit| and so on */ + /* VMS searches text libraries faster */ +@z + +@x section 63 (01-FEB-1992 ST) +@d isxalpha(c) ((c)=='_') /* non-alpha character allowed in identifier */ +@y +@d isxalpha(c) ((c)=='_' || (c)=='$') /* non-alpha characters allowed in id */ +@z diff --git a/web/c_cpp/cweb/ctang-w32.ch b/web/c_cpp/cweb/ctang-w32.ch new file mode 100644 index 0000000000..523985ce3d --- /dev/null +++ b/web/c_cpp/cweb/ctang-w32.ch @@ -0,0 +1,66 @@ +This is the change file for CWEB's CTANGLE under Win32 +(Contributed by Fabrice Popineau, February 2002) + +Changes necessary for compiling with Borland C/C++ + +@x section 1 +@d banner "This is CTANGLE (Version 3.64)\n" +@y +@d banner "This is CTANGLE (Version 3.64win32)\n" +@z + +@x +@ We predeclare several standard system functions here instead of including +their system header files, because the names of the header files are not as +standard as the names of the functions. (For example, some \CEE/ environments +have \.{<string.h>} where others have \.{<strings.h>}.) + +@<Predecl...@>= +extern int strlen(); /* length of string */ +extern int strcmp(); /* compare strings lexicographically */ +extern char* strcpy(); /* copy one string to another */ +extern int strncmp(); /* compare up to $n$ string characters */ +extern char* strncpy(); /* copy up to $n$ string characters */ +@y +@z + +@x +int names_match(p,first,l) +name_pointer p; /* points to the proposed match */ +char *first; /* position of first character of string */ +@y +int __cdecl names_match(p,first,l) +name_pointer p; /* points to the proposed match */ +char *first; /* position of first character of string */ +@z + +@x +void +init_node(node) +name_pointer node; +{ + node->equiv=(char *)text_info; +} +void +init_p() {} +@y +void +init_node(node) +name_pointer node; +{ + node->equiv=(char *)text_info; +} +void __cdecl +init_p() {} +@z + +@x +@ @<Include...@>= +#include <ctype.h> /* definition of |isalpha|, |isdigit| and so on */ +#include <stdlib.h> /* definition of |exit| */ +@y +@ @<Include...@>= +#include <ctype.h> /* definition of |isalpha|, |isdigit| and so on */ +#include <stdlib.h> /* definition of |exit| */ +#include <string.h> +@z diff --git a/web/c_cpp/cweb/ctangle.c b/web/c_cpp/cweb/ctangle.c new file mode 100644 index 0000000000..b7a1b553ce --- /dev/null +++ b/web/c_cpp/cweb/ctangle.c @@ -0,0 +1,1579 @@ +/*1:*/ +#line 64 "ctangle.w" + +/*6:*/ +#line 35 "common.h" + +#include <stdio.h> + +/*:6*//*62:*/ +#line 888 "ctangle.w" + +#include <ctype.h> +#include <stdlib.h> + +/*:62*/ +#line 65 "ctangle.w" + +#define banner "This is CTANGLE (Version 3.64)\n" \ + +#define max_bytes 90000 \ + +#define max_toks 270000 +#define max_names 4000 \ + +#define max_texts 2500 +#define hash_size 353 +#define longest_name 10000 +#define stack_size 50 +#define buf_size 100 \ + +#define ctangle 0 +#define cweave 1 \ + +#define and_and 04 +#define lt_lt 020 +#define gt_gt 021 +#define plus_plus 013 +#define minus_minus 01 +#define minus_gt 031 +#define not_eq 032 +#define lt_eq 034 +#define gt_eq 035 +#define eq_eq 036 +#define or_or 037 +#define dot_dot_dot 016 +#define colon_colon 06 +#define period_ast 026 +#define minus_gt_ast 027 \ + +#define xisalpha(c) (isalpha(c) &&((eight_bits) c<0200) ) +#define xisdigit(c) (isdigit(c) &&((eight_bits) c<0200) ) +#define xisspace(c) (isspace(c) &&((eight_bits) c<0200) ) +#define xislower(c) (islower(c) &&((eight_bits) c<0200) ) +#define xisupper(c) (isupper(c) &&((eight_bits) c<0200) ) +#define xisxdigit(c) (isxdigit(c) &&((eight_bits) c<0200) ) \ + +#define length(c) (c+1) ->byte_start-(c) ->byte_start +#define print_id(c) term_write((c) ->byte_start,length((c) ) ) +#define llink link +#define rlink dummy.Rlink +#define root name_dir->rlink \ + +#define chunk_marker 0 \ + +#define spotless 0 +#define harmless_message 1 +#define error_message 2 +#define fatal_message 3 +#define mark_harmless {if(history==spotless) history= harmless_message;} +#define mark_error history= error_message +#define confusion(s) fatal("! This can't happen: ",s) \ + +#define max_file_name_length 60 +#define cur_file file[include_depth] +#define cur_file_name file_name[include_depth] +#define web_file_name file_name[0] +#define cur_line line[include_depth] \ + +#define show_banner flags['b'] +#define show_progress flags['p'] +#define show_happiness flags['h'] \ + +#define update_terminal fflush(stdout) +#define new_line putchar('\n') +#define putxchar putchar +#define term_write(a,b) fflush(stdout) ,fwrite(a,sizeof(char) ,b,stdout) +#define C_printf(c,a) fprintf(C_file,c,a) +#define C_putc(c) putc(c,C_file) \ + +#define equiv equiv_or_xref \ + +#define section_flag max_texts \ + +#define string 02 +#define join 0177 +#define output_defs_flag (2*024000-1) \ + +#define cur_end cur_state.end_field +#define cur_byte cur_state.byte_field +#define cur_name cur_state.name_field +#define cur_repl cur_state.repl_field +#define cur_section cur_state.section_field \ + +#define section_number 0201 +#define identifier 0202 \ + +#define normal 0 +#define num_or_id 1 +#define post_slash 2 +#define unbreakable 3 +#define verbatim 4 \ + +#define max_files 256 +#define translit_length 10 \ + +#define ignore 0 +#define ord 0302 +#define control_text 0303 +#define translit_code 0304 +#define output_defs_code 0305 +#define format_code 0306 +#define definition 0307 +#define begin_C 0310 +#define section_name 0311 +#define new_section 0312 \ + +#define constant 03 \ + +#define isxalpha(c) ((c) =='_'||(c) =='$') \ + +#define ishigh(c) ((unsigned char) (c) > 0177) \ + \ + +#define compress(c) if(loc++<=limit) return(c) \ + +#define macro 0 +#define app_repl(c) {if(tok_ptr==tok_mem_end) overflow("token") ;*tok_ptr++= c;} \ + + +#line 66 "ctangle.w" + +/*5:*/ +#line 29 "common.h" + +typedef short boolean; +typedef char unsigned eight_bits; +extern boolean program; +extern int phase; + +/*:5*//*7:*/ +#line 57 "common.h" + +char section_text[longest_name+1]; +char*section_text_end= section_text+longest_name; +char*id_first; +char*id_loc; + +/*:7*//*8:*/ +#line 72 "common.h" + +extern char buffer[]; +extern char*buffer_end; +extern char*loc; +extern char*limit; + +/*:8*//*9:*/ +#line 87 "common.h" + +typedef struct name_info{ +char*byte_start; +struct name_info*link; +union{ +struct name_info*Rlink; + +char Ilk; +}dummy; +char*equiv_or_xref; +}name_info; +typedef name_info*name_pointer; +typedef name_pointer*hash_pointer; +extern char byte_mem[]; +extern char*byte_mem_end; +extern name_info name_dir[]; +extern name_pointer name_dir_end; +extern name_pointer name_ptr; +extern char*byte_ptr; +extern name_pointer hash[]; +extern hash_pointer hash_end; +extern hash_pointer h; +extern name_pointer id_lookup(); +extern name_pointer section_lookup(); +extern void print_section_name(),sprint_section_name(); + +/*:9*//*10:*/ +#line 122 "common.h" + +extern history; +extern err_print(); +extern wrap_up(); +extern void fatal(); +extern void overflow(); + +/*:10*//*11:*/ +#line 137 "common.h" + +extern include_depth; +extern FILE*file[]; +extern FILE*change_file; +extern char C_file_name[]; +extern char tex_file_name[]; +extern char idx_file_name[]; +extern char scn_file_name[]; +extern char file_name[][max_file_name_length]; + +extern char change_file_name[]; +extern line[]; +extern change_line; +extern change_depth; +extern boolean input_has_ended; +extern boolean changing; +extern boolean web_file_open; +extern reset_input(); +extern get_line(); +extern check_complete(); + +/*:11*//*12:*/ +#line 159 "common.h" + +typedef unsigned short sixteen_bits; +extern sixteen_bits section_count; +extern boolean changed_section[]; +extern boolean change_pending; +extern boolean print_where; + +/*:12*//*13:*/ +#line 171 "common.h" + +extern int argc; +extern char**argv; +extern boolean flags[]; + +/*:13*//*14:*/ +#line 183 "common.h" + +extern FILE*C_file; +extern FILE*tex_file; +extern FILE*idx_file; +extern FILE*scn_file; +extern FILE*active_file; + +/*:14*//*15:*/ +#line 192 "common.h" + +extern void common_init(); +#line 128 "ctangle.w" + +/*:15*/ +#line 67 "ctangle.w" + +/*16:*/ +#line 152 "ctangle.w" + +typedef struct{ +eight_bits*tok_start; +sixteen_bits text_link; +}text; +typedef text*text_pointer; + +/*:16*//*27:*/ +#line 296 "ctangle.w" + +typedef struct{ +eight_bits*end_field; +eight_bits*byte_field; +name_pointer name_field; +text_pointer repl_field; +sixteen_bits section_field; +}output_state; +typedef output_state*stack_pointer; + +/*:27*/ +#line 68 "ctangle.w" + +/*17:*/ +#line 159 "ctangle.w" + +text text_info[max_texts]; +text_pointer text_info_end= text_info+max_texts-1; +text_pointer text_ptr; +eight_bits tok_mem[max_toks]; +eight_bits*tok_mem_end= tok_mem+max_toks-1; +eight_bits*tok_ptr; + +/*:17*//*23:*/ +#line 227 "ctangle.w" + +text_pointer last_unnamed; + +/*:23*//*28:*/ +#line 312 "ctangle.w" + +output_state cur_state; + +output_state stack[stack_size+1]; +stack_pointer stack_ptr; +stack_pointer stack_end= stack+stack_size; + +/*:28*//*32:*/ +#line 384 "ctangle.w" + +int cur_val; + +/*:32*//*36:*/ +#line 473 "ctangle.w" + +eight_bits out_state; +boolean protect; + +/*:36*//*38:*/ +#line 502 "ctangle.w" + +name_pointer output_files[max_files]; +name_pointer*cur_out_file,*end_output_files,*an_output_file; +char cur_section_name_char; +char output_file_name[longest_name]; + +/*:38*//*45:*/ +#line 599 "ctangle.w" + +boolean output_defs_seen= 0; + +/*:45*//*51:*/ +#line 710 "ctangle.w" + +char translit[128][translit_length]; + +/*:51*//*56:*/ +#line 789 "ctangle.w" + +eight_bits ccode[256]; + +/*:56*//*59:*/ +#line 845 "ctangle.w" + +boolean comment_continues= 0; + +/*:59*//*61:*/ +#line 884 "ctangle.w" + +name_pointer cur_section_name; +int no_where; + +/*:61*//*75:*/ +#line 1195 "ctangle.w" + +text_pointer cur_text; +eight_bits next_control; + +/*:75*//*82:*/ +#line 1353 "ctangle.w" + +extern sixteen_bits section_count; + +/*:82*/ +#line 69 "ctangle.w" + +/*2:*/ +#line 77 "ctangle.w" + +extern int strlen(); +extern int strcmp(); +extern char*strcpy(); +extern int strncmp(); +extern char*strncpy(); + +/*:2*//*41:*/ +#line 533 "ctangle.w" + +void phase_two(); + +/*:41*//*46:*/ +#line 602 "ctangle.w" + +void output_defs(); + +/*:46*//*48:*/ +#line 648 "ctangle.w" + +static void out_char(); + +/*:48*//*90:*/ +#line 1460 "ctangle.w" + +void phase_one(); + +/*:90*//*92:*/ +#line 1478 "ctangle.w" + +void skip_limbo(); + +/*:92*/ +#line 70 "ctangle.w" + + +/*:1*//*3:*/ +#line 91 "ctangle.w" + +int main(ac,av) +int ac; +char**av; +{ +argc= ac;argv= av; +program= ctangle; +/*18:*/ +#line 167 "ctangle.w" + +text_info->tok_start= tok_ptr= tok_mem; +text_ptr= text_info+1;text_ptr->tok_start= tok_mem; + + +/*:18*//*20:*/ +#line 177 "ctangle.w" + +name_dir->equiv= (char*)text_info; + +/*:20*//*24:*/ +#line 230 "ctangle.w" +last_unnamed= text_info;text_info->text_link= 0; + +/*:24*//*39:*/ +#line 512 "ctangle.w" + +cur_out_file= end_output_files= output_files+max_files; + +/*:39*//*52:*/ +#line 713 "ctangle.w" + +{ +int i; +for(i= 0;i<128;i++)sprintf(translit[i],"X%02X",(unsigned)(128+i)); +} + +/*:52*//*57:*/ +#line 792 "ctangle.w" +{ +int c; +for(c= 0;c<256;c++)ccode[c]= ignore; +ccode[' ']= ccode['\t']= ccode['\n']= ccode['\v']= ccode['\r']= ccode['\f'] += ccode['*']= new_section; +ccode['@']= '@';ccode['=']= string; +ccode['d']= ccode['D']= definition; +ccode['f']= ccode['F']= ccode['s']= ccode['S']= format_code; +ccode['c']= ccode['C']= ccode['p']= ccode['P']= begin_C; +ccode['^']= ccode[':']= ccode['.']= ccode['t']= ccode['T']= +ccode['q']= ccode['Q']= control_text; +ccode['h']= ccode['H']= output_defs_code; +ccode['l']= ccode['L']= translit_code; +ccode['&']= join; +ccode['<']= ccode['(']= section_name; +ccode['\'']= ord; +} + +/*:57*//*71:*/ +#line 1116 "ctangle.w" +section_text[0]= ' '; + +/*:71*/ +#line 98 "ctangle.w" +; +common_init(); +if(show_banner)printf(banner); +phase_one(); +phase_two(); +return wrap_up(); +} + +/*:3*//*21:*/ +#line 183 "ctangle.w" + +int names_match(p,first,l) +name_pointer p; +char*first; +int l; +{ +if(length(p)!=l)return 0; +return!strncmp(first,p->byte_start,l); +} + +/*:21*//*22:*/ +#line 198 "ctangle.w" + +void +init_node(node) +name_pointer node; +{ +node->equiv= (char*)text_info; +} +void +init_p(){} + +/*:22*//*26:*/ +#line 260 "ctangle.w" + +void +store_two_bytes(x) +sixteen_bits x; +{ +if(tok_ptr+2> tok_mem_end)overflow("token"); +*tok_ptr++= x>>8; +*tok_ptr++= x&0377; +} + +/*:26*//*30:*/ +#line 336 "ctangle.w" + +void +push_level(p) +name_pointer p; +{ +if(stack_ptr==stack_end)overflow("stack"); +*stack_ptr= cur_state; +stack_ptr++; +if(p!=NULL){ +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; +} +} + +/*:30*//*31:*/ +#line 355 "ctangle.w" + +void +pop_level(flag) +int flag; +{ +if(flag&&cur_repl->text_link<section_flag){ +cur_repl= cur_repl->text_link+text_info; +cur_byte= cur_repl->tok_start;cur_end= (cur_repl+1)->tok_start; +return; +} +stack_ptr--; +if(stack_ptr> stack)cur_state= *stack_ptr; +} + +/*:31*//*33:*/ +#line 391 "ctangle.w" + +void +get_output() +{ +sixteen_bits a; +restart:if(stack_ptr==stack)return; +if(cur_byte==cur_end){ +cur_val= -((int)cur_section); +pop_level(1); +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); +else if(a<0200)out_char(a); +else{ +a= (a-0200)*0400+*cur_byte++; +switch(a/024000){ +case 0:cur_val= a;out_char(identifier);break; +case 1:if(a==output_defs_flag)output_defs(); +else/*34:*/ +#line 423 "ctangle.w" + +{ +a-= 024000; +if((a+name_dir)->equiv!=(char*)text_info)push_level(a+name_dir); +else if(a!=0){ +printf("\n! Not present: <"); +print_section_name(a+name_dir);err_print(">"); + +} +goto restart; +} + +/*:34*/ +#line 412 "ctangle.w" +; +break; +default:cur_val= a-050000;if(cur_val> 0)cur_section= cur_val; +out_char(section_number); +} +} +} + +/*:33*//*37:*/ +#line 481 "ctangle.w" + +void +flush_buffer() +{ +C_putc('\n'); +if(cur_line%100==0&&show_progress){ +printf("."); +if(cur_line%500==0)printf("%d",cur_line); +update_terminal; +} +cur_line++; +} + +/*:37*//*42:*/ +#line 536 "ctangle.w" + +void +phase_two(){ +web_file_open= 0; +cur_line= 1; +/*29:*/ +#line 325 "ctangle.w" + +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; + +/*:29*/ +#line 541 "ctangle.w" +; +/*44:*/ +#line 595 "ctangle.w" + +if(!output_defs_seen) +output_defs(); + +/*:44*/ +#line 542 "ctangle.w" +; +if(text_info->text_link==0&&cur_out_file==end_output_files){ +printf("\n! No program text was specified.");mark_harmless; + +} +else{ +if(cur_out_file==end_output_files){ +if(show_progress) +printf("\nWriting the output file (%s):",C_file_name); +} +else{ +if(show_progress){ +printf("\nWriting the output files:"); + +printf(" (%s)",C_file_name); +update_terminal; +} +if(text_info->text_link==0)goto writeloop; +} +while(stack_ptr> stack)get_output(); +flush_buffer(); +writeloop:/*43:*/ +#line 572 "ctangle.w" + +for(an_output_file= end_output_files;an_output_file> cur_out_file;){ +an_output_file--; +sprint_section_name(output_file_name,*an_output_file); +fclose(C_file); +C_file= fopen(output_file_name,"w"); +if(C_file==0)fatal("! Cannot open output file:",output_file_name); + +printf("\n(%s)",output_file_name);update_terminal; +cur_line= 1; +stack_ptr= stack+1; +cur_name= (*an_output_file); +cur_repl= (text_pointer)cur_name->equiv; +cur_byte= cur_repl->tok_start; +cur_end= (cur_repl+1)->tok_start; +while(stack_ptr> stack)get_output(); +flush_buffer(); +} + +/*:43*/ +#line 563 "ctangle.w" +; +if(show_happiness)printf("\nDone."); +} +} + +/*:42*//*47:*/ +#line 605 "ctangle.w" + +void +output_defs() +{ +sixteen_bits a; +push_level(NULL); +for(cur_text= text_info+1;cur_text<text_ptr;cur_text++) +if(cur_text->text_link==0){ +cur_byte= cur_text->tok_start; +cur_end= (cur_text+1)->tok_start; +C_printf("%s","#define "); +out_state= normal; +protect= 1; +while(cur_byte<cur_end){ +a= *cur_byte++; +if(cur_byte==cur_end&&a=='\n')break; +if(out_state==verbatim&&a!=string&&a!=constant&&a!='\n') +C_putc(a); + +else if(a<0200)out_char(a); +else{ +a= (a-0200)*0400+*cur_byte++; +if(a<024000){ +cur_val= a;out_char(identifier); +} +else if(a<050000){confusion("macro defs have strange char");} +else{ +cur_val= a-050000;cur_section= cur_val;out_char(section_number); +} + +} +} +protect= 0; +flush_buffer(); +} +pop_level(0); +} + +/*:47*//*49:*/ +#line 651 "ctangle.w" + +static void +out_char(cur_char) +eight_bits cur_char; +{ +char*j,*k; +restart: +switch(cur_char){ +case'\n':if(protect&&out_state!=verbatim)C_putc(' '); +if(protect||out_state==verbatim)C_putc('\\'); +flush_buffer();if(out_state!=verbatim)out_state= normal;break; +/*53:*/ +#line 719 "ctangle.w" + +case identifier: +if(out_state==num_or_id)C_putc(' '); +j= (cur_val+name_dir)->byte_start; +k= (cur_val+name_dir+1)->byte_start; +while(j<k){ +if((unsigned char)(*j)<0200)C_putc(*j); + +else C_printf("%s",translit[(unsigned char)(*j)-0200]); +j++; +} +out_state= num_or_id;break; + +/*:53*/ +#line 662 "ctangle.w" +; +/*54:*/ +#line 732 "ctangle.w" + +case section_number: +if(cur_val> 0)C_printf("/*%d:*/",cur_val); +else if(cur_val<0)C_printf("/*:%d*/",-cur_val); +else if(protect){ +cur_byte+= 4; +cur_char= '\n'; +goto restart; +}else{ +sixteen_bits a; +a= 0400**cur_byte++; +a+= *cur_byte++; +C_printf("\n#line %d \"",a); + +cur_val= *cur_byte++; +cur_val= 0400*(cur_val-0200)+*cur_byte++; +for(j= (cur_val+name_dir)->byte_start,k= (cur_val+name_dir+1)->byte_start; +j<k;j++){ +if(*j=='\\'||*j=='"')C_putc('\\'); +C_putc(*j); +} +C_printf("%s","\"\n"); +} +break; + +/*:54*/ +#line 663 "ctangle.w" +; +/*50:*/ +#line 681 "ctangle.w" + +case plus_plus:C_putc('+');C_putc('+');out_state= normal;break; +case minus_minus:C_putc('-');C_putc('-');out_state= normal;break; +case minus_gt:C_putc('-');C_putc('>');out_state= normal;break; +case gt_gt:C_putc('>');C_putc('>');out_state= normal;break; +case eq_eq:C_putc('=');C_putc('=');out_state= normal;break; +case lt_lt:C_putc('<');C_putc('<');out_state= normal;break; +case gt_eq:C_putc('>');C_putc('=');out_state= normal;break; +case lt_eq:C_putc('<');C_putc('=');out_state= normal;break; +case not_eq:C_putc('!');C_putc('=');out_state= normal;break; +case and_and:C_putc('&');C_putc('&');out_state= normal;break; +case or_or:C_putc('|');C_putc('|');out_state= normal;break; +case dot_dot_dot:C_putc('.');C_putc('.');C_putc('.');out_state= normal; +break; +case colon_colon:C_putc(':');C_putc(':');out_state= normal;break; +case period_ast:C_putc('.');C_putc('*');out_state= normal;break; +case minus_gt_ast:C_putc('-');C_putc('>');C_putc('*');out_state= normal; +break; + +/*:50*/ +#line 664 "ctangle.w" +; +case'=':case'>':C_putc(cur_char);C_putc(' '); +out_state= normal;break; +case join:out_state= unbreakable;break; +case constant:if(out_state==verbatim){ +out_state= num_or_id;break; +} +if(out_state==num_or_id)C_putc(' ');out_state= verbatim;break; +case string:if(out_state==verbatim)out_state= normal; +else out_state= verbatim;break; +case'/':C_putc('/');out_state= post_slash;break; +case'*':if(out_state==post_slash)C_putc(' '); + +default:C_putc(cur_char);out_state= normal;break; +} +} + +/*:49*//*58:*/ +#line 813 "ctangle.w" + +eight_bits +skip_ahead() +{ +eight_bits c; +while(1){ +if(loc> limit&&(get_line()==0))return(new_section); +*(limit+1)= '@'; +while(*loc!='@')loc++; +if(loc<=limit){ +loc++;c= ccode[(eight_bits)*loc];loc++; +if(c!=ignore||*(loc-1)=='>')return(c); +} +} +} + +/*:58*//*60:*/ +#line 848 "ctangle.w" + +int skip_comment(is_long_comment) +boolean is_long_comment; +{ +char c; +while(1){ +if(loc> limit){ +if(is_long_comment){ +if(get_line())return(comment_continues= 1); +else{ +err_print("! Input ended in mid-comment"); + +return(comment_continues= 0); +} +} +else return(comment_continues= 0); +} +c= *(loc++); +if(is_long_comment&&c=='*'&&*loc=='/'){ +loc++;return(comment_continues= 0); +} +if(c=='@'){ +if(ccode[(eight_bits)*loc]==new_section){ +err_print("! Section name ended in mid-comment");loc--; + +return(comment_continues= 0); +} +else loc++; +} +} +} + +/*:60*//*63:*/ +#line 900 "ctangle.w" + +eight_bits +get_next() +{ +static int preprocessing= 0; +eight_bits c; +while(1){ +if(loc> limit){ +if(preprocessing&&*(limit-1)!='\\')preprocessing= 0; +if(get_line()==0)return(new_section); +else if(print_where&&!no_where){ +print_where= 0; +/*77:*/ +#line 1225 "ctangle.w" + +store_two_bytes(0150000); +if(changing&&include_depth==change_depth){ +id_first= change_file_name; +store_two_bytes((sixteen_bits)change_line); +}else{ +id_first= cur_file_name; +store_two_bytes((sixteen_bits)cur_line); +} +id_loc= id_first+strlen(id_first); +{int a= id_lookup(id_first,id_loc,0)-name_dir;app_repl((a/0400)+0200); +app_repl(a%0400);} + +/*:77*/ +#line 912 "ctangle.w" +; +} +else return('\n'); +} +c= *loc; +if(comment_continues||(c=='/'&&(*(loc+1)=='*'||*(loc+1)=='/'))){ +skip_comment(comment_continues||*(loc+1)=='*'); + +if(comment_continues)return('\n'); +else continue; +} +loc++; +if(xisdigit(c)||c=='.')/*66:*/ +#line 978 "ctangle.w" +{ +id_first= loc-1; +if(*id_first=='.'&&!xisdigit(*loc))goto mistake; +if(*id_first=='0'){ +if(*loc=='x'||*loc=='X'){ +loc++;while(xisxdigit(*loc))loc++;goto found; +} +} +while(xisdigit(*loc))loc++; +if(*loc=='.'){ +loc++; +while(xisdigit(*loc))loc++; +} +if(*loc=='e'||*loc=='E'){ +if(*++loc=='+'||*loc=='-')loc++; +while(xisdigit(*loc))loc++; +} +found:while(*loc=='u'||*loc=='U'||*loc=='l'||*loc=='L' +||*loc=='f'||*loc=='F')loc++; +id_loc= loc; +return(constant); +} + +/*:66*/ +#line 924 "ctangle.w" + +else if(c=='\''||c=='"'||(c=='L'&&(*loc=='\''||*loc=='"'))) +/*67:*/ +#line 1006 "ctangle.w" +{ +char delim= c; +id_first= section_text+1; +id_loc= section_text;*++id_loc= delim; +if(delim=='L'){ +delim= *loc++;*++id_loc= delim; +} +while(1){ +if(loc>=limit){ +if(*(limit-1)!='\\'){ +err_print("! String didn't end");loc= limit;break; + +} +if(get_line()==0){ +err_print("! Input ended in middle of string");loc= buffer;break; + +} +else if(++id_loc<=section_text_end)*id_loc= '\n'; + +} +if((c= *loc++)==delim){ +if(++id_loc<=section_text_end)*id_loc= c; +break; +} +if(c=='\\'){ +if(loc>=limit)continue; +if(++id_loc<=section_text_end)*id_loc= '\\'; +c= *loc++; +} +if(++id_loc<=section_text_end)*id_loc= c; +} +if(id_loc>=section_text_end){ +printf("\n! String too long: "); + +term_write(section_text+1,25); +err_print("..."); +} +id_loc++; +return(string); +} + +/*:67*/ +#line 926 "ctangle.w" + +else if(isalpha(c)||isxalpha(c)||ishigh(c)) +/*65:*/ +#line 972 "ctangle.w" +{ +id_first= --loc; +while(isalpha(*++loc)||isdigit(*loc)||isxalpha(*loc)||ishigh(*loc)); +id_loc= loc;return(identifier); +} + +/*:65*/ +#line 928 "ctangle.w" + +else if(c=='@')/*68:*/ +#line 1050 "ctangle.w" +{ +c= ccode[(eight_bits)*loc++]; +switch(c){ +case ignore:continue; +case translit_code:err_print("! Use @l in limbo only");continue; + +case control_text:while((c= skip_ahead())=='@'); + +if(*(loc-1)!='>') +err_print("! Double @ should be used in control text"); + +continue; +case section_name: +cur_section_name_char= *(loc-1); +/*70:*/ +#line 1098 "ctangle.w" +{ +char*k; +/*72:*/ +#line 1118 "ctangle.w" + +k= section_text; +while(1){ +if(loc> limit&&get_line()==0){ +err_print("! Input ended in section name"); + +loc= buffer+1;break; +} +c= *loc; +/*73:*/ +#line 1142 "ctangle.w" + +if(c=='@'){ +c= *(loc+1); +if(c=='>'){ +loc+= 2;break; +} +if(ccode[(eight_bits)c]==new_section){ +err_print("! Section name didn't end");break; + +} +if(ccode[(eight_bits)c]==section_name){ +err_print("! Nesting of section names not allowed");break; + +} +*(++k)= '@';loc++; +} + +/*:73*/ +#line 1127 "ctangle.w" +; +loc++;if(k<section_text_end)k++; +if(xisspace(c)){ +c= ' ';if(*(k-1)==' ')k--; +} +*k= c; +} +if(k>=section_text_end){ +printf("\n! Section name too long: "); + +term_write(section_text+1,25); +printf("...");mark_harmless; +} +if(*k==' '&&k> section_text)k--; + +/*:72*/ +#line 1100 "ctangle.w" +; +if(k-section_text> 3&&strncmp(k-2,"...",3)==0) +cur_section_name= section_lookup(section_text+1,k-3,1); +else cur_section_name= section_lookup(section_text+1,k,0); +if(cur_section_name_char=='(') +/*40:*/ +#line 516 "ctangle.w" + +{ +for(an_output_file= cur_out_file; +an_output_file<end_output_files;an_output_file++) +if(*an_output_file==cur_section_name)break; +if(an_output_file==end_output_files){ +if(cur_out_file> output_files) +*--cur_out_file= cur_section_name; +else{ +overflow("output files"); +} +} +} + +/*:40*/ +#line 1106 "ctangle.w" +; +return(section_name); +} + +/*:70*/ +#line 1064 "ctangle.w" +; +case string:/*74:*/ +#line 1164 "ctangle.w" +{ +id_first= loc++;*(limit+1)= '@';*(limit+2)= '>'; +while(*loc!='@'||*(loc+1)!='>')loc++; +if(loc>=limit)err_print("! Verbatim string didn't end"); + +id_loc= loc;loc+= 2; +return(string); +} + +/*:74*/ +#line 1065 "ctangle.w" +; +case ord:/*69:*/ +#line 1077 "ctangle.w" + +id_first= loc; +if(*loc=='\\'){ +if(*++loc=='\'')loc++; +} +while(*loc!='\''){ +if(*loc=='@'){ +if(*(loc+1)!='@') +err_print("! Double @ should be used in ASCII constant"); + +else loc++; +} +loc++; +if(loc> limit){ +err_print("! String didn't end");loc= limit-1;break; + +} +} +loc++; +return(ord); + +/*:69*/ +#line 1066 "ctangle.w" +; +default:return(c); +} +} + +/*:68*/ +#line 929 "ctangle.w" + +else if(xisspace(c)){ +if(!preprocessing||loc> limit)continue; + +else return(' '); +} +else if(c=='#'&&loc==buffer+1)preprocessing= 1; +mistake:/*64:*/ +#line 950 "ctangle.w" + +switch(c){ +case'+':if(*loc=='+')compress(plus_plus);break; +case'-':if(*loc=='-'){compress(minus_minus);} +else if(*loc=='>')if(*(loc+1)=='*'){loc++;compress(minus_gt_ast);} +else compress(minus_gt);break; +case'.':if(*loc=='*'){compress(period_ast);} +else if(*loc=='.'&&*(loc+1)=='.'){ +loc++;compress(dot_dot_dot); +} +break; +case':':if(*loc==':')compress(colon_colon);break; +case'=':if(*loc=='=')compress(eq_eq);break; +case'>':if(*loc=='='){compress(gt_eq);} +else if(*loc=='>')compress(gt_gt);break; +case'<':if(*loc=='='){compress(lt_eq);} +else if(*loc=='<')compress(lt_lt);break; +case'&':if(*loc=='&')compress(and_and);break; +case'|':if(*loc=='|')compress(or_or);break; +case'!':if(*loc=='=')compress(not_eq);break; +} + +/*:64*/ +#line 936 "ctangle.w" + +return(c); +} +} + +/*:63*//*76:*/ +#line 1199 "ctangle.w" + +void +scan_repl(t) +eight_bits t; +{ +sixteen_bits a; +if(t==section_name){/*77:*/ +#line 1225 "ctangle.w" + +store_two_bytes(0150000); +if(changing&&include_depth==change_depth){ +id_first= change_file_name; +store_two_bytes((sixteen_bits)change_line); +}else{ +id_first= cur_file_name; +store_two_bytes((sixteen_bits)cur_line); +} +id_loc= id_first+strlen(id_first); +{int a= id_lookup(id_first,id_loc,0)-name_dir;app_repl((a/0400)+0200); +app_repl(a%0400);} + +/*:77*/ +#line 1205 "ctangle.w" +;} +while(1)switch(a= get_next()){ +/*78:*/ +#line 1238 "ctangle.w" + +case identifier:a= id_lookup(id_first,id_loc,0)-name_dir; +app_repl((a/0400)+0200); +app_repl(a%0400);break; +case section_name:if(t!=section_name)goto done; +else{ +/*79:*/ +#line 1271 "ctangle.w" +{ +char*try_loc= loc; +while(*try_loc==' '&&try_loc<limit)try_loc++; +if(*try_loc=='+'&&try_loc<limit)try_loc++; +while(*try_loc==' '&&try_loc<limit)try_loc++; +if(*try_loc=='=')err_print("! Missing `@ ' before a named section"); + + + +} + +/*:79*/ +#line 1244 "ctangle.w" +; +a= cur_section_name-name_dir; +app_repl((a/0400)+0250); +app_repl(a%0400); +/*77:*/ +#line 1225 "ctangle.w" + +store_two_bytes(0150000); +if(changing&&include_depth==change_depth){ +id_first= change_file_name; +store_two_bytes((sixteen_bits)change_line); +}else{ +id_first= cur_file_name; +store_two_bytes((sixteen_bits)cur_line); +} +id_loc= id_first+strlen(id_first); +{int a= id_lookup(id_first,id_loc,0)-name_dir;app_repl((a/0400)+0200); +app_repl(a%0400);} + +/*:77*/ +#line 1248 "ctangle.w" +;break; +} +case output_defs_code:if(t!=section_name)err_print("! Misplaced @h"); + +else{ +output_defs_seen= 1; +a= output_defs_flag; +app_repl((a/0400)+0200); +app_repl(a%0400); +/*77:*/ +#line 1225 "ctangle.w" + +store_two_bytes(0150000); +if(changing&&include_depth==change_depth){ +id_first= change_file_name; +store_two_bytes((sixteen_bits)change_line); +}else{ +id_first= cur_file_name; +store_two_bytes((sixteen_bits)cur_line); +} +id_loc= id_first+strlen(id_first); +{int a= id_lookup(id_first,id_loc,0)-name_dir;app_repl((a/0400)+0200); +app_repl(a%0400);} + +/*:77*/ +#line 1257 "ctangle.w" +; +} +break; +case constant:case string: +/*80:*/ +#line 1282 "ctangle.w" + +app_repl(a); +while(id_first<id_loc){ +if(*id_first=='@'){ +if(*(id_first+1)=='@')id_first++; +else err_print("! Double @ should be used in string"); + +} +app_repl(*id_first++); +} +app_repl(a);break; + +/*:80*/ +#line 1261 "ctangle.w" +; +case ord: +/*81:*/ +#line 1298 "ctangle.w" +{ +int c= (eight_bits)*id_first; +if(c=='\\'){ +c= *++id_first; +if(c>='0'&&c<='7'){ +c-= '0'; +if(*(id_first+1)>='0'&&*(id_first+1)<='7'){ +c= 8*c+*(++id_first)-'0'; +if(*(id_first+1)>='0'&&*(id_first+1)<='7'&&c<32) +c= 8*c+*(++id_first)-'0'; +} +} +else switch(c){ +case't':c= '\t';break; +case'n':c= '\n';break; +case'b':c= '\b';break; +case'f':c= '\f';break; +case'v':c= '\v';break; +case'r':c= '\r';break; +case'a':c= '\7';break; +case'?':c= '?';break; +case'x': +if(xisdigit(*(id_first+1)))c= *(++id_first)-'0'; +else if(xisxdigit(*(id_first+1))){ +++id_first; +c= toupper(*id_first)-'A'+10; +} +if(xisdigit(*(id_first+1)))c= 16*c+*(++id_first)-'0'; +else if(xisxdigit(*(id_first+1))){ +++id_first; +c= 16*c+toupper(*id_first)-'A'+10; +} +break; +case'\\':c= '\\';break; +case'\'':c= '\'';break; +case'\"':c= '\"';break; +default:err_print("! Unrecognized escape sequence"); + +} +} + +app_repl(constant); +if(c>=100)app_repl('0'+c/100); +if(c>=10)app_repl('0'+(c/10)%10); +app_repl('0'+c%10); +app_repl(constant); +} +break; + +/*:81*/ +#line 1263 "ctangle.w" +; +case definition:case format_code:case begin_C:if(t!=section_name)goto done; +else{ +err_print("! @d, @f and @c are ignored in C text");continue; + +} +case new_section:goto done; + +/*:78*/ +#line 1210 "ctangle.w" + +case')':app_repl(a); +if(t==macro)app_repl(' '); +break; +default:app_repl(a); +} +done:next_control= (eight_bits)a; +if(text_ptr> text_info_end)overflow("text"); +cur_text= text_ptr;(++text_ptr)->tok_start= tok_ptr; +} + +/*:76*//*83:*/ +#line 1360 "ctangle.w" + +void +scan_section() +{ +name_pointer p; +text_pointer q; +sixteen_bits a; +section_count++;no_where= 1; +if(*(loc-1)=='*'&&show_progress){ +printf("*%d",section_count);update_terminal; +} +next_control= 0; +while(1){ +/*84:*/ +#line 1399 "ctangle.w" + +while(next_control<definition) + +if((next_control= skip_ahead())==section_name){ +loc-= 2;next_control= get_next(); +} + +/*:84*/ +#line 1374 "ctangle.w" +; +if(next_control==definition){ +/*85:*/ +#line 1406 "ctangle.w" +{ +while((next_control= get_next())=='\n'); +if(next_control!=identifier){ +err_print("! Definition flushed, must start with identifier"); + +continue; +} +app_repl(((a= id_lookup(id_first,id_loc,0)-name_dir)/0400)+0200); + +app_repl(a%0400); +if(*loc!='('){ +app_repl(string);app_repl(' ');app_repl(string); +} +scan_repl(macro); +cur_text->text_link= 0; +} + +/*:85*/ +#line 1376 "ctangle.w" + +continue; +} +if(next_control==begin_C){ +p= name_dir;break; +} +if(next_control==section_name){ +p= cur_section_name; +/*86:*/ +#line 1431 "ctangle.w" + +while((next_control= get_next())=='+'); +if(next_control!='='&&next_control!=eq_eq) +continue; + +/*:86*/ +#line 1384 "ctangle.w" +; +break; +} +return; +} +no_where= print_where= 0; +/*87:*/ +#line 1436 "ctangle.w" + +/*88:*/ +#line 1441 "ctangle.w" + +store_two_bytes((sixteen_bits)(0150000+section_count)); + + +/*:88*/ +#line 1437 "ctangle.w" +; +scan_repl(section_name); +/*89:*/ +#line 1445 "ctangle.w" + +if(p==name_dir||p==0){ +(last_unnamed)->text_link= cur_text-text_info;last_unnamed= cur_text; +} +else if(p->equiv==(char*)text_info)p->equiv= (char*)cur_text; + +else{ +q= (text_pointer)p->equiv; +while(q->text_link<section_flag) +q= q->text_link+text_info; +q->text_link= cur_text-text_info; +} +cur_text->text_link= section_flag; + + +/*:89*/ +#line 1439 "ctangle.w" +; + +/*:87*/ +#line 1390 "ctangle.w" +; +} + +/*:83*//*91:*/ +#line 1463 "ctangle.w" + +void +phase_one(){ +phase= 1; +section_count= 0; +reset_input(); +skip_limbo(); +while(!input_has_ended)scan_section(); +check_complete(); +phase= 2; +} + +/*:91*//*93:*/ +#line 1481 "ctangle.w" + +void +skip_limbo() +{ +char c; +while(1){ +if(loc> limit&&get_line()==0)return; +*(limit+1)= '@'; +while(*loc!='@')loc++; +if(loc++<=limit){ +c= *loc++; +if(ccode[(eight_bits)c]==new_section)break; +switch(ccode[(eight_bits)c]){ +case translit_code:/*94:*/ +#line 1510 "ctangle.w" + +while(xisspace(*loc)&&loc<limit)loc++; +loc+= 3; +if(loc> limit||!xisxdigit(*(loc-3))||!xisxdigit(*(loc-2)) +||(*(loc-3)>='0'&&*(loc-3)<='7')||!xisspace(*(loc-1))) +err_print("! Improper hex number following @l"); + +else{ +unsigned i; +char*beg; +sscanf(loc-3,"%x",&i); +while(xisspace(*loc)&&loc<limit)loc++; +beg= loc; +while(loc<limit&&(xisalpha(*loc)||xisdigit(*loc)||*loc=='_'))loc++; +if(loc-beg>=translit_length) +err_print("! Replacement string in @l too long"); + +else{ +strncpy(translit[i-0200],beg,loc-beg); +translit[i-0200][loc-beg]= '\0'; +} +} + +/*:94*/ +#line 1494 "ctangle.w" +;break; +case format_code:case'@':break; +case control_text:if(c=='q'||c=='Q'){ +while((c= skip_ahead())=='@'); +if(*(loc-1)!='>') +err_print("! Double @ should be used in control text"); + +break; +} +default:err_print("! Double @ should be used in limbo"); + +} +} +} +} + +/*:93*//*95:*/ +#line 1536 "ctangle.w" + +void +print_stats(){ +printf("\nMemory usage statistics:\n"); +printf("%ld names (out of %ld)\n", +(long)(name_ptr-name_dir),(long)max_names); +printf("%ld replacement texts (out of %ld)\n", +(long)(text_ptr-text_info),(long)max_texts); +printf("%ld bytes (out of %ld)\n", +(long)(byte_ptr-byte_mem),(long)max_bytes); +printf("%ld tokens (out of %ld)\n", +(long)(tok_ptr-tok_mem),(long)max_toks); +} + +/*:95*/ diff --git a/web/c_cpp/cweb/ctangle.w b/web/c_cpp/cweb/ctangle.w new file mode 100644 index 0000000000..130800210e --- /dev/null +++ b/web/c_cpp/cweb/ctangle.w @@ -0,0 +1,1557 @@ +% 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.64 --- February 2002 +% (same as Version 3.5 except for minor corrections) +% (also quotes backslashes in file names of #line directives) + +% 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. + +% 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 3.64)} +\def\topofcontents{\null\vfill + \centerline{\titlefont The {\ttitlefont CTANGLE} processor} + \vskip 15pt + \centerline{(Version 3.64)} + \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 +@s not_eq normal @q unreserve a C++ keyword @> + +@** 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 3.64)\n" + +@c +@<Include files@>@/ +@h +@<Common code for \.{CWEAVE} and \.{CTANGLE}@>@/ +@<Typedef declarations@>@/ +@<Global variables@>@/ +@<Predeclaration of procedures@>@/ + +@ We predeclare several standard system functions here instead of including +their system header files, because the names of the header files are not as +standard as the names of the functions. (For example, some \CEE/ environments +have \.{<string.h>} where others have \.{<strings.h>}.) + +@<Predecl...@>= +extern int strlen(); /* length of string */ +extern int strcmp(); /* compare strings lexicographically */ +extern char* strcpy(); /* copy one string to another */ +extern int strncmp(); /* compare up to $n$ string characters */ +extern char* strncpy(); /* copy up to $n$ string characters */ + +@ \.{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 (ac, av) +int ac; +char **av; +{ + argc=ac; argv=av; + program=ctangle; + @<Set initial values@>; + common_init(); + if (show_banner) printf(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 following parameters were sufficient in the original \.{TANGLE} to +handle \TEX/, +so they should be sufficient for most applications of \.{CTANGLE}. +If you change |max_bytes|, |max_names|, or |hash_size| you should also +change them in the file |"common.w"|. + +@d max_bytes 90000 /* the number of bytes in identifiers, + index entries, and section names; used in |"common.w"| */ +@d max_toks 270000 /* number of bytes in compressed \CEE/ code */ +@d max_names 4000 /* number of identifiers, strings, section names; + must be less than 10240; used in |"common.w"| */ +@d max_texts 2500 /* number of replacement texts, must be less than 10240 */ +@d hash_size 353 /* should be prime; used in |"common.w"| */ +@d longest_name 10000 /* section names and strings shouldn't be longer than this */ +@d stack_size 50 /* number of simultaneous levels of macro expansion */ +@d buf_size 100 /* for \.{CWEAVE} and \.{CTANGLE} */ + +@ 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 needs to be updated when |"common.w"| changes. + +@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|. + +If your machine does not support |unsigned char| you should change +the definition of \&{eight\_bits} to |unsigned short|. +@^system dependencies@> + +@<Typed...@>= +typedef struct { + eight_bits *tok_start; /* pointer into |tok_mem| */ + sixteen_bits text_link; /* relates replacement texts */ +} text; +typedef text *text_pointer; + +@ @<Glob...@>= +text text_info[max_texts]; +text_pointer text_info_end=text_info+max_texts-1; +text_pointer text_ptr; /* first unused position in |text_info| */ +eight_bits tok_mem[max_toks]; +eight_bits *tok_mem_end=tok_mem+max_toks-1; +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...@>= +name_dir->equiv=(char *)text_info; /* 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 +int names_match(p,first,l) +name_pointer p; /* points to the proposed match */ +char *first; /* position of first character of string */ +int l; /* length of identifier */ +{ + if (length(p)!=l) return 0; + 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(node) +name_pointer node; +{ + node->equiv=(char *)text_info; +} +void +init_p() {} + +@* Tokens. +Replacement texts, which represent \CEE/ code in a compressed format, +appear in |tok_mem| as mentioned above. The codes in +these texts are called `tokens'; some tokens occupy two consecutive +eight-bit byte positions, and the others take just one byte. + +If $p$ points to a replacement text, |p->tok_start| is the |tok_mem| position +of the first eight-bit code of that text. If |p->text_link==0|, +this is the replacement text for a macro, otherwise it is the replacement +text for a section. In the latter case |p->text_link| is either equal to +|section_flag|, which means that there is no further text for this section, or +|p->text_link| points to a continuation of this replacement text; such +links are created when several sections have \CEE/ texts with the same +name, and they also tie together all the \CEE/ texts of unnamed sections. +The replacement text pointer for the first unnamed section appears in +|text_info->text_link|, and the most recent such pointer is |last_unnamed|. + +@d section_flag max_texts /* final |text_link| in section replacement texts */ + +@<Glob...@>= +text_pointer last_unnamed; /* most recent replacement text of unnamed section */ + +@ @<Set init...@>= last_unnamed=text_info; text_info->text_link=0; + +@ If the first byte of a token is less than |0200|, the token occupies a +single byte. Otherwise we make a sixteen-bit token by combining two consecutive +bytes |a| and |b|. If |0200<=a<0250|, then |(a-0200)@t${}\times2^8$@>+b| +points to an identifier; if |0250<=a<0320|, then +|(a-0250)@t${}\times2^8$@>+b| points to a section name +(or, if it has the special value |output_defs_flag|, +to the area where the preprocessor definitions are stored); and if +|0320<=a<0400|, then |(a-0320)@t${}\times2^8$@>+b| is the number of the section +in which the current replacement text appears. + +Codes less than |0200| are 7-bit |char| codes that represent themselves. +Some of the 7-bit codes will not be present, however, so we can +use them for special purposes. The following symbolic names are used: + +\yskip \hang |join| denotes the concatenation of adjacent items with no +space or line breaks allowed between them (the \.{@@\&} operation of \.{CWEB}). + +\hang |string| denotes the beginning or end of a string, verbatim +construction or numerical constant. +@^ASCII code dependencies@> + +@d string 02 /* takes the place of extended ASCII \.{\char2} */ +@d join 0177 /* takes the place of ASCII delete */ +@d output_defs_flag (2*024000-1) + +@ The following procedure is used to enter a two-byte value into +|tok_mem| when a replacement text is being generated. + +@c +void +store_two_bytes(x) +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 */ +} + +@** 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 */ + +@<Global...@>= +output_state cur_state; /* |cur_end|, |cur_byte|, |cur_name|, |cur_repl|, + and |cur_section| */ +output_state stack[stack_size+1]; /* info for non-current levels */ +stack_pointer stack_ptr; /* first unused location in the output state stack */ +stack_pointer stack_end=stack+stack_size; /* end of |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 +void +push_level(p) /* 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; + } +} + +@ 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 +void +pop_level(flag) /* do this when |cur_byte| reaches |cur_end| */ +int flag; /* |flag==0| 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 */ + +@<Global...@>= +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 +void +get_output() /* 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(1); + 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); + } + } +} + +@ 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!=(char *)text_info) push_level(a+name_dir); + else if (a!=0) { + printf("\n! Not present: <"); + 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 positive, 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 */ + +@<Global...@>= +eight_bits out_state; /* current status of partial output */ +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 +void +flush_buffer() /* writes one line to output file */ +{ + C_putc('\n'); + if (cur_line % 100 == 0 && show_progress) { + printf("."); + if (cur_line % 500 == 0) printf("%d",cur_line); + update_terminal; /* progress report */ + } + cur_line++; +} + +@ 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 +@<Glob...@>= +name_pointer output_files[max_files]; +name_pointer *cur_out_file, *end_output_files, *an_output_file; +char cur_section_name_char; /* is it |'<'| or |'('| */ +char output_file_name[longest_name]; /* name of the file */ + +@ We make |end_output_files| point just beyond the end of +|output_files|. The stack pointer +|cur_out_file| starts out there. Every time we see a new file, we +decrement |cur_out_file| and then write it in. +@<Set initial...@>= +cur_out_file=end_output_files=output_files+max_files; + +@ @<If it's not there, add |cur_section_name| to the output file stack, or +complain we're out of room@>= +{ + for (an_output_file=cur_out_file; + an_output_file<end_output_files; an_output_file++) + if (*an_output_file==cur_section_name) break; + if (an_output_file==end_output_files) { + if (cur_out_file>output_files) + *--cur_out_file=cur_section_name; + else { + overflow("output files"); + } + } +} + +@* The big output switch. Here then is the routine that does the +output. + +@<Predecl...@>= +void phase_two(); + +@ @c +void +phase_two () { + web_file_open=0; + cur_line=1; + @<Initialize the output stacks@>; + @<Output macro definitions if appropriate@>; + if (text_info->text_link==0 && cur_out_file==end_output_files) { + printf("\n! No program text was specified."); mark_harmless; +@.No program text...@> + } + else { + if(cur_out_file==end_output_files) { + if(show_progress) + printf("\nWriting the output file (%s):",C_file_name); + } + else { + if (show_progress) { + printf("\nWriting the output files:"); +@.Writing the output...@> + printf(" (%s)",C_file_name); + update_terminal; + } + if (text_info->text_link==0) goto writeloop; + } + while (stack_ptr>stack) get_output(); + flush_buffer(); +writeloop: @<Write all the named output files@>; + if(show_happiness) printf("\nDone."); + } +} + +@ To write the named output files, we proceed as for the unnamed +section. +The only subtlety is that we have to open each one. + +@<Write all the named output files@>= +for (an_output_file=end_output_files; an_output_file>cur_out_file;) { + an_output_file--; + sprint_section_name(output_file_name,*an_output_file); + fclose(C_file); + C_file=fopen(output_file_name,"w"); + if (C_file ==0) fatal("! Cannot open output file:",output_file_name); +@.Cannot open output file@> + printf("\n(%s)",output_file_name); update_terminal; + cur_line=1; + stack_ptr=stack+1; + cur_name= (*an_output_file); + cur_repl= (text_pointer)cur_name->equiv; + cur_byte=cur_repl->tok_start; + cur_end=(cur_repl+1)->tok_start; + while (stack_ptr > stack) get_output(); + flush_buffer(); +} + +@ If a \.{@@h} was not encountered in the input, +we go through the list of replacement texts and copy the ones +that refer to macros, preceded by the \.{\#define} preprocessor command. + +@<Output macro definitions if appropriate@>= + if (!output_defs_seen) + output_defs(); + +@ @<Glob...@>= +boolean output_defs_seen=0; + +@ @<Predecl...@>= +void output_defs(); + +@ @c +void +output_defs() +{ + sixteen_bits a; + push_level(NULL); + for (cur_text=text_info+1; cur_text<text_ptr; cur_text++) + if (cur_text->text_link==0) { /* |cur_text| is the text for a macro */ + cur_byte=cur_text->tok_start; + cur_end=(cur_text+1)->tok_start; + C_printf("%s","#define "); + out_state=normal; + protect=1; /* newlines should be preceded by |'\\'| */ + while (cur_byte<cur_end) { + a=*cur_byte++; + if (cur_byte==cur_end && a=='\n') break; /* disregard a final newline */ + if (out_state==verbatim && a!=string && a!=constant && a!='\n') + C_putc(a); /* a high-bit character can occur in a string */ +@^high-bit character handling@> + else if (a<0200) out_char(a); /* one-byte token */ + else { + a=(a-0200)*0400+*cur_byte++; + if (a<024000) { /* |024000==(0250-0200)*0400| */ + cur_val=a; out_char(identifier); + } + else if (a<050000) { confusion("macro defs have strange char");} + else { + cur_val=a-050000; cur_section=cur_val; out_char(section_number); + } + /* no other cases */ + } + } + protect=0; + flush_buffer(); + } + pop_level(0); +} + +@ A many-way switch is used to send the output. Note that this function +is not called if |out_state==verbatim|, except perhaps with arguments +|'\n'| (protect the newline), |string| (end the string), or |constant| +(end the constant). + +@<Predecl...@>= +static void out_char(); + +@ @c +static void +out_char(cur_char) +eight_bits cur_char; +{ + char *j, *k; /* pointer into |byte_mem| */ +restart: + switch (cur_char) { + case '\n': if (protect && out_state!=verbatim) C_putc(' '); + if (protect || out_state==verbatim) C_putc('\\'); + flush_buffer(); if (out_state!=verbatim) out_state=normal; break; + @/@t\4@>@<Case of an identifier@>; + @/@t\4@>@<Case of a section number@>; + @/@t\4@>@<Cases like \.{!=}@>; + case '=': case '>': C_putc(cur_char); C_putc(' '); + out_state=normal; break; + case join: out_state=unbreakable; break; + case constant: if (out_state==verbatim) { + out_state=num_or_id; break; + } + if(out_state==num_or_id) C_putc(' '); out_state=verbatim; break; + case string: if (out_state==verbatim) out_state=normal; + else out_state=verbatim; break; + case '/': C_putc('/'); out_state=post_slash; break; + case '*': if (out_state==post_slash) C_putc(' '); + /* fall through */ + default: C_putc(cur_char); out_state=normal; break; + } +} + +@ @<Cases like \.{!=}@>= +case plus_plus: C_putc('+'); C_putc('+'); out_state=normal; break; +case minus_minus: C_putc('-'); C_putc('-'); out_state=normal; break; +case minus_gt: C_putc('-'); C_putc('>'); out_state=normal; break; +case gt_gt: C_putc('>'); C_putc('>'); out_state=normal; break; +case eq_eq: C_putc('='); C_putc('='); out_state=normal; break; +case lt_lt: C_putc('<'); C_putc('<'); out_state=normal; break; +case gt_eq: C_putc('>'); C_putc('='); out_state=normal; break; +case lt_eq: C_putc('<'); C_putc('='); out_state=normal; break; +case not_eq: C_putc('!'); C_putc('='); out_state=normal; break; +case and_and: C_putc('&'); C_putc('&'); out_state=normal; break; +case or_or: C_putc('|'); C_putc('|'); out_state=normal; break; +case dot_dot_dot: C_putc('.'); C_putc('.'); C_putc('.'); out_state=normal; + break; +case colon_colon: C_putc(':'); C_putc(':'); out_state=normal; break; +case period_ast: C_putc('.'); C_putc('*'); out_state=normal; break; +case minus_gt_ast: C_putc('-'); C_putc('>'); C_putc('*'); out_state=normal; + break; + +@ When an identifier is output to the \CEE/ file, characters in the +range 128--255 must be changed into something else, so the \CEE/ +compiler won't complain. By default, \.{CTANGLE} converts the +character with code $16 x+y$ to the three characters `\.X$xy$', but +a different transliteration table can be specified. Thus a German +might want {\it gr\"un\/} to appear as a still readable \.{gruen}. +This makes debugging a lot less confusing. + +@d translit_length 10 + +@<Glo...@>= +char translit[128][translit_length]; + +@ @<Set init...@>= +{ + int i; + for (i=0;i<128;i++) sprintf(translit[i],"X%02X",(unsigned)(128+i)); +} + +@ @<Case of an identifier@>= +case identifier: + if (out_state==num_or_id) C_putc(' '); + j=(cur_val+name_dir)->byte_start; + k=(cur_val+name_dir+1)->byte_start; + while (j<k) { + if ((unsigned char)(*j)<0200) C_putc(*j); +@^high-bit character handling@> + else C_printf("%s",translit[(unsigned char)(*j)-0200]); + j++; + } + out_state=num_or_id; break; + +@ @<Case of a sec...@>= +case section_number: + if (cur_val>0) C_printf("/*%d:*/",cur_val); + else if(cur_val<0) C_printf("/*:%d*/",-cur_val); + else if (protect) { + cur_byte +=4; /* skip line number and file name */ + cur_char = '\n'; + goto restart; + } else { + sixteen_bits a; + a=0400* *cur_byte++; + a+=*cur_byte++; /* gets the line number */ + C_printf("\n#line %d \"",a); +@:line}{\.{\#line}@> + cur_val=*cur_byte++; + cur_val=0400*(cur_val-0200)+ *cur_byte++; /* points to the file name */ + for (j=(cur_val+name_dir)->byte_start, k=(cur_val+name_dir+1)->byte_start; + j<k; j++) { + if (*j=='\\' || *j=='"') C_putc('\\'); + C_putc(*j); + } + C_printf("%s","\"\n"); + } + break; + +@** Introduction to the input phase. +We have now seen that \.{CTANGLE} will be able to output the full +\CEE/ program, if we can only get that program into the byte memory in +the proper format. The input process is something like the output process +in reverse, since we compress the text as we read it in and we expand it +as we write it out. + +There are three main input routines. The most interesting is the one that gets +the next token of a \CEE/ text; the other two are used to scan rapidly past +\TEX/ text in the \.{CWEB} source code. One of the latter routines will jump to +the next token that starts with `\.{@@}', and the other skips to the end +of a \CEE/ comment. + +@ Control codes in \.{CWEB} begin with `\.{@@}', and the next character +identifies the code. Some of these are of interest only to \.{CWEAVE}, +so \.{CTANGLE} ignores them; the others are converted by \.{CTANGLE} into +internal code numbers by the |ccode| table below. The ordering +of these internal code numbers has been chosen to simplify the program logic; +larger numbers are given to the control codes that denote more significant +milestones. + +@d ignore 0 /* control code of no interest to \.{CTANGLE} */ +@d ord 0302 /* control code for `\.{@@'}' */ +@d control_text 0303 /* control code for `\.{@@t}', `\.{@@\^}', etc. */ +@d translit_code 0304 /* control code for `\.{@@l}' */ +@d output_defs_code 0305 /* control code for `\.{@@h}' */ +@d format_code 0306 /* control code for `\.{@@f}' */ +@d definition 0307 /* control code for `\.{@@d}' */ +@d begin_C 0310 /* control code for `\.{@@c}' */ +@d section_name 0311 /* control code for `\.{@@<}' */ +@d new_section 0312 /* control code for `\.{@@\ }' and `\.{@@*}' */ + +@<Global...@>= +eight_bits ccode[256]; /* meaning of a char following \.{@@} */ + +@ @<Set ini...@>= { + int c; /* must be |int| so the |for| loop will end */ + for (c=0; c<256; c++) ccode[c]=ignore; + ccode[' ']=ccode['\t']=ccode['\n']=ccode['\v']=ccode['\r']=ccode['\f'] + =ccode['*']=new_section; + ccode['@@']='@@'; ccode['=']=string; + ccode['d']=ccode['D']=definition; + ccode['f']=ccode['F']=ccode['s']=ccode['S']=format_code; + ccode['c']=ccode['C']=ccode['p']=ccode['P']=begin_C; + ccode['^']=ccode[':']=ccode['.']=ccode['t']=ccode['T']= + ccode['q']=ccode['Q']=control_text; + ccode['h']=ccode['H']=output_defs_code; + ccode['l']=ccode['L']=translit_code; + ccode['&']=join; + ccode['<']=ccode['(']=section_name; + ccode['\'']=ord; +} + +@ The |skip_ahead| procedure reads through the input at fairly high speed +until finding the next non-ignorable control code, which it returns. + +@c +eight_bits +skip_ahead() /* skip to next control code */ +{ + eight_bits c; /* control code found */ + while (1) { + if (loc>limit && (get_line()==0)) return(new_section); + *(limit+1)='@@'; + while (*loc!='@@') loc++; + if (loc<=limit) { + loc++; c=ccode[(eight_bits)*loc]; loc++; + if (c!=ignore || *(loc-1)=='>') return(c); + } + } +} + +@ The |skip_comment| procedure reads through the input at somewhat high +speed in order to pass over comments, which \.{CTANGLE} does not transmit +to the output. If the comment is introduced by \.{/*}, |skip_comment| +proceeds until finding the end-comment token \.{*/} or a newline; in the +latter case |skip_comment| will be called again by |get_next|, since the +comment is not finished. This is done so that each newline in the +\CEE/ part of a section is copied to the output; otherwise the \&{\#line} +commands inserted into the \CEE/ file by the output routines become useless. +On the other hand, if the comment is introduced by \.{//} (i.e., if it +is a \CPLUSPLUS/ ``short comment''), it always is simply delimited by the next +newline. The boolean argument |is_long_comment| distinguishes between +the two types of comments. + +If |skip_comment| comes to the end of the section, it prints an error message. +No comment, long or short, is allowed to contain `\.{@@\ }' or `\.{@@*}'. + +@<Global...@>= +boolean comment_continues=0; /* are we scanning a comment? */ + +@ @c +int skip_comment(is_long_comment) /* skips over comments */ +boolean is_long_comment; +{ + char c; /* current character */ + while (1) { + if (loc>limit) { + if (is_long_comment) { + if(get_line()) return(comment_continues=1); + else{ + err_print("! Input ended in mid-comment"); +@.Input ended in mid-comment@> + return(comment_continues=0); + } + } + else return(comment_continues=0); + } + c=*(loc++); + if (is_long_comment && c=='*' && *loc=='/') { + loc++; return(comment_continues=0); + } + if (c=='@@') { + if (ccode[(eight_bits)*loc]==new_section) { + err_print("! Section name ended in mid-comment"); loc--; +@.Section name ended in mid-comment@> + return(comment_continues=0); + } + else loc++; + } + } +} + +@* Inputting the next token. + +@d constant 03 + +@<Global...@>= +name_pointer cur_section_name; /* name of section just scanned */ +int no_where; /* suppress |print_where|? */ + +@ @<Include...@>= +#include <ctype.h> /* definition of |isalpha|, |isdigit| and so on */ +#include <stdlib.h> /* definition of |exit| */ + +@ As one might expect, |get_next| consists mostly of a big switch +that branches to the various special cases that can arise. + +@d isxalpha(c) ((c)=='_' || (c)=='$') + /* non-alpha characters allowed in identifier */ +@d ishigh(c) ((unsigned char)(c)>0177) +@^high-bit character handling@> + +@c +eight_bits +get_next() /* produces the next input token */ +{ + static int preprocessing=0; + eight_bits c; /* the current character */ + while (1) { + if (loc>limit) { + if (preprocessing && *(limit-1)!='\\') preprocessing=0; + if (get_line()==0) return(new_section); + else if (print_where && !no_where) { + print_where=0; + @<Insert the line number into |tok_mem|@>; + } + else return ('\n'); + } + c=*loc; + if (comment_continues || (c=='/' && (*(loc+1)=='*' || *(loc+1)=='/'))) { + skip_comment(comment_continues||*(loc+1)=='*'); + /* scan to end of comment or newline */ + if (comment_continues) return('\n'); + else continue; + } + loc++; + if (xisdigit(c) || c=='.') @<Get a constant@>@; + else if (c=='\'' || c=='"' || (c=='L'&&(*loc=='\'' || *loc=='"'))) + @<Get a string@>@; + else if (isalpha(c) || isxalpha(c) || ishigh(c)) + @<Get an identifier@>@; + else if (c=='@@') @<Get control code and possible section name@>@; + else if (xisspace(c)) { + if (!preprocessing || loc>limit) continue; + /* we don't want a blank after a final backslash */ + else return(' '); /* ignore spaces and tabs, unless preprocessing */ + } + else if (c=='#' && loc==buffer+1) preprocessing=1; + mistake: @<Compress two-symbol operator@>@; + return(c); + } +} + +@ The following code assigns values to the combinations \.{++}, +\.{--}, \.{->}, \.{>=}, \.{<=}, \.{==}, \.{<<}, \.{>>}, \.{!=}, \.{||} and +\.{\&\&}, and to the \CPLUSPLUS/ +combinations \.{...}, \.{::}, \.{.*} and \.{->*}. +The compound assignment operators (e.g., \.{+=}) are +treated as separate tokens. + +@d compress(c) if (loc++<=limit) return(c) + +@<Compress tw...@>= +switch(c) { + case '+': if (*loc=='+') compress(plus_plus); break; + case '-': if (*loc=='-') {compress(minus_minus);} + else if (*loc=='>') if (*(loc+1)=='*') {loc++; compress(minus_gt_ast);} + else compress(minus_gt); break; + case '.': if (*loc=='*') {compress(period_ast);} + else if (*loc=='.' && *(loc+1)=='.') { + loc++; compress(dot_dot_dot); + } + break; + case ':': if (*loc==':') compress(colon_colon); break; + case '=': if (*loc=='=') compress(eq_eq); break; + case '>': if (*loc=='=') {compress(gt_eq);} + else if (*loc=='>') compress(gt_gt); break; + case '<': if (*loc=='=') {compress(lt_eq);} + else if (*loc=='<') compress(lt_lt); break; + case '&': if (*loc=='&') compress(and_and); break; + case '|': if (*loc=='|') compress(or_or); break; + case '!': if (*loc=='=') compress(not_eq); break; +} + +@ @<Get an identifier@>= { + id_first=--loc; + while (isalpha(*++loc) || isdigit(*loc) || isxalpha(*loc) || ishigh(*loc)); + id_loc=loc; return(identifier); +} + +@ @<Get a constant@>= { + id_first=loc-1; + if (*id_first=='.' && !xisdigit(*loc)) goto mistake; /* not a constant */ + if (*id_first=='0') { + if (*loc=='x' || *loc=='X') { /* hex constant */ + loc++; while (xisxdigit(*loc)) loc++; goto found; + } + } + while (xisdigit(*loc)) loc++; + if (*loc=='.') { + loc++; + while (xisdigit(*loc)) loc++; + } + if (*loc=='e' || *loc=='E') { /* float constant */ + if (*++loc=='+' || *loc=='-') loc++; + while (xisdigit(*loc)) loc++; + } + found: while (*loc=='u' || *loc=='U' || *loc=='l' || *loc=='L' + || *loc=='f' || *loc=='F') loc++; + id_loc=loc; + return(constant); +} + +@ \CEE/ strings and character constants, delimited by double and single +quotes, respectively, can contain newlines or instances of their own +delimiters if they are protected by a backslash. We follow this +convention, but do not allow the string to be longer than |longest_name|. + +@<Get a string@>= { + char delim = c; /* what started the string */ + id_first = section_text+1; + id_loc = section_text; *++id_loc=delim; + if (delim=='L') { /* wide character constant */ + delim=*loc++; *++id_loc=delim; + } + while (1) { + if (loc>=limit) { + if(*(limit-1)!='\\') { + err_print("! String didn't end"); loc=limit; break; +@.String didn't end@> + } + if(get_line()==0) { + err_print("! Input ended in middle of string"); loc=buffer; break; +@.Input ended in middle of string@> + } + else if (++id_loc<=section_text_end) *id_loc='\n'; /* will print as + \.{"\\\\\\n"} */ + } + if ((c=*loc++)==delim) { + if (++id_loc<=section_text_end) *id_loc=c; + break; + } + if (c=='\\') { + if (loc>=limit) continue; + if (++id_loc<=section_text_end) *id_loc = '\\'; + c=*loc++; + } + if (++id_loc<=section_text_end) *id_loc=c; + } + if (id_loc>=section_text_end) { + printf("\n! String too long: "); +@.String too long@> + term_write(section_text+1,25); + err_print("..."); + } + id_loc++; + return(string); +} + +@ After an \.{@@} sign has been scanned, the next character tells us +whether there is more work to do. + +@<Get control code and possible section name@>= { + c=ccode[(eight_bits)*loc++]; + switch(c) { + case ignore: continue; + case translit_code: err_print("! Use @@l in limbo only"); continue; +@.Use @@l in limbo...@> + case control_text: while ((c=skip_ahead())=='@@'); + /* only \.{@@@@} and \.{@@>} are expected */ + if (*(loc-1)!='>') + err_print("! Double @@ should be used in control text"); +@.Double @@ should be used...@> + continue; + case section_name: + cur_section_name_char=*(loc-1); + @<Scan the section name and make |cur_section_name| point to it@>; + case string: @<Scan a verbatim string@>; + case ord: @<Scan an ASCII constant@>; + default: return(c); + } +} + +@ After scanning a valid ASCII constant that follows +\.{@@'}, this code plows ahead until it finds the next single quote. +(Special care is taken if the quote is part of the constant.) +Anything after a valid ASCII constant is ignored; +thus, \.{@@'\\nopq'} gives the same result as \.{@@'\\n'}. + +@<Scan an ASCII constant@>= + id_first=loc; + if (*loc=='\\') { + if (*++loc=='\'') loc++; + } + while (*loc!='\'') { + if (*loc=='@@') { + if (*(loc+1)!='@@') + err_print("! Double @@ should be used in ASCII constant"); +@.Double @@ should be used...@> + else loc++; + } + loc++; + if (loc>limit) { + err_print("! String didn't end"); loc=limit-1; break; +@.String didn't end@> + } + } + loc++; + return(ord); + +@ @<Scan the section name...@>= { + char *k; /* pointer into |section_text| */ + @<Put section name into |section_text|@>; + if (k-section_text>3 && strncmp(k-2,"...",3)==0) + cur_section_name=section_lookup(section_text+1,k-3,1); /* 1 means is a prefix */ + else cur_section_name=section_lookup(section_text+1,k,0); + if (cur_section_name_char=='(') + @<If it's not there, add |cur_section_name| to the output file stack, or + complain we're out of room@>; + return(section_name); +} + +@ Section names are placed into the |section_text| array with consecutive spaces, +tabs, and carriage-returns replaced by single spaces. There will be no +spaces at the beginning or the end. (We set |section_text[0]=' '| to facilitate +this, since the |section_lookup| routine uses |section_text[1]| as the first +character of the name.) + +@<Set init...@>=section_text[0]=' '; + +@ @<Put section name...@>= +k=section_text; +while (1) { + if (loc>limit && get_line()==0) { + err_print("! Input ended in section name"); +@.Input ended in section name@> + loc=buffer+1; break; + } + c=*loc; + @<If end of name or erroneous nesting, |break|@>; + loc++; if (k<section_text_end) k++; + if (xisspace(c)) { + c=' '; if (*(k-1)==' ') k--; + } +*k=c; +} +if (k>=section_text_end) { + printf("\n! Section name too long: "); +@.Section name too long@> + term_write(section_text+1,25); + printf("..."); mark_harmless; +} +if (*k==' ' && k>section_text) k--; + +@ @<If end of name or erroneous nesting,...@>= +if (c=='@@') { + c=*(loc+1); + if (c=='>') { + loc+=2; break; + } + if (ccode[(eight_bits)c]==new_section) { + err_print("! Section name didn't end"); break; +@.Section name didn't end@> + } + if (ccode[(eight_bits)c]==section_name) { + err_print("! Nesting of section names not allowed"); break; +@.Nesting of section names...@> + } + *(++k)='@@'; loc++; /* now |c==*loc| again */ +} + +@ At the present point in the program we +have |*(loc-1)==string|; we set |id_first| to the beginning +of the string itself, and |id_loc| to its ending-plus-one location in the +buffer. We also set |loc| to the position just after the ending delimiter. + +@<Scan a verbatim string@>= { + id_first=loc++; *(limit+1)='@@'; *(limit+2)='>'; + while (*loc!='@@' || *(loc+1)!='>') loc++; + if (loc>=limit) err_print("! Verbatim string didn't end"); +@.Verbatim string didn't end@> + id_loc=loc; loc+=2; + return(string); +} + +@* Scanning a macro definition. +The rules for generating the replacement texts corresponding to macros and +\CEE/ texts of a section are almost identical; the only differences are that + +\yskip \item{a)}Section names are not allowed in macros; +in fact, the appearance of a section name terminates such macros and denotes +the name of the current section. + +\item{b)}The symbols \.{@@d} and \.{@@f} and \.{@@c} are not allowed after +section names, while they terminate macro definitions. + +\item{c)}Spaces are inserted after right parentheses in macros, because the +ANSI \CEE/ preprocessor sometimes requires it. + +\yskip Therefore there is a single procedure |scan_repl| whose parameter +|t| specifies either |macro| or |section_name|. After |scan_repl| has +acted, |cur_text| will point to the replacement text just generated, and +|next_control| will contain the control code that terminated the activity. + +@d macro 0 +@d app_repl(c) {if (tok_ptr==tok_mem_end) overflow("token"); *tok_ptr++=c;} + +@<Global...@>= +text_pointer cur_text; /* replacement text formed by |scan_repl| */ +eight_bits next_control; + +@ @c +void +scan_repl(t) /* creates a replacement text */ +eight_bits t; +{ + sixteen_bits a; /* the current token */ + if (t==section_name) {@<Insert the line number into |tok_mem|@>;} + while (1) switch (a=get_next()) { + @<In cases that |a| is a non-|char| token (|identifier|, + |section_name|, etc.), either process it and change |a| to a byte + that should be stored, or |continue| if |a| should be ignored, + or |goto done| if |a| signals the end of this replacement text@>@; + case ')': app_repl(a); + if (t==macro) app_repl(' '); + break; + default: app_repl(a); /* store |a| in |tok_mem| */ + } + done: next_control=(eight_bits) a; + if (text_ptr>text_info_end) overflow("text"); + cur_text=text_ptr; (++text_ptr)->tok_start=tok_ptr; +} + +@ Here is the code for the line number: first a |sixteen_bits| equal +to |0150000|; then the numeric line number; then a pointer to the +file name. + +@<Insert the line...@>= +store_two_bytes(0150000); +if (changing && include_depth==change_depth) { /* correction made Feb 2017 */ + id_first=change_file_name; + store_two_bytes((sixteen_bits)change_line); +}@+else { + id_first=cur_file_name; + store_two_bytes((sixteen_bits)cur_line); +} +id_loc=id_first+strlen(id_first); +{int a=id_lookup(id_first,id_loc,0)-name_dir; app_repl((a / 0400)+0200); + app_repl(a % 0400);} + +@ @<In cases that |a| is...@>= +case identifier: a=id_lookup(id_first,id_loc,0)-name_dir; + app_repl((a / 0400)+0200); + app_repl(a % 0400); break; +case section_name: if (t!=section_name) goto done; + else { + @<Was an `@@' missed here?@>; + a=cur_section_name-name_dir; + app_repl((a / 0400)+0250); + app_repl(a % 0400); + @<Insert the line number into |tok_mem|@>; break; + } +case output_defs_code: if (t!=section_name) err_print("! Misplaced @@h"); +@.Misplaced @@h@> + else { + output_defs_seen=1; + a=output_defs_flag; + app_repl((a / 0400)+0200); + app_repl(a % 0400); + @<Insert the line number into |tok_mem|@>; + } + break; +case constant: case string: + @<Copy a string or verbatim construction or numerical constant@>; +case ord: + @<Copy an ASCII constant@>; +case definition: case format_code: case begin_C: if (t!=section_name) goto done; + else { + err_print("! @@d, @@f and @@c are ignored in C text"); continue; +@.@@d, @@f and @@c are ignored in C text@> + } +case new_section: goto done; + +@ @<Was an `@@'...@>= { + char *try_loc=loc; + while (*try_loc==' ' && try_loc<limit) try_loc++; + if (*try_loc=='+' && try_loc<limit) try_loc++; + while (*try_loc==' ' && try_loc<limit) try_loc++; + if (*try_loc=='=') err_print ("! Missing `@@ ' before a named section"); +@.Missing `@@ '...@> + /* user who isn't defining a section should put newline after the name, + as explained in the manual */ +} + +@ @<Copy a string...@>= + app_repl(a); /* |string| or |constant| */ + while (id_first < id_loc) { /* simplify \.{@@@@} pairs */ + if (*id_first=='@@') { + if (*(id_first+1)=='@@') id_first++; + else err_print("! Double @@ should be used in string"); +@.Double @@ should be used...@> + } + app_repl(*id_first++); + } + app_repl(a); break; + +@ This section should be rewritten on machines that don't use ASCII +code internally. +@^ASCII code dependencies@> + +@<Copy an ASCII constant@>= { + int c=(eight_bits) *id_first; + if (c=='\\') { + c=*++id_first; + if (c>='0' && c<='7') { + c-='0'; + if (*(id_first+1)>='0' && *(id_first+1)<='7') { + c=8*c+*(++id_first) - '0'; + if (*(id_first+1)>='0' && *(id_first+1)<='7' && c<32) + c=8*c+*(++id_first)- '0'; + } + } + else switch (c) { + case 't':c='\t';@+break; + case 'n':c='\n';@+break; + case 'b':c='\b';@+break; + case 'f':c='\f';@+break; + case 'v':c='\v';@+break; + case 'r':c='\r';@+break; + case 'a':c='\7';@+break; + case '?':c='?';@+break; + case 'x': + if (xisdigit(*(id_first+1))) c=*(++id_first)-'0'; + else if (xisxdigit(*(id_first+1))) { + ++id_first; + c=toupper(*id_first)-'A'+10; + } + if (xisdigit(*(id_first+1))) c=16*c+*(++id_first)-'0'; + else if (xisxdigit(*(id_first+1))) { + ++id_first; + c=16*c+toupper(*id_first)-'A'+10; + } + break; + case '\\':c='\\';@+break; + case '\'':c='\'';@+break; + case '\"':c='\"';@+break; + default: err_print("! Unrecognized escape sequence"); +@.Unrecognized escape sequence@> + } + }@/ + /* at this point |c| should have been converted to its ASCII code number */ + app_repl(constant); + if (c>=100) app_repl('0'+c/100); + if (c>=10) app_repl('0'+(c/10)%10); + app_repl('0'+c%10); + app_repl(constant); +} +break; + +@* Scanning a section. +The |scan_section| procedure starts when `\.{@@\ }' or `\.{@@*}' has been +sensed in the input, and it proceeds until the end of that section. It +uses |section_count| to keep track of the current section number; with luck, +\.{CWEAVE} and \.{CTANGLE} will both assign the same numbers to sections. + +@<Global...@>= +extern sixteen_bits section_count; /* the current section number */ + +@ The body of |scan_section| is a loop where we look for control codes +that are significant to \.{CTANGLE}: those +that delimit a definition, the \CEE/ part of a module, or a new module. + +@c +void +scan_section() +{ + name_pointer p; /* section name for the current section */ + text_pointer q; /* text for the current section */ + sixteen_bits a; /* token for left-hand side of definition */ + section_count++; @+ no_where=1; + if (*(loc-1)=='*' && show_progress) { /* starred section */ + printf("*%d",section_count); update_terminal; + } + next_control=0; + while (1) { + @<Skip ahead until |next_control| corresponds to \.{@@d}, \.{@@<}, + \.{@@\ } or the like@>; + if (next_control == definition) { /* \.{@@d} */ + @<Scan a definition@>@; + continue; + } + if (next_control == begin_C) { /* \.{@@c} or \.{@@p} */ + p=name_dir; break; + } + if (next_control == section_name) { /* \.{@@<} or \.{@@(} */ + p=cur_section_name; + @<If section is not being defined, |continue| @>; + break; + } + return; /* \.{@@\ } or \.{@@*} */ + } + no_where=print_where=0; + @<Scan the \CEE/ part of the current section@>; +} + +@ At the top of this loop, if |next_control==section_name|, the +section name has already been scanned (see |@<Get control code +and...@>|). Thus, if we encounter |next_control==section_name| in the +skip-ahead process, we should likewise scan the section name, so later +processing will be the same in both cases. + +@<Skip ahead until |next_control| ...@>= +while (next_control<definition) + /* |definition| is the lowest of the ``significant'' codes */ + if((next_control=skip_ahead())==section_name){ + loc-=2; next_control=get_next(); + } + +@ @<Scan a definition@>= { + while ((next_control=get_next())=='\n'); /*allow newline before definition */ + if (next_control!=identifier) { + err_print("! Definition flushed, must start with identifier"); +@.Definition flushed...@> + continue; + } + app_repl(((a=id_lookup(id_first,id_loc,0)-name_dir) / 0400)+0200); + /* append the lhs */ + app_repl(a % 0400); + if (*loc!='(') { /* identifier must be separated from replacement text */ + app_repl(string); app_repl(' '); app_repl(string); + } + scan_repl(macro); + cur_text->text_link=0; /* |text_link==0| characterizes a macro */ +} + +@ If the section name is not followed by \.{=} or \.{+=}, no \CEE/ +code is forthcoming: the section is being cited, not being +defined. This use is illegal after the definition part of the +current section has started, except inside a comment, but +\.{CTANGLE} does not enforce this rule; it simply ignores the offending +section name and everything following it, up to the next significant +control code. + +@<If section is not being defined, |continue| @>= +while ((next_control=get_next())=='+'); /* allow optional \.{+=} */ +if (next_control!='=' && next_control!=eq_eq) + continue; + +@ @<Scan the \CEE/...@>= +@<Insert the section number into |tok_mem|@>; +scan_repl(section_name); /* now |cur_text| points to the replacement text */ +@<Update the data structure so that the replacement text is accessible@>; + +@ @<Insert the section number...@>= +store_two_bytes((sixteen_bits)(0150000+section_count)); + /* |0150000==0320*0400| */ + +@ @<Update the data...@>= +if (p==name_dir||p==0) { /* unnamed section, or bad section name */ + (last_unnamed)->text_link=cur_text-text_info; last_unnamed=cur_text; +} +else if (p->equiv==(char *)text_info) p->equiv=(char *)cur_text; + /* first section of this name */ +else { + q=(text_pointer)p->equiv; + while (q->text_link<section_flag) + q=q->text_link+text_info; /* find end of list */ + q->text_link=cur_text-text_info; +} +cur_text->text_link=section_flag; + /* mark this replacement text as a nonmacro */ + +@ @<Predec...@>= +void phase_one(); + +@ @c +void +phase_one() { + phase=1; + section_count=0; + reset_input(); + skip_limbo(); + while (!input_has_ended) scan_section(); + check_complete(); + phase=2; +} + +@ Only a small subset of the control codes is legal in limbo, so limbo +processing is straightforward. + +@<Predecl...@>= +void skip_limbo(); + +@ @c +void +skip_limbo() +{ + char c; + while (1) { + if (loc>limit && get_line()==0) return; + *(limit+1)='@@'; + while (*loc!='@@') loc++; + if (loc++<=limit) { + c=*loc++; + if (ccode[(eight_bits)c]==new_section) break; + switch (ccode[(eight_bits)c]) { + case translit_code: @<Read in transliteration of a character@>; break; + case format_code: case '@@': break; + case control_text: if (c=='q' || c=='Q') { + while ((c=skip_ahead())=='@@'); + if (*(loc-1)!='>') + err_print("! Double @@ should be used in control text"); +@.Double @@ should be used...@> + break; + } /* otherwise fall through */ + default: err_print("! Double @@ should be used in limbo"); +@.Double @@ should be used...@> + } + } + } +} + +@ @<Read in transliteration of a character@>= + while(xisspace(*loc)&&loc<limit) loc++; + loc+=3; + if (loc>limit || !xisxdigit(*(loc-3)) || !xisxdigit(*(loc-2)) @| + || (*(loc-3)>='0' && *(loc-3)<='7') || !xisspace(*(loc-1))) + err_print("! Improper hex number following @@l"); +@.Improper hex number...@> + else { + unsigned i; + char *beg; + sscanf(loc-3,"%x",&i); + while(xisspace(*loc)&&loc<limit) loc++; + beg=loc; + while(loc<limit&&(xisalpha(*loc)||xisdigit(*loc)||*loc=='_')) loc++; + if (loc-beg>=translit_length) + err_print("! Replacement string in @@l too long"); +@.Replacement string in @@l...@> + else{ + strncpy(translit[i-0200],beg,loc-beg); + translit[i-0200][loc-beg]='\0'; + } + } + +@ Because on some systems the difference between two pointers is a |long| +but not an |int|, we use \.{\%ld} to print these quantities. + +@c +void +print_stats() { + printf("\nMemory usage statistics:\n"); + printf("%ld names (out of %ld)\n", + (long)(name_ptr-name_dir),(long)max_names); + printf("%ld replacement texts (out of %ld)\n", + (long)(text_ptr-text_info),(long)max_texts); + printf("%ld bytes (out of %ld)\n", + (long)(byte_ptr-byte_mem),(long)max_bytes); + printf("%ld tokens (out of %ld)\n", + (long)(tok_ptr-tok_mem),(long)max_toks); +} + +@** Index. +Here is a cross-reference table for \.{CTANGLE}. +All sections in which an identifier is +used are listed with that identifier, except that reserved words are +indexed only when they appear in format definitions, and the appearances +of identifiers in section names are not indexed. Underlined entries +correspond to where the identifier was declared. Error messages and +a few other things like ``ASCII code dependencies'' are indexed here too. diff --git a/web/c_cpp/cweb/cweav-bs.ch b/web/c_cpp/cweb/cweav-bs.ch new file mode 100644 index 0000000000..8c4c1fe8ff --- /dev/null +++ b/web/c_cpp/cweb/cweav-bs.ch @@ -0,0 +1,302 @@ +Changes for CWEAVE for MSDOS and Borland C++ 3.1 using the following +options (and perhaps others): + + -mc -w-pro -Ff=5000 -Z- -O-p + +The options -Z- and -O-p explicitly turn off optimizations that break +the code. (See makefile.bs.) + +The main purpose of these changes is to support MSDOS with full-size arrays +by using "huge" pointers. + +This file contributed by Barry Schwartz, trashman@crud.mn.org, 28 Jun 94. +(Includes workaround for compiler bug [pointers wrapping around at + segment boundaries], November 1993, contributed by Jorge Fernandez Arnaiz + -- arnaiz@redvax1.dgsca.unam.mx) +(Last revised 5 Dec 94 with help of bob@microprograms.com.) + + +@x Section 1. +The ``banner line'' defined here should be changed whenever \.{CWEAVE} +is modified. + +@d banner "This is CWEAVE (Version 3.64)\n" +@y +The ``banner line'' defined here should be changed whenever \.{CWEAVE} +is modified. + +@d banner "This is CWEAVE (Version 3.64pc/big)\n" +@z + + +@x Section 9. +@d chunk_marker 0 + +@<Common code...@>= +typedef struct name_info { + char *byte_start; /* beginning of the name in |byte_mem| */ + struct name_info *link; + union { + struct name_info *Rlink; /* right link in binary search tree for section + names */ + char Ilk; /* used by identifiers in \.{CWEAVE} only */ + } dummy; + char *equiv_or_xref; /* info corresponding to names */ +} name_info; /* contains information about an identifier or section name */ +typedef name_info *name_pointer; /* pointer into array of \&{name\_info}s */ +typedef name_pointer *hash_pointer; +extern char byte_mem[]; /* characters of names */ +extern char *byte_mem_end; /* end of |byte_mem| */ +extern name_info name_dir[]; /* information about names */ +extern name_pointer name_dir_end; /* end of |name_dir| */ +extern name_pointer name_ptr; /* first unused position in |name_dir| */ +extern char *byte_ptr; /* first unused position in |byte_mem| */ +extern name_pointer hash[]; /* heads of hash lists */ +extern hash_pointer hash_end; /* end of |hash| */ +extern hash_pointer h; /* index into hash-head array */ +extern name_pointer id_lookup(); /* looks up a string in the identifier table */ +extern name_pointer section_lookup(); /* finds section name */ +extern void print_section_name(), sprint_section_name(); +@y +@d chunk_marker 0 + +@f huge extern + +@<Common code...@>= +typedef struct name_info { + char huge* byte_start; /* beginning of the name in |byte_mem| */ + struct name_info *link; + union { + struct name_info *Rlink; /* right link in binary search tree for section + names */ + char Ilk; /* used by identifiers in \.{CWEAVE} only */ + } dummy; + union { + char *equiv_member; + char huge* xref_member; + } ptr_union; /* info corresponding to names */ +} name_info; /* contains information about an identifier or section name */ +typedef name_info *name_pointer; /* pointer into array of \&{name\_info}s */ +typedef name_pointer *hash_pointer; +extern char huge byte_mem[]; /* characters of names */ +extern char huge* byte_mem_end; /* end of |byte_mem| */ +extern name_info name_dir[]; /* information about names */ +extern name_pointer name_dir_end; /* end of |name_dir| */ +extern name_pointer name_ptr; /* first unused position in |name_dir| */ +extern char huge* byte_ptr; /* first unused position in |byte_mem| */ +extern name_pointer hash[]; /* heads of hash lists */ +extern hash_pointer hash_end; /* end of |hash| */ +extern hash_pointer h; /* index into hash-head array */ +extern name_pointer id_lookup(); /* looks up a string in the identifier table */ +extern name_pointer section_lookup(); /* finds section name */ +extern void print_section_name(), sprint_section_name(); +@z + + +@x Section 18. +typedef struct xref_info { + sixteen_bits num; /* section number plus zero or |def_flag| */ + struct xref_info *xlink; /* pointer to the previous cross-reference */ +} xref_info; +typedef xref_info *xref_pointer; +@y +typedef struct xref_info { + sixteen_bits num; /* section number plus zero or |def_flag| */ + struct xref_info huge* xlink; /* pointer to the previous cross-reference */ +} xref_info; +typedef xref_info huge* xref_pointer; +@z + + +@x Section 19. +xref_info xmem[max_refs]; /* contains cross-reference information */ +xref_pointer xmem_end = xmem+max_refs-1; +@y +xref_info huge xmem[max_refs]; /* contains cross-reference information */ +xref_pointer xmem_end; +@z + + +@x Section 20. +@d xref equiv_or_xref +@y +@d xref ptr_union.xref_member +@z + + +@x Section 20. +xref_ptr=xmem; name_dir->xref=(char*)xmem; xref_switch=0; section_xref_switch=0; +xmem->num=0; /* sentinel value */ +@y +xmem_end = xmem + max_refs - 1; +xref_ptr=xmem; name_dir->xref=(char*)xmem; xref_switch=0; section_xref_switch=0; +xmem->num=0; /* sentinel value */ +@z + + +@x Section 21. + append_xref(m); xref_ptr->xlink=q; p->xref=(char*)xref_ptr; +@y + append_xref(m); xref_ptr->xlink=q; p->xref=(char huge*)xref_ptr; +@z + + +@x Section 22. + if (r==xmem) p->xref=(char*)xref_ptr; +@y + if (r==xmem) p->xref=(char huge*)xref_ptr; +@z + + +@x Section 23. + q=(xref_pointer)p->xref; + if (q->num==file_flag) return; + append_xref(file_flag); + xref_ptr->xlink = q; + p->xref = (char *)xref_ptr; +@y + q=(xref_pointer)p->xref; + if (q->num==file_flag) return; + append_xref(file_flag); + xref_ptr->xlink = q; + p->xref = (char huge*)xref_ptr; +@z + +@x Section 25. (to please Borland's C++, version 4.02) +token tok_mem[max_toks]; /* tokens */ +token_pointer tok_mem_end = tok_mem+max_toks-1; /* end of |tok_mem| */ +token_pointer tok_start[max_texts]; /* directory into |tok_mem| */ +token_pointer tok_ptr; /* first unused position in |tok_mem| */ +text_pointer text_ptr; /* first unused position in |tok_start| */ +text_pointer tok_start_end = tok_start+max_texts-1; /* end of |tok_start| */ +token_pointer max_tok_ptr; /* largest value of |tok_ptr| */ +@y +token tok_mem[max_toks]; /* tokens */ +token_pointer tok_mem_end; /* end of |tok_mem| */ +token_pointer tok_start[max_texts]; /* directory into |tok_mem| */ +token_pointer tok_ptr; /* first unused position in |tok_mem| */ +text_pointer text_ptr; /* first unused position in |tok_start| */ +text_pointer tok_start_end; /* end of |tok_start| */ +token_pointer max_tok_ptr; /* largest value of |tok_ptr| */ +@z + +@x Section 26. (goes with the previous change) +tok_start[1]=tok_mem+1; +max_tok_ptr=tok_mem+1; max_text_ptr=tok_start+1; +@y +tok_start[1]=tok_mem+1; +tok_mem_end=tok_mem+max_toks-1; +tok_start_end=tok_start+max_texts-1; +max_tok_ptr=tok_mem+1; max_text_ptr=tok_start+1; +@z + + +@x Section 27. + p->ilk=t; p->xref=(char*)xmem; +@y + p->ilk=t; p->xref=(char huge*)xmem; +@z + + +@x Section 27. + p->xref=(char*)xmem; +@y + p->xref=(char huge*)xmem; +@z + + +@x Section 70. + if (unindexed(lhs)) { /* retain only underlined entries */ + xref_pointer q,r=NULL; + for (q=(xref_pointer)lhs->xref;q>xmem;q=q->xlink) + if (q->num<def_flag) + if (r) r->xlink=q->xlink; + else lhs->xref=(char*)q->xlink; + else r=q; + } +@y + if (unindexed(lhs)) { /* retain only underlined entries */ + xref_pointer q,r=NULL; + for (q=(xref_pointer)lhs->xref;q>xmem;q=q->xlink) + if (q->num<def_flag) + if (r) r->xlink=q->xlink; + else lhs->xref=(char huge*)q->xlink; + else r=q; + } +@z + + +@x Section 87. + char *k, *k_end=(p+1)->byte_start; /* pointers into |byte_mem| */ + out('{'); + for (k=p->byte_start; k<k_end; k++) { +@y + char huge* k, huge* k_end=(p+1)->byte_start; /* pointers into |byte_mem| */ + out('{'); + for (k=p->byte_start; k<k_end; k++) { +@z + + +@x Section 116. + append_xref(0); /* this number doesn't matter */ + xref_ptr->xlink=(xref_pointer)p->xref; r=xref_ptr; + p->xref=(char*)xref_ptr; + while (r->xlink!=q) {r->num=r->xlink->num; r=r->xlink;} + r->num=m; /* everything from |q| on is left undisturbed */ +@y + append_xref(0); /* this number doesn't matter */ + xref_ptr->xlink=(xref_pointer)p->xref; r=xref_ptr; + p->xref=(char huge*)xref_ptr; + while (r->xlink!=q) {r->num=r->xlink->num; r=r->xlink;} + r->num=m; /* everything from |q| on is left undisturbed */ +@z + + +@x Section 163. +@ @<Change |pp| to $\max...@>= +@y +@ @<Change |pp| to $\max...@>= +#ifdef __MSDOS__ +if (d<0 && pp+d>pp) pp=scrap_base; /* segmented architecture caused wrap */ +else +#endif +@z + + +@x Section 194. + char *p; /* index into |byte_mem| */ +@y + char huge *p; /* index into |byte_mem| */ +@z + + +@x Section 229. + if (cur_name->xref!=(char*)xmem) { +@y + if (cur_name->xref!=(char huge*)xmem) { +@z + + +@x Section 232. +char *cur_byte; /* index into |byte_mem| */ +@y +char huge* cur_byte; /* index into |byte_mem| */ +@z + + +@x Section 241. +switch (cur_name->ilk) { + case normal: if (is_tiny(cur_name)) out_str("\\|"); + else {char *j; +@y +switch (cur_name->ilk) { + case normal: if (is_tiny(cur_name)) out_str("\\|"); + else {char huge* j; +@z + + +@x Section 241. + case custom: case quoted: {char *j; out_str("$\\"); +@y + case custom: case quoted: {char huge* j; out_str("$\\"); +@z diff --git a/web/c_cpp/cweb/cweav-man.ch b/web/c_cpp/cweb/cweav-man.ch new file mode 100644 index 0000000000..6cfcc8fe2d --- /dev/null +++ b/web/c_cpp/cweb/cweav-man.ch @@ -0,0 +1,22 @@ +Change file to help make Appendix F of the full CWEB manual +@x +\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. +} +@y +\def\botofcontents{\vfill\titlefalse} +\def\contentspagenumber{109} +\def\title{APPENDIX F: CWEAVE} +\let\K=\leftarrow +@z diff --git a/web/c_cpp/cweb/cweav-pc.ch b/web/c_cpp/cweb/cweav-pc.ch new file mode 100644 index 0000000000..28cd94f5a3 --- /dev/null +++ b/web/c_cpp/cweb/cweav-pc.ch @@ -0,0 +1,39 @@ +This is the change file for CWEB's CWEAVE under DOS +(Contributed by Lee Wittenberg, March 1993) +(Also includes workaround for compiler bug, November 1993, + contributed by Jorge Fernandez Arnaiz -- arnaiz@redvax1.dgsca.unam.mx) + +Changes necessary for compiling with Borland C/C++ +Use compilation switches -mc -w-pro -Ff=5000 + +Note: The changes to section 4 are not necessary if using a compiler +that allows >64K arrays. (If you need lots more bytes, try the alternate +change files that have -bs in their name instead of -pc.) + +@x section 1 +@d banner "This is CWEAVE (Version 3.64)\n" +@y +@d banner "This is CWEAVE (Version 3.64pc)\n" +@z + +@x section 4 +@d max_bytes 90000 /* the number of bytes in identifiers, +@y +@d max_bytes (unsigned)60000 /* the number of bytes in identifiers, +@z +@x +@d max_refs 20000 /* number of cross-references; must be less than 65536 */ +@y +@d max_refs 10000 /* number of cross-references; must be less than 65536 */ +@z + +@x section 163 +@ @<Change |pp| to $\max...@>= +@y +@ @<Change |pp| to $\max...@>= +#ifdef __MSDOS__ +if (d<0 && pp+d>pp) pp=scrap_base; /* segmented architecture caused wrap */ +else +#endif +@z + diff --git a/web/c_cpp/cweb/cweav-ql.ch b/web/c_cpp/cweb/cweav-ql.ch new file mode 100644 index 0000000000..3e37ba2352 --- /dev/null +++ b/web/c_cpp/cweb/cweav-ql.ch @@ -0,0 +1,32 @@ +This is the change file for CWEB's CWEAVE under QDOS/SMSQ +(Contributed by Robert H. Klein, September 1994) + +This change file is intended for use with C68 v4.14 (or later). +compile with +ex <dev_>cc;"-v -h -c =500000 cweave_c" + + +@x +\def\title{CWEAVE (Version 3.64)} +@y +\def\title{CWEAVE (QL Version 3.64)} +@z + +@x section 1 +@d banner "This is CWEAVE (Version 3.64)\n" +@y +@d banner "This is CWEAVE (QL Version 3.64)\n" +@z + +@x +@i common.h +@y +@i common_h +@z + + +@x +@i prod.w +@y +@i prod_w +@z diff --git a/web/c_cpp/cweb/cweav-vms.ch b/web/c_cpp/cweb/cweav-vms.ch new file mode 100644 index 0000000000..b6f46559cc --- /dev/null +++ b/web/c_cpp/cweb/cweav-vms.ch @@ -0,0 +1,37 @@ +This is the change file for CWEB's CWEAVE for VAX/VMS. + +created: + 1991 JM (John Mulhollen, Science Applications International Corporation) + +modified: + 01-FEB-1992 ST (Stephan Trebels <trebels@ams02.dnet.gwdg.de>) + > include ctype,stdio from textlibrary SYS$SHARE:VAXCDEF.TLB + > change banner line to include (VAX/VMS) + > allow $ in identifiers (*necessary* for VAX/VMS) + ? will someone eventally make a CLD interface? (should be easy) + +(also modified by Don Knuth to keep version numbers uptodate) + +@x section 1 (01-FEB-1992 ST) +@d banner "This is CWEAVE (Version 3.64)\n" +@y +@d banner "This is CWEAVE (VAX/VMS Version 3.64)\n" +@z + +@x section 6 (from common.h) (01-FEB-1992 ST) +#include <stdio.h> +@y +#include stdio /* VMS searches Textlibraries faster */ +@z + +@x section 38 (1991 JM) (01-FEB-1992 ST) +#include <ctype.h> /* definition of |isalpha|, |isdigit| and so on */ +@y +#include ctype /* VMS searches Textlibraries faster */ +@z + +@x section 39 (01-FEB-1992 ST) +@d isxalpha(c) ((c)=='_') /* non-alpha character allowed in identifier */ +@y +@d isxalpha(c) ((c)=='_' || (c)=='$') /* non-alpha characters allowed in id */ +@z diff --git a/web/c_cpp/cweb/cweav-w32.ch b/web/c_cpp/cweb/cweav-w32.ch new file mode 100644 index 0000000000..26825b0bfd --- /dev/null +++ b/web/c_cpp/cweb/cweav-w32.ch @@ -0,0 +1,52 @@ +This is the change file for CWEB's CWEAVE under Win32 +(Contributed by Fabrice Popineau, February 2002) + +@x section 1 +@d banner "This is CWEAVE (Version 3.64)\n" +@y +@d banner "This is CWEAVE (Version 3.64win32)\n" +@z + +@x +@ We predeclare several standard system functions here instead of including +their system header files, because the names of the header files are not as +standard as the names of the functions. (For example, some \CEE/ environments +have \.{<string.h>} where others have \.{<strings.h>}.) + +@<Predecl...@>= +extern int strlen(); /* length of string */ +extern int strcmp(); /* compare strings lexicographically */ +extern char* strcpy(); /* copy one string to another */ +extern int strncmp(); /* compare up to $n$ string characters */ +extern char* strncpy(); /* copy up to $n$ string characters */ +@y +@z + +@x +int names_match(p,first,l,t) +name_pointer p; /* points to the proposed match */ +@y +int __cdecl names_match(p,first,l,t) +name_pointer p; /* points to the proposed match */ +@z + +@x +void +init_p(p,t) +name_pointer p; +@y +void __cdecl +init_p(p,t) +name_pointer p; +@z + +@x +@ @<Include...@>= +#include <ctype.h> /* definition of |isalpha|, |isdigit| and so on */ +#include <stdlib.h> /* definition of |exit| */ +@y +@ @<Include...@>= +#include <ctype.h> /* definition of |isalpha|, |isdigit| and so on */ +#include <stdlib.h> /* definition of |exit| */ +#include <string.h> /* definition of |strncmp| and |strncpy| */ +@z diff --git a/web/c_cpp/cweb/cweave.w b/web/c_cpp/cweb/cweave.w new file mode 100644 index 0000000000..e06762b1fc --- /dev/null +++ b/web/c_cpp/cweb/cweave.w @@ -0,0 +1,4652 @@ +% 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.64 --- February 2002 +% (essentially the same as version 3.6, which added +% recently introduced features of standard C++ to version 3.4) +% (In November 2016 I made minor adjustments but changed no code -- DEK) + +% 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. + +% 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 +\def\dleft{[\![} \def\dright{]\!]} % double brackets +\mathchardef\RA="3221 % right arrow +\mathchardef\BA="3224 % double arrow +\def\({} % ) kludge for alphabetizing certain section names +\def\TeXxstring{\\{\TEX/\_string}} +\def\skipxTeX{\\{skip\_\TEX/}} +\def\copyxTeX{\\{copy\_\TEX/}} + +\def\title{CWEAVE (Version 3.64)} +\def\topofcontents{\null\vfill + \centerline{\titlefont The {\ttitlefont CWEAVE} processor} + \vskip 15pt + \centerline{(Version 3.64)} + \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 +@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.64)\n" + +@c @<Include files@>@/ +@h +@<Common code for \.{CWEAVE} and \.{CTANGLE}@>@/ +@<Typedef declarations@>@/ +@<Global variables@>@/ +@<Predeclaration of procedures@> + +@ We predeclare several standard system functions here instead of including +their system header files, because the names of the header files are not as +standard as the names of the functions. (For example, some \CEE/ environments +have \.{<string.h>} where others have \.{<strings.h>}.) + +@<Predecl...@>= +extern int strlen(); /* length of string */ +extern int strcmp(); /* compare strings lexicographically */ +extern char* strcpy(); /* copy one string to another */ +extern int strncmp(); /* compare up to $n$ string characters */ +extern char* strncpy(); /* copy up to $n$ string characters */ + +@ \.{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 +file, finally it sorts and outputs the index. + +Please read the documentation for \.{common}, the set of routines common +to \.{CTANGLE} and \.{CWEAVE}, before proceeding further. + +@c +int main (ac, av) +int ac; /* argument count */ +char **av; /* argument values */ +{ + argc=ac; argv=av; + program=cweave; + make_xrefs=force_lines=make_pb=1; /* controlled by command-line options */ + common_init(); + @<Set initial values@>; + if (show_banner) printf(banner); /* print a ``banner line'' */ + @<Store all the reserved words@>; + phase_one(); /* read all the user's text and store the cross-references */ + phase_two(); /* read all the text again and translate it to \TEX/ form */ + phase_three(); /* output the cross-reference index */ + return wrap_up(); /* and exit gracefully */ +} + +@ The following parameters were sufficient in the original \.{WEAVE} to +handle \TEX/, so they should be sufficient for most applications of \.{CWEAVE}. +If you change |max_bytes|, |max_names|, |hash_size|, or |buf_size| +you have to change them also in the file |"common.w"|. + +@d max_bytes 90000 /* the number of bytes in identifiers, + index entries, and section names */ +@d max_names 4000 /* number of identifiers, strings, section names; + must be less than 10240; used in |"common.w"| */ +@d max_sections 2000 /* greater than the total number of sections */ +@d hash_size 353 /* should be prime */ +@d buf_size 100 /* maximum length of input line, plus one */ +@d longest_name 10000 /* section names and strings shouldn't be longer than this */ +@d long_buf_size (buf_size+longest_name) +@d line_length 80 /* lines of \TEX/ output have at most this many characters; + should be less than 256 */ +@d max_refs 20000 /* number of cross-references; must be less than 65536 */ +@d max_toks 20000 /* number of symbols in \CEE/ texts being parsed; + must be less than 65536 */ +@d max_texts 4000 /* number of phrases in \CEE/ texts being parsed; + must be less than 10240 */ +@d max_scraps 2000 /* number of tokens in \CEE/ texts being parsed */ +@d stack_size 400 /* number of simultaneous output levels */ + +@ 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 needs to be updated when |"common.w"| changes. + +@i common.h + +@* Data structures exclusive to {\tt CWEAVE}. +As explained in \.{common.w}, the field of a |name_info| structure +that contains the |rlink| of a section name is used for a completely +different purpose in the case of identifiers. It is then called the +|ilk| of the identifier, and it is used to +distinguish between various types of identifiers, as follows: + +\yskip\hang |normal| and |func_template| identifiers are part of the +\CEE/ program that will appear in italic type (or in typewriter type +if all uppercase). + +\yskip\hang |custom| identifiers are part of the \CEE/ program that +will be typeset in special ways. + +\yskip\hang |roman| identifiers are index entries that appear after +\.{@@\^} in the \.{CWEB} file. + +\yskip\hang |wildcard| identifiers are index entries that appear after +\.{@@:} in the \.{CWEB} file. + +\yskip\hang |typewriter| identifiers are index entries that appear after +\.{@@.} in the \.{CWEB} file. + +\yskip\hang |alfop|, \dots, |template_like| +identifiers are \CEE/ or \CPLUSPLUS/ reserved words whose |ilk| +explains how they are to be treated when \CEE/ code is being +formatted. + +@d ilk dummy.Ilk +@d normal 0 /* ordinary identifiers have |normal| ilk */ +@d roman 1 /* normal index entries have |roman| ilk */ +@d wildcard 2 /* user-formatted index entries have |wildcard| ilk */ +@d typewriter 3 /* `typewriter type' entries have |typewriter| ilk */ +@d abnormal(a) (a->ilk>typewriter) /* tells if a name is special */ +@d func_template 4 /* identifiers that can be followed by optional template */ +@d custom 5 /* identifiers with user-given control sequence */ +@d alfop 22 /* alphabetic operators like \&{and} or \&{not\_eq} */ +@d else_like 26 /* \&{else} */ +@d public_like 40 /* \&{public}, \&{private}, \&{protected} */ +@d operator_like 41 /* \&{operator} */ +@d new_like 42 /* \&{new} */ +@d catch_like 43 /* \&{catch} */ +@d for_like 45 /* \&{for}, \&{switch}, \&{while} */ +@d do_like 46 /* \&{do} */ +@d if_like 47 /* \&{if}, \&{ifdef}, \&{endif}, \&{pragma}, \dots */ +@d delete_like 48 /* \&{delete} */ +@d raw_ubin 49 /* `\.\&' or `\.*' when looking for \&{const} following */ +@d const_like 50 /* \&{const}, \&{volatile} */ +@d raw_int 51 /* \&{int}, \&{char}, \dots; also structure and class names */ +@d int_like 52 /* same, when not followed by left parenthesis or \DC\ */ +@d case_like 53 /* \&{case}, \&{return}, \&{goto}, \&{break}, \&{continue} */ +@d sizeof_like 54 /* \&{sizeof} */ +@d struct_like 55 /* \&{struct}, \&{union}, \&{enum}, \&{class} */ +@d typedef_like 56 /* \&{typedef} */ +@d define_like 57 /* \&{define} */ +@d template_like 58 /* \&{template} */ + +@ 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. + +@<Global...@>= +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 +|p->xref|, which points into the |xmem| array. The elements of |xmem| +are structures consisting of an integer, |num|, and a pointer |xlink| +to another element of |xmem|. If |x=p->xref| is a pointer into |xmem|, +the value of |x->num| is either a section number where |p| is used, +or |cite_flag| plus a section number where |p| is mentioned, +or |def_flag| plus a section number where |p| is defined; +and |x->xlink| points to the next such cross-reference for |p|, +if any. This list of cross-references is in decreasing order by +section number. The next unused slot in |xmem| is |xref_ptr|. +The linked list ends at |&xmem[0]|. + +The global variable |xref_switch| is set either to |def_flag| or to zero, +depending on whether the next cross-reference to an identifier is to be +underlined or not in the index. This switch is set to |def_flag| when +\.{@@!} or \.{@@d} is scanned, and it is cleared to zero when +the next identifier or index entry cross-reference has been made. +Similarly, the global variable |section_xref_switch| is either +|def_flag| or |cite_flag| or zero, depending +on whether a section name is being defined, cited or used in \CEE/ text. + +@<Type...@>= +typedef struct xref_info { + sixteen_bits num; /* section number plus zero or |def_flag| */ + struct xref_info *xlink; /* pointer to the previous cross-reference */ +} xref_info; +typedef xref_info *xref_pointer; + +@ @<Global...@>= +xref_info xmem[max_refs]; /* contains cross-reference information */ +xref_pointer xmem_end = xmem+max_refs-1; +xref_pointer xref_ptr; /* the largest occupied position in |xmem| */ +sixteen_bits xref_switch,section_xref_switch; /* either zero or |def_flag| */ + +@ A section that is used for multi-file output (with the \.{@@(} feature) +has a special first cross-reference whose |num| field is |file_flag|. + +@d file_flag (3*cite_flag) +@d def_flag (2*cite_flag) +@d cite_flag 10240 /* must be strictly larger than |max_sections| */ +@d xref equiv_or_xref + +@<Set init...@>= +xref_ptr=xmem; name_dir->xref=(char*)xmem; xref_switch=0; section_xref_switch=0; +xmem->num=0; /* sentinel value */ + +@ A new cross-reference for an identifier is formed by calling |new_xref|, +which discards duplicate entries and ignores non-underlined references +to one-letter identifiers or \CEE/'s reserved words. + +If the user has sent the |no_xref| flag (the \.{-x} option of the command line), +it is unnecessary to keep track of cross-references for identifiers. +If one were careful, one could probably make more changes around section +100 to avoid a lot of identifier looking up. + +@d append_xref(c) if (xref_ptr==xmem_end) overflow("cross-reference"); + else (++xref_ptr)->num=c; +@d no_xref (flags['x']==0) +@d make_xrefs flags['x'] /* should cross references be output? */ +@d is_tiny(p) ((p+1)->byte_start==(p)->byte_start+1) +@d unindexed(a) (a<res_wd_end && a->ilk>=custom) + /* tells if uses of a name are to be indexed */ + +@c +void +new_xref(p) +name_pointer p; +{ + xref_pointer q; /* pointer to previous cross-reference */ + sixteen_bits m, n; /* new and previous cross-reference value */ + if (no_xref) return; + if ((unindexed(p) || is_tiny(p)) && xref_switch==0) return; + m=section_count+xref_switch; xref_switch=0; q=(xref_pointer)p->xref; + if (q != xmem) { + n=q->num; + if (n==m || n==m+def_flag) return; + else if (m==n+def_flag) { + q->num=m; return; + } + } + append_xref(m); xref_ptr->xlink=q; p->xref=(char*)xref_ptr; +} + +@ The cross-reference lists for section names are slightly different. +Suppose that a section name is defined in sections $m_1$, \dots, +$m_k$, cited in sections $n_1$, \dots, $n_l$, and used in sections +$p_1$, \dots, $p_j$. Then its list will contain $m_1+|def_flag|$, +\dots, $m_k+|def_flag|$, $n_1+|cite_flag|$, \dots, +$n_l+|cite_flag|$, $p_1$, \dots, $p_j$, in this order. + +Although this method of storage takes quadratic time with respect to +the length of the list, under foreseeable uses of \.{CWEAVE} this inefficiency +is insignificant. + +@c +void +new_section_xref(p) +name_pointer p; +{ + xref_pointer q,r; /* pointers to previous cross-references */ + q=(xref_pointer)p->xref; r=xmem; + if (q>xmem) + while (q->num>section_xref_switch) {r=q; q=q->xlink;} + if (r->num==section_count+section_xref_switch) + return; /* don't duplicate entries */ + append_xref(section_count+section_xref_switch); + xref_ptr->xlink=q; section_xref_switch=0; + if (r==xmem) p->xref=(char*)xref_ptr; + else r->xlink=xref_ptr; +} + +@ The cross-reference list for a section name may also begin with +|file_flag|. Here's how that flag gets put~in. + +@c +void +set_file_flag(p) +name_pointer p; +{ + xref_pointer q; + q=(xref_pointer)p->xref; + if (q->num==file_flag) return; + append_xref(file_flag); + xref_ptr->xlink = q; + p->xref = (char *)xref_ptr; +} + +@ A third large area of memory is used for sixteen-bit `tokens', which appear +in short lists similar to the strings of characters in |byte_mem|. Token lists +are used to contain the result of \CEE/ code translated into \TEX/ form; +further details about them will be explained later. A |text_pointer| variable +is an index into |tok_start|. + +@<Typed...@>= +typedef sixteen_bits token; +typedef token *token_pointer; +typedef token_pointer *text_pointer; + +@ The first position of |tok_mem| +that is unoccupied by replacement text is called |tok_ptr|, and the first +unused location of |tok_start| is called |text_ptr|. +Thus, we usually have |*text_ptr==tok_ptr|. + +@<Global...@>= +token tok_mem[max_toks]; /* tokens */ +token_pointer tok_mem_end = tok_mem+max_toks-1; /* end of |tok_mem| */ +token_pointer tok_start[max_texts]; /* directory into |tok_mem| */ +token_pointer tok_ptr; /* first unused position in |tok_mem| */ +text_pointer text_ptr; /* first unused position in |tok_start| */ +text_pointer tok_start_end = tok_start+max_texts-1; /* end of |tok_start| */ +token_pointer max_tok_ptr; /* largest value of |tok_ptr| */ +text_pointer max_text_ptr; /* largest value of |text_ptr| */ + +@ @<Set init...@>= +tok_ptr=tok_mem+1; text_ptr=tok_start+1; tok_start[0]=tok_mem+1; +tok_start[1]=tok_mem+1; +max_tok_ptr=tok_mem+1; max_text_ptr=tok_start+1; + +@ Here are the three procedures needed to complete |id_lookup|: +@c +int names_match(p,first,l,t) +name_pointer p; /* points to the proposed match */ +char *first; /* position of first character of string */ +int l; /* length of identifier */ +eight_bits t; /* desired ilk */ +{ + if (length(p)!=l) return 0; + if (p->ilk!=t && !(t==normal && abnormal(p))) return 0; + return !strncmp(first,p->byte_start,l); +} + +void +init_p(p,t) +name_pointer p; +eight_bits t; +{ + p->ilk=t; p->xref=(char*)xmem; +} + +void +init_node(p) +name_pointer p; +{ + p->xref=(char*)xmem; +} + +@ We have to get \CEE/'s +reserved words into the hash table, and the simplest way to do this is +to insert them every time \.{CWEAVE} is run. Fortunately there are relatively +few reserved words. (Some of these are not strictly ``reserved,'' but +are defined in header files of the ISO Standard \CEE/ Library.) +@^reserved words@> + +@<Store all the reserved words@>= +id_lookup("and",NULL,alfop); +id_lookup("and_eq",NULL,alfop); +id_lookup("asm",NULL,sizeof_like); +id_lookup("auto",NULL,int_like); +id_lookup("bitand",NULL,alfop); +id_lookup("bitor",NULL,alfop); +id_lookup("bool",NULL,raw_int); +id_lookup("break",NULL,case_like); +id_lookup("case",NULL,case_like); +id_lookup("catch",NULL,catch_like); +id_lookup("char",NULL,raw_int); +id_lookup("class",NULL,struct_like); +id_lookup("clock_t",NULL,raw_int); +id_lookup("compl",NULL,alfop); +id_lookup("const",NULL,const_like); +id_lookup("const_cast",NULL,raw_int); +id_lookup("continue",NULL,case_like); +id_lookup("default",NULL,case_like); +id_lookup("define",NULL,define_like); +id_lookup("defined",NULL,sizeof_like); +id_lookup("delete",NULL,delete_like); +id_lookup("div_t",NULL,raw_int); +id_lookup("do",NULL,do_like); +id_lookup("double",NULL,raw_int); +id_lookup("dynamic_cast",NULL,raw_int); +id_lookup("elif",NULL,if_like); +id_lookup("else",NULL,else_like); +id_lookup("endif",NULL,if_like); +id_lookup("enum",NULL,struct_like); +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); +id_lookup("fpos_t",NULL,raw_int); +id_lookup("friend",NULL,int_like); +id_lookup("goto",NULL,case_like); +id_lookup("if",NULL,if_like); +id_lookup("ifdef",NULL,if_like); +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); +id_lookup("long",NULL,raw_int); +id_lookup("mutable",NULL,int_like); +id_lookup("namespace",NULL,struct_like); +id_lookup("new",NULL,new_like); +id_lookup("not",NULL,alfop); +id_lookup("not_eq",NULL,alfop); +id_lookup("NULL",NULL,custom); +id_lookup("offsetof",NULL,raw_int); +id_lookup("operator",NULL,operator_like); +id_lookup("or",NULL,alfop); +id_lookup("or_eq",NULL,alfop); +id_lookup("pragma",NULL,if_like); +id_lookup("private",NULL,public_like); +id_lookup("protected",NULL,public_like); +id_lookup("ptrdiff_t",NULL,raw_int); +id_lookup("public",NULL,public_like); +id_lookup("register",NULL,int_like); +id_lookup("reinterpret_cast",NULL,raw_int); +id_lookup("return",NULL,case_like); +id_lookup("short",NULL,raw_int); +id_lookup("sig_atomic_t",NULL,raw_int); +id_lookup("signed",NULL,raw_int); +id_lookup("size_t",NULL,raw_int); +id_lookup("sizeof",NULL,sizeof_like); +id_lookup("static",NULL,int_like); +id_lookup("static_cast",NULL,raw_int); +id_lookup("struct",NULL,struct_like); +id_lookup("switch",NULL,for_like); +id_lookup("template",NULL,template_like); +id_lookup("this",NULL,custom); +id_lookup("throw",NULL,case_like); +id_lookup("time_t",NULL,raw_int); +id_lookup("try",NULL,else_like); +id_lookup("typedef",NULL,typedef_like); +id_lookup("typeid",NULL,raw_int); +id_lookup("typename",NULL,struct_like); +id_lookup("undef",NULL,if_like); +id_lookup("union",NULL,struct_like); +id_lookup("unsigned",NULL,raw_int); +id_lookup("using",NULL,int_like); +id_lookup("va_dcl",NULL,decl); /* Berkeley's variable-arg-list convention */ +id_lookup("va_list",NULL,raw_int); /* ditto */ +id_lookup("virtual",NULL,int_like); +id_lookup("void",NULL,raw_int); +id_lookup("volatile",NULL,const_like); +id_lookup("wchar_t",NULL,raw_int); +id_lookup("while",NULL,for_like); +id_lookup("xor",NULL,alfop); +id_lookup("xor_eq",NULL,alfop); +res_wd_end=name_ptr; +id_lookup("TeX",NULL,custom); +id_lookup("make_pair",NULL,func_template); + +@* 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: +One simply skips to the next `\.{@@\ }' or `\.{@@*}' that begins a +section; another passes over the \TEX/ text at the beginning of a +section; the third passes over the \TEX/ text in a \CEE/ comment; +and the last, which is the most interesting, gets the next token of +a \CEE/ text. They all use the pointers |limit| and |loc| into +the line of input currently being studied. + +@ Control codes in \.{CWEB}, which begin with `\.{@@}', are converted +into a numeric code designed to simplify \.{CWEAVE}'s logic; for example, +larger numbers are given to the control codes that denote more significant +milestones, and the code of |new_section| should be the largest of +all. Some of these numeric control codes take the place of |char| +control codes that will not otherwise appear in the output of the +scanning routines. +@^ASCII code dependencies@> + +@d ignore 00 /* control code of no interest to \.{CWEAVE} */ +@d verbatim 02 /* takes the place of extended ASCII \.{\char2} */ +@d begin_short_comment 03 /* \CPLUSPLUS/ short comment */ +@d begin_comment '\t' /* tab marks will not appear */ +@d underline '\n' /* this code will be intercepted without confusion */ +@d noop 0177 /* takes the place of ASCII delete */ +@d xref_roman 0203 /* control code for `\.{@@\^}' */ +@d xref_wildcard 0204 /* control code for `\.{@@:}' */ +@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|. + +@<Global...@>= +eight_bits ccode[256]; /* meaning of a char following \.{@@} */ + +@ @<Set ini...@>= +{int c; for (c=0; c<256; c++) ccode[c]=0;} +ccode[' ']=ccode['\t']=ccode['\n']=ccode['\v']=ccode['\r']=ccode['\f'] + =ccode['*']=new_section; +ccode['@@']='@@'; /* `quoted' at sign */ +ccode['=']=verbatim; +ccode['d']=ccode['D']=definition; +ccode['f']=ccode['F']=ccode['s']=ccode['S']=format_code; +ccode['c']=ccode['C']=ccode['p']=ccode['P']=begin_C; +ccode['t']=ccode['T']=TeX_string; +ccode['l']=ccode['L']=translit_code; +ccode['q']=ccode['Q']=noop; +ccode['h']=ccode['H']=output_defs_code; +ccode['&']=join; ccode['<']=ccode['(']=section_name; +ccode['!']=underline; ccode['^']=xref_roman; +ccode[':']=xref_wildcard; ccode['.']=xref_typewriter; ccode[',']=thin_space; +ccode['|']=math_break; ccode['/']=line_break; ccode['#']=big_line_break; +ccode['+']=no_line_break; ccode[';']=pseudo_semi; +ccode['[']=macro_arg_open; ccode[']']=macro_arg_close; +ccode['\'']=ord; +@<Special control codes for debugging@>@; + +@ Users can write +\.{@@2}, \.{@@1}, and \.{@@0} to turn tracing fully on, partly on, +and off, respectively. + +@<Special control codes...@>= +ccode['0']=ccode['1']=ccode['2']=trace; + +@ The |skip_limbo| routine is used on the first pass to skip through +portions of the input that are not in any sections, i.e., that precede +the first section. After this procedure has been called, the value of +|input_has_ended| will tell whether or not a section has actually been found. + +There's a complication that we will postpone until later: If the \.{@@s} +operation appears in limbo, we want to use it to adjust the default +interpretation of identifiers. + +@<Predec...@>= +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++]; + if (c==new_section) return; + if (c==noop) skip_restricted(); + else if (c==format_code) @<Process simple format in limbo@>; + } + } +} + +@ The |skip_TeX| routine is used on the first pass to skip through +the \TEX/ code at the beginning of a section. It returns the next +control code or `\.{\v}' found in the input. A |new_section| is +assumed to exist at the very end of the file. + +@f skip_TeX TeX + +@c +unsigned +skip_TeX() /* skip past pure \TEX/ code */ +{ + while (1) { + if (loc>limit && get_line()==0) return(new_section); + *(limit+1)='@@'; + while (*loc!='@@' && *loc!='|') loc++; + if (*loc++ =='|') return('|'); + if (loc<=limit) return(ccode[(eight_bits)*(loc++)]); + } +} + +@*1 Inputting the next token. +As stated above, \.{CWEAVE}'s most interesting lexical scanning routine is the +|get_next| function that inputs the next token of \CEE/ input. However, +|get_next| is not especially complicated. + +The result of |get_next| is either a |char| code for some special character, +or it is a special code representing a pair of characters (e.g., `\.{!=}'), +or it is the numeric value computed by the |ccode| +table, or it is one of the following special codes: + +\yskip\hang |identifier|: In this case the global variables |id_first| and +|id_loc| will have been set to the beginning and ending-plus-one locations +in the buffer, as required by the |id_lookup| routine. + +\yskip\hang |string|: The string will have been copied into the array +|section_text|; |id_first| and |id_loc| are set as above (now they are +pointers into |section_text|). + +\yskip\hang |constant|: The constant is copied into |section_text|, with +slight modifications; |id_first| and |id_loc| are set. + +\yskip\noindent Furthermore, some of the control codes cause +|get_next| to take additional actions: + +\yskip\hang |xref_roman|, |xref_wildcard|, |xref_typewriter|, |TeX_string|, +|verbatim|: The values of |id_first| and |id_loc| will have been set to +the beginning and ending-plus-one locations in the buffer. + +\yskip\hang |section_name|: In this case the global variable |cur_section| will +point to the |byte_start| entry for the section name that has just been scanned. +The value of |cur_section_char| will be |'('| if the section name was +preceded by \.{@@(} instead of \.{@@<}. + +\yskip\noindent If |get_next| sees `\.{@@!}' +it sets |xref_switch| to |def_flag| and goes on to the next token. + +@d constant 0200 /* \CEE/ constant */ +@d string 0201 /* \CEE/ string */ +@d identifier 0202 /* \CEE/ identifier or reserved word */ + +@<Global...@>= +name_pointer cur_section; /* name of section just scanned */ +char cur_section_char; /* the character just before that name */ + +@ @<Include...@>= +#include <ctype.h> /* definition of |isalpha|, |isdigit| and so on */ +#include <stdlib.h> /* definition of |exit| */ + +@ As one might expect, |get_next| consists mostly of a big switch +that branches to the various special cases that can arise. +\CEE/ allows underscores to appear in identifiers, and some \CEE/ +compilers even allow the dollar sign. + +@d isxalpha(c) ((c)=='_' || (c)=='$') + /* non-alpha characters allowed in identifier */ +@d ishigh(c) ((eight_bits)(c)>0177) +@^high-bit character handling@> + +@<Predecl...@>= +eight_bits get_next(); + +@ @c +eight_bits +get_next() /* produces the next input token */ +{@+eight_bits c; /* the current character */ + while (1) { + @<Check if we're at the end of a preprocessor command@>; + if (loc>limit && get_line()==0) return(new_section); + c=*(loc++); + if (xisdigit(c) || c=='.') @<Get a constant@>@; + else if (c=='\'' || c=='"' || (c=='L'&&(*loc=='\'' || *loc=='"'))@| + || (c=='<' && sharp_include_line==1)) + @<Get a string@>@; + else if (xisalpha(c) || isxalpha(c) || ishigh(c)) + @<Get an identifier@>@; + else if (c=='@@') @<Get control code and possible section name@>@; + else if (xisspace(c)) continue; /* ignore spaces and tabs */ + if (c=='#' && loc==buffer+1) @<Raise preprocessor flag@>; + mistake: @<Compress two-symbol operator@>@; + return(c); + } +} + +@ Because preprocessor commands do not fit in with the rest of the syntax +of \CEE/, +we have to deal with them separately. One solution is to enclose such +commands between special markers. Thus, when a \.\# is seen as the +first character of a line, |get_next| returns a special code +|left_preproc| and raises a flag |preprocessing|. + +We can use the same internal code number for |left_preproc| as we do +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 */ + +@<Glob...@>= +boolean preprocessing=0; /* are we scanning a preprocessor command? */ + +@ @<Raise prep...@>= { + preprocessing=1; + @<Check if next token is |include|@>; + return (left_preproc); +} + +@ An additional complication is the freakish use of \.< and \.> to delimit +a file name in lines that start with \.{\#include}. We must treat this file +name as a string. + +@<Glob...@>= +boolean sharp_include_line=0; /* are we scanning a |#include| line? */ + +@ @<Check if next token is |include|@>= +while (loc<=buffer_end-7 && xisspace(*loc)) loc++; +if (loc<=buffer_end-6 && strncmp(loc,"include",7)==0) sharp_include_line=1; + +@ When we get to the end of a preprocessor line, +we lower the flag and send a code |right_preproc|, unless +the last character was a \.\\. + +@<Check if we're at...@>= + while (loc==limit-1 && preprocessing && *loc=='\\') + if (get_line()==0) return(new_section); /* still in preprocessor mode */ + if (loc>=limit && preprocessing) { + preprocessing=sharp_include_line=0; + return(right_preproc); + } + +@ The following code assigns values to the combinations \.{++}, +\.{--}, \.{->}, \.{>=}, \.{<=}, \.{==}, \.{<<}, \.{>>}, \.{!=}, \.{\v\v}, and +\.{\&\&}, and to the \CPLUSPLUS/ +combinations \.{...}, \.{::}, \.{.*} and \.{->*}. +The compound assignment operators (e.g., \.{+=}) are +treated as separate tokens. + +@d compress(c) if (loc++<=limit) return(c) + +@<Compress tw...@>= +switch(c) { + case '/': if (*loc=='*') {compress(begin_comment);} + else if (*loc=='/') compress(begin_short_comment); break; + case '+': if (*loc=='+') compress(plus_plus); break; + case '-': if (*loc=='-') {compress(minus_minus);} + else if (*loc=='>') if (*(loc+1)=='*') {loc++; compress(minus_gt_ast);} + else compress(minus_gt); break; + case '.': if (*loc=='*') {compress(period_ast);} + else if (*loc=='.' && *(loc+1)=='.') { + loc++; compress(dot_dot_dot); + } + break; + case ':': if (*loc==':') compress(colon_colon); break; + case '=': if (*loc=='=') compress(eq_eq); break; + case '>': if (*loc=='=') {compress(gt_eq);} + else if (*loc=='>') compress(gt_gt); break; + case '<': if (*loc=='=') {compress(lt_eq);} + else if (*loc=='<') compress(lt_lt); break; + case '&': if (*loc=='&') compress(and_and); break; + case '|': if (*loc=='|') compress(or_or); break; + case '!': if (*loc=='=') compress(not_eq); break; +} + +@ @<Get an identifier@>= { + id_first=--loc; + while (isalpha(*++loc) || isdigit(*loc) || isxalpha(*loc) || ishigh(*loc)); + id_loc=loc; return(identifier); +} + +@ Different conventions are followed by \TEX/ and \CEE/ to express octal +and hexadecimal numbers; it is reasonable to stick to each convention +within its realm. Thus the \CEE/ part of a \.{CWEB} file has octals +introduced by \.0 and hexadecimals by \.{0x}, but \.{CWEAVE} will print +with \TEX/ macros that the user can redefine to fit the context. +In order to simplify such macros, we replace some of the characters. + +Notice that in this section and the next, |id_first| and |id_loc| +are pointers into the array |section_text|, not into |buffer|. + +@<Get a constant@>= { + id_first=id_loc=section_text+1; + if (*(loc-1)=='0') { + if (*loc=='x' || *loc=='X') {*id_loc++='^'; loc++; + while (xisxdigit(*loc)) *id_loc++=*loc++;} /* hex constant */ + else if (xisdigit(*loc)) {*id_loc++='~'; + while (xisdigit(*loc)) *id_loc++=*loc++;} /* octal constant */ + else goto dec; /* decimal constant */ + } + else { /* decimal constant */ + if (*(loc-1)=='.' && !xisdigit(*loc)) goto mistake; /* not a constant */ + dec: *id_loc++=*(loc-1); + while (xisdigit(*loc) || *loc=='.') *id_loc++=*loc++; + if (*loc=='e' || *loc=='E') { /* float constant */ + *id_loc++='_'; loc++; + if (*loc=='+' || *loc=='-') *id_loc++=*loc++; + while (xisdigit(*loc)) *id_loc++=*loc++; + } + } + while (*loc=='u' || *loc=='U' || *loc=='l' || *loc=='L' + || *loc=='f' || *loc=='F') { + *id_loc++='$'; *id_loc++=toupper(*loc); loc++; + } + return(constant); +} + +@ \CEE/ strings and character constants, delimited by double and single +quotes, respectively, can contain newlines or instances of their own +delimiters if they are protected by a backslash. We follow this +convention, but do not allow the string to be longer than |longest_name|. + +@<Get a string@>= { + char delim = c; /* what started the string */ + id_first = section_text+1; + id_loc = section_text; + if (delim=='\'' && *(loc-2)=='@@') {*++id_loc='@@'; *++id_loc='@@';} + *++id_loc=delim; + if (delim=='L') { /* wide character constant */ + delim=*loc++; *++id_loc=delim; + } + if (delim=='<') delim='>'; /* for file names in |#include| lines */ + while (1) { + if (loc>=limit) { + if(*(limit-1)!='\\') { + err_print("! String didn't end"); loc=limit; break; +@.String didn't end@> + } + if(get_line()==0) { + err_print("! Input ended in middle of string"); loc=buffer; break; +@.Input ended in middle of string@> + } + } + if ((c=*loc++)==delim) { + if (++id_loc<=section_text_end) *id_loc=c; + break; + } + if (c=='\\') if (loc>=limit) continue; + else if (++id_loc<=section_text_end) { + *id_loc = '\\'; c=*loc++; + } + if (++id_loc<=section_text_end) *id_loc=c; + } + if (id_loc>=section_text_end) { + printf("\n! String too long: "); +@.String too long@> + term_write(section_text+1,25); + printf("..."); mark_error; + } + id_loc++; + return(string); +} + +@ After an \.{@@} sign has been scanned, the next character tells us +whether there is more work to do. + +@<Get control code and possible section name@>= { + c=*loc++; + switch(ccode[(eight_bits)c]) { + 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: + @<Scan the section name and make |cur_section| point to it@>; + case verbatim: @<Scan a verbatim string@>; + case ord: @<Get a string@>; + default: return(ccode[(eight_bits)c]); + } +} + +@ The occurrence of a section name sets |xref_switch| to zero, +because the section name might (for example) follow \&{int}. + +@<Scan the section name...@>= { + char *k; /* pointer into |section_text| */ + cur_section_char=*(loc-1); + @<Put section name into |section_text|@>; + if (k-section_text>3 && strncmp(k-2,"...",3)==0) + cur_section=section_lookup(section_text+1,k-3,1); /* 1 indicates a prefix */ + else cur_section=section_lookup(section_text+1,k,0); + xref_switch=0; return(section_name); +} + +@ Section names are placed into the |section_text| array with consecutive spaces, +tabs, and carriage-returns replaced by single spaces. There will be no +spaces at the beginning or the end. (We set |section_text[0]=' '| to facilitate +this, since the |section_lookup| routine uses |section_text[1]| as the first +character of the name.) + +@<Set init...@>=section_text[0]=' '; + +@ @<Put section name...@>= +k=section_text; +while (1) { + if (loc>limit && get_line()==0) { + err_print("! Input ended in section name"); +@.Input ended in section name@> + loc=buffer+1; break; + } + c=*loc; + @<If end of name or erroneous control code, |break|@>; + loc++; if (k<section_text_end) k++; + if (xisspace(c)) { + c=' '; if (*(k-1)==' ') k--; + } +*k=c; +} +if (k>=section_text_end) { + printf("\n! Section name too long: "); +@.Section name too long@> + term_write(section_text+1,25); + printf("..."); mark_harmless; +} +if (*k==' ' && k>section_text) k--; + +@ @<If end of name...@>= +if (c=='@@') { + c=*(loc+1); + if (c=='>') { + loc+=2; break; + } + if (ccode[(eight_bits)c]==new_section) { + err_print("! Section name didn't end"); break; +@.Section name didn't end@> + } + if (c!='@@') { + err_print("! Control codes are forbidden in section name"); break; +@.Control codes are forbidden...@> + } + *(++k)='@@'; loc++; /* now |c==*loc| again */ +} + +@ This function skips over a restricted context at relatively high speed. + +@<Predecl...@>= +void skip_restricted(); + +@ @c +void +skip_restricted() +{ + id_first=loc; *(limit+1)='@@'; +false_alarm: + while (*loc!='@@') loc++; + id_loc=loc; + if (loc++>limit) { + err_print("! Control text didn't end"); loc=limit; +@.Control text didn't end@> + } + else { + if (*loc=='@@'&&loc<=limit) {loc++; goto false_alarm;} + if (*loc++!='>') + err_print("! Control codes are forbidden in control text"); +@.Control codes are forbidden...@> + } +} + +@ At the present point in the program we +have |*(loc-1)==verbatim|; we set |id_first| to the beginning +of the string itself, and |id_loc| to its ending-plus-one location in the +buffer. We also set |loc| to the position just after the ending delimiter. + +@<Scan a verbatim string@>= { + id_first=loc++; *(limit+1)='@@'; *(limit+2)='>'; + while (*loc!='@@' || *(loc+1)!='>') loc++; + if (loc>=limit) err_print("! Verbatim string didn't end"); +@.Verbatim string didn't end@> + id_loc=loc; loc+=2; + return (verbatim); +} + +@** Phase one processing. +We now have accumulated enough subroutines to make it possible to carry out +\.{CWEAVE}'s first pass over the source file. If everything works right, +both phase one and phase two of \.{CWEAVE} will assign the same numbers to +sections, and these numbers will agree with what \.{CTANGLE} does. + +The global variable |next_control| often contains the most recent output of +|get_next|; in interesting cases, this will be the control code that +ended a section or part of a section. + +@<Global...@>= +eight_bits next_control; /* control code waiting to be acting upon */ + +@ The overall processing strategy in phase one has the following +straightforward outline. + +@<Predecl...@>= +void phase_one(); + +@ @c +void +phase_one() { + phase=1; reset_input(); section_count=0; + skip_limbo(); change_exists=0; + while (!input_has_ended) + @<Store cross-reference data for the current section@>; + changed_section[section_count]=change_exists; + /* the index changes if anything does */ + phase=2; /* prepare for second phase */ + @<Print error messages about unused or undefined section names@>; +} + +@ @<Store cross-reference data...@>= +{ + 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 */ + } + @<Store cross-references in the \TEX/ part of a section@>; + @<Store cross-references in the definition part of a section@>; + @<Store cross-references in the \CEE/ part of a section@>; + if (changed_section[section_count]) change_exists=1; +} + +@ The |C_xref| subroutine stores references to identifiers in +\CEE/ text material beginning with the current value of |next_control| +and continuing until |next_control| is `\.\{' or `\.{\v}', or until the next +``milestone'' is passed (i.e., |next_control>=format_code|). If +|next_control>=format_code| when |C_xref| is called, nothing will happen; +but if |next_control=='|'| upon entry, the procedure assumes that this is +the `\.{\v}' preceding \CEE/ text that is to be processed. + +The parameter |spec_ctrl| is used to change this behavior. In most cases +|C_xref| is called with |spec_ctrl==ignore|, which triggers the default +processing described above. If |spec_ctrl==section_name|, section names will +be gobbled. This is used when \CEE/ text in the \TEX/ part or inside comments +is parsed: It allows for section names to appear in \pb, but these +strings will not be entered into the cross reference lists since they are not +definitions of section names. + +The program uses the fact that our internal code numbers satisfy +the relations |xref_roman==identifier+roman| and |xref_wildcard==identifier ++wildcard| and |xref_typewriter==identifier+typewriter|, +as well as |normal==0|. + +@<Predecl...@>= +void C_xref(); + +@ @c +void +C_xref( spec_ctrl ) /* makes cross-references for \CEE/ identifiers */ + eight_bits spec_ctrl; +{ + name_pointer p; /* a referenced name */ + while (next_control<format_code || next_control==spec_ctrl) { + if (next_control>=identifier && next_control<=xref_typewriter) { + if (next_control>identifier) @<Replace |"@@@@"| by |"@@"| @>@; + p=id_lookup(id_first, id_loc,next_control-identifier); new_xref(p); + } + if (next_control==section_name) { + section_xref_switch=cite_flag; + new_section_xref(cur_section); + } + next_control=get_next(); + if (next_control=='|' || next_control==begin_comment || + next_control==begin_short_comment) return; + } +} + +@ The |outer_xref| subroutine is like |C_xref| except that it begins +with |next_control!='|'| and ends with |next_control>=format_code|. Thus, it +handles \CEE/ text with embedded comments. + +@<Predecl...@>= +void outer_xref(); + +@ @c +void +outer_xref() /* extension of |C_xref| */ +{ + int bal; /* brace level in comment */ + while (next_control<format_code) + if (next_control!=begin_comment && next_control!=begin_short_comment) + C_xref(ignore); + else { + boolean is_long_comment=(next_control==begin_comment); + bal=copy_comment(is_long_comment,1); next_control='|'; + while (bal>0) { + C_xref(section_name); /* do not reference section names in comments */ + if (next_control=='|') bal=copy_comment(is_long_comment,bal); + else bal=0; /* an error message will occur in phase two */ + } + } +} + +@ In the \TEX/ part of a section, cross-reference entries are made only for +the identifiers in \CEE/ texts enclosed in \pb, or for control texts +enclosed in \.{@@\^}$\,\ldots\,$\.{@@>} or \.{@@.}$\,\ldots\,$\.{@@>} +or \.{@@:}$\,\ldots\,$\.{@@>}. + +@<Store cross-references in the \T...@>= +while (1) { + switch (next_control=skip_TeX()) { + 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=*(loc-1)-'0'; continue; + case '|': C_xref(section_name); break; + case xref_roman: case xref_wildcard: case xref_typewriter: + case noop: case section_name: + loc-=2; next_control=get_next(); /* scan to \.{@@>} */ + if (next_control>=xref_roman && next_control<=xref_typewriter) { + @<Replace |"@@@@"| by |"@@"| @>@; + new_xref(id_lookup(id_first, id_loc,next_control-identifier)); + } + break; + } + if (next_control>=format_code) break; +} + +@ @<Replace |"@@@@"| by |"@@"| @>= +{ + char *src=id_first,*dst=id_first; + while(src<id_loc){ + if(*src=='@@') src++; + *dst++=*src++; + } + id_loc=dst; + while (dst<src) *dst++=' '; /* clean up in case of error message display */ +} + +@ During the definition and \CEE/ parts of a section, cross-references +are made for all identifiers except reserved words. However, the right +identifier in a format definition is not referenced, and the left +identifier is referenced only if it has been explicitly +underlined (preceded by \.{@@!}). +The \TEX/ code in comments is, of course, ignored, except for +\CEE/ portions enclosed in \pb; the text of a section name is skipped +entirely, even if it contains \pb\ constructions. + +The variables |lhs| and |rhs| point to the respective identifiers involved +in a format definition. + +@<Global...@>= +name_pointer lhs, rhs; /* pointers to |byte_start| for format identifiers */ +name_pointer res_wd_end; /* pointer to the first nonreserved identifier */ + +@ When we get to the following code we have |next_control>=format_code|. + +@<Store cross-references in the d...@>= +while (next_control<=definition) { /* |format_code| or |definition| */ + if (next_control==definition) { + xref_switch=def_flag; /* implied \.{@@!} */ + next_control=get_next(); + } else @<Process a format definition@>; + outer_xref(); +} + +@ Error messages for improper format definitions will be issued in phase +two. Our job in phase one is to define the |ilk| of a properly formatted +identifier, and to remove cross-references to identifiers that we now +discover should be unindexed. + +@<Process a form...@>= { + next_control=get_next(); + if (next_control==identifier) { + lhs=id_lookup(id_first, id_loc,normal); lhs->ilk=normal; + if (xref_switch) new_xref(lhs); + next_control=get_next(); + if (next_control==identifier) { + rhs=id_lookup(id_first, id_loc,normal); + lhs->ilk=rhs->ilk; + if (unindexed(lhs)) { /* retain only underlined entries */ + xref_pointer q,r=NULL; + for (q=(xref_pointer)lhs->xref;q>xmem;q=q->xlink) + if (q->num<def_flag) + if (r) r->xlink=q->xlink; + else lhs->xref=(char*)q->xlink; + else r=q; + } + next_control=get_next(); + } + } +} + +@ A much simpler processing of format definitions occurs when the +definition is found in limbo. + +@<Process simple format in limbo@>= +{ + if (get_next()!=identifier) + err_print("! Missing left identifier of @@s"); +@.Missing left identifier...@> + else { + lhs=id_lookup(id_first,id_loc,normal); + if (get_next()!=identifier) + err_print("! Missing right identifier of @@s"); +@.Missing right identifier...@> + else { + rhs=id_lookup(id_first,id_loc,normal); + lhs->ilk=rhs->ilk; + } + } +} + +@ Finally, when the \TEX/ and definition parts have been treated, we have +|next_control>=begin_C|. + +@<Store cross-references in the \CEE/...@>= +if (next_control<=section_name) { /* |begin_C| or |section_name| */ + if (next_control==begin_C) section_xref_switch=0; + else { + section_xref_switch=def_flag; + if(cur_section_char=='(' && cur_section!=name_dir) + set_file_flag(cur_section); + } + do { + if (next_control==section_name && cur_section!=name_dir) + new_section_xref(cur_section); + next_control=get_next(); outer_xref(); + } while ( next_control<=section_name); +} + +@ After phase one has looked at everything, we want to check that each +section name was both defined and used. The variable |cur_xref| will point +to cross-references for the current section name of interest. + +@<Global...@>= +xref_pointer cur_xref; /* temporary cross-reference pointer */ +boolean an_output; /* did |file_flag| precede |cur_xref|? */ + +@ The following recursive procedure +walks through the tree of section names and prints out anomalies. +@^recursion@> + +@<Predecl...@>= +void section_check(); + +@ @c +void +section_check(p) +name_pointer p; /* print anomalies in subtree |p| */ +{ + if (p) { + section_check(p->llink); + cur_xref=(xref_pointer)p->xref; + if (cur_xref->num==file_flag) {an_output=1; cur_xref=cur_xref->xlink;} + else an_output=0; + if (cur_xref->num <def_flag) { + printf("\n! Never defined: <"); print_section_name(p); putchar('>'); mark_harmless; +@.Never defined: <section name>@> + } + while (cur_xref->num >=cite_flag) cur_xref=cur_xref->xlink; + if (cur_xref==xmem && !an_output) { + printf("\n! Never used: <"); print_section_name(p); putchar('>'); mark_harmless; +@.Never used: <section name>@> + } + section_check(p->rlink); + } +} + +@ @<Print error messages about un...@>=section_check(root) + +@* Low-level output routines. +The \TEX/ output is supposed to appear in lines at most |line_length| +characters long, so we place it into an output buffer. During the output +process, |out_line| will hold the current line number of the line about to +be output. + +@<Global...@>= +char out_buf[line_length+1]; /* assembled characters */ +char *out_ptr; /* last character in |out_buf| */ +char *out_buf_end = out_buf+line_length; /* end of |out_buf| */ +int out_line; /* number of next line to be output */ + +@ The |flush_buffer| routine empties the buffer up to a given breakpoint, +and moves any remaining characters to the beginning of the next line. +If the |per_cent| parameter is 1 a |'%'| is appended to the line +that is being output; in this case the breakpoint |b| should be strictly +less than |out_buf_end|. If the |per_cent| parameter is |0|, +trailing blanks are suppressed. +The characters emptied from the buffer form a new line of output; +if the |carryover| parameter is true, a |"%"| in that line will be +carried over to the next line (so that \TEX/ will ignore the completion +of commented-out text). + +@d c_line_write(c) fflush(active_file),fwrite(out_buf+1,sizeof(char),c,active_file) +@d tex_putc(c) putc(c,active_file) +@d tex_new_line putc('\n',active_file) +@d tex_printf(c) fprintf(active_file,c) + +@c +void +flush_buffer(b,per_cent,carryover) +char *b; /* outputs from |out_buf+1| to |b|,where |b<=out_ptr| */ +boolean per_cent,carryover; +{ + char *j; j=b; /* pointer into |out_buf| */ + if (! per_cent) /* remove trailing blanks */ + while (j>out_buf && *j==' ') j--; + c_line_write(j-out_buf); + if (per_cent) tex_putc('%'); + tex_new_line; out_line++; + if (carryover) + while (j>out_buf) + if (*j--=='%' && (j==out_buf || *j!='\\')) { + *b--='%'; break; + } + if (b<out_ptr) strncpy(out_buf+1,b+1,out_ptr-b); + out_ptr-=b-out_buf; +} + +@ When we are copying \TEX/ source material, we retain line breaks +that occur in the input, except that an empty line is not +output when the \TEX/ source line was nonempty. For example, a line +of the \TEX/ file that contains only an index cross-reference entry +will not be copied. The |finish_line| routine is called just before +|get_line| inputs a new line, and just after a line break token has +been emitted during the output of translated \CEE/ text. + +@c +void +finish_line() /* do this at the end of a line */ +{ + char *k; /* pointer into |buffer| */ + if (out_ptr>out_buf) flush_buffer(out_ptr,0,0); + else { + for (k=buffer; k<=limit; k++) + if (!(xisspace(*k))) return; + flush_buffer(out_buf,0,0); + } +} + +@ 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}'. + +@<Set init...@>= +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 +full. If we want to append more than one character at once, we say +|out_str(s)|, where |s| is a string containing the characters. + +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 +out_str(s) /* output characters from |s| to end of string */ +char *s; +{ + while (*s) out(*s++); +} + +@ The |break_out| routine is called just before the output buffer is about +to overflow. To make this routine a little faster, we initialize position +0 of the output buffer to `\.\\'; this character isn't really output. + +@<Set init...@>= +out_buf[0]='\\'; + +@ A long line is broken at a blank space or just before a backslash that isn't +preceded by another backslash. In the latter case, a |'%'| is output at +the break. + +@<Predecl...@>= +void break_out(); + +@ @c +void +break_out() /* finds a way to break the output line */ +{ + char *k=out_ptr; /* pointer into |out_buf| */ + while (1) { + if (k==out_buf) @<Print warning message, break the line, |return|@>; + if (*k==' ') { + flush_buffer(k,0,1); return; + } + if (*(k--)=='\\' && *k!='\\') { /* we've decreased |k| */ + flush_buffer(k,1,1); return; + } + } +} + +@ We get to this section only in the unusual case that the entire output line +consists of a string of backslashes followed by a string of nonblank +non-backslashes. In such cases it is almost always safe to break the +line by putting a |'%'| just before the last character. + +@<Print warning message...@>= +{ + printf("\n! Line had to be broken (output l. %d):\n",out_line); +@.Line had to be broken@> + term_write(out_buf+1, out_ptr-out_buf-1); + new_line; mark_harmless; + flush_buffer(out_ptr-1,1,1); return; +} + +@ 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 +out_section(n) +sixteen_bits n; +{ + 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 +entry, enclosing it in braces. + +@c +void +out_name(p,quote_xalpha) +name_pointer p; +boolean quote_xalpha; +{ + char *k, *k_end=(p+1)->byte_start; /* pointers into |byte_mem| */ + out('{'); + for (k=p->byte_start; k<k_end; k++) { + if (isxalpha(*k) && quote_xalpha) out('\\'); +@.\\\$@> +@.\\\_@> + out(*k); + } + out('}'); +} + +@* Routines that copy \TEX/ material. +During phase two, we use subroutines |copy_limbo|, |copy_TeX|, and +|copy_comment| in place of the analogous |skip_limbo|, |skip_TeX|, and +|skip_comment| that were used in phase one. (Well, |copy_comment| +was actually written in such a way that it functions as |skip_comment| +in phase one.) + +The |copy_limbo| routine, for example, takes \TEX/ material that is not +part of any section and transcribes it almost verbatim to the output file. +The use of `\.{@@}' signs is severely restricted in such material: +`\.{@@@@}' pairs are replaced by singletons; `\.{@@l}' and `\.{@@q}' and +`\.{@@s}' are interpreted. + +@c +void +copy_limbo() +{ + char c; + while (1) { + if (loc>limit && (finish_line(), get_line()==0)) return; + *(limit+1)='@@'; + while (*loc!='@@') out(*(loc++)); + if (loc++<=limit) { + c=*loc++; + if (ccode[(eight_bits)c]==new_section) break; + switch (ccode[(eight_bits)c]) { + case translit_code: out_str("\\ATL"); break; +@.\\ATL@> + case '@@': out('@@'); break; + case noop: skip_restricted(); break; + 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 */ + default: err_print("! Double @@ should be used in limbo"); +@.Double @@ should be used...@> + out('@@'); + } + } + } +} + +@ The |copy_TeX| routine processes the \TEX/ code at the beginning of a +section; for example, the words you are now reading were copied in this +way. It returns the next control code or `\.{\v}' found in the input. +We don't copy spaces or tab marks into the beginning of a line. This +makes the test for empty lines in |finish_line| work. + +@ @f copy_TeX TeX +@c +eight_bits +copy_TeX() +{ + char c; /* current character being copied */ + while (1) { + if (loc>limit && (finish_line(), get_line()==0)) return(new_section); + *(limit+1)='@@'; + while ((c=*(loc++))!='|' && c!='@@') { + out(c); + if (out_ptr==out_buf+1 && (xisspace(c))) out_ptr--; + } + if (c=='|') return('|'); + if (loc<=limit) return(ccode[(eight_bits)*(loc++)]); + } +} + +@ The |copy_comment| function issues a warning if more braces are opened than +closed, and in the case of a more serious error it supplies enough +braces to keep \TEX/ from complaining about unbalanced braces. +Instead of copying the \TEX/ material +into the output buffer, this function copies it into the token memory +(in phase two only). +The abbreviation |app_tok(t)| is used to append token |t| to the current +token list, and it also makes sure that it is possible to append at least +one further token without overflow. + +@d app_tok(c) {if (tok_ptr+2>tok_mem_end) overflow("token"); *(tok_ptr++)=c;} + +@<Predec...@>= +int copy_comment(); + +@ @c +int copy_comment(is_long_comment,bal) /* copies \TEX/ code in comments */ +boolean is_long_comment; /* is this a traditional \CEE/ comment? */ +int bal; /* brace balance */ +{ + char c; /* current character being copied */ + while (1) { + if (loc>limit) { + if (is_long_comment) { + if (get_line()==0) { + err_print("! Input ended in mid-comment"); +@.Input ended in mid-comment@> + loc=buffer+1; goto done; + } + } + else { + if (bal>1) err_print("! Missing } in comment"); +@.Missing \} in comment@> + goto done; + } + } + c=*(loc++); + if (c=='|') return(bal); + if (is_long_comment) @<Check for end of comment@>; + if (phase==2) { + if (ishigh(c)) app_tok(quoted_char); + app_tok(c); + } + @<Copy special things when |c=='@@', '\\'|@>; + if (c=='{') bal++; + else if (c=='}') { + if(bal>1) bal--; + else {err_print("! Extra } in comment"); +@.Extra \} in comment@> + if (phase==2) tok_ptr--; + } + } + } +done:@<Clear |bal| and |return|@>; +} + +@ @<Check for end of comment@>= +if (c=='*' && *loc=='/') { + loc++; + if (bal>1) err_print("! Missing } in comment"); +@.Missing \} in comment@> + goto done; +} + +@ @<Copy special things when |c=='@@'...@>= +if (c=='@@') { + if (*(loc++)!='@@') { + err_print("! Illegal use of @@ in comment"); +@.Illegal use of @@...@> + loc-=2; if (phase==2) *(tok_ptr-1)=' '; goto done; + } +} +else if (c=='\\' && *loc!='@@') + if (phase==2) app_tok(*(loc++))@; else loc++; + +@ We output +enough right braces to keep \TEX/ happy. + +@<Clear |bal|...@>= +if (phase==2) while (bal-- >0) app_tok('}'); +return(0); + +@** Parsing. +The most intricate part of \.{CWEAVE} is its mechanism for converting +\CEE/-like code into \TEX/ code, and we might as well plunge into this +aspect of the program now. A ``bottom up'' approach is used to parse the +\CEE/-like material, since \.{CWEAVE} must deal with fragmentary +constructions whose overall ``part of speech'' is not known. + +At the lowest level, the input is represented as a sequence of entities +that we shall call {\it scraps}, where each scrap of information consists +of two parts, its {\it category} and its {\it translation}. The category +is essentially a syntactic class, and the translation is a token list that +represents \TEX/ code. Rules of syntax and semantics tell us how to +combine adjacent scraps into larger ones, and if we are lucky an entire +\CEE/ text that starts out as hundreds of small scraps will join +together into one gigantic scrap whose translation is the desired \TEX/ +code. If we are unlucky, we will be left with several scraps that don't +combine; their translations will simply be output, one by one. + +The combination rules are given as context-sensitive productions that are +applied from left to right. Suppose that we are currently working on the +sequence of scraps $s_1\,s_2\ldots s_n$. We try first to find the longest +production that applies to an initial substring $s_1\,s_2\ldots\,$; but if +no such productions exist, we try to find the longest production +applicable to the next substring $s_2\,s_3\ldots\,$; and if that fails, we +try to match $s_3\,s_4\ldots\,$, etc. + +A production applies if the category codes have a given pattern. For +example, one of the productions (see rule~3) is +$$\hbox{|exp| }\left\{\matrix{\hbox{|binop|}\cr\hbox{|ubinop|}}\right\} +\hbox{ |exp| }\RA\hbox{ |exp|}$$ +and it means that three consecutive scraps whose respective categories are +|exp|, |binop| (or |ubinop|), +and |exp| are converted to one scrap whose category +is |exp|. The translations of the original +scraps are simply concatenated. The case of +$$\hbox{|exp| |comma| |exp| $\RA$ |exp|} \hskip4emE_1C\,\\{opt}9\,E_2$$ +(rule 4) is only slightly more complicated: +Here the resulting |exp| translation +consists not only of the three original translations, but also of the +tokens |opt| and 9 between the translations of the +|comma| and the following |exp|. +In the \TEX/ file, this will specify an optional line break after the +comma, with penalty 90. + +At each opportunity the longest possible production is applied. For +example, if the current sequence of scraps is |int_like| |cast| +|lbrace|, rule 31 is applied; but if the sequence is |int_like| |cast| +followed by anything other than |lbrace|, rule 32 takes effect. + +Translation rules such as `$E_1C\,\\{opt}9\,E_2$' above use subscripts +to distinguish between translations of scraps whose categories have the +same initial letter; these subscripts are assigned from left to right. + +@ Here is a list of the category codes that scraps can have. +(A few others, like |int_like|, have already been defined; the +|cat_name| array contains a complete list.) + +@d exp 1 /* denotes an expression, including perhaps a single identifier */ +@d unop 2 /* denotes a unary operator */ +@d binop 3 /* denotes a binary operator */ +@d ubinop 4 + /* denotes an operator that can be unary or binary, depending on context */ +@d cast 5 /* denotes a cast */ +@d question 6 /* denotes a question mark and possibly the expressions flanking it */ +@d lbrace 7 /* denotes a left brace */ +@d rbrace 8 /* denotes a right brace */ +@d decl_head 9 /* denotes an incomplete declaration */ +@d comma 10 /* denotes a comma */ +@d lpar 11 /* denotes a left parenthesis or left bracket */ +@d rpar 12 /* denotes a right parenthesis or right bracket */ +@d prelangle 13 /* denotes `$<$' before we know what it is */ +@d prerangle 14 /* denotes `$>$' before we know what it is */ +@d langle 15 /* denotes `$<$' when it's used as angle bracket in a template */ +@d colcol 18 /* denotes `::' */ +@d base 19 /* denotes a colon that introduces a base specifier */ +@d decl 20 /* denotes a complete declaration */ +@d struct_head 21 /* denotes the beginning of a structure specifier */ +@d stmt 23 /* denotes a complete statement */ +@d function 24 /* denotes a complete function */ +@d fn_decl 25 /* denotes a function declarator */ +@d semi 27 /* denotes a semicolon */ +@d colon 28 /* denotes a colon */ +@d tag 29 /* denotes a statement label */ +@d if_head 30 /* denotes the beginning of a compound conditional */ +@d else_head 31 /* denotes a prefix for a compound statement */ +@d if_clause 32 /* pending \.{if} together with a condition */ +@d lproc 35 /* begins a preprocessor command */ +@d rproc 36 /* ends a preprocessor command */ +@d insert 37 /* a scrap that gets combined with its neighbor */ +@d section_scrap 38 /* section name */ +@d dead 39 /* scrap that won't combine */ +@d ftemplate 59 /* \\{make\_pair} */ +@d new_exp 60 /* \&{new} and a following type identifier */ +@d begin_arg 61 /* \.{@@[} */ +@d end_arg 62 /* \.{@@]} */ + +@<Glo...@>= +char cat_name[256][12]; +eight_bits cat_index; + +@ @<Set in...@>= + for (cat_index=0;cat_index<255;cat_index++) + strcpy(cat_name[cat_index],"UNKNOWN"); +@.UNKNOWN@> + strcpy(cat_name[exp],"exp"); + strcpy(cat_name[unop],"unop"); + strcpy(cat_name[binop],"binop"); + strcpy(cat_name[ubinop],"ubinop"); + strcpy(cat_name[cast],"cast"); + strcpy(cat_name[question],"?"); + strcpy(cat_name[lbrace],"{"@q}@>); + strcpy(cat_name[rbrace],@q{@>"}"); + strcpy(cat_name[decl_head],"decl_head"); + strcpy(cat_name[comma],","); + strcpy(cat_name[lpar],"("); + strcpy(cat_name[rpar],")"); + strcpy(cat_name[prelangle],"<"); + strcpy(cat_name[prerangle],">"); + strcpy(cat_name[langle],"\\<"); + strcpy(cat_name[colcol],"::"); + strcpy(cat_name[base],"\\:"); + strcpy(cat_name[decl],"decl"); + strcpy(cat_name[struct_head],"struct_head"); + strcpy(cat_name[alfop],"alfop"); + strcpy(cat_name[stmt],"stmt"); + strcpy(cat_name[function],"function"); + strcpy(cat_name[fn_decl],"fn_decl"); + strcpy(cat_name[else_like],"else_like"); + strcpy(cat_name[semi],";"); + strcpy(cat_name[colon],":"); + strcpy(cat_name[tag],"tag"); + strcpy(cat_name[if_head],"if_head"); + strcpy(cat_name[else_head],"else_head"); + strcpy(cat_name[if_clause],"if()"); + strcpy(cat_name[lproc],"#{"@q}@>); + strcpy(cat_name[rproc],@q{@>"#}"); + strcpy(cat_name[insert],"insert"); + strcpy(cat_name[section_scrap],"section"); + strcpy(cat_name[dead],"@@d"); + strcpy(cat_name[public_like],"public"); + strcpy(cat_name[operator_like],"operator"); + strcpy(cat_name[new_like],"new"); + strcpy(cat_name[catch_like],"catch"); + strcpy(cat_name[for_like],"for"); + strcpy(cat_name[do_like],"do"); + strcpy(cat_name[if_like],"if"); + strcpy(cat_name[delete_like],"delete"); + strcpy(cat_name[raw_ubin],"ubinop?"); + strcpy(cat_name[const_like],"const"); + strcpy(cat_name[raw_int],"raw"); + strcpy(cat_name[int_like],"int"); + strcpy(cat_name[case_like],"case"); + strcpy(cat_name[sizeof_like],"sizeof"); + strcpy(cat_name[struct_like],"struct"); + strcpy(cat_name[typedef_like],"typedef"); + strcpy(cat_name[define_like],"define"); + strcpy(cat_name[template_like],"template"); + strcpy(cat_name[ftemplate],"ftemplate"); + strcpy(cat_name[new_exp],"new_exp"); + strcpy(cat_name[begin_arg],"@@["@q]@>); + strcpy(cat_name[end_arg],@q[@>"@@]"); + strcpy(cat_name[0],"zero"); + +@ This code allows \.{CWEAVE} to display its parsing steps. + +@c +void +print_cat(c) /* symbolic printout of a category */ +eight_bits c; +{ + printf(cat_name[c]); +} + +@ The token lists for translated \TEX/ output contain some special control +symbols as well as ordinary characters. These control symbols are +interpreted by \.{CWEAVE} before they are written to the output file. + +\yskip\hang |break_space| denotes an optional line break or an en space; + +\yskip\hang |force| denotes a line break; + +\yskip\hang |big_force| denotes a line break with additional vertical space; + +\yskip\hang |preproc_line| denotes that the line will be printed flush left; + +\yskip\hang |opt| denotes an optional line break (with the continuation +line indented two ems with respect to the normal starting position)---this +code is followed by an integer |n|, and the break will occur with penalty +$10n$; + +\yskip\hang |backup| denotes a backspace of one em; + +\yskip\hang |cancel| obliterates any |break_space|, |opt|, |force|, or +|big_force| tokens that immediately precede or follow it and also cancels any +|backup| tokens that follow it; + +\yskip\hang |indent| causes future lines to be indented one more em; + +\yskip\hang |outdent| causes future lines to be indented one less em. + +\yskip\noindent All of these tokens are removed from the \TEX/ output that +comes from \CEE/ text between \pb\ signs; |break_space| and |force| and +|big_force| become single spaces in this mode. The translation of other +\CEE/ texts results in \TEX/ control sequences \.{\\1}, \.{\\2}, +\.{\\3}, \.{\\4}, \.{\\5}, \.{\\6}, \.{\\7}, \.{\\8} +corresponding respectively to +|indent|, |outdent|, |opt|, |backup|, |break_space|, |force|, +|big_force| and |preproc_line|. +However, a sequence of consecutive `\.\ ', |break_space|, +|force|, and/or |big_force| tokens is first replaced by a single token +(the maximum of the given ones). + +The token |math_rel| will be translated into +\.{\\MRL\{}, and it will get a matching \.\} later. +Other control sequences in the \TEX/ output will be +`\.{\\\\\{}$\,\ldots\,$\.\}' +surrounding identifiers, `\.{\\\&\{}$\,\ldots\,$\.\}' surrounding +reserved words, `\.{\\.\{}$\,\ldots\,$\.\}' surrounding strings, +`\.{\\C\{}$\,\ldots\,$\.\}$\,$|force|' surrounding comments, and +`\.{\\X$n$:}$\,\ldots\,$\.{\\X}' surrounding section names, where +|n| is the section number. + +@d math_rel 0206 +@d big_cancel 0210 /* like |cancel|, also overrides spaces */ +@d cancel 0211 /* overrides |backup|, |break_space|, |force|, |big_force| */ +@d indent 0212 /* one more tab (\.{\\1}) */ +@d outdent 0213 /* one less tab (\.{\\2}) */ +@d opt 0214 /* optional break in mid-statement (\.{\\3}) */ +@d backup 0215 /* stick out one unit to the left (\.{\\4}) */ +@d break_space 0216 /* optional break between statements (\.{\\5}) */ +@d force 0217 /* forced break between statements (\.{\\6}) */ +@d big_force 0220 /* forced break with additional space (\.{\\7}) */ +@d preproc_line 0221 /* begin line without indentation (\.{\\8}) */ +@^high-bit character handling@> +@d quoted_char 0222 + /* introduces a character token in the range |0200|--|0377| */ +@d end_translation 0223 /* special sentinel token at end of list */ +@d inserted 0224 /* sentinel to mark translations of inserts */ +@d qualifier 0225 /* introduces an explicit namespace qualifier */ + +@ The raw input is converted into scraps according to the following table, +which gives category codes followed by the translations. +\def\stars {\.{**}}% +The symbol `\stars' stands for `\.{\\\&\{{\rm identifier}\}}', +i.e., the identifier itself treated as a reserved word. +The right-hand column is the so-called |mathness|, which is explained +further below. + +An identifier |c| of length 1 is translated as \.{\\\v c} instead of +as \.{\\\\\{c\}}. An identifier \.{CAPS} in all caps is translated as +\.{\\.\{CAPS\}} instead of as \.{\\\\\{CAPS\}}. An identifier that has +become a reserved word via |typedef| is translated with \.{\\\&} replacing +\.{\\\\} and |raw_int| replacing |exp|. + +A string of length greater than 20 is broken into pieces of size at most~20 +with discretionary breaks in between. + +\yskip\halign{\quad#\hfil&\quad#\hfil&\quad\hfil#\hfil\cr +\.{!=}&|binop|: \.{\\I}&yes\cr +\.{<=}&|binop|: \.{\\Z}&yes\cr +\.{>=}&|binop|: \.{\\G}&yes\cr +\.{==}&|binop|: \.{\\E}&yes\cr +\.{\&\&}&|binop|: \.{\\W}&yes\cr +\.{\v\v}&|binop|: \.{\\V}&yes\cr +\.{++}&|unop|: \.{\\PP}&yes\cr +\.{--}&|unop|: \.{\\MM}&yes\cr +\.{->}&|binop|: \.{\\MG}&yes\cr +\.{>>}&|binop|: \.{\\GG}&yes\cr +\.{<<}&|binop|: \.{\\LL}&yes\cr +\.{::}&|colcol|: \.{\\DC}&maybe\cr +\.{.*}&|binop|: \.{\\PA}&yes\cr +\.{->*}&|binop|: \.{\\MGA}&yes\cr +\.{...}&|raw_int|: \.{\\,\\ldots\\,}&yes\cr +\."string\."&|exp|: \.{\\.\{}string with special characters quoted\.\}&maybe\cr +\.{@@=}string\.{@@>}&|exp|: \.{\\vb\{}string with special characters + quoted\.\}&maybe\cr +\.{@@'7'}&|exp|: \.{\\.\{@@'7'\}}&maybe\cr +\.{077} or \.{\\77}&|exp|: \.{\\T\{\\\~77\}}&maybe\cr +\.{0x7f}&|exp|: \.{\\T\{\\\^7f\}}&maybe\cr +\.{77}&|exp|: \.{\\T\{77\}}&maybe\cr +\.{77L}&|exp|: \.{\\T\{77\\\$L\}}&maybe\cr +\.{0.1E5}&|exp|: \.{\\T\{0.1\\\_5\}}&maybe\cr +\.+&|ubinop|: \.+&yes\cr +\.-&|ubinop|: \.-&yes\cr +\.*&|raw_ubin|: \.*&yes\cr +\./&|binop|: \./&yes\cr +\.<&|prelangle|: \.{\\langle}&yes\cr +\.=&|binop|: \.{\\K}&yes\cr +\.>&|prerangle|: \.{\\rangle}&yes\cr +\..&|binop|: \..&yes\cr +\.{\v}&|binop|: \.{\\OR}&yes\cr +\.\^&|binop|: \.{\\XOR}&yes\cr +\.\%&|binop|: \.{\\MOD}&yes\cr +\.?&|question|: \.{\\?}&yes\cr +\.!&|unop|: \.{\\R}&yes\cr +\.\~&|unop|: \.{\\CM}&yes\cr +\.\&&|raw_ubin|: \.{\\AND}&yes\cr +\.(&|lpar|: \.(&maybe\cr +\.[&|lpar|: \.[&maybe\cr +\.)&|rpar|: \.)&maybe\cr +\.]&|rpar|: \.]&maybe\cr +\.\{&|lbrace|: \.\{&yes\cr +\.\}&|lbrace|: \.\}&yes\cr +\.,&|comma|: \.,&yes\cr +\.;&|semi|: \.;&maybe\cr +\.:&|colon|: \.:&no\cr +\.\# (within line)&|ubinop|: \.{\\\#}&yes\cr +\.\# (at beginning)&|lproc|: |force| |preproc_line| \.{\\\#}&no\cr +end of \.\# line&|rproc|: |force|&no\cr +identifier&|exp|: \.{\\\\\{}identifier with underlines and + dollar signs quoted\.\}&maybe\cr +\.{and}&|alfop|: \stars&yes\cr +\.{and\_eq}&|alfop|: \stars&yes\cr +\.{asm}&|sizeof_like|: \stars&maybe\cr +\.{auto}&|int_like|: \stars&maybe\cr +\.{bitand}&|alfop|: \stars&yes\cr +\.{bitor}&|alfop|: \stars&yes\cr +\.{bool}&|raw_int|: \stars&maybe\cr +\.{break}&|case_like|: \stars&maybe\cr +\.{case}&|case_like|: \stars&maybe\cr +\.{catch}&|catch_like|: \stars&maybe\cr +\.{char}&|raw_int|: \stars&maybe\cr +\.{class}&|struct_like|: \stars&maybe\cr +\.{clock\_t}&|raw_int|: \stars&maybe\cr +\.{compl}&|alfop|: \stars&yes\cr +\.{const}&|const_like|: \stars&maybe\cr +\.{const\_cast}&|raw_int|: \stars&maybe\cr +\.{continue}&|case_like|: \stars&maybe\cr +\.{default}&|case_like|: \stars&maybe\cr +\.{define}&|define_like|: \stars&maybe\cr +\.{defined}&|sizeof_like|: \stars&maybe\cr +\.{delete}&|delete_like|: \stars&maybe\cr +\.{div\_t}&|raw_int|: \stars&maybe\cr +\.{do}&|do_like|: \stars&maybe\cr +\.{double}&|raw_int|: \stars&maybe\cr +\.{dynamic\_cast}&|raw_int|: \stars&maybe\cr +\.{elif}&|if_like|: \stars&maybe\cr +\.{else}&|else_like|: \stars&maybe\cr +\.{endif}&|if_like|: \stars&maybe\cr +\.{enum}&|struct_like|: \stars&maybe\cr +\.{error}&|if_like|: \stars&maybe\cr +\.{explicit}&|int_like|: \stars&maybe\cr +\.{export}&|int_like|: \stars&maybe\cr +\.{extern}&|int_like|: \stars&maybe\cr +\.{FILE}&|raw_int|: \stars&maybe\cr +\.{float}&|raw_int|: \stars&maybe\cr +\.{for}&|for_like|: \stars&maybe\cr +\.{fpos\_t}&|raw_int|: \stars&maybe\cr +\.{friend}&|int_like|: \stars&maybe\cr +\.{goto}&|case_like|: \stars&maybe\cr +\.{if}&|if_like|: \stars&maybe\cr +\.{ifdef}&|if_like|: \stars&maybe\cr +\.{ifndef}&|if_like|: \stars&maybe\cr +\.{include}&|if_like|: \stars&maybe\cr +\.{inline}&|int_like|: \stars&maybe\cr +\.{int}&|raw_int|: \stars&maybe\cr +\.{jmp\_buf}&|raw_int|: \stars&maybe\cr +\.{ldiv\_t}&|raw_int|: \stars&maybe\cr +\.{line}&|if_like|: \stars&maybe\cr +\.{long}&|raw_int|: \stars&maybe\cr +\.{make\_pair}&|ftemplate|: \.{\\\\\{make\\\_pair\}}&maybe\cr +\.{mutable}&|int_like|: \stars&maybe\cr +\.{namespace}&|struct_like|: \stars&maybe\cr +\.{new}&|new_like|: \stars&maybe\cr +\.{not}&|alfop|: \stars&yes\cr +\.{not\_eq}&|alfop|: \stars&yes\cr +\.{NULL}&|exp|: \.{\\NULL}&yes\cr +\.{offsetof}&|raw_int|: \stars&maybe\cr +\.{operator}&|operator_like|: \stars&maybe\cr +\.{or}&|alfop|: \stars&yes\cr +\.{or\_eq}&|alfop|: \stars&yes\cr +\.{pragma}&|if_like|: \stars&maybe\cr +\.{private}&|public_like|: \stars&maybe\cr +\.{protected}&|public_like|: \stars&maybe\cr +\.{ptrdiff\_t}&|raw_int|: \stars&maybe\cr +\.{public}&|public_like|: \stars&maybe\cr +\.{register}&|int_like|: \stars&maybe\cr +\.{reinterpret\_cast}&|raw_int|: \stars&maybe\cr +\.{return}&|case_like|: \stars&maybe\cr +\.{short}&|raw_int|: \stars&maybe\cr +\.{sig\_atomic\_t}&|raw_int|: \stars&maybe\cr +\.{signed}&|raw_int|: \stars&maybe\cr +\.{size\_t}&|raw_int|: \stars&maybe\cr +\.{sizeof}&|sizeof_like|: \stars&maybe\cr +\.{static}&|int_like|: \stars&maybe\cr +\.{static\_cast}&|raw_int|: \stars&maybe\cr +\.{struct}&|struct_like|: \stars&maybe\cr +\.{switch}&|for_like|: \stars&maybe\cr +\.{template}&|template_like|: \stars&maybe\cr +\.{TeX}&|exp|: \.{\\TeX}&yes\cr +\.{this}&|exp|: \.{\\this}&yes\cr +\.{throw}&|case_like|: \stars&maybe\cr +\.{time\_t}&|raw_int|: \stars&maybe\cr +\.{try}&|else_like|: \stars&maybe\cr +\.{typedef}&|typedef_like|: \stars&maybe\cr +\.{typeid}&|raw_int|: \stars&maybe\cr +\.{typename}&|struct_like|: \stars&maybe\cr +\.{undef}&|if_like|: \stars&maybe\cr +\.{union}&|struct_like|: \stars&maybe\cr +\.{unsigned}&|raw_int|: \stars&maybe\cr +\.{using}&|int_like|: \stars&maybe\cr +\.{va\_dcl}&|decl|: \stars&maybe\cr +\.{va\_list}&|raw_int|: \stars&maybe\cr +\.{virtual}&|int_like|: \stars&maybe\cr +\.{void}&|raw_int|: \stars&maybe\cr +\.{volatile}&|const_like|: \stars&maybe\cr +\.{wchar\_t}&|raw_int|: \stars&maybe\cr +\.{while}&|for_like|: \stars&maybe\cr +\.{xor}&|alfop|: \stars&yes\cr +\.{xor\_eq}&|alfop|: \stars&yes\cr +\.{@@,}&|insert|: \.{\\,}&maybe\cr +\.{@@\v}&|insert|: |opt| \.0&maybe\cr +\.{@@/}&|insert|: |force|&no\cr +\.{@@\#}&|insert|: |big_force|&no\cr +\.{@@+}&|insert|: |big_cancel| \.{\{\}} |break_space| + \.{\{\}} |big_cancel|&no\cr +\.{@@;}&|semi|: &maybe\cr +\.{@@[@q]@>}&|begin_arg|: &maybe\cr +\.{@q[@>@@]}&|end_arg|: &maybe\cr +\.{@@\&}&|insert|: \.{\\J}&maybe\cr +\.{@@h}&|insert|: |force| \.{\\ATH} |force|&no\cr +\.{@@<}\thinspace section name\thinspace\.{@@>}&|section_scrap|: + \.{\\X}$n$\.:translated section name\.{\\X}&maybe\cr +\.{@@(@q)@>}\thinspace section name\thinspace\.{@@>}&|section_scrap|: + \.{\\X}$n$\.{:\\.\{}section name with special characters + quoted\.{\ \}\\X}&maybe\cr +\.{/*}comment\.{*/}&|insert|: |cancel| + \.{\\C\{}translated comment\.\} |force|&no\cr +\.{//}comment&|insert|: |cancel| + \.{\\SHC\{}translated comment\.\} |force|&no\cr +} + +\smallskip +The construction \.{@@t}\thinspace stuff\/\thinspace\.{@@>} contributes +\.{\\hbox\{}\thinspace stuff\/\thinspace\.\} to the following scrap. + +@i prod.w + +@* Implementing the productions. +More specifically, a scrap is a structure consisting of a category +|cat| and a |text_pointer| |trans|, which points to the translation in +|tok_start|. When \CEE/ text is to be processed with the grammar above, +we form an array |scrap_info| containing the initial scraps. +Our production rules have the nice property that the right-hand side is never +longer than the left-hand side. Therefore it is convenient to use sequential +allocation for the current sequence of scraps. Five pointers are used to +manage the parsing: + +\yskip\hang |pp| is a pointer into |scrap_info|. We will try to match +the category codes |pp->cat,@,@,(pp+1)->cat|$,\,\,\ldots\,$ +to the left-hand sides of productions. + +\yskip\hang |scrap_base|, |lo_ptr|, |hi_ptr|, and |scrap_ptr| are such that +the current sequence of scraps appears in positions |scrap_base| through +|lo_ptr| and |hi_ptr| through |scrap_ptr|, inclusive, in the |cat| and +|trans| arrays. Scraps located between |scrap_base| and |lo_ptr| have +been examined, while those in positions |>=hi_ptr| have not yet been +looked at by the parsing process. + +\yskip\noindent Initially |scrap_ptr| is set to the position of the final +scrap to be parsed, and it doesn't change its value. The parsing process +makes sure that |lo_ptr>=pp+3|, since productions have as many as four terms, +by moving scraps from |hi_ptr| to |lo_ptr|. If there are +fewer than |pp+3| scraps left, the positions up to |pp+3| are filled with +blanks that will not match in any productions. Parsing stops when +|pp==lo_ptr+1| and |hi_ptr==scrap_ptr+1|. + +Since the |scrap| structure will later be used for other purposes, we +declare its second element as a union. + +@<Type...@>= +typedef struct { + eight_bits cat; + eight_bits mathness; + union { + text_pointer Trans; + @<Rest of |trans_plus| union@>@; + } trans_plus; +} scrap; +typedef scrap *scrap_pointer; + +@ @d trans trans_plus.Trans /* translation texts of scraps */ + +@<Global...@>= +scrap scrap_info[max_scraps]; /* memory array for scraps */ +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 */ +scrap_pointer scrap_ptr; /* ending of the current scrap sequence */ +scrap_pointer lo_ptr; /* last scrap that has been examined */ +scrap_pointer hi_ptr; /* first scrap that has not been examined */ +scrap_pointer max_scr_ptr; /* largest value assumed by |scrap_ptr| */ + +@ @<Set init...@>= +scrap_base=scrap_info+1; +max_scr_ptr=scrap_ptr=scrap_info; + +@ Token lists in |@!tok_mem| are composed of the following kinds of +items for \TEX/ output. + +\yskip\item{$\bullet$}Character codes and special codes like |force| and +|math_rel| represent themselves; + +\item{$\bullet$}|id_flag+p| represents \.{\\\\\{{\rm identifier $p$}\}}; + +\item{$\bullet$}|res_flag+p| represents \.{\\\&\{{\rm identifier $p$}\}}; + +\item{$\bullet$}|section_flag+p| represents section name |p|; + +\item{$\bullet$}|tok_flag+p| represents token list number |p|; + +\item{$\bullet$}|inner_tok_flag+p| represents token list number |p|, to be +translated without line-break controls. + +@d id_flag 10240 /* signifies an identifier */ +@d res_flag 2*id_flag /* signifies a reserved word */ +@d section_flag 3*id_flag /* signifies a section name */ +@d tok_flag 4*id_flag /* signifies a token list */ +@d inner_tok_flag 5*id_flag /* signifies a token list in `\pb' */ + +@c +void +print_text(p) /* prints a token list for debugging; not used in |main| */ +text_pointer p; +{ + token_pointer j; /* index into |tok_mem| */ + sixteen_bits r; /* remainder of token after the flag has been stripped off */ + if (p>=text_ptr) printf("BAD"); + else for (j=*p; j<*(p+1); j++) { + r=*j%id_flag; + switch (*j/id_flag) { + case 1: printf("\\\\{"@q}@>); print_id((name_dir+r)); printf(@q{@>"}"); + break; /* |id_flag| */ + case 2: printf("\\&{"@q}@>); print_id((name_dir+r)); printf(@q{@>"}"); + break; /* |res_flag| */ + case 3: printf("<"); print_section_name((name_dir+r)); printf(">"); + break; /* |section_flag| */ + case 4: printf("[[%d]]",r); break; /* |tok_flag| */ + case 5: printf("|[[%d]]|",r); break; /* |inner_tok_flag| */ + default: @<Print token |r| in symbolic form@>; + } + } + fflush(stdout); +} + +@ @<Print token |r|...@>= +switch (r) { + case math_rel: printf("\\mathrel{"@q}@>); break; + case big_cancel: printf("[ccancel]"); break; + case cancel: printf("[cancel]"); break; + case indent: printf("[indent]"); break; + case outdent: printf("[outdent]"); break; + case backup: printf("[backup]"); break; + case opt: printf("[opt]"); break; + case break_space: printf("[break]"); break; + case force: printf("[force]"); break; + case big_force: printf("[fforce]"); break; + case preproc_line: printf("[preproc]"); break; + case quoted_char: j++; printf("[%o]",(unsigned)*j); break; + case end_translation: printf("[quit]"); break; + case inserted: printf("[inserted]"); break; + default: putxchar(r); +} + +@ The production rules listed above are embedded directly into \.{CWEAVE}, +since it is easier to do this than to write an interpretive system +that would handle production systems in general. Several macros are defined +here so that the program for each production is fairly short. + +All of our productions conform to the general notion that some |k| +consecutive scraps starting at some position |j| are to be replaced by a +single scrap of some category |c| whose translation is composed from the +translations of the disappearing scraps. After this production has been +applied, the production pointer |pp| should change by an amount |d|. Such +a production can be represented by the quadruple |(j,k,c,d)|. For example, +the production `|exp@,comma@,exp| $\RA$ |exp|' would be represented by +`|(pp,3,exp,-2)|'; in this case the pointer |pp| should decrease by 2 +after the production has been applied, because some productions with +|exp| in their second or third positions might now match, +but no productions have +|exp| in the fourth position of their left-hand sides. Note that +the value of |d| is determined by the whole collection of productions, not +by an individual one. +The determination of |d| has been +done by hand in each case, based on the full set of productions but not on +the grammar of \CEE/ or on the rules for constructing the initial +scraps. + +We also attach a serial number to each production, so that additional +information is available when debugging. For example, the program below +contains the statement `|reduce(pp,3,exp,-2,4)|' when it implements +the production just mentioned. + +Before calling |reduce|, the program should have appended the tokens of +the new translation to the |tok_mem| array. We commonly want to append +copies of several existing translations, and macros are defined to +simplify these common cases. For example, \\{app2}|(pp)| will append the +translations of two consecutive scraps, |pp->trans| and |(pp+1)->trans|, to +the current token list. If the entire new translation is formed in this +way, we write `|squash(j,k,c,d,n)|' instead of `|reduce(j,k,c,d,n)|'. For +example, `|squash(pp,3,exp,-2,3)|' is an abbreviation for `\\{app3}|(pp); +reduce(pp,3,exp,-2,3)|'. + +A couple more words of explanation: +Both |big_app| and |app| append a token (while |big_app1| to |big_app4| +append the specified number of scrap translations) to the current token list. +The difference between |big_app| and |app| is simply that |big_app| +checks whether there can be a conflict between math and non-math +tokens, and intercalates a `\.{\$}' token if necessary. When in +doubt what to use, use |big_app|. + +The |mathness| is an attribute of scraps that says whether they are +to be printed in a math mode context or not. It is separate from the +``part of speech'' (the |cat|) because to make each |cat| have +a fixed |mathness| (as in the original \.{WEAVE}) would multiply the +number of necessary production rules. + +The low two bits (i.e. |mathness % 4|) control the left boundary. +(We need two bits because we allow cases |yes_math|, |no_math| and +|maybe_math|, which can go either way.) +The next two bits (i.e. |mathness / 4|) control the right boundary. +If we combine two scraps and the right boundary of the first has +a different mathness from the left boundary of the second, we +insert a \.{\$} in between. Similarly, if at printing time some +irreducible scrap has a |yes_math| boundary the scrap gets preceded +or followed by a \.{\$}. The left boundary is |maybe_math| if and +only if the right boundary is. + +The code below is an exact translation of the production rules into +\CEE/, using such macros, and the reader should have no difficulty +understanding the format by comparing the code with the symbolic +productions as they were listed earlier. + +@d no_math 2 /* should be in horizontal mode */ +@d yes_math 1 /* should be in math mode */ +@d maybe_math 0 /* works in either horizontal or math mode */ +@d big_app2(a) big_app1(a);big_app1(a+1) +@d big_app3(a) big_app2(a);big_app1(a+2) +@d big_app4(a) big_app3(a);big_app1(a+3) +@d app(a) *(tok_ptr++)=a +@d app1(a) *(tok_ptr++)=tok_flag+(int)((a)->trans-tok_start) + +@<Global...@>= +int cur_mathness, init_mathness; + +@ @c +void +app_str(s) +char *s; +{ + while (*s) app_tok(*(s++)); +} + +void +big_app(a) +token a; +{ + if (a==' ' || (a>=big_cancel && a<=big_force)) /* non-math token */ { + if (cur_mathness==maybe_math) init_mathness=no_math; + else if (cur_mathness==yes_math) app_str("{}$"); + cur_mathness=no_math; + } + else { + if (cur_mathness==maybe_math) init_mathness=yes_math; + else if (cur_mathness==no_math) app_str("${}"); + cur_mathness=yes_math; + } + app(a); +} + +void +big_app1(a) +scrap_pointer a; +{ + switch (a->mathness % 4) { /* left boundary */ + case (no_math): + if (cur_mathness==maybe_math) init_mathness=no_math; + else if (cur_mathness==yes_math) app_str("{}$"); + cur_mathness=a->mathness / 4; /* right boundary */ + break; + case (yes_math): + if (cur_mathness==maybe_math) init_mathness=yes_math; + else if (cur_mathness==no_math) app_str("${}"); + cur_mathness=a->mathness / 4; /* right boundary */ + break; + case (maybe_math): /* no changes */ break; + } + app(tok_flag+(int)((a)->trans-tok_start)); +} + +@ Let us consider the big switch for productions now, before looking +at its context. We want to design the program so that this switch +works, so we might as well not keep ourselves in suspense about exactly what +code needs to be provided with a proper environment. + +@d cat1 (pp+1)->cat +@d cat2 (pp+2)->cat +@d cat3 (pp+3)->cat +@d lhs_not_simple (pp->cat!=public_like + && pp->cat!=semi + && pp->cat!=prelangle + && pp->cat!=prerangle + && pp->cat!=template_like + && pp->cat!=new_like + && pp->cat!=new_exp + && pp->cat!=ftemplate + && pp->cat!=raw_ubin + && pp->cat!=const_like + && pp->cat!=raw_int + && pp->cat!=operator_like) + /* not a production with left side length 1 */ + +@<Match a production at |pp|, or increase |pp| if there is no match@>= { + if (cat1==end_arg && lhs_not_simple) + if (pp->cat==begin_arg) squash(pp,2,exp,-2,124); + else squash(pp,2,end_arg,-1,125); + else if (cat1==insert) squash(pp,2,pp->cat,-2,0); + else if (cat2==insert) squash(pp+1,2,(pp+1)->cat,-1,0); + else if (cat3==insert) squash(pp+2,2,(pp+2)->cat,0,0); + else + switch (pp->cat) { + case exp: @<Cases for |exp|@>; @+break; + case lpar: @<Cases for |lpar|@>; @+break; + case unop: @<Cases for |unop|@>; @+break; + case ubinop: @<Cases for |ubinop|@>; @+break; + case binop: @<Cases for |binop|@>; @+break; + case cast: @<Cases for |cast|@>; @+break; + case sizeof_like: @<Cases for |sizeof_like|@>; @+break; + case int_like: @<Cases for |int_like|@>; @+break; + case public_like: @<Cases for |public_like|@>; @+break; + case colcol: @<Cases for |colcol|@>; @+break; + case decl_head: @<Cases for |decl_head|@>; @+break; + case decl: @<Cases for |decl|@>; @+break; + case base: @<Cases for |base|@>; @+break; + case struct_like: @<Cases for |struct_like|@>; @+break; + case struct_head: @<Cases for |struct_head|@>; @+break; + case fn_decl: @<Cases for |fn_decl|@>; @+break; + case function: @<Cases for |function|@>; @+break; + case lbrace: @<Cases for |lbrace|@>; @+break; + case if_like: @<Cases for |if_like|@>; @+break; + case else_like: @<Cases for |else_like|@>; @+break; + case else_head: @<Cases for |else_head|@>; @+break; + case if_clause: @<Cases for |if_clause|@>; @+break; + case if_head: @<Cases for |if_head|@>; @+break; + case do_like: @<Cases for |do_like|@>; @+break; + case case_like: @<Cases for |case_like|@>; @+break; + case catch_like: @<Cases for |catch_like|@>; @+break; + case tag: @<Cases for |tag|@>; @+break; + case stmt: @<Cases for |stmt|@>; @+break; + case semi: @<Cases for |semi|@>; @+break; + case lproc: @<Cases for |lproc|@>; @+break; + case section_scrap: @<Cases for |section_scrap|@>; @+break; + case insert: @<Cases for |insert|@>; @+break; + case prelangle: @<Cases for |prelangle|@>; @+break; + case prerangle: @<Cases for |prerangle|@>; @+break; + case langle: @<Cases for |langle|@>; @+break; + case template_like: @<Cases for |template_like|@>; @+break; + case new_like: @<Cases for |new_like|@>; @+break; + case new_exp: @<Cases for |new_exp|@>; @+break; + case ftemplate: @<Cases for |ftemplate|@>; @+break; + case for_like: @<Cases for |for_like|@>; @+break; + case raw_ubin: @<Cases for |raw_ubin|@>; @+break; + case const_like: @<Cases for |const_like|@>; @+break; + case raw_int: @<Cases for |raw_int|@>; @+break; + case operator_like: @<Cases for |operator_like|@>; @+break; + case typedef_like: @<Cases for |typedef_like|@>; @+break; + case delete_like: @<Cases for |delete_like|@>; @+break; + case question: @<Cases for |question|@>; @+break; + } + pp++; /* if no match was found, we move to the right */ +} + +@ In \CEE/, new specifier names can be defined via |typedef|, and we want +to make the parser recognize future occurrences of the identifier thus +defined as specifiers. This is done by the procedure |make_reserved|, +which changes the |ilk| of the relevant identifier. + +We first need a procedure to recursively seek the first +identifier in a token list, because the identifier might +be enclosed in parentheses, as when one defines a function +returning a pointer. + +If the first identifier found is a keyword like `\&{case}', we +return the special value |case_found|; this prevents underlining +of identifiers in case labels. + +If the first identifier is the keyword `\&{operator}', we give up; +users who want to index definitions of overloaded \CPLUSPLUS/ operators +should say, for example, `\.{@@!@@\^\\\&\{operator\} \$+\{=\}\$@@>}' (or, +more properly alphebetized, +`\.{@@!@@:operator+=\}\{\\\&\{operator\} \$+\{=\}\$@@>}'). + +@d no_ident_found (token_pointer)0 /* distinct from any identifier token */ +@d case_found (token_pointer)1 /* likewise */ +@d operator_found (token_pointer)2 /* likewise */ + +@c +token_pointer +find_first_ident(p) +text_pointer p; +{ + token_pointer q; /* token to be returned */ + token_pointer j; /* token being looked at */ + sixteen_bits r; /* remainder of token after the flag has been stripped off */ + if (p>=text_ptr) confusion("find_first_ident"); + for (j=*p; j<*(p+1); j++) { + r=*j%id_flag; + switch (*j/id_flag) { + case 2: /* |res_flag| */ + if (name_dir[r].ilk==case_like) return case_found; + if (name_dir[r].ilk==operator_like) return operator_found; + if (name_dir[r].ilk!=raw_int) break; + case 1: return j; + case 4: case 5: /* |tok_flag| or |inner_tok_flag| */ + if ((q=find_first_ident(tok_start+r))!=no_ident_found) + return q; + default: ; /* char, |section_flag|, fall thru: move on to next token */ + if (*j==inserted) return no_ident_found; /* ignore inserts */ + else if (*j==qualifier) j++; /* bypass namespace qualifier */ + } + } + return no_ident_found; +} + +@ The scraps currently being parsed must be inspected for any +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) { + if (**(p->trans)==tok_value) { + p->cat=raw_int; + **(p->trans)=tok_value%id_flag+res_flag; + } + } + } + (name_dir+(sixteen_bits)(tok_value%id_flag))->ilk=raw_int; + *tok_loc=tok_value%id_flag+res_flag; +} + +@ In the following situations we want to mark the occurrence of +an identifier as a definition: when |make_reserved| is just about to be +used; after a specifier, as in |char **argv|; +before a colon, as in \\{found}:; and in the declaration of a function, +as in \\{main}()$\{\ldots;\}$. This is accomplished by the invocation +of |make_underlined| at appropriate times. Notice that, in the declaration +of a function, we find out that the identifier is being defined only after +it has been swallowed up by an |exp|. + +@c +void +make_underlined(p) +/* 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; + underline_xref(*tok_loc%id_flag+name_dir); +} + +@ We cannot use |new_xref| to underline a cross-reference at this point +because this would just make a new cross-reference at the end of the list. +We actually have to search through the list for the existing +cross-reference. + +@<Predecl...@>= +void underline_xref(); + +@ @c +void +underline_xref(p) +name_pointer p; +{ + xref_pointer q=(xref_pointer)p->xref; /* pointer to cross-reference being examined */ + xref_pointer r; /* temporary pointer for permuting cross-references */ + sixteen_bits m; /* cross-reference value to be installed */ + sixteen_bits n; /* cross-reference value being examined */ + if (no_xref) return; + m=section_count+xref_switch; + while (q != xmem) { + n=q->num; + if (n==m) return; + else if (m==n+def_flag) { + q->num=m; return; + } + else if (n>=def_flag && n<m) break; + q=q->xlink; + } + @<Insert new cross-reference at |q|, not at beginning of list@>; +} + +@ We get to this section only when the identifier is one letter long, +so it didn't get a non-underlined entry during phase one. But it may +have got some explicitly underlined entries in later sections, so in order +to preserve the numerical order of the entries in the index, we have +to insert the new cross-reference not at the beginning of the list +(namely, at |p->xref|), but rather right before |q|. + +@<Insert new cross-reference at |q|...@>= + append_xref(0); /* this number doesn't matter */ + xref_ptr->xlink=(xref_pointer)p->xref; r=xref_ptr; + p->xref=(char*)xref_ptr; + while (r->xlink!=q) {r->num=r->xlink->num; r=r->xlink;} + r->num=m; /* everything from |q| on is left undisturbed */ + +@ 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 +to be performed, followed by |goto found|. + +@<Cases for |exp|@>= +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); +else if ((cat1==binop || cat1==ubinop) && cat2==exp) + squash(pp,3,exp,-2,3); +else if (cat1==comma && cat2==exp) { + big_app2(pp); + app(opt); app('9'); big_app1(pp+2); reduce(pp,3,exp,-2,4); +} +else if (cat1==lpar && cat2==rpar && cat3==colon) squash(pp+3,1,base,0,5); +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)) { + big_app1(pp+2); big_app(' '); big_app1(pp+3); reduce(pp+2,2,rpar,0,9); +} +else if (cat1==cast && (cat2==const_like || cat2==case_like)) { + big_app1(pp+1); big_app(' '); big_app1(pp+2); reduce(pp+1,2,cast,0,9); +} +else if (cat1==exp || cat1==cast) squash(pp,2,exp,-2,10); + +@ @<Cases for |lpar|@>= +if ((cat1==exp||cat1==ubinop) && cat2==rpar) squash(pp,3,exp,-2,11); +else if (cat1==rpar) { + big_app1(pp); app('\\'); app(','); big_app1(pp+1); +@.\\,@> + reduce(pp,2,exp,-2,12); +} +else if ((cat1==decl_head || cat1==int_like || cat1==cast) && cat2==rpar) + squash(pp,3,cast,-2,13); +else if ((cat1==decl_head || cat1==int_like || cat1==exp) && cat2==comma) { + big_app3(pp); app(opt); app('9'); reduce(pp,3,lpar,-1,14); +} +else if (cat1==stmt || cat1==decl) { + big_app2(pp); big_app(' '); reduce(pp,2,lpar,-1,15); +} + +@ @<Cases for |unop|@>= +if (cat1==exp || cat1==int_like) squash(pp,2,exp,-2,16); + +@ @<Cases for |ubinop|@>= +if (cat1==cast && cat2==rpar) { + big_app('{'); big_app1(pp); big_app('}'); big_app1(pp+1); + reduce(pp,2,cast,-2,17); +} +else if (cat1==exp || cat1==int_like) { + big_app('{'); big_app1(pp); big_app('}'); big_app1(pp+1); + reduce(pp,2,cat1,-2,18); +} +else if (cat1==binop) { + big_app(math_rel); big_app1(pp); big_app('{'); big_app1(pp+1); big_app('}'); + big_app('}'); reduce(pp,2,binop,-1,19); +} + +@ @<Cases for |binop|@>= +if (cat1==binop) { + big_app(math_rel); big_app('{'); big_app1(pp); big_app('}'); + big_app('{'); big_app1(pp+1); big_app('}'); + big_app('}'); reduce(pp,2,binop,-1,20); +} + +@ @<Cases for |cast|@>= +if (cat1==lpar) squash(pp,2,lpar,-1,21); +else if (cat1==exp) { + big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,exp,-2,21); +} +else if (cat1==semi) squash(pp,1,exp,-2,22); + +@ @<Cases for |sizeof_like|@>= +if (cat1==cast) squash(pp,2,exp,-2,23); +else if (cat1==exp) { + big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,exp,-2,24); +} + +@ @<Cases for |int_like|@>= +if (cat1==int_like|| cat1==struct_like) { + big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,cat1,-2,25); +} +else if (cat1==exp && (cat2==raw_int||cat2==struct_like)) + squash(pp,2,int_like,-2,26); +else if (cat1==exp || cat1==ubinop || cat1==colon) { + big_app1(pp); big_app(' '); reduce(pp,1,decl_head,-1,27); +} +else if (cat1==semi || cat1==binop) squash(pp,1,decl_head,0,28); + +@ @<Cases for |public_like|@>= +if (cat1==colon) squash(pp,2,tag,-1,29); +else squash(pp,1,int_like,-2,30); + +@ @<Cases for |colcol|@>= +if (cat1==exp||cat1==int_like) { + app(qualifier); squash(pp,2,cat1,-2,31); +}@+else if (cat1==colcol) squash(pp,2,colcol,-1,32); + +@ @<Cases for |decl_head|@>= +if (cat1==comma) { + big_app2(pp); big_app(' '); reduce(pp,2,decl_head,-1,33); +} +else if (cat1==ubinop) { + big_app1(pp); big_app('{'); big_app1(pp+1); big_app('}'); + 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)) + squash(pp,3,decl_head,-1,36); +else if (cat1==cast) squash(pp,2,decl_head,-1,37); +else if (cat1==lbrace || cat1==int_like || cat1==decl) { + big_app1(pp); big_app(indent); app(indent); reduce(pp,1,fn_decl,0,38); +} +else if (cat1==semi) squash(pp,2,decl,-1,39); + +@ @<Cases for |decl|@>= +if (cat1==decl) { + big_app1(pp); big_app(force); big_app1(pp+1); + reduce(pp,2,decl,-1,40); +} +else if (cat1==stmt || cat1==function) { + big_app1(pp); big_app(big_force); + big_app1(pp+1); reduce(pp,2,cat1,-1,41); +} + +@ @<Cases for |base|@>= +if (cat1==int_like || cat1==exp) { + if (cat2==comma) { + big_app1(pp); big_app(' '); big_app2(pp+1); + app(opt); app('9'); reduce(pp,3,base,0,42); + } + else if (cat2==lbrace) { + big_app1(pp); big_app(' '); big_app1(pp+1); big_app(' '); big_app1(pp+2); + reduce(pp,3,lbrace,-2,43); + } +} + +@ @<Cases for |struct_like|@>= +if (cat1==lbrace) { + big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,struct_head,0,44); +} +else if (cat1==exp||cat1==int_like) { + if (cat2==lbrace || cat2==semi) { + make_underlined(pp+1); make_reserved(pp+1); + big_app1(pp); big_app(' '); big_app1(pp+1); + if (cat2==semi) reduce(pp,2,decl_head,0,45); + else { + big_app(' '); big_app1(pp+2);reduce(pp,3,struct_head,0,46); + } + } + else if (cat2==colon) squash(pp+2,1,base,2,47); + else if (cat2!=base) { + big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,int_like,-2,48); + } +} + +@ @<Cases for |struct_head|@>= +if ((cat1==decl || cat1==stmt || cat1==function) && cat2==rbrace) { + big_app1(pp); big_app(indent); big_app(force); big_app1(pp+1); + big_app(outdent); big_app(force); big_app1(pp+2); + reduce(pp,3,int_like,-2,49); +} +else if (cat1==rbrace) { + big_app1(pp); app_str("\\,"); big_app1(pp+1); +@.\\,@> + reduce(pp,2,int_like,-2,50); +} + +@ @<Cases for |fn_decl|@>= +if (cat1==decl) { + big_app1(pp); big_app(force); big_app1(pp+1); reduce(pp,2,fn_decl,0,51); +} +else if (cat1==stmt) { + big_app1(pp); app(outdent); app(outdent); big_app(force); + big_app1(pp+1); reduce(pp,2,function,-1,52); +} + +@ @<Cases for |function|@>= +if (cat1==function || cat1==decl || cat1==stmt) { + big_app1(pp); big_app(big_force); big_app1(pp+1); reduce(pp,2,cat1,-1,53); +} + +@ @<Cases for |lbrace|@>= +if (cat1==rbrace) { + big_app1(pp); app('\\'); app(','); big_app1(pp+1); +@.\\,@> + reduce(pp,2,stmt,-1,54); +} +else if ((cat1==stmt||cat1==decl||cat1==function) && cat2==rbrace) { + big_app(force); big_app1(pp); big_app(indent); big_app(force); + big_app1(pp+1); big_app(force); big_app(backup); big_app1(pp+2); + big_app(outdent); big_app(force); reduce(pp,3,stmt,-1,55); +} +else if (cat1==exp) { + if (cat2==rbrace) squash(pp,3,exp,-2,56); + else if (cat2==comma && cat3==rbrace) squash(pp,4,exp,-2,56); +} + +@ @<Cases for |if_like|@>= +if (cat1==exp) { + big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,if_clause,0,57); +} + +@ @<Cases for |else_like|@>= +if (cat1==colon) squash(pp+1,1,base,1,58); +else if (cat1==lbrace) squash(pp,1,else_head,0,59); +else if (cat1==stmt) { + big_app(force); big_app1(pp); big_app(indent); big_app(break_space); + big_app1(pp+1); big_app(outdent); big_app(force); + reduce(pp,2,stmt,-1,60); +} + +@ @<Cases for |else_head|@>= +if (cat1==stmt || cat1==exp) { + big_app(force); big_app1(pp); big_app(break_space); app(noop); + big_app(cancel); big_app1(pp+1); big_app(force); + reduce(pp,2,stmt,-1,61); +} + +@ @<Cases for |if_clause|@>= +if (cat1==lbrace) squash(pp,1,if_head,0,62); +else if (cat1==stmt) { + if (cat2==else_like) { + big_app(force); big_app1(pp); big_app(indent); big_app(break_space); + big_app1(pp+1); big_app(outdent); big_app(force); big_app1(pp+2); + if (cat3==if_like) { + big_app(' '); big_app1(pp+3); reduce(pp,4,if_like,0,63); + }@+else reduce(pp,3,else_like,0,64); + } + else squash(pp,1,else_like,0,65); +} + +@ @<Cases for |if_head|@>= +if (cat1==stmt || cat1==exp) { + if (cat2==else_like) { + big_app(force); big_app1(pp); big_app(break_space); app(noop); + big_app(cancel); big_app1(pp+1); big_app(force); big_app1(pp+2); + if (cat3==if_like) { + big_app(' '); big_app1(pp+3); reduce(pp,4,if_like,0,66); + }@+else reduce(pp,3,else_like,0,67); + } + else squash(pp,1,else_head,0,68); +} + +@ @<Cases for |do_like|@>= +if (cat1==stmt && cat2==else_like && cat3==semi) { + big_app1(pp); big_app(break_space); app(noop); big_app(cancel); + big_app1(pp+1); big_app(cancel); app(noop); big_app(break_space); + big_app2(pp+2); reduce(pp,4,stmt,-1,69); +} + +@ @<Cases for |case_like|@>= +if (cat1==semi) squash(pp,2,stmt,-1,70); +else if (cat1==colon) squash(pp,2,tag,-1,71); +else if (cat1==exp) { + big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,exp,-2,72); +} + +@ @<Cases for |catch_like|@>= +if (cat1==cast || cat1==exp) { + big_app2(pp); big_app(indent); big_app(indent); reduce(pp,2,fn_decl,0,73); +} + +@ @<Cases for |tag|@>= +if (cat1==tag) { + big_app1(pp); big_app(break_space); big_app1(pp+1); reduce(pp,2,tag,-1,74); +} +else if (cat1==stmt||cat1==decl||cat1==function) { + big_app(force); big_app(backup); big_app1(pp); big_app(break_space); + big_app1(pp+1); reduce(pp,2,cat1,-1,75); +} + +@ The user can decide at run-time whether short statements should be +grouped together on the same line. + +@d force_lines flags['f'] /* should each statement be on its own line? */ +@<Cases for |stmt|@>= +if (cat1==stmt||cat1==decl||cat1==function) { + big_app1(pp); + if (cat1==function) big_app(big_force); + else if (cat1==decl) big_app(big_force); + else if (force_lines) big_app(force); + else big_app(break_space); + big_app1(pp+1); reduce(pp,2,cat1,-1,76); +} + +@ @<Cases for |semi|@>= +big_app(' '); big_app1(pp); reduce(pp,1,stmt,-1,77); + +@ @<Cases for |lproc|@>= +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) { + app(inserted); big_app2(pp); reduce(pp,2,insert,-1,79); +} else if (cat1==exp || cat1==function) { + if (cat2==rproc) { + app(inserted); big_app1(pp); big_app(' '); big_app2(pp+1); + reduce(pp,3,insert,-1,80); + } + else if (cat2==exp && cat3==rproc && cat1==exp) { + app(inserted); big_app1(pp); big_app(' '); big_app1(pp+1); app_str(" \\5"); +@.\\5@> + big_app2(pp+2); reduce(pp,4,insert,-1,80); + } +} + +@ @<Cases for |section_scrap|@>= +if (cat1==semi) { + big_app2(pp); big_app(force); reduce(pp,2,stmt,-2,81); +} +else squash(pp,1,exp,-2,82); + +@ @<Cases for |insert|@>= +if (cat1) + squash(pp,2,cat1,0,83); + +@ @<Cases for |prelangle|@>= +init_mathness=cur_mathness=yes_math; +app('<'); reduce(pp,1,binop,-2,84); + +@ @<Cases for |prerangle|@>= +init_mathness=cur_mathness=yes_math; +app('>'); reduce(pp,1,binop,-2,85); + +@ @<Cases for |langle|@>= +if (cat1==prerangle) { + big_app1(pp); app('\\'); app(','); big_app1(pp+1); +@.\\,@> + reduce(pp,2,cast,-1,86); +} +else if (cat1==decl_head || cat1==int_like || cat1==exp) { + if (cat2==prerangle) squash(pp,3,cast,-1,87); + else if (cat2==comma) { + big_app3(pp); app(opt); app('9'); reduce(pp,3,langle,0,88); + } +} + +@ @<Cases for |template_like|@>= +if (cat1==exp && cat2==prelangle) squash(pp+2,1,langle,2,89); +else if (cat1==exp || cat1==raw_int) { + big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,cat1,-2,90); +}@+ else squash(pp,1,raw_int,0,91); + +@ @<Cases for |new_like|@>= +if (cat1==lpar && cat2==exp && cat3==rpar) squash(pp,4,new_like,0,92); +else if (cat1==cast) { + big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,exp,-2,93); +} +else if (cat1!=lpar) squash(pp,1,new_exp,0,94); + +@ @<Cases for |new_exp|@>= +if (cat1==int_like || cat1==const_like) { + big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,new_exp,0,95); +} +else if (cat1==struct_like && (cat2==exp || cat2==int_like)) { + big_app1(pp); big_app(' '); big_app1(pp+1); big_app(' '); + big_app1(pp+2); reduce(pp,3,new_exp,0,96); +} +else if (cat1==raw_ubin) { + big_app1(pp); big_app('{'); big_app1(pp+1); big_app('}'); + reduce(pp,2,new_exp,0,97); +} +else if (cat1==lpar) squash(pp,1,exp,-2,98); +else if (cat1==exp) { + big_app1(pp); big_app(' '); reduce(pp,1,exp,-2,98); +} +else if (cat1!=raw_int && cat1!=struct_like && cat1!=colcol) + squash(pp,1,exp,-2,99); + +@ @<Cases for |ftemplate|@>= +if (cat1==prelangle) squash(pp+1,1,langle,1,100); +else squash(pp,1,exp,-2,101); + +@ @<Cases for |for_like|@>= +if (cat1==exp) { + big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,else_like,-2,102); +} + +@ @<Cases for |raw_ubin|@>= +if (cat1==const_like) { + big_app2(pp); app_str("\\ "); reduce(pp,2,raw_ubin,0,103); +@.\\\ @> +} else squash(pp,1,ubinop,-2,104); + +@ @<Cases for |const_like|@>= +squash(pp,1,int_like,-2,105); + +@ @<Cases for |raw_int|@>= +if (cat1==prelangle) squash(pp+1,1,langle,1,106); +else if (cat1==colcol) squash(pp,2,colcol,-1,107); +else if (cat1==cast) squash(pp,2,raw_int,0,108); +else if (cat1==lpar) squash(pp,1,exp,-2,109); +else if (cat1!=langle) squash(pp,1,int_like,-3,110); + +@ @<Cases for |operator_like|@>= +if (cat1==binop || cat1==unop || cat1==ubinop) { + if (cat2==binop) break; + big_app1(pp); big_app('{'); big_app1(pp+1); big_app('}'); + reduce(pp,2,exp,-2,111); +} +else if (cat1==new_like || cat1==delete_like) { + big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,exp,-2,112); +} +else if (cat1==comma) squash(pp,2,exp,-2,113); +else if (cat1!=raw_ubin) squash(pp,1,new_exp,0,114); + +@ @<Cases for |typedef_like|@>= +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); +} + +@ @<Cases for |delete_like|@>= +if (cat1==lpar && cat2==rpar) { + big_app2(pp); app('\\'); app(','); big_app1(pp+2); +@.\\,@> + reduce(pp,3,delete_like,0,121); +} +else if (cat1==exp) { + big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,exp,-2,122); +} + +@ @<Cases for |question|@>= +if (cat1==exp && (cat2==colon || cat2==base)) { + (pp+2)->mathness=5*yes_math; /* this colon should be in math mode */ + squash(pp,3,binop,-2,123); +} + +@ Now here's the |reduce| procedure used in our code for productions. + +The `|freeze_text|' macro is used to give official status to a token list. +Before saying |freeze_text|, items are appended to the current token list, +and we know that the eventual number of this token list will be the current +value of |text_ptr|. But no list of that number really exists as yet, +because no ending point for the current list has been +stored in the |tok_start| array. After saying |freeze_text|, the +old current token list becomes legitimate, and its number is the current +value of |text_ptr-1| since |text_ptr| has been increased. The new +current token list is empty and ready to be appended to. +Note that |freeze_text| does not check to see that |text_ptr| hasn't gotten +too large, since it is assumed that this test was done beforehand. + +@d freeze_text *(++text_ptr)=tok_ptr + +@c +void +reduce(j,k,c,d,n) +scrap_pointer j; +eight_bits c; +short k, d, n; +{ + scrap_pointer i, i1; /* pointers into scrap memory */ + j->cat=c; j->trans=text_ptr; + j->mathness=4*cur_mathness+init_mathness; + freeze_text; + if (k>1) { + for (i=j+k, i1=j+1; i<=lo_ptr; i++, i1++) { + i1->cat=i->cat; i1->trans=i->trans; + i1->mathness=i->mathness; + } + lo_ptr=lo_ptr-k+1; + } + pp=(pp+d<scrap_base? scrap_base: pp+d); + @<Print a snapshot of the scrap list if debugging @>; + pp--; /* we next say |pp++| */ +} + +@ Here's the |squash| procedure, which +takes advantage of the simplification that occurs when |k==1|. + +@c +void +squash(j,k,c,d,n) +scrap_pointer j; +eight_bits c; +short k, d, n; +{ + scrap_pointer i; /* pointers into scrap memory */ + if (k==1) { + j->cat=c; pp=(pp+d<scrap_base? scrap_base: pp+d); + @<Print a snapshot...@>; + pp--; /* we next say |pp++| */ + return; + } + for (i=j; i<j+k; i++) big_app1(i); + reduce(j,k,c,d,n); +} + +@ And here now is the code that applies productions as long as possible. +Before applying the production mechanism, we must make sure +it has good input (at least four scraps, the length of the lhs of the +longest rules), and that there is enough room in the memory arrays +to hold the appended tokens and texts. Here we use a very +conservative test; it's more important to make sure the program +will still work if we change the production rules (within reason) +than to squeeze the last bit of space from the memory arrays. + +@d safe_tok_incr 20 +@d safe_text_incr 10 +@d safe_scrap_incr 10 + +@<Reduce the scraps using the productions until no more rules apply@>= +while (1) { + @<Make sure the entries |pp| through |pp+3| of |cat| are defined@>; + if (tok_ptr+safe_tok_incr>tok_mem_end) { + if (tok_ptr>max_tok_ptr) max_tok_ptr=tok_ptr; + overflow("token"); + } + if (text_ptr+safe_text_incr>tok_start_end) { + if (text_ptr>max_text_ptr) max_text_ptr=text_ptr; + overflow("text"); + } + if (pp>lo_ptr) break; + init_mathness=cur_mathness=maybe_math; + @<Match a production...@>; +} + +@ If we get to the end of the scrap list, category codes equal to zero are +stored, since zero does not match anything in a production. + +@<Make sure the entries...@>= +if (lo_ptr<pp+3) { + while (hi_ptr<=scrap_ptr && lo_ptr!=pp+3) { + (++lo_ptr)->cat=hi_ptr->cat; lo_ptr->mathness=(hi_ptr)->mathness; + lo_ptr->trans=(hi_ptr++)->trans; + } + for (i=lo_ptr+1;i<=pp+3;i++) i->cat=0; +} + +@ If \.{CWEAVE} is being run in debugging mode, the production numbers and +current stack categories will be printed out when |tracing| is set to 2; +a sequence of two or more irreducible scraps will be printed out when +|tracing| is set to 1. + +@<Global...@>= +int tracing; /* can be used to show parsing details */ + +@ @<Print a snapsh...@>= +{ scrap_pointer k; /* pointer into |scrap_info| */ + if (tracing==2) { + printf("\n%d:",n); + for (k=scrap_base; k<=lo_ptr; k++) { + if (k==pp) putxchar('*'); else putxchar(' '); + if (k->mathness %4 == yes_math) putchar('+'); + else if (k->mathness %4 == no_math) putchar('-'); + print_cat(k->cat); + if (k->mathness /4 == yes_math) putchar('+'); + else if (k->mathness /4 == no_math) putchar('-'); + } + if (hi_ptr<=scrap_ptr) printf("..."); /* indicate that more is coming */ + } +} + +@ The |translate| function assumes that scraps have been stored in +positions |scrap_base| through |scrap_ptr| of |cat| and |trans|. It +applies productions as much as +possible. The result is a token list containing the translation of +the given sequence of scraps. + +After calling |translate|, we will have |text_ptr+3<=max_texts| and +|tok_ptr+6<=max_toks|, so it will be possible to create up to three token +lists with up to six tokens without checking for overflow. Before calling +|translate|, we should have |text_ptr<max_texts| and |scrap_ptr<max_scraps|, +since |translate| might add a new text and a new scrap before it checks +for overflow. + +@c +text_pointer +translate() /* converts a sequence of scraps */ +{ + scrap_pointer i, /* index into |cat| */ + j; /* runs through final scraps */ + pp=scrap_base; lo_ptr=pp-1; hi_ptr=pp; + @<If tracing, print an indication of where we are@>; + @<Reduce the scraps...@>; + @<Combine the irreducible scraps that remain@>; +} + +@ If the initial sequence of scraps does not reduce to a single scrap, +we concatenate the translations of all remaining scraps, separated by +blank spaces, with dollar signs surrounding the translations of scraps +where appropriate. + +@<Combine the irreducible...@>= { + @<If semi-tracing, show the irreducible scraps@>; + for (j=scrap_base; j<=lo_ptr; j++) { + if (j!=scrap_base) app(' '); + if (j->mathness % 4 == yes_math) app('$'); + app1(j); + if (j->mathness / 4 == yes_math) app('$'); + if (tok_ptr+6>tok_mem_end) overflow("token"); + } + freeze_text; return(text_ptr-1); +} + +@ @<If semi-tracing, show the irreducible scraps@>= +if (lo_ptr>scrap_base && tracing==1) { + printf("\nIrreducible scrap sequence in section %d:",section_count); +@.Irreducible scrap sequence...@> + mark_harmless; + for (j=scrap_base; j<=lo_ptr; j++) { + printf(" "); print_cat(j->cat); + } +} + +@ @<If tracing,...@>= +if (tracing==2) { + printf("\nTracing after l. %d:\n",cur_line); mark_harmless; +@.Tracing after...@> + if (loc>buffer+50) { + printf("..."); + term_write(loc-51,51); + } + else term_write(buffer,loc-buffer); +} + +@* Initializing the scraps. +If we are going to use the powerful production mechanism just developed, we +must get the scraps set up in the first place, given a \CEE/ text. A table +of the initial scraps corresponding to \CEE/ tokens appeared above in the +section on parsing; our goal now is to implement that table. We shall do this +by implementing a subroutine called |C_parse| that is analogous to the +|C_xref| routine used during phase one. + +Like |C_xref|, the |C_parse| procedure starts with the current +value of |next_control| and it uses the operation |next_control=get_next()| +repeatedly to read \CEE/ text until encountering the next `\.{\v}' or +`\.{/*}', or until |next_control>=format_code|. The scraps corresponding to +what it reads are appended into the |cat| and |trans| arrays, and |scrap_ptr| +is advanced. + +@c +void +C_parse(spec_ctrl) /* creates scraps from \CEE/ tokens */ + eight_bits spec_ctrl; +{ + int count; /* characters remaining before string break */ + while (next_control<format_code || next_control==spec_ctrl) { + @<Append the scrap appropriate to |next_control|@>; + next_control=get_next(); + if (next_control=='|' || next_control==begin_comment || + next_control==begin_short_comment) return; + } +} + +@ The following macro is used to append a scrap whose tokens have just +been appended: + +@d app_scrap(c,b) { + (++scrap_ptr)->cat=(c); scrap_ptr->trans=text_ptr; + scrap_ptr->mathness=5*(b); /* no no, yes yes, or maybe maybe */ + freeze_text; +} + +@ @<Append the scr...@>= +@<Make sure that there is room for the new scraps, tokens, and texts@>; +switch (next_control) { + case section_name: + app(section_flag+(int)(cur_section-name_dir)); + app_scrap(section_scrap,maybe_math); + app_scrap(exp,yes_math);@+break; + case string: case constant: case verbatim: @<Append a string or constant@>; + @+break; + case identifier: app_cur_id(1);@+break; + case TeX_string: @<Append a \TEX/ string, without forming a scrap@>;@+break; + case '/': case '.': + app(next_control); app_scrap(binop,yes_math);@+break; + case '<': app_str("\\langle");@+app_scrap(prelangle,yes_math);@+break; +@.\\langle@> + case '>': app_str("\\rangle");@+app_scrap(prerangle,yes_math);@+break; +@.\\rangle@> + case '=': app_str("\\K"); app_scrap(binop,yes_math);@+break; +@.\\K@> + case '|': app_str("\\OR"); app_scrap(binop,yes_math);@+break; +@.\\OR@> + case '^': app_str("\\XOR"); app_scrap(binop,yes_math);@+break; +@.\\XOR@> + case '%': app_str("\\MOD"); app_scrap(binop,yes_math);@+break; +@.\\MOD@> + case '!': app_str("\\R"); app_scrap(unop,yes_math);@+break; +@.\\R@> + case '~': app_str("\\CM"); app_scrap(unop,yes_math);@+break; +@.\\CM@> + case '+': case '-': app(next_control); app_scrap(ubinop,yes_math);@+break; + case '*': app(next_control); app_scrap(raw_ubin,yes_math);@+break; + case '&': app_str("\\AND"); app_scrap(raw_ubin,yes_math);@+break; +@.\\AND@> + case '?': app_str("\\?"); app_scrap(question,yes_math);@+break; +@.\\?@> + case '#': app_str("\\#"); app_scrap(ubinop,yes_math);@+break; +@.\\\#@> + case ignore: case xref_roman: case xref_wildcard: + 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; + case '{': app_str("\\{"@q}@>); app_scrap(lbrace,yes_math);@+break; +@.\\\{@>@q}@> + case '}': app_str(@q{@>"\\}"); app_scrap(rbrace,yes_math);@+break; +@q{@>@.\\\}@> + case ',': app(','); app_scrap(comma,yes_math);@+break; + case ';': app(';'); app_scrap(semi,maybe_math);@+break; + case ':': app(':'); app_scrap(colon,no_math);@+break;@/ + @t\4@> @<Cases involving nonstandard characters@>@; + case thin_space: app_str("\\,"); app_scrap(insert,maybe_math);@+break; +@.\\,@> + case math_break: app(opt); app_str("0"); + app_scrap(insert,maybe_math);@+break; + case line_break: app(force); app_scrap(insert,no_math);@+break; + case left_preproc: app(force); app(preproc_line); + app_str("\\#"); app_scrap(lproc,no_math);@+break; +@.\\\#@> + case right_preproc: app(force); app_scrap(rproc,no_math);@+break; + case big_line_break: app(big_force); app_scrap(insert,no_math);@+break; + case no_line_break: app(big_cancel); app(noop); app(break_space); + app(noop); app(big_cancel); + app_scrap(insert,no_math);@+break; + case pseudo_semi: app_scrap(semi,maybe_math);@+break; + case macro_arg_open: app_scrap(begin_arg,maybe_math);@+break; + case macro_arg_close: app_scrap(end_arg,maybe_math);@+break; + case join: app_str("\\J"); app_scrap(insert,no_math);@+break; +@.\\J@> + case output_defs_code: app(force); app_str("\\ATH"); app(force); + app_scrap(insert,no_math);@+break; +@.\\ATH@> + default: app(inserted); app(next_control); + app_scrap(insert,maybe_math);@+break; +} + +@ @<Make sure that there is room for the new...@>= +if (scrap_ptr+safe_scrap_incr>scrap_info_end || + tok_ptr+safe_tok_incr>tok_mem_end @| || + text_ptr+safe_text_incr>tok_start_end) { + if (scrap_ptr>max_scr_ptr) max_scr_ptr=scrap_ptr; + if (tok_ptr>max_tok_ptr) max_tok_ptr=tok_ptr; + if (text_ptr>max_text_ptr) max_text_ptr=text_ptr; + overflow("scrap/token/text"); +} + +@ Some nonstandard characters may have entered \.{CWEAVE} by means of +standard ones. They are converted to \TEX/ control sequences so that it is +possible to keep \.{CWEAVE} from outputting unusual |char| codes. + +@<Cases involving nonstandard...@>= +case not_eq: app_str("\\I");@+app_scrap(binop,yes_math);@+break; +@.\\I@> +case lt_eq: app_str("\\Z");@+app_scrap(binop,yes_math);@+break; +@.\\Z@> +case gt_eq: app_str("\\G");@+app_scrap(binop,yes_math);@+break; +@.\\G@> +case eq_eq: app_str("\\E");@+app_scrap(binop,yes_math);@+break; +@.\\E@> +case and_and: app_str("\\W");@+app_scrap(binop,yes_math);@+break; +@.\\W@> +case or_or: app_str("\\V");@+app_scrap(binop,yes_math);@+break; +@.\\V@> +case plus_plus: app_str("\\PP");@+app_scrap(unop,yes_math);@+break; +@.\\PP@> +case minus_minus: app_str("\\MM");@+app_scrap(unop,yes_math);@+break; +@.\\MM@> +case minus_gt: app_str("\\MG");@+app_scrap(binop,yes_math);@+break; +@.\\MG@> +case gt_gt: app_str("\\GG");@+app_scrap(binop,yes_math);@+break; +@.\\GG@> +case lt_lt: app_str("\\LL");@+app_scrap(binop,yes_math);@+break; +@.\\LL@> +case dot_dot_dot: app_str("\\,\\ldots\\,");@+app_scrap(raw_int,yes_math); + @+break; +@.\\,@> +@.\\ldots@> +case colon_colon: app_str("\\DC");@+app_scrap(colcol,maybe_math);@+break; +@.\\DC@> +case period_ast: app_str("\\PA");@+app_scrap(binop,yes_math);@+break; +@.\\PA@> +case minus_gt_ast: app_str("\\MGA");@+app_scrap(binop,yes_math);@+break; +@.\\MGA@> + +@ The following code must use |app_tok| instead of |app| in order to +protect against overflow. Note that |tok_ptr+1<=max_toks| after |app_tok| +has been used, so another |app| is legitimate before testing again. + +Many of the special characters in a string must be prefixed by `\.\\' so that +\TEX/ will print them properly. +@^special string characters@> + +@<Append a string or...@>= +count= -1; +if (next_control==constant) app_str("\\T{"@q}@>); +@.\\T@> +else if (next_control==string) { + count=20; app_str("\\.{"@q}@>); +} +@.\\.@> +else app_str("\\vb{"@q}@>); +@.\\vb@> +while (id_first<id_loc) { + if (count==0) { /* insert a discretionary break in a long string */ + app_str(@q(@>@q{@>"}\\)\\.{"@q}@>); count=20; +@q(@>@.\\)@> + } +@^high-bit character handling@> + if((eight_bits)(*id_first)>0177) { + app_tok(quoted_char); + app_tok((eight_bits)(*id_first++)); + } + else { + switch (*id_first) { + case ' ':case '\\':case '#':case '%':case '$':case '^': + case '{': case '}': case '~': case '&': case '_': app('\\'); break; +@.\\\ @> +@.\\\\@> +@.\\\#@> +@.\\\%@> +@.\\\$@> +@.\\\^@> +@.\\\{@>@q}@> +@q{@>@.\\\}@> +@.\\\~@> +@.\\\&@> +@.\\\_@> + case '@@': if (*(id_first+1)=='@@') id_first++; + else err_print("! Double @@ should be used in strings"); +@.Double @@ should be used...@> + } + app_tok(*id_first++); + } + count--; +} +app(@q{@>'}'); +app_scrap(exp,maybe_math); + +@ We do not make the \TEX/ string into a scrap, because there is no +telling what the user will be putting into it; instead we leave it +open, to be picked up by the next scrap. If it comes at the end of a +section, it will be made into a scrap when |finish_C| is called. + +There's a known bug here, in cases where an adjacent scrap is +|prelangle| or |prerangle|. Then the \TEX/ string can disappear +when the \.{\\langle} or \.{\\rangle} becomes \.{<} or \.{>}. +For example, if the user writes \.{\v x<@@ty@@>\v}, the \TEX/ string +\.{\\hbox\{y\}} eventually becomes part of an |insert| scrap, which is combined +with a |prelangle| scrap and eventually lost. The best way to work around +this bug is probably to enclose the \.{@@t...@@>} in \.{@@[...@@]} so that +the \TEX/ string is treated as an expression. +@^bug, known@> + +@<Append a \TEX/ string, without forming a scrap@>= +app_str("\\hbox{"@q}@>); +@^high-bit character handling@> +while (id_first<id_loc) + if((eight_bits)(*id_first)>0177) { + app_tok(quoted_char); + app_tok((eight_bits)(*id_first++)); + } + else { + if (*id_first=='@@') id_first++; + app_tok(*id_first++); + } +app(@q{@>'}'); + +@ The function |app_cur_id| appends the current identifier to the +token list; it also builds a new scrap if |scrapping==1|. + +@<Predec...@>= +void app_cur_id(); + +@ @c +void +app_cur_id(scrapping) +boolean scrapping; /* are we making this into a scrap? */ +{ + name_pointer p=id_lookup(id_first,id_loc,normal); + if (p->ilk<=custom) { /* not a reserved word */ + app(id_flag+(int)(p-name_dir)); + if (scrapping) app_scrap(p->ilk==func_template? ftemplate: exp, + p->ilk==custom? yes_math: maybe_math); +@.\\NULL@> + } else { + app(res_flag+(int)(p-name_dir)); + if (scrapping) { + if (p->ilk==alfop) app_scrap(ubinop,yes_math)@; + else app_scrap(p->ilk,maybe_math); + } + } +} + +@ When the `\.{\v}' that introduces \CEE/ text is sensed, a call on +|C_translate| will return a pointer to the \TEX/ translation of +that text. If scraps exist in |scrap_info|, they are +unaffected by this translation process. + +@c +text_pointer +C_translate() +{ + text_pointer p; /* points to the translation */ + scrap_pointer save_base; /* holds original value of |scrap_base| */ + save_base=scrap_base; scrap_base=scrap_ptr+1; + C_parse(section_name); /* get the scraps together */ + if (next_control!='|') err_print("! Missing '|' after C text"); +@.Missing '|'...@> + app_tok(cancel); app_scrap(insert,maybe_math); + /* place a |cancel| token as a final ``comment'' */ + p=translate(); /* make the translation */ + if (scrap_ptr>max_scr_ptr) max_scr_ptr=scrap_ptr; + scrap_ptr=scrap_base-1; scrap_base=save_base; /* scrap the scraps */ + return(p); +} + +@ The |outer_parse| routine is to |C_parse| as |outer_xref| +is to |C_xref|: It constructs a sequence of scraps for \CEE/ text +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. + +@d make_pb flags['e'] + +@c +void +outer_parse() /* makes scraps from \CEE/ tokens and comments */ +{ + int bal; /* brace level in comment */ + text_pointer p, q; /* partial comments */ + while (next_control<format_code) + if (next_control!=begin_comment && next_control!=begin_short_comment) + C_parse(ignore); + else { + boolean is_long_comment=(next_control==begin_comment); + @<Make sure that there is room for the new...@>; + app(cancel); app(inserted); + if (is_long_comment) app_str("\\C{"@q}@>); +@.\\C@> + else app_str("\\SHC{"@q}@>); +@.\\SHC@> + bal=copy_comment(is_long_comment,1); next_control=ignore; + while (bal>0) { + 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)); + if (make_pb) app_str("\\PB{"); +@.\\PB@> + app(inner_tok_flag+(int)(q-tok_start)); + if (make_pb) app_tok('}'); + if (next_control=='|') { + bal=copy_comment(is_long_comment,bal); + next_control=ignore; + } + else bal=0; /* an error has been reported */ + } + app(force); app_scrap(insert,no_math); + /* the full comment becomes a scrap */ + } +} + +@* Output of tokens. +So far our programs have only built up multi-layered token lists in +\.{CWEAVE}'s internal memory; we have to figure out how to get them into +the desired final form. The job of converting token lists to characters in +the \TEX/ output file is not difficult, although it is an implicitly +recursive process. Four main considerations had to be kept in mind when +this part of \.{CWEAVE} was designed. (a) There are two modes of output: +|outer| mode, which translates tokens like |force| into line-breaking +control sequences, and |inner| mode, which ignores them except that blank +spaces take the place of line breaks. (b) The |cancel| instruction applies +to adjacent token or tokens that are output, and this cuts across levels +of recursion since `|cancel|' occurs at the beginning or end of a token +list on one level. (c) The \TEX/ output file will be semi-readable if line +breaks are inserted after the result of tokens like |break_space| and +|force|. (d) The final line break should be suppressed, and there should +be no |force| token output immediately after `\.{\\Y\\B}'. + +@ The output process uses a stack to keep track of what is going on at +different ``levels'' as the token lists are being written out. Entries on +this stack have three parts: + +\yskip\hang |end_field| is the |tok_mem| location where the token list of a +particular level will end; + +\yskip\hang |tok_field| is the |tok_mem| location from which the next token +on a particular level will be read; + +\yskip\hang |mode_field| is the current mode, either |inner| or |outer|. + +\yskip\noindent The current values of these 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_tok|, and +|cur_mode|. + +The global variable |stack_ptr| tells how many levels of output are +currently in progress. The end of output occurs when an |end_translation| +token is found, so the stack is never empty except when we first begin the +output process. + +@d inner 0 /* value of |mode| for \CEE/ texts within \TEX/ texts */ +@d outer 1 /* value of |mode| for \CEE/ texts in sections */ + +@<Typed...@>= typedef int mode; +typedef struct { + token_pointer end_field; /* ending location of token list */ + token_pointer tok_field; /* present location within token list */ + boolean mode_field; /* interpretation of control tokens */ +} output_state; +typedef output_state *stack_pointer; + +@ @d cur_end cur_state.end_field /* current ending location in |tok_mem| */ +@d cur_tok cur_state.tok_field /* location of next output token in |tok_mem| */ +@d cur_mode cur_state.mode_field /* current mode of interpretation */ +@d init_stack stack_ptr=stack;cur_mode=outer /* initialize the stack */ + +@<Global...@>= +output_state cur_state; /* |cur_end|, |cur_tok|, |cur_mode| */ +output_state stack[stack_size]; /* info for non-current levels */ +stack_pointer stack_ptr; /* first unused location in the output state stack */ +stack_pointer stack_end=stack+stack_size-1; /* end of |stack| */ +stack_pointer max_stack_ptr; /* largest value assumed by |stack_ptr| */ + +@ @<Set init...@>= +max_stack_ptr=stack; + +@ To insert token-list |p| into the output, the |push_level| subroutine +is called; it saves the old level of output and gets a new one going. +The value of |cur_mode| is not changed. + +@c +void +push_level(p) /* suspends the current level */ +text_pointer p; +{ + if (stack_ptr==stack_end) overflow("stack"); + if (stack_ptr>stack) { /* save current state */ + stack_ptr->end_field=cur_end; + stack_ptr->tok_field=cur_tok; + stack_ptr->mode_field=cur_mode; + } + stack_ptr++; + if (stack_ptr>max_stack_ptr) max_stack_ptr=stack_ptr; + cur_tok=*p; cur_end=*(p+1); +} + +@ Conversely, the |pop_level| routine restores the conditions that were in +force when the current level was begun. This subroutine will never be +called when |stack_ptr==1|. + +@c +void +pop_level() +{ + cur_end=(--stack_ptr)->end_field; + cur_tok=stack_ptr->tok_field; cur_mode=stack_ptr->mode_field; +} + +@ The |get_output| function returns the next byte of output that is not a +reference to a token list. It returns the values |identifier| or |res_word| +or |section_code| if the next token is to be an identifier (typeset in +italics), a reserved word (typeset in boldface), or a section name (typeset +by a complex routine that might generate additional levels of output). +In these cases |cur_name| points to the identifier or section name in +question. + +@<Global...@>= +name_pointer cur_name; + +@ @d res_word 0201 /* returned by |get_output| for reserved words */ +@d section_code 0200 /* returned by |get_output| for section names */ + +@c +eight_bits +get_output() /* returns the next token of output */ +{ + sixteen_bits a; /* current item read from |tok_mem| */ + restart: while (cur_tok==cur_end) pop_level(); + a=*(cur_tok++); + if (a>=0400) { + cur_name=a % id_flag + name_dir; + switch (a / id_flag) { + case 2: return(res_word); /* |a==res_flag+cur_name| */ + case 3: return(section_code); /* |a==section_flag+cur_name| */ + case 4: push_level(a % id_flag + tok_start); goto restart; + /* |a==tok_flag+cur_name| */ + case 5: push_level(a % id_flag + tok_start); cur_mode=inner; goto restart; + /* |a==inner_tok_flag+cur_name| */ + default: return(identifier); /* |a==id_flag+cur_name| */ + } + } + return(a); +} + +@ The real work associated with token output is done by |make_output|. +This procedure appends an |end_translation| token to the current token list, +and then it repeatedly calls |get_output| and feeds characters to the output +buffer until reaching the |end_translation| sentinel. It is possible for +|make_output| to be called recursively, since a section name may include +embedded \CEE/ text; however, the depth of recursion never exceeds one +level, since section names cannot be inside of section names. + +A procedure called |output_C| does the scanning, translation, and +output of \CEE/ text within `\pb' brackets, and this procedure uses +|make_output| to output the current token list. Thus, the recursive call +of |make_output| actually occurs when |make_output| calls |output_C| +while outputting the name of a section. +@^recursion@> + +@c +void +output_C() /* outputs the current token list */ +{ + token_pointer save_tok_ptr; + text_pointer save_text_ptr; + sixteen_bits save_next_control; /* values to be restored */ + text_pointer p; /* translation of the \CEE/ text */ + save_tok_ptr=tok_ptr; save_text_ptr=text_ptr; + save_next_control=next_control; next_control=ignore; p=C_translate(); + app(inner_tok_flag+(int)(p-tok_start)); + if (make_pb) { + out_str("\\PB{"); make_output(); out('}'); +@.\\PB@> + }@+else make_output(); /* output the list */ + if (text_ptr>max_text_ptr) max_text_ptr=text_ptr; + if (tok_ptr>max_tok_ptr) max_tok_ptr=tok_ptr; + text_ptr=save_text_ptr; tok_ptr=save_tok_ptr; /* forget the tokens */ + next_control=save_next_control; /* restore |next_control| to original state */ +} + +@ Here is \.{CWEAVE}'s major output handler. + +@<Predecl...@>= +void make_output(); + +@ @c +void +make_output() /* outputs the equivalents of tokens */ +{ + eight_bits a, /* current output byte */ + b; /* next output byte */ + int c; /* count of |indent| and |outdent| tokens */ + char scratch[longest_name]; /* scratch area for section names */ + char *k, *k_limit; /* indices into |scratch| */ + char *j; /* index into |buffer| */ + char *p; /* index into |byte_mem| */ + char delim; /* first and last character of string being copied */ + char *save_loc, *save_limit; /* |loc| and |limit| to be restored */ + name_pointer cur_section_name; /* name of section being output */ + boolean save_mode; /* value of |cur_mode| before a sequence of breaks */ + app(end_translation); /* append a sentinel */ + freeze_text; push_level(text_ptr-1); + while (1) { + a=get_output(); + reswitch: switch(a) { + case end_translation: return; + case identifier: case res_word: @<Output an identifier@>; break; + case section_code: @<Output a section name@>; break; + case math_rel: out_str("\\MRL{"@q}@>); +@.\\MRL@> + case noop: case inserted: break; + case cancel: case big_cancel: c=0; b=a; + while (1) { + a=get_output(); + if (a==inserted) continue; + if ((a<indent && !(b==big_cancel&&a==' ')) || a>big_force) break; + if (a==indent) c++; else if (a==outdent) c--; + else if (a==opt) a=get_output(); + } + @<Output saved |indent| or |outdent| tokens@>; + goto reswitch; + case indent: case outdent: case opt: case backup: case break_space: + case force: case big_force: case preproc_line: @<Output a control, + look ahead in case of line breaks, possibly |goto reswitch|@>; break; + case quoted_char: out(*(cur_tok++)); + case qualifier: break; + default: out(a); /* otherwise |a| is an ordinary character */ + } + } +} + +@ An identifier of length one does not have to be enclosed in braces, and it +looks slightly better if set in a math-italic font instead of a (slightly +narrower) text-italic font. Thus we output `\.{\\\v}\.{a}' but +`\.{\\\\\{aa\}}'. + +@<Output an identifier@>= +out('\\'); +if (a==identifier) { + if (cur_name->ilk==custom && !doing_format) { + custom_out: + for (p=cur_name->byte_start;p<(cur_name+1)->byte_start;p++) + out(*p=='_'? 'x': *p=='$'? 'X': *p); + break; + } else if (is_tiny(cur_name)) out('|')@; +@.\\|@> + else { delim='.'; + for (p=cur_name->byte_start;p<(cur_name+1)->byte_start;p++) + if (xislower(*p)) { /* not entirely uppercase */ + delim='\\'; break; + } + out(delim); + } +@.\\\\@> +@.\\.@> +}@+else if (cur_name->ilk==alfop) { + out('X'); + goto custom_out; +}@+else out('&'); /* |a==res_word| */ +@.\\\&@> +if (is_tiny(cur_name)) { + if (isxalpha((cur_name->byte_start)[0])) + out('\\'); + out((cur_name->byte_start)[0]); +} +else out_name(cur_name,1); + +@ The current mode does not affect the behavior of \.{CWEAVE}'s output routine +except when we are outputting control tokens. + +@<Output a control...@>= +if (a<break_space || a==preproc_line) { + if (cur_mode==outer) { + out('\\'); out(a-cancel+'0'); +@.\\1@> +@.\\2@> +@.\\3@> +@.\\4@> +@.\\8@> + if (a==opt) { + b=get_output(); /* |opt| is followed by a digit */ + if (b!='0' || force_lines==0) out(b)@; + else out_str("{-1}"); /* |force_lines| encourages more \.{@@\v} breaks */ + } + } else if (a==opt) b=get_output(); /* ignore digit following |opt| */ + } +else @<Look ahead for strongest line break, |goto reswitch|@> + +@ If several of the tokens |break_space|, |force|, |big_force| occur in a +row, possibly mixed with blank spaces (which are ignored), +the largest one is used. A line break also occurs in the output file, +except at the very end of the translation. The very first line break +is suppressed (i.e., a line break that follows `\.{\\Y\\B}'). + +@<Look ahead for st...@>= { + b=a; save_mode=cur_mode; c=0; + while (1) { + a=get_output(); + if (a==inserted) continue; + if (a==cancel || a==big_cancel) { + @<Output saved |indent| or |outdent| tokens@>; + goto reswitch; /* |cancel| overrides everything */ + } + if ((a!=' ' && a<indent) || a==backup || a>big_force) { + if (save_mode==outer) { + if (out_ptr>out_buf+3 && strncmp(out_ptr-3,"\\Y\\B",4)==0) + goto reswitch; + @<Output saved |indent| or |outdent| tokens@>; + out('\\'); out(b-cancel+'0'); +@.\\5@> +@.\\6@> +@.\\7@> + if (a!=end_translation) finish_line(); + } + else if (a!=end_translation && cur_mode==inner) out(' '); + goto reswitch; + } + if (a==indent) c++; + else if (a==outdent) c--; + else if (a==opt) a=get_output(); + else if (a>b) b=a; /* if |a==' '| we have |a<b| */ + } +} + +@ @<Output saved...@>= + for (;c>0;c--) out_str("\\1"); +@.\\1@> + for (;c<0;c++) out_str("\\2"); +@.\\2@> + +@ The remaining part of |make_output| is somewhat more complicated. When we +output a section name, we may need to enter the parsing and translation +routines, since the name may contain \CEE/ code embedded in +\pb\ constructions. This \CEE/ code is placed at the end of the active +input buffer and the translation process uses the end of the active +|tok_mem| area. + +@<Output a section name@>= { + out_str("\\X"); +@.\\X@> + cur_xref=(xref_pointer)cur_name->xref; + if (cur_xref->num==file_flag) {an_output=1; cur_xref=cur_xref->xlink;} + else an_output=0; + if (cur_xref->num>=def_flag) { + out_section(cur_xref->num-def_flag); + if (phase==3) { + cur_xref=cur_xref->xlink; + while (cur_xref->num>=def_flag) { + out_str(", "); + out_section(cur_xref->num-def_flag); + cur_xref=cur_xref->xlink; + } + } + } + else out('0'); /* output the section number, or zero if it was undefined */ + out(':'); + if (an_output) out_str("\\.{"@q}@>); +@.\\.@> + @<Output the text of the section name@>; + if (an_output) out_str(@q{@>" }"); + out_str("\\X"); +} + +@ @<Output the text...@>= +sprint_section_name(scratch,cur_name); +k=scratch; +k_limit=scratch+strlen(scratch); +cur_section_name=cur_name; +while (k<k_limit) { + b=*(k++); + if (b=='@@') @<Skip next character, give error if not `\.{@@}'@>; + if (an_output) + switch (b) { + case ' ':case '\\':case '#':case '%':case '$':case '^': + case '{': case '}': case '~': case '&': case '_': + out('\\'); /* falls through */ +@.\\\ @> +@.\\\\@> +@.\\\#@> +@.\\\%@> +@.\\\$@> +@.\\\^@> +@.\\\{@>@q}@> +@q{@>@.\\\}@> +@.\\\~@> +@.\\\&@> +@.\\\_@> + default: out(b); + } + else if (b!='|') out(b)@; + else { + @<Copy the \CEE/ text into the |buffer| array@>; + save_loc=loc; save_limit=limit; loc=limit+2; limit=j+1; + *limit='|'; output_C(); + loc=save_loc; limit=save_limit; + } +} + +@ @<Skip next char...@>= +if (*k++!='@@') { + printf("\n! Illegal control code in section name: <"); +@.Illegal control code...@> + print_section_name(cur_section_name); printf("> "); mark_error; +} + +@ The \CEE/ text enclosed in \pb\ should not contain `\.{\v}' characters, +except within strings. We put a `\.{\v}' at the front of the buffer, so that an +error message that displays the whole buffer will look a little bit sensible. +The variable |delim| is zero outside of strings, otherwise it +equals the delimiter that began the string being copied. + +@<Copy the \CEE/ text into...@>= +j=limit+1; *j='|'; delim=0; +while (1) { + if (k>=k_limit) { + printf("\n! C text in section name didn't end: <"); +@.C text...didn't end@> + print_section_name(cur_section_name); printf("> "); mark_error; break; + } + b=*(k++); + if (b=='@@' || (b=='\\' && delim!=0)) + @<Copy a quoted character into the buffer@>@; + else { + if (b=='\'' || b=='"') + if (delim==0) delim=b; + else if (delim==b) delim=0; + if (b!='|' || delim!=0) { + if (j>buffer+long_buf_size-3) overflow("buffer"); + *(++j)=b; + } + else break; + } +} + +@ @<Copy a quoted char...@>= { + if (j>buffer+long_buf_size-4) overflow("buffer"); + *(++j)=b; *(++j)=*(k++); +} + +@** Phase two processing. +We have assembled enough pieces of the puzzle in order to be ready to specify +the processing in \.{CWEAVE}'s main pass over the source file. Phase two +is analogous to phase one, except that more work is involved because we must +actually output the \TEX/ material instead of merely looking at the +\.{CWEB} specifications. + +@<Predecl...@>= +void phase_two(); + +@ @c +void +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) @<Translate the current section@>; +} + +@ 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 +printed. However, we don't want \.{\\Y} to occur between two definitions +within a single section. The variables |out_line| or |out_ptr| will +change if a section is non-null, so the following macros `|save_position|' +and `|emit_space_if_needed|' are able to handle the situation: + +@d save_position save_line=out_line; save_place=out_ptr +@d emit_space_if_needed if (save_line!=out_line || save_place!=out_ptr) + out_str("\\Y"); + space_checked=1 +@.\\Y@> + +@<Global...@>= +int save_line; /* former value of |out_line| */ +char *save_place; /* former value of |out_ptr| */ +int sec_depth; /* the integer, if any, following \.{@@*} */ +boolean space_checked; /* have we done |emit_space_if_needed|? */ +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? */ + +@ @<Translate the current section@>= { + section_count++; + @<Output the code for the beginning of a new section@>; + save_position; + @<Translate the \TEX/ part of the current section@>; + @<Translate the definition part of the current section@>; + @<Translate the \CEE/ part of the current section@>; + @<Show cross-references to this section@>; + @<Output the code for the end of a section@>; +} + +@ Sections beginning with the \.{CWEB} control sequence `\.{@@\ }' start in the +output with the \TEX/ control sequence `\.{\\M}', followed by the section +number. Similarly, `\.{@@*}' sections lead to the control sequence `\.{\\N}'. +In this case there's an additional parameter, representing one plus the +specified depth, immediately after the \.{\\N}. +If the section has changed, we put \.{\\*} just after the section number. + +@<Output the code for the beginning...@>= +if (*(loc-1)!='*') out_str("\\M"); +@.\\M@> +else { + while (*loc == ' ') loc++; + if (*loc=='*') { /* ``top'' level */ + sec_depth = -1; + loc++; + } + else { + for (sec_depth=0; xisdigit(*loc);loc++) + sec_depth = sec_depth*10 + (*loc) -'0'; + } + while (*loc == ' ') loc++; /* remove spaces before group title */ + group_found=1; + out_str("\\N"); +@.\\N@> + {@+ 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("}"); + +@ 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. + +@<Translate the \T...@>= do { + next_control=copy_TeX(); + 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...@> + case thin_space: case math_break: case ord: + case line_break: case big_line_break: case no_line_break: case join: + case pseudo_semi: case macro_arg_open: case macro_arg_close: + case output_defs_code: + err_print("! You can't do that in TeX text"); break; +@.You can't do that...@> + } +} while (next_control<format_code); + +@ When we get to the following code we have |next_control>=format_code|, and +the token memory is in its initial empty state. + +@<Translate the d...@>= +space_checked=0; +while (next_control<=definition) { /* |format_code| or |definition| */ + init_stack; + if (next_control==definition) @<Start a macro definition@>@; + else @<Start a format definition@>; + 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 +memories to their initial empty state. + +A |force| token is appended to the current scraps before translation +takes place, so that the translation will normally end with \.{\\6} or +\.{\\7} (the \TEX/ macros for |force| and |big_force|). This \.{\\6} or +\.{\\7} is replaced by the concluding \.{\\par} or by \.{\\Y\\par}. + +@<Predecl...@>= +void finish_C(); + +@ @c +void +finish_C(visible) /* finishes a definition or a \CEE/ part */ + boolean visible; /* nonzero if we should produce \TEX/ output */ +{ + text_pointer p; /* translation of the scraps */ + if (visible) { + out_str("\\B"); app_tok(force); app_scrap(insert,no_math); + p=translate(); +@.\\B@> + app(tok_flag+(int)(p-tok_start)); make_output(); /* output the list */ + if (out_ptr>out_buf+1) + if (*(out_ptr-1)=='\\') +@.\\6@> +@.\\7@> +@.\\Y@> + if (*out_ptr=='6') out_ptr-=2; + else if (*out_ptr=='7') *out_ptr='Y'; + out_str("\\par"); finish_line(); + } + if (text_ptr>max_text_ptr) max_text_ptr=text_ptr; + if (tok_ptr>max_tok_ptr) max_tok_ptr=tok_ptr; + if (scrap_ptr>max_scr_ptr) max_scr_ptr=scrap_ptr; + tok_ptr=tok_mem+1; text_ptr=tok_start+1; scrap_ptr=scrap_info; + /* forget the tokens and the scraps */ +} + +@ Keeping in line with the conventions of the \CEE/ preprocessor (and +otherwise contrary to the rules of \.{CWEB}) we distinguish here +between the case that `\.(' immediately follows an identifier and the +case that the two are separated by a space. In the latter case, and +if the identifier is not followed by `\.(' at all, the replacement +text starts immediately after the identifier. In the former case, +it starts after we scan the matching `\.)'. + +@<Start a macro...@>= { + 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 }' */ +@.\\D@> + if ((next_control=get_next())!=identifier) + err_print("! Improper macro definition"); +@.Improper macro definition@> + else { + app('$'); app_cur_id(0); + if (*loc=='(') + reswitch: switch (next_control=get_next()) { + case '(': case ',': app(next_control); goto reswitch; + case identifier: app_cur_id(0); goto reswitch; + case ')': app(next_control); next_control=get_next(); break; + default: err_print("! Improper macro definition"); break; + } + else next_control=get_next(); + app_str("$ "); app(break_space); + app_scrap(dead,no_math); /* scrap won't take part in the parsing */ + } +} + +@ @<Start a format...@>= { + doing_format=1; + 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 }' */ +@.\\F@> + next_control=get_next(); + if (next_control==identifier) { + app(id_flag+(int)(id_lookup(id_first, id_loc,normal)-name_dir)); + app(' '); + app(break_space); /* this is syntactically separate from what follows */ + next_control=get_next(); + if (next_control==identifier) { + app(id_flag+(int)(id_lookup(id_first, id_loc,normal)-name_dir)); + app_scrap(exp,maybe_math); app_scrap(semi,maybe_math); + next_control=get_next(); + } + } + if (scrap_ptr!=scrap_info+2) err_print("! Improper format definition"); +@.Improper format definition@> +} + +@ Finally, when the \TEX/ and definition parts have been treated, we have +|next_control>=begin_C|. We will make the global variable |this_section| +point to the current section name, if it has a name. + +@<Global...@>= +name_pointer this_section; /* the current section name, or zero */ + +@ @<Translate the \CEE/...@>= +this_section=name_dir; +if (next_control<=section_name) { + emit_space_if_needed; init_stack; + if (next_control==begin_C) next_control=get_next(); + else { + this_section=cur_section; + @<Check that '=' or '==' follows this section name, and + emit the scraps to start the section definition@>; + } + while (next_control<=section_name) { + outer_parse(); + @<Emit the scrap for a section name if present@>; + } + finish_C(1); +} + +@ The title of the section and an $\E$ or $\mathrel+\E$ are made +into a scrap that should not take part in the parsing. + +@<Check that '='...@>= +do next_control=get_next(); + while (next_control=='+'); /* allow optional `\.{+=}' */ +if (next_control!='=' && next_control!=eq_eq) + err_print("! You need an = sign after the section name"); +@.You need an = sign...@> + else next_control=get_next(); +if (out_ptr>out_buf+1 && *out_ptr=='Y' && *(out_ptr-1)=='\\') app(backup); + /* the section name will be flush left */ +@.\\Y@> +app(section_flag+(int)(this_section-name_dir)); +cur_xref=(xref_pointer)this_section->xref; +if(cur_xref->num==file_flag) cur_xref=cur_xref->xlink; +app_str("${}"); +if (cur_xref->num!=section_count+def_flag) { + app_str("\\mathrel+"); /*section name is multiply defined*/ + this_section=name_dir; /*so we won't give cross-reference info here*/ +} +app_str("\\E"); /* output an equivalence sign */ +@.\\E@> +app_str("{}$"); +app(force); app_scrap(dead,no_math); + /* this forces a line break unless `\.{@@+}' follows */ + +@ @<Emit the scrap...@>= +if (next_control<section_name) { + err_print("! You can't do that in C text"); +@.You can't do that...@> + next_control=get_next(); +} +else if (next_control==section_name) { + app(section_flag+(int)(cur_section-name_dir)); + app_scrap(section_scrap,maybe_math); + next_control=get_next(); +} + +@ Cross references relating to a named section are given +after the section ends. + +@<Show cross...@>= +if (this_section>name_dir) { + cur_xref=(xref_pointer)this_section->xref; + if (cur_xref->num==file_flag){an_output=1;cur_xref=cur_xref->xlink;} + else an_output=0; + if (cur_xref->num>def_flag) + cur_xref=cur_xref->xlink; /* bypass current section number */ + footnote(def_flag); footnote(cite_flag); footnote(0); +} + +@ The |footnote| procedure gives cross-reference information about +multiply defined section names (if the |flag| parameter is +|def_flag|), or about references to a section name +(if |flag==cite_flag|), or to its uses (if |flag==0|). It assumes that +|cur_xref| points to the first cross-reference entry of interest, and it +leaves |cur_xref| pointing to the first element not printed. Typical outputs: +`\.{\\A101.}'; `\.{\\Us 370\\ET1009.}'; +`\.{\\As 8, 27\\*\\ETs64.}'. + +Note that the output of \.{CWEAVE} is not English-specific; users may +supply new definitions for the macros \.{\\A}, \.{\\As}, etc. + +@<Predecl...@>= +void footnote(); + +@ @c +void +footnote(flag) /* outputs section cross-references */ +sixteen_bits flag; +{ + xref_pointer q; /* cross-reference pointer variable */ + if (cur_xref->num<=flag) return; + finish_line(); out('\\'); +@.\\A@> +@.\\Q@> +@.\\U@> + out(flag==0? 'U': flag==cite_flag? 'Q': 'A'); + @<Output all the section numbers on the reference list |cur_xref|@>; + out('.'); +} + +@ The following code distinguishes three cases, according as the number +of cross-references is one, two, or more than two. Variable |q| points +to the first cross-reference, and the last link is a zero. + +@<Output all the section numbers...@>= +q=cur_xref; if (q->xlink->num>flag) out('s'); /* plural */ +while (1) { + out_section(cur_xref->num-flag); + cur_xref=cur_xref->xlink; /* point to the next cross-reference to output */ + if (cur_xref->num<=flag) break; + if (cur_xref->xlink->num>flag) out_str(", "); /* not the last */ + else {out_str("\\ET"); /* the last */ +@.\\ET@> + if (cur_xref != q->xlink) out('s'); /* the last of more than two */ + } +} + +@ @<Output the code for the end of a section@>= +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. + +@<Predecl...@>= +void phase_three(); + +@ @c +void +phase_three() { +if (no_xref) { + finish_line(); + out_str("\\end"); +@.\\end@> + finish_line(); +} +else { + phase=3; if (show_progress) printf("\nWriting the index..."); +@.Writing the index...@> + finish_line(); + if ((idx_file=fopen(idx_file_name,"w"))==NULL) + fatal("! Cannot open index file ",idx_file_name); +@.Cannot open index file@> + if (change_exists) { + @<Tell about changed sections@>; finish_line(); finish_line(); + } + out_str("\\inx"); finish_line(); +@.\\inx@> + active_file=idx_file; /* change active file to the index file */ + @<Do the first pass of sorting@>; + @<Sort and output the index@>; + finish_line(); fclose(active_file); /* finished with |idx_file| */ + active_file=tex_file; /* switch back to |tex_file| for a tic */ + out_str("\\fin"); finish_line(); +@.\\fin@> + if ((scn_file=fopen(scn_file_name,"w"))==NULL) + fatal("! Cannot open section file ",scn_file_name); +@.Cannot open section file@> + active_file=scn_file; /* change active file to section listing file */ + @<Output all the section names@>; + finish_line(); fclose(active_file); /* finished with |scn_file| */ + active_file=tex_file; + if (group_found) out_str("\\con");@+else out_str("\\end"); +@.\\con@> +@.\\end@> + finish_line(); + fclose(active_file); +} +if (show_happiness) printf("\nDone."); +check_complete(); /* was all of the change file used? */ +} + +@ Just before the index comes a list of all the changed sections, including +the index section itself. + +@<Global...@>= +sixteen_bits k_section; /* runs through the sections */ + +@ @<Tell about changed 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<section_count) { + while (!changed_section[++k_section]); + out_str(", "); out_section(k_section); + } + out('.'); +} + +@ 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 +identifiers into 102 different lists based on their first characters. +(Uppercase letters are put into the same list as the corresponding lowercase +letters, since we want to have `$t<\\{TeX}<\&{to}$'.) The +list for character |c| begins at location |bucket[c]| and continues through +the |blink| array. + +@<Global...@>= +name_pointer bucket[256]; +name_pointer next_name; /* successor of |cur_name| when sorting */ +name_pointer blink[max_names]; /* links in the buckets */ + +@ To begin the sorting, we go through all the hash lists and put each entry +having a nonempty cross-reference list into the proper bucket. + +@<Do the first pass...@>= { +int c; +for (c=0; c<=255; c++) bucket[c]=NULL; +for (h=hash; h<=hash_end; h++) { + next_name=*h; + while (next_name) { + cur_name=next_name; next_name=cur_name->link; + if (cur_name->xref!=(char*)xmem) { + c=(eight_bits)((cur_name->byte_start)[0]); + if (xisupper(c)) c=tolower(c); + blink[cur_name-name_dir]=bucket[c]; bucket[c]=cur_name; + } + } +} +} + +@ During the sorting phase we shall use the |cat| and |trans| arrays from +\.{CWEAVE}'s parsing algorithm and rename them |depth| and |head|. They now +represent a stack of identifier lists for all the index entries that have +not yet been output. The variable |sort_ptr| tells how many such lists are +present; the lists are output in reverse order (first |sort_ptr|, then +|sort_ptr-1|, etc.). The |j|th list starts at |head[j]|, and if the first +|k| characters of all entries on this list are known to be equal we have +|depth[j]==k|. + +@ @<Rest of |trans_plus| union@>= +name_pointer Head; + +@ @d depth cat /* reclaims memory that is no longer needed for parsing */ +@d head trans_plus.Head /* ditto */ +@f sort_pointer int +@d sort_pointer scrap_pointer /* ditto */ +@d sort_ptr scrap_ptr /* ditto */ +@d max_sorts max_scraps /* ditto */ + +@<Global...@>= +eight_bits cur_depth; /* depth of current buckets */ +char *cur_byte; /* index into |byte_mem| */ +sixteen_bits cur_val; /* current cross-reference number */ +sort_pointer max_sort_ptr; /* largest value of |sort_ptr| */ + +@ @<Set init...@>= +max_sort_ptr=scrap_info; + +@ The desired alphabetic order is specified by the |collate| array; namely, +$|collate|[0]<|collate|[1]<\cdots<|collate|[100]$. + +@<Global...@>= +eight_bits collate[102+128]; /* collation order */ +@^high-bit character handling@> + +@ We use the order $\hbox{null}<\.\ <\hbox{other characters}<{}$\.\_${}< +\.A=\.a<\cdots<\.Z=\.z<\.0<\cdots<\.9.$ Warning: The collation mapping +needs to be changed if ASCII code is not being used. +@^ASCII code dependencies@> +@^high-bit character handling@> + +We initialize |collate| by copying a few characters at a time, because +some \CEE/ compilers choke on long strings. + +@<Set init...@>= +collate[0]=0; +strcpy(collate+1," \1\2\3\4\5\6\7\10\11\12\13\14\15\16\17"); +/* 16 characters + 1 = 17 */ +strcpy(collate+17,"\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37"); +/* 16 characters + 17 = 33 */ +strcpy(collate+33,"!\42#$%&'()*+,-./:;<=>?@@[\\]^`{|}~_"); +/* 32 characters + 33 = 65 */ +strcpy(collate+65,"abcdefghijklmnopqrstuvwxyz0123456789"); +/* (26 + 10) characters + 65 = 101 */ +strcpy(collate+101,"\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217"); +/* 16 characters + 101 = 117 */ +strcpy(collate+117,"\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237"); +/* 16 characters + 117 = 133 */ +strcpy(collate+133,"\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257"); +/* 16 characters + 133 = 149 */ +strcpy(collate+149,"\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277"); +/* 16 characters + 149 = 165 */ +strcpy(collate+165,"\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317"); +/* 16 characters + 165 = 181 */ +strcpy(collate+181,"\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337"); +/* 16 characters + 181 = 197 */ +strcpy(collate+197,"\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357"); +/* 16 characters + 197 = 213 */ +strcpy(collate+213,"\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377"); +/* 16 characters + 213 = 229 */ + +@ Procedure |unbucket| goes through the buckets and adds nonempty lists +to the stack, using the collating sequence specified in the |collate| array. +The parameter to |unbucket| tells the current depth in the buckets. +Any two sequences that agree in their first 255 character positions are +regarded as identical. + +@d infinity 255 /* $\infty$ (approximately) */ + +@<Predecl...@>= +void unbucket(); + +@ @c +void +unbucket(d) /* empties buckets having depth |d| */ +eight_bits d; +{ + int c; /* index into |bucket|; cannot be a simple |char| because of sign + comparison below*/ + for (c=100+128; c>= 0; c--) if (bucket[collate[c]]) { +@^high-bit character handling@> + if (sort_ptr>=scrap_info_end) overflow("sorting"); + sort_ptr++; + if (sort_ptr>max_sort_ptr) max_sort_ptr=sort_ptr; + if (c==0) sort_ptr->depth=infinity; + else sort_ptr->depth=d; + sort_ptr->head=bucket[collate[c]]; bucket[collate[c]]=NULL; + } +} + +@ @<Sort and output...@>= +sort_ptr=scrap_info; unbucket(1); +while (sort_ptr>scrap_info) { + cur_depth=sort_ptr->depth; + if (blink[sort_ptr->head-name_dir]==0 || cur_depth==infinity) + @<Output index entries for the list at |sort_ptr|@>@; + else @<Split the list at |sort_ptr| into further lists@>; +} + +@ @<Split the list...@>= { + eight_bits c; + next_name=sort_ptr->head; + do { + cur_name=next_name; next_name=blink[cur_name-name_dir]; + cur_byte=cur_name->byte_start+cur_depth; + if (cur_byte==(cur_name+1)->byte_start) c=0; /* hit end of the name */ + else { + c=(eight_bits) *cur_byte; + if (xisupper(c)) c=tolower(c); + } + blink[cur_name-name_dir]=bucket[c]; bucket[c]=cur_name; + } while (next_name); + --sort_ptr; unbucket(cur_depth+1); +} + +@ @<Output index...@>= { + cur_name=sort_ptr->head; + do { + out_str("\\I"); +@.\\I@> + @<Output the name at |cur_name|@>; + @<Output the cross-references at |cur_name|@>; + cur_name=blink[cur_name-name_dir]; + } while (cur_name); + --sort_ptr; +} + +@ @<Output the name...@>= +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; + out_str("\\."); break; +lowcase: out_str("\\\\"); + } + break; +@.\\|@> +@.\\.@> +@.\\\\@> + 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 +`\.{\\[}$\,\ldots\,$\.]'. + +@<Output the cross-references...@>= +@<Invert the cross-reference list at |cur_name|, making |cur_xref| the head@>; +do { + out_str(", "); cur_val=cur_xref->num; + if (cur_val<def_flag) out_section(cur_val); + else {out_str("\\["); out_section(cur_val-def_flag); out(']');} +@.\\[@> + cur_xref=cur_xref->xlink; +} while (cur_xref!=xmem); +out('.'); finish_line(); + +@ List inversion is best thought of as popping elements off one stack and +pushing them onto another. In this case |cur_xref| will be the head of +the stack that we push things onto. +@<Global...@>= +xref_pointer next_xref, this_xref; + /* pointer variables for rearranging a list */ + +@ @<Invert the cross-reference list at |cur_name|, making |cur_xref| the head@>= +this_xref=(xref_pointer)cur_name->xref; cur_xref=xmem; +do { + next_xref=this_xref->xlink; this_xref->xlink=cur_xref; + cur_xref=this_xref; this_xref=next_xref; +} while (this_xref!=xmem); + +@ The following recursive procedure walks through the tree of section names and +prints them. +@^recursion@> + +@<Predecl...@>= +void section_print(); + +@ @c +void +section_print(p) /* print all section names in subtree |p| */ +name_pointer p; +{ + if (p) { + section_print(p->llink); out_str("\\I"); +@.\\I@> + tok_ptr=tok_mem+1; text_ptr=tok_start+1; scrap_ptr=scrap_info; init_stack; + app(p-name_dir+section_flag); make_output(); + footnote(cite_flag); + footnote(0); /* |cur_xref| was set by |make_output| */ + finish_line();@/ + section_print(p->rlink); + } +} + +@ @<Output all the section names@>=section_print(root) + +@ Because on some systems the difference between two pointers is a |long| +rather than an |int|, we use \.{\%ld} to print these quantities. + +@c +void +print_stats() { + printf("\nMemory usage statistics:\n"); +@.Memory usage statistics:@> + printf("%ld names (out of %ld)\n", + (long)(name_ptr-name_dir),(long)max_names); + printf("%ld cross-references (out of %ld)\n", + (long)(xref_ptr-xmem),(long)max_refs); + printf("%ld bytes (out of %ld)\n", + (long)(byte_ptr-byte_mem),(long)max_bytes); + printf("Parsing:\n"); + printf("%ld scraps (out of %ld)\n", + (long)(max_scr_ptr-scrap_info),(long)max_scraps); + printf("%ld texts (out of %ld)\n", + (long)(max_text_ptr-tok_start),(long)max_texts); + printf("%ld tokens (out of %ld)\n", + (long)(max_tok_ptr-tok_mem),(long)max_toks); + printf("%ld levels (out of %ld)\n", + (long)(max_stack_ptr-stack),(long)stack_size); + printf("Sorting:\n"); + printf("%ld levels (out of %ld)\n", + (long)(max_sort_ptr-scrap_info),(long)max_scraps); +} + +@** Index. +If you have read and understood the code for Phase III above, you know what +is in this index and how it got here. All sections in which an identifier is +used are listed with that identifier, except that reserved words are +indexed only when they appear in format definitions, and the appearances +of identifiers in section names are not indexed. Underlined entries +correspond to where the identifier was declared. Error messages, control +sequences put into the output, and a few +other things like ``recursion'' are indexed here too. diff --git a/web/c_cpp/cweb/cweb.1 b/web/c_cpp/cweb/cweb.1 new file mode 100644 index 0000000000..ad41c427ea --- /dev/null +++ b/web/c_cpp/cweb/cweb.1 @@ -0,0 +1,138 @@ +.TH CWEB 1L 2002-Apr-13 +. +.SH NAME +ctangle, cweave \- translate CWEB to C and/or TeX +. +.SH SYNOPSIS +.na +.B ctangle +[ +.B \-bhp +] [ +.B +s +] webfile[.w] [changefile[.ch]] [outputfile[.c]] +.br +.B cweave +[ +.B \-befhpx +] [ +.B +s +] webfile[.w] [changefile[.ch]] [outputfile[.tex]] +.ad +. +.SH DESCRIPTION +The +.I ctangle +program converts a CWEB +source document into a C\ program that may be compiled in the usual way. +The output file includes #line specifications so that debugging can be +done in terms of the CWEB source file. +.PP +The +.I cweave +program converts the same CWEB file into a TeX file that may be +formatted and printed in the usual way. +It takes appropriate care of typographic details like page +layout and the use of indentation, italics, boldface, etc., and it supplies +extensive cross-index information that it gathers automatically. +.PP +CWEB allows you to prepare a single +document containing all the information that is needed both to produce +a compilable C\ program and to produce a well-formatted document +describing the program in as much detail as the writer may desire. +The user of CWEB ought to be familiar with TeX as well as\ C. +.PP +The command line should have one, two, or three names on it. +The first is taken as the CWEB file (and .w is added if there is no +extension). +If that file cannot be opened, the extension .web is tried instead. +(But .w is recommended, since .web usually implies Pascal.) +If there is a second name, it is a change file (and .ch is added if there is +no extension). +The change file overrides parts of the WEB file, +as described in the documentation. +If there is a third name, it overrides +the default name of the output file, which is ordinarily the same as +the name of the input file (but on the current directory) with the +extension .c or .tex. +.PP +Options in the command line may be either turned off with\ \- +(if they are on by default) or turned on with\ + (if they are off by +default). +In fact, the options are processed from left to right, +so a sequence like -f\ +f corresponds to +f (which is the default). +.PP +The +.B \-b +option suppresses the banner line that normally appears on your terminal +when ctangle or cweave begins. +The +.B \-h +option suppresses the happy message that normally appears if the processing +was successful. +The +.B \-p +option suppresses progress reports (starred module numbers) as the processing +takes place. +If you say -bhp, you get nothing but error messages. +.PP +The +.B +s +option prints statistics about memory usage at the end of a run +(assuming that the programs have been compiled with the -DSTAT switch). +.PP +There are three other options applicable to +.I cweave +only: +.B \-f +means do not force a newline after every statement in the formatted output. +.B \-e +inhibits the enclosure of C\ material formatted by +.I cweave +in brackets +\ePB{...}. +Such brackets are normally inserted so that special hooks +can be used by +.I cweb-latex +and similar programs. +.B \-x +means omit the index and table of contents. +. +.SH FILES +.TP +/usr/local/lib/tex/inputs/cwebmac.tex +TeX macros used by cweave output. +.TP +/usr/local/src/cweb/cwebman.tex +The user manual. +.TP +/usr/local/src/cweb/examples/wc.w +An introductory example. +.TP +/usr/local/src/cweb/examples/wmerge.w +Patch program based on CWEB-style change files. +.TP +/usr/local/lib/cweb +Directory for cweb "include" files. +. +.SH "SEE ALSO" +.TP +.I Literate Programming +by D. E. Knuth +.TP +.I Weaving a Program +by Wayne Sewell +.TP +.I The CWEB System of Structured Documentation +by Donald E. Knuth and Silvio Levy (hardcopy version of cwebman.tex +and the source code listings) +.TP +tex(1), cc(1) +. +.SH AUTHORS +Don Knuth wrote WEB for TeX and Pascal. +Silvio Levy designed and developed CWEB +by adapting the WEB conventions to\ C and by recoding everything in CWEB. +Knuth began using CWEB and made further refinements. +Many other helpers are acknowledged in the CWEB manual. + diff --git a/web/c_cpp/cweb/cweb.el b/web/c_cpp/cweb/cweb.el new file mode 100644 index 0000000000..c243f35b2e --- /dev/null +++ b/web/c_cpp/cweb/cweb.el @@ -0,0 +1,218 @@ +;; This file contains extensions to GNU-Emacs, to wit: +; (1) some WEB-oriented functions that are also of general use +; (2) changes to the GNU-distributed TeX mode +; (3) definitions of simple WEB and CWEB modes + +; To use: Put this in your EMACS-lisp library and say (load-library "cweb") +; in your .emacs init file. + +; Contributed by Don Knuth, July 1990 + +;; OK, here's part (1): some WEB-oriented functions whose main purpose is +; to maintain a stack of module names that are "pending" as you are writing +; a program. When you first think of a module that needs to be written later, +; put it into the pending list (by typing CTL-Z instead of @> after the +; name). Later you can say CTL-\ to retrieve a pending name (and if +; you want to cycle through the pending names, ESC-y after CTL-\ will +; do it, just as ESC-y works after a yank). +; After you've said CTL-\, the current region is the name just removed from +; the pending list. If you change your mind, you can put it back again by +; saying ESC-\. If you had put it into the pending list by mistake, you +; can get rid of it by using the normal CTL-W operation (kill-region). +; The following code binds the new commands to CTL-Z, CTL-\, and ESC-\ +; in all modes. You may prefer other bindings, of course. +; CTL-Z is normally "suspend emacs", but it is best not used when emacs +; has its own window as it usually does nowadays; if you need the +; old CTL-Z, you might rather bind it to CTL-X CTL-Z. +; CTL-\ is normally undefined. +; ESC-\ is normally "delete space", but ESC-space DEL does that easily too. + +(defvar pending-list nil + "List of strings (usually WEB module names) still pending.") + +(defun into-pending-list (beg end) + "Copy region into pending-list." + (interactive "r") + (indicate-region) + (setq pending-list (cons (buffer-substring beg end) pending-list))) + +(defun new-module-name-pending () + "Insert @> to complete a module name, then put it into pending-list." + (interactive) + (insert "@>") + (push-mark) + (if (search-backward "@<" nil t) + (progn + (exchange-point-and-mark) + (into-pending-list (point) (mark)) + ) + (message "There's no @< to begin the module name!"))) +(global-set-key "\C-z" 'new-module-name-pending) + +(defun pop-pending-list (arg) + "Remove first element of pending-list and insert it as current region. +With argument, put point at left; otherwise point will follow the insertion. +Say \\[new-yank-pop] to replace this by another element of the list. +Say \\[into-pending-list] to put it back in the list." + (interactive "*P") + (if (consp pending-list) + (progn + (push-mark (point)) + (insert (car pending-list)) + (setq pending-list (cdr pending-list)) + (if arg + (exchange-point-and-mark))) + (message "Nothing is pending.") + (setq this-command nil))) +(global-set-key "\C-\\" 'pop-pending-list) +(global-set-key "\M-\\" 'into-pending-list) + +(defun new-yank-pop (arg) + "If previous command was \\[pop-pending-list], pop a different string; +otherwise do an ordinary Meta-y." + (interactive "*p") + (if (eq last-command 'pop-pending-list) + (let (xch) + (setq xch (< (point) (mark))) + (setq pending-list (append pending-list + (list (buffer-substring (point) (mark))))) + (delete-region (point) (mark)) + (setq this-command 'pop-pending-list) + (pop-pending-list xch)) + (yank-pop arg))) +(global-set-key "\M-y" 'new-yank-pop) + +(defun indicate-region () + "Bounce cursor to mark and back again" + (let ((point-save (point))) + (unwind-protect + (progn (goto-char (mark)) + (sit-for 0 300)) ;; wait 300 milliseconds + (goto-char point-save)))) + +; I prefer to change the standard copy-region command to the following, +; which gives me visual feedback about what I've copied to the kill ring: +(defun indicate-and-copy-region (beg end) + "Indicate current region, then copy it to the kill ring." + (interactive "r")(indicate-region)(copy-region-as-kill beg end)) +(global-set-key "\M-w" 'indicate-and-copy-region) + +; Here's another convenient command, bound to the usually unused ESC-". +(defun ditto (arg) + "Copy ARG characters from the line above." + (interactive "*p") + (let (ch) + (while (> arg 0) + (setq temporary-goal-column (current-column)) + (save-excursion + (previous-line 1) + (setq ch (following-char))) + (insert ch) + (setq arg (1- arg))))) +(global-set-key "\M-\"" 'ditto) +; If "ditto" suddenly fails to work, you have probably set goal-column +; inadvertently. To unset it, say C-u C-x C-n. + +;; OK, here's part (2): Changes to TeX mode. +; The WEB modes below are very much like TeX mode, but some improvements were +; desirable in TeX mode: +; I made newline act as it does in indented-text mode, since this +; works nicely for both TeX and WEB (Pascal or C code). +; I made RET check for unmatched delimiters if it ends a paragraph. +; Otherwise TeX mode remains as it was before. + +(setq TeX-mode-map (make-sparse-keymap)) +(define-key TeX-mode-map "\C-c\C-k" 'TeX-kill-job) +(define-key TeX-mode-map "\C-c\C-l" 'TeX-recenter-output-buffer) +(define-key TeX-mode-map "\C-c\C-q" 'TeX-show-print-queue) +(define-key TeX-mode-map "\C-c\C-p" 'TeX-print) +(define-key TeX-mode-map "\"" 'TeX-insert-quote) +(define-key TeX-mode-map "\e}" 'up-list) +(define-key TeX-mode-map "\e{" 'TeX-insert-braces) +(define-key TeX-mode-map "\C-c\C-r" 'TeX-region) +(define-key TeX-mode-map "\C-c\C-b" 'TeX-buffer) +(define-key TeX-mode-map "\C-c\C-f" 'TeX-close-LaTeX-block) +(define-key TeX-mode-map "\r" 'TeX-newline) +(define-key TeX-mode-map "\t" 'indent-relative) +(setq TeX-mode-hook '(lambda () + (make-local-variable 'indent-line-function) + (setq indent-line-function 'indent-relative-maybe))) + +(defun TeX-newline (arg) +"If previous character is newline and no ARG, check for unbalanced braces +and/or dollar signs in previous paragraph. If ARG is \\[universal-argument], +do a single newline; otherwise do ordinary newline." + (interactive "*P") + (if (and (eq (preceding-char) ?\n) (not arg)) + (TeX-check-paragraph) + (if (listp arg) + (newline) + (newline arg)))) + +(defun TeX-check-paragraph () +"Insert a newline following a newline, breaking a paragraph for TeX. +Check for mismatched delimiters in paragraph being terminated." + (interactive) + (if (TeX-validate-paragraph + (save-excursion + (search-backward "\n\n" nil 'move) + (point)) + (point)) + (insert ?\n) + (insert ?\n) + (error "Mismatched delimiters in that paragraph?"))) + +;; and now, part (3): WEB and CWEB modes. +; These are like plain TeX mode except that the automatic conversion of +; " to `` or '' is disabled. (Personally I never liked that feature anyway, +; since it's easy to get used to typing `` and ''. In WEB modes, the +; feature soon becomes intolerable, unless you never use string constants!) +; Another thing distinguishing WEB mode from TeX is ESC-p and ESC-n, to +; move to previous or next module. These keys are usually unbound, except +; when processing email. + +(defun forward-module (arg) +"Advance past next WEB module beginning; with ARG, repeat ARG times." + (interactive "p") + (move-to-module arg)) +(defun backward-module (arg) +"Advance to previous WEB module beginning; with ARG, repeat ARG times." + (interactive "p") + (move-to-module (- arg))) +(defun move-to-module (arg) + (while (> arg 0) + (re-search-forward "@ \\|@\\*\\|@\n") + (setq arg (1- arg))) + (while (< arg 0) + (re-search-backward "@ \\|@\\*\\|@\n") + (setq arg (1+ arg)))) + +(defun web-mode () + "Major mode like TeX mode plus \\[forward-module] and \\[backward-module] +for relative module movement. The automatic \" feature is disabled." + (interactive) + (plain-tex-mode) + (local-set-key "\M-n" 'forward-module) + (local-set-key "\M-p" 'backward-module) + (local-set-key "\"" 'self-insert-command) + (setq mode-name "WEB") + (setq major-mode 'web-mode) + (run-hooks 'web-mode-hook)) +(setq auto-mode-alist (cons '("\\.web$" . web-mode) auto-mode-alist)) + +(defun cweb-mode () + "Major mode like TeX mode plus \\[forward-module] and \\[backward-module] +for relative module movement. The automatic \" feature is disabled." + (interactive) + (plain-tex-mode) + (local-set-key "\M-n" 'forward-module) + (local-set-key "\M-p" 'backward-module) + (local-set-key "\"" 'self-insert-command) + (setq comment-start nil) + (modify-syntax-entry ?% "@") + (setq mode-name "CWEB") + (setq major-mode 'cweb-mode) + (setq tex-fontify-script nil) ;; needed in GNU Emacs version 22? + (run-hooks 'cweb-mode-hook)) +(setq auto-mode-alist (cons '("\\.w$" . cweb-mode) auto-mode-alist)) +(setq auto-mode-alist (cons '("\\.ch$" . cweb-mode) auto-mode-alist)) diff --git a/web/c_cpp/cweb/cwebmac.tex b/web/c_cpp/cweb/cwebmac.tex new file mode 100644 index 0000000000..60f86ce5a9 --- /dev/null +++ b/web/c_cpp/cweb/cwebmac.tex @@ -0,0 +1,553 @@ +% standard macros for CWEB listings (in addition to plain.tex) +% Version 3.70 --- July 2017 +\ifx\renewenvironment\undefined\else\endinput\fi % LaTeX will use other macros +\xdef\fmtversion{\fmtversion+CWEB3.70} +\chardef\cwebversion=3 \chardef\cwebrevision=70 +\newif\ifpdf +\ifx\pdf+\pdftrue\fi +% Uncomment the following line if you want PDF goodies to be the default +%\ifx\pdf-\else\pdftrue\fi +\def\pdflinkcolor{0 0 1} % the RGB values for hyperlink color +\newif\ifpdftex +\ifx\pdfoutput\undefined \pdftexfalse \else\ifnum\pdfoutput=0 \pdftexfalse +%\else \pdftextrue \pdfoutput=1 \input pdfcolor \let\setcolor\pdfsetcolor \fi\fi +\else \pdftextrue \pdfoutput=1 % changed in 3.69 + \def\Black{\pdfliteral{0 g 0 G}} % use rgb colors for direct PDF output too + \def\Blue{\pdfliteral{0 0 1 rg 0 0 1 RG}} +\fi\fi +\newif\ifacro \ifpdf\acrotrue\fi \ifpdftex\acrotrue\fi + +\let\:=\. % preserve a way to get the dot accent + % (all other accents will still work as usual) + +\parskip 0pt % no stretch between paragraphs +\parindent 1em % for paragraphs and for the first line of C text + +\font\ninerm=cmr9 +\let\mc=\ninerm % medium caps +\def\CEE/{{\mc C\spacefactor1000}} +\def\UNIX/{{\mc U\kern-.05emNIX\spacefactor1000}} +\def\TEX/{\TeX} +\def\CPLUSPLUS/{{\mc C\PP\spacefactor1000}} +\def\Cee{\CEE/} % for backward compatibility +\def\9#1{} + % with this definition of \9 you can say @:sort key}{TeX code@> + % to alphabetize an index entry by the sort key but format with the TeX code +\font\eightrm=cmr8 +\let\sc=\eightrm % for smallish caps (NOT a caps-and-small-caps font) +\let\mainfont=\tenrm +\let\cmntfont\tenrm +%\font\tenss=cmss10 \let\cmntfont\tenss % alternative comment font +\font\titlefont=cmr7 scaled\magstep4 % title on the contents page +\font\ttitlefont=cmtt10 scaled\magstep2 % typewriter type in title +\font\tentex=cmtex10 % TeX extended character set (used in strings) +\fontdimen7\tentex=0pt % no double space after sentences + +\def\\#1{\leavevmode\hbox{\it#1\/\kern.05em}} % italic type for identifiers +\def\|#1{\leavevmode\hbox{$#1$}} % one-letter identifiers look better this way +\def\{\leavevmode\hbox{\bf + \def\_{\kern.04em\vbox{\hrule width.3em height .6pt}\kern.08em}% + #1\/\kern.05em}} % boldface type for reserved words +\def\.#1{\leavevmode\hbox{\tentex % typewriter type for strings + \let\\=\BS % backslash in a string + \let\{=\LB % left brace in a string + \let\}=\RB % right brace in a string + \let\~=\TL % tilde in a string + \let\ =\SP % space in a string + \let\_=\UL % underline in a string + \let\&=\AM % ampersand in a string + \let\^=\CF % circumflex in a string + #1\kern.05em}} +\def\){{\tentex\kern-.05em}\discretionary{\hbox{\tentex\BS}}{}{}} +\def\AT{@} % at sign for control text (not needed in versions >= 2.9) +\def\ATL{\par\noindent\bgroup\catcode`\_=12 \postATL} % print @l in limbo +\def\postATL#1 #2 {\bf letter \\{\uppercase{\char"#1}} + tangles as \tentex "#2"\egroup\par} +\def\noATL#1 #2 {} +\def\noatl{\let\ATL=\noATL} % suppress output from @l +\def\ATH{{\acrofalse\X\kern-.5em:Preprocessor definitions\X}} +\let\PB=\relax % hook for program brackets |...| in TeX part or section name + +\chardef\AM=`\& % ampersand character in a string +\chardef\BS=`\\ % backslash in a string +\chardef\LB=`\{ % left brace in a string +\chardef\RB=`\} % right brace in a string +\def\SP{{\tt\char`\ }} % (visible) space in a string +\chardef\TL=`\~ % tilde in a string +\chardef\UL=`\_ % underline character in a string +\chardef\CF=`\^ % circumflex character in a string + +\newbox\PPbox % symbol for ++ +\setbox\PPbox=\hbox{\kern.5pt\raise1pt\hbox{\sevenrm+\kern-1pt+}\kern.5pt} +\def\PP{\copy\PPbox} +\newbox\MMbox \setbox\MMbox=\hbox{\kern.5pt\raise1pt\hbox{\sevensy\char0 + \kern-1pt\char0}\kern.5pt} +\def\MM{\copy\MMbox} +\newbox\MGbox % symbol for -> +\setbox\MGbox=\hbox{\kern-2pt\lower3pt\hbox{\teni\char'176}\kern1pt} +\def\MG{\copy\MGbox} +\def\MRL#1{\mathrel{\let\K==#1}} +%\def\MRL#1{\KK#1}\def\KK#1#2{\buildrel\;#1\over{#2}} +\let\GG=\gg +\let\LL=\ll +\let\NULL=\Lambda +\mathchardef\AND="2026 % bitwise and; also \& (unary operator) +\let\OR=\mid % bitwise or +\let\XOR=\oplus % bitwise exclusive or +\def\CM{{\sim}} % bitwise complement +\newbox\MODbox \setbox\MODbox=\hbox{\eightrm\%} +\def\MOD{\mathbin{\copy\MODbox}} +\def\DC{\kern.1em{::}\kern.1em} % symbol for :: +\def\PA{\mathbin{.*}} % symbol for .* +\def\MGA{\mathbin{\MG*}} % symbol for ->* +\def\this{\&{this}} + +\newbox\bak \setbox\bak=\hbox to -1em{} % backspace one em +\newbox\bakk\setbox\bakk=\hbox to -2em{} % backspace two ems + +\newcount\ind % current indentation in ems +\def\1{\global\advance\ind by1\hangindent\ind em} % indent one more notch +\def\2{\global\advance\ind by-1} % indent one less notch +\def\3#1{\hfil\penalty#10\hfilneg} % optional break within a statement +\def\4{\copy\bak} % backspace one notch +\def\5{\hfil\penalty-1\hfilneg\kern2.5em\copy\bakk\ignorespaces}% optional break +\def\6{\ifmmode\else\par % forced break + \hangindent\ind em\noindent\kern\ind em\copy\bakk\ignorespaces\fi} +\def\7{\Y\6} % forced break and a little extra space +\def\8{\hskip-\ind em\hskip 2em} % no indentation + +\newcount\gdepth % depth of current major group, plus one +\newcount\secpagedepth +\secpagedepth=3 % page breaks will occur for depths -1, 0, and 1 +\newtoks\gtitle % title of current major group +\newskip\intersecskip \intersecskip=12pt minus 3pt % space between sections +\let\yskip=\smallskip +\def\?{\mathrel?} +\def\note#1#2.{\Y\noindent{\hangindent2em% + \baselineskip10pt\eightrm#1~\ifacro{\pdfnote#2.}\else#2\fi.\par}} + +\newtoks\toksA \newtoks\toksB \newtoks\toksC \newtoks\toksD +\newtoks\toksE \newtoks\toksF \newtoks\usersanitizer +\newcount\countA \countA=0 \newcount\countB \countB=0 +\newcount\countC \countC=0 +\newif\iftokprocessed \newif\ifTnum \newif\ifinstr +{\def\\{\global\let\spacechar= }\\ } + +\ifacro % The following are pdf macros +\def\thewidth{\the\wd0 \space} +\def\theheight{\the\ht\strutbox\space} +\def\thedepth{\the\dp\strutbox\space} +\ifpdftex + \ifx\pdfannotlink\undefined\let\pdfannotlink\pdfstartlink\fi% for pdfTeX 0.14 + \def\pdflink#1#2{\hbox{\pdfannotlink height\ht\strutbox depth\dp\strutbox + attr{/Border [0 0 0]} goto num #1 \Blue #1\Black\pdfendlink}} % changed 3.69 +\else\def\pdflink#1#2{\setbox0=\hbox{\special{pdf: bc [ \pdflinkcolor ]}{#1}% + \special{pdf: ec}}\special{pdf: ann width \thewidth height \theheight + depth \thedepth << /Type /Annot /Subtype /Link + /Border [0 0 0] /A << /S /GoTo /D (#2) >> >>}\box0\relax}\fi +\def\pdfnote#1.{\setbox0=\hbox{\toksA={#1.}\toksB={}\maketoks}\the\toksA} +\def\firstsecno#1.{\setbox0=\hbox{\toksA={#1.}\toksB={}% + \def\makenote{\addtokens\toksB{\the\toksC}\def\makenote{\toksD={} + \toksC={}\let\space\empty}\makenote}\maketoks}} +\def\addtokens#1#2{\edef\addtoks{\noexpand#1={\the#1#2}}\addtoks} +\def\poptoks#1#2|ENDTOKS|{\let\first=#1\toksD={#1}% + \ifcat\noexpand\first0\countB=`#1\else\countB=0\fi\toksA={#2}} +\def\maketoks{\expandafter\poptoks\the\toksA|ENDTOKS|% + \ifnum\countB>`9 \countB=0 \fi + \ifnum\countB<`0 + \ifnum0=\countC\else\makenote\fi + \ifx\first.\let\next=\maketoksdone\else + \let\next=\maketoks + \addtokens\toksB{\the\toksD} + \ifx\first,\addtokens\toksB{\space}\fi + \fi + \else \addtokens\toksC{\the\toksD}\global\countC=1\let\next=\maketoks + \fi + \next +} +\def\makenote{\addtokens\toksB + {\noexpand\pdflink{\the\toksC}{\romannumeral\the\toksC}}\toksC={}\global\countC=0} +\def\maketoksdone{\edef\st{\global\noexpand\toksA={\the\toksB}}\st} +\def\pdfURL#1#2{\ifpdftex\pdfannotlink height\ht\strutbox depth\dp\strutbox + attr {/Border [0 0 0]} user { /Type /Action /Subtype /Link /A + << /S /URI /URI (#2) >>}\Blue #1\Black \pdfendlink % changed in 3.69 + \else \ifpdf{\setbox0=\hbox{\special{pdf: bc [ \pdflinkcolor ]}{#1}% + \special{pdf: ec}}\special{pdf: ann width \thewidth\space height \theheight + \space depth \thedepth\space << /Border [0 0 0] + /Type /Action /Subtype /Link /A << /S /URI /URI (#2) >> >>}\box0\relax}% + \else #1 ({\tt#2})\fi\fi} +{\catcode`\~=12 \gdef\TILDE/{~}} % ~ in a URL +{\catcode`\_=12 \gdef\UNDER/{_}} % _ in a URL +\def\sanitizecommand#1#2{\addtokens\usersanitizer + {\noexpand\dosanitizecommand\noexpand#1{#2}}} +\def\dosanitizecommand#1#2{\ifx\nxt#1\addF{#2}\fi} + +\catcode`\[=1 \catcode`\]=2 \catcode`\{=12 \catcode`\}=12 + \def\lbchar[{] \def\rbchar[}] +\catcode`\[=12 \catcode`\]=12 \catcode`\{=1 \catcode`\}=2 +\catcode`\~=12 \def\tildechar{~} \catcode`\~=13 +\catcode`\|=0 |catcode`|\=12 |def|bschar{\} |catcode`|\=0 \catcode`\|=12 +\def\makeoutlinetoks{\Tnumfalse\afterassignment\makeolproctok\let\nxt= } +\def\makeolnexttok{\afterassignment\makeolproctok\let\nxt= } +\def\makeolgobbletok{\afterassignment\makeolnexttok\let\nxt= } +\def\addF#1{\addtokens\toksF{#1}\tokprocessedtrue} +% now comes a routine to "sanitize" section names, for pdf outlines +\def\makeolproctok{\tokprocessedfalse + \let\next\makeolnexttok % default + \ifx\nxt\outlinedone\let\next\outlinedone + \else\ifx{\nxt \else\ifx}\nxt \Tnumfalse \instrfalse % skip braces + \else\ifx$\nxt % or a $ sign + \else\ifx^\nxt \addF^\else\ifx_\nxt \addF_% sanitize ^ and _ + \else\ifx\nxt\spacechar \addF\space + \else\if\noexpand\nxt\relax % we have a control sequence; is it one we know? + \ifx\nxt~\addF\space + \else\ifx\nxt\onespace\addF\space + \else\the\usersanitizer + \iftokprocessed\else\makeolproctokctli + \iftokprocessed\else\makeolproctokctlii + \iftokprocessed\else\makeolproctokctliii % if not recognised, skip it + \fi\fi\fi\fi\fi + \else % we don't have a control sequence, it's an ordinary char + \ifx/\nxt \addF{\string\/}% quote chars special to PDF with backslash + \else\ifx(\nxt \addF{\string\(}\else\ifx)\nxt \addF{\string\)}% + \else\ifx[\nxt \addF{\string\[}\else\ifx]\nxt \addF{\string\]}% + \else\expandafter\makeolproctokchar\meaning\nxt + \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi + \next +} +\def\makeolproctokchar#1 #2 #3{\addF{#3}} +\def\makeolproctokctli{% + \ifx\nxt\CEE\addF{C}\let\next\makeolgobbletok % \CEE/ + \else\ifx\nxt\UNIX\addF{UNIX}\let\next\makeolgobbletok % \UNIX/ + \else\ifx\nxt\TEX\addF{TeX}\let\next\makeolgobbletok % \TEX/ + \else\ifx\nxt\TeX\addF{TeX}\else\ifx\nxt\LaTeX\addF{LaTeX}% + \else\ifx\nxt\CPLUSPLUS\addF{C++}\let\next\makeolgobbletok % \CPLUSPLUS/ + \else\ifx\nxt\Cee\addF{C}% + \else\ifx\nxt\PB \let\next\makeolgobbletok \tokprocessedtrue % \PB{...} + \else\ifx\nxt\.\tokprocessedtrue\instrtrue % \.{...} + % skip \| + \else\ifx\nxt\\\ifinstr\addF{\bschar\bschar}\else\tokprocessedtrue\fi + \else\ifx\nxt\&\ifinstr\addF&\else\tokprocessedtrue\fi + \else\ifx\nxt\~\ifTnum\addF{0}\else\addF\tildechar\fi % 077->\T{\~77} + \else\ifx\nxt\_\ifTnum\addF{E}\else\addF_\fi % 0.1E5->\T{0.1\_5} + \else\ifx\nxt\^\ifTnum\addF{0x}\else\addF^\fi % 0x77 -> \T{\^77} + \else\ifx\nxt\$\ifTnum\tokprocessedtrue\else\addF$\fi % \T{77\$L} + \else\ifx\nxt\{\addF\lbchar \else\ifx\nxt\}\addF\rbchar + \else\ifx\nxt\ \addF\space \else\ifx\nxt\#\addF{\string\#}% + \else\ifx\nxt\PP\addF{++}\else\ifx\nxt\MM\addF{--}% + \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi +} +\def\makeolproctokctlii{% + \ifx\nxt\MG\addF{->}\else\ifx\nxt\GG\addF{>>}% + \else\ifx\nxt\LL\addF{<<}\else\ifx\nxt\NULL\addF{NULL}% + \else\ifx\nxt\AND\addF&\else\ifx\nxt\OR\addF|% + \else\ifx\nxt\XOR\addF^\else\ifx\nxt\CM\addF\tildechar + \else\ifx\nxt\MOD\addF{\string\%}\else\ifx\nxt\DC\addF{::}% + \else\ifx\nxt\PA\addF{.*}\else\ifx\nxt\MGA\addF{->*}% + \else\ifx\nxt\this\addF{this}\else\ifx\nxt\?\addF?% + \else\ifx\nxt\E\addF{==}\else\ifx\nxt\G\addF{>=}% + \else\ifx\nxt\I\addF{!=}\else\ifx\nxt\K\addF{=}% + \else\ifx\nxt\l\addF{l}\else\ifx\nxt\L\addF{L}% + \else\ifx\nxt\o\addF{o}\else\ifx\nxt\O\addF{O}% + \else\ifx\nxt\R\addF!% + \else\ifx\nxt\T \Tnumtrue \let\next\makeolgobbletok + \tokprocessedtrue % \T{number} + \else\ifx\nxt\AM\addF&\else\ifx\nxt\%\addF{\string\%}% + \fi\fi\fi\fi\fi \fi\fi\fi\fi\fi + \fi\fi\fi\fi\fi \fi\fi\fi\fi\fi + \fi\fi\fi\fi\fi\fi +} +\def\makeolproctokctliii{% + \ifx\nxt\V\addF{||}\else\ifx\nxt\W\addF{&&}\else\ifx\nxt\Z\addF{<=}% + \else\ifx\nxt\*\addF*\else\ifx\nxt\Xand\addF{\space and\space}% + \else\ifx\nxt\Xandxeq\addF{\space and_eq\space}% + \else\ifx\nxt\Xbitand\addF{\space bitand\space}% + \else\ifx\nxt\Xbitor\addF{\space bitor\space}% + \else\ifx\nxt\Xcompl\addF{\space compl\space}% + \else\ifx\nxt\Xnot\addF{\space not\space}% + \else\ifx\nxt\Xnotxeq\addF{\space not_eq\space}% + \else\ifx\nxt\Xor\addF{\space or\space}% + \else\ifx\nxt\Xorxeq\addF{\space or_eq\space}% + \else\ifx\nxt\Xxor\addF{\space xor\space}% + \else\ifx\nxt\Xxorxeq\addF{\space xor_eq\space}% + \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi + \fi\fi\fi\fi\fi +} +\def\outlinedone{\edef\outlinest{\global\noexpand\toksE={\the\toksF}}% + \outlinest\let\outlinedone=\relax} +\fi % End of pdf macros + +\def\lapstar{\rlap{*}} +\def\stsec{\rightskip=0pt % get out of C mode (cf. \B) + \sfcode`;=1500 \pretolerance 200 \hyphenpenalty 50 \exhyphenpenalty 50 + \noindent{\let\*=\lapstar\bf\secstar.\quad}% + \ifpdftex\smash{\raise\baselineskip\hbox to0pt{% +% \let\*=\empty\pdfdest num \secstar fith}} % bad space in versions < 3.68 + \let\*=\empty\pdfdest num \secstar fith}}% changed in version 3.68 + \else\ifpdf\smash{\raise\baselineskip\hbox to0pt{% + \let\*=\empty\special{% + pdf: dest (\romannumeral\secstar) [ @thispage /FitH @ypos ]}}}\fi\fi} +\let\startsection=\stsec +\def\defin#1{\global\advance\ind by 2 \1\&{#1 } } % begin `define' or `format' +\def\A{\note{See also section}} % xref for doubly defined section name +\def\As{\note{See also sections}} % xref for multiply defined section name +\def\B{\rightskip=0pt plus 100pt minus 10pt % go into C mode + \sfcode`;=3000 + \pretolerance 10000 + \hyphenpenalty 1000 % so strings can be broken (discretionary \ is inserted) + \exhyphenpenalty 10000 + \global\ind=2 \1\ \unskip} +\def\C#1{\5\5\quad$/\ast\,${\cmntfont #1}$\,\ast/$} +\let\SHC\C % "// short comments" treated like "/* ordinary comments */" +%\def\C#1{\5\5\quad$\triangleright\,${\cmntfont#1}$\,\triangleleft$} +%\def\SHC#1{\5\5\quad$\diamond\,${\cmntfont#1}} +\def\D{\defin{\#define}} % macro definition +\let\E=\equiv % equivalence sign +\def\ET{ and~} % conjunction between two section numbers +\def\ETs{, and~} % conjunction between the last two of several section numbers +\def\F{\defin{format}} % format definition +\let\G=\ge % greater than or equal sign +% \H is long Hungarian umlaut accent +\let\I=\ne % unequal sign +\def\J{\.{@\&}} % TANGLE's join operation +\let\K== % assignment operator +%\let\K=\leftarrow % "honest" alternative to standard assignment operator +% \L is Polish letter suppressed-L +\outer\def\M#1{\MN{#1}\ifon\vfil\penalty-100\vfilneg % beginning of section + \vskip\intersecskip\startsection\ignorespaces} +\outer\def\N#1#2#3.{% beginning of starred section + \ifacro{\toksF={}\makeoutlinetoks#3\outlinedone\outlinedone}\fi + \gdepth=#1\gtitle={#3}\MN{#2}% + \ifon\ifnum#1<\secpagedepth \vfil\eject % force page break if depth is small + \else\vfil\penalty-100\vfilneg\vskip\intersecskip\fi\fi + \message{*\secno} % progress report + \def\stripprefix##1>{}\def\gtitletoks{#3}% + \edef\gtitletoks{\expandafter\stripprefix\meaning\gtitletoks}% + \edef\next{\write\cont{\ZZ{\gtitletoks}{#1}{\secno}% write to contents file + {\noexpand\the\pageno}{\the\toksE}}}\next % \ZZ{title}{depth}{sec}{page}{ss} + \ifpdftex\expandafter\xdef\csname curr#1\endcsname{\secno} + \ifnum#1>0\countB=#1 \advance\countB by-1 + \advancenumber{chunk\the\countB.\expnumber{curr\the\countB}}\fi\fi + \ifpdf\special{pdf: outline #1 << /Title (\the\toksE) /Dest + [ @thispage /FitH @ypos ] >>}\fi + \ifon\startsection{\bf#3.\quad}\ignorespaces} +\def\MN#1{\par % common code for \M, \N + {\xdef\secstar{#1}\let\*=\empty\xdef\secno{#1}}% remove \* from section name + \ifx\secno\secstar \onmaybe \else\ontrue \fi + \mark{{{\tensy x}\secno}{\the\gdepth}{\the\gtitle}}} +% each \mark is {section reference or null}{depth plus 1}{group title} +% \O is Scandinavian letter O-with-slash +% \P is paragraph sign +\def\Q{\note{This code is cited in section}} % xref for mention of a section +\def\Qs{\note{This code is cited in sections}} % xref for mentions of a section +\let\R=\lnot % logical not +% \S is section sign +\def\T#1{\leavevmode % octal, hex or decimal constant + \hbox{$\def\?{\kern.2em}% +% \def\$##1{\egroup_{\,\rm##1}\bgroup}% suffix to constant % versions < 3.67 + \def\$##1{\egroup_{\rm##1}\bgroup}% suffix to constant % in version 3.67 + \def\_{\cdot 10^{\aftergroup}}% power of ten (via dirty trick) + \let\~=\oct \let\^=\hex {#1}$}} +\def\U{\note{This code is used in section}} % xref for use of a section +\def\Us{\note{This code is used in sections}} % xref for uses of a section +\let\V=\lor % logical or +\let\W=\land % logical and +\def\X#1:#2\X{\ifmmode\gdef\XX{\null$\null}\else\gdef\XX{}\fi %$% section name + \XX$\langle\,${\let\I=\ne#2\eightrm\kern.5em + \ifacro{\pdfnote#1.}\else#1\fi}$\,\rangle$\XX} +\def\Y{\par\yskip} +\let\Z=\le +\let\ZZ=\let % now you can \write the control sequence \ZZ +\let\*=* + +\def\Xand{\W} \def\Xandxeq{\MRL{{\AND}{\K}}} \def\Xbitand{\AND} +\def\Xbitor{\OR} \def\Xcompl{\CM} \def\Xnot{\R} \def\Xnotxeq{\I} \def\Xor{\V} +\def\Xorxeq{\MRL{{\OR}{\K}}} \def\Xxor{\XOR} \def\Xxorxeq{\MRL{{\XOR}{\K}}} + +%\def\oct{\hbox{\rm\char'23\kern-.2em\it\aftergroup\?\aftergroup}} % WEB style +%\def\hex{\hbox{\rm\char"7D\tt\aftergroup}} % WEB style +\def\oct{\hbox{$^\circ$\kern-.1em\it\aftergroup\?\aftergroup}}% CWEB style +\def\hex{\hbox{$^{\scriptscriptstyle\#}$\tt\aftergroup}} % CWEB style +\def\vb#1{\leavevmode\hbox{\kern2pt\vrule\vtop{\vbox{\hrule + \hbox{\strut\kern2pt\.{#1}\kern2pt}} + \hrule}\vrule\kern2pt}} % verbatim string + +\def\onmaybe{\let\ifon=\maybe} \let\maybe=\iftrue +\newif\ifon \newif\iftitle \newif\ifpagesaved + +\newif\ifheader +\def\lheader{\headertrue\mainfont\the\pageno\eightrm\qquad\grouptitle + \hfill\title\qquad\mainfont\topsecno} % top line on left-hand pages +\def\rheader{\headertrue\mainfont\topsecno\eightrm\qquad\title\hfill + \grouptitle\qquad\mainfont\the\pageno} % top line on right-hand pages +\def\grouptitle{\let\i=I\let\j=J\uppercase\expandafter{\expandafter + \takethree\topmark}} +\def\topsecno{\expandafter\takeone\topmark} +\def\takeone#1#2#3{#1} +\def\taketwo#1#2#3{#2} +\def\takethree#1#2#3{#3} +\def\nullsec{\eightrm\kern-2em} % the \kern-2em cancels \qquad in headers + +\let\page=\pagebody \raggedbottom +% \def\page{\box255 }\normalbottom % faster, but loses plain TeX footnotes +\def\normaloutput#1#2#3{\ifodd\pageno\hoffset=\pageshift\fi + \shipout\vbox{ + \vbox to\fullpageheight{ + \iftitle\global\titlefalse + \else\hbox to\pagewidth{\vbox to10pt{}\ifodd\pageno #3\else#2\fi}\fi + \vfill#1}} % parameter #1 is the page itself + \global\advance\pageno by1} + +\gtitle={\.{CWEB} output} % this running head is reset by starred sections +\mark{\noexpand\nullsec0{\the\gtitle}} +\def\title{\expandafter\uppercase\expandafter{\jobname}} +\def\topofcontents{\centerline{\titlefont\title}\vskip.7in + \vfill} % this material will start the table of contents page +\def\startpdf{\ifpdftex\pdfcatalog{/PageMode /UseOutlines}\else + \ifpdf{\special{pdf: docview << /PageMode /UseOutlines >>}}\fi\fi} +\def\botofcontents{\vfill + \centerline{\covernote}} % this material will end the table of contents page +\def\covernote{} +\def\contentspagenumber{0} % default page number for table of contents +\newdimen\pagewidth \pagewidth=6.5in % the width of each page +\newdimen\pageheight \pageheight=8.7in % the height of each page +\newdimen\fullpageheight \fullpageheight=9in % page height including headlines +\newdimen\pageshift \pageshift=\hoffset + % shift righthand pages wrt lefthand ones (changed in version 3.70) +\def\magnify#1{\mag=#1\pagewidth=6.5truein\pageheight=8.7truein + \fullpageheight=9truein\setpage} +\def\setpage{\hsize\pagewidth\vsize\pageheight} % use after changing page size +\def\contentsfile{\jobname.toc} % file that gets table of contents info +\def\readcontents{\input \contentsfile} +\def\readindex{\input \jobname.idx} +\def\readsections{\input \jobname.scn} + +\newwrite\cont +\output{\setbox0=\page % the first page is garbage + \openout\cont=\contentsfile + \write\cont{\catcode `\noexpand\@=11\relax} % \makeatletter + \global\output{\normaloutput\page\lheader\rheader}} +\setpage +\vbox to \vsize{} % the first \topmark won't be null + +\def\ch{\note{The following sections were changed by the change file:} + \let\*=\relax} +\newbox\sbox % saved box preceding the index +\newbox\lbox % lefthand column in the index +\def\inx{\par\vskip6pt plus 1fil % we are beginning the index + \def\page{\box255 } \normalbottom + \write\cont{} % ensure that the contents file isn't empty + \write\cont{\catcode `\noexpand\@=12\relax} % \makeatother + \closeout\cont % the contents information has been fully gathered + \output{\ifpagesaved\normaloutput{\box\sbox}\lheader\rheader\fi + \global\setbox\sbox=\page \global\pagesavedtrue} + \pagesavedfalse \eject % eject the page-so-far and predecessors + \setbox\sbox\vbox{\unvbox\sbox} % take it out of its box + \vsize=\pageheight \advance\vsize by -\ht\sbox % the remaining height + \hsize=.5\pagewidth \advance\hsize by -10pt + % column width for the index (20pt between cols) + \parfillskip 0pt plus .6\hsize % try to avoid almost empty lines + \def\lr{L} % this tells whether the left or right column is next + \output{\if L\lr\global\setbox\lbox=\page \gdef\lr{R} + \else\normaloutput{\vbox to\pageheight{\box\sbox\vss + \hbox to\pagewidth{\box\lbox\hfil\page}}}\lheader\rheader + \global\vsize\pageheight\gdef\lr{L}\global\pagesavedfalse\fi} + \message{Index:} + \parskip 0pt plus .5pt + \outer\def\I##1, ##2.{\par\hangindent2em\noindent##1:\kern1em + \ifacro\pdfnote##2.\else##2\fi.} % index entry + \def\[##1]{$\underline{##1}$} % underlined index item + \rm \rightskip0pt plus 2.5em \tolerance 10000 \let\*=\lapstar + \hyphenpenalty 10000 \parindent0pt + \readindex} +\def\fin{\par\vfill\eject % this is done when we are ending the index + \ifpagesaved\null\vfill\eject\fi % output a null index column + \if L\lr\else\null\vfill\eject\fi % finish the current page + \ifpdftex \makebookmarks \fi % added in Version 3.68 + \parfillskip 0pt plus 1fil + \def\grouptitle{NAMES OF THE SECTIONS} + \let\topsecno=\nullsec + \message{Section names:} + \output={\normaloutput\page\lheader\rheader} + \setpage + \def\note##1##2.{\quad{\eightrm##1~\ifacro{\pdfnote##2.}\else{##2}\fi.}} + \def\Q{\note{Cited in section}} % crossref for mention of a section + \def\Qs{\note{Cited in sections}} % crossref for mentions of a section + \def\U{\note{Used in section}} % crossref for use of a section + \def\Us{\note{Used in sections}} % crossref for uses of a section + \def\I{\par\hangindent 2em}\let\*=* + \ifacro \def\outsecname{Names of the sections} \let\Xpdf\X +% \ifpdftex \makebookmarks \pdfdest name {NOS} fitb % in versions < 3.68 + \ifpdftex \pdfdest name {NOS} fith % changed in version 3.69 + \pdfoutline goto name {NOS} count -\secno {\outsecname} + \def\X##1:##2\X{\Xpdf##1:##2\X \firstsecno##1.% + {\toksF={}\makeoutlinetoks##2\outlinedone\outlinedone}% + \pdfoutline goto num \the\toksA \expandafter{\the\toksE}} + \else\ifpdf + \special{pdf: outline -1 << /Title (\outsecname) + /Dest [ @thispage /FitH @ypos ] >>} + \def\X##1:##2\X{\Xpdf##1:##2\X \firstsecno##1.% + {\toksF={}\makeoutlinetoks##2\outlinedone\outlinedone}% + \special{pdf: outline 0 << /Title (\the\toksE) + /A << /S /GoTo /D (\romannumeral\the\toksA) >> >>}} + \fi\fi\fi + \readsections} +\def\makebookmarks{\let\ZZ=\writebookmarkline \readcontents\relax} +\def\expnumber#1{\expandafter\ifx\csname#1\endcsname\relax 0% + \else \csname#1\endcsname \fi} % Petr Olsak's macros from texinfo.tex +\def\advancenumber#1{\countA=\expnumber{#1}\relax \advance\countA by1 + \expandafter\xdef\csname#1\endcsname{\the\countA}} +\def\writebookmarkline#1#2#3#4#5{{% + \let\(=\let \let\)=\let \let\[=\let \let\]=\let \let\/=\let + \pdfoutline goto num #3 count -\expnumber{chunk#2.#3} {#5}}} +\def\con{\par\vfill\eject % finish the section names +% \ifodd\pageno\else\titletrue\null\vfill\eject\fi % for duplex printers + \rightskip 0pt \hyphenpenalty 50 \tolerance 200 + \setpage \output={\normaloutput\page\lheader\rheader} + \titletrue % prepare to output the table of contents + \pageno=\contentspagenumber + \def\grouptitle{TABLE OF CONTENTS} + \message{Table of contents:} + \topofcontents \startpdf + \line{\hfil Section\hbox to3em{\hss Page}} + \let\ZZ=\contentsline + \readcontents\relax % read the contents info + \botofcontents \end} % print the contents page(s) and terminate +\def\contentsline#1#2#3#4#5{\ifnum#2=0 \smallbreak\fi + \line{\consetup{#2}#1 + \rm\leaders\hbox to .5em{.\hfil}\hfil + \ \ifacro\pdflink{#3}{\romannumeral#3}\else#3\fi\hbox to3em{\hss#4}}} +\def\consetup#1{\ifcase#1 \bf % depth -1 (@**) + \or % depth 0 (@*) + \or \hskip2em % depth 1 (@*1) + \or \hskip4em \or \hskip6em \or \hskip8em \or \hskip10em % depth 2,3,4,5 + \else \hskip12em \fi} % depth 6 or more +\def\noinx{\let\inx=\end} % no indexes or table of contents +\def\nosecs{\let\FIN=\fin \def\fin{\let\parfillskip=\end \FIN}} + % no index of section names or table of contents +\def\nocon{\let\con=\end} % no table of contents +\def\today{\ifcase\month\or + January\or February\or March\or April\or May\or June\or + July\or August\or September\or October\or November\or December\fi + \space\number\day, \number\year} +\newcount\twodigits +\def\hours{\twodigits=\time \divide\twodigits by 60 \printtwodigits + \multiply\twodigits by-60 \advance\twodigits by\time :\printtwodigits} +\def\gobbleone1{} +\def\printtwodigits{\advance\twodigits100 + \expandafter\gobbleone\number\twodigits + \advance\twodigits-100 } +\def\TeX{{\ifmmode\it\fi + \leavevmode\hbox{T\kern-.1667em\lower.424ex\hbox{E}\hskip-.125em X}}} +\def\,{\relax\ifmmode\mskip\thinmuskip\else\thinspace\fi} +\def\datethis{\def\startsection{\leftline{\sc\today\ at \hours}\bigskip + \let\startsection=\stsec\stsec}} + % say `\datethis' in limbo, to get your listing timestamped before section 1 +%\def\datecontentspage{% versions up to 3.65 +% \def\topofcontents{\leftline{\sc\today\ at \hours}\bigskip +% \centerline{\titlefont\title}\vfill}} % timestamps the contents page +\def\datecontentspage{% changed in version 3.66 + \def\botofcontents{\vfill + \centerline{\covernote} + \bigskip + \leftline{\sc\today\ at \hours}}} % timestamps the contents page diff --git a/web/c_cpp/cweb/cwebman.tex b/web/c_cpp/cweb/cwebman.tex new file mode 100644 index 0000000000..2e4ec65ea5 --- /dev/null +++ b/web/c_cpp/cweb/cwebman.tex @@ -0,0 +1,1763 @@ +% This file generates the user manual; TeX it, don't read it! +% update 14 Nov 2016 by DEK: corrected obsolete remarks re debugging info + +\def\tangref{3} % where the main explanation of CTANGLing is given +\input cwebmac +\acrofalse\pdffalse\pdftexfalse +\def\page{\box255 } \normalbottom +\parskip 0pt plus 1pt +\def\RA{\char'31 } % right arrow +\def\hang{\hangindent 4em\ignorespaces} +\font\eightrm=cmr8 +\font\ninerm=cmr9 +\font\ninett=cmtt9 +\font\eighttt=cmtt8 +\font\twelvett=cmtt12 +\font\quoterm=cmssq8 +\font\quoteit=cmssqi8 +\font\authorfont=cmr12 +\font\sectionfont=cmbx12 + +\def\pb{\.{|...|}} +\def\v{\.{\char'174}} % vertical (|) in typewriter font +\def\lpile{\def\cr{\hfill\endline}\matrix} % I only use \lpile by itself +\abovedisplayskip=.5\abovedisplayskip +\belowdisplayskip=.5\belowdisplayskip +\abovedisplayshortskip=.5\abovedisplayshortskip +\belowdisplayshortskip=.5\belowdisplayshortskip +\advance\baselineskip by -.5pt +\advance\pageheight by \baselineskip % the manual just got a bit longer +\advance\fullpageheight by \baselineskip +\setpage + +\outer\def\section #1.{\penalty-500\bigskip + \centerline{\sectionfont\def\.##1{{\twelvett##1}} #1}\nobreak\vskip 6pt + \everypar{\hskip-\parindent\everypar{}}} + +\def\lheader{\mainfont\the\pageno\hfill\sc\runninghead\hfill} +\def\rheader{\hfill\sc\runninghead\hfill\mainfont\the\pageno} +\def\runninghead{{\tentt CWEB} USER MANUAL (VERSION 3.64)} + +% This verbatim mode assumes that ! marks are !! in the text being copied. +\def\verbatim{\begingroup + \def\do##1{\catcode`##1=12 } \dospecials + \parskip 0pt \parindent 0pt \let\!=! + \catcode`\ =13 \catcode`\^^M=13 + \tt \catcode`\!=0 \verbatimdefs \verbatimgobble} +{\catcode`\^^M=13{\catcode`\ =13\gdef\verbatimdefs{\def^^M{\ \par}\let =\ }} % + \gdef\verbatimgobble#1^^M{}} + +\null\vfill +\centerline{\titlefont The {\ttitlefont CWEB} System of + Structured Documentation} +\vskip 18pt\centerline{(Version 3.64 --- February 2002)} +\vskip 24pt +\centerline{\authorfont Donald E. Knuth and Silvio Levy} +\vfill + +\noindent +\TeX\ is a trademark of the American Mathematical Society. + +\noindent +Acrobat Reader is a trademark of Adobe Systems Incorporated. + +\bigskip\noindent +The printed form of this manual is copyright \copyright\ 1994 + by Addison-Wesley Publishing Company, Inc. All rights reserved. +\smallskip\noindent +The electronic form is copyright \copyright\ 1987, 1990, 1993, 2000 + by Silvio Levy and Donald E. Knuth. + +\bigskip\noindent +Permission is granted to make and distribute verbatim copies of the +electronic form of this document provided that the electronic copyright +notice and this permission notice are preserved on all copies. + +\smallskip\noindent +Permission is granted to copy and distribute modified versions of the +electronic form of this document under the conditions for verbatim copying, +provided that the entire resulting derived work is distributed under the terms +of a permission notice identical to this one. + +\smallskip\noindent +Individuals may make copies of the documentation from the electronic files +for their own personal use. + +\smallskip\noindent +Internet page \.{http://www-cs-faculty.stanford.edu/\char`\~knuth/cweb.html} +contains current info about \.{CWEB} and related topics. + +\pageno=0 \titletrue\eject + +\titletrue +\centerline{\titlefont The {\ttitlefont CWEB} System of + Structured Documentation} + +\vskip 15pt plus 3pt minus 3pt +\centerline{\authorfont Donald E. Knuth and Silvio Levy} + +\vskip 24pt plus 3pt minus 3pt +\noindent +This document describes a version of Don Knuth's \.{WEB} system, +adapted to \CEE/ by Silvio Levy. Since its creation in 1987, \.{CWEB} +has been revised and enhanced in various ways, by both Knuth and Levy. +We now believe that its evolution is near an end; however, bug +reports, suggestions and comments are still welcome, and +should be sent to Levy (\.{levy@math.berkeley.edu}). + +Readers who are familiar with Knuth's memo ``The \.{WEB} System of Structured +Documentation'' will be able +to skim this material rapidly, because \.{CWEB} and \.{WEB} share +the same philosophy and (essentially) the same syntax. In some respects +\.{CWEB} is a simplification +of \.{WEB}: for example, \.{CWEB} does not need \.{WEB}'s features +for macro definition and string handling, because \CEE/ and its +preprocessor already take care of macros and strings. Similarly, the \.{WEB} +conventions of denoting octal and hexadecimal constants by \.{@'77} +and \.{@"3f} are replaced by \CEE/'s conventions \.{077} and +\.{0x3f}. All other features of \.{WEB} have been +retained, and new features have been added. + +We thank all who contributed suggestions and criticism to +the development of \.{CWEB}. We are especially grateful to Steve +Avery, Nelson Beebe, Hans-Hermann Bode, Klaus Guntermann, Norman Ramsey, +Joachim Schnitter, and Saroj Mahapatra, +who contributed code, and to Cameron Smith, who +made many suggestions improving the manual. Ramsey has made +literate programming accessible to users of yet other languages by means of +his \.{SPIDER} system [see {\sl Communications of the ACM\/ \bf32} (1989), +1051--1055]. The book {\sl Literate Programming\/} by Knuth (1992) contains +a comprehensive bibliography of related early work. +Bode, Schnitter, and Mahapatra adapted \.{CWEB} so that it works +for \CPLUSPLUS/ as well; therefore in the text below you can read \CPLUSPLUS/ +for \CEE/ if you so desire. + +\section Introduction. +The philosophy behind \.{CWEB} is +that programmers who want to provide the best +possible documentation for their programs need two things +simultaneously: a language like \TEX/ for formatting, and a language like +\CEE/ for programming. Neither type of language can provide the +best documentation by itself. But when both are appropriately combined, we +obtain a system that is much more useful than either language separately. + +The structure of a software program may be thought of as a ``web'' that is +made up of many interconnected pieces. To document such a program, we want +to explain each individual part of the web and how it relates to its +neighbors. The typographic tools provided by \TEX/ give us an opportunity +to explain the local structure of each part by making that structure +visible, and the programming tools provided by \CEE/ make it possible +for us to specify the algorithms formally and unambiguously. By combining +the two, we can develop a style of programming that maximizes our ability +to perceive the structure of a complex piece of software, and at the same +time the documented programs can be mechanically translated into a working +software system that matches the documentation. + +The \.{CWEB} system consists of two programs named \.{CWEAVE} and \.{CTANGLE}. +When writing a \.{CWEB} program the user keeps the +\CEE/ code and the documentation in the same file, called the \.{CWEB} +file and generally named \.{something.w}. The command +`\.{cweave} \.{something}' creates an output file \.{something.tex}, which +can then be fed to \TEX/, yielding a ``pretty printed'' version of +\.{something.w} that correctly handles +typographic details like page layout and the use of indentation, +italics, boldface, and mathematical symbols. The typeset output also +includes extensive cross-index +information that is gathered automatically. Similarly, if you run the +command `\.{ctangle} \.{something}' you will get a \CEE/ file \.{something.c}, +which can then be compiled to yield executable code. + +Besides providing a documentation tool, \.{CWEB} enhances the \CEE/ +language by providing the +ability to permute pieces of the program text, so that a large system can +be understood entirely in terms of small sections and their local +interrelationships. The \.{CTANGLE} program is so named because it takes a +given web and moves the sections from their web structure into the order +required by \CEE/; the advantage of programming in \.{CWEB} is that the +algorithms can be expressed in ``untangled'' form, with each section +explained separately. The \.{CWEAVE} program is so named because it takes +a given web and intertwines the \TEX/ and \CEE/ portions contained in +each section, then it knits the whole fabric into a structured document. +(Get it? Wow.) Perhaps there is some deep connection here with the fact +that the German word for ``weave'' is ``{\it webe\/}'', and the +corresponding Latin imperative is ``{\it texe\/}''! + +A user of \.{CWEB} should be fairly familiar with the \CEE/ +programming language. A minimal amount of acquaintance with \TEX/ is also +desirable, but in fact it can be acquired as one uses \.{CWEB}, since +straight text can be typeset in \TEX/ with virtually no knowledge of +that language. To someone familiar with both \CEE/ and \TEX/ the amount of +effort necessary to learn the commands of \.{CWEB} is small. + +\section Overview. +Two kinds of material go into \.{CWEB} files: \TEX/ text and \CEE/ text. +A programmer writing in \.{CWEB} should be thinking both of the +documentation and of the \CEE/ program being created; +i.e., the programmer should be instinctively aware of the different +actions that \.{CWEAVE} and \.{CTANGLE} will perform on the \.{CWEB} file. +\TEX/ text is essentially copied without change by \.{CWEAVE}, and it is +entirely deleted by \.{CTANGLE}; the \TEX/ text is ``pure +documentation.'' \CEE/ text, on the other hand, is formatted by +\.{CWEAVE} and it is shuffled around by \.{CTANGLE}, according to rules that +will become clear later. For now the important point to keep in mind is +that there are two kinds of text. Writing \.{CWEB} programs is something +like writing \TEX/ documents, but with an additional ``\CEE/ mode'' +that is added to \TEX/'s horizontal mode, vertical mode, and math mode. + +A \.{CWEB} file is built up from units called {\sl sections\/} that are more +or less self-contained. Each section has three parts: + +\yskip\item{$\bullet$} A \TEX/ part, containing explanatory material about what +is going on in the section. + +\item{$\bullet$} A middle part, containing macro definitions that serve as +abbreviations for \CEE/ constructions that would be less comprehensible +if written out in full each time. They are turned by \.{CTANGLE} into +preprocessor macro definitions. + +\item{$\bullet$} A \CEE/ part, containing a piece of the program that +\.{CTANGLE} will produce. This \CEE/ code should ideally be about a +dozen lines long, so that it is easily comprehensible as a unit and so +that its structure is readily perceived. + +\yskip\noindent The three parts of each section must appear in this order; +i.e., the \TEX/ commentary must come first, then the middle part, and +finally the \CEE/ code. Any of the parts may be empty. + +A section begins with either of the symbols `\.{@\ }' or `\.{@*}', where +`\.{\ }' denotes a blank space. A section ends +at the beginning of the next section (i.e., at the next +`\.{@\ }' or `\.{@*}'), or at the end of the file, whichever comes first. +The \.{CWEB} file may also contain material that is not part of any section +at all, namely the text (if any) that occurs before the first section. +Such text is said to be ``in limbo''; it is ignored by \.{CTANGLE} +and copied essentially verbatim by \.{CWEAVE}, so its function is to +provide any additional formatting instructions that may be desired in the +\TEX/ output. Indeed, it is customary to begin a \.{CWEB} file with +\TEX/ code in limbo that loads special fonts, defines special macros, +changes the page sizes, and/or produces a title page. + +Sections are numbered consecutively, starting with 1. These numbers appear +at the beginning of each section of the \TEX/ documentation output by +\.{CWEAVE}, and they appear +as bracketed comments at the beginning and end of the code generated by that +section in the \CEE/ program output by \.{CTANGLE}. + +\section Section Names. +Fortunately, you never mention these numbers yourself when you are writing +in \.{CWEB}. You just say `\.{@\ }' or `\.{@*}' at the beginning of each +new section, and the numbers are supplied automatically by \.{CWEAVE} and +\.{CTANGLE}. As far as you are concerned, a section has a +{\sl name\/} instead of a number; its name is specified by writing +`\.{@<}' followed by \TEX/ text followed by `\.{@>}'. When \.{CWEAVE} +outputs a section name, it replaces the `\.{@<}' and `\.{@>}' by +angle brackets and inserts the section number in small type. Thus, when you +read the output of \.{CWEAVE} it is easy to locate any section that is +referred to in another section. + +For expository purposes, a section name should be a good description of the +contents of that section; i.e., it should stand for the abstraction +represented by the section. Then the section can be ``plugged into'' one or +more other sections in such a way +that unimportant details of its inner workings +are suppressed. A section name therefore ought to be long enough to convey +the necessary meaning. + +Unfortunately, it is laborious to type +such long names over and over again, and it is also difficult to specify a +long name twice in exactly the same way so that \.{CWEAVE} and \.{CTANGLE} +will be able to match the names to the sections. To ameliorate this situation, +\.{CWEAVE} and \.{CTANGLE} let you abbreviate a section name, so long as +the full name appears somewhere in the \.{CWEB} file; you can type simply +`\.{@<$\alpha$...@>}', where $\alpha$ is any string that is a prefix of +exactly one section name appearing in the file. For example, `\.{@<Clear +the arrays@>}' can be abbreviated to `\.{@<Clear...@>}' if no other section +name begins with the five letters `\.{Clear}'. Elsewhere +you might use the abbreviation `\.{@<Clear t...@>}', and so on. + +Section names must otherwise match character for character, except +that consecutive characters of white space (spaces, tab marks, newlines, and/or +form feeds) are treated as equivalent to a single space, and such spaces are +deleted at the beginning and end of the name. Thus, `\.{@< Clear { }the +arrays @>}' will also match the name in the previous example. +Spaces following the ellipsis in abbreviations are ignored as well, but +not those before, so that `\.{@<Clear t ...@>}' would not match +`\.{@<Clear the arrays@>}'. + +\section What \.{CTANGLE} Does. +We have said that a section begins with `\.{@\ }' or `\.{@*}', but we +didn't say how it gets divided up into a \TEX/ part, a middle part, +and a \CEE/ part. The middle part begins with the first appearance of +`\.{@d}' or `\.{@f}' in the section, and the \CEE/ part begins with +the first appearance of `\.{@c}' or `\.{@<section name@>=}'. In the +latter case you are saying, in effect, that the section name stands +for the \CEE/ text that follows. Alternatively, if the \CEE/ part +begins with `\.{@c}' instead of a section name, the current section is +said to be {\sl unnamed}. + +The construct `\.{@<section name@>}' can appear +any number of times in the \CEE/ part of a section: +Subsequent appearances indicate that a named section is being +``used'' rather than ``defined.'' In other words, the +\CEE/ code for the named section, presumably defined elsewhere, should be +spliced in at this point in the \CEE/ program. Indeed, +the main idea of \.{CTANGLE} is to make a \CEE/ program out of +individual sections, named and unnamed. The exact way in which this is done +is this: First all the macro definitions +indicated by `\.{@d}' are turned into \CEE/ preprocessor macro definitions +and copied at the beginning. +Then the \CEE/ parts of unnamed sections are copied down, +in order; this constitutes the first-order +approximation to the text of the program. (There should be at least +one unnamed section, otherwise there will be no program.) Then all section +names that appear in the first-order approximation are replaced by the \CEE/ +parts of the corresponding sections, and this substitution process +continues until no section names remain. All comments are removed, because +the \CEE/ program is intended only for the eyes of the \CEE/ compiler. + +If the same name has been given to more than one section, the \CEE/ text +for that name is obtained by putting together all of the \CEE/ parts in +the corresponding sections. This feature is useful, for example, in a +section named `Global variables', since one can then +declare global variables in whatever sections those variables are +introduced. When several sections have the same name, \.{CWEAVE} assigns the +first section number as the number corresponding to that name, and it +inserts a note at the bottom of that section telling the reader to `See +also sections so-and-so'; this footnote gives the numbers of all the other +sections having the same name as the present one. The \CEE/ text +corresponding to a section is usually formatted by \.{CWEAVE} so that the +output has an equivalence sign in place of the equals sign in the \.{CWEB} +file; i.e., the output says `$\langle\,$section +name$\,\rangle\equiv\null$\CEE/ text'. However, in the case of the second +and subsequent appearances of a section with the same name, this `$\equiv$' +sign is replaced by `$\mathrel+\equiv$', as an indication that the +following \CEE/ text is being appended to the \CEE/ text of another section. + +As \.{CTANGLE} enters and leaves sections, it inserts preprocessor +\.{\#line} commands into the \CEE/ output file. This means that +when the compiler gives you error messages, or when you debug your program, +the messages refer to line numbers in the \.{CWEB} file, and not in the +\CEE/ file. In most cases you can therefore +forget about the \CEE/ file altogether. + +\section What \.{CWEAVE} Does. +The general idea of \.{CWEAVE} is to make a \.{.tex} file from the \.{CWEB} +file in the following way: The first line of the \.{.tex} file +tells \TEX/ to input a file with macros that +define \.{CWEB}'s documentation conventions. The next lines of the file +will be copied from whatever \TEX/ text is in limbo before the first +section. Then comes the output for each section in turn, possibly +interspersed with end-of-page marks. Finally, \.{CWEAVE} will generate a +cross-reference index that lists each section number in which each \CEE/ +identifier appears, and it will also generate an alphabetized list +of the section names, as well as a table of contents that +shows the page and section numbers for each ``starred'' section. + +What is a ``starred'' section, you ask? A section that begins with `\.{@*}' +instead of `\.{@\ }' is slightly special in that it denotes a new major +group of sections. The `\.{@*}' should be followed by the title of this +group, followed by a period. Such sections will always start on a new page +in the \TEX/ output, and the group title will appear as a running headline +on all subsequent pages until the next starred section. The title will also +appear in the table of contents, and in boldface type at the beginning of +its section. Caution: Do not use \TEX/ control sequences in such titles, +unless you know that the \.{cwebmac} macros will do the right thing with +them. The reason is that these titles are converted to uppercase when +they appear as running heads, and they are converted to boldface when they +appear at the beginning of their sections, and they are also written out to +a table-of-contents file used for temporary storage while \TEX/ is +working; whatever control sequences you use must be meaningful in all +three of these modes. + +The \TEX/ output produced by \.{CWEAVE} for each section consists of +the following: First comes the section number (e.g., `\.{\\M123.}' +at the beginning of section 123, except that `\.{\\N}' appears in place of +`\.{\\M}' at the beginning of a starred section). Then comes the +\TEX/ part of the section, copied almost verbatim except as noted +below. Then comes the middle part and the \CEE/ part, formatted +so that there will be a little extra space between them if both are +nonempty. The middle and \CEE/ parts are obtained by inserting +a bunch of funny-looking \TEX/ macros into the \CEE/ program; these +macros handle typographic details about fonts and proper math spacing, +as well as line breaks and indentation. + +\section C Code in \TEX/ Text and Vice Versa. +When you are typing \TEX/ text, you will probably want to make frequent +reference to variables and other quantities in your \CEE/ code, and you +will want those variables to have the same typographic treatment +when they appear in your text as when they appear in your +program. Therefore the \.{CWEB} language allows you to get the effect of +\CEE/ editing within \TEX/ text, if you place `\.|' marks before and +after the \CEE/ material. For example, suppose you want to say something +like this: +$$\hbox{ If \\{pa} is declared as `\&{int} ${}{*}\\{pa}$', +the assignment $\\{pa}\K{\AND}\|a[\T{0}]$ makes \\{pa} +point to the zeroth element of \|a.}$$ +The \TEX/ text would look like this in your \.{CWEB} file: +$$\lpile{\.{If |pa| is declared as `|int *pa|', the}\cr +\.{assignment |pa=\&a[0]| makes |pa| point +to the zeroth element of |a|.}\cr}$$ +And \.{CWEAVE} translates this into something you are glad you didn't have +to type: +$$\lpile{\.{If \\\\\{pa\} is declared as + `\\\&\{int\} \$\{\}\{*\}\\\\\{pa\}\$',}\cr +\.{the assignment \$\\\\\{pa\}\\K\{\\AND\}\\|a[\\T\{0\}]\$}\cr +\.{makes \\\\\{pa\} point to the zeroth element of \\|a.}\cr}$$ +Incidentally, the cross-reference index that \.{CWEAVE} would make, in +the presence of a comment like this, would include +the current section number as one of the index entries for \\{pa}, +even though \\{pa} might not appear in the \CEE/ part of +this section. Thus, the index covers references to identifiers in +the explanatory comments as well as in the program itself; you will +soon learn to appreciate this feature. However, the identifiers +\&{int} and \|a\ would not be indexed, +because \.{CWEAVE} does not make index entries for reserved words or +single-letter identifiers. Such identifiers are felt to be so ubiquitous +that it would be pointless to mention every place where they occur. + +Although a section begins with \TEX/ text and ends with \CEE/ text, we +have noted that the dividing line isn't sharp, since \CEE/ text can be +included in \TEX/ text if it is enclosed in `\pb'. Conversely, \TEX/ text +appears frequently within \CEE/ text, because everything in +comments (i.e., between \.{/*} and \.{*/}, or following \.{//}) +is treated as \TEX/ text. +Likewise, the text of a section name consists of \TEX/ text, but +the construct \.{@<section name@>} as a whole is expected to be found +in \CEE/ text; thus, one typically goes back and forth +between the \CEE/ and \TEX/ environments in a natural way, as in these +examples: +$$ +\displaylines{ +\hbox{\.{if} \.{(x==0)} \.{@<Empty} \.{the} \.{|buffer|} \.{array@>}} \cr +\hbox{\.{...} \.{using} \.{the} \.{algorithm} +\.{in} \.{|@<Empty} \.{the} \.{|buffer|} \.{array@>|.}} } +$$ +The first of these excerpts +would be found in the \CEE/ part of a section, into which the code +from the section +named ``Empty the \\{buffer} array'' is being spliced. The second excerpt +would be found in the \TEX/ part of the section, and the named section +is being ``cited'', rather than defined or used. +(Note the `\pb' surrounding the section name in this case.) + +\section Macros. +The control code \.{@d} followed by +$$\\{identifier}\.{ }\hbox{\CEE/ text}\qquad\hbox{or by}\qquad +\\{identifier}\.(\\{par}_1,\ldots,\\{par}_n\.{) }\hbox{\CEE/ text}$$ +(where there is no blank between the +\\{identifier} and the parentheses in the second case) is +transformed by \.{CTANGLE} into a preprocessor command, starting with +\.{\#define}, which is printed at the top of the \CEE/ output file +as explained earlier. + +A `\.{@d}' macro definition can go on for several lines, and the +newlines don't have to be protected by backslashes, since \.{CTANGLE} +itself inserts the backslashes. If +for any reason you need a \.{\#define} command at a specific spot in +your \CEE/ file, you can treat it as \CEE/ code, instead of as a +\.{CWEB} macro; but then you do have to protect newlines yourself. + +\section Strings and constants. +If you want a string to appear in the \CEE/ file, delimited by pairs of +\.' or \." marks as usual, you can type it exactly so in the \.{CWEB} file, +except that the character `\.@' should be typed `\.{@@}' (it becomes a +control code, the only one that can appear in strings; see below). +Strings should end on the same line as they begin, unless there's a +backslash at the end of lines within them. + +\TEX/ and \CEE/ have different ways to refer to octal and hex constants, +because \TEX/ is oriented to technical writing while \CEE/ is oriented to +computer processing. In \TEX/ you +make a constant octal or hexadecimal by prepending \.' or \.", +respectively, to it; in \CEE/ the constant should be preceded by \.0 +or \.{0x}. In \.{CWEB} it seems reasonable to let each convention hold +in its respective realm; so in \CEE/ text you get $40_8$ by typing +`\.{040}', which \.{CTANGLE} faithfully copies into the \CEE/ file (for +the compiler's benefit) and which \.{CWEAVE} prints as $\T{\~40}$. +Similarly, \.{CWEAVE} prints the hexadecimal \CEE/ constant `\.{0x20}' +as \T{\^20}. The use of italic font for octal digits and typewriter font +for hexadecimal digits makes the meaning of such constants clearer in +a document. For consistency, then, you +should type `\.{|040|}' or `\.{|0x20|}' +in the \TEX/ part of the section. + +\section Control codes. +A \.{CWEB} {\sl control code\/} +is a two-character combination of which the first is `\.@'. +We've already seen the meaning of several control codes; it's time to +list them more methodically. + +In the following list, +the letters in brackets after a control code indicate in what contexts that +code is allowed. $L$ indicates that the code is allowed in limbo; $T$ +(for \TEX/), $M$ (for middle), and $C$ (for \CEE/) mean that the code is +allowed in each of the three parts of a section, at top level---that +is, outside such constructs as `\pb' and section names. An arrow $\to$ +means that the control code terminates the present part of the \.{CWEB} +file, and inaugurates the part indicated by the letter following the +arrow. Thus $[LTMC\to T]$ next to \.{@\ } indicates that this control +code can occur in limbo, or in any of the three parts of a section, and +that it starts the (possibly empty) \TEX/ part of the following section. + +Two other abbreviations can occur in these brackets: The letter $r$ stands for +{\it restricted context}, that is, material inside \CEE/ comments, section +names, \CEE/ strings and control texts (defined below); the letter +$c$ stands for {\it inner \CEE/ context}, that is, \CEE/ material +inside `\pb' (including `\pb's inside comments, but not those +occurring in other restricted contexts). An asterisk $*$ following +the brackets means +that the context from this control code to the matching \.{@>} is +restricted. + +Control codes involving letters are case-insensitive; thus \.{@d} and +\.{@D} are equivalent. Only the lowercase versions are mentioned +specifically below. + +\gdef\@#1[#2] {\penalty-50\yskip\hangindent 2em\noindent\.{@#1\unskip + \spacefactor1000{ }}$[#2]$\quad} +\def\more{\hangindent 2em \hangafter0} +\def\subsec{\penalty-300\medskip\noindent} + +\@@ [LTMCrc] A double \.@ denotes the single character `\.@'. This is +the only control code that is legal everywhere. +Note that you must use this convention if you are giving an internet +email address in a \.{CWEB} file (e.g., \.{levy@@math.berkeley.edu}). + +\subsec +Here are the codes that introduce the \TEX/ part of a section. + +\@\ [LTMC\to T] This denotes the beginning of a new (unstarred) +section. A tab mark or form feed or +end-of-line character is equivalent to a space when it follows an \.@ +sign (and in most other cases). + +\@* [LTMC\to T] This denotes the beginning of a new starred +section, i.e., a section that begins a new major group. The title of the new +group should appear after the \.{@*}, followed by a period. As explained +above, \TEX/ control sequences should be avoided in such titles unless +they are quite simple. When \.{CWEAVE} and \.{CTANGLE} read a \.{@*}, they +print an asterisk on the terminal +followed by the current section number, so that the user +can see some indication of progress. The very first section should be starred. + +\more You can specify the ``depth'' of a starred section by typing \.* or a +decimal number after the \.{@*}; this indicates the relative ranking +of the current group of sections in the program hierarchy. Top-level +portions of the program, introduced by \.{@**}, get their names typeset +in boldface type in the table of contents; they are said to have +depth~$-1$. Otherwise the depth is a nonnegative number, which governs +the amount of indentation on the contents page. Such indentation helps +clarify the structure of a long program. The depth is assumed to be 0 +if it is not specified explicitly; when your program is short, you +might as well leave all depths zero. A starred section always begins +a new page in the output, unless the depth is greater than~1. + +\subsec +The middle part of each section consists of any number of +macro definitions (beginning with \.{@d}) and format definitions (beginning +with \.{@f} or \.{@s}), intermixed in any order. + +\@d [TM\to M] Macro definitions begin with \.{@d}, followed by +an identifier and optional parameters and \CEE/ text as explained earlier. + +\@f [TM\to M] Format definitions begin with \.{@f}; they cause +\.{CWEAVE} to treat identifiers in a special way when they appear in +\CEE/ text. The general form of a format definition is `\.{@f} \|l +\|r', followed by an optional comment enclosed between +\.{/*} and \.{*/}, where \|l and \|r +are identifiers; \.{CWEAVE} will subsequently treat identifier \|l as it +currently treats \|r. This feature allows a \.{CWEB} programmer to invent +new reserved words and/or to unreserve some of \CEE/'s reserved +identifiers. For example, the common words `error' and `line' +have been given a special meaning in the \CEE/ preprocessor, +so \.{CWEAVE} is set up to format them specially; if you want a variable +named \\{error} or \\{line}, you should say +$$\.{@f error normal}\qquad\qquad\.{@f line normal}$$ +somewhere in your program. + +\more If \|r is the special identifier `\\{TeX}', identifier \|l +will be formatted as a \TEX/ control sequence; for example, +`\.{@f foo TeX}' in the \.{CWEB} file will cause identifier \\{foo} to +be output as \.{\\foo} by \.{CWEAVE}. The programmer should define +\.{\\foo} to have whatever custom format is desired, assuming \TEX/ +math mode. (Each underline +character is converted to \.{x} when making the \TEX/ control sequence, +and each dollar sign is converted to~\.X; +thus \\{foo\_bar} becomes \.{\\fooxbar}. Other characters, including digits, +are left untranslated, so \TEX/ will consider them as macro parameters, +not as part of the control sequence itself. For example, +$$\.{\\def\\x\#1\{x\_\{\#1\}\} @f x1 TeX @f x2 TeX}$$ +will format \.{x1} and \.{x2} not as \\{x1} and \\{x2} but as $x_1$ and $x_2$.) + +\more If \|r is the special identifier `\\{make\_pair}', identifier \|l will +be treated as a \CPLUSPLUS/ function template. For example, after +\.{@f}~\.{convert}~\.{make\_pair} one can say `\.{convert<int>(2.5)}' without +having \.< and \.> misunderstood as less-than and greater-than signs. + +\more \.{CWEAVE} knows that identifiers being +defined with a \&{typedef} should become reserved words; thus you +don't need format definitions very often. + +\@s [TM\to M;\;L] Same as \.{@f}, but \.{CWEAVE} does not show the format +definition in the output, and the optional \CEE/ comment is not +allowed. This is used mostly in \.{@i} files. + +\subsec +Next come the codes that govern the \CEE/ part of a section. + +\@{c @p} [TM\to C] The \CEE/ part of an unnamed section begins with \.{@c} +(or with \.{@p} for ``program''; both control codes do the same thing). +This causes \.{CTANGLE} to append the following \CEE/ code +to the first-order program text, as explained on page~\tangref. +Note that \.{CWEAVE} does not print a `\.{@c}' in the \TEX/ +output, so if you are creating a \.{CWEB} file based on a \TEX/-printed +\.{CWEB} documentation you have to remember to insert \.{@c} in the +appropriate places of the unnamed sections. + +\@< [TM\to C;\; C;\; c] $*$ This control code introduces a +section name (or unambiguous prefix, as discussed above), which +consists of \TEX/ text and extends to the matching \.{@>}. +The whole construct \.{@<...@>} is conceptually a \CEE/ element. +The behavior is different depending on the context: + +\more A \.{@<} appearing in contexts $T$ and $M$ attaches the +following section name to the current section, and inaugurates the +\CEE/ part of the section. The closing \.{@>} should be followed by +\.{=} or \.{+=}. + +\more +In context $C$, \.{@<} indicates that the named +section is being used---its \CEE/ definition is spliced in by +\.{CTANGLE}, as explained on page~\tangref. +As an error-detection measure, +\.{CTANGLE} and \.{CWEAVE} complain if such a section name is followed +by \.=, because most likely this is meant as the definition of a new +section, and so should be preceded by \.{@\ }. If you really want to +say $\langle\,$foo$\,\rangle=\\{bar}$, where $\langle\,$foo$\,\rangle$ +is being used and not defined, put a newline before the \.=. + +\more +Finally, in inner \CEE/ context (that is, within `\pb' in the \TEX/ part +of a section or in a comment), \.{@<...@>} +means that the named section is being +cited. Such an occurrence is ignored by \.{CTANGLE}. Note that +even here we think of the section name as being a \CEE/ element, hence the \pb. + +\@( [TM\to C;\;C;\;c] $*$ A section name can begin with \.{@(}. +Everything works just as for \.{@<}, except that the \CEE/ code +of the section named \.{@(foo@>} is written by \.{CTANGLE} +to file \.{foo}. In this way you can get multiple-file output from +a single \.{CWEB} file. (The \.{@d} definitions are not output +to such files, only to the master \.{.c} file.) One use of this feature +is to produce header files for other program modules that will be loaded +with the present one. Another use is to produce a test routine that +goes with your program. By keeping the sources for a program and its +header and test routine together, you are more likely to keep +all three consistent with each other. Notice that the output of a named +section can be incorporated in several different output files, because +you can mention \.{@<foo@>} in both \.{@(bar1@>} and \.{@(bar2@>}. + +\@h [Cc] Causes \.{CTANGLE} +to insert at the current spot the \.{\#define} statements +from the middle parts of all sections, +and {\it not\/} to write them at the beginning of the \CEE/ file. +Useful when you want the macro definitions to come after the include files, +say. (Ignored by \.{CTANGLE} inside `\pb'.) + +\subsec +The next several control codes introduce ``control +texts,'' which end with the next `\.{@>}'. The closing `\.{@>}' must be on +the same line of the \.{CWEB} file as the line where the control text began. +The context from each of these control codes to the matching \.{@>} is +restricted. + +\@\^ [TMCc] $*$ The control text that follows, up to the next +`\.{@>}', will be entered into the index together with the identifiers of +the \CEE/ program; this text will appear in roman type. For example, to +put the phrase ``system dependencies'' into the index that is output by +\.{CWEAVE}, type +`\.{@\^system dependencies@>}' in each section +that you want to index as system dependent. + +\@. [TMCc] $*$ The control text that follows will be entered into the index +in \.{typewriter} \.{type}. + +\@: [TMCc] $*$ The control text that follows will be entered into the index +in a format controlled by the \TEX/ macro `\.{\\9}', which you +should define as desired. + +\@t [MCc] $*$ The control text that follows will +be put into a \TEX/ \.{\\hbox} and formatted along with the neighboring +\CEE/ program. This text is ignored by \.{CTANGLE}, but it can be used +for various purposes within \.{CWEAVE}. For example, you can make comments +that mix \CEE/ and classical mathematics, as in `$\\{size}<2^{15}$', by +typing `\.{|size < 2@t\$\^\{15\}\$@>|}'. + +\@= [MCc] $*$ The control text that follows will +be passed verbatim to the \CEE/ program. + +\@q [LTMCc] $*$ The control text that follows will +be totally ignored---it's a comment for readers of the \.{CWEB} file only. +A file intended to be included in +limbo, with \.{@i}, can identify itself with \.{@q} comments. +Another use is to balance unbalanced parentheses in \CEE/ strings, +so that your text editor's parenthesis matcher doesn't go into a tailspin. + +\@! [TMCc] $*$ +The section number in an index entry will be underlined if `\.{@!}' +immediately precedes the identifier or control text being indexed. This +convention is used to distinguish the sections where an identifier is +defined, or where it is explained in some special way, from the sections +where it is used. A~reserved word or an identifier of length one will not +be indexed except for underlined entries. An `\.{@!}' is implicitly inserted +by \.{CWEAVE} when an identifier is being defined or declared in \CEE/ +code; for example, the definition +$$\hbox{\&{int} \\{array}[\\{max\_dim}], \\{count}${}=\\{old\_count};$}$$ +makes the names \\{array} and \\{count} get an underlined entry in the +index. Statement labels, function definitions like +\\{main}(\&{int}~\\{argc},\,\&{char}~$*$\\{argv}[\,]), +and \&{typedef} definitions also +imply underlining. An old-style +function definition (without prototyping) doesn't define its arguments; +the arguments will, however, be considered to be defined +(i.e., their index entries will be underlined) if their types are +declared before the body of the function in the usual way +(e.g., `\&{int}~\\{argc}; \&{char}~${*}\\{argv}[\,]$; $\{\,\ldots\,\}$'). +Thus \.{@!} is not needed very often, except in unusual constructions +or in cases like +$$\.{enum boolean \{@!false, @!true\};}$$ +here \.{@!} gives the best results because individual constants enumerated +by \.{enum} are not automatically underlined in the index at their +point of definition. + +\subsec +We now turn to control codes that affect only the operation of +\.{CTANGLE}. + +\@' [MCc] This control code is dangerous because it has quite different +meanings in \.{CWEB} and the original \.{WEB}. In \.{CWEB} it produces the +decimal constant corresponding to the ASCII code for a string of length~1 +(e.g., \.{@'a'} is \.{CTANGLE}d into \.{97} and \.{@'\\t'} into +\.9). You might want to use this if you need to work in ASCII on a +non-ASCII machine; but in most cases the \CEE/ conventions of +\.{<ctype.h>} are adequate for character-set-independent programming. + +\@\& [MCc] The \.{@\&} operation causes whatever is on its left to be +adjacent to whatever is on its right, in the \CEE/ output. No spaces or +line breaks will separate these two items. + +\@l [L] \.{CWEB} programmers have the option of using any 8-bit character +code from the often-forbidden range 128--255 within \TEX/ text; such +characters are also permitted in strings and even in identifiers of the +\CEE/ program. +Under various extensions of the basic +ASCII standard, the higher 8-bit codes correspond +to accented letters, letters from non-Latin alphabets, +and so on. When such characters occur in identifiers, \.{CTANGLE} must replace +them by standard ASCII alphanumeric characters or +\.{\_}, in order to generate legal \CEE/ code. It does this by means +of a transliteration table, which by default associates the string +\.{Xab} to the character with ASCII code \T{\^}$ab$ (where $a$ and $b$ are +hexadecimal digits, and $a\ge8$). By placing the +construction \.{@l\ ab\ newstring} in limbo, you are telling +\.{CTANGLE} to replace this character by \.{newstring} instead. +For example, the ISO Latin-1 code for the letter `\"u' is \T{\^FC} +(or \.{'\char`\\374'}), +and \.{CTANGLE} will normally change this code to the +three-character sequence \.{XFC} if it +appears in an identifier. If you say \.{@l} \.{fc} \.{ue}, the code will +be transliterated into \.{ue} instead. + +\more +\.{CWEAVE} passes 8-bit characters straight through to \TEX/ without +transliteration; therefore \TEX/ must be prepared to receive them. +If you are formatting all your nonstandard identifiers as ``custom'' +control sequences, you should +make \TEX/ treat all their characters as letters. Otherwise you should either +make your 8-bit codes ``active'' in \TEX/, or load fonts that +contain the special characters you need in the correct positions. +(The font selected by \TEX/ control sequence \.{\\it} is used for +identifiers.) +Look for special macro packages designed for \.{CWEB} users in your language; +or, if you are brave, write one yourself. + +\subsec +The next eight control codes (namely `\.{@,}', `\.{@/}', `\.{@|}', `\.{@\#}', +`\.{@+}', `\.{@;}', `\.{@[}', and `\.{@]}') have no effect on the \CEE/ +program output by \.{CTANGLE}; they merely help to improve the readability +of the \TEX/-formatted \CEE/ that is output by \.{CWEAVE}, in unusual +circumstances. \.{CWEAVE}'s built-in formatting method is fairly good +when dealing with syntactically correct \CEE/ text, but +it is incapable of handling all possible cases, because it must deal with +fragments of text involving macros and section names; these fragments do +not necessarily obey \CEE/'s syntax. Although \.{CWEB} allows you to +override the automatic formatting, your best strategy is not to worry +about such things until you have seen what \.{CWEAVE} produces automatically, +since you will probably need to make only a few corrections when you are +touching up your documentation. + +\@, [MCc] This control code inserts a thin space in \.{CWEAVE}'s output. +Sometimes you need this extra space if you are using +macros in an unusual way, e.g., if two identifiers are adjacent. + +\@/ [MC] This control code causes a line break to occur within a \CEE/ +program formatted by \.{CWEAVE}. Line breaks +are chosen automatically by \TEX/ according to a scheme that works 99\%\ +of the time, but sometimes you will prefer to force a line break so that +the program is segmented according to logical rather than visual +criteria. If a comment follows, say `\.{@/@,}' to break the line +before the comment. + +\@| [MC] This control code specifies an optional line break in the midst of +an expression. For example, if you have +a long expression on the right-hand side of an assignment +statement, you can use `\.{@|}' to specify breakpoints more logical than +the ones that \TEX/ might choose on visual grounds. + +\@\# [MC] This control code forces a line break, like \.{@/} does, +and it also causes a little extra white space to appear between the lines at +this break. You might use it, for example, +between groups of macro definitions that are logically separate but within +the same section. \.{CWEB} automatically inserts this extra space +between functions, between external declarations and functions, and +between declarations and statements within a function. + +\@+ [MC] This control code cancels a line break that might otherwise be +inserted by \.{CWEAVE}, e.g., before the word `\&{else}', if you want to +put a short if--else construction on a single line. +If you say `\.{\{@+}' at the beginning of a compound statement +that is the body of a function, the first declaration or +statement of the function will appear on the same line as the +left brace, and it will be indented by the same amount as the +second declaration or statement on the next line. + +\@; [MC] This control code is treated like a semicolon, for formatting +purposes, except that it is invisible. You can use it, for example, after +a section name or macro when the \CEE/ text represented by that section or macro +is a compound statement or ends +with a semicolon. Consider constructions like +$$\lpile{\.{if (condition) macro @;}\cr +\.{else break;}\cr}$$ +where \\{macro} is defined to be a compound statement (enclosed in braces). +This is a well-known infelicity of \CEE/ syntax. + +\@{[} [MC] See \.{@]}. + +\@] [MC] Place \.{@[...@]} brackets around program text that \.{CWEAVE} is +supposed to format as an expression, if it doesn't already do so. (This +occasionally applies to unusual macro arguments.) Also +insert `\.{@[@]}' between a simple type name and a left parenthesis +when declaring a pointer to a function, as in +$$\.{int @[@] (*f)();}$$ +otherwise \.{CWEAVE} will confuse the first part of that declaration with +the \CPLUSPLUS/ expression `\&{int}($*f$)'. Another example, for people +who want to use low-level \.{\#define} commands in the midst of \CEE/ code +and the definition begins with a cast: +$$\.{\#define foo @[(int)(bar)@]}$$ + +\subsec +The remaining control codes govern the input that \.{CWEB} sees. + +\@{x @y @z}[\\{change\_file}] +\.{CWEAVE} and \.{CTANGLE} are designed to work with two input files, +called \\{web\_file} and \\{change\_file}, where \\{change\_file} contains +data that overrides selected portions of \\{web\_file}. The resulting merged +text is actually what has been called the \.{CWEB} file elsewhere in this +report. + +\more Here's how it works: The change file consists of zero or more ``changes,'' +where a change has the form `\.{@x}$\langle$old lines$\rangle$\.{@y}$\langle$% +new lines$\rangle$\.{@z}'. The special control codes \.{@x}, \.{@y}, \.{@z}, +which are allowed only in change files, must appear at the beginning of a line; +the remainder of such a line is ignored. +The $\langle$old lines$\rangle$ represent material that exactly matches +consecutive lines of the \\{web\_file}; the $\langle$new lines$\rangle$ +represent zero or more lines that are supposed to replace the old. Whenever +the first ``old line'' of a change is found to match a line in the +\\{web\_file}, all the other lines in that change must match too. + +\more Between changes, before the first change, and after the last change, +the change file can have any number of lines that do not begin with +`\.{@x}', `\.{@y}', or~`\.{@z}'. Such lines are bypassed and not used for +matching purposes. + +\more This dual-input feature is useful when working with a master \.{CWEB} +file that has been received from elsewhere (e.g., \.{tangle.w} or +\.{weave.w} or \.{tex.web}), when changes are desirable to customize the +program for your local computer system. You will be able to debug your +system-dependent changes without clobbering the master web file; and once +your changes are working, you will be able to incorporate them readily +into new releases of the master web file that you might receive from time +to time. + +\@i [\\{web\_file}] +Furthermore the \\{web\_file} itself can be a combination of +several files. When either \.{CWEAVE} or \.{CTANGLE} is reading a file and +encounters the control code \.{@i} at the beginning of a line, it +interrupts normal reading and starts looking at the file named after the +\.{@i}, much as the \CEE/ preprocessor does when it encounters an \.{\#include} +line. After the included file has been entirely read, the program +goes back to the next line +of the original file. The file name following \.{@i} can be +surrounded by \." characters, but such delimiters are +optional. Include files can nest. + +\more +Change files can have lines starting with \.{@i}. In this way +you can replace one included file with another. Conceptually, the +replacement mechanism described above does its work first, and +its output is then checked for \.{@i} lines. If \.{@i} \.{foo} +occurs between \.{@y} and \.{@z} in a change file, individual lines +of file \.{foo} and files it includes are not changeable; but changes +can be made to lines from files that were included by unchanged input. + +\more On \UNIX/ systems (and others that support environment variables), +if the environment variable \.{CWEBINPUTS} is set, or if the compiler flag +of the same name was defined at compile time, +\.{CWEB} will look for include files in the directory thus named, if +it cannot find them in the current directory. + +\section Additional features and caveats. + +1. In certain installations of \.{CWEB} that +{\def\\#1#2{`{\tentex\char'#1#2}'}% +have an extended character set, the characters +\\13, \\01, \\31, \\32, \\34, \\35, +\\36, \\37, \\04, \\20, and \\21} +can be typed as abbreviations for `\.{++}', `\.{--}', `\.{->}', +`\.{!=}', `\.{<=}', `\.{>=}', `\.{==}', `\.{\v\v}', `\.{\&\&}', +`\.{<<}', and `\.{>>}', +respectively. + +2. If you have an extended character set, you can use it with only minimal +restrictions, as discussed under the rules for \.{@l} above. But you should +stick to standard ASCII characters if you want to write programs that will +be useful to all the poor souls out there who don't have extended +character sets. + +3. The \TEX/ file output by \.{CWEAVE} is broken into lines having at most +80 characters each. When +\TEX/ text is being copied, the existing line breaks are copied as well. +If you aren't doing anything too tricky, \.{CWEAVE} will recognize when +a \TEX/ comment is being split across two or more lines, and it will +append `\.\%' to the beginning of such continued comments. + +4. \CEE/ text is translated by a ``bottom up'' procedure that +identifies each token as a ``part of speech'' and combines parts of speech +into larger and larger phrases as much as possible according to a special +grammar that is explained in the documentation of \.{CWEAVE}. It is easy to +learn the translation scheme for simple constructions like single +identifiers and short expressions, just by looking at a few examples of +what \.{CWEAVE} does, but the general mechanism is somewhat complex because +it must handle much more than \CEE/ itself. Furthermore the output +contains embedded codes that cause \TEX/ to indent and break lines as +necessary, depending on the fonts used and the desired page width. For +best results it is wise to avoid enclosing long \CEE/ texts in \pb, since the +indentation and line breaking codes are omitted when the \pb\ text is +translated from \CEE/ to \TEX/. Stick to simple expressions or +statements. If a \CEE/ preprocessor command is enclosed in \pb, +the \.\# that introduces it must be at the beginning of a line, +or \.{CWEAVE} won't print it correctly. + +5. Comments are not permitted in \pb\ text. After a `\.|' +signals the change from \TEX/ text to \CEE/ text, the next `\.|' that is +not part of a string or control text or section name ends the \CEE/ text. + +6. A comment must have properly nested occurrences of left and right +braces, otherwise \.{CWEAVE} will complain. But it +does try to balance the braces, so that \TEX/ won't foul up too much. + +7. When you're debugging a program and decide to omit some of your +\CEE/ code, do NOT simply ``comment it out.'' Such comments are not +in the spirit of \.{CWEB} documentation; they will appear to readers +as if they were explanations of the uncommented-out instructions. +Furthermore, comments of a program must be valid \TEX/ text; hence +\.{CWEAVE} will get confused if you enclose \CEE/ statements in +\.{/*...*/} instead of in \.{/*|...|*/}. If you must comment out +\CEE/ code, you can surround it with preprocessor commands +like \.{\#if 0==1} and \.{\#endif}. + +8. The \.{@f} feature allows you to define one identifier to act like +another, and these format definitions are carried out sequentially. +In general, a given identifier has only one printed format +throughout the entire document, and this format is used even before +the \.{@f} that defines it. The reason is that \.{CWEAVE} operates in two +passes; it processes \.{@f}'s and cross-references on the first pass and +it does the output on the second. (However, identifiers that +implicitly get a boldface format, thanks to a \.{typedef} declaration, +don't obey this rule; they are printed differently before and after +the relevant \.{typedef}. This is unfortunate, but hard to fix. You can +get around the restriction by saying, say, `\.{@s} \.{foo} \.{int}', +before or after the \.{typedef}.) + +9. Sometimes it is desirable to insert spacing into formatted \CEE/ code that +is more general than the thin space provided by `\.{@,}'. The \.{@t} feature +can be used for this purpose; e.g., `\.{@t\\hskip 1in@>}' will +leave one inch of blank space. Furthermore, `\.{@t\\4@>}' can be +used to backspace by one unit of indentation, since the control sequence +\.{\\4} is defined in \.{cwebmac} to be such a backspace. (This +control sequence is used, for example, at the beginning of lines that +contain labeled statements, so that the label will stick out a little at +the left.) You can also use `\.{@t\}\\3\{-5@>}' to force a break +in the middle of an expression. + +10. Each identifier in \.{CWEB} has a single formatting convention. Therefore +you shouldn't use the same identifier to denote, say, both a type name and +part of a \.{struct}, even though \CEE/ does allow this. + +\section Running the programs. +The \UNIX/ command line for \.{CTANGLE} is +$$\.{ctangle [options] web\_file[.w] [\{change\_file[.ch]|-\} [out\_file]]}$$ +and the same conventions apply to \.{CWEAVE}. If `\.-' or no change file is +specified, the change file is null. The extensions \.{.w} and \.{.ch} +are appended only if the given file names contain no dot. If the +web file defined in this way cannot be found, the extension \.{.web} +will be tried. For example, `\.{cweave} \.{cob}' will try to read +\.{cob.w}; failing that, it will try \.{cob.web} before giving up. +If no output file name is specified, the name of the \CEE/ file output by +\.{CTANGLE} is obtained by appending the extension \.{.c}; +the name of the \TEX/ file output by \.{CWEAVE} gets the extension \.{.tex}. +Index files output by \.{CWEAVE} replace \.{.tex} by \.{.idx} and \.{.scn}. + +Programmers who like terseness might choose to set up their + operating shell so that `\.{wv}' expands to +`\.{cweave -bhp}'; this will suppress most terminal output from \.{CWEAVE} +except for error messages. + +Options are introduced either by a \.- sign, to turn an option off, +or by a \.+ sign to turn one on. For example, `\.{-fb}' turns off +options \.f and \.b; `\.{+s}' turns on option \.s. Options can be +specified before the file names, after the file names, or both. The following +options are currently implemented: + +\yskip +\def\option#1 {\textindent{\.#1}\hangindent2\parindent} + +\option b Print a banner line at the beginning of execution. (On +by default.) + +\option e Enclose \CEE/ material formatted by \.{CWEAVE} in +brackets \.{\\PB\{...\}}, so that special hooks can be used. +(Off by default; has no effect on \.{CTANGLE}.) + +\option f Force line breaks after each \CEE/ statement formatted +by \.{CWEAVE}. (On by default; \.{-f} saves paper but looks less \CEE/-like +to some people.) (Has no effect on \.{CTANGLE}.) + +\option h Print a happy message at the conclusion of a successful +run. (On by default.) + +\option p Give progress reports as the program runs. (On by default.) + +\option s Show statistics about memory usage after the program +runs to completion. (Off by default.) +If you +have large \.{CWEB} files or sections, you may need to see +how close you come to exceeding the capacity of \.{CTANGLE} and/or \.{CWEAVE}. + +\option x Include indexes and a table of contents in the \TEX/ file +output by \.{CWEAVE}. (On by default.) (Has no effect on \.{CTANGLE}.) + +\section Further details about formatting. +You may not like the way \.{CWEAVE} handles certain +situations. If you're desperate, you can customize \.{CWEAVE} +by changing its grammar. This means changing the source code, +a task that you might find amusing. A table of grammar rules +appears in the \.{CWEAVE} source listing, and you can make a separate +copy of that table by copying the file \.{prod.w} found in the \.{CWEB} +sources and saying `\.{cweave}~\.{-x}~\.{prod}', followed by +`\.{tex}~\.{prod}'. + +You can see exactly +how \.{CWEAVE} is parsing your \CEE/ code by preceding +it with the line `\.{@ @c @2}'. (The control code `\.{@2}' +turns on a ``peeping'' mode, and `\.{@0}' turns it off.) +For example, if you run \.{CWEAVE} on the file +\medskip +\begingroup +\verbatim +@ @c @2 +main (argc,argv) +char **argv; +{ for (;argc>0;argc--) printf("%s\n",argv[argc-1]); } +!endgroup +\endgroup +\medskip\noindent +you get the following gibberish on your screen: +\medskip +\begingroup +\verbatim +[...] +14:*exp +(+ exp... +11:*exp +exp+ raw... +10:*+exp+ raw +ubinop?+... +[...] +60: +fn_decl+*+{+ -stmt- +}- +55:*+fn_decl+ -stmt- +52:*+function- +[...] +!endgroup +\endgroup +\medskip +The first line says that grammar rule 14 has just been applied, and \.{CWEAVE} +currently has in its memory a sequence of chunks of \TEX/ code (called +``scraps'') that are respectively +of type \\{exp} (for expression), open-parenthesis, +\\{exp} again, close-parenthesis, and further scraps that haven't yet +been considered by the parser. (The \.+ and \.- signs stipulate that +\TEX/ should be in or out of math mode at the scrap boundaries. The \.* shows +the parser's current position.) +Then rule 11 is applied, and +the sequence $(\,exp\,)$ becomes an \\{exp} and so on. In the +end the whole \CEE/ text has become one big scrap of type \\{function}. + +Sometimes things don't work as smoothly, and you get a bunch of +lines lumped together. This means that \.{CWEAVE} could not +digest something in your \CEE/ code. For instance, suppose +`\.{@<Argument definitions@>}' had appeared instead of +`\.{char **argv;}' in the program above. Then \.{CWEAVE} would have +been somewhat mystified, since it thinks that section names +are just \\{exp}s. Thus it would tell \TEX/ to format +`\X2:Argument declarations\X' on the same line as +`$\\{main}(\\{argc},\39\\{argv}{}$)'. +In this case you should help \.{CWEAVE} by putting `\.{@/}' after +`\.{main(argc,argv)}'. + +\.{CWEAVE} automatically inserts a bit of extra space between declarations +and the first apparent statement of a block. One way to defeat this +spacing locally is +$$\vbox{\halign{#\hfil\cr +\.{int x;@+@t\}\\6\{@>}\cr +\.{@<Other locals@>@;@\#}\cr}}$$ +the `\.{@\#}' will put extra space after `$\langle\,$Other locals$\,\rangle$'. + +\section Hypertext and hyperdocumentation. + +Many people have of course noticed analogies between \.{CWEB} and the +World Wide Web. The \.{CWEB} macros are in fact set up so that the output of +\.{CWEAVE} can be converted easily into Portable Document Format, +with clickable hyperlinks that can be read with Adobe's Acrobat Reader, +using a widely available open-source program called \.{dvipdfm} developed +by Mark~A. Wicks. After using \.{CWEAVE} to convert \.{cob.w} into +\.{cob.tex}, you can prepare and view a hypertext version of the program +by giving the commands +$$\vbox{\halign{\.{#}\hfil\cr +tex "\\let\\pdf+ \\input cob"\cr +dvipdfm cob\cr +acroread cob.pdf\cr}}$$ +instead of invoking \TeX\ in the normal way. (Thanks to Hans Hagen, +C\'esar Augusto Rorato Crusius, and Julian Gilbey +for the macros that make this work.) +Alternatively, thanks to H\`an Th\^e\kern-.3em\raise.3ex\hbox{\'{}} Th\`anh +and Andreas Scherer, you can generate \.{cob.pdf} in one step by simply +saying `\.{pdftex}~\.{cob}'. + +A more elaborate system called \.{CTWILL}, which extends the usual cross +references of \.{CWEAVE} by preparing links from the uses of identifiers +to their definitions, is also available---provided that you are willing +to work a bit harder in cases where an identifier is multiply defined. +\.{CTWILL} is intended primarily for hardcopy output, but its principles +could be used for hypertext as well. +See Chapter 11 of {\sl Digital Typography\/} by D.~E. Knuth (1999), and +the program sources at \.{ftp://ftp.cs.stanford.edu/pub/ctwill}. + +\section Appendices. + +As an example of a real program written in \.{CWEB}, Appendix~A +contains an excerpt from the \.{CWEB} program itself. The reader who +examines the listings in this appendix carefully will get a good +feeling for the basic ideas of \.{CWEB}. + +Appendix B is the file that sets \TEX/ up to accept +the output of \.{CWEAVE}, and Appendix~C discusses how to use some of those +macros to vary the output formats. + +A ``long'' version of this manual, which can be produced from the \.{CWEB} +sources via the \UNIX/ command \.{make} \.{fullmanual}, also contains +appendices D, E, and~F, which exhibit the complete source code for +\.{CTANGLE} and \.{CWEAVE}. + +\vfil\eject\titletrue + +\def\runninghead{APPENDIX A --- {\tentt CWEB} FILE FORMAT} +\section Appendix A: Excerpts from a \.{CWEB} Program. + +This appendix consists +of four listings. The first shows the \.{CWEB} input that +generated sections 12--15 of the file \.{common.w}, which contains +routines common to \.{CWEAVE} and \.{CTANGLE}. +Note that some of the lines are indented to show the program structure; +the indentation is ignored by \.{CWEAVE} and \.{CTANGLE}, but users find +that \.{CWEB} files are quite readable if they have some such indentation. + +The second and third listings +show corresponding parts +of the \CEE/ code output by \.{CTANGLE} and of +the corresponding \TEX/ code output by \.{CWEAVE}, when run on \.{common.w}. +The fourth listing shows how that output looks when printed out. + +\vskip 6pt +\begingroup \def\tt{\eighttt} \baselineskip9pt +\verbatim +@ Procedure |prime_the_change_buffer| +sets |change_buffer| in preparation for the next matching operation. +Since blank lines in the change file are not used for matching, we have +|(change_limit==change_buffer && !!changing)| if and only if +the change file is exhausted. This procedure is called only when +|changing| is 1; hence error messages will be reported correctly. + +@c +void +prime_the_change_buffer() +{ + change_limit=change_buffer; /* this value is used if the change file ends */ + @<Skip over comment lines in the change file; |return| if end of file@>; + @<Skip to the next nonblank line; |return| if end of file@>; + @<Move |buffer| and |limit| to |change_buffer| and |change_limit|@>; +} + +@ While looking for a line that begins with \.{@@x} in the change file, we +allow lines that begin with \.{@@}, as long as they don't begin with \.{@@y}, +\.{@@z}, or \.{@@i} (which would probably mean that the change file is fouled up). + +@<Skip over comment lines in the change file...@>= +while(1) { + change_line++; + if (!!input_ln(change_file)) return; + if (limit<buffer+2) continue; + if (buffer[0]!!='@@') continue; + if (xisupper(buffer[1])) buffer[1]=tolower(buffer[1]); + if (buffer[1]=='x') break; + if (buffer[1]=='y' || buffer[1]=='z' || buffer[1]=='i') { + loc=buffer+2; + err_print("!! Missing @@x in change file"); +@.Missing @@x...@> + } +} + +@ Here we are looking at lines following the \.{@@x}. + +@<Skip to the next nonblank line...@>= +do { + change_line++; + if (!!input_ln(change_file)) { + err_print("!! Change file ended after @@x"); +@.Change file ended...@> + return; + } +} while (limit==buffer); + +@ @<Move |buffer| and |limit| to |change_buffer| and |change_limit|@>= +{ + change_limit=change_buffer-buffer+limit; + strncpy(change_buffer,buffer,limit-buffer+1); +} + +!endgroup +\endgroup +\vfill\eject + +\def\runninghead{APPENDIX A --- TRANSLATION BY {\tentt CTANGLE}} + +Here's the portion of the \CEE/ code generated by \.{CTANGLE} that corresponds +to the source on the preceding page. Notice that sections~13, 14 and~15 +have been tangled into section~12. + +\vskip6pt +\begingroup \def\tt{\eighttt} \baselineskip9pt +\verbatim +/*:9*//*12:*/ +#line 247 "common.w" + +void +prime_the_change_buffer() +{ +change_limit= change_buffer; +/*13:*/ +#line 261 "common.w" + +while(1){ +change_line++; +if(!!input_ln(change_file))return; +if(limit<buffer+2)continue; +if(buffer[0]!!='@')continue; +if(xisupper(buffer[1]))buffer[1]= tolower(buffer[1]); +if(buffer[1]=='x')break; +if(buffer[1]=='y'||buffer[1]=='z'||buffer[1]=='i'){ +loc= buffer+2; +err_print("!! Missing @x in change file"); + +} +} + +/*:13*/ +#line 252 "common.w" +; +/*14:*/ +#line 278 "common.w" + +do{ +change_line++; +if(!!input_ln(change_file)){ +err_print("!! Change file ended after @x"); + +return; +} +}while(limit==buffer); + +/*:14*/ +#line 253 "common.w" +; +/*15:*/ +#line 288 "common.w" + +{ +change_limit= change_buffer-buffer+limit; +strncpy(change_buffer,buffer,limit-buffer+1); +} + +/*:15*/ +#line 254 "common.w" +; +} + +/*:12*//*16:*/ +!endgroup +\endgroup +\vfill\eject + +\def\runninghead{APPENDIX A --- TRANSLATION BY {\tentt CWEAVE}} + +Here is the corresponding excerpt from \.{common.tex}. + +\vskip6pt +\begingroup \def\tt{\eighttt} \baselineskip9pt +\verbatim +\M{12}Procedure \PB{\\{prime\_the\_change\_buffer}} +sets \PB{\\{change\_buffer}} in preparation for the next matching operation. +Since blank lines in the change file are not used for matching, we have +\PB{$(\\{change\_limit}\E\\{change\_buffer}\W\R\\{changing})$} if and only if +the change file is exhausted. This procedure is called only when +\PB{\\{changing}} is 1; hence error messages will be reported correctly. +!vskip-.5!baselineskip +\Y\B\&{void} \\{prime\_the\_change\_buffer}(\,)\1\1\2\2\6 +${}\{{}$\1\6 +${}\\{change\_limit}\K\\{change\_buffer}{}$;\C{ this value is used if the +change file ends }\6 +\X13:Skip over comment lines in the change file; \PB{\&{return}} if end of file% +\X;\6 +\X14:Skip to the next nonblank line; \PB{\&{return}} if end of file\X;\6 +\X15:Move \PB{\\{buffer}} and \PB{\\{limit}} to \PB{\\{change\_buffer}} and % +\PB{\\{change\_limit}}\X;\6 +\4${}\}{}$\2\par +\fi +!vskip-.5!baselineskip +\M{13}While looking for a line that begins with \.{@x} in the change file, we +allow lines that begin with \.{@}, as long as they don't begin with \.{@y}, +\.{@z}, or \.{@i} (which would probably mean that the change file is fouled +up). +!vskip-.5!baselineskip +\Y\B\4\X13:Skip over comment lines in the change file; \PB{\&{return}} if end +of file\X${}\E{}$\6 +\&{while} (\T{1})\5 +${}\{{}$\1\6 +${}\\{change\_line}\PP;{}$\6 +\&{if} ${}(\R\\{input\_ln}(\\{change\_file})){}$\1\5 +\&{return};\2\6 +\&{if} ${}(\\{limit}<\\{buffer}+\T{2}){}$\1\5 +\&{continue};\2\6 +\&{if} ${}(\\{buffer}[\T{0}]\I\.{'@'}){}$\1\5 +\&{continue};\2\6 +\&{if} (\\{xisupper}(\\{buffer}[\T{1}]))\1\5 +${}\\{buffer}[\T{1}]\K\\{tolower}(\\{buffer}[\T{1}]);{}$\2\6 +\&{if} ${}(\\{buffer}[\T{1}]\E\.{'x'}){}$\1\5 +\&{break};\2\6 +\&{if} ${}(\\{buffer}[\T{1}]\E\.{'y'}\V\\{buffer}[\T{1}]\E\.{'z'}\V\\{buffer}[% +\T{1}]\E\.{'i'}){}$\5 +${}\{{}$\1\6 +${}\\{loc}\K\\{buffer}+\T{2};{}$\6 +\\{err\_print}(\.{"!!\ Missing\ @x\ in\ cha}\)\.{nge\ file"});\6 +\4${}\}{}$\2\6 +\4${}\}{}$\2\par +\U12.\fi +!vskip-.5!baselineskip +\M{14}Here we are looking at lines following the \.{@x}. +!vskip-.5!baselineskip +\Y\B\4\X14:Skip to the next nonblank line; \PB{\&{return}} if end of file\X${}% +\E{}$\6 +\&{do}\5 +${}\{{}$\1\6 +${}\\{change\_line}\PP;{}$\6 +\&{if} ${}(\R\\{input\_ln}(\\{change\_file})){}$\5 +${}\{{}$\1\6 +\\{err\_print}(\.{"!!\ Change\ file\ ended}\)\.{\ after\ @x"});\6 +\&{return};\6 +\4${}\}{}$\2\6 +\4${}\}{}$\2\5 +\&{while} ${}(\\{limit}\E\\{buffer}){}$;\par +\U12.\fi +!vskip-.5!baselineskip +\M{15}\B\X15:Move \PB{\\{buffer}} and \PB{\\{limit}} to \PB{\\{change\_buffer}} +and \PB{\\{change\_limit}}\X${}\E{}$\6 +${}\{{}$\1\6 +${}\\{change\_limit}\K\\{change\_buffer}-\\{buffer}+\\{limit};{}$\6 +${}\\{strncpy}(\\{change\_buffer},\39\\{buffer},\39\\{limit}-\\{buffer}+% +\T{1});{}$\6 +\4${}\}{}$\2\par +\Us12\ET16.\fi +!endgroup +\endgroup +\vfil\eject + +\def\runninghead{APPENDIX A --- FINAL DOCUMENT} + +And here's what the same excerpt looks like when typeset. + +\M{12}Procedure \PB{\\{prime\_the\_change\_buffer}} +sets \PB{\\{change\_buffer}} in preparation for the next matching operation. +Since blank lines in the change file are not used for matching, we have +\PB{$(\\{change\_limit}\E\\{change\_buffer}\W\R\\{changing})$} if and only if +the change file is exhausted. This procedure is called only when +\PB{\\{changing}} is 1; hence error messages will be reported correctly. + +\Y\B\&{void} \\{prime\_the\_change\_buffer}(\,)\1\1\2\2\6 +${}\{{}$\1\6 +${}\\{change\_limit}\K\\{change\_buffer}{}$;\C{ this value is used if the +change file ends }\6 +\X13:Skip over comment lines in the change file; \PB{\&{return}} if end of file% +\X;\6 +\X14:Skip to the next nonblank line; \PB{\&{return}} if end of file\X;\6 +\X15:Move \PB{\\{buffer}} and \PB{\\{limit}} to \PB{\\{change\_buffer}} and % +\PB{\\{change\_limit}}\X;\6 +\4${}\}{}$\2\par +\fi + +\M{13}While looking for a line that begins with \.{@x} in the change file, we +allow lines that begin with \.{@}, as long as they don't begin with \.{@y}, +\.{@z}, or \.{@i} (which would probably mean that the change file is fouled up). + +\Y\B\4\X13:Skip over comment lines in the change file; \PB{\&{return}} if end +of file\X${}\E{}$\6 +\&{while} (\T{1})\5 +${}\{{}$\1\6 +${}\\{change\_line}\PP;{}$\6 +\&{if} ${}(\R\\{input\_ln}(\\{change\_file})){}$\1\5 +\&{return};\2\6 +\&{if} ${}(\\{limit}<\\{buffer}+\T{2}){}$\1\5 +\&{continue};\2\6 +\&{if} ${}(\\{buffer}[\T{0}]\I\.{'@'}){}$\1\5 +\&{continue};\2\6 +\&{if} (\\{xisupper}(\\{buffer}[\T{1}]))\1\5 +${}\\{buffer}[\T{1}]\K\\{tolower}(\\{buffer}[\T{1}]);{}$\2\6 +\&{if} ${}(\\{buffer}[\T{1}]\E\.{'x'}){}$\1\5 +\&{break};\2\6 +\&{if} ${}(\\{buffer}[\T{1}]\E\.{'y'}\V\\{buffer}[\T{1}]\E\.{'z'}\V\\{buffer}[% +\T{1}]\E\.{'i'}){}$\5 +${}\{{}$\1\6 +${}\\{loc}\K\\{buffer}+\T{2};{}$\6 +\\{err\_print}(\.{"!\ Missing\ @x\ in\ cha}\)\.{nge\ file"});\6 +\4${}\}{}$\2\6 +\4${}\}{}$\2\par +\U12.\fi + +\M{14}Here we are looking at lines following the \.{@x}. + +\Y\B\4\X14:Skip to the next nonblank line; \PB{\&{return}} if end of file\X${}% +\E{}$\6 +\&{do}\5 +${}\{{}$\1\6 +${}\\{change\_line}\PP;{}$\6 +\&{if} ${}(\R\\{input\_ln}(\\{change\_file})){}$\5 +${}\{{}$\1\6 +\\{err\_print}(\.{"!\ Change\ file\ ended}\)\.{\ after\ @x"});\6 +\&{return};\6 +\4${}\}{}$\2\6 +\4${}\}{}$\2\5 +\&{while} ${}(\\{limit}\E\\{buffer}){}$;\par +\U12.\fi + +\M{15}\B\X15:Move \PB{\\{buffer}} and \PB{\\{limit}} to \PB{\\{change\_buffer}} +and \PB{\\{change\_limit}}\X${}\E{}$\6 +${}\{{}$\1\6 +${}\\{change\_limit}\K\\{change\_buffer}-\\{buffer}+\\{limit};{}$\6 +${}\\{strncpy}(\\{change\_buffer},\39\\{buffer},\39\\{limit}-\\{buffer}+% +\T{1});{}$\6 +\4${}\}{}$\2\par +\Us12\ET16.\fi + +\vfil\eject\titletrue +\rightskip=0pt % get out of C mode (cf. \B) +\sfcode`;=1500 \pretolerance 200 \hyphenpenalty 50 \exhyphenpenalty 50 + +\def\runninghead{APPENDIX B --- MACROS FOR FORMATTING} +\section Appendix B: The \.{cwebmac.tex} file. +This is the file that extends ``plain \TEX/'' format in order to support the +features needed by the output of \.{CWEAVE}. + +\vskip6pt +\begingroup \def\tt{\eighttt} \baselineskip9pt +\def\printmacs{\begingroup + \def\do##1{\catcode`##1=12 } \dospecials + \parskip 0pt \parindent 0pt + \catcode`\ =13 \catcode`\^^M=13 + \tt \verbatimdefs \input cwebmac \endgroup} +\printmacs +\endgroup +\vfill\eject + +\def\runninghead{APPENDIX C --- NOTES ON FORMATTING} +\section Appendix C: How to use \.{CWEB} macros. +The macros in \.{cwebmac} make it possible to produce a variety of formats +without editing the output of \.{CWEAVE}, and the purpose of this appendix +is to explain some of the possibilities. + +\def\point#1.{\yskip\indent#1.\quad\ignorespaces} + +\point 1. Four fonts have been declared in addition to the standard fonts of +\.{PLAIN} format: You can say `\.{\{\\mc UNIX\}}' to get {\mc UNIX} in +medium-size caps; you can say `\.{\{\\sc STUFF\}}' to get {\sc STUFF} +in small caps; and you can select the largish fonts \.{\\titlefont} +and \.{\\ttitlefont} in the title of your document, where \.{\\ttitlefont} +is a typewriter style of type. There are macros \.{\\UNIX/} and \.{\\CEE/} +to refer to \UNIX/ and \CEE/ with medium-size caps. + +\point 2. When you mention an identifier in \TEX/ text, you normally call +it `\.{|identifier|}'. But you can also say `\.{\\\\\{identifier\}}'. The +output will look the same in both cases, but the second alternative +doesn't put \\{identifier} into the index, since +it bypasses \.{CWEAVE}'s translation from \CEE/ mode. In the second +case you must put a backslash before each underline character +in the identifier. + +\point 3. To get typewriter-like type, as when referring to `\.{CWEB}', you +can use the `\.{\\.}' macro (e.g., `\.{\\.\{CWEB\}}'). In the argument to +this macro you should insert an additional backslash before the symbols +listed as `special string characters' in the index to \.{CWEAVE}, i.e., +before backslashes and dollar signs and the like. +A `\.{\\\ }' here will result in the visible space symbol; to get an +invisible space following a control sequence you can say `\.{\{\ \}}'. +If the string is long, you can break it up into substrings that +are separated by `\.{\\)}'\,; the latter gives a discretionary backslash +if \TEX/ has to break a line here. + +\point 4. The three control sequences \.{\\pagewidth}, \.{\\pageheight}, +and \.{\\fullpageheight} can be redefined in the limbo section at the +beginning of your \.{CWEB} file, to change the dimensions of each page. +The default settings +$$\lpile{\.{\\pagewidth=6.5in}\cr + \.{\\pageheight=8.7in}\cr + \.{\\fullpageheight=9in}\cr}$$ +were used to prepare this manual; \.{\\fullpageheight} is +\.{\\pageheight} plus room for the additional heading and page numbers at +the top of each page. If you change any of these quantities, you should +call the macro \.{\\setpage} immediately after making the change. + +\point 5. The \.{\\pageshift} macro defines an amount by which right-hand +pages (i.e., odd-numbered pages) are shifted right with respect to +left-hand (even-numbered) ones. By adjusting this amount you may be +able to get two-sided output in which the page numbers line up on +opposite sides of each sheet. + +\point 6. The \.{\\title} macro will appear at the top of each page +in small caps; it is the job name unless redefined. + +\point 7. The first page usually is assigned page +number 1. To start on page 16, with contents +on page 15, say this: `\.{\\def\\contentspagenumber\{15\}} +\.{\\pageno=\\contentspagenumber} \.{\\advance\\pageno by 1}'. + +\point 8. The macro \.{\\iftitle} will suppress the header line if it is +defined by `\.{\\titletrue}'. The normal value is \.{\\titlefalse} +except for the table of contents; thus, the contents +page is usually unnumbered. + +Two macros are provided to give flexibility to the table of +contents: \.{\\topofcontents} is invoked just before the contents +info is read, and \.{\\botofcontents} is invoked just after. +Here's a typical definition: +$$\lpile{\.{\\def\\topofcontents\{\\null\\vfill}\cr + \.{ { }\\titlefalse \% include headline on the contents page}\cr + \.{ { }\\def\\rheader\{\\mainfont The \{\\tt CWEAVE\}{ }processor\\hfil\}}\cr + \.{ { }\\centerline\{\\titlefont The \{\\ttitlefont CWEAVE\}{ }processor\}}\cr + \.{ { }\\vskip 15pt \\centerline\{(Version 3.64)\}{ }\\vfill\}}\cr}$$ +Redefining \.{\\rheader}, which is the headline for right-hand pages, +suffices in this case to put the desired information at the top of the +contents page. + +\point 9. Data for the table of contents is written to a file that +is read after the indexes have been \TEX/ed; there's one line of data +for every starred section. The file \.{common.toc} might look like this: +$$\lpile{\.{\\ZZ \{Introduction\}\{0\}\{1\}\{28\}\{\}}\cr + \.{\\ZZ \{The character set\}\{2\}\{5\}\{29\}\{\}}\cr}$$ +and so on. The \.{\\topofcontents} macro could +redefine \.{\\ZZ} so that the information appears in any desired format. +(See also point~19 below.) + +\point 10. Sometimes it is necessary or desirable to divide the output of +\.{CWEAVE} into subfiles that can be processed separately. For example, +the listing of \TEX/ runs to more than 500 pages, and that is enough to +exceed the capacity of many printing devices and/or their software. +When an extremely large job isn't cut into smaller pieces, the entire +process might be spoiled by a single error of some sort, making it +necessary to start everything over. + +Here's a safe way to break a woven file into three parts: +Say the pieces are $\alpha$, +$\beta$, and $\gamma$, where each piece begins with a starred section. +All macros should be defined in the opening limbo section of $\alpha$, +and copies of this \TEX/ code should be placed at the +beginning of $\beta$ and of $\gamma$. In order to process the parts +separately, we need to take care of two things: The starting page +numbers of $\beta$ and $\gamma$ need to be set up properly, and +the table of contents data from all three runs needs to be +accumulated. + +The \.{cwebmac} macros include two control sequences \.{\\contentsfile} and +\.{\\readcontents} that facilitate the necessary processing. We include +`\.{\\def\\contentsfile\{cont1\}}' in the limbo section of $\alpha$, and +we include `\.{\\def\\contentsfile\{cont2\}}' in the limbo section of +$\beta$; this causes \TEX/ to write the contents data for $\alpha$ and $\beta$ +into \.{cont1.tex} and \.{cont2.tex}. Now in $\gamma$ we say +$$\.{\\def\\readcontents\{\\input cont1 \\input cont2 + \\input \\contentsfile\}}\,;$$ +this brings in the data from all three pieces, in the proper order. + +However, we still need to solve the page-numbering problem. One way to +do it is to include the following in the limbo material for $\beta$: +$$\lpile{\.{\\message\{Please type the last page number of part 1: \}}\cr + \.{\\read -1 to \\temp \\pageno=\\temp \\advance\\pageno by 1}\cr}$$ +Then you simply provide the necessary data when \TEX/ requests +it; a similar construction is used at the beginning of $\gamma$. + +This method can, of course, be used to divide a woven file into +any number of pieces. + +\point 11. Sometimes it is nice to include things in the index that are +typeset in a special way. For example, we might want to have an +index entry for `\TeX'. \.{CWEAVE} provides two simple ways to +typeset an index entry (unless the entry is an identifier or a reserved word): +`\.{@\^}' gives roman type, and `\.{@.}' gives typewriter type. +But if we try to typeset `\TeX' in roman type by saying, e.g., +`\.{@\^\\TeX@>}', the backslash character gets in the way, +and this entry wouldn't appear in the index with the T's. + +The solution is to use the `\.{@:}' feature, declaring a macro that +simply removes a sort key as follows: +$$\.{\\def\\9\#1\{\}}$$ +Now you can say, e.g., `\.{@:TeX\}\{\\TeX@>}' in your \.{CWEB} file; \.{CWEAVE} +puts it into the index alphabetically, based on the sort key, and +produces the macro call `\.{\\9\{TeX\}\{\\TeX\}}' which will ensure that +the sort key isn't printed. + +A similar idea can be used to insert hidden material into section +names so that they are alphabetized in whatever way you might wish. +Some people call these tricks ``special refinements''; others call +them ``kludges.'' + +\point 12. The control sequence \.{\\secno} is set to the number of the +section being typeset. + +\point 13. If you want to list only the sections that have changed, +together with the index, put the command `\.{\\let\\maybe=\\iffalse}' in +the limbo section before the first section of your \.{CWEB} file. It's +customary to make this the first change in your change file. + +This feature has a \TeX nical limitation, however: You cannot use it +together with control sequences like \.{\\proclaim} or \.{\\+} or +\.{\\newcount} that plain \TeX\ has declared to be `\.{\\outer}', +because \TeX\ refuses to skip silently over such control sequences. +One way to work around this limitation is to say +$$\.{\\fi \\let\\proclaim\\relax \\def\\proclaim\{...\} \\ifon}$$ +where \.{\\proclaim} is redefined to be the same as usual but without +an \.{\\outer} qualification. (The \.{\\fi} here stops the conditional +skipping, and the \.{\\ifon} turns it back on again.) Similarly, +$$\.{\\fi \\newcount\\n \\ifon}$$ +is a safe way to use \.{\\newcount}. Plain \TeX\ already provides a +non-outer macro \.{\\tabalign} that does the work of \.{\\+}; you can say +$$\postdisplaypenalty=10000 +\.{\\fi \\let\\+\\tabalign \\ifon}$$ +if you prefer the shorter notation \.{\\+}. + +\point 14. To get output in languages other than English, redefine the +macros \.{\\A}, \.{\\As}, \.{\\ATH}, +\.{\\ET}, \.{\\ETs}, \.{\\Q}, \.{\\Qs}, \.{\\U}, +\.{\\Us}, \.{\\ch}, \.{\\fin}, \.{\\con}, \.{\\today}, \.{\\datethis}, and +\.{\\datecontentspage}. \.{CWEAVE} itself need not be changed. + +\point 15. Some output can be selectively suppressed with the macros +\.{\\noatl}, \.{\\noinx}, \.{\\nosecs}, \.{\\nocon}. + +\point 16. All accents and special text symbols of plain \TEX/ format +will work in \.{CWEB} documents just as they are described in +Chapter~9 of {\sl The \TEX/book}, with one exception. +The dot accent (normally \.{\\.}) must be typed \.{\\:} instead. + +\point 17. Several commented-out lines in \.{cwebmac.tex} are suggestions +that users may wish to adopt. For example, one such line inserts a blank page +if you have a duplex printer. Appendices D, E, and F of the complete +version of this manual are printed using a commented-out option that +substitutes `$\gets$' for `$=$' in the program listings. Looking at those +appendices might help you decide which format you like better. + +\point 18. Andreas Scherer has contributed a macro called \.{\\pdfURL} +with which one can say things like the following, anywhere in the \TeX\ parts +or the \CEE/ comments of a \.{CWEB} file: +$$\vbox{\halign{\.{#}\hfil\cr +You can send email to + \\pdfURL\{the author\}\{mailto:andreas.scherer@@pobox.com\}\cr +or visit \\pdfURL\{his home page\}\{http://www.pobox.com/% + \\TILDE/scherer\}.\cr}}$$ +In a {\mc PDF} document, the first argument will appear in blue as +clickable text; the Acrobat reader, if correctly configured, will then +redirect those links to the user's browser and open either the +email client or the HTML viewer. In a hardcopy document, both arguments +will be printed ({\tt the second in parentheses and typewriter type}). +Certain special characters in an Internet address need to be handled +in a somewhat awkward way, so that \.{CWEAVE} and/or \TeX\ +will not confuse them with formatting controls: Use \.{@@} for \.@ +and \.{\\TILDE/} for \.\~ and \.{\\UNDER/} for \.\_. + +\point 19. {\mc PDF} documents contain bookmarks that list all the major group +titles in the table of contents, some of which will be subsidiary to +others if the depth feature of \.{@*} has been used. +Such bookmark entries are also known as ``outlines.'' +Moreover, the final group title, `Names of the sections', +can be opened up to list every section name; Acrobat users can therefore +navigate easily to any desired section. + +The macros of \.{cwebmac.tex} are careful to ``sanitize'' all the names +that appear as bookmarks, by removing special characters and +formatting codes that are inappropriate for the limited typographic +capabilities of {\mc PDF} outlines. For example, one section of \.{CWEAVE} +is named `Cases for \\{case\_like}', which is represented by the \TeX\ code +`\.{Cases} \.{for} \.{\\PB\{\\\\\{case\\\_like\}\}}' in \.{cweave.tex}; its +sanitized name is simply `\.{Cases} \.{for} \.{case\_like}'. +(When \.{.pdf} files are produced, the fifth parameter of every \.{\\ZZ} in +the \.{.toc} file is set to the sanitized form of the first parameter; +see point~9 above and point~20 below.) + +In general, sanitization removes \TeX\ control sequences and braces, +except for control sequences defined by \.{CWEB} itself. Such a translation +works most of the time, but you can override the defaults and obtain any +translation that you want by using \TeX nical tricks. For example, after +$$\.{\\sanitizecommand\\foo\{bar\}}$$ +the control sequence \.{\\foo} will sanitize to `\.{bar}'. And after +$$\.{\\def\\kluj\#1\\\\\{foo\}}$$ +the \TeX\ code `\.{\\kluj bar\\\\}' will print as `foo' but sanitize +to `\.{bar}', because the control sequences \.{\\kluj} and +\.{\\\\} are removed by sanitization. + +\point 20. Furthermore, group titles can be converted to an arbitrary +sanitized text while also changing their form in running headlines, by +using \.{\\ifheader}. Consider, for example, a \.{CWEB} source file that +begins with the two lines +$$\lpile{\.{\\def\\klujj\#1\\\\\{\\ifheader FOO\\else foo\\fi\}}\cr +\.{@*Chinese \\klujj bar\\.}\cr}$$ +This coding introduces a major group entitled `{\bf Chinese foo}', with +running headline `{\eightrm CHINESE FOO}' and table-of-contents entry +`Chinese foo'. The corresponding bookmark is, however, `\.{Chinese} \.{bar}'. +And the corresponding \.{.toc} file entry is +`\.{\\ZZ \{Chinese \\klujj bar\\\\\}\{1\}\{1\}\{1\}\{Chinese bar\}}'. + +\vfill\end + diff --git a/web/c_cpp/cweb/makefile.bs b/web/c_cpp/cweb/makefile.bs new file mode 100644 index 0000000000..5e97012804 --- /dev/null +++ b/web/c_cpp/cweb/makefile.bs @@ -0,0 +1,60 @@ +# Makefile for CTANGLE and CWEAVE, pc/big versions, using +# Borland C++ 3.1 and Borland Make. +# +# (This file contributed by Barry Schwartz, trashman@crud.mn.org, +# 24 Jul 94.) + + + +CC = bcc + +# Redundant Load Suppression (-Z) is turned off, because it seems to +# break the code. Likewise for Copy Propagation (-Op). (These +# optimizations don't seem to cause any problems in common.w, but +# better safe than sorry--compile everything with them turned off.) +OPT = -O2-p -Z- + +# Compile with symbols. That way you'll be able to use the debugger if +# you run into trouble. You can always use tdstrip later, to remove the +# symbols. +DEBUG = -v + +LCFLAGS = -mc -Ff=5000 -d -w-pro $(DEBUG) +CFLAGS = $(LCFLAGS) $(OPT) +COMPILE = $(CC) $(CFLAGS) + +LINK = $(CC) $(LCFLAGS) -e + + + +# Where to find an "old" version of ctangle, for bootstrapping. The first +# time you make ctangle, you may have to edit the distributed ctangle.c +# and/or common.c by hand to reduce the size of one or more arrays. +# This will give you a functional ctangle.exe, which you can use to bootstrap +# the "real" ctangle.exe. +CTANGLE = ctangle + + + + +all default: ctangle.exe cweave.exe + + +ctangle.exe: ctangle.obj common.obj + $(LINK)$* $** + +cweave.exe: cweave.obj common.obj + $(LINK)$* $** + + +common.obj: common.w comm-bs.ch + $(CTANGLE) common.w comm-bs.ch + $(COMPILE) -c common.c + +ctangle.obj: ctangle.w common.h ctang-bs.ch + $(CTANGLE) ctangle.w ctang-bs.ch + $(COMPILE) -c ctangle.c + +cweave.obj: cweave.w common.h cweav-bs.ch + $(CTANGLE) cweave.w cweav-bs.ch + $(COMPILE) -c cweave.c diff --git a/web/c_cpp/cweb/prod.w b/web/c_cpp/cweb/prod.w new file mode 100644 index 0000000000..5f76a19379 --- /dev/null +++ b/web/c_cpp/cweb/prod.w @@ -0,0 +1,307 @@ +% 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.6 --- May 2000 +% +@ Here is a table of all the productions. Each production that +combines two or more consecutive scraps implicitly inserts a {\tt \$} +where necessary, that is, between scraps whose abutting boundaries +have different |mathness|. In this way we never get double {\tt\$\$}. + +% The following kludge is needed because \newcount, \newdimen, and \+ +% are "\outer" control sequences that cannot be used in skipped text! +\fi \newcount\prodno \newdimen\midcol \let\+\relax \ifon + +\def\v{\char'174} +\mathchardef\RA="3221 % right arrow +\mathchardef\BA="3224 % double arrow + +A translation is provided when the resulting scrap is not merely a +juxtaposition of the scraps it comes from. An asterisk$^*$ next to a scrap +means that its first identifier gets an underlined entry in the index, +via the function |make_underlined|. Two asterisks$^{**}$ means that both +|make_underlined| and |make_reserved| are called; that is, the +identifier's ilk becomes |raw_int|. A dagger \dag\ before the +production number refers to the notes at the end of this section, +which deal with various exceptional cases. + +We use \\{in}, \\{out}, \\{back} and +\\{bsp} as shorthands for |indent|, |outdent|, |backup| and +|break_space|, respectively. + +\begingroup \lineskip=4pt +\def\alt #1 #2 +{$\displaystyle\Bigl\{\!\matrix{\strut\hbox{#1}\cr + \strut\hbox{#2}\cr}\!\Bigr\}$ } +\def\altt #1 #2 #3 +{$\displaystyle\Biggl\{\!\matrix{\strut\hbox{#1}\cr\hbox{#2}\cr + \strut\hbox{#3}\cr}\!\Biggr\}$ } +\def\malt #1 #2 +{$\displaystyle\matrix{\strut\hbox{#1}\hfill\cr\strut\hbox{#2}\hfill\cr}$} +\def\maltt #1 #2 #3 +{$\displaystyle\matrix{\strut\hbox{#1}\hfill\cr\hbox{#2}\hfill\cr + \strut\hbox{#3}\hfill\cr}$} +\yskip +\prodno=0 \midcol=2.5in +\def\theprodno{\number\prodno \global\advance\prodno by1\enspace} +\def\dagit{\dag\theprodno} +\def\+#1\cr{\def\next{#1}% + \line{\hbox to 2em{\hss + \ifx\next\empty\theprodno\else\next\fi}\strut + \ignorespaces#2\hfil\hbox to\midcol{$\RA$ + \ignorespaces#3\hfil}\quad \hbox to1.45in{\ignorespaces#4\hfil}}} +\+\relax & LHS & RHS \hfill Translation & Example\cr +\yskip +\+& \altt\\{any} {\\{any} \\{any}} {\\{any} \\{any} \\{any}} +|insert| & \altt\\{any} {\\{any} \\{any}} {\\{any} \\{any} \\{any}} +& stmt; \ /$\ast\,$comment$\,*$/\cr +\+& |exp| \altt|lbrace| |int_like| |decl| + & |fn_decl| \altt|lbrace| |int_like| |decl| + \hfill $F=E^*\,|in|\,|in|$ & \malt {\\{main}()$\{$} + {\\{main}$(\\{ac},\\{av})$ \&{int} \\{ac};} \cr +\+& |exp| |unop| & |exp| & |x++|\cr +\+& |exp| \alt |binop| |ubinop| |exp| & |exp| & \malt {|x/y|} {|x+y|} \cr +\+& |exp| |comma| |exp| & |exp| \hfill $EC\,|opt|9\,E$& |f(x,y)|\cr +\+& |exp| \alt {|lpar| |rpar|} |cast| |colon| & + |exp| \alt {|lpar| |rpar|} |cast| |base| & + \malt \&C|()|: {\&C|int i)|:} \cr +\+& |exp| |semi| & |stmt| & |x=0;|\cr +\+& |exp| |colon| & |tag| \hfill $E^*C$ & |found:|\cr +\+& |exp| |rbrace| & |stmt| |rbrace| & end of \&{enum} list\cr +\+& |exp| \alt {|lpar| |rpar|} |cast| \alt|const_like| |case_like| & + |exp| \alt {|lpar| |rpar|} |cast| \hfill + \alt $R=R\.\ C$ $C_1=C_1\.\ C_2$ & + \malt {|f() const|} {|f(int) throw|} \cr +\+& |exp| \alt |exp| |cast| & |exp| & |time()|\cr +\+& |lpar| \alt |exp| |ubinop| |rpar| & |exp| & \malt{|(x)|} {|(*)|} \cr +\+& |lpar| |rpar| & |exp| \hfill $L\.{\\,}R$ & functions, declarations\cr +\+& |lpar| \altt |decl_head| |int_like| |cast| |rpar| & |cast| & |(char*)|\cr +\+& |lpar| \altt |decl_head| |int_like| |exp| |comma| & |lpar| \hfill + $L$\,\altt $D$ $I$ $E$ $C$\,|opt|9 & |(int,|\cr +\+& |lpar| \alt |stmt| |decl| & |lpar| \hfill \alt {$LS\.\ $} {$LD\.\ $} & + \malt {|(k=5;|} {|(int k=5;|} \cr +\+& |unop| \alt |exp| |int_like| & |exp| & + \malt |!x| |~|\&C \cr +\+& |ubinop| |cast| |rpar| & |cast| |rpar| \hfill + $C=\.\{U\.\}C$ & |*|\&{CPtr}) \cr +\+& |ubinop| \alt|exp| |int_like| & \alt|exp| |int_like| \hfill + \.\{$U$\.\}\alt$E$ $I$ & \malt |*x| |*|\&{CPtr} \cr +\+& |ubinop| |binop| & |binop| \hfill $|math_rel|\,U\.\{B\.\}\.\}$ & |*=|\cr +\+& |binop| |binop| & |binop| \hfill + $|math_rel|\,\.\{B_1\.\}\.\{B_2\.\}\.\}$ & |>>=|\cr +\+& |cast| \alt |lpar| |exp| & \alt |lpar| |exp| \hfill + \alt $CL$ $C\.\ E$ & \malt |(double)(x+2)| |(double)x| \cr +\+& |cast| |semi| & |exp| |semi| & |(int);|\cr +\+& |sizeof_like| |cast| & |exp| & |sizeof (double)|\cr +\+& |sizeof_like| |exp| & |exp| \hfill $S\.\ E$ & |sizeof x|\cr +\+& |int_like| \alt|int_like| |struct_like| & + \alt|int_like| |struct_like| \hfill $I\.\ $\alt $I$ $S$ + \unskip& |extern char|\cr +\+& |int_like| |exp| \alt|raw_int| |struct_like| & + |int_like| \alt|raw_int| |struct_like| & |extern "Ada" int|\cr +\+& |int_like| \altt|exp| |ubinop| |colon| & + |decl_head| \altt|exp| |ubinop| |colon| \hfill + $D=I$\.\ & \maltt|int x| |int*x| |unsigned:| \cr +\+& |int_like| \alt |semi| |binop| & |decl_head| \alt |semi| |binop| & + \malt |int x;| {\&{int} $f(\&{int}=4)$} \cr +\+& |public_like| |colon| & |tag| & \&{private}:\cr +\+& |public_like| & |int_like| & \&{private}\cr +\+& |colcol| \alt|exp| |int_like| & \alt|exp| |int_like| \hfill + |qualifier| $C$ \alt$E$ $I$ & \&C\DC$x$\cr +\+& |colcol| |colcol| & |colcol| & \&C\DC\&B\DC\cr +\+& |decl_head| |comma| & |decl_head| \hfill $DC\.\ $ & |int x,|\cr +\+& |decl_head| |ubinop| & |decl_head| \hfill $D\.\{U\.\}$ & |int *|\cr +\+\dagit& |decl_head| |exp| & |decl_head| \hfill $DE^*$ & |int x|\cr +\+& |decl_head| \alt|binop| |colon| |exp| \altt|comma| |semi| |rpar| & + |decl_head| \altt|comma| |semi| |rpar| \hfill + $D=D$\alt $B$ $C$ \unskip$E$ & \malt {\&{int} $f(\&{int}\ x=2)$} |int b:1| \cr +\+& |decl_head| |cast| & |decl_head| & |int f(int)|\cr +\+& |decl_head| \altt|int_like| |lbrace| |decl| & |fn_decl| + \altt|int_like| |lbrace| |decl| \hfill $F=D\,|in|\,|in|$ + & |long time () {|\cr +\+& |decl_head| |semi| & |decl| & |int n;|\cr +\+& |decl| |decl| & |decl| \hfill $D_1\,|force|\,D_2$ & |int n;double x;|\cr +\+& |decl| \alt|stmt| |function| & \alt|stmt| |function| + \hfill $D\,|big_force|\,$\alt $S$ $F$ \unskip& \&{extern} $n$; + \\{main} ()|{}|\cr +\+& |base| \alt |int_like| |exp| |comma| & |base| \hfill + $B$\.\ \alt $I$ $E$ \unskip$C$\,|opt|9 + & \malt {: \&{public} \&A,} {: $i(5)$,} \cr +\+& |base| \alt |int_like| |exp| |lbrace| & |lbrace| \hfill + $B$\.\ \alt $I$ $E$ \unskip\.\ $L$ & \&D : \&{public} \&A $\{$\cr +\+& |struct_like| |lbrace| & |struct_head| \hfill $S\.\ L$ & |struct {|\cr +\+& |struct_like| \alt|exp| |int_like| |semi| & |decl_head| |semi| + \hfill $S\.\ $\alt $E^{**}$ $I^{**}$ & \&{struct} \&{forward};\cr +\+& |struct_like| \alt|exp| |int_like| |lbrace| & |struct_head| \hfill + $S\.\ $\alt $E^{**}$ $I^{**}$ \unskip $\.\ L$ & + \&{struct} \&{name\_info} $\{$\cr +\+& |struct_like| \alt|exp| |int_like| |colon| & + |struct_like| \alt|exp| |int_like| |base| & |class| \&C :\cr +\+\dagit& |struct_like| \alt|exp| |int_like| & |int_like| + \hfill $S\.\ $\alt$E$ $I$ & \&{struct} \&{name\_info} $z$;\cr +\+& |struct_head| \altt|decl| |stmt| |function| |rbrace| & |int_like|\hfill + $S\,\\{in}\,|force|$\altt$D$ $S$ $F$ $\\{out}\,|force|\,R$ & + |struct {| declaration |}|\cr +\+& |struct_head| |rbrace| & |int_like|\hfill $S\.{\\,}R$ & |class C{}|\cr +\+& |fn_decl| |decl| & |fn_decl| \hfill $F\,|force|\,D$ + & $f(z)$ \&{double} $z$; \cr +\+& |fn_decl| |stmt| & |function| \hfill $F\,|out|\,|out|\,|force|\,S$ + & \\{main}() {\dots}\cr +\+& |function| \altt|stmt| |decl| |function| & \altt |stmt| |decl| |function| + \hfill $F\,|big_force|\,$\altt $S$ $D$ $F$ & outer block\cr +\+& |lbrace| |rbrace| & |stmt| \hfill $L\.{\\,}R$ & empty statement\cr +\advance\midcol35pt +\+& |lbrace| \altt|stmt| |decl| |function| |rbrace| & |stmt| \hfill + $|force|\,L\,\\{in}\,|force|\,S\, + |force|\,\\{back}\,R\,\\{out}\,|force|$ & compound statement\cr +\advance\midcol-20pt +\+& |lbrace| |exp| [|comma|] |rbrace| & |exp| & initializer\cr +\+& |if_like| |exp| & |if_clause| \hfill $I\.{\ }E$ & |if (z)|\cr +\+& |else_like| |colon| & |else_like| |base| & \&{try} :\cr +\+& |else_like| |lbrace| & |else_head| |lbrace| & \&{else} $\{$\cr +\+& |else_like| |stmt| & |stmt| \hfill + $|force|\,E\,\\{in}\,\\{bsp}\,S\,\\{out}\,|force|$ & |else x=0;|\cr +\+& |else_head| \alt|stmt| |exp| & |stmt| \hfill + $|force|\,E\,\\{bsp}\,|noop|\,|cancel|\,S\,\\{bsp}$ & |else{x=0;}|\cr +\+& |if_clause| |lbrace| & |if_head| |lbrace| & |if (x) {|\cr +\+& |if_clause| |stmt| |else_like| |if_like| & |if_like| \hfill + $|force|\,I\,\\{in}\,\\{bsp}\,S\,\\{out}\,|force|\,E\,\.\ I$ & + |if (x) y; else if|\cr +\+& |if_clause| |stmt| |else_like| & |else_like| \hfill + $|force|\,I\,\\{in}\,\\{bsp}\,S\,\\{out}\,|force|\,E$ & + |if (x) y; else|\cr +\+& |if_clause| |stmt| & |else_like| |stmt| & |if (x)|\cr +\+& |if_head| \alt|stmt| |exp| |else_like| |if_like| & |if_like| \hfill + $|force|\,I\,\\{bsp}\,|noop|\,|cancel|\,S\,|force|\,E\,\.\ I$ & + |if (x){y;}else if|\cr +\+& |if_head| \alt|stmt| |exp| |else_like| & |else_like| \hfill + $|force|\,I\,\\{bsp}\,|noop|\,|cancel|\,S\,|force|\,E$ & + |if (x){y;}else|\cr +\+& |if_head| \alt|stmt| |exp| & |else_head| \alt|stmt| |exp| & |if (x){y;}|\cr +\advance\midcol20pt +\+& |do_like| |stmt| |else_like| |semi| & |stmt| \hfill + $D\,\\{bsp}\,|noop|\,|cancel|\,S\,|cancel|\,|noop|\,\\{bsp}\,ES$% + & |do f(x); while (g(x));|\cr +\advance\midcol-20pt +\+& |case_like| |semi| & |stmt| & |return;|\cr +\+& |case_like| |colon| & |tag| & |default:|\cr +\+& |case_like| |exp| & |exp| \hfill $C\.\ E$ & |return 0|\cr +\+& |catch_like| \alt|cast| |exp| & |fn_decl| \hfill + $C$\alt $C$ $E$ \\{in}\,\\{in} & |catch (...)|\cr +\+& |tag| |tag| & |tag| \hfill $T_1\,\\{bsp}\,T_2$ & |case 0: case 1:|\cr +\+& |tag| \altt|stmt| |decl| |function| & \altt|stmt| |decl| |function| + \hfill $|force|\,\\{back}\,T\,\\{bsp}\,S$ & |case 0: z=0;|\cr +\+\dagit& |stmt| \altt|stmt| |decl| |function| & + \altt|stmt| |decl| |function| + \hfill $S\,$\altt$|force|\,S$ $|big_force|\,D$ $|big_force|\,F$ & + |x=1;y=2;|\cr +\+& |semi| & |stmt| \hfill \.\ $S$& empty statement\cr +\+\dagit& |lproc| \altt |if_like| |else_like| |define_like| & |lproc| & + \maltt {{\bf \#include}} {\bf\#else} {\bf\#define} \cr +\+& |lproc| |rproc| & |insert| & {\bf\#endif} \cr +\+& |lproc| \alt {|exp| [|exp|]} |function| |rproc| & |insert| \hfill + $I$\.\ \alt {$E{[\.{\ \\5}E]}$} {$F$} & + \malt{{\bf\#define} $a$\enspace 1} {{\bf\#define} $a$\enspace$\{\,b;\,\}$} \cr +\+& |section_scrap| |semi| & |stmt|\hfill $MS$ |force| + &$\langle\,$section name$\,\rangle$;\cr +\+& |section_scrap| & |exp| &$\langle\,$section name$\,\rangle$\cr +\+& |insert| |any| & |any| & \.{\v\#include\v}\cr +\+& |prelangle| & |binop| \hfill \.< & $<$ not in template\cr +\+& |prerangle| & |binop| \hfill \.> & $>$ not in template\cr +\+& |langle| |prerangle| & |cast| \hfill $L\.{\\,}P$ & $\langle\,\rangle$\cr +\+& |langle| \altt|decl_head| |int_like| |exp| |prerangle| & |cast| & + $\langle\&{class}\,\&C\rangle$\cr +\+& |langle| \altt|decl_head| |int_like| |exp| |comma| & |langle| \hfill + $L$\,\altt $D$ $I$ $E$ $C$\,|opt|9 & $\langle\&{class}\,\&C,$\cr +\+& |template_like| |exp| |prelangle| & |template_like| |exp| |langle| & + \&{template} $a\langle100\rangle$\cr +\+& |template_like| \alt|exp| |raw_int| & \alt|exp| |raw_int| \hfill + $T$\.\ \alt$E$ $R$ & \&C\DC\&{template} |a()|\cr +\+& |template_like| & |raw_int| & \&{template}$\langle\&{class}\,\&T\rangle$\cr +\+& |new_like| |lpar| |exp| |rpar| & |new_like| & \&{new}(\\{nothrow})\cr +\+& |new_like| |cast| & |exp| \hfill $N\.\ C$ & |new(int*)|\cr +\+\dagit& |new_like| & |new_exp| & \&{new} \&C|()|\cr +\+& |new_exp| \alt|int_like| |const_like| & |new_exp| \hfill + $N$\.\ \alt $I$ $C$ & |new const int|\cr +\+& |new_exp| |struct_like| \alt |exp| |int_like| & |new_exp| \hfill + $N\.\ S$\.\ \alt $E$ $I$ & \&{new} \&{struct} \&S\cr +\+& |new_exp| |raw_ubin| & |new_exp| \hfill $N\.\{R\.\}$ & |new int*[2]|\cr +\+& |new_exp| \alt |lpar| |exp| & |exp| \alt |lpar| |exp| \hfill + $E=N$\,\alt {} {\.\ } & \malt |operator[](int)| {|new int(2)|} \cr +\+\dagit& |new_exp| & |exp| & |new int;|\cr +\+& |ftemplate| |prelangle| & |ftemplate| |langle| & |make_pair<int,int>|\cr +\+& |ftemplate| & |exp| & |make_pair(1,2)|\cr +\+& |for_like| |exp| & |else_like| \hfill $F\.\ E$ & |while (1)|\cr +\+& |raw_ubin| |const_like| & |raw_ubin| \hfill $RC$\.{\\\ } + & $*$\&{const} |x|\cr +\+& |raw_ubin| & |ubinop| & $*$ |x|\cr +\+& |const_like| & |int_like| & \&{const} |x|\cr +\+& |raw_int| |prelangle| & |raw_int| |langle| & \&C$\langle$\cr +\+& |raw_int| |colcol| & |colcol| & \&C\DC\cr +\+& |raw_int| |cast| & |raw_int| & \&C$\langle\&{class}\ \&T\rangle$\cr +\+& |raw_int| |lpar| & |exp| |lpar| & \&{complex}$(x,y)$\cr +\+\dagit& |raw_int| & |int_like| & \&{complex} |z|\cr +\+\dagit& |operator_like| \altt|binop| |unop| |ubinop| & |exp| + \hfill $O$\.\{\altt $B$ $U$ $U$ \unskip\.\} & |operator+|\cr +\+& |operator_like| \alt|new_like| |delete_like| & |exp| \hfill + $O$\.\ \alt $N$ $S$ & |operator delete|\cr +\+& |operator_like| |comma| & |exp| & \&{operator},\cr +\+\dagit& |operator_like| & |new_exp| & |operator char*|\cr +\advance\midcol-8pt +\+& |typedef_like| \alt|int_like| |cast| \alt|comma| |semi| & + |typedef_like| |exp| \alt|comma| |semi| & \&{typedef} \&{int} \&I,\cr +\advance\midcol+8pt +\+& |typedef_like| |int_like| & |typedef_like| \hfill $T\.\ I$ & + \&{typedef} \&{char}\cr +\+\dagit& |typedef_like| |exp| & |typedef_like| \hfill $T\.\ E^{**}$ & + \&{typedef} \&I \.{@@[@@]} (|*|\&P)\cr +\+& |typedef_like| |comma| & |typedef_like| \hfill $TC\.\ $ & + \&{typedef} \&{int} \&x,\cr +\+& |typedef_like| |semi| & |decl| & \&{typedef} \&{int} $\&x,\&y$;\cr +\+& |typedef_like| |ubinop| \alt |cast| |ubinop| & + |typedef_like| \alt |cast| |ubinop| \hfill + \alt $C=\.\{U\.\}C$ $U_2=\.\{U_1\.\}U_2$ \unskip & + \&{typedef} |*|{}|*|(\&{CPtr})\cr +\+& |delete_like| |lpar| |rpar| & |delete_like|\hfill $DL\.{\\,}R$ & + \&{delete}|[]| \cr +\+& |delete_like| |exp| & |exp| \hfill $D\.\ E$ & |delete p| \cr +\+\dagit& |question| |exp| \alt |colon| |base| & |binop| & + \malt |?x:| |?f():| \cr +\+& |begin_arg| |end_arg| & |exp| & \.{@@[}\&{char}$*$\.{@@]}\cr +\+& |any_other| |end_arg| & |end_arg| & \&{char}$*$\.{@@]}\cr +\yskip +\yskip +\yskip +\parindent=0pt +\dag{\bf Notes} +\yskip +Rule 35: The |exp| must not be immediately followed by |lpar|, |exp|, + or~|cast|. + +Rule 48: The |exp| or |int_like| must not be immediately followed by |base|. + +Rule 76: The |force| in the |stmt| line becomes \\{bsp} if \.{CWEAVE} has +been invoked with the \.{-f} option. + +Rule 78: The |define_like| case calls |make_underlined| on the following scrap. + +Rule 94: The |new_like| must not be immediately followed by |lpar|. + +Rule 99: The |new_exp| must not be immediately followed by |raw_int|, +|struct_like|, or |colcol|. + +Rule 110: The |raw_int| must not be immediately followed by |langle|. + +Rule 111: The operator after |operator_like| +must not be immediately followed by a |binop|. + +Rule 114: The |operator_like| must not be immediately followed by +|raw_ubin|. + +Rule 117: The |exp| must not be immediately followed by |lpar|, |exp|, +or |cast|. + +Rule 123: The mathness of the |colon| or |base| changes to `yes'. + +\endgroup diff --git a/web/c_cpp/cweb/readme.ql b/web/c_cpp/cweb/readme.ql new file mode 100644 index 0000000000..907d4c3192 --- /dev/null +++ b/web/c_cpp/cweb/readme.ql @@ -0,0 +1,84 @@ +This is the readme file for CWEB for QDOS/SMSQ systems + +(These instructions are for systems with Toolkit II, + please report any problems to me, the address is at the + end of the file) + +If you want to compile CWEB for your QL system, you must first +convert the ASCII code to the QL's character set. Therefore you +can use the little BASIC program appended at the end of the file. + +For a first version you have to alter the supplied ctangle_c file. +Set your DATA_USE to the directory you have the CWEB sources in, and +the PROG_USE to C68's directory. +compile it with: +ex cc;'common_c ctangle_c -bufp500K -octangle' + +then ctangle the source files with: +ex datad$&'ctangle';'common_w comm-ql_ch' +ex datad$&'ctangle';'ctangle_w ctang-ql_ch' +ex datad$&'ctangle';'cweave_w cweav-ql_ch' + +Compile the following way (C68v4.14 +): + +ex cc;'-c common_c' +ex cc;'-c ctangle_c' +ex cc;'-c cweave_c' +ex ld;'-bufp500K common_o ctangle_o -octangle' +ex ld;'-bufp500K common_o cweave_o -ocweave' + +After that copy the ctangle and cweave executables into your +programs directory (if you want them there). + +The DATA default may be used as usual. E.g. if you have +a file win2_data_test_w and the DATA default set to +win2_data_ you may call ctangle with + ex ctangle;'test' +assuming that ctangle is in your PROG default. + +Please report any problems with the QL version to: +Robert Klein +Bluecherstrasse 24 +D-56349 Kaub +Germany + + +**************** ASCII Conversion program *************** +With this program you can convert DOS and Unix 7-Bit ASCII +files to the QL's character set. You'll need Toolkit II. +The program will take all the files in the source directory +and put them converted into the destination&source directory, +e.g. you have as drive1$='win2_', drive2$='win3_', +path1$='cweb_' and path2$='ql_'. Then the routine will take +all files in win2_cweb_ (wthout subdirectories!) convert them +and write them to 'win3_ql_cweb_'. It's only a lousy hack, +but it'll do it's job. + +1000 DEFine PROCedure dos2ql (drive1$,path1$,drive2$,path2$) +1002 LOCal zx$,zx%,ishl,ashl +1005 DELETE drive2$&path2$&'dirlist' +1010 OPEN_NEW#5,drive2$&path2$&'dirlist' +1020 DIR#5,drive1$&path1$ +1030 CLOSE#5 +1040 OPEN_IN#5,drive2$&path2$&'dirlist' +1050 INPUT#5,zx$:INPUT#5,zx$:REMark devicename and sectors +1060 REPeat ashl +1070 IF EOF(#5)THEN EXIT ashl +1080 INPUT#5,zx$ +1085 IF '->' INSTR zx$ THEN NEXT ashl +1090 OPEN_IN#3,drive1$&zx$ +1100 OPEN_NEW#4,drive2$&path2$&zx$ +1110 REPeat ishl +1120 IF EOF(#3)THEN EXIT ishl +1130 BGET#3,zx% +1140 SELect ON zx% +1150 = 13:REMark do nothing (DOS LineFeed) +1155 = 26:REMark do nothing (DOS EOF sign) +1160 = 96: BPUT#4,159:REMark Unix/DOS beginning quote sign +1170 = REMAINDER : BPUT#4,zx% +1180 END SELect +1190 END REPeat ishl +1195 CLOSE#3:CLOSE#4 +1200 END REPeat ashl +1205 CLOSE#5 +1210 END DEFine dos2ql diff --git a/web/c_cpp/cwebbin/README-2019.txt b/web/c_cpp/cwebbin/README-2019.txt new file mode 100644 index 0000000000..b09a017c18 --- /dev/null +++ b/web/c_cpp/cwebbin/README-2019.txt @@ -0,0 +1,478 @@ + -*-Text-*- +%%% LEGALESE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% This file is part of CWEBbin (Version 3.64c [2019]). +% (Set TAB to 8 spaces to get the spacing right.) +% The CWEB programs by Silvio Levy are based on programs by D. E. Knuth. +% They are distributed WITHOUT ANY WARRANTY, express or implied. + +% This README file was last updated January 1, 2019 by Andreas Scherer. + +% The following copyright notices extend to the respective parts of the +% changed or added source code introduced in this patch only, not to the +% original CWEB distribution, which is copyright (C) 1987,1990,1993,1998 +% by Silvio Levy and Donald E. Knuth. +% +% Copyright (C) 1991-1993 Hans-Hermann Bode +% Copyright (C) 1992 Klaus Guntermann +% Copyright (C) 1991,1993 Carsten Steger +% Copyright (C) 1993-2000,2005,2011,2018,2019 Andreas Scherer + +% 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. + +% Please send comments, suggestions, etc., concerning the original CWEB +% implementation to tex-k@tug.org. + +% Please send comments, suggestions, etc., related to changes specific +% for this modified distribution CWEBbin, especially language catalog +% translations to https://ascherer.github.io, or write an issue or pull +% request in https://github.com/ascherer/cwebbin. + +%%% WHAT IS THIS STUFF? %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +CWEBbin is the ANSI-C/C++ implementation of Silvio Levy's and Donald Ervin +Knuth's CWEB system, compiled and provided with changefiles for ANSI-C and +makefiles for Borland C/C++ 3.1 (no longer actively supported), MS/VC++ 4.1 +and 6.0 (both no longer actively supported), SAS/C++ 6.57 (no longer actively +supported), and most of all UNIX-cc/g++. Additional features differing from +the original distribution are described further below. General information is +provided in the original README file shipped with the CWEB distribution. + +CWEBbin started from the Amiga port of CWEB 2.8 by Carsten Steger. Then it +was merged with the CWEB-PC 3.0 distribution by Hans-Hermann Bode and Klaus +Guntermann, and the Amiga port of CWEB 3.1 by Tomas Charles Willis. Of +course, it includes all features of the original UNIX implementation and all +enhancements of these Amiga and MS/DOS/Windows ports. + +The distribution of CWEBbin consists of the additional files for this patch +only. The complete contents of the original distribution is a prerequisite. +CWEB can be retrieved via anonymous ftp from + + FTP: ftp://ftp.cs.stanford.edu/pub/cweb + CTAN: https://ctan.org/pkg/cweb + +To apply this patched version of CWEB, extract the patch archive (yielding a +new subdirectory) and add the original distribution. Care has been taken to +avoid collisions with any files from the original distribution and the +Makefiles provide a target to cleanly remove the patch leaving only the files +of the original distribution (and vice versa). + +Intermediate files with extensions .tex and .cxx are reproducable from the +general sources by means of an executable ctangle, cweave, and an ANSI-C/C++ +compiler. In addition you need Klaus Guntermann's TIE processor, which is +available at + + CTAN: https://ctan.org/pkg/tie + +TIE should be available on all systems with a recent TeX installation. +Alternatively, you can also try to use Julian Gilbey's CTIE processor, but +intermediate results are different. + +%%% TABLE OF CONTENTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +The following files were added to this distribution by various contributors: + +GENERAL CONTRIBUTION: +. +├── 0001-Make-clean-twinx.patch Patch for twinx.w +├── 0002-Make-clean-refsort.patch Patch for refsort.w +├── arexx [AMIGA] +│  ├── catalogs +│  │  ├── cweb_arexx.cd [AMIGA] catalog description for German defaults +│  │  ├── cweb_arexx.e.ct [AMIGA] catalog translation for English users +│  │  ├── cweb_arexx.f.ct [AMIGA] catalog translation for French users +│  │  ├── cweb_arexx.i.ct [AMIGA] catalog translation for Italian users +│  │  ├── english +│  │  │  └── cweb_arexx.catalog [AMIGA] message catalog for English users +│  │  ├── francais +│  │  │  └── cweb_arexx.catalog [AMIGA] message catalog for French users +│  │  └── italiano +│  │  └── cweb_arexx.catalog [AMIGA] message catalog for Italian users +│  ├── compile.ced [AMIGA] ARexx script to start SAS/C 6.x from CED (V3.5+) +│  └── start_web.ced [AMIGA] ARexx script to start cweave/ctangle from CED +├── catalogs +│  ├── cweb.cd [AMIGA] catalog description for English defaults +│  ├── cweb.d.ct [AMIGA] catalog translation for German users +│  ├── cweb.i.ct [AMIGA] catalog translation for Italian users +│  ├── dcweb.h [AMIGA,BCC] header file with the German strings +│  ├── ecweb.h [AMIGA,BCC] header file with the English default strings +│  └── icweb.h [AMIGA,BCC] header file with the Italian strings +├── comm-ansi.ch change file for common.w +├── comm-ansi.hch change file for common.h +├── comm-arexx.ch [AMIGA] change file for common.w +├── comm-arexx.hch [AMIGA] change file for common.h +├── comm-borlandc.ch [BCC] change file for common.w +├── comm-borlandc.hch [BCC] change file for common.h +├── comm-extensions.ch change file for common.w +├── comm-extensions.hch change file for common.h +├── comm-i18n.ch change file for common.w +├── comm-i18n.hch change file for common.h +├── comm-memory.ch [AMIGA,BCC] change file for common.w +├── comm-memory.hch [AMIGA,BCC] change file for common.h +├── comm-newpage.ch change file for common.w +├── common.cxx product of common.w and comm-foo.ch +├── comm-output.ch change file for common.w +├── comm-output.hch change file for common.h +├── comm-patch.ch change file for common.w +├── comm-texlive.ch change file for common.w +├── comm-texlive.hch change file for common.h +├── comm-translation.ch [AMIGA,BCC] change file for common.w +├── comm-translation.hch [AMIGA,BCC] change file for common.h +├── ctang-ansi.ch change file for ctangle.w +├── ctang-borlandc.ch [BCC] change file for ctangle.w +├── ctang-extensions.ch change file for ctangle.w +├── ctang-i18n.ch change file for ctangle.w +├── ctangle.1 clone of cweb.1 +├── ctangle.cxx product of ctangle.w and ctang-foo.ch +├── ctangle.dsp [MSVC] Developer Studio Project for CTANGLE +├── ctang-memory.ch [AMIGA,BCC] change file for ctangle.w +├── ctang-newpage.ch change file for ctangle.w +├── ctang-output.ch change file for ctangle.w +├── ctang-patch.ch change file for ctangle.w +├── ctang-texlive.ch change file for ctangle.w +├── ctang-translation.ch [AMIGA,BCC] change file for ctangle.w +├── ctwill.bux auxiliary file for CTWILL +├── ctwill.md manpage for CTWILL in markdown +├── ctwill-mini.ch auxiliary file for CTWILL +├── ctwill-refsort.1 manpage link to ctwill.1 +├── ctwill-texlive.ch change file for cweave.w/ctwill.w +├── ctwill-twinx.1 manpage link to ctwill.1 +├── cweav-ansi.ch change file for cweave.w +├── cweav-borlandc.ch [BCC] change file for cweave.w +├── cweave.1 clone of cweb.1 +├── cweave.dsp [MSVC] Developer Studio Project for CWEAVE +├── cweav-extensions.ch change file for cweave.w +├── cweav-i18n.ch change file for cweave.w +├── cweav-memory.ch [AMIGA,BCC] change file for cweave.w +├── cweav-newpage.ch change file for cweave.w +├── cweav-output.ch change file for cweave.w +├── cweav-patch.ch change file for cweave.w +├── cweav-texlive.ch change file for cweave.w +├── cweav-translation.ch [AMIGA,BCC] change file for cweave.w +├── cweav-twill.ch change file for cweave.w +├── cwebbin.dsw [MSVC] Developer Studio Workspace for all three +├── cwebbin.spec extended build recipe for debbuild +├── cwebinputs +│  ├── amiga_types.w keywords by Commodore and SAS +│  ├── c++1xlib.w Keywords for modern C++ standards +│  ├── cweb_ecma94.w Transliteration table for Latin-1 +│  ├── cweb_hp8.w Transliteration table for HP Roman 8 +│  ├── cweb_mac8.w Transliteration table for Mac 8-bit +│  └── cweb_pc850.w Transliteration table for Codepage 850 +│  ├── iso_types.w Keywords for ISO C/C++ standards +├── cwebman.ch change file for cwebman.tex +├── examples +│  ├── cct.w program to translate between character tables +│  ├── commonwords.w program to count word frequencies in text files +│  ├── extex-ansi.ch change file for extex.w +│  ├── Makefile.sas [AMIGA] makefile for SAS/C compiler 6.0 and up +│  ├── matrix.w C++ program to invert complex 4x4 matrices +│  ├── primes.ch changes for the first 1000000 prime numbers +│  ├── primes.w program to print the first 1000 prime numbers +│  ├── README.p11 extended description for the added examples +│  ├── sample.w program to produce random numbers +│  ├── treeprint-ansi.ch change file for treeprint.w +│  ├── wc-ansi.ch change file for wc.w +│  ├── wmerg-ansi.ch change file for wmerge.w +│  ├── wmerg-borlandc.ch [BCC] change file for wmerge.w +│  ├── wmerge.dsp [MSVC] Developer Studio Project for WMERGE +│  ├── wmerg-extensions.ch change file for wmerge.w +│  ├── wmerg-memory.ch [AMIGA,BCC] change file for wmerge.w +│  ├── wmerg-output.ch change file for wmerge.w +│  ├── wmerg-patch.ch change file for wmerge.w +│  └── wordtest-ansi.ch change file for wordtest.w +├── LICENSE MIT license +├── Makefile.bcc [BCC] makefile for Borland C/C++ +├── Makefile.sas [AMIGA] makefile for SAS/C compiler 6.0 and up +├── Makefile.unix makefile for cc, c89, gcc, ... on several machines +├── po +│  ├── cweb.pot Portable Object Template for i18n +│  ├── cweb-tl.pot Portable Object Template for i18n +│  ├── de +│  │  ├── cweb.po Portable Object for German t10n +│  │  ├── cweb-tl.po Portable Object for German t10n +│  │  └── web2c-help.po Portable Object for German t10n +│  ├── it +│  │  └── cweb.po Portable Object for Italian t10n +│  └── web2c-help.pot Portable Object Template for i18n +├── prod-twill.ch change file for prod.w/CTWILL +├── README.md Github project description +├── README.txt the current file +├── system.bux auxiliary file for CTWILL +├── texinputs +│  ├── cweb_ecma94.sty Umlauts and special characters for Latin-1 +│  ├── cweb_hp8.sty Umlauts and special characters for HP Roman 8 +│  ├── cweb_mac8.sty Umlauts and special characters for Macintosh +│  ├── cweb_pc850.sty Umlauts and special characters for IBM 850 +│  ├── dctwimac.tex German captions in addition to ctwimac.tex +│  ├── dcwebmac.tex German captions in addition to cwebmac.tex +│  ├── dcwebstrings.tex Common German captions +│  ├── dproofmac.tex German captions in addition to proofmac.tex +│  ├── fcwebmac.tex French captions in addition to cwebmac.tex +│  ├── icwebmac.tex Italian captions in addition to cwebmac.tex +│  └── Xcwebmac.tex beautifications for cwebmac.tex +└── texlive.w introduction to *-w2c.ch change files + +%%% HOW TO CREATE THE BINARIES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +[UNIX/Linux, GCC] The current setup of CWEBbin has been created on a Linux PC + running GNU/Linux including the gcc/g++ compiler and the TeXLive system. + Any similar setup should also work. All set? + -- Then proceed as follows: + + 1. Check the entries in Makefile.unix for local conditions like a + different compiler name or additional compiler options you want to + use. Check also the other lines in Makefile.unix so that all + directory definitions are set properly for your local system + configuration. + + 2. If you want to create the ctangle and cweave binaries for a + different language than English, set a link from one of the + `[dei]cweb.h' header files to `cweb.h' in the ./catalogs directory. + + 3. Touch common.cxx and ctangle.cxx to make them up-to-date. Then say + `make -f Makefile.unix boot'; this will create a local ctangle binary + simply by compiling the distributed .cxx files. + + 4. To make ctangle, cweave, and the wmerge processor say `make -f + Makefile.unix [all]'. To get some reassurance that things are OK, you + can say `make cautiously', which ensures that ctangle will reproduce + itself. (Otherwise the source files common.w and ctangle.w won't + actually have been used.) + + 5. After all is set, you should become `superuser' and run `make -f + Makefile.unix install'; this will install the binaries, the TeX macros + and CWEB @include files, and some documentation in places specified in + Makefile.unix. + + 6. If you want a full documentation of the tangle and weave programs, + type `make -f Makefile.unix docs' or preferably `make -f Makefile.unix + fullmanual'. The documentation created will include all changes + introduced by the .ch files. + + 7. To delete all intermediate files created in the make processes say + `make -f Makefile.unix clean'. + + 8. To remove the complete patch from the installed CWEB system, say + `make -f Makefile.unix remove'. You will be left with the original + CWEB system. + + 9. To remove the complete base system leaving only the patch behind, + you can say `make -f Makefile.unix remove-orig'. + + 10. The other labels have not been tested seriously. :-) + +[AMIGA, SAS/C++ 6] I can no longer support this compiler and computer system + and most likely it's impossible to install CWEBbin with SAS/C++. The + source files common.cxx, common.h, and ctangle.cxx can definitely NOT + be used with SAS/C++ anymore. They were created on a Linux PC leaving + the Amiga stuff out. You will have to use an older ctangle binary to + create the TIE processor and then create the complete set of change + files according to Makefile.sas. Good luck! + +[MS/DOS, BORLAND C++ 3.1] I can no longer support this compiler and most + likely it's impossible to install CWEBbin with BC. The source files + common.cxx, common.h, and ctangle.cxx can definitely NOT be used with + BC anymore. They were created on a Linux PC leaving the + `*-borlandc.*' change files out. You will have to use an older + ctangle.exe binary to create the TIE processor and then create the + complete set of change files according to Makefile.bcc. Good luck! + +[MS/WINDOWS, MS/VC++ 4.1 and 6.0] I can no longer support this compiler and + most likely it is impossible to install CWEBbin with this version of + the Microsoft compiler. The current version of MS/VC++ is supported + through binary versions of CTANGLE and CWEAVE at + http://www.literateprogramming.com. + +%%% LIST OF FEATURES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +After the announcement of CWEB 3.3 [p11a] on news:comp.programming.literate +and news:comp.text.tex there have been questions: ``What's different in your +CWEB system in respect to the original system? Why should programmers use +your's instead of the Levy/Knuth distribution?'' + +So here are some words about the `special features': + + - [GENERAL] The complete source code can (or at least at some point of time + could) be recompiled with SAS/C for AmigaOS, with Borland C/C++ 3.1 for + MS/DOS, with MS/VC++ 4.1 and 6.0 for MS/Windows, and with CC, C89, and + GCC for UNIX without the need for further changes. Well, to be honest, + the complete source code was only compiled using GCC. + + - [GENERAL] The `+l' (or `-l') option with its argument `X' causes CWEAVE + to prepend `X' to `cwebmac.tex' in the first line of the output file. + `X' may be any string of characters (case dependent and possibly empty), + e.g., if you called CWEAVE with the option `+ldansk', the danish macro + file `danskcwebmac.tex' (if there's such a thing) would be included + instead of `cwebmac.tex'. The macro file `Xcwebmac.tex' itself contains + some modifications to the original `cwebmac.tex' file as described in the + documentation. At the time of writing there are macro packages + `dcwebmac.tex' by Andreas Scherer for German users, `icwebmac.tex' by + Giuseppe Ghibò for Italian users, and `fcwebmac.tex' by Denis B. Roegel + for French users. These macro files translate all captions used by the + CWEAVE processor into the respective languages and also include support + for special characters like umlauts (diacritics) and some other + frequently used specials according to ISO Latin-1, IBM PC International + Codepage 850, Macintosh 8-bit font table and HP Roman 8. Similar + packages for other languages can easily be added to this scheme. To date + there are no conflicting languages, so single-character strings are used. + However, there is no support for UTF-8 at this time. + + - [GENERAL] <Obsolete> As of March 1997 five new macros with the prefix + `pdf' are available. Together with the "tex2pdf/pdftex" system they + enable the presenation of CWEB source code in "Hyper CWEB" form, i.e., + all section references and the table of contents are hyperlinked and can + be used in PDF readers. Call "cweave -lpdf" (or "cweave -lpdfd", etc.) + to include the respective macro file in the TeX output, before running + "pdftex". </Obsolete> As of July 2000 these macros are united with the + generic macros, i.e., `cwebmac.tex' can be used with "plain TeX," "plain + TeX + dvipdfm," and "pdftex." + + - [GENERAL] There are transliteration tables `cweb_ecma94.w', `cweb_hp8.w', + `cweb_mac8.w', and `cweb_pc850.w' for @inclusion in limbo. These files + \input the associated TeX macro files `cweb_ecma94.sty', `cweb_hp8.sty', + `cweb_mac8.sty', and `cweb_pc850.sty' with i18n support of extended code + tables. These macros can, of course, also be used independently of + the CWEB files and have been set up to work as `packages' for LaTeX + as well. The option `+a' of former versions of the AMIGA installation + is now replaced by the file `amiga_types.w' that can be `@i'ncluded in + the source files where needed. + + - [GENERAL] Both CTANGLE and CWEAVE are fully localized for other natural + languages than the English default. See the 'po/' directory for the + files used with the "GNU gettext utilities". + <Obsolete>#include files with the English, German, and Italian strings + are provided in the ./catalogs directory for non-localized operating + systems. On AmigaOS 2.1/3.0 localization is done with the help of + language catalogs and the system `locale.library'. Default is (of + course) English; catalogs descriptions for German and Italian are + provided and other language catalogs can easily be added.</Obsolete> + + - [GENERAL] An algorithm for recursive `@i'nclude file search was adapted + from the Web2c implementation of TeX and METAFONT. The weave and tangle + processors as well as wmerge now recognize the environment variable + CWEBINPUTS. When an include file name (argument to the @i command) can + not be found in the current directory or in the absolute path, a + multi-path search is performed. CWEBINPUTS---if not empty---may contain + a list of search path names, delimited by PATH_SEPARATORs, e.g., colons. + The current directory is represented by an empty path entry, but it is + searched first anyway. In the respective makefiles for AMIGA, UNIX and + MS/DOS some decent defaults are set in the compile time constant + CWEBINPUTS, which always is appended to any environment variable, so you + don't have to repeat the defaults. + + - [GENERAL] The `-o' option was added in connection with Carsten Steger's + `-i' option. Both affect typographic niceties in the woven output. + + - [GENERAL] The output files of CTANGLE and CWEAVE are only written when + there are significant changes to their contents, i.e., when a CWEB source + creates multiple output files, only those modules effected by changes + will be recompiled in the `make' process. This mechanism was taken from + Preston Briggs's NUWEB program, to whom credit is due. + + - [GENERAL] Five additional CWEB example programs are included as examples + for the portability between PASCAL-WEB and C-CWEB and for the usage of + the C++ features of version 3.1. The fifth example program is a simple + text converter for files with extended character codes of ISO-Latin-1, + PC-850, MAC 8-bit font, and HP 8-bit font. + + - [GENERAL] The latest release of CWEBbin is fully compatible with the + "TeX Live" system. 'cwebbin.spec' and 'Makefile.unix' are prepared to + work with the '--with texlive' option. This appends and merges the + *-texlive.* change files and creates a set of *-w2c.ch change files and + an extended comm-foo.h @include file. These files are drop-in + replacements for the hand-coded files used in the TL ecosystem. + + - [GENERAL] The latest latest release of CWEBbin includes CWEAVE's + 'evil twin' CTWILL, together with its utility programs 'refsort' and + 'twinx', and the associated TeX macros. + + - [AMIGA] Two AREXX scripts for use with the CygnusEd Professional Editor + were added to this package, one for CTANGLE and CWEAVE, localized with + the help of language catalogs under AmigaOS version 2.1 or 3.0, the other + for use with the SAS/C 6.x compiler in connection with CED V3.5. + + - [AMIGA] All three system programs support the `version' information + command of the Amiga operating system. + + - [AMIGA] All three system programs are compiled in the NEAR data segment + and can be made `resident'. + + - [AMIGA] The `+m' option enables ARexx communication between CWEB and the + SAS/C message browser SCMSG. Any warnings or error messages are + transferred to its list window. The behaviour of this feature can be + controlled via the external environment variable SCMSGOPT, which may be + set to any legal option string as described in the documentation of the + SAS/C development system by SAS Institute. + +%%% A BIT OF HISTORY %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +Patch level [p9b] of this implementation introduced localization to other +languages than English. All output strings of ctangle and cweave were +replaced by variable references to an array AppStrings in cweb.h (also new), +set to the English defaults at startup time. This works for all systems and +compilers in a compatible way. However, version 2.1 of the AMIGA operating +system introduced the use of language catalogs, thus enabling programs and +applications to be inherently multilingual without recompilation. To +activate this feature, you only have to install AmigaOS 2.1/3.0. Catalogs +for various natural languages are already present at the correct place +(./catalogs/), so ctangle and cweave will greet you in any of these +languages, if you start them from their home directory ./bin and if your +system default language is supported. If you want to use CTANGLE, CWEAVE, +and WMERGE as resident programs, you have to move the language catalogs to +the appropriate directories of the LOCALE: drawer. Catalog translations for +other languages can easily be installed. The author of this distribution +would be grateful to receive catalog translations for other languages for +inclusion in future versions. + +Patch level [p9d] of this implementation introduced dynamic memory allocation +for all internal arrays except for `flags[256]'. By this method both ctangle +and cweave can be compiled in the `NEAR' data segment, so on the AMIGA they +can be made `resident', i.e., they can reside in RAM and don't have to be +loaded from disk after the first time. Patch level [p9d] also introduced an +ARexx communication facility between CWEB (i.e., ctangle and cweave) and the +`Message Browser' SCMSG, included in the SAS/C 6.X development system. If +you run CWEB with the `+m' option, any error messages will be displayed in +the window of SCMSG. The default setting is `off'. You can control the +behaviour of this feature by setting the environment variable SCMSGOPT to +a string with any legal command line options for SCMSG as described in the +documentation provided with the compiler package by SAS Institute. If the +environment variable is not set, the default behaviour of SCMSG will be used. +Patch level [p9d] was evaluated on July 1, 1994, by Andreas Scherer and +Burkhard Schmitt of Bayreuth University with CC and GCC on a HP Apollo +workstation and a Sun 4 workstation and a SGI workstation, and on July 2, +with Borland C/C++ version 3.1 on a MS/DOS machine without any problems. + +Patch level [p10] was released to the AmiNet and the Comprehensive TeX +Archive Network (CTAN) in August 1994. At least one user had problems +installing the MS/DOS version. + +Patch level [p11] was released to the AmiNet and the Comprehensive TeX +Archive Network (CTAN) in December 1994. Only two days later another +update of CWEB 3.3 was released in Stanford, so a new patch level [p11a] +had to be created and released in mid-December 1994. In January 1995 the +successful installation on at least two more systems has been notified. +The first is MS/DOS with GCC, the second OS/2 in genuine mode, both working +with Makefile.unix (and the obvious modifications). + +%%% CREDIT WHERE CREDIT IS DUE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +I want to express my deep gratitude to Silvio Levy and Donald Knuth for the +marvelous tool they provided us with. Also I am indepted to my various pre- +and co-workers on whose initial ideas and contributions this package is based. + + +Happy CWEBbin'! + +January 1, 2019. + +Andreas Scherer + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/web/c_cpp/cwebbin/README.md b/web/c_cpp/cwebbin/README.md new file mode 100644 index 0000000000..245a1a232c --- /dev/null +++ b/web/c_cpp/cwebbin/README.md @@ -0,0 +1,58 @@ +# literate programming in ansi-c/c++ + +*cwebbin* is the ansi-c/c++ implementation of silvio levy's and donald e. +knuth's [cweb system](http://www-cs-faculty.stanford.edu/~uno/cweb.html) and +donald e. knuth's [ctwill program](ftp://ftp.cs.stanford.edu/pub/ctwill). it +requires the contents of [the original source +drop](http://mirrors.ctan.org/web/c_cpp/cweb/cweb.tar.gz) and [the secondary +source drop](ftp://ftp.cs.stanford.edu/pub/ctwill/ctwill.tar.gz), to which it +applies a set of change files to fix warnings issued by modern c/c++ compilers +and to introduce advanced features. see the extensive [readme](README.txt) for +the full story. + +extract *cwebbin-2019.tar.gz* and add the contents of *cweb-3.64c.tar.gz* and +*ctwill.tar.gz* for the full set of source files. unix/linux users should work +with [make -f Makefile.unix](Makefile.unix) exclusively (targets ‘boot,’ +‘cautiously,’ and ‘all’). + +## advanced packaging + +alternatively, you may want to use *rpmbuild* or *debbuild* for compiling the +sources and for creating installable packages in *rpm* and *deb* format. clone +[cweb](https://github.com/ascherer/cweb) and +[cwebbin](https://github.com/ascherer/cwebbin), create the source drops with +``` +git archive -o cweb-3.64c.tar.gz cweb-3.64c +git archive -o cwebbin-2019.tar.gz cwebbin-2019 +``` +respectively, put these two tarballs and the original *ctwill.tar.gz* in the +*SOURCES* directory and *cwebbin.spec* in the *SPECS* directory of your build +arena, and run +``` +rpmbuild -ba SPECS/cwebbin.spec +debbuild -ba SPECS/cwebbin.spec +``` +depending on your preferences. + +## plain vanilla cweb + +if all you want is the original cweb without any add-ons but minus heaps of +compiler warnings, use the special option `--with ansi_only` to include only +the minimal changes required for a clean compilation. +``` +rpmbuild -ba SPECS/cwebbin.spec --with ansi_only +debbuild -ba SPECS/cwebbin.spec --with ansi_only +``` + +## cweb for texlive + +the extended sources and the build system were modified to smoothly integrate +with the [texlive build system](https://github.com/TeX-Live/texlive-source). by invoking +``` +debbuild -bi SPECS/cwebbin.spec --with texlive +``` +you receive a small tarball `cweb-texlive.tar.gz`, which should be extracted in +texlive's source directory `texk/web2c/cwebdir`. this tarball contains +`*-w2c.ch` files that modify the original cweb sources for the texlive +ecosystem. additionally, it contains language catalogs, tex macros, and cweb +include files. diff --git a/web/c_cpp/cwebx/Makefile b/web/c_cpp/cwebx/Makefile new file mode 100644 index 0000000000..a61bd6280a --- /dev/null +++ b/web/c_cpp/cwebx/Makefile @@ -0,0 +1,122 @@ +# This file is part of CWEB, version x3.0. + +# +# Read the README file, then edit this file to reflect local conditions +# + +# The C compiler should be ANSI compatible + +CC = gcc + +# We let CWEAVE report syntax errors by setting +d. +# The flag +m makes the TeX output slightly more compact. +# We use the +e flag since our printer is two-sided. + +CWFLAGS = +mde +CTFLAGS = + +# We keep debugging info around to enable the `+d' option of cweave +CFLAGS = -DDEBUG -DSTAT -g + +# RM and CP are used below in case rm and cp are aliased + +RM= /bin/rm +CP= /bin/cp +RENAME= /bin/mv + + +# Set CCHANGES to common-foo.ch if you need changes to common.w in another +# file than common.ch + +CCHANGES= + +# Set TCHANGES to ctangle-foo.ch if you need changes to ctangle.w in another +# file than ctangle.ch + +TCHANGES= + +# Set WCHANGES to cweave-foo.ch if you need changes to cweave.w in another +# file than cweave.ch + +WCHANGES= + +########## You shouldn't have to change anything after this point ####### + +CWEAVE = ./cweave $(CWFLAGS) +CTANGLE = ./ctangle $(CTFLAGS) + +.SUFFIXES: .tex .dvi .w + +.w.tex: + $(CWEAVE) $* + +.tex.dvi: + tex $*.tex + +.w.c: + $(CTANGLE) $* + +.w.dvi: + make $*.tex + make $*.dvi + +.w.o: + make $*.c + make $*.o + +all: cweb doc listings +cweb: ctangle cweave +manual doc: manual.dvi +listings: common.dvi ctangle.dvi cweave.dvi + +cautiously: + make common.c ctangle.c cweave.c + $(RENAME) ctangle SAVEctangle # save version in case things mess up + make ctangle + $(RENAME) common.c SAVEcommon.c + ./ctangle common $(CCHANGES) + diff common.c SAVEcommon.c + $(RENAME) SAVEcommon.c common.c # restore date + $(RENAME) ctangle.c SAVEctangle.c + ./ctangle ctangle $(TCHANGES) + diff ctangle.c SAVEctangle.c + $(RENAME) SAVEctangle.c ctangle.c # restore date + $(RENAME) cweave.c SAVEcweave.c + ./ctangle cweave $(WCHANGES) + diff cweave.c SAVEcweave.c + $(RENAME) SAVEcweave.c cweave.c # restore date + $(RM) SAVEctangle # succeeded, use new binary from now on + +SAVEctangle.c: + $(CP) ctangle.c SAVEctangle.c + +SAVEcommon.c: + $(CP) common.c SAVEcommon.c + +common.c: common.w $(CCHANGES) common.inc + $(CTANGLE) common $(CCHANGES) +common.h: common.w $(CCHANGES) + $(CTANGLE) common $(CCHANGES) + +common.tex: common.w common.inc + $(CWEAVE) common $(CCHANGES) + +ctangle: ctangle.o common.o + $(CC) $(CFLAGS) -o ctangle ctangle.o common.o + +ctangle.c: ctangle.w $(TCHANGES) common.inc common.h + $(CTANGLE) ctangle $(TCHANGES) + +ctangle.tex: ctangle.w $(TCHANGES) common.inc intro.inc + $(CWEAVE) ctangle $(TCHANGES) + +cweave: cweave.o common.o + $(CC) $(CFLAGS) -o cweave cweave.o common.o + +cweave.c: cweave.w $(WCHANGES) common.inc common.h parser.w rules.w + $(CTANGLE) cweave $(WCHANGES) + +cweave.tex: cweave.w $(WCHANGES) parser.w rules.w common.inc intro.inc + $(CWEAVE) cweave $(WCHANGES) + +manual.dvi: compare.tex diff --git a/web/c_cpp/cwebx/README b/web/c_cpp/cwebx/README new file mode 100644 index 0000000000..66fd57e171 --- /dev/null +++ b/web/c_cpp/cwebx/README @@ -0,0 +1,111 @@ +This file is part of CWEB version x3.04, and explains briefly what CWEB is, +and how to set it up, and what the various files are for. Here is a listing +of the other files constituting the release with a brief indication of their +purpose. + + +common.w % CWEB source for routines common to CTANGLE and CWEAVE +ctangle.w % CWEB source for CTANGLE +cweave.w % CWEB source for bulk of CWEAVE +parser.w % CWEB source for parser of CWEAVE +rules.w % CWEB source for grammar rules of CWEAVE +common.inc % CWEB source included by common.w, ctangle.w, and cweave.w +intro.inc % CWEB source included by ctangle.w and cweave.w + +common.c % C file produced (by CTANGLE) from common.w; for bootstrap +common.h % C file produced (by CTANGLE) from common.w; for bootstrap +ctangle.c % C file produced (by CTANGLE) from ctangle.w; for bootstrap + +cwebxmac.tex % TeX macro file needed to process files produced by CWEAVE +cwebcmac.tex % TeX macro file needed in addition for compatibility mode + +cwebhmac.tex % optional TeX macro file creating hyperlinks for xhdvi (e.g.) + +manual.tex % master file for the CWEB manual +compare.tex % file \input by manual.tex (present to facilitate bootstrap) + +Makefile % control file for UNIX make facility to build CWEB + +man1 % directory with UNIX manpage +man1/cweb.1 % UNIX manpage for CTANGLE and CWEAVE + +examples % directory with sample CWEB programs +examples/compare.w % Compare files; source for compare.tex above +examples/treeprint.w % Convert sorted file listings to a tree diagram +examples/wc.w % Count characters, words and lines in text files +examples/wmerge.w % Perform preprocessing of change files as filter +examples/Makefile + +cweb.el % EMACS file that might facilitate editing CWEB files + + +The CWEB system allows you to write C programs and their documentation at +the same time, in a way that optimises human readability. This version +fully supports ANSI/ISO C, and indeed requires such a compiler for its own +compilation. Like other WEB systems, CWEB allows you to decompose your +programs into small, logically related portions, to add any desired amount +of documentation to each of them, and to present them in any order that help +understanding. As TeX is used to obtain the printed source document, a high +typographic quality can be obtained while allowing for formatting of +complicated formulae and tables; formatting of C program fragments is simple +however, since it is done fully automatically. When used properly, CWEB can +be used to produce documents that combine the qualities of a scientific +publication about algorithms and of a well documented source listing for an +implementation of those algorithms. + +The CWEB system consists of two programs, CTANGLE and CWEAVE, that are to be +used in combination with a C compiler and TeX; both transform text files, +while they share the same input language. CWEB source files, that typically +have suffix `.w', can be converted by CTANGLE to C files (suffix `.c') that +can be compiled, and alternatively can be converted by CWEAVE to TeX files +(suffix `.tex') from which the typeset source listing can be produced by +TeX. More details can be found in the manual, which you obtain by running +TeX on the file manual.tex. Here the main interest is that since CWEB is +written using CWEB you need a bootstrap procedure to get things going. For +this purpose the derived files common.c, common.h and ctangle.c are supplied. +If you have the UNIX make facility, all you have to do is check that these +files are newer than the sources common.w and ctangle.w (they should be when +the archive is unpacked, but you can use "touch" to make them newer in case +this should be necessary), and that the Makefile macro CC refers to an ANSI +compiler (it is set to "cc"; you may need to select "gcc" instead or to +supply additional flags in CFLAGS) and then invoke "make all". Otherwise +here is the required sequence of commands. + +$ cc -c ctangle.c # compile main part of CTANGLE +$ cc -c common.c # compile common code +$ cc -o ctangle ctangle.o common.o # link CTANGLE together +$ ./ctangle cweave # create cweave.c from cweave.w +$ cc -DDEBUG -DSTAT -g -c cweave.c # compile main part of CWEAVE +$ cc -DDEBUG -DSTAT -g -o cweave cweave.o common.o # link CWEAVE together +$ tex manual.tex # typeset the manual + +$ # the rest is only needed to obtain source listings for the CWEB system +$ ./cweave common # produce common.tex +$ tex common # typeset listing of common code +$ ./cweave ctangle # produce ctangle.tex +$ tex ctangle # typeset listing of CTANGLE +$ ./cweave cweave # produce cweave.tex +$ tex cweave # typeset listing of CWEAVE + +By the time you have done all this successfully, you have already tested +CTANGLE and CWEAVE on a substantial amount of input, and you may be +confident that the system is working properly. + +Should you on the other hand encounter problems during bootstrapping, then +things are of course less pleasant, especially because you cannot print the +source listings yet. Few problems are expected though, since there are no +known system dependencies, and identical source code compiles on several +dissimilar systems, one of which is quite non-UNIX. It is expected though that +the compiler can handle large arrays (certainly larger than 64 Kb) and +programs that used command line arguments; if your compiler does not, you will +either have to replace it by a better one or do some tailoring of the +programs. If you encounter any other difficulties, please report them to me at +<maavl@zenon.univ-poitiers.fr>. If you do need to make changes to common.c, +common.h, or ctangle.c, don't forget to write them down, since they will have +to be applied at the proper places in common.w, ctangle.w, or common.inc in +order to persist after bootstrapping. Such patches are best incorporated into +a change file (e.g., ctangle.ch) as explained in the manual. + +Marc van Leeuwen +Universit\'e de Poitiers +http://wallis.univ-poitiers.fr/~maavl/ diff --git a/web/c_cpp/cwebx/common.c b/web/c_cpp/cwebx/common.c new file mode 100644 index 0000000000..bbd7c75d0a --- /dev/null +++ b/web/c_cpp/cwebx/common.c @@ -0,0 +1,885 @@ +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <limits.h> +#include "common.h" +#define max_bytes 50000L +#define max_modules 1000 +#define max_idents 5000 +#define max_sections 4000 +#define hash_size 353 +#define buf_size 100 +#define longest_name 1000 +#define long_buf_size (buf_size+longest_name) +#define local static +#define array_size(a)((int)(sizeof(a)/sizeof(a[0]))) +#define false (boolean)0 +#define true (boolean)1 +#define ctangle 0 +#define cweave 1 +#define and_and 04 +#define lt_lt 020 +#define gt_gt 021 +#define plus_plus 013 +#define minus_minus 01 +#define minus_gt 031 +#define not_eq 032 +#define lt_eq 034 +#define gt_eq 035 +#define eq_eq 036 +#define or_or 037 +#define find_char()(loc<=limit||get_line()) +#define id_index(p)((sixteen_bits)((p)-id_table)) +#define id_at(i)(&id_table[i]) +#define mod_index(p)((sixteen_bits)((p)-mod_table)) +#define mod_at(i)(&mod_table[i]) +#define name_begin(p)((p)->byte_start) +#define length(p)((int)(strlen(name_begin(p)))) +#define name_end(p)(name_begin(p)+length(p)) +#define complete_name(p)((p)->byte_start[-1]=='\0') +#define print_mod(p) \ +printf(": <%s%s>",name_begin(p),complete_name(p)?"":"...") +#define spotless 0 +#define harmless_message 1 +#define error_message 2 +#define fatal_message 3 +#define mark_harmless() \ +if(history==spotless)history=harmless_message;else +#define mark_error()(history=error_message) +#define overflow(t)fatal("\n! Sorry, %s capacity exceeded",t) +#define confusion(s)fatal("\n! This can't happen: %s",s) +#define show_banner flags['b'] +#define show_happiness flags['h'] +#define show_progress flags['p'] +#define show_stats flags['s'] +#define C_plus_plus flags['+'] +#define compatibility_mode flags['c'] +#define update_terminal()fflush(stdout) +#define new_line()putchar('\n') +#define term_write(string,leng)printf("%.*s",(int)(leng),string) +#define buffer_end (&buffer[buf_size-2]) +#define max_include_depth 10 +#define max_include_paths 8 +#define max_path_length 80 +#define lines_match() \ +(change_limit-change_buffer==limit-buffer \ +&&strncmp(buffer,change_buffer,limit-buffer)==0) +#define byte_mem_end (&byte_mem[max_bytes]) +#define id_table_end (&id_table[max_idents]) +#define mod_table_end (&mod_table[max_modules]) +#define copy_char(c)if(id_loc<mod_text_end)*id_loc++=c;else(void)(c) +/*13:*//*8:*/ +#line 122 "common.inc" +boolean names_match(id_pointer,char*,int,int); +void init_id_name(id_pointer,int); +void init_module_name(mod_pointer);/*:8*//*17:*/ +#line 106 "common.w" +int program,phase;/*:17*//*21:*/ +#line 145 "common.w" +char buffer[long_buf_size]; +char*loc=buffer; + +char*limit=buffer;/*:21*//*24:*/ +#line 230 "common.w" +struct f file[max_include_depth]; +struct f change; +local char web_file_name[max_file_name_length] +,change_file_name[max_file_name_length] +,alt_web_file_name[max_file_name_length]; +int include_depth; +boolean input_has_ended; +boolean changing; +boolean web_file_open=false; +boolean print_where=false; +local struct{char*name;int length;} +at_h_path[max_include_paths],at_i_path; + +boolean including_header_file=false;/*:24*//*29:*/ +#line 382 "common.w" +local boolean saved_changing; +local char*saved_change_limit; +local int saved_include_depth=0;/*:29*//*32:*/ +#line 452 "common.w" +local char change_buffer[buf_size]; +local char*change_limit;/*:32*//*44:*/ +#line 632 "common.w" +sixteen_bits section_count; +eight_bits changed_section[(max_sections+7)/8];/*:44*//*53:*/ +#line 811 "common.w" +char byte_mem[max_bytes]; +char*byte_ptr= &byte_mem[0]; +id_info id_table[max_idents]; +id_pointer id_ptr= &id_table[0]; +mod_info mod_table[max_modules]; +mod_pointer mod_ptr= &mod_table[0];/*:53*//*59:*/ +#line 893 "common.w" +id_pointer hash[hash_size];/*:59*//*68:*/ +#line 1009 "common.w" +mod_pointer root=NULL;/*:68*//*81:*/ +#line 1276 "common.w" +char mod_text[longest_name+1]; +char*id_first; +char*id_loc;/*:81*//*89:*/ +#line 1439 "common.w" +int history=spotless;/*:89*//*96:*/ +#line 1535 "common.w" +boolean flags[UCHAR_MAX+1]; +char C_file_name[max_file_name_length]; +local char tex_file_name[max_file_name_length]; +char idx_file_name[max_file_name_length]; +char scn_file_name[max_file_name_length]; +local boolean change_file_explicit=false;/*:96*//*107:*/ +#line 1704 "common.w" +FILE*C_file; +FILE*tex_file;/*:107*//*110:*/ +#line 1730 "common.w" +local boolean term_line_empty=true;/*:110*//*69:*/ +#line 1021 "common.w" +enum mod_comparison +{less, + +equal, +greater, + +prefix, +extension +};/*:69*//*97:*/ +#line 1547 "common.w" +local void scan_args(int argc,char* *argv);/*:97*//*:13*//*19:*/ +#line 116 "common.w" +void common_init(int argc,char* *argv) +{/*28:*/ +#line 352 "common.w" +{char*cwebinputs=getenv("CWEBINPUTS"); +at_h_path[0].name=at_i_path.name=NULL; +#ifdef CWEBHEADERS +at_h_path[0].name=CWEBHEADERS; +at_h_path[0].length=(int)strlen(CWEBHEADERS); +#endif +if(cwebinputs!=NULL) +{at_i_path.length=(int)strlen(cwebinputs); +at_i_path.name=strcpy(byte_ptr,cwebinputs); +byte_ptr+=at_i_path.length+1; +} +else +{ +#ifdef CWEBINPUTS +at_i_path.name=CWEBINPUTS;at_i_path.length=(int)strlen(CWEBINPUTS); +#endif +} +}/*:28*//*60:*/ +#line 901 "common.w" +{int i=hash_size;do hash[--i]=NULL;while(i>0);}/*:60*//*66:*/ +#line 1000 "common.w" +*byte_ptr++='\0';/*:66*//*83:*/ +#line 1307 "common.w" +mod_text[0]=' ';/*:83*//*102:*/ +#line 1649 "common.w" +show_banner=show_happiness=show_progress=true;/*:102*/ +#line 119 "common.w" +scan_args(argc,argv); +}/*:19*//*22:*/ +#line 162 "common.w" +local boolean input_ln(FILE*f) + +{register int c; +register char*k=limit=buffer; +while((c=getc(f))!='\n'&&c!=EOF) +if(k<=buffer_end){*k++=c;if(!isspace(c))limit=k;} +if(k>buffer_end) +{loc= &buffer[0]; +err_print("! Input line too long"); +if(limit>buffer_end)limit=buffer_end; +} +if(buffer[0]=='@'&&limit> &buffer[1]&&strchr("IXYZ",buffer[1])!=NULL) +buffer[1]=tolower(buffer[1]); +return c!=EOF||limit>buffer; +}/*:22*//*25:*/ +#line 261 "common.w" +boolean push_input_file(boolean header,boolean suspend) +{boolean success=false; +char delim=' '; +while(loc<limit&&(isspace((eight_bits)*loc)))++loc; +if(*loc=='"')delim= *loc++; +else if(*loc=='<')delim='>',++loc; +if(loc>=limit)err_print("! Include file name not given"); + +else if(++include_depth>=max_include_depth) +{--include_depth; +err_print("! Too many nested includes"); +} +else +{/*26:*/ +#line 298 "common.w" +{char*k=cur_file_name; +while(loc<limit +&&(delim==' '?!isspace((eight_bits)*loc):*loc!=delim)) +if(k== &cur_file_name[max_file_name_length-1]) +{err_print("! Include file name truncated");break;} + +else*k++= *loc++; +*k='\0'; +}/*:26*/ +#line 275 "common.w" +if(delim!='>'&&(cur_file=fopen(cur_file_name,"r"))!=NULL) +success=true; +else/*27:*/ +#line 320 "common.w" +{char name_buf[max_path_length+max_file_name_length];int i; +if(header) +for(i=0;i<max_include_paths;++i) +if(at_h_path[i].name==NULL)break; +else +{strcpy(name_buf,at_h_path[i].name); +strcpy(&name_buf[at_h_path[i].length],cur_file_name); +if((cur_file=fopen(name_buf,"r"))!=NULL){success=true;break;} +} +else if(at_i_path.name!=NULL) +{strcpy(name_buf,at_i_path.name); +strcpy(&name_buf[at_i_path.length],cur_file_name); +success=(cur_file=fopen(name_buf,"r"))!=NULL; +} +}/*:27*/ +#line 278 "common.w" +if(success) +{cur_line=0;print_where=true;/*30:*/ +#line 392 "common.w" +if(suspend) +{saved_changing=changing;changing=false; +saved_change_limit=change_limit;change_limit=change_buffer; +saved_include_depth=include_depth; +}/*:30*/ +#line 281 "common.w" +} +else +{--include_depth; +if(delim!='>') +err_print("! Cannot open include file"); + +} +} +loc= &limit[1]; +return success; +}/*:25*//*31:*/ +#line 418 "common.w" +local boolean get_web_line(void) +{do +if(++cur_line,input_ln(cur_file)) +if(!compatibility_mode +&&limit> &buffer[1]&&buffer[0]=='@'&&buffer[1]=='i') +{loc= &buffer[2];print_where=true;push_input_file(false,false);} + +else return true; +else if(include_depth==0) +{input_has_ended=true;web_file_open=false;return false;} +else +{fclose(cur_file);print_where=true; +if(include_depth-- ==saved_include_depth) +{changing=saved_changing;change_limit=saved_change_limit; +saved_include_depth=0;including_header_file=false; +if(changing)return false; +} +} +while(true); +}/*:31*//*33:*/ +#line 464 "common.w" +local void prime_the_change_buffer(void) +{change_limit=change_buffer;/*34:*/ +#line 477 "common.w" +do +{if(++change_line,!input_ln(change_file))return; +if(limit> &buffer[1]&&buffer[0]=='@') +if(buffer[1]=='x')break; +else if(buffer[1]=='y'||buffer[1]=='z') +{loc= &buffer[2]; +err_print("! Where is the matching @x?"); +} +else/*35:*/ +#line 497 "common.w" +{if(buffer[1]=='i'&&!compatibility_mode) +{loc= &buffer[2];err_print("! No includes allowed in change file");} +}/*:35*/ +#line 486 "common.w" +}while(true);/*:34*//*36:*/ +#line 506 "common.w" +do +if(++change_line,!input_ln(change_file)) +{loc= &buffer[0];err_print("! Change file ended after @x");return;} +while(limit==buffer);/*:36*//*37:*/ +#line 512 "common.w" +{int n=(int)(limit-buffer);change_limit=change_buffer+n; +strncpy(change_buffer,buffer,n); +}/*:37*/ +#line 470 "common.w" +}/*:33*//*38:*/ +#line 529 "common.w" +local void check_change(void) + +{int n=0; +if(!lines_match())return; +print_where=true; +do +{changing=true;/*39:*/ +#line 560 "common.w" +{if(++change_line,!input_ln(change_file)) +{loc= &buffer[0];err_print("! Change file ended before @y"); + +change_limit=change_buffer;changing=false;return; +} +if(limit> &buffer[1]&&buffer[0]=='@') +if(buffer[1]=='y')break; +else if(buffer[1]=='x'||buffer[1]=='z') +{loc= &buffer[2];err_print("! Where is the matching @y?");} + +else/*35:*/ +#line 497 "common.w" +{if(buffer[1]=='i'&&!compatibility_mode) +{loc= &buffer[2];err_print("! No includes allowed in change file");} +}/*:35*//*37:*/ +#line 512 "common.w" +{int n=(int)(limit-buffer);change_limit=change_buffer+n; +strncpy(change_buffer,buffer,n); +}/*:37*/ +#line 572 "common.w" +}/*:39*/ +#line 538 "common.w" +changing=false; +if(!get_web_line()) +{loc= &buffer[0]; +err_print("! CWEB file ended during a change");return; + +} +if(!lines_match())++n; +}while(true); +if(n>0) +{loc= &buffer[2]; +print("\n! Hmm... %d of the preceding lines failed to match",n); + +err_print(""); +} +}/*:38*//*41:*/ +#line 585 "common.w" +void reset_input(void) +{boolean use_change_file=change_file_name[0]!='\0';/*42:*/ +#line 601 "common.w" +{if((web_file=fopen(web_file_name,"r"))!=NULL) +strcpy(file[0].name,web_file_name); +else if((web_file=fopen(alt_web_file_name,"r"))!=NULL) +strcpy(file[0].name,alt_web_file_name); +else fatal("! Cannot open \"%s\" as input file",web_file_name); + +web_file_open=true; +if(use_change_file) +if((change_file=fopen(change_file_name,"r"))!=NULL) +strcpy(change.name,change_file_name); +else if(!change_file_explicit) +use_change_file=false; +else fatal("! Cannot open \"%s\" as change file",change_file_name); + +}/*:42*/ +#line 588 "common.w" +cur_line=0;change_line=0;include_depth=0; +if(use_change_file){changing=true;prime_the_change_buffer();} + +else change_limit=change_buffer; + +limit=buffer;loc= &buffer[1]; +changing=false;input_has_ended=false; +}/*:41*//*45:*/ +#line 660 "common.w" +boolean get_line(void) +{ +restart: +if(changing)mark_section_as_changed(section_count); +else/*47:*/ +#line 715 "common.w" +{if(get_web_line() +&&change_limit>change_buffer +&&limit-buffer==change_limit-change_buffer +&&buffer[0]==change_buffer[0] +)check_change(); +}/*:47*/ +#line 665 "common.w" +if(changing) +{/*48:*/ +#line 728 "common.w" +{if(++change_line,!input_ln(change_file)) +{err_print("! Change file ended without @z"); +buffer[0]='@';buffer[1]='z';limit= &buffer[2]; +} +if(limit> &buffer[1]&&buffer[0]=='@') +if(buffer[1]=='z') +{prime_the_change_buffer();changing=false;print_where=true;} +else if(buffer[1]=='x'||buffer[1]=='y') +{loc= &buffer[2];err_print("! Where is the matching @z?");} + +else/*35:*/ +#line 497 "common.w" +{if(buffer[1]=='i'&&!compatibility_mode) +{loc= &buffer[2];err_print("! No includes allowed in change file");} +}/*:35*/ +#line 739 "common.w" +}/*:48*/ +#line 667 "common.w" +if(!changing) +{mark_section_as_changed(section_count);goto restart;} +} +loc= &buffer[0];*limit=' '; +if(compatibility_mode&&buffer[0]=='@'&&buffer[1]=='i') +{loc+=2;print_where=true;push_input_file(false,changing); +goto restart; +} +if(limit-buffer>5 +&&strncmp(buffer,"#line",5)==0&&isspace((eight_bits)buffer[5]))/*46:*/ +#line 686 "common.w" +{sixteen_bits line=0; +print_where=true; +loc= &buffer[6];while(loc<limit&&isspace((eight_bits)*loc))++loc; +if(isdigit((eight_bits)*loc)) +{do line=10*line+ *loc++ -'0';while(isdigit((eight_bits)*loc)); +while(loc<limit&&isspace((eight_bits)*loc))++loc; +if(*loc++ =='"') +{int i=0;while(&loc[i]<limit&&loc[i]!='"')++i; +if(loc[i]=='"'&&i<max_file_name_length) +{struct f*cur_f=changing? &change:&file[include_depth]; +cur_f->line=line-1; +strncpy(cur_f->name,loc,i);cur_f->name[i]='\0'; +goto restart; +} +} +} +err_print("! Improper #line directive");goto restart; + +}/*:46*/ +#line 679 "common.w" +return!input_has_ended; +}/*:45*//*50:*/ +#line 755 "common.w" +void check_complete(void) +{if(change_limit!=change_buffer) +{int l=(int)(change_limit-change_buffer); +strncpy(buffer,change_buffer,l);limit= &buffer[l]; +changing=true;loc=buffer;web_file_open=true; + +err_print("! Change file entry did not match"); + +} +}/*:50*//*54:*/ +#line 823 "common.w" +char*store_string(char*s,int l) +{char*dest=byte_ptr; +if(byte_mem_end-byte_ptr<=l)overflow("byte memory"); +byte_ptr+=l;*byte_ptr++='\0';return strncpy(dest,s,l); +}/*:54*//*61:*/ +#line 914 "common.w" +id_pointer id_lookup(char*first,char*last,int ilk) + +{int l,h; +if(last==NULL)last=first+(l=(int)strlen(first)); +else l=(int)(last-first);/*62:*/ +#line 932 "common.w" +{char*p=first; +h= *p;while(++p<last)h=((h<<1)+ *p)%hash_size; +}/*:62*//*63:*/ +#line 942 "common.w" +{id_pointer p=hash[h]; +while(p!=NULL&&!names_match(p,first,l,ilk))p=p->hash_link; +if(p==NULL)/*64:*/ +#line 955 "common.w" +{p=id_ptr; +if(id_ptr++ >=id_table_end)overflow("identifier"); +name_begin(p)=store_string(first,l); +if(program==cweave)init_id_name(p,ilk); +p->hash_link=hash[h];hash[h]=p; +}/*:64*/ +#line 947 "common.w" +return p; +}/*:63*/ +#line 922 "common.w" +}/*:61*//*70:*/ +#line 1036 "common.w" +local enum mod_comparison mod_name_cmp +(char*p,int l1,char*q,int l2) +{int l=l1<l2?l1:l2; +while(--l>=0)if(*p++ != *q++)return* --p< * --q?less:greater; +return l1<l2?prefix:l1>l2?extension:equal; +}/*:70*//*71:*/ +#line 1051 "common.w" +local mod_pointer make_mod_node(char*name) +{mod_pointer node=mod_ptr; +if(mod_ptr++ >=mod_table_end)overflow("module name"); +name_begin(node)=name; +node->llink=NULL;node->rlink=NULL; +init_module_name(node); +return node; +}/*:71*//*72:*/ +#line 1067 "common.w" +local mod_pointer mod_name_lookup(char*name,int l) + +{mod_pointer p; +mod_pointer*loc= &root; +while((p= *loc)!=NULL) +{int l0=p->key_length;char*key=name_begin(p); +switch(mod_name_cmp(name,l,key,l0)) +{case less:loc= &p->llink;break; +case greater:loc= &p->rlink;break; +case equal:case extension:/*74:*/ +#line 1109 "common.w" +{enum mod_comparison cmp= +mod_name_cmp(name+l0,l-l0,key+l0,(int)strlen(key+l0)); +switch(cmp) +{case less:case greater: +err_print("! Incompatible module name"); +print("\nName inconsistently extends <%.*s...>.\n",l0,key); + +return NULL; +case extension:case equal: +if(complete_name(p)) +if(cmp==equal)return p; +else +{err_print("! Incompatible module name"); +print("\nPrefix exists: <%s>.\n",key);return NULL; + +} +name_begin(p)=store_string(name,l);/*77:*/ +#line 1200 "common.w" +free(key-1);/*:77*/ +#line 1128 "common.w" +return p; +} +}/*:74*/ +#line 1081 "common.w" +case prefix: +err_print("! Incompatible module name"); +print("\nName is a prefix of <%s%s>.\n" +,key,complete_name(p)?"":"..."); +return NULL; +} +}/*73:*/ +#line 1098 "common.w" +{(p=make_mod_node(store_string(name,l)))->key_length=l; +return*loc=p; +}/*:73*/ +#line 1090 "common.w" +}/*:72*//*75:*/ +#line 1152 "common.w" +local mod_pointer prefix_lookup(char*name,int l) + +{mod_pointer p=root,*loc= &root; +mod_pointer match=NULL; +mod_pointer saved=NULL; +while(p!=NULL) +{int l0=p->key_length;char*key=name_begin(p); +switch(mod_name_cmp(name,l,key,l0)) +{case less:p= *(loc= &p->llink);break; +case greater:p= *(loc= &p->rlink);break; +case equal:return p; +case extension:/*78:*/ +#line 1209 "common.w" +{enum mod_comparison cmp= +mod_name_cmp(name+l0,l-l0,key+l0,(int)strlen(key+l0)); +switch(cmp) +{case less:case greater: +err_print("! Incompatible module name"); +print("\nName inconsistently extends <%.*s...>.\n",l0,key); + +return NULL; +case prefix:case equal:return p; +case extension: +if(complete_name(p)) +{err_print("! Incompatible module name"); +print("\nPrefix exists: <%s>.\n",key);return NULL; +}/*79:*/ +#line 1236 "common.w" +{/*77:*/ +#line 1200 "common.w" +free(key-1);/*:77*/ +#line 1237 "common.w" +if((key=(char*)malloc(l+2))==NULL)fatal("Out of dynamic memory!"); +*key++='\1'; +strncpy(key,name,l);key[l]='\0'; +name_begin(p)=key; +}/*:79*/ +#line 1224 "common.w" +return p; +} +}/*:78*/ +#line 1167 "common.w" +case prefix: +if(match!=NULL) +{err_print("! Ambiguous prefix");return NULL;} + +match=p;saved=p->rlink;p=p->llink; +} +if(p==NULL&&match!=NULL) +p=saved,saved=NULL; +} +if(match==NULL)/*76:*/ +#line 1190 "common.w" +{char*key=(char*)malloc(l+2); +if(key==NULL)fatal("Out of dynamic memory!"); +*key++='\1'; +strncpy(key,name,l);key[l]='\0'; +(p=make_mod_node(key))->key_length=l; +return*loc=p; +}/*:76*/ +#line 1179 "common.w" +match->key_length=l; +return match; +}/*:75*//*82:*/ +#line 1291 "common.w" +mod_pointer get_module_name(void) +{/*84:*/ +#line 1315 "common.w" +{eight_bits c;char*k=mod_text; +do +{if(!find_char()) +{err_print("! Input ended in module name");break;} + +c= *loc++;/*85:*/ +#line 1342 "common.w" +if(c=='@') +{if((c= *loc++)=='>')break; +if(isspace(c)||c=='*'||c=='~') +{err_print("! Module name didn't end");loc-=2;break;} + +if(k<mod_text_end-1)* ++k='@'; + +}/*:85*/ +#line 1322 "common.w" +if(isspace(c))c=' '; +if(k<mod_text_end-1&&!(c==' '&& *k==' '))* ++k=c; +}while(true); +id_first= &mod_text[1]; +if(k>=mod_text_end-1) +{print("\n! Module name too long: "); +term_write(id_first,25);err_print(".."); +} +id_loc= *k==' '&&k>mod_text?k:k+1; + +}/*:84*/ +#line 1294 "common.w" +{int l=(int)(id_loc-id_first); +return l>=3&&strncmp(id_loc-3,"...",3)==0 +?prefix_lookup(id_first,l-3):mod_name_lookup(id_first,l); +} +}/*:82*//*86:*/ +#line 1360 "common.w" +boolean get_control_text(void) +{char c,*k=id_first= &mod_text[1]; +do +if((*k++= *loc++)=='@') +if((c= *loc++)!='@') +{if(c!='>') +err_print("! Control codes are forbidden in control text"); + +return(id_loc=k-1)==id_first; +} +while(loc<=limit); +err_print("! Control text didn't end"); +return(id_loc=k)==id_first; +}/*:86*//*87:*/ +#line 1386 "common.w" +void get_string(void) +{char c,delim=loc[-1]; +id_loc=id_first= &mod_text[1];copy_char(delim); +if(delim=='L') +*id_loc++=delim= *loc++; +else if(delim=='<')delim='>'; +do +{if(loc>=limit) +{err_print("! String didn't end");loc=limit;break;} + +copy_char(c= *loc++); +if(c=='\\') +if(loc<limit)copy_char(*loc++); + +else if(get_line()) +if(program==cweave)--id_loc; +else copy_char('\n'); +else +{loc=buffer; +err_print("! Input ended in middle of string"); + +break; +} +else if(!including_header_file&&c=='@') +if(*loc=='@')++loc; +else err_print("! Double @ required in strings"); + +} +while(c!=delim); +if(id_loc>=mod_text_end) +{print("\n! String too long: "); +term_write(mod_text+1,25);err_print(".."); +} +}/*:87*//*90:*/ +#line 1449 "common.w" +void err_print(char*s) +{print(*s=='!'?"\n%s.":"%s.",s); +if(web_file_open)/*91:*/ +#line 1462 "common.w" +{char*k,*l=(loc<limit)?loc:limit; +if(changing)printf(" (l. %d of change file)\n",change_line); +else if(include_depth==0)printf(" (l. %d)\n",cur_line); +else printf(" (l. %d of include file %s)\n",cur_line,cur_file_name); +if(l>buffer) +{for(k=buffer;k<l;k++)putchar(*k=='\t'?' ':*k); +new_line(); +for(k=buffer;k<l;k++)putchar(' '); +} +for(k=l;k<limit;k++)putchar(*k); +}/*:91*/ +#line 1452 "common.w" +update_terminal();mark_error(); +}/*:90*//*92:*/ +#line 1483 "common.w" +void wrap_up(void) +{ +#ifdef STAT +if(show_stats)print_stats(); +#endif/*93:*/ +#line 1496 "common.w" +{static char*mess[]= +{"No errors were found.", +"Did you see the warning message above?", +"Pardon me, but I think I spotted something wrong", +"That was a fatal error, my friend." +}; +if(show_happiness||history>0)print("\n(%s)\n",mess[history]); +}/*:93*/ +#line 1489 "common.w" +exit(history>harmless_message); +}/*:92*//*94:*/ +#line 1510 "common.w" +void fatal(char*s,...) +{va_list p;va_start(p,s); +vprintf(s,p);va_end(p);err_print(""); + +history=fatal_message;wrap_up(); +}/*:94*//*98:*/ +#line 1563 "common.w" +local void scan_args(int argc,char* *argv) +{char*dot_pos; +int files_found=0,paths_found=at_h_path[0].name==NULL?0:1; +while(--argc>0) +if(((* ++argv)[0]=='+'||(*argv)[0]=='-')&&(*argv)[1]!='\0')/*103:*/ +#line 1656 "common.w" +{boolean flag_change=(* *argv=='+'); +char*p= &(*argv)[1];unsigned char c; +while((c= *p++)!='\0') +if((c=tolower(c))!='i')flags[c]=flag_change; +else/*104:*/ +#line 1668 "common.w" +{size_t l=strlen(p); +if(l==0)err_print("! Empty include path"); + +else if(l>max_path_length)err_print("! Include path too long"); + +else if(paths_found>=max_include_paths) +err_print("! Too many include paths"); + +else +{at_h_path[paths_found].length=(int)l; +at_h_path[paths_found++].name=strcpy(byte_ptr,p); +byte_ptr+=l+1; +} +break; +}/*:104*/ +#line 1661 "common.w" +}/*:103*/ +#line 1569 "common.w" +else +{if(strlen(*argv)+5>max_file_name_length) + +fatal("! Filename too long:\n%s",*argv); +dot_pos=strrchr(*argv,'.'); +switch(++files_found) +{case 1:/*99:*/ +#line 1597 "common.w" +#ifndef CPPEXT +#define CPPEXT "C" + +#endif +{if(dot_pos==NULL)sprintf(web_file_name,"%s.w",*argv); +else +{sprintf(web_file_name,"%s",*argv); +*dot_pos='\0'; +} +sprintf(alt_web_file_name,"%s.web",*argv); +sprintf(change_file_name,"%s.ch",*argv); +if(program==ctangle) +sprintf(C_file_name,"%s.%s",*argv,C_plus_plus?CPPEXT:"c"); +else +{sprintf(tex_file_name,"%s.tex",*argv); +sprintf(idx_file_name,"%s.idx",*argv); +sprintf(scn_file_name,"%s.scn",*argv); +} +}/*:99*/ +#line 1577 "common.w" + +break;case 2:/*100:*/ +#line 1624 "common.w" +if((*argv)[0]=='-')change_file_name[0]='\0'; +else if((*argv)[0]!='+') +{change_file_explicit=true; +sprintf(change_file_name,dot_pos==NULL?"%s.ch":"%s",*argv); +}/*:100*/ +#line 1578 "common.w" + +break;case 3:/*101:*/ +#line 1634 "common.w" +if(program==ctangle) +if(dot_pos!=NULL)sprintf(C_file_name,"%s",*argv); +else sprintf(C_file_name,"%s.%s",*argv,C_plus_plus?CPPEXT:"c"); +else +{if(dot_pos!=NULL) +{sprintf(tex_file_name,"%s",*argv);*dot_pos='\0';} +else sprintf(tex_file_name,"%s.tex",*argv); +sprintf(idx_file_name,"%s.idx",*argv); +sprintf(scn_file_name,"%s.scn",*argv); +}/*:101*/ +#line 1579 "common.w" + +break;default:/*105:*/ +#line 1688 "common.w" +fatal("! Usage:\n" +"c%se [(+|-)options] cwebfile[.w] [(changefile[.ch]|+|-) [outputfile[.%s]]]" +,program==ctangle?"tangl":"weav" +,program==ctangle?"c":"tex");/*:105*/ +#line 1581 "common.w" +} +} +if(files_found==0)/*105:*/ +#line 1688 "common.w" +fatal("! Usage:\n" +"c%se [(+|-)options] cwebfile[.w] [(changefile[.ch]|+|-) [outputfile[.%s]]]" +,program==ctangle?"tangl":"weav" +,program==ctangle?"c":"tex");/*:105*/ +#line 1584 "common.w" +if(paths_found<max_include_paths) +at_h_path[paths_found].name=NULL; +}/*:98*//*108:*/ +#line 1708 "common.w" +void open_output_file(void) +{char*name;FILE* *file; +if(program==ctangle){name=C_file_name;file= &C_file;} +else{name=tex_file_name;file= &tex_file;} +if((*file=fopen(name,"w"))==NULL) +fatal("! Cannot open \"%s\" as output file",name); + +}/*:108*//*111:*/ +#line 1744 "common.w" +void print(char*s,...) +{va_list p;va_start(p,s); +if(term_line_empty&& *s=='\n')++s; +vprintf(s,p);va_end(p); +term_line_empty=s[strlen(s)-1]=='\n';update_terminal(); +} + +void print_progress(char*s){if(show_progress)print(s);} + +void print_section_progress(void) +{if(show_progress)print("*%u",section_count);}/*:111*/ diff --git a/web/c_cpp/cwebx/common.h b/web/c_cpp/cwebx/common.h new file mode 100644 index 0000000000..6981a0bbcc --- /dev/null +++ b/web/c_cpp/cwebx/common.h @@ -0,0 +1,90 @@ +/*14:*//*15:*/ +#line 90 "common.w" +typedef char boolean; +typedef unsigned char eight_bits; +typedef unsigned short sixteen_bits;/*:15*//*51:*/ +#line 778 "common.w" +typedef struct id_info +{char*byte_start;/*55:*/ +#line 860 "common.w" +struct variant*equiv_or_xref;/*:55*//*57:*/ +#line 871 "common.w" +struct id_info*hash_link; +int ilk;/*:57*/ +#line 780 "common.w" + +}id_info,*id_pointer; + +typedef struct mod_info +{char*byte_start;/*56:*/ +#line 863 "common.w" +struct variant*equiv_or_xref;/*:56*//*65:*/ +#line 979 "common.w" +struct mod_info*llink,*rlink; + +int key_length;/*:65*/ +#line 785 "common.w" + +}mod_info,*mod_pointer;/*:51*//*16:*/ +#line 103 "common.w" +extern int program,phase;/*:16*//*18:*/ +#line 112 "common.w" +void common_init(int argc,char* *argv);/*:18*//*20:*/ +#line 133 "common.w" +extern char buffer[],*loc,*limit;/*:20*//*23:*/ +#line 201 "common.w" +#define max_file_name_length 60 +extern struct f +{FILE*file;char name[max_file_name_length];sixteen_bits line;} +file[],change; +extern int include_depth; +extern boolean input_has_ended,changing,web_file_open,print_where +,including_header_file; + +boolean push_input_file(boolean,boolean); +boolean get_line(void); + +#define cur_file file[include_depth].file +#define cur_file_name file[include_depth].name +#define cur_line file[include_depth].line + +#define web_file file[0].file +#define change_file change.file +#define change_line change.line/*:23*//*40:*/ +#line 579 "common.w" +void reset_input(void);/*:40*//*43:*/ +#line 625 "common.w" +extern sixteen_bits section_count; +extern eight_bits changed_section[]; +#define mark_section_as_changed(n) (changed_section[(n)>>3]|=1<<((n)&7)) +#define section_changed(n) ((changed_section[(n)>>3]&(1<<((n)&7)))!=0)/*:43*//*49:*/ +#line 745 "common.w" +extern void check_complete(void);/*:49*//*52:*/ +#line 796 "common.w" +extern char byte_mem[],*byte_ptr; +extern id_info id_table[],*id_ptr; +extern mod_info mod_table[],*mod_ptr;/*:52*//*58:*/ +#line 886 "common.w" +extern id_pointer hash[]; +#define hash_end (&hash[hash_size]) +id_pointer id_lookup(char*,char*,int);/*:58*//*67:*/ +#line 1005 "common.w" +extern mod_pointer root;/*:67*//*80:*/ +#line 1266 "common.w" +extern char mod_text[],*id_first,*id_loc; +#define mod_text_end (&mod_text[longest_name+1]) +mod_pointer get_module_name(void); +boolean get_control_text(void); +void get_string(void);/*:80*//*88:*/ +#line 1433 "common.w" +extern history; +extern void err_print(char*),wrap_up(void),print_stats(void), +fatal(char*,...);/*:88*//*95:*/ +#line 1531 "common.w" +extern boolean flags[]; +extern char C_file_name[],idx_file_name[],scn_file_name[];/*:95*//*106:*/ +#line 1697 "common.w" +extern FILE*C_file,*tex_file; +void open_output_file(void);/*:106*//*109:*/ +#line 1723 "common.w" +void print(char*,...),print_progress(char*),print_section_progress(void);/*:109*//*:14*/ diff --git a/web/c_cpp/cwebx/common.inc b/web/c_cpp/cwebx/common.inc new file mode 100644 index 0000000000..db117631a6 --- /dev/null +++ b/web/c_cpp/cwebx/common.inc @@ -0,0 +1,184 @@ +@ This is is the beginning of material included (by means of `\.{@@i}') from +the file \.{common.inc}, so that it appears in an identical way in the +programs for \.{CTANGLE}, \.{CWEAVE}, and in the code shared between them +(from the file \.{common.w}). There will therefore not be any code producing +material, just like in a header file. However, the major part of the +declarative information that is shared between all the compilation units is +contained in an actual header file \.{common.h}, which is written as an +auxiliary output file when \.{CTANGLE} processes \.{common.w}; in particular +that file contains all the typedefs, |extern| variable declarations and +function prototype declarations relating to code shared by \.{CTANGLE} and +\.{CWEAVE}. Therefore this file is almost exclusively devoted to some macro +definitions of general utility and a few function prototype declarations for +functions that are {\it used\/} by the shared code but defined separately in +\.{CTANGLE} and \.{CWEAVE}. + +@h <stdlib.h> +@h <stdio.h> +@h <string.h> +@h <ctype.h> +@h <limits.h> +@h "common.h" + +@ We start with some dimensioning parameters that are used both in +\.{CTANGLE} and \.{CWEAVE}. Some of them have been decreased with respect to +their earlier values which were sufficient in the original \.{WEB} to handle +\TeX. Nevertheless they should be sufficient for most applications of +\.{CWEB}, since in \Cee\ there is no need to generate large programs +entirely as a single file (and \TeX\ is not written in \.{CWEB}!). The +smaller values allow \.{CWEB} to run on rather moderate size computers. + +@d max_bytes 50000L + /* the number of bytes in identifier and module names */ +@d max_modules 1000 /* maximal number of module names */ +@d max_idents 5000 /* maximal number of identifiers */ +@d max_sections 4000 + /* greater than the total number of sections, and less than $10240$ */ +@d hash_size 353 /* hash modulus, preferably odd */ +@d buf_size 100 /* maximum length of input line, plus two */ +@d longest_name 1000 + /* module names and strings shouldn't be longer than this */ +@d long_buf_size (buf_size+longest_name) /* for \.{CWEAVE} */ + +@ Here are some macros of general utility. We use |local| in place of +|static| for functions and variables when we want to stress the property +of file-scope rather than static initialisation and permanence of value. +The macro |array_size| can be used to compute the number of elements in a +statically initialised array. For truth values we use the type |boolean| +and the values |true| and |false|. We also make the general convention that +whenever we mention a pointer to the ``end'' of a subsequence of a linear +array, we mean the address of first entry beyond the subsequence itself. +This is in keeping with general practice in~\Cee, and the language definition +in fact guarantees the existence of the address even if the subsequence +should contain the last entry of the array; this convention avoids repeated +use of turgid phrases such as ``pointing one place beyond the last entry +of~\dots''. + +@d local static +@f local static +@d array_size(a) ((int)(sizeof(a)/sizeof(a[0]))) +@d false (boolean) 0 +@d true (boolean) 1 +@d ctangle 0 +@d cweave 1 + +@ Although \.{CWEB} uses the conventions of \Cee\ programs found in +the standard \.{<ctype.h>} header file, it does assume that the character set +is the \caps{ASCII} code. This dependency is mild however, and limited to +the assumption that certain character codes below |040| are not occupied by +ordinary characters. To be able to use such vacant spots in a character +code independent way is possible (as is done in \TeX) by mapping all +characters on input to their \caps{ASCII} positions (thereby ensuring that +certain positions remain unused) and unmapping them on output; such an +approach was deemed too tedious for the \.{CWEB} system however. Rather, +the few places that have to be modified for non-\caps{ASCII} codes can be +located by looking up the entry named ``\caps{ASCII} code dependencies'' +in the index of each of the programs. + +@ A few character pairs are encoded internally as single characters, using +the definitions below. These definitions are consistent with an extension of +\caps{ASCII} code originally developed at \caps{MIT} and explained in +Appendix~C of {\sl The \TeX book\/}. Thus, users who have such a character +set can type things like `\.{\char'32}' and `\.{\char'4}' instead of +`\.{!=}' and `\.{\&\&}'; however, their files will not be too portable until +more people adopt the extended code. To be precise, when moved to other +installations their files can still be processed by \.{CTANGLE} and +\.{CWEAVE}, producing the required output, but users will have problems +reading the source files. Therefore it is advised to use the two-character +form in the \.{CWEB} files, which will be converted to single characters on +input. + +@^ASCII code dependencies@> @^system dependencies@> + +@d and_and 04 /* `\.{\&\&}'; this corresponds to \caps{MIT}'s \.{\char'4} */ +@d lt_lt 020 /* `\.{<<}'; this corresponds to \caps{MIT}'s \.{\char'20} */ +@d gt_gt 021 /* `\.{>>}'; this corresponds to \caps{MIT}'s \.{\char'21} */ +@d plus_plus 013 /* `\.{++}'; this corresponds to \caps{MIT}'s \.{\char'13} */ +@d minus_minus 01 /* `\.{--}'; this corresponds to \caps{MIT}'s \.{\char'1} */ +@d minus_gt 031 /* `\.{->}'; this corresponds to \caps{MIT}'s \.{\char'31} */ +@d not_eq 032 /* `\.{!=}'; this corresponds to \caps{MIT}'s \.{\char'32} */ +@d lt_eq 034 /* `\.{<=}'; this corresponds to \caps{MIT}'s \.{\char'34} */ +@d gt_eq 035 /* `\.{>=}'; this corresponds to \caps{MIT}'s \.{\char'35} */ +@d eq_eq 036 /* `\.{==}'; this corresponds to \caps{MIT}'s \.{\char'36} */ +@d or_or 037 /* `\.{\v\v}'; this corresponds to \caps{MIT}'s \.{\char'37} */ + +@ Invoking the macro |find_char| will fetch a new line if the current one +has been completely read, and it will either return |false| to indicate that +there is no input left, or otherwise ensure that it is safe to inspect +|*loc| (although it might be the line-ending space). + +@d find_char() (loc<=limit || get_line()) + +@ The following declarations give the interface to the searching algorithms. +They are defined separately in \.{CTANGLE} and \.{CWEAVE} and used by the +common code, rather than the other way around. The small number of such +functions does not seem to justify creating a separate header file for them, +and moreover, it would be difficult to decide whether such a header file +should be produced by the source file for \.{CTANGLE} or by the source file +for \.{CWEAVE}. + +@< Function prototypes used but not defined in the shared code @>= + +boolean names_match (id_pointer,char*,int,int); +void init_id_name (id_pointer,int); +void init_module_name (mod_pointer); + +@ Here are some macros associated with the storage of names. The first four +of them convert pointers to structures representing identifiers or module +names to smaller |sixteen_bits| indices into the arrays they are stored in, +and back again. The macros |name_begin|, |name_end| and |length| provide +the basic attributes of the actual string referred to by a name pointer. +The macro |complete_name| tells whether the complete name for a module has +been encountered so far, and |print_mod| prints a module name on the user's +terminal for error reporting, providing a colon, angle brackets and an +ellipsis if necessary. + +@d id_index(p) ((sixteen_bits)((p)-id_table)) +@d id_at(i) (&id_table[i]) +@d mod_index(p) ((sixteen_bits)((p)-mod_table)) +@d mod_at(i) (&mod_table[i]) +@d name_begin(p) ((p)->byte_start) +@d length(p) ((int)(strlen(name_begin(p)))) +@d name_end(p) (name_begin(p)+length(p)) +@d complete_name(p) ((p)->byte_start[-1]=='\0') +@d print_mod(p) + printf(": <%s%s>",name_begin(p), complete_name(p) ? "" : "..." ) + +@ Three levels of error severity are distinguished: informative, serious and +fatal. An overflow stop occurs if \.{CWEB}'s tables aren't large enough. +Sometimes the program will detect a violation of one of its supposed +invariants (at least this could happen if the program still contains some +errors), and \.{CWEB} then prints an error message that is really for the +\.{CWEB} maintenance person, not the user. In such cases the program says +|confusion("indication of where we are")|. + +@d spotless 0 /* |history| value for normal jobs */ +@d harmless_message 1 /* |history| value when non-serious info was printed */ +@d error_message 2 /* |history| value when an error was noted */ +@d fatal_message 3 /* |history| value when we had to stop prematurely */ +@d mark_harmless() + @+ if (history==spotless) history=harmless_message; @+ else @; +@d mark_error() (history=error_message) +@d overflow(t) fatal("\n! Sorry, %s capacity exceeded",t) + @.Sorry,... capacity exceeded@> +@d confusion(s) fatal("\n! This can't happen: %s",s) @.This can't happen@> + +@ Command line flag settings are stored in an array |flags|. For some of +them we use symbolic names. + +@d show_banner flags['b'] /* should the banner line be printed? */ +@d show_happiness flags['h'] /* should lack of errors be announced? */ +@d show_progress flags['p'] /* should progress reports be printed? */ +@d show_stats flags['s'] /* should statistics be printed at end of run? */ +@d C_plus_plus flags['+'] /* is the language `\Cpp' rather than `\Cee'? */ +@d compatibility_mode flags['c'] /* emulate \LKC.? */ + +@ Here are some macros for terminal output. The macro |update_terminal| is +invoked when we want to make sure that everything we have output to the +terminal so far has actually left the computer's internal buffers and been +sent. Note that the cast on the |leng| parameter of |term_write| is +necessary because a pointer difference need not be of type |int|. + +@d update_terminal() fflush(stdout) /* empty the terminal output buffer */ +@d new_line() putchar('\n') +@d term_write(string,leng) printf("%.*s",(int)(leng),string) /* write on the standard output */ diff --git a/web/c_cpp/cwebx/common.w b/web/c_cpp/cwebx/common.w new file mode 100644 index 0000000000..9d5176f559 --- /dev/null +++ b/web/c_cpp/cwebx/common.w @@ -0,0 +1,1757 @@ +% This file is part of CWEBx. +% This program by Marc van Leeuwen based on earlier versions by +% D. E. Knuth., Silvio Levy and Frank Jensen. +% It is distributed WITHOUT ANY WARRANTY, express or implied. +% CWEB (Revision: 2.0) % Don Knuth, July 1990 +% Version 3.x, Marc van Leeuwen, December 1993 +% CWEBx 2+1.0, Marc van Leeuwen, August 1994 +% CWEBx 3.0, Marc van Leeuwen, Januari 1995 +% CWEBx 3.02, Marc van Leeuwen, April 1996 + +% Copyright (C) 1987,1990 Silvio Levy and Donald E. Knuth +% Copyright 1994 Marc A. A. van Leeuwen + +% 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 distributed under the terms of a +% permission notice identical to this one. + +\def\pb{$\.|\ldots\.|$} % C brackets (|...|) +\def\LKC.{Levy/Knuth \.{CWEB}} +\def\:#1{`\.{@@#1}'} + +\def\title{Common code for CTANGLE and CWEAVE (Version x3.0)} +\def\topofcontents +{\topglue 0pt plus .5 fill + \centerline{\titlefont Common code for {\ttitlefont CTANGLE} and + {\ttitlefont CWEAVE}} + \vskip 15pt + \centerline{(\.{CWEB} version x3.0)} +} +\def\botofcontents +{\vfill\noindent + Copyright \copyright\ 1987,\thinspace1990 Silvio Levy and Donald E. Knuth + \par\noindent + Copyright 1994 Marc A. A. van Leeuwen + \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 distributed under the terms of a + permission notice identical to this one. +} + +@* Introduction. This file contains code common to both \.{CTANGLE} +and \.{CWEAVE}, that roughly concerns the following problems: input +routines, name table handling, error handling and handling of the command +line. + +@h <stdarg.h> + +@i common.inc + + +@* Generalities. +That completes the contents of \.{common.inc}. +In the texts below we will sometimes use \.{CWEB} to refer to either +of the two component programs, if no confusion can arise. + +Here is the overall appearance of this file, except for the function +definitions for (local and public) functions, which will follow in the +remaining unnamed sections. + +@c +@< Function prototypes used but not defined in the shared code @>@; +@< Definitions of variables common to \.{CTANGLE} and \.{CWEAVE} @>@; +@< Prototypes of local functions @>@; + +@ For all functions and variables defined here that are accessible to +\.{CTANGLE} and \.{CWEAVE}, prototype respectively |extern| declarations are +placed on the file \.{common.h} that is included by all three source files. +Typedef declarations that are publicly visible also appear in this file. + +@( common.h @>= +@< Public typedef declarations @>@; +@< Declarations of public variables and function prototypes @>@; + +@ Since a number of large arrays are used to store various kinds of data, we +wish to have some control over the number of bytes occupied by the basic +items. In such cases we use one of the following types rather than |int|. + +@< Public typedef declarations @>= +typedef char boolean; +typedef unsigned char eight_bits; +typedef unsigned short sixteen_bits; + +@ In certain cases \.{CTANGLE} and \.{CWEAVE} should do almost, but not +quite, the same thing. In these cases we've written common code for both, +differentiating between the two by means of the global variable |program|. +Furthermore, |CTANGLE| operates in two phases (input and output), and +similarly |CWEAVE| operates in three phases (cross-reference collection, +translation of the source, and output of the index); the global variable +|phase| tells which phase we are in. + +@< Declarations... @>= +extern int program, phase; + +@~@< Definitions... @>= +int program, phase; + +@ There's an initialisation function that gets both \.{CTANGLE} and +\.{CWEAVE} off to a good start. + +@< Declarations...@>= +void common_init (int argc,char** argv); + +@~We will fill in the details of this function later. +@c +void common_init (int argc,char** argv) +{ @< Initialise variables @> + @< Set the default options common to \.{CTANGLE} and \.{CWEAVE} @> + scan_args(argc,argv); +} + + +@* Input routines. +The lowest level of input to the \.{CWEB} programs is performed by +|input_ln|, which must be told which file to read from. The return value +of |input_ln| is |true| if the read is successful and |false| if not +(i.e., if file has ended). The conventions of \TeX\ are followed; i.e., +the characters of the next line of the file are copied into the |buffer| +array, and the global variable |limit| will point to the first unoccupied +position; trailing white space is ignored. + +@< Declarations... @>= +extern char buffer[], *loc, *limit; + +@~The value of |limit| must be less than or equal to |buffer_end|, so that +|*buffer_end| is never filled by |input_ln|. The characters |*limit| and +|limit[1]| are reserved for placing a sentinel at the end of the line. For +the convenience of |CWEAVE|, the buffer is extended by |longest_name| +characters, so that there is enough space to place any module name after +the input. + +@d buffer_end (&buffer[buf_size-2]) /* practical end of |buffer| */ + +@< Definitions... @>= +char buffer[long_buf_size]; /* where each line of input goes */ +char *loc=buffer; + /* points to the next character to be read from the buffer */ +char *limit=buffer; /* points to the end of the input line */ + +@ If a non-empty line follows the last newline in a file, we return it with +a success status when |EOF| is read; in this case the next call will read +|EOF| once again, and that time return failure. We are careful in case +|isspace| is a macro using its argument more than once. As a service to the +include and change file handling functions, |get_line| replaces any initial +\:I, \:X, \:Y, or \:Z by its lower-case counterpart. The value of |loc| is +usually irrelevant during the lower level input functions, and will later be +set properly by |get_line|; however, when error messages are given, the +value of |loc| will determine the way that the current line of input is +displayed, so we give it an appropriate value in those cases. + +@c +local boolean input_ln (FILE *f) + /* copies a line into |buffer| or returns |false| */ +{ register int c; /* the character read */ + register char* k=limit=buffer; /* where next character goes */ + while ((c=getc(f))!='\n' && c!=EOF) + if (k<=buffer_end) {@; *k++=c; @+ if (!isspace(c)) limit=k; } + if (k>buffer_end) + { loc=&buffer[0]; /* now |err_print| will display unbroken input line */ + err_print ("! Input line too long"); @.Input line too long@> + if (limit>buffer_end) limit=buffer_end; /* truncate line */ + } + if (buffer[0]=='@@' && limit>&buffer[1] && strchr("IXYZ",buffer[1])!=NULL) + buffer[1]=tolower(buffer[1]); + return c!=EOF || limit>buffer; /* whether anything new has been found */ +} + +@ Now comes the problem of deciding which file to read from next. Recall +that the actual text that \.{CWEB} should process comes from two streams: a +|web_file|, which can contain possibly nested include commands \:i, +and a |change_file|. +For each file we store its name and line number for error reporting and for +the production of \&{\#line} directives by |CTANGLE|. Information for the +|web_file| together with the currently open include files is kept on a stack +|file| with stack pointer |include_depth|, while the change file has its own +record. The boolean |changing| tells whether or not we're reading from the +|change_file|. Whenever we switch from the |cur_file| to the |change_file| +or vice versa, or if the |cur_file| has changed, we tell |CTANGLE| to print +this information by means of a \&{\#line} directive in the \Cee\ file, by +raising the |print_where| flag. This flag is handled like an interrupt +request, i.e., it remains raised until it is serviced at an appropriate time +(when a complete token has been scanned), whereupon it is cleared by the +service routine. In Phase~I of \.{CWEAVE} header files following \:h +are swiftly scanned for typedef declarations, which creates one or more +extra levels of input, for which we use the same stack as for~\:i. +Certain functions operate differently at such times, so we must be able +to distinguish these inclusions; this is achieved by maintaining a boolean +variable |including_header_file|. + +@< Declarations...@>= +#define max_file_name_length 60 +extern struct f +{ FILE *file; char name[max_file_name_length]; sixteen_bits line; } +file[], change; +extern int include_depth; +extern boolean input_has_ended, changing, web_file_open, print_where + , including_header_file; +@) +boolean push_input_file(boolean,boolean); /* start a new level of input */ +boolean get_line (void); /* get the next line of merged input */ + +#define cur_file file[include_depth].file /* current file */ +#define cur_file_name file[include_depth].name /* current file name */ +#define cur_line file[include_depth].line + /* number of current line in current file */ +#define web_file file[0].file +#define change_file change.file +#define change_line change.line + +@~We also keep an array |at_h_path| of alternative search paths for locating +\:h files, and a path |at_i_path| for locating \:i files. + +@d max_include_depth 10 + /* maximum nesting depth of source files, not counting the change file */ +@d max_include_paths 8 + /* maximum number of additional search paths for \:h files */ +@d max_path_length 80 /* maximal length of a search path */ + +@<Definitions...@>= +struct f file[max_include_depth]; /* stack of non-change files */ +struct f change; /* change file */ +local char web_file_name[max_file_name_length] + , change_file_name[max_file_name_length] + , alt_web_file_name[max_file_name_length]; +int include_depth; /* current level of nesting */ +boolean input_has_ended; /* whether there is no more input */ +boolean changing; /* whether the current line is from |change_file| */ +boolean web_file_open=false; /* whether the web file is being read */ +boolean print_where=false; /* should |CTANGLE| print line and file info? */ +local struct { char* name; int length; } + at_h_path[max_include_paths],at_i_path; + /* alternative search paths for \:h and \:i */ +boolean including_header_file=false; /* are we processing \:h? */ + +@ Before we consider merging the main input with the change file, we first +handle pushing and popping files in the main stream. The function +|push_input_file| opens a new level of input, e.g., when a \:i line is +found. It is used both from within the common code, namely to open files +included by~\:i, and by a direct call from |CWEAVE|, to open files included +by~\:h (and nested include files) during its first phase. When it is +called, the file name is supposed to start at the first non-blank character +from |loc|; it is delimited by either blank space, double quotes or angle +brackets. Once the file name is located and the file is opened, any further +input from the current line will be discarded. This function has two boolean +parameters, the first telling whether the file to be included is a header +file (rather than a \:i file), the second telling whether changes should be +suspended during the file inclusion; the boolean result tells whether a +file was actually opened. + +@c +boolean push_input_file(boolean header,boolean suspend) +{ boolean success=false; /* whether a file as been opened */ + char delim=' '; /* the character being used to delimit the file name */ + while (loc<limit && (isspace((eight_bits)*loc))) ++loc; + if (*loc=='"') delim=*loc++; /* file name in quotes */ + else if (*loc=='<') delim='>',++loc; /* file name in angle brackets */ + if (loc>=limit) err_print("! Include file name not given"); + @.Include file name not given@> + else if (++include_depth>=max_include_depth) + @/{@; --include_depth; + err_print("! Too many nested includes"); @.Too many nested includes@> + } + else + { @< Read file name into |cur_file_name| @> + if (delim!='>' && (cur_file=fopen(cur_file_name,"r"))!=NULL) + success=true; + else @< Try to open file in alternative directory, and set |success| @> + if (success) + {@; cur_line=0; print_where=true; + @< If necessary deactivate the change file @> + } + else + { --include_depth; + if (delim!='>') /* don't complain about system header files */ + err_print("! Cannot open include file"); + @.Cannot open include file@> + } + } + loc=&limit[1]; /* force |input_ln| before next character is read */ + return success; +} + +@ When the include file name is delimited by spaces, any white-space +character will end the name; in any case we accept the end of the line as +end of the file name. + +@< Read file name into |cur_file_name| @>= +{ char* k=cur_file_name; + while (loc<limit + &&(delim==' ' ? !isspace((eight_bits)*loc) : *loc!=delim)) + if (k==&cur_file_name[max_file_name_length-1]) + @/{@; err_print("! Include file name truncated"); break; } + @.Include file name truncated@> + else *k++=*loc++; + *k='\0'; +} + +@ At initialisation time, paths may have been stored in |at_i_path| and +|at_h_path|, that will be prefixed to given file name in an attempt to find +include files. For a file opened in this manner |cur_file_name| will not +contain the full path name but just the final component; this affects error +messages and \&{\#line} directives produced while reading the file. The only +problem that this might cause is that a debugger could be unable to locate +a source file for code included from a \:i file; however, it is not likely +that such a file outside the current directory will contain any code +producing material, and good debuggers have their own means to specify a +search path for source files. + +@< Try to open file in alternative directory... @>= +{ char name_buf[max_path_length+max_file_name_length]; int i; + if (header) + for (i=0; i<max_include_paths; ++i) + if (at_h_path[i].name==NULL) break; + else + { strcpy(name_buf,at_h_path[i].name); + strcpy(&name_buf[at_h_path[i].length],cur_file_name); + if ((cur_file=fopen(name_buf,"r"))!=NULL) {@; success=true; break; } + } + else if (at_i_path.name!=NULL) /* \:i include file */ + { strcpy(name_buf,at_i_path.name); + strcpy(&name_buf[at_i_path.length],cur_file_name); + success= (cur_file=fopen(name_buf,"r"))!=NULL; + } +} + +@ We allow values for |at_h_path[0]| and |at_i_path| to be built into the +program by defining the preprocessor symbols |CWEBHEADERS| respectively +|CWEBINPUTS| to be the appropriate strings; these should be complete +prefixes that can be attached to file names, so they probably have to end +with a pathname separator. For |CWEBHEADERS| one could take the system +include file area, although this should usually not be necessary since the +typedefs contained in system header files are already built into |CWEAVE|. +Defining |CWEBINPUTS| only makes sense if \.{CWEB} is compiled specifically +for one particular project, as long as there are no general purpose +\:i~files. Following \LKC. we also allow |CWEBINPUTS| +to be overridden by an environment variable. For specifying further values of +|at_h_path| we use the more practical method of command line arguments (just +like most \Cee~compilers use for specifying alternative include +directories), which are handled by |scan_args|. + +@< Init... @>= +{ char* cwebinputs=getenv("CWEBINPUTS"); + at_h_path[0].name=at_i_path.name=NULL; /* defaults */ +#ifdef CWEBHEADERS + at_h_path[0].name=CWEBHEADERS; + at_h_path[0].length=(int)strlen(CWEBHEADERS); +#endif + if (cwebinputs!=NULL) + { at_i_path.length=(int)strlen(cwebinputs); + at_i_path.name=strcpy(byte_ptr,cwebinputs); + byte_ptr+=at_i_path.length+1; + } + else + { +#ifdef CWEBINPUTS + at_i_path.name=CWEBINPUTS; at_i_path.length=(int)strlen(CWEBINPUTS); +#endif + } +} + +@ In some cases we wish to suspend changes during the inclusion of a file, +and any nested inclusions. This happens when during the first pass of +\.{CWEAVE} a header file specified after \:h is being read in, or when +in compatibility mode a \:i inclusion is issued under control of the +change file. In such cases we suspend the change file by setting +|changing=false| and |change_limit=change_buffer| as if the change file had +ended, after having saved the old values of |changing| and |change_limit|. +The change file will be reactivated when the included file ends, which +requires saving |include_depth| as well. + +@<Definitions...@>= +local boolean saved_changing; /* were we changing before it was suspended? */ +local char* saved_change_limit; /* end of suspended change line */ +local int saved_include_depth=0; /* depth after opening \:h file */ + +@~Although this could be easily changed, the current code relies on the fact +that suspension cannot be nested (as would be the case if \:h were +activated in a file included under control of the change file), since +compatibility mode does not support \:h file inclusion. + +@< If necessary deactivate the change file @>= +if (suspend) +{ saved_changing=changing; changing=false; +@/saved_change_limit=change_limit; change_limit=change_buffer; +@/saved_include_depth=include_depth; +} + +@ The function |get_web_line| fetches the next line from the main input +stream, taking care of the interpretation on \:i and of restoring input to +the parent file when an include file has ended. Like |input_ln| it returns a +boolean value telling whether a line could be found. When this is not the +case, it means that either the main input stream has dried up, or we have +come to the end of a header file included by an \:h code issued from the +change file, in which case |changing==true| afterwards, and we should not +read from the main input stream after all. + +In compatibility mode |get_web_line| operates differently, since in +\LKC. include files are expanded after matching against the change file +rather than before, although an expanded include file will be reconsidered +for matching against (the same line of) the change file, unless it was +included under control of the change file itself. This means that in +compatibility mode no include file should be opened while preparing the +main input stream for a match. On the other hand, if an include file ends, +there is no other sensible action but to close the file and read on at the +previous level. + +@c +local boolean get_web_line(void) +{ do + if (++cur_line,input_ln(cur_file)) /* then a line has been found */ + if (!compatibility_mode + && limit>&buffer[1] && buffer[0]=='@@' && buffer[1]=='i') + @/{@;loc=&buffer[2]; print_where=true; push_input_file(false,false); } + /* expand \:i */ + else return true; /* return the line without further action */ + else if (include_depth==0) /* then end of input has been reached */ + @/{@; input_has_ended=true; web_file_open=false; return false; } + else + { fclose(cur_file); print_where=true; + if (include_depth--==saved_include_depth) /* then restore |changing| */ + { changing=saved_changing; change_limit=saved_change_limit; + saved_include_depth=0; including_header_file=false; + if (changing) return false; /* fall back into change file */ + } + } + while (true); +} + +@ Now we come to merging the main input stream with the change file. +When |changing| is false, the first non-empty line of |change_file| after +the next \:x to be matched is kept in |change_buffer|, for purposes of +comparison with the next line of |cur_file|. The test |lines_match| is +used for equality between the two lines; it will never return |true| when +|change_limit==change_buffer| because it will not be invoked when +|limit==buffer|. + +@d lines_match() + (change_limit-change_buffer==limit-buffer + && strncmp(buffer, change_buffer, limit-buffer)==0) + +@<Definitions...@>= +local char change_buffer[buf_size]; /* next line of |change_file| */ +local char *change_limit; /* points to the effective end of |change_buffer| */ + +@ The function |prime_the_change_buffer| sets |change_buffer| in preparation +for the next matching operation. After the change file has been completely +input, we set |change_limit=change_buffer|, so that no further matches will +be made; since blank lines in the change file are not used for matching, we +have |(change_limit==change_buffer && !changing)| if and only if the change +file is exhausted (or suspended). This function is called only when +|changing| is true; hence error messages will be reported correctly. + +@c +local void prime_the_change_buffer (void) +{ change_limit=change_buffer; + /* this value is used if the change file ends */ + @<Skip over comment lines in the change file; |return| if end of file@> + @<Skip to the next non-blank line; |return| if end of file@> + @<Move |buffer| and |limit| to |change_buffer| and |change_limit|@> +} + +@ While looking for a line that begins with \:x in the change file, we allow +lines that begin with `\.{@@}', as long as they don't begin with \:y or \:z +(which would probably indicate that the change file is fouled up). + +@<Skip over comment lines in the change file...@>= +do +{ if (++change_line,!input_ln(change_file)) return; + if (limit>&buffer[1] && buffer[0]=='@@') + if (buffer[1]=='x') break; + else if (buffer[1]=='y' || buffer[1]=='z') + { loc=&buffer[2]; /* point out error after \:y or \:z */ + err_print ("! Where is the matching @@x?"); @.Where is the match...@> + } + else @< Check for erroneous \:i @> +} while (true); + +@ When not in compatibility mode, \:i lines are expanded by |get_web_line|, +so it makes no sense to place such a code between \:x and~\:y or between \:y +and~\:z; to allow them outside of the changes they would only cause +confusion by suggesting the inclusion of a subsidiary change file. Therefore +we normally do not allow \:i at the beginning of any line of the change file; +in compatibility mode however, \:i can be used in both sides of a change, +and outside of the changes the code is allowed but ignored. + +@< Check for erron... @>= +{ if (buffer[1]=='i' && !compatibility_mode) + @/{@; loc=&buffer[2]; err_print ("! No includes allowed in change file"); } +} @.No includes allowed...@> + +@ After a \:x has been found, we ignore the rest of the line, as well +as any blank lines that follow it; since |input_ln| removes trailing blanks, +we can simply test for empty lines. + +@< Skip to the next non-blank line... @>= +do + if (++change_line,!input_ln(change_file)) + @/{@; loc=&buffer[0]; err_print("! Change file ended after @@x"); return; } +while (limit==buffer); @.Change file ended...@> + +@ @<Move |buffer| and |limit| to |change_buffer| and |change_limit|@>= +{@; int n=(int)(limit-buffer); change_limit=change_buffer+n; + strncpy(change_buffer,buffer,n); +} + +@ The function |check_change| is used to see if the next change entry should +go into effect; it is called only when |changing| is false. The idea is to +test whether or not the current contents of |buffer| matches the current +contents of |change_buffer|. If not, there's nothing more to do, but if so, +a change is called for. When this happens, all of the text down to the \:y +is supposed to match, and an error message is issued if any discrepancy is +found; after finding \:y we have |changing==true|, so that subsequent lines +will be read from the change file. Since |check_change| is called only when +|change_limit>change_buffer|, i.e., when the change file is active, we don't +have to consider the case here that |get_web_line| returns |false| after +reactivating the suspended change file. + +@c +local void check_change (void) + /* switches to |change_file| if the buffers match */ +{ int n=0; /* the number of discrepancies found */ + if (!lines_match()) return; + print_where=true; /* indicate interrupted line sequencing */ + do + { changing=true; + @< Read a line from the change file into the change buffer; + if \:y is found, |break|; if the change file ends, |return| @> + changing=false; + if (!get_web_line()) + @/{@; loc=&buffer[0]; + err_print("! CWEB file ended during a change"); return; + @.CWEB file ended...@> + } + if (!lines_match()) ++n; + } while (true); + if (n>0) + { loc=&buffer[2]; + print("\n! Hmm... %d of the preceding lines failed to match",n); + @.Hmm... $n$ of the preceding...@> + err_print(""); + } +} + +@ Since we read a line from the change file before reading the line from the +main input stream that should match it, we can use |input_ln| to read the +line into |buffer| first, and move it to |change_buffer| afterwards. +When expecting \:y, we signal and ignore any \:x or \:z. + +@< Read a line from the change file into the change buffer... @>= +{ if (++change_line,!input_ln(change_file)) + { loc=&buffer[0]; err_print("! Change file ended before @@y"); + @.Change file ended...@> + change_limit=change_buffer; changing=false; return; + } + if (limit>&buffer[1] && buffer[0]=='@@') + if (buffer[1]=='y') break; + else if (buffer[1]=='x' || buffer[1]=='z') + @/{@; loc=&buffer[2]; err_print("! Where is the matching @@y?"); } + @.Where is the match...@> + else @< Check for erron... @> + @< Move |buffer| and |limit|... @> +} + +@ The function |reset_input|, which gets \.{CWEB} ready to read the \.{CWEB} +source file(s), is used at the beginning of Phase~I of |CTANGLE|, and +of Phases I~and~II of |CWEAVE|. + +@< Declarations... @>= +void reset_input (void); + +@~Although |reset_input| will not read anything from |web_file| after +opening it, it will move up to the first change line in |change_file|. + +@c +void reset_input (void) /* initialise to read the web file and change file */ +{ boolean use_change_file= change_file_name[0]!='\0'; + @<Open input files@> + cur_line=0; change_line=0; include_depth=0; + if (use_change_file) {@; changing=true; prime_the_change_buffer(); } + /* prepare change file */ + else change_limit=change_buffer; + /* emulate that change file that has ended */ + limit=buffer; loc=&buffer[1]; /* now |find_char()| will read a line */ + changing=false; input_has_ended=false; +} + +@ The following code opens the input files. We complain about a missing +change file only if it was explicitly mentioned as a command line argument. + +@<Open input files@>= +{ if ((web_file=fopen(web_file_name,"r"))!=NULL) + strcpy(file[0].name,web_file_name); + else if ((web_file=fopen(alt_web_file_name,"r"))!=NULL) + strcpy(file[0].name,alt_web_file_name); + else fatal("! Cannot open \"%s\" as input file", web_file_name); + @.Cannot open input file@> + web_file_open=true; + if (use_change_file) + if ((change_file=fopen(change_file_name,"r"))!=NULL) + strcpy(change.name,change_file_name); + else if (!change_file_explicit) + use_change_file=false; /* forget about the change file */ + else fatal("! Cannot open \"%s\" as change file", change_file_name); + @.Cannot open change file@> +} + +@ Here are some more variables relevant to the reading of input files. +Every time an input line is read coming from the change file, and on +returning to reading from |cur_file| (possibly after one or more lines of +the main input stream have been removed by the change file), we mark the +current section as having changed by setting a bit in the bitmap +|changed_section|. + +@< Declarations... @>= +extern sixteen_bits section_count; +extern eight_bits changed_section[]; +#define mark_section_as_changed(n) (changed_section[(n)>>3]|=1<<((n)&7)) +#define section_changed(n) ((changed_section[(n)>>3]&(1<<((n)&7)))!=0) + +@~ +@<Defin...@>= +sixteen_bits section_count; /* the current section number */ +eight_bits changed_section[(max_sections+7)/8]; /* is the section changed? */ + +@ The function |get_line| puts the next line of merged input into the +buffer and updates the variables appropriately. A space is placed at the +right end of the line (i.e., at |*limit|), serving many purposes, like +ensuring that a final `\.@@' on a line will be interpreted as \:\ , +and at other times allowing us to test for interesting characters without +first testing for line end. The function returns |!input_has_ended| because +we often want to check the value of that variable after calling the +function. Usually |get_line| is called after the space at |*limit| has been +processed; therefore a call often takes the form of the macro |find_char|, +which calls |get_line| if necessary, and returns whether it has succeeded +in making |loc| point to a valid character (possibly a line-ending space). + +The logic of marking sections as changed, which is implemented below, is a +bit subtle. The status of |changing| is noted for every line read, but since +the test is made at the start of |get_line|, it actually happens just before +the next line is read in. This means that if the first replacement line of a +change involves the start of a new section, then the new section is marked +as changed rather than the one before it, which is the right choice, +assuming that sections are started at the beginning of a line. It is +possible that |changing| is switched on and then right back off again (if +the line after \:y starts with \:z); in that case we mark the +current section as changed and restart |get_line|, since we still have found +no actual line. + +@c +boolean get_line (void) /* inputs the next line */ +{ +restart: + if (changing) mark_section_as_changed(section_count); + else @<Read from |cur_file| and maybe turn on |changing|@> + if (changing) + { @<Read from |change_file| and maybe turn off |changing|@> + if (!changing) + {@; mark_section_as_changed(section_count); goto restart; } + } + loc=&buffer[0]; *limit= ' '; /* place sentinel space */ + if (compatibility_mode && buffer[0]=='@@' && buffer[1]=='i') + @/{@; loc+=2; print_where=true; push_input_file(false,changing); + goto restart; + } + if (limit-buffer>5 + && strncmp(buffer,"#line",5)==0 && isspace((eight_bits)buffer[5])) + @< Set file name and line number according to \&{\#line} + directive and |goto restart| @> + return !input_has_ended; +} + +@ A \&{\#line} directive should have the form `\.{\#line 85 "common.w"}'. +The line number and file name simply override |cur_line| and |cur_file_name|. + +@< Set file name and line number... @>= +{ sixteen_bits line=0; + print_where=true; /* output a \&{\#line} directive soon */ + loc=&buffer[6]; @+ while (loc<limit && isspace((eight_bits)*loc)) ++loc; + if (isdigit((eight_bits)*loc)) + { do line=10*line + *loc++ -'0'; while (isdigit((eight_bits)*loc)); + while (loc<limit && isspace((eight_bits)*loc)) ++loc; + if (*loc++=='"') + { int i=0; @+ while (&loc[i]<limit && loc[i]!='"') ++i; + if (loc[i]=='"' && i<max_file_name_length) + { struct f* cur_f= changing ? &change : &file[include_depth]; + cur_f->line=line-1; /* directive applies to next line, not this one */ + strncpy(cur_f->name,loc,i); cur_f->name[i]='\0'; + goto restart; + } + } + } + err_print("! Improper #line directive"); goto restart; + @.Improper \#line directive@> +} + +@ After checking that a line could be obtained from the main input stream, +some quick tests are made that will avoid calling |check_change| in most +cases. The switch to |changing| mentioned in the module name is +usually brought about by |check_change|, but may also happen when the +suspension of the change file during inclusion of a header file is ended by +|get_web_line|. In either case further input lines should be taken from +the change file. + +@< Read from |cur_file|... @>= +{ if (get_web_line() + && change_limit>change_buffer + && limit-buffer==change_limit-change_buffer + && buffer[0]==change_buffer[0] + ) check_change(); +} + +@ Here we get a line of input from the change file, unless it starts +with~\:z. The statements `|loc=buffer; *limit=' ';|' were performed in +|get_web_line| for lines from the main input stream, but must be issued +explicitly for lines from the change file. + +@< Read from |change_file|... @>= +{ if (++change_line,!input_ln (change_file)) + { err_print("! Change file ended without @@z"); @.Change file ended...@> + @/buffer[0]='@@'; buffer[1]='z'; limit=&buffer[2]; + } + if (limit>&buffer[1] && buffer[0]=='@@') /* check if the change has ended */ + if (buffer[1]=='z') + @/{@; prime_the_change_buffer(); changing=false; print_where=true; } + else if (buffer[1]=='x' || buffer[1]=='y') + @/{@; loc=&buffer[2]; err_print("! Where is the matching @@z?"); } + @.Where is the match...@> + else @< Check for erron... @> +} + +@ The function |check_complete| will be called at the end of \.{CTANGLE} +and \.{CWEAVE} to check for an unfinished state of the change file. + +@< Declarations...@>= +extern void check_complete (void); + +@~When |check_complete| is called we have |input_has_ended|, which implies +|!changing|. The only thing to test for is that there is no change line still +waiting for a match. In order to get a decent display of the non-matching +line, we copy it from the |change_buffer| to the |buffer|, and set some other +variable appropriately. There is no need to restore any variables, since +|check_complete| is called at the very end of the run. + +@c +void check_complete (void) /* checks that all changes were picked up */ +{ if (change_limit!=change_buffer) + { int l=(int)(change_limit-change_buffer); + strncpy(buffer,change_buffer,l); limit=&buffer[l]; + changing=true; loc=buffer; web_file_open=true; + /* prepare unmatched line for display */ + err_print("! Change file entry did not match"); + @.Change file entry did not match@> + } +} + + +@* Storage of names and strings. +Both \.{CTANGLE} and \.{CWEAVE} store the strings representing identifiers, +module names and (in case of \.{CWEAVE}) index entries in a large array of +characters, called |byte_mem|. These strings are not accessed directly, +but via structures that collect further information about these objects. +These structures come in two kinds, depending on whether they correspond +to identifiers (or index entries) or to module names; these structures are +called |id_info| and |mod_info| respectively. + +@< Public typedef...@>= + +typedef struct id_info +{ char *byte_start; /* beginning of the name in |byte_mem| */ + @<More fields of |struct id_info|@>@; +} id_info, *id_pointer; +@) +typedef struct mod_info +{ char *byte_start; /* beginning of the name in |byte_mem| */ + @<More fields of |struct mod_info|@>@; +} mod_info, *mod_pointer; + +@ All |id_info| and |mod_info| structures are stored in one of two arrays, +called |id_table| and |mod_table| respectively, and hence all |id_pointer| +and |mod_pointer| values point into these arrays. Therefore we can freely +convert between such a pointer and an index into the appropriate array; +the macros |id_index|, |id_at|, |mod_index| and |mod_at| defined above +perform these conversions. + +@< Declarations... @>= +extern char byte_mem[], *byte_ptr; +extern id_info id_table[], *id_ptr; +extern mod_info mod_table[], *mod_ptr; + +@~The first unused position in |byte_mem| is kept in |byte_ptr|, and the +first unused positions in |id_table| and |mod_table| are similarly kept in +|id_ptr| and |mod_ptr|, respectively. We want to keep +|byte_ptr<=byte_mem_end|, |id_ptr<=id_table_end| and +|mod_ptr<=mod_table_end|. + +@d byte_mem_end (&byte_mem[max_bytes]) /* end of |byte_mem| */ +@d id_table_end (&id_table[max_idents]) /* end of |id_table| */ +@d mod_table_end (&mod_table[max_modules]) /* end of |mod_table| */ + +@<Definitions...@>= +char byte_mem[max_bytes]; /* characters of names */ +char *byte_ptr=&byte_mem[0]; /* first unused position in |byte_mem| */ +id_info id_table[max_idents]; /* information about identifiers */ +id_pointer id_ptr=&id_table[0]; /* first unused position in |id_table| */ +mod_info mod_table[max_modules]; /* information about module names */ +mod_pointer mod_ptr=&mod_table[0]; /* first unused position in |mod_table| */ + +@ Here is a simple function that copies a (not necessarily null-terminated) +string~|s| of length~|l| into |byte_mem| and returns a pointer to the copied +string. + +@c +char* store_string(char* s, int l) +{ char* dest=byte_ptr; + if (byte_mem_end-byte_ptr<=l) overflow ("byte memory"); + byte_ptr+=l; *byte_ptr++='\0'; return strncpy(dest,s,l); +} + +@ A component is present in both |id_info| and |mod_info| structures, +whose function is different for \.{CTANGLE} and \.{CWEAVE}. In |CTANGLE|, +it is used only in |mod_info| structures, and is a pointer to a replacement +text for the module name. In |CWEAVE| it is a pointer to a list of +cross-references for the identifier or module name. The precise nature of +these pointers is of no interest to the common code, and at this point we do +not have the types available to express this field as a |union|. However, +since in both cases it will be a pointer to a structure, we can use a little +trick by declaring it as a pointer to a |struct variant| where |variant| is +not further specified here. Then in \.{CTANGLE} and \.{CWEAVE} we define +|variant| as a macro for the appropriate |struct| specifier, while in the +common code we leave it undefined (the \:d defining |variant| is given +very early, so that it will precede the `\.{@@h "common.h"}' that will read +in the definition of |struct id_info| and |struct mod_info|). + +It is not quite proper that in the common code these structures contain a +pointer to a named but undefined structure, while in the other compilation +unit of \.{CTANGLE} and \.{CWEAVE} respectively, they contain a pointer to +a known structure with a {\it different\/} specifier (due to the macro +definition); in \Cee\ structures with different specifiers can never be the +same type. However, since the linker does not type-check, and the compiled +code is not likely to depend on the name of a structure specifier, this +should cause no problems, except possibly some mild confusion to a debugger. +An alternative solution of defining a structure of the same name to denote +entirely different types in \.{CTANGLE} and \.{CWEAVE} would be clearer to +the computer, but more confusing to humans. Of course we could also have +used a |(void*)| field instead of a pointer to a structure, but this would +require a lot of explicit or implicit casts, allowing possible type errors +to go undetected by the compiler. + +@< More fields of |struct id_info| @>= +struct variant* equiv_or_xref; /* extra information about this identifier */ +@~ +@< More fields of |struct mod_info| @>= +struct variant* equiv_or_xref; /* extra information about this module */ + +@ The |id_info| structures are linked together into (hopefully) short lists +sharing the same hash key by means of the |hash_link| field. In |CWEAVE|, +an additional field is used to store information about the syntactic r\^ole +of the identifier, its so-called |ilk|. + +@< More fields of |struct id_info| @>= +struct id_info *hash_link; /* links identifiers with same hash code */ +int ilk; /* syntactic information, used in |CWEAVE| only */ + +@ The pointers to the heads of the hash lists are stored in the +array~|hash|. Identifiers can be found by computing a hash code~|h| and then +looking at the identifiers represented by the |id_pointer|s |hash[h]|, +|hash[h]->hash_link|, |hash[h]->hash_link->hash_link|,~\dots, until either +finding the desired name or encountering the null pointer. Thus the hash +table consists of entries of type |id_pointer|; it is maintained by the +function |id_lookup|, which finds a given identifier and returns the +appropriate |id_pointer|. The basic matching is done by the function +|names_match|, which is slightly different in \.{CTANGLE} and \.{CWEAVE}. If +there is no match for the identifier, it is inserted into the table. + +@< Declarations... @>= +extern id_pointer hash[]; +#define hash_end (&hash[hash_size]) /* end of |hash| */ +id_pointer id_lookup(char*,char*,int); + +@~The |hash| table has size~|hash_size|. + +@<Definitions...@>= +id_pointer hash[hash_size]; /* heads of hash lists */ + +@ Initially all the hash lists are empty. Although strictly speaking the +array |hash| should be initialised to null pointers by the compiler, +we don't count too much on this in case such pointers are not represented +by null bit patterns. + +@<Init...@>= +{@; int i=hash_size; do hash[--i]=NULL; while(i>0); } + +@ Here is the main function for finding identifiers (and index entries). +The parameters |first| and |last| point to the beginning and end of the +string. The parameter |ilk| is used by |CWEAVE| only in the course of the +matching process; within |id_lookup| it is just passed on to the functions +|names_match| and~|init_id_name|, which are defined in \.{CTANGLE} and +\.{CWEAVE} separately. We facilitate the initialisation of the reserved +words in |CWEAVE| by allowing the use of null-terminated strings rather +than using a pointer to the end of the string: in this case |first| should +point to such a string, and |last==NULL|. + +@c +id_pointer id_lookup (char* first,char* last,int ilk) + /* look up an identifier */ +{ int l,h; /* length and hash code of the given identifier */ + if (last==NULL) last=first+(l=(int)strlen(first)); /* null-terminated string */ + else l=(int)(last-first); /* compute the length */ + @<Compute the hash code |h| of the string from |first| to |last| @> + @<Compute and |return| the name location, + possibly by entering a new identifier into the tables@> +} + +@ A simple hash code is used: If the sequence of character codes is +$c_1c_2\ldots c_n$, its hash value will be +$$ (2^{n-1}c_1+2^{n-2}c_2+\cdots+c_n)\bmod\hbox{|hash_size|}. $$ +The purist might worry about the empty string, which, although of course +impossible as identifier, might be flagged as an index entry; such an index +entry is however explicitly ruled out in |CWEAVE|. + +@<Compute the hash...@>= +{ char* p=first; + h=*p; while (++p<last) h=((h<<1)+*p)%hash_size; +} + +@ Here we either find an existing node for the identifier, or create one, +inserting it into the name table and the hash list. The maximal average +length |max_idents/hash_prime| of hash lists should be sufficiently small +that linearly searching them is quite fast. + +@<Compute and |return|...@>= +{ id_pointer p=hash[h]; /* the head of the hash list */ + while (p!=NULL && !names_match(p,first,l,ilk)) p=p->hash_link; + if (p==NULL) /* we haven't seen this identifier before */ + @< Make |p| point to a fresh |id_node| that refers to a string copied from + |first|, and prepend the node to hash list |h| @> + return p; +} + +@ The information associated with a new identifier must be initialised +in |CWEAVE| in a way that does not apply to~\.{CTANGLE}; hence the +function |init_id_name|. + +@< Make |p| point to a fresh |id_node|... @>= +{ p=id_ptr; /* this is where the new name entry will be created */ + if (id_ptr++>=id_table_end) overflow ("identifier"); + name_begin(p)=store_string(first,l); + if (program==cweave) init_id_name(p,ilk); + p->hash_link=hash[h]; hash[h]=p; /* insert |p| at beginning of hash list */ +} + +@ The names of modules are stored in |byte_mem| together with the identifier +names, but a hash table is not used for them because \.{CWEB} needs to be +able to recognise a module name when given a prefix of that name. To this +end the |mod_info| structures are linked into a binary search tree. The only +unconventional thing about this search tree is that the search key is not +necessarily equal to the full string stored but that a prefix is decisive +for matching purposes: once that prefix matches, not matching the remainder +of the string is considered to be an error. The reason for this is that the +module name may have been specified first by a short prefix and is later +extended; we need to be aware of the ambiguity when a second incompatible +extension of the original prefix is attempted. Therefore a field +|key_length| is included in |struct mod_info| that indicates the length of +the prefix that should determine a unique match; its value is the length of +the shortest partial specification of this module name that has been +encountered so far. + +@< More fields of |struct mod_info| @>= +struct mod_info *llink,*rlink; + /* left and right links in binary search tree */ +int key_length; /* number of characters decisive for match */ + +@ There is one more attribute that must be recorded for a module name, +namely whether the string accessed by the |byte_start| field is the full +module name, or just the longest prefix specified so far (i.e., all +occurrences so far ended with `\.{...}'). Rather than extending |struct +mod_info| with another (boolean) field or encoding this information in one +of the other fields we use a small trick to record this information. All +full module names will be stored in |byte_mem|, and since that is filled +with null-terminated strings, we will have for any |mod_pointer p@;| +representing a complete module name that |p->byte_start[-1]=='\0'|. The +macro |complete_name| defined above tests this condition, and we shall +store incomplete module names in such a way that the condition fails for +them. This method costs just one byte of temporary storage for each module +name that is incompletely specified on its first occurrence, except that a +one-time sacrifice of another byte is needed to guarantee that even the +first name is preceded by a null byte. + +@< Initialise... @>= +*byte_ptr++='\0'; /* prefix a null byte to the first string */ + +@~Like most trees, the search tree for module names starts at its |root|. + +@< Declarations... @>= +extern mod_pointer root; + +@~The search tree starts out empty. +@< Definitions... @>= +mod_pointer root=NULL; + /* the root of the binary search tree for module names */ + +@ The set of all possible strings can be conceptually arranged in an +infinite tree with the empty string at its root, where the direct descendents +of a string are those strings obtained by appending a single letter to it, +listed in alphabetical order. The position of one string relative to another +in this tree is one of five possibilities: to the left of, to the right of, +ancestor of, descendent of, or equal to. The type |enum mod_comparison| +encodes these five possibilities. + +@< Prototypes of local functions @>= +enum mod_comparison @/ +{ less, /* the first name is lexicographically less than, + but no prefix of the second */ + equal, /* the first name is equal to the second */ + greater, /* the first name is lexicographically greater than, + but no extension of the second */ + prefix, /* the first name is a proper prefix of the second */ + extension /* the first name is a proper extension of the second */ +}; + +@~The function |mod_name_cmp| will determine which one of the relations +described above holds for a given pair of strings, each represented by a +pointer to its beginning and its length. + +@c +local enum mod_comparison mod_name_cmp + (char* p, int l1, char* q, int l2) +{ int l= l1<l2 ? l1 : l2; + while (--l>=0) if (*p++!=*q++) return *--p<*--q ? less : greater; + return l1<l2 ? prefix : l1>l2 ? extension : equal; +} + +@ When a new module name is found and approved, we first store the string +representing it in an appropriate place, and then call |new_mod_node| to +install a node for the name in the |mod_table| array and prepare it for +inclusion in the search tree. The information associated with the new node +must be initialised in a slightly different way in |CWEAVE| than in +|CTANGLE|; hence the call of |init_module_name|. + +@c +local mod_pointer make_mod_node (char* name) +{ mod_pointer node=mod_ptr; /* allocate new node */ + if (mod_ptr++>=mod_table_end) overflow ("module name"); + name_begin(node)=name; + node->llink=NULL; node->rlink=NULL; + init_module_name(node); /* initialise new node */ + return node; +} + +@ The function |mod_name_lookup| is used to look up a complete module name +in the search tree, inserting it if necessary, and returns a pointer to +where it was found. Its parameters give the beginning and length of the +module name. The name might be illegal, for instance if it is a prefix of an +existing name, in which case |NULL| is returned rather than a pointer into +|mod_table|, after printing an error message. +@c +local mod_pointer mod_name_lookup (char* name, int l) + /* finds complete module name */ +{ mod_pointer p; /* current node of the search tree */ + mod_pointer* loc=&root; /* |p| will come from this location */ + while ((p=*loc)!=NULL) + { int l0=p->key_length; char* key=name_begin(p); + switch (mod_name_cmp(name,l,key,l0)) + { case less: loc=&p->llink; break; + case greater: loc=&p->rlink; break; + case equal: case extension: + @< Check that |name| matches the remainder of |key|; + if so, complete |p| if necessary and |return p|, + if |name| is a prefix of~|key| fall through, + and otherwise report an error and |return NULL| @> + case prefix: + err_print("! Incompatible module name"); @.Incompatible module name@> + print("\nName is a prefix of <%s%s>.\n" @.Name is a prefix of <...>@> + ,key, complete_name(p) ? "" : "..."); + return NULL; /* dummy module name */ + } + } + @< Copy |name| into |byte_mem|, install a new node in the tree at |*loc|, + and |return| it @> +} + +@ When the name is not found in the tree, a new module has been specified +whose name is complete from the start, and can therefore be installed in +|byte_mem|. Its |key_length| field is set to the length~|l| of the name as +specified here, but it might be decreased later. + +@< Copy |name| into |byte_mem|... @>= +{ (p=make_mod_node(store_string(name,l)))->key_length=l; /* prepare new node */ + return *loc=p; /* install new node into tree */ +} + +@ When the module name |name| being looked up matches the string |key| in a +node~|p| of the search tree for the first |p->key_length| characters, then +this is guaranteed to be the only such match in the tree. It is required +that the rest of |key| matches the remainder of~|name| as well, and if +|complete_name(p)| holds then the match must exhaust~|name|. + +@< Check that |name| matches the remainder of |key|... @>= +{ enum mod_comparison cmp= + mod_name_cmp(name+l0,l-l0,key+l0,(int)strlen(key+l0)); + switch(cmp) + { case less: case greater: + err_print("! Incompatible module name"); @.Incompatible module name@> + print("\nName inconsistently extends <%.*s...>.\n",l0,key); + @.Name inconsistently extends <...>@> + return NULL; + case extension: case equal: + if (complete_name(p)) + if (cmp==equal) return p; + else + { err_print("! Incompatible module name"); @.Incompatible module name@> + print("\nPrefix exists: <%s>.\n",key); return NULL; + @.Prefix exists@> + } + name_begin(p)=store_string(name,l); + /* install |name| in place of |key| */ + @< Deallocate memory for |key| @> + return p; + } +} + +@ The function |prefix_lookup| is similar to |mod_name_lookup|, but it is +called when the specification of a module name ends with `\.{...}'. Here, +unlike in |mod_name_lookup|, there is the possibility that there is more +than one match. The function decides whether there are $0$,~$1$, or~$>1$ +matches present in the tree, but in the final case it need not find all +matches. There is a match of |name| with node~|p| if the first +|p->key_length| characters at |name_begin(p)| are equal, an extension or a +prefix of~|name| (in the last case we should not have |completed(p)|). It is +clear that if there is a match with two distinct nodes, then there is also a +match with their closest common ancestor in the search tree, and since we do +not allow inclusions among the significant parts of the keys in the tree, +this situation can only occur if |name| is a proper prefix for all its +matches. Hence, once a first match is found, any further match must be +among its descendents in the tree. + +The value of |loc| is maintained as in |mod_name_lookup| to allow insertion +of a new node if no match is found; however, once we have |match!=NULL|, the +value of~|loc| becomes irrelevant. + +@c +local mod_pointer prefix_lookup (char* name,int l) + /* finds module name given a prefix */ +{ mod_pointer p=root,* loc=&root; /* current node and where it comes from */ + mod_pointer match=NULL; /* the first matching node, if any */ + mod_pointer saved=NULL; /* another subtree that might have matches */ + while (p!=NULL) + { int l0=p->key_length; char* key=name_begin(p); + switch (mod_name_cmp(name,l,key,l0)) + { case less: p=*(loc=&p->llink); break; + case greater: p=*(loc=&p->rlink); break; + case equal: return p; /* a match, and no other matches are possible */ + case extension: + @< Check that |name| matches the remainder of~|key|; + if so, extend its string if necessary and |return p|, + otherwise report an error and |return NULL| @> + case prefix: + if (match!=NULL) + {@; err_print("! Ambiguous prefix"); return NULL; } + @.Ambiguous prefix@> + match=p; saved=p->rlink; p=p->llink; /* |loc| is irrelevant now */ + } + if (p==NULL && match!=NULL) + p=saved, saved=NULL; /* search other subtree */ + } + if (match==NULL) + @< Copy the incomplete |name| in a temporary place, install a new node + in the tree at |*loc|, and |return| it @> + match->key_length=l; /* |name| is a shorter prefix than used before */ + return match; +} + +@ Incomplete nodes are not stored in |byte_mem|, but temporarily set aside +in dynamic memory, which will be freed when the name is completed. A +non-zero byte is installed at the beginning to make the |complete_name| +macro function properly. Like for complete names, the initial |key_length| +is the full length of the string specified. + +@< Copy the incomplete |name|... @>= +{ char* key=(char*)malloc(l+2); + if (key==NULL) fatal("Out of dynamic memory!"); + *key++='\1'; /* ensure that |complete_name(p)| is false afterwards */ + strncpy(key,name,l); key[l]='\0'; /* store the incomplete name */ + (p=make_mod_node(key))->key_length=l; /* prepare new node */ + return *loc=p; /* install new node into tree */ +} + +@~When freeing the dynamic memory, we mustn't forget to subtract~1. + +@< Deallocate memory for |key| @>= @+ free(key-1); + +@ When we come here, we have found that |name| extends the first +|p->key_length| characters of the name stored in~|p|, and therefore cannot +similarly match any other nodes. + +@< Check that |name| matches the remainder of~|key|; + if so, extend its string if necessary and |return p|, + otherwise report an error and |return NULL| @>= +{ enum mod_comparison cmp= + mod_name_cmp(name+l0,l-l0,key+l0,(int)strlen(key+l0)); + switch(cmp) + { case less: case greater: + err_print("! Incompatible module name"); @.Incompatible module name@> + print("\nName inconsistently extends <%.*s...>.\n",l0,key); + @.Name inconsistently extends <...>@> + return NULL; + case prefix: case equal: return p; + case extension: + if (complete_name(p)) + { err_print("! Incompatible module name"); @.Incompatible module name@> + print("\nPrefix exists: <%s>.\n",key); return NULL; @.Prefix exists@> + } + @< Replace name stored in |p| by the larger prefix |name| @> + return p; + } +} + +@ We come here in the rather unusual case that a second larger prefix of the +same module name is given before the full name is specified. We discard the +old memory for |key| and replace it by a fresh copy of |name|, which is +easier and probably more efficient than to try to reuse the old |key| by +using |realloc|. As for the initial dynamic allocation, we must not forget +the byte at~|key[-1]| here. + +@< Replace name stored in |p| by the larger prefix |name| @>= +{ @< Deallocate memory for |key| @> + if ((key=(char*)malloc(l+2))==NULL) fatal("Out of dynamic memory!"); + *key++='\1'; /* ensure that |complete_name(p)| is false afterwards */ + strncpy(key,name,l); key[l]='\0'; /* store the incomplete name */ + name_begin(p)=key; /* install new name in node |p| */ +} + +@* Lexical routines. +Much of the code of \.{CWEB} deals with lexical matters such as recognising +tokens, control texts, etc. This inevitably involves some complications +since the relevant set of lexical rules varies from place to place in the +source file, being sometimes that of \TeX, sometimes of \Cee, and sometimes +rules defined by \.{CWEB} itself. Furthermore the source file is read in on +three different occasions: once by \.{CTANGLE} and twice by \.{CWEAVE}; the +level of detail in which the source is inspected differs between these +passes. We can alleviate the task of lexical recognition by collecting in +the common code a few functions that will scan specific lexical items; we do +this for various classes of ``large'' lexical items: module names, control +texts and strings. + +In all cases we copy the characters of the lexical item to a separate place, +making minor modifications as necessary (for instance replacing \:@@ +by~`\.@@'). To this end there is a buffer |mod_text| used to copy the +characters into; its name is derived from its most important purpose of +storing module names, for which a separate buffer is definitely needed since +these names can be spread over several lines of input. There are also two +pointers |id_first| and~|id_loc| that are used to point to the beginning +and end of lexical entities. + +@< Declarations... @>= +extern char mod_text[], *id_first, *id_loc; +#define mod_text_end (&mod_text[longest_name+1]) /* end of |mod_text| */ +mod_pointer get_module_name (void); +boolean get_control_text(void); +void get_string(void); + +@~The character |mod_text[0]| will not be used to store any actual +characters, whence we make |mod_text| one slot longer than |longest_name|. + +@<Definitions...@>= +char mod_text[longest_name+1]; /* name being sought for */ +char *id_first; /* where the current identifier begins in the buffer */ +char *id_loc; /* just after the current identifier in the buffer */ + +@ The function for scanning and looking up module names is +|get_module_name|; it will read the module name from the input, and decide +how to search depending on the presence or absence of an ellipsis. This +function is called when \:< has just been scanned; it will scan the +module name and look it up by |mod_name_lookup| or |prefix_lookup| as +appropriate; it returns a |mod_pointer| to the name found, or |NULL| if the +name was found to be erroneous. Consequent to the limit, \.{CWEB} will treat +`\.{@@<...@@>}' as a valid module name if and only if if there exactly one +module name in the entire program. + +@c +mod_pointer get_module_name (void) +{ @< Put module name into |mod_text|, + set |id_first| and |id_loc| appropriately @> + { int l=(int)(id_loc-id_first); + return l>=3 && strncmp(id_loc-3,"...",3)==0 + @/ ? prefix_lookup(id_first,l-3) : mod_name_lookup(id_first,l); + } +} + +@ Module names are placed into the |mod_text| array with consecutive spaces, +tabs, and carriage-returns replaced by single spaces. There will be no +spaces at the beginning or the end. We set |mod_text[0]=' '| to facilitate +this, and put |id_first=&mod_text[1]| for the calls to the lexical routines +that use |mod_text|. + +@<Init...@>= +mod_text[0]=' '; + +@~A module name of exactly |longest_name| characters will cause an error +message, even though all its characters are stored; this is because we +don't take the effort to distinguish a `full' state of the buffer from an +`overflowed' one. + +@< Put module name... @>= +{ eight_bits c; char* k=mod_text; /* points to last recorded character */ + do + { if (!find_char()) + {@; err_print("! Input ended in module name"); break; } + @.Input ended in module name@> + c=*loc++; + @<Handle |c=='@@'|; if end of name, |break|@> + if (isspace(c)) c=' '; /* convert tabs, newlines etc. */ + if (k<mod_text_end-1 && !(c==' ' && *k==' ') ) *++k=c; + } while(true); + id_first=&mod_text[1]; + if (k>=mod_text_end-1) +@/{@; print("\n! Module name too long: "); @.Module name too long@> + term_write(id_first,25); err_print(".."); + } + id_loc= *k==' ' && k>mod_text ? k : k+1; + /* point after last non-space character */ +} + +@ Except for the beginning of new sections we are not fussy about unexpected +control sequences within module names, since we might be within `\pb'; +any problems will be eventually be detected during the output of module +names by |CWEAVE|. So we are only looking for \:> (incidentally this +means that control texts like those introduced by \:t are forbidden +in module names). + +@< Handle |c=='@@'|... @>= +if (c=='@@') +{ if ((c=*loc++)=='>') break; + if (isspace(c) || c=='*' || c=='~') + @/{@; err_print("! Module name didn't end"); loc-=2; break; } + @.Module name didn't end@> + if (k<mod_text_end-1) *++k='@@'; + /* record the `\.{@@}', now |c==loc[-1]| again */ +} + +@ The function |get_control_text| handles control texts. Like +|get_module_name| it copies text until \:> into the |mod_text| array, +and sets |id_first| and |id_loc| appropriately. The function returns a +boolean value telling whether the control text was in fact empty, since that +case sometimes needs special treatment. Always using |get_control_text| +guarantees uniformity in the recognition of various types of control texts +(not counting module names). + +@c +boolean get_control_text(void) +{ char c,* k=id_first=&mod_text[1]; /* points after last recorded character */ + do + if ((*k++=*loc++)=='@@') + if ((c=*loc++)!='@@') + { if (c!='>') + err_print("! Control codes are forbidden in control text"); + @.Control codes are forbidden...@> + return (id_loc=k-1)==id_first; + } + while(loc<=limit); + err_print("! Control text didn't end"); @.Control text didn't end@> + return (id_loc=k)==id_first; +} + +@ And here is a similar function |get_string| that is used to scan strings +and character constants. These can contain newlines or instances of their own +delimiters if they are protected by a backslash. We follow this convention, +but do not allow the string to be longer than |longest_name|. The macro +|copy_char| copies a character to |mod_text| without risking overflow; the +|else| branch of its definition serves only to ensure that any side effect +of its argument is always performed. + +@d copy_char(c) @+ if (id_loc<mod_text_end) *id_loc++=c; @+ else (void)(c) @; + +@c +void get_string(void) +{ char c, delim = loc[-1]; /* what started the string */ + id_loc=id_first = &mod_text[1]; copy_char(delim); + if (delim=='L') + *id_loc++=delim=*loc++; /* after `\.L' comes the real delimiter */ + else if (delim=='<') delim='>'; /* for file names in \&{\#include} lines */ + do + { if (loc>=limit) + {@; err_print("! String didn't end"); loc=limit; break; } + @.String didn't end@> + copy_char(c=*loc++); + if (c=='\\') + if (loc<limit) copy_char(*loc++); + /* record `\.\\|c|' with |c!='\n'| literally */ + else if (get_line()) + if (program==cweave) --id_loc; /* |CWEAVE| erases backslash */ + else copy_char('\n'); /* but |CTANGLE| copies escaped newline */ + else + {@; loc=buffer; + err_print("! Input ended in middle of string"); + @.Input ended in middle of string@> + break; + } + else if (!including_header_file && c=='@@') + if (*loc=='@@') ++loc; /* undouble \:@@ */ + else err_print("! Double @@ required in strings"); + @.Double @@ required...@> + } + while (c!=delim); + if (id_loc>=mod_text_end) + @/{@; print("\n! String too long: "); @.String too long@> + term_write(mod_text+1,25); err_print(".."); + } +} + + +@* Reporting errors to the user. +A global variable called |history| will contain one of four values at the +end of every run: |spotless| means that no unusual messages were printed; +|harmless_message| means that a message of possible interest was printed +but no serious errors were detected; |error_message| means that at least +one error was found; |fatal_message| means that the program terminated +abnormally. The value of |history| does not influence the behaviour of the +program; it is simply computed for the convenience of systems that might +want to use such information. + +@< Declarations... @>= +extern history; /* indicates how bad this run was */ +extern void err_print (char*), wrap_up (void), print_stats (void), + fatal (char*,...); + +@~Despite the name |history|, each run starts in an unblemished state. +@< Definitions... @>= +int history=spotless; /* indicates how bad this run was */ + +@ The call |err_print("! Error message")| will report an error to the user, +printing the error message at the beginning of a new line and then giving an +indication of where the error was spotted in the source file. Note that the +string passed to |err_print| does not end with a period, since one will be +automatically supplied, as will an initial newline if the string begins +with~|"!"|. + +@c +void err_print (char *s) /* prints `\..' and location of error message */ +{ print(*s=='!' ? "\n%s." : "%s.",s); + if (web_file_open) @<Print error location based on input buffer@> + update_terminal(); mark_error(); +} + +@ The error locations can be indicated by using the global variables +|loc|, |cur_line|, |cur_file_name| and |changing|, which tell +respectively the first unlooked-at position in |buffer|, the current +line number, the current file, and whether the current line is from +|change_file| or |cur_file|. + +@< Print error location based on input buffer @>= +{ char *k, *l=(loc<limit) ? loc : limit; /* pointers into |buffer| */ + if (changing) printf(" (l. %d of change file)\n", change_line); + else if (include_depth==0) printf(" (l. %d)\n", cur_line); + else printf(" (l. %d of include file %s)\n", cur_line, cur_file_name); + if (l>buffer) + { for (k=buffer; k<l; k++) putchar(*k=='\t' ? ' ': *k); + new_line(); + for (k=buffer; k<l; k++) putchar(' '); /* space out the next line */ + } + for (k=l; k<limit; k++) putchar(*k); /* print the part not yet read */ +} + +@ When a fatal error is encountered, or when the program comes to its normal +termination, the function |wrap_up| is called, which will not return to the +caller. Some implementations may wish to pass the |history| value to the +operating system so that it can be used to govern whether or not other +programs are started. Here, for instance, we pass the operating system +a status of~0 if and only if at worst only harmless messages were printed. +@^system dependencies@> + +@c +void wrap_up (void) +{ +#ifdef STAT + if (show_stats) print_stats(); /* print statistics about memory usage */ +#endif + @< Print the job |history| @> + exit(history>harmless_message); +} + +@ A spotless status is reported if |show_happiness| is true, any other +exit status is always reported. + +@< Print the job |history| @>= +{ static char* mess[]= + { "No errors were found.", + "Did you see the warning message above?", + "Pardon me, but I think I spotted something wrong", + "That was a fatal error, my friend." + }; + if (show_happiness || history>0) print("\n(%s)\n",mess[history]); +} + +@ When an error or overflow condition is detected for which no recovery is +possible, we call |fatal|, which aborts the program after pointing out the +source of trouble and wrapping up as graciously as possible. + +@c +void fatal(char* s,...) +{ va_list p; va_start(p,s); + vprintf(s,p); va_end(p); err_print(""); + /* print reason and location of fatal stop */ + history=fatal_message; wrap_up(); +} + + +@* Command line arguments. +The user calls \.{CWEAVE} and \.{CTANGLE} with one or more arguments on the +command line. These are either file names or sets of flags to be turned on +(beginning with |"+"|) or off (beginning with |"-"|); in case the special +flag |'i'| occurs, the remainder of that argument is interpreted as a string +rather than as a set of flags. + +The following globals are for communicating the user's desires to the rest +of the program. The various file name variables contain strings with the +names of those files. Most of the flags are undefined but available for +future extensions. + +@< Declarations... @>= +extern boolean flags[]; +extern char C_file_name[],idx_file_name[],scn_file_name[]; + +@~@< Definitions... @>= +boolean flags[UCHAR_MAX+1]; /* an option for each character code */ +char C_file_name[max_file_name_length]; /* name of |C_file| */ +local char tex_file_name[max_file_name_length]; /* name of |tex_file| */ +char idx_file_name[max_file_name_length]; /* name of index file */ +char scn_file_name[max_file_name_length]; /* name of module names file */ +local boolean change_file_explicit=false; + /* was a change file argument specified? */ + +@ We now must look at the command line arguments and set the file names +accordingly. + +@< Prototypes of local functions @>= +local void scan_args (int argc,char** argv); + +@~At least one file name must be present: the \.{CWEB} file. It may have an +extension, or it may omit the extension to get |".w"| or |".web"| added. +The \TeX\ output file name is formed by replacing the \.{CWEB} file name +extension by |".tex"|, and the \Cee\ file name by replacing the extension +by~|".c"|. + +If a second file name is given, it is the change file, again either with an +extension or without one to get |".ch"|. An omitted change file argument +means that the change file name is that of the \.{CWEB} file with the +extension replaced by |".ch"| if that file exists, or none at all otherwise. +If present, a third file name replaces the default output file name, +possibly including the extension. + +@c +local void scan_args (int argc,char** argv) +{ char *dot_pos; /* position of rightmost |'.'| in the argument */ + int files_found=0, paths_found= at_h_path[0].name==NULL ? 0 : 1; + while (--argc>0) /* first ``argument'' (program name) is irrelevant */ + if (((*++argv)[0]=='+' || (*argv)[0]=='-') && (*argv)[1]!='\0') + @<Handle flag argument@> + else + { if (strlen(*argv)+5>max_file_name_length) + /* we need room to add things like `\.{.web}' */ + fatal("! Filename too long:\n%s", *argv); + dot_pos=strrchr(*argv,'.'); + switch (++files_found) + { case 1: + @< Make |web_file_name|, and defaults for other file names, + from~|*argv| @> @+ + break; case 2: @< Make |change_file_name| from |*argv| @> @+ + break; case 3: @< Make output file names from |*argv| @> @+ + break; default: @< Print usage error message and quit @> + } + } + if (files_found==0) @< Print usage error message and quit @> + if (paths_found<max_include_paths) + at_h_path[paths_found].name=NULL; /* mark end of list */ +} + +@ We use |*argv| for the |web_file_name| if there is a |'.'| in it, +otherwise we add |".w"|. We prepare an alternative name |alt_web_file_name| +by adding |"web"| after the dot. The other file names come from adding other +things after the dot. Since there is no general agreement about the proper +extension for \Cpp\ file names, we use a macro |CPPEXT| for it, which +defaults to |"C"| but can be overridden by predefining this preprocessor +symbol when \.{common.c} is compiled. + +@< Make |web_file_name|... @>= +#ifndef CPPEXT +#define CPPEXT "C" + /* extension for \Cpp\ file names; should not exceed 3 characters */ +#endif +{ if (dot_pos==NULL) sprintf(web_file_name,"%s.w",*argv); + else + { sprintf(web_file_name,"%s",*argv); /* use file name and extension */ + *dot_pos='\0'; /* truncate the name before the dot */ + } + sprintf(alt_web_file_name,"%s.web",*argv); + sprintf(change_file_name,"%s.ch",*argv); + if (program==ctangle) + sprintf(C_file_name,"%s.%s",*argv, C_plus_plus ? CPPEXT : "c"); + else + { sprintf(tex_file_name,"%s.tex",*argv); + sprintf(idx_file_name,"%s.idx",*argv); + sprintf(scn_file_name,"%s.scn",*argv); + } +} + +@ If in place of the change file name a `\.-' is specified, we clear the +|change_file_name|, and if a `\.+' is specified we retain the default +(but |files_found| is increased so that the next file name will be the +output file); otherwise, |change_file_explicit| is raised and the default +change file name is replaced by the given one. + +@< Make |change_file_name|... @>= +if ((*argv)[0]=='-') change_file_name[0]='\0'; +else if ((*argv)[0]!='+') +@/{@; change_file_explicit=true; + sprintf(change_file_name,dot_pos==NULL ? "%s.ch" : "%s", *argv); +} + +@ If an output file name is given when calling \.{CWEAVE}, its base name is +also used for |idx_file_name| and |scn_file_name|. + +@< Make output file names... @>= +if (program==ctangle) + if (dot_pos!=NULL) sprintf(C_file_name, "%s", *argv); + else sprintf(C_file_name,"%s.%s", *argv, C_plus_plus ? CPPEXT : "c"); +else +{ if (dot_pos!=NULL) + {@; sprintf(tex_file_name, "%s", *argv); *dot_pos='\0'; } + else sprintf(tex_file_name,"%s.tex", *argv); + sprintf(idx_file_name,"%s.idx",*argv); + sprintf(scn_file_name,"%s.scn",*argv); +} + +@ The |flags| are turned off initially; any flags that are on by default are +set before calling |common_init|. + +@< Set the default options common to \.{CTANGLE} and \.{CWEAVE} @>= +show_banner=show_happiness=show_progress=true; + +@~Flags are made case-insensitive, since some operating systems do not +allow passing of both lower and upper case arguments. The |'i'| flag gets +a special treatment. + +@< Handle flag... @>= +{ boolean flag_change=(**argv == '+'); + char* p=&(*argv)[1]; unsigned char c; + while ((c=*p++)!='\0') + if ((c=tolower(c))!='i') flags[c]=flag_change; + else @< Store string |p| as new include path and |break| @> +} + +@ We copy include paths into |byte_mem|; since we are at the very beginning +of the run, there is no chance that we overflow |byte_mem| already, so that +we need not include a fourth error message below. + +@< Store string |p| as new include path and |break| @>= +{ size_t l=strlen(p); + if (l==0) err_print("! Empty include path"); + @.Empty include path@> + else if (l>max_path_length) err_print("! Include path too long"); + @.Include path too long@> + else if (paths_found>=max_include_paths) + err_print("! Too many include paths"); + @.Too many include paths@> + else + { at_h_path[paths_found].length=(int)l; + at_h_path[paths_found++].name=strcpy(byte_ptr,p); + byte_ptr+=l+1; + } + break; +} + +@ The usage message gives only a sketch of the proper way to call the +\.{CWEB}. programs; more details can be found in the manual. + +@< Print usage error message and quit @>= +fatal("! Usage:\n" @+ +"c%se [(+|-)options] cwebfile[.w] [(changefile[.ch]|+|-) [outputfile[.%s]]]" + , program==ctangle ? "tangl" : "weav" + , program==ctangle ? "c" : "tex"); + +@* Output. The only thing done in the common code for the output files is +declaring the variables through which they are accessed, and opening them. + +@< Declarations... @>= +extern FILE *C_file, *tex_file; +void open_output_file(void); + +@~Having separate variables for the output files of \.{CTANGLE} and +\.{CWEAVE} only serves to give them more meaningful names. + +@<Definitions...@>= +FILE *C_file; /* where output of \.{CTANGLE} goes */ +FILE *tex_file; /* where output of \.{CWEAVE} goes */ + +@~@c +void open_output_file(void) +{ char* name; FILE** file; + if (program==ctangle) {@; name=C_file_name; file=&C_file; } + else {@; name=tex_file_name; file=&tex_file; } + if ((*file=fopen(name,"w"))==NULL) + fatal("! Cannot open \"%s\" as output file",name); + @.Cannot open output file@> +} + +@ All regular terminal output passes through the function |print|, which +takes the place of |printf|; it suppresses initial newlines if the line is +already empty. The function |print_progress| and |print_section_progress| +are used to display progress reports on the screen. + +@< Declarations...@>= +void print(char*,...), print_progress(char*), print_section_progress(void); + +@~A variable |term_line_empty| keeps track of whether we are at the +beginning of an output line, so that we can force certain terminal output +to start on a new line without producing empty lines. + +@<Definitions...@>= +local boolean term_line_empty=true; + /* has anything been written to the current line? */ + +@~Any initial or final newline produced by~|print| is caused by the format +string, not by any of the arguments following it; this makes it easy to +maintain |term_line_empty|. + +If a function produces terminal output otherwise than through one of the +three functions below, it should take care of |term_line_empty| itself; +for instance |err_print| produces the error context using |printf| and +|putchar|, knowing that |term_line_empty==false| at that time, which is +also the correct value afterwards. + +@c +void print(char* s,...) +{ va_list p; va_start(p,s); + if (term_line_empty && *s=='\n') ++s; /* avoid printing empty line */ + vprintf(s,p); va_end(p); /* print formatted value */ + term_line_empty= s[strlen(s)-1]=='\n'; update_terminal(); +} + +void print_progress (char* s) @+{@; if (show_progress) print(s); } + +void print_section_progress (void) +@+{@; if (show_progress) print("*%u",section_count); } + + +@* Index. % CWEAVE will add the index to this section. diff --git a/web/c_cpp/cwebx/compare.tex b/web/c_cpp/cwebx/compare.tex new file mode 100644 index 0000000000..8d8c03f490 --- /dev/null +++ b/web/c_cpp/cwebx/compare.tex @@ -0,0 +1,202 @@ +\input cwebxmac + +\N0 1. Comparing text files. +This is an entirely trivial program, that tests whether two text files are +equal, and if not so, points out the first point of difference. + +\Y\B\h$\.{<stdio.h>}$\par +\B\h$\.{<stdlib.h>}$\par +\Y\B$\&{typedef}~\&{char}~\&{bool};$\par +\fi + +\M2. The outline of the program is simple. We read characters from both input +files into $ c_1$ and~$ c_2$ until the comparison is complete. Line and column +counts are maintained in $\\{line}$ and~$\\{col}$. + +\Y\B\D$\\{left\_margin}$\5 +$\T{1}$\C{ leftmost column number; change to 0 if you prefer }\par +\Y\B$\X5:Functions\X$\7 +\&{int} $\\{main}$\5 +$(\1\1\1\&{int}~ n,\31~\&{char}~\m*\m*\\{arg}\2\2\2)$\6 +$\a\{\1\&{FILE}~\m* f_1,\31~\m* f_2;$\C{ the two input files }\6 +$\&{int}~ c_1,\31~ c_2,\31~\\{col}\K\\{left\_margin};$\6 +$\&{long}~\\{line}\K\T{1};$\7 +$\X6:Open the files $ f_1$ and~$ f_2$, taking their names from the command line +or from the terminal; in case of an error for which no recovery is possible, +call $\\{exit}(\T{1})$\X$\6 +$\X3:Search for first difference, leaving $ c_1\I c_2$ if and only if a +difference was found\X$\6 +$\X4:Report the outcome of the comparison\X$\6 +$\&{return}~\T{0};$\C{ successful completion }\2\6 +$\}$\par +\fi + +\M3. The heart of the program is this simple loop. When we reach the end of +one of the files, the files match if and only if the other file has also +reached its end. For this reason the test $ c_1\E c_2$, which requires +characters to be read from both files, must precede the test for file end; +when only one file ends, it is the former test which breaks the loop. + +\Y\B\4$\X3:Search for first difference, leaving $ c_1\I c_2$ if and only if a +difference was found\X\EQ{}$\6 +\&{while}~$(( c_1\K\\{getc}( f_1))\E( c_2\K\\{getc}( f_2))\W c_1\I\.{EOF})\1$\6 +\&{if}~$( c_1\E\.{'\\n'})\1$\5 +$\{\1$\5 +$\PP\\{line};$\5 +$\\{col}\K\\{left\_margin};\2$\5 +$\}$\5 +\2\&{else}\1\5 +$\PP\\{col};\2$\2\par +\U 2.\fi + +\M4. When the first difference occurs at the end of one of the files, or at the +end of a line, we give a message indicating this fact. + +\Y\B\4$\X4:Report the outcome of the comparison\X\EQ{}$\6 +\&{if}~$( c_1\E c_2)\1$\5 +$\\{printf}(\.{"Files\ match.\\n"});\2$\6 +\&{else}\6 +$\a\{\1\\{printf}(\.{"Files\ differ.\\n"});$\6 +\&{if}~$( c_1\E\.{EOF}\V c_2\E\.{EOF})$\1\6 +$\{\1$\5 +$\\{the\_file}( c_1\E\.{EOF});$\5 +$\\{printf}(\.{"is\ contained\ in\ the\)\ other\ as\ initial\ seg\)ment.\\n"});% +\2$\5 +$\}\2$\6 +\&{else}~\&{if}~$( c_1\E\.{'\\n'}\V c_2\E\.{'\\n'})$\1\6 +$\{\1$\5 +$\\{the\_file}( c_1\E\.{'\\n'});$\5 +$\\{printf}(\.{"has\ a\ shorter\ line\ \)number\ \%ld\ than\ the\ o\)ther.% +\\n"},\31\\{line});\2$\5 +$\}\2$\6 +\&{else}\1\5 +$\\{printf}(\.{"First\ difference\ at\)\ line\ \%ld,\ column\ \%d.\)\\n"},\31% +\\{line},\31\\{col});\2$\2\6 +$\}$\par +\U 2.\fi + +\M5. The function $\\{the\_file}$ starts a sentence about the first or second +file, +depending on its boolean argument. + +\Y\B\4$\X5:Functions\X\EQ{}$\6 +\&{void} $\\{the\_file}$\5 +$(\1\1\1\&{bool}~\\{is\_first}\2\2\2)$\5 +$\{\1$\5 +$\\{printf}(\.{"The\ \%s\ file\ "},\31\\{is\_first}\?\.{"first"}:\.{"second"});% +\2$\5 +$\}$\par +\A 7. +\U 2.\fi + +\M6. There can be be zero, one or two command line arguments. If there are +none, +the user is prompted to supply them, and if there are two these are taken as +the file names, prompting the user only in case a file could not be opened. +In case just one argument is present, the first file is assumed to be the +standard input, which does not have to be opened; in this case however we +will not read a file name from terminal in case the second file cannot be +opened. + +\Y\B\D$\\{read\_mode}$\5 +$\.{"r"}$\par +\Y\B\4$\X6:Open the files $ f_1$ and~$ f_2$, taking their names from the +command line or from the terminal; in case of an error for which no recovery is +possible, call $\\{exit}(\T{1})$\X\EQ{}$\6 +$\MM n;$\5 +$\PP\\{arg};$\C{ ignore ``argument'' 0, which is the program name }\6 +\&{if}~$( n\E\T{0})$\1\6 +$\{\1$\5 +$\\{open\_file}(\m\AND f_1,\31\.{"First\ file\ to\ compa\)re"},\31\NULL);$\5 +$\\{open\_file}(\m\AND f_2,\31\.{"Second\ file\ to\ comp\)are"},\31\NULL);\2$\5 +$\}\2$\6 +\&{else}~\&{if}~$( n\E\T{1})$\6 +$\a\{\1 f_1\K\\{stdin};$\6 +\&{if}~$(( f_2\K\\{fopen}(\m*\\{arg},\31\\{read\_mode}))\E\NULL)\1$\5 +$\{\1$\5 +$\\{printf}(\.{"Could\ not\ open\ file\)\ \%s.\\n"},\31\m*\\{arg});$\5 +$\\{exit}(\T{1});\2$\5 +$\}\2$\2\6 +$\}$\6 +\&{else}~\&{if}~$( n\E\T{2})$\6 +$\a\{\1\\{open\_file}(\m\AND f_1,\31\.{"Give\ another\ first\ \)file"},\31\m*% +\\{arg}\PP);$\5 +$\\{open\_file}(\m\AND f_2,\31\.{"Give\ another\ second\)\ file"},\31\m*% +\\{arg});\2$\6 +$\}$\6 +\&{else}\1\5 +$\{\1$\5 +$\\{printf}(\.{"No\ more\ than\ two\ co\)mmand\ line\ arguments\ \)are\ +allowed.\\n"});$\5 +$\\{exit}(\T{1});\2$\5 +$\}\2$\par +\U 2.\fi + +\M7. The function $\\{open\_file}$ will try to open the file $\\{name}$ for +reading, and +if this fails it will prompt for another file name until it has success. If +called with $\\{name}\E\NULL$, the function starts with prompting right away. + +\Y\B\4$\X5:Functions\X\PE{}$\6 +\&{void} $\\{open\_file}$\5 +$(\1\1\1\&{FILE}~\m*\m* f,\31~\&{char}~\m*\\{prompt},\31~\&{char}~\m*\\{name}\2% +\2\2)$\6 +$\a\{\1\&{char}~\\{buf}[\T{80}];$\7 +\&{if}~$(\\{name}\E\NULL\V(\m* f\K\\{fopen}(\\{name},\31\\{read\_mode}))\E% +\NULL)\1$\6 +\&{do}\1\5 +$\{\1$\5 +$\\{printf}(\.{"\%s:\ "},\31\\{prompt});$\5 +$\\{fflush}(\\{stdout});$\5 +$\\{scanf}(\.{"\%79s"},\31\\{buf});\2$\5 +$\}$\2\6 +\&{while}~$((\m* f\K\\{fopen}(\\{buf},\31\\{read\_mode}))\E\NULL);$\2\2\6 +$\}$\par +\fi + +\N0 8. Index. +\fi + + +\inx +\@m\\{arg}, \[2], 6. +\@h\&{bool}, \[1], 5. +\@m\\{buf}, \[7]. +\@m\\{col}, \[2], 3, 4. +\@m c_1, \[2], 3, 4. +\@m c_2, \[2], 3, 4. +\@m\.{EOF}, 3, 4. +\@m\\{exit}, 6. +\@m f, \[7]. +\@m\\{fflush}, 7. +\@m\\{fopen}, 6, 7. +\@m f_1, \[2], 3, 6. +\@m f_2, \[2], 3, 6. +\@m\\{getc}, 3. +\@m\\{is\_first}, \[5]. +\@m\\{left\_margin}, \[2], 3. +\@m\\{line}, \[2], 3, 4. +\@m\\{main}, \[2]. +\@m n, \[2]. +\@m\\{name}, \[7]. +\@m\\{open\_file}, 6, \[7]. +\@m\\{printf}, 4, 5, 6, 7. +\@m\\{prompt}, \[7]. +\@m\\{read\_mode}, \[6], 7. +\@m\\{scanf}, 7. +\@m\\{stdin}, 6. +\@m\\{stdout}, 7. +\@m\\{the\_file}, 4, \[5]. +\fin +\@$\X5, 7:Functions\X$ +\U 2. +\@$\X6:Open the files $ f_1$ and~$ f_2$, taking their names from the command +line or from the terminal; in case of an error for which no recovery is +possible, call $\\{exit}(\T{1})$\X$ +\U 2. +\@$\X4:Report the outcome of the comparison\X$ +\U 2. +\@$\X3:Search for first difference, leaving $ c_1\I c_2$ if and only if a +difference was found\X$ +\U 2. +\con diff --git a/web/c_cpp/cwebx/ctangle.c b/web/c_cpp/cwebx/ctangle.c new file mode 100644 index 0000000000..2ce302a9ef --- /dev/null +++ b/web/c_cpp/cwebx/ctangle.c @@ -0,0 +1,996 @@ +#define version_string "3.04" +#define banner "This is CTANGLE (Version x" version_string ")\n" +#define max_toks 150000L +#define max_texts 2500 +#define max_files 50 +#define stack_size_max 50 +#define max_indent 1000 +#define variant text +#define line_output flags['l'] +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <limits.h> +#include "common.h" +#define max_bytes 50000L +#define max_modules 1000 +#define max_idents 5000 +#define max_sections 4000 +#define hash_size 353 +#define buf_size 100 +#define longest_name 1000 +#define long_buf_size (buf_size+longest_name) +#define local static +#define array_size(a)((int)(sizeof(a)/sizeof(a[0]))) +#define false (boolean)0 +#define true (boolean)1 +#define ctangle 0 +#define cweave 1 +#define and_and 04 +#define lt_lt 020 +#define gt_gt 021 +#define plus_plus 013 +#define minus_minus 01 +#define minus_gt 031 +#define not_eq 032 +#define lt_eq 034 +#define gt_eq 035 +#define eq_eq 036 +#define or_or 037 +#define find_char()(loc<=limit||get_line()) +#define id_index(p)((sixteen_bits)((p)-id_table)) +#define id_at(i)(&id_table[i]) +#define mod_index(p)((sixteen_bits)((p)-mod_table)) +#define mod_at(i)(&mod_table[i]) +#define name_begin(p)((p)->byte_start) +#define length(p)((int)(strlen(name_begin(p)))) +#define name_end(p)(name_begin(p)+length(p)) +#define complete_name(p)((p)->byte_start[-1]=='\0') +#define print_mod(p) \ +printf(": <%s%s>",name_begin(p),complete_name(p)?"":"...") +#define spotless 0 +#define harmless_message 1 +#define error_message 2 +#define fatal_message 3 +#define mark_harmless() \ +if(history==spotless)history=harmless_message;else +#define mark_error()(history=error_message) +#define overflow(t)fatal("\n! Sorry, %s capacity exceeded",t) +#define confusion(s)fatal("\n! This can't happen: %s",s) +#define show_banner flags['b'] +#define show_happiness flags['h'] +#define show_progress flags['p'] +#define show_stats flags['s'] +#define C_plus_plus flags['+'] +#define compatibility_mode flags['c'] +#define update_terminal()fflush(stdout) +#define new_line()putchar('\n') +#define term_write(string,leng)printf("%.*s",(int)(leng),string) +#define tok_begin(p)(p)->tok_start +#define tok_end(p)((p)+1)->tok_start +#define text_table_end (&text_table[max_texts]) +#define tok_mem_end (&tok_mem[max_toks]) +#define store_byte(c) \ +if(tok_ptr==tok_mem_end)overflow("token");else*tok_ptr++=c +#define macro_flag (text_table_end-1) +#define header_flag text_table_end +#define next_sec(m)((m)->text_link) +#define equiv equiv_or_xref +#define verb_quote 0x2 +#define join 0x3 +#define cur_repl cur_state.repl_field +#define cur_byte cur_state.byte_field +#define cur_end cur_state.end_field +#define cur_sec cur_state.sec_nr_field +#define cur_ind cur_state.indent_field +#define cur_state stack[0] +#define stack_end (&stack[stack_size_max]) +#define stack_empty()(stack_ptr== &stack[0]) +#define C_printf(format,x)fprintf(C_file,format,x) +#define C_putc(c)putc(c,C_file) +#define put_indent() \ +(indent_buffer[ind_i=cur_ind]='\0',C_printf("%s",indent_buffer)) +#define append_white(c) \ +if(ind_i>=max_indent)overflow("indent buffer"); \ +else indent_buffer[ind_i++]=isspace(c)?c:' ' +#define trans_limit 9 +#define trans_of(c)c_trans[(unsigned char)(c)-0x80] +#define translation_exists(c)(trans_of(c)[0]!='\0') +#define comp_op(op) \ +(C_printf(out_state==operator&&line_output?" %s":"%s",op) \ +,out_state=operator) +#define code_of(c)ccode[(unsigned char)(c)] +#define compress(char2,code) \ +if(*loc==char2)return++loc,code +#define preproc_directive 0 +#define section_body 1 +#define report(k,c,m) \ +printf("%lu %ss (out of %lu)\n",(unsigned long)(c),k,(unsigned long)(m)) +/*3:*//*10:*/ +#line 122 "common.inc" +boolean names_match(id_pointer,char*,int,int); +void init_id_name(id_pointer,int); +void init_module_name(mod_pointer);/*:10*//*15:*/ +#line 116 "ctangle.w" +typedef struct text +{eight_bits*tok_start; +struct text*text_link; +}text,*text_pointer;/*:15*//*30:*/ +#line 442 "ctangle.w" +typedef struct +{text_pointer repl_field; +eight_bits*byte_field; +eight_bits*end_field; +sixteen_bits sec_nr_field; +sixteen_bits indent_field; +}output_state,*stack_pointer;/*:30*//*36:*/ +#line 561 "ctangle.w" +enum{identifier=0x80,section_start,section_end,line_mark};/*:36*//*44:*/ +#line 747 "ctangle.w" +enum{no_space,num_or_id,operator,literal};/*:44*//*54:*/ +#line 936 "ctangle.w" +enum +{ignore=0x80, +id_code,constant, +verbatim, +at_sign_image, +join_code, +ord, +control_text, +include_preproc, +char_trans, +format, +definition, +header, +begin_C, +module_name, +new_section +};/*:54*//*24:*/ +#line 301 "ctangle.w" +void phase_two(void); +void output(text_pointer); +void output_preproc_directives(void); +void C_newline(void); +void out_char(eight_bits);/*:24*//*80:*/ +#line 1604 "ctangle.w" +boolean mark_line(void);/*:80*//*89:*/ +#line 1770 "ctangle.w" +void phase_one(void);/*:89*//*16:*/ +#line 136 "ctangle.w" +text text_table[max_texts]; +text_pointer text_ptr= &text_table[0]; +eight_bits tok_mem[max_toks]; +eight_bits*tok_ptr= &tok_mem[0];/*:16*//*20:*/ +#line 197 "ctangle.w" +text_pointer text_root=NULL; +text_pointer*last_unnamed= &text_root;/*:20*//*23:*/ +#line 291 "ctangle.w" +boolean atp_seen=false;/*:23*//*26:*/ +#line 343 "ctangle.w" +mod_pointer output_file[max_files]; +int output_file_count=0;/*:26*//*31:*/ +#line 466 "ctangle.w" +output_state stack[stack_size_max]; +stack_pointer stack_ptr;/*:31*//*32:*/ +#line 486 "ctangle.w" +char indent_buffer[max_indent]; + +sixteen_bits ind_i;/*:32*//*35:*/ +#line 543 "ctangle.w" +int cur_val;/*:35*//*43:*/ +#line 723 "ctangle.w" +eight_bits out_state; +boolean protect;/*:43*//*47:*/ +#line 819 "ctangle.w" +char c_trans[UCHAR_MAX+1-0x80][trans_limit+1];/*:47*//*55:*/ +#line 962 "ctangle.w" +eight_bits ccode[UCHAR_MAX+1];/*:55*//*60:*/ +#line 1100 "ctangle.w" +id_pointer cur_id; +mod_pointer cur_mod;/*:60*//*70:*/ +#line 1342 "ctangle.w" +text_pointer cur_text; +eight_bits next_control;/*:70*/ +#line 79 "ctangle.w" + + +int main(int argc,char* *argv) +{program=ctangle; +line_output=true; +common_init(argc,argv);/*17:*/ +#line 144 "ctangle.w" +tok_begin(text_ptr)=tok_ptr;/*:17*//*48:*/ +#line 827 "ctangle.w" +if(compatibility_mode) +{unsigned char c=UCHAR_MAX; +do sprintf(trans_of(c),"X%X",c);while(--c>=0x80); +}/*:48*//*56:*/ +#line 970 "ctangle.w" +{unsigned char c=0; +do ccode[c]=isspace(c)?new_section:ignore;while(c++ !=UCHAR_MAX); +ccode['v']=ccode['V']='|'; +ccode['=']=verbatim; +ccode['@']=at_sign_image; +ccode['&']=join_code; +ccode['\'']=ord; +ccode['^']=ccode['?']=ccode['.']=ccode[':']=ccode['#']= +ccode['t']=ccode['T']=ccode['q']=ccode['Q']=control_text; +ccode['p']=ccode['P']=include_preproc; +ccode['l']=ccode['L']=char_trans; +ccode['f']=ccode['F']=ccode['s']=ccode['S']=format; +ccode['d']=ccode['D']=definition; +ccode['h']=ccode['H']=header; +ccode['c']=ccode['C']=begin_C; +ccode['<']=ccode['(']=module_name; +ccode['~']=ccode['*']=new_section; +if(compatibility_mode)/*57:*/ +#line 1000 "ctangle.w" +{ccode['h']=ccode['H']=include_preproc; +ccode['p']=ccode['P']=begin_C; +ccode['#']=ignore; +}/*:57*/ +#line 989 "ctangle.w" +}/*:56*/ +#line 86 "ctangle.w" +if(show_banner)print(banner); +phase_one(); +phase_two(); +wrap_up(); +return 0; +}/*:3*//*18:*/ +#line 153 "ctangle.w" +void store_two_bytes(sixteen_bits x) +{if(tok_ptr+2>tok_mem_end)overflow("token"); +*tok_ptr++=x>>8; +*tok_ptr++=x&0xFF; +}/*:18*//*21:*/ +#line 211 "ctangle.w" +boolean names_match(id_pointer x,char*q,int l,int dummy) +{char*p=name_begin(x);while(--l>=0)if(*p++ != *q++)return false; +return*p=='\0'; +} + +void init_module_name(mod_pointer node) +{node->equiv=NULL;} + +void init_id_name(id_pointer dummy,int ilk){}/*:21*//*25:*/ +#line 314 "ctangle.w" +void phase_two(void) +{phase=2; +if(text_root==NULL&&output_file_count==0) +{print("\n! No program text was specified.");mark_harmless();} + +else +{if(show_progress) +{print("\nWriting the output file%s" +,(text_root!=NULL)+output_file_count>1?"s":""); +if(text_root!=NULL)printf(" (%s):",C_file_name); +update_terminal(); +} +if(text_root==NULL)C_file=NULL; +else +{open_output_file();cur_line=1; +if(!atp_seen)output_preproc_directives(); +output(text_root); +}/*27:*/ +#line 355 "ctangle.w" +{int i; +char output_file_name[longest_name+1]; +for(i=0;i<output_file_count;i++) +{mod_pointer output_module=output_file[i];/*28:*/ +#line 384 "ctangle.w" +{char*p=output_file_name,*q=name_begin(output_module); +while(*q!='\0') +if((*p++= *q++)=='@') +if(*q++ !='@') +{print("\n! Illegal control code in file name"); + +print_mod(output_module);err_print(""); +} +*p='\0'; +}/*:28*/ +#line 360 "ctangle.w" +if(C_file!=NULL)fclose(C_file); +if(output_module->equiv==NULL) +{print("\n! Module not present"); + +print_mod(output_module);err_print(""); +} +else if((C_file=fopen(output_file_name,"w"))==NULL) +{print("\n! Cannot open \"%s\" as output file",output_file_name); + +err_print(""); +} +else +{if(show_progress)print("\n(%s):",output_file_name); +cur_line=1; +output(output_module->equiv); +} +} +}/*:27*/ +#line 333 "ctangle.w" +print_progress("\nDone.\n"); +} +}/*:25*//*33:*/ +#line 499 "ctangle.w" +void push_level(mod_pointer p) +{if(stack_ptr==stack_end)overflow("output stack"); +*stack_ptr++=cur_state; +cur_repl=p->equiv; +cur_byte=tok_begin(cur_repl);cur_end=tok_end(cur_repl); +cur_ind=ind_i; +}/*:33*//*34:*/ +#line 516 "ctangle.w" +void continue_or_pop_level(void) +{if(cur_repl->text_link!=NULL) + +{cur_repl=next_sec(cur_repl); +cur_byte=tok_begin(cur_repl);cur_end=tok_end(cur_repl); +} +else if(--stack_ptr> &stack[0])cur_state= *stack_ptr; + +ind_i=cur_ind; +}/*:34*//*37:*/ +#line 576 "ctangle.w" +void output(text_pointer repl) +{stack_ptr= &stack[1]; +cur_repl=repl;cur_byte=tok_begin(cur_repl);cur_end=tok_end(cur_repl); +cur_ind=ind_i=0; +do +if(cur_byte==cur_end) +{cur_val=cur_sec; +continue_or_pop_level(); +out_char(section_end); +} +else/*38:*/ +#line 601 "ctangle.w" +{int a= *cur_byte++; +if(a<0x80)out_char(a); +else if(a>=0xF8) +if(a<0xFA)out_char(a==0xF8? *cur_byte++:line_mark); +else{C_newline();output_preproc_directives();} +else +{cur_val=(((a-=0x80)%0x28)<<8)+ *cur_byte++; +switch(a/0x28) +{case 0:out_char(identifier);break; +case 1:/*39:*/ +#line 623 "ctangle.w" +{mod_pointer mod_name=mod_at(cur_val); +if(mod_name->equiv!=NULL)push_level(mod_name); +else +{print("\n! Module not present");print_mod(mod_name);err_print(""); + +} +}/*:39*/ +#line 610 "ctangle.w" +break; +case 2:cur_sec=cur_val;out_char(section_start); + +} +} +}/*:38*/ +#line 589 "ctangle.w" +while(!stack_empty()); +C_newline(); +}/*:37*//*40:*/ +#line 644 "ctangle.w" +void output_preproc_directives(void) +{text_pointer repl,l; +eight_bits*p,*end; +protect=true; +for(repl= &text_table[0];repl<text_ptr;repl++) +if((l=repl->text_link)==macro_flag||l==header_flag) +{p=tok_begin(repl);end=tok_end(repl); +C_printf("#%se ",l==macro_flag?"defin":"includ"); +out_state=no_space; +while(p<end)/*41:*/ +#line 670 "ctangle.w" +{int a= *p++; +if(a<0x80)out_char(a); +else if(a>=0xF8) +if(a==0xF8)out_char(*p++); +else confusion("`@p' within macro"); +else +{cur_val=(((a-=0x80)%0x28)<<8)+ *p++; +if(a<0x28)out_char(identifier); +else confusion("module within macro"); +} +}/*:41*/ +#line 656 "ctangle.w" +C_newline(); +} +protect=false; +}/*:40*//*42:*/ +#line 692 "ctangle.w" +void C_newline(void) +{C_putc('\n'); +if(!line_output)put_indent(); +if(cur_line%100==0&&show_progress) +{if(cur_line%500!=0)print("."); +else print(cur_line%2500==0?"%u\n":"%u",cur_line); +update_terminal(); +} +++cur_line; +}/*:42*//*45:*/ +#line 764 "ctangle.w" +void out_char(eight_bits c) +{if(out_state==literal) +if(c==verb_quote)out_state=num_or_id; +else C_putc(c); +else if(isalnum(c)&&c<0x80||c=='_') + +{if(out_state==num_or_id&&line_output)C_putc(' '); +C_putc(c);out_state=num_or_id; +if(!line_output)append_white(c); +} +else switch(c) +{case verb_quote: +if(out_state==num_or_id&&line_output)C_putc(' '); +out_state=literal;break; +case join:out_state=no_space;break; +case '\n':/*46:*/ +#line 803 "ctangle.w" +{if(protect){C_putc(' ');C_putc('\\');} +C_newline(); +if(out_state!=literal)out_state=no_space; +}/*:46*/ +#line 779 "ctangle.w" +break; +case identifier:/*49:*/ +#line 838 "ctangle.w" +{char*p=name_begin(id_at(cur_val));int l=0; +if(out_state==num_or_id&&line_output)C_putc(' '); +do +if((unsigned char)(*p)<0x80){C_putc(*p);++l;} +else +{char*q=trans_of(*p);do{C_putc(*q);++l;}while(* ++q!='\0');} +while(* ++p!='\0'); +out_state=num_or_id; +if(!line_output)do append_white(' ');while(--l>0); +}/*:49*/ +#line 780 "ctangle.w" +break; +case section_start: +if(line_output)C_printf("/*%d:*/",cur_val);else C_newline(); +out_state=no_space;break; +case section_end: +if(line_output)C_printf("/*:%d*/",cur_val);else C_newline(); +out_state=no_space;break; +case line_mark:/*50:*/ +#line 854 "ctangle.w" +{sixteen_bits a; +a=(*cur_byte++)<<8;a+= *cur_byte++; +C_newline();C_printf("#line %u \"",a); +a=(*cur_byte++)<<8;a+= *cur_byte++; +C_printf("%s\"",name_begin(id_at(a))); +C_newline();out_state=no_space; +}/*:50*/ +#line 787 "ctangle.w" +break;/*51:*/ +#line 877 "ctangle.w" +case '+':case '-':case '*':case '/':case '%':case '?': +case '<':case '>':case '&':case '|': +if(out_state==operator&&line_output)C_putc(' '); +case '=':C_putc(c);out_state=operator;break;/*:51*//*52:*/ +#line 892 "ctangle.w" +case plus_plus:comp_op("++");break; +case minus_minus:comp_op("--");break; +case minus_gt:comp_op("->");break; +case gt_gt:comp_op(">>");break; +case eq_eq:comp_op("==");break; +case lt_lt:comp_op("<<");break; +case gt_eq:comp_op(">=");break; +case lt_eq:comp_op("<=");break; +case not_eq:comp_op("!=");break; +case and_and:comp_op("&&");break; +case or_or:comp_op("||");break;/*:52*/ +#line 790 "ctangle.w" +default:C_putc(c);out_state=no_space; +if(!line_output)append_white(c); +} +}/*:45*//*58:*/ +#line 1012 "ctangle.w" +eight_bits skip_ahead(void) +{eight_bits c; +while(find_char()) +{limit[1]='@'; +while(*loc++ !='@'){} +if(loc<=limit&&(c=code_of(*loc++))!=ignore)return c; +} +return new_section; +}/*:58*//*59:*/ +#line 1051 "ctangle.w" +boolean skip_comment(boolean one_liner) +{char c; +do +{if(loc>=limit) +if(one_liner)return false; +else if(get_line())return true; +else +{err_print("! Input ended in mid-comment");return false;} + +if((c= *loc++)=='/'&& *loc=='*') +err_print("! `/*' inside comment, did you forget `*/' before? "); + +if(c=='@') +{eight_bits cc=code_of(*loc++); +if(cc==new_section) +{err_print("! Section ended in mid-comment");loc-=2;return false;} + +if(cc==module_name){/*68:*/ +#line 1291 "ctangle.w" +{boolean file_module=loc[-1]=='('; + +cur_mod=get_module_name(); +if(file_module&&cur_mod!=NULL)/*29:*/ +#line 398 "ctangle.w" +{int i=0; +while(i<output_file_count) +if(output_file[i]==cur_mod)break;else++i; +if(i==output_file_count) +if(output_file_count==max_files)overflow("output files"); +else output_file[output_file_count++]=cur_mod; +}/*:29*/ +#line 1296 "ctangle.w" +}/*:68*/ +#line 1068 "ctangle.w" +continue;} +} +if(!line_output) +{store_byte(c); +if(c=='@'&&(c=loc[-1])!='@')store_byte(c); +} +}while(c!='*'|| *loc!='/'||one_liner); +++loc;if(!line_output)store_byte('/'); +return false; +}/*:59*//*61:*/ +#line 1115 "ctangle.w" +eight_bits get_next(void) +{static boolean preprocessing=false; +static boolean comment_continues=false; +eight_bits c; +restart:/*62:*/ +#line 1152 "ctangle.w" +{if(loc>=limit) +{if(preprocessing&&limit>buffer&&limit[-1]!='\\') +preprocessing=false; +return get_line()?'\n':new_section; +} +if(comment_continues +||(c= *loc++)=='/'&&(*loc=='*'||C_plus_plus&& *loc=='/'))/*63:*/ +#line 1184 "ctangle.w" +{boolean one_liner=false; +if(!comment_continues) +{if(!line_output){store_byte('/');store_byte(*loc);} +one_liner= *loc++ =='/'; + +} +else if(preprocessing) +{print("\nWarning: Multi-line comment in preprocessor line"); + +preprocessing=false;mark_harmless(); +} +if(comment_continues=skip_comment(one_liner)) + +return '\n'; +else goto restart; +}/*:63*/ +#line 1162 "ctangle.w" +if(isspace(c)) +if(line_output) +if(preprocessing)return ' ';else goto restart; + +else return c; + +if(c=='#'&&loc==buffer+1)preprocessing=true; +}/*:62*/ +#line 1123 "ctangle.w" +if(c=='L'&&(*loc=='\''|| *loc=='\"')) +{get_string();return constant;} +if(c<0x80?isalpha(c)||c=='_':translation_exists(c)) +{/*65:*/ +#line 1233 "ctangle.w" +{id_first= --loc; +do c= * ++loc; +while(c<0x80?isalnum(c)||c=='_':translation_exists(c)); +cur_id= +loc==id_first+1&&(eight_bits)(*id_first)<0x80 +?NULL:id_lookup(id_first,loc,0); +}/*:65*/ +#line 1126 "ctangle.w" +return id_code;} +if(c>=0x80){err_print("! Illegal 8-bit character");goto restart;} + +if(isdigit(c)||c=='.'&&isdigit((eight_bits)*loc)) +{/*66:*/ +#line 1250 "ctangle.w" +{if(*(id_first=loc-1)=='0'&&tolower((eight_bits)*loc)=='x') + +do c= * ++loc;while(isxdigit(c)); +else +{while(isdigit(c))c= *loc++; +if(c=='.')do c= *loc++;while(isdigit(c)); +if(tolower(c)=='e') +{if((c= *loc)=='+'||c=='-')c= * ++loc; +while(isdigit(c))c= * ++loc; +} +else--loc; +} +while(isalpha(c))c= * ++loc; + +id_loc=loc; +}/*:66*/ +#line 1130 "ctangle.w" +return constant;} +switch(c) +{case '\'':case '"':get_string();return constant; +case '@':/*67:*/ +#line 1275 "ctangle.w" +{eight_bits cc=code_of(*loc++); +switch(cc) +{case ignore:goto restart; +case control_text:get_control_text();goto restart; +case verbatim:if(get_control_text())goto restart;else break; +case ord:/*69:*/ +#line 1302 "ctangle.w" +id_first=loc; +while(*loc!='\'') +{if(*loc++ =='\\')loc++; +if(loc>=limit){err_print("! ASCII constant didn't end");break;} + +} +id_loc=loc++;/*:69*/ +#line 1280 "ctangle.w" +break; +case module_name:/*68:*/ +#line 1291 "ctangle.w" +{boolean file_module=loc[-1]=='('; + +cur_mod=get_module_name(); +if(file_module&&cur_mod!=NULL)/*29:*/ +#line 398 "ctangle.w" +{int i=0; +while(i<output_file_count) +if(output_file[i]==cur_mod)break;else++i; +if(i==output_file_count) +if(output_file_count==max_files)overflow("output files"); +else output_file[output_file_count++]=cur_mod; +}/*:29*/ +#line 1296 "ctangle.w" +}/*:68*/ +#line 1283 "ctangle.w" +} +return cc; +}/*:67*//*64:*/ +#line 1217 "ctangle.w" +case '+':compress('+',plus_plus);break; +case '-':compress('-',minus_minus);compress('>',minus_gt);break; +case '=':compress('=',eq_eq);break; +case '>':compress('=',gt_eq);compress('>',gt_gt);break; +case '<':compress('=',lt_eq);compress('<',lt_lt);break; +case '&':compress('&',and_and);break; +case '|':compress('|',or_or);break; +case '!':compress('=',not_eq);break;/*:64*/ +#line 1137 "ctangle.w" +} +return c; +}/*:61*//*71:*/ +#line 1367 "ctangle.w" +void scan_repl(eight_bits context) +{eight_bits a; +eight_bits*keep=tok_ptr; +int brace_level=0,par_level=0; +if(context==section_body)/*81:*/ +#line 1612 "ctangle.w" +{if(mark_line()){a=new_section;goto done;}}/*:81*/ +#line 1373 "ctangle.w" +do/*72:*/ +#line 1388 "ctangle.w" +{switch(a=get_next()) +{/*75:*/ +#line 1459 "ctangle.w" +case id_code:/*76:*/ +#line 1496 "ctangle.w" +if(cur_id==NULL)store_byte(*id_first); +else store_two_bytes(0x8000+id_index(cur_id));/*:76*/ +#line 1460 "ctangle.w" +keep=tok_ptr;continue; +case module_name:if(context==preproc_directive)goto done; +if(cur_mod!=NULL) +{sixteen_bits n=mod_index(cur_mod);/*77:*/ +#line 1508 "ctangle.w" +{char*p=loc; +while(*p==' '&&p<limit)++p; +if(*p=='+')++p; +if(*p=='=') +err_print +("! Illegal defining occurrence of module name; did you forget `@ '?"); + +}/*:77*/ +#line 1465 "ctangle.w" +if(line_output)tok_ptr=keep; +store_two_bytes(0xA800+n);keep=tok_ptr;/*81:*/ +#line 1612 "ctangle.w" +{if(mark_line()){a=new_section;goto done;}}/*:81*/ +#line 1469 "ctangle.w" + +} +continue; +case constant:case verbatim:/*78:*/ +#line 1530 "ctangle.w" +if(id_loc==id_first+1)store_byte(*id_first); +else +{store_byte(verb_quote); +do +if(*id_first==verb_quote) +{++id_first;store_byte('\\');store_byte('0'+(verb_quote>>6)); +store_byte('0'+((verb_quote>>3)&7));store_byte('0'+(verb_quote&7)); +} +else +{if((eight_bits)(*id_first)>=0x80)store_byte(0xF8); + +store_byte(*id_first++); +} +while(id_first<id_loc); +store_byte(verb_quote); +if(context==section_body&&print_where)/*81:*/ +#line 1612 "ctangle.w" +{if(mark_line()){a=new_section;goto done;}}/*:81*/ +#line 1547 "ctangle.w" +}/*:78*/ +#line 1473 "ctangle.w" +keep=tok_ptr; +continue; +case ord:/*79:*/ +#line 1559 "ctangle.w" +{int c= *id_first++; +if(c=='@'&& *id_first++ !='@') +{err_print("! Double `@' should be used in ASCII constant"); + +--id_first; +} +if(c=='\\') +{c= *id_first++; +switch(c) +{case 't':c='\011';break; +case 'n':c='\012';break; +case 'b':c='\010';break; +case 'f':c='\014';break; +case 'v':c='\013';break; +case 'r':c='\015';break; +case '0':c='\0';break; +case '\\':c='\134';break; +case '\'':c='\047';break; +case '\"':c='\042';break; +default:err_print("! Unrecognised escape sequence"); + +} +} +else +{ +} +if(id_first!=id_loc) +if(id_loc>id_first) +err_print("! ASCII constant should be single character"); + +else{err_print("! Empty ASCII constant");c=0;} + +store_byte(verb_quote); +if(c>=100)store_byte('0'+c/100);if(c>=10)store_byte('0'+(c/10)%10); +store_byte('0'+c%10); +store_byte(verb_quote); +}/*:79*/ +#line 1476 "ctangle.w" +keep=tok_ptr;continue; +case include_preproc: +if(context==preproc_directive) +err_print("! `@p' is forbidden in preprocessor directive"); + +else +{if(line_output)tok_ptr=keep; +store_byte(0xFA);atp_seen=true;keep=tok_ptr;/*81:*/ +#line 1612 "ctangle.w" +{if(mark_line()){a=new_section;goto done;}}/*:81*/ +#line 1485 "ctangle.w" +} +continue; +case char_trans: +err_print("! `@l' is only allowed in limbo"); + +continue;/*:75*/ +#line 1394 "ctangle.w" +case format:case definition:case header:case begin_C: +if(context==preproc_directive)goto done; +err_print +("! `@f', `@d', `@h', and `@c' are ignored in section body"); + +continue; +case new_section:goto done;/*73:*/ +#line 1420 "ctangle.w" +case '(':++par_level;break; +case ')': +if(par_level<=0) +{err_print("! Unmatched closing parenthesis");continue;} + +--par_level;break; +case '{':++brace_level;break; +case '}': +if(brace_level<=0) +{err_print("! Unmatched closing brace");continue;} +--brace_level;break;/*:73*/ +#line 1402 "ctangle.w" +case '\n':store_byte('\n'); +if(context==section_body&&print_where) +{tok_ptr=keep;/*81:*/ +#line 1612 "ctangle.w" +{if(mark_line()){a=new_section;goto done;}}/*:81*/ +#line 1406 "ctangle.w" +} +continue; +case join_code:a=join; +} +store_byte(a);keep=tok_ptr; +}/*:72*/ +#line 1375 "ctangle.w" +while(true); +done:tok_ptr=keep; +next_control=a; +if(par_level>0||brace_level>0)/*74:*/ +#line 1439 "ctangle.w" +{char*p,*s;int l; +if(par_level>0)l=par_level,s="parenthes",p=l>1?"es":"is"; +else l=brace_level,s="brace",p=l>1?"s":""; +print("\n! There %s %d unclosed %s%s" +,par_level+brace_level>1?"are":"is",l,s,p); + +if(par_level>0&&brace_level>0) +print(" and %d unclosed brace%s" +,brace_level,brace_level>1?"s":""); +print(" in the previous "); +err_print(context==preproc_directive?"macro":"section"); +while(--par_level>=0)store_byte(')'); +while(--brace_level>=0)store_byte('}'); +}/*:74*//*19:*/ +#line 166 "ctangle.w" +{cur_text=text_ptr++; +if(text_ptr>=text_table_end)overflow("text"); +tok_begin(text_ptr)=tok_ptr; +}/*:19*/ +#line 1381 "ctangle.w" +}/*:71*//*82:*/ +#line 1627 "ctangle.w" +boolean mark_line(void) +{while(loc>=limit) +if(!get_line())return true; +print_where=false; +if(line_output) +{store_byte(0xF9); +id_first=changing?change.name:cur_file_name; +store_two_bytes(changing?change_line:cur_line); +store_two_bytes(id_index(id_lookup(id_first,NULL,0))); +} +return false; +}/*:82*//*83:*/ +#line 1648 "ctangle.w" +void scan_section(void) +{++section_count; +if(loc[-1]=='*')print_section_progress();/*84:*/ +#line 1668 "ctangle.w" +{next_control=ignore; +do +{if(next_control<definition) +{if((next_control=skip_ahead())==module_name) +{loc-=2;get_next();} + +} +else if(next_control==definition) +{/*85:*/ +#line 1694 "ctangle.w" +{do next_control=get_next(); +while(line_output?next_control=='\n' +:next_control<0x80&&isspace(next_control)); +if(next_control!=id_code) +{err_print("! Macro definition ignored, must start with identifier"); + +continue; +}/*76:*/ +#line 1496 "ctangle.w" +if(cur_id==NULL)store_byte(*id_first); +else store_two_bytes(0x8000+id_index(cur_id));/*:76*/ +#line 1703 "ctangle.w" +if(isspace((eight_bits)*loc))store_byte(' '); + +}/*:85*/ +#line 1677 "ctangle.w" +scan_repl(preproc_directive); +cur_text->text_link=macro_flag; +} +else +{scan_repl(preproc_directive);cur_text->text_link=header_flag;} +if(next_control==module_name)/*86:*/ +#line 1718 "ctangle.w" +{eight_bits t=get_next(); +if(t=='+')t=get_next(); +if(t!='='&&t!=eq_eq) +{next_control=ignore; +if(t!='|'&&!compatibility_mode) +err_print("! `=' sign missing, module name ignored"); + +} +}/*:86*/ +#line 1684 "ctangle.w" +}while(next_control<begin_C); +}/*:84*//*87:*/ +#line 1733 "ctangle.w" +{mod_pointer p; +switch(next_control) +{default:return; +case begin_C:p=NULL;break; +case module_name:p=cur_mod; +} +store_two_bytes(0xD000+section_count); +scan_repl(section_body);/*88:*/ +#line 1753 "ctangle.w" +{if(p==NULL) +{*last_unnamed=cur_text;last_unnamed= &cur_text->text_link;} +else +{text_pointer*q= &p->equiv; +while(*q!=NULL)q= &(*q)->text_link; +*q=cur_text; +} +cur_text->text_link=NULL; + +}/*:88*/ +#line 1744 "ctangle.w" +}/*:87*/ +#line 1653 "ctangle.w" +}/*:83*//*90:*/ +#line 1777 "ctangle.w" +void phase_one(void) +{phase=1;section_count=0;reset_input(); +while((next_control=skip_ahead())!=new_section) +if(next_control==char_trans)/*91:*/ +#line 1791 "ctangle.w" +{int c; +while(loc<limit&&isspace((eight_bits)*loc))++loc; +if(!(isxdigit((eight_bits)loc[0])&& +isxdigit((eight_bits)loc[1])&& +isspace((eight_bits)loc[2]))) +err_print("! Two-digit hex number and space should follow `@l'"); + +else if(sscanf(loc,"%x",&c),c<0x80) +err_print("! You cannot translate characters < 0x80"); + +else +{char*p=trans_of(c);int i=0; +loc+=3; +while(find_char()&&isspace((eight_bits)*loc))++loc; +if(!input_has_ended) +while(isalnum((eight_bits)*loc)|| *loc=='_') +if(++i<=trans_limit)*p++= *loc++;else break; +if(i>0)*p='\0';/*92:*/ +#line 1824 "ctangle.w" +if(i==0)err_print("! Translation string absent after `@l'"); + +else if(i>trans_limit)err_print("! Translation string too long"); +else if(!isspace((eight_bits)*loc)) +err_print("! Translation string not terminated by space");/*:92*/ +#line 1810 "ctangle.w" +} +}/*:91*/ +#line 1781 "ctangle.w" +while(!input_has_ended)scan_section(); +check_complete(); +}/*:90*//*93:*/ +#line 1838 "ctangle.w" +#ifdef STAT +void print_stats(void) +{print("\nMemory usage statistics:\n"); +report("identifier",id_index(id_ptr),max_idents); +report("module name",mod_index(mod_ptr),max_modules); +report("byte",byte_ptr-byte_mem,max_bytes); +report("replacement text",text_ptr-text_table,max_texts); +report("token",tok_ptr-tok_mem,max_toks); +} +#endif/*:93*/ diff --git a/web/c_cpp/cwebx/ctangle.w b/web/c_cpp/cwebx/ctangle.w new file mode 100644 index 0000000000..5c0eb7a813 --- /dev/null +++ b/web/c_cpp/cwebx/ctangle.w @@ -0,0 +1,1856 @@ +% This file is part of CWEBx. +% This program by Marc van Leeuwen based on earlier versions by +% D. E. Knuth., Silvio Levy and Frank Jensen. +% It is distributed WITHOUT ANY WARRANTY, express or implied. +% CWEB (Revision: 2.0) % Don Knuth, July 1990 +% Version 3.x, Marc van Leeuwen, December 1993 +% CWEBx 2+1.0, Marc van Leeuwen, August 1994 +% CWEBx 3.0, Marc van Leeuwen, Januari 1995 +% CWEBx 3.02, Marc van Leeuwen, April 1996 +% CWEBx 3.02a, Marc van Leeuwen, September 1996 +% CWEBx 3.03, Marc van Leeuwen, January 1998 (ctangle unchanged) + +% Copyright (C) 1987,1990 Silvio Levy and Donald E. Knuth +% Copyright 1994 Marc A. A. van Leeuwen + +% 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 distributed under the terms of a +% permission notice identical to this one. + +\def\me.{CTANGLE} \def\myroots{\.{TANGLE}} + +% Here is TeX material that gets inserted after \input cwebxmac +\def\ASCII.{\caps{ASCII}} + +@i intro.inc % Here is some text that matches the start of CWEAVE + +@d banner "This is CTANGLE (Version x"@+version_string@+")\n" + +@ The following parameters are specific to \.{CTANGLE}; those which are +common to \.{CTANGLE} and \.{CWEAVE} are defined in the file \.{common.inc} +and appear below. Some of these values have been decreased with respect to +their earlier values which were sufficient in the original \.{WEB} to handle +\TeX; a motivation is given at the common declarations. The macro |variant| +is used in the include file \.{common.h}, and should therefore be defined +early; it refers to the |struct text| that will be declared later. + +@d max_toks 150000L /* number of bytes in compressed \Cee~code */ +@d max_texts 2500 /* number of replacement texts, must be less than 10240 */ +@d max_files 50 /* number of auxiliary output files */ +@d stack_size_max 50 /* module nesting level during output */ +@d max_indent 1000 /* size of buffer for save indentation characters */ +@) +@d variant @;text + +@ The program is built from two compilation units, one with source file +\.{common.w}, which contains a collection of routines and data shared +between \.{CTANGLE} and \.{CWEAVE}, and a second with source file +\.{ctangle.w} containing all code specific to \.{\me.}, and whose +typeset version you are now reading. All compilation units of the \.{CWEB} +system incorporate the file \.{common.inc} containing common declarations. + +\.{\me.} has a fairly straightforward outline. It operates in +two phases: first it reads the source file, saving the \Cee~code in +compressed form; then outputs the code, after shuffling it around. +It can optionally be compiled with the preprocessor symbol |STAT| defined, +in which case it will keep track of how much of \.{\me.}'s resources +were actually used. One command line argument is specifically designated to +modify the behaviour of \.{\me.}. Normally \.{\me.} will produce +machine-oriented output that supplies \&{\#line} directives (to allow +compilers and debuggers to locate the original source lines of statements), +while preserving line breaks but otherwise ignoring the lay-out and comments +in the source text. When the flag `\.{-l}' is supplied on the command line +however, the output cannot be traced back easily to the source lines, but +will be easier for humans to read: no \&{\#line} directives are produced, +but original lay-out and comments are preserved as well as possible. The +flag controlling this behaviour is called |line_output|; in the standard +mode of operation we have |line_output==true|. + +@d line_output flags['l'] +@c +@< Function prototypes used but not defined in the shared code @>@; +@< Typedef and enumeration declarations @>@; +@< Prototypes @>@; +@< Global variables @>@; + +int main (int argc, char** argv) +{ program=ctangle; + line_output=true; + common_init(argc,argv); + @<Set initial values@> + if (show_banner) print(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 */ + wrap_up(); /* and exit gracefully */ + return 0; +} + +@i common.inc + + +@* Data structures exclusive to {\tt \me.}. +The basic mission of \.{\me.} is a relatively simple one, namely to +separate the \Cee~code from the commentary, to forget about the latter, and +to output the former almost verbatim, but plugging in the program text +corresponding to a module each time a reference is made. To this end the +bodies of all the sections are collected during Phase~I, and the proper +links are laid, after which the code is linearised for output in a +straightforward manner during Phase~II. + +The \Cee~code itself will be stored as a sequence of bytes (more precisely, +of |eight_bits|) in a single large array |tok_mem|, much in the same way as +the characters for identifiers and module names are stored in |byte_mem|; +the encoding used will be described below. The individual sections are +represented by structures of type |text|, which contain pointers to the +|tok_mem| array as well as links by which they may be connected into lists; +the same structures are also used to represent the replacement texts of +macros. All |text| structures reside in an array |text_table|, which is +comparable to |id_table| and |mod_table|. + +@<Typedef...@>= +typedef struct text +{ eight_bits* tok_start; /* pointer into |tok_mem| */ + struct text* text_link; /* relates replacement texts */ +} text,* text_pointer; + +@ Since all allocation is during Phase~I and all deallocation during +Phase~II, there is no reason for reclaiming memory when it is no longer +needed. Hence allocation is strictly linear, and a single pointer into +|tok_mem| for each |text| suffices, since a texts ends where the next one +starts. The first position of |tok_mem| that is unoccupied by a replacement +text is called |tok_ptr|, and the first unused location of |text_table| is +called |text_ptr|. Whenever we are not in the process of adding tokens to +|tok_mem|, we have the identity |tok_begin(text_ptr)==tok_ptr|. + +@d tok_begin(p) (p)->tok_start +@d tok_end(p) ((p)+1)->tok_start +@d text_table_end (&text_table[max_texts]) +@d tok_mem_end (&tok_mem[max_toks]) + +@<Glob...@>= +text text_table[max_texts]; +text_pointer text_ptr=&text_table[0]; /* first unused position in |text_table| */ +eight_bits tok_mem[max_toks]; +eight_bits *tok_ptr=&tok_mem[0]; /* first unused position in |tok_mem| */ + +@ Invariants must be initialised. + +@<Set init...@>= +tok_begin(text_ptr)=tok_ptr; + +@ The following macro and function are used to enter one- and two-byte +tokens into |tok_mem| when a replacement text is being generated. + +@d store_byte(c) @+ +if (tok_ptr==tok_mem_end) overflow("token"); @+ else *tok_ptr++=c@; + +@c +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 & 0xFF; /* store low byte */ +} + +@ When by successive calls of |store_byte| and |store_two_bytes| a complete +replacement text has been stored, the following code makes it into an +official text object by advancing |text_ptr| and storing the starting +location of the next text to be generated into the |text_table| array. + +@< Wrap up the accumulated bytes into a completed text, + pointed to by |cur_text| @>= +{ cur_text=text_ptr++; /* consolidate the replacement text */ + if (text_ptr>=text_table_end) overflow("text"); + tok_begin(text_ptr)=tok_ptr; /* mark end of replacement text */ +} + +@ The |text_link| field links a section to a possible continuation of it (the +next section with the same name, or for unnamed sections the next unnamed +section). The set of all sections linked together in such a way will be termed +a `module'. Apart from the value |NULL| used to indicate the end of such a +list, we need two other distinguished values: |macro_flag|, which indicates +that the text is a macro replacement text rather than a section body, and +|header_flag| similarly indicating that the text is the (quoted) file name +of a header file that is included. By a fortunate coincidence, there are two +unused pointer values near the end of |text_table|: since the macro |tok_end| +should work for all pointers to a replacement text, the last entry of +|text_table| is never used to store a replacement text; its address can be +used as one reserved pointer value, and the definition of~\Cee\ generously +offers us the address of the non-existent entry following it as another +valid pointer value. The pointer to the first section of the unnamed module +appears in |text_root|, and the address of the final link of this module is +recorded in |last_unnamed|. The named modules are accessed by the extra +pointer in the |mod_info| structure for a module name, which is a +|text_pointer| pointing to the body of the first section of that module; +in~\.{\me.} this field will be called |equiv|. + +@d macro_flag (text_table_end-1) /* address of unused entry */ +@d header_flag text_table_end /* address just beyond |text_table| */ +@d next_sec(m) ((m)->text_link) /* next section of the same module, if any */ +@d equiv equiv_or_xref /* info corresponding to names */ + +@<Glob...@>= +text_pointer text_root=NULL; +text_pointer* last_unnamed=&text_root; /* where to link new unnamed sections */ + +@ Here are the functions specific to \.{\me.}, which are used by the +common lookup routines. The function |names_match| decides whether a name of +length~|l| starting at position |q| equals the identifier represented +by~|x|. The parameter |dummy| is present only for compatibility with +\.{CWEAVE}, where it contains the |ilk| code. The common lookup routines +refer to separate routines |init_module_name| and |init_id_name| when the +data structure grows. Actually |init_id_name| is called only when +|program==cweave|, but we need to declare a dummy version so that the linker +won't complain about its absence. + +@c +boolean names_match (id_pointer x,char* q,int l,int dummy) +{@; char* p=name_begin(x); while (--l>=0) if (*p++!=*q++) return false; + return *p=='\0'; +} + +void init_module_name(mod_pointer node) +@+{@; node->equiv=NULL; } + +void init_id_name (id_pointer dummy,int ilk) @+ {} + + +@* Tokens. +Clearly, \.{\me.} must hold all the code for a complete program file at +the end of Phase~I (not counting the possible multiple use of some modules, +which will probably not make a great difference), and so it is worth while +to use a compact representation for the internal storage of the \Cee~code. +Using full-fledged data compression techniques would probably be overdoing +things a bit, but a few simple methods will make a great difference. There +are likely to be many identical occurrences of identifiers and keywords of +the language, so it is efficient to collect them into a table and replace +them in the internal format by references to the table; module names have +to be entered in a table anyway for the purpose of associating occurrences +of the same name with each other. Furthermore white space and comments can +be removed upon storing the texts; any necessary white space can be +reinserted during the output process (but note that newlines must be +represented internally, in order to be able to match output lines with +input lines). + +@ These compressed texts representing \Cee~code, while physically +represented as a sequence of eight-bit bytes in |tok_mem|, logically consist +of streams of `tokens', some of which occupy two or more consecutive byte +positions, while others take just one byte. If the first byte is |a| and, +in case |a>=0x80|, the next byte is~|b|, then the interpretation of the +token is as follows. + +\Y +\item{$\bullet$} |0<=a<0x80|: the token represents the character~|a|; +\item{$\bullet$} |0x80<=a<0xA8|: the two-byte token represents the + identifier with name |id_at((a-0x80)*@t$2^8$@>+b)|; +\item{$\bullet$} |0xA8<=a<0xD0|: the two-byte token represents the module + with name |mod_at((a-0xA8)*@t$2^8$@>+b)|; +\item{$\bullet$} |0xD0<=a<0xF8|: this two-byte token marks the beginning of + (a part of) the repacement text for the current module, defined in section + number |(a-0xD0)*@t$2^8$@>+b|; +\item{$\bullet$} |a==0xF8|: the two-byte token represents the (8-bit) + character~|b|. +\item{$\bullet$} |a==0xF9|: the token consists of 5 bytes, say |a|,~|b|, + |c|, |d| and~|e|, and represents a \&{\#line} directive for line number + |b*@t$2^8$@>+c| in the file |id_at(d*@t$2^8$@>+e)|. +\item{$\bullet$} |a==0xFA|: the token consists of a single byte, and + represents the location, indicated by the \:p control codes in the source + file, where the preprocessor directives produced by \:d and \:h will be + included in the output; this location will be marked by the presence of + the virtual module called `|@p|' in the typeset output. +\Y + +Any \Cee~token that consists of a single 7-bit character is represented by +that character itself; in particular, a single-character identifier like +`|x|' will be a one-byte token, while all longer identifiers will occupy two +bytes. Some of the 7-bit codes will not be present however, since they are +control codes and they do not represent a symbol useful for \Cee\ in the +\caps{MIT} extension of \ASCII., so we can use them for special purposes. +The following symbolic names are used: + +\yskip +\hang |join| denotes the concatenation of adjacent items with no space or + line breaks allowed between them (the \:\& operation of \.{CWEB}). + +\hang |verb_quote| denotes the beginning or end of a sequence of bytes that + is to be copied verbatim to the output, i.e., no spaces should be inserted. + Such sequences are used to transmit numerical constants and strings (in + which case the ordinary string quotes appear within the |verb_quote| + bytes), and to implement the \:= operation. + +@^ASCII code dependencies@> + +@d verb_quote 0x2 /* takes the place of extended \ASCII. \.{\char2} */ +@d join 0x3 /* takes the place of extended \ASCII. \.{\char3} */ + +@< Global var... @>= +boolean atp_seen=false; /* whether \:p occurs anywhere */ + + +@* Phase II processing. +We will start to explain the structure of the tokens lists built up in +memory by considering how they are output during Phase~II; this is the +simpler part of the program. We will proceed in top-down fashion for this +part. + +@<Prototypes@>= +void phase_two (void); /* output the contents of the compressed tables */ +void output (text_pointer); /* recursively write out modules */ +void output_preproc_directives(void); /* write out all \:d and \:h stuff */ +void C_newline (void); /* send a newline to the \Cee~file */ +void out_char (eight_bits); /* write a single token */ + +@ Here is the general control routine. After Phase~I, the quantity +|output_file_count| will be equal to the number of extra output files to be +written. There should at least be either an unnamed section or one that +produces an additional output file, for otherwise there will be no output at +all; if the is no unnamed section, \:d~and~\:h commands will only have +effect if \:p occurs somewhere. + +@c void phase_two (void) +{ phase=2; + if (text_root==NULL && output_file_count==0) + @/{@; print("\n! No program text was specified."); mark_harmless(); } + @.No program text...@> + else + { if (show_progress) + { print("\nWriting the output file%s" @.Writing the output...@> + ,(text_root!=NULL)+output_file_count>1 ? "s" : ""); + if (text_root!=NULL) printf(" (%s):",C_file_name); + update_terminal(); + } + if (text_root==NULL) C_file=NULL; + else + { open_output_file(); cur_line=1; + if (!atp_seen) output_preproc_directives(); + output(text_root); + } + @<Write all the named output files@> + print_progress("\nDone.\n"); + } +} + +@ \.{\me.} can write output on multiple files. If a module name is +introduced in at least one place by \:( instead of \:<, it is treated as the +name of a file. All these special module names are saved in the array +|output_files|. + +@<Glob...@>= +mod_pointer output_file[max_files]; +int output_file_count=0; + +@ To write the named output files, we proceed as for the unnamed module, the +only subtlety is that we have to open each one after closing the previous +one. However, if there is no main file, there is no file to close the first +time, and this situation can be recognised because we have set |C_file=NULL| +in that case. The length of a module name can be at most |longest_name| +(which is rather more generous than allowed elsewhere for file names), so we +need no test for overflowing |output_file_name|. + +@<Write all the named output files@>= +{ int i; + char output_file_name[longest_name+1]; /* name of the file */ + for (i=0; i<output_file_count; i++) + { mod_pointer output_module=output_file[i]; + @< Copy name of |output_module| into |output_file_name| @> + if (C_file!=NULL) fclose(C_file); + if (output_module->equiv==NULL) + @/{@; print("\n! Module not present"); + @.Module not present@> + print_mod(output_module); err_print(""); + } + else if ((C_file=fopen(output_file_name,"w"))==NULL) + @/{@; print("\n! Cannot open \"%s\" as output file",output_file_name); + @.Cannot open output file@> + err_print(""); + } + else + { if (show_progress) print("\n(%s):",output_file_name); + cur_line=1; + output(output_module->equiv); + } + } +} + +@ If the module name defining the output file contains any occurrences of +`\.{@@@@}', they are undoubled in forming the actual file name; apart from +this should be no control codes in the module name. + +@< Copy name of |output_module| into |output_file_name| @>= +{ char* p=output_file_name, *q=name_begin(output_module); + while (*q!='\0') + if ((*p++=*q++)=='@@') + if (*q++!='@@') + { print("\n! Illegal control code in file name"); + @.Illegal control code in file name@> + print_mod(output_module); err_print(""); + } + *p='\0'; +} + +@ Here is how a file name gets added to the list of output files. + +@<Adjoin |cur_mod| to the set of output files@>= +{ int i=0; + while(i<output_file_count) + if (output_file[i]==cur_mod) break; @+ else ++i; + if (i==output_file_count) /* if not present, add |cur_mod| */ + if (output_file_count==max_files) overflow ("output files"); + else output_file[output_file_count++]=cur_mod; +} + + +@* Writing replacement texts. +We now come to the functions |output| and |output_preproc_directives|; the +task of the former is a recursive one, and it may also call the latter if +\:p occurs. The function |output| is not recursive, however, but it 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 |repl_field| points to replacement text of the active section; + +\hang |byte_field| is the |tok_mem| location from which the next token + on a particular level will be read; + +\hang |end_field| is the |tok_mem| location where the replacement + text of a particular level will end; + +\hang |sec_nr_field| is the section number. + +\hang |indent_field| determines the amount of indentation, for in case + |line_output==false| + +\yskip\noindent +The current values of these 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_byte|, |cur_end|, |cur_repl|, |cur_sec| and +|cur_ind|. + +@d cur_repl cur_state.repl_field +@d cur_byte cur_state.byte_field +@d cur_end cur_state.end_field +@d cur_sec cur_state.sec_nr_field +@d cur_ind cur_state.indent_field + +@<Typedef...@>= +typedef struct +{ text_pointer repl_field; /* replacement text of active section */ + eight_bits *byte_field; /* present location within replacement text */ + eight_bits *end_field; /* ending location of replacement text */ + sixteen_bits sec_nr_field; /* section number */ + sixteen_bits indent_field; /* amount of indentation */ +} output_state, * stack_pointer; + +@ The global variable |stack_ptr| tells how many levels of output are +currently in progress; the stack grows upwards with |stack_ptr| pointing to +the first vacant location above the top of the stack. The entry |stack[0]| +at the bottom of the stack is not used; when |stack_ptr| points to it then +|cur_state| itself has become invalid and the output process is completed; +this condition can be tested as |stack_empty()|. This somewhat strange use of +the stack is forced upon us because the \Cee\ language does not guarantee +that a pointer value at offset~$-1$ of an allocated array exists. Since the +location |stack[0]| is vacant we might as well use it to store |cur_state| +in. + +@d cur_state stack[0] +@d stack_end (&stack[stack_size_max]) +@d stack_empty() (stack_ptr==&stack[0]) + +@<Global...@>= +output_state stack[stack_size_max]; /* info for non-current levels */ +stack_pointer stack_ptr; /* points above top of output state stack */ + +@ When |line_output==false| we keep track of the current indentation level +in an array of white space characters |indent_buffer|. Every time a newline +is output, a sequence of |cur_ind| characters from |indent_buffer| are also +output by invoking |put_indent|. When relevant non-newline characters are +output, a white space counterpart of the character is appended to +|indent_buffer| by invoking the macro |append_white|, whose argument should +not involve any side effects. The current position of writing the characters +is maintained in |ind_i|, and reset to |cur_ind| on output of a newline. + +@d C_printf(format,x) fprintf(C_file,format,x) +@d C_putc(c) putc(c,C_file) +@d put_indent() + (indent_buffer[ind_i=cur_ind]='\0',C_printf("%s",indent_buffer)) +@d append_white(c) + if (ind_i>=max_indent) overflow("indent buffer"); + else indent_buffer[ind_i++]= isspace(c) ? c : ' ' @; +@< Global var... @>= +char indent_buffer[max_indent]; + /* white space characters to produce indentation */ +sixteen_bits ind_i; + /* number of those characters needed to reach current position */ + +@ When the replacement text for name |p| is to be inserted into the output, +the following function is called to save the old level of output and get +the new one going. The value of |cur_sec| is not set since its value is +recorded in the first token to be read after this function is called. It is +by calling |push_level| that the current value of |ind_i| becomes +significant, by being copied to |cur_ind|. + +@c +void push_level (mod_pointer p) /* suspends the current level */ +{ if (stack_ptr==stack_end) overflow("output stack"); + *stack_ptr++=cur_state; +@/cur_repl=p->equiv; + cur_byte=tok_begin(cur_repl); cur_end=tok_end(cur_repl); +@/cur_ind=ind_i; +} + +@ When we come to the end of a replacement text, the function +|continue_or_pop_level| does what its name suggests: it either moves to the +continuation of this replacement text (i.e., to the next section of the same +module) or returns the state to the most recently stacked level when a +module is completed. In the latter case all the fields of |cur_state| are +restored; in the former case, like for |push_level|, the value of |cur_sec| +is not yet set, but will be adjusted immediately afterwards. + +@c +void continue_or_pop_level (void) +{ if (cur_repl->text_link != NULL) + /* then link to a continuation, staying on the same level */ + {@; cur_repl=next_sec(cur_repl); + cur_byte=tok_begin(cur_repl); cur_end=tok_end(cur_repl); + } + else if (--stack_ptr > &stack[0]) cur_state=*stack_ptr; + /* otherwise |stack_empty()| holds */ + ind_i=cur_ind; /* reset indentation to base of current level */ +} + +@ The function |output| handles output of tokens by sending them to a lower +level function |out_char|, until the condition |stack_empty()| holds. The +task of |output| is to isolate and decode tokens, to handle stacking and +unstacking as necessary, and to issue special calls to |out_char| to mark +the beginning and end of replacements texts and to produce appropriate +\&{\#line} directives. As its name suggests, |out_char| usually handles the +output of single characters, but some special codes will make it perform a +few other operations. Some of these, like |verb_quote| and compressed +operator codes occur, as bytes with values below |0x80| inside the +replacements texts, and these get no special treatment by |output|. A few +other ones are generated explicitly by |output|, and these have values from +|0x80| upwards, defined in the enumeration below. Information about the +precise value of two-byte tokens is passed to |out_char| via the global +variable |cur_val|. + +@<Global...@>= +int cur_val; /* additional information corresponding to output token */ + +@~The code |identifier| is used for identifiers of length two or more, in +which case |cur_val| indicates the identifier name. The codes +|section_start| or |section_end| are sent at the beginning and end of parts +of the replacement text defined in a one section, in which case |cur_val| is +the section number; this number will be recorded in a comment to aid human +readers of the \Cee~file produced. When a \&{\#line} directive is to be +output, |line_mark| is sent to |out_char|, which will fetch the following +4~bytes (specifying the line number and file name) from the replacement text +itself; this is the only occasion where |output| does not read a complete +token before calling |out_char|. Any 8-bit characters that were escaped by a +byte `|0xF8|' are sent directly to |out_char|; they do not interfere with +the codes below since the input routine guarantees that they only occur +between |verb_quote| tokens, at which times |out_char| does not honour any +special codes. + +@< Typedef and enumeration... @>= +enum {identifier=0x80, section_start, section_end, line_mark }; + +@ Due to the output stack operations, the function |output| can be written +iteratively rather than recursively. A user who is not afraid of recursion +may however easily rewrite this code into a recursive form, and dispose of +the output stack and its routines altogether; the recursion depth will be +equal to the level of nesting of modules, which is not likely to cause any +problems in practice. We have retained the iterative version out of +reverence for the author of the original |WEB| system; it has the +additional advantage that, should there be a cycle in the directed graph of +module references, a reasonable overflow message will be produced by +|push_level|, whereas otherwise system stack overflow would occur, and it +would depend on the \Cee~runtime support whether it is properly detected. + +@c +void output (text_pointer repl) /* sends tokens to |out_char| */ +{ stack_ptr=&stack[1]; +@/cur_repl=repl; cur_byte=tok_begin(cur_repl); cur_end=tok_end(cur_repl); + cur_ind=ind_i=0; + do + if (cur_byte==cur_end) + { cur_val=cur_sec; + continue_or_pop_level(); + out_char(section_end); /* output ending section number comment */ + } + else + @< Output the token starting at |*cur_byte|, advancing |cur_byte| + correspondingly @> + while(!stack_empty()); + C_newline(); +} + +@ Most tokens lead to calling |out_char| with an appropriate value, after +possibly setting |cur_val| and |cur_sec|. Exceptions are module names, which +will push their replacement text on the stack but not produce any direct +output, and tokens |0xFA| which will produce the collected preprocessor +directives by calling |output_preproc_directives|, after ensuring that this +will start on a fresh line of output. + +@< Output the token starting at |*cur_byte|... @>= +{ int a = *cur_byte++; + if (a<0x80) out_char(a); /* single byte token */ + else if (a>=0xF8) + if (a<0xFA) out_char(a==0xF8 ? *cur_byte++ : line_mark); + else {@; C_newline(); output_preproc_directives(); } /* |a==0xFA| */ + else + { cur_val=(((a-=0x80)%0x28)<<8)+*cur_byte++; + switch (a/0x28) + { case 0: out_char(identifier); break; + case 1: @<Expand module name |cur_val| @> @+ break; + case 2: cur_sec=cur_val; out_char(section_start); + /* set the correct section number and output comment */ + } + } +} + +@ If any defining occurrence of a module name was encountered during the +Phase~I, its replacement text will have been be linked into to |equiv| field +of that name; otherwise that field will contain a null pointer and we must +report an error. + +@<Expand module name...@>= +{ mod_pointer mod_name=mod_at(cur_val); + if (mod_name->equiv!=NULL) push_level(mod_name); + else + {@; print("\n! Module not present"); print_mod(mod_name); err_print(""); + @.Module not present@> + } +} + +@ Output of macro definitions and inclusions of header files is sufficiently +different from ordinary output that we use a special function +|output_preproc_directives| for it. We bypass |output| and call |out_char| +directly, since in these cases there can be no nesting of replacement texts. +We go through the list of all replacement texts and copy the ones that refer +to macros or header files, preceded respectively by \&{\#define} and +\&{\#include}. During the output of a single directive any line breaks +present in the source file will be protected by backslashes. We use local +variables |p| and |end| instead of |cur_byte| and |cur_end|, since the +current level of the stack is already in use if this function is called from +|output|. + +@c +void output_preproc_directives (void) +{ text_pointer repl,l; + eight_bits* p, *end; + protect=true; /* newlines should be preceded by |'\\'| */ + for (repl=&text_table[0]; repl<text_ptr; repl++) + if ((l=repl->text_link)==macro_flag || l==header_flag) + { p=tok_begin(repl); end=tok_end(repl); + C_printf ("#%se ",l==macro_flag ? "defin" : "includ"); + out_state=no_space; + while (p<end) + @< Output preprocessor token starting at |p|, advancing |p| + correspondingly @> + C_newline(); /* this newline is not escaped */ + } + protect=false; +} + +@ The situation here is simpler than in |output|, since module names and \:p +control codes are forbidden in the replacement texts handled here, nor will +codes for \.{\#line} directives or section number indications have been +included in them during input. Should any such token nevertheless turn up, +then one of the two calls of |confusion| below is invoked; the diagnostic is +not quite exhaustive as to the possible cause in either case, but we do not +wish to elaborate on code that will never be executed. + +@< Output preprocessor token... @>= +{ int a=*p++; + if (a<0x80) out_char(a); /* single byte token */ + else if (a>=0xF8) + if (a==0xF8) out_char(*p++); @+ + else confusion("`@@p' within macro"); @.`@@p' within macro@> + else + { cur_val=(((a-=0x80)%0x28)<<8)+*p++; + if (a<0x28) out_char(identifier); @+ + else confusion("module within macro"); @.module within macro@> + } +} + + +@* Writing characters. The |output| routine above handles the global +structure of output generation; we now present the routines that transform +the lexical items produced by |output| into characters. +First we give a function that is called whenever we want to finish off a +line sent to the \Cee~file. +It keeps |cur_line| equal to the number of the next line to be output, +and displays a progress report every 100 lines. + +@c +void C_newline (void) /* writes one line to output file */ +{ C_putc('\n'); + if (!line_output) put_indent(); + if (cur_line%100==0 && show_progress) + { if (cur_line%500!=0) print("."); /* progress report */ + else print(cur_line%2500==0 ? "%u\n" : "%u",cur_line); + update_terminal(); + } + ++cur_line; +} + +@ The function |out_char| must make sure that the output has the proper +``surface structure''. When |line_output==false| everything should +essentially be written as it is stored, but when |line_output==true| only +the essential tokens have been stored, and white space has to be inserted +where appropriate. Spaces should not occur at certain 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), while in other places +they are required (e.g., between identifiers, which for \.{\me.} includes +reserved words, and between certain operators that might otherwise be +considered as a single token). Such surface structure can very nicely be +obtained by attaching a small finite state machine to the output generator, +recording information about the most recent token (as a matter of fact, even +\TeX's fabulous formatting of math formulae is largely based on such a simple +device). In our present case, the state of the output process is recorded in +the global variable |out_state|. Furthermore there is a variable |protect| +that is explicitly managed by the function calling |out_char| (notably by +|output_preproc_directives|), being set to |true| whenever newlines are to +be escaped by a backslash. + +@<Global...@>= +eight_bits out_state; /* current status of partial output */ +boolean protect; /* should newline characters be quoted? */ + +@ The output process can be in one of following states: + +\yskip\hang |no_space| means that no space will precede the following item. +This state is set by \:\&, and after the output of anything that could not +possibly combine into a larger token, such as punctuation. + +\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 |operator| means that the last item in the buffer is an operator, +hence a blank space or line break must be inserted if the next item is also +an operator. + +\yskip\hang |literal| 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; the |verb_quote| +character serves as a quoting mechanism which switches this state +on~and~off. + +@< Typedef and enum... @>= +enum { no_space, num_or_id, operator, literal}; + +@ The function |out_char| is a many-way switch on the kind of character or +token transmitted to it. The |verb_quote| mechanism is used amongst others +to encapsulate numeric constants, so we make the whole construction behave +as an identifier with respect to spacing (the user can still defeat any +spaces around it by means of \:\&). The code |line_mark| will only be stored +(and output) if |line_output==true|. We do not anticipate the need of +indentation across literals, operators and such, and assume that module +names are preceded on the same line only by white space, identifiers +(keywords) and things like braces; apart from identifiers all of these come +through the |default| case below. Therefore we only include an invocation of +|append_white| in that case and for identifiers; to handle all possible +cases the assiduous user could add such invocations to all other calls of +|C_putc| and |C_printf|. + +@c +void out_char (eight_bits c) +{ if (out_state==literal) + if (c==verb_quote) out_state=num_or_id; /* close literal mode */ + else C_putc(c); /* write literal character, possibly 8-bit */ + else if (isalnum(c)&&c<0x80 || c=='_') + /* single character identifier or number */ + { if (out_state==num_or_id && line_output) C_putc(' '); + C_putc(c); out_state=num_or_id; + if (!line_output) append_white(c); + } + else switch (c) + { case verb_quote: + if (out_state==num_or_id && line_output) C_putc(' '); + out_state=literal; break; + case join: out_state=no_space; break; + case '\n': @< Output a newline @> @+ break; + case identifier: @< Output an identifier @> @+ break; + case section_start: + if (line_output) C_printf("/*%d:*/",cur_val); @+ else C_newline(); + out_state=no_space; break; + case section_end: + if (line_output) C_printf("/*:%d*/",cur_val); @+ else C_newline(); + out_state=no_space; break; + case line_mark: @< Output a \.{\#line} directive @> @+ break; + @\@<Cases of operator symbols@> + @\@<Cases of compressed operators@> + default: C_putc(c); out_state=no_space; + if (!line_output) append_white(c); + } +} + +@ Newlines are escaped whenever |protect| holds; a space is prepended so +that the newline is effectively replaced by a space. This also means that +the user who cannot break the habit of escaping newlines in multi-line +preprocessor directives, even though this is unnecessary after \:d and \:h, +is not punished for this by the creation of the sequence `\.{\\\\}' at the +end of the lines, but rather gets `\.{\\\ \\}', which is harmless. + +@< Output a newline @>= +{ if (protect) {@; C_putc(' '); C_putc('\\'); } + C_newline(); + if (out_state!=literal) out_state=no_space; +} + +@ Apart from alphanumeric characters and underscores, we allow identifiers +to contain characters in the range |0x80<=c<=UCHAR_MAX|, if a translation +into a string of legal characters is known to \.{\me.}; such translations +can be specified by \:l directives in limbo. These translations are stored +in an array |c_trans|. + +@d trans_limit 9 /* maximal length of a translation string */ +@d trans_of(c) c_trans[(unsigned char)(c)-0x80] +@d translation_exists(c) (trans_of(c)[0]!='\0') /* non-empty translation */ + +@< Global var... @>= +char c_trans[UCHAR_MAX+1-0x80][trans_limit+1]; + +@~In compatibility mode, default translations exist of the form `\.X\\{NN}', +where \\{NN} is the (two digit) upper case hexadecimal representation of the +character in question. Otherwise the static initialisation will have made all +translation strings empty. + +@< Set init... @>= +if (compatibility_mode) +@/{@; unsigned char c=UCHAR_MAX; + do sprintf(trans_of(c),"X%X",c); while (--c>=0x80); +} + +@ In case of identifiers the name indexed by |cur_val| is written out. The +translation of characters in the range from |0x80| upwards is performed +here, rather than on input of such characters, since this is slightly more +convenient. The check that |translations_exists| was done on input, however. + +@< Output an identifier @>= +{ char* p=name_begin(id_at(cur_val)); int l=0; + if (out_state==num_or_id && line_output) C_putc(' '); + do + if ((unsigned char)(*p)<0x80) {@; C_putc(*p);++l; } + else + {@; char* q=trans_of(*p); do {@; C_putc(*q); ++l; } while (*++q!='\0'); } + while (*++p!='\0'); + out_state=num_or_id; + if (!line_output) do append_white(' '); while (--l>0); +} + +@ As indicated above, a call |out_char(line_mark)| causes four further bytes +to be fetched directly from the token memory, increasing |cur_byte| so that +|output| will resume at the correct byte. + +@< Output a \.{\#line} directive @>= +{ sixteen_bits a; + a=(*cur_byte++)<<8; a+=*cur_byte++; /* get the line number */ + C_newline(); C_printf("#line %u \"",a); @/ + a=(*cur_byte++)<<8; a+=*cur_byte++; /* get the file name index */ + C_printf("%s\"",name_begin(id_at(a))); + C_newline(); out_state=no_space; +} + +@ In some cases when two operator symbols are adjacent, a space is required +in between to avoid interpretation as a compound symbol. The language +definition is not explicit about when such a space is mandatory, but +examples are the somewhat unusual constructions | x /@, *p |, | a++ + b != a ++ ++b| and |sum = a - -b|; certain backward \Cee~compilers even think |x=-x| +is ambiguous without a space before the minus sign. Rather than trying to +detect precisely the problematic cases we take the conservative approach of +always putting in a space, unless the second operator is~`\.='; this +exception is made because we store operators like `\.{+=}' as two separate +tokens, but they form a single token according to the \caps{ANSI/ISO} +standard. Note that other multi-character operators like `\.{\&\&}' stay +intact because they will be compressed on input, and therefore do not involve +the present section. + +@<Cases of operator symbols@>= +case '+': case '-': case '*': case '/': case '%': case'?': +case '<': case '>': case '&': case '|': + if (out_state==operator && line_output) C_putc(' '); /* fall through */ +case '=': C_putc(c); out_state=operator; break; + +@ Compilers don't mind doing repetitive work, so we use a macro to produce +several almost identical cases. Had the cases been much longer or more +numerous however, then combining the cases and using an array of strings +would have been preferable. + +@d comp_op(op) + (C_printf(out_state==operator && line_output ? " %s" : "%s",op) + ,out_state=operator) + +@<Cases of compressed operators@>= +case plus_plus: comp_op("++"); break; +case minus_minus: comp_op("--"); break; +case minus_gt: comp_op("->"); break; +case gt_gt: comp_op(">>"); break; +case eq_eq: comp_op("=="); break; +case lt_lt: comp_op("<<"); break; +case gt_eq: comp_op(">="); break; +case lt_eq: comp_op("<="); break; +case not_eq: comp_op("!="); break; +case and_and: comp_op("&&"); break; +case or_or: comp_op("||"); break; + + +@* Introduction to the phase I. We have now seen that \.{\me.} will be +able to output the full \Cee\ program, if we can only get that program into +the byte memory in the proper format. The input process is something like +the output process in reverse, since we compress the text as we read it in +and we expand it as we write it out (but of course, as any programmer knows, +input requires a bit more work than the corresponding output). To preserve +the symmetry we shall proceed in a bottom-up fashion in presenting this part +of the program. + +The basic character input routines are defined in the code shared with +\.{CWEAVE}. At the next higher level there are three main input routines. +The most interesting is the one that gets the next token of a \Cee\ text; +the other two are used to scan rapidly past \TeX\ text in the \.{CWEB} +source code. One of the latter routines will jump to the next token that +starts with `\.{@@}', and the other skips to the end of a \Cee~comment. + +@ Control codes in \.{CWEB} begin with `\.{@@}', and the next character +identifies the code. Some of these are of interest only to \.{CWEAVE}, so +\.{\me.} ignores them; the others are converted by \.{\me.} into +internal code numbers. The code numbers have been chosen such that they are +distinct from ordinary characters (so that a function can return either a +character or a control code), and their ordering is such as to simplify the +program logic; larger numbers are given to the control codes that denote +more significant milestones. This order is only relevant starting from +|format| however. The code \:> is treated as ignored because it should not +occur in places where we are not searching for it. The following enumeration +lists the non-character values that may be returned from~|get_next|; +therefore the two values |id_code| and |constant| are included although they +do not belong to any control code. + +@<Typedef and enum...@>= +enum @/ +{ ignore=0x80, /* control code of no interest to \.{\me.}, or \:> */ + id_code, constant, /* token codes but not control codes */ + verbatim, /* control code for \:= */ + at_sign_image, /* control code for \:@@ */ + join_code, /* control code for \:\& */ + ord, /* control code for \:' */ + control_text, /* control code for \:t, \:\^, etc. */ + include_preproc, /* control code for \:p */ + char_trans, /* control code for \:l */ + format, /* control code for \:f */ + definition, /* control code for \:d */ + header, /* control code for \:h */ + begin_C, /* control code for \:c */ + module_name, /* control code for \:< or \:( */ + new_section /* control code for \:\ , \:\~, or \:* */ +}; + +@ The conversion from the character following `\.@@' to the corresponding +numeric code is performed by means of the table~|ccode|. Since we don't know +whether characters are signed or not, we always access |ccode| via the macro +|code_of|. + +@d code_of(c) ccode[(unsigned char)(c)] + +@<Global...@>= +eight_bits ccode[UCHAR_MAX+1]; /* meaning of a char following `\.@@' */ + +@~Here we initialise |ccode|. The code code for~\:v is the only case where a +value stored in the |ccode| table is an ordinary character; this is done so +that it may be used in the \Cee~part of a section to stand for~`|@v|', even +though there is no need for using it there. + +@<Set ini...@>= +{ unsigned char c=0; + do ccode[c] = isspace (c) ? new_section : ignore; while(c++!=UCHAR_MAX); +@/ccode['v']=ccode['V']='|'; +@/ccode['=']=verbatim; +@/ccode['@@']=at_sign_image; +@/ccode['&']=join_code; +@/ccode['\'']=ord; +@/ccode['^']=ccode['?']=ccode['.']=ccode[':']=ccode['#']= + ccode['t']=ccode['T']=ccode['q']=ccode['Q']=control_text; +@/ccode['p']=ccode['P']=include_preproc; +@/ccode['l']=ccode['L']=char_trans; +@/ccode['f']=ccode['F']=ccode['s']=ccode['S']=format; +@/ccode['d']=ccode['D']=definition; +@/ccode['h']=ccode['H']=header; +@/ccode['c']=ccode['C']=begin_C; +@/ccode['<']=ccode['(']=module_name; +@/ccode['~']=ccode['*']=new_section; +if (compatibility_mode) + @< Reset some control codes to match Levy/Knuth \.{CWEB} @> +} + +@ In \.{CWEBx} there are a few control codes that also exist in \LKC. but +have a different meaning. In compatibility mode we reassign the meaning of +these codes to that of \LKC., making their usual function inaccessible, +since it is not intended that hybrid programs should be written using the +codes of \LKC. together with features particular to \.{CWEBx}. For +\.{\me.} the change of meaning of \:: (which is used instead of \:? in +\LKC.) has no effect since it introduces a control text anyway. + +@< Reset some control codes... @>= +{ ccode['h']=ccode['H']=include_preproc; /* \:h means \:p */ + ccode['p']=ccode['P']=begin_C; /* \:p means \:c */ + ccode['#']=ignore; /* \:\# means \:) */ +} + +@ The function |skip_ahead| reads through the input at fairly high speed +until finding the next non-ignorable control code, which it returns. It is +used to skip over \TeX~text, and text in limbo. It uses the fact that +|get_line| places a~|' '| at~|*limit|, so that placing the sentinel cannot +inadvertently create a token \:@@. + +@c +eight_bits skip_ahead (void) /* skip to next control code */ +{ eight_bits c; /* control code found */ + while (find_char()) + { limit[1]='@@'; /* place a sentinel */ + while (*loc++!='@@') {} + if (loc<=limit && (c=code_of(*loc++))!=ignore) return c; + } + return new_section; +} + +@ The function |skip_comment| reads through the input until finding the +end-comment token `\.{*/}' or a newline, or in case of a \Cpp\ one-line +comment (starting with `$/\!/$') just until finding a newline. The |one_liner| +parameter tells whether the latter applies, and a boolean result is returned +telling whether a newline in the middle of a comment was scanned (which can +only happen if |one_liner==false|). Returning such a newline is necessary so +that the each newline in the \Cee\ part of a section may be copied to the +output; otherwise the \&{\#line} commands inserted into the \Cee\ file by +the output routines become useless. When |line_output==false|, all characters +encountered are also stored, so that they can be output along with the +\Cee~code. + +The function gives an error message if it encounters a sequence `\.{/*}', +since this almost certainly means the user has forgotten to close the +comment. In this way we avoid the most pernicious consequence of \Cee's rule +that nested comments are forbidden, namely that such an omission often leads +to a syntactically correct, but unintended program. This means that one +cannot write either `\.{/*}' or `\.{*/}' in a comment; this difficulty can +usually be circumvented by inserting something like `\.{\{\}}' between the +two characters. + +As a safety measure, |skip_comment| comes to an end if it runs into the next +section, and prints an error message; it is slightly non-trivial that this +test does not interfere with searching for the end of the comment. If a +module name occurs in a comment (presumably in `\pb'), it is scanned by the +normal routines that will be given later: this could be the only place where +the name is spelled out in full, and/or using \:(. + +@c +boolean skip_comment (boolean one_liner) /* skips over comments */ +{ char c; /* current character */ + do + { if (loc>=limit) + if (one_liner) return false; + else if (get_line ()) return true; + else + {@; err_print("! Input ended in mid-comment"); return false; } + @.Input ended in mid-comment@> + if ((c=*loc++)=='/' && *loc=='*') + err_print("! `/*' inside comment, did you forget `*/' before? "); + @.`/*' inside comment...@> + if (c=='@@') + { eight_bits cc=code_of(*loc++); + if (cc==new_section) /* watch out for \:\ , \:\~, and \:* */ + @/{@; err_print("! Section ended in mid-comment"); loc-=2; return false; } + @.Section ended in mid-comment@> + if (cc==module_name) @/{@; @< Scan the module name... @> continue; } + } + if (!line_output) + {@; store_byte(c); + if (c=='@@' && (c=loc[-1])!='@@') store_byte(c); + } + } while (c!='*' || *loc!='/' || one_liner); + ++loc; @+ if (!line_output) store_byte('/'); + return false; +} + + +@* Getting the next token. +We now come to the function |get_next|, which does more extensive scanning. +It will move the input pointer over a single token of input, and if that +token consists of a single character that is not an identifier or a number, +it will return that character; certain two-character operators are also +returned as a single character representing the operator. In the case of +longer tokens, it will return one of the codes of the enumeration given +earlier, after possibly setting some global variables in such a way that the +complete token can be retrieved. For numeric constants |id_first| and +|id_loc| will be made to point to the beginning and end of the token +within~|buffer|. For string constants, which need not reside in a single +line of input, the characters are copied into the separate buffer |mod_text| +and this is where |id_first| and |id_loc| will then point. Identifiers and +module names are even looked up, and |cur_id| or |cur_mod| is made to point +to the name found. In the case of single-character identifiers or bad module +names, which are not entered into the name tables, |cur_id| respectively +|cur_mod| is set to~|NULL|; in the former case |id_first| is made to point +to the character that forms the identifier. + +@<Global...@>= +id_pointer cur_id; /* identifier just scanned */ +mod_pointer cur_mod; /* module name just scanned */ + +@ As one might expect, |get_next| consists mostly of a big switch that +branches to the various special cases that can arise. The static flag +|preprocessing| is raised while we are scanning an explicit preprocessor +directive (i.e., not one introduced by \:d or \:h). The static flag +|comment_continues| is raised when |get_next| must return a newline while +scanning a comment, so that it will remember what it was doing the next time +it is called. Although we allow 8-bit character input, any characters +|c>=0x80| should be contained in a comment or a character or string constant, +or in case |translation_exists(c)| an identifier, since they cannot +otherwise be part of a valid \Cee~symbol. + +@c +eight_bits get_next (void) /* produces the next input token */ +{ static boolean preprocessing=false; /* did this line start with `\.\#'? */ + static boolean comment_continues=false; /* were we scanning a comment? */ + eight_bits c; /* the current character */ +restart: + @< Handle cases involving newlines and comments; + if a token is found |return| it, or |goto restart| if it is ignored; + otherwise read the next character into |c| @> + if (c=='L' && (*loc=='\'' || *loc=='\"')) + {@; get_string(); return constant; } + if (c<0x80 ? isalpha(c) || c=='_' : translation_exists(c)) + @/{@; @< Get an identifier @> return id_code; } + if (c>=0x80) {@; err_print("! Illegal 8-bit character"); goto restart; } + @.Illegal 8-bit character@> + if (isdigit(c) || c=='.' && isdigit((eight_bits)*loc)) + @/{@; @< Get a numeric constant @> return constant; } + switch(c) + { case '\'': case '"': get_string(); return constant; + case '@@': @< Get a control code and possibly a complete module name, + and either |return| it, + or |goto restart| if control code is |ignore| @> + @\@< In applicable cases |return| compressed two-symbol operator @> + } + return c; +} + +@ The flag |preprocessing| raised when the first character of a line +is~`\.\#', and is lowered at a non-escaped newline; while it is raised spaces +are not ignored. The test whether a newline is escaped is not entirely +sound, since a final `\.{\\}' might be part of a \:\\ control code or of a +\Cpp\ one-line comment (after `$/\!/$'); a completely correct check would be +more difficult, and leaving |preprocessing| raised a bit too long does no +real harm. Contrary to |preprocessing|, the flag |comment_continues| will +always be given a new value on the first call to |get_next| after it is set, +and therefore can remain set only if |skip_comment| finds another newline. + +@< Handle cases involving newlines... @>= +{ if (loc>=limit) + { if (preprocessing && limit>buffer && limit[-1]!='\\') + preprocessing=false; + return get_line() ? '\n' : new_section; + } + if (comment_continues + ||(c=*loc++)=='/' && (*loc=='*' || C_plus_plus && *loc=='/')) + @< Scan to the end of the comment or of the current line, + setting |comment_continues| to indicate which case applies; + in the former case |goto restart|, in the latter |return '\n'| @> + if (isspace(c)) + if (line_output) + if (preprocessing) return ' '; @+ else goto restart; + /* ignore other spaces */ + else return c; + /* when |line_output==false| preserve white space faithfully */ + if (c=='#' && loc==buffer+1) preprocessing=true; +} + +@ When we come to this code |loc| either points to the beginning of a +multi-line comment or to the second character of `\.{//}' or `\.{/*}'; in +the latter case we must advance |loc| before looking for `\.{*/}', lest +`\.{/*/}' would be scanned as a complete comment. Another subtlety arises +when both |preprocessing| and |comment_continues| are raised, which means +that a preprocessor line has ended inside a comment; although in \Cee\ this +means that the preprocessor line continues, \.{\me.} will terminate it +because the comment is removed and a non-escaped newline remains. The +warning message below can be ignored by the user unless something follows +the comment, but the proper way to resolve this is to start the comment on +the line after the preprocessor line, which avoids all confusion. + +@< Scan to the end of the comment... @>= +{ boolean one_liner=false; + if (!comment_continues) + { if (!line_output) {@; store_byte('/'); store_byte(*loc); } + one_liner=*loc++=='/'; + /*/ record kind of comment, and advance to comment proper {/}*/ + } + else if (preprocessing) + { print("\nWarning: Multi-line comment in preprocessor line"); + @.Multi-line comment...@> + preprocessing=false; mark_harmless(); + } + if (comment_continues=skip_comment(one_liner)) + /* scan to end of comment or newline */ + return '\n'; /* comment contains a newline; |get_line| has been called */ + else goto restart; /* comment complete, get next token */ +} + +@ The following code assigns values to the combinations `\.{++}', `\.{--}', +`\.{->}', `\.{>=}', `\.{<=}', `\.{==}', `\.{<<}', `\.{>>}', `\.{!=}', +`\.{\v\v}' and `\.{\&\&}'. The compound assignment operators (e.g., `\.{+=}') +are separate tokens, according to {\sl The C Reference Manual\/} (so there may +be white space and even comments in between), but according to the +\caps{ANSI/ISO} standard they are single tokens. We have not allocated +single-byte codes for them, so we scan them as two separate symbols; these +will be adjacent on output. Therefore, as far as \.{\me.} is concerned, one +may write a separation between the two characters even if the \Cee-compiler +does not accept this; however, this practice should still be avoided because +it will confuse the parser of |CWEAVE|, leading to ill-formatted output. + +@d compress(char2,code) @+ + if (*loc==char2) return ++loc,code @; + +@< In applicable cases... @>= +case '+': compress('+',plus_plus); break; +case '-': compress('-',minus_minus); compress('>',minus_gt); break; +case '=': compress('=',eq_eq); break; +case '>': compress('=',gt_eq); compress('>',gt_gt); break; +case '<': compress('=',lt_eq); compress('<',lt_lt); break; +case '&': compress('&',and_and); break; +case '|': compress('|',or_or); break; +case '!': compress('=',not_eq); break; + +@ Identifiers are looked up unless they consist of a single character +|c<0x80|, in which case that character can be found as |*id_first|; there is +no need to set |id_loc| in either case. In case any 8-bit characters appear in +identifiers, they are only stored in the name table, and cannot interfere with +any of the special codes we use. + +@< Get an identifier @>= +{ id_first=--loc; /* mark beginning of identifier */ + do c=*++loc; + while (c<0x80 ? isalnum(c) || c=='_' : translation_exists(c)); + cur_id= + loc==id_first+1 && (eight_bits)(*id_first)<0x80 + ? NULL : id_lookup(id_first,loc,0); +} + +@ Scanning numeric constants is straightforward. The only subtle point is +that we must not call functions like |isdigit| with an argument of type +|char|, like |*loc|, since that may fail for 8-bits characters if |char| is +a signed type; therefore we copy characters into |c|, which has type +|unsigned char|, before applying such functions. We don't mind \.{8}'s and +\.{9}'s appearing in octal constants (although the \Cee~compiler will), so +there is no need here to distinguish between octal and decimal constants. + +@< Get a numeric constant @>= +{ if (*(id_first=loc-1)=='0' && tolower((eight_bits)*loc)=='x') + /* hex constant */ + do c=*++loc; while (isxdigit(c)); + else /* octal, decimal or float constant */ + { while (isdigit(c)) c=*loc++; + if (c=='.') @+ do c=*loc++; while (isdigit(c)); + if (tolower(c)=='e') /* floating point constant with exponent */ + { if ((c=*loc)=='+' || c=='-') c=*++loc; + while (isdigit(c)) c=*++loc; + } + else --loc; /* back up to first character after constant */ + } + while (isalpha(c)) c=*++loc; + /* incorporate any `\.{U}', `\.{L}', or `\.{F}' suffixes */ + id_loc=loc; +} + +@ After an `\.@@' sign has been scanned, the next character tells us whether +there is more work to do. Although control codes like \:\^ and \:t are +ignored by \.{\me.}, their control text has to be skipped, which is +performed by |get_control_text|. Verbatim constructions also use +|get_control_text|, but then return the |verbatim| control code, unless their +control text was empty, in which case they are ignored. + +@< Get a control code... @>= +{ eight_bits cc=code_of(*loc++); + switch(cc) + { case ignore: goto restart; + case control_text: get_control_text(); goto restart; + case verbatim: if (get_control_text()) goto restart; @+ else break; + case ord: @<Scan an \ASCII. constant@> @+ break; + case module_name: + @<Scan the module name and make |cur_mod| point to it@> + } + return cc; +} + +@ Before we scan a module name, we record whether it started with \:(, so +that we may record it afterwards as an auxiliary file name if necessary. + +@<Scan the module name...@>= +{ boolean file_module= loc[-1]=='('; + /* does this module define an output file? */ + cur_mod=get_module_name(); + if (file_module && cur_mod!=NULL) + @<Adjoin |cur_mod| to the set of output files@> +} + +@ There is no reason why we should allow a newline within an \ASCII. +constant, even if it is escaped. + +@<Scan an \ASCII. constant@>= +id_first=loc; /* first character after opening quote */ +while (*loc!='\'') +{ if (*loc++=='\\') loc++; /* accept any character following backslash */ + if (loc>=limit) {@; err_print("! ASCII constant didn't end"); break; } + @.ASCII constant didn't end@> +} +id_loc=loc++; /* move past closing quote */ + + +@* Scanning a piece of \Cee\ text. +Having acquired the skills of skipping over text and isolating tokens that +really matter, we are now ready to tackle complete pieces of \Cee~text. The +rules for generating the replacement texts for preprocessor directives +following \:d and \:h and for bodies of sections are almost identical; the +only differences are that + +\yskip \item{a)} + Module names are not possible in preprocessor directives; indeed, the + appearance of a module name terminates such directives and starts the + \Cee~part of the section. + +\item{b)} + The codes \:d, \:h, \:f, and \:c are not allowed in a \Cee~part, while + they terminate preprocessor directives. + +\item{c)} + The code \:p is not allowed inside preprocessor directives. +\yskip + +\noindent Therefore there is a single function |scan_repl| with a parameter +|context| indicating which of the two kinds of replacement texts is being +scanned; its value is either |preproc_directive| or |section_body|. After +|scan_repl| has acted, |cur_text| will point to the replacement text just +generated, and |next_control| will contain the control code that terminated +the activity. + +@d preproc_directive 0 +@d section_body 1 + +@<Global...@>= +text_pointer cur_text; /* replacement text formed by |scan_repl| */ +eight_bits next_control; /* control code which has already been scanned */ + +@ We will try to reduce storage requirements by discarding any trailing +newlines both after preprocessor directives and section bodies. This does not +affect the line sequencing indicated by \&{\#line} directives in either case, +since in the former case such directives are not issued in the first place, +and in the latter case a new \&{\#line} directive will be issued before any +further code is added. Note that we cannot reliably recognise newlines in a +right-to-left motion when the end of a replacement text is reached, since +there is a mixture of one-byte, two-byte and five-byte tokens. Therefore, +instead of tracing backwards, we mark the position of the last non-discardable +token every time one is appended, and back up to the last position recorded +when reaching the end of a replacement text. Given this mechanism, it is easy +to discard other things than newlines, such as \&{\#line} directives that +apply to no lines at all, and to back up at other occasions where the line +sequencing is interrupted as well, like before applied module names. + +This is also a good place to add a simple but useful check, namely that in +each section and macro replacement text the parentheses and braces should be +matched (failure in this respect can lead to syntax errors that are very +hard to locate in the \.{CWEB} source, since the real problem may be buried +inside a module name or macro). + +@c +void scan_repl (eight_bits context) /* creates a replacement text */ +{ eight_bits a; /* the current token */ + eight_bits* keep=tok_ptr; /* non-discardable stuff up to this point */ + int brace_level=0, par_level=0; + if (context==section_body) + @< Insert the line number into |tok_mem| @> + do @< Scan and store a token, setting |keep=tok_ptr| afterwards unless it + is discardable; when the replacement text has ended |goto done| @> + while (true); +done: tok_ptr=keep; /* backup over trailing newlines and \&{\#line} marks */ + next_control=a; /* this control code must be reconsidered */ + if (par_level>0 || brace_level>0) @< Report unmatched opening symbols @> + @< Wrap up the accumulated bytes into a completed text, + pointed to by |cur_text| @> +} + +@ When the function |scan_repl| is called we have either just scanned \:h or +the identifier following \:d or a defining occurrence of a module name (with +the following `\.='); in all cases we start by calling |get_next|. + +@< Scan and store a token... @>= +{ switch (a=get_next()) + { + @\@< Cases where |a| is a special token (|id_code|, + |module_name|, etc.): either store the corresponding bytes and + |continue|, or |goto done| if |a| signals the end of this + replacement text@> + case format: case definition: case header: case begin_C: + if (context==preproc_directive) goto done; + err_print + ("! `@@f', `@@d', `@@h', and `@@c' are ignored in section body"); + @.`@@f', `@@d', ... are ignored...@> + continue; + case new_section: goto done; + @\@< Cases that keep track of |par_level| and |brace_level| @> + case '\n': store_byte('\n'); + if (context==section_body && print_where) /* input file was switched */ + { tok_ptr=keep; /* back up over discardable items */ + @< Insert the line number into |tok_mem| @> + } + continue; + case join_code: a=join; + } + store_byte(a); keep=tok_ptr; /* mark as non-discardable */ +} + +@ We test matching parentheses and braces separately because it is easier, +and in the unlikely case that a mismatch should go undetected the compiler +will surely report an error that is easy to locate. All parentheses and +braces that are returned from |get_next| are real ones, i.e., not part of a +string or comment. + +@< Cases that keep track of |par_level| and |brace_level| @>= +case '(': ++par_level; break; +case ')': + if (par_level<=0) + {@; err_print("! Unmatched closing parenthesis"); continue; } + @.Unmatched closing...@> + --par_level; break; +case '{': ++brace_level; break; +case '}': + if (brace_level<=0) + {@; err_print("! Unmatched closing brace"); continue; } + --brace_level; break; + +@ The most difficult part of matching parentheses and braces is reporting any +errors at the end of the macro or section in proper English. We supply the +missing closing symbols so that there is a slightly larger chance that the +produced \Cee~code will compile. Note that there is no need to update |keep| +here, since no backing up will follow. + +@< Report unmatched opening symbols @>= +{ char *p, *s; int l; + if (par_level>0) l=par_level, s="parenthes", p= l>1 ? "es" : "is"; + else l=brace_level, s="brace", p=l>1 ? "s" : ""; + print("\n! There %s %d unclosed %s%s" + ,par_level+brace_level>1 ? "are" : "is", l, s, p ); + @.There are unclosed...@> + if (par_level>0 && brace_level>0) + print(" and %d unclosed brace%s" + , brace_level, brace_level>1 ? "s" : ""); + print(" in the previous "); + err_print(context==preproc_directive ? "macro" : "section"); + while (--par_level>=0) store_byte(')'); + while (--brace_level>=0) store_byte('}'); +} + +@ When a module reference is inserted the line sequencing is interrupted, +so we must restore it afterwards; on the other hand newlines before the +insertion can be discarded. + +@< Cases where |a| is...@>= +case id_code: + @< Store the identifier just scanned @> @+ keep=tok_ptr; continue; +case module_name: if (context==preproc_directive) goto done; + if (cur_mod!=NULL) /* don't record bad module name */ + { sixteen_bits n = mod_index(cur_mod); /* index of module name */ + @< If this looks like a defining occurrence, report a runaway section @> + if (line_output) tok_ptr=keep; /* back up */ + store_two_bytes(0xA800+n); keep=tok_ptr; + /* store reference to module name */ + @< Insert the line number into |tok_mem| @> + /* to get in phase after module insertion */ + } + continue; +case constant: case verbatim: + @/@< Copy a constant or verbatim construction @> @+ keep=tok_ptr; + continue; +case ord: + @<Translate an \ASCII. constant@> @+ keep=tok_ptr; continue; +case include_preproc: + if (context==preproc_directive) + err_print("! `@@p' is forbidden in preprocessor directive"); + @.`@@p' is forbidden...@> + else + { if (line_output) tok_ptr=keep; + store_byte(0xFA); atp_seen=true; keep=tok_ptr; + @< Insert the line number... @> + } + continue; +case char_trans: + err_print("! `@@l' is only allowed in limbo"); + @.`@@l' is only allowed in limbo@> + continue; + +@ Identifiers of length~$1$ have no name pointer, but |id_first| still +points at the relevant character. + +@< Store the identifier... @>= +if (cur_id==NULL) store_byte(*id_first); /* single-character identifier */ +else store_two_bytes(0x8000+id_index(cur_id)); + +@ Here we look to see whether a module name is followed by~`\.{=}' +or~`\.{+=}' (whether another `\.{=}' follows is irrelevant at this point), +in which case we assume that introduces the \Cee-part of a new section, of +which we somehow missed the opening \:\ . Note that this need not limit the +use of module names standing for single expressions, even if followed by the +`\.{=}' or the `\.{+=}' operator, since such names should be followed by +`\.{@@;@@;}' for proper treatment by \.{CWEAVE}. + +@< If this looks like a defining occurrence, report... @>= +{ char *p=loc; + while (*p==' ' && p<limit) ++p; + if (*p=='+') ++p; /* an optional `\.+' is allowed */ + if (*p=='=') + err_print + ("! Illegal defining occurrence of module name; did you forget `@@ '?"); + @.Illegal defining occurrence...@> +} + +@ Constants of length one can only be single digit numbers, so they can +be stored in a single (unquoted) byte like single letter identifiers. + +Inside strings (and verbatim constructions) {\it any\/} character may in +principle show up, including the character |verb_quote| used to delimit the +verbatim storage of the string. Even if this is hardly a sign of good +programming, the effect on output would be so disastrous that some counter +measure is called for: we replace |verb_quote| by its three-digit octal +escape code, which the user should have written to begin with. Because a \:i +line or a change file match can occur inside a string, we must test +|print_where| at the end of a string. + +@< Copy a constant... @>= +if (id_loc==id_first+1) store_byte(*id_first); /* single digit number */ +else +{ store_byte(verb_quote); /* opening */ + do + if (*id_first==verb_quote) + { ++id_first; store_byte('\\'); store_byte('0'+(verb_quote>>6)); + store_byte('0'+((verb_quote>>3)&7)); store_byte('0'+(verb_quote&7)); + } + else + { if ((eight_bits)(*id_first)>=0x80) store_byte(0xF8); + /* quote 8-bit characters */ + store_byte(*id_first++); + } + while (id_first<id_loc); + store_byte(verb_quote); /* closing */ + if (context==section_body && print_where) + @< Insert the line number into |tok_mem| @> +} + +@ This section should be rewritten on machines that don't use \ASCII. +code internally. It is probably best to use a table indexed by characters +and containing \ASCII. codes for this purpose (like the |xord| array in +\TeX). Incidentally, it would be tempting to write statements like +`|case'\\': c=@'\\';|' here, which would in principle be correct in any +other place, but which would cause circularity in a bootstrapped system like +this. +@^ASCII code dependencies@> + +@< Translate an \ASCII. constant @>= +{ int c=*id_first++; + if (c=='@@' && *id_first++ !='@@') + @/{@; err_print("! Double `@@' should be used in ASCII constant"); + @.Double `@@' should be used...@> + --id_first; + } + if (c=='\\') + { c=*id_first++; + switch (c) + { case 't':c='\011';break; + case 'n':c='\012';break; + case 'b':c='\010';break; + case 'f':c='\014';break; + case 'v':c='\013';break; + case 'r':c='\015';break; + case '0':c='\0';break; + case '\\':c='\134';break; + case '\'':c='\047'; break; + case '\"':c='\042'; break; + default: err_print("! Unrecognised escape sequence"); + @.Unrecognised escape sequence@> + } + } + else + { /* at this point |c| should be converted to its \ASCII. code number */ + @+} + if (id_first!=id_loc) + if (id_loc>id_first) + err_print("! ASCII constant should be single character"); + @.ASCII constant should be...@> + else {@; err_print("! Empty ASCII constant"); c=0; } + @.Empty ASCII constant@> + store_byte(verb_quote); + if (c>=100) store_byte('0'+c/100); @+ if (c>=10) store_byte('0'+(c/10)%10); + store_byte('0'+c%10); @/ + store_byte(verb_quote); +} + +@ Line number marks, which will lead to \&{\#line} directives on output, are +inserted in several places, so we shall call a function to do the necessary +work. For reasons explained below this function scans ahead a bit through +the input, and in doing so it may hit the end of the input; when this +happens it must be reported back to |scan_repl|. Therefore |mark_line| +returns a boolean value which tells whether |input_has_ended|. + +@< Prototypes @>= boolean mark_line(void); + +@~When |mark_line| returns |true|, we should terminate |scan_repl| with +|next_control==new_section|; this is achieved by jumping to |done|, where +some exitialisations are done, and where |next_control| is assigned from the +variable~|a|. + +@< Insert the line... @>= +{@; if (mark_line()) {@; a=new_section; goto done; } } + +@~If |line_output==true|, the following items are stored for recording the +line number: first a byte equal to |0xF9|, then the numeric line number, +and finally the |id_index| of the file name, which is looked up as if it were +an identifier (except that we use the feature of |id_lookup| to accept +null-terminated strings as first argument if the second argument is~|NULL|). +Note that |keep| is not increased; if nothing essential follows this line +number indication then it need not be recorded. We improve compactness of +storage and output a bit by discarding any newlines that are immediately +ahead; the inserted line number will automatically be increased properly. +While reading we might find there is no input left, in which case no line +number is recorded and |true| is returned. + +@c +boolean mark_line(void) +{ while (loc>=limit) + @+ if (!get_line()) return true; /* skip over newlines */ + print_where=false; /* request is being serviced */ + if (line_output) + { store_byte(0xF9);@/ + id_first=changing ? change.name : cur_file_name; + store_two_bytes(changing ? change_line : cur_line); + store_two_bytes(id_index(id_lookup(id_first,NULL,0))); + } + return false; +} + + +@* Scanning a section. +The function |scan_section| starts when \:\ , \:\~, or \:* has been sensed +in the input, and it proceeds until the end of that section. It uses +|section_count| to keep track of the current section number; hopefully +\.{CWEAVE} and \.{\me.} will both assign the same numbers to sections. + +@c +void scan_section (void) +{ ++section_count; + if (loc[-1]=='*') print_section_progress(); + @<Scan the \TeX\ and definition parts of the current section@> + @<Scan the \Cee\ part of the current section@> +} + +@ For \.{\me.} there is no real difference between the \TeX\ and +definition parts of a section; after all, the latter can contain \&{format} +commands that are ignored just like \TeX~text is. So we set up a loop that +stops when |next_control>=begin_C|, and handles the cases |definition| and +|header|, while ignoring anything else. The price we pay for our haste in +skipping the \TeX~part of a section (and \:f commands) is that we may have +to back up after finding \:< in order to scan the whole module name and set +|cur_mod|, which requires calling~|get_next|. In case \:< follows a +|definition| or |header| code, it is scanned by |scan_repl|, and no backing +up is required; therefore the code for backing up and rescanning cannot be +moved outside the loop. + +@<Scan the \TeX\ and definition part...@>= +{ next_control=ignore; /* clear |new_section| */ + do + { if (next_control<definition) + { if ((next_control=skip_ahead())==module_name) + @/{@; loc -= 2; get_next(); } + /* back up and scan the module name itself */ + } + else if (next_control==definition) + { @< Scan and store the identifier after \:d, if not found |continue| @> + scan_repl(preproc_directive); + cur_text->text_link=macro_flag; /* characterise as macro */ + } + else /* |next_control==header| */ + {@; scan_repl(preproc_directive); cur_text->text_link=header_flag; } + if (next_control==module_name) + @< If this is not a defining occurrence, set |next_control=ignore| @> + } while (next_control<begin_C); +} + +@ We retain the strange lexical rule of the \Cee\ preprocessor that a space +after the identifier defined as a macro is quite significant, especially +when followed by `\.{(}'. Together with the spaces in preprocessor lines +contained in a \Cee~part, these are the only kind of unquoted spaces that +are ever stored as tokens, when |line_output| holds. + +@< Scan and store the id... @>= +{ do next_control=get_next(); /* allow white space before identifier */ + while (line_output ? next_control=='\n' + : next_control<0x80 && isspace(next_control)); + if (next_control!=id_code) + @/{@; err_print("! Macro definition ignored, must start with identifier"); + @.Macro definition ignored...@> + continue; + } + @< Store the id... @> + if (isspace((eight_bits)*loc)) store_byte(' '); + /* separate the replacement text from a parameterless macro */ +} + +@ Due to the fact that module names are allowed enclosed in `\pb', it is not +certain that the occurrence of a module name signals the start of the +\Cee~part of the section. The decisive factor will be what follows the +module name: if this is `\.=' or `\.{==}', possibly preceded by `\.+', then +this is really the name of the module defined in this section. If not, then +we require that the next token is a `\.\v' (the one closing `\pb'), unless +we are in compatibility mode, in which case anything goes. We use the fact +that scanning the `\.+' and `\.=' symbols will not destroy the value in +|cur_mod|. + +@< If this is not a defining occurrence, set |next_control=ignore| @>= +{ eight_bits t=get_next(); + if (t=='+') t=get_next(); /* skip an optional `\.+' */ + if (t!='=' && t!=eq_eq) /* then apparently a cited occurrence, ignore it */ + { next_control=ignore; + if (t!='|' && !compatibility_mode) + err_print("! `=' sign missing, module name ignored"); + @.`=' sign missing, module name ignored@> + } +} + +@ If we come to this module with |next_control==module_name|, then the code +of the previous section has just removed the `\.=' or `\.{==}' and optional +`\.+' following the module name. + +@<Scan the \Cee...@>= +{ mod_pointer p; /* module name for the current section */ + switch (next_control) + { default: return; /* no \Cee\ part present */ + case begin_C: p=NULL; break; /* section contributing to unnamed module */ + case module_name: p=cur_mod; /* section contributing to named module */ + } + store_two_bytes(0xD000+section_count); /* record section number */ + scan_repl(section_body); + /* now |cur_text| points to the replacement text */ + @< Link the section body at |cur_text| to the module named |p|, + or to the unnamed module if |p==NULL| @> +} + +@ Sections whose body starts with a bad name (e.g., an ambiguous prefix) will +be treated as if they were unnamed, so the user had better not ignore the +error message. Unnamed sections get a slightly special treatment, which makes +accumulating many of them a bit more efficient than building up a named module +that is defined in many sections. + +@<Link the section...@>= +{ if (p == NULL) /* unnamed section, or bad module name */ + {@; *last_unnamed = cur_text; last_unnamed = &cur_text->text_link; } + else + { text_pointer* q=&p->equiv; /* text for the current module */ + while(*q!=NULL) q=&(*q)->text_link; /* find end of list */ + *q=cur_text; /* add section to module */ + } + cur_text->text_link=NULL; + /* end list, also marking replacement text as a non-macro */ +} + + +@* Phase I processing. +Finally we can wrap everything up and define the global structure +of~Phase~I. + +@<Proto...@>= +void phase_one (void); + /* read all the user's text and compress it into |tok_mem| */ + +@~The only thing left to do is picking up character translations in the limbo +part. + +@c +void phase_one (void) +{ phase=1; section_count=0; reset_input(); + while ((next_control=skip_ahead())!=new_section) + if (next_control==char_trans) @< Store a character translation @> + while (!input_has_ended) scan_section(); /* load all sections */ + check_complete(); /* verify that change file hasn't got out of sync */ +} + +@ The code \:l should be followed (after optional space) by a two-digit hex +number~|c| with |c>=0x80|, white space, and a translation string of at most +|trans_limit| characters, either alphanumeric or underscores, terminated by +another space. + +@< Store a character translation @>= +{ int c; + while (loc<limit && isspace((eight_bits)*loc)) ++loc; /* skip space */ + if (!(isxdigit((eight_bits)loc[0])&& + isxdigit((eight_bits)loc[1])&& + isspace ((eight_bits)loc[2]))) + err_print("! Two-digit hex number and space should follow `@@l'"); + @.Two-digit hex number...@> + else if (sscanf(loc,"%x",&c),c<0x80) + err_print("! You cannot translate characters < 0x80"); + @.You cannot translate...@> + else + { char* p=trans_of(c); int i=0; + loc+=3; @+ + while (find_char() && isspace((eight_bits)*loc)) ++loc; /* skip space */ + if (!input_has_ended) + while (isalnum((eight_bits)*loc) || *loc=='_') + if (++i<=trans_limit) *p++=*loc++; @+ else break; + if (i>0) *p='\0'; /* terminate translation unless none was given */ + @< Report any problems with the translation string @> + } +} + +@ The main contribution of the \:l feature to \.{CWEB} is new syntax, +leading to a variety of new error messages. It is possible to give an empty +translation for a character by terminating the translation string by a +non-space character that is not allowed in identifiers; this is forbidden +however, since an empty translation for~|c| would just make +|translation_exists(c)| fail, making~|c| useless anyway. Even if the +translation string is not empty, terminating it by a non-space character is +considered an error, since this would make the \TeX\ macro used for +formatting the corresponding line of the printed document fail. + +@< Report any problems... @>= +if (i==0) err_print("! Translation string absent after `@@l'"); + @.Translation string...@> +else if (i>trans_limit) err_print("! Translation string too long"); +else if (!isspace((eight_bits)*loc)) + err_print("! Translation string not terminated by space"); + + +@ At the end of the run, if |STAT| was defined and the `\.{+s}' flag +present, we report how much of all the arrays was actually needed. + +@d report(k,c,m) + printf("%lu %ss (out of %lu)\n",(unsigned long)(c),k,(unsigned long)(m)) + +@c +#ifdef STAT +void print_stats (void) +{ print("\nMemory usage statistics:\n"); +@/report("identifier", id_index(id_ptr), max_idents); +@/report("module name", mod_index(mod_ptr), max_modules); +@/report("byte", byte_ptr-byte_mem, max_bytes); +@/report("replacement text", text_ptr-text_table, max_texts); +@/report("token", tok_ptr-tok_mem, max_toks); +} +#endif + + +@* Index. Here is a cross-reference table for the \.{\me.} +processor. All sections in which an identifier is used are listed with +that identifier, except that reserved words are indexed only when they +appear in format definitions, and the appearances of identifiers in +module names are not indexed. Underlined entries correspond to where +the identifier was declared. Error messages and a few other things +like ``\ASCII. code dependencies'' are indexed here too. diff --git a/web/c_cpp/cwebx/cweave.w b/web/c_cpp/cwebx/cweave.w new file mode 100644 index 0000000000..037ebdff3d --- /dev/null +++ b/web/c_cpp/cwebx/cweave.w @@ -0,0 +1,3588 @@ +% This file is part of CWEBx. +% This program by Marc van Leeuwen based on earlier versions by +% D. E. Knuth., Silvio Levy and Frank Jensen. +% It is distributed WITHOUT ANY WARRANTY, express or implied. +% CWEB (Revision: 2.0) % Don Knuth, July 1990 +% Version 3.x, Marc van Leeuwen, December 1993 +% CWEBx 2+1.0, Marc van Leeuwen, August 1994 +% CWEBx 3.0, Marc van Leeuwen, Januari 1995 +% CWEBx 3.02, Marc van Leeuwen, April 1996 +% CWEBx 3.03, Marc van Leeuwen, January 1998 + +% Copyright (C) 1987,1990 Silvio Levy and Donald E. Knuth +% Copyright 1994 Marc A. A. van Leeuwen + +% 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 distributed under the terms of a +% permission notice identical to this one. + +\def\me.{CWEAVE} \def\myroots{\.{WEAVE}} + +% Here is TeX material that gets inserted after \input cwebxmac +\def\xr.{cross-reference} +\def\Cpp{\Cee\PP} +\def\TeXxstring{\TeX\_string} +\def\TeXxlike{\TeX\_like} +\def\skipxTeX{skip\_\TeX} +\def\copyxTeX{copy\_\TeX} + +@i intro.inc % Here is some text that matches the start of CWEAVE + +@d banner "This is CWEAVE (Version x"@+version_string@+")\n" + +@ The following parameters are specific to \.{\me.}; those which are +common to \.{CTANGLE} and \.{CWEAVE} are defined in the file \.{common.inc} +and appear below. Some of these values have been decreased with respect to +their earlier values which were sufficient in the original |WEB| to handle +\TeX; a motivation is given at the common declarations. + +@d max_refs 10000 /* number of \xr.s; must be less than 65536 */ +@d max_toks 10000 /* number of symbols in \Cee\ texts being parsed; + must be less than |65536| */ +@d max_texts 2500 /* number of phrases in \Cee\ texts being parsed; + must be less than |10240| */ +@d max_scraps 4000 /* number of tokens in \Cee\ texts being parsed */ +@d max_no_of_nodes 215 /* number of nodes in search trie for grammar rules, + must be at most |256| */ +@d line_length 80 /* maximal line length for \TeX\ output; + should be less than |256| */ +@d stack_size 400 /* number of simultaneous output levels */ +@d sort_stack_size 500 /* number of identifier lists during sorting */ + +@ The program is built from two compilation units, one with source file +\.{common.w}, which contains a collection of routines and data shared +between \.{CTANGLE} and \.{CWEAVE}, and a second with master source file +\.{cweave.w} containing all code specific to \.{\me.}, and whose +typeset version you are now reading. All compilation units of the \.{CWEB} +system incorporate the file \.{common.inc} containing common declarations. + +\.{\me.} has the following outline. It operates in three phases: first +it reads the source file once for collecting \xr. data, then it +reads the source file for a second time, meanwhile producing the bulk of +the \TeX\ output file, and finally it outputs the information in its tables +from which \TeX\ will produce the list of module names, index and table +of contents. Syntax errors would often be reported identically in the first +two phases, and problems in printing module names similarly in the second +and third phase; to avoid this we check after each phase if any serious +errors were found, and if so, we stop prematurely. \.{\me.} can optionally +be compiled with preprocessor symbols |DEBUG| and/or |STAT| defined; the +former is useful for the user who requires detailed information about the +parsing process, the latter if one wishes to keep track of how much of the +static resources were actually used. + +@c +@< Function prototypes used but not defined in the shared code @>@; +@< Typedef and enumeration declarations @>@; +@< Prototypes @>@; +@< Global variables @>@; + +int main (int argc,char** argv) +{ program=cweave; + make_xrefs=true; + common_init(argc,argv); + @< Set initial values @> + if (show_banner) print(banner); /* print a ``banner line'' */ + @< Store all the reserved words @> + phase_one (); /* read all the user's text and store the \xr.s */ + if (history>harmless_message) wrap_up(); /* stop in case of trouble */ + open_output_file(); + phase_two (); /* read all the text again and translate it to \TeX\ form */ + if (history>harmless_message) wrap_up(); /* stop in case of trouble */ + phase_three (); /* output the \xr. index */ + wrap_up (); /* and exit gracefully */ + return 0; /* for completeness---not reached */ +} + +@ The macro |variant| needs to be defined before we include the file +\.{common.h}, in order to affect the definition of |id_info| and |mod_info| +(see \.{common.w} for an explanation); it refers to the |struct xref_info| +that will be declared later. + +@d variant @;xref_info +@< Prototypes @>= +void phase_one (void); + /* read all the user's text and store the \xr.s */ +void phase_two (void); + /* read all the text again and translate it to \TeX\ form */ +void phase_three (void); /* output the \xr. index */ + +@i common.inc + + +@* Data structures exclusive to {\tt \me.}. @:title@> +Although the eventual typeset output produced after running \.{\me.} +is meant to closely resemble the \.{CWEB} source from which it was created +(or maybe it is the other way around), the task of \.{\me.} is more +complicated than that of \.{CTANGLE}, because it involves a much more +detailed ``understanding'' of the program fragments present in the source. +Therefore the bulk of the program text is concerned with the parsing +process during Phase~II. Detailed information about these matters shall be +given at more appropriate places below; here we only discuss how individual +tokens are tagged on input, and how \xr. infromation is stored. + +The |ilk| field of an |id_info| structure is used to distinguish between +various types of identifiers, as follows: + +\yskip\hang |normal| identifiers are part of the \Cee\ program and +will usually appear in italic type. + +\yskip\hang |roman| identifiers are index entries that appear after \:\^ in +the \.{CWEB} file. + +\yskip\hang |wildcard| identifiers are index entries that appear after \:? +in the \.{CWEB} file. + +\yskip\hang |typewriter| identifiers are index entries that appear after \:. +in the \.{CWEB} file. + +@:section number example@> +\yskip\hang |reference| identifiers are used for explicit \xr.s by the user +within pieces of \TeX~text, allowing us for instance to remark that this is +section@#section number example@> out of a total of@#index@> in this file, +and that the sections having titles are number@#title@>. These \xr.s marks +are set by~\:: and referred to by~\:\#. + +\yskip\hang |type_defined| identifiers have occurred in a |typedef| +declaration. They will parse as types like |int|, but \xr.s to +them will be collected. + +\yskip\hang |TeX_like| identifiers like |TeX| are typeset as user-definable +control sequences. + +\yskip\hang |NULL_like| identifiers like `|NULL|' (\.{NULL}) are also +typeset as (\TeX) control sequences, but in addition are treated like +reserved words for indexing purposes, i.e., only underlined references are +generated. + +\yskip\hang |case_like|, \dots, |while_like| identifiers are \Cee\ +reserved words that will be typeset in boldface. The further distinction +between these |ilk| values determines their distinguished behaviour in +parsing (and consequently in the formatted output), since the values +are also used as category codes when these reserved words are converted +into scraps. Their values, which all exceed |NULL_like|, shall be defined +together with the other category codes. Two special cases of these are +given here, whose distinction is only needed in the lexical analysis, and +which will get a category different from their |ilk|: |const_like| and +|typedef_like| will become |int_like| in parsing (and the same holds for +|type_defined| identifiers). + +@f TeX_like TeX + +@d reserved(a) (a->ilk>=type_defined) + /* whether identifier automatically gets this |ilk| */ +@d unindexed(a) (a->ilk>=NULL_like) + /* whether cross-referencing is suppressed */ + +@< Typedef and enum... @>= +enum @/ +{ normal, /* |ilk| of ordinary identifiers */ + roman, /* |ilk| of roman type index entries */ + wildcard, /* |ilk| of user-formatted index entries */ + typewriter, /* |ilk| of typewriter type entries */ + reference, /* |ilk| of identifiers used for explicit \xr.s */ + type_defined, /* |ilk| of identifiers that are defined by |typedef| */ + TeX_like, /* |ilk| of identifiers with user-given control sequences */ + NULL_like, /* |ilk| of \.{NULL} */ + const_like, typedef_like /* special reserved words */ +}; + +@ Besides the memory used for storing names, another large memory area is +used in \.{\me.} for keeping the \xr. data. All uses of the name~|p| are +recorded in a linked list beginning at |p->xref|, which is an |xref_pointer| +pointing into the |xmem| array (here we use the pointer field for additional +information in |id_info| and |mod_info| structures, which was called |equiv| +in~\.{CTANGLE}). The elements of |xmem| are structures consisting of an +integer, |num|, and the index |next| of another element of |xmem|. If +|x==p->xref|, the value of |x->num| is a section number where |p| occurs, +plus a multiple of |cite_flag| that indicates the nature of the occurrence, +or it is |file_flag| marking the fact that |p| is a module name which is the +name of an auxiliary output file (for \.{CTANGLE}). The next such \xr. +for~|p|, if any, is |xmem[x->next]|. + +Since the entries of |xmem| are small but numerous, and the \xr. lists are +not traversed very frequently, a significant amount of space is saved by +storing a |sixteen_bits| index as a link to the next node rather than an +|xref_pointer|, and at very little cost in performance. The main price paid +is in terms of elegance, as somewhat different methods of traversal of the +lists are appropriate depending on whether the traversal may leed to the +insertion of a new node into the list or not. If no such insertion is +needed, then we can work with |xref_pointer| values as if we were dealing +with an ordinary linked list, provided only that the pointer to a successor +node is obtained by invoking a macro |next_xref| instead of selecting the +link field. If however we may need to insert a new node into the list, the +the most convenient method is to use a pointer to a link field, which then +must have type |(sixteen_bits*)|; for that case the macros |xnum| and +|xlink|, which select the |num| and |next| fields respectively of a node +specified by its index, and which yield expressions that one can assign to +or take the address of, are more useful than |next_xref|. Since the +beginning of the list is indicated by a |xref_pointer| rather than by its +index, we need to convert that pointer into an index (for which the macro +|xref_index| is supplied) when using the second method, and store this +index in a local variable, not forgetting to update the original +|xref_pointer| in case an insertion occurs at the front of the list. + +During collection of \xr.s, the lists are basically in decreasing order by +section number, as a result of the fact that new nodes are added at the +beginning of the list (for module names the situation is actually a bit more +complicated, as described below), so that in that phase the |next| field +really refers to the previous \xr. for the same name; at the end of Phase~I, +however, the lists are reversed. + +The global variable |xref_switch| is set either to |def_flag| or to zero, +depending on whether the next \xr. to an identifier is to be underlined or +not in the index. This switch is set to |def_flag| when \:! or \:d or \:f is +scanned, and it is cleared to zero when the next identifier or index entry +\xr. has been made. Similarly, the global variable |mod_xref_switch| is +either |def_flag|, |cite_flag|, or zero, depending on whether a module name +is being defined, cited, or used. During Phase~II a number of \xr.s for +identifiers will be changed into underlined ones, when the identifier is +found to occur in a declaration or function definition, since these +occurrences cannot be reliably recognised in Phase~I. + +@d xref equiv_or_xref +@d next_xref(x) (&xmem[(x)->next]) +@d xnum(i) (xmem[i].num) +@d xlink(i) (xmem[i].next) +@d xref_index(p) ((sixteen_bits)((p)-xmem)) +@d cite_flag 0x4000 + /* a |sixteen_bits| power of 2 that is at least |max_sections+2| */ +@d def_flag 0x8000 /* twice that */ +@d num_mask (cite_flag-1) /* a bit-mask for working modulo |cite_flag| */ + +@<Typedef...@>= +typedef struct xref_info +{ sixteen_bits num; /* section number plus a multiple of |cite_flag| */ + sixteen_bits next; /* index of the next \xr. in the list */ +} xref_info, *xref_pointer; + +@ We use an allocation pointer~|xref_ptr| that indicates how much of |xmem| +is already in use. Unlike in the other cases of sequential allocation, where +the allocation pointer points to the first free position, |xref_ptr| points +to the last occupied position in |xmem|; the first node |xmem[0]| is already +in use when \.{\me.} starts. All allocation of |xref_info| nodes is +performed by calling |make_xref(n,i)|, where |n| is the |num| field of the +new node, and |i| is the index of its successor in the list; after +|make_xref| has been invoked, |xref_ptr| points to the new node. + +@d make_xref(n,i) + /* create \xr. node with |num==n| and successor |xmem[i]| */ + if (++xref_ptr >= &xmem[max_refs]) + overflow ("cross-reference"); @.cross-reference capacity exceeded@> + else xref_ptr->num=n, xref_ptr->next=i @; + +@<Global...@>= +xref_info xmem[max_refs]; /* contains \xr. information */ +xref_pointer xref_ptr = &xmem[0]; /* the last used position in |xmem| */ +sixteen_bits xref_switch = 0, mod_xref_switch = 0; + /* either zero or |def_flag| */ + +@ A first node is initially allocated, with its |num==0|; the node is used +to initialise \xr. lists (see |init_id_name| and |init_module_name| below), +and serves as a sentinel at the end of those lists. + +@<Set init...@>= +xnum(0)=0; /* sentinel node terminating \xr. lists */ + +@ A new \xr. for an identifier~|p| is formed by calling |new_id_xref(p)| +with |section_count| and |xref_switch| set to the appropriate value. +Multiple references to the same section are merged (with underlining +activated if any reference requires so) while non-underlined references to +one-letter identifiers, reserved words or identifiers with |ilk==NULL_like| +(like `\.{NULL}') are ignored. Explicit \xr.s set by~\:: are never +underlined, so |xref_switch| is ignored in this case. If the user has set +the |no_xref| flag (the \.{-x} option of the command line), |new_id_xref| is +active only for explicit \xr.s; when are reading a header file included +by~\:h, it is inactive altogether (but since \:: is disabled in such header +files, this case is treated together with the |no_xref|~case). + +@d no_xref (!flags['x']) +@d make_xrefs flags['x'] /* should cross references be output? */ + +@c +void new_id_xref (id_pointer p) +{ sixteen_bits f=xref_switch; xref_switch=0; + if (p->ilk==reference) f=0; + else if (f==0 && (unindexed(p) || length(p)==1) + || no_xref || including_header_file) return; + if ((p->xref->num&num_mask)==section_count) p->xref->num|=f; + else + {@; make_xref(section_count|f,xref_index(p->xref)); p->xref=xref_ptr; } +} + +@ The \xr. lists for module names are slightly different. Suppose that a +module name is defined in sections $d_1$,~\dots,~$d_k$, cited in sections +$c_1$,~\dots,~$c_l$, and used in sections $u_1$,~\dots,~$u_m$, where the +sequences of $d$'s, $c$'s and $u$'s each are in increasing order. Then its +list will contain |@t$d_k$@>+def_flag|, \dots, |d1+def_flag|, +|@t$c_l$@>+cite_flag|, \dots, |c1+cite_flag|, $u_m$, \dots, $u_1$,~$0$, in +this order; the final~$0$ is the sentinel node, which allows the loops below +to be a little faster. If the module name specifies an output file (with the +\:( feature) then a node containing the special value |file_flag| is +prepended to this list. The special ordering described here only serves for +efficiency of insertion, and after Phase~II the order will be adjusted to +the more natural sequence |d1+def_flag|, \dots, |@t$d_k$@>+def_flag|, +|c1+cite_flag|, \dots, |@t$c_l$@>+cite_flag|, $u_1$, \dots,~$u_m$,~0. + +There can be multiple applied or cited occurrences of some module name +within one section, but only one defining occurrence. Therefore, in the +former cases we perform a test to avoid duplicate references. + +@d file_flag (cite_flag-1) + /* a distinguished value, not divisible by |cite_flag| */ + +@c +void new_mod_xref (mod_pointer p) +{ sixteen_bits head, *q, m=section_count+mod_xref_switch; + if (p->xref->num==file_flag) q=&p->xref->next; /* skip |file_flag| */ + else head=xref_index(p->xref),q=&head; + if (mod_xref_switch!=def_flag) + { while (xnum(*q)>m) + q=&xlink(*q); /* skip the $d_i$'s and possibly $c_i$'s */ + if (xnum(*q)==m) return; /* don't duplicate */ + } + make_xref(m,*q); mod_xref_switch=0; + if (q==&head) p->xref=xref_ptr; @+ else *q=xref_index(xref_ptr); +} + +@ When a module name starts with \:(, we will call |set_file_flag|. + +@c +void set_file_flag (mod_pointer p) +{@; if (p->xref->num!=file_flag) + {@; make_xref(file_flag,xref_index(p->xref)); p->xref=xref_ptr; } +} + +@ A third large area of memory is used for sixteen-bit `tokens', which +appear in short lists similar to the strings of characters in |byte_mem|. +Token lists are used to contain the result of \Cee\ code translated into +\TeX\ form; further details about them will be explained later. Sequences of +tokens which have been delimited as such are called texts, and can be +accessed via elements of the array |text_mem|. Only a pointer to the first +token of the sequence is stored, so in order to be able to compute the +extent of a token list one needs a pointer into |text_mem|, which is called +a |text_pointer|. + +@<Typedef...@>= +typedef sixteen_bits token, * token_pointer, ** text_pointer; + +@~The first position of |tok_mem| that is unoccupied by replacement text is +called |tok_ptr|, and the first unused location of |text_mem| is called +|text_ptr|. Since we already know that the next text to be stored will start +at |tok_ptr|, we make sure that |*text_ptr==tok_ptr| whenever we are not in +the process of appending tokens. In this way we can also always find the end +of any stored text as the beginning of the next one, which is computed by +the macro |text_end|. + +@d tok_mem_end (&tok_mem[max_toks]) /* end of |tok_mem| */ +@d text_mem_end (&text_mem[max_texts]) /* end of |text_mem| */ +@d text_index(p) ((sixteen_bits)((p)-text_mem)) +@d text_at(i) (&text_mem[i]) +@d text_begin(p) (*(p)) +@d text_end(p) (*(p+1)) + +@<Global...@>= +token tok_mem[max_toks]; /* tokens */ +token_pointer text_mem[max_texts]; /* directory into |tok_mem| */ +token_pointer tok_ptr = tok_mem; /* first unused position in |tok_mem| */ +text_pointer text_ptr = text_mem; /* first unused position in |text_mem| */ +#ifdef STAT +token_pointer max_tok_ptr = tok_mem; /* largest value of |tok_ptr| */ +text_pointer max_text_ptr = text_mem; /* largest value of |text_ptr| */ +#endif + +@ We initialise our invariant. + +@<Set init...@>= +*text_ptr=tok_ptr; + +@ Here are the three functions needed to complete |id_lookup|. For a +name~|x| stored in the table to match a string of length~|l| starting at~|q| +and of requested ilk~|ilk|, it is required that the names match exactly, and +either |x->ilk==ilk|, or |ilk==normal| and |x->ilk| specifies some reserved +word (where the ilks |TeX_like| and |NULL_like| are considered as reserved). +This rule means that if we look up an ``identifier'' whose name matches +reserved word stored in the table, the reserved word rather than an +identifier is returned, so that recognition of reserved words is automatic +when the tables are properly initialised. The other two functions install +the sentinel node |xmem[0]| at the end of each \xr. list as it is created. + +@c +boolean names_match (id_pointer x, char* q, int l, int ilk) +{ char* p=name_begin(x); + if ((x->ilk==ilk || ilk==normal && reserved(x))) + @/{@; while (--l>=0) if (*p++!=*q++) return false; return *p=='\0'; } + else return false; +} + +void init_id_name (id_pointer p, int t) +{@; p->ilk = t; p->xref = &xmem[0]; } + +void init_module_name (mod_pointer p) {@; p->xref=&xmem[0]; } + + +@* Skipping and copying \TeX\ material. @:title@> +The source file consists roughly speaking of two sorts of input, namely +\TeX\ material and pieces of \Cee~text. The limbo part is entirely the +realm of \TeX, and the sections are divided (possibly quite unevenly) +into a \TeX~part, and the remainder which is~\Cee, although the distinction +is not quite as pure here due to `\pb' interjections and comments. +The meddling of \.{\me.} (and indeed of all of \.{CWEB}) in the \TeX\ +parts is extremely superficial: it is restricted to skipping or copying it, +only replacing \:@@ by~`\.{@@}', and the main interest in these parts +of the source is to find out where they end. By contrast the \Cee\ fragments +have to be broken up into meaningful parts by \.{\me.} in order to perform +the required operations of collecting \xr.s and formatting for +pretty-printing output. Before we study that more complicated process of +lexical scanning, let us study the easier question of what happens with the +\TeX~parts. + +The three tasks of passing over limbo material, over the \TeX~part of a +section, and over a comment embedded in the \Cee~part are sufficiently +different that they merit separate routines. Moreover, the entire source is +scanned twice by \.{\me.}, with different purposes, and so the scanning +routines have two flavours. We could choose either to use distinct scanning +routines on both passes, or to have a single all-purpose scanning routine. +The first option is more attractive if the functions are simple and small, +and can perform tasks specific to their pass on-the-fly, while the second +option is to be favoured if the scanning task becomes complicated, to avoid +near-duplication of substantial pieces of code. We have chosen the first +option for the routines that pass over ordinary \TeX\ material, both in +limbo and in sections, and the second option for the function passing over +comments; for the lexical scanning of \Cee~text we shall also write a single +function for both phases. Where distinct routines are used we must of +course make sure that they are sufficiently similar that they will make the +same decisions about where these parts of the source text end. + +@ Although the functions to be described here have a quite trivial task, +they will eventually run into a control code that terminates their action, +and so we have to be aware of tokens that do not belong in the \TeX~part. +The relevant tokens form only a small subset of the tokens that can be +recognised by the function |get_next| that scans \Cee~text, so we defer +an enumeration of possible tokens to a later point. At this point it +is sufficient to know there is an array |ccode| which translates characters +that may follow `\.{@@}' in a control code into a numeric value greater +than any (unsigned) character, which values have symbolic names and are +arranged in order of increasing significance. In particular any value +greater than or equal to |format| terminates the \TeX~part of a section, +and the largest value of all is |new_section|. By using the same encoding, +the \TeX~text scanning functions can return a value directly usable for +continuing the scan into \Cee~territory. Tokens consisting of a single +character are represented as that character itself by |get_next|, and at +this point the relevant case is the character~`\.\v' which interrupts +\TeX~text. + +Like in |CTANGLE|, we always access |ccode| by means of the macro |code_of|. + +@d code_of(c) ccode[(unsigned char)(c)] + +@<Global...@>= +int ccode[UCHAR_MAX + 1]; /* meaning of a character following `\.{@@}' */ + +@ This section performs the simplest of all scanning operations, namely to +skip through portions of the input that are not in any sections, i.e., that +precede the first section, on the first pass. It uses the fact that +|get_line| places a~|' '| at~|*limit|, so that placing the sentinel cannot +inadvertently create a token \.{@@@@}. Although a few control codes like \:q +are allowed in limbo, they can be ignored here, since they do not affect the +recognition of the end of the limbo part. An exception is the format code +\:s, which must be obeyed in this first phase; the code doing this will be +given together with the processing of other format codes. After the code +below is executed, the value of |input_has_ended| will tell whether or not a +section has actually been found. + +@< Skip the limbo part @>= +while (find_char()) +{ limit[1]='@@'; /* place a sentinel */ + while (*loc++!='@@') {} + if (loc<=limit) + { int c=code_of(*loc++); + if (c==new_section) break; + if (c==format) @< Process a format code in limbo @> + } +} + +@ In Phase~II, the corresponding task is slightly less trivial, as the limbo +material must also be copied to the output; it is performed by the +|copy_limbo| function. Output is generated by calling |out| for ordinary +characters, and |finish_line| when a completed line is to be sent out. No +spaces or tab marks are copied by |copy_limbo| into the beginning of a line +(indicated by the macro |output_line_empty|), nor by the function |copy_TeX| +below. As a consequence a line with only spaces and removed items (like \:q, +\:s, or for |copy_TeX| \xr. entries for the index) will produce a completely +empty line of output; such lines will only be actually written out by +|finish_line| if they come from a completely blank input line. Any pair \:@@ +is replaced by `\.{@@}', and apart from this only the control codes \:q, +\:s, and \:l are allowed to be present before the first section is +encountered. Note that we have chosen to detect violations of this rule in +Phase~II; on other occasions however, errors were checked in Phase~I, and we +can then assume that errors are absent in Phase~II, as we would not even get +there otherwise. + +@c +void copy_limbo (void) /* copy \TeX\ code until the next section begins */ +{ while (loc<=limit || (finish_line(),get_line())) + { eight_bits c; + limit[1]='@@'; /* place a sentinel */ + while ((c=*loc++)!='@@') + if (!(output_line_empty() && isspace(c))) out(c); + if (loc<=limit) + switch(code_of(*loc++)) + { + case new_section: return; + case ignored_text: get_control_text(); break; + case format: get_next(); get_next(); break; /* skip two identifiers */ + case char_trans: out_str("\\ATL "); break; @.\\ATL@> + default: err_print("! Double @@ required in limbo part"); + @.Double @@ required...@> + /* fall through */ + case at_sign_image: out('@@'); + } + } +} + +@ The function |skip_TeX| is used on the first pass to skip through the +\TeX\ code at the beginning of a section. It returns the next control code +or `\.\v' found in the input. A |new_section| is assumed to exist at the +very end of the file. Any comment character `\.{\%}' that is not escaped +(with a backslash) will disable recognition of `\.\v' and cross-referencing +control codes for the remainder of the line, so that no spurious \xr.s +will be created to commented-out text. Recognition of control codes that +terminate the \TeX~part of the section will still be enabled however, to +maintain synchronisation with the activities of |CTANGLE|. + +@f skip_TeX TeX + +@c +int skip_TeX (void) /* skip past pure \TeX\ code */ +{ char c; + while (find_char()) + { limit[1]='@@'; + while ((c=*loc++)!='@@' && c!='%') + if (c=='|') return c; + else if (c=='\\' && *loc!='@@') ++loc; + /* ignore `\.{\\\%}' and `\.{\\\v}' */ + if (loc<=limit) + if (c=='@@') return code_of(*loc++); + else /* ignore remainder of line unless a major control code occurs */ + do @+ + if ((c=*loc++)=='@@' && code_of(*loc++)>=format) + return code_of(loc[-1]); + while (loc<limit); + } + return new_section; +} + +@ During Phase~II, the function |copy_TeX| processes the \TeX\ code at the +beginning of a section; for example, the words you are now reading were +copied in this way. Like |skip_TeX|, it returns the next control code or +`\.\v' found in the input, and ignores anything that is commented out, +except control codes that terminate the \TeX~part; the characters after the +`\.{\%}' are not even copied to the output. (Note that in the limbo part +comments {\sl are\/} copied, allowing any commented-out header information +such as copyright notices to be passed on to the \TeX~file.) + +@f copy_TeX TeX + +@c +int copy_TeX (void) /* copy pure \TeX\ material */ +{ eight_bits c; /* current character being copied */ + while (loc<=limit || (finish_line(),get_line())) + { limit[1]='@@'; + while((c=*loc++)!='@@') + { if (c=='|') return '|'; + if (!(output_line_empty() && isspace(c))) out(c); + if (c=='%') break; + if (c=='\\' && *loc!='@@') out(*loc++); + /* copy `\.{\\\%}' and `\.{\\\v}' */ + } + if (loc<=limit) + if (c=='@@') return code_of(*loc++); + else /* ignore remainder of line unless a major control code occurs */ + do + if ((c=*loc++)=='@@' && code_of(*loc++)>=format) + return finish_line(),code_of(loc[-1]); + while(loc<limit); + } + return new_section; +} + +@ The final \TeX~scanning function is |scan_comment| which scans the +\TeX~text in comments. It is used both in Phase~I and in Phase~II, but +stores the characters it sees only if |phase==2|. The function returns the +token that terminated its action, which is one of |end_comment|, `\.\v' or +(in erroneous cases) |new_module|. + +@< Prototypes @>= +int scan_comment(int* bal, boolean one_liner); + /* skip or copy \TeX\ text in comments */ + +@~The function |scan_comment| counts the braces it encounters to see if they +are balanced; the parameter |bal| points to an integer variable keeping +track of the brace level (it can be positive initially if |scan_comment| is +called after `\pb' occurring within braces in a comment). This feature is +mainly a remnant from \.{CTANGLE}'s Pascal origins, since there comments are +closed by `\.\}', and counting is necessary to establish which braces are +for \TeX\ and which are not. Although there is no such need in \Cee, the +feature is retained. The parameter |one_liner| tells whether we are dealing +with a \Cpp\ one-line comment. + +@c +int scan_comment (int* bal, boolean one_liner) +{ char c; boolean forced_out=false; /* prematurely terminated? */ + while (one_liner ? loc<limit + : find_char() && (*loc!='*' || loc[1]!='/' )) + @< Handle next character; if |'|'|, |return| it, + if a new section starts, set |forced_out| and |goto done| @> + if (input_has_ended) + forced_out=true,err_print("! Input ended in mid-comment"); + @.Input ended in mid-comment@> + else if (!one_liner) loc+=2; /* move past `\.{*{}/}' */ +done: + if (*bal>0) err_print("! Too few closing braces in comment"); + @.Too few closing braces...@> + return forced_out ? new_section : end_comment; +} + +@ Like elsewhere, `\.@@' should be doubled in comments; |scan_comment| +replaces them by a single `\.@@'. In Phase~II, instead of copying the \TeX\ +material into the output buffer like |copy_TeX|, |scan_comment| copies it +into the token memory, since comments will be output together with the rest +of the formatted \Cee~code. To this end it calls the macro |app_char_tok(c)| +rather than |out(c)|. When including header files comments should be skipped +with no processing at all, so this module is almost completely disabled in +that case. + +@< Handle next character... @>= +if (including_header_file) ++loc; /* don't process characters here */ +else +{ switch(c=*loc++) + { + case '|': return '|'; /* beginning of `\pb' inside comment */ + case '@@': + if (*loc++!='@@') + if (code_of(loc[-1])!=new_section) + err_print("! Double @@ required in comment"); + @.Double @@ required...@> + else + {@; err_print("! Section ended in mid-comment"); + @.Section ended in mid-comment@> + forced_out=true; goto done; + } + break; + case '\\': + if (*loc!='@@') {@; if (phase==2) app_char_tok(c); c=*loc++; } + break; + case '{': ++*bal; break; + case '}': + @+if (*bal>0) --*bal; + @+else err_print("! Extra } in comment"); + @.Extra \} in comment@> + @+break; + case '/': @+ if (*loc=='*') err_print("! Nested comment"); + @.Nested comment@> + + } + if (phase==2) app_char_tok(c); +} + + +@* Getting the next token. @:title@> +We now come to the most important lexical scanning function, |get_next|, +which locates and classifies the next token of \Cee~text. Before we give the +function itself, we shall first specify which kind of values it can return. +The result value is an integer, which can either be at most |UCHAR_MAX|, in +which case the character code represents that character, or possibly a +compressed multi-character symbol as defined by the macros +in~\.{common.inc}, or it is one of the values greater than |UCHAR_MAX| +defined in the enumeration below, which indicates a particular token or +class of tokens or a control code. (Actually, |underline| and +|trace0|,~\dots,~|trace3| are never returned by |get_next| because they are +treated within the scanner; they are included however because they are +distinguished control codes.) The ordering of this enumeration is designed +to simplify \.{\me.}'s logic; for example, from |format| on, larger +numbers are given to the control codes that denote more significant +milestones, and the code of |new_section| is the largest of all. Also, the +sequence |identifier|,~\dots,~|xref_mark| runs parallel to the first five +|ilk|~codes. For efficiency all codes that produce a fixed scrap are placed +at the beginning; these are all codes preceding |ignore|. The code \:> is +treated as ignored because it should not occur in places where we are not +searching for it. + +@f TeX_string TeX + +@<Typedef and enum...@>= +enum @/ +{ at_sign_image = UCHAR_MAX+1, /* quoted `\.{@@}' */ + or, /* \:v */ + mul_assign, div_assign, mod_assign, plus_assign, minus_assign, + left_assign, right_assign, and_assign, xor_assign, or_assign, + sh_sh, ellipsis, colon_colon, @/ + start_preproc, end_preproc, /* begin and end of a preprocessor directive */ + join, /* \:\& */ + thin_space, /* \:, */ + math_break, /* \:\v */ + line_break, /* \:/ */ + big_line_break, /* \:) */ + no_line_break, /* \:+ */ + backup_line, /* \:\\ */ + pseudo_semi, /* \:; */ + force_expr_open, force_expr_close, /* \:[, \ \:] */ + include_preproc, /* \:p */ +@) + ignore, /* control code of no interest to \.{\me.} */ + constant, string, /* the next five codes should remain in this order */ + identifier, /* any (possibly reserved) word found in \Cee\ text */ + xref_roman, xref_wildcard, xref_typewriter, xref_mark, + /* \:\^, \ \:?, \ \:., \ \:: */ + refer, /* \:\# */ + TeX_string, /* \:t */ + verbatim, /* \:= */ + ignored_text, /* \:q */ + char_trans, /* \:l */ + ASCII_code, /* \:' */ + begin_comment, end_comment, + underline, /* \:! */ +#ifdef DEBUG + trace0, trace1, trace2, trace3, /* \:0, \dots, \:3 */ +#endif + format, /* \:f */ + definition, /* \:d */ + header, /* \:h */ + begin_C, /* \:c */ + module_name, /* \:< and \:( */ + new_section /* \:\ , \:\~ and \:* */ +}; + +@ Here we initialise the |ccode| table in accordance with the comments given +in the enumeration above. + +@<Set ini...@>= +{ unsigned char c=0; + do ccode[c] = isspace(c) ? new_section : ignore; while(c++!=UCHAR_MAX); + ccode['@@'] = at_sign_image; +@/ccode['v'] = ccode['V'] = or; +@/ccode['!'] = underline; /* set definition flag */ +@/ccode['^'] = xref_roman; /* index entry to be typeset normally */ +@/ccode['?'] = xref_wildcard; /* index entry to be in user format */ +@/ccode['.'] = xref_typewriter; /* index entry to be in typewriter type */ +@/ccode[':'] = xref_mark; ccode['#']=refer; /* explicit \xr.s */ +@/ccode['t'] = ccode['T'] = TeX_string; /* \TeX\ box within \Cee\ text */ +@/ccode['='] = verbatim; +@/ccode['q'] = ccode['Q'] = ignored_text; +@/ccode['l'] = ccode['L'] = char_trans; +@/ccode['\''] = ASCII_code; +@/ccode['&'] = join; /* concatenate two tokens */ +@/ccode[','] = thin_space; +@/ccode['|'] = math_break; +@/ccode['/'] = line_break; +@/ccode[')'] = big_line_break; +@/ccode['\\']= backup_line; +@/ccode['+'] = no_line_break; +@/ccode[';'] = pseudo_semi; +@/ccode['['] = force_expr_open; ccode[']'] = force_expr_close; +ccode['p'] = ccode['P'] = include_preproc; +#ifdef DEBUG + ccode['0'] = trace0; ccode['1'] = trace1; + ccode['2'] = trace2; ccode['3'] = trace3; +#endif +@/ccode['f'] = ccode['F'] = ccode['s'] = ccode['S'] = format; +@/ccode['d'] = ccode['D'] = definition; +@/ccode['h'] = ccode['H'] = header; +@/ccode['c'] = ccode['C'] = begin_C; + /* \Cee\ text in unnamed module */ +@/ccode['<'] = ccode['('] = module_name; /* beginning of a module name */ +@/ccode['~'] = ccode['*'] = new_section; /* beginning of a new section */ + if (compatibility_mode) + @< Reset some control codes to match \LKC. @> +} + +@ In \.{CWEBx} there are a few control codes that also exist in Levy/Knuth +\.{CWEB} but have a different meaning. In compatibility mode we reassign the +meaning of these codes to that of \LKC., making their usual function +inaccessible, since it is not intended that hybrid programs should be +written using the codes of \LKC. together with features particular to +\.{CWEBx}. +@^Levy/Knuth \.{CWEB}@> + +@< Reset some control codes... @>= +{ ccode['h']=ccode['H']=include_preproc; /* \:h means \:p */ + ccode['p']=ccode['P']=begin_C; /* \:p means \:c */ + ccode['#']=big_line_break; /* \:\# means \:) */ + ccode[':']=xref_wildcard; /* \:: means \:? */ +} + +@ We come now to the definition of |get_next| itself. When returning certain +values it will have performed some additional actions, as follows. + +\yskip\hang |constant|, |string|, |TeX_string|, |verbatim|: The token is + copied into |mod_text|, with slight modifications; the global variables + |id_first| and |id_loc| are set to the beginning and ending-plus-one + locations in |mod_text|. + +\yskip\hang |identifier|, |xref_roman|, |xref_wildcard|, |xref_typewriter|, + |xref_mark|, |refer|, |module_name|: The global variable |cur_id| or + |cur_mod| will point to the identifier, control text or module name that + has just been scanned (for the |xref_roman|, \dots, |xref_mark| this is + only true if |phase==1|). + +\yskip\hang |underline|: this value is not even returned. If |get_next| sees + \:!, it sets |xref_switch| to |def_flag| and goes on to the next token. +\yskip + +Preprocessing directives complicate scanning in two ways: first, their +lexical structure is different from that of ordinary text, and second, a +preprocessor directive can occur at any place in a \Cee~text, so +syntactically it should be treated like a comment. The first issue is +resolved by maintaining a static variable |preprocessing| which is~$0$ in +ordinary \Cee~text, $2$ in \&{\#include} directives, and~$1$ in other +preprocessor directives. The second issue must be dealt with during parsing, +and in order to be able to do so, |get_next| emits special tokens +|start_preproc| and |end_preproc| when it has sensed the boundaries of a +preprocessor directive. + +@<Global...@>= +id_pointer cur_id; /* identifier or index entry just scanned */ +mod_pointer cur_mod; /* module name just scanned */ +int preprocessing=0; + +@ As one might expect, |get_next| consists mostly of a big switch that +branches to the various cases that can arise. Any character |c>=0x80| that +is not contained in a string or comment is assumed to belong to an +identifier. + +@< Prototypes @>= int get_next (void); +@~@c +int get_next (void) /* produces the next input token */ +{ eight_bits c; /* the current character */ +restart: + if (!find_char()) {@; preprocessing=0; return new_section; } + @< If a preprocessor line has ended, handle it and |return end_preproc| @> + if ((c=*loc++)=='@@') + @< Get control code and possibly module name, and either |return| it, + or |goto restart| if ignored or handled within |get_next| @> + if (isspace(c)) + if (preprocessing>0) return ' '; /* keep spaces in preprocessor lines */ + else goto restart; /* ignore other white space */ + if (c=='L' && (*loc=='\'' || *loc=='"')) + {@; get_string(); return string; } + if (isalpha(c) || c=='_' || c>=0x80) + {@; @< Get an identifier @> return identifier; } + if (isdigit(c) || c=='.' && isdigit((eight_bits)*loc)) + @/{@; @< Get a numeric constant @> return constant; } + if (c=='\'' || c=='"' || (c=='<' && preprocessing==2)) + {@; get_string(); return string; } + if (c=='#' && loc==&buffer[1]) + @/{@; @< Handle start of preprocessor directive; maybe |goto restart| @> + return start_preproc; } + if (c=='\\' && preprocessing>0 && loc==limit) + { ++loc; /* move past |limit|, so |get_line| will be called */ + goto restart; + } + @< Compress multi-character tokens @> + return c; +} + +@ When a `\.\#' is seen as the first character of a line, |get_next| returns +a special code |start_preproc| and sets |preprocessing| to a non-zero value. +Because of the freakish use of `\.<' and `\.>' to delimit a file name in +lines that start with `\.{\#include}', those lines get an extra-special +treatment, and |preprocessing| is set to~$2$ rather than to~$1$. If however +we encounter a `\.{\#include}' directive when we are already busy reading a +header file due to \:h in Phase~I, then we actually execute the directive, +and start reading the nested header file; this action will be transparent to +the stream of tokens produced, so we |goto restart| to fetch the first token +from the newly opened file. + +@<Handle start of prep...@>= +{ while (loc<limit && isspace((eight_bits)*loc)) ++loc; + /* allow spaces after `\.\#' */ + if (limit-loc>=7 && strncmp(loc,"include",7)==0) /* `\.{\#include}' line */ + if (including_header_file) + @/{@; loc+=7; push_input_file(true,false); goto restart; @+} + /* start nested header file */ + else preprocessing=2; + else preprocessing=1; +} + +@ When we get to the end of a preprocessor line, we lower the flag and +send a code |end_preproc| (unless the last character was a `\.\\', but that +case has already been taken out and never comes here). + +@<If a prep...@>= +if (preprocessing>0 && loc==limit) +{@; preprocessing=0; return end_preproc; } + +@ The following code assigns values to the compound operators `\.{++}', +`\.{--}', `\.{->}', `\.{>=}', `\.{<=}', `\.{==}', `\.{<<}', `\.{>>}', +`\.{!=}', `\.{\v\v}', and `\.{\&\&}', to the special symbols `\.{/*}', +`\.{*/}', `\.{//}', `\.{...}', `\.{::}', and `\.{\#\#}', and moreover, if not +in compatibility mode, to the assignment operators `\.{*=}', `\.{/=}', +`\.{+=}', `\.{-=}', `\.{>>=}', `\.{<<=}', `\.{\&=}', `\.{\^=}', `\.{\v=}'. +Although the comment ending token `\.{*/}' should never occur when we are +scanning \Cee~text, we must recognise it in order to detect unclosed `\pb' +constructions within comments (fortunately no legal combination of operators +causes an adjacent sequence `\.{*/}'; only `\.*' immediately followed by a +comment could cause it, and the user should simply not do this). The reason +that in compatibility mode we are forced to follow suit with \LKC. +@^Levy/Knuth \.{CWEB}@> in not combining assignment operators is a truly +stupid one: @:truly stupid@> the Stanford GraphBase @^Stanford GraphBase@> +contains fragments like `\.{\$\v n1\v=n\_1\$}' which will cause trouble if +`\.{\v=}' is parsed as a single symbol (of course the fragments should have +been written as `\.{\v n1\v\$\{\}=n\_1\$}', but we have set the goal to handle +the GraphBase as it is, and moreover its files cannot be changed). For +splitting up the assignment operators here, we shall have to pay the price of +including syntax rules (in compatibility mode) for recombining them. + +The macros defined below are for strictly local use, so we don't mind that +they could mess things up if used in the |if|-part of an |if|-|else| +statement. Also, there is no need to test whether |*loc| or |loc[1]| lie +beyond |limit|, since if they do, they are not evaluated anyway because we +know |*limit==' '|. Note that the three-symbol assignment operators +`\.{>>=}' and~`\.{<<=}' must be tested before their non-assignment +counterparts `\.{>>}' and~`\.{<<}'. + +@d compress2(char2,code) if (*loc==char2) return ++loc, code @; +@d compress3(char2,char3,code) + if (*loc==char2 && loc[1]==char3) return loc+=2, code @; +@d comp_ass_op2(code) + if (*loc=='=' && !compatibility_mode) return ++loc, code @; +@d comp_ass_op3(char2,code) + if (*loc==char2 && loc[1]=='=' && !compatibility_mode) return loc+=2,code @; + +@<Compress multi...@>= +switch (c) { +case '/': compress2('*',begin_comment); @+ + if (C_plus_plus) compress2('/',begin_comment); + comp_ass_op2(div_assign); break; +case '*': compress2('/',end_comment); comp_ass_op2(mul_assign); break; +case '%': comp_ass_op2(mod_assign); break; +case '+': compress2('+',plus_plus); comp_ass_op2(plus_assign); break; +case '-': compress2('-',minus_minus); compress2 ('>', minus_gt); + comp_ass_op2(minus_assign); break; +case '=': compress2('=',eq_eq); break; +case '>': compress2('=',gt_eq); comp_ass_op3('>',right_assign); + compress2 ('>',gt_gt); break; +case '<': compress2('=', lt_eq); comp_ass_op3('<',left_assign); + compress2 ('<', lt_lt); break; +case '&': compress2('&',and_and); comp_ass_op2(and_assign); break; +case '^': comp_ass_op2(xor_assign); break; +case '|': compress2('|',or_or); comp_ass_op2(or_assign); break; +case '!': compress2('=',not_eq); break; +case '.': compress3('.','.', ellipsis); break; +case '#': compress2 ('#', sh_sh); break; +case ':': @+ if (C_plus_plus) compress2 (':',colon_colon); +} + +@ The code below is almost identical to the corresponding module in +|CTANGLE|; the difference is that we accept characters |c>=0x80| without +testing whether a translation is defined for them (since we have not +recorded such information) and that we always look up identifiers, even if +they have length~$1$. + +@< Get an identifier @>= +{ id_first=--loc; /* mark beginning of identifier */ + do c=*++loc; while (isalnum(c) || c=='_' || c>=0x80); + cur_id= id_lookup(id_first,loc,normal); +} + +@ In \Cee~text, numeric constants are specified in the ordinary \Cee~manner: +octals start with `\.0', hexadecimals with `\.{0x}', and anything starting +with a non-zero digit is a decimal constant; however for octal and +hexadecimal constants \.{\me.} will produce output using italics or +typewriter font, respectively, and introduced by a raised circle or hash +mark. Forgivably in contradiction with the definition of~\Cee, we treat the +ubiquitous constant~`0' as decimal rather than as octal. When the kind of +constant we are dealing with has been recognised, we represent this +information internally by special marker characters, which replace the marks +used in the \Cee~source (like `\.{0x}' for hexadecimal or `\.E' for the +exponent of a floating point constant). These markers are characters like +`\.\^' that will get a backslash prepended by the output routine for +constants, so that formatting of the constants can be controlled by defining +the corresponding \TeX\ control words (like `\.{\\\^}' in the case +mentioned) appropriately. + +@d shift_and_store(ch) (*id_loc++=ch,c=*++loc) + +@<Get a numeric constant@>= +{ id_first=id_loc=&mod_text[1]; + + if (c=='0' && (isdigit(c=*loc) || tolower(c)=='x')) /* octal or hex */ + { if (isdigit(c)) /* octal constant with at least two digits */ + { *id_loc++ = '~'; /* store `\.\~' in place of leading `\.0' */ + do shift_and_store(c); while (isdigit(c)); + /* copy second and following digits */ + } + else /* hex constant */ + { shift_and_store('^'); /* replace `\.{0x}' by `\.\^' */ + while (isxdigit(c)) shift_and_store(c); + } + } + else /* decimal constant */ + { c=*--loc; /* recover first digit or decimal point */ + while (isdigit(c)) shift_and_store(c); + if (c=='.') @+ do shift_and_store(c); while (isdigit(c)); + if (tolower(c)== 'e') /* floating point constant with exponent */ + { shift_and_store('_'); /* replace `\.e' by `\.\_' */ + if (c=='+' || c=='-') {@; *id_loc++ = c; c=*++loc; } + while (isdigit(c)) shift_and_store(c); /* exponent */ + } + } + if (isalpha(c)) /* `\.{U}', `\.{L}', and/or `\.{F}' suffix */ + @/{@; *id_loc++ = '$'; do shift_and_store(c); while (isalpha(c)); } +} + +@ After an `\.{@@}' sign has been scanned, the next character tells us +whether there is more work to do. This code uses the fact that our internal +code numbers |xref_roman|, |xref_wildcard|, |xref_typewriter|, and +|xref_mark| are consecutive in the stated order, as are the |ilk| codes +|roman|, |wildcard|, |typewriter|, and |reference|. We silently eliminate the +possibility of indexing the empty string, since it would cause anomalous +situations in hashing and sorting of the index, and it would look rather +silly anyway. + +@<Get control code and possibly module name...@>= +if (including_header_file) goto restart; /* ignore `\.@@' in header files */ +else +{ int cc=code_of(*loc++); + switch (cc) + { case ignore: goto restart; + case underline: xref_switch=def_flag; goto restart; +#ifdef DEBUG + case trace0: case trace1: case trace2: case trace3: @+ + if (phase==2) tracing=cc; @+ goto restart; +#endif + case char_trans: + err_print("! `@@l' only allowed in limbo"); goto restart; + @.`@@l' only allowed in limbo@> + case ASCII_code: @< Scan an \caps{ASCII} constant @> @+ return string; + case module_name: + @< Scan the module name and make |cur_mod| point to it @> @+ break; + case ignored_text: get_control_text(); goto restart; + case verbatim: case TeX_string: get_control_text(); break; + case xref_roman: case xref_wildcard: case xref_typewriter: + case xref_mark: case refer: + if (get_control_text()) goto restart; /* don't index empty strings */ + if (cc==refer) cur_id=id_lookup(id_first,id_loc,reference); + else if (phase==1) + cur_id=id_lookup(id_first,id_loc,cc-xref_roman+roman); + } + return cc; +} + +@ There is no reason why we should allow a newline within an \caps{ASCII} +constant, even if it is escaped. + +@< Scan an \caps{ASCII} constant @>= +{ id_first=&mod_text[1]; strncpy(id_first,"@@'",2); id_loc=&id_first[2]; + while ((*id_loc++=c=*loc++)!='\'') + { if (c=='\\') + *id_loc++=*loc++; /* copy any character following backslash */ + else if (c=='@@' && *loc++!='@@') + {@; err_print("! Double @@ required in strings"); --loc; } + @.Double @@ required...@> + if (loc>=limit) {@; err_print("! ASCII constant didn't end"); break; } + @.ASCII constant didn't end@> + } +} + +@ Here |get_module_name| does nearly all the work; we only need to recognise +when \:( is used rather than~\:<, and if so insert |file_flag| in the +appropriate \xr. list. + +@< Scan the module name... @>= +{ boolean file_module=loc[-1]=='('; + cur_mod=get_module_name(); + if (file_module && phase==1 && cur_mod!=NULL) set_file_flag(cur_mod); +} + + +@* Phase I processing. @:title@> +We now have accumulated enough functions to make it possible to carry out +\.{\me.}'s first pass over the source file. If everything works right, +both Phase~I and Phase~II of \.{\me.} will assign the same numbers to +sections, and these numbers will agree with what \.{CTANGLE} does. + +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. Meanwhile we also keep track using +|change_exists| of whether any change was made at all, which will tell us +whether the index has changed. The global variable |next_control| often +contains the most recent output of |get_next|; in interesting cases, this +will be the control code that ended a section or part of a section. + +@d shift() (next_control=get_next()) + +@<Global...@>= +boolean change_exists=false; /* has any section changed? */ +int next_control; /* control code waiting to be acted upon */ + +@ The overall processing strategy in Phase~I has the following +straightforward outline. + +@c +void phase_one (void) + /* read all the user's text and store the \xr.s */ +{ phase=1; reset_input(); section_count=0; + @< Skip the limbo part @> + while (!input_has_ended) + @< Store cross-reference data for the current section @> + if (change_exists) mark_section_as_changed(section_count); + /* the index changes if anything does */ + @< Print error messages about unused or undefined module names @> + @< Reverse the \xr. lists for identifiers @> +} + +@ The outline for each section is equally straightforward. + +@< Store cross-reference data... @>= +{ if (++section_count==max_sections) + overflow("section number"); @.section number capacity exceeded@> + if (loc[-1]=='*') print_section_progress (); + @< Store cross-references in the \TeX~part of a section @> + @< Store cross-references in the definition part of a section @> + @< Store cross-references in the \Cee~part of a section @> + if (section_changed(section_count)) change_exists=true; + } + +@ We interrupt our refinement of |phase_one| temporarily for some auxiliary +functions that are used in its various parts. + +@< Prototypes @>= +void C_xref (boolean); /* make \xr.s within in straight \Cee~text */ +void outer_xref (void); /* make \xr.s in \Cee~text with comments */ +void mod_check (mod_pointer); /* check \xr.s for module names */ + +@ The function |C_xref| stores references to identifiers in \Cee~text, +either for an entire fragment enclosed in `\pb', or for a portion of a macro +or section body delimited by comments or module names. The boolean parameter +|inner| tells whether the former is the case; if so |C_xref| should stop +with |next_control=='|'|, and otherwise it should stop with either +|next_control==begin_comment| or |next_control>=format|. In fact, setting +|inner| will make |C_xref| stop at |'|'| but proceed past module names, +while comment delimiters and major control codes will make |C_xref| stop +regardless of |inner|. If |next_control>=format| when |C_xref| is called, +nothing will happen, but it is safe to call the function when +|next_control=='|'| or |next_control==end_comment|, which will be stepped +over rather than considered as termination condition. Thus we can avoid +saying |shift()| immediately before calling |C_xref| on several occasions. +After a `\.\#' that starts a preprocessor directive, an identifier must +follow, which must not be \xr.d; this is achieved by performing an extra +|shift()|. If no identifier follows, we report an error, but do not perform +the extra |shift()|. The code below uses the fact that our internal code +numbers |identifier|, |xref_roman|, |xref_wildcard|, |xref_typewriter|, and +|xref_mark| are consecutive. + +The other task of Phase~I is to collect all unusual |ilk| assignments, +and the subtler part of this is the processing of |typedef| declarations. +The details of this process will be explained later, but this is where +it is hooked into the other actions, since all relevant tokens (which do +not include module names or comments) pass here one by one. + +@c +void C_xref (boolean inner) +{ while (next_control<format || next_control==module_name && inner) + { @< Keep track of tokens relevant to |typedef| declarations @> + if (next_control>=identifier && next_control<=xref_mark) + new_id_xref(cur_id); + else if (next_control==module_name && cur_mod!=NULL) + mod_xref_switch=cite_flag,new_mod_xref(cur_mod); + if (next_control==start_preproc && shift()!=end_preproc + &&next_control!=identifier) + err_print("! Identifier should follow `#'"); + @.Identifier should follow `\#'@> + else shift(); + if (next_control=='|' && inner + || next_control==begin_comment || next_control==end_comment) + return; + } +} + +@ The function |outer_xref| is like |C_xref|, but is used to scan an entire +macro body, or a portion of a section body delimited by module names; it +handles \Cee~text with embedded comments. It is called after \:d, a format +definition, \:c, or a module name (either defining or applied) has been +scanned, and after \:h has fired up its header file; in all cases +|next_control| is already processed, and we start with |shift()|. (There is +also one call that can occur during Phase~II, namely if illegal items +following a \:s format definition were found; its purpose then is merely to +ensure |next_control>=format| without producing any output.) + +While a comment is being scanned, tokens that pass |C_xref| should not be +considered as part of a possible |typedef| that is in progress; this is +achieved by invoking the macro |typedef_tracking| with the proper boolean +value at the beginning and end of the comment. + +@c +void outer_xref (void) /* extension of |C_xref| */ +{ shift(); /* move past previously processed token */ + while (next_control<format) + if (next_control!=begin_comment) C_xref(false); + else + { boolean one_liner=loc[-1]=='/'; int bal=0; /* brace level in comment */ + typedef_tracking(false); + while ((next_control=scan_comment(&bal,one_liner))=='|') + @/{@; C_xref(true); if (next_control!='|') break; } + typedef_tracking(true); + } +} + +@ In the \TeX~part of a section, \xr. entries are made only for the +identifiers in \Cee\ texts enclosed in `\pb', or for control texts +introduced by \:\^, \:., \:?, or \::. + +@< Store cross-references in the \TeX... @>= +do + switch (next_control=skip_TeX()) + { case underline: xref_switch=def_flag; break; + case '|': C_xref(true); break; + case module_name: case refer: loc-=2; get_next(); break; + case ignored_text: get_control_text(); break; + case char_trans: err_print("! `@@l' only allowed in limbo"); break; + @.`@@l' only allowed in limbo@> + case xref_roman: case xref_wildcard: case xref_typewriter: + case xref_mark: loc-=2; get_next(); new_id_xref(cur_id); + } +while (next_control<format); + +@ During the definition and \Cee~parts of a section, \xr.s are made for all +identifiers except reserved words and the right hand sides of format +definitions. The \TeX\ code in comments is, of course, ignored, except for +\Cee\ portions enclosed in `\pb'; the text of a module name is skipped +entirely, even if it contains `\pb' constructions. + +@ When we get to the following code we have |next_control>=format|. + +@< Store cross-references in the def... @>= +while (next_control<begin_C) /* |format|, |definition| or |header| */ + if(next_control!=header) + { xref_switch=def_flag; /* implied \:! for first identifier */ + if (next_control==format) @< Process a format code in a section @> + outer_xref(); /* macro definition or comment after format definition */ + } + else @< Read a header file, scanning it for |typedef| declarations @> + +@ Before we handle format codes occurring in a section, let us consider +their treatment in limbo. Here the code must be a non-printing \:s rather +than \:f, since we are not prepared to emit formatted output in limbo. The +syntax and semantics are simple: two identifiers must follow, and the |ilk| +of the latter is is assigned to the former. + +@< Process a format code in limbo @>= +if (tolower((eight_bits)loc[-1])=='f') + err_print("! Double @@ required in limbo part"); + @.Double @@ required...@> +else +{ id_pointer lhs; + if (shift()==identifier && (lhs=cur_id,shift()==identifier)) + lhs->ilk=cur_id->ilk; + else err_print("! Improper format definition"); + @.Improper format definition@> +} + +@ In a section \:s is processed in the same way; for \:f we additionally +produce a defining \xr. for the left hand side. We do not call |shift| since +this will be done by |outer_xref|, which is called after this code to +process any comments that follow the format definition. + +@< Process a format code in a section @>= +{ boolean f= tolower((eight_bits)loc[-1])=='f'; + id_pointer lhs; + if (shift()==identifier && (lhs=cur_id,shift()==identifier)) + { if (f) new_id_xref(lhs); @+ else xref_switch=0; + lhs->ilk=cur_id->ilk; + } + else err_print("! Improper format definition"); + @.Improper format definition@> +} + +@ After \:h a file name follows, enclosed in white space, double quotes, or +angle brackets. We open the header file by the same routine used to open \:i +files, but here we do suspend reading from the change file. If all is well +the whole file will contain pure \Cee~text without any control codes, so +|outer_xref| will come to a halt shortly after returning from that file. + +@< Read a header file... @>= +{ if (push_input_file(true,true)) /* prepare for reading header file */ + including_header_file=true; /* will be reset on closing the file */ + typedef_tracking(true); /* this is what we are doing it for */ + outer_xref(); + /* |shift()| and collect typedefs until |next_control>=format| */ + typedef_tracking(false); +} + + +@ Finally, when the \TeX\ and definition parts have been treated, we have +|next_control>=begin_C|. The loop repeatedly marks a module name +\xr. and then calls |outer_xref| to scan everything up to the +next module name; if |next_control==module_name| initially, we raise +|mod_xref_switch| which will cause that first module name to be marked as +defining. + +@< Store cross-references in the \Cee... @>= +{ if (next_control<new_section) /* |begin_C| or |module_name| */ + { typedef_tracking(true); + mod_xref_switch= next_control==module_name ? def_flag : 0; + do + { if (next_control==module_name && cur_mod!=NULL) + new_mod_xref(cur_mod); + outer_xref(); + } while (next_control<new_section); + typedef_tracking(false); + } +} + +@ After we have seen everything, we want to check that each module name was +both defined and used. The following recursive function walks through the +tree of module names alphabetically and prints out anomalies. It also +reverses the sublists of \xr.s to sections where a module is being defined, +cited, and used. In this section we use the fact that \xr. lists end with a +sentinel with |num==0|. +@^recursion@> + +@c +void mod_check (mod_pointer p) /* print anomalies in subtree |p| */ +{ if (p != NULL) + { mod_check (p->llink); /* traverse left subtree */ + { boolean file_module = p->xref->num==file_flag; + sixteen_bits head, *q, threshold; + /* lower limit of |num| values of current interest */ + if (file_module) q=&p->xref->next; @+ + else head=xref_index(p->xref),q=&head; + if (!complete_name(p)) + @/{@; print("\n! Never completed"); print_mod(p); mark_harmless(); } + @.Never completed: <module name>@> + if (xnum(*q)<=(threshold=def_flag)) + @/{@; print("\n! Never defined"); print_mod(p); mark_harmless(); } + @.Never defined: <module name>@> + else + @< Reverse sublist after |*q| with entries |num>threshold|; + make |q| point to final |next| field @> + if (xnum(*q)>(threshold=cite_flag)) @/@< Reverse sublist... @> + if (xnum(*q)==(threshold=0)) + @/{@; if(!file_module) + {@; print("\n! Never used"); print_mod(p); mark_harmless(); } + @.Never used: <module name>@> + } + else @< Reverse sublist... @> + if (!file_module) p->xref=&xmem[head]; + /* set pointer to possibly modified value */ + } + mod_check (p->rlink); /* traverse right subtree */ + } +} + +@~@<Print error messages about un...@>= +mod_check(root); + +@ We now come to the reversal of the \xr. lists, which is necessary because +by repeatedly prepending elements to these lists, or in the case of module +names to one of three sublists, these (sub)lists have obtained reverse +ordering. The method of traversal of the set of all identifiers is different +from that for the set of all module names, whence these tasks are linked +into the program at different points, but the reversal routines themselves +are quite similar. As we already traverse the module names in |check_root|, +the reversal code for module \xr.s was simply inserted at the proper place +in that function; for traversal of the set of identifiers we use the |hash| +table, by following all non-empty hash lists. + +@< Reverse the \xr. lists... @>= +{ id_pointer name; id_pointer *h; /* pointer into |hash| */ + for (h=hash; h<hash_end; h++) + for (name=*h; name!=NULL; name=name->hash_link) + /* traverse all hash lists */ + @< Reverse the list |name->xref| @> +} + +@ As Knuth keeps reminding us, list reversal can be thought of as a process +of repeatedly popping values off one list~|x| and pushing them onto the +reversed list~|y| (or you may read ``stack'' for ``list'' if you like). It +can also be useful to remember that the basic action can be performed by a +four-stroke engine, where the left hand side of each assignment equals the +right hand side of the previous one. The basic cycle can actually take +different forms, each using an auxiliary variable~|t|. One way is to +use~|t| to hold the entry moved, repeating |{ t=x; x=t->next; t->next=y; +y=t;}| until |x==NULL|; another way is to use~|t| to hold the remainder of +the list to be reversed, repeating |{ t=x->next; x->next=y; y=x; x=t;}|, +again until |x==NULL|. For reversing the \xr. lists of identifiers, we use +the first form. + +@< Reverse the list |name->xref| @>= +{ sixteen_bits x=xref_index(name->xref),t,y=0; + /* index of the sentinel node */ + while (xnum(x)!=0) {@; t=x; x=xlink(t); xlink(t)=y; y=t; } + name->xref=&xmem[y]; /* don't forget to link in the reversed list */ +} + +@ The reversal of sublists of the \xr. list attached to module names is only +slightly more complicated. At the three places where the code below is used, +things have been set up so that |q| points to the location of the link +pointing to the start of the sublist (since this link is going to be +changed, we need a pointer to it) and the end of the list is implicitly +indicated by |threshold|: the sublist ends before the first entry with +|num<=threshold| (which always exists because of the sentinel with +|num==0|). It has also been ensured that the code is only invoked when the +indicated sublist is not empty, so that we can use a |do|-|while| loop; we +have chosen the alternative order of assignments with respect to the +previous section, mainly to demonstrate the possibility, although it also +allows the code to be slightly shorter here. + +After the sublist has been reversed, some links must be redirected to +install it in its proper place. The link |*q| must be pointed to the head of +the reversed list, which is in |y|, while the link at the end of the sublist +must be pointed to the unaffected remainder of the list (this remainder +should ideally have been assigned to |y| initially, but it is only located +once we have arrived at the first entry with |num<=threshold|). Fortunately +the final node of the sublist is not only pointed to by its predecessor, but +also by |*q| (before it is changed) since it used to be the first node of +the sublist; therefore a small sequence of carefully ordered assignments +will do the trick. It is instructive to check that if the sublist to be +reversed has length~$1$, then all variables will eventually return to their +original state. The initialisation of~|y| is only present to keep certain +compilers from complaining that its value is used before it is first +assigned to; the initial value is irrelevant since it will be overwritten. + +@< Reverse sublist... @>= +{ sixteen_bits x=*q,y=0,t; + do {@; t=xlink(x); xlink(x)=y; y=x; } while (xnum(x=t)>threshold); + xlink(t=*q)=x; *q=y; q=&xlink(t); +} + + +@* Outline of Phase II processing. @:title@> +With the description of Phase~I still fresh in our memory, let us look at +the general outline of Phase~II, which is analogous, although it is more +complicated. The extra complication is due to the fact that much more has to +be done during Phase~II, notably the \Cee~texts have to be parsed in order +to determine their proper formatting, and the resulting token lists have to +be written to a file in a form that \TeX\ will be able to process. Most of +the actual work however is localised in a few powerful functions that will +be defined in detail later on, so that the definition of |phase_two| can be +given here without much problems. + +There are three stages in the processing of a piece of \Cee~text during +Phase~II: first it is scanned lexically (using |get_next|), and the +resulting tokens are collected in the form of `scraps' that form the input +for the second stage, the parsing algorithm, which transforms the scraps into +a recursively nested token list, which is converted in the third stage to +textual output. +For the small parts of \Cee~text enclosed in `\pb', a function~|do_C| is +available which handles all three stages of processing: it is called when an +opening `\.\v' is seen, and when it is completed one has arrived at the +closing `\.\v', and the required output is written on the \TeX~file. +For the larger parts of \Cee~text, the function |outer_read| will read in, +and convert to scraps, chunks of \Cee~text delimited by control codes +|c>=format| (so like |outer_xref| it handles comments, but it will not +incorporate module names), and when enough of these have been accumulated, +a call on |finish_C| will invoke the parsing algorithm and send the +resulting tokens to the output file. + +A number of simple functions for producing output are also called explicitly +at certain points. We have already seen |out| and~|finish_line| for +character-based output; there is also |out_str| for writing a string, +|out_sec_nr| for a section number, |list_refs| for generating the text for +\:\#, and |footnote| for producing the module \xr. information at the end of +sections. Invoking the macro |tex_new_line| immediately after |finish_line| +was called will produce an empty line on the output. Furthermore certain +small pieces of code which have been scanned directly rather than via |do_C| +or |outer_read| (for instance after format or macro definitions) are +converted into scraps: first a number of tokens are appended by means of +|app| or~|app_str| and then the whole sequence is converted to a scrap by +calling |pack_scrap|; |app| and~|pack_scrap| are macros. + +@< Prototypes @>= +void do_C (void); /* handle \Cee~text enclosed in `\pb' */ +void outer_read (void); /* transform input into scraps */ +void finish_C (void); /* finishes a definition or a \Cee~part */ +void finish_line(void); /* send out a line of output */ +void out_str (char*); /* write multiple characters */ +void out_sec_nr (int); /* output a section number */ +xref_pointer list_refs (xref_pointer,sixteen_bits); + /* output module \xr.s */ +void footnote(xref_pointer*,sixteen_bits); /* same with heading text */ +void app_str(char*); /* append a sequence of character tokens */ + +@ Like in |phase_one|, we loop over the sections after passing over the limbo +part. + +@c +void phase_two (void) + /* read all the text again and translate it to \TeX\ form */ +{ phase=2; reset_input (); + print_progress("\nWriting the output file..."); + @.Writing the output file...@> + section_count=0; copy_limbo(); finish_line(); + tex_new_line(); /* insert a blank line, it looks nice */ + while (!input_has_ended) @<Translate the current section@> +} + +@ The output file will contain the control sequence `\.{\\Y}' before a +non-empty definition portion of a section, and before a non-empty +\Cee~portion (the \TeX~portion is always considered to be non-empty, since +it contains at least the section number). This puts a little white space +between adjacent portions when they are printed. + +@d emit_space() out_str ("\\Y"); @.\\Y@> + +@< Translate the current section @>= +{ section_count++; + @< Output the code for the beginning of a new section @> + @< Translate the \TeX~part of the current section @> + if (next_control<begin_C) + @/{@; emit_space(); + @< Translate the definition part of the current section @> + } + if (next_control<new_section) + { mod_pointer this_module=NULL; /* the current module name */ + emit_space(); @< Translate the \Cee~part of the current section @> + @< Show cross-references to this section @> + } + @< Output the code for the end of a section @> +} + +@ Sections beginning with the \.{CWEB} control sequence \:{\ } start in the +output with the \TeX\ control sequence `\.{\\M}', followed by the section +number. Similarly, \:* sections lead to the control sequence `\.{\\N}', and +\:\~ sections to `\.{\\n}'. If this is a changed section, we put `\.*' just +before the section number. + +@< Output the code for the beginning... @>= +{ out('\\'); out(loc[-1]=='*' ? 'N' : loc[-1]=='~' ? 'n' : 'M' ); + @.\\N@> @.\\n@> @.\\M@> + if (loc[-1]=='*') + {@; print_section_progress(); @< Handle title level @>@+ } + out_sec_nr(section_count); out_str(". "); +} + +@ Between \:* and the title that follows, a level can be specified in the +form of another `\.*', or a decimal number; the absence of a number will be +interpreted as level~0. The level will be written out after `\.{\\N}' as a +first argument, delimited by a space, after which the second argument is the +section number and the third specifies the title. + +@< Handle title level @>= +{ if (*loc=='*') ++loc,out_str("-1"); + else if (!isdigit((eight_bits)*loc)) out('0'); + else do out(*loc++); while (isdigit((eight_bits)*loc)); + out(' '); /* terminate level by a space */ +} + +@ 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; +during this translation we track typedef definitions so that any complete +typedef declaration within `\pb' will be parsed correctly, as will be +explained below. + +@< Translate the \TeX... @>= +do + switch (next_control=copy_TeX()) + { case '|': typedef_master=0; do_C(); break; + case at_sign_image: out('@@'); break; + case thin_space: case math_break: case ASCII_code: case line_break: + case big_line_break: case no_line_break: case join: case pseudo_semi: + case force_expr_open: case force_expr_close: + err_print("! You can't do that in TeX text"); + @.You can't do that...@> + break; +#ifdef DEBUG + case trace0: case trace1: case trace2: case trace3: tracing=next_control; + break; +#endif + case module_name: loc-=2; get_next(); break; /* get module name */ + case refer: loc-=2; get_next(); /* get name referred to */ + if (cur_id->xref->num==0) err_print("! Undefined reference"); + else list_refs(cur_id->xref,0); + break; + case TeX_string: err_print("! TeX string should be in C text only"); + @.TeX string should be...@> + /* fall through */ + case xref_roman: case xref_wildcard: case xref_typewriter: + case xref_mark: case ignored_text: + get_control_text(); /* skip to \:> */ + } +while (next_control<format); + +@ When we get to the following code we have |format<=next_control<begin_C|. +The first conditional statement below processes the first few tokens of a +preprocessor directive, which do not follow the ordinary syntax rules; the +remainder is then treated by |outer_read| using the regular parsing +mechanism, although in the case of a format definition this should only +involve possible comments and formatting controls, and in case of \:s even +these should be absent. + +@< Translate the def... @>= +{ typedef_tracking(false); + do + { boolean suppressed=false; /* whether output suppressed by \:s */ + if (next_control==format) @< Start a format definition @> + else if (next_control==definition) @< Start a macro definition @> + else @< Start a header file inclusion @> + if (!suppressed) outer_read(), finish_C(); + else if (next_control<format) + { err_print("! Improper stuff after `@@s' format definition"); + @.Improper stuff after `@@s'...@> + if (next_control==begin_comment) loc-=2; /* try to get back in phase */ + outer_xref(); /* skip illegal stuff */ + } + } while (next_control<begin_C); /* |format|, |definition|, or |header| */ +} + +@ The syntax of a format definition has already been checked. It suffices to +build scraps that will produce the desired output. The trickiest point is +formatting the identifiers in a way that is least confusing to readers. We +try to keep analogy to lines for macro definitions in case these define one +identifier to stand for another: the left hand side is set in italics +regardless of its |ilk|, while the right hand side has its normal +appearance, prescribed by its |ilk|. If the left hand side is a |TeX_like| or +|NULL_like| identifier, we stick to the representation in italics, even though +this costs some extra work, and is not what would happen for a macro +definition; in addition we append the usual formatted form of the identifier +in parentheses, to show the correspondence of name and printed symbol. + +@< Start a format... @>= +if (tolower((eight_bits)loc[-1])=='s') +@/{@; suppressed=true; shift(); shift(); shift(); } + /* skip format definition */ +else +{ int saved_code=0,saved_mathness; + app_str("\\F"); shift(); /* this will produce `\&{format}' */ @.\\F@> + if (cur_id->ilk!=TeX_like && cur_id->ilk!=NULL_like) + app(id_flag+id_index(cur_id)); + else @< Expand identifier and set |saved_code| and |saved_mathness| @> + app('~'); pack_scrap(insert,yes_math); shift(); + app((cur_id->ilk==normal || cur_id->ilk==TeX_like || cur_id->ilk==NULL_like + ? id_flag : res_flag + )+id_index(cur_id)); +@/check_scrap(); + pack_scrap(insert,cur_id->ilk==TeX_like ? no_math : yes_math); + shift(); + if (saved_code!=0) + { app_str("\\quad("); app(saved_code); app(')'); + @/check_scrap(); pack_scrap(insert,saved_mathness); + } +} + +@ Since conversion of an identifier into a \TeX\ control sequence is +performed by the output routine, we need to circumvent this to force italic +type; this is done by expanding the name into characters directly rather +than leaving this to the output routine. + +@< Expand identifier... @>= +{ char* p=name_begin(cur_id); + saved_mathness=cur_id->ilk==TeX_like ? no_math : yes_math; + saved_code=id_flag+id_index(cur_id);/* save to print afterwards */ + app_str("\\\\{"); @.\\\\@> + do {@; if (*p=='_') app('\\'); app_tok(*p); } while (*++p!='\0'); + app('}'); check_toks(10); +} + +@ Keeping in line with the conventions of the \Cee\ preprocessor (and +otherwise contrary to the rules of \.{CWEB}) we distinguish here between the +cases that a `\.(' immediately follows the identifier being defined, and the +case that anything else (possibly a space) does. In the latter case, the +replacement text starts immediately after the identifier, in the former +case, it starts after we scan the matching `\.)', which must be simply the +first `\.)' that follows. + +@<Start a macro...@>= +{ if (shift()!=identifier) + err_print("! Improper macro definition"); + @.Improper macro definition@> + else + { app_str("\\D$"); /* this will produce \&{\#define} */ @.\\D@> + app(id_flag+id_index(cur_id)); + if (*loc=='(') + { shift(); + do + { app_char_tok(next_control); + if (shift()!=identifier) break; + app(id_flag+id_index(cur_id)); + } while(shift()==','); + check_toks(2); + if (next_control==')') {@; app(')'); shift(); } + else err_print("! Improper macro definition"); + } + else shift(); + app('$'); app(break_space); pack_scrap(insert,no_math); + } +} + +@ For scanning the token following \:h we temporarily set |preprocessing=2|, +so that angle brackets will be recognised as string quotes. + +@<Start a header file...@>= +{ app_str("\\h"); /* this will produce \&{\#include} */ @.\\h@> + pack_scrap(insert,no_math); + { int save=preprocessing; preprocessing=2; /* emulate `\.{\#include}' */ + while (shift()==' ') {} /* skip spaces and read file name as string */ + preprocessing=save; + } +} + +@ Finally, when the \TeX\ and definition parts have been treated, we have +|next_control>=begin_C|. If the section defines a module name, we assign the +name to the variable |this_module|, so that the proper \xr. information can +be listed at the end of the section. Like in Phase~I it is necessary to pay +special attention to |typedef| declarations, and this time tracking is +enabled both within `\pb' in \TeX~text and in the \Cee~part of a section; +since the former may involve incomplete pieces of syntax like a sole +`|typedef|', we reset the master counter to its neutral state at the +beginning of a \Cee~part. After the heading of the \Cee~text has been +processed we alternatively read ordinary pieces of \Cee~text and module +names until the module has ended; we start with calling |outer_read| before +testing termination, in order to ensure that the overflow tests contained in +|outer_read| will be executed even in case of a section with a \Cee~part +consisting only of a heading. + +@<Translate the \Cee...@>= +{ typedef_master=0; + if (next_control==begin_C) shift(); + else + { this_module=cur_mod; /* register the name for this module */ + @< Check that `\.{=}' or `\.{==}' follows this module name, and + emit the scraps to start the module definition @> + } + do + { outer_read(); + if (next_control==new_section) break; + if (next_control==module_name) @< Append a module name scrap @> + else err_print("! You can't do that in C text"); + @.You can't do that...@> + /* |format|, |definition| or |begin_C| */ + shift(); + } while (true); + finish_C(); +} + +@ Despite the name of this module, we allow `\.+' to precede the `\.{=}' or +`\.{==}', just as |CTANGLE| does. Note however that, unlike in |CTANGLE|, +the `\.+' will be scanned as part of an `\.{+=}' compound operator (whence +in fact `\.{+= =}' is allowed here whereas `\.{+ ==}' is not; we hope +nobody minds this). Note also that if for whatever reason the current +section number should fail to appear in the \xr. list for the +module name (e.g., if section numbers have inadvertently got out of +synchronisation with respect to Phase~I), then listing the +\xr. information at the end of the section is suppressed by setting +|this_module=NULL|. + +@< Check that `\.{=}' ... @>= +{ if (shift()=='=' || next_control==eq_eq || next_control==plus_assign) + @/{@; if (next_control!=plus_assign || shift()=='=') shift(); } + /* accept `\.=', `\.{==}', `\.{+=}' or `\.{+==}' */ + else err_print("! You need an = sign after the module name"); + @.You need an = sign...@> + if (this_module!=NULL) /* i.e., unless module name was bad */ + { xref_pointer x=this_module->xref; + if (x->num==file_flag) x=next_xref(x); + app_str("\\4$"); /* module name will be flush left */ @.\\4@> + app(mod_flag+mod_index(this_module)); + if (x->num != section_count+def_flag) + { app_str("\\PE"); /* module has also been defined before */ @.\\PE@> + this_module = NULL; /* so we won't give \xr. info here */ + } + else app_str("\\EQ"); /* output a module definition sign */ @.\\EQ@> + app_str("{}$"); app(force); pack_scrap(insert,no_math); + /* this forces a line break unless \:+ follows */ + } +} + +@ Cross references relating to a named module are given after its first +defining section ends (for further defining sections of this name we will +have put |this_module=NULL|). + +@< Show cross-references... @>= +{ if (this_module != NULL) + { xref_pointer foot_ref=this_module->xref; + if (foot_ref->num==file_flag) foot_ref=next_xref(foot_ref); + foot_ref=next_xref(foot_ref); /* don't \xr. to yourself */ + footnote(&foot_ref,def_flag); + /* display further defining sections; advance |foot_ref| */ + footnote(&foot_ref,cite_flag); /* display any citations */ + footnote(&foot_ref,0); /* display uses */ + } +} + +@ The `\.{\\fi}' closes a \TeX~conditional that was initiated by the macro +that started off the section; this allows printing of only the changed +sections in a simple way. + +@<Output the code for the end of a section@>= +{@; out_str ("\\fi"); finish_line (); tex_new_line(); } +@.\\fi@> /* insert a blank line, it looks nice */ + + +@* Auxiliary functions used in Phase~II. @:title@> +We now define the functions that do the actual processing of \Cee~code +during Phase~II, but without going into the details of parsing and output. +We explain the functions |do_C|, |outer_read|, |finish_C|, |list_refs|, +and |footnote| used above, and also two further auxiliaries |C_read| +and~|C_translate|. + +@< Prototypes @>= +text_pointer translate(void); + /* build formatted text from collected scraps */ +void make_output(text_pointer,mode); + /* output text in |inner| or |outer| mode */ + +@ Before we discuss these functions, we must first discuss what scraps are. +Scraps are the objects manipulated during parsing, and they have two main +attributes: a syntactic category |cat|, that determines the way they will be +treated by the parser, and a translation |trans|, which is a pointer +into~|text_mem| denoting a (possibly recursively nested) sequence of tokens, +that determines the representation of the scrap upon output. Since some +parts of the output are to be processed in \TeX's math mode, and other parts +in horizontal mode, an additional field |mathness| tells which mode is +required at each end of the translation of the scrap. + +@<Typedef...@>= +typedef struct +{ eight_bits cat; /* category code */ + eight_bits mathness; /* whether in math mode at left and right boundary */ + text_pointer trans; /* translation text */ +} scrap, *scrap_pointer; + +@ When \Cee\ text is converted into scraps for parsing, the resulting scraps +are placed in an array |scrap_info|, between the locations pointed to by +|scrap_base| and~|scrap_ptr|. Actually, |scrap_info| is one field of a +|union|, since the same memory is used for a different purpose during +Phase~III. + +Basic scraps are created by invoking |app|, |app_tok| or |app_char_tok| a +number of times creating the constituent tokens, and then consolidating the +text by means of |freeze_text|; the resulting text is accessible as +|text_ptr| before, and as |text_ptr-1| after the call of |freeze_text|. In +the common case that the text forms the translation of a new scrap that is +to be added to the scrap sequence, |pack_scrap| can be used in place of +|freeze_text|; a category and `mathness' should be supplied in this case. +The latter can take one of three values as explained later, and by +multiplying it by~5 (binary~$0101$) it is duplicated into the two least +significant pairs of bits, because for elementary scraps the value is the +same at its left and right boundaries. Note that none of |app|, |freeze_text| +and |pack_scrap| do bound checks, since it is assumed that these have been +done beforehand; |app_tok| and |app_char_tok| however can be called at more +uncertain times. + +@d scrap_info scrap_union.scrap_field +@d scrap_info_end (&scrap_info[max_scraps]) /* end of |scrap_info| */ +@) +@d app(a) (*tok_ptr++ = a) +@d app_tok(a) @+ + if (tok_ptr>tok_mem_end-2) overflow("token"); @.token capacity exceeded@> + @+ else app(a) @; +@d app_char_tok(c) app_tok((unsigned char)(c)) +@d freeze_text() (*++text_ptr = tok_ptr) +@d pack_scrap(c,m) + ( scrap_ptr->cat = c, scrap_ptr->trans = text_ptr, freeze_text(), + (scrap_ptr++)->mathness = 5*(m) ) + +@<Global...@>= +union +{ scrap scrap_field[max_scraps]; /* memory array for scraps */ + @< Alternative use of |scrap_union| @>@; +} scrap_union; +scrap_pointer scrap_base=scrap_info; + /* beginning of the current scrap sequence */ +scrap_pointer scrap_ptr = scrap_info; + /* points to end of the current scrap sequence */ +#ifdef STAT +scrap_pointer max_scr_ptr = scrap_info; + /* largest value assumed by |scrap_ptr| */ +#endif + +@ Token lists are stored in |tok_mem| and represent text to be output to the +\TeX~file. Because during parsing token lists will often be formed by +concatenation of existing ones, a representation is chosen where this can be +done easily; in particular a token can be a reference to a token list stored +elsewhere. Also identifiers, reserved words and module names are represented +by a reference to the name table rather than by their constituent +characters. All other items are stored as list of characters, which have +been widened to fill a 16-bit |token|, and special layout codes that will be +explained below. More precisely, a |token t@;| is interpreted as follows. +\yskip + +\item{$\bullet$} |t<=UCHAR_MAX|: the character~|t|, which possibly is + a compressed operator like `\.{\&\&}'; +\item{$\bullet$} |UCHAR_MAX<t<id_flag|: a special layout feature such as + |indent|, +\item{$\bullet$}|id_flag<=t<res_flag|: the identifier with name + |id_at(t-id_flag)|; +\item{$\bullet$}|res_flag<=t<mod_flag|: the reserved word + |id_at(t-res_flag)|; +\item{$\bullet$}|mod_flag<=t<text_flag|: the module named + |mod_at(t-mod_flag)|; +\item{$\bullet$}|text_flag<=t<inner_text_flag|: a reference to the token list + |text_at(t-text_flag)|; +\item{$\bullet$}|inner_text_flag<=t|: a reference to the token list + |text_at(t-inner_text_flag)|, which is to be translated without + line-break controls. + +@d id_flag 10240U /* signifies an identifier */ +@d res_flag (2*id_flag) /* signifies a reserved word */ +@d mod_flag (3*id_flag) /* signifies a module name */ +@d text_flag (4*id_flag) /* signifies a token list */ +@d inner_text_flag (5*id_flag) /* signifies a token list in `\pb' */ + +@~The special layout tokens are the following: +\yskip + +\item{$\bullet$} |indent| causes future lines to be indented one more unit; +\item{$\bullet$} |outdent| causes future lines to be indented one less unit; +\item{$\bullet$} |opt| denotes an optional line break, it is followed by an + integer~|n|, and the break will normally occur with penalty~$10n$ (in + fact \TeX\ raises some penalties, but this remains invisible to + \.{\me.}); +\item{$\bullet$} |flush_left| denotes that the line will be printed flush + left, regardless of the current indentation level; +\item{$\bullet$} |break_space| denotes an optional line break or an ``en'' + space; +\item{$\bullet$} |force| denotes a forced line break; +\item{$\bullet$} |big_force| denotes a forced line break with additional + vertical space; +\item{$\bullet$} |backup| denotes a forced line break followed by a + backspace of one indentation unit; +\item{$\bullet$} |big_backup| denotes a forced line break with additional + vertical space, followed by a backspace of one indentation unit; +\item{$\bullet$} |cancel| obliterates any space, |break_space|, |force|, + |big_force|, |backup|, or |big_backup| tokens that immediately precede + or follow it and also cancels any |opt| tokens that follow it; +\item{$\bullet$} |relax| does nothing, but serves as a ``stopper''. + +\yskip\noindent The character tokens |' '|~and~|'~'|, together with the +sequence of tokens from |break_space| to |big_backup| form a hierarchy, in +the sense that on output any consecutive sequence of such tokens is +equivalent to their maximum, except that |big_force| and |backup| combine to +|big_backup|. To facilitate the computation we added |space|~and~|tilde| to +the enumeration below, that overlay |opt|~and~|flush_left| respectively +(this causes no problems, since |space|~and~|tilde| are not used as token +values). Formally, we define a partial ordering on the set |{' ', '~', +break_space, force, big_force, backup, big_backup }| that differs from the +linear order in which they were listed only by the fact that |big_force| and +|backup| are incomparable; then any consecutive list of such tokens is +replaced by their least upper bound. + +@< Typedef and enum... @>= +enum @/ +{ cancel=UCHAR_MAX+1,/* the following 9 items should remain in this order */ + indent, outdent, opt, flush_left, break_space, force, big_force, + backup, big_backup, @/ + relax, @/ + space=opt, tilde=flush_left @/ +}; + +@ The memory management for tokens follows a simple block regime. +The source file is divided into blocks, and whenever a block is entered, +the current states of the |text_mem| and~|tok_mem| are marked, after +which they will gradually get filled up further; at the end of the block +all memory used during the block is released by resetting the pointers +into these arrays to the values they had on block entry. There are three +kinds of blocks: the global block, which is filled during initialisation and +is never released, the section blocks, which correspond to each individual +section with a non-empty \Cee~part (or to a macro or format definition), +and the inner blocks, which correspond to each `\pb' contained in the +\TeX~part of a section or in a module name (but not in a comment). Because +nesting is at most three blocks deep (for `\pb' inside module names) a +two-element stack will suffice to hold the saved markers (nothing needs to +be saved for the global block), and we can address these elements directly +without a stack pointer since we know at which level we are. In fact the +section blocks butt together, and nothing is added to the global block +except at initialisation time, so that each section block starts in the same +state. Therefore, if we call |enter_block(0)| after initialisation to record +this state, then there is no need to call it any more, and it suffices to +call |leave_block(0)| each time upon leaving a section. + +Scraps follow a different regime than texts and tokens, since they must be +assembled into a single contiguous sequence before each translation, and can +be discarded when the translation is over. In particular the scraps used to +parse `\pb' within a comment can and must be released before reading in the +\Cee~text following the comment, but the tokens and texts formed while +translating the comment must remain until they are output. + +@d enter_block(i) save[i].txt=text_ptr, save[i].tok=tok_ptr; +@d leave_block(i) text_ptr=save[i].txt, tok_ptr=save[i].tok; + +@< Global variables @>= +struct {@; text_pointer txt; token_pointer tok; } save[2]; + +@ The conversion of input tokens as obtained by |get_next| into scraps +that can be processed by the parser is mainly handled by the function +|C_read|, which is analogous to the |C_xref| routine used during Phase~I. +Like |C_xref|, the function |C_read| takes a boolean argument telling +whether it is processing `\pb'; it starts with the current value of +|next_control| and it uses the operation |shift| repeatedly to read +\Cee~text until encountering a terminating token. Also like |C_ref|, the +initial conditions |next_control=='|'| or |next_control==end_comment| will +not lead to immediate termination; in these cases the first time through the +|while| loop will have no effect. + +@c +void C_read (boolean inner) /* creates scraps from \Cee\ tokens */ +{ while (next_control<format || next_control==module_name && inner) + { @<Append the scrap appropriate to |next_control|@> + if (shift()=='|' && inner + || next_control==begin_comment || next_control==end_comment) return; + } +} + +@ Many input tokens are completely determined by the value returned from +|get_next|; we have arranged it that such a value is always less than +|ignore|. For those tokens we will install corresponding scrap at initialisation +time in an array |token_trans|, so that when such a token comes along, we +can simply copy the scrap from |token_trans| into scrap memory. + +@< Global variables @>= +scrap token_trans[ignore]; + +@~Since the scraps in |token_trans| contain pointers into |tok_mem|, that +array cannot be initialised statically; rather we do the initialisation +dynamically based on information stored statically. For each token three +kinds of information are supplied: the category of its translation, a +string of characters giving the translation itself, and an indication for +its the |mathness| (whether the translation must or must not occur in math +mode, or whether both are allowed). The actual initialisation values will +be given later. + +@< Set initial values @>= +{ static struct {@; short tok; eight_bits cat, mathness; char* tr; } + trans_ini [] = @/{ @< Initialiser for |trans_ini| @>@;@; };@/ + int i,n=array_size(trans_ini); + scrap o={ insert, 5*maybe_math, NULL }; /* completely inert scrap */ + + for (i=0; i<n; ++i) + { scrap* p=&token_trans[trans_ini[i].tok]; + p->cat=trans_ini[i].cat; p->mathness=5*trans_ini[i].mathness; + app_str(trans_ini[i].tr); p->trans=text_ptr; freeze_text(); + } + @< Install the translations of tokens involving line breaks @> + + o.trans=text_ptr; freeze_text(); /* empty translation */ + for (i=0; i<=UCHAR_MAX; ++i) /* clear all remaining tokens */ + if (token_trans[i].cat==0) token_trans[i]=o; + enter_block(0); /* fix tokens; will be restored after each section */ +} + +@ Having installed |token_trans|, the number of cases in the +switch statement below is greatly reduced. + +@< Append the scr... @>= +{ check_scrap(); check_toks(6); /* `\.{\\hbox\{}' */ + switch (next_control) + { case string: case constant: case verbatim: + @< Append a string or constant @> @+ goto done; + case TeX_string: @< Append a \TeX\ string scrap @> @+ goto done; + case identifier: @< Append an identifier scrap @> @+ goto done; + case module_name: @< Append a module name scrap @> @+ goto done; + case start_preproc: + @< Append a scrap starting a preprocessing directive @> @+ goto done; + case refer: + err_print("! You can't use `@@#' in C text"); /*fall through */ + case ignore: case begin_comment: case end_comment: + case xref_roman: case xref_wildcard: case xref_typewriter: + case xref_mark: goto done; + @\@< Cases necessary for proper parsing of typedefs @> + case '|': @+ if (inner) goto done; /* skip initial `\.\v' of `\pb' */ + } + *scrap_ptr=token_trans[next_control]; /* fixed scrap for this input token */ + @< Possibly scoop up some dangling output tokens in compatibility mode @> + ++scrap_ptr; /* incorporate the scrap */ + done: {} +} + +@ When we need to be sure there is enough space to store |n|~more tokens, +we say |check_toks(n)|; when a scrap has to be appended we invoke +|check_scrap|, and when a text is to be formed otherwise than by +|pack_scrap|, we invoke |check_text|. The key points in the program where +we make such checks is when reading in \Cee~text in the functions |C_read| +and |outer_read|, and when translating the text in |reduce|. At these points +we make sure that there is enough room to spare so that in cases where +explicit tokens occasionally need to be appended (e.g., by |app_str|) no +test is necessary. When we cannot be sure about this, for instance because +an indefinite number of tokens is appended, we use |app_tok| or +|app_char_tok| instead of |app|; after this it will in fact be safe to do +one additional |app|. + +@d check_toks(n) @+ if (tok_ptr>tok_mem_end-n) + overflow("token"); @.token capacity exceeded@> @+ else @; +@d check_text() @+ if (text_ptr>=text_mem_end-1) + overflow("text"); @.text capacity exceeded@> @+ else @; +@d check_scrap() @+ if (scrap_ptr>=scrap_info_end) + overflow("scrap"); @.scrap capacity exceeded@> @+ else check_text() @; + +@ As was just explained, we can use |app| rather than |app_char_tok| here. + +@c +void app_str(char* s) @+{@; while(*s!='\0') app(*s++); } + +@ In long strings we insert a discretionary break every 20~characters, so +that \TeX\ is less likely to run into problems, especially if the string +occurs in the \TeX~part of a section; if we are directly after a backslash +however, we postpone the break since otherwise the quote escaping the line +break would appear to be escaped itself. Many of the special characters in +a string must be prefixed by `\.\\' so that \TeX\ will print them properly. +In the case of constants however, this `\.\\' converts marker characters +that were inserted during scanning into control sequences used in formatting +the constant. +@^special string characters@> + +@< Append a string or... @>= +{ int count = -1; /* characters remaining before string break */ + + if (next_control==constant) app_str("\\T{"); @.\\T@> + else if (next_control==string) {@; count=20; app_str("\\.{"); } @.\\.@> + else app_str("\\vb{"); @.\\vb@> + + while (id_first<id_loc) + { if (count--==0) /* insert a discretionary break in a long string */ + if (id_first[-1]=='\\') count=0; /* no break after backslash */ + else {@; check_toks(2); app_str("\\)"); count = 20; } @.\\)@> + if (strchr(" \\#%$^{}~&_",*id_first)!=NULL) app('\\'); +@.\\\ @> @.\\\\@> @.\\\#@> @.\\\%@> @.\\\$@> @.\\\^@> +@.\\\{@> @.\\\}@> @.\\\~@> @.\\\~@> @.\\\&@> @.\\\_@> + app_char_tok(*id_first++); + } + app('}'); + if (next_control==verbatim) pack_scrap(insert,maybe_math); + else pack_scrap(expression,yes_math); +} + +@ A \TeX~string is boxed and copied without further ado; undoubling of any +\:@@ has already been done by |get_control_text|. In compatibility mode we +do not however produce a scrap, but rather leave the output tokens produced +to be picked up by the next scrap appended; this makes +|dangling_tokens()| hold. In order to make sure that this next scrap +exists, we will append a dummy scrap in |translate| if necessary. +@:TeX string@> + +@d dangling_tokens() (compatibility_mode && tok_ptr>*text_ptr) + +@< Append a \TeX\ string scrap @>= +{ app_str("\\hbox{"); + while (id_first<id_loc) app_char_tok(*id_first++); + app('}'); + if (!compatibility_mode) pack_scrap(expression,maybe_math); +} + +@ Input tokens that have prefabricated scraps will not automatically incorporate +any output tokens left behind by the code in section@#TeX string@> since they do +not call |freeze_text|, so we must take some action to avoid that +\TeX~strings will appear too late in the output. When the code below is +encountered, the fixed scrap has been copied to |*scrap_ptr|. Its category +and mathness are correct, but we may need to prepend tokens to its +translation; this is done by appending that translation to the dangling +tokens, wrapping them up together, and replacing the original translation by +a pointer to the combined result. + +@< Possibly scoop up... @>= + +{@; if (dangling_tokens()) + {@; app_trans(scrap_ptr); scrap_ptr->trans=text_ptr; freeze_text(); } +} + +@ It is during the conversion of identifiers to scraps that their |ilk| +plays a crucial r\^ole. Ordinary identifiers, and those with |ilk| equal to +|TeX_like| or |NULL_like|, will get the category |expression|, and are set in +math mode except in the case of |TeX_like| identifiers. If the |ilk| specifies +some reserved word on the other hand, that |ilk| becomes the category of the +identifier, determining its behaviour during parsing. The translation of +these reserved words is done using |res_flag| rather than |id_flag|, as a +result of which they will be printed in boldface; they get mathness +|maybe_math|, indicating that they can be set equally well inside and +outside math mode. Identifiers whose |ilk| is |type_defined|, |const_like|, +or |typedef_like| will become reserved words with category |int_like|. These +special |ilk| values have served their purpose during the scanning of +typedef declarations; this involves subtle manoeuvres that will be explained +later. + +@< Append an identifier scrap @>= +{ id_pointer p=cur_id; int cat=p->ilk; + @< Track identifiers relevant to typedef; + maybe change |cat| from |int_like| to |expression| @> + if (cat==normal || cat==TeX_like || cat==NULL_like) + { app(id_flag+id_index(p)); + pack_scrap(expression + , cat==TeX_like && !compatibility_mode ? no_math : yes_math); + } + else + { if (cat==type_defined || cat==const_like || cat==typedef_like) + cat=int_like; + app(res_flag+id_index(p)); pack_scrap(cat,maybe_math); + } +} + +@ For bad module names (e.g., an ambiguous prefix) an error has already +been reported, and they are silently suppressed from the output. + +@< Append a module name scrap @>= +{@; if (cur_mod!=NULL) + app(mod_flag+mod_index(cur_mod)), pack_scrap(mod_scrap,yes_math); +} + +@ We tested in Phase~I that `\.\#' is followed by an identifier or by a +newline (which will cause |get_next| to return |end_preproc|). In the former +case we incorporate the identifier into the scrap for the preprocessor +directive; in the latter case the |lproc| scrap will just contain the +`\.\#', but since we already scanned the following |end_preproc|, we append +the corresponding |rproc| scrap as well. + +@< Append a scrap starting a preprocessing directive @>= +{ app(force); app(flush_left); app_str("\\&\\#"); + if (shift()==identifier) + {@; app(res_flag+id_index(cur_id)); pack_scrap(lproc,no_math); } + else if (next_control==end_preproc) + @/{@; pack_scrap(lproc,no_math); + check_scrap(); *scrap_ptr++=token_trans[end_preproc]; + } + else confusion("no identifier after `#'"); + @.no identifier after `\#'@> + +} + +@ When the `\.\v' that introduces \Cee\ text is sensed, a call on +|C_translate| will return a pointer to the \TeX\ translation of that +text. If scraps exist in |scrap_info|, they are unaffected by this +translation process, which is useful since we convert comments to single +scraps with help of |C_translate| while building the scrap sequence for the +surrounding piece of \Cee~text. + +@c +text_pointer C_translate(void) +{ text_pointer p; scrap_pointer save_base=scrap_base; + scrap_base=scrap_ptr; + C_read(true); /* get the scraps together */ + if (next_control != '|') err_print("! Missing `|' after C text"); + @.Missing `|'...@> + p=translate(); /* make the translation */ +#ifdef STAT + if (scrap_ptr>max_scr_ptr) max_scr_ptr=scrap_ptr; +#endif + scrap_ptr=scrap_base; scrap_base=save_base; return p; +} + +@ The function |outer_read| is to |C_read| as |outer_xref| is to |C_xref|: +it constructs a sequence of scraps for \Cee~text until +|next_control>=format|, taking care of embedded comments. It is called +between each occurrence of \:d, \:f, \:h, or a module name, which makes it a +convenient place to test whether the memory arrays have enough spare room to +cater for the stuff that could be needed for processing the next such token; +the most demanding requirements are for a module name heading the \Cee~part +of a section. These tests must be made outside the main loop of |outer_read|. + +We use that if |next_control==end_comment| when |C_read| is called, +this value is effectively ignored. + +@c +void outer_read (void) /* makes scraps from \Cee\ tokens and comments */ +{ while (next_control<format) + if (next_control!=begin_comment) C_read(false); + else @< Read a comment, and convert it into a scrap @> + check_scrap(); check_toks(11); /* `\.{\$\\4$m$\\PE\{\}\$$f$}' */ +} + +@ Since the call on |C_translate| used to process `\pb' inside comments will +itself create tokens, the token sequence for the comment under construction +must be wrapped up each time this happens, and a token referring to that +initial segment contributed as first new item after the translation is made. +Tests on the availability of two more tokens and a text must be made inside +the loop that incorporates successive `\pb' fragments; the space for tokens +outside these fragments is tested within |scan_comment|. + +@< Read a comment, and convert it into a scrap @>= +{ boolean one_liner=loc[-1]=='/'; int bal=0; /* brace level in comment */ + typedef_tracking(false); + check_scrap(); check_toks(4); + app(cancel); app_str(one_liner ? "\\SHC{" : "\\C{"); @.\\C@> @.\\SHC@> + while ((next_control=scan_comment(&bal,one_liner))=='|') + { text_pointer p=text_ptr, q=(freeze_text(), C_translate()); + check_toks(7); + app_tok(text_flag+text_index(p)); + if (compatibility_mode) app_str("\\PB{"); @.\\PB@> + app(inner_text_flag+text_index(q)); + if (compatibility_mode) app('}'); + check_text(); + } + app_char_tok('}'); app(force); pack_scrap(insert, no_math); + /* the full comment becomes a scrap */ + typedef_tracking(true); +} + +@ The function |do_C| does the scanning, translation, and output of +\Cee~text within `\pb' brackets. It is called during the scanning of the +\TeX~part of a section and during the output of module names. As we have +seen, this function is not called when processing comments, where +|C_translate| is used instead, because no direct output should be produced +at such times. + +@c +void do_C (void) /* read, translate, and output \Cee~text in `\pb' */ +{ enter_block(1); + if (compatibility_mode) out_str("\\PB{"); @.\\PB@> + make_output(C_translate(),inner); /* output the list */ + if (compatibility_mode) out('}'); +#ifdef STAT + if (text_ptr>max_text_ptr) max_text_ptr = text_ptr; + if (tok_ptr>max_tok_ptr) max_tok_ptr = tok_ptr; +#endif + leave_block(1); /* forget the tokens */ +} + +@ The function |finish_C| 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 memories to their +state as immediately after initialisation. + +@c +void finish_C (void) +{ out_str ("\\B"); @.\\B@> + make_output(translate(),outer); + out_str("\\par"); finish_line(); +#ifdef STAT + if (text_ptr>max_text_ptr) max_text_ptr=text_ptr; + if (tok_ptr>max_tok_ptr) max_tok_ptr=tok_ptr; + if (scrap_ptr>max_scr_ptr) max_scr_ptr=scrap_ptr; +#endif + leave_block(0); scrap_ptr=scrap_info; + /* forget the tokens and the scraps */ +} + +@ The function |footnote| gives \xr. information about +further definitions of a module name (if |flag==def_flag|), +about citations of a module name (if |flag==cite_flag|), +or about the uses of a module name (if |flag==0|). +It assumes that |*p| points to the first \xr. entry +of interest, and it leaves |*p| pointing to the first element +not printed (possibly the sentinel with |num==0|). +Typical outputs are `\hbox{\.{\\Q 2001.}}', +`\hbox{\.{\\Us 370\\ET1009.}}'\ETs `\hbox{\.{\\As 8, 27\\*, 51\\ETs64.}}'. + +@c +void footnote (xref_pointer* p,sixteen_bits flag) +{ if ((*p)->num<=flag) return; + finish_line(); out('\\'); + out(flag==0 ? 'U' : flag==cite_flag ? 'Q' : 'A'); @.\\A@> @.\\Q@> @.\\U@> + *p=list_refs(*p,flag); + out('.'); +} + +@~The function |list_refs|, which does the main work for |footnote| and is +also used to produce the text replacing \:\#, distinguishes three cases, +according as the number of relevant \xr.s is one, two, or more than two. The +function always produces at least one \xr.: it is never called with +|x->num<=flag|. The value of |x| after traversing the references is +returned, for the benefit of |footnote|. + +@c +xref_pointer list_refs (xref_pointer x,sixteen_bits flag) +{ xref_pointer q=next_xref(x); /* second element in \xr. list */ + if (q->num>flag) out('s'); + /* use `\.{\\As}', `\.{\\Qs}' or `\.{\\Us}' */ + @.\\As@> @.\\Qs@> @.\\Us@> + out(' '); + while (out_sec_nr(x->num&num_mask),x=next_xref(x),x->num>flag) + if (next_xref(x)->num>flag) out_str(", "); /* |x| is not the last */ + else + { out_str("\\ET"); /* next number printed will be the last */ + if (x!=q) out('s'); /* `\.{\\ETs}' for the last of more than two */ + } @.\\ET@> @.\\ETs@> + return x; +} + + +@* The problem of typedef declarations. @:title@> +We now consider how |typedef| declarations are processed. It is a slightly +problematic matter, since it involves the syntactic structure of the +program, but we are doing only lexical analysis during Phase~I. +Nevertheless, we don't want to delay recognition of |typedef| declarations to +Phase~II, since this would restrict the user's freedom of ordering the +sections so that all typedef declarations remain before any of their uses, +and it would make it cumbersome to mention a typedef identifier in the +commentary directly before its definition. Our approach will be to specify +simple rules that pinpoint the identifiers which are subject to a |typedef| +definition in any syntactically correct program; we don't care too much +about strange behaviour in the presence of syntax errors, and also assume +some basic decency on the part of the user, e.g., the identifier should +occur in the same section as the |typedef| token. + +Obviously there is little to care about until a |typedef| token +(recognisable by its |ilk|) comes along, but then it becomes tricky. +Ordinarily the identifier being defined is the first non-reserved word that +follows, but |struct| (and |union| and |enum|) tokens complicate the +situation; also there may be more than one identifier subject to the +|typedef|, separated by commas from each other. Because of |struct| we must +keep track of the nesting of braces, and because the mentioned commas should +be distinguished from those occurring in the parameter specifications of +functions, we should also keep track of parenthesis nesting. A semicolon at +the proper level of brace nesting signals the end of a |typedef| declaration. + +Fortunately |typedef| declarations cannot be nested inside each other, +so we can use global variables to keep the proper counts. Three integer +counters are used, two for the nesting levels of braces and parentheses, +which are set to~0 whenever a |typedef| is scanned and properly maintained +thereafter, and a master counter which determines if we are paying +attention at all. + +@d typedef_tracking(b) (typedef_master += b ? 5 : -5) + +@< Global... @>= +local int typedef_master=-5; /* tracking disabled outside \Cee~parts */ +local int brace_level, par_level; + +@ The master counter is ordinarily equal to~0 when tracking is enabled, and +negative if it is disabled. When it is~0 and a |typedef| is seen, it is +raised to~2, after which an |int_like| or |type_defined| identifier will +further raise it to~4, indicating that any |normal| identifier coming along +at the same brace level will be made |type_defined|. When that happens the +master counter drops to~1, indicating that it can still be rekindled by a +comma at the proper parenthesis level. We must be prepared to see more than +one typedef for the same identifier, as we may have scanned a header file +before seeing the source that produced that file, so we also let the master +counter drop to~1 if it was~4 and an identifier that is already +|type_defined| is seen. If however following the |typedef|, when the master +counter is~2, a |struct_like| identifier is seen, the master counter is +raised only to~3, so that a following identifier will not be made +|type_defined|, but rather pass the honour on by setting the master counter +to~4; this also happens when instead of an identifier a left brace is seen. +Finally, a semicolon at the right brace level will return a positive value +of the master counter to~0. + +@< Keep track of tokens relevant to |typedef| declarations @>= +{ if (typedef_master==0 && + next_control==identifier && cur_id->ilk==typedef_like) + @/{@; typedef_master=2; brace_level=par_level=0; } + else if (typedef_master>0) switch(next_control) + { case identifier: + if (brace_level==0) + if (typedef_master==2) + { if (cur_id->ilk==int_like || cur_id->ilk==type_defined) + typedef_master=4; + else if (cur_id->ilk==struct_like) typedef_master=3; + } + else if (typedef_master==4) + { if(cur_id->ilk==normal||cur_id->ilk==type_defined) /* this is it */ + cur_id->ilk=type_defined, typedef_master=1; + } + else if (typedef_master==3) typedef_master=4; + break; + case '{': @+ + if (brace_level++==0 && typedef_master==3) typedef_master=4; @+ break; + case '}': --brace_level; break; + case ',': @+ + if (typedef_master==1 && par_level==0) typedef_master=4; @+ break; + case '(': ++par_level; break; + case ')': --par_level; break; + case ';': @+ if (brace_level==0) typedef_master=0; + } + if (C_plus_plus) + @< Take action to mark identifiers following \&{class} as |type_defined| @> +} + +@ In \Cpp, any identifier that follows \&{class} (or |struct| of |union|) is +considered as a typedef identifier, i.e., every time some \&{class~x} is +encountered, an implicit `|typedef| \&{class~x~x}' is assumed. + +@< Take action to mark identifiers following \&{class}... @>= +{ static boolean class_seen=false; + if (class_seen) + { if (next_control==identifier && cur_id->ilk==normal) + cur_id->ilk=type_defined; + class_seen=false; + } + else if (next_control==identifier && cur_id->ilk==struct_like) + class_seen=true; +} + +@ We now consider |typedef| declarations in Phase~II. In Phase~I we have set +the |ilk| of the defined identifiers to~|type_defined|, which will make them +behave as |int_like|; although this works fine everywhere else, it thwarts a +correct parse of their |typedef| declaration itself during Phase~II. In +fact, according to the \caps{ANSI}/\caps{ISO}~\Cee\ syntax, the identifier +being declared in a typedef declaration should not be considered to be a +\\{typedef-name}, since that would make the declaration unsyntactical; this +is justified by the fact that the typedef identifier only comes into scope +after the declaration is completed. This is not a problem for single-pass +compilers, but it is for us: we have recorded the typedef declaration on the +first pass, and it will be active during the entire second pass, since we +cannot delimit the declaration to its proper range (we are not processing +the code in the same order that the \Cee~compiler will, and for the +commentary parts of sections, the concept of range does not even apply). To +attempt to write the grammar in such a way that it will accept typedef +declarations in which the defined identifier is |int_like| would be very +difficult, since without the help of some rather remote context, a +declarator of this kind can not always be distinguished form an abstract +declarator; compare the declaration `\hbox{|typedef char *(example[4]);|}', +which declares |example| to specify the type ``array of 4 pointers to +character'', with a valid declaration `|void f(example[3]);|' that might +follow it, declaring |f| as a function that takes as an argument an array of +3 such |example| objects (in fact the argument will be passed as a pointer to +|example|, of course). + +So, rather than solving the problem in a syntactic way, we stoop down to +emulating a one-pass system by setting the |category| of the defining +occurrence of an identifier in a typedef declaration explicitly to +|expression|, despite the fact that its |ilk| is |type_defined|. The defining +occurrence is located by the same lexical means used in Phase~I, in fact, by +using the same intricate succession of states of |typedef_master| that was +used there. A difference is that at the {\sl moment supr\`eme\/} the +identifier found is now |type_defined| rather than |normal|. The fact that +this identifier is not necessarily the only or first |type_defined| +identifier in the declaration, and that is has to be recognised in a +left-to-right pass, may explain some of the details of our code, for +instance why |const_like| had to be distinguished from both |int_like| and +|type_defined|; one may compare `\hbox{|typedef unsigned long int * const +example;|}' with `\hbox{|typedef const example * examp2;|}'. + +With respect to the code for Phase~I, there is also a slight difference in +the way the code is hooked into the program, since the function |C_read| +already contains a |switch| on the value of |next_control|, which has a +special case for identifiers. So let us first consider the identifier cases +(including reserved words). + +@f example scrap /* pretend |example| is |type_defined| */ +@f examp2 example /* and |examp2| is another such identifier */ + +@< Track identifiers... @>= +{ if (typedef_master==0 && cat==typedef_like) + typedef_master=2, brace_level=par_level=0; + else if (typedef_master>0 && brace_level==0) + if (typedef_master==2) + { if (cat==int_like || cat==type_defined) typedef_master=4; + else if (cat==struct_like) typedef_master=3; + } + else if (typedef_master==4 && cat==type_defined) /* this is it */ + cat=expression, typedef_master=1; + else if (typedef_master==3) typedef_master=4; +} + +@ And here are the relevant cases of non-identifiers. + +@< Cases necessary for proper parsing of typedefs @>= + +case '{': @+ + if (typedef_master>0 && brace_level++==0 && typedef_master==3) + typedef_master=4; + @+break; +case '}':@+ if (typedef_master>0) --brace_level; @+break; +case ',':@+ if (typedef_master==1 && par_level==0) typedef_master=4; @+break; +case '(':@+ if (typedef_master>0) ++par_level; @+break; +case ')':@+ if (typedef_master>0) --par_level; @+break; +case ';':@+ if (typedef_master>0 && brace_level==0) typedef_master=0; @+break; + + +@i parser.w +@i rules.w + + +@* Output of tokens. @:title@> +Now that we have treated the highest level of processing by \.{\me.}, we +shall have to descend again to the level of character strings, which +eventually have to written to the output file. Our first concern is to +linearise the multi-layered token lists into a sequence of output tokens. +The output of special layout tokens is affected by whether or not they were +generated from within `\pb', so there are two modes of output: during output +of `\pb' we are in |inner| mode, and otherwise in |outer| mode. +A switch from |outer| to |inner| mode can occur in the middle of a text, +namely for `\pb' fragments inside comments; this is indicated by the fact +that the reference to the translation of the fragment is tagged with +|inner_text_flag|. Apart from the fact that the mode is set to |inner| +during the output of such subtrees, no traces of the tree structure of the +internal representation of texts are left after linearisation. + +Linearising texts is therefore a straightforward process, that is easy to +implement using a stack. In the linearised stream of tokens certain +sequences of tokens, particularly layout tokens, have to be considered +together, e.g., |cancel| will remove any adjacent line-breaking tokens. No +line-breaking tokens will occur at either end of the output of a complete +text either; if a line break is required there, the caller of the output +routines will supply it explicitly. For identifiers, reserved words and +module names, the transformation of a single token into a sequence of +characters is handled by separate functions to be discussed later. For +module names the transformation may result in a recursive call of the output +routines via the processing of `\pb' fragments by~|do_C|, but this recursion +in never more than one level deep, since such fragments cannot contain +module names. +@^recursion@> + +@< Prototypes @>= +void out_identifier (id_pointer); +void out_keyword (id_pointer); +xref_pointer out_module_name(mod_pointer); + +@ The stack that is used to keep track of token lists at different levels +of output is similar to the one used in |CTANGLE|. Entries have three parts: +for the token list at each level |tok_field| and |end_field| record where we +are respectively where we should stop, and |mode_field| records the output +mode. The current values of these quantities are referred to quite +frequently, so they are stored in a separate place, and are called +|cur_tok|, |cur_end|, and |cur_mode|. + +@d cur_tok cur_state.tok_field /* location of next output token in |tok_mem| */ +@d cur_end cur_state.end_field /* current ending location in |tok_mem| */ +@d cur_mode cur_state.mode_field /* current mode of interpretation */ + +@<Typedef...@>= +typedef enum { inner, outer } mode; +typedef struct +{ token_pointer tok_field; /* present location within token list */ + token_pointer end_field; /* ending location of token list */ + mode mode_field; /* interpretation of control tokens */ +} output_stack_element, *stack_pointer; + +@ The stack grows upwards with the global variable |stack_ptr| pointing to +the first vacant location above the top of the stack. The entry |stack[0]| +at the bottom of the stack is not used; when |stack_ptr| points to it then +|cur_state| itself has become invalid and the output process is completed. +Therefore we can use |stack[0]| to store |cur_state| in. + +@d cur_state stack[0] /* the currently active state variables */ +@d stack_end (&stack[stack_size]) /* end of |stack| */ + +@<Global...@>= +output_stack_element stack[stack_size]; /* info for non-current levels */ +stack_pointer stack_ptr=&stack[0]; + /* first unused location in the output state stack */ +#ifdef STAT +stack_pointer max_stack_ptr = stack; /* largest value assumed by |stack_ptr| */ +#endif + +@ To insert token list |p| into the output, the function |push_level| is +called; it saves the old level of output and gets a new one going. +Conversely, the macro |pop_level| restores the conditions that were in force +when the current level was begun. The value of |cur_mode| is not changed by +|push_level|, but it might be changed explicitly directly after the call; if +so this setting will remain in effect until the matching invocation of +|pop_level|. At the beginning of |make_output|, a call to |push_level| is +made to put the root text into the output stream. If this is not a recursive +call to |make_output|, the old (undefined) value of |cur_state| will be +``saved'' into |stack[0]|, which is |cur_state| itself; although this is +redundant, no harm is done. + +@d pop_level() cur_state = *--stack_ptr +@c +void push_level (text_pointer p) /* suspends the current level */ +{ if (stack_ptr==stack_end) overflow("stack"); @.stack capacity exceeded@> + *stack_ptr++=cur_state; +#ifdef STAT + if (stack_ptr>max_stack_ptr) max_stack_ptr=stack_ptr; +#endif + cur_tok=text_begin(p); cur_end=text_end(p); +} + +@ The function |make_output| traverses the nested token lists using the +stack, and producing the corresponding output. Since it can be called +recursively, the initial value of |stack_ptr| is saved in |stack_bot|; we +return from |make_output| when |stack_ptr| drops back again to this level. +When we encounter tokens marked with |id_flag|, |res_flag| and |mod_flag|, we +respectively call |out_identifier|, |out_keyword| and |out_module_name|. +As mentioned above, the last of these may result in a recursive call to +|make_output| via |do_C|; because calls of |do_C| also involve +lexical scanning, the values of |next_control|, |cur_id| are saved, and +restored when |make_output| is complete. +@^recursion@> + +@c +void make_output(text_pointer t,mode m) /* output a complete text */ +{ int save_next_control=next_control; id_pointer save_cur_id=cur_id; + stack_pointer stack_bot=stack_ptr; token state=cancel; + push_level(t); cur_mode=m; + do + if (cur_tok==cur_end) pop_level(); + else + { token a= *cur_tok % id_flag; + switch (*cur_tok++/id_flag) + { + @\@< Cases 1, 2, 3: identifiers, reserved words and module names @> + case 4: push_level(text_at(a)); break; + case 5: push_level(text_at(a)); cur_mode=inner; break; + case 0: @< Output the character or format control |a| @> + } + } + while(stack_ptr>stack_bot); + @< Complete any unfinished work at the end of the translation @> + next_control=save_next_control; cur_id=save_cur_id; +} + +@ To handle the proper interaction of adjacent output tokens, we keep track +of an integer |state| variable to record that a possibly unfinished sequence +is being processed. The state is mainly affected by the output of format +control tokens like |break_space|, so for convenience we often set the state +equal to the value of such a token. If |state==0| the most recent token was +an ordinary one, and no special action is called for. If |state| has one of +the values |space|, |tilde|, |break_space|, |force|, |big_force|, |backup|, +or |big_backup|, it is the upper bound in the sense mentioned before of a +sequence of such tokens that has recently passed (where |space|~and~|tilde| +represent the tokens |' '|~and~|'~'|, respectively). We call such states +`white-space states'; when an ordinary character comes along in a +white-space state, the token recorded in |state| is first output. + +@< Output white space if specified by |state| @>= +{ if (state>=space) + if (state<break_space) out(state==space ? ' ' : '~'); + else + { out('\\'); + if (state<backup) out(state-break_space+'5'); + /* `\.{\\5}', `\.{\\6}', or `\.{\\7}' */ @.\\5@> @.\\6@> @.\\7@> + else out(state-backup+'6'),out_str("\\4"); + /* `\.{\\6\\4}' or `\.{\\7\\4}' */ @.\\6@> @.\\7@> @.\\4@> + finish_line(); + } + state=0; +} + +@ The following three cases now become easy. +@< Cases 1, 2, 3... @>= +case 1: @< Output white space... @>@+ out_identifier(id_at(a)); break; +case 2: @< Output white space... @>@+ out_keyword(id_at(a)); break; +case 3: @< Output white space... @>@+ out_module_name(mod_at(a)); break; + +@ Whether or not a forced break is required at the end of the translation is +determined by the function calling |make_output|, not by the code being +translated; therefore we do not invoke |@< Output white space... @>| at the +end, and if a white-space state was set, it will sink silently into +oblivion. An exception is made for |big_force| or |big_backup|, for which we +output `\.{\\Y}' so that the extra vertical white space (which must have been +inserted explicitly by a \:) control code) will not vanish, for instance +when it occurs between macro definitions. + +@< Complete any unfinished work... @>= +{@; if (cur_mode==outer && (state==big_force || state==big_backup)) + out_str("\\Y"); +} + +@ White-space format controls operate by modifying |state| when appropriate, +as described above. The same holds basically for |' '| and |'~'|, but these +characters can also occur in ordinary text inside comments, where they must +not be contracted, so we take some care that in such a position (where +|cur_mode==outer| holds), they are output as ordinary characters. In +white-space states |opt| and the digit following it are ignored. Like the +white-space format controls, the token |cancel| also sets |state| to its own +value, but it ignores the value it previously had. In this state any spaces +and format control tokens except |indent|, |outdent| and |flush_left| are +ignored. We also have set |state==cancel| at the beginning of |make_output|, +so that there will be no forced break or white space at the beginning of the +translation. + +The tokens |indent| and |outdent| are output directly without altering +|state|; effectively this means that they will be moved in front of any +white-space format controls that preceded them. This is important because +the format controls that are explicitly inserted by codes like \:/ will +stick to the token to their left, so that any indentation changes generated +by the syntax rules at the same point will necessarily come {\it after\/} +them, while in fact these indentation changes must be allowed to affect the +indentation at explicitly inserted line breaks. With the exceptions noted +above, |opt| and |flush_left| are transmitted like normal tokens. + +When |cur_mode==inner| things are a bit different. The tokens |indent|, +|outdent|, and |flush_left| are completely ignored, and all white-space +tokens except |'~'| are treated like |' '|, i.e., they set |state=space|. +Optional breaks are produced using `\.{\\0}' rather than `\.{\\3}' to avoid +a ragged right margin within a paragraph. + +@< Output the character or format control |a| @>= +{ switch (a) + { case relax: @< Output white space... @>@+ break; + case cancel: state=cancel; break; + case indent: case outdent: + if (cur_mode==outer) {@; out('\\'); out(a-indent+'1'); } + break; @.\\1@> @.\\2@> + case opt: + { int digit=*cur_tok++; + if (state==0) + {@; out('\\'); out(cur_mode==outer ? '3' : '0'); out(digit); } + @.\\3@> @.\\0@> + break; + } + case flush_left: + if (cur_mode==outer) + {@; @< Output white space... @> out_str("\\8"); } @.\\8@> + break; + case big_force: case backup: + if (a+state==big_force+backup) a=big_backup; /* fall through */ + case break_space: case force: case big_backup: + if (cur_mode==inner) a=space; + up_state: + if (state!=cancel && state<a) state=a; + break; + case ' ': case '~': + if (cur_mode==inner) {@; a= a==' ' ? space : tilde; goto up_state; } + if (state==cancel || state>=break_space) break; + /* else fall through */ + default: @< Output white space... @>@+ out(a); + } +} + +@* Low-level output routines. @:title@> +We now come to the functions that write individual tokens and characters. +We start with a simple function to output a section number in decimal +notation. The number to be converted by |out_sec_nr| is known to be less +than |def_flag|, so it cannot have more than five decimal digits. If the +section was changed, we output `\.{\\*}' just after the number. + +@c +void out_sec_nr (int n) /* output a section number */ +{ char s[6]; + sprintf(s,"%d",n); out_str(s); + if (section_changed(n)) out_str ("\\*"); @.\\*@> +} + +@ Since we want to typeset identifiers such as \.{catch22} as |catch22|, which +is achieved by writing the string `\.{\$\\\\\{catch\}\_\{22\}\$}' to the +\TeX~file, it is useful to have an auxiliary function |out_id_part| that +will output a substring of an identifier (or reserved word), enclosing it in +braces if it contains more than one character. The substring is specified by +a pointer and a length count. A call to |out_id_part| for the full name +referred to by an |id_pointer p@;| is achieved by invoking |out_id_full(p)|. + + +@d out_id_full(p) out_id_part(name_begin(p),length(p)) + +@c +void out_id_part (char* s, int l) +{ boolean b=l!=1; + if (b) out ('{'); + while (--l>=0) {@; if (*s=='_') out ('\\'); out (*s++); } + if (b) out('}'); +} + +@ For index entries we do not wish to give underscores a special treatment, +so we define a function |out_index| to use in place of |out_id_full|. Index +entries of length~0 are discarded on input, so they never come here; for +index entries of length~1 we omit braces. + +In \LKC., there is no function like |out_index|, and the equivalent of +|out_id_part| is used instead, and as a consequence, underscores are +automatically escaped in index entries, but other special characters are +not. While there is no particular rhyme or reason to such a behaviour, user +have learned to adapt to it, and so, in an attempt at bug-to-bug +compatibility, we transfer control to |out_id_part| in compatibility mode. + +@c +void out_index (id_pointer p) +{ char* s=name_begin(p); boolean b= s[1]!='\0'; + if (compatibility_mode) {@; out_id_full(p); return; } + if (b) out ('{'); + out_str(s); + if (b) out('}'); +} + +@ The function |out_keyword| simply prefixes `\.{\\\&}' to the output of +|out_id_full|. + +@c +void out_keyword(id_pointer p) @+{@; out_str("\\&"); out_id_full(p); } +@.\\\&@> + +@ The function |out_identifier| formats ordinary identifiers (those that +were stored with |id_flag|). Yet there is some variation in the way these +are formatted. Identifiers whose |ilk| is |TeX_like| or |NULL_like| are +converted into control sequences. If only one alphabetic character is +present, it is set in math (rather than text) italic, otherwise if all +characters are either upper case letters or digits, the identifier is set in +typewriter type, and in the remaining, most common, case, it is set in text +italic. If math or text italic are used, a trailing sequence of digits in +the name, if present, is set as a subscript to the rest of the identifier. + +@c +void out_identifier (id_pointer p) +{ int k=length(p); eight_bits* ch=(eight_bits*)name_begin(p); +@/enum { ord, indexed, caps, single, indexed_single } kind; + if (p->ilk==TeX_like || p->ilk==NULL_like) @.\\NULL@> @.\\TeX@> + @/{@; + @< Output the identifier |p| in the form of a \TeX\ control sequence @> + return; + } + if (!compatibility_mode) + { do --k; while (isdigit((eight_bits)ch[k])); /* terminates because |!isdigit(ch[0])| */ + ++k; /* point to end of identifier without its index (if any) */ + } + @< Determine the |kind| of |p|, and possibly output some + leading characters @> + if (kind==indexed || kind==indexed_single) + { out_id_part(name_begin(p),k); /* main part */ + out ('_'); out_id_part(name_begin(p)+k,length(p)-k); /* subscript */ + } + else out_id_full (p); +} + +@ When an identifier is output in the form of a control sequence, +underscores are replaced by `\.x' so that they will become part of the +control sequence. If the |ilk| of the identifier was |TeX_like|, the control +sequence will be processed in horizontal mode with italic type selected; if +the |ilk| was |NULL_like|, it will be processed in math mode. + +@< Output the identifier |p| in the form of a \TeX\ control sequence @>= +{ if (p->ilk==TeX_like) out_str("\\\\{"); + out('\\'); @+ do out(*ch=='_' ? 'x' : *ch); while (*++ch!='\0'); + if (p->ilk==TeX_like) out('}'); +} + +@ For identifiers that consist of a single character, not counting any +trailing digits, that character is written in unadorned form, since it will +be processed in math mode, so that the math italic font will be used. We do +output a space before the character however, to eliminate the possibility +that it is captured by a control word at the end of the previous output. + +@< Determine the |kind|... @>= +if (k==1) {@; out(' '); kind= length(p)==1 ? single : indexed_single; } +else +{ int i=k; @+ + while (--i>=0) @+ if (!isupper(ch[i])&&!isdigit(ch[i])&&ch[i]!='_') break; + kind= i<0 ? caps : k<length(p) ? indexed : ord; + out('\\'); out(kind==caps ? '.' : '\\'); @.\\\\@> @.\\.@> +} + +@ The last function for output of tokens, |out_module_name|, represents the +most complicated aspect of such output, since up to here we have not paid +any attention to `\pb' constructions in module names. When these are present +they will need a complete treatment by the scanning, parsing and general +output routines described above. Now |out_module_name| does not involve +itself directly in such actions---they are contained in a call to +|do_C|---but we do have to be aware that we are in the midst of scanning and +output operations going on at a different level, and be careful not to +disturb them. The text between `\pb' will be placed at the end of the active +input buffer (this is why the buffer has size |long_buf_size| rather than +|buf_size|) and the translation process uses the end of the active |tok_mem| +area. + +On the other hand |out_module_name| is also used during Phase~III while +writing the list of module names (which implies that scanning and parsing is +not over at the end of Phase~II). We want |out_module_name| to behave +slightly differently in Phase~III however, by printing a full list of +section numbers in which the module is defined rather than just the first +one. Also, immediately after calling |out_module_name| we will need a +pointer to the sublist of \xr.s to sections in which the module is used, and +since this sublist starts at the end of the list of defining sections, we +might as well return it as result from |out_module_name|. + +@c +xref_pointer out_module_name(mod_pointer name) +{ xref_pointer x=name->xref; boolean file_module= x->num==file_flag; + if (file_module) x=next_xref(x); + out_str ("\\X"); @.\\X@> + if (x->num>=def_flag) + { out_sec_nr(x->num-def_flag); /* output the defining section number */ + if (phase==3) /* all of them in Phase III */ + while (x=next_xref(x), x->num>=def_flag) + out_str (", "), out_sec_nr(x->num-def_flag); + } + else out ('0'); /* section number `0' means `nowhere defined' */ + out (':'); if (file_module) out_str("\\.{"); @.\\.@> + @<Output the text of the module name@> + if (file_module) out_str ("}"); + out_str ("\\X"); + return x; +} + +@ Copying \TeX\ text from a module name to the output is fairly simple, but +we shouldn't forget to escape special characters if this is actually a file +name (which is typeset verbatim), even though they are probably quite rare. + +@< Output the text... @>= +{ char* k=name_begin(name),c; + while ((c=*k++)!='\0') + { if (file_module) @+{@; if (strchr(" \\#%$^{}~&_",c)!=NULL) out ('\\'); } + if (c=='@@' && *k++!='@@') @< Report illegal control code in module name @> + if (file_module || c!='|') out(c); + else + { char* save_loc=loc, *save_limit=limit; + @< Copy the \Cee\ text into the |buffer| array @> + do_C(); loc=save_loc; *(limit=save_limit)=' '; + } + } +} + +@ We haven't checked for illegal control codes in module names yet, so +we should report an error if we encounter one. + +@< Report illegal control... @>= +{@; print("\n! Illegal control code in module name"); print_mod(name); + @.Illegal control code...@> + mark_error(); +} + +@ Within `\pb' we should be aware of character and string constants, since +any `\.\v' occurring there is not the closing one. The variable |delimiter| +is zero outside such constants, otherwise it equals the delimiter that began +the constant. We copy the opening and closing `\.\v' into the buffer, so +that an error message that displays the whole buffer will look a little bit +sensible. We also add a space at the end (just like |get_line| does) so that +the closing `\.\v' cannot by accident be parsed as part of an operator such +as `\.{\v=}'. Putting |next_control='|'| completes the proper initial +conditions for calling |do_C|. We need not test for overflow of |buffer| +here, since we start filling it at position |limit| which is at most +|&buffer[buf_size-2]|, and add at most |longest_name| characters from the +module name, so that if worst comes to worst, the final space stored at the +end is at position |&buffer[long_buf_size-2]| leaving one more place after +|limit|, which is occasionally needed by |get_next|. + +@< Copy the \Cee\ text into... @>= +{ char delimiter='\0'; /* |'"'| or |'\''|, or |'\0'| outside of strings */ + next_control=*limit++='|'; loc=limit; + do + if ((c=*k++)=='\0') @< Report a runaway \Cee~text @> + else + { *limit++=c; + @/@< In special cases copy the character after |c|, + or change |delimiter| @> + } + while (c!='|' || delimiter!='\0'); + *limit=' '; +} + +@ Here cases like `\.{1+@@\v2}', `\.{@@'\v'}' and `\.{'\\''}' within `\pb' +in module names are handled properly, as well as the more ordinary cases of +character constants and strings. In case of a control code, the character +following the `\.@@' cannot be the null character that terminates the module +name, because it is impossible to enter a module name that ends with a +single `\.@@'. If the user is being really devious by saying `\.{@@< Sudden +\v"death\\ @@>}', then the final `\.\\' is silently removed to avoid +disaster to \.{\me.}. + +@< In special cases... @>= +{ if (c=='@@') /* control code; now |*k!='\0'| */ + { if ((*limit++=*k++)=='\'' && delimiter=='\0') /* copy code, test for \:' */ + delimiter='\''; /* which behaves like `\.'' */ + } + else if (c=='\\' && delimiter!='\0') + { char d=*limit++=*k++; /* escaped character, possibly |delimiter| */ + if (d=='\0') --k,limit-=2; /* remove backslash, error is issued anyway */ + } + else if (c=='\'' || c=='"') + if (delimiter=='\0') delimiter=c; + else if (delimiter==c) delimiter='\0'; +} + +@ If the module name ends before the \Cee~text does, we add an |'|'|, and if +a string or character constant was left unclosed we prepend the closing +delimiter to it: we must prevent that |do_C| will ever call |get_line|, even +in erroneous situations. In these cases there is a slight chance that we +might overflow |buffer|, in which case we quit since the user is just trying +to break \.{\me.} anyway. + +@< Report a runaway \Cee~text @>= +{ print("\n! C text in module name didn't end"); print_mod(name); + @.C text...didn't end@> + mark_error(); + if (delimiter!='\0') *limit++=delimiter; + *limit++='|'; + if (limit>&buffer[long_buf_size-2]) + fatal("fix that first, you sneaky devil"); + @.fix that first...@> + break; +} + +@ Finally we come to the point where the characters produced are sent off to +the \TeX~file. The \TeX\ output is supposed to appear in lines at most +|line_length| characters long, so we place it into an output buffer +|out_buf|. The first character of the output buffer is used as a sentinel, +and is not actually written to the output file, so we usually refer to the +output buffer via |out_line|. The pointer |out_ptr| indicates the next +position to be used in the output buffer. During the output process, +|out_line_nr| will hold the current line number of the line about to be +output; it is only used for (rather unlikely) diagnostic messages. + +@d out_line (&out_buf[1]) /* start of actual output line */ +@d out_buf_end (&out_line[line_length]) /* end of |out_buf| */ + +@<Global...@>= +char out_buf[line_length+1]; /* assembled characters */ +char* out_ptr; /* first unused position in |out_buf| */ +int out_line_nr=1; /* number of next line to be output */ + +@ The auxiliary function |flush_buffer| empties the buffer up to a given +breakpoint~|b|, and moves any remaining characters to the beginning of the +buffer, so that they will appear on the next line. If the |percent| +parameter is |true| a |'%'| is appended to the line that is being output; in +this case the breakpoint |b| should be strictly less than |out_buf_end|. If +the |percent| parameter is |false|, trailing blanks are suppressed. The +characters emptied from the buffer form a new line of output. + +@d tex_putc(c) putc (c, tex_file) +@d tex_new_line() (putc('\n', tex_file),++out_line_nr) +@d tex_printf(format) fprintf(tex_file, format) + +@c +void flush_buffer(char* b, boolean percent) + /* output from |out_line| to |b|, where |b<=out_ptr| */ +{ int j=(int)(b-out_line); /* number of characters to be output */ + if (!percent) @+ while (j>0 && out_line[j-1]==' ') --j; + /* remove trailing blanks */ + fprintf(tex_file, "%.*s",j,out_line); + if (percent) tex_putc('%'); + tex_new_line(); + { char* p=out_line; + while (b<out_ptr) *p++=*b++; /* shift back remainder of line */ + out_ptr=p; /* adjust to end of (possibly empty) shifted part */ + } +} + +@ The usual way to send a completed line to the output is to call +|finish_line|. When we are copying \TeX\ source material, we retain line +breaks that occur in the input, except that an empty line is not output when +the \TeX\ source line was not entirely blank. For example, a line of the +\TeX\ file that contains only an index \xr. entry will not be copied. Since +|get_line| has removed trailing blanks, the input line is blank if and only +if it is empty; similarly the emptiness of the output line is tested by a +simple comparison because |copy_limbo| and |copy_TeX| have not copied spaces +into an empty output line. The |finish_line| routine is called just before +|get_line| inputs a new line, and just after a line break token has been +emitted during the output of translated \Cee~text. + +@d output_line_empty() (out_ptr==out_line) + +@c +void finish_line(void) /* do this at the end of a line */ +{ if (!output_line_empty()) flush_buffer(out_ptr, false); + else if (limit==buffer) tex_new_line(); /* copy blank input line */ +} + +@ In particular, the function |finish_line| is called near the very +beginning of Phase~II. We initialize the output buffer in such a way that +the first line of the output file will be `\.{\\input cwebxmac}', or +`\.{\\input cwebcmac}' in compatibility mode. At position~0 of the output +buffer, which is never really output, we put a space that will help make the +function |break_out| a little faster. + +@< Set initial... @>= +{ char* line1=" \\input cwebxmac"; + out_ptr=&out_buf[0]; @+ do *out_ptr++=*line1++; while (*line1!='\0'); + if (compatibility_mode) out_ptr[-4]='c'; /* change to \.{cwebcmac} */ +} + +@ When we wish to append one character |c| to the output buffer, we write +`|out(c)|'; this will cause the buffer to be broken at a sensible place and +flushed, if it was already full. If we want to append more than one +character at once, we say |out_str(s)|, where |s| is a string containing the +characters. + +@d out(c) + *(out_ptr>=out_buf_end ? (break_out(),out_ptr++) : out_ptr++)=c + +@c +void out_str (char* s) @+{@; while (*s!='\0') out (*s++); } + +@ The function |break_out| will determine a good break point in the output +buffer when it is about to overflow. + +@< Prototypes @>= +void break_out (void); + +@~For speed we search from right to left for a proper break point, although +in this direction we cannot know whether we are inside a control sequence. +Nevertheless any blank space is a safe break point, as is the position just +before a backslash that isn't preceded by another backslash. If the break is +not at a space, a |'%'| is output at the break. + +@c +void break_out (void) /* finds a way to break the output line */ +{ char* k=out_ptr,c; int count=0; /* number of backslashes seen */ + do @+ if ((c=*--k)==' ') goto found; @+ while (c!='\\'); + do ++count; while ((c=*--k)=='\\'); +found: + if (++k>out_line) flush_buffer(k,c!=' '); @+ else @< Break peculiar line @> +} + +@ We get to this section only in the unusual case that the entire output line +consists of a string of backslashes followed by a string of characters that +contains no spaces or backslashes. Depending on the number |count| of +backslashes we handle this case as well as possible. If the |count>=2| we +split off a maximal even number of initial backslashes, and if |count==0| we +break the line by putting a |'%'| at the position of the last character. In +the latter case we do not place the |'%'| after the last character, since +that would make the output line one character longer than we promised; +similarly we are cautious in the former case not to place a |'%'| after a +buffer completely filled with backslashes. If |count==1| the line is +probably one enormous control word, and in this unlikely case we simply +output the whole line as is stands, after issuing a warning message. + +@< Break peculiar line @>= +if (count==0) flush_buffer(out_ptr-1,true); +else if (count>=2) flush_buffer + (&out_line[count&=~1]==out_buf_end ? out_buf_end-2 : &out_line[count],true); +else +{ print("\n! Line had to be broken (output l.%d):\n",out_line_nr); + @.Line had to be broken@> + term_write(out_line,out_ptr-out_line); new_line(); mark_harmless(); + flush_buffer(out_ptr,false); +} + + +@* Phase III processing. @:title@> +After Phase~II is completed, \.{\me.}'s only remaining task is writing out +the index, after sorting the identifiers and index entries, and a list of +module names. If the user has set the |no_xref| flag (the \.{-x} option on +the command line), we just finish off the page, omitting the index, module +name list, and table of contents. + +@d triple_file_output flags['t'] +@d even_out_pages flags['e'] + +@c +void phase_three (void) /* output the \xr. index */ +{ finish_line(); + if (no_xref) out_str("\\endcodemode\\vfill\\end"); + else + { phase=3; print_progress("\nWriting the index..."); + @.Writing the index...@> + typedef_tracking(false); /* during parse of `\pb' in module names */ + if (change_exists) + {@; @<Tell about changed sections @> finish_line(); } + if (triple_file_output) + @< Finish the \TeX~file and open the index file @> + else {@; out_str("\\inx"); finish_line(); } @.\\inx@> /* index */ + @<Sort and output the index@> + if (triple_file_output) + @< Finish the index file and open the module listing file @> + else {@; out_str("\\fin"), finish_line(); } @.\\fin@> /* end of index */ + @<Output all the module names@> + if (!triple_file_output) + { out_str("\\con"); @.\\con@> /* table of contents */ + if (even_out_pages) out_str("even"); @.\\coneven@> + } + } + finish_line(); fclose(tex_file); + print_progress("\nDone.\n"); + check_complete(); /* was all of the change file used? */ +} + +@ When output is to be distributed over three files, we can already finish +off the main output file, since it ends in a standard way: with `\.{\\input}' +commands for the two other files (that still have to be written) and either +`\.{\\con}' or `\.{\\coneven}' to produce the table of contents. Since the +three output files are written one after the other, we can reuse the file +pointer |tex_file| for all of them. + +@< Finish the \TeX~file and open the index file @>= +{ out_str("\\inx \\input \\jobname.idx\n" @+ + "\\fin \\input \\jobname.scn\n" @+ + "\\con"); + if (even_out_pages) out_str("even"); + finish_line(); fclose(tex_file); + if ((tex_file=fopen(idx_file_name,"w"))==NULL) + fatal("! Cannot open \"%s\" as output file",idx_file_name); +} + +@~The switch from the index file to the module listing file is similar, but +simpler. + +@< Finish the index file and open the module listing file @>= +{ finish_line(); fclose(tex_file); + if ((tex_file=fopen(scn_file_name,"w"))==NULL) + fatal("! Cannot open \"%s\" as output file",scn_file_name); +} + +@ Just before the index comes a list of all the changed sections, including +the index section itself. The outer |while| loop will terminate without +overstepping the bounds of |changed_section| in the inner loop since +|section_changed(section_count)| holds. + +@<Tell about changed sections@>= +{ int k=0; boolean first=true; + out_str("\\ch "); @.\\ch@> /* changes */ + while (k<section_count) + { do ++k; while (!section_changed(k)); + if (first) first=false; @+ else out_str(", "); + out_sec_nr(k); + } + out('.'); +} + +@ A left-to-right radix sorting method is used, since this makes it easy to +adjust the collating sequence, and the running time will be at worst +proportional to the total length of all entries in the index. +During the sorting phase the partially sorted index entries that have not +yet been output are held on a stack of identifier lists called |sort_info|. +Each of these lists is unsorted, but between elements of different lists the +ordering is completely known: for any pair of lists, all elements of the +list nearer to the top of the stack precede all elements of the other list +(this ordering is chosen because elements are output at the top of the +stack). The entries of |sort_info| are structures of type |sort_node|; they +have a |head| field that points to the first name of the list, and a +|depth| field that tells how many initial characters are known to be equal +among the entries of the list. + +@<Typedef...@>= +typedef struct {@; id_pointer head; int depth; } sort_node, * sort_pointer; + +@~In fact |sort_info| shares its memory with the |scrap_info| array that +serves no purpose at this moment (although it will be used again during +the output of module names). + +@d sort_info scrap_union.id_list_field +@d sort_info_end (&sort_info[sort_stack_size]) +@< Alternative use... @>= +sort_node id_list_field[sort_stack_size]; + +@ The variable |sort_ptr| points to the first unused slot above the top of +the stack in~|sort_info|. + +@<Global...@>= +sort_pointer sort_ptr=sort_info; +#ifdef STAT +sort_pointer max_sort_ptr=sort_info; /* largest value of |sort_ptr| */ +#endif + +@ The desired alphabetic order is specified by the |collate| array; namely, +|collate[0] < collate[1] < @t\dots@> < collate[end_collate-1]|. Upper case +letters are treated like the corresponding lower case letters, since we want +to have `|t<TeX<@[token@]|'. Therefore the collation array should have length +|UCHAR_MAX+1-26|. We are a bit extra cautious however, in case there are +implementations in which the verdict |isalpha(c)| is given (incorrectly) to +certain values |c>=128|, which would cause the part of |collate| that is +actually used to be shorter than that; therefore we compute the actual +length in |end_collate|. + +@<Global...@>= +eight_bits collate[UCHAR_MAX-25]; /* collation order */ +int end_collate; /* length of the part of |collate| actually used */ + +@ We use the order |'\0' < ' ' < @tother characters@> < '_' < 'A'=='a' < +@t\dots@> < 'Z'=='z' < '0' <@t\dots@> < '9'|. If there should be any +characters~|c| for which |tolower(c)| does not occur in |collate| (this can +only happen if |isalnum(c)| incorrectly holds), then entries containing the +character~|c| will not appear in the index. By computing |end_collate| +rather than using a constant |UCHAR_MAX-25|, we avoid however that the mere +existence of such characters would disrupt the proper order of the +index entries that do appear, due to spurious bytes |'\0'| at the end +of~|collate|. + +@<Set init...@>= +{ char *p="_abcdefghijklmnopqrstuvwxyz0123456789"; + int c='\1', k=2; + collate[0]='\0'; collate[1]=' '; + do @+ if (!isalnum(c) && c!='_' && c!=' ') collate[k++]=c; @+ + while (++c<=UCHAR_MAX); + while ((c=*p++)!='\0') collate[k++]=c; + end_collate=k; /* record the length */ +} + +@ The lists of identifiers used for sorting the index cannot be linked +together using |hash_link|, since we still need to be able to look up +identifiers when writing the list of module names. Therefore we declare a +separate array |index_link| that will provide the necessary links, and a +macro |ilink| used to access it: |ilink(p)| points to the successor of~|p| +(or is~|NULL|) for any |id_pointer p@;|. + +At each sorting step we partition a list of identifiers into at most +|UCHAR_MAX-25| sublists, based on the first character position where the +entries of the list are not known to be equal. After the partitioning step +the sublist for character~|c| is pointed to by |bucket[tolower(c)]|. + +@d ilink(p) index_link[id_index(p)] + +@<Global...@>= +id_pointer index_link[max_idents]; /* links identifiers during sorting */ +id_pointer bucket[UCHAR_MAX+1]; + +@ The basic sorting step is to pop the list from the top of the stack, and +either output it, if needs no further refinement, or to split it up into the +buckets, after which the function |unbucket| collects the non-empty buckets +and pushes them back on the stack. At the very first step however, the +identifiers do not come from the stack but directly from the identifier +table. In the remaining cases, the |depth| field of the element popped off +the stack is used to determine the character position used for splitting, +and is passed to |unbucket| so that it can set the proper depth when it +pushes lists back on the stack. When |depth| has risen to~|255| (or more +likely, has been set explicitly to that value by a previous |unbucket|), no +attempt is made to split up a list, even if it is not reduced to a +singleton. + +@d infinity 255 /* $\infty$ (approximately) */ + +@< Sort and output... @>= +{ @<Do the first pass of sorting@> + /* the first time, entries do not come from |sort_info| */ + unbucket(1); /* pick up first-order bucketed lists */ + while (sort_ptr>sort_info) /* i.e., the stack is not empty */ + { eight_bits depth=(--sort_ptr)->depth; + id_pointer name=sort_ptr->head; + if (ilink(name)==NULL || depth==infinity) + /* singleton or set of look-alikes */ + @< Output index entries for the list starting at |name| @> + else + {@; @< Split the list starting at |name| into further lists @> + unbucket(depth+1); + } + } +} + +@ To begin the sorting, we go through all the hash lists and put each entry +having a non-empty \xr. list into the proper bucket. The buckets are emptied +initially; they will also be emptied by each call to~|unbucket|. The entries +of the |index_link| array are initialised when their corresponding +identifiers are prepended to the list of some bucket. + +@<Do the first pass...@>= +{ id_pointer name; + eight_bits c=UCHAR_MAX; + id_pointer *h; /* pointer into |hash| */ + + do bucket[c]=NULL; while (c--!=0); + for (h=hash; h<hash_end; h++) + for (name=*h; name!=NULL; name=name->hash_link) + /* traverse all hash lists */ + if (name->ilk!=reference && name->xref->num!=0) + /* leave out unreferenced names */ + {@; c=name_begin(name)[0]; c=tolower(c); + ilink(name)=bucket[c]; bucket[c]=name; + } +} + +@ Here we split the list at the top of the stack into buckets. Entries of +length |depth| will bet sent to |bucket['\0']| due to the null character +stored at the end of identifier names; shorter entries do not come here +since they will already have been output. Since we are changing the |ilink| +of the front node of the list when putting it into its bucket, we must be a +bit more cautious then usual in traversing the list. + +@< Split the list... @>= +do +{ eight_bits c= tolower((eight_bits)name_begin(name)[depth]); + id_pointer next_name=ilink(name); /* save link */ + ilink(name)=bucket[c]; bucket[c]=name; name=next_name; + /* put into bucket */ +} +while (name!=NULL); + +@ The function |unbucket| goes through the buckets in the reverse order of +the collating sequence specified in the |collate| array, and adds non-empty +lists to the stack. The parameter |d| to |unbucket| tells the current depth +in the buckets; it will be recorded in all lists pushed on the stack during +this call, except that the contents of the bucket containing the identifiers +of length~|d-1| will be given depth |infinity| so that they will be output +directly afterwards. Any two sequences that agree up to the case of their +characters (with any characters beyond position~255 being ignored) are +regarded as identical and will be output in a random order. It is only +because of this case-merging that there could possibly be more than one +identifier of length~|d-1|, which makes setting the depth to |infinity| +necessary; in other cases the special attention is superfluous, since +singleton lists will be output directly anyway. + +@< Prototypes @>= +void unbucket (eight_bits); + +@~@c +void unbucket (eight_bits d) /* empties buckets having depth |d| */ +{ int i=end_collate; /* index into |collate| */ + while(--i>=0) @+ if (bucket[collate[i]]!=NULL) + { if (sort_ptr>=sort_info_end) + overflow("sorting"); @.sorting capacity exceeded@> + sort_ptr->depth= i==0 ? infinity : d; + /* |infinity| means there is nothing left to compare */ + sort_ptr++->head=bucket[collate[i]]; + bucket[collate[i]]=NULL; /* push and empty bucket */ +#ifdef STAT + if (sort_ptr>max_sort_ptr) max_sort_ptr=sort_ptr; +#endif + } +} + +@ Each line in the index file starts with the macro `\.{\\@@}', which will +be properly defined when it is encountered. + +@< Output index... @>= +do +{ out_str("\\@@"); @.\\@@@> + @<Output the index entry |name|@> + @<Output the \xr.s of |name|@> +} while ((name=ilink(name))!= NULL); + +@ In this section the distinction between |xref_roman|, |xref_wildcard|, and +|xref_typewriter| finally becomes effective. As first character (immediately +after `\.{\\@@}') we output a character `\.h' or `\.m',indicating whether the +index entry should be processed in horizontal or in math mode. +@.\\NULL@> @.\\TeX@> + +@<Output the index entry...@>= +switch (name->ilk) +{ case normal: case NULL_like: out('m'); out_identifier(name); break; + case TeX_like: out('h'); out_identifier(name); break; + case roman: out('h'); out_index(name); break; + case wildcard: out_str("h\\9"); out_index(name); break; @.\\9@> + case typewriter: out_str("h\\."); out_index(name); break; @.\\.@> + default: out('h'); out_keyword(name); +} + +@ Section numbers that are to be underlined are enclosed in +`\.{\\[}\dots\.]'. The first `\.{,\ }' will be scooped up by the macro +`\.{\\@@}'. + +@<Output the \xr.s...@>= +{ xref_pointer x=name->xref; + do + { sixteen_bits n=x->num; + out_str(", "); + if (n<def_flag) out_sec_nr(n); + else {@; out_str("\\["); out_sec_nr(n-def_flag); out(']'); } @.\\[@> + } while ((x=next_xref(x))->num!=0); + out('.'); finish_line(); +} + +@ The following recursive function traverses the tree of module names and +prints them. We use the order already present in the tree, which means that +we do not use the collation sequence that was employed for sorting the +index. If the user starts all module names with capital letters however, the +difference should hardly be noticeable. +@^recursion@> + +@< Prototypes @>= +void list_modules (mod_pointer); + +@~The macro `\.{\\@@}' is redefined to serve for lines in the list of module +names as well. Since module names are to be processed in math mode, we +enclose them in dollar signs. + +@c +void list_modules (mod_pointer p) /* print all module names in subtree |p| */ +{ if (p != NULL) + { list_modules(p->llink); +@/ out_str("\\@@$"); @.\\@@@> + leave_block(0); scrap_ptr=scrap_info; /* get ready for parsing */ + {@; xref_pointer x=out_module_name(p); out('$'); + footnote(&x,cite_flag); footnote(&x,0); + }@/ + finish_line(); +@/ list_modules(p->rlink); + } +} + +@~Initially |list_modules| is called for the root of the tree, of course. + +@<Output all the module names@>= list_modules(root); + +@ At the end of the run, if |STAT| was defined and the `\.{+s}' flag +present, we report how much of all the arrays was actually needed. + +@d report(k,c,m) + printf("\t%lu %ss (out of %lu)\n",(unsigned long)(c),k,(unsigned long)(m)) +@c +#ifdef STAT +void print_stats() +{ print("\nMemory usage statistics:\n"); @.Memory usage statistics@> +@/report("identifier", id_index(id_ptr), max_idents); +@/report("module name", mod_index(mod_ptr), max_modules); +@/report("byte", byte_ptr-byte_mem, max_bytes); +@/report("cross-reference", xref_ptr-xmem, max_refs-1); +@)printf("Parsing:\n"); +@/report("scrap", max_scr_ptr-scrap_info, max_scraps); +@/report("text", max_text_ptr-text_mem, max_texts); +@/report("token", max_tok_ptr-tok_mem, max_toks); +@/report("trie node", node_no, max_no_of_nodes); +@/report("level", max_stack_ptr-stack, stack_size); +@)printf("Sorting:\n"); +@/report("level", max_sort_ptr-sort_info, sort_stack_size); +} +#endif + + +@* Index. @:index@> @:title@> +If you have read and understood the code for Phase~III above, +you know what is in this index and how it got here. All sections in +which an identifier is used are listed with that identifier, except +that reserved words are indexed only when they appear in format +definitions, and the appearances of identifiers in module names are +not indexed. Underlined entries correspond to where the identifier +was declared. Error messages, control sequences put into the output, +and a few other things like ``recursion'' are indexed here too. diff --git a/web/c_cpp/cwebx/cwebcmac.tex b/web/c_cpp/cwebx/cwebcmac.tex new file mode 100644 index 0000000000..42d76748ec --- /dev/null +++ b/web/c_cpp/cwebx/cwebcmac.tex @@ -0,0 +1,77 @@ +% Compatibility mode macros for CWEBx (in addition to cwebxmac.tex) +% File: cwebcmac.tex, Author: Marc van Leeuwen, Date: November 1994 + +% This file gives definitions to macros defined in cwebmac.tex that are +% undefined in cwebxmac.tex, in order to simulate the cwebmac macro +% environment as much as possible in compatibility mode of CWEBx. +% In addition \PB is defined so as to get decent behaviour of |...| when used +% inside math mode (which one should't do, but some do it anyway). + +\ifx\documentstyle\undefined\else\endinput\fi % LaTeX will use other macros + +\input cwebxmac % that's the standard stuff +\xdef\fmtversion{\fmtversion c} + +\let\mc=\ninerm % medium caps +\let\sc=\eightrm % small caps (NOT a caps-and-small-caps font) +%\font\tenss=cmss10 \let\cmntfont\tenss % alternative comment font + +\def\CEE/{\Cee} +\def\UNIX/{\caps{UNIX}} +\def\TEX/{\TeX} +\def\CPLUSPLUS/{\Cpp} +\def\9#1{} +\let\cmntfont\tenrm + +\def\|#1{\leavevmode\hbox{$#1$}} % in case this is used directly +\def\ATP{\X\kern-.5em:Preprocessor definitions\X} +\let\ATH=\ATP % in case the user refers to \ATH directly +\def\PB#1{\ifmmode\hbox{#1}\else#1\fi} % make |...| safe in math mode + +\def\DC{\kern.1em{::}\kern.1em} % symbol for :: +\def\PA{\mathbin{.*}} % symbol for .* +\def\MGA{\mathbin{\MG*}} % symbol for ->* + +% Revert to Levy/Knuth representations of operators + +\def\K{=\Penalty2} % assignment operator +\def\E{\Penalty7\equiv} % equality test +\newbox\MGbox % symbol for -> +\setbox\MGbox=\hbox{\kern-2pt\lower3pt\hbox{\teni\char'176}\kern1pt} +\def\MG{\copy\MGbox} + +\secpagedepth=3 % page breaks will occur for depths -1, 0, and 1 +\let\SHC\C % "// short comments" treated like "/* ordinary comments */" +\outer\def\N#1 #2. #3. % start of `@*' section + {\global\gdepth=#1\global\advance\gdepth1 \global\gtitle={#3}\MN#2.% + \ifon \ifnum#1<\secpagedepth \vfil\eject + \else \vskip 0pt plus .5 \vsize \penalty-1000\vskip 0pt plus -.5 \vsize + \vskip\intersecskip + \fi + \fi + \message{*\secno} % progress report + \edef\next{\write\cont % to contents file + {\ZZ{#3}{\number\gdepth}{\secno}{\noexpand\number\pageno}}} + \next + \ifon\startsection{\bf#3.\quad}% + } + +\def\contentsline#1#2#3#4% #1==title, #2=depth+1, #3=secno, #4=pageno + {\ifnum#2=0 \smallbreak\fi + \line{\consetup{#2}#1 \rm\leaders\hbox to .5em{.\hfil}\hfil + \ #3\hbox to3em{\hss#4}% + }% + } +\def\consetup#1% #1=depth+1 + {\ifcase#1 \bf % @** + \or % @* + \or \hskip2em % @*1 + \or \hskip4em % @*2 + \or \hskip6em % @*3 + \or \hskip8em % @*4 + \or \hskip10em % @*5 + \else \hskip12em % depth >=6 + \fi + } + +\def\,{\relax\ifmmode\mskip\thinmuskip\else\thinspace\fi} diff --git a/web/c_cpp/cwebx/cwebhmac.tex b/web/c_cpp/cwebx/cwebhmac.tex new file mode 100644 index 0000000000..3b55c34e59 --- /dev/null +++ b/web/c_cpp/cwebx/cwebhmac.tex @@ -0,0 +1,143 @@ +% This is file cwebhmac.tex, part of the CWEBx distribution. + +% This file can be loaded after cwebxmac.tex, in order to automatically +% insert \specials for use by the xhdvi previewer, or any other HyperTeX +% dvi-file processing tool. Current version is alpha release. + +% Author: Marc van Leeuwen +% Last modified: 8 november 1996. + +% The following macros are redefined +% \stsec +% \note<text><section list>. +% \finnote<text><section list>. +% \X<number>:<module name>\X + +\ifx\hyperloaded\undefined \let\hyperloaded\relax \else \endinput\fi + + +% Low level interface to hyperlinks and targets + +% \hyperanchor<attributes><anchor text> +% general mechanism to define <achor text> as anchor with <attributes> +\long\def\hyperanchor#1#2{\special{html:<A #1>}{#2}\special{html:</A>}} + +% \hyperlink<URL><active text> +% makes <active text> an active link referring to <URL> +\def\hyperlink#1{\hyperanchor{href="#1"}} + +% \hypertarget<tag><target text> +% defines a local target for hyperlinks, that can be referenced by <tag> +\def\hypertarget#1{\hyperanchor{name="#1"}} + +% \hyperdef<kind><number><target text> +% defines a hypertarget classified by <kind> and sequence <number> +\def\hyperdef#1#2{\hypertarget{#1.#2}} + +% \hyperref<assignments><kind><number><active text> +% creates a hyperlink to local target created by \hyperdef<kind><number> +% <assignments> are optional assignments for expansion of <kind> and <number> +{\catcode`\#=12 \gdef\sharp{#}} % to access neutralised '#' character +\def\hyperref#1#2#3% +{{#1\xdef\next{{\sharp#2.#3}}}\expandafter\hyperlink\next} + + +% High level interface attached to CWEAVE-produced macros (or to those +% accessed by them via the definition of cwebxmac.tex) + +% Target attached to beginning of each section. <kind>=sec, <number>=\secno. +\def\stsec + {\endcodemode\noindent + \hyperdef{sec}\secno{\let\*=\lapstar\bf\secstar.}\quad + } +\let\startsection=\stsec % this one is used; provides hook for extra actions + +% List of links attached to end of some sections. +\def\note#1#2.% +{\Y\noindent{\hangindent2\indentunit\baselineskip10pt\eightrm + #1~\hypersecrefs#2, .\par}} + +% Same, but for references from the list of module names. +\def\finnote#1#2.{\quad{\eightrm#1~\hypersecrefs#2, .}} + +% the following two are auxiliary macros that implement reference lists. + +% \hypersecrefs<number>, <number>, ..., <number><ETcode><number>, . +% or \hypersecrefs<number>, . +% make list of hyperlinks to each of then mentioned sections +\def\hypersecrefs#1, #2.% +{\ifx|#2|\def\next{\finalsecrefs#1\ET\ET.#1\ETs\ETs}% final `.' follows below + \else\hyperref{\let\*=\empty}{sec}{#1}{#1}, \let\next=\hypersecrefs + \fi\next#2.% +} + +% \finalsecref<n><ETcode><m>\ET\ET.<n><ETcode><m>\ETs\ETs. +% or \finalsecref<n>\ET\ET.<n>\ETs\ETs. +\def\finalsecrefs#1\ET#2\ET#3.#4\ETs#5\ETs#6.% +{\ifx|#2#5|\hyperref{\let\*=\empty}{sec}{#1}{#1}% just 1 reference + \else % <n>\ET<m> or <n>\ETs<m> + \edef\next{\ifx|#5|#1\else#4\fi}% <n> + \toks0={\let\*=\empty}% + \edef\next{{\the\toks0}{sec}{\next}{\next}}% + \expandafter\hyperref\next% link to sec.<n> with text <n> + #3#6% one is empty, the other is \ET or \ETs, as appropriate + \hyperref{\let\*=\empty}{sec}{#2#5}{#2#5}% similar trick, link to sec.<m> + \fi +} + +% we must change \ATP slightly, so that \X can recognise it and avoid a link +\def\ATP{\X\ATP\kern-.5em:Preprocessor directives\X} + +\def\X#1:#2\X % module name +{\langle\,${#2\eightrm\enspace\ifx\ATP#1\else\hypersecrefs#1, .\fi}$\,\rangle} + +{\catcode`_=12 \gdef\usplain{\def\_{_}}} + +\let\plainCident=\Cident + +\def\Cident#1{\def\idstart{#1}\futurelet\next\testid} +\let\\=\Cident % cwebx only; a \hbox would prevent us from seeing "subscript" + +\def\testid +{\ifx_\next + \def\next_##1% + {\hyperref\usplain{id}{\idstart##1}{\plainCident\idstart_{##1}}}% + \else \hyperref\usplain{id}\idstart{\plainCident\idstart}% + \let\next=\empty + \fi\next +} + +\def\indextestid +{\ifx_\next + \def\next_##1% + {{\usplain\xdef\next{{id}{\idstart##1}}}% + \expandafter\hyperdef\next{\plainCident\idstart_{##1}}% + }% + \else + {\usplain\xdef\next{{id}{\idstart}}}% + \expandafter\hyperdef\next{\plainCident\idstart}% + \let\next=\empty + \fi\next +} + +{\let \inxentry=\relax % remove \outer-ness + \toks0=\expandafter{\inx \let\testid=\indextestid}\xdef\inx{\the\toks0 } +} + +\outer\def\inxentry#1#2, #3.% #1 is `h' or `m' for horiz/math mode + {\par\hangindent2em\noindent + \if#1m$#2$\else#2\relax\fi % \relax avoids fatal \next=\fi in \indextestid + :\kern1em\indexrefs#3, .% + } + +\def\indexrefs#1, #2.% +{\hyperref{\let\*=\empty\def\[##1]{##1}}{sec}{#1}{#1}% + \ifx|#2|\let\next=\empty\else, \let\next=\indexrefs\fi\next#2.% +} + +\def\contentsline#1#2#3#4% #1=depth, #2=title, #3=secno, #4=pageno + {\line{\hyperref{}{sec}{#3}{\ignorespaces#2} + \leaders\hbox to .5em{.\hfil}\hfil + \ \hyperref{}{sec}{#3}{#3 \hbox to3em{\hss#4}}% + }% + } diff --git a/web/c_cpp/cwebx/cwebxmac.tex b/web/c_cpp/cwebx/cwebxmac.tex new file mode 100644 index 0000000000..47157c8aba --- /dev/null +++ b/web/c_cpp/cwebx/cwebxmac.tex @@ -0,0 +1,442 @@ +% Standard macros for CWEBx listings (in addition to plain.tex) +% File: cwebxmac.tex, Author: Marc van Leeuwen, Date: November 1994 + +\ifx\documentstyle\undefined\else\endinput\fi % LaTeX will use other macros +\ifx \cwebxmacloaded\undefined \let\cwebxmacloaded=\relax \else \endinput \fi +\xdef\fmtversion{\fmtversion+CWEBx3.0} +\let\:=\. % preserve a way to get the dot accent + +\font\ninerm=cmr9 +\font\eightrm=cmr8 +\font\titlefont=cmr7 scaled\magstep4 % title on the contents page +\font\ttitlefont=cmtt10 scaled\magstep2 % typewriter type in title +\font\tentex=cmtex10 % TeX extended character set (used in strings) +\fontdimen7\tentex=0pt % no double space after sentences +\hyphenchar\tentex=-1 % no automatic hyphenation within strings + +\newdimen\indentunit \indentunit 1em +\parskip 0pt % no stretch between paragraphs +\parindent\indentunit % for paragraphs and for the first line of C text + +\chardef\v=`| % vertical (|) +\def\caps#1{\hbox{\ninerm #1}} +\def\Cee{\caps C} \def\Cpp{\Cee\PP} % \def\UNIX{\caps{UNIX}} etc. +\let\Sec=\S % section mark +\def\Secs{\Sec\Sec} % as in \Sec@#label@> + +\let\mainfont=\tenrm + +\def\Cident#1{{\it#1\/\kern.05em}} % italic type for identifiers +\def\\#1{\leavevmode\hbox\Cident{#1}} % robust version +\def\Cbold#1{{\bf + \def\_{\kern.04em\vbox{\hrule width.3em height .6pt}\kern.08em}% + #1\/\kern.05em}} % boldface type for reserved words +\def\{\leavevmode\hbox\Cbold{#1}} % robust version +\def\Cstring#1{\ifmmode {}$\typewriter{#1}${}\else\typewriter{#1}\fi} +\def\typewriter#1{{\tentex % typewriter type for strings + \let\\=\BS % backslash in a string + \let\{=\LB % left brace in a string + \let\}=\RB % right brace in a string + \let\~=\TL % tilde in a string + \let\ =\SP % space in a string + \let\_=\UL % underline in a string + \let\&=\AM % ampersand in a string + \let\^=\CF % circumflex in a string + #1\kern.05em}} +\def\.#1{\leavevmode\hbox\typewriter{#1}} +\def\){\discretionary{"}{"}{}} % discretionary string break +\def\AT{@} % at sign for control text (no longer needed since CWEBx2+1.0) +\def\a#1{\mathopen{\hbox to \indentunit{$#1$\hss}}} % '{' in tab space +\def\m#1{\mathord{#1}} % braces required here if #1 generates a penalty + +\chardef\AM=`\& % ampersand character in a string +\chardef\BS=`\\ % backslash in a string +\chardef\LB=`\{ % left brace in a string +\chardef\RB=`\} % right brace in a string +\def\SP{{\tt\char`\ }} % (visible) space in a string +\chardef\TL=`\~ % tilde in a string +\chardef\UL=`\_ % underline character in a string +\chardef\CF=`\^ % circumflex character in a string + +\newbox\PPbox % symbol for ++ +\setbox\PPbox=\hbox{\kern.5pt\raise1pt\hbox{\sevenrm+\kern-1pt+}\kern.5pt} +\def\PP{\copy\PPbox} +\newbox\MMbox +\setbox\MMbox=\hbox{\kern.5pt\raise1pt\hbox{\sevensy\char0 + \kern-1pt\char0}\kern.5pt} +\def\MM{\copy\MMbox} +\newbox\SSbox % symbol for ## +\setbox\SSbox=\hbox{\kern.5pt\raise1pt\hbox{\sevenrm\#\kern-1pt\#}\kern.5pt} +\def\SS{\mathbin{\copy\SSbox}} +\def\MG{{\rightarrow}} % symbol for `->' +\let\GG=\gg +\let\LL=\ll +\let\NULL=\odot +\mathchardef\AND="2026 % bitwise and; also \& (unary operator) +\def\OR{\Penalty6\mid} % bitwise or +\let\XOR=\oplus % bitwise exclusive or +\def\CM{{\sim}} % bitwise complement +\newbox\MODbox \setbox\MODbox=\hbox{\eightrm\%} +\def\MOD{\mathbin{\copy\MODbox}} +\def\CC{::} + +\newbox\bak \setbox\bak=\hbox to -\indentunit{} % backspace one em +\newbox\bakk\setbox\bakk=\hbox to -2\indentunit{} % backspace two ems + +\newcount\ind % current indentation level + 2 (for hanging indentation) +\newcount\pl %level of parentheses +\mathchardef\lpar=\mathcode`( \mathchardef\rpar=\mathcode`) +\mathchardef\lbrac=\mathcode`[ \mathchardef\rbrac=\mathcode`] +\mathchardef\plus=\mathcode`+ \mathchardef\minus=\mathcode`- +\mathchardef\mcolon=\mathcode`: +{\catcode`(=\active \catcode`)=\active + \catcode`[=\active \catcode`]=\active + \gdef({\global\advance\pl 1\lpar} + \gdef){\ifnum\pl>0\global\advance\pl-1\fi\rpar} + \gdef[{\global\advance\pl 2\lbrac} + \gdef]{\ifnum\pl>0\global\advance\pl-2\fi\rbrac} + \catcode`+=\active \catcode`-=\active \catcode`*=\active + \gdef+{\Penalty8\plus} \gdef-{\Penalty8\minus} \gdef*{\Penalty9\ast} + \catcode`:=\active \gdef:{\Penalty3\mcolon} +} +\def\cweblbrace{\global\advance\pl 1\lbrace} +\def\cwebrbrace{\ifnum\pl>0\global\advance\pl-1\fi\rbrace} + +% breaking within parentheses will be unattractive, +% and within brackets even more so + +\def\0#1{\penalty\number\pl#10 } % optional break in inner mode +\def\1{\global\advance\ind by1\global\hangindent\ind\indentunit} + % indent one more notch +\def\2{\global\advance\ind by-1} % indent one less notch +\def\3#1{\hfil\0#1\hfilneg} % optional break in outer mode +\def\4{\copy\bak\ignorespaces} % backspace one notch +\def\5{\hfil\penalty-1\hfilneg\enspace\kern2\indentunit\copy\bakk + \ignorespaces}% break space +\def\6{\ifmmode\else\par % forced break + \hangindent\ind\indentunit\noindent\kern\ind\indentunit + \copy\bakk\ignorespaces\fi} +\def\7{\Y\6} % forced break and a little extra space +\def\8{\hskip-\ind\indentunit\hskip 2\indentunit} % cancel full indentation + +\let\yskip=\smallskip % amount of space between subsections, and at `@)' +\newskip\intersecskip \intersecskip=12pt minus 3pt % space between sections +\newif\ifcodemode \codemodefalse % whether we are in \Cee part of section +\newif\ifon % whether a section will produce any printed output +\let\maybe=\iftrue % \if-like macro; governs printing of unchanged sections +\def\changesonly{\let\maybe=\iffalse} % the user might set this in limbo +\def\onmaybe{\let\ifon=\maybe} % this is executed for each unchanged section + +\def\Penalty#1{\relax\ifcodemode\3#1\fi} % optional break if in code mode + +% Summary of use of \Penalty +%\def*{\Penalty9\ast} +%\def+{\Penalty8\plus} \gdef-{\Penalty8\minus} +%\def\I{\Penalty7\neq} % inequality test +%\def\S{\Penalty7=} % equality test +%\def\OR{\Penalty6\mid} % bitwise OR +%\def\W{\Penalty5\land} % logical AND +%\def\V{\Penalty4\lor} % logical OR +%\def\?{\Penalty3\mathrel?} % `?' operator +%\def:{\Penalty3\mcolon} % colon in math mode matches `?' operator +%\def\K{\Leftarrow\Penalty2} % assignment operator, +% comma operator gets \Penalty1 by CWEAVE + +\def\note#1#2.% for cross-referencing notes, as at the end of a section +{\Y\noindent{\hangindent2\indentunit\baselineskip10pt\eightrm#1~#2.\par}} +\def\defin#1{\global\advance\ind by 2 + \1\&{#1}\quad} % begin `#define' or `format' of `#include' +\def\lapstar{\rlap{*}} +\def\stsec % start up setion + {\endcodemode\noindent{\let\*=\lapstar\bf\secstar.\quad}} +\let\startsection=\stsec % this one is used; provides hook for extra actions +\newcount\gdepth % depth of last starred section +\newcount\secpagedepth \secpagedepth=2 % depth where no page break is forced +\newtoks\gtitle % title of last starred section + +\def\A{\note{See also section}} % xref for doubly defined module name +\def\As{\note{See also sections}} % xref for multiply defined module name +\def\ATL{\par\noindent\bgroup\catcode`\_=12 \postATL} % print @l in limbo +\def\postATL#1 #2 {\bf letter \\{\uppercase{\char"#1}} + tangles as \tentex "#2"\egroup\par} +\def\noATL#1 #2 {} +\def\noatl{\let\ATL=\noATL} % suppress output from @l +\def\ATP{\X\kern-.5em:Preprocessor directives\X} +\def\B{\leavevmode % go into C mode + \ifcodemode\else + \begingroup\codemodetrue + \rightskip=0pt plus 300pt + \pretolerance 10000 + \hyphenpenalty 9999 % so strings can be broken (with string break inserted) + \exhyphenpenalty 10000 + \binoppenalty 10000 + \relpenalty 10000 + \mathcode`(="8000 \mathcode`)="8000 \mathcode`[="8000 \mathcode`]="8000 + \mathcode`+="8000 \mathcode`-="8000 \mathcode`*="8000 \mathcode`:="8000 + \let\{=\cweblbrace \let\}=\cwebrbrace + \let\.=\Cstring \let\\=\Cident \let\&=\Cbold + \fi + \global\ind=2 + \global\pl=0 + \1} +\def\endcodemode + {\ifcodemode + \endgroup % get out of C mode + \global\hangindent=0pt % counteract \global\hangindent in \1 + \fi + } +\def\C#1% ordinary C comment; try to allow a break if little room is left + {\hfil\penalty0\hfilneg\kern4\indentunit\copy\bakk$/\ast\,$#1$\,\ast/$} +\def\SHC#1% C++ one-line comment; force onto the current line + {\nobreak\kern2\indentunit\hbox{$/\!/\,$#1\unskip}}% +\def\D{\8\defin{\#define}} % macro definition +\def\E{\Penalty7=} % equality test +\let\EQ=\equiv % after defining module name +\def\PE{\mathrel\plus\EQ} % further defining section of a module +\def\ET{ and~} % conjunction between two section numbers +\def\ETs{, and~} % conjunction between the last two of several section numbers +\def\F{\defin{format}} % format definition +\let\G=\ge % greater than or equal sign +% \H is long Hungarian umlaut accent +\def\h{\8\defin{\#include}} % header file inclusion +\def\I{\Penalty7\neq} % inequality test +\def\J{\.{@\&}} % TANGLE's join operation +\def\K{\Leftarrow\Penalty2} % assignment operator, + % can be changed to `=' (if \E is redefined) or `\leftarrow', if desired +\def\KK#1{\mathrel{#1}\K} % composite assignment operators + % braces are required for same reason as for \m +\def\MRL#1{\mathrel{#1}} % for first such operator in compatibility mode +% \L is Polish letter suppressed-L +\outer\def\M#1. % start of `@ ' section + {\MN#1.\ifon + \vskip 0pt plus .5 \vsize \penalty-1000\vskip 0pt plus -.5 \vsize + \vskip\intersecskip\startsection + } +\outer\def\n#1. % start of `@~' section + {\MN#1.\ifon\vskip\intersecskip\startsection + } +\outer\def\N#1 #2. #3. %start of `@*' section + {\global\gdepth=#1\global\gtitle={#3}\MN#2.% + \ifon \ifnum\gdepth<\secpagedepth \vfil\eject + \else \vskip 0pt plus .5 \vsize \penalty-1000\vskip 0pt plus -.5 \vsize + \vskip\intersecskip + \fi + \fi + \message{*\secno}% progress report + \edef\next{\write\cont % to contents file + {\ZZ{#1}{#3}{\secno}{\noexpand\the\pageno}}} + \next + \ifon\startsection{\bf#3.\quad}% + } +\def\MN#1.% common code for \M, \N, \n + {\par{\xdef\secstar{#1}\let\*=\empty\xdef\secno{#1}}% + \ifx\secno\secstar \onmaybe % print unchanged section if \maybe=\iftrue + \else\ontrue % changed sections are always printed + \fi \mark{{{\tensy x}\secno}{\the\gdepth}{\the\gtitle}}% + }% each \mark is {section reference}{depth}{title} +% \O is Scandinavian letter O-with-slash +% \P is paragraph sign +\def\Q{\note{This code is cited in section}} % xref for citation of a module +\def\Qs{\note{This code is cited in sections}} % xref for citations of a module +\let\R=\lnot % logical not +% \S is section sign +\def\T#1{% + {\let\~=\oct \let\^=\hex \let\_=\timestentothepower \let\$=\withsuffix #1}} +\def\oct{{}^\circ\kern-.2em\it\aftergroup\afteroct} +\def\afteroct{\kern.2em } +\def\hex{{}^{\scriptscriptstyle\#}\tt} +\def\timestentothepower{\cdot 10^{\aftergroup}} +\def\withsuffix{_{\rm\,\aftergroup}} +\def\U{\note{This code is used in section}} % xref for use of a module +\def\Us{\note{This code is used in sections}} % xref for uses of a module +\def\V{\Penalty4\lor} % logical or +\def\W{\Penalty5\land} % logical and +\def\X#1:#2\X{\langle\,${#2\eightrm\enspace#1}$\,\rangle} % module name +\def\Y{\par\yskip} +\let\Z=\le % less than or equal sign +\let\ZZ=\relax % now you can \send the control sequence \ZZ +\def\?{\Penalty3\mathrel?} % `?' operator +\def\vb#1{\leavevmode\hbox{\kern2pt\vrule\vtop{\vbox{\hrule + \hbox{\strut\kern2pt\.{#1}\kern2pt}} + \hrule}\vrule\kern2pt}} % verbatim string +\let\*=* + + +% output routines + +\newif\iftitle % if true suppresses first running head +\def\lheader{\mainfont\the\pageno\eightrm\qquad\grouptitle\hfill\title\qquad + \mainfont\topsecno} % top line on left-hand pages +\def\rheader{\mainfont\topsecno\eightrm\qquad\title\hfill\grouptitle + \qquad\mainfont\the\pageno} % top line on right-hand pages +\def\grouptitle{\let\i=I\let\j=J\uppercase\expandafter{\expandafter + \takethree\topmark}} +\def\topsecno{\expandafter\takeone\topmark} +\def\takeone#1#2#3{#1} +\def\taketwo#1#2#3{#2} +\def\takethree#1#2#3{#3} +\def\nullsec{{\eightrm\kern-2em }} % the \kern-2em cancels \qquad in headers + +\let\page=\pagebody \normalbottom +% \def\page{\box255 }% faster, but loses plain TeX footnotes +\def\normaloutput#1#2#3% #1=page, #2=running head if even, #3 idem if odd +{\ifodd\pageno\hoffset=\pageshift\fi + \shipout\vbox + {\vbox to\fullpageheight + {\iftitle\global\titlefalse % no running head, but reset for next pages + \else\hbox to\pagewidth{\vbox to10pt{}\ifodd\pageno #3\else#2\fi}% + \fi + \vfill#1% parameter #1 is the page itself + }} + \global\advance\pageno by1 +} + +\def\title{\uppercase\expandafter{\jobname}} % default title +\def\topofcontents{\centerline{\titlefont\title}\vskip.7in + \vfill} % this material will start the table of contents page +\def\botofcontents{\vfill\vfill + \centerline{\covernote}} % this material will end the table of contents page +\def\covernote{} +\def\contentspagenumber{0} % default page number for table of contents + +\newdimen\pagewidth \pagewidth=6.5in % the width of each page +\newdimen\pageheight \pageheight=8.7in % the height of each page +\newdimen\fullpageheight \fullpageheight=9in % page height including headlines +\newdimen\pageshift \pageshift=0pt % shift righthand pages wrt lefthand ones +\def\magnify#1{\mag=#1\pagewidth=6.5truein\pageheight=8.7truein + \fullpageheight=9truein\setpage} +\def\setpage{\hsize=\pagewidth \vsize=\pageheight} % use after changing page size +\def\contentsfile{\jobname.toc } % file that gets table of contents info +\def\readcontents{\input\contentsfile} + +\newwrite\cont +\output= % temporary for first page, which is emtpy so as to define \topmark +{\setbox0=\box255 % throw away empty page + \openout\cont=\contentsfile % gets written when first real page is shipped + \write\cont{\catcode `\noexpand\@=11 } % first line makes `@' letter + \global\output={\normaloutput\page\lheader\rheader}% the real \output +} +\setpage +\vbox to 2\vsize{} % dummy page, but the first \topmark won't be null +\gtitle={\.{CWEB} output} % this running head is reset by starred sections +\mark{\noexpand\nullsec0{\the\gtitle}} +% page must be oversized so even a very early \inx won't get the chance +% to reassign \output before this page is sent off. + +\newbox\sbox % one-page buffer for delayed output of page before index +\newif\ifpagesaved % whether buffer has been filled +\def\bufferedoutput +{{\ifpagesaved\normaloutput{\box\sbox}\lheader\rheader\fi % empty buffer + \global\setbox\sbox=\page \global\pagesavedtrue % fill buffer +}} + +\def\ch#1.% +{{\let\*=\relax + \note{The following sections were changed by the change file:}{#1}.% +}} + +\newbox\lbox % lefthand column in the index +\def\inx + {\par\vskip6pt plus 1fil \endcodemode % we are beginning the index + \def\page{\box255 } \normalbottom + \write\cont{\catcode `\noexpand\@=12\relax} % make `@' other char + \closeout\cont % the contents information has been fully gathered + \output=\bufferedoutput\pagesavedfalse\eject % eject pages, keeping last + \setbox\sbox=\vbox{\unvbox\sbox} % allow its glue to reset + \vsize=\pageheight \advance\vsize by -\ht\sbox % the remaining height + \hsize=.5\pagewidth \advance\hsize by -10pt + % column width for the index (20pt between cols) + \parfillskip 0pt plus .6\hsize % try to avoid almost empty lines + \def\lr{L} % this tells whether the left or right column is next + \output= + {\if L\lr\global\setbox\lbox=\page \gdef\lr{R} + \else + \normaloutput + {\vbox to\pageheight + {\box\sbox \vss \hbox to\pagewidth{\box\lbox \hfil \page}}% + }% page argument to \normaloutput + \lheader\rheader % other two arguments to \normaloutput + \global\vsize\pageheight \gdef\lr{L} \global\pagesavedfalse + \fi + }% + \message{Index:} + \parskip 0pt plus .5pt + \let\@=\inxentry + \def\[##1]{$\underline{##1}$} % underlined index item + \rm \rightskip0pt plus 2.5em \tolerance 10000 \let\*=\lapstar + \hyphenpenalty 10000 \parindent0pt + } + +\outer\def\inxentry#1#2, % index entry; #1 is `h' or `m' for horiz/math mode + {\par\hangindent2em\noindent\if#1m$#2$\else#2\fi:\kern1em} + +\def\fin + {\par\vfill\eject % this is done when we are ending the index + \ifpagesaved\null\vfill\eject\fi % needed in case index is empty + \if R\lr \null\vfill\eject\fi % finish the current page + \parfillskip 0pt plus 1fil % restore normal paragraph end + \setpage % restore page shape + \output={\normaloutput\page\lheader\rheader}% restore output routine + \message{Module names:} + \def\grouptitle{NAMES OF THE MODULES} + \let\topsecno=\nullsec + \let\note=\finnote + \def\Q{\note{Cited in section}}% crossref for mention of a section + \def\Qs{\note{Cited in sections}}% crossref for mentions of a section + \def\U{\note{Used in section}}% crossref for use of a module + \def\Us{\note{Used in sections}}% crossref for uses of a module + \def\@{\par\hangindent 2em}\let\*=* + } + +\def\finnote#1#2.{\quad{\eightrm#1~#2.}} + +\def\con + {\par\vfill\eject % finish the module names + \rightskip 0pt \hyphenpenalty 50 \tolerance 200 + \setpage + \output={\normaloutput\page\lheader\rheader} + \titletrue % prepare to output the table of contents + \pageno=\contentspagenumber + \def\grouptitle{TABLE OF CONTENTS} + \message{Table of contents:} + \topofcontents + \line{\hfil Section\hbox to3em{\hss Page}} + \let\ZZ=\contentsline + \readcontents % read the contents info + \botofcontents \eject\end % print the contents page(s) and terminate + } +\def\contentsline#1#2#3#4% #1=depth, #2=title, #3=secno, #4=pageno + {\line{\ignorespaces#2 \leaders\hbox to .5em{.\hfil}\hfil + \ #3\hbox to3em{\hss#4}% + }% + } +\def\coneven % force even number of pages before contents +{\par\vfill\eject\ifodd\pageno\else\shipout\hbox{}\advancepageno\fi\con} +\def\noinx{\def\inx{\endcodemode\end}} % no indexes or table of contents +\def\nomods{\let\FIN=\fin \def\fin{\let\parfillskip=\end \FIN}} + % no index of module names or table of contents +\def\nocon{\let\con=\end} % no table of contents +\def\today{\ifcase\month\or + January\or February\or March\or April\or May\or June\or + July\or August\or September\or October\or November\or December\fi + \space\number\day, \number\year} +\newcount\twodigits +\def\hours{\twodigits=\time \divide\twodigits by 60 \printtwodigits + \multiply\twodigits by-60 \advance\twodigits by\time \printtwodigits} +\def\gobbleone1{} +\def\printtwodigits{\advance\twodigits100 + \expandafter\gobbleone\number\twodigits + \advance\twodigits-100 } +\def\now{{\eightrm\today\ at \hours}} +\def\datethis % say `\datethis' in limbo, to get your listing timestamped + {\def\startsection{\leftline\now\bigskip\let\startsection=\stsec\stsec}} +\def\datecontentspage % timestamps the contents page + {\def\topofcontents + {\leftline\now\bigskip\centerline{\titlefont\title}\vfill}% + } + +\def\indentation#1{\indentunit#1\relax + \parindent\indentunit + \setbox\bak\hbox to-\indentunit{}% backspace one unit + \setbox\bakk\hbox to-2\indentunit{}% backspace two units +} diff --git a/web/c_cpp/cwebx/examples/Makefile b/web/c_cpp/cwebx/examples/Makefile new file mode 100644 index 0000000000..7fcdba0d84 --- /dev/null +++ b/web/c_cpp/cwebx/examples/Makefile @@ -0,0 +1,45 @@ +CFLAGS = +CWEAVE = cweave +d +CTANGLE = ctangle +.SUFFIXES: .tex .dvi .w + +.w.tex: + $(CWEAVE) $* + +.tex.dvi: + tex $* + + +.w.dvi: + $(CWEAVE) $* + tex $* + +.w.c: + $(CTANGLE) $* + +.w.o: + $(CTANGLE) $* + $(CC) $(CFLAGS) -c $*.c + +.w.dvi: + make $*.tex + make $*.dvi + +.w.o: + make $*.c + make $*.o + +treeprint: treeprint.w + make treeprint.c + make treeprint + +wc: wc.w + make wc.c + make wc + +wmerge: wmerge.w + make wmerge.o + $(CC) $(CFLAGS) -o wmerge wmerge.o ../common.o + + + diff --git a/web/c_cpp/cwebx/examples/compare.w b/web/c_cpp/cwebx/examples/compare.w new file mode 100644 index 0000000000..7a52d9b000 --- /dev/null +++ b/web/c_cpp/cwebx/examples/compare.w @@ -0,0 +1,108 @@ +@* Comparing text files. +This is an entirely trivial program, that tests whether two text files are +equal, and if not so, points out the first point of difference. + +@h <stdio.h> +@h <stdlib.h> +@c typedef char bool; + +@ The outline of the program is simple. We read characters from both input +files into |c1| and~|c2| until the comparison is complete. Line and column +counts are maintained in |line| and~|col|. + +@d left_margin 1 /* leftmost column number; change to 0 if you prefer */ +@c +@<Functions@>@; +int main(int n, char** arg) +{ FILE *f1,*f2; /* the two input files */ + int c1,c2,col=left_margin; long line=1; + @< Open the files |f1| and~|f2|, + taking their names from the command line or from the terminal; + in case of an error for which no recovery is possible, + call |exit(1)| @> + @< Search for first difference, + leaving |c1!=c2| if and only if a difference was found @> + @< Report the outcome of the comparison @> + return 0; /* successful completion */ +} + +@ The heart of the program is this simple loop. When we reach the end of +one of the files, the files match if and only if the other file has also +reached its end. For this reason the test |c1==c2|, which requires +characters to be read from both files, must precede the test for file end; +when only one file ends, it is the former test which breaks the loop. + +@< Search for first difference... @>= +while ((c1=getc(f1))==(c2=getc(f2)) && c1!=EOF) + if (c1=='\n') {@; ++line; col=left_margin; } @+ else ++col; + +@ When the first difference occurs at the end of one of the files, or at the +end of a line, we give a message indicating this fact. + +@< Report... @>= +if (c1==c2) printf("Files match.\n"); +else +{ printf("Files differ.\n"); + if (c1==EOF || c2==EOF) + @/{@; + the_file(c1==EOF); + printf("is contained in the other as initial segment.\n"); + } + else if (c1=='\n' || c2=='\n') + @/{@; + the_file(c1=='\n'); + printf("has a shorter line number %ld than the other.\n",line); + } + else printf("First difference at line %ld, column %d.\n",line,col); +} + +@ The function |the_file| starts a sentence about the first or second file, +depending on its boolean argument. + +@<Functions@>= +void the_file(bool is_first) +@+{@; printf("The %s file ", is_first ? "first" : "second" ); } + +@ There can be be zero, one or two command line arguments. If there are none, +the user is prompted to supply them, and if there are two these are taken as +the file names, prompting the user only in case a file could not be opened. +In case just one argument is present, the first file is assumed to be the +standard input, which does not have to be opened; in this case however we +will not read a file name from terminal in case the second file cannot be +opened. + +@d read_mode "r" +@< Open... @>= +--n; ++arg; /* ignore ``argument'' 0, which is the program name */ +if (n==0) +@/{@; + open_file(&f1,"First file to compare", NULL); + open_file(&f2,"Second file to compare", NULL); +} +else if (n==1) +{ f1=stdin; + if ((f2=fopen(*arg,read_mode))==NULL) + {@; printf("Could not open file %s.\n",*arg); exit(1); } +} +else if (n==2) +{ open_file(&f1,"Give another first file", *arg++); + open_file(&f2,"Give another second file", *arg); +} +else +{@; printf("No more than two command line arguments are allowed.\n"); + exit(1); +} + +@ The function |open_file| will try to open the file |name| for reading, and +if this fails it will prompt for another file name until it has success. If +called with |name==NULL|, the function starts with prompting right away. + +@<Functions@>= +void open_file(FILE** f,char* prompt,char* name) +{ char buf[80]; + if (name==NULL || (*f=fopen(name,read_mode))==NULL) + do {@; printf("%s: ",prompt); fflush(stdout); scanf("%79s",buf); }@/ + while ((*f=fopen(buf,read_mode))==NULL); +} + +@* Index. diff --git a/web/c_cpp/cwebx/examples/treeprint.w b/web/c_cpp/cwebx/examples/treeprint.w new file mode 100644 index 0000000000..ae9dc239d1 --- /dev/null +++ b/web/c_cpp/cwebx/examples/treeprint.w @@ -0,0 +1,229 @@ +% Copyright 1987 Norman Ramsey -- Rutgers University +% Adapted to CWEB version 3.0 by Marc van Leeuwen -- CWI Amsterdam + +@*Directory Trees. +The object is to print out a directory hierarchy in some pleasant way. +The program takes output from \.{find * -type d -print {\v} sort} +@^system dependencies@> +and produces a nicer-looking listing. +For those of you who may not have \.{find} or \.{sort}, the output is a +list of fully +qualified directory names (parent and child separated by slashes |'/'|), +and everything is already nicely sorted in lexicographic order. + +|treeprint| takes one option, |"-p"|, which tells it to use the printer's +line-drawing set, rather than the terminal's. +@h <stdio.h> + +@c +@< Global declarations @>@; + +main(int argc, char** argv) +{ +@< Variable declaration for |main| @> @; +@< Search for options and set special characters on |"-p"| @> +@< Read output from find and enter into tree @> +@< Write tree on standard output @> +exit(0); +} + +@ +We make all the siblings of a directory a linked list off of its left child, +and the offspring a linked list off the right side. +Data are just directory names. +@d sibling left +@d child right + +@< Global decl... @>= +typedef struct tnode { + struct tnode *left, *right; + char *data; +} TNODE; +@ @< Variable declaration for |main| @>= +struct tnode *root; + + + +@*Input. +Reading the tree is simple---we read one line at a time, and call on the +recursive |add_tree| procedure. +@c +read_tree (FILE* fp,struct tnode** rootptr) +{ + char buf[255], *p; + + while ((fgets(buf, 255, fp))!=NULL) { + @< If |buf| contains a newline, make it end there @> + add_tree(rootptr, buf); + } + } + +@ Depending what system you're on, you may or may not get a newline in |buf|. +@< If |buf| contains a newline... @>= + p=buf; while (*p!='\0'&&*p!='\n') p++; +@^system dependencies@> + *p='\0'; + +@ +To add a string, we split off the first part of the name and insert it into +the sibling list. We then do the rest of the string as a child of the new node. +@c +add_tree(struct tnode** rootptr, char* p) +{ + char *s; + int slashed; + + if (*p=='\0') return; + +@< Break up the string so |p| is the first word, + |s| points at null-begun remainder, + and |slashed| tells whether |*s=='/'| on entry @> + + if (*rootptr==NULL) { +@< Allocate new node to hold string of size |strlen(p)| @> + strcpy((*rootptr)->data,p); + } + if (strcmp((*rootptr)->data,p)==0) { + if (slashed) ++s; + add_tree(&((*rootptr)->child),s); + } + else { + if (slashed) *s='/'; + add_tree(&((*rootptr)->sibling),p); + } + } + +@ We perform some nonsense to cut off the string |p| so that |p| just holds +the first word of a multiword name. |s| points at what was either the end +of |p| or a slash delimiting names. In either case |*s| is made |'\0'|. +Later depending on wether we want to pass the whole string or the last piece, +we will restore the slash or advance |s| one character to the right. + +@< Break up... @>= + for (s=p;*s!='\0'&&*s!='/';) s++; + if (*s=='/') slashed=1, *s='\0'; + else slashed=0; + +@ Node allocation is perfectly standard\dots +@< Allocate new node... @>= + *rootptr=(struct tnode *) malloc (sizeof(struct tnode)); + (*rootptr)->left = (*rootptr)->right = NULL; + (*rootptr)->data = malloc (strlen(p)+1); + +@ +@< Global decl... @>= char *malloc(); + +@ In this simple implementation, we just read from standard input. +@< Read... @>= read_tree(stdin,&root); + +@*Output. +We begin by defining some lines, tees, and corners. +The |s| stands for screen and the |p| for printer. +You will have to change this for your line-drawing set. +@^system dependencies@> + +@d svert '|' +@d shoriz '-' +@d scross '+' +@d scorner '\\' /* lower left corner */ + +@d pvert '|' +@d phoriz '-' +@d pcross '+' +@d pcorner '\\' /* lower left corner */ + +@ The default is to use the terminal's line drawing set. +@< Global declarations @>= +char vert=svert; +char horiz=shoriz; +char cross=scross; +char corner=scorner; + +@ With option |"-p"| use the printer character set. +@< Search for options... @>= +while (--argc>0) { + if (**++argv=='-') { + switch (*++(*argv)) { + case 'p': + vert=pvert; + horiz=phoriz; + cross=pcross; + corner=pcorner; + break; + default: + fprintf(stderr,"treeprint: bad option -%c\n",**argv); + break; + } + } +} + +@ We play games with a character stack to figure out when to put in vertical +bars. +A vertical bar connects every sibling with its successor, but the last sibling +in a list is followed by blanks, not by vertical bars. The state of +bar-ness or space-ness for each preceding sibling is recorded in the +|indent_string| variable, one character (bar or blank) per sibling. + +@< Global decl... @>= +char indent_string[100]=""; + +@ Children get printed +before siblings. +We don't bother trying to bring children up to the same line as their parents, +because the \caps{UNIX} filenames are so long. + +We define a predicate telling us when a sibling is the last in a series. +@d is_last(S) (S->sibling==NULL) + +@c +print_node(FILE* fp, char* indent_string, struct tnode* node) +{ + char string[255]; + int i; + char *p, *is; + + if (node==NULL) { + } + else { + *string='\0'; + for (i=strlen(indent_string); i>0; i--) + strcat(string,@, " | "); + strcat(string,@t\ \ @> " +--"); +@< Replace chars in |string| with chars from + line-drawing set and from |indent_string| @> + fprintf(fp,"%s%s\n",string,node->data); + +@) + /* Add vertical bar or space for this sibling (claim |*is=='\0'|) */ + *is++ = (is_last(node) ? ' ' : vert); + *is=='\0'; + + print_node(fp, indent_string, node->child); /* extended |indent_string| */ + *--is='\0'; + print_node(fp, indent_string, node->sibling); /* original |indent_string| */ + } + +} +@ For simplicity, we originally wrote connecting lines with |'|'|, |'+'|, and +|'-'|. +Now we replace those characters with appropriate characters from the +line-drawing set. +We take the early vertical bars and replace them with characters from +|indent_string|, and we replace the other characters appropriately. +We are sure to put a |corner|, not a |cross|, on the last sibling in +a group. +@< Replace chars... @>= + is=indent_string; + for (p=string; *p!='\0'; p++) switch(*p) { + case '|': *p=*is++; break; + case '+': *p=(is_last(node) ? corner : cross); break; + case '-': *p=horiz; break; + default: break; + } + + +@ For this simple implementation, we just write on standard output. + +@< Write... @>= print_node(stdout, indent_string, root); + +@*Index. diff --git a/web/c_cpp/cwebx/examples/wc.w b/web/c_cpp/cwebx/examples/wc.w new file mode 100644 index 0000000000..ce741c5f3c --- /dev/null +++ b/web/c_cpp/cwebx/examples/wc.w @@ -0,0 +1,226 @@ +% wc: An example of CWEB by Silvio Levy and Donald E. Knuth +% Adapted to CWEB version 3.0 by Marc van Leeuwen -- CWI Amsterdam + +\nocon % omit table of contents +\datethis % print date on listing + +@* An example of \.{CWEB}. This example presents the ``word count'' +program from \caps{UNIX}, rewritten in \.{CWEB} to demonstrate +literate programming in \Cee. The level of detail is intentionally +high, for didactic purposes; many of the things spelled out here don't +need to be explained in other programs. + +The purpose of \.{wc} is to count lines, words, and/or characters in a list +of files. The number of lines is the number of newline characters in the +file. The number of characters is the file length in bytes. A ``word'' is a +maximal sequence of consecutive characters other than white space, +containing at least one visible character. + +@ Most \.{CWEB} programs share a common structure. It's probably a good +idea to have one module that states this structure explicitly, even though +the elements could all be introduced in sections contributing to of the +unnamed module if they don't need to appear in any special order. + +@c + +@< Global variables @>@; +@< Functions @>@; +@< The main program @>@; + +@ We must include the standard I/O definitions, since we want to send +formatted output to |stdout| and |stderr|. We also use the character +classification macro |isgraph| to detect visible characters. + +@h <stdio.h> +@h <ctype.h> +@ The |status| variable tells the operating system if the run was +successful or not, and |prog_name| is used in case there's an error message to +be printed. + +@d OK 0 /* |status| code for successful run */ +@d usage_error 1 /* |status| code for improper syntax */ +@d cannot_open_file 2 /* |status| code for file access error */ + +@< Global variables @>= +int status=OK; /* exit status of command, initially |OK| */ +char *prog_name; /* who we are */ + +@ Now we come to the general layout of the |main| function. + +@< The main... @>= +main (int argc,char** argv) +{ + @< Variables local to |main| @>@; + prog_name=*argv++; --argc; /* process program name */ + @< Set up option selection @> + @< Process all the files @> + @< Print the grand totals if there were multiple files @> + exit(status); +} + +@ If the first argument begins with a `\.-' the user is choosing +the desired counts and specifying the order in which they should be +displayed. Each selection is given by the initial character +(lines, words, or characters). For example, `\.{-cl}' would cause +just the number of characters and the number of lines to be printed. + +We do not process this string now. It will be used to control the +formatting at output time. + +@< Var... @>= +int file_count; /* how many files there are */ +char *which; /* which counts to print */ + +@ @< Set up o... @>= +which="lwc"; /* if no option is given, print all three values */ +if (argc>0 && (*argv)[0] == '-') {@; which=&(*argv++)[1]; --argc; } +file_count=argc; + +@ Now we scan the remaining arguments and try to open a file, if +possible. The file is processed and its statistics are given. +We use a |do|~\dots~|while| loop because we should read from the +standard input if no file name is given. + +@< Process... @>= +do { + @< If a file is given try to open |*argv|; |continue| if unsuccesful @> + @< Initialize pointers and counters @> + @< Scan file @> + @< Write statistics for file @> + @< Close file @> + @< Update grand totals @> /* even if there is only one file */ +} while (++argv,--argc>0); + +@ Here's the code to open the file. We use the low-level functions |open|, +|read|, and |close| that operate work file descriptors rather than with +|FILE|s. A special trick allows us to handle input from |stdin| when no name +is given. Recall that the file descriptor to |stdin| is 0; that's what we +initialize our file descriptor to. + +@< Variabl... @>= +int fd=0; /* file descriptor, initialized to |stdin| */ + +@~@d READ_ONLY 0 /* read access code for system |open| routine */ + +@< If a fi... @>= +if (file_count>0 && (fd=open(*argv,READ_ONLY))<0) { + fprintf (stderr, "%s: cannot open file %s\n", prog_name, *argv); +@.cannot open file@> + status|=cannot_open_file; + --file_count; + continue; +} + +@ @< Close file @>= +close(fd); + +@ We will do some homemade buffering in order to speed things up: Characters +will be read into the |buffer| array before we process them. +To do this we set up appropriate pointers and counters. + +@d buf_size BUFSIZ /* \.{stdio.h}'s |BUFSIZ| is chosen for efficiency*/ + +@< Var... @>= +char buffer[buf_size]; /* we read the input into this array */ +register char *ptr; /* the first unprocessed character in |buffer| */ +register char *buf_end; /* the first unused position in |buffer| */ +register int c; /* current character, or number of characters just read */ +int in_word; /* are we within a word? */ +long word_count, line_count, char_count; /* number of words, lines, + and characters found in the file so far */ + +@ @< Init... @>= +ptr=buf_end=buffer; line_count=word_count=char_count=0; in_word=0; + +@ The grand totals must be initialized to zero at the beginning of the +program. If we made these variables local to |main|, we would have to +do this initialization explicitly; however, \Cee's globals are automatically +zeroed. (Or rather, ``statically zeroed.'') (Get it?) +@^Joke@> + +@< Global var... @>= +long tot_word_count, tot_line_count, tot_char_count; /* total number of words, lines and chars */ + +@ The present module, which does the counting that is \.{wc}'s {\it raison +d'\^etre}, was actually one of the simplest to write. We look at each +character and change state if it begins or ends a word. + +@< Scan... @>= +while (1) { + @< Fill |buffer| if it is empty; |break| at end of file @> + c=*ptr++; + if (isgraph(c)) /* visible character */ + {@; if (!in_word) ++word_count, in_word=1; } + else if (isspace(c)) + { in_word=0; /* |c| white space */ + if (c=='\n') ++line_count; + } +} + +@ Buffered I/O allows us to count the number of characters almost for free. + +@< Fill |buff... @>= +if (ptr>=buf_end) { + ptr=buffer; c=read(fd,ptr,buf_size); + if (c<=0) break; + char_count+=c; buf_end=buffer+c; +} + +@ It's convenient to output the statistics by defining a new function +|wc_print|; then the same function can be used for the totals. +Additionally we must decide here if we know the name of the file +we have processed or if it was just |stdin|. + +@< Write... @>= +wc_print(which, char_count, word_count, line_count); +if (file_count) printf (" %s\n", *argv); /* not |stdin| */ +else printf ("\n"); /* |stdin| */ + +@ @< Upda... @>= +tot_line_count+=line_count; +tot_word_count+=word_count; +tot_char_count+=char_count; + +@ We might as well improve a bit on \caps{UNIX}'s \.{wc} by counting the +files too. + +@< Print the... @>= +if (file_count>1) { + wc_print(which, tot_char_count, tot_word_count, tot_line_count); + printf(" total in %d files\n",file_count); +} + +@ Here now is the function that prints the values according to the +specified options. The calling routine is supposed to supply a +newline. If an invalid option character is found we inform +the user about proper usage of the command. Counts are printed in +10-digit fields so that they will line up in columns. + +@d print_count(n) printf("%10ld",n) + +@< Fun... @>= +wc_print(char* which, long char_count, long word_count, long line_count) +{ + while (*which) + switch (*which++) { + case 'l': print_count(line_count); break; + case 'w': print_count(word_count); break; + case 'c': print_count(char_count); break; + default: if ((status & usage_error)==0) { + fprintf (stderr, "\nUsage: %s [-lwc] [filename ...]\n", prog_name); +@.Usage: ...@> + status|=usage_error; + } + } +} + +@ Incidentally, a test of this program against the system \.{wc} command +on a SPARCstation showed that the ``official'' \.{wc} was slower. Furthermore, +although that \.{wc} gave an appropriate error message for the options +`\.{-abc}', it made no complaints about the options `\.{-labc}'! +Perhaps the system routine would have been better if its programmer had +been more literate? + +@* Index. +Here is a list of the identifiers used, and where they appear. Underlined +entries indicate the place of definition. Error messages are also shown. diff --git a/web/c_cpp/cwebx/examples/wmerge.w b/web/c_cpp/cwebx/examples/wmerge.w new file mode 100644 index 0000000000..4596e97d54 --- /dev/null +++ b/web/c_cpp/cwebx/examples/wmerge.w @@ -0,0 +1,52 @@ +% Adapted to CWEB version 3.0 by Marc van Leeuwen -- CWI Amsterdam +\noinx + +@* Introduction. This file contains the program |wmerge|, which takes two +or more files and merges them according to the conventions of \.{CWEB}. We +use the routines of \.{CWEB} itself. The function |common_init| takes care of +processing command line arguments. Since the result of the merge will be +produced on the standard output, we prevent distraction as much as possible +by clearing flags |'h'| and |'p'| (for |'b'| it is not required since no +banner is produced anyway. + +@h <stdio.h> +@h "../common.h" /* the header file for \.{CWEB}'s \.{common.w} */ +@c +@< Prototype @>@; +main (int argc,char** argv) +{ common_init(argc,argv); + flags['h']=flags['p']=0; + reset_input(); + while (get_line()) + put_line(); + wrap_up(); +} + +@ This file should be linked together with the object file produced from +|"common.w"|, which is also used in both |CTANGLE| and |CWEAVE|. +That file defines the functions |common_init|, |reset_input|, |get_line|, and +|wrap_up|. There are however a number of functions that are required by that +compilation unit although they are not actually used; we define them with +trivial function bodies. Since the linker doesn't check types anyway we +don't specify any here either. + +@c +void print_stats() @+ {} +void names_match () @+ {} +void init_module_name() @+ {} +void init_id_name () @+ {} + + +@ All that remains is to define |put_line| which is trivial. The external +variable |buffer| holds the characters read by |get_line|, up to |limit|, +and |loc| points to the next character to be read, i.e., after calling +|get_line| it points to |buffer[0]|. + +@< Prototype @>= void put_line(void); +@~@c + +void put_line(void) +{ + while (loc<limit) putchar(*loc++); + putchar('\n'); +} diff --git a/web/c_cpp/cwebx/intro.inc b/web/c_cpp/cwebx/intro.inc new file mode 100644 index 0000000000..49d0376e18 --- /dev/null +++ b/web/c_cpp/cwebx/intro.inc @@ -0,0 +1,88 @@ +\def\hang{\hangindent 3em\indent\ignorespaces} +\def\pb{$\.|\ldots\.|$} % C brackets (|...|) +\def\LKC.{Levy/Knuth \.{CWEB}} +\def\:#1{`\.{@@#1}'} + +\def\title{\me. (Version x3.04)} +\def\topofcontents +{\topglue 0pt plus .5 fill + \centerline{\titlefont The {\ttitlefont \me.} program} + \vskip 15pt + \centerline{(\.{CWEB} version x3.04)} + \vfill +} +\def\botofcontents +{\vfill\noindent + Copyright \copyright\ 1987,\thinspace1990 Silvio Levy and Donald E. Knuth + \par\noindent + Copyright 1994 Marc A. A. van Leeuwen + \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 distributed under the terms of a + permission notice identical to this one. +} + + +@* Introduction. @:title@> +This is the main source text for the program \.{\me.}, one of the two text +processing tools that constitute the \.{CWEB} system. This version of the +system, identified as \.{CWEB}x3.0, was written by Marc van Leeuwen. The +history of this system is somewhat complicated, and starts with the +\.{WEB} system created by D.~E. Knuth (for the programming language Pascal); +the program corresponding to the current program in that system was +called~\myroots. That program was converted into \.{\me.}, written in and +for the \Cee~language, by Silvio Levy, and further developed under joint +responsibility with Knuth. Based on \.{CWEB}~2.1 by Levy and Knuth, a +version adapted to \caps{ANSI~C} was made by Frank Jensen. That version has +served as the starting point for the current author; an intermediate version +between it and the current version was made public under the name +\.{CWEB}~3.x. Helpful comments and suggestions by Gareth McCaughan, Werner +Lemberg, and Andreas Scherer were greatly appreciated. + +The name \.{CWEB}~3.x was an unfortunate choice, since the Levy/Knuth +version of \.{CWEB} had also been independently developed further, and it is +currently being distributed as \.{CWEB}~3.4f. Both these branches have +retained the basic functionality of the version of \.{CWEB} they were both +derived from, making only minor (but different) additions and changes. +Realising that this divergence is undesirable and confusing, the development +of this branch from \.{CWEB}~3.x to the current version was mainly concerned +with improving compatibility and peaceful coexistence with the other branch. +First of all the name was changed by moving the character~`x' to the front +of the version number, so that this branch of the system can be referred to +as `\.{CWEBx}' without mentioning a complete version number; for clarity we +shall refer to the other branch as `\LKC.'. @^Levy/Knuth \.{CWEB}@> +Secondly, all the extensions of \LKC. were included, although in a few cases +in a slightly different form to avoid name conflicts with features already +present in \.{CWEB}~3.x. Thirdly, in order to accomodate existing programs +written for \LKC., among which are those of Knuth's impressive collection +called the Stanford GraphBase, a ``compatibility mode'' was added, +selectable by a command line option (\.{+c}), in which the remaining, mostly +trivial, differences in syntax and semantics are removed, and the system +attempts to be an alternative implementation for exactly the same language +as accepted by \LKC., at the price of losing a few possibilities particular +to~\.{CWEBx}. + +The major version number `3' now corresponds to the same version number of +\LKC., and since no further extension of the possibilities of that system +are anticipated, we do not expect that compatibility will ever require any +further increase of this major version number. The Stanford GraphBase +@^Stanford GraphBase@> states that it requires \.{CWEB} version 3.0 or +greater; this version of \.{CWEBx} can fully process it in a satisfactory +way, using compatibility mode. + +The ``banner line'' defined here should be changed whenever changes to +\.{\me.} are made publicly available. Users who are discontent with some +aspect of the program are encouraged to make the necessary modifications in +their copy of the source files; if this involves an improvement of the +implementation, they are kindly requested to inform the author responsible +for the current version. If they choose to make the resulting program +available under the name \.{\me.}, they should modify the non-numeric +prefix of the version specification. + +@d version_string "3.04" diff --git a/web/c_cpp/cwebx/man1/cweb.1 b/web/c_cpp/cwebx/man1/cweb.1 new file mode 100644 index 0000000000..5f67708b44 --- /dev/null +++ b/web/c_cpp/cwebx/man1/cweb.1 @@ -0,0 +1,165 @@ +.TH WEB 1L 7/12/90 +.SH NAME +ctangle, cweave \- translate CWEB to C (or C++) and/or TeX +.SH SYNOPSIS +.B ctangle +[ +.B \-bhpl +] [ +.B \+cs+ +] cwebfile[.w] [(changefile[.ch]|+|-) [outputfile[.c]]] +.br +.B cweave +[ +.B \-bhpx +] [ +.B \+cs+dtefauwm +] [ +.B +isearchpath +] cwebfile[.w] [(changefile[.ch]|+|-) [outputfile[.tex]]] + +.SH DESCRIPTION +The +.I ctangle +program converts a CWEB +source document into a C program that may be compiled in the usual way. +The output file includes #line specifications so that error reporting and +debugging can be done in terms of the CWEB source file. +.PP +The +.I cweave +program converts the same CWEB file into a TeX file that may be +formatted and printed in the usual way. +It takes appropriate care of typographic details like page layout +and the use of indentation, italics, boldface, etc., and it supplies +extensive cross-reference information that it gathers automatically. +.PP +CWEB allows you to prepare a single +document containing all the information that is needed both to produce +a compilable C program and to produce a well-formatted document +describing the program in as much detail as the writer may desire. The +user of CWEB ought to have some familiarity with TeX as well as knowledge +of C. +.PP +The command line should have one, two, or three names on it. The first is +taken as the CWEB file (and `.w' is added if there is no extension). If that +file cannot be opened, the extension `.web' is tried instead. If there is a +second name, it is a change file (and `.ch' is added if there is no +extension), otherwise the name of the CWEB file with its extension replaced +by `.ch' is tried as change file name; if there no such file, no change file +is used. Instead of a change file one may specify `-' or `+'; `-' means do +not use a change file, and `+' means use a change file only if a file of the +default name exists (just as if only one name was given). The change file +overrides parts of the CWEB file, as described in the documentation. A third +name, if present, specifies the output file name explicitly. The default +output file name is that of the CWEB file with extension `.c' (for +.I ctangle +) or `.tex' (for +.I cweave +); the default extension is also applied if a third +name is given without extension. +.PP +Options in the command line may be either turned on with `+' +and off with `-'; several characters may follow in the same argument, which +indicate several options that are turned on respectively off simultaneously. +In fact, the options are processed from left to right, +so a sequence like `-b +b' is equivalent to `+b' (which is the default). +.PP +The +.B \+b +option shows a banner line on your terminal. The +.B \+h +option shows a happy message if the processing was successful. The +.B \+p +option shows progress reports (starred module numbers) as the processing +takes place. If you say `-bhp', you get no terminal output but error messages. +.PP +The +.B \+c +option emulates Levy/Knuth CWEB. +The +.B \+s +option prints statistics about memory usage at the end of a run +(assuming that the programs have been compiled with the -DSTAT switch). +By setting the option +.B \++ +one can select the C++ language instead of C. +.PP +The option +.B \-l +is applicable to +.I ctangle +only; when selected, the C output is made more readable for humans by omitting +.B #line +directives and preserving layout and comments. +.PP +There are several other options applicable to +.I cweave +only. The option +.B \-x +means omit the index and table of contents. By setting the option +.B \+d +one causes +.I cweave +to report problems encountered in parsing the program fragments. With +.B \+t +the the index and module names will be written to separate files. With +.B \+e +the table of contents is forced to start on a new sheet when using a +two-sided printer. +The options +.B \+f +, +.B \+a +, +.B \+u +, +.B \+w +, and +.B \+m +change the style in which programs are +formatted: +.B \+f +means force a newline after every simple statement in the formatted output, +and +.B \+a +means also force a newline before every simple statement, even if it follows +a label or a condition; the option +.B \+u +will change the style so that a line break goes after `{' instead of before, +thus preventing them from aligning with their matching `}' (this is the +style of Levy/Knuth CWEB), while the option +.B \+w +puts a line break both before and after `{', producing a more symmetric +layout at the expense of some extra paper. Finally, the option +.B \+m +avoids forcing line breaks between successive declarations in a local block. +With the option +.B \+isearchpath +one can specify a place to look for header files, when they are not found in +the current directory. The string following the `+i' will be stored and +preprended to a header file name if it cannot be found otherwise (so the +string should probably end with a pathname separator '/'). More than one +such search path can be specified; they will be tried in order from left to +right. +.PP +.SH FILES +/usr/local/lib/tex/macros/cwebxmac.tex TeX macros used by cweave output. +.br +/usr/local/lib/tex/macros/cwebcmac.tex TeX macros used in compatibility +mode. +.br +/usr/local/doc/cweb/manual.tex The user manual. +.SH "SEE ALSO" +.I Literate Programming +by D. E. Knuth. (CSLI, Stanford, 1992) +.br +tex(1), cc(1), web(1) +.SH "AUTHORS" +Don Knuth wrote WEB for TeX and Pascal. Silvio Levy designed and developed +CWEB by adapting the WEB conventions to C and by recoding everything in CWEB. +Frank Jensen adapted the programs to ANSI/ISO C, and Marc van Leeuwen made +numerous changes for version x3.0, and wrote a new manual. +.SH "BUGS" +The syntactic support for C++ does not cover that language completely, so +formatting of complex C++ code may be bad. diff --git a/web/c_cpp/cwebx/manual.dvi b/web/c_cpp/cwebx/manual.dvi Binary files differnew file mode 100644 index 0000000000..f8c4a007af --- /dev/null +++ b/web/c_cpp/cwebx/manual.dvi diff --git a/web/c_cpp/cwebx/manual.pdf b/web/c_cpp/cwebx/manual.pdf Binary files differnew file mode 100644 index 0000000000..781e87873e --- /dev/null +++ b/web/c_cpp/cwebx/manual.pdf diff --git a/web/c_cpp/cwebx/manual.tex b/web/c_cpp/cwebx/manual.tex new file mode 100644 index 0000000000..421542ad4c --- /dev/null +++ b/web/c_cpp/cwebx/manual.tex @@ -0,0 +1,2558 @@ +\input cwebxmac + +\def\title{\CWEBx. MANUAL}\titletrue +\parskip=0pt plus 2pt + +\font\boldit=cmbxti10 +\font\twelverm=cmr12 +\font\twelvett=cmtt12 + +\def\lheader{\mainfont\the\pageno\eightrm\qquad\firstmark\hfill\title} +\def\rheader{\eightrm\title\hfill\firstmark\qquad\mainfont\the\pageno} + +\countdef\secno=1 +\let\oldbeginsection=\beginsection +{\let\oldbeginsection=\relax % temporarily remove outerness + \outer\gdef\beginsection #1 \par + {\global\advance\secno by 1 + \oldbeginsection{\number\secno\space\space#1}\par + \edef\next{\write\cont{\Z{\number\secno}{#1}{\noexpand\the\pageno}}}\next + \uppercase{\mark{#1}}% +}} + + +\let\z\relax +\def\subsection#1 \par +{\smallskip\noindent{\boldit #1}\quad + {\def\.{\noexpand\noexpand\noexpand\.}% + \def\do##1{\def##1{\noexpand\noexpand\noexpand##1}}\dospecials + \edef\next{\write\cont{\z{#1}{\noexpand\the\pageno}}}\next +}} + +\def\pb{\.{|...|}} +\def\<#1>{\hbox{\rm\def\ { }$\langle\,$#1$\,\rangle$}} +\def\sp.{structured programming} +\def\lp.{literate programming} +\def\Lp.{Literate programming} +\def\LP.{Literate Programming} +\def\WEB.{\caps{WEB}} +\def\ASCII.{\caps{ASCII}} +\def\CWEB.{\.{CWEB}} +\def\CWEBx.{\.{CWEBx}} +\def\LKC.{Levy/Knuth \CWEB.} +\def\:#1{`\/\.{@#1}'} +\def\atspace.{\:{ }} + +\outer\def\N#1 #2. #3. %start of `@*' section + {\MN#2.% + \ifon \vskip 0pt plus .5 \vsize \penalty-1000\vskip 0pt plus -.5 \vsize + \vskip\intersecskip + \startsection{\bf#3.\quad}% + } +\def\MN#1.% common code for \M, \N, \n + {\par\xdef\secstar{#1}\ontrue}% no \mark, no starred sections +\def\inx + {\par\vskip6pt plus 1fil \endcodemode % we are beginning the index + \output=\bufferedoutput\pagesavedfalse\eject % eject pages, keeping last + \setbox\sbox=\vbox{\unvbox\sbox} % allow its glue to reset + \vsize=\pageheight \advance\vsize by -\ht\sbox % the remaining height + \hsize=.5\pagewidth \advance\hsize by -10pt + % column width for the index (20pt between cols) + \parfillskip 0pt plus .6\hsize % try to avoid almost empty lines + \def\lr{L} % this tells whether the left or right column is next + \output= + {\if L\lr\global\setbox\lbox=\page \gdef\lr{R} + \else + \normaloutput + {\vbox to\pageheight + {\box\sbox \vss \hbox to\pagewidth{\box\lbox \hfil \page}}% + }% page argument to \normaloutput + \lheader\rheader % other two arguments to \normaloutput + \global\vsize\pageheight \gdef\lr{L} \global\pagesavedfalse + \fi + }% + \parskip 0pt plus .5pt + \outer\def\@##1##2, % index entry; ##1 is `h' or `m' for horiz/math mode + {\par\hangindent2em\noindent\if##1m$##2$\else##2\fi:\kern1em} + \def\[##1]{$\underline{##1}$} % underlined index item + \rm \rightskip0pt plus 2.5em \tolerance 10000 \let\*=\lapstar + \hyphenpenalty 10000 \parindent0pt + } +\def\fin{\let\@\relax\con}% make these innocuous before scanning argument: +\def\con#1\con{% ignore stuff after index + \par\vfill\eject % this is done when we are ending the index + \ifpagesaved\null\vfill\eject\fi % needed in case index is empty + \if R\lr \null\vfill\eject\fi % finish the current page + \parfillskip 0pt plus 1fil % restore normal paragraph end + \setpage % restore page shape + \output={\normaloutput\page\lheader\rheader} % restore output routine +} + +\centerline{\titlefont Literate Programming in C} +\medskip +\centerline{\twelverm The {\twelvett CWEB} System + of Structured Software Documentation} +\medskip +\centerline{Manual for \CWEB.x3.0} +\bigskip +\centerline{Marc A. A. van Leeuwen} + +\bigskip\bigskip + + +\beginsection Overview + +This document describes \CWEB.x3.0, a particular implementation of \CWEB., a +system that supports the concept of ``\lp.'' for programs written in the +language~\Cee\ (more particularly for this version, in \caps{ANSI/ISO} \Cee). +As this manual aims to supply all information possibly relevant to a wide +variety of users, it is necessarily rather extensive. However, \CWEB. is not +a complicated system, and just a few simple commands suffice for practical +programming purposes; these are discussed in the section~4 (the remainder +serves mainly to allow fine-tuning of the presentation of the printed +documents describing literate programs). + +As the somewhat contrived name of the system indicates, \CWEBx. is not the +only version of \CWEB.; indeed it is based on an older version of \CWEB. by +Silvio Levy, which is an adaptation to~\Cee\ of the \.{WEB} system (for +Pascal) written by Donald~E. Knuth, the founder of \lp.. That \CWEB. system +has independently evolved into a system that is currently distributed, under +joint responsibility of Levy and Knuth, as \CWEB.~3.4. Those with experience +using \LKC. will already be acquainted with most aspects of \CWEBx., and may +turn to section~9 for a summary of the differences between the two systems; +however, \CWEBx. also provides a compatibility mode (selectable by specifying +`\.{+c}' on the command line) in which it should be able to process +\Cee~programs written using \LKC. without any modification. + +The structure of this manual is as follows. In section~2 an exposition of +the ideas underlying the concept of \lp. is given, and a description of how +systems of the \.{WEB} family provide concrete tools to support this +programming methodology. This section is directed particularly to those who +are new to \lp.; it explains the purpose of \CWEB. and the logical +connection between the various elements that \lp. adds with respect to +traditional programming. As an illustration we then give a small example +program using \CWEB. in section~3. The main commands of \CWEB., which define +the structure of the source text and tell the \CWEB. tools what to do with +the various pieces of that text, are discussed in section~4. In section~5 we +discuss how the behaviour of \CWEB. tools can be further affected by means +of command line options and file name arguments supplied when the programs +are invoked. In section~6 we discuss some facilities for distributing the +source text over several input files, including a ``change file'' that +allows applying local patches without affecting the original source files; +this is not directly related to \lp., but can be quite useful in larger +projects. In section~7 the remaining minor \CWEB. commands are explained, +and section~8 discusses some features of the \TeX\ format employed by +\CWEB., which can be used to affect the appearance of \CWEB. documents. +Section~9 is devoted to a comparison with \LKC.; finally section~10 +summarises all \CWEB. codes recognised in the source text. + + +\beginsection About literate programming + +\Lp. is a concept that was developed, implemented, and propagated by D.~E. +Knuth in the early 1980's, as a natural sequel to the concept of ``\sp.'' +that had caused a revolution in the world of software development about a +decade earlier. (At this moment, another decade further, one may conclude +that \lp. has not caused a similar revolution, since many programmers +practicing illiterate programming do not feel at all as guilty about this as +they would if they were to be found practicing unstructured programming.) So +let us first consider the idea of \sp. more closely. + +\subsection Structured programming + +Without attempting a definition of the term, it seems fair to say that \sp. +involves designing a program by hierarchical decomposition of the task at +hand, and constructing a program that has a similar decomposition into parts, +where each part ``solves'' the corresponding subtask. The subdivision of the +program manifests itself in its division into subroutines (procedures, +functions), and at the more detailed level in the syntactic composition of +those routines (control structures, blocks); this explains why much emphasis +is placed on the use of subroutines, and why languages with a linear program +model and (conditional) jumps as their main control structure (like assembly +language) do not form a natural vehicle for \sp.. The question as to which +criteria should be used in subdividing problems into smaller ones is +difficult to answer in general, but a good rule of thumb is that at the +level of abstraction at which a task is defined it should be possible to +give a reasonably simple informal description of its subtasks, in which a +new level of detail about the method used to perform the task is given. (It +would be nice if the subtasks also had simple formal specifications, but in +general this is too much to ask, since informal descriptions do not only +abstract from implementation details, they may also conceal numerous obvious +specification details). + +It can be said that the idea of \sp., undone of any dogmatism that has been +associated with it, has been rather universally accepted, and has proven to +be an effective methodology in software development. Other methodologies +have been put forward, such as data encapsulation, but this has been in +addition to rather than as an alternative for \sp.. \Lp. however is related +in a different way to \sp., as it concerns not the contents of the program +itself, but rather the way it is presented. \Lp. presupposes \sp., but is +independent of other programming paradigms; any program that has been +designed in a structured way can in principle be rendered as a literate +program, without requiring a change of the program text itself. + +\subsection Limitations of traditional \sp. + +Although the composition of a structured program should reflect the design +decisions that led to its construction, the traditional way of presenting +such programs lacks appropriate facilities for communicating this +information effectively to readers of the program, seriously limiting the +readability, especially to people other than the programmer of the code. Yet +readability is of vital importance, because it is only by careful reading +that we can verify that the design of a program is sound and +well-implemented, and to understand where and how changes can be made when +such a need arises. Of course the code may be documented by adding an +arbitrary amount of comments, but there are various reasons why this has a +limited effectiveness, so that in practice the level of documentation is +nearly always (much) lower than would be desirable from the point of view of +maintainability. + +The syntactic decomposition of a piece of a structured program into a +hierarchy of control structures, compound statements, etc., is crucial to +understanding the way it functions, yet the human eye is much less capable +of performing this task then a parser is, even when proper indentation is +applied. The difficulty rapidly increases with the size of the program +fragment, and can become a serious factor when this size exceeds a dozen or +two lines (depending on the complexity of the structure). And even when we +can recognise the structure, the meaning of the individual parts cannot be +immediately perceived, but must be derived from close inspection of the +code, or from the comments. However, as comments are localised in the code, +and it is hard to attach a comment clearly to a construct of some +considerable extent. Also, adding too many comments may actually decrease the +readability of the program, by making the structure recognition problem +worse. Even indentation, useful as it may be, can be more of a nuisance than +of any help when it becomes so deep that it forces code and comments to be +squeezed together into (or fall off) the right margin. Finally the fact that +program sources are usually plain text files, represented in a single rather +crude font, does not improve human readability. The limitations of the +character set hamper formulation of comments, where special symbols, +formulae, tables or illustrations might convey the information much more +effectively; in the program itself the eye has to do without visual clues +marking the distinction between various types of program elements +(identifiers, keywords, constants, literal strings, operators, comments, +etc.). As a consequence of all this, few people will find much pleasure in +reading source listings, even if the program is well designed and +documented, and possibly even contains some interesting and subtle +algorithms. + +Many of these problems would not be too grave if all subroutines were +severely limited in size and complexity. However, although it might be +possible to live up to such a restriction for certain kinds of programs (in +particular if the task involves mostly simple actions of administrative +nature rather than any really complicated algorithms), it would be a very +impractical requirement in general, certainly for procedural programming +languages. First of all, having to break up a subroutine using auxiliary +subroutines solely because of the size of the code, violates the basic +principle that such a decomposition should be the result of design +decisions. Furthermore, there are several technical reasons why such a +decomposition could be either impossible, or might involve a large amount of +additional code that has little to do with the actual task being performed, +and that might deteriorate performance unacceptably. Examples of such +reasons are for instance the need to perform a large multi-way branching, to +have local variables that are visible throughout the execution of a complex +algorithm, or to have the possibility to jump out of nested structures on +certain (error) conditions (while the language might not allow jumps out of +a subroutine into the routine calling~it). Finally, introducing many small +subroutines, for reasons that cannot be described easily outside the +immediate context in which they will be used, creates a serious problem of +giving them sensible names and remembering the tasks they perform. + +Concluding we may say that \sp. in its traditional form does not encourage +or even allow the level of documentation that would be desirable for +maintenance and intelligibility by people other than the author of the +program. + + +\subsection Requirements for \lp. + +The basic idea of \lp. is to take a fundamentally different starting point +for the presentation of programs to human readers, without any direct effect +on the program as seen by the computer. Rather than to present the program +in the form in which it will be compiled (or executed), and to intercalate +comments to help humans understand what is going on (and which the compiler +will kindly ignore), the presentation focuses on explaining to humans the +design and construction of the program, while pieces of actual program code +are inserted to make the description precise and to tell the computer what +it should do. The program description should describe parts of the algorithm +as they occur in the design process, rather than in the completed program +text. For reasons of maintainability it is essential however that the +program description defines the actual program text; if this were defined in +a separate source document, then inconsistencies would be almost impossible +to prevent. If programs are written in a way that concentrates on explaining +their design to human readers, then they can be considered as works of +(technical) literature; it is for this reason that Knuth has named this +style of software construction and description ``\lp.''. More background +information about this concept and its history concept can be found in +Knuths book ``\LP.'', CSLI Lecture notes~\#27, Leland Stanford Junior +University, 1992 (ISBN 0-937073-81-4). + +From the discussion above it will be clear that traditional programming +languages are not directly suitable for \lp.. We shall now try to formulate +requirements for a system that supports \lp.. Doing so we shall especially +keep in mind programs that evolve after their original design, possibly +altering certain parts of that design, and possibly being realised by +different persons, since it is in such cases that the benefits of \lp. are +particularly crucial. + +The documentation parts of the program description should allow for the same +freedom of expression that one would have in an ordinary technical paper. +This means that the document describing the program should consist of +formatted text, rather than being a plain text file. This does not exclude +the possibility that the source is written as a plain text file, but then it +should undergo some form of processing to produce the actual program +description. The document should moreover contain fragments of a program +written in some traditional (structured) programming language, in such a way +that they can be mechanically extracted and arranged into a complete +program; in the formatted document on the other hand layout and choice of +fonts for these program fragments should be so as to maximise readability. + +Parts of the program that belong together logically should appear near to +each other in the description, so that they are visible from the part of the +documentation that discusses their function. This means that it should be +possible to rearrange program text with respect to the order in which it +will be presented to the computer, for otherwise the parts that deal with +the actions at the outer level of a subroutine will be pushed apart by the +pieces specifying the details of inner levels. The most obvious and natural +way to do this is to suppress the program text for those inner levels, +leaving an outline of the outer level, while the inner levels may be +specified and documented elsewhere; this is a bit like introducing +subroutines for the inner levels, but without the semantic implications that +that would have. There should be no restrictions on the order in which the +program fragments resulting from this decomposition are presented, so that +this order can be chosen so as to obtain an optimal exposition; this may +even involve bringing together fragments whose location in the actual +program is quite unrelated, but which have some logical connection. + +Obviously there should be a clear indication of where pieces of program have +been suppressed, and which other program fragments give the detailed +specifications of those pieces. From the programming language point of view +the most obvious method of identification would be to use identifiers, +resulting in a simple system of parameterless macros, with as only unusual +aspect that uses of the macro are allowed to precede the definition, and +indeed do so more often than not. Actually, \lp. uses a method that differs +from this only trivially from a formal standpoint, but has a great advantage +in practical terms: identification is by means of a more or less elaborate +phrase or sentence, marked in a special way to indicate that it is a +reference to a program fragment. This description both stands for the +fragment that is being specified elsewhere, and also serves as a comment +describing the function of that fragment at a level of detail that is +appropriate for understanding the part of the program containing it. In this +way several purposes are served at once: a clear identification between use +and definition is established, the code at the place of use is readable +because irrelevant detail is suppressed, with a relevant description of what +is being done replacing it, and at the place of definition a reminder is +given of the task that the piece of code presented is to perform. The +documenting power of such a simple device is remarkable. In some cases the +result is so clear that there is hardly any need to supply further +documentation; also it can sometimes be useful to use this method to replace +small pieces of somewhat cryptic code by a description that is actually +longer than the code itself. It is hard to give a sharp limit on the length +of the description for a program fragment, but if substantially more than a +sentence is needed, say a full paragraph, then the fragment probably does +not represent a well chosen abstraction, which might be an indication that +the design of the program has some room for improvement. On the other hand, +it is good practice to explicitly mention any unusual control flow that +might be caused by executing the abstracted fragment, like jumping to a +label outside the fragment, since such information is vital for a proper +understanding of the program at the place where the fragment is used. + +\subsection WEB systems for \lp. + +Until now we have discussed \lp. as a general concept, independent of any +particular implementation; this was done to stress the generality of the +idea. We shall now indicate how these ideas are realised by ``\WEB. +systems'', a family of systems that were modelled after Knuth's original +\.{WEB}, and of which \CWEB. is a member. In these systems the program +source is written as a plain text file, and a pair of programs is provided, +which transform this source into other text files suitable for processing by +a compiler respectively by a typesetting program. Other kinds of \lp. tools +are conceivable (e.g., ones that would provide the programmer with a direct +graphical representation of the typeset document while editing, possibly +with hypertext facilities), but this approach has the advantage of being +fairly simple and portable across many platforms. The approach is not the +simplest possible however, as a substantial part of the work done by the +tools deals with transforming the program fragments from their plain text +form into typeset text with proper fonts and layout (i.e., with +pretty-printing); this part of the task also depends essentially on the +programming language being used. By reverting to verbatim representation of +program fragments one could make simple tools that support \lp. in a +language independent way---and indeed such tools exist---but then a price is +paid in terms of readability. We should also note that \WEB. systems support +writing documents whose purpose is to simultaneously specify and document a +program; if one is primarily writing a theoretic paper, in which only +occasionally pieces of program text are mentioned, then one might prefer a +slightly different kind of system that does not impose as much global +structure on the document as \WEB. systems do. + +Not surprisingly, \WEB. systems satisfy all the requirements for \lp. +formulated above, and they do so in a fairly straightforward manner. A +typeset \WEB. document consists of a sequence of consecutively numbered {\sl +sections}, whose size is typically less than half a page. Each section may +contain a program fragment, called a {\sl module}, preceded by a commentary +in ordinary text, although in some cases either one of these parts might be +absent. (The programming language used for the program fragments depends on +the particular \WEB. system used, as does the typesetting system that +eventually produces the printed document; for the \CWEB. system described +here these are respectively \Cee\ and \TeX.) In most cases a module is +headed by a text in angle brackets called its {\sl module name}, which gives +a description of the task it performs. This name is followed by `$\EQ$' and +the program code that constitutes the module itself; this is called a +defining occurrence of the module name. (We make a distinction between the +words `section' and `module', using the former for a numbered portion of the +\WEB. document, and the latter for a named portion of the program described +by it.) A module name can also be used in the body of some other module, +either before or after its definition in the document, to represent the +corresponding piece of program text. \WEB. facilitates locating the +definition of a module from the place where it is used by automatically +incorporating the number of the defining section in the module name. The +actual program text is then constructed by recursively replacing module +names by the text specified in their definition (this should of course +follow the grammatical structure of the program, lest the most basic +principles of \lp. be violated). A program fragment occurring at the +outermost level is distinguished by the fact that it is not headed by a +module name. + +Most module names will have just one defining occurrence, and will also be +referenced just once; in both cases there may however be exceptions, where a +module name has more than one occurrence of the indicated kind. If a module +is multiply referenced, this simply means that the corresponding part of the +program text is repeated identically in more than one place. If a module +name has more than one defining occurrence, then the text of the +corresponding module is obtained by concatenating the program fragments of +all its definitions in the order in which they occur in the document. In a +similar fashion all fragments without module name are combined into an +``unnamed module''. These are the only occasions where the order in which the +sections are given can have any effect on the final program; apart from this +the literate programmer has complete freedom of ordering the sections in a +way that facilitates understanding the program as much as possible. + +We need not discuss all aspects of \WEB. systems here, but a few points that +contribute to \lp. by improving readability should be mentioned. The proper +formatting of all program fragments is automatically taken care of by the +\WEB. system, providing a uniform style of presentation. The system also +provides a large amount of cross-reference information; this greatly +facilitates reading the program and searching for specific pieces of code. +Not only is the number of the (first) section defining a module incorporated +in its name, but in that defining section indications are also given of the +section(s) in which the module is used, and possibly of any further defining +occurrences of the same module name. At the end of the document an +alphabetically sorted index is added, listing for each identifier all the +sections in which it is defined or used; the programmer may also add +additional entries to the index by indicating in the program source that +certain sections should be referenced for particular terms. A list of all +module names is also given, which may help locating the part of the program +dealing with some issue. So in many ways the \WEB. system tries to aid human +understanding of the program, but of course the literacy of the programmer +will always remain the decisive factor in this respect. + +\beginsection What a\/ {\tentt CWEB} program looks like + +Enough now of general considerations, let us turn our attention to the +\CWEB. system this manual is really about. The best way to learn about it is +probably to read a \CWEB. document. Therefore we include a small but +complete \CWEB. program below; the program is about as small as possible +without rendering a decomposition into modules pointless. The example is not +intended as a showpiece of programming literacy, but it demonstrates various +aspects of the system. The index at the end of the program is included, but +not the list of the four module names in this program or the table of +contents. One will notice that some symbols appear to be different from +their official representation in \Cee, for instance the assignment +operator~`\.=' appears as~`$\K$', the equality operator~`\.{==}' as~`$\E$', +the logical ``and'' and ``or'' operators `\.{\&\&}'~and~`\.{||}' as +`$\W$'~and~`$\V$' respectively, the variable~`\.{f1}' as~`$f_1$', and the +null pointer~`\.{NULL}' as~`$\NULL$'; thus the possibilities of the +typesetting system are used to improve the appearance of the program. + +\vfil\eject + +\begingroup \input compare \par\endgroup + +\subsection Some remarks about the example program + +Reading the program should not cause great problems to anyone familiar with +the \Cee\ language, once one gets used to the representation of the symbols. +We mention a number of points that will have become clear in the course of +the example. + +The commentary text at the beginning of the sections is set in ordinary +paragraphs, which contrasts sufficiently with the appearance of the program +text that the dividing line between the two can be easily perceived, even +though it is only marked by a bit of white space. In case the section +defines (part of) a named module, the module name heading the program +fragment is set flush left, and is followed by~`$\EQ$', or in case this is +not the first defining occurrence of that name, by~`$\PE$' (therefore, the +occurrence of the module name `$\X5:Functions\X$' in \Sec2 is not a defining +one, whereas the occurrences of that name in \Sec5 and~\Sec7 are). The style +in which the module names and comments contained in the program fragments are +set is similar to that of ordinary text; indeed if they are too long to fit +on the line, they will be broken across lines (with proper care taken to +respect the indentation level). In \CWEB. embedded comments are always +attached to the right of a program element (usually a statement or +declaration); in the example we can see there is relatively little need for +embedded comments, because of the other means provided for documentation. +An embedded comment that is split across lines will not look very good, and +should only occur in cases of emergency; in most cases it is better to use +the commentary part at the beginning of the section for any elaborate +explanation. On the other hand, long module names (occupying up to about +four lines) are not uncommon when the task performed by the module calls for +an extensive description. + +As one can see in the example, it is common to refer to small pieces of +\Cee~code (in most cases just variables or simple expressions) from within +the commentaries, module names and comments. The \CWEB. system makes it easy +to include such pieces, by providing a variant of the formatting routines +used for the actual program fragments (differing from them by the omission +of any 2-dimensional layout features such as indentation). In many cases the +pieces of \Cee~code are so simple that they could easily be typeset directly +(using \TeX's math mode), producing the same formatted output without using +the facilities of \CWEB.. But even then it is preferable to use \CWEB. +instead, because it will then guarantee that all identifiers mentioned in +such a way in the documentation part of a section or in a comment, will be +included in the index at the end of the program. Although in many cases a +reference would have been generated anyway by the program fragment in the +same section (as happens in all cases for our example program), this +mechanism ensures that even remarks about the use of variables and functions +made in sections that contain no program fragment at all can be traced from +the index. Incidentally, identifiers that are used only in a module name are +not indexed, which is why there is no reference to \Sec2 in the index entry +for~\\{exit}. + +When an index entry is recorded, whether from within a program fragment or a +piece of \Cee~code embedded in text, the occurrence may be flagged as +`defining', depending on the context; this happens for instance in the case +of parameters in an \caps{ANSI/ISO} style function heading, of variable +declarations and of labels. If at least one occurrence of an identifier in +some section is a defining one, then the corresponding section number in the +index entry for that identifier will be underlined. Single-letter +identifiers, the special identifier \.{NULL} (appearing as `$\NULL$'), and +keywords of the language are considered so ubiquitous that no index +references for them are generated, except those that are underlined; e.g., +in the example there is no reference to \Sec6 for the variable~$n$. For +keywords this means that they will not appear in the index at all (unless +the programmer explicitly marks certain occurrences as defining); note +however that identifiers defined in a \&{typedef} declaration (like \&{bool} +in the example) will be indexed, even though they are set in boldface just +like keywords are. + +\subsection Further attributes of\/ {\tentt CWEB} programs + +An aspect of \CWEB. programs that does not stand out very clearly in our +miniature example is that it allows sets of related sections to be grouped +together into ``chapters''. Each chapter is identified by its title, which +appears in boldface after the number of its first section; in our example +sections 1~and~8 start new chapters. The division into chapters has a few +more effects on the document, which were suppressed in our example, since +they would interfere with the overall structure of this manual: each chapter +starts on a fresh page, its title appears in the running head of all its +pages, and all chapter titles are collected in the table of contents. +(Style changes such as employed in this manual are easy to obtain, since the +style is not determined by the \CWEB. system, but rather by a separate +format consisting of \TeX~macros; a few small changes to standard format can +change the overall appearance of the document, and it would be equally easy +to change for instance the page size or the symbols used to represent +operators.) + +There is one important point left to explain about the example, which is the +special position of the lines starting with \&{\#define} and \&{\#include}. +Although they look like ordinary preprocessor lines, which +could have been included in the program fragments, they are in fact separate +items that are given between the documentation part and the program part of +a section (this can be seen best in~\Sec6), forming a third type of +constituent of sections (although in most sections they will be absent). +Their place in \CWEB. is less distinctive then that of their analogues in +\WEB. systems for languages that have no preprocessor (like the original +\.{WEB} for Pascal, which provides a separate macro facility itself): indeed +the directives are just passed on to the \Cee\ preprocessor. Yet there is +some advantage in specifying them as special items to \CWEB., and in most +cases using these facilities is preferable to embedding the directives in +the \Cee~program fragments. + +One reason is that one usually wants the effects of preprocessor directives +to be visible throughout the \Cee~file that is generated, while this would +not always be the case if they were specified inside the program fragments; +for instance if the definition of \\{read\_mode} in \Sec6 had been included +in the program fragment, it could not have been validly used in \Sec7, +because that section will precede~\Sec6 in the \Cee~file produced. This +difficulty could be overcome by collecting all macro definitions in a module +that is used at the start of the program and defined in many sections +throughout the \CWEB. document. In fact this is just about how \CWEB. treats +the separately specified preprocessor directives: they are collected in +order of appearance, and placed at the very beginning of the \Cee~file. +(Some other place of insertion for the preprocessor directives can be +specified by means of a pseudo-module named `$\ATP$', but this is quite +rare.) Since a section can define only one module, the \CWEB. facility for +preprocessor directives may help avoid having to split up sections merely +because they contain such a directive. Furthermore, an important reason to +specify \&{\#include} directives to \CWEB., is that this allows it to +inspect those header files for any typedef declarations, so that programs +can be formatted properly; without this programs using typedef identifiers +defined in header files would seriously confuse the syntax analysis that +\CWEB. performs, resulting in very poor quality formatting of program +fragments. + +Preprocessor directives other than those mentioned above can only be +incorporated in a program by including them in an ordinary program module, +but there is relatively little need for such directives. In situations where +one would use conditional compilation in ordinary~\Cee, one can usually use +the ``change file'' mechanism provided by \CWEB. instead (this will be +discussed below), especially if it involves system dependent modifications; +this has the advantage that such modifications do not affect the main source +files, and only those modifications that are actually applied will be +visible in the \CWEB. document. In the rare cases that one does include a +preprocessor directive in a program fragment, the fact that it is not being +specified as a separate item to \CWEB. is usually easy to recognise in the +\CWEB. document, because the module name being defined or some program text +precedes it; however even if this should not be the case then such embedded +directives can still be distinguished by a slight difference in horizontal +and vertical spacing. + +\subsection Output to multiple files + +There is one important construction one may encounter in \CWEB. documents, that +we have not mentioned yet. There may be module names that consist of a file +name in typewriter type, like `$\X14:\.{common.h}\X$'; usually such module +names are nowhere referenced, but only have one or more defining +occurrences. \CWEB. documents containing such a module will produce a file +of that name in addition to the \Cee~program that is normally produced. The +module bearing the name of the file will form the root module of the +\Cee~code written to that file, in the same way as the unnamed module forms +the root module for the ordinary output. This feature is particularly useful +for the production of header files that can be included by other compilation +units (and even by the program produced as main output). It allows one for +instance to state function prototype declarations that go to the header file +and the matching function definitions in the \Cee~program in the immediate +vicinity of one another within the \CWEB. document. The module with the file +name can refer to submodules, and so on to any depth, just like the modules +contributing to the main output. This possibility should be used with some +restraint however, lest readers have difficulty finding out to which file +the program fragment defined by some module will be sent. The preprocessor +lines that are handled by \CWEB. will normally only become part of the main +program output, not of any additional output files; this provides one valid +reason for sometimes bypassing the facilities of \CWEB., and incorporating +\&{\#define} and \&{\#include} directives directly into program modules. + + +\beginsection How to create a\/ {\tentt CWEB} program + +In the previous section we have explained how one should read \CWEB. +documents; in this section we shall discuss how they can be written. The +``\CWEB. document'' we have been discussing is the printed text that is +eventually produced from the source file written by the programmer, but +that file does not look quite like the printed version; on the other hand +the difference in appearance is not so great that there is any difficulty +finding the place in the source file corresponding to some part of the +printed text. + +\subsection The general setup + +The programmer creates a plain text file using the format explained below, +which contains both program fragments and commentary, and has file name +extension `\.{.w}'; e.g., the file from which the example above was produced +is \.{compare.w} (it is included in the \CWEBx. distribution). The \CWEB. +system consists of two utility programs `\.{CTANGLE}' and `\.{CWEAVE}' that +can be applied to this source file. In order to create an executable +program, one issues the command `\hbox{\.{ctangle compare}}', which will +read the file \.{compare.w} and write a file \.{compare.c} containing the +corresponding \Cee~program. This file can then be processed in the ordinary +way by any \Cee~compiler to produce an executable program. To produce a +printed document on the other hand, one issues the command `\hbox{\.{cweave +compare}}', which will again read the file \.{compare.w}, and this time +write a file \.{compare.tex}. This file serves as input for the typesetting +program~\TeX: by giving the command `\hbox{\.{tex compare}}' it will be +processed, and the result is a file \.{compare.dvi}. This file can be either +previewed or converted to hardcopy output by the system dependent programs +for this purpose that accompany \TeX. Despite the somewhat elaborate +processing trajectories, it will become apparent that the programmer has +good control over the final result produced in both cases. + +A word of explanation about the names of \CWEB. and its constituent +programs. The initial \.C's stand for the programming language, of course; +the rest of the names are the same as those chosen by Knuth for the original +\.{WEB}~system (which existed long before the World Wide Web). The \CWEB. +language allows one to separately describe small parts of a \Cee~program and +their interconnections, both formal (via module references) and informal (by +some semantic relationship); with some fantasy this evokes the image of a +web of connected pieces. These parts are linearised quite differently in +their presentation for human readability than in the ``official'' form in +which they are presented to the \Cee~compiler, and it is the program +\.{CTANGLE} that does the somewhat complicated reordering to obtain the +latter from the former. This process is traditionally called ``tangling'' +the code, although one could also call it untangling if one prefers formal +to human order. The \.{CWEAVE} program intertwines the \TeX\ and \Cee~parts +of the source text and ``weaves'' them together like warp and weft, +resulting in a beautifully formatted document. Despite these pretty +metaphors, you will be forgiven if you sometimes get these names mixed up. + +This general organisation of \CWEB. has some immediate consequences. First +of all, one needs to have an operational \TeX~system and (not surprisingly) +a \Cee~compiler in order to use \CWEB.; the \CWEB. programs form only a +comparatively small part of the utilities needed. Second, the \CWEB. +language must be such that both valid \Cee~code and \TeX~input can be +derived mechanically from it, which are rather different formats. +Nevertheless the \CWEB. language is quite simple: this is because for almost +all of the \CWEB. source text the required format is either that of \TeX\ or +that of \Cee. The main function of the specific \CWEB. commands is to +structure the source file and determine which parts of the input will be +processed further in what way. Finally, a somewhat unfortunate consequence +of \CWEB.'s setup is that errors may be detected by any one of \.{CTANGLE}, +the \Cee~compiler, \.{CWEAVE} and \TeX. The knowledge about \Cee~and~\TeX\ +built into the \CWEB. programs is far from sufficient to ensure that they +will always produce error-free output code, although of course they do their +best not to introduce any errors themselves. A bright point in the case of +\Cee~errors, is that the \&{\#line} directives produced by \.{CTANGLE} enable +the compiler to refer directly to lines in the \CWEB. source file in its +errors messages, rather than to the intermediate \Cee~file (but \TeX\ does +not have a similar facility). + +It follows from these facts that the \CWEB. programmer must be acquainted +both with \Cee\ and with \TeX; however, the depth of the knowledge required +it not the same in both cases. Obviously, one cannot write a computer +program without a good understanding of the programming language used, but a +very superficial knowledge of \TeX\ will suffice: in most cases no \TeX +pertise beyond the basic facts in chapters~2--6 of {\sl The \TeX book\/} is +required (but please don't skip chapter~2, as only too many people have +done). The reason for this is that one rarely needs to instruct \TeX\ to do +sophisticated formatting. It is true that the proper typesetting of computer +programs is a subtle matter, but it is precisely this part that is taken +care of by \.{CWEAVE} (even for references to \Cee~constructs in the +commentary), and the programmer can just concentrate on writing +syntactically correct \Cee~code. On the other hand the full power of \TeX\ +is available if one wishes to use it, for instance to illuminate the program +with things like complicated tables, or math formulae of a different nature +than those occurring in a computer program. + +Since the \CWEB. commands deal only with the structure of the source file, +not with its contents, they can be very brief: they consist of `\.@' followed +by one other character, and are commonly referred to as {\sl control codes}. +For instance, \atspace. (i.e., `\.@' followed by white space) indicates the +start of a new section, and \:c marks the start of the \Cee~part of a +section that contributes to the unnamed module. Control codes may placed at +any position within the source lines, although it is customary to place the +ones defining the coarse structure of the source file at the beginning of a +line for better visibility. In some cases a control code marks the beginning +of a piece of text that will be interpreted by \CWEB. in a special way, as +for instance \:< which starts a module name; the end of these {\sl control +texts\/} is always marked by the special code \:>. The character `\.@' +was selected because it is quite uncommon both in \Cee\ and in \TeX\ source +code, but in those cases where one does need to pass on the character itself +(e.g., in \Cee~strings and comments) it should be written as \:@. + +We now discuss the various control codes, grouped by their function. Here +we shall treat only the most important control codes, which are used +regularly in ordinary programs. Treatment of a number of additional control +codes, that either serve for fine tuning in special cases, or are intended +to allow emergency fixes in unforeseen cases, is deferred to a later +section, in order not to confuse novice \CWEB. users. For the codes that +are discussed, we do however provide full details of their use; most of +these can be skipped on first reading. A summary of all \CWEB. control +codes can be found an the end of this manual. + +\subsection Sectioning codes: \:*, \atspace., \:\~ + +The most important control codes are those that specify the division of the +\CWEB. program into sections. There are three codes that indicate the start +of a new section, and are therefore called sectioning codes. Each of them +has a slightly different effect, and each section must start with one of +them (i.e., a section is never implicitly started). The three sectioning +codes are \:*, \atspace., and \:\~, of which the second one is the most +commonly used. No section numbers should be given in the source file: these +will be automatically computed and inserted by \CWEB.. A tab or newline +following `\.@' is considered equivalent to a space, and for any of these +three control codes, (further) white space separating it from the \TeX~text +that follows is ignored, as long as there is no completely blank line (which +\TeX\ would interpret as the end of the paragraph that started with the +section number). + +A section starting with \:* will start a new chapter of the \CWEB. +document; it should be followed by the title of the chapter, which is +terminated by the two-character sequence `\hbox{\.{. }}' (again the space +might be any white space character). The title is not recognised by \CWEB. +itself, but rather by \TeX, as a delimited macro argument +\unskip\footnote{${}^\dagger$} +{Therefore, if no correctly specified title follows~\:*, then + \.{CWEAVE} will find nothing wrong, but \TeX\ will complain about a + ``Runaway argument'' of a macro that the programmer did not explicitly + write (namely `\.{\\N}'); this is one of the scarier error message that + novice users can come across, so please be warned. +}. +This means that if one wants to have an occurrence of the sequence +`\hbox{\.{. }}' in the title itself, this can be achieved by enclosing the +title (but not the `\hbox{\.{. }}' terminating it) in braces. If one wants +to put other things than plain text in a chapter title, one should be aware +that it is converted to upper case in the running heads of pages and also +written to the table of contents file; only items that behave properly under +these operations should be used in a chapter title. Apart from issuing a +title that will appear in several places, a section starting a chapter will +force a page break before it, and it will cause the section number to be +printed on the terminal during the execution of \.{CTANGLE} and \.{CWEAVE}, +as a progress report. + +As a feature for advanced users of \CWEB., some extra information may be +supplied with the \:* control code: if it is immediately followed by `\.*' +or by a decimal number, than this is not included in the chapter title, but +rather interpreted as an indication of the ``level'' of the chapter. Here +`\.{@**}' indicates the start of a grouping of sections even coarser than a +chapter, and the grouping started by `\.{@*$n$}' becomes finer as $n$ +increases, with `\.{@*0}' corresponding to unadorned \:*. The effect of this +level depends on the definition of the \TeX\ macros that format the chapter +title and the lines in the table of contents, `\.{\\N}' respectively +`\.{\\contentsline}', to which the level is passed as first argument (for +`\.{@**}' the level is~$-1$); it could effect for instance the font used for +the chapter title or the amount of indentation of that title in the table of +contents. In the default definitions of these macros the level is largely +ignored, except that `\.{@*$n$}' will not force a page break for $n\geq s$, +where $s$ is the value of the `\.{\\secpagedepth}' register, which is set +initially to~2. + +In contrast to~\:*, a section starting with~\:\~ instead of~\atspace. will +tie itself to the previous section, in the sense that a page break between +these sections will be avoided. More precisely this is what happens: +normally \.{CWEAVE} will instruct \TeX\ to break pages only between sections +(except when one is too large too fit on a single page) and put as many +sections on each page as possible subject to this restriction; however, a +section starting with~\:\~ will be considered to be continuation of the +previous section for the purpose of page breaking. A situation where one +would use \:\~ is the following: suppose we define a function, and also want +to state its prototype, which will belong to a different module, since it +has to appear earlier in the program or even on a separate (header) file. A +natural place to give the prototype in the \CWEB. document is directly +before the function, so that it can easily be seen that the prototype +matches the actual definition. Now without special measures there is a +substantial chance that a page break will occur between these two sections, +since the short section with the prototype might fit on an already partially +filled page, whereas the larger section with the definition might not. By +starting the latter section with~\:\~, it can be achieved that in such cases +the former section is moved together with the latter to the new page. + +Like any other section the very first section starts with s sectioning +code (usually \:*), and any text that might precede it is not +part of any section; this text is said to be ``in limbo''. This material is +ignored by \.{CTANGLE}, and copied literally into the \TeX\ file by +\.{CWEAVE} (except for the replacement of~\:@ by~`\.@'), following the first +line which always reads `\.{\\input cwebxmac}' (in order to load the +standard format). The purpose of the text in limbo is to allow issuing +\TeX~commands that apply to the whole document (such as macro definitions, +possibly modifications or additions to the standard format), or producing a +title page or an introduction preceding the sections of the \CWEB. document. +No control codes are allowed in the limbo text (well, almost; there are two +exceptions, that will be mentioned below). The last section ends simply +at the end of the \CWEB. source file; there is no way to add material after +it (or elsewhere outside the sections). However, \.{CWEAVE} will append some +material at the end itself (unless it is invoked with a `\.{-x}' flag): an +index of identifier uses, a list of module names, and a table of contents. +Because the index is seamlessly attached to the last section, it is +customary to give that section the title ``Index'' and not to include any +program fragment in it. + + +\subsection Subsectioning codes: \:d, \:h, \:f, \:c, `\/\.{@< {\dots} @>=}' + +Each section, as delimited by the sectioning codes, contains a \TeX~part +(although it may be empty), and in addition at most one \Cee~part, which +always comes at the end of the section, and zero or more intermediate parts, +of which there are three kinds: those that specify \&{\#define} and +\&{\#include} directives, and format definitions (see below). Intermediate +parts can be given in an arbitrary order, as long as they come after the +\TeX~part and before the \Cee~part, if present. The beginning of any part +other than the \TeX~part (which starts directly after the sectioning code) +is marked by an appropriate control code, which is called a subsectioning +code; these codes are optional in the sense that they need only be given if +the corresponding part is present. The end of the \TeX~part is determined by +the first subsectioning code, or in absence of any of them by the next +sectioning code. + +The \Cee~part, if present, begins at the first occurrence of \:< or \:c; the +former starts a defining occurrence of a module name, and the latter is used +when the \Cee~part belongs to the unnamed module. The code \:c may also be +written as \:C (in fact all alphabetic codes are equivalent to their upper +case counterparts). Once the \Cee~part of a section is started, any further +module names are interpreted as modules references rather than as defining +occurrences. A module name, whether defining or not, consists of \TeX~code +between the \:< and the next occurrence of~\:>. As a measure against +accidental misinterpretation of module names, due for instance to a +forgotten \atspace. or \:c, the closing \:> of a defining occurrence must be +followed (optionally with some white space in between, but no newline) by +one of `\.=', `\.{==}', `\.{+=}' and `\.{+==}', while for a non-defining +occurrence this must not be the case. The possibilities `\.{+=}' and +`\.{+==}' are included for those who like their source code for +continuations of modules to resemble the printed output, but the distinction +is ignored by \CWEB.: it will simply print `$\EQ$' after the first defining +occurrence of a module name and `$\PE$' after any further defining +occurrences. + +The subsectioning codes that mark the beginning of intermediate parts are +\:d, \:h, and \:f. Of these the first two specify preprocessor directives +for respectively a macro definition and the inclusion of a header file, and +the last specifies a so-called format definition. The codes \:d and \:h will +be replaced by \&{\#define} respectively by \&{\#include} in both the +program and the printed document. We already mentioned how the effect of +using \:d or \:h differs from that of using \&{\#define} or \&{\#include} +directly in the \Cee~part of the section: the directive will be moved to the +beginning of the \Cee~file, and in case of \:h, the header file will be +scanned for typedef definitions. Here we mention a few more points that are +relevant when writing the source file. + +Macro definitions following \:d are not line-oriented like those in \Cee: +everything up to the next subsectioning or sectioning code is considered to +belong to the macro, and newlines need not be escaped, as \.{CTANGLE} will +take care of escaping any newlines while writing to the \Cee~file. There are +some mild restrictions on the replacement text of a \:d macro definition: +parentheses and braces should be balanced (this is a deliberate requirement, +made in order to allow detection of programming errors that would otherwise +be very hard to track; the same requirement also holds for each complete +\Cee~part of a section), and no module names should be referenced. It is not +possible to use other preprocessor directives in macro definitions either, +but that is because this is already impossible in~\Cee. After a \:h command, +at least one newline should occur before the next sectioning or +subsectioning code. + +Apart from this, \:d and \:h are followed by whatever would follow +\&{\#define} respectively \&{\#include}, with the same deviant lexical rules +as in~\Cee. So whether a macro introduced by \:d is defined with or without +arguments depends on whether the first character after the identifier +following \:d is a left parenthesis or not, where spaces {\it are\/} +significant. The file name after~\:h may be enclosed either in double quotes +or in angle brackets; the latter indicates that the header file is located in +some system include file area. After the file name a comment may be placed. + +The header file specified after \:h itself should of course contain ordinary +\Cee~code rather than \CWEB. input; after all, it will be read directly by +the \Cee~compiler. As as mentioned before, the file will be scanned by +\.{CWEAVE} as well, searching for any typedef definitions; moreover, if it +contains any lines starting with \&{\#include}, then those files will be +scanned recursively as well. In the case of system header files (specified +with angle brackets), \.{CWEAVE} will refrain from scanning the file unless +the file is found on an explicitly specified search path (see below); in +fact it is better not to scan any of the \caps{ANSI/ISO} standard header +files, since \.{CWEAVE} already knows about all typedef definitions that can +occur in such header files. It is not uncommon that a header file specified +after~\:h (using quotes) is itself an auxiliary output file produced from a +\CWEB. source file, possibly even from the very source file containing the +\:h~command. There is no circularity or other problematic aspect of such a +situation, but one should remember to run \.{CTANGLE} to produce the header +file, before the run of \.{CWEAVE} that needs it. + +The way \.{CWEAVE} searches for the header file depends on how the name +following \:h is specified: if it is enclosed in quotes then \.{CWEAVE} will +look first in the current directory. There may have been specified one or +more alternative places to look for header files, in the form of strings +that can be prefixed to the file name (given on the command line or compiled +into \.{CWEAVE}, or both). If so, these will be tried in order, regardless +of the delimiters used for the file name, until a match is found; \.{CWEAVE} +will only insist on actually finding a header file if the file name was +enclosed in quotes. + +There is one aspect of scanning header files that might cause a problem in +some cases: when scanning a header file, \.{CWEAVE} is unaware of other +preprocessor directives that may disable certain nested \&{\#include} +directives; \.{CWEAVE} will therefore obey such \&{\#include} directives +unconditionally. Such a problem is not very likely, but it could be serious +if the nested header file cannot be found (and is enclosed in quotes), or if +there are circular references between header files. Various solutions could +be found for such a problem, depending on the precise situation, varying +from creating dummy files or avoiding conditional compilation by the use of +change files to (as a last resort) avoiding the scan of the header file +altogether, by using \&{\#include} in a program fragment rather than~\:h; in +the latter case relevant information could be extracted from the header +file manually, and converted into format definitions (\:f) described below. + +When preprocessor directives are incorporated in the \Cee~part of a section, +the ordinary rules of \Cee\ apply: they should be spelled out in full, as +`\.{\#define}' or `\.{\#include}', and occur at the beginning of a line; the +directive ends at the next non-escaped newline. Although in \Cee\ it is +permissible to extend a preprocessor directive into the following line by +placing a multi-line comment that contains the newline, this should not be +done in \CWEB., since the comment will be removed by \.{CTANGLE} but the +newline will remain. If one needs a very long comment after a preprocessor +directive, one should start it on the line following the directive; in the +formatted document such a comment will be placed on the same line as the +directive. The same holds for comments placed after a \:h command. + +Format definitions, indicated by the code \:f, are entirely specific to +\CWEB., and have no effect on the \Cee~program that is defined. They are not +needed very often, but when they are, a proper use of them is essential for +obtaining acceptably formatted output. To understand why they are sometimes +needed, one has to consider the way \.{CWEAVE} formats program fragments. +The input is broken up into tokens (like identifiers, constants, operator +symbols), and a syntactic category is attached to each; the resulting +sequence of categories is then analysed according to a grammar, and formatted +correspondingly. Certain identifier tokens are recognised as reserved words +and get a corresponding the syntactic category, others are recognised as +typedef identifiers and get the same syntactic category as for instance +\&{size\_t}, and the remaining ones are treated as ordinary identifiers. This +scheme usually works fine, but occasionally there can be problems, caused by +the fact that \.{CWEAVE} is not aware of all the information that is +available to the compiler. The main reasons for this are macros (which may +cause the code seen by the compiler to be quite different from that seen by +\.{CWEAVE}), typedef declarations that are hidden from \.{CWEAVE}'s sight, +and module names that stand for a construct of a different syntactic +category than \\{statement} (which is what \.{CWEAVE} expects them to be by +default). In all these cases \CWEB. provides mechanisms for the user to put +\.{CWEAVE} on the right track, and format definitions are one such mechanism +(others will be discussed below). + +Format definitions allow the programmer to explicitly state the syntactic +category that \.{CWEAVE} should attach to a given identifier. They have the +form `\.{@f x y}', which will become `\&{format}~$x$~$y$' in the typeset +output; here $x$ and $y$ can be arbitrary identifiers or keywords. This +definition has the effect of associating to~$x$ the same syntactic category +that is associated to~$y$. Such a change of category is required when an +identifier is defined as a macro to stand for a keyword: whenever you say +`\.{@d ident keyword}', say `\.{@f ident keyword}' as well. For instance, +the author of this manual thinks the keyword \&{static} is not very +informative when applied to functions, and therefore often creates an alias +for it by saying `\hbox{\&{\#define} \\{local} \&{static}}'; this directive +is then followed by `\hbox{\&{format} \\{local} \&{static}}'. We see that +the first identifier after \:d~or~\:f is always typeset in italics; this is +so despite the fact that in the example, as a consequence of the format +definition, this identifier will be typeset as \&{local} in all other +places. Another reason to change a category could be that an identifier is +in fact a typedef identifier, but \.{CWEAVE} cannot deduce this fact +(presumably the declaration occurs in some header file that is not scanned +by \.{CWEAVE}); in such cases one can use a standard defined type like +\&{FILE} or \&{size\_t} as the second argument to \:f. Finally, it is +possible that some \Cee~implementation uses additional, non-standard +keywords (or macros that behave as a keyword); such an identifier should be +formatted like a standard keyword that has a similar syntactic function as +it (which hopefully exists). In fact the identifier \&{va\_dcl}, which is +used in a convention for functions with variable argument lists that is not +part of \caps{ANSI/ISO}~\Cee, is nevertheless built into \.{CWEAVE}, because +there is no keyword that has the required syntax category (namely +\\{declaration}), so that it would otherwise not be possible to introduce +it; one can on the other hand easily undo the reservation by saying +`\&{format}~\\{va\_dcl}~$x$'. + +Format definitions can also be used for a reason that does not have to do +with syntax analysis. There are two classes of identifiers that are parsed +like ordinary identifiers, but are nevertheless treated specially; these +classes consist initially of the identifiers \\{TeX} respectively \\{NULL}. +The main distinction of these classes is that their identifiers are typeset +differently, namely as \TeX~macros; the mentioned identifiers will therefore +be written to the \TeX~file as `\.{\\TeX}' respectively `\.{\\NULL}', which +causes them to be typeset as `\TeX' respectively `$\NULL$'. This mechanism +gives the user the ability to change the appearance of identifiers in any +desired way, simply by defining the macro appropriately. The class +of~\\{TeX} is intended for identifiers that are still alphabetic in +appearance (possibly with letters being accented or shifted), while the +class of~\\{NULL} is intended for identifiers that are represented by +mathematical symbols. Hence the \TeX~macro will be processed in +horizontal mode with italic font selected in the first case, and in math mode +in the second case. Simply saying `\.{@f alpha NULL}' suffices to make +\\{alpha} print as~$\alpha$; the format definition is typeset as +`\hbox{\&{format} \\{alpha} $\NULL$\quad($\alpha$)}' to make the +correspondence of the identifier and typeset symbol evident. + +Unlike \Cee~identifiers, \TeX~macros cannot contain underscores and digits. +On writing of the macros to the \TeX~file, underscores are replaced by~`\.x', +so that they will become part of the macro. Digits however are not changed, +so identifiers containing digits should not be put into the class of~\\{TeX} +or~\\{NULL} by a format definition, unless special care is taken: the macro +will only consist of the part up to the first digit. No index entries for +identifiers of the class of~\\{NULL} are recorded (the same holds for +keywords); on the other hand index entries for typedef identifiers are +recorded, despite the fact that they are formatted as keywords. + +\subsection Text within \Cee~program fragments: comments and module names + +Within the program part of a section, the input should basically follow the +rules of the \Cee~syntax, but amidst the \Cee~tokens there may also occur +module names and comments. In both cases the \Cee~code is temporarily +interrupted by a piece of ordinary text that is processed directly by~\TeX, +just like the \TeX~part of a section. In the case of module names this text +is delimited by \:<~and~\:>, in the case of comments by +`\.{/*}'~and~`\.{*/}'. So comments are actually valid \Cee~comments, but the +converse is not true: the contents of a comment is processed by~\TeX, so not +all \Cee~comments can be used without modification; a point to keep in mind +if one is converting ordinary \Cee~code to \CWEB.. Like \Cee~comments, the +comments of \CWEB. cannot contain the two-character sequence `\.{*/}' +(regardless of the \TeX~context, because comments are recognised before +\TeX\ even gets to see them). The sequence `\.{/*}' is forbidden as well, +which allows \.{CTANGLE} to warn the programmer about unclosed comments, +that might otherwise lead to particularly elusive errors. In the \TeX~texts +of comments and module names no control codes are allowed (except in +embedded pieces of \Cee~code, described below), but \:@ can be used to +represent the character `\.@' (this is true in all contexts); a module name +is terminated by the first occurrence of the code \:>. During the processing +of these \TeX~texts, line ends are replaced by spaces, which implies that +\TeX~comments (starting with `\.\%') cannot be used. (In the \TeX~part of a +section on the other hand, such comments can safely be used: they are +completely ignored by \.{CWEAVE}, and not even copied to the \TeX~file.) + +The text for module names serves a dual purpose: apart from determining the +text representing the module in the printed output, it also serves to +identify defining occurrences of a module name with references to it. For +the latter purpose it is irrelevant how the contents of a module name will +be further processed; there should basically be a character-by-character +match. This rule is however alleviated in two ways to make matching easier. +First, any amount of consecutive white space is replaced by a single space, +and white space at either end of a module name is discarded. Second, an +abbreviation mechanism for module names may be used. A module name may be +specified by a prefix of the full name, followed by~`\.{...}'. A few +conditions must be satisfied to allow this mechanism to work. All +specifications of one same module name must be extensions of the one among +them of minimal length, which must not be a prefix of any other (full) +module name. All specifications of the name that do not end with~`\.{...}' +must be equal; there must be at least one such specification, which defines +the full module name of which all other specifications give a prefix. +Loosely speaking, the minimal specification is used for identification +purposes, and the maximal specification is used for typesetting all +occurrences. With the help of these rules, and a text editor, there should +be little reason to choose module names any shorter than what is needed to +express the function of a module clearly. There is a limit on the length of +a module name, but it is so generous that this could hardly be a problem: +1000~characters after replacement of consecutive white space characters by +single spaces. + +The parser of \.{CWEAVE} normally assumes that references to modules stand +for (compound) statements, which is likely to be a valid assumption in the +majority of the cases (or at least one that does not upset parsing, for +instance when the module is actually a statement sequence). Occasionally +however, one of two other syntactic categories applies instead, namely +\\{declaration} or \\{expression} (the remaining categories are extremely +unlikely). When this is the case, the programmer should make it clear to +\.{CWEAVE}, lest the parser might choke on the input and produce badly +formatted output. This can be done by placing the control code \:; once (for +a \\{declaration}) respectively twice (for an \\{expression}) directly after +the module name (in the latter case this also conveniently provides a +separation from any `\.=' or `\.{+=}' that might follow). + +At the end of the \CWEB. document, after the index, a list will be placed of +all module names used. This list sorted lexicographically, with sorting +based on the source strings for the full module names, collated (unlike the +identifier index) in the order of the internal (\ASCII.) character codes. +For this reason it is a good convention to ensure that all module names are +already distinguished by a prefix consisting of alphabetic characters and +spaces only, of which the first word is capitalised; then the order of the +list will be natural and independent of any internal details that the reader +is not aware of. + +\subsection \Cee~code within text: `\pb' fragments + +In order to mention a piece of \Cee~code within \TeX~text, it can simply be +enclosed in vertical bar characters (`\.|'); then \.{CWEAVE} will format it +in a way similar to to \Cee~code of modules. This feature may be used in any +kind of \TeX~text except in limbo, i.e., in the ordinary \TeX~part of a +section, in comments and in module names. The piece of \Cee~code itself +should not contain any comment. + +The ``lightweight'' construction with vertical bars resembles the math shift +characters (`\.\$') for \TeX's math mode, and indeed in simple cases like +`\.{|a[i+3]|}' the output would be identical if the `\.|' characters were +replaced by `\.\$'. The two modes should not be confused however: the +``\Cee~mode'' is implemented by \.{CWEAVE}, which translates the +\Cee~constructs before \TeX\ ever gets to see them; it often uses math mode +itself, and as a consequence it should never be used when \TeX\ is already +in math mode. The syntax used by \.{CWEAVE} is of a stricter kind than that +of \TeX's math mode, but it can still be used for some expressions that are +not quite proper~\Cee; in particular there is no objection to writing things +like $\\{begin}\Z p<\\{end}$, which humans understand better than compilers. +On the other hand an incomplete formula like `$\leq n$' (which can be used in +sentences, with the missing operand expressed in words) is better written as +`\.{\$\\leq n\$}' than as `\.{|<=n|}': the latter is not understood by +\.{CWEAVE}'s parser, and therefore the `\.{<=}' and the `\.n' are translated +separately with an ordinary space in between; the result looks reasonable, +but \TeX\ may very well decide to break the line at the space. + +There is a lexical price to pay for using delimiters that are not control +codes: it is impossible to use character `\.|' in any piece of \TeX~text +where `\pb' constructions are allowed (even if one tries for instance to set +up a verbatim context, because \.{CWEAVE} acts before \TeX\ does). This +should not cause great problems however, since `\.|' is not a character in +ordinary text fonts, and for `\.|'~and~`\.{\\|}' in math mode, plain \TeX\ +already has the substitutes `\.{\\vert}' and~`\.{\\Vert}'; for exceptional +text fonts (like typewriter type) that do have `\.|', the standard format +for \CWEB. provides `\.{\\v}' as a substitute (by means of \.{\\chardef}) +for~`\.|'. Inside `\pb' one has a similar problem of not being able to write +the bitwise-or operator~`$\OR$' in the usual way. For this purpose \CWEB. +provides the control code~\:v to represent that operator (which you may also +use in an actual program fragment, although there is no need to do so +there). Note that the composite operators `\.{|=}'~and~`\.{||}' can be used +without problem; consequently no `\pb' should be immediately followed +by~`\.=' or by another `\pb'. + +Although \Cee~comments are forbidden inside `\pb', it is possible to mention +a module in \TeX~text by enclosing the module name in vertical bars; this +\TeX~text can either be the \TeX~part of a section or a comment, but not +another module name. Mentioning a module in this way does not imply any +inclusion of the module body, so it is not considered to be a use of the +module; in the cross-references it is referred to as a ``citation'' of the +module. For the module name itself the same rules apply as for other +occurrences of module names; in particular the abbreviation mechanism can be +used, and \.{CWEAVE} will automatically insert the relevant section number +in the module name. Citing a module may form an exception to the rule that an +occurrence of a module name when the \Cee~part of a section has not already +started must be a defining one. Since \.{CTANGLE} normally ignores the +vertical bars of `\pb' constructions together with the surrounding +\TeX~text, it needs a simple rule to decide whether a module is being cited +or defined. It does this by inspecting the next token (where a newline +counts as a token, but codes like \:; that are ignored by \.{CTANGLE} are +skipped): if this is `\.=' (or `\.{+=}' etc.), then it assumes that the +module is being defined, and if it is `\.|' that the module is being cited; +in other cases it signals an error (this could for instance happen if a \:c +code is missing). Therefore it is not really necessary that the module name +is the only item in the `\pb' construction, as long as it is the final item; +this extra freedom is not likely to be of much practical use, however. + +\subsection Modules producing additional output files: `\/\.{@( {\dots} @>}' + +As was mentioned before, there are special module names that will cause the +program produced by that module to be written to a separate output file. +Such a module name is specified by enclosing the file name in \:(~and~\:>; +in fact it is sufficient to use \:( instead of~\:< in just one occurrence of +the module name. The file name will be set in typewriter type by \.{CWEAVE}, +so that the difference with an ordinary module name is easily perceived. +Although hardly relevant for this case, the compression of white space and +the abbreviation mechanism for module names also applies to these special +module names. The file name can contain any special characters, including +`\.|' and `\.@'; the latter must as always be doubled. + +\subsection Control codes that help parsing in special situations: + \:;, \:[, \:] + +In the discussion of the \&{format} command we already mentioned the way +\.{CWEAVE} parses and formats program fragments, and the fact that some +programming constructions can confuse the parser, leading to badly formatted +output. Like \:f, the control codes in this subsection provide ways to avoid +such problems, but they do so on a local basis in the code itself, rather +than by global definitions. They are mainly used in connection with macros +with replacement texts and/or arguments that are not expressions. Since +macro invocations look like identifiers or function calls, and macro +arguments appear to be function arguments, a piece of code containing a +macro invocation whose replacement text and arguments are not all +expressions may seem syntactically incorrect when not expanded. An example +of such a scenario is a macro whose replacement text is a compound +statement; an invocation of such a macro needs no semicolon following it, +and sometimes placing a semicolon would actually cause an error (e.g., if +the invocation is used as the first branch of an \&{if}-\&{else} statement, +since the semicolon would be taken to be an empty statement {\it after\/} +the conditional statement, and the \&{else} would be unmatched). Since the +parser of \.{CWEAVE} does not expand macros, it will fail to recognise a +macro invocation without a following semicolon as a statement, and like many +parsers it is not good at recovering from such a failure. Although no error +message is usually issued, formatting can be severely disrupted; indeed, +correct formatting will only be inserted locally for constructions that do +not contain the ``error'', so one unrecognised construction can easily +destroy the layout of the entire program fragment it occurs in. + +\.{CWEAVE} provides some simple mechanisms for guiding the parser through +such unusual code, and by applying them in several ways nearly all problems +that arise in practice can be solved. One of these is the control code~\:;, +which produces no \Cee~code (nor any printed output), but which can be used +in places where the \.{CWEAVE} parser would require a semicolon for a +successful parse; another is the combination \:[,~\dots,~\:], used as +a pair of parentheses, which will cause whatever is enclosed to get the +syntactic category `expression', regardless of its actual category. + +The most obvious use of~\:;\ is in the case already mentioned of a macro +invocation that expands to a (compound) statement: placing \:; after such a +macro invocation will cause it to be recognised as a statement by \.{CWEAVE}, +keeping its parser happy while not affecting the actual \Cee~program. There +are other situations as well where one does not want to place a semicolon, +yet wishes \.{CWEAVE} to act as if it were there. If a macro stands for +statement that happens to end in a semicolon, then it is a good idea to +suppress the final semicolon in the definition: in that case all invocations +can supply the semicolon, and one does not have to remember writing \:; +instead of~`\.;' at the invocations of this macro. For instance, the macro +replacement text could be `\&{do}~\<statement>~\&{while}~(\<condition>)', or +`\&{if}~(\<condition>)~\<statement>~\&{else}~\<expression>', or even +`\&{if}~(\<condition>)~\<statement>~\&{else}', where the final \&{else} was +placed with the purpose of picking up the following semicolon as an empty +statement; in all these cases the macro invocation together with the +following semicolon is a complete statement that can be used without special +precaution, even as the first branch of an \&{if}-\&{else} statement. +However, in these cases the macro definition itself needs a bit of extra +care: a~\:; should be placed at the end to represent the semicolon that will +follow in invocations, so that \.{CWEAVE} can properly format the +replacement text of the macro. Finally, it there can be purely aesthetic +reasons for wanting to suppress a semicolon at the end of a `\pb' +construction, for instance when referring to a declaration as +`$\&{char}~\m*p$', which strictly speaking requires a final semicolon to +become a declaration; to let \.{CWEAVE} format this properly, one should +write `\.{\v char *p @;\v}'. Constructions like `$\&{return}~\\{home}$' and +`$\&{goto}~\\{sleep}$', which are fairly common to mention in module names, +would also fall into this category, but in this particular case no \:; is +necessary, since \.{CWEAVE} parses these as expressions, even though strictly +speaking they are not. + +Since \:; is invisible in the output, yet can be sensed by the parser, it +can conveniently be used to pass information to the parser, and there are a +few instances of such use where it does not stand for a semicolon. We +already mentioned placing one or two copies of \:; after a module name to +indicate the syntactic category. Another use is to place it before a typedef +identifier to cause it to be treated as an ordinary identifier; this is +useful if the identifier is locally redeclared, or used as field selector in +a \&{struct} or \&{union} specifier. When the identifier is used as a tag +immediately after \&{struct} or \&{union}, or as a selector after +`$.$'~or~`$\MG$', it is not necessary to place \:; before it. + +Unlike \:;, the control codes \:[~and~\:] themselves do not participate in +parsing. The material between them is parsed normally, which may or may not +succeed in recognising a single construct; then the pieces recognised are +concatenated (without separation), and the result is given the category +\\{expression} for the purpose of parsing further items outside. The most +obvious use of this mechanism is to encapsulate any arguments in a macro +invocation that are not expressions (e.g., some storage allocation macros +have a type as argument), so that the invocation can be parsed as a function +call. There need not be anything in between \:[~and~\:], so `\hbox{\.{@[ +@]}}' can be used as an ``invisible expression'' in the same way as \:; can +be used an invisible semicolon. An example where this is useful, is a module +standing for an initialiser list, that is moreover defined in multiple +sections (see for instance the module `$\X157:Rules\X$' in the source +document for \.{CWEAVE}): it is natural to end each program fragment +defining a part of such a module with a comma, but this will not be parsed +properly unless an expression follows, which can be achieved by adding +`\hbox{\.{@[ @]}}'. Finally, if for some tricky piece of code none of the +mentioned methods suffice to get it parsed properly by \.{CWEAVE}, one may +use \:[~and~\:] (followed by \:; if necessary) to minimise the damage: by +placing \:[~and~\:] around an appropriate part of the program containing of +the problem area, we can ignore the fact that the parser failed to recognise +it, and force it to continue as if it ad recognised an expression; thus we +can contain the problem, and prevent the effects from spreading any further. + + +\beginsection Invocation of {\tentex CTANGLE} and {\tentex CWEAVE} + +The simplest form of calling \.{CTANGLE} and \.{CWEAVE} is to supply one +command line argument, which is the name of the \CWEB. source file without +the `\.{.w}' suffix. It is possible however to modify the behaviour of the +programs by selecting certain optional settings, and small patches to the +master source file can be achieved by supplying a ``change file''. The +general syntax for invoking \.{CTANGLE} is +$$ \hbox +{ \.{ctangle}\quad + [$(\.+\mid\.-)$\<options>]\quad + \<\CWEB. file>[\.{.w}]\quad + [$(\<change file>[\.{.ch}]\mid\.+\mid\.-)$ \quad + [\<output file>[\.{.c}]]] +} +$$ +where square brackets indicate optionality, vertical bars separate +alternatives, and parentheses are used for grouping. Here \<options> is a +string of one or more characters designating options, as described below; +there may be more than one such string of options, and they may be given +between or after the files names instead of before them, with no difference +in meaning. For \.{CWEAVE} the situation is entirely similar, except that the +default extension for the output file is `\.{.tex}' instead of `\.{.c}'. + +\subsection Command line options + +A command parameter that starts with `\.+' or `\.-' and has at least one +more character, serves to control optional settings of the program being +invoked. The characters after the initial character `\.+'~or~`\.-' denote +individual options that are turned on respectively off; option characters are +case-insensitive. The character `\.i' forms an exception, since it is used +to supply a string argument rather than to set a switch; the string is the +remainder of the option string (following the `\.i'), and +`\.{+i}'~and~`\.{-i}' are equivalent. All option characters will be +accepted, but only the ones listed below have any effect on the operation of +the program. We list the switches in the direction that alters the default +setting. +$$\vcenter{\tabskip=1em \halign{\tentex#\hfil & #\hfil & #\hfil \cr +\omit\it switch& \it program& \it effect \cr +\noalign{\yskip} +-b & both & do not write a banner line to the terminal \cr +-p & both & do not show a progress report on the terminal \cr +-h & both & omit confirmation of successful completion \cr +\noalign{\yskip} +-l &\.{CTANGLE} & omit \&{\#line} directives, make \Cee~file look nice \cr +\noalign{\yskip} +-x & \.{CWEAVE} & do not attach index and other information + at the end of the document \cr ++d & \.{CWEAVE} & report failure to completely parse pieces of \Cee~code \cr ++t & \.{CWEAVE} & write three files, + with separate ones for index and list of module names \cr ++e & \.{CWEAVE} & even out number of pages before table of contents \cr +\noalign{\yskip} ++i & \.{CWEAVE} & add alternative search path for header files + (takes argument) \cr +\noalign{\yskip} ++f & \.{CWEAVE} & force a line break after each statement \cr ++a & \.{CWEAVE} & force all statements to be on a line by themselves \cr ++u & \.{CWEAVE} & ``unaligned brace style'': do not align `$\{$' and `$\}$' + vertically \cr ++w & \.{CWEAVE} & ``wide brace style'': + force line breaks before and after `$\{$' \cr ++m & \.{CWEAVE} & ``merged declarations style'': + do not force line breaks between local declarations \cr +\noalign{\yskip} + +c & both & run in compatibility mode with \LKC. \cr + +s & both & show memory usage statistics at completion \cr + ++ & both & handle \Cpp~language instead of \Cee \cr +}} +$$ +The options `\.{+d}'~and~`\.{+s}' only operate if \.{CWEAVE} or \.{CTANGLE} +was compiled with the preprocessor symbol \.{DEBUG} respectively \.{STAT} +defined (with most \Cee~compilers this can be accomplished by including a +command line parameter \.{-DDEBUG} respectively \.{-DSTAT} when compiling +the \CWEB. system). + +The options `\.b', `\.h', and `\.p' can be used to control the amount of +output that \CWEB. writes to the user terminal; the combination `\.{-bph}' +will eliminate terminal output altogether when no errors are encountered. + +The option `\.{-l}' of \.{CTANGLE} is intended either for use with broken +compilers or debuggers that cannot handle \&{\#line} directives properly, +or for cases where the \Cee~file is of more importance than just as an +intermediate file, for instance when the program is transferred to +people who do not wish to practice \lp.. Apart from omitting \&{\#line} +directives and comments that indicate the section number from which code +originates, an attempt is made to make the \Cee~file more readable to humans: +the spacing and (almost all) comments of the source file are preserved in +the \Cee~output, and when modules are substituted into others, indentation +levels are accumulated, so as to produce indentation that looks natural. +Doubtlessly the result is not perfect (and lines may get quite long), but it +is definitely more readable than the output normally produced. Since layout +and comments of the source file need to be preserved by \.{CTANGLE}, this +option consumes significantly more memory than its contrary. + +The option `\.{+d}' causes \.{CWEAVE} to issue a warning when it +could not properly parse some piece of \Cee~code; this could happen either +because a code fragment is incomplete in the sense that it does not +represent a single complete syntactic entity (as in the `\.{|<=n|}' example +above, or when a module body ends with a label without a following +statement), or because the code is actually unsyntactic, or because +\.{CWEAVE} has been fooled by an unusual construction. In all cases however +the result can be (very) badly formatted output, and a correction should be +made; users who care about the quality of the typeset output are advised to +always set this option (or at least when the document is being finalised). +Setting the `\.{+d}' switch is equivalent to placing a control code \:1 at +the beginning of the first section; the nature of the warning messages and +possible remedies will be discussed later in this manual. + +The two output files that the option `\.{+t}' will cause \.{CWEAVE} to create +in addition to its main output file, are called \.{\<name>.idx} and +\.{\<name>.scn}, where \<name> is the name of the main output file without +its extension. These files will be read by `\.{\\input}' commands in the +main output file, so that the typeset document will not be any different; on +large projects however it can be helpful to have this information on +separate files, for instance for making a global index. The option `\.{+e}' +is intended for use with two-sided printers: it ensures that the table of +contents comes out on a fresh sheet of paper, so that it can conveniently be +moved to the front. + +The option `\.{+i}' (or equivalently `\.{-i}') can be used to specify a +directory for \.{CWEAVE} to search for header files in \:h commands. +Although directory structures are system-dependent, \CWEB. assumes that a +file can be looked up in a specified directory by prefixing a string +indicating that directory to the file name (this works for many systems); +the desired prefix string should then be supplied as the remainder of the +option string after the `\.i' character. E.g., on the \caps{UNIX} system the +author uses, \.{CWEAVE} can be told about the location of the `Xlib' header +files by supplying an argument `\.{+i/usr/local/X11R5/include/}' (one could +replace `\.{+i}' by `\.{-I}' to make it look more like the similar option +passed to the \Cee~compiler); the important thing to note is the final +pathname separator `\./'. Up to~8 additional prefixes can be specified by +giving several such arguments; they will be tried in order from left to +right. It is also possible to fix one such prefix at compile time, by +defining the preprocessor symbol `\.{CWEBHEADERS}' to be the desired prefix +string when compiling the compilation unit \.{common.c} of \CWEB.; this will +behave as if it were the first prefix specified by a `\.{+i}'~argument. + +The last five options mentioned will alter layout style of program fragments. +The option `\.{+f}' will result in a more vertical style than the default, +and `\.{+a}' will do so even more; the difference between them is that +`\.{+f}' will not force a simple statement to start on a new line if it +follows a label or the condition of an \&{if} or \&{while} statement, +whereas `\.{+a}' will start a new line in such cases. The option `\.{+u}' +selects a style in which corresponding opening and closing braces are +unaligned because a line break is inserted after~`$\{$' instead of before~it. +The option `\.{+w}' on the other hand selects a brace style that has more +vertical symmetry than the default one, since opening braces will appear on +a line by themselves, like closing braces; the price is that listings will +consume more paper. The option `\.{+a}' overrides `\.{+f}', and similarly +`\.{+w}' overrides `\.{+u}'. Finally, the option `\.{+m}' is for people +(like the author) who are extremely keen on saving paper: it avoids forced +line breaks between the declarations in a compound statement, just like they +are not placed by default between the statements; the separation between +declarations and statements within a compound statement is still indicated by +a line break, that even has some extra vertical space, because this +separation is significant in the \Cee~syntax (unlike the \Cpp~syntax). + +In compatibility mode, specified by `\.{+c}', both \.{CTANGLE} and +\.{CWEAVE} modify their behaviour in such a way that they try to ensure that +they can handle any file that can be correctly processed by \LKC., and that +the output is an equivalent \Cee~program, respectively a valid \TeX~file +(this is the hard part) that produces a comparable printed document. In the +current version this claim can only be made for programs written in~\Cee; a +wholehearted attempt to do the same for \Cpp~programs would cost a +substantial amount of extra work. There are so many differences in the +details of formatting between \CWEBx. and \LKC. that one cannot expect +formatted output that is identical to what would be produced under \LKC., +but to get the best approximation, one should in addition to~`\.{+c}' +specify the options~`\.{+uft}'. + +The option `\.{+s}' is included because the \CWEB. utilities use statically +allocated memory areas, which may therefore run out; using this option one +can see how close one is to the limits of \CWEB.. The most important limited +resources that it provides information about are are: \ (a)~The name tables +in which \.{CTANGLE} and \.{CWEAVE} store all distinct identifiers and index +entires, respectively module names (the entries `identifiers', `module +names', and `bytes'); \ (b)~\.{CTANGLE}'s main memory, in which the complete +\Cee~program file processed during a single run has to be stored, albeit in a +compactified form (`replacement texts' and~`tokens'); \ (c)~\.{CWEAVE}'s +cross-reference memory, in which all the data for the index and list of +module names are stored (`cross-references'); \ (d)~its parsing buffers, +which must be able to hold any one program fragment or piece of +\Cee~code (`scraps', `texts', and~`tokens'). There should be no immediate +need to increase the size of these memory areas, since even for the main +program of \.{CWEAVE}, the largest of \CWEB.'s own compilation units, the +use of any of these resources is less than a third of the amount available. +There is one resource of which a larger fraction is used, namely `trie +nodes', but its usage depends only on the set of grammar rules used, which +is independent of the particular \CWEB. source file. When for some source +file \CWEB. is approaching its limits, one can of course try to recompile +\CWEB. with larger arrays, but alternatively one may restructure the source +file: when one of (a),~(b), or~(c) runs out, one might consider breaking up +the file into several separately processed pieces; when (d) runs out, a +remedy could be splitting up some huge module body into smaller ones, by +introducing submodules or multiple definitions of the module. + +Switching to the \Cpp~language has only a minor influence on the operation +of \CWEB.: one-line comments starting with `\.{//}' will be recognised, the +main output file produced by \.{CTANGLE} will have default extension `\.{.C}' +instead of~`\.{.c}', and \.{CWEAVE} will recognise a few more reserved words +and use a slightly different syntax. Since there is no general agreement +about the proper extension for \Cpp~files, and alternative default extension +for \Cpp~mode (instead of \.{"C"}) may be built in by setting the +preprocessor symbol \.{CPPEXT} to the desired string (that should not +contain the leading period) when compiling \.{common.c}. Currently the +\.{CWEAVE} grammar will handle only a basic subset of the \Cpp~language, +which does not include templates or exception handling. + +\subsection File name arguments + +Any command line arguments that do not have the form of an option are taken to +indicate file names; their number can vary from 1~to~3. The first one specifies +the main source file, the second (if present) indicates the change file, and +the third optionally defines a non-standard name for the main output file. +The contents and function of the change file is discussed in the next +section; here we we just indicate how the actual file names used are derived +from the given file name arguments. As far as \CWEB. is concerned a file +name is composed of a base name and an extension. Loosely speaking, the +extension of the main file defaults to~`\.{w}', that of the change file +to~`\.{ch}', and that of the output files to `\.{c}' or `\.{tex}' for +\.{CTANGLE} respectively \.{CWEAVE} (but see also the discussion of the +`\.{++}' option above); the base names of the change file and the main +output file default to that of the main file. If in place of a change file +name an argument `\.-' is specified, no change file is used; also if only +one file name argument was given, or if the change file name was specified +as `\.+', then the default change file name is tried, but if no such file +exists, processing proceeds without a change file. (Specifying the change +file as `\.+' is only useful if a third file name argument is given.) +Therefore, assuming regular naming conventions, there is no need to specify +more than the main file name without extension, whether or not a change file +is being used. + +The precise rules are as follows. On file systems where an extension is not +a standard property of file names, like that of \caps{UNIX}, it is assumed +the a period is a valid character in file names; a full file name is then +formed by concatenation of the base name, a period and the extension (note +that this implies that on such systems \CWEB. cannot access files whose name +contains no period at all). Conversely, a string designating a full file +name is broken up into a base name and an extension at the last occurrence +of a period; if no period is present, then the string is taken to specify a +base name only, and is said to have no extension. If the first file name +argument has an extension, it specifies both base name and extension of the +main source file, otherwise it specifies the base name, and the extension is +taken to be~`\.w' (if no such file is found, the extension `\.{web}' is also +tried, but this feature is obsolete). The base name of the main source file +is also the default base name of the change file and the main output file; +their default extensions are as described above. If a second and possibly +third file name argument is present and is not `\.+'~or~`\.-', it overrides +the base name, and also the extension if it has one, of the change file +respectively of the main output file. No change file will be used either if +the second file name argument is `\.-', or if no change file is found when the +second file name argument is `\.+' or absent. + + +\beginsection Subsidiary input files and change files + +As we have described it so far, the \CWEB. tools read a single source file, +from which a main output file and possibly some auxiliary output files are +produced. Since \Cee~programs can be built from several compilation units, +it is not uncommon that several \CWEB. source files contribute independently +to the same program, and there might be non-\CWEB. source files as well. +However, even what is conceptually a single \CWEB. source, described by a +single printed document, may in fact be composed from several input files. +Two mechanisms are provided for combining information from several files, +with different purposes. First, subsidiary files may be read in from the +main source file in a way similar to the way \&{\#include} files are handled +by a \Cee~compiler. In the case of \CWEB. however, the main purpose is +usually not to share information among several sources, but merely to allow +breaking up large source files into more easily manageable parts. Second +there is the change file mechanism already mentioned above, which serves to +install system dependent patches to a master source, allowing that master to +remain free of system dependencies. + +When a line of the form `\.{@i}~\<file name>' appears in a \CWEB. source +file, \CWEB. will read in the indicated file at that point, and continue +reading at the next line when it reaches the end of the subsidiary file. The +\<file name> may either be delimited by white space, or be enclosed in +double-quote characters (but not in angle brackets). Source files may be +nested in this way up to 10~levels deep. Nothing in the printed \CWEB. +document will indicate the switch from one source file to another, nor will +there be any effect on the \Cee~file(s) written by \.{CTANGLE}, except that +\&{\#line} directives will of course always point to the proper point of +origin for each piece of code written to such files. + +Like for header files, there is a way to indicate that if a file included by +\:i is not found in the current directory, an alternative place can be +tried; unlike header files however there is relatively little need to use +this facility, unless one has files that are useful to include identically +in more than one project. At most one alternative place to search can be +given, and it is specified by a prefix to be applied to the file name, in +the same way as for header files. This prefix may either be compiled into +the \CWEB. programs by setting the preprocessor symbol \.{CWEBINPUTS} equal +to that string when compiling \.{common.c} (analogously to \.{CWEBHEADERS}), +or it can be specified at run time by setting the environment variable +\.{CWEBINPUTS}; when both methods are used, the latter takes precedence. + +The change file, if present, contains a sequence of ``changes'', each of +which specifies the replacement of one or more lines from the main input +stream by another set of lines. Each change has the form `\.{@x} \<original +lines> \.{@y} \<replacement lines> \.{@z}', where each of the codes \:x, +\:y, and~\:z occupies a line by itself. The \<original lines> is a non-empty +set of lines that should match exactly with some sequence of lines in the +main input stream (except for the fact that trailing white space on any line +is ignored). Furthermore, different changes should affect non-overlapping +sets of lines, and their order in the change file should be the same as that +of the parts of the main input stream that they replace. For each change in +succession, a sequence of lines matching \<original lines> is searched for, +and replaced by the corresponding \<replacement lines>; like for \:i file +insertions, the resulting stream of lines will be processed in the usual way +as if it constituted a single \CWEB. source file. The ``main input stream'' +referred to here is the result of (recursively) inserting any auxiliary +files indicated by \:i lines into the main \CWEB. source file. It therefore +makes no sense to specify \:i in the \<original lines>, nor is \:i allowed +in the \<replacement lines>: it should simply not occur anywhere in the +change file. On the other hand it is legitimate for the \<original lines> to +match a sequence of lines coming from more than one physical source file. + +The fact that input is temporarily switched to the change file is not +entirely transparent to the \CWEB. document, as it was in the case if \:i +files: \.{CWEAVE} will mark all sections that were modified under control of +the change file, by attaching an asterisk to their section number, and to all +references to that number. (If some changes should add or remove entire +sections in the middle of the \CWEB. source, which is allowed although not +encouraged, then the section numbering will be altered, but sections for +which this is the only change will not be flagged with an asterisk.) If one +is only interested in sections that are modified, then it is even possible +to restrict printing to only those sections, by including the \TeX~command +`\.{\\changesonly}' in the text in limbo, preferably by means of the change +file. + +In order to facilitate efficient implementation of the change file +mechanism, an additional constraint is placed on the changes: once an exact +match of a line in the main input stream with the first line of a change is +found, the remaining lines of the change (up to the \:y) should also match. +Any empty lines immediately following \:x are not used for matching (and are +in fact completely ignored) so the first matching line is never an empty +one; it is preferable to choose changes such that their first line matches a +unique line of the main input. It is a good idea to start changes in the +\TeX~part of sections (after all, if the program changes, so should its +explanation); in this case uniqueness of the match of the first change line +can always be ensured (even when the \TeX~part is empty) by placing a +\TeX~comment in the main input, that serves merely as a target for +replacement by the change file. All text in the change file that is not +part of a change is ignored, except that there should be no lines starting +with \:i, \:y, or~\:z; this text can be used for instance to explain the +purpose of the change to the person installing the program on a new system, +rather than to the ordinary reader of the program. + +As we have said earlier, the change file mechanism provides an alternative +to system dependent conditional compilation, and it is usually a much more +elegant way to incorporate system dependencies. The main reason for this is +that one does not have to anticipate all possible systems that a program +could be ported to, nor is the main source polluted by such considerations: +it suffices to provide a separate change file each time the program is moved +to a system with different system dependent requirements. Users of a +particular system need to know about the change file for that system only, +and the responsibility for maintaining main source and the change file might +lie with different persons; additional effort is only required when the main +source changes in such a way that a change file fails to match. + +One should not get carried away by the benefits of change files though: they +provide only a rather crude mechanism (due to the inflexible matching +rules), and if there are many changes, they will become difficult to +maintain when the master file evolves. Portability is still best obtained by +limiting system dependent features as much as possible, and if inevitable, +confining them to some well defined part of the program. If one should wish +to create variants of a program that involve significant changes, then +writing extensive change files is probably not the best way to go. This +method could lead to a form of ``rigor mortis'' for the original version of +the program, caused by fear that any alterations could upset one of the +change files, even trivial changes that only involve the commentary, or even +just the layout of the source file. A better approach would be to collect +routines of general utility as much as possible into separate compilation +units used by all variants, and to complement these with completely +independent compilation units to define the specific behaviour of each of +the variants. It is certainly pointless to use a change file for such things +as bug fixes or further development of a program; the whole idea is that +such modifications can be made in the master file while the change files for +various systems need little or no adjustment. + +The codes \:i, \:x, \:y, and~\:z of this section have the appearance of +control codes, but they are not really part of the \CWEB. language, and +obey different rules than control codes. For instance, they are line +oriented (and rightly so, since their goal is to select which lines will be +actually processed by \CWEB.): they should appear at the beginning of a +line, and any further text on the line (in case of \:i, after the file name) +is ignored. Also they act quite independently of \CWEB.'s current mode of +operation: rules such as the one forbidding control codes in limbo do not +apply to these codes. + + +\beginsection Control codes for advanced or emergency use + +In this section we discuss control codes that are not essential for everyday +use of \CWEB., but are provided to enable either refinements in the +presentation of the \CWEB. document, or special manoeuvres to deal with +certain unusual situations or requirements. Most of them serve to allow the +programmer some form direct control over the contents of either the \CWEB. +document, the \Cee~file, or the source file, bypassing the automatic +processing by which these are normally related to each other; there are also +a few that serve as debugging aid, eliciting explicit information from the +\.{CWEAVE} parser about its actions. + +\subsection Control codes for cross-referencing: + \:!, \:\^, \:., \:?, \::, \:\# + +Some control codes are provided that allow the programmer to influence +indexing and to perform explicit cross-referencing. The codes in this +subsection are the only ones that are allowed to occur in the \TeX~part of +sections, outside `\pb'; with the exception of~\:\#, they can also be used +in \Cee~text. Control codes such as these, that are intended only to affect +the printed document, are ignored completely by \.{CTANGLE}. Incidentally, +cross-referencing in \CWEB. always means referring to section numbers rather +than to page numbers: \.{CWEAVE} cannot know about page numbers since these +are determined only at the \TeX\ processing stage. It would be possible to +have \TeX\ produce a table mapping section numbers to page numbers; in fact +the table of contents provides a coarse approximation to such a map. + +Whenever \.{CWEAVE} can determine from the context that an occurrence of an +identifier is a defining one, it will make the corresponding section +reference in the index underlined. If some case is missed by \.{CWEAVE}'s +normal rules, or if one wants to make a reference to a reserved word (which +is only made if it is underlined), then one can place the code \:! in front +of the identifier to create an underlined reference. Cases where this may be +required include arguments of functions with an old-style (pre-\caps{ANSI}) +heading for which no declaration is given before the function body (i.e., +the default type \&{int} applies), and enumeration constants that appear out +of context of the \&{enum} keyword (e.g., because the enumeration list +is given as a separate module). In general, the occasions where one needs +\:! are quite rare. + +A group of three codes serves to include additional entries in the index, +amidst those generated automatically by \.{CWEAVE} for identifiers. It may +be useful for instance to maintain references to concepts like `system +dependencies', or to all error messages that can be generated. The three +codes are \:\^, \:., and~\:?; they differ only in the way the index entry +will be typeset. In each case the index entry is specified as a control text +terminated by \:>; control code and control text will be removed by +\.{CWEAVE}, but the control text will appear in the index, followed by the +section number(s) where the control code occurred. For \:\^,~\:., and~\:?, +the index entry will be set respectively in roman type, in typewriter type, +and as argument to the control sequence `\.{\\9}' (which is undefined in the +standard format, but which the programmer may define in limbo). The first +possibility is most suited for general concepts, the second for strings that +occur in the program, and the third for any further special purpose one may +think of. These control codes can be put either in the \TeX~part of a +section or within \Cee~code; the effect will be the same, but this allows +the programmer to put the control code in such a place that it is most +likely to remain in the right place in case the section should be +reorganised and possibly subdivided. Like for references to identifiers, one +can make an index reference underlined by prefixing the corresponding +control code with~\:!. + +Unlike the control text forming a module name, the control texts discussed +here (as well as those that have not been introduced yet) should be +contained in a single line of input; also, no spaces are contracted or +removed. The control texts are passed unchanged to \TeX\ (with only `\.{@@}' +being undoubled as usual), so that they can use \TeX~commands for special +effects. Inside `\.{@.\dots@>}' one can get the special characters occurring +in `\.{\#\$\%\^\&\{\}\~\_\\}' by prepending a backslash, `\.{\\v}' gives a +vertical bar~`\.\v', and `\.{\\ }' gives a visible space `\.\ '. + +The control texts are also used as a sort key to determine the place in the +index where the entry appears. Different occurrences of these control codes +are combined in the index only if there is an exact match of both control +code and control text, and no merging takes place with identifiers whose +name happens to be equal to the control text (however, their relative order +in the index is unpredictable). In sorting, a collating sequence is used +that differs from the standard \ASCII. order: alphanumeric characters appear +at the end of the sequence, with upper and lower case being considered +equivalent, and the space character appears at the beginning of the +sequence. In case there are entries that cannot be correctly positioned by +ordinary means, the following trick has been suggested by Knuth: define +`\hbox{\.{\\def\\9\#1\{\}}}' and represent the tricky entries as +`\.{@?\<sort key>\}\{\<\TeX~code>@>}', where \<sort key> contains +sufficiently many characters to uniquely determine the position of the entry +in the index, and \<\TeX\ code> produces the index entry itself; this works +because \.{CWEAVE} will write the index entry +`\.{\\9\{\<sort~key>\}\{\<\TeX~code>\}}', which ``expands'' to +`\.{\{\<\TeX~code>\}}'. + +Besides references from the index, \.{CWEAVE} provides cross-references, in +the form of the section numbers that link the (first) defining occurrence of +a module name with the places where it is used and cited. There is also a +mechanism for the user to explicitly state similar cross-references in the +\TeX~part of a section, so that it is possible make a reference to another +section (where some related matters are treated), that will remain correct +if sections are renumbered. The mechanism is simple: in the section referred +to, one places the control code~\::, followed by a control text serving as a +label, and at the place of reference one uses \:\#, followed by the +identical control text (both control texts are terminated by~\:>). The rules +for placing \::\ are the same as for \:\^ and its relatives, except that +\:!\ has no effect here; the control text will not appear in the index, and +there is no conflict when the same string is used as an identifier or index +entry. + +For~\:\# and its control text, \.{CWEAVE} basically substitutes the section +number of the matching \::~code, but because there might be multiple +occurrences of \:: with the same control text, the precise replacement rule +is a bit more complicated. The replacing text is precisely what would follow +``See also section'' in a cross-reference for a module name: one or more +section numbers in increasing order, separated by commas and ``and'' as +appropriate, and preceded by a space and, in case there is more than one +section number, by an `\.s' before that space. This is set up so that a +reference of the form `\.{section@\#label@>}' will generate a proper +reference, whether or not there are multiple definitions of the label. One +can also use `\.{\\Sec@\#label@>}' since in the standard format `\.{\\Sec}' +expands to `\Sec' and `\.{\\Secs}' to `\Secs' (in this case the space +produced by~\:\# is ignored after the \TeX\ control sequence); by defining +other \TeX~macros one could do anything one likes with the text provided +by~\:\#. Although \:\# cannot be used directly in comments and module names, +it is possible to capture its text in a macro definition (within a +\TeX~part) and use that macro instead. + +\subsection Control codes for layout in programs: + \:,, \:|, \:/, \:), \:\\, \:+, \:; + +As we mentioned before, \.{CWEAVE} formats the program fragments and pieces +of \Cee~code by inserting formatting controls in the the output based on a +syntactic analysis of the \Cee~tokens of the program fragments; in +particular the layout of the code in the source file is completely ignored. +Although this automatic formatting usually works well provided that +\.{CWEAVE} succeeds in parsing the program fragment (possibly with help of +some codes already discussed), there may still be occasions where one is not +quite satisfied by the result. If one wishes certain constructions to be +systematically treated in a different way, then a more pleasing style might +be available by calling \.{CWEAVE} with certain options set; if not, then +there is always the possibility of changing the grammar or layout rules of +\.{CWEAVE} (that program was written in a way that tries to make this as +easy as possible, but it still requires some careful study of the relevant +chapters of the \.{CWEAVE} source document). However in some cases one +simply wants to override the general rules in specific cases by adding or +removing a few formatting controls. There are a number of control codes +which can be used to do that. These codes are ignored by~\.{CTANGLE}; since +most of them deal with line breaks, their importance for `\pb' fragments is +minimal. + +The control code \:, will insert a thinspace (a small amount of horizontal +white space) where it is placed. Within an statement \:| may be used to +indicate a place where a line break may be optionally taken (with no +associated penalty), when the statement is too long to fit on a single line. +Note however that optional breaks are already allowed at most operator +symbols, with a penalty that increases with the operator priority and the +number of enclosing parentheses, so \CWEB. will almost always succeed in +finding very a reasonable break point in long expressions. A line break can +be forced by~\:/; this can be used for instance between statements (if line +breaks are not already forced there), in order to group related statements +on one line rather than simply as much as possible. The code \:) will also +force a line break, and in addition create a bit of vertical white space to +give an even more visible separation. (\.{CWEAVE} will never issue more than +one line break on the same place, so there is no problem if a line break was +already present on that spot.) The code \:\\ is another variation: it forces +a line break and backs up the next line by one indentation unit. It is +useful before a module name that represents one or more cases in a +\&{switch} statement: this will make the name line up with the case labels. + +Finally, \:+ cancels any (forced) line break that might be inserted by +\.{CWEAVE} at the point where it is placed, and replaces it by a space with +optional line break (the kind of space that is usually inserted between +statements). Its main use is to force small conditional or loop statements +onto a single line when \.{CWEAVE} would otherwise use a multiple-line +layout. Because the line can still be broken at the inserted space, such +one-liners do not make it impossible to retypeset the program in a narrower +column. A warning is in place however if, as a result of applying \:+, a +substantial stretch of \Cee~code is void of forced breaks, and that code +contains constructions that affect the indentation level. \TeX nically +speaking, the indentation at optional breaks is governed by the hanging +indentation parameter of \TeX, whose value is constant throughout a +paragraph, which in this case is everything between two forced breaks; under +the mentioned circumstances the amount of indentation at optional breaks can +be unexpected and inappropriate. + +For convenience, an alternative method is provided to fit compound +statements on a single line, and similarly for \&{struct} and \&{union} +specifiers. Instead of writing \:+ on every place where \.{CWEAVE} would +otherwise force a line break (which incidentally depends on the chosen +layout style), it suffices to place \:; immediately after the opening brace. +This will activate a different set of layout rules than is normally used, +which will not insert forced breaks between the declarations and statements +of the compound statement, respectively between the fields of the \&{struct} +or \&{union} specifier. In the case of a compound statement, any forced +breaks caused by conditional or loop statements appearing directly inside +the compound statement are also avoided (but nested statements are not +affected, so they should be handled separately if present, possibly using +another~\:;). Compound statements starting with `\.{\{@;}' will be treated +as if they were simple statements in further parsing, which may affect +formatting; for instance, if the statement is the branch of a conditional it +will be placed on the same line as the \&{if} or \&{else} controlling it. +If this is too much of a good thing, a forced break may be explicitly +inserted at the beginning and/or end of the compound statement; in fact the +sequence `\.{@/\{@;}' is a fairly common one. + +There is another use of~\:+, which does not cause any breaks to be +cancelled, but where on the contrary the purpose is insert white space. +It applies when a long string constant is needed, for which the +string-break feature is used: a sequence of strings separated by white space +only will be concatenated by the compiler into a single string. Although +\.{CTANGLE} will correctly insert a space between any two consecutive +strings, \.{CWEAVE} (guided by syntax rather than by lexical structure) will +simply juxtapose them; by inserting \:+ between the strings, one guarantees +that in the printed document there will either be a horizontal separation or +(if the constituent strings themselves are already long) a line break. +Incidentally, if the problem of breaking a string is in the source file +rather than in the printed output, one can use the traditional solution of +an escaped newline within the string; \.{CWEAVE} will treat this as if the +parts of the string were on the same source line. If one should create a +string in this way that does no fit on a single line of output, a break will +be introduced automatically at a some point, which will be typeset as if a +string-break was used. In very long strings however it is better to write +string-breaks explicitly; for strings broken only by escaped newlines, the +same length limit holds as for module names (1000~characters). + +\subsection Codes for special items in\/ \Cee~code: + \:p, \:v, \:t, \:\&, \:=, \:' + +Contrary to \TeX~text, pieces of \Cee~code are broken up into tokens by both +\.{CTANGLE} and \.{CWEAVE}, stored internally and output at some later time +after having undergone some processing. This makes it potentially difficult +to put something into \Cee~code that \CWEB. is not prepared to handle. Since +\Cee\ is a much more regular language that \TeX, occasions where one would +need to do such a thing should be quite rare, yet some escape mechanisms +have been provided, which we treat in this subsection. + +The code \:p can be used to explicitly specify the place where the +preprocessor directives generated by \:d and \:h commands will be placed in +the \Cee~file. Multiple use of \:p is allowed; as soon as it is used at +least once, the default placement at the beginning of the \Cee~file is +cancelled. This code provides the only way to write the directives generated +by \:d and \:h to an auxiliary output file. In the formatted output this +code is represented by the pseudo-module `$\ATP$', which (like preprocessor +directives embedded in a program fragment) is set on a separate line and +does not otherwise affect the formatting of the surrounding code. + +Two other codes are intended mainly for use within `\pb'. As mentioned +earlier, \:v represents the bitwise-or operator. The code \:t is followed by +a control text, which can be used to insert any \TeX\ symbols into a +\Cee~expression; the result gets category \\{expression} but (if used in a +program fragment) does not produce any actual \Cee~code. It is for instance +possible to obtain `$\\{phi}<\hbox{$\pi$}/\T{2}$' by writing +`\hbox{\.{| phi < @t\$\\pi\$@> / 2 |}}', or if one prefers, to get +`$\\{phi}<\hbox{$\pi\over2$}$' by writing `\hbox{\.{| phi < +@t\$\\pi\\over2\$@> |}}'. The control text is put into an \.{\\hbox} that +will appear at the specified point in the formula. One might imagine using +\:t as a means to sneak in \TeX\ commands that will modify the formatting +produced by \.{CWEAVE}, but this is strongly discouraged unless one +thoroughly understands that formatting and the way it is obtained. + +The codes \:\& and \:= are intended as a means to alter or bypass the +processing of \Cee~tokens by \.{CTANGLE}; they should only be used in very +exceptional situations. The code \:\& forces \.{CTANGLE} to output the +symbols to the left and right of it directly adjacent to each other. Normally +\.{CTANGLE} inserts space between two symbols if it thinks this is necessary +for lexical reasons, regardless of whether a space was present in the input. +Items with a lexical structure unknown to \.{CTANGLE} might confuse it, so +that it would output a spurious space; this space could then be eliminated +by~\:\&. For instance, an earlier version of \.{CTANGLE} would not recognise +`\.{1000000UL}' as a constant, and consequently it output a space before +the `\.U', so that the \Cee~compiler could not recognise it either; this +problem could then be remedied by inserting~\:\&. No similar cases are known +for the current version of \.{CTANGLE}. + +The code \:= can be used to place some text in the \Cee~file that \.{CTANGLE} +will not produce by ordinary means: the control text following~\:=, up to +the next \:> is copied verbatim to the \Cee~file (with `\.{@@}' undoubled as +usual). If some special compiler activity, or some action by another tool, is +triggered by the occurrence of some special form of comment in the \Cee~code, +then such a comment can be placed using \:= (normally comments are removed +by \.{CTANGLE}). Also, should \.{CTANGLE} unjustly decide that two symbols +need no space in between them, then a space can be forced by writing +`\hbox{\.{@= @>}}' +\unskip\footnote{${}^\dagger$} +{One case where this would be necessary is the famous example + `$\T{\^123E}+\T{1}$': the \Cee~standard states that unless a space is put + between the `\.E' and the `\.+', the preprocessor should treat this as a + single number (a~kind of mixture of a hexadecimal and a floating point + constant), which turns out not to be valid, causing an error. \.{CTANGLE} + however never places a space between an identifier and an operator (even if + one was present in the input), so the way to get this expression properly + through the compiler is to write `\hbox{\.{0x123E @= @> + 1}}' (since this + bug is now documented, it has become a feature). +}. +The control text will be set in typewriter type and +framed in a box by \.{CWEAVE}, so that it stands out clearly; it is +syntactically neutral (like a comment). + +Finally, the code \:' can be used to introduce a single-character constant, +in the same way as the character `\.'' does in~\Cee. The difference between +the two ways of specifying this value is that \.{CTANGLE} will replace +\.{@'}$c$\.' by the (decimal) numeric \ASCII. value of the character~$c$, +whereas \.'$c$\.' is passed on to the \Cee~compiler, which will evaluate it +to the same value. The feature is therefore of little use in the current +version of \CWEB., which assumes the \ASCII. character set, but is provided +as an aid in writing programs that will be easier to port to non-\ASCII. +versions of \CWEB.. In such systems \.{CTANGLE} should still use the \ASCII. +code to compute \.{@'}$c$\.', while \.'$c$\.' represents the internal code +for~$c$. The idea is that one can then (as is done in the program \TeX) map +all characters on input to their \ASCII. equivalents, perform all internal +manipulations independently of the externally used character set, and +convert back to that code on output. + +\subsection Control codes behind the scenes: \:s, \:q, \:l + +The control codes of this subsection have in common that their use is never +essential, but can be convenient in some situations, and is largely or +completely invisible in the \CWEB. document. They are also the only control +codes allowed in limbo. + +The code \:s has the same effect as \:f, but produces no output in the \CWEB. +document. It can be used as a subsectioning code, just like \:f, but no +comment should follow the two identifiers it applies to in this case (since +there is nothing to attach the comments to); alternatively \:s can be used in +limbo. In either case the format definition is noted but nothing is written +to the \TeX~file. One might prefer to use \:s in situations where showing a +\&{format} definition is considered to be more distracting than informative. +Also, if a header file~$h$ is included by \&{\#include} rather than by \:h +or is located in a place where \.{CWEAVE} cannot find it, and it contains +typedef declarations, then~$h$ could be accompanied by a file containing a +line `\hbox{\.{@s ident FILE}}' for each typedef identifier defined in~$h$, +which can be read in by means of \:i by any \CWEB. file that includes~$h$. +This method of passing information between files is more error-prone than +having \.{CWEAVE} scan the header file however, so the latter method is to +be preferred whenever possible. + +The code \:q is followed by a control text, and is completely ignored both +by \.{CTANGLE} and \.{CWEAVE}; it can be used either in \TeX~text (even in +limbo) or in \Cee~code. It can be used to make comments relevant only when +the source file itself is being read, particularly within \Cee~code, where +\TeX~comments cannot be used for this purpose. For instance, it can be used +to put a descriptive or identifying comment at the beginning of a file +included using \:i. This code can also be used to accommodate any other +tools than \.{CTANGLE} and \.{CWEAVE} that might inspect the source file, +e.g., if a text editor tries to match braces and the like, it is unlikely to +correctly handle the complicated lexical structure of \CWEB. files in all +cases, and an occasional brace contained in a \:q control text may help to +keep it happy. Such occurrences of \:q are best removed however when source +files are made public. + +The code \:l is used to allow certain 8-bit characters (i.e., characters +with values in the range 128--255) to be used in identifiers. Doing so is +only useful if measures are taken to ensure that \TeX\ can handle these +characters properly. \TeX~version~3.0 and newer can handle 8-bit characters +in the input, but the standard fonts do not have any characters in positions +128--255, so one has to either load other fonts that do have characters in +those positions, or define such characters to be active characters that +somehow produce an appropriate glyph in the current font. For identifiers +the relevant font is text italic (selected by `\.{\\it}'), but if these +characters are available for identifiers, one will probably also want to use +them in \TeX~text (including module names and comments), so other fonts +should be provided for as well. \.{CWEAVE} does not take any special +measures for 8-bit characters, and just passes them on to \TeX\ (when they +occur in \Cee~code outside comments and module names, they are assumed to be +part of an identifier). However, since such characters cannot be used in +actual \Cee~identifiers, \.{CTANGLE} must replace them by characters that +are valid in \Cee~identifiers (letters, digits, and underscores). The +code~\:l can be used to specify which translation \.{CTANGLE} is to use for +a given 8-bit character. The code should only be used in limbo, and have the +form `\.{@l}~\<char number>~\<translation>', where \<char number> specifies +the character by a pair of hexadecimal digits in the range \.{80}--\.{FF} +(without leading `\.{0x}'), and \<translation> is a string of up to~9 +characters that are valid in \Cee~identifiers, terminated by a space. While +copying limbo material, \.{CWEAVE} replaces \:l by `\.{\\ATL}'; its default +definition will make `\.{@l}~fc~ue~' print a paragraph saying `{\bf letter +\\{\"u} tangles as \tentex "ue"}', assuming that `\hbox{\.{\{\\it\\char +"FC\}}}' indeed produces `\\{\"u}'; by stating `\.{\\noatl}' the definition +can be changed so that nothing appears at all. + +\subsection Control codes for tracing {\tentex CWEAVE}: + \:0, \:1, \:2, \:3 + +As will have become clear by now, the most sensitive part of \CWEB. is +\.{CWEAVE}'s parsing mechanism, and occasionally something may go wrong +with it, so that an awful result is produced. We have already discussed the +means available for corrective action, but sometimes it can be a problem to +find out just what is causing the trouble. Sometimes the reason is an actual +syntax error, which is best located by applying \.{CTANGLE} and a +\Cee~compiler to the \CWEB. source, but as noted before, \.{CWEAVE} may have +a problem that a \Cee~compiler does not experience. For this reason, its +parser can produce diagnostic messages on the terminal, showing details +about its actions and any anomalies found. The amount of diagnostics +produced is controlled by a level that may take values from~0 to~3, and can +be selected by one of the control codes \:0, \:1, \:2, and~\:3. These codes +can be placed in the \TeX~part of sections or within \Cee~code, and they +determine the level until the next such code or the end of the file; the +initial level is~0, or~1 if \.{CWEAVE} was called with the option~`\.{+d}'. +Since a complete \Cee~fragment is read in before parsing starts, the level +is constant throughout each fragment, and determined by the value at the end +of the fragment. + +The diagnostic output uses abbreviations for syntactic categories, e.g., +`\.{unop}' and `\.{binop}' stand for unary respectively binary operators, +and `\.{op}' stands for operators like `$*$' that can be used either way; +`\.{exp}' stands for an expression, `\.{decl}' for one or more declarations, +`\.{stmt}' for one or more statements. Simple symbols like braces and commas +stand for themselves, as do many keywords; `\.{for}' stands for `\&{for}' +or~`\&{while}', and `\.{int}' for a type, storage class specifier or typedef +identifier. A complete list of category abbreviations can be found in the +source code for the function $\\{print\_cat}$ in~\.{CWEAVE}. +(To fully understand the parser's diagnostic messages one has to be familiar + with the parsing algorithm and the grammar rules, but for error detection + a detailed understanding is usually not required.) + +At level~$0$ the parser will not produce any diagnostic output. At level~$1$ +it will report any \Cee~fragment that could not be recognised as a single +syntactic entity, which is a good indicator of grammatical problems and +possibly of ugly output. It can be argued that level~1 is the natural level +to use (which is why the `\.{+d}' option is provided), since not getting any +diagnostics when there are syntax problems only gives a false impression +that things are in order; after all, nobody would want to use a compiler +that would spare the programmer its diagnostics for syntax errors, but +instead would produce unreliable code. In a syntactically correct \CWEB. +program it is almost always possible to apply \:f, \:;, \:[ and~\:] in such +a way that no diagnostic output is produced at level~$1$; indeed this is has +been done for all sources of \CWEBx. itself. + +The diagnostic messages produced at level~1 print the successive categories +of the sequence of recognised items, which could not be combined into any +larger entity. Interpreting such a message takes a bit of practice, as one +has to guess which part of the program fragment corresponds to each category +printed; however, a look at the (badly) formatted output can often be +helpful. The boundaries between the entities corresponding to the printed +categories can often be recognised by the fact that some form of layout is +obviously missing, and a space appears instead; for instance, if a closing +brace of a compound statement is not preceded by a line break, then +something inside that statement must have prevented it from being recognised +by the parser, and its opening and closing brace will occur among the +printed categories. + +At levels $2$~and~$3$ the parser will print the result of every single step +it takes; this extremely verbose mode can be used to trace the exact steps +by which the parser obtains its result. Detailed knowledge of the set of +grammar rules is assumed, and these levels are mostly useful to those who +wish to study or modify the rules. The set of rules can be found as a +chapter of the \CWEB. source document for \.{CWEAVE}, or can be obtained +separately by running `\hbox{\.{cweave -x rules}}' (ignoring the warning +about an unused module) and `\hbox{\.{tex rules}}'. After each reduction +step the number of the rule used is printed, followed by a list of +categories after reduction, with the one that was formed by the reduction +step enclosed in inverted angle brackets. Tracing at level~$3$ is even more +esoteric than at level~$2$: all categories printed will have an additional +character at both ends, indicating whether \TeX\ should be in math mode +(`\.+') or in horizontal mode (`\.-') at that end of the item, or that it +doesn't matter (`\.?'); this may help to explain the positioning of math +shifts (`\.\$') in the \TeX\ output, which is controlled indirectly by the +grammar rules. + +To reduce the amount of output, all categories that have not yet been +considered by the parser are replaced by an ellipsis. The sequence of +categories before the reduction can be found by looking up the reduction +rule with the given number. Here is some sample output for a simple piece +of \Cee~code at level~$2$. +\vfil\penalty500\vfilneg\smallskip\vbox +{\narrower\obeylines\catcode`\_=12 \tentex \let\\=\BS \obeyspaces\let =\ % +Tracing after l.3: +@2 |if (n>0) printf("n-1 = \%d.\\n",n-1);| + 2: if ( >exp< ) ... + 10: if >exp< exp ... +110: >if_head< exp ( ... + 6: if_head exp ( >exp< op ... + 3: if_head exp ( >exp< ) ... + 10: if_head exp >exp< ;. + 7: if_head >exp< ;. + 80: if_head >stmt<. +117: >stmt<. +} +\smallskip +We see that the first three steps reduce `\&{if}~$(n<\T{0})$' to an +\.{if\_head}; then `$\.{"n-1\ =\ \%d.\\n"},n$' is combined to an expression, +after which `${}-1$' is incorporated as well; then the statement calling +\\{printf} is reduced in three steps, and finally it is combined with the +`\.{if\_head}' to form another statement. + +Even this small example shows that \.{CWEAVE} parses the code in a +different way than a \Cee~compiler would. This is partly due to its strict +bottom-up strategy, which is largely unaware of context: parentheses +(rule~10) and a comma (rule~6) are incorporated by the expression syntax, +even when they actually figure in a conditional statement or function call. +Furthermore, some distinctions are irrelevant for determining the +proper layout: the ``comma operator'' is unjustly given precedence over the +minus operator, but the printed output will be no different for it. + + +\beginsection Some features of the standard format + +The \TeX~file produced by \.{CWEAVE} will begin with loading the standard +format from the file \.{cwebxmac.tex}, whose definitions control the +typesetting process: \.{CWEAVE} communicates with \TeX\ mostly by using +macros defined there. Most of them have very short names, in order to limit +the size of the \TeX~file; one should be aware that most of the +single-letter control sequences and numerous two-letter ones are in use by +\.{CWEB}, and are not available for other uses (when in doubt, consult +\.{cwebxmac.tex}). All of the macros defined in plain \TeX\ for accenting +letters have retained their meaning however, except `\.{\\.}' (for the dot +accent), which is replaced by~`\.{\\:}'. Some of the macros of the standard +format can be of interest to the literate programmer, either because they +can be used directly in \TeX~text (indeed, some are not used by +\.{CWEAVE}, and are only intended for this purpose), or because they can be +redefined in limbo to alter the formatting of the program. + +In formatted \Cee~text, many operators are represented by macros that +produce the appropriate symbols; by changing the definition of these macros, +one can alter their appearance. Here is a table of the relevant cases. +$$ +\everycr{\noalign{\hrule}} +\def\:#1 #2 +{\vrule\vbox{\halign + {\strut\ \hfil##\hfil\ \cr \.{#1}\cr \.{\\#2}\cr $\csname#2\endcsname$\cr} +}} +\hbox +{\vrule\vbox + {\hrule\hbox{ \strut\it operator }\hrule + \hbox{ \strut\it macro }\hrule + \hbox{ \strut\it symbol }\hrule + }% + \:= K \:== E \:!= I \:<= Z \:>= G + \:\&\& W \:|| V \:! R + \:\& AND \:| OR \:\^ XOR \:\~ CM \:<< LL \:>> GG + \:++ PP \:-- MM \:\% MOD \:-> MG \:\#\# SS + \vrule +} +$$ +When such a macro is redefined, it is best to consult the original +definition first, since it often issues a penalty, and it is best to retain +this. Formatting of ordinary identifiers and keywords is performed by +`\.{\\\\}' and `\.{\\\&}', which have one argument, that is typeset in italic +respectively boldface type; similarly `\.{\\.}' is used for items in +typewriter type, such as strings and all-caps identifiers. In the argument of +`\.{\\.}' special characters can be used if escaped, as discussed for \:.. +For `\AM' in ordinary text `\.{\\AM}' can be used (rather than `\.{\\\&}'). +For names in all caps, like `\caps{ASCII}', or `\caps{UNIX}', the macro +`\.{\\caps}' is provided, which makes them slightly less obtrusive by +selecting a smaller font; for `\Cee' and `\Cpp' the macros `\.{\\Cee}' and +`\.{\\Cpp}' are provided. Typesetting of comments, \Cpp\ one-line comments, +and numeric constants is controlled by the macros `\.{\\C}', `\.{\\SHC}', +and~`\.{\\T}', respectively; these can be redefined if a different style is +desired. + +The dimensions of the pages can be controlled by setting the parameters +`\.{\\pagewidth}', `\.{\\pageheight}' (the height of the text area), +`\.{\\fullpageheight}' (the height including running head), and +`\.{\\pageshift}' (extra displacement of odd numbered pages with respect to +even numbered ones), and then invoking the macro `\.{\\setpage}'. A +magnification can be applied to the entire document by saying +`\.{\\magnify\{$n$\}}',where $n$ is the magnification in thousandths of the +ordinary scale; this should precede any changes of the page dimensions, +but if no changes are made, the page dimensions will be set to their +standard values, unmagnified. The unit of indentation can be set by +`\.{\\indentation\{\<size>\}}'. + +The title of the program is taken from the macro `\.{\\title}', whose +default value is the basename of the program source file, converted to upper +case. It is used in running heads and in the table of contents. Another part +of the running heads is set to the chapter title by sections starting +with~\:*, but by defining the macro `\.{\\gtitle}' in limbo the +corresponding text for the running heads on any pages before the first such +section can be set (the default is `\.{CWEB} output'). By invoking +`\.{\\titletrue}' the running head can be suppressed for one page; this is +useful if the text in limbo produces a title page. The date of processing +(by \TeX) can be included in the document before the first section by +putting `\.{\\datethis}' in limbo; it can be placed on the table of contents +by saying `\.{\\datecontentspage}'. At the end of the document one normally +has an index, a list of module names and the table of contents, in that +order, but \TeX\ can be made to stop short of any one of these by invoking +respectively `\.{\\noinx}', `\.{\\nomods}', or `\.{\\nocon}'; as already +mentioned, stating `\.{\\changesonly}' will limit the printed output to the +sections affected by the change file. The appearance of the table of +contents can be controlled by redefining `\.{\\topofcontents}' and +`\.{\\botofcontents}': these macros determine the material that comes above +the table and below it, including its title and the glue needed to fill up +the page height. The page number of the table of contents is assigned from +`\.{\\contentspagenumber}' (the default is~0), but it will not appear in +print because the running head is suppressed on that page. + +It may be noted that \CWEB. documents contain some fixed phrases in the +English language, such as the cross-references at the end of sections. These +are not produced directly by \.{CWEAVE} however: one could adapt \CWEB. to a +different language by redefining the macros `\.{\\A}', `\.{\\As}', `\.{\\Q}', +`\.{\\Qs}', `\.{\\U}', `\.{\\Us}', `\.{\\ET}', `\.{\\ETs}', `\.{\\ch}', +`\.{\\postATL}', `\.{\\ATP}', `\.{\\today}', `\.{\\now}', and parts of +`\.{\\fin}' and~`\.{\\con}'. + + +\beginsection Comparison with Levy/Knuth {\tentex CWEB} + +As was mentioned in the introduction, \CWEBx. is derived from an earlier +\CWEB. system (itself derived from Knuth's \.{WEB}), that was written and +distributed by Sylvio Levy and Donald~E. Knuth, and that \CWEB. system has +independently evolved into a version currently distributed as \CWEB.~3.3. +Both \CWEBx. and \LKC. have undergone changes with respect to their common +ancestor, although the spirit of the system has not fundamentally changed in +either case. Since we considered it undesirable to have a great divergence +between systems that both intend to be ``a \caps{WEB} system for~\Cee'', we +made a conscious effort to reduce the differences between \CWEBx. and \LKC. +by including the extensions of the latter into \CWEBx. as well. There was +one deliberate exception: we made no attempt to extend the grammar used by +\.{CWEAVE} to handle the full \Cpp~language +\unskip\footnote{${}^\dagger$} +{\Cpp\ has a significantly more complicated syntax than that of~\Cee, which + is already far from simple, and it has some forms of context dependence + that make it doubtful whether to \.{CWEAVE} could ever reliably handle + \Cpp\ in full generality (and even then, \Cpp\ is a moving target). Most + effort was spent on getting the grammar for \Cee\ correct; support for + \Cpp\ was restricted to some extensions of~\Cee\ that could be incorporated + easily. +}. +On the other hand, hoping to fully bridge the gap between \LKC. and \CWEBx. +for \Cee~programs, a compatibility mode was added to \CWEBx. in which it +tries to mimic the behaviour of \LKC. in all aspects that are relevant to +the programmer (even in cases where that behaviour is undocumented), at the +price of losing some possibilities that \CWEBx. normally has. + +The description of the differences between \LKC. and \CWEBx. can be divided +into two parts: the differences between \LKC. and the compatibility mode +of~\CWEBx., and the differences between \CWEBx. with and without +compatibility mode. The former differences are minimal, but hard to +enumerate precisely, as they are mainly a matter of difference in +implementation. The latter differences are much more significant, but they +can easily be listed, since precise details of those differences can be +found by looking up all index references of the identifier +\\{compatibility\_mode} in the sources for the programs of~\CWEBx., and (for +the differences that only involve processing by \TeX) the contents of the +file \.{cwebcmac.tex} that modifies the \.{cwebxmac} format to emulate the +environment provided by the \.{cwebmac} format of \LKC.. + +As was stated, the differences between \LKC. and the compatibility mode +of~\CWEBx. should not be relevant to the programmer, but if one uses \LKC. +in a way that relies on knowledge of intimate details of its implementation +(which are not described in the manual but can be learned from studying the +sources), then it is certainly possible to find such differences; this +applies particularly to using the \TeX~code produced by \.{CWEAVE} in +unusual ways. Unfortunately there is no clear specification of which aspects +of \CWEB. are well defined so that the user can safely rely on them, and +which aspects are implementation details. We have taken a pragmatic attitude +by reproducing all aspects that are described in the manual, and moreover +many undocumented aspects, enough to process the sources of \LKC. itself and +of the Stanford GraphBase without problems. To give an impression of the +kind of differences that remain, we shall list some of the known ones. + +The output files written by \CWEBx. are not equal to those written by \LKC., +so processing them otherwise than directly by a \Cee~compiler respectively +by~\TeX\ may reveal some deviations. For instance, entries for the index and +the list of module names are written using the control sequence `\.{\\I}' by +\LKC., but since `\.{\\I}' is also used for representing the operator +`$\I$', this causes problems for module names in which that operator is +used; therefore, \CWEBx. uses `\.{\\@}' instead. In \CWEBx. unbalanced +braces or parentheses in program fragments or macro replacement texts are +reported and corrected by \.{CTANGLE}, as an aid in catching programming +errors early; in \LKC. this is not done (but programs with such unbalanced +symbols will still bring \.{CWEAVE} into serious problems). In compatibility +mode the definition of `\.{\\PB}' ensures that `\pb' can always be used from +within math mode; in \LKC. this is true only in simple cases. Comments in +the \TeX~parts of sections (following a non-escaped `\.{\%}' character) are +ignored and removed by \CWEBx., whereas in \LKC. they are processed normally +and copied to the output, which may cause spurious index entries, and in +exceptional cases may cause part of the comment to appear in print. The +grammars used by \.{CWEAVE} in the two systems are quite unrelated; for +\CWEBx., the only guideline in constructing the grammar has been the +\caps{ANSI/ISO~C} syntax. When processed by \CWEBx. with the proper options +selected, \CWEB. documents will look similar to the result produced by +\LKC., but not identical. Unlike \LKC., \CWEBx. places optional breaks at +operators, reflecting their priority and nesting inside parentheses. In +\LKC., if \:{\ } is immediately followed by a subsectioning code, then the +output from the subsectioning code (e.g., \.{\#define} for~\:d) will be +placed on the same line as the section number, but if anything, even an +extra space, comes in between, or if the `\.@' in the sectioning code was +followed by a newline rather than by a space, then that output is moved to +the beginning of a fresh line; in \CWEBx. output from a subsectioning code +never appears on the same line as the section number. + +\CWEBx. has a number of control codes and a number of command line options +that \LKC. does not have; moreover there are some control codes that \LKC. +does have, but under different names (that are used for other purposes +in~\CWEBx.). In compatibility mode such control codes have the same +interpretation as in \LKC., but if there is no such interpretation while +there is one in \CWEBx., the latter is taken;. This means that in +compatibility mode one can use the control codes \:v, \:\\, and \:\~, which +are ignored in \LKC., while \:?~and~\:) can be used as aliases for +\::~and~\:\#, respectively; furthermore the command line options controlled +by the characters `\.l', `\.d', `\.t', `\.e', `\.a', `\.u', `\.w', `\.m', +and~`\.+' are also extras with respect to \LKC.. Finally the control code \:; +retains all its \CWEBx. uses in compatibility mode, except that of modifying +the category of module names (which is different anyway), whereas in \LKC. +it can only be used as an invisible semicolon. + +The most direct difference between \CWEBx. with and without compatibility +mode is that in compatibility mode the control codes \:h, \::, \:\#, and~\:p +are translated into respectively \:p, \:?, \:), and~\:c, which implies that +the meaning of \:h, \::, and~\:\# as described in this manual are not +available in compatibility mode. There is also an important syntactic +adjustment: in compatibility mode module names are always treated as +expressions (which means they must almost always followed by \:; to make the +combination behave as a statement, or by `\.;', which will however become an +empty statement in the \Cee~program). Then there are a few points where +compatibility mode lifts certain restrictions (thereby reducing the +diagnostic capabilities). All 8-bit characters will be accepted by +\.{CTANGLE}, whether or not an explicit translation was specified using \:l; +the default translation used corresponds to `\hbox{\.{@l \Cident{NN} +X}\\{NN}}', where \\{NN} is the 2-digit hexadecimal code, in upper case, for +the character. Module names used within `\pb' do not have to be the final +item. Rather than performing \:i inclusions before the change file is +matched, the order is more or less reversed (but if some \:i~line is not +replaced by the change file, then the included file will again be scanned +for changes); consequently \:i is allowed (and meaningful) in the change +file. + +The remaining alterations affected by compatibility mode are fairly minor. +Trailing digits in identifiers will not be set as subscripts. The \TeX\ +control sequence corresponding to identifiers that are given the category of +\\{\TeX} by means of a format definition will be processed in math mode +rather than horizontal mode. The code \:t together with the following +control text will not be treated as an expression in parsing, but as an +inert item that sticks to the token to the right of it (unlike comments that +are attached to the token to their left); this allows `\.{@/@t\\4@>}' to be +used in place of `\.{@\\}'. Compound assignment operators like `\.{+=}' are +treated as two separate tokens; this implies among other things that the +operator `\.{|=}' must be entered as `\.{@v=}' when used inside `\pb'. In +index entries produced by \:\^, \:., or~\::, the underscore character will +be automatically escaped by a backslash, unlike other special characters +(this makes it harder to enter formulas with subscripts into the index). +The output of `\pb' is made an argument to the control sequence `\.{\\PB}', +whose default definition puts its argument into an `\.{\\hbox}'; this makes +it safe to use `\pb' inside math mode when using compatibility mode. +Finally there are a few other small changes to the format used: \Cpp\ +one-line comments will be formatted as if they were ordinary \Cee~comments, +and the formatting of lines in the table of contents will be affected in +font and spacing by the ``depth'' specified for that chapter title. + + +\beginsection Summary of\/ {\tentt CWEB} codes + +For reference, we give a table with all the codes used in \CWEB. with their +main characteristics. The letters in the column {\it where\/} indicate in +which parts of the source text may immediately precede the code: +`L'~indicates text in limbo, `T'~the \TeX~part of a section, `M'~indicates +an intermediate part of a section (from \:d, \:h, ~\:f, or~\:s), `C'~the +\Cee~part of a section, and `c' pieces of \Cee~code within~`\pb' (the +letter~`M' is only used when the code terminates an intermediate part; +inside the parts after \:d, \:h, and~\:f, the letter `C' applies). The +column {\it frequency\/} indicates how commonly the code is used, with +$\\{regular} > \\{incidental} > \\{rare} > \\{emergency}$; for the codes with +$\\{frequency}\leq\\{rare}$, no sensible use could be found within any +source file for the \CWEB. system itself. The codes \:0--\:3 that are only +of temporary use are omitted from the table. + +\vfil\eject +$$\vrule\vcenter{\hrule\smallskip\tabskip 1em +\let\par=\cr\obeylines \def\title #1 + {\noalign{\smallskip\hrule\smallskip\hbox{\sl\enspace#1}}}% +\halign{\strut\.{#\unskip}\hfil & #\unskip\hfil & #\unskip\hfil & % + {\it#\unskip\/}\hfil & #\unskip\hfil +\omit\strut\it code & \it meaning & \it where & \it frequency & \it remarks +\title Sectioning codes +@* & Start of chapter & LTMC & regular % + & \.{@**}, \.{@*$n$}, or \.{@*} Title\.. +@~ & Start of section & LTMC & regular +@\~ & Start of section tied to previous one & LTMC & regular +\title Subsectioning codes +@c & Start of unnamed program fragment & TM & regular % + & also \.{@C} +@< & Start of module name & TMCc & regular % + & \.{@<} Module name \.{@>} +@d & `\.{\#define}'; start macro definition & TM & regular % + & also \.{@D} +@h & `\.{\#include}'; specify included header file & TM & regular % + & also \.{@H} +@f & Format definition; change syntactic category & TM & incidental % + & also \.{@F} +@( & Start module name defining output file & TMCc & incidental % + & \.{@(} file name \.{@>} +\title Parsing control codes +@; & Invisible semicolon, or magic wand for syntax & Cc & incidental +@[ & Start of item forced to expression & Cc & incidental +@] & End of item forced to expression & Cc & incidental +\title Cross-referencing codes +@! & Make index reference underlined & TCc & rare +@\^ & Index entry in roman type & TCc & regular % + & \.{@\^}index entry\.{@>} +@. & Index entry in typewriter type & TCc & regular % + & \.{@.}index entry\.{@>} +@? & Index entry formatted by `\.{\\9}' & TCc & rare % + & \.{@?}index entry\.{@>} +@: & Define label for explicit cross-reference & TCc & incidental % + & \.{@:}label\.{@>} +@\# & Explicit cross-reference to defined label & T & incidental % + & \.{@\#}label\.{@>} +\title Layout control codes +@, & Thin space & Cc & rare +@| & Optional line break & Cc & rare +@/ & Forced line break & Cc & incidental +@) & Forced line break with vertical white space & Cc & incidental +@\\ & Forced line break, next line backed up & Cc & incidental +@+ & Cancel any line break, replace by space & Cc & incidental +\title \Cee\ control codes +@p & Insert output from \:d and \:h & Cc & rare % + & also \.{@P} +@v & Bitwise or operator `$|$' & Cc & incidental % + & also \.{@V} +@t & \TeX~code within expression & Cc & incidental % + & \.{@t}\TeX~code\.{@>}; also \.{@T} +@\& & Glue together adjacent tokens & Cc & emergency +@= & Insert verbatim \Cee~code & Cc & emergency % + & \.{@=}verbatim \Cee~code\.{@>} +@' & \ASCII. constant converted to number & Cc & rare & \.{@'}$c$\.' +\title Silent control codes +@s & Non-printing version of \:f & LTM & rare +@q & Ignored control text & LTCc & rare % + & \.{@q}any text\.{@>}; also \.{@Q} +@l & Specify translation of 8-bit character & L & rare % + & \.{@l} $xx$ string ; also \.{@L} +%\title Debugging level switches +%@0 & Debugging off & TCc & temporary +%@1 & Report parsing failures & TCc & temporary +%@2 & Trace every parsing step & TCc & temporary % +% & grammar maintenance +%@3 & Trace every parsing step, show mathnesses & TCc & temporary % +% & implementors only +\title Miscellaneous codes (these are not control codes) +@@ & Representation of `\.@' & LTCc & incidental % + & legal in control text too +@i & Insert subsidiary source file & any & incidental % + & also \.{@I} +@x & Start of change; old lines follow & any & incidental % + & also \.{@X} +@y & Middle of change; replacement lines follow & any & incidental % + & also \.{@Y} +@z & End of change & any & incidental % + & also \.{@Z} +}\smallskip\hrule}\vrule +$$ + +\closeout\cont % the contents information has been fully gathered +\vfill\eject +\pageno=\contentspagenumber \secno=0 \titletrue +\message{Table of contents:} +\topglue 0pt plus 1.618 fil +\centerline{\ttitlefont CWEBx\titlefont\space Manual} +\vfil +\def\Z#1#2#3{\line{\kern.1\hsize\indent\llap{#1}\quad #2 + \leaders\hbox to .5em{.\hss}\hfil \enspace#3\kern.1\hsize}} +\def\z#1#2{\line{\kern.1667\hsize #1 + \leaders\hbox to .5em{.\hss}\hfil \enspace#2\kern.1\hsize}} +\readcontents +\vskip 0pt plus 2.618 fil +\eject + +\bye diff --git a/web/c_cpp/cwebx/parser.w b/web/c_cpp/cwebx/parser.w new file mode 100644 index 0000000000..ecc46d119b --- /dev/null +++ b/web/c_cpp/cwebx/parser.w @@ -0,0 +1,1233 @@ +@* Introduction to the grammar. @:title@> +The most intricate part of \.{CWEAVE} is its mechanism for converting +\Cee-like code into \TeX\ code, and we shall consider this aspect of the +program now. This parsing mechanism must be able to deal with fragmentary +constructions whose overall ``part of speech'' is not known (e.g., those +within~`\pb'), which implies that recognition must in principle be able to +proceed without any context information. Therefore a ``bottom-up'' approach +is used, that collects successive tokens, and only takes action once it has +seen enough to recognise a complete match with a syntax rule. Bottom-up +parsing is a very powerful technique (it can handle grammars that are not +tractable by many other parsing methods); it is one of the many significant +contributions to computer science that the designer of the original \.{WEB} +system, D.~E. Knuth, has made. Even so, the technique used here is less +powerful than that of traditional bottom-up parsers produced by parser +generators, since those can use information derived from the full +left-context (and a small amount of right-context) to help decide difficult +situations. In practice such context is not often required to unambiguously +parse program fragments, and most situations where there is a possibility of +incorrect recognition can be avoided be careful specification of the syntax +rules to be used; this does however make formulation of the grammar a +slightly subtle matter. + +As we have already seen, the input is represented as a sequence of +{\sl scraps}, each of which specifies a {\sl category\/} and a +{\sl translation}. The category defines a syntactic class, and the +translation is a token list that represents \TeX\ code; the latter is +effectively just a single string of characters, although it is stored in a +different form for efficiency reasons. Rules of syntax and semantics tell us +how to combine adjacent scraps into larger ones, and if we are lucky an +entire \Cee\ text that starts out as hundreds of small scraps will join +together into one gigantic scrap whose translation is the desired +\TeX\ code. If we are unlucky (i.e., if the input is not properly formed +for recognition by our parser), we will be left with several scraps that +don't combine; their translations will simply be output, one by one. + +The parsing process is governed by a set of reduction rules, each of which +specifies how a sequence of consecutive scraps with certain given categories +can be combined into a single new scrap, and it also tells what category the +new scrap has and how its translation is obtained from those of the original +scraps. The set of reduction rules is given by a table, which is actually +an initialiser for a static array, starting in section@#rules@>. +In many cases a rule is simply given by listing the category codes +forming its pattern (its left hand side) and the category code of the +resulting scrap (the right hand side). For example, one of the reduction +rules could be represented as +$$ +\hbox{|expression| |binop| |expression|}\Rightarrow\hbox{|expression|}. +$$ +For such simple rules the translations of the original scraps are simply +concatenated to form the translation of the resulting scrap. The line in the +table corresponding to this rule actually looks like this: +$$ +\hbox{|{2,{{expression,binop,expression}},{expression, NULL}}|}. +$$ +The `2' is the number used to identify the rule, which is useful when +tracing the activities of the parser, and the |NULL| signifies that the +default translation applies; the braces reflect the structure used to +represent syntax rules, which has some additional components that are not +specified explicitly here, and that are therefore initialised to~0. + +For other rules specific additional items are to be inserted in between the +translations of the scraps that match the pattern. For instance, here is +another rule present in the table +$$ +\hbox{|{ 6,{{expression, comma, expression}},{expression,"__p1_"}}|}. +$$ +Here the translation is specified by the ``format string'' |"__p1_"|, where +the underscores stand for the translations of the three participating +scraps, the `\.{p1}' for an |opt| token with argument~`\.1' (eventually +producing `\.{\\penalty10}') that comes after the translation of the second +scrap, with category |comma|. That translation will almost always be |","|, +since there are no rules with |comma| as their right hand side, but if a +comment follows the comma, it will have been incorporated into its scrap +while preparing for the reduction, and in this case the optional break will +follow the comment (and have no effect, because there is already a forced +break after comments). The interpretation of all characters that can appear +in the format strings can be found in section@#format@>. + +To handle all cases that can arise in programs, in combination with a large +number of formatting options that might be selected, our mechanism for rules +of a somewhat more general form than those presented above, but the +extensions are used only for a few rules. Consider the following rule: +$$ +\hbox{|{132,{{lpar,statement,statement},1}, + {statement,"_B_"},forced_statements}|}. +$$ +Here, the `1' indicates that the first scrap from the left in the pattern, +|lpar|, serves as context only, and does not take part in the actual +reduction. The rule therefore reduces a sequence of two consecutive +statements into a single one, inserting a |break_space| (from the `\.B' +format character) between their translations, but only if the statements are +preceded by a left parenthesis (category~|lpar|); this restriction means +that the rule will only apply to the first two semicolon-separated +expressions following `|for|~|(|'. The entry |forced_statements| means +moreover that the rule will not be used at all unless a `\.{+f}'~or~`\.{+a}' +command line option was specified, which forces line breaks between +consecutive statements; this rule then avoids such line breaks between the +controlling expressions of a |for|-loop, since it takes precedence (see +below) over the rule that normally combines statements. + +@ The rules are applied from left to right, as follows. Suppose we are +currently working on the sequence of scraps with categories $c_1\,c_2\ldots +c_n$. We try first to find a rule whose pattern matches an initial substring +$c_1\,c_2\ldots\,$, and if no such rule exists, we try to find a rule +applicable to the next substring $c_2\,c_3\ldots\,$; if that fails, we try +to match $c_3\,c_4\ldots\,$, etc. When several patterns match, starting at +the same scrap, the longest one is selected (no two rules can have identical +patterns). For instance, there is a rule that reduces `|struct_like| +|expression| |lbrace|' to `|struct_head|', and another one that reduces +`|struct_like| |expression|' to `|int_like|'; the latter is only applied +when no |lbrace| scrap follows. This is a sensible rule, since the longer +pattern is the more specific one, and without such a rule it could never +match. One should be aware however that this only works because the scrap +with category |lbrace| represents a single token that requires no reduction +to create it, for otherwise the two-scrap reduction would be applied before +the three-scrap reduction would have a chance to match. + +One might say that we use a left-to-right eager strategy for choosing +reductions; this strategy is chosen on heuristic grounds, and there is no +guarantee that it will find a successful sequence of reductions if one +exists. In other words, if we interchange left and right hand sides of the +rules and view them as {\sl production\/} rules of a context free grammar, +then the language generated by this grammar can be much larger than the one +recognised by our parser. For instance, the context free grammar would, from +the rules given above, generate expressions with multiple operators where +implicit parentheses are placed arbitrarily (and therefore it would be +ambiguous); our parsing strategy however will always place implicit +parentheses to the left. (We don't care if implicit parentheses are placed +incorrectly, because it does not influence typesetting.) It is an +interesting theoretic problem to find an algorithm that will transform a +set of reductions rules into a context free grammar generating exactly the +language recognised; that would help to verify that the rules will work as +intended. We must be cautious in formulating the rules, not only to give +rules that are sufficient to reduce all desired programs, but also not to +specify rules that could in certain circumstances match with precedence over +the ones we intended for the situation. + +Some of the formatting of the output is controlled by the \TeX~format in the +file \.{cwebxmac.tex}, rather than directly by \.{CWEAVE}. For instance, the +`\.{\\penalty10}' mentioned above is actually written as `\.{\\31}'; the +macro `\.{\\3}' will in fact raise the penalty for any enclosing pair of +braces or parentheses; thus breaks in argument lists or initialiser lists +will be avoided in favour of ones outside those lists. There are other +effects not visible in the grammar, like optional breaks automatically +associated with certain operators. In the whole it is a delicate interplay +between the set of reduction rules, the reduction mechanism, the macro +definitions, and the (mathematical) typesetting rules of \TeX\ that +determines how programs will be typeset. This division of labour is quite +convenient (e.g., it would tremendously complicate the task of \.{CWEAVE} if +it had to decide which optional breaks should actually be taken), but it has +the unfortunate consequence that it is not easy to gain a comprehensive +understanding of the translation process. Indeed there have been quite a few +surprises during the construction of the system, and we cannot be sure that +there are no more to come; yet we believe the situation is more transparent +than in the original |WEAVE|, since we have avoided rules that are oriented +towards the target language (\TeX) rather than towards the source language +(\Cee). + +@ Here is a list of the category codes that scraps can have. The category +codes that apply to reserved words (e.g., |while_like|, but also +|declaration| for |va_dcl|) as well as |expression| (that is used for +|type_defined| identifiers in their typedef declaration) are sufficiently +high that they can be distinguished from |ilk| values that are not category +codes, like |type_defined|, |TeX_like|, |NULL_like|, |const_like|, +and~|typedef_like|. A number of categories can occur in scraps, but do not +occur in any of the reduction rules, since they are handled by other means; +they have values exceeding |max_rule_cat|. The macro |valid_cat| checks +whether |c| is a category that might match in a rule; it uses its argument +twice, so its argument should not cause a side effect. If this section is +changed, section@#category output@> should be changed correspondingly. + +@d max_category end_expr /* largest scrap category */ +@d max_rule_cat return_like /* largest category in rules */ +@d valid_cat(c) ((c)>0 && (c)<=max_rule_cat) + +@<Typedef and enum...@>= +enum @/ @:categories@> +{ unop = 1, /* a unary operator like `|!|' */ + binop, /* a binary operator like `|<|' */ + unorbinop, /* an operator that can be either, like `|-|' */ + select, /* structure selection: `|.|' or `|->|' */ + question, /* a question mark operator */ + lbrace, rbrace, lpar, rpar, lbrack, rbrack, + /* `|{|', \ `|}|', `(', \ `)', `[', \ `]' */ + comma, semi, colon, colcol, magic, /* `,', `;', `:', `$\CC$', \:; */ + subscript, /* an array subscript, like `|[]|' or `|[i++]|' */ + struct_head, /* the beginning of a struct specifier, like `|struct s{|' */ + short_lbrace, short_struct_head, /* from `\.{\{@@;}', for one-liners */ + compound_statement, /* a complete compound statement */ + statement, /* a complete statement, possibly compound */ + function, /* a complete function definition */ + function_head, /* a function identifier followed by formal parameters */ + parameters, + /* parameters in function declaration, or casting operator like `|(int)|' */ + label, /* a statement label */ + if_head, /* `|if|' followed by a (parenthesised) expression */ + if_else_head, /* \lq|if @t\dots@>@; else|', \lq|while(@t\dots@>)|' + or \lq|switch(@t\dots@>)|' */ + do_head, /* `|do @t\dots@>@; while|' */ + mod_scrap, /* module name */ + declarator, /* abstract declarator, like `|(*)(int,char*[])|' */ + declaration, /* a complete declaration */ + expression, /* an expression, possibly a single identifier */ + while_like, /* `|for|', `|while|', `|switch|' */ + do_like, /* `|do|' */ + if_like, /* `|if|' */ + else_like, /* `|else|' */ + int_like, /* `|int|', `|char|', `|extern|', \dots */ + case_like, /* `|case|', `|default|' */ + sizeof_like, /* `|sizeof|' */ + struct_like, /* `|struct|', `|union|', `|enum|' */ + return_like, /* `|return|', `|break|', `|continue|', `|goto|' */ + lproc, /* `\&\#' and following identifier starting preprocessor directive */ + rproc, /* end of a preprocessor directive */ + insert, /* comment or other syntactically inert item */ + begin_expr, end_expr /* \:[ and \:] */ +}; + +@ As we have already seen, tokens are converted to elementary scraps by +the function |C_read|; these scraps form the `terminal symbols' of our +grammar. The translation of tokens to scraps is largely governed by the +static array |trans_ini|, whose initialisation values we shall now give. + +@d yes_math 1 /* should be in math mode */ +@d no_math 2 /* should be in horizontal mode */ +@d maybe_math 0 /* works in either horizontal or math mode */ + +@< Initialiser for |trans_ini| @>= +{ '!', unop, yes_math, "\\R" }, @/ @.\\R@> +{ '~', unop, yes_math, "\\CM" }, @/ @.\\CM@> +{ '/', binop, yes_math, "/" }, @/ +{ '<', binop, yes_math, "<" }, @/ +{ '>', binop, yes_math, ">" }, @/ +{ '.', select, yes_math, "." }, @/ +{ '=', binop, yes_math, "\\K" }, @/ @.\\K@> +{ '|', binop, yes_math, "\\OR" }, @.\\OR@> + { or, binop, yes_math, "\\OR" }, @/ +{ '^', binop, yes_math, "\\XOR" }, @/ @.\\XOR@> +{ '%', binop, yes_math, "\\MOD" }, @/ @.\\MOD@> +{ '+', unorbinop, yes_math, "+" }, @/ +{ '-', unorbinop, yes_math, "-" }, @/ +{ '*', unorbinop, yes_math, "*" }, @/ +{ '&', unorbinop, yes_math, "\\AND" }, @/ @.\\AND@> +{ '?', question, yes_math, "\\?" }, @/ +{ '(', lpar, yes_math, "(" }, @/ +{ ')', rpar, yes_math, ")" }, @/ +{ '[', lbrack, maybe_math, "[" }, @/ +{ ']', rbrack, maybe_math, "]" }, @/ +{ '{', lbrace, yes_math, "\\{" }, @/ +{ '}', rbrace, yes_math, "\\}" }, @/ +{ ',', comma, yes_math, "," }, @/ +{ ';', semi, yes_math, ";" }, @/ +{ ':', colon, maybe_math, ":" }, @/ +{ '#', insert, maybe_math, "\\#" }, @/ @.\\\#@> + /* this should occur only in macro definitions */ +{ at_sign_image, insert, maybe_math, "@@" }, + /* this should not occur in legal \Cee~text */ +@) +{ not_eq, binop, yes_math, "\\I" }, @/ @.\\I@> +{ lt_eq, binop, yes_math, "\\Z" }, @/ @.\\Z@> +{ gt_eq, binop, yes_math, "\\G" }, @/ @.\\G@> +{ eq_eq, binop, yes_math, "\\E" }, @/ @.\\E@> +{ and_and, binop, yes_math, "\\W" }, @/ @.\\W@> +{ or_or, binop, yes_math, "\\V" }, @/ @.\\V@> +{ plus_plus, unop, yes_math, "\\PP" }, @/ @.\\PP@> +{ minus_minus, unop, yes_math, "\\MM" }, @/ @.\\MM@> +{ minus_gt, select, yes_math, "\\MG" }, @/ @.\\MG@> +{ gt_gt, binop, yes_math, "\\GG" }, @/ @.\\GG@> +{ lt_lt, binop, yes_math, "\\LL" }, @/ @.\\LL@> +{ mul_assign, binop, yes_math, "\\KK*" }, @/ @.\\KK@> +{ div_assign, binop, yes_math, "\\KK/" }, @/ +{ mod_assign, binop, yes_math, "\\KK\\MOD" }, @/ @.\\MOD@> +{ plus_assign, binop, yes_math, "\\KK+" }, @/ +{ minus_assign, binop, yes_math, "\\KK-" }, @/ +{ left_assign, binop, yes_math, "\\KK\\LL" }, @/ +{ right_assign, binop, yes_math, "\\KK\\GG" }, @/ +{ and_assign, binop, yes_math, "\\KK\\AND" }, @/ @.\\AND@> +{ xor_assign, binop, yes_math, "\\KK\\XOR" }, @/ @.\\XOR@> +{ or_assign, binop, yes_math, "\\KK\\OR" }, @/ @.\\OR@> +{ thin_space, insert, yes_math, "\\," }, @/ @.\\,@> +{ pseudo_semi, magic, maybe_math, "" }, @/ +{ force_expr_open, begin_expr, maybe_math, "" }, @/ +{ force_expr_close, end_expr, maybe_math, "" }, @/ +{ join, insert, no_math, "\\J" }, @/ @.\\J@> +{ ellipsis, int_like, yes_math, "\\ldots" }, @/ +{ sh_sh, binop, yes_math, "\\SS" }, @/ @.\\SS@> +{ colon_colon, colcol, yes_math, "\\CC" } + +@ Certain tokens that lead to fixed scraps are not included in the +|trans_ini| array because their translations involve non-character tokens. +Since there are only a few of them the easiest solution is to install each +one explicitly into the |token_trans| array. +@d start_scrap(s,c,m) p=&token_trans[s],p->cat=c, p->mathness=5*(m) +@d end_scrap p->trans=text_ptr, freeze_text(); + +@< Install the translations of tokens involving line breaks @>= +{ scrap* p; + start_scrap(math_break,insert,maybe_math); /* \:\v */ + app(opt), app('0'); end_scrap; +@/start_scrap(line_break,insert,no_math); /* \:/ */ + app(force); end_scrap; +@/start_scrap(end_preproc,rproc,no_math); /* end of preprocessor directive */ + app(force); end_scrap; +@/start_scrap(' ',insert,no_math); /* space within preprocessor directive */ + app(break_space); end_scrap; +@/start_scrap(big_line_break,insert,no_math); /* \:) */ + app(big_force); end_scrap; +@/start_scrap(backup_line,insert,no_math); /* \:\\ */ + app(backup); end_scrap; +@/start_scrap(no_line_break,insert,no_math); /* \:+ */ + app(cancel),app(relax),app(break_space),app(relax),app(cancel); end_scrap; +@/start_scrap(include_preproc,insert,yes_math); /* \:p */ + app(force),app_str("\\ATP"),app(force); end_scrap; @.\\ATP@> +} + +@ When \.{CWEAVE} is compiled with the |DEBUG| switch, it can display +its parsing steps. The order of strings in |cat_name| must match that +in the |enum| declaration in section@#categories@>. + +@c +@:category output@> +#ifdef DEBUG +void print_cat (int c) /* symbolic printout of a category */ +{ static char* cat_name[]= + { "unop", "binop", "op", "select" + , "?", "{", "}", "(", ")", "[", "]", ",", ";", ":", "::", "@@;" + , "subscr", "struct_head", "short_{", "short_struct_head" + , "cmp_stmt", "stmt" + , "function", "function_head", "params", "label" + , "if_head", "if_else_head", "do_head" + , "mod_name", "declarator", "decl", "exp", "for", "do", "if", "else" + , "int", "case", "sizeof", "struct", "return" + , "#{", "#}", "insert", "@@[", "@@]" + }; + if (c<=max_category && c>0) printf(cat_name[c-1]); + else printf ("IMPOSSIBLE"); +} +#endif /* |DEBUG| */ + +@ Another major class of terminal symbols is formed by the reserved words. +If a name exists in the hash table with an |ilk| specifying a reserved word, +then |id_lookup| will return the reserved word when called with that name, +and |C_read| will use the |ilk| to set the category of the resulting scrap. +So all that has to be done is to get all the reserved words into the hash +table with the right ilks initially. The simplest way to do this is to call +|id_lookup| for all reserved words with the proper |ilk| at the beginning of +each run of \.{CWEAVE}. Fortunately there are not too many reserved words. +This code below uses the fact that instead of using pointers to beginning +and end of a string for |id_lookup|, one may also pass a single pointer to a +null-terminated string provided the other pointer is null. + +@^reserved words@> + +@<Store all the reserved words@>= +{ int i; static char* int_likes[]= + { "auto","char","double","extern","float","int","long","register" + , "short","signed","static","unsigned","void" }; + static char* defined_types[] = + { "FILE", "size_t", "ptrdiff_t", "wchar_t" + , "jmp_buf", "sig_atomic_t", "fpos_t", "div_t", "ldiv_t" + , "clock_t","time_t" + , "va_list" + }; + static char* return_likes[]= + {"break","continue","goto","return"}; + int int_like_nr=array_size(int_likes), + defined_type_nr=array_size(defined_types), + return_like_nr=array_size(return_likes); + + for (i=0; i<int_like_nr; ++i) id_lookup(int_likes[i],NULL,int_like); + for (i=0; i<defined_type_nr; ++i) + id_lookup(defined_types[i],NULL,type_defined); + for (i=0; i<return_like_nr; ++i) id_lookup(return_likes[i],NULL,return_like); + + id_lookup("case", NULL, case_like); + id_lookup("const", NULL, const_like); + id_lookup("default", NULL, case_like); + id_lookup("do", NULL, do_like); + id_lookup("else", NULL, else_like); + id_lookup("enum", NULL, struct_like); + id_lookup("for", NULL, while_like); + id_lookup("if", NULL, if_like); + id_lookup("sizeof", NULL, sizeof_like); + id_lookup("struct", NULL, struct_like); + id_lookup("switch", NULL, while_like); + id_lookup("typedef", NULL, typedef_like); + id_lookup("union", NULL, struct_like); + id_lookup("va_dcl",NULL, declaration); + id_lookup("volatile", NULL, const_like); + id_lookup("while", NULL, while_like); + id_lookup("NULL", NULL, NULL_like); + id_lookup("TeX", NULL, TeX_like); + if (C_plus_plus) @<Store reserved words for \Cpp@> +} + +@ The main difference between \Cee\ and \Cpp, as far as \.{CWEAVE} is +concerned, is that the latter has a number of additional reserved words. Most +of them are sufficiently like some \Cee-reserved word (or category) that we +can simply make it behave like that \Cee~symbol, without changing the syntax. +For `\&{new}' and `\&{operator}', some additional syntax rules will be needed +however; nevertheless we do not need to extend the set of syntactic +categories. For `\&{operator}' we abuse the category |case_like|, since its +proper use is rather restricted (`|case|' it is always followed by an +expression, while `|default|', `\&{private}' and its relatives are always +followed by a colon), so there will be no confusion with `\&{operator}', which +is always followed by an operator symbol. + +@<Store reserved words...@>= +{ id_lookup("asm", NULL, int_like); + id_lookup("class", NULL, struct_like); + id_lookup("delete", NULL, sizeof_like); + id_lookup("friend", NULL, int_like); + id_lookup("inline", NULL, int_like); + id_lookup("new", NULL, sizeof_like); + id_lookup("operator", NULL, case_like); + id_lookup("private", NULL, case_like); + id_lookup("protected", NULL, case_like); + id_lookup("public", NULL, case_like); + id_lookup("this", NULL, expression); + id_lookup("virtual", NULL, int_like); +} + +@ There are a few more kinds of elementary scraps that the functions we have +given before can produce, which we mention here for completeness. Ordinary +identifiers get category |expression|, and their names will be expanded on +output as argument to a control sequence that provides the proper formatting. +For strings, constants, verbatim constructions, and \TeX~strings, the +applicable control sequences and the constituent characters (escaped with +backslashes where necessary) are written explicitly into token memory; +their scraps also have category |expression|. Comments are converted to +scraps of category |insert|, and their contents are also stored literally; +in case of `\pb' fragments a reference to a text marked with +|inner_text_flag| is stored, for the production of which the parsing +mechanism has in fact already been invoked. By contrast module names are +stored by reference to the name table, just like identifiers, and their +scraps have category |mod_scrap|; in their case the parsing mechanism may +be called during the {\sl output\/} process, if any `\pb' constructions +occur. + +The final \TeX\ output produced for elementary scraps will often be marked +with special control sequences. Ordinary multi-character identifiers are +enclosed in `\.{\\\\\{}$\,\ldots\,$\.\}' (single character identifiers are +merely preceded by a space; they will be set in math italic), identifiers +whose |ilk| is |TeX_like| will become control sequences that are +also enclosed in `\.{\\\\\{}$\,\ldots\,$\.\}' (to establish italic type), +reserved words are enclosed in `\.{\\\&\{}$\,\ldots\,$\.\}', strings and +all-caps identifiers in `\.{\\.\{}$\,\ldots\,$\.\}', constants in +`\.{\\T\{}$\,\ldots\,$\.\}', and verbatim constructions in +`\.{\\vb\{}$\,\ldots\,$\.\}'. Comments are enclosed in +`\.{\\C\{}$\,\ldots\,$\.\}' and usually followed by `\.{\\6}' (a forced +break), and module names take the form `\.{\\X$n$:}$\,\ldots\,$\.{\\X}' +where |n| is the section number (since module names have +|mathness==yes_math|, there is no danger that the final `\.{\\X}' will +disable a following space when coming from a `\hbox{\.{\v@@< ... @@>\v}}' +construction). + +@* The parsing mechanism. @:title@> +Conceptually the working sequence of scraps is like a deck of cards, in +which we repeatedly replace a sequence of consecutive cards by a single new +card. Since such replacements never increase the number of cards, we can use +sequential allocation for the current sequence of scraps, and our only +difficulty will be how to conveniently fill the holes that might be left after +each reduction step. Now reduction usually takes place near the beginning of +the scrap sequence (assuming that the scrap sequence makes syntactical sense) +because that is where we are looking first, and we want to avoid shifting +down the whole remainder of the scrap sequence each time. Therefore the +sequence of scraps, which initially occupies the positions from |scrap_base| +to |scrap_ptr|, is allowed to have a hole in its middle, the low and high +end of which are pointed to by variables |lo_ptr| and~|hi_ptr|. There is +also a variable that points to the place where reductions are currently +taking place, which is the parsing pointer~|pp|. It will always point into +the area below the hole, and when it approaches the hole so closely that a +potential reduction might involve scraps from above, the situation is +remedied by sliding down scraps to the lower region, effectively raising the +hole. Therefore the scraps in the higher region are those that have never +been considered for a reduction yet. Eventually all scraps have been moved +down (i.e., we have |hi_ptr==scrap_ptr|), and after that has happened a +scrap with category~0 (which is not otherwise used) is copied down to signal +the imminent end of the reduction process. When finally no more rules +match the scraps in the lower region, the parsing stops. + +@< Global variables @>= +scrap_pointer pp; /* current position for reducing scraps */ +scrap_pointer lo_ptr; /* end of sequence of scraps that have been examined */ +scrap_pointer hi_ptr; /* first scrap that has not been examined */ + +@ The |mathness| is an attribute of scraps that says whether their +translation is to be processed in a math mode context or not. Since the +translation can be concatenated from a large number of other scraps, there +can be switches in and out of math inside the translation, and we need to +specify the mathness at each of the boundaries. For some scraps it either +makes no difference whether their translation is processed in math mode or +not, or the required mathness is to be determined by the grammatical context +rather than by the scrap itself. Such scraps have mathness |maybe_math| at +both ends; otherwise a definite mathness is specified at either end. The +least significant pair of bits of the |mathness| field of a scrap controls +the right boundary, and the pair of bits to its left controls the left +boundary. + +@d left_math(a) (a->mathness>>2) +@d right_math(a) (a->mathness&0x3) + +@ If we combine two scraps neither of which has mathness |maybe_math| at its +boundaries, then a `\.\$' is inserted in between if and only if the +mathnesses at the common boundary do not agree; if a scrap with |maybe_math| +joins one with a definite mathness, that mathness is propagated across the +former scrap to its other boundary. In order to implement this, we maintain +two mathness values while building up a text: |init_mathness| and +|cur_mathness| which represent the values at the left and right boundaries +of the part contributed so far; these are local variables of whatever +function is concatenating translations, but they should be called by these +names since they are addressed by the macros below. As a consequence of the +left-to-right order of combining translations, a |maybe_math| scrap that is +combined with scraps with definite mathnesses, will actually be set in the +mode inherited from its left (unless it appears as the leftmost scrap in a +reduction); this can be used to make certain symbols, such as colons, behave +in two slightly different ways depending on their syntactic function. (This +method is not infallible however, as a comment following the symbol will +always force it to be processed in horizontal mode; this happens because +|insert| scraps are tacked onto the scrap before them before any ordinary +reduction can affect it.) + +@ Before scraps requiring some definite mathness are contributed, we invoke +|set_mode(yes_math)| or |set_mode(no_math)| as appropriate; the first time +this happens will determine the value of |init_mathness|. + +@d set_mode(x) +if (cur_mathness==maybe_math) cur_mathness=init_mathness=x; +else if (cur_mathness!=x) {@; app('$'); cur_mathness=x; } +else @; /* expect semicolon */ + +@ The macro |app_trans| is invoked with a |scrap_pointer| as argument, +and appends its translation as a single token; |add_trans| will in addition +to this administrate |init_mathness| and |cur_mathness|, and interpolate +any necessary math shifts. + +@d app_trans(a) app_tok(text_flag+text_index((a)->trans)) +@d add_trans(a) +{ scrap_pointer scr=a; /* so that evaluating |a| may have side effects */ + if (left_math(scr)!=maybe_math) + { if (cur_mathness==maybe_math) init_mathness=left_math(scr); + else if (cur_mathness!=left_math(scr)) app('$'); + cur_mathness=right_math(scr); + } + app_trans(scr); +} + +@ The function call |fuse(s,n)| will concatenate the translations of |n| +scraps starting with |*s|, taking care of the mathnesses, and install the +resulting text into |s->trans|. + +@c +void fuse (scrap_pointer s, int n) +{ int cur_mathness=maybe_math, init_mathness=maybe_math; scrap_pointer p=s; + check_toks(n); check_text(); + do add_trans(p++)@; while (--n>0); /* gather all the translations */ + s->trans=text_ptr; freeze_text(); + s->mathness=(init_mathness<<2)+cur_mathness; +} + +@ An |int_like| identifier following a |struct_like| token, a selection +operator (`|.|' or `|->|'), or the special code \:;, is to be typeset as an +ordinary identifier. The function |make_nonreserved| alters the flag of the +token representing such an identifier occurrence. The scrap representing the +|int_like| identifier should not be formed by any reduction, but come +directly from |C_read|, so in principle we expect the translation of our +scrap to be an unnested text consisting of a single token. However, a +comment (or other |insert|) directly following the |int_like| identifier may +complicate this picture slightly, because |insert| scraps are tacked onto +the previous scrap before it gets the chance to take part in any reduction; +this means our token may be buried inside one or more levels of text +nesting, but still is the very first token of the translation. Any such +levels of nesting are soaked off by the |while| loop below; after this +process a single-token text should remain containing a reserved word, of +which we replace |res_flag| by |id_flag| to make it print as an ordinary +identifier. + +@c +void make_nonreserved (scrap_pointer p) +{ text_pointer q=p->trans; token t; + while (text_flag<=(t=text_begin(q)[0]) && t<inner_text_flag) + q=text_at(t-text_flag); + if (text_end(q)==text_begin(q)+1 && res_flag<=t && t<mod_flag) + text_begin(q)[0] = t-res_flag+id_flag; +} + +@ We wish to mark defining occurrences of identifiers, triggered by the +matching of certain reduction rules. Thanks to the unsurpassed +declaration syntax of~\Cee, it is a non-trivial task to locate the +identifier in question, since it may already have been wrapped up in a more +or less complicated declarator or function heading. Fortunately the +identifier is always the first one occurring in that expression +\unskip\footnote{${}^\dagger$} +{Due to the possible occurrence of the keywords |const| and |volatile| in + strange places within declarations, this is not entirely true. We will + arrange things however in such a way that unless we are forced otherwise by + enclosing parentheses, these keywords will remain outside the text that is + passed to |first_ident|. If we ignore the possibility of specifying + redundant parentheses, this only causes problems when one wishes to + declare certain peculiar things like constant function pointers. +}, so a left-to-right scan through the, possibly nested, text forming the +translation of the expression should reveal it. We accept either an ordinary +identifier or a reserved word; the latter possibility is needed for typedef +declarations. @^recursion@> + +@c +id_pointer first_ident(text_pointer p) +{ token_pointer q; token t; + if (p>=text_ptr) confusion("first_ident"); @.first\_ident@> + for (q=text_begin(p); q<text_end(p); ++q) + if (id_flag<=(t=*q) && t<mod_flag) return id_at(t%id_flag); + else if (text_flag<=t) /* text or inner text */ + {@; id_pointer r=first_ident(text_at(t%id_flag)); + if (r!=NULL) return r; + } + return NULL; +} + +@ In the following situations we want to mark the occurrence of an +identifier as a definition: in a declaration of a variable, as in +`|xref_pointer *r@;|', when a label is declared, as in `|found:|', in the +declaration of a function, as in `|char* f(int n){@t\dots@>@;}|', after a +|struct_like| token (declaring a structure-, union-, or enumeration tag) and +for all identifiers in an enumeration list. This is accomplished by the +invocation of |make_underlined| at appropriate times. + +@c +void make_underlined (scrap_pointer p) + /* underline entry for first identifier in |p->trans| */ +{ id_pointer name=first_ident(p->trans); /* name of first identifier */ + if (name==NULL) return; + /* this happens for unsyntactical things like `|int 3;|' */ + { sixteen_bits head=xref_index(name->xref),* r=&head; int n; + while ((n=xnum(*r)&num_mask)!=0 && n<section_count) r=&xlink(*r); + if (n==section_count) xnum(*r)|=def_flag; + else /* this may happen for one-letter identifiers */ + { make_xref(section_count+def_flag,*r); + if (r==&head) name->xref=xref_ptr; @+ else *r=xref_index(xref_ptr); + } + } +} + +@ We will now consider the how the reduction rules themselves are +represented and used. As we have seen, a rule must define a sequence of +categories for its left hand side, and for its right hand side a category +and a prescription for constructing its translation. In addition, some +categories of the left hand side may be marked as context, so that they will +not take part in the reduction, and there is a way to specify conditional +loading of rules. A few more pieces of information are included for +convenience and efficiency. + +Individual reduction rules are stored in a structure called |reduction|. +It is organised in a way that allows for semi-static initialisation, i.e., +the essential parts of information are stored near the beginning of the +structure or of one of its sub-structures, so that they can be defined by +an initialiser expression, while some further fields are computed from them +and assigned at startup time. Within the fields that are statically +initialised some fields that usually are~0 are put at the end, so that in +the default case they can be omitted from the initialiser. + +The field |id| holds an identification number for the rule, which is used in +debugging. Then follows the left hand side information, consisting of an +array of at most |max_lhs_length| categories (which include those of a +possible context; if less than the maximal number of categories are present +they are padded with zeros), followed by integers |context| and |length|. +The field |context| specifies which categories, if any, form the context: +this can be a sequence of one or more categories at either end of the left +hand side of the rule, but not at both ends. If |context==0| (as is the +case if no explicit initialiser is specified), there are no context +categories; when |context>0|, the first |context| categories form the left +context, and when |context<0|, the last |abs(context)| categories form the +right context. (In practice it is wise to use only token categories (ones +that do not require reduction to be formed) for a right context, unless one +can be quite sure that no unintended reduction will affect the categories +taking part in the intended reduction before the right context has been +reduced.) There must be at least one category that is not part of the +context, lest a ``reduction'' would increase the number of scraps. The right +hand side of a rule specifies a category and a string used as a format to +build up the translation; for the common case that the translation is formed +by concatenating the translations of all scraps of the left hand side (not +including those those of the context), a null pointer may be given instead +of a format string. The following field |mask| can be used to specify +selective loading of rule at startup time: any bit set in it will suppress +loading under some condition dependent on the setting of option flags in the +call of \.{CWEAVE}. The field |displacement| is computed at startup time to +record the number of positions (usually negative) by which the parsing +pointer~|pp| should be changed after application of the rule. + +@d max_lhs_length 4 + +@< Typedef and enumeration declarations @>= + +typedef struct +{ short id; /* for debugging */ + struct + {@; eight_bits category[max_lhs_length]; signed char context,length; } lhs; + struct {@; eight_bits category; char *translation; } rhs; + sixteen_bits mask; + short displacement; +} reduction; + +@ We shall organise the rules in a ``trie'' structure for fast matching. If +|q| points to a trie node reached after matching some sequence of +categories, and that sequence corresponds to the left hand side (including +context) of some rule (which should be unique), then |q->rule| points to +that rule, otherwise |q->rule==NULL|. If that sequence of categories is a +proper prefix of the left hand side of a rule (which may happen whether or +not |q->rule==NULL|), and $c$ is the next category in that left hand side +(which implies |0<c<=max_rule_cat|), then |q->next[c-1]| is the index of the +trie node reached after a further match of~$c$. The entries of |q->next| +that do not correspond to any such successor node are set to~0, which is +unambiguous because the root of the trie does not figure as a successor of +any node. We do not attempt a sparse representation (which would avoid +storage of such 0's), but we do use a compact |eight_bits| representation +for the entries of |q->next|; this saves a considerable amount of space, +since there is a total of |max_rule_cat*max_no_of_nodes| such entries. + +@<Typedef...@>= + +typedef struct {@; reduction* rule; eight_bits next[max_rule_cat]; } trie_node; + +@ Trie nodes are allocated from an array |trie_node_mem|, with the root of +the trie at |trie_node_mem[0]|. We introduce some macros that will help us +find our way around the trie. The address the successor of the trie node +pointed to by |q| for category |c| can be written as |successor(q,c)|. The +absence of such a successor can be found by testing |no_successor(q,c)|. If +|x| is the address of any node in the tree (except the root), then we can +make that node the successor of |q| for category |c| by invoking +|set_successor(q,c,x)|. + +@d trie_root (&trie_node_mem[0]) +@d successor(q,c) + (&trie_node_mem[(q)->next[(c)-1]]) /* category codes start at 1 */ +@d no_successor(q,c) ((q)->next[(c)-1]==0) +@d set_successor(q,c,x) ((q)->next[(c)-1]=(eight_bits)((x)-trie_node_mem)) + +@<Global...@>= +trie_node trie_node_mem[max_no_of_nodes]; +int node_no = 1; /* number of trie nodes allocated */ +#ifdef DEBUG +boolean install_failed=false; +#endif + +@ Trie nodes are allocated in a straightforward sequential way. We don't +trust that uninitialised statically allocated pointers will be |NULL| +(although they should), especially not on machines where |NULL| is not +represented as ``all bits cleared'', so we do a bit of extra work here. For +the entries of the array |next| we expect no problems however (since +|eight_bits| is an integral type), so we do not explicitly initialise them. + +@c trie_node *get_new_trie_node(void) +{ if (node_no>=max_no_of_nodes) + overflow("trie node"); @.trie node capacity exceeded@> + trie_node_mem[node_no].rule=NULL; + return &trie_node_mem[node_no++]; +} + +@ The function |install_rule| installs a reduction, and if \.{CWEAVE} was +compiled with |DEBUG| set, it also performs some checks on the validity of +the rule; if any check fails the variable |install_failed| is set to |true|. +Since |print| has a variable number of arguments, the macro |rule_error| +does not incorporate them but just prepends |print| to the argument list; +the replacement text of |rule_error| can therefore not be parenthesised, and +the macro should be used with some care. + +@d rule_error install_failed=true,print + /* report a problematic syntax rule */ + +@ A global variable |rule_mask| is set at startup time according to the +relevant option flags; any rules~|r| for which |rule_mask & r->mask!=0| are +suppressed. For each set of mutually exclusive settings, a number of bits in +|rule_mask| is reserved equal to the size of the set; the current setting +will have its corresponding bit set while the others are cleared. Therefore +the bits set in |r->mask| specify the option settings for which the rule is +disabled, and the default state |r->rule==0| means that the rule always +applies, regardless of any optional settings. + +@< Global... @>= sixteen_bits rule_mask; + +@ The function |install_rule| enters a rule into the trie structure, and if +|DEBUG| is defined, performs some sanity checks on the rule. + +@< Prototypes @>= +void install_rule (reduction* rule); + +@~The length of rules can be found because category~0 is not used for scraps. +@c +void install_rule(reduction *rule) +{ if ((rule_mask & rule->mask)==0) + { eight_bits* p=rule->lhs.category, i=0; + while (i<max_lhs_length && p[i]!=0) ++i; + rule->lhs.length=i; +#ifdef DEBUG + @< Check left-hand side of |rule| @> + @< Check right-hand side of |rule| @> +#endif + @< Install |rule| in the trie structure @> + @< Compute |rule->displacement|, + and modify |rule->lhs.length| and |rule->context| @> + } +} + +@ The left-hand side should not be only context, and all categories should +be legal ones. + +@< Check left... @>= +{ if (rule->lhs.length<=abs(rule->lhs.context)) + rule_error("\nNo scraps to replace in rule %d.\n", rule->id); + @.No scraps to replace...@> + for(i=0; i<rule->lhs.length; ++i) + if (!valid_cat(p[i])) + rule_error("\nUnknown category %d in LHS of rule %d.\n", p[i], rule->id); + @.Unknown category...@> +} + +@ The right-hand side should have a valid category, and unless its +translation is |NULL| (or |""| which we treat as if it were |NULL|), it +should contain as many times |'_'| as there are non-context categories in +the left-hand side. + +@< Check right... @>= +{ int c=rule->rhs.category; char* s=rule->rhs.translation; + if (!valid_cat(c)) + rule_error("\nUnknown category %d in RHS of rule %d.\n", c, rule->id); + @.Unknown category...@> + if (s!=NULL) + { if (*s=='\0') s=rule->rhs.translation=NULL; /* replace empty string */ + else + { i=0; + do + if (*s!='p') i+= *s++=='_'; /* count underscores */ + else if (++s,isdigit((eight_bits)*s)) ++s; /* skip digit and advance */ + else rule_error("\nDigit should follow 'p' in format of rule %d.\n" + @.Digit should follow 'p'...@> , rule->id); + while (*s!='\0'); + if (i!=rule->lhs.length-abs(rule->lhs.context)) + rule_error("\nCount of '_' not equal to length LHS in rule %d.\n" + @.Count of '\_' ...@> , rule->id); + } + } +} + +@ Since trie nodes are not represented sparsely, insertion is easy. + +@<Install |rule|...@>= +{ trie_node* q=trie_root; + for (i=0; i<rule->lhs.length; ++i) + { if (no_successor(q,p[i])) set_successor(q,p[i],get_new_trie_node()); + q=successor(q,p[i]); + } +#ifdef DEBUG + if (q->rule!=NULL) + rule_error("\nIdentical left-hand sides in rules %d and %d.\n" + @.Identical left-hand sides...@> , q->rule->id, rule->id); +#endif + q->rule=rule; +} + +@ We compute |displacement| conservatively, based on local considerations; +alternatively we might also consider the whole set of rules to find +larger (less negative) values that would make parsing go a bit faster. A +rule can have a left hand side of length |max_lhs_length|. This means that +it is safe to move |pp| so that it will afterwards be |max_lhs_length-1| +positions to the left of the first scrap that may have got a new category. + +After a rule has been installed, there is no need to record the full length +of the left hand side, including context, since this is implicit from the +place in the trie where the pointer to this rule is located; rather we store +the number of scraps that will be replaced. Similarly it is more useful to +know the offset of the first scrap to be replaced (which is~0 in case of a +right context) rather than the value of |context| as stored at +initialisation. + +@< Compute |rule->displacement|... @>= +{ int k=rule->lhs.context,d; + if (k<0) {@; rule->lhs.length+=k; k=0; } @+else rule->lhs.length-=k; + d=1-max_lhs_length+k; /* this cannot be positive */ + if (rule->lhs.category[k]==rule->rhs.category) /* no category change */ + { ++d; +#ifdef DEBUG + if (rule->lhs.length==1) + rule_error("\nNo categories change in rule %d.\n", rule->id); + @.No categories change...@> +#endif + } + rule->lhs.context=k; + rule->displacement=d; /* if positive, an error was reported */ +} + +@ The function |match| tests whether the category pointed to by~|p|, +and its successors, match the categories in the trie structure, starting +at the root of the trie, and up to a node that contains a rule. +Multiple matches are possible, in which case the longest one takes +precedence. We avoid using a side effect in the argument of |valid_cat|. + +@c +reduction *match (scrap_pointer p) +{ trie_node* q=trie_root; reduction* rule=NULL; int c; + while (c=p++->cat,valid_cat(c) && !no_successor(q,c)) + if ((q=successor(q,c))->rule!=NULL) rule=q->rule; + return rule; +} + +@ When a matching rule has been found, the function |reduce| is called to +perform the corresponding actions. At that point |pp| points to the first +scrap involved in the match, and the argument |rule| to |reduce| points to +the matching rule. + +If a rule has a left hand side of length~1 (not counting context) and also +the default translation (plain concatenation), then all that is to be done +is to change the category of a scrap, and part of the processing can be +skipped. + +@c +void reduce (reduction* rule) +{ int k=rule->lhs.context, l=rule->lhs.length; + scrap_pointer s = pp+k, p=s;/* position of the new scrap */ + char* f=rule->rhs.translation; /* format string for translation */ + + s->cat=rule->rhs.category; + + if (l>1 || f!=NULL) /* otherwise ready already */ + { if (f==NULL) fuse(s,l), p+=l; /* default translation */ + else @<Generate token list according to format string |f| @> + if (l>1) @<Fill vacant scrap positions@> + } + + @<Print a snapshot of the scrap list if debugging@> + @<Change |pp| to |max(pp+rule->displacement,scrap_base)| @> +} + +@ When we have applied a reduction to the sequence of scraps, we usually +remove scraps (we never create more scraps than we remove), thereby creating +a small ``hole'' in the sequence. We fix that hole by sliding scraps +downward across it, thereby moving the hole upwards, until it reaches the +``official'' hole at |lo_ptr|; then |lo_ptr| is adjusted so that the small +hole is incorporated in the official hole. During the translation process +the pointer~|p| was moved across all scraps that took part in the reduction, +so the scraps to move are at positions~|i| with |p<=i<lo_ptr|. + +@<Fill vacant...@>= +{ scrap_pointer q=s+1; /* position after the newly formed scrap */ + while (p<lo_ptr) *q++=*p++; + lo_ptr=q; +} + +@ Here we are careful not to create any pointers below |scrap_base|, +since they are not guaranteed to exist (but mentioning them in the module +name will do no harm). + +@<Change |pp| to...@>= +if (pp<scrap_base-rule->displacement) pp=scrap_base; @+ +else pp+=rule->displacement; + +@ We need no extensive coding mechanism for describing translations, since +they all follow a similar pattern. In all cases all the translations of the +scraps in the left hand side (not including the context scraps) are used +exactly once, in left to right order (violation of these principles would +result in very strange effects indeed for the printed output). The only +things that need to be added are formatting controls like |indent|, |force| +or |break_space|, math shifts (but these are already taken care of by +|add_trans|), and white space. We may also specify calls of +|make_underlined| or |make_unreserved| for certain scraps in the left hand +side. Although the items inserted are of a modest variety, one should +realise that their presence is the only reason we need to parse at all; +without them the translations could have been computed by purely lexical +means. + +In format strings an underscore indicates the translation of the next scrap +of the left hand side. Other characters each encode a formatting control; the +character `\.p' encodes |opt| and is followed by a digit that becomes its +argument. The characters `\.!' and `\.\$' respectively cause +|make_underlined| and |make_nonreserved| to be called for the next scrap; +`\.\ ' and~`\.\~' produce a space in the translation, where the latter is +non-breakable and the former forces horizontal mode. To just force +horizontal or math mode there are `\.h'~and~`\.m'; the latter avoids the +possibility of a completely empty formula by adding a space inside math +mode. The precise meaning of these and other formatting characters is easily +read off from the code below. The `\.o' and `\.r' format characters (the +latter is used only in compatibility mode) affect the math category used by +\TeX\ for the next symbol (character or control sequence); the syntax rules +using them do not put braces around that symbol, since these could also +capture a following comment, causing a \TeX\ error. + +The number of free tokens we require to be available by calling |check_toks| +is a conservative estimate, based on a hypothetic ``worst case'' reduction +rule with a left hand side of length~4 and with 8 additional items in its +translation (counting format codes with multi-character translations with +multiplicity), whose mathnesses alternate, so that a maximal number of math +shifts is required. + +@< Generate token list... @>= +{ int cur_mathness=maybe_math, init_mathness=maybe_math; + check_toks(23); check_text(); + do + switch (*f++) @:format@> + { case '+': app(indent); break; + case '-': app(outdent); break; + case 'p': app(opt); app(*f++); break; + case 'f': set_mode(no_math); app(force); break; + case 'F': set_mode(no_math); app(big_force); break; + case 'b': set_mode(no_math); app(backup); break; + case 'B': set_mode(no_math); app(break_space); break; + case 't': set_mode(yes_math); app_str("\\a"); break; @.\\a@> + /* next item in tab space */ + case ',': set_mode(yes_math); app_str("\\,"); break; @.\\,@> + /* thin space */ + case 'h': set_mode(no_math); break; /* force horizontal mode */ + case 'm': set_mode(yes_math); app(' '); break; + /* force math mode, avoid `\.{\$\$}' */ + case 'o': set_mode(yes_math); app_str("\\m"); break; @.\\m@> + /* make ``mathord'' */ + case 'r': set_mode(yes_math); app_str("\\MRL"); break; @.\\MRL@> + /* make ``mathrel'' */ + case '!': make_underlined(p); break; + case '$': make_nonreserved(p); break; + case ' ': set_mode(no_math); app(' '); break; + case '~': app('~'); break; + default: printf("%c: ",f[-1]); + confusion("illegal character in format string"); + case '_': add_trans(p++); + } + while (*f!='\0'); + s->trans=text_ptr; freeze_text(); + s->mathness=(init_mathness<<2)+cur_mathness; +} + +@ We have now seen how a match is made, and what is done once a matching +rule is found; we still have to consider how everything is set up properly, +and how rules are repeatedly applied until no more reduction is possible, +responding properly to successful and failing matches. All this is +performed by the function |translate|; as we have seen it is called by +|do_C| and |finish_C| after scraps have been stored from |scrap_base| +to |scrap_ptr| of the |scrap_info| array, and it returns a pointer to the +text representing the result of parsing all those scraps. + +We start with appending a dummy scrap if either no scraps are present at +all, or some tokens remain that have not been packed into a scrap yet; the +latter can only be due to `\.{@@t...@@>}' as a final item in compatibility +mode. Then we set |lo_ptr| and |hi_ptr| appropriately, and begin to apply +rules as long as possible. When this is done and more than one scrap +remains, their translations are wrapped rather bluntly together to a single +text. + +@c +text_pointer translate (void) /* converts a sequence of scraps */ +{ pp=lo_ptr=hi_ptr=scrap_base; + if (scrap_ptr==pp || dangling_tokens()) /* then append dummy scrap */ + {@; check_scrap(); pack_scrap(insert,no_math); } + @< If tracing, print an indication of where we are @> + @< Reduce the scraps using the rules until no more rules apply @> + @< Combine the irreducible scraps that remain, + and |return| the resulting text @> +} + +@ Before applying |match|, we must make sure it has good input (at least +|max_lhs_length| scraps). If a match at |pp| exists, |reduce| will perform +the required processing and updating of |pp| (in this case |pp| is +certainly not increased), if not, we move to the right and try again. + + +@< Reduce the scraps... @>= +do +{ reduction *rule; + @< Make sure the entries |pp| through |pp+max_lhs_length-1| + of |scrap_info| are defined, or that |lo_ptr->cat==0| @> + if ((rule=match(pp))!=NULL) reduce(rule); + else + {@; ++pp; + @< Take special action if |pp->cat| is |end_expr| or |rproc| @> + } +} +while (pp<lo_ptr); + +@ If we get to the end of the scrap list, we set |lo_ptr->cat=0|, which will +prevent any matches to scraps beyond those that are actually present. All +scraps of category |insert| pass across the hole between |lo_ptr| and +|hi_ptr|, and we take the opportunity to remove them, tacking their +translation to the scrap below the hole. We even make sure that +|hi_ptr->cat!=insert|, so that the scraps with which the |insert| scrap is +combined will not have undergone any ordinary reduction yet. The only +possible remaining |insert| scrap is one at the very start of the list; it +will be handled at the end of all reductions. + +@< Make sure the entries... @>= +{ scrap_pointer lo_min = pp+max_lhs_length; + while (lo_ptr<lo_min && lo_ptr->cat!=0) + if (hi_ptr>=scrap_ptr) lo_ptr->cat=0; + else + { *lo_ptr++ = *hi_ptr++; + while (hi_ptr<scrap_ptr && hi_ptr->cat==insert) + {@; *lo_ptr = *hi_ptr++; fuse(lo_ptr-1,2); } + } +} + +@ The category pairs |lproc|--|rproc| and |begin_expr|--|end_expr| are +special in that the don't occur in any rules, but rather serve only as +markers. When the material in between will not reduce any further the whole +construction will be wrapped up, their translations concatenated, and the +result treated as an |insert| respectively an |expression|. We do not +actually form an |insert| scrap if it is not at the start of the scrap +sequence, but rather combine everything directly with the scrap immediately +before the |lproc| scrap, leaving the category of that scrap as it is; this +is necessary, because the code that combines ordinary |insert| scraps with +their predecessor only looks at scraps when they cross the hole from |hi_ptr| +to~|lo_ptr|. We use here that |rproc-lproc==end_expr-begin_expr==1|. The +variable names |s| and |p| are chosen with the same meaning as in |reduce|, +so that we could reuse a module. + +@< Take special action... @>= +if (pp->cat==end_expr || pp->cat==rproc) +{ int start=pp->cat-1; /* the opening category matching |pp->cat| */ + scrap_pointer s=pp, p=pp+1; + while ((--s)->cat!=start && s>scrap_base) {} + if (s->cat==start) /* if opening symbol is missing, take no action */ + { if (start==begin_expr) s->cat=expression; + else if (s==scrap_base) s->cat=insert; @+ + else --s; /* position of new scrap */ + fuse(s,(int)(p-s)); + @< Fill vacant scrap positions @> /* using values of |p| and |s| */ + pp= s-scrap_base<max_lhs_length ? scrap_base : s+1-max_lhs_length; + } +} + +@ If the initial sequence of scraps does not reduce to a single scrap, +we concatenate the translations of all remaining scraps, separated by +blank spaces, with dollar signs surrounding the translations of scraps +where appropriate. Because of this last action we must build a new text +even if only one scrap remains. + +@< Combine the irreducible... @>= +{ scrap_pointer j; + if (scrap_base->cat==insert && lo_ptr>scrap_base+1) + { fuse(scrap_base,2); /* merge initial |insert| into the next scrap */ + j=scrap_base; j->cat=j[1].cat; --lo_ptr; while (++j<lo_ptr) *j=j[1]; + } + @<If semi-tracing, show the irreducible scraps@> + check_toks(1); + for (j=scrap_base; j<lo_ptr; j++) + { if (j!=scrap_base) app_char_tok(' '); + if (left_math(j)==yes_math) app('$'); + app_trans(j); + if (right_math(j)==yes_math) app('$'); + } + freeze_text(); + return text_ptr-1; +} + +@ If \.{CWEAVE} is compiled with |DEBUG| defined, it can be put into +debugging mode by setting |tracing| to a positive value by means of \:1, +\:2, or \:3. When |tracing| is set to level~1 any sequence of two or more +irreducible scraps remaining at the end of a call to |translate| will be +printed out. When |tracing| is set to level~2, the rule numbers and current +scrap categories will be printed out after each reduction that takes place, +and if it is set to level~3, then mathnesses at the boundaries of scraps +will also be indicated. + +@<Global...@>= +#ifdef DEBUG +int tracing; /* how much parsing details to show */ +#endif + +@ When parsing fails to reduce everything to a single scrap, pleasing results +will probably not be obtained; it is therefore advisable to run +\.{CWEAVE} with |tracing==trace1| before a final version of a |CWEB| +program is fixed. In order to allow this without changing the source file +itself, we initialise |tracing| to |trace1| if the flag `\.{+d}' is supplied +to \.{CWEAVE}. + +@< Set initial values @>= +#ifdef DEBUG +tracing = flags['d'] ? trace1 : trace0; +#endif + +@ The following code is activated by the `\.{+d}' flag or the \:1 control +code. + +@<If semi-tracing, show the irreducible scraps@>= +#ifdef DEBUG +{ if (tracing==trace1 && lo_ptr>=scrap_base+2) + { print("\nIrreducible scrap sequence at line %d in section %d:\n" + @.Irreducible scrap sequence...@> ,cur_line, section_count); + mark_harmless(); + for (j=scrap_base; j<lo_ptr-1; j++) + print_cat(j->cat), putchar(' '); + print_cat(j->cat); new_line(); /* |term_line_empty| is still valid */ + } +} +#endif + +@ When full tracing is enabled the following message indicates which piece +of \Cee~text is being parsed (but for section bodies it will generally show +the first line of the next section, since that has already been fetched). + +@<If tracing,...@>= +#ifdef DEBUG +{ if (tracing>=trace2) + { print("\nTracing after l.%d:\n", cur_line); @.Tracing after...@> + if (loc>buffer+50) /* shorten long lines to keep within margins */ + {@; printf("..."); term_write (loc-50,50); } + else term_write(buffer,loc-buffer); + new_line(); /* |term_line_empty| is still valid */ + } +} +#endif + +@ After each reduction, full tracing will print a line starting with the +rule number, followed by a display of all the categories of scraps which +have been considered until now, i.e., those at positions below |lo_ptr|. +The scrap that was produced by this reduction, which is pointed to by~|s|, +has its category highlighted by enclosing it in inverted angle brackets. +If tracing is set to~3, extra-full tracing is active, and mathnesses at the +boundaries of scraps are indicated. + +@d math_char(x) ((x)==yes_math ? '+' : (x)==no_math ? '-' : '?') + +@<Print a snapsh...@>= +#ifdef DEBUG +{ scrap_pointer k; /* pointer into |scrap_info| */ + if (tracing>=trace2) + { print("\n%3d:", rule->id); + for (k=scrap_base; k<lo_ptr; k++) + { putchar (' '); + if (tracing==trace3) putchar(math_char(left_math(k))); + if (k==s) putchar('>'), print_cat(k->cat), putchar('<'); + else print_cat(k->cat); + if (tracing==trace3) putchar(math_char(right_math(k))); + } + print("%s\n", hi_ptr<scrap_ptr ? " ..." : "."); + } +} +#endif + diff --git a/web/c_cpp/cwebx/rules.w b/web/c_cpp/cwebx/rules.w new file mode 100644 index 0000000000..d58144e99e --- /dev/null +++ b/web/c_cpp/cwebx/rules.w @@ -0,0 +1,701 @@ +@* Rules of the grammar. @:title@> +\def\:#1{`\.{@@#1}'}% for in case this file is processed in isolation +We first arrange the proper setting of |rule_mask|, which will control the +selection of rules actually used. Recall that any bits set in the mask of a +rule prescribe its {\it suppression\/} when the same bit is set in +|rule_mask|; therefore for instance the bit characterising \Cpp\ is called +|no_plus_plus|, so that rules specifying it will not be loaded for \Cpp. In +some cases two masks will be combined using the bitwise-or operator `|@v|', +this means (somewhat counterintuitively) that the rule will only be selected +if the conditions represented by the two masks are {\it both\/} satisfied. +The use of the bitwise-and operator `|&|' is even more exceptional: it is +only meaningful if its two operands both select one setting of the same +three-way switch; the rule will then be selected if that switch is in either +of the two indicated positions. The |merged_decls| flag is special in that +setting `\.{+m}' only enables an extra rule, but does no disable any rules; +therefore only one bit is used for this option, and raising this bit in +|rule_mask| suppresses the rule marked with |merged_decls|. + +@d cwebx 0x0001 /* use normally */ +@d compatibility 0x0002 /* use in compatibility mode */ +@d only_plus_plus 0x0004 /* use in \Cpp\ */ +@d no_plus_plus 0x0008 /* use in ordinary \Cee\ only */ +@d unaligned_braces 0x0050 /* use if `\.{+u}' flag was set */ +@d aligned_braces 0x0020 /* use unless `\.{+u}' flag was set */ +@d wide_braces 0x0030 /* use if `\.{+w}' set */ +@d standard_braces 0x0060 /* use unless `\.{+u}' or `\.{+w}' set */ +@d merged_decls 0x0080 /* use if `\.{+m}' set */ +@d forced_statements 0x0100 /* use if `\.{+a}' or `\.{+f}' set */ +@d no_forced_statements 0x0600 /* use unless `\.{+a}' or `\.{+f}' set */ +@d all_stats_forced 0x0300 /* use if `\.{+a}' set */ +@d not_all_stats_forced 0x0400 /* use unless `\.{+a}' set */ + +@< Set initial values @>= +rule_mask= (compatibility_mode ? 0x0001 : 0x0002) + | (C_plus_plus ? 0x0008 : 0x0004) + | (flags['w'] ? 0x0040 : flags['u'] ? 0x0020 : 0x0010) + | (flags['m'] ? 0x0000 : 0x0080) + | (flags['a'] ? 0x0400 : flags['f'] ? 0x0200 : 0x0100) + ; +{ static reduction rule[] = { @< Rules @>@;@; }; +@/int i=array_size(rule); @+ do install_rule(&rule[--i]); while (i>0); +#ifdef DEBUG + if (install_failed) fatal("inconsistent grammar",0); +#endif +} + +@ {\it Expressions}. @:rules@> +These rules should be obvious. Rule~5 allows typedef identifiers to be used +as field selectors in structures; rules 7~and~8 attach a parameter list in a +function call. In rule~14 we prefix a potentially binary operator such as +`|*|' that is used in a unary way by a `\.{\\mathord}' command to make sure +that \TeX\ will not mistake it for a binary operator. In simple cases such +as |*p| this is redundant, but if such operators are repeated more than one +level deep, as in |**p|, \TeX\ would otherwise treat the first operator as +the left operand of the second, and insert the wrong spacing. Moreover, +typical \Cee~constructions as a cast |(void*) &x| or a declaration +|char *p@;| would confuse \TeX\ even more. In rule~13 we need not insert +`\.{\\mathord}', since operators of category |unop| are already treated as +ordinary symbols by~\TeX. + +@< Rules @>= +{ 1, {{expression, unop}}, {expression, NULL}}, @/ +{ 2, {{expression, binop, expression}}, {expression, NULL}}, @/ +{ 3, {{expression, unorbinop, expression}}, {expression, NULL}}, @/ +{ 4, {{expression, select, expression}}, {expression, NULL}}, @/ +{ 5, {{expression, select, int_like}}, {expression, "__$_"}}, @/ +{ 6, {{expression, comma, expression}}, {expression, "__p1_"}}, @/ +{ 7, {{expression, expression}}, {expression, NULL}}, @/ +{ 8, {{expression, lpar, rpar}}, {expression, "__,_"}}, @/ +{ 9, {{expression, subscript}}, {expression, NULL}}, @/ +{10, {{lpar, expression, rpar}}, {expression, NULL}}, @/ +{11, {{lbrack, expression, rbrack}}, {subscript, NULL}}, @/ +{12, {{lbrack, rbrack}}, {subscript, "_,_"}}, @/ +{13, {{unop, expression}}, {expression, NULL}}, @/ +{14, {{unorbinop, expression}}, {expression, "o__"}}, @[@] + +@~Here are some less common kinds of formulae. Processing the colon belonging +to the question mark operator in math mode will give it the proper spacing, +which is different from that of a colon following a label. Rule~21 processes +casts, since the category |parameters|, which represents parenthesised lists +specifying function argument types, encompasses the case of a single +parenthesised type specification. The argument of |sizeof| may be a type +specification rather than an expression; in \Cee\ (unlike \Cpp) it then must +be parenthesised. %, but not in \Cpp\ (and |sizeof_like| might be `\&{new}'). + +@< Rules @>= +{20, {{question, expression, colon}}, {binop, "__m_"}}, @/ +{21, {{parameters, expression}}, {expression, "_,_"}}, @/ +{22, {{sizeof_like, parameters}}, {expression, NULL}}, @/ +{23, {{sizeof_like, expression}}, {expression, NULL}}, @/ +{24, {{sizeof_like, int_like}}, {expression,"_~_"},only_plus_plus}, @[@] + +@ {\it Declarations}. +In a declaration in \Cee, the identifier being declared is wrapped up in a +declarator, which looks like an expression of a restricted kind: only prefix +asterisk, postfix subscript and formal parameters, and parentheses are used. +In a bottom-up parser of the kind we are using, it is natural, and hardly +avoidable, that declarators are parsed as expressions. Therefore we start +recognising a declaration when we see a type specifier followed by the first +declarator; at that point we have a succession `|int_like| |expression| +|semi|' or `|int_like| |expression| |comma|' (rules 31~and~33). It is also +possible that there are no declarators at all, namely when a |struct|, +|union|, or~|enum| specifier is introduced without declaring any variables; +in that case we have `|int_like| |semi|' (rule~32). Because the type +specifier might be composite, like |unsigned long int|, and there might +moreover be storage class specifiers and type modifiers (like `|const|'), we +first contract any sequence of |int_like| items to a single one (rule~30). +In case the declarator was followed by a comma we reduce to |int_like|, so +that the next declarator can be matched, otherwise we reduce to +|declaration|. + +It is not quite true that declarators always look like expressions, since +the type modifiers `|const|' and `|volatile|' may penetrate into +declarators. When they do they will almost always be preceded by an +asterisk, and rule~34 will treat such cases. The choice for |int_like| as +the result category is not completely obvious, since it makes the modifier +and the preceding asterisk part of the type specifier rather than of the +declarator, which strictly speaking is not correct; the choice for |unop| or +|unorbinop| might therefore seem a more logical one. One reason for not +doing that is that a space would have to be inserted in the translation +after the modifier scrap, which would not look right in abstract declarators +for contrived cases like \hbox{|int f(char *const)@;|}; more importantly, if +the modifier would become part of the declarator, it would be a (reserved) +identifier that precedes the identifier actually being declared, and when +the declarator then receives a call from |make_underlined| by rule 31~or~33, +it would mislead |first_ident|. The current solution has a small flaw as +well, since it cannot handle the situation where the modifier is separated +from the type specifier by a parenthesis, as in +$\&{void}~(\m*\&{const}~\m*f)~(\&{int})$; such cases are quite uncommon, are +hard to handle by rules that will not spuriously match in other situations, +and even then they would still cause problems with |make_underlined|, so we +do not attempt to handle them. + +@< Rules @>= +{30, {{int_like, int_like}}, {int_like, "_~_"}}, @/ +{31, {{int_like, expression, semi}}, {declaration, "_~!__"}}, @/ +{32, {{int_like, semi}}, {declaration, NULL}}, @/ +{33, {{int_like, expression, comma}}, {int_like, "_~!__p1"}}, @/ +{34, {{unorbinop, int_like}}, {int_like, "o__"}}, @[@] + +@ If a typedef identifier is simultaneously used as a field selector in a +|struct| or |union| declaration, it must be made to parse as expression and +be printed in italic type; this can be achieved by placing the magic wand +\:; before the identifier, by rule~35. The reason that we place \:; at the +beginning rather than at the end of the construction here, is to prevent the +|int_like| identifier from combining with something before it first. +Rule~35 only applies if the \:; does not match by any rule with what comes +before it. + +Rule~36 handles the case that a function is declared with specified argument +types, which is not handled by the expression syntax given until now. It +also parses new-style (\caps{ANSI/ISO}) headings of function definitions; in +that case, the resulting |function_head| will not be incorporated into a +|declaration| (unless a comma or semicolon follows) but rather into a +|function|. If the parameter specifications include identifiers (as in the +case of function headings), the arguments look like declarations without the +final semicolon; rule~37 (with aid of rule~33) constructs such parameter +lists. Parameter specifications using abstract declarators (without +identifiers) will be treated below. In |struct| declarations we may +encounter bit-field specifications with or without an identifier; these are +handled by rules 38~and~39 (the constant expression following the colon will +later receive a spurious call from |make_underlined|, but in case of numeric +constants this does no harm). + +@< Rules @>= +{35, {{magic, int_like}}, {expression, "_$_"}}, @/ +{36, {{expression, parameters}}, {function_head, "_B_"}}, @/ +{37, {{lpar, int_like, expression, rpar}}, {parameters, "_+++_~!_---_"}},@/ +{38, {{int_like, expression, colon}}, {int_like, "_~!_m_"}}, @/ +{39, {{int_like, colon}}, {int_like, "_m_"}}, @[@] + +@ Abstract declarators are used after type specifiers in casts and for +specifying the argument types in declarations of functions or function +pointers. They are like ordinary declarators, except that the defined +identifier has been ``abstracted''; an example is `|**(* )(int)|' in `|void +g(char**(* )(int))@;|', which tells that |g| takes as argument a pointer to +a function with |int| parameter yielding a pointer to pointer to |char|. A +difficulty with abstract declarators is that they are built up around the +vacuum left by abstracting the identifier, and since for more than one +reason we cannot allow rules with empty left hand side, we have to find an +alternative way to get them started. + +The natural solution to this problem is to look for sequences that can only +occur if an identifier has been abstracted from between them, for instance +`\.{*)}' (in categories: |unorbinop| |rpar|). The most compelling reason why +in |C_read| we had to laboriously change the category of a |type_defined| +identifier to |expression| instead of |int_like| inside its defining typedef +declaration, is that it allows us to ensure that any remaining |int_like| +scrap that is followed by a |subscript| is a sure sign of an abstract +declarator. + +Here are the cases that start off abstract declarators (these are the first +examples of rules that need context categories in their left hand side). As +a visual hint to the reader we leave a little bit of white space on the spot +where the identifier has vanished. Rules 40~and~41 handle declarators for +pointer arguments, where the vanished identifier is preceded by an asterisk, +which either stands at the end of the declarator, or is parenthesised (for +function pointer arguments). In these rules there is no need to prefix the +asterisk with `\.{\\mathord}', since the right context makes an +interpretation as binary operator impossible. Rules 42~and~43 treat +declarators for arrays, possibly of pointers; there are no corresponding +rules with |parameters| instead of |subscript| since abstract declarators +never specify functions themselves, only function pointers. In fact the +``function analogue'' of rule~43 would incorrectly match a cast following an +operator like `|*|' or `|-|'. Rule~44 treats an abstract declarator +consisting of subscripts only, which are redundantly parenthesised; here too +the corresponding pattern with |parameters| is not only never needed, it +would also spuriously trigger on parenthesised expressions that start with a +cast. + +@< Rules @>= +{40, {{unorbinop, rpar}, -1}, {declarator, "_,"}}, @/ +{41, {{unorbinop, comma},-1}, {declarator, "_,"}}, @/ +{42, {{int_like, subscript},1}, {declarator, ",_"}}, @/ +{43, {{unorbinop, subscript},1}, {declarator, ",_"}}, @/ +{44, {{lpar, subscript},1}, {declarator, ",_"}}, @[@] + +@~ Abstract declarators may grow just like ordinary declarators, to include +prefixed asterisks, as well as postfixed subscripts and parameters, and +grouping parentheses. + +@< Rules @>= +{45, {{unorbinop, declarator}}, {declarator, "o__"}}, @/ +{46, {{declarator, subscript}}, {declarator, NULL}}, @/ +{47, {{declarator, parameters}}, {declarator, NULL}}, @/ +{48, {{lpar, declarator, rpar}}, {declarator, NULL}}, @[@] + +@~ Here is how abstract declarators are assembled into |parameters|, keeping +in mind that the ``abstract declarator'' might be completely empty (i.e., +absent) as in `|void f(int);|' (rules 51~and~53). We put no space after the +type specifier here, since it is followed either by an abstract declarator, +a right parenthesis or comma, so certainly not by an identifier; therefore a +space is neither necessary, nor would it improve readability. The +\caps{ANSI/ISO} syntax allows empty parentheses as a parameter specification +in abstract declarators, although this is an old-style form; rule~54 has +been included to handle this case. Fortunately a parenthesised list of +identifiers (which would parse as |expression|) is not allowed as parameter +specification. + +@< Rules @>= +{50, {{lpar, int_like, declarator, comma}}, {lpar, "____p5"}}, @/ +{51, {{lpar, int_like, comma}}, {lpar, "___p5"}}, @/ +{52, {{lpar, int_like, declarator, rpar}}, {parameters, NULL}}, @/ +{53, {{lpar, int_like, rpar}}, {parameters, NULL}}, @/ +{54, {{lpar, rpar}}, {parameters, "_,_"}}, @[@] + +@ {\it Structure, union, and enumeration specifiers}. It is permissible to +use typedef identifiers as structure, union, or enumeration tags as well, so +we include cases where an |int_like| follows a |struct_like| token. In \Cpp, +we may also find things like `\&{private}:' in a class specifier; these are +parsed just like `|default:|', i.e., as a |label| (rule~66). + +@< Rules @>= +{60, {{struct_like, lbrace}}, {struct_head, "_ft_"},standard_braces}, @/ +{60, {{struct_like, lbrace}}, {struct_head, "_~_"},unaligned_braces}, @/ +{60, {{struct_like, lbrace}}, {struct_head, "_f_"},wide_braces}, @/ +{61, {{struct_like, expression, lbrace}}, + {struct_head, "_~!_ft_"},standard_braces}, @/ +{61, {{struct_like, expression, lbrace}}, + {struct_head, "_~!_~_"},unaligned_braces}, @/ +{61, {{struct_like, expression, lbrace}}, + {struct_head, "_~!_f_"},wide_braces}, @/ +{62, {{struct_like, int_like, lbrace}}, + {struct_head, "_~!$_ft_"},standard_braces|no_plus_plus}, @/ +{62, {{struct_like, int_like, lbrace}}, + {struct_head, "_~!_ft_"},standard_braces|only_plus_plus}, @/ +{62, {{struct_like, int_like, lbrace}}, + {struct_head, "_~!$_~_"},unaligned_braces|no_plus_plus}, @/ +{62, {{struct_like, int_like, lbrace}}, + {struct_head, "_~!_~_"},unaligned_braces|only_plus_plus}, @/ +{62, {{struct_like, int_like, lbrace}}, + {struct_head, "_~!$_f_"},wide_braces|no_plus_plus}, @/ +{62, {{struct_like, int_like, lbrace}}, + {struct_head, "_~!_f_"},wide_braces|only_plus_plus}, @/ +{63, {{struct_like, expression}}, {int_like, "_~_"}}, @/ +{64, {{struct_like, int_like}}, {int_like, "_~$_"},no_plus_plus}, @/ +{64, {{struct_like, int_like}}, {int_like, "_~_"},only_plus_plus}, @/ +{65, {{struct_head, declaration, rbrace}}, + {int_like, "_+_-f_"},standard_braces}, @/ +{65, {{struct_head, declaration, rbrace}}, + {int_like, "_+f_-f_"},unaligned_braces & wide_braces}, @/ +{66, {{label, declaration}}, {declaration, "b_f_"},only_plus_plus}, @[@] + +@ Rules 67--70 are for enumerations; they avoid forced line breaks and call +|make_underlined| for all the enumeration constants. + +@< Rules @>= +{67, {{struct_like, lbrace, expression},-1}, {struct_head, "_B_"}}, @/ +{68, {{struct_like, expression, lbrace, expression},-1}, + {struct_head, "_~_B_"}}, @/ +{69, {{struct_head, expression, comma, expression},1}, + {expression, "__B!_"}}, @/ +{70, {{struct_head, expression, rbrace}}, {int_like, "_~+!_-B_"}}, @[@] + +@ The following rules are added to allow short structure and union +specifiers to be kept on one line without having to repeatedly specify \:+. +The idea is to place \:; after the left brace; this will cause the rules +below to be invoked instead of those above, which avoids introducing forced +line breaks. + +@< Rules @>= +{71, {{struct_like, lbrace, magic}}, {short_struct_head, "_B__+"}}, @/ +{72, {{struct_like, expression, lbrace, magic}}, + {short_struct_head, "_~!_B__+"}}, @/ +{73, {{struct_like, int_like, lbrace, magic}}, + {short_struct_head, "_~!$_B__+"}, no_plus_plus}, @/ +{73, {{struct_like, int_like, lbrace, magic}}, + {short_struct_head, "_~!_B__+"}, only_plus_plus}, @/ +{74, {{short_struct_head, declaration}}, {short_struct_head, "_B_"}}, @/ +{75, {{short_struct_head, rbrace}}, {int_like, "_-B_"}}, @[@] + + +@ {\it Statements}. +Rule~80 gives the usual way statements are formed, while rule~81 handles the +anomalous case of an empty statement. Its use can always be avoided by using +an empty pair of braces instead, which much more visibly indicates the +absence of a statement (e.g., an empty loop body); when the empty statement +is used however, it will either be preceded by a space or start a new line +(like any other statement), so there is always some distinction between a +|while| loop with empty body and the |while| that ends a |do|~statement. A +rule like this with left hand side of length~1 makes the corresponding +category (viz.~|semi|) ``unstable'', and can only be useful for categories +that usually are scooped up (mostly from the left) by a longer rule. Rules +82--84 make labels (ordinary, case and default), and rules 85~and~86 attach +the labels to statements. Rule~87 makes \:; behave like an invisible +semicolon when it does not match any of the rules designed for it, for +instance if it follows an expression. + +@< Rules @>= +{80, {{expression, semi}}, {statement, NULL}}, @/ +{81, {{semi}}, {statement, NULL}}, @/ +{82, {{expression, colon}}, {label, "!_h_"}}, @/ +{83, {{case_like, expression, colon}}, {label, "_ _h_"}}, @/ +{84, {{case_like, colon}}, {label, "_h_"}}, @/ +{85, {{label, label}}, {label, "_B_"}}, @/ +{86, {{label, statement}}, {statement, "b_B_"},not_all_stats_forced}, @/ +{86, {{label, statement}}, {statement, "b_f_"},all_stats_forced}, @/ +{87, {{magic}}, {semi, NULL}}, @[@] + +@ The following rules format compound statements and aggregate initialisers. +Rules 90--94 combine declarations and statements within compound statements. +A newline is forced between declarations by rule~90, unless the declarations +are local (preceded by a left brace) and `\.{+m}' was specified (rule~91); +this rule does not apply to structure specifiers, because the left brace +will already have been captured in a |struct_head| before the rule can match. +If `\.{+f}'~or~`\.{+a}' was specified, then a newline is forced between +statements as well (rule~93). Between the declarations and statements some +extra white space appears in ordinary \Cee\ (rule~92), but not in \Cpp, where +declarations and statements may be arbitrarily mixed (rule~94). Rules +95--97 then build compound statements, where the last case is the unusual +one where a compound statement ends with a declaration; empty compound +statements are made into simple statements so that they will look better +when used in a loop statement or after a label. If compound statements are +not engulfed by a conditional or loop statement (see below) then they decay +to ordinary statements by rule~98. Rules 99~and~100 reduce aggregate +initialiser expressions, where the reduction of comma-separated lists of +expressions is already handled by the expression syntax. + +@< Rules @>= +{90, {{declaration, declaration}}, {declaration, "_f_"}}, @/ +{91, {{lbrace, declaration, declaration},1}, + {declaration, "_B_"},merged_decls}, @/ +{92, {{declaration, statement}}, {statement, "_F_"},no_plus_plus}, @/ +{92, {{declaration, statement}}, {statement, "_f_"},only_plus_plus}, @/ +{93, {{statement, statement}}, {statement, "_f_"},forced_statements}, @/ +{93, {{statement, statement}}, {statement, "_B_"},no_forced_statements},@/ +{94, {{statement, declaration}}, {declaration, "_f_"},only_plus_plus}, @/ +{95, {{lbrace, rbrace}}, {statement, "_,_"}}, @/ +{96, {{lbrace, statement, rbrace}}, + {compound_statement, "ft_+_-f_"},standard_braces}, @/ +{96, {{lbrace, statement, rbrace}}, + {compound_statement, "_+f_-f_"},unaligned_braces}, @/ +{96, {{lbrace, statement, rbrace}}, + {compound_statement, "f_+f_-f_"},wide_braces}, @/ +{97, {{lbrace, declaration, rbrace}}, + {compound_statement, "ft_+_-f_"},standard_braces}, @/ +{97, {{lbrace, declaration, rbrace}}, + {compound_statement, "_+f_-f_"},unaligned_braces}, @/ +{97, {{lbrace, declaration, rbrace}}, + {compound_statement, "f_+f_-f_"},wide_braces}, @/ +{98, {{compound_statement}}, {statement, "f_f"}}, @/ +{99, {{lbrace, expression, comma, rbrace}}, {expression, "_,__,_"}},@/ +{100, {{lbrace, expression, rbrace}}, {expression, "_,_,_"}}, @[@] + +@ Like for structure and union specifiers, we allow compound statements to +be kept on one line by inserting \:; after the left brace. Such statements +will reduce to |statement| rather that to |compound_statement|, so that they +will be treated as if they were simple statements. + +@< Rules @>= +{101, {{lbrace, magic}}, {short_lbrace, "__+"}}, @/ +{102, {{short_lbrace, declaration}}, {short_lbrace, "_B_"}}, @/ +{103, {{short_lbrace, statement}}, {short_lbrace, "_B_"}}, @/ +{104, {{short_lbrace, rbrace}}, {statement, "_-B_"}}, @[@] + +@ {\it Selection, iteration and jump statements}. +There are three intermediate categories involved in the recognition of +conditional statements. The category |if_like| stands for `|if|' or an +initial segment of a repeated if-clause, up to and including `|else|~|if|'. +An |if_head| is an |if_like| followed by its (parenthesised) condition +(rules 110~and~111). If the statement following the condition is followed by +`|else|~|if|', the whole construct reduces to |if_like| (so that the +indentation will not increase after the second condition, rules 112~and~113), +otherwise, if only `|else|' follows, reduction is to an |if_else_head| +(rules 114~and~115), and finally, if no |else| follows at all, we reduce with +only the if-branch to |statement| (rules 116~and~117). The reduction rules for +|if_else_head| differ from those for |if_head| in that it will not combine +with an |else|, even if it is present; the formatting is identical to that +of an |else|-less |if_head| (rules 118~and~119). (It might be tempting to +replace rules 116~and~117 by a reduction from |if_head| to |if_else_head| to +be applied if no matching `|else|' is found, but that would require some +subtle measures to prevent this decay at times when the right context is +insufficiently reduced to decide whether an `|else|' is present or not.) The +formatting of the if and else branches depends on whether they are compound +statements or some other kind of statement (possibly another conditional +statement), and on the flags for statement forcing and brace alignment. + +@< Rules @>= +{110, {{if_like, expression}}, {if_head, "f_~_"}}, @/ +{111, {{lbrace,if_like,expression},1}, {if_head, "_~_"},standard_braces},@/ +{112, {{if_head, compound_statement, else_like, if_like}}, + {if_like, "__f_~_"},aligned_braces}, @/ +{112, {{if_head, compound_statement, else_like, if_like}}, + {if_like, "_~_~_~_"},unaligned_braces}, @/ +{113, {{if_head, statement, else_like, if_like}}, + {if_like, "_+B_-f_~_"},not_all_stats_forced}, @/ +{113, {{if_head, statement, else_like, if_like}}, + {if_like, "_+f_-f_~_"},all_stats_forced}, @/ +{114, {{if_head, compound_statement, else_like}}, + {if_else_head, "__f_"},aligned_braces}, @/ +{114, {{if_head, compound_statement, else_like}}, + {if_else_head, "_~_~_"},unaligned_braces}, @/ +{115, {{if_head, statement, else_like}}, + {if_else_head, "_+B_-f_"},not_all_stats_forced},@/ +{115, {{if_head, statement, else_like}}, + {if_else_head, "_+f_-f_"},all_stats_forced}, @/ +{116, {{if_head, compound_statement}}, + {statement, "__f"},aligned_braces}, @/ +{116, {{if_head, compound_statement}}, + {statement, "_~_f"},unaligned_braces}, @/ +{117, {{if_head, statement}}, + {statement, "_+B_-f"},not_all_stats_forced}, @/ +{117, {{if_head, statement}}, + {statement, "_+f_-f"},all_stats_forced}, @/ +{118, {{if_else_head, compound_statement}}, + {statement, "__f"},aligned_braces}, @/ +{118, {{if_else_head, compound_statement}}, + {statement, "_~_f"},unaligned_braces}, @/ +{119, {{if_else_head, statement}}, + {statement, "_+B_-f"},not_all_stats_forced}, @/ +{119, {{if_else_head, statement}}, + {statement, "_+f_-f"},all_stats_forced}, @[@] + +@ The following rules prevent forced line breaks from conditional statements +that occur within a one-line compound statement. + +@< Rules @>= +{120, {{short_lbrace, if_like, expression},1}, + {if_head, "_~_"}}, @/ +{121, {{short_lbrace, if_head, statement, else_like}}, + {short_lbrace, "_B_B_B_"}}, @/ +{122, {{short_lbrace, if_head, statement}}, + {short_lbrace, "_B_B_"}}, @[@] + +@ Switch and loop statements make use of the syntax for conditionals by +reducing to |if_else_head| which will take one further statement and indent +it (rules 130~and~131). Recall that `|for|' and `|switch|' are both +|while_like|; the parenthesised object following `|for|' looks like nothing +we have seen before, however, so we need extra rules to come to terms with +it (rules 132--134). Rule~132 is needed to avoid a line break when these are +normally inserted between statements, and rule~134 is needed in case the +third expression is empty. The |do|-|while| loops have to be treated +separately. Because we want to distinguish the case of a +|compound_statement| as loop body from other kinds of statements, we cannot +wait until the |while| combines with the loop control condition to an +|if_else_head|, since by then a |compound_statement| will have decayed to +|statement|. Hence we pick up the unreduced `|while|' token and form a new +category |do_head| (rules 135~and~136); in case of a compound statement the +`|while|' will be on the same line as the closing brace. Rules 137~and~138 +then combine this with the condition and the ridiculous mandatory semicolon +at the end to form a |statement|. + +@< Rules @>= +{130, {{while_like, expression}}, {if_else_head, "f_~_"}}, @/ +{131, {{lbrace, while_like, expression},1}, + {if_else_head, "_~_"},standard_braces}, @/ +{132, {{lpar, statement, statement}, 1}, + {statement, "_B_"}, forced_statements}, @/ +{133, {{lpar, statement, expression, rpar}}, {expression, "__B__"}}, @/ +{134, {{lpar, statement, rpar}}, {expression, NULL}}, @/ +{135, {{do_like, compound_statement, while_like}}, + {do_head, "__~_"},standard_braces}, @/ +{135, {{do_like, compound_statement, while_like}}, + {do_head, "_~_~_"},unaligned_braces}, @/ +{135, {{do_like, compound_statement, while_like}}, + {do_head, "__f_"},wide_braces}, @/ +{136, {{do_like, statement, while_like}}, + {do_head, "_+B_-B_"},not_all_stats_forced}, @/ +{136, {{do_like, statement, while_like}}, + {do_head, "_+f_-f_"},all_stats_forced}, @/ +{137, {{do_head, expression, semi}}, {statement, "f_~__f"}}, @/ +{138, {{lbrace, do_head, expression, semi},1}, {statement, "_~__f"}}, @[@] + +@ The following rules prevent forced line breaks from loop statements +that occur within a one-line compound statement. Since no special layout is +required between the heading of a |while| loop and its body, rule~139 +incorporates the heading as if it were a separate statement. For a +|do|-|while| loop we must take a bit more effort to get the spacing +following the |while| correct. + +@< Rules @>= +{139, {{short_lbrace, while_like, expression}}, + {short_lbrace, "_B_~_"}}, @/ +{140, {{short_lbrace, do_like, statement, while_like},1}, + {do_head, "_B_B_"}}, @/ +{141, {{short_lbrace, do_head, expression, semi}}, + {short_lbrace, "_B_~__"}}, @[@] + +@ The tokens `|goto|', `|continue|', `|break|', and `|return|' are all +|return_like|; although what may follow them is not the same in all cases, +the following two rules cover all legal uses. Note that rule~146 does not +wait for a semicolon to come along; this may lead to a premature match as in +`|return a+b;|', but this does not affect formatting, while the rule allows +saying things like `|return home|' in a module name (or elsewhere) without +risking irreducible scraps. + +@< Rules @>= +{145, {{return_like, semi}}, {statement, NULL}}, @/ +{146, {{return_like, expression}}, {expression, "_~_"}}, @[@] + +@ {\it Function definitions and external declarations}. +Apart from the initial specification of the result type (which is optional, +defaulting to |int|), a new-style function heading will parse as an +|function_head| (see the declaration syntax above), while an old-style function +heading is an |expression| possibly followed by a |declaration| (specifying +the function parameters). Rules 150--152 parse these two kinds of +function headings together with the function body, yielding category +|function|; rule~153 attaches the optional result type specifier. Although +the \Cee~syntax requires that the function body is a compound statement, we +allow it to be a |statement| (to which |compound_statement| will decay), for +in case a very short function body is specified using `\.{\{@@;}'. + +At the outer level declarations and functions can be mixed; when they do a bit +of white space surrounds the functions (rules 154--156). The combination of +several declarations is already taken care of by the syntax for compound +statements; no extra white space is involved there. Rules 157--159 take care +of function declarations that are not definitions (i.e., there is no function +body); if followed by a semicolon, a comma or a right parenthesis, the +|function_head| decays to an |expression|, and the rest of the syntax will +take care of recognising a |declaration| or |parameters|. Rules 153~and~157 +will be replaced in~\Cpp, for reasons explained below (incidentally, this is +the reason the category |function_head| was introduced; it used to be simply +|expression|). + +@< Rules @>= +{150, {{function_head, statement}}, {function, "!_f_"}}, @/ +{151, {{expression, statement}}, {function, "!_f_"}}, @/ +{152, {{expression, declaration, statement}}, + {function, "!_++f_--f_"}}, @/ +{153, {{int_like, function}}, {function, "_ _"}}, @/ +{154, {{declaration, function}}, {function, "_F_"}}, @/ +{155, {{function, declaration}}, {declaration, "_F_"}}, @/ +{156, {{function, function}}, {function, "_F_"}}, @/ +{157, {{function_head, semi},-1}, {expression, NULL},no_plus_plus}, @/ +{158, {{function_head, comma},-1}, {expression, NULL}}, @/ +{159, {{function_head, rpar},-1}, {expression, NULL}}, @[@] + +@ {\it Module names}. +Although module names nearly always stand for statements, they can be made +to stand for a declaration by appending \:;, or for an expression by +appending `\.{@@;@@;}'. The latter possibility is most likely to be useful +if the module stands for (part of) an initialiser list. A module name can +also be made into an expression by enclosing it in \:[ and~\:], but in that +case rule~160 will apply first, placing a forced break after the module +name. Rules 161, 164,~and~165 prevent a module name from generating forced +breaks if it occurs on a one-line compound statement or structure or union +specifier, while rules 167~and~168 serve to prevent rules 163~and~164 from +matching with priority over rule~166. The rules given here will be replaced by +other ones in compatibility mode. + +@< Rules @>= +{160, {{mod_scrap}}, {statement, "_f"},cwebx}, @/ +{161, {{short_lbrace, mod_scrap},1}, {statement, NULL},cwebx}, @/ +{162, {{mod_scrap, magic}}, {declaration, "f__f"},cwebx}, @/ +{163, {{lbrace, mod_scrap, magic},1}, + {declaration, "__f"},cwebx|standard_braces}, @/ +{164, {{short_lbrace, mod_scrap, magic},1}, {declaration, NULL},cwebx}, @/ +{165, {{short_struct_head, mod_scrap, magic},1}, + {declaration,NULL},cwebx}, @/ +{166, {{mod_scrap, magic, magic}}, {expression, NULL},cwebx}, @/ +{167, {{lbrace, mod_scrap, magic, magic},1}, + {expression, NULL},cwebx|standard_braces}, @/ +{168, {{short_lbrace, mod_scrap, magic, magic},1}, + {expression, NULL},cwebx}, @[@] + +@ {\it Additional rules for compatibilty mode}. +@^Levy/Knuth \.{CWEB}@> +Although our grammar differs completely from the one used in \LKC., we use +most of it also in compatibility mode (the exception is formed by the rules +concerning module names). We do add a few rules in compatibility mode, mostly +do deal with circumstances that are different for some reason or other. + +We start with module names, which behave in a completely different way. In +compatibility mode, as in \LKC., a module name normally stands for an +expression (rule~164) and in practice is almost always followed by a visible +or invisible (|magic|) semicolon. Rules 160~and~161 treat these cases +explicitly, in order to insert a forced break after the semicolon; rule~161 +for the case of an invisible semicolon is needed because if we would wait for +the |magic| semicolon to decay to an ordinary one, it might instead combine +with an |int_like| token following it. Rules 162~and~163 are provided to allow +the short form of compound statements even in compatibility mode (even though +it is not present in \LKC.): they preempt rules 160~and~161, avoiding the +forced break. Since in compatibility mode one has no means of indicating that +a module name stands for a set of declarations, we add rule~165 to allow them +nevertheless to be used before a function definition. + +Rules 170~and~171 compensate for the fact that compound assignment operators +like `|+=|' are scanned as two tokens in compatibility mode (see +section@#truly stupid@> for an explanation why this is done). +Rule~172 allows types to be used in the argument lists of macros, without +enclosing them between \:[~and~\:], in compatibility mode; this is done +frequently in the Stanford GraphBase. @^Stanford GraphBase@> It is sufficient +to remove expressions from the beginning of the argument list, since types, +and more generally types followed by declarators, are already removed by the +standard rules for |parameters|. As a result the argument list will either +reduce to an |expression| or to |parameters|, depending on whether the final +item was an expression. In both cases it will combine with the macro name to +an |expression|, although the spacing will be a bit too wide in the +|parameters| case. But then, one ought to use \:[~and~\:] anyway, which avoids +this problem. + +@< Rules @>= +{160, {{mod_scrap, semi}}, {statement, "__f"},compatibility}, @/ +{161, {{mod_scrap, magic}}, {statement, "__f"},compatibility}, @/ +{162, {{short_lbrace, mod_scrap, semi},1}, + {statement, NULL},compatibility}, @/ +{163, {{short_lbrace, mod_scrap, magic},1}, + {statement, NULL},compatibility}, @/ +{164, {{mod_scrap}}, {expression, NULL},compatibility}, @/ +{165, {{statement, function}}, {function, "_F_"},compatibility}, @/ +@) +{170, {{binop, binop}}, {binop,"r__"},compatibility}, @/ +{171, {{unorbinop, binop}}, {binop,"r__"},compatibility}, @/ +{172, {{lpar, expression, comma}}, {lpar, "___p1"}, compatibility}, @[@] +@[@] + +@ {\it Additional rules for \Cpp}. +Up to this point we have included some specific rules for \Cpp, in places +where a slight deviation from the \Cee~syntax was required. There are however +a large number of syntactic possibilities of \Cpp\ that are not even remotely +similar to those of~\Cee, so it is most convenient to collect them in a +separate section. The author of \.{CWEBx} wishes to make it clear that he is +quite aware of the incompleteness of the set of rules specified below, and +that he assumes no responsibility for correcting this. One reason for this is +that he has no readable formal grammar of \Cpp, which possibly could be used +for validation (nor does he use \Cpp\ himself), another is that the pieces +of grammar that he has seen show so little coherence that he seriously doubts +whether it is possible at all to parse \Cpp\ reliably with a grammar of the +type implemented here. In fact, the rules here were merely added in an +attempt to cope with problems reported by users. + +We start with rules for `\&{operator}', which are simple: it should combine +with a following operator symbol of any type to form an expression (rules +180--182). Then rules 183--186 take care of the `::'~operator: either a class +name or nothing is expected at the left, and either an ordinary or class +identifier at the right; the resulting category is that of the right hand +side. Type identifiers may appear as the left hand side of an assignment +within a list of formal parameters, indicating a default argument; in this +case the while assignment should behave as a type identifier (rule~187). + +Next we give rules catering with constructor declarations in class +definitions. First of all we must recognise the fact that the class name is +being used as a function name here; the simplest solution is to recognise the +combination of an |int_like| followed by a (possibly empty) parameter list +(rules 190~and~191). We cannot let a |function_head| (possibly created by the +rules kust mentioned) decay to an |expression| when followed be a semicolon, +as we do for~\Cee, since declarations of constructor members of a class lack +an initial type specification, so the |expression| would fail to become part +of a |declaration|. Therefore, special measures are necessary: the simplest +solution is to simply absorb (rule~192) any preceding type specifier into the +|function_head| (thereby removing the distinction between its presence or +absence), and construct de |declaration| explicitly from the |function_head| +and the following semicolon (rule~193). + +@< Rules @>= +{180, {{case_like, binop}}, {expression, "_o_"},only_plus_plus}, @/ +{181, {{case_like, unorbinop}}, {expression, "_o_"},only_plus_plus}, @/ +{182, {{case_like, unop}}, {expression, NULL},only_plus_plus}, @/ +{183, {{int_like, colcol, expression}}, + {expression, NULL},only_plus_plus}, @/ +{184, {{colcol, expression}}, {expression, "o__"},only_plus_plus}, @/ +{185, {{int_like, colcol, int_like}}, + {int_like, NULL},only_plus_plus}, @/ +{186, {{colcol, int_like}}, {int_like, "o__"},only_plus_plus}, @/ +{187, {{int_like, binop, expression}}, + {int_like, NULL},only_plus_plus}, @/ +{190, {{int_like, parameters}}, {function_head, "_B_"},only_plus_plus}, @/ +{191, {{int_like, lpar,rpar}}, {function_head, "_B_,_"},only_plus_plus},@/ +{192, {{int_like, function_head}}, + {function_head, "_ _"},only_plus_plus}, @/ +{193, {{function_head, semi}}, {declaration, "!__"},only_plus_plus}, @[@] diff --git a/web/c_cpp/cwebx/sample.ps b/web/c_cpp/cwebx/sample.ps new file mode 100644 index 0000000000..53ae91cb63 --- /dev/null +++ b/web/c_cpp/cwebx/sample.ps @@ -0,0 +1,621 @@ +%!PS-Adobe-2.0 +%%Creator: dvipsk 5.58a Copyright 1986, 1994 Radical Eye Software +%%Pages: 2 +%%PageOrder: Ascend +%%BoundingBox: 0 0 596 842 +%%DocumentPaperSizes: a4 +%%EndComments +%DVIPSCommandLine: dvips -f -p6 -l7 +%DVIPSParameters: dpi=300, compressed, comments removed +%DVIPSSource: TeX output 1998.01.13:1225 +%%BeginProcSet: texc.pro +/TeXDict 250 dict def TeXDict begin /N{def}def /B{bind def}N /S{exch}N +/X{S N}B /TR{translate}N /isls false N /vsize 11 72 mul N /hsize 8.5 72 +mul N /landplus90{false}def /@rigin{isls{[0 landplus90{1 -1}{-1 1} +ifelse 0 0 0]concat}if 72 Resolution div 72 VResolution div neg scale +isls{landplus90{VResolution 72 div vsize mul 0 exch}{Resolution -72 div +hsize mul 0}ifelse TR}if Resolution VResolution vsize -72 div 1 add mul +TR[matrix currentmatrix{dup dup round sub abs 0.00001 lt{round}if} +forall round exch round exch]setmatrix}N /@landscape{/isls true N}B +/@manualfeed{statusdict /manualfeed true put}B /@copies{/#copies X}B +/FMat[1 0 0 -1 0 0]N /FBB[0 0 0 0]N /nn 0 N /IE 0 N /ctr 0 N /df-tail{ +/nn 8 dict N nn begin /FontType 3 N /FontMatrix fntrx N /FontBBox FBB N +string /base X array /BitMaps X /BuildChar{CharBuilder}N /Encoding IE N +end dup{/foo setfont}2 array copy cvx N load 0 nn put /ctr 0 N[}B /df{ +/sf 1 N /fntrx FMat N df-tail}B /dfs{div /sf X /fntrx[sf 0 0 sf neg 0 0] +N df-tail}B /E{pop nn dup definefont setfont}B /ch-width{ch-data dup +length 5 sub get}B /ch-height{ch-data dup length 4 sub get}B /ch-xoff{ +128 ch-data dup length 3 sub get sub}B /ch-yoff{ch-data dup length 2 sub +get 127 sub}B /ch-dx{ch-data dup length 1 sub get}B /ch-image{ch-data +dup type /stringtype ne{ctr get /ctr ctr 1 add N}if}B /id 0 N /rw 0 N +/rc 0 N /gp 0 N /cp 0 N /G 0 N /sf 0 N /CharBuilder{save 3 1 roll S dup +/base get 2 index get S /BitMaps get S get /ch-data X pop /ctr 0 N ch-dx +0 ch-xoff ch-yoff ch-height sub ch-xoff ch-width add ch-yoff +setcachedevice ch-width ch-height true[1 0 0 -1 -.1 ch-xoff sub ch-yoff +.1 sub]/id ch-image N /rw ch-width 7 add 8 idiv string N /rc 0 N /gp 0 N +/cp 0 N{rc 0 ne{rc 1 sub /rc X rw}{G}ifelse}imagemask restore}B /G{{id +gp get /gp gp 1 add N dup 18 mod S 18 idiv pl S get exec}loop}B /adv{cp +add /cp X}B /chg{rw cp id gp 4 index getinterval putinterval dup gp add +/gp X adv}B /nd{/cp 0 N rw exit}B /lsh{rw cp 2 copy get dup 0 eq{pop 1}{ +dup 255 eq{pop 254}{dup dup add 255 and S 1 and or}ifelse}ifelse put 1 +adv}B /rsh{rw cp 2 copy get dup 0 eq{pop 128}{dup 255 eq{pop 127}{dup 2 +idiv S 128 and or}ifelse}ifelse put 1 adv}B /clr{rw cp 2 index string +putinterval adv}B /set{rw cp fillstr 0 4 index getinterval putinterval +adv}B /fillstr 18 string 0 1 17{2 copy 255 put pop}for N /pl[{adv 1 chg} +{adv 1 chg nd}{1 add chg}{1 add chg nd}{adv lsh}{adv lsh nd}{adv rsh}{ +adv rsh nd}{1 add adv}{/rc X nd}{1 add set}{1 add clr}{adv 2 chg}{adv 2 +chg nd}{pop nd}]dup{bind pop}forall N /D{/cc X dup type /stringtype ne{] +}if nn /base get cc ctr put nn /BitMaps get S ctr S sf 1 ne{dup dup +length 1 sub dup 2 index S get sf div put}if put /ctr ctr 1 add N}B /I{ +cc 1 add D}B /bop{userdict /bop-hook known{bop-hook}if /SI save N @rigin +0 0 moveto /V matrix currentmatrix dup 1 get dup mul exch 0 get dup mul +add .99 lt{/QV}{/RV}ifelse load def pop pop}N /eop{SI restore userdict +/eop-hook known{eop-hook}if showpage}N /@start{userdict /start-hook +known{start-hook}if pop /VResolution X /Resolution X 1000 div /DVImag X +/IE 256 array N 0 1 255{IE S 1 string dup 0 3 index put cvn put}for +65781.76 div /vsize X 65781.76 div /hsize X}N /p{show}N /RMat[1 0 0 -1 0 +0]N /BDot 260 string N /rulex 0 N /ruley 0 N /v{/ruley X /rulex X V}B /V +{}B /RV statusdict begin /product where{pop product dup length 7 ge{0 7 +getinterval dup(Display)eq exch 0 4 getinterval(NeXT)eq or}{pop false} +ifelse}{false}ifelse end{{gsave TR -.1 .1 TR 1 1 scale rulex ruley false +RMat{BDot}imagemask grestore}}{{gsave TR -.1 .1 TR rulex ruley scale 1 1 +false RMat{BDot}imagemask grestore}}ifelse B /QV{gsave newpath transform +round exch round exch itransform moveto rulex 0 rlineto 0 ruley neg +rlineto rulex neg 0 rlineto fill grestore}B /a{moveto}B /delta 0 N /tail +{dup /delta X 0 rmoveto}B /M{S p delta add tail}B /b{S p tail}B /c{-4 M} +B /d{-3 M}B /e{-2 M}B /f{-1 M}B /g{0 M}B /h{1 M}B /i{2 M}B /j{3 M}B /k{ +4 M}B /w{0 rmoveto}B /l{p -4 w}B /m{p -3 w}B /n{p -2 w}B /o{p -1 w}B /q{ +p 1 w}B /r{p 2 w}B /s{p 3 w}B /t{p 4 w}B /x{0 S rmoveto}B /y{3 2 roll p +a}B /bos{/SS save N}B /eos{SS restore}B end +%%EndProcSet +TeXDict begin 39158280 55380996 1000 300 300 () @start +/Fa 1 1 df<B512C0A212027D871A>0 D E /Fb 21 121 df<EB01FCEB0703130CEB1C07 +1403EB18001338A45B3807FFFC3800701CA3EBE038A41470EA01C0A314E2A3380380E414 +641438140090C7FC5A120612C612E412CC12701825819C17>12 D<903901FC0FEE903907 +0E307E010C136091380CE07C90391C00C01C140113381638A2EC0380A20003B612F03A00 +70038070A2EC070016E013E0A391380E01C0A2EA01C0A2ED0388141CA201801490000314 +01ED00E04A13001300A248133038C63060EAE63838CC3180D8781FC8FC2725819C25>15 +D<EA03CCEA063C120CEA181CEA383812301270A2EAE070A413E212C0A2EA61E4EA6264EA +3C380F127B9115>97 D<123F1207A2120EA45AA4EA39C0EA3E60EA3830A2EA7038A4EAE0 +70A3136013E0EAC0C012C1EA6180EA6300123C0D1D7B9C13>I<EA01F0EA0708120CEA18 +1CEA3838EA30001270A25AA513081310EA6020EA30C0EA1F000E127B9113>I<EB1F8013 +03A2EB0700A4130EA4EA03DCEA063C120CEA181CEA383812301270A2485AA413E212C0A2 +EA61E4EA6264EA3C38111D7B9C15>I<EA01E0EA0710120CEA1808EA381012701360EA7F +80EAE000A51308EA60101320EA30C0EA1F000D127B9113>I<EB03C0EB0670130CEB1C60 +1400A25BA53803FF8038007000A55BA5485AA5485AA390C7FCA25A12C612E65A12781425 +819C0D>I<13F3EA018FEA030FEA0607EA0E0E120C121CA2EA381CA413381230A2EA1878 +13F0EA0F701200A213E0A2EAC0C012E1EAC300127E101A7D9113>I<EA0FC01201A2485A +A448C7FCA4EA0E78138CEA0F0E120E121CA4485AA35B00701380A21370EB7100EAE032EA +601C111D7D9C15>I<EA01801203EA0100C7FCA7121C12261247A2128EA2120E5AA35AA2 +1271A31272A2123C091C7C9B0D>I<EA1F801203A2EA0700A4120EA45AA45AA45AA412E4 +A412681238091D7C9C0B>108 D<393C1E078039266318C0394683A0E0384703C0008E13 +80A2120EA2391C0701C0A3EC0380D8380E1388A2EC0708151039701C032039300C01C01D +127C9122>I<EA3C3CEA2646EA4687EA4707128EA2120EA2EA1C0EA35B00381340A21338 +148038701900EA300E12127C9117>I<EA01E0EA0718EA0C0C12181238EA300E1270A2EA +E01CA31318133813301360EA60C0EA3180EA1E000F127B9115>I<EA07873804D9803808 +E0C0A23811C0E0A21201A2380381C0A31480EA070314005B1306EA0E8C137090C7FCA25A +A4123CB47E131A7F9115>I<EA3C3CEA26C2EA4687EA4707EA8E061300120EA25AA45AA4 +5A123010127C9112>114 D<EA01F0EA0608120C131CEA1818EA1C00121F13C0EA0FF012 +07EA00781338EA603012E012C0EA8060EA60C0EA1F000E127D9111>I<13C01201A3EA03 +80A4EAFFE0EA0700A3120EA45AA4EA3840A31380EA1900120E0B1A7D990E>I<EA1E03EA +27071247A2EA870EA2120EA2485AA438383880A21218EB3900EA1C59EA078E11127C9116 +>I<EA070EEA19913810E38012203841C30013C01201A2485AA4EA07021267EAE70412CB +EA8B08EA70F011127D9113>120 D E /Fc 3 51 df<1360AAB512F0A238006000AA1416 +7E9119>43 D<120C121C12EC120CAFEAFFC00A137D9211>49 D<121FEA60C01360EAF070 +13301260EA0070A2136013C012011380EA02005AEA08101210EA2020EA7FE012FF0C137E +9211>I E /Fd 5 111 df<126012F0A212701210A41220A212401280040C7C830C>59 +D<130113031306A3130CA31318A31330A31360A213C0A3EA0180A3EA0300A31206A25AA3 +5AA35AA35AA35AA210297E9E15>61 D<EA01F0EA030C120CEA1C1EEA383CEA3018EA7000 +A25AA51304EA60081310EA3060EA1F800F127E9112>99 D<EB01E0EB0630EB0E7814F0EB +1C601400A45BA23803FFC038003800A25BA55BA5485AA55B1203A20063C7FC12F312F612 +E4127815257E9C14>102 D<EA3C1F384E6180384681C0EA4701128F128E120EA2381C03 +80A3EB070000381310A2130E1420387006403830038014127E9119>110 +D E /Fe 11 106 df<1203A4EAC30CEAE31CEA7338EA1FE0EA0780A2EA1FE0EA7338EAE3 +1CEAC30CEA0300A40E127D9215>3 D<EB3F80EBC0603803001800041304487F487F48EB +0080A2481440A2481420130E131FA4130E90C7FC00401440A26C1480A26CEB01006C1302 +6C5B000313183800C060EB3F801B1C7E9720>12 D<B612E07EC9FCA7B612E0A2C9FCA700 +7FB512E0B6FC1B147E9320>17 D<1306A25B131C13185B017FB512F090B612F8D80380C8 +FC48C9FC123C12F0123C120E12036C7E6CB612F86D14F00130C8FC7F7FA27FA225187E95 +2A>40 D<1460A214C0A2EB0180A3EB0300A21306A25BA25BA35BA25BA25BA2485AA248C7 +FCA31206A25AA25AA25AA35AA25A124013287A9D00>54 D<13101338A2136CA313C6A2EA +0183A238030180A2380600C0A3481360A2481330A2481318A348130CA24813061402171A +7E981C>94 D<00C0130214060060130CA26C1318A36C1330A26C1360A26C13C0A3380301 +80A238018300A2EA00C6A2136CA31338A21310171A7E981C>I<133C13E0EA01C0138012 +03AD13005A121C12F0121C12077E1380AD120113C0EA00E0133C0E297D9E15>102 +D<12F0121C12077E1380AD120113C0EA00E0133C13E0EA01C013801203AD13005A121C12 +F00E297D9E15>I<134013C0EA0180A3EA0300A21206A35AA25AA35AA25AA35AA21260A3 +7EA27EA37EA27EA37EA2EA0180A3EA00C013400A2A7D9E10>I<12C0A21260A37EA27EA3 +7EA27EA37EA2EA0180A3EA00C0A2EA0180A3EA0300A21206A35AA25AA35AA25AA35AA20A +2A7E9E10>I E /Ff 5 88 df<38C00180AEB5FCA211107E8916>32 +D<EA7FF8EAFFFE6C7EEA1C0FEB07801303A313071400EA1FFF5BA2EA1C1FEB038014C013 +01A41303EB0780EA7FFFB51200EA7FFC12197F9816>66 D<3801F180EA07FBEA0FFFEA1F +0FEA3C07EA38031270A200F0C7FC5AA77E38700380A21238383C0700EA1F0FEA0FFE6C5A +EA01F011197E9816>I<387FFFC0B5FC7EEA1C01A490C7FCA2131CA2EA1FFCA3EA1C1CA2 +90C7FC14E0A5EA7FFFB5FC7E13197F9816>69 D<38FC07E0EAFE0FEAFC07387001C0A300 +301380EA3803A313E3EA39F3A213B300191300A61313EA1B1BEA0F1EA2EA0E0E13197F98 +16>87 D E /Fg 35 118 df<126012F0A2126004047D830A>46 D<EA0F80EA30E0EA4070 +EA8030EAC03812E0124012001370A2136013C0EA0180EA03001206EA0C081208EA1018EA +3FF0127F12FF0D157E9412>50 D<EA0FE0EA3030EA6018EA701CA21200131813381360EA +07E0EA00301318130C130EA212E0A2EAC00CEA4018EA3030EA0FE00F157F9412>I<1330 +A2137013F012011370120212041208121812101220124012C0EAFFFEEA0070A5EA03FE0F +157F9412>I<EA2030EA3FE013C0EA24001220A4EA2F80EA30E0EA2070EA00301338A212 +4012E0A2EA8030EA4060EA20C0EA1F000D157E9412>I<EA01F0EA0608EA080CEA181C12 +30EA7000126012E0EAE3E0EAEC30EAF018130CEAE00EA31260A2EA300C1318EA1830EA07 +C00F157F9412>I<1240EA7FFE13FC13F8EAC008EA80101320EA00401380A2EA0100A25A +12021206A2120EA512040F167E9512>I<13101338A3135CA3138EA3EA0107A200031380 +EA0203A23807FFC0EA0401A2380800E0A21218003813F038FE03FE17177F961A>65 +D<B512E0EA1C00146014201410A3EB0400A3130CEA1FFCEA1C0C13041408A21300141814 +10A2143014F0B5FC15177F9618>69 D<EB7E0838038198380600584813384813185A0070 +1308A25A1400A4EB03FEEB00381270A212307E7E7E380380D838007F0817177E961C>71 +D<38FF83FE381C0070AA381FFFF0381C0070AA38FF83FE17177F961A>I<EAFFE0EA0E00 +B3A3EAFFE00B177F960D>I<38FF80FE381C0078146014401480EB0100130613085B1338 +1378139CEA1D0E121EEA1C07EB0380EB01C0A2EB00E014701478147C38FF80FF18177F96 +1B>75 D<EAFFC0001CC7FCAD1420A31460A2144014C01303B5FC13177F9616>I<00FEEB +03F8001E14C000171305A338138009A23811C011A33810E021A2EB7041A3EB3881A2EB1D +01A2130EA2123839FE040FF81D177F9620>I<00FC13FE001E1338001F13101217EA1380 +EA11C0A2EA10E013701338A2131C130E130F1307EB0390EB01D0A2EB00F0147014301238 +00FE131017177F961A>I<13FCEA0303380E01C0381C00E0481370003013300070133800 +60131800E0131CA700701338A200301330003813706C13E0380E01C038030300EA00FC16 +177E961B>I<EAFFFE381C0380EB00C014601470A4146014C0EB0380381FFE00001CC7FC +AAB47E14177F9618>I<EAFFFC381C0380EB00C014E01470A414E014C0EB0380381FFE00 +381C0780EB01C0EB00E0A514E1A2147238FF803C18177F961A>82 +D<EA0FC4EA302CEA601CEA400CEAC004A3EAE0001270127FEA3FE0EA0FF8EA01FCEA001C +130E13061280A3EAC004EAE008EAD810EA87E00F177E9614>I<387FFFF8386038180040 +1308A200801304A300001300AF3807FFC016177F9619>I<38FF80FE381C00381410B06C +132012066C13403801818038007E0017177F961A>I<3AFF07FC3F803A3C01E00E00D81C +001304A2EB0170000E5CA2EB023800075CA2EB041CD803845BA2EB880ED801C85BA2EBD8 +0F3900F00780A3D96003C7FCA321177F9624>87 D<EA1FC0EA38601330EA10381200EA03 +F8EA1E3812301270EAE039A21379EA70FFEA1F1E100E7F8D12>97 +D<EA07F0EA18381230EA7010EA600012E0A41260EA70081230EA1830EA07C00D0E7F8D10 +>99 D<137E130EA8EA07CEEA1C3EEA300E1270126012E0A412601270EA301EEA182E3807 +CFC012177F9614>I<EA0FC0EA1860EA3030EA7038EAE018EAFFF8EAE000A31260EA7008 +EA3010EA1830EA07C00D0E7F8D10>I<12FC121CA8137CEA1D8EEA1E07121CAA38FF9FE0 +1317809614>104 D<1218123CA212181200A5127C121CAC12FF081780960A>I<12FC121C +B3A3EAFF80091780960A>108 D<EAFC7CEA1D8EEA1E07121CAA38FF9FE0130E808D14> +110 D<EA07C0EA1830EA3018EA600CA2EAE00EA5EA701CEA3018EA1830EA07C00F0E7F8D +12>I<EA1F40EA60C0EAC040A2EAE000B4FCEA7F80EA1FC0EA01E0EA8060A212C0EAE0C0 +EA9F000B0E7F8D0E>115 D<1208A31218A21238EAFF80EA3800A71340A4EA1C80EA0F00 +0A147F930E>I<EAFC3FEA1C07AA5BEA0E173803E7E0130E808D14>I +E /Fh 38 122 df<EB3FC03801F0303803C0380007137CEA0F80A2143891C7FCA4B512FC +A2380F807CAE397FE1FF80A2191D809C1B>12 D<903801C070A349485AA490380701C0A4 +90380E0380B71280A27E26001C07C7FCEB380EA5495A007FB61280B7FCA22600E038C7FC +48485AA448485AA4380701C0A321257D9C28>35 D<127812FCA4127806067D850D>46 +D<1360EA01E0120F12FF12F31203B3A2387FFF80A2111B7D9A18>49 +D<EA07F8EA1FFE38383F8038780FC0EAFC0714E01303A21278EA000714C0A2EB0F80EB1F +00131E13385B5B38018060EA03001206000813E0381FFFC05A5AB5FCA2131B7E9A18>I< +EA03F8EA1FFEEA3C1FEB0F80387C07C0127E127C123838000F80A2EB1E005BEA03F8EA00 +1EEB0F80EB07C0A214E01230127812FCA214C038780F80EB1F00EA1FFEEA07F8131B7E9A +18>I<EB018013031307130F131F133F136F13CF138F1201EA030F1206120C1218123012 +6012C0B512F8A238000F80A63801FFF8A2151B7F9A18>I<38180180381FFF005B5B5B13 +C00018C7FCA4EA19F8EA1E0E38180F80EA1007000013C014E0A3127812F8A214C012F038 +600F8038381F00EA1FFEEA07F0131B7E9A18>I<137EEA03FF38078180380F03C0EA1E07 +123CEB038048C7FCA212F813F8EAFB0E38FA0780EAFC0314C000F813E0A41278A214C012 +3CEB0780381E0F00EA07FEEA03F8131B7E9A18>I<1260387FFFE0A214C01480A238E003 +00EAC0065B5BC65AA25B13E0A212015B1203A41207A66C5A131C7D9B18>I<EA03F8EA0F +FE381E0F80EA3807EB03C01278A2127E387F8380EBC700EA3FFE6C5A6C7E3807FF80121D +38387FC038781FE0EAF00713031301A3007813C0EB0380EA3E07381FFE00EA03F8131B7E +9A18>I<90381FE0209038FFF8E03803F80F3807C003380F800148C7FC123E1560127E12 +7C00FC1400A8007C1460127E123E15C07E390F8001803907C003003803F80E3800FFFCEB +1FE01B1C7D9B22>67 D<B6FCA2380FC01F1407801580A2EBC181A3EC800013C313FFA213 +C313C1A21560A29038C000E015C0A21401A21403EC0F80B6FCA21B1C7E9B1F>69 +D<B6FCA2380FC01F1407801580A21401EBC181A2EC8000A213C313FFA213C313C1A401C0 +C7FCA6B5FCA2191C7E9B1E>I<B5FCA2EA07E0B3A6B5FCA2101C7F9B12>73 +D<B5FCA2EA0FC0AF1403A31407A21406140E141E147EB512FEA2181C7E9B1D>76 +D<EA0FF8EA1C1E383E0F80130714C0121C1200137FEA07E7EA1F07123C127C12F8A3EA78 +0B383E13F8EA0FE115127F9117>97 D<B4FCA2121FA9EB3F80EBE1E0EB8070EB0078143C +A2143EA6143CA21478EB8070381EC1E0381C3F00171D7F9C1B>I<EA03FCEA0E0EEA1C1F +123C1278130E00F8C7FCA61278EB0180123C381C0300EA0E0EEA03F811127E9115>I<EB +0FF0A21301A9EA01F9EA0F07EA1C03EA3C011278A212F8A61278A2123CEA1C03380F0FFE +EA03F9171D7E9C1B>I<EA01FCEA0F07381C0380383C01C0127814E012F8A2B5FC00F8C7 +FCA31278007C1360123C6C13C0380F03803801FC0013127F9116>I<137F3801E3803803 +C7C0EA0787120FEB8380EB8000A5EAFFF8A2EA0F80AEEA7FF8A2121D809C0F>I<3803F8 +F0380E0F38121E381C0730003C1380A4001C1300EA1E0FEA0E0EEA1BF80010C7FC1218A2 +EA1FFF14C06C13E04813F0387801F838F00078A300701370007813F0381E03C03807FF00 +151B7F9118>I<B4FCA2121FA9EB0FC0EB31E0EB40F0EB80F8A21300AB38FFE7FFA2181D +7F9C1B>I<121E123FA4121EC7FCA6B4FCA2121FAEEAFFE0A20B1E7F9D0E>I<B4FCA2121F +B3A7EAFFE0A20B1D7F9C0E>108 D<39FF0FC07E903831E18F3A1F40F20780D980FC13C0 +A2EB00F8AB3AFFE7FF3FF8A225127F9128>I<38FF0FC0EB31E0381F40F0EB80F8A21300 +AB38FFE7FFA218127F911B>I<EA01FC380F0780381C01C0003C13E0387800F0A200F813 +F8A6007813F0A2383C01E0381E03C0380F07803801FC0015127F9118>I<38FF3F80EBE1 +E0381F80F0EB0078147C143C143EA6143C147C1478EB80F0EBC1E0EB3F0090C7FCA6EAFF +E0A2171A7F911B>I<EAFE3E1347381E8F80A3381F070090C7FCAAEAFFF0A211127F9114> +114 D<EA1FD8EA3078EA601812E0A2EAF000EAFF80EA7FE013F0EA1FF8EA07FCEA007CEA +C01CA212E01318EAF830EACFC00E127E9113>I<1203A45AA25AA2EA3FFC12FFEA1F00A9 +130CA4EA0F08EA0798EA03F00E1A7F9913>I<38FF07F8A2EA1F00AC1301120F380786FF +EA01F818127F911B>I<38FFC1FCA2381F0060EB80E0000F13C013C03807C180A23803E3 +00A2EA01F6A213FE6C5AA21378A2133016127F9119>I<39FF8FF8FEA2391F03E030A201 +831370000FEBF0601386D807C613C0EBCEF8EBEC790003EB7D80EBF83D0001EB3F00A249 +7E0000131EEBE00EA21F127F9122>I<38FFC7FCA2381F8180EA0F833807C700EA03EEEA +01FC5B1200137C13FEEA01DFEA039F38070F80380607C0380C03E038FF07FCA216127F91 +19>I<38FFC1FCA2381F0060EB80E0000F13C013C03807C180A23803E300A2EA01F713F6 +EA00FE5BA21378A21330A21370EA706012F85BEAF9800073C7FC123E161A7F9119>I +E /Fi 42 121 df<EAE038EAF078A7EAE038A2EA60300D0B7C9816>34 +D<38380180EA7C03A238EE0700A2130EA35B127C5B1238C65AA35BA2485AA3485AEB8380 +380707C0A2380E0EE0A3121CA2383807C0A23818038013207F9C16>37 +D<12301278127C123C121CA41238127812F012E01240060D789816>39 +D<1238127C127EA2123E120E121E121C127812F01260070B798416>44 +D<127012F8A312700505788416>46 D<12E0B51280A338E00F00131EEA001C5B13781370 +5BA2485AA3485AA448C7FCA7111A7E9916>55 D<EA03E0EA0FF8487EEA3C1EEA700E7F12 +E014801303A213071270EA780FEA3FFFEA1FFBEA0FE338000700A21230EA780E5BEA7078 +EA3FF06C5AEA0F8011197E9816>57 D<127012F8A312701200A8127012F8A31270051278 +9116>I<EB01801307131FEB3E0013F8485AEA07C0485A003EC7FC12FC12F012FC123EEA +0F806C7EEA01F06C7E133EEB1F801307130111157E9616>60 D<12C012F012FC123EEA0F +806C7EEA01F06C7E133EEB1F801307131FEB3E0013F8485AEA07C0485A003EC7FC12FC12 +F012C011157E9616>62 D<EA7FF8EAFFFE6C7EEA1C0FEB07801303A313071400EA1FFF5B +A2EA1C1FEB038014C01301A41303EB0780EA7FFFB51200EA7FFC12197F9816>66 +D<3801F180EA07FBEA0FFFEA1F0FEA3C07EA38031270A200F0C7FC5AA77E38700380A212 +38383C0700EA1F0FEA0FFE6C5AEA01F011197E9816>I<387FFFC0B5FC7EEA1C01A490C7 +FCA2131CA2EA1FFCA3EA1C1CA290C7FC14E0A5EA7FFFB5FC7E13197F9816>69 +D<B512E0A3EA1C00A41400A2131CA2EA1FFCA3EA1C1CA290C7FCA6B47E7F5B13197F9816 +>I<EA03E348B4FC5AEA1E1F487EEA38071270A200F0C7FC5AA4EB3F80EB7FC0EB3F8038 +F007001270A26C5A123C6C5A6CB4FCEA07F7EA03E712197E9816>I<387E1FC038FF3FE0 +387F1FC0381D07001387A313C7A2121CA213E7A31367A21377A21337A31317EA7F1FEAFF +9FEA7F0F13197F9816>78 D<EA1FFC487E487EEA780F38F00780EAE003AEEAF007A23878 +0F00EA7FFF6C5A6C5A11197E9816>I<EA07E3EA1FFF5AEA781FEAF00712E0A290C7FC7E +1278EA3F80EA1FF0EA07FCC67E130F7FEB0380A2126012E0EB0700EAF80FEAFFFE5BEAC7 +F011197E9816>83 D<387FFFE0B5FCA2EAE0E0A400001300AFEA07FC487E6C5A13197F98 +16>I<38FC07E0EAFE0FEAFC07387001C0A300301380EA3803A313E3EA39F3A213B30019 +1300A61313EA1B1BEA0F1EA2EA0E0E13197F9816>87 D<12C07EA21270A27EA27EA27EA2 +7EA26C7EA26C7EA26C7EA21370A27FA27FA27FA27FA2EB0380A2130111207E9C16>92 +D<EA1FE0487E487EEA783CEA300E1200A2EA03FE121FEA3E0E127012E0A3EA783E387FFF +E0EA3FE7EA0F8313127E9116>97 D<127E12FE127E120EA4133E13FF000F1380EB83C0EB +00E0120E1470A614E0EA0F01EB83C0EBFF80000E1300EA063C1419809816>I<EA03F8EA +0FFCEA1FFEEA3C1EEA780CEA70005AA57EEA70071278EA3E0EEA1FFCEA0FF8EA03F01012 +7D9116>I<133F5B7F1307A4EA03C7EA0FF748B4FCEA3C1F487EEA700712E0A6EA700FA2 +EA3C1F381FFFE0380FE7F03807C7E014197F9816>I<EA03E0EA0FF8EA1FFCEA3C1EEA78 +0EEA700712E0B5FCA3EAE000A2EA70071278EA3C0FEA1FFEEA0FFCEA03F010127D9116> +I<131FEB7F8013FFEA01E7EBC30013C0A2EA7FFFB5FCA2EA01C0ACEA3FFE487E6C5A1119 +7F9816>I<3803E3C03807F7E0EA0FFF381C1CC038380E00A56C5AEA0FF8485AEA1BE000 +38C7FC1218EA1FFC13FF481380387803C038E000E0A4387001C0EA7C07383FFF80380FFE +00EA03F8131C7F9116>I<127E12FE127E120EA4133C13FEEA0FFFEB87801303120EAA38 +7FC7F038FFE7F8387FC7F01519809816>I<EA0180EA03C0A2EA0180C7FCA4EA7FC0A312 +01ACEA7FFFB5FC7E101A7D9916>I<EAFFC0A31201B3B51280A311197E9816>108 +D<38F9C38038FFEFC0EBFFE0EA3C78A2EA3870AA38FE7CF8A2EB3C781512809116>I<EA +7E3CEAFEFEEA7FFF380F87801303120EAA387FC7F038FFE7F8387FC7F01512809116>I< +EA03E0EA0FF8487EEA3C1E487EEA700738E00380A5EAF00700701300EA780FEA3C1EEA1F +FC6C5AEA03E011127E9116>I<EA7E3EEAFEFF007F1380380F83C0EB00E0120E1470A614 +E0EA0F01EB83C0EBFF80000E1300133C90C7FCA6EA7FC0487E6C5A141B809116>I<38FF +0FC0EB3FE0137F3807F040EBC0005BA290C7FCA8EAFFFCA313127F9116>114 +D<EA0FECEA3FFC127FEAF03CEAE01CA2EA7000EA7F80EA1FF0EA07F8EA003CEA600E12E0 +12F0EAF81EEAFFFC13F8EAC7E00F127D9116>I<12035AA4EA7FFFB5FCA20007C7FCA75B +EB0380A3EB8700EA03FE6C5A6C5A11177F9616>I<387E1F80EAFE3FEA7E1FEA0E03AB13 +0F380FFFF03807FBF83803E3F01512809116>I<387F1FC000FF13E0007F13C0381C0700 +EA1E0FEA0E0EA36C5AA4EA03B8A3EA01F0A26C5A13127F9116>I<38FF1FE013BF131F38 +380380A413E33819F300A213B3EA1DB7A4EA0F1EA313127F9116>I<387F1FC0133F131F +380F1C00EA073CEA03B813F012016C5A12017FEA03B8EA073C131CEA0E0E387F1FC038FF +3FE0387F1FC013127F9116>I E /Fj 59 123 df<EB7E1F3901C1B180390303E3C00007 +13C3000EEBC180903801C000A6B512FC380E01C0B0387F87FC1A1D809C18>11 +D<137E3801C180EA0301380703C0120EEB018090C7FCA5B512C0EA0E01B0387F87F8151D +809C17>I<EA6060EAF0F0EAF8F8EA6868EA0808A3EA1010A2EA2020EA4040EA80800D0C +7F9C15>34 D<13E0EA0310EA0608A2120EA45BA25B6C5AEC3FE09038800F80EC06000003 +130412073809C00800115BEA30E03820F020EA607038E03840EB3C80131C90380F00207F +0070EB8040383009C0391830E180390FC03F001B1F7E9D20>38 D<1380EA010012021206 +5AA25AA25AA35AA412E0AC1260A47EA37EA27EA27E12027EEA0080092A7C9E10>40 +D<7E12407E12307EA27EA27EA37EA41380AC1300A41206A35AA25AA25A12205A5A092A7E +9E10>I<1306ADB612E0A2D80006C7FCAD1B1C7E9720>43 D<126012F0A212701210A412 +20A212401280040C7C830C>I<126012F0A2126004047C830C>46 +D<EA03C0EA0C30EA1818EA300CA2EA700EEA6006A2EAE007ADEA6006A2EA700EEA300CA2 +EA1818EA0C30EA07E0101D7E9B15>48 D<5A1207123F12C71207B3A5EAFFF80D1C7C9B15 +>I<EA07C0EA1830EA201CEA400C130EEAF00F12F81307A21270EA000F130EA2131CA213 +381370136013C0EA0180EA0300EA0601120C1218EA1002EA3FFE127F12FF101C7E9B15> +I<EA07E0EA1830EA201CA2EA781E130E131E1238EA001CA2131813301360EA07C0EA0030 +131CA2130E130FA2127012F8A3EAF00EEA401C1220EA1830EA07C0101D7E9B15>I<130C +A2131C133CA2135C13DC139CEA011C120312021204120C1208121012301220124012C0B5 +12C038001C00A73801FFC0121C7F9B15>I<EA300CEA3FF813F013C0EA2000A6EA23E0EA +2C30EA3018131CEA200E1200130FA3126012F0A3EA800EEA401E131CEA2038EA1870EA07 +C0101D7E9B15>I<13F0EA030CEA0404EA0C0EEA181E1230130CEA7000A21260EAE3E0EA +E430EAE818EAF00C130EEAE0061307A51260A2EA7006EA300E130CEA1818EA0C30EA03E0 +101D7E9B15>I<1240387FFF801400A2EA4002485AA25B485AA25B1360134013C0A21201 +5BA21203A41207A66CC7FC111D7E9B15>I<EA03E0EA0C30EA1008EA200C13061260A212 +70A2EA7C0CEA3E18EA3FB0EA1FE0EA07F013F8EA18FCEA307EEA601E130FEAC0071303A4 +EA60021304EA300CEA1C10EA07E0101D7E9B15>I<126012F0A212601200AA126012F0A2 +126004127C910C>58 D<126012F0A212601200AA126012F0A212701210A41220A2124012 +80041A7C910C>I<007FB512C0B612E0C9FCA8B612E06C14C01B0C7E8F20>61 +D<EA0FE0EA3038EA401CEAE00E12F0A3EA000C131C1330136013C0138012011300A6C7FC +A51203EA0780A2EA03000F1D7E9C14>63 D<B512F8380F007814181408A2140C1404A213 +011400A35B13FF13037FA490C7FCA8EAFFF8161C7E9B1B>70 D<EAFFF0EA0F00B3A8EAFF +F00C1C7F9B0F>73 D<EAFFF8EA0F8090C7FCB01408A31418A2141014301470EB01F0B5FC +151C7E9B1A>76 D<EB3F80EBE0E03803803848487E000E7F487F003C1480003813030078 +14C0A20070130100F014E0A8007014C000781303A200381480003C1307001C14006C130E +6C5B6C6C5A3800E0E0EB3F801B1E7E9C20>79 D<B5FC380F01E0EB007880141C141EA414 +1C143C5CEB01E001FFC7FCEB01E0EB0070147880A61510A2141CA239FFF00E20C7EA03C0 +1C1D7E9B1F>82 D<3807E080EA1C19EA30051303EA600112E01300A36C13007E127CEA7F +C0EA3FF8EA1FFEEA07FFC61380130FEB07C0130313011280A300C01380A238E00300EAD0 +02EACC0CEA83F8121E7E9C17>I<007FB512C038700F010060130000401440A200C01420 +1280A300001400B1497E3803FFFC1B1C7F9B1E>I<3AFFE1FFC0FF3A1F003E003C001E01 +3C13186C6D1310A32607801F1320A33A03C0278040A33A01E043C080A33A00F081E100A3 +9038F900F3017913F2A2017E137E013E137CA2013C133C011C1338A20118131801081310 +281D7F9B2B>87 D<12FEA212C0B3B312FEA207297C9E0C>91 D<EA0808EA1010EA2020EA +4040A2EA8080A3EAB0B0EAF8F8EA7878EA30300D0C7A9C15>I<12FEA21206B3B312FEA2 +0729809E0C>I<EA1FC0EA3070EA78387F12301200A2EA01FCEA0F1C12381270126000E0 +1340A3EA603C38304E80381F870012127E9115>97 D<12FC121CAA137CEA1D87381E0180 +381C00C014E014601470A6146014E014C0381E018038190700EA10FC141D7F9C17>I<EA +03F8EA0C0CEA181E1230EA700CEA600012E0A61260EA70021230EA1804EA0C18EA03E00F +127F9112>I<EB1F801303AAEA03F3EA0E0BEA1807EA30031270126012E0A61260127012 +30EA1807EA0E1B3803E3F0141D7F9C17>I<EA07E0EA0C30EA1818EA300CEA700EEA6006 +12E0EAFFFEEAE000A41260EA70021230EA1804EA0C18EA03E00F127F9112>I<13F8EA01 +8CEA071E1206EA0E0C1300A6EAFFE0EA0E00B0EA7FE00F1D809C0D>I<EB03803807C4C0 +EA1C783838388038301800EA701CA4EA3018EA38386C5AEA27C00020C7FCA21230EA3FF8 +6CB4FC1480EA2003386001C0EAC000A33860018038300300EA1C0EEA07F8121C7F9215> +I<12FC121CAA137C1387EA1D03001E1380121CAD38FF9FF0141D7F9C17>I<1218123CA2 +1218C7FCA712FC121CB0EAFF80091D7F9C0C>I<13C0EA01E0A2EA00C01300A7EA07E012 +00B3A21260EAF0C012F1EA6180EA3E000B25839C0D>I<12FC121CAAEB0FE0EB0780EB06 +005B13105B5B13E0121DEA1E70EA1C781338133C131C7F130F148038FF9FE0131D7F9C16 +>I<12FC121CB3A9EAFF80091D7F9C0C>I<39FC7E07E0391C838838391D019018001EEBE0 +1C001C13C0AD3AFF8FF8FF8021127F9124>I<EAFC7CEA1C87EA1D03001E1380121CAD38 +FF9FF014127F9117>I<EA03F0EA0E1CEA1806487E00701380EA600100E013C0A6006013 +80EA700300301300EA1806EA0E1CEA03F012127F9115>I<EAFC7CEA1D87381E0180001C +13C0EB00E0A21470A614E0A2EB01C0001E1380381D0700EA1CFC90C7FCA7B47E141A7F91 +17>I<3803E080EA0E19EA1805EA3807EA7003A212E0A61270A2EA38071218EA0E1BEA03 +E3EA0003A7EB1FF0141A7F9116>I<EAFCE0EA1D38EA1E78A2EA1C301300ACEAFFC00D12 +7F9110>I<EA1F90EA2070EA4030EAC010A212E0EAF800EA7F80EA3FE0EA0FF0EA00F8EA +8038131812C0A2EAE010EAD060EA8FC00D127F9110>I<1204A4120CA2121C123CEAFFE0 +EA1C00A91310A5120CEA0E20EA03C00C1A7F9910>I<38FC1F80EA1C03AD1307120CEA0E +1B3803E3F014127F9117>I<38FF07E0383C0380381C0100A2EA0E02A2EA0F06EA0704A2 +EA0388A213C8EA01D0A2EA00E0A3134013127F9116>I<39FF3FC7E0393C0703C0001CEB +01801500130B000E1382A21311000713C4A213203803A0E8A2EBC06800011370A2EB8030 +000013201B127F911E>I<38FF0FE0381E0700EA1C06EA0E046C5AEA039013B0EA01E012 +007F12011338EA021C1204EA0C0E487E003C138038FE1FF014127F9116>I<38FF07E038 +3C0380381C0100A2EA0E02A2EA0F06EA0704A2EA0388A213C8EA01D0A2EA00E0A31340A2 +5BA212F000F1C7FC12F312661238131A7F9116>I<EA7FFCEA70381260EA407013F013E0 +EA41C012031380EA0700EA0F04120E121CEA3C0CEA380812701338EAFFF80E127F9112> +I E end +%%EndProlog +%%BeginSetup +%%Feature: *Resolution 300dpi +TeXDict begin +%%PaperSize: a4 +%%BeginPaperSize: a4 +a4 +%%EndPaperSize + +%%EndSetup +%%Page: 6 1 +6 0 bop 0 42 a Fj(6)70 b Fg(WHA)m(T)14 b(A)e Ff(CWEB)f +Fg(PR)o(OGRAM)h(LOOKS)h(LIKE)893 b Fi(CWEBx)13 b Fg(MANUAL)0 +132 y Fh(1.)48 b(Compar)q(in)o(g)12 b(t)o(ext)g(\014le)q(s.)47 +b Fj(Thi)q(s)11 b(i)q(s)g(an)h(en)o(t)o(irely)f(tr)q(ivial)f(program,)g +(t)n(h)o(a)o(t)i(t)o(e)q(st)o(s)i(wh)o(et)n(h)o(er)f(t)o(w)o(o)e(t)o +(ext)h(\014le)q(s)h(are)f(equal,)0 181 y(an)o(d)i(if)f(not)g(so,)h(p)q +(oin)o(t)o(s)g(ou)o(t)g(t)n(h)o(e)h(\014rst)g(p)q(oin)o(t)e(of)g +(di\013erence.)0 244 y Fh(#includ)o(e)41 b Fi(<stdio.h>)0 +294 y Fh(#includ)o(e)g Fi(<stdlib.h>)42 357 y Fh(t)o(yp)q(e)q(d)o(ef)18 +b(c)o(h)o(ar)d(b)q(o)q(o)o(l)r Fj(;)0 457 y Fh(2.)48 +b Fj(Th)o(e)15 b(ou)o(t)n(lin)o(e)f(of)g(t)n(h)o(e)h(program)e(i)q(s)h +(s)q(imple.)j(W)m(e)d(read)h(c)o(h)o(aract)o(ers)i(f)q(rom)12 +b(b)q(ot)n(h)j(inpu)o(t)f(\014le)q(s)h(in)o(t)o(o)f Fd(c)1634 +463 y Fc(1)1667 457 y Fj(an)o(d)h Fd(c)1766 463 y Fc(2)1799 +457 y Fj(u)o(n)o(t)o(il)e(t)n(h)o(e)0 506 y(compar)q(i)q(son)f(i)q(s)h +(complet)o(e.)k(Lin)o(e)c(an)o(d)h(colu)o(mn)e(cou)o(n)o(t)o(s)k(are)e +(m)o(ain)o(t)o(ain)o(e)q(d)d(in)j Fb(line)19 b Fj(an)o(d)13 +b Fb(c)n(ol)6 b Fj(.)0 569 y Fh(#d)o(e\014n)o(e)42 b +Fb(left)p 268 569 13 2 v 18 w(mar)n(gin)26 b Fj(1)83 +b Fd(=)p Fe(\003)20 b Fj(left)o(most)13 b(colu)o(mn)f(n)n(u)o(m)n(b)q +(er;)h(c)o(h)o(an)o(ge)i(t)o(o)f(0)g(if)f(y)o(ou)g(prefer)22 +b Fe(\003)p Fd(=)42 632 y Fe(h)7 b Fj(F)m(u)o(nct)o(ions)18 +b Fg(5)6 b Fe(i)42 694 y Fh(in)o(t)14 b Fb(main)26 b +Fj(\()p Fh(in)o(t)14 b Fd(n;)20 b Fh(c)o(h)o(ar)c Fe(\003)o(\003)p +Fb(ar)n(g)6 b Fj(\))42 744 y Fe(f)20 b Fh(FILE)c Fe(\003)p +Fd(f)248 750 y Fc(1)267 744 y Fd(;)k Fe(\003)p Fd(f)340 +750 y Fc(2)359 744 y Fj(;)83 b Fd(=)p Fe(\003)20 b Fj(t)n(h)o(e)15 +b(t)o(w)o(o)e(inpu)o(t)h(\014le)q(s)21 b Fe(\003)p Fd(=)83 +794 y Fh(in)o(t)14 b Fd(c)174 800 y Fc(1)193 794 y Fd(;)20 +b(c)243 800 y Fc(2)262 794 y Fd(;)g Fb(c)n(ol)e Fe(\()11 +b Fb(left)p 477 794 V 18 w(mar)n(gin)5 b Fj(;)83 844 +y Fh(lon)o(g)16 b Fb(line)g Fe(\()11 b Fj(1;)83 907 y +Fe(h)c Fj(Op)q(en)15 b(t)n(h)o(e)f(\014le)q(s)h Fd(f)391 +913 y Fc(1)424 907 y Fj(an)o(d)f Fd(f)524 913 y Fc(2)543 +907 y Fj(,)f(t)o(akin)o(g)h(t)n(h)o(e)q(ir)g(n)o(am)o(e)q(s)g(f)q(rom)e +(t)n(h)o(e)i(comm)o(an)o(d)e(lin)o(e)h(or)h(f)q(rom)e(t)n(h)o(e)j(t)o +(ermin)o(al;)d(in)h(cas)q(e)i(of)166 957 y(an)f(error)h(for)e(whic)o(h) +h(no)f(reco)o(v)o(ery)j(i)q(s)d(p)q(oss)q(ible,)h(call)f +Fb(exit)6 b Fj(\(1\))17 b Fg(6)7 b Fe(i)83 1007 y(h)g +Fj(Searc)o(h)15 b(for)f(\014rst)h(di\013erence,)g(lea)o(vin)o(g)f +Fd(c)745 1013 y Fc(1)775 1007 y Fe(6)p Fj(=)e Fd(c)837 +1013 y Fc(2)869 1007 y Fj(if)h(an)o(d)h(only)f(if)g(a)g(di\013erence)j +(w)o(as)e(fou)o(n)o(d)k Fg(3)6 b Fe(i)83 1057 y(h)h Fj(Rep)q(ort)14 +b(t)n(h)o(e)h(ou)o(t)o(com)o(e)e(of)g(t)n(h)o(e)i(compar)q(i)q(son)g +Fg(4)7 b Fe(i)83 1107 y Fh(ret)o(ur)q(n)13 b Fj(0;)83 +b Fd(=)p Fe(\003)20 b Fj(su)o(cce)q(ssful)c(complet)o(ion)j +Fe(\003)p Fd(=)42 1157 y Fe(g)0 1257 y Fh(3.)48 b Fj(Th)o(e)12 +b(h)o(eart)h(of)f(t)n(h)o(e)h(program)d(i)q(s)i(t)n(hi)q(s)g(s)q(imple) +d(lo)q(o)o(p.)17 b(Wh)o(en)12 b(w)o(e)g(reac)o(h)i(t)n(h)o(e)f(en)o(d)f +(of)g(on)o(e)g(of)f(t)n(h)o(e)i(\014le)q(s,)g(t)n(h)o(e)f(\014le)q(s)h +(m)o(a)o(t)o(c)o(h)e(if)0 1307 y(an)o(d)i(only)g(if)g(t)n(h)o(e)h(ot)n +(h)o(er)h(\014le)f(h)o(as)f(also)g(reac)o(h)o(e)q(d)j(it)o(s)e(en)o(d.) +k(F)m(or)c(t)n(hi)q(s)f(reason)i(t)n(h)o(e)f(t)o(e)q(st)h +Fd(c)1357 1313 y Fc(1)1387 1307 y Fj(=)d Fd(c)1449 1313 +y Fc(2)1468 1307 y Fj(,)h(whic)o(h)g(require)q(s)i(c)o(h)o(aract)o(ers) +0 1357 y(t)o(o)h(b)q(e)g(read)g(f)q(rom)e(b)q(ot)n(h)i(\014le)q(s,)g(m) +n(ust)f(prece)q(d)o(e)k(t)n(h)o(e)d(t)o(e)q(st)h(for)e(\014le)h(en)o +(d;)g(wh)o(en)h(only)d(on)o(e)i(\014le)g(en)o(ds,)g(it)g(i)q(s)f(t)n(h) +o(e)h(form)o(er)f(t)o(e)q(st)0 1406 y(whic)o(h)f(breaks)h(t)n(h)o(e)f +(lo)q(o)o(p.)0 1469 y Fe(h)7 b Fj(Searc)o(h)15 b(for)f(\014rst)h +(di\013erence,)g(lea)o(vin)o(g)f Fd(c)662 1475 y Fc(1)692 +1469 y Fe(6)p Fj(=)e Fd(c)754 1475 y Fc(2)786 1469 y +Fj(if)h(an)o(d)h(only)f(if)g(a)g(di\013erence)j(w)o(as)e(fou)o(n)o(d)k +Fg(3)6 b Fe(i)12 b(\021)42 1519 y Fh(while)i Fj(\(\()p +Fd(c)217 1525 y Fc(1)248 1519 y Fe(\()d Fb(getc)t Fj(\()p +Fd(f)412 1525 y Fc(1)431 1519 y Fj(\)\))h(=)g(\()p Fd(c)553 +1525 y Fc(2)583 1519 y Fe(\()f Fb(getc)5 b Fj(\()p Fd(f)748 +1525 y Fc(2)767 1519 y Fj(\)\))k Fe(^)g Fd(c)863 1525 +y Fc(1)893 1519 y Fe(6)p Fj(=)j Fi(EOF)r Fj(\))83 1569 +y Fh(if)20 b Fj(\()p Fd(c)166 1575 y Fc(1)196 1569 y +Fj(=)12 b Fi('\\n')r Fj(\))20 b Fe(f)410 1565 y Fc(+)l(+)458 +1569 y Fb(line)5 b Fj(;)21 b Fb(c)n(ol)c Fe(\()12 b Fb(left)p +745 1569 V 18 w(mar)n(gin)5 b Fj(;)20 b Fe(g)h Fh(els)q(e)1064 +1565 y Fc(+)l(+)1113 1569 y Fb(c)n(ol)6 b Fj(;)0 1623 +y Fg(This)12 b(co)q(d)o(e)f(is)h(us)q(e)q(d)e(in)h(s)q(ect)o(ion)f(2.)0 +1723 y Fh(4.)48 b Fj(Wh)o(en)14 b(t)n(h)o(e)h(\014rst)g(di\013erence)h +(o)q(ccurs)f(a)o(t)f(t)n(h)o(e)h(en)o(d)f(of)f(on)o(e)h(of)g(t)n(h)o(e) +h(\014le)q(s,)f(or)g(a)o(t)f(t)n(h)o(e)i(en)o(d)f(of)g(a)g(lin)o(e,)e +(w)o(e)j(giv)o(e)e(a)h(m)o(e)q(ssage)0 1773 y(in)o(dica)o(t)o(in)o(g)f +(t)n(hi)q(s)g(f)q(act.)0 1836 y Fe(h)7 b Fj(Rep)q(ort)14 +b(t)n(h)o(e)h(ou)o(t)o(com)o(e)e(of)g(t)n(h)o(e)i(compar)q(i)q(son)g +Fg(4)7 b Fe(i)12 b(\021)42 1886 y Fh(if)19 b Fj(\()p +Fd(c)124 1892 y Fc(1)154 1886 y Fj(=)12 b Fd(c)216 1892 +y Fc(2)235 1886 y Fj(\))21 b Fb(printf)10 b Fj(\()p Fi("Files)p +Ff( )p Fi(match.\\n")r Fj(\);)42 1936 y Fh(els)q(e)42 +1986 y Fe(f)20 b Fb(printf)10 b Fj(\()p Fi("Files)p Ff( )p +Fi(differ.\\n")r Fj(\);)83 2036 y Fh(if)20 b Fj(\()p +Fd(c)166 2042 y Fc(1)196 2036 y Fj(=)12 b Fi(EOF)e Fe(_)f +Fd(c)371 2042 y Fc(2)401 2036 y Fj(=)j Fi(EOF)r Fj(\))125 +2086 y Fe(f)20 b Fb(the)p 226 2086 V 19 w(\014le)5 b +Fj(\()p Fd(c)331 2092 y Fc(1)361 2086 y Fj(=)12 b Fi(EOF)r +Fj(\);)20 b Fb(printf)10 b Fj(\()p Fi("is)p Ff( )p Fi(contained)p +Ff( )p Fi(in)p Ff( )p Fi(th)o(e)p Ff( )p Fi(oth)o(er)p +Ff( )p Fi(a)o(s)p Ff( )p Fi(ini)o(tial)p Ff( )o Fi(segme)o(nt.\\n)o(")r +Fj(\);)18 b Fe(g)83 2136 y Fh(els)q(e)d(if)20 b Fj(\()p +Fd(c)259 2142 y Fc(1)289 2136 y Fj(=)12 b Fi('\\n')e +Fe(_)f Fd(c)486 2142 y Fc(2)516 2136 y Fj(=)j Fi('\\n')r +Fj(\))125 2186 y Fe(f)20 b Fb(the)p 226 2186 V 19 w(\014le)5 +b Fj(\()p Fd(c)331 2192 y Fc(1)361 2186 y Fj(=)12 b Fi('\\n')r +Fj(\);)20 b Fb(printf)10 b Fj(\()p Fi("has)p Ff( )p Fi(a)p +Ff( )p Fi(shorter)p Ff( )p Fi(lin)o(e)p Ff( )p Fi(num)o(ber)p +Ff( )o Fi(\045ld)p Ff( )p Fi(t)o(han)p Ff( )p Fi(t)o(he)p +Ff( )p Fi(ot)o(her.\\)o(n")r Fd(;)d Fb(l)o(ine)s Fj(\);)20 +b Fe(g)83 2236 y Fh(els)q(e)i Fb(printf)10 b Fj(\()p +Fi("First)p Ff( )p Fi(difference)p Ff( )p Fi(a)o(t)p +Ff( )p Fi(lin)o(e)p Ff( )p Fi(\045ld)o(,)p Ff( )p Fi(co)o(lumn)p +Ff( )o Fi(\045d.\\n)o(")r Fd(;)d Fb(l)o(ine)s Fd(;)g +Fb(c)n(ol)f Fj(\);)42 2286 y Fe(g)0 2341 y Fg(This)12 +b(co)q(d)o(e)f(is)h(us)q(e)q(d)e(in)h(s)q(ect)o(ion)f(2.)0 +2440 y Fh(5.)48 b Fj(Th)o(e)13 b(fu)o(nct)o(ion)g Fb(the)p +385 2440 V 15 w(\014le)18 b Fj(st)o(art)o(s)c(a)f(s)q(en)o(t)o(ence)h +(a)o(b)q(ou)o(t)f(t)n(h)o(e)h(\014rst)g(or)f(s)q(econ)o(d)g(\014le,)g +(d)o(ep)q(en)o(din)o(g)g(on)g(it)o(s)g(b)q(o)q(olean)f(argu)o(m)o(en)o +(t.)0 2503 y Fe(h)7 b Fj(F)m(u)o(nct)o(ions)18 b Fg(5)7 +b Fe(i)k(\021)42 2553 y Fh(v)o(oid)j Fb(the)p 205 2553 +V 19 w(\014le)26 b Fj(\()p Fh(b)q(o)q(o)o(l)16 b Fb(is)p +455 2553 V 18 w(\014rst)6 b Fj(\))21 b Fe(f)f Fb(printf)10 +b Fj(\()p Fi("The)p Ff( )p Fi(\045s)p Ff( )p Fi(file)p +Ff( )p Fi(")r Fd(;)d Fb(is)p 1116 2553 V 16 w(\014rst)17 +b Fj(?)11 b Fi("first")h Fj(:)f Fi("second")r Fj(\);)19 +b Fe(g)0 2607 y Fg(See)11 b(also)f(s)q(ect)o(ion)g(7.)0 +2662 y(This)i(co)q(d)o(e)f(is)h(us)q(e)q(d)e(in)h(s)q(ect)o(ion)f(2.)p +eop +%%Page: 7 2 +7 1 bop 0 42 a Fi(CWEBx)13 b Fg(MANUAL)895 b(WHA)m(T)13 +b(A)f Ff(CWEB)f Fg(PR)o(OGRAM)i(LOOKS)g(LIKE)70 b Fj(7)0 +132 y Fh(6.)48 b Fj(Th)o(ere)16 b(can)f(b)q(e)g(b)q(e)h(zero,)f(on)o(e) +h(or)f(t)o(w)o(o)f(comm)o(an)o(d)e(lin)o(e)j(argu)o(m)o(en)o(t)o(s.)21 +b(If)15 b(t)n(h)o(ere)h(are)g(non)o(e,)f(t)n(h)o(e)h(us)q(er)g(i)q(s)e +(prompt)o(e)q(d)h(t)o(o)0 181 y(sup)o(ply)h(t)n(h)o(em,)f(an)o(d)h(if)f +(t)n(h)o(ere)j(are)f(t)o(w)o(o)f(t)n(h)o(e)q(s)q(e)h(are)g(t)o(ak)o(en) +f(as)g(t)n(h)o(e)h(\014le)g(n)o(am)o(e)q(s,)e(prompt)o(in)o(g)g(t)n(h)o +(e)i(us)q(er)g(only)e(in)h(cas)q(e)h(a)f(\014le)0 231 +y(could)d(not)g(b)q(e)h(o)o(p)q(en)o(e)q(d.)k(In)13 b(cas)q(e)h(just)g +(on)o(e)f(argu)o(m)o(en)o(t)f(i)q(s)h(pre)q(s)q(en)o(t,)h(t)n(h)o(e)g +(\014rst)h(\014le)e(i)q(s)g(assu)o(m)o(e)q(d)g(t)o(o)h(b)q(e)f(t)n(h)o +(e)h(st)o(an)o(d)o(ard)g(inpu)o(t,)0 281 y(whic)o(h)h(do)q(e)q(s)h(not) +f(h)o(a)o(v)o(e)g(t)o(o)h(b)q(e)g(o)o(p)q(en)o(e)q(d;)f(in)g(t)n(hi)q +(s)h(cas)q(e)g(h)o(o)o(w)o(ev)o(er)g(w)o(e)g(will)e(not)h(read)h(a)g +(\014le)f(n)o(am)o(e)f(f)q(rom)f(t)o(ermin)o(al)h(in)h(cas)q(e)0 +331 y(t)n(h)o(e)g(s)q(econ)o(d)f(\014le)g(cannot)g(b)q(e)g(o)o(p)q(en)o +(e)q(d.)0 393 y Fh(#d)o(e\014n)o(e)42 b Fb(r)n(e)n(ad)p +287 393 13 2 v 19 w(mo)n(de)27 b Fi("r")0 455 y Fe(h)7 +b Fj(Op)q(en)15 b(t)n(h)o(e)g(\014le)q(s)f Fd(f)308 461 +y Fc(1)341 455 y Fj(an)o(d)g Fd(f)441 461 y Fc(2)460 +455 y Fj(,)g(t)o(akin)o(g)g(t)n(h)o(e)q(ir)g(n)o(am)o(e)q(s)g(f)q(rom)e +(t)n(h)o(e)j(comm)o(an)o(d)c(lin)o(e)j(or)g(f)q(rom)e(t)n(h)o(e)j(t)o +(ermin)o(al;)d(in)i(cas)q(e)h(of)e(an)125 505 y(error)i(for)e(whic)o(h) +h(no)f(reco)o(v)o(ery)i(i)q(s)f(p)q(oss)q(ible,)f(call)h +Fb(exit)5 b Fj(\(1\))18 b Fg(6)7 b Fe(i)k(\021)44 551 +y Fa(\000)l(\000)93 555 y Fd(n)p Fj(;)153 551 y Fc(+)l(+)202 +555 y Fb(ar)n(g)5 b Fj(;)83 b Fd(=)p Fe(\003)20 b Fj(ignore)14 +b(\\argu)o(m)o(en)o(t")e(0,)h(whic)o(h)h(i)q(s)f(t)n(h)o(e)i(program)d +(n)o(am)o(e)19 b Fe(\003)p Fd(=)42 605 y Fh(if)g Fj(\()p +Fd(n)12 b Fj(=)g(0\))83 655 y Fe(f)21 b Fb(op)n(en)p +213 655 V 19 w(\014le)5 b Fj(\(&)p Fd(f)352 661 y Fc(1)371 +655 y Fd(;)i Fi("First)p Ff( )p Fi(file)p Ff( )p Fi(to)p +Ff( )p Fi(co)o(mpar)o(e")r Fd(;)g Fe(\014)p Fj(\);)17 +b Fb(op)n(en)p 1080 655 V 20 w(\014le)5 b Fj(\(&)p Fd(f)1220 +661 y Fc(2)1239 655 y Fd(;)i Fi("Second)p Ff( )p Fi(file)p +Ff( )p Fi(to)p Ff( )o Fi(compa)o(re")r Fd(;)f Fe(\014)p +Fj(\);)18 b Fe(g)42 704 y Fh(els)q(e)d(if)k Fj(\()p Fd(n)12 +b Fj(=)g(1\))42 754 y Fe(f)20 b Fd(f)103 760 y Fc(1)133 +754 y Fe(\()12 b Fb(stdin)5 b Fj(;)83 804 y Fh(if)20 +b Fj(\(\()p Fd(f)184 810 y Fc(2)214 804 y Fe(\()11 b +Fb(fop)n(en)6 b Fj(\()p Fe(\003)p Fb(ar)n(g)g Fd(;)h +Fb(r)n(e)n(ad)p 568 804 V 18 w(mo)n(de)f Fj(\)\))12 b(=)g +Fe(\014)p Fj(\))21 b Fe(f)f Fb(printf)11 b Fj(\()p Fi("Could)p +Ff( )p Fi(not)p Ff( )p Fi(open)p Ff( )o Fi(file)p Ff( )o +Fi(\045s.\\n)o(")r Fd(;)c Fe(\003)l Fb(ar)n(g)f Fj(\);)20 +b Fb(exit)6 b Fj(\(1\);)21 b Fe(g)42 854 y(g)42 904 y +Fh(els)q(e)15 b(if)k Fj(\()p Fd(n)12 b Fj(=)g(2\))42 +953 y Fe(f)20 b Fb(op)n(en)p 171 953 V 19 w(\014le)6 +b Fj(\(&)p Fd(f)311 959 y Fc(1)330 953 y Fd(;)h Fi("Give)p +Ff( )p Fi(another)p Ff( )o Fi(first)o Ff( )p Fi(file)o(")r +Fd(;)g Fe(\003)m Fb(ar)n(g)998 949 y Fc(+)l(+)1047 953 +y Fj(\);)83 1003 y Fb(op)n(en)p 171 1003 V 19 w(\014le)f +Fj(\(&)p Fd(f)311 1009 y Fc(2)330 1003 y Fd(;)h Fi("Give)p +Ff( )p Fi(another)p Ff( )o Fi(secon)o(d)p Ff( )p Fi(fil)o(e")r +Fd(;)g Fe(\003)l Fb(ar)n(g)f Fj(\);)42 1053 y Fe(g)42 +1103 y Fh(els)q(e)21 b Fe(f)g Fb(printf)10 b Fj(\()p +Fi("No)p Ff( )p Fi(more)p Ff( )p Fi(than)p Ff( )p Fi(two)p +Ff( )p Fi(c)o(omman)o(d)p Ff( )p Fi(lin)o(e)p Ff( )p +Fi(ar)o(gumen)o(ts)p Ff( )p Fi(ar)o(e)p Ff( )p Fi(all)o(owed.)o(\\n")r +Fj(\);)17 b Fb(exit)6 b Fj(\(1\);)21 b Fe(g)0 1157 y +Fg(This)12 b(co)q(d)o(e)f(is)h(us)q(e)q(d)e(in)h(s)q(ect)o(ion)f(2.)0 +1256 y Fh(7.)48 b Fj(Th)o(e)18 b(fu)o(nct)o(ion)g Fb(op)n(en)p +423 1256 V 16 w(\014le)23 b Fj(will)17 b(try)h(t)o(o)g(o)o(p)q(en)g(t)n +(h)o(e)g(\014le)g Fb(name)24 b Fj(for)18 b(readin)o(g,)h(an)o(d)e(if)g +(t)n(hi)q(s)h(f)q(ails)f(it)g(will)g(prompt)f(for)0 1306 +y(anot)n(h)o(er)h(\014le)g(n)o(am)o(e)f(u)o(n)o(t)o(il)g(it)g(h)o(as)h +(su)o(cce)q(ss.)30 b(If)16 b(calle)q(d)h(wit)n(h)f Fb(name)22 +b Fj(=)17 b Fe(\014)p Fj(,)g(t)n(h)o(e)h(fu)o(nct)o(ion)f(st)o(art)o(s) +h(wit)n(h)e(prompt)o(in)o(g)g(r)q(igh)o(t)0 1356 y(aw)o(ay)m(.)0 +1418 y Fe(h)7 b Fj(F)m(u)o(nct)o(ions)18 b Fg(5)7 b Fe(i)k +Fj(+)p Fe(\021)42 1468 y Fh(v)o(oid)j Fb(op)n(en)p 233 +1468 V 19 w(\014le)26 b Fj(\()p Fh(FILE)17 b Fe(\003\003)p +Fd(f)r(;)j Fh(c)o(h)o(ar)c Fe(\003)o Fb(pr)n(ompt)6 b +Fd(;)20 b Fh(c)o(h)o(ar)c Fe(\003)o Fb(name)6 b Fj(\))42 +1518 y Fe(f)20 b Fh(c)o(h)o(ar)15 b Fb(buf)c Fj([80)o(];)83 +1580 y Fh(if)20 b Fj(\()p Fb(name)d Fj(=)12 b Fe(\014)d(_)g +Fj(\()p Fe(\003)p Fd(f)17 b Fe(\()11 b Fb(fop)n(en)5 +b Fj(\()p Fb(name)h Fd(;)h Fb(r)n(e)n(ad)p 831 1580 V +19 w(mo)n(de)f Fj(\)\))12 b(=)f Fe(\014)p Fj(\))125 1630 +y Fh(do)22 b Fe(f)f Fb(printf)10 b Fj(\()p Fi("\045s:)p +Ff( )p Fi(")r Fd(;)d Fb(pr)n(ompt)t Fj(\);)21 b Fb(\017ush)5 +b Fj(\()p Fb(stdout)i Fj(\);)20 b Fb(sc)n(anf)12 b Fj(\()p +Fi("\04579s")r Fd(;)7 b Fb(buf)i Fj(\);)21 b Fe(g)125 +1680 y Fh(while)14 b Fj(\(\()p Fe(\003)p Fd(f)j Fe(\()11 +b Fb(fop)n(en)5 b Fj(\()p Fb(buf)12 b Fd(;)7 b Fb(r)n(e)n(ad)p +676 1680 V 18 w(mo)n(de)f Fj(\)\))12 b(=)g Fe(\014)p +Fj(\);)42 1730 y Fe(g)0 1829 y Fh(8.)48 b(In)o(d)o(ex.)0 +1896 y Fb(ar)n(g)5 b Fj(:)42 b(2)p 115 1903 21 2 v -1 +w(,)23 b(6.)0 1946 y Fh(b)q(o)q(o)o(l)r Fj(:)41 b(1)p +145 1953 V(,)22 b(5.)0 1996 y Fb(buf)11 b Fj(:)41 b(7)p +118 2003 V(.)0 2047 y Fb(c)n(ol)6 b Fj(:)41 b(2)p 108 +2054 V(,)22 b(3,)g(4.)0 2097 y Fd(c)18 2103 y Fc(1)37 +2097 y Fj(:)41 b(2)p 90 2104 V -1 w(,)23 b(3,)f(4.)0 +2147 y Fd(c)18 2153 y Fc(2)37 2147 y Fj(:)41 b(2)p 90 +2154 V -1 w(,)23 b(3,)f(4.)0 2197 y Fi(EOF)r Fj(:)41 +b(3,)22 b(4.)0 2248 y Fb(exit)6 b Fj(:)41 b(6.)0 2298 +y Fd(f)t Fj(:)h(7)p 78 2305 V(.)0 2348 y Fb(\017ush)6 +b Fj(:)41 b(7.)0 2398 y Fb(fop)n(en)6 b Fj(:)41 b(6,)22 +b(7.)0 2449 y Fd(f)20 2455 y Fc(1)39 2449 y Fj(:)41 b(2)p +92 2456 V(,)22 b(3,)g(6.)0 2499 y Fd(f)20 2505 y Fc(2)39 +2499 y Fj(:)41 b(2)p 92 2506 V(,)22 b(3,)g(6.)0 2549 +y Fb(getc)5 b Fj(:)41 b(3.)0 2599 y Fb(is)p 33 2599 13 +2 v 15 w(\014rst)6 b Fj(:)41 b(5)p 176 2606 21 2 v -1 +w(.)0 2650 y Fb(left)p 60 2650 13 2 v 14 w(mar)n(gin)6 +b Fj(:)41 b(2)p 256 2657 21 2 v -1 w(,)21 b(3.)0 2700 +y Fb(line)5 b Fj(:)41 b(2)p 124 2707 V(,)22 b(3,)g(4.)1017 +1896 y Fb(main)5 b Fj(:)41 b(2)p 1166 1903 V(.)1017 1946 +y Fd(n)p Fj(:)f(2)p 1094 1953 V(.)1017 1995 y Fb(name)5 +b Fj(:)41 b(7)p 1172 2002 V(.)1017 2045 y Fb(op)n(en)p +1102 2045 13 2 v 16 w(\014le)5 b Fj(:)41 b(6,)21 b(7)p +1280 2052 21 2 v(.)1017 2095 y Fb(printf)10 b Fj(:)41 +b(4,)20 b(5,)h(6,)g(7.)1017 2145 y Fb(pr)n(ompt)5 b Fj(:)41 +b(7)p 1202 2152 V(.)1017 2195 y Fb(r)n(e)n(ad)p 1095 +2195 13 2 v 15 w(mo)n(de)5 b Fj(:)41 b(6)p 1258 2202 +21 2 v(,)21 b(7.)1017 2244 y Fb(sc)n(anf)11 b Fj(:)41 +b(7.)1017 2294 y Fb(stdin)5 b Fj(:)41 b(6.)1017 2344 +y Fb(stdout)6 b Fj(:)41 b(7.)1017 2394 y Fb(the)p 1074 +2394 13 2 v 15 w(\014le)5 b Fj(:)41 b(4,)22 b(5)p 1252 +2401 21 2 v(.)p eop +%%Trailer +end +userdict /end-hook known{end-hook}if +%%EOF diff --git a/web/c_cpp/macweave b/web/c_cpp/macweave new file mode 120000 index 0000000000..221e337e3c --- /dev/null +++ b/web/c_cpp/macweave @@ -0,0 +1 @@ +../systems/mac/macweave
\ No newline at end of file |