[Extensions::Census::] Extension Census. To conduct a census of all the extensions installed (whether used on this run or not), and keep the documentation index for them up to date. @ = typedef struct extension_census { struct linked_list *search_list; /* of |inbuild_nest| */ struct linked_list *census_data; /* of |extension_census_datum| */ struct linked_list *raw_data; /* of |inbuild_search_result| */ int no_census_errors; MEMORY_MANAGEMENT } extension_census; extension_census *Extensions::Census::new(void) { extension_census *C = CREATE(extension_census); C->search_list = Inbuild::nest_list(); C->census_data = NEW_LINKED_LIST(extension_census_datum); C->raw_data = NEW_LINKED_LIST(inbuild_search_result); C->no_census_errors = 0; return C; } pathname *Extensions::Census::internal_path(extension_census *C) { inbuild_nest *N = NULL; LOOP_OVER_LINKED_LIST(N, inbuild_nest, C->search_list) if (Nests::get_tag(N) == INTERNAL_NEST_TAG) return ExtensionManager::path_within_nest(N); return NULL; } pathname *Extensions::Census::external_path(extension_census *C) { inbuild_nest *N = NULL; LOOP_OVER_LINKED_LIST(N, inbuild_nest, C->search_list) if (Nests::get_tag(N) == EXTERNAL_NEST_TAG) return ExtensionManager::path_within_nest(N); return NULL; } @ In addition to the extensions read in, there are the roads not taken: the ones which I7 has at its disposal, but which the source text never asks to include. Inform performs a "census" of installed extensions on every run, essentially by scanning the directories which hold them to see what the user has installed there. Each extension discovered will produce a single "extension census datum", or ECD. = typedef struct extension_census_datum { struct inbuild_search_result *found_as; int built_in; /* found in the Inform 7 application's private stock */ int project_specific; /* found in the Materials folder for the current project */ int overriding_a_built_in_extension; /* not built in, but overriding one which is */ struct extension_census_datum *next; /* next one in lexicographic order */ MEMORY_MANAGEMENT } extension_census_datum; text_stream *Extensions::Census::ecd_rubric(extension_census_datum *ecd) { return Extensions::get_rubric(ExtensionManager::from_copy(ecd->found_as->copy)); } @ This is a narrative section and describes the story of the census. Just as Caesar Augustus decreed that all the world should be taxed, and that each should return to his place of birth, so we will open and inspect every extension we can find, checking that each is in the right place. Note that if the same extension is found in more than one domain, the first to be found is considered the definitive version: this is why the external area is searched first, so that the user can override built-in extensions by placing his own versions in the external area. (Should this convention ever be reversed, a matching change would need to be made in the code which opens extension files in Read Source Text.) = void Extensions::Census::perform(extension_census *C) { inbuild_requirement *req = Requirements::anything_of_genre(extension_genre); Nests::search_for(req, C->search_list, C->raw_data); inbuild_search_result *R; LOOP_OVER_LINKED_LIST(R, inbuild_search_result, C->raw_data) { C->no_census_errors += LinkedLists::len(R->copy->errors_reading_source_text); int overridden_by_an_extension_already_found = FALSE; @; if (overridden_by_an_extension_already_found == FALSE) @; } } @h Adding the extension to the census, or not. Recall that the higher-priority external domain is scanned first; the built-in domain is scanned second. So if we find that our new extension has the same title and author as one already known, it must be the case that we are now scanning the built-in area and that the previous one was an extension which the user had installed to override this built-in extension. @ = extension_census_datum *other; LOOP_OVER_LINKED_LIST(other, extension_census_datum, C->census_data) if ((Works::match(R->copy->edition->work, other->found_as->copy->edition->work)) && ((other->built_in) || (Nests::get_tag(R->nest) == INTERNAL_NEST_TAG))) { other->overriding_a_built_in_extension = TRUE; overridden_by_an_extension_already_found = TRUE; } @ Assuming the new extension was not overridden in this way, we come here. Because we didn't check the version number text for validity, it might through being invalid be longer than we expect: in case this is so, we truncate it. @ = extension_census_datum *ecd = CREATE(extension_census_datum); ecd->found_as = R; Works::add_to_database(R->copy->edition->work, INSTALLED_WDBC); ecd->built_in = FALSE; if (Nests::get_tag(R->nest) == INTERNAL_NEST_TAG) ecd->built_in = TRUE; ecd->project_specific = FALSE; if (Nests::get_tag(R->nest) == MATERIALS_NEST_TAG) ecd->project_specific = TRUE; ecd->overriding_a_built_in_extension = FALSE; ecd->next = NULL; @ And this is where the inclusion of that material into the catalogue is taken care of. First, we sometimes position a warning prominently at the top of the listing, because otherwise its position at the bottom will be invisible unless the user scrolls a long way: = void Extensions::Census::warn_about_census_errors(OUTPUT_STREAM, extension_census *C) { if (C->no_census_errors == 0) return; /* no need for a warning */ if (NUMBER_CREATED(extension_census_datum) < 20) return; /* it's a short page anyway */ HTML_OPEN("p"); HTML_TAG_WITH("img", "border=0 src=inform:/doc_images/misinstalled.png"); WRITE(" " "Warning. One or more extensions are installed incorrectly: " "see details below."); HTML_CLOSE("p"); } @ = void Extensions::Census::transcribe_census_errors(OUTPUT_STREAM, extension_census *C) { if (C->no_census_errors == 0) return; /* nothing to include, then */ @; inbuild_search_result *R; LOOP_OVER_LINKED_LIST(R, inbuild_search_result, C->raw_data) if (LinkedLists::len(R->copy->errors_reading_source_text) > 0) { copy_error *CE; LOOP_OVER_LINKED_LIST(CE, copy_error, R->copy->errors_reading_source_text) { #ifdef INDEX_MODULE HTMLFiles::open_para(OUT, 2, "hanging"); #endif #ifndef INDEX_MODULE HTML_OPEN("p"); #endif WRITE("%X - ", R->copy->edition->work); CopyErrors::write(OUT, CE); HTML_CLOSE("p"); } } } @ We only want to warn people here: not to stop them from using Inform until they put matters right. (Suppose, for instance, they are using an account not giving them sufficient privileges to modify files in the external extensions area: they'd then be locked out if anything was amiss there.) @ = HTML_TAG("hr"); HTML_OPEN("p"); HTML_TAG_WITH("img", "border=0 align=\"left\" src=inform:/doc_images/census_problem.png"); WRITE("Warning. Inform checks the folder of user-installed extensions " "each time it translates the source text, in order to keep this directory " "page up to date. Each file must be a properly labelled extension (with " "its titling line correctly identifying itself), and must be in the right " "place - e.g. 'Marbles by Daphne Quilt' must have the filename 'Marbles.i7x' " "(or just 'Marbles' with no file extension) and be stored in the folder " "'Daphne Quilt'. The title should be at most %d characters long; the " "author name, %d. At the last check, these rules were not being followed:", MAX_EXTENSION_TITLE_LENGTH, MAX_EXTENSION_AUTHOR_LENGTH); HTML_CLOSE("p"); @ Here we write the copy for the directory page of the extensions documentation: the one which the user currently sees by clicking on the "Installed Extensions" link from the contents page of the documentation. It contains an alphabetised catalogue of extensions by author and then title, along with some useful information about them, and then a list of any oddities found in the external extensions area. @d SORT_CE_BY_TITLE 1 @d SORT_CE_BY_AUTHOR 2 @d SORT_CE_BY_INSTALL 3 @d SORT_CE_BY_DATE 4 @d SORT_CE_BY_LENGTH 5 = void Extensions::Census::write_results(OUTPUT_STREAM, extension_census *C) { @; Extensions::Census::warn_about_census_errors(OUT, C); HTML::end_html_row(OUT); HTML::end_html_table(OUT); HTML_TAG("hr"); @