4
* $Id: gread.c,v 1.1 2003/03/08 15:26:45 travo Exp $
6
* Define Drauing gread read routine for GIST
9
/* Copyright (c) 1994. The Regents of the University of California.
10
All rights reserved. */
17
extern void GdKillSystems(void); /* defined in draw.c */
20
#define GISTPATH "~/gist:~/Gist:/usr/local/lib/gist"
22
char *gistPathDefault= GISTPATH;
24
/* ------------------------------------------------------------------------ */
32
} modelSystem, tempSystem;
36
GpTextAttribs textStyle;
37
int nchars, nlines, nwrap;
40
static char *FormGistPath(void);
41
static char *FormFullName(char *gistPath, const char *name);
42
static void FormatError(p_file *fp, const char *name, const char *id);
43
static int SnarfColor(char *token);
44
static int SnarfRGB(char *token, GpColorCell *cell);
45
static int SnarfGray(GpColorCell *cell, int lookingFor4);
46
static char *WhiteSkip(char *input);
47
static char *DelimitRead(char *input, int *closed, int nlOK);
48
static char *ColRead(char *input, GpColorCell *dest);
49
static char *IntRead(char *input, int *dest);
50
static char *RealRead(char *input, GpReal *dest);
51
static char *StringRead(char *input, char **dest);
52
static char *MemberRead(char *input, char **member);
53
static char *ArrayRead(char *input, GpReal *dest, int narray);
54
static char *LineRead(char *input, GpLineAttribs *dest);
55
static char *TextRead(char *input, GpTextAttribs *dest);
56
static char *AxisRead(char *input, GaAxisStyle *dest);
57
static char *TickRead(char *input, GaTickStyle *dest);
58
static char *SystemRead(char *input, struct GsysRead *dest);
59
static char *LegendsRead(char *input, struct GlegRead *dest);
61
/* ------------------------------------------------------------------------ */
62
/* A palette file (.gp) or style file (.gs) will be found if it:
64
1. Is in the current working directory.
65
2. Is the first file of its name encountered in any of the directories
66
named in the GISTPATH environment variable.
67
3. Ditto for the GISTPATH default string built in at compile time.
68
Note that the environment variable is in addition to the compile
69
time variable, not in place of it.
71
The path name list should consist of directory names (with or without
72
trailing '/'), separated by ':' with no intervening whitespace. The
73
symbol '~', if it is the first symbol of a directory name, will be
74
expanded to the value of the HOME environment variable, but other
75
environment variable expansions are not recognized.
77
If the given filename begins with '/' the path search is
78
not done. '~' is NOT recognized in the given filename.
84
static char *scratch = 0;
85
static char *gist_path = 0;
87
static char *FormGistPath(void)
90
char *gistPath = getenv("GISTPATH");
91
int len = gistPath? strlen(gistPath) : 0;
92
int len0 = g_argv0? strlen(g_argv0) : 0;
93
int lend = gistPathDefault? strlen(gistPathDefault) : 0;
96
/* Get enough scratch space to hold
97
the concatenation of the GISTPATH environment variable and the
98
GISTPATH compile-time option, and a fallback computed from argv[0] */
99
gist_path = p_malloc(len+len0+lend+4);
100
if (!gist_path) return 0;
104
strcpy(place, gistPath);
108
strcpy(place, gistPathDefault);
110
/* back up to sibling of directory containing executable */
111
for (len=len0-1 ; len>0 ; len--) if (g_argv0[len]=='/') break;
112
for (len-- ; len>0 ; len--) if (g_argv0[len]=='/') break;
114
/* tack /g/ sibling of executable directory onto path */
116
strncpy(place, g_argv0, ++len);
122
scratch = p_malloc(1028);
123
if (!scratch) return 0;
127
static char *FormFullName(char *gistPath, const char *name)
129
int nlen= strlen(name);
134
/* Skip past any components of the GISTPATH which result in impossibly
136
do len= strcspn(gistPath, ":"); while (!len);
137
/* handle MS Windows drive letters */
138
if (len==1 && gistPath[1]==':' &&
139
((gistPath[0]>='A' && gistPath[0]<='Z') ||
140
(gistPath[0]>='a' && gistPath[0]<='z')))
141
len = 2+strcspn(gistPath+2, ":");
146
if (gistPath[0]=='~') {
147
/* Get name of home directory from HOME environment variable */
148
char *home= getenv("HOME");
150
if (home && (hlen= strlen(home))<1024) {
159
if (elen+nlen<1023) break;
165
strncpy(now, gistPath, len);
167
if (now[-1]!='/') *now++= '/';
173
return gistPath+len + strspn(gistPath+len, ":");
176
extern p_file *GistOpen(const char *name);
177
p_file *GistOpen(const char *name)
182
f= p_fopen(name, "r");
184
if (!f && name[0]!='/') {
185
/* Try to find relative file names somewhere on GISTPATH or, failing
186
that, in the default directory specified at compile time. */
187
char *gistPath= FormGistPath();
190
gistPath= FormFullName(gistPath, name);
191
f= p_fopen(scratch, "r");
192
} while (!f && gistPath[0]);
198
strcpy(gistError, "unable to open file ");
199
strncat(gistError, name, 100);
204
static void FormatError(p_file *fp, const char *name, const char *id)
207
strcpy(gistError, id);
208
strcat(gistError, " file format error in ");
209
strncat(gistError, name, 127-strlen(gistError));
212
static char line[137]; /* longest allowed line is 136 characters */
214
/* ------------------------------------------------------------------------ */
216
static int SnarfColor(char *token)
217
/* returns -1 if not unsigned char, -2 if missing */
222
if (!token) token= strtok(0, " \t\n");
223
if (token) color= (int)strtol(token, &suffix, 0);
225
if (suffix==token || color<0 || color>255) return -1;
229
static int SnarfRGB(char *token, GpColorCell *cell)
231
int red, blue, green;
232
red= SnarfColor(token);
234
green= SnarfColor(0);
235
if (green<0) return 1;
237
if (blue<0) return 1;
238
cell[0] = P_RGB(red, green, blue);
243
static int SnarfGray(GpColorCell *cell, int lookingFor4)
245
int gray= SnarfColor(0);
246
if (gray==-2) return lookingFor4;
247
else if (gray<0 || !lookingFor4) return 1;
248
/* cell->gray= gray; */
252
int GpReadPalette(Engine *engine, const char *gpFile,
253
GpColorCell **palette, int maxColors)
255
char *token, *suffix;
257
int iColor= -1, nColors= 0, ntsc= 0, lookingFor4= 0;
258
p_file *gp= GistOpen(gpFile);
263
for (;;) { /* loop on lines in file */
264
token= p_fgets(gp, line, 137);
265
if (!token) break; /* eof (or error) */
267
token= strtok(token, " =\t\n");
268
if (!token || token[0]=='#') continue; /* blank or comment line */
272
if (strcmp(token, "ncolors")==0) dest= &nColors;
273
else if (strcmp(token, "ntsc")==0) dest= &ntsc;
276
/* this is ncolors=... or ntsc=... line */
277
token= strtok(0, " =\t\n");
278
if (token) *dest= (int)strtol(token, &suffix, 0);
280
if (suffix==token || strtok(0, " \t\n")) goto err;
283
/* this must be the first rgb line */
286
/* previous ncolors= is mandatory so palette can be allocated */
287
if (nColors<=0) goto err;
288
pal= p_malloc(sizeof(GpColorCell)*nColors);
291
/* if first rgb line has 4 numbers, all must have 4, else 3 */
292
if (SnarfRGB(token, pal)) goto err;
294
if (gray==-1) goto err;
297
/* pal->gray= gray; */
298
if (SnarfGray(pal, 0)) goto err; /* just check for eol */
301
/* already got eol */
307
} else if (iColor<nColors) {
308
/* read next rgb line */
309
if (SnarfRGB(token, pal+iColor)) goto err;
310
if (SnarfGray(pal+iColor, lookingFor4)) goto err;
314
goto err; /* too many rgb for specified ncolors */
317
if (iColor<nColors) goto err; /* too few rgb for specified ncolors */
321
if (nColors>maxColors && maxColors>1) {
322
/* attempt to rescale the palette to maxColors */
323
int oldCell, newCell, nextCell, r, g, b;
324
double ratio= ((double)(nColors-1))/((double)(maxColors-1));
325
double frac, frac1, old= 0.0;
326
for (newCell=0 ; newCell<maxColors ; newCell++) {
329
if (nextCell>=nColors) nextCell= oldCell;
330
frac= old-(double)oldCell;
332
r = (int)(frac1*P_R(pal[oldCell]) + frac*P_R(pal[nextCell]));
333
g = (int)(frac1*P_G(pal[oldCell]) + frac*P_G(pal[nextCell]));
334
b = (int)(frac1*P_B(pal[oldCell]) + frac*P_B(pal[nextCell]));
335
pal[newCell] = P_RGB(r, g, b);
337
pal[newCell].gray= frac1*pal[oldCell].gray+frac*pal[nextCell].gray;*/
344
/* gray values were not explicitly specified */
345
if (ntsc) GpPutNTSC(nColors, pal);
346
else GpPutGray(nColors, pal);
350
iColor= GpSetPalette(engine, pal, nColors);
351
return iColor>nColors? nColors : iColor;
354
FormatError(gp, gpFile, "palette");
355
if (pal) p_free(pal);
359
strcpy(gistError, "memory manager failed to get space for palette");
364
/* ------------------------------------------------------------------------ */
366
#define OPEN_BRACE '{'
367
#define CLOSE_BRACE '}'
369
static p_file *gs= 0;
371
static char *WhiteSkip(char *input)
373
input+= strspn(input, " \t\n");
375
while (!input[0] || input[0]=='#') { /* rest of line missing or comment */
376
input= p_fgets(gs, line, 137);
377
if (input) input= line + strspn(line, " \t\n");
384
static char *DelimitRead(char *input, int *closed, int nlOK)
389
input+= strspn(input, " \t");
390
if (*input=='\n' || *input=='\0') nlFound= 1;
393
input= WhiteSkip(input);
395
if (*input == CLOSE_BRACE) {
403
if (!nlOK || !nlFound) input= 0;
408
/* distinguish end-of-file from comma not found */
416
ColRead(char *input, GpColorCell *dest)
421
input = WhiteSkip(input); /* may be on a new line */
422
value = strtol(input, &suffix, 0);
423
if (suffix==input) return 0;
425
if (value<0) value += 256;
430
static char *IntRead(char *input, int *dest)
435
input= WhiteSkip(input); /* may be on a new line */
436
value= (int)strtol(input, &suffix, 0);
437
if (suffix==input) return 0;
443
static char *RealRead(char *input, GpReal *dest)
448
input= WhiteSkip(input); /* may be on a new line */
449
value= (GpReal)strtod(input, &suffix);
450
if (suffix==input) return 0;
456
char legendString[41];
458
static char *StringRead(char *input, char **dest)
460
input= WhiteSkip(input);
465
} else if (*input=='\"') {
466
long len= strcspn(++input, "\"");
467
int nc= len>40? 40 : len;
468
strncpy(legendString, input, nc);
470
if (*input=='\"') { *dest= legendString; input++; }
479
static char *MemberRead(char *input, char **member)
481
input= WhiteSkip(input);
485
input+= strcspn(input, "= \t\n");
486
if (*input == '=') gotEqual= 1;
487
if (*input) *input++= '\0';
489
input= WhiteSkip(input);
490
if (input && *input++!='=') input= 0;
496
static char *ArrayRead(char *input, GpReal *dest, int narray)
500
input= WhiteSkip(input);
501
if (!input) return 0;
503
if (*input++ != OPEN_BRACE) return 0; /* no open brace */
504
input= WhiteSkip(input);
505
if (!input) return 0; /* eof after open brace */
507
for (narray-- ; ; narray--) {
508
if (narray<0) return 0; /* too many numbers in aggregate */
510
input= RealRead(input, dest++);
511
if (!input) return 0; /* token was not a number */
513
input= DelimitRead(input, &foundClose, 0);
514
if (!input) return 0; /* neither comma nor close brace */
515
if (foundClose) break;
521
static char *LineRead(char *input, GpLineAttribs *dest)
526
input= WhiteSkip(input);
527
if (!input || *input++!=OPEN_BRACE) return 0;
530
input= MemberRead(input, &member);
531
if (!input) return 0; /* couldn't find member = */
533
if (strcmp(member, "color")==0) {
534
input= ColRead(input, &dest->color);
535
} else if (strcmp(member, "type")==0) {
536
input= IntRead(input, &dest->type);
537
} else if (strcmp(member, "width")==0) {
538
input= RealRead(input, &dest->width);
540
return 0; /* unknown member */
542
if (!input) return 0; /* illegal format */
544
input= DelimitRead(input, &foundClose, 1);
545
if (!input) return 0; /* not comma, nl, or close brace */
546
if (foundClose) break;
552
static char *TextRead(char *input, GpTextAttribs *dest)
559
input= WhiteSkip(input);
560
if (!input || *input++!=OPEN_BRACE) return 0;
563
input= MemberRead(input, &member);
564
if (!input) return 0; /* couldn't find member = */
566
if (strcmp(member, "color")==0) {
567
input= ColRead(input, &dest->color);
568
} else if (strcmp(member, "font")==0) {
569
input= IntRead(input, &dest->font);
570
} else if (strcmp(member, "prec")==0) {
571
input= IntRead(input, &ijunk);
572
} else if (strcmp(member, "height")==0) {
573
input= RealRead(input, &dest->height);
574
} else if (strcmp(member, "expand")==0) {
575
input= RealRead(input, &rjunk);
576
} else if (strcmp(member, "spacing")==0) {
577
input= RealRead(input, &rjunk);
578
} else if (strcmp(member, "upX")==0) {
579
input= RealRead(input, &rjunk);
580
} else if (strcmp(member, "upY")==0) {
581
input= RealRead(input, &rjunk);
582
} else if (strcmp(member, "path")==0 || strcmp(member, "orient")==0) {
583
input= IntRead(input, &dest->orient);
584
} else if (strcmp(member, "alignH")==0) {
585
input= IntRead(input, &dest->alignH);
586
} else if (strcmp(member, "alignV")==0) {
587
input= IntRead(input, &dest->alignV);
588
} else if (strcmp(member, "opaque")==0) {
589
input= IntRead(input, &dest->opaque);
591
return 0; /* unknown member */
593
if (!input) return 0; /* illegal format */
595
input= DelimitRead(input, &foundClose, 1);
596
if (!input) return 0; /* not comma, nl, or close brace */
597
if (foundClose) break;
603
static char *AxisRead(char *input, GaAxisStyle *dest)
608
input= WhiteSkip(input);
609
if (!input || *input++!=OPEN_BRACE) return 0;
612
input= MemberRead(input, &member);
613
if (!input) return 0; /* couldn't find member = */
615
if (strcmp(member, "nMajor")==0) {
616
input= RealRead(input, &dest->nMajor);
617
} else if (strcmp(member, "nMinor")==0) {
618
input= RealRead(input, &dest->nMinor);
619
} else if (strcmp(member, "logAdjMajor")==0) {
620
input= RealRead(input, &dest->logAdjMajor);
621
} else if (strcmp(member, "logAdjMinor")==0) {
622
input= RealRead(input, &dest->logAdjMinor);
623
} else if (strcmp(member, "nDigits")==0) {
624
input= IntRead(input, &dest->nDigits);
625
} else if (strcmp(member, "gridLevel")==0) {
626
input= IntRead(input, &dest->gridLevel);
627
} else if (strcmp(member, "flags")==0) {
628
input= IntRead(input, &dest->flags);
629
} else if (strcmp(member, "tickOff")==0) {
630
input= RealRead(input, &dest->tickOff);
631
} else if (strcmp(member, "labelOff")==0) {
632
input= RealRead(input, &dest->labelOff);
633
} else if (strcmp(member, "tickLen")==0) {
634
input= ArrayRead(input, dest->tickLen, 5);
635
} else if (strcmp(member, "tickStyle")==0) {
636
input= LineRead(input, &dest->tickStyle);
637
} else if (strcmp(member, "gridStyle")==0) {
638
input= LineRead(input, &dest->gridStyle);
639
} else if (strcmp(member, "textStyle")==0) {
640
input= TextRead(input, &dest->textStyle);
641
} else if (strcmp(member, "xOver")==0) {
642
input= RealRead(input, &dest->xOver);
643
} else if (strcmp(member, "yOver")==0) {
644
input= RealRead(input, &dest->yOver);
646
return 0; /* unknown member */
648
if (!input) return 0; /* illegal format */
650
input= DelimitRead(input, &foundClose, 1);
651
if (!input) return 0; /* not comma, nl, or close brace */
652
if (foundClose) break;
658
static char *TickRead(char *input, GaTickStyle *dest)
663
input= WhiteSkip(input);
664
if (!input || *input++!=OPEN_BRACE) return 0;
667
input= MemberRead(input, &member);
668
if (!input) return 0; /* couldn't find member = */
670
if (strcmp(member, "horiz")==0) {
671
input= AxisRead(input, &dest->horiz);
672
} else if (strcmp(member, "vert")==0) {
673
input= AxisRead(input, &dest->vert);
674
} else if (strcmp(member, "frame")==0) {
675
input= IntRead(input, &dest->frame);
676
} else if (strcmp(member, "frameStyle")==0) {
677
input= LineRead(input, &dest->frameStyle);
679
return 0; /* unknown member */
681
if (!input) return 0; /* illegal format */
683
input= DelimitRead(input, &foundClose, 1);
684
if (!input) return 0; /* not comma, nl, or close brace */
685
if (foundClose) break;
691
/* defaultSystem is initialized to reasonable value for portrait mode */
692
#define DEF_XMIN 0.25
693
#define DEF_XMAX 0.60
694
#define DEF_YMIN 0.50
695
#define DEF_YMAX 0.85
696
struct GsysRead defaultSystem= {
697
0, { DEF_XMIN, DEF_XMAX, DEF_YMIN, DEF_YMAX },
700
{7.5, 50., 1.2, 1.2, 3, 1, TICK_L|TICK_U|TICK_OUT|LABEL_L,
702
{12.*ONE_POINT, 8.*ONE_POINT, 5.*ONE_POINT, 3.*ONE_POINT, 2.*ONE_POINT},
703
{FG_COLOR, L_SOLID, DEFAULT_LINE_WIDTH},
704
{FG_COLOR, L_DOT, DEFAULT_LINE_WIDTH},
705
{FG_COLOR, T_HELVETICA, 14.*ONE_POINT, TX_RIGHT, TH_NORMAL, TV_NORMAL, 1},
706
0.5*(DEF_XMIN+DEF_XMAX), DEF_YMIN-52.*ONE_POINT},
708
{7.5, 50., 1.2, 1.2, 4, 1, TICK_L|TICK_U|TICK_OUT|LABEL_L,
710
{12.*ONE_POINT, 8.*ONE_POINT, 5.*ONE_POINT, 3.*ONE_POINT, 2.*ONE_POINT},
711
{FG_COLOR, L_SOLID, DEFAULT_LINE_WIDTH},
712
{FG_COLOR, L_DOT, DEFAULT_LINE_WIDTH},
713
{FG_COLOR, T_HELVETICA, 14.*ONE_POINT, TX_RIGHT, TH_NORMAL, TV_NORMAL, 1},
714
DEF_XMIN, DEF_YMIN-52.*ONE_POINT},
716
0, {FG_COLOR, L_SOLID, DEFAULT_LINE_WIDTH}
720
struct GlegRead defaultLegends[2]= {
721
/* Ordinary legends form two 36x22 character columns below viewport */
722
{ 0.5*ONE_INCH, DEF_YMIN-64.*ONE_POINT, 3.875*ONE_INCH, 0.0,
723
{FG_COLOR, T_COURIER, 12.*ONE_POINT, TX_RIGHT, TH_LEFT, TV_TOP, 1},
725
/* Contour legends get a single 14x28 column to left of viewport */
726
{ DEF_XMAX+14.*ONE_POINT, DEF_YMAX+12.*ONE_POINT, 0.0, 0.0,
727
{FG_COLOR, T_COURIER, 12.*ONE_POINT, TX_RIGHT, TH_LEFT, TV_TOP, 1},
731
static char *SystemRead(char *input, struct GsysRead *dest)
736
input= WhiteSkip(input);
737
if (!input || *input++!=OPEN_BRACE) return 0;
740
input= MemberRead(input, &member);
741
if (!input) return 0; /* couldn't find member = */
743
if (strcmp(member, "viewport")==0) {
745
box[0]= box[1]= box[2]= box[3]= -1.0;
746
input= ArrayRead(input, box, 4);
747
if (box[3]<0.0) input= 0; /* all four required */
749
dest->viewport.xmin= box[0];
750
dest->viewport.xmax= box[1];
751
dest->viewport.ymin= box[2];
752
dest->viewport.ymax= box[3];
754
} else if (strcmp(member, "ticks")==0) {
755
input= TickRead(input, &dest->ticks);
756
} else if (strcmp(member, "legend")==0) {
757
input= StringRead(input, &dest->legend);
759
return 0; /* unknown member */
761
if (!input) return 0; /* illegal format */
763
input= DelimitRead(input, &foundClose, 1);
764
if (!input) return 0; /* not comma, nl, or close brace */
765
if (foundClose) break;
771
static char *LegendsRead(char *input, struct GlegRead *dest)
776
input= WhiteSkip(input);
777
if (!input || *input++!=OPEN_BRACE) return 0;
780
input= MemberRead(input, &member);
781
if (!input) return 0; /* couldn't find member = */
783
if (strcmp(member, "x")==0) {
784
input= RealRead(input, &dest->x);
785
} else if (strcmp(member, "y")==0) {
786
input= RealRead(input, &dest->y);
787
} else if (strcmp(member, "dx")==0) {
788
input= RealRead(input, &dest->dx);
789
} else if (strcmp(member, "dy")==0) {
790
input= RealRead(input, &dest->dy);
791
} else if (strcmp(member, "textStyle")==0) {
792
input= TextRead(input, &dest->textStyle);
793
} else if (strcmp(member, "nchars")==0) {
794
input= IntRead(input, &dest->nchars);
795
} else if (strcmp(member, "nlines")==0) {
796
input= IntRead(input, &dest->nlines);
797
} else if (strcmp(member, "nwrap")==0) {
798
input= IntRead(input, &dest->nwrap);
800
return 0; /* unknown member */
802
if (!input) return 0; /* illegal format */
804
input= DelimitRead(input, &foundClose, 1);
805
if (!input) return 0; /* not comma, nl, or close brace */
806
if (foundClose) break;
812
int GdReadStyle(Drauing *drawing, const char *gsFile)
814
int foundClose, sysIndex, landscape;
815
char *input, *keyword;
817
if (!gsFile) return 0;
819
gs= GistOpen(gsFile);
822
tempSystem= defaultSystem;
825
input= p_fgets(gs, line, 137);
826
if (!input) goto err; /* eof (or error) */
831
input= WhiteSkip(input);
834
input= MemberRead(input, &keyword);
835
if (!input) goto err; /* couldn't find keyword = */
837
if (strcmp(keyword, "default")==0) {
838
input= SystemRead(input, &tempSystem);
839
} else if (strcmp(keyword, "system")==0) {
840
modelSystem= tempSystem;
841
input= SystemRead(input, &modelSystem);
843
gistD.legend= modelSystem.legend;
844
sysIndex= GdNewSystem(&modelSystem.viewport, &modelSystem.ticks);
845
if (sysIndex<0) return 1;
846
} else if (strcmp(keyword, "landscape")==0) {
847
input= IntRead(input, &landscape);
848
} else if (strcmp(keyword, "legends")==0) {
849
modelLegends= defaultLegends[0];
850
input= LegendsRead(input, &modelLegends);
851
if (input) GdLegendBox(0, modelLegends.x, modelLegends.y,
852
modelLegends.dx, modelLegends.dy,
853
&modelLegends.textStyle, modelLegends.nchars,
854
modelLegends.nlines, modelLegends.nwrap);
855
} else if (strcmp(keyword, "clegends")==0) {
856
modelLegends= defaultLegends[1];
857
input= LegendsRead(input, &modelLegends);
858
if (input) GdLegendBox(1, modelLegends.x, modelLegends.y,
859
modelLegends.dx, modelLegends.dy,
860
&modelLegends.textStyle, modelLegends.nchars,
861
modelLegends.nlines, modelLegends.nwrap);
863
goto err; /* unknown keyword */
865
if (!input) goto err; /* illegal format */
867
input= DelimitRead(input, &foundClose, 1);
869
if (foundClose) break;
870
goto err; /* not comma, nl, or eof */
872
if (foundClose) goto err; /* close brace not legal here */
875
if (landscape) GdLandscape(1);
880
FormatError(gs, gsFile, "drawing style");
884
/* ------------------------------------------------------------------------ */