[Swarm::] The Swarm. To feed multiple output requests to the weaver, and to present weaver results, and update indexes or contents pages. @h 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); } @ 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); @; 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); @; } return wv; } @ 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; @ = 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) @; 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) @ From the range and the theme, we work out the weave title, the leafname, and details of any cover-sheet to use. @ = match_results mr = Regexp::create_mr(); if (Str::eq_wide_string(range, L"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(L"Complete Program"); WRITE_TO(leafname, "Complete"); } if (wv->theme_match) @; } else if (Regexp::match(&mr, range, L"%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, L"%[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, L"P")) { wv->booklet_title = Str::new_from_wide_string(L"Preliminaries"); Str::copy(leafname, wv->booklet_title); } else if (Str::eq_wide_string(range, L"M")) { wv->booklet_title = Str::new_from_wide_string(L"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); @ = 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); @ Each weave results in a compressed one-line printed report: @ = PRINT("[%S: %S -> %f", wv->booklet_title, wv->format->format_name, wv->weave_to); Formats::report_on_post_processing(wv); PRINT("]\n"); @ = 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); } @ 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); } }