version 1.171, 2006/05/01 18:17:39 |
version 1.172, 2006/05/27 03:30:31 |
|
|
|
|
#include "includes.h" |
#include "includes.h" |
|
|
|
#include "buf.h" |
#include "cvs.h" |
#include "cvs.h" |
|
#include "diff.h" |
#include "log.h" |
#include "log.h" |
#include "rcs.h" |
#include "rcs.h" |
#include "diff.h" |
#include "util.h" |
|
#include "xmalloc.h" |
|
|
#define RCS_BUFSIZE 16384 |
#define RCS_BUFSIZE 16384 |
#define RCS_BUFEXTSIZE 8192 |
#define RCS_BUFEXTSIZE 8192 |
#define RCS_KWEXP_SIZE 1024 |
#define RCS_KWEXP_SIZE 1024 |
|
|
|
|
/* RCS token types */ |
/* RCS token types */ |
#define RCS_TOK_ERR -1 |
#define RCS_TOK_ERR -1 |
#define RCS_TOK_EOF 0 |
#define RCS_TOK_EOF 0 |
|
|
#define RCS_TOK_SCOLON 4 |
#define RCS_TOK_SCOLON 4 |
#define RCS_TOK_COLON 5 |
#define RCS_TOK_COLON 5 |
|
|
|
|
#define RCS_TOK_HEAD 8 |
#define RCS_TOK_HEAD 8 |
#define RCS_TOK_BRANCH 9 |
#define RCS_TOK_BRANCH 9 |
#define RCS_TOK_ACCESS 10 |
#define RCS_TOK_ACCESS 10 |
|
|
|
|
#define RCS_ISKEY(t) (((t) >= RCS_TOK_HEAD) && ((t) <= RCS_TOK_BRANCHES)) |
#define RCS_ISKEY(t) (((t) >= RCS_TOK_HEAD) && ((t) <= RCS_TOK_BRANCHES)) |
|
|
|
|
#define RCS_NOSCOL 0x01 /* no terminating semi-colon */ |
#define RCS_NOSCOL 0x01 /* no terminating semi-colon */ |
#define RCS_VOPT 0x02 /* value is optional */ |
#define RCS_VOPT 0x02 /* value is optional */ |
|
|
|
|
/* opaque parse data */ |
/* opaque parse data */ |
struct rcs_pdata { |
struct rcs_pdata { |
u_int rp_lines; |
u_int rp_lines; |
|
|
FILE *rp_file; |
FILE *rp_file; |
}; |
}; |
|
|
|
|
#define RCS_TOKSTR(rfp) ((struct rcs_pdata *)rfp->rf_pdata)->rp_buf |
#define RCS_TOKSTR(rfp) ((struct rcs_pdata *)rfp->rf_pdata)->rp_buf |
#define RCS_TOKLEN(rfp) ((struct rcs_pdata *)rfp->rf_pdata)->rp_tlen |
#define RCS_TOKLEN(rfp) ((struct rcs_pdata *)rfp->rf_pdata)->rp_tlen |
|
|
|
|
/* invalid characters in RCS symbol names */ |
/* invalid characters in RCS symbol names */ |
static const char rcs_sym_invch[] = RCS_SYM_INVALCHAR; |
static const char rcs_sym_invch[] = RCS_SYM_INVALCHAR; |
|
|
|
|
/* comment leaders, depending on the file's suffix */ |
/* comment leaders, depending on the file's suffix */ |
static const struct rcs_comment { |
static const struct rcs_comment { |
const char *rc_suffix; |
const char *rc_suffix; |
|
|
|
|
#define NB_COMTYPES (sizeof(rcs_comments)/sizeof(rcs_comments[0])) |
#define NB_COMTYPES (sizeof(rcs_comments)/sizeof(rcs_comments[0])) |
|
|
#ifdef notyet |
|
static struct rcs_kfl { |
|
char rk_char; |
|
int rk_val; |
|
} rcs_kflags[] = { |
|
{ 'k', RCS_KWEXP_NAME }, |
|
{ 'v', RCS_KWEXP_VAL }, |
|
{ 'l', RCS_KWEXP_LKR }, |
|
{ 'o', RCS_KWEXP_OLD }, |
|
{ 'b', RCS_KWEXP_NONE }, |
|
}; |
|
#endif |
|
|
|
static struct rcs_key { |
static struct rcs_key { |
char rk_str[16]; |
char rk_str[16]; |
int rk_id; |
int rk_id; |
|
|
|
|
#define RCS_NKEYS (sizeof(rcs_keys)/sizeof(rcs_keys[0])) |
#define RCS_NKEYS (sizeof(rcs_keys)/sizeof(rcs_keys[0])) |
|
|
|
|
static const char *rcs_errstrs[] = { |
static const char *rcs_errstrs[] = { |
"No error", |
"No error", |
"No such entry", |
"No such entry", |
|
|
|
|
#define RCS_NERR (sizeof(rcs_errstrs)/sizeof(rcs_errstrs[0])) |
#define RCS_NERR (sizeof(rcs_errstrs)/sizeof(rcs_errstrs[0])) |
|
|
|
|
int rcs_errno = RCS_ERR_NOERR; |
int rcs_errno = RCS_ERR_NOERR; |
char *timezone_flag = NULL; |
|
|
|
|
int rcs_patch_lines(struct cvs_lines *, struct cvs_lines *); |
|
static int rcs_movefile(char *, char *, mode_t, u_int); |
static void rcs_parse_init(RCSFILE *); |
static void rcs_parse_init(RCSFILE *); |
static int rcs_parse_admin(RCSFILE *); |
static int rcs_parse_admin(RCSFILE *); |
static int rcs_parse_delta(RCSFILE *); |
static int rcs_parse_delta(RCSFILE *); |
|
|
static char* rcs_expand_keywords(char *, struct rcs_delta *, char *, |
static char* rcs_expand_keywords(char *, struct rcs_delta *, char *, |
size_t, int); |
size_t, int); |
|
|
/* |
|
* rcs_open() |
|
* |
|
* Open a file containing RCS-formatted information. The file's path is |
|
* given in <path>, and the opening flags are given in <flags>, which is either |
|
* RCS_READ, RCS_WRITE, or RCS_RDWR. If the open requests write access and |
|
* the file does not exist, the RCS_CREATE flag must also be given, in which |
|
* case it will be created with the mode specified in a third argument of |
|
* type mode_t. If the file exists and RCS_CREATE is passed, the open will |
|
* fail. |
|
* Returns a handle to the opened file on success, or NULL on failure. |
|
*/ |
|
RCSFILE * |
RCSFILE * |
rcs_open(const char *path, int flags, ...) |
rcs_open(const char *path, int fd, int flags, ...) |
{ |
{ |
int ret, mode; |
int mode; |
mode_t fmode; |
mode_t fmode; |
RCSFILE *rfp; |
RCSFILE *rfp; |
struct stat st; |
|
va_list vap; |
va_list vap; |
struct rcs_delta *rdp; |
struct rcs_delta *rdp; |
struct rcs_lock *lkr; |
struct rcs_lock *lkr; |
|
|
fmode = S_IRUSR|S_IRGRP|S_IROTH; |
fmode = S_IRUSR|S_IRGRP|S_IROTH; |
flags &= 0xffff; /* ditch any internal flags */ |
flags &= 0xffff; /* ditch any internal flags */ |
|
|
if (((ret = stat(path, &st)) == -1) && errno == ENOENT) { |
if (flags & RCS_CREATE) { |
if (flags & RCS_CREATE) { |
va_start(vap, flags); |
va_start(vap, flags); |
mode = va_arg(vap, int); |
mode = va_arg(vap, int); |
va_end(vap); |
va_end(vap); |
fmode = (mode_t)mode; |
fmode = (mode_t)mode; |
|
} else { |
|
/* XXX, make this command dependant? */ |
|
#if 0 |
|
cvs_log(LP_ERR, "RCS file `%s' does not exist", path); |
|
#endif |
|
rcs_errno = RCS_ERR_NOENT; |
|
return (NULL); |
|
} |
|
} else if (ret == 0 && (flags & RCS_CREATE)) { |
|
cvs_log(LP_ERR, "RCS file `%s' exists", path); |
|
return (NULL); |
|
} |
} |
|
|
rfp = xcalloc(1, sizeof(*rfp)); |
rfp = xcalloc(1, sizeof(*rfp)); |
|
|
rfp->rf_path = xstrdup(path); |
rfp->rf_path = xstrdup(path); |
rfp->rf_flags = flags | RCS_SLOCK | RCS_SYNCED; |
rfp->rf_flags = flags | RCS_SLOCK | RCS_SYNCED; |
rfp->rf_mode = fmode; |
rfp->rf_mode = fmode; |
|
rfp->fd = fd; |
|
|
TAILQ_INIT(&(rfp->rf_delta)); |
TAILQ_INIT(&(rfp->rf_delta)); |
TAILQ_INIT(&(rfp->rf_access)); |
TAILQ_INIT(&(rfp->rf_access)); |
|
|
* |
* |
* Write the contents of the RCS file handle <rfp> to disk in the file whose |
* Write the contents of the RCS file handle <rfp> to disk in the file whose |
* path is in <rf_path>. |
* path is in <rf_path>. |
* Returns 0 on success, or -1 on failure. |
|
*/ |
*/ |
int |
void |
rcs_write(RCSFILE *rfp) |
rcs_write(RCSFILE *rfp) |
{ |
{ |
FILE *fp; |
FILE *fp; |
char buf[1024], numbuf[64], fn[19] = ""; |
char buf[1024], numbuf[64], *fn; |
void *bp; |
|
struct rcs_access *ap; |
struct rcs_access *ap; |
struct rcs_sym *symp; |
struct rcs_sym *symp; |
struct rcs_branch *brp; |
struct rcs_branch *brp; |
struct rcs_delta *rdp; |
struct rcs_delta *rdp; |
struct rcs_lock *lkp; |
struct rcs_lock *lkp; |
ssize_t nread, nwritten; |
|
size_t len; |
size_t len; |
int fd, from_fd, to_fd; |
int fd, from_fd, to_fd; |
|
|
from_fd = to_fd = fd = -1; |
from_fd = to_fd = fd = -1; |
|
|
if (rfp->rf_flags & RCS_SYNCED) |
if (rfp->rf_flags & RCS_SYNCED) |
return (0); |
return; |
|
|
/* Write operations need the whole file parsed */ |
/* Write operations need the whole file parsed */ |
rcs_parse_deltatexts(rfp, NULL); |
rcs_parse_deltatexts(rfp, NULL); |
|
|
strlcpy(fn, "/tmp/rcs.XXXXXXXXXX", sizeof(fn)); |
(void)xasprintf(&fn, "%s/rcs.XXXXXXXXXX", cvs_tmpdir); |
|
|
if ((fd = mkstemp(fn)) == -1) |
if ((fd = mkstemp(fn)) == -1) |
fatal("mkstemp: `%s': %s", fn, strerror(errno)); |
fatal("%s", fn); |
|
|
if ((fp = fdopen(fd, "w+")) == NULL) { |
if ((fp = fdopen(fd, "w+")) == NULL) { |
fd = errno; |
int saved_errno; |
unlink(fn); |
|
fatal("fdopen: %s", strerror(fd)); |
saved_errno = errno; |
|
(void)unlink(fn); |
|
errno = saved_errno; |
|
fatal("%s", fn); |
} |
} |
|
|
if (rfp->rf_head != NULL) |
if (rfp->rf_head != NULL) |
|
|
fprintf(fp, "symbols"); |
fprintf(fp, "symbols"); |
TAILQ_FOREACH(symp, &(rfp->rf_symbols), rs_list) { |
TAILQ_FOREACH(symp, &(rfp->rf_symbols), rs_list) { |
rcsnum_tostr(symp->rs_num, numbuf, sizeof(numbuf)); |
rcsnum_tostr(symp->rs_num, numbuf, sizeof(numbuf)); |
strlcpy(buf, symp->rs_name, sizeof(buf)); |
if (strlcpy(buf, symp->rs_name, sizeof(buf)) >= sizeof(buf) || |
strlcat(buf, ":", sizeof(buf)); |
strlcat(buf, ":", sizeof(buf)) >= sizeof(buf) || |
strlcat(buf, numbuf, sizeof(buf)); |
strlcat(buf, numbuf, sizeof(buf)) >= sizeof(buf)) |
|
fatal("rcs_write: string overflow"); |
fprintf(fp, "\n\t%s", buf); |
fprintf(fp, "\n\t%s", buf); |
} |
} |
fprintf(fp, ";\n"); |
fprintf(fp, ";\n"); |
|
|
} |
} |
fputs("@\n", fp); |
fputs("@\n", fp); |
} |
} |
fclose(fp); |
(void)fclose(fp); |
|
|
/* |
if (rcs_movefile(fn, rfp->rf_path, rfp->rf_mode, rfp->rf_flags) == -1) { |
* We try to use rename() to atomically put the new file in place. |
(void)unlink(fn); |
* If that fails, we try a copy. |
fatal("rcs_movefile failed"); |
*/ |
} |
if (rename(fn, rfp->rf_path) == -1) { |
|
if (errno == EXDEV) { |
|
/* rename() not supported so we have to copy. */ |
|
if (chmod(rfp->rf_path, S_IWUSR) == -1 && |
|
!(rfp->rf_flags & RCS_CREATE)) { |
|
fatal("chmod(%s, 0%o) failed", |
|
rfp->rf_path, S_IWUSR); |
|
} |
|
|
|
if ((from_fd = open(fn, O_RDONLY)) == -1) { |
rfp->rf_flags |= RCS_SYNCED; |
cvs_log(LP_ERRNO, "failed to open `%s'", |
|
rfp->rf_path); |
|
return (-1); |
|
} |
|
|
|
if ((to_fd = open(rfp->rf_path, |
if (fn != NULL) |
O_WRONLY|O_TRUNC|O_CREAT)) == -1) { |
xfree(fn); |
cvs_log(LP_ERRNO, "failed to open `%s'", fn); |
} |
close(from_fd); |
|
return (-1); |
|
} |
|
|
|
bp = xmalloc(MAXBSIZE); |
/* |
for (;;) { |
* rcs_movefile() |
if ((nread = read(from_fd, bp, MAXBSIZE)) == 0) |
* |
break; |
* Move a file using rename(2) if possible and copying if not. |
if (nread == -1) |
* Returns 0 on success, -1 on failure. |
goto err; |
*/ |
nwritten = write(to_fd, bp, (size_t)nread); |
static int |
if (nwritten == -1 || nwritten != nread) |
rcs_movefile(char *from, char *to, mode_t perm, u_int to_flags) |
goto err; |
{ |
} |
FILE *src, *dst; |
|
size_t nread, nwritten; |
|
char *buf; |
|
int ret; |
|
|
if (nread < 0) { |
ret = -1; |
err: if (unlink(rfp->rf_path) == -1) |
|
cvs_log(LP_ERRNO, |
|
"failed to unlink `%s'", |
|
rfp->rf_path); |
|
close(from_fd); |
|
close(to_fd); |
|
xfree(bp); |
|
return (-1); |
|
} |
|
|
|
close(from_fd); |
if (rename(from, to) == 0) { |
close(to_fd); |
if (chmod(to, perm) == -1) { |
xfree(bp); |
cvs_log(LP_ERRNO, "%s", to); |
|
|
if (unlink(fn) == -1) { |
|
cvs_log(LP_ERRNO, |
|
"failed to unlink `%s'", fn); |
|
return (-1); |
|
} |
|
} else { |
|
cvs_log(LP_ERRNO, |
|
"failed to access temp RCS output file"); |
|
return (-1); |
return (-1); |
} |
} |
|
return (0); |
|
} else if (errno != EXDEV) { |
|
cvs_log(LP_NOTICE, "failed to access temp RCS output file"); |
|
return (-1); |
} |
} |
|
|
if (chmod(rfp->rf_path, rfp->rf_mode) == -1) { |
if ((chmod(to, S_IWUSR) == -1) && !(to_flags & RCS_CREATE)) { |
cvs_log(LP_ERRNO, "failed to chmod `%s'", |
cvs_log(LP_ERR, "chmod(%s, 0%o) failed", to, S_IWUSR); |
rfp->rf_path); |
|
return (-1); |
return (-1); |
} |
} |
|
|
rfp->rf_flags |= RCS_SYNCED; |
/* different filesystem, have to copy the file */ |
|
if ((src = fopen(from, "r")) == NULL) { |
|
cvs_log(LP_ERRNO, "%s", from); |
|
return (-1); |
|
} |
|
if ((dst = fopen(to, "w")) == NULL) { |
|
cvs_log(LP_ERRNO, "%s", to); |
|
return (-1); |
|
} |
|
if (fchmod(fileno(dst), perm)) { |
|
cvs_log(LP_ERR, "%s", to); |
|
(void)unlink(to); |
|
return (-1); |
|
} |
|
|
return (0); |
buf = xmalloc(MAXBSIZE); |
|
while ((nread = fread(buf, sizeof(char), MAXBSIZE, src)) != 0) { |
|
if (ferror(src)) { |
|
cvs_log(LP_ERRNO, "failed to read `%s'", from); |
|
(void)unlink(to); |
|
goto out; |
|
} |
|
nwritten = fwrite(buf, sizeof(char), nread, dst); |
|
if (nwritten != nread) { |
|
cvs_log(LP_ERRNO, "failed to write `%s'", to); |
|
(void)unlink(to); |
|
goto out; |
|
} |
|
} |
|
|
|
ret = 0; |
|
|
|
(void)fclose(src); |
|
(void)fclose(dst); |
|
(void)unlink(from); |
|
|
|
out: |
|
xfree(buf); |
|
|
|
return (ret); |
} |
} |
|
|
/* |
/* |
|
|
int |
int |
rcs_rev_remove(RCSFILE *rf, RCSNUM *rev) |
rcs_rev_remove(RCSFILE *rf, RCSNUM *rev) |
{ |
{ |
size_t len; |
char *newdeltatext, *path_tmp1, *path_tmp2; |
char *tmpdir; |
|
char *newdeltatext, path_tmp1[MAXPATHLEN], path_tmp2[MAXPATHLEN]; |
|
struct rcs_delta *rdp, *prevrdp, *nextrdp; |
struct rcs_delta *rdp, *prevrdp, *nextrdp; |
BUF *nextbuf, *prevbuf, *newdiff; |
BUF *nextbuf, *prevbuf, *newdiff; |
|
|
#if defined(RCSPROG) |
|
tmpdir = rcs_tmpdir; |
|
#else |
|
tmpdir = cvs_tmpdir; |
|
#endif |
|
|
|
if (rev == RCS_HEAD_REV) |
if (rev == RCS_HEAD_REV) |
rev = rf->rf_head; |
rev = rf->rf_head; |
|
|
|
|
newdiff = cvs_buf_alloc(64, BUF_AUTOEXT); |
newdiff = cvs_buf_alloc(64, BUF_AUTOEXT); |
|
|
/* calculate new diff */ |
/* calculate new diff */ |
len = strlcpy(path_tmp1, tmpdir, sizeof(path_tmp1)); |
(void)xasprintf(&path_tmp1, "%s/diff1.XXXXXXXXXX", cvs_tmpdir); |
if (len >= sizeof(path_tmp1)) |
cvs_buf_write_stmp(nextbuf, path_tmp1, 0600, NULL); |
fatal("path truncation in rcs_rev_remove"); |
|
|
|
len = strlcat(path_tmp1, "/diff1.XXXXXXXXXX", |
|
sizeof(path_tmp1)); |
|
if (len >= sizeof(path_tmp1)) |
|
fatal("path truncation in rcs_rev_remove"); |
|
|
|
cvs_buf_write_stmp(nextbuf, path_tmp1, 0600); |
|
cvs_buf_free(nextbuf); |
cvs_buf_free(nextbuf); |
|
|
len = strlcpy(path_tmp2, tmpdir, sizeof(path_tmp2)); |
(void)xasprintf(&path_tmp2, "%s/diff2.XXXXXXXXXX", cvs_tmpdir); |
if (len >= sizeof(path_tmp2)) |
cvs_buf_write_stmp(prevbuf, path_tmp2, 0600, NULL); |
fatal("path truncation in rcs_rev_remove"); |
|
|
|
len = strlcat(path_tmp2, "/diff2.XXXXXXXXXX", |
|
sizeof(path_tmp2)); |
|
if (len >= sizeof(path_tmp2)) |
|
fatal("path truncation in rcs_rev_remove"); |
|
|
|
cvs_buf_write_stmp(prevbuf, path_tmp2, 0600); |
|
cvs_buf_free(prevbuf); |
cvs_buf_free(prevbuf); |
|
|
diff_format = D_RCSDIFF; |
diff_format = D_RCSDIFF; |
cvs_diffreg(path_tmp1, path_tmp2, newdiff); |
if (cvs_diffreg(path_tmp1, path_tmp2, newdiff) == D_ERROR) |
|
fatal("rcs_diffreg failed"); |
|
|
newdeltatext = cvs_buf_release(newdiff); |
newdeltatext = cvs_buf_release(newdiff); |
} else if (nextrdp == NULL && prevrdp != NULL) { |
} else if (nextrdp == NULL && prevrdp != NULL) { |
|
|
if (newdeltatext != NULL) |
if (newdeltatext != NULL) |
xfree(newdeltatext); |
xfree(newdeltatext); |
|
|
|
if (path_tmp1 != NULL) |
|
xfree(path_tmp1); |
|
if (path_tmp2 != NULL) |
|
xfree(path_tmp2); |
|
|
return (0); |
return (0); |
} |
} |
|
|
|
|
pdp->rp_lines = 0; |
pdp->rp_lines = 0; |
pdp->rp_pttype = RCS_TOK_ERR; |
pdp->rp_pttype = RCS_TOK_ERR; |
|
|
if ((pdp->rp_file = fopen(rfp->rf_path, "r")) == NULL) |
if ((pdp->rp_file = fdopen(rfp->fd, "r")) == NULL) |
fatal("fopen: `%s': %s", rfp->rf_path, strerror(errno)); |
fatal("fopen: `%s'", rfp->rf_path); |
|
|
pdp->rp_buf = xmalloc((size_t)RCS_BUFSIZE); |
pdp->rp_buf = xmalloc((size_t)RCS_BUFSIZE); |
pdp->rp_blen = RCS_BUFSIZE; |
pdp->rp_blen = RCS_BUFSIZE; |
|
|
break; |
break; |
default: |
default: |
rcs_errno = RCS_ERR_PARSE; |
rcs_errno = RCS_ERR_PARSE; |
cvs_log(LP_ERR, |
cvs_log(LP_ERR, "unexpected token `%s' in RCS delta", |
"unexpected token `%s' in RCS delta", |
|
RCS_TOKSTR(rfp)); |
RCS_TOKSTR(rfp)); |
rcs_freedelta(rdp); |
rcs_freedelta(rdp); |
return (-1); |
return (-1); |
|
|
} |
} |
|
|
rdp->rd_text = xmalloc(RCS_TOKLEN(rfp) + 1); |
rdp->rd_text = xmalloc(RCS_TOKLEN(rfp) + 1); |
strlcpy(rdp->rd_text, RCS_TOKSTR(rfp), (RCS_TOKLEN(rfp) + 1)); |
if (strlcpy(rdp->rd_text, RCS_TOKSTR(rfp), (RCS_TOKLEN(rfp) + 1)) >= |
|
RCS_TOKLEN(rfp) + 1) |
|
fatal("rcs_parse_deltatext: strlcpy"); |
rdp->rd_tlen = RCS_TOKLEN(rfp); |
rdp->rd_tlen = RCS_TOKLEN(rfp); |
|
|
return (1); |
return (1); |
|
|
|
|
if (pdp->rp_pttype != RCS_TOK_ERR) { |
if (pdp->rp_pttype != RCS_TOK_ERR) { |
type = pdp->rp_pttype; |
type = pdp->rp_pttype; |
strlcpy(pdp->rp_buf, pdp->rp_ptok, pdp->rp_blen); |
if (strlcpy(pdp->rp_buf, pdp->rp_ptok, pdp->rp_blen) >= |
|
pdp->rp_blen) |
|
fatal("rcs_gettok: strlcpy"); |
pdp->rp_pttype = RCS_TOK_ERR; |
pdp->rp_pttype = RCS_TOK_ERR; |
return (type); |
return (type); |
} |
} |
|
|
return (-1); |
return (-1); |
|
|
pdp->rp_pttype = type; |
pdp->rp_pttype = type; |
strlcpy(pdp->rp_ptok, tok, sizeof(pdp->rp_ptok)); |
if (strlcpy(pdp->rp_ptok, tok, sizeof(pdp->rp_ptok)) >= |
|
sizeof(pdp->rp_ptok)) |
|
fatal("rcs_pushtok: strlcpy"); |
return (0); |
return (0); |
} |
} |
|
|
|
|
i = 0; |
i = 0; |
|
|
/* |
/* |
* -z support for RCS |
|
*/ |
|
tb = rdp->rd_date; |
|
if (timezone_flag != NULL) |
|
rcs_set_tz(timezone_flag, rdp, &tb); |
|
|
|
/* |
|
* Keyword formats: |
* Keyword formats: |
* $Keyword$ |
* $Keyword$ |
* $Keyword: value$ |
* $Keyword: value$ |
|
|
expbuf[0] = '\0'; |
expbuf[0] = '\0'; |
|
|
if (mode & RCS_KWEXP_NAME) { |
if (mode & RCS_KWEXP_NAME) { |
strlcat(expbuf, "$", sizeof(expbuf)); |
if (strlcat(expbuf, "$", sizeof(expbuf)) |
strlcat(expbuf, kwstr, sizeof(expbuf)); |
>= sizeof(expbuf) || |
if (mode & RCS_KWEXP_VAL) |
strlcat(expbuf, kwstr, sizeof(expbuf)) |
strlcat(expbuf, ": ", sizeof(expbuf)); |
>= sizeof(expbuf)) |
|
fatal("rcs_expand_keywords: truncated"); |
|
if ((mode & RCS_KWEXP_VAL) && |
|
strlcat(expbuf, ": ", sizeof(expbuf)) |
|
>= sizeof(expbuf)) |
|
fatal("rcs_expand_keywords: truncated"); |
} |
} |
|
|
/* |
/* |
|
|
if (mode & RCS_KWEXP_VAL) { |
if (mode & RCS_KWEXP_VAL) { |
if (kwtype & RCS_KW_RCSFILE) { |
if (kwtype & RCS_KW_RCSFILE) { |
if (!(kwtype & RCS_KW_FULLPATH)) |
if (!(kwtype & RCS_KW_FULLPATH)) |
strlcat(expbuf, |
(void)strlcat(expbuf, |
basename(rcsfile), |
basename(rcsfile), |
sizeof(expbuf)); |
sizeof(expbuf)); |
else |
else |
strlcat(expbuf, rcsfile, |
(void)strlcat(expbuf, |
sizeof(expbuf)); |
rcsfile, sizeof(expbuf)); |
strlcat(expbuf, " ", sizeof(expbuf)); |
if (strlcat(expbuf, " ", |
|
sizeof(expbuf)) >= sizeof(expbuf)) |
|
fatal("rcs_expand_keywords: " |
|
"truncated"); |
} |
} |
|
|
if (kwtype & RCS_KW_REVISION) { |
if (kwtype & RCS_KW_REVISION) { |
rcsnum_tostr(rdp->rd_num, buf, |
rcsnum_tostr(rdp->rd_num, buf, |
sizeof(buf)); |
sizeof(buf)); |
strlcat(buf, " ", sizeof(buf)); |
if (strlcat(buf, " ", sizeof(buf)) |
strlcat(expbuf, buf, sizeof(expbuf)); |
>= sizeof(buf) || |
|
strlcat(expbuf, buf, |
|
sizeof(expbuf)) >= sizeof(buf)) |
|
fatal("rcs_expand_keywords: " |
|
"truncated"); |
} |
} |
|
|
if (kwtype & RCS_KW_DATE) { |
if (kwtype & RCS_KW_DATE) { |
if (timezone_flag != NULL) |
fmt = "%Y/%m/%d %H:%M:%S "; |
fmt = "%Y/%m/%d %H:%M:%S%z "; |
|
else |
|
fmt = "%Y/%m/%d %H:%M:%S "; |
|
|
|
strftime(buf, sizeof(buf), fmt, &tb); |
strftime(buf, sizeof(buf), fmt, &tb); |
strlcat(expbuf, buf, sizeof(expbuf)); |
if (strlcat(expbuf, buf, |
|
sizeof(expbuf)) >= sizeof(expbuf)) |
|
fatal("rcs_expand_keywords: " |
|
"string truncated"); |
} |
} |
|
|
if (kwtype & RCS_KW_AUTHOR) { |
if (kwtype & RCS_KW_AUTHOR) { |
strlcat(expbuf, rdp->rd_author, |
if (strlcat(expbuf, rdp->rd_author, |
sizeof(expbuf)); |
sizeof(expbuf)) >= sizeof(expbuf) || |
strlcat(expbuf, " ", sizeof(expbuf)); |
strlcat(expbuf, " ", |
|
sizeof(expbuf)) >= sizeof(expbuf)) |
|
fatal("rcs_expand_keywords: " |
|
"string truncated"); |
} |
} |
|
|
if (kwtype & RCS_KW_STATE) { |
if (kwtype & RCS_KW_STATE) { |
strlcat(expbuf, rdp->rd_state, |
if (strlcat(expbuf, rdp->rd_state, |
sizeof(expbuf)); |
sizeof(expbuf)) >= sizeof(expbuf) || |
strlcat(expbuf, " ", sizeof(expbuf)); |
strlcat(expbuf, " ", |
|
sizeof(expbuf)) >= sizeof(expbuf)) |
|
fatal("rcs_expand_keywords: " |
|
"string truncated"); |
} |
} |
|
|
/* order does not matter anymore below */ |
/* order does not matter anymore below */ |
if (kwtype & RCS_KW_LOG) |
if (kwtype & RCS_KW_LOG) |
strlcat(expbuf, " ", sizeof(expbuf)); |
if (strlcat(expbuf, " ", |
|
sizeof(expbuf)) >= sizeof(expbuf)) |
|
fatal("rcs_expand_keywords: " |
|
"string truncated"); |
|
|
if (kwtype & RCS_KW_SOURCE) { |
if (kwtype & RCS_KW_SOURCE) { |
strlcat(expbuf, rcsfile, |
if (strlcat(expbuf, rcsfile, |
sizeof(expbuf)); |
sizeof(expbuf)) >= sizeof(expbuf) || |
strlcat(expbuf, " ", sizeof(expbuf)); |
strlcat(expbuf, " ", |
|
sizeof(expbuf)) >= sizeof(expbuf)) |
|
fatal("rcs_expand_keywords: " |
|
"string truncated"); |
} |
} |
|
|
if (kwtype & RCS_KW_NAME) |
if (kwtype & RCS_KW_NAME) |
strlcat(expbuf, " ", sizeof(expbuf)); |
if (strlcat(expbuf, " ", |
|
sizeof(expbuf)) >= sizeof(expbuf)) |
|
fatal("rcs_expand_keywords: " |
|
"string truncated"); |
} |
} |
|
|
/* end the expansion */ |
/* end the expansion */ |
if (mode & RCS_KWEXP_NAME) |
if (mode & RCS_KWEXP_NAME) |
strlcat(expbuf, "$", sizeof(expbuf)); |
if (strlcat(expbuf, "$", |
|
sizeof(expbuf)) >= sizeof(expbuf)) |
|
fatal("rcs_expand_keywords: truncated"); |
|
|
sizdiff = strlen(expbuf) - (end - start); |
sizdiff = strlen(expbuf) - (end - start); |
tbuf = xstrdup(end); |
tbuf = xstrdup(end); |
|
|
/* only realloc if we have to */ |
/* only realloc if we have to */ |
if (sizdiff > 0) { |
if (sizdiff > 0) { |
char *newdata; |
char *newdata; |
|
|
len += sizdiff; |
len += sizdiff; |
newdata = xrealloc(data, 1, len); |
newdata = xrealloc(data, 1, len); |
data = newdata; |
data = newdata; |
|
|
/* |
/* |
* ensure string pointers are not invalidated |
* ensure string pointers are not invalidated |
* after realloc() |
* after realloc() |
|
|
start = data + start_offset; |
start = data + start_offset; |
c = data + c_offset; |
c = data + c_offset; |
} |
} |
strlcpy(start, expbuf, len); |
if (strlcpy(start, expbuf, len) >= len || |
strlcat(data, tbuf, len); |
strlcat(data, tbuf, len) >= len) |
|
fatal("rcs_expand_keywords: string truncated"); |
xfree(tbuf); |
xfree(tbuf); |
i += strlen(expbuf); |
i += strlen(expbuf); |
} |
} |
|
|
|
|
if (!(expmode & RCS_KWEXP_NONE)) { |
if (!(expmode & RCS_KWEXP_NONE)) { |
if ((rdp = rcs_findrev(rf, rev)) == NULL) |
if ((rdp = rcs_findrev(rf, rev)) == NULL) |
fatal("could not fetch revision"); |
fatal("could not fetch revision"); |
cvs_buf_putc(bp, '\0'); |
cvs_buf_putc(bp, '\0'); |
len = cvs_buf_len(bp); |
len = cvs_buf_len(bp); |
tbuf = cvs_buf_release(bp); |
tbuf = cvs_buf_release(bp); |
|
|
} |
} |
return (bp); |
return (bp); |
} |
} |
|
|
#if !defined(RCSPROG) |
|
|
|
static char *month_tab[] = { |
|
"Jan", |
|
"Feb", |
|
"Mar", |
|
"Apr", |
|
"May", |
|
"Jun", |
|
"Jul", |
|
"Aug", |
|
"Sep", |
|
"Oct", |
|
"Nov", |
|
"Dec" |
|
}; |
|
|
|
void |
|
rcs_kflag_usage(void) |
|
{ |
|
(void)fprintf(stderr, "Valid expansion modes include:\n" |
|
"\t-kkv\tGenerate keywords using the default form.\n" |
|
"\t-kkvl\tLike -kkv, except locker's name inserted.\n" |
|
"\t-kk\tGenerate only keyword names in keyword strings.\n" |
|
"\t-kv\tGenerate only keyword values in keyword strings.\n" |
|
"\t-ko\tGenerate old keyword string " |
|
"(no changes from checked in file).\n" |
|
"\t-kb\tGenerate binary file unmodified (merges not allowed).\n"); |
|
} |
|
|
|
/* |
|
* Checkout a certain revision <rev> of RCS file <rf> to either standard |
|
* output when running in server mode, or to <fpath> when running in local mode. |
|
* |
|
* If type is CHECKOUT_REV_MERGED we have an extra argument, which |
|
* is the buffer containing the merged file. |
|
* |
|
* If type is CHECKOUT_REV_REMOVED, the file has been removed and we |
|
* need to do the same thing. |
|
*/ |
|
int |
|
cvs_checkout_rev(RCSFILE *rf, RCSNUM *rev, CVSFILE *cf, char *fpath, |
|
int local, int type, ...) |
|
{ |
|
BUF *bp; |
|
int l, ret, fsize; |
|
char timebuf[32], entry[MAXPATHLEN], copyfile[MAXPATHLEN]; |
|
char *content, *repo, buf[MAXPATHLEN], modestr[16]; |
|
struct cvsroot *root; |
|
struct cvs_ent *ent; |
|
va_list ap; |
|
time_t rcstime; |
|
struct timeval tv[2]; |
|
struct tm *tp; |
|
RCSNUM *oldrev; |
|
|
|
bp = NULL; |
|
ret = -1; |
|
content = NULL; |
|
oldrev = NULL; |
|
|
|
if (type != CHECKOUT_REV_MERGED && type != CHECKOUT_REV_REMOVED) { |
|
/* fetch the contents of the revision */ |
|
if ((bp = rcs_getrev(rf, rev)) == NULL) { |
|
cvs_log(LP_ERR, "revision '%s' not found in file '%s'", |
|
rcsnum_tostr(rev, buf, sizeof(buf)), fpath); |
|
goto out; |
|
} |
|
bp = rcs_kwexp_buf(bp, rf, rev); |
|
} else if (type != CHECKOUT_REV_REMOVED) { |
|
va_start(ap, type); |
|
bp = va_arg(ap, BUF *); |
|
va_end(ap); |
|
} |
|
|
|
if (type == CHECKOUT_REV_CREATED) |
|
rcstime = rcs_rev_getdate(rf, rev); |
|
else if (type == CHECKOUT_REV_MERGED || |
|
type == CHECKOUT_REV_UPDATED) { |
|
time(&rcstime); |
|
if ((rcstime = cvs_hack_time(rcstime, 1)) < 0) |
|
goto out; |
|
} |
|
|
|
if (type == CHECKOUT_REV_CREATED || |
|
type == CHECKOUT_REV_MERGED || |
|
type == CHECKOUT_REV_UPDATED) { |
|
ctime_r(&rcstime, timebuf); |
|
l = strlen(timebuf); |
|
if (l > 0 && timebuf[l - 1] == '\n') |
|
timebuf[--l] = '\0'; |
|
|
|
l = snprintf(entry, sizeof(entry), "/%s/%s/%s/%s/", cf->cf_name, |
|
rcsnum_tostr(rev, buf, sizeof(buf)), |
|
(local == 1) ? timebuf : "", |
|
(type == CHECKOUT_REV_MERGED) ? "+=" : ""); |
|
if (l == -1 || l >= (int)sizeof(buf)) |
|
goto out; |
|
} |
|
|
|
if (type == CHECKOUT_REV_MERGED) { |
|
oldrev = rcsnum_alloc(); |
|
rcsnum_cpy(rev, oldrev, 0); |
|
|
|
if (oldrev->rn_id[oldrev->rn_len - 1] <= 0) |
|
goto out; |
|
oldrev = rcsnum_dec(oldrev); |
|
|
|
l = snprintf(copyfile, sizeof(copyfile), ".#%s.%s", |
|
cf->cf_name, rcsnum_tostr(oldrev, buf, sizeof(buf))); |
|
if (l == -1 || l >= (int)sizeof(copyfile)) |
|
goto out; |
|
} |
|
|
|
root = CVS_DIR_ROOT(cf); |
|
repo = CVS_DIR_REPO(cf); |
|
|
|
/* |
|
* In local mode, just copy the entire contents to fpath. |
|
* In server mode, we need to send it to the client together with |
|
* some responses. |
|
*/ |
|
if (local) { |
|
l = 0; |
|
if (cf->cf_entry == NULL) { |
|
l = 1; |
|
cf->cf_entry = cvs_ent_open(cf->cf_dir, O_RDWR); |
|
if (cf->cf_entry == NULL) { |
|
cvs_log(LP_ERR, "failed to open Entry " |
|
"file '%s'", cf->cf_dir); |
|
goto out; |
|
} |
|
} |
|
|
|
cvs_ent_remove(cf->cf_entry, cf->cf_name, 1); |
|
if (type != CHECKOUT_REV_REMOVED) { |
|
cvs_ent_addln(cf->cf_entry, entry); |
|
ent = cvs_ent_get(cf->cf_entry, cf->cf_name); |
|
ent->processed = 1; |
|
} |
|
|
|
if (l == 1) |
|
cvs_ent_close(cf->cf_entry); |
|
|
|
switch (type) { |
|
case CHECKOUT_REV_REMOVED: |
|
if (cvs_unlink(fpath) < 0) |
|
goto out; |
|
break; |
|
case CHECKOUT_REV_MERGED: |
|
/* XXX move the old file when merging */ |
|
case CHECKOUT_REV_UPDATED: |
|
case CHECKOUT_REV_CREATED: |
|
cvs_buf_write(bp, fpath, cf->cf_mode); |
|
/* |
|
* correct the time first |
|
*/ |
|
if ((rcstime = cvs_hack_time(rcstime, 0)) == 0) |
|
goto out; |
|
|
|
tv[0].tv_sec = rcstime; |
|
tv[0].tv_usec = 0; |
|
tv[1] = tv[0]; |
|
if (utimes(fpath, tv) == -1) |
|
cvs_log(LP_ERRNO, "failed to set timestamps"); |
|
break; |
|
} |
|
} else { |
|
/* sanity */ |
|
if (cf->cf_type != DT_REG) { |
|
cvs_log(LP_ERR, "cvs_checkout_rev: none DT_REG file"); |
|
goto out; |
|
} |
|
|
|
/* |
|
* if we are removing a file, we don't need this stuff. |
|
*/ |
|
if (type != CHECKOUT_REV_REMOVED) { |
|
if ((rcstime = cvs_hack_time(rcstime, 0)) == 0) |
|
goto out; |
|
|
|
tp = gmtime(&rcstime); |
|
l = snprintf(timebuf, sizeof(timebuf), |
|
"%02d %s %d %02d:%02d:%02d -0000", |
|
tp->tm_mday, month_tab[tp->tm_mon], |
|
tp->tm_year + 1900, tp->tm_hour, |
|
tp->tm_min, tp->tm_sec); |
|
if (l == -1 || l >= (int)sizeof(timebuf)) |
|
goto out; |
|
|
|
fsize = cvs_buf_len(bp); |
|
cvs_modetostr(cf->cf_mode, modestr, sizeof(modestr)); |
|
cvs_buf_putc(bp, '\0'); |
|
content = cvs_buf_release(bp); |
|
bp = NULL; |
|
} |
|
|
|
if (type == CHECKOUT_REV_MERGED) { |
|
printf("Copy-file %s/\n", (cf->cf_dir != NULL) ? |
|
cf->cf_dir : "."); |
|
printf("%s/%s/%s\n", root->cr_dir, repo, cf->cf_name); |
|
printf("%s\n", copyfile); |
|
} |
|
|
|
switch (type) { |
|
case CHECKOUT_REV_MERGED: |
|
printf("Merged"); |
|
break; |
|
case CHECKOUT_REV_REMOVED: |
|
printf("Removed"); |
|
break; |
|
case CHECKOUT_REV_CREATED: |
|
printf("Mod-time %s\n", timebuf); |
|
printf("Created"); |
|
break; |
|
default: |
|
cvs_log(LP_ERR, "cvs_checkout_rev: bad type %d", |
|
type); |
|
goto out; |
|
} |
|
|
|
printf(" %s/\n", (cf->cf_dir != NULL) ? cf->cf_dir : "."); |
|
printf("%s/%s\n", repo, cf->cf_name); |
|
|
|
if (type != CHECKOUT_REV_REMOVED) { |
|
printf("%s\n", entry); |
|
printf("%s\n%d\n%s", modestr, fsize, content); |
|
} |
|
} |
|
|
|
ret = 0; |
|
|
|
out: |
|
if (oldrev != NULL) |
|
rcsnum_free(oldrev); |
|
if (bp != NULL) |
|
cvs_buf_free(bp); |
|
if (content != NULL) |
|
xfree(content); |
|
|
|
return (ret); |
|
} |
|
|
|
#endif /* !RCSPROG */ |
|