foundation-module: Chapter_8: Nowebify.
This commit is contained in:
parent
a15028316e
commit
dea875195b
5 changed files with 227 additions and 223 deletions
|
@ -3,11 +3,11 @@
|
||||||
To manage key-value pairs of bibliographic data, metadata if you like,
|
To manage key-value pairs of bibliographic data, metadata if you like,
|
||||||
associated with a given web.
|
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
|
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.
|
convenient to store them directly here than to use a dictionary.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
typedef struct web_bibliographic_datum {
|
typedef struct web_bibliographic_datum {
|
||||||
struct text_stream *key;
|
struct text_stream *key;
|
||||||
struct text_stream *value;
|
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:
|
@ 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)
|
LOOP_OVER_LINKED_LIST(bd, web_bibliographic_datum, Wm->bibliographic_data)
|
||||||
|
|
||||||
@ The following check the rules:
|
@ The following check the rules:
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
int Bibliographic::datum_can_be_declared(web_md *Wm, text_stream *key) {
|
int Bibliographic::datum_can_be_declared(web_md *Wm, text_stream *key) {
|
||||||
web_bibliographic_datum *bd = Bibliographic::look_up_datum(Wm, key);
|
web_bibliographic_datum *bd = Bibliographic::look_up_datum(Wm, key);
|
||||||
if (bd == NULL) return FALSE;
|
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;
|
return bd->on_or_off;
|
||||||
}
|
}
|
||||||
|
|
||||||
@h Initialising a web.
|
@ \section{Initialising a web.}
|
||||||
Each web has the following slate of data:
|
Each web has the following slate of data:
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void Bibliographic::initialise_data(web_md *Wm) {
|
void Bibliographic::initialise_data(web_md *Wm) {
|
||||||
web_bibliographic_datum *bd;
|
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
|
@ Once the declarations for a web have been processed, the following is called
|
||||||
to check that all the mandatory declarations have indeed been made:
|
to check that all the mandatory declarations have indeed been made:
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void Bibliographic::check_required_data(web_md *Wm) {
|
void Bibliographic::check_required_data(web_md *Wm) {
|
||||||
web_bibliographic_datum *bd;
|
web_bibliographic_datum *bd;
|
||||||
LOOP_OVER_BIBLIOGRAPHIC_DATA(bd, Wm)
|
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);
|
"The web does not specify '%S: ...'", bd->key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@h Reading bibliographic data.
|
@ \section{Reading bibliographic data.}
|
||||||
Key names are case-sensitive.
|
Key names are case-sensitive.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
text_stream *Bibliographic::get_datum(web_md *Wm, text_stream *key) {
|
text_stream *Bibliographic::get_datum(web_md *Wm, text_stream *key) {
|
||||||
web_bibliographic_datum *bd = Bibliographic::look_up_datum(Wm, key);
|
web_bibliographic_datum *bd = Bibliographic::look_up_datum(Wm, key);
|
||||||
if (bd) return bd->value;
|
if (bd) return bd->value;
|
||||||
|
@ -118,20 +119,20 @@ web_bibliographic_datum *Bibliographic::look_up_datum(web_md *Wm, text_stream *k
|
||||||
return NULL;
|
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,
|
Note that a key-value pair is created if the key doesn't exist at present,
|
||||||
so this routine never fails.
|
so this routine never fails.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
web_bibliographic_datum *Bibliographic::set_datum(web_md *Wm, text_stream *key, text_stream *val) {
|
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);
|
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);
|
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;
|
return bd;
|
||||||
}
|
}
|
||||||
|
|
||||||
@<Create a new datum, then@> =
|
<<Create a new datum, then>>=
|
||||||
bd = CREATE(web_bibliographic_datum);
|
bd = CREATE(web_bibliographic_datum);
|
||||||
bd->key = Str::duplicate(key);
|
bd->key = Str::duplicate(key);
|
||||||
bd->value = Str::duplicate(val);
|
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
|
written to a "Capitalized Title" key. (This enables cover sheets which
|
||||||
want to typeset the title in full caps to do so.)
|
want to typeset the title in full caps to do so.)
|
||||||
|
|
||||||
@<Also set a capitalized form@> =
|
<<Also set a capitalized form>>=
|
||||||
TEMPORARY_TEXT(recapped)
|
TEMPORARY_TEXT(recapped)
|
||||||
Str::copy(recapped, val);
|
Str::copy(recapped, val);
|
||||||
LOOP_THROUGH_TEXT(P, recapped)
|
LOOP_THROUGH_TEXT(P, recapped)
|
|
@ -2,11 +2,11 @@
|
||||||
|
|
||||||
Manages the build metadata for an inweb project.
|
Manages the build metadata for an inweb project.
|
||||||
|
|
||||||
@h About build files.
|
@ \section{About build files.}
|
||||||
When we read a web, we look for a file in it called |build.txt|. If no such
|
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.
|
file exists, we look for the same thing in the current working directory.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
filename *BuildFiles::build_file_for_web(web_md *WS) {
|
filename *BuildFiles::build_file_for_web(web_md *WS) {
|
||||||
filename *F = Filenames::in(WS->path_to_web, I"build.txt");
|
filename *F = Filenames::in(WS->path_to_web, I"build.txt");
|
||||||
if (TextFiles::exists(F)) return F;
|
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:
|
@ The format of such a file is very simple: up to three text fields:
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
typedef struct build_file_data {
|
typedef struct build_file_data {
|
||||||
struct text_stream *prerelease_text;
|
struct text_stream *prerelease_text;
|
||||||
struct text_stream *build_code;
|
struct text_stream *build_code;
|
||||||
|
@ -26,7 +26,7 @@ typedef struct build_file_data {
|
||||||
|
|
||||||
@ Here's how to read in a build file:
|
@ Here's how to read in a build file:
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
build_file_data BuildFiles::read(filename *F) {
|
build_file_data BuildFiles::read(filename *F) {
|
||||||
build_file_data bfd;
|
build_file_data bfd;
|
||||||
bfd.prerelease_text = Str::new();
|
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:
|
@ And here is how to write one:
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void BuildFiles::write(build_file_data bfd, filename *F) {
|
void BuildFiles::write(build_file_data bfd, filename *F) {
|
||||||
text_stream vr_stream;
|
text_stream vr_stream;
|
||||||
text_stream *OUT = &vr_stream;
|
text_stream *OUT = &vr_stream;
|
||||||
|
@ -69,11 +69,11 @@ void BuildFiles::write(build_file_data bfd, filename *F) {
|
||||||
Streams::close(OUT);
|
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
|
Whenever a web is read in by Inweb, its build file is looked at in order to
|
||||||
set some bibliographic data.
|
set some bibliographic data.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void BuildFiles::set_bibliographic_data_for(web_md *WS) {
|
void BuildFiles::set_bibliographic_data_for(web_md *WS) {
|
||||||
filename *F = BuildFiles::build_file_for_web(WS);
|
filename *F = BuildFiles::build_file_for_web(WS);
|
||||||
if (F) {
|
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
|
called even if no build file had ever been found, so it's quite legal for
|
||||||
the Contents page to specify all of this.
|
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.
|
guaranteed to produce a semver-legal version number.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void BuildFiles::deduce_semver(web_md *WS) {
|
void BuildFiles::deduce_semver(web_md *WS) {
|
||||||
TEMPORARY_TEXT(combined)
|
TEMPORARY_TEXT(combined)
|
||||||
text_stream *s = Bibliographic::get_datum(WS, I"Semantic Version Number");
|
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)
|
DISCARD_TEXT(combined)
|
||||||
}
|
}
|
||||||
|
|
||||||
@h Advancing.
|
@ \section{Advancing.}
|
||||||
We update the build date to today and, if supplied, also increment the build
|
We update the build date to today and, if supplied, also increment the build
|
||||||
number if we find that the date has changed.
|
number if we find that the date has changed.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void BuildFiles::advance_for_web(web_md *WS) {
|
void BuildFiles::advance_for_web(web_md *WS) {
|
||||||
filename *F = BuildFiles::build_file_for_web(WS);
|
filename *F = BuildFiles::build_file_for_web(WS);
|
||||||
if (F) BuildFiles::advance(F);
|
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
|
@ 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
|
[[dateline]] match today's date in this format, we return [[TRUE]]; otherwise we
|
||||||
rewrite |dateline| to today and return |FALSE|.
|
rewrite [[dateline]] to today and return [[FALSE]].
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
int BuildFiles::dated_today(text_stream *dateline) {
|
int BuildFiles::dated_today(text_stream *dateline) {
|
||||||
char *monthname[12] = { "January", "February", "March", "April", "May", "June",
|
char *monthname[12] = { "January", "February", "March", "April", "May", "June",
|
||||||
"July", "August", "September", "October", "November", "December" };
|
"July", "August", "September", "October", "November", "December" };
|
||||||
|
@ -161,16 +161,16 @@ int BuildFiles::dated_today(text_stream *dateline) {
|
||||||
return rv;
|
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
|
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
|
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|
|
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.
|
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
|
This allows for 21384 distinct build codes, enough to use one each day for
|
||||||
some 58 years.
|
some 58 years.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void BuildFiles::increment(text_stream *T) {
|
void BuildFiles::increment(text_stream *T) {
|
||||||
if (Str::len(T) != 4) Errors::with_text("build code malformed: %S", T);
|
if (Str::len(T) != 4) Errors::with_text("build code malformed: %S", T);
|
||||||
else {
|
else {
|
||||||
|
@ -178,8 +178,8 @@ void BuildFiles::increment(text_stream *T) {
|
||||||
int L = Str::get_at(T, 1);
|
int L = Str::get_at(T, 1);
|
||||||
int M1 = Str::get_at(T, 2) - '0';
|
int M1 = Str::get_at(T, 2) - '0';
|
||||||
int M2 = Str::get_at(T, 3) - '0';
|
int M2 = Str::get_at(T, 3) - '0';
|
||||||
if ((N < 0) || (N > 9) || (L < 'A') || (L > 'Z') ||
|
if ((N < 0) [[| (N > 9) || (L < 'A') || (L > 'Z') |]]
|
||||||
(M1 < 0) || (M1 > 9) || (M2 < 0) || (M2 > 9)) {
|
(M1 < 0) [[| (M1 > 9) || (M2 < 0) |]] (M2 > 9)) {
|
||||||
Errors::with_text("build code malformed: %S", T);
|
Errors::with_text("build code malformed: %S", T);
|
||||||
} else {
|
} else {
|
||||||
M2++;
|
M2++;
|
|
@ -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.
|
[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
|
@ 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.
|
functions to have a shared state of their own.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
typedef struct simple_tangle_docket {
|
typedef struct simple_tangle_docket {
|
||||||
void (*raw_callback)(struct text_stream *, struct simple_tangle_docket *);
|
void (*raw_callback)(struct text_stream *, struct simple_tangle_docket *);
|
||||||
void (*command_callback)(struct text_stream *, struct text_stream *,
|
void (*command_callback)(struct text_stream *, struct text_stream *,
|
||||||
|
@ -27,7 +27,7 @@ typedef struct simple_tangle_docket {
|
||||||
struct pathname *web_path;
|
struct pathname *web_path;
|
||||||
} simple_tangle_docket;
|
} simple_tangle_docket;
|
||||||
|
|
||||||
@ =
|
<<*>>=
|
||||||
simple_tangle_docket SimpleTangler::new_docket(
|
simple_tangle_docket SimpleTangler::new_docket(
|
||||||
void (*A)(struct text_stream *, struct simple_tangle_docket *),
|
void (*A)(struct text_stream *, struct simple_tangle_docket *),
|
||||||
void (*B)(struct text_stream *, struct text_stream *,
|
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
|
should open), or a section (which the tangler should find and open), or a
|
||||||
whole web of section files (ditto):
|
whole web of section files (ditto):
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void SimpleTangler::tangle_text(simple_tangle_docket *docket, text_stream *text) {
|
void SimpleTangler::tangle_text(simple_tangle_docket *docket, text_stream *text) {
|
||||||
SimpleTangler::tangle_L1(docket, text, NULL, NULL, FALSE);
|
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);
|
SimpleTangler::tangle_L1(docket, NULL, NULL, NULL, TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ =
|
<<*>>=
|
||||||
void SimpleTangler::tangle_L1(simple_tangle_docket *docket, text_stream *text,
|
void SimpleTangler::tangle_L1(simple_tangle_docket *docket, text_stream *text,
|
||||||
filename *F, text_stream *leafname, int whole_web) {
|
filename *F, text_stream *leafname, int whole_web) {
|
||||||
TEMPORARY_TEXT(T)
|
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.
|
@ First, dispose of the "whole web" possibility.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void SimpleTangler::tangle_L2(OUTPUT_STREAM, text_stream *text, filename *F,
|
void SimpleTangler::tangle_L2(OUTPUT_STREAM, text_stream *text, filename *F,
|
||||||
text_stream *leafname, simple_tangle_docket *docket, int whole_web) {
|
text_stream *leafname, simple_tangle_docket *docket, int whole_web) {
|
||||||
if (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.
|
not so much.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void SimpleTangler::tangle_L3(OUTPUT_STREAM, text_stream *text,
|
void SimpleTangler::tangle_L3(OUTPUT_STREAM, text_stream *text,
|
||||||
text_stream *leafname, simple_tangle_docket *docket, filename *F) {
|
text_stream *leafname, simple_tangle_docket *docket, filename *F) {
|
||||||
int comment = FALSE;
|
int comment = FALSE;
|
||||||
FILE *Input_File = NULL;
|
FILE *Input_File = NULL;
|
||||||
if ((Str::len(leafname) > 0) || (F)) {
|
if ((Str::len(leafname) > 0) || (F)) {
|
||||||
@<Open the file@>;
|
<<Open the file>>;
|
||||||
comment = TRUE;
|
comment = TRUE;
|
||||||
}
|
}
|
||||||
@<Tangle the material@>;
|
<<Tangle the material>>;
|
||||||
if (Input_File) fclose(Input_File);
|
if (Input_File) fclose(Input_File);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ Note that if we are looking for an explicit section -- say, |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|.
|
from a web [[W]], we translate that into the path [[W/Sections/Juggling.i6t]].
|
||||||
|
|
||||||
@<Open the file@> =
|
<<Open the file>>=
|
||||||
if (F) {
|
if (F) {
|
||||||
Input_File = Filenames::fopen(F, "r");
|
Input_File = Filenames::fopen(F, "r");
|
||||||
} else if (Str::len(leafname) > 0) {
|
} 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)
|
if (Input_File == NULL)
|
||||||
(*(docket->error_callback))("unable to open the file '%S'", leafname);
|
(*(docket->error_callback))("unable to open the file '%S'", leafname);
|
||||||
|
|
||||||
@<Tangle the material@> =
|
<<Tangle the material>>=
|
||||||
TEMPORARY_TEXT(command)
|
TEMPORARY_TEXT(command)
|
||||||
TEMPORARY_TEXT(argument)
|
TEMPORARY_TEXT(argument)
|
||||||
int skip_part = FALSE, extract = FALSE;
|
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 {
|
do {
|
||||||
Str::clear(command);
|
Str::clear(command);
|
||||||
Str::clear(argument);
|
Str::clear(argument);
|
||||||
@<Read next character@>;
|
<<Read next character>>;
|
||||||
NewCharacter: if (cr == EOF) break;
|
NewCharacter: if (cr == EOF) break;
|
||||||
if (((cr == '@') || (cr == '=')) && (col == 1)) {
|
if (((cr == '@') || (cr == '=')) && (col == 1)) {
|
||||||
int inweb_syntax = -1;
|
int inweb_syntax = -1;
|
||||||
if (cr == '=') @<Read the rest of line as an equals-heading@>
|
if (cr == '=') <<Read the rest of line as an equals-heading>>
|
||||||
else @<Read the rest of line as an at-heading@>;
|
else <<Read the rest of line as an at-heading>>;
|
||||||
@<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>>;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (comment == FALSE) @<Deal with material which isn't commentary@>;
|
if (comment == FALSE) <<Deal with material which isn't commentary>>;
|
||||||
} while (cr != EOF);
|
} while (cr != EOF);
|
||||||
DISCARD_TEXT(command)
|
DISCARD_TEXT(command)
|
||||||
DISCARD_TEXT(argument)
|
DISCARD_TEXT(argument)
|
||||||
|
|
||||||
@ Our text files are encoded as ISO Latin-1, not as Unicode UTF-8, so ordinary
|
@ 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
|
[[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
|
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
|
trouble over [[0a0d]] or [[0d0a]] combinations.) The built-in template files, almost
|
||||||
always the only ones used, are line terminated |0x0a| in Unix fashion.
|
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);
|
if (Input_File) cr = fgetc(Input_File);
|
||||||
else if (text) {
|
else if (text) {
|
||||||
cr = Str::get_at(text, sfp); if (cr == 0) cr = EOF; else sfp++;
|
cr = Str::get_at(text, sfp); if (cr == 0) cr = EOF; else sfp++;
|
||||||
} else cr = EOF;
|
} else cr = EOF;
|
||||||
col++; if ((cr == 10) || (cr == 13)) col = 0;
|
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.
|
commands can be used, at least.
|
||||||
|
|
||||||
@d INWEB_PARAGRAPH_SYNTAX 1
|
<<*>>=
|
||||||
@d INWEB_CODE_SYNTAX 2
|
#define INWEB_PARAGRAPH_SYNTAX 1
|
||||||
@d INWEB_DASH_SYNTAX 3
|
#define INWEB_CODE_SYNTAX 2
|
||||||
@d INWEB_PURPOSE_SYNTAX 4
|
#define INWEB_DASH_SYNTAX 3
|
||||||
@d INWEB_FIGURE_SYNTAX 5
|
#define INWEB_PURPOSE_SYNTAX 4
|
||||||
@d INWEB_EQUALS_SYNTAX 6
|
#define INWEB_FIGURE_SYNTAX 5
|
||||||
@d INWEB_EXTRACT_SYNTAX 7
|
#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)
|
TEMPORARY_TEXT(at_cmd)
|
||||||
int committed = FALSE, unacceptable_character = FALSE;
|
int committed = FALSE, unacceptable_character = FALSE;
|
||||||
while (TRUE) {
|
while (TRUE) {
|
||||||
@<Read next character@>;
|
<<Read next character>>;
|
||||||
if ((committed == FALSE) && ((cr == 10) || (cr == 13) || (cr == ' '))) {
|
if ((committed == FALSE) && ((cr == 10) [[| (cr == 13) |]] (cr == ' '))) {
|
||||||
if (Str::eq_wide_string(at_cmd, L"p"))
|
if (Str::eq_wide_string(at_cmd, L"p"))
|
||||||
inweb_syntax = INWEB_PARAGRAPH_SYNTAX;
|
inweb_syntax = INWEB_PARAGRAPH_SYNTAX;
|
||||||
else if (Str::eq_wide_string(at_cmd, L"h"))
|
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'))
|
if (!(((cr >= 'A') && (cr <= 'Z')) || ((cr >= 'a') && (cr <= 'z'))
|
||||||
|| ((cr >= '0') && (cr <= '9'))
|
|| ((cr >= '0') && (cr <= '9'))
|
||||||
|| (cr == '-') || (cr == '>') || (cr == ':') || (cr == '_')))
|
[[| (cr == '-') || (cr == '>') || (cr == ':') |]] (cr == '_')))
|
||||||
unacceptable_character = TRUE;
|
unacceptable_character = TRUE;
|
||||||
if ((cr == 10) || (cr == 13)) break;
|
if ((cr == 10) || (cr == 13)) break;
|
||||||
PUT_TO(at_cmd, cr);
|
PUT_TO(at_cmd, cr);
|
||||||
|
@ -210,10 +211,10 @@ commands can be used, at least.
|
||||||
Str::copy(command, at_cmd);
|
Str::copy(command, at_cmd);
|
||||||
DISCARD_TEXT(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)
|
TEMPORARY_TEXT(equals_cmd)
|
||||||
while (TRUE) {
|
while (TRUE) {
|
||||||
@<Read next character@>;
|
<<Read next character>>;
|
||||||
if ((cr == 10) || (cr == 13)) break;
|
if ((cr == 10) || (cr == 13)) break;
|
||||||
PUT_TO(equals_cmd, cr);
|
PUT_TO(equals_cmd, cr);
|
||||||
}
|
}
|
||||||
|
@ -231,14 +232,14 @@ commands can be used, at least.
|
||||||
Regexp::dispose_of(&mr);
|
Regexp::dispose_of(&mr);
|
||||||
DISCARD_TEXT(equals_cmd)
|
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) {
|
switch (inweb_syntax) {
|
||||||
case INWEB_PARAGRAPH_SYNTAX: {
|
case INWEB_PARAGRAPH_SYNTAX: {
|
||||||
TEMPORARY_TEXT(heading_name)
|
TEMPORARY_TEXT(heading_name)
|
||||||
Str::copy_tail(heading_name, command, 2);
|
Str::copy_tail(heading_name, command, 2);
|
||||||
int c;
|
int c;
|
||||||
while (((c = Str::get_last_char(heading_name)) != 0) &&
|
while (((c = Str::get_last_char(heading_name)) != 0) &&
|
||||||
((c == ' ') || (c == '\t') || (c == '.')))
|
((c == ' ') [[| (c == '\t') |]] (c == '.')))
|
||||||
Str::delete_last_character(heading_name);
|
Str::delete_last_character(heading_name);
|
||||||
if (Str::len(heading_name) == 0)
|
if (Str::len(heading_name) == 0)
|
||||||
(*(docket->error_callback))("Empty heading name", NULL);
|
(*(docket->error_callback))("Empty heading name", NULL);
|
||||||
|
@ -266,11 +267,11 @@ commands can be used, at least.
|
||||||
case INWEB_FIGURE_SYNTAX: break;
|
case INWEB_FIGURE_SYNTAX: break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@<Deal with material which isn't commentary@> =
|
<<Deal with material which isn't commentary>>=
|
||||||
if (cr == '{') {
|
if (cr == '{') {
|
||||||
@<Read next character@>;
|
<<Read next character>>;
|
||||||
if ((cr == '-') && (docket->command_callback)) {
|
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;
|
if (Str::get_first_char(command) == '!') continue;
|
||||||
(*(docket->command_callback))(OUT, command, argument, docket);
|
(*(docket->command_callback))(OUT, command, argument, docket);
|
||||||
continue;
|
continue;
|
||||||
|
@ -280,9 +281,9 @@ commands can be used, at least.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((cr == '(') && (docket->bplus_callback)) {
|
if ((cr == '(') && (docket->bplus_callback)) {
|
||||||
@<Read next character@>;
|
<<Read next character>>;
|
||||||
if (cr == '+') {
|
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;
|
continue;
|
||||||
} else { /* otherwise the open bracket was a literal */
|
} else { /* otherwise the open bracket was a literal */
|
||||||
PUT_TO(OUT, '(');
|
PUT_TO(OUT, '(');
|
||||||
|
@ -291,29 +292,29 @@ commands can be used, at least.
|
||||||
}
|
}
|
||||||
PUT_TO(OUT, cr);
|
PUT_TO(OUT, cr);
|
||||||
|
|
||||||
@ And here we read a normal command. The command name 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
|
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 |}|.
|
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(command);
|
||||||
Str::clear(argument);
|
Str::clear(argument);
|
||||||
int com_mode = TRUE;
|
int com_mode = TRUE;
|
||||||
while (TRUE) {
|
while (TRUE) {
|
||||||
@<Read next character@>;
|
<<Read next character>>;
|
||||||
if ((cr == '}') || (cr == EOF)) break;
|
if ((cr == '}') || (cr == EOF)) break;
|
||||||
if ((cr == ':') && (com_mode)) { com_mode = FALSE; continue; }
|
if ((cr == ':') && (com_mode)) { com_mode = FALSE; continue; }
|
||||||
if (com_mode) PUT_TO(command, cr);
|
if (com_mode) PUT_TO(command, cr);
|
||||||
else PUT_TO(argument, 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:
|
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)
|
TEMPORARY_TEXT(material)
|
||||||
while (TRUE) {
|
while (TRUE) {
|
||||||
@<Read next character@>;
|
<<Read next character>>;
|
||||||
if (cr == EOF) break;
|
if (cr == EOF) break;
|
||||||
if ((cr == ')') && (Str::get_last_char(material) == '+')) {
|
if ((cr == ')') && (Str::get_last_char(material) == '+')) {
|
||||||
Str::delete_last_character(material); break; }
|
Str::delete_last_character(material); break; }
|
|
@ -2,32 +2,33 @@
|
||||||
|
|
||||||
To search for included modules, and track dependencies between them.
|
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.
|
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
|
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
|
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
|
makefile for a suite of tools, it can also discover multiple webs by other
|
||||||
means.
|
means.
|
||||||
|
|
||||||
@e READING_WEB_MOM from 0
|
<<*>>=
|
||||||
@e MAKEFILE_TOOL_MOM
|
enum READING_WEB_MOM from 0
|
||||||
@e MAKEFILE_WEB_MOM
|
enum MAKEFILE_TOOL_MOM
|
||||||
@e MAKEFILE_MODULE_MOM
|
enum MAKEFILE_WEB_MOM
|
||||||
|
enum MAKEFILE_MODULE_MOM
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
typedef struct module {
|
typedef struct module {
|
||||||
struct pathname *module_location;
|
struct pathname *module_location;
|
||||||
struct text_stream *module_name;
|
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;
|
struct text_stream *module_tag;
|
||||||
int origin_marker; /* one of the |*_MOM| values above */
|
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 *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 */
|
struct linked_list *sections_md; /* of [[section_md]]: just the ones in this module */
|
||||||
CLASS_DEFINITION
|
CLASS_DEFINITION
|
||||||
} module;
|
} module;
|
||||||
|
|
||||||
@ =
|
<<*>>=
|
||||||
module *WebModules::new(text_stream *name, pathname *at, int m) {
|
module *WebModules::new(text_stream *name, pathname *at, int m) {
|
||||||
module *M = CREATE(module);
|
module *M = CREATE(module);
|
||||||
M->module_location = at;
|
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
|
contains a suite of utility routines, or a major component of a program, but
|
||||||
which is not a program in its own right.
|
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
|
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) {
|
module *WebModules::create_main_module(web_md *WS) {
|
||||||
return WebModules::new(I"(main)", WS->path_to_web, READING_WEB_MOM);
|
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
|
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
|
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
|
needed when constructing makefiles, since the source code in B affects the
|
||||||
program generated by A.
|
program generated by A.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void WebModules::dependency(module *A, module *B) {
|
void WebModules::dependency(module *A, module *B) {
|
||||||
if ((A == NULL) || (B == NULL)) internal_error("no module");
|
if ((A == NULL) || (B == NULL)) internal_error("no module");
|
||||||
ADD_TO_LINKED_LIST(B, module, A->dependencies);
|
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.
|
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.)
|
(At one time there was going to be a more elaborate search hierarchy.)
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
typedef struct module_search {
|
typedef struct module_search {
|
||||||
struct pathname *path_to_search;
|
struct pathname *path_to_search;
|
||||||
CLASS_DEFINITION
|
CLASS_DEFINITION
|
||||||
} module_search;
|
} module_search;
|
||||||
|
|
||||||
@ =
|
<<*>>=
|
||||||
module_search *WebModules::make_search_path(pathname *ext_path) {
|
module_search *WebModules::make_search_path(pathname *ext_path) {
|
||||||
module_search *ms = CREATE(module_search);
|
module_search *ms = CREATE(module_search);
|
||||||
ms->path_to_search = ext_path;
|
ms->path_to_search = ext_path;
|
||||||
return ms;
|
return ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ When a web's contents page says to |import Blah|, how do we find the module
|
@ 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:
|
called [[Blah]] on disc? We try four possibilities in sequence:
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
module *WebModules::find(web_md *WS, module_search *ms, text_stream *name, pathname *X) {
|
module *WebModules::find(web_md *WS, module_search *ms, text_stream *name, pathname *X) {
|
||||||
TEMPORARY_TEXT(T)
|
TEMPORARY_TEXT(T)
|
||||||
WRITE_TO(T, "%S-module", name);
|
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;
|
int N = 4;
|
||||||
for (int i=0; i<N; i++) {
|
for (int i=0; i<N; i++) {
|
||||||
pathname *P = Pathnames::from_text_relative(tries[i], T);
|
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)
|
DISCARD_TEXT(T)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ When the module is found (if it is), a suitable module structure is made,
|
@ 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);
|
pathname *Q = Pathnames::from_text(name);
|
||||||
module *M = WebModules::new(Pathnames::directory_name(Q), P, READING_WEB_MOM);
|
module *M = WebModules::new(Pathnames::directory_name(Q), P, READING_WEB_MOM);
|
||||||
WebModules::dependency(WS->as_module, M);
|
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
|
@ We accept that a plausibly-named directory is indeed the module being
|
||||||
sought if it looks like a web.
|
sought if it looks like a web.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
int WebModules::exists(pathname *P) {
|
int WebModules::exists(pathname *P) {
|
||||||
return WebMetadata::directory_looks_like_a_web(P);
|
return WebMetadata::directory_looks_like_a_web(P);
|
||||||
}
|
}
|
||||||
|
|
||||||
@h Resolving cross-reference names.
|
@ \section{Resolving cross-reference names.}
|
||||||
Suppose we are in module |from_M| and want to understand which section of
|
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,
|
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
|
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
|
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
|
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|,
|
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
|
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.
|
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
|
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
|
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
|
of that name: for example, [["foundation: Web Modules"]] would find the
|
||||||
section of code you are now reading.
|
section of code you are now reading.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
int WebModules::named_reference(module **return_M, section_md **return_Sm,
|
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 *named_as_module, text_stream *title, module *from_M, text_stream *text,
|
||||||
int list, int sections_only) {
|
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) {
|
LOOP_OVER_LINKED_LIST(M, module, from_M->dependencies) {
|
||||||
if (Str::eq_insensitive(M->module_name, seek_module)) {
|
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);
|
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++) {
|
for (int stage = 1; ((finds == 0) && (stage <= 2)); stage++) {
|
||||||
if (stage == 1) {
|
if (stage == 1) {
|
||||||
M = from_M;
|
M = from_M;
|
||||||
@<Look for references to chapters or sections in M@>;
|
<<Look for references to chapters or sections in M>>;
|
||||||
}
|
}
|
||||||
if (stage == 2) {
|
if (stage == 2) {
|
||||||
LOOP_OVER_LINKED_LIST(M, module, from_M->dependencies)
|
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;
|
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 (M == NULL) internal_error("no module");
|
||||||
if (Str::eq_insensitive(M->module_name, seek))
|
if (Str::eq_insensitive(M->module_name, seek))
|
||||||
@<Found first section in module@>;
|
<<Found first section in module>>;
|
||||||
chapter_md *Cm;
|
chapter_md *Cm;
|
||||||
section_md *Sm;
|
section_md *Sm;
|
||||||
LOOP_OVER_LINKED_LIST(Cm, chapter_md, M->chapters_md) {
|
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_title, seek)) ||
|
||||||
(Str::eq_insensitive(Cm->ch_basic_title, seek)) ||
|
(Str::eq_insensitive(Cm->ch_basic_title, seek)) ||
|
||||||
(Str::eq_insensitive(Cm->ch_decorated_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)
|
LOOP_OVER_LINKED_LIST(Sm, section_md, Cm->sections_md)
|
||||||
if (Str::eq_insensitive(Sm->sect_title, seek))
|
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++;
|
finds++;
|
||||||
if (finds == 1) {
|
if (finds == 1) {
|
||||||
*return_M = M; *return_Sm = FIRST_IN_LINKED_LIST(section_md, M->sections_md);
|
*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);
|
if (list) WRITE_TO(STDERR, "(%d) Module '%S'\n", finds, M->module_name);
|
||||||
|
|
||||||
@<Found first section in chapter@> =
|
<<Found first section in chapter>>=
|
||||||
finds++;
|
finds++;
|
||||||
if (finds == 1) {
|
if (finds == 1) {
|
||||||
*return_M = M; *return_Sm = FIRST_IN_LINKED_LIST(section_md, Cm->sections_md);
|
*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",
|
if (list) WRITE_TO(STDERR, "(%d) Chapter '%S' of module '%S'\n",
|
||||||
finds, Cm->ch_title, M->module_name);
|
finds, Cm->ch_title, M->module_name);
|
||||||
|
|
||||||
@<Found section by name@> =
|
<<Found section by name>>=
|
||||||
finds++;
|
finds++;
|
||||||
if (finds == 1) {
|
if (finds == 1) {
|
||||||
*return_M = M; *return_Sm = Sm;
|
*return_M = M; *return_Sm = Sm;
|
|
@ -3,7 +3,7 @@
|
||||||
To read the structure of a literate programming web from a path in the file
|
To read the structure of a literate programming web from a path in the file
|
||||||
system.
|
system.
|
||||||
|
|
||||||
@h Introduction.
|
@ \section{Introduction.}
|
||||||
Webs are literate programs for the Inweb LP system. A single web consists of
|
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
|
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
|
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
|
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.
|
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.
|
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
|
Our task in this section will be to read a web from the filing system and
|
||||||
produce the following metadata structure.
|
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 {
|
typedef struct web_md {
|
||||||
struct pathname *path_to_web; /* relative to the current working directory */
|
struct pathname *path_to_web; /* relative to the current working directory */
|
||||||
struct filename *single_file; /* 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 */
|
struct semantic_version_number version_number; /* as deduced from bibliographic data */
|
||||||
int default_syntax; /* which version syntax the sections will have */
|
int default_syntax; /* which version syntax the sections will have */
|
||||||
int chaptered; /* has the author explicitly divided it into named chapters? */
|
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 module *as_module; /* the root of a small dependency graph */
|
||||||
|
|
||||||
struct filename *contents_filename; /* or |NULL| for a single-file web */
|
struct filename *contents_filename; /* or [[NULL]] for a single-file web */
|
||||||
struct linked_list *tangle_target_names; /* of |text_stream| */
|
struct linked_list *tangle_target_names; /* of [[text_stream]] */
|
||||||
struct linked_list *header_filenames; /* of |filename| */
|
struct linked_list *header_filenames; /* of [[filename]] */
|
||||||
|
|
||||||
struct linked_list *chapters_md; /* of |chapter_md| */
|
struct linked_list *chapters_md; /* of [[chapter_md]] */
|
||||||
struct linked_list *sections_md; /* of |section_md| */
|
struct linked_list *sections_md; /* of [[section_md]] */
|
||||||
CLASS_DEFINITION
|
CLASS_DEFINITION
|
||||||
} web_md;
|
} 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 {
|
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_title; /* e.g., "Chapter 3: Fresh Water Fish" */
|
||||||
struct text_stream *ch_basic_title; /* e.g., "Chapter 3" */
|
struct text_stream *ch_basic_title; /* e.g., "Chapter 3" */
|
||||||
struct text_stream *ch_decorated_title; /* e.g., "Fresh Water Fish" */
|
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? */
|
int imported; /* from a different web? */
|
||||||
|
|
||||||
struct linked_list *sections_md; /* of |section_md| */
|
struct linked_list *sections_md; /* of [[section_md]] */
|
||||||
CLASS_DEFINITION
|
CLASS_DEFINITION
|
||||||
} chapter_md;
|
} 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 {
|
typedef struct section_md {
|
||||||
struct text_stream *sect_title; /* e.g., "Program Control" */
|
struct text_stream *sect_title; /* e.g., "Program Control" */
|
||||||
struct text_stream *sect_range; /* e.g., "2/ct" */
|
struct text_stream *sect_range; /* e.g., "2/ct" */
|
||||||
|
@ -82,13 +83,13 @@ typedef struct section_md {
|
||||||
CLASS_DEFINITION
|
CLASS_DEFINITION
|
||||||
} section_md;
|
} 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,
|
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
|
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) {
|
web_md *WebMetadata::get_without_modules(pathname *P, filename *alt_F) {
|
||||||
return WebMetadata::get(P, alt_F, V2_SYNTAX, NULL, FALSE, FALSE, NULL);
|
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) {
|
module_search *I, int verbosely, int including_modules, pathname *path_to_inweb) {
|
||||||
if ((including_modules) && (I == NULL)) I = WebModules::make_search_path(NULL);
|
if ((including_modules) && (I == NULL)) I = WebModules::make_search_path(NULL);
|
||||||
web_md *Wm = CREATE(web_md);
|
web_md *Wm = CREATE(web_md);
|
||||||
@<Begin the bibliographic data@>;
|
<<Begin the bibliographic data>>;
|
||||||
@<Initialise the rest of the web MD@>;
|
<<Initialise the rest of the web MD>>;
|
||||||
WebMetadata::read_contents_page(Wm, Wm->as_module, I, verbosely,
|
WebMetadata::read_contents_page(Wm, Wm->as_module, I, verbosely,
|
||||||
including_modules, NULL, path_to_inweb);
|
including_modules, NULL, path_to_inweb);
|
||||||
@<Consolidate the bibliographic data@>;
|
<<Consolidate the bibliographic data>>;
|
||||||
@<Work out the section ranges@>;
|
<<Work out the section ranges>>;
|
||||||
return Wm;
|
return Wm;
|
||||||
}
|
}
|
||||||
|
|
||||||
@<Begin the bibliographic data@> =
|
<<Begin the bibliographic data>>=
|
||||||
Wm->bibliographic_data = NEW_LINKED_LIST(web_bibliographic_datum);
|
Wm->bibliographic_data = NEW_LINKED_LIST(web_bibliographic_datum);
|
||||||
Bibliographic::initialise_data(Wm);
|
Bibliographic::initialise_data(Wm);
|
||||||
|
|
||||||
@<Initialise the rest of the web MD@> =
|
<<Initialise the rest of the web MD>>=
|
||||||
if (P) {
|
if (P) {
|
||||||
Wm->path_to_web = P;
|
Wm->path_to_web = P;
|
||||||
Wm->single_file = NULL;
|
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->header_filenames = NEW_LINKED_LIST(filename);
|
||||||
Wm->as_module = WebModules::create_main_module(Wm);
|
Wm->as_module = WebModules::create_main_module(Wm);
|
||||||
|
|
||||||
@<Consolidate the bibliographic data@> =
|
<<Consolidate the bibliographic data>>=
|
||||||
Bibliographic::check_required_data(Wm);
|
Bibliographic::check_required_data(Wm);
|
||||||
BuildFiles::set_bibliographic_data_for(Wm);
|
BuildFiles::set_bibliographic_data_for(Wm);
|
||||||
BuildFiles::deduce_semver(Wm);
|
BuildFiles::deduce_semver(Wm);
|
||||||
|
|
||||||
@ If no range is supplied, we make one ourselves.
|
@ 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? */
|
int sequential = FALSE; /* are we numbering sections sequentially? */
|
||||||
if (Str::eq(Bibliographic::get_datum(Wm, I"Sequential Section Ranges"), I"On"))
|
if (Str::eq(Bibliographic::get_datum(Wm, I"Sequential Section Ranges"), I"On"))
|
||||||
sequential = TRUE;
|
sequential = TRUE;
|
||||||
|
@ -147,12 +148,12 @@ web_md *WebMetadata::get(pathname *P, filename *alt_F, int syntax_version,
|
||||||
int section_counter = 1;
|
int section_counter = 1;
|
||||||
LOOP_OVER_LINKED_LIST(Sm, section_md, Cm->sections_md) {
|
LOOP_OVER_LINKED_LIST(Sm, section_md, Cm->sections_md) {
|
||||||
if (Str::len(Sm->sect_range) == 0)
|
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++;
|
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) {
|
if (sequential) {
|
||||||
WRITE_TO(Sm->sect_range, "%S/", Cm->ch_range);
|
WRITE_TO(Sm->sect_range, "%S/", Cm->ch_range);
|
||||||
WRITE_TO(Sm->sect_range, "s%d", section_counter);
|
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 {
|
do {
|
||||||
Str::clear(Sm->sect_range);
|
Str::clear(Sm->sect_range);
|
||||||
WRITE_TO(Sm->sect_range, "%S/", Cm->ch_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;
|
if (--letters_from_each_word == 0) break;
|
||||||
} while (Str::len(Sm->sect_range) > 5);
|
} 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"
|
@ We collapse words to an initial letter plus consonants: thus "electricity"
|
||||||
would be "elctrcty", since we don't count "y" as a vowel here.
|
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);
|
int sn = 0, sw = Str::len(Sm->sect_range);
|
||||||
if (Platform::is_folder_separator(Str::get_at(from, sn))) sn++;
|
if (Platform::is_folder_separator(Str::get_at(from, sn))) sn++;
|
||||||
int letters_from_current_word = 0;
|
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.
|
@ 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)
|
TEMPORARY_TEXT(original_range)
|
||||||
Str::copy(original_range, Sm->sect_range);
|
Str::copy(original_range, Sm->sect_range);
|
||||||
int disnum = 0, collision = FALSE;
|
int disnum = 0, collision = FALSE;
|
||||||
|
@ -224,10 +225,10 @@ would be "elctrcty", since we don't count "y" as a vowel here.
|
||||||
} while (collision);
|
} while (collision);
|
||||||
DISCARD_TEXT(original_range)
|
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
|
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
|
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
|
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
|
make it look odd). When the word "section" is used in the Inweb code, it
|
||||||
almost always means "section other than the contents".
|
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
|
own right: instead, it's the top few lines of the single file. We handle that
|
||||||
by halting at the junction point.
|
by halting at the junction point.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
typedef struct reader_state {
|
typedef struct reader_state {
|
||||||
struct web_md *Wm;
|
struct web_md *Wm;
|
||||||
struct filename *contents_filename;
|
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,
|
module_search *import_path, int verbosely,
|
||||||
int including_modules, pathname *path, pathname *X) {
|
int including_modules, pathname *path, pathname *X) {
|
||||||
reader_state RS;
|
reader_state RS;
|
||||||
@<Initialise the reader state@>;
|
<<Initialise the reader state>>;
|
||||||
|
|
||||||
int cl = TextFiles::read(RS.contents_filename, FALSE, "can't open contents file",
|
int cl = TextFiles::read(RS.contents_filename, FALSE, "can't open contents file",
|
||||||
TRUE, WebMetadata::read_contents_line, NULL, &RS);
|
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;
|
if (RS.section_count == 1) RS.last_section->is_a_singleton = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@<Initialise the reader state@> =
|
<<Initialise the reader state>>=
|
||||||
RS.Wm = Wm;
|
RS.Wm = Wm;
|
||||||
RS.reading_from = of_module;
|
RS.reading_from = of_module;
|
||||||
RS.in_biblio = TRUE;
|
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
|
and sets out bibliographic information about the web, the sections and their
|
||||||
organisation, and so on.
|
organisation, and so on.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void WebMetadata::read_contents_line(text_stream *line, text_file_position *tfp, void *X) {
|
void WebMetadata::read_contents_line(text_stream *line, text_file_position *tfp, void *X) {
|
||||||
reader_state *RS = (reader_state *) X;
|
reader_state *RS = (reader_state *) X;
|
||||||
if (RS->halted) return;
|
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;
|
begins_with_white_space = TRUE;
|
||||||
Str::trim_white_space(line);
|
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;
|
int syntax = RS->Wm->default_syntax;
|
||||||
|
|
||||||
filename *filename_of_single_file_web = NULL;
|
filename *filename_of_single_file_web = NULL;
|
||||||
if ((RS->halt_at_at) && (Str::get_at(line, 0) == '@'))
|
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
|
@ 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
|
no good simply to store this up for later: we have to change the web structure
|
||||||
immediately.
|
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"))
|
if (Str::eq(line, I"Web Syntax Version: 1"))
|
||||||
RS->Wm->default_syntax = V1_SYNTAX;
|
RS->Wm->default_syntax = V1_SYNTAX;
|
||||||
else if (Str::eq(line, I"Web Syntax Version: 2"))
|
else if (Str::eq(line, I"Web Syntax Version: 2"))
|
||||||
RS->Wm->default_syntax = V2_SYNTAX;
|
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
|
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
|
to give the web a single chapter ("Sections", range "S"), which contains a
|
||||||
single section ("All") consisting of the remainder of the single file.
|
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;
|
RS->halted = TRUE;
|
||||||
text_stream *new_chapter_range = I"S";
|
text_stream *new_chapter_range = I"S";
|
||||||
text_stream *language_name = NULL;
|
text_stream *language_name = NULL;
|
||||||
line = I"Sections";
|
line = I"Sections";
|
||||||
@<Create the new chapter with these details@>;
|
<<Create the new chapter with these details>>;
|
||||||
line = I"All";
|
line = I"All";
|
||||||
filename_of_single_file_web = tfp->text_file_filename;
|
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;
|
return;
|
||||||
|
|
||||||
@ With those two complications out of the way, we now know that we're reading
|
@ 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
|
series of bibliographic data values; then there's a blank line, and then
|
||||||
we're into the section listing.
|
we're into the section listing.
|
||||||
|
|
||||||
@<Read regular contents material@> =
|
<<Read regular contents material>>=
|
||||||
if (Str::len(line) == 0) @<End bibliographic data here, at the blank line@>
|
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 if (RS->in_biblio) <<Read the bibliographic data block at the top>>
|
||||||
else @<Read the roster of sections at the bottom@>;
|
else <<Read the roster of sections at the bottom>>;
|
||||||
|
|
||||||
@ At this point we've gone through the bibliographic lines at the top of the
|
@ 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.
|
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;
|
RS->in_biblio = FALSE;
|
||||||
|
|
||||||
@ The bibliographic data gives lines in any order specifying values of
|
@ The bibliographic data gives lines in any order specifying values of
|
||||||
variables with fixed names; a blank line ends the block.
|
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) {
|
if (RS->main_web_not_module) {
|
||||||
match_results mr = Regexp::create_mr();
|
match_results mr = Regexp::create_mr();
|
||||||
if (Regexp::match(&mr, line, L"(%c+?): (%c+?) *")) {
|
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]);
|
Str::copy(key, mr.exp[0]);
|
||||||
TEMPORARY_TEXT(value)
|
TEMPORARY_TEXT(value)
|
||||||
Str::copy(value, mr.exp[1]);
|
Str::copy(value, mr.exp[1]);
|
||||||
@<Set bibliographic key-value pair@>;
|
<<Set bibliographic key-value pair>>;
|
||||||
DISCARD_TEXT(key)
|
DISCARD_TEXT(key)
|
||||||
DISCARD_TEXT(value)
|
DISCARD_TEXT(value)
|
||||||
} else {
|
} else {
|
||||||
|
@ -403,7 +404,7 @@ variables with fixed names; a blank line ends the block.
|
||||||
Regexp::dispose_of(&mr);
|
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_can_be_declared(RS->Wm, key)) {
|
||||||
if (Bibliographic::datum_on_or_off(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"))) {
|
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
|
@ In the bulk of the contents, we find indented lines for sections and
|
||||||
unindented ones for chapters.
|
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 (begins_with_white_space == FALSE) {
|
||||||
if (Str::get_first_char(line) == '"') {
|
if (Str::get_first_char(line) == '"') {
|
||||||
RS->in_purpose = TRUE; Str::delete_first_character(line);
|
RS->in_purpose = TRUE; Str::delete_first_character(line);
|
||||||
}
|
}
|
||||||
if (RS->in_purpose == TRUE) @<Record the purpose of the current chapter@>
|
if (RS->in_purpose == TRUE) <<Record the purpose of the current chapter>>
|
||||||
else @<Read about a new chapter@>;
|
else <<Read about a new chapter>>;
|
||||||
} else @<Read about, and read in, a new section@>;
|
} else <<Read about, and read in, a new section>>;
|
||||||
|
|
||||||
@ After a declared chapter heading, subsequent lines form its purpose, until
|
@ 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 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.
|
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) == '"')) {
|
if ((Str::len(line) > 0) && (Str::get_last_char(line) == '"')) {
|
||||||
Str::truncate(line, Str::len(line)-1); RS->in_purpose = FALSE;
|
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:
|
@ 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(new_chapter_range) /* e.g., S, P, 1, 2, 3, A, B, ... */
|
||||||
TEMPORARY_TEXT(pdf_leafname)
|
TEMPORARY_TEXT(pdf_leafname)
|
||||||
text_stream *language_name = NULL;
|
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*)%)")) {
|
if (Regexp::match(&mr, line, L"(%c*%C) %(Independent(%c*)%)")) {
|
||||||
text_stream *title_alone = mr.exp[0];
|
text_stream *title_alone = mr.exp[0];
|
||||||
language_name = mr.exp[1];
|
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);
|
Str::copy(line, title_alone);
|
||||||
}
|
}
|
||||||
int this_is_a_chapter = TRUE;
|
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)
|
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(new_chapter_range)
|
||||||
DISCARD_TEXT(pdf_leafname)
|
DISCARD_TEXT(pdf_leafname)
|
||||||
Regexp::dispose_of(&mr);
|
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,
|
@ A chapter whose title marks it as Independent becomes a new tangle target,
|
||||||
with the same language as the main web unless stated otherwise.
|
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();
|
match_results mr = Regexp::create_mr();
|
||||||
if (Regexp::match(&mr, language_name, L" *"))
|
if (Regexp::match(&mr, language_name, L" *"))
|
||||||
language_name = Bibliographic::get_datum(RS->Wm, I"Language");
|
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];
|
language_name = mr.exp[0];
|
||||||
Regexp::dispose_of(&mr);
|
Regexp::dispose_of(&mr);
|
||||||
|
|
||||||
@<Create the new chapter with these details@> =
|
<<Create the new chapter with these details>>=
|
||||||
chapter_md *Cm = CREATE(chapter_md);
|
chapter_md *Cm = CREATE(chapter_md);
|
||||||
Cm->ch_range = Str::duplicate(new_chapter_range);
|
Cm->ch_range = Str::duplicate(new_chapter_range);
|
||||||
if (line == NULL) PRINT("Nullity!\n");
|
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
|
of registering a new section within a chapter -- more interesting because
|
||||||
we also read in and process its file.
|
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);
|
section_md *Sm = CREATE(section_md);
|
||||||
@<Initialise the section structure@>;
|
<<Initialise the section structure>>;
|
||||||
@<Add the section to the web and the current chapter@>;
|
<<Add the section to the web and the current chapter>>;
|
||||||
@<Work out the language and tangle target for the section@>;
|
<<Work out the language and tangle target for the section>>;
|
||||||
|
|
||||||
if (Sm->source_file_for_section == NULL)
|
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->source_file_for_section = filename_of_single_file_web;
|
||||||
Sm->using_syntax = syntax;
|
Sm->using_syntax = syntax;
|
||||||
Sm->is_a_singleton = FALSE;
|
Sm->is_a_singleton = FALSE;
|
||||||
|
@ -612,7 +613,7 @@ we also read in and process its file.
|
||||||
Regexp::dispose_of(&mr);
|
Regexp::dispose_of(&mr);
|
||||||
Sm->owning_module = RS->reading_from;
|
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;
|
chapter_md *Cm = RS->chapter_being_scanned;
|
||||||
RS->section_count++;
|
RS->section_count++;
|
||||||
RS->last_section = Sm;
|
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->Wm->sections_md);
|
||||||
ADD_TO_LINKED_LIST(Sm, section_md, RS->reading_from->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 */
|
Sm->sect_language_name = RS->chapter_being_scanned->ch_language_name; /* by default */
|
||||||
match_results mr = Regexp::create_mr();
|
match_results mr = Regexp::create_mr();
|
||||||
if (Regexp::match(&mr, line, L"(%c*%C) %(Independent (%c*) *%)")) {
|
if (Regexp::match(&mr, line, L"(%c*%C) %(Independent (%c*) *%)")) {
|
||||||
text_stream *title_alone = mr.exp[0];
|
text_stream *title_alone = mr.exp[0];
|
||||||
text_stream *language_name = mr.exp[1];
|
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);
|
Str::copy(Sm->sect_title, title_alone);
|
||||||
}
|
}
|
||||||
Regexp::dispose_of(&mr);
|
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;
|
text_stream *p = language_name;
|
||||||
if (Str::len(p) == 0) p = Bibliographic::get_datum(RS->Wm, I"Language");
|
if (Str::len(p) == 0) p = Bibliographic::get_datum(RS->Wm, I"Language");
|
||||||
Sm->sect_independent_language = Str::duplicate(p);
|
Sm->sect_independent_language = Str::duplicate(p);
|
||||||
|
|
||||||
@ If we're told that a section is called "Bells and Whistles", what filename
|
@ 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|,
|
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,
|
but the extension used doesn't have to be [[.w]]: for Inform 6 template files,
|
||||||
the extension needs to be |.i6t|. We allow either.
|
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)
|
TEMPORARY_TEXT(leafname_to_use)
|
||||||
WRITE_TO(leafname_to_use, "%S.i6t", Sm->sect_title);
|
WRITE_TO(leafname_to_use, "%S.i6t", Sm->sect_title);
|
||||||
pathname *P = RS->path_to;
|
pathname *P = RS->path_to;
|
||||||
|
@ -656,9 +657,9 @@ the extension needs to be |.i6t|. We allow either.
|
||||||
}
|
}
|
||||||
DISCARD_TEXT(leafname_to_use)
|
DISCARD_TEXT(leafname_to_use)
|
||||||
|
|
||||||
@h Relative pathnames or filenames.
|
@ \section{Relative pathnames or filenames.}
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
int WebMetadata::directory_looks_like_a_web(pathname *P) {
|
int WebMetadata::directory_looks_like_a_web(pathname *P) {
|
||||||
return TextFiles::exists(WebMetadata::contents_filename(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");
|
return Filenames::in(P, I"Contents.w");
|
||||||
}
|
}
|
||||||
|
|
||||||
@h Statistics.
|
@ \section{Statistics.}
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
int WebMetadata::chapter_count(web_md *Wm) {
|
int WebMetadata::chapter_count(web_md *Wm) {
|
||||||
int n = 0;
|
int n = 0;
|
||||||
chapter_md *Cm;
|
chapter_md *Cm;
|
Loading…
Reference in a new issue