/*
 * Copyright (c) 2004-2009, Luiz Otavio O Souza <loos.br@gmail.com>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

static const char rcsid[] = "$Id: fmt.c 96 2009-01-20 15:34:19Z loos-br $";

#include <stdarg.h>

#include "string.h"
#include "fmt.h"

int
need_escape(register char c) {
    if (c == '"' || c == '\'' || c == '\\') {
	return(1);
    }
    return(0);
}

unsigned int
fmt_str(register unsigned char *s, register const char *t) {
 register unsigned int len;
 char ch;

    len = 0;
    if (s) { while ((ch = t[len])) s[len++] = ch; }
    else while (t[len]) len++;
    return len;
}

unsigned int
fmt_chr(register unsigned char *s, const unsigned char c) {
    if (s) *s = c;
    return 1;
}

unsigned int
fmt_sql(register unsigned char *s, register const char *t) {
 register unsigned int len;
 register unsigned int pos;
 char ch;

    pos = 0;
    len = 0;
    if (t == NULL)
	return(len);

    if (s)
	while ((ch = t[pos])) {
	    if (need_escape(ch)) s[len++] = '\\';
	    s[len++] = ch; ++pos;
	}
    else
	while ((ch = t[pos])) {
	    if (need_escape(ch)) len++;
	    len++; ++pos;
	}

    return len;
}

unsigned int
fmt_ulong(register unsigned char *s, register unsigned long u) {
 register unsigned int	len;
 register unsigned long	q;

    len = 1;
    q = u;
    while (q > 9) { ++len; q /= 10; }
    if (s) {
	s += len;
	do { *--s = '0' + (u % 10); u /= 10; } while(u); /* handles u == 0 */
    }
    return len;
}

unsigned int
fmt_u64(register unsigned char *s, register __uint64_t u) {
 register unsigned int  len;
 register __uint64_t	q;

    len = 1;
    q = u;
    while (q > 9) { ++len; q /= 10; }
    if (s) {
        s += len;
        do { *--s = '0' + (u % 10); u /= 10; } while(u); /* handles u == 0 */
    }
    return len;
}

unsigned int
fmt_vprintf(unsigned char *s, const char *fmt, va_list ap) {
 unsigned int	len;
 unsigned int	i;
 string		*p;
 char		*S;
 char		*q;
 int		f;
 unsigned char	c;
 unsigned long	d;
 __uint64_t	l;

    f = 0;
    len = 0;

    while (*fmt) {
	switch (*fmt) {

	    case '%':

		if (f == 0)
		    f++;
		else if (f == 1) {
		    i = fmt_str(s, "%"); len += i; if (s) s += i;
		    f = 0;
		}

		break;

	    case 'S':

		if (f == 1) {
		    S = va_arg(ap, char *);
		    if (S) {
			i = fmt_str(s, S); len += i; if (s) s += i;
		    } else {
			i = fmt_str(s, "(null)"); len += i; if (s) s += i;
		    }

		    f = 0;
		    break;
		}

	    case 's':

		if (f == 1) {
		    p = va_arg(ap, string *);
		    if (p && p->len > 0 && p->s) {
			i = fmt_str(s, (char *)p->s); len += i; if (s) s += i;
		    } else {
			i = fmt_str(s, "(null)"); len += i; if (s) s += i;
		    }

		    f = 0;
		    break;
		}

	    case 'c':

		if (f == 1) {
		    c = (unsigned char)va_arg(ap, int);
		    i = fmt_chr(s, c); len += i; if (s) s += i;

		    f = 0;
		    break;
		}

	    case 'q':

		if (f == 1) {
		    q = va_arg(ap, char *);
		    if (q) {
			i = fmt_sql(s, q); len += i; if (s) s += i;
		    } else {
			i = fmt_sql(s, ""); len += i; if (s) s += i;
		    }

		    f = 0;
		    break;
		}

	    case 'd':

		if (f == 1) {

		    d = va_arg(ap, unsigned long);
		    i = fmt_ulong(s, d); len += i; if (s) s += i;

		    f = 0;
		    break;
		}

	    case 'l':

		if (f == 1) {

		    l = va_arg(ap, __uint64_t);
		    i = fmt_u64(s, l); len += i; if (s) s += i;

		    f = 0;
		    break;
		}

	    default:
		f = 0;

		len++;
		if (s) *s++ = *fmt;
	}
	fmt++;
    }
    if (s) *s = 0;
    return(len);
}

unsigned int
fmt_printf(unsigned char *s, const char *fmt, ...) {
 va_list	ap;
 unsigned int	len = 0;

    va_start(ap, fmt);
    len = fmt_vprintf(s, fmt, ap);
    va_end(ap);

    return(len);
}

