/* $Header: /belch_a/users/rearl/tinymuck/src/RCS/stringutil.c,v 1.10 90/09/28 12:25:08 rearl Exp $ */

/*
 * $Log:	stringutil.c,v $
 * Revision 1.10  90/09/28  12:25:08  rearl
 * Moved alloc_string() and alloc_prog_string() to here.
 * 
 * Revision 1.9  90/09/18  08:02:25  rearl
 * Speedups mainly, improved upper/lowercase lookup.
 * 
 * Revision 1.8  90/09/16  04:43:06  rearl
 * Preparation code added for disk-based MUCK.
 * 
 * Revision 1.7  90/09/10  02:19:51  rearl
 * Introduced string compression of properties, for the
 * COMPRESS compiler option.
 * 
 * Revision 1.6  90/08/15  03:08:38  rearl
 * Messed around with pronoun_substitute.  Hopefully made it easier to use.
 * 
 * Revision 1.5  90/08/02  18:50:43  rearl
 * Fixed bug in capitalized self-substitutions.
 * 
 * Revision 1.4  90/08/02  02:17:16  rearl
 * Capital % substitutions now automatically capitalize the
 * corresponding self-sub property.  Example: %O -> Her.
 * 
 * Revision 1.3  90/07/29  17:45:08  rearl
 * Pronoun substitution for programs fixed.
 * 
 * Revision 1.2  90/07/22  04:28:33  casie
 * Added %r/%R substitutions for reflexive pronouns.
 * 
 * Revision 1.1  90/07/19  23:04:13  casie
 * Initial revision
 * 
 *
 */

#include "copyright.h"
#include "config.h"
#include "interface.h"

/* String utilities */

#include <ctype.h>
#include "externs.h"

#define DOWNCASE(x) (lowercase[x])

int string_compare(register const char *s1, register const char *s2)
{
  while(*s1 && *s2 && DOWNCASE(*s1) == DOWNCASE(*s2)) s1++, s2++;
  
  return(DOWNCASE(*s1) - DOWNCASE(*s2));
}

int string_prefix(register const char *string, register const char *prefix)
{
  while(*string && *prefix && DOWNCASE(*string) == DOWNCASE(*prefix))
    string++, prefix++;
  return *prefix == '\0';
}

/* accepts only nonempty matches starting at the beginning of a word */
const char *string_match(register const char *src, register const char *sub)
{
  if(*sub != '\0') {
    while(*src) {
      if(string_prefix(src, sub)) return src;
      /* else scan to beginning of next word */
      while(*src && isalnum(*src)) src++;
      while(*src && !isalnum(*src)) src++;
    }
  }
  
  return 0;
}

/*
 * pronoun_substitute()
 *
 * %-type substitutions for pronouns
 *
 * %s/%S for subjective pronouns (he/she/it, He/She/It)
 * %o/%O for objective pronouns (him/her/it, Him/Her/It)
 * %p/%P for possessive pronouns (his/her/its, His/Her/Its)
 * %n    for the player's name.
 */
const char *pronoun_substitute(dbref player, char *str, dbref privs)
{
  static char result[BUFFER_LEN];
  char temp[BUFFER_LEN];
  char c;
  int gend;
  const int none = GENDER_UNASSIGNED;

  const static char *subjective[4] = { "", "it", "she", "he" };
  const static char *possessive[4] = { "", "its", "her", "his" };
  const static char *objective[4] = { "", "it", "her", "him" };
  const static char *reflexive[4] = { "", "itself", "herself", "himself" };
  const static char *absolute[4] = { "", "its", "hers", "his" };

  *result = '\0';

  if ((privs < 0) || (privs >= db_top))
    privs = 1;

  /* figure out player gender */
  gend = genderof(player);

  while (*str) {
    *temp = '\0';
    switch(*str) {
    case '[':
      str++;
      exec(&str, temp, privs, player, 0);
      if (*str == ']')
	str++;
      break;
    case '%':
      c = *(++str);
      switch(c) {
      case '%':
	strcpy(temp, "%");
	break;
      case '0':
      case '1':
      case '2':
      case '3':
      case '4':
      case '5':
      case '6':
      case '7':
      case '8':
      case '9':
	if (awptr[c - '0'])
	  strcpy(temp, awptr[c - '0']); 
	break;
      case 'v':
      case 'V':
	{
	  int a;
	  char x[5];
	  a = toupper(str[1]);
	  if ((a < 'A') || (a > 'Z'))
	    break;
	  if (*str)
	    str++;
	  *x = 'v';
	  *(x+2) = '\0';
	  *(x+1) = a;
	  if(get_attr(privs, x))
	    strcpy(temp, get_attr(privs, x));
	}
	break;
      case 'a':
      case 'A':
	if(gend == none)
	  sprintf(temp, "%s's", NAME(player));
	else
	  strcpy(temp, absolute[gend]);
	break;
      case 'r':
      case 'R':
	strcpy(temp, (gend == none) ? NAME(player) : reflexive[gend]);
	break;
      case 's':
      case 'S':
	strcpy(temp, (gend == none) ? NAME(player) : subjective[gend]);
	break;
      case 'p':
      case 'P':
	if (gend == none)
	  sprintf(temp, "%s's", NAME(player));
	else
	  strcpy(temp, possessive[gend]);
	break;
      case 'o':
      case 'O':
	strcpy(temp, (gend == none) ? NAME(player) : objective[gend]);
	break;
      case 'n':
      case 'N':
	strcpy(temp, NAME(player));
	break;
      case '#':
	sprintf(temp, "#%d", (int) player);
	break;
      default:
	break;
      }
      if(isupper(c) && islower(*temp))
	*temp = toupper(*temp);
      if (*str)
	str++;
      break;
    case '\\':  /* check for escape */
      if(str[1])
	str++;
      sprintf(temp, "%c", *str++);
      break;
    default:
      sprintf(temp, "%c", *str++);
      break;
    }
    if(strlen(temp) + strlen(result) < BUFFER_LEN)
      strcat(result, temp);
  }
  return result;
} 

char *alloc_string(const char *string)
{
  char *s;
  
  /* NULL, "" -> NULL */
  if(string == 0 || *string == '\0') return 0;
  if((s = (char *) malloc(strlen(string)+5)) == 0) {
    abort();
  }
  strcpy(s, string);
  return s;
}
char *alloc_ec_string(const char *what)
{
  char *x;

  x = alloc_string(what);
  if(x) return x;
  x = alloc_string("X"); /* easiest thing */
  *x = '\0'; /*yep, easiest thing */
  return x;
}
struct shared_string *
  alloc_prog_string(const char *s)
{
  struct shared_string *ss;
  int length;
  
  if(s == NULL || *s == '\0')
    return(NULL);
  
  length = strlen(s);
  if((ss = (struct shared_string *)
      malloc(sizeof(struct shared_string) + length)) == NULL)
    abort();
  ss->links = 1;
  ss->length = length;
  bcopy(s, ss->data, ss->length + 1);
  return(ss);
}

/* function to split up a list given a seperator */
/* note str will get hacked up */
char * parse_up(char **str, char *delimit)
{
  int deep = 0;
  char *s = *str, *os = *str;
  if (!*s)
    return (NULL);
  while (*s && (*s != *delimit)) {
    if (*s++ == '{') {
      deep = 1;
      while (deep && *s)
        switch (*s++) {
          case '{':
            deep++;
            break;
          case '}':
            deep--;
            break;
        }
    }
  }
  if (*s)
    *s++ = 0;
  *str = s;
  return (os);
}