1 /*
2  * $Id: gread.c,v 1.2 2009-10-19 04:37:51 dhmunro Exp $
3  * Define Drauing gread read routine for GIST
4  */
5 /* Copyright (c) 2005, The Regents of the University of California.
6  * All rights reserved.
7  * This file is part of yorick (http://yorick.sourceforge.net).
8  * Read the accompanying LICENSE file for details.
9  */
10 
11 #include "gist.h"
12 #include "pstdio.h"
13 #include "pstdlib.h"
14 #include "play.h"
15 #include <errno.h>
16 
17 extern void GdKillSystems(void);  /* defined in draw.c */
18 
19 #ifndef GISTPATH
20 #define GISTPATH "~/gist:~/Gist:/usr/local/lib/gist"
21 #endif
22 char *gistPathDefault= GISTPATH;
23 
24 /* ------------------------------------------------------------------------ */
25 
26 #include <string.h>
27 
28 struct GsysRead {
29   char *legend;
30   GpBox viewport;
31   GaTickStyle ticks;
32 } modelSystem, tempSystem;
33 
34 struct GlegRead {
35   GpReal x, y, dx, dy;
36   GpTextAttribs textStyle;
37   int nchars, nlines, nwrap;
38 } modelLegends;
39 
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);
60 
61 /* ------------------------------------------------------------------------ */
62 /* A palette file (.gp) or style file (.gs) will be found if it:
63 
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.
70 
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.
76 
77    If the given filename begins with '/' the path search is
78    not done.  '~' is NOT recognized in the given filename.
79  */
80 
81 extern char *g_argv0;
82 char *g_argv0 = 0;
83 
84 static char *scratch = 0;
85 static char *gist_path = 0;
86 
87 char *
g_set_path(char * gpath)88 g_set_path(char *gpath)
89 {
90   if (gpath) {
91     char *p = gist_path;
92     gist_path = p_strcpy(gpath);
93     if (p) p_free(p);
94   } else {
95     FormGistPath();
96   }
97   return gist_path;
98 }
99 
FormGistPath(void)100 static char *FormGistPath(void)
101 {
102   if (!gist_path) {
103     char *gistPath = getenv("GISTPATH");
104     int len = gistPath? strlen(gistPath) : 0;
105     int len0 = g_argv0? strlen(g_argv0) : 0;
106     int lend = gistPathDefault? strlen(gistPathDefault) : 0;
107     char *place;
108 
109     /* Get enough scratch space to hold
110        the concatenation of the GISTPATH environment variable and the
111        GISTPATH compile-time option, and a fallback computed from argv[0] */
112     gist_path = p_malloc(len+len0+lend+4);
113     if (!gist_path) return 0;
114 
115     place = gist_path;
116     if (gistPath) {
117       strcpy(place, gistPath);
118       place += len;
119       *(place++) = ':';
120     }
121     strcpy(place, gistPathDefault);
122     place += lend;
123     /* back up to sibling of directory containing executable */
124     for (len=len0-1 ; len>0 ; len--) if (g_argv0[len]=='/') break;
125     for (len-- ; len>0 ; len--) if (g_argv0[len]=='/') break;
126     if (len > 0) {
127       /* tack /g/ sibling of executable directory onto path */
128       *(place++) = ':';
129       strncpy(place, g_argv0, ++len);
130       place += len;
131       strcpy(place, "g");
132     }
133   }
134 
135   scratch = p_malloc(1028);
136   if (!scratch) return 0;
137   return gist_path;
138 }
139 
FormFullName(char * gistPath,const char * name)140 static char *FormFullName(char *gistPath, const char *name)
141 {
142   int nlen= strlen(name);
143   int len, elen;
144   char *now= scratch;
145 
146   for (;;) {
147     /* Skip past any components of the GISTPATH which result in impossibly
148        long path names */
149     do len= strcspn(gistPath, ":"); while (!len);
150     /* handle MS Windows drive letters */
151     if (len==1 && gistPath[1]==':' &&
152         ((gistPath[0]>='A' && gistPath[0]<='Z') ||
153          (gistPath[0]>='a' && gistPath[0]<='z')))
154       len = 2+strcspn(gistPath+2, ":");
155     if (!len) break;
156     elen= len;
157 
158     now= scratch;
159     if (gistPath[0]=='~') {
160       /* Get name of home directory from HOME environment variable */
161       char *home= getenv("HOME");
162       int hlen;
163       if (home && (hlen= strlen(home))<1024) {
164         strcpy(now, home);
165         now+= hlen;
166         gistPath++;
167         len--;
168         elen+= hlen-1;
169       }
170     }
171 
172     if (elen+nlen<1023) break;
173 
174     gistPath+= len+1;
175   }
176 
177   if (len) {
178     strncpy(now, gistPath, len);
179     now+= len;
180     if (now[-1]!='/') *now++= '/';
181     strcpy(now, name);
182   } else {
183     scratch[0]= '\0';
184   }
185 
186   return gistPath+len + strspn(gistPath+len, ":");
187 }
188 
189 extern p_file *GistOpen(const char *name);
GistOpen(const char * name)190 p_file *GistOpen(const char *name)
191 {
192   p_file *f;
193   if (!name) return 0;
194 
195   f= p_fopen(name, "r");
196 
197   if (!f && name[0]!='/') {
198     /* Try to find relative file names somewhere on GISTPATH or, failing
199        that, in the default directory specified at compile time.  */
200     char *gistPath= FormGistPath();
201     if (gistPath) {
202       do {
203         gistPath= FormFullName(gistPath, name);
204         f= p_fopen(scratch, "r");
205       } while (!f && gistPath[0]);
206       p_free(scratch);
207     }
208   }
209 
210   if (!f) {
211     strcpy(gistError, "unable to open file ");
212     strncat(gistError, name, 100);
213   }
214   return f;
215 }
216 
FormatError(p_file * fp,const char * name,const char * id)217 static void FormatError(p_file *fp, const char *name, const char *id)
218 {
219   p_fclose(fp);
220   strcpy(gistError, id);
221   strcat(gistError, " file format error in ");
222   strncat(gistError, name, 127-strlen(gistError));
223 }
224 
225 static char line[137];  /* longest allowed line is 136 characters */
226 
227 /* ------------------------------------------------------------------------ */
228 
SnarfColor(char * token)229 static int SnarfColor(char *token)
230      /* returns -1 if not unsigned char, -2 if missing */
231 {
232   int color;
233   char *suffix;
234 
235   if (!token) token= strtok(0, " \t\n");
236   if (token) color= (int)strtol(token, &suffix, 0);
237   else return -2;
238   if (suffix==token || color<0 || color>255) return -1;
239   else return color;
240 }
241 
SnarfRGB(char * token,GpColorCell * cell)242 static int SnarfRGB(char *token, GpColorCell *cell)
243 {
244   int red, blue, green;
245   red= SnarfColor(token);
246   if (red<0) return 1;
247   green= SnarfColor(0);
248   if (green<0) return 1;
249   blue= SnarfColor(0);
250   if (blue<0) return 1;
251   cell[0] = P_RGB(red, green, blue);
252   return 0;
253 }
254 
255 /* ARGSUSED */
SnarfGray(GpColorCell * cell,int lookingFor4)256 static int SnarfGray(GpColorCell *cell, int lookingFor4)
257 {
258   int gray= SnarfColor(0);
259   if (gray==-2) return lookingFor4;
260   else if (gray<0 || !lookingFor4) return 1;
261   /* cell->gray= gray; */
262   return 0;
263 }
264 
GpReadPalette(Engine * engine,const char * gpFile,GpColorCell ** palette,int maxColors)265 int GpReadPalette(Engine *engine, const char *gpFile,
266                   GpColorCell **palette, int maxColors)
267 {
268   char *token, *suffix;
269   GpColorCell *pal= 0;
270   int iColor= -1,  nColors= 0,  ntsc= 0,  lookingFor4= 0;
271   p_file *gp= GistOpen(gpFile);
272 
273   *palette= 0;
274   if (!gp) return 0;
275 
276   for (;;) {  /* loop on lines in file */
277     token= p_fgets(gp, line, 137);
278     if (!token) break;                      /* eof (or error) */
279 
280     token= strtok(token, " =\t\n");
281     if (!token || token[0]=='#') continue;  /* blank or comment line */
282 
283     if (iColor<=0) {
284       int *dest= 0;
285       if (strcmp(token, "ncolors")==0) dest= &nColors;
286       else if (strcmp(token, "ntsc")==0) dest= &ntsc;
287 
288       if (dest) {
289         /* this is ncolors=... or ntsc=... line */
290         token= strtok(0, " =\t\n");
291         if (token) *dest= (int)strtol(token, &suffix, 0);
292         else goto err;
293         if (suffix==token || strtok(0, " \t\n")) goto err;
294 
295       } else {
296         /* this must be the first rgb line */
297         int gray;
298 
299         /* previous ncolors= is mandatory so palette can be allocated */
300         if (nColors<=0) goto err;
301         pal= p_malloc(sizeof(GpColorCell)*nColors);
302         if (!pal) goto merr;
303 
304         /* if first rgb line has 4 numbers, all must have 4, else 3 */
305         if (SnarfRGB(token, pal)) goto err;
306         gray= SnarfColor(0);
307         if (gray==-1) goto err;
308         if (gray>=0) {
309           lookingFor4= 1;
310           /* pal->gray= gray; */
311           if (SnarfGray(pal, 0)) goto err;  /* just check for eol */
312         } else {
313           lookingFor4= 0;
314           /* already got eol */
315         }
316 
317         iColor= 1;
318       }
319 
320     } else if (iColor<nColors) {
321       /* read next rgb line */
322       if (SnarfRGB(token, pal+iColor)) goto err;
323       if (SnarfGray(pal+iColor, lookingFor4)) goto err;
324       iColor++;
325 
326     } else {
327       goto err;                  /* too many rgb for specified ncolors */
328     }
329   }
330   if (iColor<nColors) goto err;  /* too few rgb for specified ncolors */
331 
332   p_fclose(gp);
333 
334   if (nColors>maxColors && maxColors>1) {
335     /* attempt to rescale the palette to maxColors */
336     int oldCell, newCell, nextCell, r, g, b;
337     double ratio= ((double)(nColors-1))/((double)(maxColors-1));
338     double frac, frac1, old= 0.0;
339     for (newCell=0 ; newCell<maxColors ; newCell++) {
340       oldCell= (int)old;
341       nextCell= oldCell+1;
342       if (nextCell>=nColors) nextCell= oldCell;
343       frac= old-(double)oldCell;
344       frac1= 1.0-frac;
345       r = (int)(frac1*P_R(pal[oldCell]) + frac*P_R(pal[nextCell]));
346       g = (int)(frac1*P_G(pal[oldCell]) + frac*P_G(pal[nextCell]));
347       b = (int)(frac1*P_B(pal[oldCell]) + frac*P_B(pal[nextCell]));
348       pal[newCell] = P_RGB(r, g, b);
349       /*if (!lookingFor4)
350         pal[newCell].gray= frac1*pal[oldCell].gray+frac*pal[nextCell].gray;*/
351       old+= ratio;
352     }
353     nColors= maxColors;
354   }
355 
356   if (!lookingFor4) {
357     /* gray values were not explicitly specified */
358     if (ntsc) GpPutNTSC(nColors, pal);
359     else GpPutGray(nColors, pal);
360   }
361 
362   *palette= pal;
363   iColor= GpSetPalette(engine, pal, nColors);
364   return iColor>nColors? nColors : iColor;
365 
366  err:
367   FormatError(gp, gpFile, "palette");
368   if (pal) p_free(pal);
369   return 0;
370 
371  merr:
372   strcpy(gistError, "memory manager failed to get space for palette");
373   p_fclose(gp);
374   return 0;
375 }
376 
377 /* ------------------------------------------------------------------------ */
378 
379 #define OPEN_BRACE '{'
380 #define CLOSE_BRACE '}'
381 
382 static p_file *gs= 0;
383 
WhiteSkip(char * input)384 static char *WhiteSkip(char *input)
385 {
386   input+= strspn(input, " \t\n");
387 
388   while (!input[0] || input[0]=='#') { /* rest of line missing or comment */
389     input= p_fgets(gs, line, 137);
390     if (input) input= line + strspn(line, " \t\n");
391     else break;
392   }
393 
394   return input;
395 }
396 
DelimitRead(char * input,int * closed,int nlOK)397 static char *DelimitRead(char *input, int *closed, int nlOK)
398 {
399   int nlFound= 0;
400 
401   if (nlOK) {
402     input+= strspn(input, " \t");
403     if (*input=='\n' || *input=='\0') nlFound= 1;
404   }
405 
406   input= WhiteSkip(input);
407   if (input) {
408     if (*input == CLOSE_BRACE) {
409       *closed= 1;
410       input++;
411     } else {
412       *closed= 0;
413       if (*input == ',') {
414         input++;
415       } else {
416         if (!nlOK || !nlFound) input= 0;
417       }
418     }
419 
420   } else {
421     /* distinguish end-of-file from comma not found */
422     *closed= 1;
423   }
424 
425   return input;
426 }
427 
428 static char *
ColRead(char * input,GpColorCell * dest)429 ColRead(char *input, GpColorCell *dest)
430 {
431   long value;
432   char *suffix;
433 
434   input = WhiteSkip(input);  /* may be on a new line */
435   value = strtol(input, &suffix, 0);
436   if (suffix==input) return 0;
437 
438   if (value<0) value += 256;
439   *dest = value;
440   return suffix;
441 }
442 
IntRead(char * input,int * dest)443 static char *IntRead(char *input, int *dest)
444 {
445   int value;
446   char *suffix;
447 
448   input= WhiteSkip(input);  /* may be on a new line */
449   value= (int)strtol(input, &suffix, 0);
450   if (suffix==input) return 0;
451 
452   *dest= value;
453   return suffix;
454 }
455 
RealRead(char * input,GpReal * dest)456 static char *RealRead(char *input, GpReal *dest)
457 {
458   GpReal value;
459   char *suffix;
460 
461   input= WhiteSkip(input);  /* may be on a new line */
462   errno = 0;
463   value= (GpReal)strtod(input, &suffix);
464   if (errno || suffix==input)
465     return 0;
466 
467   *dest= value;
468   return suffix;
469 }
470 
471 char legendString[41];
472 
StringRead(char * input,char ** dest)473 static char *StringRead(char *input, char **dest)
474 {
475   input= WhiteSkip(input);
476   if (input) {
477     if (*input=='0') {
478       *dest= 0;
479       input++;
480     } else if (*input=='\"') {
481       long len= strcspn(++input, "\"");
482       int nc= len>40? 40 : len;
483       strncpy(legendString, input, nc);
484       input+= len;
485       if (*input=='\"') { *dest= legendString;  input++; }
486       else input= 0;
487     } else {
488       input= 0;
489     }
490   }
491   return input;
492 }
493 
MemberRead(char * input,char ** member)494 static char *MemberRead(char *input, char **member)
495 {
496   input= WhiteSkip(input);
497   *member= input;
498   if (input) {
499     int gotEqual= 0;
500     input+= strcspn(input, "= \t\n");
501     if (*input == '=') gotEqual= 1;
502     if (*input) *input++= '\0';
503     if (!gotEqual) {
504       input= WhiteSkip(input);
505       if (input && *input++!='=') input= 0;
506     }
507   }
508   return input;
509 }
510 
ArrayRead(char * input,GpReal * dest,int narray)511 static char *ArrayRead(char *input, GpReal *dest, int narray)
512 {
513   int foundClose;
514 
515   input= WhiteSkip(input);
516   if (!input) return 0;
517 
518   if (*input++ != OPEN_BRACE) return 0;  /* no open brace */
519   input= WhiteSkip(input);
520   if (!input) return 0;                  /* eof after open brace */
521 
522   for (narray-- ; ; narray--) {
523     if (narray<0) return 0;           /* too many numbers in aggregate */
524 
525     input= RealRead(input, dest++);
526     if (!input) return 0;             /* token was not a number */
527 
528     input= DelimitRead(input, &foundClose, 0);
529     if (!input) return 0;             /* neither comma nor close brace */
530     if (foundClose) break;
531   }
532 
533   return input;
534 }
535 
LineRead(char * input,GpLineAttribs * dest)536 static char *LineRead(char *input, GpLineAttribs *dest)
537 {
538   int foundClose;
539   char *member;
540 
541   input= WhiteSkip(input);
542   if (!input || *input++!=OPEN_BRACE) return 0;
543 
544   for (;;) {
545     input= MemberRead(input, &member);
546     if (!input) return 0;             /* couldn't find member = */
547 
548     if (strcmp(member, "color")==0) {
549       input= ColRead(input, &dest->color);
550     } else if (strcmp(member, "type")==0) {
551       input= IntRead(input, &dest->type);
552     } else if (strcmp(member, "width")==0) {
553       input= RealRead(input, &dest->width);
554     } else {
555       return 0;                       /* unknown member */
556     }
557     if (!input) return 0;             /* illegal format */
558 
559     input= DelimitRead(input, &foundClose, 1);
560     if (!input) return 0;             /* not comma, nl, or close brace */
561     if (foundClose) break;
562   }
563 
564   return input;
565 }
566 
TextRead(char * input,GpTextAttribs * dest)567 static char *TextRead(char *input, GpTextAttribs *dest)
568 {
569   int foundClose;
570   char *member;
571   int ijunk;
572   GpReal rjunk;
573 
574   input= WhiteSkip(input);
575   if (!input || *input++!=OPEN_BRACE) return 0;
576 
577   for (;;) {
578     input= MemberRead(input, &member);
579     if (!input) return 0;             /* couldn't find member = */
580 
581     if (strcmp(member, "color")==0) {
582       input= ColRead(input, &dest->color);
583     } else if (strcmp(member, "font")==0) {
584       input= IntRead(input, &dest->font);
585     } else if (strcmp(member, "prec")==0) {
586       input= IntRead(input, &ijunk);
587     } else if (strcmp(member, "height")==0) {
588       input= RealRead(input, &dest->height);
589     } else if (strcmp(member, "expand")==0) {
590       input= RealRead(input, &rjunk);
591     } else if (strcmp(member, "spacing")==0) {
592       input= RealRead(input, &rjunk);
593     } else if (strcmp(member, "upX")==0) {
594       input= RealRead(input, &rjunk);
595     } else if (strcmp(member, "upY")==0) {
596       input= RealRead(input, &rjunk);
597     } else if (strcmp(member, "path")==0 || strcmp(member, "orient")==0) {
598       input= IntRead(input, &dest->orient);
599     } else if (strcmp(member, "alignH")==0) {
600       input= IntRead(input, &dest->alignH);
601     } else if (strcmp(member, "alignV")==0) {
602       input= IntRead(input, &dest->alignV);
603     } else if (strcmp(member, "opaque")==0) {
604       input= IntRead(input, &dest->opaque);
605     } else {
606       return 0;                       /* unknown member */
607     }
608     if (!input) return 0;             /* illegal format */
609 
610     input= DelimitRead(input, &foundClose, 1);
611     if (!input) return 0;             /* not comma, nl, or close brace */
612     if (foundClose) break;
613   }
614 
615   return input;
616 }
617 
AxisRead(char * input,GaAxisStyle * dest)618 static char *AxisRead(char *input, GaAxisStyle *dest)
619 {
620   int foundClose;
621   char *member;
622 
623   input= WhiteSkip(input);
624   if (!input || *input++!=OPEN_BRACE) return 0;
625 
626   for (;;) {
627     input= MemberRead(input, &member);
628     if (!input) return 0;             /* couldn't find member = */
629 
630     if (strcmp(member, "nMajor")==0) {
631       input= RealRead(input, &dest->nMajor);
632     } else if (strcmp(member, "nMinor")==0) {
633       input= RealRead(input, &dest->nMinor);
634     } else if (strcmp(member, "logAdjMajor")==0) {
635       input= RealRead(input, &dest->logAdjMajor);
636     } else if (strcmp(member, "logAdjMinor")==0) {
637       input= RealRead(input, &dest->logAdjMinor);
638     } else if (strcmp(member, "nDigits")==0) {
639       input= IntRead(input, &dest->nDigits);
640     } else if (strcmp(member, "gridLevel")==0) {
641       input= IntRead(input, &dest->gridLevel);
642     } else if (strcmp(member, "flags")==0) {
643       input= IntRead(input, &dest->flags);
644     } else if (strcmp(member, "tickOff")==0) {
645       input= RealRead(input, &dest->tickOff);
646     } else if (strcmp(member, "labelOff")==0) {
647       input= RealRead(input, &dest->labelOff);
648     } else if (strcmp(member, "tickLen")==0) {
649       input= ArrayRead(input, dest->tickLen, 5);
650     } else if (strcmp(member, "tickStyle")==0) {
651       input= LineRead(input, &dest->tickStyle);
652     } else if (strcmp(member, "gridStyle")==0) {
653       input= LineRead(input, &dest->gridStyle);
654     } else if (strcmp(member, "textStyle")==0) {
655       input= TextRead(input, &dest->textStyle);
656     } else if (strcmp(member, "xOver")==0) {
657       input= RealRead(input, &dest->xOver);
658     } else if (strcmp(member, "yOver")==0) {
659       input= RealRead(input, &dest->yOver);
660     } else {
661       return 0;                       /* unknown member */
662     }
663     if (!input) return 0;             /* illegal format */
664 
665     input= DelimitRead(input, &foundClose, 1);
666     if (!input) return 0;             /* not comma, nl, or close brace */
667     if (foundClose) break;
668   }
669 
670   return input;
671 }
672 
TickRead(char * input,GaTickStyle * dest)673 static char *TickRead(char *input, GaTickStyle *dest)
674 {
675   int foundClose;
676   char *member;
677 
678   input= WhiteSkip(input);
679   if (!input || *input++!=OPEN_BRACE) return 0;
680 
681   for (;;) {
682     input= MemberRead(input, &member);
683     if (!input) return 0;             /* couldn't find member = */
684 
685     if (strcmp(member, "horiz")==0) {
686       input= AxisRead(input, &dest->horiz);
687     } else if (strcmp(member, "vert")==0) {
688       input= AxisRead(input, &dest->vert);
689     } else if (strcmp(member, "frame")==0) {
690       input= IntRead(input, &dest->frame);
691     } else if (strcmp(member, "frameStyle")==0) {
692       input= LineRead(input, &dest->frameStyle);
693     } else {
694       return 0;                       /* unknown member */
695     }
696     if (!input) return 0;             /* illegal format */
697 
698     input= DelimitRead(input, &foundClose, 1);
699     if (!input) return 0;             /* not comma, nl, or close brace */
700     if (foundClose) break;
701   }
702 
703   return input;
704 }
705 
706 /* defaultSystem is initialized to reasonable value for portrait mode */
707 #define DEF_XMIN 0.25
708 #define DEF_XMAX 0.60
709 #define DEF_YMIN 0.50
710 #define DEF_YMAX 0.85
711 struct GsysRead defaultSystem= {
712   0, { DEF_XMIN, DEF_XMAX, DEF_YMIN, DEF_YMAX },
713   {
714 
715     {7.5, 50., 1.2, 1.2, 3, 1, TICK_L|TICK_U|TICK_OUT|LABEL_L,
716      0.0, 14.0*ONE_POINT,
717      {12.*ONE_POINT, 8.*ONE_POINT, 5.*ONE_POINT, 3.*ONE_POINT, 2.*ONE_POINT},
718      {FG_COLOR, L_SOLID, DEFAULT_LINE_WIDTH},
719      {FG_COLOR, L_DOT, DEFAULT_LINE_WIDTH},
720      {FG_COLOR, T_HELVETICA, 14.*ONE_POINT, TX_RIGHT, TH_NORMAL, TV_NORMAL, 1},
721      0.5*(DEF_XMIN+DEF_XMAX), DEF_YMIN-52.*ONE_POINT},
722 
723     {7.5, 50., 1.2, 1.2, 4, 1, TICK_L|TICK_U|TICK_OUT|LABEL_L,
724      0.0, 14.0*ONE_POINT,
725      {12.*ONE_POINT, 8.*ONE_POINT, 5.*ONE_POINT, 3.*ONE_POINT, 2.*ONE_POINT},
726      {FG_COLOR, L_SOLID, DEFAULT_LINE_WIDTH},
727      {FG_COLOR, L_DOT, DEFAULT_LINE_WIDTH},
728      {FG_COLOR, T_HELVETICA, 14.*ONE_POINT, TX_RIGHT, TH_NORMAL, TV_NORMAL, 1},
729      DEF_XMIN, DEF_YMIN-52.*ONE_POINT},
730 
731   0, {FG_COLOR, L_SOLID, DEFAULT_LINE_WIDTH}
732   }
733 };
734 
735 struct GlegRead defaultLegends[2]= {
736   /* Ordinary legends form two 36x22 character columns below viewport */
737   { 0.5*ONE_INCH, DEF_YMIN-64.*ONE_POINT, 3.875*ONE_INCH, 0.0,
738     {FG_COLOR, T_COURIER, 12.*ONE_POINT, TX_RIGHT, TH_LEFT, TV_TOP, 1},
739     36, 22, 2 },
740   /* Contour legends get a single 14x28 column to left of viewport */
741   { DEF_XMAX+14.*ONE_POINT, DEF_YMAX+12.*ONE_POINT, 0.0, 0.0,
742     {FG_COLOR, T_COURIER, 12.*ONE_POINT, TX_RIGHT, TH_LEFT, TV_TOP, 1},
743     14, 28, 1 },
744 };
745 
SystemRead(char * input,struct GsysRead * dest)746 static char *SystemRead(char *input, struct GsysRead *dest)
747 {
748   int foundClose;
749   char *member;
750 
751   input= WhiteSkip(input);
752   if (!input || *input++!=OPEN_BRACE) return 0;
753 
754   for (;;) {
755     input= MemberRead(input, &member);
756     if (!input) return 0;             /* couldn't find member = */
757 
758     if (strcmp(member, "viewport")==0) {
759       GpReal box[4];
760       box[0]= box[1]= box[2]= box[3]= -1.0;
761       input= ArrayRead(input, box, 4);
762       if (box[3]<0.0) input= 0;       /* all four required */
763       else {
764         dest->viewport.xmin= box[0];
765         dest->viewport.xmax= box[1];
766         dest->viewport.ymin= box[2];
767         dest->viewport.ymax= box[3];
768       }
769     } else if (strcmp(member, "ticks")==0) {
770       input= TickRead(input, &dest->ticks);
771     } else if (strcmp(member, "legend")==0) {
772       input= StringRead(input, &dest->legend);
773     } else {
774       return 0;                       /* unknown member */
775     }
776     if (!input) return 0;             /* illegal format */
777 
778     input= DelimitRead(input, &foundClose, 1);
779     if (!input) return 0;             /* not comma, nl, or close brace */
780     if (foundClose) break;
781   }
782 
783   return input;
784 }
785 
LegendsRead(char * input,struct GlegRead * dest)786 static char *LegendsRead(char *input, struct GlegRead *dest)
787 {
788   int foundClose;
789   char *member;
790 
791   input= WhiteSkip(input);
792   if (!input || *input++!=OPEN_BRACE) return 0;
793 
794   for (;;) {
795     input= MemberRead(input, &member);
796     if (!input) return 0;             /* couldn't find member = */
797 
798     if (strcmp(member, "x")==0) {
799       input= RealRead(input, &dest->x);
800     } else if (strcmp(member, "y")==0) {
801       input= RealRead(input, &dest->y);
802     } else if (strcmp(member, "dx")==0) {
803       input= RealRead(input, &dest->dx);
804     } else if (strcmp(member, "dy")==0) {
805       input= RealRead(input, &dest->dy);
806     } else if (strcmp(member, "textStyle")==0) {
807       input= TextRead(input, &dest->textStyle);
808     } else if (strcmp(member, "nchars")==0) {
809       input= IntRead(input, &dest->nchars);
810     } else if (strcmp(member, "nlines")==0) {
811       input= IntRead(input, &dest->nlines);
812     } else if (strcmp(member, "nwrap")==0) {
813       input= IntRead(input, &dest->nwrap);
814     } else {
815       return 0;                       /* unknown member */
816     }
817     if (!input) return 0;             /* illegal format */
818 
819     input= DelimitRead(input, &foundClose, 1);
820     if (!input) return 0;             /* not comma, nl, or close brace */
821     if (foundClose) break;
822   }
823 
824   return input;
825 }
826 
GdReadStyle(Drauing * drawing,const char * gsFile)827 int GdReadStyle(Drauing *drawing, const char *gsFile)
828 {
829   int foundClose, sysIndex, landscape;
830   char *input, *keyword;
831 
832   if (!gsFile) return 0;
833 
834   gs= GistOpen(gsFile);
835   if (!gs) return 1;
836 
837   tempSystem= defaultSystem;
838   landscape= 0;
839 
840   input= p_fgets(gs, line, 137);
841   if (!input) goto err;                      /* eof (or error) */
842 
843   GdKillSystems();
844 
845   for (;;) {
846     input= WhiteSkip(input);
847     if (!input) break;
848 
849     input= MemberRead(input, &keyword);
850     if (!input) goto err;             /* couldn't find keyword = */
851 
852     if (strcmp(keyword, "default")==0) {
853       input= SystemRead(input, &tempSystem);
854     } else if (strcmp(keyword, "system")==0) {
855       modelSystem= tempSystem;
856       input= SystemRead(input, &modelSystem);
857       gistD.hidden= 0;
858       gistD.legend= modelSystem.legend;
859       sysIndex= GdNewSystem(&modelSystem.viewport, &modelSystem.ticks);
860       if (sysIndex<0) return 1;
861     } else if (strcmp(keyword, "landscape")==0) {
862       input= IntRead(input, &landscape);
863     } else if (strcmp(keyword, "legends")==0) {
864       modelLegends= defaultLegends[0];
865       input= LegendsRead(input, &modelLegends);
866       if (input) GdLegendBox(0, modelLegends.x, modelLegends.y,
867                              modelLegends.dx, modelLegends.dy,
868                              &modelLegends.textStyle, modelLegends.nchars,
869                              modelLegends.nlines, modelLegends.nwrap);
870     } else if (strcmp(keyword, "clegends")==0) {
871       modelLegends= defaultLegends[1];
872       input= LegendsRead(input, &modelLegends);
873       if (input) GdLegendBox(1, modelLegends.x, modelLegends.y,
874                              modelLegends.dx, modelLegends.dy,
875                              &modelLegends.textStyle, modelLegends.nchars,
876                              modelLegends.nlines, modelLegends.nwrap);
877     } else {
878       goto err;                       /* unknown keyword */
879     }
880     if (!input) goto err;             /* illegal format */
881 
882     input= DelimitRead(input, &foundClose, 1);
883     if (!input) {
884       if (foundClose) break;
885       goto err;                       /* not comma, nl, or eof */
886     }
887     if (foundClose) goto err;         /* close brace not legal here */
888   }
889 
890   if (landscape) GdLandscape(1);
891   p_fclose(gs);
892   return 0;
893 
894  err:
895   FormatError(gs, gsFile, "drawing style");
896   return 1;
897 }
898 
899 /* ------------------------------------------------------------------------ */
900