foundation-module: Chapter_8: Nowebify.

This commit is contained in:
AwesomeAdam54321 2024-03-09 11:58:05 +08:00
parent a15028316e
commit dea875195b
5 changed files with 227 additions and 223 deletions

View file

@ -3,11 +3,11 @@
To manage key-value pairs of bibliographic data, metadata if you like,
associated with a given web.
@h Storing data.
@ \section{Storing data.}
There are never more than a dozen or so key-value pairs, and it's more
convenient to store them directly here than to use a dictionary.
=
<<*>>=
typedef struct web_bibliographic_datum {
struct text_stream *key;
struct text_stream *value;
@ -20,12 +20,13 @@ typedef struct web_bibliographic_datum {
@ We keep these in linked lists, and here's a convenient way to scan them:
@d LOOP_OVER_BIBLIOGRAPHIC_DATA(bd, Wm)
<<*>>=
#define LOOP_OVER_BIBLIOGRAPHIC_DATA(bd, Wm)
LOOP_OVER_LINKED_LIST(bd, web_bibliographic_datum, Wm->bibliographic_data)
@ The following check the rules:
=
<<*>>=
int Bibliographic::datum_can_be_declared(web_md *Wm, text_stream *key) {
web_bibliographic_datum *bd = Bibliographic::look_up_datum(Wm, key);
if (bd == NULL) return FALSE;
@ -38,10 +39,10 @@ int Bibliographic::datum_on_or_off(web_md *Wm, text_stream *key) {
return bd->on_or_off;
}
@h Initialising a web.
@ \section{Initialising a web.}
Each web has the following slate of data:
=
<<*>>=
void Bibliographic::initialise_data(web_md *Wm) {
web_bibliographic_datum *bd;
@ -82,7 +83,7 @@ void Bibliographic::initialise_data(web_md *Wm) {
@ Once the declarations for a web have been processed, the following is called
to check that all the mandatory declarations have indeed been made:
=
<<*>>=
void Bibliographic::check_required_data(web_md *Wm) {
web_bibliographic_datum *bd;
LOOP_OVER_BIBLIOGRAPHIC_DATA(bd, Wm)
@ -92,10 +93,10 @@ void Bibliographic::check_required_data(web_md *Wm) {
"The web does not specify '%S: ...'", bd->key);
}
@h Reading bibliographic data.
@ \section{Reading bibliographic data.}
Key names are case-sensitive.
=
<<*>>=
text_stream *Bibliographic::get_datum(web_md *Wm, text_stream *key) {
web_bibliographic_datum *bd = Bibliographic::look_up_datum(Wm, key);
if (bd) return bd->value;
@ -118,20 +119,20 @@ web_bibliographic_datum *Bibliographic::look_up_datum(web_md *Wm, text_stream *k
return NULL;
}
@h Writing bibliographic data.
@ \section{Writing bibliographic data.}
Note that a key-value pair is created if the key doesn't exist at present,
so this routine never fails.
=
<<*>>=
web_bibliographic_datum *Bibliographic::set_datum(web_md *Wm, text_stream *key, text_stream *val) {
web_bibliographic_datum *bd = Bibliographic::look_up_datum(Wm, key);
if (bd == NULL) @<Create a new datum, then@>
if (bd == NULL) <<Create a new datum, then>>
else Str::copy(bd->value, val);
if (Str::eq_wide_string(key, L"Title")) @<Also set a capitalized form@>;
if (Str::eq_wide_string(key, L"Title")) <<Also set a capitalized form>>;
return bd;
}
@<Create a new datum, then@> =
<<Create a new datum, then>>=
bd = CREATE(web_bibliographic_datum);
bd->key = Str::duplicate(key);
bd->value = Str::duplicate(val);
@ -146,7 +147,7 @@ written to the "Title" key, then a full-caps "WUTHERING HEIGHTS" is
written to a "Capitalized Title" key. (This enables cover sheets which
want to typeset the title in full caps to do so.)
@<Also set a capitalized form@> =
<<Also set a capitalized form>>=
TEMPORARY_TEXT(recapped)
Str::copy(recapped, val);
LOOP_THROUGH_TEXT(P, recapped)

View file

@ -2,11 +2,11 @@
Manages the build metadata for an inweb project.
@h About build files.
When we read a web, we look for a file in it called |build.txt|. If no such
@ \section{About build files.}
When we read a web, we look for a file in it called [[build.txt]]. If no such
file exists, we look for the same thing in the current working directory.
=
<<*>>=
filename *BuildFiles::build_file_for_web(web_md *WS) {
filename *F = Filenames::in(WS->path_to_web, I"build.txt");
if (TextFiles::exists(F)) return F;
@ -17,7 +17,7 @@ filename *BuildFiles::build_file_for_web(web_md *WS) {
@ The format of such a file is very simple: up to three text fields:
=
<<*>>=
typedef struct build_file_data {
struct text_stream *prerelease_text;
struct text_stream *build_code;
@ -26,7 +26,7 @@ typedef struct build_file_data {
@ Here's how to read in a build file:
=
<<*>>=
build_file_data BuildFiles::read(filename *F) {
build_file_data bfd;
bfd.prerelease_text = Str::new();
@ -55,7 +55,7 @@ void BuildFiles::build_file_helper(text_stream *text, text_file_position *tfp, v
@ And here is how to write one:
=
<<*>>=
void BuildFiles::write(build_file_data bfd, filename *F) {
text_stream vr_stream;
text_stream *OUT = &vr_stream;
@ -69,11 +69,11 @@ void BuildFiles::write(build_file_data bfd, filename *F) {
Streams::close(OUT);
}
@h Bibliographic implications.
@ \section{Bibliographic implications.}
Whenever a web is read in by Inweb, its build file is looked at in order to
set some bibliographic data.
=
<<*>>=
void BuildFiles::set_bibliographic_data_for(web_md *WS) {
filename *F = BuildFiles::build_file_for_web(WS);
if (F) {
@ -92,10 +92,10 @@ synthesize the semantic version number for the project. Note that this is
called even if no build file had ever been found, so it's quite legal for
the Contents page to specify all of this.
If no error occurs, then the expansion |[[Semantic Version Number]]| is
If no error occurs, then the expansion [[[[Semantic Version Number]]]] is
guaranteed to produce a semver-legal version number.
=
<<*>>=
void BuildFiles::deduce_semver(web_md *WS) {
TEMPORARY_TEXT(combined)
text_stream *s = Bibliographic::get_datum(WS, I"Semantic Version Number");
@ -121,11 +121,11 @@ void BuildFiles::deduce_semver(web_md *WS) {
DISCARD_TEXT(combined)
}
@h Advancing.
@ \section{Advancing.}
We update the build date to today and, if supplied, also increment the build
number if we find that the date has changed.
=
<<*>>=
void BuildFiles::advance_for_web(web_md *WS) {
filename *F = BuildFiles::build_file_for_web(WS);
if (F) BuildFiles::advance(F);
@ -141,10 +141,10 @@ void BuildFiles::advance(filename *F) {
}
@ The standard date format we use is "26 February 2018". If the contents of
|dateline| match today's date in this format, we return |TRUE|; otherwise we
rewrite |dateline| to today and return |FALSE|.
[[dateline]] match today's date in this format, we return [[TRUE]]; otherwise we
rewrite [[dateline]] to today and return [[FALSE]].
=
<<*>>=
int BuildFiles::dated_today(text_stream *dateline) {
char *monthname[12] = { "January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December" };
@ -161,16 +161,16 @@ int BuildFiles::dated_today(text_stream *dateline) {
return rv;
}
@ Traditional Inform build codes are four-character, e.g., |3Q27|. Here, we
@ Traditional Inform build codes are four-character, e.g., [[3Q27]]. Here, we
read such a code and increase it by one. The two-digit code at the back is
incremented, but rolls around from |99| to |01|, in which case the letter is
advanced, except that |I| and |O| are skipped, and if the letter passes |Z|
then it rolls back around to |A| and the initial digit is incremented.
incremented, but rolls around from [[99]] to [[01]], in which case the letter is
advanced, except that [[I]] and [[O]] are skipped, and if the letter passes [[Z]]
then it rolls back around to [[A]] and the initial digit is incremented.
This allows for 21384 distinct build codes, enough to use one each day for
some 58 years.
=
<<*>>=
void BuildFiles::increment(text_stream *T) {
if (Str::len(T) != 4) Errors::with_text("build code malformed: %S", T);
else {
@ -178,8 +178,8 @@ void BuildFiles::increment(text_stream *T) {
int L = Str::get_at(T, 1);
int M1 = Str::get_at(T, 2) - '0';
int M2 = Str::get_at(T, 3) - '0';
if ((N < 0) || (N > 9) || (L < 'A') || (L > 'Z') ||
(M1 < 0) || (M1 > 9) || (M2 < 0) || (M2 > 9)) {
if ((N < 0) [[| (N > 9) || (L < 'A') || (L > 'Z') |]]
(M1 < 0) [[| (M1 > 9) || (M2 < 0) |]] (M2 > 9)) {
Errors::with_text("build code malformed: %S", T);
} else {
M2++;

View file

@ -13,10 +13,10 @@ a program: something much simpler would surely be sufficient. And here it is.
[1] Why might we have this? Because kits of Inter code take this form.
@ The simple tangler is controlled using a parcel of settings. Note also the
|state|, which is not used by the reader itself, but instead allows the callback
[[state]], which is not used by the reader itself, but instead allows the callback
functions to have a shared state of their own.
=
<<*>>=
typedef struct simple_tangle_docket {
void (*raw_callback)(struct text_stream *, struct simple_tangle_docket *);
void (*command_callback)(struct text_stream *, struct text_stream *,
@ -27,7 +27,7 @@ typedef struct simple_tangle_docket {
struct pathname *web_path;
} simple_tangle_docket;
@ =
<<*>>=
simple_tangle_docket SimpleTangler::new_docket(
void (*A)(struct text_stream *, struct simple_tangle_docket *),
void (*B)(struct text_stream *, struct text_stream *,
@ -49,7 +49,7 @@ simple_tangle_docket SimpleTangler::new_docket(
should open), or a section (which the tangler should find and open), or a
whole web of section files (ditto):
=
<<*>>=
void SimpleTangler::tangle_text(simple_tangle_docket *docket, text_stream *text) {
SimpleTangler::tangle_L1(docket, text, NULL, NULL, FALSE);
}
@ -66,7 +66,7 @@ void SimpleTangler::tangle_web(simple_tangle_docket *docket) {
SimpleTangler::tangle_L1(docket, NULL, NULL, NULL, TRUE);
}
@ =
<<*>>=
void SimpleTangler::tangle_L1(simple_tangle_docket *docket, text_stream *text,
filename *F, text_stream *leafname, int whole_web) {
TEMPORARY_TEXT(T)
@ -77,7 +77,7 @@ void SimpleTangler::tangle_L1(simple_tangle_docket *docket, text_stream *text,
@ First, dispose of the "whole web" possibility.
=
<<*>>=
void SimpleTangler::tangle_L2(OUTPUT_STREAM, text_stream *text, filename *F,
text_stream *leafname, simple_tangle_docket *docket, int whole_web) {
if (whole_web) {
@ -95,26 +95,26 @@ void SimpleTangler::tangle_L2(OUTPUT_STREAM, text_stream *text, filename *F,
}
}
@ When tangling a file, we begin in |comment| mode; when tangling other matter,
@ When tangling a file, we begin in [[comment]] mode; when tangling other matter,
not so much.
=
<<*>>=
void SimpleTangler::tangle_L3(OUTPUT_STREAM, text_stream *text,
text_stream *leafname, simple_tangle_docket *docket, filename *F) {
int comment = FALSE;
FILE *Input_File = NULL;
if ((Str::len(leafname) > 0) || (F)) {
@<Open the file@>;
<<Open the file>>;
comment = TRUE;
}
@<Tangle the material@>;
<<Tangle the material>>;
if (Input_File) fclose(Input_File);
}
@ Note that if we are looking for an explicit section -- say, |Juggling.i6t| --
from a web |W|, we translate that into the path |W/Sections/Juggling.i6t|.
@ Note that if we are looking for an explicit section -- say, [[Juggling.i6t]] --
from a web [[W]], we translate that into the path [[W/Sections/Juggling.i6t]].
@<Open the file@> =
<<Open the file>>=
if (F) {
Input_File = Filenames::fopen(F, "r");
} else if (Str::len(leafname) > 0) {
@ -124,7 +124,7 @@ from a web |W|, we translate that into the path |W/Sections/Juggling.i6t|.
if (Input_File == NULL)
(*(docket->error_callback))("unable to open the file '%S'", leafname);
@<Tangle the material@> =
<<Tangle the material>>=
TEMPORARY_TEXT(command)
TEMPORARY_TEXT(argument)
int skip_part = FALSE, extract = FALSE;
@ -132,50 +132,51 @@ from a web |W|, we translate that into the path |W/Sections/Juggling.i6t|.
do {
Str::clear(command);
Str::clear(argument);
@<Read next character@>;
<<Read next character>>;
NewCharacter: if (cr == EOF) break;
if (((cr == '@') || (cr == '=')) && (col == 1)) {
int inweb_syntax = -1;
if (cr == '=') @<Read the rest of line as an equals-heading@>
else @<Read the rest of line as an at-heading@>;
@<Act on the heading, going in or out of comment mode as appropriate@>;
if (cr == '=') <<Read the rest of line as an equals-heading>>
else <<Read the rest of line as an at-heading>>;
<<Act on the heading, going in or out of comment mode as appropriate>>;
continue;
}
if (comment == FALSE) @<Deal with material which isn't commentary@>;
if (comment == FALSE) <<Deal with material which isn't commentary>>;
} while (cr != EOF);
DISCARD_TEXT(command)
DISCARD_TEXT(argument)
@ Our text files are encoded as ISO Latin-1, not as Unicode UTF-8, so ordinary
|fgetc| is used, and no BOM marker is parsed. Lines are assumed to be terminated
with either |0x0a| or |0x0d|. (Since blank lines are harmless, we take no
trouble over |0a0d| or |0d0a| combinations.) The built-in template files, almost
always the only ones used, are line terminated |0x0a| in Unix fashion.
[[fgetc]] is used, and no BOM marker is parsed. Lines are assumed to be terminated
with either [[0x0a]] or [[0x0d]]. (Since blank lines are harmless, we take no
trouble over [[0a0d]] or [[0d0a]] combinations.) The built-in template files, almost
always the only ones used, are line terminated [[0x0a]] in Unix fashion.
@<Read next character@> =
<<Read next character>>=
if (Input_File) cr = fgetc(Input_File);
else if (text) {
cr = Str::get_at(text, sfp); if (cr == 0) cr = EOF; else sfp++;
} else cr = EOF;
col++; if ((cr == 10) || (cr == 13)) col = 0;
@ Here we see the limited range of Inweb syntaxes allowed; but some |@| and |=|
@ Here we see the limited range of Inweb syntaxes allowed; but some [[@]] and [[=]]
commands can be used, at least.
@d INWEB_PARAGRAPH_SYNTAX 1
@d INWEB_CODE_SYNTAX 2
@d INWEB_DASH_SYNTAX 3
@d INWEB_PURPOSE_SYNTAX 4
@d INWEB_FIGURE_SYNTAX 5
@d INWEB_EQUALS_SYNTAX 6
@d INWEB_EXTRACT_SYNTAX 7
<<*>>=
#define INWEB_PARAGRAPH_SYNTAX 1
#define INWEB_CODE_SYNTAX 2
#define INWEB_DASH_SYNTAX 3
#define INWEB_PURPOSE_SYNTAX 4
#define INWEB_FIGURE_SYNTAX 5
#define INWEB_EQUALS_SYNTAX 6
#define INWEB_EXTRACT_SYNTAX 7
@<Read the rest of line as an at-heading@> =
<<Read the rest of line as an at-heading>>=
TEMPORARY_TEXT(at_cmd)
int committed = FALSE, unacceptable_character = FALSE;
while (TRUE) {
@<Read next character@>;
if ((committed == FALSE) && ((cr == 10) || (cr == 13) || (cr == ' '))) {
<<Read next character>>;
if ((committed == FALSE) && ((cr == 10) [[| (cr == 13) |]] (cr == ' '))) {
if (Str::eq_wide_string(at_cmd, L"p"))
inweb_syntax = INWEB_PARAGRAPH_SYNTAX;
else if (Str::eq_wide_string(at_cmd, L"h"))
@ -202,7 +203,7 @@ commands can be used, at least.
}
if (!(((cr >= 'A') && (cr <= 'Z')) || ((cr >= 'a') && (cr <= 'z'))
|| ((cr >= '0') && (cr <= '9'))
|| (cr == '-') || (cr == '>') || (cr == ':') || (cr == '_')))
[[| (cr == '-') || (cr == '>') || (cr == ':') |]] (cr == '_')))
unacceptable_character = TRUE;
if ((cr == 10) || (cr == 13)) break;
PUT_TO(at_cmd, cr);
@ -210,10 +211,10 @@ commands can be used, at least.
Str::copy(command, at_cmd);
DISCARD_TEXT(at_cmd)
@<Read the rest of line as an equals-heading@> =
<<Read the rest of line as an equals-heading>>=
TEMPORARY_TEXT(equals_cmd)
while (TRUE) {
@<Read next character@>;
<<Read next character>>;
if ((cr == 10) || (cr == 13)) break;
PUT_TO(equals_cmd, cr);
}
@ -231,14 +232,14 @@ commands can be used, at least.
Regexp::dispose_of(&mr);
DISCARD_TEXT(equals_cmd)
@<Act on the heading, going in or out of comment mode as appropriate@> =
<<Act on the heading, going in or out of comment mode as appropriate>>=
switch (inweb_syntax) {
case INWEB_PARAGRAPH_SYNTAX: {
TEMPORARY_TEXT(heading_name)
Str::copy_tail(heading_name, command, 2);
int c;
while (((c = Str::get_last_char(heading_name)) != 0) &&
((c == ' ') || (c == '\t') || (c == '.')))
((c == ' ') [[| (c == '\t') |]] (c == '.')))
Str::delete_last_character(heading_name);
if (Str::len(heading_name) == 0)
(*(docket->error_callback))("Empty heading name", NULL);
@ -266,11 +267,11 @@ commands can be used, at least.
case INWEB_FIGURE_SYNTAX: break;
}
@<Deal with material which isn't commentary@> =
<<Deal with material which isn't commentary>>=
if (cr == '{') {
@<Read next character@>;
<<Read next character>>;
if ((cr == '-') && (docket->command_callback)) {
@<Read up to the next close brace as a braced command and argument@>;
<<Read up to the next close brace as a braced command and argument>>;
if (Str::get_first_char(command) == '!') continue;
(*(docket->command_callback))(OUT, command, argument, docket);
continue;
@ -280,9 +281,9 @@ commands can be used, at least.
}
}
if ((cr == '(') && (docket->bplus_callback)) {
@<Read next character@>;
<<Read next character>>;
if (cr == '+') {
@<Read up to the next plus close-bracket as an I7 expression@>;
<<Read up to the next plus close-bracket as an I7 expression>>;
continue;
} else { /* otherwise the open bracket was a literal */
PUT_TO(OUT, '(');
@ -291,29 +292,29 @@ commands can be used, at least.
}
PUT_TO(OUT, cr);
@ And here we read a normal command. The command name must not include |}|
or |:|. If there is no |:| then the argument is left unset (so that it will
be the empty string: see above). The argument must not include |}|.
@ And here we read a normal command. The command name must not include [[}]]
or [[:]]. If there is no [[:]] then the argument is left unset (so that it will
be the empty string: see above). The argument must not include [[}]].
@<Read up to the next close brace as a braced command and argument@> =
<<Read up to the next close brace as a braced command and argument>>=
Str::clear(command);
Str::clear(argument);
int com_mode = TRUE;
while (TRUE) {
@<Read next character@>;
<<Read next character>>;
if ((cr == '}') || (cr == EOF)) break;
if ((cr == ':') && (com_mode)) { com_mode = FALSE; continue; }
if (com_mode) PUT_TO(command, cr);
else PUT_TO(argument, cr);
}
@ And similarly, for the |(+| ... |+)| notation which was once used to mark
@ And similarly, for the [[(+| ... |+)]] notation which was once used to mark
I7 material within I6:
@<Read up to the next plus close-bracket as an I7 expression@> =
<<Read up to the next plus close-bracket as an I7 expression>>=
TEMPORARY_TEXT(material)
while (TRUE) {
@<Read next character@>;
<<Read next character>>;
if (cr == EOF) break;
if ((cr == ')') && (Str::get_last_char(material) == '+')) {
Str::delete_last_character(material); break; }

View file

@ -2,32 +2,33 @@
To search for included modules, and track dependencies between them.
@h Creation.
@ \section{Creation.}
Each web of source material discovered by Inweb is given one of the following.
Ordinarily these are found only when reading in a web for weaving, tangling
and so on: in the vast majority of Inweb runs, all modules will have the
"module origin marker" |READING_WEB_MOM|. But when Inweb is constructing a
"module origin marker" [[READING_WEB_MOM]]. But when Inweb is constructing a
makefile for a suite of tools, it can also discover multiple webs by other
means.
@e READING_WEB_MOM from 0
@e MAKEFILE_TOOL_MOM
@e MAKEFILE_WEB_MOM
@e MAKEFILE_MODULE_MOM
<<*>>=
enum READING_WEB_MOM from 0
enum MAKEFILE_TOOL_MOM
enum MAKEFILE_WEB_MOM
enum MAKEFILE_MODULE_MOM
=
<<*>>=
typedef struct module {
struct pathname *module_location;
struct text_stream *module_name;
struct linked_list *dependencies; /* of |module|: which other modules does this need? */
struct linked_list *dependencies; /* of [[module]]: which other modules does this need? */
struct text_stream *module_tag;
int origin_marker; /* one of the |*_MOM| values above */
struct linked_list *chapters_md; /* of |chapter_md|: just the ones in this module */
struct linked_list *sections_md; /* of |section_md|: just the ones in this module */
int origin_marker; /* one of the [[*_MOM]] values above */
struct linked_list *chapters_md; /* of [[chapter_md]]: just the ones in this module */
struct linked_list *sections_md; /* of [[section_md]]: just the ones in this module */
CLASS_DEFINITION
} module;
@ =
<<*>>=
module *WebModules::new(text_stream *name, pathname *at, int m) {
module *M = CREATE(module);
M->module_location = at;
@ -44,48 +45,48 @@ module *WebModules::new(text_stream *name, pathname *at, int m) {
contains a suite of utility routines, or a major component of a program, but
which is not a program in its own right.
Internally, though, every web produces a |module| structure. The one for the
Internally, though, every web produces a [[module]] structure. The one for the
main web -- which can be tangled, and results in an actual program -- is
internally named |"(main)"|, a name which the user will never see.
internally named [["(main)"]], a name which the user will never see.
=
<<*>>=
module *WebModules::create_main_module(web_md *WS) {
return WebModules::new(I"(main)", WS->path_to_web, READING_WEB_MOM);
}
@h Dependencies.
@ \section{Dependencies.}
When web A imports module B, we will say that A is dependent on B. A web
can import multiple modules, so there can a list of dependencies. These are
needed when constructing makefiles, since the source code in B affects the
program generated by A.
=
<<*>>=
void WebModules::dependency(module *A, module *B) {
if ((A == NULL) || (B == NULL)) internal_error("no module");
ADD_TO_LINKED_LIST(B, module, A->dependencies);
}
@h Searching.
@ \section{Searching.}
The following abstracts the idea of a place where modules might be found.
(At one time there was going to be a more elaborate search hierarchy.)
=
<<*>>=
typedef struct module_search {
struct pathname *path_to_search;
CLASS_DEFINITION
} module_search;
@ =
<<*>>=
module_search *WebModules::make_search_path(pathname *ext_path) {
module_search *ms = CREATE(module_search);
ms->path_to_search = ext_path;
return ms;
}
@ When a web's contents page says to |import Blah|, how do we find the module
called |Blah| on disc? We try four possibilities in sequence:
@ When a web's contents page says to [[import Blah]], how do we find the module
called [[Blah]] on disc? We try four possibilities in sequence:
=
<<*>>=
module *WebModules::find(web_md *WS, module_search *ms, text_stream *name, pathname *X) {
TEMPORARY_TEXT(T)
WRITE_TO(T, "%S-module", name);
@ -97,16 +98,16 @@ module *WebModules::find(web_md *WS, module_search *ms, text_stream *name, pathn
int N = 4;
for (int i=0; i<N; i++) {
pathname *P = Pathnames::from_text_relative(tries[i], T);
if ((P) && (WebModules::exists(P))) @<Accept this directory as the module@>;
if ((P) && (WebModules::exists(P))) <<Accept this directory as the module>>;
}
DISCARD_TEXT(T)
return NULL;
}
@ When the module is found (if it is), a suitable module structure is made,
and a dependency created from the web's |(main)| module to this one.
and a dependency created from the web's [[(main)]] module to this one.
@<Accept this directory as the module@> =
<<Accept this directory as the module>>=
pathname *Q = Pathnames::from_text(name);
module *M = WebModules::new(Pathnames::directory_name(Q), P, READING_WEB_MOM);
WebModules::dependency(WS->as_module, M);
@ -115,30 +116,30 @@ and a dependency created from the web's |(main)| module to this one.
@ We accept that a plausibly-named directory is indeed the module being
sought if it looks like a web.
=
<<*>>=
int WebModules::exists(pathname *P) {
return WebMetadata::directory_looks_like_a_web(P);
}
@h Resolving cross-reference names.
Suppose we are in module |from_M| and want to understand which section of
a relevant web |text| might refer to. It could be the name of a module,
@ \section{Resolving cross-reference names.}
Suppose we are in module [[from_M]] and want to understand which section of
a relevant web [[text]] might refer to. It could be the name of a module,
either this one or one dependent on it; or the name of a chapter in one
of those, or the shortened forms of those; or the name of a section. It
may match multiple possibilities: we return how many, and if this is
positive, we write the module in which the first find was made in |*return M|,
the section in |*return_Sm|, and set the flag |*named_as_module| according
positive, we write the module in which the first find was made in [[*return M]],
the section in [[*return_Sm]], and set the flag [[*named_as_module]] according
to whether the reference was a bare module name (say, "foundation") or not.
Note that we consider first the possibilities within |from_M|: we only
Note that we consider first the possibilities within [[from_M]]: we only
look at other modules if there are none. Thus, an unambiguous result in
|from_M| is good enough, even if there are other possibilities elsewhere.
[[from_M]] is good enough, even if there are other possibilities elsewhere.
A reference in the form |module: reference| is taken to be in the module
of that name: for example, |"foundation: Web Modules"| would find the
A reference in the form [[module: reference]] is taken to be in the module
of that name: for example, [["foundation: Web Modules"]] would find the
section of code you are now reading.
=
<<*>>=
int WebModules::named_reference(module **return_M, section_md **return_Sm,
int *named_as_module, text_stream *title, module *from_M, text_stream *text,
int list, int sections_only) {
@ -156,7 +157,7 @@ int WebModules::named_reference(module **return_M, section_md **return_Sm,
}
LOOP_OVER_LINKED_LIST(M, module, from_M->dependencies) {
if (Str::eq_insensitive(M->module_name, seek_module)) {
@<Look for references to chapters or sections in M@>;
<<Look for references to chapters or sections in M>>;
}
}
Regexp::dispose_of(&mr);
@ -164,20 +165,20 @@ int WebModules::named_reference(module **return_M, section_md **return_Sm,
for (int stage = 1; ((finds == 0) && (stage <= 2)); stage++) {
if (stage == 1) {
M = from_M;
@<Look for references to chapters or sections in M@>;
<<Look for references to chapters or sections in M>>;
}
if (stage == 2) {
LOOP_OVER_LINKED_LIST(M, module, from_M->dependencies)
@<Look for references to chapters or sections in M@>;
<<Look for references to chapters or sections in M>>;
}
}
return finds;
}
@<Look for references to chapters or sections in M@> =
<<Look for references to chapters or sections in M>>=
if (M == NULL) internal_error("no module");
if (Str::eq_insensitive(M->module_name, seek))
@<Found first section in module@>;
<<Found first section in module>>;
chapter_md *Cm;
section_md *Sm;
LOOP_OVER_LINKED_LIST(Cm, chapter_md, M->chapters_md) {
@ -185,13 +186,13 @@ int WebModules::named_reference(module **return_M, section_md **return_Sm,
((Str::eq_insensitive(Cm->ch_title, seek)) ||
(Str::eq_insensitive(Cm->ch_basic_title, seek)) ||
(Str::eq_insensitive(Cm->ch_decorated_title, seek))))
@<Found first section in chapter@>;
<<Found first section in chapter>>;
LOOP_OVER_LINKED_LIST(Sm, section_md, Cm->sections_md)
if (Str::eq_insensitive(Sm->sect_title, seek))
@<Found section by name@>;
<<Found section by name>>;
}
@<Found first section in module@> =
<<Found first section in module>>=
finds++;
if (finds == 1) {
*return_M = M; *return_Sm = FIRST_IN_LINKED_LIST(section_md, M->sections_md);
@ -200,7 +201,7 @@ int WebModules::named_reference(module **return_M, section_md **return_Sm,
}
if (list) WRITE_TO(STDERR, "(%d) Module '%S'\n", finds, M->module_name);
@<Found first section in chapter@> =
<<Found first section in chapter>>=
finds++;
if (finds == 1) {
*return_M = M; *return_Sm = FIRST_IN_LINKED_LIST(section_md, Cm->sections_md);
@ -209,7 +210,7 @@ int WebModules::named_reference(module **return_M, section_md **return_Sm,
if (list) WRITE_TO(STDERR, "(%d) Chapter '%S' of module '%S'\n",
finds, Cm->ch_title, M->module_name);
@<Found section by name@> =
<<Found section by name>>=
finds++;
if (finds == 1) {
*return_M = M; *return_Sm = Sm;

View file

@ -3,7 +3,7 @@
To read the structure of a literate programming web from a path in the file
system.
@h Introduction.
@ \section{Introduction.}
Webs are literate programs for the Inweb LP system. A single web consists of
a number of chapters (though sometimes just one, called "Sections"), each
of which consists of a number of sections. A web can represent a stand-alone
@ -13,21 +13,22 @@ called a "module".
Inweb syntax has gradually shifted over the years, but there are two main
versions: the second was cleaned up and simplified from the first in 2019.
@e V1_SYNTAX from 1
@e V2_SYNTAX
<<*>>=
enum V1_SYNTAX from 1
enum V2_SYNTAX
@h Web MD.
@ \section{Web MD.}
No relation to the website of the same name: MD here stands for metadata.
Our task in this section will be to read a web from the filing system and
produce the following metadata structure.
Each web produces a single instance of |web_md|:
Each web produces a single instance of [[web_md]]:
=
<<*>>=
typedef struct web_md {
struct pathname *path_to_web; /* relative to the current working directory */
struct filename *single_file; /* relative to the current working directory */
struct linked_list *bibliographic_data; /* of |web_bibliographic_datum| */
struct linked_list *bibliographic_data; /* of [[web_bibliographic_datum]] */
struct semantic_version_number version_number; /* as deduced from bibliographic data */
int default_syntax; /* which version syntax the sections will have */
int chaptered; /* has the author explicitly divided it into named chapters? */
@ -35,20 +36,20 @@ typedef struct web_md {
struct module *as_module; /* the root of a small dependency graph */
struct filename *contents_filename; /* or |NULL| for a single-file web */
struct linked_list *tangle_target_names; /* of |text_stream| */
struct linked_list *header_filenames; /* of |filename| */
struct filename *contents_filename; /* or [[NULL]] for a single-file web */
struct linked_list *tangle_target_names; /* of [[text_stream]] */
struct linked_list *header_filenames; /* of [[filename]] */
struct linked_list *chapters_md; /* of |chapter_md| */
struct linked_list *sections_md; /* of |section_md| */
struct linked_list *chapters_md; /* of [[chapter_md]] */
struct linked_list *sections_md; /* of [[section_md]] */
CLASS_DEFINITION
} web_md;
@ The |chapters_md| list in a |web_md| contains these as its entries:
@ The [[chapters_md]] list in a [[web_md]] contains these as its entries:
=
<<*>>=
typedef struct chapter_md {
struct text_stream *ch_range; /* e.g., |P| for Preliminaries, |7| for Chapter 7, |C| for Appendix C */
struct text_stream *ch_range; /* e.g., [[P| for Preliminaries, |7| for Chapter 7, |C]] for Appendix C */
struct text_stream *ch_title; /* e.g., "Chapter 3: Fresh Water Fish" */
struct text_stream *ch_basic_title; /* e.g., "Chapter 3" */
struct text_stream *ch_decorated_title; /* e.g., "Fresh Water Fish" */
@ -58,13 +59,13 @@ typedef struct chapter_md {
int imported; /* from a different web? */
struct linked_list *sections_md; /* of |section_md| */
struct linked_list *sections_md; /* of [[section_md]] */
CLASS_DEFINITION
} chapter_md;
@ And the |sections_md| list in a |chapter_md| contains these as its entries:
@ And the [[sections_md]] list in a [[chapter_md]] contains these as its entries:
=
<<*>>=
typedef struct section_md {
struct text_stream *sect_title; /* e.g., "Program Control" */
struct text_stream *sect_range; /* e.g., "2/ct" */
@ -82,13 +83,13 @@ typedef struct section_md {
CLASS_DEFINITION
} section_md;
@h Reading from the file system.
@ \section{Reading from the file system.}
Webs can be stored in two ways: as a directory containing a multitude of files,
in which case the pathname |P| is supplied; or as a single file with everything
in which case the pathname [[P]] is supplied; or as a single file with everything
in one (and thus, implicitly, a single chapter and a single section), in which
case a filename |alt_F| is supplied.
case a filename [[alt_F]] is supplied.
=
<<*>>=
web_md *WebMetadata::get_without_modules(pathname *P, filename *alt_F) {
return WebMetadata::get(P, alt_F, V2_SYNTAX, NULL, FALSE, FALSE, NULL);
}
@ -97,20 +98,20 @@ web_md *WebMetadata::get(pathname *P, filename *alt_F, int syntax_version,
module_search *I, int verbosely, int including_modules, pathname *path_to_inweb) {
if ((including_modules) && (I == NULL)) I = WebModules::make_search_path(NULL);
web_md *Wm = CREATE(web_md);
@<Begin the bibliographic data@>;
@<Initialise the rest of the web MD@>;
<<Begin the bibliographic data>>;
<<Initialise the rest of the web MD>>;
WebMetadata::read_contents_page(Wm, Wm->as_module, I, verbosely,
including_modules, NULL, path_to_inweb);
@<Consolidate the bibliographic data@>;
@<Work out the section ranges@>;
<<Consolidate the bibliographic data>>;
<<Work out the section ranges>>;
return Wm;
}
@<Begin the bibliographic data@> =
<<Begin the bibliographic data>>=
Wm->bibliographic_data = NEW_LINKED_LIST(web_bibliographic_datum);
Bibliographic::initialise_data(Wm);
@<Initialise the rest of the web MD@> =
<<Initialise the rest of the web MD>>=
if (P) {
Wm->path_to_web = P;
Wm->single_file = NULL;
@ -130,14 +131,14 @@ web_md *WebMetadata::get(pathname *P, filename *alt_F, int syntax_version,
Wm->header_filenames = NEW_LINKED_LIST(filename);
Wm->as_module = WebModules::create_main_module(Wm);
@<Consolidate the bibliographic data@> =
<<Consolidate the bibliographic data>>=
Bibliographic::check_required_data(Wm);
BuildFiles::set_bibliographic_data_for(Wm);
BuildFiles::deduce_semver(Wm);
@ If no range is supplied, we make one ourselves.
@<Work out the section ranges@> =
<<Work out the section ranges>>=
int sequential = FALSE; /* are we numbering sections sequentially? */
if (Str::eq(Bibliographic::get_datum(Wm, I"Sequential Section Ranges"), I"On"))
sequential = TRUE;
@ -147,12 +148,12 @@ web_md *WebMetadata::get(pathname *P, filename *alt_F, int syntax_version,
int section_counter = 1;
LOOP_OVER_LINKED_LIST(Sm, section_md, Cm->sections_md) {
if (Str::len(Sm->sect_range) == 0)
@<Concoct a range for section Sm in chapter Cm in web Wm@>;
<<Concoct a range for section Sm in chapter Cm in web Wm>>;
section_counter++;
}
}
@<Concoct a range for section Sm in chapter Cm in web Wm@> =
<<Concoct a range for section Sm in chapter Cm in web Wm>>=
if (sequential) {
WRITE_TO(Sm->sect_range, "%S/", Cm->ch_range);
WRITE_TO(Sm->sect_range, "s%d", section_counter);
@ -162,17 +163,17 @@ web_md *WebMetadata::get(pathname *P, filename *alt_F, int syntax_version,
do {
Str::clear(Sm->sect_range);
WRITE_TO(Sm->sect_range, "%S/", Cm->ch_range);
@<Make the tail using this many consonants from each word@>;
<<Make the tail using this many consonants from each word>>;
if (--letters_from_each_word == 0) break;
} while (Str::len(Sm->sect_range) > 5);
@<Terminate with disambiguating numbers in case of collisions@>;
<<Terminate with disambiguating numbers in case of collisions>>;
}
@ We collapse words to an initial letter plus consonants: thus "electricity"
would be "elctrcty", since we don't count "y" as a vowel here.
@<Make the tail using this many consonants from each word@> =
<<Make the tail using this many consonants from each word>>=
int sn = 0, sw = Str::len(Sm->sect_range);
if (Platform::is_folder_separator(Str::get_at(from, sn))) sn++;
int letters_from_current_word = 0;
@ -197,7 +198,7 @@ would be "elctrcty", since we don't count "y" as a vowel here.
@ We never want two sections to have the same range.
@<Terminate with disambiguating numbers in case of collisions@> =
<<Terminate with disambiguating numbers in case of collisions>>=
TEMPORARY_TEXT(original_range)
Str::copy(original_range, Sm->sect_range);
int disnum = 0, collision = FALSE;
@ -224,10 +225,10 @@ would be "elctrcty", since we don't count "y" as a vowel here.
} while (collision);
DISCARD_TEXT(original_range)
@h Reading the contents page.
@ \section{Reading the contents page.}
Making the web begins by reading the contents section, which really isn't a
section at all (and perhaps we shouldn't pretend that it is by the use of the
|.w| file extension, but we probably want it to have the same file extension,
[[.w]] file extension, but we probably want it to have the same file extension,
and its syntax is chosen so that syntax-colouring for regular sections doesn't
make it look odd). When the word "section" is used in the Inweb code, it
almost always means "section other than the contents".
@ -242,7 +243,7 @@ With a single-file web, the "contents section" doesn't exist as a file in its
own right: instead, it's the top few lines of the single file. We handle that
by halting at the junction point.
=
<<*>>=
typedef struct reader_state {
struct web_md *Wm;
struct filename *contents_filename;
@ -268,7 +269,7 @@ void WebMetadata::read_contents_page(web_md *Wm, module *of_module,
module_search *import_path, int verbosely,
int including_modules, pathname *path, pathname *X) {
reader_state RS;
@<Initialise the reader state@>;
<<Initialise the reader state>>;
int cl = TextFiles::read(RS.contents_filename, FALSE, "can't open contents file",
TRUE, WebMetadata::read_contents_line, NULL, &RS);
@ -282,7 +283,7 @@ void WebMetadata::read_contents_page(web_md *Wm, module *of_module,
if (RS.section_count == 1) RS.last_section->is_a_singleton = TRUE;
}
@<Initialise the reader state@> =
<<Initialise the reader state>>=
RS.Wm = Wm;
RS.reading_from = of_module;
RS.in_biblio = TRUE;
@ -318,7 +319,7 @@ void WebMetadata::read_contents_page(web_md *Wm, module *of_module,
and sets out bibliographic information about the web, the sections and their
organisation, and so on.
=
<<*>>=
void WebMetadata::read_contents_line(text_stream *line, text_file_position *tfp, void *X) {
reader_state *RS = (reader_state *) X;
if (RS->halted) return;
@ -328,40 +329,40 @@ void WebMetadata::read_contents_line(text_stream *line, text_file_position *tfp,
begins_with_white_space = TRUE;
Str::trim_white_space(line);
@<Act immediately if the web syntax version is changed@>;
<<Act immediately if the web syntax version is changed>>;
int syntax = RS->Wm->default_syntax;
filename *filename_of_single_file_web = NULL;
if ((RS->halt_at_at) && (Str::get_at(line, 0) == '@'))
@<Halt at this point in the single file, and make the rest of it a one-chapter section@>;
<<Halt at this point in the single file, and make the rest of it a one-chapter section>>;
@<Read regular contents material@>;
<<Read regular contents material>>;
}
@ Since the web syntax version affects how the rest of the file is read, it's
no good simply to store this up for later: we have to change the web structure
immediately.
@<Act immediately if the web syntax version is changed@> =
<<Act immediately if the web syntax version is changed>>=
if (Str::eq(line, I"Web Syntax Version: 1"))
RS->Wm->default_syntax = V1_SYNTAX;
else if (Str::eq(line, I"Web Syntax Version: 2"))
RS->Wm->default_syntax = V2_SYNTAX;
@ Suppose we're reading a single-file web, and we hit the first |@| marker.
@ Suppose we're reading a single-file web, and we hit the first [[@]] marker.
The contents part has now ended, so we should halt scanning. But we also need
to give the web a single chapter ("Sections", range "S"), which contains a
single section ("All") consisting of the remainder of the single file.
@<Halt at this point in the single file, and make the rest of it a one-chapter section@> =
<<Halt at this point in the single file, and make the rest of it a one-chapter section>>=
RS->halted = TRUE;
text_stream *new_chapter_range = I"S";
text_stream *language_name = NULL;
line = I"Sections";
@<Create the new chapter with these details@>;
<<Create the new chapter with these details>>;
line = I"All";
filename_of_single_file_web = tfp->text_file_filename;
@<Read about, and read in, a new section@>;
<<Read about, and read in, a new section>>;
return;
@ With those two complications out of the way, we now know that we're reading
@ -369,21 +370,21 @@ a line of contents material. At the start of the contents, this will be a
series of bibliographic data values; then there's a blank line, and then
we're into the section listing.
@<Read regular contents material@> =
if (Str::len(line) == 0) @<End bibliographic data here, at the blank line@>
else if (RS->in_biblio) @<Read the bibliographic data block at the top@>
else @<Read the roster of sections at the bottom@>;
<<Read regular contents material>>=
if (Str::len(line) == 0) <<End bibliographic data here, at the blank line>>
else if (RS->in_biblio) <<Read the bibliographic data block at the top>>
else <<Read the roster of sections at the bottom>>;
@ At this point we've gone through the bibliographic lines at the top of the
contents page, and are soon going to read in the sections.
@<End bibliographic data here, at the blank line@> =
<<End bibliographic data here, at the blank line>>=
RS->in_biblio = FALSE;
@ The bibliographic data gives lines in any order specifying values of
variables with fixed names; a blank line ends the block.
@<Read the bibliographic data block at the top@> =
<<Read the bibliographic data block at the top>>=
if (RS->main_web_not_module) {
match_results mr = Regexp::create_mr();
if (Regexp::match(&mr, line, L"(%c+?): (%c+?) *")) {
@ -391,7 +392,7 @@ variables with fixed names; a blank line ends the block.
Str::copy(key, mr.exp[0]);
TEMPORARY_TEXT(value)
Str::copy(value, mr.exp[1]);
@<Set bibliographic key-value pair@>;
<<Set bibliographic key-value pair>>;
DISCARD_TEXT(key)
DISCARD_TEXT(value)
} else {
@ -403,7 +404,7 @@ variables with fixed names; a blank line ends the block.
Regexp::dispose_of(&mr);
}
@<Set bibliographic key-value pair@> =
<<Set bibliographic key-value pair>>=
if (Bibliographic::datum_can_be_declared(RS->Wm, key)) {
if (Bibliographic::datum_on_or_off(RS->Wm, key)) {
if ((Str::ne_wide_string(value, L"On")) && (Str::ne_wide_string(value, L"Off"))) {
@ -426,20 +427,20 @@ variables with fixed names; a blank line ends the block.
@ In the bulk of the contents, we find indented lines for sections and
unindented ones for chapters.
@<Read the roster of sections at the bottom@> =
<<Read the roster of sections at the bottom>>=
if (begins_with_white_space == FALSE) {
if (Str::get_first_char(line) == '"') {
RS->in_purpose = TRUE; Str::delete_first_character(line);
}
if (RS->in_purpose == TRUE) @<Record the purpose of the current chapter@>
else @<Read about a new chapter@>;
} else @<Read about, and read in, a new section@>;
if (RS->in_purpose == TRUE) <<Record the purpose of the current chapter>>
else <<Read about a new chapter>>;
} else <<Read about, and read in, a new section>>;
@ After a declared chapter heading, subsequent lines form its purpose, until
we reach a closed quote: we then stop, but remove the quotation marks. Because
we like a spoonful of syntactic sugar on our porridge, that's why.
@<Record the purpose of the current chapter@> =
<<Record the purpose of the current chapter>>=
if ((Str::len(line) > 0) && (Str::get_last_char(line) == '"')) {
Str::truncate(line, Str::len(line)-1); RS->in_purpose = FALSE;
}
@ -451,7 +452,7 @@ we like a spoonful of syntactic sugar on our porridge, that's why.
@ The title tells us everything we need to know about a chapter:
@<Read about a new chapter@> =
<<Read about a new chapter>>=
TEMPORARY_TEXT(new_chapter_range) /* e.g., S, P, 1, 2, 3, A, B, ... */
TEMPORARY_TEXT(pdf_leafname)
text_stream *language_name = NULL;
@ -460,7 +461,7 @@ we like a spoonful of syntactic sugar on our porridge, that's why.
if (Regexp::match(&mr, line, L"(%c*%C) %(Independent(%c*)%)")) {
text_stream *title_alone = mr.exp[0];
language_name = mr.exp[1];
@<Mark this chapter as an independent tangle target@>;
<<Mark this chapter as an independent tangle target>>;
Str::copy(line, title_alone);
}
int this_is_a_chapter = TRUE;
@ -540,7 +541,7 @@ we like a spoonful of syntactic sugar on our porridge, that's why.
DISCARD_TEXT(err)
}
if (this_is_a_chapter) @<Create the new chapter with these details@>;
if (this_is_a_chapter) <<Create the new chapter with these details>>;
DISCARD_TEXT(new_chapter_range)
DISCARD_TEXT(pdf_leafname)
Regexp::dispose_of(&mr);
@ -548,7 +549,7 @@ we like a spoonful of syntactic sugar on our porridge, that's why.
@ A chapter whose title marks it as Independent becomes a new tangle target,
with the same language as the main web unless stated otherwise.
@<Mark this chapter as an independent tangle target@> =
<<Mark this chapter as an independent tangle target>>=
match_results mr = Regexp::create_mr();
if (Regexp::match(&mr, language_name, L" *"))
language_name = Bibliographic::get_datum(RS->Wm, I"Language");
@ -556,7 +557,7 @@ with the same language as the main web unless stated otherwise.
language_name = mr.exp[0];
Regexp::dispose_of(&mr);
@<Create the new chapter with these details@> =
<<Create the new chapter with these details>>=
chapter_md *Cm = CREATE(chapter_md);
Cm->ch_range = Str::duplicate(new_chapter_range);
if (line == NULL) PRINT("Nullity!\n");
@ -584,16 +585,16 @@ with the same language as the main web unless stated otherwise.
of registering a new section within a chapter -- more interesting because
we also read in and process its file.
@<Read about, and read in, a new section@> =
<<Read about, and read in, a new section>>=
section_md *Sm = CREATE(section_md);
@<Initialise the section structure@>;
@<Add the section to the web and the current chapter@>;
@<Work out the language and tangle target for the section@>;
<<Initialise the section structure>>;
<<Add the section to the web and the current chapter>>;
<<Work out the language and tangle target for the section>>;
if (Sm->source_file_for_section == NULL)
@<Work out the filename of this section file@>;
<<Work out the filename of this section file>>;
@<Initialise the section structure@> =
<<Initialise the section structure>>=
Sm->source_file_for_section = filename_of_single_file_web;
Sm->using_syntax = syntax;
Sm->is_a_singleton = FALSE;
@ -612,7 +613,7 @@ we also read in and process its file.
Regexp::dispose_of(&mr);
Sm->owning_module = RS->reading_from;
@<Add the section to the web and the current chapter@> =
<<Add the section to the web and the current chapter>>=
chapter_md *Cm = RS->chapter_being_scanned;
RS->section_count++;
RS->last_section = Sm;
@ -620,28 +621,28 @@ we also read in and process its file.
ADD_TO_LINKED_LIST(Sm, section_md, RS->Wm->sections_md);
ADD_TO_LINKED_LIST(Sm, section_md, RS->reading_from->sections_md);
@<Work out the language and tangle target for the section@> =
<<Work out the language and tangle target for the section>>=
Sm->sect_language_name = RS->chapter_being_scanned->ch_language_name; /* by default */
match_results mr = Regexp::create_mr();
if (Regexp::match(&mr, line, L"(%c*%C) %(Independent (%c*) *%)")) {
text_stream *title_alone = mr.exp[0];
text_stream *language_name = mr.exp[1];
@<Mark this section as an independent tangle target@>;
<<Mark this section as an independent tangle target>>;
Str::copy(Sm->sect_title, title_alone);
}
Regexp::dispose_of(&mr);
@<Mark this section as an independent tangle target@> =
<<Mark this section as an independent tangle target>>=
text_stream *p = language_name;
if (Str::len(p) == 0) p = Bibliographic::get_datum(RS->Wm, I"Language");
Sm->sect_independent_language = Str::duplicate(p);
@ If we're told that a section is called "Bells and Whistles", what filename
is it stored in? Firstly, the leafname is normally |Bells and Whistles.w|,
but the extension used doesn't have to be |.w|: for Inform 6 template files,
the extension needs to be |.i6t|. We allow either.
is it stored in? Firstly, the leafname is normally [[Bells and Whistles.w]],
but the extension used doesn't have to be [[.w]]: for Inform 6 template files,
the extension needs to be [[.i6t]]. We allow either.
@<Work out the filename of this section file@> =
<<Work out the filename of this section file>>=
TEMPORARY_TEXT(leafname_to_use)
WRITE_TO(leafname_to_use, "%S.i6t", Sm->sect_title);
pathname *P = RS->path_to;
@ -656,9 +657,9 @@ the extension needs to be |.i6t|. We allow either.
}
DISCARD_TEXT(leafname_to_use)
@h Relative pathnames or filenames.
@ \section{Relative pathnames or filenames.}
=
<<*>>=
int WebMetadata::directory_looks_like_a_web(pathname *P) {
return TextFiles::exists(WebMetadata::contents_filename(P));
}
@ -667,9 +668,9 @@ filename *WebMetadata::contents_filename(pathname *P) {
return Filenames::in(P, I"Contents.w");
}
@h Statistics.
@ \section{Statistics.}
=
<<*>>=
int WebMetadata::chapter_count(web_md *Wm) {
int n = 0;
chapter_md *Cm;