1 /*	main.c	1.24	(Berkeley)	86/04/14
2  *
3  *	This file contains the main and file system dependent routines
4  * for processing gremlin files into troff input.  The program watches
5  * input go by to standard output, only interpretting things between .GS
6  * and .GE lines.  Default values (font, size, scale, thickness) may be
7  * overridden with a "default" command and are further overridden by
8  * commands in the input.  A description of the command-line options are
9  * listed below.  A space is NOT required for the operand of an option.
10  *
11  *	command options are:
12  *
13  *	-L dir	set the library directory to dir.  If a file is not found
14  *		in the current directory, it is looked for in dir (default
15  *		is /usr/lib/gremlib).
16  *
17  *	-T dev	Prepare output for "dev" printer.  Default is for the varian
18  *	-P dev	and versatec printers.  Devices acceptable are:  ver, var, ip.
19  *
20  *		Inside the GS and GE, commands are accepted to reconfigure
21  *	    the picture.  At most one command may reside on each line, and
22  *	    each command is followed by a parameter separated by white space.
23  *	    The commands are as follows, and may be abbreviated down to one
24  *	    character (with exception of "scale" and "stipple" down to "sc"
25  *	    and "st") and may be upper or lower case.
26  *
27  *			      default  -  make all settings in the current
28  *					  .GS/.GE the global defaults.
29  *					  Height, width and file are NOT saved.
30  *			   1, 2, 3, 4  -  set size 1, 2, 3, or 4 (followed
31  *					  by an integer point size).
32  *	roman, italics, bold, special  -  set gremlin's fonts to any other
33  *					  troff font (one or two characters)
34  *			   stipple, l  -  use a stipple font for polygons.  Arg
35  *					  is troff font name.  No Default.  Can
36  *					  use only one stipple font per picture.
37  *					  (see below for stipple font index)
38  *			     scale, x  -  scale is IN ADDITION to the global
39  *					  scale factor from the default.
40  *			   pointscale  -  turn on scaling point sizes to
41  *					  match "scale" commands.  (optional
42  *					  operand "off" to turn it off)
43  *		narrow, medium, thick  -  set pixel widths of lines.
44  *				 file  -  set the file name to read the
45  *					  gremlin picture from.  If the file
46  *					  isn't in the current directory, the
47  *					  gremlin library is tried.
48  *			width, height  -  these two commands override any
49  *					  scaling factor that is in effect,
50  *					  and forces the picture to fit into
51  *					  either the height or width specified,
52  *					  whichever makes the picture smaller.
53  *					  The operand for these two commands is
54  *					  a floating-point number in units of
55  *					  inches
56  *		        oldstipplemap  -  use the old-style stipple mapping.
57  *					  THE FOLLOWING COMMANDS ARE IGNORED
58  *					  UNLESS OLDSTIPPLEMAP IS SPECIFIED.
59  *     l1, l2, l3, l4, l5, l6, l7, l8  -  set association between stipples
60  *					  (1 - 8) and the stipple font file
61  *					  index.  Valid cifplot indices are
62  *					  1 - 32 (although 24 is not defined)
63  *					  and valid unigrafix indices are
64  *					  1 - 64.  Nonetheless, any number
65  *					  between 0 and 255 is accepted since
66  *					  new stipple fonts may be added.
67  *					  An integer operand is required.
68  *
69  *	Troff number registers used:  g1 through g9.  g1 is the width of the
70  *	picture, and g2 is the height.  g3, and g4, save information, g8
71  *	and g9 are used for text processing and g5-g7 are reserved.
72  */
73 
74 
75 #include <ctype.h>
76 #include "gprint.h"
77 #include "dev.h"
78 
79 extern char *malloc();
80 extern char *rindex();
81 
82 /* database imports */
83 
84 extern HGPrintElt();
85 extern ELT *DBInit(), *DBRead();
86 extern POINT *PTInit(), *PTMakePoint();
87 
88 
89 #ifndef GREMLIB
90 #define GREMLIB		"/usr/local/gremlib/"
91 #endif
92 
93 #define SUN_SCALEFACTOR 0.70
94 
95 #ifndef DEVDIR
96 #define DEVDIR		"/usr/lib/font"
97 #endif
98 #define DEFAULTDEV	"va"
99 #define DEFSTIPPLE	"cf"
100 
101 #define MAXINLINE	100		/* input line length */
102 #define DEFTHICK	3		/* default thicknes */
103 #define DEFSTYLE	SOLID		/* default line style */
104 
105 #ifdef oldversion
106 #define SCREENtoINCH	0.02		/* scaling factor, screen to inches */
107 #endif
108 
109 double SCREENtoINCH;			/* scaling factor, screen to inches */
110 
111 #define BIG	999999999999.0		/* unweildly large floating number */
112 
113 
114 static char sccsid[] = "@(#) (Berkeley) 04/14/86";
115 
116 char	*printer = DEFAULTDEV;	/* device to look up resolution of */
117 char	*gremlib = GREMLIB;	/* place to find files after current dir. */
118 double	res;			/* that printer's resolution goes here */
119 
120 int	linethickness;		/* brush styles */
121 int	linmod;
122 int	lastx;			/* point registers for printing elements */
123 int	lasty;
124 int	lastyline;		/* a line's vertical position is NOT the same */
125 				/* after that line is over, so for a line of */
126 				/* drawing commands, vertical spacing is kept */
127 				/* in lastyline */
128 
129 			/* these are the default fonts, sizes, line styles, */
130 			/*   and thicknesses.  These can be modified from a */
131 			/*   "default" command and are reset each time the  */
132 			/*   start of a picture (.GS) is found.		    */
133 
134 char *	deffont[] = {  "R", "I", "B", "S"  };
135 int	defsize[] = {  10, 16, 24, 36  };
136 int	defthick[STYLES] = {  1, 1, 5, 1, 1, 3  };
137 int	defstipple_index[NSTIPPLES] = { 1, 3, 12, 14, 16, 19, 21, 23 };
138 int	style[STYLES] = {  DOTTED, DOTDASHED, SOLID, DASHED, SOLID, SOLID  };
139 double	scale = 1.0;		/* no scaling, default */
140 int	defpoint = 0;		/* flag for pointsize scaling */
141 char *  defstipple = (char *) 0;
142 
143 int	thick[STYLES];	/* thicknesses set by defaults, then by commands */
144 char	*tfont[FONTS];	/* fonts originally set to deffont values, then */
145 int 	tsize[SIZES];	/*    optionally changed by commands inside grn */
146 int	stipple_index[NSTIPPLES];	/* stipple font file indices */
147 char *  stipple;
148 
149 double	xscale;		/* scaling factor from individual pictures */
150 double	troffscale;	/* scaling factor at output time */
151 double	width;		/* user-request maximum width for picture (in inches) */
152 double	height;		/* user-request height */
153 int	pointscale;	/* flag for pointsize scaling */
154 int	setdefault;	/* flag for a .GS/.GE to remember all settings */
155 
156 double	toppoint;		/* remember the picture */
157 double	bottompoint;		/* bounds in these variables */
158 double	leftpoint;
159 double	rightpoint;
160 
161 int	ytop;			/* these are integer versions of the above */
162 int	ybottom;		/* so not to convert each time they're used */
163 int	xleft;
164 int	xright;
165 
166 int	linenum = 0;			/* line number of input file */
167 char	inputline[MAXINLINE];		/* spot to filter through the file */
168 char	*c1 = inputline;		/* c1, c2, and c3 will be used to */
169 char	*c2 = inputline + 1;		/* hunt for lines that begin with */
170 char	*c3 = inputline + 2;		/* ".GS" by looking individually */
171 char	GScommand[MAXINLINE];		/* put user's ".GS" command line here */
172 char	gremlinfile[MAXINLINE];		/* filename to use for a picture */
173 int	SUNFILE = FALSE;		/* TRUE if SUN gremlin file */
174 int	oldstipmap = FALSE;		/* TRUE if old-style stipple mapping */
175 
176 char *doinput();
177 
178 
179 /*----------------------------------------------------------------------------*
180  | Routine:	main (argument_count, argument_pointer)
181  |
182  | Results:	parses the command line, accumulating input file names, then
183  |		reads the inputs, passing it directly to output until a ".GS"
184  |		line is read.  Main then passes control to "conv" to do the
185  |		gremlin file conversions.
186  *----------------------------------------------------------------------------*/
187 
188 main(argc, argv)
189 int argc;
190 char **argv;
191 {
192 	register FILE *fp;
193 	register int k;
194 	register char c;
195 	register gfil = 0;
196 	char *file[50];
197 
198 	char *operand();
199 	char *getenv();
200 
201 
202 	if (fp = (FILE *) getenv("PRINTER")) printer = (char *) fp;
203 	if (fp = (FILE *) getenv("TYPESETTER")) printer = (char *) fp;
204 	while (--argc) {
205 	    if (**++argv != '-')
206 		file[gfil++] = *argv;
207 	    else
208 	      switch (c = (*argv)[1]) {
209 
210 		case 0:
211 			file[gfil++] = NULL;
212 			break;
213 
214 		case 'P':
215 		case 'T':	/* final output typesetter name */
216 			printer = operand(&argc, &argv);
217 			break;
218 
219 		case 'L':	/* set library directory */
220 			gremlib = operand(&argc, &argv);
221 			break;
222 
223 		default:
224 			error("unknown switch: %c", c);
225 	    }
226 	}
227 				/* set the resolution for an output device */
228 	getres(printer);	/* named in "printer" */
229 
230 	if (gfil == 0) {	/* no filename, use standard input */
231 		file[0] = NULL;
232 		gfil++;
233 	}
234 
235 	for (k=0; k<gfil; k++) {
236 		if (file[k] != NULL) {
237 			if ((fp = fopen(file[k], "r")) == NULL) {
238 			    error("can't open %s", file[k]);
239 			    exit(1);
240 			}
241 		} else {
242 			fp = stdin;
243 		}
244 
245 		while (doinput(fp) != NULL) {
246 		    if (*c1 == '.' && *c2 == 'G' && *c3 == 'S') {
247 			conv(fp, linenum);
248 		    } else {
249 			fputs(inputline, stdout);
250 		    }
251 		}
252 	}
253 }
254 
255 
256 /*----------------------------------------------------------------------------*
257  | Routine:	error (control_string, args, . . . )
258  |
259  | Results:	prints ("grn: ", the control_string + args, "\n") to stderr
260  *----------------------------------------------------------------------------*/
261 
262 /* VARARGS1 */
263 error(s, a1, a2, a3, a4)
264 char *	s;
265 {
266 	fprintf(stderr, "grn: ");
267 	fprintf(stderr, s, a1, a2, a3, a4);
268 	fprintf(stderr, "\n");
269 }
270 
271 
272 /*----------------------------------------------------------------------------*
273  | Routine:	char  * operand (& argc,  & argv)
274  |
275  | Results:	returns address of the operand given with a command-line
276  |		option.  It uses either "-Xoperand" or "-X operand", whichever
277  |		is present.  The program is terminated if no option is present.
278  |
279  | Side Efct:	argc and argv are updated as necessary.
280  *----------------------------------------------------------------------------*/
281 
282 char *operand(argcp, argvp)
283 int * argcp;
284 char ***argvp;
285 {
286 	if ((**argvp)[2]) return(**argvp + 2); /* operand immediately follows */
287 	if ((--*argcp) <= 0) {			/* no operand */
288 	    error("command-line option operand missing.");
289 	    exit(8);
290 	}
291 	return(*(++(*argvp)));			/* operand is next word */
292 }
293 
294 
295 /*----------------------------------------------------------------------------*
296  | Routine:	getres (device_name)
297  |
298  | Results:	sets "res" to the resolution of the output device specified
299  |		by the string dev.
300  *----------------------------------------------------------------------------*/
301 
302 getres(name)
303 char *name;
304 {
305 	int fin;
306 	struct dev device;
307 	char temp[60];
308 
309 	sprintf(temp, "%s/dev%s/DESC.out", DEVDIR, name);
310 	if ((fin = open(temp, 0)) < 0) {
311 	    error("can't open tables for %s", temp);
312 	    exit(1);
313 	}
314 	read(fin, &device, sizeof(struct dev));
315 	res = (double) device.res;
316 	close(fin);
317 }
318 
319 
320 /*----------------------------------------------------------------------------*
321  | Routine:	char  * doinput (file_pointer)
322  |
323  | Results:	a line of input is read into "inputline".
324  |
325  | Side Efct:	"linenum" is incremented.
326  |
327  | Bugs:	lines longer than MAXINLINE are NOT checked, except for
328  |		updating "linenum"
329  *----------------------------------------------------------------------------*/
330 
331 char *doinput(fp)
332 FILE *fp;
333 {
334     char *k;
335 
336 
337     if ((k = fgets(inputline, MAXINLINE, fp)) == NULL)
338 	return k;
339     if (index (inputline, '\n'))	/* ++ only if it's a complete line */
340 	linenum++;
341     return (char*) !NULL;
342 }
343 
344 
345 /*----------------------------------------------------------------------------*
346  | Routine:	initpic ( )
347  |
348  | Results:	sets all parameters to the normal defaults, possibly overridden
349  |		by a setdefault command.  Initilaize the picture variables,
350  |		and output the startup commands to troff to begin the picture.
351  *----------------------------------------------------------------------------*/
352 
353 initpic()
354 {
355     register int i;
356 
357     for (i = 0; i < STYLES; i++) {	/* line thickness defaults */
358 	thick[i] = defthick[i];
359     }
360     for (i = 0; i < FONTS; i++) {	/* font name defaults */
361 	tfont[i] = deffont[i];
362     }
363     for (i = 0; i < SIZES; i++) {	/* font size defaults */
364 	tsize[i] = defsize[i];
365     }
366     for (i = 0; i < NSTIPPLES; i++) {	/* stipple font file default indices */
367 	stipple_index[i] = defstipple_index[i];
368     }
369     stipple = defstipple;
370 
371     gremlinfile[0] = 0;		/* filename is "null" */
372     setdefault = 0;		/* this is not the default settings (yet) */
373 
374     toppoint = BIG;		/* set the picture bounds out */
375     bottompoint = -BIG;		/* of range so they'll be set */
376     leftpoint = BIG;		/* by "savebounds" on input */
377     rightpoint = -BIG;
378 
379     pointscale = defpoint;	/* Flag for scaling point sizes default. */
380     xscale = scale;		/* default scale of individual pictures */
381     width = 0.0;		/* size specifications input by user */
382     height = 0.0;
383 
384     linethickness = DEFTHICK;	/* brush styles */
385     linmod = DEFSTYLE;
386 }
387 
388 
389 /*----------------------------------------------------------------------------*
390  | Routine:	conv (file_pointer, starting_line)
391  |
392  | Results:	at this point, we just passed a ".GS" line in the input file.
393  |		conv reads the input and calls "interpret" to process commands,
394  |		gathering up information until a ".GE" line is found.  It then
395  |		calls "HGPrint" to do the translation of the gremlin file to
396  |		troff commands.
397  *----------------------------------------------------------------------------*/
398 
399 conv(fp, baseline)
400 register FILE *fp;
401 int baseline;
402 {
403 	register FILE *gfp = NULL;	/* input file pointer */
404 	register int done = 0;		/* flag to remember if finished */
405 	register ELT *e;	/* current element pointer */
406 	ELT *PICTURE;		/* whole picture data base pointer */
407 	double temp;		/* temporary calculating area */
408 	POINT ptr;	/* coordinates of a point to pass to "mov" routine */
409 	int flyback;	/* flag "want to end up at the top of the picture?" */
410 
411 
412 	initpic();			/* set defaults, ranges, etc. */
413 	strcpy (GScommand, inputline);	/* save ".GS" line for later */
414 	do {
415 	    done = (doinput(fp) == NULL);		     /* test for EOF */
416 	    flyback = *c3 == 'F';			   /* and .GE or .GF */
417 	    done |= (*c1 == '.' && *c2 == 'G' && (*c3 == 'E' || flyback));
418 
419 	    if (done) {
420 		if (setdefault) savestate();
421 
422 		if (!gremlinfile[0]) {
423 		    if(!setdefault)
424 			error("at line %d: no picture filename.\n", baseline);
425 		    return;
426 		}
427 		if ((gfp = fopen(gremlinfile, "r")) == NULL) {
428 		    char name[MAXINLINE]; /* if the file isn't in the current */
429 					/* directory, try the gremlin library */
430 		    sprintf(name, "%s%s", gremlib, gremlinfile);
431 		    if ((gfp = fopen(name, "r")) == NULL) {
432 			error("can't open %s", gremlinfile);
433 			return;
434 		    }
435 		}
436 		PICTURE = DBRead(gfp);			/* read picture file */
437 		fclose(gfp);
438 		if (DBNullelt(PICTURE))
439 		    return;		/* if a request is made to make the */
440 					/* picture fit into a specific area, */
441 					/* set the scale to do that. */
442 
443 		SCREENtoINCH = (SUNFILE) ? 0.014 : 0.02;
444 
445 		if (stipple == (char *) NULL)	/* if user forgot stipple */
446 		    if (has_polygon(PICTURE))	/* and picture has a polygon */
447 			stipple = DEFSTIPPLE;	/* then set the default */
448 
449 		if ((temp = bottompoint - toppoint) < 0.1) temp = 0.1;
450 		temp = (height != 0.0) ? height / (temp * SCREENtoINCH)  : BIG;
451 		if ((troffscale = rightpoint - leftpoint) < 0.1) troffscale=0.1;
452 		troffscale = (width != 0.0) ?
453 				width / (troffscale * SCREENtoINCH)  : BIG;
454 		if (temp == BIG && troffscale == BIG) {
455 		    troffscale = xscale;
456 		} else {
457 		    if (temp < troffscale) troffscale = temp;
458 		}				/* here, troffscale is the */
459 						/* picture's scaling factor */
460 		if (pointscale) {
461 		    register int i;		/* do pointscaling here, when */
462 					     /* scale is known, before output */
463 
464 		    for (i = 0; i < SIZES; i++)
465 			tsize[i] = (int) (troffscale * (double) tsize[i] + 0.5);
466 
467 		}
468 						   /* change to device units */
469 		troffscale *= SCREENtoINCH * res;	/* from screen units */
470 
471 		ytop = toppoint * troffscale;		/* calculate integer */
472 		ybottom = bottompoint * troffscale;	/* versions of the */
473 		xleft = leftpoint * troffscale;		/* picture limits */
474 		xright = rightpoint * troffscale;
475 					/* save stuff in number registers, */
476 					/*   register g1 = picture width and */
477 					/*   register g2 = picture height, */
478 					/*   set vertical spacing, no fill, */
479 					/*   and break (to make sure picture */
480 					/*   starts on left), and put out the */
481 					/*   user's ".GS" line. */
482 		printf(
483 ".br\n.nr g1 %du\n.nr g2 %du\n%s.nr g3 \\n(.f\n.nr g4 \\n(.s\n\\0\n.sp -1\n",
484 			xright-xleft, ybottom-ytop, GScommand);
485 
486 		if (stipple) {		/* stipple requested for this picture */
487 		    printf(".st %s\n", stipple);
488 		}
489 
490 		lastx = xleft;		/* note where we are, (upper left */
491 		lastyline = lasty = ytop;	/* corner of the picture) */
492 
493 		e = PICTURE;
494 		while (!DBNullelt(e)) {	/* traverse picture;  print elements */
495 		    HGPrintElt(e, baseline);
496 		    e = DBNextElt(e);
497 		}
498 				/* decide where to end picture */
499 		if (flyback) {		/* end piture at upper left */
500 		    ptr.x = leftpoint;
501 		    ptr.y = toppoint;
502 		} else {		/* end picture at lower left */
503 		    ptr.x = leftpoint;
504 		    ptr.y = bottompoint;
505 		}
506 		tmove(&ptr);		/* restore default line parameters, */
507 					/* restore everything to the way */
508 					/* it was before the .GS, then put */
509 					/* out the ".GE" line from user */
510 		printf("\\D't %du'\\D's %du'\n", DEFTHICK, DEFSTYLE);
511 		if (flyback) {		/* make sure we end up at top of */
512 		    printf(".sp -1\n");		/* picture if "flying back" */
513 		}
514 		if (stipple) {		/* restore stipple to previous */
515 		    printf(".st\n");
516 		}
517 		printf(".br\n.ft \\n(g3\n.ps \\n(g4\n%s", inputline);
518 	    } else {
519 		interpret(inputline);	/* take commands from the input file */
520 	    }
521 	} while (!done);
522 }
523 
524 
525 /*----------------------------------------------------------------------------*
526  | Routine:	savestate  ( )
527  |
528  | Results:	all the current  scaling / font size / font name / thickness /
529  |		pointscale  settings are saved to be the defaults.  Scaled
530  |		point sizes are NOT saved.  The scaling is done each time a
531  |		new picture is started.
532  |
533  | Side Efct:	scale, and def* are modified.
534  *----------------------------------------------------------------------------*/
535 
536 savestate()
537 {
538     register int i;
539 
540     for (i = 0; i < STYLES; i++) {	/* line thickness defaults */
541 	defthick[i] = thick[i];
542     }
543     for (i = 0; i < FONTS; i++) {	/* font name defaults */
544 	deffont[i] = tfont[i];
545     }
546     for (i = 0; i < SIZES; i++) {	/* font size defaults */
547 	defsize[i] = tsize[i];
548     }
549     for (i = 0; i < NSTIPPLES; i++) {	/* stipple font file default indices */
550 	defstipple_index[i] = stipple_index[i];
551     }
552     defstipple = stipple;	/* if stipple has been set, it's remembered */
553 
554     scale *= xscale;		/* default scale of individual pictures */
555     defpoint = pointscale;	/* flag for scaling pointsizes from x factors */
556 }
557 
558 
559 /*----------------------------------------------------------------------------*
560  | Routine:	savebounds (x_coordinate, y_coordinate)
561  |
562  | Results:	keeps track of the maximum and minimum extent of a picture
563  |		in the global variables:  left-, right-, top- and bottompoint.
564  |		"savebounds" assumes that the points have been oriented to
565  |		the correct direction.  No scaling has taken place, though.
566  *----------------------------------------------------------------------------*/
567 
568 savebounds(x, y)
569 float x;
570 float y;
571 {
572     if (x < leftpoint) leftpoint = x;
573     if (x > rightpoint) rightpoint = x;
574     if (y < toppoint) toppoint = y;
575     if (y > bottompoint) bottompoint = y;
576 }
577 
578 
579 /*----------------------------------------------------------------------------*
580  | Routine:	interpret (character_string)
581  |
582  | Results:	commands are taken from the input string and performed.
583  |		Commands are separated by the endofline, and are of the
584  |		format:
585  |			string1 string2
586  |
587  |		where string1 is the command and string2 is the argument.
588  |
589  | Side Efct:	font and size strings, plus the gremlin file name and the
590  |		width and height variables are set by this routine.
591  *----------------------------------------------------------------------------*/
592 
593 interpret (line)
594 char *line;
595 {
596     char str1[MAXINLINE];
597     char str2[MAXINLINE];
598     register char *chr;
599     register int i;
600     double par;
601 
602     str2[0] = '\0';
603     sscanf(line, "%80s%80s", &str1[0], &str2[0]);
604     for (chr = &str1[0]; *chr; chr++)		/* convert command to */
605 	if(isupper(*chr)) *chr = tolower(*chr);		/* lower case */
606     switch (str1[0]) {
607 
608 	case '1':
609 	case '2':	/* font sizes */
610 	case '3':
611 	case '4':
612 	    i = atoi(str2);
613 	    if (i > 0 && i < 1000)
614 		tsize[str1[0] - '1'] = i;
615 	    else
616 		error("bad font size value at line %d", linenum);
617 	    break;
618 
619 	case 'r':	/* roman */
620 	    if(str2[0] < '0') goto nofont;
621 	    tfont[0] = malloc(strlen(str2) + 1);
622 	    strcpy(tfont[0], str2);
623 	    break;
624 
625 	case 'i':	/* italics */
626 	    if(str2[0] < '0') goto nofont;
627 	    tfont[1] = malloc(strlen(str2) + 1);
628 	    strcpy(tfont[1], str2);
629 	    break;
630 
631 	case 'b':	/* bold */
632 	    if(str2[0] < '0') goto nofont;
633 	    tfont[2] = malloc(strlen(str2) + 1);
634 	    strcpy(tfont[2], str2);
635 	    break;
636 
637 	case 's':	/* special */
638 	    if (str1[1] == 'c') goto scalecommand;	/* or scale */
639 
640 	    if(str2[0] < '0') {
641 	nofont:	error("no fontname specified in line %d", linenum);
642 		break;
643 	    }
644 	    if (str1[1] == 't') goto stipplecommand;	/* or stipple */
645 
646 	    tfont[3] = malloc(strlen(str2) + 1);
647 	    strcpy(tfont[3], str2);
648 	    break;
649 
650 	case 'l':	/* l */
651 	    if ((str1[1] < '1') || (str1[1] > '8'))
652 		goto stipplecommand;
653 
654 	    /* else set stipple index */
655 	    i = atoi(str2);
656 	    if (i >= 0 && i < 256)
657 		stipple_index[str1[1] - '1'] = i;
658 	    else
659 		error("bad stipple index value at line %d", linenum);
660 	    break;
661 
662 	stipplecommand:	/* stipple */
663 	    stipple = malloc(strlen(str2) + 1);
664 	    strcpy(stipple, str2);
665 	    break;
666 
667 	case 'o':	/* oldstipplemap */
668 	    oldstipmap = TRUE;
669 	    break;
670 
671 	case 't':	/* thick */
672 	    thick[2] = atoi(str2);
673 	    break;
674 
675 	case 'm':	/* medium */
676 	    thick[5] = atoi(str2);
677 	    break;
678 
679 	case 'n':	/* narrow */
680 	    thick[0] = thick[1] = thick[3] = thick[4] = atoi(str2);
681 	    break;
682 
683 	case 'x':	/* x */
684 	scalecommand:	/* scale */
685 	    par = atof(str2);
686 	    if (par > 0.0)
687 		xscale *= par;
688 	    else
689 		error("illegal scale value on line %d", linenum);
690 	    break;
691 
692 	case 'f':	/* file */
693 	    strcpy(gremlinfile, str2);
694 	    break;
695 
696 	case 'w':	/* width */
697 	    width = atof(str2);
698 	    if (width < 0.0) width = -width;
699 	    break;
700 
701 	case 'h':	/* height */
702 	    height = atof(str2);
703 	    if (height < 0.0) height = -height;
704 	    break;
705 
706 	case 'd':	/* defaults */
707 	    setdefault = 1;
708 	    break;
709 
710 	case 'p':	/* pointscale */
711 	    if (strcmp("off", str2))
712 		pointscale = 1;
713 	    else
714 		pointscale = 0;
715 	    break;
716 
717 	default:
718 	    error("unknown command, %s, on line %d", str1, linenum);
719 	    exit(8);
720 	    break;
721     };
722 }
723 
724 
725 /*
726  * return TRUE if picture contains a polygon
727  * otherwise FALSE
728  */
729 has_polygon(elist)
730 register ELT *elist;
731 {
732     while (!DBNullelt(elist)) {
733 	if (elist->type == POLYGON)
734 	    return(1);
735 	elist = DBNextElt(elist);
736     }
737 
738     return(0);
739 }
740