1291 lines · 46.6 KB · Text
RawDownload
% This file is part of CWEB.% This program by Silvio Levy and Donald E. Knuth% is based on a program by Knuth.% It is distributed WITHOUT ANY WARRANTY, express or implied.% Version 4.3 --- May 2021 % Copyright (C) 1987,1990,1993,2000 Silvio Levy and Donald E. Knuth % Permission is granted to make and distribute verbatim copies of this% document provided that the copyright notice and this permission notice% are preserved on all copies. % Permission is granted to copy and distribute modified versions of this% document under the conditions for verbatim copying, provided that the% entire resulting derived work is given a different name and distributed% under the terms of a permission notice identical to this one. % Amendments to 'common.w' resulting in this updated version were created% by numerous collaborators over the course of many years. % Please send comments, suggestions, etc. to tex-k@@tug.org. \def\v{\char'174} % vertical (|) in typewriter font \def\title{Common code for CTANGLE and CWEAVE (Version 4.3)}\def\topofcontents{\null\vfill  \centerline{\titlefont Common code for {\ttitlefont CTANGLE} and    {\ttitlefont CWEAVE}}  \vskip 15pt  \centerline{(Version 4.3)}  \vfill}\def\botofcontents{\vfill\noindentCopyright \copyright\ 1987, 1990, 1993, 2000 Silvio Levy and Donald E. Knuth\bigskip\noindentPermission is granted to make and distribute verbatim copies of thisdocument provided that the copyright notice and this permission noticeare preserved on all copies. \smallskip\noindentPermission is granted to copy and distribute modified versions of thisdocument under the conditions for verbatim copying, provided that theentire resulting derived work is given a different name and distributedunder the terms of a permission notice identical to this one.} \pageno=\contentspagenumber \advance\pageno by 1\let\maybe=\iftrue @** Introduction.  This file contains code commonto both \.{CTANGLE} and \.{CWEAVE}, which roughly concerns the followingproblems: character uniformity, input routines, error handling andparsing of command line.  We have tried to concentrate in this fileall the system dependencies, so as to maximize portability. In the texts below we willsometimes use \.{CWEB} to refer to either of the two componentprograms, if no confusion can arise. The file begins with a few basic definitions. @c@<Include files@>@/@h@<Common code for \.{CWEAVE} and \.{CTANGLE}@>@/@<Global variables@>@/@<Predeclaration of procedures@> @ The details will be filled in due course.  The interface of this moduleis included first.  It is also used by the main programs. @i common.h @ In certain cases \.{CTANGLE} and \.{CWEAVE} should do almost, but notquite, the same thing.  In these cases we've written common code forboth, differentiating between the two by means of the global variable|program|. @<Global var...@>=boolean program; /* \.{CWEAVE} or \.{CTANGLE}? */ @ \.{CWEAVE} operates in three phases: First it inputs the sourcefile and stores cross-reference data, then it inputs the source once again andproduces 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. @<Global var...@>=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 thisprocedure later. @cvoidcommon_init(void){  @<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. A few character pairs are encoded internally as single characters,using the definitions in the interface sections above. These definitionsare consistent with an extension of ASCII code originally developed atMIT and explained in Appendix~C of {\sl The \TEX/book\/}; thus, userswho have such a character set can type things like \.{\char'32} and\.{\char'4} instead of \.{!=} and \.{\&\&}. (However, their files willnot be too portable until more people adopt the extended code.) If the character set is not ASCII, the definitions given may conflictwith existing characters; in such cases, other arbitrary codes should besubstituted. The indexes to \.{CTANGLE} and \.{CWEAVE} mention everycase where similar codes may have to be changed in order toavoid character conflicts. Look for the entry ``ASCII code dependencies''in those indexes. @^ASCII code dependencies@>@^system dependencies@> @<Global var...@>=char section_text[longest_name+1]; /* text 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 */ @** Input routines.  The lowest level of input to the \.{CWEB} programsis 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 (generally this means the file has ended). The conventionsof \TEX/ are followed; i.e., the characters of the next line of the fileare 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 lessthan |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. @<Global var...@>=char buffer[long_buf_size]; /* where each line of input goes */char *buffer_end=buffer+buf_size-2; /* end of |buffer| */char *loc=buffer; /* points to the next character to be read from the buffer */char *limit=buffer; /* points to the last character in the buffer */ @ In the unlikely event that your standard I/O library does notsupport |feof|, |getc|, and |ungetc| you may have to change things here.@^system dependencies@> @cstatic boolean input_ln( /* copies a line into |buffer| or returns |false| */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 false; /* 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 false; /* there was nothing after    the last newline */  return true;} @ @<Predecl...@>=@+static boolean input_ln(FILE *); @ Now comes the problem of deciding which file to read from next.Recall that the actual text that \.{CWEB} should process comes from twostreams: a |web_file|, which can contain possibly nested includecommands \.{@@i}, and a |change_file|, which might also containincludes.  The |web_file| together with the currently open includefiles form a stack |file|, whose names are stored in a parallel stack|file_name|.  The boolean |changing| tells whether or not we're readingfrom the |change_file|. The line number of each open file is also kept for error reporting andfor the benefit of \.{CTANGLE}. @<Global var...@>=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 */static 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=false; /* if the web file is being read */ @ When |changing==false|, the next line of |change_file| is kept in|change_buffer|, for purposes of comparison with the nextline of |cur_file|. After the change file has been completely input, weset |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, (size_t)(limit-buffer))) @<Global var...@>=static char change_buffer[buf_size]; /* next line of |change_file| */static 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 ifthe change file is exhausted. This procedure is called only when|changing| is |true|; hence error messages will be reported correctly. @cstatic voidprime_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 nonblank line; |return| if end of file@>@;  @<Move |buffer| and |limit| to |change_buffer| and |change_limit|@>@;} @ @<Predecl...@>=@+static void prime_the_change_buffer(void); @ While looking for a line that begins with \.{@@x} in the change file, weallow 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(true) {  change_line++;  if (!input_ln(change_file)) return;  if (limit<buffer+2) continue;  if (buffer[0]!='@@') continue;  if (xisupper(buffer[1])) buffer[1]=tolower((eight_bits)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+(ptrdiff_t)(limit-buffer);  strncpy(change_buffer,buffer,(size_t)(limit-buffer+1));} @ The following procedure is used to see if the next change entry shouldgo into effect; it is called only when |changing| is |false|.The idea is to test whether or not the currentcontents 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 errormessage is issued if any discrepancy is found. Then the procedureprepares to read the next line from |change_file|. When a match is found, the current section is marked as changed unlessthe first line after the \.{@@x} and after the \.{@@y} both start witheither `\.{@@*}' or `\.{@@\ }' (possibly preceded by whitespace). This procedure is called only when |buffer<limit|, i.e., when thecurrent 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;} @cstatic voidcheck_change(void) /* switches to |change_file| if the buffers match */{  int n=0; /* the number of discrepancies found */  if (lines_dont_match) return;  change_pending=false;  if (!changed_section[section_count]) {    if_section_start_make_pending(true);    if (!change_pending) changed_section[section_count]=true;  }  while (true) {    changing=print_where=true; change_line++;    if (!input_ln(change_file)) {      err_print("! Change file ended before @@y");@.Change file ended...@>      change_limit=change_buffer; changing=false;      return;    }    if (limit>buffer+1 && buffer[0]=='@@') {      char xyz_code=xisupper(buffer[1])? tolower((eight_bits)buffer[1]): buffer[1];      @<If the current line starts with \.{@@y},        report any discrepancies and |return|@>@;    }    @<Move |buffer| and |limit|...@>@;    changing=false; 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=true; return;      }      include_depth--; cur_line++;    }    if (lines_dont_match) n++;  }} @ @<Predecl...@>=@+static void check_change(void); @ @<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 theuser's \.{CWEB} input, is used at the beginning of phase one of \.{CTANGLE},phases one and two of \.{CWEAVE}. @cvoidreset_input(void){  limit=buffer; loc=buffer+1; buffer[0]=' ';  @<Open input files@>@;  include_depth=cur_line=change_line=0;  change_depth=include_depth;  changing=true; prime_the_change_buffer(); changing=!changing;  limit=buffer; loc=buffer+1; buffer[0]=' '; input_has_ended=false;} @ 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=true;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 nextline of merged input into the buffer and updates the other variablesappropriately. A space is placed at the right end of the line.This procedure returns |!input_has_ended| because we often want tocheck the value of that variable after calling the procedure. If we've just changed from the |cur_file| to the |change_file|, or ifthe |cur_file| has changed, we tell \.{CTANGLE} to print thisinformation in the \CEE/ file by means of the |print_where| flag. @<Global var...@>=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=false; /* should \.{CTANGLE} print line and file info? */ @ @cboolean get_line(void) /* 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 false;  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 true;} @ When an \.{@@i} line is found in the |cur_file|, we must temporarilystop reading it and start reading from the named include file.  The\.{@@i} line should give a complete file name with or withoutdouble quotes.If the environment variable \.{CWEBINPUTS} is set, or if the compiler flagof the same name was defined at compile time,\.{CWEB} will look for include files in the directory thus named, ifit 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;} @<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 *kk, *k=cur_file_name;  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=true;    goto restart; /* success */  }  if ((kk=getenv("CWEBINPUTS"))!=NULL) {@.CWEBINPUTS@>    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=true;      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=true;    if (include_depth==0) {input_has_ended=true; break;}    else {      fclose(cur_file); include_depth--;

Lines 1–500 of 1291.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t