/*
 * poor man's snprintf() - for systems which don't have their own
 *
 * This version of snprintf() currently supports only %s, %c, %d, %u,
 * %ld, %lu, %li, %i. It supports the 0, + and width modifiers only for decimal
 * output (%[l]{d|u|i}).
 *
 * Copyright (c) Cornelius Krasel 2000.
 * Modified by Matthias Andree <matthias.andree@gmx.de>.
 * Modifications (C)opyright 2002 - 2003.
 *
 * 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 "leafnode.h"

#ifndef HAVE_WORKING_SNPRINTF

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

/** format unsigned value u into p */
static void fmtu(char *p, unsigned long u) {
    unsigned int len = 0;
    unsigned long i = u, ni;
    unsigned char b[22];
    do {
	ni = i / 10;
	b[len++] = i - 10*ni;
	i = ni;
    } while (i);

    while (len) {
	*p++  = "0123456789"[b[--len]];
    }
    *p = '\0';
}

/** format signed value s into p, prefix asign if its nonzero and the
 * value is positive. asign is usually '+' or ' '. */
static inline void fmts(char *p, long s, char asign) {
    if (s < 0) {
	*p++ = '-';
	fmtu(p, -s);
    } else {
	if (asign)
	    *p++ = asign;
	fmtu(p, s);
    }
}

int
ln_vsnprintf(char *str, size_t n, const char *format, va_list ap)
{
    const char *p;
    char *q;
    int flag = 0;
    size_t width = 0;
    char fill = ' ';
    int lflag = 0;		/* checking for longs */
    int asign = 0;		/* always show sign */
    size_t i = 1;		/* because the terminating \0 also counts */
    size_t len, olen;
    char buf[30];		/* buffer for converting longs and ints */

    char *s;
    char c;

    p = format;
    q = str;
    while (p && *p) {
	if ((*p == '%') && !flag) {
	    /* swallow the %, switch to % mode and initialize */
	    lflag = 0;
	    flag = 1;
	    asign = 0; /* can be 0, '+' or later ' ' (' ' is unimplemented) */
	    fill = ' ';
	    width = 0;
	    p++;
	} else if (flag) {
	    /* % mode */
	    switch (*p) {
	    case 's': case 'm': {
		    if (*p == 's')
			s = va_arg(ap, char *);
		    else
			s = strerror(errno);
		    olen = len = strlen(s);
		    if (len > (n - i))
			len = n - i;
		    *q = '\0';
		    strncat(q, s, len);
		    p++;
		    q += len;
		    i += olen;
		    flag = 0;
		    break;
		}
	    case 'u':{
		    unsigned long u;
		    if (lflag) {
			u = va_arg(ap, unsigned long);
		    } else {
			u = (unsigned long)va_arg(ap, unsigned int);
		    }
		    fmtu(buf, u);
		    goto printdec;
		}
	    case 'd': case 'i': {
		    long l;
		    if (lflag) {
			l = va_arg(ap, long);
		    } else {
			l = (long)va_arg(ap, int);
		    }
		    fmts(buf, l, asign);
printdec:
		    olen = len = strlen(buf);
		    if (width) {
			int off = !!asign;
			if (len < width) {
			    switch (fill) {
				case ' ':
				    memmove(buf + width - len, buf, len + 1);
				    memset(buf, fill, width - len);
				    break;
				case '0':
				    memmove(buf + off + width - len, buf + off, len + 1 - off);
				    memset(buf + off, fill, width - len);
				    break;
				default:
				    abort();
			    }
		       }
		       olen = len = strlen(buf);
		    }
		    if (len > (n - i))
			len = n - i;
		    *q = '\0';
		    strncat(q, buf, len);
		    q += len;
		    i += olen;
		    flag = 0;
		    p++;
		    break;
		}
	    case 'l':{
		    /* next argument will be long */
		    lflag = 1;
		    p++;
		    break;
		}
	    case 'c':{
		    c = va_arg(ap, int);
		    flag = 0;
		    if (i < n)
			*q++ = c;
		    i++;
		    p++;
		    break;
		}
	    case '%':{
		    flag = 0;
		    if (i < n)
			*q++ = *p++;
		    i++;
		    break;
		}
	    case '0':
		     if (fill == ' ' && width == 0)
			 fill='0';
		     else
			 width *= 10;
		     p++;
		     break;
	    case '1': case '2': case '3': case '4': case '5':
	    case '6': case '7': case '8': case '9':
		     width = width * 10 + *p - '0';
		     p++;
		     break;
	    case '+':
		     asign = '+';
		     p++;
		     break;
	    default:
		     abort();
		     break;
	    }
	} else {
	    if (i < n) {
		*q++ = *p;
	    }
	    p++;
	    i++;
	}
    }
    va_end(ap);
    *q = '\0';
    return (i - 1);
}

int
ln_snprintf(char *str, size_t n, const char *format, ...)
{
    int r;
    va_list ap;

    va_start(ap, format);
    r = ln_vsnprintf(str, n, format, ap);
    va_end(ap);
    return r;
}

#endif


syntax highlighted by Code2HTML, v. 0.9.1