176861Skris/* sdiff-format output routines for GNU DIFF. 276861Skris 376861Skris Copyright (C) 1991, 1992, 1993, 1998, 2001, 2002, 2004 Free 476861Skris Software Foundation, Inc. 576861Skris 676861Skris This file is part of GNU DIFF. 776861Skris 876861Skris GNU DIFF is distributed in the hope that it will be useful, 996316Sobrien but WITHOUT ANY WARRANTY. No author or distributor 1096316Sobrien accepts responsibility to anyone for the consequences of using it 1176861Skris or for whether it serves any particular purpose or works at all, 1276861Skris unless he says so in writing. Refer to the GNU DIFF General Public 1376861Skris License for full details. 1499542Sobrien 1599542Sobrien Everyone is granted permission to copy, modify and redistribute 1699542Sobrien GNU DIFF, but only under the conditions described in the 1776861Skris GNU DIFF General Public License. A copy of this license is 1876861Skris supposed to have been given to you along with GNU DIFF so you 1996316Sobrien can know your rights and responsibilities. It should be in a 2076861Skris file named COPYING. Among other things, the copyright notice 2187976Sobrien and this notice must be preserved on all copies. */ 2287976Sobrien 2387976Sobrien#include "diff.h" 2487976Sobrien 2587976Sobrienstatic void print_sdiff_common_lines (lin, lin); 2687976Sobrienstatic void print_sdiff_hunk (struct change *); 2794332Sobrien 2894332Sobrien/* Next line number to be printed in the two input files. */ 2994332Sobrienstatic lin next0, next1; 3094332Sobrien 3194332Sobrien/* Print the edit-script SCRIPT as a sdiff style output. */ 3294332Sobrien 3394332Sobrienvoid 3494332Sobrienprint_sdiff_script (struct change *script) 3588936Sdwmalone{ 3688936Sdwmalone begin_output (); 3788936Sdwmalone 3888936Sdwmalone next0 = next1 = - files[0].prefix_lines; 3976861Skris print_script (script, find_change, print_sdiff_hunk); 4076861Skris 4176861Skris print_sdiff_common_lines (files[0].valid_lines, files[1].valid_lines); 4276861Skris} 4376861Skris 4476861Skris/* Tab from column FROM to column TO, where FROM <= TO. Yield TO. */ 4576861Skris 4696316Sobrienstatic size_t 4796316Sobrientab_from_to (size_t from, size_t to) 4899542Sobrien{ 4999542Sobrien FILE *out = outfile; 5099542Sobrien size_t tab; 5176861Skris size_t tab_size = tabsize; 5276861Skris 5376861Skris if (!expand_tabs) 5476861Skris for (tab = from + tab_size - from % tab_size; tab <= to; tab += tab_size) 5576861Skris { 5676861Skris putc ('\t', out); 5794127Sru from = tab; 5894127Sru } 5994127Sru while (from++ < to) 6094127Sru putc (' ', out); 6194127Sru return to; 62} 63 64/* Print the text for half an sdiff line. This means truncate to 65 width observing tabs, and trim a trailing newline. Return the 66 last column written (not the number of chars). */ 67 68static size_t 69print_half_line (char const *const *line, size_t indent, size_t out_bound) 70{ 71 FILE *out = outfile; 72 register size_t in_position = 0; 73 register size_t out_position = 0; 74 register char const *text_pointer = line[0]; 75 register char const *text_limit = line[1]; 76 77 while (text_pointer < text_limit) 78 { 79 register unsigned char c = *text_pointer++; 80 81 switch (c) 82 { 83 case '\t': 84 { 85 size_t spaces = tabsize - in_position % tabsize; 86 if (in_position == out_position) 87 { 88 size_t tabstop = out_position + spaces; 89 if (expand_tabs) 90 { 91 if (out_bound < tabstop) 92 tabstop = out_bound; 93 for (; out_position < tabstop; out_position++) 94 putc (' ', out); 95 } 96 else 97 if (tabstop < out_bound) 98 { 99 out_position = tabstop; 100 putc (c, out); 101 } 102 } 103 in_position += spaces; 104 } 105 break; 106 107 case '\r': 108 { 109 putc (c, out); 110 tab_from_to (0, indent); 111 in_position = out_position = 0; 112 } 113 break; 114 115 case '\b': 116 if (in_position != 0 && --in_position < out_bound) 117 { 118 if (out_position <= in_position) 119 /* Add spaces to make up for suppressed tab past out_bound. */ 120 for (; out_position < in_position; out_position++) 121 putc (' ', out); 122 else 123 { 124 out_position = in_position; 125 putc (c, out); 126 } 127 } 128 break; 129 130 case '\f': 131 case '\v': 132 control_char: 133 if (in_position < out_bound) 134 putc (c, out); 135 break; 136 137 default: 138 if (! isprint (c)) 139 goto control_char; 140 /* falls through */ 141 case ' ': 142 if (in_position++ < out_bound) 143 { 144 out_position = in_position; 145 putc (c, out); 146 } 147 break; 148 149 case '\n': 150 return out_position; 151 } 152 } 153 154 return out_position; 155} 156 157/* Print side by side lines with a separator in the middle. 158 0 parameters are taken to indicate white space text. 159 Blank lines that can easily be caught are reduced to a single newline. */ 160 161static void 162print_1sdiff_line (char const *const *left, char sep, 163 char const *const *right) 164{ 165 FILE *out = outfile; 166 size_t hw = sdiff_half_width; 167 size_t c2o = sdiff_column2_offset; 168 size_t col = 0; 169 bool put_newline = false; 170 171 if (left) 172 { 173 put_newline |= left[1][-1] == '\n'; 174 col = print_half_line (left, 0, hw); 175 } 176 177 if (sep != ' ') 178 { 179 col = tab_from_to (col, (hw + c2o - 1) / 2) + 1; 180 if (sep == '|' && put_newline != (right[1][-1] == '\n')) 181 sep = put_newline ? '/' : '\\'; 182 putc (sep, out); 183 } 184 185 if (right) 186 { 187 put_newline |= right[1][-1] == '\n'; 188 if (**right != '\n') 189 { 190 col = tab_from_to (col, c2o); 191 print_half_line (right, col, hw); 192 } 193 } 194 195 if (put_newline) 196 putc ('\n', out); 197} 198 199/* Print lines common to both files in side-by-side format. */ 200static void 201print_sdiff_common_lines (lin limit0, lin limit1) 202{ 203 lin i0 = next0, i1 = next1; 204 205 if (!suppress_common_lines && (i0 != limit0 || i1 != limit1)) 206 { 207 if (sdiff_merge_assist) 208 { 209 long int len0 = limit0 - i0; 210 long int len1 = limit1 - i1; 211 fprintf (outfile, "i%ld,%ld\n", len0, len1); 212 } 213 214 if (!left_column) 215 { 216 while (i0 != limit0 && i1 != limit1) 217 print_1sdiff_line (&files[0].linbuf[i0++], ' ', 218 &files[1].linbuf[i1++]); 219 while (i1 != limit1) 220 print_1sdiff_line (0, ')', &files[1].linbuf[i1++]); 221 } 222 while (i0 != limit0) 223 print_1sdiff_line (&files[0].linbuf[i0++], '(', 0); 224 } 225 226 next0 = limit0; 227 next1 = limit1; 228} 229 230/* Print a hunk of an sdiff diff. 231 This is a contiguous portion of a complete edit script, 232 describing changes in consecutive lines. */ 233 234static void 235print_sdiff_hunk (struct change *hunk) 236{ 237 lin first0, last0, first1, last1; 238 register lin i, j; 239 240 /* Determine range of line numbers involved in each file. */ 241 enum changes changes = 242 analyze_hunk (hunk, &first0, &last0, &first1, &last1); 243 if (!changes) 244 return; 245 246 /* Print out lines up to this change. */ 247 print_sdiff_common_lines (first0, first1); 248 249 if (sdiff_merge_assist) 250 { 251 long int len0 = last0 - first0 + 1; 252 long int len1 = last1 - first1 + 1; 253 fprintf (outfile, "c%ld,%ld\n", len0, len1); 254 } 255 256 /* Print ``xxx | xxx '' lines */ 257 if (changes == CHANGED) 258 { 259 for (i = first0, j = first1; i <= last0 && j <= last1; i++, j++) 260 print_1sdiff_line (&files[0].linbuf[i], '|', &files[1].linbuf[j]); 261 changes = (i <= last0 ? OLD : 0) + (j <= last1 ? NEW : 0); 262 next0 = first0 = i; 263 next1 = first1 = j; 264 } 265 266 /* Print `` > xxx '' lines */ 267 if (changes & NEW) 268 { 269 for (j = first1; j <= last1; ++j) 270 print_1sdiff_line (0, '>', &files[1].linbuf[j]); 271 next1 = j; 272 } 273 274 /* Print ``xxx < '' lines */ 275 if (changes & OLD) 276 { 277 for (i = first0; i <= last0; ++i) 278 print_1sdiff_line (&files[0].linbuf[i], '<', 0); 279 next0 = i; 280 } 281} 282