From 55338a19d2a43d3c702abc8f1965b055d4d22954 Mon Sep 17 00:00:00 2001 From: Graham Nelson Date: Fri, 28 Jul 2023 00:43:38 +0100 Subject: [PATCH] inbuild -modernise --- README.md | 2 +- build.txt | 4 +- docs/WorldModelKit/S-tst.html | 4 +- docs/inbuild/1-mn.html | 8 +- docs/inbuild/M-rc.html | 1 + docs/indoc/2-exm.html | 14 +- docs/supervisor-module/2-cps.html | 18 +- docs/supervisor-module/2-edt.html | 4 +- docs/supervisor-module/2-gnr.html | 9 + docs/supervisor-module/2-rqr.html | 2 +- docs/supervisor-module/2-wrk.html | 2 +- docs/supervisor-module/3-bg.html | 2 +- docs/supervisor-module/4-ebm.html | 10 +- docs/supervisor-module/4-em.html | 10 +- docs/supervisor-module/5-es.html | 20 +- docs/supervisor-module/5-ks.html | 2 +- docs/supervisor-module/5-ls.html | 2 +- docs/supervisor-module/5-ps.html | 2 +- docs/supervisor-module/5-ps2.html | 2 +- docs/supervisor-module/5-ts.html | 2 +- docs/supervisor-module/7-dc.html | 8 +- docs/supervisor-module/7-dr.html | 4 +- docs/supervisor-module/7-dt.html | 4 +- docs/supervisor-module/7-eip.html | 4 +- docs/supervisor-module/7-tc.html | 339 ++++++++++++++++++ docs/supervisor-module/7-ti.html | 4 +- docs/supervisor-module/7-tm.html | 4 +- docs/supervisor-module/index.html | 7 +- docs/words-module/3-tff.html | 35 ++ inbuild/Chapter 1/Main.w | 6 + inbuild/Figures/help.txt | 1 + inbuild/supervisor-module/Chapter 2/Copies.w | 15 + inbuild/supervisor-module/Chapter 2/Genres.w | 8 + .../Chapter 4/Extension Bundle Manager.w | 8 + .../Chapter 4/Extension Manager.w | 8 + .../Chapter 5/Extension Services.w | 12 + .../Chapter 7/The Converter.w | 225 ++++++++++++ inbuild/supervisor-module/Contents.w | 3 +- indoc/Chapter 2/Examples.w | 14 +- indoc/Tests/Basic/Examples/(Recipes).txt | 3 - indoc/Tests/Basic/Examples/Example.txt | 7 +- inform7/Figures/memory-diagnostics.txt | 74 ++-- inform7/Figures/timings-diagnostics.txt | 28 +- .../Inter/Architecture16Kit/kit_metadata.json | 2 +- .../Inter/Architecture32Kit/kit_metadata.json | 2 +- .../Inter/BasicInformKit/kit_metadata.json | 2 +- .../Inter/CommandParserKit/kit_metadata.json | 2 +- .../EnglishLanguageKit/kit_metadata.json | 2 +- .../Inter/WorldModelKit/kit_metadata.json | 2 +- .../words-module/Chapter 3/Text From Files.w | 31 ++ 50 files changed, 870 insertions(+), 114 deletions(-) create mode 100644 docs/supervisor-module/7-tc.html create mode 100644 inbuild/supervisor-module/Chapter 7/The Converter.w delete mode 100644 indoc/Tests/Basic/Examples/(Recipes).txt diff --git a/README.md b/README.md index f1649620d..3c42b712a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Inform 7 -[Version](notes/versioning.md): 10.2.0-beta+6W91 'Krypton' (27 July 2023) +[Version](notes/versioning.md): 10.2.0-beta+6W92 'Krypton' (28 July 2023) ## About Inform diff --git a/build.txt b/build.txt index 8275ec9af..15b73a9ec 100644 --- a/build.txt +++ b/build.txt @@ -1,3 +1,3 @@ Prerelease: beta -Build Date: 27 July 2023 -Build Number: 6W91 +Build Date: 28 July 2023 +Build Number: 6W92 diff --git a/docs/WorldModelKit/S-tst.html b/docs/WorldModelKit/S-tst.html index 91b5a4663..924a74187 100644 --- a/docs/WorldModelKit/S-tst.html +++ b/docs/WorldModelKit/S-tst.html @@ -532,7 +532,7 @@ of 0 (off, the default) to 5. [ TraceOffSub; parser_trace=0; say__p = 1; "Trace off."; ]; -

§18. Tree Command. TREE prints out the I6 object tree, though this is not always very helpful +

§18. Tree Command. SHOWTREE prints out the I6 object tree, though this is not always very helpful in I7 terms. It should arguably be withdrawn, but doesn't seem to do any harm.

@@ -641,7 +641,7 @@ not picked up) and able to accept multiple objects. * number -> TraceLevel * 'on' -> TraceOn * 'off' -> TraceOff; -Verb meta 'tree' +Verb meta 'showtree' * -> XTree * scope=testcommandnoun -> XTree; #Ifdef TARGET_GLULX; diff --git a/docs/inbuild/1-mn.html b/docs/inbuild/1-mn.html index 6d400cb25..71dca886a 100644 --- a/docs/inbuild/1-mn.html +++ b/docs/inbuild/1-mn.html @@ -195,7 +195,7 @@ error in this case.
     if (documentation_dest == NULL)
         Errors::fatal("need to specify '-document-to' directory");
-    compiled_documentation *cd = DocumentationCompiler::compile_from_file(
+    compiled_documentation *cd = DocumentationCompiler::compile_from_file(
         documentation_source, NULL);
     if (cd) DocumentationRenderer::as_HTML(documentation_dest, cd, NULL);
 
@@ -255,6 +255,7 @@ utility functions in the enum COPY_TO_TTASK enum SYNC_TO_TTASK enum DOCUMENT_TTASK +enum MODERNISE_TTASK

§2.5.2. Carry out the required task on the copy C2.5.2 =

@@ -294,6 +295,7 @@ utility functions in the if (documentation_dest == NULL) Errors::fatal("need to specify '-document-to' directory"); Copies::document(C, documentation_dest); break; + case MODERNISE_TTASK: Copies::modernise(OUT, C); break; } @@ -494,6 +496,7 @@ other options to the selection defined here. enum DOCUMENT_CLSW enum DOCUMENT_FROM_CLSW enum DOCUMENT_TO_CLSW +enum MODERNISE_CLSW

Read the command line2.10 =

@@ -574,6 +577,8 @@ other options to the selection defined here. L"generate documentation from documentation source file X"); CommandLine::declare_switch(DOCUMENT_TO_CLSW, L"document-to", 2, L"divert generated documentation to directory X"); + CommandLine::declare_switch(MODERNISE_CLSW, L"modernise", 1, + L"update copies to the newest available format"); Supervisor::declare_options(); CommandLine::read(argc, argv, NULL, &Main::option, &Main::bareword); @@ -596,6 +601,7 @@ other options to the selection defined here. case USE_LOCATE_CLSW: inbuild_task = USE_LOCATE_TTASK; break; case BUILD_LOCATE_CLSW: inbuild_task = BUILD_LOCATE_TTASK; break; case DOCUMENT_CLSW: inbuild_task = DOCUMENT_TTASK; break; + case MODERNISE_CLSW: inbuild_task = MODERNISE_TTASK; break; case ARCHIVE_TO_CLSW: destination_nest = Nests::new(Pathnames::from_text(arg)); inbuild_task = ARCHIVE_TO_TTASK; diff --git a/docs/inbuild/M-rc.html b/docs/inbuild/M-rc.html index 3c9f1d858..ab29e10b3 100644 --- a/docs/inbuild/M-rc.html +++ b/docs/inbuild/M-rc.html @@ -81,6 +81,7 @@ and those not documented in this manual are covered in that one. -install install extension within the Inform GUI apps -json X write output of -inspect to a JSON file in X (or '-' for stdout) -matching X apply to all works in nest(s) matching requirement X +-modernise update copies to the newest available format -preprocess-app X use CSS suitable for app platform X (macos, windows, linux) -preprocess-html X construct HTML page based on X -preprocess-html-to X set destination for -preprocess-html to be X diff --git a/docs/indoc/2-exm.html b/docs/indoc/2-exm.html index 53fb328ee..90714bc53 100644 --- a/docs/indoc/2-exm.html +++ b/docs/indoc/2-exm.html @@ -230,11 +230,12 @@ Inform documentation. Errors::in_text_file("'Example:' should be on line 1", tfp); } else if (Str::eq(field, I"Location")) { text_stream *sname = content; - section *S = Dictionaries::read_value(volumes[0]->sections_by_name, sname); - if (S) E->example_belongs_to_section[0] = S; - else { + if (Dictionaries::find(volumes[0]->sections_by_name, sname) == NULL) { E->example_belongs_to_section[0] = NULL; Errors::in_text_file("example belongs to an unknown section", tfp); + } else { + section *S = Dictionaries::read_value(volumes[0]->sections_by_name, sname); + if (S) E->example_belongs_to_section[0] = S; } E->ex_filename = ehs->ef; } else if (Str::eq(field, I"Description")) { @@ -247,11 +248,12 @@ Inform documentation. E->ex_subtitle = Str::duplicate(content); } else if (Str::eq(field, I"RecipeLocation")) { text_stream *sname = content; - section *S = Dictionaries::read_value(volumes[1]->sections_by_name, sname); - if (S) E->example_belongs_to_section[1] = S; - else { + if (Dictionaries::find(volumes[1]->sections_by_name, sname) == NULL) { E->example_belongs_to_section[1] = NULL; Errors::in_text_file("example belongs to an unknown section", tfp); + } else { + section *S = Dictionaries::read_value(volumes[1]->sections_by_name, sname); + if (S) E->example_belongs_to_section[1] = S; } } else { Errors::in_text_file("no such example details field", tfp); diff --git a/docs/supervisor-module/2-cps.html b/docs/supervisor-module/2-cps.html index f85dda432..365f6bf28 100644 --- a/docs/supervisor-module/2-cps.html +++ b/docs/supervisor-module/2-cps.html @@ -91,7 +91,7 @@ stored here. CLASS_DEFINITION } inbuild_copy; - +

§2. Copies are created by the managers for the respective genres, usually when claiming. If you are a manager, do not call this...

@@ -417,6 +417,22 @@ its main task: building an Inform project. VOID_METHOD_CALL(C->edition->work->genre, GENRE_DOCUMENT_MTID, C, dest); } +

§17. And -modernise: +

+ +
+void Copies::modernise(OUTPUT_STREAM, inbuild_copy *C) {
+    TEMPORARY_TEXT(work_done)
+    WRITE_TO(work_done, "%X: ", C->edition->work);
+    int rv = FALSE;
+    INT_METHOD_CALL(rv, C->edition->work->genre, GENRE_MODERNISE_MTID, C, work_done);
+    if (rv == FALSE) {
+        WRITE("already up to date\n", C->edition->work);
+    }
+    WRITE("%S", work_done);
+    DISCARD_TEXT(work_done)
+}
+
diff --git a/docs/supervisor-module/2-edt.html b/docs/supervisor-module/2-edt.html index 9ee4afaf7..8e40d8a72 100644 --- a/docs/supervisor-module/2-edt.html +++ b/docs/supervisor-module/2-edt.html @@ -95,7 +95,7 @@ might work with all VMs, while version 8 required a 32-bit architecture. } } - +

§2. When a copy is to be duplicated into a nest N, we need to work out where to put it. For example, version 2.1 of the extension Marbles by Steve Hogarth would go into N/Extensions/Steve Hogarth/Marbles-v2_1.i7x. The following @@ -109,7 +109,7 @@ contributes only the un-filename-extended leafname canonical_leaves_have_versions = which; } -void Editions::write_canonical_leaf(OUTPUT_STREAM, inbuild_edition *E) { +void Editions::write_canonical_leaf(OUTPUT_STREAM, inbuild_edition *E) { WRITE("%S", E->work->title); if ((canonical_leaves_have_versions) && (VersionNumbers::is_null(E->version) == FALSE)) { diff --git a/docs/supervisor-module/2-gnr.html b/docs/supervisor-module/2-gnr.html index 0c2d87244..d17070ac8 100644 --- a/docs/supervisor-module/2-gnr.html +++ b/docs/supervisor-module/2-gnr.html @@ -273,6 +273,15 @@ the Inbuild command-line options VOID_METHOD_TYPE(GENRE_DOCUMENT_MTID, inbuild_genre *gen, inbuild_copy *C, pathname *dest) +

§14. This performs some sort of automatic-update to the latest format: +

+ +
enum GENRE_MODERNISE_MTID
+
+
+INT_METHOD_TYPE(GENRE_MODERNISE_MTID,
+    inbuild_genre *gen, inbuild_copy *C, text_stream *OUT)
+
diff --git a/docs/supervisor-module/2-rqr.html b/docs/supervisor-module/2-rqr.html index 38bd32101..5f2146d47 100644 --- a/docs/supervisor-module/2-rqr.html +++ b/docs/supervisor-module/2-rqr.html @@ -74,7 +74,7 @@ we can give a semantic version number range: CLASS_DEFINITION } inbuild_requirement; - +

§2. Here are some creators:

diff --git a/docs/supervisor-module/2-wrk.html b/docs/supervisor-module/2-wrk.html index b21adbb3b..67f201c20 100644 --- a/docs/supervisor-module/2-wrk.html +++ b/docs/supervisor-module/2-wrk.html @@ -86,7 +86,7 @@ combination of the textual names and the hash code: CLASS_DEFINITION } inbuild_work; - +

§2. Each work structure is written only once, and its title and author name are not subsequently altered.

diff --git a/docs/supervisor-module/3-bg.html b/docs/supervisor-module/3-bg.html index d380c5c8c..4395b4eb3 100644 --- a/docs/supervisor-module/3-bg.html +++ b/docs/supervisor-module/3-bg.html @@ -111,7 +111,7 @@ compiled, is a file vertex. CLASS_DEFINITION } build_vertex; - +

§2. Creation. First, the three colours of vertex.

diff --git a/docs/supervisor-module/4-ebm.html b/docs/supervisor-module/4-ebm.html index b9c8d1710..c82f9a835 100644 --- a/docs/supervisor-module/4-ebm.html +++ b/docs/supervisor-module/4-ebm.html @@ -59,7 +59,7 @@ function togglePopup(material_id) {

Claiming and creating copies of the kit genre: used for kits of precompiled Inter code.

-
+

§1. Genre definition. The extension_bundle_genre can be summarised as follows.

@@ -76,6 +76,7 @@ function togglePopup(material_id) { METHOD_ADD(extension_bundle_genre, GENRE_BUILDING_SOON_MTID, ExtensionBundleManager::building_soon); METHOD_ADD(extension_bundle_genre, GENRE_DOCUMENT_MTID, ExtensionBundleManager::document); METHOD_ADD(extension_bundle_genre, GENRE_READ_SOURCE_TEXT_FOR_MTID, ExtensionBundleManager::read_source_text_for); + METHOD_ADD(extension_bundle_genre, GENRE_MODERNISE_MTID, ExtensionBundleManager::modernise); } void ExtensionBundleManager::write_work(inbuild_genre *gen, OUTPUT_STREAM, inbuild_work *work) { @@ -561,6 +562,13 @@ directory, we need to rsync Extensions::document(Extensions::from_copy(C), dest); } +

§11. Modernisation.

+ +
+int ExtensionBundleManager::modernise(inbuild_genre *gen, inbuild_copy *C, text_stream *OUT) {
+    return Extensions::modernise(Extensions::from_copy(C), OUT);
+}
+
diff --git a/docs/supervisor-module/4-em.html b/docs/supervisor-module/4-em.html index 74ef042e7..2ce678981 100644 --- a/docs/supervisor-module/4-em.html +++ b/docs/supervisor-module/4-em.html @@ -59,7 +59,7 @@ function togglePopup(material_id) {

Claiming and creating copies of the extension genre: used for Inform 7 extensions.

-
+

§1. Genre definition. The extension_genre can be summarised as follows. Copies consist of single files. These are recognised by having the filename extension .i7x. They are @@ -88,6 +88,7 @@ later on, as needed, just for extensions of interest: see below. METHOD_ADD(extension_genre, GENRE_READ_SOURCE_TEXT_FOR_MTID, ExtensionManager::read_source_text_for); METHOD_ADD(extension_genre, GENRE_BUILDING_SOON_MTID, ExtensionManager::building_soon); METHOD_ADD(extension_genre, GENRE_DOCUMENT_MTID, ExtensionManager::document); + METHOD_ADD(extension_genre, GENRE_MODERNISE_MTID, ExtensionManager::modernise); } void ExtensionManager::write_work(inbuild_genre *gen, OUTPUT_STREAM, inbuild_work *work) { @@ -328,6 +329,13 @@ the current VM settings. Extensions::document(Extensions::from_copy(C), dest); } +

§11. Modernisation.

+ +
+int ExtensionManager::modernise(inbuild_genre *gen, inbuild_copy *C, text_stream *OUT) {
+    return Extensions::modernise(Extensions::from_copy(C), OUT);
+}
+
diff --git a/docs/supervisor-module/5-es.html b/docs/supervisor-module/5-es.html index 3d6cabb08..1a901682b 100644 --- a/docs/supervisor-module/5-es.html +++ b/docs/supervisor-module/5-es.html @@ -59,7 +59,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: @@ -96,7 +96,7 @@ length to one character less than the following constants: CLASS_DEFINITION } inform_extension; -

+

§2. This is called as soon as a new copy C of the extension genre is created. We scan the extension file for the title, author, version number and any compatibility notes given (such as "for Glulx only"). @@ -726,7 +726,7 @@ in a minimal sort of way, with just an -inform_extension *Extensions::from_copy(inbuild_copy *C) { +inform_extension *Extensions::from_copy(inbuild_copy *C) { inform_extension *ext = ExtensionBundleManager::from_copy(C); if (ext == NULL) ext = ExtensionManager::from_copy(C); return ext; @@ -942,7 +942,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);
@@ -1196,6 +1196,18 @@ its requirements (even though it did when first loaded). This tests for that:
     return rv;
 }
 
+

§23. Modernisation.

+ +
+int Extensions::modernise(inform_extension *E, text_stream *OUT) {
+    if (E->as_copy->edition->work->genre == extension_bundle_genre) {
+        WRITE("already in directory format\n");
+        return TRUE;
+    }
+    ExtensionConverter::go(E, OUT);
+    return TRUE;
+}
+
diff --git a/docs/supervisor-module/5-ks.html b/docs/supervisor-module/5-ks.html index d98ba141b..7840f3b27 100644 --- a/docs/supervisor-module/5-ks.html +++ b/docs/supervisor-module/5-ks.html @@ -100,7 +100,7 @@ module of inform7 CLASS_DEFINITION } inform_kit; - +

§2. Kits come with an "if this then that" service for including other kits, and we represent rules with the following:

diff --git a/docs/supervisor-module/5-ls.html b/docs/supervisor-module/5-ls.html index add9869df..d873f7fe3 100644 --- a/docs/supervisor-module/5-ls.html +++ b/docs/supervisor-module/5-ls.html @@ -89,7 +89,7 @@ small resource folder called its "bundle". (This includes English.) CLASS_DEFINITION } inform_language; - +

§2. This is called as soon as a new copy C of the language genre is created.

diff --git a/docs/supervisor-module/5-ps.html b/docs/supervisor-module/5-ps.html index 95eaef820..efd0a55b8 100644 --- a/docs/supervisor-module/5-ps.html +++ b/docs/supervisor-module/5-ps.html @@ -72,7 +72,7 @@ in the following structure. CLASS_DEFINITION } inform_pipeline; - +

§2. This is called as soon as a new copy C of the language genre is created.

diff --git a/docs/supervisor-module/5-ps2.html b/docs/supervisor-module/5-ps2.html index e02962adb..a13ec439b 100644 --- a/docs/supervisor-module/5-ps2.html +++ b/docs/supervisor-module/5-ps2.html @@ -94,7 +94,7 @@ function togglePopup(material_id) { CLASS_DEFINITION } inform_project; - +

§2. This is called as soon as a new copy C of the language genre is created. It doesn't actually do any scanning to speak of, in fact: we may eventually learn a lot about the project, but for now we simply initialise to bland diff --git a/docs/supervisor-module/5-ts.html b/docs/supervisor-module/5-ts.html index 10cbae906..5d2058407 100644 --- a/docs/supervisor-module/5-ts.html +++ b/docs/supervisor-module/5-ts.html @@ -72,7 +72,7 @@ stored in the following structure. CLASS_DEFINITION } inform_template; -

+

§2. This is called as soon as a new copy C of the language genre is created.

diff --git a/docs/supervisor-module/7-dc.html b/docs/supervisor-module/7-dc.html index 572631e4c..7a8fb15f8 100644 --- a/docs/supervisor-module/7-dc.html +++ b/docs/supervisor-module/7-dc.html @@ -56,7 +56,7 @@ function togglePopup(material_id) {
+

To compile documentation from the textual syntax in an extension into a tree.

§1. We will actually wrap the result in the following structure, but @@ -102,7 +102,7 @@ it's really not much more than the tree produced by CLASS_DEFINITION } satellite_test_case; -

+
  • The structure compiled_documentation is accessed in 2/wrk, 2/edt, 2/cps, 2/rqr, 2/jm, 3/bg, 3/is, 4/ebm, 4/km, 4/lm, 4/pm, 4/pbm, 4/pfm, 4/tm, 5/es, 5/ks, 5/ls, 5/ps2, 6/inc, 7/eip, 7/ti, 7/tc, 7/dr and here.
  • The structure satellite_test_case is accessed in 5/ks and here.

§2. We can compile either from a file...

@@ -180,7 +180,7 @@ it's really not much more than the tree produced by int past_header; } example_scanning_state; -
  • The structure example_scanning_state is accessed in 5/es, 7/dt, 7/dr and here.
+
  • The structure example_scanning_state is accessed in 5/es, 7/tc, 7/dt, 7/dr and here.

§2.1.2. Scan the example for its header and content2.1.2 =

@@ -773,7 +773,7 @@ of a code sample either, since a non-blank indented line is needed to trigger on }
diff --git a/docs/supervisor-module/7-dr.html b/docs/supervisor-module/7-dr.html index 36d982337..2899ba79b 100644 --- a/docs/supervisor-module/7-dr.html +++ b/docs/supervisor-module/7-dr.html @@ -56,7 +56,7 @@ function togglePopup(material_id) {
+

To render a passage of extension documentation as HTML.


@@ -801,7 +801,7 @@ had its infamous PNG transparency bug.) }
diff --git a/docs/supervisor-module/7-dt.html b/docs/supervisor-module/7-dt.html index 6d3bfc327..6417bb1d1 100644 --- a/docs/supervisor-module/7-dt.html +++ b/docs/supervisor-module/7-dt.html @@ -56,7 +56,7 @@ function togglePopup(material_id) {
+

A data structure to hold segments of Inform resource documentation.


@@ -439,7 +439,7 @@ if not.
  • The structure dc_find_example_task is private to this section.
diff --git a/docs/supervisor-module/7-eip.html b/docs/supervisor-module/7-eip.html index 3607f1021..ea0dc722f 100644 --- a/docs/supervisor-module/7-eip.html +++ b/docs/supervisor-module/7-eip.html @@ -56,7 +56,7 @@ function togglePopup(material_id) {
+

To generate the index page for the extension mini-website, which is the home page displayed in the Extensions tab for the Inform GUI apps.


@@ -844,7 +844,7 @@ handed to qsort }
diff --git a/docs/supervisor-module/7-tc.html b/docs/supervisor-module/7-tc.html new file mode 100644 index 000000000..ae7499203 --- /dev/null +++ b/docs/supervisor-module/7-tc.html @@ -0,0 +1,339 @@ + + + + The Converter + + + + + + + + + + + + + + + + + + +
+ + +

To convert an extension from the traditional one-file format to the more modern directory-based format.

+ +

§1. In all paths through this function, we must write either good news or bad +to OUT: bad news usually following from the file system refusing to create +a text file or directory. +

+ +
+void ExtensionConverter::go(inform_extension *E, text_stream *OUT) {
+    Extensions::read_source_text_for(E);
+
+    TEMPORARY_TEXT(dirname)
+    Editions::write_canonical_leaf(dirname, E->as_copy->edition);
+    WRITE_TO(dirname, ".i7xd");
+    pathname *P_home = Pathnames::down(Filenames::up(E->as_copy->location_if_file), dirname);
+    pathname *P_source =        Pathnames::down(P_home, I"Source");
+    pathname *P_documentation = Pathnames::down(P_home, I"Documentation");
+    pathname *P_examples =      Pathnames::down(P_documentation, I"Examples");
+
+    Create the home directory for the extension1.1;
+    Construct JSON metadata and write it as a file1.2;
+    Write out the source code1.3;
+    Write out the documentation1.4;
+
+    WRITE("migrated to directory '%S'\n", dirname);
+    DISCARD_TEXT(dirname)
+}
+
+

§1.1. Create the home directory for the extension1.1 = +

+ +
+    if (Directories::exists(P_home)) {
+        WRITE("can't make this into %S because directory '%p' already exists", dirname, P_home);
+        return;
+    }
+    if (ExtensionConverter::mkdir(E, OUT, P_home) == FALSE) return;
+
+
  • This code is used in §1.
+

§1.2. Construct JSON metadata and write it as a file1.2 = +

+ +
+    JSON_value *JM = JSON::new_object();
+    JSON_value *is = JSON::new_object();
+    JSON::add_to_object(JM, I"is", is);
+    JSON::add_to_object(is, I"type", JSON::new_string(I"extension"));
+    JSON::add_to_object(is, I"title", JSON::new_string(E->as_copy->edition->work->title));
+    JSON::add_to_object(is, I"author", JSON::new_string(E->as_copy->edition->work->author_name));
+    semantic_version_number V = E->as_copy->edition->version;
+    if (VersionNumbers::is_null(V) == FALSE) {
+        TEMPORARY_TEXT(vt)
+        WRITE_TO(vt, "%v", &V);
+        JSON::add_to_object(is, I"version", JSON::new_string(vt));
+        DISCARD_TEXT(vt)
+    }
+
+    filename *JF = Filenames::in(P_home, I"extension_metadata.json");
+    text_stream JSONF_struct;
+    text_stream *JS = &JSONF_struct;
+    if (ExtensionConverter::fopen(E, OUT, JS, JF) == FALSE) return;
+    JSON::encode(JS, JM);
+    STREAM_CLOSE(JS);
+
+
  • This code is used in §1.
+

§1.3. Write out the source code1.3 = +

+ +
+    if (ExtensionConverter::mkdir(E, OUT, P_source) == FALSE) return;
+    TEMPORARY_TEXT(sleaf)
+    Editions::write_canonical_leaf(sleaf, E->as_copy->edition);
+    WRITE_TO(sleaf, ".i7x");
+    filename *SF = Filenames::in(P_source, sleaf);
+    DISCARD_TEXT(sleaf)
+    text_stream SRCF_struct;
+    text_stream *SS = &SRCF_struct;
+    if (ExtensionConverter::fopen(E, OUT, SS, SF) == FALSE) return;
+    WRITE_TO(SS, "%S", E->read_into_file->body_text);
+    STREAM_CLOSE(SS);
+
+
  • This code is used in §1.
+

§1.4. Write out the documentation1.4 = +

+ +
+    text_stream *source = E->read_into_file->torn_off_documentation;
+    if (Str::is_whitespace(source) == FALSE) {
+        if (ExtensionConverter::mkdir(E, OUT, P_documentation) == FALSE) return;
+        filename *F_documentation = Filenames::in(P_documentation, I"Documentation.txt");
+        text_stream DOCF_struct;
+        text_stream *S_documentation = &DOCF_struct;
+        if (ExtensionConverter::fopen(E, OUT, S_documentation, F_documentation) == FALSE) return;
+        Filter the documentation through1.4.1;
+        STREAM_CLOSE(S_documentation);
+    }
+
+
  • This code is used in §1.
+

§1.4.1. We work through the documentation attached to the original extension (if +there was any) in two passes. On pass 1, we do nothing except to count the +number of section and chapter headings. On pass 2, we split up the content +into the main file and individual example files. +

+ +

Filter the documentation through1.4.1 = +

+ +
+    int chapter_count = 0, section_count = 0, example_count = 0;
+    text_stream EG_struct;
+    for (int pass = 1; pass <= 2; pass++) {
+        text_stream *dest = S_documentation, *S_example = NULL;
+        TEMPORARY_TEXT(line)
+        int indentation = 0, space_count = 0;
+        for (int i=0; i<Str::len(source); i++) {
+            wchar_t c = Str::get_at(source, i);
+            if (c == '\n') {
+                Line read1.4.1.1;
+                Str::clear(line);
+                indentation = 0; space_count = 0;
+            } else if ((Str::len(line) == 0) && (Characters::is_whitespace(c))) {
+                if (c == '\t') indentation++;
+                if (c == ' ') space_count++;
+                if (space_count == 4) { indentation++; space_count = 0; }
+            } else {
+                PUT_TO(line, c);
+            }
+        }
+        if (Str::len(line) > 0) Line read1.4.1.1;
+        DISCARD_TEXT(line)
+        if (pass == 2) End any example1.4.1.2;
+    }
+
+
  • This code is used in §1.4.
+

§1.4.1.1. Line read1.4.1.1 = +

+ +
+    Str::trim_white_space(line);
+    match_results mr = Regexp::create_mr();
+    if ((Regexp::match(&mr, line, L"Section *: *(%c+?)")) ||
+        (Regexp::match(&mr, line, L"Section *- *(%c+?)"))) {
+        if (pass == 1) section_count++;
+        if (pass == 2) End any example1.4.1.2;
+    } else if ((Regexp::match(&mr, line, L"Chapter *: *(%c+?)")) ||
+        (Regexp::match(&mr, line, L"Chapter *- *(%c+?)"))) {
+        if (pass == 1) chapter_count++;
+        if (pass == 2) {
+            End any example1.4.1.2;
+            Amend this to a section heading1.4.1.1.1;
+        }
+    }
+    if (pass == 2) {
+        if ((Regexp::match(&mr, line, L"Example *: *(%**) *(%c+?)")) ||
+            (Regexp::match(&mr, line, L"Example *- *(%**) *(%c+?)")))
+            Deal with an example heading1.4.1.1.2
+        else
+            Copy the line out to the appropriate file1.4.1.1.3;
+    }
+
+    Regexp::dispose_of(&mr);
+
+
  • This code is used in §1.4.1 (twice).
+

§1.4.1.1.1. Old single-file extensions tended to use Chapters, intended as major headings, +with not much content, and not to use Sections at all. Because we now split +off Chapters into their own HTML pages, we don't want that, so when converting +an old extension which has chapters but no sections, we downgrade all the chapters +to sections. +

+ +

Amend this to a section heading1.4.1.1.1 = +

+ +
+    if (section_count == 0) {
+        Str::clear(line);
+        WRITE_TO(line, "Section: %S", mr.exp[0]);
+    }
+
+ +

§1.4.1.1.2. Deal with an example heading1.4.1.1.2 = +

+ +
+    text_stream *stars = mr.exp[0];
+    text_stream *title = mr.exp[1];
+    text_stream *desc = NULL;
+    match_results mr2 = Regexp::create_mr();
+    if (Regexp::match(&mr2, title, L" *(%c+?) - *(%c+) *")) {
+        title = mr2.exp[0];
+        desc = mr2.exp[1];
+    }
+    Begin an example1.4.1.1.2.1;
+    Regexp::dispose_of(&mr2);
+
+ +

§1.4.1.1.2.1. Begin an example1.4.1.1.2.1 = +

+ +
+    if ((example_count++ == 0) &&
+        (ExtensionConverter::mkdir(E, OUT, P_examples) == FALSE)) return;
+
+    TEMPORARY_TEXT(eleaf)
+    for (int i=0, last_was_ws=TRUE; i<Str::len(title); i++) {
+        wchar_t c = Str::get_at(title, i);
+        if (Characters::is_whitespace(c)) { last_was_ws = TRUE; continue; }
+        if (last_was_ws) c = Characters::toupper(c);
+        last_was_ws = FALSE;
+        if ((c == '.') || (c == ',') || (c == ';') || (c == '"') || (c == '\''))
+            continue;
+        PUT_TO(eleaf, c);
+    }
+    WRITE_TO(eleaf, ".txt");
+    filename *F_example = Filenames::in(P_examples, eleaf);
+    DISCARD_TEXT(eleaf)
+    S_example = &EG_struct;
+    if (ExtensionConverter::fopen(E, OUT, S_example, F_example) == FALSE) return;
+    dest = S_example;
+    WRITE_TO(dest, "Example: %S %S\n", stars, title);
+    if (Str::len(desc) > 0) WRITE_TO(dest, "Description: %S\n", desc);
+
+ +

§1.4.1.2. End any example1.4.1.2 = +

+ +
+    if (S_example) {
+        STREAM_CLOSE(S_example);
+        S_example = NULL;
+        dest = S_documentation;
+    }
+
+ +

§1.4.1.1.3. Note that we amend the old-style paste marker to the new style, though both +are legal. +

+ +

Copy the line out to the appropriate file1.4.1.1.3 = +

+ +
+    for (int i=0; i<indentation; i++) PUT_TO(dest, '\t');
+    match_results mr2 = Regexp::create_mr();
+    if ((indentation == 1) && (Regexp::match(&mr2, line, L"%* *: *(%c+?)"))) {
+        WRITE_TO(dest, "{*}%S\n", mr2.exp[0]);
+    } else {
+        WRITE_TO(dest, "%S\n", line);
+    }
+    Regexp::dispose_of(&mr2);
+
+ +

§2. And this provides bad news texts when the two main file-system operations fail. +

+ +
+int ExtensionConverter::mkdir(inform_extension *E, text_stream *OUT, pathname *P) {
+    if (Pathnames::create_in_file_system(P) == FALSE) {
+        WRITE("unable to create directory '%p'", P);
+        return FALSE;
+    }
+    return TRUE;
+}
+
+int ExtensionConverter::fopen(inform_extension *E, text_stream *OUT, text_stream *S, filename *F) {
+    if (STREAM_OPEN_TO_FILE(S, F, UTF8_ENC) == FALSE) {
+        WRITE("unable to create file '%f'", F);
+        return FALSE;
+    }
+    return TRUE;
+}
+
+ + +
+ + + diff --git a/docs/supervisor-module/7-ti.html b/docs/supervisor-module/7-ti.html index 438dd50a5..827b1314b 100644 --- a/docs/supervisor-module/7-ti.html +++ b/docs/supervisor-module/7-ti.html @@ -56,7 +56,7 @@ function togglePopup(material_id) {
+

To produce a report page of HTML for use in the Inform GUI apps, when a resource such as an extension is inspected or installed.


@@ -596,7 +596,7 @@ produces a second report. }
diff --git a/docs/supervisor-module/7-tm.html b/docs/supervisor-module/7-tm.html index 60d51fc6c..722e97667 100644 --- a/docs/supervisor-module/7-tm.html +++ b/docs/supervisor-module/7-tm.html @@ -56,7 +56,7 @@ function togglePopup(material_id) {
+

To refresh the mini-website of available extensions presented in the Inform GUI applications.

§1. The Inform GUI apps present HTML in-app documentation on extensions: in @@ -301,7 +301,7 @@ a somewhat enigmatic icon. }

diff --git a/docs/supervisor-module/index.html b/docs/supervisor-module/index.html index a37aa32d6..0b76e8994 100644 --- a/docs/supervisor-module/index.html +++ b/docs/supervisor-module/index.html @@ -298,7 +298,7 @@
  • - Chapter 7: Extension Indexing

    + Chapter 7: Extension Management

    • @@ -315,6 +315,11 @@ The Installer - To produce a report page of HTML for use in the Inform GUI apps, when a resource such as an extension is inspected or installed.

    • +
    • +

      + The Converter - + To convert an extension from the traditional one-file format to the more modern directory-based format.

      +
    • Documentation Tree - diff --git a/docs/words-module/3-tff.html b/docs/words-module/3-tff.html index f9f57c657..bc5faa2fa 100644 --- a/docs/words-module/3-tff.html +++ b/docs/words-module/3-tff.html @@ -73,6 +73,7 @@ in a source_file int words_of_quoted_text; word count for text in double-quotes FILE *handle; file handle while open general_pointer your_ref; for the client to attach some meaning + struct text_stream *body_text; struct text_stream *torn_off_documentation; CLASS_DEFINITION } source_file; @@ -102,6 +103,7 @@ instance, so they are not similarly converted. sf->your_ref = ref; sf->name = F; sf->handle = handle; + sf->body_text = Str::new(); sf->torn_off_documentation = Str::new(); source_location top_of_file; int cr, last_cr, next_cr, read_cr, newline_char = 0, torn_off = FALSE; @@ -139,6 +141,7 @@ instance, so they are not similarly converted. if (torn_off) { PUT_TO(sf->torn_off_documentation, cr); } else { + PUT_TO(sf->body_text, cr); Lexer::feed_triplet(last_cr, cr, next_cr); torn_off = Lexer::detect_tear_off(); } @@ -147,6 +150,7 @@ instance, so they are not similarly converted. sf->text_read = Lexer::feed_ends(TRUE, leaf); Word count the new material2.1; + if (torn_off) Trim the edges of the tear tidily2.2; return sf; } @@ -162,6 +166,37 @@ quoted text (i.e., their text within double-quotes). sf->words_of_source += TextFromFiles::word_count(wc);

      • This code is used in §2.
      +

      §2.2. Trim the edges of the tear tidily2.2 = +

      + +
      +    Str::trim_white_space(sf->torn_off_documentation);
      +    if (Str::get_first_char(sf->torn_off_documentation) == '\n')
      +        Str::delete_first_character(sf->torn_off_documentation);
      +    PUT_TO(sf->torn_off_documentation, '\n');
      +    Str::trim_white_space(sf->body_text);
      +    int quads = 0, i = Str::len(sf->body_text)-1;
      +    for (; i >= 0; i--) {
      +        if ((Str::get_at(sf->body_text, i) == '-') &&
      +            (Str::get_at(sf->body_text, i+1) == '-') &&
      +            (Str::get_at(sf->body_text, i+2) == '-') &&
      +            (Str::get_at(sf->body_text, i+3) == '-')) {
      +            i--; quads++;
      +            break;
      +        }
      +    }
      +    for (; i >= 0; i--) {
      +        if ((Str::get_at(sf->body_text, i) == '-') &&
      +            (Str::get_at(sf->body_text, i+1) == '-') &&
      +            (Str::get_at(sf->body_text, i+2) == '-') &&
      +            (Str::get_at(sf->body_text, i+3) == '-')) {
      +            i--; quads++;
      +            break;
      +        }
      +    }
      +    if (quads == 2) Str::truncate(sf->body_text, i);
      +
      +
      • This code is used in §2.

      §3. A much simpler version:

      diff --git a/inbuild/Chapter 1/Main.w b/inbuild/Chapter 1/Main.w index 10083bfbf..f25a65116 100644 --- a/inbuild/Chapter 1/Main.w +++ b/inbuild/Chapter 1/Main.w @@ -172,6 +172,7 @@ utility functions in the //supervisor// module, which we call. @e COPY_TO_TTASK @e SYNC_TO_TTASK @e DOCUMENT_TTASK +@e MODERNISE_TTASK @ = text_stream *OUT = STDOUT; @@ -208,6 +209,7 @@ utility functions in the //supervisor// module, which we call. if (documentation_dest == NULL) Errors::fatal("need to specify '-document-to' directory"); Copies::document(C, documentation_dest); break; + case MODERNISE_TTASK: Copies::modernise(OUT, C); break; } @ = @@ -401,6 +403,7 @@ other options to the selection defined here. @e DOCUMENT_CLSW @e DOCUMENT_FROM_CLSW @e DOCUMENT_TO_CLSW +@e MODERNISE_CLSW @ = CommandLine::declare_heading( @@ -478,6 +481,8 @@ other options to the selection defined here. L"generate documentation from documentation source file X"); CommandLine::declare_switch(DOCUMENT_TO_CLSW, L"document-to", 2, L"divert generated documentation to directory X"); + CommandLine::declare_switch(MODERNISE_CLSW, L"modernise", 1, + L"update copies to the newest available format"); Supervisor::declare_options(); CommandLine::read(argc, argv, NULL, &Main::option, &Main::bareword); @@ -498,6 +503,7 @@ void Main::option(int id, int val, text_stream *arg, void *state) { case USE_LOCATE_CLSW: inbuild_task = USE_LOCATE_TTASK; break; case BUILD_LOCATE_CLSW: inbuild_task = BUILD_LOCATE_TTASK; break; case DOCUMENT_CLSW: inbuild_task = DOCUMENT_TTASK; break; + case MODERNISE_CLSW: inbuild_task = MODERNISE_TTASK; break; case ARCHIVE_TO_CLSW: destination_nest = Nests::new(Pathnames::from_text(arg)); inbuild_task = ARCHIVE_TO_TTASK; diff --git a/inbuild/Figures/help.txt b/inbuild/Figures/help.txt index e21a57cf2..16d1f9ee4 100644 --- a/inbuild/Figures/help.txt +++ b/inbuild/Figures/help.txt @@ -22,6 +22,7 @@ usage: inbuild [-TASK] TARGET1 TARGET2 ... -install install extension within the Inform GUI apps -json X write output of -inspect to a JSON file in X (or '-' for stdout) -matching X apply to all works in nest(s) matching requirement X +-modernise update copies to the newest available format -preprocess-app X use CSS suitable for app platform X (macos, windows, linux) -preprocess-html X construct HTML page based on X -preprocess-html-to X set destination for -preprocess-html to be X diff --git a/inbuild/supervisor-module/Chapter 2/Copies.w b/inbuild/supervisor-module/Chapter 2/Copies.w index 719ae9611..d64ed11c5 100644 --- a/inbuild/supervisor-module/Chapter 2/Copies.w +++ b/inbuild/supervisor-module/Chapter 2/Copies.w @@ -345,3 +345,18 @@ void Copies::overwrite_error(inbuild_copy *C, inbuild_nest *N) { void Copies::document(inbuild_copy *C, pathname *dest) { VOID_METHOD_CALL(C->edition->work->genre, GENRE_DOCUMENT_MTID, C, dest); } + +@ And |-modernise|: + += +void Copies::modernise(OUTPUT_STREAM, inbuild_copy *C) { + TEMPORARY_TEXT(work_done) + WRITE_TO(work_done, "%X: ", C->edition->work); + int rv = FALSE; + INT_METHOD_CALL(rv, C->edition->work->genre, GENRE_MODERNISE_MTID, C, work_done); + if (rv == FALSE) { + WRITE("already up to date\n", C->edition->work); + } + WRITE("%S", work_done); + DISCARD_TEXT(work_done) +} diff --git a/inbuild/supervisor-module/Chapter 2/Genres.w b/inbuild/supervisor-module/Chapter 2/Genres.w index f6adcc3ff..15b281e88 100644 --- a/inbuild/supervisor-module/Chapter 2/Genres.w +++ b/inbuild/supervisor-module/Chapter 2/Genres.w @@ -195,3 +195,11 @@ VOID_METHOD_TYPE(GENRE_COPY_TO_NEST_MTID, = VOID_METHOD_TYPE(GENRE_DOCUMENT_MTID, inbuild_genre *gen, inbuild_copy *C, pathname *dest) + +@ This performs some sort of automatic-update to the latest format: + +@e GENRE_MODERNISE_MTID + += +INT_METHOD_TYPE(GENRE_MODERNISE_MTID, + inbuild_genre *gen, inbuild_copy *C, text_stream *OUT) diff --git a/inbuild/supervisor-module/Chapter 4/Extension Bundle Manager.w b/inbuild/supervisor-module/Chapter 4/Extension Bundle Manager.w index be25ecf09..772001a91 100644 --- a/inbuild/supervisor-module/Chapter 4/Extension Bundle Manager.w +++ b/inbuild/supervisor-module/Chapter 4/Extension Bundle Manager.w @@ -18,6 +18,7 @@ void ExtensionBundleManager::start(void) { METHOD_ADD(extension_bundle_genre, GENRE_BUILDING_SOON_MTID, ExtensionBundleManager::building_soon); METHOD_ADD(extension_bundle_genre, GENRE_DOCUMENT_MTID, ExtensionBundleManager::document); METHOD_ADD(extension_bundle_genre, GENRE_READ_SOURCE_TEXT_FOR_MTID, ExtensionBundleManager::read_source_text_for); + METHOD_ADD(extension_bundle_genre, GENRE_MODERNISE_MTID, ExtensionBundleManager::modernise); } void ExtensionBundleManager::write_work(inbuild_genre *gen, OUTPUT_STREAM, inbuild_work *work) { @@ -482,3 +483,10 @@ void ExtensionBundleManager::read_source_text_for(inbuild_genre *G, inbuild_copy void ExtensionBundleManager::document(inbuild_genre *gen, inbuild_copy *C, pathname *dest) { Extensions::document(Extensions::from_copy(C), dest); } + +@h Modernisation. + += +int ExtensionBundleManager::modernise(inbuild_genre *gen, inbuild_copy *C, text_stream *OUT) { + return Extensions::modernise(Extensions::from_copy(C), OUT); +} diff --git a/inbuild/supervisor-module/Chapter 4/Extension Manager.w b/inbuild/supervisor-module/Chapter 4/Extension Manager.w index a09dacda8..720a9de3a 100644 --- a/inbuild/supervisor-module/Chapter 4/Extension Manager.w +++ b/inbuild/supervisor-module/Chapter 4/Extension Manager.w @@ -27,6 +27,7 @@ void ExtensionManager::start(void) { METHOD_ADD(extension_genre, GENRE_READ_SOURCE_TEXT_FOR_MTID, ExtensionManager::read_source_text_for); METHOD_ADD(extension_genre, GENRE_BUILDING_SOON_MTID, ExtensionManager::building_soon); METHOD_ADD(extension_genre, GENRE_DOCUMENT_MTID, ExtensionManager::document); + METHOD_ADD(extension_genre, GENRE_MODERNISE_MTID, ExtensionManager::modernise); } void ExtensionManager::write_work(inbuild_genre *gen, OUTPUT_STREAM, inbuild_work *work) { @@ -259,3 +260,10 @@ void ExtensionManager::read_source_text_for(inbuild_genre *G, inbuild_copy *C) { void ExtensionManager::document(inbuild_genre *gen, inbuild_copy *C, pathname *dest) { Extensions::document(Extensions::from_copy(C), dest); } + +@h Modernisation. + += +int ExtensionManager::modernise(inbuild_genre *gen, inbuild_copy *C, text_stream *OUT) { + return Extensions::modernise(Extensions::from_copy(C), OUT); +} diff --git a/inbuild/supervisor-module/Chapter 5/Extension Services.w b/inbuild/supervisor-module/Chapter 5/Extension Services.w index 6e046baf5..0ec67dbf4 100644 --- a/inbuild/supervisor-module/Chapter 5/Extension Services.w +++ b/inbuild/supervisor-module/Chapter 5/Extension Services.w @@ -992,3 +992,15 @@ int Extensions::rename_file(filename *F, text_stream *new_name) { if (rv) WRITE_TO(STDOUT, "%S", task); return rv; } + +@h Modernisation. + += +int Extensions::modernise(inform_extension *E, text_stream *OUT) { + if (E->as_copy->edition->work->genre == extension_bundle_genre) { + WRITE("already in directory format\n"); + return TRUE; + } + ExtensionConverter::go(E, OUT); + return TRUE; +} diff --git a/inbuild/supervisor-module/Chapter 7/The Converter.w b/inbuild/supervisor-module/Chapter 7/The Converter.w new file mode 100644 index 000000000..d4340ffa2 --- /dev/null +++ b/inbuild/supervisor-module/Chapter 7/The Converter.w @@ -0,0 +1,225 @@ +[ExtensionConverter::] The Converter. + +To convert an extension from the traditional one-file format to the more +modern directory-based format. + +@ In all paths through this function, we must write either good news or bad +to |OUT|: bad news usually following from the file system refusing to create +a text file or directory. + += +void ExtensionConverter::go(inform_extension *E, text_stream *OUT) { + Extensions::read_source_text_for(E); + + TEMPORARY_TEXT(dirname) + Editions::write_canonical_leaf(dirname, E->as_copy->edition); + WRITE_TO(dirname, ".i7xd"); + pathname *P_home = Pathnames::down(Filenames::up(E->as_copy->location_if_file), dirname); + pathname *P_source = Pathnames::down(P_home, I"Source"); + pathname *P_documentation = Pathnames::down(P_home, I"Documentation"); + pathname *P_examples = Pathnames::down(P_documentation, I"Examples"); + + @; + @; + @; + @; + + WRITE("migrated to directory '%S'\n", dirname); + DISCARD_TEXT(dirname) +} + +@ = + if (Directories::exists(P_home)) { + WRITE("can't make this into %S because directory '%p' already exists", dirname, P_home); + return; + } + if (ExtensionConverter::mkdir(E, OUT, P_home) == FALSE) return; + +@ = + JSON_value *JM = JSON::new_object(); + JSON_value *is = JSON::new_object(); + JSON::add_to_object(JM, I"is", is); + JSON::add_to_object(is, I"type", JSON::new_string(I"extension")); + JSON::add_to_object(is, I"title", JSON::new_string(E->as_copy->edition->work->title)); + JSON::add_to_object(is, I"author", JSON::new_string(E->as_copy->edition->work->author_name)); + semantic_version_number V = E->as_copy->edition->version; + if (VersionNumbers::is_null(V) == FALSE) { + TEMPORARY_TEXT(vt) + WRITE_TO(vt, "%v", &V); + JSON::add_to_object(is, I"version", JSON::new_string(vt)); + DISCARD_TEXT(vt) + } + + filename *JF = Filenames::in(P_home, I"extension_metadata.json"); + text_stream JSONF_struct; + text_stream *JS = &JSONF_struct; + if (ExtensionConverter::fopen(E, OUT, JS, JF) == FALSE) return; + JSON::encode(JS, JM); + STREAM_CLOSE(JS); + +@ = + if (ExtensionConverter::mkdir(E, OUT, P_source) == FALSE) return; + TEMPORARY_TEXT(sleaf) + Editions::write_canonical_leaf(sleaf, E->as_copy->edition); + WRITE_TO(sleaf, ".i7x"); + filename *SF = Filenames::in(P_source, sleaf); + DISCARD_TEXT(sleaf) + text_stream SRCF_struct; + text_stream *SS = &SRCF_struct; + if (ExtensionConverter::fopen(E, OUT, SS, SF) == FALSE) return; + WRITE_TO(SS, "%S", E->read_into_file->body_text); + STREAM_CLOSE(SS); + +@ = + text_stream *source = E->read_into_file->torn_off_documentation; + if (Str::is_whitespace(source) == FALSE) { + if (ExtensionConverter::mkdir(E, OUT, P_documentation) == FALSE) return; + filename *F_documentation = Filenames::in(P_documentation, I"Documentation.txt"); + text_stream DOCF_struct; + text_stream *S_documentation = &DOCF_struct; + if (ExtensionConverter::fopen(E, OUT, S_documentation, F_documentation) == FALSE) return; + @; + STREAM_CLOSE(S_documentation); + } + +@ We work through the documentation attached to the original extension (if +there was any) in two passes. On pass 1, we do nothing except to count the +number of section and chapter headings. On pass 2, we split up the content +into the main file and individual example files. + +@ = + int chapter_count = 0, section_count = 0, example_count = 0; + text_stream EG_struct; + for (int pass = 1; pass <= 2; pass++) { + text_stream *dest = S_documentation, *S_example = NULL; + TEMPORARY_TEXT(line) + int indentation = 0, space_count = 0; + for (int i=0; i; + Str::clear(line); + indentation = 0; space_count = 0; + } else if ((Str::len(line) == 0) && (Characters::is_whitespace(c))) { + if (c == '\t') indentation++; + if (c == ' ') space_count++; + if (space_count == 4) { indentation++; space_count = 0; } + } else { + PUT_TO(line, c); + } + } + if (Str::len(line) > 0) @; + DISCARD_TEXT(line) + if (pass == 2) @; + } + +@ = + Str::trim_white_space(line); + match_results mr = Regexp::create_mr(); + if ((Regexp::match(&mr, line, L"Section *: *(%c+?)")) || + (Regexp::match(&mr, line, L"Section *- *(%c+?)"))) { + if (pass == 1) section_count++; + if (pass == 2) @; + } else if ((Regexp::match(&mr, line, L"Chapter *: *(%c+?)")) || + (Regexp::match(&mr, line, L"Chapter *- *(%c+?)"))) { + if (pass == 1) chapter_count++; + if (pass == 2) { + @; + @; + } + } + if (pass == 2) { + if ((Regexp::match(&mr, line, L"Example *: *(%**) *(%c+?)")) || + (Regexp::match(&mr, line, L"Example *- *(%**) *(%c+?)"))) + @ + else + @; + } + + Regexp::dispose_of(&mr); + +@ Old single-file extensions tended to use Chapters, intended as major headings, +with not much content, and not to use Sections at all. Because we now split +off Chapters into their own HTML pages, we don't want that, so when converting +an old extension which has chapters but no sections, we downgrade all the chapters +to sections. + +@ = + if (section_count == 0) { + Str::clear(line); + WRITE_TO(line, "Section: %S", mr.exp[0]); + } + +@ = + text_stream *stars = mr.exp[0]; + text_stream *title = mr.exp[1]; + text_stream *desc = NULL; + match_results mr2 = Regexp::create_mr(); + if (Regexp::match(&mr2, title, L" *(%c+?) - *(%c+) *")) { + title = mr2.exp[0]; + desc = mr2.exp[1]; + } + @; + Regexp::dispose_of(&mr2); + +@ = + if ((example_count++ == 0) && + (ExtensionConverter::mkdir(E, OUT, P_examples) == FALSE)) return; + + TEMPORARY_TEXT(eleaf) + for (int i=0, last_was_ws=TRUE; i 0) WRITE_TO(dest, "Description: %S\n", desc); + +@ = + if (S_example) { + STREAM_CLOSE(S_example); + S_example = NULL; + dest = S_documentation; + } + +@ Note that we amend the old-style paste marker to the new style, though both +are legal. + +@ = + for (int i=0; isections_by_name, sname); - if (S) E->example_belongs_to_section[0] = S; - else { + if (Dictionaries::find(volumes[0]->sections_by_name, sname) == NULL) { E->example_belongs_to_section[0] = NULL; Errors::in_text_file("example belongs to an unknown section", tfp); + } else { + section *S = Dictionaries::read_value(volumes[0]->sections_by_name, sname); + if (S) E->example_belongs_to_section[0] = S; } E->ex_filename = ehs->ef; } else if (Str::eq(field, I"Description")) { @@ -170,11 +171,12 @@ void Examples::examples_helper(text_stream *line, text_file_position *tfp, void E->ex_subtitle = Str::duplicate(content); } else if (Str::eq(field, I"RecipeLocation")) { text_stream *sname = content; - section *S = Dictionaries::read_value(volumes[1]->sections_by_name, sname); - if (S) E->example_belongs_to_section[1] = S; - else { + if (Dictionaries::find(volumes[1]->sections_by_name, sname) == NULL) { E->example_belongs_to_section[1] = NULL; Errors::in_text_file("example belongs to an unknown section", tfp); + } else { + section *S = Dictionaries::read_value(volumes[1]->sections_by_name, sname); + if (S) E->example_belongs_to_section[1] = S; } } else { Errors::in_text_file("no such example details field", tfp); diff --git a/indoc/Tests/Basic/Examples/(Recipes).txt b/indoc/Tests/Basic/Examples/(Recipes).txt deleted file mode 100644 index d88268c99..000000000 --- a/indoc/Tests/Basic/Examples/(Recipes).txt +++ /dev/null @@ -1,3 +0,0 @@ ->INTRODUCTION - *PREFACE - About the examples diff --git a/indoc/Tests/Basic/Examples/Example.txt b/indoc/Tests/Basic/Examples/Example.txt index a11f0f627..5c0049e44 100644 --- a/indoc/Tests/Basic/Examples/Example.txt +++ b/indoc/Tests/Basic/Examples/Example.txt @@ -1,6 +1,7 @@ -* Truth -(About the examples; The Über-complète Absolute Truth) -An explanation of the examples in this documentation, and the asterisks attached to them. Click the heading of the example, or the example number, to reveal the text. +Example: * The Über-complète Absolute Truth +Location: Truth +RecipeLocation: Preface +Description: An explanation of the examples in this documentation, and the asterisks attached to them. Click the heading of the example, or the example number, to reveal the text. This is the first of about 400 numbered examples. In a few cases, such as this one, they provide a little background information, but almost all demonstrate Inform source text. The techniques demonstrated tend to be included either because they are frequently asked for, or because they show how to achieve some interesting effect. diff --git a/inform7/Figures/memory-diagnostics.txt b/inform7/Figures/memory-diagnostics.txt index 81fdc8dfb..4a704a254 100644 --- a/inform7/Figures/memory-diagnostics.txt +++ b/inform7/Figures/memory-diagnostics.txt @@ -1,13 +1,13 @@ -Total memory consumption was 139642K = 136 MB +Total memory consumption was 139117K = 136 MB - ---- was used for 2123161 objects, in 374915 frames in 0 x 800K = 0K = 0 MB: + ---- was used for 2123163 objects, in 374917 frames in 0 x 800K = 0K = 0 MB: - 30.2% inter_tree_node_array 60 x 8192 = 491520 objects, 43255680 bytes - 19.2% text_stream_array 4883 x 100 = 488300 objects, 27501056 bytes - 17.7% linked_list 45352 objects, 25397120 bytes + 30.3% inter_tree_node_array 60 x 8192 = 491520 objects, 43255680 bytes + 19.3% text_stream_array 4883 x 100 = 488300 objects, 27501056 bytes + 17.8% linked_list 45352 objects, 25397120 bytes 10.0% inter_symbol_array 135 x 1024 = 138240 objects, 14381280 bytes 9.8% inter_error_stash_array 107 x 1024 = 109568 objects, 14028128 bytes - 7.4% parse_node 133796 objects, 10703680 bytes + 7.5% parse_node 133796 objects, 10703680 bytes 5.3% verb_conjugation 164 objects, 7610912 bytes 4.0% parse_node_annotation_array 357 x 500 = 178500 objects, 5723424 bytes 3.1% scan_directory 1092 objects, 4507776 bytes @@ -65,8 +65,8 @@ Total memory consumption was 139642K = 136 MB ---- spatial_data 677 objects, 64992 bytes ---- kind_constructor 79 objects, 64464 bytes ---- linked_list_item_array 4 x 1000 = 4000 objects, 64128 bytes - ---- scenes_rcd_data 1958 objects, 62656 bytes ---- actions_rcd_data 1958 objects, 62656 bytes + ---- scenes_rcd_data 1958 objects, 62656 bytes ---- booking 868 objects, 62496 bytes ---- kind_macro_definition 9 objects, 62280 bytes ---- command_grammar 130 objects, 58240 bytes @@ -103,14 +103,14 @@ Total memory consumption was 139642K = 136 MB ---- pipeline_step 15 objects, 20280 bytes ---- action_name 90 objects, 20160 bytes ---- timed_rules_rfd_data 404 objects, 19392 bytes - ---- method 401 objects, 19248 bytes + ---- method 403 objects, 19344 bytes ---- build_vertex 157 objects, 18840 bytes ---- instance 162 objects, 18144 bytes ---- pcalc_prop_deferral 86 objects, 17888 bytes ---- to_phrase_request 63 objects, 17136 bytes ---- understanding_reference_array 2 x 100 = 200 objects, 16064 bytes - ---- match_avinue_array 1 x 1000 objects, 16032 bytes ---- action_name_list_array 1 x 1000 objects, 16032 bytes + ---- match_avinue_array 1 x 1000 objects, 16032 bytes ---- adjective 140 objects, 15680 bytes ---- JSON_value 174 objects, 15312 bytes ---- booking_list 407 objects, 13024 bytes @@ -171,8 +171,8 @@ Total memory consumption was 139642K = 136 MB ---- special_meaning_holder 35 objects, 1400 bytes ---- inter_node_array 35 objects, 1400 bytes ---- JSON_requirement 42 objects, 1344 bytes - ---- table_column 16 objects, 1280 bytes ---- constant_phrase 20 objects, 1280 bytes + ---- table_column 16 objects, 1280 bytes ---- invocation_options_array 1 x 100 objects, 1224 bytes ---- direction_inference_data 30 objects, 1200 bytes ---- inbuild_search_result 29 objects, 1160 bytes @@ -181,12 +181,12 @@ Total memory consumption was 139642K = 136 MB ---- runtime_kind_structure 13 objects, 1040 bytes ---- quantifier 16 objects, 1024 bytes ---- web_md 7 objects, 1008 bytes - ---- named_rulebook_outcome 15 objects, 960 bytes ---- pipeline_stage 20 objects, 960 bytes + ---- named_rulebook_outcome 15 objects, 960 bytes ---- JSON_pair_requirement 29 objects, 928 bytes ---- control_structure_phrase 12 objects, 864 bytes - ---- kit_configuration 21 objects, 840 bytes ---- cached_understanding 21 objects, 840 bytes + ---- kit_configuration 21 objects, 840 bytes ---- phrase_option_array 1 x 100 objects, 824 bytes ---- inform_kit 7 objects, 784 bytes ---- copy_error 7 objects, 784 bytes @@ -194,38 +194,38 @@ Total memory consumption was 139642K = 136 MB ---- relation_guard 5 objects, 640 bytes ---- implication 13 objects, 624 bytes ---- chapter_md 7 objects, 616 bytes - ---- cdoc_paragraph 18 objects, 576 bytes ---- code_generation 1 object, 576 bytes - ---- module 7 objects, 560 bytes + ---- cdoc_paragraph 18 objects, 576 bytes + ---- inter_annotation_form 14 objects, 560 bytes ---- generated_segment 14 objects, 560 bytes ---- inter_warehouse_room 10 objects, 560 bytes - ---- inter_annotation_form 14 objects, 560 bytes + ---- module 7 objects, 560 bytes ---- rulebook_outcome 17 objects, 544 bytes ---- small_word_set 11 objects, 528 bytes - ---- equation 4 objects, 480 bytes ---- i6_memory_setting 15 objects, 480 bytes - ---- inbuild_genre 8 objects, 448 bytes + ---- equation 4 objects, 480 bytes ---- bp_family 14 objects, 448 bytes + ---- inbuild_genre 8 objects, 448 bytes ---- inference_family 11 objects, 440 bytes - ---- source_file 5 objects, 400 bytes + ---- source_file 5 objects, 440 bytes ---- tree_node_type 8 objects, 384 bytes ---- article_usage 8 objects, 384 bytes - ---- module_request 8 objects, 320 bytes - ---- tree_inventory 1 object, 320 bytes - ---- grammatical_category 8 objects, 320 bytes - ---- cached_kind_declaration 8 objects, 320 bytes - ---- pronoun 8 objects, 320 bytes ---- door_dir_notice 5 objects, 320 bytes + ---- pronoun 8 objects, 320 bytes + ---- cached_kind_declaration 8 objects, 320 bytes + ---- grammatical_category 8 objects, 320 bytes + ---- tree_inventory 1 object, 320 bytes + ---- module_request 8 objects, 320 bytes ---- inter_pipeline 1 object, 312 bytes - ---- cdoc_code_sample 6 objects, 288 bytes ---- up_family 9 objects, 288 bytes + ---- cdoc_code_sample 6 objects, 288 bytes + ---- explicit_bp_data 5 objects, 280 bytes ---- door_to_notice 5 objects, 280 bytes ---- compilation_unit 5 objects, 280 bytes ---- contents_entry 7 objects, 280 bytes - ---- explicit_bp_data 5 objects, 280 bytes ---- compiled_documentation 3 objects, 264 bytes - ---- verb_usage_tier 5 objects, 240 bytes ---- kit_dependency 5 objects, 240 bytes + ---- verb_usage_tier 5 objects, 240 bytes ---- inform_project 1 object, 232 bytes ---- adjective_meaning_family 7 objects, 224 bytes ---- local_block_value 4 objects, 224 bytes @@ -234,36 +234,36 @@ Total memory consumption was 139642K = 136 MB ---- attachment_instruction 5 objects, 200 bytes ---- build_skill 5 objects, 200 bytes ---- code_generator 5 objects, 200 bytes + ---- element_activation 6 objects, 192 bytes ---- cdoc_example 4 objects, 192 bytes ---- plural_dictionary_entry 4 objects, 192 bytes - ---- element_activation 6 objects, 192 bytes ---- cdoc_passage 7 objects, 168 bytes ---- cdoc_heading 3 objects, 168 bytes - ---- inter_architecture 4 objects, 160 bytes ---- imperative_defn_family 4 objects, 160 bytes + ---- inter_architecture 4 objects, 160 bytes ---- inference_subject_family 5 objects, 160 bytes ---- inbuild_nest 3 objects, 120 bytes ---- heterogeneous_tree 3 objects, 120 bytes ---- inform_language 1 object, 104 bytes - ---- article 2 objects, 80 bytes ---- compile_task_data 1 object, 80 bytes ---- group_together_function 2 objects, 80 bytes + ---- article 2 objects, 80 bytes + ---- inter_warehouse 1 object, 56 bytes ---- figures_data 1 object, 56 bytes ---- build_methodology 1 object, 56 bytes - ---- inter_warehouse 1 object, 56 bytes ---- HTML_file_state 1 object, 48 bytes ---- inform_kit_ittt 1 object, 48 bytes ---- star_invention 1 object, 48 bytes ---- kind_template_definition 1 object, 40 bytes - ---- I6_generation_data 1 object, 40 bytes - ---- by_function_bp_data 1 object, 40 bytes - ---- tree_type 1 object, 40 bytes ---- target_pragma_setting 1 object, 40 bytes + ---- by_function_bp_data 1 object, 40 bytes ---- loop_over_scope 1 object, 40 bytes + ---- tree_type 1 object, 40 bytes + ---- I6_generation_data 1 object, 40 bytes -100.0% was used for memory not allocated for objects: +99.9% was used for memory not allocated for objects: - 62.3% text stream storage 89129024 bytes in 508657 claims + 62.1% text stream storage 88591296 bytes in 508365 claims 3.8% dictionary storage 5480960 bytes in 7754 claims ---- sorting 2624 bytes in 531 claims 5.0% source text 7200000 bytes in 3 claims @@ -271,7 +271,7 @@ Total memory consumption was 139642K = 136 MB 0.1% documentation fragments 262144 bytes in 1 claim ---- linguistic stock array 81920 bytes in 2 claims ---- small word set array 105600 bytes in 22 claims - 3.2% inter symbols storage 4704736 bytes in 28298 claims + 3.3% inter symbols storage 4704736 bytes in 28298 claims 11.7% inter bytecode storage 16758228 bytes in 15 claims 4.3% inter links storage 6222976 bytes in 11 claims 0.1% inter tree location list storage 191232 bytes in 32 claims @@ -281,5 +281,5 @@ Total memory consumption was 139642K = 136 MB ---- code generation workspace for objects 3488 bytes in 19 claims 0.1% emitter array storage 281184 bytes in 2006 claims --136.-3% was overhead - -195021008 bytes = -190450K = -185 MB +-136.-8% was overhead - -195021144 bytes = -190450K = -185 MB diff --git a/inform7/Figures/timings-diagnostics.txt b/inform7/Figures/timings-diagnostics.txt index e0c3245cd..accd5f382 100644 --- a/inform7/Figures/timings-diagnostics.txt +++ b/inform7/Figures/timings-diagnostics.txt @@ -1,29 +1,29 @@ 100.0% in inform7 run - 67.8% in compilation to Inter - 45.8% in //Sequence::undertake_queued_tasks// - 4.1% in //MajorNodes::pre_pass// - 3.7% in //MajorNodes::pass_1// - 1.8% in //ImperativeDefinitions::assess_all// - 1.8% in //RTPhrasebook::compile_entries// + 67.5% in compilation to Inter + 45.9% in //Sequence::undertake_queued_tasks// + 4.2% in //MajorNodes::pre_pass// + 3.8% in //MajorNodes::pass_1// + 1.9% in //ImperativeDefinitions::assess_all// 1.5% in //RTKindConstructors::compile// + 1.5% in //RTPhrasebook::compile_entries// 1.1% in //Sequence::lint_inter// - 0.7% in //Sequence::undertake_queued_tasks// 0.3% in //CompletionModule::compile// 0.3% in //ImperativeDefinitions::compile_first_block// 0.3% in //MajorNodes::pass_2// 0.3% in //RTKindConstructors::compile_permissions// 0.3% in //Sequence::undertake_queued_tasks// + 0.3% in //Sequence::undertake_queued_tasks// 0.3% in //World::stage_V// 4.6% not specifically accounted for - 27.6% in running Inter pipeline - 9.4% in step 14/15: generate inform6 -> auto.inf - 6.8% in step 5/15: load-binary-kits - 5.6% in step 6/15: make-synoptic-module - 1.8% in step 9/15: make-identifiers-unique + 28.1% in running Inter pipeline + 9.6% in step 14/15: generate inform6 -> auto.inf + 6.9% in step 5/15: load-binary-kits + 5.7% in step 6/15: make-synoptic-module + 1.9% 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 2.3% not specifically accounted for - 3.4% in supervisor - 1.2% not specifically accounted for + 3.8% in supervisor + 0.4% not specifically accounted for diff --git a/inform7/Internal/Inter/Architecture16Kit/kit_metadata.json b/inform7/Internal/Inter/Architecture16Kit/kit_metadata.json index 1930cbd0f..f55190924 100644 --- a/inform7/Internal/Inter/Architecture16Kit/kit_metadata.json +++ b/inform7/Internal/Inter/Architecture16Kit/kit_metadata.json @@ -2,7 +2,7 @@ "is": { "type": "kit", "title": "Architecture16Kit", - "version": "10.2.0-beta+6W91" + "version": "10.2.0-beta+6W92" }, "compatibility": "16-bit", "kit-details": { diff --git a/inform7/Internal/Inter/Architecture32Kit/kit_metadata.json b/inform7/Internal/Inter/Architecture32Kit/kit_metadata.json index 2bcf61605..42b991fe7 100644 --- a/inform7/Internal/Inter/Architecture32Kit/kit_metadata.json +++ b/inform7/Internal/Inter/Architecture32Kit/kit_metadata.json @@ -2,7 +2,7 @@ "is": { "type": "kit", "title": "Architecture32Kit", - "version": "10.2.0-beta+6W91" + "version": "10.2.0-beta+6W92" }, "compatibility": "32-bit", "kit-details": { diff --git a/inform7/Internal/Inter/BasicInformKit/kit_metadata.json b/inform7/Internal/Inter/BasicInformKit/kit_metadata.json index 80d865b15..7ec238ea3 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+6W91" + "version": "10.2.0-beta+6W92" }, "needs": [ { "need": { diff --git a/inform7/Internal/Inter/CommandParserKit/kit_metadata.json b/inform7/Internal/Inter/CommandParserKit/kit_metadata.json index ff9da7682..b2d0abe29 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+6W91" + "version": "10.2.0-beta+6W92" }, "needs": [ { "need": { diff --git a/inform7/Internal/Inter/EnglishLanguageKit/kit_metadata.json b/inform7/Internal/Inter/EnglishLanguageKit/kit_metadata.json index 1c9370a8d..2823f7428 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+6W91" + "version": "10.2.0-beta+6W92" }, "needs": [ { "need": { diff --git a/inform7/Internal/Inter/WorldModelKit/kit_metadata.json b/inform7/Internal/Inter/WorldModelKit/kit_metadata.json index c3dd80ed0..682cfa747 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+6W91" + "version": "10.2.0-beta+6W92" }, "needs": [ { "need": { diff --git a/services/words-module/Chapter 3/Text From Files.w b/services/words-module/Chapter 3/Text From Files.w index 2a78f4e5d..c1c1f54ee 100644 --- a/services/words-module/Chapter 3/Text From Files.w +++ b/services/words-module/Chapter 3/Text From Files.w @@ -15,6 +15,7 @@ typedef struct source_file { int words_of_quoted_text; /* word count for text in double-quotes */ FILE *handle; /* file handle while open */ general_pointer your_ref; /* for the client to attach some meaning */ + struct text_stream *body_text; struct text_stream *torn_off_documentation; CLASS_DEFINITION } source_file; @@ -41,6 +42,7 @@ source_file *TextFromFiles::feed_open_file_into_lexer(filename *F, FILE *handle, sf->your_ref = ref; sf->name = F; sf->handle = handle; + sf->body_text = Str::new(); sf->torn_off_documentation = Str::new(); source_location top_of_file; int cr, last_cr, next_cr, read_cr, newline_char = 0, torn_off = FALSE; @@ -78,6 +80,7 @@ source_file *TextFromFiles::feed_open_file_into_lexer(filename *F, FILE *handle, if (torn_off) { PUT_TO(sf->torn_off_documentation, cr); } else { + PUT_TO(sf->body_text, cr); Lexer::feed_triplet(last_cr, cr, next_cr); torn_off = Lexer::detect_tear_off(); } @@ -86,6 +89,7 @@ source_file *TextFromFiles::feed_open_file_into_lexer(filename *F, FILE *handle, sf->text_read = Lexer::feed_ends(TRUE, leaf); @; + if (torn_off) @; return sf; } @@ -96,6 +100,33 @@ quoted text (i.e., their text within double-quotes). LOOP_THROUGH_WORDING(wc, sf->text_read) sf->words_of_source += TextFromFiles::word_count(wc); +@ = + Str::trim_white_space(sf->torn_off_documentation); + if (Str::get_first_char(sf->torn_off_documentation) == '\n') + Str::delete_first_character(sf->torn_off_documentation); + PUT_TO(sf->torn_off_documentation, '\n'); + Str::trim_white_space(sf->body_text); + int quads = 0, i = Str::len(sf->body_text)-1; + for (; i >= 0; i--) { + if ((Str::get_at(sf->body_text, i) == '-') && + (Str::get_at(sf->body_text, i+1) == '-') && + (Str::get_at(sf->body_text, i+2) == '-') && + (Str::get_at(sf->body_text, i+3) == '-')) { + i--; quads++; + break; + } + } + for (; i >= 0; i--) { + if ((Str::get_at(sf->body_text, i) == '-') && + (Str::get_at(sf->body_text, i+1) == '-') && + (Str::get_at(sf->body_text, i+2) == '-') && + (Str::get_at(sf->body_text, i+3) == '-')) { + i--; quads++; + break; + } + } + if (quads == 2) Str::truncate(sf->body_text, i); + @ A much simpler version: =