Annotation of src/usr.bin/cvs/logmsg.c, Revision 1.50
1.50 ! joris 1: /* $OpenBSD: logmsg.c,v 1.49 2008/06/08 20:08:43 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.49 tobias 24: #include <libgen.h>
1.39 xsa 25: #include <paths.h>
26: #include <signal.h>
1.45 chl 27: #include <stdlib.h>
1.38 otto 28: #include <string.h>
29: #include <unistd.h>
1.1 jfb 30:
31: #include "cvs.h"
32:
1.30 joris 33: #define CVS_LOGMSG_PREFIX "CVS:"
34: #define CVS_LOGMSG_LINE \
1.1 jfb 35: "----------------------------------------------------------------------"
36:
1.39 xsa 37: int cvs_logmsg_edit(const char *);
38:
1.17 xsa 39: char *
1.30 joris 40: cvs_logmsg_read(const char *path)
1.1 jfb 41: {
1.30 joris 42: int fd;
43: BUF *bp;
44: FILE *fp;
1.1 jfb 45: size_t len;
46: struct stat st;
1.30 joris 47: char *buf, *lbuf;
48:
49: if ((fd = open(path, O_RDONLY)) == -1)
50: fatal("cvs_logmsg_read: open %s", strerror(errno));
1.1 jfb 51:
1.30 joris 52: if (fstat(fd, &st) == -1)
53: fatal("cvs_logmsg_read: fstat %s", strerror(errno));
1.1 jfb 54:
1.24 xsa 55: if (!S_ISREG(st.st_mode))
1.30 joris 56: fatal("cvs_logmsg_read: file is not a regular file");
1.1 jfb 57:
1.30 joris 58: if ((fp = fdopen(fd, "r")) == NULL)
59: fatal("cvs_logmsg_read: fdopen %s", strerror(errno));
1.47 tobias 60:
61: if (st.st_size > SIZE_MAX)
1.48 tobias 62: fatal("cvs_logmsg_read: %s: file size too big", path);
1.1 jfb 63:
1.30 joris 64: lbuf = NULL;
1.46 tobias 65: bp = cvs_buf_alloc(st.st_size);
1.30 joris 66: while ((buf = fgetln(fp, &len))) {
67: if (buf[len - 1] == '\n') {
68: buf[len - 1] = '\0';
69: } else {
70: lbuf = xmalloc(len + 1);
1.36 otto 71: memcpy(lbuf, buf, len);
72: lbuf[len] = '\0';
1.30 joris 73: buf = lbuf;
74: }
1.1 jfb 75:
1.30 joris 76: len = strlen(buf);
77:
78: if (!strncmp(buf, CVS_LOGMSG_PREFIX,
1.44 tobias 79: sizeof(CVS_LOGMSG_PREFIX) - 1))
1.1 jfb 80: continue;
81:
1.30 joris 82: cvs_buf_append(bp, buf, len);
83: cvs_buf_putc(bp, '\n');
84: }
85:
86: if (lbuf != NULL)
87: xfree(lbuf);
1.1 jfb 88:
1.11 jfb 89: (void)fclose(fp);
90:
1.23 xsa 91: cvs_buf_putc(bp, '\0');
1.30 joris 92: return (cvs_buf_release(bp));
1.1 jfb 93: }
94:
1.17 xsa 95: char *
1.49 tobias 96: cvs_logmsg_create(char *dir, struct cvs_flisthead *added,
97: struct cvs_flisthead *removed, struct cvs_flisthead *modified)
1.1 jfb 98: {
1.50 ! joris 99: FILE *fp, *rp;
! 100: int c, fd, rd, saved_errno;
1.30 joris 101: struct cvs_filelist *cf;
1.1 jfb 102: struct stat st1, st2;
1.50 ! joris 103: char *fpath, *logmsg, repo[MAXPATHLEN];
! 104: struct stat st;
! 105: struct trigger_list *line_list;
! 106: struct trigger_line *line;
1.49 tobias 107: static int reuse = 0;
108: static char *prevmsg = NULL;
1.8 jfb 109:
1.49 tobias 110: if (reuse)
111: return xstrdup(prevmsg);
112:
1.35 xsa 113: (void)xasprintf(&fpath, "%s/cvsXXXXXXXXXX", cvs_tmpdir);
1.1 jfb 114:
1.45 chl 115: if ((fd = mkstemp(fpath)) == -1)
1.30 joris 116: fatal("cvs_logmsg_create: mkstemp %s", strerror(errno));
1.33 joris 117:
118: cvs_worklist_add(fpath, &temp_files);
1.1 jfb 119:
1.24 xsa 120: if ((fp = fdopen(fd, "w")) == NULL) {
1.34 xsa 121: saved_errno = errno;
1.30 joris 122: (void)unlink(fpath);
1.34 xsa 123: fatal("cvs_logmsg_create: fdopen %s", strerror(saved_errno));
1.8 jfb 124: }
1.1 jfb 125:
1.49 tobias 126: if (prevmsg != NULL && prevmsg[0] != '\0')
127: fprintf(fp, "%s", prevmsg);
128: else
129: fputc('\n', fp);
130:
1.50 ! joris 131: line_list = cvs_trigger_getlines(CVS_PATH_RCSINFO, repo);
! 132: if (line_list != NULL) {
! 133: TAILQ_FOREACH(line, line_list, flist) {
! 134: if ((rd = open(line->line, O_RDONLY)) == -1)
! 135: fatal("cvs_logmsg_create: open %s",
! 136: strerror(errno));
! 137: if (fstat(rd, &st) == -1)
! 138: fatal("cvs_logmsg_create: fstat %s",
! 139: strerror(errno));
! 140: if (!S_ISREG(st.st_mode))
! 141: fatal("cvs_logmsg_create: file is not a "
! 142: "regular file");
! 143: if ((rp = fdopen(rd, "r")) == NULL)
! 144: fatal("cvs_logmsg_create: fdopen %s",
! 145: strerror(errno));
! 146: if (st.st_size > SIZE_MAX)
! 147: fatal("cvs_logmsg_create: %s: file size "
! 148: "too big", line->line);
! 149: logmsg = xmalloc(st.st_size);
! 150: fread(logmsg, st.st_size, 1, rp);
! 151: fwrite(logmsg, st.st_size, 1, fp);
! 152: xfree(logmsg);
! 153: (void)fclose(rp);
! 154: }
! 155: cvs_trigger_freelist(line_list);
! 156: }
! 157:
1.49 tobias 158: fprintf(fp, "%s %s\n%s Enter Log. Lines beginning with `%s' are "
1.8 jfb 159: "removed automatically\n%s\n", CVS_LOGMSG_PREFIX, CVS_LOGMSG_LINE,
160: CVS_LOGMSG_PREFIX, CVS_LOGMSG_PREFIX, CVS_LOGMSG_PREFIX);
161:
1.49 tobias 162: if (cvs_cmdop == CVS_OP_COMMIT) {
163: fprintf(fp, "%s Committing in %s\n%s\n", CVS_LOGMSG_PREFIX,
164: dir != NULL ? dir : ".", CVS_LOGMSG_PREFIX);
165: }
166:
1.31 joris 167: if (added != NULL && !TAILQ_EMPTY(added)) {
1.30 joris 168: fprintf(fp, "%s Added Files:", CVS_LOGMSG_PREFIX);
169: TAILQ_FOREACH(cf, added, flist)
1.49 tobias 170: fprintf(fp, "\n%s\t%s", CVS_LOGMSG_PREFIX,
171: dir != NULL ? basename(cf->file_path) :
172: cf->file_path);
1.30 joris 173: fputs("\n", fp);
174: }
175:
1.31 joris 176: if (removed != NULL && !TAILQ_EMPTY(removed)) {
1.30 joris 177: fprintf(fp, "%s Removed Files:", CVS_LOGMSG_PREFIX);
178: TAILQ_FOREACH(cf, removed, flist)
1.49 tobias 179: fprintf(fp, "\n%s\t%s", CVS_LOGMSG_PREFIX,
180: dir != NULL ? basename(cf->file_path) :
181: cf->file_path);
1.30 joris 182: fputs("\n", fp);
183: }
184:
1.31 joris 185: if (modified != NULL && !TAILQ_EMPTY(modified)) {
1.30 joris 186: fprintf(fp, "%s Modified Files:", CVS_LOGMSG_PREFIX);
187: TAILQ_FOREACH(cf, modified, flist)
1.49 tobias 188: fprintf(fp, "\n%s\t%s", CVS_LOGMSG_PREFIX,
189: dir != NULL ? basename(cf->file_path) :
190: cf->file_path);
1.30 joris 191: fputs("\n", fp);
192: }
1.1 jfb 193:
1.9 jfb 194: fprintf(fp, "%s %s\n", CVS_LOGMSG_PREFIX, CVS_LOGMSG_LINE);
1.1 jfb 195: (void)fflush(fp);
196:
197: if (fstat(fd, &st1) == -1) {
1.34 xsa 198: saved_errno = errno;
1.30 joris 199: (void)unlink(fpath);
1.34 xsa 200: fatal("cvs_logmsg_create: fstat %s", strerror(saved_errno));
1.1 jfb 201: }
202:
1.30 joris 203: logmsg = NULL;
204:
1.1 jfb 205: for (;;) {
1.41 ray 206: if (cvs_logmsg_edit(fpath) == -1)
1.1 jfb 207: break;
1.8 jfb 208:
1.1 jfb 209: if (fstat(fd, &st2) == -1) {
1.34 xsa 210: saved_errno = errno;
1.30 joris 211: (void)unlink(fpath);
1.34 xsa 212: fatal("cvs_logmsg_create: fstat %s",
213: strerror(saved_errno));
1.1 jfb 214: }
215:
1.30 joris 216: if (st1.st_mtime != st2.st_mtime) {
217: logmsg = cvs_logmsg_read(fpath);
1.49 tobias 218: if (prevmsg != NULL)
219: xfree(prevmsg);
220: prevmsg = xstrdup(logmsg);
1.1 jfb 221: break;
222: }
223:
1.30 joris 224: printf("\nLog message unchanged or not specified\n"
1.49 tobias 225: "a)bort, c)ontinue, e)dit, !)reuse this message "
226: "unchanged for remaining dirs\nAction: (continue) ");
1.30 joris 227: (void)fflush(stdout);
228:
229: c = getc(stdin);
1.43 tobias 230: if (c == EOF || c == 'a') {
1.30 joris 231: fatal("Aborted by user");
232: } else if (c == '\n' || c == 'c') {
1.49 tobias 233: if (prevmsg == NULL)
234: prevmsg = xstrdup("");
235: logmsg = xstrdup(prevmsg);
1.1 jfb 236: break;
1.30 joris 237: } else if (c == 'e') {
1.1 jfb 238: continue;
1.49 tobias 239: } else if (c == '!') {
240: reuse = 1;
241: if (prevmsg == NULL)
242: prevmsg = xstrdup("");
243: logmsg = xstrdup(prevmsg);
244: break;
1.30 joris 245: } else {
246: cvs_log(LP_ERR, "invalid input");
1.1 jfb 247: continue;
248: }
249: }
250:
251: (void)fclose(fp);
1.30 joris 252: (void)unlink(fpath);
253: xfree(fpath);
1.1 jfb 254:
1.30 joris 255: return (logmsg);
1.39 xsa 256: }
257:
1.41 ray 258: /*
259: * Execute an editor on the specified pathname, which is interpreted
260: * from the shell. This means flags may be included.
261: *
262: * Returns -1 on error, or the exit value on success.
263: */
1.39 xsa 264: int
265: cvs_logmsg_edit(const char *pathname)
266: {
267: char *argp[] = {"sh", "-c", NULL, NULL}, *p;
268: sig_t sighup, sigint, sigquit;
269: pid_t pid;
1.40 ray 270: int saved_errno, st;
1.39 xsa 271:
272: (void)xasprintf(&p, "%s %s", cvs_editor, pathname);
273: argp[2] = p;
274:
275: sighup = signal(SIGHUP, SIG_IGN);
276: sigint = signal(SIGINT, SIG_IGN);
277: sigquit = signal(SIGQUIT, SIG_IGN);
1.40 ray 278: if ((pid = fork()) == -1)
279: goto fail;
1.39 xsa 280: if (pid == 0) {
281: execv(_PATH_BSHELL, argp);
282: _exit(127);
283: }
1.40 ray 284: while (waitpid(pid, &st, 0) == -1)
285: if (errno != EINTR)
286: goto fail;
1.39 xsa 287: xfree(p);
288: (void)signal(SIGHUP, sighup);
289: (void)signal(SIGINT, sigint);
290: (void)signal(SIGQUIT, sigquit);
1.40 ray 291: if (!WIFEXITED(st)) {
292: errno = EINTR;
1.39 xsa 293: return (-1);
294: }
1.40 ray 295: return (WEXITSTATUS(st));
296:
297: fail:
298: saved_errno = errno;
299: (void)signal(SIGHUP, sighup);
300: (void)signal(SIGINT, sigint);
301: (void)signal(SIGQUIT, sigquit);
302: xfree(p);
303: errno = saved_errno;
304: return (-1);
1.1 jfb 305: }
1.50 ! joris 306:
! 307: int
! 308: cvs_logmsg_verify(char *logmsg)
! 309: {
! 310: int fd, ret = 0;
! 311: char *fpath;
! 312: struct trigger_list *line_list;
! 313: struct file_info_list files_info;
! 314: struct file_info *fi;
! 315:
! 316: line_list = cvs_trigger_getlines(CVS_PATH_VERIFYMSG, "DEFAULT");
! 317: if (line_list != NULL) {
! 318: TAILQ_INIT(&files_info);
! 319:
! 320: (void)xasprintf(&fpath, "%s/cvsXXXXXXXXXX", cvs_tmpdir);
! 321: if ((fd = mkstemp(fpath)) == -1)
! 322: fatal("cvs_logmsg_verify: mkstemp %s", strerror(errno));
! 323:
! 324: fi = xcalloc(1, sizeof(*fi));
! 325: fi->file_path = xstrdup(fpath);
! 326: TAILQ_INSERT_TAIL(&files_info, fi, flist);
! 327:
! 328: if (cvs_trigger_handle(CVS_TRIGGER_VERIFYMSG, NULL, NULL,
! 329: line_list, &files_info)) {
! 330: cvs_log(LP_ERR, "Log message check failed");
! 331: ret = 1;
! 332: }
! 333:
! 334: cvs_trigger_freeinfo(&files_info);
! 335: (void)close(fd);
! 336: (void)unlink(fpath);
! 337: xfree(fpath);
! 338: cvs_trigger_freelist(line_list);
! 339: }
! 340:
! 341: return ret;
! 342: }
! 343: