xref: /openbsd-src/gnu/usr.bin/cvs/diff/ifdef.c (revision b2346922a76a50a89e33beab4ebbc0950de8a8df)
12286d8edStholo /* #ifdef-format output routines for GNU DIFF.
2*b2346922Stholo    Copyright (C) 1989, 1991, 1992, 1993, 1994, 1998 Free Software Foundation, Inc.
32286d8edStholo 
42286d8edStholo This file is part of GNU DIFF.
52286d8edStholo 
62286d8edStholo GNU DIFF is distributed in the hope that it will be useful,
72286d8edStholo but WITHOUT ANY WARRANTY.  No author or distributor
82286d8edStholo accepts responsibility to anyone for the consequences of using it
92286d8edStholo or for whether it serves any particular purpose or works at all,
102286d8edStholo unless he says so in writing.  Refer to the GNU DIFF General Public
112286d8edStholo License for full details.
122286d8edStholo 
132286d8edStholo Everyone is granted permission to copy, modify and redistribute
142286d8edStholo GNU DIFF, but only under the conditions described in the
152286d8edStholo GNU DIFF General Public License.   A copy of this license is
162286d8edStholo supposed to have been given to you along with GNU DIFF so you
172286d8edStholo can know your rights and responsibilities.  It should be in a
182286d8edStholo file named COPYING.  Among other things, the copyright notice
192286d8edStholo and this notice must be preserved on all copies.  */
202286d8edStholo 
212286d8edStholo 
222286d8edStholo #include "diff.h"
232286d8edStholo 
242286d8edStholo struct group
252286d8edStholo {
262286d8edStholo   struct file_data const *file;
272286d8edStholo   int from, upto; /* start and limit lines for this group of lines */
282286d8edStholo };
292286d8edStholo 
30*b2346922Stholo static char *format_group PARAMS((int, char *, int, struct group const *));
312286d8edStholo static char *scan_char_literal PARAMS((char *, int *));
322286d8edStholo static char *scan_printf_spec PARAMS((char *));
332286d8edStholo static int groups_letter_value PARAMS((struct group const *, int));
342286d8edStholo static void format_ifdef PARAMS((char *, int, int, int, int));
352286d8edStholo static void print_ifdef_hunk PARAMS((struct change *));
36*b2346922Stholo static void print_ifdef_lines PARAMS((int, char *, struct group const *));
372286d8edStholo 
382286d8edStholo static int next_line;
392286d8edStholo 
402286d8edStholo /* Print the edit-script SCRIPT as a merged #ifdef file.  */
412286d8edStholo 
422286d8edStholo void
print_ifdef_script(script)432286d8edStholo print_ifdef_script (script)
442286d8edStholo      struct change *script;
452286d8edStholo {
462286d8edStholo   next_line = - files[0].prefix_lines;
472286d8edStholo   print_script (script, find_change, print_ifdef_hunk);
482286d8edStholo   if (next_line < files[0].valid_lines)
492286d8edStholo     {
502286d8edStholo       begin_output ();
512286d8edStholo       format_ifdef (group_format[UNCHANGED], next_line, files[0].valid_lines,
522286d8edStholo 		    next_line - files[0].valid_lines + files[1].valid_lines,
532286d8edStholo 		    files[1].valid_lines);
542286d8edStholo     }
552286d8edStholo }
562286d8edStholo 
572286d8edStholo /* Print a hunk of an ifdef diff.
582286d8edStholo    This is a contiguous portion of a complete edit script,
592286d8edStholo    describing changes in consecutive lines.  */
602286d8edStholo 
612286d8edStholo static void
print_ifdef_hunk(hunk)622286d8edStholo print_ifdef_hunk (hunk)
632286d8edStholo      struct change *hunk;
642286d8edStholo {
652286d8edStholo   int first0, last0, first1, last1, deletes, inserts;
662286d8edStholo   char *format;
672286d8edStholo 
682286d8edStholo   /* Determine range of line numbers involved in each file.  */
692286d8edStholo   analyze_hunk (hunk, &first0, &last0, &first1, &last1, &deletes, &inserts);
702286d8edStholo   if (inserts)
712286d8edStholo     format = deletes ? group_format[CHANGED] : group_format[NEW];
722286d8edStholo   else if (deletes)
732286d8edStholo     format = group_format[OLD];
742286d8edStholo   else
752286d8edStholo     return;
762286d8edStholo 
772286d8edStholo   begin_output ();
782286d8edStholo 
792286d8edStholo   /* Print lines up to this change.  */
802286d8edStholo   if (next_line < first0)
812286d8edStholo     format_ifdef (group_format[UNCHANGED], next_line, first0,
822286d8edStholo 		  next_line - first0 + first1, first1);
832286d8edStholo 
842286d8edStholo   /* Print this change.  */
852286d8edStholo   next_line = last0 + 1;
862286d8edStholo   format_ifdef (format, first0, next_line, first1, last1 + 1);
872286d8edStholo }
882286d8edStholo 
892286d8edStholo /* Print a set of lines according to FORMAT.
902286d8edStholo    Lines BEG0 up to END0 are from the first file;
912286d8edStholo    lines BEG1 up to END1 are from the second file.  */
922286d8edStholo 
932286d8edStholo static void
format_ifdef(format,beg0,end0,beg1,end1)942286d8edStholo format_ifdef (format, beg0, end0, beg1, end1)
952286d8edStholo      char *format;
962286d8edStholo      int beg0, end0, beg1, end1;
972286d8edStholo {
982286d8edStholo   struct group groups[2];
992286d8edStholo 
1002286d8edStholo   groups[0].file = &files[0];
1012286d8edStholo   groups[0].from = beg0;
1022286d8edStholo   groups[0].upto = end0;
1032286d8edStholo   groups[1].file = &files[1];
1042286d8edStholo   groups[1].from = beg1;
1052286d8edStholo   groups[1].upto = end1;
106*b2346922Stholo   format_group (1, format, '\0', groups);
1072286d8edStholo }
1082286d8edStholo 
109*b2346922Stholo /* If DOIT is non-zero, output a set of lines according to FORMAT.
1102286d8edStholo    The format ends at the first free instance of ENDCHAR.
1112286d8edStholo    Yield the address of the terminating character.
1122286d8edStholo    GROUPS specifies which lines to print.
1132286d8edStholo    If OUT is zero, do not actually print anything; just scan the format.  */
1142286d8edStholo 
1152286d8edStholo static char *
format_group(doit,format,endchar,groups)116*b2346922Stholo format_group (doit, format, endchar, groups)
117*b2346922Stholo      int doit;
1182286d8edStholo      char *format;
1192286d8edStholo      int endchar;
1202286d8edStholo      struct group const *groups;
1212286d8edStholo {
1222286d8edStholo   register char c;
1232286d8edStholo   register char *f = format;
1242286d8edStholo 
1252286d8edStholo   while ((c = *f) != endchar && c != 0)
1262286d8edStholo     {
1272286d8edStholo       f++;
1282286d8edStholo       if (c == '%')
1292286d8edStholo 	{
1302286d8edStholo 	  char *spec = f;
1312286d8edStholo 	  switch ((c = *f++))
1322286d8edStholo 	    {
1332286d8edStholo 	    case '%':
1342286d8edStholo 	      break;
1352286d8edStholo 
1362286d8edStholo 	    case '(':
1372286d8edStholo 	      /* Print if-then-else format e.g. `%(n=1?thenpart:elsepart)'.  */
1382286d8edStholo 	      {
1392286d8edStholo 		int i, value[2];
140*b2346922Stholo 		int thendoit, elsedoit;
1412286d8edStholo 
1422286d8edStholo 		for (i = 0; i < 2; i++)
1432286d8edStholo 		  {
1442286d8edStholo 		    unsigned char f0 = f[0];
1452286d8edStholo 		    if (ISDIGIT (f0))
1462286d8edStholo 		      {
1472286d8edStholo 			value[i] = atoi (f);
1482286d8edStholo 			while (ISDIGIT ((unsigned char) *++f))
1492286d8edStholo 			  continue;
1502286d8edStholo 		      }
1512286d8edStholo 		    else
1522286d8edStholo 		      {
1532286d8edStholo 			value[i] = groups_letter_value (groups, f0);
1542286d8edStholo 			if (value[i] < 0)
1552286d8edStholo 			  goto bad_format;
1562286d8edStholo 			f++;
1572286d8edStholo 		      }
1582286d8edStholo 		    if (*f++ != "=?"[i])
1592286d8edStholo 		      goto bad_format;
1602286d8edStholo 		  }
1612286d8edStholo 		if (value[0] == value[1])
162*b2346922Stholo 		  thendoit = doit, elsedoit = 0;
1632286d8edStholo 		else
164*b2346922Stholo 		  thendoit = 0, elsedoit = doit;
165*b2346922Stholo 		f = format_group (thendoit, f, ':', groups);
1662286d8edStholo 		if (*f)
1672286d8edStholo 		  {
168*b2346922Stholo 		    f = format_group (elsedoit, f + 1, ')', groups);
1692286d8edStholo 		    if (*f)
1702286d8edStholo 		      f++;
1712286d8edStholo 		  }
1722286d8edStholo 	      }
1732286d8edStholo 	      continue;
1742286d8edStholo 
1752286d8edStholo 	    case '<':
1762286d8edStholo 	      /* Print lines deleted from first file.  */
177*b2346922Stholo 	      print_ifdef_lines (doit, line_format[OLD], &groups[0]);
1782286d8edStholo 	      continue;
1792286d8edStholo 
1802286d8edStholo 	    case '=':
1812286d8edStholo 	      /* Print common lines.  */
182*b2346922Stholo 	      print_ifdef_lines (doit, line_format[UNCHANGED], &groups[0]);
1832286d8edStholo 	      continue;
1842286d8edStholo 
1852286d8edStholo 	    case '>':
1862286d8edStholo 	      /* Print lines inserted from second file.  */
187*b2346922Stholo 	      print_ifdef_lines (doit, line_format[NEW], &groups[1]);
1882286d8edStholo 	      continue;
1892286d8edStholo 
1902286d8edStholo 	    default:
1912286d8edStholo 	      {
1922286d8edStholo 		int value;
1932286d8edStholo 		char *speclim;
1942286d8edStholo 
1952286d8edStholo 		f = scan_printf_spec (spec);
1962286d8edStholo 		if (!f)
1972286d8edStholo 		  goto bad_format;
1982286d8edStholo 		speclim = f;
1992286d8edStholo 		c = *f++;
2002286d8edStholo 		switch (c)
2012286d8edStholo 		  {
2022286d8edStholo 		    case '\'':
2032286d8edStholo 		      f = scan_char_literal (f, &value);
2042286d8edStholo 		      if (!f)
2052286d8edStholo 			goto bad_format;
2062286d8edStholo 		      break;
2072286d8edStholo 
2082286d8edStholo 		    default:
2092286d8edStholo 		      value = groups_letter_value (groups, c);
2102286d8edStholo 		      if (value < 0)
2112286d8edStholo 			goto bad_format;
2122286d8edStholo 		      break;
2132286d8edStholo 		  }
214*b2346922Stholo 		if (doit)
2152286d8edStholo 		  {
2162286d8edStholo 		    /* Temporarily replace e.g. "%3dnx" with "%3d\0x".  */
2172286d8edStholo 		    *speclim = 0;
218*b2346922Stholo 		    printf_output (spec - 1, value);
2192286d8edStholo 		    /* Undo the temporary replacement.  */
2202286d8edStholo 		    *speclim = c;
2212286d8edStholo 		  }
2222286d8edStholo 	      }
2232286d8edStholo 	      continue;
2242286d8edStholo 
2252286d8edStholo 	    bad_format:
2262286d8edStholo 	      c = '%';
2272286d8edStholo 	      f = spec;
2282286d8edStholo 	      break;
2292286d8edStholo 	    }
2302286d8edStholo 	}
231*b2346922Stholo       if (doit)
232*b2346922Stholo 	{
233*b2346922Stholo 	  /* Don't take the address of a register variable.  */
234*b2346922Stholo 	  char cc = c;
235*b2346922Stholo 	  write_output (&cc, 1);
236*b2346922Stholo 	}
2372286d8edStholo     }
2382286d8edStholo   return f;
2392286d8edStholo }
2402286d8edStholo 
2412286d8edStholo /* For the line group pair G, return the number corresponding to LETTER.
2422286d8edStholo    Return -1 if LETTER is not a group format letter.  */
2432286d8edStholo static int
groups_letter_value(g,letter)2442286d8edStholo groups_letter_value (g, letter)
2452286d8edStholo      struct group const *g;
2462286d8edStholo      int letter;
2472286d8edStholo {
2482286d8edStholo   if (ISUPPER (letter))
2492286d8edStholo     {
2502286d8edStholo       g++;
2512286d8edStholo       letter = tolower (letter);
2522286d8edStholo     }
2532286d8edStholo   switch (letter)
2542286d8edStholo     {
2552286d8edStholo       case 'e': return translate_line_number (g->file, g->from) - 1;
2562286d8edStholo       case 'f': return translate_line_number (g->file, g->from);
2572286d8edStholo       case 'l': return translate_line_number (g->file, g->upto) - 1;
2582286d8edStholo       case 'm': return translate_line_number (g->file, g->upto);
2592286d8edStholo       case 'n': return g->upto - g->from;
2602286d8edStholo       default: return -1;
2612286d8edStholo     }
2622286d8edStholo }
2632286d8edStholo 
264*b2346922Stholo /* Output using FORMAT to print the line group GROUP.
265*b2346922Stholo    But do nothing if DOIT is zero.  */
2662286d8edStholo static void
print_ifdef_lines(doit,format,group)267*b2346922Stholo print_ifdef_lines (doit, format, group)
268*b2346922Stholo      int doit;
2692286d8edStholo      char *format;
2702286d8edStholo      struct group const *group;
2712286d8edStholo {
2722286d8edStholo   struct file_data const *file = group->file;
2732286d8edStholo   char const * const *linbuf = file->linbuf;
2742286d8edStholo   int from = group->from, upto = group->upto;
2752286d8edStholo 
276*b2346922Stholo   if (!doit)
2772286d8edStholo     return;
2782286d8edStholo 
2792286d8edStholo   /* If possible, use a single fwrite; it's faster.  */
2802286d8edStholo   if (!tab_expand_flag && format[0] == '%')
2812286d8edStholo     {
2822286d8edStholo       if (format[1] == 'l' && format[2] == '\n' && !format[3])
2832286d8edStholo 	{
284*b2346922Stholo 	  write_output (linbuf[from],
285*b2346922Stholo 			(linbuf[upto] + (linbuf[upto][-1] != '\n')
286*b2346922Stholo 			 - linbuf[from]));
2872286d8edStholo 	  return;
2882286d8edStholo 	}
2892286d8edStholo       if (format[1] == 'L' && !format[2])
2902286d8edStholo 	{
291*b2346922Stholo 	  write_output (linbuf[from],
292*b2346922Stholo 			linbuf[upto] -  linbuf[from]);
2932286d8edStholo 	  return;
2942286d8edStholo 	}
2952286d8edStholo     }
2962286d8edStholo 
2972286d8edStholo   for (;  from < upto;  from++)
2982286d8edStholo     {
2992286d8edStholo       register char c;
3002286d8edStholo       register char *f = format;
301*b2346922Stholo       char cc;
3022286d8edStholo 
3032286d8edStholo       while ((c = *f++) != 0)
3042286d8edStholo 	{
3052286d8edStholo 	  if (c == '%')
3062286d8edStholo 	    {
3072286d8edStholo 	      char *spec = f;
3082286d8edStholo 	      switch ((c = *f++))
3092286d8edStholo 		{
3102286d8edStholo 		case '%':
3112286d8edStholo 		  break;
3122286d8edStholo 
3132286d8edStholo 		case 'l':
3142286d8edStholo 		  output_1_line (linbuf[from],
3152286d8edStholo 				 linbuf[from + 1]
3162286d8edStholo 				   - (linbuf[from + 1][-1] == '\n'), 0, 0);
3172286d8edStholo 		  continue;
3182286d8edStholo 
3192286d8edStholo 		case 'L':
3202286d8edStholo 		  output_1_line (linbuf[from], linbuf[from + 1], 0, 0);
3212286d8edStholo 		  continue;
3222286d8edStholo 
3232286d8edStholo 		default:
3242286d8edStholo 		  {
3252286d8edStholo 		    int value;
3262286d8edStholo 		    char *speclim;
3272286d8edStholo 
3282286d8edStholo 		    f = scan_printf_spec (spec);
3292286d8edStholo 		    if (!f)
3302286d8edStholo 		      goto bad_format;
3312286d8edStholo 		    speclim = f;
3322286d8edStholo 		    c = *f++;
3332286d8edStholo 		    switch (c)
3342286d8edStholo 		      {
3352286d8edStholo 			case '\'':
3362286d8edStholo 			  f = scan_char_literal (f, &value);
3372286d8edStholo 			  if (!f)
3382286d8edStholo 			    goto bad_format;
3392286d8edStholo 			  break;
3402286d8edStholo 
3412286d8edStholo 			case 'n':
3422286d8edStholo 			  value = translate_line_number (file, from);
3432286d8edStholo 			  break;
3442286d8edStholo 
3452286d8edStholo 			default:
3462286d8edStholo 			  goto bad_format;
3472286d8edStholo 		      }
3482286d8edStholo 		    /* Temporarily replace e.g. "%3dnx" with "%3d\0x".  */
3492286d8edStholo 		    *speclim = 0;
350*b2346922Stholo 		    printf_output (spec - 1, value);
3512286d8edStholo 		    /* Undo the temporary replacement.  */
3522286d8edStholo 		    *speclim = c;
3532286d8edStholo 		  }
3542286d8edStholo 		  continue;
3552286d8edStholo 
3562286d8edStholo 		bad_format:
3572286d8edStholo 		  c = '%';
3582286d8edStholo 		  f = spec;
3592286d8edStholo 		  break;
3602286d8edStholo 		}
3612286d8edStholo 	    }
362*b2346922Stholo 
363*b2346922Stholo 	  /* Don't take the address of a register variable.  */
364*b2346922Stholo 	  cc = c;
365*b2346922Stholo 	  write_output (&cc, 1);
3662286d8edStholo 	}
3672286d8edStholo     }
3682286d8edStholo }
3692286d8edStholo 
3702286d8edStholo /* Scan the character literal represented in the string LIT; LIT points just
3712286d8edStholo    after the initial apostrophe.  Put the literal's value into *INTPTR.
3722286d8edStholo    Yield the address of the first character after the closing apostrophe,
3732286d8edStholo    or zero if the literal is ill-formed.  */
3742286d8edStholo static char *
scan_char_literal(lit,intptr)3752286d8edStholo scan_char_literal (lit, intptr)
3762286d8edStholo      char *lit;
3772286d8edStholo      int *intptr;
3782286d8edStholo {
3792286d8edStholo   register char *p = lit;
3802286d8edStholo   int value, digits;
3812286d8edStholo   char c = *p++;
3822286d8edStholo 
3832286d8edStholo   switch (c)
3842286d8edStholo     {
3852286d8edStholo       case 0:
3862286d8edStholo       case '\'':
3872286d8edStholo 	return 0;
3882286d8edStholo 
3892286d8edStholo       case '\\':
3902286d8edStholo 	value = 0;
3912286d8edStholo 	while ((c = *p++) != '\'')
3922286d8edStholo 	  {
3932286d8edStholo 	    unsigned digit = c - '0';
3942286d8edStholo 	    if (8 <= digit)
3952286d8edStholo 	      return 0;
3962286d8edStholo 	    value = 8 * value + digit;
3972286d8edStholo 	  }
3982286d8edStholo 	digits = p - lit - 2;
3992286d8edStholo 	if (! (1 <= digits && digits <= 3))
4002286d8edStholo 	  return 0;
4012286d8edStholo 	break;
4022286d8edStholo 
4032286d8edStholo       default:
4042286d8edStholo 	value = c;
4052286d8edStholo 	if (*p++ != '\'')
4062286d8edStholo 	  return 0;
4072286d8edStholo 	break;
4082286d8edStholo     }
4092286d8edStholo   *intptr = value;
4102286d8edStholo   return p;
4112286d8edStholo }
4122286d8edStholo 
4132286d8edStholo /* Scan optional printf-style SPEC of the form `-*[0-9]*(.[0-9]*)?[cdoxX]'.
4142286d8edStholo    Return the address of the character following SPEC, or zero if failure.  */
4152286d8edStholo static char *
scan_printf_spec(spec)4162286d8edStholo scan_printf_spec (spec)
4172286d8edStholo      register char *spec;
4182286d8edStholo {
4192286d8edStholo   register unsigned char c;
4202286d8edStholo 
4212286d8edStholo   while ((c = *spec++) == '-')
4222286d8edStholo     continue;
4232286d8edStholo   while (ISDIGIT (c))
4242286d8edStholo     c = *spec++;
4252286d8edStholo   if (c == '.')
4262286d8edStholo     while (ISDIGIT (c = *spec++))
4272286d8edStholo       continue;
4282286d8edStholo   switch (c)
4292286d8edStholo     {
4302286d8edStholo       case 'c': case 'd': case 'o': case 'x': case 'X':
4312286d8edStholo 	return spec;
4322286d8edStholo 
4332286d8edStholo       default:
4342286d8edStholo 	return 0;
4352286d8edStholo     }
4362286d8edStholo }
437