/* $OpenBSD: history.c,v 1.46 2024/05/21 05:00:48 jsg Exp $ */ /* * Copyright (c) 2007 Joris Vink * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include "cvs.h" #include "remote.h" static void history_compress(char *, const char *); struct cvs_cmd cvs_cmd_history = { CVS_OP_HISTORY, CVS_USE_WDIR, "history", { "hi", "his" }, /* omghi2you */ "Display history of actions done in the base repository", "[-ac]", "ac", NULL, cvs_history }; /* keep in sync with the defines for history stuff in cvs.h */ const char historytab[] = { 'T', 'O', 'E', 'F', 'W', 'U', 'G', 'C', 'M', 'A', 'R', '\0' }; #define HISTORY_ALL_USERS 0x01 #define HISTORY_DISPLAY_ARCHIVED 0x02 void cvs_history_add(int type, struct cvs_file *cf, const char *argument) { BUF *buf; FILE *fp; RCSNUM *hrev; size_t len; int fd; char *cwd, *p, *rev; char revbuf[CVS_REV_BUFSZ], repo[PATH_MAX], fpath[PATH_MAX]; char timebuf[CVS_TIME_BUFSZ]; struct tm datetm; if (cvs_nolog == 1) return; if (cvs_cmdop == CVS_OP_CHECKOUT || cvs_cmdop == CVS_OP_EXPORT) { if (type != CVS_HISTORY_CHECKOUT && type != CVS_HISTORY_EXPORT) return; } cvs_log(LP_TRACE, "cvs_history_add(`%c', `%s', `%s')", historytab[type], (cf != NULL) ? cf->file_name : "", argument); /* construct repository field */ if (cvs_cmdop != CVS_OP_CHECKOUT && cvs_cmdop != CVS_OP_EXPORT) { cvs_get_repository_name((cf != NULL) ? cf->file_wd : ".", repo, sizeof(repo)); } else { cvs_get_repository_name(argument, repo, sizeof(repo)); } if (cvs_server_active == 1) { cwd = ""; } else { if (getcwd(fpath, sizeof(fpath)) == NULL) fatal("cvs_history_add: getcwd: %s", strerror(errno)); p = fpath; if (cvs_cmdop == CVS_OP_CHECKOUT || cvs_cmdop == CVS_OP_EXPORT) { if (strlcat(fpath, "/", sizeof(fpath)) >= sizeof(fpath) || strlcat(fpath, argument, sizeof(fpath)) >= sizeof(fpath)) fatal("cvs_history_add: string truncation"); } if (cvs_homedir != NULL && cvs_homedir[0] != '\0') { len = strlen(cvs_homedir); if (strncmp(cvs_homedir, fpath, len) == 0 && fpath[len] == '/') { p += len - 1; *p = '~'; } } history_compress(p, repo); cwd = xstrdup(p); } /* construct revision field */ revbuf[0] = '\0'; rev = revbuf; switch (type) { case CVS_HISTORY_TAG: strlcpy(revbuf, argument, sizeof(revbuf)); break; case CVS_HISTORY_CHECKOUT: case CVS_HISTORY_EXPORT: /* * buf_alloc uses xcalloc(), so we are safe even * if neither cvs_specified_tag nor cvs_specified_date * have been supplied. */ buf = buf_alloc(128); if (cvs_specified_tag != NULL) { buf_puts(buf, cvs_specified_tag); if (cvs_specified_date != -1) buf_putc(buf, ':'); } if (cvs_specified_date != -1) { gmtime_r(&cvs_specified_date, &datetm); strftime(timebuf, sizeof(timebuf), "%Y.%m.%d.%H.%M.%S", &datetm); buf_puts(buf, timebuf); } rev = buf_release(buf); break; case CVS_HISTORY_UPDATE_MERGED: case CVS_HISTORY_UPDATE_MERGED_ERR: case CVS_HISTORY_COMMIT_MODIFIED: case CVS_HISTORY_COMMIT_ADDED: case CVS_HISTORY_COMMIT_REMOVED: case CVS_HISTORY_UPDATE_CO: if ((hrev = rcs_head_get(cf->file_rcs)) == NULL) fatal("cvs_history_add: rcs_head_get failed"); rcsnum_tostr(hrev, revbuf, sizeof(revbuf)); free(hrev); break; } (void)xsnprintf(fpath, sizeof(fpath), "%s/%s", current_cvsroot->cr_dir, CVS_PATH_HISTORY); if ((fd = open(fpath, O_WRONLY|O_APPEND)) == -1) { if (errno != ENOENT) cvs_log(LP_ERR, "failed to open history file"); } else { if ((fp = fdopen(fd, "a")) != NULL) { fprintf(fp, "%c%08llx|%s|%s|%s|%s|%s\n", historytab[type], (long long)time(NULL), getlogin(), cwd, repo, rev, (cf != NULL) ? cf->file_name : argument); (void)fclose(fp); } else { cvs_log(LP_ERR, "failed to add entry to history file"); (void)close(fd); } } if (rev != revbuf) free(rev); if (cvs_server_active != 1) free(cwd); } static void history_compress(char *wdir, const char *repo) { char *p; const char *q; size_t repo_len, wdir_len; repo_len = strlen(repo); wdir_len = strlen(wdir); p = wdir + wdir_len; q = repo + repo_len; while (p >= wdir && q >= repo) { if (*p != *q) break; p--; q--; } p++; q++; /* if it's not worth the effort, skip compression */ if (repo + repo_len - q < 3) return; (void)xsnprintf(p, strlen(p) + 1, "*%zx", q - repo); } int cvs_history(int argc, char **argv) { int ch, flags; flags = 0; while ((ch = getopt(argc, argv, cvs_cmd_history.cmd_opts)) != -1) { switch (ch) { case 'a': flags |= HISTORY_ALL_USERS; break; case 'c': flags |= HISTORY_DISPLAY_ARCHIVED; break; default: fatal("%s", cvs_cmd_history.cmd_synopsis); } } argc -= optind; argv += optind; return (0); }