/* * Copyright (c) 2002, The Tendra Project * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * * 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. * * $TenDRA: tendra/src/tools/disp/pretty.c,v 1.10 2005/11/05 12:03:15 stefanf Exp $ */ #include "config.h" #include "types.h" #include "ascii.h" #include "file.h" #include "pretty.h" #include "tree.h" /* * FORWARD DECLARATIONS */ static void display(int); static void expression(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. */ BoolT helpflag = TRUE; BoolT progress = FALSE; BoolT quickflag = FALSE; /* * 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(void) { 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 indentation 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(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(word *ptr, int col, int first, int last, int flag, 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; }