⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 wmerge.w

📁 模拟器提供了一个简单易用的平台
💻 W
📖 第 1 页 / 共 2 页
字号:
\def\9#1{} % this hack is explained in CWEB manual Appendix F11@* Introduction.  This file contains the program \.{wmerge},which takes two or more files and merges them accordingto the conventions of \.{CWEB}. Namely, it takes an ordinary \.{.w}file and and optional \.{.ch} file and sends the corresponding\.{.w}-style file to standard output (or to a named file), expanding all ``includes''that might be specified by \.{@@i} in the original \.{.w} file.(A more precise description appears in the section on ``command linearguments'' below.)@c#include <stdio.h>#include <stdlib.h> /* declaration of |getenv| */#include <ctype.h> /* definition of |isalpha|, |isdigit| and so on */@<Definitions@>@;@<Predeclarations of functions@>@;@<Functions@>@;main (ac,av)int ac; char **av;{  argc=ac; argv=av;  @<Set the default options@>;  @<Scan arguments and open output file@>;  reset_input();  while (get_line())    put_line();  fflush(out_file);  check_complete();  fflush(out_file);  return wrap_up();}@ @<Definitions@>=typedef short boolean;typedef unsigned char eight_bits;typedef char ASCII; /* type of characters inside \.{WEB} */@ We predeclare some standard string-handling functions here instead ofincluding their system header files, because the names of the header filesare not as standard as the names of the functions. (There's confusionbetween \.{<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 */@ @<Predec...@>=@ The lowest level of input to the \.{WEB} programsis 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 ifnot (generally this means the file has ended).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.Some of the routines use the fact that it is safe to refer to|*(limit+2)| without overstepping the bounds of the array.@d buf_size 1024@<Definitions...@>=ASCII buffer[buf_size]; /* where each line of input goes */ASCII *buffer_end=buffer+buf_size-2; /* end of |buffer| */ASCII *limit; /* points to the last character in the buffer */ASCII *loc; /* points to the next character to be read from 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@>Incidentally, here's a curious fact about \.{CWEB} for those of youwho are reading this file as an example of \.{CWEB} programming.The file \.{stdio.h} includes a typedef forthe identifier |FILE|, which is not, strictly speaking, part of \CEE/.It turns out \.{CWEAVE} knows that |FILE| is a reserved word (after all,|FILE| is almost as common as |int|); indeed, \.{CWEAVE} knows allthe types of the ISO standard \CEE/ library. Butif you're using other types like {\bf caddr\_t},@:caddr_t}{\bf caddr_t@>which is defined in \.{/usr/include/sys/types.h}, you should let\.{WEAVE} know that this is a type, either by including the \.{.h} fileat \.{WEB} time (saying \.{@@i /usr/include/sys/types.h}), or byusing \.{WEB}'s format command (saying \.{@@f caddr\_t int}).  Either ofthese will make {\bf caddr\_t} be treated in the same way as |int|. @<Func...@>=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 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.@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 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, limit-buffer))@<Def...@>=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| inpreparation for the next matching operation. Since blank lines in the changefile 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 1; hence error messages will be reported correctly.@<Func...@>=voidprime_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, 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(1) {  change_line++;  if (!input_ln(change_file)) return;  if (limit<buffer+2) continue;  if (buffer[0]!='@@') continue;  if (isupper(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 shouldgo into effect; it is called only when |changing| is 0.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|.This procedure is called only when |buffer<limit|, i.e., when thecurrent line is nonempty.@<Func...@>=voidcheck_change() /* switches to |change_file| if the buffers match */{  int n=0; /* the number of discrepancies found */  if (lines_dont_match) return;  while (1) {    changing=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 (isupper(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;    fprintf(stderr,"\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 gets the program ready to read theuser's \.{WEB} input.@<Func...@>=voidreset_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 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.@<Fun...@>=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);}void put_line(){  char *ptr=buffer;  while (ptr<limit) putc(*ptr++,out_file);  putc('\n',out_file);}@ 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.

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -