home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Fred Fish Collection 1.5
/
ffcollection-1-5-1992-11.iso
/
ff_disks
/
100-199
/
ff191.lzh
/
Blk
/
blk.c
< prev
next >
Wrap
C/C++ Source or Header
|
1989-03-14
|
40KB
|
1,752 lines
/*
* 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