xref: /openbsd-src/usr.bin/cvs/history.c (revision ce7279d89b71439c96c854f612f4ac93a461fdc4)
1*ce7279d8Sjsg /*	$OpenBSD: history.c,v 1.46 2024/05/21 05:00:48 jsg Exp $	*/
23901dfa5Sjoris /*
33901dfa5Sjoris  * Copyright (c) 2007 Joris Vink <joris@openbsd.org>
43901dfa5Sjoris  *
53901dfa5Sjoris  * Permission to use, copy, modify, and distribute this software for any
63901dfa5Sjoris  * purpose with or without fee is hereby granted, provided that the above
73901dfa5Sjoris  * copyright notice and this permission notice appear in all copies.
83901dfa5Sjoris  *
93901dfa5Sjoris  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
103901dfa5Sjoris  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
113901dfa5Sjoris  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
123901dfa5Sjoris  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
133901dfa5Sjoris  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
143901dfa5Sjoris  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
153901dfa5Sjoris  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
163901dfa5Sjoris  */
173901dfa5Sjoris 
183901dfa5Sjoris #include <sys/stat.h>
193901dfa5Sjoris 
203901dfa5Sjoris #include <ctype.h>
213901dfa5Sjoris #include <errno.h>
22b35edee1Stobias #include <fcntl.h>
233901dfa5Sjoris #include <pwd.h>
243901dfa5Sjoris #include <stdlib.h>
253901dfa5Sjoris #include <string.h>
2628fb0957Sokan #include <time.h>
273901dfa5Sjoris #include <unistd.h>
283901dfa5Sjoris 
293901dfa5Sjoris #include "cvs.h"
303901dfa5Sjoris #include "remote.h"
313901dfa5Sjoris 
32062b8fd7Stobias static void	history_compress(char *, const char *);
33062b8fd7Stobias 
343901dfa5Sjoris struct cvs_cmd		cvs_cmd_history = {
35f331ff59Stobias 	CVS_OP_HISTORY, CVS_USE_WDIR, "history",
36eb5e8157Sxsa 	{ "hi", "his" },			/* omghi2you */
372a04ffe6Stobias 	"Display history of actions done in the base repository",
383901dfa5Sjoris 	"[-ac]",
393901dfa5Sjoris 	"ac",
403901dfa5Sjoris 	NULL,
413901dfa5Sjoris 	cvs_history
423901dfa5Sjoris };
433901dfa5Sjoris 
443901dfa5Sjoris /* keep in sync with the defines for history stuff in cvs.h */
453901dfa5Sjoris const char historytab[] = {
463901dfa5Sjoris 	'T',
473901dfa5Sjoris 	'O',
483901dfa5Sjoris 	'E',
493901dfa5Sjoris 	'F',
503901dfa5Sjoris 	'W',
513901dfa5Sjoris 	'U',
523901dfa5Sjoris 	'G',
533901dfa5Sjoris 	'C',
543901dfa5Sjoris 	'M',
553901dfa5Sjoris 	'A',
563901dfa5Sjoris 	'R',
575a781597Sray 	'\0'
583901dfa5Sjoris };
593901dfa5Sjoris 
603901dfa5Sjoris #define HISTORY_ALL_USERS		0x01
613901dfa5Sjoris #define HISTORY_DISPLAY_ARCHIVED	0x02
623901dfa5Sjoris 
633901dfa5Sjoris void
cvs_history_add(int type,struct cvs_file * cf,const char * argument)643901dfa5Sjoris cvs_history_add(int type, struct cvs_file *cf, const char *argument)
653901dfa5Sjoris {
6628b0a253Stobias 	BUF *buf;
673901dfa5Sjoris 	FILE *fp;
68062b8fd7Stobias 	RCSNUM *hrev;
69062b8fd7Stobias 	size_t len;
70b35edee1Stobias 	int fd;
71062b8fd7Stobias 	char *cwd, *p, *rev;
72b9fc9a72Sderaadt 	char revbuf[CVS_REV_BUFSZ], repo[PATH_MAX], fpath[PATH_MAX];
7328b0a253Stobias 	char timebuf[CVS_TIME_BUFSZ];
7428b0a253Stobias 	struct tm datetm;
753901dfa5Sjoris 
763901dfa5Sjoris 	if (cvs_nolog == 1)
773901dfa5Sjoris 		return;
783901dfa5Sjoris 
793901dfa5Sjoris 	if (cvs_cmdop == CVS_OP_CHECKOUT || cvs_cmdop == CVS_OP_EXPORT) {
803901dfa5Sjoris 		if (type != CVS_HISTORY_CHECKOUT &&
813901dfa5Sjoris 		    type != CVS_HISTORY_EXPORT)
823901dfa5Sjoris 			return;
833901dfa5Sjoris 	}
843901dfa5Sjoris 
853901dfa5Sjoris 	cvs_log(LP_TRACE, "cvs_history_add(`%c', `%s', `%s')",
863901dfa5Sjoris 	    historytab[type], (cf != NULL) ? cf->file_name : "", argument);
873901dfa5Sjoris 
883901dfa5Sjoris 	/* construct repository field */
893901dfa5Sjoris 	if (cvs_cmdop != CVS_OP_CHECKOUT && cvs_cmdop != CVS_OP_EXPORT) {
903c84fc2eStobias 		cvs_get_repository_name((cf != NULL) ? cf->file_wd : ".",
913c84fc2eStobias 		    repo, sizeof(repo));
923901dfa5Sjoris 	} else {
93062b8fd7Stobias 		cvs_get_repository_name(argument, repo, sizeof(repo));
94062b8fd7Stobias 	}
95062b8fd7Stobias 
96062b8fd7Stobias 	if (cvs_server_active == 1) {
97062b8fd7Stobias 		cwd = "<remote>";
98062b8fd7Stobias 	} else {
99062b8fd7Stobias 		if (getcwd(fpath, sizeof(fpath)) == NULL)
100062b8fd7Stobias 			fatal("cvs_history_add: getcwd: %s", strerror(errno));
101062b8fd7Stobias 		p = fpath;
102062b8fd7Stobias 		if (cvs_cmdop == CVS_OP_CHECKOUT ||
103062b8fd7Stobias 		    cvs_cmdop == CVS_OP_EXPORT) {
104062b8fd7Stobias 			if (strlcat(fpath, "/", sizeof(fpath)) >=
105062b8fd7Stobias 			    sizeof(fpath) || strlcat(fpath, argument,
106062b8fd7Stobias 			    sizeof(fpath)) >= sizeof(fpath))
107062b8fd7Stobias 				fatal("cvs_history_add: string truncation");
108062b8fd7Stobias 		}
109062b8fd7Stobias 		if (cvs_homedir != NULL && cvs_homedir[0] != '\0') {
110062b8fd7Stobias 			len = strlen(cvs_homedir);
111062b8fd7Stobias 			if (strncmp(cvs_homedir, fpath, len) == 0 &&
112062b8fd7Stobias 			    fpath[len] == '/') {
113062b8fd7Stobias 				p += len - 1;
114062b8fd7Stobias 				*p = '~';
115062b8fd7Stobias 			}
116062b8fd7Stobias 		}
117062b8fd7Stobias 
118062b8fd7Stobias 		history_compress(p, repo);
119062b8fd7Stobias 		cwd = xstrdup(p);
1203901dfa5Sjoris 	}
1213901dfa5Sjoris 
1223901dfa5Sjoris 	/* construct revision field */
1233901dfa5Sjoris 	revbuf[0] = '\0';
12428b0a253Stobias 	rev = revbuf;
1253901dfa5Sjoris 	switch (type) {
1263901dfa5Sjoris 	case CVS_HISTORY_TAG:
1273901dfa5Sjoris 		strlcpy(revbuf, argument, sizeof(revbuf));
1283901dfa5Sjoris 		break;
1293901dfa5Sjoris 	case CVS_HISTORY_CHECKOUT:
1303901dfa5Sjoris 	case CVS_HISTORY_EXPORT:
13128b0a253Stobias 		/*
1327bb3ddb0Sray 		 * buf_alloc uses xcalloc(), so we are safe even
13328b0a253Stobias 		 * if neither cvs_specified_tag nor cvs_specified_date
13428b0a253Stobias 		 * have been supplied.
13528b0a253Stobias 		 */
1367bb3ddb0Sray 		buf = buf_alloc(128);
13728b0a253Stobias 		if (cvs_specified_tag != NULL) {
1387bb3ddb0Sray 			buf_puts(buf, cvs_specified_tag);
13928b0a253Stobias 			if (cvs_specified_date != -1)
1407bb3ddb0Sray 				buf_putc(buf, ':');
14128b0a253Stobias 		}
14228b0a253Stobias 		if (cvs_specified_date != -1) {
14328b0a253Stobias 			gmtime_r(&cvs_specified_date, &datetm);
14428b0a253Stobias 			strftime(timebuf, sizeof(timebuf),
14528b0a253Stobias 			    "%Y.%m.%d.%H.%M.%S", &datetm);
1467bb3ddb0Sray 			buf_puts(buf, timebuf);
14728b0a253Stobias 		}
1487bb3ddb0Sray 		rev = buf_release(buf);
1493901dfa5Sjoris 		break;
1503901dfa5Sjoris 	case CVS_HISTORY_UPDATE_MERGED:
1513901dfa5Sjoris 	case CVS_HISTORY_UPDATE_MERGED_ERR:
1523901dfa5Sjoris 	case CVS_HISTORY_COMMIT_MODIFIED:
1533901dfa5Sjoris 	case CVS_HISTORY_COMMIT_ADDED:
1543901dfa5Sjoris 	case CVS_HISTORY_COMMIT_REMOVED:
1553901dfa5Sjoris 	case CVS_HISTORY_UPDATE_CO:
156062b8fd7Stobias 		if ((hrev = rcs_head_get(cf->file_rcs)) == NULL)
157062b8fd7Stobias 			fatal("cvs_history_add: rcs_head_get failed");
158062b8fd7Stobias 		rcsnum_tostr(hrev, revbuf, sizeof(revbuf));
15953ce2177Sfcambus 		free(hrev);
1603901dfa5Sjoris 		break;
1613901dfa5Sjoris 	}
1623901dfa5Sjoris 
1633901dfa5Sjoris 	(void)xsnprintf(fpath, sizeof(fpath), "%s/%s",
1643901dfa5Sjoris 	    current_cvsroot->cr_dir, CVS_PATH_HISTORY);
1653901dfa5Sjoris 
166b35edee1Stobias 	if ((fd = open(fpath, O_WRONLY|O_APPEND)) == -1) {
167b35edee1Stobias 		if (errno != ENOENT)
168b35edee1Stobias 			cvs_log(LP_ERR, "failed to open history file");
169b35edee1Stobias 	} else {
170b35edee1Stobias 		if ((fp = fdopen(fd, "a")) != NULL) {
17142354d6eSzhuk 			fprintf(fp, "%c%08llx|%s|%s|%s|%s|%s\n",
17242354d6eSzhuk 			    historytab[type], (long long)time(NULL),
17342354d6eSzhuk 			    getlogin(), cwd, repo, rev,
17442354d6eSzhuk 			    (cf != NULL) ? cf->file_name : argument);
1753901dfa5Sjoris 			(void)fclose(fp);
1763901dfa5Sjoris 		} else {
1773901dfa5Sjoris 			cvs_log(LP_ERR, "failed to add entry to history file");
178b35edee1Stobias 			(void)close(fd);
179b35edee1Stobias 		}
1803901dfa5Sjoris 	}
1813901dfa5Sjoris 
18228b0a253Stobias 	if (rev != revbuf)
183397ddb8aSnicm 		free(rev);
1844f5c3994Sjoris 	if (cvs_server_active != 1)
185397ddb8aSnicm 		free(cwd);
1863901dfa5Sjoris }
1873901dfa5Sjoris 
188062b8fd7Stobias static void
history_compress(char * wdir,const char * repo)189062b8fd7Stobias history_compress(char *wdir, const char *repo)
190062b8fd7Stobias {
191062b8fd7Stobias 	char *p;
192062b8fd7Stobias 	const char *q;
193062b8fd7Stobias 	size_t repo_len, wdir_len;
194062b8fd7Stobias 
195062b8fd7Stobias 	repo_len = strlen(repo);
196062b8fd7Stobias 	wdir_len = strlen(wdir);
197062b8fd7Stobias 
198062b8fd7Stobias 	p = wdir + wdir_len;
199062b8fd7Stobias 	q = repo + repo_len;
200062b8fd7Stobias 
201062b8fd7Stobias 	while (p >= wdir && q >= repo) {
202062b8fd7Stobias 		if (*p != *q)
203062b8fd7Stobias 			break;
204062b8fd7Stobias 		p--;
205062b8fd7Stobias 		q--;
206062b8fd7Stobias 	}
207062b8fd7Stobias 	p++;
208062b8fd7Stobias 	q++;
209062b8fd7Stobias 
210062b8fd7Stobias 	/* if it's not worth the effort, skip compression */
211062b8fd7Stobias 	if (repo + repo_len - q < 3)
212062b8fd7Stobias 		return;
213062b8fd7Stobias 
214062b8fd7Stobias 	(void)xsnprintf(p, strlen(p) + 1, "*%zx", q - repo);
215062b8fd7Stobias }
216062b8fd7Stobias 
2173901dfa5Sjoris int
cvs_history(int argc,char ** argv)2183901dfa5Sjoris cvs_history(int argc, char **argv)
2193901dfa5Sjoris {
2203901dfa5Sjoris 	int ch, flags;
2213901dfa5Sjoris 
2223901dfa5Sjoris 	flags = 0;
2233901dfa5Sjoris 
2243901dfa5Sjoris 	while ((ch = getopt(argc, argv, cvs_cmd_history.cmd_opts)) != -1) {
2253901dfa5Sjoris 		switch (ch) {
2263901dfa5Sjoris 		case 'a':
2273901dfa5Sjoris 			flags |= HISTORY_ALL_USERS;
2283901dfa5Sjoris 			break;
2293901dfa5Sjoris 		case 'c':
2303901dfa5Sjoris 			flags |= HISTORY_DISPLAY_ARCHIVED;
2313901dfa5Sjoris 			break;
2323901dfa5Sjoris 		default:
2333901dfa5Sjoris 			fatal("%s", cvs_cmd_history.cmd_synopsis);
2343901dfa5Sjoris 		}
2353901dfa5Sjoris 	}
2363901dfa5Sjoris 
2373901dfa5Sjoris 	argc -= optind;
2383901dfa5Sjoris 	argv += optind;
2393901dfa5Sjoris 
2403901dfa5Sjoris 	return (0);
2413901dfa5Sjoris }
242