snore.c (2848B)
1 /* See LICENSE for license details. */ 2 #include <errno.h> 3 #include <limits.h> 4 #include <stdarg.h> 5 #include <stdio.h> 6 #include <stdlib.h> 7 #include <string.h> 8 #include <time.h> 9 10 #define BILLION 1000000000.0; 11 #define TICK 10000 12 #define CLEAR "\33[2K\r" 13 #define LENGTH(X) (sizeof X / sizeof X[0]) 14 #define ISCHR(c) (c >= 'a' && c <= 'z') 15 16 typedef struct symbol_t { 17 char sym; 18 int mult; 19 int precision; 20 } Symbol; 21 22 void die(const char *errstr, ...); 23 int sleepu(double usec); 24 double time_to_sec(char *s); 25 void time_print(double tm); 26 27 /* must be in ascending order */ 28 static Symbol symbols[] = { 29 /* symbol multiplier precision */ 30 { 's', 1, 3 }, /* first is default (if no suffix) */ 31 { 'm', 60, 0 }, 32 { 'h', 3600, 0 }, 33 { 'd', 86400, 0 }, /* last is default (if no arguments) */ 34 }; 35 36 void 37 die(const char *errstr, ...) { 38 va_list ap; 39 40 va_start(ap, errstr); 41 vfprintf(stderr, errstr, ap); 42 va_end(ap); 43 exit(1); 44 } 45 46 int 47 sleepu(double usec) { 48 struct timespec req, rem; 49 int r; 50 51 req.tv_sec = 0; 52 req.tv_nsec = usec * 1000; 53 while((r = nanosleep(&req, &rem)) == -1 && errno == EINTR) 54 req = rem; 55 return r; 56 } 57 58 double 59 time_to_sec(char *s) { 60 double calculated = 0.0, part; 61 char *parse_end, *string_end; 62 int j; 63 64 string_end = s + strlen(s); 65 while(s < string_end) { 66 part = strtod(s, &parse_end); 67 if(parse_end == s) { 68 /* error parsing float */ 69 return -1; 70 } 71 s = parse_end; 72 if(s < string_end && ISCHR(s[0])) { 73 for(j = 0; j < LENGTH(symbols); ++j) { 74 if(s[0] == symbols[j].sym) { 75 part *= symbols[j].mult; 76 if(part >= INT_MAX) 77 return -1; 78 s++; 79 break; 80 } 81 } 82 } 83 calculated += part; 84 } 85 return calculated; 86 } 87 88 void 89 time_print(double tm) { 90 double piece; 91 int i; 92 93 for(i = LENGTH(symbols) - 1; i >= 0; --i) { 94 piece = tm / symbols[i].mult; 95 if(!symbols[i].precision) 96 piece = (int)piece; 97 printf("%.*f%c%s", symbols[i].precision, piece, symbols[i].sym, i ? " " : ""); 98 tm -= piece * symbols[i].mult; 99 } 100 } 101 102 int 103 main(int argc, char *argv[]) { 104 struct timespec start, current; 105 double endtm = 0, tm; 106 int i; 107 108 clock_gettime(CLOCK_REALTIME, &start); 109 if(argc == 2 && !strcmp("-v", argv[1])) 110 die("snore-"VERSION"\n"); 111 if(argc == 1) { 112 endtm = symbols[LENGTH(symbols) - 1].mult; 113 } 114 else { 115 for(i = 1; i < argc; ++i) { 116 tm = time_to_sec(argv[i]); 117 if(tm < 0) 118 die("%s: wrong time\n", argv[i]); 119 endtm += tm; 120 if(endtm >= INT_MAX) 121 die("%s: time too large\n", argv[0]); 122 } 123 } 124 125 for(tm = 0; tm < endtm; ) { 126 time_print(tm); /* ascending */ 127 printf(" | "); 128 time_print(endtm - tm); /* descending */ 129 fflush(stdout); 130 sleepu(TICK); 131 printf(CLEAR); 132 clock_gettime(CLOCK_REALTIME, ¤t); 133 tm = (current.tv_sec - start.tv_sec) + (current.tv_nsec - start.tv_nsec) / BILLION; 134 } 135 time_print(tm); 136 printf("\n"); 137 return 0; 138 }