To feed multiple output requests to the weaver, and to present weaver results, and update indexes or contents pages.
§1. Swarming. A "weave" occurs when Inweb takes a portion of the web — one section, one chapter, or the whole thing — and writes it out in a human-readable form (or in some intermediate state which can be made into one, like a TeX file). There can be many weaves in a single run of Inweb, in which case we call the resulting flurry a "swarm", like the glittering cloud of locusts in the title of Chapter 25 of "On the Banks of Plum Creek".
This routine is called with mode SWARM_SECTIONS_SWM, SWARM_CHAPTERS_SWM or SWARM_INDEX_SWM, so in a non-swarming run it isn't called at all.
weave_order *swarm_leader = NULL; the most inclusive one we weave void Swarm::weave(web *W, text_stream *range, int swarm_mode, theme_tag *tag, weave_pattern *pattern, filename *to, pathname *into, linked_list *breadcrumbs, filename *navigation) { swarm_leader = NULL; chapter *C; section *S; LOOP_OVER_LINKED_LIST(C, chapter, W->chapters) if (C->md->imported == FALSE) { if (swarm_mode == SWARM_CHAPTERS_SWM) if ((W->md->chaptered == TRUE) && (Reader::range_within(C->md->ch_range, range))) { C->ch_weave = Swarm::weave_subset(W, C->md->ch_range, FALSE, tag, pattern, to, into, breadcrumbs, navigation); if (Str::len(range) > 0) swarm_leader = C->ch_weave; } if (swarm_mode == SWARM_SECTIONS_SWM) LOOP_OVER_LINKED_LIST(S, section, C->sections) if (Reader::range_within(S->md->sect_range, range)) S->sect_weave = Swarm::weave_subset(W, S->md->sect_range, FALSE, tag, pattern, to, into, breadcrumbs, navigation); } Swarm::weave_index_templates(W, range, pattern, into, navigation, breadcrumbs); }
§2. The following is where an individual weave task begins, whether it comes from the swarm, or has been specified at the command line (in which case the call comes from Program Control).
weave_order *Swarm::weave_subset(web *W, text_stream *range, int open_afterwards, theme_tag *tag, weave_pattern *pattern, filename *to, pathname *into, linked_list *breadcrumbs, filename *navigation) { weave_order *wv = NULL; if (no_inweb_errors == 0) { Analyser::analyse_code(W); Compile a set of instructions for the weaver2.2; if (Weaver::weave(wv) == 0) i.e., the number of lines woven was zero Errors::fatal("empty weave request"); Patterns::post_process(wv->pattern, wv); Formats::post_process_weave(wv, open_afterwards); Report on the outcome of the weave to the console2.3; } return wv; }
§2.1. Each individual weave generates one of the following sets of instructions:
typedef struct weave_order { struct web *weave_web; which web we weave struct text_stream *weave_range; which parts of the web in this weave struct theme_tag *theme_match; pick out only paragraphs with this theme struct text_stream *booklet_title; struct weave_pattern *pattern; which pattern is to be followed struct filename *weave_to; where to put it struct weave_format *format; plain text, say, or HTML void *post_processing_results; optional typesetting diagnostics after running through int self_contained; make a self-contained file if possible struct linked_list *breadcrumbs; non-standard breadcrumb trail, if any struct filename *navigation; navigation links, or NULL if not supplied struct linked_list *plugins; of weave_plugin: these are for HTML extensions struct linked_list *colour_schemes; of colour_scheme: these are for HTML used for workspace during an actual weave: struct source_line *current_weave_line; CLASS_DEFINITION } weave_order;
- The structure weave_order is accessed in 1/cnf, 1/ptt, 3/tc, 3/tw, 3/twot, 4/lm, 4/is, 5/fm, 5/tf, 5/hf, 5/tu, 6/cln and here.
§2.2. Compile a set of instructions for the weaver2.2 =
wv = CREATE(weave_order); wv->weave_web = W; wv->weave_range = Str::duplicate(range); wv->pattern = pattern; wv->theme_match = tag; wv->booklet_title = Str::new(); wv->format = pattern->pattern_format; wv->post_processing_results = NULL; wv->self_contained = FALSE; wv->navigation = navigation; wv->breadcrumbs = breadcrumbs; wv->plugins = NEW_LINKED_LIST(weave_plugin); wv->colour_schemes = NEW_LINKED_LIST(colour_scheme); if (Reader::web_has_one_section(W)) wv->self_contained = TRUE; wv->current_weave_line = NULL; int has_content = FALSE; chapter *C; section *S; LOOP_OVER_LINKED_LIST(C, chapter, W->chapters) LOOP_OVER_LINKED_LIST(S, section, C->sections) if (Reader::range_within(S->md->sect_range, wv->weave_range)) has_content = TRUE; if (has_content == FALSE) Errors::fatal("no sections match that range"); TEMPORARY_TEXT(leafname) Translate the subweb range into details of what to weave2.2.1; pathname *H = W->redirect_weaves_to; if (H == NULL) H = into; if (H == NULL) { if (W->md->single_file == NULL) H = Reader::woven_folder(W); else H = Filenames::up(W->md->single_file); } if (to) { wv->weave_to = to; wv->self_contained = TRUE; } else wv->weave_to = Filenames::in(H, leafname); if (Str::len(pattern->initial_extension) > 0) wv->weave_to = Filenames::set_extension(wv->weave_to, pattern->initial_extension); DISCARD_TEXT(leafname)
- This code is used in §2.
§2.2.1. From the range and the theme, we work out the weave title, the leafname, and details of any cover-sheet to use.
Translate the subweb range into details of what to weave2.2.1 =
match_results mr = Regexp::create_mr(); if (Str::eq_wide_string(range, U"0")) { if (W->md->single_file) { wv->booklet_title = Str::duplicate(Bibliographic::get_datum(W->md, I"Title")); Filenames::write_unextended_leafname(leafname, W->md->single_file); } else { wv->booklet_title = Str::new_from_wide_string(U"Complete Program"); WRITE_TO(leafname, "Complete"); } if (wv->theme_match) Change the titling and leafname to match the tagged theme2.2.1.1; } else if (Regexp::match(&mr, range, U"%d+")) { Str::clear(wv->booklet_title); WRITE_TO(wv->booklet_title, "Chapter %S", range); Str::copy(leafname, wv->booklet_title); } else if (Regexp::match(&mr, range, U"%[A-O]")) { Str::clear(wv->booklet_title); WRITE_TO(wv->booklet_title, "Appendix %S", range); Str::copy(leafname, wv->booklet_title); } else if (Str::eq_wide_string(range, U"P")) { wv->booklet_title = Str::new_from_wide_string(U"Preliminaries"); Str::copy(leafname, wv->booklet_title); } else if (Str::eq_wide_string(range, U"M")) { wv->booklet_title = Str::new_from_wide_string(U"Manual"); Str::copy(leafname, wv->booklet_title); } else { section *S = Reader::get_section_for_range(W, range); if (S) Str::copy(wv->booklet_title, S->md->sect_title); else Str::copy(wv->booklet_title, range); Str::copy(leafname, range); } Bibliographic::set_datum(W->md, I"Booklet Title", wv->booklet_title); LOOP_THROUGH_TEXT(P, leafname) if ((Str::get(P) == '/') || (Str::get(P) == ' ')) Str::put(P, '-'); WRITE_TO(leafname, "%S", Formats::file_extension(wv->format)); Regexp::dispose_of(&mr);
- This code is used in §2.2.
§2.2.1.1. Change the titling and leafname to match the tagged theme2.2.1.1 =
Str::clear(wv->booklet_title); WRITE_TO(wv->booklet_title, "Extracts: %S", wv->theme_match->tag_name); Str::copy(leafname, wv->theme_match->tag_name);
- This code is used in §2.2.1.
§2.3. Each weave results in a compressed one-line printed report:
Report on the outcome of the weave to the console2.3 =
PRINT("[%S: %S -> %f", wv->booklet_title, wv->format->format_name, wv->weave_to); Formats::report_on_post_processing(wv); PRINT("]\n");
- This code is used in §2.
void Swarm::ensure_plugin(weave_order *wv, text_stream *name) { weave_plugin *existing; LOOP_OVER_LINKED_LIST(existing, weave_plugin, wv->plugins) if (Str::eq_insensitive(name, existing->plugin_name)) return; weave_plugin *wp = Assets::new(name); ADD_TO_LINKED_LIST(wp, weave_plugin, wv->plugins); } colour_scheme *Swarm::ensure_colour_scheme(weave_order *wv, text_stream *name, text_stream *pre) { colour_scheme *existing; LOOP_OVER_LINKED_LIST(existing, colour_scheme, wv->colour_schemes) if (Str::eq_insensitive(name, existing->scheme_name)) return existing; colour_scheme *cs = Assets::find_colour_scheme(wv->pattern, name, pre); if (cs == NULL) { if (Str::eq(name, I"Colours")) { TEMPORARY_TEXT(err) WRITE_TO(err, "No CSS file for the colour scheme '%S' can be found", name); Main::error_in_web(err, NULL); } else { return Swarm::ensure_colour_scheme(wv, I"Colours", I""); } } if (cs) ADD_TO_LINKED_LIST(cs, colour_scheme, wv->colour_schemes); return cs; } void Swarm::include_plugins(OUTPUT_STREAM, web *W, weave_order *wv, filename *from) { weave_plugin *wp; LOOP_OVER_LINKED_LIST(wp, weave_plugin, wv->plugins) Assets::include_plugin(OUT, W, wp, wv->pattern, from); colour_scheme *cs; LOOP_OVER_LINKED_LIST(cs, colour_scheme, wv->colour_schemes) Assets::include_colour_scheme(OUT, W, cs, wv->pattern, from); }
§4. After every swarm, we rebuild the index:
void Swarm::weave_index_templates(web *W, text_stream *range, weave_pattern *pattern, pathname *into, filename *nav, linked_list *crumbs) { if (!(Bibliographic::data_exists(W->md, I"Version Number"))) Bibliographic::set_datum(W->md, I"Version Number", I" "); filename *INF = Patterns::find_template(pattern, I"template-index.html"); if (INF) { pathname *H = W->redirect_weaves_to; if (H == NULL) H = Reader::woven_folder(W); filename *Contents = Filenames::in(H, I"index.html"); text_stream TO_struct; text_stream *OUT = &TO_struct; if (STREAM_OPEN_TO_FILE(OUT, Contents, ISO_ENC) == FALSE) Errors::fatal_with_file("unable to write contents file", Contents); if (W->as_ebook) Epub::note_page(W->as_ebook, Contents, I"Index", I"index"); PRINT("[Index file: %f]\n", Contents); Collater::collate(OUT, W, range, INF, pattern, nav, crumbs, NULL, Contents); STREAM_CLOSE(OUT); } }