/*                                                                           */
/*  * Copyright (c) 1989, 1990, 1991 by the University of Washington         */
/*  *                                                                        */
/*  * For copying and distribution information, please see the file          */
/*  * <copyright.h>.                                                         */

/*  * Miscellaneous routines pulled from ~beta/lib/pfs and ~beta/lib/filters */

#include "pmachine.h"
#include <sys/file.h>

#include "pfs.h"
#include "pprot.h"
#include "perrno.h"
#include "pcompat.h"
#include "pauthent.h"
#include "regex.h"

int	pfs_enable = PMAP_ATSIGN;

/*                                                                           */
/*  * wcmatch - Match string s against template containing widlcards         */
/*  *                                                                        */
/*  * WCMATCH takes a string and a template, and returns                     */
/*  * true if the string matches the template, and                           */
/*  * FALSE otherwise.                                                       */
/*  *                                                                        */
/*  * ARGS: s - string to be tested                                          */
/*  * template - Template containing optional wildcards                      */
/*  *                                                                        */
/*  * RETURNS: TRUE (non-zero) on match.  FALSE (0) otherwise.               */
/*  *                                                                        */
/*  * NOTE: If template is NULL, will return TRUE.                           */
/*  *                                                                        */
int
wcmatch(s,template)
    char	*s;
    char	*template;
    {
	char	temp[200];
	char	*p = temp;

	if(!template) return(TRUE);
	*p++ = '^';

	while(*template) {
	    if(*template == '*') {*(p++)='.'; *(p++) = *(template++);}
	    else if(*template == '?') {*(p++)='.';template++;}
	    else if(*template == '.') {*(p++)='\\';*(p++)='.';template++;}
	    else if(*template == '[') {*(p++)='\\';*(p++)='[';template++;}
	    else if(*template == '$') {*(p++)='\\';*(p++)='$';template++;}
	    else if(*template == '^') {*(p++)='\\';*(p++)='^';template++;}
	    else if(*template == '\\') {*(p++)='\\';*(p++)='\\';template++;}
	    else *(p++) = *(template++);
	}
	    
	*p++ = '$';
	*p++ = '\0';

	if(re_comp(temp)) return(FALSE);

#ifdef AUX
	if (re_exec(s) == (char *)NULL)
	  return 0;
	return 1;
#else
	return(re_exec(s));
#endif
    }

/*                                                                           */
/*  * ul_insert - Insert a union link at the right location                  */
/*  *                                                                        */
/*  * UL_INSERT takes a directory and a union link to be added               */
/*  * to a the list of union links in the directory.  It then                */
/*  * inserts the union link in the right spot in the linked                 */
/*  * list of union links associated with that directory.                    */
/*  *                                                                        */
/*  * If an identical link already exists, then the link which               */
/*  * would be evaluated earlier (closer to the front of the list)           */
/*  * wins and the other one is freed.  If this happens, an error            */
/*  * will also be returned.                                                 */
/*  *                                                                        */
/*  * ARGS: ul - link to be inserted                                         */
/*  * vd - directory to get link                                             */
/*  * p - vl that this link will apper after                                 */
/*  * NULL - This vl will go at end of list                                  */
/*  * vd - This vl will go at head of list                                   */
/*  *                                                                        */
/*  * RETURNS: Success, or UL_INSERT_ALREADY_THERE or UL_INSERT_SUPERSEDING  */
int
ul_insert(ul,vd,p)
    VLINK	ul;		/* Link to be inserted                       */
    PVDIR	vd;		/* Directory to receive link                 */
    VLINK	p;		/* Union link to appear prior to new one     */
    {
	VLINK	current;

	/* This is the first ul in the directory                             */
	if(vd->ulinks == NULL) {
	    vd->ulinks = ul;
	    ul->previous = NULL;
	    ul->next = NULL;
	    return(PSUCCESS);
	}

	/* This ul will go at the head of the list                           */
	if(p == (VLINK) vd) {
	    ul->next = vd->ulinks;
	    ul->next->previous = ul;
	    vd->ulinks = ul;
	    ul->previous = NULL;
	}
	/* Otherwise, decide if it must be inserted at all If an identical   */
	/* link appears before the position at which the new one is to be    */
	/* inserted, we can return without inserting it                      */
	else {
	    current = vd->ulinks;

	    while(current) {
		/* p == NULL means we insert after last link                 */
		if(!p && (current->next == NULL))
		    p = current;

		if(vl_comp(current,ul) == 0) {
		    vlfree(ul);
		    return(UL_INSERT_ALREADY_THERE);
		}

		if(current == p) break;
		current = current->next;
	    }

	    /* If current is null, p was not found                           */
	    if(current == NULL)
		return(UL_INSERT_POS_NOTFOUND);

	    /* Insert ul                                                     */
	    ul->next = p->next;
	    p->next = ul;
	    ul->previous = p;
	    if(ul->next) ul->next->previous = ul;
	}

	/* Check for identical links after ul                                */
	current = ul->next;

	while(current) {
	    if(vl_comp(current,ul) == 0) {
		current->previous->next = current->next;
		if(current->next)
		    current->next->previous = current->previous;
		vlfree(current);
		return(UL_INSERT_SUPERSEDING);
	    }
	    current = current->next;
	}
	
	return(PSUCCESS);
    }

/*                                                                           */
/*  * vl_insert - Insert a directory link at the right location              */
/*  *                                                                        */
/*  * VL_INSERT takes a directory and a link to be added to a                */
/*  * directory and inserts it in the linked list of links for               */
/*  * that directory.                                                        */
/*  *                                                                        */
/*  * If a link already exists with the same name, and if the                */
/*  * information associated with the new link matches that in               */
/*  * the existing link, an error is returned.  If the information           */
/*  * associated with the new link is different, but the magic numbers       */
/*  * match, then the new link will be added as a replica of the             */
/*  * existing link.  If the magic numbers do not match, the new             */
/*  * link will only be added to the list of "replicas" if the               */
/*  * allow_conflict flag has been set.                                      */
/*  *                                                                        */
/*  * If the link is not added, an error is returned and the link            */
/*  * is freed.  Ordering for the list of links is by the link name.         */
/*  *                                                                        */
/*  * If vl is a union link, then VL_INSERT calls ul_insert with an          */
/*  * added argument indicating the link is to be included at the            */
/*  * end of the union link list.                                            */
/*  *                                                                        */
/*  * ARGS: vl - Link to be inserted, vd - directory to get link             */
/*  * allow_conflict - insert links with conflicting names                   */
/*  *                                                                        */
/*  * RETURNS: Success, or VL_INSERT_ALREADY_THERE                           */
int
vl_insert(vl,vd,allow_conflict)
    VLINK	vl;		/* Link to be inserted                       */
    PVDIR	vd;		/* Directory to receive link                 */
    int		allow_conflict;	/* Allow duplicate names                     */
    {
	VLINK	current;	/* To step through list                      */
	VLINK	crep;		/* To step through list of replicas          */
	int	retval;		/* Temp for checking returned values         */

	/* This can also be used to insert union links at end of list        */
	if(vl->linktype == 'U') return(ul_insert(vl,vd,NULL));

	/* If this is the first link in the directory                        */
	if(vd->links == NULL) {
	    vd->links = vl;
	    vl->previous = NULL;
	    vl->next = NULL;
	    vd->lastlink = vl;
	    return(PSUCCESS);
	}

	/* If no sorting is to be done, just insert at end of list           */
	if(allow_conflict == VLI_NOSORT) {
	    vd->lastlink->next = vl;
	    vl->previous = vd->lastlink;
	    vl->next = NULL;
	    vd->lastlink = vl;
	    return(PSUCCESS);
	}

	/* If it is to be inserted at start of list                          */
	if(vl_comp(vl,vd->links) < 0) {
	    vl->next = vd->links;
	    vl->previous = NULL;
	    vl->next->previous = vl;
	    vd->links = vl;
	    return(PSUCCESS);
	}

	current = vd->links;

	/* Otherwise, we must find the right spot to insert it               */
	while((retval = vl_comp(vl,current)) > 0) {
	    if(!current->next) {
		/* insert at end */
		vl->previous = current;
		vl->next = NULL;
		current->next = vl;
		vd->lastlink = vl;
		return(PSUCCESS);
	    }
	    current = current->next;
	}

	/* If we found an equivilant entry already in list                   */
	if(!retval) {
	    if(vl_equal(vl,current)) {
		vlfree(vl);
		return(VL_INSERT_ALREADY_THERE);
	    }
	    if((allow_conflict == VLI_NOCONFLICT) &&
	       ((vl->f_magic_no != current->f_magic_no) ||
		(vl->f_magic_no==0)))
		return(VL_INSERT_CONFLICT);
	    /* Insert the link into the list of "replicas" If magic is 0,    */
	    /* then create a pseudo magic number                             */
	    if(vl->f_magic_no == 0) vl->f_magic_no = -1;
	    crep = current->replicas;
	    if(!crep) {
		current->replicas = vl;
		vl->next = NULL;
		vl->previous = NULL;
	    }
	    else {
		while(crep->next) {
		    /* If magic was 0, then we need a unique magic number    */
		    if((crep->f_magic_no < 0) && (vl->f_magic_no < 1))
			(vl->f_magic_no)--;
		    crep = crep->next;
		}
		/* If magic was 0, then we need a unique magic number        */
		if((crep->f_magic_no < 0) && (vl->f_magic_no < 1))
		    (vl->f_magic_no)--;
		crep->next = vl;
		vl->previous = crep;
		vl->next = NULL;
	    }
	    return(PSUCCESS);
	}

	/* We found the spot where vl is to be inserted                      */
	vl->next = current;
	vl->previous = current->previous;
	current->previous = vl;
	vl->previous->next = vl;
	return(PSUCCESS);
    }

/*                                                                           */
/*  * nlsindex - Find first instance of string 2 in string 1 following       */
/* newline                                                                   */
/*  *                                                                        */
/*  * NLSINDEX scans string 1 for the first instance of string               */
/*  * 2 that immediately follows a newline.  If found, NLSINDEX              */
/*  * returns a pointer to the first character of that instance.             */
/*  * If no instance is found, NLSINDEX returns NULL (0).                    */
/*  *                                                                        */
/*  * NOTE: This function is only useful for searching strings that          */
/*  * consist of multiple lines.  s1 is assumed to be preceeded              */
/*  * by a newline.  Thus, if s2 is at the start of s1, it will              */
/*  * be found.                                                              */
/*  * ARGS: s1 - string to be searched                                       */
/*  * s2 - string to be found                                                */
/*  * RETURNS: First instance of s2 in s1, or NULL (0) if not found          */
char *
nlsindex(s1,s2)
    char	*s1;		/* String to be searched                     */
    char	*s2;		/* String to be found                        */
    {
	register int s2len = strlen(s2);
	char	*curline = s1;	/* Pointer to start of current line          */

	/* In case s2 appears at start of s1                                 */
	if(strncmp(curline,s2,s2len) == 0)
	    return(curline);

	/* Check remaining lines of s1                                       */
	while((curline = (char *) index(curline,'\n')) != NULL) {
	    curline++;
	    if(strncmp(curline,s2,s2len) == 0)
		return(curline);
	}

	/* We didn't find it                                                 */
	return(NULL);
    }

/*                                                                           */
/*  * month_sname - Return a month name from it's number                     */
/*  *                                                                        */
/*  * MONTH_SNAME takes a number in the range 0                              */
/*  * to 12 and returns a pointer to a string                                */
/*  * representing the three letter abbreviation                             */
/*  * for that month.  If the argument is out of                             */
/*  * range, MONTH_SNAME returns a pointer to "Unk".                         */
/*  *                                                                        */
/*  * ARGS: n - Number of the month                                          */
/*  * RETURNS: Abbreviation for selected month                               */
char *month_sname(n)
    int n;		/* Month number */
{
    static char *name[] = { "Unk",
        "Jan","Feb","Mar","Apr","May","Jun",
        "Jul","Aug","Sep","Oct","Nov","Dec"
    };
    return((n < 1 || n > 12) ? name[0] : name[n]);
}

/*                                                                           */
/*  * sindex - Find first instance of string 2 in string 1                   */
/*  *                                                                        */
/*  * SINDEX scans string 1 for the first instance of string                 */
/*  * 2.  If found, SINDEX returns a pointer to the first                    */
/*  * character of that instance.  If no instance is found,                  */
/*  * SINDEX returns NULL (0).                                               */
/*  *                                                                        */
/*  * ARGS: s1 - string to be searched                                       */
/*  * s2 - string to be found                                                */
/*  * RETURNS: First instance of s2 in s1, or NULL (0) if not found          */
char *
sindex(s1,s2)
    char	*s1;		/* String to be searched                     */
    char	*s2;		/* String to be found                        */
    {
	register int s2len = strlen(s2);
	char	*s = s1;	/* Temp pointer to string                    */

	/* Check for first character of s2                                   */
	while((s = (char *) index(s,*s2)) != NULL) {
	    if(strncmp(s,s2,s2len) == 0)
		return(s);
	    s++;
	}

	/* We didn't find it                                                 */
	return(NULL);
    }

int
scan_error(erst)
    char	*erst;
    {
	*p_err_string = '\0';

	if(strncmp(erst,"NOT-A-DIRECTORY",15) == 0) 
	    return(DIRSRV_NOT_DIRECTORY);

	if(strncmp(erst,"UNIMPLEMENTED",13) == 0) {
	    perrno = DIRSRV_UNIMPLEMENTED;
	    sscanf(erst+13,"%*[^\n \t\r]%*[ \t]%[^\n]",p_err_string);
	    return(perrno);
	}

	if(strncmp(erst,"WARNING ",8) == 0) {
	    erst += 8;
	    *p_warn_string = '\0';
	    sscanf(erst,"%*[^\n \t\r]%*[ \t]%[^\n]",p_warn_string);
	    /* Return values for warnings are negative */
	    if(strncmp(erst,"OUT-OF-DATE",11) == 0) {
		pwarn = PWARN_OUT_OF_DATE;
		return(PSUCCESS);
	    }
	    if(strncmp(erst,"MESSAGE",7) == 0) {
		pwarn = PWARN_MSG_FROM_SERVER;
		return(PSUCCESS);
	    }
	    pwarn = PWARNING;
	    sscanf(erst,"%[^\n]",p_warn_string);
	    return(PSUCCESS);
	}
	else if(strncmp(erst,"ERROR",5) == 0) {
	    if(*(erst+5)) sscanf(erst+6,"%[^\n]",p_err_string);
	    perrno = DIRSRV_ERROR;
	    return(perrno);
	}
	/* The rest start with "FAILURE" */
	else if(strncmp(erst,"FAILURE",7) != 0) {
	    /* Unrecognized - Give warning, but return PSUCCESS */
	    if(pwarn == 0) {
		*p_warn_string = '\0';
		pwarn = PWARN_UNRECOGNIZED_RESP;
		sscanf(erst,"%[^\n]",p_warn_string);
	    }
	    return(PSUCCESS);
	}

	if(strncmp(erst,"FAILURE ",8) != 0) {
	    perrno = PFAILURE;
	    return(perrno);
	}	
	erst += 8;
	
	sscanf(erst,"%*[^\n \t\r]%*[ \t]%[^\n]",p_err_string);

	/* Still to add               */
	/* DIRSRV_AUTHENT_REQ     242 */
	/* DIRSRV_BAD_VERS        245 */

	if(strncmp(erst,"NOT-FOUND",9) == 0) 
	    perrno = DIRSRV_NOT_FOUND;
	else if(strncmp(erst,"NOT-AUTHORIZED",13) == 0) 
	    perrno = DIRSRV_NOT_AUTHORIZED;
	else if(strncmp(erst,"ALREADY-EXISTS",14) == 0) 
	    perrno = DIRSRV_ALREADY_EXISTS;
	else if(strncmp(erst,"NAME-CONFLICT",13) == 0) 
	    perrno = DIRSRV_NAME_CONFLICT;
	else if(strncmp(erst,"SERVER-FAILED",13) == 0) 
	    perrno = DIRSRV_SERVER_FAILED;
 	/* Use it whether it starts with FAILURE or not */
	else if(strncmp(erst,"NOT-A-DIRECTORY",15) == 0) 
	    perrno = DIRSRV_NOT_DIRECTORY;
	else perrno = PFAILURE;

	return(perrno);
    }

PATTRIB 
parse_attribute(line)
    char	*line;
    {
	char	l_precedence[MAX_DIR_LINESIZE];
	char	l_name[MAX_DIR_LINESIZE];
	char	l_type[MAX_DIR_LINESIZE];
	char	l_value[MAX_DIR_LINESIZE];
	PATTRIB	at;
	int	tmp;

	tmp = sscanf(line,"OBJECT-INFO %s %s %[^\n]", l_name, l_type, l_value);
	
	if(tmp < 3) {
	    tmp = sscanf(line,"LINK-INFO %s %s %s %[^\n]", l_precedence,
			 l_name, l_type, l_value);
	    if(tmp < 4) {
		perrno = DIRSRV_BAD_FORMAT;
		return(NULL);
	    }
	}

	at = atalloc();

	if(tmp == 4) {
	    if(strcmp(l_precedence,"CACHED") == 0) 
		at->precedence = ATR_PREC_CACHED;
	    else if(strcmp(l_precedence,"LINK") == 0) 
		at->precedence = ATR_PREC_LINK;
	    else if(strcmp(l_precedence,"REPLACEMENT") == 0) 
		at->precedence = ATR_PREC_REPLACE;
	    else if(strcmp(l_precedence,"ADDITIONAL") == 0) 
		at->precedence = ATR_PREC_ADD;
	}

	at->aname = stcopy(l_name);
	at->avtype = stcopy(l_type);
	if(strcmp(l_type,"ASCII") == 0) 
	    at->value.ascii = stcopy(l_value);
	else if(strcmp(l_type,"LINK") == 0) {
	    char		ftype[MAX_DIR_LINESIZE];
	    char		lname[MAX_DIR_LINESIZE];
	    char		htype[MAX_DIR_LINESIZE];
	    char		host[MAX_DIR_LINESIZE];
	    char		ntype[MAX_DIR_LINESIZE];
	    char		fname[MAX_DIR_LINESIZE];
	    VLINK		al;

	    al = vlalloc();
	    at->value.link = al;

	    tmp = sscanf(l_value,"%c %s %s %s %s %s %s %ld %ld",
			 &(al->linktype),
			 ftype,lname,htype,host,ntype,fname,
			 &(al->version),
			 &(al->f_magic_no));
	    if(tmp == 9) {
		al->type = stcopyr(ftype,al->type);
		al->name = stcopyr(unquote(lname),al->name);
		al->hosttype = stcopyr(htype,al->hosttype);
		al->host = stcopyr(host,al->host);
		al->nametype = stcopyr(ntype,al->nametype);
		al->filename = stcopyr(fname,al->filename);
	    }
	    else {
		perrno = DIRSRV_BAD_FORMAT;
		return(NULL);
	    }
	    
	}

	return(at);
    }

/*                                                                           */
/*  * nxtline - Find the next line in the string                             */
/*  *                                                                        */
/*  * NXTLINE takes a string and returns a pointer to                        */
/*  * the character immediately following the next newline.                  */
/*  *                                                                        */
/*  * ARGS: s - string to be searched                                        */
/*  *                                                                        */
/*  * RETURNS: Next line or NULL (0) on failure                              */
char *
nxtline(s)
    char	*s;		/* String to be searched */
 {
	s = (char *) index(s,'\n');
	if(s) return(++s);
	else return(NULL);
    }


/*                                                                           */
/*  * unquote - unquote string if necessary                                  */
/*  *                                                                        */
/*  * UNQUOTE takes a string and unquotes it if it has been quoted.          */
/*  *                                                                        */
/*  * ARGS: s - string to be unquoted                                        */
/*  *                                                                        */
/*  * RETURNS: The original string.  If the string has been quoted, then the */
/*  * result appears in static storage, and must be copied if                */
/*  * it is to last beyond the next call to quote.                           */
/*  *                                                                        */
char *
unquote(s)
    char	*s;		/* String to be quoted */
    {
	static char	unquoted[200];
	char		*c = unquoted;

	if(*s != '\'') return(s);

	s++;

	/* This should really treat a quote followed by other */
	/* than a quote or a null as an error                 */
	while(*s) {
	    if(*s == '\'') s++;
	    if(*s) *c++ = *s++;
	}

	*c++ = '\0';

	return(unquoted);
    }

#if defined(DEBUG) && !defined(HAVE_STRSPN)
/* needed for -D option parsing                                              */
/*                                                                           */
/*  * strspn - Count initial characters from chrs in s                       */
/*  *                                                                        */
/*  * STRSPN counts the occurances of chacters from chrs                     */
/*  * in the string s preceeding the first occurance of                      */
/*  * a character not in s.                                                  */
/*  *                                                                        */
/*  * ARGS: s - string to be checked                                         */
/*  * chrs - string of characters we are looking for                         */
/*  *                                                                        */
/*  * RETURNS: Count of initial characters from chrs in s                    */
strspn(s,chrs)
    char	*s;    /* String to search                         */
    char	*chrs; /* String of characters we are looking for  */
    {
	char	*cp;   /* Pointer to the current character in chrs */
	int	count; /* Count of characters seen so far          */
	
	count = 0;

	while(*s) {
	    for(cp = chrs;*cp;cp++)
		if(*cp == *s) {
		    s++;
		    count++;
		    goto done;
		}
	    return(count);
	done:
	    ;
	}
	return(count);
    }
#endif

#ifndef HAVE_GETENV
/*                                                                           */
/*  * Copyright (c) 1987 Regents of the University of California.            */
/*  * All rights reserved.                                                   */
/*  *                                                                        */
/*  * Redistribution and use in source and binary forms are permitted        */
/*  * provided that: (1) source distributions retain this entire copyright   */
/*  * notice and comment, and (2) distributions including binaries display   */
/*  * the following acknowledgement: ``This product includes software        */
/*  * developed by the University of California, Berkeley and its            */
/* contributors''                                                            */
/*  * in the documentation or other materials provided with the distribution */
/*  * and in all advertising materials mentioning features or use of this    */
/*  * software. Neither the name of the University nor the names of its      */
/*  * contributors may be used to endorse or promote products derived        */
/*  * from this software without specific prior written permission.          */
/*  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR         */
/*  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED         */
/*  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.    */

#if defined(LIBC_SCCS) && !defined(lint)
static char sccsid[] = "@(#)getenv.c	5.7 (Berkeley) 6/1/90";
#endif /* LIBC_SCCS and not lint */

#include <stdlib.h>
#include <stddef.h>

/*                                                                           */
/*  * getenv --                                                              */
/*  * Returns ptr to value associated with name, if any, else NULL.          */
char *
getenv(name)
	const char *name;
{
	int offset;
	char *_findenv();

	return(_findenv(name, &offset));
}

/*                                                                           */
/*  * _findenv --                                                            */
/*  * Returns pointer to value associated with name, if any, else NULL.      */
/*  * Sets offset to be the offset of the name/value combination in the      */
/*  * environmental array, for use by setenv(3) and unsetenv(3).             */
/*  * Explicitly removes '=' in argument name.                               */
/*  *                                                                        */
/*  * This routine *should* be a static; don't use it.                       */
char *
_findenv(name, offset)
	register const char *name;
	int *offset;
{
	extern char **environ;
	register int len;
	register const char *C;
	register char **P;

	for (C = name, len = 0; *C && *C != '='; ++C, ++len);
	for (P = environ; *P; ++P)
		if (!strncmp(*P, name, len))
			if (*(C = *P + len) == '=') {
				*offset = P - environ;
				return(++C);
			}
	return(NULL);
}
#endif
