1*59c8e88eSDag-Erling Smørgrav /* Produce a unidiff output from a diff_result. */ 2*59c8e88eSDag-Erling Smørgrav /* 3*59c8e88eSDag-Erling Smørgrav * Copyright (c) 2020 Neels Hofmeyr <neels@hofmeyr.de> 4*59c8e88eSDag-Erling Smørgrav * 5*59c8e88eSDag-Erling Smørgrav * Permission to use, copy, modify, and distribute this software for any 6*59c8e88eSDag-Erling Smørgrav * purpose with or without fee is hereby granted, provided that the above 7*59c8e88eSDag-Erling Smørgrav * copyright notice and this permission notice appear in all copies. 8*59c8e88eSDag-Erling Smørgrav * 9*59c8e88eSDag-Erling Smørgrav * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10*59c8e88eSDag-Erling Smørgrav * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11*59c8e88eSDag-Erling Smørgrav * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12*59c8e88eSDag-Erling Smørgrav * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13*59c8e88eSDag-Erling Smørgrav * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14*59c8e88eSDag-Erling Smørgrav * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15*59c8e88eSDag-Erling Smørgrav * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16*59c8e88eSDag-Erling Smørgrav */ 17*59c8e88eSDag-Erling Smørgrav 18*59c8e88eSDag-Erling Smørgrav #include <errno.h> 19*59c8e88eSDag-Erling Smørgrav #include <stdbool.h> 20*59c8e88eSDag-Erling Smørgrav #include <stdint.h> 21*59c8e88eSDag-Erling Smørgrav #include <stdio.h> 22*59c8e88eSDag-Erling Smørgrav #include <stdlib.h> 23*59c8e88eSDag-Erling Smørgrav #include <string.h> 24*59c8e88eSDag-Erling Smørgrav #include <assert.h> 25*59c8e88eSDag-Erling Smørgrav 26*59c8e88eSDag-Erling Smørgrav #include <arraylist.h> 27*59c8e88eSDag-Erling Smørgrav #include <diff_main.h> 28*59c8e88eSDag-Erling Smørgrav #include <diff_output.h> 29*59c8e88eSDag-Erling Smørgrav 30*59c8e88eSDag-Erling Smørgrav #include "diff_internal.h" 31*59c8e88eSDag-Erling Smørgrav #include "diff_debug.h" 32*59c8e88eSDag-Erling Smørgrav 33*59c8e88eSDag-Erling Smørgrav off_t 34*59c8e88eSDag-Erling Smørgrav diff_chunk_get_left_start_pos(const struct diff_chunk *c) 35*59c8e88eSDag-Erling Smørgrav { 36*59c8e88eSDag-Erling Smørgrav return c->left_start->pos; 37*59c8e88eSDag-Erling Smørgrav } 38*59c8e88eSDag-Erling Smørgrav 39*59c8e88eSDag-Erling Smørgrav off_t 40*59c8e88eSDag-Erling Smørgrav diff_chunk_get_right_start_pos(const struct diff_chunk *c) 41*59c8e88eSDag-Erling Smørgrav { 42*59c8e88eSDag-Erling Smørgrav return c->right_start->pos; 43*59c8e88eSDag-Erling Smørgrav } 44*59c8e88eSDag-Erling Smørgrav 45*59c8e88eSDag-Erling Smørgrav bool 46*59c8e88eSDag-Erling Smørgrav diff_chunk_context_empty(const struct diff_chunk_context *cc) 47*59c8e88eSDag-Erling Smørgrav { 48*59c8e88eSDag-Erling Smørgrav return diff_range_empty(&cc->chunk); 49*59c8e88eSDag-Erling Smørgrav } 50*59c8e88eSDag-Erling Smørgrav 51*59c8e88eSDag-Erling Smørgrav int 52*59c8e88eSDag-Erling Smørgrav diff_chunk_get_left_start(const struct diff_chunk *c, 53*59c8e88eSDag-Erling Smørgrav const struct diff_result *r, int context_lines) 54*59c8e88eSDag-Erling Smørgrav { 55*59c8e88eSDag-Erling Smørgrav int left_start = diff_atom_root_idx(r->left, c->left_start); 56*59c8e88eSDag-Erling Smørgrav return MAX(0, left_start - context_lines); 57*59c8e88eSDag-Erling Smørgrav } 58*59c8e88eSDag-Erling Smørgrav 59*59c8e88eSDag-Erling Smørgrav int 60*59c8e88eSDag-Erling Smørgrav diff_chunk_get_left_end(const struct diff_chunk *c, 61*59c8e88eSDag-Erling Smørgrav const struct diff_result *r, int context_lines) 62*59c8e88eSDag-Erling Smørgrav { 63*59c8e88eSDag-Erling Smørgrav int left_start = diff_chunk_get_left_start(c, r, 0); 64*59c8e88eSDag-Erling Smørgrav return MIN(r->left->atoms.len, 65*59c8e88eSDag-Erling Smørgrav left_start + c->left_count + context_lines); 66*59c8e88eSDag-Erling Smørgrav } 67*59c8e88eSDag-Erling Smørgrav 68*59c8e88eSDag-Erling Smørgrav int 69*59c8e88eSDag-Erling Smørgrav diff_chunk_get_right_start(const struct diff_chunk *c, 70*59c8e88eSDag-Erling Smørgrav const struct diff_result *r, int context_lines) 71*59c8e88eSDag-Erling Smørgrav { 72*59c8e88eSDag-Erling Smørgrav int right_start = diff_atom_root_idx(r->right, c->right_start); 73*59c8e88eSDag-Erling Smørgrav return MAX(0, right_start - context_lines); 74*59c8e88eSDag-Erling Smørgrav } 75*59c8e88eSDag-Erling Smørgrav 76*59c8e88eSDag-Erling Smørgrav int 77*59c8e88eSDag-Erling Smørgrav diff_chunk_get_right_end(const struct diff_chunk *c, 78*59c8e88eSDag-Erling Smørgrav const struct diff_result *r, int context_lines) 79*59c8e88eSDag-Erling Smørgrav { 80*59c8e88eSDag-Erling Smørgrav int right_start = diff_chunk_get_right_start(c, r, 0); 81*59c8e88eSDag-Erling Smørgrav return MIN(r->right->atoms.len, 82*59c8e88eSDag-Erling Smørgrav right_start + c->right_count + context_lines); 83*59c8e88eSDag-Erling Smørgrav } 84*59c8e88eSDag-Erling Smørgrav 85*59c8e88eSDag-Erling Smørgrav struct diff_chunk * 86*59c8e88eSDag-Erling Smørgrav diff_chunk_get(const struct diff_result *r, int chunk_idx) 87*59c8e88eSDag-Erling Smørgrav { 88*59c8e88eSDag-Erling Smørgrav return &r->chunks.head[chunk_idx]; 89*59c8e88eSDag-Erling Smørgrav } 90*59c8e88eSDag-Erling Smørgrav 91*59c8e88eSDag-Erling Smørgrav int 92*59c8e88eSDag-Erling Smørgrav diff_chunk_get_left_count(struct diff_chunk *c) 93*59c8e88eSDag-Erling Smørgrav { 94*59c8e88eSDag-Erling Smørgrav return c->left_count; 95*59c8e88eSDag-Erling Smørgrav } 96*59c8e88eSDag-Erling Smørgrav 97*59c8e88eSDag-Erling Smørgrav int 98*59c8e88eSDag-Erling Smørgrav diff_chunk_get_right_count(struct diff_chunk *c) 99*59c8e88eSDag-Erling Smørgrav { 100*59c8e88eSDag-Erling Smørgrav return c->right_count; 101*59c8e88eSDag-Erling Smørgrav } 102*59c8e88eSDag-Erling Smørgrav 103*59c8e88eSDag-Erling Smørgrav void 104*59c8e88eSDag-Erling Smørgrav diff_chunk_context_get(struct diff_chunk_context *cc, const struct diff_result *r, 105*59c8e88eSDag-Erling Smørgrav int chunk_idx, int context_lines) 106*59c8e88eSDag-Erling Smørgrav { 107*59c8e88eSDag-Erling Smørgrav const struct diff_chunk *c = &r->chunks.head[chunk_idx]; 108*59c8e88eSDag-Erling Smørgrav int left_start = diff_chunk_get_left_start(c, r, context_lines); 109*59c8e88eSDag-Erling Smørgrav int left_end = diff_chunk_get_left_end(c, r, context_lines); 110*59c8e88eSDag-Erling Smørgrav int right_start = diff_chunk_get_right_start(c, r, context_lines); 111*59c8e88eSDag-Erling Smørgrav int right_end = diff_chunk_get_right_end(c, r, context_lines); 112*59c8e88eSDag-Erling Smørgrav 113*59c8e88eSDag-Erling Smørgrav *cc = (struct diff_chunk_context){ 114*59c8e88eSDag-Erling Smørgrav .chunk = { 115*59c8e88eSDag-Erling Smørgrav .start = chunk_idx, 116*59c8e88eSDag-Erling Smørgrav .end = chunk_idx + 1, 117*59c8e88eSDag-Erling Smørgrav }, 118*59c8e88eSDag-Erling Smørgrav .left = { 119*59c8e88eSDag-Erling Smørgrav .start = left_start, 120*59c8e88eSDag-Erling Smørgrav .end = left_end, 121*59c8e88eSDag-Erling Smørgrav }, 122*59c8e88eSDag-Erling Smørgrav .right = { 123*59c8e88eSDag-Erling Smørgrav .start = right_start, 124*59c8e88eSDag-Erling Smørgrav .end = right_end, 125*59c8e88eSDag-Erling Smørgrav }, 126*59c8e88eSDag-Erling Smørgrav }; 127*59c8e88eSDag-Erling Smørgrav } 128*59c8e88eSDag-Erling Smørgrav 129*59c8e88eSDag-Erling Smørgrav bool 130*59c8e88eSDag-Erling Smørgrav diff_chunk_contexts_touch(const struct diff_chunk_context *cc, 131*59c8e88eSDag-Erling Smørgrav const struct diff_chunk_context *other) 132*59c8e88eSDag-Erling Smørgrav { 133*59c8e88eSDag-Erling Smørgrav return diff_ranges_touch(&cc->chunk, &other->chunk) 134*59c8e88eSDag-Erling Smørgrav || diff_ranges_touch(&cc->left, &other->left) 135*59c8e88eSDag-Erling Smørgrav || diff_ranges_touch(&cc->right, &other->right); 136*59c8e88eSDag-Erling Smørgrav } 137*59c8e88eSDag-Erling Smørgrav 138*59c8e88eSDag-Erling Smørgrav void 139*59c8e88eSDag-Erling Smørgrav diff_chunk_contexts_merge(struct diff_chunk_context *cc, 140*59c8e88eSDag-Erling Smørgrav const struct diff_chunk_context *other) 141*59c8e88eSDag-Erling Smørgrav { 142*59c8e88eSDag-Erling Smørgrav diff_ranges_merge(&cc->chunk, &other->chunk); 143*59c8e88eSDag-Erling Smørgrav diff_ranges_merge(&cc->left, &other->left); 144*59c8e88eSDag-Erling Smørgrav diff_ranges_merge(&cc->right, &other->right); 145*59c8e88eSDag-Erling Smørgrav } 146*59c8e88eSDag-Erling Smørgrav 147*59c8e88eSDag-Erling Smørgrav void 148*59c8e88eSDag-Erling Smørgrav diff_chunk_context_load_change(struct diff_chunk_context *cc, 149*59c8e88eSDag-Erling Smørgrav int *nchunks_used, 150*59c8e88eSDag-Erling Smørgrav struct diff_result *result, 151*59c8e88eSDag-Erling Smørgrav int start_chunk_idx, 152*59c8e88eSDag-Erling Smørgrav int context_lines) 153*59c8e88eSDag-Erling Smørgrav { 154*59c8e88eSDag-Erling Smørgrav int i; 155*59c8e88eSDag-Erling Smørgrav int seen_minus = 0, seen_plus = 0; 156*59c8e88eSDag-Erling Smørgrav 157*59c8e88eSDag-Erling Smørgrav if (nchunks_used) 158*59c8e88eSDag-Erling Smørgrav *nchunks_used = 0; 159*59c8e88eSDag-Erling Smørgrav 160*59c8e88eSDag-Erling Smørgrav for (i = start_chunk_idx; i < result->chunks.len; i++) { 161*59c8e88eSDag-Erling Smørgrav struct diff_chunk *chunk = &result->chunks.head[i]; 162*59c8e88eSDag-Erling Smørgrav enum diff_chunk_type t = diff_chunk_type(chunk); 163*59c8e88eSDag-Erling Smørgrav struct diff_chunk_context next; 164*59c8e88eSDag-Erling Smørgrav 165*59c8e88eSDag-Erling Smørgrav if (t != CHUNK_MINUS && t != CHUNK_PLUS) { 166*59c8e88eSDag-Erling Smørgrav if (nchunks_used) 167*59c8e88eSDag-Erling Smørgrav (*nchunks_used)++; 168*59c8e88eSDag-Erling Smørgrav if (seen_minus || seen_plus) 169*59c8e88eSDag-Erling Smørgrav break; 170*59c8e88eSDag-Erling Smørgrav else 171*59c8e88eSDag-Erling Smørgrav continue; 172*59c8e88eSDag-Erling Smørgrav } else if (t == CHUNK_MINUS) 173*59c8e88eSDag-Erling Smørgrav seen_minus = 1; 174*59c8e88eSDag-Erling Smørgrav else if (t == CHUNK_PLUS) 175*59c8e88eSDag-Erling Smørgrav seen_plus = 1; 176*59c8e88eSDag-Erling Smørgrav 177*59c8e88eSDag-Erling Smørgrav if (diff_chunk_context_empty(cc)) { 178*59c8e88eSDag-Erling Smørgrav /* Note down the start point, any number of subsequent 179*59c8e88eSDag-Erling Smørgrav * chunks may be joined up to this chunk by being 180*59c8e88eSDag-Erling Smørgrav * directly adjacent. */ 181*59c8e88eSDag-Erling Smørgrav diff_chunk_context_get(cc, result, i, context_lines); 182*59c8e88eSDag-Erling Smørgrav if (nchunks_used) 183*59c8e88eSDag-Erling Smørgrav (*nchunks_used)++; 184*59c8e88eSDag-Erling Smørgrav continue; 185*59c8e88eSDag-Erling Smørgrav } 186*59c8e88eSDag-Erling Smørgrav 187*59c8e88eSDag-Erling Smørgrav /* There already is a previous chunk noted down for being 188*59c8e88eSDag-Erling Smørgrav * printed. Does it join up with this one? */ 189*59c8e88eSDag-Erling Smørgrav diff_chunk_context_get(&next, result, i, context_lines); 190*59c8e88eSDag-Erling Smørgrav 191*59c8e88eSDag-Erling Smørgrav if (diff_chunk_contexts_touch(cc, &next)) { 192*59c8e88eSDag-Erling Smørgrav /* This next context touches or overlaps the previous 193*59c8e88eSDag-Erling Smørgrav * one, join. */ 194*59c8e88eSDag-Erling Smørgrav diff_chunk_contexts_merge(cc, &next); 195*59c8e88eSDag-Erling Smørgrav if (nchunks_used) 196*59c8e88eSDag-Erling Smørgrav (*nchunks_used)++; 197*59c8e88eSDag-Erling Smørgrav continue; 198*59c8e88eSDag-Erling Smørgrav } else 199*59c8e88eSDag-Erling Smørgrav break; 200*59c8e88eSDag-Erling Smørgrav } 201*59c8e88eSDag-Erling Smørgrav } 202*59c8e88eSDag-Erling Smørgrav 203*59c8e88eSDag-Erling Smørgrav struct diff_output_unidiff_state { 204*59c8e88eSDag-Erling Smørgrav bool header_printed; 205*59c8e88eSDag-Erling Smørgrav char prototype[DIFF_FUNCTION_CONTEXT_SIZE]; 206*59c8e88eSDag-Erling Smørgrav int last_prototype_idx; 207*59c8e88eSDag-Erling Smørgrav }; 208*59c8e88eSDag-Erling Smørgrav 209*59c8e88eSDag-Erling Smørgrav struct diff_output_unidiff_state * 210*59c8e88eSDag-Erling Smørgrav diff_output_unidiff_state_alloc(void) 211*59c8e88eSDag-Erling Smørgrav { 212*59c8e88eSDag-Erling Smørgrav struct diff_output_unidiff_state *state; 213*59c8e88eSDag-Erling Smørgrav 214*59c8e88eSDag-Erling Smørgrav state = calloc(1, sizeof(struct diff_output_unidiff_state)); 215*59c8e88eSDag-Erling Smørgrav if (state != NULL) 216*59c8e88eSDag-Erling Smørgrav diff_output_unidiff_state_reset(state); 217*59c8e88eSDag-Erling Smørgrav return state; 218*59c8e88eSDag-Erling Smørgrav } 219*59c8e88eSDag-Erling Smørgrav 220*59c8e88eSDag-Erling Smørgrav void 221*59c8e88eSDag-Erling Smørgrav diff_output_unidiff_state_reset(struct diff_output_unidiff_state *state) 222*59c8e88eSDag-Erling Smørgrav { 223*59c8e88eSDag-Erling Smørgrav state->header_printed = false; 224*59c8e88eSDag-Erling Smørgrav memset(state->prototype, 0, sizeof(state->prototype)); 225*59c8e88eSDag-Erling Smørgrav state->last_prototype_idx = 0; 226*59c8e88eSDag-Erling Smørgrav } 227*59c8e88eSDag-Erling Smørgrav 228*59c8e88eSDag-Erling Smørgrav void 229*59c8e88eSDag-Erling Smørgrav diff_output_unidiff_state_free(struct diff_output_unidiff_state *state) 230*59c8e88eSDag-Erling Smørgrav { 231*59c8e88eSDag-Erling Smørgrav free(state); 232*59c8e88eSDag-Erling Smørgrav } 233*59c8e88eSDag-Erling Smørgrav 234*59c8e88eSDag-Erling Smørgrav static int 235*59c8e88eSDag-Erling Smørgrav output_unidiff_chunk(struct diff_output_info *outinfo, FILE *dest, 236*59c8e88eSDag-Erling Smørgrav struct diff_output_unidiff_state *state, 237*59c8e88eSDag-Erling Smørgrav const struct diff_input_info *info, 238*59c8e88eSDag-Erling Smørgrav const struct diff_result *result, 239*59c8e88eSDag-Erling Smørgrav bool print_header, bool show_function_prototypes, 240*59c8e88eSDag-Erling Smørgrav const struct diff_chunk_context *cc) 241*59c8e88eSDag-Erling Smørgrav { 242*59c8e88eSDag-Erling Smørgrav int rc, left_start, left_len, right_start, right_len; 243*59c8e88eSDag-Erling Smørgrav off_t outoff = 0, *offp; 244*59c8e88eSDag-Erling Smørgrav uint8_t *typep; 245*59c8e88eSDag-Erling Smørgrav 246*59c8e88eSDag-Erling Smørgrav if (diff_range_empty(&cc->left) && diff_range_empty(&cc->right)) 247*59c8e88eSDag-Erling Smørgrav return DIFF_RC_OK; 248*59c8e88eSDag-Erling Smørgrav 249*59c8e88eSDag-Erling Smørgrav if (outinfo && outinfo->line_offsets.len > 0) { 250*59c8e88eSDag-Erling Smørgrav unsigned int idx = outinfo->line_offsets.len - 1; 251*59c8e88eSDag-Erling Smørgrav outoff = outinfo->line_offsets.head[idx]; 252*59c8e88eSDag-Erling Smørgrav } 253*59c8e88eSDag-Erling Smørgrav 254*59c8e88eSDag-Erling Smørgrav if (print_header && !(state->header_printed)) { 255*59c8e88eSDag-Erling Smørgrav rc = fprintf(dest, "--- %s\n", 256*59c8e88eSDag-Erling Smørgrav diff_output_get_label_left(info)); 257*59c8e88eSDag-Erling Smørgrav if (rc < 0) 258*59c8e88eSDag-Erling Smørgrav return errno; 259*59c8e88eSDag-Erling Smørgrav if (outinfo) { 260*59c8e88eSDag-Erling Smørgrav ARRAYLIST_ADD(offp, outinfo->line_offsets); 261*59c8e88eSDag-Erling Smørgrav if (offp == NULL) 262*59c8e88eSDag-Erling Smørgrav return ENOMEM; 263*59c8e88eSDag-Erling Smørgrav outoff += rc; 264*59c8e88eSDag-Erling Smørgrav *offp = outoff; 265*59c8e88eSDag-Erling Smørgrav ARRAYLIST_ADD(typep, outinfo->line_types); 266*59c8e88eSDag-Erling Smørgrav if (typep == NULL) 267*59c8e88eSDag-Erling Smørgrav return ENOMEM; 268*59c8e88eSDag-Erling Smørgrav *typep = DIFF_LINE_MINUS; 269*59c8e88eSDag-Erling Smørgrav } 270*59c8e88eSDag-Erling Smørgrav rc = fprintf(dest, "+++ %s\n", 271*59c8e88eSDag-Erling Smørgrav diff_output_get_label_right(info)); 272*59c8e88eSDag-Erling Smørgrav if (rc < 0) 273*59c8e88eSDag-Erling Smørgrav return errno; 274*59c8e88eSDag-Erling Smørgrav if (outinfo) { 275*59c8e88eSDag-Erling Smørgrav ARRAYLIST_ADD(offp, outinfo->line_offsets); 276*59c8e88eSDag-Erling Smørgrav if (offp == NULL) 277*59c8e88eSDag-Erling Smørgrav return ENOMEM; 278*59c8e88eSDag-Erling Smørgrav outoff += rc; 279*59c8e88eSDag-Erling Smørgrav *offp = outoff; 280*59c8e88eSDag-Erling Smørgrav ARRAYLIST_ADD(typep, outinfo->line_types); 281*59c8e88eSDag-Erling Smørgrav if (typep == NULL) 282*59c8e88eSDag-Erling Smørgrav return ENOMEM; 283*59c8e88eSDag-Erling Smørgrav *typep = DIFF_LINE_PLUS; 284*59c8e88eSDag-Erling Smørgrav } 285*59c8e88eSDag-Erling Smørgrav state->header_printed = true; 286*59c8e88eSDag-Erling Smørgrav } 287*59c8e88eSDag-Erling Smørgrav 288*59c8e88eSDag-Erling Smørgrav left_len = cc->left.end - cc->left.start; 289*59c8e88eSDag-Erling Smørgrav if (result->left->atoms.len == 0) 290*59c8e88eSDag-Erling Smørgrav left_start = 0; 291*59c8e88eSDag-Erling Smørgrav else if (left_len == 0 && cc->left.start > 0) 292*59c8e88eSDag-Erling Smørgrav left_start = cc->left.start; 293*59c8e88eSDag-Erling Smørgrav else 294*59c8e88eSDag-Erling Smørgrav left_start = cc->left.start + 1; 295*59c8e88eSDag-Erling Smørgrav 296*59c8e88eSDag-Erling Smørgrav right_len = cc->right.end - cc->right.start; 297*59c8e88eSDag-Erling Smørgrav if (result->right->atoms.len == 0) 298*59c8e88eSDag-Erling Smørgrav right_start = 0; 299*59c8e88eSDag-Erling Smørgrav else if (right_len == 0 && cc->right.start > 0) 300*59c8e88eSDag-Erling Smørgrav right_start = cc->right.start; 301*59c8e88eSDag-Erling Smørgrav else 302*59c8e88eSDag-Erling Smørgrav right_start = cc->right.start + 1; 303*59c8e88eSDag-Erling Smørgrav 304*59c8e88eSDag-Erling Smørgrav if (show_function_prototypes) { 305*59c8e88eSDag-Erling Smørgrav rc = diff_output_match_function_prototype(state->prototype, 306*59c8e88eSDag-Erling Smørgrav sizeof(state->prototype), &state->last_prototype_idx, 307*59c8e88eSDag-Erling Smørgrav result, cc); 308*59c8e88eSDag-Erling Smørgrav if (rc) 309*59c8e88eSDag-Erling Smørgrav return rc; 310*59c8e88eSDag-Erling Smørgrav } 311*59c8e88eSDag-Erling Smørgrav 312*59c8e88eSDag-Erling Smørgrav if (left_len == 1 && right_len == 1) { 313*59c8e88eSDag-Erling Smørgrav rc = fprintf(dest, "@@ -%d +%d @@%s%s\n", 314*59c8e88eSDag-Erling Smørgrav left_start, right_start, 315*59c8e88eSDag-Erling Smørgrav state->prototype[0] ? " " : "", 316*59c8e88eSDag-Erling Smørgrav state->prototype[0] ? state->prototype : ""); 317*59c8e88eSDag-Erling Smørgrav } else if (left_len == 1 && right_len != 1) { 318*59c8e88eSDag-Erling Smørgrav rc = fprintf(dest, "@@ -%d +%d,%d @@%s%s\n", 319*59c8e88eSDag-Erling Smørgrav left_start, right_start, right_len, 320*59c8e88eSDag-Erling Smørgrav state->prototype[0] ? " " : "", 321*59c8e88eSDag-Erling Smørgrav state->prototype[0] ? state->prototype : ""); 322*59c8e88eSDag-Erling Smørgrav } else if (left_len != 1 && right_len == 1) { 323*59c8e88eSDag-Erling Smørgrav rc = fprintf(dest, "@@ -%d,%d +%d @@%s%s\n", 324*59c8e88eSDag-Erling Smørgrav left_start, left_len, right_start, 325*59c8e88eSDag-Erling Smørgrav state->prototype[0] ? " " : "", 326*59c8e88eSDag-Erling Smørgrav state->prototype[0] ? state->prototype : ""); 327*59c8e88eSDag-Erling Smørgrav } else { 328*59c8e88eSDag-Erling Smørgrav rc = fprintf(dest, "@@ -%d,%d +%d,%d @@%s%s\n", 329*59c8e88eSDag-Erling Smørgrav left_start, left_len, right_start, right_len, 330*59c8e88eSDag-Erling Smørgrav state->prototype[0] ? " " : "", 331*59c8e88eSDag-Erling Smørgrav state->prototype[0] ? state->prototype : ""); 332*59c8e88eSDag-Erling Smørgrav } 333*59c8e88eSDag-Erling Smørgrav if (rc < 0) 334*59c8e88eSDag-Erling Smørgrav return errno; 335*59c8e88eSDag-Erling Smørgrav if (outinfo) { 336*59c8e88eSDag-Erling Smørgrav ARRAYLIST_ADD(offp, outinfo->line_offsets); 337*59c8e88eSDag-Erling Smørgrav if (offp == NULL) 338*59c8e88eSDag-Erling Smørgrav return ENOMEM; 339*59c8e88eSDag-Erling Smørgrav outoff += rc; 340*59c8e88eSDag-Erling Smørgrav *offp = outoff; 341*59c8e88eSDag-Erling Smørgrav ARRAYLIST_ADD(typep, outinfo->line_types); 342*59c8e88eSDag-Erling Smørgrav if (typep == NULL) 343*59c8e88eSDag-Erling Smørgrav return ENOMEM; 344*59c8e88eSDag-Erling Smørgrav *typep = DIFF_LINE_HUNK; 345*59c8e88eSDag-Erling Smørgrav } 346*59c8e88eSDag-Erling Smørgrav 347*59c8e88eSDag-Erling Smørgrav /* Got the absolute line numbers where to start printing, and the index 348*59c8e88eSDag-Erling Smørgrav * of the interesting (non-context) chunk. 349*59c8e88eSDag-Erling Smørgrav * To print context lines above the interesting chunk, nipping on the 350*59c8e88eSDag-Erling Smørgrav * previous chunk index may be necessary. 351*59c8e88eSDag-Erling Smørgrav * It is guaranteed to be only context lines where left == right, so it 352*59c8e88eSDag-Erling Smørgrav * suffices to look on the left. */ 353*59c8e88eSDag-Erling Smørgrav const struct diff_chunk *first_chunk; 354*59c8e88eSDag-Erling Smørgrav int chunk_start_line; 355*59c8e88eSDag-Erling Smørgrav first_chunk = &result->chunks.head[cc->chunk.start]; 356*59c8e88eSDag-Erling Smørgrav chunk_start_line = diff_atom_root_idx(result->left, 357*59c8e88eSDag-Erling Smørgrav first_chunk->left_start); 358*59c8e88eSDag-Erling Smørgrav if (cc->left.start < chunk_start_line) { 359*59c8e88eSDag-Erling Smørgrav rc = diff_output_lines(outinfo, dest, " ", 360*59c8e88eSDag-Erling Smørgrav &result->left->atoms.head[cc->left.start], 361*59c8e88eSDag-Erling Smørgrav chunk_start_line - cc->left.start); 362*59c8e88eSDag-Erling Smørgrav if (rc) 363*59c8e88eSDag-Erling Smørgrav return rc; 364*59c8e88eSDag-Erling Smørgrav } 365*59c8e88eSDag-Erling Smørgrav 366*59c8e88eSDag-Erling Smørgrav /* Now write out all the joined chunks and contexts between them */ 367*59c8e88eSDag-Erling Smørgrav int c_idx; 368*59c8e88eSDag-Erling Smørgrav for (c_idx = cc->chunk.start; c_idx < cc->chunk.end; c_idx++) { 369*59c8e88eSDag-Erling Smørgrav const struct diff_chunk *c = &result->chunks.head[c_idx]; 370*59c8e88eSDag-Erling Smørgrav 371*59c8e88eSDag-Erling Smørgrav if (c->left_count && c->right_count) 372*59c8e88eSDag-Erling Smørgrav rc = diff_output_lines(outinfo, dest, 373*59c8e88eSDag-Erling Smørgrav c->solved ? " " : "?", 374*59c8e88eSDag-Erling Smørgrav c->left_start, c->left_count); 375*59c8e88eSDag-Erling Smørgrav else if (c->left_count && !c->right_count) 376*59c8e88eSDag-Erling Smørgrav rc = diff_output_lines(outinfo, dest, 377*59c8e88eSDag-Erling Smørgrav c->solved ? "-" : "?", 378*59c8e88eSDag-Erling Smørgrav c->left_start, c->left_count); 379*59c8e88eSDag-Erling Smørgrav else if (c->right_count && !c->left_count) 380*59c8e88eSDag-Erling Smørgrav rc = diff_output_lines(outinfo, dest, 381*59c8e88eSDag-Erling Smørgrav c->solved ? "+" : "?", 382*59c8e88eSDag-Erling Smørgrav c->right_start, c->right_count); 383*59c8e88eSDag-Erling Smørgrav if (rc) 384*59c8e88eSDag-Erling Smørgrav return rc; 385*59c8e88eSDag-Erling Smørgrav 386*59c8e88eSDag-Erling Smørgrav if (cc->chunk.end == result->chunks.len) { 387*59c8e88eSDag-Erling Smørgrav rc = diff_output_trailing_newline_msg(outinfo, dest, c); 388*59c8e88eSDag-Erling Smørgrav if (rc != DIFF_RC_OK) 389*59c8e88eSDag-Erling Smørgrav return rc; 390*59c8e88eSDag-Erling Smørgrav } 391*59c8e88eSDag-Erling Smørgrav } 392*59c8e88eSDag-Erling Smørgrav 393*59c8e88eSDag-Erling Smørgrav /* Trailing context? */ 394*59c8e88eSDag-Erling Smørgrav const struct diff_chunk *last_chunk; 395*59c8e88eSDag-Erling Smørgrav int chunk_end_line; 396*59c8e88eSDag-Erling Smørgrav last_chunk = &result->chunks.head[cc->chunk.end - 1]; 397*59c8e88eSDag-Erling Smørgrav chunk_end_line = diff_atom_root_idx(result->left, 398*59c8e88eSDag-Erling Smørgrav last_chunk->left_start 399*59c8e88eSDag-Erling Smørgrav + last_chunk->left_count); 400*59c8e88eSDag-Erling Smørgrav if (cc->left.end > chunk_end_line) { 401*59c8e88eSDag-Erling Smørgrav rc = diff_output_lines(outinfo, dest, " ", 402*59c8e88eSDag-Erling Smørgrav &result->left->atoms.head[chunk_end_line], 403*59c8e88eSDag-Erling Smørgrav cc->left.end - chunk_end_line); 404*59c8e88eSDag-Erling Smørgrav if (rc) 405*59c8e88eSDag-Erling Smørgrav return rc; 406*59c8e88eSDag-Erling Smørgrav 407*59c8e88eSDag-Erling Smørgrav if (cc->left.end == result->left->atoms.len) { 408*59c8e88eSDag-Erling Smørgrav rc = diff_output_trailing_newline_msg(outinfo, dest, 409*59c8e88eSDag-Erling Smørgrav &result->chunks.head[result->chunks.len - 1]); 410*59c8e88eSDag-Erling Smørgrav if (rc != DIFF_RC_OK) 411*59c8e88eSDag-Erling Smørgrav return rc; 412*59c8e88eSDag-Erling Smørgrav } 413*59c8e88eSDag-Erling Smørgrav } 414*59c8e88eSDag-Erling Smørgrav 415*59c8e88eSDag-Erling Smørgrav return DIFF_RC_OK; 416*59c8e88eSDag-Erling Smørgrav } 417*59c8e88eSDag-Erling Smørgrav 418*59c8e88eSDag-Erling Smørgrav int 419*59c8e88eSDag-Erling Smørgrav diff_output_unidiff_chunk(struct diff_output_info **output_info, FILE *dest, 420*59c8e88eSDag-Erling Smørgrav struct diff_output_unidiff_state *state, 421*59c8e88eSDag-Erling Smørgrav const struct diff_input_info *info, 422*59c8e88eSDag-Erling Smørgrav const struct diff_result *result, 423*59c8e88eSDag-Erling Smørgrav const struct diff_chunk_context *cc) 424*59c8e88eSDag-Erling Smørgrav { 425*59c8e88eSDag-Erling Smørgrav struct diff_output_info *outinfo = NULL; 426*59c8e88eSDag-Erling Smørgrav int flags = (result->left->root->diff_flags | 427*59c8e88eSDag-Erling Smørgrav result->right->root->diff_flags); 428*59c8e88eSDag-Erling Smørgrav bool show_function_prototypes = (flags & DIFF_FLAG_SHOW_PROTOTYPES); 429*59c8e88eSDag-Erling Smørgrav 430*59c8e88eSDag-Erling Smørgrav if (output_info) { 431*59c8e88eSDag-Erling Smørgrav *output_info = diff_output_info_alloc(); 432*59c8e88eSDag-Erling Smørgrav if (*output_info == NULL) 433*59c8e88eSDag-Erling Smørgrav return ENOMEM; 434*59c8e88eSDag-Erling Smørgrav outinfo = *output_info; 435*59c8e88eSDag-Erling Smørgrav } 436*59c8e88eSDag-Erling Smørgrav 437*59c8e88eSDag-Erling Smørgrav return output_unidiff_chunk(outinfo, dest, state, info, 438*59c8e88eSDag-Erling Smørgrav result, false, show_function_prototypes, cc); 439*59c8e88eSDag-Erling Smørgrav } 440*59c8e88eSDag-Erling Smørgrav 441*59c8e88eSDag-Erling Smørgrav int 442*59c8e88eSDag-Erling Smørgrav diff_output_unidiff(struct diff_output_info **output_info, 443*59c8e88eSDag-Erling Smørgrav FILE *dest, const struct diff_input_info *info, 444*59c8e88eSDag-Erling Smørgrav const struct diff_result *result, 445*59c8e88eSDag-Erling Smørgrav unsigned int context_lines) 446*59c8e88eSDag-Erling Smørgrav { 447*59c8e88eSDag-Erling Smørgrav struct diff_output_unidiff_state *state; 448*59c8e88eSDag-Erling Smørgrav struct diff_chunk_context cc = {}; 449*59c8e88eSDag-Erling Smørgrav struct diff_output_info *outinfo = NULL; 450*59c8e88eSDag-Erling Smørgrav int atomizer_flags = (result->left->atomizer_flags| 451*59c8e88eSDag-Erling Smørgrav result->right->atomizer_flags); 452*59c8e88eSDag-Erling Smørgrav int flags = (result->left->root->diff_flags | 453*59c8e88eSDag-Erling Smørgrav result->right->root->diff_flags); 454*59c8e88eSDag-Erling Smørgrav bool show_function_prototypes = (flags & DIFF_FLAG_SHOW_PROTOTYPES); 455*59c8e88eSDag-Erling Smørgrav bool force_text = (flags & DIFF_FLAG_FORCE_TEXT_DATA); 456*59c8e88eSDag-Erling Smørgrav bool have_binary = (atomizer_flags & DIFF_ATOMIZER_FOUND_BINARY_DATA); 457*59c8e88eSDag-Erling Smørgrav off_t outoff = 0, *offp; 458*59c8e88eSDag-Erling Smørgrav uint8_t *typep; 459*59c8e88eSDag-Erling Smørgrav int rc, i; 460*59c8e88eSDag-Erling Smørgrav 461*59c8e88eSDag-Erling Smørgrav if (!result) 462*59c8e88eSDag-Erling Smørgrav return EINVAL; 463*59c8e88eSDag-Erling Smørgrav if (result->rc != DIFF_RC_OK) 464*59c8e88eSDag-Erling Smørgrav return result->rc; 465*59c8e88eSDag-Erling Smørgrav 466*59c8e88eSDag-Erling Smørgrav if (output_info) { 467*59c8e88eSDag-Erling Smørgrav *output_info = diff_output_info_alloc(); 468*59c8e88eSDag-Erling Smørgrav if (*output_info == NULL) 469*59c8e88eSDag-Erling Smørgrav return ENOMEM; 470*59c8e88eSDag-Erling Smørgrav outinfo = *output_info; 471*59c8e88eSDag-Erling Smørgrav } 472*59c8e88eSDag-Erling Smørgrav 473*59c8e88eSDag-Erling Smørgrav if (have_binary && !force_text) { 474*59c8e88eSDag-Erling Smørgrav for (i = 0; i < result->chunks.len; i++) { 475*59c8e88eSDag-Erling Smørgrav struct diff_chunk *c = &result->chunks.head[i]; 476*59c8e88eSDag-Erling Smørgrav enum diff_chunk_type t = diff_chunk_type(c); 477*59c8e88eSDag-Erling Smørgrav 478*59c8e88eSDag-Erling Smørgrav if (t != CHUNK_MINUS && t != CHUNK_PLUS) 479*59c8e88eSDag-Erling Smørgrav continue; 480*59c8e88eSDag-Erling Smørgrav 481*59c8e88eSDag-Erling Smørgrav if (outinfo && outinfo->line_offsets.len > 0) { 482*59c8e88eSDag-Erling Smørgrav unsigned int idx = 483*59c8e88eSDag-Erling Smørgrav outinfo->line_offsets.len - 1; 484*59c8e88eSDag-Erling Smørgrav outoff = outinfo->line_offsets.head[idx]; 485*59c8e88eSDag-Erling Smørgrav } 486*59c8e88eSDag-Erling Smørgrav 487*59c8e88eSDag-Erling Smørgrav rc = fprintf(dest, "Binary files %s and %s differ\n", 488*59c8e88eSDag-Erling Smørgrav diff_output_get_label_left(info), 489*59c8e88eSDag-Erling Smørgrav diff_output_get_label_right(info)); 490*59c8e88eSDag-Erling Smørgrav if (outinfo) { 491*59c8e88eSDag-Erling Smørgrav ARRAYLIST_ADD(offp, outinfo->line_offsets); 492*59c8e88eSDag-Erling Smørgrav if (offp == NULL) 493*59c8e88eSDag-Erling Smørgrav return ENOMEM; 494*59c8e88eSDag-Erling Smørgrav outoff += rc; 495*59c8e88eSDag-Erling Smørgrav *offp = outoff; 496*59c8e88eSDag-Erling Smørgrav ARRAYLIST_ADD(typep, outinfo->line_types); 497*59c8e88eSDag-Erling Smørgrav if (typep == NULL) 498*59c8e88eSDag-Erling Smørgrav return ENOMEM; 499*59c8e88eSDag-Erling Smørgrav *typep = DIFF_LINE_NONE; 500*59c8e88eSDag-Erling Smørgrav } 501*59c8e88eSDag-Erling Smørgrav break; 502*59c8e88eSDag-Erling Smørgrav } 503*59c8e88eSDag-Erling Smørgrav 504*59c8e88eSDag-Erling Smørgrav return DIFF_RC_OK; 505*59c8e88eSDag-Erling Smørgrav } 506*59c8e88eSDag-Erling Smørgrav 507*59c8e88eSDag-Erling Smørgrav state = diff_output_unidiff_state_alloc(); 508*59c8e88eSDag-Erling Smørgrav if (state == NULL) { 509*59c8e88eSDag-Erling Smørgrav if (output_info) { 510*59c8e88eSDag-Erling Smørgrav diff_output_info_free(*output_info); 511*59c8e88eSDag-Erling Smørgrav *output_info = NULL; 512*59c8e88eSDag-Erling Smørgrav } 513*59c8e88eSDag-Erling Smørgrav return ENOMEM; 514*59c8e88eSDag-Erling Smørgrav } 515*59c8e88eSDag-Erling Smørgrav 516*59c8e88eSDag-Erling Smørgrav #if DEBUG 517*59c8e88eSDag-Erling Smørgrav unsigned int check_left_pos, check_right_pos; 518*59c8e88eSDag-Erling Smørgrav check_left_pos = 0; 519*59c8e88eSDag-Erling Smørgrav check_right_pos = 0; 520*59c8e88eSDag-Erling Smørgrav for (i = 0; i < result->chunks.len; i++) { 521*59c8e88eSDag-Erling Smørgrav struct diff_chunk *c = &result->chunks.head[i]; 522*59c8e88eSDag-Erling Smørgrav enum diff_chunk_type t = diff_chunk_type(c); 523*59c8e88eSDag-Erling Smørgrav 524*59c8e88eSDag-Erling Smørgrav debug("[%d] %s lines L%d R%d @L %d @R %d\n", 525*59c8e88eSDag-Erling Smørgrav i, (t == CHUNK_MINUS ? "minus" : 526*59c8e88eSDag-Erling Smørgrav (t == CHUNK_PLUS ? "plus" : 527*59c8e88eSDag-Erling Smørgrav (t == CHUNK_SAME ? "same" : "?"))), 528*59c8e88eSDag-Erling Smørgrav c->left_count, 529*59c8e88eSDag-Erling Smørgrav c->right_count, 530*59c8e88eSDag-Erling Smørgrav c->left_start ? diff_atom_root_idx(result->left, c->left_start) : -1, 531*59c8e88eSDag-Erling Smørgrav c->right_start ? diff_atom_root_idx(result->right, c->right_start) : -1); 532*59c8e88eSDag-Erling Smørgrav assert(check_left_pos == diff_atom_root_idx(result->left, c->left_start)); 533*59c8e88eSDag-Erling Smørgrav assert(check_right_pos == diff_atom_root_idx(result->right, c->right_start)); 534*59c8e88eSDag-Erling Smørgrav check_left_pos += c->left_count; 535*59c8e88eSDag-Erling Smørgrav check_right_pos += c->right_count; 536*59c8e88eSDag-Erling Smørgrav 537*59c8e88eSDag-Erling Smørgrav } 538*59c8e88eSDag-Erling Smørgrav assert(check_left_pos == result->left->atoms.len); 539*59c8e88eSDag-Erling Smørgrav assert(check_right_pos == result->right->atoms.len); 540*59c8e88eSDag-Erling Smørgrav #endif 541*59c8e88eSDag-Erling Smørgrav 542*59c8e88eSDag-Erling Smørgrav for (i = 0; i < result->chunks.len; i++) { 543*59c8e88eSDag-Erling Smørgrav struct diff_chunk *c = &result->chunks.head[i]; 544*59c8e88eSDag-Erling Smørgrav enum diff_chunk_type t = diff_chunk_type(c); 545*59c8e88eSDag-Erling Smørgrav struct diff_chunk_context next; 546*59c8e88eSDag-Erling Smørgrav 547*59c8e88eSDag-Erling Smørgrav if (t != CHUNK_MINUS && t != CHUNK_PLUS) 548*59c8e88eSDag-Erling Smørgrav continue; 549*59c8e88eSDag-Erling Smørgrav 550*59c8e88eSDag-Erling Smørgrav if (diff_chunk_context_empty(&cc)) { 551*59c8e88eSDag-Erling Smørgrav /* These are the first lines being printed. 552*59c8e88eSDag-Erling Smørgrav * Note down the start point, any number of subsequent 553*59c8e88eSDag-Erling Smørgrav * chunks may be joined up to this unidiff chunk by 554*59c8e88eSDag-Erling Smørgrav * context lines or by being directly adjacent. */ 555*59c8e88eSDag-Erling Smørgrav diff_chunk_context_get(&cc, result, i, context_lines); 556*59c8e88eSDag-Erling Smørgrav debug("new chunk to be printed:" 557*59c8e88eSDag-Erling Smørgrav " chunk %d-%d left %d-%d right %d-%d\n", 558*59c8e88eSDag-Erling Smørgrav cc.chunk.start, cc.chunk.end, 559*59c8e88eSDag-Erling Smørgrav cc.left.start, cc.left.end, 560*59c8e88eSDag-Erling Smørgrav cc.right.start, cc.right.end); 561*59c8e88eSDag-Erling Smørgrav continue; 562*59c8e88eSDag-Erling Smørgrav } 563*59c8e88eSDag-Erling Smørgrav 564*59c8e88eSDag-Erling Smørgrav /* There already is a previous chunk noted down for being 565*59c8e88eSDag-Erling Smørgrav * printed. Does it join up with this one? */ 566*59c8e88eSDag-Erling Smørgrav diff_chunk_context_get(&next, result, i, context_lines); 567*59c8e88eSDag-Erling Smørgrav debug("new chunk to be printed:" 568*59c8e88eSDag-Erling Smørgrav " chunk %d-%d left %d-%d right %d-%d\n", 569*59c8e88eSDag-Erling Smørgrav next.chunk.start, next.chunk.end, 570*59c8e88eSDag-Erling Smørgrav next.left.start, next.left.end, 571*59c8e88eSDag-Erling Smørgrav next.right.start, next.right.end); 572*59c8e88eSDag-Erling Smørgrav 573*59c8e88eSDag-Erling Smørgrav if (diff_chunk_contexts_touch(&cc, &next)) { 574*59c8e88eSDag-Erling Smørgrav /* This next context touches or overlaps the previous 575*59c8e88eSDag-Erling Smørgrav * one, join. */ 576*59c8e88eSDag-Erling Smørgrav diff_chunk_contexts_merge(&cc, &next); 577*59c8e88eSDag-Erling Smørgrav debug("new chunk to be printed touches previous chunk," 578*59c8e88eSDag-Erling Smørgrav " now: left %d-%d right %d-%d\n", 579*59c8e88eSDag-Erling Smørgrav cc.left.start, cc.left.end, 580*59c8e88eSDag-Erling Smørgrav cc.right.start, cc.right.end); 581*59c8e88eSDag-Erling Smørgrav continue; 582*59c8e88eSDag-Erling Smørgrav } 583*59c8e88eSDag-Erling Smørgrav 584*59c8e88eSDag-Erling Smørgrav /* No touching, so the previous context is complete with a gap 585*59c8e88eSDag-Erling Smørgrav * between it and this next one. Print the previous one and 586*59c8e88eSDag-Erling Smørgrav * start fresh here. */ 587*59c8e88eSDag-Erling Smørgrav debug("new chunk to be printed does not touch previous chunk;" 588*59c8e88eSDag-Erling Smørgrav " print left %d-%d right %d-%d\n", 589*59c8e88eSDag-Erling Smørgrav cc.left.start, cc.left.end, cc.right.start, cc.right.end); 590*59c8e88eSDag-Erling Smørgrav output_unidiff_chunk(outinfo, dest, state, info, result, 591*59c8e88eSDag-Erling Smørgrav true, show_function_prototypes, &cc); 592*59c8e88eSDag-Erling Smørgrav cc = next; 593*59c8e88eSDag-Erling Smørgrav debug("new unprinted chunk is left %d-%d right %d-%d\n", 594*59c8e88eSDag-Erling Smørgrav cc.left.start, cc.left.end, cc.right.start, cc.right.end); 595*59c8e88eSDag-Erling Smørgrav } 596*59c8e88eSDag-Erling Smørgrav 597*59c8e88eSDag-Erling Smørgrav if (!diff_chunk_context_empty(&cc)) 598*59c8e88eSDag-Erling Smørgrav output_unidiff_chunk(outinfo, dest, state, info, result, 599*59c8e88eSDag-Erling Smørgrav true, show_function_prototypes, &cc); 600*59c8e88eSDag-Erling Smørgrav diff_output_unidiff_state_free(state); 601*59c8e88eSDag-Erling Smørgrav return DIFF_RC_OK; 602*59c8e88eSDag-Erling Smørgrav } 603