version 1.56, 2006/04/14 02:45:35 |
version 1.57, 2006/05/27 03:30:30 |
|
|
/* $OpenBSD$ */ |
/* $OpenBSD$ */ |
/* |
/* |
* Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org> |
* Copyright (c) 2006 Joris Vink <joris@openbsd.org> |
* All rights reserved. |
|
* |
* |
* Redistribution and use in source and binary forms, with or without |
* Permission to use, copy, modify, and distribute this software for any |
* modification, are permitted provided that the following conditions |
* purpose with or without fee is hereby granted, provided that the above |
* are met: |
* copyright notice and this permission notice appear in all copies. |
* |
* |
* 1. Redistributions of source code must retain the above copyright |
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
* notice, this list of conditions and the following disclaimer. |
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
* 2. The name of the author may not be used to endorse or promote products |
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
* derived from this software without specific prior written permission. |
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
* |
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, |
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY |
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
* 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 "includes.h" |
#include "includes.h" |
|
|
#include "cvs.h" |
#include "cvs.h" |
#include "log.h" |
#include "log.h" |
|
|
|
|
#define CVS_ENTRIES_NFIELDS 6 |
#define CVS_ENTRIES_NFIELDS 6 |
#define CVS_ENTRIES_DELIM '/' |
#define CVS_ENTRIES_DELIM '/' |
|
|
|
static struct cvs_ent_line *ent_get_line(CVSENTRIES *, const char *); |
|
|
/* |
|
* cvs_ent_open() |
|
* |
|
* Open the CVS Entries file for the directory <dir>. |
|
* Returns a pointer to the CVSENTRIES file structure on success, or NULL |
|
* on failure. |
|
*/ |
|
CVSENTRIES * |
CVSENTRIES * |
cvs_ent_open(const char *dir, int flags) |
cvs_ent_open(const char *dir) |
{ |
{ |
size_t len; |
|
int exists, nodir; |
|
char bpath[MAXPATHLEN], *p; |
|
char cdpath[MAXPATHLEN], ebuf[CVS_ENT_MAXLINELEN], entpath[MAXPATHLEN]; |
|
char mode[4]; |
|
FILE *fp; |
FILE *fp; |
struct stat st; |
size_t len; |
struct cvs_ent *ent; |
|
CVSENTRIES *ep; |
CVSENTRIES *ep; |
|
char *p, buf[MAXPATHLEN]; |
|
struct cvs_ent *ent; |
|
struct cvs_ent_line *line; |
|
|
exists = 0; |
ep = (CVSENTRIES *)xmalloc(sizeof(*ep)); |
nodir = 1; |
memset(ep, 0, sizeof(*ep)); |
memset(mode, 0, sizeof(mode)); |
|
|
|
/* |
cvs_path_cat(dir, CVS_PATH_ENTRIES, buf, sizeof(buf)); |
* Check if the CVS/ dir does exist. If it does, |
ep->cef_path = xstrdup(buf); |
* maybe the Entries file was deleted by accident, |
|
* display error message. Else we might be doing a fresh |
|
* update or checkout of a module. |
|
*/ |
|
len = cvs_path_cat(dir, CVS_PATH_CVSDIR, cdpath, sizeof(cdpath)); |
|
if (len >= sizeof(cdpath)) |
|
return (NULL); |
|
|
|
if (stat(cdpath, &st) == 0 && S_ISDIR(st.st_mode)) |
cvs_path_cat(dir, CVS_PATH_BACKUPENTRIES, buf, sizeof(buf)); |
nodir = 0; /* the CVS/ directory does exist */ |
ep->cef_bpath = xstrdup(buf); |
|
|
len = cvs_path_cat(dir, CVS_PATH_BACKUPENTRIES, bpath, sizeof(bpath)); |
cvs_path_cat(dir, CVS_PATH_LOGENTRIES, buf, sizeof(buf)); |
if (len >= sizeof(entpath)) |
ep->cef_lpath = xstrdup(buf); |
return (NULL); |
|
|
|
len = cvs_path_cat(dir, CVS_PATH_ENTRIES, entpath, sizeof(entpath)); |
|
if (len >= sizeof(entpath)) |
|
return (NULL); |
|
|
|
switch (flags & O_ACCMODE) { |
|
case O_WRONLY: |
|
case O_RDWR: |
|
/* we have to use append otherwise the file gets truncated */ |
|
mode[0] = 'w'; |
|
mode[1] = '+'; |
|
break; |
|
case O_RDONLY: |
|
mode[0] = 'r'; |
|
break; |
|
} |
|
|
|
/* we can use 'r' if the file already exists */ |
|
if (stat(entpath, &st) == 0) { |
|
exists = 1; |
|
mode[0] = 'r'; |
|
} |
|
|
|
fp = fopen(entpath, mode); |
|
if (fp == NULL) { |
|
if (nodir == 0) |
|
cvs_log(LP_ERRNO, "cannot open %s for %s", entpath, |
|
mode[1] == '+' ? "writing" : "reading"); |
|
return (NULL); |
|
} |
|
|
|
ep = xcalloc(1, sizeof(*ep)); |
|
|
|
ep->cef_path = xstrdup(entpath); |
|
ep->cef_bpath = xstrdup(bpath); |
|
ep->cef_cur = NULL; |
|
TAILQ_INIT(&(ep->cef_ent)); |
TAILQ_INIT(&(ep->cef_ent)); |
|
|
while (fgets(ebuf, (int)sizeof(ebuf), fp) != NULL) { |
if ((fp = fopen(ep->cef_path, "r")) != NULL) { |
len = strlen(ebuf); |
while (fgets(buf, sizeof(buf), fp)) { |
if (len > 0 && ebuf[len - 1] == '\n') |
len = strlen(buf); |
ebuf[--len] = '\0'; |
if (len > 0 && buf[len - 1] == '\n') |
if (ebuf[0] == 'D' && ebuf[1] == '\0') |
buf[len - 1] = '\0'; |
break; |
|
ent = cvs_ent_parse(ebuf); |
|
if (ent == NULL) |
|
continue; |
|
|
|
TAILQ_INSERT_TAIL(&(ep->cef_ent), ent, ce_list); |
if (buf[0] == 'D' && buf[1] == '\0') |
} |
break; |
|
|
if (ferror(fp)) { |
line = (struct cvs_ent_line *)xmalloc(sizeof(*line)); |
cvs_log(LP_ERRNO, "read error on %s", entpath); |
line->buf = xstrdup(buf); |
|
TAILQ_INSERT_TAIL(&(ep->cef_ent), line, entries_list); |
|
} |
|
|
(void)fclose(fp); |
(void)fclose(fp); |
cvs_ent_close(ep); |
|
return (NULL); |
|
} |
} |
|
|
/* only keep a pointer to the open file if we're in writing mode */ |
if ((fp = fopen(ep->cef_lpath, "r")) != NULL) { |
if ((flags & O_WRONLY) || (flags & O_RDWR)) |
while (fgets(buf, sizeof(buf), fp)) { |
ep->cef_flags |= CVS_ENTF_WR; |
len = strlen(buf); |
|
if (len > 0 && buf[strlen(buf) - 1] == '\n') |
|
buf[strlen(buf) - 1] = '\0'; |
|
|
(void)fclose(fp); |
p = &buf[1]; |
|
|
/* |
if (buf[0] == 'A') { |
* look for Entries.Log and add merge it together with our |
line = xmalloc(sizeof(*line)); |
* list of things. |
line->buf = xstrdup(p); |
*/ |
TAILQ_INSERT_TAIL(&(ep->cef_ent), line, |
len = cvs_path_cat(dir, CVS_PATH_LOGENTRIES, entpath, sizeof(entpath)); |
entries_list); |
if (len >= sizeof(entpath)) { |
} else if (buf[0] == 'R') { |
cvs_ent_close(ep); |
ent = cvs_ent_parse(p); |
return (NULL); |
line = ent_get_line(ep, ent->ce_name); |
} |
if (line != NULL) |
|
TAILQ_REMOVE(&(ep->cef_ent), line, |
fp = fopen(entpath, "r"); |
entries_list); |
if (fp != NULL) { |
cvs_ent_free(ent); |
while (fgets(ebuf, (int)sizeof(ebuf), fp) != NULL) { |
} |
len = strlen(ebuf); |
|
if (len > 0 && ebuf[len - 1] == '\n') |
|
ebuf[--len] = '\0'; |
|
|
|
p = &ebuf[2]; |
|
ent = cvs_ent_parse(p); |
|
if (ent == NULL) |
|
continue; |
|
|
|
if (ebuf[0] == 'A') |
|
cvs_ent_add(ep, ent); |
|
else if (ebuf[0] == 'R') |
|
cvs_ent_remove(ep, ent->ce_name, 0); |
|
} |
} |
(void)fclose(fp); |
|
|
|
/* always un-synced here, because we |
(void)fclose(fp); |
* just added or removed entries. |
|
*/ |
|
ep->cef_flags &= ~CVS_ENTF_SYNC; |
|
} else { |
|
if (exists == 1) |
|
ep->cef_flags |= CVS_ENTF_SYNC; |
|
} |
} |
|
|
return (ep); |
return (ep); |
} |
} |
|
|
|
struct cvs_ent * |
/* |
cvs_ent_parse(const char *entry) |
* cvs_ent_close() |
|
* |
|
* Close the Entries file <ep> and free all data. Any reference to entries |
|
* structure within that file become invalid. |
|
*/ |
|
void |
|
cvs_ent_close(CVSENTRIES *ep) |
|
{ |
{ |
|
int i; |
struct cvs_ent *ent; |
struct cvs_ent *ent; |
|
char *fields[CVS_ENTRIES_NFIELDS], *buf, *sp, *dp; |
|
|
if (cvs_noexec == 0 && (ep->cef_flags & CVS_ENTF_WR) && |
buf = xstrdup(entry); |
!(ep->cef_flags & CVS_ENTF_SYNC)) { |
sp = buf; |
/* implicit sync with disk */ |
i = 0; |
(void)cvs_ent_write(ep); |
do { |
} |
dp = strchr(sp, CVS_ENTRIES_DELIM); |
|
if (dp != NULL) |
|
*(dp++) = '\0'; |
|
fields[i++] = sp; |
|
sp = dp; |
|
} while (dp != NULL && i < CVS_ENTRIES_NFIELDS); |
|
|
if (ep->cef_path != NULL) |
if (i < CVS_ENTRIES_NFIELDS) |
xfree(ep->cef_path); |
fatal("missing fields in entry line '%s'", entry); |
|
|
if (ep->cef_bpath != NULL) |
ent = (struct cvs_ent *)xmalloc(sizeof(*ent)); |
xfree(ep->cef_bpath); |
ent->ce_buf = buf; |
|
|
while (!TAILQ_EMPTY(&(ep->cef_ent))) { |
if (*fields[0] == '\0') |
ent = TAILQ_FIRST(&(ep->cef_ent)); |
ent->ce_type = CVS_ENT_FILE; |
TAILQ_REMOVE(&(ep->cef_ent), ent, ce_list); |
else if (*fields[0] == 'D') |
cvs_ent_free(ent); |
ent->ce_type = CVS_ENT_DIR; |
} |
else |
|
ent->ce_type = CVS_ENT_NONE; |
|
|
xfree(ep); |
ent->ce_status = CVS_ENT_REG; |
} |
ent->ce_name = fields[1]; |
|
ent->ce_rev = NULL; |
|
|
|
if (ent->ce_type == CVS_ENT_FILE) { |
|
if (*fields[2] == '-') { |
|
ent->ce_status = CVS_ENT_REMOVED; |
|
sp = fields[2] + 1; |
|
} else { |
|
sp = fields[2]; |
|
if (fields[2][0] == '0' && fields[2][1] == '\0') |
|
ent->ce_status = CVS_ENT_ADDED; |
|
} |
|
|
/* |
if ((ent->ce_rev = rcsnum_parse(sp)) == NULL) |
* cvs_ent_add() |
fatal("failed to parse entry revision '%s'", entry); |
* |
|
* Add the entry <ent> to the Entries file <ef>. The disk contents are not |
|
* modified until a call to cvs_ent_write() is performed. This is done |
|
* implicitly on a call to cvs_ent_close() on an Entries file that has been |
|
* opened for writing. |
|
* Returns 0 on success, or -1 on failure. |
|
*/ |
|
int |
|
cvs_ent_add(CVSENTRIES *ef, struct cvs_ent *ent) |
|
{ |
|
if (!(ef->cef_flags & CVS_ENTF_WR)) { |
|
cvs_log(LP_ERR, "Entries file is opened in read-only mode"); |
|
return (-1); |
|
} |
|
|
|
if (cvs_ent_get(ef, ent->ce_name) != NULL) { |
if (strcmp(fields[3], CVS_DATE_DUMMY) == 0 || |
cvs_log(LP_ERR, "attempt to add duplicate entry for `%s'", |
strncmp(fields[3], "Initial ", 8) == 0 || |
ent->ce_name); |
strncmp(fields[3], "Result of merge", 15) == 0) |
return (-1); |
ent->ce_mtime = CVS_DATE_DMSEC; |
|
else |
|
ent->ce_mtime = cvs_date_parse(fields[3]); |
} |
} |
|
|
TAILQ_INSERT_TAIL(&(ef->cef_ent), ent, ce_list); |
ent->ce_conflict = fields[3]; |
|
if ((dp = strchr(ent->ce_conflict, '+')) != NULL) |
|
*dp = '\0'; |
|
else |
|
ent->ce_conflict = NULL; |
|
|
ef->cef_flags &= ~CVS_ENTF_SYNC; |
if (strcmp(fields[4], "")) |
|
ent->ce_opts = fields[4]; |
|
else |
|
ent->ce_opts = NULL; |
|
|
return (0); |
if (strcmp(fields[5], "")) |
|
ent->ce_tag = fields[5]; |
|
else |
|
ent->ce_tag = NULL; |
|
|
|
return (ent); |
} |
} |
|
|
|
struct cvs_ent * |
/* |
cvs_ent_get(CVSENTRIES *ep, const char *name) |
* cvs_ent_addln() |
|
* |
|
* Add a line to the Entries file. |
|
*/ |
|
int |
|
cvs_ent_addln(CVSENTRIES *ef, const char *line) |
|
{ |
{ |
struct cvs_ent *ent; |
struct cvs_ent *ent; |
|
struct cvs_ent_line *l; |
|
|
if (!(ef->cef_flags & CVS_ENTF_WR)) { |
l = ent_get_line(ep, name); |
cvs_log(LP_ERR, "Entries file is opened in read-only mode"); |
if (l == NULL) |
return (-1); |
return (NULL); |
} |
|
|
|
ent = cvs_ent_parse(line); |
ent = cvs_ent_parse(l->buf); |
if (ent == NULL) |
return (ent); |
return (-1); |
} |
|
|
if (cvs_ent_get(ef, ent->ce_name) != NULL) |
int |
return (-1); |
cvs_ent_exists(CVSENTRIES *ep, const char *name) |
|
{ |
|
struct cvs_ent_line *l; |
|
|
TAILQ_INSERT_TAIL(&(ef->cef_ent), ent, ce_list); |
l = ent_get_line(ep, name); |
ef->cef_flags &= ~CVS_ENTF_SYNC; |
if (l == NULL) |
|
return (0); |
|
|
return (0); |
return (1); |
} |
} |
|
|
|
void |
/* |
cvs_ent_close(CVSENTRIES *ep, int writefile) |
* cvs_ent_remove() |
|
* |
|
* Remove an entry from the Entries file <ef>. The entry's name is given |
|
* by <name>. |
|
*/ |
|
int |
|
cvs_ent_remove(CVSENTRIES *ef, const char *name, int useprev) |
|
{ |
{ |
struct cvs_ent *ent; |
FILE *fp; |
|
struct cvs_ent_line *l; |
|
|
cvs_log(LP_TRACE, "cvs_ent_remove(%s)", name); |
if (writefile) { |
|
if ((fp = fopen(ep->cef_bpath, "w")) == NULL) |
|
fatal("cvs_ent_close: failed to write %s", |
|
ep->cef_path); |
|
} |
|
|
ent = cvs_ent_get(ef, name); |
while ((l = TAILQ_FIRST(&(ep->cef_ent))) != NULL) { |
if (ent == NULL) |
if (writefile) { |
return (-1); |
fputs(l->buf, fp); |
|
fputc('\n', fp); |
if (ef->cef_cur == ent) { |
|
/* if this element was the last one retrieved through a |
|
* call to cvs_ent_next(), point to the next element to avoid |
|
* keeping an invalid reference. |
|
*/ |
|
if (useprev) { |
|
ef->cef_cur = TAILQ_PREV(ef->cef_cur, |
|
cvsentrieshead, ce_list); |
|
} else { |
|
ef->cef_cur = TAILQ_NEXT(ef->cef_cur, ce_list); |
|
} |
} |
|
|
|
TAILQ_REMOVE(&(ep->cef_ent), l, entries_list); |
|
xfree(l->buf); |
|
xfree(l); |
} |
} |
TAILQ_REMOVE(&(ef->cef_ent), ent, ce_list); |
|
cvs_ent_free(ent); |
|
|
|
ef->cef_flags &= ~CVS_ENTF_SYNC; |
if (writefile) { |
|
fputc('D', fp); |
|
(void)fclose(fp); |
|
|
return (0); |
if (rename(ep->cef_bpath, ep->cef_path) == -1) |
} |
fatal("cvs_ent_close: %s: %s", ep->cef_path, |
|
strerror(errno)); |
|
|
|
(void)unlink(ep->cef_lpath); |
|
} |
|
|
/* |
xfree(ep->cef_path); |
* cvs_ent_get() |
xfree(ep->cef_bpath); |
* |
xfree(ep->cef_lpath); |
* Get the CVS entry from the Entries file <ef> whose 'name' portion matches |
xfree(ep); |
* <file>. |
} |
* Returns a pointer to the cvs entry structure on success, or NULL on failure. |
|
*/ |
void |
struct cvs_ent * |
cvs_ent_add(CVSENTRIES *ep, const char *line) |
cvs_ent_get(CVSENTRIES *ef, const char *file) |
|
{ |
{ |
|
FILE *fp; |
|
struct cvs_ent_line *l; |
struct cvs_ent *ent; |
struct cvs_ent *ent; |
|
|
TAILQ_FOREACH(ent, &(ef->cef_ent), ce_list) |
if ((ent = cvs_ent_parse(line)) == NULL) |
if (strcmp(ent->ce_name, file) == 0) |
fatal("cvs_ent_add: parsing failed '%s'", line); |
return (ent); |
|
|
|
return (NULL); |
l = ent_get_line(ep, ent->ce_name); |
} |
if (l != NULL) |
|
cvs_ent_remove(ep, ent->ce_name); |
|
|
|
cvs_ent_free(ent); |
|
|
/* |
cvs_log(LP_TRACE, "cvs_ent_add(%s, %s)", ep->cef_path, line); |
* cvs_ent_next() |
|
* |
|
* This function is used to iterate over the entries in an Entries file. The |
|
* first call will return the first entry of the file and each subsequent call |
|
* will return the entry following the last one returned. |
|
* Returns a pointer to the cvs entry structure on success, or NULL on failure. |
|
*/ |
|
struct cvs_ent * |
|
cvs_ent_next(CVSENTRIES *ef) |
|
{ |
|
if (ef->cef_cur == NULL) |
|
ef->cef_cur = TAILQ_FIRST(&(ef->cef_ent)); |
|
else |
|
ef->cef_cur = TAILQ_NEXT(ef->cef_cur, ce_list); |
|
return (ef->cef_cur); |
|
} |
|
|
|
|
if ((fp = fopen(ep->cef_lpath, "a")) == NULL) |
|
fatal("cvs_ent_add: failed to open '%s'", ep->cef_lpath); |
|
|
/* |
fputc('A', fp); |
* cvs_ent_parse() |
fputs(line, fp); |
* |
fputc('\n', fp); |
* Parse a single line from a CVS/Entries file and return a cvs_ent structure |
|
* containing all the parsed information. |
|
*/ |
|
struct cvs_ent* |
|
cvs_ent_parse(const char *entry) |
|
{ |
|
int i; |
|
char *fields[CVS_ENTRIES_NFIELDS], *buf, *sp, *dp; |
|
struct cvs_ent *ent; |
|
|
|
buf = xstrdup(entry); |
(void)fclose(fp); |
sp = buf; |
|
i = 0; |
|
do { |
|
dp = strchr(sp, CVS_ENTRIES_DELIM); |
|
if (dp != NULL) |
|
*(dp++) = '\0'; |
|
fields[i++] = sp; |
|
sp = dp; |
|
} while (dp != NULL && i < CVS_ENTRIES_NFIELDS); |
|
|
|
if (i < CVS_ENTRIES_NFIELDS) { |
l = (struct cvs_ent_line *)xmalloc(sizeof(*l)); |
cvs_log(LP_ERR, "missing fields in entry line `%s'", entry); |
l->buf = xstrdup(line); |
return (NULL); |
TAILQ_INSERT_TAIL(&(ep->cef_ent), l, entries_list); |
} |
} |
|
|
ent = xcalloc(1, sizeof(*ent)); |
void |
ent->ce_buf = buf; |
cvs_ent_remove(CVSENTRIES *ep, const char *name) |
|
{ |
|
FILE *fp; |
|
struct cvs_ent_line *l; |
|
|
if (*fields[0] == '\0') |
cvs_log(LP_TRACE, "cvs_ent_remove(%s, %s)", ep->cef_path, name); |
ent->ce_type = CVS_ENT_FILE; |
|
else if (*fields[0] == 'D') |
|
ent->ce_type = CVS_ENT_DIR; |
|
else |
|
ent->ce_type = CVS_ENT_NONE; |
|
|
|
ent->ce_status = CVS_ENT_REG; |
l = ent_get_line(ep, name); |
ent->ce_name = fields[1]; |
if (l == NULL) |
ent->processed = 0; |
return; |
|
|
if (ent->ce_type == CVS_ENT_FILE) { |
if ((fp = fopen(ep->cef_lpath, "a")) == NULL) |
if (*fields[2] == '-') { |
fatal("cvs_ent_remove: failed to open '%s'", |
ent->ce_status = CVS_ENT_REMOVED; |
ep->cef_lpath); |
sp = fields[2] + 1; |
|
} else { |
|
sp = fields[2]; |
|
if (fields[2][0] == '0' && fields[2][1] == '\0') |
|
ent->ce_status = CVS_ENT_ADDED; |
|
} |
|
|
|
if ((ent->ce_rev = rcsnum_parse(sp)) == NULL) { |
fputc('R', fp); |
cvs_ent_free(ent); |
fputs(l->buf, fp); |
return (NULL); |
fputc('\n', fp); |
} |
|
|
|
if (cvs_cmdop == CVS_OP_SERVER) { |
(void)fclose(fp); |
if (!strcmp(fields[3], "up to date")) |
|
ent->ce_status = CVS_ENT_UPTODATE; |
|
} else { |
|
if (strcmp(fields[3], CVS_DATE_DUMMY) == 0 || |
|
strncmp(fields[3], "Initial ", 8) == 0) |
|
ent->ce_mtime = CVS_DATE_DMSEC; |
|
else |
|
ent->ce_mtime = cvs_date_parse(fields[3]); |
|
} |
|
} |
|
|
|
ent->ce_opts = fields[4]; |
TAILQ_REMOVE(&(ep->cef_ent), l, entries_list); |
ent->ce_tag = fields[5]; |
xfree(l->buf); |
return (ent); |
xfree(l); |
} |
} |
|
|
/* |
|
* cvs_ent_free() |
|
* |
|
* Free a single CVS entries structure. |
|
*/ |
|
void |
void |
cvs_ent_free(struct cvs_ent *ent) |
cvs_ent_free(struct cvs_ent *ent) |
{ |
{ |
if (ent->ce_rev != NULL) |
if (ent->ce_rev != NULL) |
rcsnum_free(ent->ce_rev); |
rcsnum_free(ent->ce_rev); |
if (ent->ce_buf != NULL) |
xfree(ent->ce_buf); |
xfree(ent->ce_buf); |
|
xfree(ent); |
xfree(ent); |
} |
} |
|
|
/* |
static struct cvs_ent_line * |
* cvs_ent_write() |
ent_get_line(CVSENTRIES *ep, const char *name) |
* |
|
* Explicitly write the contents of the Entries file <ef> to disk. |
|
* Returns 0 on success, or -1 on failure. |
|
*/ |
|
int |
|
cvs_ent_write(CVSENTRIES *ef) |
|
{ |
{ |
size_t len; |
char *p, *s; |
char revbuf[64], timebuf[32]; |
struct cvs_ent_line *l; |
struct cvs_ent *ent; |
|
FILE *fp; |
|
|
|
if (ef->cef_flags & CVS_ENTF_SYNC) |
TAILQ_FOREACH(l, &(ep->cef_ent), entries_list) { |
return (0); |
if (l->buf[0] == 'D') |
|
p = &(l->buf[2]); |
|
else |
|
p = &(l->buf[1]); |
|
|
if ((fp = fopen(ef->cef_bpath, "w")) == NULL) { |
if ((s = strchr(p, '/')) == NULL) |
cvs_log(LP_ERRNO, "failed to open Entries `%s'", ef->cef_bpath); |
fatal("ent_get_line: bad entry line '%s'", l->buf); |
return (-1); |
|
} |
|
|
|
TAILQ_FOREACH(ent, &(ef->cef_ent), ce_list) { |
*s = '\0'; |
if (ent->ce_type == CVS_ENT_DIR) { |
|
putc('D', fp); |
|
timebuf[0] = '\0'; |
|
revbuf[0] = '\0'; |
|
} else { |
|
rcsnum_tostr(ent->ce_rev, revbuf, sizeof(revbuf)); |
|
if (ent->ce_mtime == CVS_DATE_DMSEC && |
|
ent->ce_status != CVS_ENT_ADDED) |
|
strlcpy(timebuf, CVS_DATE_DUMMY, |
|
sizeof(timebuf)); |
|
else if (ent->ce_status == CVS_ENT_ADDED) { |
|
strlcpy(timebuf, "Initial ", sizeof(timebuf)); |
|
strlcat(timebuf, ent->ce_name, sizeof(timebuf)); |
|
} else { |
|
ctime_r(&(ent->ce_mtime), timebuf); |
|
len = strlen(timebuf); |
|
if (len > 0 && timebuf[len - 1] == '\n') |
|
timebuf[--len] = '\0'; |
|
} |
|
} |
|
|
|
if (cvs_cmdop == CVS_OP_SERVER) { |
if (!strcmp(p, name)) { |
if (ent->ce_status == CVS_ENT_UPTODATE) |
*s = '/'; |
strlcpy(timebuf, "up to date", sizeof(timebuf)); |
return (l); |
else |
|
timebuf[0] = '\0'; |
|
} |
} |
|
|
fprintf(fp, "/%s/%s%s/%s/%s/%s\n", ent->ce_name, |
*s = '/'; |
(ent->ce_status == CVS_ENT_REMOVED) ? "-" : "", revbuf, |
|
timebuf, (ent->ce_opts != NULL) ? ent->ce_opts : "", |
|
(ent->ce_tag != NULL) ? ent->ce_tag : ""); |
|
} |
} |
|
|
/* terminating line */ |
return (NULL); |
putc('D', fp); |
|
putc('\n', fp); |
|
|
|
ef->cef_flags |= CVS_ENTF_SYNC; |
|
fclose(fp); |
|
|
|
/* rename Entries.Backup to Entries */ |
|
cvs_rename(ef->cef_bpath, ef->cef_path); |
|
|
|
/* remove Entries.Log */ |
|
cvs_unlink(CVS_PATH_LOGENTRIES); |
|
|
|
return (0); |
|
} |
} |