1 /* $OpenBSD: logmsg.c,v 1.37 2007/01/25 22:49:39 xsa Exp $ */ 2 /* 3 * Copyright (c) 2007 Joris Vink <joris@openbsd.org> 4 * 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. 16 */ 17 18 #include "includes.h" 19 20 #include "cvs.h" 21 #include "file.h" 22 #include "log.h" 23 #include "worklist.h" 24 25 #define CVS_LOGMSG_PREFIX "CVS:" 26 #define CVS_LOGMSG_LINE \ 27 "----------------------------------------------------------------------" 28 29 char * 30 cvs_logmsg_read(const char *path) 31 { 32 int fd; 33 BUF *bp; 34 FILE *fp; 35 size_t len; 36 struct stat st; 37 char *buf, *lbuf; 38 39 if ((fd = open(path, O_RDONLY)) == -1) 40 fatal("cvs_logmsg_read: open %s", strerror(errno)); 41 42 if (fstat(fd, &st) == -1) 43 fatal("cvs_logmsg_read: fstat %s", strerror(errno)); 44 45 if (!S_ISREG(st.st_mode)) 46 fatal("cvs_logmsg_read: file is not a regular file"); 47 48 if ((fp = fdopen(fd, "r")) == NULL) 49 fatal("cvs_logmsg_read: fdopen %s", strerror(errno)); 50 51 lbuf = NULL; 52 bp = cvs_buf_alloc(st.st_size, BUF_AUTOEXT); 53 while ((buf = fgetln(fp, &len))) { 54 if (buf[len - 1] == '\n') { 55 buf[len - 1] = '\0'; 56 } else { 57 lbuf = xmalloc(len + 1); 58 memcpy(lbuf, buf, len); 59 lbuf[len] = '\0'; 60 buf = lbuf; 61 } 62 63 len = strlen(buf); 64 if (len == 0) 65 continue; 66 67 if (!strncmp(buf, CVS_LOGMSG_PREFIX, 68 strlen(CVS_LOGMSG_PREFIX))) 69 continue; 70 71 cvs_buf_append(bp, buf, len); 72 cvs_buf_putc(bp, '\n'); 73 } 74 75 if (lbuf != NULL) 76 xfree(lbuf); 77 78 (void)fclose(fp); 79 80 cvs_buf_putc(bp, '\0'); 81 return (cvs_buf_release(bp)); 82 } 83 84 char * 85 cvs_logmsg_create(struct cvs_flisthead *added, struct cvs_flisthead *removed, 86 struct cvs_flisthead *modified) 87 { 88 FILE *fp; 89 int c, fd, argc, saved_errno; 90 struct cvs_filelist *cf; 91 struct stat st1, st2; 92 char *fpath, *logmsg, *argv[4]; 93 94 (void)xasprintf(&fpath, "%s/cvsXXXXXXXXXX", cvs_tmpdir); 95 96 if ((fd = mkstemp(fpath)) == NULL) 97 fatal("cvs_logmsg_create: mkstemp %s", strerror(errno)); 98 99 cvs_worklist_add(fpath, &temp_files); 100 101 if ((fp = fdopen(fd, "w")) == NULL) { 102 saved_errno = errno; 103 (void)unlink(fpath); 104 fatal("cvs_logmsg_create: fdopen %s", strerror(saved_errno)); 105 } 106 107 fprintf(fp, "\n%s %s\n%s Enter Log. Lines beginning with `%s' are " 108 "removed automatically\n%s\n", CVS_LOGMSG_PREFIX, CVS_LOGMSG_LINE, 109 CVS_LOGMSG_PREFIX, CVS_LOGMSG_PREFIX, CVS_LOGMSG_PREFIX); 110 111 if (added != NULL && !TAILQ_EMPTY(added)) { 112 fprintf(fp, "%s Added Files:", CVS_LOGMSG_PREFIX); 113 TAILQ_FOREACH(cf, added, flist) 114 fprintf(fp, "\n%s\t%s", 115 CVS_LOGMSG_PREFIX, cf->file_path); 116 fputs("\n", fp); 117 } 118 119 if (removed != NULL && !TAILQ_EMPTY(removed)) { 120 fprintf(fp, "%s Removed Files:", CVS_LOGMSG_PREFIX); 121 TAILQ_FOREACH(cf, removed, flist) 122 fprintf(fp, "\n%s\t%s", 123 CVS_LOGMSG_PREFIX, cf->file_path); 124 fputs("\n", fp); 125 } 126 127 if (modified != NULL && !TAILQ_EMPTY(modified)) { 128 fprintf(fp, "%s Modified Files:", CVS_LOGMSG_PREFIX); 129 TAILQ_FOREACH(cf, modified, flist) 130 fprintf(fp, "\n%s\t%s", 131 CVS_LOGMSG_PREFIX, cf->file_path); 132 fputs("\n", fp); 133 } 134 135 fprintf(fp, "%s %s\n", CVS_LOGMSG_PREFIX, CVS_LOGMSG_LINE); 136 (void)fflush(fp); 137 138 if (fstat(fd, &st1) == -1) { 139 saved_errno = errno; 140 (void)unlink(fpath); 141 fatal("cvs_logmsg_create: fstat %s", strerror(saved_errno)); 142 } 143 144 argc = 0; 145 argv[argc++] = cvs_editor; 146 argv[argc++] = fpath; 147 argv[argc] = NULL; 148 149 logmsg = NULL; 150 151 for (;;) { 152 if (cvs_exec(argc, argv) < 0) 153 break; 154 155 if (fstat(fd, &st2) == -1) { 156 saved_errno = errno; 157 (void)unlink(fpath); 158 fatal("cvs_logmsg_create: fstat %s", 159 strerror(saved_errno)); 160 } 161 162 if (st1.st_mtime != st2.st_mtime) { 163 logmsg = cvs_logmsg_read(fpath); 164 break; 165 } 166 167 printf("\nLog message unchanged or not specified\n" 168 "a)bort, c)ontinue, e)dit\nAction: (continue) "); 169 (void)fflush(stdout); 170 171 c = getc(stdin); 172 if (c == 'a') { 173 fatal("Aborted by user"); 174 } else if (c == '\n' || c == 'c') { 175 logmsg = xstrdup(""); 176 break; 177 } else if (c == 'e') { 178 continue; 179 } else { 180 cvs_log(LP_ERR, "invalid input"); 181 continue; 182 } 183 } 184 185 (void)fclose(fp); 186 (void)unlink(fpath); 187 xfree(fpath); 188 189 return (logmsg); 190 } 191