Handling interruption of system calls by signals

/** Example of handling cases when system calls are interrupted by
 * a signal. This program copies its standard input to the standard output.
 * Here read() and write() may be interrupted. Additionally after receiving
 * a signal the program prints how much data it transfered so far.
 */
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
 
static volatile int print_stats = 0;
 
size_t total_read = 0;
size_t total_write = 0;
 
static void hdl (int sig)
{
	print_stats = 1;
}
 
static void do_print_stats (void)
{
	if (print_stats) {
		print_stats = 0;
		fprintf (stderr, "Read: %zd, Write: %zd\n", total_read, total_write);
	}
}
 
int main (int argc, char *argv[])
{
	ssize_t nread;
	ssize_t nwrite;
	struct sigaction act;
 
	memset (&act, 0, sizeof(act));
 
	act.sa_handler = hdl;
 
	if (sigaction(SIGUSR1, &act, NULL) < 0) {
		perror ("sigaction");
		return 1;
	}
 
	do {
		char buf[512];
 
		nread = read (0, buf, sizeof(buf));
		if (nread < 0) {
			if (errno != EINTR) {
				perror ("read");
				return 1;
			}
 
			/* We are here because the read() call was interrupted before
			 * anything was read. */
 
			do_print_stats ();
		}
		else if (nread == 0)
			break;
		else {
			ssize_t written = 0;
			total_read += nread;
 
			while (written < nread) {
				nwrite = write (1, buf + written, nread - written);
				if (nwrite < 0) {
					if (errno != EINTR) {
						perror ("write");
						return 1;
					}
 
					/* We are here because write() call was interrupted
					 * before anything could be written. */
 
				}
				else {
					written += nwrite;
					total_write += nwrite;
				}
 
				do_print_stats ();
			}
		}
 
	} while (1);
 
	return 0;
}