1255736Sdavidch/* $OpenBSD: edit.c,v 1.19 2009/06/07 13:29:50 ray Exp $ */ 2265411Sdavidcs 3255736Sdavidch/* 4255736Sdavidch * Written by Raymond Lai <ray@cyth.net>. 5255736Sdavidch * Public domain. 6255736Sdavidch */ 7255736Sdavidch 8255736Sdavidch#include <sys/cdefs.h> 9255736Sdavidch__FBSDID("$FreeBSD: stable/11/usr.bin/sdiff/edit.c 307772 2016-10-22 13:15:19Z bapt $"); 10255736Sdavidch 11255736Sdavidch#include <sys/types.h> 12255736Sdavidch#include <sys/wait.h> 13255736Sdavidch 14255736Sdavidch#include <ctype.h> 15255736Sdavidch#include <err.h> 16255736Sdavidch#include <errno.h> 17255736Sdavidch#include <paths.h> 18255736Sdavidch#include <signal.h> 19255736Sdavidch#include <stdio.h> 20255736Sdavidch#include <stdlib.h> 21255736Sdavidch#include <string.h> 22255736Sdavidch#include <unistd.h> 23255736Sdavidch 24255736Sdavidch#include "extern.h" 25255736Sdavidch 26255736Sdavidchstatic void 27255736Sdavidchcleanup(const char *filename) 28255736Sdavidch{ 29255736Sdavidch 30255736Sdavidch if (unlink(filename)) 31255736Sdavidch err(2, "could not delete: %s", filename); 32255736Sdavidch exit(2); 33255736Sdavidch} 34255736Sdavidch 35255736Sdavidch/* 36255736Sdavidch * Execute an editor on the specified pathname, which is interpreted 37255736Sdavidch * from the shell. This means flags may be included. 38255736Sdavidch * 39255736Sdavidch * Returns -1 on error, or the exit value on success. 40255736Sdavidch */ 41255736Sdavidchstatic int 42255736Sdavidcheditit(const char *pathname) 43255736Sdavidch{ 44255736Sdavidch sig_t sighup, sigint, sigquit, sigchld; 45255736Sdavidch pid_t pid; 46255736Sdavidch int saved_errno, st, ret = -1; 47255736Sdavidch const char *ed; 48255736Sdavidch 49255736Sdavidch ed = getenv("VISUAL"); 50255736Sdavidch if (ed == NULL) 51255736Sdavidch ed = getenv("EDITOR"); 52255736Sdavidch if (ed == NULL) 53255736Sdavidch ed = _PATH_VI; 54281855Srodrigc 55255736Sdavidch sighup = signal(SIGHUP, SIG_IGN); 56255736Sdavidch sigint = signal(SIGINT, SIG_IGN); 57255736Sdavidch sigquit = signal(SIGQUIT, SIG_IGN); 58255736Sdavidch sigchld = signal(SIGCHLD, SIG_DFL); 59255736Sdavidch if ((pid = fork()) == -1) 60255736Sdavidch goto fail; 61266979Smarcel if (pid == 0) { 62255736Sdavidch execlp(ed, ed, pathname, (char *)NULL); 63255736Sdavidch _exit(127); 64255736Sdavidch } 65255736Sdavidch while (waitpid(pid, &st, 0) == -1) 66255736Sdavidch if (errno != EINTR) 67255736Sdavidch goto fail; 68255736Sdavidch if (!WIFEXITED(st)) 69255736Sdavidch errno = EINTR; 70255736Sdavidch else 71255736Sdavidch ret = WEXITSTATUS(st); 72255736Sdavidch 73255736Sdavidch fail: 74255736Sdavidch saved_errno = errno; 75255736Sdavidch (void)signal(SIGHUP, sighup); 76255736Sdavidch (void)signal(SIGINT, sigint); 77255736Sdavidch (void)signal(SIGQUIT, sigquit); 78255736Sdavidch (void)signal(SIGCHLD, sigchld); 79255736Sdavidch errno = saved_errno; 80255736Sdavidch return (ret); 81255736Sdavidch} 82255736Sdavidch 83255736Sdavidch/* 84255736Sdavidch * Parse edit command. Returns 0 on success, -1 on error. 85255736Sdavidch */ 86255736Sdavidchint 87255736Sdavidcheparse(const char *cmd, const char *left, const char *right) 88255736Sdavidch{ 89255736Sdavidch FILE *file; 90255736Sdavidch size_t nread; 91255736Sdavidch int fd; 92255736Sdavidch char *filename; 93255736Sdavidch char buf[BUFSIZ], *text; 94255736Sdavidch 95255736Sdavidch /* Skip whitespace. */ 96255736Sdavidch while (isspace(*cmd)) 97255736Sdavidch ++cmd; 98255736Sdavidch 99255736Sdavidch text = NULL; 100255736Sdavidch switch (*cmd) { 101255736Sdavidch case '\0': 102255736Sdavidch /* Edit empty file. */ 103255736Sdavidch break; 104255736Sdavidch 105255736Sdavidch case 'b': 106255736Sdavidch /* Both strings. */ 107255736Sdavidch if (left == NULL) 108255736Sdavidch goto RIGHT; 109255736Sdavidch if (right == NULL) 110255736Sdavidch goto LEFT; 111255736Sdavidch 112255736Sdavidch /* Neither column is blank, so print both. */ 113255736Sdavidch if (asprintf(&text, "%s\n%s\n", left, right) == -1) 114296071Sdavidcs err(2, "could not allocate memory"); 115296071Sdavidcs break; 116296071Sdavidcs 117255736Sdavidch case 'l': 118255736SdavidchLEFT: 119255736Sdavidch /* Skip if there is no left column. */ 120255736Sdavidch if (left == NULL) 121255736Sdavidch break; 122255736Sdavidch 123255736Sdavidch if (asprintf(&text, "%s\n", left) == -1) 124255736Sdavidch err(2, "could not allocate memory"); 125255736Sdavidch 126255736Sdavidch break; 127255736Sdavidch 128255736Sdavidch case 'r': 129255736SdavidchRIGHT: 130255736Sdavidch /* Skip if there is no right column. */ 131255736Sdavidch if (right == NULL) 132255736Sdavidch break; 133255736Sdavidch 134255736Sdavidch if (asprintf(&text, "%s\n", right) == -1) 135255736Sdavidch err(2, "could not allocate memory"); 136255736Sdavidch 137255736Sdavidch break; 138255736Sdavidch 139255736Sdavidch default: 140255736Sdavidch return (-1); 141255736Sdavidch } 142255736Sdavidch 143255736Sdavidch /* Create temp file. */ 144255736Sdavidch if (asprintf(&filename, "%s/sdiff.XXXXXXXXXX", tmpdir) == -1) 145255736Sdavidch err(2, "asprintf"); 146255736Sdavidch if ((fd = mkstemp(filename)) == -1) 147255736Sdavidch err(2, "mkstemp"); 148255736Sdavidch if (text != NULL) { 149255736Sdavidch size_t len; 150255736Sdavidch ssize_t nwritten; 151255736Sdavidch 152255736Sdavidch len = strlen(text); 153255736Sdavidch if ((nwritten = write(fd, text, len)) == -1 || 154255736Sdavidch (size_t)nwritten != len) { 155255736Sdavidch warn("error writing to temp file"); 156255736Sdavidch cleanup(filename); 157255736Sdavidch } 158255736Sdavidch } 159255736Sdavidch close(fd); 160255736Sdavidch 161255736Sdavidch /* text is no longer used. */ 162255736Sdavidch free(text); 163255736Sdavidch 164255736Sdavidch /* Edit temp file. */ 165255736Sdavidch if (editit(filename) == -1) { 166255736Sdavidch warn("error editing %s", filename); 167255736Sdavidch cleanup(filename); 168255736Sdavidch } 169255736Sdavidch 170255736Sdavidch /* Open temporary file. */ 171321517Sae if (!(file = fopen(filename, "r"))) { 172255736Sdavidch warn("could not open edited file: %s", filename); 173255736Sdavidch cleanup(filename); 174255736Sdavidch } 175255736Sdavidch 176255736Sdavidch /* Copy temporary file contents to output file. */ 177255736Sdavidch for (nread = sizeof(buf); nread == sizeof(buf);) { 178255736Sdavidch size_t nwritten; 179255736Sdavidch 180255736Sdavidch nread = fread(buf, sizeof(*buf), sizeof(buf), file); 181255736Sdavidch /* Test for error or end of file. */ 182255736Sdavidch if (nread != sizeof(buf) && 183255736Sdavidch (ferror(file) || !feof(file))) { 184255736Sdavidch warnx("error reading edited file: %s", filename); 185255736Sdavidch cleanup(filename); 186255736Sdavidch } 187255736Sdavidch 188255736Sdavidch /* 189255736Sdavidch * If we have nothing to read, break out of loop 190255736Sdavidch * instead of writing nothing. 191255736Sdavidch */ 192255736Sdavidch if (!nread) 193255736Sdavidch break; 194255736Sdavidch 195255736Sdavidch /* Write data we just read. */ 196255736Sdavidch nwritten = fwrite(buf, sizeof(*buf), nread, outfp); 197255736Sdavidch if (nwritten != nread) { 198255736Sdavidch warnx("error writing to output file"); 199255736Sdavidch cleanup(filename); 200255736Sdavidch } 201255736Sdavidch } 202255736Sdavidch 203255736Sdavidch /* We've reached the end of the temporary file, so remove it. */ 204255736Sdavidch if (unlink(filename)) 205255736Sdavidch warn("could not delete: %s", filename); 206255736Sdavidch fclose(file); 207255736Sdavidch 208255736Sdavidch free(filename); 209255736Sdavidch 210255736Sdavidch return (0); 211255736Sdavidch} 212255736Sdavidch