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