To parse the command line arguments with which inweb was called, and to handle any errors it needs to issue.
§1. Instructions. The following structure exists just to hold what the user specified on the command line: there will only ever be one of these.
typedef struct inweb_instructions { int inweb_mode; our main mode of operation: one of the *_MODE constants struct pathname *chosen_web; project folder relative to cwd struct filename *chosen_file; or, single file relative to cwd struct text_stream *chosen_range; which subset of this web we apply to (often, all of it) int chosen_range_actually_chosen; rather than being a default choice int swarm_mode; relevant to weaving only: one of the *_SWARM constants struct text_stream *tag_setting; -weave-tag X: weave, but only the material tagged X struct text_stream *weave_pattern; -weave-as X: for example, -weave-to HTML int show_languages_switch; -show-languages: print list of available PLs int catalogue_switch; -catalogue: print catalogue of sections int functions_switch; -functions: print catalogue of functions within sections int structures_switch; -structures: print catalogue of structures within sections int advance_switch; -advance-build: advance build file for web int scan_switch; -scan: simply show the syntactic scan of the source int ctags_switch; -ctags: generate a set of Universal Ctags on each tangle struct filename *weave_to_setting; -weave-to X: the pathname X, if supplied struct pathname *weave_into_setting; -weave-into X: the pathname X, if supplied int sequential; give the sections sequential sigils struct filename *tangle_setting; -tangle-to X: the pathname X, if supplied struct filename *ctags_setting; -ctags-to X: the pathname X, if supplied struct filename *makefile_setting; -makefile X: the filename X, if supplied struct filename *gitignore_setting; -gitignore X: the filename X, if supplied struct filename *advance_setting; -advance-build-file X: advance build file X struct filename *writeme_setting; -write-me X: advance build file X struct filename *prototype_setting; -prototype X: the pathname X, if supplied struct filename *navigation_setting; -navigation X: the filename X, if supplied struct filename *colony_setting; -colony X: the filename X, if supplied struct text_stream *member_setting; -member X: sets web to member X of colony struct linked_list *breadcrumb_setting; of breadcrumb_request struct text_stream *platform_setting; -platform X: sets prevailing platform to X int verbose_switch; -verbose: print names of files read to stdout int targets; used only for parsing struct programming_language *test_language_setting; -test-language X struct filename *test_language_on_setting; -test-language-on X struct pathname *import_setting; -import X: where to find imported webs } inweb_instructions;
- The structure inweb_instructions is accessed in 1/pc and here.
§2. Reading the command line. The dull work of this is done by the Foundation module: all we need to do is to enumerate constants for the Inweb-specific command line switches, and then declare them.
inweb_instructions Configuration::read(int argc, char **argv) { inweb_instructions args; Initialise the args2.1; Declare the command-line switches specific to Inweb2.3; CommandLine::read(argc, argv, &args, &Configuration::switch, &Configuration::bareword); Configuration::member_and_colony(&args); if (Str::len(args.weave_pattern) == 0) WRITE_TO(args.weave_pattern, "HTML"); if ((args.chosen_web == NULL) && (args.chosen_file == NULL)) { if ((args.makefile_setting) || (args.gitignore_setting)) args.inweb_mode = TRANSLATE_MODE; if (args.inweb_mode != TRANSLATE_MODE) args.inweb_mode = NO_MODE; } if (Str::len(args.chosen_range) == 0) { Str::copy(args.chosen_range, I"0"); } return args; }
§2.1. Initialise the args2.1 =
args.inweb_mode = NO_MODE; args.swarm_mode = SWARM_OFF_SWM; args.show_languages_switch = FALSE; args.catalogue_switch = FALSE; args.functions_switch = FALSE; args.structures_switch = FALSE; args.advance_switch = FALSE; args.scan_switch = FALSE; args.verbose_switch = FALSE; args.ctags_switch = TRUE; args.chosen_web = NULL; args.chosen_file = NULL; args.chosen_range = Str::new(); args.chosen_range_actually_chosen = FALSE; args.tangle_setting = NULL; args.ctags_setting = NULL; args.weave_to_setting = NULL; args.weave_into_setting = NULL; args.makefile_setting = NULL; args.gitignore_setting = NULL; args.advance_setting = NULL; args.writeme_setting = NULL; args.prototype_setting = NULL; args.navigation_setting = NULL; args.colony_setting = NULL; args.member_setting = NULL; args.breadcrumb_setting = NEW_LINKED_LIST(breadcrumb_request); args.platform_setting = NULL; args.tag_setting = Str::new(); args.weave_pattern = Str::new(); args.import_setting = NULL; args.targets = 0; args.test_language_setting = NULL; args.test_language_on_setting = NULL;
- This code is used in §2.
§2.2. The CommandLine section of Foundation needs to be told what command-line switches we want, other than the standard set (such as -help) which it provides automatically.
enum VERBOSE_CLSW enum IMPORT_FROM_CLSW enum LANGUAGES_CLSG enum LANGUAGE_CLSW enum LANGUAGES_CLSW enum SHOW_LANGUAGES_CLSW enum TEST_LANGUAGE_CLSW enum TEST_LANGUAGE_ON_CLSW enum ANALYSIS_CLSG enum CATALOGUE_CLSW enum FUNCTIONS_CLSW enum STRUCTURES_CLSW enum ADVANCE_CLSW enum GITIGNORE_CLSW enum MAKEFILE_CLSW enum WRITEME_CLSW enum PLATFORM_CLSW enum ADVANCE_FILE_CLSW enum PROTOTYPE_CLSW enum SCAN_CLSW enum WEAVING_CLSG enum WEAVE_CLSW enum WEAVE_INTO_CLSW enum WEAVE_TO_CLSW enum OPEN_CLSW enum WEAVE_AS_CLSW enum WEAVE_TAG_CLSW enum BREADCRUMB_CLSW enum NAVIGATION_CLSW enum TANGLING_CLSG enum TANGLE_CLSW enum TANGLE_TO_CLSW enum CTAGS_TO_CLSW enum CTAGS_CLSW enum COLONIAL_CLSG enum COLONY_CLSW enum MEMBER_CLSW
§2.3. Declare the command-line switches specific to Inweb2.3 =
CommandLine::declare_heading(U"inweb: a tool for literate programming\n\n" U"Usage: inweb WEB OPTIONS RANGE\n\n" U"WEB must be a directory holding a literate program (a 'web')\n\n" U"The legal RANGEs are:\n" U" all: complete web (the default if no TARGETS set)\n" U" P: all preliminaries\n" U" 1: Chapter 1 (and so on)\n" U" A: Appendix A (and so on, up to Appendix O)\n" U" 3/eg: section with abbreviated name \"3/eg\" (and so on)\n" U"You can also, or instead, specify:\n" U" index: to weave an HTML page indexing the project\n" U" chapters: to weave all chapters as individual documents\n" U" sections: ditto with sections\n"); CommandLine::begin_group(LANGUAGES_CLSG, I"for locating programming language definitions"); CommandLine::declare_switch(LANGUAGE_CLSW, U"read-language", 2, U"read language definition from file X"); CommandLine::declare_switch(LANGUAGES_CLSW, U"read-languages", 2, U"read all language definitions in path X"); CommandLine::declare_switch(SHOW_LANGUAGES_CLSW, U"show-languages", 1, U"list programming languages supported by Inweb"); CommandLine::declare_switch(TEST_LANGUAGE_CLSW, U"test-language", 2, U"test language X on..."); CommandLine::declare_switch(TEST_LANGUAGE_ON_CLSW, U"test-language-on", 2, U"...the code in the file X"); CommandLine::end_group(); CommandLine::begin_group(ANALYSIS_CLSG, I"for analysing a web"); CommandLine::declare_switch(CATALOGUE_CLSW, U"catalogue", 1, U"list the sections in the web"); CommandLine::declare_switch(CATALOGUE_CLSW, U"catalog", 1, U"same as '-catalogue'"); CommandLine::declare_switch(MAKEFILE_CLSW, U"makefile", 2, U"write a makefile for this web and store it in X"); CommandLine::declare_switch(GITIGNORE_CLSW, U"gitignore", 2, U"write a .gitignore file for this web and store it in X"); CommandLine::declare_switch(ADVANCE_FILE_CLSW, U"advance-build-file", 2, U"increment daily build code in file X"); CommandLine::declare_switch(WRITEME_CLSW, U"write-me", 2, U"write a read-me file following instructions in file X"); CommandLine::declare_switch(PLATFORM_CLSW, U"platform", 2, U"use platform X (e.g. 'windows') when making e.g. makefiles"); CommandLine::declare_switch(PROTOTYPE_CLSW, U"prototype", 2, U"translate makefile from prototype X"); CommandLine::declare_switch(FUNCTIONS_CLSW, U"functions", 1, U"catalogue the functions in the web"); CommandLine::declare_switch(STRUCTURES_CLSW, U"structures", 1, U"catalogue the structures in the web"); CommandLine::declare_switch(ADVANCE_CLSW, U"advance-build", 1, U"increment daily build code for the web"); CommandLine::declare_switch(SCAN_CLSW, U"scan", 1, U"scan the web"); CommandLine::end_group(); CommandLine::begin_group(WEAVING_CLSG, I"for weaving a web"); CommandLine::declare_switch(WEAVE_CLSW, U"weave", 1, U"weave the web into human-readable form"); CommandLine::declare_switch(WEAVE_INTO_CLSW, U"weave-into", 2, U"weave, but into directory X"); CommandLine::declare_switch(WEAVE_TO_CLSW, U"weave-to", 2, U"weave, but to filename X (for single files only)"); CommandLine::declare_switch(OPEN_CLSW, U"open", 1, U"weave then open woven file"); CommandLine::declare_switch(WEAVE_AS_CLSW, U"weave-as", 2, U"set weave pattern to X (default is 'HTML')"); CommandLine::declare_switch(WEAVE_TAG_CLSW, U"weave-tag", 2, U"weave, but only using material tagged as X"); CommandLine::declare_switch(BREADCRUMB_CLSW, U"breadcrumb", 2, U"use the text X as a breadcrumb in overhead navigation"); CommandLine::declare_switch(NAVIGATION_CLSW, U"navigation", 2, U"use the file X as a column of navigation links"); CommandLine::end_group(); CommandLine::begin_group(TANGLING_CLSG, I"for tangling a web"); CommandLine::declare_switch(TANGLE_CLSW, U"tangle", 1, U"tangle the web into machine-compilable form"); CommandLine::declare_switch(TANGLE_TO_CLSW, U"tangle-to", 2, U"tangle, but to filename X"); CommandLine::declare_switch(CTAGS_TO_CLSW, U"ctags-to", 2, U"tangle, but write Universal Ctags file to X not to 'tags'"); CommandLine::declare_boolean_switch(CTAGS_CLSW, U"ctags", 1, U"write a Universal Ctags file when tangling", TRUE); CommandLine::end_group(); CommandLine::begin_group(COLONIAL_CLSG, I"for dealing with colonies of webs together"); CommandLine::declare_switch(COLONY_CLSW, U"colony", 2, U"use the file X as a list of webs in this colony"); CommandLine::declare_switch(MEMBER_CLSW, U"member", 2, U"use member X from the colony as our web"); CommandLine::end_group(); CommandLine::declare_boolean_switch(VERBOSE_CLSW, U"verbose", 1, U"explain what inweb is doing", FALSE); CommandLine::declare_switch(IMPORT_FROM_CLSW, U"import-from", 2, U"specify that imported modules are at pathname X");
- This code is used in §2.
§3. Foundation calls this on any -switch argument read:
void Configuration::switch(int id, int val, text_stream *arg, void *state) { inweb_instructions *args = (inweb_instructions *) state; switch (id) { Miscellaneous case VERBOSE_CLSW: args->verbose_switch = TRUE; break; case IMPORT_FROM_CLSW: args->import_setting = Pathnames::from_text(arg); break; Analysis case LANGUAGE_CLSW: Languages::read_definition(Filenames::from_text(arg)); break; case LANGUAGES_CLSW: Languages::read_definitions(Pathnames::from_text(arg)); break; case SHOW_LANGUAGES_CLSW: args->show_languages_switch = TRUE; Configuration::set_fundamental_mode(args, ANALYSE_MODE); break; case TEST_LANGUAGE_CLSW: args->test_language_setting = Languages::read_definition(Filenames::from_text(arg)); Configuration::set_fundamental_mode(args, ANALYSE_MODE); break; case TEST_LANGUAGE_ON_CLSW: args->test_language_on_setting = Filenames::from_text(arg); Configuration::set_fundamental_mode(args, ANALYSE_MODE); break; case CATALOGUE_CLSW: args->catalogue_switch = TRUE; Configuration::set_fundamental_mode(args, ANALYSE_MODE); break; case FUNCTIONS_CLSW: args->functions_switch = TRUE; Configuration::set_fundamental_mode(args, ANALYSE_MODE); break; case STRUCTURES_CLSW: args->structures_switch = TRUE; Configuration::set_fundamental_mode(args, ANALYSE_MODE); break; case ADVANCE_CLSW: args->advance_switch = TRUE; Configuration::set_fundamental_mode(args, ANALYSE_MODE); break; case MAKEFILE_CLSW: args->makefile_setting = Filenames::from_text(arg); if (args->inweb_mode != TRANSLATE_MODE) Configuration::set_fundamental_mode(args, ANALYSE_MODE); break; case GITIGNORE_CLSW: args->gitignore_setting = Filenames::from_text(arg); if (args->inweb_mode != TRANSLATE_MODE) Configuration::set_fundamental_mode(args, ANALYSE_MODE); break; case PLATFORM_CLSW: args->platform_setting = Str::duplicate(arg); break; case ADVANCE_FILE_CLSW: args->advance_setting = Filenames::from_text(arg); Configuration::set_fundamental_mode(args, TRANSLATE_MODE); break; case WRITEME_CLSW: args->writeme_setting = Filenames::from_text(arg); Configuration::set_fundamental_mode(args, TRANSLATE_MODE); break; case PROTOTYPE_CLSW: args->prototype_setting = Filenames::from_text(arg); Configuration::set_fundamental_mode(args, TRANSLATE_MODE); break; case SCAN_CLSW: args->scan_switch = TRUE; Configuration::set_fundamental_mode(args, ANALYSE_MODE); break; Weave-related case WEAVE_CLSW: Configuration::set_fundamental_mode(args, WEAVE_MODE); break; case WEAVE_INTO_CLSW: args->weave_into_setting = Pathnames::from_text(arg); Configuration::set_fundamental_mode(args, WEAVE_MODE); break; case WEAVE_TO_CLSW: args->weave_to_setting = Filenames::from_text(arg); Configuration::set_fundamental_mode(args, WEAVE_MODE); break; case WEAVE_AS_CLSW: args->weave_pattern = Str::duplicate(arg); Configuration::set_fundamental_mode(args, WEAVE_MODE); break; case WEAVE_TAG_CLSW: args->tag_setting = Str::duplicate(arg); Configuration::set_fundamental_mode(args, WEAVE_MODE); break; case BREADCRUMB_CLSW: ADD_TO_LINKED_LIST(Colonies::request_breadcrumb(arg), breadcrumb_request, args->breadcrumb_setting); Configuration::set_fundamental_mode(args, WEAVE_MODE); break; case NAVIGATION_CLSW: args->navigation_setting = Filenames::from_text(arg); Configuration::set_fundamental_mode(args, WEAVE_MODE); break; Colonial case COLONY_CLSW: args->colony_setting = Filenames::from_text(arg); break; case MEMBER_CLSW: args->member_setting = Str::duplicate(arg); break; Tangle-related case TANGLE_CLSW: Configuration::set_fundamental_mode(args, TANGLE_MODE); break; case TANGLE_TO_CLSW: args->tangle_setting = Filenames::from_text(arg); Configuration::set_fundamental_mode(args, TANGLE_MODE); break; case CTAGS_TO_CLSW: args->ctags_setting = Filenames::from_text(arg); break; case CTAGS_CLSW: args->ctags_switch = val; break; default: internal_error("unimplemented switch"); } }
§4. The colony file is, in one sense, a collection of presets for the web location and its navigational aids.
void Configuration::member_and_colony(inweb_instructions *args) { if (args->colony_setting) Colonies::load(args->colony_setting); if (Str::len(args->member_setting) > 0) { if ((args->chosen_web == NULL) && (args->chosen_file == NULL)) { colony_member *CM = Colonies::find(args->member_setting); if (CM == NULL) Errors::fatal("the colony has no member of that name"); Configuration::bareword(0, CM->path, args); if (Str::len(args->weave_pattern) == 0) args->weave_pattern = CM->default_weave_pattern; if (LinkedLists::len(args->breadcrumb_setting) == 0) args->breadcrumb_setting = CM->breadcrumb_tail; if (args->navigation_setting == NULL) args->navigation_setting = CM->navigation; if (args->weave_into_setting == NULL) args->weave_into_setting = CM->weave_path; } else { Errors::fatal("cannot specify a web and also use -member"); } } }
§5. Foundation calls this routine on any command-line argument which is neither a switch (like -weave), nor an argument for a switch (like the X in -weave-as X).
void Configuration::bareword(int id, text_stream *opt, void *state) { inweb_instructions *args = (inweb_instructions *) state; if ((args->chosen_web == NULL) && (args->chosen_file == NULL)) { if (Str::suffix_eq(opt, I".inweb", 6)) args->chosen_file = Filenames::from_text(opt); else args->chosen_web = Pathnames::from_text(opt); } else Configuration::set_range(args, opt); }
§6. Here we read a range. The special ranges index, chapters and sections are converted into swarm settings instead. all is simply an alias for 0. Otherwise, a range is a chapter number/letter, or a section range.
void Configuration::set_range(inweb_instructions *args, text_stream *opt) { match_results mr = Regexp::create_mr(); if (Str::eq_wide_string(opt, U"index")) { args->swarm_mode = SWARM_INDEX_SWM; } else if (Str::eq_wide_string(opt, U"chapters")) { args->swarm_mode = SWARM_CHAPTERS_SWM; } else if (Str::eq_wide_string(opt, U"sections")) { args->swarm_mode = SWARM_SECTIONS_SWM; } else { if (++args->targets > 1) Errors::fatal("at most one target may be given"); if (Str::eq_wide_string(opt, U"all")) { Str::copy(args->chosen_range, I"0"); } else if (((Characters::isalnum(Str::get_first_char(opt))) && (Str::len(opt) == 1)) || (Regexp::match(&mr, opt, U"%i+/%i+"))) { Str::copy(args->chosen_range, opt); string_position P = Str::start(args->chosen_range); Str::put(P, Characters::toupper(Str::get(P))); } else { TEMPORARY_TEXT(ERM) WRITE_TO(ERM, "target not recognised (see -help for more): %S", opt); Main::error_in_web(ERM, NULL); DISCARD_TEXT(ERM) exit(1); } } args->chosen_range_actually_chosen = TRUE; Regexp::dispose_of(&mr); }
§7. We can only be in a single mode at a time:
void Configuration::set_fundamental_mode(inweb_instructions *args, int new_material) { if ((args->inweb_mode != NO_MODE) && (args->inweb_mode != new_material)) Errors::fatal("can only do one at a time - weaving, tangling or analysing"); args->inweb_mode = new_material; }