beta-0.89.2
[luatex.git] / source / texk / web2c / tiedir / tie.w
blob227328b729ee3132736602c166b94a6064f48d31
1 % This is TIE.W as of 17 Dec 92
2 %---------------------------------------------------------
3 % Copyright (c) 1989,1992 by THD/ITI.
4 % All rights reserved.
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
24 % cweb by Levy&Knuth.
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}
33 \font\mc=cmr9
34 \def\PASCAL{Pascal}
35 \def\Cl{{\rm C}}
36 \def\ASCII{{\mc ASCII}}
38 \def\title{TIE}
39 \def\topofcontents{\null\vfill
40 \centerline{\titlefont The {\ttitlefont TIE} processor}
41 \vskip 15pt
42 \centerline{(CWEB Version 2.4)}
43 \vfill}
44 \def\botofcontents{
45 \null\vfill
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.
51 \hfill\break
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.
55 \hfill\break
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.
66 @* Introduction.
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
95 installations.
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."
106 @d copyright
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@>@;
115 @<Global types@>@;
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 */
127 @f loop while
130 @ Furthermore we include the additional types |boolean| and |string|.
131 @d false 0
132 @d true 1
133 @<Global types@>=
134 typedef int boolean;
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.)
155 @d spotless 0
156 @d troublesome 1
157 @d fatal 2
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}
183 \count255='40
184 \vbox{
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}}
193 \vskip 4pt
194 \hrule
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@>
224 @<Global types@>=
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
244 necessary.
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|*/
250 @<Global types@>=
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|
258 functions.
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
294 character set.
296 @<Set initial values@>=
297 xchr[@' ']=' ';
298 xchr[@'!']='!';
299 xchr[@'\"']='\"';
300 xchr[@'#']='#';@/
301 xchr[@'$']='$';
302 xchr[@'%']='%';
303 xchr[@'&']='&';
304 xchr[@'\'']='\'';@/
305 xchr[@'(']='(';
306 xchr[@')']=')';
307 xchr[@'*']='*';
308 xchr[@'+']='+';@/
309 xchr[@',']=',';
310 xchr[@'-']='-';
311 xchr[@'.']='.';
312 xchr[@'/']='/';@/
313 xchr[@'0']='0';
314 xchr[@'1']='1';
315 xchr[@'2']='2';
316 xchr[@'3']='3';@/
317 xchr[@'4']='4';
318 xchr[@'5']='5';
319 xchr[@'6']='6';
320 xchr[@'7']='7';@/
321 xchr[@'8']='8';
322 xchr[@'9']='9';
323 xchr[@':']=':';
324 xchr[@';']=';';@/
325 xchr[@'<']='<';
326 xchr[@'=']='=';
327 xchr[@'>']='>';
328 xchr[@'?']='?';@/
329 xchr[@'@@']='@@';
330 xchr[@'A']='A';
331 xchr[@'B']='B';
332 xchr[@'C']='C';@/
333 xchr[@'D']='D';
334 xchr[@'E']='E';
335 xchr[@'F']='F';
336 xchr[@'G']='G';@/
337 xchr[@'H']='H';
338 xchr[@'I']='I';
339 xchr[@'J']='J';
340 xchr[@'K']='K';@/
341 xchr[@'L']='L';
342 xchr[@'M']='M';
343 xchr[@'N']='N';
344 xchr[@'O']='O';@/
345 xchr[@'P']='P';
346 xchr[@'Q']='Q';
347 xchr[@'R']='R';
348 xchr[@'S']='S';@/
349 xchr[@'T']='T';
350 xchr[@'U']='U';
351 xchr[@'V']='V';
352 xchr[@'W']='W';@/
353 xchr[@'X']='X';
354 xchr[@'Y']='Y';
355 xchr[@'Z']='Z';
356 xchr[@'[']='[';@/
357 xchr[@'\\']='\\';
358 xchr[@']']=']';
359 xchr[@'^']='^';
360 xchr[@'_']='_';@/
361 xchr[@'`']='`';
362 xchr[@'a']='a';
363 xchr[@'b']='b';
364 xchr[@'c']='c';@/
365 xchr[@'d']='d';
366 xchr[@'e']='e';
367 xchr[@'f']='f';
368 xchr[@'g']='g';@/
369 xchr[@'h']='h';
370 xchr[@'i']='i';
371 xchr[@'j']='j';
372 xchr[@'k']='k';@/
373 xchr[@'l']='l';
374 xchr[@'m']='m';
375 xchr[@'n']='n';
376 xchr[@'o']='o';@/
377 xchr[@'p']='p';
378 xchr[@'q']='q';
379 xchr[@'r']='r';
380 xchr[@'s']='s';@/
381 xchr[@'t']='t';
382 xchr[@'u']='u';
383 xchr[@'v']='v';
384 xchr[@'w']='w';@/
385 xchr[@'x']='x';
386 xchr[@'y']='y';
387 xchr[@'z']='z';
388 xchr[@'{']='{';@/
389 xchr[@'|']='|';
390 xchr[@'}']='}';
391 xchr[@'~']='~';@/
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
397 meaning.
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
406 index variable, |i|.
408 @<Local variables for initialisation@>=
409 int i;
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
416 changes here.
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.
429 @<Set init...@>=
430 for (i=1;i<@' ';xchr[i++]=' ');
431 xchr[tab_mark]='\t';
432 xchr[form_feed]='\f';
433 xchr[nl_mark]='\n';
436 @ The following system-independent code makes the |xord|
437 array contain a suitable inverse to the information in
438 |xchr|.
440 @<Set init...@>=
441 for ( i=first_text_char ; i<=last_text_char ; xord[i++]=@' ' ) do_nothing;
442 for ( i=1 ; i<=@'~' ; i++ ) xord[xchr[i]] = i;
448 @* Input and output.
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@>
460 @d term_out stdout
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@>=
479 #include <stdio.h>
482 @ And we need dynamic memory allocation.
483 This should cause no trouble in any \Cl\ program.
484 @^system dependencies@>
486 @<Global |#include|s@>=
487 #ifdef __STDC__
488 #include <stdlib.h>
489 #else
490 #include <malloc.h>
491 #endif
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
496 been sent.
497 @^system dependencies@>
499 @d update_terminal fflush(term_out) /* empty the terminal output buffer */
505 @* Data structures.
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
520 been found.
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.)
529 @<Global types@>=
530 #define search 0
531 #define test 1
532 #define reading 2
533 #define ignore 3
534 typedef int in_file_modes; /* should be |enum(search,test,reading,ignore)| */
535 #define unknown 0
536 #define master 1
537 #define chf 2
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
545 written.
547 @<Global types@>=
548 #define normal 0
549 #define pre 1
550 #define post 2
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.
557 @<Global types@>=
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
563 these input files.
564 %`line' is a normal identifier throughout this program
565 @f line dummy
566 @<Global types@>=
567 typedef struct _idsc{
568 string name_of_file;
569 ASCII_Code buffer[buf_size];
570 in_file_modes mode;
571 long line;
572 file_types type_of_file;
573 buffer_index limit;
574 text_file the_file;
575 } input_description;
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];
601 @* File I/O.
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@>=
617 void get_line(i)
618 file_index i;
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;
635 return;
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
659 certain times@>@;
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
677 of lines is shown.
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);
683 else print_c('.');
684 update_terminal;
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
693 is translated.
694 @^system dependencies@>
696 @<Check |c| for |EOF|...@>=
697 if (c==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;
702 return;
703 } else { /* add end of line mark */
704 c=nl_mark;
705 break;
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@>=
713 if (c!=nl_mark) {
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 */
746 int i;
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.
760 @d fatal_error(m) {
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
799 global variables.
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);
816 term_new_line;
817 input_organization[0]->type_of_file=master;
818 get_line(0);
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@>=
826 {file_index i;
827 i=1;
828 while (i<no_ch) {
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);
835 term_new_line;
836 init_change_file(i,true);
837 incr(i);
845 @*Input/output organization.
847 \noindent Here's a simple function that checks if two lines
848 are different.
850 @<Internal functions@>=
851 boolean lines_dont_match(i,j)
852 file_index i,j;
854 buffer_index k,lmt;
855 if (input_organization[i]->limit != input_organization[j]->limit)
856 return(true);
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])
860 return(true);
861 return(false);
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
869 our skip.
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;
887 get_line(i);
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*/
894 if (c==@'x') break;
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...@>=
906 get_line(i);
907 if (inp_desc->mode==ignore) {
908 err_print("! Change file ended after @@x")(i);
909 @.Change file ended...@>
910 return;
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@>=
919 void put_line(j)
920 file_index j;
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);
927 new_line(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)
936 file_index 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);
942 term_new_line;
943 return(true);
944 } else if (inp_desc->limit>=2) if (inp_desc->buffer[0]==@'@@' &&
945 (inp_desc->buffer[1]==@'Z' || inp_desc->buffer[1]==@'z'))
946 return(true);
947 return(false);
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)
956 file_index 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);
960 return(false);
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
968 |actual_input|.
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@>@;
975 @<Handle output@>@;
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.
985 @<Check the...@>=
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...@>=
1010 test_input=none;
1011 test_file=actual_input;
1012 while (test_input==none && test_file<no_ch-1){
1013 incr(test_file);
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;
1019 break;
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@>
1025 err_loc(test_file);
1026 init_change_file(test_file,false);
1027 } else test_input=test_file;
1028 break;
1029 case reading: do_nothing; /* this can't happen */
1030 break;
1031 case ignore: do_nothing; /* nothing to do */
1032 break;
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.
1043 @<Handle output@>=
1044 if (prod_chf==chf) {
1045 loop @+ {
1046 @<Test for normal, |break| when done@>@;
1047 @<Test for pre, |break| when done@>@;
1048 @<Test for post, |break| when done@>@;
1050 } else
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);
1061 new_line(out_file);
1062 out_mode=pre;
1063 } else break;
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
1070 change file.
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);
1076 new_line(out_file);
1077 out_mode=post;
1078 } else {
1079 if (input_organization[actual_input]->type_of_file==master)
1080 put_line(actual_input);
1081 break;
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);
1096 break;
1097 } else {
1098 fputc(map_xchr(@'@@'),out_file); fputc(map_xchr(@'z'),out_file);
1099 new_line(out_file);
1100 new_line(out_file);
1101 out_mode=normal;
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;
1117 test_input=none;
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
1126 was to be changed.
1128 @<Process the input@>=
1129 actual_input=0;
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);
1135 new_line(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@>=
1144 {file_index i;
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.
1157 @<Intern...@>=
1158 void usage()
1160 print("Usage: tie -[mc] outfile master changefile(s)");
1161 term_new_line;
1162 jump_out();
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@>=
1178 {int act_arg;
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
1192 output files.
1193 Currently we just have to set the processing flag properly.
1195 @<Set a flag@>=
1196 if (prod_chf!=unknown) usage();
1197 else
1198 switch (argv[act_arg][1]) {
1199 case 'c':
1200 case 'C': prod_chf=chf; break;
1201 case 'm':
1202 case 'M': prod_chf=master; break;
1203 default: usage();
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|
1209 must be filled.
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));
1217 if (inp_desc==NULL)
1218 fatal_error("! No memory for descriptor");
1219 @.No memory for descriptor@>
1220 inp_desc->mode=search;
1221 inp_desc->line=0;
1222 inp_desc->type_of_file=chf;
1223 inp_desc->limit=0;
1224 inp_desc->name_of_file=argv[act_arg];
1225 input_organization[no_ch]=inp_desc;
1227 incr(no_ch);
1231 @* The main program.
1233 \noindent Here is where \.{TIE} starts, and where it ends.
1235 @<The main function@>=
1236 main(argc,argv)
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 */
1243 actual_input=0;
1244 out_mode=normal;
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
1258 \TeX{} do the same.
1259 @^system dependencies@>
1261 @<Print the job |history|@>=
1262 {string msg;
1263 switch (history) {
1264 case spotless: msg="No errors were found"; break;
1265 case troublesome: msg="Pardon me, but I think I spotted something wrong.";
1266 break;
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
1287 module number.
1288 @^system dependencies@>
1294 @* Index.
1296 \noindent Here is the cross-reference table for the \.{TIE}
1297 processor.