CHAPTER 23:

THE MAIN FUNCTION

Like all C programs, Small C begins execution in a function called main(). Main() resides in the first part of the compiler (file CC1.C) and performs six tasks--(1) displaying the signon message, (2) allocating storage for various buffers, (3) initializing variables, (4) setting run-time options, (5) opening files, and (6) controlling overall program flow.

Main(), and the other functions mentioned below, can be located in the compiler listings (Appendix C) by referring to the function index in Appendix H. Textual references are also given so that further investigation of these functions can be easily pursued.

Displaying the Signon Message

The file NOTICE.H contains macro definitions for two symbols which specify version and copyright information. VERSION specifies the version and revision numbers of the compiler, and CRIGHT1 contains copyright information. NOTICE.H is included into part one of the compiler, making these notices available to main() for display on the console.

Allocating Storage

Calloc() is called repeatedly to allocate the switch queue, the staging buffer, the while queue, the literal pool, the macro name and text buffers, the line buffers, and the symbol table. This process also involves saving the addresses of these data structures in pointers and placing buffer ending addresses in other pointers.

Initializing Variables

Most global objects are initialized either by default or by the use of initializers. Main() initializes others which must be set at run time. Those are argcs, argvs, locptr, and glbptr. Of course, the setting of pointers to allocated blocks of memory, described above, is also an initializing activity.

Processing Command-Line Switches

Command-line switches are processed by ask(). Basically, this consists of setting default values for several global variables, then scanning the command line for switches that specify different values for them. These variables are described below:

If ask() finds an undefined switch it displays a usage message, showing which switches are valid, and then aborts the run. This reminder can be evoked deliberately by supplying the null switch (-).

To gain access to the command-line arguments, ask() calls the Small C library function getarg(). Any arguments without a leading hyphen are considered to be filenames and so are ignored by ask(). By converting switch characters to uppercase and comparing them to uppercase constants ask() is effectively case blind.

Opening Files

Before compiling begins main() calls openfile() to open the first source file that might be specified in the command line (also an output file of the same name but with an ASM extension). Later, openfile() will be called, at a lower level, to look for the next source file when the current file has been exhausted.

Like ask(), openfile() calls getarg() repeatedly looking for file names (any argument not preceded by a hyphen). That failing, it sets input to the value of stdin, thereby establishing the default input device. (Recall that the standard input file may be redirected to any file or input device.) On the other hand, if it finds a filename, openfile() tries to open it for input. First, however, it supplies C as a default extension if none was given. If an input file was given, openfile() assumes that the output should go to a file of the same name, but with an extension of ASM--unless the standard output file was redirected from the screen, that is. Redirecting it implies that the user has other ideas, and so the output should be left alone. If openfile() cannot open a file, it displays an error message and aborts the run.

After opening the first input file, main() calls preprocess() to fetch the first input line for parsing.

Controlling Program Flow

With these preliminaries out of the way, main() calls five functions to effect high level control over the compiler. In order of execution, they are as follows:

  1. Header(), as we saw in Chapter 21, writes preliminary text to the output file.
  2. Setcodes() initializes the array code[] to the addresses of the p-code translation strings. It also calls setseq() which initializes seq[] to the addresses of arrays of optimizing sequences. Peep() scans this array to find potential optimizing cases (Chapter 27).
  3. Parse() is the highest level parsing function. Chapter 24 describes parse(); suffice it to say here that this step in main() is where the compiling takes place.
  4. Trailer() writes text at the end of the output file. This consists of (1) external declarations for functions that were referenced but not declared, (2) the external declaration of _main() (if the module being compiled contains the function main() ), (3) a directive that terminates the active memory segment, and (4) the END directive that signals the end of the output file. Also, if the compiler was compiled with DISOPT defined in CC4.C, this function dumps optimizing statistics to the standard output file.
  5. Finally, fclose() is called to close the output file. This call is not strictly necessary since all open files will be closed upon program exit anyway.

After that, main() exits through its closing brace and control returns to the operating system.

Go to Chapter 24 Return to Table of Contents