Expose expiry character configuration
[khatus.git] / x5 / khatus.c
CommitLineData
77c76070 1#include <sys/select.h>
bec93767 2#include <sys/stat.h>
4d66492f 3
9b5ebc12
SK
4#include <assert.h>
5#include <ctype.h>
77c76070 6#include <errno.h>
c5d1af8c 7#include <fcntl.h>
4727e38d 8#include <signal.h>
9b5ebc12
SK
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
b7487ec5 12#include <time.h>
c5d1af8c
SK
13#include <unistd.h>
14
15#include <X11/Xlib.h>
9b5ebc12 16
b7487ec5 17#include "bsdtimespec.h"
1084633a
SK
18#include "khlib_log.h"
19#include "khlib_time.h"
b7487ec5 20
544b0835
SK
21#define usage(...) { \
22 print_usage(); \
23 fprintf(stderr, "Error:\n " __VA_ARGS__); \
24 exit(EXIT_FAILURE); \
25}
e6c523cd
SK
26#define ERRMSG "ERROR"
27
ca7a3d6c 28
e6c523cd
SK
29static const char errmsg[] = ERRMSG;
30static const int errlen = sizeof(ERRMSG) - 1;
31
4727e38d
SK
32char *argv0 = NULL;
33int running = 1;
9b5ebc12 34
900b9a6b
SK
35/* TODO: Convert slot list to slot array. */
36typedef struct Slot Slot;
37struct Slot {
38 char *in_fifo;
39 int in_fd;
40 struct timespec in_last_read;
41 struct timespec out_ttl;
42 int out_width;
43 int out_pos_lo; /* Lowest position on the output buffer. */
44 int out_pos_cur; /* Current position on the output buffer. */
45 int out_pos_hi; /* Highest position on the output buffer. */
46 Slot *next;
9b5ebc12
SK
47};
48
4d66492f
SK
49typedef struct Config Config;
50struct Config {
348d5f36 51 double interval;
4d66492f 52 char * separator;
b67b5f05 53 char expiry_character;
900b9a6b
SK
54 Slot * slots;
55 int slot_count;
457f091d
SK
56 int buf_width;
57 int to_x_root;
9b5ebc12
SK
58};
59
efa97b71 60enum read_status {
e6441710
SK
61 END_OF_FILE,
62 END_OF_MESSAGE,
63 RETRY,
64 FAILURE
efa97b71
SK
65};
66
457f091d
SK
67char *
68buf_create(Config *cfg)
69{
70 int seplen;
71 char *buf;
72 Slot *s;
73
74 buf = calloc(1, cfg->buf_width + 1);
75 if (buf == NULL)
76 khlib_fatal(
77 "[memory] Failed to allocate buffer of %d bytes",
78 cfg->buf_width
79 );
80 memset(buf, ' ', cfg->buf_width);
81 buf[cfg->buf_width] = '\0';
82 seplen = strlen(cfg->separator);
83 /* Set the separators */
84 for (s = cfg->slots; s; s = s->next) {
85 /* Skip the first, left-most */
86 if (s->out_pos_lo) {
87 /* Copying only seplen ensures we omit the '\0' byte. */
88 strncpy(
89 buf + (s->out_pos_lo - seplen),
90 cfg->separator,
91 seplen
92 );
93 }
94 }
95 return buf;
96}
97
ca7a3d6c
SK
98Slot *
99slots_rev(Slot *old)
100{
101 Slot *tmp = NULL;
102 Slot *new = NULL;
103
104 while (old) {
105 tmp = old->next;
106 old->next = new;
107 new = old;
108 old = tmp;
109 }
110 return new;
111}
112
77c76070 113void
457f091d 114slot_log(Slot *s)
77c76070 115{
900b9a6b 116 khlib_info("Slot "
a6b13fa2 117 "{"
900b9a6b
SK
118 " in_fifo = %s,"
119 " in_fd = %d,"
120 " out_width = %d,"
121 " in_last_read = {tv_sec = %ld, tv_nsec = %ld}"
122 " out_ttl = {tv_sec = %ld, tv_nsec = %ld},"
123 " out_pos_lo = %d,"
124 " out_pos_cur = %d,"
125 " out_pos_hi = %d,"
a6b13fa2
SK
126 " next = %p,"
127 " }\n",
900b9a6b
SK
128 s->in_fifo,
129 s->in_fd,
130 s->out_width,
131 s->in_last_read.tv_sec,
132 s->in_last_read.tv_nsec,
133 s->out_ttl.tv_sec,
134 s->out_ttl.tv_nsec,
135 s->out_pos_lo,
136 s->out_pos_cur,
137 s->out_pos_hi,
138 s->next
77c76070
SK
139 );
140}
141
142void
457f091d 143slots_log(Slot *head)
77c76070 144{
4727e38d
SK
145 Slot *s = head;
146
147 for (; s; s = s->next) {
457f091d 148 slot_log(s);
77c76070
SK
149 }
150}
151
457f091d
SK
152void
153slots_assert_fifos_exist(Slot *s)
154{
155 struct stat st;
156 int errors = 0;
157
158 for (; s; s = s->next) {
159 if (lstat(s->in_fifo, &st) < 0) {
160 khlib_error(
161 "Cannot stat \"%s\". Error: %s\n",
162 s->in_fifo,
163 strerror(errno)
164 );
165 errors++;
166 continue;
167 }
168 if (!(st.st_mode & S_IFIFO)) {
169 khlib_error("\"%s\" is not a FIFO\n", s->in_fifo);
170 errors++;
171 continue;
172 }
173 }
174 if (errors)
175 khlib_fatal(
176 "Encountered errors with given file paths. See log.\n"
177 );
178}
179
4727e38d
SK
180void
181slot_close(Slot *s)
182{
183 close(s->in_fd);
184 s->in_fd = -1;
185 s->out_pos_cur = s->out_pos_lo;
186}
187
188void
189slots_close(Slot *s)
190{
191 for (; s; s = s->next)
192 if (s->in_fd > -1)
193 slot_close(s);
194}
195
196
4bfac488 197void
b67b5f05 198slot_expire(Slot *s, struct timespec t, char expiry_character, char *buf)
4bfac488
SK
199{
200 struct timespec td;
201
202 timespecsub(&t, &(s->in_last_read), &td);
203 if (timespeccmp(&td, &(s->out_ttl), >=)) {
b67b5f05
SK
204 memset(
205 buf + s->out_pos_lo,
206 expiry_character,
207 s->out_width
208 );
4bfac488
SK
209 khlib_warn("Slot expired: \"%s\"\n", s->in_fifo);
210 }
211}
212
213void
4e3d71e8 214slot_set_error(Slot *s, char *buf)
4bfac488
SK
215{
216 char *b;
217 int i;
218
4e3d71e8 219 s->in_fd = -1;
4bfac488
SK
220 b = buf + s->out_pos_lo;
221 /* Copy as much of the error message as possible.
222 * EXCLUDING the terminating \0. */
223 for (i = 0; i < errlen && i < s->out_width; i++)
224 b[i] = errmsg[i];
225 /* Any remaining positions: */
4e3d71e8 226 memset(b + i, '_', s->out_width - i);
4bfac488
SK
227}
228
229enum read_status
51e63a6f 230slot_read(Slot *s, char *buf)
4bfac488
SK
231{
232 char c; /* Character read. */
233 int r; /* Remaining unused positions in buffer range. */
234
235 for (;;) {
236 switch (read(s->in_fd, &c, 1)) {
237 case -1:
238 khlib_error(
239 "Failed to read: \"%s\". errno: %d, msg: %s\n",
240 s->in_fifo,
241 errno,
242 strerror(errno)
243 );
244 switch (errno) {
245 case EINTR:
246 case EAGAIN:
247 return RETRY;
248 default:
249 return FAILURE;
250 }
251 case 0:
252 khlib_debug("%s: End of FILE\n", s->in_fifo);
253 s->out_pos_cur = s->out_pos_lo;
254 return END_OF_FILE;
255 case 1:
256 /* TODO: Consider making msg term char a CLI option */
257 if (c == '\n' || c == '\0') {
4e3d71e8 258 r = (s->out_pos_hi - s->out_pos_cur) + 1;
4bfac488
SK
259 if (r > 0)
260 memset(buf + s->out_pos_cur, ' ', r);
4bfac488
SK
261 return END_OF_MESSAGE;
262 } else {
263 if (s->out_pos_cur <= s->out_pos_hi)
264 buf[s->out_pos_cur++] = c;
51e63a6f
SK
265 else
266 /*
267 * Force EOM beyond available range.
268 * To ensure that a rogue large message
269 * doesn't trap us here needlessly
270 * long.
271 */
272 return END_OF_MESSAGE;
4bfac488
SK
273 }
274 break;
275 default:
276 assert(0);
277 }
278 }
279}
280
281void
282slots_read(Config *cfg, struct timespec *ti, char *buf)
283{
284 fd_set fds;
285 int maxfd = -1;
286 int ready = 0;
287 struct stat st;
288 struct timespec t;
289 Slot *s;
290
291 FD_ZERO(&fds);
292 for (s = cfg->slots; s; s = s->next) {
293 /* TODO: Create the FIFO if it doesn't already exist. */
294 if (lstat(s->in_fifo, &st) < 0) {
295 khlib_error(
296 "Cannot stat \"%s\". Error: %s\n",
297 s->in_fifo,
298 strerror(errno)
299 );
4e3d71e8 300 slot_set_error(s, buf);
4bfac488
SK
301 continue;
302 }
303 if (!(st.st_mode & S_IFIFO)) {
304 khlib_error("\"%s\" is not a FIFO\n", s->in_fifo);
4e3d71e8 305 slot_set_error(s, buf);
4bfac488
SK
306 continue;
307 }
308 if (s->in_fd < 0) {
309 khlib_debug(
310 "%s: closed. opening. in_fd: %d\n",
311 s->in_fifo,
312 s->in_fd
313 );
314 s->in_fd = open(s->in_fifo, O_RDONLY | O_NONBLOCK);
315 } else {
316 khlib_debug(
317 "%s: already openned. in_fd: %d\n",
318 s->in_fifo,
319 s->in_fd
320 );
321 }
322 if (s->in_fd == -1) {
323 /* TODO Consider backing off retries for failed slots */
324 khlib_error("Failed to open \"%s\"\n", s->in_fifo);
4e3d71e8 325 slot_set_error(s, buf);
4bfac488
SK
326 continue;
327 }
328 khlib_debug("%s: open. in_fd: %d\n", s->in_fifo, s->in_fd);
329 if (s->in_fd > maxfd)
330 maxfd = s->in_fd;
331 FD_SET(s->in_fd, &fds);
332 }
333 khlib_debug("selecting...\n");
334 ready = pselect(maxfd + 1, &fds, NULL, NULL, ti, NULL);
335 khlib_debug("ready: %d\n", ready);
336 clock_gettime(CLOCK_MONOTONIC, &t);
337 if (ready == -1) {
338 switch (errno) {
339 case EINTR:
340 khlib_error(
4727e38d 341 "pselect interrupted: %d, errno: %d, msg: %s\n",
4bfac488
SK
342 ready,
343 errno,
344 strerror(errno)
345 );
346 /* TODO: Reconsider what to do here. */
347 return;
348 default:
349 khlib_fatal(
350 "pselect failed: %d, errno: %d, msg: %s\n",
351 ready,
352 errno,
353 strerror(errno)
354 );
355 }
356 }
357 /* At-least-once ensures that expiries are still checked on timeouts. */
358 do {
359 for (s = cfg->slots; s; s = s->next) {
4e3d71e8
SK
360 if (s->in_fd < 0)
361 continue;
4bfac488
SK
362 if (FD_ISSET(s->in_fd, &fds)) {
363 khlib_debug("reading: %s\n", s->in_fifo);
51e63a6f 364 switch (slot_read(s, buf)) {
4bfac488
SK
365 /*
366 * ### MESSAGE LOSS ###
367 * is introduced by closing at EOM in addition
368 * to EOF, since there may be unread messages
369 * remaining in the pipe. However,
370 *
371 * ### INTER-MESSAGE PUSHBACK ###
372 * is also gained, since pipes block at the
373 * "open" call.
374 *
375 * This is an acceptable trade-off because we
376 * are a stateless reporter of a _most-recent_
377 * status, not a stateful accumulator.
c21cacfe
SK
378 *
379 * ### LOSSLESS ALTERNATIVES ###
380 * - Read each pipe until EOF before reading
381 * another.
382 * PROBLEM: a fast writer can trap us in the
383 * read loop.
384 *
385 * - Read each pipe until EOM, but close only
386 * at EOF.
387 * PROBLEM: a fast writer can fill the pipe
388 * faster than we can read it and we end-up
389 * displaying stale data.
390 *
4bfac488
SK
391 */
392 case END_OF_MESSAGE:
393 case END_OF_FILE:
394 case FAILURE:
4727e38d 395 slot_close(s);
51e63a6f 396 s->in_last_read = t;
4bfac488
SK
397 ready--;
398 break;
399 case RETRY:
400 break;
401 default:
402 assert(0);
403 }
404 } else {
b67b5f05 405 slot_expire(s, t, cfg->expiry_character, buf);
4bfac488
SK
406 }
407 }
408 } while (ready);
409 assert(ready == 0);
410}
411
77c76070 412void
457f091d 413config_log(Config *cfg)
77c76070 414{
1084633a 415 khlib_info(
a6b13fa2
SK
416 "Config "
417 "{"
348d5f36 418 " interval = %f,"
a6b13fa2 419 " separator = %s,"
900b9a6b 420 " slot_count = %d,"
457f091d 421 " buf_width = %d,"
900b9a6b 422 " slots = ..."
a6b13fa2
SK
423 " }\n",
424 cfg->interval,
425 cfg->separator,
900b9a6b 426 cfg->slot_count,
457f091d 427 cfg->buf_width
77c76070 428 );
457f091d
SK
429 slots_log(cfg->slots);
430}
431
432void
433config_stretch_for_separators(Config *cfg)
434{
435 int seplen = strlen(cfg->separator);
436 int prefix = 0;
437 int nslots = 0;
438 Slot *s = cfg->slots;
439
440 while (s) {
441 s->out_pos_lo += prefix;
442 s->out_pos_hi += prefix;
443 s->out_pos_cur = s->out_pos_lo;
444 prefix += seplen;
445 nslots++;
446 s = s->next;
447 }
448 cfg->buf_width += (seplen * (nslots - 1));
77c76070 449}
9b5ebc12
SK
450
451int
900b9a6b 452is_pos_num(char *str)
9b5ebc12 453{
900b9a6b
SK
454 while (*str != '\0')
455 if (!isdigit(*(str++)))
9b5ebc12
SK
456 return 0;
457 return 1;
458}
459
348d5f36 460int
900b9a6b 461is_decimal(char *str)
348d5f36
SK
462{
463 char c;
464 int seen = 0;
465
900b9a6b 466 while ((c = *(str++)) != '\0')
348d5f36
SK
467 if (!isdigit(c)) {
468 if (c == '.' && !seen++)
469 continue;
470 else
471 return 0;
472 }
473 return 1;
474}
475
9b5ebc12
SK
476void
477print_usage()
478{
4d66492f 479 assert(argv0);
9b5ebc12 480 fprintf(
a6b13fa2
SK
481 stderr,
482 "\n"
483 "Usage: %s [OPTION ...] SPEC [SPEC ...]\n"
484 "\n"
485 " SPEC = FILE_PATH DATA_WIDTH DATA_TTL\n"
486 " FILE_PATH = string\n"
487 " DATA_WIDTH = int (* (positive) number of characters *)\n"
0a01172a 488 " DATA_TTL = float (* (positive) number of seconds *)\n"
a6b13fa2
SK
489 " OPTION = -i INTERVAL\n"
490 " | -s SEPARATOR\n"
491 " | -x (* Output to X root window *)\n"
492 " | -l LOG_LEVEL\n"
b67b5f05 493 " | -e EXPIRY_CHARACTER\n"
a6b13fa2 494 " SEPARATOR = string\n"
0a01172a 495 " INTERVAL = float (* (positive) number of seconds *)\n"
a6b13fa2 496 " LOG_LEVEL = int (* %d through %d *)\n"
b67b5f05
SK
497 " EXPIRY_CHARACTER = string "
498 "(* Character with which to fill the slot upon expiration. *)\n"
a6b13fa2
SK
499 "\n",
500 argv0,
501 Nothing,
502 Debug
9b5ebc12
SK
503 );
504 fprintf(
a6b13fa2
SK
505 stderr,
506 "Example: %s -i 1 /dev/shm/khatus/khatus_sensor_x 4 10\n"
507 "\n",
508 argv0
9b5ebc12
SK
509 );
510}
511
544b0835
SK
512/* For mutually-recursive calls. */
513void opts_parse_any(Config *, int, char *[], int);
9b5ebc12
SK
514
515void
4d66492f 516parse_opts_opt_i(Config *cfg, int argc, char *argv[], int i)
9b5ebc12 517{
4c438cef
SK
518 char *param;
519
520 if (i >= argc)
9b5ebc12 521 usage("Option -i parameter is missing.\n");
4c438cef 522 param = argv[i++];
348d5f36 523 if (!is_decimal(param))
4c438cef 524 usage("Option -i parameter is invalid: \"%s\"\n", param);
348d5f36 525 cfg->interval = atof(param);
4c438cef 526 opts_parse_any(cfg, argc, argv, i);
9b5ebc12
SK
527}
528
529void
4d66492f
SK
530parse_opts_opt_s(Config *cfg, int argc, char *argv[], int i)
531{
4c438cef 532 if (i >= argc)
4d66492f 533 usage("Option -s parameter is missing.\n");
4c438cef
SK
534 cfg->separator = calloc((strlen(argv[i]) + 1), sizeof(char));
535 strcpy(cfg->separator, argv[i]);
536 opts_parse_any(cfg, argc, argv, ++i);
4d66492f
SK
537}
538
b6316e94
SK
539void
540parse_opts_opt_l(Config *cfg, int argc, char *argv[], int i)
541{
4c438cef 542 char *param;
b6316e94
SK
543 int log_level;
544
4c438cef 545 if (i >= argc)
b6316e94 546 usage("Option -l parameter is missing.\n");
4c438cef
SK
547 param = argv[i++];
548 if (!is_pos_num(param))
549 usage("Option -l parameter is invalid: \"%s\"\n", param);
550 log_level = atoi(param);
551 if (log_level > Debug)
544b0835
SK
552 usage(
553 "Option -l value (%d) exceeds maximum (%d)\n",
554 log_level,
555 Debug
556 );
1084633a 557 _khlib_log_level = log_level;
4c438cef 558 opts_parse_any(cfg, argc, argv, i);
b6316e94
SK
559}
560
b67b5f05
SK
561void
562parse_opts_opt_e(Config *cfg, int argc, char *argv[], int i)
563{
564 if (i >= argc)
565 usage("Option -e parameter is missing.\n");
566 cfg->expiry_character = argv[i++][0];
567 opts_parse_any(cfg, argc, argv, i);
568}
569
4d66492f
SK
570void
571parse_opts_opt(Config *cfg, int argc, char *argv[], int i)
9b5ebc12
SK
572{
573 switch (argv[i][1]) {
ce552549
SK
574 case 'i':
575 /* TODO: Generic set_int */
576 parse_opts_opt_i(cfg, argc, argv, ++i);
577 break;
578 case 's':
579 /* TODO: Generic set_str */
580 parse_opts_opt_s(cfg, argc, argv, ++i);
581 break;
582 case 'x':
457f091d 583 cfg->to_x_root = 1;
ce552549
SK
584 opts_parse_any(cfg, argc, argv, ++i);
585 break;
586 case 'l':
587 /* TODO: Generic set_int */
588 parse_opts_opt_l(cfg, argc, argv, ++i);
589 break;
b67b5f05
SK
590 case 'e':
591 /* TODO: Generic set_str */
592 parse_opts_opt_e(cfg, argc, argv, ++i);
593 break;
ce552549
SK
594 default :
595 usage("Option \"%s\" is invalid\n", argv[i]);
9b5ebc12
SK
596 }
597}
598
599void
4d66492f 600parse_opts_spec(Config *cfg, int argc, char *argv[], int i)
9b5ebc12 601{
ca7a3d6c
SK
602 char *n;
603 char *w;
604 char *t;
605 struct timespec in_last_read;
606 Slot *s;
607
9b5ebc12 608 if ((i + 3) > argc)
544b0835
SK
609 usage(
610 "[spec] Parameter(s) missing for fifo \"%s\".\n",
611 argv[i]
612 );
9b5ebc12 613
ca7a3d6c
SK
614 n = argv[i++];
615 w = argv[i++];
616 t = argv[i++];
0a01172a 617
9b5ebc12 618 if (!is_pos_num(w))
1872c5c1 619 usage("[spec] Invalid width: \"%s\", for fifo \"%s\"\n", w, n);
0a01172a 620 if (!is_decimal(t))
1872c5c1 621 usage("[spec] Invalid TTL: \"%s\", for fifo \"%s\"\n", t, n);
ca7a3d6c 622
900b9a6b
SK
623 in_last_read.tv_sec = 0;
624 in_last_read.tv_nsec = 0;
ca7a3d6c
SK
625 s = calloc(1, sizeof(struct Slot));
626
900b9a6b
SK
627 if (s) {
628 s->in_fifo = n;
629 s->in_fd = -1;
ca7a3d6c
SK
630 s->out_width = atoi(w);
631 s->out_ttl = khlib_timespec_of_float(atof(t));
900b9a6b 632 s->in_last_read = in_last_read;
457f091d 633 s->out_pos_lo = cfg->buf_width;
900b9a6b 634 s->out_pos_cur = s->out_pos_lo;
ca7a3d6c
SK
635 s->out_pos_hi = s->out_pos_lo + s->out_width - 1;
636 s->next = cfg->slots;
900b9a6b
SK
637
638 cfg->slots = s;
457f091d 639 cfg->buf_width += s->out_width;
900b9a6b 640 cfg->slot_count++;
9b5ebc12 641 } else {
1084633a 642 khlib_fatal("[memory] Allocation failure.");
9b5ebc12 643 }
4d66492f 644 opts_parse_any(cfg, argc, argv, i);
9b5ebc12
SK
645}
646
647void
4d66492f 648opts_parse_any(Config *cfg, int argc, char *argv[], int i)
9b5ebc12
SK
649{
650 if (i < argc) {
651 switch (argv[i][0]) {
ce552549
SK
652 case '-':
653 parse_opts_opt(cfg, argc, argv, i);
654 break;
655 default :
656 parse_opts_spec(cfg, argc, argv, i);
4d66492f
SK
657 }
658 }
659}
660
661void
5400b86f 662opts_parse(Config *cfg, int argc, char *argv[])
4d66492f
SK
663{
664 opts_parse_any(cfg, argc, argv, 1);
ca7a3d6c 665 cfg->slots = slots_rev(cfg->slots);
457f091d
SK
666 config_log(cfg);
667 if (cfg->slots == NULL)
668 usage("No slot specs were given!\n");
4d66492f
SK
669}
670
457f091d
SK
671void
672loop(Config *cfg, char *buf, Display *d)
9b5ebc12 673{
b7487ec5 674 struct timespec
900b9a6b
SK
675 t0, /* time stamp. before reading slots */
676 t1, /* time stamp. after reading slots */
b7487ec5
SK
677 ti, /* time interval desired (t1 - t0) */
678 td, /* time interval measured (t1 - t0) */
679 tc; /* time interval correction (ti - td) when td < ti */
4d66492f 680
457f091d 681 ti = khlib_timespec_of_float(cfg->interval);
4727e38d 682 while (running) {
b7487ec5 683 clock_gettime(CLOCK_MONOTONIC, &t0); // FIXME: check errors
457f091d
SK
684 slots_read(cfg, &ti, buf);
685 if (cfg->to_x_root) {
544b0835 686 if (XStoreName(d, DefaultRootWindow(d), buf) < 0)
1084633a 687 khlib_fatal("XStoreName failed.\n");
544b0835 688 XFlush(d);
fabb8771
SK
689 } else {
690 puts(buf);
691 fflush(stdout);
692 }
b7487ec5
SK
693 clock_gettime(CLOCK_MONOTONIC, &t1); // FIXME: check errors
694 timespecsub(&t1, &t0, &td);
1084633a 695 khlib_debug(
544b0835
SK
696 "td {tv_sec = %ld, tv_nsec = %ld}\n",
697 td.tv_sec,
698 td.tv_nsec
699 );
a415999c 700 if (timespeccmp(&td, &ti, <)) {
ca7a3d6c
SK
701 /*
702 * Pushback on data producers by refusing to read the
b7487ec5
SK
703 * pipe more frequently than the interval.
704 */
705 timespecsub(&ti, &td, &tc);
1084633a 706 khlib_sleep(&tc);
574a4bff 707 }
4d66492f 708 }
457f091d
SK
709}
710
4727e38d
SK
711void
712terminate(int s)
713{
714 khlib_debug("terminating due to signal %d\n", s);
715 running = 0;
716}
717
457f091d
SK
718int
719main(int argc, char *argv[])
720{
4727e38d
SK
721 argv0 = argv[0];
722
457f091d
SK
723 Config cfg = {
724 .interval = 1.0,
725 .separator = "|",
b67b5f05 726 .expiry_character = '_',
457f091d
SK
727 .slots = NULL,
728 .slot_count = 0,
729 .buf_width = 0,
730 .to_x_root = 0,
731 };
732 char *buf;
733 Display *d = NULL;
4727e38d 734 struct sigaction sa;
457f091d 735
4727e38d
SK
736 memset(&sa, 0, sizeof(sa));
737 sa.sa_handler = terminate;
738 sigaction(SIGTERM, &sa, NULL);
739 sigaction(SIGINT , &sa, NULL);
457f091d 740
457f091d
SK
741 opts_parse(&cfg, argc, argv);
742 slots_assert_fifos_exist(cfg.slots);
743 config_stretch_for_separators(&cfg);
744 buf = buf_create(&cfg);
745 if (cfg.to_x_root && !(d = XOpenDisplay(NULL)))
746 khlib_fatal("XOpenDisplay failed with: %p\n", d);
747 loop(&cfg, buf, d);
4727e38d 748 slots_close(cfg.slots);
3d7e82a8 749 return EXIT_SUCCESS;
9b5ebc12 750}
This page took 0.103406 seconds and 4 git commands to generate.