pkgsrc/devel/diffbreaker/files/diffbreaker/diffbreaker.c
2020-10-11 01:57:03 +00:00

702 lines
16 KiB
C

/* $NetBSD: diffbreaker.c,v 1.10 2020/10/11 01:57:04 nat Exp $ */
/*-
* Copyright (c) 2018, 2019 Nathanial Sloss <nathanialsloss@yahoo.com.au>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <curses.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
ssize_t finalize_context(ssize_t context, ssize_t current, ssize_t secthead,
ssize_t first, ssize_t part, ssize_t origoffs,
ssize_t newoffs, ssize_t last, ssize_t pos);
ssize_t find_next_marker(ssize_t current, int direction);
ssize_t update_context(ssize_t lines, ssize_t current, ssize_t last);
ssize_t get_context(ssize_t current, ssize_t last, ssize_t num);
void print_buffer(ssize_t myLine, ssize_t dispLines);
void parse_buffer(char *outfile, bool incremental, uint32_t *filesuffix);
void read_data_to_buffer(char *myFile);
void free_buffers_actions(void);
void setup_screen(void);
void mark_dirty(void);
#ifndef __dead
#define __dead __attribute__((__noreturn__))
#endif
#ifdef __linux__
extern char *__progname;
#define getprogname() __progname
#endif
__dead static void usage(void);
int dispLine, promptLine, scrcols, scrlines;
int displayLines;
WINDOW *mywin = NULL;
static char *buffer;
static char *newbuffer;
static char *action;
static char *newaction;
static ssize_t currentLine, totalLines, cpl;
#define ORIGBUF(x) (buffer + ((x) * cpl))
#define NEWBUF(x) (newbuffer + ((x) * cpl))
#define COLORTEXT(x) \
do { \
if (has_colors()) \
attrset(COLOR_PAIR(x)); \
else \
attrset(A_REVERSE); \
} while (0)
#define NORMALTEXT \
do { \
if (has_colors()) \
attrset(COLOR_PAIR(1)); \
else \
attrset(A_NORMAL); \
} while (0)
void
mark_dirty(void)
{
bool writetome = false, display = false;
bool pending = false, writesect = false;
ssize_t i, j = 0, last = 0, myfile = 0;
size_t len = (size_t)cpl;
j = 0;
for (i = 0; i < totalLines; i++) {
switch (action[i]) {
case 2: /* Selected */
if (*ORIGBUF(i) == '-') {
continue;
}
if (*ORIGBUF(i) == '+') {
strncpy(NEWBUF(j), ORIGBUF(i), len);
*NEWBUF(j) = ' ';
action[j] = 0;
}
break;
case 5: /* @@ marker */
if (pending == false && writesect == false)
j = last;
last = j;
pending = false;
writesect = false;
action[j] = action[i];
strncpy(NEWBUF(j), ORIGBUF(i), len);
break;
case 6: /* --- */
if (pending == false && writesect == false)
j = last;
if (writetome == false)
j = myfile;
myfile = j;
pending = true;
writetome = false;
writesect = true;
strncpy(NEWBUF(j), ORIGBUF(i), len);
action[j] = action[i];
break;
case 7: /* +++ */
strncpy(NEWBUF(j), ORIGBUF(i), len);
action[j] = action[i];
break;
case 1: /* unselected change */
display = true;
writesect = true;
writetome = true;
/* fallthrough */
default:
strncpy(NEWBUF(j), ORIGBUF(i), len);
action[j] = action[i];
break;
}
strncpy(ORIGBUF(j), NEWBUF(j), len);
j++;
}
totalLines = display ? j-- : 0;
return;
}
#define DIR_UP -1
#define DIR_DOWN 1
ssize_t
find_next_marker(ssize_t current, int direction)
{
ssize_t newline = current;
while (newline >= 0 && newline < totalLines - 1) {
newline += direction;
if (action[newline] != 0)
break;
}
if (newline < 0 || newline >= totalLines - 1)
return current;
return newline;
}
ssize_t
update_context(ssize_t lines, ssize_t current, ssize_t last)
{
ssize_t j, l = 0;
j = current;
while (lines > 0) {
if (*ORIGBUF(last + l) == '+') {
l++;
continue;
}
sprintf(NEWBUF(j), "%s", ORIGBUF(last + l++));
*NEWBUF(j++) = ' ';
if (last + l > totalLines)
break;
lines--;
}
return j;
}
ssize_t
get_context(ssize_t current, ssize_t last, ssize_t num)
{
ssize_t context = 0, tmpcontext = 0, i, j, l;
i = current - 1;
j = last;
for (l = 0; tmpcontext < num; l++) {
if ((l + i) > totalLines)
break;
if (i - l >= 0)
context++;
if (action[i - l] == 2)
goto next;
if (action[i - l] != 6 && *ORIGBUF(i - l) == '-') {
tmpcontext++;
continue;
}
if (*ORIGBUF(i - l) == ' ') {
tmpcontext++;
continue;
}
next:
if (context == tmpcontext) {
j = update_context(tmpcontext, j, i - context - 1);
tmpcontext = 0;
num -= tmpcontext;
}
if (action[i - l] == 6 || action[i - l] == 7)
break;
if (action[i - l] == 2)
break;
}
if (l > 0)
l--;
if (tmpcontext)
j = update_context(tmpcontext, j, i - l);
return j;
}
ssize_t
finalize_context(ssize_t context, ssize_t current, ssize_t secthead,
ssize_t first, ssize_t part, ssize_t origoffs,
ssize_t newoffs, ssize_t last, ssize_t pos)
{
ssize_t blanks, orig, addition, l;
ssize_t fixoffs, j, begblanks;
bool firstf;
fixoffs = secthead;
j = current;
firstf = false;
begblanks = 0;
j = update_context(context, j, last);
blanks = orig = addition = 0;
for (l = secthead + 1; l < j; l++) {
if (*NEWBUF(l) == ' ') {
if (!firstf)
begblanks++;
blanks++;
}
if (*NEWBUF(l) == '-') {
firstf = true;
orig++;
}
if (*NEWBUF(l) == '+') {
firstf = true;
addition++;
}
}
sprintf(NEWBUF(fixoffs), "@@ %ld,%ld +%ld,%ld @@\n",
origoffs - first + part + (begblanks - context),
orig + blanks, newoffs +
first - part - (begblanks - context), addition + blanks);
newaction[secthead] = action[pos];
return j;
}
void
parse_buffer(char *outfile, bool incremental, uint32_t *filesuffix)
{
ssize_t origoffs = 0, newoffs = 0, first = 0, last = 0, part = 0;
ssize_t final = 0, context = 0, fixoffs = 0, i, j, adj;
bool found = false, writecount = false;
char tmpstr[100], tmpstr1[100];
FILE *myfile = NULL;
j = 0;
for (i = 0; i < totalLines; i++) {
switch (action[i]) {
case 6: /* --- */
if (found == true) {
j = finalize_context(context, j, fixoffs, first,
part, origoffs, newoffs, last, i);
final = j;
last = i;
found = false;
}
first = 0;
if (writecount == false)
j = final;
newaction[j] = action[i];
sprintf(NEWBUF(j++), "%s", ORIGBUF(i));
break;
case 5: /* @@ */
if (found == true) {
j = finalize_context(context, j, fixoffs, first,
part, origoffs, newoffs, last, i);
final = j;
last = i;
found = false;
}
first = 0;
part = 0;
fixoffs = j;
// XXX: this was uninitialized according to valgrind
// check the sscanf return?
tmpstr[0] = '\0';
tmpstr1[0] = '\0';
sscanf(ORIGBUF(i), "@@ %s %s\n", tmpstr, tmpstr1);
char *rest = NULL;
origoffs = strtol(tmpstr, &rest, 10);
rest++;
(void)strtol(rest, &rest, 10);
newoffs = strtol(tmpstr1, &rest, 10);
rest++;
//(void)strtol(rest, &rest, 10);
break;
case 1: /* unselected change */
if (part == 0)
part = i;
if (part && !found && *ORIGBUF(i) == '+')
part++;
break;
case 2: /* selected */
adj = 0;
if (!found) {
writecount = true;
context = 0;
first = i;
last = i;
j++;
adj = 3;
if (part == 0)
part = i;
found = true;
}
j = get_context(i, j, i - last + adj);
sprintf(NEWBUF(j++), "%s", ORIGBUF(i));
last = i;
if (*ORIGBUF(i) == '-')
last++;
final = j;
context = 3;
break;
case 7:
case 4:
writecount = false;
newaction[j] = action[i];
sprintf(NEWBUF(j++), "%s", ORIGBUF(i));
default:
break;
}
}
if (context && found)
j = finalize_context(context, j, fixoffs, first,
part, origoffs, newoffs, last, i);
if (writecount == false && found == false)
j = final;
char tmppath [512];
if (incremental)
snprintf(tmppath, sizeof(tmppath), "%s.%d.diff", outfile,
*filesuffix);
else
snprintf(tmppath, sizeof(tmppath), "%s", outfile);
if (j == 0)
return;
if (!strcmp(tmppath, "-"))
myfile = stderr;
else if ((myfile = fopen(tmppath, "a")) == NULL) {
free_buffers_actions();
errx(errno, "Could not create/open file(s) for writing.");
/* Not reached */
}
for (i = 0; i < j; i++)
fprintf(myfile, "%s", NEWBUF(i));
fclose(myfile);
*filesuffix += 1;
return;
}
void
free_buffers_actions(void)
{
free(buffer);
free(newbuffer);
free(newaction);
free(action);
buffer = NULL;
newbuffer = NULL;
newaction = NULL;
action = NULL;
}
void
read_data_to_buffer(char *myFile)
{
char data, line[512], myaction = 0;
ssize_t nr;
ssize_t l = 0, j = 0, n = 0;
int fd, i;
if ((fd = open(myFile, O_RDONLY)) < 0)
errx(errno, "Error Opening file %s.", myFile);
memset(line, 0, sizeof(line));
i = 0;
cpl = 0;
while ((nr = read(fd, line, sizeof(line))) > 0) {
for (i = 0; i < nr; i++) {
j++;
if (line[i] == '\n') {
n++;
if (j > cpl)
cpl = j;
j = 0;
}
}
}
cpl++;
l = n;
size_t totalalloc = (size_t)(l * cpl);
if (totalalloc <= 0 || l <= 0)
errx(EXIT_FAILURE, "File mem calc error.");
if ((newaction = calloc((size_t)l, sizeof(*newaction))) == NULL)
errx(errno, "File mem alloc error.");
if ((action = calloc((size_t)l, sizeof(*action))) == NULL) {
free(newaction);
errx(errno, "File mem alloc error.");
}
if ((buffer = calloc(totalalloc, sizeof(*buffer))) == NULL) {
free(action);
free(newaction);
errx(errno, "File mem alloc error.");
}
if ((newbuffer = calloc(totalalloc, sizeof(*newbuffer))) == NULL) {
free(buffer);
free(action);
free(newaction);
errx(errno, "File mem alloc error.");
}
memset(newaction, 0, (size_t)l * sizeof(*newaction));
memset(action, 0, (size_t)l * sizeof(*action));
memset(buffer, 0, (size_t)totalalloc * sizeof(*buffer));
memset(newbuffer, 0, (size_t)totalalloc * sizeof(*newbuffer));
j = n = 0;
lseek(fd, 0, SEEK_SET);
while ((nr = read(fd, line, sizeof(line))) > 0) {
for (i = 0; i < nr; i++) {
data = line[i];
*(ORIGBUF(j) + n++) = data;
if (data == '\n') {
myaction = 0;
if (*ORIGBUF(j) == '-' && *(ORIGBUF(j) + 1) ==
'-' && *(ORIGBUF(j) + 2) == '-')
myaction = 6;
else if (*ORIGBUF(j) == '+' && *(ORIGBUF(j) + 1) ==
'+' && *(ORIGBUF(j) + 2) == '+')
myaction = 7;
else if (*ORIGBUF(j) == '+' && myaction != 7)
myaction = 1;
else if (*ORIGBUF(j) == '-' && myaction != 6)
myaction = 1;
else if (*ORIGBUF(j) == '@' && *(ORIGBUF(j) + 1) ==
'@')
myaction = 5;
else if (*ORIGBUF(j) != ' ')
myaction = 4;
action[j] = myaction;
if (action[j] != 4)
j++;
else {
memset(ORIGBUF(j), 0, (size_t)cpl);
action[j] = 0;
}
n = 0;
}
}
}
totalLines = j--;
close(fd);
return;
}
void
print_buffer(ssize_t myLine, ssize_t dispLines)
{
static char tmpline[512];
ssize_t i, m, startLine;
int j = 0;
startLine = (myLine / dispLines) * dispLines;
move (0, 0);
for (i = startLine; i < (startLine + dispLines) && i < totalLines;
i++) {
NORMALTEXT;
if (action[i] == 2 && i == myLine)
COLORTEXT(4);
else if (i == myLine)
COLORTEXT(2);
else if (action[i] == 2)
COLORTEXT(5);
else if (action[i] == 4)
COLORTEXT(5);
else if (action[i] == 5)
COLORTEXT(3);
else if (action[i] == 6 || action[i] == 7)
COLORTEXT(3);
if (action[i] != 9) {
snprintf(tmpline, sizeof(tmpline), "%s", ORIGBUF(i));
int p = 0;
ssize_t llen = (ssize_t)strlen(tmpline);
char tmpstr[1024];
for (m = 0; m < llen; m++) {
if ((tmpline[m] < ' ') && (tmpline[m] != '\t')
&& (tmpline[m] != '\n')) {
snprintf(tmpstr+p, (size_t)(1024 - p), "%s",
unctrl(tmpline[m]));
p += (int)strlen(unctrl(tmpline[m])) - 1;
} else if (tmpline[m] == '\t') {
snprintf(tmpstr+p, (size_t)(1024 - p), " ");
p += 3;
} else
snprintf(tmpstr+p, (size_t)(1024 - p), "%c", tmpline[m]);
p++;
}
p = 0;
llen = (ssize_t)strlen(tmpstr);
for (m = 0; m < llen && p < (scrcols - 1); m++)
mvprintw(j, p++, "%c", tmpstr[m]);
j++;
}
}
NORMALTEXT;
for (; i < startLine + dispLines; i++) {
mvprintw(j, 0, "\n");
j++;
}
COLORTEXT(2);
mvprintw(promptLine, 0, "%ld lines remaining", totalLines);
refresh();
return;
}
void
usage(void)
{
fprintf(stderr, "Usage: %s [-s] -i infile -o outpath/file\n",
getprogname());
exit(EXIT_FAILURE);
/* Not reached */
}
void
setup_screen(void)
{
if (!(mywin = initscr())) {
free_buffers_actions();
errx(EXIT_FAILURE, "\nUnknown terminal type.");
}
scrlines = LINES;
scrcols = COLS;
if (scrlines <= 0 || scrcols <= 0) {
delwin(mywin);
endwin();
mywin = NULL;
free_buffers_actions();
errx(EXIT_FAILURE, "\nUnknown screen dimensions.");
}
promptLine = scrlines - 1;
displayLines = scrlines - 2;
if (has_colors()) {
start_color();
init_pair(1, COLOR_WHITE, COLOR_BLACK);
init_pair(2, COLOR_BLACK, COLOR_WHITE);
init_pair(3, COLOR_WHITE, COLOR_BLUE);
init_pair(4, COLOR_WHITE, COLOR_YELLOW);
init_pair(5, COLOR_WHITE, COLOR_GREEN);
init_pair(6, COLOR_WHITE, COLOR_RED);
NORMALTEXT;
}
}
int
main(int argc, char *argv[])
{
char *infile = NULL, *outfile = NULL;
bool incremental = false;
uint32_t filesuffix = 1;
int ch;
char myKey;
dispLine = 0;
currentLine = 0;
while ((ch = getopt(argc, argv, "si:o:")) != EOF) {
switch (ch) {
case 's':
incremental = true;
break;
case 'i':
infile = (char *)(optarg);
break;
case 'o':
outfile = (char *)(optarg);
break;
default:
usage();
/* Not reached */
}
}
argc -= optind;
argv += optind;
if (infile == NULL || outfile == NULL) {
usage();
/* Not reached */
}
read_data_to_buffer(infile);
setup_screen();
mark_dirty();
print_buffer(currentLine, (ssize_t)(displayLines + 1));
COLORTEXT(2);
for (;;) {
myKey = (char)getch();
if (myKey == 'q')
break;
if (myKey == 'k' && currentLine > 0)
currentLine = find_next_marker(currentLine, DIR_UP);
if (myKey == ' ') {
if (action[currentLine] == 1)
action[currentLine] = 2;
else if (action[currentLine] == 2)
action[currentLine] = 1;
if (currentLine < totalLines -1)
currentLine = find_next_marker(currentLine,
DIR_DOWN);
}
if (myKey == 'j' && currentLine < totalLines - 1)
currentLine = find_next_marker(currentLine, DIR_DOWN);
if (myKey == 'G')
currentLine = totalLines - 1;
if (myKey == 'g')
currentLine = 0;
if (myKey == 'l') {
if (is_term_resized(LINES, COLS)) {
resizeterm(LINES, COLS);
scrcols = COLS;
scrlines = LINES;
promptLine = scrlines - 1;
displayLines = scrlines - 2;
}
}
if (myKey == 'w') {
currentLine = 0;
parse_buffer(outfile, incremental, &filesuffix);
mark_dirty();
if (totalLines <= 0)
break;
}
print_buffer(currentLine, (ssize_t)(displayLines + 1));
}
delwin(mywin);
endwin();
free_buffers_actions();
mywin = NULL;
return EXIT_SUCCESS;
}