commit 82d6b985067893135f275d8142a1bbf49ec8082f
parent 9c195cb929710440fd90dbc8eca305ac3aa02ea5
Author: citbl <citbl@citbl.org>
Date: Wed, 8 Oct 2025 21:50:15 +1000
open comments and handle dead feeds
Diffstat:
6 files changed, 51 insertions(+), 36 deletions(-)
diff --git a/config.h b/config.h
@@ -6,6 +6,7 @@
#define POSTS_CAP 128
#define FEEDS_CAP 32
#define TITLE_CAP 128
+#define URL_CAP 8192
#define TEXT_COLOR TB_WHITE
#define BACK_COLOR TB_DEFAULT
diff --git a/feeds.c b/feeds.c
@@ -49,8 +49,11 @@ fetch_feed(feed_t* feed, char* url)
static mrss_t* rss = NULL;
mrss_error_t rc = mrss_parse_url(url, &rss);
- if (rc != MRSS_OK) {
- fprintf(stderr, "parse feed failed: %s\n", mrss_strerror(rc));
+ if (rc != MRSS_OK || rss == NULL) {
+ char short_url[19];
+ strncpy(short_url, url, 18);
+ short_url[18] = '\0';
+ asprintf(&feed->title, "%s%s", "(bad) ", short_url);
return;
}
@@ -59,16 +62,17 @@ fetch_feed(feed_t* feed, char* url)
for (mrss_item_t* it = rss->item; it; it = it->next) {
char* title = (it->title && *it->title) ? it->title : "";
const char* link = (it->link && *it->link) ? it->link : "";
+ const char* comments = (it->comments && *it->comments) ? it->comments : "";
char* desc = (it->description && *it->description) ? it->description : "";
const char* date = (it->pubDate && *it->pubDate) ? it->pubDate : "";
const char* author = (it->author && *it->author) ? it->author : "";
remove_all_tags(desc);
- const size_t title_ = strlen(title), link_ = strlen(link), desc_ = strlen(desc),
- date_ = strlen(date), author_ = strlen(author);
+ const size_t title_ = strlen(title), link_ = strlen(link), comments_ = strlen(comments),
+ desc_ = strlen(desc), date_ = strlen(date), author_ = strlen(author);
- size_t data_len = title_ + link_ + desc_ + date_ + author_ + 1;
+ size_t data_len = title_ + link_ + comments_ + desc_ + date_ + author_ + 1;
char* d = calloc(data_len + 1, sizeof(char));
@@ -76,6 +80,7 @@ fetch_feed(feed_t* feed, char* url)
strncat(d, title, title_);
strncat(d, link, link_);
+ strncat(d, comments, comments_);
strncat(d, desc, desc_);
strncat(d, date, date_);
strncat(d, author, author_);
@@ -90,8 +95,9 @@ fetch_feed(feed_t* feed, char* url)
.data_len = data_len,
.title = (slice_t) { .start = 0, .len = title_ },
.link = (slice_t) { .start = title_, .len = link_ },
- .date = (slice_t) { .start = title_ + link_, .len = date_ },
- .author = (slice_t) { .start = title_ + link_ + date_, .len = author_ },
+ .comments = (slice_t) { .start = title_ + link_, .len = comments_ },
+ .date = (slice_t) { .start = title_ + link_ + comments_, .len = date_ },
+ .author = (slice_t) { .start = title_ + link_ + comments_ + date_, .len = author_ },
};
if (feed->posts_len == feed->posts_cap) {
@@ -99,11 +105,6 @@ fetch_feed(feed_t* feed, char* url)
feed->posts = realloc(feed->posts, feed->posts_cap);
}
feed->posts[feed->posts_len++] = post;
-
- // if (it->link && *it->link) {
- // const char* domain = host_from_url(it->link);
- // printf("%-60.60s \t%s \t%s\n", title, domain, it->link);
- // }
}
mrss_free(rss);
diff --git a/keys.c b/keys.c
@@ -1,4 +1,6 @@
#include <sys/param.h>
+
+#include "config.h"
#include "readr.h"
#include "tui.h"
#include "utils.h"
@@ -37,15 +39,29 @@ handle_key(app_t* app, struct tb_event ev)
break;
case TB_KEY_ENTER: {
if (app->selected_panel == 1) {
- static char url[512] = { 0 };
+ char url[URL_CAP] = { 0 };
post_t* post = app->feeds[app->selected_feed]->posts[app->selected_post];
- size_t len = MIN(512, post->link.len);
+ size_t len = MIN(URL_CAP, post->link.len);
+ if (len == 0) return;
strncpy(url, &post->data[post->link.start], len);
url[len] = '\0';
open_url(url);
}
- } break;
+ break;
+ }
default:
break;
}
+
+ if (ev.ch == ' ') { // TB_KEY_SPACE for some reason doesn't work /shrug
+ if (app->selected_panel == 1) {
+ char url[URL_CAP] = { 0 };
+ post_t* post = app->feeds[app->selected_feed]->posts[app->selected_post];
+ size_t len = MIN(URL_CAP, post->comments.len);
+ if (len == 0) return;
+ strncpy(url, &post->data[post->comments.start], len);
+ url[len] = '\0';
+ open_url(url);
+ }
+ }
}
diff --git a/readr.h b/readr.h
@@ -14,7 +14,7 @@ typedef struct {
typedef struct {
const char* data;
size_t data_len;
- slice_t title, link, desc, date, author;
+ slice_t title, link, comments, desc, date, author;
} post_t;
typedef struct {
@@ -22,7 +22,6 @@ typedef struct {
char* title;
post_t** posts;
int posts_len, posts_cap;
-
} feed_t;
typedef struct {
diff --git a/render.c b/render.c
@@ -1,7 +1,8 @@
+#include <sys/param.h>
+
#include "termbox2.h"
#include "tui.h"
#include "config.h"
-#include <sys/param.h>
static void
draw_top_bar(void)
@@ -12,7 +13,7 @@ draw_top_bar(void)
}
tb_print(1, 0, TEXT_COLOR, LINE_COLOR, "readr");
- tb_print(width - 10, 0, TEXT_COLOR, LINE_COLOR, "[q] quit");
+ tb_print(width - 51, 0, TEXT_COLOR, LINE_COLOR, "[enter] open link [space] open comments [q] quit");
}
static void
@@ -52,11 +53,8 @@ render(app_t* app)
size_t len = MIN(TITLE_CAP, post->title.len);
strncpy(title, &post->data[post->title.start], len);
title[len] = '\0';
- // printf("%s/%zu", title, post->title.len);
- // exit(0);
color = (app->selected_panel == 1 && i == app->selected_post) ? (POST_COLOR | TB_REVERSE)
: (POST_COLOR);
- // nonascii_replace(title, '\'');
tb_print(FEED_CAP + 10, 4 + i, color, TB_DEFAULT, title);
}
diff --git a/tui.c b/tui.c
@@ -6,30 +6,29 @@
int
present(app_t* app)
{
- int ret = tb_init();
+ struct tb_event ev = { 0 };
+ int can_poll = -1;
+ int cannot_init = tb_init();
+
tb_hide_cursor();
- if (ret) {
+ if (cannot_init) {
fprintf(stderr, "could not TUI\n");
exit(1);
}
render(app);
- struct tb_event ev;
- int res;
-
while (1) {
- res = tb_poll_event(&ev);
+ can_poll = tb_poll_event(&ev);
- if (res == TB_OK) {
+ if (can_poll == TB_OK) {
switch (ev.type) {
case (TB_EVENT_KEY):
- if (ev.key == TB_KEY_CTRL_Q || ev.key == TB_KEY_F10 || ev.ch == 'q') goto RIP;
+ if (ev.key == TB_KEY_CTRL_Q || ev.key == TB_KEY_ESC || ev.ch == 'q') goto RIP;
handle_key(app, ev);
break;
case (TB_EVENT_MOUSE):
- // todo handle mouse events
break;
case (TB_EVENT_RESIZE):
break;
@@ -37,15 +36,16 @@ present(app_t* app)
render(app);
- } else if (res == TB_ERR_POLL && tb_last_errno() == EINTR) {
+ } else if (can_poll == TB_ERR_POLL && tb_last_errno() == EINTR) {
continue;
- } else if (res != TB_ERR_NO_EVENT) {
- fprintf(stderr, "(aborting) renderer error: %s\n", tb_strerror(res));
- return 1;
+
+ } else if (can_poll != TB_ERR_NO_EVENT) {
+ fprintf(stderr, "(aborting) renderer error: %s\n", tb_strerror(can_poll));
+ goto RIP;
}
}
RIP:
tb_shutdown();
- return 0;
+ exit(1);
}