/*
 * 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, also recursively
 * Borders generated and resolved into a minimum set of Border structs
 * File written, sometimes linearly, sometimes recursively
 **
 *  Problems:
 * The file reading isn't the most elegant in the world, relying on global
 * variables and backspacing to look ahead.  A more general lexical approach
 * would be nice so that macros could be included (macros are sorely
 * needed).  Also, the file formatted front-end is not the most
 * user-friendly in the world and something more mouse-driven would be
 * neat.  The source-code generation is not terribily elegant either.  A
 * better approach would be to build the actual structures used in the
 * requester dynamically so the requester in the preview window could
 * actually be played with, and generating source-code would be just a
 * matter of dumping the structures to a file.  Borders do this already, and
 * it's somewhat more understandable than the other structures.
 **
 *  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.  Personally I shudder at the thought of
 * re-writing this into a real general-purpose tool just because bulk of
 * the code is so poorly written.  It does give a nice example of
 * recursive-decent parsing, and the box layout stuff is kind of neat and
 * can be put to good use in other places.
 **
 * Please inform my of any changes or improvements.  Enjoy.
 */

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


/* Useful defines */

#define NEW(typ)        (typ*)AllocMem((LONG)sizeof(typ),0L)
#define FREE(p,typ)     FreeMem(p,(LONG)sizeof(typ))
#define NEW_N(typ,n)    (typ*)AllocMem((LONG)((n)*sizeof(typ)),0L)
#define FREE_N(p,typ,n) FreeMem(p,(LONG)((n)*sizeof(typ)))
#define BACKSPACE       if (gc != -1) ungetc(gc,file)
#define abs(x)          ((x)<0 ? -(x) : (x))

/* Box types */

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

/* Dimension limits */

#define MAX_GAD 50
#define BUF_SIZ 500

/* 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 {
   short type,    /* box type - possibilities defined above */
     gid,         /* Gadget id code (0 if none) */
     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 */
   struct Box *next,*sub;  /* binary tree links */
} BOX;

/* Information on each gadget, zero-th is default
 */
struct GadInfo {
   short gid,  /* gadget id code */
     str,      /* string gadget */
     prp;      /* prop gadget */
   char *actf, /* flag string */
     *gnam;    /* gadget name (GadgetID field) */
}
  ginfo[MAX_GAD] = { -1,-1,-1,"v",NULL };

/* GLOBAL */

FILE *file;          /* input and output files */
int gc;              /* last character read */
char buf[BUF_SIZ];   /* big buffer for holding strings */
short bufpos=0;      /* position of free space in buf */
char base[10];       /* base name */
short ngad=0,igad=0,ntxt=0,itxt=0,  /* counters for gadgets, ituitexts, */
 nstr=0,istr=0,nprp=0,iprp=0;       /* string and prop gadgets */
short lgad=1;        /* counter for gadinfo array (0 already defined) */
short def_bcol=1,def_tcol=1;     /* default border and text colors */
struct Border *blst = NULL;      /* list header for border structs */

/* generic border and intuitext structs
 */
struct Border brd = {0,0,1,0,JAM1,2,NULL,NULL};
struct IntuiText it = {1,0,JAM2,0,0,NULL,NULL,NULL};

/* the preview window
 */
struct NewWindow nwin = {
   0,0,300,150,
   -1,-1,0,WINDOWDEPTH|WINDOWDRAG|SMART_REFRESH,
   NULL,NULL,(UBYTE*)"Blocks",NULL,
   NULL,0,0,0,0,WBENCHSCREEN
};

struct IntuitionBase *IntuitionBase;


/* Returns pointer to string containing box type name
 */
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");
   }
}


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

   b = NEW(BOX);
   b->type = type;
   b->nfil = b->gid = 0;
   b->val = NULL;
   b->next = b->sub = NULL;
}


/* Recursively frees Box tree
 */
FreeBox (box)
   BOX *box;
{
   if (!box) return;
   FreeBox (box->sub);
   FreeBox (box->next);
   FREE(box,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.
 */
CreateBorder (box)
   register BOX *box;
{
   register struct Border *bd;

   if (!box) return;

   if (box->type == HRULE || box->type == VRULE) {
      bd = NEW(struct Border);
      *bd = brd;
      bd->FrontPen = box->col;
      bd->XY = NEW_N(SHORT,4);
      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;
   }
   CreateBorder (box->sub);
   CreateBorder (box->next);
}


/* debug routine
PrintBorder ()
{
   struct Border *b;
   short i;

   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");
   }
}
*/


/* 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);
      FREE(b,struct Border);
   }
}


/* 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;
      for (a=blst; a; a=a->NextBorder) {
         for (b=a->NextBorder; b; b=b->NextBorder) {
            if (a->FrontPen != b->FrontPen) continue;
            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) {
         xy = NEW_N(SHORT,(bc+ac)*2);
         x = (merge&2) == 0;  /* reverse a */
         y = (merge&1) == 1;  /* 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];
         }
         a->Count = j/2;
         FREE_N(a->XY,SHORT,ac*2);
         a->XY = xy;
/* free b and remove 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 part of Border merging: Eliminates linear segments from all
 * Borders XY lists.
 */
MergeLinear ()
{
   register struct Border *b;
   register short i0,i1,i2,k,*xy;

   for (b=blst; b; b=b->NextBorder) {
      if (b->Count < 3) continue;
      xy = b->XY;
      i0 = 0;
      i1 = 1;
      i2 = 2;
      k = 2;
      while (i2 < b->Count) {
         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) {
            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;
      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)
   BOX *box;
{
   BOX *b;
   short mx,my,sx,sy,nf;

   if (!box) {
      printf ("Probably an error\nImproper leaf types\n");
      return;
   }

/* deal with the basis (leaf) cases */
   switch (box->type) {
    case BLOK:
    case TEXT:
      return;  /* size already set */
    case FILL:
/* fill node has no size */
      box->xs = box->ys = 0;
      box->nfil = 1;
      return;
/* H and VRULES have their Y and X sizes already set */
    case HRULE:
      box->xs = 0;
      return;
    case VRULE:
      box->ys = 0;
      return;
   }

/* only H and VBOXes left
 * 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
 */
   if (box->type == HBOX) {
      box->xs = sx;
      box->ys = my;
   } else if (box->type == VBOX) {
      box->xs = mx;
      box->ys = sy;
   } else printf ("Problem city --\nImpossible case\n");
}


short Layin();

/* Compute the layout 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)
   BOX *box;
   short mx,my;
{
   BOX *b;
   short ish,z,nfil;
   long gap,ifil;

/* handle basis cases
 */
   if (!box) {
      printf ("Bad leaf nodes\n");
      return;
   }
/* rules fill out to their max possible size
 */
   if (box->type == HRULE) {
      box->xs = mx;
   } else if (box->type == VRULE) {
      box->ys = my;
   }
   if (box->type != HBOX && box->type != VBOX) return;

/* process only HBOX and VBOX cases recursively
 * any onther case (a basis case) has its position set correctly
 *  (see assumptions at head of function)
 */
   ish = (box->type == HBOX);
   z = (ish ? box->x : box->y);
   gap = (ish ? mx-box->xs : my-box->ys);

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


/* Layout internal boxes
 * Having this as a recursive function deals with
 * the case of VBOXes within VBOXes
 */
short Layin (box,ifil,ish,z,nfil,gap)
   BOX *box;
   short *ifil,ish,z,nfil;
   long gap;
{
   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;
}


/* Recursively prints this box and all its contents
 */
PrintBox (box,lev)
   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->gid>0) printf (" [%d]", box->gid);
   printf ("\n");
   PrintBox (box->sub,lev+1);
   PrintBox (box->next,lev);
}


/* Renders the text boxes in their correct locations in the window.
 * Recursive.
 */
DisplayBox (win,box,biasx,biasy)
   struct Window *win;
   struct Box *box;
   short biasx,biasy;
{
   if (!box) return;

   if (box->type == TEXT) {
      it.IText = (UBYTE*)box->val;
      it.LeftEdge = box->x;
      it.TopEdge = box->y;
      it.FrontPen = box->col;
      PrintIText (win->RPort,&it,(LONG)biasx,(LONG)biasy);
   }
   DisplayBox (win,box->sub,biasx,biasy);
   DisplayBox (win,box->next,biasx,biasy);
}



/* INPUT SECTION */


/* Returns true for "c" a blank
 */
BOOL Blank(c)
   char c;
{
   return (c==' ' || c=='\n' || c=='\t');
}


/* Returns next non-blank char
 */
char NextChar()
{
   char c;
   while (Blank(c=(char)(gc=getc(file))));
   return c;
}


/* read a number if there is one
 * otherwise return false and don't change n's value 
 */
BOOL Qnum(n,radix)
   short *n,radix;
{
   char c;
   short i=0;
   BOOL isnum=FALSE;

   c = NextChar();
   while (c >= '0' && c <= '9') {
      isnum = TRUE;
      i = i*radix + (c-'0');
      c = (char)(gc=getc(file));
   }
   BACKSPACE;

   if (isnum) *n = i;
   return isnum;
}


/* Reads a double-quoted string like
 * "stuff"
 * from the file into a place in the string buffer.  Returns
 * pointer to the string constant.
 */
char *ReadString()
{
   short oldpos;
   char c,*cp;

   oldpos = bufpos;
   cp = &buf[bufpos];
   c = NextChar();
   if (c != '\"') {
      printf ("String not found\n");
      return 0;
   }
   while ((c=(char)(gc=getc(file))) != '\"') buf[bufpos++] = c;
   buf[bufpos++] = '\0';
   return cp;
}


/* Read ID of the form
 * :number
 * if there is one.  If not, return 0.
 * Number is read as hex.
 */
short ReadID()
{
   short id;
   char c;

   c = NextChar();
   if (c == ':') {
      fscanf (file, "%x", &id);
      ngad++;
      return id;
   } else {
      BACKSPACE;
      return 0;
   }
}


BOX *ReadBoxList ();

/* Get a box from the open file
 */
BOX *ReadBox ()
{
   char c;
   BOX *b;

   c = NextChar();

   if (c == 'f') return NewBox (FILL);
   if (c == '-') {
      b = NewBox (HRULE);
      b->ys = 1;
      b->col = def_bcol;
      return b;
   }
   if (c == '|') {
      b = NewBox (VRULE);
      b->xs = 1;
      b->col = def_bcol;
      return b;
   }
   if (c == '(') {
      c = NextChar();
      switch (c) {
       case 'h':
         b = NewBox (HBOX);
         b->sub = ReadBoxList ();
         break;
       case 'v':
         b = NewBox (VBOX);
         b->sub = ReadBoxList ();
         break;
       case 't':
         ntxt++;
         b = NewBox (TEXT);
         b->col = def_tcol;
         Qnum (&b->col,10);
         b->val = ReadString();
         if (!b->val) {
            FreeBox (b);
            return 0;
         }
         b->xs = strlen(b->val)*8;
         b->ys = 8;
         break;
       case 'b':
         b = NewBox (BLOK);
         fscanf (file, "%d%d", &b->xs, &b->ys);
         break;
       case '-':
         b = NewBox (HRULE);
         fscanf (file, "%d", &b->ys);
         b->col = def_bcol;
         Qnum (&b->col,10);
         break;
       case '|':
         b = NewBox (VRULE);
         fscanf (file, "%d", &b->xs);
         b->col = def_bcol;
         Qnum (&b->col,10);
         break;
       default:
         printf ("Unknown Key Char\n");
         return 0;
      }

      c = NextChar();
      if (c != ')') {
         printf ("parse problem - expected ')'\n");
         FreeBox (b);
         return 0;
      }
      b->gid = ReadID();
      return b;

   } else {
      BACKSPACE;
      return 0;
   }
}


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

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

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


/* Reads the list of gadget info from the end of the file
 * Reads as much as there is
 * format is:
 *   number {s|p} {:string} string
 * stuff in {}'s is optional
 */
ReadGadInfo()
{
   char c;
   short i;

   i = 0;
   while (Qnum(&i,16)) {
      ginfo[lgad].gid = i;
      ginfo[lgad].gnam = NULL;
      ginfo[lgad].str = -1;
      ginfo[lgad].prp = -1;
      c = NextChar();
      if (c == 's') {
         ginfo[lgad].str = nstr++;
         c = NextChar();
      } else if (c == 'p') {
         ginfo[lgad].prp = nprp++;
         c = NextChar();
      }
      if (c==':') {
         ginfo[lgad].gnam = ReadString();
      } else {
         BACKSPACE;
      }
      ginfo[lgad++].actf = ReadString();
   }

/* print out list of gadget info structs for pure delight
 */
   for (i=0; i<lgad; i++) {
      printf ("gad %d %d, <%s>", i, ginfo[i].gid, ginfo[i].actf);
      if (ginfo[i].gnam) printf (" [%s]", ginfo[i].gnam);
      if (ginfo[i].str != -1) printf (" (string)");
      if (ginfo[i].prp != -1) printf (" (prop)");
      printf ("\n");
   }
}


/* 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
 */
BOX *ReadFile (nam)
   char *nam;
{
   BOX *box;
   short i;

   printf ("opening file <%s>\n", nam);
   file = fopen (nam,"r");
   if (!file) {
      printf ("Cannot open %s\n", nam);
      exit(0);
   }
   fscanf (file, "%s", base);
   Qnum (&def_bcol,10);
   Qnum (&def_tcol,10);
   box = ReadBox ();
   ReadGadInfo();

   fclose (file);
   return box;
}


/* OUTPUT SECTION (this whole thing is crufty, sorry)
 * (except perhaps the Border stuff)
 */


/* Recursively locate and print the string for a given gadget id
 * Truncate trailing spaces
 */
WriteBufEntry (id,box)
   short id;
   BOX *box;
{
   int i;

   if (!box) return;

   if (box->gid == id) {
      for (i=strlen(box->val)-1; i>0; i--) {
         if (box->val[i] == ' ') box->val[i] = '\0';
          else break;
      }
      fprintf (file, "\"%s\"", box->val);
      if (++istr != nstr) fprintf (file, ", ");
      box->val = NULL;
      ntxt--;
   } else {
      WriteBufEntry (id,box->sub);
      WriteBufEntry (id,box->next);
   }
}


/* Output the StringInfo structs for all the string gadgets
 */
WriteStrInfo ()
{
   int i;

   for (i=0; i<lgad; i++) {
      if (ginfo[i].str != -1) {
         fprintf (file, "  {&%s_nbuf[%d][0],undo,0,NUMCHR,0}",base,istr++);
         if (istr == nstr) fprintf (file, "\n");
          else fprintf (file, ",\n");
      }
   }
}


/* Output the PropInfo structs for all the proportional gadgets
 */
WritePrpInfo ()
{
   int i;

   for (i=0; i<lgad; i++) {
      if (ginfo[i].prp != -1) {
         fprintf (file, "  {AUTOKNOB|FREEHORIZ|PROPBORDERLESS,\
0x8000,0,0x8000,0x8000}");
         if (++iprp == nprp) fprintf (file, "\n");
          else fprintf (file, ",\n");
      }
   }
}


/* Recursively locate and output Gadget structs for all gadgets
 */
WriteGad (box)
   BOX *box;
{
   int i,j,l,t;

   if (!box) return;

   if (box->gid) {
/* find the Gadinfo entry for this gadget.  search backwards so that a
 * failure to locate lands us on Gadinfo 0, the default
 */
      for (i=lgad-1; i>=0; i--)
       if (ginfo[i].gid == box->gid || i == 0) break;

      if (++igad==ngad) {
         fprintf (file, "  {NULL");
      } else {
         fprintf (file, "  {&%s_gad[%d]", base,igad);
      }
      fprintf (file, ",%d,%d,%d,%d,\n   ", box->x,box->y,box->xs,box->ys);
      if (index(ginfo[i].actf, 'B')) fprintf (file, "GADGHBOX,");
       else fprintf (file, "GADGHCOMP,");

      t = 0;
      if (l = strlen(ginfo[i].actf)) for (j=0; j<l; j++) {
         switch (ginfo[i].actf[j]) {
          case 't':
          case 'v':
          case 'e':
          case 'i':
          case 'c':
          case 'f':
            if (t) fprintf (file, "|");
            t = 1;
         }
         switch (ginfo[i].actf[j]) {
          case 't': fprintf (file, "TOGGLESELECT"); break;
          case 'v': fprintf (file, "RELVERIFY"); break;
          case 'e': fprintf (file, "ENDGADGET"); break;
          case 'i': fprintf (file, "GADGIMMEDIATE"); break;
          case 'c': fprintf (file, "STRINGCENTER"); break;
          case 'f': fprintf (file, "FOLLOWMOUSE"); break;
         }
      } else fprintf (file, "0");

      if (ginfo[i].str != -1) fprintf (file, ",STRGADGET");
       else if (ginfo[i].prp != -1) fprintf (file, ",PROPGADGET");
       else fprintf (file, ",BOOLGADGET");
      fprintf (file, "|REQGADGET,\n   ");
      if (ginfo[i].prp != -1)
        fprintf (file, "(APTR)&%s_pimg[%d],", base,ginfo[i].prp);
       else fprintf (file, "NULL,");
      fprintf (file, "NULL,NULL,0,");
      if (ginfo[i].str != -1)
        fprintf (file, "(APTR)&%s_sinfo[%d],", base,ginfo[i].str);
       else if (ginfo[i].prp != -1)
        fprintf (file, "(APTR)&%s_pinfo[%d],", base,ginfo[i].prp);
       else fprintf (file, "NULL,");

      if (ginfo[i].gnam) fprintf (file, "%s,NULL}", ginfo[i].gnam);
       else fprintf (file, "0x%x,NULL}", box->gid);
      if (igad==ngad) fprintf (file, "\n");
       else fprintf (file, ",\n");
   }
   WriteGad (box->sub);
   WriteGad (box->next);
}


/* Recursively locate and output IntuiText structs for all TEXT boxes
 */
WriteText (box)
   BOX *box;
{
   if (!box) return;

   if (box->type == TEXT && box->val) {
      fprintf (file, "  {%d,0,JAM2,%d,%d,&ta,(UBYTE*)\"%s\",",
       box->col,box->x,box->y,box->val);
      if (++itxt==ntxt) {
         fprintf (file, "NULL}\n");
      } else {
         fprintf (file, "&%s_txt[%d]},\n", base,itxt);
      }
   }
   WriteText (box->sub);
   WriteText (box->next);
}


/* Write out list of Border structs from main list
 */
WriteBorder ()
{
   register struct Border *b;
   register short i=0,j=0;

   fprintf (file, "struct Border %s_brd[] = {\n", base);
   for (b=blst; b; b=b->NextBorder) {
      fprintf (file, "  {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,++j);
      } else {
         fprintf (file, "NULL}\n");
      }
   }
   fprintf (file, "};\n");
}


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

   fprintf (file, "short %s_brd_XY[] = {\n", base);
   for (b=blst; b; b=b->NextBorder) {
      fprintf (file, "  ");
      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");
}


/* The main output function
 */
WriteFile (name,box)
   char *name;
   BOX *box;
{
   short i;

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

/* write optional string gadget structs
 */
   if (nstr) {
      fprintf (file,"UBYTE %s_nbuf[%d][NUMCHR] = {\n ", base,nstr);
      for (i=0; i<lgad; i++)
       if (ginfo[i].str != -1) WriteBufEntry (ginfo[i].gid,box);
      istr = 0;
      fprintf (file,"\n};\n\nstruct StringInfo %s_sinfo[] = {\n", base);
      WriteStrInfo();
      fprintf (file,"\n};\n");
   }

/* write optional prop gadget structs
 */
   if (nprp) {
      fprintf (file,"\nstruct Image %s_pimg[%d];\n", base,nprp);
      fprintf (file,"struct PropInfo %s_pinfo[] = {\n", base);
      WritePrpInfo();
      fprintf (file,"};\n");
   }

/* write gadgets, text and borders (not optional)
 */
   fprintf (file,"\nstruct Gadget %s_gad[] = {\n", base);
   WriteGad (box);
   fprintf (file,"};\n\nstruct IntuiText %s_txt[] = {\n", base);
   WriteText (box);
   fprintf (file, "};\n\n");
   WriteBorderXY ();
   WriteBorder ();

/* the requester itself
 */
   fprintf (file, "\n\nstruct Requester %s_req = {\n\
  NULL,0,0,%d,%d,0,0,%s_gad,%s_brd,%s_txt,0,0,\n\
  NULL,{NULL},NULL,NULL,{NULL}};\n", base,box->xs,box->ys,base,base,base);

   fclose (file);
}


main(argc,argv)
   int argc;
   char *argv[];
{
   if (argc < 2 || argc > 4) {
      printf ("Usage: blk <file> [<outfile> | %%]\n");
      exit(0);
   }

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


Body(argc,argv)
   int argc;
   char *argv[];
{
   struct Window *win;
   BOX *b;
   short h,w;

   b = ReadFile (argv[1]);
   Format (b);
   b->x = b->y = 0;
   Layout (b,b->xs,b->ys);
   if (argc == 3 && argv[2][0] == '%') PrintBox (b,0);
   CreateBorder (b);
   MergeBorders ();
   MergeLinear ();

/* open a window to preview the requester layout
 */
   w = b->xs+20;
   h = b->ys+20;
   if (w>640 || h>200) printf ("requester too large\n");
    else {
      nwin.Width = w;
      nwin.Height = h;
      win = OpenWindow (&nwin);
      if (win) {
         DrawBorder (win->RPort,blst,(LONG)win->BorderLeft+5,(LONG)win->BorderTop+5);
         DisplayBox (win,b,win->BorderLeft+5,win->BorderTop+5);
/* if output specified, write it out, otherwise wait */
         if (argc == 3 && argv[2][0] != '%') WriteFile(argv[2],b);
          else Delay (300L);
         CloseWindow (win);
      } else printf ("Unable to open Window\n");
   }

   FreeBorder ();
   FreeBox (b);
}
