// zeal - A portable Glk-based Z-code interpreter
// Copyright (C) 2000 Jeremy Condit <jcondit@eecs.harvard.edu>
// 
// 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

// =======================================================================
//  stdlite.cc:
//
//  digital unix has no snprintf().  i finally got fed up and just wrote
//  my own.  it supports %d, %s, and %s, plus field widths.
//
//  i also considered rewriting the bits of the c library that i used to
//  make this code completely self-sufficient, but then i realized that
//  that was just silly.  if someone doesn't have a working memcpy(),
//  they've got bigger problems than whether they can play zork.
//  =======================================================================

#include "stdlite.h"

// memcpy_lite
//
// well, i wrote it once.  might as well keep it.

void*
memcpy_lite(void* s1, const void* s2, uint n)
{
    char* cur1 = (char*) s1;
    const char* cur2 = (const char*) s2;
    const char* end = cur2 + n;

    while (cur2 < end) {
        *(cur1++) = *(cur2++);
    }

    return s1;
}

// helper_int
//
// helper for printf that handles %d and %x.

static
uint
helper_int(char* s, char* send, uint width, bool pad, int base, int n)
{
    uint len = 0;
    uint dashlen = 0;
    char* scur = s;

    int ncur;
    uint i;
    uint j;

    if (base != 10 && base != 16) {
        return 0;
    }

    // do we need a dash?
    if (n < 0) {
        n *= -1;
        dashlen++;
    }

    // get length of number part
    len++;
    for (ncur = n; ncur >= base; ncur /= base) {
        len++;
    }

    // write dash if necessary (padding)
    if (pad && dashlen == 1 && scur < send) {
        *(scur++) = '-';
    }

    // write padding if width is larger than number
    if (len + dashlen < width) {
        for (i = 0; i < width - len - dashlen && scur < send; i++) {
            *(scur++) = pad ? '0' : ' ';
        }
    }

    // write dash if necessary (no padding)
    if (!pad && dashlen == 1 && scur < send) {
        *(scur++) = '-';
    }

    // write the number (a little inefficient, since we calculate the log
    // each time through, but this reduces the number of ways i can screw
    // up :-))
    for (i = 0; i < len && scur < send; i++) {
        int nprint = n;
        for (j = 0; j < len - i - 1; j++) {
            nprint /= base;
        }
        nprint %= base;
        if (nprint < 10) {
            *(scur++) = '0' + nprint;
        } else {
            *(scur++) = 'a' + nprint - 10;
        }
    }

    return scur - s;
}

// helper_str
//
// helper for printf that handles %s.

static
uint
helper_str(char* s, char* send, uint width, const char* t)
{
    uint len = 0;
    char* scur = s;

    const char* tcur;
    uint i;

    // calculate the length (could use strlen, but i want this to be self-
    // contained, for some odd reason)
    for (tcur = t; *tcur != 0; tcur++) {
        len++;
    }

    // print padding
    if (len < width) {
        for (i = 0; i < width - len && scur < send; i++) {
            *(scur++) = ' ';
        }
    }

    // print the string
    for (tcur = t; *tcur != 0 && scur < send; tcur++) {
        *(scur++) = *tcur;
    }

    return scur - s;
}

// vsnprintf_lite
//
// my very own vsnprintf.  supports %s, %d, and %x, as well as field
// widths placed between % and the field type indicator.

int
vsnprintf_lite(char *s, uint n, const char *format, va_list args)
{
    const char* fcur = format;
    char* scur = s;
    char* send = s + n;

    // pct tells whether we've seen a % and should be substituting based on
    // the next character.
    bool pct = false;

    // pad indicates whether we're supposed to pad the data if shorter than
    // the field width (indicated by width starting w/ 0).  width keeps
    // track of the desired minimum field width.
    bool pad;
    uint width;

    // loop until we run out of room or format string
    while (*fcur != 0 && scur < send) {
        if (!pct) {
            // regular mode:  look for a %, but print normally otherwise.
            if (*fcur == '%') {
                pct = true;
                pad = false;
                width = 0;
            } else {
                *(scur++) = *fcur;
            }
        } else {
            // pct mode:  start interpreting the format string.
            if (width == 0 && *fcur == '0') {
                pad = true;
            } else if (*fcur >= '0' && *fcur <= '9') {
                width = (width * 10) + (*fcur - '0');
            } else {
                switch (*fcur) {
                    case 'd':
                        scur += helper_int(scur, send, width, pad, 10,
                                           va_arg(args, int));
                        break;
                    case 'x':
                        scur += helper_int(scur, send, width, pad, 16,
                                           va_arg(args, int));
                        break;
                    case 's':
                        scur += helper_str(scur, send, width,
                                           va_arg(args, const char*));
                        break;
                }
                pct = false;
            }
        }
        fcur++;
    }

    // add the null termination.
    if (scur < send) {
        *scur = 0;
        return scur - s;
    } else {
        s[n - 1] = 0;
        return -1;
    }
}

// snprintf_lite
//
// my very own snprintf.

int
snprintf_lite(char *s, uint n, const char *format, ...)
{
    int result;
    
    va_list args;
    va_start(args, format);
    result = vsnprintf_lite(s, n, format, args);
    va_end(args);

    return result;
}
