📄 common.w
字号:
}}@ @<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 theuser's \.{CWEB} input, is used at the beginning of phase one of \.{CTANGLE},phases one and two of \.{CWEAVE}.@cvoidreset_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.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.@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? */@ @cint 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 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;}@<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 filehad a line that didn't match any relevant line in |web_file|.@cvoidcheck_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 andother strings in a large array of |char|s, called |byte_mem|.Information about the names is kept in the array |name_dir|, whoseelements are structures of type |name_info|, containing a pointer intothe |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_pointerp| 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| iskept in |byte_ptr| and |name_ptr|, respectively. Thus weusually have |name_ptr->byte_start==byte_ptr|, and certainlywe 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| andthen 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 itselfconsists of |hash_size| entries of type |name_pointer|, and isupdated by the |id_lookup| procedure, which finds a given identifierand returns the appropriate |name_pointer|. The matching is done by thefunction |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:@cname_pointerid_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 ofcharacter 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 initializedin 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -