diff --git a/README.md b/README.md index c026b20a2..488de9069 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Inform 7 -[Version](notes/versioning.md): 10.2.0-beta+6W22 'Krypton' (5 February 2023) +[Version](notes/versioning.md): 10.2.0-beta+6W24 'Krypton' (8 February 2023) ## About Inform diff --git a/build.txt b/build.txt index a7ffe3575..7e203376f 100644 --- a/build.txt +++ b/build.txt @@ -1,3 +1,3 @@ Prerelease: beta -Build Date: 5 February 2023 -Build Number: 6W22 +Build Date: 8 February 2023 +Build Number: 6W24 diff --git a/docs/core-module/2-pwst.html b/docs/core-module/2-pwst.html index 44222218e..596fb0044 100644 --- a/docs/core-module/2-pwst.html +++ b/docs/core-module/2-pwst.html @@ -99,6 +99,15 @@ group. "what that directory is called (which is not fine). Specifically, %2."); Problems::issue_problem_end(); break; + case EXT_BAD_FILENAME_CE: + Problems::quote_work(1, CE->copy->found_by->work); + Problems::quote_stream(2, CE->details); + StandardProblems::handmade_problem(Task::syntax_tree(), _p_(Untestable)); + Problems::issue_problem_segment( + "The extension %1, which your source text makes use of, has the wrong " + "filename for its source text. Specifically, %2."); + Problems::issue_problem_end(); + break; case METADATA_MALFORMED_CE: if (CE->copy->found_by) { Problems::quote_work(1, CE->copy->found_by->work); diff --git a/docs/supervisor-module/2-ce.html b/docs/supervisor-module/2-ce.html index 4749055c5..ab3b28bbf 100644 --- a/docs/supervisor-module/2-ce.html +++ b/docs/supervisor-module/2-ce.html @@ -75,6 +75,7 @@ fields are blank. enum METADATA_MALFORMED_CE enum EXT_MISWORDED_CE enum EXT_BAD_DIRNAME_CE +enum EXT_BAD_FILENAME_CE enum EXT_TITLE_TOO_LONG_CE enum EXT_AUTHOR_TOO_LONG_CE enum LANGUAGE_UNAVAILABLE_CE @@ -124,7 +125,7 @@ fields are blank. return CE; } -copy_error *CopyErrors::new_T(int cat, int subcat, text_stream *NB) { +copy_error *CopyErrors::new_T(int cat, int subcat, text_stream *NB) { copy_error *CE = CopyErrors::new(cat, subcat); CE->details = Str::duplicate(NB); return CE; @@ -195,6 +196,7 @@ output. case OPEN_FAILED_CE: WRITE("unable to open file %f", CE->details_file); break; case EXT_MISWORDED_CE: WRITE("extension misworded: %S", CE->details); break; case EXT_BAD_DIRNAME_CE: WRITE("extension directory name wrong: %S", CE->details); break; + case EXT_BAD_FILENAME_CE: WRITE("extension filename wrong: %S", CE->details); break; case METADATA_MALFORMED_CE: WRITE("%S has incorrect metadata: %S", CE->copy->edition->work->genre->genre_name, CE->details); break; case EXT_TITLE_TOO_LONG_CE: WRITE("title too long: %d characters (max is %d)", diff --git a/docs/supervisor-module/2-cps.html b/docs/supervisor-module/2-cps.html index 5995e6220..693f403e4 100644 --- a/docs/supervisor-module/2-cps.html +++ b/docs/supervisor-module/2-cps.html @@ -147,7 +147,7 @@ for later reporting. These are stored in a list.

-void Copies::attach_error(inbuild_copy *C, copy_error *CE) {
+void Copies::attach_error(inbuild_copy *C, copy_error *CE) {
     if (C == NULL) internal_error("no copy to attach to");
     CopyErrors::supply_attached_copy(CE, C);
     ADD_TO_LINKED_LIST(CE, copy_error, C->errors_reading_source_text);
diff --git a/docs/supervisor-module/2-nst.html b/docs/supervisor-module/2-nst.html
index ffbf1e668..f1654913a 100644
--- a/docs/supervisor-module/2-nst.html
+++ b/docs/supervisor-module/2-nst.html
@@ -98,7 +98,7 @@ see below for why. Lower-tag-numbered origins are better than later ones.
 enum EXTENSION_NEST_TAG
 
-int Nests::get_tag(inbuild_nest *N) {
+int Nests::get_tag(inbuild_nest *N) {
     if (N == NULL) return -1;
     return N->tag_value;
 }
diff --git a/docs/supervisor-module/2-wrk.html b/docs/supervisor-module/2-wrk.html
index 378adcf95..1b01c049a 100644
--- a/docs/supervisor-module/2-wrk.html
+++ b/docs/supervisor-module/2-wrk.html
@@ -175,7 +175,7 @@ would both pass, whereas Works::normal
 

-void Works::normalise_casing_mixed(text_stream *p) {
+void Works::normalise_casing_mixed(text_stream *p) {
     int boundary = TRUE;
     LOOP_THROUGH_TEXT(pos, p) {
         wchar_t c = Str::get(pos);
diff --git a/docs/supervisor-module/4-ebm.html b/docs/supervisor-module/4-ebm.html
index 343a88f14..6b2880f77 100644
--- a/docs/supervisor-module/4-ebm.html
+++ b/docs/supervisor-module/4-ebm.html
@@ -110,7 +110,7 @@ which stores data about extensions used by the Inform compiler.
 
 dictionary *eb_copy_cache = NULL;
 inbuild_copy *ExtensionBundleManager::new_copy(text_stream *name, pathname *P,
-    inbuild_nest *N, semantic_version_number apparent_V) {
+    inbuild_nest *N, semantic_version_number apparent_V, int force_renaming) {
     if (eb_copy_cache == NULL) eb_copy_cache = Dictionaries::new(16, FALSE);
     TEMPORARY_TEXT(key)
     WRITE_TO(key, "%p", P);
@@ -124,13 +124,41 @@ which stores data about extensions used by the Inform compiler.
         inbuild_edition *edition = Editions::new(work, VersionNumbers::null());
         C = Copies::new_in_path(edition, P, N);
         Extensions::scan(C);
+
+        if ((force_renaming == FALSE) &&
+            (VersionNumbers::ne(apparent_V, C->edition->version)))
+            force_renaming = TRUE;
+
+        if ((force_renaming == FALSE) &&
+            (Str::ne(name, C->edition->work->title)))
+            force_renaming = TRUE;
+
+        if (force_renaming == TRUE) {
+            TEMPORARY_TEXT(new_name)
+            TEMPORARY_TEXT(new_version)
+            WRITE_TO(new_version, "%v", &(C->edition->version));
+            LOOP_THROUGH_TEXT(pos, new_version)
+                if (Str::get(pos) == '.')
+                    Str::put(pos, '_');
+            WRITE_TO(new_name, "%S-v%S.i7xd", C->edition->work->title, new_version);
+            if (Extensions::rename_directory(P, new_name)) {
+                Str::clear(key);
+                WRITE_TO(key, "%p", P);
+                apparent_V = C->edition->version;
+                name = C->edition->work->title;
+            }
+            DISCARD_TEXT(new_name)
+            DISCARD_TEXT(new_version)
+        }
+
         Dictionaries::create(eb_copy_cache, key);
         Dictionaries::write_value(eb_copy_cache, key, C);
         if (VersionNumbers::is_null(apparent_V)) {
             TEMPORARY_TEXT(error_text)
             WRITE_TO(error_text,
                 "an extension in directory format must have a directory name ending "
-                "'-vN', giving the version number: for example, 'Advanced Algebra-v2_3_6'");
+                "'-vN.i7xd', giving the version number: for example, "
+                "'Advanced Algebra-v2_3_6.i7xd'");
             Copies::attach_error(C, CopyErrors::new_T(EXT_BAD_DIRNAME_CE, -1, error_text));
             DISCARD_TEXT(error_text)
         } else if (VersionNumbers::ne(apparent_V, C->edition->version)) {
@@ -170,19 +198,69 @@ copy name.
 

+
+void ExtensionBundleManager::dismantle_name(text_stream *name,
+    text_stream *to_title, text_stream *to_ext, semantic_version_number *to_V) {
+    TEMPORARY_TEXT(ext)
+    TEMPORARY_TEXT(title)
+    int pos = Str::len(name) - 1, dotpos = -1;
+    while (pos >= 0) {
+        wchar_t c = Str::get_at(name, pos);
+        if (Platform::is_folder_separator(c)) break;
+        if (c == '.') dotpos = pos;
+        pos--;
+    }
+    if (dotpos >= 0) {
+        Str::substr(ext, Str::at(name, dotpos+1), Str::end(name));
+        Str::substr(title, Str::start(name), Str::at(name, dotpos));
+    } else {
+        WRITE_TO(title, "%S", name);
+    }
+    semantic_version_number V = VersionNumbers::null();
+    match_results mr = Regexp::create_mr();
+    if (Regexp::match(&mr, title, L"(%c+)-v([0-9_]+)")) {
+        Str::clear(title);
+        WRITE_TO(title, "%S", mr.exp[0]);
+        LOOP_THROUGH_TEXT(pos, mr.exp[1])
+            if (Str::get(pos) == '_')
+                Str::put(pos, '.');
+        V = VersionNumbers::from_text(mr.exp[1]);
+    }
+    Regexp::dispose_of(&mr);
+    Str::copy(to_title, title);
+    Str::copy(to_ext, ext);
+    if (to_V) *to_V = V;
+    DISCARD_TEXT(ext)
+    DISCARD_TEXT(title)
+}
+
 inbuild_copy *ExtensionBundleManager::claim_folder_as_copy(pathname *P, inbuild_nest *N) {
     filename *canary = Filenames::in(P, I"extension_metadata.json");
-    if (TextFiles::exists(canary)) {
-        text_stream *name = Str::duplicate(Pathnames::directory_name(P));
-        semantic_version_number V = VersionNumbers::null();
-        match_results mr = Regexp::create_mr();
-        if (Regexp::match(&mr, name, L"(%c+)-v([0-9_]+)")) {
-            name = Str::duplicate(mr.exp[0]);
-            V = VersionNumbers::from_text(mr.exp[1]);
+    TEMPORARY_TEXT(ext)
+    TEMPORARY_TEXT(title)
+    semantic_version_number V = VersionNumbers::null();
+    ExtensionBundleManager::dismantle_name(Pathnames::directory_name(P), title, ext, &V);
+    inbuild_copy *C = NULL;
+    if ((Str::eq_insensitive(ext, I"i7xd")) || (TextFiles::exists(canary))) {
+        int force_renaming = NOT_APPLICABLE;
+        if (Nests::get_tag(N) == MATERIALS_NEST_TAG) force_renaming = FALSE;
+        if (Str::eq_insensitive(ext, I"i7xd") == FALSE) {
+            if (Nests::get_tag(N) == MATERIALS_NEST_TAG) {
+                force_renaming = TRUE;
+            } else {
+                TEMPORARY_TEXT(error_text)
+                WRITE_TO(error_text,
+                    "the extension directory '%S' needs to have the extension '.i7xd' added to its name",
+                    Pathnames::directory_name(P));
+                Copies::attach_error(C, CopyErrors::new_T(EXT_BAD_DIRNAME_CE, -1, error_text));
+                DISCARD_TEXT(error_text)
+            }
         }
-        return ExtensionBundleManager::new_copy(name, P, N, V);
+        C = ExtensionBundleManager::new_copy(title, P, N, V, force_renaming);
     }
-    return NULL;
+    DISCARD_TEXT(ext)
+    DISCARD_TEXT(title)
+    return C;
 }
 

§6. Searching. Here we look through a nest to find all extension bundles: diff --git a/docs/supervisor-module/5-es.html b/docs/supervisor-module/5-es.html index 9f7d49377..67488bf97 100644 --- a/docs/supervisor-module/5-es.html +++ b/docs/supervisor-module/5-es.html @@ -60,7 +60,7 @@ function togglePopup(material_id) {

Behaviour specific to copies of the extension genre.

-
+

§1. Scanning metadata. An extension has a title and an author name, each of which is limited in length to one character less than the following constants: @@ -115,8 +115,12 @@ compatibility notes given (such as "for Glulx only"). TEMPORARY_TEXT(claimed_title) TEMPORARY_TEXT(reqs) semantic_version_number V = VersionNumbers::null(); + filename *extension_source_filename = NULL; Scan the file2.2; Change the edition of the copy in light of the metadata found in the scan2.3; +if (C->location_if_path) { +WRITE_TO(STDERR, "Okay %p: %S\n", C->location_if_path, C->edition->work->title); +} DISCARD_TEXT(claimed_author_name) DISCARD_TEXT(claimed_title) DISCARD_TEXT(reqs) @@ -124,6 +128,9 @@ compatibility notes given (such as "for Glulx only"). if (Works::is_standard_rules(C->edition->work)) E->standard = TRUE; Scan the metadata file, if there is one2.4; +if (C->location_if_path) { +WRITE_TO(STDERR, "Now %S\n", C->edition->work->title); +} Check that the version numbers agree2.5; }

@@ -170,18 +177,35 @@ alone, and the version number is returned. filename *F = Extensions::main_source_file(C); FILE *EXTF = Filenames::fopen_caseless(F, "r"); if (EXTF == NULL) { - Copies::attach_error(C, CopyErrors::new_F(OPEN_FAILED_CE, -1, F)); + filename *A = Extensions::alternative_source_file(C); + if (A) { + EXTF = Filenames::fopen_caseless(A, "r"); + if (EXTF) { + extension_source_filename = A; + Look inside the file2.2.1; + fclose(EXTF); + } else Copies::attach_error(C, CopyErrors::new_F(OPEN_FAILED_CE, -1, F)); + } else Copies::attach_error(C, CopyErrors::new_F(OPEN_FAILED_CE, -1, F)); } else { - Read the titling line of the extension and normalise its casing2.2.1; - Read the rubric text, if any is present2.2.2; - Parse the version, title, author and VM requirements from the titling line2.2.3; + extension_source_filename = F; + Look inside the file2.2.1; fclose(EXTF); - if (Str::len(version_text) > 0) { - V = VersionNumbers::from_text(version_text); - if (VersionNumbers::is_null(V)) { + } + if (C->location_if_path) { + WRITE_TO(STDERR, "Read from %f\n", extension_source_filename); + TEMPORARY_TEXT(correct_leafname) + WRITE_TO(correct_leafname, "%S.i7x", claimed_title); + if (Str::ne_insensitive(correct_leafname, Filenames::get_leafname(extension_source_filename))) { + int allow = FALSE; + if ((C->nest_of_origin) && (Nests::get_tag(C->nest_of_origin) == MATERIALS_NEST_TAG) && + (Extensions::rename_file(extension_source_filename, correct_leafname))) + allow = TRUE; + if (allow == FALSE) { TEMPORARY_TEXT(error_text) - WRITE_TO(error_text, "the version number '%S' is malformed", version_text); - Copies::attach_error(C, CopyErrors::new_T(EXT_MISWORDED_CE, -1, error_text)); + WRITE_TO(error_text, + "the source file in the extension is called '%S' but should be '%S' to match the contents", + Filenames::get_leafname(extension_source_filename), correct_leafname); + Copies::attach_error(C, CopyErrors::new_T(EXT_BAD_FILENAME_CE, -1, error_text)); DISCARD_TEXT(error_text) } } @@ -190,11 +214,29 @@ alone, and the version number is returned. DISCARD_TEXT(version_text)
-

§2.2.1. The titling line is terminated by any of 0A, 0D, 0A 0D or 0D 0A, or +

§2.2.1. Look inside the file2.2.1 = +

+ +
+    Read the titling line of the extension and normalise its casing2.2.1.1;
+    Read the rubric text, if any is present2.2.1.2;
+    Parse the version, title, author and VM requirements from the titling line2.2.1.3;
+    if (Str::len(version_text) > 0) {
+        V = VersionNumbers::from_text(version_text);
+        if (VersionNumbers::is_null(V)) {
+            TEMPORARY_TEXT(error_text)
+            WRITE_TO(error_text, "the version number '%S' is malformed", version_text);
+            Copies::attach_error(C, CopyErrors::new_T(EXT_MISWORDED_CE, -1, error_text));
+            DISCARD_TEXT(error_text)
+        }
+    }
+
+ +

§2.2.1.1. 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 casing2.2.1 = +

Read the titling line of the extension and normalise its casing2.2.1.1 =

@@ -207,8 +249,8 @@ by the local \n
     Str::trim_white_space(titling_line);
     Works::normalise_casing_mixed(titling_line);
 
- -

§2.2.2. In the following, all possible newlines are converted to white space, and +

+

§2.2.1.2. 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 @@ -216,7 +258,7 @@ halfway through a line division combination like 0D.

-

Read the rubric text, if any is present2.2.2 = +

Read the rubric text, if any is present2.2.1.2 =

@@ -232,8 +274,8 @@ thing we read here is a meaningless         }
     }
 
- -

§2.2.3. In general, once case-normalised, a titling line looks like this: +

+

§2.2.1.3. In general, once case-normalised, a titling line looks like this:

@@ -246,7 +288,7 @@ are not. We break it up into pieces; for speed, we won't use the lexer to load the entire file.

-

Parse the version, title, author and VM requirements from the titling line2.2.3 = +

Parse the version, title, author and VM requirements from the titling line2.2.1.3 =

@@ -263,20 +305,20 @@ load the entire file.
         Copies::attach_error(C, CopyErrors::new_T(EXT_MISWORDED_CE, -1,
             I"the opening line does not end 'begin(s) here'"));
     }
-    Scan the version text, if any, and advance to the position past Version... Of2.2.3.1;
+    Scan the version text, if any, and advance to the position past Version... Of2.2.1.3.1;
     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 By2.2.3.2;
-    Extract the VM requirements text, if any, from the claimed title2.2.3.3;
+    Divide the remaining text into a claimed author name and title, divided by By2.2.1.3.2;
+    Extract the VM requirements text, if any, from the claimed title2.2.1.3.3;
     Regexp::dispose_of(&mr);
 
- -

§2.2.3.1. We make no attempt to check the version number for validity: the purpose +

+

§2.2.1.3.1. 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... Of2.2.3.1 = +

Scan the version text, if any, and advance to the position past Version... Of2.2.1.3.1 =

@@ -285,13 +327,13 @@ if used.
         Str::copy(titling_line, mr.exp[1]);
     }
 
- -

§2.2.3.2. The earliest "by" is the divider: note that extension titles are not +

+

§2.2.1.3.2. 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 By2.2.3.2 = +

Divide the remaining text into a claimed author name and title, divided by By2.2.1.3.2 =

@@ -304,12 +346,12 @@ not a situation we need to contend with.
             I"the titling line does not give both author and title"));
     }
 
- -

§2.2.3.3. Similarly, extension titles are not allowed to contain parentheses, so +

+

§2.2.1.3.3. Similarly, extension titles are not allowed to contain parentheses, so this is unambiguous.

-

Extract the VM requirements text, if any, from the claimed title2.2.3.3 = +

Extract the VM requirements text, if any, from the claimed title2.2.1.3.3 =

@@ -318,7 +360,7 @@ this is unambiguous.
         Str::copy(reqs, mr.exp[1]);
     }
 
- +

§2.3. Note that we don't attempt to modify the inbuild_work structure inside the edition; we create an entirely new inbuild_work. That's because they are immutable, and need to be for the extensions dictionary to work. @@ -553,13 +595,35 @@ are immutable, and need to be for the extensions dictionary to work. return F; } +filename *Extensions::alternative_source_file(inbuild_copy *C) { + pathname *P = C->location_if_path; + if (P) { + P = Pathnames::down(P, I"Source"); + linked_list *L = Directories::listing(P); + filename *A = NULL; int c = 0; + text_stream *entry; + LOOP_OVER_LINKED_LIST(entry, text_stream, L) { + if (Platform::is_folder_separator(Str::get_last_char(entry)) == FALSE) { + filename *F = Filenames::in(P, entry); + TEMPORARY_TEXT(fext) + Filenames::write_extension(fext, F); + if (Str::eq_insensitive(fext, I".i7x")) { + A = F; c++; + } + } + } + if (c == 1) return A; + } + return NULL; +} + pathname *Extensions::materials_path(inform_extension *E) { pathname *P = E->as_copy->location_if_path; if (P) P = Pathnames::down(P, I"Materials"); return P; } -inbuild_nest *Extensions::materials_nest(inform_extension *E) { +inbuild_nest *Extensions::materials_nest(inform_extension *E) { pathname *P = Extensions::materials_path(E); if ((E->materials_nest == NULL) && (P)) { E->materials_nest = Nests::new(P); @@ -572,17 +636,17 @@ are immutable, and need to be for the extensions dictionary to work.

-void Extensions::set_usage_date(inform_extension *E, text_stream *date) {
+void Extensions::set_usage_date(inform_extension *E, text_stream *date) {
     Str::clear(E->last_usage_date);
     Str::copy(E->last_usage_date, date);
 }
 
-void Extensions::set_sort_date(inform_extension *E, text_stream *date) {
+void Extensions::set_sort_date(inform_extension *E, text_stream *date) {
     Str::clear(E->sort_usage_date);
     Str::copy(E->sort_usage_date, date);
 }
 
-text_stream *Extensions::get_usage_date(inform_extension *E) {
+text_stream *Extensions::get_usage_date(inform_extension *E) {
     return E->last_usage_date;
 }
 
@@ -590,11 +654,11 @@ are immutable, and need to be for the extensions dictionary to work.
     return E->sort_usage_date;
 }
 
-void Extensions::set_word_count(inform_extension *E, int wc) {
+void Extensions::set_word_count(inform_extension *E, int wc) {
     E->word_count = wc;
 }
 
-int Extensions::get_word_count(inform_extension *E) {
+int Extensions::get_word_count(inform_extension *E) {
     return E->word_count;
 }
 
@@ -604,7 +668,7 @@ are immutable, and need to be for the extensions dictionary to work.
     return T;
 }
 
-int Extensions::compare_by_edition(inform_extension *E1, inform_extension *E2) {
+int Extensions::compare_by_edition(inform_extension *E1, inform_extension *E2) {
     if ((E1 == NULL) || (E2 == NULL)) internal_error("bad work match");
     int d = Works::cmp(E1->as_copy->edition->work, E2->as_copy->edition->work);
     if (d != 0) return d;
@@ -612,14 +676,14 @@ are immutable, and need to be for the extensions dictionary to work.
         E1->as_copy->edition->version, E2->as_copy->edition->version);
 }
 
-int Extensions::compare_by_date(inform_extension *E1, inform_extension *E2) {
+int Extensions::compare_by_date(inform_extension *E1, inform_extension *E2) {
     if ((E1 == NULL) || (E2 == NULL)) internal_error("bad work match");
     int d = Str::cmp(Extensions::get_sort_date(E2), Extensions::get_sort_date(E1));
     if (d != 0) return d;
     return Extensions::compare_by_edition(E1, E2);
 }
 
-int Extensions::compare_by_author(inform_extension *E1, inform_extension *E2) {
+int Extensions::compare_by_author(inform_extension *E1, inform_extension *E2) {
     if ((E1 == NULL) || (E2 == NULL)) internal_error("bad work match");
     int d = Str::cmp(E2->as_copy->edition->work->author_name,
         E1->as_copy->edition->work->author_name);
@@ -627,7 +691,7 @@ are immutable, and need to be for the extensions dictionary to work.
     return Extensions::compare_by_edition(E1, E2);
 }
 
-int Extensions::compare_by_title(inform_extension *E1, inform_extension *E2) {
+int Extensions::compare_by_title(inform_extension *E1, inform_extension *E2) {
     if ((E1 == NULL) || (E2 == NULL)) internal_error("bad work match");
     int d = Str::cmp(E2->as_copy->edition->work->title,
         E1->as_copy->edition->work->title);
@@ -635,7 +699,7 @@ are immutable, and need to be for the extensions dictionary to work.
     return Extensions::compare_by_edition(E1, E2);
 }
 
-int Extensions::compare_by_length(inform_extension *E1, inform_extension *E2) {
+int Extensions::compare_by_length(inform_extension *E1, inform_extension *E2) {
     if ((E1 == NULL) || (E2 == NULL)) internal_error("bad work match");
     int d = Str::cmp(
         Extensions::get_sort_word_count(E2), Extensions::get_sort_word_count(E1));
@@ -649,7 +713,7 @@ project, then...
 

-void Extensions::set_associated_project(inform_extension *E, inform_project *P) {
+void Extensions::set_associated_project(inform_extension *E, inform_project *P) {
     E->read_into_project = P;
 }
 
@@ -658,7 +722,7 @@ the Materials folder of the project in question:

-linked_list *Extensions::nest_list(inform_extension *E) {
+linked_list *Extensions::nest_list(inform_extension *E) {
     if (E == NULL) return Supervisor::shared_nest_list();
     RUN_ONLY_FROM_PHASE(NESTED_INBUILD_PHASE)
     if (LinkedLists::len(E->search_list) == 0) {
@@ -678,7 +742,7 @@ features as E w
 

-void Extensions::activate_elements(inform_extension *E, inform_project *proj) {
+void Extensions::activate_elements(inform_extension *E, inform_project *proj) {
     element_activation *EA;
     LOOP_OVER_LINKED_LIST(EA, element_activation, E->activations) {
         compiler_feature *P = Features::from_name(EA->element_name);
@@ -728,7 +792,7 @@ no project involved, we must take action ourselves.)
 

-void Extensions::construct_graph(inform_extension *E) {
+void Extensions::construct_graph(inform_extension *E) {
     Copies::get_source_text(E->as_copy);
     Sentences::set_start_of_source(sfsm, -1);
     Inclusions::traverse(E->as_copy, E->syntax_tree);
@@ -744,7 +808,7 @@ This is that time.
 

-void Extensions::read_source_text_for(inform_extension *E) {
+void Extensions::read_source_text_for(inform_extension *E) {
     inform_language *L = Languages::find_for(I"English", Extensions::nest_list(E));
     Languages::read_Preform_definition(L, Extensions::nest_list(E));
     filename *F = Extensions::main_source_file(E->as_copy);
@@ -837,7 +901,7 @@ make use of that:
 

-inform_extension *Extensions::corresponding_to(source_file *sf) {
+inform_extension *Extensions::corresponding_to(source_file *sf) {
     if (sf == NULL) return NULL;
     inbuild_copy *C = RETRIEVE_POINTER_inbuild_copy(sf->your_ref);
     if (C == NULL) return NULL;
@@ -879,7 +943,7 @@ that happens, the following function will be called to set the rubric.
     LOGIF(EXTENSIONS_CENSUS, "Extension rubric: %S\n", E->rubric_as_lexed);
 }
 
-text_stream *Extensions::get_rubric(inform_extension *E) {
+text_stream *Extensions::get_rubric(inform_extension *E) {
     if (E == NULL) return NULL;
     return E->rubric_as_lexed;
 }
@@ -917,10 +981,10 @@ problem messages and the index.
 

-void Extensions::set_inclusion_sentence(inform_extension *E, parse_node *N) {
+void Extensions::set_inclusion_sentence(inform_extension *E, parse_node *N) {
     E->inclusion_sentence = N;
 }
-parse_node *Extensions::get_inclusion_sentence(inform_extension *E) {
+parse_node *Extensions::get_inclusion_sentence(inform_extension *E) {
     if (E == NULL) return NULL;
     return E->inclusion_sentence;
 }
@@ -941,7 +1005,7 @@ becomes empty and stays that way.
 

-void Extensions::must_satisfy(inform_extension *E, inbuild_requirement *req) {
+void Extensions::must_satisfy(inform_extension *E, inbuild_requirement *req) {
     if (E->must_satisfy == NULL) E->must_satisfy = req;
     else VersionNumberRanges::intersect_range(E->must_satisfy->version_range, req->version_range);
 }
@@ -952,11 +1016,30 @@ its requirements (even though it did when first loaded). This tests for that:
 

-int Extensions::satisfies(inform_extension *E) {
+int Extensions::satisfies(inform_extension *E) {
     if (E == NULL) return FALSE;
     return Requirements::meets(E->as_copy->edition, E->must_satisfy);
 }
 
+

§20. File hierarchy tidying.

+ +
+int Extensions::rename_directory(pathname *P, text_stream *new_name) {
+    TEMPORARY_TEXT(task)
+    WRITE_TO(task, "(Changing directory name '%p' to '%S')\n", P, new_name);
+    int rv = Directories::rename(P, new_name);
+    if (rv) WRITE_TO(STDOUT, "%S", task);
+    return rv;
+}
+
+int Extensions::rename_file(filename *F, text_stream *new_name) {
+    TEMPORARY_TEXT(task)
+    WRITE_TO(task, "(Changing file name '%f' to '%S')\n", F, new_name);
+    int rv = Filenames::rename(F, new_name);
+    if (rv) WRITE_TO(STDOUT, "%S", task);
+    return rv;
+}
+
diff --git a/inbuild/supervisor-module/Chapter 2/Copy Errors.w b/inbuild/supervisor-module/Chapter 2/Copy Errors.w index a4e7857bd..5f0f6dd94 100644 --- a/inbuild/supervisor-module/Chapter 2/Copy Errors.w +++ b/inbuild/supervisor-module/Chapter 2/Copy Errors.w @@ -16,6 +16,7 @@ fields are blank. @e METADATA_MALFORMED_CE @e EXT_MISWORDED_CE @e EXT_BAD_DIRNAME_CE +@e EXT_BAD_FILENAME_CE @e EXT_TITLE_TOO_LONG_CE @e EXT_AUTHOR_TOO_LONG_CE @e LANGUAGE_UNAVAILABLE_CE @@ -131,6 +132,7 @@ void CopyErrors::write(OUTPUT_STREAM, copy_error *CE) { case OPEN_FAILED_CE: WRITE("unable to open file %f", CE->details_file); break; case EXT_MISWORDED_CE: WRITE("extension misworded: %S", CE->details); break; case EXT_BAD_DIRNAME_CE: WRITE("extension directory name wrong: %S", CE->details); break; + case EXT_BAD_FILENAME_CE: WRITE("extension filename wrong: %S", CE->details); break; case METADATA_MALFORMED_CE: WRITE("%S has incorrect metadata: %S", CE->copy->edition->work->genre->genre_name, CE->details); break; case EXT_TITLE_TOO_LONG_CE: WRITE("title too long: %d characters (max is %d)", diff --git a/inbuild/supervisor-module/Chapter 4/Extension Bundle Manager.w b/inbuild/supervisor-module/Chapter 4/Extension Bundle Manager.w index bd661e958..fe329524c 100644 --- a/inbuild/supervisor-module/Chapter 4/Extension Bundle Manager.w +++ b/inbuild/supervisor-module/Chapter 4/Extension Bundle Manager.w @@ -50,7 +50,7 @@ inform_extension *ExtensionBundleManager::from_copy(inbuild_copy *C) { dictionary *eb_copy_cache = NULL; inbuild_copy *ExtensionBundleManager::new_copy(text_stream *name, pathname *P, - inbuild_nest *N, semantic_version_number apparent_V) { + inbuild_nest *N, semantic_version_number apparent_V, int force_renaming) { if (eb_copy_cache == NULL) eb_copy_cache = Dictionaries::new(16, FALSE); TEMPORARY_TEXT(key) WRITE_TO(key, "%p", P); @@ -64,13 +64,41 @@ inbuild_copy *ExtensionBundleManager::new_copy(text_stream *name, pathname *P, inbuild_edition *edition = Editions::new(work, VersionNumbers::null()); C = Copies::new_in_path(edition, P, N); Extensions::scan(C); + + if ((force_renaming == FALSE) && + (VersionNumbers::ne(apparent_V, C->edition->version))) + force_renaming = TRUE; + + if ((force_renaming == FALSE) && + (Str::ne(name, C->edition->work->title))) + force_renaming = TRUE; + + if (force_renaming == TRUE) { + TEMPORARY_TEXT(new_name) + TEMPORARY_TEXT(new_version) + WRITE_TO(new_version, "%v", &(C->edition->version)); + LOOP_THROUGH_TEXT(pos, new_version) + if (Str::get(pos) == '.') + Str::put(pos, '_'); + WRITE_TO(new_name, "%S-v%S.i7xd", C->edition->work->title, new_version); + if (Extensions::rename_directory(P, new_name)) { + Str::clear(key); + WRITE_TO(key, "%p", P); + apparent_V = C->edition->version; + name = C->edition->work->title; + } + DISCARD_TEXT(new_name) + DISCARD_TEXT(new_version) + } + Dictionaries::create(eb_copy_cache, key); Dictionaries::write_value(eb_copy_cache, key, C); if (VersionNumbers::is_null(apparent_V)) { TEMPORARY_TEXT(error_text) WRITE_TO(error_text, "an extension in directory format must have a directory name ending " - "'-vN', giving the version number: for example, 'Advanced Algebra-v2_3_6'"); + "'-vN.i7xd', giving the version number: for example, " + "'Advanced Algebra-v2_3_6.i7xd'"); Copies::attach_error(C, CopyErrors::new_T(EXT_BAD_DIRNAME_CE, -1, error_text)); DISCARD_TEXT(error_text) } else if (VersionNumbers::ne(apparent_V, C->edition->version)) { @@ -108,19 +136,69 @@ void ExtensionBundleManager::claim_as_copy(inbuild_genre *gen, inbuild_copy **C, copy name. = + +void ExtensionBundleManager::dismantle_name(text_stream *name, + text_stream *to_title, text_stream *to_ext, semantic_version_number *to_V) { + TEMPORARY_TEXT(ext) + TEMPORARY_TEXT(title) + int pos = Str::len(name) - 1, dotpos = -1; + while (pos >= 0) { + wchar_t c = Str::get_at(name, pos); + if (Platform::is_folder_separator(c)) break; + if (c == '.') dotpos = pos; + pos--; + } + if (dotpos >= 0) { + Str::substr(ext, Str::at(name, dotpos+1), Str::end(name)); + Str::substr(title, Str::start(name), Str::at(name, dotpos)); + } else { + WRITE_TO(title, "%S", name); + } + semantic_version_number V = VersionNumbers::null(); + match_results mr = Regexp::create_mr(); + if (Regexp::match(&mr, title, L"(%c+)-v([0-9_]+)")) { + Str::clear(title); + WRITE_TO(title, "%S", mr.exp[0]); + LOOP_THROUGH_TEXT(pos, mr.exp[1]) + if (Str::get(pos) == '_') + Str::put(pos, '.'); + V = VersionNumbers::from_text(mr.exp[1]); + } + Regexp::dispose_of(&mr); + Str::copy(to_title, title); + Str::copy(to_ext, ext); + if (to_V) *to_V = V; + DISCARD_TEXT(ext) + DISCARD_TEXT(title) +} + inbuild_copy *ExtensionBundleManager::claim_folder_as_copy(pathname *P, inbuild_nest *N) { filename *canary = Filenames::in(P, I"extension_metadata.json"); - if (TextFiles::exists(canary)) { - text_stream *name = Str::duplicate(Pathnames::directory_name(P)); - semantic_version_number V = VersionNumbers::null(); - match_results mr = Regexp::create_mr(); - if (Regexp::match(&mr, name, L"(%c+)-v([0-9_]+)")) { - name = Str::duplicate(mr.exp[0]); - V = VersionNumbers::from_text(mr.exp[1]); + TEMPORARY_TEXT(ext) + TEMPORARY_TEXT(title) + semantic_version_number V = VersionNumbers::null(); + ExtensionBundleManager::dismantle_name(Pathnames::directory_name(P), title, ext, &V); + inbuild_copy *C = NULL; + if ((Str::eq_insensitive(ext, I"i7xd")) || (TextFiles::exists(canary))) { + int force_renaming = NOT_APPLICABLE; + if (Nests::get_tag(N) == MATERIALS_NEST_TAG) force_renaming = FALSE; + if (Str::eq_insensitive(ext, I"i7xd") == FALSE) { + if (Nests::get_tag(N) == MATERIALS_NEST_TAG) { + force_renaming = TRUE; + } else { + TEMPORARY_TEXT(error_text) + WRITE_TO(error_text, + "the extension directory '%S' needs to have the extension '.i7xd' added to its name", + Pathnames::directory_name(P)); + Copies::attach_error(C, CopyErrors::new_T(EXT_BAD_DIRNAME_CE, -1, error_text)); + DISCARD_TEXT(error_text) + } } - return ExtensionBundleManager::new_copy(name, P, N, V); + C = ExtensionBundleManager::new_copy(title, P, N, V, force_renaming); } - return NULL; + DISCARD_TEXT(ext) + DISCARD_TEXT(title) + return C; } @h Searching. diff --git a/inbuild/supervisor-module/Chapter 5/Extension Services.w b/inbuild/supervisor-module/Chapter 5/Extension Services.w index 71ddae546..ca15976dd 100644 --- a/inbuild/supervisor-module/Chapter 5/Extension Services.w +++ b/inbuild/supervisor-module/Chapter 5/Extension Services.w @@ -53,8 +53,12 @@ void Extensions::scan(inbuild_copy *C) { TEMPORARY_TEXT(claimed_title) TEMPORARY_TEXT(reqs) semantic_version_number V = VersionNumbers::null(); + filename *extension_source_filename = NULL; @; @; +if (C->location_if_path) { +WRITE_TO(STDERR, "Okay %p: %S\n", C->location_if_path, C->edition->work->title); +} DISCARD_TEXT(claimed_author_name) DISCARD_TEXT(claimed_title) DISCARD_TEXT(reqs) @@ -62,6 +66,9 @@ void Extensions::scan(inbuild_copy *C) { if (Works::is_standard_rules(C->edition->work)) E->standard = TRUE; @; +if (C->location_if_path) { +WRITE_TO(STDERR, "Now %S\n", C->edition->work->title); +} @; } @@ -101,18 +108,35 @@ alone, and the version number is returned. filename *F = Extensions::main_source_file(C); FILE *EXTF = Filenames::fopen_caseless(F, "r"); if (EXTF == NULL) { - Copies::attach_error(C, CopyErrors::new_F(OPEN_FAILED_CE, -1, F)); + filename *A = Extensions::alternative_source_file(C); + if (A) { + EXTF = Filenames::fopen_caseless(A, "r"); + if (EXTF) { + extension_source_filename = A; + @; + fclose(EXTF); + } else Copies::attach_error(C, CopyErrors::new_F(OPEN_FAILED_CE, -1, F)); + } else Copies::attach_error(C, CopyErrors::new_F(OPEN_FAILED_CE, -1, F)); } else { - @; - @; - @; + extension_source_filename = F; + @; fclose(EXTF); - if (Str::len(version_text) > 0) { - V = VersionNumbers::from_text(version_text); - if (VersionNumbers::is_null(V)) { + } + if (C->location_if_path) { + WRITE_TO(STDERR, "Read from %f\n", extension_source_filename); + TEMPORARY_TEXT(correct_leafname) + WRITE_TO(correct_leafname, "%S.i7x", claimed_title); + if (Str::ne_insensitive(correct_leafname, Filenames::get_leafname(extension_source_filename))) { + int allow = FALSE; + if ((C->nest_of_origin) && (Nests::get_tag(C->nest_of_origin) == MATERIALS_NEST_TAG) && + (Extensions::rename_file(extension_source_filename, correct_leafname))) + allow = TRUE; + if (allow == FALSE) { TEMPORARY_TEXT(error_text) - WRITE_TO(error_text, "the version number '%S' is malformed", version_text); - Copies::attach_error(C, CopyErrors::new_T(EXT_MISWORDED_CE, -1, error_text)); + WRITE_TO(error_text, + "the source file in the extension is called '%S' but should be '%S' to match the contents", + Filenames::get_leafname(extension_source_filename), correct_leafname); + Copies::attach_error(C, CopyErrors::new_T(EXT_BAD_FILENAME_CE, -1, error_text)); DISCARD_TEXT(error_text) } } @@ -120,6 +144,20 @@ alone, and the version number is returned. DISCARD_TEXT(titling_line) DISCARD_TEXT(version_text) +@ = + @; + @; + @; + if (Str::len(version_text) > 0) { + V = VersionNumbers::from_text(version_text); + if (VersionNumbers::is_null(V)) { + TEMPORARY_TEXT(error_text) + WRITE_TO(error_text, "the version number '%S' is malformed", version_text); + Copies::attach_error(C, CopyErrors::new_T(EXT_MISWORDED_CE, -1, error_text)); + DISCARD_TEXT(error_text) + } + } + @ The titling line is terminated by any of |0A|, |0D|, |0A 0D| or |0D 0A|, or by the local |\n| for good measure. @@ -399,6 +437,28 @@ filename *Extensions::main_source_file(inbuild_copy *C) { return F; } +filename *Extensions::alternative_source_file(inbuild_copy *C) { + pathname *P = C->location_if_path; + if (P) { + P = Pathnames::down(P, I"Source"); + linked_list *L = Directories::listing(P); + filename *A = NULL; int c = 0; + text_stream *entry; + LOOP_OVER_LINKED_LIST(entry, text_stream, L) { + if (Platform::is_folder_separator(Str::get_last_char(entry)) == FALSE) { + filename *F = Filenames::in(P, entry); + TEMPORARY_TEXT(fext) + Filenames::write_extension(fext, F); + if (Str::eq_insensitive(fext, I".i7x")) { + A = F; c++; + } + } + } + if (c == 1) return A; + } + return NULL; +} + pathname *Extensions::materials_path(inform_extension *E) { pathname *P = E->as_copy->location_if_path; if (P) P = Pathnames::down(P, I"Materials"); @@ -773,3 +833,22 @@ int Extensions::satisfies(inform_extension *E) { if (E == NULL) return FALSE; return Requirements::meets(E->as_copy->edition, E->must_satisfy); } + +@h File hierarchy tidying. + += +int Extensions::rename_directory(pathname *P, text_stream *new_name) { + TEMPORARY_TEXT(task) + WRITE_TO(task, "(Changing directory name '%p' to '%S')\n", P, new_name); + int rv = Directories::rename(P, new_name); + if (rv) WRITE_TO(STDOUT, "%S", task); + return rv; +} + +int Extensions::rename_file(filename *F, text_stream *new_name) { + TEMPORARY_TEXT(task) + WRITE_TO(task, "(Changing file name '%f' to '%S')\n", F, new_name); + int rv = Filenames::rename(F, new_name); + if (rv) WRITE_TO(STDOUT, "%S", task); + return rv; +} diff --git a/inform7/Figures/memory-diagnostics.txt b/inform7/Figures/memory-diagnostics.txt index 2eb0ccd70..b6b8d67d5 100644 --- a/inform7/Figures/memory-diagnostics.txt +++ b/inform7/Figures/memory-diagnostics.txt @@ -248,7 +248,7 @@ Total memory consumption was 121571K = 119 MB 100.0% was used for memory not allocated for objects: - 56.9% text stream storage 70953260 bytes in 482723 claims + 56.9% text stream storage 70953168 bytes in 482725 claims 4.2% dictionary storage 5315584 bytes in 7623 claims ---- sorting 2720 bytes in 387 claims 5.7% source text 7200000 bytes in 3 claims diff --git a/inform7/Figures/timings-diagnostics.txt b/inform7/Figures/timings-diagnostics.txt index 1329c04dc..36ac825b8 100644 --- a/inform7/Figures/timings-diagnostics.txt +++ b/inform7/Figures/timings-diagnostics.txt @@ -1,33 +1,31 @@ 100.0% in inform7 run - 70.6% in compilation to Inter - 50.4% in //Sequence::undertake_queued_tasks// - 4.6% in //MajorNodes::pre_pass// - 3.3% in //MajorNodes::pass_1// - 1.7% in //ImperativeDefinitions::assess_all// - 1.5% in //RTPhrasebook::compile_entries// - 1.3% in //RTKindConstructors::compile// - 0.9% in //Sequence::lint_inter// - 0.5% in //MajorNodes::pass_2// - 0.5% in //Sequence::undertake_queued_tasks// - 0.5% in //Sequence::undertake_queued_tasks// - 0.5% in //World::stage_V// - 0.3% in //ImperativeDefinitions::compile_first_block// - 0.1% in //Closures::compile_closures// - 0.1% in //CompletionModule::compile// - 0.1% in //InferenceSubjects::emit_all// - 0.1% in //RTKindConstructors::compile_permissions// - 0.1% in //Task::make_built_in_kind_constructors// - 2.9% not specifically accounted for - 25.8% in running Inter pipeline - 9.9% in step 14/15: generate inform6 -> auto.inf - 5.8% in step 5/15: load-binary-kits - 5.2% in step 6/15: make-synoptic-module - 1.7% in step 9/15: make-identifiers-unique - 0.3% in step 12/15: eliminate-redundant-operations - 0.3% in step 4/15: compile-splats - 0.3% in step 7/15: shorten-wiring - 0.3% in step 8/15: detect-indirect-calls - 0.1% in step 11/15: eliminate-redundant-labels - 1.4% not specifically accounted for - 2.9% in supervisor - 0.6% not specifically accounted for + 70.4% in compilation to Inter + 50.2% in //Sequence::undertake_queued_tasks// + 4.9% in //MajorNodes::pre_pass// + 3.2% in //MajorNodes::pass_1// + 1.8% in //ImperativeDefinitions::assess_all// + 1.6% in //RTPhrasebook::compile_entries// + 1.4% in //RTKindConstructors::compile// + 1.0% in //Sequence::lint_inter// + 0.6% in //MajorNodes::pass_2// + 0.6% in //Sequence::undertake_queued_tasks// + 0.4% in //ImperativeDefinitions::compile_first_block// + 0.4% in //Sequence::undertake_queued_tasks// + 0.4% in //World::stage_V// + 0.2% in //CompletionModule::compile// + 0.2% in //InferenceSubjects::emit_all// + 0.2% in //RTKindConstructors::compile_permissions// + 3.0% not specifically accounted for + 26.0% in running Inter pipeline + 9.8% in step 14/15: generate inform6 -> auto.inf + 5.7% in step 5/15: load-binary-kits + 5.5% in step 6/15: make-synoptic-module + 1.8% in step 9/15: make-identifiers-unique + 0.4% in step 12/15: eliminate-redundant-operations + 0.4% in step 4/15: compile-splats + 0.4% in step 7/15: shorten-wiring + 0.4% in step 8/15: detect-indirect-calls + 0.2% in step 11/15: eliminate-redundant-labels + 1.3% not specifically accounted for + 3.0% in supervisor + 0.5% not specifically accounted for diff --git a/inform7/Internal/Inter/BasicInformExtrasKit/kit_metadata.json b/inform7/Internal/Inter/BasicInformExtrasKit/kit_metadata.json index 7eeeb8097..3d24fb64f 100644 --- a/inform7/Internal/Inter/BasicInformExtrasKit/kit_metadata.json +++ b/inform7/Internal/Inter/BasicInformExtrasKit/kit_metadata.json @@ -2,7 +2,7 @@ "is": { "type": "kit", "title": "BasicInformExtrasKit", - "version": "10.2.0-beta+6W22" + "version": "10.2.0-beta+6W24" }, "kit-details": { "has-priority": 1 diff --git a/inform7/Internal/Inter/BasicInformKit/kit_metadata.json b/inform7/Internal/Inter/BasicInformKit/kit_metadata.json index 0e920f754..2daa97fbe 100644 --- a/inform7/Internal/Inter/BasicInformKit/kit_metadata.json +++ b/inform7/Internal/Inter/BasicInformKit/kit_metadata.json @@ -2,7 +2,7 @@ "is": { "type": "kit", "title": "BasicInformKit", - "version": "10.2.0-beta+6W22" + "version": "10.2.0-beta+6W24" }, "needs": [ { "unless": { diff --git a/inform7/Internal/Inter/CommandParserKit/kit_metadata.json b/inform7/Internal/Inter/CommandParserKit/kit_metadata.json index 5ad86f093..5fc79464e 100644 --- a/inform7/Internal/Inter/CommandParserKit/kit_metadata.json +++ b/inform7/Internal/Inter/CommandParserKit/kit_metadata.json @@ -2,7 +2,7 @@ "is": { "type": "kit", "title": "CommandParserKit", - "version": "10.2.0-beta+6W22" + "version": "10.2.0-beta+6W24" }, "needs": [ { "need": { diff --git a/inform7/Internal/Inter/EnglishLanguageKit/kit_metadata.json b/inform7/Internal/Inter/EnglishLanguageKit/kit_metadata.json index cec0dd7ef..d72c22536 100644 --- a/inform7/Internal/Inter/EnglishLanguageKit/kit_metadata.json +++ b/inform7/Internal/Inter/EnglishLanguageKit/kit_metadata.json @@ -2,7 +2,7 @@ "is": { "type": "kit", "title": "EnglishLanguageKit", - "version": "10.2.0-beta+6W22" + "version": "10.2.0-beta+6W24" }, "needs": [ { "need": { diff --git a/inform7/Internal/Inter/WorldModelKit/kit_metadata.json b/inform7/Internal/Inter/WorldModelKit/kit_metadata.json index e7fa9ddf6..9d3968821 100644 --- a/inform7/Internal/Inter/WorldModelKit/kit_metadata.json +++ b/inform7/Internal/Inter/WorldModelKit/kit_metadata.json @@ -2,7 +2,7 @@ "is": { "type": "kit", "title": "WorldModelKit", - "version": "10.2.0-beta+6W22" + "version": "10.2.0-beta+6W24" }, "needs": [ { "need": { diff --git a/inform7/core-module/Chapter 2/Problems With Source Text.w b/inform7/core-module/Chapter 2/Problems With Source Text.w index 104f03d10..ad2be6e18 100644 --- a/inform7/core-module/Chapter 2/Problems With Source Text.w +++ b/inform7/core-module/Chapter 2/Problems With Source Text.w @@ -43,6 +43,15 @@ void SourceProblems::issue_problems_arising(inbuild_copy *C) { "what that directory is called (which is not fine). Specifically, %2."); Problems::issue_problem_end(); break; + case EXT_BAD_FILENAME_CE: + Problems::quote_work(1, CE->copy->found_by->work); + Problems::quote_stream(2, CE->details); + StandardProblems::handmade_problem(Task::syntax_tree(), _p_(Untestable)); + Problems::issue_problem_segment( + "The extension %1, which your source text makes use of, has the wrong " + "filename for its source text. Specifically, %2."); + Problems::issue_problem_end(); + break; case METADATA_MALFORMED_CE: if (CE->copy->found_by) { Problems::quote_work(1, CE->copy->found_by->work);