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