
/** @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
 *
 *  @version $Revision: 1.20 $ $Name: not supported by cvs2svn $
 *  @date $Date: 2005-07-12 19:12:01 $
 *
 *  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 "psErrorText.h"

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

psString psStringNCopy(const char *string, unsigned int nChar)
{
    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
    returnValue = strncpy(psAlloc((size_t) nChar + 1), string, (size_t) nChar);

    // Ensure the last byte is NULL character
    if (nChar > 0) {
        returnValue[nChar] = '\0';
    }
    // Return the string pointer
    return returnValue;
}

ssize_t psStringAppend(char **dest, const char *format, ...)
{
    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, ...)
{
    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;
}
