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