/* cfengine for GNU
 
        Copyright (C) 1995
        Free Software Foundation, Inc.
 
   This file is part of GNU cfengine - written and maintained 
   by Mark Burgess, Dept of Computing and Engineering, Oslo College,
   Dept. of Theoretical physics, University of Oslo
 
   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.
 
  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA

*/


/*******************************************************************/
/*                                                                 */
/* Routines which install actions parsed by the parser             */
/*                                                                 */
/* Derived from parse.c                                            */
/*                                                                 */
/*******************************************************************/

#define INET

#include "cf.defs.h"
#include "cf.extern.h"

/*******************************************************************/

InstallLocalInfo (class,value)

enum classes class;
char *value;

{ int number = -1;
  char buffer[maxvarsize], *sp;

if ( ! IsInstallable(CLASSBUFF))
   {
   Debug1("Not installing %s, no match\n",value);
   return;
   }

Debug1("(Action is control, storing class [%s=%s])\n",CLASSTEXT[class],value);

switch (class)
   {
   case site:
   case faculty:  if (VFACULTY[0] != '\0')
                     {
                     yyerror("Multiple declaration of variable faculty / site");
                     FatalError("Redefinition of basic system variable");
                     }

                  strcpy(VFACULTY,value);
                  break;

   case domain:  if (strcmp(VDOMAIN,"undefined.domain") != 0)
                     {
                     yyerror("Multiple declaration of variable domain");
                     FatalError("Redefinition of basic system variable");
                     }

                  strcpy(VDOMAIN,value);
		  
		  if (!strstr(VSYSNAME.nodename,VDOMAIN))
		     {
		     sprintf(VFQNAME,"%s.%s",VSYSNAME.nodename,VDOMAIN);
		     }
		  else
		     {
		     strcpy(VFQNAME,VSYSNAME.nodename);
		     }
		  
		  if (! NOHARDCLASSES)
		     {
		     if (strlen(VFQNAME) > maxvarsize-1)
			{
			FatalError("The fully qualified name is longer than maxvarsize!!");
			}
		     
		     strcpy(buffer,VFQNAME);
		     
		     for (sp=buffer; *sp != '\0'; sp++)
			{
			if (*sp == '.')
			   {
			   *sp = '_';
			   }
			}
		     AddClassToHeap(buffer);
		     }
                  break;

   case sysadm:  /* Can be redefined */

                  strcpy(VSYSADM,value);
                  break;

   case netmask:  if (VNETMASK[0] != '\0')
                     {
                     yyerror("Multiple declaration of variable netmask");
                     FatalError("Redefinition of basic system variable");
                     }
                  strcpy(VNETMASK,value);
                  break;


   case mountpath: SetMountPath(value);
                   break;

   case repository:
                   SetRepository(value);
		   break;

   case homepat:  
                  Debug1("Installing %s as home pattern\n",value);
                  AppendItem(&VHOMEPATLIST,value,CLASSBUFF);
                  break;


   case timezon:  if (VTIMEZONE[0] != '\0')
                     {
                     yyerror ("Multiple declaration of variable timezone");
                     FatalError("Redefinition of basic system variable");
                     }

                  strcpy(VTIMEZONE,value);
                  break;

   case sensiblesize: 
                  sscanf(value,"%d",&number);
                  if (number > 0)
                     {
                     SENSIBLEFSSIZE = number;
                     }
                  else
                     {
                     yyerror("Silly value for sensiblesize");
                     }
                  break;

   case sensiblecount:
                  sscanf(value,"%d",&number);
                  if (number > 0)
                     {
                     SENSIBLEFILECOUNT = number;
                     }
                  else
                     {
                     yyerror("Silly value for sensiblecount");
                     }

                  break;

   case editfilesize:
                  sscanf(value,"%d",&number);

                  if (number > 10)
                     {
                     EDITFILESIZE = number;
                     }
                  else
                     {
                     yyerror("Silly value for editfilesize");
                     }

                  break;


   case ifelapsed:
                  sscanf(value,"%d",&number);

                  if (number >= 0)
                     {
                     VDEFAULTIFELAPSED = VIFELAPSED = number;
                     }
                  else
                     {
                     yyerror("Silly value for IfElapsed");
                     }

                  break;
		  
   case expireafter:
                  sscanf(value,"%d",&number);

                  if (number > 0)
                     {
                     VDEFAULTEXPIREAFTER = VEXPIREAFTER = number;
                     }
                  else
                     {
                     yyerror("Silly value for ExpireAfter");
                     }

                  break;
		  

   case actionsequence:
                  AppendToActionSequence(value);
                  break;

   case accesss:
                  AppendToAccessList(value);
                  break;

   case nfstypec:
                  strcpy(VNFSTYPE,value); 
                  break;

   case addclasses:
                  AddCompoundClass(value);
                  break;

   case excludecopy:
                  PrependItem(&VEXCLUDECOPY,value,CLASSBUFF);
                  break;
		  
   case excludelink:
                  PrependItem(&VEXCLUDELINK,value,CLASSBUFF);
                  break;
		  
   case copylinks:
                  PrependItem(&VCOPYLINKS,value,CLASSBUFF);
                  break;
   case linkcopies:
                  PrependItem(&VLINKCOPIES,value,CLASSBUFF);
                  break;

   case reposchar:
		    if (strlen(value) > 1)
			{
			yyerror("reposchar can only be a single letter");
			break;
			}
		     if (value[0] == '/')
			{
			yyerror("illegal value for reposchar");
			break;
			}
		     REPOSCHAR = value[0];
		     break;

   case listseparator:
			if (strlen(value) > 1)
			   {
			   yyerror("listseparator can only be a single letter");
			   break;
			   }
			if (value[0] == '/')
			   {
			   yyerror("illegal value for listseparator");
			   break;
			   }
			LISTSEPARATOR = value[0];
			break;
			
   case underscoreclasses:
                        if (strcmp(value,"on") == 0)
			   { char rename[maxvarsize];
			   UNDERSCORE_CLASSES=true;
			   Verbose("Resetting classes using underscores...\n");
			   while(DeleteItemContaining(&VHEAP,CLASSTEXT[VSYSTEMHARDCLASS]))
			      {
			      }

			   sprintf(rename,"_%s",CLASSTEXT[VSYSTEMHARDCLASS]);
			   
                           AddClassToHeap(rename);
			   break;
			   }

			if (strcmp(value,"off") == 0)
			   {
			   UNDERSCORE_CLASSES=false;
			   break;
			   }
			
                        yyerror("illegal value for underscoreclasses");
			break;

   case interfacename:if (strlen(value)>15)
                        {
	     	        yyerror("Silly interface name, (should be something link eth0)");
	                }

                     strcpy(VIFNAME,value);
		     VIFDEV[VSYSTEMHARDCLASS] = VIFNAME; /* override */
		     Debug("Overriding interface with %s\n",VIFDEV[VSYSTEMHARDCLASS]);
		     break;

   default:       AddMacroValue(CURRENTITEM,value);
                  break;
                  
   }


}

/*******************************************************************/

HandleOptionalFileAttribute(item)

char *item;

{ char value[maxvarsize];

VBUFF[0] = value[0] = '\0';

ExpandVarstring(item,VBUFF,NULL);

sscanf(VBUFF,"%*[^=]=%s",value);

if (value[0] == '\0')
   {
   yyerror("File attribute with no value");
   }

Debug1("HandleOptionalFileAttribute(%s)\n",value);

switch(GetCommAttribute(item))
   {
   case cfrecurse: HandleRecurse(value);
                   break;
   case cfmode:    ParseModeString(value,&PLUSMASK,&MINUSMASK);
                   break;
   case cfowner:   strcpy(VUIDNAME,value);
                   break;
   case cfgroup:   strcpy(VGIDNAME,value);
                   break;
   case cfaction:  FILEACTION = GetFileAction(value);
                   break;
   case cflinks:   HandleTravLinks(value);
                   break;
   case cfexclude: PrependItem(&VEXCLUDEPARSE,value,"any");
                   break;
   case cfinclude: PrependItem(&VINCLUDEPARSE,value,"any");
                   break;
   default:        yyerror("Illegal file attribute");
   }
}


/*******************************************************************/

HandleOptionalImageAttribute(item)

char *item;

{ char value[maxvarsize];

VBUFF[0] = value[0] = '\0';

ExpandVarstring(item,VBUFF,NULL);

sscanf(VBUFF,"%*[^=]=%s",value);

if (value[0] == '\0')
   {
   yyerror("Image attribute with no value");
   }

Debug1("HandleOptionalImageAttribute(%s)\n",value);

switch(GetCommAttribute(item))
   {
   case cfmode:    ParseModeString(value,&PLUSMASK,&MINUSMASK);
                   break;
   case cfowner:   strcpy(VUIDNAME,value);
                   break;
   case cfgroup:   strcpy(VGIDNAME,value);
                   break;
   case cfdest:    strcpy(DESTINATION,value);
                   break;
   case cfaction:  strcpy(IMAGEACTION,value);
                   break;
   case cfforce:   HandleForceCopy(value);
                   break;
   case cfbackup:  HandleCopyBackup(value);
                   break;
   case cfrecurse: HandleRecurse(value);
                   break;
   case cftype:    HandleCopyType(value);
                   break;
   case cfexclude: PrependItem(&VEXCLUDEPARSE,value,"any");
                   break;
   case cfsymlink: PrependItem(&VCPLNPARSE,value,"any");
                   break;
   case cfinclude: PrependItem(&VINCLUDEPARSE,value,"any");
                   break; 
   case cflntype:  HandleLinkType(value);
                   break;
   case cfserver:  HandleServer(value);
                   break;
   case cfdefine:  HandleDefine(value);
                   break;
   case cfsize:    HandleCopySize(value);
                   break;
   default:        yyerror("Illegal copy attribute");
   }
}

/******************************************************************/

HandleOptionalRequired(item)

char *item;

{ char value[maxvarsize];

VBUFF[0] = value[0] = '\0';

ExpandVarstring(item,VBUFF,NULL);

sscanf(VBUFF,"%*[^=]=%s",value);

if (value[0] == '\0')
   {
   yyerror("Required/disk attribute with no value");
   }

Debug1("HandleOptionalRequiredAttribute(%s)\n",value);

switch(GetCommAttribute(item))
   {
   case cffree:    HandleRequiredSize(value);
                   break;
   case cfdefine:  HandleDefine(value);
                   break;
   default:        yyerror("Illegal disk/required attribute");
   }

}

/******************************************************************/

HandleOptionalTidyAttribute(item)

char *item;

{ char value[maxvarsize];

VBUFF[0] = value[0] = '\0';

ExpandVarstring(item,VBUFF,NULL);

sscanf(VBUFF,"%*[^=]=%s",value);

if (value[0] == '\0')
   {
   yyerror("Tidy attribute with no value");
   }

Debug1("HandleOptionalTidyAttribute(%s)\n",value);

switch(GetCommAttribute(item))
   {
   case cfrecurse: HandleRecurse(value);
                   break;

   case cfpattern: strcpy(CURRENTITEM,value);
                   if (*value == '/')
		      {
		      yyerror("search pattern begins with / must be a relative name");
		      }
                   break;

   case cfage:     HandleAge(value);
                   break;

   case cflinks:   HandleTravLinks(value);
                   break;

   case cfsize:    HandleTidySize(value);
                   break;

   case cftype:    HandleTidyType(value);
                   break;

   case cfdirlinks:
                   HandleTidyLinkDirs(value);
		   break;

   case cfrmdirs:  HandleTidyRmdirs(value);
                   break;

   default:        yyerror("Illegal tidy attribute");
   }
}

/******************************************************************/

HandleOptionalDirAttribute(item)

char *item;

{ char value[maxvarsize];

VBUFF[0] = value[0] = '\0';

ExpandVarstring(item,VBUFF,NULL);

sscanf(item,"%*[^=]=%s",value);

if (value[0] == '\0')
   {
   yyerror("Directory attribute with no value");
   }

Debug1("HandleOptionalDirAttribute(%s)\n",value);

switch(GetCommAttribute(item))
   {
   case cfmode:    ParseModeString(value,&PLUSMASK,&MINUSMASK);
                   break;
   case cfowner:   strcpy(VUIDNAME,value);
                   break;
   case cfgroup:   strcpy(VGIDNAME,value);
                   break;
   default:        yyerror("Illegal directory attribute");
   }
}


/*******************************************************************/

HandleOptionalDisableAttribute(item)

char *item;

{ char value[maxvarsize];

VBUFF[0] = value[0] = '\0';

ExpandVarstring(item,VBUFF,NULL);

sscanf(VBUFF,"%*[^=]=%s",value);

if (value[0] == '\0')
   {
   yyerror("Disable attribute with no value");
   }

Debug1("HandleOptionalDisableAttribute(%s)\n",value);

switch(GetCommAttribute(item))
   {
   case cftype:    HandleDisableFileType(value);
                   break;

   case cfrotate:  HandleDisableRotate(value);
                   break;
		   
   case cfsize:    HandleDisableSize(value);
                   break;

   default:        yyerror("Illegal disable attribute");
   }
}


/*******************************************************************/

HandleOptionalLinkAttribute(item)

char *item;

{ char value[maxvarsize];

VBUFF[0] = value[0] = '\0';

ExpandVarstring(item,VBUFF,NULL);

sscanf(VBUFF,"%*[^=]=%s",value);

if (value[0] == '\0')
   {
   yyerror("Link attribute with no value");
   }

Debug1("HandleOptionalLinkAttribute(%s)\n",value);

switch(GetCommAttribute(item))
   {
   case cfaction:  HandleLinkAction(value);
                   break;
   case cftype:    HandleLinkType(value);
                   break;
   case cfexclude: PrependItem(&VEXCLUDEPARSE,value,"any");
                   break;
   case cfinclude: PrependItem(&VINCLUDEPARSE,value,"any");
                   break;
   case cfcopy:    PrependItem(&VCPLNPARSE,value,"any");
                   break;
   case cfrecurse: HandleRecurse(value);
                   break;
   case cfcptype:  HandleCopyType(value);
                   break;
   default:        yyerror("Illegal link attribute");
   }
}

/*******************************************************************/

HandleOptionalProcessAttribute(item)

char *item;

{ char value[maxvarsize];

if (HAVE_RESTART)     /* wrote `restart' without following by ".." */
   {
   yyerror("Missing restart command (quoted string expected) or bad SetOptionString placement");
   }

VBUFF[0] = value[0] = '\0';

ExpandVarstring(item,VBUFF,NULL);

sscanf(VBUFF,"%*[^=]=%s",value);

if (value[0] == '\0')
   {
   yyerror("Process attribute with no value");
   }

Debug1("HandleOptionalProcessAttribute(%s)\n",value);

switch(GetCommAttribute(item))
   {
   case cfaction:  if (strcmp(value,"signal") == 0 || strcmp(value,"do") == 0)
                      {
		      PROACTION = 's';
                      }
                   else if (strcmp(value,"warn") == 0)
		      {
		      PROACTION = 'w';
		      }
                   else
		      {
		      yyerror("Unknown action for processes");
		      }
                   break;
   case cfmatches: HandleProcessMatches(value);
                   break;
   case cfsignal:  HandleProcessSignal(value);
                   break;
   default:        yyerror("Illegal process attribute");
   }
}

/*******************************************************************/

HandleOptionalScriptAttribute(item)

char *item;

{ char value[maxvarsize];

VBUFF[0] = value[0] = '\0';

ExpandVarstring(item,VBUFF,NULL);

sscanf(VBUFF,"%*[^=]=%s",value);

if (value[0] == '\0')
   {
   yyerror("Shellcommand attribute with no value");
   }

Debug1("HandleOptionalLinkAttribute(%s)\n",value);

switch(GetCommAttribute(item))
   {
   case cftimeout: HandleTimeOut(value);
                   break;
   default:        yyerror("Illegal shellcommand attribute");
   }

}

/*******************************************************************/

HandleFileItem(item)

char *item;

{ char err[100];

if (strcmp(item,"home") == 0)
   {
   ACTIONPENDING=true;
   strcpy(CURRENTPATH,"home");
   return;
   }

sprintf(err,"Unknown attribute %s",item);
yyerror(err);
}


/*******************************************************************/

InstallBroadcastItem(item)

char *item;

{
Debug1("Install broadcast mode (%s)\n",item);

if ( ! IsInstallable(CLASSBUFF))
   {
   Debug1("Not installing %s, no match\n",item);
   return;
   }

if (VBROADCAST[0] != '\0')
   {
   yyerror("Multiple declaration of variable broadcast");
   FatalError("Redefinition of basic system variable");
   }

if (strcmp("ones",item) == 0)
   {
   strcpy(VBROADCAST,"one");
   return;
   }

if (strcmp("zeroes",item) == 0)
   {
   strcpy(VBROADCAST,"zero");
   return;
   }

if (strcmp("zeros",item) == 0)
   {
   strcpy(VBROADCAST,"zero");
   return;
   }

yyerror ("Unknown broadcast mode (should be ones, zeros or zeroes)");
FatalError("Unknown broadcast mode");
}

/*******************************************************************/

InstallDefaultRouteItem(item)

char *item;

{ struct hostent *hp;
  struct in_addr inaddr;

Debug1("Install defaultroute mode (%s)\n",item);

if ( ! IsInstallable(CLASSBUFF))
   {
   Debug1("Not installing %s, no match\n",item);
   return;
   }

if (VDEFAULTROUTE[0] != '\0')
   {
   yyerror("Multiple declaration of variable defaultroute");
   FatalError("Redefinition of basic system variable");
   }

if (inet_addr(item) == -1)
   {
   if ((hp = gethostbyname(item)) == NULL)
      {
      perror("InstallDefaultRouteItem: gethostbyname: ");
      yyerror ("Bad specification of default packet route: hostname or decimal IP address");
      FatalError("Stopped.");
      }
   else
      {
      bcopy(hp->h_addr,&inaddr, hp->h_length);
      strcpy(VDEFAULTROUTE,inet_ntoa(inaddr));
      }
   }
else
   {
   strcpy(VDEFAULTROUTE,item);
   }
}

/*******************************************************************/

HandleGroupItem(item,type)

char *item;
enum itemtypes type;

{ char *machine, *user, *domain;

Debug1("Handling item (%s) in group (%s), type=%d\n",item,GROUPBUFF,type);

switch (type)
   {
   case simple:    if (strcmp(item,VDEFAULTBINSERVER.name) == 0)
                      {
                      AddClassToHeap(GROUPBUFF);
                      break;
                      }

                   if (IsItemIn(VHEAP,item))  /* group reference */
                      {
                      AddClassToHeap(GROUPBUFF);
                      break;
                      }

                   break;

   case netgroup:  setnetgrent(item);

                   while (getnetgrent(&machine,&user,&domain))
                      {
                      if (strcmp(machine,VDEFAULTBINSERVER.name) == 0)
                         {
                         Debug1("Matched %s in netgroup %s\n",machine,item);
                         AddClassToHeap(GROUPBUFF);
                         break;
                         }

		      if (strcmp(machine,VFQNAME) == 0)
                         {
                         Debug1("Matched %s in netgroup %s\n",machine,item);
                         AddClassToHeap(GROUPBUFF);
                         break;
                         }
                      }
                   
                   endnetgrent();
                   break;


   case groupdeletion: 

                   setnetgrent(item);

                   while (getnetgrent(&machine,&user,&domain))
                      {
                      if (strcmp(machine,VDEFAULTBINSERVER.name) == 0)
                         {
                         Debug1("Matched delete item %s in netgroup %s\n",machine,item);
                         DeleteItemStarting(&VHEAP,GROUPBUFF);
                         break;
                         }
		      
                      if (strcmp(machine,VFQNAME) == 0)
                         {
                         Debug1("Matched delete item %s in netgroup %s\n",machine,item);
                         DeleteItemStarting(&VHEAP,GROUPBUFF);
                         break;
                         }
		      
                      }
                   
                   endnetgrent();
                   break;

   case classscript:

                   VBUFF[0] = '\0';
                   ExpandVarstring (item+1,VBUFF,NULL);
                   if (VBUFF[0] != '/')
                      {
                      yyerror("Quoted scripts must begin with / for absolute path");
                      break;
                      }

                   if (ShellCommandReturnsZero(VBUFF))
                      {
                      AddClassToHeap(GROUPBUFF);
                      }

                   break;

   case deletion:  if (strcmp(item,VDEFAULTBINSERVER.name) == 0)
                      {
                      DeleteItemStarting(&VHEAP,GROUPBUFF);
                      }
                   break;

   default:        yyerror("Software error");
                   FatalError("Unknown item type");
   }
}

/*******************************************************************/

HandleHomePattern(pattern)

char *pattern;

{
VBUFF[0]='\0';
ExpandVarstring(pattern,VBUFF,"");
AppendItem(&VHOMEPATLIST,VBUFF,CLASSBUFF);
}

/*******************************************************************/

AppendNameServer(item)

char *item;

{ 
Debug1("Installing item (%s) in the nameserver list\n",item);

if ( ! IsInstallable(CLASSBUFF))
   {
   Debug1("Not installing %s, no match\n",item);
   return;
   }

AppendItem(&VRESOLVE,item,CLASSBUFF);
}

/*******************************************************************/

AppendImport(item)

char *item;

{ 
if ( ! IsInstallable(CLASSBUFF))
   {
   Debug1("Not installing %s, no match\n",item);
   return;
   }

if (strcmp(item,VCURRENTFILE) == 0)
   {
   yyerror("A file cannot import itself");
   FatalError("Infinite self-reference in class inheritance");
   }

Debug1(">>Installing item (%s) in the import list\n",item);

AppendItem(&VIMPORT,item,CLASSBUFF);
}


/*******************************************************************/

InstallHomeserverItem(item)

char *item;

{
if ( ! IsInstallable(CLASSBUFF))
   {
   Debug1("Not installing %s, no match\n",item);
   return;
   }

Debug1("Installing item (%s) in  list for homeservers (%s) in group (%s)\n",item,CLASSBUFF,GROUPBUFF);

AppendItem(&VHOMESERVERS,item,CLASSBUFF);
}

/*******************************************************************/

InstallBinserverItem(item)           /* Install if matches classes */

char *item;

{
if ( ! IsInstallable(CLASSBUFF))
   {
   Debug1("Not installing %s, no match\n",item);
   return;
   }

AppendItem(&VBINSERVERS,item,CLASSBUFF);
}

/*******************************************************************/

InstallMailserverPath(path)

char *path;

{
if ( ! IsInstallable(CLASSBUFF))
   {
   Debug1("Not installing %s, no match\n",path);
   return;
   }

if (VMAILSERVER[0] != '\0')
   {
   FatalError("Redefinition of mailserver");
   }

strcpy(VMAILSERVER,path);

Debug1("Installing mailserver (%s) for group (%s)",path,GROUPBUFF);
}

/*******************************************************************/

AppendScript(item,timeout)

char *item;
int timeout;

{ struct TwoDimList *tp = NULL;
  struct ShellComm *ptr;
  char *sp, *spe, *spn;

Debug1("Installing item (%s) in the script list\n",item);

if ( ! IsInstallable(CLASSBUFF))
   {
   Debug1("Not installing %s, no match\n",item);
   return;
   }

Build2DListFromVarstring(&tp,item,' '); /* Must be at least one space between each var */

Set2DList(tp);

for (sp = Get2DListEnt(tp); sp != NULL; sp = Get2DListEnt(tp))
   {
   if (*sp != '/')
      {
      yyerror("scripts or commands must have absolute path names");
      printf ("cfengine: concerns: %s\n",sp);
      return;
      }

   if ((ptr = (struct ShellComm *)malloc(sizeof(struct ShellComm))) == NULL)
      {
      FatalError("Memory Allocation failed for AppendScript() #1");
      }

   if ((spe = malloc(strlen(CLASSBUFF)+1)) == NULL)
      {
      FatalError("Memory Allocation failed for Appendscript() #2");
      }

   if ((spn = malloc(strlen(sp)+1)) == NULL)
      {
      FatalError("Memory Allocation failed for Appendscript() #3");
      }

   if (VSCRIPTTOP == NULL)                 /* First element in the list */
      {
      VSCRIPT = ptr;
      }
   else
      {
      VSCRIPTTOP->next = ptr;
      }

   strcpy (spn,sp);
   strcpy (spe,CLASSBUFF);

   ptr->timeout = timeout;
   ptr->name = spn;
   ptr->classes = spe;
   ptr->next = NULL;
   
   VSCRIPTTOP = ptr;
   }

Delete2DList(tp);
}

/*******************************************************************/

InstallLinkItem (from,to)

char *from, *to;

{ struct Link *ptr;
  char *spfrom,*spto,*spe;
  char buffer[bufsize];
  
Debug1("Storing Link: %s -> %s\n",from,to);

if ( ! IsInstallable(CLASSBUFF))
   {
   Debug1("Not installing link no match\n");
   return;
   }

bzero(VBUFF,bufsize);
ExpandVarstring(from,VBUFF,"");

bzero(buffer,bufsize);
ExpandVarstring(to,buffer,"");

if ((ptr = (struct Link *)malloc(sizeof(struct Link))) == NULL)
   {
   FatalError("Memory Allocation failed for InstallListItem() #1");
   }
if ((spfrom = malloc(strlen(VBUFF)+1)) == NULL)
   {
   FatalError("Memory Allocation failed for InstallListItem() #2");
   }
if ((spto = malloc(strlen(buffer)+1)) == NULL)
   {
   FatalError("Memory Allocation failed for InstallListItem() #3");
   }
if ((spe = malloc(strlen(CLASSBUFF)+1)) == NULL)
   {
   FatalError("Memory Allocation failed for InstallListItem() #4");
   }

if (VLINKTOP == NULL)                 /* First element in the list */
   {
   VLINK = ptr;
   }
else
   {
   VLINKTOP->next = ptr;
   }

strcpy (spfrom,VBUFF);
strcpy (spto,buffer);
strcpy (spe,CLASSBUFF);

if (strlen(spfrom) > 1)
   {
   DeleteSlash(spfrom);
   }

if (strlen(spto) > 1)
   {
   DeleteSlash(spto);
   }

ptr->classes = spe;
ptr->from = spfrom;
ptr->to = spto;
ptr->force = FORCELINK;
ptr->silent = LINKSILENT;
ptr->type = LINKTYPE;
ptr->copytype = COPYTYPE;
ptr->next = NULL;
ptr->copy = VCPLNPARSE;
ptr->exclusions = VEXCLUDEPARSE;
ptr->inclusions = VINCLUDEPARSE;
ptr->recurse = VRECURSE;
VLINKTOP = ptr;

if (ptr->recurse != 0)
   {
   yyerror("Recursion can only be used with +> multiple links");
   }

InitializeAction();
}

/*******************************************************************/

InstallLinkChildrenItem (from,to)

char *from, *to;

{ struct Link *ptr;
  char *spfrom,*spto, *spe, *sp;
  struct TwoDimList *tp = NULL;

Debug1("Storing Linkchildren item: %s -> %s\n",from,to);

if ( ! IsInstallable(CLASSBUFF))
   {
   Debug1("Not installing linkchildren no match\n");
   return;
   }

VBUFF[0]='\0';                                /* Expand any variables */
ExpandVarstring(from,VBUFF,"");

Build2DListFromVarstring(&tp,to,'/');
    
Set2DList(tp);

for (sp = Get2DListEnt(tp); sp != NULL; sp = Get2DListEnt(tp))
   {
   if ((ptr = (struct Link *)malloc(sizeof(struct Link))) == NULL)
      {
      FatalError("Memory Allocation failed for InstallListChildrenItem() #1");
      }

   if ((spfrom = malloc(strlen(VBUFF)+1)) == NULL)
      {
      FatalError("Memory Allocation failed for InstallLinkchildrenItem() #2");
      }

   if ((spto = malloc(strlen(sp)+1)) == NULL)
      {
      FatalError("Memory Allocation failed for InstallLinkChildrenItem() #3");
      }

   if ((spe = malloc(strlen(CLASSBUFF)+1)) == NULL)
      {
      FatalError("Memory Allocation failed for InstallLinkChildrenItem() #3");
      }

   if (VCHLINKTOP == NULL)                 /* First element in the list */
      {
      VCHLINK = ptr;
      }
   else
      {
      VCHLINKTOP->next = ptr;
      }

   strcpy (spfrom,VBUFF);
   strcpy (spto,sp);
   strcpy (spe,CLASSBUFF);

   ptr->classes = spe;
   ptr->from = spfrom;
   ptr->to = spto;
   ptr->force = FORCELINK;
   ptr->silent = LINKSILENT;
   ptr->type = LINKTYPE;
   ptr->next = NULL;
   ptr->copy = VCPLNPARSE;
   ptr->exclusions = VEXCLUDEPARSE;
   ptr->inclusions = VINCLUDEPARSE;
   ptr->recurse = VRECURSE;
   VCHLINKTOP = ptr;

   if (ptr->recurse != 0 && strcmp(spto,"linkchildren") == 0)
      {
      yyerror("Sorry don't know how to recurse with linkchildren keyword");
      }
   }

Delete2DList(tp);

InitializeAction();
}


/*******************************************************************/

InstallRequiredPath(path,defines,freespace)

char *path, *defines;
int freespace;

{ struct Disk *ptr;
  char *spname, *spclass, *spdef, buffer[bufsize];

Debug1("Installing item (%s) in the required list\n",path);

if ( ! IsInstallable(CLASSBUFF))
   {
   Debug1("Not installing %s, no match\n",path);
   return;
   }

VBUFF[0] = '\0';
ExpandVarstring(path,VBUFF,"");

buffer[0] = '\0';
ExpandVarstring(defines,buffer,"");


/* AppendItem(&VREQUIRED,VBUFF,CLASSBUFF);*/

if ((ptr = (struct Disk *)malloc(sizeof(struct Disk))) == NULL)
   {
   FatalError("Memory Allocation failed for InstallRequired() #1");
   }

if ((spname = malloc(strlen(VBUFF)+1)) == NULL)
   {
   FatalError("Memory Allocation failed for InstallRequired() #2");
   }

if ((spclass = malloc(strlen(buffer)+1)) == NULL)
   {
   FatalError("Memory Allocation failed for InstallRequired() #2");
   }

if ((spdef = malloc(strlen(defines)+1)) == NULL)
   {
   FatalError("Memory Allocation failed for Installrequied() #2");
   }

if (VREQUIRED == NULL)                 /* First element in the list */
   {
   VREQUIRED = ptr;
   }
else
   {
   VREQUIREDTOP->next = ptr;
   }

strcpy(spclass,CLASSBUFF);
strcpy(spdef,buffer);
strcpy(spname,VBUFF);

ptr->name = spname;
ptr->classes = spclass;
ptr->define = spdef;
ptr->freespace = freespace;
VREQUIREDTOP = ptr;

InitializeAction();
}

/*******************************************************************/

AppendMountable(path)

char *path;

{
Debug1("Adding mountable %s to list\n",path);

AppendItem(&VMOUNTABLES,path,CLASSBUFF);
}

/*******************************************************************/

AppendUmount(path)

char *path;

{ 
if ( ! IsInstallable(CLASSBUFF))
   {
   Debug1("Not installing %s, no match\n",path);
   return;
   }

Debug1("Adding unmount %s to list\n",path);
AppendItem(&VUNMOUNT,path,CLASSBUFF);
}

/*******************************************************************/

AppendMiscMount(from,onto,perm)

char *from, *onto, *perm;

{ struct MiscMount *ptr;
  char *sp1,*sp2,*sp3, *spe;

Debug1("Adding misc mountable %s %s (%s) to list\n",from,onto,perm);

if ( ! IsInstallable(CLASSBUFF))
   {
   Debug1("Not installing %s, no match\n",from);
   return;
   }

if ((ptr = (struct MiscMount *)malloc(sizeof(struct MiscMount))) == NULL)
   {
   FatalError("Memory Allocation failed for AppendMiscMount #1");
   }

if ((sp1 = malloc(strlen(from)+1)) == NULL)
   {
   FatalError("Memory Allocation failed for AppendMiscMount() #2");
   }

if ((sp2 = malloc(strlen(onto)+1)) == NULL)
   {
   FatalError("Memory Allocation failed for AppendMiscMount() #3");
   }

if ((sp3 = malloc(strlen(perm)+1)) == NULL)
   {
   FatalError("Memory Allocation failed for AppendMiscMount() #4");
   }

if ((spe = malloc(strlen(CLASSBUFF)+1)) == NULL)
   {
   FatalError("Memory Allocation failed for AppendMiscMount() #5");
   }

strcpy(sp1,from);
strcpy(sp2,onto);
strcpy(sp3,perm);
strcpy(spe,CLASSBUFF);

if (VMISCMOUNTTOP == NULL)                 /* First element in the list */
   {
   VMISCMOUNT = ptr;
   }
else
   {
   VMISCMOUNTTOP->next = ptr;
   }

ptr->classes = CLASSBUFF;
ptr->from = sp1;
ptr->onto = sp2;
ptr->options  = sp3;
ptr->next = NULL;
VMISCMOUNTTOP = ptr;
}


/*******************************************************************/

AppendIgnore(path)

char *path;

{ struct TwoDimList *tp = NULL;
  char *sp;

Debug1("Installing item (%s) in the ignore list\n",path);

if ( ! IsInstallable(CLASSBUFF))
   {
   Debug1("Not installing %s, no match\n",path);
   return;
   }

Build2DListFromVarstring(&tp,path,'/');
    
Set2DList(tp);

for (sp = Get2DListEnt(tp); sp != NULL; sp = Get2DListEnt(tp))
   {
   AppendItem(&VIGNORE,sp,CLASSBUFF);
   }

Delete2DList(tp);
}

/*******************************************************************/

InstallPending(action)

enum actions action;

{
if (ACTIONPENDING)
   {
   Debug1("\n   [BEGIN InstallPending %s\n",ACTIONTEXT[action]);
   }
else
   {
   Debug1("   (No actions pending in %s)\n",ACTIONTEXT[action]);
   return;
   }

switch (action)
   {
   case files:
                  InstallFileListItem(CURRENTPATH,PLUSMASK,MINUSMASK,FILEACTION,
				      VUIDNAME,VGIDNAME,VRECURSE,(char)PTRAVLINKS);
                  break;

   case processes: InstallProcessItem(EXPR,RESTART,PROMATCHES,PROCOMP,
				      PROSIGNAL,PROACTION,CLASSBUFF);
                   break;
   case image:
                  InstallImageItem(CURRENTPATH,PLUSMASK,MINUSMASK,DESTINATION,
				   IMAGEACTION,VUIDNAME,VGIDNAME,FORCECOPY,IMGSIZE,IMGCOMP,
				   IMAGEBACKUP,VRECURSE,COPYTYPE,LINKTYPE,CFSERVER,ALLCLASSBUFFER);
                  break;

   case tidy:     if (VAGE >= 99999)
                     {
                     yyerror("Must specify an age for tidy actions");
                     return;
                     }
                  InstallTidyItem(CURRENTPATH,CURRENTITEM,VRECURSE,VAGE,(char)PTRAVLINKS,
				  TIDYSIZE,AGETYPE,LINKDIRS,TIDYDIRS,CLASSBUFF);
                  break;

   case makepath: InstallMakePath(CURRENTPATH,PLUSMASK,MINUSMASK,VUIDNAME,VGIDNAME);
                  break;

   case disable:  AppendDisable(CURRENTPATH,CURRENTITEM,ROTATE,DISCOMP,DISABLESIZE);
                  break;

   case shellcommands:
                  AppendScript(CURRENTPATH,VTIMEOUT);
		  break;

   case required:
                  InstallRequiredPath(CURRENTPATH,ALLCLASSBUFFER,IMGSIZE);
                  break;

   case links:

                  if (LINKTO[0] == '\0')
                     {
                     return;
                     }

                  if (ACTION_IS_LINKCHILDREN)
                     {
                     InstallLinkChildrenItem(LINKFROM,LINKTO);
                     ACTION_IS_LINKCHILDREN = false;
                     }
                  else if (ACTION_IS_LINK)
                     {
                     InstallLinkItem(LINKFROM,LINKTO);
                     ACTION_IS_LINK = false;
                     }
                  else
                     {
                     return;                                   /* Don't have whole command */
                     }

                  break;
   }

LINKFROM[0] = '\0';
LINKTO[0] = '\0';
ACTIONPENDING = false;
CURRENTITEM[0] = '\0';
CURRENTITEM[0] = '\0';
VRECURSE = 0;
VAGE=99999;
Debug1("   END InstallPending]\n\n");
}

/*******************************************************************/
/* Level 3                                                         */
/*******************************************************************/

AppendDisable(path,type,rotate,comp,size)

char *path, *type, comp;
short rotate;
int size;

{ char *sp, *spe, *sp2;
  char buf[bufsize];
  struct Disable *ptr;
  int i;

Debug1("Installing item (%s) in the disable list\n",path);

if ( ! IsInstallable(CLASSBUFF))
   {
   Debug1("Not installing %s, no match\n",path);
   return;
   }

if (strlen(type) > 0 && strcmp(type,"plain") != 0 && strcmp(type,"file") !=0 && strcmp(type,"link") !=0
    && strcmp(type,"links") !=0 )
   {
   yyerror("Invalid file type in Disable");
   }

for (i=0; i<bufsize; i++)
   {
   buf[i] = VBUFF[i] = '\0';
   }

ExpandVarstring(path,buf,"");

if ((ptr = (struct Disable *)malloc(sizeof(struct Disable))) == NULL)
   {
   FatalError("Memory Allocation failed for AppendDisable() #1");
   }

if ((sp = malloc(strlen(buf)+1)) == NULL)
   {
   FatalError("Memory Allocation failed for AppendDisable() #2");
   }

if ((spe = malloc(strlen(CLASSBUFF)+1)) == NULL)
   {
   FatalError("Memory Allocation failed for AppendDisable() #3");
   }

if (strlen(type) == 0)
   {
   sprintf(VBUFF,"all");
   }
else
   {
   sprintf(VBUFF,"%s",type);
   }

if ((sp2 = malloc(strlen(VBUFF)+1)) == NULL)
   {
   FatalError("Memory Allocation failed for AppendDisable() #4");
   }

if (VDISABLETOP == NULL)                 /* First element in the list */
   {
   VDISABLELIST = ptr;
   }
else
   {
   VDISABLETOP->next = ptr;
   }

strcpy (sp,buf);
strcpy (spe,CLASSBUFF);
strcpy (sp2,VBUFF);

ptr->name = sp;
ptr->type = sp2;
ptr->classes = spe;
ptr->rotate = rotate;
ptr->comp = comp;
ptr->size = size;
ptr->next = NULL;
VDISABLETOP = ptr;
InitializeAction();
}

/*******************************************************************/

InstallTidyItem (path,wild,rec,age,travlinks,tidysize,type,ldirs,tidydirs,classes)

char *wild, *path;
short age,tidysize,tidydirs;
int rec, travlinks;
char type, ldirs, *classes;

{ struct TwoDimList *tp = NULL;
  char *sp;

Build2DListFromVarstring(&tp,path,'/');
   
Set2DList(tp);

for (sp = Get2DListEnt(tp); sp != NULL; sp = Get2DListEnt(tp))
   {
   if (TidyPathExists(sp))
      {
      AddTidyItem(sp,wild,rec,age,travlinks,tidysize,type,ldirs,tidydirs,classes);
      }
   else
      {
      InstallTidyPath(sp,wild,rec,age,travlinks,tidysize,type,ldirs,tidydirs,classes);
      }
   }

Delete2DList(tp);
}


/*******************************************************************/

InstallMakePath(path,plus,minus,uidnames,gidnames)

char *path;
mode_t plus,minus;
char *uidnames;
char *gidnames;

{ struct File *ptr;
  char *sp, *spe;

Debug1("InstallMakePath (%s) (+%o)(-%o)(%s)(%s)\n",path,plus,minus,uidnames,gidnames);

if ( ! IsInstallable(CLASSBUFF))
   {
   Debug1("Not installing directory item, no match\n");
   return;
   }

VBUFF[0]='\0';                                /* Expand any variables */
ExpandVarstring(path,VBUFF,"");

if ((ptr = (struct File *)malloc(sizeof(struct File))) == NULL)
   {
   FatalError("Memory Allocation failed for InstallMakepath() #1");
   }

if ((sp = malloc(strlen(VBUFF)+1)) == NULL)
   {
   FatalError("Memory Allocation failed for InstallMakepath() #2");
   }

if ((spe = malloc(strlen(CLASSBUFF)+1)) == NULL)
   {
   FatalError("Memory Allocation failed for InstallMakepath() #3");
   }

if (VMAKEPATHTOP == NULL)                 /* First element in the list */
   {
   VMAKEPATH = ptr;
   }
else
   {
   VMAKEPATHTOP->next = ptr;
   }

strcpy (sp,VBUFF);
strcpy (spe,CLASSBUFF);

ptr->classes = spe;
ptr->path = sp;
ptr->plus = plus;
ptr->minus = minus;
ptr->recurse = 0;
ptr->action = fixdirs;
ptr->uid = MakeUidList(uidnames);
ptr->gid = MakeGidList(gidnames);
ptr->next = NULL;
VMAKEPATHTOP = ptr;
InitializeAction();
}

/*******************************************************************/

HandleTravLinks(value)

char *value;

{
if (ACTION == tidy && strncmp(CURRENTPATH,"home",4) == 0)
   {
   yyerror("Can't use links= option with special variable home in tidy");
   yyerror("Use command line options instead.\n");
   }

if (PTRAVLINKS != '?')
   {
   Warning("redefinition of links= option");
   }

if ((strcmp(value,"stop") == 0) || (strcmp(value,"false") == 0))
   {
   PTRAVLINKS = (short) 'F';
   return;
   }

if ((strcmp(value,"traverse") == 0) || (strcmp(value,"follow") == 0) || (strcmp(value,"true") == 0))
   {
   PTRAVLINKS = (short) 'T';
   return;
   }

if ((strcmp(value,"tidy"))==0)
   {
   PTRAVLINKS = (short) 'K';
   return;
   }

yyerror("Illegal links= specifier");
}

/*******************************************************************/

HandleTidySize(value)

char *value;

{ int num = -1;
  char *sp, units = 'k';

for (sp = value; *sp != '\0'; sp++)
   {
   *sp = ToLower(*sp);
   }

if (strcmp(value,"empty") == 0)
   {
   TIDYSIZE = CF_EMPTYFILE;
   }
else
   {
   sscanf(value,"%d%c",&num,&units);

   if (num <= 0)
      {
      yyerror("disable/rotate value must be a decimal number >= zero or keyword empty");
      }

   switch (units)
      {
      case 'b': TIDYSIZE = num;
	        break;
      case 'm': TIDYSIZE = num * 1024 * 1024;
	        break;
      default:  TIDYSIZE = num * 1024;
      }
   }
}

/*******************************************************************/

HandleDisableSize(value)

char *value;

{ int i = -1;
  char *sp, units = 'b';

for (sp = value; *sp != '\0'; sp++)
   {
   *sp = ToLower(*sp);
   }

switch (*value)
   {
   case '>': DISCOMP = '>';
             value++;
             break;
   case '<': DISCOMP = '<';
             value++;
             break;
   default : DISCOMP = '=';
   }

sscanf(value,"%d%c",&i,&units);

if (i < 1)
   {
   yyerror("disable size attribute with silly value (must be > 0)");
   }

switch (units)
   {
   case 'k': DISABLESIZE = i * 1024;
             break;
   case 'm': DISABLESIZE = i * 1024 * 1024;
             break;
   default:  DISABLESIZE = i;
   }
}

/*******************************************************************/

HandleCopySize(value)

char *value;

{ int i = -1;
  char *sp, units = 'b';

for (sp = value; *sp != '\0'; sp++)
   {
   *sp = ToLower(*sp);
   }

switch (*value)
   {
   case '>': IMGCOMP = '>';
             value++;
             break;
   case '<': IMGCOMP = '<';
             value++;
             break;
   default : IMGCOMP = '=';
   }

sscanf(value,"%d%c",&i,&units);

if (i < 0)
   {
   yyerror("copy size attribute with silly value (must be a non-negative number)");
   }

switch (units)
   {
   case 'k': IMGSIZE = i * 1024;
             break;
   case 'm': IMGSIZE = i * 1024 * 1024;
             break;
   default:  IMGSIZE = i;
   }
}

/*******************************************************************/

HandleRequiredSize(value)

char *value;

{ int i = -1;
  char *sp, units = 'b';

for (sp = value; *sp != '\0'; sp++)
   {
   *sp = ToLower(*sp);
   }

switch (*value)
   {
   case '>': IMGCOMP = '>';
             value++;
             break;
   case '<': IMGCOMP = '<';
             value++;
             break;
   default : IMGCOMP = '=';
   }

sscanf(value,"%d%c",&i,&units);

if (i < 1)
   {
   yyerror("disk/required size attribute with silly value (must be > 0)");
   }

switch (units)
   {
   case 'b': IMGSIZE = i / 1024;
             break;
   case 'm': IMGSIZE = i * 1024;
             break;
   default:  IMGSIZE = i;
   }
}

/*******************************************************************/

HandleTidyType(value)

char *value;

{
if (strcmp(value,"a")== 0 || strcmp(value,"atime") == 0)
   {
   AGETYPE = 'a';
   return;
   }

if (strcmp(value,"m")== 0 || strcmp(value,"mtime") == 0)
   {
   AGETYPE = 'm';
   return;
   }

if (strcmp(value,"c")== 0 || strcmp(value,"ctime") == 0)
   {
   AGETYPE = 'c';
   return;
   }

yyerror("Illegal age search type, must be atime/ctime/mtime");
}

/*******************************************************************/

HandleTidyLinkDirs(value)

char *value;

{
if (strcmp(value,"keep")== 0)
   {
   LINKDIRS = 'k';
   return;
   }

if (strcmp(value,"tidy")== 0 || strcmp(value,"delete") == 0)
   {
   LINKDIRS = 't';
   return;
   }

yyerror("Illegal linkdirs value, must be keep/delete/tidy");
}

/*******************************************************************/

HandleTidyRmdirs(value)

char *value;

{
if (strcmp(value,"true") == 0)
   {
   TIDYDIRS = true;
   return;
   }

if (strcmp(value,"false") == 0)
   {
   TIDYDIRS = false;
   return;
   }

yyerror("Illegal rmdirs value, must be true/false");
}

/*******************************************************************/

HandleForceCopy(value)

char *value;

{
if (strcmp(value,"true") == 0 || strcmp(value,"on") == 0)
   {
   FORCECOPY = true;
   return;
   }

if (strcmp(value,"false") == 0 || strcmp(value,"off") == 0)
   {
   FORCECOPY = false;
   return;
   }

yyerror("Illegal copy attribute for force= ");
}

/*******************************************************************/

HandleCopyBackup(value)

char *value;

{
if (strcmp(value,"true") == 0 || strcmp(value,"on") == 0)
   {
   IMAGEBACKUP = true;
   return;
   }

if (strcmp(value,"false") == 0 || strcmp(value,"off") == 0)
   {
   IMAGEBACKUP = false;
   return;
   }

yyerror("Illegal copy attribute for force= ");
}

/*******************************************************************/

HandleTimeOut(value)

char *value;

{ int num = -1;

sscanf(value,"%d",&num);

if (num <= 0)
   {
   yyerror("timeout value must be a decimal number > 0");
   }

VTIMEOUT = num;
}


/*******************************************************************/

GetFileAction(action)

char *action;

{ int i;

for (i = 0; FILEACTIONTEXT[i] != '\0'; i++)
   {
   if (strcmp(action,FILEACTIONTEXT[i]) == 0)
      {
      return i;
      }
   }

yyerror("Unknown action type");
return (int) warnall;
}


/*******************************************************************/

InstallFileListItem(path,plus,minus,action,uidnames,gidnames,recurse,travlinks)

char *path;
mode_t plus,minus;
enum fileactions action;
char *uidnames;
char *gidnames;
int recurse;
char travlinks;

{ struct File *ptr;
  char *sp, *spe, *spl;
  struct TwoDimList *tp = NULL;

Debug1("InstallFileaction (%s) (+%o)(-%o) (%s) (%d) (%c)\n",path,plus,minus,FILEACTIONTEXT[action],action,travlinks);

if ( ! IsInstallable(CLASSBUFF))
   {
   Debug1("Not installing file item, no match\n");
   return;
   }

Build2DListFromVarstring(&tp,path,'/');
    
Set2DList(tp);

for (spl = Get2DListEnt(tp); spl != NULL; spl = Get2DListEnt(tp))
   {
   if ((ptr = (struct File *)malloc(sizeof(struct File))) == NULL)
      {
      FatalError("Memory Allocation failed for InstallFileListItem() #1");
      }

   if ((sp = malloc(strlen(spl)+1)) == NULL)
      {
      FatalError("Memory Allocation failed for InstallFileListItem() #2");
      }

   if ((spe = malloc(strlen(CLASSBUFF)+1)) == NULL)
      {
      FatalError("Memory Allocation failed for InstallFileListItem() #3");
     }

   if (VFILETOP == NULL)                 /* First element in the list */
      {
      VFILE = ptr;
      }
   else
      {
      VFILETOP->next = ptr;
      }

   strcpy (sp,spl);
   strcpy (spe,CLASSBUFF);

   ptr->classes = spe;
   ptr->path = sp;
   ptr->action = action;
   ptr->plus = plus;
   ptr->minus = minus;
   ptr->recurse = recurse;
   ptr->uid = MakeUidList(uidnames);
   ptr->gid = MakeGidList(gidnames);
   ptr->exclusions = VEXCLUDEPARSE;
   ptr->inclusions = VINCLUDEPARSE;
   ptr->travlinks = travlinks;
   ptr->next = NULL;
   VFILETOP = ptr;
   }

Delete2DList(tp);

InitializeAction();
}


/*******************************************************************/

InstallProcessItem(expr,restart,matches,comp,signal,action,classes)

char *expr, *restart, *classes;
short matches, signal;
char action, comp;

{ struct Process *ptr;
  char *sp1, *sp2, *spe;
  char buf1[bufsize], buf2[bufsize];

if (comp == 't')
   {
   comp = '=';
   }

Debug1("InstallProcessItem(%s,%s,%d,%d,%c)\n",expr,restart,matches,signal,action);

bzero(buf1,bufsize);
bzero(buf2,bufsize);

ExpandVarstring(expr,buf1,"");
ExpandVarstring(restart,buf2,"");

if ( ! IsInstallable(CLASSBUFF))
   {
   Debug1("Not installing process item, no match\n");
   return;
   }

if ((ptr = (struct Process *)malloc(sizeof(struct Process))) == NULL)
   {
   FatalError("Memory Allocation failed for InstallProcItem() #1");
   }

if ((sp1 = malloc(strlen(buf1)+1)) == NULL)
   {
   FatalError("Memory Allocation failed for InstallProcItem() #2");
   }

if ((sp2 = malloc(strlen(buf2)+1)) == NULL)
   {
   FatalError("Memory Allocation failed for InstallProcItem() #3");
   }

if ((spe = malloc(strlen(CLASSBUFF)+1)) == NULL)
   {
   FatalError("Memory Allocation failed for InstallProcItem() #4");
   }


if (VPROCTOP == NULL)                 /* First element in the list */
   {
   VPROCLIST = ptr;
   }
else
   {
   VPROCTOP->next = ptr;
   }

strcpy (sp1,buf1);
strcpy (sp2,buf2);
strcpy (spe,CLASSBUFF);

ptr->classes = spe;
ptr->expr = sp1;
ptr->restart = sp2;
ptr->matches = matches;
ptr->comp = comp;
ptr->signal = signal;
ptr->action = action;
ptr->next = NULL;
VPROCTOP = ptr;
InitializeAction();
}

/*******************************************************************/

InstallImageItem(path,plus,minus,destination,action,uidnames,gidnames,
		 force,size,comp,backup,rec,type,lntype,server,classbuffer)

char *path, *destination, *action, *server, *classbuffer;
mode_t plus,minus;
char *uidnames;
char *gidnames;
short force;
short backup;
char type, lntype, comp;
int rec, size;

{ struct Image *ptr;
  char *sp, *spe, *spd, *spa, *spl, *sps, *spcl = NULL;
  char buf1[bufsize], buf2[bufsize], buf3[bufsize], buf4[bufsize];
  struct TwoDimList *tp = NULL;
  int i;
  struct hostent *hp;

if ( ! IsInstallable(CLASSBUFF))
   {
   Debug1("Not installing copy item, no match\n");
   return;
   }
  
Debug1("InstallImageItem (%s) (+%o)(-%o) (%s)\n",path,plus,minus,destination);

if (strlen(destination) == 0)
   {
   yyerror("No destination specified in file image declaration");
   return;
   }

if (strlen(action) == 0)   /* default action */
   {
   strcat(action,"fix");
   }

if (!(strcmp(action,"silent") == 0 || strcmp(action,"warn") == 0 || strcmp(action,"fix") == 0))
   {
   sprintf(VBUFF,"Illegal action in image/copy item: %s",action);
   yyerror(VBUFF);
   return;
   }

bzero(buf1,bufsize);
bzero(buf2,bufsize);
bzero(buf3,bufsize);
bzero(buf4,bufsize);

ExpandVarstring(path,buf1,"");
ExpandVarstring(destination,buf2,"");
ExpandVarstring(server,buf3,"");
ExpandVarstring(classbuffer,buf4,"");

if (strlen(buf1) > 1)
   {
   DeleteSlash(buf1);
   }

if (strlen(buf2) > 1)
   {
   DeleteSlash(buf2);
   }

if (buf2[0] != '/')
   {
   if (strncmp(buf2,"home",4) == 0)
      {
      if (strlen(buf2) > 4 && buf2[4] != '/')
         {
         yyerror("illegal use of home or not absolute pathname");
         return;
         }
      }
   else
      {
      yyerror("Image needs an absolute pathname");
      return;
      }
   }

if ((strcmp(buf3,VFQNAME) == 0) || (strcmp(buf3,VUQNAME) == 0) || (strcmp(buf3,VSYSNAME.nodename) == 0))
   {
   Verbose("Swapping %s for localhost\n",server);
   strcpy(buf3,"localhost");
   }

Build2DListFromVarstring(&tp,path,'/');  /* Must split on space in comm string */
    
Set2DList(tp);


if ((ptr = (struct Image *)malloc(sizeof(struct Image))) == NULL)
   {
   FatalError("Memory Allocation failed for InstallImageItem() #1");
   }

if ((spe = malloc(strlen(CLASSBUFF)+1)) == NULL)
   {
   FatalError("Memory Allocation failed for InstallImageItem() #3");
   }

if ((spd = malloc(strlen(buf2)+1)) == NULL)
   {
   FatalError("Memory Allocation failed for InstallImageItem() #4");
   }

if ((sps = malloc(strlen(buf3)+1)) == NULL)
   {
   FatalError("Memory Allocation failed for InstallImageItem() #5");
   }

if ((spa = malloc(strlen(action)+1)) == NULL)
   {
   FatalError("Memory Allocation failed for InstallImageItem() #6");
   }

if ((spcl = malloc(strlen(buf4)+1)) == NULL)
   {
   FatalError("Memory Allocation failed for InstallImageItem() #7");
   }

for (spl = Get2DListEnt(tp); spl != NULL; spl = Get2DListEnt(tp))
   {
   if ((strcmp(spl,buf2) == 0) && (strcmp(buf3,"localhost") == 0))
      {
      yyerror("image loop: file/dir copies to itself");
      return;
      }

   if ((sp = malloc(strlen(spl)+1)) == NULL)
      {
      FatalError("Memory Allocation failed for InstallImageItem() #2");
      }

   if (VIMAGETOP == NULL)
      {
      VIMAGE = ptr;
      }
   else
      {
      VIMAGETOP->next = ptr;
      }

   strcpy (sp,spl);
   strcpy (spe,CLASSBUFF);
   strcpy (spd,buf2);
   strcpy (spa,action);
   strcpy (sps,buf3);
   strcpy (spcl,buf4);

   ptr->classes = spe;
   ptr->server = sps;
   ptr->path = sp;
   ptr->destination = spd;
   ptr->action=spa;
   ptr->plus = plus;
   ptr->minus = minus;
   ptr->uid = MakeUidList(uidnames);
   ptr->gid = MakeGidList(gidnames);
   ptr->force = force;
   ptr->next = NULL;
   ptr->backup = backup;
   ptr->recurse = rec;
   ptr->type = type;
   ptr->size = size;
   ptr->comp = comp;
   ptr->linktype = lntype;
   ptr->symlink = VCPLNPARSE;
   ptr->exclusions = VEXCLUDEPARSE;
   ptr->inclusions = VINCLUDEPARSE;
   ptr->defines = spcl;
   ptr->cache = NULL;

   if (strcmp(server,"localhost") != 0)
      {
      if ((hp = gethostbyname(server)) == NULL)
	 {
	 yyerror("DNS lookup failure. Unknown host");
	 perror("gethostbyname: ");
	 exit(1);
	 }
      
      ptr->dns = (struct hostent *) malloc(sizeof(struct hostent));

      bcopy(hp,ptr->dns,sizeof(struct hostent));
      }
   
   ptr->inode_cache = NULL;
   
   VIMAGETOP = ptr;

   for (sp = ptr->defines; *sp != '\0'; sp++)
      {
      bzero(buf1,bufsize);
      sscanf(sp,"%[^,:.]",buf1);
      sp += strlen(buf1);
   
      if (! IsItemIn(VALLADDCLASSES,buf1))
         {
         AppendItem(&VALLADDCLASSES,buf1,NULL);
         }
      }
   }

/* Add to possible classes so actions will be installed */

   
Delete2DList(tp);

InitializeAction();
}

/*******************************************************************/

InstallAuthItem(path,hostname,list,listtop,classes)

char *path, *hostname, *classes;
struct Auth **list, **listtop;

{ struct TwoDimList *tp = NULL;
  char *sp;

Debug1("InstallAuthItem(%s,%s)\n",path,hostname);

Build2DListFromVarstring(&tp,path,'/');
   
Set2DList(tp);

for (sp = Get2DListEnt(tp); sp != NULL; sp = Get2DListEnt(tp))
   {
   if (AuthPathExists(sp,*list))
      {
      AddAuthHostItem(sp,hostname,classes,list,listtop);
      }
   else
      {
      InstallAuthPath(sp,hostname,classes,list,listtop);
      }
   }

Delete2DList(tp);
}

/*******************************************************************/

GetCommAttribute(s)

char *s;

{ int i;
  char comm[maxvarsize];

for (i = 0; s[i] != '\0'; i++)
   {
   s[i] = ToLower(s[i]);
   }

comm[0]='\0';

sscanf(s,"%[^=]",comm);

Debug1("GetCommAttribute(%s)\n",comm);

for (i = 0; COMMATTRIBUTES[i] != NULL; i++)
   {
   if (strncmp(COMMATTRIBUTES[i],comm,strlen(comm)) == 0)
      {
      Debug1("GetCommAttribute - got: %s\n",COMMATTRIBUTES[i]);
      return i;
      }
   }

return cfbad;
}

/*******************************************************************/

HandleRecurse(value)

char *value;

{ int n = -1;

if (strcmp(value,"inf") == 0)
   {
   VRECURSE = INFINITERECURSE;
   }
else
   {
   if (strncmp(CURRENTPATH,"home",4) == 0)
      {
      yyerror("Recursion is always infinite for home");
      return;
      }

   sscanf(value,"%d",&n);

   if (n == -1)
      {
      yyerror("Illegal recursion specifier");
      }
   else
      {
      VRECURSE = n;
      }
   }
}

/*******************************************************************/

HandleCopyType(value)

char *value;

{
if (strcmp(value,"ctime") == 0)
   {
   Debug1("Set copy by ctime\n");
   COPYTYPE = 't';
   return;
   }
else if (strcmp(value,"checksum")==0 || strcmp(value,"sum") == 0)
   {
   Debug1("Set copy by md5 checksum\n");
   COPYTYPE = 'c';
   return;
   }
else if (strcmp(value,"byte")==0 || strcmp(value,"binary") == 0)
   {
   Debug1("Set copy by byte comaprison\n");
   COPYTYPE = 'b';
   return;
   }
yyerror("Illegal copy type");
}

/*******************************************************************/

HandleDisableFileType(value)

char *value;

{
if (strlen(CURRENTITEM) != 0)
   {
   Warning("Redefinition of filetype in disable");
   }

if (strcmp(value,"link") == 0 || strcmp(value,"links") == 0)
   {
   strcpy(CURRENTITEM,"link");
   }
else if (strcmp(value,"plain") == 0 || strcmp(value,"file") == 0)
   {
   strcpy(CURRENTITEM,"file");
   }
else
   {
   yyerror("Disable filetype unknown");
   }
}

/*******************************************************************/

HandleDisableRotate(value)

char *value;

{ int num = 0;

if (strcmp(value,"empty") == 0 || strcmp(value,"truncate") == 0)
   {
   ROTATE = CF_TRUNCATE;
   }
else
   {
   sscanf(value,"%d",&num);

   if (num == 0)
      {
      yyerror("disable/rotate value must be a decimal number greater than zero or keyword empty");
      }

   if (! SILENT && num > 99)
      {
      Warning("rotate value looks silly");
      }

   ROTATE = (short) num;
   }
}

/*******************************************************************/

HandleAge(days)

char *days;

{ 
sscanf(days,"%d",&VAGE);
Debug1("HandleAge(%d)\n",VAGE);
}

/*******************************************************************/

HandleProcessMatches(value)

char *value;

{ int i = 0;

switch (*value)
   {
   case '>': PROCOMP = '>';
             value++;
             break;
   case '<': PROCOMP = '<';
             value++;
             break;
   default : PROCOMP = '=';
   }

sscanf(value,"%d",&i);

if (i < 1)
   {
   yyerror("matches attribute with silly value (must be > 0)");
   }

PROMATCHES = (short) i;
}

/*******************************************************************/

HandleProcessSignal(value)

char *value;

{ int i;
  char *sp;

for (i = 1; SIGNALS[i] != 0; i++)
   {
   for (sp = value; *sp != '\0'; sp++)
      {
      *sp = ToUpper(*sp);
      }
   
   if (strcmp(SIGNALS[i]+3,value) == 0)  /* 3 to cut off "sig" */
      {
      PROSIGNAL = (short) i;
      return;
      }
   }

i = 0;

sscanf(value,"%d",&i);

if (i < 1 && i > highest_signal)
   {
   yyerror("Unknown signal in attribute");
   }

PROSIGNAL = (short) i;
}

/*******************************************************************/

AppendToActionSequence (action)

char *action;

{ int j = 0;
  char *sp,cbuff[bufsize],actiontxt[bufsize];

Debug1("Installing item (%s) in the action sequence list\n",action);

AppendItem(&VACTIONSEQ,action,NULL);

cbuff[0]='\0';
actiontxt[0]='\0';
sp = action;

while (*sp != '\0')
   {
   ++j;
   sscanf(sp,"%[^.]",cbuff);

   while ((*sp != '\0') && (*sp !='.'))
      {
      sp++;
      }
 
   if (*sp == '.')
      {
      sp++;
      }
 
   if (IsHardClass(cbuff))
      {
      yyerror("Error in action sequence: %s\n",action);
      yyerror("You cannot add a reserved class!");
      return;
      }
 
   if (j == 1)
      {
      strcpy(actiontxt,cbuff);
      continue;
      }
   else if ((!IsSpecialClass(cbuff)) && (!IsItemIn(VALLADDCLASSES,cbuff)))
      {
      AppendItem(&VALLADDCLASSES,cbuff,NULL);
      }
   }
}

/*******************************************************************/

AppendToAccessList (user)

char *user;

{ char id[maxvarsize];
  struct passwd *pw;

Debug1("Adding to access list for %s\n",user);

if (isalpha(user[0]))
   {
   if ((pw = getpwnam(user)) == NULL)
      {
      yyerror("No such user in password database");
      return;
      }

   sprintf(id,"%d",pw->pw_uid);
   AppendItem(&VACCESSLIST,id,NULL);
   }
else
   {
   AppendItem(&VACCESSLIST,user,NULL);
   }
}

/*******************************************************************/

HandleLinkAction(value)

char *value;

{
if (strcmp(value,"silent") == 0)
   {
   LINKSILENT = true;
   return;
   }

yyerror("Invalid link action");
}

/*******************************************************************/

HandleLinkType(value)

char *value;

{
if (strcmp(value,"hard") == 0)
   {
   if (ACTION_IS_LINKCHILDREN)
      {
      yyerror("hard links inappropriate for multiple linkage");
      }

   if (ACTION == image)
      {
      yyerror("hard links inappropriate for copy operation");
      }

   LINKTYPE = 'h';
   return;
   }

if (strcmp(value,"symbolic") == 0 || strcmp(value,"sym") == 0)
   {
   LINKTYPE = 's';
   return;
   }

if (strcmp(value,"abs") == 0 || strcmp(value,"absolute") == 0)
   {
   LINKTYPE = 'a';
   return;
   }

if (strcmp(value,"rel") == 0 || strcmp(value,"relative") == 0)
   {
   LINKTYPE = 'r';
   return;
   }
yyerror("Invalid link type");
}

/*******************************************************************/

HandleServer(value)

char *value;

{
Debug("Server in copy set to : %s\n",value);
strcpy(CFSERVER,value);
}

/*******************************************************************/

HandleDefine(value)

char *value;

{
Debug("Additional classes on copying: %s\n",value);

if (strlen(value) > bufsize)
   {
   yyerror("class list too long in copy action - can't handle it!");
   }

strcpy(ALLCLASSBUFFER,value);
}


/*******************************************************************/
/* Level 4                                                         */
/*******************************************************************/

struct UidList *MakeUidList(uidnames)

char *uidnames;

{ struct UidList *uidlist;
  struct Item *ip, *tmplist;
  char uidbuff[bufsize];
  char *sp;
  int offset;
  struct passwd *pw;
  char *machine, *user, *domain;
  int uid;
  int tmp;

uidlist = NULL;

for (sp = uidnames; *sp != '\0'; sp+=strlen(uidbuff))
   {
   if (*sp == ',')
      {
      sp++;
      }

   if (sscanf(sp,"%[^,]",uidbuff))
      {
      if (uidbuff[0] == '+')        /* NIS group - have to do this in a roundabout     */
         {                          /* way because calling getpwnam spoils getnetgrent */
         offset = 1;
         if (uidbuff[1] == '@')
            {
            offset++;
            }

         setnetgrent(uidbuff+offset);
         tmplist = NULL;

         while (getnetgrent(&machine,&user,&domain))
            {
            if (user != NULL)
               {
               AppendItem(&tmplist,user,NULL);
               }
            }
                   
         endnetgrent();

         for (ip = tmplist; ip != NULL; ip=ip->next)
            {
            if ((pw = getpwnam(ip->name)) == NULL)
               {
               if (VERBOSE || DEBUG || D1)
                  {
                  sprintf(CURRENTITEM,"Unknown user [%s]\n",ip->name);
                  Warning(CURRENTITEM);
                  sprintf(CURRENTITEM,"User in netgroup [%s] not in passwd file\n",uidbuff+offset);
                  Warning(CURRENTITEM);
                  }
               continue;
               }
            else
               {
               uid = pw->pw_uid;
               }
            AddSimpleUidItem(&uidlist,uid); 
            }

         DeleteItemList(tmplist);
         continue;
         }

      if (isdigit(uidbuff[0]))
         {
         sscanf(uidbuff,"%d",&tmp);
         uid = (uid_t)tmp;
         }
      else
         {
         if (strcmp(uidbuff,"*") == 0)
            {
            uid = -1;                     /* signals wildcard */
            }
         else if ((pw = getpwnam(uidbuff)) == NULL)
            {
            printf("Unknown user %s\n",uidbuff);
            Warning("User is not known in this passwd domain");
            continue;
            }
         else
            {
            uid = pw->pw_uid;
            }
         }
      AddSimpleUidItem(&uidlist,uid);
      }
   }

return (uidlist);
}

/*********************************************************************/

struct GidList *MakeGidList(gidnames)

char *gidnames;

{ struct GidList *gidlist;
  char gidbuff[bufsize];
  char err[bufsize];
  char *sp;
  struct group *gr;
  int gid;
  int tmp;

gidlist = NULL;

for (sp = gidnames; *sp != '\0'; sp+=strlen(gidbuff))
   {
   if (*sp == ',')
      {
      sp++;
      }

   if (sscanf(sp,"%[^,]",gidbuff))
      {
      if (isdigit(gidbuff[0]))
         {
         sscanf(gidbuff,"%d",&tmp);
         gid = (gid_t)tmp;
         }
      else
         {
         if (strcmp(gidbuff,"*") == 0)
            {
            gid = -1;                     /* signals wildcard */
            }
         else if ((gr = getgrnam(gidbuff)) == NULL)
            {
            sprintf(err,"Unknown group %s\n",gidbuff);
            Warning(err);
            continue;
            }
         else
            {
            gid = gr->gr_gid;
            }
         }
      AddSimpleGidItem(&gidlist,gid);
      }
   }


return(gidlist);
}


/*******************************************************************/


InstallTidyPath(path,wild,rec,age,travlinks,tidysize,type,ldirs,tidydirs,classes)

char *wild, *path;
short age,tidydirs;
int rec, travlinks,tidysize;
char type, ldirs, *classes;

{ struct Tidy *ptr;
  char *pp, *sp;
  int no_of_links = 0;

if (!IsInstallable(classes))
   {
   Debug1("Not installing tidy path, no match\n");
   return;
   }

Debug1("InstallTidyPath(%s,%s,%d,%d,%c,%c,%d)\n",path,wild,rec,age,travlinks,type,ldirs,tidydirs);

VBUFF[0]='\0';                                /* Expand any variables */
ExpandVarstring(path,VBUFF,"");

if (strlen(VBUFF) != 1)
   {
   DeleteSlash(VBUFF);
   }

if ((ptr = (struct Tidy *)malloc(sizeof(struct Tidy))) == NULL)
   {
   FatalError("Memory Allocation failed for InstallTidyItem() #1");
   }

if ((pp = malloc(strlen(VBUFF)+1)) == NULL)
   {
   FatalError("Memory Allocation failed for InstallTidyItem() #3");
   }

if (VTIDYTOP == NULL)                 /* First element in the list */
   {
   VTIDY = ptr;
   }
else
   {
   VTIDYTOP->next = ptr;
   }

if (rec != INFINITERECURSE && strncmp("home/",pp,5) == 0)     /* Is this a subdir of home wildcard? */
   {
   for (sp = pp; *sp != '\0'; sp++)                     /* Need to make recursion relative to start */
      {                                                    /* of the search, not relative to home */
      if (*sp == '/')
         {
         no_of_links++;
         }
      }
   }

strcpy (pp,VBUFF);

ptr->tidylist = NULL;
ptr->path = pp;
ptr->recurse = rec + no_of_links;
ptr->next = NULL;
VTIDYTOP = ptr;

AddTidyItem(path,wild,rec,age,travlinks,tidysize,type,ldirs,tidydirs,classes);

InitializeAction();
}

/*********************************************************************/

AddTidyItem(path,wild,rec,age,travlinks,tidysize,type,ldirs,tidydirs,classes)

char *wild, *path;
short age,tidysize,tidydirs;
int rec, travlinks;
char type, ldirs,*classes;

{ char varbuff[bufsize];
  struct Tidy *ptr;

Debug1("AddTidyItem(%s,pat=%s)\n",path,wild);

if ( ! IsInstallable(CLASSBUFF))
   {
   Debug1("Not installing TidyItem no match\n");
   return;
   }

for (ptr = VTIDY; ptr != NULL; ptr=ptr->next)
   {
   varbuff[0] = '\0';
   ExpandVarstring(path,varbuff,"");

   if (strcmp(ptr->path,varbuff) == 0)
      {
      PrependTidy(&(ptr->tidylist),wild,rec,age,travlinks,tidysize,type,ldirs,tidydirs,classes);
      
      /* Must have the maximum recursion level here */
      
      if (rec == INFINITERECURSE || (ptr->recurse < rec && ptr->recurse != INFINITERECURSE))
	 {
         ptr->recurse = rec;
	 }
      return;
      }
   }
}

/*********************************************************************/

TidyPathExists(path)

char *path;

{ struct Tidy *tp;

VBUFF[0]='\0';                                /* Expand any variables */
ExpandVarstring(path,VBUFF,"");

for (tp = VTIDY; tp != NULL; tp=tp->next)
   {
   if (strcmp(tp->path,path) == 0)
      {
      Debug1("TidyPathExists(%s)\n",path);
      return true;
      }
   }

return false;
}


/*******************************************************************/
/* Level 5                                                         */
/*******************************************************************/

AddSimpleUidItem(uidlist,uid)

struct UidList **uidlist;
int uid;

{ struct UidList *ulp, *u;

if ((ulp = (struct UidList *)malloc(sizeof(struct UidList))) == NULL)
   {
   FatalError("cfengine: malloc() failed #1 in AddSimpleUidItem()");
   }

ulp->uid = uid;
ulp->next = NULL;

if (*uidlist == NULL)
   {
   *uidlist = ulp;
   }
else
   {
   for (u = *uidlist; u->next != NULL; u = u->next)
      {
      }
   u->next = ulp;
   }
}

/*******************************************************************/

AddSimpleGidItem(gidlist,gid)

struct GidList **gidlist;
int gid;

{ struct GidList *glp,*g;

if ((glp = (struct GidList *)malloc(sizeof(struct GidList))) == NULL)
   {
   FatalError("cfengine: malloc() failed #1 in AddSimpleGidItem()");
   }
 
glp->gid = gid;
glp->next = NULL;

if (*gidlist == NULL)
   {
   *gidlist = glp;
   }
else
   {
   for (g = *gidlist; g->next != NULL; g = g->next)
      {
      }
   g->next = glp;
   }

}


/***********************************************************************/

InstallAuthPath(path,hostname,classes,list,listtop)

char *path, *hostname, *classes;
struct Auth **list, **listtop;

{ struct Auth *ptr;
  char *pp;

if (!IsInstallable(classes))
   {
   Debug1("Not installing Auth path, no match\n");
   return;
   }

Debug1("InstallAuthPath(%s,%s)\n",path,hostname);

VBUFF[0]='\0';                                /* Expand any variables */
ExpandVarstring(path,VBUFF,"");

if (strlen(VBUFF) != 1)
   {
   DeleteSlash(VBUFF);
   }

if ((ptr = (struct Auth *)malloc(sizeof(struct Auth))) == NULL)
   {
   FatalError("Memory Allocation failed for InstallAuthPath() #1");
   }

if ((pp = malloc(strlen(VBUFF)+1)) == NULL)
   {
   FatalError("Memory Allocation failed for InstallAuthPath() #3");
   }

if (*listtop == NULL)                 /* First element in the list */
   {
   *list = ptr;
   }
else
   {
   (*listtop)->next = ptr;
   }

strcpy (pp,VBUFF);

ptr->accesslist = NULL;
ptr->path = pp;
ptr->next = NULL;
*listtop = ptr;

AddAuthHostItem(pp,hostname,classes,list);
}

/***********************************************************************/

AddAuthHostItem(path,hostname,classes,list)

char *path, *hostname, *classes;
struct Auth **list;

{ char varbuff[bufsize];
  struct Auth *ptr;

Debug1("AppendAuthHostItem(%s,%s)\n",path,hostname);

if ( ! IsInstallable(CLASSBUFF))
   {
   Debug1("Not installing TidyItem no match\n");
   return;
   }

for (ptr = *list; ptr != NULL; ptr=ptr->next)
   {
   varbuff[0] = '\0';
   ExpandVarstring(path,varbuff,"");

   if (strcmp(ptr->path,varbuff) == 0)
      {
      PrependItem(&(ptr->accesslist),hostname,classes);
      return;
      }
   }
}


/*********************************************************************/

AuthPathExists(path,list)

char *path;
struct Auth *list;

{ struct Auth *ap;

Debug1("AuthPathExists(%s)\n",path);

VBUFF[0]='\0';                                /* Expand any variables */
ExpandVarstring(path,VBUFF,"");

if (VBUFF[0] != '/')
   {
   yyerror("Missing absolute path to a directory");
   FatalError("Cannot continue");
   }

for (ap = list; ap != NULL; ap=ap->next)
   {
   if (strcmp(ap->path,VBUFF) == 0)
      {
      return true;
      }
   }

return false;
}

/*********************************************************************/

PrependTidy(list,wild,rec,age,travlinks,tidysize,type,ldirs,tidydirs,classes)

struct TidyPattern **list;
char *wild;
short age,tidysize,tidydirs;
int rec, travlinks;
char type, ldirs,*classes;

{ struct TidyPattern *tp;
  char *spe,*sp;

if ((tp = (struct TidyPattern *)malloc(sizeof(struct TidyPattern))) == NULL)
   {
   perror("Can't allocate memory in PrependTidy()");
   FatalError("");
   }

if ((sp = malloc(strlen(wild)+1)) == NULL)
   {
   FatalError("Memory Allocation failed for PrependTidy() #2");
   }

if ((classes!= NULL) && (spe = malloc(strlen(classes)+2)) == NULL)
   {
   perror("Can't allocate memory in PrependItem()");
   FatalError("");
   }

strcpy(sp,wild);

tp->size = tidysize;
tp->recurse = rec;
tp->age = age;
tp->searchtype = type;
tp->travlinks = travlinks;
tp->pattern = sp;
tp->next = *list;
tp->dirlinks = ldirs;

if (tidydirs)
   {
   tp->rmdirs = 't';
   }
else
   {
   tp->rmdirs = 'f';
   }

*list = tp;

if (classes != NULL)
   {
   strcpy(spe,classes);
   tp->classes = spe;
   }
else
   {
   tp->classes = NULL;
   }
}

/* EOF */
