Subversion Repositories tendra.SVN

Rev

Rev 7 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
                 Crown Copyright (c) 1997
    
    This TenDRA(r) Computer Program is subject to Copyright
    owned by the United Kingdom Secretary of State for Defence
    acting through the Defence Evaluation and Research Agency
    (DERA).  It is made available to Recipients with a
    royalty-free licence for its use, reproduction, transfer
    to other parties and amendment for any purpose not excluding
    product development provided that any such use et cetera
    shall be deemed to be acceptance of the following conditions:-
    
        (1) Its Recipients shall ensure that this Notice is
        reproduced upon any copies or amended versions of it;
    
        (2) Any amended version of it shall be clearly marked to
        show both the nature of and the organisation responsible
        for the relevant amendment or amendments;
    
        (3) Its onward transfer from a recipient to another
        party shall be deemed to be that party's acceptance of
        these conditions;
    
        (4) DERA gives no warranty or assurance as to its
        quality or suitability for any purpose and DERA accepts
        no liability whatsoever in relation to any use to which
        it may be put.
*/


#include "config.h"
#if FS_STDARG
#include <stdarg.h>
#else
#include <varargs.h>
#endif
#include <ctype.h>
#include "calculus.h"
#include "common.h"
#include "error.h"
#include "lex.h"
#include "output.h"
#include "suffix.h"
#include "type_ops.h"


/*
    FIND BINARY LOG OF A NUMBER

    This routine calculates the binary log of n (i.e. the smallest number
    r such that n <= 2**r).
*/

number log2
    PROTO_N ( ( n ) )
    PROTO_T ( number n )
{
    number r ;
    number m ;
    for ( r = 0, m = 1 ; n > m && m ; r++, m *= 2 ) /* empty */ ;
    return ( r ) ;
}


/*
    LOOP VARIABLES

    These are the counter variables used in the LOOP macros defined in
    output.h.
*/

LIST ( ECONST_P ) crt_ec = NULL_list ( ECONST_P ) ;
LIST ( ENUM_P ) crt_en = NULL_list ( ENUM_P ) ;
LIST ( IDENTITY_P ) crt_id = NULL_list ( IDENTITY_P ) ;
LIST ( PRIMITIVE_P ) crt_prim = NULL_list ( PRIMITIVE_P ) ;
LIST ( STRUCTURE_P ) crt_str = NULL_list ( STRUCTURE_P ) ;
LIST ( UNION_P ) crt_union = NULL_list ( UNION_P ) ;
LIST ( COMPONENT_P ) crt_cmp = NULL_list ( COMPONENT_P ) ;
LIST ( FIELD_P ) crt_fld = NULL_list ( FIELD_P ) ;
LIST ( MAP_P ) crt_map = NULL_list ( MAP_P ) ;
LIST ( ARGUMENT_P ) crt_arg = NULL_list ( ARGUMENT_P ) ;
LIST ( TYPE_P ) crt_type = NULL_list ( TYPE_P ) ;
int unique = 0 ;


/*
    CURRENT OUTPUT FILE

    This gives the file which is currently being used for output.
*/

FILE *output_file = NULL ;
static int output_posn = 0 ;
static char output_buff [256] ;
static FILE *output_file_old = NULL ;
static int column = 0 ;
int verbose_output = 1 ;
int const_tokens = 1 ;
int have_varargs = 1 ;


/*
    PRINT A CHARACTER

    This routine prints the single character c.
*/

static void output_char
    PROTO_N ( ( c ) )
    PROTO_T ( int c )
{
    int i = output_posn ;
    output_buff [i] = ( char ) c ;
    if ( ++i >= 250 || c == '\n' ) {
        output_buff [i] = 0 ;
        IGNORE fputs ( output_buff, output_file ) ;
        i = 0 ;
    }
    if ( c == '\n' ) {
        column = 0 ;
    } else if ( c == '\t' ) {
        column = 8 * ( ( column + 8 ) / 8 ) ;
    } else {
        column++ ;
    }
    output_posn = i ;
    return ;
}


/*
    PRINT A STRING

    This routine prints the string s.
*/

static void output_string
    PROTO_N ( ( s ) )
    PROTO_T ( CONST char *s )
{
    for ( ; *s ; s++ ) output_char ( *s ) ;
    return ;
}


/*
    FLUSH OUTPUT FILE

    This routine flushes the output file buffer by printing a newline
    character.
*/

void flush_output
    PROTO_Z ()
{
    if ( output_posn ) output_char ( '\n' ) ;
    return ;
}


/*
    PRINT A TYPE

    This routine prints the type t.
*/

void output_type
    PROTO_N ( ( t ) )
    PROTO_T ( TYPE_P t )
{
    TYPE t0 = DEREF_type ( t ) ;
    switch ( TAG_type ( t0 ) ) {
        case type_vec_tag : {
            TYPE_P_P s = type_vec_sub ( t0 ) ;
            output_string ( "VEC ( " ) ;
            output_type ( DEREF_ptr ( s ) ) ;
            output_string ( " )" ) ;
            break ;
        }
        case type_ptr_tag : {
            TYPE_P_P s = type_ptr_sub ( t0 ) ;
            output_string ( "PTR ( " ) ;
            output_type ( DEREF_ptr ( s ) ) ;
            output_string ( " )" ) ;
            break ;
        }
        case type_list_tag : {
            TYPE_P_P s = type_list_sub ( t0 ) ;
            output_string ( "LIST ( " ) ;
            output_type ( DEREF_ptr ( s ) ) ;
            output_string ( " )" ) ;
            break ;
        }
        case type_stack_tag : {
            TYPE_P_P s = type_stack_sub ( t0 ) ;
            output_string ( "STACK ( " ) ;
            output_type ( DEREF_ptr ( s ) ) ;
            output_string ( " )" ) ;
            break ;
        }
        case type_vec_ptr_tag : {
            TYPE_P_P s = type_vec_ptr_sub ( t0 ) ;
            output_string ( "VEC_PTR ( " ) ;
            output_type ( DEREF_ptr ( s ) ) ;
            output_string ( " )" ) ;
            break ;
        }
        default : {
            output_string ( name_type ( t ) ) ;
            break ;
        }
    }
    return ;
}


/*
    PRINT A TYPE IDENTIFIER

    This routine prints an identifier derived from the type t.  depth
    determines the depth to which identities are to be expanded.
*/

static void output_type_id
    PROTO_N ( ( t, depth ) )
    PROTO_T ( TYPE_P t X int depth )
{
    TYPE t0 = DEREF_type ( t ) ;
    switch ( TAG_type ( t0 ) ) {
        case type_vec_tag : {
            TYPE_P_P s = type_vec_sub ( t0 ) ;
            output_string ( "vec_" ) ;
            output_type_id ( DEREF_ptr ( s ), depth ) ;
            break ;
        }
        case type_ptr_tag : {
            TYPE_P_P s = type_ptr_sub ( t0 ) ;
            output_string ( "ptr_" ) ;
            output_type_id ( DEREF_ptr ( s ), depth ) ;
            break ;
        }
        case type_list_tag : {
            TYPE_P_P s = type_list_sub ( t0 ) ;
            output_string ( "list_" ) ;
            output_type_id ( DEREF_ptr ( s ), depth ) ;
            break ;
        }
        case type_stack_tag : {
            TYPE_P_P s = type_stack_sub ( t0 ) ;
            output_string ( "stack_" ) ;
            output_type_id ( DEREF_ptr ( s ), depth ) ;
            break ;
        }
        case type_vec_ptr_tag : {
            TYPE_P_P s = type_vec_ptr_sub ( t0 ) ;
            output_string ( "vptr_" ) ;
            output_type_id ( DEREF_ptr ( s ), depth ) ;
            break ;
        }
        case type_ident_tag : {
            IDENTITY_P id = DEREF_ptr ( type_ident_id ( t0 ) ) ;
            if ( depth ) {
                TYPE_P_P s = ident_defn ( id ) ;
                output_type_id ( DEREF_ptr ( s ), depth - 1 ) ;
            } else {
                CLASS_ID_P nm = DEREF_ptr ( ident_id ( id ) ) ;
                output_string ( DEREF_string ( cid_name ( nm ) ) ) ;
            }
            break ;
        }
        default : {
            output_string ( name_aux_type ( t ) ) ;
            break ;
        }
    }
    return ;
}


/*
    PRINT A TYPE SIZE

    This routine print the size of the type t.
*/

static void output_type_size
    PROTO_N ( ( t ) )
    PROTO_T ( TYPE_P t )
{
    TYPE t0 = DEREF_type ( t ) ;
    switch ( TAG_type ( t0 ) ) {
        case type_vec_tag : {
            TYPE_P_P s = type_vec_sub ( t0 ) ;
            output ( "SIZE_vec ( %TT )", DEREF_ptr ( s ) ) ;
            break ;
        }
        case type_ptr_tag : {
            TYPE_P_P s = type_ptr_sub ( t0 ) ;
            output ( "SIZE_ptr ( %TT )", DEREF_ptr ( s ) ) ;
            break ;
        }
        case type_list_tag : {
            TYPE_P_P s = type_list_sub ( t0 ) ;
            output ( "SIZE_list ( %TT )", DEREF_ptr ( s ) ) ;
            break ;
        }
        case type_stack_tag : {
            TYPE_P_P s = type_stack_sub ( t0 ) ;
            output ( "SIZE_stack ( %TT )", DEREF_ptr ( s ) ) ;
            break ;
        }
        case type_vec_ptr_tag : {
            TYPE_P_P s = type_vec_ptr_sub ( t0 ) ;
            output ( "SIZE_vec_ptr ( %TT )", DEREF_ptr ( s ) ) ;
            break ;
        }
        case type_ident_tag : {
            IDENTITY_P id = DEREF_ptr ( type_ident_id ( t0 ) ) ;
            output_type_size ( DEREF_ptr ( ident_defn ( id ) ) ) ;
            break ;
        }
        default : {
            output_string ( "SIZE_" ) ;
            output_string ( name_aux_type ( t ) ) ;
            break ;
        }
    }
    return ;
}


/*
    PRINT A FORMAT STRING

    This routine prints the string s, taking any formatting characters
    into account.  These formatting characters have the form %X or %XY
    for characters X and Y.  Each is commented within the body of the
    procedure in the form "%XY -> ....".
*/

void output
    PROTO_V ( ( char *s, ... ) )
    /*VARARGS*/
{
    char c ;
    va_list args ;
    char nbuff [100] ;

#if FS_STDARG
    va_start ( args, s ) ;
#else
    char *s ;
    va_start ( args ) ;
    s = va_arg ( args, char * ) ;
#endif

    while ( c = *( s++ ), c != 0 ) {
        if ( c == '%' ) {
            char *s0 = s ;
            c = *( s++ ) ;
            switch ( c ) {

                case 'A' : {
                    /* Arguments */
                    c = *( s++ ) ;
                    if ( c == 'N' ) {
                        /* %AN -> argument name */
                        if ( HAVE_ARGUMENT ) {
                            string_P ps = arg_name ( CRT_ARGUMENT ) ;
                            output_string ( DEREF_string ( ps ) ) ;
                        } else {
                            goto misplaced_arg ;
                        }
                    } else if ( c == 'T' ) {
                        /* %AT -> argument type */
                        if ( HAVE_ARGUMENT ) {
                            TYPE_P_P pt = arg_type ( CRT_ARGUMENT ) ;
                            output_type ( DEREF_ptr ( pt ) ) ;
                        } else {
                            goto misplaced_arg ;
                        }
                    } else {
                        goto bad_format ;
                    }
                    break ;
                }

                case 'C' : {
                    /* Components */
                    c = *( s++ ) ;
                    if ( c == 'N' ) {
                        /* %CN -> component name */
                        if ( HAVE_COMPONENT ) {
                            string_P ps = cmp_name ( CRT_COMPONENT ) ;
                            output_string ( DEREF_string ( ps ) ) ;
                        } else {
                            goto misplaced_arg ;
                        }
                    } else if ( c == 'T' ) {
                        /* %CT -> component type */
                        if ( HAVE_COMPONENT ) {
                            TYPE_P_P pt = cmp_type ( CRT_COMPONENT ) ;
                            output_type ( DEREF_ptr ( pt ) ) ;
                        } else {
                            goto misplaced_arg ;
                        }
                    } else if ( c == 'U' ) {
                        /* %CU -> short component type */
                        if ( HAVE_COMPONENT ) {
                            TYPE_P_P pt = cmp_type ( CRT_COMPONENT ) ;
                            TYPE_P ta = DEREF_ptr ( pt ) ;
                            char *tn = name_aux_type ( ta ) ;
                            output_string ( tn ) ;
                        } else {
                            goto misplaced_arg ;
                        }
                    } else if ( c == 'V' ) {
                        /* %CV -> component default value */
                        if ( HAVE_COMPONENT ) {
                            string_P ps = cmp_name ( CRT_COMPONENT ) ;
                            string s1 = DEREF_string ( ps ) ;
                            if ( s1 ) output_string ( s1 ) ;
                        } else {
                            goto misplaced_arg ;
                        }
                    } else {
                        goto bad_format ;
                    }
                    break ;
                }

                case 'E' : {
                    /* Enumerations */
                    c = *( s++ ) ;
                    if ( c == 'N' ) {
                        /* %EN -> enumeration name */
                        if ( HAVE_ENUM ) {
                            CLASS_ID_P_P pi = en_id ( CRT_ENUM ) ;
                            string_P ps = cid_name ( DEREF_ptr ( pi ) ) ;
                            output_string ( DEREF_string ( ps ) ) ;
                        } else {
                            goto misplaced_arg ;
                        }
                    } else if ( c == 'M' ) {
                        /* %EM -> short enumeration name */
                        if ( HAVE_ENUM ) {
                            CLASS_ID_P_P pi = en_id ( CRT_ENUM ) ;
                            string_P ps = cid_name_aux ( DEREF_ptr ( pi ) ) ;
                            output_string ( DEREF_string ( ps ) ) ;
                        } else {
                            goto misplaced_arg ;
                        }
                    } else if ( c == 'O' ) {
                        /* %EO -> enumeration order */
                        if ( HAVE_ENUM ) {
                            number_P pn = en_order ( CRT_ENUM ) ;
                            number n = DEREF_number ( pn ) ;
                            if ( *s == '2' ) {
                                n = log2 ( n ) ;
                                s++ ;
                            }
                            sprintf_v ( nbuff, "%lu", n ) ;
                            output_string ( nbuff ) ;
                        } else {
                            goto misplaced_arg ;
                        }
                    } else if ( c == 'S' ) {
                        /* %ES -> enumerator name */
                        if ( HAVE_ECONST ) {
                            string_P ps = ec_name ( CRT_ECONST ) ;
                            output_string ( DEREF_string ( ps ) ) ;
                        } else {
                            goto misplaced_arg ;
                        }
                    } else if ( c == 'V' ) {
                        /* %EV -> enumerator value */
                        if ( HAVE_ECONST ) {
                            number_P pn = ec_value ( CRT_ECONST ) ;
                            number n = DEREF_number ( pn ) ;
                            sprintf_v ( nbuff, "%lu", n ) ;
                            output_string ( nbuff ) ;
                        } else {
                            goto misplaced_arg ;
                        }
                    } else {
                        goto bad_format ;
                    }
                    break ;
                }

                case 'F' : {
                    /* Fields */
                    c = *( s++ ) ;
                    if ( c == 'N' ) {
                        /* %FN -> field name */
                        if ( HAVE_FIELD ) {
                            string_P ps = fld_name ( CRT_FIELD ) ;
                            output_string ( DEREF_string ( ps ) ) ;
                        } else {
                            goto misplaced_arg ;
                        }
                    } else if ( c == ',' ) {
                        /* %F, -> ',' (if not the last field) */
                        if ( HAVE_FIELD ) {
                            LIST ( FIELD_P ) nf = TAIL_list ( crt_fld ) ;
                            if ( !IS_NULL_list ( nf ) ) output_string ( "," ) ;
                        } else {
                            goto misplaced_arg ;
                        }
                    } else {
                        goto bad_format ;
                    }
                    break ;
                }

                case 'I' : {
                    /* Identities */
                    c = *( s++ ) ;
                    if ( c == 'N' ) {
                        /* %IN -> identity name */
                        if ( HAVE_IDENTITY ) {
                            CLASS_ID_P_P pi = ident_id ( CRT_IDENTITY ) ;
                            string_P ps = cid_name ( DEREF_ptr ( pi ) ) ;
                            output_string ( DEREF_string ( ps ) ) ;
                        } else {
                            goto misplaced_arg ;
                        }
                    } else if ( c == 'M' ) {
                        /* %IM -> short identity name */
                        if ( HAVE_IDENTITY ) {
                            CLASS_ID_P_P pi = ident_id ( CRT_IDENTITY ) ;
                            string_P ps = cid_name_aux ( DEREF_ptr ( pi ) ) ;
                            output_string ( DEREF_string ( ps ) ) ;
                        } else {
                            goto misplaced_arg ;
                        }
                    } else if ( c == 'T' ) {
                        /* %IT -> identity type definition */
                        if ( HAVE_IDENTITY ) {
                            TYPE_P_P pt = ident_defn ( CRT_IDENTITY ) ;
                            output_type ( DEREF_ptr ( pt ) ) ;
                        } else {
                            goto misplaced_arg ;
                        }
                    } else {
                        goto bad_format ;
                    }
                    break ;
                }

                case 'M' : {
                    /* Maps */
                    c = *( s++ ) ;
                    if ( c == 'N' ) {
                        /* %MN -> map name */
                        if ( HAVE_MAP ) {
                            string_P ps = map_name ( CRT_MAP ) ;
                            output_string ( DEREF_string ( ps ) ) ;
                        } else {
                            goto misplaced_arg ;
                        }
                    } else if ( c == 'R' ) {
                        /* %MR -> map return type */
                        if ( HAVE_MAP ) {
                            TYPE_P_P pt = map_ret_type ( CRT_MAP ) ;
                            output_type ( DEREF_ptr ( pt ) ) ;
                        } else {
                            goto misplaced_arg ;
                        }
                    } else {
                        goto bad_format ;
                    }
                    break ;
                }

                case 'P' : {
                    /* Primitives */
                    c = *( s++ ) ;
                    if ( c == 'N' ) {
                        /* %PN -> primitive name */
                        if ( HAVE_PRIMITIVE ) {
                            CLASS_ID_P_P pi = prim_id ( CRT_PRIMITIVE ) ;
                            string_P ps = cid_name ( DEREF_ptr ( pi ) ) ;
                            output_string ( DEREF_string ( ps ) ) ;
                        } else {
                            goto misplaced_arg ;
                        }
                    } else if ( c == 'M' ) {
                        /* %PM -> short primitive name */
                        if ( HAVE_PRIMITIVE ) {
                            CLASS_ID_P_P pi = prim_id ( CRT_PRIMITIVE ) ;
                            string_P ps = cid_name_aux ( DEREF_ptr ( pi ) ) ;
                            output_string ( DEREF_string ( ps ) ) ;
                        } else {
                            goto misplaced_arg ;
                        }
                    } else if ( c == 'D' ) {
                        /* %PD -> primitive definition */
                        if ( HAVE_PRIMITIVE ) {
                            string_P ps = prim_defn ( CRT_PRIMITIVE ) ;
                            output_string ( DEREF_string ( ps ) ) ;
                        } else {
                            goto misplaced_arg ;
                        }
                    } else {
                        goto bad_format ;
                    }
                    break ;
                }

                case 'S' : {
                    /* Structures */
                    c = *( s++ ) ;
                    if ( c == 'N' ) {
                        /* %SN -> structure name */
                        if ( HAVE_STRUCTURE ) {
                            CLASS_ID_P_P pi = str_id ( CRT_STRUCTURE ) ;
                            string_P ps = cid_name ( DEREF_ptr ( pi ) ) ;
                            output_string ( DEREF_string ( ps ) ) ;
                        } else {
                            goto misplaced_arg ;
                        }
                    } else if ( c == 'M' ) {
                        /* %SM -> short structure name */
                        if ( HAVE_STRUCTURE ) {
                            CLASS_ID_P_P pi = str_id ( CRT_STRUCTURE ) ;
                            string_P ps = cid_name_aux ( DEREF_ptr ( pi ) ) ;
                            output_string ( DEREF_string ( ps ) ) ;
                        } else {
                            goto misplaced_arg ;
                        }
                    } else {
                        goto bad_format ;
                    }
                    break ;
                }

                case 'T' : {
                    /* Types */
                    c = *( s++ ) ;
                    if ( have_varargs ) {
                        TYPE_P ta = va_arg ( args, TYPE_P ) ;
                        if ( c == 'N' ) {
                            /* %TN -> type name */
                            char *tn = name_type ( ta ) ;
                            output_string ( tn ) ;
                        } else if ( c == 'M' ) {
                            /* %TM -> short type name */
                            char *tn = name_aux_type ( ta ) ;
                            output_string ( tn ) ;
                        } else if ( c == 'I' ) {
                            /* %TI -> type identifier */
                            output_type_id ( ta, 0 ) ;
                        } else if ( c == 'J' ) {
                            /* %TJ -> type identifier */
                            output_type_id ( ta, 1 ) ;
                        } else if ( c == 'S' ) {
                            /* %TS -> type size */
                            output_type_size ( ta ) ;
                        } else if ( c == 'T' ) {
                            /* %TT -> type definition */
                            output_type ( ta ) ;
                        } else {
                            goto bad_format ;
                        }
                        break ;
                    }
                    goto bad_format ;
                }

                case 'U' : {
                    /* Unions */
                    c = *( s++ ) ;
                    if ( c == 'N' ) {
                        /* %UN -> union name */
                        if ( HAVE_UNION ) {
                            CLASS_ID_P_P pi = un_id ( CRT_UNION ) ;
                            string_P ps = cid_name ( DEREF_ptr ( pi ) ) ;
                            output_string ( DEREF_string ( ps ) ) ;
                        } else {
                            goto misplaced_arg ;
                        }
                    } else if ( c == 'M' ) {
                        /* %UM -> short union name */
                        if ( HAVE_UNION ) {
                            CLASS_ID_P_P pi = un_id ( CRT_UNION ) ;
                            string_P ps = cid_name_aux ( DEREF_ptr ( pi ) ) ;
                            output_string ( DEREF_string ( ps ) ) ;
                        } else {
                            goto misplaced_arg ;
                        }
                    } else if ( c == 'O' ) {
                        /* %UO -> union order */
                        if ( HAVE_UNION ) {
                            int_P pi = un_no_fields ( CRT_UNION ) ;
                            number n = ( number ) DEREF_int ( pi ) ;
                            c = *s ;
                            if ( c == '2' ) {
                                n = log2 ( n ) ;
                                s++ ;
                            } else if ( c == '3' ) {
                                n = log2 ( n + 1 ) ;
                                s++ ;
                            }
                            sprintf_v ( nbuff, "%lu", n ) ;
                            output_string ( nbuff ) ;
                        } else {
                            goto misplaced_arg ;
                        }
                    } else {
                        goto bad_format ;
                    }
                    break ;
                }

                case 'V' : {
                    /* %V -> overall version */
                    int v1 = algebra->major_no ;
                    int v2 = algebra->minor_no ;
                    sprintf_v ( nbuff, "%d.%d", v1, v2 ) ;
                    output_string ( nbuff ) ;
                    break ;
                }

                case 'X' : {
                    /* %X -> overall name */
                    output_string ( algebra->name ) ;
                    break ;
                }

                case 'Z' : {
                    c = *( s++ ) ;
                    if ( c == 'V' ) {
                        /* %ZV -> program version */
                        output_string ( progvers ) ;
                    } else if ( c == 'X' ) {
                        /* %ZX -> program name */
                        output_string ( progname ) ;
                    } else {
                        goto bad_format ;
                    }
                    break ;
                }

                case 'b' : {
                    /* %b -> backspace */
                    if ( output_posn ) output_posn-- ;
                    break ;
                }

                case 'd' : {
                    /* %d -> integer (extra argument) */
                    if ( have_varargs ) {
                        int da = va_arg ( args, int ) ;
                        sprintf_v ( nbuff, "%d", da ) ;
                        output_string ( nbuff ) ;
                        break ;
                    }
                    goto bad_format ;
                }

                case 'e' : {
                    /* %e -> evaluated string (extra argument) */
                    if ( have_varargs ) {
                        char *ea = va_arg ( args, char * ) ;
                        if ( ea ) output ( ea ) ;
                        break ;
                    }
                    goto bad_format ;
                }

                case 'n' : {
                    /* %n -> number (extra argument) */
                    if ( have_varargs ) {
                        number na = va_arg ( args, number ) ;
                        sprintf_v ( nbuff, "%lu", na ) ;
                        output_string ( nbuff ) ;
                        break ;
                    }
                    goto bad_format ;
                }

                case 'p' : {
                    /* Pragmas */
                    c = *( s++ ) ;
                    if ( c == 't' ) {
                        /* %pt -> '#pragma token' */
                        output_string ( "#pragma token" ) ;
                    } else if ( c == 'i' ) {
                        /* %pi -> '#pragma interface' */
                        output_string ( "#pragma interface" ) ;
                    } else {
                        goto bad_format ;
                    }
                    break ;
                }

                case 's' : {
                    /* %s -> string (extra argument) */
                    if ( have_varargs ) {
                        char *sa = va_arg ( args, char * ) ;
                        if ( sa ) output_string ( sa ) ;
                        break ;
                    }
                    goto bad_format ;
                }

                case 't' : {
                    /* %t[0-9]* -> tab */
                    int t = 0 ;
                    while ( c = *s, ( c >= '0' && c <= '9' ) ) {
                        t = 10 * t + ( c - '0' ) ;
                        s++ ;
                    }
                    while ( column < t ) output_char ( '\t' ) ;
                    break ;
                }

                case 'u' : {
                    /* %u -> unique */
                    sprintf_v ( nbuff, "%d", unique ) ;
                    output_string ( nbuff ) ;
                    break ;
                }

                case 'x' : {
                    /* Expression tokens */
                    c = *( s++ ) ;
                    if ( c == 'r' ) {
                        /* %xr -> 'EXP rvalue' */
                        output_string ( "EXP" ) ;
                    } else if ( c == 'l' ) {
                        /* %xl -> 'EXP lvalue' */
                        output_string ( "EXP lvalue" ) ;
                    } else if ( c == 'c' ) {
                        /* %xc -> 'EXP const' */
                        output_string ( "EXP" ) ;
                        if ( const_tokens ) output_string ( " const" ) ;
                    } else {
                        goto bad_format ;
                    }
                    break ;
                }

                case '0' : {
                    /* %0 -> x<unique>_ */
                    sprintf_v ( nbuff, "x%d_", unique ) ;
                    output_string ( nbuff ) ;
                    break ;
                }

                case '%' : {
                    /* %% -> '%' */
                    output_string ( "%" ) ;
                    break ;
                }

                case '@' : {
                    /* %@ -> '@' */
                    output_string ( "@" ) ;
                    break ;
                }

                case '\n' : {
                    /* %\n -> ignored newline */
                    break ;
                }

                misplaced_arg : {
                    error ( ERROR_SERIOUS,
                            "Misplaced formatting string '%%%.2s'", s0 ) ;
                    break ;
                }

                default :
                bad_format : {
                    error ( ERROR_SERIOUS,
                            "Unknown formatting string '%%%.2s'", s0 ) ;
                    s = s0 ;
                    break ;
                }
            }
        } else {
            output_char ( c ) ;
        }
    }
    va_end ( args ) ;
    return ;
}


/*
    PRINT INITIAL COMMENT

    This comment is printed at the start of each output file to indicate
    that it is automatically generated.
*/

static void print_comment
    PROTO_Z ()
{
    if ( first_comment ) {
        /* Print copyright comment, if present */
        output ( "%s\n\n", first_comment ) ;
    }
    output ( "/*\n" ) ;
    output ( "    AUTOMATICALLY GENERATED FROM ALGEBRA %X (VERSION %V)\n" ) ;
    output ( "    BY %ZX (VERSION %ZV)\n" ) ;
    output ( "*/\n\n" ) ;
    return ;
}


/*
    C CODE FLAG

    This flag is true if C code is being output.
*/

int output_c_code = 1 ;


/*
    OPEN AN OUTPUT FILE

    This routine opens the output file formed by concatenating nm and suff.
    Two files can be open at once.
*/

void open_file
    PROTO_N ( ( dir, nm, suff ) )
    PROTO_T ( char *dir X char *nm X char *suff )
{
    char *p ;
    char buff [1000] ;
    flush_output () ;
    sprintf_v ( buff, "%s/%s%s", dir, nm, suff ) ;
    output_file_old = output_file ;
    output_file = fopen ( buff, "w" ) ;
    if ( output_file == NULL ) {
        error ( ERROR_FATAL, "Can't open output file, %s", buff ) ;
    }
    if ( verbose_output ) {
        fprintf_v ( stderr, "Creating %s ...\n", buff ) ;
    }
    column = 0 ;

    if ( output_c_code ) {
        /* Set up protection macro */
        char *tok = "" ;
        if ( output_c_code == 2 ) tok = "_TOK" ;
        sprintf_v ( buff, "%s%s%s_INCLUDED", nm, suff, tok ) ;
        for ( p = buff ; *p ; p++ ) {
            char c = *p ;
            if ( isalpha ( c ) ) {
                if ( islower ( c ) ) c = ( char ) toupper ( c ) ;
            } else if ( !isdigit ( c ) ) {
                c = '_' ;
            }
            *p = c ;
        }

        /* Print file header */
        print_comment () ;
        output ( "#ifndef %s\n", buff ) ;
        output ( "#define %s\n\n", buff ) ;
    }
    return ;
}


/*
    CLOSE AN OUTPUT FILE

    This routine closes the current output file.
*/

void close_file
    PROTO_Z ()
{
    if ( output_c_code ) output ( "#endif\n" ) ;
    flush_output () ;
    fclose_v ( output_file ) ;
    output_file = output_file_old ;
    output_file_old = NULL ;
    output_c_code = 1 ;
    return ;
}