mirror of
https://github.com/ganelson/inform.git
synced 2024-07-08 01:54:21 +03:00
366 lines
12 KiB
OpenEdge ABL
366 lines
12 KiB
OpenEdge ABL
[Copies::] Copies.
|
|
|
|
A copy is an instance in the file system of a specific edition of a work.
|
|
|
|
@h Creation.
|
|
A "copy" of a work exists in the file system when we've actually got hold of
|
|
some edition of it. For some genres, copies will be files; for others,
|
|
directories holding a set of files.
|
|
|
|
A purist view would be that a copy is simply an edition at a location in the
|
|
file system. And so it is. But copies are the main things Inbuild works on,
|
|
and we will need to generate data about them, some of which is most usefully
|
|
stored here.
|
|
|
|
=
|
|
typedef struct inbuild_copy {
|
|
struct inbuild_edition *edition; /* what is this a copy of? */
|
|
struct pathname *location_if_path; /* exactly one of these must be non-|NULL| */
|
|
struct filename *location_if_file;
|
|
struct inbuild_nest *nest_of_origin; /* note that copies do not always come from nests */
|
|
|
|
general_pointer metadata; /* the type of which depends on the work's genre */
|
|
struct JSON_value *metadata_record; /* where read in from a JSON file */
|
|
struct build_vertex *vertex; /* head vertex of build graph for this copy */
|
|
int graph_constructed;
|
|
int source_text_read; /* have we attempted to read Inform source text from this? */
|
|
struct wording source_text; /* the source text we read, if so */
|
|
struct inbuild_requirement *found_by; /* if this was claimed in a search */
|
|
struct linked_list *errors_reading_source_text; /* of |copy_error| */
|
|
int last_scanned;
|
|
CLASS_DEFINITION
|
|
} inbuild_copy;
|
|
|
|
@ Copies are created by the managers for the respective genres, usually when
|
|
claiming. If you are a manager, do not call this...
|
|
|
|
=
|
|
inbuild_copy *Copies::new_p(inbuild_edition *edition) {
|
|
inbuild_copy *copy = CREATE(inbuild_copy);
|
|
copy->edition = edition;
|
|
copy->location_if_path = NULL;
|
|
copy->location_if_file = NULL;
|
|
copy->nest_of_origin = NULL;
|
|
copy->metadata = NULL_GENERAL_POINTER;
|
|
copy->metadata_record = NULL;
|
|
copy->vertex = Graphs::copy_vertex(copy);
|
|
copy->graph_constructed = FALSE;
|
|
copy->source_text_read = FALSE;
|
|
copy->source_text = EMPTY_WORDING;
|
|
copy->found_by = NULL;
|
|
copy->errors_reading_source_text = NEW_LINKED_LIST(copy_error);
|
|
copy->last_scanned = 0;
|
|
return copy;
|
|
}
|
|
|
|
@ ...call one of these:
|
|
|
|
=
|
|
inbuild_copy *Copies::new_in_file(inbuild_edition *edition, filename *F, inbuild_nest *N) {
|
|
inbuild_copy *copy = Copies::new_p(edition);
|
|
copy->location_if_file = F;
|
|
copy->nest_of_origin = N;
|
|
SVEXPLAIN(3, "(made %S copy C%d at file %f)\n",
|
|
Genres::name(copy->edition->work->genre), copy->allocation_id, F);
|
|
return copy;
|
|
}
|
|
|
|
inbuild_copy *Copies::new_in_path(inbuild_edition *edition, pathname *P, inbuild_nest *N) {
|
|
inbuild_copy *copy = Copies::new_p(edition);
|
|
copy->location_if_path = P;
|
|
copy->nest_of_origin = N;
|
|
SVEXPLAIN(3, "(made %S copy C%d at directory %p)\n",
|
|
Genres::name(copy->edition->work->genre), copy->allocation_id, P);
|
|
return copy;
|
|
}
|
|
|
|
@ And then probably follow up by calling this, to attach a pointer to some
|
|
additional data specific to your genre:
|
|
|
|
=
|
|
void Copies::set_metadata(inbuild_copy *C, general_pointer ref) {
|
|
C->metadata = ref;
|
|
}
|
|
|
|
inbuild_nest *Copies::origin(inbuild_copy *C) {
|
|
if (C == NULL) return NULL;
|
|
return C->nest_of_origin;
|
|
}
|
|
|
|
@h List of errors.
|
|
When copies are found to be malformed, error messages are attached to them
|
|
for later reporting. These are stored in a list.
|
|
|
|
=
|
|
void Copies::attach_error(inbuild_copy *C, copy_error *CE) {
|
|
if (C == NULL) internal_error("no copy to attach to");
|
|
CopyErrors::supply_attached_copy(CE, C);
|
|
ADD_TO_LINKED_LIST(CE, copy_error, C->errors_reading_source_text);
|
|
}
|
|
|
|
void Copies::list_attached_errors(OUTPUT_STREAM, inbuild_copy *C) {
|
|
if (C == NULL) return;
|
|
copy_error *CE;
|
|
int c = 1;
|
|
LOOP_OVER_LINKED_LIST(CE, copy_error, C->errors_reading_source_text) {
|
|
WRITE("%d. ", c++); CopyErrors::write(OUT, CE); WRITE("\n");
|
|
}
|
|
}
|
|
|
|
void Copies::list_attached_errors_to_HTML(OUTPUT_STREAM, inbuild_copy *C) {
|
|
if (C == NULL) return;
|
|
HTML_OPEN("ul"); WRITE("\n");
|
|
copy_error *CE;
|
|
LOOP_OVER_LINKED_LIST(CE, copy_error, C->errors_reading_source_text) {
|
|
HTML_OPEN("li");
|
|
CopyErrors::write(OUT, CE);
|
|
HTML_CLOSE("li"); WRITE("\n");
|
|
}
|
|
HTML_CLOSE("ul"); WRITE("\n");
|
|
}
|
|
|
|
void Copies::list_attached_errors_to_JSON(JSON_value *errors, inbuild_copy *C) {
|
|
if (C == NULL) return;
|
|
copy_error *CE;
|
|
LOOP_OVER_LINKED_LIST(CE, copy_error, C->errors_reading_source_text) {
|
|
TEMPORARY_TEXT(err)
|
|
CopyErrors::write(err, CE);
|
|
JSON_value *err_jv = JSON::new_string(err);
|
|
JSON::add_to_array(errors, err_jv);
|
|
DISCARD_TEXT(err)
|
|
}
|
|
}
|
|
|
|
@h Writing.
|
|
|
|
=
|
|
void Copies::write_copy(OUTPUT_STREAM, inbuild_copy *C) {
|
|
Editions::write(OUT, C->edition);
|
|
}
|
|
|
|
@h Reading source text.
|
|
|
|
=
|
|
int Copies::source_text_has_been_read(inbuild_copy *C) {
|
|
if (C == NULL) internal_error("no copy");
|
|
return C->source_text_read;
|
|
}
|
|
|
|
wording Copies::get_source_text(inbuild_copy *C, text_stream *reason) {
|
|
if (C->source_text_read == FALSE) {
|
|
C->source_text_read = TRUE;
|
|
if (LinkedLists::len(C->errors_reading_source_text) > 0) {
|
|
C->source_text = EMPTY_WORDING;
|
|
} else {
|
|
feed_t id = Feeds::begin();
|
|
VOID_METHOD_CALL(C->edition->work->genre, GENRE_READ_SOURCE_TEXT_FOR_MTID, C);
|
|
wording W = Feeds::end(id);
|
|
if (Wordings::nonempty(W)) C->source_text = W;
|
|
}
|
|
}
|
|
return C->source_text;
|
|
}
|
|
|
|
@h Going operational.
|
|
|
|
=
|
|
void Copies::construct_graph(inbuild_copy *C) {
|
|
if (C->graph_constructed == FALSE) {
|
|
C->graph_constructed = TRUE;
|
|
VOID_METHOD_CALL(C->edition->work->genre, GENRE_CONSTRUCT_GRAPH_MTID, C);
|
|
}
|
|
}
|
|
|
|
@ Some copies, such as projects, are not fully graphed by //Copies::construct_graph//
|
|
because this would be too slow when inbuild is scanning a directory; a project
|
|
is only graphed when we are interested in building or analysing it.
|
|
|
|
This process of full graphing can cause new copies to come into existence (for
|
|
example, for kits which the project depends on), and we need to ensure that any
|
|
such newcomers are graphed too.
|
|
|
|
=
|
|
build_vertex *Copies::construct_project_graph(inbuild_copy *C) {
|
|
build_vertex *V = Copies::building_soon(C);
|
|
Copies::graph_everything();
|
|
return V;
|
|
}
|
|
|
|
void Copies::graph_everything(void) {
|
|
inbuild_copy *C;
|
|
LOOP_OVER(C, inbuild_copy) Copies::construct_graph(C);
|
|
}
|
|
|
|
build_vertex *Copies::building_soon(inbuild_copy *C) {
|
|
build_vertex *V = C->vertex;
|
|
VOID_METHOD_CALL(C->edition->work->genre, GENRE_BUILDING_SOON_MTID, C, &V);
|
|
return V;
|
|
}
|
|
|
|
@h Sorting.
|
|
The command-line //inbuild// uses this when sorting search results.
|
|
|
|
=
|
|
int Copies::cmp(const void *v1, const void *v2) {
|
|
const inbuild_copy **C1 = (const inbuild_copy **) v1;
|
|
const inbuild_copy **C2 = (const inbuild_copy **) v2;
|
|
if ((*C1 == NULL) || (*C2 == NULL)) internal_error("sort on null search results");
|
|
if (*C1 == *C2) return 0;
|
|
int r = Editions::cmp((*C1)->edition, (*C2)->edition);
|
|
if (r == 0) {
|
|
TEMPORARY_TEXT(L1)
|
|
TEMPORARY_TEXT(L2)
|
|
WRITE_TO(L1, "%f ___ %p", (*C1)->location_if_file, (*C1)->location_if_path);
|
|
WRITE_TO(L2, "%f ___ %p", (*C2)->location_if_file, (*C2)->location_if_path);
|
|
r = Str::cmp(L1, L2);
|
|
DISCARD_TEXT(L1)
|
|
DISCARD_TEXT(L2)
|
|
}
|
|
return r;
|
|
}
|
|
|
|
@h Miscellaneous Inbuild commands.
|
|
This function implements the command-line instruction to |-inspect|.
|
|
|
|
=
|
|
void Copies::inspect(OUTPUT_STREAM, JSON_value *obj, inbuild_copy *C) {
|
|
if (obj) {
|
|
JSON_value *results = JSON::look_up_object(obj, I"inspection");
|
|
if (results == NULL) {
|
|
results = JSON::new_array();
|
|
JSON::add_to_object(obj, I"inspection", results);
|
|
}
|
|
JSON_value *result = JSON::new_object();
|
|
JSON_value *resource = JSON::new_object();
|
|
JSON::add_to_object(resource, I"type",
|
|
JSON::new_string(Genres::metadata_type_name(C->edition->work->genre)));
|
|
JSON::add_to_object(resource, I"title",
|
|
JSON::new_string(C->edition->work->title));
|
|
if (Str::len(C->edition->work->author_name) > 0)
|
|
JSON::add_to_object(resource, I"author",
|
|
JSON::new_string(C->edition->work->author_name));
|
|
semantic_version_number V = C->edition->version;
|
|
if (VersionNumbers::is_null(V) == FALSE) {
|
|
TEMPORARY_TEXT(version)
|
|
WRITE_TO(version, "%v", &V);
|
|
JSON::add_to_object(resource, I"version", JSON::new_string(version));
|
|
DISCARD_TEXT(version)
|
|
}
|
|
JSON::add_to_object(result, I"resource", resource);
|
|
int N = LinkedLists::len(C->errors_reading_source_text);
|
|
if (N > 0) {
|
|
JSON_value *errors = JSON::new_array();
|
|
Copies::list_attached_errors_to_JSON(errors, C);
|
|
JSON::add_to_object(result, I"errors", errors);
|
|
}
|
|
TEMPORARY_TEXT(location)
|
|
if (C->location_if_path) {
|
|
WRITE_TO(location, "%p", C->location_if_path);
|
|
JSON::add_to_object(result, I"location-directory", JSON::new_string(location));
|
|
}
|
|
if (C->location_if_file) {
|
|
WRITE_TO(location, "%f", C->location_if_file);
|
|
JSON::add_to_object(result, I"location-file", JSON::new_string(location));
|
|
}
|
|
DISCARD_TEXT(location)
|
|
JSON::add_to_array(results, result);
|
|
} else {
|
|
WRITE("%S: ", Genres::name(C->edition->work->genre));
|
|
Editions::inspect(OUT, C->edition);
|
|
if (C->location_if_path) WRITE(" = directory %p", C->location_if_path);
|
|
if (C->location_if_file) WRITE(" = file %f", C->location_if_file);
|
|
int N = LinkedLists::len(C->errors_reading_source_text);
|
|
if (N > 0) {
|
|
WRITE(" - %d error", N);
|
|
if (N > 1) WRITE("s");
|
|
}
|
|
WRITE("\n");
|
|
if (N > 0) {
|
|
INDENT; Copies::list_attached_errors(OUT, C); OUTDENT;
|
|
}
|
|
}
|
|
}
|
|
|
|
@ And here are |-build| and |-rebuild|, though note that |Copies::build|
|
|
is also called by the |core| module of the Inform 7 compiler to perform
|
|
its main task: building an Inform project.
|
|
|
|
=
|
|
void Copies::build(OUTPUT_STREAM, inbuild_copy *C, build_methodology *BM) {
|
|
build_vertex *V = Copies::construct_project_graph(C);
|
|
IncrementalBuild::build(OUT, V, BM);
|
|
}
|
|
void Copies::rebuild(OUTPUT_STREAM, inbuild_copy *C, build_methodology *BM) {
|
|
build_vertex *V = Copies::construct_project_graph(C);
|
|
IncrementalBuild::rebuild(OUT, V, BM);
|
|
}
|
|
|
|
@ Now in quick succession |-graph|, |-build-needs|, |-use-needs|, |-build-missing|,
|
|
|-use-missing|:
|
|
|
|
=
|
|
void Copies::show_graph(OUTPUT_STREAM, inbuild_copy *C) {
|
|
build_vertex *V = Copies::construct_project_graph(C);
|
|
Graphs::describe(OUT, V, TRUE);
|
|
}
|
|
void Copies::show_needs(OUTPUT_STREAM, inbuild_copy *C, int uses_only, int paths) {
|
|
Copies::construct_project_graph(C);
|
|
Graphs::show_needs(OUT, C->vertex, uses_only, paths);
|
|
}
|
|
void Copies::show_missing(OUTPUT_STREAM, inbuild_copy *C, int uses_only) {
|
|
Copies::construct_project_graph(C);
|
|
int N = Graphs::show_missing(OUT, C->vertex, uses_only);
|
|
if (N == 0) WRITE("Nothing is missing\n");
|
|
}
|
|
|
|
@ And here is |-archive| and |-archive-to N|:
|
|
|
|
=
|
|
void Copies::archive(OUTPUT_STREAM, inbuild_copy *C, inbuild_nest *N, build_methodology *BM) {
|
|
Copies::construct_project_graph(C);
|
|
int NM = Graphs::show_missing(OUT, C->vertex, FALSE);
|
|
if (NM > 0) WRITE("Because there are missing resources, -archive is cancelled\n");
|
|
else if (N) Graphs::archive(OUT, C->vertex, N, BM);
|
|
}
|
|
|
|
@ Now lastly |-copy-to N| and |-sync-to N|:
|
|
|
|
=
|
|
int Copies::copy_to(inbuild_copy *C, inbuild_nest *destination_nest, int syncing,
|
|
build_methodology *meth) {
|
|
int rv = 0;
|
|
if (destination_nest) {
|
|
INT_METHOD_CALL(rv, C->edition->work->genre, GENRE_COPY_TO_NEST_MTID,
|
|
C, destination_nest, syncing, meth);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
void Copies::overwrite_error(inbuild_copy *C, inbuild_nest *N) {
|
|
text_stream *ext = Str::new();
|
|
WRITE_TO(ext, "%X", C->edition->work);
|
|
Errors::with_text("already present (to overwrite, use -sync-to not -copy-to): '%S'", ext);
|
|
}
|
|
|
|
@ And |-document|:
|
|
|
|
=
|
|
void Copies::document(inbuild_copy *C, pathname *dest, filename *sitemap) {
|
|
VOID_METHOD_CALL(C->edition->work->genre, GENRE_DOCUMENT_MTID, C, dest, sitemap);
|
|
}
|
|
|
|
@ And |-modernise|:
|
|
|
|
=
|
|
void Copies::modernise(OUTPUT_STREAM, inbuild_copy *C) {
|
|
TEMPORARY_TEXT(work_done)
|
|
WRITE_TO(work_done, "%X: ", C->edition->work);
|
|
int rv = FALSE;
|
|
INT_METHOD_CALL(rv, C->edition->work->genre, GENRE_MODERNISE_MTID, C, work_done);
|
|
if (rv == FALSE) {
|
|
WRITE("already up to date\n", C->edition->work);
|
|
}
|
|
WRITE("%S", work_done);
|
|
DISCARD_TEXT(work_done)
|
|
}
|