Section 1330: The main program

This is it: the part of that executes all those procedures we have written.

Well—almost. Let’s leave space for a few more routines that we may have forgotten.

init_cleanup.c
// << Last-minute procedures, 1333 >>

Section 1331

We have noted that there are two versions of . One, called INITEX, has to be run first; it initializes everything from scratch, without reading a format file, and it has the capability of dumping a format file. The other one is called ‘VIRTEX’; it is a “virgin” program that needs to input a format file in order to get started. VIRTEX typically has more memory capacity than INITEX, because it does not need the space consumed by the auxiliary hyphenation tables and the numerous calls on primitive, etc.

The VIRTEX program cannot read a format file instantaneously, of course; the best implementations therefore allow for production versions of that not only avoid the loading routine for Pascal object code, they also have a format file pre-loaded. This is impossible to do if we stick to standard Pascal; but there is a simple way to fool many systems into avoiding the initialization, as follows:

  1. We declare a global integer variable called ready_already. The probability is negligible that this variable holds any particular value like 314159 when VIRTEX is first loaded.

  2. After we have read in a format file and initialized everything, we set ready_already ← 314159.

  3. Soon VIRTEX will print ‘*’, waiting for more input; and at this point we interrupt the program and save its core image in some form that the operating system can reload speedily.

  4. When that core image is activated, the program starts again at the beginning; but now ready_already = 314159 and all the other global variables have their initial values too. The former chastity has vanished!

In other words, if we allow ourselves to test the condition ready_already = 314159, before ready_already has been assigned a value, we can avoid the lengthy initialization. Dirty tricks rarely pay off so handsomely.

On systems that allow such preloading, the standard program called TeX should be the one that has plain format preloaded, since that agrees with The TeXbook. Other versions, e.g., AmSTeX, should also be provided for commonly used formats.

NOTE

This implementation does not support this dirty trick.

 ready_already: integer; { a sacrifice of purity for economy }

Section 1332

Now this is really it: starts and ends here.

The initial test involving ready_already should be deleted if the Pascal runtime system is smart enough to detect such a “mistake”.

main.c
// << Start file |main.c|, 1382 >>

// |start_here|
int main(int argc, char *argv[]) {
    history = FATAL_ERROR_STOP; // in case we quit during initialization
    // if ready_already = 314159 then goto start_of_TEX
    // << Check the "constant" values for consistency, 14 >>
    if (bad > 0) {
        printf(
            "Ouch---my internal constants have been clobbered!" "---case %d\n", bad
        );
        exit(0);
    }
    initialize(); // set global variables to their starting values
#ifdef INIT
    get_strings_started();
    init_prim(); // call |primitive| for each primitive
    init_str_ptr = str_ptr;
    init_pool_ptr = pool_ptr;
    fix_date_and_time();
#endif
    // ready_already = 314159; Sorry, not supported
    // start_of_TEX:
    // << Initialize the output routines, 55 >>
    // << Get the first line of input and prepare to start, 1337 >>
    history = SPOTLESS; // ready to go!
    main_control();     // come to life
    final_cleanup();    // prepare for death
    // end_of_TEX:
    close_files_and_terminate();
    return 0;
}

Section 1333

Here we do whatever is needed to complete ’s job gracefully on the local operating system. The code here might come into play after a fatal error; it must therefore consist entirely of “safe” operations that cannot produce error messages. For example, it would be a mistake to call str_room or make_string at this time, because a call on overflow might lead to an infinite loop. (Actually there’s one way to get error messages, via prepare_mag; but that can’t cause infinite recursion.)

If final_cleanup is bypassed, this program doesn’t bother to close the input files that may still be open.

⟨ Last-minute procedures 1333 ⟩≡

void close_files_and_terminate() {
    int k; // all-purpose index
    // << Finish the extensions, 1378 >>
    new_line_char = -1;
#ifdef STAT
    if (tracing_stats > 0) {
        // << Output statistics about this job, 1334 >>
    }
#endif
    // << Finish the DVI file, 642 >>
    if (log_opened) {
        wlog_cr;
        a_close(log_file);
        selector -= 2;
        if (selector == TERM_ONLY) {
            print_nl("Transcript written on ");
            slow_print(log_name);
            print_char('.');
            print_ln();
        }
    }
}

Section 1334

The present section goes directly to the log file instead of using print commands, because there’s no need for these strings to take up str_pool memory when a non-stat version of is being used.

⟨ Output statistics about this job 1334 ⟩≡

if (log_opened) {
    wlog_ln(" ");
    wlog_ln("Here is how much of TeX's memory you used:");
    wlog(" %d string", str_ptr - init_str_ptr);
    if (str_ptr != init_str_ptr + 1) {
        wlog_char('s');
    }
    wlog_ln(" out of %d", MAX_STRINGS - init_str_ptr);
    wlog_ln(" %d string characters out of %d", pool_ptr - init_pool_ptr, POOL_SIZE - init_pool_ptr);
    wlog_ln(
        " %d words of memory out of %d",
        lo_mem_max - MEM_MIN + mem_end - hi_mem_min + 2,
        mem_end + 1 - MEM_MIN
    );
    wlog_ln(
        " %d multiletter control sequences out of %d",
        cs_count, HASH_SIZE
    );
    wlog(
        " %d words of font info for %d font",
        fmem_ptr, font_ptr - FONT_BASE
    );
    if (font_ptr != FONT_BASE + 1) {
        wlog_char('s');
    }
    wlog_ln(", out of %d for %d", FONT_MEM_SIZE, FONT_MAX - FONT_BASE);
    wlog(" %d hyphenation exception", hyph_count);
    if (hyph_count != 1) {
        wlog_char('s');
    }
    wlog_ln(" out of %d", HYPH_SIZE);
    wlog_ln(
        " %di,%dn,%dp,%db,%ds stack positions out of %di,%dn,%dp,%db,%ds", 
        max_in_stack,
        max_nest_stack,
        max_param_stack,
        max_buf_stack,
        max_save_stack + 6,
        STACK_SIZE,
        NEST_SIZE,
        PARAM_SIZE,
        BUF_SIZE,
        SAVE_SIZE
    );
}

Section 1335

We get to the final_cleanup routine when \end or \dump has been scanned and its_all_over.

⟨ Last-minute procedures 1333 ⟩+≡

void final_cleanup() {
    small_number c; // 0 for \end, 1 for \dump
    c = cur_chr;
    if (c != 1) {
        new_line_char = -1;
    }
    if (job_name == 0) {
        open_log_file();
    }
    while (input_ptr > 0) {
        if (state == TOKEN_LIST) {
            end_token_list();
        }
        else {
            end_file_reading();
        }
    }
    while (open_parens > 0) {
        print(" )");
        decr(open_parens);
    }
    if (cur_level > LEVEL_ONE) {
        print_nl("(");
        print_esc("end occurred ");
        print("inside a group at level ");
        print_int(cur_level - LEVEL_ONE);
        print_char(')');
    }
    while (cond_ptr != null) {
        print_nl("(");
        print_esc("end occurred ");
        print("when ");
        print_cmd_chr(IF_TEST, cur_if);
        if (if_line != 0) {
            print(" on line ");
            print_int(if_line);
        }
        print(" was incomplete)");
        if_line = if_line_field(cond_ptr);
        cur_if = subtype(cond_ptr);
        temp_ptr = cond_ptr;
        cond_ptr = link(cond_ptr);
        free_node(temp_ptr, IF_NODE_SIZE);
    }
    if (history != SPOTLESS
        && (history == WARNING_ISSUED || interaction < ERROR_STOP_MODE)
        && selector == TERM_AND_LOG)
    {
        selector = TERM_ONLY;
        print_nl("(see the transcript file for additional information)");
        selector = TERM_AND_LOG;
    }
    if (c == 1) {
#ifdef INIT
        for(c = TOP_MARK_CODE; c <= SPLIT_BOT_MARK_CODE; c++) {
            if (cur_mark[c] != null) {
                delete_token_ref(cur_mark[c]);
            }
        }
        if (last_glue != MAX_HALFWORD) {
            delete_glue_ref(last_glue);
        }
        store_fmt_file();
        return;
#endif
        print_nl("(\\dump is performed only by INITEX)");
        return;
    }
}

Section 1336

⟨ Last-minute procedures 1333 ⟩+≡

#ifdef INIT
// initialize all the primitives
void init_prim() {
    no_new_control_sequence = false;

    // << Put each of TeX's primitives into the hash table, 226 >>
    
    no_new_control_sequence = true;
}
#endif

Section 1337

When we begin the following code, ’s tables may still contain garbage; the strings might not even be present. Thus we must proceed cautiously to get bootstrapped in.

But when we finish this part of the program, is ready to call on the main_control routine to do its work.

⟨ Get the first line of input and prepare to start 1337 ⟩≡

// << Initialize the input routines, 331 >>
if (format_ident == 0 || buffer[loc] == '&') {
    if (format_ident != 0) {
        initialize(); // erase preloaded format
    }
    if (!open_fmt_file()) {
        exit(0);
    }
    if (!load_fmt_file()) {
        w_close(fmt_file);
        exit(0);
    }
    w_close(fmt_file);
    while (loc < limit && buffer[loc] == ' ') {
        incr(loc);
    }
}
if (end_line_char_inactive) {
    decr(limit);
}
else {
    buffer[limit] = end_line_char;
}
fix_date_and_time();
// << Initialize the print |selector| based on |interaction|, 75 >>
if (loc < limit && cat_code(buffer[loc]) != ESCAPE) {
    // \input assumed
    start_input();
}