version 1.25, 2005/02/25 20:32:48 |
version 1.26, 2005/02/27 00:22:08 |
|
|
#include <stdio.h> |
#include <stdio.h> |
#include <ctype.h> |
#include <ctype.h> |
#include <stdlib.h> |
#include <stdlib.h> |
|
#include <stdarg.h> |
#include <string.h> |
#include <string.h> |
|
|
#include "rcs.h" |
#include "rcs.h" |
#include "log.h" |
#include "log.h" |
|
|
#define RCS_BUFSIZE 16384 |
#define RCS_BUFSIZE 16384 |
#define RCS_BUFEXTSIZE 8192 |
#define RCS_BUFEXTSIZE 8192 |
|
|
|
|
|
|
struct rcs_tqh rl_lines; |
struct rcs_tqh rl_lines; |
}; |
}; |
|
|
static int rcs_parse_admin (RCSFILE *); |
|
static int rcs_parse_delta (RCSFILE *); |
|
static int rcs_parse_deltatext (RCSFILE *); |
|
|
|
static int rcs_parse_access (RCSFILE *); |
|
static int rcs_parse_symbols (RCSFILE *); |
|
static int rcs_parse_locks (RCSFILE *); |
|
static int rcs_parse_branches (RCSFILE *, struct rcs_delta *); |
|
static void rcs_freedelta (struct rcs_delta *); |
|
static void rcs_freepdata (struct rcs_pdata *); |
|
static int rcs_gettok (RCSFILE *); |
|
static int rcs_pushtok (RCSFILE *, const char *, int); |
|
static int rcs_growbuf (RCSFILE *); |
|
static int rcs_patch_lines (struct rcs_foo *, struct rcs_foo *); |
|
|
|
static struct rcs_delta* rcs_findrev (RCSFILE *, RCSNUM *); |
|
static struct rcs_foo* rcs_splitlines (const char *); |
|
static void rcs_freefoo (struct rcs_foo *); |
|
|
|
#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_blen |
#define RCS_TOKLEN(rfp) ((struct rcs_pdata *)rfp->rf_pdata)->rp_blen |
|
|
|
|
#define RCS_NKEYS (sizeof(rcs_keys)/sizeof(rcs_keys[0])) |
#define RCS_NKEYS (sizeof(rcs_keys)/sizeof(rcs_keys[0])) |
|
|
|
|
|
static int rcs_parse (RCSFILE *); |
|
static int rcs_parse_admin (RCSFILE *); |
|
static int rcs_parse_delta (RCSFILE *); |
|
static int rcs_parse_deltatext (RCSFILE *); |
|
|
|
static int rcs_parse_access (RCSFILE *); |
|
static int rcs_parse_symbols (RCSFILE *); |
|
static int rcs_parse_locks (RCSFILE *); |
|
static int rcs_parse_branches (RCSFILE *, struct rcs_delta *); |
|
static void rcs_freedelta (struct rcs_delta *); |
|
static void rcs_freepdata (struct rcs_pdata *); |
|
static int rcs_gettok (RCSFILE *); |
|
static int rcs_pushtok (RCSFILE *, const char *, int); |
|
static int rcs_growbuf (RCSFILE *); |
|
static int rcs_patch_lines (struct rcs_foo *, struct rcs_foo *); |
|
|
|
static struct rcs_delta* rcs_findrev (RCSFILE *, RCSNUM *); |
|
static struct rcs_foo* rcs_splitlines (const char *); |
|
static void rcs_freefoo (struct rcs_foo *); |
|
|
|
|
/* |
/* |
* rcs_open() |
* rcs_open() |
* |
* |
* Open a file containing RCS-formatted information. The file's path is |
* Open a file containing RCS-formatted information. The file's path is |
* given in <path>, and the opening mode is given in <mode>, which is either |
* given in <path>, and the opening flags are given in <flags>, which is either |
* RCS_MODE_READ, RCS_MODE_WRITE, or RCS_MODE_RDWR. If the mode requests write |
* RCS_READ, RCS_WRITE, or RCS_RDWR. If the open requests write access and |
* access and the file does not exist, it will be created. |
* the file does not exist, the RCS_CREATE flag must also be given, in which |
* The file isn't actually parsed by rcs_open(); parsing is delayed until the |
* case it will be created with the mode specified in a third argument of |
* first operation that requires information from the file. |
* 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. |
* Returns a handle to the opened file on success, or NULL on failure. |
*/ |
*/ |
RCSFILE* |
RCSFILE* |
rcs_open(const char *path, u_int mode) |
rcs_open(const char *path, int flags, ...) |
{ |
{ |
|
int ret; |
|
mode_t fmode; |
RCSFILE *rfp; |
RCSFILE *rfp; |
struct stat st; |
struct stat st; |
|
va_list vap; |
|
|
if ((stat(path, &st) == -1) && (errno == ENOENT) && |
fmode = 0; |
!(mode & RCS_MODE_WRITE)) { |
flags &= 0xffff; /* ditch any internal flags */ |
cvs_log(LP_ERRNO, "cannot open RCS file `%s'", path); |
|
|
if (((ret = stat(path, &st)) == -1) && (errno == ENOENT)) { |
|
if (flags & RCS_CREATE) { |
|
va_start(vap, flags); |
|
fmode = va_arg(vap, mode_t); |
|
va_end(vap); |
|
} else { |
|
cvs_log(LP_ERR, "RCS file `%s' does not exist", path); |
|
return (NULL); |
|
} |
|
} else if ((ret == 0) && (flags & RCS_CREATE)) { |
|
cvs_log(LP_ERR, "RCS file `%s' exists", path); |
return (NULL); |
return (NULL); |
} |
} |
|
|
rfp = (RCSFILE *)malloc(sizeof(*rfp)); |
if ((rfp = (RCSFILE *)malloc(sizeof(*rfp))) == NULL) { |
if (rfp == NULL) { |
|
cvs_log(LP_ERRNO, "failed to allocate RCS file structure"); |
cvs_log(LP_ERRNO, "failed to allocate RCS file structure"); |
return (NULL); |
return (NULL); |
} |
} |
memset(rfp, 0, sizeof(*rfp)); |
memset(rfp, 0, sizeof(*rfp)); |
|
|
rfp->rf_head = rcsnum_alloc(); |
if ((rfp->rf_head = rcsnum_parse(RCS_HEAD_INIT)) == NULL) { |
if (rfp->rf_head == NULL) { |
|
free(rfp); |
free(rfp); |
return (NULL); |
return (NULL); |
} |
} |
|
|
rfp->rf_branch = rcsnum_alloc(); |
if ((rfp->rf_branch = rcsnum_alloc()) == NULL) { |
if (rfp->rf_branch == NULL) { |
rcsnum_free(rfp->rf_head); |
rcs_close(rfp); |
free(rfp); |
return (NULL); |
return (NULL); |
} |
} |
|
|
rfp->rf_path = strdup(path); |
if ((rfp->rf_path = strdup(path)) == NULL) { |
if (rfp->rf_path == NULL) { |
|
cvs_log(LP_ERRNO, "failed to duplicate RCS file path"); |
cvs_log(LP_ERRNO, "failed to duplicate RCS file path"); |
rcs_close(rfp); |
rcsnum_free(rfp->rf_branch); |
|
rcsnum_free(rfp->rf_head); |
|
free(rfp); |
return (NULL); |
return (NULL); |
} |
} |
|
|
rcsnum_aton(RCS_HEAD_INIT, NULL, rfp->rf_head); |
|
|
|
rfp->rf_ref = 1; |
rfp->rf_ref = 1; |
rfp->rf_flags |= RCS_RF_SLOCK; |
rfp->rf_flags = flags | RCS_SLOCK; |
rfp->rf_mode = mode; |
rfp->rf_mode = fmode; |
|
|
TAILQ_INIT(&(rfp->rf_delta)); |
TAILQ_INIT(&(rfp->rf_delta)); |
TAILQ_INIT(&(rfp->rf_symbols)); |
TAILQ_INIT(&(rfp->rf_symbols)); |
TAILQ_INIT(&(rfp->rf_locks)); |
TAILQ_INIT(&(rfp->rf_locks)); |
|
|
if (rcs_parse(rfp) < 0) { |
if (rfp->rf_flags & RCS_CREATE) { |
|
} else if (rcs_parse(rfp) < 0) { |
rcs_close(rfp); |
rcs_close(rfp); |
return (NULL); |
return (NULL); |
} |
} |
|
|
return; |
return; |
} |
} |
|
|
|
if ((rfp->rf_flags & RCS_WRITE) && !(rfp->rf_flags & RCS_SYNCED)) |
|
rcs_write(rfp); |
|
|
while (!TAILQ_EMPTY(&(rfp->rf_delta))) { |
while (!TAILQ_EMPTY(&(rfp->rf_delta))) { |
rdp = TAILQ_FIRST(&(rfp->rf_delta)); |
rdp = TAILQ_FIRST(&(rfp->rf_delta)); |
TAILQ_REMOVE(&(rfp->rf_delta), rdp, rd_list); |
TAILQ_REMOVE(&(rfp->rf_delta), rdp, rd_list); |
|
|
struct rcs_sym *symp; |
struct rcs_sym *symp; |
struct rcs_delta *rdp; |
struct rcs_delta *rdp; |
|
|
if (rfp->rf_flags & RCS_RF_SYNCED) |
if ((rfp->rf_flags & RCS_SYNCED) || (rfp->rf_ndelta == 0)) |
return (0); |
return (0); |
|
|
fp = fopen(rfp->rf_path, "w"); |
fp = fopen(rfp->rf_path, "w"); |
|
|
|
|
fprintf(fp, "locks;"); |
fprintf(fp, "locks;"); |
|
|
if (rfp->rf_flags & RCS_RF_SLOCK) |
if (rfp->rf_flags & RCS_SLOCK) |
fprintf(fp, " strict;"); |
fprintf(fp, " strict;"); |
fputc('\n', fp); |
fputc('\n', fp); |
|
|
|
|
numbuf, sizeof(numbuf))); |
numbuf, sizeof(numbuf))); |
} |
} |
|
|
fprintf(fp, "\ndesc\n@%s@\n\n", rfp->rf_desc); |
fprintf(fp, "\ndesc\n@%s@\n\n", |
|
(rfp->rf_desc == NULL) ? "" : rfp->rf_desc); |
|
|
/* deltatexts */ |
/* deltatexts */ |
TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) { |
TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) { |
|
|
} |
} |
fclose(fp); |
fclose(fp); |
|
|
rfp->rf_flags |= RCS_RF_SYNCED; |
rfp->rf_flags |= RCS_SYNCED; |
|
|
return (0); |
return (0); |
} |
} |
|
|
|
|
/* |
/* |
* rcs_addsym() |
* rcs_sym_add() |
* |
* |
* Add a symbol to the list of symbols for the RCS file <rfp>. The new symbol |
* Add a symbol to the list of symbols for the RCS file <rfp>. The new symbol |
* is named <sym> and is bound to the RCS revision <snum>. |
* is named <sym> and is bound to the RCS revision <snum>. |
* Returns 0 on success, or -1 on failure. |
* Returns 0 on success, or -1 on failure. |
*/ |
*/ |
int |
int |
rcs_addsym(RCSFILE *rfp, const char *sym, RCSNUM *snum) |
rcs_sym_add(RCSFILE *rfp, const char *sym, RCSNUM *snum) |
{ |
{ |
struct rcs_sym *symp; |
struct rcs_sym *symp; |
|
|
|
|
TAILQ_INSERT_HEAD(&(rfp->rf_symbols), symp, rs_list); |
TAILQ_INSERT_HEAD(&(rfp->rf_symbols), symp, rs_list); |
|
|
/* not synced anymore */ |
/* not synced anymore */ |
rfp->rf_flags &= ~RCS_RF_SYNCED; |
rfp->rf_flags &= ~RCS_SYNCED; |
|
|
return (0); |
return (0); |
} |
} |
|
|
if (res == 1) { |
if (res == 1) { |
cvs_log(LP_ERR, "sorry, can't travel in the future yet"); |
cvs_log(LP_ERR, "sorry, can't travel in the future yet"); |
return (NULL); |
return (NULL); |
} else { |
} |
rdp = rcs_findrev(rfp, rfp->rf_head); |
|
if (rdp == NULL) { |
rdp = rcs_findrev(rfp, rfp->rf_head); |
cvs_log(LP_ERR, "failed to get RCS HEAD revision"); |
if (rdp == NULL) { |
|
cvs_log(LP_ERR, "failed to get RCS HEAD revision"); |
|
return (NULL); |
|
} |
|
|
|
len = strlen(rdp->rd_text); |
|
if ((rbuf = cvs_buf_alloc(len, BUF_AUTOEXT)) == NULL) |
|
return (NULL); |
|
|
|
cvs_buf_append(rbuf, rdp->rd_text, len); |
|
|
|
if (res != 0) { |
|
/* Apply patches backwards to get the right version. |
|
* This will need some rework to support sub branches. |
|
*/ |
|
if ((crev = rcsnum_alloc()) == NULL) { |
|
cvs_buf_free(rbuf); |
return (NULL); |
return (NULL); |
} |
} |
|
rcsnum_cpy(rfp->rf_head, crev, 0); |
|
do { |
|
crev->rn_id[crev->rn_len - 1]--; |
|
rdp = rcs_findrev(rfp, crev); |
|
if (rdp == NULL) { |
|
rcsnum_free(crev); |
|
cvs_buf_free(rbuf); |
|
return (NULL); |
|
} |
|
|
len = strlen(rdp->rd_text); |
if (cvs_buf_putc(rbuf, '\0') < 0) { |
rbuf = cvs_buf_alloc(len, BUF_AUTOEXT); |
rcsnum_free(crev); |
if (rbuf == NULL) |
|
return (NULL); |
|
cvs_buf_append(rbuf, rdp->rd_text, len); |
|
|
|
if (res != 0) { |
|
/* Apply patches backwards to get the right version. |
|
* This will need some rework to support sub branches. |
|
*/ |
|
if ((crev = rcsnum_alloc()) == NULL) { |
|
cvs_buf_free(rbuf); |
cvs_buf_free(rbuf); |
return (NULL); |
return (NULL); |
} |
} |
rcsnum_cpy(rfp->rf_head, crev, 0); |
bp = cvs_buf_release(rbuf); |
do { |
rbuf = rcs_patch((char *)bp, rdp->rd_text); |
crev->rn_id[crev->rn_len - 1]--; |
if (rbuf == NULL) |
rdp = rcs_findrev(rfp, crev); |
break; |
if (rdp == NULL) { |
} while (rcsnum_cmp(crev, rev, 0) != 0); |
rcsnum_free(crev); |
|
cvs_buf_free(rbuf); |
|
return (NULL); |
|
} |
|
|
|
if (cvs_buf_putc(rbuf, '\0') < 0) { |
rcsnum_free(crev); |
rcsnum_free(crev); |
|
cvs_buf_free(rbuf); |
|
return (NULL); |
|
} |
|
bp = cvs_buf_release(rbuf); |
|
rbuf = rcs_patch((char *)bp, rdp->rd_text); |
|
if (rbuf == NULL) |
|
break; |
|
} while (rcsnum_cmp(crev, rev, 0) != 0); |
|
|
|
rcsnum_free(crev); |
|
} |
|
} |
} |
|
|
return (rbuf); |
return (rbuf); |
|
|
struct rcs_delta *rdp; |
struct rcs_delta *rdp; |
struct rcs_dlist *hp; |
struct rcs_dlist *hp; |
int found; |
int found; |
|
|
cmplen = 2; |
cmplen = 2; |
hp = &(rfp->rf_delta); |
hp = &(rfp->rf_delta); |
|
|
|
|
|
|
|
|
/* |
/* |
|
* rcs_kwexp_set() |
|
* |
|
* Set the keyword expansion mode to use on the RCS file <file> to <mode>. |
|
* Returns 0 on success, or -1 on failure. |
|
*/ |
|
int |
|
rcs_kwexp_set(RCSFILE *file, int mode) |
|
{ |
|
int i; |
|
char *tmp, buf[8] = ""; |
|
|
|
if (RCS_KWEXP_INVAL(mode)) |
|
return (-1); |
|
|
|
i = 0; |
|
if (mode == RCS_KWEXP_NONE) |
|
buf[0] = 'b'; |
|
else if (mode == RCS_KWEXP_OLD) |
|
buf[0] = 'o'; |
|
else { |
|
if (mode & RCS_KWEXP_NAME) |
|
buf[i++] = 'k'; |
|
if (mode & RCS_KWEXP_VAL) |
|
buf[i++] = 'v'; |
|
if (mode & RCS_KWEXP_LKR) |
|
buf[i++] = 'l'; |
|
} |
|
|
|
if ((tmp = strdup(buf)) == NULL) { |
|
cvs_log(LP_ERRNO, "%s: failed to copy expansion mode", |
|
file->rf_path); |
|
return (-1); |
|
} |
|
|
|
free(file->rf_expand); |
|
file->rf_expand = tmp; |
|
|
|
return (0); |
|
} |
|
|
|
/* |
|
* rcs_kwexp_get() |
|
* |
|
* Retrieve the keyword expansion mode to be used for the RCS file <file>. |
|
*/ |
|
int |
|
rcs_kwexp_get(RCSFILE *file) |
|
{ |
|
return rcs_kflag_get(file->rf_expand); |
|
} |
|
|
|
/* |
* rcs_kflag_get() |
* rcs_kflag_get() |
* |
* |
* Get the keyword expansion mode from a set of character flags given in |
* Get the keyword expansion mode from a set of character flags given in |
|
|
"\t-kb\tGenerate binary file unmodified (merges not allowed).\n"); |
"\t-kb\tGenerate binary file unmodified (merges not allowed).\n"); |
} |
} |
|
|
|
|
/* |
/* |
* rcs_parse() |
* rcs_parse() |
* |
* |
* Parse the contents of file <path>, which are in the RCS format. |
* Parse the contents of file <path>, which are in the RCS format. |
* Returns 0 on success, or -1 on failure. |
* Returns 0 on success, or -1 on failure. |
*/ |
*/ |
int |
static int |
rcs_parse(RCSFILE *rfp) |
rcs_parse(RCSFILE *rfp) |
{ |
{ |
int ret; |
int ret; |
struct rcs_pdata *pdp; |
struct rcs_pdata *pdp; |
|
|
if (rfp->rf_flags & RCS_RF_PARSED) |
if (rfp->rf_flags & RCS_PARSED) |
return (0); |
return (0); |
|
|
pdp = (struct rcs_pdata *)malloc(sizeof(*pdp)); |
if ((pdp = (struct rcs_pdata *)malloc(sizeof(*pdp))) == NULL) { |
if (pdp == NULL) { |
|
cvs_log(LP_ERRNO, "failed to allocate RCS parser data"); |
cvs_log(LP_ERRNO, "failed to allocate RCS parser data"); |
return (-1); |
return (-1); |
} |
} |
|
|
pdp->rp_bufend = pdp->rp_buf + pdp->rp_blen - 1; |
pdp->rp_bufend = pdp->rp_buf + pdp->rp_blen - 1; |
|
|
/* ditch the strict lock */ |
/* ditch the strict lock */ |
rfp->rf_flags &= ~RCS_RF_SLOCK; |
rfp->rf_flags &= ~RCS_SLOCK; |
rfp->rf_pdata = pdp; |
rfp->rf_pdata = pdp; |
|
|
if (rcs_parse_admin(rfp) < 0) { |
if (rcs_parse_admin(rfp) < 0) { |
|
|
rcs_freepdata(pdp); |
rcs_freepdata(pdp); |
|
|
rfp->rf_pdata = NULL; |
rfp->rf_pdata = NULL; |
rfp->rf_flags |= RCS_RF_PARSED|RCS_RF_SYNCED; |
rfp->rf_flags |= RCS_PARSED | RCS_SYNCED; |
|
|
return (0); |
return (0); |
} |
} |
|
|
if (ntok != RCS_TOK_SCOLON) { |
if (ntok != RCS_TOK_SCOLON) { |
cvs_log(LP_ERR, |
cvs_log(LP_ERR, |
"missing semi-colon after RCS `%s' key", |
"missing semi-colon after RCS `%s' key", |
rk->rk_str); |
rk->rk_str); |
return (-1); |
return (-1); |
} |
} |
break; |
break; |
|
|
if (ntok != RCS_TOK_SCOLON) { |
if (ntok != RCS_TOK_SCOLON) { |
cvs_log(LP_ERR, |
cvs_log(LP_ERR, |
"missing semi-colon after RCS `%s' key", |
"missing semi-colon after RCS `%s' key", |
rk->rk_str); |
rk->rk_str); |
rcs_freedelta(rdp); |
rcs_freedelta(rdp); |
return (-1); |
return (-1); |
} |
} |
|
|
free(tokstr); |
free(tokstr); |
|
|
TAILQ_INSERT_TAIL(&(rfp->rf_delta), rdp, rd_list); |
TAILQ_INSERT_TAIL(&(rfp->rf_delta), rdp, rd_list); |
|
rfp->rf_ndelta++; |
|
|
return (ret); |
return (ret); |
} |
} |
|
|
if (type != RCS_TOK_STRICT) { |
if (type != RCS_TOK_STRICT) { |
rcs_pushtok(rfp, RCS_TOKSTR(rfp), type); |
rcs_pushtok(rfp, RCS_TOKSTR(rfp), type); |
} else { |
} else { |
rfp->rf_flags |= RCS_RF_SLOCK; |
rfp->rf_flags |= RCS_SLOCK; |
|
|
type = rcs_gettok(rfp); |
type = rcs_gettok(rfp); |
if (type != RCS_TOK_SCOLON) { |
if (type != RCS_TOK_SCOLON) { |