#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <err.h>
#include <errno.h>
#include <limits.h>
#include <ctype.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "lpi.h"

char *
toBase(unsigned long long n, int b, int space, int group, char delim)
{
    static char buf[BUFSIZ];
    char        digits[] = "0123456789abcdef";
    int         i, d;

    if (b > 16 || b < 2) {
        warnx("base must be >= 2 and <= 16");
        return NULL;
    }

    memset(buf, 0, sizeof(buf));
    i = 0;
    d = 0;
    do {
        buf[i++] = digits[n % b];
        d++;                // converted digit so far
        if (group && d%group == 0)
            buf[i++] = delim;
    } while (n /= b);
    if (buf[i-1] == ' ')
        i--;
    buf[i] = '\0';

    if (space > 0 && d < space) {
        space -= d;
        int j = 0;          // zero inserted
        while (space--) {
            if (group && (d+j)%group == 0)
                buf[i++] = delim;
            buf[i++] = '0';
            j++;            // another zero inserted
        }
        buf[i] = '\0';
    }

    return reverse(buf);
}

char *
reverse(char *s)
{
    int i, j;
    char t;

    for (i = 0, j = strlen(s) - 1; i < j; i++, j--) {
        t = s[j];
        s[j] = s[i];
        s[i] = t;
    }

    return s;
}

char *
chomp(char *s)
{
    int i;

    i = strlen(s) - 1;
    if (s[i] == '\n')
        s[i] = '\0';
    return s;
}

char **
toArray(char *s, const char *delim)
{
    static char *v[10];
    char *t;
    int i;

    i = 0;
    memset(v, 0, sizeof(v));

    while ((t = strtok(s, delim)) != NULL) {
        if (i+1 >= NELEM(v)) {
            warnx("array overflow");
            break;
        }
        v[i++] = t;
        s = NULL;
    }
    v[i] = NULL;
    return v;
}

int
arrayLength(void **v)
{
    int i;

    i = 0;
    while (*v++ != NULL)
        i++;
    return i;
}

int
toInt(const char *s, int *num)
{
    int err;
    long tmp;
    int oldnum;

    oldnum = *num;   // save nums's original value
    tmp = *num;
    if ((err = toLong(s, (long *) &tmp)) != 0) {
        *num = oldnum;
        return err;
    }

    *num = (int) tmp;
    if (tmp > INT_MAX || tmp < INT_MIN) {
        *num = oldnum;
        return ERANGE;
    }
    return 0;
}

int
toLong(const char *s, long *num)
{
    char *endptr;
    long oldnum;

    oldnum = *num;  // save num's orignial value
    errno = 0;
    *num = strtol(s, &endptr, 0);
    if (errno) {
        *num = oldnum;      // on error restore old value of num
        return errno;
    }

    if (*endptr != '\0') {
        *num = oldnum;      // on error restore old value of num
        return EINVAL;
    }

    return errno;
}

int
toLongLong(const char *s, long long *num)
{
    char *endptr;
    long long oldnum;

    oldnum = *num;  // save num's orignial value
    errno = 0;
    *num = strtoll(s, &endptr, 0);
    if (errno) {
        *num = oldnum;      // on error restore old value of num
        return errno;
    }

    if (*endptr != '\0') {
        *num = oldnum;      // on error restore old value of num
        return EINVAL;
    }

    return errno;
}

char *
trim(char *s)
{
    rtrim(s);
    ltrim(s);
    return s;
}

char *
ltrim(char *s)
{
    int i, j;

    for (i = 0; i < strlen(s) && isspace(s[i]); i++)
        ;
    for (j = 0; i <= strlen(s); )
        s[j++] = s[i++];
    return s;
}

char *
rtrim(char *s)
{
    int i;

    for (i = strlen(s) - 1; i >=0 && isspace(s[i]); i--)
        ;
    s[i+1] = '\0';
    return s;
}

int
printFile(const char *filename)
{
    int fd;
    char buf[BUFSIZ];
    int n;                      // total bytes read.
    int nread;

    n = 0;
    fd = open(filename, O_RDONLY);
    if (fd == -1)
        return -1;

    while ((nread = read(fd, buf, sizeof(buf))) > 0) {
        if (write(STDOUT_FILENO, buf, nread) != nread) {
            perror("printFile");
            return -1;
        }
        n += nread;
    }

    if (nread == -1) {
        perror("printFile");
        return -1;
    }

    return n;
}

int
listDir(const char *dirname)
{
    DIR *dir;
    struct dirent *dirent;
    int n;

    if ((dir = opendir(dirname)) == NULL)
        return -1;

    printf("dirfd=%d\n", dirfd(dir));
    errno = 0;
    while ((dirent = readdir(dir)) != NULL) {
        if (strcmp(dirent->d_name, ".") == 0 ||
                strcmp(dirent->d_name, "..") == 0)
            continue;
        printf("%s\n", dirent->d_name);
        n++;
    }
    closedir(dir);
    if (errno)
        return -1;
    return n;
}

off_t
tell(int fd)
{
    return lseek(fd, 0L, SEEK_CUR);
}

ssize_t
printOffset(int fd, off_t offset, size_t len)
{
    char buf[BUFSIZ];

    if (len > BUFSIZ) {
        warnx("buffer to small");
        return -1;
    }

    if (lseek(fd, offset, SEEK_SET) == -1) {
        warnx("hello");
    }

    // Not complete
    return 0;
}

size_t
safePrint(const char *s, int n)
{
    size_t i;

    for (i = 0; i < n && s[i] != '\0'; i++)
        putchar(s[i]);
    return i;
}

int
ddup(int oldfd)
{
    int newfd;

    /* invalid old file descriptor? */
    if (!isValidFd(oldfd)) {
        errno = EBADF;
        return -1;
    }

    newfd = fcntl(oldfd, F_DUPFD, 0);
    if (newfd == -1) {
        return -1;
    }

    return newfd;
}

int
ddup2(int oldfd, int newfd)
{
    int n;

    if (!isValidFd(oldfd)) {
        errno = EBADF;
        return -1;
    }
    if (oldfd == newfd)
        return newfd;

    if (isValidFd(newfd))
        if (close(newfd) == -1) {
            // errno = ?
            return -1;
        }

    if ((n = fcntl(oldfd, F_DUPFD, newfd)) == -1) {
        // errno = ?
        return -1;
    }

    if (n != newfd) {
        close(n);
        // errno = ?
        return -1;
    }

    return n;
}

int
isValidFd(int fd)
{
    if (fcntl(fd, F_GETFL) == -1)
        return 0;
    return 1;
}

char *
userNameFromId(uid_t uid)
{
    struct passwd *pwd;

    pwd = getpwuid(uid);
    return (pwd == NULL) ? NULL : pwd->pw_name;
}

uid_t
userIdFromName(const char *name)
{
    struct passwd *pwd;
    uid_t u;
    char *endptr;

    if (name == NULL || *name == '\0')      // on NULL or empty string
        return -1;                          // return an error

    u = strtol(name, &endptr, 10);
    if (*endptr == '\0')
        return u;

    pwd = getpwnam(name);
    if (pwd == NULL)
        return -1;

    return pwd->pw_uid;
}

char *
groupNameFromId(gid_t gid)
{
    struct group *grp;

    grp = getgrgid(gid);
    return (grp == NULL) ? NULL : grp->gr_name;
}

gid_t
groupIdFromName(const char *name)
{
    struct group *grp;
    gid_t g;
    char *endptr;

    if (name == NULL || *name == '\0')
        return -1;

    g = strtol(name, &endptr, 10);
    if (*endptr == '\0')
        return g;

    grp = getgrnam(name);
    if (grp == NULL)
        return -1;

    return grp->gr_gid;
}

char *
currTime(const char *format)
{
    static char buf[BUFSIZ];      /* Nonreentrant */
    time_t t;
    size_t s;
    struct tm *tm;

    t = time(NULL);
    tm = localtime(&t);
    if (tm == NULL)
        return NULL;

    s = strftime(buf, BUFSIZ, (format != NULL) ? format : "%c", tm);

    return (s == 0) ? NULL : buf;
}

void
onOffBits(unsigned long long *number, int bit, int status)
{
    if (status == ON)
        *number |= ((unsigned long long) 1 << bit);
    else if (status == OFF)
        *number &= ~((unsigned long long) 1 << bit);
}

void
printSigset(FILE *of, const char *prefix, const sigset_t *sigset)
{
    int sig, cnt;

    cnt = 0;
    for (sig = 1; sig < NSIG; sig++) {
        if (sigismember(sigset, sig)) {
            cnt++;
            fprintf(of, "%s%d (%s)\n", prefix ? prefix : "", sig, strsignal(sig));
        }
    }
    if (cnt == 0)
        fprintf(of, "%s<empty signal set>\n", prefix ? prefix : "");
}

int
printSigMask(FILE *of, const char *msg)
{
    sigset_t currMask;

    if (msg != NULL)
        fprintf(of, "%s", msg);

    if (sigprocmask(SIG_BLOCK, NULL, &currMask) == -1)
        return -1;

    printSigset(of, "\t\t", &currMask);

    return 0;
}

int
printPendingSigs(FILE *of, const char *msg)
{
    sigset_t pendingSigs;

    if (msg != NULL)
        fprintf(of, "%s", msg);

    if (sigpending(&pendingSigs) == -1)
        return -1;

    printSigset(of, "\t\t", &pendingSigs);

    return 0;
}

void
printWaitStatus(const char *msg, int status)
{
    if (msg != NULL)
        printf("%s", msg);

    if (WIFEXITED(status)) {
        printf("child exited, status=%d\n", WEXITSTATUS(status));
    } else if (WIFSIGNALED(status)) {
        printf("child killed by signal %d (%s)",
                WTERMSIG(status), strsignal(WTERMSIG(status)));
#ifdef WCOREDUMP        /* Not in SUSv3, may be asent on some systems */
        if (WCOREDUMP(status))
            printf(" (core dumped)");
#endif
        printf("\n");
    } else if (WIFSTOPPED(status)) {
        printf("child stopped by signal %d (%s)\n",
                WSTOPSIG(status), strsignal(WSTOPSIG(status)));

#ifdef WIFCONTINUED     /* SUSv3 has this, but older linux versions and
                           some other UNIX implementations don't */
    } else if (WIFCONTINUED(status)) {
        printf("child continued\n");
#endif
    } else {            /* should never happen */
        printf("what happend to this child? (status=%#x)\n",
                (unsigned int) status);
    }
}
