xref: /freebsd/contrib/diff/src/ifdef.c (revision 18fd37a72c3a7549d2d4f6c6ea00bdcd2bdaca01)
118fd37a7SXin LI /* #ifdef-format output routines for GNU DIFF.
218fd37a7SXin LI 
318fd37a7SXin LI    Copyright (C) 1989, 1991, 1992, 1993, 1994, 2001, 2002, 2004 Free
418fd37a7SXin LI    Software Foundation, Inc.
518fd37a7SXin LI 
618fd37a7SXin LI    This file is part of GNU DIFF.
718fd37a7SXin LI 
818fd37a7SXin LI    GNU DIFF is distributed in the hope that it will be useful,
918fd37a7SXin LI    but WITHOUT ANY WARRANTY.  No author or distributor
1018fd37a7SXin LI    accepts responsibility to anyone for the consequences of using it
1118fd37a7SXin LI    or for whether it serves any particular purpose or works at all,
1218fd37a7SXin LI    unless he says so in writing.  Refer to the GNU DIFF General Public
1318fd37a7SXin LI    License for full details.
1418fd37a7SXin LI 
1518fd37a7SXin LI    Everyone is granted permission to copy, modify and redistribute
1618fd37a7SXin LI    GNU DIFF, but only under the conditions described in the
1718fd37a7SXin LI    GNU DIFF General Public License.   A copy of this license is
1818fd37a7SXin LI    supposed to have been given to you along with GNU DIFF so you
1918fd37a7SXin LI    can know your rights and responsibilities.  It should be in a
2018fd37a7SXin LI    file named COPYING.  Among other things, the copyright notice
2118fd37a7SXin LI    and this notice must be preserved on all copies.  */
2218fd37a7SXin LI 
2318fd37a7SXin LI #include "diff.h"
2418fd37a7SXin LI 
2518fd37a7SXin LI #include <xalloc.h>
2618fd37a7SXin LI 
2718fd37a7SXin LI struct group
2818fd37a7SXin LI {
2918fd37a7SXin LI   struct file_data const *file;
3018fd37a7SXin LI   lin from, upto; /* start and limit lines for this group of lines */
3118fd37a7SXin LI };
3218fd37a7SXin LI 
3318fd37a7SXin LI static char const *format_group (FILE *, char const *, char,
3418fd37a7SXin LI 				 struct group const *);
3518fd37a7SXin LI static char const *do_printf_spec (FILE *, char const *,
3618fd37a7SXin LI 				   struct file_data const *, lin,
3718fd37a7SXin LI 				   struct group const *);
3818fd37a7SXin LI static char const *scan_char_literal (char const *, char *);
3918fd37a7SXin LI static lin groups_letter_value (struct group const *, char);
4018fd37a7SXin LI static void format_ifdef (char const *, lin, lin, lin, lin);
4118fd37a7SXin LI static void print_ifdef_hunk (struct change *);
4218fd37a7SXin LI static void print_ifdef_lines (FILE *, char const *, struct group const *);
4318fd37a7SXin LI 
4418fd37a7SXin LI static lin next_line0;
4518fd37a7SXin LI static lin next_line1;
4618fd37a7SXin LI 
4718fd37a7SXin LI /* Print the edit-script SCRIPT as a merged #ifdef file.  */
4818fd37a7SXin LI 
4918fd37a7SXin LI void
print_ifdef_script(struct change * script)5018fd37a7SXin LI print_ifdef_script (struct change *script)
5118fd37a7SXin LI {
5218fd37a7SXin LI   next_line0 = next_line1 = - files[0].prefix_lines;
5318fd37a7SXin LI   print_script (script, find_change, print_ifdef_hunk);
5418fd37a7SXin LI   if (next_line0 < files[0].valid_lines
5518fd37a7SXin LI       || next_line1 < files[1].valid_lines)
5618fd37a7SXin LI     {
5718fd37a7SXin LI       begin_output ();
5818fd37a7SXin LI       format_ifdef (group_format[UNCHANGED],
5918fd37a7SXin LI 		    next_line0, files[0].valid_lines,
6018fd37a7SXin LI 		    next_line1, files[1].valid_lines);
6118fd37a7SXin LI     }
6218fd37a7SXin LI }
6318fd37a7SXin LI 
6418fd37a7SXin LI /* Print a hunk of an ifdef diff.
6518fd37a7SXin LI    This is a contiguous portion of a complete edit script,
6618fd37a7SXin LI    describing changes in consecutive lines.  */
6718fd37a7SXin LI 
6818fd37a7SXin LI static void
print_ifdef_hunk(struct change * hunk)6918fd37a7SXin LI print_ifdef_hunk (struct change *hunk)
7018fd37a7SXin LI {
7118fd37a7SXin LI   lin first0, last0, first1, last1;
7218fd37a7SXin LI 
7318fd37a7SXin LI   /* Determine range of line numbers involved in each file.  */
7418fd37a7SXin LI   enum changes changes = analyze_hunk (hunk, &first0, &last0, &first1, &last1);
7518fd37a7SXin LI   if (!changes)
7618fd37a7SXin LI     return;
7718fd37a7SXin LI 
7818fd37a7SXin LI   begin_output ();
7918fd37a7SXin LI 
8018fd37a7SXin LI   /* Print lines up to this change.  */
8118fd37a7SXin LI   if (next_line0 < first0 || next_line1 < first1)
8218fd37a7SXin LI     format_ifdef (group_format[UNCHANGED],
8318fd37a7SXin LI 		  next_line0, first0,
8418fd37a7SXin LI 		  next_line1, first1);
8518fd37a7SXin LI 
8618fd37a7SXin LI   /* Print this change.  */
8718fd37a7SXin LI   next_line0 = last0 + 1;
8818fd37a7SXin LI   next_line1 = last1 + 1;
8918fd37a7SXin LI   format_ifdef (group_format[changes],
9018fd37a7SXin LI 		first0, next_line0,
9118fd37a7SXin LI 		first1, next_line1);
9218fd37a7SXin LI }
9318fd37a7SXin LI 
9418fd37a7SXin LI /* Print a set of lines according to FORMAT.
9518fd37a7SXin LI    Lines BEG0 up to END0 are from the first file;
9618fd37a7SXin LI    lines BEG1 up to END1 are from the second file.  */
9718fd37a7SXin LI 
9818fd37a7SXin LI static void
format_ifdef(char const * format,lin beg0,lin end0,lin beg1,lin end1)9918fd37a7SXin LI format_ifdef (char const *format, lin beg0, lin end0, lin beg1, lin end1)
10018fd37a7SXin LI {
10118fd37a7SXin LI   struct group groups[2];
10218fd37a7SXin LI 
10318fd37a7SXin LI   groups[0].file = &files[0];
10418fd37a7SXin LI   groups[0].from = beg0;
10518fd37a7SXin LI   groups[0].upto = end0;
10618fd37a7SXin LI   groups[1].file = &files[1];
10718fd37a7SXin LI   groups[1].from = beg1;
10818fd37a7SXin LI   groups[1].upto = end1;
10918fd37a7SXin LI   format_group (outfile, format, 0, groups);
11018fd37a7SXin LI }
11118fd37a7SXin LI 
11218fd37a7SXin LI /* Print to file OUT a set of lines according to FORMAT.
11318fd37a7SXin LI    The format ends at the first free instance of ENDCHAR.
11418fd37a7SXin LI    Yield the address of the terminating character.
11518fd37a7SXin LI    GROUPS specifies which lines to print.
11618fd37a7SXin LI    If OUT is zero, do not actually print anything; just scan the format.  */
11718fd37a7SXin LI 
11818fd37a7SXin LI static char const *
format_group(register FILE * out,char const * format,char endchar,struct group const * groups)11918fd37a7SXin LI format_group (register FILE *out, char const *format, char endchar,
12018fd37a7SXin LI 	      struct group const *groups)
12118fd37a7SXin LI {
12218fd37a7SXin LI   register char c;
12318fd37a7SXin LI   register char const *f = format;
12418fd37a7SXin LI 
12518fd37a7SXin LI   while ((c = *f) != endchar && c != 0)
12618fd37a7SXin LI     {
12718fd37a7SXin LI       char const *f1 = ++f;
12818fd37a7SXin LI       if (c == '%')
12918fd37a7SXin LI 	switch ((c = *f++))
13018fd37a7SXin LI 	  {
13118fd37a7SXin LI 	  case '%':
13218fd37a7SXin LI 	    break;
13318fd37a7SXin LI 
13418fd37a7SXin LI 	  case '(':
13518fd37a7SXin LI 	    /* Print if-then-else format e.g. `%(n=1?thenpart:elsepart)'.  */
13618fd37a7SXin LI 	    {
13718fd37a7SXin LI 	      int i;
13818fd37a7SXin LI 	      uintmax_t value[2];
13918fd37a7SXin LI 	      FILE *thenout, *elseout;
14018fd37a7SXin LI 
14118fd37a7SXin LI 	      for (i = 0; i < 2; i++)
14218fd37a7SXin LI 		{
14318fd37a7SXin LI 		  if (ISDIGIT (*f))
14418fd37a7SXin LI 		    {
14518fd37a7SXin LI 		      char *fend;
14618fd37a7SXin LI 		      errno = 0;
14718fd37a7SXin LI 		      value[i] = strtoumax (f, &fend, 10);
14818fd37a7SXin LI 		      if (errno)
14918fd37a7SXin LI 			goto bad_format;
15018fd37a7SXin LI 		      f = fend;
15118fd37a7SXin LI 		    }
15218fd37a7SXin LI 		  else
15318fd37a7SXin LI 		    {
15418fd37a7SXin LI 		      value[i] = groups_letter_value (groups, *f);
15518fd37a7SXin LI 		      if (value[i] == -1)
15618fd37a7SXin LI 			goto bad_format;
15718fd37a7SXin LI 		      f++;
15818fd37a7SXin LI 		    }
15918fd37a7SXin LI 		  if (*f++ != "=?"[i])
16018fd37a7SXin LI 		    goto bad_format;
16118fd37a7SXin LI 		}
16218fd37a7SXin LI 	      if (value[0] == value[1])
16318fd37a7SXin LI 		thenout = out, elseout = 0;
16418fd37a7SXin LI 	      else
16518fd37a7SXin LI 		thenout = 0, elseout = out;
16618fd37a7SXin LI 	      f = format_group (thenout, f, ':', groups);
16718fd37a7SXin LI 	      if (*f)
16818fd37a7SXin LI 		{
16918fd37a7SXin LI 		  f = format_group (elseout, f + 1, ')', groups);
17018fd37a7SXin LI 		  if (*f)
17118fd37a7SXin LI 		    f++;
17218fd37a7SXin LI 		}
17318fd37a7SXin LI 	    }
17418fd37a7SXin LI 	    continue;
17518fd37a7SXin LI 
17618fd37a7SXin LI 	  case '<':
17718fd37a7SXin LI 	    /* Print lines deleted from first file.  */
17818fd37a7SXin LI 	    print_ifdef_lines (out, line_format[OLD], &groups[0]);
17918fd37a7SXin LI 	    continue;
18018fd37a7SXin LI 
18118fd37a7SXin LI 	  case '=':
18218fd37a7SXin LI 	    /* Print common lines.  */
18318fd37a7SXin LI 	    print_ifdef_lines (out, line_format[UNCHANGED], &groups[0]);
18418fd37a7SXin LI 	    continue;
18518fd37a7SXin LI 
18618fd37a7SXin LI 	  case '>':
18718fd37a7SXin LI 	    /* Print lines inserted from second file.  */
18818fd37a7SXin LI 	    print_ifdef_lines (out, line_format[NEW], &groups[1]);
18918fd37a7SXin LI 	    continue;
19018fd37a7SXin LI 
19118fd37a7SXin LI 	  default:
19218fd37a7SXin LI 	    f = do_printf_spec (out, f - 2, 0, 0, groups);
19318fd37a7SXin LI 	    if (f)
19418fd37a7SXin LI 	      continue;
19518fd37a7SXin LI 	    /* Fall through. */
19618fd37a7SXin LI 	  bad_format:
19718fd37a7SXin LI 	    c = '%';
19818fd37a7SXin LI 	    f = f1;
19918fd37a7SXin LI 	    break;
20018fd37a7SXin LI 	  }
20118fd37a7SXin LI 
20218fd37a7SXin LI       if (out)
20318fd37a7SXin LI 	putc (c, out);
20418fd37a7SXin LI     }
20518fd37a7SXin LI 
20618fd37a7SXin LI   return f;
20718fd37a7SXin LI }
20818fd37a7SXin LI 
20918fd37a7SXin LI /* For the line group pair G, return the number corresponding to LETTER.
21018fd37a7SXin LI    Return -1 if LETTER is not a group format letter.  */
21118fd37a7SXin LI static lin
groups_letter_value(struct group const * g,char letter)21218fd37a7SXin LI groups_letter_value (struct group const *g, char letter)
21318fd37a7SXin LI {
21418fd37a7SXin LI   switch (letter)
21518fd37a7SXin LI     {
21618fd37a7SXin LI     case 'E': letter = 'e'; g++; break;
21718fd37a7SXin LI     case 'F': letter = 'f'; g++; break;
21818fd37a7SXin LI     case 'L': letter = 'l'; g++; break;
21918fd37a7SXin LI     case 'M': letter = 'm'; g++; break;
22018fd37a7SXin LI     case 'N': letter = 'n'; g++; break;
22118fd37a7SXin LI     }
22218fd37a7SXin LI 
22318fd37a7SXin LI   switch (letter)
22418fd37a7SXin LI     {
22518fd37a7SXin LI       case 'e': return translate_line_number (g->file, g->from) - 1;
22618fd37a7SXin LI       case 'f': return translate_line_number (g->file, g->from);
22718fd37a7SXin LI       case 'l': return translate_line_number (g->file, g->upto) - 1;
22818fd37a7SXin LI       case 'm': return translate_line_number (g->file, g->upto);
22918fd37a7SXin LI       case 'n': return g->upto - g->from;
23018fd37a7SXin LI       default: return -1;
23118fd37a7SXin LI     }
23218fd37a7SXin LI }
23318fd37a7SXin LI 
23418fd37a7SXin LI /* Print to file OUT, using FORMAT to print the line group GROUP.
23518fd37a7SXin LI    But do nothing if OUT is zero.  */
23618fd37a7SXin LI static void
print_ifdef_lines(register FILE * out,char const * format,struct group const * group)23718fd37a7SXin LI print_ifdef_lines (register FILE *out, char const *format,
23818fd37a7SXin LI 		   struct group const *group)
23918fd37a7SXin LI {
24018fd37a7SXin LI   struct file_data const *file = group->file;
24118fd37a7SXin LI   char const * const *linbuf = file->linbuf;
24218fd37a7SXin LI   lin from = group->from, upto = group->upto;
24318fd37a7SXin LI 
24418fd37a7SXin LI   if (!out)
24518fd37a7SXin LI     return;
24618fd37a7SXin LI 
24718fd37a7SXin LI   /* If possible, use a single fwrite; it's faster.  */
24818fd37a7SXin LI   if (!expand_tabs && format[0] == '%')
24918fd37a7SXin LI     {
25018fd37a7SXin LI       if (format[1] == 'l' && format[2] == '\n' && !format[3] && from < upto)
25118fd37a7SXin LI 	{
25218fd37a7SXin LI 	  fwrite (linbuf[from], sizeof (char),
25318fd37a7SXin LI 		  linbuf[upto] + (linbuf[upto][-1] != '\n') -  linbuf[from],
25418fd37a7SXin LI 		  out);
25518fd37a7SXin LI 	  return;
25618fd37a7SXin LI 	}
25718fd37a7SXin LI       if (format[1] == 'L' && !format[2])
25818fd37a7SXin LI 	{
25918fd37a7SXin LI 	  fwrite (linbuf[from], sizeof (char),
26018fd37a7SXin LI 		  linbuf[upto] -  linbuf[from], out);
26118fd37a7SXin LI 	  return;
26218fd37a7SXin LI 	}
26318fd37a7SXin LI     }
26418fd37a7SXin LI 
26518fd37a7SXin LI   for (;  from < upto;  from++)
26618fd37a7SXin LI     {
26718fd37a7SXin LI       register char c;
26818fd37a7SXin LI       register char const *f = format;
26918fd37a7SXin LI 
27018fd37a7SXin LI       while ((c = *f++) != 0)
27118fd37a7SXin LI 	{
27218fd37a7SXin LI 	  char const *f1 = f;
27318fd37a7SXin LI 	  if (c == '%')
27418fd37a7SXin LI 	    switch ((c = *f++))
27518fd37a7SXin LI 	      {
27618fd37a7SXin LI 	      case '%':
27718fd37a7SXin LI 		break;
27818fd37a7SXin LI 
27918fd37a7SXin LI 	      case 'l':
28018fd37a7SXin LI 		output_1_line (linbuf[from],
28118fd37a7SXin LI 			       (linbuf[from + 1]
28218fd37a7SXin LI 				- (linbuf[from + 1][-1] == '\n')),
28318fd37a7SXin LI 			       0, 0);
28418fd37a7SXin LI 		continue;
28518fd37a7SXin LI 
28618fd37a7SXin LI 	      case 'L':
28718fd37a7SXin LI 		output_1_line (linbuf[from], linbuf[from + 1], 0, 0);
28818fd37a7SXin LI 		continue;
28918fd37a7SXin LI 
29018fd37a7SXin LI 	      default:
29118fd37a7SXin LI 		f = do_printf_spec (out, f - 2, file, from, 0);
29218fd37a7SXin LI 		if (f)
29318fd37a7SXin LI 		  continue;
29418fd37a7SXin LI 		c = '%';
29518fd37a7SXin LI 		f = f1;
29618fd37a7SXin LI 		break;
29718fd37a7SXin LI 	      }
29818fd37a7SXin LI 
29918fd37a7SXin LI 	  putc (c, out);
30018fd37a7SXin LI 	}
30118fd37a7SXin LI     }
30218fd37a7SXin LI }
30318fd37a7SXin LI 
30418fd37a7SXin LI static char const *
do_printf_spec(FILE * out,char const * spec,struct file_data const * file,lin n,struct group const * groups)30518fd37a7SXin LI do_printf_spec (FILE *out, char const *spec,
30618fd37a7SXin LI 		struct file_data const *file, lin n,
30718fd37a7SXin LI 		struct group const *groups)
30818fd37a7SXin LI {
30918fd37a7SXin LI   char const *f = spec;
31018fd37a7SXin LI   char c;
31118fd37a7SXin LI   char c1;
31218fd37a7SXin LI 
31318fd37a7SXin LI   /* Scan printf-style SPEC of the form %[-'0]*[0-9]*(.[0-9]*)?[cdoxX].  */
31418fd37a7SXin LI   /* assert (*f == '%'); */
31518fd37a7SXin LI   f++;
31618fd37a7SXin LI   while ((c = *f++) == '-' || c == '\'' || c == '0')
31718fd37a7SXin LI     continue;
31818fd37a7SXin LI   while (ISDIGIT (c))
31918fd37a7SXin LI     c = *f++;
32018fd37a7SXin LI   if (c == '.')
32118fd37a7SXin LI     while (ISDIGIT (c = *f++))
32218fd37a7SXin LI       continue;
32318fd37a7SXin LI   c1 = *f++;
32418fd37a7SXin LI 
32518fd37a7SXin LI   switch (c)
32618fd37a7SXin LI     {
32718fd37a7SXin LI     case 'c':
32818fd37a7SXin LI       if (c1 != '\'')
32918fd37a7SXin LI 	return 0;
33018fd37a7SXin LI       else
33118fd37a7SXin LI 	{
33218fd37a7SXin LI 	  char value;
33318fd37a7SXin LI 	  f = scan_char_literal (f, &value);
33418fd37a7SXin LI 	  if (!f)
33518fd37a7SXin LI 	    return 0;
33618fd37a7SXin LI 	  if (out)
33718fd37a7SXin LI 	    putc (value, out);
33818fd37a7SXin LI 	}
33918fd37a7SXin LI       break;
34018fd37a7SXin LI 
34118fd37a7SXin LI     case 'd': case 'o': case 'x': case 'X':
34218fd37a7SXin LI       {
34318fd37a7SXin LI 	lin value;
34418fd37a7SXin LI 
34518fd37a7SXin LI 	if (file)
34618fd37a7SXin LI 	  {
34718fd37a7SXin LI 	    if (c1 != 'n')
34818fd37a7SXin LI 	      return 0;
34918fd37a7SXin LI 	    value = translate_line_number (file, n);
35018fd37a7SXin LI 	  }
35118fd37a7SXin LI 	else
35218fd37a7SXin LI 	  {
35318fd37a7SXin LI 	    value = groups_letter_value (groups, c1);
35418fd37a7SXin LI 	    if (value < 0)
35518fd37a7SXin LI 	      return 0;
35618fd37a7SXin LI 	  }
35718fd37a7SXin LI 
35818fd37a7SXin LI 	if (out)
35918fd37a7SXin LI 	  {
36018fd37a7SXin LI 	    /* For example, if the spec is "%3xn", use the printf
36118fd37a7SXin LI 	       format spec "%3lx".  Here the spec prefix is "%3".  */
36218fd37a7SXin LI 	    long int long_value = value;
36318fd37a7SXin LI 	    size_t spec_prefix_len = f - spec - 2;
36418fd37a7SXin LI #if HAVE_C_VARARRAYS
36518fd37a7SXin LI 	    char format[spec_prefix_len + 3];
36618fd37a7SXin LI #else
36718fd37a7SXin LI 	    char *format = xmalloc (spec_prefix_len + 3);
36818fd37a7SXin LI #endif
36918fd37a7SXin LI 	    char *p = format + spec_prefix_len;
37018fd37a7SXin LI 	    memcpy (format, spec, spec_prefix_len);
37118fd37a7SXin LI 	    *p++ = 'l';
37218fd37a7SXin LI 	    *p++ = c;
37318fd37a7SXin LI 	    *p = '\0';
37418fd37a7SXin LI 	    fprintf (out, format, long_value);
37518fd37a7SXin LI #if ! HAVE_C_VARARRAYS
37618fd37a7SXin LI 	    free (format);
37718fd37a7SXin LI #endif
37818fd37a7SXin LI 	  }
37918fd37a7SXin LI       }
38018fd37a7SXin LI       break;
38118fd37a7SXin LI 
38218fd37a7SXin LI     default:
38318fd37a7SXin LI       return 0;
38418fd37a7SXin LI     }
38518fd37a7SXin LI 
38618fd37a7SXin LI   return f;
38718fd37a7SXin LI }
38818fd37a7SXin LI 
38918fd37a7SXin LI /* Scan the character literal represented in the string LIT; LIT points just
39018fd37a7SXin LI    after the initial apostrophe.  Put the literal's value into *VALPTR.
39118fd37a7SXin LI    Yield the address of the first character after the closing apostrophe,
39218fd37a7SXin LI    or zero if the literal is ill-formed.  */
39318fd37a7SXin LI static char const *
scan_char_literal(char const * lit,char * valptr)39418fd37a7SXin LI scan_char_literal (char const *lit, char *valptr)
39518fd37a7SXin LI {
39618fd37a7SXin LI   register char const *p = lit;
39718fd37a7SXin LI   char value;
39818fd37a7SXin LI   ptrdiff_t digits;
39918fd37a7SXin LI   char c = *p++;
40018fd37a7SXin LI 
40118fd37a7SXin LI   switch (c)
40218fd37a7SXin LI     {
40318fd37a7SXin LI       case 0:
40418fd37a7SXin LI       case '\'':
40518fd37a7SXin LI 	return 0;
40618fd37a7SXin LI 
40718fd37a7SXin LI       case '\\':
40818fd37a7SXin LI 	value = 0;
40918fd37a7SXin LI 	while ((c = *p++) != '\'')
41018fd37a7SXin LI 	  {
41118fd37a7SXin LI 	    unsigned int digit = c - '0';
41218fd37a7SXin LI 	    if (8 <= digit)
41318fd37a7SXin LI 	      return 0;
41418fd37a7SXin LI 	    value = 8 * value + digit;
41518fd37a7SXin LI 	  }
41618fd37a7SXin LI 	digits = p - lit - 2;
41718fd37a7SXin LI 	if (! (1 <= digits && digits <= 3))
41818fd37a7SXin LI 	  return 0;
41918fd37a7SXin LI 	break;
42018fd37a7SXin LI 
42118fd37a7SXin LI       default:
42218fd37a7SXin LI 	value = c;
42318fd37a7SXin LI 	if (*p++ != '\'')
42418fd37a7SXin LI 	  return 0;
42518fd37a7SXin LI 	break;
42618fd37a7SXin LI     }
42718fd37a7SXin LI 
42818fd37a7SXin LI   *valptr = value;
42918fd37a7SXin LI   return p;
43018fd37a7SXin LI }
431