113b5b548SBaptiste Daroussin /* $OpenBSD: edit.c,v 1.19 2009/06/07 13:29:50 ray Exp $ */
213b5b548SBaptiste Daroussin
313b5b548SBaptiste Daroussin /*
413b5b548SBaptiste Daroussin * Written by Raymond Lai <ray@cyth.net>.
513b5b548SBaptiste Daroussin * Public domain.
613b5b548SBaptiste Daroussin */
713b5b548SBaptiste Daroussin
813b5b548SBaptiste Daroussin #include <sys/types.h>
913b5b548SBaptiste Daroussin #include <sys/wait.h>
1013b5b548SBaptiste Daroussin
1113b5b548SBaptiste Daroussin #include <ctype.h>
1213b5b548SBaptiste Daroussin #include <err.h>
1313b5b548SBaptiste Daroussin #include <errno.h>
1413b5b548SBaptiste Daroussin #include <paths.h>
1513b5b548SBaptiste Daroussin #include <signal.h>
1613b5b548SBaptiste Daroussin #include <stdio.h>
1713b5b548SBaptiste Daroussin #include <stdlib.h>
1813b5b548SBaptiste Daroussin #include <string.h>
1913b5b548SBaptiste Daroussin #include <unistd.h>
2013b5b548SBaptiste Daroussin
2113b5b548SBaptiste Daroussin #include "extern.h"
2213b5b548SBaptiste Daroussin
23*db1fcc80SBaptiste Daroussin static void
cleanup(const char * filename)24*db1fcc80SBaptiste Daroussin cleanup(const char *filename)
25*db1fcc80SBaptiste Daroussin {
26*db1fcc80SBaptiste Daroussin
27*db1fcc80SBaptiste Daroussin if (unlink(filename))
28*db1fcc80SBaptiste Daroussin err(2, "could not delete: %s", filename);
29*db1fcc80SBaptiste Daroussin exit(2);
30*db1fcc80SBaptiste Daroussin }
31*db1fcc80SBaptiste Daroussin
3213b5b548SBaptiste Daroussin /*
3313b5b548SBaptiste Daroussin * Execute an editor on the specified pathname, which is interpreted
3413b5b548SBaptiste Daroussin * from the shell. This means flags may be included.
3513b5b548SBaptiste Daroussin *
3613b5b548SBaptiste Daroussin * Returns -1 on error, or the exit value on success.
3713b5b548SBaptiste Daroussin */
38e8ad1a6fSBaptiste Daroussin static int
editit(const char * pathname)3913b5b548SBaptiste Daroussin editit(const char *pathname)
4013b5b548SBaptiste Daroussin {
4113b5b548SBaptiste Daroussin sig_t sighup, sigint, sigquit, sigchld;
4213b5b548SBaptiste Daroussin pid_t pid;
4313b5b548SBaptiste Daroussin int saved_errno, st, ret = -1;
4415eaa1aeSBaptiste Daroussin const char *ed;
4513b5b548SBaptiste Daroussin
461ddda798SBaptiste Daroussin ed = getenv("VISUAL");
471ddda798SBaptiste Daroussin if (ed == NULL)
481ddda798SBaptiste Daroussin ed = getenv("EDITOR");
491ddda798SBaptiste Daroussin if (ed == NULL)
5013b5b548SBaptiste Daroussin ed = _PATH_VI;
5113b5b548SBaptiste Daroussin
5213b5b548SBaptiste Daroussin sighup = signal(SIGHUP, SIG_IGN);
5313b5b548SBaptiste Daroussin sigint = signal(SIGINT, SIG_IGN);
5413b5b548SBaptiste Daroussin sigquit = signal(SIGQUIT, SIG_IGN);
5513b5b548SBaptiste Daroussin sigchld = signal(SIGCHLD, SIG_DFL);
5613b5b548SBaptiste Daroussin if ((pid = fork()) == -1)
5713b5b548SBaptiste Daroussin goto fail;
5813b5b548SBaptiste Daroussin if (pid == 0) {
5915eaa1aeSBaptiste Daroussin execlp(ed, ed, pathname, (char *)NULL);
6013b5b548SBaptiste Daroussin _exit(127);
6113b5b548SBaptiste Daroussin }
6213b5b548SBaptiste Daroussin while (waitpid(pid, &st, 0) == -1)
6313b5b548SBaptiste Daroussin if (errno != EINTR)
6413b5b548SBaptiste Daroussin goto fail;
6513b5b548SBaptiste Daroussin if (!WIFEXITED(st))
6613b5b548SBaptiste Daroussin errno = EINTR;
6713b5b548SBaptiste Daroussin else
6813b5b548SBaptiste Daroussin ret = WEXITSTATUS(st);
6913b5b548SBaptiste Daroussin
7013b5b548SBaptiste Daroussin fail:
7113b5b548SBaptiste Daroussin saved_errno = errno;
7213b5b548SBaptiste Daroussin (void)signal(SIGHUP, sighup);
7313b5b548SBaptiste Daroussin (void)signal(SIGINT, sigint);
7413b5b548SBaptiste Daroussin (void)signal(SIGQUIT, sigquit);
7513b5b548SBaptiste Daroussin (void)signal(SIGCHLD, sigchld);
7613b5b548SBaptiste Daroussin errno = saved_errno;
7713b5b548SBaptiste Daroussin return (ret);
7813b5b548SBaptiste Daroussin }
7913b5b548SBaptiste Daroussin
8013b5b548SBaptiste Daroussin /*
8113b5b548SBaptiste Daroussin * Parse edit command. Returns 0 on success, -1 on error.
8213b5b548SBaptiste Daroussin */
8313b5b548SBaptiste Daroussin int
eparse(const char * cmd,const char * left,const char * right)8413b5b548SBaptiste Daroussin eparse(const char *cmd, const char *left, const char *right)
8513b5b548SBaptiste Daroussin {
8613b5b548SBaptiste Daroussin FILE *file;
8713b5b548SBaptiste Daroussin size_t nread;
8813b5b548SBaptiste Daroussin int fd;
8913b5b548SBaptiste Daroussin char *filename;
9013b5b548SBaptiste Daroussin char buf[BUFSIZ], *text;
9113b5b548SBaptiste Daroussin
9213b5b548SBaptiste Daroussin /* Skip whitespace. */
9313b5b548SBaptiste Daroussin while (isspace(*cmd))
9413b5b548SBaptiste Daroussin ++cmd;
9513b5b548SBaptiste Daroussin
9613b5b548SBaptiste Daroussin text = NULL;
9713b5b548SBaptiste Daroussin switch (*cmd) {
9813b5b548SBaptiste Daroussin case '\0':
9913b5b548SBaptiste Daroussin /* Edit empty file. */
10013b5b548SBaptiste Daroussin break;
10113b5b548SBaptiste Daroussin
10213b5b548SBaptiste Daroussin case 'b':
10313b5b548SBaptiste Daroussin /* Both strings. */
10413b5b548SBaptiste Daroussin if (left == NULL)
10513b5b548SBaptiste Daroussin goto RIGHT;
10613b5b548SBaptiste Daroussin if (right == NULL)
10713b5b548SBaptiste Daroussin goto LEFT;
10813b5b548SBaptiste Daroussin
10913b5b548SBaptiste Daroussin /* Neither column is blank, so print both. */
11013b5b548SBaptiste Daroussin if (asprintf(&text, "%s\n%s\n", left, right) == -1)
11113b5b548SBaptiste Daroussin err(2, "could not allocate memory");
11213b5b548SBaptiste Daroussin break;
11313b5b548SBaptiste Daroussin
11413b5b548SBaptiste Daroussin case 'l':
11513b5b548SBaptiste Daroussin LEFT:
11613b5b548SBaptiste Daroussin /* Skip if there is no left column. */
11713b5b548SBaptiste Daroussin if (left == NULL)
11813b5b548SBaptiste Daroussin break;
11913b5b548SBaptiste Daroussin
12013b5b548SBaptiste Daroussin if (asprintf(&text, "%s\n", left) == -1)
12113b5b548SBaptiste Daroussin err(2, "could not allocate memory");
12213b5b548SBaptiste Daroussin
12313b5b548SBaptiste Daroussin break;
12413b5b548SBaptiste Daroussin
12513b5b548SBaptiste Daroussin case 'r':
12613b5b548SBaptiste Daroussin RIGHT:
12713b5b548SBaptiste Daroussin /* Skip if there is no right column. */
12813b5b548SBaptiste Daroussin if (right == NULL)
12913b5b548SBaptiste Daroussin break;
13013b5b548SBaptiste Daroussin
13113b5b548SBaptiste Daroussin if (asprintf(&text, "%s\n", right) == -1)
13213b5b548SBaptiste Daroussin err(2, "could not allocate memory");
13313b5b548SBaptiste Daroussin
13413b5b548SBaptiste Daroussin break;
13513b5b548SBaptiste Daroussin
13613b5b548SBaptiste Daroussin default:
13713b5b548SBaptiste Daroussin return (-1);
13813b5b548SBaptiste Daroussin }
13913b5b548SBaptiste Daroussin
14013b5b548SBaptiste Daroussin /* Create temp file. */
14113b5b548SBaptiste Daroussin if (asprintf(&filename, "%s/sdiff.XXXXXXXXXX", tmpdir) == -1)
14213b5b548SBaptiste Daroussin err(2, "asprintf");
14313b5b548SBaptiste Daroussin if ((fd = mkstemp(filename)) == -1)
14413b5b548SBaptiste Daroussin err(2, "mkstemp");
14513b5b548SBaptiste Daroussin if (text != NULL) {
14613b5b548SBaptiste Daroussin size_t len;
14713b5b548SBaptiste Daroussin ssize_t nwritten;
14813b5b548SBaptiste Daroussin
14913b5b548SBaptiste Daroussin len = strlen(text);
15013b5b548SBaptiste Daroussin if ((nwritten = write(fd, text, len)) == -1 ||
15113b5b548SBaptiste Daroussin (size_t)nwritten != len) {
15213b5b548SBaptiste Daroussin warn("error writing to temp file");
15313b5b548SBaptiste Daroussin cleanup(filename);
15413b5b548SBaptiste Daroussin }
15513b5b548SBaptiste Daroussin }
15613b5b548SBaptiste Daroussin close(fd);
15713b5b548SBaptiste Daroussin
15813b5b548SBaptiste Daroussin /* text is no longer used. */
15913b5b548SBaptiste Daroussin free(text);
16013b5b548SBaptiste Daroussin
16113b5b548SBaptiste Daroussin /* Edit temp file. */
16213b5b548SBaptiste Daroussin if (editit(filename) == -1) {
16313b5b548SBaptiste Daroussin warn("error editing %s", filename);
16413b5b548SBaptiste Daroussin cleanup(filename);
16513b5b548SBaptiste Daroussin }
16613b5b548SBaptiste Daroussin
16713b5b548SBaptiste Daroussin /* Open temporary file. */
16813b5b548SBaptiste Daroussin if (!(file = fopen(filename, "r"))) {
16913b5b548SBaptiste Daroussin warn("could not open edited file: %s", filename);
17013b5b548SBaptiste Daroussin cleanup(filename);
17113b5b548SBaptiste Daroussin }
17213b5b548SBaptiste Daroussin
17313b5b548SBaptiste Daroussin /* Copy temporary file contents to output file. */
17413b5b548SBaptiste Daroussin for (nread = sizeof(buf); nread == sizeof(buf);) {
17513b5b548SBaptiste Daroussin size_t nwritten;
17613b5b548SBaptiste Daroussin
17713b5b548SBaptiste Daroussin nread = fread(buf, sizeof(*buf), sizeof(buf), file);
17813b5b548SBaptiste Daroussin /* Test for error or end of file. */
17913b5b548SBaptiste Daroussin if (nread != sizeof(buf) &&
18013b5b548SBaptiste Daroussin (ferror(file) || !feof(file))) {
18113b5b548SBaptiste Daroussin warnx("error reading edited file: %s", filename);
18213b5b548SBaptiste Daroussin cleanup(filename);
18313b5b548SBaptiste Daroussin }
18413b5b548SBaptiste Daroussin
18513b5b548SBaptiste Daroussin /*
18613b5b548SBaptiste Daroussin * If we have nothing to read, break out of loop
18713b5b548SBaptiste Daroussin * instead of writing nothing.
18813b5b548SBaptiste Daroussin */
18913b5b548SBaptiste Daroussin if (!nread)
19013b5b548SBaptiste Daroussin break;
19113b5b548SBaptiste Daroussin
19213b5b548SBaptiste Daroussin /* Write data we just read. */
19313b5b548SBaptiste Daroussin nwritten = fwrite(buf, sizeof(*buf), nread, outfp);
19413b5b548SBaptiste Daroussin if (nwritten != nread) {
19513b5b548SBaptiste Daroussin warnx("error writing to output file");
19613b5b548SBaptiste Daroussin cleanup(filename);
19713b5b548SBaptiste Daroussin }
19813b5b548SBaptiste Daroussin }
19913b5b548SBaptiste Daroussin
20013b5b548SBaptiste Daroussin /* We've reached the end of the temporary file, so remove it. */
20113b5b548SBaptiste Daroussin if (unlink(filename))
20213b5b548SBaptiste Daroussin warn("could not delete: %s", filename);
20313b5b548SBaptiste Daroussin fclose(file);
20413b5b548SBaptiste Daroussin
20513b5b548SBaptiste Daroussin free(filename);
20613b5b548SBaptiste Daroussin
20713b5b548SBaptiste Daroussin return (0);
20813b5b548SBaptiste Daroussin }
209