Path: blob/main/contrib/libdiff/lib/diff_output_plain.c
35065 views
/* Output all lines of a diff_result. */1/*2* Copyright (c) 2020 Neels Hofmeyr <[email protected]>3*4* Permission to use, copy, modify, and distribute this software for any5* purpose with or without fee is hereby granted, provided that the above6* copyright notice and this permission notice appear in all copies.7*8* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES9* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF10* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR11* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES12* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN13* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF14* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.15*/1617#include <errno.h>18#include <stdint.h>19#include <stdio.h>20#include <stdbool.h>21#include <stdlib.h>2223#include <arraylist.h>24#include <diff_main.h>25#include <diff_output.h>2627#include "diff_internal.h"2829static int30output_plain_chunk(struct diff_output_info *outinfo,31FILE *dest, const struct diff_input_info *info,32const struct diff_result *result,33struct diff_chunk_context *cc, off_t *outoff, bool headers_only)34{35off_t *offp;36int left_start, left_len, right_start, right_len;37int rc;38bool change = false;3940left_len = cc->left.end - cc->left.start;41if (left_len < 0)42return EINVAL;43else if (result->left->atoms.len == 0)44left_start = 0;45else if (left_len == 0 && cc->left.start > 0)46left_start = cc->left.start;47else if (cc->left.end > 0)48left_start = cc->left.start + 1;49else50left_start = cc->left.start;5152right_len = cc->right.end - cc->right.start;53if (right_len < 0)54return EINVAL;55else if (result->right->atoms.len == 0)56right_start = 0;57else if (right_len == 0 && cc->right.start > 0)58right_start = cc->right.start;59else if (cc->right.end > 0)60right_start = cc->right.start + 1;61else62right_start = cc->right.start;6364if (left_len == 0) {65/* addition */66if (right_len == 1) {67rc = fprintf(dest, "%da%d\n", left_start, right_start);68} else {69rc = fprintf(dest, "%da%d,%d\n", left_start,70right_start, cc->right.end);71}72} else if (right_len == 0) {73/* deletion */74if (left_len == 1) {75rc = fprintf(dest, "%dd%d\n", left_start,76right_start);77} else {78rc = fprintf(dest, "%d,%dd%d\n", left_start,79cc->left.end, right_start);80}81} else {82/* change */83change = true;84if (left_len == 1 && right_len == 1) {85rc = fprintf(dest, "%dc%d\n", left_start, right_start);86} else if (left_len == 1) {87rc = fprintf(dest, "%dc%d,%d\n", left_start,88right_start, cc->right.end);89} else if (right_len == 1) {90rc = fprintf(dest, "%d,%dc%d\n", left_start,91cc->left.end, right_start);92} else {93rc = fprintf(dest, "%d,%dc%d,%d\n", left_start,94cc->left.end, right_start, cc->right.end);95}96}97if (rc < 0)98return errno;99if (outinfo) {100ARRAYLIST_ADD(offp, outinfo->line_offsets);101if (offp == NULL)102return ENOMEM;103*outoff += rc;104*offp = *outoff;105}106107/*108* Now write out all the joined chunks.109*110* If the hunk denotes a change, it will come in the form of a deletion111* chunk followed by a addition chunk. Print a marker to break up the112* additions and deletions when this happens.113*/114int c_idx;115for (c_idx = cc->chunk.start; !headers_only && c_idx < cc->chunk.end;116c_idx++) {117const struct diff_chunk *c = &result->chunks.head[c_idx];118if (c->left_count && !c->right_count)119rc = diff_output_lines(outinfo, dest,120c->solved ? "< " : "?",121c->left_start, c->left_count);122else if (c->right_count && !c->left_count) {123if (change) {124rc = fprintf(dest, "---\n");125if (rc < 0)126return errno;127if (outinfo) {128ARRAYLIST_ADD(offp,129outinfo->line_offsets);130if (offp == NULL)131return ENOMEM;132*outoff += rc;133*offp = *outoff;134}135}136rc = diff_output_lines(outinfo, dest,137c->solved ? "> " : "?",138c->right_start, c->right_count);139}140if (rc)141return rc;142if (cc->chunk.end == result->chunks.len) {143rc = diff_output_trailing_newline_msg(outinfo, dest, c);144if (rc != DIFF_RC_OK)145return rc;146}147}148149return DIFF_RC_OK;150}151152int153diff_output_plain(struct diff_output_info **output_info,154FILE *dest, const struct diff_input_info *info,155const struct diff_result *result, int hunk_headers_only)156{157struct diff_output_info *outinfo = NULL;158struct diff_chunk_context cc = {};159int atomizer_flags = (result->left->atomizer_flags|160result->right->atomizer_flags);161int flags = (result->left->root->diff_flags |162result->right->root->diff_flags);163bool force_text = (flags & DIFF_FLAG_FORCE_TEXT_DATA);164bool have_binary = (atomizer_flags & DIFF_ATOMIZER_FOUND_BINARY_DATA);165int i, rc;166off_t outoff = 0, *offp;167168if (!result)169return EINVAL;170if (result->rc != DIFF_RC_OK)171return result->rc;172173if (output_info) {174*output_info = diff_output_info_alloc();175if (*output_info == NULL)176return ENOMEM;177outinfo = *output_info;178}179180if (have_binary && !force_text) {181for (i = 0; i < result->chunks.len; i++) {182struct diff_chunk *c = &result->chunks.head[i];183enum diff_chunk_type t = diff_chunk_type(c);184185if (t != CHUNK_MINUS && t != CHUNK_PLUS)186continue;187188rc = fprintf(dest, "Binary files %s and %s differ\n",189diff_output_get_label_left(info),190diff_output_get_label_right(info));191if (rc < 0)192return errno;193if (outinfo) {194ARRAYLIST_ADD(offp, outinfo->line_offsets);195if (offp == NULL)196return ENOMEM;197outoff += rc;198*offp = outoff;199}200break;201}202203return DIFF_RC_OK;204}205206for (i = 0; i < result->chunks.len; i++) {207struct diff_chunk *chunk = &result->chunks.head[i];208enum diff_chunk_type t = diff_chunk_type(chunk);209struct diff_chunk_context next;210211if (t != CHUNK_MINUS && t != CHUNK_PLUS)212continue;213214if (diff_chunk_context_empty(&cc)) {215/* Note down the start point, any number of subsequent216* chunks may be joined up to this chunk by being217* directly adjacent. */218diff_chunk_context_get(&cc, result, i, 0);219continue;220}221222/* There already is a previous chunk noted down for being223* printed. Does it join up with this one? */224diff_chunk_context_get(&next, result, i, 0);225226if (diff_chunk_contexts_touch(&cc, &next)) {227/* This next context touches or overlaps the previous228* one, join. */229diff_chunk_contexts_merge(&cc, &next);230/* When we merge the last chunk we can end up with one231* hanging chunk and have to come back for it after the232* loop */233continue;234}235rc = output_plain_chunk(outinfo, dest, info, result, &cc,236&outoff, hunk_headers_only);237if (rc != DIFF_RC_OK)238return rc;239cc = next;240}241if (!diff_chunk_context_empty(&cc))242return output_plain_chunk(outinfo, dest, info, result, &cc,243&outoff, hunk_headers_only);244return DIFF_RC_OK;245}246247248