ifdef.c revision 32785
132785Speter/* #ifdef-format output routines for GNU DIFF. 232785Speter Copyright (C) 1989, 1991, 1992, 1993, 1994 Free Software Foundation, Inc. 332785Speter 432785SpeterThis file is part of GNU DIFF. 532785Speter 632785SpeterGNU DIFF is distributed in the hope that it will be useful, 732785Speterbut WITHOUT ANY WARRANTY. No author or distributor 832785Speteraccepts responsibility to anyone for the consequences of using it 932785Speteror for whether it serves any particular purpose or works at all, 1032785Speterunless he says so in writing. Refer to the GNU DIFF General Public 1132785SpeterLicense for full details. 1232785Speter 1332785SpeterEveryone is granted permission to copy, modify and redistribute 1432785SpeterGNU DIFF, but only under the conditions described in the 1532785SpeterGNU DIFF General Public License. A copy of this license is 1632785Spetersupposed to have been given to you along with GNU DIFF so you 1732785Spetercan know your rights and responsibilities. It should be in a 1832785Speterfile named COPYING. Among other things, the copyright notice 1932785Speterand this notice must be preserved on all copies. */ 2032785Speter 2132785Speter 2232785Speter#include "diff.h" 2332785Speter 2432785Speterstruct group 2532785Speter{ 2632785Speter struct file_data const *file; 2732785Speter int from, upto; /* start and limit lines for this group of lines */ 2832785Speter}; 2932785Speter 3032785Speterstatic char *format_group PARAMS((FILE *, char *, int, struct group const *)); 3132785Speterstatic char *scan_char_literal PARAMS((char *, int *)); 3232785Speterstatic char *scan_printf_spec PARAMS((char *)); 3332785Speterstatic int groups_letter_value PARAMS((struct group const *, int)); 3432785Speterstatic void format_ifdef PARAMS((char *, int, int, int, int)); 3532785Speterstatic void print_ifdef_hunk PARAMS((struct change *)); 3632785Speterstatic void print_ifdef_lines PARAMS((FILE *, char *, struct group const *)); 3732785Speter 3832785Speterstatic int next_line; 3932785Speter 4032785Speter/* Print the edit-script SCRIPT as a merged #ifdef file. */ 4132785Speter 4232785Spetervoid 4332785Speterprint_ifdef_script (script) 4432785Speter struct change *script; 4532785Speter{ 4632785Speter next_line = - files[0].prefix_lines; 4732785Speter print_script (script, find_change, print_ifdef_hunk); 4832785Speter if (next_line < files[0].valid_lines) 4932785Speter { 5032785Speter begin_output (); 5132785Speter format_ifdef (group_format[UNCHANGED], next_line, files[0].valid_lines, 5232785Speter next_line - files[0].valid_lines + files[1].valid_lines, 5332785Speter files[1].valid_lines); 5432785Speter } 5532785Speter} 5632785Speter 5732785Speter/* Print a hunk of an ifdef diff. 5832785Speter This is a contiguous portion of a complete edit script, 5932785Speter describing changes in consecutive lines. */ 6032785Speter 6132785Speterstatic void 6232785Speterprint_ifdef_hunk (hunk) 6332785Speter struct change *hunk; 6432785Speter{ 6532785Speter int first0, last0, first1, last1, deletes, inserts; 6632785Speter char *format; 6732785Speter 6832785Speter /* Determine range of line numbers involved in each file. */ 6932785Speter analyze_hunk (hunk, &first0, &last0, &first1, &last1, &deletes, &inserts); 7032785Speter if (inserts) 7132785Speter format = deletes ? group_format[CHANGED] : group_format[NEW]; 7232785Speter else if (deletes) 7332785Speter format = group_format[OLD]; 7432785Speter else 7532785Speter return; 7632785Speter 7732785Speter begin_output (); 7832785Speter 7932785Speter /* Print lines up to this change. */ 8032785Speter if (next_line < first0) 8132785Speter format_ifdef (group_format[UNCHANGED], next_line, first0, 8232785Speter next_line - first0 + first1, first1); 8332785Speter 8432785Speter /* Print this change. */ 8532785Speter next_line = last0 + 1; 8632785Speter format_ifdef (format, first0, next_line, first1, last1 + 1); 8732785Speter} 8832785Speter 8932785Speter/* Print a set of lines according to FORMAT. 9032785Speter Lines BEG0 up to END0 are from the first file; 9132785Speter lines BEG1 up to END1 are from the second file. */ 9232785Speter 9332785Speterstatic void 9432785Speterformat_ifdef (format, beg0, end0, beg1, end1) 9532785Speter char *format; 9632785Speter int beg0, end0, beg1, end1; 9732785Speter{ 9832785Speter struct group groups[2]; 9932785Speter 10032785Speter groups[0].file = &files[0]; 10132785Speter groups[0].from = beg0; 10232785Speter groups[0].upto = end0; 10332785Speter groups[1].file = &files[1]; 10432785Speter groups[1].from = beg1; 10532785Speter groups[1].upto = end1; 10632785Speter format_group (outfile, format, '\0', groups); 10732785Speter} 10832785Speter 10932785Speter/* Print to file OUT a set of lines according to FORMAT. 11032785Speter The format ends at the first free instance of ENDCHAR. 11132785Speter Yield the address of the terminating character. 11232785Speter GROUPS specifies which lines to print. 11332785Speter If OUT is zero, do not actually print anything; just scan the format. */ 11432785Speter 11532785Speterstatic char * 11632785Speterformat_group (out, format, endchar, groups) 11732785Speter register FILE *out; 11832785Speter char *format; 11932785Speter int endchar; 12032785Speter struct group const *groups; 12132785Speter{ 12232785Speter register char c; 12332785Speter register char *f = format; 12432785Speter 12532785Speter while ((c = *f) != endchar && c != 0) 12632785Speter { 12732785Speter f++; 12832785Speter if (c == '%') 12932785Speter { 13032785Speter char *spec = f; 13132785Speter switch ((c = *f++)) 13232785Speter { 13332785Speter case '%': 13432785Speter break; 13532785Speter 13632785Speter case '(': 13732785Speter /* Print if-then-else format e.g. `%(n=1?thenpart:elsepart)'. */ 13832785Speter { 13932785Speter int i, value[2]; 14032785Speter FILE *thenout, *elseout; 14132785Speter 14232785Speter for (i = 0; i < 2; i++) 14332785Speter { 14432785Speter unsigned char f0 = f[0]; 14532785Speter if (ISDIGIT (f0)) 14632785Speter { 14732785Speter value[i] = atoi (f); 14832785Speter while (ISDIGIT ((unsigned char) *++f)) 14932785Speter continue; 15032785Speter } 15132785Speter else 15232785Speter { 15332785Speter value[i] = groups_letter_value (groups, f0); 15432785Speter if (value[i] < 0) 15532785Speter goto bad_format; 15632785Speter f++; 15732785Speter } 15832785Speter if (*f++ != "=?"[i]) 15932785Speter goto bad_format; 16032785Speter } 16132785Speter if (value[0] == value[1]) 16232785Speter thenout = out, elseout = 0; 16332785Speter else 16432785Speter thenout = 0, elseout = out; 16532785Speter f = format_group (thenout, f, ':', groups); 16632785Speter if (*f) 16732785Speter { 16832785Speter f = format_group (elseout, f + 1, ')', groups); 16932785Speter if (*f) 17032785Speter f++; 17132785Speter } 17232785Speter } 17332785Speter continue; 17432785Speter 17532785Speter case '<': 17632785Speter /* Print lines deleted from first file. */ 17732785Speter print_ifdef_lines (out, line_format[OLD], &groups[0]); 17832785Speter continue; 17932785Speter 18032785Speter case '=': 18132785Speter /* Print common lines. */ 18232785Speter print_ifdef_lines (out, line_format[UNCHANGED], &groups[0]); 18332785Speter continue; 18432785Speter 18532785Speter case '>': 18632785Speter /* Print lines inserted from second file. */ 18732785Speter print_ifdef_lines (out, line_format[NEW], &groups[1]); 18832785Speter continue; 18932785Speter 19032785Speter default: 19132785Speter { 19232785Speter int value; 19332785Speter char *speclim; 19432785Speter 19532785Speter f = scan_printf_spec (spec); 19632785Speter if (!f) 19732785Speter goto bad_format; 19832785Speter speclim = f; 19932785Speter c = *f++; 20032785Speter switch (c) 20132785Speter { 20232785Speter case '\'': 20332785Speter f = scan_char_literal (f, &value); 20432785Speter if (!f) 20532785Speter goto bad_format; 20632785Speter break; 20732785Speter 20832785Speter default: 20932785Speter value = groups_letter_value (groups, c); 21032785Speter if (value < 0) 21132785Speter goto bad_format; 21232785Speter break; 21332785Speter } 21432785Speter if (out) 21532785Speter { 21632785Speter /* Temporarily replace e.g. "%3dnx" with "%3d\0x". */ 21732785Speter *speclim = 0; 21832785Speter fprintf (out, spec - 1, value); 21932785Speter /* Undo the temporary replacement. */ 22032785Speter *speclim = c; 22132785Speter } 22232785Speter } 22332785Speter continue; 22432785Speter 22532785Speter bad_format: 22632785Speter c = '%'; 22732785Speter f = spec; 22832785Speter break; 22932785Speter } 23032785Speter } 23132785Speter if (out) 23232785Speter putc (c, out); 23332785Speter } 23432785Speter return f; 23532785Speter} 23632785Speter 23732785Speter/* For the line group pair G, return the number corresponding to LETTER. 23832785Speter Return -1 if LETTER is not a group format letter. */ 23932785Speterstatic int 24032785Spetergroups_letter_value (g, letter) 24132785Speter struct group const *g; 24232785Speter int letter; 24332785Speter{ 24432785Speter if (ISUPPER (letter)) 24532785Speter { 24632785Speter g++; 24732785Speter letter = tolower (letter); 24832785Speter } 24932785Speter switch (letter) 25032785Speter { 25132785Speter case 'e': return translate_line_number (g->file, g->from) - 1; 25232785Speter case 'f': return translate_line_number (g->file, g->from); 25332785Speter case 'l': return translate_line_number (g->file, g->upto) - 1; 25432785Speter case 'm': return translate_line_number (g->file, g->upto); 25532785Speter case 'n': return g->upto - g->from; 25632785Speter default: return -1; 25732785Speter } 25832785Speter} 25932785Speter 26032785Speter/* Print to file OUT, using FORMAT to print the line group GROUP. 26132785Speter But do nothing if OUT is zero. */ 26232785Speterstatic void 26332785Speterprint_ifdef_lines (out, format, group) 26432785Speter register FILE *out; 26532785Speter char *format; 26632785Speter struct group const *group; 26732785Speter{ 26832785Speter struct file_data const *file = group->file; 26932785Speter char const * const *linbuf = file->linbuf; 27032785Speter int from = group->from, upto = group->upto; 27132785Speter 27232785Speter if (!out) 27332785Speter return; 27432785Speter 27532785Speter /* If possible, use a single fwrite; it's faster. */ 27632785Speter if (!tab_expand_flag && format[0] == '%') 27732785Speter { 27832785Speter if (format[1] == 'l' && format[2] == '\n' && !format[3]) 27932785Speter { 28032785Speter fwrite (linbuf[from], sizeof (char), 28132785Speter linbuf[upto] + (linbuf[upto][-1] != '\n') - linbuf[from], 28232785Speter out); 28332785Speter return; 28432785Speter } 28532785Speter if (format[1] == 'L' && !format[2]) 28632785Speter { 28732785Speter fwrite (linbuf[from], sizeof (char), 28832785Speter linbuf[upto] - linbuf[from], out); 28932785Speter return; 29032785Speter } 29132785Speter } 29232785Speter 29332785Speter for (; from < upto; from++) 29432785Speter { 29532785Speter register char c; 29632785Speter register char *f = format; 29732785Speter 29832785Speter while ((c = *f++) != 0) 29932785Speter { 30032785Speter if (c == '%') 30132785Speter { 30232785Speter char *spec = f; 30332785Speter switch ((c = *f++)) 30432785Speter { 30532785Speter case '%': 30632785Speter break; 30732785Speter 30832785Speter case 'l': 30932785Speter output_1_line (linbuf[from], 31032785Speter linbuf[from + 1] 31132785Speter - (linbuf[from + 1][-1] == '\n'), 0, 0); 31232785Speter continue; 31332785Speter 31432785Speter case 'L': 31532785Speter output_1_line (linbuf[from], linbuf[from + 1], 0, 0); 31632785Speter continue; 31732785Speter 31832785Speter default: 31932785Speter { 32032785Speter int value; 32132785Speter char *speclim; 32232785Speter 32332785Speter f = scan_printf_spec (spec); 32432785Speter if (!f) 32532785Speter goto bad_format; 32632785Speter speclim = f; 32732785Speter c = *f++; 32832785Speter switch (c) 32932785Speter { 33032785Speter case '\'': 33132785Speter f = scan_char_literal (f, &value); 33232785Speter if (!f) 33332785Speter goto bad_format; 33432785Speter break; 33532785Speter 33632785Speter case 'n': 33732785Speter value = translate_line_number (file, from); 33832785Speter break; 33932785Speter 34032785Speter default: 34132785Speter goto bad_format; 34232785Speter } 34332785Speter /* Temporarily replace e.g. "%3dnx" with "%3d\0x". */ 34432785Speter *speclim = 0; 34532785Speter fprintf (out, spec - 1, value); 34632785Speter /* Undo the temporary replacement. */ 34732785Speter *speclim = c; 34832785Speter } 34932785Speter continue; 35032785Speter 35132785Speter bad_format: 35232785Speter c = '%'; 35332785Speter f = spec; 35432785Speter break; 35532785Speter } 35632785Speter } 35732785Speter putc (c, out); 35832785Speter } 35932785Speter } 36032785Speter} 36132785Speter 36232785Speter/* Scan the character literal represented in the string LIT; LIT points just 36332785Speter after the initial apostrophe. Put the literal's value into *INTPTR. 36432785Speter Yield the address of the first character after the closing apostrophe, 36532785Speter or zero if the literal is ill-formed. */ 36632785Speterstatic char * 36732785Speterscan_char_literal (lit, intptr) 36832785Speter char *lit; 36932785Speter int *intptr; 37032785Speter{ 37132785Speter register char *p = lit; 37232785Speter int value, digits; 37332785Speter char c = *p++; 37432785Speter 37532785Speter switch (c) 37632785Speter { 37732785Speter case 0: 37832785Speter case '\'': 37932785Speter return 0; 38032785Speter 38132785Speter case '\\': 38232785Speter value = 0; 38332785Speter while ((c = *p++) != '\'') 38432785Speter { 38532785Speter unsigned digit = c - '0'; 38632785Speter if (8 <= digit) 38732785Speter return 0; 38832785Speter value = 8 * value + digit; 38932785Speter } 39032785Speter digits = p - lit - 2; 39132785Speter if (! (1 <= digits && digits <= 3)) 39232785Speter return 0; 39332785Speter break; 39432785Speter 39532785Speter default: 39632785Speter value = c; 39732785Speter if (*p++ != '\'') 39832785Speter return 0; 39932785Speter break; 40032785Speter } 40132785Speter *intptr = value; 40232785Speter return p; 40332785Speter} 40432785Speter 40532785Speter/* Scan optional printf-style SPEC of the form `-*[0-9]*(.[0-9]*)?[cdoxX]'. 40632785Speter Return the address of the character following SPEC, or zero if failure. */ 40732785Speterstatic char * 40832785Speterscan_printf_spec (spec) 40932785Speter register char *spec; 41032785Speter{ 41132785Speter register unsigned char c; 41232785Speter 41332785Speter while ((c = *spec++) == '-') 41432785Speter continue; 41532785Speter while (ISDIGIT (c)) 41632785Speter c = *spec++; 41732785Speter if (c == '.') 41832785Speter while (ISDIGIT (c = *spec++)) 41932785Speter continue; 42032785Speter switch (c) 42132785Speter { 42232785Speter case 'c': case 'd': case 'o': case 'x': case 'X': 42332785Speter return spec; 42432785Speter 42532785Speter default: 42632785Speter return 0; 42732785Speter } 42832785Speter} 429