OSDN Git Service

[lfsja-git] r12.0-92 対応。
[lfsbookja/lfsja-git.git] / ctie / ctie.w
1 % This is ctie.w
2 % Copyright (c) 2002,2003 by Julian Gilbey
3 % All rights reserved.
4 %
5 % This program is distributed WITHOUT ANY WARRANTY, express or implied.
6 %
7 % Permission is granted to make and distribute verbatim copies of this
8 % program provided that the copyright notice and this permission notice
9 % are preserved on all copies.
10 %
11 % Permission is granted to copy and distribute modified versions of this
12 % program under the conditions for verbatim copying, provided that the
13 % entire resulting derived work is distributed under the terms of a
14 % permission notice identical to this one.
15 %
16 % This program is based heavily on tie.w and common.w, part of the
17 % CWEB source.
18
19 \def\title{The CTIE processor}
20 \def\botofcontents{%
21 \vfill
22 $\copyright$ 2002,2003 Julian Gilbey
23
24 All rights reserved.
25
26 This program is distributed WITHOUT ANY WARRANTY, express or implied.
27
28 Permission is granted to make and distribute verbatim copies of this
29 program provided that the copyright notice and this permission notice
30 are preserved on all copies.
31
32 Permission is granted to copy and distribute modified versions of this
33 program under the conditions for verbatim copying, provided that the
34 entire resulting derived work is distributed under the terms of a
35 permission notice identical to this one.
36 }
37
38
39 @* Introduction.
40 Whenever a programmer wants to change a given \.{WEB} or \.{CWEB}
41 program (referred to as a \.{WEB} program throughout this program)
42 because of system dependencies, she or he will create a new change
43 file.  In addition there may be a second change file to modify system
44 independent modules of the program.  But the \.{WEB} file cannot be
45 tangled and weaved with more than one change file simultaneously.
46 The \.{TIE} program was designed to merge a \.{WEB} file
47 and several change files producing a new \.{WEB} file, and since the
48 input files are tied together, the program was called \.{TIE}.
49 Furthermore, the program could be used to merge several change files
50 giving a new single change file.  This method seems to be more
51 important because it doesn't modify the original source file.
52
53 However, the introduction of \.{CWEB} has meant that \.{TIE} is not
54 quite able to perform its task correctly any longer: \.{CWEB}
55 introduced the idea of include files, which are input into \.{CWEB}
56 files using the \.{@@i} command, and \.{TIE} is unable to handle such
57 constructs if the change files modify lines included in those files.
58 The present program, \.{CTIE}, is designed to overcome this lack.
59 Like \.{TIE}, upon which it is based, it can either output a single
60 master \.{WEB} file or a master change file.  However, in both cases,
61 any include commands will be totally expanded and the files included
62 in the output rather than the \.{@@i} commands being left; this makes
63 this code feasible, which it would not necessarily be otherwise.
64 Other than this difference, \.{CTIE} should function identically to
65 \.{TIE} on files which do not involve any \.{CWEB} include commands.
66
67 The algorithm used is essentially the same as that of \.{TIE}, with
68 modifications to check for and handle \.{@@i} commands.  Thus, as with
69 \.{TIE}, the method used only needs one buffer line for each input
70 file.  Thus the storage requirement of \.{CTIE} does not depend on the
71 sizes of the input files but only on their number.
72
73 The program is written in \CEE/ and has few system dependencies.
74
75 The ``banner line'' defined here should be changed whenever
76 \.{CTIE} is modified.  We also keep the version number here separately
77 for ease; it is used below.
78
79 @d version_number "1.1"
80 @d banner  "This is CTIE, Version 1.1"
81 @d copyright 
82     "Copyright 2002,2003 Julian Gilbey.  All rights reserved.  There is no warranty.\n\
83 Run with the --version option for other important information."
84
85
86 @ The main outline of the program is now given.
87 This can be used more or less for any \CEE/ program.
88
89 @c
90 @<Global \&{\#include}s@>@;
91 @<Global types@>@;
92 @<Predeclaration of functions@>@;
93 @<Global variables@>@;
94 @<Error handling functions@>@;
95 @<Internal functions@>@;
96 @<The main function@>@;
97
98 @ And this is the structure of the |main| function: this is where
99 \.{CTIE} starts, and where it ends.
100
101 @<The main function@>=
102 main(argc, argv)
103         int argc; string *argv;
104 {
105     @<Initialise parameters@>;
106     @<Scan the parameters@>@;
107     @<Print the banners@>;
108     @<Get the master file started@>@;
109     @<Prepare the change files@>@;
110     @<Prepare the output file@>@;
111     @<Process the input@>@;
112     @<Check that all changes have been read@>@;
113     exit(wrap_up());
114 }
115
116 @ We include the additional types |boolean| and |string|.  \.{CTIE}
117 replaces the complex \.{TIE} character set handling (based on that of
118 the original \.{WEB} system) with the standard \.{CWEB} behaviour, and
119 so uses the |char| type for input and output.
120
121 @d false 0
122 @d true 1
123
124 @<Global types@>=
125 typedef int boolean;
126 typedef char* string;
127
128
129 @ We predeclare some standard string-handling functions here instead of
130 including their system header files, because the names of the header files
131 are not as standard as the names of the functions.  (There's confusion
132 between \.{<string.h>} and \.{<strings.h>}.)
133
134 @<Predecl...@>=
135 extern int strlen(); /* length of string */
136 extern char* strcpy(); /* copy one string to another */
137 extern int strncmp(); /* compare up to $n$ string characters */
138 extern char* strncpy(); /* copy up to $n$ string characters */
139 extern char *strerror();
140
141
142 @ The following parameters should be sufficient for most
143 applications of \.{CTIE}.
144 @^system dependencies@>
145
146 @d buf_size 1024 /* maximum length of one input line */
147 @d max_file_index 32
148 /* we don't think that anyone needs more than 32 change files */
149 @d xisupper(c) (isupper(c)&&((unsigned char)c<0200))
150
151
152 @ We introduce a history variable that allows us to set a
153 return code if the operating system can use it.
154 First we introduce the coded values for the history.
155 This variable must be initialized.
156 (We do this even if the value given may be the default for
157 variables, just to document the need for the initial value.)
158 @d spotless 0
159 @d troublesome 1
160 @d fatal 2
161
162 @<Global variables@>=
163 int history=spotless;
164
165
166 @* Input and output.
167 Standard output for the user is done by writing on |stdout|.  Error
168 messages are written to |stderr|.  Terminal input is not needed in
169 this version of \.{CTIE}.  |stdin|, |stdout| and |stderr| are
170 predefined as we include the \.{stdio.h} definitions.
171
172 @<Global \&{\#include}s@>=
173 #include <stdio.h>
174
175
176 @ And we need dynamic memory allocation.
177 This should cause no trouble in any \CEE/ program.
178 @^system dependencies@>
179
180 @<Global \&{\#include}s@>=
181 #ifdef __STDC__
182 #include <stdlib.h>
183 #else
184 #include <malloc.h>
185 #endif
186
187
188 @* Data structures.
189 The multiple primary input files (master file and change
190 files) are treated the same way.  To organize the
191 simultaneous usage of several input files, we introduce the
192 data type \&{in\_file\_modes}.
193
194 The mode |search| indicates that \.{CTIE} searches for a
195 match of the input line with any line of an input file in
196 |reading| mode.  |test| is used whenever a match is found
197 and it has to be tested if the next input lines do match
198 also.  |reading| describes that the lines can be read without
199 any check for matching other lines.  |ignore| denotes
200 that the file cannot be used.  This may happen because an
201 error has been detected or because the end of the file has
202 been found.
203
204 \leavevmode |file_types| is used to describe whether a file
205 is a master file or a change file. The value |unknown| is added
206 to this type to set an initial mode for the output file.
207 This enables us to check whether any option was used to select
208 the kind of output. (this would even be necessary if we
209 would assume a default action for missing options.)
210
211 @<Global types@>=
212 #define search 0
213 #define test 1
214 #define reading 2
215 #define ignore 3
216 typedef int in_file_modes; /* should be |enum(search, test, reading, ignore)| */
217 #define unknown 0
218 #define master 1
219 #define chf 2
220 typedef int file_types; /* should be |enum(unknown, master, chf)| */
221
222
223 @ A variable of type |out_md_type| will tell us in what state the output
224 change file is during processing. |normal| will be the state, when we
225 did not yet start a change, |pre| will be set when we write the lines
226 to be changes and |post| will indicate that the replacement lines are
227 written.
228
229 @<Global types@>=
230 #define normal 0
231 #define pre 1
232 #define post 2
233 typedef int out_md_type; /* should be |enum(normal, pre, post)| */
234
235
236 @ The next type will indicate variables used as an index into the file
237 table.
238
239 @<Global types@>=
240 typedef int file_index;  /* |-1..max_file_index+1| */
241
242
243 @ This is the data structure in which we collect information about
244 each include file.
245
246 @<Global types@>=
247 typedef struct _indsc {
248     char file_name[max_file_name_length];
249     long line;
250     FILE *the_file;
251     struct _indsc *parent;
252 } include_description;
253
254
255 @ The following data structure contains all of the information needed
256 to use these input files.
257 %`line' is a normal identifier throughout this program 
258 @f line dummy
259 @<Global types@>=
260 typedef struct _idsc{
261     string file_name;
262     char buffer[buf_size];
263     in_file_modes mode;
264     long line;
265     file_types type_of_file;
266     include_description *current_include;
267     char *buffer_end;
268     char *limit;
269     char *loc;
270     FILE *the_file;
271     int dont_match;
272     } input_description;
273
274
275 @ Every one of the primary input files might include another file
276 using the \.{@@i} include mechanism.  In turn, each of these might
277 include other files, and so on.  We allow a limited number of these
278 files to be opened simultaneously, and we store information about the
279 currently open include files as a linked list attached to each primary
280 file.
281
282 @d max_include_files 20
283         /* maximum number of include files open simultaneously */
284 @d max_file_name_length 60
285
286 @<Global variables@>=
287 int total_include_files = 0;  /* count 'em */
288
289 @ The following variables refer to the files in action, the number of
290 change files, the mode of operation and the current output state.
291
292 @<Global variables@>=
293 file_index actual_input, test_input, no_ch;
294 file_types prod_chf=unknown;
295 out_md_type out_mode;
296
297 @ And the |actual_input| and |out_mode| variables need to be
298 initialised sensibly.
299
300 @<Initialise parameters@>=
301 actual_input=0;
302 out_mode=normal;
303
304 @ All primary input files (including the master file) are recorded
305 in the following structure.
306 The components are usually accessed through a local pointer variable,
307 requiring only a one-time-computation of the index expression.
308
309 @<Global variables@>=
310 input_description *input_organisation[max_file_index+1];
311
312
313 @* File I/O.
314 The basic function |get_line| can be used to get a line from
315 an input file.  The line is stored in the |buffer| part of the
316 descriptor.  The components |limit| and |line| are updated.  If the
317 end of the file is reached |mode| is set to |ignore|.  On some systems
318 it might be useful to replace tab characters by a proper number of
319 spaces since several editors used to create change files insert tab
320 characters into a source file not under control of the user.  So it
321 might be a problem to create a matching change file. 
322 @^tab character expansion@>
323
324 We define |get_line| to read a line from a file specified by the
325 corresponding file descriptor.  This function returns |true| if it is
326 successful and |false| if the end of the file has been reached.
327
328 @<Internal functions@>=
329 boolean get_line(i, do_includes)
330         file_index i; boolean do_includes;
331 {
332     register input_description *inp_desc=input_organisation[i];
333     register FILE *fp;
334
335     if (inp_desc->mode==ignore) return false;
336
337   restart:
338     if (inp_desc->current_include != NULL) {
339         register include_description *inc_desc=inp_desc->current_include;
340
341         fp=inc_desc->the_file;
342         @<Get include line into buffer or |goto restart| if end of file@>@;
343     }
344     else {
345         fp=inp_desc->the_file;
346         @<Get line into buffer, |return false| if end of file@>@;
347     }
348
349     if (do_includes)
350         @<Check for \.{@@i} in newly read line, |goto restart| if
351             include fails@>@;
352     return true;
353 }
354
355
356 @ Lines must fit into the buffer completely.
357 We read all characters sequentially until an end of line is found
358 (but do not forget to check for |EOF|!).
359 Too long input lines will be truncated.
360 This will result in a damaged output if they occur in the
361 replacement part of a change file, or in an incomplerte check if the
362 matching part is concerned.
363 Tab character expansion might be done here.
364 @^tab character expansion@>
365
366 @<Get line into buffer,...@>=
367 {
368     register int c; /* the actual character read */
369     register char *k; /* where the next character goes */
370
371     if (feof(fp))
372         @<Handle end of file and return@>@;
373
374     inp_desc->limit = k = inp_desc->buffer;  /* beginning of buffer */
375     while (k<=inp_desc->buffer_end && (c=getc(fp)) != EOF && c!='\n')
376         if ((*(k++) = c) != ' ') inp_desc->limit = k;
377     if (k>inp_desc->buffer_end)
378         if ((c=getc(fp))!=EOF && c!='\n') {
379             ungetc(c, fp); inp_desc->loc=inp_desc->buffer;
380             err_print(i, "! Input line too long");
381 @.Input line too long@>
382         }
383     if (c==EOF && inp_desc->limit==inp_desc->buffer)
384         @<Handle end of file...@>@;
385
386     @<Increment the line number and print a progess report at
387         certain times@>@;
388 }
389
390
391 @ End of file is special if this file is the master file.
392 Then we set the global flag variable |input_has_ended|.
393
394 @<Handle end of file ...@>=
395 {
396     inp_desc->mode=ignore;
397     inp_desc->limit=NULL;  /* mark end-of-file */
398     if (inp_desc->type_of_file==master) input_has_ended=true;
399     fclose(fp);
400     return false;
401 }
402
403
404 @ This variable must be declared for global access.
405
406 @<Global variables@>=
407 boolean input_has_ended=false;
408
409
410 @ This section does what its name says. Every 100 lines
411 in the master file we print a dot, every 500 lines the number
412 of lines is shown.
413
414 @<Increment the line number and print ...@>=
415 inp_desc->line++;
416 if (inp_desc->type_of_file==master && inp_desc->line % 100==0) {
417    if (inp_desc->line % 500 == 0)  printf("%ld", inp_desc->line);
418    else  putchar('.');
419    fflush(stdout);
420 }
421
422
423 @ The following is very similar to the above, but for the case where
424 we are reading from an include file.
425
426 @<Get include line into buffer or |goto restart| if end of file@>=
427 {
428     register int c; /* the actual character read */
429     register char *k; /* where the next character goes */
430
431     if (feof(fp))
432         @<Handle end of include file and |goto restart|@>@;
433
434     inp_desc->limit = k = inp_desc->buffer;  /* beginning of buffer */
435     while (k<=inp_desc->buffer_end && (c=getc(fp)) != EOF && c!='\n')
436         if ((*(k++) = c) != ' ') inp_desc->limit = k;
437     if (k>inp_desc->buffer_end)
438         if ((c=getc(fp))!=EOF && c!='\n') {
439             ungetc(c, fp); inp_desc->loc=inp_desc->buffer;
440             err_print(i, "! Input line too long");
441 @.Input line too long@>
442         }
443     if (c==EOF && inp_desc->limit==inp_desc->buffer)
444         @<Handle end of include file...@>@;
445
446     inc_desc->line++;
447 }
448
449 @ We don't bail out if we find the end of an include file, we just
450 return to the parent file.
451
452 @<Handle end of include file and |goto restart|@>=
453 {
454     include_description *temp=inc_desc->parent;
455
456     fclose(fp);
457     free(inc_desc);
458     total_include_files--;
459     inp_desc->current_include=temp;
460     goto restart;
461 }
462
463
464 @ Usually, we have to check the line we have just read to see whether
465 it begins with \.{@@i} and therefore needs expanding.
466
467 @<Check for \.{@@i} in newly read line...@>=
468 {
469     inp_desc->loc=inp_desc->buffer;
470     *inp_desc->limit=' ';
471     if (*inp_desc->buffer=='@@' &&
472         (inp_desc->buffer[1]=='i' || inp_desc->buffer[1]=='I')) {
473         inp_desc->loc=inp_desc->buffer+2;
474         *inp_desc->limit='"'; /* this will terminate the search in all cases */
475         while (*inp_desc->loc==' '||*inp_desc->loc=='\t')
476             inp_desc->loc++;
477         if (inp_desc->loc>=inp_desc->limit) {
478             err_print(i, "! Include file name not given");
479 @.Include file name ...@>
480             goto restart;
481         }
482         if (total_include_files>=max_include_files) {
483             err_print(i, "! Too many nested includes");
484 @.Too many nested includes@>
485             goto restart;
486         } 
487         total_include_files++; /* push input stack */
488         @<Try to open include file, abort push if unsuccessful, go to |restart|@>;
489     }
490 }
491
492
493 @ When an \.{@@i} line is found in the file, we must temporarily
494 stop reading it and start reading from the named include file.  The
495 \.{@@i} line should give a complete file name with or without
496 double quotes.
497 If the environment variable \.{CWEBINPUTS} is set, or if the compiler flag 
498 of the same name was defined at compile time,
499 \.{CWEB} will look for include files in the directory thus named, if
500 it cannot find them in the current directory.
501 (Colon-separated paths are not supported.)
502 The remainder of the \.{@@i} line after the file name is ignored.
503
504 @d too_long() {total_include_files--; free(new_inc);
505         err_print(i, "! Include file name too long"); goto restart;}
506
507 @<Try to open...@>=
508 {
509     include_description *new_inc;
510     char temp_file_name[max_file_name_length]; 
511     char *file_name_end;
512     char *k, *kk;
513     int l; /* length of file name */
514   
515     new_inc=(include_description *) malloc(sizeof(include_description));
516     if (new_inc==NULL)
517         fatal_error(i, "! No memory for new include descriptor", "");
518     new_inc->line=0;
519     k=new_inc->file_name;
520     file_name_end=k+max_file_name_length-1;
521
522     if (*inp_desc->loc=='"') {
523         inp_desc->loc++;
524         while (*inp_desc->loc!='"' && k<=file_name_end)
525             *k++=*inp_desc->loc++;
526         if (inp_desc->loc==inp_desc->limit)
527             k=file_name_end+1; /* unmatched quote is `too long' */
528     } else
529         while (*inp_desc->loc!=' '&&*inp_desc->loc!='\t'&&
530                *inp_desc->loc!='"'&&k<=file_name_end) *k++=*inp_desc->loc++;
531     if (k>file_name_end) too_long();
532 @.Include file name ...@>
533     *k='\0';
534     if ((new_inc->the_file=fopen(new_inc->file_name, "r"))!=NULL) {
535         new_inc->parent=inp_desc->current_include; /* link it in */
536         inp_desc->current_include=new_inc;
537         goto restart; /* success */
538     }
539     kk=getenv("CWEBINPUTS");
540     if (kk!=NULL) {
541         if ((l=strlen(kk))>max_file_name_length-2) too_long();
542         strcpy(temp_file_name, kk);
543     }
544     else {
545 #ifdef CWEBINPUTS
546         if ((l=strlen(CWEBINPUTS))>max_file_name_length-2) too_long();
547         strcpy(temp_file_name, CWEBINPUTS);
548 #else
549         l=0; 
550 #endif /* |CWEBINPUTS| */
551     }
552     if (l>0) {
553         if (k+l+2>=file_name_end)  too_long();
554         for (; k>= new_inc->file_name; k--) *(k+l+1)=*k;
555         strcpy(new_inc->file_name, temp_file_name);
556         new_inc->file_name[l]='/'; /* \UNIX/ pathname separator */
557         if ((new_inc->the_file=fopen(new_inc->file_name, "r"))!=NULL) {
558             new_inc->parent=inp_desc->current_include; /* link it in */
559             inp_desc->current_include=new_inc;
560             goto restart; /* success */
561         }
562     }
563     total_include_files--;
564     free(new_inc);
565     err_print(i, "! Cannot open include file");
566 @.Cannot open include file@>
567     goto restart;
568 }
569
570
571
572 @* Reporting errors to the user.
573 There may be errors if a line in a given change
574 file does not match a line in the master file or a
575 replacement in a previous change file.  Such errors are
576 reported to the user by saying
577 $$
578    \hbox{|err_print(file_no, "! Error message")|;}
579 $$
580 where |file_no| is the number of the file which is concerned by the
581 error.  Please note that no trailing dot is supplied in the error
582 message because it is appended by |err_print|.
583
584 @<Predecl...@>=
585 void err_print();
586
587 @ Here is the outline of the |err_print| function.
588
589 @<Error handling...@>=
590 void err_print(i, s) /* prints `\..' and location of error message */
591 file_index i; char *s;
592 {
593   char *k, *l; /* pointers into an appropriate |buffer| */
594   fprintf(stderr, *s=='!'? "\n%s" : "%s", s);
595   if(i>=0) @<Print error location based on input buffer@>@;
596   else putc('\n', stderr);
597   fflush(stderr);
598   history=troublesome;
599 }
600
601 @ The error locations can be indicated by using the variables
602 |loc|, |line| and |file_name| within the appropriate file description
603 structures, which tell respectively the first
604 unlooked-at position in the |buffer|, the current line number and the
605 current file.  We can determine whether we are looking at an included
606 file or not by examining the |current_include| variable.
607 This routine should be modified on systems whose standard text editor
608 has special line-numbering conventions.
609 @^system dependencies@>
610
611 @<Print error location based on input buffer@>=
612 {
613     register input_description *inp_desc=input_organisation[i];
614     register include_description *inc_desc=inp_desc->current_include;
615
616     if (inc_desc!=NULL) {
617         fprintf(stderr, ". (l. %ld of include file %s", inc_desc->line,
618             inc_desc->file_name);
619         fprintf(stderr, " included from l. %ld of %s file %s)\n",
620             inp_desc->line,
621             inp_desc->type_of_file==master?"master":"change",
622             inp_desc->file_name);
623     }
624     else 
625         fprintf(stderr, ". (l. %ld of %s file %s)\n", inp_desc->line,
626             inp_desc->type_of_file==master?"master":"change",
627             inp_desc->file_name);
628     l=(inp_desc->loc>=inp_desc->limit? inp_desc->limit: inp_desc->loc);
629     if (l>inp_desc->buffer) {
630         for (k=inp_desc->buffer; k<l; k++)
631             if (*k=='\t') putc(' ', stderr);
632             else putc(*k, stderr); /* print the characters already read */
633         putc('\n', stderr);
634         for (k=inp_desc->buffer; k<l; k++)
635             putc(' ', stderr); /* space out the next line */
636     }
637     for (k=l; k<inp_desc->limit; k++)
638         putc(*k, stderr); /* print the part not yet read */
639     putc('\n', stderr);
640 }
641
642
643 @ Non recoverable errors are handled by calling |fatal_error| that
644 outputs a message and then calls `|wrap_up|' and exits.  |err_print|
645 will print the error message followed by an indication of where the
646 error was spotted in the source files.  |fatal_error| cannot state any
647 files because the problem is usually to access these.
648
649 @d fatal_error(i, s, t) {
650     fprintf(stderr, "\n%s", s);
651     err_print(i, t);
652     history=fatal;
653     exit(wrap_up());
654     }
655
656
657 @ Some implementations may wish to pass the |history| value to the
658 operating system so that it can be used to govern whether or not other
659 programs are started. Here, for instance, we pass the operating system
660 a status of~0 if and only if only harmless messages were printed.
661 @^system dependencies@>
662
663 @<Internal func...@>=
664 int wrap_up()
665 {
666     @<Print the job |history|@>;
667     if (history > spotless) return 1;
668     else return 0;
669 }
670
671 @ Always good to prototype.
672
673 @<Predecl...@>=
674 int wrap_up();
675
676 @ We report the history to the user, although this may not
677 be ``\UNIX/'' style---but we are in good company: \.{WEB} and
678 \TEX/ do the same.  We put this on |stdout| rather than |stderr|, so
679 that users can easily filter this away if they wish.
680 @^system dependencies@>
681
682 @<Print the job |history|@>=
683 switch (history) {
684   case spotless:
685     printf("\n(No errors were found.)\n"); break;
686   case troublesome:
687     printf("\n(Pardon me, but I think I spotted something wrong.)\n"); break;
688   case fatal: printf("(That was a fatal error, my friend.)\n");
689 } /* there are no other cases */
690
691
692 @ If there's a system error, we may be able to give the user more
693 information with the |pfatal_error| function.  This prints out system
694 error information if it is available.
695
696 @<Predecl...@>=
697 void pfatal_error();
698
699 @ @<Error handling...@>=
700 void pfatal_error(s, t)
701 char *s, *t;
702 {
703     char *strerr=strerror(errno);
704
705     fprintf(stderr, "\n%s%s", s, t);
706     if (strerr) fprintf(stderr, " (%s)\n", strerr);
707     else fprintf(stderr, "\n");
708     history=fatal;
709     exit(wrap_up());
710 }
711
712
713 @ We need an include file for the above.
714
715 @<Global \&{\#include}s@>=
716 #include <errno.h>
717
718
719 @* Handling multiple change files.
720 In the standard version we take the name of the
721 files from the command line.
722 It is assumed that filenames can be used as given in the
723 command line without changes.
724
725 First there are some sections to open all files.
726 If a file is not accessible, the run will be aborted.
727 Otherwise the name of the open file will be displayed.
728
729 @<Prepare the output file@>=
730 {
731     out_file=fopen(out_name, "w");
732     if (out_file==NULL) {
733          pfatal_error("! Cannot open/create output file", "");
734 @.Cannot open/create output file@>
735     }
736 }
737
738
739 @ The name of the file and the file desciptor are stored in
740 global variables.
741
742 @<Global variables@>=
743 FILE *out_file;
744 string out_name;
745
746
747 @ For the master file we start by reading its first line into the
748 buffer, if we could open it.
749
750 @<Get the master file started@>=
751 {
752     input_organisation[0]->the_file=
753         fopen(input_organisation[0]->file_name, "r");
754
755     if (input_organisation[0]->the_file==NULL)
756         pfatal_error("! Cannot open master file ",
757             input_organisation[0]->file_name);
758 @.Cannot open master file@>
759     printf("(%s)\n", input_organisation[0]->file_name);
760     input_organisation[0]->type_of_file=master;
761     get_line(0, true);
762 }
763
764
765 @ For the change files we must skip any comment part and see whether
766 there are any changes in it.  This is done by |init_change_file|.
767
768 @<Prepare the change files@>=
769 {
770     file_index i;
771
772     i=1;
773     while (i<no_ch) {
774         input_organisation[i]->the_file=
775             fopen(input_organisation[i]->file_name, "r");
776         if (input_organisation[i]->the_file==NULL)
777             pfatal_error("! Cannot open change file ",
778                 input_organisation[i]->file_name);
779 @.Cannot open change file@>
780         printf("(%s)\n", input_organisation[i]->file_name);
781         init_change_file(i);
782         i++;
783     }
784 }
785
786
787 @*Input/output organisation.
788 Here's a simple function that checks if two lines
789 are different.
790
791 @<Internal functions@>=
792 boolean lines_dont_match(i, j)
793         file_index i, j;
794 {
795     register input_description *iptr=input_organisation[i],
796                                *jptr=input_organisation[j];
797
798     if (iptr->limit-iptr->buffer != jptr->limit-jptr->buffer)
799         return true;
800     return strncmp(iptr->buffer, jptr->buffer, iptr->limit-iptr->buffer);
801 }
802
803
804 @ Function |init_change_file(i)| is used to ignore all
805 lines of the input file with index~|i| until the next change
806 module is found.
807
808 @<Internal functions@>=
809 void init_change_file(i)
810         file_index i;
811 {
812     register input_description *inp_desc=input_organisation[i];
813     char ccode;
814
815     inp_desc->limit=inp_desc->buffer;
816     @<Skip over comment lines; |return| if end of file@>@;
817     @<Skip to the next nonblank line; |return| if end of file@>@;
818     inp_desc->dont_match=0;
819 }
820
821
822 @ While looking for a line that begins with \.{@@x} in the change
823 file, we allow lines that begin with \.{@@}, as long as they don't
824 begin with \.{@@y}, \.{@@z} or \.{@@i} (which would probably mean that
825 the change file is fouled up).
826
827 @<Skip over comment lines...@>=
828 while(1) {
829     if (!get_line(i, false)) return; /* end of file reached */
830     if (inp_desc->limit<inp_desc->buffer+2) continue;
831     if (inp_desc->buffer[0]!='@@') continue;
832     ccode=inp_desc->buffer[1];
833     if (xisupper(ccode)) ccode=tolower(ccode);
834     if (ccode=='x') break;
835     if (ccode=='y' || ccode=='z' || ccode=='i') {
836         inp_desc->loc=inp_desc->buffer+2;
837         err_print(i, "! Missing @@x in change file");
838 @.Missing @@x...@>
839     }
840 }
841
842 @ Here we are looking at lines following the \.{@@x}.
843
844 @<Skip to the next nonblank line...@>=
845 do {
846     if (!get_line(i, true)) {
847         err_print(i, "! Change file ended after @@x");
848 @.Change file ended...@>
849         return;
850     }
851 } while (inp_desc->limit==inp_desc->buffer);
852
853
854 @ The |put_line| function is used to write a line from
855 input buffer |j| to the output file.
856
857 @<Internal functions@>=
858 void put_line(j)
859        file_index j;
860 {
861     char *ptr=input_organisation[j]->buffer;
862     char *lmt=input_organisation[j]->limit;
863
864     while(ptr<lmt) putc(*ptr++, out_file);
865     putc('\n', out_file);
866 }
867
868
869 @ The function |e_of_ch_module| returns true if the input
870 line from file |i| starts with \.{@@z}.
871
872 @<Internal functions@>=
873 boolean e_of_ch_module(i)
874         file_index i;
875 {
876     register input_description *inp_desc=input_organisation[i];
877
878     if (inp_desc->limit==NULL) {
879         err_print(i, "! Change file ended without @@z");
880 @.Change file ended without @@z@>
881         return true;
882     } else if (inp_desc->limit>=inp_desc->buffer+2)
883         if (inp_desc->buffer[0]=='@@' &&
884             (inp_desc->buffer[1]=='Z' || inp_desc->buffer[1]=='z'))
885         return true;
886     return false;
887 }
888
889
890 @ The function |e_of_ch_preamble| returns |true| if the input
891 line from file~|i| starts with \.{@@y}.
892
893 @<Internal functions@>=
894 boolean e_of_ch_preamble(i)
895         file_index i;
896 {
897     register input_description *inp_desc=input_organisation[i];
898
899     if (inp_desc->limit>=inp_desc->buffer+2 && inp_desc->buffer[0]=='@@')
900         if (inp_desc->buffer[1]=='Y'||inp_desc->buffer[1]=='y') {
901             if (inp_desc->dont_match>0) {
902                 inp_desc->loc=inp_desc->buffer+2;
903                 fprintf(stderr, "\n! Hmm... %d ", inp_desc->dont_match);
904                 err_print(i, "of the preceding lines failed to match");
905             }
906             return true;
907         }
908     return false;
909 }
910
911
912
913 @ To process the input file the next section reads a line of the
914 current (actual) input file and updates the |input_organisation| for
915 all files with index greater than |actual_input|.
916
917 @<Process a line, |break| when end of source reached@>=
918 {
919     file_index test_file;
920
921     @<Check the current files for any ends of changes@>@;
922     if (input_has_ended && actual_input==0) break; /* all done */
923     @<Scan all other files for changes to be done@>@;
924     @<Handle output@>@;
925     @<Step to next line@>@;
926 }
927
928
929 @ Any of the current change files may have reached the end of the
930 current change.  In such a case, intermediate lines must be skipped
931 and the next start of change is to be found.  This may make a change
932 file become inactive if the end of the file is reached.
933
934 @<Check the...@>=
935 {
936     register input_description *inp_desc;
937     while (actual_input>0 && e_of_ch_module(actual_input)) {
938         inp_desc=input_organisation[actual_input];
939         if (inp_desc->type_of_file==master) {
940           /* emergency exit, everything mixed up!*/
941           fatal_error(-1, "! This can't happen: change file is master file", "");
942 @.This can't happen...@>
943         }
944         inp_desc->mode=search;
945         init_change_file(actual_input);
946         while ((input_organisation[actual_input]->mode!=reading
947                  && actual_input>0))
948             actual_input--;
949     }
950 }
951
952
953 @ Now we will set |test_input| to the first change file that is being
954 tested against the current line.  If no other file is testing, then
955 |actual_input| refers to a line to write and |test_input| is set to
956 |none|.
957
958 @d none  (-1)
959
960 @<Scan all other files...@>=
961 test_input=none;
962 test_file=actual_input;
963 while (test_input==none && test_file<no_ch-1){
964     test_file++;
965     switch (input_organisation[test_file]->mode) {
966       case search:
967         if (lines_dont_match(actual_input, test_file)==false) {
968             input_organisation[test_file]->mode=test;
969             test_input=test_file;
970         }
971         break;
972       case test:
973         if (lines_dont_match(actual_input, test_file)) {
974             /* error, sections do not match; just note at this point */
975             input_organisation[test_file]->dont_match++;
976         }
977         test_input=test_file;
978         break;
979       case reading: /* this can't happen */
980         break;
981       case ignore: /* nothing to do */
982         break;
983     }
984 }
985
986
987 @ For the output we must distinguish between whether we are creating a
988 new change file or a new master file.  Change file creation requires
989 closer inspection because we may be before a change, in the pattern
990 (match) part or in the replacement part.  For master file creation, we
991 simply have to write the line from the current (actual) input.
992
993 @<Handle output@>=
994 if (prod_chf==chf) {
995     while(1) {
996         @<Test for |normal|, |break| when done@>@;
997         @<Test for |pre|, |break| when done@>@;
998         @<Test for |post|, |break| when done@>@;
999     }
1000 } else
1001     if (test_input==none) put_line(actual_input);
1002
1003
1004 @ Check whether we have to start a change file entry.
1005 Without a match nothing needs to be done.
1006
1007 @<Test for |normal|...@>=
1008 if (out_mode==normal) {
1009     if (test_input!=none) {
1010         fprintf(out_file, "@@x\n");
1011         out_mode=pre;
1012     } else break;
1013 }
1014
1015
1016 @ Check whether we have to start the replacement text.  This is the
1017 case when we are in |pre| mode but have no more matching lines.
1018 Otherwise the master file source line must be copied to the change
1019 file.
1020
1021 @<Test for |pre|...@>=
1022 if (out_mode==pre) {
1023     if (test_input==none) {
1024         fprintf(out_file, "@@y\n");
1025         out_mode=post;
1026     } else {
1027         if (input_organisation[actual_input]->type_of_file==master)
1028             put_line(actual_input);
1029         break;
1030     }
1031 }
1032
1033
1034 @ Check whether an entry from a change file is complete.  If the
1035 current input is from a change file which is not being tested against
1036 a later change file, then this change file line must be written.  If
1037 the actual input has been reset to the master file, we can finish this
1038 change.
1039
1040 @<Test for |post|...@>=
1041 if (out_mode==post) {
1042     if (input_organisation[actual_input]->type_of_file==chf) {
1043         if (test_input==none)  put_line(actual_input);
1044         break;
1045     } else {
1046         fprintf(out_file, "@@z\n\n");
1047         out_mode=normal;
1048     }
1049 }
1050
1051
1052 @ If we had a change, we must proceed in the actual file
1053 to be changed and in the change file in effect.
1054
1055 @<Step to next line@>=
1056 get_line(actual_input, true);
1057 if (test_input!=none) {
1058     get_line(test_input, true);
1059     if (e_of_ch_preamble(test_input)==true) {
1060         get_line(test_input, true); /* update current changing file */
1061         input_organisation[test_input]->mode=reading;
1062         actual_input=test_input;
1063         test_input=none;
1064     }
1065 }
1066
1067
1068 @ To create the new output file we have to scan the whole
1069 master file and all changes in effect when it ends.
1070 At the very end it is wise to check for all changes
1071 to have completed, in case the last line of the master file
1072 was to be changed.
1073
1074 @<Process the input@>=
1075 actual_input=0;
1076 input_has_ended=false;
1077 while (input_has_ended==false||actual_input!=0)
1078     @<Process a line...@>@;
1079 if (out_mode==post) /* last line has been changed */
1080     fprintf(out_file, "@@z\n");
1081
1082
1083 @ At the end of the program, we will tell the user if the
1084 change file had a line that didn't match any relevant line
1085 in the master file or any of the change files.
1086
1087 @<Check that all changes have been read@>=
1088 {
1089     file_index i;
1090
1091     for (i=1;i<no_ch;i++) { /* all change files */
1092         if (input_organisation[i]->mode!=ignore) {
1093             input_organisation[i]->loc=input_organisation[i]->buffer;
1094             err_print(i, "! Change file entry did not match");
1095 @.Change file entry ...@>
1096         }
1097     }
1098 }
1099
1100
1101 @ We want to tell the user about our command line options if they made
1102 a mistake.  This is done by the |usage_error()| function.  It contains
1103 merely the necessary print statements and exits afterwards.
1104
1105 @<Intern...@>=
1106 void usage_error()
1107 {
1108     @<Print the banners@>;
1109     fprintf(stderr, "Usage: ctie -[mc] outfile master changefile(s)\n");
1110     fprintf(stderr, "Type ctie --help for more information\n");
1111     exit(1);
1112 }
1113
1114
1115 @ Printing our welcome banners; we only do this if we are not asked
1116 for version or help information.
1117
1118 @<Print the banners@>=
1119 printf("%s\n", banner); /* print a ``banner line'' */
1120 printf("%s\n", copyright); /* include the copyright notice */
1121
1122
1123 @ We must scan through the list of parameters, given in |argv|.  The
1124 number is in |argc|.  We must pay attention to the flag parameter.  We
1125 need at least 3~parameters (\.{-m} or \.{-c}, an output file and a
1126 master file) and can handle up to |max_file_index| change files.  The
1127 names of the file parameters will be inserted into the structure of
1128 |input_organisation|.  The first file is special.  It indicates the
1129 output file.  When we allow flags at any position, we must find out
1130 which name is for what purpose.  The master file is already part of
1131 the |input_organisation| structure (index~0).  As long as the number
1132 of files found (counted in |no_ch|) is |-1| we have not yet found the
1133 output file name.
1134
1135 @<Scan the parameters@>=
1136 {
1137     if (argc>max_file_index+5-1)  usage_error();
1138     no_ch = -1; /* fill this part of |input_organisation| */
1139     while (--argc>0) {
1140         argv++;
1141         if (strcmp("-help", *argv)==0 || strcmp("--help", *argv)==0)
1142             @<Display help message and exit@>;
1143         if (strcmp("-version", *argv)==0 || strcmp("--version", *argv)==0)
1144             @<Display version information and exit@>;
1145         if (**argv=='-') @<Set a flag@>@;
1146         else @<Get a file name@>@;
1147     }
1148     if (no_ch<=0|| prod_chf==unknown)  usage_error();
1149 }
1150
1151
1152 @ The flag is about to determine the processing mode.
1153 We must make sure that this flag has not been set before.
1154 Further flags might be introduced to avoid/force overwriting of
1155 output files.
1156 Currently we just have to set the processing flag properly.
1157
1158 @<Set a flag@>=
1159 if (prod_chf!=unknown)  usage_error();
1160 else
1161     switch (*(*argv+1)) {
1162       case 'c': case 'C': prod_chf=chf;    break;
1163       case 'm': case 'M': prod_chf=master; break;
1164       default:  usage_error(); 
1165     }
1166
1167
1168 @ We have to distinguish whether this is the very first file name
1169 (which is the case if |no_ch==(-1)|) or if the next element of
1170 |input_organisation| must be filled.
1171
1172 @<Get a file name@>=
1173 {
1174     if (no_ch==(-1)) {
1175         out_name=*argv;
1176     } else {
1177         register input_description *inp_desc;
1178
1179         inp_desc=(input_description *) malloc(sizeof(input_description));
1180         if (inp_desc==NULL)
1181             fatal_error(-1, "! No memory for input descriptor", "");
1182 @.No memory for descriptor@>
1183         inp_desc->mode=search;
1184         inp_desc->line=0;
1185         inp_desc->type_of_file=chf;
1186         inp_desc->limit=inp_desc->buffer;
1187         inp_desc->buffer[0]=' ';
1188         inp_desc->loc=inp_desc->buffer+1;
1189         inp_desc->buffer_end=inp_desc->buffer+buf_size-2;
1190         inp_desc->file_name=*argv;
1191         inp_desc->current_include=NULL;
1192         input_organisation[no_ch]=inp_desc;
1193     }
1194     no_ch++;
1195 }
1196
1197
1198 @ Modules for dealing with help messages and version info.  We follow
1199 the \.{kpathsea} standard code here, so that we can easily adapt this
1200 to work with \.{kpathsea}.
1201
1202 @<Display help message and exit@>=
1203 usage_help();
1204 @.--help@>
1205
1206
1207
1208 @<Display version information and exit@>=
1209 {
1210     print_version_and_exit("CTIE", version_number);
1211 @.--version@>
1212 }
1213
1214
1215 @ Here is the usage information for \.{--help}.
1216
1217 @<Global variables@>=
1218 string CTIEHELP[] = {
1219     "Usage: ctie -[mc] outfile master changefile(s)",
1220     "  Create a new master file or change file from the given",
1221     "  master (C)WEB file and changefiles.",
1222     "  All filenames are taken literally; no suffixes are added.",
1223     "",
1224     "-m  create a new master file from original (C)WEB and change file(s)",
1225     "-c  create a master change file for original (C)WEB file from changefile(s)",
1226     "--help      display this help and exit",
1227     "--version   display version information and exit",
1228     NULL
1229 };
1230
1231
1232 @ @<Predec...@>=
1233 void usage_help();
1234 void print_version_and_exit();
1235
1236
1237 @ @c
1238 void usage_help()
1239 {
1240     string *message=CTIEHELP;
1241
1242     while (*message) {
1243         fputs(*message, stdout);
1244         putchar('\n');
1245         ++message;
1246     }
1247     putchar('\n');
1248     exit(0);
1249 }
1250
1251
1252 @ @c
1253 void print_version_and_exit(name, version)
1254         string name, version;
1255 {
1256     printf ("%s %s\n", name, version);
1257
1258     puts ("Copyright (C) 2002,2003 Julian Gilbey.");
1259
1260     puts ("There is NO warranty.  This is free software.  See the source");
1261     puts ("code of CTIE for redistribution conditions.");
1262
1263     exit (0);
1264 }
1265
1266
1267 @* System-dependent changes.
1268 This section should be replaced, if necessary, by
1269 changes to the program that are necessary to make \.{CTIE}
1270 work at a particular installation.  It is usually best to
1271 design your change file so that all changes to previous
1272 modules preserve the module numbering; then everybody's
1273 version will be consistent with the printed program.  More
1274 extensive changes, which introduce new modules, can be
1275 inserted here; then only the index itself will get a new
1276 module number.
1277 @^system dependencies@>
1278
1279
1280 @* Index.