aboutsummaryrefslogtreecommitdiff
path: root/contrib/libdiff/lib/diff_output.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/libdiff/lib/diff_output.c')
-rw-r--r--contrib/libdiff/lib/diff_output.c415
1 files changed, 415 insertions, 0 deletions
diff --git a/contrib/libdiff/lib/diff_output.c b/contrib/libdiff/lib/diff_output.c
new file mode 100644
index 000000000000..2a0d95896e1a
--- /dev/null
+++ b/contrib/libdiff/lib/diff_output.c
@@ -0,0 +1,415 @@
+/* Common parts for printing diff output */
+/*
+ * Copyright (c) 2020 Neels Hofmeyr <neels@hofmeyr.de>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <arraylist.h>
+#include <diff_main.h>
+#include <diff_output.h>
+
+#include "diff_internal.h"
+
+static bool color;
+static const char *del_code = "31";
+static const char *add_code = "32";
+
+void
+diff_output_set_colors(bool _color,
+ const char *_del_code,
+ const char *_add_code)
+{
+ color = _color;
+ if (_del_code)
+ del_code = _del_code;
+ if (_add_code)
+ add_code = _add_code;
+}
+
+static int
+get_atom_byte(int *ch, struct diff_atom *atom, off_t off)
+{
+ off_t cur;
+
+ if (atom->at != NULL) {
+ *ch = atom->at[off];
+ return 0;
+ }
+
+ cur = ftello(atom->root->f);
+ if (cur == -1)
+ return errno;
+
+ if (cur != atom->pos + off &&
+ fseeko(atom->root->f, atom->pos + off, SEEK_SET) == -1)
+ return errno;
+
+ *ch = fgetc(atom->root->f);
+ if (*ch == EOF && ferror(atom->root->f))
+ return errno;
+
+ return 0;
+}
+
+#define DIFF_OUTPUT_BUF_SIZE 512
+
+int
+diff_output_lines(struct diff_output_info *outinfo, FILE *dest,
+ const char *prefix, struct diff_atom *start_atom,
+ unsigned int count)
+{
+ struct diff_atom *atom;
+ off_t outoff = 0, *offp;
+ uint8_t *typep;
+ int rc;
+ bool colored;
+
+ if (outinfo && outinfo->line_offsets.len > 0) {
+ unsigned int idx = outinfo->line_offsets.len - 1;
+ outoff = outinfo->line_offsets.head[idx];
+ }
+
+ if (color) {
+ colored = true;
+ if (*prefix == '-' || *prefix == '<')
+ printf("\033[%sm", del_code);
+ else if (*prefix == '+' || *prefix == '>')
+ printf("\033[%sm", add_code);
+ else
+ colored = false;
+ } else {
+ colored = false;
+ }
+
+ foreach_diff_atom(atom, start_atom, count) {
+ off_t outlen = 0;
+ int i, ch, nbuf = 0;
+ size_t len = atom->len, wlen;
+ char buf[DIFF_OUTPUT_BUF_SIZE + 1 /* '\n' */];
+ size_t n;
+
+ n = strlcpy(buf, prefix, sizeof(buf));
+ if (n >= DIFF_OUTPUT_BUF_SIZE) { /* leave room for '\n' */
+ rc = ENOBUFS;
+ goto out;
+ }
+ nbuf += n;
+
+ if (len) {
+ rc = get_atom_byte(&ch, atom, len - 1);
+ if (rc)
+ goto out;
+ if (ch == '\n')
+ len--;
+ }
+
+ for (i = 0; i < len; i++) {
+ rc = get_atom_byte(&ch, atom, i);
+ if (rc)
+ goto out;
+ if (nbuf >= DIFF_OUTPUT_BUF_SIZE) {
+ wlen = fwrite(buf, 1, nbuf, dest);
+ if (wlen != nbuf) {
+ rc = errno;
+ goto out;
+ }
+ outlen += wlen;
+ nbuf = 0;
+ }
+ buf[nbuf++] = ch;
+ }
+ buf[nbuf++] = '\n';
+ wlen = fwrite(buf, 1, nbuf, dest);
+ if (wlen != nbuf) {
+ rc = errno;
+ goto out;
+ }
+ outlen += wlen;
+ if (outinfo) {
+ ARRAYLIST_ADD(offp, outinfo->line_offsets);
+ if (offp == NULL) {
+ rc = ENOMEM;
+ goto out;
+ }
+ outoff += outlen;
+ *offp = outoff;
+ ARRAYLIST_ADD(typep, outinfo->line_types);
+ if (typep == NULL) {
+ rc = ENOMEM;
+ goto out;
+ }
+ *typep = *prefix == ' ' ? DIFF_LINE_CONTEXT :
+ *prefix == '-' ? DIFF_LINE_MINUS :
+ *prefix == '+' ? DIFF_LINE_PLUS : DIFF_LINE_NONE;
+ }
+ }
+
+ rc = DIFF_RC_OK;
+out:
+ if (colored)
+ printf("\033[m");
+ return rc;
+}
+
+int
+diff_output_chunk_left_version(struct diff_output_info **output_info,
+ FILE *dest,
+ const struct diff_input_info *info,
+ const struct diff_result *result,
+ const struct diff_chunk_context *cc)
+{
+ int rc, c_idx;
+ struct diff_output_info *outinfo = NULL;
+
+ if (diff_range_empty(&cc->left))
+ return DIFF_RC_OK;
+
+ if (output_info) {
+ *output_info = diff_output_info_alloc();
+ if (*output_info == NULL)
+ return ENOMEM;
+ outinfo = *output_info;
+ }
+
+ /* Write out all chunks on the left side. */
+ for (c_idx = cc->chunk.start; c_idx < cc->chunk.end; c_idx++) {
+ const struct diff_chunk *c = &result->chunks.head[c_idx];
+
+ if (c->left_count) {
+ rc = diff_output_lines(outinfo, dest, "",
+ c->left_start, c->left_count);
+ if (rc)
+ return rc;
+ }
+ }
+
+ return DIFF_RC_OK;
+}
+
+int
+diff_output_chunk_right_version(struct diff_output_info **output_info,
+ FILE *dest,
+ const struct diff_input_info *info,
+ const struct diff_result *result,
+ const struct diff_chunk_context *cc)
+{
+ int rc, c_idx;
+ struct diff_output_info *outinfo = NULL;
+
+ if (diff_range_empty(&cc->right))
+ return DIFF_RC_OK;
+
+ if (output_info) {
+ *output_info = diff_output_info_alloc();
+ if (*output_info == NULL)
+ return ENOMEM;
+ outinfo = *output_info;
+ }
+
+ /* Write out all chunks on the right side. */
+ for (c_idx = cc->chunk.start; c_idx < cc->chunk.end; c_idx++) {
+ const struct diff_chunk *c = &result->chunks.head[c_idx];
+
+ if (c->right_count) {
+ rc = diff_output_lines(outinfo, dest, "", c->right_start,
+ c->right_count);
+ if (rc)
+ return rc;
+ }
+ }
+
+ return DIFF_RC_OK;
+}
+
+int
+diff_output_trailing_newline_msg(struct diff_output_info *outinfo, FILE *dest,
+ const struct diff_chunk *c)
+{
+ enum diff_chunk_type chunk_type = diff_chunk_type(c);
+ struct diff_atom *atom, *start_atom;
+ unsigned int atom_count;
+ int rc, ch;
+ off_t outoff = 0, *offp;
+ uint8_t *typep;
+
+
+ if (chunk_type == CHUNK_MINUS || chunk_type == CHUNK_SAME) {
+ start_atom = c->left_start;
+ atom_count = c->left_count;
+ } else if (chunk_type == CHUNK_PLUS) {
+ start_atom = c->right_start;
+ atom_count = c->right_count;
+ } else
+ return EINVAL;
+
+ /* Locate the last atom. */
+ if (atom_count == 0)
+ return EINVAL;
+ atom = &start_atom[atom_count - 1];
+
+ rc = get_atom_byte(&ch, atom, atom->len - 1);
+ if (rc != DIFF_RC_OK)
+ return rc;
+
+ if (ch != '\n') {
+ if (outinfo && outinfo->line_offsets.len > 0) {
+ unsigned int idx = outinfo->line_offsets.len - 1;
+ outoff = outinfo->line_offsets.head[idx];
+ }
+ rc = fprintf(dest, "\\ No newline at end of file\n");
+ if (rc < 0)
+ return errno;
+ if (outinfo) {
+ ARRAYLIST_ADD(offp, outinfo->line_offsets);
+ if (offp == NULL)
+ return ENOMEM;
+ outoff += rc;
+ *offp = outoff;
+ ARRAYLIST_ADD(typep, outinfo->line_types);
+ if (typep == NULL)
+ return ENOMEM;
+ *typep = DIFF_LINE_NONE;
+ }
+ }
+
+ return DIFF_RC_OK;
+}
+
+static bool
+is_function_prototype(char ch)
+{
+ return (isalpha((unsigned char)ch) || ch == '_' || ch == '$' ||
+ ch == '-' || ch == '+');
+}
+
+#define begins_with(s, pre) (strncmp(s, pre, sizeof(pre)-1) == 0)
+
+int
+diff_output_match_function_prototype(char *prototype, size_t prototype_size,
+ int *last_prototype_idx, const struct diff_result *result,
+ int chunk_start_line)
+{
+ struct diff_atom *start_atom, *atom;
+ const struct diff_data *data;
+ char buf[DIFF_FUNCTION_CONTEXT_SIZE];
+ const char *state = NULL;
+ int rc, i, ch;
+
+ if (result->left->atoms.len > 0 && chunk_start_line > 0) {
+ data = result->left;
+ start_atom = &data->atoms.head[chunk_start_line - 1];
+ } else
+ return DIFF_RC_OK;
+
+ diff_data_foreach_atom_backwards_from(start_atom, atom, data) {
+ int atom_idx = diff_atom_root_idx(data, atom);
+ if (atom_idx < *last_prototype_idx)
+ break;
+ rc = get_atom_byte(&ch, atom, 0);
+ if (rc)
+ return rc;
+ buf[0] = ch;
+ if (!is_function_prototype(buf[0]))
+ continue;
+ for (i = 1; i < atom->len && i < sizeof(buf) - 1; i++) {
+ rc = get_atom_byte(&ch, atom, i);
+ if (rc)
+ return rc;
+ if (ch == '\n')
+ break;
+ buf[i] = ch;
+ }
+ buf[i] = '\0';
+ if (begins_with(buf, "private:")) {
+ if (!state)
+ state = " (private)";
+ } else if (begins_with(buf, "protected:")) {
+ if (!state)
+ state = " (protected)";
+ } else if (begins_with(buf, "public:")) {
+ if (!state)
+ state = " (public)";
+ } else {
+ if (state) /* don't care about truncation */
+ strlcat(buf, state, sizeof(buf));
+ strlcpy(prototype, buf, prototype_size);
+ break;
+ }
+ }
+
+ *last_prototype_idx = diff_atom_root_idx(data, start_atom);
+ return DIFF_RC_OK;
+}
+
+struct diff_output_info *
+diff_output_info_alloc(void)
+{
+ struct diff_output_info *output_info;
+ off_t *offp;
+ uint8_t *typep;
+
+ output_info = malloc(sizeof(*output_info));
+ if (output_info != NULL) {
+ ARRAYLIST_INIT(output_info->line_offsets, 128);
+ ARRAYLIST_ADD(offp, output_info->line_offsets);
+ if (offp == NULL) {
+ diff_output_info_free(output_info);
+ return NULL;
+ }
+ *offp = 0;
+ ARRAYLIST_INIT(output_info->line_types, 128);
+ ARRAYLIST_ADD(typep, output_info->line_types);
+ if (typep == NULL) {
+ diff_output_info_free(output_info);
+ return NULL;
+ }
+ *typep = DIFF_LINE_NONE;
+ }
+ return output_info;
+}
+
+void
+diff_output_info_free(struct diff_output_info *output_info)
+{
+ ARRAYLIST_FREE(output_info->line_offsets);
+ ARRAYLIST_FREE(output_info->line_types);
+ free(output_info);
+}
+
+const char *
+diff_output_get_label_left(const struct diff_input_info *info)
+{
+ if (info->flags & DIFF_INPUT_LEFT_NONEXISTENT)
+ return "/dev/null";
+
+ return info->left_path ? info->left_path : "a";
+}
+
+const char *
+diff_output_get_label_right(const struct diff_input_info *info)
+{
+ if (info->flags & DIFF_INPUT_RIGHT_NONEXISTENT)
+ return "/dev/null";
+
+ return info->right_path ? info->right_path : "b";
+}