File: [local] / src / usr.bin / cvs / util.c (download)
Revision 1.159, Thu Oct 13 20:51:25 2016 UTC (7 years, 7 months ago) by fcambus
Branch: MAIN
CVS Tags: OPENBSD_6_1_BASE, OPENBSD_6_1 Changes since 1.158: +3 -3 lines
rcsnum_free() -> free() cleanups. From Michael W. Bombardieri.
OK millert@
|
/* $OpenBSD: util.c,v 1.159 2016/10/13 20:51:25 fcambus Exp $ */
/*
* Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
* Copyright (c) 2005, 2006 Joris Vink <joris@openbsd.org>
* Copyright (c) 2005, 2006 Xavier Santolaria <xsa@openbsd.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <atomicio.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <paths.h>
#include <unistd.h>
#include "cvs.h"
#include "remote.h"
#include "hash.h"
extern int print_stdout;
extern int build_dirs;
extern int disable_fast_checkout;
/* letter -> mode type map */
static const int cvs_modetypes[26] = {
-1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1,
-1, 2, -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1,
};
/* letter -> mode map */
static const mode_t cvs_modes[3][26] = {
{
0, 0, 0, 0, 0, 0, 0, /* a - g */
0, 0, 0, 0, 0, 0, 0, /* h - m */
0, 0, 0, S_IRUSR, 0, 0, 0, /* n - u */
0, S_IWUSR, S_IXUSR, 0, 0 /* v - z */
},
{
0, 0, 0, 0, 0, 0, 0, /* a - g */
0, 0, 0, 0, 0, 0, 0, /* h - m */
0, 0, 0, S_IRGRP, 0, 0, 0, /* n - u */
0, S_IWGRP, S_IXGRP, 0, 0 /* v - z */
},
{
0, 0, 0, 0, 0, 0, 0, /* a - g */
0, 0, 0, 0, 0, 0, 0, /* h - m */
0, 0, 0, S_IROTH, 0, 0, 0, /* n - u */
0, S_IWOTH, S_IXOTH, 0, 0 /* v - z */
}
};
/* octal -> string */
static const char *cvs_modestr[8] = {
"", "x", "w", "wx", "r", "rx", "rw", "rwx"
};
/*
* cvs_strtomode()
*
* Read the contents of the string <str> and generate a permission mode from
* the contents of <str>, which is assumed to have the mode format of CVS.
* The CVS protocol specification states that any modes or mode types that are
* not recognized should be silently ignored. This function does not return
* an error in such cases, but will issue warnings.
*/
void
cvs_strtomode(const char *str, mode_t *mode)
{
char type;
size_t l;
mode_t m;
char buf[32], ms[4], *sp, *ep;
m = 0;
l = strlcpy(buf, str, sizeof(buf));
if (l >= sizeof(buf))
fatal("cvs_strtomode: string truncation");
sp = buf;
ep = sp;
for (sp = buf; ep != NULL; sp = ep + 1) {
ep = strchr(sp, ',');
if (ep != NULL)
*ep = '\0';
memset(ms, 0, sizeof ms);
if (sscanf(sp, "%c=%3s", &type, ms) != 2 &&
sscanf(sp, "%c=", &type) != 1) {
fatal("failed to scan mode string `%s'", sp);
}
if (type <= 'a' || type >= 'z' ||
cvs_modetypes[type - 'a'] == -1) {
cvs_log(LP_ERR,
"invalid mode type `%c'"
" (`u', `g' or `o' expected), ignoring", type);
continue;
}
/* make type contain the actual mode index */
type = cvs_modetypes[type - 'a'];
for (sp = ms; *sp != '\0'; sp++) {
if (*sp <= 'a' || *sp >= 'z' ||
cvs_modes[(int)type][*sp - 'a'] == 0) {
fatal("invalid permission bit `%c'", *sp);
} else
m |= cvs_modes[(int)type][*sp - 'a'];
}
}
*mode = m;
}
/*
* cvs_modetostr()
*
* Generate a CVS-format string to represent the permissions mask on a file
* from the mode <mode> and store the result in <buf>, which can accept up to
* <len> bytes (including the terminating NUL byte). The result is guaranteed
* to be NUL-terminated.
*/
void
cvs_modetostr(mode_t mode, char *buf, size_t len)
{
char tmp[16], *bp;
mode_t um, gm, om;
um = (mode & S_IRWXU) >> 6;
gm = (mode & S_IRWXG) >> 3;
om = mode & S_IRWXO;
bp = buf;
*bp = '\0';
if (um) {
if (strlcpy(tmp, "u=", sizeof(tmp)) >= sizeof(tmp) ||
strlcat(tmp, cvs_modestr[um], sizeof(tmp)) >= sizeof(tmp))
fatal("cvs_modetostr: overflow for user mode");
if (strlcat(buf, tmp, len) >= len)
fatal("cvs_modetostr: string truncation");
}
if (gm) {
if (um) {
if (strlcat(buf, ",", len) >= len)
fatal("cvs_modetostr: string truncation");
}
if (strlcpy(tmp, "g=", sizeof(tmp)) >= sizeof(tmp) ||
strlcat(tmp, cvs_modestr[gm], sizeof(tmp)) >= sizeof(tmp))
fatal("cvs_modetostr: overflow for group mode");
if (strlcat(buf, tmp, len) >= len)
fatal("cvs_modetostr: string truncation");
}
if (om) {
if (um || gm) {
if (strlcat(buf, ",", len) >= len)
fatal("cvs_modetostr: string truncation");
}
if (strlcpy(tmp, "o=", sizeof(tmp)) >= sizeof(tmp) ||
strlcat(tmp, cvs_modestr[gm], sizeof(tmp)) >= sizeof(tmp))
fatal("cvs_modetostr: overflow for others mode");
if (strlcat(buf, tmp, len) >= len)
fatal("cvs_modetostr: string truncation");
}
}
/*
* cvs_getargv()
*
* Parse a line contained in <line> and generate an argument vector by
* splitting the line on spaces and tabs. The resulting vector is stored in
* <argv>, which can accept up to <argvlen> entries.
* Returns the number of arguments in the vector, or -1 if an error occurred.
*/
int
cvs_getargv(const char *line, char **argv, int argvlen)
{
u_int i;
int argc, error;
char *linebuf, *lp, *cp;
linebuf = xstrdup(line);
memset(argv, 0, argvlen * sizeof(char *));
argc = 0;
/* build the argument vector */
error = 0;
for (lp = linebuf; lp != NULL;) {
cp = strsep(&lp, " \t");
if (cp == NULL)
break;
else if (*cp == '\0')
continue;
if (argc == argvlen) {
error++;
break;
}
argv[argc] = xstrdup(cp);
argc++;
}
if (error != 0) {
/* ditch the argument vector */
for (i = 0; i < (u_int)argc; i++)
free(argv[i]);
argc = -1;
}
free(linebuf);
return (argc);
}
/*
* cvs_makeargv()
*
* Allocate an argument vector large enough to accommodate for all the
* arguments found in <line> and return it.
*/
char **
cvs_makeargv(const char *line, int *argc)
{
int i, ret;
char *argv[1024], **copy;
ret = cvs_getargv(line, argv, 1024);
if (ret == -1)
return (NULL);
copy = xcalloc(ret + 1, sizeof(char *));
for (i = 0; i < ret; i++)
copy[i] = argv[i];
copy[ret] = NULL;
*argc = ret;
return (copy);
}
/*
* cvs_freeargv()
*
* Free an argument vector previously generated by cvs_getargv().
*/
void
cvs_freeargv(char **argv, int argc)
{
int i;
for (i = 0; i < argc; i++)
free(argv[i]);
}
/*
* cvs_chdir()
*
* Change to directory <path>.
* If <rm> is equal to `1', <path> is removed if chdir() fails so we
* do not have temporary directories leftovers.
* Returns 0 on success.
*/
int
cvs_chdir(const char *path, int rm)
{
if (chdir(path) == -1) {
if (rm == 1)
cvs_unlink(path);
fatal("cvs_chdir: `%s': %s", path, strerror(errno));
}
return (0);
}
/*
* cvs_rename()
* Change the name of a file.
* rename() wrapper with an error message.
* Returns 0 on success.
*/
int
cvs_rename(const char *from, const char *to)
{
if (cvs_server_active == 0)
cvs_log(LP_TRACE, "cvs_rename(%s,%s)", from, to);
if (cvs_noexec == 1)
return (0);
if (rename(from, to) == -1)
fatal("cvs_rename: `%s'->`%s': %s", from, to, strerror(errno));
return (0);
}
/*
* cvs_unlink()
*
* Removes the link named by <path>.
* unlink() wrapper with an error message.
* Returns 0 on success, or -1 on failure.
*/
int
cvs_unlink(const char *path)
{
if (cvs_server_active == 0)
cvs_log(LP_TRACE, "cvs_unlink(%s)", path);
if (cvs_noexec == 1 && disable_fast_checkout != 0)
return (0);
if (unlink(path) == -1 && errno != ENOENT) {
cvs_log(LP_ERRNO, "%s", path);
return (-1);
}
return (0);
}
/*
* cvs_rmdir()
*
* Remove a directory tree from disk.
* Returns 0 on success, or -1 on failure.
*/
int
cvs_rmdir(const char *path)
{
int type, ret = -1;
DIR *dirp;
struct dirent *ent;
struct stat st;
char fpath[PATH_MAX];
if (cvs_server_active == 0)
cvs_log(LP_TRACE, "cvs_rmdir(%s)", path);
if (cvs_noexec == 1 && disable_fast_checkout != 0)
return (0);
if ((dirp = opendir(path)) == NULL) {
cvs_log(LP_ERR, "failed to open '%s'", path);
return (-1);
}
while ((ent = readdir(dirp)) != NULL) {
if (!strcmp(ent->d_name, ".") ||
!strcmp(ent->d_name, ".."))
continue;
(void)xsnprintf(fpath, sizeof(fpath), "%s/%s",
path, ent->d_name);
if (ent->d_type == DT_UNKNOWN) {
if (lstat(fpath, &st) == -1)
fatal("'%s': %s", fpath, strerror(errno));
switch (st.st_mode & S_IFMT) {
case S_IFDIR:
type = CVS_DIR;
break;
case S_IFREG:
type = CVS_FILE;
break;
default:
fatal("'%s': Unknown file type in copy",
fpath);
}
} else {
switch (ent->d_type) {
case DT_DIR:
type = CVS_DIR;
break;
case DT_REG:
type = CVS_FILE;
break;
default:
fatal("'%s': Unknown file type in copy",
fpath);
}
}
switch (type) {
case CVS_DIR:
if (cvs_rmdir(fpath) == -1)
goto done;
break;
case CVS_FILE:
if (cvs_unlink(fpath) == -1 && errno != ENOENT)
goto done;
break;
default:
fatal("type %d unknown, shouldn't happen", type);
}
}
if (rmdir(path) == -1 && errno != ENOENT) {
cvs_log(LP_ERRNO, "%s", path);
goto done;
}
ret = 0;
done:
closedir(dirp);
return (ret);
}
void
cvs_get_repository_path(const char *dir, char *dst, size_t len)
{
char buf[PATH_MAX];
cvs_get_repository_name(dir, buf, sizeof(buf));
(void)xsnprintf(dst, len, "%s/%s", current_cvsroot->cr_dir, buf);
cvs_validate_directory(dst);
}
void
cvs_get_repository_name(const char *dir, char *dst, size_t len)
{
FILE *fp;
char fpath[PATH_MAX];
dst[0] = '\0';
if (!(cmdp->cmd_flags & CVS_USE_WDIR)) {
if (strlcpy(dst, dir, len) >= len)
fatal("cvs_get_repository_name: truncation");
return;
}
switch (cvs_cmdop) {
case CVS_OP_EXPORT:
if (strcmp(dir, "."))
if (strlcpy(dst, dir, len) >= len)
fatal("cvs_get_repository_name: truncation");
break;
case CVS_OP_IMPORT:
if (strlcpy(dst, import_repository, len) >= len)
fatal("cvs_get_repository_name: truncation");
if (strlcat(dst, "/", len) >= len)
fatal("cvs_get_repository_name: truncation");
if (strcmp(dir, "."))
if (strlcat(dst, dir, len) >= len)
fatal("cvs_get_repository_name: truncation");
break;
default:
(void)xsnprintf(fpath, sizeof(fpath), "%s/%s",
dir, CVS_PATH_REPOSITORY);
if ((fp = fopen(fpath, "r")) != NULL) {
if ((fgets(dst, len, fp)) == NULL)
fatal("%s: bad repository file", fpath);
dst[strcspn(dst, "\n")] = '\0';
(void)fclose(fp);
} else if (cvs_cmdop != CVS_OP_CHECKOUT)
fatal("%s is missing", fpath);
break;
}
}
void
cvs_mkadmin(const char *path, const char *root, const char *repo,
char *tag, char *date)
{
FILE *fp;
int fd;
char buf[PATH_MAX];
struct hash_data *hdata, hd;
hdata = hash_table_find(&created_cvs_directories, path, strlen(path));
if (hdata != NULL)
return;
hd.h_key = xstrdup(path);
hd.h_data = NULL;
hash_table_enter(&created_cvs_directories, &hd);
if (cvs_server_active == 0)
cvs_log(LP_TRACE, "cvs_mkadmin(%s, %s, %s, %s, %s)",
path, root, repo, (tag != NULL) ? tag : "",
(date != NULL) ? date : "");
(void)xsnprintf(buf, sizeof(buf), "%s/%s", path, CVS_PATH_CVSDIR);
if (mkdir(buf, 0755) == -1 && errno != EEXIST)
fatal("cvs_mkadmin: %s: %s", buf, strerror(errno));
if (cvs_cmdop == CVS_OP_CHECKOUT || cvs_cmdop == CVS_OP_ADD ||
(cvs_cmdop == CVS_OP_UPDATE && build_dirs == 1)) {
(void)xsnprintf(buf, sizeof(buf), "%s/%s",
path, CVS_PATH_ROOTSPEC);
if ((fp = fopen(buf, "w")) == NULL)
fatal("cvs_mkadmin: %s: %s", buf, strerror(errno));
fprintf(fp, "%s\n", root);
(void)fclose(fp);
}
(void)xsnprintf(buf, sizeof(buf), "%s/%s", path, CVS_PATH_REPOSITORY);
if ((fp = fopen(buf, "w")) == NULL)
fatal("cvs_mkadmin: %s: %s", buf, strerror(errno));
fprintf(fp, "%s\n", repo);
(void)fclose(fp);
cvs_write_tagfile(path, tag, date);
(void)xsnprintf(buf, sizeof(buf), "%s/%s", path, CVS_PATH_ENTRIES);
if ((fd = open(buf, O_WRONLY|O_CREAT|O_EXCL, 0666 & ~cvs_umask))
== -1) {
if (errno == EEXIST)
return;
fatal("cvs_mkadmin: %s: %s", buf, strerror(errno));
}
if (atomicio(vwrite, fd, "D\n", 2) != 2)
fatal("cvs_mkadmin: %s", strerror(errno));
close(fd);
}
void
cvs_mkpath(const char *path, char *tag)
{
CVSENTRIES *ent;
FILE *fp;
size_t len;
struct hash_data *hdata, hd;
char *entry, *sp, *dp, *dir, *p, rpath[PATH_MAX], repo[PATH_MAX];
hdata = hash_table_find(&created_directories, path, strlen(path));
if (hdata != NULL)
return;
hd.h_key = xstrdup(path);
hd.h_data = NULL;
hash_table_enter(&created_directories, &hd);
if (current_cvsroot->cr_method != CVS_METHOD_LOCAL ||
cvs_server_active == 1)
cvs_validate_directory(path);
dir = xstrdup(path);
STRIP_SLASH(dir);
if (cvs_server_active == 0)
cvs_log(LP_TRACE, "cvs_mkpath(%s)", dir);
repo[0] = '\0';
rpath[0] = '\0';
if ((cvs_cmdop != CVS_OP_CHECKOUT) && (cvs_cmdop != CVS_OP_EXPORT)) {
if ((fp = fopen(CVS_PATH_REPOSITORY, "r")) != NULL) {
if ((fgets(repo, sizeof(repo), fp)) == NULL)
fatal("cvs_mkpath: bad repository file");
repo[strcspn(repo, "\n")] = '\0';
(void)fclose(fp);
}
}
for (sp = dir; sp != NULL; sp = dp) {
dp = strchr(sp, '/');
if (dp != NULL)
*(dp++) = '\0';
if (sp == dir && module_repo_root != NULL) {
len = strlcpy(repo, module_repo_root, sizeof(repo));
if (len >= (int)sizeof(repo))
fatal("cvs_mkpath: overflow");
} else if (strcmp(sp, ".")) {
if (repo[0] != '\0') {
len = strlcat(repo, "/", sizeof(repo));
if (len >= (int)sizeof(repo))
fatal("cvs_mkpath: overflow");
}
len = strlcat(repo, sp, sizeof(repo));
if (len >= (int)sizeof(repo))
fatal("cvs_mkpath: overflow");
}
if (rpath[0] != '\0') {
len = strlcat(rpath, "/", sizeof(rpath));
if (len >= (int)sizeof(rpath))
fatal("cvs_mkpath: overflow");
}
len = strlcat(rpath, sp, sizeof(rpath));
if (len >= (int)sizeof(rpath))
fatal("cvs_mkpath: overflow");
if (mkdir(rpath, 0755) == -1 && errno != EEXIST)
fatal("cvs_mkpath: %s: %s", rpath, strerror(errno));
if (cvs_cmdop == CVS_OP_EXPORT && !cvs_server_active)
continue;
cvs_mkadmin(rpath, current_cvsroot->cr_str, repo,
tag, NULL);
if (dp != NULL) {
if ((p = strchr(dp, '/')) != NULL)
*p = '\0';
entry = xmalloc(CVS_ENT_MAXLINELEN);
cvs_ent_line_str(dp, NULL, NULL, NULL, NULL, 1, 0,
entry, CVS_ENT_MAXLINELEN);
ent = cvs_ent_open(rpath);
cvs_ent_add(ent, entry);
free(entry);
if (p != NULL)
*p = '/';
}
}
free(dir);
}
void
cvs_mkdir(const char *path, mode_t mode)
{
size_t len;
char *sp, *dp, *dir, rpath[PATH_MAX];
if (current_cvsroot->cr_method != CVS_METHOD_LOCAL ||
cvs_server_active == 1)
cvs_validate_directory(path);
dir = xstrdup(path);
STRIP_SLASH(dir);
if (cvs_server_active == 0)
cvs_log(LP_TRACE, "cvs_mkdir(%s)", dir);
rpath[0] = '\0';
for (sp = dir; sp != NULL; sp = dp) {
dp = strchr(sp, '/');
if (dp != NULL)
*(dp++) = '\0';
len = strlcat(rpath, "/", sizeof(rpath));
if (len >= (int)sizeof(rpath))
fatal("cvs_mkdir: overflow");
len = strlcat(rpath, sp, sizeof(rpath));
if (len >= (int)sizeof(rpath))
fatal("cvs_mkdir: overflow");
if (1 == len)
continue;
if (mkdir(rpath, mode) == -1 && errno != EEXIST)
fatal("cvs_mkdir: %s: %s", rpath, strerror(errno));
}
free(dir);
}
/*
* Split the contents of a file into a list of lines.
*/
struct rcs_lines *
cvs_splitlines(u_char *data, size_t len)
{
u_char *p, *c;
size_t i, tlen;
struct rcs_lines *lines;
struct rcs_line *lp;
lines = xcalloc(1, sizeof(*lines));
TAILQ_INIT(&(lines->l_lines));
lp = xcalloc(1, sizeof(*lp));
TAILQ_INSERT_TAIL(&(lines->l_lines), lp, l_list);
p = c = data;
for (i = 0; i < len; i++) {
if (*p == '\n' || (i == len - 1)) {
tlen = p - c + 1;
lp = xcalloc(1, sizeof(*lp));
lp->l_line = c;
lp->l_len = tlen;
lp->l_lineno = ++(lines->l_nblines);
TAILQ_INSERT_TAIL(&(lines->l_lines), lp, l_list);
c = p + 1;
}
p++;
}
return (lines);
}
void
cvs_freelines(struct rcs_lines *lines)
{
struct rcs_line *lp;
while ((lp = TAILQ_FIRST(&(lines->l_lines))) != NULL) {
TAILQ_REMOVE(&(lines->l_lines), lp, l_list);
if (lp->l_needsfree == 1)
free(lp->l_line);
free(lp);
}
free(lines);
}
/*
* cvs_strsplit()
*
* Split a string <str> of <sep>-separated values and allocate
* an argument vector for the values found.
*/
struct cvs_argvector *
cvs_strsplit(char *str, const char *sep)
{
struct cvs_argvector *av;
size_t i = 0;
char *cp, *p;
cp = xstrdup(str);
av = xmalloc(sizeof(*av));
av->str = cp;
av->argv = xmalloc(sizeof(*(av->argv)));
while ((p = strsep(&cp, sep)) != NULL) {
av->argv[i++] = p;
av->argv = xreallocarray(av->argv,
i + 1, sizeof(*(av->argv)));
}
av->argv[i] = NULL;
return (av);
}
/*
* cvs_argv_destroy()
*
* Free an argument vector previously allocated by cvs_strsplit().
*/
void
cvs_argv_destroy(struct cvs_argvector *av)
{
free(av->str);
free(av->argv);
free(av);
}
u_int
cvs_revision_select(RCSFILE *file, char *range)
{
int i;
u_int nrev;
char *lstr, *rstr;
struct rcs_delta *rdp;
struct cvs_argvector *revargv, *revrange;
RCSNUM *lnum, *rnum;
nrev = 0;
lnum = rnum = NULL;
revargv = cvs_strsplit(range, ",");
for (i = 0; revargv->argv[i] != NULL; i++) {
revrange = cvs_strsplit(revargv->argv[i], ":");
if (revrange->argv[0] == NULL)
fatal("invalid revision range: %s", revargv->argv[i]);
else if (revrange->argv[1] == NULL)
lstr = rstr = revrange->argv[0];
else {
if (revrange->argv[2] != NULL)
fatal("invalid revision range: %s",
revargv->argv[i]);
lstr = revrange->argv[0];
rstr = revrange->argv[1];
if (strcmp(lstr, "") == 0)
lstr = NULL;
if (strcmp(rstr, "") == 0)
rstr = NULL;
}
if (lstr == NULL)
lstr = RCS_HEAD_INIT;
if ((lnum = rcs_translate_tag(lstr, file)) == NULL)
fatal("cvs_revision_select: could not translate tag `%s'", lstr);
if (rstr != NULL) {
if ((rnum = rcs_translate_tag(rstr, file)) == NULL)
fatal("cvs_revision_select: could not translate tag `%s'", rstr);
} else {
rnum = rcsnum_alloc();
rcsnum_cpy(file->rf_head, rnum, 0);
}
cvs_argv_destroy(revrange);
TAILQ_FOREACH(rdp, &(file->rf_delta), rd_list) {
if (rcsnum_cmp(rdp->rd_num, lnum, 0) <= 0 &&
rcsnum_cmp(rdp->rd_num, rnum, 0) >= 0 &&
!(rdp->rd_flags & RCS_RD_SELECT)) {
rdp->rd_flags |= RCS_RD_SELECT;
nrev++;
}
}
free(lnum);
free(rnum);
}
cvs_argv_destroy(revargv);
return (nrev);
}
int
cvs_yesno(void)
{
int c, ret;
ret = 0;
fflush(stderr);
fflush(stdout);
if ((c = getchar()) != 'y' && c != 'Y')
ret = -1;
else
while (c != EOF && c != '\n')
c = getchar();
return (ret);
}
/*
* cvs_exec()
*
* Execute <prog> and send <in> to the STDIN if not NULL.
* If <needwait> == 1, return the result of <prog>,
* else, 0 or -1 if an error occur.
*/
int
cvs_exec(char *prog, const char *in, int needwait)
{
pid_t pid;
size_t size;
int fds[2], st;
char *argp[4] = { "sh", "-c", prog, NULL };
if (in != NULL && pipe(fds) < 0) {
cvs_log(LP_ERR, "cvs_exec: pipe failed");
return (-1);
}
if ((pid = fork()) == -1) {
cvs_log(LP_ERR, "cvs_exec: fork failed");
return (-1);
} else if (pid == 0) {
if (in != NULL) {
close(fds[1]);
dup2(fds[0], STDIN_FILENO);
}
setenv("CVSROOT", current_cvsroot->cr_dir, 1);
execv(_PATH_BSHELL, argp);
cvs_log(LP_ERR, "cvs_exec: failed to run '%s'", prog);
_exit(127);
}
if (in != NULL) {
close(fds[0]);
size = strlen(in);
if (atomicio(vwrite, fds[1], in, size) != size)
cvs_log(LP_ERR, "cvs_exec: failed to write on STDIN");
close(fds[1]);
}
if (needwait == 1) {
while (waitpid(pid, &st, 0) == -1)
;
if (!WIFEXITED(st)) {
errno = EINTR;
return (-1);
}
return (WEXITSTATUS(st));
}
return (0);
}