📄 wmerge.w
字号:
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, 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 *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; 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; 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 */ 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 */ *limit=' '; if (buffer[0]=='@@') { if (isupper(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; } } }}@ At the end of the program, we will tell the user if the change filehad a line that didn't match any relevant line in |web_file|.@<Funct...@>=voidcheck_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@> }}@* Reporting errors to the user.A global variable called |history| will contain one of four valuesat the end of every run: |spotless| means that no unusual messages wereprinted; |harmless_message| means that a message of possible interestwas printed but no serious errors were detected; |error_message| means thatat least one error was found; |fatal_message| means that the programterminated abnormally. The value of |history| does not influence thebehavior of the program; it is simply computed for the convenienceof 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 tothe user, by printing the error message at the beginning of a new line andthen giving an indication of where the error was spotted in the source file.Note that no period follows the error message, since the error routinewill automatically supply a period. A newline is automatically suppliedif the string begins with |"!"|.The actual error indications are provided by a procedure called |error|.@<Predecl...@>=void err_print();@@<Functions...@>=voiderr_print(s) /* prints `\..' and location of error message */char *s;{ char *k,*l; /* pointers into |buffer| */ fprintf(stderr,*s=='!'? "\n%s" : "%s",s); if(web_file_open) @<Print error location based on input buffer@>@; else putc('\n',stderr); 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 firstunlooked-at position in |buffer|, the current line number, the currentfile, and whether the current line is from |change_file| or |cur_file|.This routine should be modified on systems whose standard text editorhas special line-numbering conventions.@^system dependencies@>@<Print error location based on input buffer@>={if (changing && include_depth==change_depth) fprintf(stderr,". (l. %d of change file)\n", change_line);else if (include_depth==0) fprintf(stderr,". (l. %d)\n", cur_line); else fprintf(stderr,". (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') putc(' ',stderr); else putc(*k,stderr); /* print the characters already read */ putc('\n',stderr); for (k=buffer; k<l; k++) putc(' ',stderr); /* space out the next line */}for (k=l; k<limit; k++) putc(*k,stderr); /* print the part not yet read */ putc('\n',stderr);}@ When no recovery from some error has been provided, we have to wrapup and quit as graciously as possible. This is done by calling thefunction |wrap_up| at the end of the code.@d fatal(s,t) { fprintf(stderr,s); err_print(t); history=fatal_message; exit(wrap_up());}@ Some implementations may wish to pass the |history| value to theoperating system so that it can be used to govern whether or not otherprograms are started. Here, for instance, we pass the operating systema status of 0 if and only if only harmless messages were printed.@^system dependencies@>@<Func...@>=wrap_up() { @<Print the job |history|@>; if (history > harmless_message) return(1); else return(0);}@ @<Print the job |history|@>=switch (history) {case spotless: if (show_happiness) fprintf(stderr,"(No errors were found.)\n"); break;case harmless_message: fprintf(stderr,"(Did you see the warning message above?)\n"); break;case error_message: fprintf(stderr,"(Pardon me, but I think I spotted something wrong.)\n"); break;case fatal_message: fprintf(stderr,"(That was a fatal error, my friend.)\n");} /* there are no other cases */@* Command line arguments.The user calls \.{wmerge} 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 restof the program. The various file name variables contain strings withthe names of those files. Most of the 128 flags are undefined but availablefor future extensions.@d show_banner flags['b'] /* should the banner line be printed? */@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 out_file_name[max_file_name_length]; /* name of |out_file| */boolean flags[128]; /* an option for each 7-bit code */@ The |flags| will be initially 1.@<Set the default options@>=show_banner=show_happiness=1;@ We now must look at the command line arguments and set the file namesaccordingly. At least one file name must be present: the \.{WEB}file. It may have an extension, or it may omit it to get |'.w'|added.If there is another file name present among the arguments, it is thechange 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();@@<Function...@>=voidscan_args(){ char *dot_pos; /* position of |'.'| in the argument */ 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=='+') @<Handle flag argument@>@; else { s=*argv;@+dot_pos=NULL; while (*s) { if (*s=='.') dot_pos=s++; else if (*s=='/') dot_pos=NULL,++s; else s++; } if (!found_web) @<Make |web_file_name|@>@; else if (!found_change) @<Make |change_file_name| from |fname|@>@; else if (!found_out) @<Override output file name@>@; else @<Print usage error message and quit@>; } } if (!found_web) @<Print usage error message and quit@>; if (!found_change) 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 thingsafter 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); *out_file_name='\0'; /* this will print to stdout */ found_web=1;}@ @<Make |change_file_name|...@>={ 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(out_file_name,"%s.out",*argv); else strcpy(out_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@>={ fatal("! Usage: wmerge webfile[.w] [changefile[.ch] [outfile[.out]]]\n","")@;}@ @<Complain about arg...@>= fatal("! Filename too long\n", *argv);@* Output. Here is the code that opens the output file:@^system dependencies@>@<Defin...@>=FILE *out_file; /* where output goes */@ @<Scan arguments and open output file@>=scan_args();if (out_file_name[0]=='\0') out_file=stdout;else if ((out_file=fopen(out_file_name,"w"))==NULL) fatal("! Cannot open output file ", out_file_name);@.Cannot open output file@>@ The |update_terminal| procedure is called when we wantto make sure that everything we have output to the terminal so far hasactually left the computer's internal buffers and been sent.@^system dependencies@>@d update_terminal fflush(stderr) /* empty the terminal output buffer */@* Index.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -