#include "util.h" #include "machine.h" #include "error.h" #include "kutil.h" #include "html_util.h" #include "latex_util.h" #include "locator.h" // #include "state.h" #include "document_class.h" #include "show.h" #include "log.h" #include "file.h" bool strbool(const std::string& s, const Locator& loc) { std::vector values = {"false", "False", "0", "true", "True", "1"}; if (is_not_in(s, values)) { std::stringstream ss {}; ss << "The value \"" << s << "\" is not a Boolean value. Possible values are:\n " << join(values, ", "); throw Argument_error(ss.str(), loc); } bool result = (find(values.begin(), values.end(), s) - values.begin()) > 2; return result; } Document_class::Document_class(Machine& machine) : Klammer_base(machine) { (void)K::log(3); // show("document in main SKS"); m_machine = machine; sloc = get("K_loc"); m_title = get("title"); m_subtitle = get("subtitle"); m_page_title = get("page_title"); m_author = get("author"); m_date = get("date"); // m_nav = strbool(get("nav"), loc); // m_toc = strbool(get("toc"), loc); m_structure = docstruct(get("structure")); m_date = get("date"); m_version = get("version"); m_logo = get("logo"); m_text = get("text"); // Filename lists: standalone-"/" separation, existence rescue for // spaces, ~ expansion (resolve_filename_list in mac/file.cpp); the // existence checks resolve relative names against the input directory, // as parse_input_filename() will. // Filename-bearing values arrive ESCAPED for the target (the state // stores escaped values so they flow correctly into output); a filename // is programmatic use, so decode the markers first -- the C++ mirror of // the Python-side unescape_ktesc() rule. Found 2026-08-22: under tex, // ":files my_chapter" searched for "myKTESC005fKTESCchapter". m_files = resolve_filename_list(ktesc_resolve(get("files")), get("K_input_dir")); m_css_text = get("css_text"); m_css_filenames = resolve_filename_list(ktesc_resolve(get("css_files")), get("K_input_dir")); m_include_sks_css = strbool(get("include_sks_css"), loc); frame_background_color = get("frame_background_color"); frame_text_color = get("frame_text_color"); nav_background_color = get("nav_background_color"); nav_text_color = get("nav_text_color"); js_text = get("js_text"); m_js_filenames = resolve_filename_list(ktesc_resolve(get("js_files")), get("K_input_dir")); m_include_sks_js = strbool(get("include_sks_js"), loc); // font_dirs = word_split(get("font_dirs")); auto strip_quotes = [](std::string s) { if (s.size() >= 2 && s.front() == '"' && s.back() == '"') s = s.substr(1, s.size() - 2); return s; }; m_serif_font = strip_quotes(get("serif_font")); m_sans_font = strip_quotes(get("sans_font")); m_mono_font = strip_quotes(get("mono_font")); m_font_scale = stof(get("font_scale")); if (!m_serif_font.empty()) m_resolved_serif = resolve_font(m_serif_font); if (!m_sans_font.empty()) m_resolved_sans = resolve_font(m_sans_font); if (!m_mono_font.empty()) m_resolved_mono = resolve_font(m_mono_font); paper_size = get("paper_size"); landscape = strbool(get("landscape"), loc); leading = stof(get("leading")); point_size = stoi(get("point_size")); ragged_right = strbool(get("ragged_right"), loc); cover = get("cover"); prolog = get("prolog"); m_two_column = strbool(get("two_column"), loc); m_copyright = get("copyright"); m_bottom = get("bottom"); create_output_directory = strbool(get("create_output_directory"), loc); output_directory_name = get("output_directory_name"); resources_in_file = strbool(get("resources_in_file"), loc); //use_pages_dir = strbool(get("use_pages_dir"), loc); // m_toc_only = strbool(get("K_toc_only"), loc); m_kt_root_filename = get("K_input_filename"); m_no_cache = !strbool(get("cache"), loc); write_files = !strbool(get("K_stdout_only"), loc); m_input_dir = get("K_input_dir"); if (m_text.size() == 0 and m_files.size() == 0) { throw Argument_error( "Neither the :text or :files options have values"); //, //get("K_loc")); } /* for (std::string file : m_files) { sources.push_back(string_from_file(find_kt_file(file))); } */ // std::cout << "IN DOCUMENT CLASS:\n" << m_state << "\n"; } void Document_class::save_string_input_as_file() { if (!m_text.empty()) { std::string input_text_filename = m_cache_dir + "/_text.kt"; // msg() << "Write string input to cache file: " // << input_text_filename << "\n"; string_to_file(input_text_filename, m_text); m_files.insert(m_files.begin(), input_text_filename); } } fs::path parse_input_filename(const std::string& s, const std::string& input_dir) { // The two-class rule (2026-08-22), replacing a three-stage search whose // second stage could quietly shadow a file beside the document: // // * a BARE WORD -- no directory separator, no extension -- is the kt/ // SHORTCUT: ":files X" MEANS kt/X.kt in the root file's directory, // and nothing else. Missing is an immediate error whose message // teaches the convention, not a fallback. The kt/ directory is the // conventional home for a document's input files, and putting the // meaning entirely in the name lets several root documents share it. // // * anything else is a real PATHNAME, absolute or resolved against the // input file's directory (K_input_dir), so a document renders // identically wherever ktext is run from. // // Which file a name landed on is a DERIVED value, so "-v 1" reports it. fs::path p(s); bool bare = s.find('/') == std::string::npos && p.extension().empty(); if (bare) { fs::path in_kt = fs::path(input_dir) / "kt" / (s + ".kt"); if (!file_exists(in_kt.string())) { throw Argument_error( "The \":files\" name \"" + s + "\" is a bare word, which by " "convention means the file kt/" + s + ".kt in the root " "document's directory:\n" " " + in_kt.string() + "\n" "That file does not exist. Create it there, or write a real " "pathname (a name with a directory or the \".kt\" extension) " "to use a file elsewhere.", Locator::none()); } // The resolved path itself shows kt/ -- no label needed. (void)K::log(1, "Input file \"" + s + "\": " + in_kt.string()); return in_kt; } if (p.extension() != ".kt") { p += ".kt"; } if (p.is_absolute()) { return p; } fs::path in_input_dir = fs::path(input_dir) / p; (void)K::log(1, "Input file \"" + s + "\": " + in_input_dir.string()); return in_input_dir; } void Document_class::write(const std::string& filename, const std::string& contents) { write_file(filename, contents, write_files); } void write_file(const std::string& filename, const std::string& contents, bool write_p) { if (write_p) { string_to_file(filename, contents); } else { // Not writing: name the file that would have been written. A // derived value, so "-v 1" -- and never on stdout, which carries // the document itself. (void)K::log(1, "Output filename:", filename); } } /* void output_msg(const std::string& msg) { std::cout << red; (void)K::log(1, msg); std::cout << black; } void Document_class::create_directory(const std::string& directory) { if (write_files) { fs::create_directories(directory); } else { output_msg("Output directory: " + directory); } } */