Annotation of src/usr.bin/cvs/logmsg.c, Revision 1.61
1.61 ! naddy 1: /* $OpenBSD: logmsg.c,v 1.60 2017/05/28 16:57:01 joris 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.56 millert 27: #include <stdint.h>
1.45 chl 28: #include <stdlib.h>
1.38 otto 29: #include <string.h>
30: #include <unistd.h>
1.1 jfb 31:
32: #include "cvs.h"
33:
1.30 joris 34: #define CVS_LOGMSG_PREFIX "CVS:"
35: #define CVS_LOGMSG_LINE \
1.1 jfb 36: "----------------------------------------------------------------------"
37:
1.39 xsa 38: int cvs_logmsg_edit(const char *);
39:
1.17 xsa 40: char *
1.30 joris 41: cvs_logmsg_read(const char *path)
1.1 jfb 42: {
1.30 joris 43: int fd;
44: BUF *bp;
45: FILE *fp;
1.1 jfb 46: size_t len;
47: struct stat st;
1.30 joris 48: char *buf, *lbuf;
49:
50: if ((fd = open(path, O_RDONLY)) == -1)
51: fatal("cvs_logmsg_read: open %s", strerror(errno));
1.1 jfb 52:
1.30 joris 53: if (fstat(fd, &st) == -1)
54: fatal("cvs_logmsg_read: fstat %s", strerror(errno));
1.1 jfb 55:
1.24 xsa 56: if (!S_ISREG(st.st_mode))
1.30 joris 57: fatal("cvs_logmsg_read: file is not a regular file");
1.1 jfb 58:
1.30 joris 59: if ((fp = fdopen(fd, "r")) == NULL)
60: fatal("cvs_logmsg_read: fdopen %s", strerror(errno));
1.47 tobias 61:
1.59 millert 62: if ((uintmax_t)st.st_size > SIZE_MAX)
1.48 tobias 63: fatal("cvs_logmsg_read: %s: file size too big", path);
1.1 jfb 64:
1.30 joris 65: lbuf = NULL;
1.54 ray 66: bp = buf_alloc(st.st_size);
1.30 joris 67: while ((buf = fgetln(fp, &len))) {
68: if (buf[len - 1] == '\n') {
69: buf[len - 1] = '\0';
1.51 ray 70: --len;
1.30 joris 71: } else {
72: lbuf = xmalloc(len + 1);
1.36 otto 73: memcpy(lbuf, buf, len);
74: lbuf[len] = '\0';
1.30 joris 75: buf = lbuf;
76: }
77:
78: if (!strncmp(buf, CVS_LOGMSG_PREFIX,
1.44 tobias 79: sizeof(CVS_LOGMSG_PREFIX) - 1))
1.1 jfb 80: continue;
81:
1.54 ray 82: buf_append(bp, buf, len);
83: buf_putc(bp, '\n');
1.30 joris 84: }
85:
1.57 nicm 86: free(lbuf);
1.1 jfb 87:
1.11 jfb 88: (void)fclose(fp);
89:
1.54 ray 90: buf_putc(bp, '\0');
91: return (buf_release(bp));
1.1 jfb 92: }
93:
1.17 xsa 94: char *
1.49 tobias 95: cvs_logmsg_create(char *dir, struct cvs_flisthead *added,
96: struct cvs_flisthead *removed, struct cvs_flisthead *modified)
1.1 jfb 97: {
1.50 joris 98: FILE *fp, *rp;
99: int c, fd, rd, saved_errno;
1.30 joris 100: struct cvs_filelist *cf;
1.1 jfb 101: struct stat st1, st2;
1.55 deraadt 102: char *fpath, *logmsg, repo[PATH_MAX];
1.61 ! naddy 103: char *f, path[PATH_MAX];
1.50 joris 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:
1.53 ray 118: 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));
1.59 millert 146: if ((uintmax_t)st.st_size > SIZE_MAX)
1.50 joris 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);
1.57 nicm 152: free(logmsg);
1.50 joris 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.60 joris 159: "removed automatically\n%s \n", CVS_LOGMSG_PREFIX, CVS_LOGMSG_LINE,
1.8 jfb 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.52 joris 167: if (added != NULL && !RB_EMPTY(added)) {
1.30 joris 168: fprintf(fp, "%s Added Files:", CVS_LOGMSG_PREFIX);
1.61 ! naddy 169: RB_FOREACH(cf, cvs_flisthead, added) {
! 170: f = cf->file_path;
! 171: if (dir != NULL) {
! 172: if (strlcpy(path, f, sizeof(path)) >=
! 173: sizeof(path))
! 174: fatal("cvs_logmsg_create: truncation");
! 175: f = basename(path);
! 176: }
! 177: fprintf(fp, "\n%s \t%s ", CVS_LOGMSG_PREFIX, f);
! 178: }
1.30 joris 179: fputs("\n", fp);
180: }
181:
1.52 joris 182: if (removed != NULL && !RB_EMPTY(removed)) {
1.30 joris 183: fprintf(fp, "%s Removed Files:", CVS_LOGMSG_PREFIX);
1.61 ! naddy 184: RB_FOREACH(cf, cvs_flisthead, removed) {
! 185: f = cf->file_path;
! 186: if (dir != NULL) {
! 187: if (strlcpy(path, f, sizeof(path)) >=
! 188: sizeof(path))
! 189: fatal("cvs_logmsg_create: truncation");
! 190: f = basename(path);
! 191: }
! 192: fprintf(fp, "\n%s \t%s ", CVS_LOGMSG_PREFIX, f);
! 193: }
1.30 joris 194: fputs("\n", fp);
195: }
196:
1.52 joris 197: if (modified != NULL && !RB_EMPTY(modified)) {
1.30 joris 198: fprintf(fp, "%s Modified Files:", CVS_LOGMSG_PREFIX);
1.61 ! naddy 199: RB_FOREACH(cf, cvs_flisthead, modified) {
! 200: f = cf->file_path;
! 201: if (dir != NULL) {
! 202: if (strlcpy(path, f, sizeof(path)) >=
! 203: sizeof(path))
! 204: fatal("cvs_logmsg_create: truncation");
! 205: f = basename(path);
! 206: }
! 207: fprintf(fp, "\n%s \t%s ", CVS_LOGMSG_PREFIX, f);
! 208: }
1.30 joris 209: fputs("\n", fp);
210: }
1.1 jfb 211:
1.9 jfb 212: fprintf(fp, "%s %s\n", CVS_LOGMSG_PREFIX, CVS_LOGMSG_LINE);
1.1 jfb 213: (void)fflush(fp);
214:
215: if (fstat(fd, &st1) == -1) {
1.34 xsa 216: saved_errno = errno;
1.30 joris 217: (void)unlink(fpath);
1.34 xsa 218: fatal("cvs_logmsg_create: fstat %s", strerror(saved_errno));
1.1 jfb 219: }
220:
1.30 joris 221: logmsg = NULL;
222:
1.1 jfb 223: for (;;) {
1.41 ray 224: if (cvs_logmsg_edit(fpath) == -1)
1.1 jfb 225: break;
1.8 jfb 226:
1.1 jfb 227: if (fstat(fd, &st2) == -1) {
1.34 xsa 228: saved_errno = errno;
1.30 joris 229: (void)unlink(fpath);
1.34 xsa 230: fatal("cvs_logmsg_create: fstat %s",
231: strerror(saved_errno));
1.1 jfb 232: }
233:
1.30 joris 234: if (st1.st_mtime != st2.st_mtime) {
235: logmsg = cvs_logmsg_read(fpath);
1.57 nicm 236: free(prevmsg);
1.49 tobias 237: prevmsg = xstrdup(logmsg);
1.1 jfb 238: break;
239: }
240:
1.30 joris 241: printf("\nLog message unchanged or not specified\n"
1.49 tobias 242: "a)bort, c)ontinue, e)dit, !)reuse this message "
1.58 tb 243: "unchanged for remaining dirs\nAction: (abort) ");
1.30 joris 244: (void)fflush(stdout);
245:
246: c = getc(stdin);
1.58 tb 247: if (c == EOF || c == '\n' || c == 'a' || c == 'A') {
1.30 joris 248: fatal("Aborted by user");
1.58 tb 249: } else if (c == 'c' || c == 'C') {
1.49 tobias 250: if (prevmsg == NULL)
251: prevmsg = xstrdup("");
252: logmsg = xstrdup(prevmsg);
1.1 jfb 253: break;
1.58 tb 254: } else if (c == 'e' || c == 'E') {
1.1 jfb 255: continue;
1.49 tobias 256: } else if (c == '!') {
257: reuse = 1;
258: if (prevmsg == NULL)
259: prevmsg = xstrdup("");
260: logmsg = xstrdup(prevmsg);
261: break;
1.30 joris 262: } else {
263: cvs_log(LP_ERR, "invalid input");
1.1 jfb 264: continue;
265: }
266: }
267:
268: (void)fclose(fp);
1.30 joris 269: (void)unlink(fpath);
1.57 nicm 270: free(fpath);
1.1 jfb 271:
1.30 joris 272: return (logmsg);
1.39 xsa 273: }
274:
1.41 ray 275: /*
276: * Execute an editor on the specified pathname, which is interpreted
277: * from the shell. This means flags may be included.
278: *
279: * Returns -1 on error, or the exit value on success.
280: */
1.39 xsa 281: int
282: cvs_logmsg_edit(const char *pathname)
283: {
284: char *argp[] = {"sh", "-c", NULL, NULL}, *p;
285: sig_t sighup, sigint, sigquit;
286: pid_t pid;
1.40 ray 287: int saved_errno, st;
1.39 xsa 288:
289: (void)xasprintf(&p, "%s %s", cvs_editor, pathname);
290: argp[2] = p;
291:
292: sighup = signal(SIGHUP, SIG_IGN);
293: sigint = signal(SIGINT, SIG_IGN);
294: sigquit = signal(SIGQUIT, SIG_IGN);
1.40 ray 295: if ((pid = fork()) == -1)
296: goto fail;
1.39 xsa 297: if (pid == 0) {
298: execv(_PATH_BSHELL, argp);
299: _exit(127);
300: }
1.40 ray 301: while (waitpid(pid, &st, 0) == -1)
302: if (errno != EINTR)
303: goto fail;
1.57 nicm 304: free(p);
1.39 xsa 305: (void)signal(SIGHUP, sighup);
306: (void)signal(SIGINT, sigint);
307: (void)signal(SIGQUIT, sigquit);
1.40 ray 308: if (!WIFEXITED(st)) {
309: errno = EINTR;
1.39 xsa 310: return (-1);
311: }
1.40 ray 312: return (WEXITSTATUS(st));
313:
314: fail:
315: saved_errno = errno;
316: (void)signal(SIGHUP, sighup);
317: (void)signal(SIGINT, sigint);
318: (void)signal(SIGQUIT, sigquit);
1.57 nicm 319: free(p);
1.40 ray 320: errno = saved_errno;
321: return (-1);
1.1 jfb 322: }
1.50 joris 323:
324: int
325: cvs_logmsg_verify(char *logmsg)
326: {
327: int fd, ret = 0;
328: char *fpath;
329: struct trigger_list *line_list;
330: struct file_info_list files_info;
331: struct file_info *fi;
332:
333: line_list = cvs_trigger_getlines(CVS_PATH_VERIFYMSG, "DEFAULT");
334: if (line_list != NULL) {
335: TAILQ_INIT(&files_info);
336:
337: (void)xasprintf(&fpath, "%s/cvsXXXXXXXXXX", cvs_tmpdir);
338: if ((fd = mkstemp(fpath)) == -1)
339: fatal("cvs_logmsg_verify: mkstemp %s", strerror(errno));
340:
341: fi = xcalloc(1, sizeof(*fi));
342: fi->file_path = xstrdup(fpath);
343: TAILQ_INSERT_TAIL(&files_info, fi, flist);
344:
345: if (cvs_trigger_handle(CVS_TRIGGER_VERIFYMSG, NULL, NULL,
346: line_list, &files_info)) {
347: cvs_log(LP_ERR, "Log message check failed");
348: ret = 1;
349: }
350:
351: cvs_trigger_freeinfo(&files_info);
352: (void)close(fd);
353: (void)unlink(fpath);
1.57 nicm 354: free(fpath);
1.50 joris 355: cvs_trigger_freelist(line_list);
356: }
357:
358: return ret;
359: }
360: