Annotation of src/usr.bin/sdiff/edit.c, Revision 1.1
1.1 ! tedu 1: /* $Id: edit.c,v 1.4 2005/12/15 16:19:27 ray Exp $ */
! 2:
! 3: /*
! 4: * Written by Raymond Lai <ray@cyth.net>.
! 5: * Public domain.
! 6: */
! 7:
! 8: #include <sys/types.h>
! 9: #include <sys/wait.h>
! 10:
! 11: #include <assert.h>
! 12: #include <ctype.h>
! 13: #include <err.h>
! 14: #include <stdio.h>
! 15: #include <stdlib.h>
! 16: #include <unistd.h>
! 17:
! 18: #include "extern.h"
! 19:
! 20: __dead static void cleanup(const char *);
! 21: static void edit(const char *);
! 22: static char *xmktemp(const char *);
! 23:
! 24: static void
! 25: cleanup(const char *filename)
! 26: {
! 27: if (unlink(filename))
! 28: err(2, "could not delete: %s", filename);
! 29: exit(2);
! 30: }
! 31:
! 32: /*
! 33: * Takes the name of a file and opens it with an editor.
! 34: */
! 35: static void
! 36: edit(const char *filename)
! 37: {
! 38: int status;
! 39: pid_t pid;
! 40: const char *editor;
! 41:
! 42: editor = getenv("VISUAL");
! 43: if (editor == NULL)
! 44: editor = getenv("EDITOR");
! 45: if (editor == NULL)
! 46: editor = "vi";
! 47:
! 48: /* Start editor on temporary file. */
! 49: switch (pid = fork()) {
! 50: case 0:
! 51: /* child */
! 52: execlp(editor, editor, filename, (void *)NULL);
! 53: warn("could not execute editor: %s", editor);
! 54: cleanup(filename);
! 55: /* NOTREACHED */
! 56: case -1:
! 57: warn("could not fork");
! 58: cleanup(filename);
! 59: /* NOTREACHED */
! 60: }
! 61:
! 62: /* parent */
! 63: /* Wait for editor to exit. */
! 64: if (waitpid(pid, &status, 0) == -1) {
! 65: warn("waitpid");
! 66: cleanup(filename);
! 67: /* NOTREACHED */
! 68: }
! 69:
! 70: /* Check that editor terminated normally. */
! 71: if (!WIFEXITED(status)) {
! 72: warn("%s terminated abnormally", editor);
! 73: cleanup(filename);
! 74: /* NOTREACHED */
! 75: }
! 76: }
! 77:
! 78: /*
! 79: * Creates and returns the name of a temporary file. Takes a string
! 80: * (or NULL) is written to the temporary file. The returned string
! 81: * needs to be freed.
! 82: */
! 83: static char *
! 84: xmktemp(const char *s)
! 85: {
! 86: FILE *file;
! 87: int fd;
! 88: const char *tmpdir;
! 89: char *filename;
! 90:
! 91: /* If TMPDIR is set, use it; otherwise use /tmp. */
! 92: if (!(tmpdir = getenv("TMPDIR")))
! 93: tmpdir = "/tmp";
! 94: if (asprintf(&filename, "%s/sdiff.XXXXXXXXXX", tmpdir) == -1)
! 95: err(2, "could not allocate memory");
! 96:
! 97: /* Create temp file. */
! 98: if ((fd = mkstemp(filename)) == -1)
! 99: err(2, "could not create temporary file");
! 100:
! 101: /* If we don't write anything to the file, just close. */
! 102: if (s == NULL) {
! 103: if (close(fd)) {
! 104: warn("could not close %s", filename);
! 105: cleanup(filename);
! 106: /* NOTREACHED */
! 107: }
! 108:
! 109: return (filename);
! 110: }
! 111:
! 112: /* Open temp file for writing. */
! 113: if ((file = fdopen(fd, "w")) == NULL) {
! 114: warn("could not open %s", filename);
! 115: cleanup(filename);
! 116: /* NOTREACHED */
! 117: }
! 118:
! 119: /* Write to file. */
! 120: if (fputs(s, file)) {
! 121: warn("could not write to %s", filename);
! 122: cleanup(filename);
! 123: /* NOTREACHED */
! 124: }
! 125:
! 126: /* Close temp file. */
! 127: if (fclose(file)) {
! 128: warn("could not close %s", filename);
! 129: cleanup(filename);
! 130: /* NOTREACHED */
! 131: }
! 132:
! 133: return (filename);
! 134: }
! 135:
! 136: /*
! 137: * Parse edit command. Returns 0 on success, -1 on error.
! 138: */
! 139: int
! 140: eparse(const char *cmd, const char *left, const char *right)
! 141: {
! 142: FILE *file;
! 143: size_t nread, nwritten;
! 144: const char *filename;
! 145: char buf[BUFSIZ], *text;
! 146:
! 147: assert(cmd);
! 148:
! 149: /* Skip whitespace. */
! 150: while (isspace(*cmd))
! 151: ++cmd;
! 152:
! 153: text = NULL;
! 154: switch (*cmd) {
! 155: case '\0':
! 156: /* Edit empty file. */
! 157: break;
! 158:
! 159: case 'b':
! 160: /* Both strings. */
! 161: if (left == NULL)
! 162: goto RIGHT;
! 163: if (right == NULL)
! 164: goto LEFT;
! 165:
! 166: /* Neither column is blank, so print both. */
! 167: if (asprintf(&text, "%s%s\n", left, right) == -1)
! 168: err(2, "could not allocate memory");
! 169: break;
! 170:
! 171: LEFT:
! 172: case 'l':
! 173: /* Skip if there is no left column. */
! 174: if (left == NULL)
! 175: break;
! 176:
! 177: if (asprintf(&text, "%s\n", left) == -1)
! 178: err(2, "could not allocate memory");
! 179:
! 180: break;
! 181:
! 182: RIGHT:
! 183: case 'r':
! 184: /* Skip if there is no right column. */
! 185: if (right == NULL)
! 186: break;
! 187:
! 188: if (asprintf(&text, "%s\n", right) == -1)
! 189: err(2, "could not allocate memory");
! 190:
! 191: break;
! 192:
! 193: default:
! 194: return (-1);
! 195: }
! 196:
! 197: /* Create temp file. */
! 198: filename = xmktemp(text);
! 199:
! 200: /* text is no longer used. */
! 201: free(text);
! 202:
! 203: /* Edit temp file. */
! 204: edit(filename);
! 205:
! 206: /* Open temporary file. */
! 207: if (!(file = fopen(filename, "r"))) {
! 208: warn("could not open edited file: %s", filename);
! 209: cleanup(filename);
! 210: /* NOTREACHED */
! 211: }
! 212:
! 213: /* Copy temporary file contents to output file. */
! 214: for (nread = sizeof(buf); nread == sizeof(buf);) {
! 215: nread = fread(buf, sizeof(*buf), sizeof(buf), file);
! 216: /* Test for error or end of file. */
! 217: if (nread != sizeof(buf) &&
! 218: (ferror(file) || !feof(file))) {
! 219: warnx("error reading edited file: %s", filename);
! 220: cleanup(filename);
! 221: /* NOTREACHED */
! 222: }
! 223:
! 224: /*
! 225: * If we have nothing to read, break out of loop
! 226: * instead of writing nothing.
! 227: */
! 228: if (!nread)
! 229: break;
! 230:
! 231: /* Write data we just read. */
! 232: nwritten = fwrite(buf, sizeof(*buf), nread, outfile);
! 233: if (nwritten != nread) {
! 234: warnx("error writing to output file");
! 235: cleanup(filename);
! 236: /* NOTREACHED */
! 237: }
! 238: }
! 239:
! 240: /* We've reached the end of the temporary file, so remove it. */
! 241: if (unlink(filename))
! 242: warn("could not delete: %s", filename);
! 243: if (fclose(file))
! 244: warn("could not close: %s", filename);
! 245:
! 246: /* filename was malloc()ed in xmktemp(). */
! 247: free((char *)filename);
! 248:
! 249: return (0);
! 250: }