summaryrefslogtreecommitdiff
path: root/sfeed_curses.c
diff options
context:
space:
mode:
Diffstat (limited to 'sfeed_curses.c')
-rw-r--r--sfeed_curses.c490
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();