xref: /dragonfly/contrib/cvs-1.12/diff/ifdef.c (revision 86d7f5d3)
1 /* #ifdef-format output routines for GNU DIFF.
2    Copyright (C) 1989, 1991, 1992, 1993, 1994, 1998 Free Software Foundation, Inc.
3 
4 This file is part of GNU DIFF.
5 
6 GNU DIFF is distributed in the hope that it will be useful,
7 but WITHOUT ANY WARRANTY.  No author or distributor
8 accepts responsibility to anyone for the consequences of using it
9 or for whether it serves any particular purpose or works at all,
10 unless he says so in writing.  Refer to the GNU DIFF General Public
11 License for full details.
12 
13 Everyone is granted permission to copy, modify and redistribute
14 GNU DIFF, but only under the conditions described in the
15 GNU DIFF General Public License.   A copy of this license is
16 supposed to have been given to you along with GNU DIFF so you
17 can know your rights and responsibilities.  It should be in a
18 file named COPYING.  Among other things, the copyright notice
19 and this notice must be preserved on all copies.  */
20 
21 
22 #include "diff.h"
23 
24 struct group
25 {
26   struct file_data const *file;
27   int from, upto; /* start and limit lines for this group of lines */
28 };
29 
30 static char *format_group PARAMS((int, char *, int, struct group const *));
31 static char *scan_char_literal PARAMS((char *, int *));
32 static char *scan_printf_spec PARAMS((char *));
33 static int groups_letter_value PARAMS((struct group const *, int));
34 static void format_ifdef PARAMS((char *, int, int, int, int));
35 static void print_ifdef_hunk PARAMS((struct change *));
36 static void print_ifdef_lines PARAMS((int, char *, struct group const *));
37 
38 static int next_line;
39 
40 /* Print the edit-script SCRIPT as a merged #ifdef file.  */
41 
42 void
print_ifdef_script(script)43 print_ifdef_script (script)
44      struct change *script;
45 {
46   next_line = - files[0].prefix_lines;
47   print_script (script, find_change, print_ifdef_hunk);
48   if (next_line < files[0].valid_lines)
49     {
50       begin_output ();
51       format_ifdef (group_format[UNCHANGED], next_line, files[0].valid_lines,
52 		    next_line - files[0].valid_lines + files[1].valid_lines,
53 		    files[1].valid_lines);
54     }
55 }
56 
57 /* Print a hunk of an ifdef diff.
58    This is a contiguous portion of a complete edit script,
59    describing changes in consecutive lines.  */
60 
61 static void
print_ifdef_hunk(hunk)62 print_ifdef_hunk (hunk)
63      struct change *hunk;
64 {
65   int first0, last0, first1, last1, deletes, inserts;
66   char *format;
67 
68   /* Determine range of line numbers involved in each file.  */
69   analyze_hunk (hunk, &first0, &last0, &first1, &last1, &deletes, &inserts);
70   if (inserts)
71     format = deletes ? group_format[CHANGED] : group_format[NEW];
72   else if (deletes)
73     format = group_format[OLD];
74   else
75     return;
76 
77   begin_output ();
78 
79   /* Print lines up to this change.  */
80   if (next_line < first0)
81     format_ifdef (group_format[UNCHANGED], next_line, first0,
82 		  next_line - first0 + first1, first1);
83 
84   /* Print this change.  */
85   next_line = last0 + 1;
86   format_ifdef (format, first0, next_line, first1, last1 + 1);
87 }
88 
89 /* Print a set of lines according to FORMAT.
90    Lines BEG0 up to END0 are from the first file;
91    lines BEG1 up to END1 are from the second file.  */
92 
93 static void
format_ifdef(format,beg0,end0,beg1,end1)94 format_ifdef (format, beg0, end0, beg1, end1)
95      char *format;
96      int beg0, end0, beg1, end1;
97 {
98   struct group groups[2];
99 
100   groups[0].file = &files[0];
101   groups[0].from = beg0;
102   groups[0].upto = end0;
103   groups[1].file = &files[1];
104   groups[1].from = beg1;
105   groups[1].upto = end1;
106   format_group (1, format, '\0', groups);
107 }
108 
109 /* If DOIT is non-zero, output a set of lines according to FORMAT.
110    The format ends at the first free instance of ENDCHAR.
111    Yield the address of the terminating character.
112    GROUPS specifies which lines to print.
113    If OUT is zero, do not actually print anything; just scan the format.  */
114 
115 static char *
format_group(doit,format,endchar,groups)116 format_group (doit, format, endchar, groups)
117      int doit;
118      char *format;
119      int endchar;
120      struct group const *groups;
121 {
122   register char c;
123   register char *f = format;
124 
125   while ((c = *f) != endchar && c != 0)
126     {
127       f++;
128       if (c == '%')
129 	{
130 	  char *spec = f;
131 	  switch ((c = *f++))
132 	    {
133 	    case '%':
134 	      break;
135 
136 	    case '(':
137 	      /* Print if-then-else format e.g. `%(n=1?thenpart:elsepart)'.  */
138 	      {
139 		int i, value[2];
140 		int thendoit, elsedoit;
141 
142 		for (i = 0; i < 2; i++)
143 		  {
144 		    unsigned char f0 = f[0];
145 		    if (ISDIGIT (f0))
146 		      {
147 			value[i] = atoi (f);
148 			while (ISDIGIT ((unsigned char) *++f))
149 			  continue;
150 		      }
151 		    else
152 		      {
153 			value[i] = groups_letter_value (groups, f0);
154 			if (value[i] < 0)
155 			  goto bad_format;
156 			f++;
157 		      }
158 		    if (*f++ != "=?"[i])
159 		      goto bad_format;
160 		  }
161 		if (value[0] == value[1])
162 		  thendoit = doit, elsedoit = 0;
163 		else
164 		  thendoit = 0, elsedoit = doit;
165 		f = format_group (thendoit, f, ':', groups);
166 		if (*f)
167 		  {
168 		    f = format_group (elsedoit, f + 1, ')', groups);
169 		    if (*f)
170 		      f++;
171 		  }
172 	      }
173 	      continue;
174 
175 	    case '<':
176 	      /* Print lines deleted from first file.  */
177 	      print_ifdef_lines (doit, line_format[OLD], &groups[0]);
178 	      continue;
179 
180 	    case '=':
181 	      /* Print common lines.  */
182 	      print_ifdef_lines (doit, line_format[UNCHANGED], &groups[0]);
183 	      continue;
184 
185 	    case '>':
186 	      /* Print lines inserted from second file.  */
187 	      print_ifdef_lines (doit, line_format[NEW], &groups[1]);
188 	      continue;
189 
190 	    default:
191 	      {
192 		int value;
193 		char *speclim;
194 
195 		f = scan_printf_spec (spec);
196 		if (!f)
197 		  goto bad_format;
198 		speclim = f;
199 		c = *f++;
200 		switch (c)
201 		  {
202 		    case '\'':
203 		      f = scan_char_literal (f, &value);
204 		      if (!f)
205 			goto bad_format;
206 		      break;
207 
208 		    default:
209 		      value = groups_letter_value (groups, c);
210 		      if (value < 0)
211 			goto bad_format;
212 		      break;
213 		  }
214 		if (doit)
215 		  {
216 		    /* Temporarily replace e.g. "%3dnx" with "%3d\0x".  */
217 		    *speclim = 0;
218 		    printf_output (spec - 1, value);
219 		    /* Undo the temporary replacement.  */
220 		    *speclim = c;
221 		  }
222 	      }
223 	      continue;
224 
225 	    bad_format:
226 	      c = '%';
227 	      f = spec;
228 	      break;
229 	    }
230 	}
231       if (doit)
232 	{
233 	  /* Don't take the address of a register variable.  */
234 	  char cc = c;
235 	  write_output (&cc, 1);
236 	}
237     }
238   return f;
239 }
240 
241 /* For the line group pair G, return the number corresponding to LETTER.
242    Return -1 if LETTER is not a group format letter.  */
243 static int
groups_letter_value(g,letter)244 groups_letter_value (g, letter)
245      struct group const *g;
246      int letter;
247 {
248   if (ISUPPER (letter))
249     {
250       g++;
251       letter = tolower (letter);
252     }
253   switch (letter)
254     {
255       case 'e': return translate_line_number (g->file, g->from) - 1;
256       case 'f': return translate_line_number (g->file, g->from);
257       case 'l': return translate_line_number (g->file, g->upto) - 1;
258       case 'm': return translate_line_number (g->file, g->upto);
259       case 'n': return g->upto - g->from;
260       default: return -1;
261     }
262 }
263 
264 /* Output using FORMAT to print the line group GROUP.
265    But do nothing if DOIT is zero.  */
266 static void
print_ifdef_lines(doit,format,group)267 print_ifdef_lines (doit, format, group)
268      int doit;
269      char *format;
270      struct group const *group;
271 {
272   struct file_data const *file = group->file;
273   char const * const *linbuf = file->linbuf;
274   int from = group->from, upto = group->upto;
275 
276   if (!doit)
277     return;
278 
279   /* If possible, use a single fwrite; it's faster.  */
280   if (!tab_expand_flag && format[0] == '%')
281     {
282       if (format[1] == 'l' && format[2] == '\n' && !format[3])
283 	{
284 	  write_output (linbuf[from],
285 			(linbuf[upto] + (linbuf[upto][-1] != '\n')
286 			 - linbuf[from]));
287 	  return;
288 	}
289       if (format[1] == 'L' && !format[2])
290 	{
291 	  write_output (linbuf[from],
292 			linbuf[upto] -  linbuf[from]);
293 	  return;
294 	}
295     }
296 
297   for (;  from < upto;  from++)
298     {
299       register char c;
300       register char *f = format;
301       char cc;
302 
303       while ((c = *f++) != 0)
304 	{
305 	  if (c == '%')
306 	    {
307 	      char *spec = f;
308 	      switch ((c = *f++))
309 		{
310 		case '%':
311 		  break;
312 
313 		case 'l':
314 		  output_1_line (linbuf[from],
315 				 linbuf[from + 1]
316 				   - (linbuf[from + 1][-1] == '\n'), 0, 0);
317 		  continue;
318 
319 		case 'L':
320 		  output_1_line (linbuf[from], linbuf[from + 1], 0, 0);
321 		  continue;
322 
323 		default:
324 		  {
325 		    int value;
326 		    char *speclim;
327 
328 		    f = scan_printf_spec (spec);
329 		    if (!f)
330 		      goto bad_format;
331 		    speclim = f;
332 		    c = *f++;
333 		    switch (c)
334 		      {
335 			case '\'':
336 			  f = scan_char_literal (f, &value);
337 			  if (!f)
338 			    goto bad_format;
339 			  break;
340 
341 			case 'n':
342 			  value = translate_line_number (file, from);
343 			  break;
344 
345 			default:
346 			  goto bad_format;
347 		      }
348 		    /* Temporarily replace e.g. "%3dnx" with "%3d\0x".  */
349 		    *speclim = 0;
350 		    printf_output (spec - 1, value);
351 		    /* Undo the temporary replacement.  */
352 		    *speclim = c;
353 		  }
354 		  continue;
355 
356 		bad_format:
357 		  c = '%';
358 		  f = spec;
359 		  break;
360 		}
361 	    }
362 
363 	  /* Don't take the address of a register variable.  */
364 	  cc = c;
365 	  write_output (&cc, 1);
366 	}
367     }
368 }
369 
370 /* Scan the character literal represented in the string LIT; LIT points just
371    after the initial apostrophe.  Put the literal's value into *INTPTR.
372    Yield the address of the first character after the closing apostrophe,
373    or zero if the literal is ill-formed.  */
374 static char *
scan_char_literal(lit,intptr)375 scan_char_literal (lit, intptr)
376      char *lit;
377      int *intptr;
378 {
379   register char *p = lit;
380   int value, digits;
381   char c = *p++;
382 
383   switch (c)
384     {
385       case 0:
386       case '\'':
387 	return 0;
388 
389       case '\\':
390 	value = 0;
391 	while ((c = *p++) != '\'')
392 	  {
393 	    unsigned digit = c - '0';
394 	    if (8 <= digit)
395 	      return 0;
396 	    value = 8 * value + digit;
397 	  }
398 	digits = p - lit - 2;
399 	if (! (1 <= digits && digits <= 3))
400 	  return 0;
401 	break;
402 
403       default:
404 	value = c;
405 	if (*p++ != '\'')
406 	  return 0;
407 	break;
408     }
409   *intptr = value;
410   return p;
411 }
412 
413 /* Scan optional printf-style SPEC of the form `-*[0-9]*(.[0-9]*)?[cdoxX]'.
414    Return the address of the character following SPEC, or zero if failure.  */
415 static char *
scan_printf_spec(spec)416 scan_printf_spec (spec)
417      register char *spec;
418 {
419   register unsigned char c;
420 
421   while ((c = *spec++) == '-')
422     continue;
423   while (ISDIGIT (c))
424     c = *spec++;
425   if (c == '.')
426     while (ISDIGIT (c = *spec++))
427       continue;
428   switch (c)
429     {
430       case 'c': case 'd': case 'o': case 'x': case 'X':
431 	return spec;
432 
433       default:
434 	return 0;
435     }
436 }
437