hachem.wtf
aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhachem <im@hachem.wtf>2026-09-02 21:57:15 +0200
committerhachem <im@hachem.wtf>2026-09-02 21:57:15 +0200
commit0893b9de6610f0b94e79203afa094340487a9d93 (patch)
treefc18e61e7718ba55dc680585c367d0de6930fb9b
parent6a21c072ae0e08c3ed664312f6c8b1aa10051170 (diff)
fix: refactor slightly
-rw-r--r--jafm.c352
1 files changed, 225 insertions, 127 deletions
diff --git a/jafm.c b/jafm.c
index 3e19d14..424fc5a 100644
--- a/jafm.c
+++ b/jafm.c
@@ -38,6 +38,16 @@
#define WEIGHT_SYNOPSIS 50
#define WEIGHT_BODY 1
+#define C_RESET "\x1b[0m"
+#define C_DIM "\x1b[2m"
+#define C_BOLD "\x1b[1m"
+#define C_KEY "\x1b[36m"
+#define C_TITLE "\x1b[1;36m"
+#define C_NAME "\x1b[1;36m"
+#define C_SYMBOL "\x1b[33m"
+#define C_MATCH "\x1b[1;33m"
+#define C_COUNT "\x1b[32m"
+
struct Match
{
char* path;
@@ -128,6 +138,17 @@ static bool ends_with(const char* string, const char* suffix)
return strcmp(string + string_length - suffix_length, suffix) == 0;
}
+static void strip_compression_suffix(char* name)
+{
+ static const char* suffixes[] = {".gz", ".zst", ".xz", ".bz2"};
+ for (size_t index = 0; index < sizeof(suffixes) / sizeof(suffixes[0]); index++)
+ if (ends_with(name, suffixes[index]))
+ {
+ name[strlen(name) - strlen(suffixes[index])] = '\0';
+ return;
+ }
+}
+
static bool is_section_header(const char* line)
{
return strncmp(line, ".SH", 3) == 0 || strncmp(line, ".Sh", 3) == 0;
@@ -269,6 +290,13 @@ struct Finding
char kind[KIND_SIZE];
};
+struct Search
+{
+ const char* query;
+ bool exact;
+ const char* want_kind;
+};
+
static void scan_line(struct Scan* scan, const char* line, const char* query)
{
if (is_section_header(line))
@@ -334,24 +362,17 @@ static long scan_score(const struct Scan* scan)
+ scan->synopsis_hits * WEIGHT_SYNOPSIS + scan->body_hits * WEIGHT_BODY;
}
-static char* read_plain(const char* path)
+static char* read_stream(FILE* stream)
{
- FILE* page = fopen(path, "rb");
- if (page == NULL)
- return NULL;
-
size_t capacity = 65536;
size_t length = 0;
char* buffer = malloc(capacity);
if (buffer == NULL)
- {
- (void) fclose(page);
return NULL;
- }
for (;;)
{
- size_t got = fread(buffer + length, 1, capacity - length - 1, page);
+ size_t got = fread(buffer + length, 1, capacity - length - 1, stream);
length += got;
if (length + 1 < capacity)
break;
@@ -361,17 +382,41 @@ static char* read_plain(const char* path)
if (grown == NULL)
{
free(buffer);
- (void) fclose(page);
return NULL;
}
buffer = grown;
}
- (void) fclose(page);
buffer[length] = '\0';
return buffer;
}
+static char* read_plain(const char* path)
+{
+ FILE* page = fopen(path, "rb");
+ if (page == NULL)
+ return NULL;
+
+ char* buffer = read_stream(page);
+ (void) fclose(page);
+ return buffer;
+}
+
+static char* read_command(const char* tool, const char* path)
+{
+ char command[8192];
+ (void) snprintf(command, sizeof(command), "%s -dc -- '%s' 2>/dev/null", tool,
+ path);
+
+ FILE* pipe = popen(command, "r");
+ if (pipe == NULL)
+ return NULL;
+
+ char* buffer = read_stream(pipe);
+ (void) pclose(pipe);
+ return buffer;
+}
+
static char* read_gzip(const char* path)
{
gzFile page = gzopen(path, "rb");
@@ -413,6 +458,20 @@ static char* read_gzip(const char* path)
return buffer;
}
+static char* read_page(const char* path)
+{
+ if (ends_with(path, ".gz"))
+ return read_gzip(path);
+ if (ends_with(path, ".zst"))
+ return read_command("zstd", path);
+ if (ends_with(path, ".xz"))
+ return read_command("xz", path);
+ if (ends_with(path, ".bz2"))
+ return read_command("bzip2", path);
+
+ return read_plain(path);
+}
+
static void scan_buffer(char* buffer, const char* query, struct Scan* scan)
{
if (strstr(buffer, query) == NULL)
@@ -430,20 +489,20 @@ static void scan_buffer(char* buffer, const char* query, struct Scan* scan)
}
}
-static void page_find(const char* path, const char* query, bool exact,
- const char* want_kind, struct Finding* finding)
+static void page_find(const char* path, const struct Search* search,
+ struct Finding* finding)
{
- char* buffer = ends_with(path, ".gz") ? read_gzip(path) : read_plain(path);
+ char* buffer = read_page(path);
if (buffer == NULL)
return;
struct Scan scan = {0};
- scan.exact = exact;
- scan.want_kind = want_kind;
- scan_buffer(buffer, query, &scan);
+ scan.exact = search->exact;
+ scan.want_kind = search->want_kind;
+ scan_buffer(buffer, search->query, &scan);
free(buffer);
- if (want_kind != NULL)
+ if (search->want_kind != NULL)
{
if (!scan.has_wanted)
return;
@@ -451,7 +510,8 @@ static void page_find(const char* path, const char* query, bool exact,
finding->score = scan_score(&scan);
(void) snprintf(finding->symbol, sizeof(finding->symbol), "%s",
scan.wanted_symbol);
- (void) snprintf(finding->kind, sizeof(finding->kind), "%s", want_kind);
+ (void) snprintf(finding->kind, sizeof(finding->kind), "%s",
+ search->want_kind);
return;
}
@@ -486,9 +546,7 @@ static bool filename_matches(const char* name, const char* query)
char base[256];
(void) snprintf(base, sizeof(base), "%s", name);
- size_t length = strlen(base);
- if (length > 3 && strcmp(base + length - 3, ".gz") == 0)
- base[length - 3] = '\0';
+ strip_compression_suffix(base);
char* section_dot = strrchr(base, '.');
if (section_dot != NULL)
@@ -569,9 +627,7 @@ static void page_label(const char* path, char* label, size_t label_size)
char base[256];
(void) snprintf(base, sizeof(base), "%s", name);
- size_t length = strlen(base);
- if (length > 3 && strcmp(base + length - 3, ".gz") == 0)
- base[length - 3] = '\0';
+ strip_compression_suffix(base);
char* section_dot = strrchr(base, '.');
if (section_dot != NULL)
@@ -650,18 +706,18 @@ static size_t print_list_cell(const struct Match* item, bool selected, size_t bu
char label[256];
page_label(item->path, label, sizeof(label));
- size_t used = print_segment(label, selected ? "\x1b[1m" : "\x1b[2m", budget);
+ size_t used = print_segment(label, selected ? C_BOLD : C_DIM, budget);
if (item->symbol[0] != '\0')
{
used += print_segment(" ", "", budget - used);
- used += print_segment(item->symbol, "\x1b[33m", budget - used);
+ used += print_segment(item->symbol, C_SYMBOL, budget - used);
if (item->kind[0] != '\0')
{
char kind[KIND_SIZE + 3];
(void) snprintf(kind, sizeof(kind), " (%s)", item->kind);
- used += print_segment(kind, "\x1b[2m", budget - used);
+ used += print_segment(kind, C_DIM, budget - used);
}
}
@@ -682,97 +738,112 @@ static void print_highlighted(const char* text, const char* query)
hit = strstr(cursor, query))
{
(void) printf("%.*s", (int) (hit - cursor), cursor);
- (void) printf("\x1b[1;33m%.*s\x1b[0m", (int) query_length, hit);
+ (void) printf(C_MATCH "%.*s" C_RESET, (int) query_length, hit);
cursor = hit + query_length;
}
(void) printf("%s", cursor);
}
-static size_t render_menu(const struct MatchList* matches, size_t highlight,
- size_t top, size_t visible, size_t cols,
- const char* query, size_t previous_height)
+struct Layout
{
- if (previous_height > 0)
- (void) printf("\x1b[%zuA", previous_height);
-
- size_t indent = 2;
- size_t usable = cols > indent + 1 ? cols - indent - 1 : 0;
- bool panel = usable >= PANEL_MIN_WIDTH;
- size_t separator = 3;
+ bool panel;
+ size_t usable;
+ size_t left_width;
+ size_t right_width;
+};
- size_t left_width = usable;
- if (panel)
+static size_t list_column_width(const struct MatchList* matches)
+{
+ size_t widest = LIST_WIDTH;
+ for (size_t index = 0; index < matches->count; index++)
{
- left_width = usable * 2 / 5;
- if (left_width < LIST_WIDTH)
- left_width = LIST_WIDTH;
- if (left_width > 64)
- left_width = 64;
+ char label[256];
+ page_label(matches->items[index].path, label, sizeof(label));
+
+ const char* symbol = matches->items[index].symbol;
+ const char* kind = matches->items[index].kind;
+
+ size_t entry = strlen(label) + 2;
+ if (symbol[0] != '\0')
+ {
+ entry += 2 + strlen(symbol);
+ if (kind[0] != '\0')
+ entry += 3 + strlen(kind);
+ }
+
+ if (entry > widest)
+ widest = entry;
}
- size_t right_width = panel ? usable - left_width - separator : 0;
- const struct Match* current = &matches->items[highlight];
- size_t height = 0;
+ return widest;
+}
- (void) printf("\r\x1b[2K \x1b[1;36mjafm\x1b[0m \x1b[2m%zu matches\x1b[0m\r\n",
- matches->count);
- height++;
+static struct Layout compute_layout(const struct MatchList* matches, size_t cols)
+{
+ size_t indent = 2;
+ size_t usable = cols > indent + 1 ? cols - indent - 1 : 0;
- (void) printf("\x1b[2K\r\n");
- height++;
+ struct Layout layout = {false, usable, usable, 0};
+ if (usable < PANEL_MIN_WIDTH)
+ return layout;
- size_t end = top + visible;
- if (end > matches->count)
- end = matches->count;
+ size_t separator = 3;
+ size_t minimum_preview = 24;
+ size_t cap = usable - minimum_preview - separator;
+ size_t widest = list_column_width(matches);
- for (size_t row = 0; row < visible; row++)
- {
- (void) printf("\x1b[2K ");
+ layout.panel = true;
+ layout.left_width = widest < cap ? widest : cap;
+ layout.right_width = usable - layout.left_width - separator;
+ return layout;
+}
- size_t list_index = top + row;
- if (list_index < end)
- {
- bool selected = list_index == highlight;
+static void render_row(const struct MatchList* matches, size_t list_index,
+ size_t highlight, size_t end, const struct Layout* layout,
+ const struct Match* current, size_t row, const char* query)
+{
+ (void) printf("\x1b[2K ");
- if (selected)
- (void) printf("\x1b[36m\xe2\x9d\xaf\x1b[0m ");
- else
- (void) printf(" ");
+ if (list_index < end)
+ {
+ bool selected = list_index == highlight;
+ if (selected)
+ (void) printf(C_KEY "\xe2\x9d\xaf" C_RESET " ");
+ else
+ (void) printf(" ");
- size_t budget = left_width > 2 ? left_width - 2 : 0;
- size_t used = print_list_cell(&matches->items[list_index], selected,
- budget);
+ size_t budget = layout->left_width > 2 ? layout->left_width - 2 : 0;
+ size_t used = print_list_cell(&matches->items[list_index], selected, budget);
- if (panel)
- for (size_t pad = used + 2; pad < left_width; pad++)
- (void) putchar(' ');
- }
- else if (panel)
- for (size_t pad = 0; pad < left_width; pad++)
+ if (layout->panel)
+ for (size_t pad = used + 2; pad < layout->left_width; pad++)
(void) putchar(' ');
+ }
+ else if (layout->panel)
+ for (size_t pad = 0; pad < layout->left_width; pad++)
+ (void) putchar(' ');
- if (panel)
- {
- (void) printf("\x1b[2m \xe2\x94\x82\x1b[0m ");
+ if (layout->panel)
+ {
+ (void) printf(C_DIM " \xe2\x94\x82" C_RESET " ");
- size_t preview_index = current->preview_start + row;
- if (current->preview_loaded && preview_index < current->preview_count)
- {
- char cell[1024];
- (void) fit(cell, sizeof(cell), current->preview[preview_index],
- right_width);
- print_highlighted(cell, query);
- }
+ size_t preview_index = current->preview_start + row;
+ if (current->preview_loaded && preview_index < current->preview_count)
+ {
+ char cell[1024];
+ (void) fit(cell, sizeof(cell), current->preview[preview_index],
+ layout->right_width);
+ print_highlighted(cell, query);
}
-
- (void) printf("\r\n");
- height++;
}
- (void) printf("\x1b[2K\r\n");
- height++;
+ (void) printf("\r\n");
+}
+static void render_footer(const struct Match* current, size_t highlight,
+ size_t count, size_t usable)
+{
(void) printf("\x1b[2K ");
const char* path = current->path;
const char* slash = strrchr(path, '/');
@@ -781,29 +852,62 @@ static size_t render_menu(const struct MatchList* matches, size_t highlight,
char directory[1024];
(void) snprintf(directory, sizeof(directory), "%.*s",
(int) (slash - path + 1), path);
- size_t used = print_segment(directory, "\x1b[2m", usable);
- (void) print_segment(slash + 1, "\x1b[1;36m", usable - used);
+ size_t used = print_segment(directory, C_DIM, usable);
+ (void) print_segment(slash + 1, C_NAME, usable - used);
}
else
- (void) print_segment(path, "\x1b[1;36m", usable);
+ (void) print_segment(path, C_NAME, usable);
(void) printf("\r\n");
- height++;
(void) printf("\x1b[2K ");
size_t used = 0;
- used += print_segment("up/down", "\x1b[36m", usable - used);
- used += print_segment(" move ", "\x1b[2m", usable - used);
- used += print_segment("enter", "\x1b[36m", usable - used);
- used += print_segment(" open ", "\x1b[2m", usable - used);
- used += print_segment("q", "\x1b[36m", usable - used);
- used += print_segment(" quit ", "\x1b[2m", usable - used);
+ used += print_segment("up/down", C_KEY, usable - used);
+ used += print_segment(" move ", C_DIM, usable - used);
+ used += print_segment("enter", C_KEY, usable - used);
+ used += print_segment(" open ", C_DIM, usable - used);
+ used += print_segment("q", C_KEY, usable - used);
+ used += print_segment(" quit ", C_DIM, usable - used);
+
char counter[32];
- (void) snprintf(counter, sizeof(counter), "[%zu/%zu]", highlight + 1,
- matches->count);
- (void) print_segment(counter, "\x1b[32m", usable - used);
+ (void) snprintf(counter, sizeof(counter), "[%zu/%zu]", highlight + 1, count);
+ (void) print_segment(counter, C_COUNT, usable - used);
(void) printf("\r\n");
+}
+
+static size_t render_menu(const struct MatchList* matches, size_t highlight,
+ size_t top, size_t visible, size_t cols,
+ const char* query, size_t previous_height)
+{
+ if (previous_height > 0)
+ (void) printf("\x1b[%zuA", previous_height);
+
+ struct Layout layout = compute_layout(matches, cols);
+ const struct Match* current = &matches->items[highlight];
+ size_t height = 0;
+
+ (void) printf("\r\x1b[2K " C_TITLE "jafm" C_RESET " " C_DIM "%zu matches" C_RESET
+ "\r\n", matches->count);
+ height++;
+
+ (void) printf("\x1b[2K\r\n");
height++;
+ size_t end = top + visible;
+ if (end > matches->count)
+ end = matches->count;
+
+ for (size_t row = 0; row < visible; row++)
+ {
+ render_row(matches, top + row, highlight, end, &layout, current, row, query);
+ height++;
+ }
+
+ (void) printf("\x1b[2K\r\n");
+ height++;
+
+ render_footer(current, highlight, matches->count, layout.usable);
+ height += 2;
+
(void) printf("\x1b[J");
return height;
}
@@ -991,25 +1095,26 @@ static void collect_sections(const char* directory, struct TaskList* tasks)
(void) closedir(directory_stream);
}
-static void scan_task(const struct Task* task, const char* query, bool exact,
- const char* want_kind, struct MatchList* matches)
+static void scan_task(const struct Task* task, const struct Search* search,
+ struct MatchList* matches)
{
const char* path = task->path;
struct Finding finding = {0};
- page_find(path, query, exact, want_kind, &finding);
+ page_find(path, search, &finding);
- if (want_kind != NULL && finding.score == 0)
+ if (search->want_kind != NULL && finding.score == 0)
return;
long score = finding.score * task->weight;
const char* base = strrchr(path, '/');
base = base != NULL ? base + 1 : path;
- if (filename_matches(base, query))
+ if (filename_matches(base, search->query))
{
score += WEIGHT_FILENAME;
if (finding.symbol[0] == '\0')
- (void) snprintf(finding.symbol, sizeof(finding.symbol), "%s", query);
+ (void) snprintf(finding.symbol, sizeof(finding.symbol), "%s",
+ search->query);
}
if (score > 0)
@@ -1021,9 +1126,7 @@ struct Worker
const struct Task* tasks;
size_t start;
size_t end;
- const char* query;
- bool exact;
- const char* want_kind;
+ const struct Search* search;
struct MatchList matches;
};
@@ -1031,8 +1134,7 @@ static void* worker_main(void* argument)
{
struct Worker* worker = argument;
for (size_t index = worker->start; index < worker->end; index++)
- scan_task(&worker->tasks[index], worker->query, worker->exact,
- worker->want_kind, &worker->matches);
+ scan_task(&worker->tasks[index], worker->search, &worker->matches);
return NULL;
}
@@ -1048,9 +1150,8 @@ static size_t worker_count(size_t task_count, size_t requested)
return count == 0 ? 1 : count;
}
-static void scan_tasks(const struct TaskList* tasks, const char* query, bool exact,
- const char* want_kind, struct MatchList* matches,
- size_t requested)
+static void scan_tasks(const struct TaskList* tasks, const struct Search* search,
+ struct MatchList* matches, size_t requested)
{
size_t threads = worker_count(tasks->count, requested);
@@ -1072,9 +1173,7 @@ static void scan_tasks(const struct TaskList* tasks, const char* query, bool exa
workers[index].tasks = tasks->items;
workers[index].start = offset;
workers[index].end = offset + chunk;
- workers[index].query = query;
- workers[index].exact = exact;
- workers[index].want_kind = want_kind;
+ workers[index].search = search;
offset += chunk;
(void) pthread_create(&handles[index], NULL, worker_main, &workers[index]);
}
@@ -1141,9 +1240,7 @@ static bool page_man_args(const char* path, char* root, size_t root_size,
char work[4096];
(void) snprintf(work, sizeof(work), "%s", path);
- size_t length = strlen(work);
- if (length > 3 && strcmp(work + length - 3, ".gz") == 0)
- work[length - 3] = '\0';
+ strip_compression_suffix(work);
char* name_slash = strrchr(work, '/');
if (name_slash == NULL)
@@ -1404,8 +1501,9 @@ int main(int argc, char** argv)
directory = strtok_r(NULL, ":", &save_pointer))
collect_sections(directory, &tasks);
+ struct Search search = {query, exact, want_kind};
struct MatchList matches = {0};
- scan_tasks(&tasks, query, exact, want_kind, &matches, requested_threads);
+ scan_tasks(&tasks, &search, &matches, requested_threads);
task_list_free(&tasks);
if (matches.count == 0)