1
0
Fork 0
mirror of https://github.com/ganelson/inform.git synced 2024-07-08 10:04:21 +03:00
inform7/inbuild/inbuild-module/Chapter 3/Extension Manager.w
2020-02-19 20:48:30 +00:00

232 lines
8 KiB
OpenEdge ABL

[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) {
extension_genre = Genres::new(I"extension");
METHOD_ADD(extension_genre, GENRE_WRITE_WORK_MTID, ExtensionManager::write_work);
METHOD_ADD(extension_genre, GENRE_CLAIM_AS_COPY_MTID, ExtensionManager::claim_as_copy);
METHOD_ADD(extension_genre, GENRE_SCAN_COPY_MTID, Extensions::scan);
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);
}
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;
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) {
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);
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);
*C = ExtensionManager::claim_file_as_copy(F);
}
}
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);
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
author is required, and to the specific extension file if title is
also known. (In particular, this happens when the Inform compiler is
using us to search for, say, Locksmith by Emily Short.)
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;
pathname *P = ExtensionManager::path_within_nest(N);
if (Str::len(req->work->author_name) > 0) {
pathname *Q = Pathnames::subfolder(P, req->work->author_name);
if (Str::len(req->work->title) > 0) {
for (int i7x_flag = 1; i7x_flag >= 0; i7x_flag--) {
TEMPORARY_TEXT(leaf);
if (i7x_flag) WRITE_TO(leaf, "%S.i7x", req->work->title);
else WRITE_TO(leaf, "%S", req->work->title);
filename *F = Filenames::in_folder(Q, leaf);
ExtensionManager::search_nest_for_single_file(F, N, req, search_results);
DISCARD_TEXT(leaf);
}
} else {
ExtensionManager::search_nest_for_r(Q, N, req, search_results);
}
} 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);
if (Str::ne(LEAFNAME, I"Reserved")) {
pathname *Q = Pathnames::subfolder(P, LEAFNAME);
ExtensionManager::search_nest_for_r(Q, N, req, search_results);
}
} 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) {
inbuild_copy *C = ExtensionManager::claim_file_as_copy(F);
if ((C) && (Requirements::meets(C->edition, req))) {
Nests::add_search_result(search_results, N, C, req);
}
}
@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.
=
filename *ExtensionManager::filename_in_nest(inbuild_nest *N,
text_stream *title, text_stream *author) {
pathname *E = ExtensionManager::path_within_nest(N);
TEMPORARY_TEXT(leaf);
WRITE_TO(leaf, "%S.i7x", title);
filename *F = Filenames::in_folder(Pathnames::subfolder(E, author), leaf);
DISCARD_TEXT(leaf);
return F;
}
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);
WRITE_TO(leaf, "%S.i7x", C->edition->work->title);
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) {
Graphs::copy_vertex(C);
}
@h Source text.
=
void ExtensionManager::read_source_text_for(inbuild_genre *G, inbuild_copy *C) {
Extensions::read_source_text_for(ExtensionManager::from_copy(C));
}