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