From d732b8cea5f301e6879bc2321a777490c8df9d27 Mon Sep 17 00:00:00 2001 From: Tomasz Kramkowski Date: Sun, 24 May 2015 20:25:56 +0100 Subject: New and improved timer. --- timer.c | 196 +++++++++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 162 insertions(+), 34 deletions(-) (limited to 'timer.c') diff --git a/timer.c b/timer.c index fa46885..d32c9b1 100644 --- a/timer.c +++ b/timer.c @@ -10,70 +10,198 @@ * along with this program. If not, see [http://www.gnu.org/licenses/]. */ +#include +#include +#include +#include #include #include #include +#include #include #include +#include -unsigned long int get_seconds(char *); +#define SEC_SECOND 1 +#define SEC_MINUTE 60 +#define SEC_HOUR 3600 +#define SEC_DAY 86400 +#define SEC_MONTH 2629746 +#define SEC_YEAR 31556940 + +static const unsigned long interval_nsec = 1000000000 / 4; + +unsigned long int get_seconds(char *code) +{ + int length = strlen(code), multiplier = 0; + char suffix = code[length - 1], value[length + 1]; + unsigned long retval; + + if (length < 2) { + return 0; + } + + switch (suffix) { + case 's': multiplier = SEC_SECOND; break; // 1 second + case 'm': multiplier = SEC_MINUTE; break; // 1 minute + case 'h': multiplier = SEC_HOUR; break; // 1 hour + case 'D': multiplier = SEC_DAY; break; // 1 day + case 'M': multiplier = SEC_MONTH; break; // 30.4368 days + case 'Y': multiplier = SEC_YEAR; break; // 365.242 days + default : return 0; + } + + strncpy(value, code, length + 1); + + value[length - 1] = '\0'; + + errno = 0; + + retval = strtoul(value, NULL, 10) * multiplier; + + if (retval == ULONG_MAX && errno != 0) + error(1, errno, "Error converting time specifier %s", code); + + return retval; +} + +void clear_line(void) +{ + struct winsize ws; + + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1) + error(1, errno, "IOCTL failed"); + + if (ws.ws_col <= 1) + return; + + for (unsigned short i = 0; i < ws.ws_col - 1; i++) + putchar(' '); + + putchar('\r'); + + if (fflush(stdout) != 0) + error(1, errno, "Failed to flush stdout"); +} + + +void print_time(unsigned long total_sec, bool show_colon) +{ + unsigned long years, months, days, hours, minutes, seconds; + char colon = show_colon ? ':' : ' '; + + years = total_sec / SEC_YEAR; + total_sec %= SEC_YEAR; + + months = total_sec / SEC_MONTH; + total_sec %= SEC_MONTH; + + days = total_sec / SEC_DAY; + total_sec %= SEC_DAY; + + hours = total_sec / SEC_HOUR; + total_sec %= SEC_HOUR; + + minutes = total_sec / SEC_MINUTE; + total_sec %= SEC_MINUTE; + + seconds = total_sec / SEC_SECOND; + total_sec %= SEC_SECOND; + + if (total_sec != 0) + error(1, 0, "An error occured during time formatting"); + + printf(" %luY %luM %luD %.2lu%c%.2lu%c%.2lu\r", years, months, + days, hours, colon, minutes, colon, seconds); + + if (fflush(stdout) != 0) + error(1, errno, "Failed to flush stdout"); +} void usage(char *cmd) { - printf("Usage:\n" - "\t%s {s,m,h,D,M,Y} ...\n", cmd); + printf("Usage:\n\t%s {s,m,h,D,M,Y} ...\n", cmd); } int main(int argc, char **argv) { + bool blink = false; + int sig; + sigset_t sigset; + struct itimerspec its; + struct sigevent sev; + timer_t timerid; + + unsigned long total_seconds = 0; + if (argc < 2) { printf("Not enough arguments.\n"); usage(argv[0]); exit(1); } - unsigned long int total_seconds = 0; - for (int i = 1; i < argc; i++) total_seconds += get_seconds(argv[i]); - printf("Total time: %lu second(s).\nStarted at: %ld\n", total_seconds, time(NULL)); + sev.sigev_notify = SIGEV_SIGNAL; + sev.sigev_signo = SIGRTMIN; + sev.sigev_value.sival_ptr = &timerid; - sleep(total_seconds); + if (timer_create(CLOCK_MONOTONIC, &sev, &timerid) != 0) + error(1, errno, "Could not create timer"); - printf("Ended at: %ld\n", time(NULL)); + its = (struct itimerspec){ + {0, interval_nsec}, + {0, interval_nsec} + }; - while (1) { - printf("Ring!\a\n"); - sleep(1); - } + if (sigemptyset(&sigset) != 0) + error(1, errno, "Could not empty signal set"); - return 0; -} + if (sigaddset(&sigset, SIGRTMIN) != 0) + error(1, errno, "Could not add SIGRTMIN to signal set"); -unsigned long int get_seconds(char *code) -{ - int length = strlen(code); - if (length < 2) { - return 0; - } + if (sigprocmask(SIG_BLOCK, &sigset, NULL) != 0) + error(1, errno, "Could not block SIGRTMIN"); - int multiplier = 0; - char suffix = code[length - 1]; - switch (suffix) { - case 's': multiplier = 1; break; // 1 second - case 'm': multiplier = 60; break; // 1 minute - case 'h': multiplier = 3600; break; // 1 hour - case 'D': multiplier = 86400; break; // 1 day - case 'M': multiplier = 2629746; break; // 30.4368 days - case 'Y': multiplier = 31556940; break; // 365.242 days - default : return 0; + if (timer_settime(timerid, 0, &its, NULL) != 0) + error(1, errno, "Could not set timer"); + + for (unsigned long i = 0; i < total_seconds; i++) + for (int ii = 0; ii < 4; ii++) { + if (sigwait(&sigset, &sig), sig != SIGRTMIN) + error(1, 0, "sigwait returned unexpected signal %d", sig); + if (sigaddset(&sigset, SIGRTMIN) != 0) + error(1, errno, "Could not add signal to set"); + + clear_line(); + print_time(total_seconds - i, ii < 2); + } + + if (sigaddset(&sigset, SIGINT) != 0) + error(1, errno, "Could not add SIGINT to signal set"); + + while (true) { + blink = !blink; + sigwait(&sigset, &sig); + if (sig != SIGRTMIN) + break; + + if (sigaddset(&sigset, SIGRTMIN) != 0) + error(1, errno, "Could not add signal to set"); + + clear_line(); + + if (blink) + printf(" -- BEEP -- \a\r"); + + if (fflush(stdout) != 0) + error(1, errno, "Failed to flush stdout"); } - char value[length + 1]; - strncpy(value, code, length + 1); + timer_delete(timerid); - value[length - 1] = '\0'; + clear_line(); - return strtoul(value, NULL, 10) * multiplier; + return 0; } -- cgit v1.2.3-54-g00ecf