2 % Copyright (c) 2002,2003 by Julian Gilbey
5 % This program is distributed WITHOUT ANY WARRANTY, express or implied.
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.
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.
16 % This program is based heavily on tie.w and common.w, part of the
19 \def\title{The CTIE processor}
22 $\copyright$ 2002,2003 Julian Gilbey
26 This program is distributed WITHOUT ANY WARRANTY, express or implied.
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.
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.
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.
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.
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.
73 The program is written in \CEE/ and has few system dependencies.
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.
79 @d version_number "1.1"
80 @d banner "This is CTIE, Version 1.1"
82 "Copyright 2002,2003 Julian Gilbey. All rights reserved. There is no warranty.\n\
83 Run with the --version option for other important information."
86 @ The main outline of the program is now given.
87 This can be used more or less for any \CEE/ program.
90 @<Global \&{\#include}s@>@;
92 @<Predeclaration of functions@>@;
93 @<Global variables@>@;
94 @<Error handling functions@>@;
95 @<Internal functions@>@;
96 @<The main function@>@;
98 @ And this is the structure of the |main| function: this is where
99 \.{CTIE} starts, and where it ends.
101 @<The main function@>=
103 int argc; string *argv;
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@>@;
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.
126 typedef char* string;
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>}.)
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();
142 @ The following parameters should be sufficient for most
143 applications of \.{CTIE}.
144 @^system dependencies@>
146 @d buf_size 1024 /* maximum length of one input line */
148 /* we don't think that anyone needs more than 32 change files */
149 @d xisupper(c) (isupper(c)&&((unsigned char)c<0200))
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.)
162 @<Global variables@>=
163 int history=spotless;
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.
172 @<Global \&{\#include}s@>=
176 @ And we need dynamic memory allocation.
177 This should cause no trouble in any \CEE/ program.
178 @^system dependencies@>
180 @<Global \&{\#include}s@>=
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}.
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
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.)
216 typedef int in_file_modes; /* should be |enum(search, test, reading, ignore)| */
220 typedef int file_types; /* should be |enum(unknown, master, chf)| */
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
233 typedef int out_md_type; /* should be |enum(normal, pre, post)| */
236 @ The next type will indicate variables used as an index into the file
240 typedef int file_index; /* |-1..max_file_index+1| */
243 @ This is the data structure in which we collect information about
247 typedef struct _indsc {
248 char file_name[max_file_name_length];
251 struct _indsc *parent;
252 } include_description;
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
260 typedef struct _idsc{
262 char buffer[buf_size];
265 file_types type_of_file;
266 include_description *current_include;
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
282 @d max_include_files 20
283 /* maximum number of include files open simultaneously */
284 @d max_file_name_length 60
286 @<Global variables@>=
287 int total_include_files = 0; /* count 'em */
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.
292 @<Global variables@>=
293 file_index actual_input, test_input, no_ch;
294 file_types prod_chf=unknown;
295 out_md_type out_mode;
297 @ And the |actual_input| and |out_mode| variables need to be
298 initialised sensibly.
300 @<Initialise parameters@>=
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.
309 @<Global variables@>=
310 input_description *input_organisation[max_file_index+1];
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@>
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.
328 @<Internal functions@>=
329 boolean get_line(i, do_includes)
330 file_index i; boolean do_includes;
332 register input_description *inp_desc=input_organisation[i];
335 if (inp_desc->mode==ignore) return false;
338 if (inp_desc->current_include != NULL) {
339 register include_description *inc_desc=inp_desc->current_include;
341 fp=inc_desc->the_file;
342 @<Get include line into buffer or |goto restart| if end of file@>@;
345 fp=inp_desc->the_file;
346 @<Get line into buffer, |return false| if end of file@>@;
350 @<Check for \.{@@i} in newly read line, |goto restart| if
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@>
366 @<Get line into buffer,...@>=
368 register int c; /* the actual character read */
369 register char *k; /* where the next character goes */
372 @<Handle end of file and return@>@;
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@>
383 if (c==EOF && inp_desc->limit==inp_desc->buffer)
384 @<Handle end of file...@>@;
386 @<Increment the line number and print a progess report at
391 @ End of file is special if this file is the master file.
392 Then we set the global flag variable |input_has_ended|.
394 @<Handle end of file ...@>=
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;
404 @ This variable must be declared for global access.
406 @<Global variables@>=
407 boolean input_has_ended=false;
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
414 @<Increment the line number and print ...@>=
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);
423 @ The following is very similar to the above, but for the case where
424 we are reading from an include file.
426 @<Get include line into buffer or |goto restart| if end of file@>=
428 register int c; /* the actual character read */
429 register char *k; /* where the next character goes */
432 @<Handle end of include file and |goto restart|@>@;
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@>
443 if (c==EOF && inp_desc->limit==inp_desc->buffer)
444 @<Handle end of include file...@>@;
449 @ We don't bail out if we find the end of an include file, we just
450 return to the parent file.
452 @<Handle end of include file and |goto restart|@>=
454 include_description *temp=inc_desc->parent;
458 total_include_files--;
459 inp_desc->current_include=temp;
464 @ Usually, we have to check the line we have just read to see whether
465 it begins with \.{@@i} and therefore needs expanding.
467 @<Check for \.{@@i} in newly read line...@>=
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')
477 if (inp_desc->loc>=inp_desc->limit) {
478 err_print(i, "! Include file name not given");
479 @.Include file name ...@>
482 if (total_include_files>=max_include_files) {
483 err_print(i, "! Too many nested includes");
484 @.Too many nested includes@>
487 total_include_files++; /* push input stack */
488 @<Try to open include file, abort push if unsuccessful, go to |restart|@>;
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
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.
504 @d too_long() {total_include_files--; free(new_inc);
505 err_print(i, "! Include file name too long"); goto restart;}
509 include_description *new_inc;
510 char temp_file_name[max_file_name_length];
513 int l; /* length of file name */
515 new_inc=(include_description *) malloc(sizeof(include_description));
517 fatal_error(i, "! No memory for new include descriptor", "");
519 k=new_inc->file_name;
520 file_name_end=k+max_file_name_length-1;
522 if (*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' */
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 ...@>
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 */
539 kk=getenv("CWEBINPUTS");
541 if ((l=strlen(kk))>max_file_name_length-2) too_long();
542 strcpy(temp_file_name, kk);
546 if ((l=strlen(CWEBINPUTS))>max_file_name_length-2) too_long();
547 strcpy(temp_file_name, CWEBINPUTS);
550 #endif /* |CWEBINPUTS| */
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 */
563 total_include_files--;
565 err_print(i, "! Cannot open include file");
566 @.Cannot open include file@>
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
578 \hbox{|err_print(file_no, "! Error message")|;}
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|.
587 @ Here is the outline of the |err_print| function.
589 @<Error handling...@>=
590 void err_print(i, s) /* prints `\..' and location of error message */
591 file_index i; char *s;
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);
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@>
611 @<Print error location based on input buffer@>=
613 register input_description *inp_desc=input_organisation[i];
614 register include_description *inc_desc=inp_desc->current_include;
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",
621 inp_desc->type_of_file==master?"master":"change",
622 inp_desc->file_name);
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 */
634 for (k=inp_desc->buffer; k<l; k++)
635 putc(' ', stderr); /* space out the next line */
637 for (k=l; k<inp_desc->limit; k++)
638 putc(*k, stderr); /* print the part not yet read */
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.
649 @d fatal_error(i, s, t) {
650 fprintf(stderr, "\n%s", s);
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@>
663 @<Internal func...@>=
666 @<Print the job |history|@>;
667 if (history > spotless) return 1;
671 @ Always good to prototype.
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@>
682 @<Print the job |history|@>=
685 printf("\n(No errors were found.)\n"); break;
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 */
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.
699 @ @<Error handling...@>=
700 void pfatal_error(s, t)
703 char *strerr=strerror(errno);
705 fprintf(stderr, "\n%s%s", s, t);
706 if (strerr) fprintf(stderr, " (%s)\n", strerr);
707 else fprintf(stderr, "\n");
713 @ We need an include file for the above.
715 @<Global \&{\#include}s@>=
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.
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.
729 @<Prepare the output file@>=
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@>
739 @ The name of the file and the file desciptor are stored in
742 @<Global variables@>=
747 @ For the master file we start by reading its first line into the
748 buffer, if we could open it.
750 @<Get the master file started@>=
752 input_organisation[0]->the_file=
753 fopen(input_organisation[0]->file_name, "r");
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;
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|.
768 @<Prepare the change files@>=
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);
787 @*Input/output organisation.
788 Here's a simple function that checks if two lines
791 @<Internal functions@>=
792 boolean lines_dont_match(i, j)
795 register input_description *iptr=input_organisation[i],
796 *jptr=input_organisation[j];
798 if (iptr->limit-iptr->buffer != jptr->limit-jptr->buffer)
800 return strncmp(iptr->buffer, jptr->buffer, iptr->limit-iptr->buffer);
804 @ Function |init_change_file(i)| is used to ignore all
805 lines of the input file with index~|i| until the next change
808 @<Internal functions@>=
809 void init_change_file(i)
812 register input_description *inp_desc=input_organisation[i];
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;
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).
827 @<Skip over comment lines...@>=
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");
842 @ Here we are looking at lines following the \.{@@x}.
844 @<Skip to the next nonblank line...@>=
846 if (!get_line(i, true)) {
847 err_print(i, "! Change file ended after @@x");
848 @.Change file ended...@>
851 } while (inp_desc->limit==inp_desc->buffer);
854 @ The |put_line| function is used to write a line from
855 input buffer |j| to the output file.
857 @<Internal functions@>=
861 char *ptr=input_organisation[j]->buffer;
862 char *lmt=input_organisation[j]->limit;
864 while(ptr<lmt) putc(*ptr++, out_file);
865 putc('\n', out_file);
869 @ The function |e_of_ch_module| returns true if the input
870 line from file |i| starts with \.{@@z}.
872 @<Internal functions@>=
873 boolean e_of_ch_module(i)
876 register input_description *inp_desc=input_organisation[i];
878 if (inp_desc->limit==NULL) {
879 err_print(i, "! Change file ended without @@z");
880 @.Change file ended without @@z@>
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'))
890 @ The function |e_of_ch_preamble| returns |true| if the input
891 line from file~|i| starts with \.{@@y}.
893 @<Internal functions@>=
894 boolean e_of_ch_preamble(i)
897 register input_description *inp_desc=input_organisation[i];
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");
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|.
917 @<Process a line, |break| when end of source reached@>=
919 file_index test_file;
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@>@;
925 @<Step to next line@>@;
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.
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...@>
944 inp_desc->mode=search;
945 init_change_file(actual_input);
946 while ((input_organisation[actual_input]->mode!=reading
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
960 @<Scan all other files...@>=
962 test_file=actual_input;
963 while (test_input==none && test_file<no_ch-1){
965 switch (input_organisation[test_file]->mode) {
967 if (lines_dont_match(actual_input, test_file)==false) {
968 input_organisation[test_file]->mode=test;
969 test_input=test_file;
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++;
977 test_input=test_file;
979 case reading: /* this can't happen */
981 case ignore: /* nothing to do */
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.
996 @<Test for |normal|, |break| when done@>@;
997 @<Test for |pre|, |break| when done@>@;
998 @<Test for |post|, |break| when done@>@;
1001 if (test_input==none) put_line(actual_input);
1004 @ Check whether we have to start a change file entry.
1005 Without a match nothing needs to be done.
1007 @<Test for |normal|...@>=
1008 if (out_mode==normal) {
1009 if (test_input!=none) {
1010 fprintf(out_file, "@@x\n");
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
1021 @<Test for |pre|...@>=
1022 if (out_mode==pre) {
1023 if (test_input==none) {
1024 fprintf(out_file, "@@y\n");
1027 if (input_organisation[actual_input]->type_of_file==master)
1028 put_line(actual_input);
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
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);
1046 fprintf(out_file, "@@z\n\n");
1052 @ If we had a change, we must proceed in the actual file
1053 to be changed and in the change file in effect.
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;
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
1074 @<Process the input@>=
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");
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.
1087 @<Check that all changes have been read@>=
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 ...@>
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.
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");
1115 @ Printing our welcome banners; we only do this if we are not asked
1116 for version or help information.
1118 @<Print the banners@>=
1119 printf("%s\n", banner); /* print a ``banner line'' */
1120 printf("%s\n", copyright); /* include the copyright notice */
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
1135 @<Scan the parameters@>=
1137 if (argc>max_file_index+5-1) usage_error();
1138 no_ch = -1; /* fill this part of |input_organisation| */
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@>@;
1148 if (no_ch<=0|| prod_chf==unknown) usage_error();
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
1156 Currently we just have to set the processing flag properly.
1159 if (prod_chf!=unknown) usage_error();
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();
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.
1172 @<Get a file name@>=
1177 register input_description *inp_desc;
1179 inp_desc=(input_description *) malloc(sizeof(input_description));
1181 fatal_error(-1, "! No memory for input descriptor", "");
1182 @.No memory for descriptor@>
1183 inp_desc->mode=search;
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;
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}.
1202 @<Display help message and exit@>=
1208 @<Display version information and exit@>=
1210 print_version_and_exit("CTIE", version_number);
1215 @ Here is the usage information for \.{--help}.
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.",
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",
1234 void print_version_and_exit();
1240 string *message=CTIEHELP;
1243 fputs(*message, stdout);
1253 void print_version_and_exit(name, version)
1254 string name, version;
1256 printf ("%s %s\n", name, version);
1258 puts ("Copyright (C) 2002,2003 Julian Gilbey.");
1260 puts ("There is NO warranty. This is free software. See the source");
1261 puts ("code of CTIE for redistribution conditions.");
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
1277 @^system dependencies@>