ifdef.c revision 32785
132785Speter/* #ifdef-format output routines for GNU DIFF.
232785Speter   Copyright (C) 1989, 1991, 1992, 1993, 1994 Free Software Foundation, Inc.
332785Speter
432785SpeterThis file is part of GNU DIFF.
532785Speter
632785SpeterGNU DIFF is distributed in the hope that it will be useful,
732785Speterbut WITHOUT ANY WARRANTY.  No author or distributor
832785Speteraccepts responsibility to anyone for the consequences of using it
932785Speteror for whether it serves any particular purpose or works at all,
1032785Speterunless he says so in writing.  Refer to the GNU DIFF General Public
1132785SpeterLicense for full details.
1232785Speter
1332785SpeterEveryone is granted permission to copy, modify and redistribute
1432785SpeterGNU DIFF, but only under the conditions described in the
1532785SpeterGNU DIFF General Public License.   A copy of this license is
1632785Spetersupposed to have been given to you along with GNU DIFF so you
1732785Spetercan know your rights and responsibilities.  It should be in a
1832785Speterfile named COPYING.  Among other things, the copyright notice
1932785Speterand this notice must be preserved on all copies.  */
2032785Speter
2132785Speter
2232785Speter#include "diff.h"
2332785Speter
2432785Speterstruct group
2532785Speter{
2632785Speter  struct file_data const *file;
2732785Speter  int from, upto; /* start and limit lines for this group of lines */
2832785Speter};
2932785Speter
3032785Speterstatic char *format_group PARAMS((FILE *, char *, int, struct group const *));
3132785Speterstatic char *scan_char_literal PARAMS((char *, int *));
3232785Speterstatic char *scan_printf_spec PARAMS((char *));
3332785Speterstatic int groups_letter_value PARAMS((struct group const *, int));
3432785Speterstatic void format_ifdef PARAMS((char *, int, int, int, int));
3532785Speterstatic void print_ifdef_hunk PARAMS((struct change *));
3632785Speterstatic void print_ifdef_lines PARAMS((FILE *, char *, struct group const *));
3732785Speter
3832785Speterstatic int next_line;
3932785Speter
4032785Speter/* Print the edit-script SCRIPT as a merged #ifdef file.  */
4132785Speter
4232785Spetervoid
4332785Speterprint_ifdef_script (script)
4432785Speter     struct change *script;
4532785Speter{
4632785Speter  next_line = - files[0].prefix_lines;
4732785Speter  print_script (script, find_change, print_ifdef_hunk);
4832785Speter  if (next_line < files[0].valid_lines)
4932785Speter    {
5032785Speter      begin_output ();
5132785Speter      format_ifdef (group_format[UNCHANGED], next_line, files[0].valid_lines,
5232785Speter		    next_line - files[0].valid_lines + files[1].valid_lines,
5332785Speter		    files[1].valid_lines);
5432785Speter    }
5532785Speter}
5632785Speter
5732785Speter/* Print a hunk of an ifdef diff.
5832785Speter   This is a contiguous portion of a complete edit script,
5932785Speter   describing changes in consecutive lines.  */
6032785Speter
6132785Speterstatic void
6232785Speterprint_ifdef_hunk (hunk)
6332785Speter     struct change *hunk;
6432785Speter{
6532785Speter  int first0, last0, first1, last1, deletes, inserts;
6632785Speter  char *format;
6732785Speter
6832785Speter  /* Determine range of line numbers involved in each file.  */
6932785Speter  analyze_hunk (hunk, &first0, &last0, &first1, &last1, &deletes, &inserts);
7032785Speter  if (inserts)
7132785Speter    format = deletes ? group_format[CHANGED] : group_format[NEW];
7232785Speter  else if (deletes)
7332785Speter    format = group_format[OLD];
7432785Speter  else
7532785Speter    return;
7632785Speter
7732785Speter  begin_output ();
7832785Speter
7932785Speter  /* Print lines up to this change.  */
8032785Speter  if (next_line < first0)
8132785Speter    format_ifdef (group_format[UNCHANGED], next_line, first0,
8232785Speter		  next_line - first0 + first1, first1);
8332785Speter
8432785Speter  /* Print this change.  */
8532785Speter  next_line = last0 + 1;
8632785Speter  format_ifdef (format, first0, next_line, first1, last1 + 1);
8732785Speter}
8832785Speter
8932785Speter/* Print a set of lines according to FORMAT.
9032785Speter   Lines BEG0 up to END0 are from the first file;
9132785Speter   lines BEG1 up to END1 are from the second file.  */
9232785Speter
9332785Speterstatic void
9432785Speterformat_ifdef (format, beg0, end0, beg1, end1)
9532785Speter     char *format;
9632785Speter     int beg0, end0, beg1, end1;
9732785Speter{
9832785Speter  struct group groups[2];
9932785Speter
10032785Speter  groups[0].file = &files[0];
10132785Speter  groups[0].from = beg0;
10232785Speter  groups[0].upto = end0;
10332785Speter  groups[1].file = &files[1];
10432785Speter  groups[1].from = beg1;
10532785Speter  groups[1].upto = end1;
10632785Speter  format_group (outfile, format, '\0', groups);
10732785Speter}
10832785Speter
10932785Speter/* Print to file OUT a set of lines according to FORMAT.
11032785Speter   The format ends at the first free instance of ENDCHAR.
11132785Speter   Yield the address of the terminating character.
11232785Speter   GROUPS specifies which lines to print.
11332785Speter   If OUT is zero, do not actually print anything; just scan the format.  */
11432785Speter
11532785Speterstatic char *
11632785Speterformat_group (out, format, endchar, groups)
11732785Speter     register FILE *out;
11832785Speter     char *format;
11932785Speter     int endchar;
12032785Speter     struct group const *groups;
12132785Speter{
12232785Speter  register char c;
12332785Speter  register char *f = format;
12432785Speter
12532785Speter  while ((c = *f) != endchar && c != 0)
12632785Speter    {
12732785Speter      f++;
12832785Speter      if (c == '%')
12932785Speter	{
13032785Speter	  char *spec = f;
13132785Speter	  switch ((c = *f++))
13232785Speter	    {
13332785Speter	    case '%':
13432785Speter	      break;
13532785Speter
13632785Speter	    case '(':
13732785Speter	      /* Print if-then-else format e.g. `%(n=1?thenpart:elsepart)'.  */
13832785Speter	      {
13932785Speter		int i, value[2];
14032785Speter		FILE *thenout, *elseout;
14132785Speter
14232785Speter		for (i = 0; i < 2; i++)
14332785Speter		  {
14432785Speter		    unsigned char f0 = f[0];
14532785Speter		    if (ISDIGIT (f0))
14632785Speter		      {
14732785Speter			value[i] = atoi (f);
14832785Speter			while (ISDIGIT ((unsigned char) *++f))
14932785Speter			  continue;
15032785Speter		      }
15132785Speter		    else
15232785Speter		      {
15332785Speter			value[i] = groups_letter_value (groups, f0);
15432785Speter			if (value[i] < 0)
15532785Speter			  goto bad_format;
15632785Speter			f++;
15732785Speter		      }
15832785Speter		    if (*f++ != "=?"[i])
15932785Speter		      goto bad_format;
16032785Speter		  }
16132785Speter		if (value[0] == value[1])
16232785Speter		  thenout = out, elseout = 0;
16332785Speter		else
16432785Speter		  thenout = 0, elseout = out;
16532785Speter		f = format_group (thenout, f, ':', groups);
16632785Speter		if (*f)
16732785Speter		  {
16832785Speter		    f = format_group (elseout, f + 1, ')', groups);
16932785Speter		    if (*f)
17032785Speter		      f++;
17132785Speter		  }
17232785Speter	      }
17332785Speter	      continue;
17432785Speter
17532785Speter	    case '<':
17632785Speter	      /* Print lines deleted from first file.  */
17732785Speter	      print_ifdef_lines (out, line_format[OLD], &groups[0]);
17832785Speter	      continue;
17932785Speter
18032785Speter	    case '=':
18132785Speter	      /* Print common lines.  */
18232785Speter	      print_ifdef_lines (out, line_format[UNCHANGED], &groups[0]);
18332785Speter	      continue;
18432785Speter
18532785Speter	    case '>':
18632785Speter	      /* Print lines inserted from second file.  */
18732785Speter	      print_ifdef_lines (out, line_format[NEW], &groups[1]);
18832785Speter	      continue;
18932785Speter
19032785Speter	    default:
19132785Speter	      {
19232785Speter		int value;
19332785Speter		char *speclim;
19432785Speter
19532785Speter		f = scan_printf_spec (spec);
19632785Speter		if (!f)
19732785Speter		  goto bad_format;
19832785Speter		speclim = f;
19932785Speter		c = *f++;
20032785Speter		switch (c)
20132785Speter		  {
20232785Speter		    case '\'':
20332785Speter		      f = scan_char_literal (f, &value);
20432785Speter		      if (!f)
20532785Speter			goto bad_format;
20632785Speter		      break;
20732785Speter
20832785Speter		    default:
20932785Speter		      value = groups_letter_value (groups, c);
21032785Speter		      if (value < 0)
21132785Speter			goto bad_format;
21232785Speter		      break;
21332785Speter		  }
21432785Speter		if (out)
21532785Speter		  {
21632785Speter		    /* Temporarily replace e.g. "%3dnx" with "%3d\0x".  */
21732785Speter		    *speclim = 0;
21832785Speter		    fprintf (out, spec - 1, value);
21932785Speter		    /* Undo the temporary replacement.  */
22032785Speter		    *speclim = c;
22132785Speter		  }
22232785Speter	      }
22332785Speter	      continue;
22432785Speter
22532785Speter	    bad_format:
22632785Speter	      c = '%';
22732785Speter	      f = spec;
22832785Speter	      break;
22932785Speter	    }
23032785Speter	}
23132785Speter      if (out)
23232785Speter	putc (c, out);
23332785Speter    }
23432785Speter  return f;
23532785Speter}
23632785Speter
23732785Speter/* For the line group pair G, return the number corresponding to LETTER.
23832785Speter   Return -1 if LETTER is not a group format letter.  */
23932785Speterstatic int
24032785Spetergroups_letter_value (g, letter)
24132785Speter     struct group const *g;
24232785Speter     int letter;
24332785Speter{
24432785Speter  if (ISUPPER (letter))
24532785Speter    {
24632785Speter      g++;
24732785Speter      letter = tolower (letter);
24832785Speter    }
24932785Speter  switch (letter)
25032785Speter    {
25132785Speter      case 'e': return translate_line_number (g->file, g->from) - 1;
25232785Speter      case 'f': return translate_line_number (g->file, g->from);
25332785Speter      case 'l': return translate_line_number (g->file, g->upto) - 1;
25432785Speter      case 'm': return translate_line_number (g->file, g->upto);
25532785Speter      case 'n': return g->upto - g->from;
25632785Speter      default: return -1;
25732785Speter    }
25832785Speter}
25932785Speter
26032785Speter/* Print to file OUT, using FORMAT to print the line group GROUP.
26132785Speter   But do nothing if OUT is zero.  */
26232785Speterstatic void
26332785Speterprint_ifdef_lines (out, format, group)
26432785Speter     register FILE *out;
26532785Speter     char *format;
26632785Speter     struct group const *group;
26732785Speter{
26832785Speter  struct file_data const *file = group->file;
26932785Speter  char const * const *linbuf = file->linbuf;
27032785Speter  int from = group->from, upto = group->upto;
27132785Speter
27232785Speter  if (!out)
27332785Speter    return;
27432785Speter
27532785Speter  /* If possible, use a single fwrite; it's faster.  */
27632785Speter  if (!tab_expand_flag && format[0] == '%')
27732785Speter    {
27832785Speter      if (format[1] == 'l' && format[2] == '\n' && !format[3])
27932785Speter	{
28032785Speter	  fwrite (linbuf[from], sizeof (char),
28132785Speter		  linbuf[upto] + (linbuf[upto][-1] != '\n') -  linbuf[from],
28232785Speter		  out);
28332785Speter	  return;
28432785Speter	}
28532785Speter      if (format[1] == 'L' && !format[2])
28632785Speter	{
28732785Speter	  fwrite (linbuf[from], sizeof (char),
28832785Speter		  linbuf[upto] -  linbuf[from], out);
28932785Speter	  return;
29032785Speter	}
29132785Speter    }
29232785Speter
29332785Speter  for (;  from < upto;  from++)
29432785Speter    {
29532785Speter      register char c;
29632785Speter      register char *f = format;
29732785Speter
29832785Speter      while ((c = *f++) != 0)
29932785Speter	{
30032785Speter	  if (c == '%')
30132785Speter	    {
30232785Speter	      char *spec = f;
30332785Speter	      switch ((c = *f++))
30432785Speter		{
30532785Speter		case '%':
30632785Speter		  break;
30732785Speter
30832785Speter		case 'l':
30932785Speter		  output_1_line (linbuf[from],
31032785Speter				 linbuf[from + 1]
31132785Speter				   - (linbuf[from + 1][-1] == '\n'), 0, 0);
31232785Speter		  continue;
31332785Speter
31432785Speter		case 'L':
31532785Speter		  output_1_line (linbuf[from], linbuf[from + 1], 0, 0);
31632785Speter		  continue;
31732785Speter
31832785Speter		default:
31932785Speter		  {
32032785Speter		    int value;
32132785Speter		    char *speclim;
32232785Speter
32332785Speter		    f = scan_printf_spec (spec);
32432785Speter		    if (!f)
32532785Speter		      goto bad_format;
32632785Speter		    speclim = f;
32732785Speter		    c = *f++;
32832785Speter		    switch (c)
32932785Speter		      {
33032785Speter			case '\'':
33132785Speter			  f = scan_char_literal (f, &value);
33232785Speter			  if (!f)
33332785Speter			    goto bad_format;
33432785Speter			  break;
33532785Speter
33632785Speter			case 'n':
33732785Speter			  value = translate_line_number (file, from);
33832785Speter			  break;
33932785Speter
34032785Speter			default:
34132785Speter			  goto bad_format;
34232785Speter		      }
34332785Speter		    /* Temporarily replace e.g. "%3dnx" with "%3d\0x".  */
34432785Speter		    *speclim = 0;
34532785Speter		    fprintf (out, spec - 1, value);
34632785Speter		    /* Undo the temporary replacement.  */
34732785Speter		    *speclim = c;
34832785Speter		  }
34932785Speter		  continue;
35032785Speter
35132785Speter		bad_format:
35232785Speter		  c = '%';
35332785Speter		  f = spec;
35432785Speter		  break;
35532785Speter		}
35632785Speter	    }
35732785Speter	  putc (c, out);
35832785Speter	}
35932785Speter    }
36032785Speter}
36132785Speter
36232785Speter/* Scan the character literal represented in the string LIT; LIT points just
36332785Speter   after the initial apostrophe.  Put the literal's value into *INTPTR.
36432785Speter   Yield the address of the first character after the closing apostrophe,
36532785Speter   or zero if the literal is ill-formed.  */
36632785Speterstatic char *
36732785Speterscan_char_literal (lit, intptr)
36832785Speter     char *lit;
36932785Speter     int *intptr;
37032785Speter{
37132785Speter  register char *p = lit;
37232785Speter  int value, digits;
37332785Speter  char c = *p++;
37432785Speter
37532785Speter  switch (c)
37632785Speter    {
37732785Speter      case 0:
37832785Speter      case '\'':
37932785Speter	return 0;
38032785Speter
38132785Speter      case '\\':
38232785Speter	value = 0;
38332785Speter	while ((c = *p++) != '\'')
38432785Speter	  {
38532785Speter	    unsigned digit = c - '0';
38632785Speter	    if (8 <= digit)
38732785Speter	      return 0;
38832785Speter	    value = 8 * value + digit;
38932785Speter	  }
39032785Speter	digits = p - lit - 2;
39132785Speter	if (! (1 <= digits && digits <= 3))
39232785Speter	  return 0;
39332785Speter	break;
39432785Speter
39532785Speter      default:
39632785Speter	value = c;
39732785Speter	if (*p++ != '\'')
39832785Speter	  return 0;
39932785Speter	break;
40032785Speter    }
40132785Speter  *intptr = value;
40232785Speter  return p;
40332785Speter}
40432785Speter
40532785Speter/* Scan optional printf-style SPEC of the form `-*[0-9]*(.[0-9]*)?[cdoxX]'.
40632785Speter   Return the address of the character following SPEC, or zero if failure.  */
40732785Speterstatic char *
40832785Speterscan_printf_spec (spec)
40932785Speter     register char *spec;
41032785Speter{
41132785Speter  register unsigned char c;
41232785Speter
41332785Speter  while ((c = *spec++) == '-')
41432785Speter    continue;
41532785Speter  while (ISDIGIT (c))
41632785Speter    c = *spec++;
41732785Speter  if (c == '.')
41832785Speter    while (ISDIGIT (c = *spec++))
41932785Speter      continue;
42032785Speter  switch (c)
42132785Speter    {
42232785Speter      case 'c': case 'd': case 'o': case 'x': case 'X':
42332785Speter	return spec;
42432785Speter
42532785Speter      default:
42632785Speter	return 0;
42732785Speter    }
42832785Speter}
429