1
0
Fork 0
mirror of https://github.com/ganelson/inform.git synced 2024-07-05 16:44:21 +03:00
inform7/inbuild/inbuild-module/Chapter 4/Extension Manager.w

210 lines
7.1 KiB
OpenEdge ABL
Raw Normal View History

2020-02-08 12:34:58 +02:00
[ExtensionManager::] Extension Manager.
An Inform 7 extension.
@h Genre definition.
= (early code)
inbuild_genre *extension_genre = NULL;
@ An extension has a title and an author name, each of which is limited in
length to one character less than the following constants:
@d MAX_EXTENSION_TITLE_LENGTH 51
@d MAX_EXTENSION_AUTHOR_LENGTH 51
@ =
void ExtensionManager::start(void) {
2020-03-10 22:21:55 +02:00
extension_genre = Genres::new(I"extension", TRUE);
2020-02-08 12:34:58 +02:00
METHOD_ADD(extension_genre, GENRE_WRITE_WORK_MTID, ExtensionManager::write_work);
METHOD_ADD(extension_genre, GENRE_CLAIM_AS_COPY_MTID, ExtensionManager::claim_as_copy);
2020-02-19 22:48:30 +02:00
METHOD_ADD(extension_genre, GENRE_SCAN_COPY_MTID, Extensions::scan);
2020-02-08 12:34:58 +02:00
METHOD_ADD(extension_genre, GENRE_SEARCH_NEST_FOR_MTID, ExtensionManager::search_nest_for);
METHOD_ADD(extension_genre, GENRE_COPY_TO_NEST_MTID, ExtensionManager::copy_to_nest);
METHOD_ADD(extension_genre, GENRE_READ_SOURCE_TEXT_FOR_MTID, ExtensionManager::read_source_text_for);
2020-02-08 12:34:58 +02:00
}
void ExtensionManager::write_work(inbuild_genre *gen, OUTPUT_STREAM, inbuild_work *work) {
WRITE("%X", work);
}
@ Extensions live in their namesake subdirectory of a nest:
=
pathname *ExtensionManager::path_within_nest(inbuild_nest *N) {
if (N == NULL) internal_error("no nest");
return Pathnames::subfolder(N->location, I"Extensions");
}
@ Extension copies are annotated with a structure called an |inform_extension|,
which stores data about extensions used by the Inform compiler.
=
inform_extension *ExtensionManager::from_copy(inbuild_copy *C) {
if ((C) && (C->edition->work->genre == extension_genre)) {
return RETRIEVE_POINTER_inform_extension(C->content);
}
return NULL;
}
dictionary *ext_copy_cache = NULL;
2020-02-19 22:48:30 +02:00
inbuild_copy *ExtensionManager::new_copy(filename *F) {
if (ext_copy_cache == NULL) ext_copy_cache = Dictionaries::new(16, FALSE);
TEMPORARY_TEXT(key);
WRITE_TO(key, "%f", F);
inbuild_copy *C = NULL;
if (Dictionaries::find(ext_copy_cache, key))
C = Dictionaries::read_value(ext_copy_cache, key);
if (C == NULL) {
2020-02-19 22:48:30 +02:00
C = Copies::new_in_file(
Editions::new(Works::new(extension_genre, I"Untitled", I"Anonymous"),
VersionNumbers::null()), F, NULL_GENERAL_POINTER);
Copies::scan(C);
if (Works::is_standard_rules(C->edition->work))
Extensions::make_standard(ExtensionManager::from_copy(C));
Dictionaries::create(ext_copy_cache, key);
Dictionaries::write_value(ext_copy_cache, key, C);
}
DISCARD_TEXT(key);
2020-02-08 12:34:58 +02:00
return C;
}
@h Claiming.
Here |arg| is a textual form of a filename or pathname, such as may have been
supplied at the command line; |ext| is a substring of it, and is its extension
(e.g., |jpg| if |arg| is |Geraniums.jpg|), or is empty if there isn't one;
|directory_status| is true if we know for some reason that this is a directory
not a file, false if we know the reverse, and otherwise not applicable.
An extension, for us, needs to be a file with extension |i7x|, but it needs
also to scan properly -- which means the top line of the file has to be right.
So we'll open it and look.
=
void ExtensionManager::claim_as_copy(inbuild_genre *gen, inbuild_copy **C,
text_stream *arg, text_stream *ext, int directory_status) {
if (directory_status == TRUE) return;
if (Str::eq_insensitive(ext, I"i7x")) {
filename *F = Filenames::from_text(arg);
2020-02-19 22:48:30 +02:00
*C = ExtensionManager::claim_file_as_copy(F);
2020-02-08 12:34:58 +02:00
}
}
2020-02-19 22:48:30 +02:00
inbuild_copy *ExtensionManager::claim_file_as_copy(filename *F) {
if (TextFiles::exists(F) == FALSE) return NULL;
inbuild_copy *C = ExtensionManager::new_copy(F);
ExtensionManager::build_vertex(C);
Works::add_to_database(C->edition->work, CLAIMED_WDBC);
2020-02-08 12:34:58 +02:00
return C;
}
@h Searching.
Here we look through a nest to find all extensions matching the supplied
requirements.
For efficiency's sake, since the nest could contain many hundreds of
extensions, we narrow down to the author's subfolder if a specific
2020-03-10 02:08:35 +02:00
author is required.
2020-02-08 12:34:58 +02:00
Nobody should any longer be storing extension files without the file
extension |.i7x|, but this was allowed in the early days of Inform 7,
so we'll quietly allow for it.
=
void ExtensionManager::search_nest_for(inbuild_genre *gen, inbuild_nest *N,
inbuild_requirement *req, linked_list *search_results) {
if ((req->work->genre) && (req->work->genre != extension_genre)) return;
2020-02-08 12:34:58 +02:00
pathname *P = ExtensionManager::path_within_nest(N);
if (Str::len(req->work->author_name) > 0) {
pathname *Q = Pathnames::subfolder(P, req->work->author_name);
2020-03-10 02:08:35 +02:00
ExtensionManager::search_nest_for_r(Q, N, req, search_results);
2020-02-08 12:34:58 +02:00
} else {
ExtensionManager::search_nest_for_r(P, N, req, search_results);
}
}
void ExtensionManager::search_nest_for_r(pathname *P, inbuild_nest *N,
inbuild_requirement *req, linked_list *search_results) {
scan_directory *D = Directories::open(P);
if (D) {
TEMPORARY_TEXT(LEAFNAME);
while (Directories::next(D, LEAFNAME)) {
if (Str::get_last_char(LEAFNAME) == FOLDER_SEPARATOR) {
Str::delete_last_character(LEAFNAME);
2020-02-18 21:57:31 +02:00
if (Str::ne(LEAFNAME, I"Reserved")) {
pathname *Q = Pathnames::subfolder(P, LEAFNAME);
ExtensionManager::search_nest_for_r(Q, N, req, search_results);
}
2020-02-08 12:34:58 +02:00
} else {
filename *F = Filenames::in_folder(P, LEAFNAME);
ExtensionManager::search_nest_for_single_file(F, N, req, search_results);
}
}
DISCARD_TEXT(LEAFNAME);
Directories::close(D);
}
}
void ExtensionManager::search_nest_for_single_file(filename *F, inbuild_nest *N,
inbuild_requirement *req, linked_list *search_results) {
2020-02-19 22:48:30 +02:00
inbuild_copy *C = ExtensionManager::claim_file_as_copy(F);
2020-02-08 12:34:58 +02:00
if ((C) && (Requirements::meets(C->edition, req))) {
Nests::add_search_result(search_results, N, C, req);
2020-02-08 12:34:58 +02:00
}
}
@h Copying.
Now the task is to copy an extension into place in a nest. This is easy,
since an extension is a single file; to sync, we just overwrite.
=
void ExtensionManager::copy_to_nest(inbuild_genre *gen, inbuild_copy *C, inbuild_nest *N,
int syncing, build_methodology *meth) {
pathname *E = ExtensionManager::path_within_nest(N);
TEMPORARY_TEXT(leaf);
2020-03-10 02:08:35 +02:00
Editions::write_canonical_leaf(leaf, C->edition);
WRITE_TO(leaf, ".i7x");
2020-02-08 12:34:58 +02:00
filename *F = Filenames::in_folder(
Pathnames::subfolder(E, C->edition->work->author_name), leaf);
DISCARD_TEXT(leaf);
if (TextFiles::exists(F)) {
if (syncing == FALSE) { Nests::overwrite_error(N, C); return; }
} else {
if (meth->methodology == DRY_RUN_METHODOLOGY) {
TEMPORARY_TEXT(command);
WRITE_TO(command, "mkdir -p ");
Shell::quote_path(command, Filenames::get_path_to(F));
WRITE_TO(STDOUT, "%S\n", command);
DISCARD_TEXT(command);
} else {
Pathnames::create_in_file_system(N->location);
Pathnames::create_in_file_system(Pathnames::subfolder(N->location, I"Extensions"));
Pathnames::create_in_file_system(Filenames::get_path_to(F));
}
}
TEMPORARY_TEXT(command);
WRITE_TO(command, "cp -f ");
Shell::quote_file(command, C->location_if_file);
Shell::quote_file(command, F);
BuildSteps::shell(command, meth);
DISCARD_TEXT(command);
}
@h Build graph.
The build graph for an extension is just a single node: you don't need to
build an extension at all.
=
void ExtensionManager::build_vertex(inbuild_copy *C) {
2020-02-08 12:34:58 +02:00
Graphs::copy_vertex(C);
}
@h Source text.
=
2020-02-17 02:16:38 +02:00
void ExtensionManager::read_source_text_for(inbuild_genre *G, inbuild_copy *C) {
Extensions::read_source_text_for(ExtensionManager::from_copy(C));
}