1*fcb086e6Stobias /* $OpenBSD: logmsg.c,v 1.47 2008/03/09 01:02:38 tobias Exp $ */ 2db758d52Sjoris /* 3db758d52Sjoris * Copyright (c) 2007 Joris Vink <joris@openbsd.org> 4db758d52Sjoris * 5db758d52Sjoris * Permission to use, copy, modify, and distribute this software for any 6db758d52Sjoris * purpose with or without fee is hereby granted, provided that the above 7db758d52Sjoris * copyright notice and this permission notice appear in all copies. 8db758d52Sjoris * 9db758d52Sjoris * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10db758d52Sjoris * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11db758d52Sjoris * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12db758d52Sjoris * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13db758d52Sjoris * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14db758d52Sjoris * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15db758d52Sjoris * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16db758d52Sjoris */ 17db758d52Sjoris 181f8531bdSotto #include <sys/stat.h> 1966510bb6Sxsa #include <sys/types.h> 2066510bb6Sxsa #include <sys/wait.h> 211f8531bdSotto 221f8531bdSotto #include <errno.h> 231f8531bdSotto #include <fcntl.h> 2466510bb6Sxsa #include <paths.h> 2566510bb6Sxsa #include <signal.h> 26f99068d5Schl #include <stdlib.h> 271f8531bdSotto #include <string.h> 281f8531bdSotto #include <unistd.h> 29db758d52Sjoris 30db758d52Sjoris #include "cvs.h" 31db758d52Sjoris 32db758d52Sjoris #define CVS_LOGMSG_PREFIX "CVS:" 33db758d52Sjoris #define CVS_LOGMSG_LINE \ 34db758d52Sjoris "----------------------------------------------------------------------" 35db758d52Sjoris 3666510bb6Sxsa int cvs_logmsg_edit(const char *); 3766510bb6Sxsa 38db758d52Sjoris char * 39db758d52Sjoris cvs_logmsg_read(const char *path) 40db758d52Sjoris { 41db758d52Sjoris int fd; 42db758d52Sjoris BUF *bp; 43db758d52Sjoris FILE *fp; 44db758d52Sjoris size_t len; 45db758d52Sjoris struct stat st; 46db758d52Sjoris char *buf, *lbuf; 47db758d52Sjoris 48db758d52Sjoris if ((fd = open(path, O_RDONLY)) == -1) 49db758d52Sjoris fatal("cvs_logmsg_read: open %s", strerror(errno)); 50db758d52Sjoris 51db758d52Sjoris if (fstat(fd, &st) == -1) 52db758d52Sjoris fatal("cvs_logmsg_read: fstat %s", strerror(errno)); 53db758d52Sjoris 54db758d52Sjoris if (!S_ISREG(st.st_mode)) 55db758d52Sjoris fatal("cvs_logmsg_read: file is not a regular file"); 56db758d52Sjoris 57db758d52Sjoris if ((fp = fdopen(fd, "r")) == NULL) 58db758d52Sjoris fatal("cvs_logmsg_read: fdopen %s", strerror(errno)); 59db758d52Sjoris 60*fcb086e6Stobias if (st.st_size > SIZE_MAX) 61*fcb086e6Stobias fatal("cvs_buf_load_fd: %s: file size too big", path); 62*fcb086e6Stobias 63db758d52Sjoris lbuf = NULL; 649aad96bcStobias bp = cvs_buf_alloc(st.st_size); 65db758d52Sjoris while ((buf = fgetln(fp, &len))) { 66db758d52Sjoris if (buf[len - 1] == '\n') { 67db758d52Sjoris buf[len - 1] = '\0'; 68db758d52Sjoris } else { 69db758d52Sjoris lbuf = xmalloc(len + 1); 707347534fSotto memcpy(lbuf, buf, len); 717347534fSotto lbuf[len] = '\0'; 72db758d52Sjoris buf = lbuf; 73db758d52Sjoris } 74db758d52Sjoris 75db758d52Sjoris len = strlen(buf); 76db758d52Sjoris 77db758d52Sjoris if (!strncmp(buf, CVS_LOGMSG_PREFIX, 78870158f9Stobias sizeof(CVS_LOGMSG_PREFIX) - 1)) 79db758d52Sjoris continue; 80db758d52Sjoris 81db758d52Sjoris cvs_buf_append(bp, buf, len); 82db758d52Sjoris cvs_buf_putc(bp, '\n'); 83db758d52Sjoris } 84db758d52Sjoris 85db758d52Sjoris if (lbuf != NULL) 86db758d52Sjoris xfree(lbuf); 87db758d52Sjoris 88db758d52Sjoris (void)fclose(fp); 89db758d52Sjoris 90db758d52Sjoris cvs_buf_putc(bp, '\0'); 91db758d52Sjoris return (cvs_buf_release(bp)); 92db758d52Sjoris } 93db758d52Sjoris 94db758d52Sjoris char * 95db758d52Sjoris cvs_logmsg_create(struct cvs_flisthead *added, struct cvs_flisthead *removed, 96db758d52Sjoris struct cvs_flisthead *modified) 97db758d52Sjoris { 98db758d52Sjoris FILE *fp; 9966510bb6Sxsa int c, fd, saved_errno; 100db758d52Sjoris struct cvs_filelist *cf; 101db758d52Sjoris struct stat st1, st2; 10266510bb6Sxsa char *fpath, *logmsg; 103db758d52Sjoris 1048ad0f384Sxsa (void)xasprintf(&fpath, "%s/cvsXXXXXXXXXX", cvs_tmpdir); 105db758d52Sjoris 106f99068d5Schl if ((fd = mkstemp(fpath)) == -1) 107db758d52Sjoris fatal("cvs_logmsg_create: mkstemp %s", strerror(errno)); 108db758d52Sjoris 10964f70df8Sjoris cvs_worklist_add(fpath, &temp_files); 11064f70df8Sjoris 111db758d52Sjoris if ((fp = fdopen(fd, "w")) == NULL) { 112ba617dc1Sxsa saved_errno = errno; 113db758d52Sjoris (void)unlink(fpath); 114ba617dc1Sxsa fatal("cvs_logmsg_create: fdopen %s", strerror(saved_errno)); 115db758d52Sjoris } 116db758d52Sjoris 117db758d52Sjoris fprintf(fp, "\n%s %s\n%s Enter Log. Lines beginning with `%s' are " 118db758d52Sjoris "removed automatically\n%s\n", CVS_LOGMSG_PREFIX, CVS_LOGMSG_LINE, 119db758d52Sjoris CVS_LOGMSG_PREFIX, CVS_LOGMSG_PREFIX, CVS_LOGMSG_PREFIX); 120db758d52Sjoris 1218c49b454Sjoris if (added != NULL && !TAILQ_EMPTY(added)) { 122db758d52Sjoris fprintf(fp, "%s Added Files:", CVS_LOGMSG_PREFIX); 123db758d52Sjoris TAILQ_FOREACH(cf, added, flist) 124db758d52Sjoris fprintf(fp, "\n%s\t%s", 125db758d52Sjoris CVS_LOGMSG_PREFIX, cf->file_path); 126db758d52Sjoris fputs("\n", fp); 127db758d52Sjoris } 128db758d52Sjoris 1298c49b454Sjoris if (removed != NULL && !TAILQ_EMPTY(removed)) { 130db758d52Sjoris fprintf(fp, "%s Removed Files:", CVS_LOGMSG_PREFIX); 131db758d52Sjoris TAILQ_FOREACH(cf, removed, flist) 132db758d52Sjoris fprintf(fp, "\n%s\t%s", 133db758d52Sjoris CVS_LOGMSG_PREFIX, cf->file_path); 134db758d52Sjoris fputs("\n", fp); 135db758d52Sjoris } 136db758d52Sjoris 1378c49b454Sjoris if (modified != NULL && !TAILQ_EMPTY(modified)) { 138db758d52Sjoris fprintf(fp, "%s Modified Files:", CVS_LOGMSG_PREFIX); 139db758d52Sjoris TAILQ_FOREACH(cf, modified, flist) 140db758d52Sjoris fprintf(fp, "\n%s\t%s", 141db758d52Sjoris CVS_LOGMSG_PREFIX, cf->file_path); 142db758d52Sjoris fputs("\n", fp); 143db758d52Sjoris } 144db758d52Sjoris 145db758d52Sjoris fprintf(fp, "%s %s\n", CVS_LOGMSG_PREFIX, CVS_LOGMSG_LINE); 146db758d52Sjoris (void)fflush(fp); 147db758d52Sjoris 148db758d52Sjoris if (fstat(fd, &st1) == -1) { 149ba617dc1Sxsa saved_errno = errno; 150db758d52Sjoris (void)unlink(fpath); 151ba617dc1Sxsa fatal("cvs_logmsg_create: fstat %s", strerror(saved_errno)); 152db758d52Sjoris } 153db758d52Sjoris 154db758d52Sjoris logmsg = NULL; 155db758d52Sjoris 156db758d52Sjoris for (;;) { 157717c55ebSray if (cvs_logmsg_edit(fpath) == -1) 158db758d52Sjoris break; 159db758d52Sjoris 160db758d52Sjoris if (fstat(fd, &st2) == -1) { 161ba617dc1Sxsa saved_errno = errno; 162db758d52Sjoris (void)unlink(fpath); 163ba617dc1Sxsa fatal("cvs_logmsg_create: fstat %s", 164ba617dc1Sxsa strerror(saved_errno)); 165db758d52Sjoris } 166db758d52Sjoris 167db758d52Sjoris if (st1.st_mtime != st2.st_mtime) { 168db758d52Sjoris logmsg = cvs_logmsg_read(fpath); 169db758d52Sjoris break; 170db758d52Sjoris } 171db758d52Sjoris 172db758d52Sjoris printf("\nLog message unchanged or not specified\n" 173e93f9b40Sjasper "a)bort, c)ontinue, e)dit\nAction: (continue) "); 174db758d52Sjoris (void)fflush(stdout); 175db758d52Sjoris 176db758d52Sjoris c = getc(stdin); 1777a2038a8Stobias if (c == EOF || c == 'a') { 178db758d52Sjoris fatal("Aborted by user"); 179db758d52Sjoris } else if (c == '\n' || c == 'c') { 180db758d52Sjoris logmsg = xstrdup(""); 181db758d52Sjoris break; 182db758d52Sjoris } else if (c == 'e') { 183db758d52Sjoris continue; 184db758d52Sjoris } else { 185db758d52Sjoris cvs_log(LP_ERR, "invalid input"); 186db758d52Sjoris continue; 187db758d52Sjoris } 188db758d52Sjoris } 189db758d52Sjoris 190db758d52Sjoris (void)fclose(fp); 191db758d52Sjoris (void)unlink(fpath); 192db758d52Sjoris xfree(fpath); 193db758d52Sjoris 194db758d52Sjoris return (logmsg); 195db758d52Sjoris } 19666510bb6Sxsa 197717c55ebSray /* 198717c55ebSray * Execute an editor on the specified pathname, which is interpreted 199717c55ebSray * from the shell. This means flags may be included. 200717c55ebSray * 201717c55ebSray * Returns -1 on error, or the exit value on success. 202717c55ebSray */ 20366510bb6Sxsa int 20466510bb6Sxsa cvs_logmsg_edit(const char *pathname) 20566510bb6Sxsa { 20666510bb6Sxsa char *argp[] = {"sh", "-c", NULL, NULL}, *p; 20766510bb6Sxsa sig_t sighup, sigint, sigquit; 20866510bb6Sxsa pid_t pid; 20906381711Sray int saved_errno, st; 21066510bb6Sxsa 21166510bb6Sxsa (void)xasprintf(&p, "%s %s", cvs_editor, pathname); 21266510bb6Sxsa argp[2] = p; 21366510bb6Sxsa 21466510bb6Sxsa sighup = signal(SIGHUP, SIG_IGN); 21566510bb6Sxsa sigint = signal(SIGINT, SIG_IGN); 21666510bb6Sxsa sigquit = signal(SIGQUIT, SIG_IGN); 21706381711Sray if ((pid = fork()) == -1) 21806381711Sray goto fail; 21966510bb6Sxsa if (pid == 0) { 22066510bb6Sxsa execv(_PATH_BSHELL, argp); 22166510bb6Sxsa _exit(127); 22266510bb6Sxsa } 22306381711Sray while (waitpid(pid, &st, 0) == -1) 22466510bb6Sxsa if (errno != EINTR) 22506381711Sray goto fail; 22606381711Sray xfree(p); 22766510bb6Sxsa (void)signal(SIGHUP, sighup); 22866510bb6Sxsa (void)signal(SIGINT, sigint); 22966510bb6Sxsa (void)signal(SIGQUIT, sigquit); 23006381711Sray if (!WIFEXITED(st)) { 23106381711Sray errno = EINTR; 23266510bb6Sxsa return (-1); 23366510bb6Sxsa } 23406381711Sray return (WEXITSTATUS(st)); 23506381711Sray 23606381711Sray fail: 23706381711Sray saved_errno = errno; 23806381711Sray (void)signal(SIGHUP, sighup); 23906381711Sray (void)signal(SIGINT, sigint); 24006381711Sray (void)signal(SIGQUIT, sigquit); 24106381711Sray xfree(p); 24206381711Sray errno = saved_errno; 24306381711Sray return (-1); 24466510bb6Sxsa } 245