132785Speter/* #ifdef-format output routines for GNU DIFF.
244852Speter   Copyright (C) 1989, 1991, 1992, 1993, 1994, 1998 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
3044852Speterstatic char *format_group PARAMS((int, 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 *));
3644852Speterstatic void print_ifdef_lines PARAMS((int, 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;
10644852Speter  format_group (1, format, '\0', groups);
10732785Speter}
10832785Speter
10944852Speter/* If DOIT is non-zero, output 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 *
11644852Speterformat_group (doit, format, endchar, groups)
11744852Speter     int doit;
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];
14044852Speter		int thendoit, elsedoit;
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])
16244852Speter		  thendoit = doit, elsedoit = 0;
16332785Speter		else
16444852Speter		  thendoit = 0, elsedoit = doit;
16544852Speter		f = format_group (thendoit, f, ':', groups);
16632785Speter		if (*f)
16732785Speter		  {
16844852Speter		    f = format_group (elsedoit, f + 1, ')', groups);
16932785Speter		    if (*f)
17032785Speter		      f++;
17132785Speter		  }
17232785Speter	      }
17332785Speter	      continue;
17432785Speter
17532785Speter	    case '<':
17632785Speter	      /* Print lines deleted from first file.  */
17744852Speter	      print_ifdef_lines (doit, line_format[OLD], &groups[0]);
17832785Speter	      continue;
17932785Speter
18032785Speter	    case '=':
18132785Speter	      /* Print common lines.  */
18244852Speter	      print_ifdef_lines (doit, line_format[UNCHANGED], &groups[0]);
18332785Speter	      continue;
18432785Speter
18532785Speter	    case '>':
18632785Speter	      /* Print lines inserted from second file.  */
18744852Speter	      print_ifdef_lines (doit, 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		  }
21444852Speter		if (doit)
21532785Speter		  {
21632785Speter		    /* Temporarily replace e.g. "%3dnx" with "%3d\0x".  */
21732785Speter		    *speclim = 0;
21844852Speter		    printf_output (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	}
23144852Speter      if (doit)
23244852Speter	{
23344852Speter	  /* Don't take the address of a register variable.  */
23444852Speter	  char cc = c;
23544852Speter	  write_output (&cc, 1);
23644852Speter	}
23732785Speter    }
23832785Speter  return f;
23932785Speter}
24032785Speter
24132785Speter/* For the line group pair G, return the number corresponding to LETTER.
24232785Speter   Return -1 if LETTER is not a group format letter.  */
24332785Speterstatic int
24432785Spetergroups_letter_value (g, letter)
24532785Speter     struct group const *g;
24632785Speter     int letter;
24732785Speter{
24832785Speter  if (ISUPPER (letter))
24932785Speter    {
25032785Speter      g++;
25132785Speter      letter = tolower (letter);
25232785Speter    }
25332785Speter  switch (letter)
25432785Speter    {
25532785Speter      case 'e': return translate_line_number (g->file, g->from) - 1;
25632785Speter      case 'f': return translate_line_number (g->file, g->from);
25732785Speter      case 'l': return translate_line_number (g->file, g->upto) - 1;
25832785Speter      case 'm': return translate_line_number (g->file, g->upto);
25932785Speter      case 'n': return g->upto - g->from;
26032785Speter      default: return -1;
26132785Speter    }
26232785Speter}
26332785Speter
26444852Speter/* Output using FORMAT to print the line group GROUP.
26544852Speter   But do nothing if DOIT is zero.  */
26632785Speterstatic void
26744852Speterprint_ifdef_lines (doit, format, group)
26844852Speter     int doit;
26932785Speter     char *format;
27032785Speter     struct group const *group;
27132785Speter{
27232785Speter  struct file_data const *file = group->file;
27332785Speter  char const * const *linbuf = file->linbuf;
27432785Speter  int from = group->from, upto = group->upto;
27532785Speter
27644852Speter  if (!doit)
27732785Speter    return;
27832785Speter
27932785Speter  /* If possible, use a single fwrite; it's faster.  */
28032785Speter  if (!tab_expand_flag && format[0] == '%')
28132785Speter    {
28232785Speter      if (format[1] == 'l' && format[2] == '\n' && !format[3])
28332785Speter	{
28444852Speter	  write_output (linbuf[from],
28544852Speter			(linbuf[upto] + (linbuf[upto][-1] != '\n')
28644852Speter			 - linbuf[from]));
28732785Speter	  return;
28832785Speter	}
28932785Speter      if (format[1] == 'L' && !format[2])
29032785Speter	{
29144852Speter	  write_output (linbuf[from],
29244852Speter			linbuf[upto] -  linbuf[from]);
29332785Speter	  return;
29432785Speter	}
29532785Speter    }
29632785Speter
29732785Speter  for (;  from < upto;  from++)
29832785Speter    {
29932785Speter      register char c;
30032785Speter      register char *f = format;
30144852Speter      char cc;
30232785Speter
30332785Speter      while ((c = *f++) != 0)
30432785Speter	{
30532785Speter	  if (c == '%')
30632785Speter	    {
30732785Speter	      char *spec = f;
30832785Speter	      switch ((c = *f++))
30932785Speter		{
31032785Speter		case '%':
31132785Speter		  break;
31232785Speter
31332785Speter		case 'l':
31432785Speter		  output_1_line (linbuf[from],
31532785Speter				 linbuf[from + 1]
31632785Speter				   - (linbuf[from + 1][-1] == '\n'), 0, 0);
31732785Speter		  continue;
31832785Speter
31932785Speter		case 'L':
32032785Speter		  output_1_line (linbuf[from], linbuf[from + 1], 0, 0);
32132785Speter		  continue;
32232785Speter
32332785Speter		default:
32432785Speter		  {
32532785Speter		    int value;
32632785Speter		    char *speclim;
32732785Speter
32832785Speter		    f = scan_printf_spec (spec);
32932785Speter		    if (!f)
33032785Speter		      goto bad_format;
33132785Speter		    speclim = f;
33232785Speter		    c = *f++;
33332785Speter		    switch (c)
33432785Speter		      {
33532785Speter			case '\'':
33632785Speter			  f = scan_char_literal (f, &value);
33732785Speter			  if (!f)
33832785Speter			    goto bad_format;
33932785Speter			  break;
34032785Speter
34132785Speter			case 'n':
34232785Speter			  value = translate_line_number (file, from);
34332785Speter			  break;
34432785Speter
34532785Speter			default:
34632785Speter			  goto bad_format;
34732785Speter		      }
34832785Speter		    /* Temporarily replace e.g. "%3dnx" with "%3d\0x".  */
34932785Speter		    *speclim = 0;
35044852Speter		    printf_output (spec - 1, value);
35132785Speter		    /* Undo the temporary replacement.  */
35232785Speter		    *speclim = c;
35332785Speter		  }
35432785Speter		  continue;
35532785Speter
35632785Speter		bad_format:
35732785Speter		  c = '%';
35832785Speter		  f = spec;
35932785Speter		  break;
36032785Speter		}
36132785Speter	    }
36244852Speter
36344852Speter	  /* Don't take the address of a register variable.  */
36444852Speter	  cc = c;
36544852Speter	  write_output (&cc, 1);
36632785Speter	}
36732785Speter    }
36832785Speter}
36932785Speter
37032785Speter/* Scan the character literal represented in the string LIT; LIT points just
37132785Speter   after the initial apostrophe.  Put the literal's value into *INTPTR.
37232785Speter   Yield the address of the first character after the closing apostrophe,
37332785Speter   or zero if the literal is ill-formed.  */
37432785Speterstatic char *
37532785Speterscan_char_literal (lit, intptr)
37632785Speter     char *lit;
37732785Speter     int *intptr;
37832785Speter{
37932785Speter  register char *p = lit;
38032785Speter  int value, digits;
38132785Speter  char c = *p++;
38232785Speter
38332785Speter  switch (c)
38432785Speter    {
38532785Speter      case 0:
38632785Speter      case '\'':
38732785Speter	return 0;
38832785Speter
38932785Speter      case '\\':
39032785Speter	value = 0;
39132785Speter	while ((c = *p++) != '\'')
39232785Speter	  {
39332785Speter	    unsigned digit = c - '0';
39432785Speter	    if (8 <= digit)
39532785Speter	      return 0;
39632785Speter	    value = 8 * value + digit;
39732785Speter	  }
39832785Speter	digits = p - lit - 2;
39932785Speter	if (! (1 <= digits && digits <= 3))
40032785Speter	  return 0;
40132785Speter	break;
40232785Speter
40332785Speter      default:
40432785Speter	value = c;
40532785Speter	if (*p++ != '\'')
40632785Speter	  return 0;
40732785Speter	break;
40832785Speter    }
40932785Speter  *intptr = value;
41032785Speter  return p;
41132785Speter}
41232785Speter
41332785Speter/* Scan optional printf-style SPEC of the form `-*[0-9]*(.[0-9]*)?[cdoxX]'.
41432785Speter   Return the address of the character following SPEC, or zero if failure.  */
41532785Speterstatic char *
41632785Speterscan_printf_spec (spec)
41732785Speter     register char *spec;
41832785Speter{
41932785Speter  register unsigned char c;
42032785Speter
42132785Speter  while ((c = *spec++) == '-')
42232785Speter    continue;
42332785Speter  while (ISDIGIT (c))
42432785Speter    c = *spec++;
42532785Speter  if (c == '.')
42632785Speter    while (ISDIGIT (c = *spec++))
42732785Speter      continue;
42832785Speter  switch (c)
42932785Speter    {
43032785Speter      case 'c': case 'd': case 'o': case 'x': case 'X':
43132785Speter	return spec;
43232785Speter
43332785Speter      default:
43432785Speter	return 0;
43532785Speter    }
43632785Speter}
437