To manage key-value pairs of bibliographic data, metadata if you like, associated with a given web.


§1. Storing data. There are never more than a dozen or so key-value pairs, and it's more convenient to store them directly here than to use a dictionary.

typedef struct web_bibliographic_datum {
    struct text_stream *key;
    struct text_stream *value;
    int declaration_permitted;  is the contents page of the web allowed to set this?
    int declaration_mandatory;  is it positively required to?
    int on_or_off;  boolean: which we handle as the string "On" or "Off"
    struct web_bibliographic_datum *alias;
    CLASS_DEFINITION
} web_bibliographic_datum;

§2. We keep these in linked lists, and here's a convenient way to scan them:

define LOOP_OVER_BIBLIOGRAPHIC_DATA(bd, Wm)
    LOOP_OVER_LINKED_LIST(bd, web_bibliographic_datum, Wm->bibliographic_data)

§3. The following check the rules:

int Bibliographic::datum_can_be_declared(web_md *Wm, text_stream *key) {
    web_bibliographic_datum *bd = Bibliographic::look_up_datum(Wm, key);
    if (bd == NULL) return FALSE;
    return bd->declaration_permitted;
}

int Bibliographic::datum_on_or_off(web_md *Wm, text_stream *key) {
    web_bibliographic_datum *bd = Bibliographic::look_up_datum(Wm, key);
    if (bd == NULL) return FALSE;
    return bd->on_or_off;
}

§4. Initialising a web. Each web has the following slate of data:

void Bibliographic::initialise_data(web_md *Wm) {
    web_bibliographic_datum *bd;

    bd = Bibliographic::set_datum(Wm, I"Title", NULL); bd->declaration_mandatory = TRUE;
    bd = Bibliographic::set_datum(Wm, I"Author", NULL); bd->declaration_mandatory = TRUE;
    bd = Bibliographic::set_datum(Wm, I"Language", I"None");
    bd = Bibliographic::set_datum(Wm, I"Purpose", I"");

    bd = Bibliographic::set_datum(Wm, I"License", NULL);
    bd->alias = Bibliographic::set_datum(Wm, I"Licence", NULL);  alias US to UK spelling

    Bibliographic::set_datum(Wm, I"Short Title", NULL);
    Bibliographic::set_datum(Wm, I"Capitalized Title", NULL);
    Bibliographic::set_datum(Wm, I"Build Date", NULL);
    Bibliographic::set_datum(Wm, I"Build Number", NULL);
    Bibliographic::set_datum(Wm, I"Prerelease", NULL);
    Bibliographic::set_datum(Wm, I"Semantic Version Number", NULL);
    Bibliographic::set_datum(Wm, I"Version Number", I"1");
    Bibliographic::set_datum(Wm, I"Version Name", NULL);
    Bibliographic::set_datum(Wm, I"Index Template", NULL);
    Bibliographic::set_datum(Wm, I"Preform Language", NULL);

    bd = Bibliographic::set_datum(Wm, I"Declare Section Usage", I"Off"); bd->on_or_off = TRUE;
    bd = Bibliographic::set_datum(Wm, I"Namespaces", I"Off"); bd->on_or_off = TRUE;
    bd = Bibliographic::set_datum(Wm, I"Sequential Section Ranges", I"Off"); bd->on_or_off = TRUE;
    bd = Bibliographic::set_datum(Wm, I"Strict Usage Rules", I"Off"); bd->on_or_off = TRUE;
    bd = Bibliographic::set_datum(Wm, I"TeX Mathematics Notation", I"$");
    bd = Bibliographic::set_datum(Wm, I"TeX Mathematics Displayed Notation", I"$$");
    bd = Bibliographic::set_datum(Wm, I"Footnote Begins Notation", I"[");
    bd = Bibliographic::set_datum(Wm, I"Footnote Ends Notation", I"]");
    bd = Bibliographic::set_datum(Wm, I"Code In Commentary Notation", I"|");
    bd = Bibliographic::set_datum(Wm, I"Code In Code Comments Notation", I"|");
    bd = Bibliographic::set_datum(Wm, I"Cross-References Notation", I"//");
    bd = Bibliographic::set_datum(Wm, I"Web Syntax Version", NULL);
    bd = Bibliographic::set_datum(Wm, I"Paragraph Numbers Visibility", I"On");
}

§5. Once the declarations for a web have been processed, the following is called to check that all the mandatory declarations have indeed been made:

void Bibliographic::check_required_data(web_md *Wm) {
    web_bibliographic_datum *bd;
    LOOP_OVER_BIBLIOGRAPHIC_DATA(bd, Wm)
        if ((bd->declaration_mandatory) &&
            (Str::len(bd->value) == 0))
                Errors::fatal_with_text(
                    "The web does not specify '%S: ...'", bd->key);
}

§6. Reading bibliographic data. Key names are case-sensitive.

text_stream *Bibliographic::get_datum(web_md *Wm, text_stream *key) {
    web_bibliographic_datum *bd = Bibliographic::look_up_datum(Wm, key);
    if (bd) return bd->value;
    return NULL;
}

int Bibliographic::data_exists(web_md *Wm, text_stream *key) {
    web_bibliographic_datum *bd = Bibliographic::look_up_datum(Wm, key);
    if ((bd) && (Str::len(bd->value) > 0)) return TRUE;
    return FALSE;
}

web_bibliographic_datum *Bibliographic::look_up_datum(web_md *Wm, text_stream *key) {
    web_bibliographic_datum *bd;
    LOOP_OVER_BIBLIOGRAPHIC_DATA(bd, Wm)
        if (Str::eq(key, bd->key)) {
            if (bd->alias) return bd->alias;
            return bd;
        }
    return NULL;
}

§7. Writing bibliographic data. Note that a key-value pair is created if the key doesn't exist at present, so this routine never fails.

web_bibliographic_datum *Bibliographic::set_datum(web_md *Wm, text_stream *key, text_stream *val) {
    web_bibliographic_datum *bd = Bibliographic::look_up_datum(Wm, key);
    if (bd == NULL) Create a new datum, then7.1
    else Str::copy(bd->value, val);
    if (Str::eq_wide_string(key, U"Title")) Also set a capitalized form7.2;
    return bd;
}

§7.1. Create a new datum, then7.1 =

    bd = CREATE(web_bibliographic_datum);
    bd->key = Str::duplicate(key);
    bd->value = Str::duplicate(val);
    bd->declaration_mandatory = FALSE;
    bd->declaration_permitted = TRUE;
    bd->on_or_off = FALSE;
    bd->alias = NULL;
    ADD_TO_LINKED_LIST(bd, web_bibliographic_datum, Wm->bibliographic_data);

§7.2. A slightly foolish feature, this; if text like "Wuthering Heights" is written to the "Title" key, then a full-caps "WUTHERING HEIGHTS" is written to a "Capitalized Title" key. (This enables cover sheets which want to typeset the title in full caps to do so.)

Also set a capitalized form7.2 =

    TEMPORARY_TEXT(recapped)
    Str::copy(recapped, val);
    LOOP_THROUGH_TEXT(P, recapped)
        Str::put(P, Characters::toupper(Str::get(P)));
    Bibliographic::set_datum(Wm, I"Capitalized Title", recapped);
    DISCARD_TEXT(recapped)