ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
mdl_config.c
Go to the documentation of this file.
1
9#include "mdl_config.h"
10
11#include <string.h>
12
13#include "mdl_politeness.h"
14#include "ra8_attributes.h"
15
17typedef enum : uint16_t {
18 k_line_max = 512,
20
27
37
39typedef enum : uint8_t {
44
52
60
63{
64 while ((*s == ' ') || (*s == '\t') || (*s == '\r') || (*s == '\n')) {
65 ++s;
66 }
67 size_t n = strlen(s);
68 while ((n > 0U) && ((s[n - 1U] == ' ') || (s[n - 1U] == '\t') || (s[n - 1U] == '\r') ||
69 (s[n - 1U] == '\n'))) {
70 s[n - 1U] = '\0';
71 --n;
72 }
73 return s;
74}
75
92RA8_INTERNAL static bool internal_config_set_str(char* dst, size_t cap, const char* val)
93{
94 const size_t len = strlen(val);
95 if (len >= cap) {
96 return false;
97 }
98 memcpy(dst, val, len + 1U);
99 return true;
100}
101
121internal_config_apply_string(mdl_site_t* s, const char* key, const char* val)
122{
123 const struct {
124 const char* key;
125 char* dst;
126 size_t cap;
127 } fields[] = {
128 {"name", s->name, sizeof(s->name)},
129 {"host", s->host, sizeof(s->host)},
130 {"kind", s->kind, sizeof(s->kind)},
131 {"contact", s->contact, sizeof(s->contact)},
132 {"chapter_url_contains", s->chapter_url_contains, sizeof(s->chapter_url_contains)},
133 {"chapter_url_prefix", s->chapter_url_prefix, sizeof(s->chapter_url_prefix)},
134 {"page_img_attr", s->page_img_attr, sizeof(s->page_img_attr)},
135 {"page_img_url_contains", s->page_img_url_contains, sizeof(s->page_img_url_contains)},
136 {"search_url", s->search_url, sizeof(s->search_url)},
137 {"search_result_contains", s->search_result_contains, sizeof(s->search_result_contains)},
138 {"browse_url", s->browse_url, sizeof(s->browse_url)},
139 {"series_title_selector", s->series_title_selector, sizeof(s->series_title_selector)},
140 {"series_summary_selector", s->series_summary_selector, sizeof(s->series_summary_selector)},
141 {"series_author_selector", s->series_author_selector, sizeof(s->series_author_selector)},
142 {"series_artist_selector", s->series_artist_selector, sizeof(s->series_artist_selector)},
143 {"series_cover_selector", s->series_cover_selector, sizeof(s->series_cover_selector)},
144 {"chapter_title_selector", s->chapter_title_selector, sizeof(s->chapter_title_selector)},
145 {"chapter_number_selector", s->chapter_number_selector, sizeof(s->chapter_number_selector)},
146 {"language", s->language, sizeof(s->language)},
147 {"reading_direction", s->reading_direction, sizeof(s->reading_direction)},
148 };
149 for (size_t i = 0U; i < (sizeof(fields) / sizeof(fields[0])); ++i) {
150 if (strcmp(key, fields[i].key) == 0) {
151 return internal_config_set_str(fields[i].dst, fields[i].cap, val) ? k_kv_applied
152 : k_kv_invalid;
153 }
154 }
155 return k_kv_unknown;
156}
157
173RA8_INTERNAL static bool internal_config_parse_u32(const char* val, uint32_t* out)
174{
175 if ((val[0] == '\0') || (val[0] == '+') || (val[0] == '-')) {
176 return false;
177 }
178 uint32_t parsed = 0U;
179 for (size_t index = 0U; val[index] != '\0'; ++index) {
180 if ((val[index] < '0') || (val[index] > '9')) {
181 return false;
182 }
183 const uint32_t digit = (uint32_t)(val[index] - '0');
184 if (parsed > ((UINT32_MAX - digit) / (uint32_t)k_config_dec_base)) {
185 return false;
186 }
187 parsed = (parsed * (uint32_t)k_config_dec_base) + digit;
188 }
189 *out = parsed;
190 return true;
191}
192
212internal_config_apply_u32(mdl_site_t* s, const char* key, const char* val)
213{
214 const struct {
215 const char* key;
216 uint32_t* dst;
217 } fields[] = {
218 {"img_delay_min", &s->img_delay_min},
219 {"img_delay_max", &s->img_delay_max},
220 {"chapter_delay_min", &s->chapter_delay_min},
221 {"chapter_delay_max", &s->chapter_delay_max},
222 {"rate_per_min", &s->rate_per_min},
223 {"burst", &s->burst},
224 {"backoff_base_ms", &s->backoff_base_ms},
225 {"backoff_max_ms", &s->backoff_max_ms},
226 {"max_inflight", &s->max_inflight},
227 };
228 for (size_t i = 0U; i < (sizeof(fields) / sizeof(fields[0])); ++i) {
229 if (strcmp(key, fields[i].key) == 0) {
230 return internal_config_parse_u32(val, fields[i].dst) ? k_kv_applied : k_kv_invalid;
231 }
232 }
233 return k_kv_unknown;
234}
235
252{
253 if (strcmp(val, "reverse") == 0) {
254 *out = k_mdl_order_reverse;
255 return true;
256 }
257 if (strcmp(val, "asc") == 0) {
258 *out = k_mdl_order_asc;
259 return true;
260 }
261 if (strcmp(val, "doc") == 0) {
262 *out = k_mdl_order_doc;
263 return true;
264 }
265 return false;
266}
267
285RA8_INTERNAL static bool internal_config_apply_pair(mdl_site_t* s, const char* key, const char* val)
286{
287 const mdl_kv_result_t str_result = internal_config_apply_string(s, key, val);
288 if (str_result != k_kv_unknown) {
289 return str_result == k_kv_applied;
290 }
291 const mdl_kv_result_t int_result = internal_config_apply_u32(s, key, val);
292 if (int_result != k_kv_unknown) {
293 return int_result == k_kv_applied;
294 }
295 if (strcmp(key, "chapter_order") == 0) {
297 }
298 return false;
299}
300
314{
315 *out = (mdl_site_t){};
316 (void)internal_config_set_str(out->name, sizeof(out->name), "site");
317 (void)internal_config_set_str(out->kind, sizeof(out->kind), "manhwa");
319 sizeof(out->chapter_url_contains),
320 "chapter");
321 (void)internal_config_set_str(out->page_img_attr, sizeof(out->page_img_attr), "data-src");
322 (void)internal_config_set_str(out->language, sizeof(out->language), "en");
323 (void)internal_config_set_str(out->reading_direction, sizeof(out->reading_direction), "ltr");
325 out->img_delay_min = (uint32_t)k_def_img_delay_min;
326 out->img_delay_max = (uint32_t)k_def_img_delay_max;
329
330 /* Governor bounds default to the conservative single source of truth. */
332 out->rate_per_min = gov.rate_per_min;
333 out->burst = gov.burst;
336 out->max_inflight = (uint32_t)gov.max_inflight;
337}
338
358internal_config_next(internal_config_reader_t* reader, uint8_t* out_byte, bool* out_eof)
359{
360 *out_eof = false;
361 while (reader->position == reader->available) {
362 if (reader->eof) {
363 *out_eof = true;
364 return k_ra8_ok;
365 }
366 if (reader->read_calls >= (uint32_t)k_config_read_calls) {
368 }
369 uint32_t count = 0U;
370 const ra8_err_t err = fw_fs_read(&reader->file,
371 reader->storage->io_buffer,
372 reader->storage->io_buffer_bytes,
373 &count);
374 ++reader->read_calls;
375 if (err != k_ra8_ok) {
376 return err;
377 }
378 reader->position = 0U;
379 reader->available = count;
380 reader->eof = count == 0U;
381 }
382 *out_byte = reader->storage->io_buffer[reader->position];
383 ++reader->position;
384 return k_ra8_ok;
385}
386
407 char* line,
408 uint32_t capacity,
409 bool* out_present)
410{
411 uint32_t length = 0U;
412 *out_present = false;
413 for (;;) {
414 uint8_t byte = 0U;
415 bool eof = false;
416 ra8_err_t err = internal_config_next(reader, &byte, &eof);
417 if (err != k_ra8_ok) {
418 return err;
419 }
420 if (eof) {
421 line[length] = '\0';
422 *out_present = length > 0U;
423 return k_ra8_ok;
424 }
425 *out_present = true;
426 if (byte == (uint8_t)'\n') {
427 line[length] = '\0';
428 return k_ra8_ok;
429 }
430 if ((length + 1U) >= capacity) {
432 }
433 line[length] = (char)byte;
434 ++length;
435 }
436}
437
455{
456 char* text = internal_config_trim(line);
457 if ((text[0] == '\0') || (text[0] == '#') || (text[0] == '[')) {
458 return k_ra8_ok;
459 }
460 char* equals = strchr(text, '=');
461 if (equals == nullptr) {
463 }
464 *equals = '\0';
465 const char* key = internal_config_trim(text);
466 const char* val = internal_config_trim(equals + 1);
467 return ((key[0] != '\0') && internal_config_apply_pair(site, key, val)) ? k_ra8_ok
469}
470
489 mdl_site_t* out)
490{
491 for (;;) {
492 char line[k_line_max];
493 bool present = false;
494 ra8_err_t err = internal_config_read_line(reader, line, sizeof(line), &present);
495 if ((err != k_ra8_ok) || !present) {
496 return err;
497 }
498 err = internal_config_apply_line(out, line);
499 if (err != k_ra8_ok) {
500 return err;
501 }
502 }
503}
504
521{
522 const char* selectors[] = {site->series_title_selector,
529 for (size_t i = 0U; i < (sizeof(selectors) / sizeof(selectors[0])); ++i) {
530 const char* selector = selectors[i];
531 if (selector[0] == '\0') {
532 continue;
533 }
534 const char* prefixes[] = {"meta:", "class:", "label:", "literal:"};
535 bool valid = false;
536 for (size_t prefix_index = 0U; prefix_index < (sizeof(prefixes) / sizeof(prefixes[0]));
537 ++prefix_index) {
538 const size_t prefix_len = strlen(prefixes[prefix_index]);
539 if ((strncmp(selector, prefixes[prefix_index], prefix_len) == 0) &&
540 (selector[prefix_len] != '\0')) {
541 valid = true;
542 break;
543 }
544 }
545 if (!valid) {
546 return false;
547 }
548 }
549 return (site->host[0] != '\0') && (site->language[0] != '\0') &&
550 ((strcmp(site->reading_direction, "ltr") == 0) ||
551 (strcmp(site->reading_direction, "rtl") == 0)) &&
552 (site->img_delay_min <= site->img_delay_max) &&
553 (site->chapter_delay_min <= site->chapter_delay_max) && (site->burst > 0U) &&
554 (site->max_inflight > 0U) && (site->backoff_base_ms <= site->backoff_max_ms);
555}
556
557ra8_err_t mdl_config_load(mdl_storage_t* storage, const char* path, mdl_site_t* out)
558{
559 if ((storage == nullptr) || (storage->fs == nullptr) || (storage->file_workspace == nullptr) ||
560 (storage->io_buffer == nullptr) || (storage->io_buffer_bytes == 0U) || (path == nullptr) ||
561 (out == nullptr)) {
563 }
565
566 internal_config_reader_t reader = {.storage = storage};
567 ra8_err_t err = fw_fs_open(&storage->fs->streams,
568 path,
570 &reader.file,
571 storage->file_workspace,
572 storage->file_workspace_bytes);
573 if (err != k_ra8_ok) {
574 return err;
575 }
576 uint64_t file_bytes = 0U;
577 err = fw_fs_file_size(&reader.file, &file_bytes);
578 if ((err == k_ra8_ok) && (file_bytes > (uint64_t)k_config_file_max)) {
580 }
581 if (err == k_ra8_ok) {
582 err = internal_config_parse(&reader, out);
583 }
584 const ra8_err_t close_err = fw_fs_close(&reader.file);
585 if (close_err != k_ra8_ok) {
586 return close_err;
587 }
588 if (err != k_ra8_ok) {
589 return err;
590 }
592}
593
595{
597 cfg.rate_per_min = site->rate_per_min;
598 cfg.burst = site->burst;
600 cfg.backoff_max_ms = site->backoff_max_ms;
601 cfg.max_inflight = (uint16_t)site->max_inflight;
602 return cfg;
603}
604
620RA8_INTERNAL static uint32_t internal_config_max_u32(uint32_t a, uint32_t b)
621{
622 return (a > b) ? a : b;
623}
624
ra8_err_t fw_fs_open(const fw_fs_stream_port_t *streams, const char *path, fw_fs_open_mode_t mode, fw_fs_file_t *file, void *workspace, uint32_t workspace_size)
Open a file into a caller-owned handle and backend workspace.
Definition fw_if_fs.c:648
ra8_err_t fw_fs_read(fw_fs_file_t *file, uint8_t *dst, uint32_t cap, uint32_t *out_read)
Read up to cap bytes; zero bytes is EOF.
Definition fw_if_fs.c:699
ra8_err_t fw_fs_close(fw_fs_file_t *file)
Close and consume an open handle.
Definition fw_if_fs.c:783
ra8_err_t fw_fs_file_size(fw_fs_file_t *file, uint64_t *out_size)
Report the open file's current length.
Definition fw_if_fs.c:758
@ k_fw_fs_open_read
Existing file, read-only.
@ k_polite_img_min_ms
Polite per-image floor.
@ k_polite_img_max_ms
Polite per-image ceiling.
static uint32_t internal_config_max_u32(uint32_t a, uint32_t b)
Return the larger of two unsigned values.
Definition mdl_config.c:620
mdl_config_defaults_t
Default politeness bounds (ms) applied before the file is read.
Definition mdl_config.c:46
@ k_def_img_delay_max
Per-image spacing ceiling.
Definition mdl_config.c:48
@ k_def_chapter_delay_min
Inter-chapter spacing floor.
Definition mdl_config.c:49
@ k_def_img_delay_min
Per-image spacing floor.
Definition mdl_config.c:47
@ k_def_chapter_delay_max
Inter-chapter spacing ceiling.
Definition mdl_config.c:50
static void internal_config_set_defaults(mdl_site_t *out)
Apply the polite default descriptor before the file overrides it.
Definition mdl_config.c:313
mdl_config_polite_floor_t
--polite per-request delay floors (milliseconds).
Definition mdl_config.c:54
@ k_polite_chap_min_ms
Polite inter-chapter floor.
Definition mdl_config.c:57
@ k_polite_chap_max_ms
Polite inter-chapter ceiling.
Definition mdl_config.c:58
ra8_err_t mdl_config_load(mdl_storage_t *storage, const char *path, mdl_site_t *out)
Load a site descriptor from a flat key=value file.
Definition mdl_config.c:557
mdl_gov_cfg_t mdl_config_gov_cfg(const mdl_site_t *site)
Build the politeness-governor tunables for a loaded site descriptor.
Definition mdl_config.c:594
static mdl_kv_result_t internal_config_apply_u32(mdl_site_t *s, const char *key, const char *val)
Apply an unsigned-integer key (jitter delay or governor bound).
Definition mdl_config.c:212
mdl_parse_limits_t
Local parser limits.
Definition mdl_config.c:17
@ k_line_max
Max config line bytes including NUL.
Definition mdl_config.c:18
static bool internal_config_valid(const mdl_site_t *site)
Validate descriptor cross-field and selector invariants.
Definition mdl_config.c:520
static ra8_err_t internal_config_next(internal_config_reader_t *reader, uint8_t *out_byte, bool *out_eof)
Read one byte through a bounded buffered portable stream.
Definition mdl_config.c:358
static mdl_kv_result_t internal_config_apply_string(mdl_site_t *s, const char *key, const char *val)
Apply a string-valued key and distinguish unknown from invalid.
Definition mdl_config.c:121
static ra8_err_t internal_config_apply_line(mdl_site_t *site, char *line)
Apply one already bounded descriptor record.
Definition mdl_config.c:454
static bool internal_config_apply_pair(mdl_site_t *s, const char *key, const char *val)
Apply one descriptor key/value pair.
Definition mdl_config.c:285
static bool internal_config_set_str(char *dst, size_t cap, const char *val)
Copy one descriptor value without truncation.
Definition mdl_config.c:92
void mdl_config_apply_polite(mdl_site_t *site)
Raise a descriptor's per-request delay floors for --polite.
Definition mdl_config.c:625
static ra8_err_t internal_config_parse(internal_config_reader_t *reader, mdl_site_t *out)
Parse every descriptor record from an open portable stream.
Definition mdl_config.c:488
static bool internal_config_parse_u32(const char *val, uint32_t *out)
Parse one complete unsigned 32-bit decimal field.
Definition mdl_config.c:173
static char * internal_config_trim(char *s)
Trim leading/trailing ASCII whitespace in place; return start.
Definition mdl_config.c:62
static ra8_err_t internal_config_read_line(internal_config_reader_t *reader, char *line, uint32_t capacity, bool *out_present)
Read one complete bounded descriptor line.
Definition mdl_config.c:406
static bool internal_config_parse_order(const char *val, mdl_chapter_order_t *out)
Parse the configured chapter-order spelling.
Definition mdl_config.c:251
mdl_config_io_limit_t
Complete descriptor and backend-progress ceilings.
Definition mdl_config.c:22
@ k_config_dec_base
Base-10 integer fields.
Definition mdl_config.c:25
@ k_config_file_max
Largest accepted descriptor file.
Definition mdl_config.c:23
@ k_config_read_calls
One-byte reads plus EOF.
Definition mdl_config.c:24
mdl_kv_result_t
Result of applying one descriptor key.
Definition mdl_config.c:39
@ k_kv_unknown
Key is not owned by this helper.
Definition mdl_config.c:40
@ k_kv_applied
Key and value were accepted.
Definition mdl_config.c:41
@ k_kv_invalid
Key is known but value is invalid.
Definition mdl_config.c:42
Per-site descriptor loaded from a flat key=value config file.
mdl_chapter_order_t
How to order chapters extracted from a series page.
Definition mdl_config.h:25
@ k_mdl_order_asc
Sort by the number parsed from the URL.
Definition mdl_config.h:28
@ k_mdl_order_reverse
Reverse document order (newest-first -> old).
Definition mdl_config.h:27
@ k_mdl_order_doc
Keep document order.
Definition mdl_config.h:26
Jittered inter-request delay plus the per-host politeness governor.
mdl_gov_cfg_t mdl_gov_cfg_default(void)
Conservative default tunables for a site that configures none.
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
@ k_ra8_err_invalid_arg
Invalid function argument.
Definition ra8_err.h:152
@ k_ra8_err_invalid_state
Module in wrong state for requested operation.
Definition ra8_err.h:161
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
@ k_ra8_err_invalid_size
Invalid size parameter (too large, too small, or misaligned).
Definition ra8_err.h:167
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
int strncmp(const char *s1, const char *s2, size_t n)
Compare two strings up to a specified length.
int strcmp(const char *s1, const char *s2)
Compare two null-terminated strings.
size_t strlen(const char *s)
Calculate string length.
void * memcpy(void *dst, const void *src, size_t n)
Copy memory area between non-overlapping regions.
char * strchr(const char *s, int c)
Locate first occurrence of character in string.
Caller-owned open file; fields are private to the facade.
fw_fs_stream_port_t streams
Byte-stream operations.
Buffered reader borrowing one initialized storage binding.
Definition mdl_config.c:29
uint32_t read_calls
Bounded backend read calls.
Definition mdl_config.c:34
fw_fs_file_t file
Caller-owned portable handle.
Definition mdl_config.c:31
uint32_t available
Readable scratch byte count.
Definition mdl_config.c:33
mdl_storage_t * storage
Injected filesystem and scratch.
Definition mdl_config.c:30
bool eof
Clean zero-byte read observed.
Definition mdl_config.c:35
uint32_t position
Next unread scratch offset.
Definition mdl_config.c:32
Per-site politeness tunables the governor is initialised with.
uint32_t burst
Token-bucket capacity, in requests (>= 1).
uint32_t rate_per_min
Sustained per-host request ceiling (0 = off).
uint16_t max_inflight
Per-host in-flight request cap (>= 1).
uint32_t backoff_max_ms
Backoff-window ceiling.
uint32_t backoff_base_ms
First backoff window on a 429/503.
One site's scraping rules (all data, no logic).
Definition mdl_config.h:46
char chapter_number_selector[k_mdl_match_max]
Per-chapter number selector.
Definition mdl_config.h:80
uint32_t chapter_delay_min
Inter-chapter spacing floor.
Definition mdl_config.h:89
char browse_url[k_mdl_search_url_max]
Latest-updates page (no {q}).
Definition mdl_config.h:67
char series_author_selector[k_mdl_match_max]
Series writer selector.
Definition mdl_config.h:75
char kind[k_mdl_kind_max]
Content kind hint (reader mode).
Definition mdl_config.h:49
uint32_t burst
Token-bucket capacity, in requests.
Definition mdl_config.h:95
uint32_t rate_per_min
Sustained per-host request ceiling (0 = off).
Definition mdl_config.h:94
char search_url[k_mdl_search_url_max]
Query template with a {q} slot.
Definition mdl_config.h:63
uint32_t img_delay_max
Per-image spacing ceiling.
Definition mdl_config.h:88
char reading_direction[k_mdl_kind_max]
ltr or rtl.
Definition mdl_config.h:83
char name[k_mdl_name_max]
Human-readable site name.
Definition mdl_config.h:47
uint32_t chapter_delay_max
Inter-chapter spacing ceiling.
Definition mdl_config.h:90
char host[k_mdl_host_max]
Site host, e.g.
Definition mdl_config.h:48
char contact[k_mdl_contact_max]
Operator contact for the UA.
Definition mdl_config.h:50
char chapter_title_selector[k_mdl_match_max]
Per-chapter title selector.
Definition mdl_config.h:78
char page_img_url_contains[k_mdl_match_max]
Keep only URLs with this.
Definition mdl_config.h:60
char series_cover_selector[k_mdl_match_max]
Series cover-URL selector.
Definition mdl_config.h:77
uint32_t backoff_base_ms
First backoff window on a 429/503.
Definition mdl_config.h:96
char chapter_url_contains[k_mdl_match_max]
href must contain this.
Definition mdl_config.h:53
uint32_t backoff_max_ms
Backoff-window ceiling.
Definition mdl_config.h:97
char language[k_mdl_kind_max]
BCP-47 language tag.
Definition mdl_config.h:82
char series_summary_selector[k_mdl_match_max]
Series synopsis selector.
Definition mdl_config.h:73
char page_img_attr[k_mdl_attr_max]
Preferred attr (fallback other).
Definition mdl_config.h:59
mdl_chapter_order_t chapter_order
Ordering to apply.
Definition mdl_config.h:56
uint32_t img_delay_min
Per-image spacing floor.
Definition mdl_config.h:87
char search_result_contains[k_mdl_match_max]
Series-link substring on results.
Definition mdl_config.h:65
char series_artist_selector[k_mdl_match_max]
Series artist selector.
Definition mdl_config.h:76
uint32_t max_inflight
Per-host in-flight request cap.
Definition mdl_config.h:98
char chapter_url_prefix[k_mdl_search_url_max]
Absolute series-chapter prefix.
Definition mdl_config.h:54
char series_title_selector[k_mdl_match_max]
Series display-title selector.
Definition mdl_config.h:71
One non-reentrant downloader filesystem dependency bundle.
Definition mdl_storage.h:40
uint32_t file_workspace_bytes
File workspace extent.
Definition mdl_storage.h:45
uint8_t * io_buffer
Caller-owned stream scratch.
Definition mdl_storage.h:44
const fw_fs_t * fs
Injected portable filesystem.
Definition mdl_storage.h:41
void * file_workspace
Open-file backend state.
Definition mdl_storage.h:42
uint32_t io_buffer_bytes
Stream scratch extent.
Definition mdl_storage.h:47