aboutsummaryrefslogtreecommitdiffstats
path: root/timer.c
diff options
context:
space:
mode:
Diffstat (limited to 'timer.c')
-rw-r--r--timer.c196
1 files changed, 162 insertions, 34 deletions
diff --git a/timer.c b/timer.c
index 506af50..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 <errno.h>
+#include <error.h>
+#include <signal.h>
+#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/ioctl.h>
#include <time.h>
#include <unistd.h>
+#include <limits.h>
-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 <n>{s,m,h,D,M,Y} ...\n", cmd);
+ printf("Usage:\n\t%s <n>{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: %d\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: %d\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;
}