freebsd-dev/usr.bin/sdiff/edit.c
Baptiste Daroussin 13b5b54865 import sdiff(1) from GSoC 2012
Import sdiff(1) from the diff version written by Raymond Lai,
improved during GSoC 2012 by Jesse Hagewood.

Compared to the version done in during that summer of code:
- Remove the zlib frontend: zsdiff
- Compatible output (column size and separators) with GNU sdiff

Compared to GNU sdiff in ports:
- The only difference is padding using spaces vs tabs

Compared to OpenBSD and NetBSD import:
- Implement missing options (including long options) from GNU sdiff
- Improved support for the edition mode (signal handling)
- Output visually compatible with GNU sdiff: size of columns

While here import regression tests from NetBSD adapted to fit the output as
expected by GNU sdiff

Reviewed by:	emaste (in part)
Obtained from:	OpenBSD, NetBSD, GSoC 2012
Relnotes:	yes
Differential Revision:	https://reviews.freebsd.org/D5981
Differential Revision:	https://reviews.freebsd.org/D6032 (diff with NetBSD version)
Differential Revision:	https://reviews.freebsd.org/D6033 (diff with OpenBSD version)
2016-04-29 23:27:15 +00:00

210 lines
4.1 KiB
C

/* $OpenBSD: edit.c,v 1.19 2009/06/07 13:29:50 ray Exp $ */
/*
* Written by Raymond Lai <ray@cyth.net>.
* Public domain.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <sys/wait.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <paths.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "common.h"
#include "extern.h"
int editit(const char *);
/*
* Execute an editor on the specified pathname, which is interpreted
* from the shell. This means flags may be included.
*
* Returns -1 on error, or the exit value on success.
*/
int
editit(const char *pathname)
{
char *argp[] = {"sh", "-c", NULL, NULL}, *ed, *p;
sig_t sighup, sigint, sigquit, sigchld;
pid_t pid;
int saved_errno, st, ret = -1;
ed = getenv("VISUAL");
if (ed == NULL || ed[0] == '\0')
ed = getenv("EDITOR");
if (ed == NULL || ed[0] == '\0')
ed = _PATH_VI;
if (asprintf(&p, "%s %s", ed, pathname) == -1)
return (-1);
argp[2] = p;
sighup = signal(SIGHUP, SIG_IGN);
sigint = signal(SIGINT, SIG_IGN);
sigquit = signal(SIGQUIT, SIG_IGN);
sigchld = signal(SIGCHLD, SIG_DFL);
if ((pid = fork()) == -1)
goto fail;
if (pid == 0) {
execv(_PATH_BSHELL, argp);
_exit(127);
}
while (waitpid(pid, &st, 0) == -1)
if (errno != EINTR)
goto fail;
if (!WIFEXITED(st))
errno = EINTR;
else
ret = WEXITSTATUS(st);
fail:
saved_errno = errno;
(void)signal(SIGHUP, sighup);
(void)signal(SIGINT, sigint);
(void)signal(SIGQUIT, sigquit);
(void)signal(SIGCHLD, sigchld);
free(p);
errno = saved_errno;
return (ret);
}
/*
* Parse edit command. Returns 0 on success, -1 on error.
*/
int
eparse(const char *cmd, const char *left, const char *right)
{
FILE *file;
size_t nread;
int fd;
char *filename;
char buf[BUFSIZ], *text;
/* Skip whitespace. */
while (isspace(*cmd))
++cmd;
text = NULL;
switch (*cmd) {
case '\0':
/* Edit empty file. */
break;
case 'b':
/* Both strings. */
if (left == NULL)
goto RIGHT;
if (right == NULL)
goto LEFT;
/* Neither column is blank, so print both. */
if (asprintf(&text, "%s\n%s\n", left, right) == -1)
err(2, "could not allocate memory");
break;
case 'l':
LEFT:
/* Skip if there is no left column. */
if (left == NULL)
break;
if (asprintf(&text, "%s\n", left) == -1)
err(2, "could not allocate memory");
break;
case 'r':
RIGHT:
/* Skip if there is no right column. */
if (right == NULL)
break;
if (asprintf(&text, "%s\n", right) == -1)
err(2, "could not allocate memory");
break;
default:
return (-1);
}
/* Create temp file. */
if (asprintf(&filename, "%s/sdiff.XXXXXXXXXX", tmpdir) == -1)
err(2, "asprintf");
if ((fd = mkstemp(filename)) == -1)
err(2, "mkstemp");
if (text != NULL) {
size_t len;
ssize_t nwritten;
len = strlen(text);
if ((nwritten = write(fd, text, len)) == -1 ||
(size_t)nwritten != len) {
warn("error writing to temp file");
cleanup(filename);
}
}
close(fd);
/* text is no longer used. */
free(text);
/* Edit temp file. */
if (editit(filename) == -1) {
warn("error editing %s", filename);
cleanup(filename);
}
/* Open temporary file. */
if (!(file = fopen(filename, "r"))) {
warn("could not open edited file: %s", filename);
cleanup(filename);
}
/* Copy temporary file contents to output file. */
for (nread = sizeof(buf); nread == sizeof(buf);) {
size_t nwritten;
nread = fread(buf, sizeof(*buf), sizeof(buf), file);
/* Test for error or end of file. */
if (nread != sizeof(buf) &&
(ferror(file) || !feof(file))) {
warnx("error reading edited file: %s", filename);
cleanup(filename);
}
/*
* If we have nothing to read, break out of loop
* instead of writing nothing.
*/
if (!nread)
break;
/* Write data we just read. */
nwritten = fwrite(buf, sizeof(*buf), nread, outfp);
if (nwritten != nread) {
warnx("error writing to output file");
cleanup(filename);
}
}
/* We've reached the end of the temporary file, so remove it. */
if (unlink(filename))
warn("could not delete: %s", filename);
fclose(file);
free(filename);
return (0);
}