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