text/plain
•
8.46 KB
•
337 lines
#include "bookmark.h"
#include "storage.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BOOKMARK_DIR ".onigiri"
#define BOOKMARK_FILE ".onigiri/state.toml"
#define BOOKMARK_BUF_SIZE 2048
// Format milliseconds as HH:MM:SS
static void format_timestamp(uint32_t ms, char *buf, size_t buf_size) {
uint32_t total_sec = ms / 1000;
uint32_t hours = total_sec / 3600;
uint32_t mins = (total_sec % 3600) / 60;
uint32_t secs = total_sec % 60;
snprintf(buf, buf_size, "%02u:%02u:%02u", (unsigned)hours, (unsigned)mins,
(unsigned)secs);
}
// Parse HH:MM:SS timestamp to milliseconds
static uint32_t parse_timestamp(const char *ts) {
unsigned h = 0, m = 0, s = 0;
if (sscanf(ts, "%u:%u:%u", &h, &m, &s) == 3) {
return (h * 3600 + m * 60 + s) * 1000;
}
return 0;
}
// Read entire bookmark file into buffer. Returns bytes read.
static size_t read_bookmark_file(char *buf, size_t buf_size) {
storage_file_t f = storage_open(BOOKMARK_FILE, "r");
if (!f) {
buf[0] = '\0';
return 0;
}
size_t n = storage_read(f, buf, buf_size - 1);
storage_close(f);
buf[n] = '\0';
return n;
}
// Write buffer contents to bookmark file
static bool write_bookmark_file(const char *buf, size_t len) {
storage_mkdir(BOOKMARK_DIR);
storage_file_t f = storage_open(BOOKMARK_FILE, "w");
if (!f)
return false;
size_t written = storage_write(f, buf, len);
storage_close(f);
return written == len;
}
uint32_t bookmark_load(const char *path) {
if (!path || !path[0])
return 0;
char buf[BOOKMARK_BUF_SIZE];
if (read_bookmark_file(buf, sizeof(buf)) == 0)
return 0;
// Search for the key line: "path" = "HH:MM:SS"
char key[STORAGE_MAX_PATH + 4];
snprintf(key, sizeof(key), "\"%s\"", path);
char *line = buf;
while (line && *line) {
char *eol = strchr(line, '\n');
char *found = strstr(line, key);
if (found && found == line) {
// Found the key at start of line, find the value
char *eq = strchr(found, '=');
if (eq) {
char *quote1 = strchr(eq, '"');
if (quote1) {
quote1++;
char *quote2 = strchr(quote1, '"');
if (quote2) {
char ts[16];
size_t ts_len = (size_t)(quote2 - quote1);
if (ts_len < sizeof(ts)) {
memcpy(ts, quote1, ts_len);
ts[ts_len] = '\0';
return parse_timestamp(ts);
}
}
}
}
}
if (!eol)
break;
line = eol + 1;
}
return 0;
}
bool bookmark_save(const char *path, uint32_t position_ms) {
if (!path || !path[0])
return false;
char buf[BOOKMARK_BUF_SIZE];
size_t len = read_bookmark_file(buf, sizeof(buf));
char ts[16];
format_timestamp(position_ms, ts, sizeof(ts));
// Build the new line
char new_line[STORAGE_MAX_PATH + 32];
int new_line_len =
snprintf(new_line, sizeof(new_line), "\"%s\" = \"%s\"\n", path, ts);
if (new_line_len <= 0 || (size_t)new_line_len >= sizeof(new_line))
return false;
// Build key to search for
char key[STORAGE_MAX_PATH + 4];
snprintf(key, sizeof(key), "\"%s\"", path);
// Output buffer
char out[BOOKMARK_BUF_SIZE];
size_t out_len = 0;
bool replaced = false;
bool in_bookmarks = false;
char *line = buf;
while (line && *line) {
char *eol = strchr(line, '\n');
size_t line_len = eol ? (size_t)(eol - line + 1) : strlen(line);
// Check for [bookmarks] section header
if (strncmp(line, "[bookmarks]", 11) == 0) {
in_bookmarks = true;
} else if (line[0] == '[') {
in_bookmarks = false;
}
// Replace existing entry
if (in_bookmarks && strstr(line, key) == line) {
if (out_len + (size_t)new_line_len < sizeof(out)) {
memcpy(out + out_len, new_line, (size_t)new_line_len);
out_len += (size_t)new_line_len;
replaced = true;
}
} else {
if (out_len + line_len < sizeof(out)) {
memcpy(out + out_len, line, line_len);
out_len += line_len;
}
}
if (!eol)
break;
line = eol + 1;
}
// If no existing entry, append
if (!replaced) {
if (len == 0) {
// New file - add section header
const char *header = "[bookmarks]\n";
size_t hlen = strlen(header);
if (out_len + hlen < sizeof(out)) {
memcpy(out + out_len, header, hlen);
out_len += hlen;
}
}
if (out_len + (size_t)new_line_len < sizeof(out)) {
memcpy(out + out_len, new_line, (size_t)new_line_len);
out_len += (size_t)new_line_len;
}
}
out[out_len] = '\0';
return write_bookmark_file(out, out_len);
}
bool bookmark_load_string(const char *path, char *out_buf,
size_t out_buf_size) {
if (!path || !path[0] || !out_buf || out_buf_size == 0)
return false;
char buf[BOOKMARK_BUF_SIZE];
if (read_bookmark_file(buf, sizeof(buf)) == 0)
return false;
char key[STORAGE_MAX_PATH + 4];
snprintf(key, sizeof(key), "\"%s\"", path);
char *line = buf;
while (line && *line) {
char *eol = strchr(line, '\n');
char *found = strstr(line, key);
if (found && found == line) {
char *eq = strchr(found, '=');
if (eq) {
char *quote1 = strchr(eq, '"');
if (quote1) {
quote1++;
char *quote2 = strchr(quote1, '"');
if (quote2) {
size_t val_len = (size_t)(quote2 - quote1);
if (val_len < out_buf_size) {
memcpy(out_buf, quote1, val_len);
out_buf[val_len] = '\0';
return true;
}
}
}
}
}
if (!eol)
break;
line = eol + 1;
}
return false;
}
bool bookmark_save_string(const char *path, const char *value) {
if (!path || !path[0] || !value)
return false;
char buf[BOOKMARK_BUF_SIZE];
size_t len = read_bookmark_file(buf, sizeof(buf));
char new_line[STORAGE_MAX_PATH + STORAGE_MAX_NAME + 16];
int new_line_len =
snprintf(new_line, sizeof(new_line), "\"%s\" = \"%s\"\n", path, value);
if (new_line_len <= 0 || (size_t)new_line_len >= sizeof(new_line))
return false;
char key[STORAGE_MAX_PATH + 4];
snprintf(key, sizeof(key), "\"%s\"", path);
char out[BOOKMARK_BUF_SIZE];
size_t out_len = 0;
bool replaced = false;
bool in_bookmarks = false;
char *line = buf;
while (line && *line) {
char *eol = strchr(line, '\n');
size_t line_len = eol ? (size_t)(eol - line + 1) : strlen(line);
if (strncmp(line, "[bookmarks]", 11) == 0) {
in_bookmarks = true;
} else if (line[0] == '[') {
in_bookmarks = false;
}
if (in_bookmarks && strstr(line, key) == line) {
if (out_len + (size_t)new_line_len < sizeof(out)) {
memcpy(out + out_len, new_line, (size_t)new_line_len);
out_len += (size_t)new_line_len;
replaced = true;
}
} else {
if (out_len + line_len < sizeof(out)) {
memcpy(out + out_len, line, line_len);
out_len += line_len;
}
}
if (!eol)
break;
line = eol + 1;
}
if (!replaced) {
if (len == 0) {
const char *header = "[bookmarks]\n";
size_t hlen = strlen(header);
if (out_len + hlen < sizeof(out)) {
memcpy(out + out_len, header, hlen);
out_len += hlen;
}
}
if (out_len + (size_t)new_line_len < sizeof(out)) {
memcpy(out + out_len, new_line, (size_t)new_line_len);
out_len += (size_t)new_line_len;
}
}
out[out_len] = '\0';
return write_bookmark_file(out, out_len);
}
bool bookmark_clear_all(void) { return storage_remove(BOOKMARK_FILE); }
bool bookmark_remove(const char *path) {
if (!path || !path[0])
return false;
char buf[BOOKMARK_BUF_SIZE];
if (read_bookmark_file(buf, sizeof(buf)) == 0)
return true;
char key[STORAGE_MAX_PATH + 4];
snprintf(key, sizeof(key), "\"%s\"", path);
char out[BOOKMARK_BUF_SIZE];
size_t out_len = 0;
bool in_bookmarks = false;
char *line = buf;
while (line && *line) {
char *eol = strchr(line, '\n');
size_t line_len = eol ? (size_t)(eol - line + 1) : strlen(line);
if (strncmp(line, "[bookmarks]", 11) == 0) {
in_bookmarks = true;
} else if (line[0] == '[') {
in_bookmarks = false;
}
// Skip matching entry
if (in_bookmarks && strstr(line, key) == line) {
// Skip this line
} else {
if (out_len + line_len < sizeof(out)) {
memcpy(out + out_len, line, line_len);
out_len += line_len;
}
}
if (!eol)
break;
line = eol + 1;
}
out[out_len] = '\0';
return write_bookmark_file(out, out_len);
}