#define VERSION "0.99f"
/*
flow.c 22/1/93 (C) 2005 Terry Brown
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
flow - a flow-chart -> LaTeX generator. Parses a file in a flow-chart
specification language, then produces a corresponding LaTeX
pic environment.
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
typedef int bool;
#define TRUE 1
#define FALSE 0
typedef struct {float x,y;} coord;
typedef struct {char name[80];
int Params;
coord size;
bool HasText;
} FlowCom;
#define ParamLen 120
#define LineLen 120
typedef char param[ParamLen];
#define MaxBoxes 2048 /* could not possibly have more than this */
#define Commands 16
FlowCom fcom[Commands] = {
{ "SetTrack", 1, {0,0}, FALSE },
{ "Up", 1, {0,0}, FALSE },
{ "Down", 1, {0,0}, FALSE },
{ "Left", 1, {0,0}, FALSE },
{ "Right", 1, {0,0}, FALSE },
{ "Box", 0, {4,2}, TRUE },
{ "Oval", 0, {4,2}, TRUE },
{ "Choice", 4, {4,4}, TRUE },
{ "Tag", 0, {0,0}, FALSE },
{ "ToTag", 0, {0,0}, FALSE },
{ "Scale", 2, {0,0}, FALSE },
{ "Tilt", 0, {4,2}, TRUE },
{ "Text", 0, {4,2}, TRUE },
{ "TxtPos", 4, {0,0}, FALSE },
{ "Skip", 2, {0,0}, FALSE },
{ "%", 0, {0,0}, FALSE }
};
typedef enum {SetTrack,
Up,
Down,
Left,
Right,
Box,
Oval,
Choice,
Tag,
ToTag,
Scale,
Tilt,
Text,
TxtPos,
Skip,
Comment
} TheCommands;
typedef enum {ArrowS, LineS, NoneS} trackSymb;
typedef enum {UpD, DownD, LeftD, RightD} direcs;
typedef struct ATAG {coord pos;
coord size;
struct ATAG *next;
} aTag;
typedef struct {
coord min;
coord max;
} boundingBox;
/* state / position tracking variables */
boundingBox pic = {{0,0},{0,0}};
coord Coords[MaxBoxes] = {{0,0}}; /* just initialise first coord */
int CurCoord = 0;
int CurTrack = ArrowS;
int CurDirec = DownD;
coord CurScale = {1.,1.};
coord CurSize = {0.,0.};
aTag *CurTag = NULL;
char CurPos[20] = "[c]";
char CurBoxPos[20] = "[c]";
char leader[20]="";
char tailer[20]="";
float TrackX = 1.;
float TrackY = 1.;
int InputLine = 0;
char line[LineLen];
float VertGap=1.;
float HorzGap=1.;
FILE *inFile, *outFile; /* global input output streams */
int doText();
void checkBounds(boundingBox *b, coord *c) {
if (b->min.x > c->x) b->min.x = c->x;
if (b->max.x < c->x) b->max.x = c->x;
if (b->min.y > c->y) b->min.y = c->y;
if (b->max.y < c->y) b->max.y = c->y;
}
void checkBoundsRng(
boundingBox *b,
float x,
float y,
float sx,
float sy
) {
if (b->min.x > x) b->min.x = x;
if (b->max.x < x+sx) b->max.x = x+sx;
if (b->min.y > y) b->min.y = y;
if (b->max.y < y+sy) b->max.y = y+sy;
}
int getCommand(char line[], param plist) {
/*
tries to interpret the next line of inFile as a command, returns -1 if it can't
*/
int i=0, command = -1;
while (i<Commands) {
if ( strncmp(fcom[i].name,line,strlen(fcom[i].name)) == 0) {
command = i;
break;
}
i++;
}
if (command == -1) {
if (!feof(inFile) && line[0] != '\n')
fprintf(stderr,"flow error : can't interpret line %d\n",InputLine);
return command;
}
strncpy(plist, line+strlen(fcom[command].name), ParamLen);
return command;
}
int doCommand(int command, param pList) {
/*
output the LaTeX bits for this command, updating the coords, coord list
etc. as required
*/
static int init = 0;
aTag *tempTag;
char params[10][80];
char *trackStr = "vector";
float dimen,x,y,x1,y1;
int i,xs,ys;
coord t;
dimen = 0.;
params[0][0]=0; /* so Up / Down / Left / Right can find *'s for line
drawing */
if (CurTrack == LineS) trackStr = "line";
if (fcom[command].HasText) {
if ((command != Choice && sscanf(pList,"%f %f",&x,&y) == 2) ||
(command == Choice && sscanf(pList,"%s %s %s %s %f %f",
params[0],
params[0],
params[0],
params[0],
&x,&y) == 6)) {
fcom[command].size.x = x;
fcom[command].size.y = y;
}
}
if (!init && CurSize.x == 0. && CurSize.y == 0.) {
CurSize.x = fcom[command].size.x*CurScale.x;
CurSize.y = fcom[command].size.y*CurScale.y;
}
if ( init && (fcom[command].size.x != 0 || fcom[command].size.y != 0 )) {
switch (CurDirec) {
case DownD :
t.x = Coords[CurCoord].x+CurSize.x/2;
t.y = Coords[CurCoord].y-CurSize.y;
if (CurTrack != NoneS)
fprintf(outFile,"\\put(%3.4f,%3.4f){\\%s(%d,%d){%3.4f}}\n",
t.x,
t.y,
trackStr,
0,-1,
VertGap);
checkBounds(&pic,&t);
Coords[CurCoord+1].x = Coords[CurCoord].x + CurSize.x/2 -
fcom[command].size.x*CurScale.x / 2;
Coords[CurCoord+1].y = Coords[CurCoord].y - CurSize.y
- VertGap;
checkBounds(&pic,Coords+CurCoord+1);
break;
case UpD :
t.x = Coords[CurCoord].x+CurSize.x/2;
t.y = Coords[CurCoord].y;
if (CurTrack != NoneS)
fprintf(outFile,"\\put(%3.4f,%3.4f){\\%s(%d,%d){%3.4f}}\n",
t.x,
t.y,
trackStr,
0,1,
VertGap);
checkBounds(&pic,&t);
Coords[CurCoord+1].x = Coords[CurCoord].x + CurSize.x/2 -
fcom[command].size.x*CurScale.x / 2;
Coords[CurCoord+1].y = Coords[CurCoord].y +
fcom[command].size.y*CurScale.y
+ VertGap;
checkBounds(&pic,Coords+CurCoord+1);
break;
case RightD :
t.x = Coords[CurCoord].x+CurSize.x;
t.y = Coords[CurCoord].y-CurSize.y/2;
if (CurTrack != NoneS)
fprintf(outFile,"\\put(%3.4f,%3.4f){\\%s(%d,%d){%3.4f}}\n",
t.x,
t.y,
trackStr,
1,0,
HorzGap);
checkBounds(&pic,&t);
Coords[CurCoord+1].x = Coords[CurCoord].x + CurSize.x
+ HorzGap;
Coords[CurCoord+1].y = Coords[CurCoord].y - CurSize.y/2 +
fcom[command].size.y*CurScale.y / 2;
checkBounds(&pic,Coords+CurCoord+1);
break;
case LeftD :
t.x = Coords[CurCoord].x;
t.y = Coords[CurCoord].y-CurSize.y/2;
if (CurTrack != NoneS)
fprintf(outFile,"\\put(%3.4f,%3.4f){\\%s(%d,%d){%3.4f}}\n",
t.x,
t.y,
trackStr,
-1,0,
HorzGap);
checkBounds(&pic,&t);
Coords[CurCoord+1].x = Coords[CurCoord].x -
fcom[command].size.x*CurScale.x
- HorzGap;
Coords[CurCoord+1].y = Coords[CurCoord].y - CurSize.y/2 +
fcom[command].size.y*CurScale.y / 2;
checkBounds(&pic,Coords+CurCoord+1);
break;
default:
break;
}
CurCoord++;
CurSize.x = fcom[command].size.x*CurScale.x;
CurSize.y = fcom[command].size.y*CurScale.y;
}
switch (command) {
case Skip :
if (sscanf(pList,"%f %f %f %f",&x,&y,&x1,&y1) == 4) {
VertGap = y;
HorzGap = x;
TrackX = x1;
TrackY = y1;
}
break;
case TxtPos :
CurPos[0]=CurBoxPos[0]=leader[0]=tailer[0]=0;
sscanf(pList,"%s %s %s %s",CurPos,CurBoxPos,leader,tailer);
break;
case Box :
init=1;
fprintf(outFile,"\\put(%3.4f,%3.4f){\\framebox(%3.4f,%3.4f)%s{\\shortstack%s{\n",
Coords[CurCoord].x,
Coords[CurCoord].y-CurSize.y,
CurSize.x,
CurSize.y,
CurBoxPos,
CurPos);
doText();
fprintf(outFile,"}}}\n");
checkBoundsRng(
&pic,
Coords[CurCoord].x,
Coords[CurCoord].y-CurSize.y,
CurSize.x,
CurSize.y
);
break;
case Tilt :
init=1;
fprintf(outFile,"\\put(%3.4f,%3.4f){\\makebox(%3.4f,%3.4f)%s{\\shortstack%s{\n",
Coords[CurCoord].x,
Coords[CurCoord].y-CurSize.y,
CurSize.x,
CurSize.y,
CurBoxPos,
CurPos);
doText();
fprintf(outFile,"}}}\n");
fprintf(outFile,"\\put(%3.4f,%3.4f){\\line(%d,%d){%3.4f}}\n",
Coords[CurCoord].x+1./6.*CurSize.y,
Coords[CurCoord].y,
1,0,
CurSize.x);
fprintf(outFile,"\\put(%3.4f,%3.4f){\\line(%d,%d){%3.4f}}\n",
Coords[CurCoord].x-1./6.*CurSize.y,
Coords[CurCoord].y-CurSize.y,
1,0,
CurSize.x);
fprintf(outFile,"\\put(%3.4f,%3.4f){\\line(%d,%d){%3.4f}}\n",
Coords[CurCoord].x-1./6.*CurSize.y,
Coords[CurCoord].y-CurSize.y,
1,3,
CurSize.y*1./3.);
fprintf(outFile,"\\put(%3.4f,%3.4f){\\line(%d,%d){%3.4f}}\n",
Coords[CurCoord].x-1./6.*CurSize.y+CurSize.x,
Coords[CurCoord].y-CurSize.y,
1,3,
CurSize.y*1./3.);
checkBoundsRng(
&pic,
Coords[CurCoord].x-1./6.*CurSize.y,
Coords[CurCoord].y-CurSize.y,
CurSize.x + 1./6.*CurSize.y,
CurSize.y
);
break;
case Text :
init=1;
fprintf(outFile,"\\put(%3.4f,%3.4f){\\makebox(%3.4f,%3.4f)%s{\\shortstack%s{\n",
Coords[CurCoord].x,
Coords[CurCoord].y-CurSize.y,
CurSize.x,
CurSize.y,
CurBoxPos,
CurPos);
doText();
fprintf(outFile,"}}}\n");
checkBoundsRng(
&pic,
Coords[CurCoord].x,
Coords[CurCoord].y-CurSize.y,
CurSize.x,
CurSize.y
);
break;
case Oval :
init=1;
fprintf(outFile,"\\put(%3.4f,%3.4f){\\oval(%3.4f,%3.4f)}\n",
Coords[CurCoord].x+CurSize.x/2,
Coords[CurCoord].y-CurSize.y/2,
CurSize.x,
CurSize.y );
fprintf(outFile,"\\put(%3.4f,%3.4f){\\makebox(%3.4f,%3.4f)%s{\\shortstack%s{\n",
Coords[CurCoord].x,
Coords[CurCoord].y-CurSize.y,
CurSize.x,
CurSize.y,
CurBoxPos,
CurPos );
doText();
fprintf(outFile,"}}}\n");
checkBoundsRng(
&pic,
Coords[CurCoord].x,
Coords[CurCoord].y-CurSize.y,
CurSize.x,
CurSize.y
);
break;
case Choice :
init=1;
xs = (int)CurSize.x; ys = (int)CurSize.y;
for (i = (xs>ys) ? xs : ys; i>1; i--) {
if ( (xs % i) == 0 && (ys % i) == 0 ) {
xs /= i;
ys /= i;
i = (xs>ys) ? xs : ys;
}
}
if (xs>6) {
fprintf(stderr,"Flow warning - illegal Choice aspect ratio\n");
xs = 6;
}
if (ys>6) {
fprintf(stderr,"Flow warning - illegal Choice aspect ratio\n");
ys = 6;
}
fprintf(outFile,"\\put(%3.4f,%3.4f){\\line(%d,%d){%3.4f}}\n",
Coords[CurCoord].x,
Coords[CurCoord].y-CurSize.y/2,
xs,ys,CurSize.x/2
);
fprintf(outFile,"\\put(%3.4f,%3.4f){\\line(%d,%d){%3.4f}}\n",
Coords[CurCoord].x,
Coords[CurCoord].y-CurSize.y/2,
xs,-ys,CurSize.x/2
);
fprintf(outFile,"\\put(%3.4f,%3.4f){\\line(%d,%d){%3.4f}}\n",
Coords[CurCoord].x+CurSize.x,
Coords[CurCoord].y-CurSize.y/2,
-xs,-ys,CurSize.x/2
);
fprintf(outFile,"\\put(%3.4f,%3.4f){\\line(%d,%d){%3.4f}}\n",
Coords[CurCoord].x+CurSize.x,
Coords[CurCoord].y-CurSize.y/2,
-xs,ys,CurSize.x/2
);
fprintf(outFile,"\\put(%3.4f,%3.4f){\\makebox(%3.4f,%3.4f)%s{\\shortstack%s{\n",
Coords[CurCoord].x,
Coords[CurCoord].y-CurSize.y,
CurSize.x,
CurSize.y,
CurBoxPos,
CurPos );
doText();
fprintf(outFile,"}}}\n");
sscanf(pList,"%s %s %s %s",params[0],params[1],params[2],params[3]);
if (params[0][0] != '.')
fprintf(outFile,"\\put(%3.4f,%3.4f){\\makebox(0,0)[lt]{%s}}\n",
Coords[CurCoord].x+
CurSize.x*0.65,
Coords[CurCoord].y,
params[0]
);
if (params[1][0] != '.')
fprintf(outFile,"\\put(%3.4f,%3.4f){\\makebox(0,0)[rt]{%s}}\n",
Coords[CurCoord].x,
Coords[CurCoord].y-
CurSize.y/2.*0.7,
params[1]
);
if (params[2][0] != '.')
fprintf(outFile,"\\put(%3.4f,%3.4f){\\makebox(0,0)[lt]{%s}}\n",
Coords[CurCoord].x+
CurSize.x,
Coords[CurCoord].y-
CurSize.y/2.*0.7,
params[2]
);
if (params[3][0] != '.')
fprintf(outFile,"\\put(%3.4f,%3.4f){\\makebox(0,0)[lb]{%s}}\n",
Coords[CurCoord].x+
CurSize.x*0.65,
Coords[CurCoord].y-
CurSize.y,
params[3]
);
checkBoundsRng(
&pic,
Coords[CurCoord].x,
Coords[CurCoord].y-CurSize.y,
CurSize.x,
CurSize.y
);
break;
case SetTrack :
sscanf(pList,"%s",params[0]);
if ( strcmp("arrow",params[0]) == 0)
CurTrack = ArrowS;
if ( strcmp("line",params[0]) == 0)
CurTrack = LineS;
if ( strcmp("none",params[0]) == 0)
CurTrack = NoneS;
break;
case Scale :
sscanf(pList,"%f %f",&CurScale.x,&CurScale.y);
break;
case Tag :
tempTag = (aTag*)malloc(sizeof(aTag));
tempTag->size.x = CurSize.x;
tempTag->size.y = CurSize.y;
tempTag->pos.x = Coords[CurCoord].x;
tempTag->pos.y = Coords[CurCoord].y;
tempTag->next = CurTag;
CurTag = tempTag;
break;
case ToTag :
if (CurTag == NULL) {
fprintf(stderr,"flow error - Tag stack empty \n");
break;
}
tempTag = CurTag;
CurSize.x = tempTag->size.x;
CurSize.y = tempTag->size.y;
Coords[CurCoord].x = tempTag->pos.x;
Coords[CurCoord].y = tempTag->pos.y;
CurTag = tempTag->next;
free(tempTag);
break;
case Right :
CurDirec = RightD;
if (sscanf(pList,"%f %19s",&dimen,params[0])>=1) {
init=1;
dimen *= TrackX;
if (CurTrack != NoneS)
fprintf(outFile,"\\put(%3.4f,%3.4f){\\%s(%d,%d){%3.4f}}\n",
Coords[CurCoord].x+CurSize.x,
Coords[CurCoord].y-CurSize.y/2,
(params[0][0]=='*')?"vector":"line",
1,0,
dimen);
Coords[CurCoord+1].x = Coords[CurCoord].x + CurSize.x +
dimen;
Coords[CurCoord+1].y = Coords[CurCoord].y - CurSize.y/2;
CurCoord++;
CurSize = fcom[command].size;
}
checkBounds(&pic,&(Coords[CurCoord]));
break;
case Down :
CurDirec = DownD;
if (sscanf(pList,"%f %19s",&dimen,params[0])>=1) {
init=1;
dimen *= TrackY;
if (CurTrack != NoneS)
fprintf(outFile,"\\put(%3.4f,%3.4f){\\%s(%d,%d){%3.4f}}\n",
Coords[CurCoord].x+CurSize.x/2,
Coords[CurCoord].y-CurSize.y,
(params[0][0]=='*')?"vector":"line",
0,-1,
dimen);
Coords[CurCoord+1].x = Coords[CurCoord].x + CurSize.x/2 -
fcom[command].size.x / 2;
Coords[CurCoord+1].y = Coords[CurCoord].y - CurSize.y
- dimen;
CurCoord++;
CurSize = fcom[command].size;
}
checkBounds(&pic,&(Coords[CurCoord]));
break;
case Left :
CurDirec = LeftD;
if (sscanf(pList,"%f %19s",&dimen,params[0])>=1) {
init=1;
dimen *= TrackX;
if (CurTrack != NoneS)
fprintf(outFile,"\\put(%3.4f,%3.4f){\\%s(%d,%d){%3.4f}}\n",
Coords[CurCoord].x,
Coords[CurCoord].y-CurSize.y/2,
(params[0][0]=='*')?"vector":"line",
-1,0,
dimen);
Coords[CurCoord+1].x = Coords[CurCoord].x -
dimen;
Coords[CurCoord+1].y = Coords[CurCoord].y - CurSize.y/2;
CurCoord++;
CurSize = fcom[command].size;
}
checkBounds(&pic,&(Coords[CurCoord]));
break;
case Up :
CurDirec = UpD;
if (sscanf(pList,"%f %19s",&dimen,params[0])>=1) {
init=1;
dimen *= TrackY;
if (CurTrack != NoneS)
fprintf(outFile,"\\put(%3.4f,%3.4f){\\%s(%d,%d){%3.4f}}\n",
Coords[CurCoord].x+CurSize.x/2,
Coords[CurCoord].y,
(params[0][0]=='*')?"vector":"line",
0,1,
dimen);
Coords[CurCoord+1].x = Coords[CurCoord].x + CurSize.x/2 -
fcom[command].size.x / 2;
Coords[CurCoord+1].y = Coords[CurCoord].y +
dimen;
CurCoord++;
CurSize = fcom[command].size;
}
checkBounds(&pic,&(Coords[CurCoord]));
break;
case Comment :
break;
default:
fprintf(stderr,"flow error : unknown command %4d\n",command);
return 0;
}
if (command != Scale) CurScale.x = CurScale.y = 1.;
if (fcom[command].HasText == FALSE) {
line[0]=0;
fgets(line,LineLen,inFile);
InputLine++;
}
return 1;
}
int doText() {
/*
output text for those commands that require it, spit out all lines that start
with white space (0x20, 0x09)
*/
int i;
line[0]=0;
fgets(line,LineLen,inFile);
InputLine++;
while (!feof(inFile) && (
line[0] == 0x20 ||
line[0] == 0x09 )
) {
line[strlen(line)-1]=0;
for (i=0; i<strlen(line) &&
(line[i] == 0x09 || line[i] == 0x20); i++);
fprintf(outFile,"%s%s%s",leader,line+i,tailer);
line[0]=0;
fgets(line,LineLen,inFile);
InputLine++;
if (line[0] == 0x20 || line[0] == 0x09 )
fprintf(outFile,"\\\\\n");
else
fprintf(outFile,"\n");
}
return 1;
}
void applyPicWrapper(FILE *inFile, FILE *outFile) {
char buf[1024];
fprintf(outFile,"\\begin{picture}(%f,%f)(%f,%f)\n",
pic.max.x - pic.min.x,
pic.max.y - pic.min.y,
pic.min.x,
pic.min.y
);
while (!feof(inFile)) {
fwrite(buf,fread(buf,1,1024,inFile),1,outFile);
}
fprintf(outFile,"\\end{picture}\n");
}
int main(int argc, char **argv) {
param params;
int command=0;
char tmpfileNm[80];
inFile = stdin; outFile = stdout;
tmpnam(tmpfileNm);
outFile = fopen(tmpfileNm,"w");
if (argc > 1) {
inFile = fopen(argv[1],"r");
if (inFile == NULL) {
fprintf(stderr,"Couldn't open input file %s\n",argv[1]);
exit(0);
}
}
line[0]=0;
fgets(line,LineLen,inFile);
InputLine++;
fprintf(outFile,"%% picture environment flowchart generated by flow ");
fprintf(outFile,"%s\n",VERSION);
while (command != -1) {
command = getCommand(line,params);
if (command != -1)
if (!doCommand(command, params)) return 10;
}
fclose(inFile);
fclose(outFile);
outFile = stdout;
if (argc > 2) {
outFile = fopen(argv[2],"w");
if (outFile == NULL) {
fprintf(stderr,"Couldn't open output file %s\n",argv[2]);
fclose(inFile);
exit(0);
}
}
inFile = fopen(tmpfileNm,"r");
applyPicWrapper(inFile,outFile);
if (outFile != stdout) fclose(outFile);
fclose(inFile);
remove(tmpfileNm);
exit(0);
return 0; /* just to suppress the warning */
}