'\"!  pic | mmdoc
.if n .pH port.chap06 @(#)chap06	40.4
.\" Porting manual: Ch 5 PostScript chapter
.\"
.\" Ps = POSTSCRIPT
.\" Sd = NeWS
.\" Cs = CSCRIPT

.\" .RV "3 October 1989--Chapter 5, PostScript Interpreter"

.ds Ps P\s-2OST\s+2S\s-2CRIPT\s+2
.ds Sd \s-2X11/Ne\h'-0.2n'WS\s+2
.ds Cs \CS\s-2CRIPT\s+2
.BK "Porting the X11/NeWS Server"
.CH "PostScript Interpreter" 6
.H 1 "PostScript Interpreter" 
.H 2 "Introduction"
This chapter describes how to get the \*(Ps
Interpreter part of \*(Sd running.   
The essential steps are as follows:
.BL
.LI
Compile and Link the \*(Ps Interpreter Code 
.LI
Setting Up the Interpreter-only Server
.LI
Debugging the \*(Ps Interpreter Code
.LE
.H 2 "Compile and Link Interpreter Code" 
This section describes how to compile and link the PostScript
interpreter code.  Use this section as a general guideline.  
Note that the UNIX commands shown assume the
Korn or Bourne Shells.  Also, the shell environment variable 
.UI $SRCROOT 
is assigned as the pathname of the top of the \*(Sd 
source tree.
.H 2 "C Compiler Problems"
.P
Because of the complexity of the \*(Ps code, you may have trouble compiling
.UI Postscript.c .
Some C Preprocessors don't have enough storage allocated for macro
definitions.  Possible error messages for this problem may include the 
following:
.SS
Too many defines
Too much defining
.ft1
.SE
If this is the case, you'll need to increase the size of the data 
structures used in 
.UI cpp 
(the C language preprocessor) to hold 
.UI #defines 
(this requires the source to CPP).
.\" For further information on C compiler issues, refer to Appendix A.
.H 2 "Set Up Environment"
.P
Set up the \*(Sd environment by entering the following:
.SS
\f4Example$\fP cd /usr/src/xnews
\f4Example$\fP . ./setup
\f4Example$\fP make subdirs
.ft1
.SE
.H 3 "Install \f5libcps\fP Include Files"
.P
This task installs 
.UI encoding.h ,
.UI psio.h ,
.UI systoklst.h 
and
.UI psmacros.h 
in 
.UI $DESTDIR/usr/include/NeWS .
.P
Execute the following commands:
.SS
\f4Example$\fP cd $SRCROOT/usr.lib/libcps
\f4Example$\fP make -i install_h
.ft1
.SE
This will first build the cps program,
which is then used to generate
.UI systoklst.h 
and
.UI psmacros.h.
.P
Afterwards  
.UI encoding.h 
and 
.UI psio.h 
are installed.
If this fails, execute the following commands:
.SS
\f4Example%\fP cd $SRCROOT/usr.lib/libcps
\f4Example%\fP cp encoding.h $DESTDIR/usr/include/NeWS
\f4Example%\fP cp psio.h $DESTDIR/usr/include/NeWS
.ft1
.SE
.H 2 "Install the X11 Include Files"
.P
Some of the nucleus server code depends on X11 include files such as
.UI X.h ,
so these must be installed.
Execute the following commands:
.SS

\f4Example$\fP mkdir -p $DESTDIR/usr/include/X11
\f4Example$\fP cd $SRCROOT/usr.lib/libX11
\f4Example$\fP make install_h
.ft1
.SE

.H 2 "Build the \f4psio\f1 Code"
.P
Now that the include files are in place, you can build the 
.UI psio 
code. 
The 
.UI psio 
code provides an interface similar to UNIX's 
.UI stdio .
It is needed because 
.UI stdio
doesn't provide support for non-blocking I/O or separate buffering for
input and output which is 
necessary for the interpreter.

.H 2 "Compile \f5psio.c\fP and \f5psiofprntf.c\fP"
.P
To compile \f4psio.c\fP and \f4psiofprntf.c\fP, 
execute the following commands:
.SS

\f4Example$\fP cd $SRCROOT/usr.lib/libcps 
\f4Example$\fP make psio.o
\f4Example$\fP make psiofprntf.o
.ft1
.SE

If either of these fails,
it is probably due to the source trying to 
.UI #include 
a non-existent file.
If this happens, you will get an appropriate message from the compiler, 
such as ``Cannot find include file xyz.''
If this is the case,
it is likely because the -D flags have been improperly specified in
.UI ~/Master.cfg .
In any event, you will need to correct and problems before proceeding.

.H 2 "Compile the \f5palloc\fP Code"
The server depends on the 
.UI palloc 
memory allocation routines. For initial work,
.UI malloc 
can be used instead. The file 
.UI debug_palloc.c 
in 
.UI ~/usr.bin/xnews/xnews/palloc
contains code which replaces 
.UI palloc 
with 
.UI malloc .
Build this as follows:
.SS

\f4Example$\fP cd $SRCROOT/usr.bin/xnews/xnews/palloc
\f4Example$\fP make all
\f4Example$\fP make install
.ft1
.SE

.H 2 "Build the Nucleus and Core Code"
To build the nucleus and core code, execute the following:
.SS

\f4Example$\fP \s-1cd $SRCROOT/usr.bin/xnews/xnews/server/nucleus\s+1
\f4Example$\fP make > make.out 2>&1 &
.ft1
.SE

Note that we have redirected the output from this command into a file so that
we have a record of any problems.  Note also that the 
.UI make 
is run in the background since it is likely to take 15 to 20 minutes.
.P
As stated previously, failures
are probably due to the source trying to 
.UI #include 
a non-existent file. You will get an appropriate message from the compiler
such as ``cannot find include file xyz''.
If this is the case,
it is likely because the -D flags have been improperly specified.
.P
Note that because of compiler limitations, the compile may fail on
.UI PostScript.c .
You may also get syntax errors 
or other problems.
.UI PostScript.c
consists of over 30,000 lines of code, and puts heavy stresses
on compilers.  Once
.UI PostScript.c 
does compile, you are well on the way to getting the interpreter working.

.H 2 "Build the Core Directory"
.P
After the nucleus directory is built, you can proceed with building the 
Core directory. Actually, this task can be done in parallel with the nucleus 
directory should you have the available personnel.
.P
The Core directory consists of significantly less code and should
present fewer problems then nucleus.
Once again, to build the Core directory, execute the following: 
.SS
\f4Example$\fP cd $SRCROOT/usr.bin/xnews/xnews/server/core
\f4Example$\fP make > make.out 2>&1 &
.ft1
.SE

.H 2 "Compile Dummy Stubs and Initialization Code"
.P
Once you have the psio, nucleus, Core and the compatibility code 
(see the chapter on BSD emulation library) compiled,
you are ready to perform the final steps for constructing an \*(Ps 
interpreter-only server.
These final two steps are compiling the dummy stub subroutines 
to resolve
references the nucleus code makes to \*(Cs and Shapes code and compiling a set
of calls to initialization routines which are needed to initialize the
server.  These routines are contained in the directory
.UI \s-2~/usr.bin/xnews/xnews/server/portinghelp/interpreter\s+2 .
.P
To build the dummy stubs, enter the command shown below:
.SS

\f4Example$\fP ~/usr.bin/xnews/xnews/server/portinghelp/interpreter
\f4Example$\fP sh ld_dummy.sh
.ft1
.SE

You may first have to change the -D and other flags passed to the compiler in 
.UI ld_dummy.sh .
They should correspond to the flags set up in 
.UI DEFS 
in the 
.UI Master.cfg 
files.
.UI ld_dummy.sh 
builds 
.UI ld_dummy.c,
creating 
.UI ld_dummy.o.
.P
Next, build the initialization code by executing the command
.SS

\f4Example$\fP sh init.sh
.ft1
.SE
Again, you may have to edit the script to change the flags passed to the 
compiler. This script builds 
.UI initialize.c , 
creating 
.UI initialize.o.
.H 2 "Link the Interpreter-only Server"
Now that the dummy stubs and initialization code are compiled,
you can link the server.
This is done with the script 
.UI ld.sh 
in
.UI \s-2~/usr.bin/xnews/xnews/server/portinghelp/interpreter\s+2 .
.P
Execute the following command:
.SS

\f4Example$\fP cd
\f4Example$\fP sh ld.sh
.ft1
.SE

If all goes well, this will build an executable called 
.UI xnews
which is the \*(Ps interpreter-only server.
.P
If the linkage editor (ld) reports unresolved symbols,
you will have to investigate why they're unresolved and correct
the problem.  It is possible for unresolved references to be due to incorrect
specification of -D flags.
.P
If 
.UI ld 
reports unresolved references in 
.UI libnews.a 
or 
.UI libcore.a,
it can be difficult to find exactly what 
.UI .o 
file contains the reference.
The best way to do this is to execute the 
.UI nm 
command on the archive
in question and examine the output. The 
.UI nm 
command produces a symbol table for each .o file in the archive. For example,
if a reference in 
.UI libnews.a 
is unresolved, enter:
.SS
\f4Example$\fP cd $SRCROOT/usr.bin/xnews/xnews/server/nucleus
\f4Example$\fP nm libnews.a > libnews.nm
.br
nucleus/libnews.a > libnews.nm
.ft1
.SE

Then look in 
.UI libnews.nm 
and search for the unresolved symbol.
You can do this with vi.
Once the symbol is found, search backwards for the header that
.UI nm 
puts before each 
.UI .o 
file.  This header contains the word 'Symbols'.
Also keep in mind that the unresolved symbol may be referenced in more
than one 
.UI .o 
file.
.P
Once the 
.UI .o 
file has been found,
examine the corresponding 
.UI .c 
file to see where the reference is coming from.
Sometimes it's due to an incorrect pre-processor macro usage caused by
the wrong -D flag being specified.
If you suspect this, sometimes the -E option of the C compiler is helpful.
This generates output from the pre-processor only, so you can see
to what the macro invocations are translated.  To do this, add the following
to the Makefile you are using:
.UI LOCALCFLAGS=-E .
The output from the pre-processor goes to 
.UI stdout , 
so it may be wise to redirect the output of make to a file.

.H 2 "Setting Up the Interpreter-only Server" 
.P 
This section describes how to set up and run the \*(Ps interpreter-only server.
When the server runs, it attempts to open a file called
.UI etc/NeWS/init.ps .
It first looks in the current directory,
then in the directory assigned to the environment variable
.UI XNEWSHOME .
.P
The distribution tape comes with a sample 
.UI init.ps 
file which contains PostScript appropriate for testing the interpreter.
This is in
.UI ~/usr.bin/xnews/xnews/server/portinghelp/interpreter
.UI /NeWS/init.ps.
Thus to run the interpreter, set 
.UI $XNEWSHOME 
to the current directory by entering the following commands:
.SS

\f4Example$\fP cd $SRCROOT/usr.bin/xnews/xnews/server/\e
.br
portinghelp/interpreter/etc/NeWS/init.ps 
\f4Example$\fP XNEWSHOME=. export XNEWSHOME
.ft1
.SE
To run the server enter the following command:
.SS

\f4Example$\fP xnews
.ft1
.SE
This should print out the following:
.SS
Hello, world!
2+2=4
Sic Transit Gloria Postscript
.ft1
.SE 
.H 2 "Debugging the \*(Ps Interpreter" 
.P 
In order to debug the \*(Ps interpreter, it is necessary to  
understand the flow of execution.  In the following discussion we will describe 
the execution flow of the interpreter.  Although many files and
subroutines are provided in the text, we also recommend that you follow along
in the files listed.

.H 3 "Execution Flow of \*(Ps Interpreter" 
.P 
Basic initialization is performed by the subroutine \f4main()\fP which is
contained in the file \f4nucleus/main.c\fP.  After basic initialization, the
routine \f4runPostScript()\fP, which follows \f4main()\fP in
.UI  main.c ,
is called.
.SS
main(argc, argv)
    char      **argv;
{

    char   *initstr = argc > 1 ?
    	argv[1] : "(NeWS/init.ps) (r) file cvx exec &main ";
    .
    .
    .
    runPostScript(argv, initstr);
    RunScheduler();
}
.SE

.UI runPostScript()
then calls the routine \f4applyinitialization()\fP, which
accomplishes the remainder of server initialization.  
.SS
runPostScript(argv, initstr)
    char      **argv;
    char       *initstr;
{
    register struct execution_environment *ee;
    REF file;
    register struct corpus *c;

    .
    .
    .
    apply_initialization();
    ee = create_process();
    .
    .
    .
}
.SE

Afterwards, 
.UI runPostScript()
calls 
.UI create_process() 
(in \f4nucleus/processes.c\fP) to create 
the first process thread which will be 
used to read and execute the file 
.UI init.ps .   
The routine 
.UI runPostScript()
is passed a pointer to an initialization string,
which by default contains \*(Ps code to open \f4init.ps\fP and execute
its contents.
The process thread created is initialized to execute the code in this
initialization string by calling \f4new_composite()\fP
in \f4nucleus/objects.c\fP
followed by \f4psio_sopen()\fP
in \f4usr.lib/libcps/psio.c\fP.
.P
The default initialization string in 
.UI main() 
contains the following \*(Ps code:
.SS

   \fH(NeWS/init.ps) (r) file cvx exec &main\fP
.SE

where: 
.P
.DS I UI
\f3(NeWS/init.ps) (r) file\fP opens the file \f3NeWS/init.ps\fP for 
reading,
\f3cvx\fP converts the file to be executable,
\f3exec\fP executes the file as \*(Ps code, and
\f3&main\fP executes the procedure \f3&main\fP, which should have been 
defined in \f3init.ps\fP.
.DE
.P
Once the process thread is created,
\f4runPostScript()\fP returns to \f4main()\fP.  
The routine
.UI main()
then calls \f4RunScheduler()\fP
(\f4nucleus/sched.c\fP),
which starts the execution of the first process.
.SS
main(argc, argv)
    char      **argv;
{

    char   *initstr = argc > 1 ?
    	    argv[1] : "(NeWS/init.ps) (r) file cvx exec &main ";
    .
    .
    .
    runPostScript(argv, initstr);
    RunScheduler();
}
.SE

The first process begins execution in the routine \f4PostScript()\fP
(\f4nucleus/PostScript.c\fP), which is the \*(Ps language interpreter.
The input stream contains the following \*(Ps code:
.SS
   \fH(NeWS/init.ps) (r) file\fP
.SE

The first two items are strings,
and these are pushed on the operand stack.
The keyword \f3file\fP invokes the file primitive
(a \*(Sd extension to \*(Ps) which checks the type of the two top 
operands on the stack to make sure they're strings.  The figure at the 
end of this chapter shows the Operand Stack as it processes the input stream.
The C code for \f3file\fP can be found in 
.UI nucleus/objects.c 
and is shown below.
.SS
case file_primitive:
       {
        register char	*access;
        if (optop[-1].type != string_type || optop[-2].type !=  
              string_type)
            goto typecheck_error;
            access = (char *)corpus_of(optop[-1])
                        ->corpus.string.chars
                        + optop[-1].value.substring.start;
        if (optop[-1].value.substring.length != 1
                || (*access != 'r' && *access != 'w'))
            error(invalidfileaccess);
            errno = 0;
            newobj = open_file((char *)corpus_of(optop[-2])->
              corpus.string.chars+optop[-2].value.substring.start,
              (int) optop[-2].value.substring.length, access);
         if (newobj.type == null_type) {
                extern char *sys_errlist[];
                if (errno)
                    ee->error_detail = sys_errlist[errno];
                goto undefinedfilename_error;
            }
            goto typed_result;
        }
.SE

.B file 
then calls the routine 
.UI open_file() 
in
.UI nucleus/operators.h 
to open the file specified--in this case 
.UI NeWS/init.ps 
with the mode specified (read/only here).
.P
.UI open_file()
first tries opening the file in the current directory.  The routine 
\f4abspath()\fP in \f4nucleus/abspath.c\fP
is called to construct an absolute path name for the current directory.
If this doesn't work 
.UI open_file ()
appends the contents of the environment variable \f4XNEWSHOME\fP with
\f4/etc\fP and tries opening the subsequent file 
.UI $XNEWSHOME/etc/NeWS/init.ps.
.P
.UI open_file 
calls \f4psio_open()\fP in \f4usr.lib/libcps/psio.c\fP to perform the 
actual operating system dependent file opening function.
For the case of a read-only open,
.UI psio_open
simply calls the \f4open(2)\fP system call.
.P
If the call to \f4psio_open\fP succeeds,
.UI open_file 
continues and sets up the file as an object which can
be manipulated by the \*(Ps interpreter.

.H 3 "Executing the File"
.P
After the file 
.UI init.ps
is opened, the interpreter parses the next 
keyword 
in the initialization string 
.B (cvx) 
and
converts the file object to be executable.
Then, the next keyword (
.B exec )
directs the interpreter to
execute the object on top of the stack, which is 
.UI init.ps .
Object execution is done at the label \f4exec_primitive\fP in 
.UI operators.h , 
and simply sets the execution environment to point to the object on the 
top of the stack.
.SS
    case exec_primitive:
	.
	.
	.
	optop--;
	ee->execee = *optop;
	goto execute_top;
.SE

Execution then transfers to the label \f4execute_top\fP
in \f4PostScript()\fP.
.SS
    execute_top:
    .
    .
    .
execute_operator:
            switch (ee->execee.type) {
.SE

Since the object is executable, 
execution flows to the label \f4execute_operator\fP,
where the code switches on 
.UI "ee\(hy>execee.type" .
Because the object on the top of the stack is of type \f4file_type\fP,
execution transfers to the label 
.UI file_type 
(again in 
.UI PostScript.c) . 
.SS
    case file_type:
        {
            register struct corpus *c = 
                        corpus_of(ee->execee);
            if (c->corpus.file.inbuf == 0 ||
                    psio_error(c->corpus.file.inbuf))
                goto ioerror_error;
            estack_overflow_check();
            es++;
            es->executed = ee->execee;
            es->type = file_execution;
            es->interpreter = PostScript;
            es->env.file.file = c->corpus.file.inbuf;
            if (c->corpus.file.outbuf) {
                object_decref(&ee->stdprnt);
                ee->stdprnt = ee->execee;
                object_incref(&ee->stdprnt);
                psio_setnonblock(c->corpus.file.outbuf);
            }
        }
        break;
.SE

This adds a new entry to the execution stack (
.UI es++ 
), and gives it a type of \f4file_execution\fP and sets it to execute from the
file 
.UI "(es\(hy>env.file.file = c\(hy>corpus.file.inbuf)" .
Execution then continues at the top of the \*(Ps interpreter
with the switch on 
.UI es\(hy>type .
.SS
    while (1) {
	struct object newobj;

	.
	.
	.

retry_estack:

	.
	.
	.
	/* Pick up an object to execute */
	switch (es->type) {

	.
	.
	.
.SE

Since the type of the object on top of the execution stack is now
.UI file_execution , 
the switch branches to that label
(again in \f4PostScript()\fP). 
.SS
	case file_execution:
	.
	.
	.
	    {
		register PSFILE *f = es->env.file.file;
		/* be charitable and think of this as a procedure call */
#include	"parse_file.h"
		break;
	    }
.SE

The code in 
.UI parse_file.h 
is 
included 
here, so execution continues in that file.
.P
The code in \f4parse_file.h\fP is used for more than just files.
For example, \f4parse_file.h\fP originally parsed the input in the 
initialization string. Basically,
\f4parse_file.h\fP reads through the input stream,
and parses the next token.
The file \f4parse_file.h\fP ``returns'' an object, either executable or 
non\(hyexecutable, in 
.UI ee\(hy>execee .
Once the token is parsed into an object,
the 
.UI break 
statement (back in \f4PostScript.c\fP) takes execution to the label
\f4execute_unknown_object\fP,
where the test for 
.UI "ee\(hy>execee.type == packedarray_type" 
or 
.UI array_type 
fails.
.SS
execute_unknown_object:
	/* Execute the object */
	if (ee->execee.type == packedarray_type || 
	    ee->execee.type == array_type) {
	    overflow_check(1);
	    *optop++ = ee->execee;
	}
	else {
    execute_top:
	.
	.
	.
	if (!ee->execee.executable) {
	    overflow_check(1);
	    *optop++ = ee->execee;
	}
	else {
.SE

The server then continues execution at the label 
.UI execute_top 
in 
.UI PostScript() .
If 
.UI ee\(hy>execee 
is not executable, (for example a string or a number),
it places the object at the top of the operand stack 
.UI "(*optop++ = ee\(hy>execee)."
Otherwise, execution proceeds at the label 
.UI execute_operator ,
where the server switches on 
.UI ee\(hy>execee.type .
.SS
    else {
execute_operator:
        switch (ee->execee.type) {
    .
    .
    .
        case operator_type:
            if (optop - ee->execee.value.def->args_used < 
                ee->underflow)
                goto stack_underflow;
            overflow_check(ee->execee.value.def->growth);
            clear_object(&newobj);
            /*
             * operators.c contains a big switch statement that
             * implements all of the primitive operators
             */
#include    "operators.h"
            break;

.SE

The cases of the 
.UI switch 
statement are discussed below.  The principle
cases we'll see first are ``built\(hyin'' 
operators (\f4case operator_type\fP), files (\f4case file_type\fP) and strings 
(\f4case string_type\fP).
.P
.H 3 "\f4case string_type\fP"
.P
This case is the case we already saw, as the server initialization string is
executed as this type. The routine \f4psio_sopen()\fP 
(\f4usr.lib/libcps/psio.c\fP)
is called to "open" the string as a file.  In other words, it is set 
up so that the code in \f4parse_file.h\fP can handle it as if it were a file.
.SS
case string_type:
    {
        REF file;
        register struct corpus *c;
        estack_overflow_check();
        file = pnew(current_pool, file_type, 0);
        c = corpus_of(file);
        c->corpus.file.xstr  = ee->execee;
        c->corpus.file.inbuf = psio_sopen(
        (char *)corpus_of(ee->execee)->corpus.string.chars
                          + ee->execee.value.substring.start,
                           ee->execee.value.substring.length,
                           "r");
        set_object_from_ref(&ee->execee, file);
    }
    /* NO BREAK; */
.SE

.H 3 "\f4case operator_type\fP"
.P
\f4operator_type\fP is the second case we see, and is where
we are now, as the third token 
in the initialization
string is \f3file\fP, which invokes the \f4file_primitive\fP operator.  Built-in
operators are handled by a large 
.UI switch 
statement in \f4operators.h\fP.
The code switches on 
.UI ee\(hy>execee.value.def\(hy>index .  
For the 
.B file 
primitive,
execution is handled at the 
.UI file_primitive 
label in 
.UI operators.h .
.SS
        case operator_type:
            if (optop - ee->execee.value.def->args_used < 
               ee->underflow)
                goto stack_underflow;
            overflow_check(ee->execee.value.def->growth);
            clear_object(&newobj);
            /*
             * operators.c contains a big switch statement 
             * that implements all of the primitive operators
             */
#include     "operators.h"
            break;

.SE

.H 3 "\f4case file_type\fP"
.P
For files, a new entry is placed on the execution stack.  This entry
has type \f4file_execution\fP.  The interpreter is set to \f4PostScript()\fP,
and the input stream (\f4es\(hy>env.file.file\fP) is set to the input stream 
associated with the file object 
.UI corpus_of(ee\(hy>execee)\(hy>corpus.file.inbuf .
.SS
        case file_type:
            {
                register struct corpus *c = 
                            corpus_of(ee->execee);
                    .
                    .
                    .
                if (c->corpus.file.inbuf == 0 ||
                        psio_error(c->corpus.file.inbuf))
                    goto ioerror_error;
                estack_overflow_check();
                es++;
                es->executed = ee->execee;
                es->type = file_execution;
                es->interpreter = PostScript;
                es->env.file.file = c->corpus.file.inbuf;
                if (c->corpus.file.outbuf) {
                    object_decref(&ee->stdprnt);
                    ee->stdprnt = ee->execee;
                    object_incref(&ee->stdprnt);
                    psio_setnonblock(c->corpus.file.outbuf);
                }
            }
            break;
.SE

After each of these cases, the 
.UI break 
statement takes execution to
the 
.UI continue 
statement right before the label \f4undefinedfilename_error\fP.
This takes us back to the top of the \f4while(1)\fP loop which is the main loop
of the server.  
.SS
    while (1) {
        struct object newobj;
        .
        .
        .
                break;
            } /* end switch */
        } /* end else */
        } /* end else just before execute_top */
        continue;
undefinedfilename_error:
.SE
In the case of server initialization, the switch on 
\f4es\(hy>type\fP will take us back into \f4parse_file.h\fP.
.P
.H 3 "\f4parse_file.h\fP"
.P
.UI parse_file.h 
consists of a loop starting at the label \f4restart_parse.\fP
.SS
{
    register    c;

restart_parse:
    ee->ofill = ee->fill;

    pf_getc_nb(1, c);	/* non-blocking so that we don't hang on
                       * startup */
.SE

The first thing it does is read the next character from the input stream
via \f4pf_getc_nb()\fP, a macro defined at the beginning of 
.UI parse_file .

.UI pf_getc_nb() 
reads the next character without blocking the process if there
is no character to read. 
.SS
#define pf_getc_nb(e,c) \e
	psio_pgetc_nb(f, c, \e 
		ee->restart_state = e; \e 
		ee->event = psio_fileno(f) + 1; \e
		goto suspend_process; \e
		CPPCONCAT(read_,e): \e
		f = es->env.file.file); \e
.SE
If you examine 
.UI pf_getc_nb , 
you'll see that it contains a rather
strange looking invocation of the macro 
.UI psio_pgetc_nb 
in 
.UI ~/usr.lib/libcps/psio.h .  
At first glance, it looks 
like a syntax error, since
the macro is not terminated on one line with a right parenthesis.
.P
To understand this, let's look at 
.UI psio_pgetc_nb:
.SS
psio_pgetc_nb(p, dest, pausecode) {	\e
    if (--(p)->cnt >= 0)				\e
        (dest) = *(p)->ptr++ & 0377;			\e
    else						\e
        while (1) {					\e
            psio_clearblockok(p);			\e
            dest = psio_filbuf(p);			\e
            if ((int)dest >= 0 || (!psio_error(p) && 
                                !psio_blocked(p)))	\e
                break;					\e
            (p)->flag &= ~PSERR;			\e
            pausecode;					\e
        }						\e
}
.SE
Note that the third parameter to this macro, 
.UI pausecode , 
corresponds to multiple
lines of code in the invocation of 
.UI psio_pgetc_nb 
used in 
.UI pf_getc_nb .
.P
This may cause a syntax error on the C preprocessor of your development system.
If it does, you will have to recode all of invocations of these and
similar macros.
.P
After reading the character, the code then switches on the character. 
.SS
    pf_getc_nb(1, c);	/* non-blocking so that we don't hang on
	               * startup */
    switch (c) {
.SE
Some special cases handled first.  These are described below.

.VL
.LI "\f(BIWhite space\fP "
This consists of tab,
space, newline, carriage return, or form feed.  Skip it by going back to 
\f4restart_parse\fP.  
.SS
    case ' ':
    case '\et':
    case '\en':
    case '\er':
    case 014:
	goto restart_parse;
.SE
.LE
.P
.VL
.LI "\f(BI% \fP"
Indicates a comment. Read until EOF or end of line is found, then go back 
to \f4restart_parse\fP.
.SS
    case '%':
        do {
            pf_getc(2, c);
        } while (c != '\n' && c != EOF);
        goto restart_parse;
.SE

.LI "``\f(BI[\fP'' or ``\f(BI]\fP''"
Handle as an identifier by going to \f4its_an_identifier\fP (see below).
.SS
    case '[':
    case ']':
       .
       .
       .
       check_buffer(0);
       ee->static_buffer[ee->fill++] = c;
       goto its_an_identifier;
.SE
.LI "\f(BI(\fP "
Handle as a quoted string.  The code reads characters until it finds
EOF or a ``)''.  It handles \\ as an escape (\\n, \\b, \\f, \\r, \\t, \\xxx).
Once it has the entire string, it creates a new object by calling
\f4new_composite()\fP in \f4nucleus/objects.c\fP.
.SS
    case '(':
        {
#define depth ee->execee.value.fixed
        .
        .
        .
            depth = 0;
            while (1) {
                pf_getc(3, c);
                if (c == EOF || (c == ')' && --depth < 0))
                    break;
                switch (c) {
                case '(':
                    depth++;
                    break;
                case BACKSLASH:
                    pf_getc(4, c);
                    switch (c) {
                    case '\n':
                        goto ignore_character;
                    case 'b':
                        c = 010;
                        break;
                    case 'f':
                        c = 014;
                        break;
                    case 'n':
                        c = 012;
                        break;
                    case 'r':
                        c = 015;
                        break;
                    case 't':
                        c = 011;
                        break;
                    default:
                        if (isoctdigit(c)) {
                            temp = c & 7;
                            pf_getc(5, c);
                            if (isoctdigit(c)) {
                                temp = (temp << 3) + (c & 7);
                                pf_getc(6, c);
                                if (isoctdigit(c))
                                 temp = (temp << 3) + (c & 7);
                                else
                                    psio_ungetc(c, f);
                            } else
                                psio_ungetc(c, f);
                            c = temp;
                        }
                    }
                    break;
                }
                check_buffer(0);
                ee->static_buffer[ee->fill++] = c;
        ignore_character:;
            }
            new_composite(current_pool, &ee->execee,
                    string_type, ee->fill - ee->ofill,
                    ee->static_buffer + ee->ofill, nulldict);
								
#undef depth
        }
        break;
.SE

.LI "\f(BI<\fP "
Handle as a hexadecimal number.
.SS
    case '<':
	.
	.
	.
	while (1) {
	    do {
		pf_getc(7, c);
	    } while (isspace(c));
	    if (!isxdigit(c))
		break;
	    check_buffer(0);
	    ee->static_buffer[ee->fill] = (c <= '9' ? c - '0' : 
		(c & 037) + 9) << 4;
	    do {
		pf_getc(8, c);
	    } while (isspace(c));
	    if (!isxdigit(c)) {
		ee->fill++;
		break;
	    }
	    ee->static_buffer[ee->fill++]|=(c <= '9' ? c - '0' : 
		(c & 037) + 9);
	}
	new_composite(current_pool, &ee->execee,
		      string_type, ee->fill - ee->ofill,
		      ee->static_buffer + ee->ofill, nulldict);
	break;
.SE

.LI "\f(BI{\fP "
Set mark for the beginning of an executable array 
.UI (ee\(hy>arrstart) .
.SS
    case '{':
        .
        .
        .
        check_buffer(sizeof(int) - 1);
        *(int *) (ee->static_buffer+ee->fill) = ee->arrstart;
        ee->ofill += sizeof(int);
        ee->arrstart = ee->fill = ee->ofill;
        goto restart_parse;
.SE

.LI "\f(BI}\fP "
Make an executable array starting at the mark made when the
``{'' was found (\f4ee\(hy>arrstart\fP).
.SS
    case '}':
        if (ee->arrstart < 0)
            goto syntax_error;
        ee->execee = make_executable_array(
           (struct object *)(ee->static_buffer + ee->arrstart),
                 (int) (ee->fill-ee->arrstart) / sizeof(struct 
                        object),
                        ee->packedarrays);
        .
        .
        .
.SE

.LI "\f(BI/\fP "
Put in token buffer (\f4ee\(hy>static_buffer\fP).
.SS
    case '/':
        .
        .
        .
        do {
            check_buffer(0);
            ee->static_buffer[ee->fill++] = c;
            pf_getc(9, c);
        } while (c == '/');
.SE

.LI "\f(BIdefault\fP"
Every other ASCII character is handled by assuming it is the start
of a new token.  The code loops reading characters from the input
stream and putting them in the token buffer (\f4ee\(hy>static_buffer\fP)
until a space or control character or a delimiter is seen.
.SS
    default:
        .
        .
        .
        while (c != EOF && !delim[c]) {
            check_buffer(0);
            ee->static_buffer[ee->fill++] = c;
            pf_getc(10, c);
        }
        if (ee->fill == ee->ofill)  
                        /* delimiter > 128 - not special */
            goto restart_parse;
        if (delim[c] && c > ' ')  
                        /*token ends with special delimiter*/
            psio_ungetc(c, f);
.SE
.LE
.P 
Non-ASCII characters (> 127) are assumed to be indicators that
what follows is an encoded data element.  This will not be described
in depth here.
.P
Once a complete token has been accumulated in 
.UI ee\(hy>static_buffer ,
the code tries to convert it to a number.  If this fails (or if the
leading character is non-numeric), the code assumes that the token is
an identifier and goes to \f4its_an_identifier\fP.
.SS
        if (ee->static_buffer[ee->ofill] <= '9') {
        .
        .
        .
        } else
    its_an_identifier:
            {
               register struct kwd_hash_table *htp;
								
               register char *start = ee->static_buffer 
                                        + ee->ofill;
               register    len = ee->fill - ee->ofill;
               register unsigned    hash;
               register    rlen;
               int                executable = 1;
               .
               .
               .
               while (*start == '/') {
                   executable--;
                   start++;
                   len--;
               }
               rlen = len;
               hash = 0;

              /* macro side effects: len and start are changed */
               kwd_hash(hash, len, start);

               len = rlen;
               start -= len;

               htp = &kwd_hash_table[hash % kwd_hash_table_size];
								
               assert(len != -1);/*REMIND: may not be necessary*/
               while(htp->kwd) {
                    if (htp->kwd->namelen == len) {
                        register char *s1 = htp->kwd->name;
                        register char *s2 = start;
                        rlen = len;
                        while(rlen >= 0) {
           /*
            * Assert: following comparison should run as fast 
            * or equal to a bcmp for many machines.
            * Assert is true for sparc and 68020s.
            */
                    if (!rlen-- || *s1++ != *s2++ ||
                        !rlen-- || *s1++ != *s2++ ||
                        !rlen-- || *s1++ != *s2++ ||
                        !rlen-- || *s1++ != *s2++ ||
                        !rlen-- || *s1++ != *s2++ ||
                        !rlen-- || *s1++ != *s2++ ||
                        !rlen-- || *s1++ != *s2++ ||
                        !rlen-- || *s1++ != *s2++ ||
                        !rlen-- || *s1++ != *s2++ ||
                        !rlen-- || *s1++ != *s2++) {
                        if (rlen == -1) {
                                set_keyword_object(&ee->execee,
                                                     htp->kwd);
                                goto try_autobind;
                            }
                            else
                                break;
                            }
                        }
                    }
                    if (--htp < kwd_hash_table)
                      htp=&kwd_hash_table[kwd_hash_table_size-1];
                }
                /* Keyword not in kwd table, create an entry */
                if (len > MAX_KWD_LEN)
                    error(limitcheck);
                ee->execee = new_kwd_name(htp, start, len);
.SE
The code here looks through all the entries in a hash table of identifiers
trying to find a match.  If a match is found, execution proceeds at the
label \f3try_autobind\fP.  In most cases, 
the test on 
.UI ee->ofill 
fails, 
so execution falls through to the end of 
.UI parse_file.h 
with 
.UI ee->execee
set to the object parsed.
.SS
try_autobind:
            switch (executable) {
            case 1:
                ee->execee.executable = 1;
                if (ee->ofill && ee->autobind && 
                            !ee->seeking_token) {
.SE

.UI ee->execee 
is set to the keyword parsed, 
with 
.UI ee->execee.type 
set to
.UI keyword_type 
and 
.UI ee->execee.executable 
set to non-zero.
.P
The 
.UI break 
statement in 
.UI PostScript.c
after 
.UI parse_file.h 
causes execution to proceed back to
.UI execute_top .  
Since 
.UI .ee->execee 
is executable, execution flows to the
switch on 
.UI ee->execee.type .  
Since 
.UI ee->execee.type 
is 
.UI keyword_type ,
execution proceeds to 
.UI "case keyword_type" , 
shown below:
.SS
    case keyword_type:
	{
          register struct dictstack_ent *stackent=ee->dict_top;
          register hash;
          hash_object(hash, &ee->execee);
#ifdef GPROF_HOOKS
asm("gP_ExecKeyword:");
#endif
    while (--stackent >= ee->dictionary_stack) {
        register DICT *c;
        register struct object *ret;

        if (stackent->dict.type == dictionary_type)
            c = dict_of(stackent->dict);
        else
            c = dict_table[(unsigned) stackent->dict.type];

            FIND_HASHED_OBJECT_IN_DICTIONARY(
                        ee->execee, hash, c, ret);

            if (ret) {
                if (ret->type == magic_variable_type) {
                    /* GET_MAGIC returns value in ee->optop */
                    overflow_check(1);
                    ee->optop = optop;
                    ee->pos = es;
                    (*ret->value.def->function)
                        (entity_addr(stackent->dict),
                            GET_MAGIC(ret->value.def->index));
                    if (ee->error_code)
                        goto handle_error;
                    /* GET_MAGIC already did incref */
                    ee->execee = *optop;
                    /* GET_MAGIC bumped ee->optop */
                    ee->optop = optop;
                    } else {
                        ee->execee = *ret;
                        INCREF(ee->execee);
                        magic_target = stackent;
                    }
                    goto execute_top;
                } else
                    /* try instance dictionary if magic dict */
                    if (stackent->dict.type != dictionary_type) {
                           c=instancedict_of(entity_addr(
                           stackent->dict));
                         if (c) {
                            FIND_HASHED_OBJECT_IN_DICTIONARY(
                                     ee->execee, hash, c, ret);
                            if (ret) {
                                ee->execee = *ret;
                                object_incref(&ee->execee);
                                magic_target = stackent;
                                goto execute_top;
                             }
                        }
                    }
            }
        }
.SE

The code here is similar to that at 
\f4try_autobind\f1 
in 
\f4parse_file.h\f1. 
It looks
up the keyword in the dictionaries in the dictionary stack.  If an entry
is found in the dictionary, execution goes back to 
.UI execute_top .  
This time,
.UI ee->execee.type 
will not be 
.UI keyword_type .  
Its value will be whatever was
in the dictionary entry.  For primitive operators like 
.B file , 
.B exec 
and 
.B cvx ,
its value will be 
.UI operator_type , 
so execution will continue with the 
.UI switch 
statement in 
\f4operators.h\f1.
.P
The code at 
.UI try_autobind 
in 
.UI parse_file.h 
which does dictionary look ups
is used only when an aggregate object, such as code bracketed by
curly braces, is being parsed.  In this case, 
.UI ee->ofill 
will be non-zero.
.SS
try_autobind:
            switch (executable) {
            case 1:
                ee->execee.executable = 1;
                if (ee->ofill && ee->autobind && 
                            !ee->seeking_token) {
                    register struct dictstack_ent 
                            *stackent = ee->dict_top;
                    while (--stackent >= ee->
                            dictionary_stack) {
                        register struct object *ret;
                        register DICT *dp;
                         if (stackent->dict.type 
                            == dictionary_type)
                            dp = dict_of(stackent->dict);
                        else
                            dp = dict_table[(unsigned) 
                                    stackent->dict.type];
                        ret = find_object_in_dictionary
                                    (ee->execee, dp);

                        if (ret) {
                            if (ret->type == called_operator_type
                                   || ret->type == operator_type)
                                ee->execee = *ret;
                            break;
                        }
                    }
                }
            case 0:
                break;
            default:
                {
                    register struct dictstack_ent *stackent = 
                            ee->dict_top;
                    while (--stackent >= ee->dictionary_stack) {
                        register struct object *ret;
                        register DICT *dp;
                         if (stackent->dict.type==dictionary_type)
                            dp = dict_of(stackent->dict);
                        else
                            dp = dict_table[(unsigned) 
                                    stackent->dict.type];

                        ret = find_object_in_dictionary(ee->
                                    execee, dp);
                         if (ret) {
                            object_incref(ret);
                            ee->execee = *ret;
                            break;
                        }
                    }
                }
            }
        }
    break;
.SE

.H 2 "file_primitive"
.P
The code which handles the 
.B file 
operator calls
the routine \f4open_file()\fP in \f4objects.c\fP, which opens the file with name
and mode on the operand stack. An object corresponding to the opened
file is left on the operand stack.
.P
In the case of our debug \f4init.ps\fP,
the following is read:
.DS I UI
/&main {
(Hello, world!) print
(2 + 2 = ) print 
2 2 add =
} def
.DE
This defines the procedure \f3&main\fP,
which is executed at the end of the initialization string.
When the end\(hyof\(hyfile is reached on the input stream,
(in this case \f4init.ps\fP), the code in \f4parse_file\fP
pops the execution stack by branching to the label
\f4pop_estack\fP in \f4PostScript.c\fP.
.P
When the end-of-file is reached on the input stream,
the code in 
.UI parse_file 
pops the execution stack by branching to the label
.UI pop_estack 
in 
.UI PostScript.c .
End-of-file is handled at the 
.UI "case EOF" 
label of the switch on the 
character read (described above):
.SS
    pf_getc_nb(1, c);  /* non-blocking so that we don't 
                         * hang on startup */
    switch (c) {
    .
    .
    .
    case EOF:{
            register PSFILE *pff = corpus_of(ee->stdprnt)
                                ->corpus.file.outbuf;
            if (selectable_file(psio_fileno(f), 1))
                remove_selectable_file(psio_fileno(f), 1);
            if (pff && f->file == pff->file)
                psio_nodropclose(pff);
            psio_nodropclose(f);
            goto pop_estack;
    }
.SE

Here's the code at
.UI pop_estack
.SS
        pop_estack:
        .
        .
        .
        pop_simple_estack:
                if (--es < ee->execution_stack || 
                        es->interpreter != PostScript)
        .
        .
        .
.SE

Execution will then resume where it left off in the
initialization string, with the parsing of the token 
.B &main .
Since this is now defined and executable, the server will execute it.

.H 3 "General debug comments"
.P
The most important tool to have at your disposal is a symbolic source
level debugger, such as dbx or sdb. Without such a tool,
debugging the server will be difficult, if not impossible.
.P
In order for such debuggers to work properly,
it is necessary that the source code be compiled with the -g
option turned on.  See the chapter on source code overview 
for more information.
.P
When using such a debugger on the code in \f4PostScript.c\fP,
you will find that the debugger is likely to get confused by the
fact that \f4PostScript.c\fP #includes other code in \f4parse_file.h\fP,
\f4primitives.h\fP and \f4operators.h\fP.
This is not a normal use of the preprocessor #include mechanism,
but was done to reduce the complexity of the interpreter.
To overcome this difficulty,
comment out the #include directives in \f4PostScript.c\fP
and literally include the code in \f4PostScript.c\fP.
Use an editor such as vi to read in the code at the appropriate places.
Once you have done this,
recompile \f4PostScript.c\fP and relink the server.
.H 2 "Debugging Procedure"
.P
The procedure for debugging the \*(Ps interpreter consists of a list
of breakpoint locations, and a description of what should happen at each 
breakpoint.
.P
Place the first breakpoint at the end of \f4apply_initialization()\fP in 
\f4xnews/xnews/server/initialize.c\fP.  This function is called by 
\f4runPostScript()\fP (\f4main.c\fP),
which is called by \f4main()\fP. If this section of code is completed, 
then the server has initialized its internal mechanisms properly. For the 
stripped server, the internal initialization should complete with no problems.
Later, however, it may fail when the rest of the server is introduced.
.P
Once the server completes internal initialization,
it will call \f4create_process()\fP in \f4processes.c\fP to create the
process thread which will execute the \*(Ps initialization
string, \fH(NeWS/init.ps) (r) file cvx &main\fP. 
The figure at the end of this section 
shows the operand stack as it processes the initialization string.
You can put a breakpoint right after this call
in \f4runPostScript()\fP to see if this completes properly.
.P
The process created is set up with a type of \f4file_execution\fP and
an interpreter of \f4PostScript()\fP.
The initialization string is opened by \f4psio_sopen()\fP so that \f4parse_file\fP
can handle it as a file (see above
.UI "case string_execution" ).
.P
.UI runPostScript() 
then returns to \f4main()\fP,
which calls \f4RunScheduler()\fP in \f4sched.c\fP.
After much checking around,
\f4RunScheduler()\fP will call \f4run_process()\fP also in \f4sched.c\fP.
You can put a breakpoint at \f4run_process()\fP and see if execution arrives.
.P
After some more checking, \f4run_process()\fP will call the interpreter for the
process, which in this case is \f4PostScript()\fP.
A breakpoint at \f4PostScript()\fP will show if execution has proceeded
there properly.
.P
The switch on \f4es\(hy>type\fP should cause execution to proceed to
\f4restart_parse\fP in \f4parse_file.h\fP.  By single stepping the code,
you should be able to see the parser recognize the open parenthesis
which enclose the first two strings and create the objects containing
them as described above in the discussion of \f4parse_file.h\fP.
.P
The next token is \f3file\fP.
If you put a breakpoint in the 
.UI while 
loop at the default case of the switch on
the character read (which is placed in variable \f4c\fP),
you should be able to see the parser accumulate the characters making up
"file" in 
.UI ee\(hy>static_buffer .
Note that this string is not null-terminated,
but is instead delimited by \f4ee\(hy>fill\fP,
which corresponds to the next empty character in \f4static_buffer\fP.
.SS
    default:
        .
        .
        .
        if (c > 127) {
            psio_ungetc(c, f);
        } else {
            while (1) {
                check_buffer(0);
                ee->static_buffer[ee->fill++] = c;
                pf_getc(10, c);
                if (c <= ' ')
                    break;
                if (delim[c]) {
                    psio_ungetc(c, f);
                    break;
                }
            }
        }
.SE

.P
Once the token is accumulated in 
.UI static_buffer
you should be able to follow the execution to
\f4its_an_identifier\fP.
which locates the file operator and
places a pointer to it in \f4ee\(hy>execee\fP.
.P
If you place a breakpoint at \f4open_file()\fP (\f4objects.c\fP), you
should see execution flow there.
If it doesn't, single step the code after \f4parse_file\fP, at 
.UI "case keyword_type"
and at 
.UI execute_operator 
in \f4PostScript.c\fP and in \f4operators.h\fP
which checks the type of 
.UI  ee\(hy>execee 
and switches to the code which invokes \f4open_file()\fP.
.P
The routine \f4open_file()\fP is fairly straightforward and shouldn't
pose any problems.
It tries to open the named file in the current directory
first (calling \f4abspath()\fP in 
.UI abspath.c 
to construct an absolute
path name to the current directory).
Then it tries to open the file in the user's home directory.
Finally,
it tries in \f4$XNEWSHOME/etc\fP.
This is where it should succeed for \f4NeWS/init.ps\fP if you have
set up the test environment properly (see the previous section 
on setting up the interpreter-only server). 
.P
The routine \f4open_file\fP 
calls \f4psio_open()\fP (\f4usr.lib/libcps/psio.c\fP) to 
actually open the file.
It puts an object corresponding to the open file on the operand
stack if it is successful.
.P
Once the file is open,
execution continues at the top of the main interpreter \f4while(1)\fP loop.
Since we are still dealing with a \f4file_execution\fP,
we go back into \f4parse_file.h\fP and pick up the
next token, \f3cvx\fP.
Picking up \f3cvx\fP simply sets the executable flag for the object on top
of the stack to true.  Put a breakpoint at \f4cvx_primitive\fP in
\f4operators.h\fP if necessary.
.P
The next token parsed by \f4parse_file.h\fP is 
.B "exec ."
This is handled by \f4case exec_primitive\fP in \f4operators.h\fP,   
which sets 
.UI ee\(hy>execee\fP 
to the object on top of the stack and
goes to \f4execute_top\fP in \f4PostScript.c\fP.
Put a breakpoint at 
.UI execute_top 
or at \f4case file_type\fP of the switch on \f4execee\(hy>type\fP.
.P
The code at 
.UI "case file_type"
makes a new entry on the execution stack,
sets up the interpreter field, the input stream field,
the executed field, and breaks.
.P
The break takes execution to the 
.UI continue 
statement immediately
before \f4undefinedfile_error\fP.
Single step to here, and through the 
.UI continue .
Execution should go back to the top of the main interpreter
loop (\f4while(1)\fP).
.P
The switch on \f4es\(hy>type\fP will take us back into \f4parse_file.h\fP,
but this time we'll be reading from the opened file (in this case,
\f4NeWS/init.ps\fP).
You can step through the code as the executable array is
accumulated on the stack.
The \f4def\fP primitive at the end of our sample \f4init.ps\fP will
be executed at 
.UI def_primitive 
in \f4operators.h\fP. This creates an entry in the dictionary selected.
Put a breakpoint here.
.P
The last time through \f4parse_file.h\fP,
we should hit the end\(hyof\(hyfile on \f4init.ps\fP.
This is handled by \f4case EOF\fP of the switch on
the character read (placed in variable \f4c\fP) in \f4parse_file.h\fP.
Place a breakpoint here or single step to it.
.P
The code at 
.UI "case EOF"
will clean up the input and output streams and then 
branch to the label 
.UI pop_estack 
in \f4PostScript.c\fP.
Place a breakpoint here or single step to it.
.P
The code at 
.UI pop_estack
cleans up for some types of executable objects, but not 
.UI file_execution .
Afterwards, the following test is performed to see if the process is dead:
.P
.SS
(\f4\(hy\(hyes < ee\(hy>execution_stack\fP).  
.SE
.P
The test should fail
(note that the execution stack is decremented as a side-effect,
thus popping off the execution state for \f4init.ps\fP),
and the program should proceed to \f4retry_estack\fP.
This code is immediately after the beginning of the main loop
of the interpreter (\f4while(1)\fP).
.P
Trace to \f4retry_estack\fP, and through to \f4parse_file.h\fP once again.
This time, we should be back to the initialization string,
and the interpreter should accumulate the token \f3&main\fP 
in \f4ee\(hy>static_buffer\fP.
.P
.B &main
has a type of 
.UI array_type , 
so the switch on 
.UI ee\(hy>execee.type
at \f4execute_operator\fP in \f4PostScript.c\fP will take us to
\f4case array_type\fP. Put a breakpoint here or single step.
The code at 
.UI "case array_type"
does some initialization, and then branches to 
.UI execute_array 
in \f4PostScript.c\fP, which
corresponds to \f4case array_body_execution\fP of the switch on
\f4es\(hy>type\fP.  Put a breakpoint here.
.P
The code here at 
.UI "case array_body_execution"
checks to see if there's anything 
left in the array
(\f4es\(hy>env.array.left\fP), then 
takes the next array entry and puts it in \f4ee\(hy>execee\fP.
The pointer into the array (\f4es\(hy>env.array.ip\fP) is incremented past
the object just picked up.
.P
Execution then continues with the break after the code at
\f4pop_estack\fP, right before 
.UI "case file_execution" .
This takes us out of the switch on \f4es\(hy>type\fP
to the code at \f4execute_unknown_object\fP.
.P
The program then proceeds to \f4execute_top\fP,
and since \f4ee\(hy>execee\fP is executable,
the program switches on \f4ee\(hy>execee.type\fP,
and executes the object just pulled out of the array.
After that object is executed,
the flow will return to the top of the main \f4while(1)\fP loop,
and the switch on 
.UI es\(hy>type
returns the program to 
.UI "case array_body_execution".
.P
You can put breakpoints at each operator in 
.UI operator.h 
called
by \f3&main\fP to see that execution proceeds to each of these. 
The code at 
.UI "case add_primitive"
in 
.UI operator.h
corresponds to the \*(Ps 
.B add .
The code at 
.UI "case clever_primitive"
corresponds to the \*(Ps
.B "==" .
.P
When there are no more objects in the array
to execute,
(\f4es\(hy>env.array.left == 0\fP),
the test at
.UI "case array_body_execute"
will fail and execution will fall
to the 
.UI else 
which is also the beginning of \f4pop_estack\fP.
This will pop the execution stack as described above and
execution will revert back to the initialization string.
.P
Since we have reached the end of the initialization string,
the code in \f4parse_file.h\fP
(\f4case EOF\fP of the switch on 
.UI c )
will branch to \f4pop_estack\fP.
Trace through 
.UI "case EOF"
once again to \f4pop_estack\fP.
.P
This time, when the execution stack (
.UI es ) 
is decremented,
it will be below the start of stack, so
execution will transfer to the label \f4suspend_process\fP
in \f4PostScript.c\fP.
Put a breakpoint here.
.P
This action sets \f4ee\(hy>pos\fP to \f4es\fP and returns to 
\f4run_process()\fP.
\f4run_process\fP checks to see if \f4ee\(hy>pos < ee\(hy>execution_stack\fP.

In this case it is, so it calls
\f4free_process() (processes.c)\fP.
Put a breakpoint here.
.P
\f4run_process\fP then returns to \f4RunScheduler()\fP.
\f4RunScheduler()\fP  will figure out that there's no more work to do,
and will break out of its \f4while(1)\fP loop.
That's when the server will print out
"Sic Transit Gloria \*(Ps."
Put a breakpoint here.
.P
Once you've gotten to here,
you have a working \*(Ps interpreter!
.B
Operand Stack as It Processes the Input String: (NeWS/init.ps)(r)file
.ft 1
.AL 1
.LI
Operand Stack After First String is Parsed
.PS 
boxwid = 1.125; boxht =.25
box "(News/init.ps)"
box with .n at last box.s; 
box with .n at last box.s;
.PE 
.LI
 Operand Stack after Second String is Parsed
.PS
box "(r)"
box with .n at last box.s "(News/init.ps)"
box with .n at last box.s; 
.PE
.LI
Operand Stack after Token is Parsed
.PS
box "file" ljust
box with .n at last box.s "(r)" ljust
box with .n at last box.s "(News/init.ps)"
.PE
.LI
Operand Stack after \f3file\fP Executed
.PS
box "file object"; "   (init.ps opened, executable=0)" ljust
box with .n at last box.s; 
box with .n at last box.s;
.PE
.LI
Operand Stack after \f3cvx\fP is Parsed But Not Executed
.PS
box "cvx"
box with .n at last box.s "file object"; "   (init.ps opened, executable=0)" ljust
box with .n at last box.s; 
.PE
.LI
Operand Stack after \f3cvx\fP is Executed
.PS
box "file object"; "   (init.ps opened, executable = 1)" ljust
box with .n at last box.s; "   " ljust
box with .n at last box.s; "   " ljust
.PE
.LI
Operand Stack after \f3exec\fP is Parsed, but not Executed
.PS
box "exec"
box with .n at last box.s "file object"; "   (init.ps opened, executable = 1)" ljust
box with .n at last box.s; "   " ljust
.PE
.LI
Operand Stack after \f3exec\fP of init.ps
.PS
box; "   "
box with .n at last box.s; "   " ljust
box with .n at last box.s; "   " ljust
.PE
.LI
Operand Stack after \f3&main\fP is Parsed
.PS
box "&main"; "   (executable = 1)" ljust
box with .n at last box.s; "   " ljust
box with .n at last box.s; "   " ljust
.PE
.LI
Operand Stack after Executing \f3&main\fP Initially
.PS
box; "   " ljust
box with .n at last box.s; "   " ljust
box with .n at last box.s; "   " ljust
.PE
.LI
Operand Stack While Executing \f3&main\fP (Part 1 of 2)
.PS
box "(Hello world)"
box with .n at last box.s; "   " ljust
box with .n at last box.s; "   " ljust
.PE
.LI
Operand Stack While Executing \f3&main\fP (Part 2 of 2)
.PS
box "print"
box with .n at last box.s "(Hello world)"
box with .n at last box.s; "   " ljust
.PE
.LE
After 12, the remainder of \f4init.ps\f1 is then executed:  
.SS
\fH
(2+2=) print
2 2 add =
} def\fP
.SE

