
/** @file  psString.c
 *
 * -*- mode: C; c-basic-indent: 4; tab-width: 8; indent-tabs-mode: nil -*-
 * vim: set cindent ts=8 sw=4 expandtab:
 *
 *  @brief Contains the definition of string utility functions
 *
 *  String utility functions defined shall provide basic string copying
 *  capabilities while using the preferred memory management utilities.
 *
 *  @author Eric Van Alst, MHPCC
 *  @author David Robbins, MHPCC
 *
 *  @version $Revision: 1.24 $ $Name: not supported by cvs2svn $
 *  @date $Date: 2006-02-01 20:40:56 $
 *
 *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
 */

#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "psString.h"
#include "psMemory.h"
#include "psError.h"
#include "psConstants.h"

#include "psErrorText.h"

psString psStringCopy(const char *string)
{
    PS_ASSERT_PTR_NON_NULL(string, NULL);
    // Allocate memory using psAlloc function
    // Copy input string to memory just allocated
    // Return the copy
    // Pass through NULL values
    return string ? strcpy(psAlloc(strlen(string) + 1), string) : NULL;
}

psString psStringNCopy(const char *string,
                       unsigned int nChar)
{
    PS_ASSERT_PTR_NON_NULL(string, NULL);
    char *returnValue = NULL;

    // Check the number of characters to copy is non-negative
    if (nChar == UINT_MAX) {
        // Log error message and return NULL
        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
                PS_ERRORTEXT_psString_NCHAR_NEGATIVE,
                nChar);
        return NULL;
    }
    // Allocate memory using psAlloc function - nChar bytes
    // Copy input string to memory allocated up to nChar characters
    // Return the copy
    // Pass through NULL values
    returnValue =
              string ? strncpy(psAlloc((size_t) nChar + 1), string, (size_t) nChar)
              : NULL;

    // Ensure the last byte is NULL character
    returnValue[nChar] = '\0';

    // Return the string pointer
    return returnValue;
}

ssize_t psStringAppend(char **dest,
                       const char *format,
                       ...)
{
    PS_ASSERT_PTR_NON_NULL(format, 0);
    va_list         args;
    size_t          length;             // complete string length (sans \0)
    size_t          oldLength;          // original string length (sans \0)
    ssize_t         tailLength;         // length of string to append

    if (!dest || !format) {
        return 0;
    }

    if (!*dest) {
        *dest = psStringCopy("");
        oldLength = 0;
    } else {
        // size of existing string
        oldLength = strlen(*dest);
    }

    // find the size of the string to append
    va_start(args, format);
    // C99 guarentees vsnprintf() to work as expected with size = 0
    tailLength = vsnprintf(*dest, 0, format, args);
    va_end(args);

    // if the new tail is zero length, return the length of the old string.  if
    // it's a format error, return the error code.
    if (tailLength < 1) {
        return tailLength == 0 ? oldLength : tailLength;
    }

    // new string length (sans \0)
    length = oldLength + tailLength;

    // realloc string to string + tail + \0
    *dest = psRealloc(*dest, length + 1);

    // append tail + \0
    va_start(args, format);
    vsnprintf(*dest + oldLength, tailLength + 1, format, args);
    va_end(args);

    return length;
}

ssize_t psStringPrepend(char **dest,
                        const char *format,
                        ...)
{
    PS_ASSERT_PTR_NON_NULL(format, 0);
    va_list         args;
    size_t          length;             // complete string length (sans \0)
    ssize_t         headLength;         // length of string to prepend
    char            *oldDest;           // copy of original string

    if (!dest || !format) {
        return 0;
    }

    if (!*dest) {
        // makes the string backup and concatination pointless
        *dest = psStringCopy("");
        length = 0;
    } else {
        // size of existing string
        length = strlen(*dest);
    }

    // find the size of the string to prepend
    va_start(args, format);
    // C99 guarentees vsnprintf() to work as expected with size = 0
    headLength = vsnprintf(*dest, 0, format, args);
    va_end(args);

    // if the new head is zero length, return the length of the old string.  if
    // it's a format error, return the error code.
    if (headLength < 1) {
        return headLength == 0 ? length : headLength;
    }

    // backup original string
    oldDest = psStringCopy(*dest);

    // new string length (sans \0)
    length += headLength;

    // realloc string to head + string + \0
    *dest = psRealloc(*dest, length + 1);

    // copy the new head to the beginning of string
    va_start(args, format);
    vsnprintf(*dest, length + 1, format, args);
    va_end(args);

    // append the original string
    strncat(*dest, oldDest, length + 1);

    psFree(oldDest);

    return length;
}

// XXX: This should probably be implemented using strpbrk
psList *psStringSplit(const char *string,
                      const char *splitters)
{
    PS_ASSERT_PTR_NON_NULL(string, NULL);
    PS_ASSERT_PTR_NON_NULL(splitters, NULL);

    psList *values = psListAlloc(NULL); // The list of values to return
    unsigned int length = strlen(string); // The length of the string
    unsigned int numSplitters = strlen(splitters); // Number of characters that might split
    unsigned int start = 0;             // The position of the start of a word
    for (int i = 1; i < length; i++) {
        bool split = false;             // Is this character a splitter?
        for (int j = 0; j < numSplitters && ! split; j++) {
            if (string[i] == splitters[j]) {
                split = true;
            }
        }
        if (split) {
            if (i == start) {
                // Some idiot put in two spaces, or two commas or something
                start++;
            } else {
                // We're at the end of the word
                psString word = psStringNCopy(&string[start], i - start);
                (void)psListAdd(values, PS_LIST_TAIL, word);
                start = i + 1;
                psFree(word);
            }
        }
    }
    if (start < length) {
        // Copy the last word
        psString word = psStringNCopy(&string[start], length - start);
        (void)psListAdd(values, PS_LIST_TAIL, word);
        psFree(word);
    }

    return values;
}

