Annotation of src/usr.bin/cvs/logmsg.c, Revision 1.41
1.41 ! ray 1: /* $OpenBSD: logmsg.c,v 1.40 2007/05/11 02:37:31 ray Exp $ */
1.1 jfb 2: /*
1.30 joris 3: * Copyright (c) 2007 Joris Vink <joris@openbsd.org>
1.1 jfb 4: *
1.30 joris 5: * Permission to use, copy, modify, and distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
8: *
9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1.1 jfb 16: */
17:
1.38 otto 18: #include <sys/stat.h>
1.39 xsa 19: #include <sys/types.h>
20: #include <sys/wait.h>
1.38 otto 21:
22: #include <errno.h>
23: #include <fcntl.h>
1.39 xsa 24: #include <paths.h>
25: #include <signal.h>
1.38 otto 26: #include <string.h>
27: #include <unistd.h>
1.1 jfb 28:
29: #include "cvs.h"
30:
1.30 joris 31: #define CVS_LOGMSG_PREFIX "CVS:"
32: #define CVS_LOGMSG_LINE \
1.1 jfb 33: "----------------------------------------------------------------------"
34:
1.39 xsa 35: int cvs_logmsg_edit(const char *);
36:
1.17 xsa 37: char *
1.30 joris 38: cvs_logmsg_read(const char *path)
1.1 jfb 39: {
1.30 joris 40: int fd;
41: BUF *bp;
42: FILE *fp;
1.1 jfb 43: size_t len;
44: struct stat st;
1.30 joris 45: char *buf, *lbuf;
46:
47: if ((fd = open(path, O_RDONLY)) == -1)
48: fatal("cvs_logmsg_read: open %s", strerror(errno));
1.1 jfb 49:
1.30 joris 50: if (fstat(fd, &st) == -1)
51: fatal("cvs_logmsg_read: fstat %s", strerror(errno));
1.1 jfb 52:
1.24 xsa 53: if (!S_ISREG(st.st_mode))
1.30 joris 54: fatal("cvs_logmsg_read: file is not a regular file");
1.1 jfb 55:
1.30 joris 56: if ((fp = fdopen(fd, "r")) == NULL)
57: fatal("cvs_logmsg_read: fdopen %s", strerror(errno));
1.1 jfb 58:
1.30 joris 59: lbuf = NULL;
60: bp = cvs_buf_alloc(st.st_size, BUF_AUTOEXT);
61: while ((buf = fgetln(fp, &len))) {
62: if (buf[len - 1] == '\n') {
63: buf[len - 1] = '\0';
64: } else {
65: lbuf = xmalloc(len + 1);
1.36 otto 66: memcpy(lbuf, buf, len);
67: lbuf[len] = '\0';
1.30 joris 68: buf = lbuf;
69: }
1.1 jfb 70:
1.30 joris 71: len = strlen(buf);
1.1 jfb 72: if (len == 0)
73: continue;
1.30 joris 74:
75: if (!strncmp(buf, CVS_LOGMSG_PREFIX,
76: strlen(CVS_LOGMSG_PREFIX)))
1.1 jfb 77: continue;
78:
1.30 joris 79: cvs_buf_append(bp, buf, len);
80: cvs_buf_putc(bp, '\n');
81: }
82:
83: if (lbuf != NULL)
84: xfree(lbuf);
1.1 jfb 85:
1.11 jfb 86: (void)fclose(fp);
87:
1.23 xsa 88: cvs_buf_putc(bp, '\0');
1.30 joris 89: return (cvs_buf_release(bp));
1.1 jfb 90: }
91:
1.17 xsa 92: char *
1.30 joris 93: cvs_logmsg_create(struct cvs_flisthead *added, struct cvs_flisthead *removed,
94: struct cvs_flisthead *modified)
1.1 jfb 95: {
96: FILE *fp;
1.39 xsa 97: int c, fd, saved_errno;
1.30 joris 98: struct cvs_filelist *cf;
1.1 jfb 99: struct stat st1, st2;
1.39 xsa 100: char *fpath, *logmsg;
1.8 jfb 101:
1.35 xsa 102: (void)xasprintf(&fpath, "%s/cvsXXXXXXXXXX", cvs_tmpdir);
1.1 jfb 103:
1.30 joris 104: if ((fd = mkstemp(fpath)) == NULL)
105: fatal("cvs_logmsg_create: mkstemp %s", strerror(errno));
1.33 joris 106:
107: cvs_worklist_add(fpath, &temp_files);
1.1 jfb 108:
1.24 xsa 109: if ((fp = fdopen(fd, "w")) == NULL) {
1.34 xsa 110: saved_errno = errno;
1.30 joris 111: (void)unlink(fpath);
1.34 xsa 112: fatal("cvs_logmsg_create: fdopen %s", strerror(saved_errno));
1.8 jfb 113: }
1.1 jfb 114:
1.8 jfb 115: fprintf(fp, "\n%s %s\n%s Enter Log. Lines beginning with `%s' are "
116: "removed automatically\n%s\n", CVS_LOGMSG_PREFIX, CVS_LOGMSG_LINE,
117: CVS_LOGMSG_PREFIX, CVS_LOGMSG_PREFIX, CVS_LOGMSG_PREFIX);
118:
1.31 joris 119: if (added != NULL && !TAILQ_EMPTY(added)) {
1.30 joris 120: fprintf(fp, "%s Added Files:", CVS_LOGMSG_PREFIX);
121: TAILQ_FOREACH(cf, added, flist)
122: fprintf(fp, "\n%s\t%s",
123: CVS_LOGMSG_PREFIX, cf->file_path);
124: fputs("\n", fp);
125: }
126:
1.31 joris 127: if (removed != NULL && !TAILQ_EMPTY(removed)) {
1.30 joris 128: fprintf(fp, "%s Removed Files:", CVS_LOGMSG_PREFIX);
129: TAILQ_FOREACH(cf, removed, flist)
130: fprintf(fp, "\n%s\t%s",
131: CVS_LOGMSG_PREFIX, cf->file_path);
132: fputs("\n", fp);
133: }
134:
1.31 joris 135: if (modified != NULL && !TAILQ_EMPTY(modified)) {
1.30 joris 136: fprintf(fp, "%s Modified Files:", CVS_LOGMSG_PREFIX);
137: TAILQ_FOREACH(cf, modified, flist)
138: fprintf(fp, "\n%s\t%s",
139: CVS_LOGMSG_PREFIX, cf->file_path);
140: fputs("\n", fp);
141: }
1.1 jfb 142:
1.9 jfb 143: fprintf(fp, "%s %s\n", CVS_LOGMSG_PREFIX, CVS_LOGMSG_LINE);
1.1 jfb 144: (void)fflush(fp);
145:
146: if (fstat(fd, &st1) == -1) {
1.34 xsa 147: saved_errno = errno;
1.30 joris 148: (void)unlink(fpath);
1.34 xsa 149: fatal("cvs_logmsg_create: fstat %s", strerror(saved_errno));
1.1 jfb 150: }
151:
1.30 joris 152: logmsg = NULL;
153:
1.1 jfb 154: for (;;) {
1.41 ! ray 155: if (cvs_logmsg_edit(fpath) == -1)
1.1 jfb 156: break;
1.8 jfb 157:
1.1 jfb 158: if (fstat(fd, &st2) == -1) {
1.34 xsa 159: saved_errno = errno;
1.30 joris 160: (void)unlink(fpath);
1.34 xsa 161: fatal("cvs_logmsg_create: fstat %s",
162: strerror(saved_errno));
1.1 jfb 163: }
164:
1.30 joris 165: if (st1.st_mtime != st2.st_mtime) {
166: logmsg = cvs_logmsg_read(fpath);
1.1 jfb 167: break;
168: }
169:
1.30 joris 170: printf("\nLog message unchanged or not specified\n"
1.32 jasper 171: "a)bort, c)ontinue, e)dit\nAction: (continue) ");
1.30 joris 172: (void)fflush(stdout);
173:
174: c = getc(stdin);
175: if (c == 'a') {
176: fatal("Aborted by user");
177: } else if (c == '\n' || c == 'c') {
178: logmsg = xstrdup("");
1.1 jfb 179: break;
1.30 joris 180: } else if (c == 'e') {
1.1 jfb 181: continue;
1.30 joris 182: } else {
183: cvs_log(LP_ERR, "invalid input");
1.1 jfb 184: continue;
185: }
186: }
187:
188: (void)fclose(fp);
1.30 joris 189: (void)unlink(fpath);
190: xfree(fpath);
1.1 jfb 191:
1.30 joris 192: return (logmsg);
1.39 xsa 193: }
194:
1.41 ! ray 195: /*
! 196: * Execute an editor on the specified pathname, which is interpreted
! 197: * from the shell. This means flags may be included.
! 198: *
! 199: * Returns -1 on error, or the exit value on success.
! 200: */
1.39 xsa 201: int
202: cvs_logmsg_edit(const char *pathname)
203: {
204: char *argp[] = {"sh", "-c", NULL, NULL}, *p;
205: sig_t sighup, sigint, sigquit;
206: pid_t pid;
1.40 ray 207: int saved_errno, st;
1.39 xsa 208:
209: (void)xasprintf(&p, "%s %s", cvs_editor, pathname);
210: argp[2] = p;
211:
212: sighup = signal(SIGHUP, SIG_IGN);
213: sigint = signal(SIGINT, SIG_IGN);
214: sigquit = signal(SIGQUIT, SIG_IGN);
1.40 ray 215: if ((pid = fork()) == -1)
216: goto fail;
1.39 xsa 217: if (pid == 0) {
218: execv(_PATH_BSHELL, argp);
219: _exit(127);
220: }
1.40 ray 221: while (waitpid(pid, &st, 0) == -1)
222: if (errno != EINTR)
223: goto fail;
1.39 xsa 224: xfree(p);
225: (void)signal(SIGHUP, sighup);
226: (void)signal(SIGINT, sigint);
227: (void)signal(SIGQUIT, sigquit);
1.40 ray 228: if (!WIFEXITED(st)) {
229: errno = EINTR;
1.39 xsa 230: return (-1);
231: }
1.40 ray 232: return (WEXITSTATUS(st));
233:
234: fail:
235: saved_errno = errno;
236: (void)signal(SIGHUP, sighup);
237: (void)signal(SIGINT, sigint);
238: (void)signal(SIGQUIT, sigquit);
239: xfree(p);
240: errno = saved_errno;
241: return (-1);
1.1 jfb 242: }