
#include <sys/ioctl.h>
#define tcgetsize(fd,ws) ioctl(fd, TIOCGWINSZ, ws)

// ---- cut here -----

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <signal.h>
#include <termios.h>
#include <unistd.h>

static volatile sig_atomic_t vrow;
static volatile sig_atomic_t vcol;
static volatile sig_atomic_t newsize;

static void winch_handler(int signum)
{
    struct winsize ws;
    int sav_errno = errno;

    (void)signum;

    /* set volatile vars to new winsize, or 0 if unavailable or too large */

    if (tcgetsize(STDERR_FILENO, &ws) == -1)
    {
	vrow = vcol = 0;
    }
    else
    {
	if (ws.ws_row <= SIG_ATOMIC_MAX && ws.ws_col <= SIG_ATOMIC_MAX)
	{
	    vrow = ws.ws_row;
	    vcol = ws.ws_col;
	}
	else
	{
	    vrow = vcol = 0;
	}
    }

    newsize = 1;

    errno = sav_errno;
}

int main(void)
{
    struct sigaction sa;
    struct winsize ws;
    sigset_t winch_set;
    char inbuf[512];

    sa.sa_handler = winch_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    sigaction(SIGWINCH, &sa, NULL);

    sigemptyset(&winch_set);
    sigaddset(&winch_set, SIGWINCH);

    raise(SIGWINCH); /* gets the initial winsize */

    for (;;)
    {
	if (fgets(inbuf, sizeof inbuf, stdin) == NULL)
	{
	    if (feof(stdin))
		exit(0);
	    else if (errno == EINTR)
		continue;
	    else
	    {
		perror("Error reading stdin");
		exit(1);
	    }
	}
	else
	{
	    if (newsize)
	    {
		/* prevent updates to volatile vars while we read them */
		sigprocmask(SIG_BLOCK, &winch_set, NULL);
		ws.ws_row = vrow;
		ws.ws_col = vcol;
		sigprocmask(SIG_UNBLOCK, &winch_set, NULL);
		newsize = 0;
	    }
	    printf("row = %3hu, col = %3hu\n", ws.ws_row, ws.ws_col);

	    /* process inbuf ... */
	}
    }
}

