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