1
0
Fork 0
mirror of https://github.com/ganelson/inform.git synced 2024-06-26 04:00:43 +03:00

Moved extension census and scanning to inbuild

This commit is contained in:
Graham Nelson 2020-02-04 00:02:34 +00:00
parent accbe7e16c
commit ace0230321
19 changed files with 447 additions and 244 deletions

View file

@ -27,6 +27,7 @@ inbuild_nest *destination_nest = NULL;
int main(int argc, char **argv) {
Foundation::start();
WordsModule::start();
InbuildModule::start();
targets = NEW_LINKED_LIST(inbuild_copy);
nest_list = NEW_LINKED_LIST(inbuild_nest);
@ -67,6 +68,7 @@ int main(int argc, char **argv) {
case SYNC_TO_TTASK: if (destination_nest) Nests::copy_to(C, destination_nest, TRUE); break;
}
}
WordsModule::end();
InbuildModule::end();
Foundation::end();
return 0;
@ -163,3 +165,58 @@ void Main::load_one(text_stream *arg, int throwing_error) {
}
ADD_TO_LINKED_LIST(C, inbuild_copy, targets);
}
@ Since we want to include the words module, we have to define the following
structure and initialiser:
@d VOCABULARY_MEANING_INITIALISER Main::ignore
=
typedef struct vocabulary_meaning {
int enigmatic_number;
} vocabulary_meaning;
@
@d LEXER_PROBLEM_HANDLER Main::lexer_problem_handler
=
vocabulary_meaning Main::ignore(vocabulary_entry *ve) {
vocabulary_meaning vm;
vm.enigmatic_number = 16339;
return vm;
}
void Main::lexer_problem_handler(int err, text_stream *problem_source_description, wchar_t *word) {
if (err == MEMORY_OUT_LEXERERROR)
Errors::fatal("Out of memory: unable to create lexer workspace");
TEMPORARY_TEXT(word_t);
if (word) WRITE_TO(word_t, "%w", word);
switch (err) {
case STRING_TOO_LONG_LEXERERROR:
Errors::with_text("Too much text in quotation marks: %S", word_t);
break;
case WORD_TOO_LONG_LEXERERROR:
Errors::with_text("Word too long: %S", word_t);
break;
case I6_TOO_LONG_LEXERERROR:
Errors::with_text("I6 inclusion too long: %S", word_t);
break;
case STRING_NEVER_ENDS_LEXERERROR:
Errors::with_text("Quoted text never ends: %S", problem_source_description);
break;
case COMMENT_NEVER_ENDS_LEXERERROR:
Errors::with_text("Square-bracketed text never ends: %S", problem_source_description);
break;
case I6_NEVER_ENDS_LEXERERROR:
Errors::with_text("I6 inclusion text never ends: %S", problem_source_description);
break;
default:
internal_error("unknown lexer error");
}
DISCARD_TEXT(word_t);
}
@
@d PREFORM_LANGUAGE_TYPE void

View file

@ -7,6 +7,7 @@ Version Number: 1
Version Name: Avignon
Import: foundation
Import: inform7/words
Import: inbuild
Preliminaries

View file

@ -24,6 +24,8 @@ Setting up the use of this module.
@e inbuild_nest_MT
@e inbuild_search_result_MT
@e inbuild_work_database_entry_array_MT
@e extension_census_datum_MT
@e extension_census_MT
=
ALLOCATE_INDIVIDUALLY(inform_kit)
@ -41,6 +43,8 @@ ALLOCATE_INDIVIDUALLY(build_script)
ALLOCATE_INDIVIDUALLY(build_step)
ALLOCATE_INDIVIDUALLY(inbuild_nest)
ALLOCATE_INDIVIDUALLY(inbuild_search_result)
ALLOCATE_INDIVIDUALLY(extension_census_datum)
ALLOCATE_INDIVIDUALLY(extension_census)
ALLOCATE_IN_ARRAYS(inbuild_work_database_entry, 100)
@ -59,8 +63,10 @@ void InbuildModule::start(void) {
@
@e EXTENSION_DICTIONARY_MREASON
@<Register this module's memory allocation reasons@> =
;
Memory::reason_name(EXTENSION_DICTIONARY_MREASON, "extension dictionary");
@<Register this module's stream writers@> =
Writers::register_writer('v', &VersionNumbers::writer);
@ -68,8 +74,10 @@ void InbuildModule::start(void) {
@
@e EXTENSIONS_CENSUS_DA
@<Register this module's debugging log aspects@> =
;
Log::declare_aspect(EXTENSIONS_CENSUS_DA, L"extensions census", FALSE, FALSE);
@<Register this module's debugging log writers@> =
;

View file

@ -158,6 +158,7 @@ inbuild_copy *Model::claim(text_stream *arg) {
}
inbuild_copy *C = NULL;
if (C == NULL) C = Kits::claim(arg, ext, directory_status);
if (C == NULL) C = Extensions::claim(arg, ext, directory_status);
DISCARD_TEXT(ext);
return C;
}

View file

@ -8,6 +8,7 @@ Nests are repositories of Inform-related resources.
typedef struct inbuild_nest {
struct pathname *location;
int read_only;
int tag_value;
MEMORY_MANAGEMENT
} inbuild_nest;
@ -22,9 +23,20 @@ inbuild_nest *Nests::new(pathname *P) {
inbuild_nest *N = CREATE(inbuild_nest);
N->location = P;
N->read_only = FALSE;
N->tag_value = -1;
return N;
}
int Nests::get_tag(inbuild_nest *N) {
if (N == NULL) return -1;
return N->tag_value;
}
void Nests::set_tag(inbuild_nest *N, int t) {
if (N == NULL) internal_error("no nest");
N->tag_value = t;
}
void Nests::protect(inbuild_nest *N) {
N->read_only = TRUE;
}

View file

@ -3,7 +3,41 @@
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.
@h Definitions.
@
=
typedef struct extension_census {
struct linked_list *search_list; /* of |inbuild_nest| */
int built_in_tag;
int materials_tag;
int external_tag;
MEMORY_MANAGEMENT
} extension_census;
extension_census *Extensions::Census::new(linked_list *L) {
extension_census *C = CREATE(extension_census);
C->search_list = L;
C->built_in_tag = -2;
C->materials_tag = -2;
C->external_tag = -2;
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) == C->built_in_tag)
return Extensions::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) == C->external_tag)
return Extensions::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
@ -17,12 +51,12 @@ or ECD.
=
typedef struct extension_census_datum {
struct inbuild_work *ecd_work; /* title, author, hash code */
struct text_stream *version_text; /* such as |23| or |14/060527| */
struct inbuild_version_number version; /* such as |2.1| or |14/060527| */
struct text_stream *VM_requirement; /* such as "(for Z-machine only)" */
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 */
pathname *domain; /* pathname of the stock in which this was found */
struct inbuild_nest *domain; /* pathname of the stock in which this was found */
struct text_stream *rubric; /* brief description found in opening lines */
struct extension_census_datum *next; /* next one in lexicographic order */
MEMORY_MANAGEMENT
@ -41,10 +75,11 @@ 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(void) {
void Extensions::Census::perform(extension_census *C) {
Extensions::Census::begin_recording_census_errors();
for (int area=0; area<NO_FS_AREAS; area++)
Extensions::Census::take_census_of_domain(pathname_of_extensions[area], area+1);
inbuild_nest *N;
LOOP_OVER_LINKED_LIST(N, inbuild_nest, C->search_list)
Extensions::Census::take_census_of_domain(C, N, Nests::get_tag(N));
Extensions::Census::end_recording_census_errors();
}
@ -56,10 +91,11 @@ special (it is read-only, for one thing), but in principle we could have
any number of other domains. The following code scans one.
=
pathname *current_extension_domain = NULL;
void Extensions::Census::take_census_of_domain(pathname *P, int origin) {
current_extension_domain = P;
Extensions::Census::census_from(P, TRUE, origin, NULL);
inbuild_nest *current_extension_domain = NULL;
void Extensions::Census::take_census_of_domain(extension_census *C, inbuild_nest *N, int origin) {
current_extension_domain = N;
pathname *P = Extensions::path_within_nest(N);
Extensions::Census::census_from(C, N, P, TRUE, origin, NULL);
}
@ The following routine is not as recursive as it looks, since it runs at
@ -90,7 +126,7 @@ typedef struct census_state {
text_stream *parent;
} census_state;
void Extensions::Census::census_from(pathname *P, int top_level, int origin, text_stream *parent) {
void Extensions::Census::census_from(extension_census *C, inbuild_nest *N, pathname *P, int top_level, int origin, text_stream *parent) {
scan_directory *dir = Directories::open(P);
if (dir) {
census_state cs;
@ -98,13 +134,13 @@ void Extensions::Census::census_from(pathname *P, int top_level, int origin, tex
TEMPORARY_TEXT(item_name);
int count = 0;
while (Directories::next(dir, item_name))
Extensions::Census::census_from_helper(item_name, &cs, ++count);
Extensions::Census::census_from_helper(C, N, item_name, &cs, ++count);
DISCARD_TEXT(item_name);
Directories::close(dir);
}
}
void Extensions::Census::census_from_helper(text_stream *item_name, census_state *cs,
void Extensions::Census::census_from_helper(extension_census *C, inbuild_nest *N, text_stream *item_name, census_state *cs,
int count) {
if (Str::len(item_name) == 0) return;
LOGIF(EXTENSIONS_CENSUS, "%d: %S\n", count, item_name);
@ -137,19 +173,19 @@ level to scan it.
if (Str::eq(item_name, I"Reserved")) return;
if (Str::len(item_name) > MAX_EXTENSION_TITLE_LENGTH-1) {
Extensions::Census::census_error("author name exceeds the maximum permitted length",
Extensions::Census::census_error(I"author name exceeds the maximum permitted length",
item_name, NULL, NULL, NULL); return;
}
if (Str::includes_character(item_name, '.')) {
Extensions::Census::census_error("author name contains a full stop",
Extensions::Census::census_error(I"author name contains a full stop",
item_name, NULL, NULL, NULL); return;
}
Extensions::Census::census_from(
Pathnames::subfolder(cs->P, item_name),
C, N, Pathnames::subfolder(cs->P, item_name),
FALSE, cs->origin, item_name);
return;
}
Extensions::Census::census_error("non-folder found where author folders should be",
Extensions::Census::census_error(I"non-folder found where author folders should be",
item_name, NULL, NULL, NULL); return;
@ At the lower level, |.| files or folders are again skipped; any other
@ -166,30 +202,28 @@ installed correctly on other Informs elsewhere (perhaps on other platforms).
@d MAX_TITLING_LINE_LENGTH 501 /* lots, allowing for an improbably large number of virtual machine restrictions */
@<Take census from a possible extension@> =
inbuild_version_number V = VersionNumbers::null();
int overridden_by_an_extension_already_found = FALSE;
TEMPORARY_TEXT(candidate_title);
TEMPORARY_TEXT(raw_title);
TEMPORARY_TEXT(candidate_author_name);
TEMPORARY_TEXT(raw_author_name);
TEMPORARY_TEXT(titling_line);
TEMPORARY_TEXT(rubric_text);
TEMPORARY_TEXT(version_text);
TEMPORARY_TEXT(requirement_text);
TEMPORARY_TEXT(claimed_author_name);
TEMPORARY_TEXT(claimed_title);
if (Str::get_last_char(item_name) == '.') return;
if (Str::get_last_char(item_name) == FOLDER_SEPARATOR) {
Extensions::Census::census_error("folder or application in author folder",
Extensions::Census::census_error(I"folder or application in author folder",
cs->parent, item_name, NULL, NULL); return;
}
if (Str::len(item_name) > MAX_EXTENSION_TITLE_LENGTH-1) {
Extensions::Census::census_error("title exceeds the maximum permitted length",
Extensions::Census::census_error(I"title exceeds the maximum permitted length",
cs->parent, item_name, NULL, NULL); return;
}
@<Make candidate title and author name from normalised casing versions of filename and parent folder name@>;
@<Extract the titling line and rubric, if any, from the extension file@>;
@<Parse the version, title, author and VM requirements from the titling line@>;
@<Scan the extension file@>;
@<Check that the candidate name and title match those claimed in the titling line@>;
@<See if we duplicate the title and author name of an extension already found in another domain@>;
if (overridden_by_an_extension_already_found == FALSE) {
@ -200,9 +234,7 @@ installed correctly on other Informs elsewhere (perhaps on other platforms).
DISCARD_TEXT(raw_title);
DISCARD_TEXT(candidate_author_name);
DISCARD_TEXT(raw_author_name);
DISCARD_TEXT(titling_line);
DISCARD_TEXT(rubric_text);
DISCARD_TEXT(version_text);
DISCARD_TEXT(requirement_text);
DISCARD_TEXT(claimed_author_name);
DISCARD_TEXT(claimed_title);
@ -238,7 +270,7 @@ stored have been waived.
if (Str::includes_character(candidate_title, '.')) {
LOG("Title is <%S>\n", candidate_title);
Extensions::Census::census_error("title contains a full stop",
Extensions::Census::census_error(I"title contains a full stop",
cs->parent, candidate_title, NULL, NULL); return;
}
@ -260,127 +292,21 @@ extract its titling line and rubric, then close it again.
@<Read the rubric text, if any is present@>;
fclose(EXTF);
@ The following should only fail in the event of some peculiar filing system
failure (or of some other process having moved or deleted the file since
we scanned the folder only moments ago).
@ The actual scanning is delegated to the extension-scanner already given.
@<Open the extension file for reading@> =
@<Scan the extension file@> =
filename *F =
Locations::of_extension(current_extension_domain,
item_name, cs->parent, FALSE);
EXTF = Filenames::fopen_caseless(F, "r");
if (EXTF == NULL) {
Extensions::Census::census_error("file cannot be read",
cs->parent, item_name, NULL, NULL); return;
}
@ The actual maximum number of characters in the titling line is one less
than |MAX_TITLING_LINE_LENGTH|, to allow for the null terminator. The titling
line is terminated by any of |0A|, |0D|, |0A 0D| or |0D 0A|, or by the local
|\n| for good measure.
@<Read the titling line of the extension and normalise its casing@> =
int titling_chars_read = 0, c;
while ((c = TextFiles::utf8_fgetc(EXTF, NULL, FALSE, NULL)) != EOF) {
if (c == 0xFEFF) return; /* skip the optional Unicode BOM pseudo-character */
if ((c == '\x0a') || (c == '\x0d') || (c == '\n')) break;
if (titling_chars_read < MAX_TITLING_LINE_LENGTH - 1) PUT_TO(titling_line, c);
}
Works::normalise_casing(titling_line);
@ In the following, all possible newlines are converted to white space, and
all white space before a quoted rubric text is ignored. We need to do this
partly because users have probably keyed a double line break before the
rubric, but also because we might have stopped reading the titling line
halfway through a line division combination like |0A 0D|, so that the first
thing we read here is a meaningless |0D|.
@<Read the rubric text, if any is present@> =
int c, found_start = FALSE;
while ((c = TextFiles::utf8_fgetc(EXTF, NULL, FALSE, NULL)) != EOF) {
if ((c == '\x0a') || (c == '\x0d') || (c == '\n') || (c == '\t')) c = ' ';
if ((c != ' ') && (found_start == FALSE)) {
if (c == '"') found_start = TRUE;
else break;
} else {
if (c == '"') break;
if (found_start) PUT_TO(rubric_text, c);
}
}
@h Parsing the titling line.
In general, once case-normalised, a titling line looks like this:
>> Version 2/070423 Of Going To The Zoo (For Glulx Only) By Cary Grant Begins Here.
and the version information, the VM restriction and the full stop are all
optional, but the division word "of" and the concluding "begin[s] here"
are not. We break it up into pieces, so that
|version_text = "2/070423"|
|claimed_title = "Going To The Zoo"|
|claimed_author_name = "Cary Grant"|
|requirement_text = "(For Glulx Only)"|
It's tempting to do this by feeding it into the lexer and then reusing some
of the code which parses these lines during sentence-breaking, but in fact
we want to use the information rather differently, and besides: it seems
useful to record some C code here which correctly parses a titling line,
since this can easily be extracted and used in other utilities handling
Inform extensions.
@<Parse the version, title, author and VM requirements from the titling line@> =
match_results mr = Regexp::create_mr();
if (Str::get_last_char(titling_line) == '.') Str::delete_last_character(titling_line);
if ((Regexp::match(&mr, titling_line, L"(%c*) Begin Here")) ||
(Regexp::match(&mr, titling_line, L"(%c*) Begins Here"))) {
Str::copy(titling_line, mr.exp[0]);
} else {
LOG("Titling: %S\n", titling_line);
Extensions::Census::census_error("appears not to be an extension (its first line does "
"not end 'begin(s) here', as extension titling lines must)",
cs->parent, item_name, NULL, NULL); return;
}
@<Scan the version text, if any, and advance to the position past Version... Of@>;
if (Regexp::match(&mr, titling_line, L"The (%c*)")) Str::copy(titling_line, mr.exp[0]);
@<Divide the remaining text into a claimed author name and title, divided by By@>;
@<Extract the VM requirements text, if any, from the claimed title@>;
Regexp::dispose_of(&mr);
@ We make no attempt to check the version number for validity: the purpose
of the census is to identify extensions and reject accidentally included
other files, not to syntax-check all extensions to see if they would work
if used.
@<Scan the version text, if any, and advance to the position past Version... Of@> =
if (Regexp::match(&mr, titling_line, L"Version (%c*?) Of (%c*)")) {
Str::copy(version_text, mr.exp[0]);
Str::copy(titling_line, mr.exp[1]);
}
@ The earliest "by" is the divider: note that extension titles are not
allowed to contain this word, so "North By Northwest By Cary Grant" is
not a situation we need to contend with.
@<Divide the remaining text into a claimed author name and title, divided by By@> =
if (Regexp::match(&mr, titling_line, L"(%c*?) By (%c*)")) {
Str::copy(claimed_title, mr.exp[0]);
Str::copy(claimed_author_name, mr.exp[1]);
} else {
Extensions::Census::census_error("appears not to be an extension (the titling line does "
"not give both author and title)",
cs->parent, item_name, NULL, NULL); return;
}
@ Similarly, extension titles are not allowed to contain parentheses, so
this is unambiguous.
@<Extract the VM requirements text, if any, from the claimed title@> =
if (Regexp::match(&mr, claimed_title, L"(%c*?) *(%(%c*%))")) {
Str::copy(claimed_title, mr.exp[0]);
Str::copy(requirement_text, mr.exp[1]);
Extensions::filename_in_nest(current_extension_domain,
item_name, cs->parent);
TEMPORARY_TEXT(error_text);
V = Extensions::scan_file(F, claimed_title, claimed_author_name,
rubric_text, requirement_text, error_text);
if (Str::len(error_text) > 0) {
Extensions::Census::census_error(error_text,
cs->parent, item_name, NULL, NULL);
return;
}
DISCARD_TEXT(error_text);
@h Making sure this is the extension we expected to find.
It's easier for these confusions to arise than might be thought. For instance,
@ -396,18 +322,15 @@ to arise when users install extensions by hand. Still, it's prudent to check.
if (Str::eq(claimed_author_name, candidate_author_name)) right_folder = TRUE;
if ((right_leafname == TRUE) && (right_folder == FALSE)) {
Extensions::Census::census_error("an extension with the right filename but in the wrong "
"author's folder",
Extensions::Census::census_error(I"an extension with the right filename but in the wrong author's folder",
cs->parent, item_name, claimed_author_name, claimed_title); return;
}
if ((right_leafname == FALSE) && (right_folder == TRUE)) {
Extensions::Census::census_error("an extension stored in the correct author's folder, but "
"with the wrong filename",
Extensions::Census::census_error(I"an extension stored in the correct author's folder, but with the wrong filename",
cs->parent, item_name, claimed_author_name, claimed_title); return;
}
if ((right_leafname == FALSE) && (right_folder == FALSE)) {
Extensions::Census::census_error("an extension but with the wrong filename and put in the "
"wrong author's folder",
Extensions::Census::census_error(I"an extension but with the wrong filename and put in the wrong author's folder",
cs->parent, item_name, claimed_author_name, claimed_title); return;
}
@ -423,7 +346,7 @@ which the user had installed to override this built-in extension.
LOOP_OVER(other, extension_census_datum)
if ((Str::eq(candidate_author_name, other->ecd_work->author_name))
&& (Str::eq(candidate_title, other->ecd_work->title))
&& ((other->built_in) || (cs->origin == ORIGIN_WAS_BUILT_IN_EXTENSIONS_AREA))) {
&& ((other->built_in) || (cs->origin == C->built_in_tag))) {
other->overriding_a_built_in_extension = TRUE;
overridden_by_an_extension_already_found = TRUE;
}
@ -439,14 +362,12 @@ truncate it.
Works::add_to_database(ecd->ecd_work, INSTALLED_WDBC);
Works::set_raw(ecd->ecd_work, raw_author_name, raw_title);
ecd->VM_requirement = Str::duplicate(requirement_text);
if (Str::len(version_text) > MAX_VERSION_NUMBER_LENGTH)
Str::truncate(version_text, MAX_VERSION_NUMBER_LENGTH); /* truncate to maximum legal length */
ecd->version_text = Str::duplicate(version_text);
ecd->version = V;
ecd->domain = current_extension_domain;
ecd->built_in = FALSE;
if (cs->origin == ORIGIN_WAS_BUILT_IN_EXTENSIONS_AREA) ecd->built_in = TRUE;
if (cs->origin == C->built_in_tag) ecd->built_in = TRUE;
ecd->project_specific = FALSE;
if (cs->origin == ORIGIN_WAS_MATERIALS_EXTENSIONS_AREA) ecd->project_specific = TRUE;
if (cs->origin == C->materials_tag) ecd->project_specific = TRUE;
ecd->overriding_a_built_in_extension = FALSE;
ecd->next = NULL;
ecd->rubric = Str::duplicate(rubric_text);
@ -484,18 +405,23 @@ void Extensions::Census::end_recording_census_errors(void) {
@ When a census error arises, then, we write it as a line to the errors stream.
=
void Extensions::Census::census_error(char *message, text_stream *auth, text_stream *title,
void Extensions::Census::census_error(text_stream *message, text_stream *auth, text_stream *title,
text_stream *claimed_author, text_stream *claimed_title) {
text_stream *OUT = CENERR;
no_census_errors++;
#ifdef INDEX_MODULE
HTMLFiles::open_para(OUT, 2, "hanging");
#endif
#ifndef INDEX_MODULE
HTML_OPEN("p");
#endif
if (Str::len(claimed_author) > 0)
WRITE("<b>%S by %S</b> - %s (the extension says it is '%S by %S')",
WRITE("<b>%S by %S</b> - %S (the extension says it is '%S by %S')",
title, auth, message, claimed_title, claimed_author);
else if ((Str::len(auth) > 0) && (Str::len(title) > 0))
WRITE("<b>%S by %S</b> - %s", title, auth, message);
WRITE("<b>%S by %S</b> - %S", title, auth, message);
else
WRITE("<b>%S</b> - %s", auth, message);
WRITE("<b>%S</b> - %S", auth, message);
HTML_CLOSE("p");
}
@ -557,7 +483,7 @@ any oddities found in the external extensions area.
@d CE_BY_LENGTH 5
=
void Extensions::Census::write_results(OUTPUT_STREAM) {
void Extensions::Census::write_results(OUTPUT_STREAM, extension_census *C) {
@<Display the location of installed extensions@>;
Extensions::Census::warn_about_census_errors(OUT);
HTML::end_html_row(OUT);
@ -608,8 +534,10 @@ void Extensions::Census::write_results(OUTPUT_STREAM) {
HTML_TAG_WITH("img", "src='inform:/doc_images/folder4.png' border=0");
WRITE("&nbsp;You have no other extensions installed at present.");
} else {
HTML::Javascript::open_file(OUT, pathname_of_extensions[EXTERNAL_FS_AREA], NULL,
#ifdef INDEX_MODULE
HTML::Javascript::open_file(OUT, Extensions::Census::external_path(C), NULL,
"src='inform:/doc_images/folder4.png' border=0");
#endif
WRITE("&nbsp;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.) "
@ -619,7 +547,9 @@ void Extensions::Census::write_results(OUTPUT_STREAM) {
HTML_CLOSE("p");
if (nps > 0) {
HTML_OPEN("p");
HTML::Javascript::open_file(OUT, pathname_of_extensions[INTERNAL_FS_AREA], NULL, PROJECT_SPECIFIC_SYMBOL);
#ifdef INDEX_MODULE
HTML::Javascript::open_file(OUT, Extensions::Census::internal_path(C), NULL, PROJECT_SPECIFIC_SYMBOL);
#endif
WRITE("&nbsp;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.",
@ -632,9 +562,11 @@ 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@> =
extension_file *ef;
LOOP_OVER(ef, extension_file)
Extensions::Dictionary::time_stamp(ef);
#ifdef CORE_MODULE
inform_extension *E;
LOOP_OVER(E, inform_extension)
Extensions::Dictionary::time_stamp(E);
#endif
@ I am the first to admit that this implementation is not inspired. There
are five radio buttons, and number 2 is selected by default.
@ -721,8 +653,10 @@ just a blank image used for horizontal spacing to keep margins straight.
WRITE(" Your version overrides the one built in&nbsp;");
}
if (key_vms) {
#ifdef CORE_MODULE
HTML_TAG("br");
VirtualMachines::write_key(OUT);
#endif
}
HTML_CLOSE("p");
}
@ -862,14 +796,14 @@ the usual ones seen in Mac OS X applications such as iTunes.
case 0:
WRITE("Supplied in the .materials folder&nbsp;&nbsp;");
HTML_OPEN_WITH("span", "class=\"smaller\"");
WRITE("%p", pathname_of_extensions[INTERNAL_FS_AREA]);
WRITE("%p", Extensions::Census::internal_path(C));
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&nbsp;&nbsp;");
HTML_OPEN_WITH("span", "class=\"smaller\"");
WRITE("%p", pathname_of_extensions[EXTERNAL_FS_AREA]);
WRITE("%p", Extensions::Census::external_path(C));
HTML_CLOSE("span"); break;
}
@ -931,14 +865,19 @@ 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@> =
wording W = Feeds::feed_stream(ecd->VM_requirement);
WRITE("&nbsp;");
#ifdef CORE_MODULE
wording W = Feeds::feed_stream(ecd->VM_requirement);
VirtualMachines::write_icons(OUT, Wordings::trim_last_word(Wordings::trim_last_word(W)));
#endif
#ifndef CORE_MODULE
WRITE("%S", ecd->VM_requirement);
#endif
@<Print column 2 of the census line@> =
HTML_OPEN_WITH("span", "class=\"smaller\"");
if (Str::len(ecd->version_text) > 0)
WRITE("v&nbsp;%S", ecd->version_text);
if (VersionNumbers::is_null(ecd->version) == FALSE)
WRITE("v&nbsp;%v", &(ecd->version));
else
WRITE("--");
HTML_CLOSE("span");
@ -951,17 +890,20 @@ the first and last word and just look at what is in between:
@<Print column 3 of the census line@> =
char *opener = "src='inform:/doc_images/folder4.png' border=0";
pathname *area = pathname_of_extensions[EXTERNAL_FS_AREA];
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;
area = pathname_of_extensions[MATERIALS_FS_AREA];
}
if (ecd->built_in) HTML_TAG_WITH("img", "%s", opener)
else HTML::Javascript::open_file(OUT, area, ecd->ecd_work->raw_author_name, opener);
else {
#ifdef INDEX_MODULE
pathname *area = Extensions::path_within_nest(ecd->domain);
HTML::Javascript::open_file(OUT, area, ecd->ecd_work->raw_author_name, opener);
#endif
}
@<Print column 4 of the census line@> =
HTML_OPEN_WITH("span", "class=\"smaller\"");

View file

@ -7,6 +7,13 @@ An Inform 7 extension.
= (early code)
inbuild_genre *extension_genre = NULL;
@ An extension has a title and an author name, each of which is limited in
length to one character less than the following constants:
@d MAX_EXTENSION_TITLE_LENGTH 51
@d MAX_EXTENSION_AUTHOR_LENGTH 51
@d MAX_VERSION_NUMBER_LENGTH 32 /* allows for |999/991231| and more besides */
@ =
void Extensions::start(void) {
extension_genre = Model::genre(I"extension");
@ -18,10 +25,23 @@ void Extensions::start(void) {
inbuild_copy *Extensions::claim(text_stream *arg, text_stream *ext, int directory_status) {
if (directory_status == TRUE) return NULL;
if (Str::eq_insensitive(ext, I"i7x")) {
// eventually load into a copy here
// Works::add_to_database(...->work, CLAIMED_WDBC);
return NULL;
filename *F = Filenames::from_text(arg);
TEMPORARY_TEXT(author);
TEMPORARY_TEXT(title);
TEMPORARY_TEXT(error_text);
TEMPORARY_TEXT(rubric_text);
TEMPORARY_TEXT(requirement_text);
inbuild_version_number V = Extensions::scan_file(F, title, author, rubric_text, requirement_text, error_text);
inform_extension *E = Extensions::load_at(title, author, F);
if (Str::len(error_text) > 0) return NULL;
Works::add_to_database(E->as_copy->edition->work, CLAIMED_WDBC);
E->version_loaded = V; E->as_copy->edition->version = V;
DISCARD_TEXT(author);
DISCARD_TEXT(title);
DISCARD_TEXT(error_text);
DISCARD_TEXT(rubric_text);
DISCARD_TEXT(requirement_text);
return E->as_copy;
}
return NULL;
}
@ -30,8 +50,169 @@ void Extensions::write_work(inbuild_genre *gen, OUTPUT_STREAM, inbuild_work *wor
WRITE("%X", work);
}
pathname *Extensions::path_within_nest(inbuild_nest *N) {
if (N == NULL) internal_error("no nest");
return Pathnames::subfolder(N->location, I"Extensions");
}
filename *Extensions::filename_in_nest(inbuild_nest *N, text_stream *title, text_stream *author) {
pathname *E = Extensions::path_within_nest(N);
TEMPORARY_TEXT(leaf);
WRITE_TO(leaf, "%S.i7x", title);
filename *F = Filenames::in_folder(Pathnames::subfolder(E, author), leaf);
DISCARD_TEXT(leaf);
return F;
}
inbuild_version_number Extensions::scan_file(filename *F,
text_stream *claimed_title, text_stream *claimed_author_name,
text_stream *rubric_text, text_stream *requirement_text, text_stream *error_text) {
inbuild_version_number V = VersionNumbers::null();
TEMPORARY_TEXT(titling_line);
TEMPORARY_TEXT(version_text);
FILE *EXTF = Filenames::fopen_caseless(F, "r");
if (EXTF == NULL) {
if (error_text) WRITE_TO(error_text, "file cannot be read");
return V;
}
@<Read the titling line of the extension and normalise its casing@>;
@<Read the rubric text, if any is present@>;
@<Parse the version, title, author and VM requirements from the titling line@>;
fclose(EXTF);
if (Str::len(version_text) > 0) V = VersionNumbers::from_text(version_text);
DISCARD_TEXT(titling_line);
DISCARD_TEXT(version_text);
return V;
}
@ The actual maximum number of characters in the titling line is one less
than |MAX_TITLING_LINE_LENGTH|, to allow for the null terminator. The titling
line is terminated by any of |0A|, |0D|, |0A 0D| or |0D 0A|, or by the local
|\n| for good measure.
@<Read the titling line of the extension and normalise its casing@> =
int titling_chars_read = 0, c;
while ((c = TextFiles::utf8_fgetc(EXTF, NULL, FALSE, NULL)) != EOF) {
if (c == 0xFEFF) return V; /* skip the optional Unicode BOM pseudo-character */
if ((c == '\x0a') || (c == '\x0d') || (c == '\n')) break;
if (titling_chars_read < MAX_TITLING_LINE_LENGTH - 1) PUT_TO(titling_line, c);
}
Works::normalise_casing(titling_line);
@ In the following, all possible newlines are converted to white space, and
all white space before a quoted rubric text is ignored. We need to do this
partly because users have probably keyed a double line break before the
rubric, but also because we might have stopped reading the titling line
halfway through a line division combination like |0A 0D|, so that the first
thing we read here is a meaningless |0D|.
@<Read the rubric text, if any is present@> =
int c, found_start = FALSE;
while ((c = TextFiles::utf8_fgetc(EXTF, NULL, FALSE, NULL)) != EOF) {
if ((c == '\x0a') || (c == '\x0d') || (c == '\n') || (c == '\t')) c = ' ';
if ((c != ' ') && (found_start == FALSE)) {
if (c == '"') found_start = TRUE;
else break;
} else {
if (c == '"') break;
if (found_start) PUT_TO(rubric_text, c);
}
}
@h Parsing the titling line.
In general, once case-normalised, a titling line looks like this:
>> Version 2/070423 Of Going To The Zoo (For Glulx Only) By Cary Grant Begins Here.
and the version information, the VM restriction and the full stop are all
optional, but the division word "of" and the concluding "begin[s] here"
are not. We break it up into pieces, so that
|version_text = "2/070423"|
|claimed_title = "Going To The Zoo"|
|claimed_author_name = "Cary Grant"|
|requirement_text = "(For Glulx Only)"|
It's tempting to do this by feeding it into the lexer and then reusing some
of the code which parses these lines during sentence-breaking, but in fact
we want to use the information rather differently, and besides: it seems
useful to record some C code here which correctly parses a titling line,
since this can easily be extracted and used in other utilities handling
Inform extensions.
@<Parse the version, title, author and VM requirements from the titling line@> =
match_results mr = Regexp::create_mr();
if (Str::get_last_char(titling_line) == '.') Str::delete_last_character(titling_line);
if ((Regexp::match(&mr, titling_line, L"(%c*) Begin Here")) ||
(Regexp::match(&mr, titling_line, L"(%c*) Begins Here"))) {
Str::copy(titling_line, mr.exp[0]);
} else {
LOG("Titling: %S\n", titling_line);
if (error_text) WRITE_TO(error_text,
"appears not to be an extension (its first line does "
"not end 'begin(s) here', as extension titling lines must)");
return V;
}
@<Scan the version text, if any, and advance to the position past Version... Of@>;
if (Regexp::match(&mr, titling_line, L"The (%c*)")) Str::copy(titling_line, mr.exp[0]);
@<Divide the remaining text into a claimed author name and title, divided by By@>;
@<Extract the VM requirements text, if any, from the claimed title@>;
Regexp::dispose_of(&mr);
@ We make no attempt to check the version number for validity: the purpose
of the census is to identify extensions and reject accidentally included
other files, not to syntax-check all extensions to see if they would work
if used.
@<Scan the version text, if any, and advance to the position past Version... Of@> =
if (Regexp::match(&mr, titling_line, L"Version (%c*?) Of (%c*)")) {
Str::copy(version_text, mr.exp[0]);
Str::copy(titling_line, mr.exp[1]);
}
@ The earliest "by" is the divider: note that extension titles are not
allowed to contain this word, so "North By Northwest By Cary Grant" is
not a situation we need to contend with.
@<Divide the remaining text into a claimed author name and title, divided by By@> =
if (Regexp::match(&mr, titling_line, L"(%c*?) By (%c*)")) {
Str::copy(claimed_title, mr.exp[0]);
Str::copy(claimed_author_name, mr.exp[1]);
} else {
if (error_text) WRITE_TO(error_text,
"appears not to be an extension (the titling line does "
"not give both author and title)");
return V;
}
@ Similarly, extension titles are not allowed to contain parentheses, so
this is unambiguous.
@<Extract the VM requirements text, if any, from the claimed title@> =
if (Regexp::match(&mr, claimed_title, L"(%c*?) *(%(%c*%))")) {
Str::copy(claimed_title, mr.exp[0]);
Str::copy(requirement_text, mr.exp[1]);
}
@
=
void Extensions::location_in_nest(inbuild_genre *gen, inbuild_nest *N, inbuild_requirement *req, linked_list *search_results) {
;
pathname *P = Extensions::path_within_nest(N);
for (int i7x_flag = 1; i7x_flag >= 0; i7x_flag--) {
TEMPORARY_TEXT(leaf);
if (i7x_flag) WRITE_TO(leaf, "%S.i7x", req->work->title);
else WRITE_TO(leaf, "%S", req->work->title);
filename *F = Filenames::in_folder(Pathnames::subfolder(P, req->work->author_name), leaf);
if (TextFiles::exists(F)) {
inform_extension *E = Extensions::load_at(req->work->title, req->work->author_name, F);
if (Model::meets(E->version_loaded, req)) {
Nests::add_search_result(search_results, N, E->as_copy);
}
}
DISCARD_TEXT(leaf);
}
}
void Extensions::copy_to_nest(inbuild_genre *gen, inbuild_copy *C, inbuild_nest *N, int syncing) {
@ -72,6 +253,8 @@ inform_extension *Extensions::load_at(text_stream *title, text_stream *author, f
E->rubric_as_lexed = NULL;
E->extra_credit_as_lexed = NULL;
#endif
// build_graph *EV =
Graphs::copy_vertex(E->as_copy);
return E;
}

View file

@ -37,7 +37,7 @@ void Kits::location_in_nest(inbuild_genre *gen, inbuild_nest *N, inbuild_require
filename *canary = Filenames::in_folder(P, I"kit_metadata.txt");
if (TextFiles::exists(canary)) {
inform_kit *K = Kits::load_at(Pathnames::directory_name(P), P);
if ((VersionNumbers::ge(K->version, req->min_version)) && (VersionNumbers::le(K->version, req->min_version))) {
if (Model::meets(K->version, req)) {
Nests::add_search_result(search_results, N, K->as_copy);
}
}

View file

@ -18,3 +18,4 @@ Chapter 2: Conceptual Framework
Chapter 3: The Genres
Kits
Extensions
Extension Census

View file

@ -37,7 +37,6 @@ We need to itemise the structures we'll want to allocate:
@e pcalc_prop_deferral_MT
@e literal_pattern_MT
@e generalisation_MT
@e extension_census_datum_MT
@e extension_dictionary_entry_MT
@e known_extension_clash_MT
@e i6_schema_array_MT
@ -117,7 +116,6 @@ ALLOCATE_INDIVIDUALLY(dval_written)
ALLOCATE_INDIVIDUALLY(equation_node)
ALLOCATE_INDIVIDUALLY(equation_symbol)
ALLOCATE_INDIVIDUALLY(equation)
ALLOCATE_INDIVIDUALLY(extension_census_datum)
ALLOCATE_INDIVIDUALLY(extension_dictionary_entry)
ALLOCATE_INDIVIDUALLY(extension_file)
ALLOCATE_INDIVIDUALLY(generalisation)
@ -257,7 +255,6 @@ void CoreModule::start(void) {
@ Not all of our memory will be claimed in the form of structures: now and then
we need to use the equivalent of traditional |malloc| and |calloc| routines.
@e EXTENSION_DICTIONARY_MREASON
@e INDEX_SORTING_MREASON
@e INSTANCE_COUNTING_MREASON
@e MAP_INDEX_MREASON
@ -271,7 +268,6 @@ we need to use the equivalent of traditional |malloc| and |calloc| routines.
@e EMIT_ARRAY_MREASON
@<Register this module's memory allocation reasons@> =
Memory::reason_name(EXTENSION_DICTIONARY_MREASON, "extension dictionary");
Memory::reason_name(INDEX_SORTING_MREASON, "index sorting");
Memory::reason_name(INSTANCE_COUNTING_MREASON, "instance-of-kind counting");
Memory::reason_name(MAP_INDEX_MREASON, "map in the World index");
@ -301,7 +297,6 @@ we need to use the equivalent of traditional |malloc| and |calloc| routines.
@e DEBUGGING_LOG_CONTENTS_DA
@e DESCRIPTION_COMPILATION_DA
@e EXPRESSIONS_DA
@e EXTENSIONS_CENSUS_DA
@e FIGURE_CREATIONS_DA
@e HEADINGS_DA
@e IMPLICATIONS_DA
@ -347,7 +342,6 @@ we need to use the equivalent of traditional |malloc| and |calloc| routines.
Log::declare_aspect(DEBUGGING_LOG_CONTENTS_DA, L"debugging log contents", TRUE, FALSE);
Log::declare_aspect(DESCRIPTION_COMPILATION_DA, L"description compilation", FALSE, FALSE);
Log::declare_aspect(EXPRESSIONS_DA, L"expressions", FALSE, FALSE);
Log::declare_aspect(EXTENSIONS_CENSUS_DA, L"extensions census", FALSE, FALSE);
Log::declare_aspect(FIGURE_CREATIONS_DA, L"figure creations", FALSE, FALSE);
Log::declare_aspect(HEADINGS_DA, L"headings", FALSE, FALSE);
Log::declare_aspect(IMPLICATIONS_DA, L"implications", FALSE, TRUE);

View file

@ -23,7 +23,6 @@ char *AREA_NAME[3] = { "from .materials", "installed", "built in" };
= (early code)
linked_list *I7_nest_list = NULL;
pathname *pathname_of_area[NO_FS_AREAS] = { NULL, NULL, NULL };
pathname *pathname_of_extensions[NO_FS_AREAS] = { NULL, NULL, NULL };
pathname *pathname_of_inter_resources[NO_FS_AREAS] = { NULL, NULL, NULL };
pathname *pathname_of_languages[NO_FS_AREAS] = { NULL, NULL, NULL };
pathname *pathname_of_website_templates[NO_FS_AREAS] = { NULL, NULL, NULL };
@ -135,12 +134,21 @@ int Locations::set_defaults(int census_mode) {
if ((census_mode) && (filename_of_i7_source))
Problems::Fatal::issue("In census mode, no source text may be supplied");
I7_nest_list = NEW_LINKED_LIST(inbuild_nest);
if (pathname_of_area[MATERIALS_FS_AREA])
ADD_TO_LINKED_LIST(Nests::new(pathname_of_area[MATERIALS_FS_AREA]), inbuild_nest, I7_nest_list);
if (pathname_of_area[EXTERNAL_FS_AREA])
ADD_TO_LINKED_LIST(Nests::new(pathname_of_area[EXTERNAL_FS_AREA]), inbuild_nest, I7_nest_list);
if (pathname_of_area[INTERNAL_FS_AREA])
ADD_TO_LINKED_LIST(Nests::new(pathname_of_area[INTERNAL_FS_AREA]), inbuild_nest, I7_nest_list);
if (pathname_of_area[MATERIALS_FS_AREA]) {
inbuild_nest *nest = Nests::new(pathname_of_area[MATERIALS_FS_AREA]);
Nests::set_tag(nest, ORIGIN_WAS_MATERIALS_EXTENSIONS_AREA);
ADD_TO_LINKED_LIST(nest, inbuild_nest, I7_nest_list);
}
if (pathname_of_area[EXTERNAL_FS_AREA]) {
inbuild_nest *nest = Nests::new(pathname_of_area[EXTERNAL_FS_AREA]);
Nests::set_tag(nest, ORIGIN_WAS_USER_EXTENSIONS_AREA);
ADD_TO_LINKED_LIST(nest, inbuild_nest, I7_nest_list);
}
if (pathname_of_area[INTERNAL_FS_AREA]) {
inbuild_nest *nest = Nests::new(pathname_of_area[INTERNAL_FS_AREA]);
Nests::set_tag(nest, ORIGIN_WAS_BUILT_IN_EXTENSIONS_AREA);
ADD_TO_LINKED_LIST(nest, inbuild_nest, I7_nest_list);
}
return TRUE;
}
@ -476,7 +484,6 @@ template files, language definitions and website templates.
=
void Locations::EILT_at(int area, pathname *P) {
pathname_of_extensions[area] = Pathnames::subfolder(P, I"Extensions");
pathname_of_languages[area] = Pathnames::subfolder(P, I"Languages");
pathname_of_website_templates[area] = Pathnames::subfolder(P, I"Templates");
pathname_of_inter_resources[area] = Pathnames::subfolder(P, I"Inter");

View file

@ -82,11 +82,11 @@ int SourceFiles::read_file(filename *F, text_stream *synopsis, extension_file *E
int area = -1;
if (EF)
area = SourceFiles::read_file_inner(F, synopsis,
pathname_of_extensions, NO_FS_AREAS + 1, documentation_only, &sf,
I7_nest_list, documentation_only, &sf,
STORE_POINTER_extension_file(EF), FALSE, EF);
else
area = SourceFiles::read_file_inner(F, synopsis,
NULL, 0, documentation_only, &sf,
NULL, documentation_only, &sf,
STORE_POINTER_extension_file(NULL), TRUE, NULL);
if (area == -1) {
if (EF) {
@ -136,7 +136,7 @@ application.
@ =
int SourceFiles::read_file_inner(filename *F, text_stream *synopsis,
pathname **list, int list_len, int documentation_only, source_file **S,
linked_list *search_list, int documentation_only, source_file **S,
general_pointer ref, int primary, extension_file *EF) {
int origin_tried = 1;
@ -165,24 +165,20 @@ application to communicate the problem badly.
@<Set pathname and filename, and open file@> =
handle = NULL;
if (list) {
TEMPORARY_TEXT(author_name);
TEMPORARY_TEXT(title);
WRITE_TO(author_name, "%W", EF->author_text);
WRITE_TO(title, "%W", EF->title_text);
for (int area=0; area<list_len; area++)
if (handle == NULL) {
pathname *P = list[area];
origin_tried = area + 1;
eventual = Locations::of_extension(P, title, author_name, TRUE);
handle = Filenames::fopen_caseless(eventual, "r");
if (handle == NULL) {
eventual = Locations::of_extension(P, title, author_name, FALSE);
handle = Filenames::fopen_caseless(eventual, "r");
}
}
DISCARD_TEXT(author_name);
DISCARD_TEXT(title);
if (search_list) {
text_stream *author_name = EF->ef_req->work->author_name;
text_stream *title = EF->ef_req->work->title;
inbuild_work *work = Works::new(extension_genre, title, author_name);
inbuild_requirement *req = Model::requirement(work, VersionNumbers::null(), VersionNumbers::null());
linked_list *L = NEW_LINKED_LIST(inbuild_search_result);
Nests::locate(req, search_list, L);
inbuild_search_result *search_result;
LOOP_OVER_LINKED_LIST(search_result, inbuild_search_result, L) {
eventual = search_result->copy->location_if_file;
handle = Filenames::fopen_caseless(eventual, "r");
origin_tried = Nests::get_tag(search_result->nest);
break;
}
} else {
handle = Filenames::fopen(F, "r");
}

View file

@ -167,8 +167,8 @@ void Extensions::Dictionary::new_entry(text_stream *category, extension_file *ef
}
}
void Extensions::Dictionary::new_entry_from_stream(text_stream *category, extension_file *ef, text_stream *headword) {
Extensions::Dictionary::new_dictionary_entry_raw(category, ef->ef_req->work->author_name, ef->ef_req->work->title, headword);
void Extensions::Dictionary::new_entry_from_stream(text_stream *category, inform_extension *E, text_stream *headword) {
Extensions::Dictionary::new_dictionary_entry_raw(category, E->as_copy->edition->work->author_name, E->as_copy->edition->work->title, headword);
}
void Extensions::Dictionary::new_dictionary_entry_raw(text_stream *category,
@ -280,7 +280,7 @@ void Extensions::Dictionary::load_helper(text_stream *line_entry,
@h Time stamping.
=
void Extensions::Dictionary::time_stamp(extension_file *ef) {
void Extensions::Dictionary::time_stamp(inform_extension *E) {
TEMPORARY_TEXT(dbuff);
char *ascday[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
char *ascmon[] = { "January", "February", "March", "April", "May", "June",
@ -288,12 +288,11 @@ void Extensions::Dictionary::time_stamp(extension_file *ef) {
WRITE_TO(dbuff, "%04d%02d%02d%02d%02d%02d/%d:%s %d %s %d %02d:%02d",
the_present->tm_year+1900, the_present->tm_mon + 1, the_present->tm_mday,
the_present->tm_hour, the_present->tm_min, the_present->tm_sec,
TextFromFiles::total_word_count(
Extensions::Files::source(ef)),
TextFromFiles::total_word_count(E->read_into_file),
ascday[the_present->tm_wday], the_present->tm_mday,
ascmon[the_present->tm_mon], the_present->tm_year+1900,
the_present->tm_hour, the_present->tm_min);
Extensions::Dictionary::new_entry_from_stream(I"indexing", ef, dbuff);
Extensions::Dictionary::new_entry_from_stream(I"indexing", E, dbuff);
DISCARD_TEXT(dbuff);
}

View file

@ -245,7 +245,8 @@ documentation page could be forgiven for thinking it a miscellany.
@<Document and dictionary the definitions made in extension file ef@> =
Extensions::Dictionary::erase_entries(ef);
Extensions::Dictionary::time_stamp(ef);
inform_extension *E = Extensions::Files::find(ef);
if (E) Extensions::Dictionary::time_stamp(E);
@<Document and dictionary the kinds made in extension@>;
@<Document and dictionary the objects made in extension@>;

View file

@ -24,12 +24,6 @@ because the application passes this as |-rules| on the command line, and it
knows the location of the latter because this is standard and fixed for
each platform: see Platform-Specific Definitions.
@ An extension has a title and an author name, each of which is limited in
length to one character less than the following constants:
@d MAX_EXTENSION_TITLE_LENGTH 51
@d MAX_EXTENSION_AUTHOR_LENGTH 51
@h How the application should install extensions.
When the Inform 7 application looks at a file chosen by the user to
be installed, it should look at the first line. (Note that this might have
@ -577,9 +571,13 @@ two alternatives are expressed here:
=
void Extensions::Files::handle_census_mode(void) {
if (census_mode) {
extension_census *C = Extensions::Census::new(I7_nest_list);
C->built_in_tag = ORIGIN_WAS_BUILT_IN_EXTENSIONS_AREA;
C->materials_tag = ORIGIN_WAS_MATERIALS_EXTENSIONS_AREA;
C->external_tag = ORIGIN_WAS_USER_EXTENSIONS_AREA;
Extensions::Dictionary::load();
Extensions::Census::perform();
Extensions::Files::write_top_level_of_extensions_documentation();
Extensions::Census::perform(C);
Extensions::Files::write_top_level_of_extensions_documentation(C);
Extensions::Files::write_sketchy_documentation_for_extensions_found();
}
}
@ -587,8 +585,12 @@ void Extensions::Files::handle_census_mode(void) {
void Extensions::Files::update_census(void) {
extension_file *ef;
Extensions::Dictionary::load();
Extensions::Census::perform();
Extensions::Files::write_top_level_of_extensions_documentation();
extension_census *C = Extensions::Census::new(I7_nest_list);
C->built_in_tag = ORIGIN_WAS_BUILT_IN_EXTENSIONS_AREA;
C->materials_tag = ORIGIN_WAS_MATERIALS_EXTENSIONS_AREA;
C->external_tag = ORIGIN_WAS_USER_EXTENSIONS_AREA;
Extensions::Census::perform(C);
Extensions::Files::write_top_level_of_extensions_documentation(C);
LOOP_OVER(ef, extension_file) Extensions::Documentation::write_detailed(ef);
Extensions::Files::write_sketchy_documentation_for_extensions_found();
Extensions::Dictionary::write_back();
@ -635,13 +637,13 @@ platform, for reasons as always to do with the vagaries of Internet
Explorer 7 for Windows.
=
void Extensions::Files::write_top_level_of_extensions_documentation(void) {
Extensions::Files::write_top_level_extensions_page(I"Extensions.html", 1);
Extensions::Files::write_top_level_extensions_page(I"ExtIndex.html", 2);
void Extensions::Files::write_top_level_of_extensions_documentation(extension_census *C) {
Extensions::Files::write_top_level_extensions_page(I"Extensions.html", 1, C);
Extensions::Files::write_top_level_extensions_page(I"ExtIndex.html", 2, NULL);
}
@ =
void Extensions::Files::write_top_level_extensions_page(text_stream *leaf, int content) {
void Extensions::Files::write_top_level_extensions_page(text_stream *leaf, int content, extension_census *C) {
text_stream HOMEPAGE_struct;
text_stream *OUT = &HOMEPAGE_struct;
filename *F = Filenames::in_folder(pathname_of_extension_docs, leaf);
@ -674,7 +676,7 @@ void Extensions::Files::write_top_level_extensions_page(text_stream *leaf, int c
HTML_CLOSE("div");
switch (content) {
case 1: Extensions::Census::write_results(OUT); break;
case 1: Extensions::Census::write_results(OUT, C); break;
case 2: Extensions::Dictionary::write_to_HTML(OUT); break;
}

View file

@ -263,8 +263,6 @@ In order that the numerical form of a version number should be a signed
|N| be at most 999. It could in fact rise to 2146 without incident, but
it seems cleaner to constrain the number of digits than the value.
@d MAX_VERSION_NUMBER_LENGTH 10 /* for |999/991231| */
=
inbuild_version_number Extensions::Inclusion::parse_version(int vwn) {
int i, slashes = 0, digits = 0, slash_at = 0;

View file

@ -69,7 +69,6 @@ which, we also handle extension installation, uninstallation and
documentation here."
Extension Files
Including Extensions
Extension Census
Extension Dictionary
Extension Documentation

View file

@ -485,7 +485,8 @@ void Index::Lexicon::list_verbs_in_file(OUTPUT_STREAM, source_file *sf, extensio
if (verb_count++ == 0) { HTML_OPEN("p"); WRITE("Verbs: "); } else WRITE(", ");
if (lex->part_of_speech == VERB_LEXE) WRITE("to <b>%S</b>", entry_text);
else WRITE("to be able to <b>%S</b>", entry_text);
Extensions::Dictionary::new_entry_from_stream(I"verb", ef, entry_text);
inform_extension *E = Extensions::Files::find(ef);
Extensions::Dictionary::new_entry_from_stream(I"verb", E, entry_text);
DISCARD_TEXT(entry_text);
}
if (verb_count > 0) HTML_CLOSE("p");

View file

@ -100,6 +100,7 @@ INBUILDX = inbuild/Tangled/inbuild
{tool} INBUILDTOOL inbuild inbuild
{dep} INBUILDTOOL on FOUNDATION
{dep} INBUILDTOOL on WORDS
{dep} INBUILDTOOL on INBUILD
{tool} INTERTOOL inter inter