summaryrefslogtreecommitdiff
path: root/web/spiderweb/tools/tie/tie.web,v
diff options
context:
space:
mode:
Diffstat (limited to 'web/spiderweb/tools/tie/tie.web,v')
-rw-r--r--web/spiderweb/tools/tie/tie.web,v576
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.
+@