/*
 * 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: io.c 96 2009-01-20 15:34:19Z loos-br $";

#include <sys/select.h>

#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

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

int
io_read(const int fd, register char *buf, register int len) {
 register int r;

    for (;;) {

	r = read(fd, buf, len);
	if (r == -1) { if (errno == EINTR) continue; }
	return r;
    }
}

int
io_write(register int fd, register const char *buf, register int len) {
 register int w;

    while(len) {
	w = write(fd, buf, len);
	if (w == -1) {
	    if (errno == EAGAIN) return(-2);
	    if (errno == EINTR) continue;
	    return(-1);
	}
	if (w == 0) ; /* luser's fault */
	buf += w;
	len -= w;
    }
    return(0);
}

int
timeoutread(int t, int fd, char *buf, int len) {
 fd_set rfds;
 struct timeval tv;

    tv.tv_sec = t;
    tv.tv_usec = 0;

    FD_ZERO(&rfds);
    FD_SET(fd,&rfds);

    if (select(fd + 1,&rfds,(fd_set *) 0,(fd_set *) 0,&tv) == -1)
	return(-1);
    if (FD_ISSET(fd,&rfds))
	return(read(fd,buf,len));

    errno = ETIMEDOUT;
    return(-1);
}

int
timeoutwrite(int t, int fd, char *buf, int len) {
 fd_set wfds;
 struct timeval tv;

    tv.tv_sec = t;
    tv.tv_usec = 0;

    FD_ZERO(&wfds);
    FD_SET(fd,&wfds);

    if (select(fd + 1,(fd_set *) 0,&wfds,(fd_set *) 0,&tv) == -1)
	return(-1);
    if (FD_ISSET(fd,&wfds))
	return(write(fd,buf,len));

    errno = ETIMEDOUT;
    return(-1);
}

int
io_vprintf(int fd, const char *fmt, va_list ap) {
 string		str;
 string		*s;
 char		*S;
 int		len = 0;
 int		f = 0;
 unsigned char	c;
 unsigned long	d;
 __uint64_t	l;

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

	    case '%':

		if (f == 0)
		    f++;
		else if (f == 1) {
		    if (io_write(fd, "%", 1) == -1)
			return(-1);
		    len++;
		}

		break;

	    case 'S':

		if (f == 1) {
		    S = va_arg(ap, char *);
		    if (S) {
			if (io_write(fd, S, strlen(S)) == -1)
			    return(-1);
			len += strlen(S);
		    } else {
			if (io_write(fd, "(null)", 6) == -1)
			    return(-1);
			len += 6;
		    }
		    f = 0;
		    break;
		}

	    case 's':

		if (f == 1) {
		    s = va_arg(ap, string *);
		    if (s && s->len > 0 && s->s) {
			if (io_write(fd, (char *)s->s, s->len) == -1)
			    return(-1);
			len += s->len;
		    } else {
			if (io_write(fd, "(null)", 6) == -1)
			    return(-1);
			len += 6;
		    }
		    f = 0;
		    break;
		}

	    case 'c':

		if (f == 1) {
		    c = (unsigned char)va_arg(ap, int);
		    if (io_write(fd, (char *)&c, 1) == -1)
			return(-1);
                    f = 0;
                    break;
		}

	    case 'p':

		if (f == 1) {
		    s = va_arg(ap, string *);
		    if (s && s->p) {
			if (io_write(fd, (char *)s->p, s->len) == -1)
			    return(-1);
			len += s->len;
		    } else {
			if (io_write(fd, "(null)", 6) == -1)
			    return(-1);
			len += 6;
		    }
		    f = 0;
		    break;
		}

	    case 'd':

		if (f == 1) {
		    d = va_arg(ap, unsigned long);

		    str_zero(&str);
		    str.a = fmt_ulong((unsigned char *)0, d) + 1;
		    if ((str.s = (unsigned char *)
				malloc(sizeof(unsigned char) * str.a)) == 0)
			exit(51);
		    str.len = fmt_ulong(str.s, d);

		    len += str.len;
		    if (io_write(fd, (char *)str.s, str.len) == -1) {
			str_free(&str);
			return(-1);
		    }
		    str_free(&str);

		    f = 0;
		    break;
		}

	    case 'l':

		if (f == 1) {
		    l = va_arg(ap, __uint64_t);

		    str_zero(&str);
		    str.a = fmt_u64((unsigned char *)0, l) + 1;
		    if ((str.s = (unsigned char *)
				malloc(sizeof(unsigned char) * str.a)) == 0)
			exit(51);
		    str.len = fmt_u64(str.s, l);

		    len += str.len;
		    if (io_write(fd, (char *)str.s, str.len) == -1) {
			str_free(&str);
			return(-1);
		    }
		    str_free(&str);

		    f = 0;
		    break;
		}

	    default:
		if (io_write(fd, fmt, 1) == -1)
		    return(-1);
		len++;
		f = 0;
	}
	fmt++;
    }
    return(len);
}

int
io_printf(int fd, const char *fmt, ...) {
 va_list	ap;
 int		len = 0;

    va_start(ap, fmt);
    len = io_vprintf(fd, fmt, ap);
    va_end(ap);

    return(len);
}
