diff options
author | Benjamin Chausse <benjamin@chausse.xyz> | 2024-08-09 14:11:50 -0400 |
---|---|---|
committer | Benjamin Chausse <benjamin@chausse.xyz> | 2024-08-09 14:11:50 -0400 |
commit | 5857d82e8e596d6fda406a0c4d8d68ca7a03c124 (patch) | |
tree | 553916894dee907825360580c5d9a05c82c5af16 /sfeed_curses.c | |
parent | 3574e3cbf9d99546e868aeb995ce2c171cdc36a6 (diff) | |
parent | 19957bc272e745af7b56b79fa648e8b6b77113b1 (diff) |
Diffstat (limited to 'sfeed_curses.c')
-rw-r--r-- | sfeed_curses.c | 490 |
1 files changed, 263 insertions, 227 deletions
diff --git a/sfeed_curses.c b/sfeed_curses.c index d12f170..3359340 100644 --- a/sfeed_curses.c +++ b/sfeed_curses.c @@ -1,10 +1,7 @@ #include <sys/ioctl.h> #include <sys/select.h> -#include <sys/time.h> -#include <sys/types.h> #include <sys/wait.h> -#include <ctype.h> #include <errno.h> #include <fcntl.h> #include <locale.h> @@ -38,10 +35,10 @@ #define LINEBAR_SYMBOL_BAR "\xe2\x94\x80" /* symbol: "light horizontal" */ #define LINEBAR_SYMBOL_RIGHT "\xe2\x94\xa4" /* symbol: "light vertical and left" */ #else -#define SCROLLBAR_SYMBOL_BAR "|" /* symbol: "light vertical" */ +#define SCROLLBAR_SYMBOL_BAR "|" #define SCROLLBAR_SYMBOL_TICK " " -#define LINEBAR_SYMBOL_BAR "-" /* symbol: "light horizontal" */ -#define LINEBAR_SYMBOL_RIGHT "|" /* symbol: "light vertical and left" */ +#define LINEBAR_SYMBOL_BAR "-" +#define LINEBAR_SYMBOL_RIGHT "|" #endif /* color-theme */ @@ -130,24 +127,30 @@ struct item { off_t offset; /* line offset in file for lazyload */ }; +struct urls { + char **items; /* array of URLs */ + size_t len; /* amount of items */ + size_t cap; /* available capacity */ +}; + struct items { - struct item *items; /* array of items */ - size_t len; /* amount of items */ - size_t cap; /* available capacity */ + struct item *items; /* array of items */ + size_t len; /* amount of items */ + size_t cap; /* available capacity */ }; -void alldirty(void); -void cleanup(void); -void draw(void); -int getsidebarsize(void); -void markread(struct pane *, off_t, off_t, int); -void pane_draw(struct pane *); -void sighandler(int); -void updategeom(void); -void updatesidebar(void); -void urls_free(void); -int urls_isnew(const char *); -void urls_read(void); +static void alldirty(void); +static void cleanup(void); +static void draw(void); +static int getsidebarsize(void); +static void markread(struct pane *, off_t, off_t, int); +static void pane_draw(struct pane *); +static void sighandler(int); +static void updategeom(void); +static void updatesidebar(void); +static void urls_free(struct urls *); +static int urls_hasmatch(struct urls *, const char *); +static void urls_read(struct urls *, const char *); static struct linebar linebar; static struct statusbar statusbar; @@ -170,10 +173,11 @@ static struct feed *feeds; static struct feed *curfeed; static size_t nfeeds; /* amount of feeds */ static time_t comparetime; -static char *urlfile, **urls; -static size_t nurls; +static struct urls urls; +static char *urlfile; -volatile sig_atomic_t sigstate = 0; +volatile sig_atomic_t state_sigchld = 0, state_sighup = 0, state_sigint = 0; +volatile sig_atomic_t state_sigterm = 0, state_sigwinch = 0; static char *plumbercmd = "xdg-open"; /* env variable: $SFEED_PLUMBER */ static char *pipercmd = "sfeed_content"; /* env variable: $SFEED_PIPER */ @@ -186,7 +190,7 @@ static int piperia = 1; /* env variable: $SFEED_PIPER_INTERACTIVE */ static int yankeria = 0; /* env variable: $SFEED_YANKER_INTERACTIVE */ static int lazyload = 0; /* env variable: $SFEED_LAZYLOAD */ -int +static int ttywritef(const char *fmt, ...) { va_list ap; @@ -200,7 +204,7 @@ ttywritef(const char *fmt, ...) return n; } -int +static int ttywrite(const char *s) { if (!s) @@ -208,13 +212,8 @@ ttywrite(const char *s) return write(1, s, strlen(s)); } -/* Hint for compilers and static analyzers that a function exits. */ -#ifndef __dead -#define __dead -#endif - /* Print to stderr, call cleanup() and _exit(). */ -__dead void +__dead static void die(const char *fmt, ...) { va_list ap; @@ -229,13 +228,13 @@ die(const char *fmt, ...) if (saved_errno) fprintf(stderr, ": %s", strerror(saved_errno)); + putc('\n', stderr); fflush(stderr); - write(2, "\n", 1); _exit(1); } -void * +static void * erealloc(void *ptr, size_t size) { void *p; @@ -245,7 +244,7 @@ erealloc(void *ptr, size_t size) return p; } -void * +static void * ecalloc(size_t nmemb, size_t size) { void *p; @@ -255,7 +254,7 @@ ecalloc(size_t nmemb, size_t size) return p; } -char * +static char * estrdup(const char *s) { char *p; @@ -266,7 +265,7 @@ estrdup(const char *s) } /* Wrapper for tparm() which allows NULL parameter for str. */ -char * +static char * tparmnull(const char *str, long p1, long p2, long p3, long p4, long p5, long p6, long p7, long p8, long p9) { @@ -277,7 +276,7 @@ tparmnull(const char *str, long p1, long p2, long p3, long p4, long p5, long p6, } /* Counts column width of character string. */ -size_t +static size_t colw(const char *s) { wchar_t wc; @@ -309,7 +308,7 @@ colw(const char *s) /* Format `len` columns of characters. If string is shorter pad the rest with characters `pad`. */ -int +static int utf8pad(char *buf, size_t bufsiz, const char *s, size_t len, int pad) { wchar_t wc; @@ -374,13 +373,13 @@ utf8pad(char *buf, size_t bufsiz, const char *s, size_t len, int pad) return 0; } -void +static void resetstate(void) { ttywrite("\x1b""c"); /* rs1: reset title and state */ } -void +static void updatetitle(void) { unsigned long totalnew = 0, total = 0; @@ -393,32 +392,32 @@ updatetitle(void) ttywritef("\x1b]2;(%lu/%lu) - sfeed_curses\x1b\\", totalnew, total); } -void +static void appmode(int on) { ttywrite(tparmnull(on ? enter_ca_mode : exit_ca_mode, 0, 0, 0, 0, 0, 0, 0, 0, 0)); } -void +static void mousemode(int on) { ttywrite(on ? "\x1b[?1000h" : "\x1b[?1000l"); /* xterm X10 mouse mode */ ttywrite(on ? "\x1b[?1006h" : "\x1b[?1006l"); /* extended SGR mouse mode */ } -void +static void cursormode(int on) { ttywrite(tparmnull(on ? cursor_normal : cursor_invisible, 0, 0, 0, 0, 0, 0, 0, 0, 0)); } -void +static void cursormove(int x, int y) { ttywrite(tparmnull(cursor_address, y, x, 0, 0, 0, 0, 0, 0, 0)); } -void +static void cursorsave(void) { /* do not save the cursor if it won't be restored anyway */ @@ -426,7 +425,7 @@ cursorsave(void) ttywrite(tparmnull(save_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0)); } -void +static void cursorrestore(void) { /* if the cursor cannot be hidden then move to a consistent position */ @@ -436,7 +435,7 @@ cursorrestore(void) cursormove(0, 0); } -void +static void attrmode(int mode) { switch (mode) { @@ -457,19 +456,19 @@ attrmode(int mode) } } -void +static void cleareol(void) { ttywrite(tparmnull(clr_eol, 0, 0, 0, 0, 0, 0, 0, 0, 0)); } -void +static void clearscreen(void) { ttywrite(tparmnull(clear_screen, 0, 0, 0, 0, 0, 0, 0, 0, 0)); } -void +static void cleanup(void) { struct sigaction sa; @@ -498,7 +497,7 @@ cleanup(void) sigaction(SIGWINCH, &sa, NULL); } -void +static void win_update(struct win *w, int width, int height) { if (width != w->width || height != w->height) @@ -507,7 +506,7 @@ win_update(struct win *w, int width, int height) w->height = height; } -void +static void resizewin(void) { struct winsize winsz; @@ -522,7 +521,7 @@ resizewin(void) alldirty(); } -void +static void init(void) { struct sigaction sa; @@ -549,40 +548,44 @@ init(void) cursormode(0); if (usemouse) - mousemode(usemouse); + mousemode(1); memset(&sa, 0, sizeof(sa)); sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; /* require BSD signal semantics */ sa.sa_handler = sighandler; + sigaction(SIGCHLD, &sa, NULL); sigaction(SIGHUP, &sa, NULL); sigaction(SIGINT, &sa, NULL); sigaction(SIGTERM, &sa, NULL); sigaction(SIGWINCH, &sa, NULL); } -void +static void processexit(pid_t pid, int interactive) { - pid_t wpid; struct sigaction sa; - memset(&sa, 0, sizeof(sa)); - sigemptyset(&sa.sa_mask); - sa.sa_flags = SA_RESTART; /* require BSD signal semantics */ - sa.sa_handler = SIG_IGN; - sigaction(SIGINT, &sa, NULL); - if (interactive) { - while ((wpid = wait(NULL)) >= 0 && wpid != pid) - ; + memset(&sa, 0, sizeof(sa)); + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; /* require BSD signal semantics */ + + /* ignore SIGINT (^C) in parent for interactive applications */ + sa.sa_handler = SIG_IGN; + sigaction(SIGINT, &sa, NULL); + + sa.sa_flags = 0; /* SIGTERM: interrupt waitpid(), no SA_RESTART */ + sa.sa_handler = sighandler; + sigaction(SIGTERM, &sa, NULL); + + /* wait for process to change state, ignore errors */ + waitpid(pid, NULL, 0); + init(); updatesidebar(); updategeom(); updatetitle(); - } else { - sa.sa_handler = sighandler; - sigaction(SIGINT, &sa, NULL); } } @@ -591,7 +594,7 @@ processexit(pid_t pid, int interactive) if `interactive` is 1 then cleanup and restore the tty and wait on the process. if 0 then don't do that and also write stdout and stderr to /dev/null. */ -void +static void pipeitem(const char *cmd, struct item *item, int field, int interactive) { FILE *fp; @@ -606,8 +609,8 @@ pipeitem(const char *cmd, struct item *item, int field, int interactive) die("fork"); case 0: if (!interactive) { - dup2(devnullfd, 1); - dup2(devnullfd, 2); + dup2(devnullfd, 1); /* stdout */ + dup2(devnullfd, 2); /* stderr */ } errno = 0; @@ -631,7 +634,7 @@ pipeitem(const char *cmd, struct item *item, int field, int interactive) } } -void +static void forkexec(char *argv[], int interactive) { pid_t pid; @@ -644,8 +647,9 @@ forkexec(char *argv[], int interactive) die("fork"); case 0: if (!interactive) { - dup2(devnullfd, 1); - dup2(devnullfd, 2); + dup2(devnullfd, 0); /* stdin */ + dup2(devnullfd, 1); /* stdout */ + dup2(devnullfd, 2); /* stderr */ } if (execvp(argv[0], argv) == -1) _exit(1); @@ -654,7 +658,7 @@ forkexec(char *argv[], int interactive) } } -struct row * +static struct row * pane_row_get(struct pane *p, off_t pos) { if (pos < 0 || pos >= p->nrows) @@ -665,7 +669,7 @@ pane_row_get(struct pane *p, off_t pos) return p->rows + pos; } -char * +static char * pane_row_text(struct pane *p, struct row *row) { /* custom formatter */ @@ -674,7 +678,7 @@ pane_row_text(struct pane *p, struct row *row) return row->text; } -int +static int pane_row_match(struct pane *p, struct row *row, const char *s) { if (p->row_match) @@ -682,7 +686,7 @@ pane_row_match(struct pane *p, struct row *row, const char *s) return (strcasestr(pane_row_text(p, row), s) != NULL); } -void +static void pane_row_draw(struct pane *p, off_t pos, int selected) { struct row *row; @@ -715,7 +719,7 @@ pane_row_draw(struct pane *p, off_t pos, int selected) cursorrestore(); } -void +static void pane_setpos(struct pane *p, off_t pos) { if (pos < 0) @@ -739,7 +743,7 @@ pane_setpos(struct pane *p, off_t pos) p->pos = pos; } -void +static void pane_scrollpage(struct pane *p, int pages) { off_t pos; @@ -757,13 +761,13 @@ pane_scrollpage(struct pane *p, int pages) } } -void +static void pane_scrolln(struct pane *p, int n) { pane_setpos(p, p->pos + n); } -void +static void pane_setfocus(struct pane *p, int on) { if (p->focused != on) { @@ -772,7 +776,7 @@ pane_setfocus(struct pane *p, int on) } } -void +static void pane_draw(struct pane *p) { off_t pos, y; @@ -789,7 +793,7 @@ pane_draw(struct pane *p) pane_row_draw(p, y + pos, (y + pos) == p->pos); } -void +static void setlayout(int n) { if (layout != LayoutMonocle) @@ -797,7 +801,7 @@ setlayout(int n) layout = n; } -void +static void updategeom(void) { int h, w, x = 0, y = 0; @@ -871,7 +875,7 @@ updategeom(void) alldirty(); } -void +static void scrollbar_setfocus(struct scrollbar *s, int on) { if (s->focused != on) { @@ -880,7 +884,7 @@ scrollbar_setfocus(struct scrollbar *s, int on) } } -void +static void scrollbar_update(struct scrollbar *s, off_t pos, off_t nrows, int pageheight) { int tickpos = 0, ticksize = 0; @@ -905,7 +909,7 @@ scrollbar_update(struct scrollbar *s, off_t pos, off_t nrows, int pageheight) s->ticksize = ticksize; } -void +static void scrollbar_draw(struct scrollbar *s) { off_t y; @@ -944,15 +948,17 @@ scrollbar_draw(struct scrollbar *s) cursorrestore(); } -int +static int readch(void) { unsigned char b; fd_set readfds; struct timeval tv; - if (cmdenv && *cmdenv) - return *(cmdenv++); + if (cmdenv && *cmdenv) { + b = *(cmdenv++); /* $SFEED_AUTOCMD */ + return (int)b; + } for (;;) { FD_ZERO(&readfds); @@ -976,15 +982,17 @@ readch(void) } } -char * +static char * lineeditor(void) { char *input = NULL; size_t cap = 0, nchars = 0; int ch; + if (usemouse) + mousemode(0); for (;;) { - if (nchars + 1 >= cap) { + if (nchars + 2 >= cap) { cap = cap ? cap * 2 : 32; input = erealloc(input, cap); } @@ -997,34 +1005,37 @@ lineeditor(void) if (!nchars) continue; input[--nchars] = '\0'; - write(1, "\b \b", 3); /* back, blank, back */ - continue; + ttywrite("\b \b"); /* back, blank, back */ } else if (ch >= ' ') { input[nchars] = ch; - write(1, &input[nchars], 1); + input[nchars + 1] = '\0'; + ttywrite(&input[nchars]); nchars++; } else if (ch < 0) { - switch (sigstate) { - case 0: - case SIGWINCH: - /* continue editing: process signal later */ - continue; - case SIGINT: - /* cancel prompt, but do not quit */ - sigstate = 0; /* reset: do not handle it */ - break; - default: /* other: SIGHUP, SIGTERM */ - /* cancel prompt and handle signal after */ - break; + if (state_sigchld) { + state_sigchld = 0; + /* wait on child processes so they don't become a zombie */ + while (waitpid((pid_t)-1, NULL, WNOHANG) > 0) + ; } + if (state_sigint) + state_sigint = 0; /* cancel prompt and don't handle this signal */ + else if (state_sighup || state_sigterm) + ; /* cancel prompt and handle these signals */ + else /* no signal, time-out or SIGCHLD or SIGWINCH */ + continue; /* do not cancel: process signal later */ + free(input); - return NULL; + input = NULL; + break; /* cancel prompt */ } } + if (usemouse) + mousemode(1); return input; } -char * +static char * uiprompt(int x, int y, char *fmt, ...) { va_list ap; @@ -1054,7 +1065,7 @@ uiprompt(int x, int y, char *fmt, ...) return input; } -void +static void linebar_draw(struct linebar *b) { int i; @@ -1075,7 +1086,7 @@ linebar_draw(struct linebar *b) cursorrestore(); } -void +static void statusbar_draw(struct statusbar *s) { if (!s->dirty) @@ -1095,7 +1106,7 @@ statusbar_draw(struct statusbar *s) cursorrestore(); } -void +static void statusbar_update(struct statusbar *s, const char *text) { if (s->text && !strcmp(s->text, text)) @@ -1107,7 +1118,7 @@ statusbar_update(struct statusbar *s, const char *text) } /* Line to item, modifies and splits line in-place. */ -int +static int linetoitem(char *line, struct item *item) { char *fields[FieldLast]; @@ -1133,7 +1144,7 @@ linetoitem(char *line, struct item *item) return 0; } -void +static void feed_items_free(struct items *items) { size_t i; @@ -1148,7 +1159,7 @@ feed_items_free(struct items *items) items->cap = 0; } -void +static void feed_items_get(struct feed *f, FILE *fp, struct items *itemsret) { struct item *item, *items = NULL; @@ -1190,13 +1201,13 @@ feed_items_get(struct feed *f, FILE *fp, struct items *itemsret) if (n <= 0 || feof(fp)) break; } - itemsret->cap = cap; itemsret->items = items; itemsret->len = nitems; + itemsret->cap = cap; free(line); } -void +static void updatenewitems(struct feed *f) { struct pane *p; @@ -1205,12 +1216,13 @@ updatenewitems(struct feed *f) size_t i; p = &panes[PaneItems]; + p->dirty = 1; f->totalnew = 0; for (i = 0; i < p->nrows; i++) { row = &(p->rows[i]); /* do not use pane_row_get() */ item = row->data; if (urlfile) - item->isnew = urls_isnew(item->matchnew); + item->isnew = !urls_hasmatch(&urls, item->matchnew); else item->isnew = (item->timeok && item->timestamp >= comparetime); row->bold = item->isnew; @@ -1219,7 +1231,7 @@ updatenewitems(struct feed *f) f->total = p->nrows; } -void +static void feed_load(struct feed *f, FILE *fp) { /* static, reuse local buffers */ @@ -1238,11 +1250,9 @@ feed_load(struct feed *f, FILE *fp) p->rows[i].data = &(items.items[i]); /* do not use pane_row_get() */ updatenewitems(f); - - p->dirty = 1; } -void +static void feed_count(struct feed *f, FILE *fp) { char *fields[FieldLast]; @@ -1258,7 +1268,7 @@ feed_count(struct feed *f, FILE *fp) parseline(line, fields); if (urlfile) { - f->totalnew += urls_isnew(fields[fields[FieldLink][0] ? FieldLink : FieldId]); + f->totalnew += !urls_hasmatch(&urls, fields[fields[FieldLink][0] ? FieldLink : FieldId]); } else { parsedtime = 0; if (!strtotime(fields[FieldUnixTimestamp], &parsedtime)) @@ -1271,7 +1281,7 @@ feed_count(struct feed *f, FILE *fp) free(line); } -void +static void feed_setenv(struct feed *f) { if (f && f->path) @@ -1281,7 +1291,7 @@ feed_setenv(struct feed *f) } /* Change feed, have one file open, reopen file if needed. */ -void +static void feeds_set(struct feed *f) { if (curfeed) { @@ -1301,16 +1311,15 @@ feeds_set(struct feed *f) curfeed = f; } -void +static void feeds_load(struct feed *feeds, size_t nfeeds) { struct feed *f; size_t i; - if ((comparetime = time(NULL)) == -1) - die("time"); - /* 1 day is old news */ - comparetime -= 86400; + errno = 0; + if ((comparetime = getcomparetime()) == (time_t)-1) + die("getcomparetime"); for (i = 0; i < nfeeds; i++) { f = &feeds[i]; @@ -1345,7 +1354,7 @@ feeds_load(struct feed *feeds, size_t nfeeds) } /* find row position of the feed if visible, else return -1 */ -off_t +static off_t feeds_row_get(struct pane *p, struct feed *f) { struct row *row; @@ -1362,7 +1371,7 @@ feeds_row_get(struct pane *p, struct feed *f) return -1; } -void +static void feeds_reloadall(void) { struct pane *p; @@ -1376,9 +1385,9 @@ feeds_reloadall(void) pos = panes[PaneItems].pos; /* store numeric item position */ feeds_set(curfeed); /* close and reopen feed if possible */ - urls_read(); + urls_read(&urls, urlfile); feeds_load(feeds, nfeeds); - urls_free(); + urls_free(&urls); /* restore numeric item position */ pane_setpos(&panes[PaneItems], pos); updatesidebar(); @@ -1391,7 +1400,7 @@ feeds_reloadall(void) pane_setpos(p, 0); } -void +static void feed_open_selected(struct pane *p) { struct feed *f; @@ -1401,10 +1410,10 @@ feed_open_selected(struct pane *p) return; f = row->data; feeds_set(f); - urls_read(); + urls_read(&urls, urlfile); if (f->fp) feed_load(f, f->fp); - urls_free(); + urls_free(&urls); /* redraw row: counts could be changed */ updatesidebar(); updatetitle(); @@ -1415,22 +1424,24 @@ feed_open_selected(struct pane *p) } } -void +static void feed_plumb_selected_item(struct pane *p, int field) { struct row *row; struct item *item; - char *cmd[] = { plumbercmd, NULL, NULL }; + char *cmd[3]; /* will have: { plumbercmd, arg, NULL } */ if (!(row = pane_row_get(p, p->pos))) return; markread(p, p->pos, p->pos, 1); item = row->data; + cmd[0] = plumbercmd; cmd[1] = item->fields[field]; /* set first argument for plumber */ + cmd[2] = NULL; forkexec(cmd, plumberia); } -void +static void feed_pipe_selected_item(struct pane *p) { struct row *row; @@ -1443,7 +1454,7 @@ feed_pipe_selected_item(struct pane *p) pipeitem(pipercmd, item, -1, piperia); } -void +static void feed_yank_selected_item(struct pane *p, int field) { struct row *row; @@ -1456,7 +1467,7 @@ feed_yank_selected_item(struct pane *p, int field) } /* calculate optimal (default) size */ -int +static int getsidebarsizedefault(void) { struct feed *feed; @@ -1489,7 +1500,7 @@ getsidebarsizedefault(void) return 0; } -int +static int getsidebarsize(void) { int size; @@ -1499,7 +1510,7 @@ getsidebarsize(void) return size; } -void +static void adjustsidebarsize(int n) { int size; @@ -1522,7 +1533,7 @@ adjustsidebarsize(int n) } } -void +static void updatesidebar(void) { struct pane *p; @@ -1574,22 +1585,19 @@ updatesidebar(void) p->pos = p->nrows - 1; } -void +static void sighandler(int signo) { switch (signo) { - case SIGHUP: - case SIGINT: - case SIGTERM: - case SIGWINCH: - /* SIGTERM is more important, do not override it */ - if (sigstate != SIGTERM) - sigstate = signo; - break; + case SIGCHLD: state_sigchld = 1; break; + case SIGHUP: state_sighup = 1; break; + case SIGINT: state_sigint = 1; break; + case SIGTERM: state_sigterm = 1; break; + case SIGWINCH: state_sigwinch = 1; break; } } -void +static void alldirty(void) { win.dirty = 1; @@ -1601,7 +1609,7 @@ alldirty(void) statusbar.dirty = 1; } -void +static void draw(void) { struct row *row; @@ -1635,7 +1643,7 @@ draw(void) statusbar_draw(&statusbar); } -void +static void mousereport(int button, int release, int keymask, int x, int y) { struct pane *p; @@ -1707,7 +1715,7 @@ mousereport(int button, int release, int keymask, int x, int y) } /* Custom formatter for feed row. */ -char * +static char * feed_row_format(struct pane *p, struct row *row) { /* static, reuse local buffers */ @@ -1748,7 +1756,7 @@ feed_row_format(struct pane *p, struct row *row) return text; } -int +static int feed_row_match(struct pane *p, struct row *row, const char *s) { struct feed *feed; @@ -1758,7 +1766,7 @@ feed_row_match(struct pane *p, struct row *row, const char *s) return (strcasestr(feed->name, s) != NULL); } -struct row * +static struct row * item_row_get(struct pane *p, off_t pos) { struct row *itemrow; @@ -1779,6 +1787,7 @@ item_row_get(struct pane *p, off_t pos) if ((linelen = getline(&line, &linesize, f->fp)) <= 0) { if (ferror(f->fp)) die("getline: %s", f->path); + free(line); return NULL; } @@ -1794,7 +1803,7 @@ item_row_get(struct pane *p, off_t pos) } /* Custom formatter for item row. */ -char * +static char * item_row_format(struct pane *p, struct row *row) { /* static, reuse local buffers */ @@ -1826,7 +1835,7 @@ item_row_format(struct pane *p, struct row *row) return text; } -void +static void markread(struct pane *p, off_t from, off_t to, int isread) { struct row *row; @@ -1834,7 +1843,7 @@ markread(struct pane *p, off_t from, off_t to, int isread) FILE *fp; off_t i; const char *cmd; - int isnew = !isread, pid, wpid, status, visstart; + int isnew = !isread, pid, status = -1, visstart; if (!urlfile || !p->nrows) return; @@ -1845,8 +1854,8 @@ markread(struct pane *p, off_t from, off_t to, int isread) case -1: die("fork"); case 0: - dup2(devnullfd, 1); - dup2(devnullfd, 2); + dup2(devnullfd, 1); /* stdout */ + dup2(devnullfd, 2); /* stderr */ errno = 0; if (!(fp = popen(cmd, "w"))) @@ -1865,11 +1874,9 @@ markread(struct pane *p, off_t from, off_t to, int isread) status = WIFEXITED(status) ? WEXITSTATUS(status) : 127; _exit(status); default: - while ((wpid = wait(&status)) >= 0 && wpid != pid) - ; - - /* fail: exit statuscode was non-zero */ - if (status) + /* waitpid() and block on process status change, + fail if the exit status code was unavailable or non-zero */ + if (waitpid(pid, &status, 0) <= 0 || status) break; visstart = p->pos - (p->pos % p->height); /* visible start */ @@ -1891,38 +1898,41 @@ markread(struct pane *p, off_t from, off_t to, int isread) } } -int +static int urls_cmp(const void *v1, const void *v2) { return strcmp(*((char **)v1), *((char **)v2)); } -int -urls_isnew(const char *url) +static void +urls_free(struct urls *urls) { - return (!nurls || - bsearch(&url, urls, nurls, sizeof(char *), urls_cmp) == NULL); + while (urls->len > 0) { + urls->len--; + free(urls->items[urls->len]); + } + free(urls->items); + urls->items = NULL; + urls->len = 0; + urls->cap = 0; } -void -urls_free(void) +static int +urls_hasmatch(struct urls *urls, const char *url) { - while (nurls > 0) - free(urls[--nurls]); - free(urls); - urls = NULL; - nurls = 0; + return (urls->len && + bsearch(&url, urls->items, urls->len, sizeof(char *), urls_cmp)); } -void -urls_read(void) +static void +urls_read(struct urls *urls, const char *urlfile) { FILE *fp; char *line = NULL; - size_t linesiz = 0, cap = 0; + size_t linesiz = 0; ssize_t n; - urls_free(); + urls_free(urls); if (!urlfile) return; @@ -1932,19 +1942,19 @@ urls_read(void) while ((n = getline(&line, &linesiz, fp)) > 0) { if (line[n - 1] == '\n') line[--n] = '\0'; - if (nurls + 1 >= cap) { - cap = cap ? cap * 2 : 16; - urls = erealloc(urls, cap * sizeof(char *)); + if (urls->len + 1 >= urls->cap) { + urls->cap = urls->cap ? urls->cap * 2 : 16; + urls->items = erealloc(urls->items, urls->cap * sizeof(char *)); } - urls[nurls++] = estrdup(line); + urls->items[urls->len++] = estrdup(line); } if (ferror(fp)) die("getline: %s", urlfile); fclose(fp); free(line); - if (nurls > 0) - qsort(urls, nurls, sizeof(char *), urls_cmp); + if (urls->len > 0) + qsort(urls->items, urls->len, sizeof(char *), urls_cmp); } int @@ -2012,9 +2022,9 @@ main(int argc, char *argv[]) nfeeds = argc - 1; } feeds_set(&feeds[0]); - urls_read(); + urls_read(&urls, urlfile); feeds_load(feeds, nfeeds); - urls_free(); + urls_free(&urls); if (!isatty(0)) { if ((fd = open("/dev/tty", O_RDONLY)) == -1) @@ -2106,30 +2116,43 @@ main(int argc, char *argv[]) mousereport(button, release, keymask, x - 1, y - 1); break; + /* DEC/SUN: ESC O char, HP: ESC char or SCO: ESC [ char */ case 'A': goto keyup; /* arrow up */ case 'B': goto keydown; /* arrow down */ - case 'C': goto keyright; /* arrow left */ - case 'D': goto keyleft; /* arrow right */ + case 'C': goto keyright; /* arrow right */ + case 'D': goto keyleft; /* arrow left */ case 'F': goto endpos; /* end */ + case 'G': goto nextpage; /* page down */ case 'H': goto startpos; /* home */ - case '4': /* end */ - if ((ch = readch()) < 0) - goto event; - if (ch == '~') - goto endpos; - continue; - case '5': /* page up */ - if ((ch = readch()) < 0) - goto event; - if (ch == '~') - goto prevpage; - continue; - case '6': /* page down */ - if ((ch = readch()) < 0) - goto event; - if (ch == '~') - goto nextpage; - continue; + case 'I': goto prevpage; /* page up */ + default: + if (!(ch >= '0' && ch <= '9')) + break; + for (i = ch - '0'; ;) { + if ((ch = readch()) < 0) { + goto event; + } else if (ch >= '0' && ch <= '9') { + i = (i * 10) + (ch - '0'); + continue; + } else if (ch == '~') { /* DEC: ESC [ num ~ */ + switch (i) { + case 1: goto startpos; /* home */ + case 4: goto endpos; /* end */ + case 5: goto prevpage; /* page up */ + case 6: goto nextpage; /* page down */ + case 7: goto startpos; /* home: urxvt */ + case 8: goto endpos; /* end: urxvt */ + } + } else if (ch == 'z') { /* SUN: ESC [ num z */ + switch (i) { + case 214: goto startpos; /* home */ + case 216: goto prevpage; /* page up */ + case 220: goto endpos; /* end */ + case 222: goto nextpage; /* page down */ + } + } + break; + } } break; keyup: @@ -2314,6 +2337,7 @@ nextpage: if (selpane == PaneItems && panes[selpane].nrows) { p = &panes[selpane]; markread(p, p->pos, p->pos, ch == 'r'); + pane_scrolln(&panes[selpane], +1); } break; case 's': /* toggle layout between monocle or non-monocle */ @@ -2332,23 +2356,35 @@ nextpage: event: if (ch == EOF) goto end; - else if (ch == -3 && sigstate == 0) + else if (ch == -3 && !state_sigchld && !state_sighup && + !state_sigint && !state_sigterm && !state_sigwinch) continue; /* just a time-out, nothing to do */ - switch (sigstate) { - case SIGHUP: - feeds_reloadall(); - sigstate = 0; - break; - case SIGINT: - case SIGTERM: + /* handle signals in a particular order */ + if (state_sigchld) { + state_sigchld = 0; + /* wait on child processes so they don't become a zombie, + do not block the parent process if there is no status, + ignore errors */ + while (waitpid((pid_t)-1, NULL, WNOHANG) > 0) + ; + } + if (state_sigterm) { + cleanup(); + _exit(128 + SIGTERM); + } + if (state_sigint) { cleanup(); - _exit(128 + sigstate); - case SIGWINCH: + _exit(128 + SIGINT); + } + if (state_sighup) { + state_sighup = 0; + feeds_reloadall(); + } + if (state_sigwinch) { + state_sigwinch = 0; resizewin(); updategeom(); - sigstate = 0; - break; } draw(); |