Annotation of src/usr.bin/cvs/history.c, Revision 1.45
1.45 ! okan 1: /* $OpenBSD: history.c,v 1.44 2016/10/13 20:51:25 fcambus Exp $ */
1.1 jfb 2: /*
1.28 joris 3: * Copyright (c) 2007 Joris Vink <joris@openbsd.org>
1.1 jfb 4: *
1.28 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.
1.1 jfb 8: *
1.28 joris 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.28 joris 18: #include <sys/stat.h>
19:
20: #include <ctype.h>
21: #include <errno.h>
1.39 tobias 22: #include <fcntl.h>
1.28 joris 23: #include <pwd.h>
24: #include <stdlib.h>
25: #include <string.h>
1.45 ! okan 26: #include <time.h>
1.28 joris 27: #include <unistd.h>
1.1 jfb 28:
29: #include "cvs.h"
1.28 joris 30: #include "remote.h"
1.1 jfb 31:
1.28 joris 32: void cvs_history_local(struct cvs_file *);
1.1 jfb 33:
1.38 tobias 34: static void history_compress(char *, const char *);
35:
1.28 joris 36: struct cvs_cmd cvs_cmd_history = {
1.35 tobias 37: CVS_OP_HISTORY, CVS_USE_WDIR, "history",
1.29 xsa 38: { "hi", "his" }, /* omghi2you */
1.34 tobias 39: "Display history of actions done in the base repository",
1.28 joris 40: "[-ac]",
41: "ac",
1.14 jfb 42: NULL,
1.28 joris 43: cvs_history
44: };
45:
46: /* keep in sync with the defines for history stuff in cvs.h */
47: const char historytab[] = {
48: 'T',
49: 'O',
50: 'E',
51: 'F',
52: 'W',
53: 'U',
54: 'G',
55: 'C',
56: 'M',
57: 'A',
58: 'R',
1.33 ray 59: '\0'
1.9 joris 60: };
61:
1.28 joris 62: #define HISTORY_ALL_USERS 0x01
63: #define HISTORY_DISPLAY_ARCHIVED 0x02
1.1 jfb 64:
1.28 joris 65: void
66: cvs_history_add(int type, struct cvs_file *cf, const char *argument)
1.1 jfb 67: {
1.37 tobias 68: BUF *buf;
1.28 joris 69: FILE *fp;
1.38 tobias 70: RCSNUM *hrev;
71: size_t len;
1.39 tobias 72: int fd;
1.38 tobias 73: char *cwd, *p, *rev;
1.42 deraadt 74: char revbuf[CVS_REV_BUFSZ], repo[PATH_MAX], fpath[PATH_MAX];
1.37 tobias 75: char timebuf[CVS_TIME_BUFSZ];
76: struct tm datetm;
1.1 jfb 77:
1.28 joris 78: if (cvs_nolog == 1)
79: return;
1.19 xsa 80:
1.28 joris 81: if (cvs_cmdop == CVS_OP_CHECKOUT || cvs_cmdop == CVS_OP_EXPORT) {
82: if (type != CVS_HISTORY_CHECKOUT &&
83: type != CVS_HISTORY_EXPORT)
84: return;
1.1 jfb 85: }
86:
1.28 joris 87: cvs_log(LP_TRACE, "cvs_history_add(`%c', `%s', `%s')",
88: historytab[type], (cf != NULL) ? cf->file_name : "", argument);
1.1 jfb 89:
1.38 tobias 90: /* construct repository field */
91: if (cvs_cmdop != CVS_OP_CHECKOUT && cvs_cmdop != CVS_OP_EXPORT) {
92: cvs_get_repository_name((cf != NULL) ? cf->file_wd : ".",
93: repo, sizeof(repo));
94: } else {
95: cvs_get_repository_name(argument, repo, sizeof(repo));
96: }
97:
1.30 joris 98: if (cvs_server_active == 1) {
99: cwd = "<remote>";
100: } else {
1.38 tobias 101: if (getcwd(fpath, sizeof(fpath)) == NULL)
1.30 joris 102: fatal("cvs_history_add: getcwd: %s", strerror(errno));
1.38 tobias 103: p = fpath;
104: if (cvs_cmdop == CVS_OP_CHECKOUT ||
105: cvs_cmdop == CVS_OP_EXPORT) {
106: if (strlcat(fpath, "/", sizeof(fpath)) >=
107: sizeof(fpath) || strlcat(fpath, argument,
108: sizeof(fpath)) >= sizeof(fpath))
109: fatal("cvs_history_add: string truncation");
110: }
111: if (cvs_homedir != NULL && cvs_homedir[0] != '\0') {
112: len = strlen(cvs_homedir);
113: if (strncmp(cvs_homedir, fpath, len) == 0 &&
114: fpath[len] == '/') {
115: p += len - 1;
116: *p = '~';
117: }
118: }
1.1 jfb 119:
1.38 tobias 120: history_compress(p, repo);
121: cwd = xstrdup(p);
1.28 joris 122: }
1.24 joris 123:
1.28 joris 124: /* construct revision field */
125: revbuf[0] = '\0';
1.37 tobias 126: rev = revbuf;
127: switch (type) {
128: case CVS_HISTORY_TAG:
129: strlcpy(revbuf, argument, sizeof(revbuf));
130: break;
131: case CVS_HISTORY_CHECKOUT:
132: case CVS_HISTORY_EXPORT:
133: /*
1.40 ray 134: * buf_alloc uses xcalloc(), so we are safe even
1.37 tobias 135: * if neither cvs_specified_tag nor cvs_specified_date
136: * have been supplied.
137: */
1.40 ray 138: buf = buf_alloc(128);
1.37 tobias 139: if (cvs_specified_tag != NULL) {
1.40 ray 140: buf_puts(buf, cvs_specified_tag);
1.37 tobias 141: if (cvs_specified_date != -1)
1.40 ray 142: buf_putc(buf, ':');
1.37 tobias 143: }
144: if (cvs_specified_date != -1) {
145: gmtime_r(&cvs_specified_date, &datetm);
146: strftime(timebuf, sizeof(timebuf),
147: "%Y.%m.%d.%H.%M.%S", &datetm);
1.40 ray 148: buf_puts(buf, timebuf);
1.15 xsa 149: }
1.40 ray 150: rev = buf_release(buf);
1.37 tobias 151: break;
152: case CVS_HISTORY_UPDATE_MERGED:
153: case CVS_HISTORY_UPDATE_MERGED_ERR:
154: case CVS_HISTORY_COMMIT_MODIFIED:
155: case CVS_HISTORY_COMMIT_ADDED:
156: case CVS_HISTORY_COMMIT_REMOVED:
157: case CVS_HISTORY_UPDATE_CO:
1.38 tobias 158: if ((hrev = rcs_head_get(cf->file_rcs)) == NULL)
159: fatal("cvs_history_add: rcs_head_get failed");
160: rcsnum_tostr(hrev, revbuf, sizeof(revbuf));
1.44 fcambus 161: free(hrev);
1.37 tobias 162: break;
1.28 joris 163: }
1.20 joris 164:
1.28 joris 165: (void)xsnprintf(fpath, sizeof(fpath), "%s/%s",
166: current_cvsroot->cr_dir, CVS_PATH_HISTORY);
1.20 joris 167:
1.39 tobias 168: if ((fd = open(fpath, O_WRONLY|O_APPEND)) == -1) {
169: if (errno != ENOENT)
170: cvs_log(LP_ERR, "failed to open history file");
1.28 joris 171: } else {
1.39 tobias 172: if ((fp = fdopen(fd, "a")) != NULL) {
1.41 zhuk 173: fprintf(fp, "%c%08llx|%s|%s|%s|%s|%s\n",
174: historytab[type], (long long)time(NULL),
175: getlogin(), cwd, repo, rev,
176: (cf != NULL) ? cf->file_name : argument);
1.39 tobias 177: (void)fclose(fp);
178: } else {
179: cvs_log(LP_ERR, "failed to add entry to history file");
180: (void)close(fd);
181: }
1.15 xsa 182: }
1.1 jfb 183:
1.37 tobias 184: if (rev != revbuf)
1.43 nicm 185: free(rev);
1.30 joris 186: if (cvs_server_active != 1)
1.43 nicm 187: free(cwd);
1.38 tobias 188: }
189:
190: static void
191: history_compress(char *wdir, const char *repo)
192: {
193: char *p;
194: const char *q;
195: size_t repo_len, wdir_len;
196:
197: repo_len = strlen(repo);
198: wdir_len = strlen(wdir);
199:
200: p = wdir + wdir_len;
201: q = repo + repo_len;
202:
203: while (p >= wdir && q >= repo) {
204: if (*p != *q)
205: break;
206: p--;
207: q--;
208: }
209: p++;
210: q++;
211:
212: /* if it's not worth the effort, skip compression */
213: if (repo + repo_len - q < 3)
214: return;
215:
216: (void)xsnprintf(p, strlen(p) + 1, "*%zx", q - repo);
1.1 jfb 217: }
218:
1.28 joris 219: int
220: cvs_history(int argc, char **argv)
221: {
222: int ch, flags;
1.1 jfb 223:
1.28 joris 224: flags = 0;
1.1 jfb 225:
1.28 joris 226: while ((ch = getopt(argc, argv, cvs_cmd_history.cmd_opts)) != -1) {
227: switch (ch) {
228: case 'a':
229: flags |= HISTORY_ALL_USERS;
230: break;
231: case 'c':
232: flags |= HISTORY_DISPLAY_ARCHIVED;
233: break;
234: default:
235: fatal("%s", cvs_cmd_history.cmd_synopsis);
236: }
1.1 jfb 237: }
238:
1.28 joris 239: argc -= optind;
240: argv += optind;
241:
242: return (0);
1.1 jfb 243: }