~ubuntu-branches/ubuntu/quantal/gclcvs/quantal

« back to all changes in this revision

Viewing changes to merge.c

  • Committer: Bazaar Package Importer
  • Author(s): Camm Maguire
  • Date: 2004-06-24 15:13:46 UTC
  • Revision ID: james.westby@ubuntu.com-20040624151346-xh0xaaktyyp7aorc
Tags: 2.7.0-26
C_GC_OFFSET is 2 on m68k-linux

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright William F. Schelter, University of Texas 1987 */
 
2
 
 
3
/* This file may be copied by anyone, but changes may not
 
4
be made without permission of the author.  The author hopes it will be
 
5
useful but cannot assume any responsibility for its use or problems
 
6
caused by its use.  The program is provided as is. */
 
7
 
 
8
/* The program takes two files file1 = orig and file2 = orig.V 
 
9
 
 
10
tutorial% merge orig orig.V > foo 
 
11
 
 
12
and copies orig according to the recipe in orig.V.  The advantage of
 
13
this program is that it does this according to the context of orig.
 
14
Thus even though orig might change slightly (eg some one added an
 
15
extra line to the copyright notice), the same change file will
 
16
probably still be valid.  
 
17
 
 
18
If the first argument is - then the orig is standard input.
 
19
If a third argument is supplied, it is the name of a file to use
 
20
instead of standard output.
 
21
 
 
22
 
 
23
tutorial% merge orig orig.V | merge - change2 final
 
24
 
 
25
would take the result of merge of orig and orig.V and use it to merge
 
26
with change2 to produce the file final.
 
27
 
 
28
 
 
29
The format of a change (.V) file is very simple: There is only ONE
 
30
type of command in a change file.  REPLACE X by Y.  Here X represents
 
31
a chunk of text in the orig file, and Y the substitution which you
 
32
wish to make for this occurrence.  The Y appears explicitly in the
 
33
change file, while the text X may be specified fully and explicitly,
 
34
OR by giving sufficient context from the beginning and end of X.  Thus
 
35
in general it takes three things to specify a change.  The beginning
 
36
of X (Xbegin), the end of X (Xend), and all of Y.  These three pieces
 
37
of text are separated by four delimiters.  The delimiters are not
 
38
single characters, but rather sequences of four characters.  This is
 
39
done so as to avoid having to quote the delimiter (see QUOTING below).
 
40
The delimiters are "\n@s[" "\n@s," "\n@s|" and "\n@s]". 
 
41
NOTE: The \n (Newline) Character IS PART OF THE DELIMITER in ALL CASES.
 
42
 
 
43
@s[X
 
44
@s|Y
 
45
@s]
 
46
 
 
47
Thus in the above case the X text is only "X" it does not have any
 
48
newlines in it! They belong to the delimiters.  For "X\n" we would see
 
49
 
 
50
 
 
51
@s[X
 
52
 
 
53
@s|Y
 
54
@s]
 
55
 
 
56
 
 
57
The general case where X is a very long chunk of text, or perhaps something
 
58
sensitive to copyright, so that you cannot include several pages, you 
 
59
could make Xbegin be the first few lines, and Xend the last few lines.
 
60
All intervening lines (including the Xbegin and the Xend, would be ripped
 
61
out, and replaced by Y.
 
62
 
 
63
@s[Xbegin
 
64
@s,Xend
 
65
@s|Y
 
66
@s]
 
67
 
 
68
 
 
69
One cycle of the merge may be thought of as:
 
70
The merge program looks in the change (.V) file for the next \ns[, 
 
71
in order to determine  the next values for Xbegin,Xend,and Y.
 
72
Having determined these, its position in the (.V) file will have 
 
73
advanced to after the \n@s].
 
74
 
 
75
The merge program then starts at its current position in the original
 
76
file and searches for the next occurrence of Xbegin, marking its
 
77
beginning, then for the end of Xend.  The inclusive interval so
 
78
marked, is deleted and Y is substituted. The current position in the
 
79
original file is now at the end of the Xend text.  The next Xbegin
 
80
text must occur after that point.  Only one pass is made through the
 
81
files.  
 
82
 
 
83
It is an error if the start of Xend does not follow
 
84
the end of Xbegin.  Thus Xbegin and Xend may NOT overlap.  A common
 
85
case will be that Xbegin is the entire interval and Xend = ""
 
86
In this case the merge program, if it finds \n@s| before \n@s,
 
87
will assume you want Xend="".
 
88
 
 
89
EXAMPLES:
 
90
 
 
91
@s[Hi bill
 
92
@s,
 
93
@s|new body
 
94
@s]
 
95
 
 
96
would delete the string "Hi bill" replacing it with "new body"
 
97
Xbegin="Hi bill"
 
98
Xend=""
 
99
Interval = "Hi bill"
 
100
Equivalently since the E interval is empty, we could have just
 
101
omitted the \@s,
 
102
 
 
103
@s[Hi bill
 
104
@s|new body
 
105
@s]
 
106
 
 
107
 
 
108
Example of change file with two changes:
 
109
****************
 
110
 
 
111
@s[(defmacro lcase (item &body body)
 
112
@s,      (setq  v (car rest))
 
113
@s|(defmacro lcase (items &body body)
 
114
      (setq  v (cadr rest))
 
115
@s]
 
116
 
 
117
Comments are allowed in change files.  In fact anything not between
 
118
matching "\ns[" and "\ns]" is a comment. 
 
119
 
 
120
 
 
121
@s[How is he
 
122
@s,
 
123
He is fine.
 
124
@s|He is sick.
 
125
@s]
 
126
 
 
127
*******
 
128
end of change file
 
129
 
 
130
The first change would replace the interval of the original file
 
131
"(defmacro....       (setq  v (car rest))"
 
132
by
 
133
"(defmacro lcase (items &body body)
 
134
      (setq  v (cadr rest))"
 
135
 
 
136
If the program could not find the interval "(defmacro....  (setq v
 
137
(car rest))" in the orignal file it would warn you. 
 
138
 
 
139
The intervals in the change file, must occur in the same order as in
 
140
the original file.  There is an emacs program merge.el which can
 
141
mechanically produce a changes file from an original and an edited
 
142
version.
 
143
 
 
144
Note: For convenience we pretend that the change file starts with
 
145
a new line, even if it does not.  Thus if @s[  are the first three
 
146
characters of the file and CHSTART1 = \n@s[, we count this as a
 
147
CHSTART1.  Since it is in the first column, it "appears" to have
 
148
the new line there.
 
149
 
 
150
 
 
151
QUOTING:
 
152
 
 
153
In order to have a change which involves one of the four letter
 
154
delimiters given above, we use the convention that "\n@@" in the first
 
155
column translates to "\n@".  You need not perform this quoting of @
 
156
unless the merger would be confused.  For example \n@(defun .. would
 
157
be ok, since this can't be mistaken for one of our delimiters.
 
158
Nonetheless \n@@(defun or \n@@s[ would translate to have one @ sign,
 
159
in the merge output.  The reason for not doubling all @ signs, is that
 
160
it is very easy to scan (visually) a change you are constructing, to
 
161
see that there are no @ signs in the first column, or at least none
 
162
which could be confused for the four letter change delimiters
 
163
"\n@s[","\n@s," ...  A poor human constructing a change (.V) file
 
164
should not have to sort through the X or Y text adding quoting
 
165
characters.
 
166
 
 
167
Note on length: Y may be any length, but Xbegin or Xend, may only be
 
168
CONTEXT_LIMIT long.
 
169
 
 
170
*/
 
171
 
 
172
 
 
173
 
 
174
 
 
175
 
 
176
 
 
177
 
 
178
 
 
179
 
 
180
 
 
181
/******************  THE CODE ********************/
 
182
 
 
183
 
 
184
#include <stdio.h>
 
185
 
 
186
 
 
187
#define CONTEXT_LIMIT 3000 /* size of the longest delimiter or replacement */
 
188
 
 
189
char *malloc();
 
190
void copy_rest();
 
191
char ssearch_for_string();
 
192
 
 
193
#define NULL_OUT (FILE *)0
 
194
 
 
195
#define CHSTART1 "\n@s["
 
196
#define CHSTART2 "\n@s,"
 
197
#define CHSTART3 "\n@s|"
 
198
#define CHSTART4 "\n@s]"
 
199
#define ACCEPT ",|"
 
200
#define NOACCEPT (char *) 0
 
201
#define NUL '\0'
 
202
#define TRUE 1
 
203
#define FALSE 0
 
204
#define eofch(ch) ((unsigned char)ch == (unsigned char) EOF)
 
205
 
 
206
char filenames[600];
 
207
 
 
208
#define myerror(string,arg) {(void)fprintf(stderr,string,arg); exit(-1);}
 
209
 
 
210
main(argc,argv)
 
211
int argc;
 
212
char *argv[];
 
213
{FILE *orig,*changes,*out;
 
214
 char *context,*endcontext;
 
215
 char *origname,*altername,*outname;
 
216
 char found;
 
217
 context=malloc(CONTEXT_LIMIT+2);
 
218
 endcontext=malloc(CONTEXT_LIMIT+2);
 
219
 
 
220
 outname=(char *)0;
 
221
 
 
222
 if (argc==1)
 
223
   {int tem;
 
224
    origname=filenames; altername=filenames+200; outname=filenames+400;
 
225
     /* get names from stdin */
 
226
     if (tem=scanf("%s %s %s",origname,altername,outname));
 
227
            else myerror("Three args weren't supplied: scanf returned %d\n",tem);
 
228
   }
 
229
 else{  if (!((argc==3) || (argc==4)))
 
230
       { myerror("Usage: merge file-orig file-changes [out-file]\n %d args given",argc-1);}
 
231
 else
 
232
   { origname=argv[1];
 
233
     altername=argv[2];
 
234
     if (argc >= 4) outname=argv[3];}};
 
235
 
 
236
 
 
237
/* now we have the names either from command or stdin, so open files */
 
238
 
 
239
if(origname[0]=='-' && origname[1]==NULL)
 
240
  orig=stdin;
 
241
 else{
 
242
 orig=fopen(origname,"r");
 
243
 if (!orig) {perror(origname); exit(-1);};
 
244
}
 
245
 
 
246
 changes=fopen(altername,"r");
 
247
 if (!changes) {perror(origname); exit(-1);};
 
248
 if (outname)
 
249
   {out=fopen(outname,"w");
 
250
    if (out); else {perror(outname); exit(-1);}}
 
251
 else out=stdout;
 
252
 
 
253
/* check if the file starts with chstart1 - newline. to avoid 
 
254
people thinking that starting file with @s[ is ok. */
 
255
 {char *str = CHSTART1;
 
256
  int ch;
 
257
  while(*(++str)) /* skip the newline start */
 
258
    { (ch=getc(changes));
 
259
      if (ch == *str) ;
 
260
      else
 
261
      { ungetc(ch,changes); goto not_found;}
 
262
    }
 
263
   goto got_one;
 
264
  not_found:;}
 
265
 
 
266
 {while(search_for_string(changes,CHSTART1,NULL_OUT,FALSE) > 0)
 
267
    { got_one:
 
268
      if (found=
 
269
          ssearch_for_string(changes,CHSTART2,context,CONTEXT_LIMIT,TRUE,
 
270
                             ACCEPT));
 
271
      else
 
272
        {myerror("\nNo end for start change context in change file:\n`%s'\n",context);};
 
273
      if (found==ACCEPT[1])
 
274
        *endcontext=NUL;
 
275
      else
 
276
        {
 
277
          if /* there is probably a non null  endcontext */
 
278
            (ssearch_for_string(changes,CHSTART3,endcontext, 
 
279
                                CONTEXT_LIMIT,TRUE,NOACCEPT));
 
280
          else
 
281
            {myerror("No %s at beginning of line to denote end of change context",
 
282
                CHSTART3);}};
 
283
/* skip in orig down to the end of the context,copying thru begin context */
 
284
      if (search_for_string(orig,context,out,FALSE)>0); 
 
285
      else{myerror("\nCould not find the change start in original:\n`%s'\n"
 
286
               ,context);};
 
287
      if  /* copy out the changed version */
 
288
        (search_for_string(changes,CHSTART4,out,TRUE)>0);
 
289
      else
 
290
        {myerror("No %s at beginning of line to denote end of change context",
 
291
            CHSTART4);};
 
292
         
 
293
/*finish skipping over the region to be deleted in orig */      
 
294
      {if( search_for_string(orig,endcontext,NULL_OUT,FALSE) > 0);
 
295
       else
 
296
         {myerror("\nCould not find the end of the change in original:\n`%s'\n",
 
297
             endcontext);}}
 
298
    }
 
299
    copy_rest(orig,out);
 
300
    return 0;
 
301
  }}
 
302
 
 
303
 
 
304
string_match(sta,stb)
 
305
char *sta, *stb;
 
306
{while(*sta!=0)
 
307
   {if (*(sta++) != *(stb++)) return 0;}
 
308
  if (*stb==0) return 1; else return 0;
 
309
 }
 
310
 
 
311
void
 
312
copy_rest(file,out)
 
313
FILE *file,*out;
 
314
{register int ch;
 
315
while(1)
 
316
 {  
 
317
   ch=getc(file);
 
318
   if (eofch(ch) && feof(file)) break;
 
319
   putc(ch,out);}}
 
320
 
 
321
/* advance file to end of first occurrence of string, copying to out 
 
322
until the beginning of string */
 
323
 
 
324
#define USE_UNQUOTE 1
 
325
 
 
326
search_for_string(file,string,out,unquoting)
 
327
FILE *file,*out;
 
328
char *string;
 
329
int unquoting;
 
330
{int result;
 
331
 result=search_for_string1(file,string,out,USE_UNQUOTE && unquoting);
 
332
 return result;}
 
333
 
 
334
 
 
335
char *nxt,*lim,*ungetlim,*bp; 
 
336
char buffer[CONTEXT_LIMIT];
 
337
 
 
338
/*
 
339
void
 
340
myungetc(ch)
 
341
char ch;
 
342
{*bp++ = ch;}
 
343
 
 
344
char
 
345
mygetc(file)
 
346
FILE *file; 
 
347
{char x=((bp==buffer)? getc(file) : *--bp);
 
348
 return x;
 
349
}
 
350
*/
 
351
 
 
352
#define mygetc(file) ((bp==buffer)? getc(file) : *--bp)
 
353
#define myungetc(ch) *bp++ = ch
 
354
 
 
355
search_for_string1(file,string,out,unquoting)
 
356
FILE *file,*out;
 
357
char *string;
 
358
int unquoting;
 
359
{ /* char *nxt,*lim; */
 
360
  char  *s;
 
361
  int ch;
 
362
  nxt=lim=(char *)0;
 
363
 bp=buffer;
 
364
 if (*string==NUL) return 1; unquoting;
 
365
  while(1)
 
366
  {begin:
 
367
     ch=mygetc(file);
 
368
   if ((eofch(ch)) &&(feof(file))) return 0;
 
369
   if( ch==*string)
 
370
     {                          /* loop for checking */
 
371
       s = string;
 
372
       while(*(++s)!=0)
 
373
         {(ch=mygetc(file));
 
374
          if (eofch(ch) && feof(file))
 
375
            {char *cp=string;while (cp++<=s)
 
376
               {putc(*cp,file) ; return 0;}};
 
377
          if (*s!=ch)
 
378
            { if (out) putc(*string,out);
 
379
              {char *cp=s;
 
380
               if (!(unquoting && ch==string[1] && (s-string ==2)))
 
381
                   myungetc(ch);
 
382
               while (--cp > string)
 
383
                 myungetc(*cp);
 
384
              goto begin;}}
 
385
        }
 
386
        return 1;
 
387
   /*    printf("<found one>");  */
 
388
       }
 
389
   else if (out) putc(ch,out);};
 
390
  }
 
391
 
 
392
 
 
393
 
 
394
#define PUTC(ch,out) {if(ind++ < outlim) ((*(out++))=(ch));\
 
395
                               else return -1;}
 
396
 
 
397
char
 
398
ssearch_for_string1(file,string,out,outlim,unquoting,accept)
 
399
int outlim,unquoting;
 
400
FILE *file;
 
401
char *out;
 
402
char *string,*accept;
 
403
{register int ch;
 
404
 char *s; int ind=0;
 
405
 if (*string==NUL) return 'a';
 
406
 while(1)
 
407
   {ch = getc(file);
 
408
  begin:
 
409
    if (feof(file)) return (char) 0;
 
410
    if (ch==(*string))
 
411
      {s=string; ind=0;
 
412
       while(*(++s)!=0)
 
413
         {if ((*s==(ch=getc(file)))
 
414
              || (accept && *s==*accept && ch == *(accept+1)));
 
415
          else
 
416
            {if (out)
 
417
               {char *cp; cp=string;
 
418
                 if (unquoting && ch==string[1] && (s-string ==2))  s--;
 
419
                while (cp!=s)
 
420
                  {PUTC(*cp,out);cp++;}
 
421
              }
 
422
               break;}}
 
423
       if(*s==0)
 
424
          {PUTC(((char) 0),out);
 
425
         /* found a match */
 
426
           return(ch);}
 
427
       else goto begin;}
 
428
    else if (out) PUTC(ch,out);
 
429
  }
 
430
}
 
431
 
 
432
char
 
433
ssearch_for_string(file,string,out1,outlim,unquoting,accept)
 
434
int outlim;
 
435
FILE *file;
 
436
char *out1;
 
437
char *string,*accept;
 
438
int unquoting;
 
439
{char result;
 
440
 result=ssearch_for_string1(file,string,out1,outlim,unquoting,accept);
 
441
 return result;}
 
442
 
 
443
/* 
 
444
 
445
* To do:
 
446
* 1)The buffering for mygetc could be more efficient (in local variable).
 
447
* 2)Eliminate the double function calls used during debugging.
 
448
* 3)Improve error message, for help in finding context if a change
 
449
*   is not found.
 
450
*/