/*
 * smoke16/libObj/src/str.c - functions to manipulate counted strings
 * $Revision: 1.5 $
 * Copyright 1999, 2000, Benjamin Sittler
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *  The author may be reached by electronic mail at <bsittler@nmt.edu>
 *
 */

#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include <smoke16/libObj.h>

/* create counted string */
Str *
newStr(size_t buflen) {
  Str *s;

  if ((s = (Str *) malloc(sizeof(Str)))) {
    s -> len = 0;
    s -> buflen = buflen ? buflen : 1;
    if ((s -> buf = (char *)malloc(s -> buflen)))
      return s;
    else
      perror("malloc");
    free(s);
  } else
    perror("malloc");
  return 0;
}

/* free counted string */
void
freeStr(Str *s) {
  if (s -> buflen)
    free(s -> buf);
  free(s);
}

/* write counted string */
int
fputStr(Str *s, FILE *f) {
  if (fwrite(s -> buf, 1, s -> len, f) != s -> len)
    return EOF;
  return s -> len;
}

/* write counted string with escaping */
int
debug_fputStr(Str *s, FILE *f) {
  size_t j;
  unsigned char c;

  putc('\"', f);
  for (j = 0; j < s -> len; j ++) {
    c = ((unsigned char *)(s -> buf))[j];
    if ((c == '\"') || (c == '\\') || (c == '\?') || (c == '\'') || ! isprint(c))
      putc('\\', f);
    if (c == '\t')
      putc('t', f);
    else if (c == '\n')
      putc('n', f);
    else if (c == '\a')
      putc('a', f);
    else if (c == '\f')
      putc('f', f);
    else if (c == '\v')
      putc('v', f);
    else if (c == '\b')
      putc('b', f);
    else if (c == '\r')
      putc('r', f);
    else if (isprint(c))
      putc(c, f);
    else
      fprintf(f, "x%2.2x", c);
  }
  putc('\"', f);
  return s -> len;
}

/* append to counted string */
Str *
apStr(Str *s, Str *t) {
  size_t len = s -> len + t -> len;

  if (! s -> buflen) {
    fprintf(stderr, "can't modify a static str\n");
    return 0;
  }
  if (len >= s -> buflen)
    if (! (s -> buf = (char *)realloc(s -> buf, s -> buflen = len))) {
      perror("realloc");
      return 0;
    }
  memcpy(s -> buf + s -> len, t -> buf, t -> len);
  s -> len = len;
  return s;
}

/* check for matching counted strings */
int
matchStr(Str *s, Str *t) {
  if (s -> len != t -> len)
    return 0;
  return ! memcmp(s -> buf, t -> buf, s -> len);
}

/* copy counted string */
Str *
dupStr(Str *s) {
  Str *ns;

  if (! (ns = newStr(s -> len))) {
    return ns;
  }
  ns -> len = s -> len;
  memcpy(ns -> buf, s -> buf, s -> len);
  return ns;
}

/* this table contains the numerals used by itoStr */
static const char numerals[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

/* create a counted string with a formatted integer */
Str *
itoStr(int a, unsigned base) {
  Str *s;
  unsigned long au, b;
  int neg;
  int digits = 1;

  if ((neg = (a < 0)))
    au = -a;
  else
    au = a;
  if (base < 2) {
    fprintf(stderr, "number base %d is too small\n",
	    base);
    return 0;
  }
  for(b = au; b >= base; b /= base)
    digits ++;
  if (! (s = newStr(digits + neg + 1)))
    return 0;
  s -> len = 0;
  if (neg)
    s -> buf[s -> len++] = '-';
  while (digits --) {
    unsigned int c;

    c = au % base;
    au /= base;
    if (c < sizeof(numerals))
      c = numerals[c];
    else {
      fprintf(stderr, "can't print digit %u in base %u\n",
	      c, base);
      freeStr(s);
      return 0;
    }
    s -> buf[s -> len + digits] = c;
  }
  s -> buf[(s -> len = s -> buflen) - 1] = ' ';
  return s;
}

/* append a char to a counted string */
Str *
putchStr(Str *s, char c) {
  Str t[1];
  char buf[1];

  *buf = c;
  t -> buf = buf;
  t -> buflen = 0;
  t -> len = 1;
  return apStr(s, t);
}

/* build a static counted string */
Str *
staticStr(char *buf, size_t len) {
  Str *s;

  if ((s = (Str *)malloc(sizeof(Str)))) {
    s -> len = len;
    s -> buf = buf;
    s -> buflen = 0;
  } else
    perror("malloc");
  return s;
}
