1 /* sdiff-format output routines for GNU DIFF. 2 Copyright (C) 1991, 1992, 1993, 1998 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 static unsigned print_half_line PARAMS((char const * const *, unsigned, unsigned)); 25 static unsigned tab_from_to PARAMS((unsigned, unsigned)); 26 static void print_1sdiff_line PARAMS((char const * const *, int, char const * const *)); 27 static void print_sdiff_common_lines PARAMS((int, int)); 28 static void print_sdiff_hunk PARAMS((struct change *)); 29 30 /* Next line number to be printed in the two input files. */ 31 static int next0, next1; 32 33 /* Print the edit-script SCRIPT as a sdiff style output. */ 34 35 void 36 print_sdiff_script (script) 37 struct change *script; 38 { 39 begin_output (); 40 41 next0 = next1 = - files[0].prefix_lines; 42 print_script (script, find_change, print_sdiff_hunk); 43 44 print_sdiff_common_lines (files[0].valid_lines, files[1].valid_lines); 45 } 46 47 /* Tab from column FROM to column TO, where FROM <= TO. Yield TO. */ 48 49 static unsigned 50 tab_from_to (from, to) 51 unsigned from, to; 52 { 53 unsigned tab; 54 55 if (! tab_expand_flag) 56 for (tab = from + TAB_WIDTH - from % TAB_WIDTH; tab <= to; tab += TAB_WIDTH) 57 { 58 write_output ("\t", 1); 59 from = tab; 60 } 61 while (from++ < to) 62 write_output (" ", 1); 63 return to; 64 } 65 66 /* 67 * Print the text for half an sdiff line. This means truncate to width 68 * observing tabs, and trim a trailing newline. Returns the last column 69 * written (not the number of chars). 70 */ 71 static unsigned 72 print_half_line (line, indent, out_bound) 73 char const * const *line; 74 unsigned indent, out_bound; 75 { 76 register unsigned in_position = 0, out_position = 0; 77 register char const 78 *text_pointer = line[0], 79 *text_limit = line[1]; 80 81 while (text_pointer < text_limit) 82 { 83 register unsigned char c = *text_pointer++; 84 /* We use CC to avoid taking the address of the register 85 variable C. */ 86 char cc; 87 88 switch (c) 89 { 90 case '\t': 91 { 92 unsigned spaces = TAB_WIDTH - in_position % TAB_WIDTH; 93 if (in_position == out_position) 94 { 95 unsigned tabstop = out_position + spaces; 96 if (tab_expand_flag) 97 { 98 if (out_bound < tabstop) 99 tabstop = out_bound; 100 for (; out_position < tabstop; out_position++) 101 write_output (" ", 1); 102 } 103 else 104 if (tabstop < out_bound) 105 { 106 out_position = tabstop; 107 cc = c; 108 write_output (&cc, 1); 109 } 110 } 111 in_position += spaces; 112 } 113 break; 114 115 case '\r': 116 { 117 cc = c; 118 write_output (&cc, 1); 119 tab_from_to (0, indent); 120 in_position = out_position = 0; 121 } 122 break; 123 124 case '\b': 125 if (in_position != 0 && --in_position < out_bound) 126 { 127 if (out_position <= in_position) 128 /* Add spaces to make up for suppressed tab past out_bound. */ 129 for (; out_position < in_position; out_position++) 130 write_output (" ", 1); 131 else 132 { 133 out_position = in_position; 134 cc = c; 135 write_output (&cc, 1); 136 } 137 } 138 break; 139 140 case '\f': 141 case '\v': 142 control_char: 143 if (in_position < out_bound) 144 { 145 cc = c; 146 write_output (&cc, 1); 147 } 148 break; 149 150 default: 151 if (! ISPRINT (c)) 152 goto control_char; 153 /* falls through */ 154 case ' ': 155 if (in_position++ < out_bound) 156 { 157 out_position = in_position; 158 cc = c; 159 write_output (&cc, 1); 160 } 161 break; 162 163 case '\n': 164 return out_position; 165 } 166 } 167 168 return out_position; 169 } 170 171 /* 172 * Print side by side lines with a separator in the middle. 173 * 0 parameters are taken to indicate white space text. 174 * Blank lines that can easily be caught are reduced to a single newline. 175 */ 176 177 static void 178 print_1sdiff_line (left, sep, right) 179 char const * const *left; 180 int sep; 181 char const * const *right; 182 { 183 unsigned hw = sdiff_half_width, c2o = sdiff_column2_offset; 184 unsigned col = 0; 185 int put_newline = 0; 186 187 if (left) 188 { 189 if (left[1][-1] == '\n') 190 put_newline = 1; 191 col = print_half_line (left, 0, hw); 192 } 193 194 if (sep != ' ') 195 { 196 char cc; 197 198 col = tab_from_to (col, (hw + c2o - 1) / 2) + 1; 199 if (sep == '|' && put_newline != (right[1][-1] == '\n')) 200 sep = put_newline ? '/' : '\\'; 201 cc = sep; 202 write_output (&cc, 1); 203 } 204 205 if (right) 206 { 207 if (right[1][-1] == '\n') 208 put_newline = 1; 209 if (**right != '\n') 210 { 211 col = tab_from_to (col, c2o); 212 print_half_line (right, col, hw); 213 } 214 } 215 216 if (put_newline) 217 write_output ("\n", 1); 218 } 219 220 /* Print lines common to both files in side-by-side format. */ 221 static void 222 print_sdiff_common_lines (limit0, limit1) 223 int limit0, limit1; 224 { 225 int i0 = next0, i1 = next1; 226 227 if (! sdiff_skip_common_lines && (i0 != limit0 || i1 != limit1)) 228 { 229 if (sdiff_help_sdiff) 230 printf_output ("i%d,%d\n", limit0 - i0, limit1 - i1); 231 232 if (! sdiff_left_only) 233 { 234 while (i0 != limit0 && i1 != limit1) 235 print_1sdiff_line (&files[0].linbuf[i0++], ' ', &files[1].linbuf[i1++]); 236 while (i1 != limit1) 237 print_1sdiff_line (0, ')', &files[1].linbuf[i1++]); 238 } 239 while (i0 != limit0) 240 print_1sdiff_line (&files[0].linbuf[i0++], '(', 0); 241 } 242 243 next0 = limit0; 244 next1 = limit1; 245 } 246 247 /* Print a hunk of an sdiff diff. 248 This is a contiguous portion of a complete edit script, 249 describing changes in consecutive lines. */ 250 251 static void 252 print_sdiff_hunk (hunk) 253 struct change *hunk; 254 { 255 int first0, last0, first1, last1, deletes, inserts; 256 register int i, j; 257 258 /* Determine range of line numbers involved in each file. */ 259 analyze_hunk (hunk, &first0, &last0, &first1, &last1, &deletes, &inserts); 260 if (!deletes && !inserts) 261 return; 262 263 /* Print out lines up to this change. */ 264 print_sdiff_common_lines (first0, first1); 265 266 if (sdiff_help_sdiff) 267 printf_output ("c%d,%d\n", last0 - first0 + 1, last1 - first1 + 1); 268 269 /* Print ``xxx | xxx '' lines */ 270 if (inserts && deletes) 271 { 272 for (i = first0, j = first1; i <= last0 && j <= last1; ++i, ++j) 273 print_1sdiff_line (&files[0].linbuf[i], '|', &files[1].linbuf[j]); 274 deletes = i <= last0; 275 inserts = j <= last1; 276 next0 = first0 = i; 277 next1 = first1 = j; 278 } 279 280 281 /* Print `` > xxx '' lines */ 282 if (inserts) 283 { 284 for (j = first1; j <= last1; ++j) 285 print_1sdiff_line (0, '>', &files[1].linbuf[j]); 286 next1 = j; 287 } 288 289 /* Print ``xxx < '' lines */ 290 if (deletes) 291 { 292 for (i = first0; i <= last0; ++i) 293 print_1sdiff_line (&files[0].linbuf[i], '<', 0); 294 next0 = i; 295 } 296 } 297