1 % This is TIE.W as of
17 Dec
92
2 %---------------------------------------------------------
3 % Copyright
(c
) 1989,1992 by THD
/ITI.
6 % This program is distributed WITHOUT
ANY WARRANTY
, express or implied.
8 % Permission is granted to make and distribute verbatim copies of this
9 % program provided that the copyright notice and this permission notice
10 % are preserved on all copies.
12 % Permission is granted to copy and distribute modified versions of this
13 % program under the conditions for verbatim copying
, provided that the
14 % entire resulting derived work is distributed under the terms of a
15 % permission notice identical to this one.
18 % Version
1.7 was built alike TIE.WEB Version
1.6 (89-01-27)
19 % but used a command line interface only
20 % Version
2.0 was revised to include optional tab expansion
(89-02-01)
21 % Version
2.1 deleted WEB relicts.
(-js
) (89-10-25)
22 % Version
2.2 repaired replacing strategy
(89-11-27)
23 % Version
2.3 was slightly modified to be processed with the
25 % also repaired loop control for end of changes test
(92-09-24)
26 % Version
2.4 included
<stdlib.h
> instead of
<malloc.h
> when
27 % used with ANSI-C
(92-12-17)
30 % Here is TeX material that gets inserted after \input cwebmac
32 \def\hang
{\hangindent
3em\indent\ignorespaces
}
36 \def\ASCII
{{\mc ASCII
}}
39 \def\topofcontents
{\null\vfill
40 \centerline
{\titlefont The
{\ttitlefont TIE
} processor
}
42 \centerline
{(CWEB Version
2.4)}
46 \item
{$\copyright$
}1989, 1992
47 by Technische Hochschule Darmstadt
,\hfill\break
48 Fachbereich Informatik
, Institut f\
"ur Theoretische Informatik\hfill\break
49 All rights reserved.\hfill\break
50 This program is distributed WITHOUT ANY WARRANTY, express or implied.
52 Permission is granted to make and distribute verbatim copies of this
53 program provided that the copyright notice and this permission notice
54 are preserved on all copies.
56 Permission is granted to copy and distribute modified versions of this
57 program under the conditions for verbatim copying, provided that the
58 entire resulting derived work is distributed under the terms of a
59 permission notice identical to this one.
68 \noindent Whenever a programmer wants to change a given
69 \.{WEB} or \.{CWEB} program (referred to as a \.{WEB} program throughout
70 this program) because of system dependencies, she or he will
71 create a new change file. In addition there may be a second
72 change file to modify system independent modules of the
73 program. But the \.{WEB} file cannot be tangled and weaved
74 with more than one change file simultaneously. Therefore,
75 we introduce the present program to merge a \.{WEB} file and
76 several change files producing a new \.{WEB} file. Since
77 the input files are tied together, the program is called
78 \.{TIE}. Furthermore, the program can be used to merge
79 several change files giving a new single change file. This
80 method seems to be more important because it doesn't modify
81 the original source file. The use of \.{TIE} can be
82 expanded to other programming languages since this processor
83 only knows about the structure of change files and does not
84 interpret the master file at all.
86 The program \.{TIE} has to read lines from several input
87 files to bring them in some special ordering. For this
88 purpose an algorithm is used which looks a little bit
89 complicated. But the method used only needs one buffer line
90 for each input file. Thus the storage requirement of
91 \.{TIE} does not depend on the input data.
93 The program is written in \Cl\ and uses only few features of a
94 particular environment that may need to be changed in other
96 E.g.\ it will not use the |enum| type declarations.
97 The changes needed may refer to the access of the command line
98 if this can be not supported by any \Cl\ compiler.
100 The ``banner line'' defined here should be changed whenever
101 \.{TIE} is modified. This program is put into the public
102 domain. Nevertheless the copyright notice must not be
103 replaced or modified.
105 @d banner "This is TIE
, CWEB Version
2.4.
"
107 "Copyright
(c
) 1989,1992 by THD
/ITI. All rights reserved.
"
110 @ The main outline of the program is given in the next section.
111 This can be used more or less for any \Cl\ program.
113 @<Global |#include|s@>@;
114 @<Global constants@>@;
116 @<Global variables@>@;
117 @<Error handling functions@>@;
118 @<Internal functions@>@;
119 @<The main function@>@;
121 @ Here are some macros for common programming idioms.
123 @d incr(v) v+=1 /* increase a variable by unity */
124 @d decr(v) v-=1 /* decrease a variable by unity */
125 @d loop @+ while (1)@+ /* repeat over and over until a |break| happens */
126 @d do_nothing /* empty statement */
130 @ Furthermore we include the additional types |boolean| and |string|.
135 typedef char* string;
138 @ The following parameters should be sufficient for most
139 applications of \.{TIE}.
140 @^system dependencies@>
142 @<Global constants@>=
143 #define buf_size 512 /* maximum length of one input line */
144 #define max_file_index 9
145 /* we don't think that anyone needs more than 9 change files,
146 but \dots\ just change it */
149 @ We introduce a history variable that allows us to set a
150 return code if the operating system can use it.
151 First we introduce the coded values for the history.
152 This variable must be initialized.
153 (We do this even if the value given may be the default for
154 variables, just to document the need for the initial value.)
159 @<Global variables@>=
160 static int history=spotless;
164 @* The character set.
166 \noindent One of the main goals in the design of \.{TIE} has
167 been to make it readily portable between a wide variety of
168 computers. Yet \.{TIE} by its very nature must use a
169 greater variety of characters than most computer programs
170 deal with, and character encoding is one of the areas in
171 which existing machines differ most widely from each other.
173 To resolve this problem, all input to \.{TIE} is converted to an
174 internal seven-bit code that is essentially standard \ASCII{}, the
175 ``American Standard Code for Information Interchange.'' The conversion
176 is done immediately when each character is read in. Conversely,
177 characters are converted from \ASCII{} to the user's external
178 representation just before they are output. But the algorithm is
179 prepared for the usage of eight-bit data.
181 \noindent Here is a table of the standard visible \ASCII{} codes:
182 $$\def\:{\char\count255\global\advance\count255 by 1}
185 \hbox{\hbox to 40pt{\it\hfill0\/\hfill}%
186 \hbox to 40pt{\it\hfill1\/\hfill}%
187 \hbox to 40pt{\it\hfill2\/\hfill}%
188 \hbox to 40pt{\it\hfill3\/\hfill}%
189 \hbox to 40pt{\it\hfill4\/\hfill}%
190 \hbox to 40pt{\it\hfill5\/\hfill}%
191 \hbox to 40pt{\it\hfill6\/\hfill}%
192 \hbox to 40pt{\it\hfill7\/\hfill}}
195 \def\^{\vrule height 10.5pt depth 4.5pt}
196 \halign{\hbox to 0pt{\hskip -24pt\O{#0}\hfill}&\^
197 \hbox to 40pt{\tt\hfill#\hfill\^}&
198 &\hbox to 40pt{\tt\hfill#\hfill\^}\cr
199 04&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
200 05&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
201 06&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
202 07&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
203 10&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
204 11&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
205 12&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
206 13&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
207 14&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
208 15&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
209 16&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
210 17&\:&\:&\:&\:&\:&\:&\:\cr}
211 \hrule width 280pt}$$
212 (Actually, of course, code |040| is an invisible blank space.) Code |0136|
213 was once an upward arrow (\.{\char'13}), and code |0137| was
214 once a left arrow (\.^^X), in olden times when the first draft
215 of ASCII code was prepared; but \.{TIE} works with today's standard
216 ASCII in which those codes represent circumflex and underline as shown.
217 The maximum value used is also defined, it must be changed if an
218 extended \ASCII{} is used.
220 If the \Cl\ compiler is not able to process \&{unsigned char}'s, you
221 should define |ASCII_Code| as \&{short}.
222 @^system dependencies@>
225 #define max_ASCII (@'~'+1)
226 typedef unsigned char ASCII_Code;
227 /* eight-bit numbers, a subrange of the integers */
230 @ \Cl\ was first implemented on a machine that uses the
231 \ASCII{} representation for characters. But to make it readily
232 available also for other machines (big brother is watching?)\
233 we add a conversion that may be undone for most installations.
234 \.{TIE} assumes that it is being used
235 with a character set that contains at least the
236 characters of standard \ASCII{} as listed above.
238 In order to accommodate this difference, we shall use the
239 name |text_char| to stand for the data type of the
240 characters in the input and output files. We shall also
241 assume that |text_char| consists of the elements
242 |first_text_char| through |last_text_char|,
243 inclusive. The following definitions should be adjusted if
245 @^system dependencies@>
247 @d first_text_char 0 /* ordinal number of the smallest element of |text_char|*/
248 @d last_text_char 255 /* ordinal number of the largest element of |text_char|*/
251 typedef unsigned char text_char; /* the data type of characters in text files */
252 typedef FILE* text_file;
255 @ The \.{TIE} processor converts between \ASCII{} code and
256 the user's external character set by means of arrays |xord|
257 and |xchr| that are analogous to \PASCAL's |ord| and |chr|
260 The mapping may be disabled by changing the following macro
261 definitions to just a cast. If your \Cl\ compiler does not support
262 \&{unsigned char}'s, you should incorporate a binary and with |0xff|.
263 @^system dependencies@>
265 @d map_xchr(c) (text_char)(c)
266 /* change this to |xchr[c]| on non \ASCII{} machines */
267 @d map_xord(c) (ASCII_Code)(c)
268 /* change this to |xord[c]| on non \ASCII{} machines */
270 @<Global variables@>=
271 static ASCII_Code xord[last_text_char+1];
272 /* specifies conversion of input characters */
273 static text_char xchr[max_ASCII+1];
274 /* specifies conversion of output characters */
277 @ If we assume that every system using \.{WEB} is able to
278 read and write the visible characters of standard \ASCII{}
279 (although not necessarily using the \ASCII{} codes to
280 represent them), the following assignment statements
281 initialize most of the |xchr| array properly, without
282 needing any system-dependent changes. For example, the
283 statement |xchr[@'A']='A'| that appears in the present
284 \.{WEB} file might be encoded in, say, {\mc EBCDIC} code on
285 the external medium on which it resides, but \.{CTANGLE} will
286 convert from this external code to \ASCII{} and back again.
287 Therefore the assignment statement |xchr[65]='A'| will
288 appear in the corresponding \Cl\ file, and \Cl\ will
289 compile this statement so that |xchr[65]| receives the
290 character \.A in the external code. Note that it
291 would be quite incorrect to say |xchr[@'A']=@'A'|, because
292 |@'A'| is a constant of type |int| not \&{char}, and
293 because we have |@'A'==65| regardless of the external
296 @<Set initial values@>=
392 xchr[0]=' '; xchr[0x7F]=' '; /* these \ASCII{} codes are not used */
395 @ Some of the \ASCII{} codes below |0x20| have been given a
396 symbolic name in \.{TIE} because they are used with a special
399 @d tab_mark @'\t' /* \ASCII{} code used as tab-skip */
400 @d nl_mark @'\n' /* \ASCII{} code used as line end marker */
401 @d form_feed @'\f' /* \ASCII{} code used as page eject */
404 @ When we initialize the |xord| array and the remaining
405 parts of |xchr|, it will be convenient to make use of an
408 @<Local variables for initialisation@>=
412 @ Here now is the system-dependent part of the character
413 set. If \.{TIE} is being implemented on a garden-variety
414 \Cl\ for which only standard \ASCII{} codes will appear
415 in the input and output files, you don't need to make any
417 @^system dependencies@>
419 Changes to the present module will make \.{TIE} more
420 friendly on computers that have an extended character set,
421 so that one can type things like \.^^Z. If you have an
422 extended set of characters that are easily incorporated into
423 text files, you can assign codes arbitrarily here, giving an
424 |xchr| equivalent to whatever characters the users of
425 \.{TIE} are allowed to have in their input files, provided
426 that unsuitable characters do not correspond to special
427 codes like |tab_mark| that are listed above.
430 for (i=1;i<@' ';xchr[i++]=' ');
432 xchr[form_feed]='\f';
436 @ The following system-independent code makes the |xord|
437 array contain a suitable inverse to the information in
441 for ( i=first_text_char ; i<=last_text_char ; xord[i++]=@' ' ) do_nothing;
442 for ( i=1 ; i<=@'~' ; i++ ) xord[xchr[i]] = i;
450 \noindent Output for the user is done by writing on file |term_out|,
451 which is assumed to consist of characters of type \&{text\_char}. It
452 should be linked to |stdout| usually. Terminal input is not needed in
453 this version of \.{TIE}. |stdin| and |stdout| are predefined if we
454 include the \.{stdio.h} definitions. Although I/O redirection for
455 |stdout| is usually available you may lead output to another file if
456 you change the definition of |term_out|. Also we define some macros
457 for terminating an output line and writing strings to the user.
459 @^system dependencies@>
461 @d print(a) fprintf(term_out,a) /* `|print|' means write on the terminal */
462 @d print2(a,b) fprintf(term_out,a,b) /* same with two arguments */
463 @d print3(a,b,c) fprintf(term_out,a,b,c) /* same with three arguments */
464 @d print_c(v) fputc(v,term_out); /* print a single character */
465 @d new_line(v) fputc('\n',v) /* start new line */
466 @d term_new_line new_line(term_out)
467 /* start new line of the terminal */
468 @d print_ln(v) {fprintf(term_out,v);term_new_line;}
469 /* `|print|' and then start new line */
470 @d print2_ln(a,b) {print2(a,b);term_new_line;} /* same with two arguments */
471 @d print3_ln(a,b,c) {print3(a,b,c);term_new_line;}
472 /* same with three arguments */
473 @d print_nl(v) {term_new_line; print(v);}
474 /* print information starting on a new line */
475 @d print2_nl(a,b) {term_new_line; print2(a,b);}
476 /* same for two arguments */
478 @<Global |#include|s@>=
482 @ And we need dynamic memory allocation.
483 This should cause no trouble in any \Cl\ program.
484 @^system dependencies@>
486 @<Global |#include|s@>=
493 @ The |update_terminal| function is called when we want to
494 make sure that everything we have output to the terminal so
495 far has actually left the computer's internal buffers and
497 @^system dependencies@>
499 @d update_terminal fflush(term_out) /* empty the terminal output buffer */
507 \noindent The multiple input files (master file and change
508 files) are treated the same way. To organize the
509 simultaneous usage of several input files, we introduce the
510 data type \&{in\_file\_modes}.
512 The mode |search| indicates that \.{TIE} searches for a
513 match of the input line with any line of an input file in
514 |reading| mode. |test| is used whenever a match is found
515 and it has to be tested if the next input lines do match
516 also. |reading| describes that the lines can be read without
517 any check for matching other lines. |ignore| denotes
518 that the file cannot be used. This may happen because an
519 error has been detected or because the end of the file has
522 \leavevmode |file_types| is used to describe whether a file
523 is a master file or a change file. The value |unknown| is added
524 to this type to set an initial mode for the output file.
525 This enables us to check whether any option was used to select
526 the kind of output. (this would even be necessary if we
527 would assume a default action for missing options.)
534 typedef int in_file_modes; /* should be |enum(search,test,reading,ignore)| */
538 typedef int file_types; /* should be |enum(unknown,master,chf)| */
541 @ A variable of type |out_md_type| will tell us in what state the output
542 change file is during processing. |normal| will be the state, when we
543 did not yet start a change, |pre| will be set when we write the lines
544 to be changes and |post| will indicate that the replacement lines are
551 typedef int out_md_type; /* should be |enum(normal,pre,post)| */
554 @ Two more types will indicate variables used as an index into either
555 the file buffer or the file table.
558 typedef int buffer_index; /* |-1..buf_size| */
559 typedef int file_index; /* |-1..max_file_index+1| */
562 @ The following data structure joins all informations needed to use
564 %`line' is a normal identifier throughout this program
567 typedef struct _idsc{
569 ASCII_Code buffer[buf_size];
572 file_types type_of_file;
578 @ These data types are used in the global variable section.
579 They refer to the files in action, the number of change files,
580 the mode of operation and the current output state.
582 @<Global variables@>=
583 static file_index actual_input,test_input,no_ch;
584 static file_types prod_chf=unknown;
585 static out_md_type out_mode;
588 @ All input files (including the master file) are recorded
589 in the following structure.
590 Mostly the components are accessed through a local pointer variable.
591 This corresponds to \PASCAL's \&{with}-statement
592 and results in a one-time-computation of the index expression.
594 @<Global variables@>=
595 static input_description *input_organization[max_file_index+1];
603 \noindent The basic function |get_line| can be used to get a line from
604 an input file. The line is stored in the |buffer| part of the
605 descriptor. The components |limit| and |line| are updated. If the
606 end of the file is reached |mode| is set to |ignore|. On some systems
607 it might be useful to replace tab characters by a proper number of
608 spaces since several editors used to create change files insert tab
609 characters into a source file not under control of the user. So it
610 might be a problem to create a matching change file.
611 @^tab character expansion@>
613 We define |get_line| to read a line from a file specified by
614 the corresponding file descriptor.
616 @<Internal functions@>=
619 {register input_description *inp_desc=input_organization[i];
620 if (inp_desc->mode==ignore) return;
621 if (feof(inp_desc->the_file))
622 @<Handle end of file and return@>@;
623 @<Get line into buffer@>@;
627 @ End of file is special if this file is the master file.
628 Then we set the global flag variable |input_has_ended|.
630 @<Handle end of file ...@>=
632 inp_desc->mode=ignore;
633 inp_desc->limit=-1; /* mark end-of-file */
634 if (inp_desc->type_of_file==master) input_has_ended=true;
639 @ This variable must be declared for global access.
641 @<Global variables@>=
642 static boolean input_has_ended=false;
645 @ Lines must fit into the buffer completely.
646 We read all characters sequentially until an end of line is found
647 (but do not forget to check for |EOF|!).
648 Too long input lines will be truncated.
649 This will result in a damaged output if they occur in the
650 replacement part of a change file, or in an incomplerte check if the
651 matching part is concerned.
652 Tab character expansion might be done here.
653 @^tab character expansion@>
655 @<Get line into buffer@>=
656 {int final_limit; /* used to delete trailing spaces */
657 int c; /* the actual character read */
658 @<Increment the line number and print a progess report at
660 inp_desc->limit=final_limit=0;
661 while (inp_desc->limit<buf_size) {
662 c=fgetc(inp_desc->the_file);
663 @<Check |c| for |EOF|, |return| if line was empty, otherwise
664 |break| to process last line@>@;
665 inp_desc->buffer[inp_desc->limit++]=c=map_xord(c);
666 if (c==nl_mark) break; /* end of line found */
667 if (c!=@' ' && c!=tab_mark)
668 final_limit=inp_desc->limit;
670 @<Test for truncated line, skip to end of line@>@;
671 inp_desc->limit=final_limit;
675 @ This section does what its name says. Every 100 lines
676 in the master file we print a dot, every 500 lines the number
679 @<Increment the line number and print ...@>=
680 incr(inp_desc->line);
681 if (inp_desc->type_of_file==master && inp_desc->line % 100==0) {
682 if (inp_desc->line % 500 == 0) print2("%ld
",inp_desc->line);
688 @ There may be incomplete lines of the editor used does
689 not make sure that the last character before end of file
690 is an end of line. In such a case we must process the
691 final line. Of the current line is empty, we just can \&{return}.
692 Note that this test must be done {\sl before} the character read
694 @^system dependencies@>
696 @<Check |c| for |EOF|...@>=
698 if (inp_desc->limit<=0) {
699 inp_desc->mode=ignore;
700 inp_desc->limit=-1; /* mark end-of-file */
701 if (inp_desc->type_of_file==master) input_has_ended=true;
703 } else { /* add end of line mark */
710 @ If the line is truncated we must skip the rest
711 (and still watch for |EOF|!).
712 @<Test for truncated line, skip to end of line@>=
714 err_print("! Input line too long
")(i);
715 @.Input line too long@>
716 while ( (c=fgetc(inp_desc->the_file)) != EOF && map_xord(c) != nl_mark )
717 do_nothing; /* skip to end */
724 @* Reporting errors to the user.
726 \noindent There may be errors if a line in a given change
727 file does not match a line in the master file or a
728 replacement in a previous change file. Such errors are
729 reported to the user by saying
731 \hbox{|err_print("! Error message
")(file_no)|;}
733 where |file_no| is the number of the file which is concerned by the
734 error. Please note that no trailing dot is supplied by the error
735 message because it is appended by |err_print|.
737 This function is implemented as a macro. It gives a message and an
738 indication of the offending file. The actions to determine the error
739 location are provided by a function called |err_loc|.
741 @d error_loc(m) err_loc(m); history=troublesome; @+ }
742 @d err_print(m) { @+ print_nl(m); error_loc
744 @<Error handling...@>=
745 void err_loc(i) /* prints location of error */
748 print3_ln(" (file
%s
, l.
%ld
).
",
749 input_organization[i]->name_of_file,
750 input_organization[i]->line);
754 @ Non recoverable errors are handled by calling |fatal_error| that
755 outputs a message and then calls `|jump_out|'. |err_print| will print
756 the error message followed by an indication of where the error was
757 spotted in the source files. |fatal_error| cannot state any files
758 because the problem is usually to access these.
761 print(m); print_c('.'); history=fatal;
762 term_new_line; jump_out();
766 @ |jump_out| just cuts across all active procedure levels and jumps
767 out of the program. It is used when no recovery from a particular
768 error has been provided. The return code from this program should be
769 regarded by the caller.
771 @d jump_out() exit(1)
777 @* Handling multiple change files.
779 \noindent In the standard version we take the name of the
780 files from the command line.
781 It is assumed that filenames can be used as given in the
782 command line without changes.
784 First there are some sections to open all files.
785 If a file is not accessible, the run will be aborted.
786 Otherwise the name of the open file will be displayed.
788 @<Prepare the output file@>=
790 out_file=fopen(out_name,"w
");
791 if (out_file==NULL) {
792 fatal_error("! Could not open
/create output file
");
793 @.Could not open/create output file@>
798 @ The name of the file and the file desciptor are stored in
801 @<Global variables@>=
802 static text_file out_file;
803 static string out_name;
806 @ For the master file we start just reading its first line
807 into the buffer, if we could open it.
809 @<Get the master file started@>=
810 { input_organization[0]->the_file=
811 fopen(input_organization[0]->name_of_file,"r
");
812 if (input_organization[0]->the_file==NULL)
813 fatal_error("! Could not open master file
");
814 @.Could not open master file@>
815 print2("(%s
)",input_organization[0]->name_of_file);
817 input_organization[0]->type_of_file=master;
821 @ For the change files we must skip the comment part and
822 see, whether we can find any change in it.
823 This is done by |init_change_file|.
825 @<Prepare the change files@>=
829 input_organization[i]->the_file=
830 fopen(input_organization[i]->name_of_file,"r
");
831 if (input_organization[i]->the_file==NULL)
832 fatal_error("!Could not open change file
");
833 @.Could not open change file@>
834 print2("(%s
)",input_organization[i]->name_of_file);
836 init_change_file(i,true);
845 @*Input/output organization.
847 \noindent Here's a simple function that checks if two lines
850 @<Internal functions@>=
851 boolean lines_dont_match(i,j)
855 if (input_organization[i]->limit != input_organization[j]->limit)
857 lmt=input_organization[i]->limit;
858 for ( k=0 ; k<lmt ; k++ )
859 if (input_organization[i]->buffer[k] != input_organization[j]->buffer[k])
865 @ Function |init_change_file(i,b)| is used to ignore all
866 lines of the input file with index |i| until the next change
867 module is found. The boolean parameter |b| indicates
868 whether we do not want to see \.{@@x} or \.{@@y} entries during
871 @<Internal functions@>=
872 void init_change_file(i,b)
873 file_index i; boolean b;
874 {register input_description *inp_desc=input_organization[i];
875 @<Skip over comment lines; |return| if end of file@>@;
876 @<Skip to the next nonblank line; |return| if end of file@>@;
880 @ While looking for a line that begins with \.{@@x} in the
881 change file, we allow lines that begin with \.{@@}, as long
882 as they don't begin with \.{@@y} or \.{@@z} (which would
883 probably indicate that the change file is fouled up).
885 @<Skip over comment lines...@>=
886 loop@+ {ASCII_Code c;
888 if (inp_desc->mode==ignore) return;
889 if (inp_desc->limit<2) continue;
890 if (inp_desc->buffer[0] != @'@@') continue;
891 c=inp_desc->buffer[1];
892 if (c>=@'X' && c<=@'Z')
893 c+=@'z'-@'Z'; /*lowercasify*/
895 if (c==@'y' || c==@'z')
896 if (b) /* scanning for start of change */
897 err_print("! Where is the matching @@x?
")(i);
898 @.Where is the match...@>
902 @ Here we are looking at lines following the \.{@@x}.
904 @<Skip to the next nonblank line...@>=
907 if (inp_desc->mode==ignore) {
908 err_print("! Change file ended after @@x
")(i);
909 @.Change file ended...@>
912 } while (inp_desc->limit<=0);
915 @ The |put_line| function is used to write a line from
916 input buffer |j| to the output file.
918 @<Internal functions@>=
921 {buffer_index i; /* index into the buffer */
922 buffer_index lmt; /* line length */
923 ASCII_Code *p; /* output pointer */
924 lmt=input_organization[j]->limit;
925 p=input_organization[j]->buffer;
926 for (i=0;i<lmt;i++) fputc(map_xchr(*p++),out_file);
931 @ The function |e_of_ch_module| returns true if the input
932 line from file |i| starts with \.{@@z}.
934 @<Internal functions@>=
935 boolean e_of_ch_module(i)
937 {register input_description *inp_desc=input_organization[i];
938 if (inp_desc->limit<0) {
939 print_nl("! At the end of change file missing @@z
");
940 @.At the end of change file...@>
941 print2("%s
",input_organization[i]->name_of_file);
944 } else if (inp_desc->limit>=2) if (inp_desc->buffer[0]==@'@@' &&
945 (inp_desc->buffer[1]==@'Z' || inp_desc->buffer[1]==@'z'))
951 @ The function |e_of_ch_preamble| returns |true| if the input
952 line from file |i| starts with \.{@@y}.
954 @<Internal functions@>=
955 boolean e_of_ch_preamble(i)
957 {register input_description *inp_desc=input_organization[i];
958 if (inp_desc->limit>=2 && inp_desc->buffer[0]==@'@@')
959 if (inp_desc->buffer[1]==@'Y'||inp_desc->buffer[1]==@'y') return(true);
965 @ To process the input file the next section
966 reads a line of the actual input file and updates the
967 |input_organization| for all files with index |test_file| greater
970 @<Process a line, |break| when end of source reached@>=
971 {file_index test_file;
972 @<Check the current files for any ends of changes@>@;
973 if (input_has_ended && actual_input==0) break; /* all done */
974 @<Scan all other files for changes to be done@>@;
976 @<Step to next line@>@;
980 @ Any of the current change files may have reached the end of change.
981 In such a case intermediate lines must be skipped and the next start
982 of change is to be found. This may make a change file inactive if
983 it reaches end of file.
986 {register input_description *inp_desc;
987 while (actual_input>0 && e_of_ch_module(actual_input)) {
988 inp_desc=input_organization[actual_input];
989 if (inp_desc->type_of_file==master) {
990 /* emergency exit, everything mixed up!*/
991 fatal_error("! This can't happen
: change file is master file
");
992 @.This can't happen...@>
994 inp_desc->mode=search;
995 init_change_file(actual_input,true);
996 while ((input_organization[actual_input]->mode!=reading
997 && actual_input>0)) decr(actual_input);
1002 @ Now we will set |test_input| to the file that has another match
1003 for the current line. This depends on the state of the other
1004 change files. If no other file matches, |actual_input| refers to
1005 a line to write and |test_input| ist set to |none|.
1007 @d none (max_file_index+1)
1009 @<Scan all other files...@>=
1011 test_file=actual_input;
1012 while (test_input==none && test_file<no_ch-1){
1014 switch (input_organization[test_file]->mode) {
1015 case search: if (lines_dont_match(actual_input,test_file)==false) {
1016 input_organization[test_file]->mode=test;
1017 test_input=test_file;
1020 case test: if (lines_dont_match(actual_input, test_file)==true) {
1021 /* error, sections do not match */
1022 input_organization[test_file]->mode=search;
1023 err_print("! Sections do not match
")(actual_input);
1024 @.Sections do not match@>
1026 init_change_file(test_file,false);
1027 } else test_input=test_file;
1029 case reading: do_nothing; /* this can't happen */
1031 case ignore: do_nothing; /* nothing to do */
1037 @ For the output we must distinguish whether we create a new change
1038 file or a new master file. The change file creation needs some closer
1039 inspection because we may be before a change, in the pattern part or
1040 in the replacement part. For a master file we have to write the line
1041 from the current actual input.
1044 if (prod_chf==chf) {
1046 @<Test for normal, |break| when done@>@;
1047 @<Test for pre, |break| when done@>@;
1048 @<Test for post, |break| when done@>@;
1051 if (test_input==none) put_line(actual_input);
1054 @ Check whether we have to start a change file entry.
1055 Without a match nothing must be done.
1057 @<Test for normal...@>=
1058 if (out_mode==normal) {
1059 if (test_input!=none) {
1060 fputc(map_xchr(@'@@'),out_file); fputc(map_xchr(@'x'),out_file);
1067 @ Check whether we have to start the replacement text.
1068 This is the case when we have no more matching line.
1069 Otherwise the master file source line must be copied to the
1071 @<Test for pre...@>=
1073 if (out_mode==pre) {
1074 if (test_input==none) {
1075 fputc(map_xchr(@'@@'),out_file); fputc(map_xchr(@'y'),out_file);
1079 if (input_organization[actual_input]->type_of_file==master)
1080 put_line(actual_input);
1086 @ Check whether an entry from a change file is complete.
1087 If the current change was a change for a change file in effect,
1088 then this change file line must be written.
1089 If the actual input has been reset to the master file we
1090 can finish this change.
1092 @<Test for post...@>=
1093 if (out_mode==post) {
1094 if (input_organization[actual_input]->type_of_file==chf) {
1095 if (test_input==none) put_line(actual_input);
1098 fputc(map_xchr(@'@@'),out_file); fputc(map_xchr(@'z'),out_file);
1106 @ If we had a change, we must proceed in the actual file
1107 to be changed and in the change file in effect.
1109 @<Step to next line@>=
1110 get_line(actual_input);
1111 if (test_input!=none) {
1112 get_line(test_input);
1113 if (e_of_ch_preamble(test_input)==true) {
1114 get_line(test_input); /* update current changing file */
1115 input_organization[test_input]->mode=reading;
1116 actual_input=test_input;
1122 @ To create the new output file we have to scan the whole
1123 master file and all changes in effect when it ends.
1124 At the very end it is wise to check for all changes
1125 to have completed--in case the last line of the master file
1128 @<Process the input@>=
1130 input_has_ended=false;
1131 while (input_has_ended==false||actual_input!=0)
1132 @<Process a line...@>@;
1133 if (out_mode==post) { /* last line has been changed */
1134 fputc(map_xchr(@'@@'),out_file); fputc(map_xchr(@'z'),out_file);
1139 @ At the end of the program, we will tell the user if the
1140 change file had a line that didn't match any relevant line
1141 in the master file or any of the change files.
1143 @<Check that all changes have been read@>=
1145 for (i=1;i<no_ch;i++) {/* all change files */
1146 if (input_organization[i]->mode!=ignore)
1147 err_print("! Change file entry did not match
")(i);
1148 @.Change file entry ...@>
1153 @ We want to tell the user about our command line options. This is
1154 done by the |usage()| function. It contains merely the necessary
1155 print statement and exits afterwards.
1160 print("Usage
: tie
-[mc
] outfile master changefile
(s
)");
1166 @ We must scan through the list of parameters, given in |argv|. The
1167 number is in |argc|. We must pay attention to the flag parameter. We
1168 need at least 5~parameters and can handle up to |max_file_index|
1169 change files. The names fo the file parameters will be inserted into
1170 the structure of |input_organization|. The first file is special. It
1171 indicates the output file. When we allow flags at any position, we
1172 must find out which name is for what purpose. The master file is
1173 already part of the |input_organization| structure (index~0). As long
1174 as the number of files found (counted in |no_ch|) is |-1| we have not
1175 yet found the output file name.
1177 @<Scan the parameters@>=
1179 if ( argc < 5 || argc > max_file_index+4-1 ) usage();
1180 no_ch = -1; /* fill this part of |input_organization| */
1181 for ( act_arg=1 ; act_arg<argc ; act_arg++ ) {
1182 if (argv[act_arg][0]=='-') @<Set a flag@>@;
1183 else @<Get a file name@>@;
1185 if (no_ch<=0|| prod_chf==unknown) usage();
1189 @ The flag is about to determine the processing mode.
1190 We must make sure that this flag has not been set before.
1191 Further flags might be introduced to avoid/force overwriting of
1193 Currently we just have to set the processing flag properly.
1196 if (prod_chf!=unknown) usage();
1198 switch (argv[act_arg][1]) {
1200 case 'C': prod_chf=chf; break;
1202 case 'M': prod_chf=master; break;
1207 @ We have to distinguish whether this is the very first file name
1208 (known if |no_ch==(-1)|) or if the next element of |input_organization|
1211 @<Get a file name@>=
1212 { if (no_ch==(-1)) {
1213 out_name=argv[act_arg];
1214 } else { register input_description *inp_desc;
1215 inp_desc=(input_description *)
1216 malloc(sizeof(input_description));
1218 fatal_error("! No memory for descriptor
");
1219 @.No memory for descriptor@>
1220 inp_desc->mode=search;
1222 inp_desc->type_of_file=chf;
1224 inp_desc->name_of_file=argv[act_arg];
1225 input_organization[no_ch]=inp_desc;
1231 @* The main program.
1233 \noindent Here is where \.{TIE} starts, and where it ends.
1235 @<The main function@>=
1237 int argc; string *argv;
1238 {{@<Local variables for initialisation@>@;
1239 @<Set initial...@>@;
1241 print_ln(banner); /* print a ``banner line'' */
1242 print_ln(copyright); /* include the copyright notice */
1245 @<Scan the parameters@>@;
1246 @<Prepare the output file@>@;
1247 @<Get the master file started@>@;
1248 @<Prepare the change files@>@;
1249 @<Process the input@>@;
1250 @<Check that all changes have been read@>@;
1251 @<Print the job |history|@>@;
1254 @ We want to pass the |history| value to the operating system so that
1255 it can be used to govern whether or not other programs are started.
1256 Additionaly we report the history to the user, although this may not
1257 be ``{\mc UNIX}'' style---but we are in best companion: \.{WEB} and
1259 @^system dependencies@>
1261 @<Print the job |history|@>=
1264 case spotless: msg="No errors were found
"; break;
1265 case troublesome: msg="Pardon me
, but I think I spotted something wrong.
";
1267 case fatal: msg="That was a fatal error
, my friend
"; break;
1268 } /* there are no other cases */
1269 print2_nl("(%s.
)",msg); term_new_line;
1270 exit ( history == spotless ? 0 : 1 );
1277 @* System-dependent changes.
1279 \noindent This section should be replaced, if necessary, by
1280 changes to the program that are necessary to make \.{TIE}
1281 work at a particular installation. It is usually best to
1282 design your change file so that all changes to previous
1283 modules preserve the module numbering; then everybody's
1284 version will be consistent with the printed program. More
1285 extensive changes, which introduce new modules, can be
1286 inserted here; then only the index itself will get a new
1288 @^system dependencies@>
1296 \noindent Here is the cross-reference table for the \.{TIE}