X-Git-Url: https://git.xandkar.net/?a=blobdiff_plain;f=x5%2Fkhatus.c;h=5f283db5c0fc88ed2687b1fe56a18728f9f4200a;hb=6c29392d250a28ffdc0661a2baa425dc4eebe499;hp=c7660b48fe8de0866774e4571e8a7df43af0b052;hpb=4d66492f4ca52068334e0cc576836b826d48cb95;p=khatus.git diff --git a/x5/khatus.c b/x5/khatus.c index c7660b4..5f283db 100644 --- a/x5/khatus.c +++ b/x5/khatus.c @@ -1,19 +1,16 @@ -/* -- [ ] Switch from regular files to named pipes - - [ ] poll with select - -*/ +#include +#include +#include +#include #include #include +#include #include #include #include -#include -#include - #define debug(args...) {fprintf(stderr, "[debug] " args);} #define info( args...) {fprintf(stderr, "[info] " args);} #define error(args...) {fprintf(stderr, "[error] " args);} @@ -22,6 +19,7 @@ char *argv0; +/* TODO: Convert file list to file array. */ typedef struct File File; struct File { char *name; @@ -37,19 +35,68 @@ typedef struct Config Config; struct Config { int interval; char * separator; - char * timefmt; File * files; int file_count; int total_width; } defaults = { .interval = 1, .separator = "|", - .timefmt = "%Y-%m-%d %H:%M:%S", .files = NULL, .file_count = 0, .total_width = 0, }; +void +file_print_one(File *f) +{ + debug( + "File " + "{" + " name = %s," + " fd = %d," + " width = %d," + " last_read = %d," + " ttl = %d," + " pos = %d," + " next = %p," + " }\n", + f->name, + f->fd, + f->width, + f->last_read, + f->ttl, + f->pos, + f->next + ); +} + +void +file_print_all(File *head) +{ + for (File *f = head; f; f = f->next) { + file_print_one(f); + } +} + +void +config_print(Config *c) +{ + debug( + "Config " + "{" + " interval = %d," + " separator = %s," + " file_count = %d," + " total_width = %d," + " files = ..." + " }\n", + c->interval, + c->separator, + c->file_count, + c->total_width + ); + file_print_all(c->files); +} int is_pos_num(char *s) @@ -111,7 +158,7 @@ void parse_opts_opt_s(Config *cfg, int argc, char *argv[], int i) { if (i < argc) { - cfg->separator = malloc((strlen(argv[i]) + 1) * sizeof(char)); + cfg->separator = calloc((strlen(argv[i]) + 1), sizeof(char)); strcpy(cfg->separator, argv[i]); opts_parse_any(cfg, argc, argv, ++i); } else { @@ -119,25 +166,12 @@ parse_opts_opt_s(Config *cfg, int argc, char *argv[], int i) } } -void -parse_opts_opt_t(Config *cfg, int argc, char *argv[], int i) -{ - if (i < argc) { - cfg->timefmt = malloc((strlen(argv[i]) + 1) * sizeof(char)); - strcpy(cfg->timefmt, argv[i]); - opts_parse_any(cfg, argc, argv, ++i); - } else { - usage("Option -t parameter is missing.\n"); - } -} - void parse_opts_opt(Config *cfg, int argc, char *argv[], int i) { switch (argv[i][1]) { case 'i': parse_opts_opt_i(cfg, argc, argv, ++i); break; /* TODO: Generic set_int */ case 's': parse_opts_opt_s(cfg, argc, argv, ++i); break; /* TODO: Generic set_str */ - case 't': parse_opts_opt_t(cfg, argc, argv, ++i); break; /* TODO: Generic set_str */ default : usage("Option \"%s\" is invalid\n", argv[i]); } } @@ -156,7 +190,7 @@ parse_opts_spec(Config *cfg, int argc, char *argv[], int i) usage("[spec] Invalid width: \"%s\", for file \"%s\"\n", w, n); if (!is_pos_num(t)) usage("[spec] Invalid TTL: \"%s\", for file \"%s\"\n", t, n); - File *f = malloc(sizeof(struct File)); + File *f = calloc(1, sizeof(struct File)); if (f) { f->name = n; f->fd = -1; @@ -201,23 +235,65 @@ opts_parse(Config *cfg, int argc, char *argv[], int i) } } +void +read_one(File *f, char *buf) +{ + ssize_t current; + ssize_t total; + char *b; + char c; + + current = 0; + total = 0; + c = '\0'; + b = buf + f->pos; + memset(b, ' ', f->width); + while ((current = read(f->fd, &c, 1)) && c != '\n' && c != '\0' && total++ < f->width) + *b++ = c; + if (current == -1) + error("Failed to read: \"%s\". Error: %s\n", f->name, strerror(errno)); + /* TODO Record timestamp read */ + close(f->fd); + f->fd = -1; +} + void read_all(Config *cfg, char *buf) { - /* TODO: stat then check TTL */ + fd_set fds; + int maxfd; + int ready; + struct stat st; + + FD_ZERO(&fds); + + /* TODO: Check TTL */ for (File *f = cfg->files; f; f = f->next) { + /* TODO: Create the FIFO if it doesn't already exist. */ + if (lstat(f->name, &st) < 0) + fatal("Cannot stat \"%s\". Error: %s\n", f->name, strerror(errno)); + if (!(st.st_mode & S_IFIFO)) + fatal("\"%s\" is not a FIFO\n", f->name); + debug("opening: %s\n", f->name); if (f->fd < 0) - f->fd = open(f->name, O_RDONLY); - if (f->fd == -1) { + f->fd = open(f->name, O_RDONLY | O_NONBLOCK); + if (f->fd == -1) /* TODO: Consider backing off retries for failed files. */ fatal("Failed to open \"%s\"\n", f->name); - } else { - lseek(f->fd, 0, 0); - ssize_t n = read(f->fd, buf + f->pos, f->width); - int lasti = n + f->pos - 1; - char lastc = buf[lasti]; - if (lastc == '\n') - buf[lasti] = ' '; + if (f->fd > maxfd) + maxfd = f->fd; + FD_SET(f->fd, &fds); + } + debug("selecting...\n"); + ready = select(maxfd + 1, &fds, NULL, NULL, NULL); + debug("ready: %d\n", ready); + assert(ready != 0); + if (ready < 0) + fatal("%s", strerror(errno)); + for (File *f = cfg->files; f; f = f->next) { + if (FD_ISSET(f->fd, &fds)) { + debug("reading: %s\n", f->name); + read_one(f, buf); } } } @@ -236,29 +312,9 @@ main(int argc, char *argv[]) opts_parse(cfg, argc, argv, 1); debug("argv0 = %s\n", argv0); - debug("[config] interval = %d\n", cfg->interval); - debug("[config] separator = %s\n", cfg->separator); - debug("[config] file_count = %d\n", cfg->file_count); - debug("[config] total_width = %d\n", cfg->total_width); + config_print(cfg); if (cfg->files == NULL) usage("No file specs were given!\n"); - for (File *f = cfg->files; f; f = f->next) { - debug( - "[config] file = " - "{" - " name = %s," - " pos = %d," - " width = %d," - " ttl = %d," - " last_read = %d," - " }\n", - f->name, - f->pos, - f->width, - f->ttl, - f->last_read - ); - } width = cfg->total_width; seplen = strlen(cfg->separator); @@ -270,7 +326,7 @@ main(int argc, char *argv[]) nfiles++; } width += (seplen * (nfiles - 1)); - buf = malloc(width + 1); + buf = calloc(1, width + 1); if (buf == NULL) fatal("[memory] Failed to allocate buffer of %d bytes", width); memset(buf, ' ', width); @@ -278,14 +334,18 @@ main(int argc, char *argv[]) /* 2nd pass to set the separators */ for (File *f = cfg->files; f; f = f->next) { if (f->pos) { /* Skip the first, left-most */ - strcpy(buf + (f->pos - seplen), cfg->separator); + /* Copying only seplen ensures we omit the '\0' byte. */ + strncpy(buf + (f->pos - seplen), cfg->separator, seplen); } } + printf("%s\n", buf); /* TODO: nanosleep and nano time diff */ for (;;) { + /* TODO: Check TTL and maybe blank-out */ + /* TODO: How to trigger TTL check? On select? Alarm signal? */ + /* TODO: Option to set X root window title or print */ read_all(cfg, buf); printf("%s\n", buf); - sleep(cfg->interval); } }