2019-02-04 22:26:45 +00:00
|
|
|
[Main::] Program Control.
|
|
|
|
|
|
|
|
The top level, which decides what is to be done and then carries
|
|
|
|
this plan out.
|
|
|
|
|
|
|
|
@ Inweb syntax has gradually shifted over the years, but there are two main
|
|
|
|
versions: the second was cleaned up and simplified from the first in 2019.
|
|
|
|
|
|
|
|
=
|
|
|
|
int default_inweb_syntax = V2_SYNTAX;
|
|
|
|
|
|
|
|
@ Inweb has a single fundamental mode of operation: on any given run, it
|
|
|
|
is either tangling, weaving or analysing. These processes use the same input
|
|
|
|
and parsing code, but then do very different things to produce their output,
|
|
|
|
so the fork in the road is not met until halfway through Inweb's execution.
|
|
|
|
|
|
|
|
@e NO_MODE from 0 /* a special mode for doing nothing except printing command-line syntax */
|
|
|
|
@e ANALYSE_MODE /* for -scan, -catalogue, -functions and so on */
|
|
|
|
@e TANGLE_MODE /* for any form of -tangle */
|
|
|
|
@e WEAVE_MODE /* for any form of -weave */
|
|
|
|
@e TRANSLATE_MODE /* a special mode for translating a multi-web makefile */
|
|
|
|
|
|
|
|
=
|
|
|
|
int fundamental_mode = NO_MODE;
|
|
|
|
|
|
|
|
@ This operation will be applied to a single web, and will apply to the whole
|
|
|
|
of that web unless we specify otherwise. Subsets of the web are represented by
|
|
|
|
short pieces of text called "ranges". This can be a section range like
|
|
|
|
|2/pine|, a chapter number like |12|, an appendix letter |A| or the
|
|
|
|
preliminaries block |P|, the special chapter |S| for the "Sections" chapter
|
|
|
|
of an unchaptered web, or the special value |0| to mean the entire web (which
|
|
|
|
is the default).
|
|
|
|
|
|
|
|
When weaving in "swarm mode", however, the user chooses a multiplicity of
|
|
|
|
operations rather than just one. Now it's no longer a matter of weaving a
|
|
|
|
particular section or chapter: we can weave all of the sections or chapters,
|
|
|
|
one after another.
|
|
|
|
|
|
|
|
@e SWARM_OFF_SWM from 0
|
|
|
|
@e SWARM_INDEX_SWM /* make index(es) as if swarming, but don't actually swarm */
|
|
|
|
@e SWARM_CHAPTERS_SWM /* swarm the chapters */
|
|
|
|
@e SWARM_SECTIONS_SWM /* swarm the individual sections */
|
|
|
|
|
|
|
|
@ In order to run, Inweb needs to know where it is installed -- this
|
|
|
|
enables it to find its configuration file, the macros file, and so on.
|
|
|
|
Unless told otherwise on the command line, we'll assume Inweb is present
|
|
|
|
in the current working directory. The "materials" will then be in a further
|
|
|
|
subfolder called |Materials|.
|
|
|
|
|
|
|
|
=
|
|
|
|
pathname *path_to_inweb = NULL; /* where we are installed */
|
|
|
|
pathname *path_to_inweb_materials = NULL; /* the materials pathname */
|
|
|
|
pathname *path_to_inweb_patterns = NULL; /* where built-in patterns are stored */
|
|
|
|
|
|
|
|
@ We count the errors in order to be able to exit with a suitable exit code.
|
|
|
|
|
|
|
|
=
|
|
|
|
int no_inweb_errors = 0;
|
|
|
|
|
|
|
|
@h Main routine.
|
|
|
|
|
|
|
|
=
|
|
|
|
int main(int argc, char **argv) {
|
|
|
|
@<Initialise inweb@>;
|
|
|
|
inweb_instructions args = Configuration::read(argc, argv);
|
|
|
|
fundamental_mode = args.inweb_mode;
|
|
|
|
path_to_inweb = Pathnames::installation_path("INWEB_PATH", I"inweb");
|
|
|
|
if (args.verbose_switch) PRINT("Installation path is %p\n", path_to_inweb);
|
2020-04-15 22:45:08 +00:00
|
|
|
path_to_inweb_patterns = Pathnames::down(path_to_inweb, I"Patterns");
|
|
|
|
path_to_inweb_materials = Pathnames::down(path_to_inweb, I"Materials");
|
2019-02-04 22:26:45 +00:00
|
|
|
|
|
|
|
Main::follow_instructions(&args);
|
|
|
|
|
|
|
|
@<Shut inweb down@>;
|
|
|
|
}
|
|
|
|
|
|
|
|
@<Initialise inweb@> =
|
|
|
|
Foundation::start();
|
|
|
|
Formats::create_weave_formats();
|
|
|
|
|
|
|
|
@<Shut inweb down@> =
|
|
|
|
Foundation::end();
|
|
|
|
return (no_inweb_errors == 0)?0:1;
|
|
|
|
|
|
|
|
@h Following instructions.
|
|
|
|
This is the whole program in a nutshell, and it's a pretty old-school
|
|
|
|
program: some input, some thinking, a choice of three forms of output.
|
|
|
|
|
|
|
|
=
|
|
|
|
void Main::follow_instructions(inweb_instructions *ins) {
|
2019-03-12 23:32:12 +00:00
|
|
|
web *W = NULL;
|
2020-04-01 23:45:41 +00:00
|
|
|
if ((ins->chosen_web) || (ins->chosen_file)) {
|
2019-03-12 23:32:12 +00:00
|
|
|
W = Reader::load_web(ins->chosen_web, ins->chosen_file,
|
2020-04-01 19:43:48 +00:00
|
|
|
WebModules::make_search_path(ins->import_setting), ins->verbose_switch,
|
2020-04-01 23:45:41 +00:00
|
|
|
TRUE);
|
|
|
|
W->redirect_weaves_to = ins->weave_into_setting;
|
|
|
|
Reader::read_web(W, ins->verbose_switch);
|
2020-04-12 22:48:34 +00:00
|
|
|
Parser::parse_web(W, ins->inweb_mode);
|
2020-04-01 23:45:41 +00:00
|
|
|
}
|
2019-03-12 23:32:12 +00:00
|
|
|
if (no_inweb_errors == 0) {
|
|
|
|
if (ins->inweb_mode == TRANSLATE_MODE) @<Translate a makefile@>
|
2020-04-05 17:37:43 +00:00
|
|
|
else if (ins->show_languages_switch) @<List available programming languages@>
|
2020-04-06 23:53:26 +00:00
|
|
|
else if ((ins->test_language_setting) || (ins->test_language_on_setting)) @<Test a language@>
|
2019-03-12 23:32:12 +00:00
|
|
|
else if (ins->inweb_mode != NO_MODE) @<Analyse, tangle or weave an existing web@>;
|
|
|
|
}
|
2019-02-04 22:26:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@ This is a one-off featurette:
|
|
|
|
|
|
|
|
@<Translate a makefile@> =
|
2019-02-10 23:08:53 +00:00
|
|
|
if ((ins->makefile_setting) && (ins->prototype_setting == NULL))
|
|
|
|
ins->prototype_setting = Filenames::from_text(I"makescript.txt");
|
|
|
|
if ((ins->gitignore_setting) && (ins->prototype_setting == NULL))
|
|
|
|
ins->prototype_setting = Filenames::from_text(I"gitignorescript.txt");
|
2020-03-23 21:39:27 +00:00
|
|
|
if ((ins->writeme_setting) && (ins->prototype_setting == NULL))
|
|
|
|
ins->prototype_setting = Filenames::from_text(I"READMEscript.txt");
|
2019-02-10 23:08:53 +00:00
|
|
|
if (ins->makefile_setting)
|
2020-04-02 12:30:38 +00:00
|
|
|
Makefiles::write(W, ins->prototype_setting, ins->makefile_setting,
|
|
|
|
WebModules::make_search_path(ins->import_setting));
|
2019-02-10 23:08:53 +00:00
|
|
|
else if (ins->gitignore_setting)
|
2019-03-12 23:32:12 +00:00
|
|
|
Git::write_gitignore(W, ins->prototype_setting, ins->gitignore_setting);
|
2020-03-23 15:04:43 +00:00
|
|
|
else if (ins->advance_setting)
|
|
|
|
BuildFiles::advance(ins->advance_setting);
|
2020-03-23 21:39:27 +00:00
|
|
|
else if (ins->writeme_setting)
|
|
|
|
Readme::write(ins->prototype_setting, ins->writeme_setting);
|
2019-02-04 22:26:45 +00:00
|
|
|
|
2020-04-05 17:37:43 +00:00
|
|
|
@ As is this:
|
|
|
|
|
|
|
|
@<List available programming languages@> =
|
|
|
|
Languages::read_definitions(NULL);
|
|
|
|
Languages::show(STDOUT);
|
|
|
|
|
2020-04-06 23:53:26 +00:00
|
|
|
@ And this:
|
|
|
|
|
|
|
|
@<Test a language@> =
|
|
|
|
if ((ins->test_language_setting) && (ins->test_language_on_setting)) {
|
|
|
|
TEMPORARY_TEXT(matter);
|
|
|
|
TEMPORARY_TEXT(coloured);
|
|
|
|
Painter::colour_file(ins->test_language_setting, ins->test_language_on_setting,
|
|
|
|
matter, coloured);
|
|
|
|
PRINT("Test of colouring for language %S:\n%S\n%S\n",
|
|
|
|
ins->test_language_setting->language_name, matter, coloured);
|
|
|
|
DISCARD_TEXT(matter);
|
|
|
|
DISCARD_TEXT(coloured);
|
|
|
|
} else {
|
|
|
|
Errors::fatal("-test-language and -test-language-on must both be given");
|
|
|
|
}
|
|
|
|
|
2019-03-12 23:32:12 +00:00
|
|
|
@ But otherwise we do something with the given web:
|
2019-02-04 22:26:45 +00:00
|
|
|
|
|
|
|
@<Analyse, tangle or weave an existing web@> =
|
2020-04-01 23:45:41 +00:00
|
|
|
Reader::print_web_statistics(W);
|
2019-03-12 23:32:12 +00:00
|
|
|
if (ins->inweb_mode == ANALYSE_MODE) @<Analyse the web@>;
|
|
|
|
if (ins->inweb_mode == TANGLE_MODE) @<Tangle the web@>;
|
|
|
|
if (ins->inweb_mode == WEAVE_MODE) @<Weave the web@>;
|
2019-02-04 22:26:45 +00:00
|
|
|
|
|
|
|
@ "Analysis" invokes any combination of the following diagnostic tools:
|
|
|
|
|
|
|
|
@<Analyse the web@> =
|
|
|
|
if (ins->swarm_mode != SWARM_OFF_SWM)
|
|
|
|
Errors::fatal("only specific parts of the web can be analysed");
|
|
|
|
if (ins->catalogue_switch)
|
|
|
|
Analyser::catalogue_the_sections(W, ins->chosen_range, BASIC_SECTIONCAT);
|
|
|
|
if (ins->functions_switch)
|
|
|
|
Analyser::catalogue_the_sections(W, ins->chosen_range, FUNCTIONS_SECTIONCAT);
|
|
|
|
if (ins->structures_switch)
|
|
|
|
Analyser::catalogue_the_sections(W, ins->chosen_range, STRUCTURES_SECTIONCAT);
|
|
|
|
if (ins->makefile_setting)
|
2020-04-02 12:30:38 +00:00
|
|
|
Analyser::write_makefile(W, ins->makefile_setting,
|
|
|
|
WebModules::make_search_path(ins->import_setting));
|
2019-02-04 22:26:45 +00:00
|
|
|
if (ins->gitignore_setting)
|
|
|
|
Analyser::write_gitignore(W, ins->gitignore_setting);
|
2020-03-23 15:04:43 +00:00
|
|
|
if (ins->advance_switch)
|
2020-04-01 19:43:48 +00:00
|
|
|
BuildFiles::advance_for_web(W->md);
|
2019-02-04 22:26:45 +00:00
|
|
|
if (ins->scan_switch)
|
|
|
|
Analyser::scan_line_categories(W, ins->chosen_range);
|
|
|
|
|
|
|
|
@ We can tangle to any one of what might be several targets, numbered upwards
|
|
|
|
from 0. Target 0 always exists, and is the main program forming the web. For
|
|
|
|
many webs, this will in fact be the only target, but Inweb also allows
|
|
|
|
marked sections of a web to be independent targets -- the idea here is to
|
|
|
|
allow an Appendix in the web to contain a configuration file, or auxiliary
|
|
|
|
program, needed for the main program to work; this might be written in a
|
|
|
|
quite different language from the rest of the web, and tangles to a different
|
|
|
|
output, but needs to be part of the web since it's essential to an understanding
|
|
|
|
of the whole system.
|
|
|
|
|
|
|
|
In this section we determine |tn|, the target number wanted, and |tangle_to|,
|
|
|
|
the filename of the tangled code to write. This may have been set at the command
|
|
|
|
line , but otherwise we impose a sensible choice based on the target.
|
|
|
|
|
|
|
|
@<Tangle the web@> =
|
|
|
|
TEMPORARY_TEXT(tangle_leaf);
|
|
|
|
tangle_target *tn = NULL;
|
|
|
|
if (Str::eq_wide_string(ins->chosen_range, L"0")) {
|
|
|
|
@<Work out main tangle destination@>;
|
|
|
|
} else if (Reader::get_section_for_range(W, ins->chosen_range)) {
|
|
|
|
@<Work out an independent tangle destination, from one section of the web@>;
|
|
|
|
}
|
|
|
|
if (Str::len(tangle_leaf) == 0) { Errors::fatal("no tangle destination known"); }
|
|
|
|
|
|
|
|
filename *tangle_to = ins->tangle_setting;
|
|
|
|
if (tangle_to == NULL) {
|
|
|
|
pathname *P = Reader::tangled_folder(W);
|
2020-04-15 22:45:08 +00:00
|
|
|
if (W->md->single_file) P = Filenames::up(W->md->single_file);
|
|
|
|
tangle_to = Filenames::in(P, tangle_leaf);
|
2019-02-04 22:26:45 +00:00
|
|
|
}
|
|
|
|
if (tn == NULL) tn = Tangler::primary_target(W);
|
2020-04-16 22:51:03 +00:00
|
|
|
Tangler::tangle(W, tn, tangle_to);
|
2019-02-04 22:26:45 +00:00
|
|
|
DISCARD_TEXT(tangle_leaf);
|
|
|
|
|
|
|
|
@ Here the target number is 0, and the tangle is of the main part of the web,
|
|
|
|
which for many small webs will be the entire thing.
|
|
|
|
|
|
|
|
@<Work out main tangle destination@> =
|
|
|
|
tn = NULL;
|
2020-04-01 19:43:48 +00:00
|
|
|
if (Bibliographic::data_exists(W->md, I"Short Title"))
|
|
|
|
Str::copy(tangle_leaf, Bibliographic::get_datum(W->md, I"Short Title"));
|
2019-02-04 22:26:45 +00:00
|
|
|
else
|
2020-04-01 19:43:48 +00:00
|
|
|
Str::copy(tangle_leaf, Bibliographic::get_datum(W->md, I"Title"));
|
2019-02-04 22:26:45 +00:00
|
|
|
Str::concatenate(tangle_leaf, W->main_language->file_extension);
|
|
|
|
|
|
|
|
@ If someone tangles, say, |2/eg| then the default filename is "Example Section".
|
|
|
|
|
|
|
|
@<Work out an independent tangle destination, from one section of the web@> =
|
|
|
|
section *S = Reader::get_section_for_range(W, ins->chosen_range);
|
|
|
|
tn = S->sect_target;
|
|
|
|
if (tn == NULL) Errors::fatal("section cannot be independently tangled");
|
2020-04-01 19:43:48 +00:00
|
|
|
Str::copy(tangle_leaf, Filenames::get_leafname(S->md->source_file_for_section));
|
2019-02-04 22:26:45 +00:00
|
|
|
|
|
|
|
@ Weaving is not actually easier, it's just more thoroughly delegated:
|
|
|
|
|
|
|
|
@<Weave the web@> =
|
|
|
|
Numbering::number_web(W);
|
|
|
|
|
|
|
|
theme_tag *tag = Tags::find_by_name(ins->tag_setting, FALSE);
|
|
|
|
if ((Str::len(ins->tag_setting) > 0) && (tag == NULL))
|
|
|
|
Errors::fatal_with_text("no such theme as '%S'", ins->tag_setting);
|
|
|
|
|
|
|
|
weave_pattern *pattern = Patterns::find(W, ins->weave_pattern);
|
|
|
|
if ((ins->chosen_range_actually_chosen == FALSE) && (ins->chosen_file == NULL))
|
|
|
|
Configuration::set_range(ins, pattern->default_range);
|
|
|
|
|
|
|
|
int r = Formats::begin_weaving(W, pattern);
|
|
|
|
if (r != SWARM_OFF_SWM) ins->swarm_mode = r;
|
|
|
|
@<Assign section numbers for printing purposes@>;
|
|
|
|
if (ins->swarm_mode == SWARM_OFF_SWM) {
|
|
|
|
int shall_we_open = ins->open_pdf_switch;
|
|
|
|
if (shall_we_open == NOT_APPLICABLE) { /* i.e., if it wasn't set at the command line */
|
|
|
|
if (Str::len(pattern->open_command) > 0) shall_we_open = TRUE;
|
|
|
|
else shall_we_open = FALSE;
|
|
|
|
}
|
|
|
|
Swarm::weave_subset(W, ins->chosen_range, shall_we_open, tag, pattern,
|
2020-04-13 16:06:45 +00:00
|
|
|
ins->weave_to_setting, ins->weave_into_setting,
|
2020-03-19 00:03:04 +00:00
|
|
|
ins->breadcrumb_setting, ins->navigation_setting);
|
2020-04-14 19:29:03 +00:00
|
|
|
Patterns::copy_payloads_into_weave(W, pattern);
|
2019-02-04 22:26:45 +00:00
|
|
|
} else {
|
|
|
|
Swarm::weave(W, ins->chosen_range, ins->swarm_mode, tag, pattern,
|
2020-04-13 16:06:45 +00:00
|
|
|
ins->weave_to_setting, ins->weave_into_setting,
|
2020-03-19 00:03:04 +00:00
|
|
|
ins->breadcrumb_setting, ins->navigation_setting);
|
2019-02-04 22:26:45 +00:00
|
|
|
}
|
|
|
|
Formats::end_weaving(W, pattern);
|
|
|
|
|
|
|
|
@<Assign section numbers for printing purposes@> =
|
|
|
|
section *S; int k = 1;
|
|
|
|
LOOP_OVER(S, section)
|
2020-04-12 16:24:23 +00:00
|
|
|
if (Reader::range_within(S->md->sect_range, ins->chosen_range))
|
2019-02-04 22:26:45 +00:00
|
|
|
S->printed_number = k++;
|
|
|
|
|
|
|
|
@h Error messages.
|
|
|
|
The Foundation module provides convenient functions to issue error messages,
|
|
|
|
but we'll use the following wrapper when issuing an error at a line of web
|
|
|
|
source:
|
|
|
|
|
|
|
|
=
|
|
|
|
void Main::error_in_web(text_stream *message, source_line *sl) {
|
|
|
|
if (sl) {
|
|
|
|
Errors::in_text_file_S(message, &(sl->source));
|
|
|
|
WRITE_TO(STDERR, "%07d %S\n", sl->source.line_count, sl->text);
|
|
|
|
} else {
|
|
|
|
Errors::in_text_file_S(message, NULL);
|
|
|
|
}
|
|
|
|
no_inweb_errors++;
|
|
|
|
}
|