?? wmerge.w
字號:
% Modified 16 Jan 2002 to agree with COMMON version 3.64\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 4096@<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+(limit-buffer); 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]=='@@') { char xyz_code=isupper(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; 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...@>=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;}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.If the environment variable \.{CWEBINPUTS} is set, or if the compiler flag
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -