code wiki / _hdl_build / nx_epub_book.nx
nx_epub_book.nx
buildroot/runtime/_hdl_build/nx_epub_book.nx
about
nx_epub_book.nx -- SOVEREIGN FULL-BOOK EPUB extraction (Reader-arc RUNG 2b, deliverable 1).
Builds on nx_epub_read.nx (RUNG 2, GREEN over real Calibre epubs). Same sovereign parse path:
ZIP central-directory walker + nx_deflate inflate + container.xml -> OPF -> spine + nx_html_to_text.
NEW logic only (everything else reused verbatim):
(a) walk ALL spine items in reading order (not the first-real-chapter pick),
(b) decode_numeric_refs: a sovereign post-pass that rewrites &#DDDD; / &#xHHHH; numeric character
references to UTF-8 bytes (nx_html_to_text leaves these RAW -- the rung-2 output showed literal
’ / “ / —; fixed here so curly quotes / em-dashes render natively),
(c) TOC/nav skip: flag the nav/TOC doc so the reader opens at real prose, but STILL include it in
chapters[] in reading order (operator requirement: skip-as-opening only, include all),
(d) emit knowledge/staging/media/reader/<slug>/book.json {title,author,format,nchapters,
chapters:[{idx,title,file,chars,is_nav}]} + one chap<K>.txt per chapter. ADDITIVE (rule 13).
SOVEREIGN: pure Nishi organs + raw syscalls. NO python/unzip/3rd-party-xml/gcc/sh, NO calibre binaries.
NO-FAKE-GREEN: a REAL Calibre epub; title/author/spine/text MEASURED from bytes; after decode, NO chapter
may contain "&#" (has_raw_entities asserted 0). license_tier: ORIGINAL
dependencies 6 imports · 0 importers
imports: nx_syscalls.nxnx_itoa_lib.nxnx_deflate.nxnx_html_to_text.nxnx_epub_nav.nxnx_charset.nx
imported by: nobody (leaf or entry point)
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 24 | const CHAP_MAGIC_1252: i64 = 1252 |
| 25 | const CHAP_MAGIC_1200: i64 = 1200 |
| 26 | const CHAP_MAGIC_1024: i64 = 1024 |
| 27 | const CHAP_MAGIC_262144: i64 = 262144 |
| 28 | const CHAP_MAGIC_2097152: i64 = 2097152 |
| 29 | const CHAP_MAGIC_8192: i64 = 8192 |
| 30 | const CHAP_MAGIC_65536: i64 = 65536 |
| 31 | const CHAP_MAGIC_16384: i64 = 16384 |
| 32 | const CHAP_MAGIC_2048: i64 = 2048 |
| 33 | const CHAP_MAGIC_32768: i64 = 32768 |
| 34 | const CHAP_MAGIC_5381: i64 = 5381 |
| 35 | const CHAP_MAGIC_2000: i64 = 2000 |
| 409 | const CHAP_CAP: i64 = 4194304 |
functions
| 38 | func er_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n } |
| 39 | func er_w(fd: i64, s: *u8) -> i64 { sys_write(fd, s, er_slen(s)); return 0 } |
| 44 | func er_n(fd: i64, v: i64) -> i64 { nxi_fd(fd, v); return 0 } |
| 45 | func er_p(s: *u8) -> i64 { er_w(1, s); return 0 } |
| 46 | func er_pn(v: i64) -> i64 { er_n(1, v); return 0 } |
| 48 | func le16(b: *u8, o: i64) -> i64 { return (b[o] as i64) | ((b[o+1] as i64) << 8) } called by 1: zip_find |
| 49 | func le32(b: *u8, o: i64) -> i64 { return (b[o] as i64) | ((b[o+1] as i64) << 8) | ((b[o+2] as i64) << 16) | ((b[o+3] as i64) << 24) } called by 1: zip_find |
| 51 | func er_cat(dst: *u8, off: i64, s: *u8) -> i64 { var i: i64=0; while s[i]!=(0 as u8){dst[off+i]=s[i];i=i+1} return off+i } |
| 52 | func er_catn(dst: *u8, off: i64, v: i64) -> i64 { let t: *u8=sys_mmap(28); var m: i64=v; var k: i64=0; if m==0{t[0]=48 as u8;k=1}; while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1}; var i: i64=0; while i<k{dst[off+i]=t[k-1-i];i=i+1} return off+k } |
| 55 | func find_sub(hay: *u8, hl: i64, needle: *u8) -> i64 called by 9: attr_valelem_texthref_for_idget_spine_idrefsis_nav_doceb_heading_inner+3 calls 1: er_slen |
| 70 | func count_sub(hay: *u8, hl: i64, needle: *u8) -> i64 |
| 85 | func attr_val(hay: *u8, hl: i64, attr: *u8, out: *u8, outcap: i64) -> i64 called by 5: href_for_idelem_owner_hrefimg_markerizefn_markerizedo_full_book calls 2: find_suber_slen |
| 96 | func elem_text(hay: *u8, hl: i64, opentag: *u8, out: *u8, outcap: i64) -> i64 |
| 108 | func find_eocd(z: *u8, zl: i64) -> i64 called by 1: zip_find |
| 118 | func zip_find(z: *u8, zl: i64, target: *u8, ob: *i64) -> i64 |
| 153 | func zip_read_named(z: *u8, zl: i64, target: *u8, out: *u8, outcap: i64) -> i64 |
| 175 | func href_for_id(opf: *u8, ol: i64, idref: *u8, out: *u8, outcap: i64) -> i64 |
| 190 | func join_path(opfpath: *u8, href: *u8, out: *u8) -> i64 |
| 204 | func get_spine_idrefs(opf: *u8, ol: i64, arena: *u8, offs: *i64, cap: i64) -> i64 |
| 231 | func er_wjson(fd: i64, s: *u8) -> i64 |
| 251 | func utf8_put(out: *u8, o: i64, cp: i64) -> i64 called by 1: decode_numeric_refs |
| 275 | func decode_numeric_refs(txt: *u8, n: i64, out: *u8, outcap: i64) -> i64 |
| 337 | func is_nav_doc(raw: *u8, rawn: i64, text: *u8, textn: i64) -> i64 |
| 350 | func first_line(txt: *u8, n: i64, out: *u8, outcap: i64) -> i64 called by 1: chap_title |
| 367 | func eb_heading_inner(raw: *u8, rawn: i64, open: *u8, close: *u8, out: *u8, cap: i64) -> i64 |
| 389 | func chap_title(raw: *u8, rawn: i64, dtxt: *u8, dn: i64, k: i64, out: *u8, outcap: i64) -> i64 |
| 412 | func ensure_dir(path: *u8) -> i64 { __syscall(258, 0-100, path, 0x1ed, 0, 0, 0); return 0 } |
| 416 | func elem_owner_href(opf: *u8, ol: i64, marker: *u8, out: *u8, outcap: i64) -> i64 |
| 426 | func base_after_slash(src: *u8, out: *u8, outcap: i64) -> i64 |
| 436 | func strip_frag(s: *u8) -> i64 |
| 441 | func str_eq0(a: *u8, b: *u8) -> i64 { var i: i64=0; while a[i]!=(0 as u8){ if a[i]!=b[i]{return 0} i=i+1 } if b[i]!=(0 as u8){return 0} return 1 } called by 1: do_full_book |
| 447 | func img_markerize(raw: *u8, n: i64, out: *u8, cap: i64) -> i64 |
| 485 | func tag_is(raw: *u8, n: i64, i: i64, tag: *u8) -> i64 called by 1: fn_markerize |
| 506 | func fn_markerize(raw: *u8, n: i64, out: *u8, cap: i64, fnctx: *i64) -> i64 |
| 560 | func do_full_book(epath: *u8, slug: *u8) -> i64 |
| 860 | func main(argc: i64, argv: *i64) -> i64 |