1 /*	main.c	1.5	(Berkeley) 83/08/12
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 may be overridden, as in gprint, on the
7  * command line and are further overridden by commands in the input.
8  *
9  *	command options are:
10  *
11  *	-1 #	sets point size 1 to #.  also for -2, -3, -4.  Defaults
12  *		are 12, 16, 24, 36.
13  *
14  *	-r ss	sets gremlin's roman font to troff font ss.  Also for -i,
15  *		-b and -s for italics, bold, and special fonts.  This does
16  *		NOT affect font changes imbedded into strings.  A \fI, for
17  *		instance, will get the italics font regardless of what -i
18  *		is set to.
19  *
20  *	-n #	set narrow line thickness to # pixels.  Also for -m (medium)
21  *		and -t (thick).  Defaults are 1, 3, and 5 pixels.
22  *
23  *	-x #	scale the picture by x (integer or decimal).
24  *
25  *	-Tdev	Prepare output for "dev" printer.  Default is for the varian
26  *		and versatec printers.  Devices acceptable are:  ver, var, ip.
27  *
28  *	-p	prompt user for fonts, sizes and thicknesses.
29  */
30 
31 
32 #include <ctype.h>
33 #include "gprint.h"
34 #include "dev.h"
35 
36 extern char *calloc();
37 extern char *rindex();
38 
39 /* database imports */
40 
41 extern HGPrintElt();
42 extern ELT *DBInit(), *DBRead();
43 extern POINT *PTInit(), *PTMakePoint();
44 
45 
46 char *doinput();
47 
48 #define GREMLIB		"/usr/local/gremlib/"
49 #define DEVDIR		"/usr/lib/font/dev"
50 #define DEFAULTDEV	"var"
51 
52 #define MAXINLINE	100		/* input line length */
53 #define DEFTHICK	3		/* default thicknes */
54 #define DEFSTYLE	SOLID		/* default line style */
55 
56 #define SCREENtoINCH	0.02		/* scaling factor, screen to inches */
57 #define BIG	100000000000.0		/* unweildly large floating number */
58 
59 #define JLEFT		-1		/* justification constants - for the */
60 #define JCENTER		0		/*    whole picture - where it will */
61 #define JRIGHT		1		/*    get placed within the line */
62 
63 
64 char	SccsId[] = "main.c	1.5	83/08/12";
65 
66 char	*printer = DEFAULTDEV;	/* device to look up resolution of */
67 double	res;			/* that printer's resolution goes here */
68 
69 int	linethickness;		/* brush styles */
70 int	linmod;
71 int	lastx;			/* point registers for printing elements */
72 int	lasty;
73 int	lastyline;		/* a line's vertical position is NOT the same */
74 				/* after that line is over, so for a line of */
75 				/* drawing commands, vertical spacing is kept */
76 				/* in lastyline */
77 double	scale = SCREENtoINCH;	/* default scale to map gremlin screen to inches
78 				   (modified by -x command-line option) */
79 
80 		    /* list of prompts for asking user to set default values */
81 char	*prompt[] = {				  /* used only for -p option */
82 		"Roman font name? (%s): ",	"Italic font name? (%s): ",
83 		"Bold font name? (%s): ",	"Special font name? (%s): ",
84 		"font size 1? (%s): ",		"font size 2? (%s): ",
85 		"font size 3? (%s): ",		"font size 4? (%s): ",
86 	};
87 
88 			/* these are the default fonts, sizes, */
89 			/*   line styles, and thicknesses.  These */
90 			/*   can be modified from command-line */
91 			/*   options, and are reset each time the */
92 			/*   start of a picture (.GS) is found. */
93 
94 char	*defstring[] = {
95 		"R\0         ", "I\0         ", "B\0         ", "S\0         ",
96 		"10\0        ", "16\0        ", "24\0        ", "36\0        "
97 	};
98 int	defthick[STYLES] = { 1, 1, 5, 1, 1, 3 };	/* defaults... */
99 int	style[STYLES] = { DOTTED, DOTDASHED, SOLID, DASHED, SOLID, SOLID };
100 int	thick[STYLES];	/* thicknesses set by defaults, then by commands */
101 char	*tfont[FONTS];	/* fonts originally set to defstring values, then */
102 char 	*tsize[SIZES];	/*    optionally changed by commands inside grn */
103 
104 double	xscale;		/* scaling factor from individual pictures */
105 double	troffscale;	/* scaling factor at output time */
106 double	width;		/* user-request maximum width for picture (in inches) */
107 double	height;		/* user-request height */
108 
109 double	toppoint;		/* remember the picture */
110 double	bottompoint;		/* bounds in these variables */
111 double	leftpoint;
112 double	rightpoint;
113 
114 int	ytop;			/* these are integer versions of the above */
115 int	ybottom;		/* so not to convert each time they're used */
116 int	xleft;
117 int	xright;
118 
119 int	linenum = 0;			/* line number of input file */
120 char	inputline[MAXINLINE];		/* spot to filter through the file */
121 char	*c1 = inputline;		/* c1, c2, and c3 will be used to */
122 char	*c2 = inputline + 1;		/* hunt for lines that begin with */
123 char	*c3 = inputline + 2;		/* ".GS" by looking individually */
124 char	GScommand[MAXINLINE];		/* put user's ".GS" command line here */
125 char	gremlinfile[50];		/* filename to use for a picture */
126 
127 
128 /*----------------------------------------------------------------------------*
129  | Routine:	main (argument_count, argument_pointer)
130  |
131  | Results:	parses the command line, accumulating input file names, then
132  |		reads the inputs, passing it directly to output until a ".GS"
133  |		line is read.  Main then passes control to "conv" to do the
134  |		gremlin file conversions.
135  |
136  | Bugs:	a -p option ALWAYS reads standard input.  Even if the input
137  |		file is coming in that way.
138  *----------------------------------------------------------------------------*/
139 
140 main(argc, argv)
141 int argc;
142 char **argv;
143 {
144 	register FILE *fp = stdin;
145 	register int k;
146 	register char c;
147 	char *file[50], string[50], *arg;
148 	float mult;
149 	int brsh, gfil = 0;
150 
151 
152 	argc--;
153 	argv++;
154 	while (argc--) {
155 	    if (*(arg = *argv++) != '-')
156 		file[gfil++] = arg;
157 	    else switch (c = *++arg) {
158 
159 		case '1':	/* select sizes */
160 		case '2':
161 		case '3':
162 		case '4':
163 			if (*++arg == '\0' && argc--)
164 			    arg = *argv++;
165 			strcpy(defstring[c + FONTS - '1'], arg);
166 			break;
167 		case 'r':	/* select Roman font */
168 			if (*++arg == '\0' && argc--)
169 			    arg = *argv++;
170 			strcpy(defstring[0], arg);
171 			break;
172 		case 'i':	/* select italics font */
173 			if (*++arg == '\0' && argc--)
174 			    arg = *argv++;
175 			strcpy(defstring[1], arg);
176 			break;
177 		case 'b':	/* select bold font */
178 			if (*++arg == '\0' && argc--)
179 			    arg = *argv++;
180 			strcpy(defstring[2], arg);
181 			break;
182 		case 's':	/* select special font */
183 			if (*++arg == '\0' && argc--)
184 			    arg = *argv++;
185 			strcpy(defstring[3], arg);
186 			break;
187 		case 'n':	/* select narrow brush width */
188 			if (*++arg == '\0' && argc--)
189 			    arg = *argv++;
190 			(void) sscanf(arg, "%d", &brsh);
191 			defthick[0]=defthick[1]=defthick[3]=defthick[4] = brsh;
192 			break;
193 		case 't':	/* select thick brush width */
194 			if (*++arg == '\0' && argc--)
195 			    arg = *argv++;
196 			(void) sscanf(arg, "%d", &brsh);
197 			defthick[2] = brsh;
198 			break;
199 		case 'm':	/* select medium brush width */
200 			if (*++arg == '\0' && argc--)
201 			    arg = *argv++;
202 			(void) sscanf(arg, "%d", &brsh);
203 			defthick[5] = brsh;
204 			break;
205 		case 'x':	/* select scale */
206 			if (*++arg == '\0' && argc--)
207 			    arg = *argv++;
208 			sscanf(arg,"%f", &mult);
209 			scale *= mult;
210 			break;
211 		case 'T':	/* final output typesetter name */
212 			printer = arg + 1;
213 			break;
214 		case 'p':	/* prompt for font and size parameters */
215 			for (k = 0; k < 8; k++) {
216 			    fprintf(stderr, prompt[k], string[k]);
217 			    gets(string);
218 			    if (*string != '\0') strcpy(string[k], string);
219 			}
220 			fprintf(stderr,"narrow brush size? (%d): ",defthick[0]);
221 			gets(string);
222 			if (*string != '\0') {
223 			    sscanf(string, "%d", &brsh);
224 			    defthick[0] = defthick[1] = defthick[3]
225 							= defthick[4] = brsh;
226 			}
227 			fprintf(stderr,"medium brush size? (%d): ",defthick[5]);
228 			gets(string);
229 			if (*string != '\0') {
230 			    sscanf(string, "%d", &brsh);
231 			    defthick[5] = brsh;
232 			}
233 			fprintf(stderr, "thick brush size? (%d): ",defthick[2]);
234 			gets(string);
235 			if (*string != '\0') {
236 			    sscanf(string, "%d", &brsh);
237 			    defthick[2] = brsh;
238 			}
239 			break;
240 		default:
241 			fprintf(stderr, "unknown switch: %c\n", c);
242 	    }
243 	}
244 				/* set the resolution for an output device */
245 	getres(printer);	/* named in "printer" */
246 
247 	if (gfil == 0) {	/* no filename, use standard input */
248 		file[0] = NULL;
249 		gfil++;
250 	}
251 	for (k=0; k<gfil; k++) {
252 		if (file[k] != NULL) {
253 			if ((fp = fopen(file[k], "r")) == NULL) {
254 			    fprintf(stderr, "grn: can't open %s\n", file[k]);
255 			    continue;
256 			}
257 			if (k == 0) {
258 				if ((arg = rindex(file[k], '/')) == NULL)
259 					arg = file[k];
260 				else
261 					arg++;
262 			}
263 		} else {
264 			fp = stdin;
265 		}
266 
267 		while (doinput(fp) != NULL) {
268 		    if (*c1 == '.' && *c2 == 'G' && *c3 == 'S') {
269 			conv(fp, linenum);
270 		    } else {
271 			fputs(inputline, stdout);
272 		    }
273 		}
274 	}
275 }
276 
277 
278 /*----------------------------------------------------------------------------*
279  | Routine:	getres (device_name)
280  |
281  | Results:	sets "res" to the resolution of the output device specified
282  |		by the string dev.
283  *----------------------------------------------------------------------------*/
284 
285 getres(name)
286 char *name;
287 {
288 	int fin;
289 	struct dev device;
290 	char temp[60];
291 
292 	sprintf(temp, "%s%s/DESC.out", DEVDIR, name);
293 	if ((fin = open(temp, 0)) < 0) {
294 	    fprintf(stderr, "can't open tables for %s\n", temp);
295 	    exit(1);
296 	}
297 	read(fin, &device, sizeof(struct dev));
298 	res = (double) device.res;
299 	close(fin);
300 }
301 
302 
303 /*----------------------------------------------------------------------------*
304  | Routine:	char  * doinput (file_pointer)
305  |
306  | Results:	a line of input is read into "inputline".
307  |
308  | Side Efct:	"linenum" is incremented.
309  |
310  | Bugs:	lines longer than MAXINLINE are NOT checked, except for
311  |		updating "linenum"
312  *----------------------------------------------------------------------------*/
313 
314 char *doinput(fp)
315 FILE *fp;
316 {
317     register char *k;
318 
319 
320     if ((k = fgets(inputline, MAXINLINE, fp)) == NULL)
321 	return k;
322     if (index (inputline, '\n'))	/* ++ only if it's a complete line */
323 	linenum++;
324     return (char*) !NULL;
325 }
326 
327 
328 /*----------------------------------------------------------------------------*
329  | Routine:	initpic ( )
330  |
331  | Results:	sets all parameters to the normal defaults, possibly overridden
332  |		by the command line flags.  Initilaize the picture variables,
333  |		and output the startup commands to troff to begin the picture.
334  *----------------------------------------------------------------------------*/
335 
336 initpic()
337 {
338     register int i;
339 
340     for (i = 0; i < STYLES; i++) {	/* line thickness defaults */
341 	thick[i] = defthick[i];
342     }
343     for (i = 0; i < FONTS; i++) {	/* font name defaults */
344 	tfont[i] = defstring[i];
345     }
346     for (i = 0; i < SIZES; i++) {	/* font size defaults */
347 	tsize[i] = defstring[FONTS + i];
348     }
349 
350     gremlinfile[0] = 0;		/* filename is "null" */
351 
352     toppoint = BIG;		/* set the picture bounds out */
353     bottompoint = 0.0;		/* of range so they'll be set */
354     leftpoint = BIG;		/* by "savebounds" on input */
355     rightpoint = 0.0;
356 
357     xscale = scale;		/* default scale of individual pictures */
358     width = 0.0;		/* size specifications input by user */
359     height = 0.0;
360 
361     linethickness = DEFTHICK;	/* brush styles */
362     linmod = DEFSTYLE;
363 }
364 
365 
366 /*----------------------------------------------------------------------------*
367  | Routine:	conv (file_pointer, starting_line)
368  |
369  | Results:	at this point, we just passed a ".GS" line in the input file.
370  |		conv reads the input and calls "interpret" to process commands,
371  |		gathering up information until a ".GE" line is found.  It then
372  |		calls "HGPrint" to do the translation of the gremlin file to
373  |		troff commands.
374  *----------------------------------------------------------------------------*/
375 
376 conv(fp, baseline)
377 register FILE *fp;
378 int baseline;
379 {
380 	register FILE *gfp = NULL;
381 	register int done = 0;
382 	register ELT *e;
383 	ELT *PICTURE;
384 	double temp;
385 	POINT ptr;
386 
387 
388 	initpic();			/* set defaults, ranges, etc. */
389 	strcpy (GScommand, inputline);	/* save ".GS" line for later */
390 	do {
391 	    done = (doinput(fp) == NULL);		     /* test for EOF */
392 	    done |= (*c1 == '.' && *c2 == 'G' && *c3 == 'E');	 /*  and .GE */
393 
394 	    if (done) {
395 		if (!gremlinfile[0]) {
396 		    fprintf(stderr, "grn: at line %d: no picture filename.\n",
397 								    baseline);
398 		    return;
399 		}
400 		if ((gfp = fopen(gremlinfile, "r")) == NULL) {
401 		    char name[100];	/* if the file isn't in the current */
402 					/* directory, try the gremlin library */
403 		    sprintf(name, "%s%s", GREMLIB, gremlinfile);
404 		    if ((gfp = fopen(name, "r")) == NULL) {
405 			fprintf(stderr, "grn: can't open %s\n", gremlinfile);
406 			return;
407 		    }
408 		}
409 		PICTURE = DBRead(gfp);			/* read picture file */
410 		fclose(gfp);
411 		if (DBNullelt(PICTURE))
412 		    return;
413 					/* if a request is made to make the */
414 					/* picture fit into a specific area, */
415 					/* set the scale to do that. */
416 		temp = (height != 0.0)  ?
417 			SCREENtoINCH * (bottompoint - toppoint) / height  : BIG;
418 		troffscale = (width != 0.0)  ?
419 			SCREENtoINCH * (rightpoint - leftpoint) / width  : BIG;
420 		if (temp == BIG && troffscale == BIG) {
421 		    troffscale = xscale;
422 		} else {
423 		    if (temp < troffscale) troffscale = temp;
424 		}
425 		troffscale *= res;	/* change to device units from inches */
426 
427 		ytop = toppoint * troffscale;		/* calculate integer */
428 		ybottom = bottompoint * troffscale;	/* versions of the */
429 		xleft = leftpoint * troffscale;		/* picture limits */
430 		xright = rightpoint * troffscale;
431 					/* save stuff in number registers, */
432 					/*   register gw = picture width and */
433 					/*   register gh = picture height, */
434 					/*   set vertical spacing, no fill, */
435 					/*   and break (to make sure picture */
436 					/*   starts on left), and put out the */
437 					/*   user's ".GS" line. */
438 		printf(".nr gw %d\n.nr gh %d\n", xright-xleft, ybottom-ytop);
439 		printf(".br\n%s", GScommand);
440 		printf(".nr g1 \\n(.f\n.nr g2 \\n(.s\n");
441 		printf(".nr g3 \\n(.v\n.nr g4 \\n(.u\n.nf\n.vs 0");
442 
443 		lastx = xleft;		/* note where we are, (upper left */
444 		lastyline = lasty = ytop;	/* corner of the picture) */
445 
446 		e = PICTURE;
447 		while (!DBNullelt(e)) {
448 		    HGPrintElt(e);	/* traverse picture;  print elements */
449 		    e = DBNextElt(e);
450 		}
451 					/* end picture at lower left */
452 		ptr.x = leftpoint;
453 		ptr.y = bottompoint;
454 		tmove(&ptr);		/* restore default line parameters, */
455 					/* put out the ".GE" line from user */
456 					/* then restore everything to the way */
457 					/* it was before the .GS */
458 		printf("\\D't %du'\\D's %du'\n", DEFTHICK, DEFSTYLE);
459 		printf(".ft \\n(g1\n.ps \\n(g2\n");
460 		printf(".vs \\n(g3u\n.if \\n(g4 .fi\n%s", inputline);
461 	    } else {
462 		interpret(inputline);	/* take commands from the input file */
463 	    }
464 	} while (!done);
465 }
466 
467 
468 /*----------------------------------------------------------------------------*
469  | Routine:	savebounds (x_coordinate, y_coordinate)
470  |
471  | Results:	keeps track of the maximum and minimum extent of a picture
472  |		in the global variables:  left-, right-, top- and bottompoint.
473  |		"savebounds" assumes that the points have been oriented to
474  |		the correct direction.  No scaling has taken place, though.
475  *----------------------------------------------------------------------------*/
476 
477 savebounds(x, y)
478 float x;
479 float y;
480 {
481     if (x < leftpoint) {
482 	leftpoint = x;
483     } else if (x > rightpoint) {
484 	rightpoint = x;
485     }
486     if (y < toppoint) {
487 	toppoint = y;
488     } else if (y > bottompoint) {
489 	bottompoint = y;
490     }
491 }
492 
493 
494 /*----------------------------------------------------------------------------*
495  | Routine:	interpret (character_string)
496  |
497  | Results:	commands are taken from the input string and performed.
498  |		Commands are separated by the endofline, and are of the
499  |		format:
500  |			string1 string2
501  |
502  |		where string1 is the command and string2 is the argument.
503  |
504  | Side Efct:	font and size strings, plus the gremlin file name and the
505  |		width and height variables are set by this routine.
506  *----------------------------------------------------------------------------*/
507 
508 interpret (line)
509 char *line;
510 {
511     char str1[MAXINLINE];
512     char str2[MAXINLINE];
513     register char *chr;
514 
515     sscanf(line, "%s%s", &str1[0], &str2[0]);
516     for (chr = &str1[0]; *chr; chr++)		/* convert command to */
517 	if(isupper(*chr)) *chr = tolower(*chr);		/* lower case */
518     switch (str1[0]) {
519 
520 	case '1':
521 	case '2':	/* font sizes */
522 	case '3':
523 	case '4':
524 	    tsize[str1[0] - '1'] = calloc(strlen(str2) + 1);
525 	    strcpy(tsize[str1[0] - '1'], str2);
526 	    break;
527 
528 	case 'r':	/* roman */
529 	    tfont[0] = calloc(strlen(str2) + 1);
530 	    strcpy(tfont[0], str2);
531 	    break;
532 
533 	case 'i':	/* italics */
534 	    tfont[1] = calloc(strlen(str2) + 1);
535 	    strcpy(tfont[1], str2);
536 	    break;
537 
538 	case 'b':	/* bold */
539 	    tfont[2] = calloc(strlen(str2) + 1);
540 	    strcpy(tfont[2], str2);
541 	    break;
542 
543 	case 's':	/* special */
544 	    if (str1[1] == 'c') goto scalecommand;
545 	    tfont[3] = calloc(strlen(str2) + 1);
546 	    strcpy(tfont[3], str2);
547 	    break;
548 
549 	case 't':	/* thick */
550 	    thick[2] = atoi(str2);
551 	    break;
552 
553 	case 'm':	/* medium */
554 	    thick[5] = atoi(str2);
555 	    break;
556 
557 	case 'n':	/* narrow */
558 	    thick[0] = thick[1] = thick[3] = thick[4] = atoi(str2);
559 	    break;
560 
561 	case 'x':	/* x */
562 	scalecommand:	/* scale */
563 	    xscale *= atof(str2);
564 	    if (xscale < 0.0) xscale = -xscale;
565 	    break;
566 
567 	case 'f':	/* file */
568 	    strcpy(gremlinfile, str2);
569 	    break;
570 
571 	case 'w':	/* width */
572 	    width = atof(str2);
573 	    if (width < 0.0) width = -width;
574 	    break;
575 
576 	case 'h':	/* height */
577 	    height = atof(str2);
578 	    if (height < 0.0) height = -height;
579 	    break;
580 
581 	default:
582 	    break;
583     };
584 }
585