Subversion Repositories tendra.SVN

Rev

Rev 2 | 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"
#include "types.h"
#include "ascii.h"
#include "file.h"
#include "tree.h"


/*
    FORWARD DECLARATIONS
*/

static void display PROTO_S ( ( int ) ) ;
static void expression PROTO_S ( ( word *, int, int, int, int, int ) ) ;


/*
    FLAGS SET BY EXTERNAL OPTIONS

    Various flags may be given on the command line with affect the form
    of the output.  These are documented elsewhere.
*/

int helpflag = 1 ;
int dflag = 1 ;
int progress = 0 ;
int quickflag = 0 ;


/*
    VARIABLES USED IN PRINTING ROUTINE

    The variable maxcol is used to specify the maximum number of columns
    into which the output should be fitted.  maxtab gives the current
    indentation.  dot_spacing is used to indicate the distance between
    the vertical alignment lines when helpflag is true.
*/

int maxcol = 80 ;
static int maxtab ;
static int dot_spacing ;


/*
    CHOICE TABLE

    This table is used to decide the maximum suitable indentation.  For
    example the entry { a, b, c } means, try printing with an indentation
    of a.  If this fits into the required number of columns, try with
    the (larger) indentation c.  Otherwise try with the (smaller)
    indentation b.
*/

#define M 100

static int dec [7][3] = {
    { 4, 2, 8 },
    { 2, 1, 3 },
    { 8, 6, M },
    { 1, 0, 1 },
    { 3, 2, 3 },
    { 6, 4, 6 },
    { M, 8, M }
} ;


/*
    PRINT THE TDF TREE

    This routine has overall control of the printing of the tree.  It
    determines the most suitable indentation by means of trial runs,
    and then prints the tree with that indentation.
*/

void pretty_tree
    PROTO_Z ()
{
    int i, j ;
    int maximum0 ;

    initialize_tree () ;

    /* Do some trial runs to try to fit into right number of columns... */
    printflag = 0 ;

    /* With a maximum identation of 0... */
    maxtab = 0 ;
    display ( 0 ) ;
    maximum0 = maximum ;

    /* Try some larger/smaller values */
    if ( !quickflag && maximum < maxcol ) {
        maxtab = 4 ;
        for ( i = 0 ; i < 3 ; i++ ) {
            display ( 1 ) ;
            j = -1 ;
            while ( dec[++j][0] != maxtab ) ;
            maxtab = dec[j][ maximum < maxcol ? 2 : 1 ] ;
        }
    }

    /* Work out dot spacing */
    dot_spacing = 8 ;
    if ( maxtab != 100 ) dot_spacing = 2 * ( maxtab + 1 ) ;
    if ( maxtab == 0 ) {
        if ( maximum0 > maxcol ) maxcol = maximum0 ;
        dot_spacing = 4 ;
    }
    init_spaces ( dot_spacing ) ;

    /* Actually do the printing */
    printflag = 1 ;
    display ( 0 ) ;
    if ( progress ) {
        IGNORE fprintf ( stderr, "Printed in %d columns.\n", maximum ) ;
    }
    return ;
}


/*
    TRY TO PRETTY-PRINT THE TDF TREE

    This routine actually outputs the tree.  If test is true, then this
    is only a trial run, and may be aborted if the output exceeds the
    required number of columns.
*/

static void display
    PROTO_N ( ( test ) )
    PROTO_T ( int test )
{
    word *ptr ;
    maximum = 0 ;
    for ( ptr = word1.bro ; ptr ; ptr = ptr->bro ) {
        column = 0 ;
        expression ( ptr, 0, 0, 0, 1, 0 ) ;
        if ( test && ( maximum > maxcol ) ) return ;
    }
    return ;
}


/*
    OUTPUT A CHARACTER

    This routine outputs a single character into the output file.
*/

#define put_out( c )                                            \
    {                                                           \
        if ( c ) {                                              \
            if ( printflag ) IGNORE fputc ( ( c ), pp_file ) ;  \
            column++ ;                                          \
            lastc = ( c ) ;                                     \
        }                                                       \
    }


/*
    OUTPUT A NEWLINE

    This routine outputs a newline character into the output file.
    It also causes column to be compared against maximum.
*/

#define new_line()                                              \
    {                                                           \
        if ( column > maximum ) maximum = column ;              \
        if ( printflag ) IGNORE fputc ( NEWLINE, pp_file ) ;    \
        column = 0 ;                                            \
        lastc = NEWLINE ;                                       \
    }


/*
    OUTPUT A TDF EXPRESSION

    This routine is called recursively to print the tree.  ptr gives
    the current position within the tree.  col gives the column
    where printing should start.  flag is true to indicate that a
    newline should be output at the end.  pending gives the number
    of trailing close brackets which are pending.  first gives a
    character which needs to be output before the current
    expression, and last gives one which should be output after it.
*/

static void expression
    PROTO_N ( ( ptr, col, first, last, flag, pending ) )
    PROTO_T ( word *ptr X int col X int first X int last X int flag X int pending )
{
    word *p ;
    char bar = '|' ;
    char open = '(' ;
    char close = ')' ;
    char comma = ',';
    int more, pends ;
    char sort, opener, sep ;
    int len, visible, horiz ;
    int mflag, new_col, temp_col, temp_max ;

    /* print initial spaces */
    if ( column == 0 ) spaces ( col ) ;

    /* output first character */
    put_out ( first ) ;

    /* copy out initial text */
    col = column ;
    len = ( int ) ptr->length ;
    sort = ptr->type ;
    if ( printflag ) IGNORE fputs ( ptr->text, pp_file ) ;
    column += len ;

    /* if we have parameters, we need to decode them */
    if ( sort != SIMPLE ) {

        /* are the brackets visible or not? */
        visible = ( ( sort == HORIZ_NONE || sort == VERT_NONE ) ? 0 : 1 ) ;
        if ( !visible ) {
            open = close = bar = 0 ;
            comma = ' ' ;
        }

        /* are we printing horizontally or vertically? */
        horiz = ( sort == HORIZ_BRACKETS || sort == HORIZ_NONE ) ;

        /* does the maximum tab come into effect? */
        mflag = ( !horiz && ( len > maxtab ) ) ;
        new_col = col + ( mflag ? maxtab : len ) + 1 ;

        if ( sort == VERT_BRACKETS ) {
            /* try to print things with only one parameter horizontally */
            p = ptr->son ;
            if ( p == null ) {
                horiz = 1 ;
            } else {
                if ( p->son == null && p->bro == null ) horiz = 1 ;
            }
        }

        /* have a test run, if printing horizontally */
        if ( horiz && printflag ) {
            horiz = 1 ;
            /* save old values */
            temp_col = column ;
            temp_max = maximum ;
            printflag = 0 ;
            maximum = column ;
            /* open bracket */
            put_out ( open ) ;
            sep = comma ;
            pends = 0 ;
            /* print parameters */
            for ( p = ptr->son ; p ; p = p->bro ) {
                if ( p->bro == null ) {
                    /* for last, take trailing brackets into account */
                    sep = close ;
                    pends = pending + visible ;
                }
                /* print this parameter */
                expression ( p, column, 0, sep, 0, pends ) ;
            }
            /* close bracket */
            put_out ( last ) ;
            /* if this doesn't fit in, try vertically */
            if ( maximum >= maxcol || column + pending >= maxcol ) {
                horiz = 0 ;
                mflag = ( len > maxtab ) ;
                new_col = col + ( mflag ? maxtab : len ) + 1 ;
            }
            /* restore old values */
            column = temp_col ;
            maximum = temp_max ;
            printflag = 1 ;
        }

        if ( horiz && printflag ) {
            /* do horizontal printing of parameters */
            /* open bracket */
            put_out ( open ) ;
            sep = comma ;
            pends = 0 ;
            /* print parameters */
            for ( p = ptr->son ; p ; p = p->bro ) {
                /* for last, take trailing brackets into account */
                if ( p->bro == null ) {
                    sep = close ;
                    pends = pending + visible ;
                }
                /* print this parameter */
                expression ( p, column, 0, sep, 0, pends ) ;
            }
        } else {
            /* do vertical printing of parameters */
            put_out ( open ) ;
            opener = bar ;
            if ( mflag ) {
                new_line () ;
                spaces ( new_col - 1 ) ;
            }
            sep = comma ;
            more = 1 ;
            pends = 0 ;
            new_col = column ;
            if ( !mflag ) {
                new_col-- ;
                opener = 0 ;
            }
            /* print parameters */
            for ( p = ptr->son ; p ; p = p->bro ) {
                if ( p->bro == null ) {
                    /* last requires special treatment */
                    sep = 0 ;
                    more = 0 ;
                    pends = pending + visible ;
                }
                /* print this parameter */
                expression ( p, new_col, opener, sep, more, pends ) ;
                opener = bar ;
            }
            if ( visible ) put_out ( close ) ;
        }
    }

    /* output last character */
    if ( last != ',' || ( lastc != '.' && lastc != ':' ) ) put_out ( last ) ;

    /* and a newline if required */
    if ( flag ) new_line () ;
    return ;
}