1/* $NetBSD: edit.c,v 1.5 2021/08/27 17:36:37 rillig Exp $ */ 2/* $OpenBSD: edit.c,v 1.14 2006/05/25 03:20:32 ray Exp $ */ 3 4/* 5 * Written by Raymond Lai <ray@cyth.net>. 6 * Public domain. 7 */ 8 9#include <sys/types.h> 10#include <sys/wait.h> 11 12#include <ctype.h> 13#include <err.h> 14#include <stdio.h> 15#include <stdlib.h> 16#include <string.h> 17#include <unistd.h> 18 19#include "common.h" 20#include "extern.h" 21 22static void edit(const char *); 23 24/* 25 * Takes the name of a file and opens it with an editor. 26 */ 27static void 28edit(const char *filename) 29{ 30 int status; 31 pid_t pid; 32 const char *editor; 33 34 editor = getenv("VISUAL"); 35 if (editor == NULL) 36 editor = getenv("EDITOR"); 37 if (editor == NULL) 38 editor = "vi"; 39 40 /* Start editor on temporary file. */ 41 switch (pid = fork()) { 42 case 0: 43 /* child */ 44 execlp(editor, editor, filename, (void *)NULL); 45 warn("could not execute editor: %s", editor); 46 cleanup(filename); 47 case -1: 48 warn("could not fork"); 49 cleanup(filename); 50 } 51 52 /* parent */ 53 /* Wait for editor to exit. */ 54 if (waitpid(pid, &status, 0) == -1) { 55 warn("waitpid"); 56 cleanup(filename); 57 } 58 59 /* Check that editor terminated normally. */ 60 if (!WIFEXITED(status)) { 61 warn("%s terminated abnormally", editor); 62 cleanup(filename); 63 } 64} 65 66/* 67 * Parse edit command. Returns 0 on success, -1 on error. 68 */ 69int 70eparse(const char *cmd, const char *left, const char *right) 71{ 72 FILE *file; 73 size_t nread, nwritten; 74 int fd; 75 char *filename; 76 char buf[BUFSIZ], *text; 77 78 /* Skip whitespace. */ 79 while (isspace((unsigned char)(*cmd))) 80 ++cmd; 81 82 text = NULL; 83 switch (*cmd) { 84 case '\0': 85 /* Edit empty file. */ 86 break; 87 88 case 'b': 89 /* Both strings. */ 90 if (left == NULL) 91 goto RIGHT; 92 if (right == NULL) 93 goto LEFT; 94 95 /* Neither column is blank, so print both. */ 96 if (asprintf(&text, "%s\n%s\n", left, right) == -1) 97 err(2, "could not allocate memory"); 98 break; 99 100 case 'l': 101LEFT: 102 /* Skip if there is no left column. */ 103 if (left == NULL) 104 break; 105 106 if (asprintf(&text, "%s\n", left) == -1) 107 err(2, "could not allocate memory"); 108 109 break; 110 111 case 'r': 112RIGHT: 113 /* Skip if there is no right column. */ 114 if (right == NULL) 115 break; 116 117 if (asprintf(&text, "%s\n", right) == -1) 118 err(2, "could not allocate memory"); 119 120 break; 121 122 default: 123 return (-1); 124 } 125 126 /* Create temp file. */ 127 if (asprintf(&filename, "%s/sdiff.XXXXXXXXXX", tmpdir) == -1) 128 err(2, "asprintf"); 129 if ((fd = mkstemp(filename)) == -1) 130 err(2, "mkstemp"); 131 if (text != NULL) { 132 size_t len; 133 134 len = strlen(text); 135 if ((size_t)write(fd, text, len) != len) { 136 warn("error writing to temp file"); 137 cleanup(filename); 138 } 139 } 140 close(fd); 141 142 /* text is no longer used. */ 143 free(text); 144 145 /* Edit temp file. */ 146 edit(filename); 147 148 /* Open temporary file. */ 149 if (!(file = fopen(filename, "r"))) { 150 warn("could not open edited file: %s", filename); 151 cleanup(filename); 152 } 153 154 /* Copy temporary file contents to output file. */ 155 for (nread = sizeof(buf); nread == sizeof(buf);) { 156 nread = fread(buf, sizeof(*buf), sizeof(buf), file); 157 /* Test for error or end of file. */ 158 if (nread != sizeof(buf) && 159 (ferror(file) || !feof(file))) { 160 warnx("error reading edited file: %s", filename); 161 cleanup(filename); 162 } 163 164 /* 165 * If we have nothing to read, break out of loop 166 * instead of writing nothing. 167 */ 168 if (!nread) 169 break; 170 171 /* Write data we just read. */ 172 nwritten = fwrite(buf, sizeof(*buf), nread, outfile); 173 if (nwritten != nread) { 174 warnx("error writing to output file"); 175 cleanup(filename); 176 } 177 } 178 179 /* We've reached the end of the temporary file, so remove it. */ 180 if (unlink(filename)) 181 warn("could not delete: %s", filename); 182 fclose(file); 183 184 free(filename); 185 186 return (0); 187} 188