/* Copyright (c) 1993 Stephen F. White */

#include "cool.h"
#include "proto.h"
#include "servers.h"

String *string_new (int len)
{
  String *new;
  int newlen = MAX (len, STRING_INIT_SIZE);

  new = (String *) cool_malloc (sizeof (String) + newlen + 1);
  new->mem = newlen;
  new->len = 0;
  new->ref = 1;
  new->str = (char *) new + sizeof (String);
  new->str[0] = '\0';
  return new;
}

#ifndef INLINE

String *string_dup (String * s)
{
  s->ref++;
  return s;
}

void string_free (String * s)
{
  s->ref--;
  if (s->ref == 0) {
    FREE (s);
  }
}

#endif

/*
 * string_cpy() - copy a char * into a String struct
 */

String *string_cpy (const char *s)
{
  String *new;

  new = string_new (strlen (s));
  new = string_cat (new, s);
  return new;
}

String *string_ncpy (const char *s, int len)
{
  String *new;

  new = string_new (len + 1);
  strncpy (new->str, s, len);
  new->len = len;
  new->str[len] = '\0';
  return new;
}

static String *string_extend_to (String * str, int mem)
{
  String *new;

  if (!str)
    return 0;
  if (mem <= str->mem) {
    return str;
  }
#ifdef STRING_DOUBLING
  while (mem > str->mem) {
    str->mem = (str->mem + sizeof (String)) * 2 - sizeof (String);
  }
#else
  str->mem = (mem / STRING_GROW_BY + 1) * STRING_GROW_BY;
#endif
  new = string_new (str->mem);
  strcpy (new->str, str->str);
  new->len = strlen (new->str);
  FREE (str);
  return new;
}

String *string_catc (String * str, char c)
{
  if (!str)
    return 0;
  if (str->len + 2 > str->mem) {
    str = string_extend_to (str, str->len + 2);
  }
  str->str[str->len++] = c;
  str->str[str->len] = '\0';
  return str;
}

String *string_cat (String * str, const char *s)
{
  int len;

  if (!str)
    return 0;
  len = str->len + strlen (s);
  if (len + 1 > str->mem) {
    str = string_extend_to (str, len + 1);
  }
  strcat (str->str + str->len, s);
  str->len = len;
  return str;
}

String *string_catnum (String * str, int num)
{
  int len;

  if (!str)
    return 0;
  len = str->len + INT_SIZE;
  if (len + 1 > str->mem) {
    str = string_extend_to (str, len + 1);
  }
  sprintf (str->str + str->len, "%d", num);
  str->len = strlen (str->str);
  return str;
}

String *string_catobj (String * str, Objid obj, int full)
{
  const char *servername = 0;
  int len;

  if (!str)
    return 0;
  len = str->len + INT_SIZE + 2 +
    (full ? strlen (servername = serv_id2name (obj.server)) : 0);
  if (len + 1 > str->mem) {
    str = string_extend_to (str, len + 1);
  }
  if (full) {
    sprintf (str->str + str->len, "#%d@%s", obj.id, servername);
  } else {
    sprintf (str->str + str->len, "#%d", obj.id);
  }
  str->len = strlen (str->str);
  return str;
}

String *string_indent_cat (String * str, int indent, const char *s)
{
  int len;
  char *c;

  if (!str)
    return 0;
  len = str->len + indent + strlen (s);
  if (len + 1 > str->mem) {
    str = string_extend_to (str, len + 1);
  }

  c = str->str + str->len;
  for (; indent; indent--) {
    *c++ = ' ';
  }
  strcpy (c, s);
  str->len = len;
  return str;
}

String *string_backslash (String * str, const char *s)
{
  int len;
  const char *t;
  char *r;

  if (!str)
    return 0;
/*
 * calculate required length, including backslashes
 * NOTE:  CR-LF turns into \n, both of which are two chars, so no
 *        length adjustment is needed
 */
  len = str->len;
  for (t = s; *t; t++, len++) {
    if (*t == '\\' || *t == '"' || *t == '\t') {
      len++;
    }
  }

/*
 * extend string, if we have to
 */
  if (len + 1 > str->mem) {
    str = string_extend_to (str, len + 1);
  }
  r = str->str + str->len;      /* skip to end of string */
  do {
    switch (*s) {
    case '\r':
      s++;                      /* ignore \r; handle \n instead */
    case '\n':
      *r++ = '\\';
      *r++ = 'n';
      break;
    case '\t':
      *r++ = '\\';
      *r++ = 't';
      break;
    case '"':
    case '\\':
      *r++ = '\\';
    default:
      *r++ = *s;
      break;
    }
  } while (*s++);
  str->len = len;
  return str;
}

String *string_pad (String * str, int padlen, char tok)
{
  int len;
  char *s;

  if (!str)
    return 0;
  len = str->len + padlen;
  if (len + 1 > str->mem) {
    str = string_extend_to (str, len + 1);
  }
  s = str->str + str->len;
  while (padlen--) {
    *s++ = tok;
  }
  *s = '\0';
  str->len = len;
  return str;
}

String *string_prepad (String * str, int padlen, char tok)
{
  int len;
  char *s, *d;

  if (!str)
    return 0;
  len = str->len + padlen;
  if (len + 1 > str->mem) {
    str = string_extend_to (str, len + 1);
  }
  for (s = str->str + str->len, d = s + padlen; s >= str->str;) {
    *d-- = *s--;
  }
  s = str->str;
  while (padlen--) {
    *s++ = tok;
  }
  str->len = len;
  return str;
}

String *string_strip_cr (String * str)
{
  char *s1, *s2;

  if (!str)
    return 0;
  for (s1 = s2 = str->str; *s1; s1++) {
    if (*s1 != '\r') {
      *s2++ = *s1;
    }
  }
  *s2 = '\0';                   /* terminate it */
  str->len = s2 - str->str;
  return str;
}                               /* string_strip_cr() */