To register the names associated with external files, and build the small I6 arrays associated with each.
§1. The test group :files exercises the features in this feature.
The following is called to activate the feature:
void ExternalFiles::start(void) { PluginCalls::plug(PRODUCTION_LINE_PLUG, ExternalFiles::production_line); PluginCalls::plug(MAKE_SPECIAL_MEANINGS_PLUG, ExternalFiles::make_special_meanings); PluginCalls::plug(NEW_BASE_KIND_NOTIFY_PLUG, ExternalFiles::files_new_base_kind_notify); PluginCalls::plug(NEW_INSTANCE_NOTIFY_PLUG, ExternalFiles::files_new_named_instance_notify); } int ExternalFiles::production_line(int stage, int debugging, stopwatch_timer *sequence_timer) { if (stage == INTER1_CSEQ) { BENCH(RTMultimedia::compile_files); } return FALSE; }
§2. One special meaning. We add one special meaning for assertions, to catch sentences with the shape:
The File of Wisdom (owned by another project) is called "wisdom".
int ExternalFiles::make_special_meanings(void) { SpecialMeanings::declare(ExternalFiles::new_file_SMF, I"new-file", 2); return FALSE; } int ExternalFiles::new_file_SMF(int task, parse_node *V, wording *NPs) { wording SW = (NPs)?(NPs[0]):EMPTY_WORDING; wording OW = (NPs)?(NPs[1]):EMPTY_WORDING; switch (task) { "File... is the file..." case ACCEPT_SMFT: if ((<nounphrase-external-file>(SW)) && (<new-file-sentence-object>(OW))) { parse_node *O = <<rp>>; <np-unparsed>(SW); V->next = <<rp>>; V->next->next = O; return TRUE; } break; case PASS_1_SMFT: ExternalFiles::register_file(Node::get_text(V->next), Node::get_text(V->next->next)); break; } return FALSE; }
§3. And this is the Preform grammar needed for the subject phrase:
<external-file-sentence-subject> ::= <definite-article> <external-file-sentence-subject> | ==> { pass 2 } internal data/binary <external-file-name> | ==> { INTERNAL_BINARY_FILE_NFSMF, -, <<ownership>> = R[1] } internal text <external-file-name> | ==> { INTERNAL_TEXT_FILE_NFSMF, -, <<ownership>> = R[1] } text <external-file-name> | ==> { EXTERNAL_TEXT_FILE_NFSMF, -, <<ownership>> = R[1] } binary <external-file-name> | ==> { EXTERNAL_BINARY_FILE_NFSMF, -, <<ownership>> = R[1] } <external-file-name> ==> { EXTERNAL_TEXT_FILE_NFSMF, -, <<ownership>> = R[1] } <external-file-name> ::= {file ...} ( owned by <external-file-owner> ) | ==> { pass 1 } {file ...} ==> { NOT_APPLICABLE, - } <external-file-owner> ::= another project | ==> { FALSE, - } project {<quoted-text-without-subs>} | ==> { TRUE, - } ... ==> Issue PM_BadFileOwner problem3.1
- This is Preform grammar, not regular C code.
§3.1. Issue PM_BadFileOwner problem3.1 =
StandardProblems::sentence_problem(Task::syntax_tree(), _p_(PM_BadFileOwner), "the owner of this file is wrongly specified", "since it is not one of the three possibilities - " "(1) Specify nothing: making the file belong to this " "project. (2) Specify only that it belongs to someone, " "without saying whom: 'The File of Wisdom (owned by " "another project) is called \"wisdom\".' (3) Specify " "that it belongs to a project with a given double-quoted " "IFID: 'The File of Wisdom (owned by project " "\"4122DDA8-A153-46BC-8F57-42220F9D8795\") " "is called \"wisdom\".'"); ==> { NOT_APPLICABLE, - };
- This code is used in §3.
§4. The object phrase is simply quoted text. Although the Preform grammar doesn't go into this level of detail, it's actually required to have 3 to 23 English letters or digits, with the first being a letter.
<external-file-sentence-object> ::= <quoted-text> | ==> { pass 1 } ... ==> Issue PM_FilenameNotTextual problem4.1 <new-file-sentence-object> ::= <indefinite-article> <new-file-sentence-object-unarticled> | ==> { pass 2 } <new-file-sentence-object-unarticled> ==> { pass 1 } <new-file-sentence-object-unarticled> ::= called <np-unparsed> ==> { TRUE, RP[1] } <nounphrase-external-file> ::= <external-file-sentence-subject> ==> { 0, Diagrams::new_UNPARSED_NOUN(W) }
- This is Preform grammar, not regular C code.
§4.1. Issue PM_FilenameNotTextual problem4.1 =
StandardProblems::sentence_problem(Task::syntax_tree(), _p_(PM_FilenameNotTextual), "a file can only be called with a single quoted piece of text", "as in: 'The File of Wisdom is called \"wisdom\".'"); ==> { -1, - };
- This code is used in §4.
§5. In assertion pass 1, then, the following is called on any sentence which has been found to create a file:
void ExternalFiles::register_file(wording W, wording FN) { <external-file-sentence-object>(FN); FN = Wordings::from(FN, <<r>>); if (Wordings::empty(FN)) return; inchar32_t *p = Lexer::word_text(Wordings::first_wn(FN)); if (<external-file-sentence-subject>(W) == FALSE) internal_error("bad ef grammar"); wording NW = GET_RW(<external-file-name>, 1); int format = <<r>>; Vet the filename5.1; int binary = FALSE; int ownership = OWNED_BY_THIS_PROJECT; switch (format) { case EXTERNAL_TEXT_FILE_NFSMF: case EXTERNAL_BINARY_FILE_NFSMF: { if (format == EXTERNAL_BINARY_FILE_NFSMF) binary = TRUE; TEMPORARY_TEXT(ifid_of_file) Determine the ownership5.2; ExternalFiles::files_create(W, binary, ownership, ifid_of_file, FN); LOGIF(MULTIMEDIA_CREATIONS, "Created external file <%W> = filename '%N'\n", W, FN); DISCARD_TEXT(ifid_of_file) break; } case INTERNAL_TEXT_FILE_NFSMF: case INTERNAL_BINARY_FILE_NFSMF: InternalFiles::files_create(<<r>>, NW, FN); LOGIF(MULTIMEDIA_CREATIONS, "Created internal file <%W> = filename '%N'\n", NW, FN); break; } }
§5.1. The restrictions here are really very conservative.
Vet the filename5.1 =
int bad_filename = FALSE; if (Wide::len(p) < 5) bad_filename = TRUE; if (Characters::isalpha(p[1]) == FALSE) bad_filename = TRUE; for (int i=0; p[i]; i++) { if (p[i] == '"') { if ((i==0) || (p[i+1] == 0)) continue; } if (i>24) bad_filename = TRUE; if ((Characters::isalpha(p[i])) || (Characters::isdigit(p[i]))) continue; if ((format == INTERNAL_TEXT_FILE_NFSMF) || (format == INTERNAL_BINARY_FILE_NFSMF)) if ((p[i] == '.') || (p[i] == '_') || (p[i] == ' ')) continue; LOG("Objected to character %c\n", p[i]); bad_filename = TRUE; } if (bad_filename) { LOG("Filename: %s\n", p); StandardProblems::sentence_problem(Task::syntax_tree(), _p_(PM_FilenameUnsafe), "filenames must be very conservatively chosen", "in order to be viable on a wide range of computers. They must " "consist of 3 to 23 English letters or digits, with the first being " "a letter. Spaces are not allowed, and nor are periods. (A file " "extension, such as '.glkdata', may be added on some platforms " "automatically: this is invisible to Inform.)"); return; }
- This code is used in §5.
§5.2. Each file can be text or binary, has a name, and can be owned by this project, by an unspecified other project, or by a project identified by its IFID.
define OWNED_BY_THIS_PROJECT 1 define OWNED_BY_ANOTHER_PROJECT 2 define OWNED_BY_SPECIFIC_PROJECT 3
Determine the ownership5.2 =
W = GET_RW(<external-file-name>, 1); Make sure W can be the name of a new file anyway5.2.1; if (<<ownership>> == TRUE) { wording OW = GET_RW(<external-file-owner>, 1); int j, invalid = FALSE; p = Lexer::word_text(Wordings::last_wn(OW)); for (j=1; (j<47) && (p[j]); j++) { if ((p[j] == '"') && (p[j+1] == 0)) break; PUT_TO(ifid_of_file, p[j]); if ((Characters::isalpha(p[j])) || (Characters::isdigit(p[j]))) continue; if (p[j] == '-') continue; invalid = TRUE; LOG("Objected to character %c\n", p[j]); } if ((invalid) || (j==47)) StandardProblems::sentence_problem(Task::syntax_tree(), _p_(PM_BadFileIFID), "the owner of the file should be specified " "using a valid double-quoted IFID", "as in: 'The File of Wisdom (owned by project " "\"4122DDA8-A153-46BC-8F57-42220F9D8795\") " "is called \"wisdom\".'"); else ownership = OWNED_BY_SPECIFIC_PROJECT; } if (<<ownership>> == FALSE) ownership = OWNED_BY_ANOTHER_PROJECT;
- This code is used in §5.
§5.2.1. Make sure W can be the name of a new file anyway5.2.1 =
Assertions::Creator::vet_name_for_noun(W); if ((<s-value>(W)) && (Rvalues::is_CONSTANT_of_kind(<<rp>>, K_external_file))) { StandardProblems::sentence_problem(Task::syntax_tree(), _p_(PM_FilenameDuplicate), "this is already the name of a file", "so there must be some duplication somewhere."); return; }
- This code is used in §5.2.
kind *K_external_file = NULL;
int ExternalFiles::files_new_base_kind_notify(kind *new_base, text_stream *name, wording W) { if (Str::eq_wide_string(name, U"EXTERNAL_FILE_TY")) { K_external_file = new_base; return TRUE; } return FALSE; }
§8. Significant new instances. This structure of additional data is attached to each figure instance.
typedef struct files_data { struct wording name; text of name int unextended_filename; word number of text like "bones" struct text_stream *exf_identifier; an Inter identifier int file_is_binary; true or false int file_ownership; one of the OWNED_BY_* values above struct text_stream *IFID_of_owner; if we know that struct instance *as_instance; struct parse_node *where_created; CLASS_DEFINITION } files_data;
- The structure files_data is accessed in 2/fgr, 2/se, 2/if and here.
§9. We allow instances of "external file" to be created only through the above code calling Figures::figures_create. If any other proposition somehow manages to make a figure, a problem message is thrown.
int allow_exf_creations = FALSE; instance *ExternalFiles::files_create(wording W, int binary, int ownership, text_stream *ifid_of_file, wording FN) { allow_exf_creations = TRUE; Assert::true(Propositions::Abstract::to_create_something(K_external_file, W), CERTAIN_CE); allow_exf_creations = FALSE; instance *I = Instances::latest(); files_data *fd = FEATURE_DATA_ON_INSTANCE(files, I); fd->name = W; fd->unextended_filename = Wordings::first_wn(FN); fd->file_is_binary = binary; fd->file_ownership = ownership; fd->IFID_of_owner = Str::duplicate(ifid_of_file); fd->where_created = current_sentence; fd->as_instance = I; return I; } int ExternalFiles::files_new_named_instance_notify(instance *I) { if (K_external_file == NULL) return FALSE; kind *K = Instances::to_kind(I); if (Kinds::eq(K, K_external_file)) { if (allow_exf_creations == FALSE) StandardProblems::sentence_problem(Task::syntax_tree(), _p_(PM_BackdoorFileCreation), "this is not the way to create a new external file", "which should be done with a special 'The File ... is called ...' " "sentence."); ATTACH_FEATURE_DATA_TO_SUBJECT(files, I->as_subject, CREATE(files_data)); return TRUE; } return FALSE; }