1 /* #ifdef-format output routines for GNU DIFF. 2 3 Copyright (C) 1989, 1991-1994, 2001-2002, 2004, 2006, 2009-2011 Free 4 Software Foundation, Inc. 5 6 This file is part of GNU DIFF. 7 8 GNU DIFF is distributed in the hope that it will be useful, 9 but WITHOUT ANY WARRANTY. No author or distributor 10 accepts responsibility to anyone for the consequences of using it 11 or for whether it serves any particular purpose or works at all, 12 unless he says so in writing. Refer to the GNU General Public 13 License for full details. 14 15 Everyone is granted permission to copy, modify and redistribute 16 GNU DIFF, but only under the conditions described in the 17 GNU General Public License. A copy of this license is 18 supposed to have been given to you along with GNU DIFF so you 19 can know your rights and responsibilities. It should be in a 20 file named COPYING. Among other things, the copyright notice 21 and this notice must be preserved on all copies. */ 22 23 #include "diff.h" 24 25 #include <xalloc.h> 26 27 struct group 28 { 29 struct file_data const *file; 30 lin from, upto; /* start and limit lines for this group of lines */ 31 }; 32 33 static char const *format_group (FILE *, char const *, char, 34 struct group const *); 35 static char const *do_printf_spec (FILE *, char const *, 36 struct file_data const *, lin, 37 struct group const *); 38 static char const *scan_char_literal (char const *, char *); 39 static lin groups_letter_value (struct group const *, char); 40 static void format_ifdef (char const *, lin, lin, lin, lin); 41 static void print_ifdef_hunk (struct change *); 42 static void print_ifdef_lines (FILE *, char const *, struct group const *); 43 44 static lin next_line0; 45 static lin next_line1; 46 47 /* Print the edit-script SCRIPT as a merged #ifdef file. */ 48 49 void 50 print_ifdef_script (struct change *script) 51 { 52 next_line0 = next_line1 = - files[0].prefix_lines; 53 print_script (script, find_change, print_ifdef_hunk); 54 if (next_line0 < files[0].valid_lines 55 || next_line1 < files[1].valid_lines) 56 { 57 begin_output (); 58 format_ifdef (group_format[UNCHANGED], 59 next_line0, files[0].valid_lines, 60 next_line1, files[1].valid_lines); 61 } 62 } 63 64 /* Print a hunk of an ifdef diff. 65 This is a contiguous portion of a complete edit script, 66 describing changes in consecutive lines. */ 67 68 static void 69 print_ifdef_hunk (struct change *hunk) 70 { 71 lin first0, last0, first1, last1; 72 73 /* Determine range of line numbers involved in each file. */ 74 enum changes changes = analyze_hunk (hunk, &first0, &last0, &first1, &last1); 75 if (!changes) 76 return; 77 78 begin_output (); 79 80 /* Print lines up to this change. */ 81 if (next_line0 < first0 || next_line1 < first1) 82 format_ifdef (group_format[UNCHANGED], 83 next_line0, first0, 84 next_line1, first1); 85 86 /* Print this change. */ 87 next_line0 = last0 + 1; 88 next_line1 = last1 + 1; 89 format_ifdef (group_format[changes], 90 first0, next_line0, 91 first1, next_line1); 92 } 93 94 /* Print a set of lines according to FORMAT. 95 Lines BEG0 up to END0 are from the first file; 96 lines BEG1 up to END1 are from the second file. */ 97 98 static void 99 format_ifdef (char const *format, lin beg0, lin end0, lin beg1, lin end1) 100 { 101 struct group groups[2]; 102 103 groups[0].file = &files[0]; 104 groups[0].from = beg0; 105 groups[0].upto = end0; 106 groups[1].file = &files[1]; 107 groups[1].from = beg1; 108 groups[1].upto = end1; 109 format_group (outfile, format, 0, groups); 110 } 111 112 /* Print to file OUT a set of lines according to FORMAT. 113 The format ends at the first free instance of ENDCHAR. 114 Yield the address of the terminating character. 115 GROUPS specifies which lines to print. 116 If OUT is zero, do not actually print anything; just scan the format. */ 117 118 static char const * 119 format_group (register FILE *out, char const *format, char endchar, 120 struct group const *groups) 121 { 122 register char c; 123 register char const *f = format; 124 125 while ((c = *f) != endchar && c != 0) 126 { 127 char const *f1 = ++f; 128 if (c == '%') 129 switch ((c = *f++)) 130 { 131 case '%': 132 break; 133 134 case '(': 135 /* Print if-then-else format e.g. `%(n=1?thenpart:elsepart)'. */ 136 { 137 int i; 138 uintmax_t value[2]; 139 FILE *thenout, *elseout; 140 141 for (i = 0; i < 2; i++) 142 { 143 if (ISDIGIT (*f)) 144 { 145 char *fend; 146 errno = 0; 147 value[i] = strtoumax (f, &fend, 10); 148 if (errno) 149 goto bad_format; 150 f = fend; 151 } 152 else 153 { 154 value[i] = groups_letter_value (groups, *f); 155 if (value[i] == -1) 156 goto bad_format; 157 f++; 158 } 159 if (*f++ != "=?"[i]) 160 goto bad_format; 161 } 162 if (value[0] == value[1]) 163 thenout = out, elseout = 0; 164 else 165 thenout = 0, elseout = out; 166 f = format_group (thenout, f, ':', groups); 167 if (*f) 168 { 169 f = format_group (elseout, f + 1, ')', groups); 170 if (*f) 171 f++; 172 } 173 } 174 continue; 175 176 case '<': 177 /* Print lines deleted from first file. */ 178 print_ifdef_lines (out, line_format[OLD], &groups[0]); 179 continue; 180 181 case '=': 182 /* Print common lines. */ 183 print_ifdef_lines (out, line_format[UNCHANGED], &groups[0]); 184 continue; 185 186 case '>': 187 /* Print lines inserted from second file. */ 188 print_ifdef_lines (out, line_format[NEW], &groups[1]); 189 continue; 190 191 default: 192 f = do_printf_spec (out, f - 2, 0, 0, groups); 193 if (f) 194 continue; 195 /* Fall through. */ 196 bad_format: 197 c = '%'; 198 f = f1; 199 break; 200 } 201 202 if (out) 203 putc (c, out); 204 } 205 206 return f; 207 } 208 209 /* For the line group pair G, return the number corresponding to LETTER. 210 Return -1 if LETTER is not a group format letter. */ 211 static lin 212 groups_letter_value (struct group const *g, char letter) 213 { 214 switch (letter) 215 { 216 case 'E': letter = 'e'; g++; break; 217 case 'F': letter = 'f'; g++; break; 218 case 'L': letter = 'l'; g++; break; 219 case 'M': letter = 'm'; g++; break; 220 case 'N': letter = 'n'; g++; break; 221 } 222 223 switch (letter) 224 { 225 case 'e': return translate_line_number (g->file, g->from) - 1; 226 case 'f': return translate_line_number (g->file, g->from); 227 case 'l': return translate_line_number (g->file, g->upto) - 1; 228 case 'm': return translate_line_number (g->file, g->upto); 229 case 'n': return g->upto - g->from; 230 default: return -1; 231 } 232 } 233 234 /* Print to file OUT, using FORMAT to print the line group GROUP. 235 But do nothing if OUT is zero. */ 236 static void 237 print_ifdef_lines (register FILE *out, char const *format, 238 struct group const *group) 239 { 240 struct file_data const *file = group->file; 241 char const * const *linbuf = file->linbuf; 242 lin from = group->from, upto = group->upto; 243 244 if (!out) 245 return; 246 247 /* If possible, use a single fwrite; it's faster. */ 248 if (!expand_tabs && format[0] == '%') 249 { 250 if (format[1] == 'l' && format[2] == '\n' && !format[3] && from < upto) 251 { 252 fwrite (linbuf[from], sizeof (char), 253 linbuf[upto] + (linbuf[upto][-1] != '\n') - linbuf[from], 254 out); 255 return; 256 } 257 if (format[1] == 'L' && !format[2]) 258 { 259 fwrite (linbuf[from], sizeof (char), 260 linbuf[upto] - linbuf[from], out); 261 return; 262 } 263 } 264 265 for (; from < upto; from++) 266 { 267 register char c; 268 register char const *f = format; 269 270 while ((c = *f++) != 0) 271 { 272 char const *f1 = f; 273 if (c == '%') 274 switch ((c = *f++)) 275 { 276 case '%': 277 break; 278 279 case 'l': 280 output_1_line (linbuf[from], 281 (linbuf[from + 1] 282 - (linbuf[from + 1][-1] == '\n')), 283 0, 0); 284 continue; 285 286 case 'L': 287 output_1_line (linbuf[from], linbuf[from + 1], 0, 0); 288 continue; 289 290 default: 291 f = do_printf_spec (out, f - 2, file, from, 0); 292 if (f) 293 continue; 294 c = '%'; 295 f = f1; 296 break; 297 } 298 299 putc (c, out); 300 } 301 } 302 } 303 304 static char const * 305 do_printf_spec (FILE *out, char const *spec, 306 struct file_data const *file, lin n, 307 struct group const *groups) 308 { 309 char const *f = spec; 310 char c; 311 char c1; 312 313 /* Scan printf-style SPEC of the form %[-'0]*[0-9]*(.[0-9]*)?[cdoxX]. */ 314 /* assert (*f == '%'); */ 315 f++; 316 while ((c = *f++) == '-' || c == '\'' || c == '0') 317 continue; 318 while (ISDIGIT (c)) 319 c = *f++; 320 if (c == '.') 321 while (ISDIGIT (c = *f++)) 322 continue; 323 c1 = *f++; 324 325 switch (c) 326 { 327 case 'c': 328 if (c1 != '\'') 329 return 0; 330 else 331 { 332 char value IF_LINT (= 0); 333 f = scan_char_literal (f, &value); 334 if (!f) 335 return 0; 336 if (out) 337 putc (value, out); 338 } 339 break; 340 341 case 'd': case 'o': case 'x': case 'X': 342 { 343 lin value; 344 345 if (file) 346 { 347 if (c1 != 'n') 348 return 0; 349 value = translate_line_number (file, n); 350 } 351 else 352 { 353 value = groups_letter_value (groups, c1); 354 if (value < 0) 355 return 0; 356 } 357 358 if (out) 359 { 360 /* For example, if the spec is "%3xn", use the printf 361 format spec "%3lx". Here the spec prefix is "%3". */ 362 long int long_value = value; 363 size_t spec_prefix_len = f - spec - 2; 364 #if HAVE_C_VARARRAYS 365 char format[spec_prefix_len + 3]; 366 #else 367 char *format = xmalloc (spec_prefix_len + 3); 368 #endif 369 char *p = format + spec_prefix_len; 370 memcpy (format, spec, spec_prefix_len); 371 *p++ = 'l'; 372 *p++ = c; 373 *p = '\0'; 374 fprintf (out, format, long_value); 375 #if ! HAVE_C_VARARRAYS 376 free (format); 377 #endif 378 } 379 } 380 break; 381 382 default: 383 return 0; 384 } 385 386 return f; 387 } 388 389 /* Scan the character literal represented in the string LIT; LIT points just 390 after the initial apostrophe. Put the literal's value into *VALPTR. 391 Yield the address of the first character after the closing apostrophe, 392 or a null pointer if the literal is ill-formed. */ 393 static char const * 394 scan_char_literal (char const *lit, char *valptr) 395 { 396 register char const *p = lit; 397 char value; 398 ptrdiff_t digits; 399 char c = *p++; 400 401 switch (c) 402 { 403 case 0: 404 case '\'': 405 return NULL; 406 407 case '\\': 408 value = 0; 409 while ((c = *p++) != '\'') 410 { 411 unsigned int digit = c - '0'; 412 if (8 <= digit) 413 return NULL; 414 value = 8 * value + digit; 415 } 416 digits = p - lit - 2; 417 if (! (1 <= digits && digits <= 3)) 418 return NULL; 419 break; 420 421 default: 422 value = c; 423 if (*p++ != '\'') 424 return NULL; 425 break; 426 } 427 428 *valptr = value; 429 return p; 430 } 431