1170754Sdelphij/* #ifdef-format output routines for GNU DIFF. 2170754Sdelphij 3170754Sdelphij Copyright (C) 1989, 1991, 1992, 1993, 1994, 2001, 2002, 2004 Free 4170754Sdelphij Software Foundation, Inc. 5170754Sdelphij 6170754Sdelphij This file is part of GNU DIFF. 7170754Sdelphij 8170754Sdelphij GNU DIFF is distributed in the hope that it will be useful, 9170754Sdelphij but WITHOUT ANY WARRANTY. No author or distributor 10170754Sdelphij accepts responsibility to anyone for the consequences of using it 11170754Sdelphij or for whether it serves any particular purpose or works at all, 12170754Sdelphij unless he says so in writing. Refer to the GNU DIFF General Public 13170754Sdelphij License for full details. 14170754Sdelphij 15170754Sdelphij Everyone is granted permission to copy, modify and redistribute 16170754Sdelphij GNU DIFF, but only under the conditions described in the 17170754Sdelphij GNU DIFF General Public License. A copy of this license is 18170754Sdelphij supposed to have been given to you along with GNU DIFF so you 19170754Sdelphij can know your rights and responsibilities. It should be in a 20170754Sdelphij file named COPYING. Among other things, the copyright notice 21170754Sdelphij and this notice must be preserved on all copies. */ 22170754Sdelphij 23170754Sdelphij#include "diff.h" 24170754Sdelphij 25170754Sdelphij#include <xalloc.h> 26170754Sdelphij 27170754Sdelphijstruct group 28170754Sdelphij{ 29170754Sdelphij struct file_data const *file; 30170754Sdelphij lin from, upto; /* start and limit lines for this group of lines */ 31170754Sdelphij}; 32170754Sdelphij 33170754Sdelphijstatic char const *format_group (FILE *, char const *, char, 34170754Sdelphij struct group const *); 35170754Sdelphijstatic char const *do_printf_spec (FILE *, char const *, 36170754Sdelphij struct file_data const *, lin, 37170754Sdelphij struct group const *); 38170754Sdelphijstatic char const *scan_char_literal (char const *, char *); 39170754Sdelphijstatic lin groups_letter_value (struct group const *, char); 40170754Sdelphijstatic void format_ifdef (char const *, lin, lin, lin, lin); 41170754Sdelphijstatic void print_ifdef_hunk (struct change *); 42170754Sdelphijstatic void print_ifdef_lines (FILE *, char const *, struct group const *); 43170754Sdelphij 44170754Sdelphijstatic lin next_line0; 45170754Sdelphijstatic lin next_line1; 46170754Sdelphij 47170754Sdelphij/* Print the edit-script SCRIPT as a merged #ifdef file. */ 48170754Sdelphij 49170754Sdelphijvoid 50170754Sdelphijprint_ifdef_script (struct change *script) 51170754Sdelphij{ 52170754Sdelphij next_line0 = next_line1 = - files[0].prefix_lines; 53170754Sdelphij print_script (script, find_change, print_ifdef_hunk); 54170754Sdelphij if (next_line0 < files[0].valid_lines 55170754Sdelphij || next_line1 < files[1].valid_lines) 56170754Sdelphij { 57170754Sdelphij begin_output (); 58170754Sdelphij format_ifdef (group_format[UNCHANGED], 59170754Sdelphij next_line0, files[0].valid_lines, 60170754Sdelphij next_line1, files[1].valid_lines); 61170754Sdelphij } 62170754Sdelphij} 63170754Sdelphij 64170754Sdelphij/* Print a hunk of an ifdef diff. 65170754Sdelphij This is a contiguous portion of a complete edit script, 66170754Sdelphij describing changes in consecutive lines. */ 67170754Sdelphij 68170754Sdelphijstatic void 69170754Sdelphijprint_ifdef_hunk (struct change *hunk) 70170754Sdelphij{ 71170754Sdelphij lin first0, last0, first1, last1; 72170754Sdelphij 73170754Sdelphij /* Determine range of line numbers involved in each file. */ 74170754Sdelphij enum changes changes = analyze_hunk (hunk, &first0, &last0, &first1, &last1); 75170754Sdelphij if (!changes) 76170754Sdelphij return; 77170754Sdelphij 78170754Sdelphij begin_output (); 79170754Sdelphij 80170754Sdelphij /* Print lines up to this change. */ 81170754Sdelphij if (next_line0 < first0 || next_line1 < first1) 82170754Sdelphij format_ifdef (group_format[UNCHANGED], 83170754Sdelphij next_line0, first0, 84170754Sdelphij next_line1, first1); 85170754Sdelphij 86170754Sdelphij /* Print this change. */ 87170754Sdelphij next_line0 = last0 + 1; 88170754Sdelphij next_line1 = last1 + 1; 89170754Sdelphij format_ifdef (group_format[changes], 90170754Sdelphij first0, next_line0, 91170754Sdelphij first1, next_line1); 92170754Sdelphij} 93170754Sdelphij 94170754Sdelphij/* Print a set of lines according to FORMAT. 95170754Sdelphij Lines BEG0 up to END0 are from the first file; 96170754Sdelphij lines BEG1 up to END1 are from the second file. */ 97170754Sdelphij 98170754Sdelphijstatic void 99170754Sdelphijformat_ifdef (char const *format, lin beg0, lin end0, lin beg1, lin end1) 100170754Sdelphij{ 101170754Sdelphij struct group groups[2]; 102170754Sdelphij 103170754Sdelphij groups[0].file = &files[0]; 104170754Sdelphij groups[0].from = beg0; 105170754Sdelphij groups[0].upto = end0; 106170754Sdelphij groups[1].file = &files[1]; 107170754Sdelphij groups[1].from = beg1; 108170754Sdelphij groups[1].upto = end1; 109170754Sdelphij format_group (outfile, format, 0, groups); 110170754Sdelphij} 111170754Sdelphij 112170754Sdelphij/* Print to file OUT a set of lines according to FORMAT. 113170754Sdelphij The format ends at the first free instance of ENDCHAR. 114170754Sdelphij Yield the address of the terminating character. 115170754Sdelphij GROUPS specifies which lines to print. 116170754Sdelphij If OUT is zero, do not actually print anything; just scan the format. */ 117170754Sdelphij 118170754Sdelphijstatic char const * 119170754Sdelphijformat_group (register FILE *out, char const *format, char endchar, 120170754Sdelphij struct group const *groups) 121170754Sdelphij{ 122170754Sdelphij register char c; 123170754Sdelphij register char const *f = format; 124170754Sdelphij 125170754Sdelphij while ((c = *f) != endchar && c != 0) 126170754Sdelphij { 127170754Sdelphij char const *f1 = ++f; 128170754Sdelphij if (c == '%') 129170754Sdelphij switch ((c = *f++)) 130170754Sdelphij { 131170754Sdelphij case '%': 132170754Sdelphij break; 133170754Sdelphij 134170754Sdelphij case '(': 135170754Sdelphij /* Print if-then-else format e.g. `%(n=1?thenpart:elsepart)'. */ 136170754Sdelphij { 137170754Sdelphij int i; 138170754Sdelphij uintmax_t value[2]; 139170754Sdelphij FILE *thenout, *elseout; 140170754Sdelphij 141170754Sdelphij for (i = 0; i < 2; i++) 142170754Sdelphij { 143170754Sdelphij if (ISDIGIT (*f)) 144170754Sdelphij { 145170754Sdelphij char *fend; 146170754Sdelphij errno = 0; 147170754Sdelphij value[i] = strtoumax (f, &fend, 10); 148170754Sdelphij if (errno) 149170754Sdelphij goto bad_format; 150170754Sdelphij f = fend; 151170754Sdelphij } 152170754Sdelphij else 153170754Sdelphij { 154170754Sdelphij value[i] = groups_letter_value (groups, *f); 155170754Sdelphij if (value[i] == -1) 156170754Sdelphij goto bad_format; 157170754Sdelphij f++; 158170754Sdelphij } 159170754Sdelphij if (*f++ != "=?"[i]) 160170754Sdelphij goto bad_format; 161170754Sdelphij } 162170754Sdelphij if (value[0] == value[1]) 163170754Sdelphij thenout = out, elseout = 0; 164170754Sdelphij else 165170754Sdelphij thenout = 0, elseout = out; 166170754Sdelphij f = format_group (thenout, f, ':', groups); 167170754Sdelphij if (*f) 168170754Sdelphij { 169170754Sdelphij f = format_group (elseout, f + 1, ')', groups); 170170754Sdelphij if (*f) 171170754Sdelphij f++; 172170754Sdelphij } 173170754Sdelphij } 174170754Sdelphij continue; 175170754Sdelphij 176170754Sdelphij case '<': 177170754Sdelphij /* Print lines deleted from first file. */ 178170754Sdelphij print_ifdef_lines (out, line_format[OLD], &groups[0]); 179170754Sdelphij continue; 180170754Sdelphij 181170754Sdelphij case '=': 182170754Sdelphij /* Print common lines. */ 183170754Sdelphij print_ifdef_lines (out, line_format[UNCHANGED], &groups[0]); 184170754Sdelphij continue; 185170754Sdelphij 186170754Sdelphij case '>': 187170754Sdelphij /* Print lines inserted from second file. */ 188170754Sdelphij print_ifdef_lines (out, line_format[NEW], &groups[1]); 189170754Sdelphij continue; 190170754Sdelphij 191170754Sdelphij default: 192170754Sdelphij f = do_printf_spec (out, f - 2, 0, 0, groups); 193170754Sdelphij if (f) 194170754Sdelphij continue; 195170754Sdelphij /* Fall through. */ 196170754Sdelphij bad_format: 197170754Sdelphij c = '%'; 198170754Sdelphij f = f1; 199170754Sdelphij break; 200170754Sdelphij } 201170754Sdelphij 202170754Sdelphij if (out) 203170754Sdelphij putc (c, out); 204170754Sdelphij } 205170754Sdelphij 206170754Sdelphij return f; 207170754Sdelphij} 208170754Sdelphij 209170754Sdelphij/* For the line group pair G, return the number corresponding to LETTER. 210170754Sdelphij Return -1 if LETTER is not a group format letter. */ 211170754Sdelphijstatic lin 212170754Sdelphijgroups_letter_value (struct group const *g, char letter) 213170754Sdelphij{ 214170754Sdelphij switch (letter) 215170754Sdelphij { 216170754Sdelphij case 'E': letter = 'e'; g++; break; 217170754Sdelphij case 'F': letter = 'f'; g++; break; 218170754Sdelphij case 'L': letter = 'l'; g++; break; 219170754Sdelphij case 'M': letter = 'm'; g++; break; 220170754Sdelphij case 'N': letter = 'n'; g++; break; 221170754Sdelphij } 222170754Sdelphij 223170754Sdelphij switch (letter) 224170754Sdelphij { 225170754Sdelphij case 'e': return translate_line_number (g->file, g->from) - 1; 226170754Sdelphij case 'f': return translate_line_number (g->file, g->from); 227170754Sdelphij case 'l': return translate_line_number (g->file, g->upto) - 1; 228170754Sdelphij case 'm': return translate_line_number (g->file, g->upto); 229170754Sdelphij case 'n': return g->upto - g->from; 230170754Sdelphij default: return -1; 231170754Sdelphij } 232170754Sdelphij} 233170754Sdelphij 234170754Sdelphij/* Print to file OUT, using FORMAT to print the line group GROUP. 235170754Sdelphij But do nothing if OUT is zero. */ 236170754Sdelphijstatic void 237170754Sdelphijprint_ifdef_lines (register FILE *out, char const *format, 238170754Sdelphij struct group const *group) 239170754Sdelphij{ 240170754Sdelphij struct file_data const *file = group->file; 241170754Sdelphij char const * const *linbuf = file->linbuf; 242170754Sdelphij lin from = group->from, upto = group->upto; 243170754Sdelphij 244170754Sdelphij if (!out) 245170754Sdelphij return; 246170754Sdelphij 247170754Sdelphij /* If possible, use a single fwrite; it's faster. */ 248170754Sdelphij if (!expand_tabs && format[0] == '%') 249170754Sdelphij { 250170754Sdelphij if (format[1] == 'l' && format[2] == '\n' && !format[3] && from < upto) 251170754Sdelphij { 252170754Sdelphij fwrite (linbuf[from], sizeof (char), 253170754Sdelphij linbuf[upto] + (linbuf[upto][-1] != '\n') - linbuf[from], 254170754Sdelphij out); 255170754Sdelphij return; 256170754Sdelphij } 257170754Sdelphij if (format[1] == 'L' && !format[2]) 258170754Sdelphij { 259170754Sdelphij fwrite (linbuf[from], sizeof (char), 260170754Sdelphij linbuf[upto] - linbuf[from], out); 261170754Sdelphij return; 262170754Sdelphij } 263170754Sdelphij } 264170754Sdelphij 265170754Sdelphij for (; from < upto; from++) 266170754Sdelphij { 267170754Sdelphij register char c; 268170754Sdelphij register char const *f = format; 269170754Sdelphij 270170754Sdelphij while ((c = *f++) != 0) 271170754Sdelphij { 272170754Sdelphij char const *f1 = f; 273170754Sdelphij if (c == '%') 274170754Sdelphij switch ((c = *f++)) 275170754Sdelphij { 276170754Sdelphij case '%': 277170754Sdelphij break; 278170754Sdelphij 279170754Sdelphij case 'l': 280170754Sdelphij output_1_line (linbuf[from], 281170754Sdelphij (linbuf[from + 1] 282170754Sdelphij - (linbuf[from + 1][-1] == '\n')), 283170754Sdelphij 0, 0); 284170754Sdelphij continue; 285170754Sdelphij 286170754Sdelphij case 'L': 287170754Sdelphij output_1_line (linbuf[from], linbuf[from + 1], 0, 0); 288170754Sdelphij continue; 289170754Sdelphij 290170754Sdelphij default: 291170754Sdelphij f = do_printf_spec (out, f - 2, file, from, 0); 292170754Sdelphij if (f) 293170754Sdelphij continue; 294170754Sdelphij c = '%'; 295170754Sdelphij f = f1; 296170754Sdelphij break; 297170754Sdelphij } 298170754Sdelphij 299170754Sdelphij putc (c, out); 300170754Sdelphij } 301170754Sdelphij } 302170754Sdelphij} 303170754Sdelphij 304170754Sdelphijstatic char const * 305170754Sdelphijdo_printf_spec (FILE *out, char const *spec, 306170754Sdelphij struct file_data const *file, lin n, 307170754Sdelphij struct group const *groups) 308170754Sdelphij{ 309170754Sdelphij char const *f = spec; 310170754Sdelphij char c; 311170754Sdelphij char c1; 312170754Sdelphij 313170754Sdelphij /* Scan printf-style SPEC of the form %[-'0]*[0-9]*(.[0-9]*)?[cdoxX]. */ 314170754Sdelphij /* assert (*f == '%'); */ 315170754Sdelphij f++; 316170754Sdelphij while ((c = *f++) == '-' || c == '\'' || c == '0') 317170754Sdelphij continue; 318170754Sdelphij while (ISDIGIT (c)) 319170754Sdelphij c = *f++; 320170754Sdelphij if (c == '.') 321170754Sdelphij while (ISDIGIT (c = *f++)) 322170754Sdelphij continue; 323170754Sdelphij c1 = *f++; 324170754Sdelphij 325170754Sdelphij switch (c) 326170754Sdelphij { 327170754Sdelphij case 'c': 328170754Sdelphij if (c1 != '\'') 329170754Sdelphij return 0; 330170754Sdelphij else 331170754Sdelphij { 332170754Sdelphij char value; 333170754Sdelphij f = scan_char_literal (f, &value); 334170754Sdelphij if (!f) 335170754Sdelphij return 0; 336170754Sdelphij if (out) 337170754Sdelphij putc (value, out); 338170754Sdelphij } 339170754Sdelphij break; 340170754Sdelphij 341170754Sdelphij case 'd': case 'o': case 'x': case 'X': 342170754Sdelphij { 343170754Sdelphij lin value; 344170754Sdelphij 345170754Sdelphij if (file) 346170754Sdelphij { 347170754Sdelphij if (c1 != 'n') 348170754Sdelphij return 0; 349170754Sdelphij value = translate_line_number (file, n); 350170754Sdelphij } 351170754Sdelphij else 352170754Sdelphij { 353170754Sdelphij value = groups_letter_value (groups, c1); 354170754Sdelphij if (value < 0) 355170754Sdelphij return 0; 356170754Sdelphij } 357170754Sdelphij 358170754Sdelphij if (out) 359170754Sdelphij { 360170754Sdelphij /* For example, if the spec is "%3xn", use the printf 361170754Sdelphij format spec "%3lx". Here the spec prefix is "%3". */ 362170754Sdelphij long int long_value = value; 363170754Sdelphij size_t spec_prefix_len = f - spec - 2; 364170754Sdelphij#if HAVE_C_VARARRAYS 365170754Sdelphij char format[spec_prefix_len + 3]; 366170754Sdelphij#else 367170754Sdelphij char *format = xmalloc (spec_prefix_len + 3); 368170754Sdelphij#endif 369170754Sdelphij char *p = format + spec_prefix_len; 370170754Sdelphij memcpy (format, spec, spec_prefix_len); 371170754Sdelphij *p++ = 'l'; 372170754Sdelphij *p++ = c; 373170754Sdelphij *p = '\0'; 374170754Sdelphij fprintf (out, format, long_value); 375170754Sdelphij#if ! HAVE_C_VARARRAYS 376170754Sdelphij free (format); 377170754Sdelphij#endif 378170754Sdelphij } 379170754Sdelphij } 380170754Sdelphij break; 381170754Sdelphij 382170754Sdelphij default: 383170754Sdelphij return 0; 384170754Sdelphij } 385170754Sdelphij 386170754Sdelphij return f; 387170754Sdelphij} 388170754Sdelphij 389170754Sdelphij/* Scan the character literal represented in the string LIT; LIT points just 390170754Sdelphij after the initial apostrophe. Put the literal's value into *VALPTR. 391170754Sdelphij Yield the address of the first character after the closing apostrophe, 392170754Sdelphij or zero if the literal is ill-formed. */ 393170754Sdelphijstatic char const * 394170754Sdelphijscan_char_literal (char const *lit, char *valptr) 395170754Sdelphij{ 396170754Sdelphij register char const *p = lit; 397170754Sdelphij char value; 398170754Sdelphij ptrdiff_t digits; 399170754Sdelphij char c = *p++; 400170754Sdelphij 401170754Sdelphij switch (c) 402170754Sdelphij { 403170754Sdelphij case 0: 404170754Sdelphij case '\'': 405170754Sdelphij return 0; 406170754Sdelphij 407170754Sdelphij case '\\': 408170754Sdelphij value = 0; 409170754Sdelphij while ((c = *p++) != '\'') 410170754Sdelphij { 411170754Sdelphij unsigned int digit = c - '0'; 412170754Sdelphij if (8 <= digit) 413170754Sdelphij return 0; 414170754Sdelphij value = 8 * value + digit; 415170754Sdelphij } 416170754Sdelphij digits = p - lit - 2; 417170754Sdelphij if (! (1 <= digits && digits <= 3)) 418170754Sdelphij return 0; 419170754Sdelphij break; 420170754Sdelphij 421170754Sdelphij default: 422170754Sdelphij value = c; 423170754Sdelphij if (*p++ != '\'') 424170754Sdelphij return 0; 425170754Sdelphij break; 426170754Sdelphij } 427170754Sdelphij 428170754Sdelphij *valptr = value; 429170754Sdelphij return p; 430170754Sdelphij} 431