#include "s.h"

static char rcsid[] = "$Id: s.c,v 1.11 2006/10/01 06:40:04 yuuji Exp yuuji $";

/* BINARY_MODE is currently not used, but leaved for the future. */

/*
 * Let string ARG, upto N bytes.
 */
static
char* nlet(STR* str, char *source, int n)
{
  int sz;
  sz = (n+1+ALIGNLENGTH-1)/ALIGNLENGTH*ALIGNLENGTH;
  if (!str->s) {
    str->mode = ASCII_MODE;
    if (NULL == (str->buffer=malloc(sz)))
      return 0;
    str->size = sz;
  } else {
    /* Already have allocated area */
    if (str->size < n+1         /* too short */
        || str->size - sz > ALIGNLENGTH) { /* too large */
      if (NULL == (str->buffer=realloc(str->buffer, sz)))
        return 0;
      str->size = sz;
    }
  }
  str->s = str->buffer;
  strncpy(str->s, source, n);
  str->s[n] = '\0';
  str->len = n;
  return str->s;
}
/*
 * simple `let' upto the length of second argument SOURCE
 */
static
char* let(STR* str, char *source)
{
  return nlet(str, source, strlen(source));
}
static
char* flushleft(STR* str)
{
  if (str->buffer < str->s) {
    int i;
    char *s = str->s, *t = str->buffer;
    for (i=0; i < str->len; i++) {
      *t++ = *s++;
    }
    if (ASCII_MODE == str->mode)
      *t = '\0';
    str->s = str->buffer;
  }
  return str->s;
}
/*
 * Append string ARG, upto N bytes.
 */
static
char* nappend(STR* str, char *arg, int n)
{
  int alen = n;
  int sz;
  int offset = str->s - str->buffer;
  if (!str->buffer)
    let(str, "");
  if (str->size < str->len + alen + 1) {
    flushleft(str);
    sz = (str->len+alen+1+ALIGNLENGTH-1)/ALIGNLENGTH*ALIGNLENGTH;
    if (NULL == (str->buffer=realloc(str->buffer, sz)))
      return 0;
    str->size = sz;
    str->s = str->buffer;
  } else if (str->size >= offset + str->len + alen + 1) {
    flushleft(str);
  }
  strncpy(str->s+str->len, arg, str->size - str->len);
  str->len += alen;
  str->s[str->len] = '\0';
  return str->s;
}
static
char* append(STR* s, char* arg)
{
  return nappend(s, arg, strlen(arg));
}
static
char* addch(STR* s, int c)
{
  char ch;
  ch = c;
  return nappend(s, &ch, 1);
}
static
char* multiply(STR* s, int n)
{
  int i;
  STR tmp={0};
  if (!nlet(&tmp, s->s, s->len))
    return 0;
  for (i=1; i<n; i++) {
    if (!nappend(s, tmp.s, tmp.len)) {
      if (tmp.buffer) free(tmp.buffer);
      return 0;
    }
  }
  if (tmp.buffer) free(tmp.buffer);
  return s->s;
}
static
char* lshift(STR* s, int n)
{
  while (s->len >= 0 && n>0) {
    s->len--;
    s->s++;
    n--;
  }
  return s->s;
}
/*
 * Chomp trailing newline codes, if any
 */
static
char *chomp(STR* s)
{
  while (s->len>0 && strchr("\r\n", s->s[s->len-1])) {
    s->s[--s->len] = '\0';
  }
  return s->s;
}
/*
 * Chop trailing character irrespective what is is.
 */
static
char *chop(STR* s)
{
  if (s->len>0) {
    s->len--;
    if (ASCII_MODE == s->mode)
      s->s[s->len] = '\0';
  }
  return s->s;
}
/*
 * Trim preceding and trailing white spaces, if any
 */
static
char *trim(STR* s)
{
  char ws[] = " \t\f\r\n";
  while (s->len>0 && strchr(ws, s->s[s->len-1])) {
    s->s[--s->len] = '\0';
  }
  while (s->len>0 && strchr(ws, s->s[0])) {
    s->s++;
    --s->len;
  }
  return s->s;
}
/*
 * Replace OLD to NEW if OLD exists
 */
// #include <stdio.h>
static
char *replace(STR* s, char* old, char* new)
{
  char *b, *e;
  int oldlen=strlen(old);
  if (!s->s)
    return NULL;
  if ((b=strstr(s->s, old))) {
    STR tmp = {0};
    int taillen = s->len-(b-s->s)-oldlen;
    e = b+oldlen;
    if (!nlet(&tmp, e, taillen)) return 0;
    s->len = b-s->s;
    if (!append(s, new)) return 0;
    if (!append(s, tmp.s)) return 0;
    //strop(&tmp, "!");
    if (tmp.buffer) free(tmp.buffer);
  }
  return s->s;
}
char* lower(STR* s)
{
  int i = 0;
  char *p = s->s;
  for (i=0; i<s->len; i++, p++)
    if (isalpha((int)*p))
      *p = tolower((int)*p);
  return s->s;
}
char* upper(STR* s)
{
  int i = 0;
  char *p = s->s;
  for (i=0; i<s->len; i++, p++)
    if (isalpha((int)*p))
      *p = toupper((int)*p);
  return s->s;
}
char* strop(STR* s, char *op, ...)
{
  /*
   * op is one of below:
   *
   * operator	arg1	arg2	Function
   * -----------------------------------------------------------------------
   * "=" 	string		Assign string to s.s
   * "+"	string		Append string to s.s
   * "*"	string	N	Multiply s.s by N
   * "n="	string	N	Assign N bytes in string to s.s
   * "n+"	string	N	Append N bytes in string to s.s
   * "join"	string	s1, s2, ..., 0  Append s1, s2, ... upto 0 to s.s
   * "<"	string	N	Shift s.s to the left in N-bytes
   * "!"			Free allocated area
   * "addch"	char		Add one character to s.s
   * "chomp"			Chomp last character if it is newline
   * "chop"			Chop last character if it is newline
   * "delete"	string		Delete string from s.s
   * "replace"	old	new	Replace old with new in s.s
   * "trim"			Trim preceding/trailing white spaces in s.s
   * "lower"			Turn all characters case lower
   * "upper"			Turn all characters case upper
   */
  char *arg1;
  char* r;
  
  va_list ap;
  va_start(ap, op);
  switch (*op++) {
  case '=':
    arg1 = va_arg(ap, char*);
    r=let(s, arg1);
    break;
  case 'n':                     /* n= and n+ */
    arg1 = va_arg(ap, char*);
    switch (*op++) {
    case '=':
      r=nlet(s, arg1, va_arg(ap, int));
      break;
    case '+':
      r=nappend(s, arg1, va_arg(ap, int));
    }
    break;
  case '+':
    arg1 = va_arg(ap, char*);
    r=append(s, arg1);
    break;
  case '*':
    r=multiply(s, va_arg(ap, int));
    break;
  case '<':
    r=lshift(s, va_arg(ap, int));
    break;
  case '!':
    if (s->s) free(s->buffer);
    s->size=0;
    s->s=0;
    r=0;
    break;
  case 'a':                     /* addch */
    return addch(s, va_arg(ap, int));
  case 'c':                     /* chomp */
    if (!strcmp("homp", op))
      r=chomp(s);
    else if (!strcmp("hop", op))
      r=chop(s);
    break;
  case 'd':                     /* delete */
    r=replace(s, va_arg(ap, char*), "");
    break;
  case 'j':                     /* join: last argument must be 0 */
    while ((arg1 = va_arg(ap, char*)))
      r=append(s, arg1);
    break;
  case 'l':                     /* lower */
    lower(s);
    break;
  case 'r':                     /* replace */
    arg1 = va_arg(ap, char*);
    r=replace(s, arg1, va_arg(ap, char*));
    break;
  case 't':                     /* trim */
    r=trim(s);
    break;
  case 'u':                     /* upper */
    upper(s);
    break;
  }
  va_end(ap);
  return r;
}

#ifdef S_MAIN_
#include <stdio.h>
int main()
{
  STR x = {0};
  char buf[50];
  char let[] = "=";
  strop(&x, let, "foo");
  printf("x=[%s]\n", x.s);
  strop(&x, "delete", "o");
  printf("del-(o) x=[%s]\n", x.s);
  strop(&x, "delete", "o");
  printf("del-(o) x=[%s]\n", x.s);
  strop(&x, "delete", "o");
  printf("del-(o) x=[%s]\n", x.s);
  strop(&x, "delete", "f");
  printf("del(f) x=[%s]\n", x.s);
  strop(&x, "delete", "f");
  printf("del(f) x=[%s]\n", x.s);
  strop(&x, "+", "b");
  printf("ap(b) x=[%s]\n", x.s);
}
#endif
