/*
 *      blk     - Automatic Requester formatter and generator.
 *
 * Reads a requester description and formats graphically then generates the
 * appropriate C-code to specify the requester.  See the examples and the
 * documentaion for using the program.
 *
 * Process flow:  Box structure read by recursive-descent.  Boxes formatted
 * and layed-out, also recursively.  Borders generated and optimized into a
 * minimum set of Border structs.  Working preview displayed for fiddling
 * with.  Output declarations written to file.
 *
 * Problems:  In a nutshell -- error recovery.  It doesn't do too well if
 * it runs out of memory, especially in the Border optimization code.
 * Also, the user error messages on parse errors could be much better --
 * like giving line number or a sample of the code at that point -- something
 * that would give a clue as to what went wrong.  Other than that, it's
 * pretty good.
 *
 * Differences from version 1:  Version 1 was distributed on a Fish disk
 * and was a real hack.  This version supports a 'C'-like preprocessor 
 * with #include's and #define's and macros with arguments.  This makes
 * the Requester source files look much nicer since the underlying 
 * grammar is so unreadable.  
 *
 * Disclaimer: This is a tool I hacked up for my own use in creating requesters
 * for Modeler 3D.  It works for me, but I make no claim as to the robustness
 * or other quality of the code.  It's not mouse-driven, but it's such a
 * useful tool that it's worth learning anyway.  Besides, you can put it in
 * your makefile's and have it work just like any other compiler.
 *
 * I'm making this available as a service to Amiga developers.  You are
 * encouraged to enhance or modify as you need to make it more useful for
 * your own purposes.  If you make any changes that make this a better
 * general-purpose tool, let me know about them.
 *
 *      Stuart Ferguson         1/89
 *      (shf@well.UUCP)
 */

#include <stdio.h>
#include <functions.h>
#include <exec/types.h>
#include <intuition/intuition.h>

/* STD.H is my collection of useful macros.
 */
#include "std.h"

/* LEX.H is the lexical analysis defines.
 */
#include "lex.h"


#define abs(x)          ((x)<0 ? -(x) : (x))

/* Size of font for text boxes (fixed width assumed).
 */
#define FONTWIDTH       8
#define FONTHEIGHT      8

/* Box types
 */
#define HBOX    1
#define VBOX    2
#define FILL    3
#define BLOK    4
#define TEXT    5
#define HRULE   6
#define VRULE   7

/*
 * Extended Gadget structure to hold an optional special gadget ID name
 * and the parent box for this gadget.
 */
struct SuperGadget {
        struct Gadget   g;
        struct Box      *box;
        char            *gnam;
};

/*
 * A requester is constructed from a tree of Boxes.  Boxes are arranged in a
 * binary tree, one subtree is what is inside the Box (sub), the other is the
 * remaining Boxes at the same level (next). 
 */
typedef struct Box {
        struct SuperGadget      *gad;   /* gadget - if any */
        struct Box     *next, *sub;     /* binary tree links */
        short           type,           /* box type - possibilities above */
                        col;    /* color - for borders and text */
        short           xs, ys, /* box size (x,y) */
                        x, y,   /* box position (x,y) */
                        nfil;   /* number of filers inside this box */
        char           *val;            /* string for TEXT boxes */
};


/* GLOBAL */

int             infoLevel = 1,  /* degree of verbosity (0=quiet, 
                                 * 1=normal, 2=verbose)
                                 */
                printBoxes = 0, /* printout flag (false normally) */
                showPreview;    /* preview flag (depends on whether there's
                                 * an output file and on the '-d' flag.
                                 */
char           *globStr = "static ";    /* structures normally static */

FILE           *file;           /* output file */
char           *base;           /* base name */
short           def_bcol = 1,   /* default border and */
                def_tcol = 1;   /* text colors */

/* String pointer ID's returned from the lexer.
 */
char           *fill_pstr, *tbox_pstr, *hbox_pstr, *vbox_pstr,
               *blok_pstr, *s_pstr, *p_pstr, *pv_pstr, *ph_pstr;

/* The Requester structures, the lists of parts and the guy itself.
 */
struct Border    *blst = NULL;  /* list header for border structs */
struct IntuiText *itlst = NULL; /* list header for text structs */
struct Gadget    *glst = NULL;  /* list header for gadgets */

#define LATER   0

struct Requester mreq = {
        NULL,5,5,LATER,LATER,0,0,LATER,LATER,LATER,
        0,0,NULL,{NULL},NULL,NULL,{NULL}
};

/*
 * Generic templates for creating Intuition structures.
 */
struct Border
        generic_border = {0, 0, LATER, 0, JAM1, LATER, LATER, LATER};
struct Gadget
        generic_gadget = {
                LATER, LATER, LATER, LATER, LATER,
                GADGHCOMP,RELVERIFY,BOOLGADGET|REQGADGET,
                LATER, NULL, LATER, 0, LATER, LATER, NULL
        };
struct IntuiText
        generic_itext = {LATER, 0, JAM2, LATER, LATER, LATER, LATER, LATER};
struct StringInfo
        generic_sinfo = {LATER, NULL, 0, LATER, 0};
struct PropInfo
        generic_pinfo = {AUTOKNOB|PROPBORDERLESS,0x8000,0x8000,0x8000,0x8000};

/* Two macros to extract GadgetType info from a Gadget structure.
 */
#define GADGETTYPEBITS  (~GADGETTYPE)
#define GTYPE(g)        (((g)->GadgetType)&GADGETTYPEBITS)

/*
 * The preview window.
 */
struct NewWindow nwin = {
        0, 0, 300, 150, -1, -1,
        CLOSEWINDOW | REQCLEAR | MOUSEMOVE | GADGETDOWN | GADGETUP | VANILLAKEY,
        WINDOWDEPTH | WINDOWDRAG,
        NULL, NULL, (UBYTE *) "Preview", NULL,
        NULL, 0, 0, 0, 0, WBENCHSCREEN
};

struct IntuitionBase *IntuitionBase;


struct Box * ReadBoxList ();
short Layin ();
char * IMClass ();

/* Lexer interface functions.
 */
char * FindString ();
short NextToken ();


/*
 * Returns a new, initialized Box struct.
 */
struct Box * NewBox (type)
        short           type;
{
        struct Box     *b;

        if (!(b = NEW (struct Box))) {
                MemError ();
                return NULL;
        }
        b->type = type;
        b->nfil = 0;
        b->gad = NULL;
        b->val = NULL;
        b->next = b->sub = NULL;
        return b;
}


/*
 * Recursively frees Box tree.
 */
FreeBox (box)
        struct Box *box;
{
        register struct Gadget  *g;
        register struct StringInfo *si;

        if (!box) return;

        FreeBox (box->sub);
        FreeBox (box->next);

        if (g = (struct Gadget *) box->gad) {
                if (GTYPE(g) == STRGADGET) {
                        si = (struct StringInfo *) g->SpecialInfo;
                        FREE_X (si, struct StringInfo, si->MaxChars);
                } else if (GTYPE(g) == PROPGADGET) {
                        FREE (g->SpecialInfo, struct PropInfo);
                        FREE (g->GadgetRender, struct Image);
                }
                FREE (g, struct SuperGadget);
        }
        FREI (box);
}


/*
 * Recursively examine all nodes of Box tree and allocate Border structs for
 * all the HRULE and VRULE boxes.  Adds new Borders to the main list. 
 * Returns 0 for failure, 1 for sucess.
 */
int CreateBorder (box)
        register struct Box    *box;
{
        register struct Border *bd;

        if (!box) return 1;

        if (box->type == HRULE || box->type == VRULE) {
                if (!(bd = NEW (struct Border))) {
                        MemError ();
                        FreeBorder ();
                        return 0;
                }
                *bd = generic_border;
                bd->FrontPen = box->col;
                bd->Count = 2;
                if (!(bd->XY = NEW_N (SHORT, 4))) {
                        MemError ();
                        FREI (bd);
                        FreeBorder ();
                        return 0;
                }
                bd->XY[0] = bd->XY[2] = box->x;
                bd->XY[1] = bd->XY[3] = box->y;
                if (box->type == HRULE) bd->XY[2] += box->xs - 1;
                  else bd->XY[3] += box->ys - 1;

                bd->NextBorder = blst;
                blst = bd;
        }
        if (!CreateBorder (box->sub)) return 0;
        return (CreateBorder (box->next));
}


/*
 * Frees all Border structs in main Border list.
 */
FreeBorder ()
{
        register struct Border *b, *nxt;

        for (b = blst; b; b = nxt) {
                nxt = b->NextBorder;
                FREE_N (b->XY, SHORT, b->Count * 2);
                FREI (b);
        }
        blst = NULL;
}


/*
 * Recursively examine all nodes of Box tree and allocate IntuiText structs
 * for TEXT boxes that are not string gadgets.  Adds new Borders to the main
 * list.  Returns 1 for sucess, 0 for failure.
 */
int CreateIText (box)
        register struct Box     *box;
{
        struct IntuiText        *it;

        if (!box) return 1;

        /*
         * "box->val" may have been zero-ed by a string gadget grabbing
         * that text.  If so, this is not an IntuiText.
         */
        if (box->type == TEXT && box->val) {
                if (!(it = NEW(struct IntuiText))) {
                        MemError ();
                        FreeIText ();
                        return 0;
                }
                *it = generic_itext;
                it->IText = (UBYTE *) box->val;
                it->LeftEdge = box->x;
                it->TopEdge = box->y;
                it->FrontPen = box->col;
                it->NextText = itlst;
                itlst = it;
        }
        if (!CreateIText (box->sub)) return 0;
        return (CreateIText (box->next));
}


/*
 * Frees all IntuiText structs in the main list.  (No need to free the
 * text itself since that is managed by the lexer.)
 */
FreeIText ()
{
        register struct IntuiText *it, *nxt;

        for (it = itlst; it; it = nxt) {
                nxt = it->NextText;
                FREI (it);
        }
        itlst = NULL;
}


/*
 * First pass at merging redundant Borders:  Examines all the Borders in
 * the list for adjacency.  Any borders that could use the same set of
 * polyline commands are merged into a single struct. 
 */
MergeBorders ()
{
        register struct Border *a, *b;
        short           i0, i1, x, y, *xy, j;
        register short  i, ac, bc, merge;

        do {
                merge = -1;
                /*
                 * Examine all pairs of borders, "a" and "b", that
                 * are drawn with the same color, seaching for a pair
                 * that can be merged.  When loop exits with merge=-1,
                 * all pairs have been merged.
                 */
                for (a = blst; a; a = a->NextBorder) {
                        for (b = a->NextBorder; b; b = b->NextBorder) {
                                if (a->FrontPen != b->FrontPen) continue;

                                /*
                                 * Examine the 4 pairs of endpoints of each
                                 * polyline to see if any are adjacent to
                                 * each other.  If any are found, the pairs
                                 * located are encoded into "merge" and
                                 * the search loop exits.
                                 */
                                ac = a->Count;
                                bc = b->Count;
                                for (i0 = 0; i0 < 2; i0++)
                                        for (i1 = 0; i1 < 2; i1++) {
                                                x = a->XY[i0*2 * (ac - 1)]
                                                  - b->XY[i1*2 * (bc - 1)];
                                                y = a->XY[i0*2 * (ac - 1) + 1]
                                                  - b->XY[i1*2 * (bc - 1) + 1];
                                                if (abs (x) + abs (y) == 1)
                                                        merge = (i0 << 1) + i1;
                                        }
                                if (merge != -1)
                                        break;
                        }
                        if (merge != -1)
                                break;
                }
                if (merge == -1) continue;

                /*
                 * Merging: Create a new polyline data array and move
                 * the two parent polylines into the new one, possibly
                 * reversing one or both in the process.
                 * -- HELP ME:  Is there a nice way out if this
                 *              allocation fails...?
                 */
                xy = NEW_N (SHORT, (bc + ac) * 2);
                x = ((merge & 2) == 0);         /* x = reverse "a" */
                y = ((merge & 1) == 1);         /* y = reverse "b" */
                j = 0;
                for (i = 0; i < ac; i++) {
                        i0 = (x ? ac - 1 - i : i) * 2;
                        xy[j++] = a->XY[i0];
                        xy[j++] = a->XY[i0 + 1];
                }
                for (i = 0; i < bc; i++) {
                        i0 = (y ? bc - 1 - i : i) * 2;
                        xy[j++] = b->XY[i0];
                        xy[j++] = b->XY[i0 + 1];
                }

                /*
                 * Set "a" to have the new polyline data array.
                 */
                a->Count = j / 2;
                FREE_N (a->XY, SHORT, ac * 2);
                a->XY = xy;

                /*
                 * Find "b's" predecessor and remove "b" from list.
                 */
                for (a = blst; a && a->NextBorder != b; a = a->NextBorder);
                a->NextBorder = b->NextBorder;
                FREE_N (b->XY, SHORT, bc * 2);
                FREE (b, struct Border);

        } while (merge != -1);
}


/*
 * Second pass of Border merging: Eliminates linear segments from all
 * Borders XY lists.  The first pass will create lots of redundant points
 * along linear line segments.  This pass will compress those out.
 */
MergeLinear ()
{
        register struct Border *b;
        register short  i0, i1, i2, k, *xy;

        /*
         * Examine all borders with more than 1 line segment.
         */
        for (b = blst; b; b = b->NextBorder) {
                if (b->Count < 3) continue;

                /*
                 * Scan along the polyline list and compress out linear
                 * segments by skiping over them.
                 */
                xy = b->XY;
                i0 = 0;
                i1 = 1;
                i2 = 2;
                k = 2;
                while (i2 < b->Count) {
                        /*
                         * Skip past linear segments. (I.e. find the bend.)
                         */
                        while (i2 < b->Count &&
                               (xy[i0 * 2] == xy[i1 * 2]
                                 && xy[i1 * 2] == xy[i2 * 2] ||
                                xy[i0 * 2 + 1] == xy[i1 * 2 + 1]
                                 && xy[i1 * 2 + 1] == xy[i2 * 2 + 1])) {
                                i1++;
                                i2++;
                        }
                        if (i2 >= b->Count) continue;

                        /*
                         * Move polyline data to itself after skipping.
                         */
                        xy[k++] = xy[i1 * 2];
                        xy[k++] = xy[i1 * 2 + 1];
                        i0 = i1;
                        i1 = i2;
                        i2 = i1 + 1;
                }
                xy[k++] = xy[i1 * 2];
                xy[k++] = xy[i1 * 2 + 1];

                k /= 2;
                if (k == b->Count) continue;

                /*
                 * If this border has gotten shorter, allocate a new
                 * array and transfer the new polyline data.
                 */
                xy = NEW_N (SHORT, k * 2);
                for (i0 = 0; i0 < k * 2; i0++) xy[i0] = b->XY[i0];
                FREE_N (b->XY, SHORT, b->Count * 2);
                b->XY = xy;
                b->Count = k;
        }
}


/*
 * Set the XSize and YSize fields for this box and all below.
 */
Format (box)
        struct Box     *box;
{
        struct Box     *b;
        short           mx, my, sx, sy, nf;

        ASSERT (box);

        /*
         * Deal with the basis (leaf) cases.
         */
        switch (box->type) {
        
            /* Blok and text nodes have fixed, already computed size.
             */
            case BLOK:
            case TEXT:
                return;

            /* Fill node has no intrinsic size.
             */
            case FILL:
                box->xs = box->ys = 0;
                box->nfil = 1;
                return;

            /* H and VRULES have no intrinsic X or Y size, respectively.
             */
            case HRULE:
                box->xs = 0;
                return;
            case VRULE:
                box->ys = 0;
                return;
        }

        /*
         * H and VBOXes are the recursive case.  First format each
         * internal box.
         */
        for (b = box->sub; b; b = b->next) Format (b);

        /*
         * Compute total and max sizes in each direction. Total (sx,sy) is sum
         * of all sub-boxes, max (mx,my) is max of sub-boxes. Also inherit
         * filler count.
         */
        my = mx = sx = sy = nf = 0;
        for (b = box->sub; b; b = b->next) {
                sx += b->xs;
                sy += b->ys;
                if (b->type == box->type || b->type == FILL) nf += b->nfil;
                if (b->xs > mx) mx = b->xs;
                if (b->ys > my) my = b->ys;
        }
        box->nfil = nf;

        /*
         * For horizontal boxes, bounding box is sum in x and max in y; for
         * vertical, bouding box is max in x and sum in y.  This is the
         * minimum size of the containing box for the given subboxes.  It
         * may still expand due to fillers.
         */
        if (box->type == HBOX) {
                box->xs = sx;
                box->ys = my;
        } else if (box->type == VBOX) {
                box->xs = mx;
                box->ys = sy;
        }
}


/*
 * Compute the position of the boxes internal to this box given that this
 * box has correct location.  The box size computed by Format() is a minimum
 * size, "mx" and "my" are the max that the box can be expanded by filler.
 */
Layout (box, mx, my)
        struct Box     *box;
        short           mx, my;
{
        struct Box     *b;
        short           ish, z, nfil;
        long            gap, ifil;

        ASSERT (box);

        /*
         * Rules fill out to their max possible size.
         */
        if (box->type == HRULE) box->xs = mx;
         else if (box->type == VRULE) box->ys = my;

        /*
         * Process only HBOX and VBOX cases recursively.  Any other case (a
         * basis case) has its position set correctly (see assumptions at
         * head of function).
         */
        if (box->type != HBOX && box->type != VBOX) return;

        /* Get important values.  Set the "is-hbox" (ish) flag.  Get the
         * current X size for HBOXes or the Y size for VBOXes as "z".
         * "gap" is the differece between the max size and minimum size 
         * given by the Format(), and is how much fillers can expand.
         */
        ish = (box->type == HBOX);
        z = (ish ? box->x : box->y);
        gap = (ish ? mx - box->xs : my - box->ys);

        /*
         * Set positions by setting filler sizes.
         */
        ifil = 0;
        Layin (box, &ifil, ish, z, box->nfil, gap);

        /* Update box size.  If it had fillers, it got as big as
         * it could.
         */
        if (box->nfil) {
                if (ish) box->xs = mx;
                    else box->ys = my;
        }
}


/*
 * Layout internal boxes.  Having this as a recursive function deals with
 * the case of VBOXes within VBOXes and HBOXes within HBOXes.
 *
 * NOTE: I'd comment this function, but I can't figure it out.  It seems to
 * figure out the horizonal position of each box and update it as it goes
 * along.  It also calls itself when there are nested same-class boxes.
 * Oh well.  There's probably a better way to do it anyway.
 */
short Layin (box, ifil, ish, z, nfil, gap)
        struct Box     *box;
        short          *ifil, ish, z, nfil;
        long            gap;
{
        struct Box     *b;
        short           t;

        for (b = box->sub; b; b = b->next) {
                if (ish) {
                        b->x = z;
                        b->y = box->y;
                } else {
                        b->x = box->x;
                        b->y = z;
                }

                if (b->type == FILL) {
                        t = (gap * (*ifil + 1)) / nfil - (gap ** ifil) / nfil;
                        (*ifil)++;
                        if (ish) b->xs = t;
                            else b->ys = t;

                } else if ((ish && b->type == HBOX)
                       || (!ish && b->type == VBOX)) {
                        if (ish) b->ys = box->ys;
                            else b->xs = box->xs;
                        t = Layin (b, ifil, ish, z, nfil, gap) - z;
                        if (ish) b->xs = t;
                            else b->ys = t;

                } else Layout (b, box->xs, box->ys);

                z += (ish ? b->xs : b->ys);
        }
        return z;
}


/*
 * Use the computed position of the boxes to set the position of
 * the associated gadgets.
 */
PositionGadgets ()
{
        struct Box      *b;
        struct Gadget   *g;

        for (g = glst; g; g = g->NextGadget) {
                b = ((struct SuperGadget *) g)->box;
                g->LeftEdge = b->x;
                g->TopEdge = b->y;
                g->Width = b->xs;
                g->Height = b->ys;
        }
}


/*
 * Returns pointer to string containing box type name for printout.
 */
char * BoxType (typ)
        short           typ;
{
        switch (typ) {
            case HBOX:  return ("HBOX");
            case VBOX:  return ("VBOX");
            case BLOK:  return ("BLOK");
            case TEXT:  return ("TEXT");
            case FILL:  return ("FILL");
            case HRULE: return ("HRULE");
            case VRULE: return ("VRULE");
        }
}


/*
 * Recursively prints this box and all its contents.
 */
PrintBox (box, lev)
        struct Box     *box;
        short           lev;
{
        int             i;

        if (!box) return;

        for (i = 0; i < lev; i++) printf ("  ");

        printf ("%s (%d,%d) %dx%d", BoxType (box->type),
                box->x, box->y, box->xs, box->ys);
        if (box->type == TEXT)  printf (" <%s>", box->val);
        if (box->gad)           printf (" [gadget]");
        printf ("\n");

        PrintBox (box->sub, lev + 1);
        PrintBox (box->next, lev);
}



/*
 * ==== INPUT SECTION ====
 *
 * File input uses the "lex" front-end for macro processing.  Main entry
 * points for this package are the NextToken() and Backspace() functions.
 * NextToken() returns the code for the next lexical item in the input 
 * stream and sets a buffer pointer to point to its value.  Backspace()
 * resets the lex package to re-read the last token read, so that the
 * file is effectively backspaced one token.  FindString() is also used
 * to get the unique identifer pointer for a string from the hash table.
 */


/*
 * Read a number if there is one.  Otherwise return false and don't
 * change n's value.
 */
BOOL Qnum (n, radix)
        short          *n, radix;
{
        short           i = 0, tok;
        char           *buf;

        tok = NextToken (&buf);
        if (tok != RT_NUM) {
                Backspace ();
                return 0;
        }
        for (; *buf >= '0' && *buf <= '9'; buf++) {
                i = i * radix + (*buf - '0');
        }
        *n = i;
        return 1;
}


/*
 * Reads a double-quoted string like
 *      "stuff"
 * from the file.  Returns pointer to the string contents. 
 */
char * ReadString ()
{
        short           tok;
        char           *buf;

        tok = NextToken (&buf);
        if (tok != RT_STR) {
                fprintf (stderr, "String not found.\n");
                Backspace ();
                return NULL;
        }
        return buf;
}


/*
 * Read gadget ID of the form 
 *      :number
 * if there is one.  Read as hex.  If there is one, create a new
 * SuperGadget structure and add to the main gadget list.
 */
struct SuperGadget * ReadOptGadget (box)
        struct Box *box;
{
        struct SuperGadget *sg;
        short           tok, id;
        char           *buf;

        tok = NextToken (&buf);
        if (tok != RT_CHR || *buf != ':') {
                Backspace ();
                return NULL;
        }
        if (!Qnum (&id, 16)) {
                fprintf (stderr, "Error reading gadget ID number\n");
                return NULL;
        }

        if (!(sg = NEW (struct SuperGadget))) {
                MemError ();
                return NULL;
        }
        sg->g = generic_gadget;
        sg->gnam = NULL;
        sg->box = box;
        sg->g.GadgetID = id;
        sg->g.NextGadget = glst;
        glst = (struct Gadget *) sg;
        return sg;
}


/*
 * Get a box from the open file.  Boxes are either single tokens ("f"
 * for FILL box, "-" and "|" for ordinary rules) or is a
 * composite of the form "("type data")".  Type can be "h" for HBOX,
 * "v" for VBOX, "b" for BLOK, "t" for TEXT, or "-" and "|" again for 
 * special rules.
 *
 * If there isn't a box here, ReadBox() returns NULL with the lexical
 * stream positioned back to read whatever was really there.
 */
struct Box * ReadBox ()
{
        short           tok, i;
        char           *buf, c;
        struct Box     *b;

        tok = NextToken (&buf);

        if (tok == RT_ID && buf == fill_pstr) return NewBox (FILL);

        if (tok != RT_CHR) {
                Backspace ();
                return NULL;
        }

        c = *buf;
        if (c == '-') {
                if (!(b = NewBox (HRULE))) return NULL;
                b->ys = 1;
                b->col = def_bcol;
                return b;
        }
        if (c == '|') {
                if (!(b = NewBox (VRULE))) return NULL;
                b->xs = 1;
                b->col = def_bcol;
                return b;
        }
        if (c != '(') {
                Backspace ();
                return NULL;
        }

        /*
         * Decode the value inside the '('.
         */
        tok = NextToken (&buf);
        c = *buf;
        if (tok == RT_ID)
                if (buf == hbox_pstr) {
                        if (!(b = NewBox (HBOX))) return NULL;
                        b->sub = ReadBoxList ();
                } else if (buf == vbox_pstr) {
                        if (!(b = NewBox (VBOX))) return NULL;
                        b->sub = ReadBoxList ();
                } else if (buf == tbox_pstr) {
                        if (!(b = NewBox (TEXT))) return NULL;
                        b->col = def_tcol;
                        Qnum (&b->col, 10);
                        if (!(b->val = ReadString ())) {
                                FreeBox (b);
                                return NULL;
                        }
                        b->xs = strlen (b->val) * FONTWIDTH;
                        b->ys = FONTHEIGHT;
                } else if (buf == blok_pstr) {
                        if (!(b = NewBox (BLOK))) return NULL;
                        i = Qnum (&b->xs, 10);
                        i &= Qnum (&b->ys, 10);
                        if (!i) {
                                fprintf (stderr, "Block needs X and Y sizes\n");
                                return NULL;
                        }
                } else {
                        fprintf (stderr, "Unrecognized box type <%s>\n", buf);
                        return NULL;
                }
        else if (tok == RT_CHR)
                switch (c) {
                    case '-':
                        if (!(b = NewBox (HRULE))) return NULL;
                        if (!Qnum (&b->ys, 10)) {
                                fprintf (stderr, "Bad hrule structure.\n");
                                return NULL;
                        }
                        b->col = def_bcol;
                        Qnum (&b->col, 10);
                        break;
                    case '|':
                        if (!(b = NewBox (VRULE))) return NULL;
                        if (!Qnum (&b->xs, 10)) {
                                fprintf (stderr, "Bad vrule structure\n");
                                return NULL;
                        }
                        b->col = def_bcol;
                        Qnum (&b->col, 10);
                        break;
                    default:
                        fprintf (stderr, "Unrecognized box type <%c>\n", c);
                        return NULL;
                }
        else {
                fprintf (stderr, "Unrecognized box type <%s>\n", buf);
                return NULL;
        }
        /*
         * Pick up the closing ')'.
         */
        tok = NextToken (&buf);
        if (tok != RT_CHR || *buf != ')') {
                fprintf (stderr, "Parse error - expected ')' !\n");
                FreeBox (b);
                return NULL;
        }

        /*
         * Read the optional Gadget for this box (as ":id").
         */
        b->gad = ReadOptGadget (b);
        return b;
}


/*
 * Read a list of boxes from the file stream.  Recursive: read a box,
 * then read a list. 
 */
struct Box * ReadBoxList ()
{
        struct Box      *b;

        b = ReadBox ();
        if (!b) return NULL;

        b->next = ReadBoxList ();
        return b;
}


/*
 * Create a new StringInfo struct and initialize to point to the 
 * given string buffer.  Allocates space for the buffer along with
 * the info struct itself (NEW_X).  Removes trailing spaces.
 */
APTR NewStrInfo (buf)
        char *buf;
{
        struct StringInfo       *si;
        short                   i;
        char                    *str;

        i = strlen (buf) + 1;
        if (!(si = NEW_X (struct StringInfo, i))) {
                MemError ();
                return NULL;
        }
        *si = generic_sinfo;
        si->Buffer = (UBYTE *) (str = (char *) (si+1));
        si->MaxChars = i;
        strcpy (str, buf);
        for (i -= 2; i>=0 && str[i] == ' '; i--) str[i] = 0;
        return (APTR) si;
}


/* Create new PropInfo struct.  Set the free motion flag based on the
 * id for this gadget "pv" = vert prop, "ph" = horiz prop, "p" = h+v prop.
 */
APTR NewPropInfo (id)
        char *id;
{
        register struct PropInfo        *pi;

        if (!(pi = NEW (struct PropInfo))) {
                MemError ();
                return NULL;
        }
        *pi = generic_pinfo;
        if (id == p_pstr || id == pv_pstr) pi->Flags |= FREEVERT;
        if (id == p_pstr || id == ph_pstr) pi->Flags |= FREEHORIZ;
        return (APTR) pi;
}


/*
 * Reads the list of gadget info from the end of the file.  Reads as much
 * as there is.  Format is:
 *      number {s|p|pv|ph} {:string} string
 * stuff in {}'s is optional.  Each entry gives extra info for the numbered
 * gadget.  {s|p} is string or prop flag.  {:string} is the optional named
 * value rather than just the nubmer.  The last string is the gadget flags.
 * Each set of info gets added to the corresponding gadget structure in
 * the main list.
 */
ReadGadInfo ()
{
        struct Gadget  *g;
        struct Box     *box;
        short           tok;
        char           *buf, c, *actf;
        short           i;
        USHORT          flag;

        while (Qnum (&i, 16)) {
                /*
                 * Locate the gadget in question and it's associated box.
                 */
                for (g = glst; g; g = g->NextGadget)
                        if (g->GadgetID == i) break;
                if (!g) {
                        fprintf (stderr, "Unknown gadget ID: %x\n", i);
                        continue;
                }
                box = ((struct SuperGadget *) g)->box;

                /* Get the optional string or prop flag.
                 */
                tok = NextToken (&buf);
                if (tok == RT_ID) {
                        if (buf == s_pstr) {
                                g->GadgetType &= ~GADGETTYPEBITS;
                                g->GadgetType |= STRGADGET;
                                if (!(g->SpecialInfo = NewStrInfo (box->val)))
                                        return;
                                box->val = NULL;
                        } else if (buf == p_pstr
                            || buf == ph_pstr
                            || buf == pv_pstr) {
                                g->GadgetType &= ~GADGETTYPEBITS;
                                g->GadgetType |= PROPGADGET;
                                if (!(g->SpecialInfo = NewPropInfo (buf)))
                                        return;
                                if (!(g->GadgetRender =
                                    (APTR) NEW (struct Image))) {
                                        MemError ();
                                        FREE (g->SpecialInfo, struct PropInfo);
                                        return;
                                }
                        } else {
                                fprintf (stderr,
                                    "Expected \"s\" or \"p\": <%s>\n", buf);
                                break;
                        }
                        tok = NextToken (&buf);
                }

                /* Get optional gadget ID name string.
                 */
                if (tok == RT_CHR && *buf == ':') {
                        ((struct SuperGadget *) g)->gnam = ReadString ();
                        tok = NextToken (&buf);
                }
                Backspace ();

                /* Get and process required activation flags string.
                 */
                actf = ReadString ();
                g->Activation &= ~RELVERIFY;
                for (; *actf; actf++) {
                        switch (*actf) {
                            case 'B':
                                g->Flags &= ~GADGHIGHBITS;
                                g->Flags |= GADGHBOX;
                                flag = 0;
                                break;
                            case 't':
                                flag = TOGGLESELECT;
                                break;
                            case 'v':
                                flag = RELVERIFY;
                                break;
                            case 'e':
                                flag = ENDGADGET;
                                break;
                            case 'i':
                                flag = GADGIMMEDIATE;
                                break;
                            case 'c':
                                flag = STRINGCENTER;
                                break;
                            case 'f':
                                flag = FOLLOWMOUSE;
                                break;
                        }
                        g->Activation |= flag;
                }
        }
}


/*
 * Get values for the identifier strings from the lexical analyzer.
 * The lexer will return the same pointer for any identifier which
 * matches.
 */
AssignStrings ()
{
        fill_pstr = FindString ("f");
        hbox_pstr = FindString ("h");
        vbox_pstr = FindString ("v");
        tbox_pstr = FindString ("t");
        blok_pstr = FindString ("b");
        s_pstr = FindString ("s");
        p_pstr = FindString ("p");
        ph_pstr = FindString ("ph");
        pv_pstr = FindString ("pv");
}


/*
 * To read file: open, read base name, read optional default border and text
 * colors, read a box (a BIG box), read gadget info blocks, close.
 */
struct Box * ReadFile ()
{
        struct Box     *box;
        short           i, tok;
        char           *buf;

        AssignStrings ();
        tok = NextToken (&base);
        if (tok != RT_ID) {
                fprintf (stderr, "Cannot find base name\n");
                return NULL;
        }
        Qnum (&def_bcol, 10);
        Qnum (&def_tcol, 10);

        if (infoLevel > 1) printf ("base name: \"%s\"\ndefault border color:"
                " %d\ndefault text color: %d\n", base, def_bcol, def_tcol);

        box = ReadBox ();
        ReadGadInfo ();

        /*
         * Make sure we're at the end of the file to make the
         * lexer happy.  Print up to 10 error messages unless there
         * is no box from the previous call in which case there's something
         * wrong anyway.  (Unless in verbose mode, then show 'em all.)
         */
        i = ((box || infoLevel > 1) ? 10 : 0);
        while (NextToken (&buf) != RT_EOF) {
                if (i) {
                        fprintf (stderr,
                            "Token found after end of data: <%s>\n", buf);
                        if (!--i) fprintf (stderr, "... etc.\n");
                }
        }

        return box;
}


/*
 * ====  OUTPUT SECTION  ====
 *
 * Dumps structures created during the input and resolution phases of
 * the processing.  Just takes a pointer to a Requester in WriteRequester()
 * and dumps the related structures as well.
 */

/*
 * Write string info and buffer declarations from string gadgets
 * (if any).
 */
WriteStrGad (glist)
        struct Gadget           *glist;
{
        struct Gadget           *g;
        struct StringInfo       *si;
        int                     i, n;

        /* Count number of string gadgets.
         */
        for (n = 0, g = glist; g; g = g->NextGadget)
                if (GTYPE(g) == STRGADGET) n++;

        if (!n) return;

        /* Write the necessary buffers for the string infos.
         */
        fprintf (file, "\n%sUBYTE %s_nbuf[%d][NUMCHR] = {\n\t",
                globStr, base, n);
        i = n;
        for (g = glist; g; g = g->NextGadget) {
                if (GTYPE(g) != STRGADGET) continue;

                si = (struct StringInfo *) g->SpecialInfo;
                fprintf (file, " \"%s\"", si->Buffer);
                if (--i) fprintf (file, ",");
        }

        fprintf (file, "\n};\n\n%sstruct StringInfo %s_sinfo[] = {\n",
                globStr, base);
        i = 0;
        for (g = glist; g; g = g->NextGadget) {
                if (GTYPE(g) != STRGADGET) continue;

                si = (struct StringInfo *) g->SpecialInfo;
                fprintf (file, "\t{&%s_nbuf[%d][0],undo,0,NUMCHR,0}",
                        base, i++);
                if (--n) fprintf (file, ",");
                fprintf (file, "\n");
        }
        fprintf (file, "};\n");

        if (infoLevel > 1) printf ("wrote %d StringInfo structs\n", i);
}


/*
 * Write prop info and image declarations for prop gadgets (if any).
 */
WritePropGad (glist)
        struct Gadget           *glist;
{
        struct Gadget           *g;
        struct PropInfo         *pi;
        int                     i, n;

        /* Count number of prop gadgets.
         */
        for (n = 0, g = glist; g; g = g->NextGadget)
                if (GTYPE(g) == PROPGADGET) n++;

        if (!n) return;

        /* Write the necessary images for the autoknobs.
         */
        fprintf (file, "\n%sstruct Image %s_pimg[%d];\n", globStr, base, n);

        /* Write the PropInfo structures themselves.
         */
        fprintf (file, "\n%sstruct PropInfo %s_pinfo[] = {\n", globStr, base);
        i = n;
        for (g = glist; g; g = g->NextGadget) {
                if (GTYPE(g) != PROPGADGET) continue;

                pi = (struct PropInfo *) g->SpecialInfo;
                fprintf (file, "\t{%u,%u,%u,%u,%u}", pi->Flags,
                        pi->HorizPot, pi->VertPot,
                        pi->HorizBody, pi->VertBody);
                if (--i) fprintf (file, ",");
                fprintf (file, "\n");
        }
        fprintf (file, "};\n");

        if (infoLevel > 1) printf ("wrote %d PropInfo structs\n", n);
}


/*
 * Write the gadgets from the main gadget list.  Returns number of
 * gadgets written.
 */
int WriteGadgets (glist)
        struct Gadget   *glist;
{
        struct Gadget   *g;
        int             k = 1, nimg=0, nprp=0, nstr=0;
        char            *nam;

        if (!glist) return 0;

        WriteStrGad (glist);
        WritePropGad (glist);

        fprintf (file, "\n%sstruct Gadget %s_gad[] = {\n", globStr, base);

        for (g = glist; g; g = g->NextGadget) {
                if (g->NextGadget)
                        fprintf (file, "\t{&%s_gad[%d]", base, k++);
                else
                        fprintf (file, "\t{NULL");

                fprintf (file, ",%d,%d,%d,%d,%u,%u,%u,", g->LeftEdge,
                        g->TopEdge, g->Width, g->Height, g->Flags,
                        g->Activation, g->GadgetType);

                if (GTYPE(g) == PROPGADGET)
                        fprintf (file, "(APTR)&%s_pimg[%d]", base, nimg++);
                else
                        fprintf (file, "NULL");

                fprintf (file, ",\n\t NULL,NULL,0,(APTR)");

                if (GTYPE(g) == PROPGADGET)
                        fprintf (file, "&%s_pinfo[%d]", base, nprp++);
                else if (GTYPE(g) == STRGADGET)
                        fprintf (file, "&%s_sinfo[%d]", base, nstr++);
                else
                        fprintf (file, "NULL");

                if (nam = ((struct SuperGadget *) g)->gnam)
                        fprintf (file, ",%s", nam);
                else
                        fprintf (file, ",0x%x", g->GadgetID);

                if (g->NextGadget)
                        fprintf (file, "},\n");
                else
                        fprintf (file, "}\n");
        }
        fprintf (file, "};\n");

        if (infoLevel > 1) printf ("wrote %d Gadget structs\n", k);
        return k;
}


/*
 * Write out list of IntuiText structs for main list.  Returns number
 * of structures written.
 */
int WriteText (tlist)
        struct IntuiText        *tlist;
{
        struct IntuiText        *it;
        int                     k = 1;

        if (!tlist) return 0;

        fprintf (file, "\n%sstruct IntuiText %s_txt[] = {\n", globStr, base);

        for (it = tlist; it; it = it->NextText) {
                fprintf (file, "\t{%d,%d,%d,%d,%d,&ta,(UBYTE*)\"%s\",",
                        it->FrontPen, it->BackPen, it->DrawMode,
                        it->LeftEdge, it->TopEdge, it->IText);
                if (it->NextText)
                        fprintf (file, "&%s_txt[%d]},\n", base, k++);
                else
                        fprintf (file, "NULL},\n");
        }
        fprintf (file, "};\n");

        if (infoLevel > 1) printf ("wrote %d IntuiText structs\n", k);
        return k;
}


/*
 * Write out list of XY arrays from Border struct main list 
 */
WriteBorderXY (lst)
        struct Border *lst;
{
        register struct Border *b;
        register short  i;

        fprintf (file, "\n%sshort %s_brd_XY[] = {\n", globStr, base);
        for (b = lst; b; b = b->NextBorder) {
                fprintf (file, "\t");
                for (i = 0; i < b->Count; i++) {
                        fprintf (file, "%d,%d", b->XY[i * 2], b->XY[i * 2 + 1]);
                        if (i != b->Count - 1 || b->NextBorder)
                                fprintf (file, ", ");
                }
                fprintf (file, "\n");
        }
        fprintf (file, "};\n");
}


/*
 * Write out list of Border structs from main list.  Returns nubmer of
 * structures written.
 */
int WriteBorder (lst)
        struct Border *lst;
{
        register struct Border *b;
        register short  i = 0, k = 1;

        if (!lst) return 0;

        WriteBorderXY (lst);

        fprintf (file, "\n%sstruct Border %s_brd[] = {\n", globStr, base);
        for (b = lst; b; b = b->NextBorder) {
                fprintf (file, "\t{0,0,%d,0,JAM1,%d,&%s_brd_XY[%d],",
                         b->FrontPen, b->Count, base, i);
                i += b->Count * 2;
                if (b->NextBorder)
                        fprintf (file, "&%s_brd[%d]},\n", base, k++);
                else
                        fprintf (file, "NULL}\n");
        }
        fprintf (file, "};\n");

        if (infoLevel > 1) printf ("wrote %d Border structs\n", k);
        return k;
}


/*
 * Reverse the gadget list so it will make more sense to the client.
 * This way they will appear in the arrays in the order that they 
 * appear in the description file.
 */
struct Gadget * ReverseGadList (head)
        struct Gadget *head;
{
        struct Gadget *newhead = NULL, *nxt;

        for (; head; head = nxt) {
                nxt = head->NextGadget;
                head->NextGadget = newhead;
                newhead = head;
        }
        return newhead;
}


/*
 * The main output function.
 */
WriteRequester (name, req)
        char             *name;
        struct Requester *req;
{
        short           i, ng, nt, nb;

        if (!(file = fopen (name, "w"))) {
                fprintf (stderr, "Can't open output file\n");
                return;
        }

        req->ReqGadget = ReverseGadList (req->ReqGadget);
        ng = WriteGadgets (req->ReqGadget);
        nt = WriteText (req->ReqText);
        nb = WriteBorder (req->ReqBorder);

        /*
         * The requester itself.
         */
        fprintf (file, "\n%sstruct Requester %s_req = {\n\
\tNULL,0,0,%d,%d,0,0,", globStr, base, req->Width, req->Height);
        if (ng) fprintf (file, "%s_gad,", base);
        else    fprintf (file, "NULL,");
        if (nb) fprintf (file, "%s_brd,", base);
        else    fprintf (file, "NULL,");
        if (nt) fprintf (file, "%s_txt,", base);
        else    fprintf (file, "NULL,");
        fprintf (file, "0,0,\n\tNULL,{NULL},NULL,NULL,{NULL}\n};\n");

        fclose (file);
}


MemError ()
{
        fprintf (stderr, "Out of memory.\n");
}


/* Main entry point.  Decode args and call body function.  Args are:
 *
 *      -p      : Print box description
 *      -q      : Run silent, run deep
 *      -v      : Verbose (not much different than normal, really)
 *      -d      : Display preview (is default unless output requested)
 *      -s      : Send Requester declarations to stdout
 */
main (argc, argv)
        int             argc;
        char           *argv[];
{
        int     i, junk = 0, prev = 0, tostdout = 0;
        char    *infile = NULL, *outfile = NULL;

        /*
         * Decode arguments.
         */
        for (i = 1; i < argc; i++) {
                if (argv[i][0] == '-') {
                        switch (argv[i][1]) {
                            case 'p':
                                printBoxes = 1;
                                break;
                            case 'q':
                                infoLevel = 0;
                                break;
                            case 'v':
                                infoLevel = 2;
                                break;
                            case 'd':
                                prev = 1;
                                break;
                            case 's':
                                tostdout = 1;
                                break;
                            case 'g':
                                globStr = "";
                                break;
                            default:
                                junk = 1;
                        }
                } else {
                        if (!infile) infile = argv[i];
                          else if (!outfile) outfile = argv[i];
                          else junk = 1;
                }
        }
        if (junk || !infile) {
                printf ("Usage: %s [-p|q|v|d|s|g] <file> [<outfile>]\n",
                        argv[0]);
                exit (1);
        }
        if (tostdout) {
                outfile = "*";
                infoLevel = 0;
        }
        showPreview = (!outfile || prev);

        if (IntuitionBase = (struct IntuitionBase *)
            OpenLibrary ("intuition.library", 0L)) {
                Body (infile, outfile);
                CloseLibrary (IntuitionBase);
        }
}


Body (infile, outfile)
        char           *infile, *outfile;
{
        struct Window  *win;
        struct Box     *b;
        short           h, w;

        if (infoLevel > 0) printf (
                "Requester generator v2   Jan 1989  Stuart Ferguson\n");
        if (!OpenLexFile (infile)) {
                fprintf (stderr, "Cannot open %s\n", infile);
                return;
        }
        if (b = ReadFile ()) {
                Format (b);
                b->x = b->y = 0;
                Layout (b, b->xs, b->ys);
                if (printBoxes) PrintBox (b, 0);

                if (CreateIText (b)) {
                        if (CreateBorder (b)) {
                                MergeBorders ();
                                MergeLinear ();
                                PositionGadgets ();

                                mreq.Width = b->xs;
                                mreq.Height = b->ys;
                                mreq.ReqGadget = glst;
                                mreq.ReqText = itlst;
                                mreq.ReqBorder = blst;

                                if (showPreview) PreviewRequester (&mreq);
                                if (outfile) WriteRequester (outfile, &mreq);

                                FreeBorder ();
                        }
                        FreeIText ();
                }
                FreeBox (b);
        } else fprintf (stderr, "Error reading box description.\n");
        LexCleanup ();
}


/*
 * Open a window to preview the requester layout.
 */
PreviewRequester (req)
        struct Requester *req;
{
        struct Window  *win;
        short           h, w;

        w = req->Width + 12;
        h = req->Height + 16;
        if (w > 640 || h > 200) {
                fprintf (stderr, "Requester too large for preview.\n");
                return;
        }
        if (w < 150) w = 150;
        if (h < 60) h = 60;

        nwin.Width = w;
        nwin.Height = h;
        if (!(win = OpenWindow (&nwin))) {
                fprintf (stderr, "Unable to open preview window.\n");
                return;
        }

        req->LeftEdge =
                (w - win->BorderRight + win->BorderLeft - req->Width) / 2;
        req->TopEdge =
                (h - win->BorderBottom + win->BorderTop - req->Height) / 2;

        RequesterLoop (win, req);
        CloseWindow (win);
}


RequesterLoop (win, req)
        struct Window *win;
        struct Requester *req;
{
        struct Gadget *g;
        struct IntuiMessage *im;
        ULONG class, oldflags;
        USHORT code;
        int gend = 0, looping;

        /*
         * Determine if this requester can be terminated with a gadget.
         * If not, provide an alternate exit facility.
         */
        for (g = req->ReqGadget; g; g = g->NextGadget)
                if (g->Activation & ENDGADGET) {
                        gend = 1;
                        break;
                }
        oldflags = req->Flags;
        if (!gend) {
                if (infoLevel > 0) printf (
                        "No Endgadget -- Press ESC to exit Requester.\n");
                req->Flags |= NOISYREQ;
        }

        if (!Request (req, win)) {
                fprintf (stderr, "Unable to post Requester.\n");
                req->Flags = oldflags;
                return;
        }

        looping = 1;
        while (looping) {
                im = (struct IntuiMessage *) GetMsg(win->UserPort);
                if (!im) {
                        WaitPort (win->UserPort);
                        continue;
                }
                class = im->Class;
                code = im->Code;
                ReplyMsg (im);
                if (class == VANILLAKEY && code == 27) break;
                if (infoLevel > 0) printf ("Message : %s\n", IMClass (class));
                if (class == REQCLEAR) looping = 0;
        }
        if (looping) EndRequest (req, win);
        req->Flags = oldflags;
}


/*
 * Returns name of message class.  Lots more classes here than are
 * possible, but what the hell.
 */
char * IMClass (class)
        ULONG class;
{
        switch (class) {
            case SIZEVERIFY:    return ("SIZEVERIFY");
            case NEWSIZE:       return ("NEWSIZE");
            case REFRESHWINDOW: return ("REFRESHWINDOW");
            case MOUSEBUTTONS:  return ("MOUSEBUTTONS");
            case MOUSEMOVE:     return ("MOUSEMOVE");
            case GADGETDOWN:    return ("GADGETDOWN");
            case GADGETUP:      return ("GADGETUP");
            case REQSET:        return ("REQSET");
            case MENUPICK:      return ("MENUPICK");
            case CLOSEWINDOW:   return ("CLOSEWINDOW");
            case RAWKEY:        return ("RAWKEY");
            case REQVERIFY:     return ("REQVERIFY");
            case REQCLEAR:      return ("REQCLEAR");
            case MENUVERIFY:    return ("MENUVERIFY");
            case NEWPREFS:      return ("NEWPREFS");
            case DISKINSERTED:  return ("DISKINSERTED");
            case DISKREMOVED:   return ("DISKREMOVED");
            case WBENCHMESSAGE: return ("WBENCHMESSAGE");
            case ACTIVEWINDOW:  return ("ACTIVEWINDOW");
            case INACTIVEWINDOW:        return ("INACTIVEWINDOW");
            case DELTAMOVE:     return ("DELTAMOVE");
            case VANILLAKEY:    return ("VANILLAKEY");
            case INTUITICKS:    return ("INTUITICKS");
        }
}


#define DEBUG

/*
 * Debug routines.
 */
#ifdef DEBUG

PrintBorder ()
{
        struct Border *b;
        short i;

        printf ("Borders:\n");
        for (b=blst; b; b=b->NextBorder) {
                printf ("%d %d %d %d\n:: ", b->LeftEdge,
                        b->TopEdge, b->FrontPen, b->Count);
                for (i=0; i<b->Count; i++)
                        printf ("%d,%d ", b->XY[i*2],b->XY[i*2+1]);
                printf ("\n");
        }
}


PrintGadget ()
{
        USHORT          typ;
        struct Gadget   *g;

        printf ("Gadgets:\n");
        for (g=glst; g; g=g->NextGadget) {
                printf ("%d,%d %d,%d  ", g->LeftEdge, g->TopEdge,
                        g->Width, g->Height);
                typ = GTYPE(g);
                if (typ == PROPGADGET)  printf ("PROP");
                if (typ == STRGADGET)   printf ("STRING");
                if (typ == BOOLGADGET)  printf ("BOOL");
                printf ("\n");
        }
}
#endif
