aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/sisudoc/io_in/paths_source.d14
-rw-r--r--src/sisudoc/io_in/read_zip_pod.d122
-rw-r--r--src/sisudoc/io_out/create_abstraction_db.d355
-rw-r--r--src/sisudoc/io_out/create_abstraction_txt.d430
-rw-r--r--src/sisudoc/io_out/source_pod.d45
-rw-r--r--src/sisudoc/meta/metadoc_from_src.d19
-rw-r--r--src/sisudoc/meta/metadoc_object_setter.d1
-rwxr-xr-xsrc/sisudoc/spine.d92
8 files changed, 1064 insertions, 14 deletions
diff --git a/src/sisudoc/io_in/paths_source.d b/src/sisudoc/io_in/paths_source.d
index 9f8ae63..41353ed 100644
--- a/src/sisudoc/io_in/paths_source.d
+++ b/src/sisudoc/io_in/paths_source.d
@@ -821,6 +821,20 @@ template spinePathsPods() {
}
return _pods();
}
+ auto abstraction_root(string fn_src) {
+ auto pod_root_ = pod_root(fn_src);
+ auto pth_1_ = ((media_root(fn_src).zpod.chainPath("abstraction")).asNormalizedPath).array;
+ auto pth_2_ = ((media_root(fn_src).filesystem_open_zpod.chainPath("abstraction")).asNormalizedPath).array;
+ struct _pods {
+ auto zpod() {
+ return pth_1_;
+ }
+ auto filesystem_open_zpod() {
+ return pth_2_;
+ }
+ }
+ return _pods();
+ }
auto image_root(string fn_src) {
auto pod_root_ = pod_root(fn_src);
auto pth_1_ = ((media_root(fn_src).zpod.chainPath("image")).asNormalizedPath).array;
diff --git a/src/sisudoc/io_in/read_zip_pod.d b/src/sisudoc/io_in/read_zip_pod.d
index 38480cd..d228f4e 100644
--- a/src/sisudoc/io_in/read_zip_pod.d
+++ b/src/sisudoc/io_in/read_zip_pod.d
@@ -265,6 +265,128 @@ template spineExtractZipPod() {
return "";
}
+ /+ ↓ download a zip pod from a URL to a temp file +/
+ enum size_t MAX_DOWNLOAD_SIZE = 200 * 1024 * 1024; /+ 200 MB download limit +/
+ enum int DOWNLOAD_TIMEOUT = 120; /+ seconds +/
+
+ static auto rgx_url_zip = ctRegex!(`^https?://[a-zA-Z0-9._:/-]+[.]zip$`);
+
+ struct DownloadResult {
+ string local_path; /+ path to downloaded temp file +/
+ bool ok;
+ string error_msg;
+ }
+
+ bool isUrl(string arg) {
+ return arg.length > 8
+ && (arg[0..8] == "https://" || arg[0..7] == "http://");
+ }
+
+ @trusted DownloadResult downloadZipUrl(string url) {
+ import std.process : execute, environment;
+ DownloadResult result;
+ result.ok = false;
+ /+ ↓ validate URL scheme +/
+ if (url.length < 8 || (url[0..8] != "https://" && url[0..7] != "http://")) {
+ result.error_msg = "only http/https URLs are supported: " ~ url;
+ return result;
+ }
+ if (url[0..7] == "http://" && url[0..8] != "https://") {
+ stderr.writeln("WARNING: downloading over insecure http: ", url);
+ }
+ /+ ↓ validate URL format +/
+ if (!(url.matchFirst(rgx_url_zip))) {
+ result.error_msg = "URL does not match expected zip URL pattern: " ~ url;
+ return result;
+ }
+ /+ ↓ reject URLs that could target internal services +/
+ {
+ import std.uni : toLower;
+ string url_lower = url.toLower;
+ /+ strip scheme to get host portion +/
+ string after_scheme = (url_lower[0..8] == "https://")
+ ? url_lower[8..$]
+ : url_lower[7..$];
+ /+ extract host (up to first / or :) +/
+ string host;
+ foreach (i, c; after_scheme) {
+ if (c == '/' || c == ':') {
+ host = after_scheme[0..i];
+ break;
+ }
+ }
+ if (host.length == 0) host = after_scheme;
+ if (host == "localhost"
+ || host == "127.0.0.1"
+ || host == "::1"
+ || host == "[::1]"
+ || host == "0.0.0.0"
+ || host.canFind("169.254.")
+ || host.canFind("10.")
+ || host.canFind("192.168.")
+ ) {
+ result.error_msg = "URL targets a local/private address: " ~ url;
+ return result;
+ }
+ }
+ /+ ↓ derive filename from URL +/
+ string url_basename = url.baseName;
+ if (url_basename.length == 0 || url_basename.indexOf('.') < 0) {
+ result.error_msg = "cannot determine filename from URL: " ~ url;
+ return result;
+ }
+ /+ ↓ create temp directory for download +/
+ string tmp_base = tempDir.buildPath("spine-zip-pod");
+ try {
+ if (!exists(tmp_base))
+ mkdirRecurse(tmp_base);
+ } catch (FileException ex) {
+ result.error_msg = "failed to create temp directory: " ~ ex.msg;
+ return result;
+ }
+ string tmp_file = tmp_base.buildPath(url_basename);
+ /+ ↓ download using curl +/
+ auto curl_result = execute([
+ "curl",
+ "--silent", "--show-error",
+ "--fail", /+ fail on HTTP errors +/
+ "--location", /+ follow redirects +/
+ "--max-redirs", "5", /+ limit redirects +/
+ "--max-time", DOWNLOAD_TIMEOUT.to!string,
+ "--max-filesize", MAX_DOWNLOAD_SIZE.to!string,
+ "--proto", "=https,http", /+ restrict protocols +/
+ "--output", tmp_file,
+ url
+ ]);
+ if (curl_result.status != 0) {
+ result.error_msg = "download failed: " ~ url;
+ if (curl_result.output.length > 0)
+ result.error_msg ~= " - " ~ curl_result.output;
+ /+ clean up partial download +/
+ try { if (exists(tmp_file)) remove(tmp_file); } catch (FileException) {}
+ return result;
+ }
+ if (!exists(tmp_file) || !tmp_file.isFile) {
+ result.error_msg = "download produced no file: " ~ url;
+ return result;
+ }
+ result.local_path = tmp_file;
+ result.ok = true;
+ return result;
+ }
+
+ /+ ↓ clean up a downloaded temp file +/
+ void cleanupDownload(ref DownloadResult dlr) {
+ if (dlr.local_path.length > 0 && exists(dlr.local_path)) {
+ try {
+ remove(dlr.local_path);
+ } catch (FileException ex) {
+ stderr.writeln("WARNING: failed to clean up downloaded file: ", dlr.local_path);
+ }
+ }
+ dlr.ok = false;
+ }
+
/+ ↓ clean up extracted temp directory +/
void cleanupZipPod(ref ZipPodResult zpr) {
if (zpr.pod_dir.length > 0 && exists(zpr.pod_dir)) {
diff --git a/src/sisudoc/io_out/create_abstraction_db.d b/src/sisudoc/io_out/create_abstraction_db.d
new file mode 100644
index 0000000..20ca074
--- /dev/null
+++ b/src/sisudoc/io_out/create_abstraction_db.d
@@ -0,0 +1,355 @@
+/+
+- Name: SisuDoc Spine, Doc Reform [a part of]
+ - Description: documents, structuring, processing, publishing, search
+ - static content generator
+
+ - Author: Ralph Amissah
+ [ralph.amissah@gmail.com]
+
+ - Copyright: (C) 2015 (continuously updated, current 2026) Ralph Amissah, All Rights Reserved.
+
+ - License: AGPL 3 or later:
+
+ Spine (SiSU), a framework for document structuring, publishing and
+ search
+
+ Copyright (C) Ralph Amissah
+
+ This program is free software: you can redistribute it and/or modify it
+ under the terms of the GNU AFERO General Public License as published by the
+ Free Software Foundation, either version 3 of the License, or (at your
+ option) any later version.
+
+ This program is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program. If not, see [https://www.gnu.org/licenses/].
+
+ If you have Internet connection, the latest version of the AGPL should be
+ available at these locations:
+ [https://www.fsf.org/licensing/licenses/agpl.html]
+ [https://www.gnu.org/licenses/agpl.html]
+
+ - Spine (by Doc Reform, related to SiSU) uses standard:
+ - docReform markup syntax
+ - standard SiSU markup syntax with modified headers and minor modifications
+ - docReform object numbering
+ - standard SiSU object citation numbering & system
+
+ - Homepages:
+ [https://www.sisudoc.org]
+ [https://www.doc-reform.org]
+
+ - Git
+ [https://git.sisudoc.org/]
+
++/
+module sisudoc.io_out.create_abstraction_db;
+
+/+ ↓ write document abstraction as per-document sqlite3 database +/
+template spineAbstractionDb() {
+ import std.conv : to;
+ import std.file;
+ import std.path;
+ import std.stdio;
+ import std.string;
+ import std.array;
+ import d2sqlite3;
+ import sisudoc.io_out.paths_output;
+
+ void spineAbstractionDb(D)(D doc) {
+ auto doc_abstraction = doc.abstraction;
+ auto doc_matters = doc.matters;
+
+ /+ ↓ determine output path +/
+ auto out_pth = spineOutPaths!()(doc_matters.output_path, doc_matters.src.language);
+ string base_dir = "abstraction";
+ string base_pth = ((out_pth.output_base.chainPath(base_dir)).asNormalizedPath).array;
+ try {
+ if (!exists(base_pth)) {
+ base_pth.mkdirRecurse;
+ }
+ } catch (Exception ex) {
+ }
+ string db_file = ((base_pth.chainPath(
+ doc_matters.src.doc_uid_out ~ ".abstraction.db")).asNormalizedPath).array;
+
+ /+ ↓ remove existing file to start fresh +/
+ try {
+ if (exists(db_file)) {
+ remove(db_file);
+ }
+ } catch (Exception ex) {
+ }
+
+ if (doc_matters.opt.action.vox_gt_1) {
+ writeln(" ", db_file);
+ }
+
+ /+ ↓ open database and create schema +/
+ auto db = Database(db_file);
+ db.run("PRAGMA journal_mode=WAL");
+ db.run("PRAGMA synchronous=NORMAL");
+
+ db.run("
+ CREATE TABLE metadata (
+ key TEXT PRIMARY KEY,
+ value TEXT NOT NULL
+ );
+
+ CREATE TABLE objects (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ section TEXT NOT NULL,
+ seq INTEGER NOT NULL,
+ ocn INTEGER DEFAULT 0,
+ is_a TEXT NOT NULL,
+ is_of_part TEXT,
+ is_of_type TEXT,
+ heading_level INTEGER,
+ identifier TEXT,
+ parent_ocn INTEGER DEFAULT 0,
+ last_descendant_ocn INTEGER DEFAULT 0,
+ ancestors TEXT,
+ dummy_heading INTEGER DEFAULT 0,
+ object_number_off INTEGER DEFAULT 0,
+ indent_base INTEGER DEFAULT 0,
+ indent_hang INTEGER DEFAULT 0,
+ bullet INTEGER DEFAULT 0,
+ lang TEXT,
+ has_links INTEGER DEFAULT 0,
+ has_notes_reg INTEGER DEFAULT 0,
+ has_notes_star INTEGER DEFAULT 0,
+ has_images INTEGER DEFAULT 0,
+ segment TEXT,
+ segment_prev TEXT,
+ segment_next TEXT,
+ anchor TEXT,
+ table_cols INTEGER,
+ table_widths TEXT,
+ table_header INTEGER,
+ code_syntax TEXT,
+ code_linenumbers INTEGER DEFAULT 0,
+ text TEXT
+ );
+
+ CREATE INDEX idx_objects_section ON objects(section);
+ CREATE INDEX idx_objects_ocn ON objects(ocn);
+ CREATE INDEX idx_objects_parent ON objects(parent_ocn);
+ CREATE INDEX idx_objects_is_a ON objects(is_a);
+ CREATE INDEX idx_objects_heading ON objects(heading_level)
+ WHERE heading_level IS NOT NULL;
+ ");
+
+ /+ ↓ populate metadata +/
+ db.run("BEGIN TRANSACTION");
+
+ auto meta_stmt = db.prepare(
+ "INSERT INTO metadata (key, value) VALUES (:key, :value)"
+ );
+ auto meta = doc_matters.conf_make_meta.meta;
+
+ void insertMeta(string key, string value) {
+ if (value.length > 0) {
+ meta_stmt.bind(":key", key);
+ meta_stmt.bind(":value", value);
+ meta_stmt.execute();
+ meta_stmt.reset();
+ }
+ }
+
+ insertMeta("title.main", meta.title_main);
+ insertMeta("title.subtitle", meta.title_subtitle);
+ insertMeta("title.full", meta.title_full);
+ insertMeta("title.language", meta.title_language);
+ insertMeta("creator.author", meta.creator_author);
+ insertMeta("creator.author_surname", meta.creator_author_surname);
+ insertMeta("creator.author_surname_fn", meta.creator_author_surname_fn);
+ insertMeta("creator.author_email", meta.creator_author_email);
+ insertMeta("creator.illustrator", meta.creator_illustrator);
+ insertMeta("creator.translator", meta.creator_translator);
+ insertMeta("date.published", meta.date_published);
+ insertMeta("date.created", meta.date_created);
+ insertMeta("date.issued", meta.date_issued);
+ insertMeta("date.available", meta.date_available);
+ insertMeta("date.modified", meta.date_modified);
+ insertMeta("date.valid", meta.date_valid);
+ insertMeta("rights.copyright", meta.rights_copyright);
+ insertMeta("rights.license", meta.rights_license);
+ insertMeta("classify.topic_register", meta.classify_topic_register);
+ insertMeta("classify.subject", meta.classify_subject);
+ insertMeta("classify.keywords", meta.classify_keywords);
+ insertMeta("classify.loc", meta.classify_loc);
+ insertMeta("classify.dewey", meta.classify_dewey);
+ insertMeta("identifier.isbn", meta.identifier_isbn);
+ insertMeta("identifier.oclc", meta.identifier_oclc);
+ insertMeta("language.document", meta.language_document);
+ insertMeta("notes.abstract", meta.notes_abstract);
+ insertMeta("notes.description", meta.notes_description);
+ insertMeta("notes.summary", meta.notes_summary);
+
+ /+ ↓ make settings +/
+ auto make = doc_matters.conf_make_meta.make;
+ insertMeta("make.doc_type", make.doc_type);
+ insertMeta("make.auto_num_top_at_level", make.auto_num_top_at_level);
+ insertMeta("make.auto_num_top_lv", make.auto_num_top_lv.to!string);
+ insertMeta("make.auto_num_depth", make.auto_num_depth.to!string);
+
+ /+ ↓ doc_has counts +/
+ insertMeta("doc_has.inline_links", doc_matters.has.inline_links.to!string);
+ insertMeta("doc_has.inline_notes_reg", doc_matters.has.inline_notes_reg.to!string);
+ insertMeta("doc_has.inline_notes_star", doc_matters.has.inline_notes_star.to!string);
+ insertMeta("doc_has.tables", doc_matters.has.tables.to!string);
+ insertMeta("doc_has.codeblocks", doc_matters.has.codeblocks.to!string);
+ insertMeta("doc_has.images", doc_matters.has.images.to!string);
+ insertMeta("doc_has.poems", doc_matters.has.poems.to!string);
+ insertMeta("doc_has.groups", doc_matters.has.groups.to!string);
+ insertMeta("doc_has.blocks", doc_matters.has.blocks.to!string);
+ insertMeta("doc_has.quotes", doc_matters.has.quotes.to!string);
+
+ meta_stmt.finalize();
+
+ /+ ↓ populate objects +/
+ auto obj_stmt = db.prepare(
+ "INSERT INTO objects ("
+ ~ "section, seq, ocn, is_a, is_of_part, is_of_type,"
+ ~ "heading_level, identifier, parent_ocn, last_descendant_ocn,"
+ ~ "ancestors, dummy_heading, object_number_off,"
+ ~ "indent_base, indent_hang, bullet, lang,"
+ ~ "has_links, has_notes_reg, has_notes_star, has_images,"
+ ~ "segment, segment_prev, segment_next, anchor,"
+ ~ "table_cols, table_widths, table_header,"
+ ~ "code_syntax, code_linenumbers, text"
+ ~ ") VALUES ("
+ ~ ":section, :seq, :ocn, :is_a, :is_of_part, :is_of_type,"
+ ~ ":heading_level, :identifier, :parent_ocn, :last_descendant_ocn,"
+ ~ ":ancestors, :dummy_heading, :object_number_off,"
+ ~ ":indent_base, :indent_hang, :bullet, :lang,"
+ ~ ":has_links, :has_notes_reg, :has_notes_star, :has_images,"
+ ~ ":segment, :segment_prev, :segment_next, :anchor,"
+ ~ ":table_cols, :table_widths, :table_header,"
+ ~ ":code_syntax, :code_linenumbers, :text"
+ ~ ")"
+ );
+
+ string[] section_order = ["head", "toc", "body", "endnotes",
+ "glossary", "bibliography", "bookindex", "blurb"];
+
+ foreach (section; section_order) {
+ if (section !in doc_abstraction) continue;
+ auto section_objs = doc_abstraction[section];
+ if (section_objs.length == 0) continue;
+
+ foreach (seq, obj; section_objs) {
+ obj_stmt.bind(":section", section);
+ obj_stmt.bind(":seq", cast(int) seq);
+ obj_stmt.bind(":ocn", obj.metainfo.ocn);
+ obj_stmt.bind(":is_a", obj.metainfo.is_a);
+
+ /+ ↓ nullable string fields +/
+ void bindStr(string param, string val) {
+ import std.typecons : Nullable;
+ if (val.length > 0) {
+ obj_stmt.bind(param, val);
+ } else {
+ obj_stmt.bind(param, Nullable!string());
+ }
+ }
+
+ bindStr(":is_of_part", obj.metainfo.is_of_part);
+ bindStr(":is_of_type", obj.metainfo.is_of_type);
+
+ /+ ↓ heading level +/
+ {
+ import std.typecons : Nullable;
+ if (obj.metainfo.is_a == "heading" && obj.metainfo.heading_lev_markup < 9) {
+ obj_stmt.bind(":heading_level", obj.metainfo.heading_lev_markup);
+ } else {
+ obj_stmt.bind(":heading_level", Nullable!int());
+ }
+ }
+
+ bindStr(":identifier", obj.metainfo.identifier);
+ obj_stmt.bind(":parent_ocn", obj.metainfo.parent_ocn);
+ obj_stmt.bind(":last_descendant_ocn", obj.metainfo.last_descendant_ocn);
+
+ /+ ↓ ancestors as space-separated integers +/
+ {
+ bool has_ancestors = false;
+ foreach (a; obj.metainfo.markedup_ancestors) {
+ if (a != 0) { has_ancestors = true; break; }
+ }
+ if (has_ancestors) {
+ string anc;
+ foreach (i, a; obj.metainfo.markedup_ancestors) {
+ if (i > 0) anc ~= " ";
+ anc ~= a.to!string;
+ }
+ obj_stmt.bind(":ancestors", anc);
+ } else {
+ import std.typecons : Nullable;
+ obj_stmt.bind(":ancestors", Nullable!string());
+ }
+ }
+
+ obj_stmt.bind(":dummy_heading", obj.metainfo.dummy_heading ? 1 : 0);
+ obj_stmt.bind(":object_number_off", obj.metainfo.object_number_off ? 1 : 0);
+ obj_stmt.bind(":indent_base", obj.attrib.indent_base);
+ obj_stmt.bind(":indent_hang", obj.attrib.indent_hang);
+ obj_stmt.bind(":bullet", obj.attrib.bullet ? 1 : 0);
+ bindStr(":lang", obj.attrib.language);
+ obj_stmt.bind(":has_links", obj.has.inline_links ? 1 : 0);
+ obj_stmt.bind(":has_notes_reg", obj.has.inline_notes_reg ? 1 : 0);
+ obj_stmt.bind(":has_notes_star", obj.has.inline_notes_star ? 1 : 0);
+ obj_stmt.bind(":has_images", obj.has.images ? 1 : 0);
+ bindStr(":segment", obj.tags.in_segment_html);
+ bindStr(":segment_prev", obj.tags.segname_prev);
+ bindStr(":segment_next", obj.tags.segname_next);
+ bindStr(":anchor", obj.tags.anchor_tag_html);
+
+ /+ ↓ table properties +/
+ {
+ import std.typecons : Nullable;
+ if (obj.metainfo.is_a == "table" && obj.table.number_of_columns > 0) {
+ obj_stmt.bind(":table_cols", obj.table.number_of_columns);
+ if (obj.table.column_widths.length > 0) {
+ string[] ws;
+ foreach (w; obj.table.column_widths) ws ~= w.to!string;
+ obj_stmt.bind(":table_widths", ws.join(" "));
+ } else {
+ obj_stmt.bind(":table_widths", Nullable!string());
+ }
+ obj_stmt.bind(":table_header", obj.table.heading ? 1 : 0);
+ } else {
+ obj_stmt.bind(":table_cols", Nullable!int());
+ obj_stmt.bind(":table_widths", Nullable!string());
+ obj_stmt.bind(":table_header", Nullable!int());
+ }
+ }
+
+ /+ ↓ code block properties +/
+ {
+ import std.typecons : Nullable;
+ if (obj.metainfo.is_a == "code") {
+ bindStr(":code_syntax", obj.code_block.syntax);
+ obj_stmt.bind(":code_linenumbers", obj.code_block.linenumbers ? 1 : 0);
+ } else {
+ obj_stmt.bind(":code_syntax", Nullable!string());
+ obj_stmt.bind(":code_linenumbers", 0);
+ }
+ }
+
+ /+ ↓ text content +/
+ bindStr(":text", obj.text);
+
+ obj_stmt.execute();
+ obj_stmt.reset();
+ }
+ }
+
+ obj_stmt.finalize();
+ db.run("COMMIT TRANSACTION");
+ }
+}
diff --git a/src/sisudoc/io_out/create_abstraction_txt.d b/src/sisudoc/io_out/create_abstraction_txt.d
new file mode 100644
index 0000000..af98f61
--- /dev/null
+++ b/src/sisudoc/io_out/create_abstraction_txt.d
@@ -0,0 +1,430 @@
+/+
+- Name: SisuDoc Spine, Doc Reform [a part of]
+ - Description: documents, structuring, processing, publishing, search
+ - static content generator
+
+ - Author: Ralph Amissah
+ [ralph.amissah@gmail.com]
+
+ - Copyright: (C) 2015 (continuously updated, current 2026) Ralph Amissah, All Rights Reserved.
+
+ - License: AGPL 3 or later:
+
+ Spine (SiSU), a framework for document structuring, publishing and
+ search
+
+ Copyright (C) Ralph Amissah
+
+ This program is free software: you can redistribute it and/or modify it
+ under the terms of the GNU AFERO General Public License as published by the
+ Free Software Foundation, either version 3 of the License, or (at your
+ option) any later version.
+
+ This program is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program. If not, see [https://www.gnu.org/licenses/].
+
+ If you have Internet connection, the latest version of the AGPL should be
+ available at these locations:
+ [https://www.fsf.org/licensing/licenses/agpl.html]
+ [https://www.gnu.org/licenses/agpl.html]
+
+ - Spine (by Doc Reform, related to SiSU) uses standard:
+ - docReform markup syntax
+ - standard SiSU markup syntax with modified headers and minor modifications
+ - docReform object numbering
+ - standard SiSU object citation numbering & system
+
+ - Homepages:
+ [https://www.sisudoc.org]
+ [https://www.doc-reform.org]
+
+ - Git
+ [https://git.sisudoc.org/]
+
++/
+module sisudoc.io_out.create_abstraction_txt;
+@safe:
+
+/+ ↓ write document abstraction as human-readable .ssp text file +/
+template spineAbstractionTxt() {
+ import std.conv : to;
+ import std.digest : toHexString;
+ import std.file;
+ import std.path;
+ import std.stdio;
+ import std.string;
+ import std.array;
+ import sisudoc.io_out.paths_output;
+
+ void spineAbstractionTxt(D)(D doc) {
+ auto doc_abstraction = doc.abstraction;
+ auto doc_matters = doc.matters;
+ string[] output;
+
+ /+ ↓ header comment +/
+ output ~= "% SiSU Document Abstraction v0.1";
+ output ~= "% Source: " ~ doc_matters.src.filename;
+ output ~= "";
+
+ /+ ↓ @meta block +/
+ output ~= "@meta {";
+ auto meta = doc_matters.conf_make_meta.meta;
+ if (meta.title_main.length > 0)
+ output ~= " title.main: " ~ meta.title_main;
+ if (meta.title_subtitle.length > 0)
+ output ~= " title.subtitle: " ~ meta.title_subtitle;
+ if (meta.title_full.length > 0)
+ output ~= " title.full: " ~ meta.title_full;
+ if (meta.title_language.length > 0)
+ output ~= " title.language: " ~ meta.title_language;
+ if (meta.creator_author.length > 0)
+ output ~= " creator.author: " ~ meta.creator_author;
+ if (meta.creator_author_surname.length > 0)
+ output ~= " creator.author_surname: " ~ meta.creator_author_surname;
+ if (meta.creator_author_surname_fn.length > 0)
+ output ~= " creator.author_surname_fn: " ~ meta.creator_author_surname_fn;
+ if (meta.creator_author_email.length > 0)
+ output ~= " creator.author_email: " ~ meta.creator_author_email;
+ if (meta.creator_illustrator.length > 0)
+ output ~= " creator.illustrator: " ~ meta.creator_illustrator;
+ if (meta.creator_translator.length > 0)
+ output ~= " creator.translator: " ~ meta.creator_translator;
+ if (meta.date_published.length > 0)
+ output ~= " date.published: " ~ meta.date_published;
+ if (meta.date_created.length > 0)
+ output ~= " date.created: " ~ meta.date_created;
+ if (meta.date_issued.length > 0)
+ output ~= " date.issued: " ~ meta.date_issued;
+ if (meta.date_available.length > 0)
+ output ~= " date.available: " ~ meta.date_available;
+ if (meta.date_modified.length > 0)
+ output ~= " date.modified: " ~ meta.date_modified;
+ if (meta.date_valid.length > 0)
+ output ~= " date.valid: " ~ meta.date_valid;
+ if (meta.rights_copyright.length > 0)
+ output ~= " rights.copyright: " ~ meta.rights_copyright;
+ if (meta.rights_license.length > 0)
+ output ~= " rights.license: " ~ meta.rights_license;
+ if (meta.classify_topic_register.length > 0)
+ output ~= " classify.topic_register: " ~ meta.classify_topic_register;
+ if (meta.classify_subject.length > 0)
+ output ~= " classify.subject: " ~ meta.classify_subject;
+ if (meta.classify_keywords.length > 0)
+ output ~= " classify.keywords: " ~ meta.classify_keywords;
+ if (meta.classify_loc.length > 0)
+ output ~= " classify.loc: " ~ meta.classify_loc;
+ if (meta.classify_dewey.length > 0)
+ output ~= " classify.dewey: " ~ meta.classify_dewey;
+ if (meta.identifier_isbn.length > 0)
+ output ~= " identifier.isbn: " ~ meta.identifier_isbn;
+ if (meta.identifier_oclc.length > 0)
+ output ~= " identifier.oclc: " ~ meta.identifier_oclc;
+ if (meta.language_document.length > 0)
+ output ~= " language.document: " ~ meta.language_document;
+ if (meta.notes_abstract.length > 0)
+ output ~= " notes.abstract: " ~ meta.notes_abstract;
+ if (meta.notes_description.length > 0)
+ output ~= " notes.description: " ~ meta.notes_description;
+ if (meta.notes_summary.length > 0)
+ output ~= " notes.summary: " ~ meta.notes_summary;
+ output ~= "}";
+ output ~= "";
+
+ /+ ↓ @make block +/
+ output ~= "@make {";
+ auto make = doc_matters.conf_make_meta.make;
+ if (make.doc_type.length > 0)
+ output ~= " doc_type: " ~ make.doc_type;
+ if (make.auto_num_top_at_level.length > 0)
+ output ~= " auto_num_top_at_level: " ~ make.auto_num_top_at_level;
+ output ~= " auto_num_top_lv: " ~ make.auto_num_top_lv.to!string;
+ output ~= " auto_num_depth: " ~ make.auto_num_depth.to!string;
+ output ~= "}";
+ output ~= "";
+
+ /+ ↓ @doc_has block +/
+ output ~= "@doc_has {";
+ output ~= " inline_links: " ~ doc_matters.has.inline_links.to!string;
+ output ~= " inline_notes_reg: " ~ doc_matters.has.inline_notes_reg.to!string;
+ output ~= " inline_notes_star: " ~ doc_matters.has.inline_notes_star.to!string;
+ output ~= " tables: " ~ doc_matters.has.tables.to!string;
+ output ~= " codeblocks: " ~ doc_matters.has.codeblocks.to!string;
+ output ~= " images: " ~ doc_matters.has.images.to!string;
+ output ~= " poems: " ~ doc_matters.has.poems.to!string;
+ output ~= " groups: " ~ doc_matters.has.groups.to!string;
+ output ~= " blocks: " ~ doc_matters.has.blocks.to!string;
+ output ~= " quotes: " ~ doc_matters.has.quotes.to!string;
+ output ~= "}";
+ output ~= "";
+
+ /+ ↓ document sections +/
+ string[] section_order = ["head", "toc", "body", "endnotes",
+ "glossary", "bibliography", "bookindex", "blurb"];
+
+ foreach (section; section_order) {
+ if (section !in doc_abstraction) continue;
+ auto section_objs = doc_abstraction[section];
+ if (section_objs.length == 0) continue;
+
+ output ~= "@" ~ section ~ " {";
+ output ~= "";
+
+ foreach (obj; section_objs) {
+ /+ ↓ object declaration line +/
+ string obj_decl = "[" ~ obj.metainfo.ocn.to!string ~ "] ";
+
+ if (obj.metainfo.is_a == "heading") {
+ string lev = obj.metainfo.marked_up_level;
+ obj_decl ~= "heading :" ~ lev;
+ if (obj.metainfo.identifier.length > 0
+ && obj.metainfo.identifier != obj.metainfo.ocn.to!string) {
+ obj_decl ~= " " ~ obj.metainfo.identifier;
+ }
+ } else {
+ obj_decl ~= obj.metainfo.is_a;
+ }
+ output ~= obj_decl;
+
+ /+ ↓ properties (only non-default values) +/
+ if (obj.metainfo.is_of_part.length > 0)
+ output ~= ".part: " ~ obj.metainfo.is_of_part;
+ if (obj.metainfo.is_of_section.length > 0
+ && obj.metainfo.is_of_section != section)
+ output ~= ".section: " ~ obj.metainfo.is_of_section;
+ if (obj.metainfo.parent_ocn != 0)
+ output ~= ".parent: " ~ obj.metainfo.parent_ocn.to!string;
+ if (obj.metainfo.last_descendant_ocn != 0)
+ output ~= ".last_descendant: " ~ obj.metainfo.last_descendant_ocn.to!string;
+
+ /+ ↓ child headings (from pre-computed map) +/
+ if (obj.metainfo.children_headings.length > 0) {
+ string[] ch;
+ foreach (c; obj.metainfo.children_headings) {
+ ch ~= c.to!string;
+ }
+ output ~= ".children: " ~ ch.join(" ");
+ }
+
+ /+ ↓ ancestors (only if non-zero) +/
+ {
+ bool has_anc = false;
+ foreach (a; obj.metainfo.markedup_ancestors) {
+ if (a != 0) { has_anc = true; break; }
+ }
+ if (has_anc) {
+ string anc;
+ foreach (i, a; obj.metainfo.markedup_ancestors) {
+ if (i > 0) anc ~= " ";
+ anc ~= a.to!string;
+ }
+ output ~= ".ancestors: " ~ anc;
+ }
+ }
+ /+ ↓ collapsed ancestors (only if non-zero) +/
+ {
+ bool has_anc_c = false;
+ foreach (a; obj.metainfo.collapsed_ancestors) {
+ if (a != 0) { has_anc_c = true; break; }
+ }
+ if (has_anc_c) {
+ string anc;
+ foreach (i, a; obj.metainfo.collapsed_ancestors) {
+ if (i > 0) anc ~= " ";
+ anc ~= a.to!string;
+ }
+ output ~= ".ancestors_collapsed: " ~ anc;
+ }
+ }
+ /+ ↓ dom structure status (only if non-zero) +/
+ {
+ bool has_dom = false;
+ foreach (d; obj.metainfo.dom_structure_markedup_tags_status) {
+ if (d != 0) { has_dom = true; break; }
+ }
+ if (has_dom) {
+ string ds;
+ foreach (i, d; obj.metainfo.dom_structure_markedup_tags_status) {
+ if (i > 0) ds ~= " ";
+ ds ~= d.to!string;
+ }
+ output ~= ".dom_status: " ~ ds;
+ }
+ }
+ {
+ bool has_dom_c = false;
+ foreach (d; obj.metainfo.dom_structure_collapsed_tags_status) {
+ if (d != 0) { has_dom_c = true; break; }
+ }
+ if (has_dom_c) {
+ string ds;
+ foreach (i, d; obj.metainfo.dom_structure_collapsed_tags_status) {
+ if (i > 0) ds ~= " ";
+ ds ~= d.to!string;
+ }
+ output ~= ".dom_status_collapsed: " ~ ds;
+ }
+ }
+
+ if (obj.metainfo.heading_lev_collapsed < 9)
+ output ~= ".heading_lev_collapsed: " ~ obj.metainfo.heading_lev_collapsed.to!string;
+ if (obj.metainfo.parent_lev_markup != 0)
+ output ~= ".parent_lev: " ~ obj.metainfo.parent_lev_markup.to!string;
+ if (obj.metainfo.dummy_heading)
+ output ~= ".dummy: true";
+ if (obj.metainfo.object_number_off)
+ output ~= ".ocn_off: true";
+ if (obj.metainfo.o_n_type != 0)
+ output ~= ".o_n_type: " ~ obj.metainfo.o_n_type.to!string;
+ if (obj.metainfo.is_of_type.length > 0)
+ output ~= ".is_of_type: " ~ obj.metainfo.is_of_type;
+ if (obj.metainfo.attrib.length > 0)
+ output ~= ".attrib: " ~ obj.metainfo.attrib;
+ if (obj.metainfo.lang.length > 0)
+ output ~= ".meta_lang: " ~ obj.metainfo.lang;
+ if (obj.metainfo.syntax.length > 0)
+ output ~= ".meta_syntax: " ~ obj.metainfo.syntax;
+
+ /+ ↓ sha256 digest +/
+ {
+ bool has_sha = false;
+ foreach (b; obj.metainfo.sha256) {
+ if (b != 0) { has_sha = true; break; }
+ }
+ if (has_sha) {
+ output ~= ".sha256: " ~ obj.metainfo.sha256.toHexString.to!string;
+ }
+ }
+
+ /+ ↓ text attributes +/
+ if (obj.attrib.indent_base != 0 || obj.attrib.indent_hang != 0)
+ output ~= ".indent: " ~ obj.attrib.indent_base.to!string
+ ~ " " ~ obj.attrib.indent_hang.to!string;
+ if (obj.attrib.bullet)
+ output ~= ".bullet: true";
+ if (obj.attrib.language.length > 0)
+ output ~= ".lang: " ~ obj.attrib.language;
+
+ /+ ↓ has flags +/
+ {
+ string[] has_flags;
+ if (obj.has.inline_links) has_flags ~= "links";
+ if (obj.has.inline_notes_reg) has_flags ~= "notes_reg";
+ if (obj.has.inline_notes_star) has_flags ~= "notes_star";
+ if (obj.has.images) has_flags ~= "images";
+ if (obj.has.image_without_dimensions) has_flags ~= "images_no_dim";
+ if (has_flags.length > 0)
+ output ~= ".has: " ~ has_flags.join(" ");
+ }
+
+ /+ ↓ table properties +/
+ if (obj.metainfo.is_a == "table" && obj.table.number_of_columns > 0) {
+ output ~= ".table_cols: " ~ obj.table.number_of_columns.to!string;
+ if (obj.table.column_widths.length > 0) {
+ string[] ws;
+ foreach (w; obj.table.column_widths) ws ~= w.to!string;
+ output ~= ".table_widths: " ~ ws.join(" ");
+ }
+ if (obj.table.column_aligns.length > 0) {
+ output ~= ".table_aligns: " ~ obj.table.column_aligns.join(" ");
+ }
+ if (obj.table.heading)
+ output ~= ".table_header: true";
+ if (obj.table.walls)
+ output ~= ".table_walls: true";
+ }
+
+ /+ ↓ code block properties +/
+ if (obj.metainfo.is_a == "code") {
+ if (obj.code_block.syntax.length > 0)
+ output ~= ".code_syntax: " ~ obj.code_block.syntax;
+ if (obj.code_block.linenumbers)
+ output ~= ".code_linenumbers: true";
+ }
+
+ /+ ↓ stow (extracted links) +/
+ if (obj.stow.link.length > 0) {
+ foreach (lnk; obj.stow.link) {
+ if (lnk.length > 0)
+ output ~= ".stow_link: " ~ lnk;
+ }
+ }
+
+ /+ ↓ tag properties +/
+ if (obj.tags.in_segment_html.length > 0)
+ output ~= ".segment: " ~ obj.tags.in_segment_html;
+ if (obj.tags.anchor_tag_html.length > 0
+ && obj.tags.anchor_tag_html != obj.tags.in_segment_html)
+ output ~= ".anchor: " ~ obj.tags.anchor_tag_html;
+ if (obj.tags.segname_prev.length > 0)
+ output ~= ".segment_prev: " ~ obj.tags.segname_prev;
+ if (obj.tags.segname_next.length > 0)
+ output ~= ".segment_next: " ~ obj.tags.segname_next;
+ if (obj.tags.heading_lev_anchor_tag.length > 0)
+ output ~= ".heading_lev_anchor: " ~ obj.tags.heading_lev_anchor_tag;
+ if (obj.tags.segment_anchor_tag_epub.length > 0)
+ output ~= ".segment_epub: " ~ obj.tags.segment_anchor_tag_epub;
+ /+ ↓ heading ancestors text +/
+ {
+ bool has_hat = false;
+ foreach (h; obj.tags.heading_ancestors_text) {
+ if (h.length > 0) { has_hat = true; break; }
+ }
+ if (has_hat) {
+ output ~= ".heading_ancestors_text: " ~ obj.tags.heading_ancestors_text.join("|");
+ }
+ }
+ /+ ↓ lev4 subtoc +/
+ if (obj.tags.lev4_subtoc.length > 0) {
+ foreach (st; obj.tags.lev4_subtoc) {
+ if (st.length > 0)
+ output ~= ".lev4_subtoc: " ~ st;
+ }
+ }
+ /+ ↓ anchor tags +/
+ if (obj.tags.anchor_tags.length > 0) {
+ foreach (at; obj.tags.anchor_tags) {
+ if (at.length > 0)
+ output ~= ".anchor_tag: " ~ at;
+ }
+ }
+
+ /+ ↓ text content +/
+ if (obj.text.length > 0) {
+ foreach (line; obj.text.split("\n")) {
+ output ~= "| " ~ line;
+ }
+ }
+
+ output ~= "";
+ }
+
+ output ~= "}";
+ output ~= "";
+ }
+
+ /+ ↓ write to file +/
+ auto out_pth = spineOutPaths!()(doc_matters.output_path, doc_matters.src.language);
+ string base_dir = "abstraction";
+ string base_pth = ((out_pth.output_base.chainPath(base_dir)).asNormalizedPath).array;
+ try {
+ if (!exists(base_pth)) {
+ base_pth.mkdirRecurse;
+ }
+ } catch (Exception ex) {
+ }
+ string out_file = ((base_pth.chainPath(
+ doc_matters.src.doc_uid_out ~ ".ssp")).asNormalizedPath).array;
+ if (doc_matters.opt.action.vox_gt_1) {
+ writeln(" ", out_file);
+ }
+ auto f = File(out_file, "w");
+ foreach (line; output) {
+ f.writeln(line);
+ }
+ }
+}
diff --git a/src/sisudoc/io_out/source_pod.d b/src/sisudoc/io_out/source_pod.d
index fa9794b..138f105 100644
--- a/src/sisudoc/io_out/source_pod.d
+++ b/src/sisudoc/io_out/source_pod.d
@@ -72,6 +72,15 @@ template spinePod() {
assert (doc_matters.src.filename.match(rgx_files.src_fn));
if (doc_matters.opt.action.source_or_pod) {
try {
+ /+ ↓ clean slate: remove per-document pod directory before regeneration,
+ but only on the first language of a multi-language document,
+ so that subsequent languages' files are not wiped +/
+ if (doc_matters.src.language == doc_matters.pod.manifest_list_of_languages[0]) {
+ string doc_pod_dir = pths_pod.base_filesystem_(doc_matters.src.filename);
+ if (exists(doc_pod_dir) && doc_pod_dir.isDir) {
+ doc_pod_dir.rmdirRecurse;
+ }
+ }
{
podArchive_directory_tree(doc_matters, pths_pod);
}
@@ -114,6 +123,11 @@ template spinePod() {
if (!exists(pths_pod.css(doc_matters.src.filename).filesystem_open_zpod)) {
pths_pod.css(doc_matters.src.filename).filesystem_open_zpod.mkdirRecurse;
}
+ if (doc_matters.opt.action.pod2) {
+ if (!exists(pths_pod.abstraction_root(doc_matters.src.filename).filesystem_open_zpod)) {
+ pths_pod.abstraction_root(doc_matters.src.filename).filesystem_open_zpod.mkdirRecurse;
+ }
+ }
if (!exists(pths_pod.image_root(doc_matters.src.filename).filesystem_open_zpod)) {
pths_pod.image_root(doc_matters.src.filename).filesystem_open_zpod.mkdirRecurse;
}
@@ -179,6 +193,34 @@ template spinePod() {
}
}
}
+ } { // bundle abstraction .ssp file (only for --pod2)
+ if (doc_matters.opt.action.pod2) {
+ import sisudoc.io_out.paths_output;
+ auto out_pth = spineOutPaths!()(doc_matters.output_path, doc_matters.src.language);
+ string abstraction_dir = ((out_pth.output_base.chainPath("abstraction")).asNormalizedPath).array;
+ string ssp_filename = doc_matters.src.doc_uid_out ~ ".ssp";
+ string fn_src_in = ((abstraction_dir.chainPath(ssp_filename)).asNormalizedPath).array.to!string;
+ auto fn_src_out_pod_zip_base
+ = pths_pod.abstraction_root(doc_matters.src.filename).zpod.to!string
+ ~ "/" ~ ssp_filename;
+ auto fn_src_out_filesystem
+ = pths_pod.abstraction_root(doc_matters.src.filename).filesystem_open_zpod.to!string
+ ~ "/" ~ ssp_filename;
+ if (exists(fn_src_in)) {
+ debug(io) { writeln("(io debug) src out found: ", fn_src_in); }
+ { // take DIGEST write to pod file digests.txt
+ auto data = (cast(byte[]) (fn_src_in).read);
+ _digests[doc_matters.src.language]["ssp"] ~= data.sha256Of.toHexString
+ ~ "::" ~ data.length.to!string ~ " - " ~ ssp_filename ~ "\n";
+ }
+ fn_src_in.copy(fn_src_out_filesystem);
+ zip = podArchive("file_path_text", fn_src_in, fn_src_out_pod_zip_base, zip);
+ } else {
+ if (doc_matters.opt.action.debug_do_pod && doc_matters.opt.action.vox_gt_2) {
+ writeln("WARNING (io) src out NOT found (abstraction): ", fn_src_in);
+ }
+ }
+ }
} { // bundle dr_document_make
auto fn_src_in = ((doc_matters.src.is_pod)
? doc_matters.src.conf_dir_path
@@ -475,6 +517,9 @@ template spinePod() {
// if (doc_matters.opt.action.vox_gt_2) { writeln(_digests[_lang]["ssi"]); }
f.writeln(_digests[_lang]["ssi"]);
}
+ if (("ssp" in _digests[_lang]) && (_digests[_lang]["ssp"].length > 0)) {
+ f.writeln(_digests[_lang]["ssp"]);
+ }
}
}
if ("shared" in _digests) {
diff --git a/src/sisudoc/meta/metadoc_from_src.d b/src/sisudoc/meta/metadoc_from_src.d
index a4388fb..4967c1f 100644
--- a/src/sisudoc/meta/metadoc_from_src.d
+++ b/src/sisudoc/meta/metadoc_from_src.d
@@ -1331,6 +1331,25 @@ template docAbstraction() {
}
}
}
+ /+ ↓ compute children_headings for heading objects +/
+ {
+ int[][int] heading_children;
+ foreach (obj; the_document_body_section) {
+ if (obj.metainfo.is_a == "heading" && obj.metainfo.parent_ocn != 0) {
+ heading_children[obj.metainfo.parent_ocn] ~= obj.metainfo.ocn;
+ }
+ }
+ foreach (ref obj; the_document_head_section) {
+ if (obj.metainfo.is_a == "heading" && obj.metainfo.ocn in heading_children) {
+ obj.metainfo.children_headings = heading_children[obj.metainfo.ocn];
+ }
+ }
+ foreach (ref obj; the_document_body_section) {
+ if (obj.metainfo.is_a == "heading" && obj.metainfo.ocn in heading_children) {
+ obj.metainfo.children_headings = heading_children[obj.metainfo.ocn];
+ }
+ }
+ }
// TODO
// - note create/insert heading object sole purpose eof close all open tags
// sort out:
diff --git a/src/sisudoc/meta/metadoc_object_setter.d b/src/sisudoc/meta/metadoc_object_setter.d
index bd4f7c8..018c51b 100644
--- a/src/sisudoc/meta/metadoc_object_setter.d
+++ b/src/sisudoc/meta/metadoc_object_setter.d
@@ -173,6 +173,7 @@ template ObjectSetter() {
int parent_lev_markup = 0;
int parent_ocn = 0;
int last_descendant_ocn = 0;
+ int[] children_headings;
ubyte[32] sha256;
}
struct ObjGenericComposite {
diff --git a/src/sisudoc/spine.d b/src/sisudoc/spine.d
index ee3bcef..e710281 100755
--- a/src/sisudoc/spine.d
+++ b/src/sisudoc/spine.d
@@ -165,7 +165,10 @@ string program_name = "spine";
"pdf-color-links" : false,
"pdf-init" : false,
"pod" : false,
+ "pod2" : false,
"serial" : false,
+ "show-abstraction" : false,
+ "show-abstraction-db" : false,
"show-config" : false,
"show-curate" : false,
"show-curate-authors" : false,
@@ -276,6 +279,7 @@ string program_name = "spine";
"pdf-color-links", "mono or color links for pdfs", &opts["pdf-color-links"],
"pdf-init", "initialise latex shared files (see latex-header-sty)", &opts["pdf-init"],
"pod", "spine (doc reform) pod source content bundled", &opts["pod"],
+ "pod2", "pod with document abstraction (.ssp) bundled", &opts["pod2"],
"quiet|q", "output to terminal", &opts["vox_is1"],
"section-backmatter", "document backmatter (default)" , &opts["backmatter"],
"section-biblio", "document biblio (default)", &opts["section_biblio"],
@@ -287,6 +291,8 @@ string program_name = "spine";
"section-toc", "table of contents (default)", &opts["section_toc"],
"serial", "serial processing", &opts["serial"],
"skip-output", "skip output", &opts["skip-output"],
+ "show-abstraction", "show document abstraction (write .ssp file)", &opts["show-abstraction"],
+ "show-abstraction-db", "show document abstraction (write .db sqlite file)", &opts["show-abstraction-db"],
"show-config", "show config", &opts["show-config"],
"show-curate", "show curate", &opts["show-curate"],
"show-curate-authors", "show curate authors", &opts["show-curate-authors"],
@@ -493,11 +499,20 @@ string program_name = "spine";
return ((opts["ocn-off"]) || (opts["no-ocn"])) ? true : false;
}
@trusted bool pod() {
- return opts["pod"];
+ return (opts["pod"] || opts["pod2"]) ? true : false;
+ }
+ @trusted bool pod2() {
+ return opts["pod2"];
}
@trusted bool show_config() {
return opts["show-config"];
}
+ @trusted bool show_abstraction() {
+ return (opts["show-abstraction"] || pod2) ? true : false;
+ }
+ @trusted bool show_abstraction_db() {
+ return opts["show-abstraction-db"];
+ }
@trusted bool show_curate() {
return opts["show-curate"];
}
@@ -538,7 +553,7 @@ string program_name = "spine";
return opts["source"];
}
@trusted bool source_or_pod() {
- return (opts["pod"] || opts["source"]) ? true : false;
+ return (opts["pod"] || opts["pod2"] || opts["source"]) ? true : false;
}
@trusted bool sqlite_discrete() {
return opts["sqlite-discrete"];
@@ -731,21 +746,23 @@ string program_name = "spine";
}
auto output_task_scheduler() {
int[] schedule;
- if (source_or_pod) { schedule ~= outTask.source_or_pod; }
- if (sqlite_discrete) { schedule ~= outTask.sqlite; }
- if (epub) { schedule ~= outTask.epub; }
- if (html_scroll) { schedule ~= outTask.html_scroll; }
- if (html_seg) { schedule ~= outTask.html_seg; }
- if (html_stuff) { schedule ~= outTask.html_stuff; }
- if (odt) { schedule ~= outTask.odt; }
- if (latex) { schedule ~= outTask.latex; }
- if (text) { schedule ~= outTask.text; }
- if (skel) { schedule ~= outTask.skel; }
+ if (source_or_pod) schedule ~= outTask.source_or_pod;
+ if (sqlite_discrete) schedule ~= outTask.sqlite;
+ if (epub) schedule ~= outTask.epub;
+ if (html_scroll) schedule ~= outTask.html_scroll;
+ if (html_seg) schedule ~= outTask.html_seg;
+ if (html_stuff) schedule ~= outTask.html_stuff;
+ if (odt) schedule ~= outTask.odt;
+ if (latex) schedule ~= outTask.latex;
+ if (text) schedule ~= outTask.text;
+ if (skel) schedule ~= outTask.skel;
return schedule.sort().uniq;
}
@trusted bool abstraction() {
return (
opts["abstraction"]
+ || show_abstraction
+ || show_abstraction_db
|| concordance
|| source_or_pod
|| curate
@@ -772,6 +789,8 @@ string program_name = "spine";
|| latex
|| odt
|| manifest
+ || show_abstraction
+ || show_abstraction_db
|| show_make
|| show_metadata
|| show_summary
@@ -786,6 +805,8 @@ string program_name = "spine";
@trusted bool meta_processing_general() {
return (
opts["abstraction"]
+ || show_abstraction
+ || show_abstraction_db
|| curate
|| html
|| epub
@@ -860,12 +881,31 @@ string program_name = "spine";
/+ ↓ track extracted zip pod temp directories for cleanup +/
mixin spineExtractZipPod;
ZipPodResult[] _zip_pod_extractions;
+ DownloadResult[] _url_downloads;
+ /+ ↓ pre-process args: resolve URL arguments to local temp files +/
+ string[] _resolved_args;
+ foreach (arg; args[1..$]) {
+ if (isUrl(arg)) {
+ auto _dlr = downloadZipUrl(arg);
+ if (_dlr.ok) {
+ _url_downloads ~= _dlr;
+ _resolved_args ~= _dlr.local_path;
+ if (_opt_action.vox_gt_1) {
+ writeln("downloaded: ", arg, " -> ", _dlr.local_path);
+ }
+ } else {
+ writeln("ERROR >> Download failed: ", arg, " - ", _dlr.error_msg);
+ }
+ } else {
+ _resolved_args ~= arg;
+ }
+ }
ConfComposite _siteConfig;
if (
_opt_action.require_processing_files
&& _opt_action.config_path_set.empty
) {
- foreach(arg; args[1..$]) {
+ foreach(arg; _resolved_args) {
if (!(arg.match(rgx.flag_action))) { /+ cli markup source path +/ // get first input markup source file names for processing
string _config_arg = arg;
/+ ↓ if first non-flag arg is a zip, extract for config discovery +/
@@ -910,7 +950,7 @@ string program_name = "spine";
}
ConfComposite _make_and_meta_struct = _siteConfig;
destroy(_siteConfig);
- foreach(arg; args[1..$]) {
+ foreach(arg; _resolved_args) {
if (arg.match(rgx.flag_action)) { /+ cli instruction, flag do +/
flag_action ~= " " ~ arg; // flags not taken by getopt
} else if (_opt_action.require_processing_files) { /+ cli, assumed to be path to source files +/
@@ -1284,6 +1324,16 @@ string program_name = "spine";
import sisudoc.meta.metadoc_show_config;
spineShowConfig!()(doc.matters);
}
+ /+ ↓ document abstraction text representation +/
+ if (doc.matters.opt.action.show_abstraction) {
+ import sisudoc.io_out.create_abstraction_txt;
+ spineAbstractionTxt!()(doc);
+ }
+ /+ ↓ document abstraction sqlite database +/
+ if (doc.matters.opt.action.show_abstraction_db) {
+ import sisudoc.io_out.create_abstraction_db;
+ spineAbstractionDb!()(doc);
+ }
if (doc.matters.opt.action.curate) {
auto _hvst = spineMetaDocCurate!()(doc.matters, hvst);
if (
@@ -1383,6 +1433,16 @@ string program_name = "spine";
import sisudoc.meta.metadoc_show_config;
spineShowConfig!()(doc.matters);
}
+ /+ ↓ document abstraction text representation +/
+ if (doc.matters.opt.action.show_abstraction) {
+ import sisudoc.io_out.create_abstraction_txt;
+ spineAbstractionTxt!()(doc);
+ }
+ /+ ↓ document abstraction sqlite database +/
+ if (doc.matters.opt.action.show_abstraction_db) {
+ import sisudoc.io_out.create_abstraction_db;
+ spineAbstractionDb!()(doc);
+ }
if (doc.matters.opt.action.curate) {
auto _hvst = spineMetaDocCurate!()(doc.matters, hvst);
if (
@@ -1453,4 +1513,8 @@ string program_name = "spine";
foreach (ref _zpr; _zip_pod_extractions) {
cleanupZipPod(_zpr);
}
+ /+ ↓ clean up any downloaded temp files +/
+ foreach (ref _dlr; _url_downloads) {
+ cleanupDownload(_dlr);
+ }
}