#include <sys/types.h>
#include <sys/socket.h>

#include <netinet/in.h>
#include <arpa/inet.h>

#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <poll.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int getserver(void);
int getsocket(int);
void senddata(int, const char *);
int delay(int, int);

const char defdata[] = "nun";
bool loop = true;


int
main(int argc, char **argv)
{
    int c;
    while ((c = getopt(argc, argv, "1")) != -1) {
	switch (c) {
	case '1':
	    loop = false;
	    break;
	default:
	    fprintf(stderr, "oobserv [-1] [pattern]\n"
		    "   where pattern is a sequnence of\n"
		    "   n - a byte of normal data\n"
		    "   u - a byte of urgent data\n"
		    "   + - a one second delay\n"
		    "   0 - infinite delay\n");
	    return EXIT_FAILURE;
	}
    }

    const char *pattern;
    if (optind < argc)
	pattern = argv[optind];
    else
	pattern = defdata;

    size_t ngood = strspn(pattern, "nu+0");
    if (pattern[ngood] != '\0')
	errx(EXIT_FAILURE, "bad pattern %s", pattern);

    int server = getserver();
    do {
	int client = getsocket(server);
	senddata(client, pattern);
	close(client);
    } while (loop);

    return 0;
}


int
getserver(void)
{
    int status;

    int server = socket(PF_INET, SOCK_STREAM, 0);
    if (server < 0)
	err(EXIT_FAILURE, "socket");

    int one = 1;
    status = setsockopt(server, SOL_SOCKET, SO_REUSEADDR,
			(char *)&one, sizeof(one));
    if (status < 0)
	err(EXIT_FAILURE, "SO_REUSEADDR");


    struct sockaddr_in sin;
    memset(&sin, 0, sizeof(sin));
#if !defined(__linux__) && !defined(__sun__)
    sin.sin_len = sizeof(sin);
#endif
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
    sin.sin_port = htons(12345);

    status = bind(server, (struct sockaddr *)&sin, sizeof(sin));
    if (status < 0)
	err(EXIT_FAILURE, "bind");

    status = listen(server, 1);
    if (status < 0)
 	err(EXIT_FAILURE, "listen");

    return server;
}


int
getsocket(int server)
{
    int client = accept(server, NULL, 0);
    if (client < 0)
	err(EXIT_FAILURE, "accept");

    return client;
}


const char alpha[] = "abcdefghijklmnopqrstuvwxyz";
const size_t nalpha = sizeof(alpha) - 1;

void
senddata(int client, const char *pattern)
{
    const char *s;
    char c;

    size_t i = 0;
    for (s = pattern; (c = *s) != '\0'; ++s) {
	if (c == '+') {
	    if (delay(client, 1000))
		return;
	    continue;
	}
	else if (c == '0') {
	    delay(client, -1);
	    return;
	}

	char d = alpha[i];

	++i;
	if (i == nalpha)
	    i = 0;

	int flags = 0;

	if (c == 'u') {
	    flags |= MSG_OOB;
	    d = toupper(d);
	}

	ssize_t nsent = send(client, &d, 1, flags);
	if (nsent < 0) {
	    warn(flags & MSG_OOB ? "send(MSG_OOB)" : "send");
	    return;
	}

	if (nsent == 0) {
	    warnx("send returned 0");
	    return;
	}
    }
}


int
delay(int s, int timeout)
{
    struct pollfd fds[1];

    fds[0].fd = s;
    fds[0].events = POLLIN | POLLPRI | POLLRDBAND;
    fds[0].revents = 0;

    int nready = poll(fds, 1, timeout);
    if (nready < 0) {
	if (errno != EINTR)
	    warn("poll");
    }

    return nready;
}
