Annotation of src/usr.bin/cvs/logmsg.c, Revision 1.47
1.47 ! tobias 1: /* $OpenBSD: logmsg.c,v 1.46 2008/02/11 20:33:11 tobias 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.45 chl 26: #include <stdlib.h>
1.38 otto 27: #include <string.h>
28: #include <unistd.h>
1.1 jfb 29:
30: #include "cvs.h"
31:
1.30 joris 32: #define CVS_LOGMSG_PREFIX "CVS:"
33: #define CVS_LOGMSG_LINE \
1.1 jfb 34: "----------------------------------------------------------------------"
35:
1.39 xsa 36: int cvs_logmsg_edit(const char *);
37:
1.17 xsa 38: char *
1.30 joris 39: cvs_logmsg_read(const char *path)
1.1 jfb 40: {
1.30 joris 41: int fd;
42: BUF *bp;
43: FILE *fp;
1.1 jfb 44: size_t len;
45: struct stat st;
1.30 joris 46: char *buf, *lbuf;
47:
48: if ((fd = open(path, O_RDONLY)) == -1)
49: fatal("cvs_logmsg_read: open %s", strerror(errno));
1.1 jfb 50:
1.30 joris 51: if (fstat(fd, &st) == -1)
52: fatal("cvs_logmsg_read: fstat %s", strerror(errno));
1.1 jfb 53:
1.24 xsa 54: if (!S_ISREG(st.st_mode))
1.30 joris 55: fatal("cvs_logmsg_read: file is not a regular file");
1.1 jfb 56:
1.30 joris 57: if ((fp = fdopen(fd, "r")) == NULL)
58: fatal("cvs_logmsg_read: fdopen %s", strerror(errno));
1.47 ! tobias 59:
! 60: if (st.st_size > SIZE_MAX)
! 61: fatal("cvs_buf_load_fd: %s: file size too big", path);
1.1 jfb 62:
1.30 joris 63: lbuf = NULL;
1.46 tobias 64: bp = cvs_buf_alloc(st.st_size);
1.30 joris 65: while ((buf = fgetln(fp, &len))) {
66: if (buf[len - 1] == '\n') {
67: buf[len - 1] = '\0';
68: } else {
69: lbuf = xmalloc(len + 1);
1.36 otto 70: memcpy(lbuf, buf, len);
71: lbuf[len] = '\0';
1.30 joris 72: buf = lbuf;
73: }
1.1 jfb 74:
1.30 joris 75: len = strlen(buf);
76:
77: if (!strncmp(buf, CVS_LOGMSG_PREFIX,
1.44 tobias 78: sizeof(CVS_LOGMSG_PREFIX) - 1))
1.1 jfb 79: continue;
80:
1.30 joris 81: cvs_buf_append(bp, buf, len);
82: cvs_buf_putc(bp, '\n');
83: }
84:
85: if (lbuf != NULL)
86: xfree(lbuf);
1.1 jfb 87:
1.11 jfb 88: (void)fclose(fp);
89:
1.23 xsa 90: cvs_buf_putc(bp, '\0');
1.30 joris 91: return (cvs_buf_release(bp));
1.1 jfb 92: }
93:
1.17 xsa 94: char *
1.30 joris 95: cvs_logmsg_create(struct cvs_flisthead *added, struct cvs_flisthead *removed,
96: struct cvs_flisthead *modified)
1.1 jfb 97: {
98: FILE *fp;
1.39 xsa 99: int c, fd, saved_errno;
1.30 joris 100: struct cvs_filelist *cf;
1.1 jfb 101: struct stat st1, st2;
1.39 xsa 102: char *fpath, *logmsg;
1.8 jfb 103:
1.35 xsa 104: (void)xasprintf(&fpath, "%s/cvsXXXXXXXXXX", cvs_tmpdir);
1.1 jfb 105:
1.45 chl 106: if ((fd = mkstemp(fpath)) == -1)
1.30 joris 107: fatal("cvs_logmsg_create: mkstemp %s", strerror(errno));
1.33 joris 108:
109: cvs_worklist_add(fpath, &temp_files);
1.1 jfb 110:
1.24 xsa 111: if ((fp = fdopen(fd, "w")) == NULL) {
1.34 xsa 112: saved_errno = errno;
1.30 joris 113: (void)unlink(fpath);
1.34 xsa 114: fatal("cvs_logmsg_create: fdopen %s", strerror(saved_errno));
1.8 jfb 115: }
1.1 jfb 116:
1.8 jfb 117: fprintf(fp, "\n%s %s\n%s Enter Log. Lines beginning with `%s' are "
118: "removed automatically\n%s\n", CVS_LOGMSG_PREFIX, CVS_LOGMSG_LINE,
119: CVS_LOGMSG_PREFIX, CVS_LOGMSG_PREFIX, CVS_LOGMSG_PREFIX);
120:
1.31 joris 121: if (added != NULL && !TAILQ_EMPTY(added)) {
1.30 joris 122: fprintf(fp, "%s Added Files:", CVS_LOGMSG_PREFIX);
123: TAILQ_FOREACH(cf, added, flist)
124: fprintf(fp, "\n%s\t%s",
125: CVS_LOGMSG_PREFIX, cf->file_path);
126: fputs("\n", fp);
127: }
128:
1.31 joris 129: if (removed != NULL && !TAILQ_EMPTY(removed)) {
1.30 joris 130: fprintf(fp, "%s Removed Files:", CVS_LOGMSG_PREFIX);
131: TAILQ_FOREACH(cf, removed, flist)
132: fprintf(fp, "\n%s\t%s",
133: CVS_LOGMSG_PREFIX, cf->file_path);
134: fputs("\n", fp);
135: }
136:
1.31 joris 137: if (modified != NULL && !TAILQ_EMPTY(modified)) {
1.30 joris 138: fprintf(fp, "%s Modified Files:", CVS_LOGMSG_PREFIX);
139: TAILQ_FOREACH(cf, modified, flist)
140: fprintf(fp, "\n%s\t%s",
141: CVS_LOGMSG_PREFIX, cf->file_path);
142: fputs("\n", fp);
143: }
1.1 jfb 144:
1.9 jfb 145: fprintf(fp, "%s %s\n", CVS_LOGMSG_PREFIX, CVS_LOGMSG_LINE);
1.1 jfb 146: (void)fflush(fp);
147:
148: if (fstat(fd, &st1) == -1) {
1.34 xsa 149: saved_errno = errno;
1.30 joris 150: (void)unlink(fpath);
1.34 xsa 151: fatal("cvs_logmsg_create: fstat %s", strerror(saved_errno));
1.1 jfb 152: }
153:
1.30 joris 154: logmsg = NULL;
155:
1.1 jfb 156: for (;;) {
1.41 ray 157: if (cvs_logmsg_edit(fpath) == -1)
1.1 jfb 158: break;
1.8 jfb 159:
1.1 jfb 160: if (fstat(fd, &st2) == -1) {
1.34 xsa 161: saved_errno = errno;
1.30 joris 162: (void)unlink(fpath);
1.34 xsa 163: fatal("cvs_logmsg_create: fstat %s",
164: strerror(saved_errno));
1.1 jfb 165: }
166:
1.30 joris 167: if (st1.st_mtime != st2.st_mtime) {
168: logmsg = cvs_logmsg_read(fpath);
1.1 jfb 169: break;
170: }
171:
1.30 joris 172: printf("\nLog message unchanged or not specified\n"
1.32 jasper 173: "a)bort, c)ontinue, e)dit\nAction: (continue) ");
1.30 joris 174: (void)fflush(stdout);
175:
176: c = getc(stdin);
1.43 tobias 177: if (c == EOF || c == 'a') {
1.30 joris 178: fatal("Aborted by user");
179: } else if (c == '\n' || c == 'c') {
180: logmsg = xstrdup("");
1.1 jfb 181: break;
1.30 joris 182: } else if (c == 'e') {
1.1 jfb 183: continue;
1.30 joris 184: } else {
185: cvs_log(LP_ERR, "invalid input");
1.1 jfb 186: continue;
187: }
188: }
189:
190: (void)fclose(fp);
1.30 joris 191: (void)unlink(fpath);
192: xfree(fpath);
1.1 jfb 193:
1.30 joris 194: return (logmsg);
1.39 xsa 195: }
196:
1.41 ray 197: /*
198: * Execute an editor on the specified pathname, which is interpreted
199: * from the shell. This means flags may be included.
200: *
201: * Returns -1 on error, or the exit value on success.
202: */
1.39 xsa 203: int
204: cvs_logmsg_edit(const char *pathname)
205: {
206: char *argp[] = {"sh", "-c", NULL, NULL}, *p;
207: sig_t sighup, sigint, sigquit;
208: pid_t pid;
1.40 ray 209: int saved_errno, st;
1.39 xsa 210:
211: (void)xasprintf(&p, "%s %s", cvs_editor, pathname);
212: argp[2] = p;
213:
214: sighup = signal(SIGHUP, SIG_IGN);
215: sigint = signal(SIGINT, SIG_IGN);
216: sigquit = signal(SIGQUIT, SIG_IGN);
1.40 ray 217: if ((pid = fork()) == -1)
218: goto fail;
1.39 xsa 219: if (pid == 0) {
220: execv(_PATH_BSHELL, argp);
221: _exit(127);
222: }
1.40 ray 223: while (waitpid(pid, &st, 0) == -1)
224: if (errno != EINTR)
225: goto fail;
1.39 xsa 226: xfree(p);
227: (void)signal(SIGHUP, sighup);
228: (void)signal(SIGINT, sigint);
229: (void)signal(SIGQUIT, sigquit);
1.40 ray 230: if (!WIFEXITED(st)) {
231: errno = EINTR;
1.39 xsa 232: return (-1);
233: }
1.40 ray 234: return (WEXITSTATUS(st));
235:
236: fail:
237: saved_errno = errno;
238: (void)signal(SIGHUP, sighup);
239: (void)signal(SIGINT, sigint);
240: (void)signal(SIGQUIT, sigquit);
241: xfree(p);
242: errno = saved_errno;
243: return (-1);
1.1 jfb 244: }