[BACK]Return to history.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / cvs

File: [local] / src / usr.bin / cvs / history.c (download)

Revision 1.42, Fri Jan 16 06:40:07 2015 UTC (9 years, 4 months ago) by deraadt
Branch: MAIN
CVS Tags: OPENBSD_5_8_BASE, OPENBSD_5_8, OPENBSD_5_7_BASE, OPENBSD_5_7
Changes since 1.41: +2 -2 lines

Replace <sys/param.h> with <limits.h> and other less dirty headers where
possible.  Annotate <sys/param.h> lines with their current reasons.  Switch
to PATH_MAX, NGROUPS_MAX, HOST_NAME_MAX+1, LOGIN_NAME_MAX, etc.  Change
MIN() and MAX() to local definitions of MINIMUM() and MAXIMUM() where
sensible to avoid pulling in the pollution.  These are the files confirmed
through binary verification.
ok guenther, millert, doug (helped with the verification protocol)

/*	$OpenBSD: history.c,v 1.42 2015/01/16 06:40:07 deraadt Exp $	*/
/*
 * Copyright (c) 2007 Joris Vink <joris@openbsd.org>
 *
 * 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 <sys/stat.h>

#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <pwd.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "cvs.h"
#include "remote.h"

void	cvs_history_local(struct cvs_file *);

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 = "<remote>";
	} 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));
		rcsnum_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)
		xfree(rev);
	if (cvs_server_active != 1)
		xfree(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);
}