2019-02-05 02:44:07 +02:00
|
|
|
[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.
|
|
|
|
|
2020-02-04 02:02:34 +02:00
|
|
|
@
|
|
|
|
|
|
|
|
=
|
|
|
|
typedef struct extension_census {
|
|
|
|
struct linked_list *search_list; /* of |inbuild_nest| */
|
2020-02-19 22:48:30 +02:00
|
|
|
struct linked_list *census_data; /* of |extension_census_datum| */
|
|
|
|
struct linked_list *raw_data; /* of |inbuild_search_result| */
|
|
|
|
int no_census_errors;
|
2020-02-04 02:02:34 +02:00
|
|
|
MEMORY_MANAGEMENT
|
|
|
|
} extension_census;
|
|
|
|
|
2020-02-10 12:27:24 +02:00
|
|
|
extension_census *Extensions::Census::new(void) {
|
2020-02-04 02:02:34 +02:00
|
|
|
extension_census *C = CREATE(extension_census);
|
2020-02-13 21:43:24 +02:00
|
|
|
C->search_list = Inbuild::nest_list();
|
2020-02-19 22:48:30 +02:00
|
|
|
C->census_data = NEW_LINKED_LIST(extension_census_datum);
|
|
|
|
C->raw_data = NEW_LINKED_LIST(inbuild_search_result);
|
|
|
|
C->no_census_errors = 0;
|
2020-02-04 02:02:34 +02:00
|
|
|
return C;
|
|
|
|
}
|
|
|
|
|
|
|
|
pathname *Extensions::Census::internal_path(extension_census *C) {
|
|
|
|
inbuild_nest *N = NULL;
|
|
|
|
LOOP_OVER_LINKED_LIST(N, inbuild_nest, C->search_list)
|
2020-02-19 22:48:30 +02:00
|
|
|
if (Nests::get_tag(N) == INTERNAL_NEST_TAG)
|
2020-02-08 12:34:58 +02:00
|
|
|
return ExtensionManager::path_within_nest(N);
|
2020-02-04 02:02:34 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
pathname *Extensions::Census::external_path(extension_census *C) {
|
|
|
|
inbuild_nest *N = NULL;
|
|
|
|
LOOP_OVER_LINKED_LIST(N, inbuild_nest, C->search_list)
|
2020-02-19 22:48:30 +02:00
|
|
|
if (Nests::get_tag(N) == EXTERNAL_NEST_TAG)
|
2020-02-08 12:34:58 +02:00
|
|
|
return ExtensionManager::path_within_nest(N);
|
2020-02-04 02:02:34 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
2019-02-05 02:44:07 +02:00
|
|
|
|
|
|
|
@ In addition to the extensions read in, there are the roads not taken: the
|
2019-03-19 01:36:20 +02:00
|
|
|
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,
|
2019-02-05 02:44:07 +02:00
|
|
|
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 {
|
2020-02-19 22:48:30 +02:00
|
|
|
struct inbuild_search_result *found_as;
|
2019-02-05 02:44:07 +02:00
|
|
|
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;
|
|
|
|
|
2020-02-19 22:48:30 +02:00
|
|
|
text_stream *Extensions::Census::ecd_rubric(extension_census_datum *ecd) {
|
|
|
|
return Extensions::get_rubric(ExtensionManager::from_copy(ecd->found_as->copy));
|
|
|
|
}
|
|
|
|
|
2019-02-05 02:44:07 +02:00
|
|
|
@ 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.)
|
|
|
|
|
|
|
|
=
|
2020-02-04 02:02:34 +02:00
|
|
|
void Extensions::Census::perform(extension_census *C) {
|
2020-02-19 22:48:30 +02:00
|
|
|
inbuild_requirement *req = Requirements::anything_of_genre(extension_genre);
|
|
|
|
Nests::search_for(req, C->search_list, C->raw_data);
|
2020-02-04 02:02:34 +02:00
|
|
|
|
2020-02-19 22:48:30 +02:00
|
|
|
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;
|
|
|
|
@<See if already known from existing data@>;
|
|
|
|
if (overridden_by_an_extension_already_found == FALSE)
|
|
|
|
@<Add to the census data@>;
|
2019-02-05 02:44:07 +02:00
|
|
|
}
|
2020-02-19 22:48:30 +02:00
|
|
|
}
|
2019-02-05 02:44:07 +02:00
|
|
|
|
|
|
|
@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.
|
|
|
|
|
2020-02-19 22:48:30 +02:00
|
|
|
@<See if already known from existing data@> =
|
2019-02-05 02:44:07 +02:00
|
|
|
extension_census_datum *other;
|
2020-02-19 22:48:30 +02:00
|
|
|
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))) {
|
2019-02-05 02:44:07 +02:00
|
|
|
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.
|
|
|
|
|
2020-02-19 22:48:30 +02:00
|
|
|
@<Add to the census data@> =
|
|
|
|
extension_census_datum *ecd = CREATE(extension_census_datum);
|
|
|
|
ecd->found_as = R;
|
|
|
|
Works::add_to_database(R->copy->edition->work, INSTALLED_WDBC);
|
2019-02-05 02:44:07 +02:00
|
|
|
ecd->built_in = FALSE;
|
2020-02-19 22:48:30 +02:00
|
|
|
if (Nests::get_tag(R->nest) == INTERNAL_NEST_TAG) ecd->built_in = TRUE;
|
2019-02-05 02:44:07 +02:00
|
|
|
ecd->project_specific = FALSE;
|
2020-02-19 22:48:30 +02:00
|
|
|
if (Nests::get_tag(R->nest) == MATERIALS_NEST_TAG) ecd->project_specific = TRUE;
|
2019-02-05 02:44:07 +02:00
|
|
|
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:
|
|
|
|
|
|
|
|
=
|
2020-02-19 22:48:30 +02:00
|
|
|
void Extensions::Census::warn_about_census_errors(OUTPUT_STREAM, extension_census *C) {
|
|
|
|
if (C->no_census_errors == 0) return; /* no need for a warning */
|
2019-02-05 02:44:07 +02:00
|
|
|
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(" "
|
|
|
|
"<b>Warning</b>. One or more extensions are installed incorrectly: "
|
|
|
|
"see details below.");
|
|
|
|
HTML_CLOSE("p");
|
|
|
|
}
|
|
|
|
|
|
|
|
@ =
|
2020-02-19 22:48:30 +02:00
|
|
|
void Extensions::Census::transcribe_census_errors(OUTPUT_STREAM, extension_census *C) {
|
|
|
|
if (C->no_census_errors == 0) return; /* nothing to include, then */
|
2019-02-05 02:44:07 +02:00
|
|
|
@<Include the headnote explaining what census errors are@>;
|
2020-02-19 22:48:30 +02:00
|
|
|
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("<b>%X</b> - ", R->copy->edition->work);
|
|
|
|
Copies::write_problem(OUT, CE);
|
|
|
|
HTML_CLOSE("p");
|
|
|
|
}
|
|
|
|
}
|
2019-02-05 02:44:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@ 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.)
|
|
|
|
|
|
|
|
@<Include the headnote explaining what census errors are@> =
|
|
|
|
HTML_TAG("hr");
|
|
|
|
HTML_OPEN("p");
|
|
|
|
HTML_TAG_WITH("img", "border=0 align=\"left\" src=inform:/doc_images/census_problem.png");
|
|
|
|
WRITE("<b>Warning</b>. 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.
|
|
|
|
|
2020-02-19 22:48:30 +02:00
|
|
|
@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
|
2019-02-05 02:44:07 +02:00
|
|
|
|
|
|
|
=
|
2020-02-04 02:02:34 +02:00
|
|
|
void Extensions::Census::write_results(OUTPUT_STREAM, extension_census *C) {
|
2019-02-05 02:44:07 +02:00
|
|
|
@<Display the location of installed extensions@>;
|
2020-02-19 22:48:30 +02:00
|
|
|
Extensions::Census::warn_about_census_errors(OUT, C);
|
2019-02-05 02:44:07 +02:00
|
|
|
HTML::end_html_row(OUT);
|
|
|
|
HTML::end_html_table(OUT);
|
|
|
|
HTML_TAG("hr");
|
|
|
|
@<Time stamp the extensions used on this run@>;
|
|
|
|
|
|
|
|
int key_vms = FALSE, key_override = FALSE, key_builtin = FALSE,
|
|
|
|
key_pspec = FALSE, key_bullet = FALSE;
|
|
|
|
|
|
|
|
@<Display the census radio buttons@>;
|
|
|
|
|
|
|
|
int no_entries = NUMBER_CREATED(extension_census_datum);
|
|
|
|
extension_census_datum **sorted_census_results = Memory::I7_calloc(no_entries,
|
|
|
|
sizeof(extension_census_datum *), EXTENSION_DICTIONARY_MREASON);
|
|
|
|
|
2020-02-19 22:48:30 +02:00
|
|
|
for (int d=1; d<=5; d++) {
|
2019-02-05 02:44:07 +02:00
|
|
|
@<Start an HTML division for this sorted version of the census@>;
|
|
|
|
@<Sort the census into the appropriate order@>;
|
|
|
|
@<Display the sorted version of the census@>;
|
|
|
|
HTML_CLOSE("div");
|
|
|
|
}
|
|
|
|
@<Print the key to any symbols used in the census lines@>;
|
2020-02-19 22:48:30 +02:00
|
|
|
Extensions::Census::transcribe_census_errors(OUT, C);
|
2019-02-05 02:44:07 +02:00
|
|
|
Memory::I7_array_free(sorted_census_results, EXTENSION_DICTIONARY_MREASON,
|
|
|
|
no_entries, sizeof(extension_census_datum *));
|
|
|
|
}
|
|
|
|
|
|
|
|
@<Display the location of installed extensions@> =
|
|
|
|
int nps = 0, nbi = 0, ni = 0;
|
|
|
|
extension_census_datum *ecd;
|
|
|
|
LOOP_OVER(ecd, extension_census_datum) {
|
|
|
|
if (ecd->project_specific) nps++;
|
|
|
|
else if (ecd->built_in) nbi++;
|
|
|
|
else ni++;
|
|
|
|
}
|
|
|
|
|
|
|
|
HTML_OPEN("p");
|
|
|
|
HTML_TAG_WITH("img", "src='inform:/doc_images/builtin_ext.png' border=0");
|
|
|
|
WRITE(" You have "
|
|
|
|
"%d extensions built-in to this copy of Inform, marked with a grey folder "
|
|
|
|
"icon in the catalogue below.",
|
|
|
|
nbi);
|
|
|
|
HTML_CLOSE("p");
|
|
|
|
HTML_OPEN("p");
|
|
|
|
if (ni == 0) {
|
|
|
|
HTML_TAG_WITH("img", "src='inform:/doc_images/folder4.png' border=0");
|
|
|
|
WRITE(" You have no other extensions installed at present.");
|
|
|
|
} else {
|
2020-02-04 02:02:34 +02:00
|
|
|
#ifdef INDEX_MODULE
|
|
|
|
HTML::Javascript::open_file(OUT, Extensions::Census::external_path(C), NULL,
|
2019-02-05 02:44:07 +02:00
|
|
|
"src='inform:/doc_images/folder4.png' border=0");
|
2020-02-04 02:02:34 +02:00
|
|
|
#endif
|
2019-02-05 02:44:07 +02:00
|
|
|
WRITE(" You have %d further extension%s installed. These are marked "
|
|
|
|
"with a blue folder icon in the catalogue below. (Click it to see "
|
|
|
|
"where the file is stored on your computer.) "
|
|
|
|
"For more extensions, visit <b>www.inform7.com</b>.",
|
|
|
|
ni, (ni==1)?"":"s");
|
|
|
|
}
|
|
|
|
HTML_CLOSE("p");
|
|
|
|
if (nps > 0) {
|
|
|
|
HTML_OPEN("p");
|
2020-02-04 02:02:34 +02:00
|
|
|
#ifdef INDEX_MODULE
|
|
|
|
HTML::Javascript::open_file(OUT, Extensions::Census::internal_path(C), NULL, PROJECT_SPECIFIC_SYMBOL);
|
|
|
|
#endif
|
2019-02-05 02:44:07 +02:00
|
|
|
WRITE(" You have %d extension%s in the .materials folder for the "
|
|
|
|
"current project. (Click the purple folder icon to show the "
|
|
|
|
"location.) %s not available to other projects.",
|
|
|
|
nps, (nps==1)?"":"s", (nps==1)?"This is":"These are");
|
|
|
|
HTML_CLOSE("p");
|
|
|
|
}
|
|
|
|
|
|
|
|
@ This simply ensures that dates used are updated to today's date for
|
|
|
|
extensions used in the current run; otherwise they wouldn't show in the
|
|
|
|
documentation as used today until the next run, for obscure timing reasons.
|
|
|
|
|
|
|
|
@<Time stamp the extensions used on this run@> =
|
2020-02-04 02:02:34 +02:00
|
|
|
#ifdef CORE_MODULE
|
|
|
|
inform_extension *E;
|
|
|
|
LOOP_OVER(E, inform_extension)
|
|
|
|
Extensions::Dictionary::time_stamp(E);
|
|
|
|
#endif
|
2019-02-05 02:44:07 +02:00
|
|
|
|
|
|
|
@ I am the first to admit that this implementation is not inspired. There
|
|
|
|
are five radio buttons, and number 2 is selected by default.
|
|
|
|
|
|
|
|
@<Display the census radio buttons@> =
|
|
|
|
HTML_OPEN("p");
|
|
|
|
WRITE("Sort catalogue: ");
|
|
|
|
HTML_OPEN_WITH("a",
|
|
|
|
"href=\"#\" style=\"text-decoration: none\" "
|
|
|
|
"onclick=\"openExtra('disp1', 'plus1'); closeExtra('disp2', 'plus2'); "
|
|
|
|
"closeExtra('disp3', 'plus3'); closeExtra('disp4', 'plus4'); "
|
|
|
|
"closeExtra('disp5', 'plus5'); return false;\"");
|
|
|
|
HTML_TAG_WITH("img", "border=0 id=\"plus1\" src=inform:/doc_images/extrarboff.png");
|
|
|
|
WRITE(" By title");
|
|
|
|
HTML_CLOSE("a");
|
|
|
|
WRITE(" | ");
|
|
|
|
HTML_OPEN_WITH("a",
|
|
|
|
"href=\"#\" style=\"text-decoration: none\" "
|
|
|
|
"onclick=\"closeExtra('disp1', 'plus1'); openExtra('disp2', 'plus2'); "
|
|
|
|
"closeExtra('disp3', 'plus3'); closeExtra('disp4', 'plus4'); "
|
|
|
|
"closeExtra('disp5', 'plus5'); return false;\"");
|
|
|
|
HTML_TAG_WITH("img", "border=0 id=\"plus2\" src=inform:/doc_images/extrarbon.png");
|
|
|
|
WRITE(" By author");
|
|
|
|
HTML_CLOSE("a");
|
|
|
|
WRITE(" | ");
|
|
|
|
HTML_OPEN_WITH("a",
|
|
|
|
"href=\"#\" style=\"text-decoration: none\" "
|
|
|
|
"onclick=\"closeExtra('disp1', 'plus1'); closeExtra('disp2', 'plus2'); "
|
|
|
|
"openExtra('disp3', 'plus3'); closeExtra('disp4', 'plus4'); "
|
|
|
|
"closeExtra('disp5', 'plus5'); return false;\"");
|
|
|
|
HTML_TAG_WITH("img", "border=0 id=\"plus3\" src=inform:/doc_images/extrarboff.png");
|
|
|
|
WRITE(" By installation");
|
|
|
|
HTML_CLOSE("a");
|
|
|
|
WRITE(" | ");
|
|
|
|
HTML_OPEN_WITH("a",
|
|
|
|
"href=\"#\" style=\"text-decoration: none\" "
|
|
|
|
"onclick=\"closeExtra('disp1', 'plus1'); closeExtra('disp2', 'plus2'); "
|
|
|
|
"closeExtra('disp3', 'plus3'); openExtra('disp4', 'plus4'); "
|
|
|
|
"closeExtra('disp5', 'plus5'); return false;\"");
|
|
|
|
HTML_TAG_WITH("img", "border=0 id=\"plus4\" src=inform:/doc_images/extrarboff.png");
|
|
|
|
WRITE(" By date used");
|
|
|
|
HTML_CLOSE("a");
|
|
|
|
WRITE(" | ");
|
|
|
|
HTML_OPEN_WITH("a",
|
|
|
|
"href=\"#\" style=\"text-decoration: none\" "
|
|
|
|
"onclick=\"closeExtra('disp1', 'plus1'); closeExtra('disp2', 'plus2'); "
|
|
|
|
"closeExtra('disp3', 'plus3'); closeExtra('disp4', 'plus4'); "
|
|
|
|
"openExtra('disp5', 'plus5'); return false;\"");
|
|
|
|
HTML_TAG_WITH("img", "border=0 id=\"plus5\" src=inform:/doc_images/extrarboff.png");
|
|
|
|
WRITE(" By word count");
|
|
|
|
HTML_CLOSE("a");
|
|
|
|
HTML_CLOSE("p");
|
|
|
|
|
|
|
|
@ Consequently, of the five divisions, number 2 is shown and the others
|
|
|
|
hidden, by default.
|
|
|
|
|
|
|
|
@<Start an HTML division for this sorted version of the census@> =
|
|
|
|
char *display = "none";
|
2020-02-19 22:48:30 +02:00
|
|
|
if (d == SORT_CE_BY_AUTHOR) display = "block";
|
2019-02-05 02:44:07 +02:00
|
|
|
HTML_OPEN_WITH("div", "id=\"disp%d\" style=\"display: %s;\"", d, display);
|
|
|
|
|
|
|
|
@ The key at the foot only explicates those symbols actually used, and
|
|
|
|
doesn't explicate the "unindexed" symbol at all, since that's actually
|
|
|
|
just a blank image used for horizontal spacing to keep margins straight.
|
|
|
|
|
|
|
|
@<Print the key to any symbols used in the census lines@> =
|
|
|
|
if ((key_builtin) || (key_override) || (key_bullet) || (key_vms) || (key_pspec)) {
|
|
|
|
HTML_OPEN("p");
|
|
|
|
WRITE("Key: ");
|
|
|
|
if (key_bullet) {
|
|
|
|
HTML_TAG_WITH("img", "%s", INDEXED_SYMBOL);
|
|
|
|
WRITE(" Used ");
|
|
|
|
}
|
|
|
|
if (key_builtin) {
|
|
|
|
HTML_TAG_WITH("img", "%s", BUILT_IN_SYMBOL);
|
|
|
|
WRITE(" Built in ");
|
|
|
|
}
|
|
|
|
if (key_pspec) {
|
|
|
|
HTML_TAG_WITH("img", "%s", PROJECT_SPECIFIC_SYMBOL);
|
|
|
|
WRITE(" Project specific ");
|
|
|
|
}
|
|
|
|
if (key_override) {
|
|
|
|
HTML_TAG_WITH("img", "%s", OVERRIDING_SYMBOL);
|
|
|
|
WRITE(" Your version overrides the one built in ");
|
|
|
|
}
|
|
|
|
if (key_vms) {
|
2020-02-04 02:02:34 +02:00
|
|
|
#ifdef CORE_MODULE
|
2019-02-05 02:44:07 +02:00
|
|
|
HTML_TAG("br");
|
|
|
|
VirtualMachines::write_key(OUT);
|
2020-02-04 02:02:34 +02:00
|
|
|
#endif
|
2019-02-05 02:44:07 +02:00
|
|
|
}
|
|
|
|
HTML_CLOSE("p");
|
|
|
|
}
|
|
|
|
|
|
|
|
@<Sort the census into the appropriate order@> =
|
|
|
|
int i = 0;
|
|
|
|
extension_census_datum *ecd;
|
|
|
|
LOOP_OVER(ecd, extension_census_datum)
|
|
|
|
sorted_census_results[i++] = ecd;
|
|
|
|
int (*criterion)(const void *, const void *) = NULL;
|
|
|
|
switch (d) {
|
2020-02-19 22:48:30 +02:00
|
|
|
case SORT_CE_BY_TITLE: criterion = Extensions::Census::compare_ecd_by_title; break;
|
|
|
|
case SORT_CE_BY_AUTHOR: criterion = Extensions::Census::compare_ecd_by_author; break;
|
|
|
|
case SORT_CE_BY_INSTALL: criterion = Extensions::Census::compare_ecd_by_installation; break;
|
|
|
|
case SORT_CE_BY_DATE: criterion = Extensions::Census::compare_ecd_by_date; break;
|
|
|
|
case SORT_CE_BY_LENGTH: criterion = Extensions::Census::compare_ecd_by_length; break;
|
2019-02-05 02:44:07 +02:00
|
|
|
default: internal_error("no such sorting criterion");
|
|
|
|
}
|
|
|
|
qsort(sorted_census_results, (size_t) no_entries, sizeof(extension_census_datum *),
|
|
|
|
criterion);
|
|
|
|
|
|
|
|
@ Standard rows have black text on striped background colours, these being
|
|
|
|
the usual ones seen in Mac OS X applications such as iTunes.
|
|
|
|
|
|
|
|
@d FIRST_STRIPE_COLOUR "#ffffff"
|
|
|
|
@d SECOND_STRIPE_COLOUR "#f3f6fa"
|
|
|
|
|
|
|
|
@<Display the sorted version of the census@> =
|
|
|
|
HTML::begin_html_table(OUT, FIRST_STRIPE_COLOUR, TRUE, 0, 0, 2, 0, 0);
|
|
|
|
@<Show a titling row explaining the census sorting, if necessary@>;
|
|
|
|
int stripe = 0;
|
|
|
|
TEMPORARY_TEXT(current_author_name);
|
|
|
|
int i, current_installation = -1;
|
|
|
|
for (i=0; i<no_entries; i++) {
|
|
|
|
extension_census_datum *ecd = sorted_census_results[i];
|
|
|
|
@<Insert a subtitling row in the census sorting, if necessary@>;
|
|
|
|
stripe = 1 - stripe;
|
|
|
|
if (stripe == 0)
|
|
|
|
HTML::first_html_column_coloured(OUT, 0, SECOND_STRIPE_COLOUR, 0);
|
|
|
|
else
|
|
|
|
HTML::first_html_column_coloured(OUT, 0, FIRST_STRIPE_COLOUR, 0);
|
|
|
|
@<Print the census line for this extension@>;
|
|
|
|
HTML::end_html_row(OUT);
|
|
|
|
}
|
|
|
|
DISCARD_TEXT(current_author_name);
|
|
|
|
@<Show a final titling row closing the census sorting@>;
|
|
|
|
HTML::end_html_table(OUT);
|
|
|
|
|
|
|
|
@<Show a titling row explaining the census sorting, if necessary@> =
|
|
|
|
switch (d) {
|
2020-02-19 22:48:30 +02:00
|
|
|
case SORT_CE_BY_TITLE:
|
2019-02-05 02:44:07 +02:00
|
|
|
@<Begin a tinted census line@>;
|
|
|
|
WRITE("Extensions in alphabetical order");
|
|
|
|
@<End a tinted census line@>;
|
|
|
|
break;
|
2020-02-19 22:48:30 +02:00
|
|
|
case SORT_CE_BY_DATE:
|
2019-02-05 02:44:07 +02:00
|
|
|
@<Begin a tinted census line@>;
|
|
|
|
WRITE("Extensions in order of date used (most recent first)");
|
|
|
|
@<End a tinted census line@>;
|
|
|
|
break;
|
2020-02-19 22:48:30 +02:00
|
|
|
case SORT_CE_BY_LENGTH:
|
2019-02-05 02:44:07 +02:00
|
|
|
@<Begin a tinted census line@>;
|
|
|
|
WRITE("Extensions in order of word count (longest first)");
|
|
|
|
@<End a tinted census line@>;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
@<Insert a subtitling row in the census sorting, if necessary@> =
|
2020-02-19 22:48:30 +02:00
|
|
|
if ((d == SORT_CE_BY_AUTHOR) &&
|
|
|
|
(Str::ne(current_author_name, ecd->found_as->copy->edition->work->author_name))) {
|
|
|
|
Str::copy(current_author_name, ecd->found_as->copy->edition->work->author_name);
|
2019-02-05 02:44:07 +02:00
|
|
|
@<Begin a tinted census line@>;
|
|
|
|
@<Print the author's line in the extension census table@>;
|
|
|
|
@<End a tinted census line@>;
|
|
|
|
stripe = 0;
|
|
|
|
}
|
2020-02-19 22:48:30 +02:00
|
|
|
if ((d == SORT_CE_BY_INSTALL) && (Extensions::Census::installation_region(ecd) != current_installation)) {
|
2019-02-05 02:44:07 +02:00
|
|
|
current_installation = Extensions::Census::installation_region(ecd);
|
|
|
|
@<Begin a tinted census line@>;
|
|
|
|
@<Print the installation region in the extension census table@>;
|
|
|
|
@<End a tinted census line@>;
|
|
|
|
stripe = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
@<Show a final titling row closing the census sorting@> =
|
|
|
|
@<Begin a tinted census line@>;
|
|
|
|
HTML_OPEN_WITH("span", "class=\"smaller\"");
|
|
|
|
WRITE("%d extensions installed", no_entries);
|
|
|
|
HTML_CLOSE("span");
|
|
|
|
@<End a tinted census line@>;
|
|
|
|
|
|
|
|
@ Black text on a grey background.
|
|
|
|
|
|
|
|
@d CENSUS_TITLING_BG "#808080"
|
|
|
|
|
|
|
|
@<Begin a tinted census line@> =
|
|
|
|
int span = 4;
|
2020-02-19 22:48:30 +02:00
|
|
|
if (d == SORT_CE_BY_TITLE) span = 3;
|
2019-02-05 02:44:07 +02:00
|
|
|
HTML::first_html_column_coloured(OUT, 0, CENSUS_TITLING_BG, span);
|
|
|
|
HTML::begin_colour(OUT, I"ffffff");
|
|
|
|
WRITE(" ");
|
|
|
|
|
|
|
|
@<End a tinted census line@> =
|
|
|
|
HTML::end_colour(OUT);
|
|
|
|
HTML::end_html_row(OUT);
|
|
|
|
|
|
|
|
@ Used only in "by author".
|
|
|
|
|
|
|
|
@<Print the author's line in the extension census table@> =
|
2020-02-19 22:48:30 +02:00
|
|
|
WRITE("%S", ecd->found_as->copy->edition->work->raw_author_name);
|
2019-02-05 02:44:07 +02:00
|
|
|
|
|
|
|
extension_census_datum *ecd2;
|
|
|
|
int cu = 0, cn = 0, j;
|
|
|
|
for (j = i; j < no_entries; j++) {
|
|
|
|
ecd2 = sorted_census_results[j];
|
2020-02-19 22:48:30 +02:00
|
|
|
if (Str::ne(current_author_name, ecd2->found_as->copy->edition->work->author_name)) break;
|
2019-02-05 02:44:07 +02:00
|
|
|
if (Extensions::Census::ecd_used(ecd2)) cu++;
|
|
|
|
else cn++;
|
|
|
|
}
|
|
|
|
WRITE(" ");
|
|
|
|
HTML_OPEN_WITH("span", "class=\"smaller\"");
|
|
|
|
WRITE("%d extension%s", cu+cn, (cu+cn==1)?"":"s");
|
|
|
|
if ((cu == 0) && (cn == 1)) WRITE(", unused");
|
|
|
|
else if ((cu == 0) && (cn == 2)) WRITE(", both unused");
|
|
|
|
else if ((cu == 0) && (cn > 2)) WRITE(", all unused");
|
|
|
|
else if ((cn == 0) && (cu == 1)) WRITE(", used");
|
|
|
|
else if ((cn == 0) && (cu == 2)) WRITE(", both used");
|
|
|
|
else if ((cn == 0) && (cu > 2)) WRITE(", all used");
|
|
|
|
else if (cn+cu > 0) WRITE(", %d used, %d unused", cu, cn);
|
|
|
|
WRITE(")");
|
|
|
|
HTML_CLOSE("span");
|
|
|
|
|
|
|
|
@ Used only in "by installation".
|
|
|
|
|
|
|
|
@<Print the installation region in the extension census table@> =
|
|
|
|
switch (current_installation) {
|
|
|
|
case 0:
|
|
|
|
WRITE("Supplied in the .materials folder ");
|
|
|
|
HTML_OPEN_WITH("span", "class=\"smaller\"");
|
2020-02-04 02:02:34 +02:00
|
|
|
WRITE("%p", Extensions::Census::internal_path(C));
|
2019-02-05 02:44:07 +02:00
|
|
|
HTML_CLOSE("span"); break;
|
|
|
|
case 1: WRITE("Built in to Inform"); break;
|
|
|
|
case 2: WRITE("User installed but overriding a built-in extension"); break;
|
|
|
|
case 3:
|
|
|
|
WRITE("User installed ");
|
|
|
|
HTML_OPEN_WITH("span", "class=\"smaller\"");
|
2020-02-04 02:02:34 +02:00
|
|
|
WRITE("%p", Extensions::Census::external_path(C));
|
2019-02-05 02:44:07 +02:00
|
|
|
HTML_CLOSE("span"); break;
|
|
|
|
}
|
|
|
|
|
|
|
|
@
|
|
|
|
|
|
|
|
@d UNINDEXED_SYMBOL "border=\"0\" src=\"inform:/doc_images/unindexed_bullet.png\""
|
|
|
|
@d INDEXED_SYMBOL "border=\"0\" src=\"inform:/doc_images/indexed_bullet.png\""
|
|
|
|
|
|
|
|
@<Print the census line for this extension@> =
|
|
|
|
@<Print column 1 of the census line@>;
|
|
|
|
HTML::next_html_column_nw(OUT, 0);
|
2020-02-19 22:48:30 +02:00
|
|
|
if (d != SORT_CE_BY_TITLE) {
|
2019-02-05 02:44:07 +02:00
|
|
|
@<Print column 2 of the census line@>;
|
|
|
|
HTML::next_html_column_nw(OUT, 0);
|
|
|
|
}
|
|
|
|
@<Print column 3 of the census line@>;
|
|
|
|
HTML::next_html_column_w(OUT, 0);
|
|
|
|
@<Print column 4 of the census line@>;
|
|
|
|
|
|
|
|
@ The appearance of the line is
|
|
|
|
|
|
|
|
>> (bullet) The Title (by The Author) (VM requirement icons)
|
|
|
|
|
|
|
|
where all is optional except the title part.
|
|
|
|
|
|
|
|
@<Print column 1 of the census line@> =
|
|
|
|
char *bulletornot = UNINDEXED_SYMBOL;
|
|
|
|
if (Extensions::Census::ecd_used(ecd)) { bulletornot = INDEXED_SYMBOL; key_bullet = TRUE; }
|
|
|
|
WRITE(" ");
|
|
|
|
HTML_TAG_WITH("img", "%s", bulletornot);
|
|
|
|
|
2020-02-19 22:48:30 +02:00
|
|
|
Works::begin_extension_link(OUT, ecd->found_as->copy->edition->work, Extensions::Census::ecd_rubric(ecd));
|
|
|
|
if (d != SORT_CE_BY_AUTHOR) {
|
2019-02-05 02:44:07 +02:00
|
|
|
HTML::begin_colour(OUT, I"404040");
|
2020-02-19 22:48:30 +02:00
|
|
|
WRITE("%S", ecd->found_as->copy->edition->work->raw_title);
|
|
|
|
if (Str::len(ecd->found_as->copy->edition->work->raw_title) + Str::len(ecd->found_as->copy->edition->work->raw_author_name) > 45) {
|
2019-02-05 02:44:07 +02:00
|
|
|
HTML_TAG("br");
|
|
|
|
WRITE(" ");
|
|
|
|
} else
|
|
|
|
WRITE(" ");
|
2020-02-19 22:48:30 +02:00
|
|
|
WRITE("by %S", ecd->found_as->copy->edition->work->raw_author_name);
|
2019-02-05 02:44:07 +02:00
|
|
|
HTML::end_colour(OUT);
|
|
|
|
} else {
|
|
|
|
HTML::begin_colour(OUT, I"404040");
|
2020-02-19 22:48:30 +02:00
|
|
|
WRITE("%S", ecd->found_as->copy->edition->work->raw_title);
|
2019-02-05 02:44:07 +02:00
|
|
|
HTML::end_colour(OUT);
|
|
|
|
}
|
2020-02-19 22:48:30 +02:00
|
|
|
Works::end_extension_link(OUT, ecd->found_as->copy->edition->work);
|
2019-02-05 02:44:07 +02:00
|
|
|
|
2020-02-20 02:39:36 +02:00
|
|
|
compatibility_specification *C = ecd->found_as->copy->edition->compatibility;
|
|
|
|
if (Str::len(C->parsed_from) > 0) {
|
2019-02-05 02:44:07 +02:00
|
|
|
@<Append icons which signify the VM requirements of the extension@>;
|
|
|
|
key_vms = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
@ VM requirements are parsed by feeding them into the lexer and calling the
|
|
|
|
same routines as would be used when parsing headings about VM requirements
|
2019-03-19 01:36:20 +02:00
|
|
|
in a normal run of Inform. Note that because the requirements are in round
|
2019-02-05 02:44:07 +02:00
|
|
|
brackets, which the lexer will split off as distinct words, we can ignore
|
|
|
|
the first and last word and just look at what is in between:
|
|
|
|
|
|
|
|
@<Append icons which signify the VM requirements of the extension@> =
|
2020-02-20 02:39:36 +02:00
|
|
|
WRITE(" %S", C->parsed_from);
|
2020-02-04 02:02:34 +02:00
|
|
|
#ifdef CORE_MODULE
|
2020-02-20 02:39:36 +02:00
|
|
|
VirtualMachines::write_icons(OUT, C);
|
2020-02-04 02:02:34 +02:00
|
|
|
#endif
|
2019-02-05 02:44:07 +02:00
|
|
|
|
|
|
|
@<Print column 2 of the census line@> =
|
|
|
|
HTML_OPEN_WITH("span", "class=\"smaller\"");
|
2020-02-19 22:48:30 +02:00
|
|
|
if (VersionNumbers::is_null(ecd->found_as->copy->edition->version) == FALSE)
|
|
|
|
WRITE("v %v", &(ecd->found_as->copy->edition->version));
|
2019-02-05 02:44:07 +02:00
|
|
|
else
|
|
|
|
WRITE("--");
|
|
|
|
HTML_CLOSE("span");
|
|
|
|
|
|
|
|
@
|
|
|
|
|
|
|
|
@d BUILT_IN_SYMBOL "border=\"0\" src=\"inform:/doc_images/builtin_ext.png\""
|
|
|
|
@d OVERRIDING_SYMBOL "border=\"0\" src=\"inform:/doc_images/override_ext.png\""
|
|
|
|
@d PROJECT_SPECIFIC_SYMBOL "border=\"0\" src=\"inform:/doc_images/pspec_ext.png\""
|
|
|
|
|
|
|
|
@<Print column 3 of the census line@> =
|
|
|
|
char *opener = "src='inform:/doc_images/folder4.png' border=0";
|
|
|
|
if (ecd->built_in) { opener = BUILT_IN_SYMBOL; key_builtin = TRUE; }
|
|
|
|
if (ecd->overriding_a_built_in_extension) {
|
|
|
|
opener = OVERRIDING_SYMBOL; key_override = TRUE;
|
|
|
|
}
|
|
|
|
if (ecd->project_specific) {
|
|
|
|
opener = PROJECT_SPECIFIC_SYMBOL; key_pspec = TRUE;
|
|
|
|
}
|
|
|
|
if (ecd->built_in) HTML_TAG_WITH("img", "%s", opener)
|
2020-02-04 02:02:34 +02:00
|
|
|
else {
|
|
|
|
#ifdef INDEX_MODULE
|
2020-02-19 22:48:30 +02:00
|
|
|
pathname *area = ExtensionManager::path_within_nest(ecd->found_as->nest);
|
|
|
|
HTML::Javascript::open_file(OUT, area, ecd->found_as->copy->edition->work->raw_author_name, opener);
|
2020-02-04 02:02:34 +02:00
|
|
|
#endif
|
|
|
|
}
|
2019-02-05 02:44:07 +02:00
|
|
|
|
|
|
|
@<Print column 4 of the census line@> =
|
|
|
|
HTML_OPEN_WITH("span", "class=\"smaller\"");
|
2020-02-19 22:48:30 +02:00
|
|
|
if ((d == SORT_CE_BY_DATE) || (d == SORT_CE_BY_INSTALL)) {
|
|
|
|
WRITE("%S", Works::get_usage_date(ecd->found_as->copy->edition->work));
|
|
|
|
} else if (d == SORT_CE_BY_LENGTH) {
|
|
|
|
if (Works::forgot(ecd->found_as->copy->edition->work))
|
2019-02-05 02:44:07 +02:00
|
|
|
WRITE("I did read this, but forgot");
|
2020-02-19 22:48:30 +02:00
|
|
|
else if (Works::never(ecd->found_as->copy->edition->work))
|
2019-02-05 02:44:07 +02:00
|
|
|
WRITE("I've never read this");
|
|
|
|
else
|
2020-02-19 22:48:30 +02:00
|
|
|
WRITE("%d words", Works::get_word_count(ecd->found_as->copy->edition->work));
|
2019-02-05 02:44:07 +02:00
|
|
|
} else {
|
2020-02-19 22:48:30 +02:00
|
|
|
if (Str::len(Extensions::Census::ecd_rubric(ecd)) > 0)
|
|
|
|
WRITE("%S", Extensions::Census::ecd_rubric(ecd));
|
2019-02-05 02:44:07 +02:00
|
|
|
else
|
|
|
|
WRITE("--");
|
|
|
|
}
|
|
|
|
HTML_CLOSE("span");
|
|
|
|
|
|
|
|
@ Two useful measurements:
|
|
|
|
|
|
|
|
=
|
|
|
|
int Extensions::Census::installation_region(extension_census_datum *ecd) {
|
|
|
|
if (ecd->project_specific) return 0;
|
|
|
|
if (ecd->built_in) return 1;
|
|
|
|
if (ecd->overriding_a_built_in_extension) return 2;
|
|
|
|
return 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Extensions::Census::ecd_used(extension_census_datum *ecd) {
|
2020-02-19 22:48:30 +02:00
|
|
|
if ((Works::no_times_used_in_context(ecd->found_as->copy->edition->work, LOADED_WDBC) > 0) ||
|
|
|
|
(Works::no_times_used_in_context(ecd->found_as->copy->edition->work, DICTIONARY_REFERRED_WDBC) > 0))
|
2019-02-05 02:44:07 +02:00
|
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
@ The following give the sorting criteria:
|
|
|
|
|
|
|
|
=
|
|
|
|
int Extensions::Census::compare_ecd_by_title(const void *ecd1, const void *ecd2) {
|
|
|
|
extension_census_datum *e1 = *((extension_census_datum **) ecd1);
|
|
|
|
extension_census_datum *e2 = *((extension_census_datum **) ecd2);
|
2020-02-19 22:48:30 +02:00
|
|
|
return Works::compare_by_title(e1->found_as->copy->edition->work, e2->found_as->copy->edition->work);
|
2019-02-05 02:44:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int Extensions::Census::compare_ecd_by_author(const void *ecd1, const void *ecd2) {
|
|
|
|
extension_census_datum *e1 = *((extension_census_datum **) ecd1);
|
|
|
|
extension_census_datum *e2 = *((extension_census_datum **) ecd2);
|
2020-02-19 22:48:30 +02:00
|
|
|
return Works::compare(e1->found_as->copy->edition->work, e2->found_as->copy->edition->work);
|
2019-02-05 02:44:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int Extensions::Census::compare_ecd_by_installation(const void *ecd1, const void *ecd2) {
|
|
|
|
extension_census_datum *e1 = *((extension_census_datum **) ecd1);
|
|
|
|
extension_census_datum *e2 = *((extension_census_datum **) ecd2);
|
|
|
|
int d = Extensions::Census::installation_region(e1) - Extensions::Census::installation_region(e2);
|
|
|
|
if (d != 0) return d;
|
2020-02-19 22:48:30 +02:00
|
|
|
return Works::compare_by_title(e1->found_as->copy->edition->work, e2->found_as->copy->edition->work);
|
2019-02-05 02:44:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int Extensions::Census::compare_ecd_by_date(const void *ecd1, const void *ecd2) {
|
|
|
|
extension_census_datum *e1 = *((extension_census_datum **) ecd1);
|
|
|
|
extension_census_datum *e2 = *((extension_census_datum **) ecd2);
|
2020-02-19 22:48:30 +02:00
|
|
|
return Works::compare_by_date(e1->found_as->copy->edition->work, e2->found_as->copy->edition->work);
|
2019-02-05 02:44:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int Extensions::Census::compare_ecd_by_length(const void *ecd1, const void *ecd2) {
|
|
|
|
extension_census_datum *e1 = *((extension_census_datum **) ecd1);
|
|
|
|
extension_census_datum *e2 = *((extension_census_datum **) ecd2);
|
2020-02-19 22:48:30 +02:00
|
|
|
return Works::compare_by_length(e1->found_as->copy->edition->work, e2->found_as->copy->edition->work);
|
2019-02-05 02:44:07 +02:00
|
|
|
}
|