#include "cvs.h"
#include "getline.h"
static Node *AddEntryNode PROTO((List * list, Entnode *entnode));
static Entnode *fgetentent PROTO((FILE *, char *, int *));
static int fputentent PROTO((FILE *, Entnode *));
static Entnode *subdir_record PROTO((int, const char *, const char *));
static FILE *entfile;
static char *entfilename;
static Entnode *Entnode_Create PROTO ((enum ent_type, const char *,
const char *, const char *,
const char *, const char *,
const char *, const char *));
static Entnode *
Entnode_Create(type, user, vn, ts, options, tag, date, ts_conflict)
enum ent_type type;
const char *user;
const char *vn;
const char *ts;
const char *options;
const char *tag;
const char *date;
const char *ts_conflict;
{
Entnode *ent;
ent = (Entnode *) xmalloc (sizeof (Entnode));
ent->type = type;
ent->user = xstrdup (user);
ent->version = xstrdup (vn);
ent->timestamp = xstrdup (ts ? ts : "");
ent->options = xstrdup (options ? options : "");
ent->tag = xstrdup (tag);
ent->date = xstrdup (date);
ent->conflict = xstrdup (ts_conflict);
return ent;
}
static void Entnode_Destroy PROTO ((Entnode *));
static void
Entnode_Destroy (ent)
Entnode *ent;
{
free (ent->user);
free (ent->version);
free (ent->timestamp);
free (ent->options);
if (ent->tag)
free (ent->tag);
if (ent->date)
free (ent->date);
if (ent->conflict)
free (ent->conflict);
free (ent);
}
static int write_ent_proc PROTO ((Node *, void *));
static int
write_ent_proc (node, closure)
Node *node;
void *closure;
{
Entnode *entnode;
entnode = (Entnode *) node->data;
if (closure != NULL && entnode->type != ENT_FILE)
*(int *) closure = 1;
if (fputentent(entfile, entnode))
error (1, errno, "cannot write %s", entfilename);
return (0);
}
static void
write_entries (list)
List *list;
{
int sawdir;
sawdir = 0;
entfilename = CVSADM_ENTBAK;
entfile = CVS_FOPEN (entfilename, "w+");
if (entfile == NULL)
{
error (0, errno, "cannot rewrite %s", entfilename);
return;
}
(void) walklist (list, write_ent_proc, (void *) &sawdir);
if (! sawdir)
{
struct stickydirtag *sdtp;
sdtp = (struct stickydirtag *) list->list->data;
if (sdtp == NULL || sdtp->subdirs)
if (fprintf (entfile, "D\n") < 0)
error (1, errno, "cannot write %s", entfilename);
}
if (fclose (entfile) == EOF)
error (1, errno, "error closing %s", entfilename);
rename_file (entfilename, CVSADM_ENT);
unlink_file (CVSADM_ENTLOG);
}
void
Scratch_Entry (list, fname)
List *list;
char *fname;
{
Node *node;
if (trace)
#ifdef SERVER_SUPPORT
(void) fprintf (stderr, "%c-> Scratch_Entry(%s)\n",
(server_active) ? 'S' : ' ', fname);
#else
(void) fprintf (stderr, "-> Scratch_Entry(%s)\n", fname);
#endif
if ((node = findnode_fn (list, fname)) != NULL)
{
if (!noexec)
{
entfilename = CVSADM_ENTLOG;
entfile = open_file (entfilename, "a");
if (fprintf (entfile, "R ") < 0)
error (1, errno, "cannot write %s", entfilename);
write_ent_proc (node, NULL);
if (fclose (entfile) == EOF)
error (1, errno, "error closing %s", entfilename);
}
delnode (node);
#ifdef SERVER_SUPPORT
if (server_active)
server_scratch (fname);
#endif
}
}
void
Register (list, fname, vn, ts, options, tag, date, ts_conflict)
List *list;
char *fname;
char *vn;
char *ts;
char *options;
char *tag;
char *date;
char *ts_conflict;
{
Entnode *entnode;
Node *node;
#ifdef SERVER_SUPPORT
if (server_active)
{
server_register (fname, vn, ts, options, tag, date, ts_conflict);
}
#endif
if (trace)
{
#ifdef SERVER_SUPPORT
(void) fprintf (stderr, "%c-> Register(%s, %s, %s%s%s, %s, %s %s)\n",
(server_active) ? 'S' : ' ',
fname, vn, ts ? ts : "",
ts_conflict ? "+" : "", ts_conflict ? ts_conflict : "",
options, tag ? tag : "", date ? date : "");
#else
(void) fprintf (stderr, "-> Register(%s, %s, %s%s%s, %s, %s %s)\n",
fname, vn, ts ? ts : "",
ts_conflict ? "+" : "", ts_conflict ? ts_conflict : "",
options, tag ? tag : "", date ? date : "");
#endif
}
entnode = Entnode_Create (ENT_FILE, fname, vn, ts, options, tag, date,
ts_conflict);
node = AddEntryNode (list, entnode);
if (!noexec)
{
entfilename = CVSADM_ENTLOG;
entfile = open_file (entfilename, "a");
if (fprintf (entfile, "A ") < 0)
error (1, errno, "cannot write %s", entfilename);
write_ent_proc (node, NULL);
if (fclose (entfile) == EOF)
error (1, errno, "error closing %s", entfilename);
}
}
static void
freesdt (p)
Node *p;
{
struct stickydirtag *sdtp;
sdtp = (struct stickydirtag *) p->data;
if (sdtp->tag)
free (sdtp->tag);
if (sdtp->date)
free (sdtp->date);
free ((char *) sdtp);
}
static Entnode *
fgetentent(fpin, cmd, sawdir)
FILE *fpin;
char *cmd;
int *sawdir;
{
Entnode *ent;
char *line;
size_t line_chars_allocated;
register char *cp;
enum ent_type type;
char *l, *user, *vn, *ts, *options;
char *tag_or_date, *tag, *date, *ts_conflict;
int line_length;
line = NULL;
line_chars_allocated = 0;
ent = NULL;
while ((line_length = getline (&line, &line_chars_allocated, fpin)) > 0)
{
l = line;
if (cmd != NULL)
{
if (l[1] != ' ')
*cmd = 'A';
else
{
*cmd = l[0];
l += 2;
}
}
type = ENT_FILE;
if (l[0] == 'D')
{
type = ENT_SUBDIR;
*sawdir = 1;
++l;
}
if (l[0] != '/')
continue;
user = l + 1;
if ((cp = strchr (user, '/')) == NULL)
continue;
*cp++ = '\0';
vn = cp;
if ((cp = strchr (vn, '/')) == NULL)
continue;
*cp++ = '\0';
ts = cp;
if ((cp = strchr (ts, '/')) == NULL)
continue;
*cp++ = '\0';
options = cp;
if ((cp = strchr (options, '/')) == NULL)
continue;
*cp++ = '\0';
tag_or_date = cp;
if ((cp = strchr (tag_or_date, '\n')) == NULL)
continue;
*cp = '\0';
tag = (char *) NULL;
date = (char *) NULL;
if (*tag_or_date == 'T')
tag = tag_or_date + 1;
else if (*tag_or_date == 'D')
date = tag_or_date + 1;
if ((ts_conflict = strchr (ts, '+')))
*ts_conflict++ = '\0';
{
struct stat sb;
if (strlen (ts) > 30 && CVS_STAT (user, &sb) == 0)
{
char *c = ctime (&sb.st_mtime);
if (!strncmp (ts + 25, c, 24))
ts = time_stamp (user);
else
{
ts += 24;
ts[0] = '*';
}
}
}
ent = Entnode_Create (type, user, vn, ts, options, tag, date,
ts_conflict);
break;
}
if (line_length < 0 && !feof (fpin))
error (0, errno, "cannot read entries file");
free (line);
return ent;
}
static int
fputentent(fp, p)
FILE *fp;
Entnode *p;
{
switch (p->type)
{
case ENT_FILE:
break;
case ENT_SUBDIR:
if (fprintf (fp, "D") < 0)
return 1;
break;
}
if (fprintf (fp, "/%s/%s/%s", p->user, p->version, p->timestamp) < 0)
return 1;
if (p->conflict)
{
if (fprintf (fp, "+%s", p->conflict) < 0)
return 1;
}
if (fprintf (fp, "/%s/", p->options) < 0)
return 1;
if (p->tag)
{
if (fprintf (fp, "T%s\n", p->tag) < 0)
return 1;
}
else if (p->date)
{
if (fprintf (fp, "D%s\n", p->date) < 0)
return 1;
}
else
{
if (fprintf (fp, "\n") < 0)
return 1;
}
return 0;
}
List *
Entries_Open (aflag, update_dir)
int aflag;
char *update_dir;
{
List *entries;
struct stickydirtag *sdtp = NULL;
Entnode *ent;
char *dirtag, *dirdate;
int dirnonbranch;
int do_rewrite = 0;
FILE *fpin;
int sawdir;
entries = getlist ();
ParseTag (&dirtag, &dirdate, &dirnonbranch);
if (aflag || dirtag || dirdate)
{
sdtp = (struct stickydirtag *) xmalloc (sizeof (*sdtp));
memset ((char *) sdtp, 0, sizeof (*sdtp));
sdtp->aflag = aflag;
sdtp->tag = xstrdup (dirtag);
sdtp->date = xstrdup (dirdate);
sdtp->nonbranch = dirnonbranch;
entries->list->data = (char *) sdtp;
entries->list->delproc = freesdt;
}
sawdir = 0;
fpin = CVS_FOPEN (CVSADM_ENT, "r");
if (fpin == NULL)
{
if (update_dir != NULL)
error (0, 0, "in directory %s:", update_dir);
error (0, errno, "cannot open %s for reading", CVSADM_ENT);
}
else
{
while ((ent = fgetentent (fpin, (char *) NULL, &sawdir)) != NULL)
{
(void) AddEntryNode (entries, ent);
}
fclose (fpin);
}
fpin = CVS_FOPEN (CVSADM_ENTLOG, "r");
if (fpin != NULL)
{
char cmd;
Node *node;
while ((ent = fgetentent (fpin, &cmd, &sawdir)) != NULL)
{
switch (cmd)
{
case 'A':
(void) AddEntryNode (entries, ent);
break;
case 'R':
node = findnode_fn (entries, ent->user);
if (node != NULL)
delnode (node);
Entnode_Destroy (ent);
break;
default:
break;
}
}
do_rewrite = 1;
fclose (fpin);
}
if (sdtp != NULL)
sdtp->subdirs = sawdir;
else if (! sawdir)
{
sdtp = (struct stickydirtag *) xmalloc (sizeof (*sdtp));
memset ((char *) sdtp, 0, sizeof (*sdtp));
sdtp->subdirs = 0;
entries->list->data = (char *) sdtp;
entries->list->delproc = freesdt;
}
if (do_rewrite && !noexec)
write_entries (entries);
if (dirtag)
free (dirtag);
if (dirdate)
free (dirdate);
return (entries);
}
void
Entries_Close(list)
List *list;
{
if (list)
{
if (!noexec)
{
if (isfile (CVSADM_ENTLOG))
write_entries (list);
}
dellist(&list);
}
}
static void
Entries_delproc (node)
Node *node;
{
Entnode *p;
p = (Entnode *) node->data;
Entnode_Destroy(p);
}
static Node *
AddEntryNode (list, entdata)
List *list;
Entnode *entdata;
{
Node *p;
if ((p = findnode_fn (list, entdata->user)) != NULL)
{
delnode (p);
}
p = getnode ();
p->type = ENTRIES;
p->delproc = Entries_delproc;
p->key = xstrdup (entdata->user);
p->data = (char *) entdata;
addnode (list, p);
return (p);
}
void
WriteTag (dir, tag, date, nonbranch, update_dir, repository)
char *dir;
char *tag;
char *date;
int nonbranch;
char *update_dir;
char *repository;
{
FILE *fout;
char *tmp;
if (noexec)
return;
tmp = xmalloc ((dir ? strlen (dir) : 0)
+ sizeof (CVSADM_TAG)
+ 10);
if (dir == NULL)
(void) strcpy (tmp, CVSADM_TAG);
else
(void) sprintf (tmp, "%s/%s", dir, CVSADM_TAG);
if (tag || date)
{
fout = open_file (tmp, "w+");
if (tag)
{
if (nonbranch)
{
if (fprintf (fout, "N%s\n", tag) < 0)
error (1, errno, "write to %s failed", tmp);
}
else
{
if (fprintf (fout, "T%s\n", tag) < 0)
error (1, errno, "write to %s failed", tmp);
}
}
else
{
if (fprintf (fout, "D%s\n", date) < 0)
error (1, errno, "write to %s failed", tmp);
}
if (fclose (fout) == EOF)
error (1, errno, "cannot close %s", tmp);
}
else
if (unlink_file (tmp) < 0 && ! existence_error (errno))
error (1, errno, "cannot remove %s", tmp);
free (tmp);
#ifdef SERVER_SUPPORT
if (server_active)
server_set_sticky (update_dir, repository, tag, date, nonbranch);
#endif
}
void
ParseTag (tagp, datep, nonbranchp)
char **tagp;
char **datep;
int *nonbranchp;
{
FILE *fp;
if (tagp)
*tagp = (char *) NULL;
if (datep)
*datep = (char *) NULL;
if (nonbranchp != NULL)
*nonbranchp = 0;
fp = CVS_FOPEN (CVSADM_TAG, "r");
if (fp)
{
char *line;
int line_length;
size_t line_chars_allocated;
line = NULL;
line_chars_allocated = 0;
if ((line_length = getline (&line, &line_chars_allocated, fp)) > 0)
{
if (line[line_length - 1] == '\n')
line[--line_length] = '\0';
switch (*line)
{
case 'T':
if (tagp != NULL)
*tagp = xstrdup (line + 1);
break;
case 'D':
if (datep != NULL)
*datep = xstrdup (line + 1);
break;
case 'N':
if (tagp != NULL)
*tagp = xstrdup (line + 1);
if (nonbranchp != NULL)
*nonbranchp = 1;
break;
default:
break;
}
}
if (line_length < 0)
{
if (feof (fp))
error (0, 0, "cannot read %s: end of file", CVSADM_TAG);
else
error (0, errno, "cannot read %s", CVSADM_TAG);
}
if (fclose (fp) < 0)
error (0, errno, "cannot close %s", CVSADM_TAG);
free (line);
}
else if (!existence_error (errno))
error (0, errno, "cannot open %s", CVSADM_TAG);
}
void
Subdirs_Known (entries)
List *entries;
{
struct stickydirtag *sdtp;
sdtp = (struct stickydirtag *) entries->list->data;
if (sdtp != NULL && ! sdtp->subdirs)
{
FILE *fp;
sdtp->subdirs = 1;
if (!noexec)
{
fp = CVS_FOPEN (CVSADM_ENTLOG, "a");
if (fp == NULL)
{
int save_errno = errno;
if (! isdir (CVSADM))
return;
error (1, save_errno, "cannot open %s", entfilename);
}
else
{
if (fclose (fp) == EOF)
error (1, errno, "cannot close %s", CVSADM_ENTLOG);
}
}
}
}
static Entnode *
subdir_record (cmd, parent, dir)
int cmd;
const char *parent;
const char *dir;
{
Entnode *entnode;
entnode = Entnode_Create (ENT_SUBDIR, dir, "", "", "",
(char *) NULL, (char *) NULL,
(char *) NULL);
if (!noexec)
{
if (parent == NULL)
entfilename = CVSADM_ENTLOG;
else
{
entfilename = xmalloc (strlen (parent)
+ sizeof CVSADM_ENTLOG
+ 10);
sprintf (entfilename, "%s/%s", parent, CVSADM_ENTLOG);
}
entfile = CVS_FOPEN (entfilename, "a");
if (entfile == NULL)
{
int save_errno = errno;
if (parent == NULL)
{
if (! isdir (CVSADM))
return entnode;
}
else
{
sprintf (entfilename, "%s/%s", parent, CVSADM);
if (! isdir (entfilename))
{
free (entfilename);
entfilename = NULL;
return entnode;
}
}
error (1, save_errno, "cannot open %s", entfilename);
}
if (fprintf (entfile, "%c ", cmd) < 0)
error (1, errno, "cannot write %s", entfilename);
if (fputentent (entfile, entnode) != 0)
error (1, errno, "cannot write %s", entfilename);
if (fclose (entfile) == EOF)
error (1, errno, "error closing %s", entfilename);
if (parent != NULL)
{
free (entfilename);
entfilename = NULL;
}
}
return entnode;
}
void
Subdir_Register (entries, parent, dir)
List *entries;
const char *parent;
const char *dir;
{
Entnode *entnode;
if (dir[0] == '.' && dir[1] == '\0')
return;
entnode = subdir_record ('A', parent, dir);
if (entries != NULL && (parent == NULL || strcmp (parent, ".") == 0))
(void) AddEntryNode (entries, entnode);
else
Entnode_Destroy (entnode);
}
void
Subdir_Deregister (entries, parent, dir)
List *entries;
const char *parent;
const char *dir;
{
Entnode *entnode;
entnode = subdir_record ('R', parent, dir);
Entnode_Destroy (entnode);
if (entries != NULL && (parent == NULL || strcmp (parent, ".") == 0))
{
Node *p;
p = findnode_fn (entries, dir);
if (p != NULL)
delnode (p);
}
}
enum base_walk {
BASE_REGISTER,
BASE_GET,
BASE_DEREGISTER
};
static void base_walk PROTO ((enum base_walk, struct file_info *, char **));
static void
base_walk (code, finfo, rev)
enum base_walk code;
struct file_info *finfo;
char **rev;
{
FILE *fp;
char *line;
size_t line_allocated;
FILE *newf;
char *baserev_fullname;
char *baserevtmp_fullname;
line = NULL;
line_allocated = 0;
newf = NULL;
baserev_fullname = xmalloc (sizeof (CVSADM_BASEREV)
+ strlen (finfo->update_dir)
+ 2);
baserev_fullname[0] = '\0';
baserevtmp_fullname = xmalloc (sizeof (CVSADM_BASEREVTMP)
+ strlen (finfo->update_dir)
+ 2);
baserevtmp_fullname[0] = '\0';
if (finfo->update_dir[0] != '\0')
{
strcat (baserev_fullname, finfo->update_dir);
strcat (baserev_fullname, "/");
strcat (baserevtmp_fullname, finfo->update_dir);
strcat (baserevtmp_fullname, "/");
}
strcat (baserev_fullname, CVSADM_BASEREV);
strcat (baserevtmp_fullname, CVSADM_BASEREVTMP);
fp = CVS_FOPEN (CVSADM_BASEREV, "r");
if (fp == NULL)
{
if (!existence_error (errno))
{
error (0, errno, "cannot open %s for reading", baserev_fullname);
goto out;
}
}
switch (code)
{
case BASE_REGISTER:
case BASE_DEREGISTER:
newf = CVS_FOPEN (CVSADM_BASEREVTMP, "w");
if (newf == NULL)
{
error (0, errno, "cannot open %s for writing",
baserevtmp_fullname);
goto out;
}
break;
case BASE_GET:
*rev = NULL;
break;
}
if (fp != NULL)
{
while (getline (&line, &line_allocated, fp) >= 0)
{
char *linefile;
char *p;
char *linerev;
if (line[0] != 'B')
continue;
linefile = line + 1;
p = strchr (linefile, '/');
if (p == NULL)
continue;
linerev = p + 1;
p = strchr (linerev, '/');
if (p == NULL)
continue;
linerev[-1] = '\0';
if (fncmp (linefile, finfo->file) == 0)
{
switch (code)
{
case BASE_REGISTER:
case BASE_DEREGISTER:
break;
case BASE_GET:
*p = '\0';
*rev = xstrdup (linerev);
*p = '/';
goto got_it;
}
}
else
{
linerev[-1] = '/';
switch (code)
{
case BASE_REGISTER:
case BASE_DEREGISTER:
if (fprintf (newf, "%s\n", line) < 0)
error (0, errno, "error writing %s",
baserevtmp_fullname);
break;
case BASE_GET:
break;
}
}
}
if (ferror (fp))
error (0, errno, "cannot read %s", baserev_fullname);
}
got_it:
if (code == BASE_REGISTER)
{
if (fprintf (newf, "B%s/%s/\n", finfo->file, *rev) < 0)
error (0, errno, "error writing %s",
baserevtmp_fullname);
}
out:
if (line != NULL)
free (line);
if (fp != NULL)
{
if (fclose (fp) < 0)
error (0, errno, "cannot close %s", baserev_fullname);
}
if (newf != NULL)
{
if (fclose (newf) < 0)
error (0, errno, "cannot close %s", baserevtmp_fullname);
rename_file (CVSADM_BASEREVTMP, CVSADM_BASEREV);
}
free (baserev_fullname);
free (baserevtmp_fullname);
}
char *
base_get (finfo)
struct file_info *finfo;
{
char *rev;
base_walk (BASE_GET, finfo, &rev);
return rev;
}
void
base_register (finfo, rev)
struct file_info *finfo;
char *rev;
{
base_walk (BASE_REGISTER, finfo, &rev);
}
void
base_deregister (finfo)
struct file_info *finfo;
{
base_walk (BASE_DEREGISTER, finfo, NULL);
}