#include "cvs.h"
#include "getline.h"
#include "fileattr.h"
static void fileattr_read (void);
static int writeattr_proc (Node *, void *);
static char *fileattr_stored_repos;
static List *attrlist;
static char *fileattr_default_attrs;
static int attr_read_attempted;
static int attrs_modified;
struct unrecog {
char *line;
struct unrecog *next;
};
static struct unrecog *unrecog_head;
void
fileattr_startdir (const char *repos)
{
assert (fileattr_stored_repos == NULL);
fileattr_stored_repos = xstrdup (repos);
assert (attrlist == NULL);
attr_read_attempted = 0;
assert (unrecog_head == NULL);
}
static void
fileattr_delproc (Node *node)
{
assert (node->data != NULL);
free (node->data);
node->data = NULL;
}
static void
fileattr_read (void)
{
char *fname;
FILE *fp;
char *line = NULL;
size_t line_len = 0;
if (attr_read_attempted)
return;
assert (fileattr_stored_repos != NULL);
fname = Xasprintf ("%s/%s", fileattr_stored_repos, CVSREP_FILEATTR);
attr_read_attempted = 1;
fp = CVS_FOPEN (fname, FOPEN_BINARY_READ);
if (fp == NULL)
{
if (!existence_error (errno))
error (0, errno, "cannot read %s", fname);
free (fname);
return;
}
attrlist = getlist ();
while (1) {
int nread;
nread = getline (&line, &line_len, fp);
if (nread < 0)
break;
if (line[nread - 1] == '\n') line[nread - 1] = '\0';
if (line[0] == 'F')
{
char *p;
Node *newnode;
p = strchr (line, '\t');
if (p == NULL)
error (1, 0,
"file attribute database corruption: tab missing in %s",
primary_root_inverse_translate (fname));
*p++ = '\0';
newnode = getnode ();
newnode->type = FILEATTR;
newnode->delproc = fileattr_delproc;
newnode->key = xstrdup (line + 1);
newnode->data = xstrdup (p);
if (addnode (attrlist, newnode) != 0)
freenode (newnode);
}
else if (line[0] == 'D')
{
char *p;
p = strchr (line, '\t');
if (p == NULL)
error (1, 0,
"file attribute database corruption: tab missing in %s",
fname);
++p;
if (fileattr_default_attrs) free (fileattr_default_attrs);
fileattr_default_attrs = xstrdup (p);
}
else
{
struct unrecog *new;
new = xmalloc (sizeof (struct unrecog));
new->line = xstrdup (line);
new->next = unrecog_head;
unrecog_head = new;
}
}
if (ferror (fp))
error (0, errno, "cannot read %s", fname);
if (line != NULL)
free (line);
if (fclose (fp) < 0)
error (0, errno, "cannot close %s", fname);
attrs_modified = 0;
free (fname);
}
char *
fileattr_get (const char *filename, const char *attrname)
{
Node *node;
size_t attrname_len = strlen (attrname);
char *p;
if (attrlist == NULL)
fileattr_read ();
if (attrlist == NULL)
return NULL;
if (filename == NULL)
p = fileattr_default_attrs;
else
{
node = findnode (attrlist, filename);
if (node == NULL)
return NULL;
p = node->data;
}
while (p)
{
if (strncmp (attrname, p, attrname_len) == 0
&& p[attrname_len] == '=')
{
return p + attrname_len + 1;
}
p = strchr (p, ';');
if (p == NULL)
break;
++p;
}
return NULL;
}
char *
fileattr_get0 (const char *filename, const char *attrname)
{
char *cp;
char *cpend;
char *retval;
cp = fileattr_get (filename, attrname);
if (cp == NULL)
return NULL;
cpend = strchr (cp, ';');
if (cpend == NULL)
cpend = cp + strlen (cp);
retval = xmalloc (cpend - cp + 1);
strncpy (retval, cp, cpend - cp);
retval[cpend - cp] = '\0';
return retval;
}
char *
fileattr_modify (char *list, const char *attrname, const char *attrval, int namevalsep, int entsep)
{
char *retval;
char *rp;
size_t attrname_len = strlen (attrname);
char *pre;
char *preend;
char *post;
char *p;
char *p2;
p = list;
pre = list;
preend = NULL;
post = NULL;
p2 = NULL;
if (list != NULL)
{
while (1) {
p2 = strchr (p, entsep);
if (p2 == NULL)
{
p2 = p + strlen (p);
if (preend == NULL)
preend = p2;
}
else
++p2;
if (strncmp (attrname, p, attrname_len) == 0
&& p[attrname_len] == namevalsep)
{
preend = p;
if (preend > list)
--preend;
post = p2;
}
if (p2[0] == '\0')
break;
p = p2;
}
}
if (post == NULL)
post = p2;
if (preend == pre && attrval == NULL && post == p2)
return NULL;
retval = xmalloc ((preend - pre)
+ 1
+ (attrval == NULL ? 0 : (attrname_len + 1
+ strlen (attrval)))
+ 1
+ (p2 - post)
+ 1);
if (preend != pre)
{
strncpy (retval, pre, preend - pre);
rp = retval + (preend - pre);
if (attrval != NULL)
*rp++ = entsep;
*rp = '\0';
}
else
retval[0] = '\0';
if (attrval != NULL)
{
strcat (retval, attrname);
rp = retval + strlen (retval);
*rp++ = namevalsep;
strcpy (rp, attrval);
}
if (post != p2)
{
rp = retval + strlen (retval);
if (preend != pre || attrval != NULL)
*rp++ = entsep;
strncpy (rp, post, p2 - post);
rp += p2 - post;
*rp = '\0';
}
return retval;
}
void
fileattr_set (const char *filename, const char *attrname, const char *attrval)
{
Node *node;
char *p;
if (filename == NULL)
{
p = fileattr_modify (fileattr_default_attrs, attrname, attrval,
'=', ';');
if (fileattr_default_attrs != NULL)
free (fileattr_default_attrs);
fileattr_default_attrs = p;
attrs_modified = 1;
return;
}
if (attrlist == NULL)
fileattr_read ();
if (attrlist == NULL)
{
attrlist = getlist ();
}
node = findnode (attrlist, filename);
if (node == NULL)
{
if (attrval == NULL)
return;
node = getnode ();
node->type = FILEATTR;
node->delproc = fileattr_delproc;
node->key = xstrdup (filename);
node->data = Xasprintf ("%s=%s", attrname, attrval);
addnode (attrlist, node);
}
p = fileattr_modify (node->data, attrname, attrval, '=', ';');
if (p == NULL)
delnode (node);
else
{
free (node->data);
node->data = p;
}
attrs_modified = 1;
}
char *
fileattr_getall (const char *filename)
{
Node *node;
char *p;
if (attrlist == NULL)
fileattr_read ();
if (attrlist == NULL)
return NULL;
if (filename == NULL)
p = fileattr_default_attrs;
else
{
node = findnode (attrlist, filename);
if (node == NULL)
return NULL;
p = node->data;
}
return xstrdup (p);
}
void
fileattr_setall (const char *filename, const char *attrs)
{
Node *node;
if (filename == NULL)
{
if (fileattr_default_attrs != NULL)
free (fileattr_default_attrs);
fileattr_default_attrs = xstrdup (attrs);
attrs_modified = 1;
return;
}
if (attrlist == NULL)
fileattr_read ();
if (attrlist == NULL)
{
attrlist = getlist ();
}
node = findnode (attrlist, filename);
if (node == NULL)
{
if (attrs != NULL)
{
node = getnode ();
node->type = FILEATTR;
node->delproc = fileattr_delproc;
node->key = xstrdup (filename);
node->data = xstrdup (attrs);
addnode (attrlist, node);
}
}
else
{
if (attrs == NULL)
delnode (node);
else
{
free (node->data);
node->data = xstrdup (attrs);
}
}
attrs_modified = 1;
}
void
fileattr_newfile (const char *filename)
{
Node *node;
if (attrlist == NULL)
fileattr_read ();
if (fileattr_default_attrs == NULL)
return;
if (attrlist == NULL)
{
attrlist = getlist ();
}
node = getnode ();
node->type = FILEATTR;
node->delproc = fileattr_delproc;
node->key = xstrdup (filename);
node->data = xstrdup (fileattr_default_attrs);
addnode (attrlist, node);
attrs_modified = 1;
}
static int
writeattr_proc (Node *node, void *data)
{
FILE *fp = (FILE *)data;
fputs ("F", fp);
fputs (node->key, fp);
fputs ("\t", fp);
fputs (node->data, fp);
fputs ("\012", fp);
return 0;
}
static int
postwatch_proc (const char *repository, const char *filter, void *closure)
{
char *cmdline;
const char *srepos = Short_Repository (repository);
TRACE (TRACE_FUNCTION, "postwatch_proc (%s, %s)", repository, filter);
cmdline = format_cmdline (
#ifdef SUPPORT_OLD_INFO_FMT_STRINGS
false, srepos,
#endif
filter,
"c", "s", cvs_cmd_name,
#ifdef SERVER_SUPPORT
"R", "s", referrer ? referrer->original : "NONE",
#endif
"p", "s", srepos,
"r", "s", current_parsed_root->directory,
(char *) NULL);
if (!cmdline || !strlen (cmdline))
{
if (cmdline) free (cmdline);
error (0, 0, "postwatch proc resolved to the empty string!");
return 1;
}
run_setup (cmdline);
free (cmdline);
return abs (run_exec (RUN_TTY, RUN_TTY, RUN_TTY,
RUN_NORMAL | RUN_SIGIGNORE));
}
void
fileattr_write (void)
{
FILE *fp;
char *fname;
mode_t omask;
struct unrecog *p;
if (!attrs_modified)
return;
if (noexec)
return;
assert (fileattr_stored_repos != NULL);
fname = Xasprintf ("%s/%s", fileattr_stored_repos, CVSREP_FILEATTR);
if (list_isempty (attrlist)
&& fileattr_default_attrs == NULL
&& unrecog_head == NULL)
{
if (unlink_file (fname) < 0)
{
if (!existence_error (errno))
{
error (0, errno, "cannot remove %s", fname);
}
}
strcpy (fname, fileattr_stored_repos);
strcat (fname, "/");
strcat (fname, CVSREP);
if (CVS_RMDIR (fname) < 0)
{
if (errno != ENOTEMPTY
&& !existence_error (errno))
error (0, errno, "cannot remove %s", fname);
}
free (fname);
return;
}
omask = umask (cvsumask);
fp = CVS_FOPEN (fname, FOPEN_BINARY_WRITE);
if (fp == NULL)
{
if (existence_error (errno))
{
char *repname;
repname = Xasprintf ("%s/%s", fileattr_stored_repos, CVSREP);
if (CVS_MKDIR (repname, 0777) < 0 && errno != EEXIST)
{
error (0, errno, "cannot make directory %s", repname);
(void) umask (omask);
free (fname);
free (repname);
return;
}
free (repname);
fp = CVS_FOPEN (fname, FOPEN_BINARY_WRITE);
}
if (fp == NULL)
{
error (0, errno, "cannot write %s", fname);
(void) umask (omask);
free (fname);
return;
}
}
(void) umask (omask);
walklist (attrlist, writeattr_proc, fp);
if (fileattr_default_attrs != NULL)
{
fputs ("D\t", fp);
fputs (fileattr_default_attrs, fp);
fputs ("\012", fp);
}
for (p = unrecog_head; p != NULL; p = p->next)
{
fputs (p->line, fp);
fputs ("\012", fp);
}
if (fclose (fp) < 0)
error (0, errno, "cannot close %s", fname);
attrs_modified = 0;
free (fname);
Parse_Info (CVSROOTADM_POSTWATCH, fileattr_stored_repos, postwatch_proc,
PIOPT_ALL, NULL);
}
void
fileattr_free (void)
{
dellist (&attrlist);
if (fileattr_stored_repos != NULL)
free (fileattr_stored_repos);
fileattr_stored_repos = NULL;
if (fileattr_default_attrs != NULL)
free (fileattr_default_attrs);
fileattr_default_attrs = NULL;
while (unrecog_head)
{
struct unrecog *p = unrecog_head;
unrecog_head = p->next;
free (p->line);
free (p);
}
}