[BACK]Return to logmsg.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / cvs

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: