| Commit | Line | Data |
|---|---|---|
| 55867888 PA |
1 | /* sdiff-format output routines for GNU DIFF. |
| 2 | ||
| 44b87433 JM |
3 | Copyright (C) 1991-1993, 1998, 2001-2002, 2004, 2009-2010 Free Software |
| 4 | Foundation, Inc. | |
| 55867888 PA |
5 | |
| 6 | This file is part of GNU DIFF. | |
| 7 | ||
| 8 | GNU DIFF is distributed in the hope that it will be useful, | |
| 9 | but WITHOUT ANY WARRANTY. No author or distributor | |
| 10 | accepts responsibility to anyone for the consequences of using it | |
| 11 | or for whether it serves any particular purpose or works at all, | |
| 44b87433 | 12 | unless he says so in writing. Refer to the GNU General Public |
| 55867888 PA |
13 | License for full details. |
| 14 | ||
| 15 | Everyone is granted permission to copy, modify and redistribute | |
| 16 | GNU DIFF, but only under the conditions described in the | |
| 44b87433 | 17 | GNU General Public License. A copy of this license is |
| 55867888 PA |
18 | supposed to have been given to you along with GNU DIFF so you |
| 19 | can know your rights and responsibilities. It should be in a | |
| 20 | file named COPYING. Among other things, the copyright notice | |
| 21 | and this notice must be preserved on all copies. */ | |
| 22 | ||
| 23 | #include "diff.h" | |
| 24 | ||
| 44b87433 | 25 | #include <wchar.h> |
| d76863bd | 26 | #include <gnuwidechar.h> |
| 44b87433 | 27 | |
| 55867888 PA |
28 | static void print_sdiff_common_lines (lin, lin); |
| 29 | static void print_sdiff_hunk (struct change *); | |
| 30 | ||
| 31 | /* Next line number to be printed in the two input files. */ | |
| 32 | static lin next0, next1; | |
| 33 | ||
| 34 | /* Print the edit-script SCRIPT as a sdiff style output. */ | |
| 35 | ||
| 36 | void | |
| 37 | print_sdiff_script (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 size_t | |
| 50 | tab_from_to (size_t from, size_t to) | |
| 51 | { | |
| 52 | FILE *out = outfile; | |
| 53 | size_t tab; | |
| 54 | size_t tab_size = tabsize; | |
| 55 | ||
| 56 | if (!expand_tabs) | |
| 57 | for (tab = from + tab_size - from % tab_size; tab <= to; tab += tab_size) | |
| 58 | { | |
| 59 | putc ('\t', out); | |
| 60 | from = tab; | |
| 61 | } | |
| 62 | while (from++ < to) | |
| 63 | putc (' ', out); | |
| 64 | return to; | |
| 65 | } | |
| 66 | ||
| 67 | /* Print the text for half an sdiff line. This means truncate to | |
| 68 | width observing tabs, and trim a trailing newline. Return the | |
| 69 | last column written (not the number of chars). */ | |
| 70 | ||
| 71 | static size_t | |
| 72 | print_half_line (char const *const *line, size_t indent, size_t out_bound) | |
| 73 | { | |
| 74 | FILE *out = outfile; | |
| 75 | register size_t in_position = 0; | |
| 76 | register size_t out_position = 0; | |
| 77 | register char const *text_pointer = line[0]; | |
| 78 | register char const *text_limit = line[1]; | |
| 44b87433 | 79 | mbstate_t mbstate = { 0 }; |
| 55867888 PA |
80 | |
| 81 | while (text_pointer < text_limit) | |
| 82 | { | |
| 44b87433 JM |
83 | char const *tp0 = text_pointer; |
| 84 | register char c = *text_pointer++; | |
| 55867888 PA |
85 | |
| 86 | switch (c) | |
| 87 | { | |
| 88 | case '\t': | |
| 89 | { | |
| 90 | size_t spaces = tabsize - in_position % tabsize; | |
| 91 | if (in_position == out_position) | |
| 92 | { | |
| 93 | size_t tabstop = out_position + spaces; | |
| 94 | if (expand_tabs) | |
| 95 | { | |
| 96 | if (out_bound < tabstop) | |
| 97 | tabstop = out_bound; | |
| 98 | for (; out_position < tabstop; out_position++) | |
| 99 | putc (' ', out); | |
| 100 | } | |
| 101 | else | |
| 102 | if (tabstop < out_bound) | |
| 103 | { | |
| 104 | out_position = tabstop; | |
| 105 | putc (c, out); | |
| 106 | } | |
| 107 | } | |
| 108 | in_position += spaces; | |
| 109 | } | |
| 110 | break; | |
| 111 | ||
| 112 | case '\r': | |
| 113 | { | |
| 114 | putc (c, out); | |
| 115 | tab_from_to (0, indent); | |
| 116 | in_position = out_position = 0; | |
| 117 | } | |
| 118 | break; | |
| 119 | ||
| 120 | case '\b': | |
| 121 | if (in_position != 0 && --in_position < out_bound) | |
| 122 | { | |
| 123 | if (out_position <= in_position) | |
| 124 | /* Add spaces to make up for suppressed tab past out_bound. */ | |
| 125 | for (; out_position < in_position; out_position++) | |
| 126 | putc (' ', out); | |
| 127 | else | |
| 128 | { | |
| 129 | out_position = in_position; | |
| 130 | putc (c, out); | |
| 131 | } | |
| 132 | } | |
| 133 | break; | |
| 134 | ||
| 44b87433 JM |
135 | default: |
| 136 | { | |
| 137 | wchar_t wc; | |
| 138 | size_t bytes = mbrtowc (&wc, tp0, text_limit - tp0, &mbstate); | |
| 139 | ||
| 140 | if (0 < bytes && bytes < (size_t) -2) | |
| 141 | { | |
| d76863bd | 142 | int width = special_wcwidth (wc); |
| 44b87433 JM |
143 | if (0 < width) |
| 144 | in_position += width; | |
| 145 | if (in_position <= out_bound) | |
| 146 | { | |
| 147 | out_position = in_position; | |
| 148 | fwrite (tp0, 1, bytes, stdout); | |
| 149 | } | |
| 150 | text_pointer = tp0 + bytes; | |
| 151 | break; | |
| 152 | } | |
| 153 | } | |
| 154 | /* Fall through. */ | |
| 55867888 PA |
155 | case '\f': |
| 156 | case '\v': | |
| 55867888 PA |
157 | if (in_position < out_bound) |
| 158 | putc (c, out); | |
| 159 | break; | |
| 160 | ||
| 44b87433 JM |
161 | case ' ': case '!': case '"': case '#': case '%': |
| 162 | case '&': case '\'': case '(': case ')': case '*': | |
| 163 | case '+': case ',': case '-': case '.': case '/': | |
| 164 | case '0': case '1': case '2': case '3': case '4': | |
| 165 | case '5': case '6': case '7': case '8': case '9': | |
| 166 | case ':': case ';': case '<': case '=': case '>': | |
| 167 | case '?': | |
| 168 | case 'A': case 'B': case 'C': case 'D': case 'E': | |
| 169 | case 'F': case 'G': case 'H': case 'I': case 'J': | |
| 170 | case 'K': case 'L': case 'M': case 'N': case 'O': | |
| 171 | case 'P': case 'Q': case 'R': case 'S': case 'T': | |
| 172 | case 'U': case 'V': case 'W': case 'X': case 'Y': | |
| 173 | case 'Z': | |
| 174 | case '[': case '\\': case ']': case '^': case '_': | |
| 175 | case 'a': case 'b': case 'c': case 'd': case 'e': | |
| 176 | case 'f': case 'g': case 'h': case 'i': case 'j': | |
| 177 | case 'k': case 'l': case 'm': case 'n': case 'o': | |
| 178 | case 'p': case 'q': case 'r': case 's': case 't': | |
| 179 | case 'u': case 'v': case 'w': case 'x': case 'y': | |
| 180 | case 'z': case '{': case '|': case '}': case '~': | |
| 181 | /* These characters are printable ASCII characters. */ | |
| 55867888 PA |
182 | if (in_position++ < out_bound) |
| 183 | { | |
| 184 | out_position = in_position; | |
| 185 | putc (c, out); | |
| 186 | } | |
| 187 | break; | |
| 188 | ||
| 189 | case '\n': | |
| 190 | return out_position; | |
| 191 | } | |
| 192 | } | |
| 193 | ||
| 194 | return out_position; | |
| 195 | } | |
| 196 | ||
| 197 | /* Print side by side lines with a separator in the middle. | |
| 198 | 0 parameters are taken to indicate white space text. | |
| 199 | Blank lines that can easily be caught are reduced to a single newline. */ | |
| 200 | ||
| 201 | static void | |
| 202 | print_1sdiff_line (char const *const *left, char sep, | |
| 203 | char const *const *right) | |
| 204 | { | |
| 205 | FILE *out = outfile; | |
| 206 | size_t hw = sdiff_half_width; | |
| 207 | size_t c2o = sdiff_column2_offset; | |
| 208 | size_t col = 0; | |
| 209 | bool put_newline = false; | |
| 210 | ||
| 211 | if (left) | |
| 212 | { | |
| 213 | put_newline |= left[1][-1] == '\n'; | |
| 214 | col = print_half_line (left, 0, hw); | |
| 215 | } | |
| 216 | ||
| 217 | if (sep != ' ') | |
| 218 | { | |
| 219 | col = tab_from_to (col, (hw + c2o - 1) / 2) + 1; | |
| 220 | if (sep == '|' && put_newline != (right[1][-1] == '\n')) | |
| 221 | sep = put_newline ? '/' : '\\'; | |
| 222 | putc (sep, out); | |
| 223 | } | |
| 224 | ||
| 225 | if (right) | |
| 226 | { | |
| 227 | put_newline |= right[1][-1] == '\n'; | |
| 228 | if (**right != '\n') | |
| 229 | { | |
| 230 | col = tab_from_to (col, c2o); | |
| 231 | print_half_line (right, col, hw); | |
| 232 | } | |
| 233 | } | |
| 234 | ||
| 235 | if (put_newline) | |
| 236 | putc ('\n', out); | |
| 237 | } | |
| 238 | ||
| 239 | /* Print lines common to both files in side-by-side format. */ | |
| 240 | static void | |
| 241 | print_sdiff_common_lines (lin limit0, lin limit1) | |
| 242 | { | |
| 243 | lin i0 = next0, i1 = next1; | |
| 244 | ||
| 245 | if (!suppress_common_lines && (i0 != limit0 || i1 != limit1)) | |
| 246 | { | |
| 247 | if (sdiff_merge_assist) | |
| 248 | { | |
| 249 | long int len0 = limit0 - i0; | |
| 250 | long int len1 = limit1 - i1; | |
| 251 | fprintf (outfile, "i%ld,%ld\n", len0, len1); | |
| 252 | } | |
| 253 | ||
| 254 | if (!left_column) | |
| 255 | { | |
| 256 | while (i0 != limit0 && i1 != limit1) | |
| 257 | print_1sdiff_line (&files[0].linbuf[i0++], ' ', | |
| 258 | &files[1].linbuf[i1++]); | |
| 259 | while (i1 != limit1) | |
| 260 | print_1sdiff_line (0, ')', &files[1].linbuf[i1++]); | |
| 261 | } | |
| 262 | while (i0 != limit0) | |
| 263 | print_1sdiff_line (&files[0].linbuf[i0++], '(', 0); | |
| 264 | } | |
| 265 | ||
| 266 | next0 = limit0; | |
| 267 | next1 = limit1; | |
| 268 | } | |
| 269 | ||
| 270 | /* Print a hunk of an sdiff diff. | |
| 271 | This is a contiguous portion of a complete edit script, | |
| 272 | describing changes in consecutive lines. */ | |
| 273 | ||
| 274 | static void | |
| 275 | print_sdiff_hunk (struct change *hunk) | |
| 276 | { | |
| 277 | lin first0, last0, first1, last1; | |
| 278 | register lin i, j; | |
| 279 | ||
| 280 | /* Determine range of line numbers involved in each file. */ | |
| 281 | enum changes changes = | |
| 282 | analyze_hunk (hunk, &first0, &last0, &first1, &last1); | |
| 283 | if (!changes) | |
| 284 | return; | |
| 285 | ||
| 286 | /* Print out lines up to this change. */ | |
| 287 | print_sdiff_common_lines (first0, first1); | |
| 288 | ||
| 289 | if (sdiff_merge_assist) | |
| 290 | { | |
| 291 | long int len0 = last0 - first0 + 1; | |
| 292 | long int len1 = last1 - first1 + 1; | |
| 293 | fprintf (outfile, "c%ld,%ld\n", len0, len1); | |
| 294 | } | |
| 295 | ||
| 296 | /* Print ``xxx | xxx '' lines */ | |
| 297 | if (changes == CHANGED) | |
| 298 | { | |
| 299 | for (i = first0, j = first1; i <= last0 && j <= last1; i++, j++) | |
| 300 | print_1sdiff_line (&files[0].linbuf[i], '|', &files[1].linbuf[j]); | |
| 301 | changes = (i <= last0 ? OLD : 0) + (j <= last1 ? NEW : 0); | |
| 302 | next0 = first0 = i; | |
| 303 | next1 = first1 = j; | |
| 304 | } | |
| 305 | ||
| 306 | /* Print `` > xxx '' lines */ | |
| 307 | if (changes & NEW) | |
| 308 | { | |
| 309 | for (j = first1; j <= last1; ++j) | |
| 310 | print_1sdiff_line (0, '>', &files[1].linbuf[j]); | |
| 311 | next1 = j; | |
| 312 | } | |
| 313 | ||
| 314 | /* Print ``xxx < '' lines */ | |
| 315 | if (changes & OLD) | |
| 316 | { | |
| 317 | for (i = first0; i <= last0; ++i) | |
| 318 | print_1sdiff_line (&files[0].linbuf[i], '<', 0); | |
| 319 | next0 = i; | |
| 320 | } | |
| 321 | } |