/* Implementation for file attribute munging features.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. */
#include "cvs.h"
#include "getline.h"
#include "fileattr.h"
static const char *stored_repos; /* Current directory */
static CXmlTree g_tree; /* Global xml context */
static CXmlNode *stored_root; /* Node tree of current directory */
static int modified; /* Set when tree changed */
static void fileattr_read();
static void fileattr_convert(CXmlNode *& root, const char *oldfile);
static void owner_convert(CXmlNode *& root, const char *oldfile);
static void perms_convert(CXmlNode *& root, const char *oldfile);
static void fileattr_convert_write(CXmlNode *& root, const char *xmlfile);
static void fileattr_convert_line(CXmlNode *node, char *line);
static char printf_buf[512];
/* Note that if noone calls fileattr_xxx, this is very cheap. No stat(),
no open(), no nothing. */
void fileattr_startdir (const char *repos)
{
assert (stored_repos == NULL || !fncmp(repos,stored_repos));
assert (stored_root == NULL);
stored_repos = xstrdup (repos);
modified = 0;
TRACE(3,"fileattr_startdir(%s)",PATCH_NULL(repos));
}
CXmlNode *_fileattr_find(CXmlNode *node, const char *exp, ...)
{
va_list va;
va_start(va, exp);
vsnprintf(printf_buf,sizeof(printf_buf),exp,va);
va_end(va);
return (XmlHandle_t)node->Lookup(printf_buf,false);
}
/* Search for a given node. A little like xpath but not nearly as complex */
XmlHandle_t fileattr_find(XmlHandle_t root, const char *exp, ...)
{
TRACE(3,"fileattr_find(%s)",exp);
if(!stored_root)
fileattr_read();
CXmlNode *node=(CXmlNode*)root;
if(!node) node = stored_root;
va_list va;
va_start(va, exp);
vsnprintf(printf_buf,sizeof(printf_buf),exp,va);
va_end(va);
return (XmlHandle_t)node->Lookup(printf_buf,false);
}
/* Search for a given node. A little like xpath but not nearly as complex */
/* This version creates any nodes that aren't in the path */
XmlHandle_t fileattr_create(XmlHandle_t root, const char *exp, ...)
{
TRACE(3,"fileattr_create(%s)",exp);
if(!stored_root)
fileattr_read();
CXmlNode *node=(CXmlNode*)root;
if(!node) node = stored_root;
va_list va;
va_start(va, exp);
vsnprintf(printf_buf,sizeof(printf_buf),exp,va);
va_end(va);
CXmlNode *ret = node->Lookup(printf_buf,false);
if(ret)
return (XmlHandle_t)ret;
modified = 1;
return (XmlHandle_t)node->Lookup(printf_buf,true);
}
/* Return the next node on this level with this name, for walking lists */
XmlHandle_t fileattr_next(XmlHandle_t root)
{
TRACE(3,"fileattr_next()");
if(!stored_root)
fileattr_read();
CXmlNode *node=(CXmlNode*)root;
if(!node) return NULL;
CXmlNode *next = node->Next();
if(!next) return NULL;
if(!strcmp(node->GetName(),next->GetName()))
return (XmlHandle_t)next;
return NULL;
}
/* Delete a value under the node. */
void fileattr_delete(XmlHandle_t root, const char *exp, ...)
{
TRACE(3,"fileattr_delete(%s)",exp);
if(!stored_root)
fileattr_read();
CXmlNode *node=(CXmlNode*)root;
if(!node) node = stored_root;
va_list va;
va_start(va, exp);
vsnprintf(printf_buf,sizeof(printf_buf),exp,va);
va_end(va);
CXmlNode *child = node->Lookup(printf_buf,false);
if(child)
{
node->Delete(child);
modified = 1;
}
}
/* Delete a value under the node. */
void fileattr_delete_child(XmlHandle_t parent, XmlHandle_t child)
{
TRACE(3,"fileattr_delete_child()");
if(!parent)
return;
if(child)
{
parent->Delete(child);
modified = 1;
}
}
/* Delete a value under the node at the next prune. */
void fileattr_batch_delete(XmlHandle_t root)
{
TRACE(3,"fileattr_batch_delete()");
if(!stored_root)
fileattr_read();
CXmlNode *node=(CXmlNode*)root;
if(!node) node = stored_root;
node->BatchDelete();
modified = 1;
}
/* Get a single value from a node. Pass null to get value of this node. */
const char *fileattr_getvalue(XmlHandle_t root, const char *name)
{
TRACE(3,"fileattr_getvalue(%s)",name);
if(!stored_root)
fileattr_read();
CXmlNode *node=(CXmlNode*)root;
if(!node) node = stored_root;
if(name) node = node->Lookup(name);
if(node) return node->GetValue();
return NULL;
}
/* Set a single value for a node. Pass null to set value of this node. */
void fileattr_setvalue(XmlHandle_t root, const char *name, const char *value)
{
TRACE(3,"fileattr_setvalue(%s,%s)",name,value?value:"");
if(!stored_root)
fileattr_read();
assert(!name || !strchr(name,'/'));
CXmlNode *node=(CXmlNode*)root;
CXmlNode *val;
if(!node) node = stored_root;
modified = 1;
val=node;
if(name) val = node->Lookup(name);
if(val) return val->SetValue(value);
else
{
if(name[0]=='@')
node->NewAttribute(name+1,value);
else
node->NewNode(name,value);
}
}
void fileattr_newfile (const char *filename)
{
TRACE(3,"fileattr_newfile(%s)",filename);
if(!stored_root)
fileattr_read();
CXmlNode *dir_default = stored_root->Lookup("directory/default");
if(dir_default)
{
CXmlNode *file = stored_root->NewNode("file",NULL);
file->NewAttribute("name",filename);
file->Paste(dir_default->Copy());
modified = 1;
}
}
void fileattr_write ()
{
char *fname;
FILE *fp;
TRACE(3,"fileattr_write()");
if(noexec)
return;
if(!modified || !stored_root)
return;
fname = (char*)xmalloc (strlen (stored_repos)
+ sizeof (CVSREP_FILEATTR) + 20);
sprintf(fname,"%s/%s",stored_repos,CVSADM);
if(!isdir(fname))
make_directory(fname);
sprintf(fname,"%s/%s",stored_repos,CVSREP_FILEATTR);
fp = CVS_FOPEN (fname, FOPEN_BINARY_WRITE);
if (fp == NULL)
{
error (0, errno, "cannot write %s", fn_root(fname));
xfree (fname);
return;
}
if(!stored_root->WriteXmlFile(fp))
error (0, errno, "cannot write %s", fn_root(fname));
xfree(fname);
fclose(fp);
modified = 0;
}
void fileattr_free ()
{
TRACE(3,"fileattr_free()");
xfree(stored_repos);
delete stored_root;
stored_root = NULL;
}
void fileattr_read()
{
_fileattr_read(stored_root, stored_repos);
if(!stored_root)
error(1,0,"Malformed fileattr.xml file in %s/CVS. Please fix or delete this file",fn_root(stored_repos));
}
void _fileattr_read(CXmlNode*& root, const char *repos)
{
char *fname,*ofname;
FILE *fp;
TRACE(3,"fileattr_read(%s)",repos);
if(root)
return; /* Boilerplate, should never happen */
fname = (char*)xmalloc (strlen (repos)
+ sizeof (CVSREP_FILEATTR) + 20);
sprintf(fname,"%s/%s",repos,CVSREP_FILEATTR);
if(!isfile(fname))
{
ofname = (char*)xmalloc (strlen (repos)
+ sizeof (CVSREP_OLDFILEATTR) + 20);
sprintf(ofname,"%s/%s",repos,CVSREP_OLDFILEATTR);
if(isfile(ofname))
{
fileattr_convert(root,ofname);
CVS_UNLINK(ofname);
}
sprintf(ofname,"%s/%s",repos,CVSREP_OLDOWNER);
if(isfile(ofname))
{
owner_convert(root,ofname);
CVS_UNLINK(ofname);
}
sprintf(ofname,"%s/%s",repos,CVSREP_OLDPERMS);
if(isfile(ofname))
{
perms_convert(root,ofname);
CVS_UNLINK(ofname);
}
if(root)
{
sprintf(ofname,"%s/%s",repos,CVSADM);
if(!isdir(ofname))
make_directory(ofname);
fileattr_convert_write(root,fname);
}
else
root = new CXmlNode(&g_tree,CXmlNode::XmlTypeNode,"fileattr",NULL);
xfree(ofname);
xfree(fname);
return;
}
fp = CVS_FOPEN (fname, FOPEN_BINARY_READ);
if (fp == NULL)
{
if (!existence_error (errno))
error (0, errno, "cannot read %s", fn_root(fname));
xfree (fname);
root = new CXmlNode(&g_tree,CXmlNode::XmlTypeNode,"fileattr",NULL);
return;
}
root = g_tree.ReadXmlFile(fp);
fclose(fp);
xfree(fname);
}
/* Read an old-style fileattr file and convert it to new-style */
void fileattr_convert(CXmlNode*& root, const char *oldfile)
{
FILE *fp;
char *line, *p;
size_t linesize;
CXmlNode *node;
TRACE(3,"fileattr_convert(%s)",oldfile);
fp = CVS_FOPEN (oldfile, FOPEN_BINARY_READ);
if (fp == NULL)
{
if (!existence_error (errno))
error (0, errno, "cannot read %s", fn_root(oldfile));
return;
}
if(!root)
root = new CXmlNode(&g_tree,CXmlNode::XmlTypeNode,"fileattr",NULL);
line = NULL;
while(getline(&line,&linesize,fp)>=0)
{
line[linesize-1]='\0';
if(!*line)
continue;
p=line+strlen(line)-1;
while(isspace(*p))
*(p--)='\0';
p = strchr (line, '\t');
if (p == NULL)
error (1, 0, "file attribute database corruption: tab missing in %s",fn_root(oldfile));
*(p++)='\0';
switch(line[0])
{
case 'D':
node = root->Lookup("directory/default",true);
fileattr_convert_line(node,p);
break;
case 'F':
node = root->NewNode("file",NULL);
node->NewAttribute("name",line+1);
fileattr_convert_line(node,p);
break;
default:
error(0,0,"Unrecognized fileattr type '%c'. Not converting.",line[0]);
break;
}
}
fclose(fp);
xfree(line);
}
void owner_convert(CXmlNode *& root, const char *oldfile)
{
FILE *fp;
char *line, *p;
size_t linesize;
TRACE(3,"owner_convert(%s)",oldfile);
fp = CVS_FOPEN (oldfile, FOPEN_BINARY_READ);
if (fp == NULL)
{
if (!existence_error (errno))
error (0, errno, "cannot read %s", fn_root(oldfile));
return;
}
if(!root)
root = new CXmlNode(&g_tree,CXmlNode::XmlTypeNode,"fileattr",NULL);
line = NULL;
if(getline(&line,&linesize,fp)>=0)
{
line[linesize-1]='\0';
if(*line)
{
p=line+strlen(line)-1;
while(isspace(*p))
*(p--)='\0';
CXmlNode *node = root->Lookup("directory/owner",true);
node->SetValue(line);
}
}
fclose(fp);
xfree(line);
}
void perms_convert(CXmlNode *& root, const char *oldfile)
{
FILE *fp;
char *line, *p;
size_t linesize;
TRACE(3,"perms_convert(%s)",oldfile);
fp = CVS_FOPEN (oldfile, FOPEN_BINARY_READ);
if (fp == NULL)
{
if (!existence_error (errno))
error (0, errno, "cannot read %s", fn_root(oldfile));
return;
}
if(!root)
root = new CXmlNode(&g_tree,CXmlNode::XmlTypeNode,"fileattr",NULL);
line = NULL;
while(getline(&line,&linesize,fp)>=0)
{
const char *user, *branch=NULL, *perms;
line[linesize-1]='\0';
if(!*line)
continue;
p=line+strlen(line)-1;
while(isspace(*p))
*(p--)='\0';
p = strchr(line,'{');
if(p)
{
*(p++)='\0';
branch = p;
p=strchr(p,'}');
if(!p)
{
error(0,0,"malformed ACL for user %s in directory",user);
continue;
}
*(p++)='\0';
}
else
p=line;
user = p;
p=strchr(p,':');
if(!p)
{
error(0,0,"malformed ACL for user %s in directory",user);
continue;
}
*(p++)='\0';
perms = p;
CXmlNode *node = root->Lookup("directory",true);
CXmlNode *acl;
acl = node->NewNode("acl",NULL);
if(strcmp(user,"default"))
acl->NewAttribute("user",user);
if(branch)
acl->NewAttribute("branch",branch);
if(strchr(perms,'n'))
{
CXmlNode *n;
n=acl->NewNode("all",NULL);
n->NewAttribute("deny","1");
}
else
{
if(strchr(perms,'r'))
acl->NewNode("read",NULL);
if(strchr(perms,'w'))
{
acl->NewNode("write",NULL);
acl->NewNode("tag",NULL);
}
if(strchr(perms,'c'))
acl->NewNode("create",NULL);
}
}
fclose(fp);
xfree(line);
}
void fileattr_convert_write(CXmlNode *& root, const char *xmlfile)
{
FILE *fp = fopen(xmlfile,FOPEN_BINARY_WRITE);
if (fp == NULL)
{
error (0, errno, "cannot write %s", fn_root(xmlfile));
return;
}
if(!root->WriteXmlFile(fp))
{
error (0, errno, "cannot write %s", fn_root(xmlfile));
return;
}
fclose(fp);
}
void fileattr_convert_line(CXmlNode *node, char *line)
{
char *l = line, *e, *p, *type,*name,*next, *att, *nextatt;
CXmlNode *subnode;
do
{
p = strchr(l,'=');
if(!p)
return;
*(p++)='\0';
e=strchr(p,';');
if(e)
*(e++)='\0';
if(!strcmp(l,"_watched"))
{
node->NewNode("watched",NULL);
}
else if(!strcmp(l,"_watchers"))
{
name=p;
do
{
type=strchr(name,'>');
if(!type)
break;
*(type++)='\0';
next=strchr(type,',');
if(next)
*(next++)='\0';
subnode=node->NewNode("watcher",NULL);
subnode->NewAttribute("name",name);
att=type;
do
{
nextatt=strchr(att,'+');
if(nextatt)
*(nextatt++)='\0';
subnode->NewNode(att,NULL);
att=nextatt;
} while(att);
name = next;
} while(name);
}
else if(!strcmp(l,"_editors"))
{
name=p;
do
{
type=strchr(name,'>');
if(!type)
break;
*(type++)='\0';
next=strchr(type,',');
if(next)
*(next++)='\0';
subnode=node->NewNode("editor",NULL);
subnode->NewAttribute("name",name);
att=type;
nextatt=strchr(att,'+');
if(nextatt)
*(nextatt++)='\0';
subnode->NewNode("time",att);
att=nextatt;
nextatt=strchr(att,'+');
if(nextatt)
*(nextatt++)='\0';
subnode->NewNode("hostname",att);
att=nextatt;
nextatt=strchr(att,'+');
if(nextatt)
*(nextatt++)='\0';
subnode->NewNode("pathname",att);
att=nextatt;
name = next;
} while(name);
}
else
{
error(0,0,"Unknown fileattr attribute '%s'. Not converting.",l);
}
l=e;
} while(l);
}
void fileattr_prune(XmlHandle_t node)
{
if(!node)
return;
((CXmlNode*)node)->Prune();
}
XmlHandle_t fileattr_copy(XmlHandle_t root)
{
if(!root)
return NULL;
return (XmlHandle_t)((CXmlNode*)root)->Copy();
}
void fileattr_paste(XmlHandle_t root, XmlHandle_t source)
{
if(!source)
return;
if(!stored_root)
fileattr_read();
CXmlNode *node=(CXmlNode*)root;
if(!node) node = stored_root;
node->Paste((CXmlNode*)source);
modified = 1;
}
void fileattr_free_subtree(XmlHandle_t *root)
{
if(!root || !*root)
return;
delete *(CXmlNode**)root;
*root=NULL;
}
void fileattr_modified()
{
modified = 1;
}
syntax highlighted by Code2HTML, v. 0.9.1