diff options
Diffstat (limited to 'web/spiderweb/tools/tie/tie.web,v')
-rw-r--r-- | web/spiderweb/tools/tie/tie.web,v | 576 |
1 files changed, 576 insertions, 0 deletions
diff --git a/web/spiderweb/tools/tie/tie.web,v b/web/spiderweb/tools/tie/tie.web,v new file mode 100644 index 0000000000..8794b356a1 --- /dev/null +++ b/web/spiderweb/tools/tie/tie.web,v @@ -0,0 +1,576 @@ +head 1.1; +branch ; +access ; +symbols ; +locks nr:1.1; strict; +comment @@; + + +1.1 +date 92.07.06.18.51.25; author nr; state Exp; +branches ; +next ; + + +desc +@tie program +@ + + + +1.1 +log +@Initial revision +@ +text +@% 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 <stdio.h> +@@<Input and output types@@>@@; +@@<Types in the outer block@@>@@; +@@<Globals in the outer block@@>@@; +@@<Error handling procedures@@>@@; +@@# +main (argc, argv) + int argc; + char **argv; +{ + int i,j; + + @@<Set initial values@@>@@; + print_ln(banner); /* print a ``banner line'' */ + @@<Read the arguments, setting the master file, the change files, + and any options@@>; + @@<Initialize the input system@@>; + @@<Process the input@@>; + @@<Check that all changes have been read@@>; + /* here files should be closed if the operating system requires it */ + @@<Print the job |history|@@>; + jump_out(); +} + +@@ The guts of the program are simple: we drag each line through the mud: +@@<Process the input@@>= +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"); +@@<Write carriage return for end of counts@@>; + + +@@ 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. + +@@<Check that all changes have been read@@>= +for (i=1; i<next_available_file; i++) /* all change files */ + if (input_organization[i].mode != ignore) + err_print("! Change file entry did not match",i); +@@.Change file entry ...@@> + +@@ 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. + +@@<Read the arguments...@@>= +while (--argc>0) { + if (**++argv=='-') + switch (*++*argv) { + case 'm': + prod_chf=false; + break; + case 'c': + prod_chf=true; + 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. +@@<Globals...@@>= +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<lmt; k++) + if (input_organization[i].buffer[k] != input_organization[j].buffer[k]) + return true; + return false; +} +@@ @@<Globals...@@>=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'))); +} + +@@ @@<Globals...@@>=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'))); +} + +@@ @@<Globals...@@>=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); +} + +@@ @@<Globals...@@>=boolean file_open(); + +@@ This procedure opens all the input files. + +@@c +@@<Procedures for |open_input|@@>@@;@@% + open_input() /* prepare to read |input_files| */ +{ +int i; + @@<Get the master file started@@>; + actual_input=0; +no_ch=0; +while (++no_ch<next_available_file) { + if (!file_open(&(input_files[no_ch]),input_organization[no_ch].name_of_file)) { + fatal_error("! Change file can\'t be opened"); + } +@@.Change file can't be opened@@> + else { + 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. +@@<Get the master file started@@>= + 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 { + 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. + +@@<Procedures for |open_input|@@>= + init_change_file(i,b) + int i; + boolean b; +{ + @@<Skip over comment lines; |return| if end of file@@>; + @@<Skip to the next nonblank line; |return| if end of file@@>; +} + + +@@ 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). + +@@<Skip over comment lines...@@>= +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}. + +@@<Skip to the next nonblank line...@@>= +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<input_organization[j].limit; i++) + fputc(xchr[input_organization[j].buffer[i]],out_file); + fputc('\n',out_file); +} + +@@ Writing strings to the output +@@c +write_string(fp, s) + text_file fp; + char *s; +{ + while (*s != '\0') { + putc(*s,fp);s++;} +} + + + + +@@ To start with our inputs we set the indication that all +change files are in state |search|. That means that we must +skip to the next (in this case the first) change entry. + +@@<Initialize the input system@@>= +common_init(); +for (i=0; i<next_available_file; i++) { + input_organization[i].mode=search; + input_organization[i].lineno=0; + input_organization[i].type_of_file=chf; + input_organization[i].limit=0; + for (j=0 ; j<= buf_size; j++) input_organization[i].buffer[j]=0; + } +actual_input=0; +out_mode=normal; + open_input(); + + +@@*The major line-eating routines. +@@ To process the input file the procedure |process_line| +reads a line of the actual input file and updates the +|input_organization| for all files with index |i| greater +|actual_input|. + +@@c +process_line() +{ + int i; + @@<Count lines processed, and shout at each hundred@@>; + @@<Set |actual_input| and |test_input|, and set their modes@@>; + @@<Write one (decorated) line of output, and set output mode@@>; + @@<Read lines from |actual_input| + (and |test_input| if necessary, checking for preamble end)@@>; +} + + +@@ If |test_input| is |none|, no change file is tested. + +@@d none = (next_available_file) + +@@<Set |actual_input| and |test_input|, and set their modes@@>= +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<no_ch)) { + i++; + switch (input_organization[i].mode) { +case search: if (!lines_dont_match(actual_input,i)) { + input_organization[i].mode=test; + test_input=i; + } + break; +case test: + if (lines_dont_match(actual_input, i)) + { + /* error, modules do not match */ + input_organization[i].mode=search; + err_print("! Sections do not match",actual_input); +@@.Sections do not match@@> + 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; +} +} + + +@@ @@<Write one (decorated) line of output, and set output mode@@>= +if (prod_chf) { + loop { + @@<Test for normal@@>; + @@<Test for pre@@>; + @@<Test for post@@>; +} +} else +if (test_input==none) put_line(actual_input); + + +@@ Check whether we have to start a change file entry. + +@@<Test for normal@@>= +{ + 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. + +@@<Test for pre@@>= +{ + 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. + +@@<Test for post@@>= +{ + 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; + } + } +} + + +@@ @@<Read lines from |actual_input| (and |test_input| if necessary, checking for preamble end)@@>= +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... +@@<Count lines proc...@@>= +if (++lines_processed % 100 == 0) { + if (lines_processed % 500 == 0) + fprintf(stderr,"%d",lines_processed); + else + fprintf(stderr,"."); + } + +@@ @@<Globals in...@@>=long int lines_processed=0; +@@ @@<Write carriage return...@@>=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@@> + +@@<System-dependent parts of char...@@>= +xchr['\t']='\t'; + + +@@* Index. + +\noindent Here is the cross-reference table for the \.{TIE} +processor. +@ |