% This is TIE.WEB %------------------------------------------------------------ % Version 0.1 ported from PASCAL (NR) 24 August 87 % Here is TeX material that gets inserted after \input webmac @i tie.preamble @* Introduction. \noindent Whenever a programmer wants to change a given \.{WEB} program because of system dependencies, he will create a new change file. In addition there may be a second change file to modify system independent modules of the program. But the \.{WEB} file cannot be tangled and weaved with more than one change file simultaneously. Therefore, we introduce the present program to merge a \.{WEB} file and several change files producing a new \.{WEB} file. Since the input files are tied together, the program is called \.{TIE}. Furthermore, the program can be used to merge several change files giving a new single change file. This method seems to be more important because it doesn't modify the original source file. The use of \.{TIE} can be expanded to other programming languages since this processor only knows about the structure of change files and does not interpret the master file at all. The program \.{TIE} has to read lines from several input files to bring them in some special ordering. For this purpose an algorithm is used which looks a little bit complicated. But the method used only needs one buffer line for each input file. Thus the storage requirement of \.{TIE} does not depend on the input data. The program uses only few features of the local \PASCAL\ compiler that may need to be changed in other installations. The changes needed may be similar to those used to change the \.{WEB} processors \.{TANGLE} and \.{WEAVE}. All places where changes may be needed are listed in the index under the entry ``system dependencies''. @!@^system dependencies@> This version of \.{TIE} has been ported from a \PASCAL version {$\copyright$}1983, 1984, 1986 by Technische Hochschule Darmstadt, Fachbereich Informatik, Institut f\"ur Theoretische Informatik. No attempt has been made to take advantage of the many nice features of C (for example to remove the restriction on the number of change files). There is no warranty, express or implied. The ``banner line'' defined here should be changed whenever \.{TIE} is modified. This program is put into the public domain. Nevertheless the copyright notice must not be replaced or modified. @d banner = "This is TIE, C Version 0.1 (ported to C)" @ Here is where \.{TIE} starts, and where it ends. If a command line interface is present, changes to initialize the access might be needed here. @^system dependencies@> @^command line processing@> @c #include @@; @@; @@; @@; @# main (argc, argv) int argc; char **argv; { int i,j; @@; if (verbose) print_ln(banner); /* print a ``banner line'' */ @; @; @; @; /* here files should be closed if the operating system requires it */ @ jump_out(); } @ The guts of the program are simple: we drag each line through the mud: @= input_has_ended=false; while (!input_has_ended || (actual_input!=0)) process_line(); if (out_mode == post) /* last line has been changed */ write_string(out_file,"@@z\n"); @; @ At the end of the program, we will tell the user if a change file had a line that didn't match any relevant line in the master file. @= for (i=1; i @ Here are some macros for common programming idioms. @d loop = while (true) @d do_nothing = /* empty statement */ @f loop while @ The following parameters should be sufficient for most applications of \.{TIE}. Note that |max_file_index| should not be enlarged without changing the processing of change file names. @^system dependencies@> @d buf_size = 100 /* maximum length of one input line*/ @d max_file_index = 9/* we dont think that anyone needs more than 9 change files*/ @d max_name_length = 54 /* adapt this to your local name space*/ @* Handling multiple change files. We read the master and change files from the argument vector. The master file is first, followed by (eventually any number) up to 9 change files. The only options are |"-m"| to produce a master file, and |"-c"| to produce a change file. The last one wins. @= while (--argc>0) { if (**++argv=='-') switch (*++*argv) { case 'm': prod_chf=false; break; case 'c': prod_chf=true; break; case 'v': verbose++; break; default: fprintf(stderr, "Bad option -%c ignored\n", **argv); break; } else { if (next_available_file > max_file_index) fatal_error("too many files"); input_organization[next_available_file++].name_of_file=*argv; } } @ |next_available_file| tells us what file numebr is available as a change file. @= int next_available_file=0; @i datadecl.web @*Routines that test input/output conditions. \noindent Here's a simple function that checks if two lines are different. @c boolean lines_dont_match(i,j) int i,j; { buffer_index k, lmt; if (input_organization[i].limit != input_organization[j].limit) return true; lmt=input_organization[i].limit; for (k=0; k=boolean lines_dont_match(); @ The function |e_of_ch_module| returns true if the input line from file |i| is equal to \.{@@z}. @c boolean e_of_ch_module(i) int i; { return ((input_organization[i].limit>=2) && (input_organization[i].buffer[0]=='@@') && ((input_organization[i].buffer[1]=='Z') || (input_organization[i].buffer[1]=='z'))); } @ @=boolean e_of_ch_module(); @ The function |e_of_ch_preamble| returns true if the input line from file |i| is equal to \.{@@y}. @c boolean e_of_ch_preamble(i) int i; { return ((input_organization[i].limit>=2) && (input_organization[i].buffer[0]=='@@') && ((input_organization[i].buffer[1]=='Y') || (input_organization[i].buffer[1]=='y'))); } @ @=boolean e_of_ch_module(); @*Routines that manipulate input and output. @ We continue with a function to open a text file. Success is indicated by a boolean result. We assume that empty files can be handled like non existent ones. @^system dependencies@> @c boolean file_open(f,name) text_file *f; name_type name; { return ((*f=fopen(name,"r"))!=NULL); } @ @=boolean file_open(); @ This procedure opens all the input files. @c @@;@% open_input() /* prepare to read |input_files| */ { int i; @; actual_input=0; no_ch=0; while (++no_ch else { if (verbose) { print("("); print_name_of_file(no_ch); print_ln(")"); } init_change_file(no_ch,true); } } if (--no_ch==0) fatal_error("! No change file found"); @.No change file found@> for (i=next_available_file; i<=max_file_index; i++) input_organization[i].mode=ignore; } @ The master file is actually started just like the change files. @= if (!file_open(&(input_files[0]),input_organization[0].name_of_file)) { fatal_error("! Master file can\'t be opened"); } @.Master file can't be opened@> else { if (verbose) { print("("); print_name_of_file(0); print_ln(")"); } input_organization[0].type_of_file = master; get_line(0); } @ The main output goes to |out_file|. @d out_file = stdout @ Procedure |init_change_file(i,b)| is used to ignore all lines of the input file with index |i| until the next change module is found. The boolean parameter |b| indicates whether we do not want to see "@@x" or "@@y" entries during our skip. @= init_change_file(i,b) int i; boolean b; { @; @; } @ While looking for a line that begins with \.{@@x} in the change file, we allow lines that begin with \.{@@}, as long as they don"t begin with \.{@@y} or \.{@@z} (which would probably indicate that the change file is fouled up). @= loop { get_line(i); if (input_organization[i].mode==ignore) return; if (input_organization[i].limit<2) continue; if (input_organization[i].buffer[0] != '@@') continue; if ((input_organization[i].buffer[1]>='X') && (input_organization[i].buffer[1]<='Z')) input_organization[i].buffer[1] += 'z'-'Z'; /* lowercasify */ if (input_organization[i].buffer[1]=='x') break; if ((input_organization[i].buffer[1]=='y') || (input_organization[i].buffer[1]=='z')) if (b) /* waiting for start of change */ err_print("! Where is the matching @@x?",i); @.Where is the match...@> } @ Here we are looking at lines following the \.{@@x}. @= do { get_line(i); if (input_organization[i].mode==ignore) { err_print("! Change file ended after @@x",i); @.Change file ended...@> return; } } while (input_organization[i].limit<=0); @ The |put_line| procedure is used to write a line from input buffer |j| to the output file. @c put_line(j) int j; { int i; /* index into the buffer */ for (i=0; i= common_init(); for (i=0; i; @; @; @; } @ If |test_input| is |none|, no change file is tested. @d none = (next_available_file) @= while (e_of_ch_module(actual_input)) { if (input_organization[actual_input].type_of_file==master) /* emergency exit, everything mixed up! */ fatal_error("! This can\'t happen"); input_organization[actual_input].mode=search; init_change_file(actual_input,true); while ((input_organization[actual_input].mode!=reading) && (actual_input>0)) actual_input--; } if (input_has_ended && (actual_input==0)) return; test_input=none; i=actual_input; while ((test_input==none) && (i err_loc(i); init_change_file(i,false); } else test_input=i; break; case reading:do_nothing; /* this can't happen */ break; case ignore: do_nothing; /* nothing to do */ break; } } @ @= if (prod_chf) { loop { @; @; @; } } else if (test_input==none) put_line(actual_input); @ Check whether we have to start a change file entry. @= { if (out_mode==normal) if (test_input!=none) { write_string(out_file,"@@x\n"); out_mode=pre; } else break; } @ Check whether we have to start the replacement text. @= { if (out_mode==pre) { if (test_input==none) { write_string(out_file,"@@y\n"); out_mode=post; } else { if (input_organization[actual_input].type_of_file==master) put_line(actual_input); break; } } } @ Check whether a change file entry is complete. @= { if (out_mode==post) { if (input_organization[actual_input].type_of_file==chf) { if (test_input==none) put_line(actual_input); break; } else { write_string(out_file,"@@z\n"); out_mode=normal; } } } @ @= get_line(actual_input); if (test_input!=none) { get_line(test_input); if (e_of_ch_preamble(test_input)) { get_line(test_input); /* update input file */ input_organization[test_input].mode=reading; actual_input=test_input; test_input=none; } } @ We shout at the user about lines processed... @= if (++lines_processed % 100 == 0 && verbose) { if (lines_processed % 500 == 0) fprintf(stderr,"%d",lines_processed); else fprintf(stderr,"."); } @ @=long int lines_processed=0; @ @=if (verbose) fprintf(stderr,"\n"); @i io.web @i history.web @i error.web @i character.web @* System-dependent changes. \noindent This section should be replaced, if necessary, by changes to the program that are necessary to make \.{TIE} work at a particular installation. It is usually best to design your change file so that all changes to previous modules preserve the module numbering; then everybody's version will be consistent with the printed program. More extensive changes, which introduce new modules, can be inserted here; then only the index itself will get a new module number. @^system dependencies@> @= xchr['\t']='\t'; @* Index. \noindent Here is the cross-reference table for the \.{TIE} processor.