/*
* Copyright (c) 2004, Tony Hoyle
*
* You may distribute under the terms of the GNU General Public License as
* specified in the README file that comes with the CVS source distribution.
*
* Manage logical->physical directory mapping
*
*/
#include "cvs.h"
#include <getline.h>
static int modules2_loaded;
static modules2_struct *modules2_list;
static int modules2_count;
static int global_lookupid;
typedef struct
{
const char *from;
const char *to;
} rename_script_entry;
typedef struct
{
RCSNode *repository_rcsfile;
const char *repository_map_tag;
const char *repository_map_date;
char *repository_buffer;
List *directory_mappings;
rename_script_entry *rename_script;
int rename_script_count,rename_script_size;
char *virtual_repos;
char *real_repos;
const char *directory_version;
} directory_data;
static directory_data *directory_stack, *current_directory;
int directory_stack_size, directory_stack_count;
static int modules2_struct_sort(const void *_a, const void *_b)
{
modules2_struct *a = (modules2_struct*)_a;
modules2_struct *b = (modules2_struct*)_b;
return fncmp(a->name,b->name);
}
static int modules2_module_struct_sort(const void *_a, const void *_b)
{
modules2_module_struct *a = (modules2_module_struct*)_a;
modules2_module_struct *b = (modules2_module_struct*)_b;
return fncmp(a->directory[0]=='*'?&a->directory[1]:a->directory,b->directory[0]=='*'?&b->directory[1]:b->directory);
}
int free_modules2()
{
int m,n;
if(modules2_loaded)
{
for(m=0; m<modules2_count; m++)
{
for(n=0; n<modules2_list[m].module_count; n++)
{
xfree(modules2_list[m].module[n].directory);
xfree(modules2_list[m].module[n].translation);
xfree(modules2_list[m].module[n].regex);
}
xfree(modules2_list[m].name);
xfree(modules2_list[m].module);
}
xfree(modules2_list);
modules2_count=0;
modules2_loaded=0;
}
for(n=0; n<directory_stack_size; n++)
{
freercsnode(&directory_stack[n].repository_rcsfile);
xfree(directory_stack[n].repository_map_tag);
xfree(directory_stack[n].repository_map_date);
xfree(directory_stack[n].repository_buffer);
dellist(&directory_stack[n].directory_mappings);
xfree(directory_stack[n].virtual_repos);
xfree(directory_stack[n].real_repos);
xfree(directory_stack[n].directory_version);
for(m=0; m<directory_stack[n].rename_script_count; m++)
{
xfree(directory_stack[n].rename_script[m].from);
xfree(directory_stack[n].rename_script[m].to);
}
xfree(directory_stack[n].rename_script);
}
xfree(directory_stack);
directory_stack_size=0;
return 0;
}
static void load_modules2()
{
char *path = (char*)xmalloc(strlen(current_parsed_root->directory)+sizeof(CVSROOTADM)+sizeof(CVSROOTADM_MODULES2)+10);
FILE *f;
int n;
modules2_struct *inmodule;
modules2_module_struct *line;
int size = 0,module_size;
char buf[MAX_PATH*3];
if(!path)
error(1,0,"Out of memory");
sprintf(path,"%s/%s/%s",current_parsed_root->directory,CVSROOTADM,CVSROOTADM_MODULES2);
#ifdef _WIN32
assert(server_active || !current_parsed_root->isremote);
#endif
TRACE(3,"Loading modules2 from %s",PATCH_NULL(path));
if((f=fopen(path,"r"))==NULL)
{
if(!existence_error(errno))
error(1,errno,"Couldn't read modules2 file");
xfree(path);
modules2_loaded = 1;
modules2_list = NULL;
return;
}
xfree(path);
inmodule=NULL;
modules2_count = 0;
modules2_list = NULL;
while(fgets(buf,sizeof(buf), f))
{
char *p,*q,*dest;
int inquote;
p=buf+strlen(buf)-1;
while(p>=buf && isspace(*p))
--p;
*(p+1)='\0';
p=buf;
while(isspace(*p))
p++;
if(*p=='#' || !*p)
continue;
if(*p=='[') /* New module */
{
/* We don't allow [] [r [foo]bar] */
if(*(p+1) == ']' || strchr(p,']')!=p+strlen(p)-1)
{
error(0,0,"Malformed line '%s' in modules2 file",p);
continue;
}
p++;
*(p+strlen(p)-1)='\0';
if(modules2_count>=size)
{
if(!size) size=8;
else size*=2;
modules2_list = (modules2_struct *)xrealloc(modules2_list,sizeof(modules2_struct)*size);
if(!modules2_list)
error(1,0,"Out of memory");
}
inmodule=modules2_list+modules2_count;
modules2_count++;
inmodule->module=NULL;
inmodule->module_count=0;
inmodule->name=xstrdup(p);
module_size = 0;
}
else
{
/* No [...] seen yet, */
if(!inmodule)
{
error(0,0,"Malformed line '%s' in modules2 file",p);
continue;
}
if(inmodule->module_count>=module_size)
{
if(!module_size) module_size=8;
else module_size*=2;
inmodule->module = (modules2_module_struct *)xrealloc(inmodule->module,sizeof(modules2_module_struct)*module_size);
if(!inmodule->module)
error(1,0,"Out of memory");
}
line = inmodule->module + inmodule->module_count;
inmodule->module_count++;
q=p;
inquote=0;
line->directory=(char*)xmalloc(strlen(p)+1);
line->translation=(char*)xmalloc(strlen(p)+1);
line->regex=NULL;
dest=(char*)line->directory;
while(*q && (inquote || (!isspace(*q) && *q!='=')))
{
if(inquote=='\\')
{
*(dest++)=*q;
inquote=0;
}
else if(*q==inquote)
inquote=0;
else if(*q=='"' || *q=='\'')
inquote=*q;
else
*(dest++)=*q;
q++;
}
*dest='\0';
if(!strcmp(line->directory,"/"))
*(char*)line->directory='\0';
inquote=0;
while(*q && (isspace(*q) || *q=='='))
{
if(*q=='=') inquote='=';
q++;
}
line->local=line->no_recursion=line->lookupid=0;
if(!inquote)
strcpy((char*)line->translation,line->directory);
else
{
inquote=0;
dest=(char*)line->translation;
while(*q=='!' || *q=='+')
{
if(*q=='!')
line->local=1;
if(*q=='+')
line->no_recursion=1;
q++;
}
while(*q)
{
if(inquote=='\\')
{
*(dest++)=*q;
inquote=0;
}
else if(*q==inquote)
inquote=0;
else if(*q=='"' || *q=='\'')
inquote=*q;
else
*(dest++)=*q;
q++;
}
*dest++='\0';
}
if(strlen(line->translation)>1 && line->translation[(strlen(line->translation)-1)==')'])
{
int brackets = 1;
p=(char*)line->translation+strlen(line->translation)-2;
for(;p>line->translation && brackets;--p)
{
if(*p==')')
brackets++;
if(*p=='(')
brackets--;
}
if(p>line->translation)
{
q=p;
p++;
while(isspace(*q))
*(q--)='\0';
*p='\0';
p++;
if(strlen(p)>1)
{
*(p+strlen(p)-1)='\0';
line->regex=xstrdup(p);
}
}
}
if(strstr(line->translation,"..") || isabsolute(line->translation))
{
error(0,0,"Ignoring invalid module translation '%s'",line->translation);
inmodule->module_count--;
}
}
}
fclose(f);
qsort(modules2_list,modules2_count,sizeof(modules2_struct),modules2_struct_sort);
for(n=0; n<modules2_count; n++)
qsort(modules2_list[n].module,modules2_list[n].module_count,sizeof(modules2_module_struct),modules2_module_struct_sort);
modules2_loaded = 1;
}
static modules2_struct *lookup_repository_module(const char *module)
{
modules2_struct term = {0};
term.name=module;
if(!modules2_list)
return NULL;
return (modules2_struct *)bsearch(&term,modules2_list,modules2_count,sizeof(modules2_struct),modules2_struct_sort);
}
static modules2_module_struct *lookup_repository_directory(modules2_struct *module, const char *directory, int *partial_match, int *subdir_match)
{
modules2_module_struct *dir, *longest_match;
int l,n;
modules2_module_struct term = {0};
term.directory=directory;
dir = (modules2_module_struct *)bsearch(&term,module->module,module->module_count,sizeof(modules2_module_struct),modules2_module_struct_sort);
if(dir)
{
*partial_match=*subdir_match=0;
return dir; /* Perfect match */
}
if(!directory[0])
{
*partial_match = 1;
*subdir_match = 0;
return NULL;
}
dir = module->module;
l=strlen(directory);
longest_match = NULL;
*subdir_match=0;
for(n=0; n<module->module_count; n++, dir++)
{
if(!strncmp(directory,dir->directory,strlen(dir->directory)) && directory[strlen(dir->directory)]=='/')
{
if(!longest_match || strlen(longest_match->directory)>strlen(dir->directory))
longest_match = dir;
}
if(!strncmp(directory,dir->directory,strlen(directory)) && dir->directory[strlen(directory)]=='/')
*subdir_match = 1;
}
if(longest_match) { *partial_match=strlen(longest_match->directory); *subdir_match = 0; }
else *partial_match = 0;
return longest_match;
}
static int _lookup_module2(const char *file, char *left, char *right, int lookupid, modules2_struct **pmod, modules2_module_struct **pdir)
{
char tmp[MAX_PATH],*p;
modules2_struct *mod;
modules2_module_struct *dir;
int partial_match,subdir_match;
int l;
/* Search the modules2 file if required */
if(!modules2_loaded)
load_modules2();
if(pmod) *pmod=NULL;
if(pdir) *pdir=NULL;
TRACE(3,"lookup_module2(%s,%d)",PATCH_NULL(file),lookupid);
/* Special case.. ls does this */
if(!*file)
return 0;
p=strchr(file,'/');
if(!p)
mod = lookup_repository_module(file);
else
{
strncpy(tmp,file,p-file);
tmp[p-file]='\0';
mod = lookup_repository_module(tmp);
}
if(!mod)
{
if(left) left[0]='\0';
if(right) strcpy(right,file);
return 0;
}
if(pmod) *pmod=mod;
if(p)
strcpy(tmp,p+1);
else
tmp[0]='\0';
do
{
dir = lookup_repository_directory(mod, tmp, &partial_match, &subdir_match);
if(dir)
{
if(dir->lookupid == lookupid)
error(1,0,"Recursive repository definition for %s",fn_root(file));
dir->lookupid = lookupid;
}
if(!dir && partial_match)
{
if(left)
sprintf(left,"%s%s%s",mod->name,tmp[0]?"/":"",tmp);
if(right)
{
l=strlen(tmp);
strcpy(right,file+strlen(mod->name)+(l?l+1:0));
}
return 3;
}
if(subdir_match)
{
if(left)
sprintf(left,"%s%s%s",mod->name,tmp[0]?"/":"",tmp);
if(right)
{
l=strlen(tmp);
strcpy(right,file+strlen(mod->name)+(l?l+1:0));
}
return 4;
}
if(dir)
break;
l=strlen(tmp);
p=strrchr(tmp,'/');
if(!p) tmp[0]='\0';
else *p='\0';
} while(l);
if(!dir)
return 0;
if(pdir)
*pdir = dir;
if(partial_match)
l=partial_match;
else
l=strlen(tmp);
if(left)
strcpy(left,dir->translation);
if(right)
strcpy(right,file+strlen(mod->name)+(l?l+1:0));
return dir->translation[0]?1:2;
}
static int lookup_module2(const char *file, char *left, char *right, modules2_struct **pmod, modules2_module_struct **pdir)
{
int ret,found=0,renamed=0;
char tmp[MAX_PATH],saved_left[MAX_PATH],saved_right[MAX_PATH];
modules2_struct *saved_mod;
modules2_module_struct *saved_dir, *mydir;
int lookupid = ++global_lookupid;
if(current_directory && current_directory->virtual_repos)
{
if(!fncmp(file,current_directory->virtual_repos))
{
file = current_directory->real_repos;
renamed = 1;
}
else if(!fnncmp(file,current_directory->virtual_repos,strlen(current_directory->virtual_repos) && file[strlen(current_directory->virtual_repos)]=='/'))
{
sprintf(tmp,"%s%s",current_directory->real_repos,file+strlen(current_directory->virtual_repos));
file = tmp;
renamed = 1;
}
}
/* Replay the rename script backwards - rename scripts hold logical filenames in the users' sandbox */
if(current_directory && current_directory->rename_script)
{
int n;
for(n=current_directory->rename_script_count-1; n>=0 && file; --n)
{
if(current_directory->rename_script[n].to && !fncmp(current_directory->rename_script[n].to,file))
{
file = current_directory->rename_script[n].from;
renamed = 1;
}
else if(!fncmp(current_directory->rename_script[n].from,file))
{
file = NULL;
renamed = 1;
}
}
}
if(!file)
return 2; /* Deleted */
strcpy(tmp,file);
do
{
ret = _lookup_module2(tmp,left,right,lookupid,pmod,&mydir);
if(pdir) *pdir=mydir;
if(ret!=1 || (mydir && mydir->no_recursion)) break;
if(left) strcpy(saved_left,left);
if(right) strcpy(saved_right,right);
if(pmod) saved_mod=*pmod;
if(pdir) saved_dir=*pdir;
sprintf(tmp,"%s%s",PATCH_NULL(left),PATCH_NULL(right));
found=1;
} while(ret==1);
if(found && !ret)
{
if(left) strcpy(left,saved_left);
if(right) strcpy(right,saved_right);
if(pmod) *pmod=saved_mod;
if(pdir) *pdir=saved_dir;
ret=1;
}
if(ret!=2 && current_directory && current_directory->directory_mappings)
{
Node *head,*p;
int may_be_deleted = 0;
sprintf(tmp,"%s%s",PATCH_NULL(left),PATCH_NULL(right));
head = current_directory->directory_mappings->list;
if(head)
{
for (p = head->next; p != head; p = p->next)
{
if(p->data && !fncmp(p->data,tmp))
{
left[0]='\0';
strcpy(right,p->key);
renamed = 1;
ret = 1;
break;
}
if(!fncmp(p->key,tmp))
may_be_deleted = 1;
}
if(!renamed && may_be_deleted)
return 2;
}
}
return ret?ret:renamed;
}
char *map_repository(const char *repository)
{
char left[MAX_PATH],right[MAX_PATH];
int res;
char *r = NULL;
if(strlen(repository)>1)
{
const char *p=repository+strlen(repository)-2;
if(!strcmp(p,"/."))
{
r=xstrdup(repository);
r[p-repository]='\0';
repository=r;
}
}
else if(!*repository || strcmp(repository,"."))
repository = "/";
TRACE(3,"map_repository(%s)",repository);
if(!strcmp(repository,current_parsed_root->directory))
{
if(r) return r;
else xstrdup(repository);
}
res = lookup_module2(relative_repos(repository),left,right, NULL, NULL);
if(res==1)
{
char *ret = (char*)xmalloc(strlen(current_parsed_root->directory)+strlen(left)+strlen(right)+10);
sprintf(ret,"%s/%s%s",current_parsed_root->directory,PATCH_NULL(left),PATCH_NULL(right));
xfree(r);
return ret;
}
if(res==2) /* Deleted */
{
xfree(r);
return NULL;
}
if((res==3 || res==4) && !*right)
{
/* Partial translation - use emptydir, or if the file/directory exists, use that */
char *ret = (char*)xmalloc(strlen(current_parsed_root->directory)+sizeof(CVSROOTADM)+sizeof(CVSNULLREPOS)+strlen(left)+strlen(right)+10);
sprintf(ret,"%s/%s%s",current_parsed_root->directory,PATCH_NULL(left),PATCH_NULL(right));
if(!isfile(ret))
sprintf(ret,"%s/%s/%s",current_parsed_root->directory,CVSROOTADM,CVSNULLREPOS);
xfree(r);
return ret;
}
if(r) return r;
else return xstrdup(repository);
}
char *map_filename(const char *repository, const char *name, const char **repository_out)
{
char tmp[MAX_PATH];
char *p;
TRACE(3,"map_filename(%s,%s)",PATCH_NULL(repository),PATCH_NULL(name));
/* Empty filename, assumed deleted */
if(!*name)
{
if(repository_out) *repository_out=NULL;
return NULL;
}
snprintf(tmp,sizeof(tmp),"%s/%s",PATCH_NULL(repository),PATCH_NULL(name));
*repository_out = map_repository(tmp);
if(!*repository_out)
return NULL;
p=strrchr(*repository_out,'/'); /* This cannot fail... */
*p='\0';
return xstrdup(p+1);
}
int nonrecursive_module(const char *repository)
{
modules2_module_struct *dir;
lookup_module2(relative_repos(repository),NULL,NULL,NULL,&dir);
TRACE(3,"nonrecursive_module(%s) = %d",PATCH_NULL(repository),dir?dir->local:0);
return dir?dir->local:0;
}
const char *lookup_regex(const char *repository)
{
modules2_module_struct *dir;
lookup_module2(relative_repos(repository),NULL,NULL,NULL,&dir);
return dir&&dir->regex?xstrdup(dir->regex):NULL;
}
int regex_filename_match(const char *regex, const char *filename)
{
cvs::wildcard_filename fn(filename);
bool regex_match = fn.matches_regexp(regex)?1:0;
TRACE(3,"Match %s to %s = %d",PATCH_NULL(regex),PATCH_NULL(filename),regex_match?1:0);
return regex_match?1:0;
}
int find_virtual_dirs (const char *repository, List *list)
{
char tmp[MAX_PATH], *q, *fn;
static char left[MAX_PATH],right[MAX_PATH];
modules2_struct *mod;
modules2_module_struct *dir;
int n,l;
TRACE(3,"find_virtual_dirs(%s)",PATCH_NULL(repository));
if(!strcmp(repository,current_parsed_root->directory) || !strcmp(repository+strlen(current_parsed_root->directory),"/"))
{
/* Special case... basically just list the modules */
mod=modules2_list;
for(n=0; n<modules2_count; n++,mod++)
{
Node *node;
strcpy(tmp,mod->name);
q=strchr(tmp,'/');
if(q) *q='\0';
/* put it in the list */
node = getnode ();
node->type = DIRS;
node->key = xstrdup (tmp);
if (addnode (list, node) != 0)
freenode (node);
}
return 0;
}
if(!lookup_module2(relative_repos(repository),left,right,&mod,NULL))
return 0;
/* Special case - virtually renamed physical directory */
if(!mod)
return 0;
/* At this point (I think) everything is normalised so don't have to take into account case, etc. */
strcat(left,right);
dir = mod->module;
fn=left+strlen(mod->name);
if(*fn) fn++;
l=strlen(fn);
for(n=0; n<mod->module_count; n++, dir++)
{
if(((!l && *dir->directory) || (l && !strncmp(fn,dir->directory,l) && dir->directory[l]=='/')))
{
Node *node;
int isfile = dir->directory[0]=='*'?1:0;
strcpy(tmp,dir->directory+isfile+(l?l+1:0));
q=strchr(tmp,'/');
if(q) *q='\0';
else if(isfile) continue; /* Don't store files in dir list */
if(dir->translation[0])
{
/* put it in the list */
node = getnode ();
node->type = DIRS;
node->key = xstrdup (tmp);
if (addnode (list, node) != 0)
freenode (node);
}
else if(!q)
{
node = findnode_fn(list, tmp);
if(node)
delnode(node);
}
}
}
return 0;
}
char *map_fixed_rename(const char *repos, char *name)
{
char tmp[MAX_PATH];
if(current_directory && current_directory->directory_mappings)
{
Node *n;
sprintf(tmp,"%s/%s",relative_repos(repos),PATCH_NULL(name));
n = findnode_fn(current_directory->directory_mappings, tmp);
if(n)
{
char *q=strrchr(n->data,'/');
if(!q) q=n->data;
else q++;
return q;
}
}
return name;
}
int find_virtual_rcs (const char *repository, List *list)
{
char tmp[MAX_PATH], *q;
static char left[MAX_PATH],right[MAX_PATH];
modules2_struct *mod;
modules2_module_struct *dir;
int n,l;
const char *fn;
TRACE(3,"find_virtual_rcs(%s)",PATCH_NULL(repository));
if(!lookup_module2(relative_repos(repository),left,right,&mod,NULL))
return 0;
/* Special case - a virtually renamed directory won't have any module entry */
if(!mod)
return 0;
/* At this point (I think) everything is normalised so don't have to take into account case, etc. */
strcat(left,right);
dir = mod->module;
fn=left+strlen(mod->name);
if(*fn) fn++;
l=strlen(fn);
for(n=0; n<mod->module_count; n++, dir++)
{
if(((!l && *dir->directory) || (l && !strncmp(fn,&dir->directory[1],l) && dir->directory[l]=='/')) && dir->directory[0]=='*')
{
Node *node;
strcpy(tmp,dir->directory+1+(l?l+1:0));
q=strchr(tmp,'/');
if(q) continue; /* Not at this level */
if(dir->translation[0])
{
/* put it in the list */
q=map_fixed_rename(repository,tmp);
if(q)
{
node = getnode ();
node->type = FILES;
node->key = xstrdup (q);
if (addnode (list, node) != 0)
freenode (node);
}
}
else
{
node=findnode_fn(list, tmp);
if(node)
delnode(node);
}
}
}
return 0;
}
int find_rename_rcs (const char *repository, List *list)
{
const char *file,*repos,*p;
int n;
Node *node;
TRACE(3,"find_rename_rcs(%s)",PATCH_NULL(repository));
if(!current_directory || (!current_directory->rename_script && !current_directory->directory_mappings))
return 0;
repos=relative_repos(repository);
if(current_directory->directory_mappings)
{
Node *n;
n=current_directory->directory_mappings->list->next;
while(n!=current_directory->directory_mappings->list)
{
file=(char*)n->data;
if(file && *file)
{
p=strrchr(file,'/');
if(!p) p=file;
else p++;
node = findnode_fn(list,p);
if(!node)
{
node=getnode();
node->type = FILES;
node->key = xstrdup (p);
if (addnode (list, node) != 0)
freenode (node);
}
}
n=n->next;
}
}
if(current_directory->rename_script)
{
for(n=0; n<current_directory->rename_script_count; n++)
{
file=current_directory->rename_script[n].from;
p=strrchr(file,'/');
if(!p) p=file;
else p++;
node = findnode_fn(list,p);
if(node)
delnode(node);
if(current_directory->rename_script[n].to)
{
file=current_directory->rename_script[n].to;
p=strrchr(file,'/');
if(!p) p=file;
else p++;
node=getnode();
node->type = FILES;
node->key = xstrdup (p);
if (addnode (list, node) != 0)
freenode (node);
}
}
}
return 0;
}
int find_rename_dirs(const char *repository, List *list)
{
const char *file,*repos,*p;
int n;
Node *node;
TRACE(3,"find_rename_dirs(%s)",PATCH_NULL(repository));
if(!current_directory || !current_directory->rename_script)
return 0;
repos=relative_repos(repository);
for(n=0; n<current_directory->rename_script_count; n++)
{
file=current_directory->rename_script[n].from;
p=strrchr(file,'/');
if(!p) p=file;
else p++;
node = findnode_fn(list,p);
if(node)
{
delnode(node);
if(current_directory->rename_script[n].to)
{
file=current_directory->rename_script[n].to;
p=strrchr(file,'/');
if(!p) p=file;
else p++;
node=getnode();
node->type = DIRS;
node->key = xstrdup (p);
if (addnode (list, node) != 0)
freenode (node);
}
}
}
return 0;
}
static void repository_checkoutproc (void *callerdat, const char *buffer, size_t len)
{
const char *p,*from,*to;
Node *node;
if(current_directory->directory_mappings)
dellist(¤t_directory->directory_mappings);
current_directory->directory_mappings = getlist();
p = buffer;
while(p-buffer<len && *p)
{
from=p;
to=p=strchr(p,'\n')+1;
if(!p)
break;
p=strchr(p,'\n')+1;
if(!p)
break;
node = getnode();
node->type=FILES;
node->key=(char*)xmalloc(to-from);
strncpy(node->key,from,(to-from)-1);
node->key[(to-from)-1]='\0';
node->data=(char*)xmalloc(p-to);
strncpy(node->data,to,(p-to)-1);
node->data[(p-to)-1]='\0';
if (addnode (current_directory->directory_mappings, node) != 0)
freenode (node);
}
}
/* Repository versioning support.
As each cvs enters each directory, it calls this which determines which
directory revision is used */
int open_directory(const char *repository, const char *dir, const char *tag, const char *date, int nonbranch, const char *version, int remote)
{
char *tmp,*pt;
TRACE(3,"open_directory(%s,%s,%s)",PATCH_NULL(repository),PATCH_NULL(tag),PATCH_NULL(date));
if(directory_stack_size == directory_stack_count)
{
directory_stack_count *= 2;
if(directory_stack_count < 50)
directory_stack_count = 50;
directory_stack = (directory_data *)xrealloc(directory_stack,sizeof(directory_data)*directory_stack_count);
if(!directory_stack)
error(1,errno,"Out of memory");
if(current_directory)
current_directory = directory_stack + directory_stack_size;
}
if(!current_directory)
current_directory = directory_stack;
else
current_directory++;
directory_stack_size++;
memset(current_directory,0,sizeof(directory_data));
if(!remote)
{
current_directory->repository_rcsfile = RCS_parse(RCSREPOVERSION,repository);
if(!current_directory->repository_rcsfile)
TRACE(3,"No mapping file in this directory.");
else
TRACE(3,"Opened mapping file %s",PATCH_NULL(current_directory->repository_rcsfile->path));
if(current_directory->repository_rcsfile)
{
int retcode;
const char *rev = NULL;;
if(version && numdots(version))
rev = xstrdup(version);
else if(date || nonbranch || (version && !strcmp(version,"_H_")))
rev = RCS_getversion(current_directory->repository_rcsfile,(char*)tag,(char*)date,1,NULL);
if(!rev)
rev = RCS_head(current_directory->repository_rcsfile);
retcode = RCS_checkout(current_directory->repository_rcsfile, NULL, (char*)rev, (char*)tag, NULL, NULL, repository_checkoutproc, NULL, NULL);
if (retcode != 0)
error (1, 0,
"failed to check out %s file", RCSREPOVERSION);
current_directory->directory_version = rev;
}
current_directory->repository_map_tag = xstrdup(tag);
current_directory->repository_map_date = xstrdup(date);
}
tmp = (char*)xmalloc(strlen(dir)+strlen(CVSADM_RENAME)+256);
if(dir && dir[0])
{
sprintf(tmp,"%s/",dir);
pt=tmp+strlen(tmp);
}
else
pt=tmp;
strcpy(pt,CVSADM_RENAME);
if(isfile(tmp))
{
FILE *fp;
size_t n;
char *from, *to;
fp = CVS_FOPEN(tmp, "r");
if(!fp)
error(1,errno,"Couldn't open %s",CVSADM_RENAME);
current_directory->rename_script_size = 50;
current_directory->rename_script = (rename_script_entry *)xmalloc(sizeof(rename_script_entry)*current_directory->rename_script_size);
from=to=NULL;
while(getline(&from, &n, fp)>0 && getline(&to, &n, fp)>0)
{
from[strlen(from)-1]='\0';
to[strlen(to)-1]='\0';
if(!to[0])
xfree(to);
if(current_directory->rename_script_count==current_directory->rename_script_size)
{
current_directory->rename_script_size*=2;
current_directory->rename_script = (rename_script_entry *)xrealloc(current_directory->rename_script,sizeof(rename_script_entry)*current_directory->rename_script_size);
}
current_directory->rename_script[current_directory->rename_script_count].from=from;
current_directory->rename_script[current_directory->rename_script_count].to=to;
current_directory->rename_script_count++;
from=to=NULL;
}
xfree(from);
xfree(to);
fclose(fp);
}
strcpy(pt,CVSADM_VIRTREPOS);
if(isfile(tmp))
{
FILE *fp = fopen(tmp,"r");
size_t len;
if(!fp)
error(1,errno,"Couldn't open %s",CVSADM_VIRTREPOS);
if(getline(¤t_directory->virtual_repos,&len,fp)<1)
error(1,errno,"Couldn't read %s",CVSADM_VIRTREPOS);
current_directory->virtual_repos[strlen(current_directory->virtual_repos)-1]='\0';
fclose(fp);
strcpy(pt,CVSADM_REP);
fp = fopen(tmp,"r");
if(!fp)
error(1,errno,"Couldn't open %s",CVSADM_REP);
if(getline(¤t_directory->real_repos,&len,fp)<1)
error(1,errno,"Couldn't read %s",CVSADM_REP);
current_directory->real_repos[strlen(current_directory->real_repos)-1]='\0';
if(isabsolute(current_directory->real_repos))
memmove(current_directory->real_repos,relative_repos(current_directory->real_repos),strlen(relative_repos(current_directory->real_repos))+1);
fclose(fp);
}
xfree(tmp);
return 0;
}
static int directory_commitproc(const char *filename, char **buffer, size_t *buflen, char **displayname)
{
Node *node;
Node *p, *head;
size_t len,count;
char *buf,*q;
if(!current_directory->directory_mappings)
current_directory->directory_mappings = getlist();
TRACE(3,"directory_commitproc(%s)",PATCH_NULL(filename));
/* Convert the final rename script into a direct file mapping */
if(current_directory->rename_script)
{
int n;
for(n=0; n<current_directory->rename_script_count; n++)
{
const char *from = current_directory->rename_script[n].from;
const char *to = current_directory->rename_script[n].to;
head = current_directory->directory_mappings->list;
if(head)
{
for (p = head->next; p != head; p = p->next)
{
if(p->data && !fncmp(p->data,from))
{
from = p->key;
break;
}
}
}
if((node=findnode_fn(current_directory->directory_mappings,from))!=NULL)
{
if(to && !fncmp(to,from))
delnode(node);
else
node->data = (char*)to;
}
else if(!to || fncmp(to,from))
{
node=getnode();
node->key = (char*)from;
node->data = (char*)to;
node->type = FILES;
if (addnode (current_directory->directory_mappings, node) != 0)
freenode (node);
}
}
sortlist (current_directory->directory_mappings, fsortcmp);
}
head = current_directory->directory_mappings->list;
len = 0;
count = 0;
buf = NULL;
if(head)
{
for (p = head->next; p != head; p = p->next)
{
count++;
len+=strlen(p->key)+1;
if(p->data)
len+=strlen(p->data);
len++;
}
buf = (char*)xmalloc(len+20);
q=buf;
for (p = head->next; p != head; p = p->next)
{
sprintf(q,"%s\n%s\n",p->key,p->data?p->data:"");
q+=strlen(q);
}
assert(q-buf==len);
*(q++)='\0';
}
/* Reset the rename script as it's no longer needed */
xfree(current_directory->rename_script);
current_directory->rename_script_count = current_directory->rename_script_size = 0;
*buffer = buf;
*buflen = len;
*displayname=xstrdup("directory update");
return 0;
}
static int create_mapping_file(const char *repository, const char *message)
{
if(!current_directory->repository_rcsfile)
{
char *fn = (char*)xmalloc(strlen(repository)+strlen(RCSREPOVERSION)+10);
TRACE(3,"Creating new directory mapping file");
sprintf(fn,"%s/%s%s",repository,RCSREPOVERSION,RCSEXT);
add_rcs_file(message?message:"created",fn,NULL,"1.1",NULL,NULL,NULL,0,NULL,"mapping file",12,NULL,NULL);
xfree(fn);
current_directory->repository_rcsfile = RCS_parse(RCSREPOVERSION,repository);
if(!current_directory->repository_rcsfile)
error(1,errno,"Couldn't create %s",RCSREPOVERSION);
}
return 0;
}
int commit_directory(const char *update_dir, const char *repository, const char *message)
{
char *newver = NULL;
const char *rev;
if(!current_directory->rename_script)
return 0;
TRACE(3,"commit_directory(%s,%s,%s)",PATCH_NULL(update_dir),PATCH_NULL(repository),PATCH_NULL(message));
if(!current_directory->repository_rcsfile)
create_mapping_file(repository,message);
if(current_directory->repository_map_tag && strcmp(current_directory->repository_map_tag,"HEAD"))
{
rev = RCS_whatbranch(current_directory->repository_rcsfile, current_directory->repository_map_tag);
if(current_directory->repository_map_date || (rev && !RCS_isbranch(current_directory->repository_rcsfile, current_directory->repository_map_tag)))
{
if(current_directory->repository_map_date)
error (0, 0, "cannot commit directory '%s' which has a sticky date of '%s'", "foo", current_directory->repository_map_date);
else
error (0, 0, "cannot commit directory '%s' which has a sticky tag of '%s'", "foo", current_directory->repository_map_tag);
xfree(rev);
return 1;
}
if(!rev)
{
char *cp,*ver;
ver = RCS_getversion(current_directory->repository_rcsfile, current_directory->repository_map_tag, current_directory->repository_map_date, 0, (int *) NULL);
rev = RCS_magicrev(current_directory->repository_rcsfile,ver);
RCS_settag(current_directory->repository_rcsfile,current_directory->repository_map_tag,rev,NULL);
cp = strrchr(rev,'.');
for(--cp;*cp!='.';--cp)
;
strcpy(cp,cp+2);
xfree(ver);
}
}
else
{
if(current_directory->repository_map_date)
{
error (0, 0, "cannot commit directory '%s' which has a sticky date of '%s'", "foo", current_directory->repository_map_date);
return 1;
}
rev = RCS_head(current_directory->repository_rcsfile);
*strchr(rev,'.')='\0';
}
if(RCS_checkin(current_directory->repository_rcsfile,NULL,message?(char*)message:"no message",rev,NULL,0,NULL,NULL,directory_commitproc, &newver, NULL, NULL))
error(1,errno,"RCS checkin to mapping file failed");
xfree(current_directory->directory_version);
current_directory->directory_version = newver;
CVS_UNLINK(CVSADM_RENAME);
#ifdef SERVER_SUPPORT
if(server_active)
reset_client_mapping(update_dir, repository);
#endif
return 0;
}
int close_directory()
{
TRACE(3,"close_directory()");
if(!directory_stack_size)
error(1,0,"Directory stack overrun");
if(current_directory->repository_rcsfile)
freercsnode(¤t_directory->repository_rcsfile);
dellist(¤t_directory->directory_mappings);
xfree(current_directory->rename_script);
current_directory->rename_script_count = current_directory->rename_script_size = 0;
xfree(current_directory->directory_version);
directory_stack_size--;
if(!directory_stack_size)
current_directory = NULL;
else
current_directory--;
return 0;
}
int free_directory()
{
while(directory_stack_size)
close_directory();
xfree(directory_stack);
return 0;
}
int set_mapping(const char *directory, const char *oldfile, const char *newfile)
{
char *ren = (char*)xmalloc(strlen(directory)+sizeof(CVSADM_RENAME)+20);
char *rennew = (char*)xmalloc(strlen(directory)+sizeof(CVSADM_RENAME)+20);
char *of = (char*)xmalloc(MAX_PATH+strlen(oldfile));
FILE *fpin,*fpout;
char from[MAX_PATH],to[MAX_PATH];
strcpy(of,oldfile);
TRACE(3,"set_mapping(%s,%s,%s)",PATCH_NULL(directory), PATCH_NULL(oldfile), PATCH_NULL(newfile));
sprintf(ren,"%s%s",directory,CVSADM_RENAME);
fpin = CVS_FOPEN(ren,"r");
if(!fpin)
{
if(!existence_error(errno))
error(1,errno,"Couldn't open %s",ren);
}
strcpy(rennew,ren);
strcat(rennew,".New");
fpout = CVS_FOPEN(rennew,"w");
if(!fpout)
error(1,errno,"Couldn't create %s",rennew);
if(fpin)
{
while(fgets(from,sizeof(from),fpin) && fgets(to,sizeof(to),fpin))
{
from[strlen(from)-1]='\0';
to[strlen(to)-1]='\0';
fprintf(fpout,"%s\n%s\n",from,to);
}
fclose(fpin);
}
fprintf(fpout,"%s\n%s\n",oldfile,newfile);
fclose(fpout);
if(CVS_RENAME(rennew,ren))
error(1,errno,"Error renaming %s to %s",rennew,ren);
xfree(ren);
xfree(rennew);
xfree(of);
return 0;
}
int add_mapping(const char *directory, const char *oldfile, const char *newfile)
{
TRACE(3,"add_mapping(%s,%s,%s)",PATCH_NULL(directory), PATCH_NULL(oldfile), PATCH_NULL(newfile));
if(current_directory->rename_script_count==current_directory->rename_script_size)
{
current_directory->rename_script_size*=2;
if(current_directory->rename_script_size<50) current_directory->rename_script_size=50;
current_directory->rename_script = (rename_script_entry *)xrealloc(current_directory->rename_script,sizeof(rename_script_entry)*current_directory->rename_script_size);
if(!current_directory->rename_script)
error(1,errno,"Out of memory");
}
current_directory->rename_script[current_directory->rename_script_count].from=xstrdup(oldfile);
current_directory->rename_script[current_directory->rename_script_count].to=xstrdup(newfile);
current_directory->rename_script_count++;
#ifdef SERVER_SUPPORT
if(server_active)
send_rename_to_client(oldfile,newfile);
#endif
return 0;
}
const char *get_directory_version()
{
return (current_directory && current_directory->directory_version)?current_directory->directory_version:NULL;
}
int get_directory_finfo(const char *repository, const char *dir, const char *update_dir, struct file_info *finfo)
{
assert(!current_parsed_root->isremote);
assert(current_directory);
if(!current_directory->repository_rcsfile)
return -1;
memset(finfo,0,sizeof(struct file_info));
finfo->mapped_file=finfo->file=finfo->fullname=RCSREPOVERSION;
finfo->rcs=current_directory->repository_rcsfile;
finfo->update_dir=(char*)update_dir;
finfo->repository=(char*)repository;
finfo->entries=getlist();
Fast_Register(finfo->entries,RCSREPOVERSION,current_directory->directory_version,"","",current_directory->repository_map_tag,current_directory->repository_map_date,"","","",0,"","","");
return 0;
}
int upgrade_entries(const char *repository, const char *dir, List **entries, List **renamed_files)
{
/* This is called with two open directories on the stack (old/new). We scan
through the entries files looking for translations in (old) and making them
equal to (new), provided they're in the same directory */
directory_data *new_directory = current_directory;
directory_data *old_directory = current_directory-1;
Node *head, *p, *q;
List *newlist;
int modified = 0;
int client_supports_rename = 1;
#ifdef SERVER_SUPPORT
if(server_active)
client_supports_rename = client_can_rename();
#endif
if((!old_directory->directory_version && !new_directory->directory_version) || !strcmp(old_directory->directory_version,new_directory->directory_version))
return 0;
TRACE(3,"Old directory version = %s\n",PATCH_NULL(old_directory->directory_version));
TRACE(3,"New directory version = %s\n",PATCH_NULL(new_directory->directory_version));
head = (*entries)->list;
*renamed_files = getlist();
if(client_supports_rename)
newlist = getlist();
for (p = head->next; p != head; p = p->next)
{
const char *oldfile;
char *oldrepos;
Node *dirhead=new_directory->directory_mappings->list;
Entnode *ent = (Entnode*)p->data;
char *name = ent->user;
Node *newnode;
TRACE(3,"Remapping file %s\n", PATCH_NULL(name));
current_directory = old_directory;
oldfile=map_filename(repository,name,(const char**)&oldrepos);
current_directory = new_directory;
if(oldfile)
{
oldrepos=(char*)xrealloc(oldrepos,strlen(oldfile)+strlen(oldrepos)+4);
strcat(oldrepos,"/");
strcat(oldrepos,oldfile);
memmove(oldrepos,oldrepos+strlen(current_parsed_root->directory)+1,strlen(oldrepos+strlen(current_parsed_root->directory)+1)+1);
if(client_supports_rename)
{
newnode=getnode();
newnode->key = xstrdup(p->key);
newnode->data = (char*)ent;
newnode->delproc = p->delproc;
p->data = NULL;
p->delproc = NULL;
}
for(q = dirhead->next; q!=dirhead; q = q->next)
{
const char *from = (const char *)q->key;
const char *to = (const char*)q->data;
if(!strcmp(from,oldrepos))
{
Node *nod = getnode();
if(client_supports_rename)
{
if(to && *to)
{
char *xfrom, *xto;
xfree(newnode->key);
xfree(ent->user);
newnode->key=xstrdup(last_component(to));
ent->user=xstrdup(last_component(to));
xfrom = (char*)xmalloc(strlen(dir)+strlen(from)+10);
sprintf(xfrom,"%s/%s",dir,last_component(from));
xto = (char*)xmalloc(strlen(dir)+strlen(to)+10);
sprintf(xto,"%s/%s",dir,last_component(to));
if(isfile(xfrom))
CVS_RENAME(xfrom,xto);
xfree(xfrom);
xfree(xto);
}
else
{
freenode(newnode);
newnode=NULL;
}
modified = 1;
}
nod->key=xstrdup(last_component(from));
nod->data=xstrdup(last_component(to));
addnode(*renamed_files,nod);
break;
}
}
xfree(oldfile);
xfree(oldrepos);
if(client_supports_rename && newnode)
addnode(newlist,newnode);
}
}
/* If modifed, create an entries.log */
/* This isn't really the best way to do it, but it's the simplest at the moment */
if(client_supports_rename && modified)
{
char *log = (char*)xmalloc(strlen(dir)+sizeof(CVSADM_ENTLOG)+10);
FILE *f;
sprintf(log,"%s/%s",dir,CVSADM_ENTLOG);
f=fopen(log,"a");
fclose(f);
}
if(client_supports_rename)
{
dellist(entries);
*entries = newlist;
}
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1