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