aboutsummaryrefslogblamecommitdiffstats
path: root/mongoclock.c
blob: 6218d5f42dddf43dd79b01cfca2f6071dd492945 (plain) (tree)
1
2
3
4
5
6
7
8
9



                                                         
                  


                   
                   
                   
                    

                   


                       
 

                















                    
 







                                                    
            
                          



           
                                                   


                














                            















                                       
















                                                                          

                         
 
                               
                      
                          


                               





                           
 



                                         

                         




























                                                                                            



                                     















                                                                    
                                         





                                                            
     





                                       
      








                                                        


                                         


























































                                                                                            



                                           








                            

                                                      

                                     

                                               



































                                                                           
























                                                                      


                                                                           

                                                  



                                                        
                       
                          
 
     
                                             

                                                  







                                                                

                               

                 
/* See LICENSE file for copyright and license details. */
#include <sys/ioctl.h>
#include <sys/timerfd.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <time.h>
#include <unistd.h>
#ifdef USE_ADJTIMEX
# include <sys/timex.h>
#endif

#include "arg.h"

#define DX 16
#define DY 12
#define DC 16

#include "mongo_0.h"
#include "mongo_1.h"
#include "mongo_2.h"
#include "mongo_3.h"
#include "mongo_4.h"
#include "mongo_5.h"
#include "mongo_6.h"
#include "mongo_7.h"
#include "mongo_8.h"
#include "mongo_9.h"
#include "mongo_c.h"


static const char **mongo_ds[] = {
	mongo_0, mongo_1, mongo_2, mongo_3, mongo_4,
	mongo_5, mongo_6, mongo_7, mongo_8, mongo_9
};

static volatile sig_atomic_t caught_sigterm = 0;
static volatile sig_atomic_t caught_sigwinch = 1;

char *argv0;
static int exit_value = 0;

static void
usage(void)
{
	fprintf(stderr, "usage: %s [-s]\n", argv0);
	exit(1);
}

static void
sigterm(int signo)
{
	caught_sigterm = 1;
	(void) signo;
}

static void
sigwinch(int signo)
{
	caught_sigwinch = 1;
	(void) signo;
}

static void
sigio(int signo)
{
	char c;
	ssize_t r;
	r = read(STDIN_FILENO, &c, 1);
	if (r <= 0) {
		if (r < 0)
			exit_value = 1;
		caught_sigterm = 1;
	} else if (c == 'q') {
		caught_sigterm = 1;
	}
	(void) signo;
}

static void
print_time(const char ***str, size_t y, size_t x)
{
	size_t r, c;

	fprintf(stdout, "\033[%zu;1H\033[1J", y + 1);

	for (r = 0; r < DY; r++) {
		fprintf(stdout, "\033[%zu;%zuH\033[1K", y + r + 1, x + 1);
		for (c = 0; str[c]; c++)
			fprintf(stdout, "%s", str[c][r]);
		fprintf(stdout, "\033[0K");
	}

	fprintf(stdout, "\033[0J");
	fflush(stdout);
}

static int
display_time(int timerfd)
{
	const char **digits[9];
	int small = 0;
	uint64_t _overrun;
	struct winsize winsize;
	size_t x = 0, y = 0;
	struct tm *now;
#ifdef USE_ADJTIMEX
	struct timex timex;
	int r;
#else
	time_t now_;
#endif

#ifdef USE_ADJTIMEX
	memset(&timex, 0, sizeof(timex));
#endif

	digits[8] = NULL;

	while (!caught_sigterm) {
		if (caught_sigwinch) {
			if (ioctl(STDOUT_FILENO, (unsigned long)TIOCGWINSZ, &winsize) < 0) {
				if (errno == EINTR)
					continue;
				goto fail;
			}
			caught_sigwinch = 0;
			y = winsize.ws_row;
			x = winsize.ws_col;
			if      (y < DY)              small = 2;
			else if (x < 4 * DX + DC)     small = 2;
			else if (x < 6 * DX + 2 * DC) small = 1;
			else                          small = 0;
			y -= DY;
			x -= 4 * DX + DC;
			if (!small)
				x -= 2 * DX + DC;
			y /= 2;
			x /= 2;
		}

		if (small == 2) {
			fprintf(stdout, "\033[H\033[2J%s\n", "Screen is too small");
			fflush(stdout);
			pause();
			continue;
		}

#ifdef USE_ADJTIMEX
		r = adjtimex(&timex);
		if (r == -1)
			goto fail;
		if (timex.time.tv_sec % (24 * 60 * 60) == 0) {
			if (r == TIME_INS) {
				timex.time.tv_sec -= 1;
				now = localtime(&timex.time.tv_sec);
				if (!now)
					goto fail;
				now->tm_sec += 1;
				goto now_checked;
			} else if (r == TIME_DEL) {
				timex.time.tv_sec += 1;
				now = localtime(&timex.time.tv_sec);
			} else {
				now = localtime(&timex.time.tv_sec);
			}
		} else if (r == TIME_OOP) {
			now = localtime(&timex.time.tv_sec);
			now->tm_sec += 1;
		} else {
			now = localtime(&timex.time.tv_sec);
		}
		if (!now)
			goto fail;
	now_checked:
#else
		now_ = time(NULL);
		if (now_ == -1)
			goto fail;
		now = localtime(&now_);
		if (now == NULL)
			goto fail;
#endif

		digits[0] = mongo_ds[now->tm_hour / 10];
		digits[1] = mongo_ds[now->tm_hour % 10];
		digits[2] = mongo_c;
		digits[3] = mongo_ds[now->tm_min / 10];
		digits[4] = mongo_ds[now->tm_min % 10];
		digits[5] = small ? NULL : mongo_c;
		digits[6] = mongo_ds[now->tm_sec / 10];
		digits[7] = mongo_ds[now->tm_sec % 10];

		print_time(digits, y, x);

		if (read(timerfd, &_overrun, sizeof(_overrun)) < 0)
			if (errno != EINTR)
				goto fail;
	}

	return 0;

fail:
	return -1;
}

static int
display_posixtime(int timerfd)
{
	const char **digits[21];
	uint64_t _overrun;
	struct winsize winsize;
	size_t w = 0, h = 0, x, y;
	time_t now;
	size_t first_digit, ndigits;

	digits[20] = NULL;

	while (!caught_sigterm) {
		if (caught_sigwinch) {
			if (ioctl(STDOUT_FILENO, (unsigned long)TIOCGWINSZ, &winsize) < 0) {
				if (errno == EINTR)
					continue;
				goto fail;
			}
			caught_sigwinch = 0;
			h = winsize.ws_row;
			w = winsize.ws_col;
		}

		now = time(NULL);
		if (now < 0)
			goto fail;

		first_digit = 20;
		do {
			if (!first_digit)
				abort();
			digits[--first_digit] = mongo_ds[now % 10];
		} while (now /= 10);
		ndigits = 20 - first_digit;

		if (h < DY || w < ndigits * DX) {
			fprintf(stdout, "\033[H\033[2J%s\n", "Screen is too small");
			fflush(stdout);
			pause();
			continue;
		}

		y = (h - DY) / 2;
		x = (w - ndigits * DX) / 2;
		print_time(&digits[first_digit], y, x);

		if (read(timerfd, &_overrun, sizeof(_overrun)) < 0)
			if (errno != EINTR)
				goto fail;
	}

	return 0;

fail:
	return -1;
}

int
main(int argc, char *argv[])
{
	int timerfd = -1, old_flags = -1, tcset = 0;
	int posixtime = 0, old_sig = 0, owner_set = 0;
	struct itimerspec itimerspec;
	struct sigaction sigact;
	struct f_owner_ex old_owner, new_owner;
	struct termios stty, saved_stty;

	ARGBEGIN {
	case 's':
		posixtime = 1;
		break;
	default:
		usage();
	} ARGEND;

	if (argc)
		usage();

	fprintf(stdout, "\033[?1049h\033[?25l");

	if (clock_gettime(CLOCK_REALTIME, &itimerspec.it_value))
		goto fail;
	itimerspec.it_interval.tv_sec = 1;
	itimerspec.it_interval.tv_nsec = 0;
	itimerspec.it_value.tv_sec += 1;
	itimerspec.it_value.tv_nsec = 0;
	timerfd = timerfd_create(CLOCK_REALTIME, 0);
	if (timerfd < 0)
		goto fail;
	if (timerfd_settime(timerfd, TFD_TIMER_ABSTIME, &itimerspec, NULL))
		goto fail;

	memset(&sigact, 0, sizeof(sigact));

	sigact.sa_handler = sigterm;
	sigaction(SIGTERM, &sigact, NULL);
	sigaction(SIGQUIT, &sigact, NULL);
	sigaction(SIGINT, &sigact, NULL);

	sigact.sa_handler = sigwinch;
	sigaction(SIGWINCH, &sigact, NULL);

	sigact.sa_handler = sigio;
	sigaction(SIGIO, &sigact, NULL);
	sigaction(SIGURG, &sigact, NULL);

	if (fcntl(STDIN_FILENO, F_GETOWN_EX, &old_owner))
		goto fail;
	memset(&new_owner, 0, sizeof(new_owner));
	new_owner.type = F_OWNER_PID;
	new_owner.pid = getpid();
	if (fcntl(STDIN_FILENO, F_SETOWN_EX, &new_owner))
		goto fail;
	owner_set = 1;
	old_flags = fcntl(STDIN_FILENO, F_GETFL);
	fcntl(STDIN_FILENO, F_SETFL, old_flags | FASYNC | O_NONBLOCK);
	fcntl(STDIN_FILENO, F_GETSIG, &old_sig);
	if (old_sig)
		fcntl(STDIN_FILENO, F_SETSIG, 0);

	if (!tcgetattr(STDIN_FILENO, &stty)) {
		saved_stty = stty;
		stty.c_lflag &= (tcflag_t)~(ECHO | ICANON);
		tcsetattr(STDIN_FILENO, TCSAFLUSH, &stty);
		tcset = 1;
	}

	if (posixtime ? display_posixtime(timerfd) : display_time(timerfd))
		goto fail;

	fprintf(stdout, "\033[?25h\n\033[?1049l");
	fflush(stdout);
	fcntl(STDIN_FILENO, F_SETOWN_EX, &old_owner);
	fcntl(STDIN_FILENO, F_SETFL, old_flags);
	fcntl(STDIN_FILENO, F_SETSIG, old_sig);
	tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_stty);
	close(timerfd);
	return exit_value;

fail:
	perror(argv0 ? argv0 : "mongoclock");
	fprintf(stdout, "\033[?25h\n\033[?1049l");
	fflush(stdout);
	if (owner_set)
		fcntl(STDIN_FILENO, F_SETOWN_EX, &old_owner);
	if (old_flags != -1)
		fcntl(STDIN_FILENO, F_SETFL, old_flags);
	if (old_sig)
		fcntl(STDIN_FILENO, F_SETSIG, old_sig);
	if (tcset)
		tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_stty);
	if (timerfd >= 0)
		close(timerfd);
	return 1;
}