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