GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/cvs/history.c Lines: 72 98 73.5 %
Date: 2017-11-07 Branches: 42 73 57.5 %

Line Branch Exec Source
1
/*	$OpenBSD: history.c,v 1.45 2017/07/20 13:39:11 okan Exp $	*/
2
/*
3
 * Copyright (c) 2007 Joris Vink <joris@openbsd.org>
4
 *
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.
16
 */
17
18
#include <sys/stat.h>
19
20
#include <ctype.h>
21
#include <errno.h>
22
#include <fcntl.h>
23
#include <pwd.h>
24
#include <stdlib.h>
25
#include <string.h>
26
#include <time.h>
27
#include <unistd.h>
28
29
#include "cvs.h"
30
#include "remote.h"
31
32
void	cvs_history_local(struct cvs_file *);
33
34
static void	history_compress(char *, const char *);
35
36
struct cvs_cmd		cvs_cmd_history = {
37
	CVS_OP_HISTORY, CVS_USE_WDIR, "history",
38
	{ "hi", "his" },			/* omghi2you */
39
	"Display history of actions done in the base repository",
40
	"[-ac]",
41
	"ac",
42
	NULL,
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',
59
	'\0'
60
};
61
62
#define HISTORY_ALL_USERS		0x01
63
#define HISTORY_DISPLAY_ARCHIVED	0x02
64
65
void
66
cvs_history_add(int type, struct cvs_file *cf, const char *argument)
67
{
68
	BUF *buf;
69
	FILE *fp;
70
	RCSNUM *hrev;
71
	size_t len;
72
	int fd;
73
	char *cwd, *p, *rev;
74
116
	char revbuf[CVS_REV_BUFSZ], repo[PATH_MAX], fpath[PATH_MAX];
75
58
	char timebuf[CVS_TIME_BUFSZ];
76
58
	struct tm datetm;
77
78
58
	if (cvs_nolog == 1)
79
		return;
80
81
58
	if (cvs_cmdop == CVS_OP_CHECKOUT || cvs_cmdop == CVS_OP_EXPORT) {
82
68
		if (type != CVS_HISTORY_CHECKOUT &&
83
34
		    type != CVS_HISTORY_EXPORT)
84
22
			return;
85
	}
86
87
36
	cvs_log(LP_TRACE, "cvs_history_add(`%c', `%s', `%s')",
88
96
	    historytab[type], (cf != NULL) ? cf->file_name : "", argument);
89
90
	/* construct repository field */
91
36
	if (cvs_cmdop != CVS_OP_CHECKOUT && cvs_cmdop != CVS_OP_EXPORT) {
92
96
		cvs_get_repository_name((cf != NULL) ? cf->file_wd : ".",
93
24
		    repo, sizeof(repo));
94
24
	} else {
95
12
		cvs_get_repository_name(argument, repo, sizeof(repo));
96
	}
97
98
36
	if (cvs_server_active == 1) {
99
		cwd = "<remote>";
100
	} else {
101
36
		if (getcwd(fpath, sizeof(fpath)) == NULL)
102
			fatal("cvs_history_add: getcwd: %s", strerror(errno));
103
		p = fpath;
104
72
		if (cvs_cmdop == CVS_OP_CHECKOUT ||
105
36
		    cvs_cmdop == CVS_OP_EXPORT) {
106
24
			if (strlcat(fpath, "/", sizeof(fpath)) >=
107
24
			    sizeof(fpath) || strlcat(fpath, argument,
108
12
			    sizeof(fpath)) >= sizeof(fpath))
109
				fatal("cvs_history_add: string truncation");
110
		}
111

72
		if (cvs_homedir != NULL && cvs_homedir[0] != '\0') {
112
36
			len = strlen(cvs_homedir);
113

36
			if (strncmp(cvs_homedir, fpath, len) == 0 &&
114
			    fpath[len] == '/') {
115
				p += len - 1;
116
				*p = '~';
117
			}
118
		}
119
120
36
		history_compress(p, repo);
121
36
		cwd = xstrdup(p);
122
	}
123
124
	/* construct revision field */
125
72
	revbuf[0] = '\0';
126
72
	rev = revbuf;
127


72
	switch (type) {
128
	case CVS_HISTORY_TAG:
129
7
		strlcpy(revbuf, argument, sizeof(revbuf));
130
7
		break;
131
	case CVS_HISTORY_CHECKOUT:
132
	case CVS_HISTORY_EXPORT:
133
		/*
134
		 * buf_alloc uses xcalloc(), so we are safe even
135
		 * if neither cvs_specified_tag nor cvs_specified_date
136
		 * have been supplied.
137
		 */
138
12
		buf = buf_alloc(128);
139
12
		if (cvs_specified_tag != NULL) {
140
7
			buf_puts(buf, cvs_specified_tag);
141
7
			if (cvs_specified_date != -1)
142
				buf_putc(buf, ':');
143
		}
144
12
		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);
148
			buf_puts(buf, timebuf);
149
		}
150
12
		rev = buf_release(buf);
151
12
		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:
158
17
		if ((hrev = rcs_head_get(cf->file_rcs)) == NULL)
159
			fatal("cvs_history_add: rcs_head_get failed");
160
17
		rcsnum_tostr(hrev, revbuf, sizeof(revbuf));
161
17
		free(hrev);
162
17
		break;
163
	}
164
165
72
	(void)xsnprintf(fpath, sizeof(fpath), "%s/%s",
166
36
	    current_cvsroot->cr_dir, CVS_PATH_HISTORY);
167
168
36
	if ((fd = open(fpath, O_WRONLY|O_APPEND)) == -1) {
169
		if (errno != ENOENT)
170
			cvs_log(LP_ERR, "failed to open history file");
171
	} else {
172
36
		if ((fp = fdopen(fd, "a")) != NULL) {
173
36
			fprintf(fp, "%c%08llx|%s|%s|%s|%s|%s\n",
174
36
			    historytab[type], (long long)time(NULL),
175
36
			    getlogin(), cwd, repo, rev,
176
108
			    (cf != NULL) ? cf->file_name : argument);
177
36
			(void)fclose(fp);
178
36
		} else {
179
			cvs_log(LP_ERR, "failed to add entry to history file");
180
			(void)close(fd);
181
		}
182
	}
183
184
36
	if (rev != revbuf)
185
12
		free(rev);
186
36
	if (cvs_server_active != 1)
187
36
		free(cwd);
188
94
}
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
72
	repo_len = strlen(repo);
198
36
	wdir_len = strlen(wdir);
199
200
36
	p = wdir + wdir_len;
201
36
	q = repo + repo_len;
202
203

528
	while (p >= wdir && q >= repo) {
204
150
		if (*p != *q)
205
			break;
206
140
		p--;
207
140
		q--;
208
	}
209
36
	p++;
210
36
	q++;
211
212
	/* if it's not worth the effort, skip compression */
213
36
	if (repo + repo_len - q < 3)
214
10
		return;
215
216
26
	(void)xsnprintf(p, strlen(p) + 1, "*%zx", q - repo);
217
62
}
218
219
int
220
cvs_history(int argc, char **argv)
221
{
222
	int ch, flags;
223
224
	flags = 0;
225
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
		}
237
	}
238
239
	argc -= optind;
240
	argv += optind;
241
242
	return (0);
243
}