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

#include <stdio.h>
#ifdef SYSV
#include <string.h>
#else
#include <strings.h>
#endif
#include <ctype.h>

#include "config.h"
#include "cool.h"
#include "proto.h"
#include "sys_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;
    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() */