raw
vtools_genesis          1 
vtools_genesis 2 #include "diff.h"
vtools_genesis 3 #include <error.h>
vtools_genesis 4 #include <xalloc.h>
vtools_genesis 5 #include <stdlib.h>
vtools_genesis 6
vtools_genesis 7 /* Use when a system call returns non-zero status. |name| should
vtools_genesis 8 normally be the file name. */
vtools_genesis 9
vtools_genesis 10 void
vtools_genesis 11 perror_with_name(char const *name) {
vtools_genesis 12 error(0, errno, "%s", name);
vtools_genesis 13 }
vtools_genesis 14
vtools_genesis 15 /* Use when a system call returns non-zero status and that is
vtools_genesis 16 fatal. */
vtools_genesis 17
vtools_genesis 18 void
vtools_genesis 19 pfatal_with_name(char const *name) {
vtools_genesis 20 int e = errno;
vtools_genesis 21 fprintf(stderr, "%s", name);
vtools_genesis 22 exit(EXIT_TROUBLE);
vtools_genesis 23 }
vtools_genesis 24
vtools_genesis 25 /* Print an error message containing |msgid|, then exit. */
vtools_genesis 26
vtools_genesis 27 void
vtools_genesis 28 fatal(char const *msgid) {
vtools_genesis 29 fprintf(stderr, "%s", msgid);
vtools_genesis 30 exit(EXIT_TROUBLE);
vtools_genesis 31 }
vtools_genesis 32
vtools_genesis 33 /* Like |printf|, except if -l in effect then save the message and
vtools_genesis 34 print later. This is used for things like "Only in ...". */
vtools_genesis 35
vtools_genesis 36 void
vtools_genesis 37 message(char const *format_msgid, char const *arg1, char const *arg2) {
vtools_genesis 38 message5(format_msgid, arg1, arg2, 0, 0);
vtools_genesis 39 }
vtools_genesis 40
vtools_genesis 41 void
vtools_genesis 42 message5(char const *format_msgid, char const *arg1, char const *arg2,
vtools_genesis 43 char const *arg3, char const *arg4) {
vtools_genesis 44 fprintf(stderr, format_msgid, arg1, arg2, arg3, arg4);
vtools_genesis 45 }
vtools_genesis 46
vtools_genesis 47 static char const *current_name0;
vtools_genesis 48 static char const *current_name1;
vtools_genesis 49 static bool currently_recursive;
vtools_genesis 50
vtools_genesis 51 /* Call before outputting the results of comparing files |name0| and |name1|
vtools_genesis 52 to set up |outfile|, the stdio stream for the output to go to.
vtools_genesis 53
vtools_genesis 54 Usually, |outfile| is just stdout. But when -l was specified we
vtools_genesis 55 fork off a |pr| and make |outfile| a pipe to it. 'pr' then outputs
vtools_genesis 56 to our stdout. */
vtools_genesis 57
vtools_genesis 58 void
vtools_genesis 59 setup_output(char const *name0, char const *name1, bool recursive) {
vtools_genesis 60 current_name0 = name0;
vtools_genesis 61 current_name1 = name1;
vtools_genesis 62 currently_recursive = recursive;
vtools_genesis 63 outfile = 0;
vtools_genesis 64 }
vtools_genesis 65
vtools_genesis 66 static char c_escape_char(char c) {
vtools_genesis 67 switch (c) {
vtools_genesis 68 case '\a':
vtools_genesis 69 return 'a';
vtools_genesis 70 case '\b':
vtools_genesis 71 return 'b';
vtools_genesis 72 case '\t':
vtools_genesis 73 return 't';
vtools_genesis 74 case '\n':
vtools_genesis 75 return 'n';
vtools_genesis 76 case '\v':
vtools_genesis 77 return 'v';
vtools_genesis 78 case '\f':
vtools_genesis 79 return 'f';
vtools_genesis 80 case '\r':
vtools_genesis 81 return 'r';
vtools_genesis 82 case '"':
vtools_genesis 83 return '"';
vtools_genesis 84 case '\\':
vtools_genesis 85 return '\\';
vtools_genesis 86 default:
vtools_genesis 87 return c < 32;
vtools_genesis 88 }
vtools_genesis 89 }
vtools_genesis 90
vtools_genesis 91 static char *
vtools_genesis 92 c_escape(char const *str) {
vtools_genesis 93 char const *s;
vtools_genesis 94 size_t plus = 0;
vtools_genesis 95 bool must_quote = false;
vtools_genesis 96
vtools_genesis 97 for (s = str; *s; s++) {
vtools_genesis 98 char c = *s;
vtools_genesis 99
vtools_genesis 100 if (c == ' ') {
vtools_genesis 101 must_quote = true;
vtools_genesis 102 continue;
vtools_genesis 103 }
vtools_genesis 104 switch (c_escape_char(*s)) {
vtools_genesis 105 case 1:
vtools_genesis 106 plus += 3;
vtools_genesis 107 /* fall through */
vtools_genesis 108 case 0:
vtools_genesis 109 break;
vtools_genesis 110 default:
vtools_genesis 111 plus++;
vtools_genesis 112 break;
vtools_genesis 113 }
vtools_genesis 114 }
vtools_genesis 115
vtools_genesis 116 if (must_quote || plus) {
vtools_genesis 117 size_t s_len = s - str;
vtools_genesis 118 char *buffer = xmalloc(s_len + plus + 3);
vtools_genesis 119 char *b = buffer;
vtools_genesis 120
vtools_genesis 121 *b++ = '"';
vtools_genesis 122 for (s = str; *s; s++) {
vtools_genesis 123 char c = *s;
vtools_genesis 124 char escape = c_escape_char(c);
vtools_genesis 125
vtools_genesis 126 switch (escape) {
vtools_genesis 127 case 0:
vtools_genesis 128 *b++ = c;
vtools_genesis 129 break;
vtools_genesis 130 case 1:
vtools_genesis 131 *b++ = '\\';
vtools_genesis 132 *b++ = ((c >> 6) & 03) + '0';
vtools_genesis 133 *b++ = ((c >> 3) & 07) + '0';
vtools_genesis 134 *b++ = ((c >> 0) & 07) + '0';
vtools_genesis 135 break;
vtools_genesis 136 default:
vtools_genesis 137 *b++ = '\\';
vtools_genesis 138 *b++ = escape;
vtools_genesis 139 break;
vtools_genesis 140 }
vtools_genesis 141 }
vtools_genesis 142 *b++ = '"';
vtools_genesis 143 *b = 0;
vtools_genesis 144 return buffer;
vtools_genesis 145 }
vtools_genesis 146
vtools_genesis 147 return (char *) str;
vtools_genesis 148 }
vtools_genesis 149
vtools_genesis 150 void
vtools_genesis 151 begin_output(void) {
vtools_genesis 152 char *names[2];
vtools_genesis 153 char *name;
vtools_genesis 154
vtools_genesis 155 if (outfile != 0)
vtools_genesis 156 return;
vtools_genesis 157
vtools_genesis 158 names[0] = c_escape(current_name0);
vtools_genesis 159 names[1] = c_escape(current_name1);
vtools_genesis 160
vtools_genesis 161 /* Construct the header of this piece of diff. */
vtools_genesis 162 if(asprintf(&name, "diff%s %s %s", switch_string, names[0], names[1]) == -1)
vtools_genesis 163 xalloc_die();
vtools_genesis 164
vtools_genesis 165 outfile = stdout;
vtools_genesis 166
vtools_genesis 167 /* If handling multiple files (because scanning a directory),
vtools_genesis 168 print which files the following output is about. */
vtools_genesis 169 if (currently_recursive)
vtools_genesis 170 printf("%s\n", name);
vtools_genesis 171
vtools_genesis 172 free(name);
vtools_genesis 173
vtools_genesis 174 print_context_header(files, (char const *const *) names);
vtools_genesis 175
vtools_genesis 176 if (names[0] != current_name0)
vtools_genesis 177 free(names[0]);
vtools_genesis 178 if (names[1] != current_name1)
vtools_genesis 179 free(names[1]);
vtools_genesis 180 }
vtools_genesis 181
vtools_genesis 182 void
vtools_genesis 183 finish_output(void) {
vtools_genesis 184 if (outfile != 0 && outfile != stdout) {
vtools_genesis 185 if (ferror(outfile))
vtools_genesis 186 fatal("write failed");
vtools_genesis 187 if (fclose(outfile) != 0)
vtools_genesis 188 pfatal_with_name("write failed");
vtools_genesis 189 }
vtools_genesis 190
vtools_genesis 191 outfile = 0;
vtools_genesis 192 }
vtools_genesis 193
vtools_genesis 194 /* Compare two lines (typically one from each input file) according to
vtools_genesis 195 the command line options. For efficiency, this is invoked only
vtools_genesis 196 when the lines do not match exactly but an option like -i might
vtools_genesis 197 cause us to ignore the difference. Return nonzero if the lines
vtools_genesis 198 differ. */
vtools_genesis 199
vtools_genesis 200 bool
vtools_genesis 201 lines_differ(char const *s1, char const *s2) {
vtools_genesis 202 register char const *t1 = s1;
vtools_genesis 203 register char const *t2 = s2;
vtools_genesis 204 size_t column = 0;
vtools_genesis 205
vtools_genesis 206 while (1) {
vtools_genesis 207 register unsigned char c1 = *t1++;
vtools_genesis 208 register unsigned char c2 = *t2++;
vtools_genesis 209
vtools_genesis 210 /* Test for exact char equality first, since it's a common
vtools_genesis 211 case. */
vtools_genesis 212 if (c1 != c2) {
vtools_genesis 213 break;
vtools_genesis 214 }
vtools_genesis 215 if (c1 == '\n')
vtools_genesis 216 return false;
vtools_genesis 217
vtools_genesis 218 column++;
vtools_genesis 219 }
vtools_genesis 220
vtools_genesis 221 return true;
vtools_genesis 222 }
vtools_genesis 223
vtools_genesis 224 /* Find the consecutive changes at the start of the script START.
vtools_genesis 225 Return the last link before the first gap. */
vtools_genesis 226
vtools_genesis 227 struct change *
vtools_genesis 228 find_change(struct change *start) {
vtools_genesis 229 return start;
vtools_genesis 230 }
vtools_genesis 231
vtools_genesis 232 /* Divide |script| into pieces by calling |hunkfun| and print each piece
vtools_genesis 233 with |printfun|. Both functions take one arg, an edit script.
vtools_genesis 234
vtools_genesis 235 |hunkfun| is called with the tail of the script and returns the
vtools_genesis 236 last link that belongs together with the start of the tail.
vtools_genesis 237
vtools_genesis 238 |printfun| takes a subscript which belongs together (with a null
vtools_genesis 239 link at the end) and prints it. */
vtools_genesis 240
vtools_genesis 241 void
vtools_genesis 242 print_script(struct change *script) {
vtools_genesis 243 struct change *next = script;
vtools_genesis 244
vtools_genesis 245 while (next) {
vtools_genesis 246 struct change *this, *end;
vtools_genesis 247
vtools_genesis 248 /* Find a set of changes that belong together. */
vtools_genesis 249 this = next;
vtools_genesis 250 end = find_hunk(next);
vtools_genesis 251
vtools_genesis 252 /* Disconnect them from the rest of the changes, making them a
vtools_genesis 253 hunk, and remember the rest for next iteration. */
vtools_genesis 254 next = end->link;
vtools_genesis 255 end->link = 0;
vtools_genesis 256 #ifdef DEBUG
vtools_genesis 257 debug_script (this);
vtools_genesis 258 #endif
vtools_genesis 259
vtools_genesis 260 /* Print this hunk. */
vtools_genesis 261 pr_unidiff_hunk(this);
vtools_genesis 262
vtools_genesis 263 /* Reconnect the script so it will all be freed properly. */
vtools_genesis 264 end->link = next;
vtools_genesis 265 }
vtools_genesis 266 }
vtools_genesis 267
vtools_genesis 268 /* Print the text of a single line |line|, flagging it with the
vtools_genesis 269 characters in |line_flag| (which say whether the line is inserted,
vtools_genesis 270 deleted, changed, etc.). |line_flag| must not end in a blank,
vtools_genesis 271 unless it is a single blank. */
vtools_genesis 272
vtools_genesis 273 void
vtools_genesis 274 print_1_line(char const *line_flag, char const *const *line) {
vtools_genesis 275 print_1_line_nl(line_flag, line, false);
vtools_genesis 276 }
vtools_genesis 277
vtools_genesis 278 /* Print the text of a single line |line|, flagging it with the
vtools_genesis 279 characters in |line_flag| (which say whether the line is inserted,
vtools_genesis 280 deleted, changed, etc.). |line_flag| must not end in a blank,
vtools_genesis 281 unless it is a single blank. If |skip_nl| is set, then the final
vtools_genesis 282 |'\n'| is not printed. */
vtools_genesis 283
vtools_genesis 284 void
vtools_genesis 285 print_1_line_nl(char const *line_flag, char const *const *line, bool skip_nl) {
vtools_genesis 286 char const *base = line[0], *limit = line[1]; /* Help the compiler. */
vtools_genesis 287 FILE *out = outfile; /* Help the compiler some more. */
vtools_genesis 288 char const *flag_format = 0;
vtools_genesis 289
vtools_genesis 290 /* If -T was specified, use a Tab between the line-flag and the
vtools_genesis 291 text. Otherwise use a Space (as Unix diff does). Print
vtools_genesis 292 neither space nor tab if line-flags are empty. But omit
vtools_genesis 293 trailing blanks if requested. */
vtools_genesis 294
vtools_genesis 295 if (line_flag && *line_flag) {
vtools_genesis 296 char const *flag_format_1 = flag_format = "%s ";
vtools_genesis 297 char const *line_flag_1 = line_flag;
vtools_genesis 298
vtools_genesis 299 if (suppress_blank_empty && **line == '\n') {
vtools_genesis 300 flag_format_1 = "%s";
vtools_genesis 301
vtools_genesis 302 /* This hack to omit trailing blanks takes advantage of
vtools_genesis 303 the fact that the only way that |line_flag| can end in
vtools_genesis 304 a blank is when |line_flag| consists of a single
vtools_genesis 305 blank. */
vtools_genesis 306 line_flag_1 += *line_flag_1 == ' ';
vtools_genesis 307 }
vtools_genesis 308
vtools_genesis 309 fprintf(out, flag_format_1, line_flag_1);
vtools_genesis 310 }
vtools_genesis 311
vtools_genesis 312 output_1_line(base, limit - (skip_nl && limit[-1] == '\n'));
vtools_genesis 313
vtools_genesis 314 if ((!line_flag || line_flag[0]) && limit[-1] != '\n') {
vtools_genesis 315 fprintf(out, "\n\\ %s\n", "No newline at end of file");
vtools_genesis 316 }
vtools_genesis 317 }
vtools_genesis 318
vtools_genesis 319 /* Output a line from |base| up to |limit|. */
vtools_genesis 320
vtools_genesis 321 void
vtools_genesis 322 output_1_line(char const *base, char const *limit) {
vtools_genesis 323 const size_t MAX_CHUNK = 1024;
vtools_genesis 324 size_t left = limit - base;
vtools_genesis 325 while (left) {
vtools_genesis 326 size_t to_write = MIN (left, MAX_CHUNK);
vtools_genesis 327 size_t written = fwrite(base, sizeof(char), to_write, outfile);
vtools_genesis 328 if (written < to_write)
vtools_genesis 329 return;
vtools_genesis 330 base += written;
vtools_genesis 331 left -= written;
vtools_genesis 332 }
vtools_genesis 333 }
vtools_genesis 334
vtools_genesis 335 /* Translate an internal line number (an index into diff's table of
vtools_genesis 336 lines) into an actual line number in the input file. The internal
vtools_genesis 337 line number is |i|. |file| points to the data on the file.
vtools_genesis 338
vtools_genesis 339 Internal line numbers count from 0 starting after the prefix.
vtools_genesis 340 Actual line numbers count from 1 within the entire file. */
vtools_genesis 341
vtools_genesis 342 lin
vtools_genesis 343 translate_line_number(struct file_data const *file, lin i) {
vtools_genesis 344 return i + file->prefix_lines + 1;
vtools_genesis 345 }
vtools_genesis 346
vtools_genesis 347 /* Translate a line number range. This is always done for printing,
vtools_genesis 348 so for convenience translate to printint rather than lin, so that
vtools_genesis 349 the caller can use |printf| with |"%"pI"d"| without casting. */
vtools_genesis 350
vtools_genesis 351 void
vtools_genesis 352 translate_range(struct file_data const *file,
vtools_genesis 353 lin a, lin b,
vtools_genesis 354 printint *aptr, printint *bptr) {
vtools_genesis 355 *aptr = translate_line_number(file, a - 1) + 1;
vtools_genesis 356 *bptr = translate_line_number(file, b + 1) - 1;
vtools_genesis 357 }
vtools_genesis 358
vtools_genesis 359 /* Look at a hunk of edit script and report the range of lines in each
vtools_genesis 360 file that it applies to. |hunk| is the start of the hunk, which is
vtools_genesis 361 a chain of |struct change|. The first and last line numbers of
vtools_genesis 362 file 0 are stored in |*first0| and |*last0|, and likewise for file
vtools_genesis 363 1 in |*first1| and |*last1|. Note that these are internal line numbers
vtools_genesis 364 that count from 0.
vtools_genesis 365
vtools_genesis 366 If no lines from file 0 are deleted, then |first0| is |last0+1|.
vtools_genesis 367
vtools_genesis 368 Return |UNCHANGED| if only ignorable lines are inserted or deleted,
vtools_genesis 369 |OLD| if lines of file 0 are deleted,
vtools_genesis 370 |NEW| if lines of file 1 are inserted,
vtools_genesis 371 and |CHANGED| if both kinds of changes are found. */
vtools_genesis 372
vtools_genesis 373 enum changes
vtools_genesis 374 analyze_hunk(struct change *hunk,
vtools_genesis 375 lin *first0, lin *last0,
vtools_genesis 376 lin *first1, lin *last1) {
vtools_genesis 377 struct change *next;
vtools_genesis 378 lin l0, l1;
vtools_genesis 379 lin show_from, show_to;
vtools_genesis 380 /* If 0, ignore zero-length lines;
vtools_genesis 381 if |SIZE_MAX|, do not ignore lines just because of their length. */
vtools_genesis 382
vtools_genesis 383 char const *const *linbuf0 = files[0].linbuf; /* Help the compiler. */
vtools_genesis 384 char const *const *linbuf1 = files[1].linbuf;
vtools_genesis 385
vtools_genesis 386 show_from = show_to = 0;
vtools_genesis 387
vtools_genesis 388 *first0 = hunk->line0;
vtools_genesis 389 *first1 = hunk->line1;
vtools_genesis 390
vtools_genesis 391 next = hunk;
vtools_genesis 392 do {
vtools_genesis 393 l0 = next->line0 + next->deleted - 1;
vtools_genesis 394 l1 = next->line1 + next->inserted - 1;
vtools_genesis 395 show_from += next->deleted;
vtools_genesis 396 show_to += next->inserted;
vtools_genesis 397 } while ((next = next->link) != 0);
vtools_genesis 398
vtools_genesis 399 *last0 = l0;
vtools_genesis 400 *last1 = l1;
vtools_genesis 401
vtools_genesis 402 return (show_from ? OLD : UNCHANGED) | (show_to ? NEW : UNCHANGED);
vtools_genesis 403 }
vtools_genesis 404
vtools_genesis 405 /* Yield a new block of |size| bytes, initialized to zero. */
vtools_genesis 406
vtools_genesis 407 void *
vtools_genesis 408 zalloc(size_t size) {
vtools_genesis 409 void *p = xmalloc(size);
vtools_genesis 410 memset (p, 0, size);
vtools_genesis 411 return p;
vtools_genesis 412 }
vtools_genesis 413
vtools_genesis 414 void
vtools_genesis 415 debug_script(struct change *sp) {
vtools_genesis 416 fflush(stdout);
vtools_genesis 417
vtools_genesis 418 for (; sp; sp = sp->link) {
vtools_genesis 419 printint line0 = sp->line0;
vtools_genesis 420 printint line1 = sp->line1;
vtools_genesis 421 printint deleted = sp->deleted;
vtools_genesis 422 printint inserted = sp->inserted;
vtools_genesis 423 fprintf(stderr, "%3"pI"d %3"pI"d delete %"pI"d insert %"pI"d\n",
vtools_genesis 424 line0, line1, deleted, inserted);
vtools_genesis 425 }
vtools_genesis 426
vtools_genesis 427 fflush(stderr);
vtools_genesis 428 }