File: [local] / src / usr.bin / cvs / entries.c (download)
Revision 1.107, Thu Oct 13 20:51:25 2016 UTC (7 years, 7 months ago) by fcambus
Branch: MAIN
CVS Tags: OPENBSD_7_5_BASE, OPENBSD_7_5, OPENBSD_7_4_BASE, OPENBSD_7_4, OPENBSD_7_3_BASE, OPENBSD_7_3, OPENBSD_7_2_BASE, OPENBSD_7_2, OPENBSD_7_1_BASE, OPENBSD_7_1, OPENBSD_7_0_BASE, OPENBSD_7_0, OPENBSD_6_9_BASE, OPENBSD_6_9, OPENBSD_6_8_BASE, OPENBSD_6_8, OPENBSD_6_7_BASE, OPENBSD_6_7, OPENBSD_6_6_BASE, OPENBSD_6_6, OPENBSD_6_5_BASE, OPENBSD_6_5, OPENBSD_6_4_BASE, OPENBSD_6_4, OPENBSD_6_3_BASE, OPENBSD_6_3, OPENBSD_6_2_BASE, OPENBSD_6_2, OPENBSD_6_1_BASE, OPENBSD_6_1, HEAD Changes since 1.106: +3 -4 lines
rcsnum_free() -> free() cleanups. From Michael W. Bombardieri.
OK millert@
|
/* $OpenBSD: entries.c,v 1.107 2016/10/13 20:51:25 fcambus Exp $ */
/*
* Copyright (c) 2006 Joris Vink <joris@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "cvs.h"
#include "remote.h"
#define CVS_ENTRIES_NFIELDS 6
#define CVS_ENTRIES_DELIM '/'
static struct cvs_ent_line *ent_get_line(CVSENTRIES *, const char *);
CVSENTRIES *current_list = NULL;
CVSENTRIES *
cvs_ent_open(const char *dir)
{
FILE *fp;
CVSENTRIES *ep;
char *p, buf[PATH_MAX];
struct cvs_ent *ent;
struct cvs_ent_line *line;
cvs_log(LP_TRACE, "cvs_ent_open(%s)", dir);
(void)xsnprintf(buf, sizeof(buf), "%s/%s", dir, CVS_PATH_ENTRIES);
if (current_list != NULL && !strcmp(current_list->cef_path, buf))
return (current_list);
if (current_list != NULL) {
cvs_ent_close(current_list, ENT_SYNC);
current_list = NULL;
}
ep = xcalloc(1, sizeof(*ep));
ep->cef_path = xstrdup(buf);
(void)xsnprintf(buf, sizeof(buf), "%s/%s",
dir, CVS_PATH_BACKUPENTRIES);
ep->cef_bpath = xstrdup(buf);
(void)xsnprintf(buf, sizeof(buf), "%s/%s", dir, CVS_PATH_LOGENTRIES);
ep->cef_lpath = xstrdup(buf);
TAILQ_INIT(&(ep->cef_ent));
if ((fp = fopen(ep->cef_path, "r")) != NULL) {
while (fgets(buf, sizeof(buf), fp)) {
buf[strcspn(buf, "\n")] = '\0';
if (buf[0] == 'D' && buf[1] == '\0')
break;
line = xmalloc(sizeof(*line));
line->buf = xstrdup(buf);
TAILQ_INSERT_TAIL(&(ep->cef_ent), line, entries_list);
}
(void)fclose(fp);
}
if ((fp = fopen(ep->cef_lpath, "r")) != NULL) {
while (fgets(buf, sizeof(buf), fp)) {
buf[strcspn(buf, "\n")] = '\0';
if (strlen(buf) < 2)
fatal("cvs_ent_open: %s: malformed line %s",
ep->cef_lpath, buf);
p = &buf[2];
if (buf[0] == 'A') {
line = xmalloc(sizeof(*line));
line->buf = xstrdup(p);
TAILQ_INSERT_TAIL(&(ep->cef_ent), line,
entries_list);
} else if (buf[0] == 'R') {
ent = cvs_ent_parse(p);
line = ent_get_line(ep, ent->ce_name);
if (line != NULL) {
TAILQ_REMOVE(&(ep->cef_ent), line,
entries_list);
free(line->buf);
free(line);
}
cvs_ent_free(ent);
}
}
(void)fclose(fp);
}
current_list = ep;
return (ep);
}
struct cvs_ent *
cvs_ent_parse(const char *entry)
{
int i;
struct tm t, dt;
struct cvs_ent *ent;
char *fields[CVS_ENTRIES_NFIELDS], *buf, *sp, *dp, *p;
buf = sp = xstrdup(entry);
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)
fatal("missing fields in entry line '%s'", entry);
ent = xmalloc(sizeof(*ent));
ent->ce_buf = buf;
if (*fields[0] == '\0')
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;
ent->ce_name = fields[1];
ent->ce_rev = NULL;
ent->ce_date = -1;
ent->ce_tag = NULL;
ent->ce_time = NULL;
if (ent->ce_type == CVS_ENT_FILE) {
if (*fields[2] == '-') {
ent->ce_status = CVS_ENT_REMOVED;
sp = fields[2] + 1;
} else if (*fields[2] == CVS_SERVER_QUESTIONABLE) {
sp = NULL;
ent->ce_status = CVS_ENT_UNKNOWN;
} else {
sp = fields[2];
if (fields[2][0] == '0' && fields[2][1] == '\0')
ent->ce_status = CVS_ENT_ADDED;
}
if (sp != NULL) {
if ((ent->ce_rev = rcsnum_parse(sp)) == NULL) {
fatal("failed to parse entry revision '%s'",
entry);
}
}
if (fields[3][0] == '\0' ||
strncmp(fields[3], CVS_DATE_DUMMY,
sizeof(CVS_DATE_DUMMY) - 1) == 0 ||
strncmp(fields[3], "Initial ", 8) == 0 ||
strcmp(fields[3], "Result of merge") == 0) {
ent->ce_mtime = CVS_DATE_DMSEC;
} else if (cvs_server_active == 1 &&
strncmp(fields[3], CVS_SERVER_UNCHANGED,
strlen(CVS_SERVER_UNCHANGED)) == 0) {
ent->ce_mtime = CVS_SERVER_UPTODATE;
} else {
p = fields[3];
if (strncmp(fields[3], "Result of merge+", 16) == 0)
p += 16;
ent->ce_time = xstrdup(p);
/* Date field can be a '+=' with remote to indicate
* conflict. In this case do nothing. */
if (strptime(p, "%a %b %d %T %Y", &t) != NULL) {
t.tm_isdst = -1; /* Figure out DST. */
t.tm_gmtoff = 0;
ent->ce_mtime = mktime(&t);
ent->ce_mtime += t.tm_gmtoff;
}
}
}
ent->ce_conflict = fields[3];
if ((dp = strchr(ent->ce_conflict, '+')) != NULL)
*dp = '\0';
else
ent->ce_conflict = NULL;
if (strcmp(fields[4], ""))
ent->ce_opts = fields[4];
else
ent->ce_opts = NULL;
if (strcmp(fields[5], "")) {
switch (*fields[5]) {
case 'D':
if (sscanf(fields[5] + 1, "%d.%d.%d.%d.%d.%d",
&dt.tm_year, &dt.tm_mon, &dt.tm_mday,
&dt.tm_hour, &dt.tm_min, &dt.tm_sec) != 6)
fatal("wrong date specification");
dt.tm_year -= 1900;
dt.tm_mon -= 1;
ent->ce_date = timegm(&dt);
ent->ce_tag = NULL;
break;
case 'T':
ent->ce_tag = fields[5] + 1;
break;
default:
fatal("invalid sticky entry");
}
}
return (ent);
}
struct cvs_ent *
cvs_ent_get(CVSENTRIES *ep, const char *name)
{
struct cvs_ent *ent;
struct cvs_ent_line *l;
l = ent_get_line(ep, name);
if (l == NULL)
return (NULL);
ent = cvs_ent_parse(l->buf);
return (ent);
}
void
cvs_ent_close(CVSENTRIES *ep, int writefile)
{
FILE *fp;
struct cvs_ent_line *l;
int dflag;
dflag = 1;
cvs_log(LP_TRACE, "cvs_ent_close(%s, %d)", ep->cef_bpath, writefile);
if (cvs_cmdop == CVS_OP_EXPORT)
writefile = 0;
fp = NULL;
if (writefile)
fp = fopen(ep->cef_bpath, "w");
while ((l = TAILQ_FIRST(&(ep->cef_ent))) != NULL) {
if (fp != NULL) {
if (l->buf[0] == 'D')
dflag = 0;
fputs(l->buf, fp);
fputc('\n', fp);
}
TAILQ_REMOVE(&(ep->cef_ent), l, entries_list);
free(l->buf);
free(l);
}
if (fp != NULL) {
if (dflag) {
fputc('D', fp);
fputc('\n', fp);
}
(void)fclose(fp);
if (rename(ep->cef_bpath, ep->cef_path) == -1)
fatal("cvs_ent_close: rename: `%s'->`%s': %s",
ep->cef_bpath, ep->cef_path, strerror(errno));
(void)unlink(ep->cef_lpath);
}
free(ep->cef_path);
free(ep->cef_bpath);
free(ep->cef_lpath);
free(ep);
}
void
cvs_ent_add(CVSENTRIES *ep, const char *line)
{
FILE *fp;
struct cvs_ent_line *l;
struct cvs_ent *ent;
if ((ent = cvs_ent_parse(line)) == NULL)
fatal("cvs_ent_add: parsing failed '%s'", line);
l = ent_get_line(ep, ent->ce_name);
if (l != NULL)
cvs_ent_remove(ep, ent->ce_name);
cvs_ent_free(ent);
if (cvs_server_active == 0)
cvs_log(LP_TRACE, "cvs_ent_add(%s, %s)", ep->cef_path, line);
if ((fp = fopen(ep->cef_lpath, "a")) == NULL)
fatal("cvs_ent_add: fopen: `%s': %s",
ep->cef_lpath, strerror(errno));
fputs("A ", fp);
fputs(line, fp);
fputc('\n', fp);
(void)fclose(fp);
l = xmalloc(sizeof(*l));
l->buf = xstrdup(line);
TAILQ_INSERT_TAIL(&(ep->cef_ent), l, entries_list);
}
void
cvs_ent_remove(CVSENTRIES *ep, const char *name)
{
FILE *fp;
struct cvs_ent_line *l;
if (cvs_server_active == 0)
cvs_log(LP_TRACE, "cvs_ent_remove(%s, %s)", ep->cef_path, name);
l = ent_get_line(ep, name);
if (l == NULL)
return;
if ((fp = fopen(ep->cef_lpath, "a")) == NULL)
fatal("cvs_ent_remove: fopen: `%s': %s", ep->cef_lpath,
strerror(errno));
fputs("R ", fp);
fputs(l->buf, fp);
fputc('\n', fp);
(void)fclose(fp);
TAILQ_REMOVE(&(ep->cef_ent), l, entries_list);
free(l->buf);
free(l);
}
/*
* cvs_ent_line_str()
*
* Build CVS/Entries line.
*
*/
void
cvs_ent_line_str(const char *name, char *rev, char *tstamp, char *opts,
char *sticky, int isdir, int isremoved, char *buf, size_t len)
{
if (isdir == 1) {
(void)xsnprintf(buf, len, "D/%s////", name);
return;
}
(void)xsnprintf(buf, len, "/%s/%s%s/%s/%s/%s",
name, isremoved == 1 ? "-" : "", rev, tstamp, opts, sticky);
}
void
cvs_ent_free(struct cvs_ent *ent)
{
free(ent->ce_rev);
free(ent->ce_time);
free(ent->ce_buf);
free(ent);
}
static struct cvs_ent_line *
ent_get_line(CVSENTRIES *ep, const char *name)
{
char *p, *s;
struct cvs_ent_line *l;
TAILQ_FOREACH(l, &(ep->cef_ent), entries_list) {
if (l->buf[0] == 'D')
p = &(l->buf[2]);
else
p = &(l->buf[1]);
if ((s = strchr(p, '/')) == NULL)
fatal("ent_get_line: bad entry line '%s'", l->buf);
*s = '\0';
if (!strcmp(p, name)) {
*s = '/';
return (l);
}
*s = '/';
}
return (NULL);
}
void
cvs_parse_tagfile(char *dir, char **tagp, char **datep, int *nbp)
{
FILE *fp;
int i, linenum;
size_t len;
struct tm datetm;
char linebuf[128], tagpath[PATH_MAX];
cvs_directory_date = -1;
if (tagp != NULL)
*tagp = NULL;
if (datep != NULL)
*datep = NULL;
if (nbp != NULL)
*nbp = 0;
i = snprintf(tagpath, PATH_MAX, "%s/%s", dir, CVS_PATH_TAG);
if (i < 0 || i >= PATH_MAX)
return;
if ((fp = fopen(tagpath, "r")) == NULL) {
if (errno != ENOENT)
cvs_log(LP_NOTICE, "failed to open `%s' : %s", tagpath,
strerror(errno));
return;
}
linenum = 0;
while (fgets(linebuf, (int)sizeof(linebuf), fp) != NULL) {
linenum++;
if ((len = strlen(linebuf)) == 0)
continue;
if (linebuf[len - 1] != '\n') {
cvs_log(LP_NOTICE, "line too long in `%s:%d'",
tagpath, linenum);
break;
}
linebuf[--len] = '\0';
switch (*linebuf) {
case 'T':
if (tagp != NULL)
*tagp = xstrdup(linebuf + 1);
break;
case 'D':
if (sscanf(linebuf + 1, "%d.%d.%d.%d.%d.%d",
&datetm.tm_year, &datetm.tm_mon, &datetm.tm_mday,
&datetm.tm_hour, &datetm.tm_min, &datetm.tm_sec) !=
6)
fatal("wrong date specification");
datetm.tm_year -= 1900;
datetm.tm_mon -= 1;
cvs_directory_date = timegm(&datetm);
if (datep != NULL)
*datep = xstrdup(linebuf + 1);
break;
case 'N':
if (tagp != NULL)
*tagp = xstrdup(linebuf + 1);
if (nbp != NULL)
*nbp = 1;
break;
default:
break;
}
}
if (ferror(fp))
cvs_log(LP_NOTICE, "failed to read line from `%s'", tagpath);
(void)fclose(fp);
}
void
cvs_write_tagfile(const char *dir, char *tag, char *date)
{
FILE *fp;
RCSNUM *rev;
char tagpath[PATH_MAX];
char sticky[CVS_REV_BUFSZ];
struct tm datetm;
int i;
cvs_log(LP_TRACE, "cvs_write_tagfile(%s, %s, %s)", dir,
tag != NULL ? tag : "", date != NULL ? date : "");
if (cvs_noexec == 1)
return;
i = snprintf(tagpath, PATH_MAX, "%s/%s", dir, CVS_PATH_TAG);
if (i < 0 || i >= PATH_MAX)
return;
if (tag != NULL || cvs_specified_date != -1 ||
cvs_directory_date != -1) {
if ((fp = fopen(tagpath, "w+")) == NULL) {
if (errno != ENOENT) {
cvs_log(LP_NOTICE, "failed to open `%s' : %s",
tagpath, strerror(errno));
}
return;
}
if (tag != NULL) {
if ((rev = rcsnum_parse(tag)) != NULL) {
(void)xsnprintf(sticky, sizeof(sticky),
"N%s", tag);
free(rev);
} else {
(void)xsnprintf(sticky, sizeof(sticky),
"T%s", tag);
}
} else {
if (cvs_specified_date != -1)
gmtime_r(&cvs_specified_date, &datetm);
else
gmtime_r(&cvs_directory_date, &datetm);
(void)strftime(sticky, sizeof(sticky),
"D"CVS_DATE_FMT, &datetm);
}
(void)fprintf(fp, "%s\n", sticky);
(void)fclose(fp);
}
}