/*
 * nsfUtil.c --
 *
 *      Utility functions of the Next Scripting Framework.
 *
 * Copyright (C) 2001-2017 Gustaf Neumann
 * Copyright (C) 2001-2007 Uwe Zdun
 *
 * Vienna University of Economics and Business
 * Institute of Information Systems and New Media
 * A-1020, Welthandelsplatz 1
 * Vienna, Austria
 *
 * This work is licensed under the MIT License http://www.opensource.org/licenses/MIT
 *
 * Copyright:
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

#include "nsfInt.h"

/*
 *----------------------------------------------------------------------
 * strnstr --
 *
 *    Implementation of strnstr() for platforms not providing it via their C
 *    library. The function strnstr locates the first occurrence of a
 *    substring in a null-terminated string.
 *
 * Results:
 *    Strbstring or NULL
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------
 */
#ifndef HAVE_STRNSTR
char *strnstr(const char *buffer, const char *needle, size_t buffer_len) {
  char *result = NULL;

  if (*needle == '\0') {
    result = (char *)buffer;

  } else {
    size_t remainder, needle_len;
    char  *p;

    needle_len = strlen(needle);
    for (p = (char *)buffer, remainder = buffer_len;
         p != NULL;
         p = memchr(p + 1, *needle, remainder-1)) {
      remainder = buffer_len - (size_t)(p - buffer);
      if (remainder < needle_len) {
        break;
      }
      if (*p == *needle && strncmp(p, needle, needle_len) == 0) {
        result = p;
        break;
      }
    }
  }

  return result;
}
#endif

/*
 *----------------------------------------------------------------------
 * Nsf_ltoa --
 *
 *    Convert a long value into a string; this function is a fast
 *    version of sprintf(buf, "%ld", l);
 *
 * Results:
 *    String containing decimal value of the provided parameter.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------
 */
char *
Nsf_ltoa(char *buf, long i, int *lengthPtr) {
  int nr_written, negative;
  char tmp[LONG_AS_STRING], *pointer = &tmp[1], *string, *p;

  nonnull_assert(buf != NULL);
  nonnull_assert(lengthPtr != NULL);

  tmp[0] = 0;

  if (i < 0) {
    i = -i;
    negative = nr_written = 1;
  } else {
    nr_written = negative = 0;
  }
  do {
    nr_written++;
    *pointer++ = (char)(i%10 + '0');
    i /= 10;
  } while (i);

  p = string = buf;
  if (negative != 0) {
    *p++ = '-';
  }
  while ((*p++ = *--pointer)) {
    ;   /* copy number (reversed) from tmp to buf */
  }
  *lengthPtr = nr_written;

  return string;
}

/*
 *----------------------------------------------------------------------
 * NsfStringIncr --
 *
 *    Increment a value on a number system with the provided alphabet. The
 *    intention of the function is to generate compact new symbols.
 *
 * Results:
 *    New symbol in form of a string.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------
 */

static const char *alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
static size_t blockIncrement = 8u;
/*
  static char *alphabet = "ab";
  static int blockIncrement = 2;
*/
static unsigned char chartable[255] = {0};

char *
NsfStringIncr(NsfStringIncrStruct *iss) {
  char newch, *currentChar;

  nonnull_assert(iss != NULL);

  currentChar = iss->buffer + iss->bufSize - 2;
  newch = *(alphabet + chartable[(unsigned)*currentChar]);

  while (1) {
    if (newch != '\0') { /* no overflow */
      *currentChar = newch;
      break;
    } else {     /* overflow */
      *currentChar = *alphabet; /* use first char from alphabet */
      currentChar--;
      assert(currentChar >= iss->buffer);

      newch = *(alphabet + chartable[(unsigned)*currentChar]);
      if (currentChar < iss->start) {
        iss->length++;
        if (currentChar == iss->buffer) {
          size_t newBufSize = iss->bufSize + blockIncrement;
          char  *newBuffer = ckalloc((unsigned)newBufSize);

          currentChar = newBuffer+blockIncrement;
          /*memset(newBuffer, 0, blockIncrement);*/
          memcpy(currentChar, iss->buffer, iss->bufSize);
          *currentChar = newch;
          iss->start = currentChar;
          ckfree(iss->buffer);
          iss->buffer = newBuffer;
          iss->bufSize = newBufSize;
        } else {
          iss->start = currentChar;
        }
      }
    }
  }
  assert(iss->buffer[iss->bufSize-1] == 0);
  assert(iss->buffer[iss->bufSize-2] != 0);
  assert(iss->length < iss->bufSize);
  assert(iss->start + iss->length + 1 == iss->buffer + iss->bufSize);

  return iss->start;
}

/*
 *----------------------------------------------------------------------
 * NsfStringIncrInit, NsfStringIncrFree --
 *
 *    Support function for NsfStringIncr(). NsfStringIncrInit() function is
 *    called before NsfStringIncr() can be used on this buffer,
 *    NsfStringIncrFree() terminates usage.
 *
 * Results:
 *    void
 *
 * Side effects:
 *    Initializes the .
 *
 *----------------------------------------------------------------------
 */

void
NsfStringIncrInit(NsfStringIncrStruct *iss) {
  const char  *p;
  int          i = 0;
  const size_t bufSize = blockIncrement;
  /*
   * The static variable blockIncrement is always large than 2, otherwise we
   * would have to use:
   *
   *   const size_t bufSize = (blockIncrement > 2) ? blockIncrement : 2;
   */

  nonnull_assert(iss != NULL);

  for (p=alphabet; *p != '\0'; p++) {
    chartable[(int)*p] = (unsigned char)(++i);
  }

  iss->buffer = ckalloc((unsigned)bufSize);
  memset(iss->buffer, 0, bufSize);
  iss->start    = iss->buffer + bufSize-2;
  iss->bufSize  = bufSize;
  iss->length   = 1;
  /*
    for (i = 1; i < 50; i++) {
    NsfStringIncr(iss);
    fprintf(stderr, "string '%s' (%d)\n",  iss->start, iss->length);
    }
  */
}

void
NsfStringIncrFree(NsfStringIncrStruct *iss) {

  nonnull_assert(iss != NULL);

  ckfree(iss->buffer);
}

/*
 * Local Variables:
 * mode: c
 * c-basic-offset: 2
 * fill-column: 78
 * indent-tabs-mode: nil
 * End:
 */