commit 7687a366dd35e55370e135973a15b77332515024
parent ea3b4ef7aa28bd5f0accb146805703c15a8e6919
Author: citbl <citbl@citbl.org>
Date: Sun, 12 Oct 2025 16:06:58 +1000
1.3 with better safety and clean ups
Diffstat:
8 files changed, 62 insertions(+), 41 deletions(-)
diff --git a/.clangd b/.clangd
@@ -3,6 +3,13 @@ CompileFlags:
-x,c,
-Wall,
-Wextra,
+ -Wshadow,
+ -Wpointer-arith,
+ -Wcast-qual,
+ -Wcast-align,
+ -Wstrict-prototypes,
+ -Wmissing-prototypes,
+ -Wconversion,
"-std=c99",
-g,
"-I/opt/homebrew/include"
diff --git a/src/config.h b/src/config.h
@@ -1,6 +1,6 @@
#pragma once
-#define VERSION "v1.2"
+#define VERSION "v1.3"
// source file for RSS URLS, one per line
// folder and file must exist
diff --git a/src/db.c b/src/db.c
@@ -155,7 +155,7 @@ db_fetch_posts(const char* feed_url)
p->seen = sqlite3_column_int(st, 7);
if (len == cap) {
cap = cap ? cap * 2 : MAX_POST_PER_FEED;
- rows = (db_post_t**)realloc(rows, sizeof(db_post_t*) * cap);
+ rows = (db_post_t**)realloc(rows, sizeof(db_post_t*) * (size_t)cap);
if (rows == NULL) {
fprintf(stderr, "could not reallocate for list of posts fetch from db\n");
break;
diff --git a/src/feeds.c b/src/feeds.c
@@ -34,7 +34,7 @@ load_app(char* contents)
if (app.feeds_cap == app.feeds_len) {
app.feeds_cap *= 2;
- app.feeds = realloc(app.feeds, app.feeds_cap);
+ app.feeds = realloc(app.feeds, (size_t)app.feeds_cap);
}
app.feeds[app.feeds_len++] = feed;
line = strtok(NULL, "\n");
@@ -46,20 +46,22 @@ load_app(char* contents)
void
fetch_feed(feed_t* feed, char* url)
{
- *feed = (feed_t) { .url = url, /*.posts_cap = POSTS_CAP*/ };
-
static mrss_t* rss = NULL;
+ *feed = (feed_t) { .url = url };
+ feed->title = (char*)ecalloc(FEED_CAP, sizeof(char));
+
mrss_error_t rc = mrss_parse_url(url, &rss);
if (rc != MRSS_OK || rss == NULL) {
- char short_url[FEEDS_CAP - 1];
- strncpy(short_url, url, FEEDS_CAP - 2);
- short_url[FEEDS_CAP - 2] = '\0';
- asprintf(&feed->title, "%s%s", "(bad) ", short_url);
+ snprintf(feed->title, FEED_CAP, "%s%s ", "(bad) ", url ? url : "(unknown feed url)");
return;
}
- feed->title = (rss->title != NULL) ? strndup(rss->title, FEED_CAP) : "Unknown feed";
+ snprintf(feed->title, FEED_CAP, "%s", rss->title ? rss->title : "(unknown feedtitle)");
+
+ // size_t len = MIN(FEED_CAP, strlen(rss->title) + 1);
+ // feed->title = (rss->title != NULL) ? strndup(rss->title, len) : "Unknown feed";
+ // feed->title[len - 1] = '\0';
for (mrss_item_t* it = rss->item; it; it = it->next) {
char* title = (it->title && *it->title) ? it->title : "";
diff --git a/src/readr_main.c b/src/readr_main.c
@@ -1,6 +1,5 @@
#include <stdlib.h>
#include <string.h>
-#include <locale.h>
#include <mrss.h>
#define UTILS_IMPL
@@ -15,14 +14,8 @@
int
main(void)
{
- // for unicode blocks/lines, curvy quotes etc.
- const char* ok = setlocale(LC_ALL, "C.UTF-8");
-
- if (!ok) {
- perror("setlocale failed");
- return 1;
- }
-
+ switch_locale();
+ validate_config();
db_create();
const char* path = expand_tilde(FEEDS);
diff --git a/src/render.c b/src/render.c
@@ -18,7 +18,7 @@ draw_bars(void)
for (i = 2; i < height - 2; i++) {
tb_set_cell(0, i, VERT_LINE, LINE_COLOR, BACK_COLOR);
- tb_set_cell(FEED_CAP + 5, i, VERT_LINE, LINE_COLOR, BACK_COLOR); /* separator line */
+ tb_set_cell(FEED_CAP + 2, i, VERT_LINE, LINE_COLOR, BACK_COLOR); /* separator line */
tb_set_cell(width - 1, i, VERT_LINE, LINE_COLOR, BACK_COLOR);
}
@@ -38,13 +38,13 @@ draw_bars(void)
// corners top
tb_set_cell(0, 1, 0x250C, LINE_COLOR, BACK_COLOR);
- tb_set_cell(FEED_CAP + 5, 1, 0x252C, LINE_COLOR, BACK_COLOR);
+ tb_set_cell(FEED_CAP + 2, 1, 0x252C, LINE_COLOR, BACK_COLOR);
tb_set_cell(width - 1, 1, 0x2510, LINE_COLOR, BACK_COLOR);
// corners bottom
tb_set_cell(0, height - 2, 0x2514, LINE_COLOR, BACK_COLOR);
- tb_set_cell(FEED_CAP + 5, height - 2, 0x2534, LINE_COLOR, BACK_COLOR);
+ tb_set_cell(FEED_CAP + 2, height - 2, 0x2534, LINE_COLOR, BACK_COLOR);
tb_set_cell(width - 1, height - 2, 0x2518, LINE_COLOR, BACK_COLOR);
}
@@ -60,7 +60,6 @@ render(app_t* app)
{
int i;
uintattr_t color;
- int title_len, domain_len;
char title[TITLE_CAP] = { 0 };
char domain[DOMAIN_CAP] = { 0 };
db_post_t* post;
@@ -92,15 +91,13 @@ render(app_t* app)
for (i = 0; i < visible_len; i++) {
post = selected_feed->posts[i];
- title_len = MIN(TITLE_CAP, strlen(post->title)) + 1;
- strncpy(title, post->title, title_len - 1);
- title[title_len - 1] = ' '; // overlapping with domain
- title[title_len] = '\0';
+ // title - added space for when title and domain overlap
+ snprintf(title, TITLE_CAP, "%s ", post->title ? post->title : "(bad title");
+ // domain
const char* domain_host = host_from_url(post->link);
- domain_len = MIN(DOMAIN_CAP, strlen(domain_host));
- strncpy(domain, domain_host, domain_len);
- domain[domain_len] = '\0';
+ snprintf(domain, DOMAIN_CAP, "%s", domain_host ? domain_host : "(bad title)");
+ int domain_len = (int)strlen(domain);
if (app->selected_panel == 1 && i == app->selected_post)
if (post->seen)
@@ -113,7 +110,7 @@ render(app_t* app)
color = (POST_COLOR);
tb_print(width - domain_len - 2, 3 + i, DOMAIN_COLOR, BACK_COLOR, domain);
- tb_print(FEED_CAP + 7, 3 + i, color, BACK_COLOR, title);
+ tb_print(FEED_CAP + 4, 3 + i, color, BACK_COLOR, title);
}
tb_present();
diff --git a/src/utils.c b/src/utils.c
@@ -2,8 +2,31 @@
#include <strings.h>
#include <errno.h>
#include <stdarg.h>
+#include <locale.h>
#include "utils.h"
+#include "config.h"
+
+void
+validate_config(void)
+{
+ if (strlen(FEEDS) < 3 || strlen(FEEDS) > 1024) panic("invalid FEEDS path");
+ if (strlen(DB_PATH) < 3 || strlen(DB_PATH) > 1024) panic("invalid DB_PATH path");
+
+ if (MAX_POST_PER_FEED < 1 || MAX_POST_PER_FEED > 256) panic("invalid MAX_POST_PER_FEED (1~256)");
+ if (POSTS_CAP < 16 || POSTS_CAP > 512) panic("invalid POSTS_CAP (16~512)");
+ if (FEEDS_CAP < 16 || FEEDS_CAP > 512) panic("invalid FEEDS_CAP (16~512)");
+ if (TITLE_CAP < 16 || TITLE_CAP > 512) panic("invalid TITLE_CAP (16~512)");
+ if (DOMAIN_CAP < 16 || DOMAIN_CAP > 512) panic("invalid DOMAIN_CAP (16~512)");
+ if (URL_CAP < 2048 || URL_CAP > 8192) panic("invalid URL_CAP (2048~8192)");
+}
+
+void
+switch_locale(void)
+{
+ // for unicode blocks/lines, curvy quotes etc.
+ if (!setlocale(LC_ALL, "C.UTF-8")) panic("could not switch to UTF-8!");
+}
void
panic(const char* fmt, ...)
@@ -21,13 +44,6 @@ panic(const char* fmt, ...)
exit(1);
}
-void
-util_fill(int* dst, size_t n, int value)
-{
- for (size_t i = 0; i < n; ++i)
- dst[i] = value;
-}
-
#if defined(_WIN32)
// to be tested on windows, and then have a shower
@@ -285,7 +301,7 @@ nonascii_replace(char* s, char to)
}
r += (i == need) ? need : 1; // tolerate invalid UTF-8
- if (w == (unsigned char*)s || w[-1] != ' ') *w++ = to;
+ if (w == (unsigned char*)s || w[-1] != ' ') *w++ = (unsigned char)to;
}
}
*w = '\0';
diff --git a/src/utils.h b/src/utils.h
@@ -5,13 +5,19 @@
#define MIN(A, B) ((A) < (B) ? (A) : (B))
#define MAX(A, B) ((A) > (B) ? (A) : (B))
+void validate_config(void);
+void switch_locale(void);
+
void panic(const char*, ...);
+void* ecalloc(size_t, size_t);
+
const char* get_home_dir(void);
const char* expand_tilde(const char*);
char* read_file(const char*);
char* host_from_url(const char*);
+
+void nonascii_replace(char*, char);
void remove_all_chars(char*, char);
void remove_all_tags(char*);
+
int open_url(const char*);
-void nonascii_replace(char*, char);
-void* ecalloc(size_t, size_t);