version 1.111, 2006/04/21 14:42:12 |
version 1.112, 2006/04/21 17:17:29 |
|
|
|
|
const char rcs_version[] = "OpenCVS RCS version 3.6"; |
const char rcs_version[] = "OpenCVS RCS version 3.6"; |
|
|
int flags; |
|
int rcsflags; |
int rcsflags; |
int rcs_optind; |
int rcs_optind; |
char *rcs_optarg; |
char *rcs_optarg; |
|
|
_exit(1); |
_exit(1); |
} |
} |
|
|
/* |
|
* Allocate an RCSNUM and store in <rev>. |
|
*/ |
|
void |
|
rcs_set_rev(const char *str, RCSNUM **rev) |
|
{ |
|
if (str == NULL || (*rev = rcsnum_parse(str)) == NULL) |
|
fatal("bad revision number '%s'", str); |
|
} |
|
|
|
/* |
|
* rcs_get_mtime() |
|
* |
|
* Get <filename> last modified time. |
|
* Returns last modified time on success, or -1 on failure. |
|
*/ |
|
time_t |
|
rcs_get_mtime(const char *filename) |
|
{ |
|
struct stat st; |
|
time_t mtime; |
|
|
|
if (stat(filename, &st) == -1) { |
|
warn("%s", filename); |
|
return (-1); |
|
} |
|
mtime = (time_t)st.st_mtimespec.tv_sec; |
|
|
|
return (mtime); |
|
} |
|
|
|
/* |
|
* rcs_set_mtime() |
|
* |
|
* Set <filename> last modified time to <mtime> if it's not set to -1. |
|
*/ |
|
void |
|
rcs_set_mtime(const char *filename, time_t mtime) |
|
{ |
|
static struct timeval tv[2]; |
|
|
|
if (mtime == -1) |
|
return; |
|
|
|
tv[0].tv_sec = mtime; |
|
tv[1].tv_sec = tv[0].tv_sec; |
|
|
|
if (utimes(filename, tv) == -1) |
|
fatal("error setting utimes: %s", strerror(errno)); |
|
} |
|
|
|
int |
int |
rcs_init(char *envstr, char **argv, int argvlen) |
rcs_init(char *envstr, char **argv, int argvlen) |
{ |
{ |
|
|
} |
} |
|
|
int |
int |
rcs_getopt(int argc, char **argv, const char *optstr) |
|
{ |
|
char *a; |
|
const char *c; |
|
static int i = 1; |
|
int opt, hasargument, ret; |
|
|
|
hasargument = 0; |
|
rcs_optarg = NULL; |
|
|
|
if (i >= argc) |
|
return (-1); |
|
|
|
a = argv[i++]; |
|
if (*a++ != '-') |
|
return (-1); |
|
|
|
ret = 0; |
|
opt = *a; |
|
for (c = optstr; *c != '\0'; c++) { |
|
if (*c == opt) { |
|
a++; |
|
ret = opt; |
|
|
|
if (*(c + 1) == ':') { |
|
if (*(c + 2) == ':') { |
|
if (*a != '\0') |
|
hasargument = 1; |
|
} else { |
|
if (*a != '\0') { |
|
hasargument = 1; |
|
} else { |
|
ret = 1; |
|
break; |
|
} |
|
} |
|
} |
|
|
|
if (hasargument == 1) |
|
rcs_optarg = a; |
|
|
|
if (ret == opt) |
|
rcs_optind++; |
|
break; |
|
} |
|
} |
|
|
|
if (ret == 0) |
|
warnx("unknown option -%c", opt); |
|
else if (ret == 1) |
|
warnx("missing argument for option -%c", opt); |
|
|
|
return (ret); |
|
} |
|
|
|
/* |
|
* rcs_choosefile() |
|
* |
|
* Given a relative filename, decide where the corresponding RCS file |
|
* should be. Tries each extension until a file is found. If no file |
|
* was found, returns a path with the first extension. |
|
* |
|
* Returns pointer to a char array on success, NULL on failure. |
|
*/ |
|
char * |
|
rcs_choosefile(const char *filename) |
|
{ |
|
struct stat sb; |
|
char *p, *ext, name[MAXPATHLEN], *next, *ptr, rcsdir[MAXPATHLEN], |
|
*ret, *suffixes, rcspath[MAXPATHLEN]; |
|
|
|
/* If -x flag was not given, use default. */ |
|
if (rcs_suffixes == NULL) |
|
rcs_suffixes = RCS_DEFAULT_SUFFIX; |
|
|
|
/* |
|
* If `filename' contains a directory, `rcspath' contains that |
|
* directory, including a trailing slash. Otherwise `rcspath' |
|
* contains an empty string. |
|
*/ |
|
if (strlcpy(rcspath, filename, sizeof(rcspath)) >= sizeof(rcspath)) |
|
return (NULL); |
|
/* If `/' is found, end string after `/'. */ |
|
if ((ptr = strrchr(rcspath, '/')) != NULL) |
|
*(++ptr) = '\0'; |
|
else |
|
rcspath[0] = '\0'; |
|
|
|
/* Append RCS/ to `rcspath' if it exists. */ |
|
if (strlcpy(rcsdir, rcspath, sizeof(rcsdir)) >= sizeof(rcsdir) || |
|
strlcat(rcsdir, RCSDIR, sizeof(rcsdir)) >= sizeof(rcsdir)) |
|
return (NULL); |
|
if (stat(rcsdir, &sb) == 0 && (sb.st_mode & S_IFDIR)) |
|
if (strlcpy(rcspath, rcsdir, sizeof(rcspath)) >= sizeof(rcspath) || |
|
strlcat(rcspath, "/", sizeof(rcspath)) >= sizeof(rcspath)) |
|
return (NULL); |
|
|
|
/* Name of file without path. */ |
|
if ((ptr = strrchr(filename, '/')) == NULL) { |
|
if (strlcpy(name, filename, sizeof(name)) >= sizeof(name)) |
|
return (NULL); |
|
} else { |
|
/* Skip `/'. */ |
|
if (strlcpy(name, ptr + 1, sizeof(name)) >= sizeof(name)) |
|
return (NULL); |
|
} |
|
|
|
/* Name of RCS file without an extension. */ |
|
if (strlcat(rcspath, name, sizeof(rcspath)) >= sizeof(rcspath)) |
|
return (NULL); |
|
|
|
/* |
|
* If only the empty suffix was given, use existing rcspath. |
|
* This ensures that there is at least one suffix for strsep(). |
|
*/ |
|
if (strcmp(rcs_suffixes, "") == 0) { |
|
ret = xstrdup(rcspath); |
|
return (ret); |
|
} |
|
|
|
/* |
|
* Cycle through slash-separated `rcs_suffixes', appending each |
|
* extension to `rcspath' and testing if the file exists. If it |
|
* does, return that string. Otherwise return path with first |
|
* extension. |
|
*/ |
|
suffixes = xstrdup(rcs_suffixes); |
|
for (ret = NULL, next = suffixes; (ext = strsep(&next, "/")) != NULL;) { |
|
char fpath[MAXPATHLEN]; |
|
|
|
if ((p = strrchr(rcspath, ',')) != NULL) { |
|
if (!strcmp(p, ext)) { |
|
if (stat(rcspath, &sb) == 0) { |
|
ret = xstrdup(rcspath); |
|
goto out; |
|
} |
|
} |
|
|
|
continue; |
|
} |
|
|
|
/* Construct RCS file path. */ |
|
if (strlcpy(fpath, rcspath, sizeof(fpath)) >= sizeof(fpath) || |
|
strlcat(fpath, ext, sizeof(fpath)) >= sizeof(fpath)) |
|
goto out; |
|
|
|
/* Don't use `filename' as RCS file. */ |
|
if (strcmp(fpath, filename) == 0) |
|
continue; |
|
|
|
if (stat(fpath, &sb) == 0) { |
|
ret = xstrdup(fpath); |
|
goto out; |
|
} |
|
} |
|
|
|
/* |
|
* `ret' is still NULL. No RCS file with any extension exists |
|
* so we use the first extension. |
|
* |
|
* `suffixes' should now be NUL separated, so the first |
|
* extension can be read just by reading `suffixes'. |
|
*/ |
|
if (strlcat(rcspath, suffixes, sizeof(rcspath)) >= |
|
sizeof(rcspath)) |
|
goto out; |
|
ret = xstrdup(rcspath); |
|
|
|
out: |
|
/* `ret' may be NULL, which indicates an error. */ |
|
xfree(suffixes); |
|
return (ret); |
|
} |
|
|
|
/* |
|
* Find the name of an RCS file, given a file name `fname'. If an RCS |
|
* file is found, the name is copied to the `len' sized buffer `out'. |
|
* Returns 0 if RCS file was found, -1 otherwise. |
|
*/ |
|
int |
|
rcs_statfile(char *fname, char *out, size_t len) |
|
{ |
|
struct stat st; |
|
char *rcspath; |
|
|
|
if ((rcspath = rcs_choosefile(fname)) == NULL) |
|
fatal("rcs_statfile: path truncation"); |
|
|
|
/* Error out if file not found and we are not creating one. */ |
|
if (stat(rcspath, &st) == -1 && !(flags & RCS_CREATE)) { |
|
if (strcmp(__progname, "rcsclean") != 0 && |
|
strcmp(__progname, "ci") != 0) |
|
warn("%s", rcspath); |
|
xfree(rcspath); |
|
return (-1); |
|
} |
|
|
|
if (strlcpy(out, rcspath, len) >= len) |
|
fatal("rcs_statfile: path truncation"); |
|
|
|
xfree(rcspath); |
|
|
|
return (0); |
|
} |
|
|
|
/* |
|
* Set <str> to <new_str>. Print warning if <str> is redefined. |
|
*/ |
|
void |
|
rcs_setrevstr(char **str, char *new_str) |
|
{ |
|
if (new_str == NULL) |
|
return; |
|
if (*str != NULL) |
|
warnx("redefinition of revision number"); |
|
*str = new_str; |
|
} |
|
|
|
/* |
|
* Set <str1> or <str2> to <new_str>, depending on which is not set. |
|
* If both are set, error out. |
|
*/ |
|
void |
|
rcs_setrevstr2(char **str1, char **str2, char *new_str) |
|
{ |
|
if (new_str == NULL) |
|
return; |
|
if (*str1 == NULL) |
|
*str1 = new_str; |
|
else if (*str2 == NULL) |
|
*str2 = new_str; |
|
else |
|
fatal("too many revision numbers"); |
|
} |
|
|
|
/* |
|
* Get revision from file. The revision can be specified as a symbol or |
|
* a revision number. |
|
*/ |
|
RCSNUM * |
|
rcs_getrevnum(const char *rev_str, RCSFILE *file) |
|
{ |
|
RCSNUM *rev; |
|
|
|
/* Search for symbol. */ |
|
rev = rcs_sym_getrev(file, rev_str); |
|
|
|
/* Search for revision number. */ |
|
if (rev == NULL) |
|
rev = rcsnum_parse(rev_str); |
|
|
|
return (rev); |
|
} |
|
|
|
int |
|
main(int argc, char **argv) |
main(int argc, char **argv) |
{ |
{ |
u_int i; |
u_int i; |
|
|
int |
int |
rcs_main(int argc, char **argv) |
rcs_main(int argc, char **argv) |
{ |
{ |
int i, j, ch, kflag, lkmode; |
int i, j, ch, flags, kflag, lkmode; |
char fpath[MAXPATHLEN], ofpath[MAXPATHLEN]; |
char fpath[MAXPATHLEN], ofpath[MAXPATHLEN]; |
char *logstr, *logmsg, *nflag, *descfile; |
char *logstr, *logmsg, *nflag, *descfile; |
char *alist, *comment, *elist, *lrev, *urev, *orange; |
char *alist, *comment, *elist, *lrev, *urev, *orange; |
|
|
while ((ch = rcs_getopt(argc, argv, RCSPROG_OPTSTRING)) != -1) { |
while ((ch = rcs_getopt(argc, argv, RCSPROG_OPTSTRING)) != -1) { |
switch (ch) { |
switch (ch) { |
case 'A': |
case 'A': |
if (rcs_statfile(rcs_optarg, ofpath, sizeof(ofpath)) < 0) |
if (rcs_statfile(rcs_optarg, ofpath, |
|
sizeof(ofpath), flags) < 0) |
exit(1); |
exit(1); |
rcsflags |= CO_ACLAPPEND; |
rcsflags |= CO_ACLAPPEND; |
break; |
break; |
|
|
} |
} |
|
|
for (i = 0; i < argc; i++) { |
for (i = 0; i < argc; i++) { |
if (rcs_statfile(argv[i], fpath, sizeof(fpath)) < 0) |
if (rcs_statfile(argv[i], fpath, sizeof(fpath), flags) < 0) |
continue; |
continue; |
|
|
if (!(rcsflags & QUIET)) |
if (!(rcsflags & QUIET)) |
|
|
|
|
rcs_desc_set(file, content); |
rcs_desc_set(file, content); |
xfree(content); |
xfree(content); |
} |
|
|
|
/* |
|
* Prompt for and store user's input in an allocated string. |
|
* |
|
* Returns the string's pointer. |
|
*/ |
|
char * |
|
rcs_prompt(const char *prompt) |
|
{ |
|
BUF *bp; |
|
size_t len; |
|
char *buf; |
|
|
|
bp = cvs_buf_alloc(0, BUF_AUTOEXT); |
|
if (isatty(STDIN_FILENO)) |
|
(void)fprintf(stderr, "%s", prompt); |
|
if (isatty(STDIN_FILENO)) |
|
(void)fprintf(stderr, ">> "); |
|
while ((buf = fgetln(stdin, &len)) != NULL) { |
|
/* The last line may not be EOL terminated. */ |
|
if (buf[0] == '.' && (len == 1 || buf[1] == '\n')) |
|
break; |
|
else |
|
cvs_buf_append(bp, buf, len); |
|
|
|
if (isatty(STDIN_FILENO)) |
|
(void)fprintf(stderr, ">> "); |
|
} |
|
cvs_buf_putc(bp, '\0'); |
|
|
|
return (cvs_buf_release(bp)); |
|
} |
|
|
|
u_int |
|
rcs_rev_select(RCSFILE *file, char *range) |
|
{ |
|
int i; |
|
u_int nrev; |
|
char *ep; |
|
char *lstr, *rstr; |
|
struct rcs_delta *rdp; |
|
struct cvs_argvector *revargv, *revrange; |
|
RCSNUM lnum, rnum; |
|
|
|
nrev = 0; |
|
(void)memset(&lnum, 0, sizeof(lnum)); |
|
(void)memset(&rnum, 0, sizeof(rnum)); |
|
|
|
if (range == NULL) { |
|
TAILQ_FOREACH(rdp, &file->rf_delta, rd_list) |
|
if (rcsnum_cmp(rdp->rd_num, file->rf_head, 0) == 0) { |
|
rdp->rd_flags |= RCS_RD_SELECT; |
|
return (1); |
|
} |
|
return (0); |
|
} |
|
|
|
revargv = cvs_strsplit(range, ","); |
|
for (i = 0; revargv->argv[i] != NULL; i++) { |
|
revrange = cvs_strsplit(revargv->argv[i], ":"); |
|
if (revrange->argv[0] == NULL) |
|
/* should not happen */ |
|
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 (rcsnum_aton(lstr, &ep, &lnum) == 0 || (*ep != '\0')) |
|
fatal("invalid revision: %s", lstr); |
|
|
|
if (rstr != NULL) { |
|
if (rcsnum_aton(rstr, &ep, &rnum) == 0 || (*ep != '\0')) |
|
fatal("invalid revision: %s", rstr); |
|
} else |
|
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++; |
|
} |
|
} |
|
cvs_argv_destroy(revargv); |
|
|
|
if (lnum.rn_id != NULL) |
|
xfree(lnum.rn_id); |
|
if (rnum.rn_id != NULL) |
|
xfree(rnum.rn_id); |
|
|
|
return (nrev); |
|
} |
} |