/*
 * usage:
 *
 * $ ./a.out 50         # child termination status = 50
 * $ ./a.out -s 2       # child emits signal number 2
 * $ ./a.out 50 waitpid # parent waits for child with waitpid system call
 */

#define _GNU_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <err.h>
#include <errno.h>
#include <sys/wait.h>
#include "../lpi.h"

void usage(void);

int
main(int argc, char *argv[])
{
    int status;
    int signo;
    int c;
    char *syscall;
    pid_t pid;

    signo = 0;
    status = 0;
    syscall = "wait";       /* default wait system call */
    while ((c = getopt(argc, argv, ":s:")) != -1) {
        switch (c) {
        case 's':
            if ((errno = toInt(optarg, &signo)) != 0)
                err(EXIT_FAILURE, "toInt('%s')", optarg);
            if (signo < 1 || signo > 64) {
                fprintf(stderr, "invalid signal number %d\n", signo);
                exit(EXIT_FAILURE);
            }
            printf(">           signal = %#x %d\n", signo, signo);
            break;

        case '?':
            fprintf(stderr, "unknown option: %c\n\n", optopt);
            usage();

        case ':':
            fprintf(stderr, "option -%c requires argument\n\n", optopt);
            usage();

        default:
            fprintf(stderr, "what happend?\n");
            exit(EXIT_FAILURE);
        }
    }

    if (signo == 0) {
        if (optind < argc) {
            if ((errno = toInt(argv[optind], &status)) != 0)
                err(EXIT_FAILURE, "toInt('%s')", argv[optind]);
            optind++;
        }

        printf(">           status = %#x %d\n", status, status);
    }

    if (status > 0 && signo > 0) {
        fprintf(stderr, "status > 0 and signo > 0: this must not happen!");
        exit(EXIT_FAILURE);
    }

    if (optind < argc) {
        if (strcmp(argv[optind], "waitpid") == 0 ||
                strcmp(argv[optind], "wait") == 0) {
            syscall = argv[optind];
        } else {
           warnx("unknown system call. jsut wait and waitpid are allowed");
           usage();
           /* UNREACHABLE */
        }

    }

    switch (pid = fork()) {
    case -1:
        err(EXIT_FAILURE, "fork");

    case 0:
        if (signo)
            raise(signo);
        _exit(status);

    default:
        if (strcmp(syscall, "wait") == 0)
            pid = wait(&status);
        else if (strcmp(syscall, "waitpid") == 0)
            pid = waitpid(pid, &status, WUNTRACED | WCONTINUED);

        printf("-----------------------------------\n");
        printf("           syscall = %s\n", syscall);
        printf("-----------------------------------\n");

        printf(">>          status = %#x %d\n",
                status, status);
        printf(">>     WIFEXITED() = %s\n",
                WIFEXITED(status) ? "true" : "false");
        printf(">>   WEXITSTATUS() = %#x %d\n",
                WEXITSTATUS(status), WEXITSTATUS(status));
        printf(">>    status >> 8  = %#x %d\n",
                status >> 8, status >> 8);
        printf(">>   WIFSIGNALED() = %s\n",
                WIFSIGNALED(status) ? "true" : "false");
        printf(">>      WTERMSIG() = %#x %d (%s)\n",
                WTERMSIG(status),WTERMSIG(status), strsignal(WTERMSIG(status)));
        printf(">>     WCOREDUMP() = %s\n",
                WCOREDUMP(status) ? "true" : "false");
        printf(">>    WIFSTOPPED() = %s\n",
                WIFSTOPPED(status) ? "true" : "false");
        printf(">>      WSTOPSIG() = %#x %d (%s)\n",
                WSTOPSIG(status),WSTOPSIG(status), strsignal(WSTOPSIG(status)));
        printf(">>  WIFCONTINUED() = %s\n",
                WIFCONTINUED(status) ? "true" : "false");
    }

    exit(EXIT_SUCCESS);
}

void
usage(void)
{
    fprintf(stderr, "usage:\n");
    fprintf(stderr, "       %s\n", program_invocation_short_name);
    fprintf(stderr, "       %s status\n", program_invocation_short_name);
    fprintf(stderr, "       %s -s signal-numberi [wait|waitpid]\n",
            program_invocation_short_name);
    exit(EXIT_FAILURE);
}
