2019-02-05 02:44:07 +02:00
|
|
|
[Instructions::] Instructions.
|
|
|
|
|
|
|
|
Instructions of indoc to different output types.
|
|
|
|
|
|
|
|
@h Definitions.
|
2019-02-17 16:08:19 +02:00
|
|
|
The command-line and Instructions-file-set values provide a large slate of
|
|
|
|
what used to be global variables in the Perl version of Indoc. Today they
|
|
|
|
are herded together into an instance of the |settings_block| structure,
|
|
|
|
and in particular into a global instance of this called |indoc_settings|.
|
2019-02-05 02:44:07 +02:00
|
|
|
|
2019-02-16 19:29:02 +02:00
|
|
|
@d LETTER_ALPHABETIZATION 1
|
|
|
|
@d WORD_ALPHABETIZATION 2
|
|
|
|
|
|
|
|
@d EXMODE_open_internal 1
|
|
|
|
@d EXMODE_openable_internal 2
|
|
|
|
|
|
|
|
@d BOOK_GRANULARITY 1
|
|
|
|
@d CHAPTER_GRANULARITY 2
|
|
|
|
@d SECTION_GRANULARITY 3
|
|
|
|
@d SAME_AS_MAIN_GRANULARITY -1
|
|
|
|
|
2019-02-17 01:28:02 +02:00
|
|
|
@d HTML_FORMAT 1
|
|
|
|
@d PLAIN_FORMAT 2
|
|
|
|
|
|
|
|
@d PASTEMODE_none 1
|
|
|
|
@d PASTEMODE_Andrew 2
|
|
|
|
@d PASTEMODE_David 3
|
|
|
|
|
|
|
|
@d WRAPPER_none 1
|
|
|
|
@d WRAPPER_epub 2
|
|
|
|
@d WRAPPER_zip 3
|
|
|
|
|
2019-02-11 12:08:27 +02:00
|
|
|
=
|
2019-02-17 16:08:19 +02:00
|
|
|
typedef struct settings_block {
|
2019-02-16 19:29:02 +02:00
|
|
|
int verbose_mode;
|
|
|
|
int test_index_mode;
|
|
|
|
|
|
|
|
struct pathname *destination; /* path to the directory where documentation will be made */
|
|
|
|
int destination_modifiable; /* can |destination| still be changed by instructions? */
|
|
|
|
struct text_stream *manifest_leafname; /* within the |destination| directory */
|
|
|
|
struct filename *standard_rules_filename;
|
2019-06-26 11:05:27 +03:00
|
|
|
struct filename *insertion_filename;
|
2019-02-16 19:29:02 +02:00
|
|
|
|
|
|
|
struct pathname *book_folder;
|
|
|
|
filename *book_cover_image; /* e.g., |cover-image.png|; by default, none */
|
|
|
|
int index_alphabetisation_algorithm; /* one of the |*_ALPHABETIZATION| values above */
|
|
|
|
|
|
|
|
int granularity; /* one of the |*_GRANULARITY| values above */
|
|
|
|
|
|
|
|
text_stream *contents_leafname;
|
|
|
|
int contents_expandable;
|
|
|
|
int toc_granularity; /* one of the |*_GRANULARITY| values above */
|
|
|
|
|
|
|
|
int book_contains_examples;
|
|
|
|
int examples_mode; /* one of the |EXMODE_*| values above */
|
|
|
|
struct text_stream *examples_alphabetical_leafname;
|
|
|
|
struct text_stream *examples_numerical_leafname;
|
|
|
|
struct text_stream *examples_thematic_leafname;
|
|
|
|
struct pathname *examples_directory;
|
|
|
|
int examples_granularity; /* one of the |*_GRANULARITY| values above */
|
2019-02-05 02:44:07 +02:00
|
|
|
|
2019-02-17 01:28:02 +02:00
|
|
|
struct pathname *change_logs_folder;
|
|
|
|
struct filename *css_source_file;
|
|
|
|
struct filename *definitions_filename;
|
|
|
|
struct text_stream *definitions_index_leafname;
|
|
|
|
|
|
|
|
int format; /* one of the |*_FORMAT| values above */
|
|
|
|
int XHTML; /* a flag: relevant only if |HTML_FORMAT| is chosen */
|
|
|
|
int javascript; /* a flag */
|
|
|
|
int javascript_paste_method; /* one of the |PASTEMDDE_*| values above */
|
|
|
|
|
|
|
|
int html_for_Inform_application;
|
|
|
|
int images_copy;
|
|
|
|
struct pathname *images_path;
|
|
|
|
int inform_definitions_mode;
|
|
|
|
int suppress_fonts;
|
|
|
|
int assume_Public_Library;
|
|
|
|
|
|
|
|
int retina_images;
|
|
|
|
int support_creation;
|
|
|
|
|
|
|
|
struct text_stream *link_to_extensions_index;
|
|
|
|
struct filename *top_and_tail;
|
|
|
|
struct filename *top_and_tail_sections;
|
|
|
|
int treat_code_as_verbatim;
|
|
|
|
int wrapper; /* one of the |WRAPPER_*| values above */
|
|
|
|
struct ebook *ebook;
|
|
|
|
struct navigation_design *navigation;
|
|
|
|
|
2019-02-16 19:29:02 +02:00
|
|
|
MEMORY_MANAGEMENT
|
2019-02-17 16:08:19 +02:00
|
|
|
} settings_block;
|
2019-02-16 19:29:02 +02:00
|
|
|
|
|
|
|
@
|
2019-02-05 02:44:07 +02:00
|
|
|
|
|
|
|
=
|
2019-02-17 16:08:19 +02:00
|
|
|
settings_block *Instructions::clean_slate(void) {
|
|
|
|
settings_block *settings = CREATE(settings_block);
|
2019-02-16 19:29:02 +02:00
|
|
|
settings->verbose_mode = FALSE;
|
|
|
|
settings->test_index_mode = FALSE;
|
|
|
|
|
|
|
|
settings->destination = NULL;
|
|
|
|
settings->destination_modifiable = TRUE;
|
|
|
|
settings->manifest_leafname = NULL;
|
|
|
|
settings->standard_rules_filename = NULL;
|
2019-06-26 11:05:27 +03:00
|
|
|
settings->insertion_filename = NULL;
|
2019-02-16 19:29:02 +02:00
|
|
|
|
|
|
|
settings->book_folder = Pathnames::from_text(I"Documentation");
|
|
|
|
settings->book_cover_image = NULL;
|
|
|
|
settings->index_alphabetisation_algorithm = LETTER_ALPHABETIZATION;
|
|
|
|
|
|
|
|
settings->granularity = SECTION_GRANULARITY;
|
|
|
|
|
|
|
|
settings->contents_leafname = NULL;
|
|
|
|
settings->contents_expandable = FALSE;
|
|
|
|
settings->toc_granularity = SAME_AS_MAIN_GRANULARITY;
|
|
|
|
|
|
|
|
settings->book_contains_examples = FALSE;
|
|
|
|
settings->examples_mode = EXMODE_open_internal;
|
|
|
|
settings->examples_alphabetical_leafname = NULL;
|
|
|
|
settings->examples_numerical_leafname = NULL;
|
|
|
|
settings->examples_thematic_leafname = NULL;
|
|
|
|
settings->examples_directory = NULL;
|
|
|
|
settings->examples_granularity = SAME_AS_MAIN_GRANULARITY;
|
|
|
|
|
2019-02-17 01:28:02 +02:00
|
|
|
settings->change_logs_folder = NULL; /* default not set here, as it depends on book folder */
|
|
|
|
settings->css_source_file = NULL;
|
|
|
|
settings->definitions_filename = NULL;
|
|
|
|
settings->definitions_index_leafname = NULL;
|
2019-02-05 02:44:07 +02:00
|
|
|
|
2019-02-17 01:28:02 +02:00
|
|
|
settings->format = HTML_FORMAT;
|
|
|
|
settings->XHTML = FALSE;
|
|
|
|
settings->javascript = FALSE;
|
|
|
|
settings->javascript_paste_method = PASTEMODE_none;
|
2019-02-05 02:44:07 +02:00
|
|
|
|
2019-02-17 01:28:02 +02:00
|
|
|
settings->html_for_Inform_application = FALSE;
|
|
|
|
settings->images_copy = FALSE;
|
|
|
|
settings->images_path = NULL;
|
|
|
|
settings->inform_definitions_mode = FALSE;
|
|
|
|
settings->suppress_fonts = FALSE;
|
|
|
|
settings->assume_Public_Library = FALSE;
|
2019-02-05 02:44:07 +02:00
|
|
|
|
2019-02-17 01:28:02 +02:00
|
|
|
settings->retina_images = FALSE;
|
|
|
|
settings->support_creation = FALSE;
|
2019-02-05 02:44:07 +02:00
|
|
|
|
2019-02-17 01:28:02 +02:00
|
|
|
settings->link_to_extensions_index = NULL;
|
|
|
|
settings->top_and_tail = NULL;
|
|
|
|
settings->top_and_tail_sections = NULL;
|
|
|
|
settings->treat_code_as_verbatim = FALSE;
|
|
|
|
settings->wrapper = WRAPPER_none;
|
|
|
|
settings->ebook = NULL;
|
2019-02-05 02:44:07 +02:00
|
|
|
|
2019-02-17 16:08:19 +02:00
|
|
|
settings->navigation = Nav::default();
|
2019-02-05 02:44:07 +02:00
|
|
|
|
2019-02-17 01:28:02 +02:00
|
|
|
return settings;
|
|
|
|
}
|
2019-02-05 02:44:07 +02:00
|
|
|
|
|
|
|
@h Instructions file.
|
|
|
|
Note that |indoc| reports errors in the instructions file, but doesn't halt on
|
|
|
|
them until all have been found. (The user may as well get all of the bad news,
|
|
|
|
not just the beginning of it.)
|
|
|
|
|
|
|
|
=
|
2019-02-16 19:29:02 +02:00
|
|
|
void Instructions::read_instructions(text_stream *target_sought, linked_list *L,
|
2019-02-17 16:08:19 +02:00
|
|
|
settings_block *settings) {
|
2019-02-05 02:44:07 +02:00
|
|
|
int found_flag = FALSE; /* was a target of this name actually found? */
|
|
|
|
|
2019-02-17 01:28:02 +02:00
|
|
|
settings->change_logs_folder = Pathnames::subfolder(settings->book_folder, I"Change Logs");
|
2019-02-16 19:29:02 +02:00
|
|
|
settings->examples_directory = Pathnames::subfolder(settings->book_folder, I"Examples");
|
2019-02-17 16:08:19 +02:00
|
|
|
settings->css_source_file = Filenames::in_folder(path_to_indoc_materials, I"base.css");
|
2019-02-17 01:28:02 +02:00
|
|
|
settings->definitions_index_leafname = Str::duplicate(I"general_index.html");
|
2019-02-05 02:44:07 +02:00
|
|
|
|
2019-02-11 12:08:27 +02:00
|
|
|
filename *F;
|
|
|
|
LOOP_OVER_LINKED_LIST(F, filename, L)
|
2019-02-16 19:29:02 +02:00
|
|
|
if (Instructions::read_instructions_from(F, target_sought, settings))
|
2019-02-05 02:44:07 +02:00
|
|
|
found_flag = TRUE;
|
|
|
|
|
|
|
|
@<Reconcile any conflicting instructions@>;
|
|
|
|
@<Declare the format and wrapper as symbols@>;
|
|
|
|
|
2019-02-17 16:08:19 +02:00
|
|
|
HTMLUtilities::add_image_source(Pathnames::subfolder(path_to_indoc_materials, I"images"));
|
2019-02-05 02:44:07 +02:00
|
|
|
|
|
|
|
if (found_flag == FALSE)
|
|
|
|
Errors::fatal_with_text("unknown target %S", target_sought);
|
|
|
|
}
|
|
|
|
|
|
|
|
@ The instructions can be either at the top level, which means they apply to
|
|
|
|
all targets, or grouped in braced blocks relevant to one target only. For
|
|
|
|
example,
|
|
|
|
|
|
|
|
|superbness = 20|
|
|
|
|
|hypercard {|
|
|
|
|
| superbness = 40|
|
|
|
|
|}|
|
|
|
|
|
|
|
|
applies 20 for all targets except |hypercard|, where it applies 40.
|
|
|
|
|
|
|
|
=
|
2019-02-11 12:08:27 +02:00
|
|
|
typedef struct ins_helper_state {
|
|
|
|
int found_aim;
|
2019-02-17 16:08:19 +02:00
|
|
|
struct settings_block *settings;
|
2019-02-11 12:08:27 +02:00
|
|
|
struct text_stream *desired_target;
|
|
|
|
struct text_stream *scanning_target;
|
|
|
|
} ins_helper_state;
|
|
|
|
|
2019-02-16 19:29:02 +02:00
|
|
|
int Instructions::read_instructions_from(filename *F, text_stream *desired,
|
2019-02-17 16:08:19 +02:00
|
|
|
settings_block *settings) {
|
2019-02-05 02:44:07 +02:00
|
|
|
ins_helper_state ihs;
|
|
|
|
ihs.scanning_target = Str::new();
|
|
|
|
ihs.desired_target = desired;
|
|
|
|
ihs.found_aim = FALSE;
|
2019-02-16 19:29:02 +02:00
|
|
|
ihs.settings = settings;
|
|
|
|
TextFiles::read(F, FALSE, "can't open instructions file",
|
2019-02-05 02:44:07 +02:00
|
|
|
TRUE, Instructions::read_instructions_helper, NULL, &ihs);
|
|
|
|
return ihs.found_aim;
|
|
|
|
}
|
|
|
|
|
2019-02-11 12:08:27 +02:00
|
|
|
@ =
|
2019-02-05 02:44:07 +02:00
|
|
|
void Instructions::read_instructions_helper(text_stream *cl, text_file_position *tfp,
|
|
|
|
void *v_ihs) {
|
|
|
|
ins_helper_state *ihs = (ins_helper_state *) v_ihs;
|
2019-02-17 16:08:19 +02:00
|
|
|
settings_block *settings = ihs->settings;
|
2019-02-05 02:44:07 +02:00
|
|
|
match_results mr = Regexp::create_mr();
|
|
|
|
|
|
|
|
if (Regexp::match(&mr, cl, L" *#%c*")) { Regexp::dispose_of(&mr); return; }
|
|
|
|
if (Regexp::match(&mr, cl, L" *")) { Regexp::dispose_of(&mr); return; }
|
|
|
|
|
|
|
|
if (Regexp::match(&mr, cl, L"(%C+) { *")) {
|
|
|
|
if (Str::len(ihs->scanning_target) > 0)
|
|
|
|
Errors::in_text_file("second target opened while first is still open", tfp);
|
|
|
|
Str::copy(ihs->scanning_target, mr.exp[0]);
|
|
|
|
if (Str::eq(ihs->scanning_target, ihs->desired_target)) ihs->found_aim = TRUE;
|
|
|
|
} else if (Regexp::match(&mr, cl, L" *} *")) {
|
|
|
|
if (Str::len(ihs->scanning_target) == 0)
|
|
|
|
Errors::in_text_file("unexpected target end-marker", tfp);
|
|
|
|
Str::clear(ihs->scanning_target);
|
|
|
|
} else {
|
|
|
|
if ((Str::len(ihs->scanning_target) == 0) ||
|
|
|
|
(Str::eq(ihs->scanning_target, ihs->desired_target))) {
|
2019-02-17 01:28:02 +02:00
|
|
|
if (settings->verbose_mode)
|
2019-02-05 02:44:07 +02:00
|
|
|
PRINT("%f, line %d: %S\n", tfp->text_file_filename, tfp->line_count, cl);
|
|
|
|
if (Regexp::match(&mr, cl, L" *follow: *(%c*?) *")) {
|
|
|
|
if (Instructions::read_instructions_from(
|
2019-02-17 01:28:02 +02:00
|
|
|
Filenames::in_folder(settings->book_folder, mr.exp[0]),
|
|
|
|
ihs->desired_target, settings))
|
2019-02-05 02:44:07 +02:00
|
|
|
ihs->found_aim = TRUE;
|
|
|
|
} else if (Regexp::match(&mr, cl, L" *declare: *(%c*?) *")) {
|
|
|
|
Symbols::declare_symbol(mr.exp[0]);
|
|
|
|
} else if (Regexp::match(&mr, cl, L" *undeclare: *(%c*?) *")) {
|
|
|
|
Symbols::undeclare_symbol(mr.exp[0]);
|
|
|
|
} else @<This is an instruction@>;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Regexp::dispose_of(&mr);
|
|
|
|
}
|
|
|
|
|
|
|
|
@<This is an instruction@> =
|
|
|
|
if (Regexp::match(&mr, cl, L" *volume: *(%c*?) *")) {
|
|
|
|
@<Disallow this in a specific target@>;
|
|
|
|
@<Act on a volume creation@>
|
|
|
|
} else if (Regexp::match(&mr, cl, L" *cover: *(%c*?) *")) {
|
|
|
|
@<Disallow this in a specific target@>;
|
2019-02-17 01:28:02 +02:00
|
|
|
settings->book_cover_image = Instructions::set_file(mr.exp[0], settings);
|
2019-02-05 02:44:07 +02:00
|
|
|
} else if (Regexp::match(&mr, cl, L" *examples *")) {
|
|
|
|
@<Disallow this in a specific target@>;
|
2019-02-17 01:28:02 +02:00
|
|
|
settings->book_contains_examples = TRUE;
|
2019-02-05 02:44:07 +02:00
|
|
|
} else if (Regexp::match(&mr, cl, L" *dc:(%C+): *(%c*?) *")) {
|
|
|
|
@<Disallow this in a specific target@>;
|
2019-02-17 16:08:19 +02:00
|
|
|
Instructions::create_ebook_metadata(Str::duplicate(mr.exp[0]), Str::duplicate(mr.exp[1]));
|
2019-02-05 02:44:07 +02:00
|
|
|
} else if (Regexp::match(&mr, cl, L" *css: *(%c*?) *")) {
|
|
|
|
@<Act on a CSS tweak@>;
|
|
|
|
} else if (Regexp::match(&mr, cl, L" *index: *(%c*?) *")) {
|
|
|
|
@<Act on an indexing notation@>;
|
|
|
|
} else if (Regexp::match(&mr, cl, L" *images: *(%c*?) *")) {
|
2019-02-17 01:28:02 +02:00
|
|
|
HTMLUtilities::add_image_source(Instructions::set_path(mr.exp[0], settings));
|
2019-02-05 02:44:07 +02:00
|
|
|
} else if (Regexp::match(&mr, cl, L" *(%C+) *= *(%c*?) *")) {
|
|
|
|
@<Act on an instructions setting@>;
|
|
|
|
} else {
|
|
|
|
Errors::in_text_file("unknown syntax in instructions file", tfp);
|
|
|
|
}
|
|
|
|
|
|
|
|
@<Disallow this in a specific target@> =
|
|
|
|
if (Str::len(ihs->scanning_target) > 0)
|
|
|
|
Errors::in_text_file(
|
|
|
|
"structural settings like this one must apply to all targets", tfp);
|
|
|
|
|
|
|
|
@ Here's where we parse the specifier part of lines like
|
|
|
|
|
|
|
|
|volume: The Inform Recipe Book (RB) = The Recipe Book.txt|
|
|
|
|
|
|
|
|
which reads:
|
|
|
|
|
|
|
|
|The Inform Recipe Book (RB) = The Recipe Book.txt|
|
|
|
|
|
|
|
|
@<Act on a volume creation@> =
|
|
|
|
@<Disallow this in a specific target@>;
|
|
|
|
text_stream *title = mr.exp[0];
|
|
|
|
TEMPORARY_TEXT(file);
|
|
|
|
TEMPORARY_TEXT(abbrev);
|
|
|
|
match_results mr2 = Regexp::create_mr();
|
|
|
|
if (Regexp::match(&mr2, title, L"(%c+?) *= *(%c+?)")) { /* the optional filename syntax */
|
|
|
|
Str::copy(title, mr2.exp[0]); Str::copy(file, mr2.exp[1]);
|
|
|
|
} else {
|
|
|
|
WRITE_TO(file, "%S.txt", title);
|
|
|
|
}
|
|
|
|
if (Regexp::match(&mr2, title, L"(%c*?) *%((%c*?)%)")) { /* the optional abbreviation syntax */
|
|
|
|
Str::copy(title, mr2.exp[0]); Str::copy(abbrev, mr2.exp[1]);
|
|
|
|
}
|
2019-02-17 01:28:02 +02:00
|
|
|
Scanner::create_volume(settings->book_folder, file, title, abbrev);
|
2019-02-05 02:44:07 +02:00
|
|
|
DISCARD_TEXT(file);
|
|
|
|
DISCARD_TEXT(abbrev);
|
|
|
|
Regexp::dispose_of(&mr2);
|
|
|
|
|
|
|
|
@<Act on a CSS tweak@> =
|
|
|
|
text_stream *tweak = mr.exp[0];
|
|
|
|
match_results mr2 = Regexp::create_mr();
|
|
|
|
match_results mr3 = Regexp::create_mr();
|
|
|
|
if (Regexp::match(&mr2, tweak, L"(%C+)text(%C+) = (%C+)")) {
|
|
|
|
CSS::add_span_notation(mr2.exp[0], mr2.exp[1], mr2.exp[2], MARKUP_SPP);
|
|
|
|
} else {
|
|
|
|
volume *act_on = NULL;
|
|
|
|
if (Regexp::match(&mr2, tweak, L"(%C+) *: *(%c+)")) {
|
|
|
|
text_stream *abbrev = mr2.exp[0];
|
|
|
|
Str::copy(tweak, mr2.exp[1]);
|
|
|
|
volume *V;
|
|
|
|
LOOP_OVER(V, volume)
|
|
|
|
if (Str::eq(V->vol_abbrev, abbrev))
|
|
|
|
act_on = V;
|
|
|
|
if (act_on == NULL) Errors::in_text_file("unknown volume abbreviation", tfp);
|
|
|
|
}
|
|
|
|
if (Regexp::match(&mr2, tweak, L"(%c+?) *{ *")) {
|
|
|
|
int plus = 0;
|
|
|
|
text_stream *tag = mr2.exp[0];
|
|
|
|
TEMPORARY_TEXT(want);
|
|
|
|
TEMPORARY_TEXT(ncl);
|
|
|
|
while ((TextFiles::read_line(ncl, FALSE, tfp)), (Str::len(ncl) > 0)) {
|
|
|
|
Str::trim_white_space(ncl);
|
|
|
|
if (Regexp::match(&mr3, ncl, L" *} *")) break;
|
|
|
|
WRITE_TO(want, "%S\n", ncl);
|
|
|
|
}
|
|
|
|
DISCARD_TEXT(ncl);
|
|
|
|
if (Regexp::match(&mr3, tag, L"(%c*?) *%+%+ *")) { plus = 2; tag = mr3.exp[0]; }
|
|
|
|
else if (Regexp::match(&mr3, tag, L"(%c*?) *%+ *")) { plus = 1; tag = mr3.exp[0]; }
|
|
|
|
CSS::request_css_tweak(act_on, tag, want, plus);
|
|
|
|
DISCARD_TEXT(want);
|
|
|
|
} else Errors::in_text_file("bad CSS tweaking syntax", tfp);
|
|
|
|
}
|
|
|
|
Regexp::dispose_of(&mr2);
|
|
|
|
Regexp::dispose_of(&mr3);
|
|
|
|
|
|
|
|
@<Act on an indexing notation@> =
|
|
|
|
text_stream *tweak = mr.exp[0];
|
|
|
|
match_results mr2 = Regexp::create_mr();
|
2019-02-17 01:28:02 +02:00
|
|
|
if (settings->test_index_mode) PRINT("Read in: %S\n", tweak);
|
2019-02-05 02:44:07 +02:00
|
|
|
if (Regexp::match(&mr2, tweak, L"^{(%C*)headword(%C*)} = (%C+) *(%c*)")) {
|
|
|
|
Indexes::add_indexing_notation(mr2.exp[0], mr2.exp[1], mr2.exp[2], mr2.exp[3]);
|
|
|
|
} else if (Regexp::match(&mr2, tweak, L"{(%C+?)} = (%C+) *(%c*)")) {
|
|
|
|
Indexes::add_indexing_notation_for_symbols(mr2.exp[0], mr2.exp[1], mr2.exp[2]);
|
|
|
|
} else if (Regexp::match(&mr2, tweak, L"definition = (%C+) *(%c*)")) {
|
|
|
|
Indexes::add_indexing_notation_for_definitions(mr2.exp[0], mr2.exp[1], NULL);
|
|
|
|
} else if (Regexp::match(&mr2, tweak, L"(%C+)-definition = (%C+) *(%c*)")) {
|
|
|
|
Indexes::add_indexing_notation_for_definitions(mr2.exp[1], mr2.exp[2], mr2.exp[0]);
|
|
|
|
} else if (Regexp::match(&mr2, tweak, L"example = (%C+) *(%c*)")) {
|
|
|
|
Indexes::add_indexing_notation_for_examples(mr2.exp[0], mr2.exp[1]);
|
|
|
|
} else {
|
|
|
|
Errors::in_text_file("bad indexing notation", tfp);
|
|
|
|
}
|
|
|
|
Regexp::dispose_of(&mr2);
|
|
|
|
|
|
|
|
@<Act on an instructions setting@> =
|
|
|
|
text_stream *key = mr.exp[0];
|
|
|
|
text_stream *val = mr.exp[1];
|
|
|
|
@<Deal with braced write values@>;
|
|
|
|
@<Set an instructions option@>;
|
|
|
|
|
|
|
|
@ The write value can span multiple lines if the first line consists only
|
|
|
|
of |{| and the last only of |}| (plus leading or trailing white space to
|
|
|
|
taste). In a multiple-line value, each line is terminated with a newline.
|
|
|
|
|
|
|
|
@<Deal with braced write values@> =
|
|
|
|
if (Str::eq(val, I"{")) {
|
|
|
|
Str::clear(val);
|
|
|
|
match_results mr2 = Regexp::create_mr();
|
|
|
|
TEMPORARY_TEXT(ncl);
|
|
|
|
while ((TextFiles::read_line(ncl, FALSE, tfp)), (Str::len(ncl) > 0)) {
|
|
|
|
if (Regexp::match(&mr2, ncl, L" *} *")) break;
|
|
|
|
WRITE_TO(val, "%S\n", ncl);
|
|
|
|
}
|
|
|
|
DISCARD_TEXT(ncl);
|
|
|
|
Regexp::dispose_of(&mr2);
|
|
|
|
}
|
|
|
|
|
|
|
|
@<Set an instructions option@> =
|
|
|
|
if (Str::eq_wide_string(key, L"alphabetization")) {
|
2019-02-17 16:08:19 +02:00
|
|
|
if (Str::eq_wide_string(val, L"word-by-word"))
|
|
|
|
settings->index_alphabetisation_algorithm = WORD_ALPHABETIZATION;
|
|
|
|
else if (Str::eq_wide_string(val, L"letter-by-letter"))
|
|
|
|
settings->index_alphabetisation_algorithm = LETTER_ALPHABETIZATION;
|
2019-02-05 02:44:07 +02:00
|
|
|
else Errors::in_text_file("no such alphabetization", tfp);
|
|
|
|
}
|
2019-02-17 16:08:19 +02:00
|
|
|
else if (Str::eq_wide_string(key, L"assume_Public_Library"))
|
|
|
|
settings->assume_Public_Library = Instructions::set_yn(key, val, tfp);
|
|
|
|
else if (Str::eq_wide_string(key, L"change_logs_directory"))
|
|
|
|
settings->change_logs_folder = Instructions::set_path(val, settings);
|
|
|
|
else if (Str::eq_wide_string(key, L"contents_leafname"))
|
|
|
|
settings->contents_leafname = Str::duplicate(val);
|
|
|
|
else if (Str::eq_wide_string(key, L"contents_expandable"))
|
|
|
|
settings->contents_expandable = Instructions::set_yn(key, val, tfp);
|
2019-02-17 01:28:02 +02:00
|
|
|
else if (Str::eq_wide_string(key, L"css_source_file")) { settings->css_source_file = Instructions::set_file(val, settings); }
|
|
|
|
else if (Str::eq_wide_string(key, L"definitions_filename")) { settings->definitions_filename = Instructions::set_file(val, settings); }
|
2019-02-05 02:44:07 +02:00
|
|
|
else if (Str::eq_wide_string(key, L"definitions_index_filename")) {
|
2019-02-17 01:28:02 +02:00
|
|
|
settings->definitions_index_leafname = Str::duplicate(val); }
|
2019-02-05 02:44:07 +02:00
|
|
|
else if (Str::eq_wide_string(key, L"destination")) {
|
2019-02-17 01:28:02 +02:00
|
|
|
if (settings->destination_modifiable)
|
|
|
|
settings->destination = Instructions::set_path(val, settings);
|
2019-02-05 02:44:07 +02:00
|
|
|
}
|
2019-02-16 19:29:02 +02:00
|
|
|
else if (Str::eq_wide_string(key, L"examples_directory")) {
|
2019-02-17 01:28:02 +02:00
|
|
|
settings->examples_directory = Instructions::set_path(val, settings); }
|
2019-02-05 02:44:07 +02:00
|
|
|
else if (Str::eq_wide_string(key, L"examples_alphabetical_leafname")) {
|
2019-02-17 01:28:02 +02:00
|
|
|
settings->examples_alphabetical_leafname = Str::duplicate(val); }
|
2019-02-05 02:44:07 +02:00
|
|
|
else if (Str::eq_wide_string(key, L"examples_granularity")) {
|
2019-02-17 01:28:02 +02:00
|
|
|
settings->examples_granularity = Instructions::set_range(key, val, 1, 3, tfp); }
|
2019-02-05 02:44:07 +02:00
|
|
|
else if (Str::eq_wide_string(key, L"examples_mode")) {
|
2019-02-17 01:28:02 +02:00
|
|
|
if (Str::eq_wide_string(val, L"open")) { settings->examples_mode = EXMODE_open_internal; }
|
|
|
|
else if (Str::eq_wide_string(val, L"openable")) { settings->examples_mode = EXMODE_openable_internal; }
|
2019-02-05 02:44:07 +02:00
|
|
|
else Errors::in_text_file("no such examples mode", tfp);
|
|
|
|
}
|
|
|
|
else if (Str::eq_wide_string(key, L"examples_numerical_leafname")) {
|
2019-02-17 01:28:02 +02:00
|
|
|
settings->examples_numerical_leafname = Str::duplicate(val); }
|
2019-02-05 02:44:07 +02:00
|
|
|
else if (Str::eq_wide_string(key, L"examples_thematic_leafname")) {
|
2019-02-17 01:28:02 +02:00
|
|
|
settings->examples_thematic_leafname = Str::duplicate(val); }
|
2019-02-05 02:44:07 +02:00
|
|
|
else if (Str::eq_wide_string(key, L"format")) {
|
2019-02-17 01:28:02 +02:00
|
|
|
if (Str::eq_wide_string(val, L"HTML")) { settings->format = HTML_FORMAT; }
|
|
|
|
else if (Str::eq_wide_string(val, L"text")) { settings->format = PLAIN_FORMAT; }
|
2019-02-05 02:44:07 +02:00
|
|
|
else Errors::in_text_file("no such format", tfp);
|
|
|
|
}
|
2019-02-17 01:28:02 +02:00
|
|
|
else if (Str::eq_wide_string(key, L"granularity")) { settings->granularity = Instructions::set_range(key, val, 1, 3, tfp); }
|
2019-02-05 02:44:07 +02:00
|
|
|
else if (Str::eq_wide_string(key, L"html_for_Inform_application")) {
|
2019-02-17 01:28:02 +02:00
|
|
|
settings->html_for_Inform_application = Instructions::set_yn(key, val, tfp); }
|
|
|
|
else if (Str::eq_wide_string(key, L"images_path")) { settings->images_path = Instructions::set_path(val, settings); }
|
|
|
|
else if (Str::eq_wide_string(key, L"images_copy")) { settings->images_copy = Instructions::set_yn(key, val, tfp); }
|
2019-02-05 02:44:07 +02:00
|
|
|
else if (Str::eq_wide_string(key, L"inform_definitions_mode")) {
|
2019-02-17 01:28:02 +02:00
|
|
|
settings->inform_definitions_mode = Instructions::set_yn(key, val, tfp); }
|
|
|
|
else if (Str::eq_wide_string(key, L"javascript")) { settings->javascript = Instructions::set_yn(key, val, tfp); }
|
2019-02-05 02:44:07 +02:00
|
|
|
else if (Str::eq_wide_string(key, L"javascript_paste_method")) {
|
2019-02-17 01:28:02 +02:00
|
|
|
if (Str::eq_wide_string(val, L"none")) { settings->javascript_paste_method = PASTEMODE_none; }
|
|
|
|
else if (Str::eq_wide_string(val, L"Andrew")) { settings->javascript_paste_method = PASTEMODE_Andrew; }
|
|
|
|
else if (Str::eq_wide_string(val, L"David")) { settings->javascript_paste_method = PASTEMODE_David; }
|
2019-02-05 02:44:07 +02:00
|
|
|
else Errors::in_text_file("no such Javascript paste mode", tfp);
|
|
|
|
}
|
|
|
|
else if (Str::eq_wide_string(key, L"link_to_extensions_index")) {
|
2019-02-17 01:28:02 +02:00
|
|
|
settings->link_to_extensions_index = Str::duplicate(val); }
|
|
|
|
else if (Str::eq_wide_string(key, L"manifest_leafname")) { settings->manifest_leafname = Str::duplicate(val); }
|
2019-02-05 02:44:07 +02:00
|
|
|
else if (Str::eq_wide_string(key, L"navigation")) {
|
2019-02-17 16:08:19 +02:00
|
|
|
settings->navigation = Nav::parse(val);
|
2019-02-17 01:28:02 +02:00
|
|
|
if (settings->navigation == NULL) Errors::in_text_file("no such navigation mode", tfp);
|
2019-02-05 02:44:07 +02:00
|
|
|
}
|
|
|
|
else if (Str::eq_wide_string(key, L"retina_images")) {
|
2019-02-17 01:28:02 +02:00
|
|
|
settings->retina_images = Instructions::set_yn(key, val, tfp); }
|
2019-02-05 02:44:07 +02:00
|
|
|
else if (Str::eq_wide_string(key, L"support_creation")) {
|
2019-02-17 01:28:02 +02:00
|
|
|
settings->support_creation = Instructions::set_yn(key, val, tfp); }
|
2019-02-05 02:44:07 +02:00
|
|
|
else if (Str::eq_wide_string(key, L"suppress_fonts")) {
|
2019-02-17 01:28:02 +02:00
|
|
|
settings->suppress_fonts = Instructions::set_yn(key, val, tfp); }
|
2019-02-05 02:44:07 +02:00
|
|
|
else if (Str::eq_wide_string(key, L"toc_granularity")) {
|
2019-02-17 01:28:02 +02:00
|
|
|
settings->toc_granularity = Instructions::set_range(key, val, 1, 3, tfp); }
|
2019-02-05 02:44:07 +02:00
|
|
|
else if (Str::eq_wide_string(key, L"top_and_tail_sections")) {
|
2019-02-17 01:28:02 +02:00
|
|
|
settings->top_and_tail_sections = Instructions::set_file(val, settings); }
|
|
|
|
else if (Str::eq_wide_string(key, L"top_and_tail")) { settings->top_and_tail = Instructions::set_file(val, settings); }
|
2019-02-05 02:44:07 +02:00
|
|
|
else if (Str::eq_wide_string(key, L"treat_code_as_verbatim")) {
|
2019-02-17 01:28:02 +02:00
|
|
|
settings->treat_code_as_verbatim = Instructions::set_yn(key, val, tfp); }
|
2019-02-05 02:44:07 +02:00
|
|
|
else if (Str::eq_wide_string(key, L"wrapper")) {
|
2019-02-17 01:28:02 +02:00
|
|
|
if (Str::eq_wide_string(val, L"EPUB")) { settings->wrapper = WRAPPER_epub; }
|
|
|
|
else if (Str::eq_wide_string(val, L"zip")) { settings->wrapper = WRAPPER_zip; }
|
|
|
|
else if (Str::eq_wide_string(val, L"none")) { settings->wrapper = WRAPPER_none; }
|
2019-02-05 02:44:07 +02:00
|
|
|
else Errors::in_text_file("no such wrapper", tfp);
|
|
|
|
}
|
2019-02-17 01:28:02 +02:00
|
|
|
else if (Str::eq_wide_string(key, L"XHTML")) { settings->XHTML = Instructions::set_yn(key, val, tfp); }
|
2019-02-05 02:44:07 +02:00
|
|
|
|
|
|
|
else Errors::in_text_file("no such setting", tfp);
|
|
|
|
|
|
|
|
@<Reconcile any conflicting instructions@> =
|
2019-02-17 01:28:02 +02:00
|
|
|
if (settings->wrapper == WRAPPER_epub) {
|
|
|
|
settings->javascript = FALSE;
|
|
|
|
settings->javascript_paste_method = PASTEMODE_none;
|
2019-02-16 19:29:02 +02:00
|
|
|
if (settings->examples_mode == EXMODE_openable_internal) {
|
|
|
|
settings->examples_mode = EXMODE_open_internal;
|
2019-02-05 02:44:07 +02:00
|
|
|
}
|
2019-02-16 19:29:02 +02:00
|
|
|
settings->contents_expandable = FALSE;
|
2019-02-17 01:28:02 +02:00
|
|
|
settings->images_copy = 1;
|
2019-02-17 16:08:19 +02:00
|
|
|
settings->navigation = Nav::for_ebook(settings->navigation);
|
2019-02-17 01:28:02 +02:00
|
|
|
settings->format = HTML_FORMAT;
|
|
|
|
settings->XHTML = TRUE;
|
|
|
|
settings->ebook = Epub::new(I"untitled ebook", "");
|
2019-02-05 02:44:07 +02:00
|
|
|
}
|
|
|
|
|
2019-02-17 01:28:02 +02:00
|
|
|
if (settings->javascript_paste_method != PASTEMODE_none)
|
|
|
|
settings->javascript = TRUE;
|
2019-02-05 02:44:07 +02:00
|
|
|
|
2019-02-16 19:29:02 +02:00
|
|
|
if (settings->examples_granularity == SAME_AS_MAIN_GRANULARITY)
|
|
|
|
settings->examples_granularity = settings->granularity;
|
|
|
|
if (settings->toc_granularity == SAME_AS_MAIN_GRANULARITY)
|
|
|
|
settings->toc_granularity = settings->granularity;
|
2019-02-05 02:44:07 +02:00
|
|
|
|
2019-02-16 19:29:02 +02:00
|
|
|
if (settings->examples_granularity < settings->granularity) {
|
|
|
|
settings->examples_granularity = settings->granularity;
|
2019-02-05 02:44:07 +02:00
|
|
|
Errors::nowhere("examples granularity can't be less than granularity");
|
|
|
|
}
|
2019-02-16 19:29:02 +02:00
|
|
|
if (settings->toc_granularity < settings->granularity) {
|
|
|
|
settings->toc_granularity = settings->granularity;
|
2019-02-05 02:44:07 +02:00
|
|
|
Errors::nowhere("TOC granularity can't be less than granularity");
|
|
|
|
}
|
|
|
|
|
2019-02-17 01:28:02 +02:00
|
|
|
if (settings->format == PLAIN_FORMAT)
|
2019-02-17 16:08:19 +02:00
|
|
|
settings->navigation = Nav::for_plain_text(settings->navigation);
|
2019-02-05 02:44:07 +02:00
|
|
|
|
|
|
|
@<Declare the format and wrapper as symbols@> =
|
2019-02-17 01:28:02 +02:00
|
|
|
if (settings->wrapper == WRAPPER_epub) Symbols::declare_symbol(I"EPUB");
|
|
|
|
else if (settings->wrapper == WRAPPER_zip) Symbols::declare_symbol(I"zip");
|
2019-02-05 02:44:07 +02:00
|
|
|
else Symbols::declare_symbol(I"unwrapped");
|
|
|
|
|
2019-02-17 01:28:02 +02:00
|
|
|
if (settings->format == HTML_FORMAT) Symbols::declare_symbol(I"HTML");
|
|
|
|
if (settings->format == PLAIN_FORMAT) Symbols::declare_symbol(I"text");
|
2019-02-05 02:44:07 +02:00
|
|
|
|
|
|
|
@h Parsing values.
|
|
|
|
Note the Unix-style conveniences for pathnames: an initial |~| means the
|
|
|
|
home folder, |~~| means the book folder.
|
|
|
|
|
|
|
|
=
|
2019-02-17 16:08:19 +02:00
|
|
|
pathname *Instructions::set_path(text_stream *val, settings_block *settings) {
|
2019-02-05 02:44:07 +02:00
|
|
|
if (Str::get_at(val, 0) == '~') {
|
|
|
|
if (Str::get_at(val, 1) == '~') {
|
|
|
|
if ((Str::get_at(val, 2) == '/') || (Str::get_at(val, 2) == FOLDER_SEPARATOR)) {
|
|
|
|
TEMPORARY_TEXT(t);
|
|
|
|
Str::copy_tail(t, val, 3);
|
2019-02-16 19:29:02 +02:00
|
|
|
pathname *P = Pathnames::from_text_relative(settings->book_folder, t);
|
2019-02-05 02:44:07 +02:00
|
|
|
DISCARD_TEXT(t);
|
|
|
|
return P;
|
2019-02-16 19:29:02 +02:00
|
|
|
} else if (Str::get_at(val, 2) == 0) return settings->book_folder;
|
2019-02-05 02:44:07 +02:00
|
|
|
}
|
|
|
|
if ((Str::get_at(val, 1) == '/') || (Str::get_at(val, 1) == FOLDER_SEPARATOR)) {
|
|
|
|
TEMPORARY_TEXT(t);
|
|
|
|
Str::copy_tail(t, val, 2);
|
|
|
|
pathname *P = Pathnames::from_text_relative(home_path, t);
|
|
|
|
DISCARD_TEXT(t);
|
|
|
|
return P;
|
|
|
|
} else if (Str::get_at(val, 1) == 0) return home_path;
|
|
|
|
}
|
|
|
|
return Pathnames::from_text(val);
|
|
|
|
}
|
|
|
|
|
2019-02-11 12:08:27 +02:00
|
|
|
@ =
|
2019-02-17 16:08:19 +02:00
|
|
|
filename *Instructions::set_file(text_stream *val, settings_block *settings) {
|
2019-02-05 02:44:07 +02:00
|
|
|
if (Str::get_at(val, 0) == '~') {
|
|
|
|
if (Str::get_at(val, 1) == '~') {
|
|
|
|
if ((Str::get_at(val, 2) == '/') || (Str::get_at(val, 2) == FOLDER_SEPARATOR)) {
|
|
|
|
TEMPORARY_TEXT(t);
|
|
|
|
Str::copy_tail(t, val, 3);
|
2019-02-16 19:29:02 +02:00
|
|
|
filename *F = Filenames::from_text_relative(settings->book_folder, t);
|
2019-02-05 02:44:07 +02:00
|
|
|
DISCARD_TEXT(t);
|
|
|
|
return F;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ((Str::get_at(val, 1) == '/') || (Str::get_at(val, 1) == FOLDER_SEPARATOR)) {
|
|
|
|
TEMPORARY_TEXT(t);
|
|
|
|
Str::copy_tail(t, val, 2);
|
|
|
|
filename *F = Filenames::from_text_relative(home_path, t);
|
|
|
|
DISCARD_TEXT(t);
|
|
|
|
return F;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return Filenames::from_text(val);
|
|
|
|
}
|
|
|
|
|
|
|
|
@ An integer value within or at the edges of the given range.
|
|
|
|
|
|
|
|
=
|
|
|
|
int Instructions::set_range(text_stream *key, text_stream *val,
|
|
|
|
int min, int max, text_file_position *tfp) {
|
|
|
|
match_results mr = Regexp::create_mr();
|
|
|
|
if (Regexp::match(&mr, val, L"%d+")) {
|
|
|
|
int v = Str::atoi(val, 0);
|
|
|
|
Regexp::dispose_of(&mr);
|
|
|
|
if ((v >= min) && (v <= max)) return v;
|
|
|
|
}
|
|
|
|
TEMPORARY_TEXT(ERM);
|
|
|
|
WRITE_TO(ERM, "'%S' must a number from %d to %d, not '%S'", key, min, max, val);
|
|
|
|
Errors::in_text_file_S(ERM, tfp);
|
|
|
|
DISCARD_TEXT(ERM);
|
|
|
|
return min;
|
|
|
|
}
|
|
|
|
|
|
|
|
@ A yes-no answer.
|
|
|
|
|
|
|
|
=
|
|
|
|
int Instructions::set_yn(text_stream *key, text_stream *val, text_file_position *tfp) {
|
|
|
|
if (Str::eq_wide_string(val, L"yes")) { return 1; }
|
|
|
|
if (Str::eq_wide_string(val, L"no")) { return 0; }
|
|
|
|
TEMPORARY_TEXT(ERM);
|
|
|
|
WRITE_TO(ERM, "'%S' must be 'yes' or 'no', not '%S'", key, val);
|
|
|
|
Errors::in_text_file_S(ERM, tfp);
|
|
|
|
DISCARD_TEXT(ERM);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
@ For ebooks only.
|
|
|
|
|
|
|
|
=
|
2019-02-17 16:08:19 +02:00
|
|
|
typedef struct dc_metadatum {
|
|
|
|
struct text_stream *dc_key;
|
|
|
|
struct text_stream *dc_val;
|
|
|
|
MEMORY_MANAGEMENT
|
|
|
|
} dc_metadatum;
|
|
|
|
|
|
|
|
void Instructions::create_ebook_metadata(text_stream *key, text_stream *value) {
|
|
|
|
dc_metadatum *dcm = CREATE(dc_metadatum);
|
|
|
|
dcm->dc_key = Str::duplicate(key);
|
|
|
|
dcm->dc_val = Str::duplicate(value);
|
|
|
|
}
|
|
|
|
|
2019-02-17 01:28:02 +02:00
|
|
|
void Instructions::apply_ebook_metadata(ebook *E) {
|
2019-02-05 02:44:07 +02:00
|
|
|
dc_metadatum *dcm;
|
|
|
|
LOOP_OVER(dcm, dc_metadatum) {
|
|
|
|
wchar_t K[1024];
|
|
|
|
Str::copy_to_wide_string(K, dcm->dc_key, 1024);
|
2019-02-17 01:28:02 +02:00
|
|
|
Epub::attach_metadata(E, K, dcm->dc_val);
|
2019-02-05 02:44:07 +02:00
|
|
|
}
|
|
|
|
}
|