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