xref: /openbsd-src/usr.bin/cvs/file.c (revision fd71e6a329ee2d6a44cd0fb734d7803da8f04d9d)
1*fd71e6a3Sguenther /*	$OpenBSD: file.c,v 1.276 2023/08/11 04:48:14 guenther Exp $	*/
23fa15b46Sjfb /*
33ad3fb45Sjoris  * Copyright (c) 2006 Joris Vink <joris@openbsd.org>
43fa15b46Sjfb  * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
53fa15b46Sjfb  * All rights reserved.
63fa15b46Sjfb  *
73fa15b46Sjfb  * Redistribution and use in source and binary forms, with or without
83fa15b46Sjfb  * modification, are permitted provided that the following conditions
93fa15b46Sjfb  * are met:
103fa15b46Sjfb  *
113fa15b46Sjfb  * 1. Redistributions of source code must retain the above copyright
123fa15b46Sjfb  *    notice, this list of conditions and the following disclaimer.
133fa15b46Sjfb  * 2. The name of the author may not be used to endorse or promote products
143fa15b46Sjfb  *    derived from this software without specific prior written permission.
153fa15b46Sjfb  *
163fa15b46Sjfb  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
173fa15b46Sjfb  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
183fa15b46Sjfb  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
193fa15b46Sjfb  * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
203fa15b46Sjfb  * EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLUDING, BUT NOT LIMITED TO,
213fa15b46Sjfb  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
223fa15b46Sjfb  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
233fa15b46Sjfb  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
243fa15b46Sjfb  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
253fa15b46Sjfb  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
263fa15b46Sjfb  */
273fa15b46Sjfb 
281f8531bdSotto #include <sys/types.h>
2993c88394Sxsa #include <sys/mman.h>
301f8531bdSotto #include <sys/stat.h>
31bed6eeadStobias #include <sys/time.h>
321f8531bdSotto 
3342354d6eSzhuk #include <dirent.h>
341f8531bdSotto #include <errno.h>
351f8531bdSotto #include <fcntl.h>
361f8531bdSotto #include <fnmatch.h>
371f8531bdSotto #include <libgen.h>
381357284aSmillert #include <stdint.h>
39397ddb8aSnicm #include <stdlib.h>
401f8531bdSotto #include <string.h>
411f8531bdSotto #include <unistd.h>
4293c88394Sxsa 
43bb59aeccStobias #include "atomicio.h"
443fa15b46Sjfb #include "cvs.h"
45a1742a04Sjoris #include "remote.h"
463fa15b46Sjfb 
473fa15b46Sjfb #define CVS_IGN_STATIC	0x01	/* pattern is static, no need to glob */
483fa15b46Sjfb 
493fa15b46Sjfb #define CVS_CHAR_ISMETA(c)	((c == '*') || (c == '?') || (c == '['))
503fa15b46Sjfb 
5146bff1c6Stobias extern int print_stdout;
521b5598b0Sjoris extern int build_dirs;
5346bff1c6Stobias 
543fa15b46Sjfb /*
553fa15b46Sjfb  * Standard patterns to ignore.
563fa15b46Sjfb  */
573fa15b46Sjfb static const char *cvs_ign_std[] = {
583fa15b46Sjfb 	".",
593fa15b46Sjfb 	"..",
603fa15b46Sjfb 	"*.o",
61376e3357Sxsa 	"*.a",
623fa15b46Sjfb 	"*.bak",
633fa15b46Sjfb 	"*.orig",
643fa15b46Sjfb 	"*.rej",
65376e3357Sxsa 	"*.old",
663fa15b46Sjfb 	"*.exe",
673fa15b46Sjfb 	"*.depend",
68376e3357Sxsa 	"*.obj",
69376e3357Sxsa 	"*.elc",
70376e3357Sxsa 	"*.ln",
71376e3357Sxsa 	"*.olb",
723fa15b46Sjfb 	"CVS",
733fa15b46Sjfb 	"core",
74a506273fSjoris 	"cvslog*",
753697e292Sjfb 	"*.core",
76013cc5c6Sjfb 	".#*",
77376e3357Sxsa 	"*~",
78376e3357Sxsa 	"_$*",
79376e3357Sxsa 	"*$",
803fa15b46Sjfb };
813fa15b46Sjfb 
8251ef6581Sjoris char *cvs_directory_tag = NULL;
833ad3fb45Sjoris struct ignore_head cvs_ign_pats;
843ad3fb45Sjoris struct ignore_head dir_ign_pats;
857fca5395Sjoris struct ignore_head checkout_ign_pats;
86bd5748ebSjfb 
87f106b389Sjoris RB_GENERATE(cvs_flisthead, cvs_filelist, flist, cvs_filelist_cmp);
88f106b389Sjoris 
893ad3fb45Sjoris void
cvs_file_init(void)903fa15b46Sjfb cvs_file_init(void)
913fa15b46Sjfb {
92c486465dSxsa 	int i;
933fa15b46Sjfb 	FILE *ifp;
94b9fc9a72Sderaadt 	char path[PATH_MAX], buf[MAXNAMLEN];
953fa15b46Sjfb 
963fa15b46Sjfb 	TAILQ_INIT(&cvs_ign_pats);
973ad3fb45Sjoris 	TAILQ_INIT(&dir_ign_pats);
987fca5395Sjoris 	TAILQ_INIT(&checkout_ign_pats);
99bd5748ebSjfb 
1003fa15b46Sjfb 	/* standard patterns to ignore */
101d336d5ebSjfb 	for (i = 0; i < (int)(sizeof(cvs_ign_std)/sizeof(char *)); i++)
1023ad3fb45Sjoris 		cvs_file_ignore(cvs_ign_std[i], &cvs_ign_pats);
1033fa15b46Sjfb 
104c5440612Stobias 	if (cvs_homedir == NULL)
105c5440612Stobias 		return;
106c5440612Stobias 
1073fa15b46Sjfb 	/* read the cvsignore file in the user's home directory, if any */
108b9fc9a72Sderaadt 	(void)xsnprintf(path, PATH_MAX, "%s/.cvsignore", cvs_homedir);
1096842d763Sxsa 
1103fa15b46Sjfb 	ifp = fopen(path, "r");
1113fa15b46Sjfb 	if (ifp == NULL) {
1123fa15b46Sjfb 		if (errno != ENOENT)
113c710bc5aSjfb 			cvs_log(LP_ERRNO,
11458cbd44dSxsa 			    "failed to open user's cvsignore file `%s'", path);
1153917c9bfSderaadt 	} else {
1163ad3fb45Sjoris 		while (fgets(buf, MAXNAMLEN, ifp) != NULL) {
117b625fa02Sgilles 			buf[strcspn(buf, "\n")] = '\0';
118b625fa02Sgilles 			if (buf[0] == '\0')
1193fa15b46Sjfb 				continue;
1203ad3fb45Sjoris 
1213ad3fb45Sjoris 			cvs_file_ignore(buf, &cvs_ign_pats);
1223fa15b46Sjfb 		}
1233ad3fb45Sjoris 
1243fa15b46Sjfb 		(void)fclose(ifp);
1253fa15b46Sjfb 	}
1263fa15b46Sjfb }
1273fa15b46Sjfb 
1283ad3fb45Sjoris void
cvs_file_ignore(const char * pat,struct ignore_head * list)1293ad3fb45Sjoris cvs_file_ignore(const char *pat, struct ignore_head *list)
1303fa15b46Sjfb {
1313fa15b46Sjfb 	char *cp;
1323ad3fb45Sjoris 	size_t len;
1333fa15b46Sjfb 	struct cvs_ignpat *ip;
1343fa15b46Sjfb 
1353b4c5c25Sray 	ip = xmalloc(sizeof(*ip));
1363ad3fb45Sjoris 	len = strlcpy(ip->ip_pat, pat, sizeof(ip->ip_pat));
1373ad3fb45Sjoris 	if (len >= sizeof(ip->ip_pat))
1383ad3fb45Sjoris 		fatal("cvs_file_ignore: truncation of pattern '%s'", pat);
1393fa15b46Sjfb 
1403fa15b46Sjfb 	/* check if we will need globbing for that pattern */
1413fa15b46Sjfb 	ip->ip_flags = CVS_IGN_STATIC;
1423fa15b46Sjfb 	for (cp = ip->ip_pat; *cp != '\0'; cp++) {
1433fa15b46Sjfb 		if (CVS_CHAR_ISMETA(*cp)) {
1443fa15b46Sjfb 			ip->ip_flags &= ~CVS_IGN_STATIC;
1453fa15b46Sjfb 			break;
1463fa15b46Sjfb 		}
1473fa15b46Sjfb 	}
1483fa15b46Sjfb 
1493ad3fb45Sjoris 	TAILQ_INSERT_TAIL(list, ip, ip_list);
1503fa15b46Sjfb }
1513fa15b46Sjfb 
1523fa15b46Sjfb int
cvs_file_chkign(const char * file)15365ff3185Sjfb cvs_file_chkign(const char *file)
1543fa15b46Sjfb {
15566436eeaSjfb 	int flags;
1563fa15b46Sjfb 	struct cvs_ignpat *ip;
1573fa15b46Sjfb 
15866436eeaSjfb 	flags = FNM_PERIOD;
15966436eeaSjfb 	if (cvs_nocase)
16066436eeaSjfb 		flags |= FNM_CASEFOLD;
16166436eeaSjfb 
1623fa15b46Sjfb 	TAILQ_FOREACH(ip, &cvs_ign_pats, ip_list) {
1633fa15b46Sjfb 		if (ip->ip_flags & CVS_IGN_STATIC) {
16466436eeaSjfb 			if (cvs_file_cmpname(file, ip->ip_pat) == 0)
1653fa15b46Sjfb 				return (1);
1663917c9bfSderaadt 		} else if (fnmatch(ip->ip_pat, file, flags) == 0)
1673fa15b46Sjfb 			return (1);
1683fa15b46Sjfb 	}
1693fa15b46Sjfb 
1703ad3fb45Sjoris 	TAILQ_FOREACH(ip, &dir_ign_pats, ip_list) {
1713ad3fb45Sjoris 		if (ip->ip_flags & CVS_IGN_STATIC) {
1723ad3fb45Sjoris 			if (cvs_file_cmpname(file, ip->ip_pat) == 0)
1733ad3fb45Sjoris 				return (1);
1743ad3fb45Sjoris 		} else if (fnmatch(ip->ip_pat, file, flags) == 0)
1753ad3fb45Sjoris 			return (1);
1765805c4b0Sjoris 	}
1775805c4b0Sjoris 
1787fca5395Sjoris 	TAILQ_FOREACH(ip, &checkout_ign_pats, ip_list) {
1797fca5395Sjoris 		if (ip->ip_flags & CVS_IGN_STATIC) {
1807fca5395Sjoris 			if (cvs_file_cmpname(file, ip->ip_pat) == 0)
1817fca5395Sjoris 				return (1);
1827fca5395Sjoris 		} else if (fnmatch(ip->ip_pat, file, flags) == 0)
1837fca5395Sjoris 			return (1);
1847fca5395Sjoris 	}
1857fca5395Sjoris 
1865805c4b0Sjoris 	return (0);
1875805c4b0Sjoris }
1885805c4b0Sjoris 
1893ad3fb45Sjoris void
cvs_file_run(int argc,char ** argv,struct cvs_recursion * cr)1903ad3fb45Sjoris cvs_file_run(int argc, char **argv, struct cvs_recursion *cr)
1915805c4b0Sjoris {
1923ad3fb45Sjoris 	int i;
1933ad3fb45Sjoris 	struct cvs_flisthead fl;
1945805c4b0Sjoris 
195f106b389Sjoris 	RB_INIT(&fl);
1969257d5c0Sjfb 
1974cb553bbSjoris 	for (i = 0; i < argc; i++) {
1984cb553bbSjoris 		STRIP_SLASH(argv[i]);
1994cb553bbSjoris 		cvs_file_get(argv[i], FILE_USER_SUPPLIED, &fl, 0);
2004cb553bbSjoris 	}
2019257d5c0Sjfb 
2023ad3fb45Sjoris 	cvs_file_walklist(&fl, cr);
2033ad3fb45Sjoris 	cvs_file_freelist(&fl);
2046bda8108Sjoris }
2059257d5c0Sjfb 
2063ad3fb45Sjoris struct cvs_filelist *
cvs_file_get(char * name,int flags,struct cvs_flisthead * fl,int type)2074cb553bbSjoris cvs_file_get(char *name, int flags, struct cvs_flisthead *fl, int type)
208afa05271Sjfb {
209f106b389Sjoris 	char *p;
210f106b389Sjoris 	struct cvs_filelist *l, find;
211afa05271Sjfb 
2123ad3fb45Sjoris 	for (p = name; p[0] == '.' && p[1] == '/';)
2133ad3fb45Sjoris 		p += 2;
214afa05271Sjfb 
215f106b389Sjoris 	find.file_path = p;
216f106b389Sjoris 	l = RB_FIND(cvs_flisthead, fl, &find);
217f106b389Sjoris 	if (l != NULL)
2183ad3fb45Sjoris 		return (l);
219afa05271Sjfb 
220cfff592fSderaadt 	l = xmalloc(sizeof(*l));
2213ad3fb45Sjoris 	l->file_path = xstrdup(p);
22298902223Sjoris 	l->flags = flags;
2234cb553bbSjoris 	l->type = type;
2243ad3fb45Sjoris 
225f106b389Sjoris 	RB_INSERT(cvs_flisthead, fl, l);
2263ad3fb45Sjoris 	return (l);
227afa05271Sjfb }
228afa05271Sjfb 
2293ad3fb45Sjoris struct cvs_file *
cvs_file_get_cf(const char * d,const char * f,const char * fpath,int fd,int type,int flags)230a4a7c2faSjoris cvs_file_get_cf(const char *d, const char *f, const char *fpath, int fd,
23198902223Sjoris 	int type, int flags)
2323ad3fb45Sjoris {
233a4a7c2faSjoris 	const char *p;
2343ad3fb45Sjoris 	struct cvs_file *cf;
235afa05271Sjfb 
236a4a7c2faSjoris 	for (p = fpath; p[0] == '.' && p[1] == '/';)
2373ad3fb45Sjoris 		p += 2;
2383ad3fb45Sjoris 
239cfff592fSderaadt 	cf = xcalloc(1, sizeof(*cf));
2403ad3fb45Sjoris 
2413ad3fb45Sjoris 	cf->file_name = xstrdup(f);
2423ad3fb45Sjoris 	cf->file_wd = xstrdup(d);
2433ad3fb45Sjoris 	cf->file_path = xstrdup(p);
2443ad3fb45Sjoris 	cf->fd = fd;
2453ad3fb45Sjoris 	cf->repo_fd = -1;
2463ad3fb45Sjoris 	cf->file_type = type;
24798902223Sjoris 	cf->file_status = 0;
24898902223Sjoris 	cf->file_flags = flags;
249fb3beb6cSjoris 	cf->in_attic = 0;
2503ad3fb45Sjoris 	cf->file_ent = NULL;
2513ad3fb45Sjoris 
2525cf15c45Sjoris 	if (cf->fd != -1)
2535cf15c45Sjoris 		cf->file_flags |= FILE_ON_DISK;
2545cf15c45Sjoris 
2554dcde513Sjoris 	if (cvsroot_is_remote() || cvs_server_active == 1)
25638be6170Sjoris 		cvs_validate_directory(cf->file_path);
25738be6170Sjoris 
25802acf2deSjfb 	return (cf);
259afa05271Sjfb }
260afa05271Sjfb 
2610c1ec078Sjfb void
cvs_file_walklist(struct cvs_flisthead * fl,struct cvs_recursion * cr)2623ad3fb45Sjoris cvs_file_walklist(struct cvs_flisthead *fl, struct cvs_recursion *cr)
2630c1ec078Sjfb {
264c486465dSxsa 	int fd, type;
2653ad3fb45Sjoris 	struct stat st;
2663ad3fb45Sjoris 	struct cvs_file *cf;
2673ad3fb45Sjoris 	struct cvs_filelist *l, *nxt;
268431378d1Snaddy 	char *d, dbuf[PATH_MAX], *f, fbuf[PATH_MAX];
269431378d1Snaddy 	char repo[PATH_MAX], fpath[PATH_MAX];
270bb029937Sjfb 
271f106b389Sjoris 	for (l = RB_MIN(cvs_flisthead, fl); l != NULL; l = nxt) {
2723ad3fb45Sjoris 		if (cvs_quit)
2733ad3fb45Sjoris 			fatal("received signal %d", sig_received);
2745805c4b0Sjoris 
2753ad3fb45Sjoris 		cvs_log(LP_TRACE, "cvs_file_walklist: element '%s'",
2763ad3fb45Sjoris 		    l->file_path);
2773ad3fb45Sjoris 
278431378d1Snaddy 		if (strlcpy(fbuf, l->file_path, sizeof(fbuf)) >= sizeof(fbuf))
279431378d1Snaddy 			fatal("cvs_file_walklist: truncation");
280431378d1Snaddy 		if ((f = basename(fbuf)) == NULL)
2813ad3fb45Sjoris 			fatal("cvs_file_walklist: basename failed");
282431378d1Snaddy 
283431378d1Snaddy 		if (strlcpy(dbuf, l->file_path, sizeof(dbuf)) >= sizeof(dbuf))
284431378d1Snaddy 			fatal("cvs_file_walklist: truncation");
285431378d1Snaddy 		if ((d = dirname(dbuf)) == NULL)
2863ad3fb45Sjoris 			fatal("cvs_file_walklist: dirname failed");
2873ad3fb45Sjoris 
2884cb553bbSjoris 		type = l->type;
2893ad3fb45Sjoris 		if ((fd = open(l->file_path, O_RDONLY)) != -1) {
2904cb553bbSjoris 			if (type == 0) {
2913ad3fb45Sjoris 				if (fstat(fd, &st) == -1) {
2923ad3fb45Sjoris 					cvs_log(LP_ERRNO, "%s", l->file_path);
2933ad3fb45Sjoris 					(void)close(fd);
2943ad3fb45Sjoris 					goto next;
2953ad3fb45Sjoris 				}
2963ad3fb45Sjoris 
2973ad3fb45Sjoris 				if (S_ISDIR(st.st_mode))
2983ad3fb45Sjoris 					type = CVS_DIR;
2993ad3fb45Sjoris 				else if (S_ISREG(st.st_mode))
3003ad3fb45Sjoris 					type = CVS_FILE;
3013ad3fb45Sjoris 				else {
3023ad3fb45Sjoris 					cvs_log(LP_ERR,
3033ad3fb45Sjoris 					    "ignoring bad file type for %s",
3043ad3fb45Sjoris 					    l->file_path);
3053ad3fb45Sjoris 					(void)close(fd);
3063ad3fb45Sjoris 					goto next;
307bb029937Sjfb 				}
3084cb553bbSjoris 			}
3094dcde513Sjoris 		} else if (cvsroot_is_local()) {
31046bff1c6Stobias 			/*
31146bff1c6Stobias 			 * During checkout -p, do not use any locally
31246bff1c6Stobias 			 * available directories.
31346bff1c6Stobias 			 */
314629c41f7Sjoris 			if ((cmdp->cmd_flags & CVS_USE_WDIR) &&
315629c41f7Sjoris 			    (cvs_cmdop != CVS_OP_CHECKOUT || !print_stdout))
3163ad3fb45Sjoris 				if (stat(d, &st) == -1) {
3173ad3fb45Sjoris 					cvs_log(LP_ERRNO, "%s", d);
3183ad3fb45Sjoris 					goto next;
319bb029937Sjfb 				}
3202d8b554aSjoris 
321b9fc9a72Sderaadt 			cvs_get_repository_path(d, repo, PATH_MAX);
322b9fc9a72Sderaadt 			(void)xsnprintf(fpath, PATH_MAX, "%s/%s",
3233ad3fb45Sjoris 			    repo, f);
3243ad3fb45Sjoris 
3253ad3fb45Sjoris 			if ((fd = open(fpath, O_RDONLY)) == -1) {
326b9fc9a72Sderaadt 				strlcat(fpath, RCS_FILE_EXT, PATH_MAX);
3273ad3fb45Sjoris 				fd = open(fpath, O_RDONLY);
3283ad3fb45Sjoris 			}
3293ad3fb45Sjoris 
3304cb553bbSjoris 			if (fd != -1 && type == 0) {
3313ad3fb45Sjoris 				if (fstat(fd, &st) == -1)
3323ad3fb45Sjoris 					fatal("cvs_file_walklist: %s: %s",
3333ad3fb45Sjoris 					     fpath, strerror(errno));
3343ad3fb45Sjoris 
3353ad3fb45Sjoris 				if (S_ISDIR(st.st_mode))
3363ad3fb45Sjoris 					type = CVS_DIR;
3373ad3fb45Sjoris 				else if (S_ISREG(st.st_mode))
3383ad3fb45Sjoris 					type = CVS_FILE;
3393ad3fb45Sjoris 				else {
3403ad3fb45Sjoris 					cvs_log(LP_ERR,
3413ad3fb45Sjoris 					    "ignoring bad file type for %s",
3423ad3fb45Sjoris 					    l->file_path);
3433ad3fb45Sjoris 					(void)close(fd);
3443ad3fb45Sjoris 					goto next;
3453ad3fb45Sjoris 				}
3463ad3fb45Sjoris 
3473ad3fb45Sjoris 				/* this file is not in our working copy yet */
3483ad3fb45Sjoris 				(void)close(fd);
3493ad3fb45Sjoris 				fd = -1;
3504cb553bbSjoris 			} else if (fd != -1) {
3514cb553bbSjoris 				close(fd);
3524cb553bbSjoris 				fd = -1;
3533ad3fb45Sjoris 			}
3543ad3fb45Sjoris 		}
3553ad3fb45Sjoris 
3564cb553bbSjoris 		cf = cvs_file_get_cf(d, f, l->file_path, fd, type, l->flags);
3573ad3fb45Sjoris 		if (cf->file_type == CVS_DIR) {
3583ad3fb45Sjoris 			cvs_file_walkdir(cf, cr);
3593ad3fb45Sjoris 		} else {
36098902223Sjoris 			if (l->flags & FILE_USER_SUPPLIED) {
36151ef6581Sjoris 				cvs_parse_tagfile(cf->file_wd,
36251ef6581Sjoris 				    &cvs_directory_tag, NULL, NULL);
36351ef6581Sjoris 
36451ef6581Sjoris 				if (cvs_directory_tag == NULL &&
36551ef6581Sjoris 				    cvs_specified_tag != NULL)
366832dc9c8Stobias 					cvs_directory_tag =
367832dc9c8Stobias 					    xstrdup(cvs_specified_tag);
368fb3beb6cSjoris 
3694dcde513Sjoris 				if (cvsroot_is_local()) {
370fb3beb6cSjoris 					cvs_get_repository_path(cf->file_wd,
371b9fc9a72Sderaadt 					    repo, PATH_MAX);
372fb3beb6cSjoris 					cvs_repository_lock(repo,
373fb3beb6cSjoris 					    (cmdp->cmd_flags & CVS_LOCK_REPO));
374fb3beb6cSjoris 				}
37551ef6581Sjoris 			}
37651ef6581Sjoris 
377bc5d89feSjoris 			if (cr->fileproc != NULL)
378bc5d89feSjoris 				cr->fileproc(cf);
379fb3beb6cSjoris 
38098902223Sjoris 			if (l->flags & FILE_USER_SUPPLIED) {
381ddb8cb02Sjoris 				if (cvsroot_is_local() &&
382ddb8cb02Sjoris 				    (cmdp->cmd_flags & CVS_LOCK_REPO)) {
383fb3beb6cSjoris 					cvs_repository_unlock(repo);
384ddb8cb02Sjoris 				}
385397ddb8aSnicm 				free(cvs_directory_tag);
386832dc9c8Stobias 				cvs_directory_tag = NULL;
387832dc9c8Stobias 			}
388832dc9c8Stobias 		}
3893ad3fb45Sjoris 
3903ad3fb45Sjoris 		cvs_file_free(cf);
3913ad3fb45Sjoris 
3923ad3fb45Sjoris next:
393f106b389Sjoris 		nxt = RB_NEXT(cvs_flisthead, fl, l);
3943ad3fb45Sjoris 	}
3953ad3fb45Sjoris }
3963ad3fb45Sjoris 
3973ad3fb45Sjoris void
cvs_file_walkdir(struct cvs_file * cf,struct cvs_recursion * cr)3983ad3fb45Sjoris cvs_file_walkdir(struct cvs_file *cf, struct cvs_recursion *cr)
3993ad3fb45Sjoris {
4007f096833Sjoris 	int l, type;
4013ad3fb45Sjoris 	FILE *fp;
4023ad3fb45Sjoris 	int nbytes;
4033ad3fb45Sjoris 	size_t bufsize;
4043ad3fb45Sjoris 	struct stat st;
4053ad3fb45Sjoris 	struct dirent *dp;
4063ad3fb45Sjoris 	struct cvs_ent *ent;
4073ad3fb45Sjoris 	struct cvs_ignpat *ip;
4083ad3fb45Sjoris 	struct cvs_ent_line *line;
4093ad3fb45Sjoris 	struct cvs_flisthead fl, dl;
4103ad3fb45Sjoris 	CVSENTRIES *entlist;
411b9fc9a72Sderaadt 	char *buf, *ebuf, *cp, repo[PATH_MAX], fpath[PATH_MAX];
4123ad3fb45Sjoris 
4133ad3fb45Sjoris 	cvs_log(LP_TRACE, "cvs_file_walkdir(%s)", cf->file_path);
4143ad3fb45Sjoris 
4153ad3fb45Sjoris 	if (cr->enterdir != NULL)
4163ad3fb45Sjoris 		cr->enterdir(cf);
4173ad3fb45Sjoris 
418bc5d89feSjoris 	if (cr->fileproc != NULL)
419bc5d89feSjoris 		cr->fileproc(cf);
4203ad3fb45Sjoris 
4216ac6a1c7Sjoris 	if (cf->file_status == FILE_SKIP)
4226ac6a1c7Sjoris 		return;
4236ac6a1c7Sjoris 
4243ad3fb45Sjoris 	/*
425f331ff59Stobias 	 * If this is a repository-only command, do not touch any
426f331ff59Stobias 	 * locally available directories or try to create them.
427f331ff59Stobias 	 */
428f331ff59Stobias 	if (!(cmdp->cmd_flags & CVS_USE_WDIR)) {
429f106b389Sjoris 		RB_INIT(&fl);
430f106b389Sjoris 		RB_INIT(&dl);
431f331ff59Stobias 		goto walkrepo;
432f331ff59Stobias 	}
433f331ff59Stobias 
434f331ff59Stobias 	/*
43560a7adedSjoris 	 * If we do not have an admin directory inside here, dont bother,
436f331ff59Stobias 	 * unless we are running export or import.
4373ad3fb45Sjoris 	 */
438b9fc9a72Sderaadt 	(void)xsnprintf(fpath, PATH_MAX, "%s/%s", cf->file_path,
4393ad3fb45Sjoris 	    CVS_PATH_CVSDIR);
4403ad3fb45Sjoris 
4416ac6a1c7Sjoris 	l = stat(fpath, &st);
4425e1effbaStobias 	if (cvs_cmdop != CVS_OP_EXPORT && cvs_cmdop != CVS_OP_IMPORT &&
4431dd2d57eSjoris 	    (l == -1 || (l == 0 && !S_ISDIR(st.st_mode)))) {
4443ad3fb45Sjoris 		return;
4453ad3fb45Sjoris 	}
4463ad3fb45Sjoris 
44751ef6581Sjoris 	cvs_parse_tagfile(cf->file_path, &cvs_directory_tag, NULL, NULL);
44851ef6581Sjoris 
4493ad3fb45Sjoris 	/*
4503ad3fb45Sjoris 	 * check for a local .cvsignore file
4513ad3fb45Sjoris 	 */
452b9fc9a72Sderaadt 	(void)xsnprintf(fpath, PATH_MAX, "%s/.cvsignore", cf->file_path);
4533ad3fb45Sjoris 
4543ad3fb45Sjoris 	if ((fp = fopen(fpath, "r")) != NULL) {
455b9fc9a72Sderaadt 		while (fgets(fpath, PATH_MAX, fp) != NULL) {
456b625fa02Sgilles 			fpath[strcspn(fpath, "\n")] = '\0';
457b625fa02Sgilles 			if (fpath[0] == '\0')
4588e3955edSmoritz 				continue;
4593ad3fb45Sjoris 
4603ad3fb45Sjoris 			cvs_file_ignore(fpath, &dir_ign_pats);
4613ad3fb45Sjoris 		}
4623ad3fb45Sjoris 
4633ad3fb45Sjoris 		(void)fclose(fp);
4643ad3fb45Sjoris 	}
4653ad3fb45Sjoris 
4663ad3fb45Sjoris 	if (fstat(cf->fd, &st) == -1)
4673ad3fb45Sjoris 		fatal("cvs_file_walkdir: %s %s", cf->file_path,
4683ad3fb45Sjoris 		    strerror(errno));
4693ad3fb45Sjoris 
470ae886706Smillert 	if ((uintmax_t)st.st_size > SIZE_MAX)
471fcb086e6Stobias 		fatal("cvs_file_walkdir: %s: file size too big", cf->file_name);
472fcb086e6Stobias 
473ae886706Smillert 	bufsize = (st.st_size > st.st_blksize) ? st.st_size : st.st_blksize;
4743ad3fb45Sjoris 
4753ad3fb45Sjoris 	buf = xmalloc(bufsize);
476f106b389Sjoris 	RB_INIT(&fl);
477f106b389Sjoris 	RB_INIT(&dl);
4783ad3fb45Sjoris 
47942354d6eSzhuk 	while ((nbytes = getdents(cf->fd, buf, bufsize)) > 0) {
4803ad3fb45Sjoris 		ebuf = buf + nbytes;
4813ad3fb45Sjoris 		cp = buf;
4823ad3fb45Sjoris 
4833ad3fb45Sjoris 		while (cp < ebuf) {
4843ad3fb45Sjoris 			dp = (struct dirent *)cp;
4853ad3fb45Sjoris 			if (!strcmp(dp->d_name, ".") ||
4863ad3fb45Sjoris 			    !strcmp(dp->d_name, "..") ||
4873ad3fb45Sjoris 			    !strcmp(dp->d_name, CVS_PATH_CVSDIR) ||
488a674b1cbSjoris 			    dp->d_fileno == 0) {
4893ad3fb45Sjoris 				cp += dp->d_reclen;
4903ad3fb45Sjoris 				continue;
4913ad3fb45Sjoris 			}
4923ad3fb45Sjoris 
493fcdcc3e6Sjoris 			(void)xsnprintf(fpath, PATH_MAX, "%s/%s",
494fcdcc3e6Sjoris 			    cf->file_path, dp->d_name);
495fcdcc3e6Sjoris 
496a15fad46Stobias 			if (cvs_file_chkign(dp->d_name) &&
497a15fad46Stobias 			    cvs_cmdop != CVS_OP_RLOG &&
498a15fad46Stobias 			    cvs_cmdop != CVS_OP_RTAG) {
499fcdcc3e6Sjoris 				if (cvs_cmdop == CVS_OP_IMPORT)
500fcdcc3e6Sjoris 					cvs_import_ignored(fpath);
5013ad3fb45Sjoris 				cp += dp->d_reclen;
5023ad3fb45Sjoris 				continue;
5033ad3fb45Sjoris 			}
5043ad3fb45Sjoris 
5053ad3fb45Sjoris 			/*
5067f096833Sjoris 			 * nfs and afs will show d_type as DT_UNKNOWN
5077f096833Sjoris 			 * for files and/or directories so when we encounter
508e5f49488Stodd 			 * this we call lstat() on the path to be sure.
5093ad3fb45Sjoris 			 */
5107f096833Sjoris 			if (dp->d_type == DT_UNKNOWN) {
511e5f49488Stodd 				if (lstat(fpath, &st) == -1)
5127f096833Sjoris 					fatal("'%s': %s", fpath,
5137f096833Sjoris 					    strerror(errno));
5147f096833Sjoris 
5157f096833Sjoris 				switch (st.st_mode & S_IFMT) {
5167f096833Sjoris 				case S_IFDIR:
5177f096833Sjoris 					type = CVS_DIR;
5187f096833Sjoris 					break;
5197f096833Sjoris 				case S_IFREG:
5207f096833Sjoris 					type = CVS_FILE;
5217f096833Sjoris 					break;
5227f096833Sjoris 				default:
523d8b555b1Sray 					type = FILE_SKIP;
524d8b555b1Sray 					break;
5257f096833Sjoris 				}
5267f096833Sjoris 			} else {
5277f096833Sjoris 				switch (dp->d_type) {
5287f096833Sjoris 				case DT_DIR:
5297f096833Sjoris 					type = CVS_DIR;
5307f096833Sjoris 					break;
5317f096833Sjoris 				case DT_REG:
5327f096833Sjoris 					type = CVS_FILE;
5337f096833Sjoris 					break;
5347f096833Sjoris 				default:
535d8b555b1Sray 					type = FILE_SKIP;
536d8b555b1Sray 					break;
5377f096833Sjoris 				}
5387f096833Sjoris 			}
5397f096833Sjoris 
540d8b555b1Sray 			if (type == FILE_SKIP) {
541d8b555b1Sray 				if (verbosity > 1) {
542d8b555b1Sray 					cvs_log(LP_NOTICE, "ignoring `%s'",
543d8b555b1Sray 					    dp->d_name);
544d8b555b1Sray 				}
545d8b555b1Sray 				cp += dp->d_reclen;
546d8b555b1Sray 				continue;
547d8b555b1Sray 			}
548d8b555b1Sray 
5497f096833Sjoris 			switch (type) {
5507f096833Sjoris 			case CVS_DIR:
551449bca81Sniallo 				if (cr->flags & CR_RECURSE_DIRS)
5524cb553bbSjoris 					cvs_file_get(fpath, 0, &dl, CVS_DIR);
5537f096833Sjoris 				break;
5547f096833Sjoris 			case CVS_FILE:
5554cb553bbSjoris 				cvs_file_get(fpath, 0, &fl, CVS_FILE);
5567f096833Sjoris 				break;
5577f096833Sjoris 			default:
5587f096833Sjoris 				fatal("type %d unknown, shouldn't happen",
5597f096833Sjoris 				    type);
5607f096833Sjoris 			}
5613ad3fb45Sjoris 
5623ad3fb45Sjoris 			cp += dp->d_reclen;
5633ad3fb45Sjoris 		}
5643ad3fb45Sjoris 	}
5653ad3fb45Sjoris 
5663ad3fb45Sjoris 	if (nbytes == -1)
5673ad3fb45Sjoris 		fatal("cvs_file_walkdir: %s %s", cf->file_path,
5683ad3fb45Sjoris 		    strerror(errno));
5693ad3fb45Sjoris 
570397ddb8aSnicm 	free(buf);
5713ad3fb45Sjoris 
5723ad3fb45Sjoris 	while ((ip = TAILQ_FIRST(&dir_ign_pats)) != NULL) {
5733ad3fb45Sjoris 		TAILQ_REMOVE(&dir_ign_pats, ip, ip_list);
574397ddb8aSnicm 		free(ip);
5753ad3fb45Sjoris 	}
5763ad3fb45Sjoris 
5773ad3fb45Sjoris 	entlist = cvs_ent_open(cf->file_path);
5783ad3fb45Sjoris 	TAILQ_FOREACH(line, &(entlist->cef_ent), entries_list) {
5793ad3fb45Sjoris 		ent = cvs_ent_parse(line->buf);
5803ad3fb45Sjoris 
581b9fc9a72Sderaadt 		(void)xsnprintf(fpath, PATH_MAX, "%s/%s", cf->file_path,
5823ad3fb45Sjoris 		    ent->ce_name);
5833ad3fb45Sjoris 
5841890abdaSjoris 		if (!(cr->flags & CR_RECURSE_DIRS) &&
5851890abdaSjoris 		    ent->ce_type == CVS_ENT_DIR)
5861890abdaSjoris 			continue;
5873ad3fb45Sjoris 		if (ent->ce_type == CVS_ENT_DIR)
5884cb553bbSjoris 			cvs_file_get(fpath, 0, &dl, CVS_DIR);
5893ad3fb45Sjoris 		else if (ent->ce_type == CVS_ENT_FILE)
5904cb553bbSjoris 			cvs_file_get(fpath, 0, &fl, CVS_FILE);
5913ad3fb45Sjoris 
5923ad3fb45Sjoris 		cvs_ent_free(ent);
5933ad3fb45Sjoris 	}
5943ad3fb45Sjoris 
595f331ff59Stobias walkrepo:
5964dcde513Sjoris 	if (cvsroot_is_local()) {
597b9fc9a72Sderaadt 		cvs_get_repository_path(cf->file_path, repo, PATH_MAX);
598fb3beb6cSjoris 		cvs_repository_lock(repo, (cmdp->cmd_flags & CVS_LOCK_REPO));
599fb3beb6cSjoris 	}
6003ad3fb45Sjoris 
601fb3beb6cSjoris 	if (cr->flags & CR_REPO) {
602f06a2289Stobias 		xsnprintf(fpath, sizeof(fpath), "%s/%s", cf->file_path,
603f06a2289Stobias 		    CVS_PATH_STATICENTRIES);
604f06a2289Stobias 
6051b5f4165Sjoshe 		if (!(cmdp->cmd_flags & CVS_USE_WDIR) ||
6061b5f4165Sjoshe 		    stat(fpath, &st) == -1 || build_dirs == 1)
6071890abdaSjoris 			cvs_repository_getdir(repo, cf->file_path, &fl, &dl,
60883389238Sjoris 			    (cr->flags & CR_RECURSE_DIRS) ?
60983389238Sjoris 			    REPOSITORY_DODIRS : 0);
6101890abdaSjoris 	}
6113ad3fb45Sjoris 
6123ad3fb45Sjoris 	cvs_file_walklist(&fl, cr);
6133ad3fb45Sjoris 	cvs_file_freelist(&fl);
6143ad3fb45Sjoris 
6154dcde513Sjoris 	if (cvsroot_is_local() && (cmdp->cmd_flags & CVS_LOCK_REPO))
6163ad3fb45Sjoris 		cvs_repository_unlock(repo);
6173ad3fb45Sjoris 
618f331ff59Stobias 	if (cvs_directory_tag != NULL && cmdp->cmd_flags & CVS_USE_WDIR) {
6192dec954eStobias 		cvs_write_tagfile(cf->file_path, cvs_directory_tag, NULL);
620397ddb8aSnicm 		free(cvs_directory_tag);
62151ef6581Sjoris 		cvs_directory_tag = NULL;
62251ef6581Sjoris 	}
6233ff43c00Stobias 
6243ff43c00Stobias 	cvs_file_walklist(&dl, cr);
6253ff43c00Stobias 	cvs_file_freelist(&dl);
6263ff43c00Stobias 
6273ff43c00Stobias 	if (cr->leavedir != NULL)
6283ff43c00Stobias 		cr->leavedir(cf);
6293ad3fb45Sjoris }
6303ad3fb45Sjoris 
6313ad3fb45Sjoris void
cvs_file_freelist(struct cvs_flisthead * fl)6323ad3fb45Sjoris cvs_file_freelist(struct cvs_flisthead *fl)
6333ad3fb45Sjoris {
634f106b389Sjoris 	struct cvs_filelist *f, *nxt;
6353ad3fb45Sjoris 
636f106b389Sjoris 	for (f = RB_MIN(cvs_flisthead, fl); f != NULL; f = nxt) {
637f106b389Sjoris 		nxt = RB_NEXT(cvs_flisthead, fl, f);
638f106b389Sjoris 		RB_REMOVE(cvs_flisthead, fl, f);
639397ddb8aSnicm 		free(f->file_path);
640397ddb8aSnicm 		free(f);
6413ad3fb45Sjoris 	}
6423ad3fb45Sjoris }
6433ad3fb45Sjoris 
6443ad3fb45Sjoris void
cvs_file_classify(struct cvs_file * cf,const char * tag)64534911d69Sjoris cvs_file_classify(struct cvs_file *cf, const char *tag)
6463ad3fb45Sjoris {
6473ad3fb45Sjoris 	size_t len;
6483ad3fb45Sjoris 	struct stat st;
649e0488abaSjoris 	BUF *b1, *b2;
65051ef6581Sjoris 	int server_has_file, notag;
651c486465dSxsa 	int rflags, ismodified, rcsdead;
6523ad3fb45Sjoris 	CVSENTRIES *entlist = NULL;
6533ad3fb45Sjoris 	const char *state;
654b9fc9a72Sderaadt 	char repo[PATH_MAX], rcsfile[PATH_MAX];
6553ad3fb45Sjoris 
656a1742a04Sjoris 	cvs_log(LP_TRACE, "cvs_file_classify(%s, %s)", cf->file_path,
657a1742a04Sjoris 	    (tag != NULL) ? tag : "none");
6583ad3fb45Sjoris 
6597390971bStobias 	if (!strcmp(cf->file_path, ".")) {
6603ad3fb45Sjoris 		cf->file_status = FILE_UPTODATE;
6613ad3fb45Sjoris 		return;
6623ad3fb45Sjoris 	}
6633ad3fb45Sjoris 
664b9fc9a72Sderaadt 	cvs_get_repository_path(cf->file_wd, repo, PATH_MAX);
665b9fc9a72Sderaadt 	(void)xsnprintf(rcsfile, PATH_MAX, "%s/%s",
6663ad3fb45Sjoris 	    repo, cf->file_name);
6673ad3fb45Sjoris 
668f331ff59Stobias 	if (cf->file_type == CVS_FILE) {
669b9fc9a72Sderaadt 		len = strlcat(rcsfile, RCS_FILE_EXT, PATH_MAX);
670b9fc9a72Sderaadt 		if (len >= PATH_MAX)
6713ad3fb45Sjoris 			fatal("cvs_file_classify: truncation");
6723ad3fb45Sjoris 	}
6733ad3fb45Sjoris 
6743ad3fb45Sjoris 	cf->file_rpath = xstrdup(rcsfile);
675f331ff59Stobias 
676f331ff59Stobias 	if (cmdp->cmd_flags & CVS_USE_WDIR) {
6771dd2d57eSjoris 		entlist = cvs_ent_open(cf->file_wd);
6783ad3fb45Sjoris 		cf->file_ent = cvs_ent_get(entlist, cf->file_name);
679f331ff59Stobias 	} else
680f331ff59Stobias 		cf->file_ent = NULL;
6813ad3fb45Sjoris 
6821c197d61Sjoris 	if (cf->file_ent != NULL) {
683323cb621Sjoris 		if (cvs_specified_tag == NULL)
6844cb553bbSjoris 			tag = cf->file_ent->ce_tag;
6854cb553bbSjoris 
6864cb553bbSjoris 		if (cf->file_flags & FILE_ON_DISK &&
6874cb553bbSjoris 		    cf->file_ent->ce_type == CVS_ENT_FILE &&
6884cb553bbSjoris 		    cf->file_type == CVS_DIR && tag != NULL) {
6894cb553bbSjoris 			cf->file_status = FILE_SKIP;
6904cb553bbSjoris 			return;
6914cb553bbSjoris 		}
6924cb553bbSjoris 
6934cb553bbSjoris 		if (cf->file_flags & FILE_ON_DISK &&
6944cb553bbSjoris 		    cf->file_ent->ce_type == CVS_ENT_DIR &&
6954cb553bbSjoris 		    cf->file_type == CVS_FILE && tag != NULL) {
6964cb553bbSjoris 			cf->file_status = FILE_SKIP;
6974cb553bbSjoris 			return;
6984cb553bbSjoris 		}
6994cb553bbSjoris 
7007c4661d9Sjoris 		if (cf->file_flags & FILE_INSIDE_ATTIC &&
7017c4661d9Sjoris 		    cf->file_ent->ce_type == CVS_ENT_DIR &&
7027c4661d9Sjoris 		    cf->file_type != CVS_DIR) {
7037c4661d9Sjoris 			cf->file_status = FILE_SKIP;
7047c4661d9Sjoris 			return;
7057c4661d9Sjoris 		}
7067c4661d9Sjoris 
7075cf15c45Sjoris 		if (cf->file_flags & FILE_ON_DISK &&
7085cf15c45Sjoris 		    cf->file_ent->ce_type == CVS_ENT_DIR &&
7091c197d61Sjoris 		    cf->file_type != CVS_DIR)
7100e375b3cSpedro 			fatal("%s is supposed to be a directory, but it is not",
7111c197d61Sjoris 			    cf->file_path);
7125cf15c45Sjoris 		if (cf->file_flags & FILE_ON_DISK &&
7135cf15c45Sjoris 		    cf->file_ent->ce_type == CVS_ENT_FILE &&
7141c197d61Sjoris 		    cf->file_type != CVS_FILE)
7150e375b3cSpedro 			fatal("%s is supposed to be a file, but it is not",
7161c197d61Sjoris 			    cf->file_path);
7171c197d61Sjoris 	}
7181c197d61Sjoris 
7193ad3fb45Sjoris 	if (cf->file_type == CVS_DIR) {
720f331ff59Stobias 		if (!(cmdp->cmd_flags & CVS_USE_WDIR))
721f331ff59Stobias 			cf->file_status = FILE_UPTODATE;
722f331ff59Stobias 		else if (cf->fd == -1 && stat(rcsfile, &st) != -1)
7233ad3fb45Sjoris 			cf->file_status = DIR_CREATE;
724a15fad46Stobias 		else if (cf->file_ent != NULL || cvs_cmdop == CVS_OP_RLOG ||
725a15fad46Stobias 		    cvs_cmdop == CVS_OP_RTAG)
7263ad3fb45Sjoris 			cf->file_status = FILE_UPTODATE;
7271c197d61Sjoris 		else
7281c197d61Sjoris 			cf->file_status = FILE_UNKNOWN;
7291c197d61Sjoris 
7303ad3fb45Sjoris 		return;
7313ad3fb45Sjoris 	}
7323ad3fb45Sjoris 
733e8c16debSjoris 	rflags = RCS_READ;
734e8c16debSjoris 	switch (cvs_cmdop) {
735e8c16debSjoris 	case CVS_OP_COMMIT:
7364cb5121aSjoris 	case CVS_OP_TAG:
7374cb5121aSjoris 	case CVS_OP_RTAG:
738e8c16debSjoris 		rflags = RCS_WRITE;
739e8c16debSjoris 		break;
740fbbb8b44Sjoris 	case CVS_OP_ADMIN:
74108458e59Sjoris 	case CVS_OP_IMPORT:
742e8c16debSjoris 	case CVS_OP_LOG:
743449bca81Sniallo 	case CVS_OP_RLOG:
744e8c16debSjoris 		rflags |= RCS_PARSE_FULLY;
745e8c16debSjoris 		break;
7467cccca8aStobias 	default:
7477cccca8aStobias 		break;
748e8c16debSjoris 	}
749e8c16debSjoris 
7503ad3fb45Sjoris 	cf->repo_fd = open(cf->file_rpath, O_RDONLY);
7513ad3fb45Sjoris 	if (cf->repo_fd != -1) {
7523ad3fb45Sjoris 		cf->file_rcs = rcs_open(cf->file_rpath, cf->repo_fd, rflags);
7533ad3fb45Sjoris 		if (cf->file_rcs == NULL)
754f436b545Sjoris 			fatal("cvs_file_classify: failed to parse RCS");
7555dd120b0Sjoris 	} else {
756b9fc9a72Sderaadt 		(void)xsnprintf(rcsfile, PATH_MAX, "%s/%s/%s%s",
7575dd120b0Sjoris 		     repo, CVS_PATH_ATTIC, cf->file_name, RCS_FILE_EXT);
7585dd120b0Sjoris 
7595dd120b0Sjoris 		cf->repo_fd = open(rcsfile, O_RDONLY);
7605dd120b0Sjoris 		if (cf->repo_fd != -1) {
761397ddb8aSnicm 			free(cf->file_rpath);
7625dd120b0Sjoris 			cf->file_rpath = xstrdup(rcsfile);
7635dd120b0Sjoris 			cf->file_rcs = rcs_open(cf->file_rpath,
7645dd120b0Sjoris 			    cf->repo_fd, rflags);
7655dd120b0Sjoris 			if (cf->file_rcs == NULL)
7665dd120b0Sjoris 				fatal("cvs_file_classify: failed to parse RCS");
7675dd120b0Sjoris 			cf->in_attic = 1;
7685dd120b0Sjoris 		} else {
7695dd120b0Sjoris 			cf->file_rcs = NULL;
7705dd120b0Sjoris 		}
7713ad3fb45Sjoris 	}
7723ad3fb45Sjoris 
77351ef6581Sjoris 	notag = 0;
7742cc1dd04Sjoris 	cf->file_flags |= FILE_HAS_TAG;
7758f2408ecSniallo 	if (tag != NULL && cf->file_rcs != NULL) {
776113cb29fStobias 		if ((cf->file_rcsrev = rcs_translate_tag(tag, cf->file_rcs))
77798c225fbStobias 		    == NULL) {
778113cb29fStobias 			cf->file_rcsrev = rcs_translate_tag(NULL, cf->file_rcs);
7799af0ab72Stobias 			if (cf->file_rcsrev != NULL) {
7809af0ab72Stobias 				notag = 1;
7812cc1dd04Sjoris 				cf->file_flags &= ~FILE_HAS_TAG;
78251ef6581Sjoris 			}
7839af0ab72Stobias 		}
7848f2408ecSniallo 	} else if (cf->file_ent != NULL && cf->file_ent->ce_tag != NULL) {
785921d4408Sjoris 		cf->file_rcsrev = rcsnum_alloc();
786921d4408Sjoris 		rcsnum_cpy(cf->file_ent->ce_rev, cf->file_rcsrev, 0);
787ca2dc546Sniallo 	} else if (cf->file_rcs != NULL) {
788113cb29fStobias 		cf->file_rcsrev = rcs_translate_tag(NULL, cf->file_rcs);
789ca2dc546Sniallo 	} else {
7903daf6609Sjoris 		cf->file_rcsrev = NULL;
791ca2dc546Sniallo 	}
7923daf6609Sjoris 
7933ad3fb45Sjoris 	ismodified = rcsdead = 0;
7945cf15c45Sjoris 	if ((cf->file_flags & FILE_ON_DISK) && cf->file_ent != NULL) {
7953ad3fb45Sjoris 		if (fstat(cf->fd, &st) == -1)
7963ad3fb45Sjoris 			fatal("cvs_file_classify: %s", strerror(errno));
7973ad3fb45Sjoris 
79847e5fe63Sjoris 		if (st.st_mtime != cf->file_ent->ce_mtime)
7993ad3fb45Sjoris 			ismodified = 1;
8003ad3fb45Sjoris 	}
8013ad3fb45Sjoris 
802a1742a04Sjoris 	server_has_file = 0;
803a1742a04Sjoris 	if (cvs_server_active == 1 && cf->file_ent != NULL &&
804a1742a04Sjoris 	    cf->file_ent->ce_mtime == CVS_SERVER_UPTODATE) {
805a1742a04Sjoris 		server_has_file = 1;
806a1742a04Sjoris 		ismodified = 0;
807a1742a04Sjoris 	}
808a1742a04Sjoris 
80983389238Sjoris 	if ((server_has_file == 1) || (cf->fd != -1))
81083389238Sjoris 		cf->file_flags |= FILE_ON_DISK;
81183389238Sjoris 
8125cf15c45Sjoris 	if (ismodified == 1 &&
8135cf15c45Sjoris 	    (cf->file_flags & FILE_ON_DISK) && cf->file_rcs != NULL &&
81425d391d7Sjoris 	    cf->file_ent != NULL && !RCSNUM_ISBRANCH(cf->file_ent->ce_rev) &&
81525d391d7Sjoris 	    cf->file_ent->ce_status != CVS_ENT_ADDED) {
8166a09de83Sjoris 		b1 = rcs_rev_getbuf(cf->file_rcs, cf->file_ent->ce_rev, 0);
8177bb3ddb0Sray 		b2 = buf_load_fd(cf->fd);
818e0488abaSjoris 
8197bb3ddb0Sray 		if (buf_differ(b1, b2))
820e0488abaSjoris 			ismodified = 1;
821e0488abaSjoris 		else
822e0488abaSjoris 			ismodified = 0;
8237bb3ddb0Sray 		buf_free(b1);
8247bb3ddb0Sray 		buf_free(b2);
825e0488abaSjoris 	}
826e0488abaSjoris 
82718cf7829Sjoris 	if (cf->file_rcs != NULL && cf->file_rcsrev != NULL &&
82818cf7829Sjoris 	    !RCSNUM_ISBRANCH(cf->file_rcsrev)) {
8293daf6609Sjoris 		state = rcs_state_get(cf->file_rcs, cf->file_rcsrev);
8303ad3fb45Sjoris 		if (state == NULL)
8313ad3fb45Sjoris 			fatal("failed to get state for HEAD for %s",
8323ad3fb45Sjoris 			    cf->file_path);
8330c09b735Sjoris 		if (!strcmp(state, RCS_STATE_DEAD))
8343ad3fb45Sjoris 			rcsdead = 1;
8353258e4a0Sjoris 
836b87788a5Stobias 		if (cvs_specified_date == -1 && cvs_directory_date == -1 &&
837b87788a5Stobias 		    tag == NULL && cf->in_attic &&
8382353e83aStobias 		    !RCSNUM_ISBRANCHREV(cf->file_rcsrev))
8392353e83aStobias 			rcsdead = 1;
8402353e83aStobias 
8413258e4a0Sjoris 		cf->file_rcs->rf_dead = rcsdead;
8423ad3fb45Sjoris 	}
8433ad3fb45Sjoris 
8443ad3fb45Sjoris 	/*
8453ad3fb45Sjoris 	 * 10 Sin
8463ad3fb45Sjoris 	 * 20 Goto hell
8473ad3fb45Sjoris 	 * (I welcome you if-else hell)
8483ad3fb45Sjoris 	 */
8493ad3fb45Sjoris 	if (cf->file_ent == NULL) {
8503ad3fb45Sjoris 		if (cf->file_rcs == NULL) {
8515cf15c45Sjoris 			if (!(cf->file_flags & FILE_ON_DISK)) {
8523ad3fb45Sjoris 				cvs_log(LP_NOTICE,
8533ad3fb45Sjoris 				    "nothing known about '%s'",
8543ad3fb45Sjoris 				    cf->file_path);
8553ad3fb45Sjoris 			}
8563ad3fb45Sjoris 
8573ad3fb45Sjoris 			cf->file_status = FILE_UNKNOWN;
85845b98c07Stobias 		} else if (rcsdead == 1 || !(cf->file_flags & FILE_HAS_TAG)) {
8595cf15c45Sjoris 			if (!(cf->file_flags & FILE_ON_DISK)) {
8603ad3fb45Sjoris 				cf->file_status = FILE_UPTODATE;
861cca0c23dSjoris 			} else if (cvs_cmdop != CVS_OP_ADD) {
8623ad3fb45Sjoris 				cf->file_status = FILE_UNKNOWN;
8633ad3fb45Sjoris 			}
864113cb29fStobias 		} else if (notag == 0 && cf->file_rcsrev != NULL) {
8653ad3fb45Sjoris 			cf->file_status = FILE_CHECKOUT;
86651ef6581Sjoris 		} else {
86751ef6581Sjoris 			cf->file_status = FILE_UPTODATE;
8683ad3fb45Sjoris 		}
8697cccca8aStobias 
8707cccca8aStobias 		return;
8717cccca8aStobias 	}
8727cccca8aStobias 
8737cccca8aStobias 	switch (cf->file_ent->ce_status) {
8747cccca8aStobias 	case CVS_ENT_ADDED:
8755cf15c45Sjoris 		if (!(cf->file_flags & FILE_ON_DISK)) {
8767a3a739bSjoris 			if (cvs_cmdop != CVS_OP_REMOVE) {
8773ad3fb45Sjoris 				cvs_log(LP_NOTICE,
8789a192d08Sdavid 				    "warning: new-born %s has disappeared",
8793ad3fb45Sjoris 				    cf->file_path);
8807a3a739bSjoris 			}
8813ad3fb45Sjoris 			cf->file_status = FILE_REMOVE_ENTRY;
88245b98c07Stobias 		} else if (cf->file_rcs == NULL || rcsdead == 1 ||
88345b98c07Stobias 		    !(cf->file_flags & FILE_HAS_TAG)) {
8843ad3fb45Sjoris 			cf->file_status = FILE_ADDED;
8853ad3fb45Sjoris 		} else {
8863ad3fb45Sjoris 			cvs_log(LP_NOTICE,
8873ad3fb45Sjoris 			    "conflict: %s already created by others",
8883ad3fb45Sjoris 			    cf->file_path);
8893ad3fb45Sjoris 			cf->file_status = FILE_CONFLICT;
8903ad3fb45Sjoris 		}
8917cccca8aStobias 		break;
8927cccca8aStobias 	case CVS_ENT_REMOVED:
8935cf15c45Sjoris 		if (cf->file_flags & FILE_ON_DISK) {
8943ad3fb45Sjoris 			cvs_log(LP_NOTICE,
8953ad3fb45Sjoris 			    "%s should be removed but is still there",
8963ad3fb45Sjoris 			    cf->file_path);
8973ad3fb45Sjoris 			cf->file_status = FILE_REMOVED;
8983ad3fb45Sjoris 		} else if (cf->file_rcs == NULL || rcsdead == 1) {
8993ad3fb45Sjoris 			cf->file_status = FILE_REMOVE_ENTRY;
9003ad3fb45Sjoris 		} else {
90198c225fbStobias 			if (rcsnum_differ(cf->file_ent->ce_rev,
90298c225fbStobias 			    cf->file_rcsrev) && cvs_cmdop != CVS_OP_ADD) {
9033ad3fb45Sjoris 				cvs_log(LP_NOTICE,
9043ad3fb45Sjoris 				    "conflict: removed %s was modified"
9053ad3fb45Sjoris 				    " by a second party",
9063ad3fb45Sjoris 				    cf->file_path);
9073ad3fb45Sjoris 				cf->file_status = FILE_CONFLICT;
9083ad3fb45Sjoris 			} else {
9093ad3fb45Sjoris 				cf->file_status = FILE_REMOVED;
9103ad3fb45Sjoris 			}
9113ad3fb45Sjoris 		}
9127cccca8aStobias 		break;
9137cccca8aStobias 	case CVS_ENT_REG:
914113cb29fStobias 		if (cf->file_rcs == NULL || cf->file_rcsrev == NULL ||
915a10a48e5Stobias 		    rcsdead == 1 || (reset_tag == 1 && cf->in_attic == 1) ||
916a10a48e5Stobias 		    (notag == 1 && tag != NULL)) {
9175cf15c45Sjoris 			if (!(cf->file_flags & FILE_ON_DISK)) {
9183ad3fb45Sjoris 				cvs_log(LP_NOTICE,
9193ad3fb45Sjoris 				    "warning: %s's entry exists but"
920d81d2f4aSjoris 				    " is no longer in the repository,"
9213ad3fb45Sjoris 				    " removing entry",
9223ad3fb45Sjoris 				     cf->file_path);
9233ad3fb45Sjoris 				cf->file_status = FILE_REMOVE_ENTRY;
9243ad3fb45Sjoris 			} else {
9253ad3fb45Sjoris 				if (ismodified) {
9263ad3fb45Sjoris 					cvs_log(LP_NOTICE,
9273ad3fb45Sjoris 					    "conflict: %s is no longer "
9283ad3fb45Sjoris 					    "in the repository but is "
9293ad3fb45Sjoris 					    "locally modified",
9303ad3fb45Sjoris 					    cf->file_path);
93109d28507Stobias 					if (cvs_cmdop == CVS_OP_COMMIT)
93209d28507Stobias 						cf->file_status = FILE_UNLINK;
93309d28507Stobias 					else
9343ad3fb45Sjoris 						cf->file_status = FILE_CONFLICT;
935372e510aStobias 				} else if (cvs_cmdop != CVS_OP_IMPORT) {
9363ad3fb45Sjoris 					cvs_log(LP_NOTICE,
9373ad3fb45Sjoris 					    "%s is no longer in the "
9383ad3fb45Sjoris 					    "repository",
9393ad3fb45Sjoris 					    cf->file_path);
9403ad3fb45Sjoris 
9413ad3fb45Sjoris 					cf->file_status = FILE_UNLINK;
9423ad3fb45Sjoris 				}
9433ad3fb45Sjoris 			}
944113cb29fStobias 		} else if (cf->file_rcsrev == NULL) {
945113cb29fStobias 			cf->file_status = FILE_UNLINK;
9463ad3fb45Sjoris 		} else {
9475cf15c45Sjoris 			if (!(cf->file_flags & FILE_ON_DISK)) {
9487a3a739bSjoris 				if (cvs_cmdop != CVS_OP_REMOVE) {
9493ad3fb45Sjoris 					cvs_log(LP_NOTICE,
9503ad3fb45Sjoris 					    "warning: %s was lost",
9513ad3fb45Sjoris 					    cf->file_path);
9527a3a739bSjoris 				}
9533ad3fb45Sjoris 				cf->file_status = FILE_LOST;
9543ad3fb45Sjoris 			} else {
9553ad3fb45Sjoris 				if (ismodified == 1)
9563ad3fb45Sjoris 					cf->file_status = FILE_MODIFIED;
9573ad3fb45Sjoris 				else
9583ad3fb45Sjoris 					cf->file_status = FILE_UPTODATE;
95998c225fbStobias 				if (rcsnum_differ(cf->file_ent->ce_rev,
96098c225fbStobias 				    cf->file_rcsrev)) {
9613ad3fb45Sjoris 					if (cf->file_status == FILE_MODIFIED)
9623ad3fb45Sjoris 						cf->file_status = FILE_MERGE;
9633ad3fb45Sjoris 					else
9643ad3fb45Sjoris 						cf->file_status = FILE_PATCH;
9653ad3fb45Sjoris 				}
9663ad3fb45Sjoris 			}
9673ad3fb45Sjoris 		}
9687cccca8aStobias 		break;
969bf2db54cSjoris 	case CVS_ENT_UNKNOWN:
970bf2db54cSjoris 		if (cvs_server_active != 1)
971bf2db54cSjoris 			fatal("server-side questionable in local mode?");
972bf2db54cSjoris 		cf->file_status = FILE_UNKNOWN;
973bf2db54cSjoris 		break;
9747cccca8aStobias 	default:
9757cccca8aStobias 		break;
9763ad3fb45Sjoris 	}
9773ad3fb45Sjoris }
9783ad3fb45Sjoris 
9793ad3fb45Sjoris void
cvs_file_free(struct cvs_file * cf)9803ad3fb45Sjoris cvs_file_free(struct cvs_file *cf)
9813ad3fb45Sjoris {
982397ddb8aSnicm 	free(cf->file_name);
983397ddb8aSnicm 	free(cf->file_wd);
984397ddb8aSnicm 	free(cf->file_path);
98553ce2177Sfcambus 	free(cf->file_rcsrev);
986397ddb8aSnicm 	free(cf->file_rpath);
9873ad3fb45Sjoris 	if (cf->file_ent != NULL)
9883ad3fb45Sjoris 		cvs_ent_free(cf->file_ent);
9893ad3fb45Sjoris 	if (cf->file_rcs != NULL)
9903ad3fb45Sjoris 		rcs_close(cf->file_rcs);
9913ad3fb45Sjoris 	if (cf->fd != -1)
9923ad3fb45Sjoris 		(void)close(cf->fd);
9933ad3fb45Sjoris 	if (cf->repo_fd != -1)
9943ad3fb45Sjoris 		(void)close(cf->repo_fd);
995397ddb8aSnicm 	free(cf);
9960c1ec078Sjfb }
9970c1ec078Sjfb 
998de08069bSxsa int
cvs_file_cmpname(const char * name1,const char * name2)99966436eeaSjfb cvs_file_cmpname(const char *name1, const char *name2)
100066436eeaSjfb {
100166436eeaSjfb 	return (cvs_nocase == 0) ? (strcmp(name1, name2)) :
100266436eeaSjfb 	    (strcasecmp(name1, name2));
100366436eeaSjfb }
100493c88394Sxsa 
100593c88394Sxsa int
cvs_file_cmp(const char * file1,const char * file2)100693c88394Sxsa cvs_file_cmp(const char *file1, const char *file2)
100793c88394Sxsa {
100893c88394Sxsa 	struct stat stb1, stb2;
100993c88394Sxsa 	int fd1, fd2, ret;
101093c88394Sxsa 
101193c88394Sxsa 	ret = 0;
101293c88394Sxsa 
1013b7041c07Sderaadt 	if ((fd1 = open(file1, O_RDONLY|O_NOFOLLOW)) == -1)
101493c88394Sxsa 		fatal("cvs_file_cmp: open: `%s': %s", file1, strerror(errno));
1015b7041c07Sderaadt 	if ((fd2 = open(file2, O_RDONLY|O_NOFOLLOW)) == -1)
101693c88394Sxsa 		fatal("cvs_file_cmp: open: `%s': %s", file2, strerror(errno));
101793c88394Sxsa 
101893c88394Sxsa 	if (fstat(fd1, &stb1) == -1)
101993c88394Sxsa 		fatal("cvs_file_cmp: `%s': %s", file1, strerror(errno));
102093c88394Sxsa 	if (fstat(fd2, &stb2) == -1)
102193c88394Sxsa 		fatal("cvs_file_cmp: `%s': %s", file2, strerror(errno));
102293c88394Sxsa 
102393c88394Sxsa 	if (stb1.st_size != stb2.st_size ||
102493c88394Sxsa 	    (stb1.st_mode & S_IFMT) != (stb2.st_mode & S_IFMT)) {
102593c88394Sxsa 		ret = 1;
102693c88394Sxsa 		goto out;
102793c88394Sxsa 	}
102893c88394Sxsa 
102993c88394Sxsa 	if (S_ISBLK(stb1.st_mode) || S_ISCHR(stb1.st_mode)) {
103093c88394Sxsa 		if (stb1.st_rdev != stb2.st_rdev)
103193c88394Sxsa 			ret = 1;
103293c88394Sxsa 		goto out;
103393c88394Sxsa 	}
103493c88394Sxsa 
103593c88394Sxsa 	if (S_ISREG(stb1.st_mode)) {
103693c88394Sxsa 		void *p1, *p2;
103793c88394Sxsa 
1038ae886706Smillert 		if ((uintmax_t)stb1.st_size > SIZE_MAX) {
103993c88394Sxsa 			ret = 1;
104093c88394Sxsa 			goto out;
104193c88394Sxsa 		}
104293c88394Sxsa 
104393c88394Sxsa 		if ((p1 = mmap(NULL, stb1.st_size, PROT_READ,
104493c88394Sxsa 		    MAP_FILE, fd1, (off_t)0)) == MAP_FAILED)
104593c88394Sxsa 			fatal("cvs_file_cmp: mmap failed");
104693c88394Sxsa 
104793c88394Sxsa 		if ((p2 = mmap(NULL, stb1.st_size, PROT_READ,
104893c88394Sxsa 		    MAP_FILE, fd2, (off_t)0)) == MAP_FAILED)
104993c88394Sxsa 			fatal("cvs_file_cmp: mmap failed");
105093c88394Sxsa 
105193c88394Sxsa 		madvise(p1, stb1.st_size, MADV_SEQUENTIAL);
105293c88394Sxsa 		madvise(p2, stb1.st_size, MADV_SEQUENTIAL);
105393c88394Sxsa 
105493c88394Sxsa 		ret = memcmp(p1, p2, stb1.st_size);
105593c88394Sxsa 
105693c88394Sxsa 		(void)munmap(p1, stb1.st_size);
105793c88394Sxsa 		(void)munmap(p2, stb1.st_size);
105893c88394Sxsa 	}
105993c88394Sxsa 
106093c88394Sxsa out:
106193c88394Sxsa 	(void)close(fd1);
106293c88394Sxsa 	(void)close(fd2);
106393c88394Sxsa 
106493c88394Sxsa 	return (ret);
106593c88394Sxsa }
10660b10fb85Sxsa 
10670b10fb85Sxsa int
cvs_file_copy(const char * from,const char * to)10680b10fb85Sxsa cvs_file_copy(const char *from, const char *to)
10690b10fb85Sxsa {
10700b10fb85Sxsa 	struct stat st;
1071*fd71e6a3Sguenther 	struct timespec ts[2];
10720b10fb85Sxsa 	int src, dst, ret;
10730b10fb85Sxsa 
10740b10fb85Sxsa 	ret = 0;
10750b10fb85Sxsa 
10760b10fb85Sxsa 	cvs_log(LP_TRACE, "cvs_file_copy(%s,%s)", from, to);
10770b10fb85Sxsa 
10780b10fb85Sxsa 	if (cvs_noexec == 1)
10790b10fb85Sxsa 		return (0);
10800b10fb85Sxsa 
1081b7041c07Sderaadt 	if ((src = open(from, O_RDONLY)) == -1)
10820b10fb85Sxsa 		fatal("cvs_file_copy: open: `%s': %s", from, strerror(errno));
10830b10fb85Sxsa 
10840b10fb85Sxsa 	if (fstat(src, &st) == -1)
10850b10fb85Sxsa 		fatal("cvs_file_copy: `%s': %s", from, strerror(errno));
10860b10fb85Sxsa 
10870b10fb85Sxsa 	if (S_ISREG(st.st_mode)) {
1088bb59aeccStobias 		char *p;
10890b10fb85Sxsa 		int saved_errno;
10900b10fb85Sxsa 
1091ae886706Smillert 		if ((uintmax_t)st.st_size > SIZE_MAX) {
10920b10fb85Sxsa 			ret = -1;
10930b10fb85Sxsa 			goto out;
10940b10fb85Sxsa 		}
10950b10fb85Sxsa 
10960b10fb85Sxsa 		if ((dst = open(to, O_CREAT|O_TRUNC|O_WRONLY,
10970b10fb85Sxsa 		    st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO))) == -1)
10980b10fb85Sxsa 			fatal("cvs_file_copy: open `%s': %s",
10990b10fb85Sxsa 			    to, strerror(errno));
11000b10fb85Sxsa 
11010b10fb85Sxsa 		if ((p = mmap(NULL, st.st_size, PROT_READ,
11020b10fb85Sxsa 		    MAP_FILE, src, (off_t)0)) == MAP_FAILED) {
11030b10fb85Sxsa 			saved_errno = errno;
11040b10fb85Sxsa 			(void)unlink(to);
11050b10fb85Sxsa 			fatal("cvs_file_copy: mmap: %s", strerror(saved_errno));
11060b10fb85Sxsa 		}
11070b10fb85Sxsa 
11080b10fb85Sxsa 		madvise(p, st.st_size, MADV_SEQUENTIAL);
11090b10fb85Sxsa 
1110ae886706Smillert 		if (atomicio(vwrite, dst, p, st.st_size) != (size_t)st.st_size) {
11110b10fb85Sxsa 			saved_errno = errno;
11120b10fb85Sxsa 			(void)unlink(to);
1113bb59aeccStobias 			fatal("cvs_file_copy: `%s': %s", from,
1114bb59aeccStobias 			    strerror(saved_errno));
11150b10fb85Sxsa 		}
11160b10fb85Sxsa 
11170b10fb85Sxsa 		(void)munmap(p, st.st_size);
11180b10fb85Sxsa 
1119*fd71e6a3Sguenther 		ts[0] = st.st_atim;
1120*fd71e6a3Sguenther 		ts[1] = st.st_mtim;
11210b10fb85Sxsa 
1122*fd71e6a3Sguenther 		if (futimens(dst, ts) == -1) {
11230b10fb85Sxsa 			saved_errno = errno;
11240b10fb85Sxsa 			(void)unlink(to);
11250b10fb85Sxsa 			fatal("cvs_file_copy: futimes: %s",
11260b10fb85Sxsa 			    strerror(saved_errno));
11270b10fb85Sxsa 		}
11280b10fb85Sxsa 		(void)close(dst);
11290b10fb85Sxsa 	}
11300b10fb85Sxsa out:
11310b10fb85Sxsa 	(void)close(src);
11320b10fb85Sxsa 
11330b10fb85Sxsa 	return (ret);
11340b10fb85Sxsa }
1135f106b389Sjoris 
1136f106b389Sjoris int
cvs_filelist_cmp(struct cvs_filelist * f1,struct cvs_filelist * f2)1137f106b389Sjoris cvs_filelist_cmp(struct cvs_filelist *f1, struct cvs_filelist *f2)
1138f106b389Sjoris {
1139f106b389Sjoris 	return (strcmp(f1->file_path, f2->file_path));
1140f106b389Sjoris }
1141