xref: /openbsd-src/usr.bin/cvs/util.c (revision 3aaa63eb46949490a39db9c6d82aacc8ee5d8551)
1*3aaa63ebSderaadt /*	$OpenBSD: util.c,v 1.162 2019/06/28 13:35:00 deraadt Exp $	*/
26c121f58Sjfb /*
36c121f58Sjfb  * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
4477497b8Sxsa  * Copyright (c) 2005, 2006 Joris Vink <joris@openbsd.org>
5477497b8Sxsa  * Copyright (c) 2005, 2006 Xavier Santolaria <xsa@openbsd.org>
66c121f58Sjfb  * All rights reserved.
76c121f58Sjfb  *
86c121f58Sjfb  * Redistribution and use in source and binary forms, with or without
96c121f58Sjfb  * modification, are permitted provided that the following conditions
106c121f58Sjfb  * are met:
116c121f58Sjfb  *
126c121f58Sjfb  * 1. Redistributions of source code must retain the above copyright
136c121f58Sjfb  *    notice, this list of conditions and the following disclaimer.
146c121f58Sjfb  * 2. The name of the author may not be used to endorse or promote products
156c121f58Sjfb  *    derived from this software without specific prior written permission.
166c121f58Sjfb  *
176c121f58Sjfb  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
186c121f58Sjfb  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
196c121f58Sjfb  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
206c121f58Sjfb  * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
216c121f58Sjfb  * EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLUDING, BUT NOT LIMITED TO,
226c121f58Sjfb  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
236c121f58Sjfb  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
246c121f58Sjfb  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
256c121f58Sjfb  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
266c121f58Sjfb  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
276c121f58Sjfb  */
286c121f58Sjfb 
291f8531bdSotto #include <sys/stat.h>
30172698f8Stobias #include <sys/types.h>
311f8531bdSotto #include <sys/wait.h>
321f8531bdSotto 
33172698f8Stobias #include <atomicio.h>
341f8531bdSotto #include <errno.h>
35172698f8Stobias #include <fcntl.h>
361f8531bdSotto #include <stdlib.h>
371f8531bdSotto #include <string.h>
38ba6315b4Sjoris #include <paths.h>
391f8531bdSotto #include <unistd.h>
406c121f58Sjfb 
416c121f58Sjfb #include "cvs.h"
425dd120b0Sjoris #include "remote.h"
438fbb14c0Sjoris #include "hash.h"
446c121f58Sjfb 
4546bff1c6Stobias extern int print_stdout;
46d049fbc0Sjoris extern int build_dirs;
471b5598b0Sjoris extern int disable_fast_checkout;
4846bff1c6Stobias 
496c121f58Sjfb /* letter -> mode type map */
506c121f58Sjfb static const int cvs_modetypes[26] = {
516c121f58Sjfb 	-1, -1, -1, -1, -1, -1,  1, -1, -1, -1, -1, -1, -1,
526c121f58Sjfb 	-1,  2, -1, -1, -1, -1, -1,  0, -1, -1, -1, -1, -1,
536c121f58Sjfb };
546c121f58Sjfb 
556c121f58Sjfb /* letter -> mode map */
566c121f58Sjfb static const mode_t cvs_modes[3][26] = {
576c121f58Sjfb 	{
586c121f58Sjfb 		0,  0,       0,       0,       0,  0,  0,    /* a - g */
596c121f58Sjfb 		0,  0,       0,       0,       0,  0,  0,    /* h - m */
606c121f58Sjfb 		0,  0,       0,       S_IRUSR, 0,  0,  0,    /* n - u */
616c121f58Sjfb 		0,  S_IWUSR, S_IXUSR, 0,       0             /* v - z */
626c121f58Sjfb 	},
636c121f58Sjfb 	{
646c121f58Sjfb 		0,  0,       0,       0,       0,  0,  0,    /* a - g */
656c121f58Sjfb 		0,  0,       0,       0,       0,  0,  0,    /* h - m */
666c121f58Sjfb 		0,  0,       0,       S_IRGRP, 0,  0,  0,    /* n - u */
676c121f58Sjfb 		0,  S_IWGRP, S_IXGRP, 0,       0             /* v - z */
686c121f58Sjfb 	},
696c121f58Sjfb 	{
706c121f58Sjfb 		0,  0,       0,       0,       0,  0,  0,    /* a - g */
716c121f58Sjfb 		0,  0,       0,       0,       0,  0,  0,    /* h - m */
726c121f58Sjfb 		0,  0,       0,       S_IROTH, 0,  0,  0,    /* n - u */
736c121f58Sjfb 		0,  S_IWOTH, S_IXOTH, 0,       0             /* v - z */
746c121f58Sjfb 	}
756c121f58Sjfb };
766c121f58Sjfb 
776c121f58Sjfb 
786c121f58Sjfb /* octal -> string */
796c121f58Sjfb static const char *cvs_modestr[8] = {
806c121f58Sjfb 	"", "x", "w", "wx", "r", "rx", "rw", "rwx"
816c121f58Sjfb };
826c121f58Sjfb 
836c121f58Sjfb /*
846c121f58Sjfb  * cvs_strtomode()
856c121f58Sjfb  *
866c121f58Sjfb  * Read the contents of the string <str> and generate a permission mode from
876c121f58Sjfb  * the contents of <str>, which is assumed to have the mode format of CVS.
886c121f58Sjfb  * The CVS protocol specification states that any modes or mode types that are
896c121f58Sjfb  * not recognized should be silently ignored.  This function does not return
906c121f58Sjfb  * an error in such cases, but will issue warnings.
916c121f58Sjfb  */
92f06855e2Sjoris void
cvs_strtomode(const char * str,mode_t * mode)936c121f58Sjfb cvs_strtomode(const char *str, mode_t *mode)
946c121f58Sjfb {
952e6997a3Sjfb 	char type;
96f06855e2Sjoris 	size_t l;
976c121f58Sjfb 	mode_t m;
986c121f58Sjfb 	char buf[32], ms[4], *sp, *ep;
996c121f58Sjfb 
1006c121f58Sjfb 	m = 0;
101f06855e2Sjoris 	l = strlcpy(buf, str, sizeof(buf));
102f06855e2Sjoris 	if (l >= sizeof(buf))
103f06855e2Sjoris 		fatal("cvs_strtomode: string truncation");
104f06855e2Sjoris 
1056c121f58Sjfb 	sp = buf;
1066c121f58Sjfb 	ep = sp;
1076c121f58Sjfb 
1086c121f58Sjfb 	for (sp = buf; ep != NULL; sp = ep + 1) {
1096c121f58Sjfb 		ep = strchr(sp, ',');
1106c121f58Sjfb 		if (ep != NULL)
1112e6997a3Sjfb 			*ep = '\0';
1126c121f58Sjfb 
1132c5243aaSweingart 		memset(ms, 0, sizeof ms);
1142c5243aaSweingart 		if (sscanf(sp, "%c=%3s", &type, ms) != 2 &&
1152c5243aaSweingart 			sscanf(sp, "%c=", &type) != 1) {
1169fac60a5Sjoris 			fatal("failed to scan mode string `%s'", sp);
1176c121f58Sjfb 		}
1186c121f58Sjfb 
119d593696fSderaadt 		if (type <= 'a' || type >= 'z' ||
120d593696fSderaadt 		    cvs_modetypes[type - 'a'] == -1) {
1213ad3fb45Sjoris 			cvs_log(LP_ERR,
1226c121f58Sjfb 			    "invalid mode type `%c'"
1236c121f58Sjfb 			    " (`u', `g' or `o' expected), ignoring", type);
1246c121f58Sjfb 			continue;
1256c121f58Sjfb 		}
1266c121f58Sjfb 
1276c121f58Sjfb 		/* make type contain the actual mode index */
1286c121f58Sjfb 		type = cvs_modetypes[type - 'a'];
1296c121f58Sjfb 
1306c121f58Sjfb 		for (sp = ms; *sp != '\0'; sp++) {
131d593696fSderaadt 			if (*sp <= 'a' || *sp >= 'z' ||
132d593696fSderaadt 			    cvs_modes[(int)type][*sp - 'a'] == 0) {
1339fac60a5Sjoris 				fatal("invalid permission bit `%c'", *sp);
1343917c9bfSderaadt 			} else
135a86c92a7Sjfb 				m |= cvs_modes[(int)type][*sp - 'a'];
1366c121f58Sjfb 		}
1376c121f58Sjfb 	}
1386c121f58Sjfb 
1396c121f58Sjfb 	*mode = m;
1406c121f58Sjfb }
1416c121f58Sjfb 
1426c121f58Sjfb /*
1436c121f58Sjfb  * cvs_modetostr()
1446c121f58Sjfb  *
14532f58b24Sjfb  * Generate a CVS-format string to represent the permissions mask on a file
14632f58b24Sjfb  * from the mode <mode> and store the result in <buf>, which can accept up to
14732f58b24Sjfb  * <len> bytes (including the terminating NUL byte).  The result is guaranteed
14832f58b24Sjfb  * to be NUL-terminated.
1496c121f58Sjfb  */
15002899e3bSjoris void
cvs_modetostr(mode_t mode,char * buf,size_t len)1516c121f58Sjfb cvs_modetostr(mode_t mode, char *buf, size_t len)
1526c121f58Sjfb {
1536c121f58Sjfb 	char tmp[16], *bp;
1546c121f58Sjfb 	mode_t um, gm, om;
1556c121f58Sjfb 
1566c121f58Sjfb 	um = (mode & S_IRWXU) >> 6;
1576c121f58Sjfb 	gm = (mode & S_IRWXG) >> 3;
1586c121f58Sjfb 	om = mode & S_IRWXO;
1596c121f58Sjfb 
1606c121f58Sjfb 	bp = buf;
1616c121f58Sjfb 	*bp = '\0';
1626c121f58Sjfb 
1636c121f58Sjfb 	if (um) {
164f182b925Sxsa 		if (strlcpy(tmp, "u=", sizeof(tmp)) >= sizeof(tmp) ||
165f182b925Sxsa 		    strlcat(tmp, cvs_modestr[um], sizeof(tmp)) >= sizeof(tmp))
16602899e3bSjoris 			fatal("cvs_modetostr: overflow for user mode");
16702899e3bSjoris 
168f182b925Sxsa 		if (strlcat(buf, tmp, len) >= len)
16902899e3bSjoris 			fatal("cvs_modetostr: string truncation");
1706c121f58Sjfb 	}
1716c121f58Sjfb 
17202899e3bSjoris 	if (gm) {
17302899e3bSjoris 		if (um) {
174f182b925Sxsa 			if (strlcat(buf, ",", len) >= len)
17502899e3bSjoris 				fatal("cvs_modetostr: string truncation");
17602899e3bSjoris 		}
17702899e3bSjoris 
178f182b925Sxsa 		if (strlcpy(tmp, "g=", sizeof(tmp)) >= sizeof(tmp) ||
179f182b925Sxsa 		    strlcat(tmp, cvs_modestr[gm], sizeof(tmp)) >= sizeof(tmp))
18002899e3bSjoris 			fatal("cvs_modetostr: overflow for group mode");
18102899e3bSjoris 
182f182b925Sxsa 		if (strlcat(buf, tmp, len) >= len)
18302899e3bSjoris 			fatal("cvs_modetostr: string truncation");
18402899e3bSjoris 	}
18502899e3bSjoris 
18602899e3bSjoris 	if (om) {
18702899e3bSjoris 		if (um || gm) {
188f182b925Sxsa 			if (strlcat(buf, ",", len) >= len)
18902899e3bSjoris 				fatal("cvs_modetostr: string truncation");
19002899e3bSjoris 		}
19102899e3bSjoris 
192f182b925Sxsa 		if (strlcpy(tmp, "o=", sizeof(tmp)) >= sizeof(tmp) ||
193f182b925Sxsa 		    strlcat(tmp, cvs_modestr[gm], sizeof(tmp)) >= sizeof(tmp))
19402899e3bSjoris 			fatal("cvs_modetostr: overflow for others mode");
19502899e3bSjoris 
196f182b925Sxsa 		if (strlcat(buf, tmp, len) >= len)
19702899e3bSjoris 			fatal("cvs_modetostr: string truncation");
19802899e3bSjoris 	}
1996c121f58Sjfb }
2006c121f58Sjfb 
2016c121f58Sjfb /*
2026c121f58Sjfb  * cvs_getargv()
2036c121f58Sjfb  *
2046c121f58Sjfb  * Parse a line contained in <line> and generate an argument vector by
2056c121f58Sjfb  * splitting the line on spaces and tabs.  The resulting vector is stored in
2066c121f58Sjfb  * <argv>, which can accept up to <argvlen> entries.
2072a165055Sdavid  * Returns the number of arguments in the vector, or -1 if an error occurred.
2086c121f58Sjfb  */
2096c121f58Sjfb int
cvs_getargv(const char * line,char ** argv,int argvlen)2106c121f58Sjfb cvs_getargv(const char *line, char **argv, int argvlen)
2116c121f58Sjfb {
2126c121f58Sjfb 	u_int i;
213b453b25eSxsa 	int argc, error;
214e6a55d17Stobias 	char *linebuf, *lp, *cp;
2156c121f58Sjfb 
216a1013731Stobias 	linebuf = xstrdup(line);
21702899e3bSjoris 
2185edf9c39Spat 	memset(argv, 0, argvlen * sizeof(char *));
2196c121f58Sjfb 	argc = 0;
2206c121f58Sjfb 
2216c121f58Sjfb 	/* build the argument vector */
222b453b25eSxsa 	error = 0;
2236c121f58Sjfb 	for (lp = linebuf; lp != NULL;) {
2246c121f58Sjfb 		cp = strsep(&lp, " \t");
2256c121f58Sjfb 		if (cp == NULL)
2266c121f58Sjfb 			break;
2276c121f58Sjfb 		else if (*cp == '\0')
2286c121f58Sjfb 			continue;
2296c121f58Sjfb 
230a139b080Sjfb 		if (argc == argvlen) {
231b453b25eSxsa 			error++;
232a139b080Sjfb 			break;
233a139b080Sjfb 		}
234a139b080Sjfb 
235e6a55d17Stobias 		argv[argc] = xstrdup(cp);
2366c121f58Sjfb 		argc++;
2376c121f58Sjfb 	}
2386c121f58Sjfb 
239b453b25eSxsa 	if (error != 0) {
2406c121f58Sjfb 		/* ditch the argument vector */
2416c121f58Sjfb 		for (i = 0; i < (u_int)argc; i++)
242397ddb8aSnicm 			free(argv[i]);
2436c121f58Sjfb 		argc = -1;
2446c121f58Sjfb 	}
2456c121f58Sjfb 
246397ddb8aSnicm 	free(linebuf);
2476c121f58Sjfb 	return (argc);
2486c121f58Sjfb }
2496c121f58Sjfb 
2506c121f58Sjfb /*
251ca1d3256Sjfb  * cvs_makeargv()
252ca1d3256Sjfb  *
2532a165055Sdavid  * Allocate an argument vector large enough to accommodate for all the
254ca1d3256Sjfb  * arguments found in <line> and return it.
255ca1d3256Sjfb  */
256ca1d3256Sjfb char **
cvs_makeargv(const char * line,int * argc)257ca1d3256Sjfb cvs_makeargv(const char *line, int *argc)
258ca1d3256Sjfb {
259ca1d3256Sjfb 	int i, ret;
260ca1d3256Sjfb 	char *argv[1024], **copy;
261ca1d3256Sjfb 
262ca1d3256Sjfb 	ret = cvs_getargv(line, argv, 1024);
263ca1d3256Sjfb 	if (ret == -1)
264ca1d3256Sjfb 		return (NULL);
265ca1d3256Sjfb 
2663b4c5c25Sray 	copy = xcalloc(ret + 1, sizeof(char *));
267ca1d3256Sjfb 
268ca1d3256Sjfb 	for (i = 0; i < ret; i++)
269ca1d3256Sjfb 		copy[i] = argv[i];
270ca1d3256Sjfb 	copy[ret] = NULL;
271ca1d3256Sjfb 
272ca1d3256Sjfb 	*argc = ret;
273ca1d3256Sjfb 	return (copy);
274ca1d3256Sjfb }
275ca1d3256Sjfb 
276ca1d3256Sjfb /*
2776c121f58Sjfb  * cvs_freeargv()
2786c121f58Sjfb  *
2796c121f58Sjfb  * Free an argument vector previously generated by cvs_getargv().
2806c121f58Sjfb  */
2816c121f58Sjfb void
cvs_freeargv(char ** argv,int argc)2826c121f58Sjfb cvs_freeargv(char **argv, int argc)
2836c121f58Sjfb {
2846c121f58Sjfb 	int i;
2856c121f58Sjfb 
2866c121f58Sjfb 	for (i = 0; i < argc; i++)
287397ddb8aSnicm 		free(argv[i]);
2886c121f58Sjfb }
2892e6997a3Sjfb 
2901f5bba1eSkrapht /*
2915202bbedSxsa  * cvs_chdir()
2925202bbedSxsa  *
29331864001Sxsa  * Change to directory <path>.
29431864001Sxsa  * If <rm> is equal to `1', <path> is removed if chdir() fails so we
29531864001Sxsa  * do not have temporary directories leftovers.
296276398bcSxsa  * Returns 0 on success.
2975202bbedSxsa  */
2985202bbedSxsa int
cvs_chdir(const char * path,int rm)29931864001Sxsa cvs_chdir(const char *path, int rm)
3005202bbedSxsa {
30131864001Sxsa 	if (chdir(path) == -1) {
30231864001Sxsa 		if (rm == 1)
30331864001Sxsa 			cvs_unlink(path);
304276398bcSxsa 		fatal("cvs_chdir: `%s': %s", path, strerror(errno));
30531864001Sxsa 	}
3065202bbedSxsa 
3075202bbedSxsa 	return (0);
3085202bbedSxsa }
3095202bbedSxsa 
3105202bbedSxsa /*
311a8589706Sxsa  * cvs_rename()
312a8589706Sxsa  * Change the name of a file.
313a8589706Sxsa  * rename() wrapper with an error message.
314276398bcSxsa  * Returns 0 on success.
315a8589706Sxsa  */
316a8589706Sxsa int
cvs_rename(const char * from,const char * to)317a8589706Sxsa cvs_rename(const char *from, const char *to)
318a8589706Sxsa {
319d0a063c4Sjoris 	if (cvs_server_active == 0)
320a8589706Sxsa 		cvs_log(LP_TRACE, "cvs_rename(%s,%s)", from, to);
321a8589706Sxsa 
322a8589706Sxsa 	if (cvs_noexec == 1)
323a8589706Sxsa 		return (0);
324a8589706Sxsa 
325276398bcSxsa 	if (rename(from, to) == -1)
326276398bcSxsa 		fatal("cvs_rename: `%s'->`%s': %s", from, to, strerror(errno));
327a8589706Sxsa 
328a8589706Sxsa 	return (0);
329a8589706Sxsa }
330a8589706Sxsa 
331a8589706Sxsa /*
332444c3027Sxsa  * cvs_unlink()
333444c3027Sxsa  *
334444c3027Sxsa  * Removes the link named by <path>.
335444c3027Sxsa  * unlink() wrapper with an error message.
336444c3027Sxsa  * Returns 0 on success, or -1 on failure.
337444c3027Sxsa  */
338444c3027Sxsa int
cvs_unlink(const char * path)339444c3027Sxsa cvs_unlink(const char *path)
340444c3027Sxsa {
341d0a063c4Sjoris 	if (cvs_server_active == 0)
342444c3027Sxsa 		cvs_log(LP_TRACE, "cvs_unlink(%s)", path);
343444c3027Sxsa 
3441b5598b0Sjoris 	if (cvs_noexec == 1 && disable_fast_checkout != 0)
345444c3027Sxsa 		return (0);
346444c3027Sxsa 
347d593696fSderaadt 	if (unlink(path) == -1 && errno != ENOENT) {
34835ab38c5Sxsa 		cvs_log(LP_ERRNO, "%s", path);
349444c3027Sxsa 		return (-1);
350444c3027Sxsa 	}
351444c3027Sxsa 
352444c3027Sxsa 	return (0);
353444c3027Sxsa }
354444c3027Sxsa 
355444c3027Sxsa /*
356506d2610Sxsa  * cvs_rmdir()
35732f58b24Sjfb  *
35832f58b24Sjfb  * Remove a directory tree from disk.
35932f58b24Sjfb  * Returns 0 on success, or -1 on failure.
360bdbad263Sjoris  */
361bdbad263Sjoris int
cvs_rmdir(const char * path)362506d2610Sxsa cvs_rmdir(const char *path)
363bdbad263Sjoris {
364aaad48f1Stodd 	int type, ret = -1;
365bdbad263Sjoris 	DIR *dirp;
366bdbad263Sjoris 	struct dirent *ent;
367aaad48f1Stodd 	struct stat st;
368b9fc9a72Sderaadt 	char fpath[PATH_MAX];
369bdbad263Sjoris 
370d0a063c4Sjoris 	if (cvs_server_active == 0)
3714da60ae0Sxsa 		cvs_log(LP_TRACE, "cvs_rmdir(%s)", path);
3724da60ae0Sxsa 
3731b5598b0Sjoris 	if (cvs_noexec == 1 && disable_fast_checkout != 0)
3744da60ae0Sxsa 		return (0);
3754da60ae0Sxsa 
376bdbad263Sjoris 	if ((dirp = opendir(path)) == NULL) {
3773ad3fb45Sjoris 		cvs_log(LP_ERR, "failed to open '%s'", path);
37832f58b24Sjfb 		return (-1);
379bdbad263Sjoris 	}
380bdbad263Sjoris 
381bdbad263Sjoris 	while ((ent = readdir(dirp)) != NULL) {
382bdbad263Sjoris 		if (!strcmp(ent->d_name, ".") ||
383bdbad263Sjoris 		    !strcmp(ent->d_name, ".."))
384bdbad263Sjoris 			continue;
385bdbad263Sjoris 
386e40de241Sxsa 		(void)xsnprintf(fpath, sizeof(fpath), "%s/%s",
387e40de241Sxsa 		    path, ent->d_name);
388bdbad263Sjoris 
389aaad48f1Stodd 		if (ent->d_type == DT_UNKNOWN) {
390e5f49488Stodd 			if (lstat(fpath, &st) == -1)
391aaad48f1Stodd 				fatal("'%s': %s", fpath, strerror(errno));
392aaad48f1Stodd 
393aaad48f1Stodd 			switch (st.st_mode & S_IFMT) {
394aaad48f1Stodd 			case S_IFDIR:
395aaad48f1Stodd 				type = CVS_DIR;
396aaad48f1Stodd 				break;
397aaad48f1Stodd 			case S_IFREG:
398aaad48f1Stodd 				type = CVS_FILE;
399aaad48f1Stodd 				break;
400aaad48f1Stodd 			default:
401aaad48f1Stodd 				fatal("'%s': Unknown file type in copy",
402aaad48f1Stodd 				    fpath);
403aaad48f1Stodd 			}
404aaad48f1Stodd 		} else {
405aaad48f1Stodd 			switch (ent->d_type) {
406aaad48f1Stodd 			case DT_DIR:
407aaad48f1Stodd 				type = CVS_DIR;
408aaad48f1Stodd 				break;
409aaad48f1Stodd 			case DT_REG:
410aaad48f1Stodd 				type = CVS_FILE;
411aaad48f1Stodd 				break;
412aaad48f1Stodd 			default:
413aaad48f1Stodd 				fatal("'%s': Unknown file type in copy",
414aaad48f1Stodd 				    fpath);
415aaad48f1Stodd 			}
416aaad48f1Stodd 		}
417aaad48f1Stodd 		switch (type) {
418aaad48f1Stodd 		case CVS_DIR:
419506d2610Sxsa 			if (cvs_rmdir(fpath) == -1)
420b5f79659Spat 				goto done;
421aaad48f1Stodd 			break;
422aaad48f1Stodd 		case CVS_FILE:
423aaad48f1Stodd 			if (cvs_unlink(fpath) == -1 && errno != ENOENT)
424b5f79659Spat 				goto done;
425aaad48f1Stodd 			break;
426aaad48f1Stodd 		default:
427aaad48f1Stodd 			fatal("type %d unknown, shouldn't happen", type);
428aaad48f1Stodd 		}
429bdbad263Sjoris 	}
430bdbad263Sjoris 
431bdbad263Sjoris 
432d593696fSderaadt 	if (rmdir(path) == -1 && errno != ENOENT) {
433554ded7cSxsa 		cvs_log(LP_ERRNO, "%s", path);
434b5f79659Spat 		goto done;
435bdbad263Sjoris 	}
436bdbad263Sjoris 
437b5f79659Spat 	ret = 0;
438b5f79659Spat done:
439b5f79659Spat 	closedir(dirp);
440b5f79659Spat 	return (ret);
44132f58b24Sjfb }
44232f58b24Sjfb 
4433ad3fb45Sjoris void
cvs_get_repository_path(const char * dir,char * dst,size_t len)4446ac6a1c7Sjoris cvs_get_repository_path(const char *dir, char *dst, size_t len)
4456ac6a1c7Sjoris {
446b9fc9a72Sderaadt 	char buf[PATH_MAX];
4476ac6a1c7Sjoris 
4486ac6a1c7Sjoris 	cvs_get_repository_name(dir, buf, sizeof(buf));
449e40de241Sxsa 	(void)xsnprintf(dst, len, "%s/%s", current_cvsroot->cr_dir, buf);
450d2d7e959Sjoris 	cvs_validate_directory(dst);
4516ac6a1c7Sjoris }
4526ac6a1c7Sjoris 
4536ac6a1c7Sjoris void
cvs_get_repository_name(const char * dir,char * dst,size_t len)4546ac6a1c7Sjoris cvs_get_repository_name(const char *dir, char *dst, size_t len)
4553ad3fb45Sjoris {
4563ad3fb45Sjoris 	FILE *fp;
457b9fc9a72Sderaadt 	char fpath[PATH_MAX];
4583ad3fb45Sjoris 
45946bff1c6Stobias 	dst[0] = '\0';
460be6cfeaeStobias 
461be6cfeaeStobias 	if (!(cmdp->cmd_flags & CVS_USE_WDIR)) {
462be6cfeaeStobias 		if (strlcpy(dst, dir, len) >= len)
46346bff1c6Stobias 			fatal("cvs_get_repository_name: truncation");
46446bff1c6Stobias 		return;
46546bff1c6Stobias 	}
46646bff1c6Stobias 
467be6cfeaeStobias 	switch (cvs_cmdop) {
468be6cfeaeStobias 	case CVS_OP_EXPORT:
469be6cfeaeStobias 		if (strcmp(dir, "."))
470be6cfeaeStobias 			if (strlcpy(dst, dir, len) >= len)
471be6cfeaeStobias 				fatal("cvs_get_repository_name: truncation");
472be6cfeaeStobias 		break;
473be6cfeaeStobias 	case CVS_OP_IMPORT:
4741dd2d57eSjoris 		if (strlcpy(dst, import_repository, len) >= len)
4754462ebc9Sjoris 			fatal("cvs_get_repository_name: truncation");
4761dd2d57eSjoris 		if (strlcat(dst, "/", len) >= len)
4774462ebc9Sjoris 			fatal("cvs_get_repository_name: truncation");
4781dd2d57eSjoris 
479be6cfeaeStobias 		if (strcmp(dir, "."))
4801dd2d57eSjoris 			if (strlcat(dst, dir, len) >= len)
481be6cfeaeStobias 				fatal("cvs_get_repository_name: truncation");
482be6cfeaeStobias 		break;
483be6cfeaeStobias 	default:
484be6cfeaeStobias 		(void)xsnprintf(fpath, sizeof(fpath), "%s/%s",
485be6cfeaeStobias 		    dir, CVS_PATH_REPOSITORY);
486be6cfeaeStobias 		if ((fp = fopen(fpath, "r")) != NULL) {
487be6cfeaeStobias 			if ((fgets(dst, len, fp)) == NULL)
488be6cfeaeStobias 				fatal("%s: bad repository file", fpath);
489be6cfeaeStobias 			dst[strcspn(dst, "\n")] = '\0';
490be6cfeaeStobias 			(void)fclose(fp);
491be6cfeaeStobias 		} else if (cvs_cmdop != CVS_OP_CHECKOUT)
492be6cfeaeStobias 			fatal("%s is missing", fpath);
493be6cfeaeStobias 		break;
4943ad3fb45Sjoris 	}
4953ad3fb45Sjoris }
4963ad3fb45Sjoris 
4973ad3fb45Sjoris void
cvs_mkadmin(const char * path,const char * root,const char * repo,char * tag,char * date)498890ecb5aSxsa cvs_mkadmin(const char *path, const char *root, const char *repo,
4992dec954eStobias     char *tag, char *date)
5003ad3fb45Sjoris {
5013ad3fb45Sjoris 	FILE *fp;
502172698f8Stobias 	int fd;
503b9fc9a72Sderaadt 	char buf[PATH_MAX];
5048fbb14c0Sjoris 	struct hash_data *hdata, hd;
5058fbb14c0Sjoris 
5068fbb14c0Sjoris 	hdata = hash_table_find(&created_cvs_directories, path, strlen(path));
5078fbb14c0Sjoris 	if (hdata != NULL)
5088fbb14c0Sjoris 		return;
5098fbb14c0Sjoris 
5108fbb14c0Sjoris 	hd.h_key = xstrdup(path);
5118fbb14c0Sjoris 	hd.h_data = NULL;
5128fbb14c0Sjoris 	hash_table_enter(&created_cvs_directories, &hd);
5133ad3fb45Sjoris 
514d0a063c4Sjoris 	if (cvs_server_active == 0)
5152dec954eStobias 		cvs_log(LP_TRACE, "cvs_mkadmin(%s, %s, %s, %s, %s)",
516890ecb5aSxsa 		    path, root, repo, (tag != NULL) ? tag : "",
5172dec954eStobias 		    (date != NULL) ? date : "");
5183ad3fb45Sjoris 
519e40de241Sxsa 	(void)xsnprintf(buf, sizeof(buf), "%s/%s", path, CVS_PATH_CVSDIR);
5203ad3fb45Sjoris 
5213ad3fb45Sjoris 	if (mkdir(buf, 0755) == -1 && errno != EEXIST)
5223ad3fb45Sjoris 		fatal("cvs_mkadmin: %s: %s", buf, strerror(errno));
5233ad3fb45Sjoris 
524d049fbc0Sjoris 	if (cvs_cmdop == CVS_OP_CHECKOUT || cvs_cmdop == CVS_OP_ADD ||
525d049fbc0Sjoris 	    (cvs_cmdop == CVS_OP_UPDATE && build_dirs == 1)) {
526f4a2933bSjoris 		(void)xsnprintf(buf, sizeof(buf), "%s/%s",
527f4a2933bSjoris 		    path, CVS_PATH_ROOTSPEC);
5283ad3fb45Sjoris 
5293ad3fb45Sjoris 		if ((fp = fopen(buf, "w")) == NULL)
5303ad3fb45Sjoris 			fatal("cvs_mkadmin: %s: %s", buf, strerror(errno));
5313ad3fb45Sjoris 
5323ad3fb45Sjoris 		fprintf(fp, "%s\n", root);
5333ad3fb45Sjoris 		(void)fclose(fp);
534f4a2933bSjoris 	}
5353ad3fb45Sjoris 
536e40de241Sxsa 	(void)xsnprintf(buf, sizeof(buf), "%s/%s", path, CVS_PATH_REPOSITORY);
5373ad3fb45Sjoris 
5383ad3fb45Sjoris 	if ((fp = fopen(buf, "w")) == NULL)
5393ad3fb45Sjoris 		fatal("cvs_mkadmin: %s: %s", buf, strerror(errno));
5403ad3fb45Sjoris 
5413ad3fb45Sjoris 	fprintf(fp, "%s\n", repo);
5423ad3fb45Sjoris 	(void)fclose(fp);
5433ad3fb45Sjoris 
5442dec954eStobias 	cvs_write_tagfile(path, tag, date);
545172698f8Stobias 
546172698f8Stobias 	(void)xsnprintf(buf, sizeof(buf), "%s/%s", path, CVS_PATH_ENTRIES);
547172698f8Stobias 
548172698f8Stobias 	if ((fd = open(buf, O_WRONLY|O_CREAT|O_EXCL, 0666 & ~cvs_umask))
549172698f8Stobias 	    == -1) {
550172698f8Stobias 		if (errno == EEXIST)
551172698f8Stobias 			return;
552172698f8Stobias 		fatal("cvs_mkadmin: %s: %s", buf, strerror(errno));
553172698f8Stobias 	}
554172698f8Stobias 
555172698f8Stobias 	if (atomicio(vwrite, fd, "D\n", 2) != 2)
556172698f8Stobias 		fatal("cvs_mkadmin: %s", strerror(errno));
557172698f8Stobias 	close(fd);
5583ad3fb45Sjoris }
5593ad3fb45Sjoris 
5603ad3fb45Sjoris void
cvs_mkpath(const char * path,char * tag)5615dd120b0Sjoris cvs_mkpath(const char *path, char *tag)
5623ad3fb45Sjoris {
5639584b8e8Stobias 	CVSENTRIES *ent;
5643ad3fb45Sjoris 	FILE *fp;
5653ad3fb45Sjoris 	size_t len;
5668fbb14c0Sjoris 	struct hash_data *hdata, hd;
567b9fc9a72Sderaadt 	char *entry, *sp, *dp, *dir, *p, rpath[PATH_MAX], repo[PATH_MAX];
5683ad3fb45Sjoris 
5698fbb14c0Sjoris 	hdata = hash_table_find(&created_directories, path, strlen(path));
5708fbb14c0Sjoris 	if (hdata != NULL)
5718fbb14c0Sjoris 		return;
5728fbb14c0Sjoris 
5738fbb14c0Sjoris 	hd.h_key = xstrdup(path);
5748fbb14c0Sjoris 	hd.h_data = NULL;
5758fbb14c0Sjoris 	hash_table_enter(&created_directories, &hd);
5768fbb14c0Sjoris 
5774dcde513Sjoris 	if (cvsroot_is_remote() || cvs_server_active == 1)
57838be6170Sjoris 		cvs_validate_directory(path);
57938be6170Sjoris 
5803ad3fb45Sjoris 	dir = xstrdup(path);
5813ad3fb45Sjoris 
5823ad3fb45Sjoris 	STRIP_SLASH(dir);
583d0a063c4Sjoris 
584d0a063c4Sjoris 	if (cvs_server_active == 0)
5853ad3fb45Sjoris 		cvs_log(LP_TRACE, "cvs_mkpath(%s)", dir);
5863ad3fb45Sjoris 
5873ad3fb45Sjoris 	repo[0] = '\0';
5883ad3fb45Sjoris 	rpath[0] = '\0';
5893ad3fb45Sjoris 
5905b2d50e5Sjoris 	if ((cvs_cmdop != CVS_OP_CHECKOUT) && (cvs_cmdop != CVS_OP_EXPORT)) {
5913ad3fb45Sjoris 		if ((fp = fopen(CVS_PATH_REPOSITORY, "r")) != NULL) {
592c9ccdb9dSthib 			if ((fgets(repo, sizeof(repo), fp)) == NULL)
593c9ccdb9dSthib 				fatal("cvs_mkpath: bad repository file");
594b625fa02Sgilles 			repo[strcspn(repo, "\n")] = '\0';
5953ad3fb45Sjoris 			(void)fclose(fp);
5963ad3fb45Sjoris 		}
5973ad3fb45Sjoris 	}
5983ad3fb45Sjoris 
5993ad3fb45Sjoris 	for (sp = dir; sp != NULL; sp = dp) {
6003ad3fb45Sjoris 		dp = strchr(sp, '/');
6013ad3fb45Sjoris 		if (dp != NULL)
6023ad3fb45Sjoris 			*(dp++) = '\0';
6033ad3fb45Sjoris 
604bf6291b7Sjoris 		if (sp == dir && module_repo_root != NULL) {
605bf6291b7Sjoris 			len = strlcpy(repo, module_repo_root, sizeof(repo));
606bf6291b7Sjoris 			if (len >= (int)sizeof(repo))
607bf6291b7Sjoris 				fatal("cvs_mkpath: overflow");
6089bea4275Sjoris 		} else if (strcmp(sp, ".")) {
6093ad3fb45Sjoris 			if (repo[0] != '\0') {
6103ad3fb45Sjoris 				len = strlcat(repo, "/", sizeof(repo));
6113ad3fb45Sjoris 				if (len >= (int)sizeof(repo))
6123ad3fb45Sjoris 					fatal("cvs_mkpath: overflow");
6133ad3fb45Sjoris 			}
6143ad3fb45Sjoris 
6153ad3fb45Sjoris 			len = strlcat(repo, sp, sizeof(repo));
6163ad3fb45Sjoris 			if (len >= (int)sizeof(repo))
6173ad3fb45Sjoris 				fatal("cvs_mkpath: overflow");
618bf6291b7Sjoris 		}
6193ad3fb45Sjoris 
6203ad3fb45Sjoris 		if (rpath[0] != '\0') {
6213ad3fb45Sjoris 			len = strlcat(rpath, "/", sizeof(rpath));
6223ad3fb45Sjoris 			if (len >= (int)sizeof(rpath))
6233ad3fb45Sjoris 				fatal("cvs_mkpath: overflow");
6243ad3fb45Sjoris 		}
6253ad3fb45Sjoris 
6263ad3fb45Sjoris 		len = strlcat(rpath, sp, sizeof(rpath));
6273ad3fb45Sjoris 		if (len >= (int)sizeof(rpath))
6283ad3fb45Sjoris 			fatal("cvs_mkpath: overflow");
6293ad3fb45Sjoris 
6303ad3fb45Sjoris 		if (mkdir(rpath, 0755) == -1 && errno != EEXIST)
6313ad3fb45Sjoris 			fatal("cvs_mkpath: %s: %s", rpath, strerror(errno));
6323ad3fb45Sjoris 
6335e1effbaStobias 		if (cvs_cmdop == CVS_OP_EXPORT && !cvs_server_active)
6345e1effbaStobias 			continue;
6355e1effbaStobias 
6365219eee5Sjoris 		cvs_mkadmin(rpath, current_cvsroot->cr_str, repo,
6372dec954eStobias 		    tag, NULL);
6385dd120b0Sjoris 
6399584b8e8Stobias 		if (dp != NULL) {
6409584b8e8Stobias 			if ((p = strchr(dp, '/')) != NULL)
6419584b8e8Stobias 				*p = '\0';
642ae83823aSxsa 
643ae83823aSxsa 			entry = xmalloc(CVS_ENT_MAXLINELEN);
644ae83823aSxsa 			cvs_ent_line_str(dp, NULL, NULL, NULL, NULL, 1, 0,
645ae83823aSxsa 			    entry, CVS_ENT_MAXLINELEN);
646ae83823aSxsa 
6479584b8e8Stobias 			ent = cvs_ent_open(rpath);
6489584b8e8Stobias 			cvs_ent_add(ent, entry);
649397ddb8aSnicm 			free(entry);
650ae83823aSxsa 
6519584b8e8Stobias 			if (p != NULL)
6529584b8e8Stobias 				*p = '/';
6539584b8e8Stobias 		}
6543ad3fb45Sjoris 	}
6553ad3fb45Sjoris 
656397ddb8aSnicm 	free(dir);
6573ad3fb45Sjoris }
65801af718aSjoris 
6590f651f47Snicm void
cvs_mkdir(const char * path,mode_t mode)6600f651f47Snicm cvs_mkdir(const char *path, mode_t mode)
6610f651f47Snicm {
6620f651f47Snicm 	size_t len;
663b9fc9a72Sderaadt 	char *sp, *dp, *dir, rpath[PATH_MAX];
6640f651f47Snicm 
6654dcde513Sjoris 	if (cvsroot_is_remote() || cvs_server_active == 1)
6660f651f47Snicm 		cvs_validate_directory(path);
6670f651f47Snicm 
6680f651f47Snicm 	dir = xstrdup(path);
6690f651f47Snicm 
6700f651f47Snicm 	STRIP_SLASH(dir);
6710f651f47Snicm 
6720f651f47Snicm 	if (cvs_server_active == 0)
6730f651f47Snicm 		cvs_log(LP_TRACE, "cvs_mkdir(%s)", dir);
6740f651f47Snicm 
6750f651f47Snicm 	rpath[0] = '\0';
6760f651f47Snicm 
6770f651f47Snicm 	for (sp = dir; sp != NULL; sp = dp) {
6780f651f47Snicm 		dp = strchr(sp, '/');
6790f651f47Snicm 		if (dp != NULL)
6800f651f47Snicm 			*(dp++) = '\0';
6810f651f47Snicm 
6820f651f47Snicm 		len = strlcat(rpath, "/", sizeof(rpath));
6830f651f47Snicm 		if (len >= (int)sizeof(rpath))
6840f651f47Snicm 			fatal("cvs_mkdir: overflow");
6850f651f47Snicm 
6860f651f47Snicm 		len = strlcat(rpath, sp, sizeof(rpath));
6870f651f47Snicm 		if (len >= (int)sizeof(rpath))
6880f651f47Snicm 			fatal("cvs_mkdir: overflow");
6890f651f47Snicm 		if (1 == len)
6900f651f47Snicm 			continue;
6910f651f47Snicm 
6920f651f47Snicm 		if (mkdir(rpath, mode) == -1 && errno != EEXIST)
6930f651f47Snicm 			fatal("cvs_mkdir: %s: %s", rpath, strerror(errno));
6940f651f47Snicm 	}
6950f651f47Snicm 
696397ddb8aSnicm 	free(dir);
6970f651f47Snicm }
6980f651f47Snicm 
69901af718aSjoris /*
70001af718aSjoris  * Split the contents of a file into a list of lines.
70101af718aSjoris  */
7027bb3ddb0Sray struct rcs_lines *
cvs_splitlines(u_char * data,size_t len)70390fdc30cSotto cvs_splitlines(u_char *data, size_t len)
70401af718aSjoris {
70531cfd684Sniallo 	u_char *p, *c;
70631cfd684Sniallo 	size_t i, tlen;
7077bb3ddb0Sray 	struct rcs_lines *lines;
7087bb3ddb0Sray 	struct rcs_line *lp;
70901af718aSjoris 
71009cb0937Stobias 	lines = xcalloc(1, sizeof(*lines));
71101af718aSjoris 	TAILQ_INIT(&(lines->l_lines));
71201af718aSjoris 
71309cb0937Stobias 	lp = xcalloc(1, sizeof(*lp));
71401af718aSjoris 	TAILQ_INSERT_TAIL(&(lines->l_lines), lp, l_list);
71501af718aSjoris 
71631cfd684Sniallo 	p = c = data;
71731cfd684Sniallo 	for (i = 0; i < len; i++) {
71831cfd684Sniallo 		if (*p == '\n' || (i == len - 1)) {
71931cfd684Sniallo 			tlen = p - c + 1;
72009cb0937Stobias 			lp = xcalloc(1, sizeof(*lp));
72131cfd684Sniallo 			lp->l_line = c;
72231cfd684Sniallo 			lp->l_len = tlen;
72301af718aSjoris 			lp->l_lineno = ++(lines->l_nblines);
72401af718aSjoris 			TAILQ_INSERT_TAIL(&(lines->l_lines), lp, l_list);
72531cfd684Sniallo 			c = p + 1;
72631cfd684Sniallo 		}
72731cfd684Sniallo 		p++;
72801af718aSjoris 	}
72901af718aSjoris 
73001af718aSjoris 	return (lines);
73101af718aSjoris }
73201af718aSjoris 
73301af718aSjoris void
cvs_freelines(struct rcs_lines * lines)7347bb3ddb0Sray cvs_freelines(struct rcs_lines *lines)
73501af718aSjoris {
7367bb3ddb0Sray 	struct rcs_line *lp;
73701af718aSjoris 
73801af718aSjoris 	while ((lp = TAILQ_FIRST(&(lines->l_lines))) != NULL) {
73901af718aSjoris 		TAILQ_REMOVE(&(lines->l_lines), lp, l_list);
7400951efdeSniallo 		if (lp->l_needsfree == 1)
741397ddb8aSnicm 			free(lp->l_line);
742397ddb8aSnicm 		free(lp);
74301af718aSjoris 	}
74401af718aSjoris 
745397ddb8aSnicm 	free(lines);
74601af718aSjoris }
74701af718aSjoris 
748b1df6b0eSjoris /*
749477497b8Sxsa  * cvs_strsplit()
750477497b8Sxsa  *
751477497b8Sxsa  * Split a string <str> of <sep>-separated values and allocate
752477497b8Sxsa  * an argument vector for the values found.
753477497b8Sxsa  */
754948d9c85Spat struct cvs_argvector *
cvs_strsplit(char * str,const char * sep)755477497b8Sxsa cvs_strsplit(char *str, const char *sep)
756477497b8Sxsa {
757948d9c85Spat 	struct cvs_argvector *av;
758f240ac99Sray 	size_t i = 0;
759477497b8Sxsa 	char *cp, *p;
760477497b8Sxsa 
761477497b8Sxsa 	cp = xstrdup(str);
7623b4c5c25Sray 	av = xmalloc(sizeof(*av));
763948d9c85Spat 	av->str = cp;
764af434b4aSray 	av->argv = xmalloc(sizeof(*(av->argv)));
765477497b8Sxsa 
766477497b8Sxsa 	while ((p = strsep(&cp, sep)) != NULL) {
767948d9c85Spat 		av->argv[i++] = p;
768caa2ffb0Sderaadt 		av->argv = xreallocarray(av->argv,
7693b4c5c25Sray 		    i + 1, sizeof(*(av->argv)));
770477497b8Sxsa 	}
771948d9c85Spat 	av->argv[i] = NULL;
772477497b8Sxsa 
773948d9c85Spat 	return (av);
774948d9c85Spat }
775948d9c85Spat 
776948d9c85Spat /*
777948d9c85Spat  * cvs_argv_destroy()
778948d9c85Spat  *
779948d9c85Spat  * Free an argument vector previously allocated by cvs_strsplit().
780948d9c85Spat  */
781948d9c85Spat void
cvs_argv_destroy(struct cvs_argvector * av)782948d9c85Spat cvs_argv_destroy(struct cvs_argvector *av)
783948d9c85Spat {
784397ddb8aSnicm 	free(av->str);
785397ddb8aSnicm 	free(av->argv);
786397ddb8aSnicm 	free(av);
787477497b8Sxsa }
7887fbffbd7Sjoris 
7897fbffbd7Sjoris u_int
cvs_revision_select(RCSFILE * file,char * range)7907fbffbd7Sjoris cvs_revision_select(RCSFILE *file, char *range)
7917fbffbd7Sjoris {
7927fbffbd7Sjoris 	int i;
7937fbffbd7Sjoris 	u_int nrev;
7947fbffbd7Sjoris 	char *lstr, *rstr;
7957fbffbd7Sjoris 	struct rcs_delta *rdp;
7967fbffbd7Sjoris 	struct cvs_argvector *revargv, *revrange;
7977fbffbd7Sjoris 	RCSNUM *lnum, *rnum;
7987fbffbd7Sjoris 
7997fbffbd7Sjoris 	nrev = 0;
8007fbffbd7Sjoris 	lnum = rnum = NULL;
8017fbffbd7Sjoris 
8027fbffbd7Sjoris 	revargv = cvs_strsplit(range, ",");
8037fbffbd7Sjoris 	for (i = 0; revargv->argv[i] != NULL; i++) {
8047fbffbd7Sjoris 		revrange = cvs_strsplit(revargv->argv[i], ":");
8057fbffbd7Sjoris 		if (revrange->argv[0] == NULL)
8067fbffbd7Sjoris 			fatal("invalid revision range: %s", revargv->argv[i]);
8077fbffbd7Sjoris 		else if (revrange->argv[1] == NULL)
8087fbffbd7Sjoris 			lstr = rstr = revrange->argv[0];
8097fbffbd7Sjoris 		else {
8107fbffbd7Sjoris 			if (revrange->argv[2] != NULL)
8117fbffbd7Sjoris 				fatal("invalid revision range: %s",
8127fbffbd7Sjoris 				    revargv->argv[i]);
8137fbffbd7Sjoris 
8147fbffbd7Sjoris 			lstr = revrange->argv[0];
8157fbffbd7Sjoris 			rstr = revrange->argv[1];
8167fbffbd7Sjoris 
8177fbffbd7Sjoris 			if (strcmp(lstr, "") == 0)
8187fbffbd7Sjoris 				lstr = NULL;
8197fbffbd7Sjoris 			if (strcmp(rstr, "") == 0)
8207fbffbd7Sjoris 				rstr = NULL;
8217fbffbd7Sjoris 		}
8227fbffbd7Sjoris 
8237fbffbd7Sjoris 		if (lstr == NULL)
8247fbffbd7Sjoris 			lstr = RCS_HEAD_INIT;
8257fbffbd7Sjoris 
826bd433604Sniallo 		if ((lnum = rcs_translate_tag(lstr, file)) == NULL)
827bd433604Sniallo 			fatal("cvs_revision_select: could not translate tag `%s'", lstr);
8287fbffbd7Sjoris 
8297fbffbd7Sjoris 		if (rstr != NULL) {
830bd433604Sniallo 			if ((rnum = rcs_translate_tag(rstr, file)) == NULL)
831bd433604Sniallo 				fatal("cvs_revision_select: could not translate tag `%s'", rstr);
8327fbffbd7Sjoris 		} else {
8337fbffbd7Sjoris 			rnum = rcsnum_alloc();
8347fbffbd7Sjoris 			rcsnum_cpy(file->rf_head, rnum, 0);
8357fbffbd7Sjoris 		}
8367fbffbd7Sjoris 
8377fbffbd7Sjoris 		cvs_argv_destroy(revrange);
8387fbffbd7Sjoris 
8397fbffbd7Sjoris 		TAILQ_FOREACH(rdp, &(file->rf_delta), rd_list) {
8407fbffbd7Sjoris 			if (rcsnum_cmp(rdp->rd_num, lnum, 0) <= 0 &&
8417fbffbd7Sjoris 			    rcsnum_cmp(rdp->rd_num, rnum, 0) >= 0 &&
8427fbffbd7Sjoris 			    !(rdp->rd_flags & RCS_RD_SELECT)) {
8437fbffbd7Sjoris 				rdp->rd_flags |= RCS_RD_SELECT;
8447fbffbd7Sjoris 				nrev++;
8457fbffbd7Sjoris 			}
8467fbffbd7Sjoris 		}
8474649b416Sjoris 
84853ce2177Sfcambus 		free(lnum);
84953ce2177Sfcambus 		free(rnum);
8507fbffbd7Sjoris 	}
8517fbffbd7Sjoris 
8527fbffbd7Sjoris 	cvs_argv_destroy(revargv);
8537fbffbd7Sjoris 
8547fbffbd7Sjoris 	return (nrev);
8557fbffbd7Sjoris }
856633f4b90Sxsa 
857633f4b90Sxsa int
cvs_yesno(void)858633f4b90Sxsa cvs_yesno(void)
859633f4b90Sxsa {
860633f4b90Sxsa 	int c, ret;
861633f4b90Sxsa 
862633f4b90Sxsa 	ret = 0;
863633f4b90Sxsa 
864633f4b90Sxsa 	fflush(stderr);
865633f4b90Sxsa 	fflush(stdout);
866633f4b90Sxsa 
867633f4b90Sxsa 	if ((c = getchar()) != 'y' && c != 'Y')
868633f4b90Sxsa 		ret = -1;
869633f4b90Sxsa 	else
870633f4b90Sxsa 		while (c != EOF && c != '\n')
871633f4b90Sxsa 			c = getchar();
872633f4b90Sxsa 
873633f4b90Sxsa 	return (ret);
874633f4b90Sxsa }
875ba6315b4Sjoris 
876b034d592Sjoris /*
877b034d592Sjoris  * cvs_exec()
878b034d592Sjoris  *
879b034d592Sjoris  * Execute <prog> and send <in> to the STDIN if not NULL.
880b034d592Sjoris  * If <needwait> == 1, return the result of <prog>,
881b034d592Sjoris  * else, 0 or -1 if an error occur.
882b034d592Sjoris  */
883b034d592Sjoris int
cvs_exec(char * prog,char * in,int needwait)8840fcddf58Sotto cvs_exec(char *prog, char *in, int needwait)
885ba6315b4Sjoris {
886ba6315b4Sjoris 	pid_t pid;
887889040c6Sjoris 	size_t size;
888889040c6Sjoris 	int fds[2], st;
889b034d592Sjoris 	char *argp[4] = { "sh", "-c", prog, NULL };
890ba6315b4Sjoris 
891*3aaa63ebSderaadt 	if (in != NULL && pipe(fds) == -1) {
892b034d592Sjoris 		cvs_log(LP_ERR, "cvs_exec: pipe failed");
893b034d592Sjoris 		return (-1);
894b034d592Sjoris 	}
895ba6315b4Sjoris 
896ba6315b4Sjoris 	if ((pid = fork()) == -1) {
897ba6315b4Sjoris 		cvs_log(LP_ERR, "cvs_exec: fork failed");
898b034d592Sjoris 		return (-1);
899ba6315b4Sjoris 	} else if (pid == 0) {
900b034d592Sjoris 		if (in != NULL) {
901b034d592Sjoris 			close(fds[1]);
902b034d592Sjoris 			dup2(fds[0], STDIN_FILENO);
903b034d592Sjoris 		}
904b034d592Sjoris 
905b034d592Sjoris 		setenv("CVSROOT", current_cvsroot->cr_dir, 1);
906ba6315b4Sjoris 		execv(_PATH_BSHELL, argp);
907b034d592Sjoris 		cvs_log(LP_ERR, "cvs_exec: failed to run '%s'", prog);
908ba6315b4Sjoris 		_exit(127);
909ba6315b4Sjoris 	}
910b034d592Sjoris 
911b034d592Sjoris 	if (in != NULL) {
912b034d592Sjoris 		close(fds[0]);
913b034d592Sjoris 		size = strlen(in);
914b034d592Sjoris 		if (atomicio(vwrite, fds[1], in, size) != size)
915b034d592Sjoris 			cvs_log(LP_ERR, "cvs_exec: failed to write on STDIN");
916b034d592Sjoris 		close(fds[1]);
917b034d592Sjoris 	}
918b034d592Sjoris 
919b034d592Sjoris 	if (needwait == 1) {
920b034d592Sjoris 		while (waitpid(pid, &st, 0) == -1)
921b034d592Sjoris 			;
922b034d592Sjoris 		if (!WIFEXITED(st)) {
923b034d592Sjoris 			errno = EINTR;
924b034d592Sjoris 			return (-1);
925b034d592Sjoris 		}
926b034d592Sjoris 		return (WEXITSTATUS(st));
927b034d592Sjoris 	}
928b034d592Sjoris 
929b034d592Sjoris 	return (0);
930ba6315b4Sjoris }
931