mirror of
https://github.com/ganelson/inform.git
synced 2024-07-01 06:24:58 +03:00
160 lines
4.9 KiB
OpenEdge ABL
160 lines
4.9 KiB
OpenEdge ABL
[Nests::] Nests.
|
|
|
|
Nests are repositories of Inform-related resources.
|
|
|
|
@h Creation.
|
|
To "create" a nest here does not mean actually altering the file system, for
|
|
example by making a directory: nests here are merely notes in memory of
|
|
positions in the file system hierarchy which may or may not exist.
|
|
|
|
=
|
|
typedef struct inbuild_nest {
|
|
struct pathname *location;
|
|
int read_only; /* files cannot be written into this nest */
|
|
int tag_value; /* used to indicate whether internal, external, and such */
|
|
CLASS_DEFINITION
|
|
} inbuild_nest;
|
|
|
|
=
|
|
inbuild_nest *Nests::new(pathname *P) {
|
|
inbuild_nest *N = CREATE(inbuild_nest);
|
|
N->location = P;
|
|
N->read_only = FALSE;
|
|
N->tag_value = -1;
|
|
return N;
|
|
}
|
|
|
|
@ Nests used by the Inform and Inbuild tools are tagged with the following
|
|
comstamts. (There used to be quite a good joke here, but refactoring of the
|
|
code removed its premiss. Literate programming is like that sometimes.)
|
|
|
|
The sequence of the following enumerated values is very significant --
|
|
see below for why. Lower-tag-numbered origins are better than later ones.
|
|
|
|
@e MATERIALS_NEST_TAG from 1
|
|
@e EXTERNAL_NEST_TAG
|
|
@e GENERIC_NEST_TAG
|
|
@e INTERNAL_NEST_TAG
|
|
|
|
=
|
|
int Nests::get_tag(inbuild_nest *N) {
|
|
if (N == NULL) return -1;
|
|
return N->tag_value;
|
|
}
|
|
|
|
void Nests::set_tag(inbuild_nest *N, int t) {
|
|
if (N == NULL) internal_error("no nest");
|
|
N->tag_value = t;
|
|
}
|
|
|
|
@ =
|
|
void Nests::protect(inbuild_nest *N) {
|
|
N->read_only = TRUE;
|
|
}
|
|
|
|
@h Search list.
|
|
When we search for copies, we do so by looking through nests in a list. The
|
|
following builds such lists, removing duplicates -- where duplicates are
|
|
shown up by having the same textual form of pathname. (This is not foolproof
|
|
by any means: Unix is replete with ways to describe the same directory, thanks
|
|
to simlinks, |~| and so on. But in the circumstances arising inside Inbuild,
|
|
it will do. In any case, having duplicates would not actually matter: it
|
|
would just produce search results which were more copious than needed.)
|
|
|
|
=
|
|
void Nests::add_to_search_sequence(linked_list *search_list, inbuild_nest *N) {
|
|
TEMPORARY_TEXT(NS)
|
|
WRITE_TO(NS, "%p", N->location);
|
|
int already_here = FALSE;
|
|
inbuild_nest *M;
|
|
LOOP_OVER_LINKED_LIST(M, inbuild_nest, search_list) {
|
|
TEMPORARY_TEXT(MS)
|
|
WRITE_TO(NS, "%p", M->location);
|
|
if (Str::eq(NS, MS)) already_here = TRUE;
|
|
DISCARD_TEXT(MS)
|
|
}
|
|
DISCARD_TEXT(NS)
|
|
if (already_here) return;
|
|
ADD_TO_LINKED_LIST(N, inbuild_nest, search_list);
|
|
}
|
|
|
|
@h Search results.
|
|
When we search a list of nests for copies satisfying certain requirements,
|
|
we create one of these for each hit:
|
|
|
|
=
|
|
typedef struct inbuild_search_result {
|
|
struct inbuild_copy *copy; /* what was found */
|
|
struct inbuild_nest *nest; /* from whence it came */
|
|
CLASS_DEFINITION
|
|
} inbuild_search_result;
|
|
|
|
@ These can be created only as entries in a list:
|
|
|
|
=
|
|
void Nests::add_search_result(linked_list *results, inbuild_nest *N, inbuild_copy *C,
|
|
inbuild_requirement *req) {
|
|
inbuild_search_result *R = CREATE(inbuild_search_result);
|
|
R->nest = N;
|
|
R->copy = C;
|
|
C->found_by = req;
|
|
if (req == NULL) internal_error("bad search result");
|
|
ADD_TO_LINKED_LIST(R, inbuild_search_result, results);
|
|
}
|
|
|
|
@ And here is our search engine, such as it is. For each nest, we ask each
|
|
genre's manager to look for copies of that genre:
|
|
|
|
=
|
|
void Nests::search_for(inbuild_requirement *req,
|
|
linked_list *search_list, linked_list *results) {
|
|
inbuild_nest *N;
|
|
LOOP_OVER_LINKED_LIST(N, inbuild_nest, search_list) {
|
|
inbuild_genre *G;
|
|
LOOP_OVER(G, inbuild_genre)
|
|
VOID_METHOD_CALL(G, GENRE_SEARCH_NEST_FOR_MTID, N, req, results);
|
|
}
|
|
}
|
|
|
|
@ Oftentimes, we want only the single best result, and won't even look at the
|
|
others:
|
|
|
|
=
|
|
inbuild_search_result *Nests::search_for_best(inbuild_requirement *req,
|
|
linked_list *search_list) {
|
|
linked_list *L = NEW_LINKED_LIST(inbuild_search_result);
|
|
Nests::search_for(req, search_list, L);
|
|
inbuild_search_result *best = NULL, *search_result;
|
|
LOOP_OVER_LINKED_LIST(search_result, inbuild_search_result, L)
|
|
if (Nests::better_result(search_result, best))
|
|
best = search_result;
|
|
return best;
|
|
}
|
|
|
|
@ Where "better" is defined as follows. This innocent-looking function is
|
|
in fact critical to what Inbuild does. It uses tags on nests to prefer copies
|
|
in the Materials folder to those in the external nest, and to prefer those in
|
|
turn to copies in the internal nest; and within nests of equal importance,
|
|
it chooses the earliest hit among those which have the highest-precedence
|
|
semantic version numbers.
|
|
|
|
=
|
|
int Nests::better_result(inbuild_search_result *R1, inbuild_search_result *R2) {
|
|
/* Something is better than nothing */
|
|
if (R1 == NULL) return FALSE;
|
|
if (R2 == NULL) return TRUE;
|
|
|
|
/* Otherwise, a more important nest beats a less important nest */
|
|
int o1 = Nests::get_tag(R1->nest);
|
|
int o2 = Nests::get_tag(R2->nest);
|
|
if (o1 < o2) return TRUE;
|
|
if (o1 > o2) return FALSE;
|
|
|
|
/* Otherwise, a higher semantic version number beats a lower */
|
|
if (VersionNumbers::gt(R1->copy->edition->version, R2->copy->edition->version))
|
|
return TRUE;
|
|
|
|
/* Otherwise, better the devil we know */
|
|
return FALSE;
|
|
}
|