#define _GNU_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <err.h>
#include <errno.h>

static int outputToTrash = 0;

static void
sigsegvHandler(int sig)
{
    int x;

    printf("Caught signal %d (%s)\n", sig, strsignal(sig));
    printf("Top of handler stack near    : %10p\n", (void *) &x);
    fflush(NULL);

    _exit(EXIT_FAILURE);
}

static void
overflowStack(int callNum)
{
    char a[100000];         // make this stack frame large

    if (!outputToTrash)
        printf("\tcall %4d - top of stack near: %10p\n", callNum, &a[0]);
    overflowStack(callNum+1);
}

void
static usage(void)
{
    fprintf(stderr, "usage: %s [-hst]\n", program_invocation_short_name);
    fprintf(stderr, "       -h (help)\n");
    fprintf(stderr, "       -s (run SIGSEGV signal handler on stack\n");
    fprintf(stderr, "       -t (send output to trash)");
    exit(EXIT_FAILURE);
}

int
main(int argc, char *argv[])
{
    stack_t sigstack;
    struct sigaction sa;
    int j, c, onStack;

    onStack = 0;
    outputToTrash = 0;
    while ((c = getopt(argc, argv, ":sht")) != -1) {
        switch (c) {
        case 'h':
            usage();
            break;      /* UNREACHABLE */
        case 's':
            onStack = 1;
            break;
        case 't':
            outputToTrash = 1;
            break;
        case '?':
            errx(EXIT_FAILURE, "unknown option %c", optopt);
        case ':':
            errx(EXIT_FAILURE, "%c needs required argument", optopt);
        }
    }

    printf("Top of standard stack is near: %10p\n", (void *) &j);

    printf("First Program break is at    : %10p\n", (char *) sbrk(0) - 1);
    if (onStack) {
        sigstack.ss_sp = malloc(SIGSTKSZ);
        if (sigstack.ss_sp == NULL)
            err(EXIT_FAILURE, "malloc");
        sigstack.ss_size = SIGSTKSZ;

        sigstack.ss_flags = 0;
        if (sigaltstack(&sigstack, NULL) == -1)
            err(EXIT_FAILURE, "sigaltstack");
        printf("Alternate stack is at        : %10p\n", sigstack.ss_sp);
        printf("Program break is at          : %10p\n", (char *) sbrk(0) - 1);
    }

    sa.sa_handler = sigsegvHandler;
    sigemptyset(&sa.sa_mask);
    if (onStack)
        sa.sa_flags = SA_ONSTACK;       /* Handler uses alternate stack */
    if (sigaction(SIGSEGV, &sa, NULL) == -1)
        err(EXIT_FAILURE, "sigaction");
    overflowStack(1);
}

