/* fdevelop: convert script to intermediate language in two passes
Input defined in script.def, output in int.def
Pass 1 reads named file or stdin, writes intermediate file
Pass 2 reads intermediate, writes output
Intermediate file is like output, with these differences:
Header lines (d v, d c, etc.) absent
Erase commands: refer to line numbers in int file and
do not have geometric command following
Geometry commands: numbers not yet scaled, slots not
assigned
fdevelop is a filter, transforming stdin or one named file to stdout
the shell script develop uses this program to develop foo.s
into foo.i, if needed
*/
/* STATUS OF EACH LINE IN INTERMEDIATE FILE*/
int linemax = LINEMAX;
char *linevec; /* malloc'ed to [linemax] before pass 1 */
#define MAXVIEWS 10
/* if >10 views, change one-byte getc in pass2 */
#define ERASED 11
#define NOTGEOM 12
/* VIEWS */
extern void recordx(int, float);
extern void recordy(int, float);
extern int scalex(int, float);
extern int scaley(int, float);
int viewcnt = 0;
struct Viewelmt {
char name[MAXSTR]; /* character string name */
float maxx; /* range of values, both coords */
float minx;
float maxy;
float miny;
float factorx; /* factors used by scalex and scaley */
float factory;
int initx; /* max and min set yet? */
int inity;
} viewarr[MAXVIEWS];
extern void pass1(void);
extern void pass2(void);
extern int strisnum(char*);
#define STOF(s) ((float) atof(s))
main(int argc, char *argv[])
{
int i;
cmdname = argv[0];
while (argc > 1 && argv[1][0] == '-') {
switch (argv[1][1]) {
case 'l':
linemax = atoi(&argv[1][2]);
break;
case 's':
slotmax = atoi(&argv[1][2]);
break;
}
argc--;
argv++;
}
if (argc == 1)
infp = stdin;
else {
if ((infp = fopen(argv[1], "r")) == NULL)
error(FATAL, "can't open input file");
}
linevec = emalloc(linemax);
for (i = 0; i < linemax; i++)
linevec[i] = 0;
tempfname = tmpnam(NULL);
if ((tempfp = fopen(tempfname, "w")) == NULL)
error(FATAL, "can't open temp file");
pass1();
fclose(tempfp);
if ((tempfp = fopen(tempfname, "r")) == NULL)
error(FATAL, "can't open temp file");
pass2();
remove(tempfname); /* DEBUG */
exit(0);
}
void pass1(void)
{
int currentview; /* view number */
int erased[MAXVIEWS]; /* last line erased */
int badlabel; /* 1 if label, 0 if geom */
char savelabel[MAXSTR]; /* label */
int geomalready = 0; /* seen any geometry yet? */
struct Symbol *sp;
int i;
/* init */
currentview = 0;
for (i = 0; i < MAXVIEWS; i++)
erased[i] = 0;
opensymtab();
/* read and process file */
lineno = 0;
outlineno = 1;
while (moreinput(infp)) {
if (++lineno > linemax)
error(FATAL, "too many input lines");
/* fprintf(stderr, "STARTING TO PROCESS INPUT LINE %d\n",
lineno); */
linevec[outlineno] = NOTGEOM;
lex(infp);
if (buf[0] == '#') {
gobble2(infp);
continue;
}
badlabel = 0;
if (buf[strlen(buf)-1] == ':') {
buf[strlen(buf)-1] = '\0';
badlabel = 1;
strcpy(savelabel, buf);
sp = lookup(buf, currentview);
if (sp != NULL) {
if (linevec[sp->outnum] == currentview) {
DOERASE(sp->outnum)
}
sp->outnum = outlineno;
} else {
insert(buf, currentview, outlineno);
}
lex(infp);
}
if (eq(buf, "text")) {
DOGEOM
getoptions(TEXT);
fprintf(tempfp, "g\t0\tt\t%d\t%s",
currentview, optstring);
dox(currentview);
lex(infp);
doy(currentview);
lexstr(infp);
fprintf(tempfp, "\t%s\n", buf);
outlineno++;
} else if (eq(buf, "circle")) {
float savex, rad;
DOGEOM
getoptions(CIRCLE);
fprintf(tempfp, "g\t0\tc\t%d\t%s",
currentview, optstring);
dox(currentview);
savex = STOF(buf);
lex(infp);
doy(currentview);
lex(infp);
if (!strisnum(buf)) {
error(WARN, "radius not a number");
strcpy(buf, "0");
}
rad = STOF(buf);
if (rad < 0.0)
error(WARN, "radius is negative");
recordx(currentview, savex-rad);
recordx(currentview, savex+rad);
fprintf(tempfp, "\t%s\n", buf);
outlineno++;
gobble(infp);
} else if (eq(buf, "line") || eq(buf, "box")) {
char cmdchar;
DOGEOM
if (eq(buf, "line")) {
getoptions(LINE);
cmdchar = 'l';
} else {
getoptions(BOX);
cmdchar = 'b';
}
fprintf(tempfp, "g\t0\t%c\t%d\t%s",
cmdchar, currentview, optstring);
dox(currentview);
lex(infp);
doy(currentview);
lex(infp);
dox(currentview);
lex(infp);
doy(currentview);
fprintf(tempfp, "\n");
outlineno++;
gobble(infp);
} else if (eq(buf, "view")) {
lex(infp);
if (eq(buf,NULLSTR)) {
error(WARN, "no name in view statement");
strcpy(buf, "def.view");
}
if (viewcnt == 0 && geomalready)
error(WARN, "first view after geom");
for (i = 0; i < viewcnt; i++)
if (eq(buf, viewarr[i].name))
break;
if (i >= viewcnt) {
viewcnt++;
if (i >= MAXVIEWS)
error(FATAL, "too many views");
strcpy(viewarr[i].name, buf);
viewarr[i].initx = viewarr[i].inity = 0;
}
currentview = i;
gobble(infp);
} else if (eq(buf, "click")) {
lex(infp);
if (eq(buf,NULLSTR))
strcpy(buf, "def.click");
for (i = 0; i < clickcnt; i++)
if (eq(buf, clickname[i]))
break;
if (i >= clickcnt) {
clickcnt++;
if (i >= MAXCLICKS)
error(FATAL, "too many click names");
strcpy(clickname[i], buf);
}
fprintf(tempfp, "c\t%d\n", i);
outlineno++;
gobble(infp);
} else if (eq(buf, "erase")) {
lex(infp);
if (eq(buf,NULLSTR))
error(WARN, "no label in erase statement");
sp = lookup(buf, currentview);
if (sp != NULL) {
if (linevec[sp->outnum] == currentview) {
DOERASE(sp->outnum)
}
delete(buf, currentview);
} else {
error(WARN, "undefined label");
}
gobble(infp);
} else if (eq(buf, "clear")) {
int i, endline;
fprintf(tempfp, "b\ts\t%d\n", currentview);
endline = outlineno++;
for (i = erased[currentview]+1; i <= endline; i++) {
if (linevec[i] == currentview) {
DOERASE(i)
}
}
erased[currentview] = outlineno;
fprintf(tempfp, "b\te\t%d\n", currentview);
linevec[outlineno] = NOTGEOM;
outlineno++;
gobble(infp);
} else {
if (!eq(buf, NULLSTR))
error(WARN, "unrecognized command");
gobble(infp);
}
if (badlabel) {
error(WARN, "label on nongeometric object");
delete(savelabel, currentview);
}
}
/* tidy up */
lineno = 0;
closesymtab();
if (viewcnt == 0) {
viewcnt = 1;
strcpy(viewarr[0].name, "def.view");
}
}
void pass2(void)
{
typedef struct Slot {
union {
int i;
char *p;
} v;
} Slot;
char cmd; /* first char on a line */
int tlineno; /* line number in temp file */
int slothead; /* ptr to free list within slots */
Slot *slotarr; /* init by malloc to slotmax elmts */
int i;
int c;
int v;
struct Viewelmt *vp;
/* Init */
opensymtab();
slotarr = (Slot *) emalloc(slotmax * sizeof(Slot));
for (i = 1; i <= slotmax-2; i++)
slotarr[i].v.i = i+1;
slothead = 1;
/* Write header for output file */
for (i = 0; i < viewcnt; i++) {
vp = &viewarr[i];
printf("d\tv\t%d\t%s\t%g\t%g\t%g\t%g\n", i,
vp->name, vp->minx, vp->miny, vp->maxx, vp->maxy);
}
for (i = 0; i < clickcnt; i++)
printf("d\tc\t%d\t%s\n", i, clickname[i]);
printf("d\tp\te\n");
/* Calculate view factors used to scale */
for (v = 0; v < viewcnt; v++) {
vp = &viewarr[v];
if (vp->minx == vp->maxx) {
vp->minx = 0.0;
vp->maxx = 2*vp->maxx;
if (vp->maxx == 0.0) {
vp->minx = -1.0;
vp->maxx = 1.0;
}
}
vp->factorx = SCALE / (vp->maxx-vp->minx);
if (vp->miny == vp->maxy) {
vp->miny = 0.0;
vp->maxy = 2*vp->maxy;
if (vp->maxy == 0.0) {
vp->miny = -1.0;
vp->maxy = 1.0;
}
}
vp->factory = SCALE / (vp->maxy-vp->miny);
}
/* Read and process intermediate file */
tlineno = 0;
while ((c = getc(tempfp)) != EOF) {
tlineno++;
cmd = c;
getc(tempfp); /* gobble tab */
putchar(cmd);
putchar('\t');
if (cmd == 'g') {
int snum, vnum;
char gcmd;
char opts[OPTMAX];
float x1, y1, x2, y2;
int i1, j1, i2, j2;
char bufa[MAXBUF+100];
/* fscanf(tempfp, "%d %c %d", &snum, &gcmd, &vnum); */
getc(tempfp); /* don't need snum -- always zero */
getc(tempfp); /* gobble tab */
gcmd = getc(tempfp);
getc(tempfp); /* gobble tab */
vnum = getc(tempfp) - '0';
fscanf(tempfp, "%s %f %f", opts, &x1, &y1);
if (linevec[tlineno] != ERASED)
snum = 0;
else {
snum = slothead;
slothead = slotarr[snum].v.i;
if (slothead == 0)
error(FATAL, "ran out of slots");
}
insert(NULLSTR, tlineno, snum);
i1 = scalex(vnum, x1);
j1 = scaley(vnum, y1);
if (gcmd == 'b' || gcmd == 'l') {
fscanf(tempfp, "%f %f", &x2, &y2);
if (getc(tempfp) != '\n')
error(FATAL, "develop bug: missing newline");
i2 = scalex(vnum, x2);
j2 = scaley(vnum, y2);
if (gcmd == 'b') {
int t; /* normalize: min, max */
if (i1 > i2) { t=i1; i1=i2; i2=t; }
if (j1 > j2) { t=j1; j1=j2; j2=t; }
}
sprintf(bufa, "%d\t%c\t%d\t%s\t%d\t%d\t%d\t%d",
snum, gcmd, vnum, opts, i1, j1, i2, j2);
} else if (gcmd == 'c') {
fscanf(tempfp, "%f", &x2);
if (getc(tempfp) != '\n')
error(FATAL, "develop bug: missing newline");
i2 = scalex(vnum, x1+x2) - i1;
if (i2 < 0)
i2 = -i2;
if (i2 == 0)
i2 = 1;
sprintf(bufa, "%d\t%c\t%d\t%s\t%d\t%d\t%d",
snum, gcmd, vnum, opts, i1, j1, i2);
} else if (gcmd == 't') {
getc(tempfp); /* gobble tab */
lexrest(tempfp);
sprintf(bufa, "%d\t%c\t%d\t%s\t%d\t%d\t%s",
snum, gcmd, vnum, opts, i1, j1, buf);
} else error(FATAL, "develop bug: invalid g cmd");
slotarr[snum].v.p = emalloc(strlen(bufa)+1);
strcpy(slotarr[snum].v.p, bufa);
puts(bufa);
} else if (cmd == 'e') {
int linenum, slotnum;
Symbol *sp;
fscanf(tempfp, "%d", &linenum);
if (getc(tempfp) != '\n')
error(FATAL, "develop bug: missing newline");
sp = lookup(NULLSTR, linenum);
if (sp == NULL)
error(FATAL, "develop bug: bad erase lookup");
slotnum = sp->outnum;
puts(slotarr[slotnum].v.p);
efree(slotarr[slotnum].v.p);
slotarr[slotnum].v.i = slothead;
slothead = slotnum;
delete(NULLSTR, linenum);
} else {
lexrest(tempfp);
puts(buf);
}
}
/* tidy up */
/* closesymtab(); delete stuff in slots? */
}
void getoptions(int cmdtype) /* put options into optstring */
{
int i;
struct Geoelmt *gp;
strcpy(optstring, defaulttab[cmdtype]);
for (lex(infp); !eq(buf,NULLSTR) && !strisnum(buf); lex(infp)) {
for (i = 0; i < geooptcnt; i++) {
gp = &geooptarr[i];
if (cmdtype == gp->cmd && eq(buf, gp->name))
break;
}
if (i < geooptcnt)
optstring[gp->pos] = gp -> val;
else
error(WARN, "unrecognized option");
}
}
void dox(int view) /* handle x in input file */
{
if (!strisnum(buf)) {
error(WARN, "x value not a number");
strcpy(buf, "0");
}
recordx(view, STOF(buf));
fprintf(tempfp, "\t%s", buf);
}
void doy(int view) /* handle y in input file */
{
if (!strisnum(buf)) {
error(WARN, "y value not a number");
strcpy(buf, "0");
}
recordy(view, STOF(buf));
fprintf(tempfp, "\t%s", buf);
}
/* symbol.c:
General: functions for mapping {string} x {int} -> {int}
In pass 1: {name} x {viewnum} -> {int file line num}
In pass 2: {} x {int file line num} -> {slot num}
Therefore be careful with blank strings
*/
#define steq(s, n, p) (p->innum == n && ((p->instr == s) || eq(p->instr,s)))
#define SIZE 2053
Symbol *head[SIZE];
int hash(char *s, int n) /* form hash value */
{
int hashval;
Symbol *lookup(char *s, int n) /* return element with s, n */
{
Symbol *p;
for (p = head[hash(s, n)]; p != NULL; p = p->next)
if (steq(s, n, p))
return p;
return NULL;
}
void insert(char *s, int n, int v) /* insert s, n with value v */
{
Symbol *p;
char *q;
int i;
void delete(char *s, int n) /* remove s, n */
{
Symbol *p, *pp;
int i;
/* fprintf(stderr, "Deleting: |%s|, %d with hash %d\n",
s, n, hash(s,n)); */
i = hash(s, n);
pp = NULL;
for (p = head[i]; p != NULL; p = p->next) {
if (steq(s, n, p))
break;
pp = p;
}
if (p == NULL)
error(FATAL, "symtab bug: bad delete");
if (p->instr != NULLSTR)
efree(p->instr);
if (pp == NULL) {
head[i] = p->next;
} else {
pp->next = p->next;
}
efree((char *) p);
}
void opensymtab(void) /* init table */
{
int i;
for (i = 0; i < SIZE; i++)
head[i] = NULL;
}
void closesymtab(void) /* reclaim storage */
{
int i;
Symbol *p, *np;
for (i = 0; i < SIZE; i++)
for (p = head[i]; p != NULL; p = np) {
if (p->instr != NULLSTR)
efree(p->instr);
np = p->next;
efree((char *) p);
}
}
/* util.c: utility routines */
void error(int f, char *s)
{
fprintf(stderr, "%s: %s\n", cmdname, s);
if (lineno)
fprintf(stderr, " source line number %d\n", lineno);
if (f)
exit(1);
}
int strisnum(char *p) /* 1 if string p represents a float */
{
int digits = 0;
int n;
/* REG EXPR: */
if (*p == '-') /* -? */
p++;
while (isdigit(*p)) {
digits++; /* [0-9]* */
p++;
}
if (*p == '.') { /* (.[0-9]*)? */
p++;
while (isdigit(*p)) {
digits++;
p++;
}
}
if (digits == 0) /* >0 digits */
return 0;
if (tolower(*p) == 'e') { /* ([eE] */
*p++;
if (*p == '+' || *p == '-') /* [+-]? */
p++;
digits = 1;
if (!isdigit(*p)) /* [0-9] */
return 0;
n = *p++ - '0';
while (isdigit(*p)) { /* [0-9]+ */
digits++;
n = 10*n + *p++ - '0';
if (n > 30)
return 0;
}
if (digits == 0)
return 0; /* )? */
}
return (*p == '\0');
}
int moreinput(FILE *fp)
{
int c;
if (lexsaweof)
return 0;
c = getc(fp);
if (c == EOF)
return 0;
ungetc(c, fp);
return 1;
}
#define CHECKEOF if (c == EOF) {\
lexsaweof = 1;\
error(WARN, "file does not end with newline");\
return;\
}
void lex(FILE *fp) /* put next string of non-white into buf */
{
int c;
char *p, *danger;
danger = &buf[MAXSTR-1];
while ((c = getc(fp)) == ' ' || c == '\t')
;
CHECKEOF
if (c == '\n') {
ungetc(c, fp);
buf[0] = '\0';
return;
}
p = buf;
*p++ = c;
while ((c = getc(fp)) != EOF && c != ' ' && c != '\t' && c != '\n') {
if (p < danger)
*p++ = c;
else if (p == danger)
error(WARN, "string too long -- truncated");
}
*p = '\0';
CHECKEOF
if (c == '\n')
ungetc(c, fp);
/* fprintf(stderr, "lex returning: %s\n", buf); */
}
void lexstr(FILE *fp) /* like lex, but go til newline and handle quotes */
{
int c;
int quoted = 0;
char *p, *danger;
while ((c = getc(fp)) == ' ' || c == '\t')
;
CHECKEOF
if (c == '\"') {
quoted = 1;
c = getc(fp);
CHECKEOF
}
if (c == '\n') {
buf[0] = '\0';
return;
}
p = buf;
*p++ = c;
danger = &buf[MAXBUF-1];
while ((c = getc(fp)) != EOF && c != '\n') {
if (p < danger)
*p++ = c;
else if (p == danger)
error(WARN, "text string too long -- truncated");
}
CHECKEOF
*p = '\0';
if (quoted && *(--p) == '\"')
*p = '\0';
}
void lexrest(FILE *fp) /* get rest of line into buf */
/* no error checking; error free from Pass 1 */
{
int c;
char *p;
p = buf;
while ((c = getc(fp)) != EOF && c != '\n') {
*p++ = c;
}
*p = '\0';
}
void gobble(FILE *fp) /* chew space til EOF; complain if nonwhite */
{
int c;
while ((c = getc(fp)) == ' ' || c == '\t')
;
CHECKEOF
if (c != '\n') {
error(WARN, "garbage at end of line");
while ((c = getc(fp)) != EOF && c != '\n')
;
CHECKEOF
}
}
void gobble2(FILE *fp) /* chew space til EOF, no complaints */
{
int c;
while ((c = getc(fp)) != EOF && c != '\n')
;
CHECKEOF
}
#define WANTPROF 0
#if WANTPROF
int hmallocinit;
FILE *hmallocfp;
#endif
#if WANTPROF
if (hmallocinit == 0) {
hmallocinit = 1;
if ((hmallocfp = fopen("/tmp/malloc.hist", "w")) == NULL)
error(FATAL, "malloc history bug: can't open file");
}
#endif
p = malloc((unsigned) n);
if (p == NULL)
error(FATAL, "out of memory");
#if WANTPROF
fprintf(hmallocfp, "m\t%d\t%d\n", (int) p, n);
#endif
return p;
}
void efree(char *p) /* personal version of free to match emalloc */
{
#if WANTPROF
if (hmallocinit == 0)
error(FATAL, "malloc history bug: first free before malloc");
fprintf(hmallocfp, "f\t%d\n", (int) p);
#endif
free(p);
}