text: /*--- file strutil.c -- string-oriented utilities.
   */
  
  #include <stdio.h>
  #include <string.h>
  #include <ctype.h>
  #include <stdlib.h>
  #include "boolean.h"
  #include "strutilp.h"
  #include "hexbinpr.h"
  #include "global.h"
  #include "rsaref.h"
  
  /*--- function match ----------------------------------------------
   *
   *  Determine whether two character strings match.  Case insensitive.
   *
   *  Entry:  str	     is a string.
   *	    pattern  is a pattern to which we are comparing.
   *
   *  Exit:   Returns TRUE iff the strings match.
   */
  int
  match(str,pattern)
  char *str;
  char *pattern;
  {
     char ch1, ch2;
  
     do {
        ch1 = (char) (islower(*str) ? toupper(*str) : *str);
        ch2 = (char) (islower(*pattern) ? toupper(*pattern) : *pattern);
        if(ch1 != ch2) return FALSE;
        str++; pattern++;
     } while(ch1 == ch2 && ch1 && ch2);
  
     if(!ch1 && !ch2) {
        return TRUE;
     } else {
        return FALSE;
     }
  }
  
  /*--- function matchn ----------------------------------------------
   *
   *  Determine whether two character prefix strings match.
   *  Case insensitive.
   *
   *  Entry:  str	     is a string.
   *	    pattern  is a pattern to which we are comparing.
   *	    nchars   is the number of characters to compare.
   *
   *  Exit:   Returns TRUE iff the prefix strings match.
   */
  int
  matchn(str,pattern,nchars)
  char *str;
  char *pattern;
  int nchars;
  {
     char ch1, ch2;
  
     do {
        ch1 = (char) (islower(*str) ? toupper(*str) : *str);
        ch2 = (char) (islower(*pattern) ? toupper(*pattern) : *pattern);
        if(ch1 != ch2) return FALSE;
        str++; pattern++; nchars--;
     } while(nchars);
  
     return TRUE;
  }
  
  /*--- function tail ------------------------------------------------
   *
   *  Returns the trailing N characters of a string.
   *
   *  Entry:  str	     is a null-terminated string.
   *	    num	     is the desired length of the tail.
   *
   *  Exit:   Returns a pointer to the tail, or to the beginning
   *	      of the string if the string is less than N characters long.
   */
  char *
  tail(str,num)
  char *str;
  int num;
  {
     int mylen = strlen(str);
  
     if(mylen < num) {
        return(str);
     } else {
        return(str+mylen-num);
     }
  }
  
  
  /*--- function WhiteSpace -------------------------------------------
   *
   *  Determine whether a character is "white space".
   *
   *  Entry:  ch	  is a character.
   *
   *  Exit:   Returns TRUE iff the character is "white space".
   */
  int
  WhiteSpace(ch)
  char ch;
  {
     return(ch==' ' || ch=='\t' || ch=='\n');
  }
  
  /*--- function LineIsWhiteSpace --------------------------------
   *
   *  Determine whether an entire line is nothing but white space.
   *
   *  Entry:	line  points to a zero-terminated line.
   *
   *	 Exit:	 Returns TRUE if line has nothing but white space
   *				 prior to the EOL.
   */
  BOOL
  LineIsWhiteSpace(line)
  char *line;
  {
  	for(; *line; line++) {
  		if(!WhiteSpace(*line)) return FALSE;
  	}
  	return TRUE;
  }
  
  /*--- function strcpyalloc -------------------------------------
   *
   *  Copy a string, allocating space for it.
   *
   *	 Entry: target points to a pointer to a character.
   *				source points to z zero-terminated string that
   *		   we want to copy.
   *
   *  Exit:	target contains a pointer to a newly-allocated
   *				       piece of memory that contains a copy of
   *						 source.
   *				Returns NULL if alloc unsuccessful, else
   *				  returns target.
   */
  char *
  strcpyalloc(target,source)
  char **target;
  char *source;
  {
  	*target = (char *) malloc(strlen(source)+1);
  	if(*target) {
  		strcpy(*target,source);
  	}
  	return *target;
  }
  
  /*--- function strcatrealloc ------------------------------
   *
   *  Append a string to another string, reallocating memory
   *  for the target string to ensure there's room.
   *
   *  Entry:	target points to a pointer to a string to
   *						 be appended to.
   *				source points to a string to append.
   *
   *	 Exit:	target now points to a possibly different address,
   *						 a pointer to the combined string.
   */
  char *
  strcatrealloc(target,source)
  char **target;
  char *source;
  {
  	*target = (char *) realloc(*target,strlen(source)+
      strlen(*target)+1);
  	if(*target) {
  		strcat(*target,source);
  	}
  	return *target;
  }
  
  
  /*--- function trim ----------------------------------------------
   *
   *  Trim trailing white space from a line.
   *
   *  Entry:	line	points to a zero-terminated line.
   *
   *  Exit:   Any trailing white space (including newlines) has
   *				 been eliminated.
   */
  void
  trim(line)
  char *line;
  {
  	char *cptr = line + strlen(line) - 1;
  
  	while(cptr >= line && WhiteSpace(*cptr)) *(cptr--) = '\0';
  }
  
  /*--- function ExtractEmailAddr ----------------------------------------------
   *
   *  Extract the proper email address from a line *containing* an
   *  email address, often a line that was part of a "From:" line
   *  in another email address.
   *  For instance, we may receive lines like:
   *
   *	rstevens@noao.edu (W. Richard Stevens)
   *    "Mark Riordan" <riordanmr@clvax1.cl.msu.edu>
   *    person@cps.msu.edu
   *
   *  and we need to figure out which characters constitute the
   *  email address.
   *
   *  Entry:	addr		is an electronic mail address, possibly with
   *							extra characters as above.
   *
   *	 Exit:	Returns a pointer to the correct substring of above.
   *				May modify the contents of "addr".
   */
  char *
  ExtractEmailAddr(addr)
  char *addr;
  {
  	char search_char='\0', *cptr;
  	
  	trim(addr);
  	
  	cptr = addr + strlen(addr) - 1;
  	/* Look at the last character of addr */
  	if(*cptr == '>') {
  		/* Extract what's between < ... > */
  		search_char = '<';
  		*cptr = '\0';
  		while(*cptr != search_char && cptr != addr) cptr--;
  		if(*cptr == search_char) cptr++;
  		return cptr;
  		
  	} else if(*cptr == ')') {
  		/* Strip the trailing "(Firstname Lastname)" from addr */
  		search_char = '(';
  		*cptr = '\0';
  		while(*cptr != search_char && cptr != addr) {
  			cptr--;
  		}
  		if(*cptr == search_char) *cptr = '\0';
  		trim(addr);
  	}
  	
  	return addr;
  }
  
  /*--- function BreakUpEmailAddr --------------------------------------------
   *
   *  Break an Internet-style email address into username and hostname.
   *
   *  Entry:	addr		is an address of form user@hostname.domain
   *				lenUser and lenHost are the number of bytes in the respective
   *							buffers.
   *	
   *	 Exit:	userName	is the username (zero-terminated).
   *				hostName is the hostname.
   *				Returns NULL upon success, else error message.
   */
  char *
  BreakUpEmailAddr(addr,userName,lenUser,hostName,lenHost)
  char *addr;
  char *userName;
  int lenUser;
  char *hostName;
  int lenHost;
  {
  	char *cptr;
  	int addrlen = strlen(addr), hostlen, nbytes;
  	
  	cptr = strchr(addr,'@');
  	if(!cptr) {
  		return "Email address is not in Internet format";
  	} else {
  		nbytes = lenUser>cptr-addr ? cptr-addr : lenUser-1;
  		strncpy(userName,addr,nbytes);
  		userName[nbytes] = '\0';
  		hostlen = addr + addrlen - (cptr+1); 
  		nbytes = lenHost>hostlen ? hostlen : lenHost-1;
  		strncpy(hostName,cptr+1,nbytes);
  		hostName[nbytes] = '\0';
  	}
  	return NULL;
  }
  
  /*--- function EmailHostnameComponents ------------------------------------
   *
   *  Calculate the number of components in the hostname portion
   *  of an Internet-style email address.
   *  E.g.,  bill@cs.bigu.edu  has 3 hostname components.
   *
   *	 Entry: user	is an email address.
   *
   *	 Exit:	 Returns number of components.
   */
  
  int
  EmailHostnameComponents(addr)
  char *addr;
  {
  	int ncomp = 0;
  	char *cptr;
  
  	cptr = strchr(addr,'@');
  	if(cptr) {
  		cptr++;
  		ncomp = 1;
  		while(*cptr) if(*(cptr++)=='.') ncomp++;
  	}
  	return ncomp;
  }
  
  /*--- function EmailAddrUpALevel -------------------------------------
   *
   *  Modify an Internet email address to remove the first component
   *  of its hostname.
   *  E.g., bill@cs.bigu.edu --> bill@bigu.edu
   *
   *	 Entry: addr	is an email address.
   *
   *  Exit:	addr  has been modified.
   *				Returns TRUE if we were able to complete processing.
   */
  int
  EmailAddrUpALevel(addr)
  char *addr;
  {
  	char *cptr, *targ;
  	
  	if(EmailHostnameComponents(addr) < 3) return FALSE;
  
  	targ = cptr = strchr(addr,'@')+1;
  	for(; *cptr && *cptr!='.'; cptr++);  /* Skip past first level */
  	for(cptr++; *cptr; ) *(targ++) = *(cptr++);  /* Copy rest */
  	*targ = '\0';
  
  	return TRUE;
  }
  
  /*--- function EmailMatch --------------------------------------
   *
   *  Determine whether a given email addresses matches a known address
   *  of a user.  Simply requires that the two usernames
   *  match and that the hostnames match, minus the first few
   *  components of the hostname.
   *
   *  Entry:	user			is the known email address.
   *				candidate   is an address that may match.
   *
   *	 Exit:   Returns TRUE iff the two correspond.
   */
  BOOL
  EmailMatch(user, candidate)
  char *user;
  char *candidate;
  {
  	char *user_copy;
  	BOOL trying=TRUE;
  	BOOL matchOK=FALSE;
  
  	strcpyalloc(&user_copy,user);  /* We may modify user_copy below */
  
  	/* Keep testing for a case-insensitive both otherwise exact
  	 * lexicographical match while we remove successive leftmost
  	 * components from the hostname of the candidate.
  	 */
  	do {
  		if(match(user_copy,candidate)) {
  			trying = FALSE;
  			matchOK = TRUE;
  		} else {
  			trying = EmailAddrUpALevel(user_copy);
  		}
  	} while(trying); 
  	
  	free(user_copy);
  	return matchOK;
  }
  
  /*--- function LowerCaseString --------------------------------------------
   *
   *  Convert an entire string to lowercase.
   *
   *  Entry:	str	is the address of a xero-terminated string.
   *
   *	 Exit:	All of the alphabetic characters in the string have 
   *				been converted to lowercase.
   *				Returns the address of the string.
   */
  char *
  LowerCaseString(str)
  char *str;
  {
  	register char ch, *addr=str;
  
  	while(ch = *addr) {
  		if(isupper(ch)) *addr = tolower(ch);
  		addr++;
  	}
  	return str;
  }
  
  /*--- function ClearBuffer --------------------------------------------------
   *
   *  Zero out a buffer.	Generally used to erase sensitive information,
   *  like passwords, so they won't appear in coredumps or whatever.
   */
  void
  ClearBuffer(buf,nbytes)
  void *buf;
  int nbytes;
  {
  	R_memset((unsigned char *)buf,0,nbytes);
  }
  
  /*--- function MakeHexDigest -------------------------------------------
   *
   *  Make an MD5 digest of the input, and return the result encoded
   *  into hex ASCII.
   */
  void
  MakeHexDigest(buf,buflen,hex_digest)
  unsigned char *buf;
  unsigned int buflen;
  char *hex_digest;
  {
  	unsigned char digest[16];
  	unsigned int digest_len;
  
  	R_DigestBlock(digest,&digest_len,buf,buflen,DA_MD5,MODE_STANDARD);
  	BinToHex(digest,16,hex_digest);
  }
  
  