132785Speter/* #ifdef-format output routines for GNU DIFF. 244852Speter Copyright (C) 1989, 1991, 1992, 1993, 1994, 1998 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 3044852Speterstatic char *format_group PARAMS((int, 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 *)); 3644852Speterstatic void print_ifdef_lines PARAMS((int, 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; 10644852Speter format_group (1, format, '\0', groups); 10732785Speter} 10832785Speter 10944852Speter/* If DOIT is non-zero, output 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 * 11644852Speterformat_group (doit, format, endchar, groups) 11744852Speter int doit; 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]; 14044852Speter int thendoit, elsedoit; 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]) 16244852Speter thendoit = doit, elsedoit = 0; 16332785Speter else 16444852Speter thendoit = 0, elsedoit = doit; 16544852Speter f = format_group (thendoit, f, ':', groups); 16632785Speter if (*f) 16732785Speter { 16844852Speter f = format_group (elsedoit, f + 1, ')', groups); 16932785Speter if (*f) 17032785Speter f++; 17132785Speter } 17232785Speter } 17332785Speter continue; 17432785Speter 17532785Speter case '<': 17632785Speter /* Print lines deleted from first file. */ 17744852Speter print_ifdef_lines (doit, line_format[OLD], &groups[0]); 17832785Speter continue; 17932785Speter 18032785Speter case '=': 18132785Speter /* Print common lines. */ 18244852Speter print_ifdef_lines (doit, line_format[UNCHANGED], &groups[0]); 18332785Speter continue; 18432785Speter 18532785Speter case '>': 18632785Speter /* Print lines inserted from second file. */ 18744852Speter print_ifdef_lines (doit, 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 } 21444852Speter if (doit) 21532785Speter { 21632785Speter /* Temporarily replace e.g. "%3dnx" with "%3d\0x". */ 21732785Speter *speclim = 0; 21844852Speter printf_output (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 } 23144852Speter if (doit) 23244852Speter { 23344852Speter /* Don't take the address of a register variable. */ 23444852Speter char cc = c; 23544852Speter write_output (&cc, 1); 23644852Speter } 23732785Speter } 23832785Speter return f; 23932785Speter} 24032785Speter 24132785Speter/* For the line group pair G, return the number corresponding to LETTER. 24232785Speter Return -1 if LETTER is not a group format letter. */ 24332785Speterstatic int 24432785Spetergroups_letter_value (g, letter) 24532785Speter struct group const *g; 24632785Speter int letter; 24732785Speter{ 24832785Speter if (ISUPPER (letter)) 24932785Speter { 25032785Speter g++; 25132785Speter letter = tolower (letter); 25232785Speter } 25332785Speter switch (letter) 25432785Speter { 25532785Speter case 'e': return translate_line_number (g->file, g->from) - 1; 25632785Speter case 'f': return translate_line_number (g->file, g->from); 25732785Speter case 'l': return translate_line_number (g->file, g->upto) - 1; 25832785Speter case 'm': return translate_line_number (g->file, g->upto); 25932785Speter case 'n': return g->upto - g->from; 26032785Speter default: return -1; 26132785Speter } 26232785Speter} 26332785Speter 26444852Speter/* Output using FORMAT to print the line group GROUP. 26544852Speter But do nothing if DOIT is zero. */ 26632785Speterstatic void 26744852Speterprint_ifdef_lines (doit, format, group) 26844852Speter int doit; 26932785Speter char *format; 27032785Speter struct group const *group; 27132785Speter{ 27232785Speter struct file_data const *file = group->file; 27332785Speter char const * const *linbuf = file->linbuf; 27432785Speter int from = group->from, upto = group->upto; 27532785Speter 27644852Speter if (!doit) 27732785Speter return; 27832785Speter 27932785Speter /* If possible, use a single fwrite; it's faster. */ 28032785Speter if (!tab_expand_flag && format[0] == '%') 28132785Speter { 28232785Speter if (format[1] == 'l' && format[2] == '\n' && !format[3]) 28332785Speter { 28444852Speter write_output (linbuf[from], 28544852Speter (linbuf[upto] + (linbuf[upto][-1] != '\n') 28644852Speter - linbuf[from])); 28732785Speter return; 28832785Speter } 28932785Speter if (format[1] == 'L' && !format[2]) 29032785Speter { 29144852Speter write_output (linbuf[from], 29244852Speter linbuf[upto] - linbuf[from]); 29332785Speter return; 29432785Speter } 29532785Speter } 29632785Speter 29732785Speter for (; from < upto; from++) 29832785Speter { 29932785Speter register char c; 30032785Speter register char *f = format; 30144852Speter char cc; 30232785Speter 30332785Speter while ((c = *f++) != 0) 30432785Speter { 30532785Speter if (c == '%') 30632785Speter { 30732785Speter char *spec = f; 30832785Speter switch ((c = *f++)) 30932785Speter { 31032785Speter case '%': 31132785Speter break; 31232785Speter 31332785Speter case 'l': 31432785Speter output_1_line (linbuf[from], 31532785Speter linbuf[from + 1] 31632785Speter - (linbuf[from + 1][-1] == '\n'), 0, 0); 31732785Speter continue; 31832785Speter 31932785Speter case 'L': 32032785Speter output_1_line (linbuf[from], linbuf[from + 1], 0, 0); 32132785Speter continue; 32232785Speter 32332785Speter default: 32432785Speter { 32532785Speter int value; 32632785Speter char *speclim; 32732785Speter 32832785Speter f = scan_printf_spec (spec); 32932785Speter if (!f) 33032785Speter goto bad_format; 33132785Speter speclim = f; 33232785Speter c = *f++; 33332785Speter switch (c) 33432785Speter { 33532785Speter case '\'': 33632785Speter f = scan_char_literal (f, &value); 33732785Speter if (!f) 33832785Speter goto bad_format; 33932785Speter break; 34032785Speter 34132785Speter case 'n': 34232785Speter value = translate_line_number (file, from); 34332785Speter break; 34432785Speter 34532785Speter default: 34632785Speter goto bad_format; 34732785Speter } 34832785Speter /* Temporarily replace e.g. "%3dnx" with "%3d\0x". */ 34932785Speter *speclim = 0; 35044852Speter printf_output (spec - 1, value); 35132785Speter /* Undo the temporary replacement. */ 35232785Speter *speclim = c; 35332785Speter } 35432785Speter continue; 35532785Speter 35632785Speter bad_format: 35732785Speter c = '%'; 35832785Speter f = spec; 35932785Speter break; 36032785Speter } 36132785Speter } 36244852Speter 36344852Speter /* Don't take the address of a register variable. */ 36444852Speter cc = c; 36544852Speter write_output (&cc, 1); 36632785Speter } 36732785Speter } 36832785Speter} 36932785Speter 37032785Speter/* Scan the character literal represented in the string LIT; LIT points just 37132785Speter after the initial apostrophe. Put the literal's value into *INTPTR. 37232785Speter Yield the address of the first character after the closing apostrophe, 37332785Speter or zero if the literal is ill-formed. */ 37432785Speterstatic char * 37532785Speterscan_char_literal (lit, intptr) 37632785Speter char *lit; 37732785Speter int *intptr; 37832785Speter{ 37932785Speter register char *p = lit; 38032785Speter int value, digits; 38132785Speter char c = *p++; 38232785Speter 38332785Speter switch (c) 38432785Speter { 38532785Speter case 0: 38632785Speter case '\'': 38732785Speter return 0; 38832785Speter 38932785Speter case '\\': 39032785Speter value = 0; 39132785Speter while ((c = *p++) != '\'') 39232785Speter { 39332785Speter unsigned digit = c - '0'; 39432785Speter if (8 <= digit) 39532785Speter return 0; 39632785Speter value = 8 * value + digit; 39732785Speter } 39832785Speter digits = p - lit - 2; 39932785Speter if (! (1 <= digits && digits <= 3)) 40032785Speter return 0; 40132785Speter break; 40232785Speter 40332785Speter default: 40432785Speter value = c; 40532785Speter if (*p++ != '\'') 40632785Speter return 0; 40732785Speter break; 40832785Speter } 40932785Speter *intptr = value; 41032785Speter return p; 41132785Speter} 41232785Speter 41332785Speter/* Scan optional printf-style SPEC of the form `-*[0-9]*(.[0-9]*)?[cdoxX]'. 41432785Speter Return the address of the character following SPEC, or zero if failure. */ 41532785Speterstatic char * 41632785Speterscan_printf_spec (spec) 41732785Speter register char *spec; 41832785Speter{ 41932785Speter register unsigned char c; 42032785Speter 42132785Speter while ((c = *spec++) == '-') 42232785Speter continue; 42332785Speter while (ISDIGIT (c)) 42432785Speter c = *spec++; 42532785Speter if (c == '.') 42632785Speter while (ISDIGIT (c = *spec++)) 42732785Speter continue; 42832785Speter switch (c) 42932785Speter { 43032785Speter case 'c': case 'd': case 'o': case 'x': case 'X': 43132785Speter return spec; 43232785Speter 43332785Speter default: 43432785Speter return 0; 43532785Speter } 43632785Speter} 437