1 /* vsort.c	1.4	83/08/19
2  *
3  *	Sorts and shuffles ditroff output for versatec wide printer.  It
4  *	puts pages side-by-side on the output, and fits as many as it can
5  *	on one horizontal span.  The versatec driver sees only pages of
6  *	full width, not the individual pages.  Output is sorted vertically
7  *	and bands are created NLINES pixels high.  Any object that has
8  *	ANY part of it in a band is put on that band.
9  */
10 
11 
12 #include	<stdio.h>
13 #include	<ctype.h>
14 #include	<math.h>
15 
16 
17 #define DEBUGABLE	/* compile-time flag for debugging */
18 #define	FATAL	1
19 #define	NVLIST	3000	/* size of list of vertical spans */
20 #define	OBUFSIZ	250000	/* size of character buffer before sorting */
21 #define	SLOP	1000	/* extra bit of buffer to allow for passing OBUFSIZ */
22 
23 #define FONTDIR "/usr/lib/font"
24 #define INCH	200	/* assumed resolution of the printer (dots/inch) */
25 #define POINT	72	/* number of points per inch */
26 #define WIDTH	7040	/* number of pixels across the page */
27 #define HALF	(INCH/2)
28 #define BAND	3
29 #define NLINES	(int)(BAND * INCH)	/* number of pixels in each band */
30 
31 #define hgoto(n)	if((hpos = leftmarg + n) > maxh) maxh = hpos
32 #define hmot(n)		if((hpos += n) > maxh) maxh = hpos
33 #define vmot(n)		vgoto(vpos + n)
34 
35 
36 #ifdef DEBUGABLE
37 int	dbg	= 0;	/* debug flag != 0 means do debug output */
38 #endif
39 
40 int	size	= 10;	/* current size (points) */
41 int	up	= 0;	/* number of pixels that the current size pushes up */
42 int	down	= 0;	/* # of pixels that the current size will hang down */
43 int	font	= 1;	/* current font */
44 char *	fontdir = FONTDIR;	/* place to find DESC.out file */
45 int	thick	= 3;	/* line thickness */
46 int	style	= -1;	/* line style bit-mask */
47 int	hpos	= 0;	/* horizontal position to be at next (left = 0) */
48 int	vpos	= 0;	/* current vertical position (down positive) */
49 
50 int	maxh	= 0;	/* farthest right we've gone on the current span */
51 int	leftmarg= 0;	/* current page offset */
52 int	pageno	= 0;	/* number of pages on this horizontal span */
53 int	spanno	= 0;	/* current span number for driver in 'p#' commands */
54 
55 
56 struct vlist {
57 	short	v;	/* vertical position of this spread */
58 	short	h;	/* horizontal position */
59 	short	t;	/* line thickness */
60 	short	st;	/* style mask */
61 	short	u;	/* upper extent of height */
62 	short	d;	/* depth of height */
63 	char	s;	/* point size */
64 	char	f;	/* font number */
65 	char	*p;	/* text pointer to this spread */
66 };
67 
68 struct	vlist	vlist[NVLIST + 1];
69 struct	vlist	*vlp;			/* current spread being added to */
70 int	nvlist	= 0;			/* number of spreads in list */
71 int	obufsiz	= OBUFSIZ;
72 char	obuf[OBUFSIZ + SLOP];
73 char	*op = obuf;			/* pointer to current spot in buffer */
74 
75 
76 main(argc, argv)
77 int argc;
78 char *argv[];
79 {
80 	FILE *fp;
81 
82 
83 	vlp = &vlist[0] - 1;		/* initialize pointer to one less */
84 	startspan(0);			/* than beginning so "startspan" can */
85 					/* increment it before using it */
86 	while (argc > 1 && **++argv == '-') {
87 	    switch ((*argv)[1]) {
88 		case 'f':
89 			fontdir = &(*argv)[2];
90 			break;
91 #ifdef DEBUGABLE
92 		case 'd':
93 			dbg = atoi(&(*argv)[2]);
94 			if (!dbg) dbg = 1;
95 			break;
96 
97 		case 's':
98 			if((obufsiz = atoi(&(*argv)[2])) > OBUFSIZ)
99 			    obufsiz = OBUFSIZ;
100 			break;
101 #endif
102 	    }
103 	    argc--;
104 	}
105 
106 	if (argc <= 1)
107 	    conv(stdin);
108 	else
109 	    while (--argc > 0) {
110 		if ((fp = fopen(*argv, "r")) == NULL)
111 		    error(FATAL, "can't open %s", *argv);
112 		conv(fp);
113 		fclose(fp);
114 	    }
115 	done();
116 }
117 
118 			/* read number from input:  copy to output */
119 int getnumber (fp)
120 register FILE *fp;
121 {
122 	register int k;
123 	register char c;
124 
125 	while ((c = getc(fp)) == ' ')
126 	    ;
127 	k = 0;
128 	do {
129 	    k = 10 * k + (*op++ = c) - '0';
130 	} while (isdigit(c = getc(fp)));
131 	ungetc(c, fp);
132 	return (k);
133 }
134 
135 			/* read number from input:  do _N_O_T copy to output */
136 int ngetnumber (fp)
137 register FILE *fp;
138 {
139 	register int k;
140 	register char c;
141 
142 	while ((c = getc(fp)) == ' ')
143 	    ;
144 	k = 0;
145 	do {
146 	    k = 10 * k + c - '0';
147 	} while (isdigit(c = getc(fp)));
148 	ungetc(c, fp);
149 	return (k);
150 }
151 
152 
153 conv(fp)
154 register FILE *fp;
155 {
156 	register int c;
157 	int m, n, m1, n1;
158 	char buf[SLOP];
159 
160 	while ((c = getc(fp)) != EOF) {
161 #ifdef DEBUGABLE
162 	    if (dbg > 2) fprintf(stderr, "%c i=%d V=%d\n", c, op-obuf, vpos);
163 #endif
164 	    if (op > obuf + obufsiz)
165 		oflush();
166 	    switch (c) {
167 		case '\n':	/* let text input through */
168 		case '\t':
169 		case ' ':
170 			*op++ = c;
171 			break;
172 		case '{':	/* push down current environment */
173 			*op++ = c;
174 			t_push();
175 			break;
176 		case '}':	/* pop up last environment */
177 			*op++ = c;
178 			t_pop();
179 			break;
180 		case '0': case '1': case '2': case '3': case '4':
181 		case '5': case '6': case '7': case '8': case '9':
182 				/* two motion digits plus a character */
183 			*op++ = c;
184 			hmot((c-'0') * 10 + (*op++ = getc(fp)) - '0');
185 			*op++ = getc(fp);
186 			setlimit();
187 			break;
188 		case 'c':	/* single ascii character */
189 			*op++ = c;
190 			*op++ = getc(fp);
191 			setlimit();
192 			break;
193 		case 'C':	/* white-space terminated funny character */
194 			*op++ = c;
195 			while ((*op++ = c = getc(fp)) != ' ' && c != '\n')
196 				;
197 			setlimit();
198 			break;
199 		case 't':	/* straight text */
200 			*op++ = c;
201 			fgets(op, SLOP, fp);
202 			op += strlen(op);
203 			setlimit();
204 			break;
205 		case 'D':	/* draw function */
206 			fgets(buf, SLOP, fp);
207 			switch (buf[0]) {
208 			case 's':	/* "style" */
209 				sscanf(buf+1, "%d", &style);
210 				sprintf(op, "D%s", buf);
211 				break;
212 			case 't':	/* thickness */
213 				sscanf(buf+1, "%d", &thick);
214 				sprintf(op, "D%s", buf);
215 				break;
216 			case 'l':	/* draw a line */
217 				sscanf(buf+1, "%d %d", &n, &m);
218 						/* put line on its own spread */
219 				if (m < 0) {
220 				    startspan(vpos + m);
221 				    vlp->d = vpos - m;
222 				} else {
223 				    startspan(vpos);
224 				    vlp->d = vpos + m;
225 				}
226 				sprintf(op, "V%dD%s", vpos, buf);
227 				op += strlen(op);
228 				hmot(n);
229 				vmot(m);
230 				break;
231 			case 'c':	/* circle */
232 				sscanf(buf+1, "%d", &n);	/* put circle */
233 				startspan(vpos - n/2);		/* on its own */
234 				vlp->d = vpos + n/2;		/* spread */
235 				sprintf(op, "V%dD%s", vpos, buf);
236 				op += strlen(op);
237 				hmot(n);
238 				startspan(vpos);
239 				break;
240 			case 'e':	/* ellipse */
241 				sscanf(buf+1, "%d %d", &m, &n);	/* same here */
242 				startspan(vpos - n/2);
243 				vlp->d = vpos + n/2;
244 				sprintf(op, "V%dD%s", vpos, buf);
245 				op += strlen(op);
246 				hmot(m);
247 				startspan(vpos);
248 				break;
249 			case 'a':	/* arc */
250 				sscanf(buf+1, "%d %d %d %d", &n, &m, &n1, &m1);
251 				startspan(vpos);
252 				arcbounds(n, m, n1, m1);
253 				sprintf(op, "V%dD%s", vpos, buf);
254 				op += strlen(op);
255 				hmot(n + n1);
256 				vmot(m + m1);
257 				break;
258 			case '~':	/* wiggly line */
259 			case 'g':	/* gremlin curve */
260 			    {
261 				register char *pop;
262 							   /* a curve goes on */
263 				startspan(vpos);	      /* its own span */
264 				sprintf(op, "V%dD", vpos);   /* vertical move */
265 				pop = op += strlen(op);     /* to curve start */
266 				do {			   /* read in rest of */
267 				    sprintf(op, "%s", buf);    /* point input */
268 				    op += strlen(op);
269 				    if (*(op - 1) != '\n')
270 					fgets(buf, SLOP, fp);
271 				} while (*(op - 1) != '\n');
272 				m = n = vpos;		/* = max/min vertical */
273 							/* position for curve */
274 				while (*++pop == ' ');	/* skip '~' & blanks */
275 				do {			/* calculate minimum */
276 				    hpos += atoi(pop);		/* vertical */
277 				    while (isdigit(*++pop));	/* position */
278 				    while (*++pop == ' ');
279 				    vpos += atoi(pop);
280 				    while (isdigit(*++pop));
281 				    while (*pop == ' ') pop++;
282 				    if (vpos < n) n = vpos;
283 				    else if (vpos > m) m = vpos;
284 				} while (*pop != '\n');
285 
286 				vlp->u = vlp->v = n < 0 ? 0 : n;
287 				vlp->d = m;
288 				startspan(vpos);
289 			    }
290 			    break;
291 
292 			default:
293 				error(FATAL,"unknown drawing command %s\n",buf);
294 				break;
295 			}
296 			break;
297 		case 's':
298 			*op++ = c;
299 			size = getnumber(fp);
300 			up = (size * INCH) / POINT;
301 			down = up / 3;
302 			break;
303 		case 'f':
304 			*op++ = c;
305 			font = getnumber(fp);
306 			break;
307 		case 'H':	/* absolute horizontal motion */
308 			*op++ = c;
309 			hgoto(ngetnumber(fp));
310 			sprintf(op, "%d", hpos);
311 			op += strlen(op);	/* reposition by page offset */
312 			break;
313 		case 'h':	/* relative horizontal motion */
314 			*op++ = c;
315 			hmot(getnumber(fp));
316 			break;
317 		case 'w':	/* useless */
318 			break;
319 		case 'V':	/* absolute vertical motion */
320 			vgoto(ngetnumber(fp));
321 			break;
322 		case 'v':
323 			vmot(ngetnumber(fp));
324 			break;
325 		case 'p':	/* new page */
326 			t_page(ngetnumber(fp));
327 			vpos = 0;
328 			break;
329 		case 'n':	/* end of line */
330 			hpos = leftmarg;
331 		case '#':	/* comment */
332 		case 'x':	/* device control */
333 			*op++ = c;
334 			while ((*op++ = getc(fp)) != '\n')
335 				;
336 			break;
337 		default:
338 			error(!FATAL, "unknown input character %o %c\n", c, c);
339 			done();
340 	    }
341 	}
342 }
343 
344 		/* set the "u" and "d" parts of the vlist given the current */
345 setlimit()	/* up and down limits set by the point size */
346 {
347 	register int upv = vpos - up;
348 	register int downv = vpos + down;
349 
350 	if (upv < vlp->u) vlp->u = upv;
351 	if (downv > vlp->d) vlp->d = downv;
352 }
353 
354 
355 arcbounds(h, v, h1, v1)		/* make a circle out of the arc to estimate */
356 int h, v, h1, v1;		/* how far up/down the arc will span */
357 {
358 	register int center = vpos + v;
359 	register int rad = ((int) sqrt ((double) (h*h + v*v))) >> 1;
360 						/* set the vertical extents */
361 	vlp->v = vlp->u = (center - rad) < 0 ? 0 : center - rad;
362 	vlp->d = center + rad;
363 }
364 
365 
366 oflush()	/* sort, then dump out contents of obuf */
367 {
368 	register struct vlist *vp;
369 	register int notdone;
370 	register int topv;
371 	register int botv;
372 	register int i;
373 	register char *p;
374 	int compar();
375 
376 #ifdef DEBUGABLE
377 	if (dbg) fprintf(stderr, "into oflush, V=%d\n", vpos);
378 #endif
379 	if (op == obuf)
380 		return;
381  	qsort((char *) vlist, nvlist, sizeof (struct vlist), compar);
382 	*op++ = 0;
383 
384 	topv = 0;
385 	botv = NLINES - 1;
386 	do {
387 	    notdone = 0;
388 	    vp = vlist;
389 #ifdef DEBUGABLE
390 	    if (dbg) fprintf(stderr, "topv=%d, botv=%d\n", topv, botv);
391 #endif
392 	    for (i = 0; i < nvlist; i++, vp++) {
393 #ifdef DEBUGABLE
394 		if(dbg>1)fprintf(stderr,"u=%d, d=%d,%.60s\n",vp->u,vp->d,vp->p);
395 #endif
396 		if (vp->u <= botv && vp->d >= topv) {
397 		    printf("V%dH%ds%df%dDs%d\nDt%d\n",
398 				vp->v, vp->h, vp->s, vp->f, vp->st, vp->t);
399 		    for (p = vp->p; *p != 0; p++) putchar(*p);
400 		}
401 		notdone |= vp->d > botv;	/* not done if there's still */
402 	    }					/* something to put lower */
403 #ifdef DEBUGABLE
404 	    if (dbg) fprintf(stderr, "topv=%d, botv=%d\n", topv, botv);
405 #endif
406 	    if (notdone) putchar('P');		/* mark the end of the spread */
407 	    topv += NLINES;			/* unless it's the last one */
408 	    botv += NLINES;
409 	} while (notdone);
410 
411 	fflush(stdout);
412 	vlp = vlist;
413 	vlp->p = op = obuf;
414 	vlp->h = hpos;
415 	vlp->v = vpos;
416 	vlp->u = vpos;
417 	vlp->d = vpos;
418 	vlp->s = size;
419 	vlp->f = font;
420 	vlp->st = style;
421 	vlp->t = thick;
422 	*op = 0;
423 	nvlist = 1;
424 }
425 
426 
427 compar(p1, p2)
428 struct vlist *p1, *p2;
429 {
430 	return(p1->v - p2->v);
431 }
432 
433 done()
434 {
435 	oflush();
436 	exit(0);
437 }
438 
439 error(f, s, a1, a2, a3, a4, a5, a6, a7) {
440 	fprintf(stderr, "vsort: ");
441 	fprintf(stderr, s, a1, a2, a3, a4, a5, a6, a7);
442 	fprintf(stderr, "\n");
443 	if (f)
444 		done();
445 }
446 
447 #define	MAXSTATE	5
448 
449 struct state {
450 	int	ssize;
451 	int	sfont;
452 	int	shpos;
453 	int	svpos;
454 };
455 struct	state	state[MAXSTATE];
456 struct	state	*statep = state;
457 
458 t_push()	/* begin a new block */
459 {
460 	statep->ssize = size;
461 	statep->sfont = font;
462 	statep->shpos = hpos;
463 	statep->svpos = vpos;
464 	hpos = vpos = 0;
465 	if (statep++ >= state+MAXSTATE)
466 		error(FATAL, "{ nested too deep");
467 	hpos = vpos = 0;
468 }
469 
470 t_pop()	/* pop to previous state */
471 {
472 	if (--statep < state)
473 		error(FATAL, "extra }");
474 	size = statep->ssize;
475 	font = statep->sfont;
476 	hpos = statep->shpos;
477 	vpos = statep->svpos;
478 }
479 
480 
481 	/* vertical motion:  start new vertical span if necessary */
482 vgoto(n)
483 register int n;
484 {
485     if (n != vpos)
486 	startspan(n);
487     vpos = n;
488 }
489 
490 
491 t_page(n)
492 int n;
493 {
494     register int x;
495 
496     pageno++;
497     if (maxh > (WIDTH - INCH)	/* if we're close to the edge */
498 	    || n == 1		/* or if i think we'll go over with this page */
499 	    || leftmarg + leftmarg / pageno > (WIDTH - INCH)) {
500 	oflush();				/* make it a REAL page-break */
501 	sprintf(op, "p%d\n", ++spanno);
502 	op += strlen(op);
503 	pageno = leftmarg = maxh = 0;
504     } else {					    /* x = last page's width */
505 	x = (maxh - leftmarg + (HALF - 1)) / HALF;	/*  (in half-inches) */
506 	if (x > 12 && x <= 17)
507 	    leftmarg += (8 * INCH) + HALF; 		/* if close to 8.5"  */
508 	else						/* then make it so   */
509 	    leftmarg = ((maxh + HALF) / HALF) * HALF;	/* else set it to the */
510     }							/* nearest half-inch */
511 }
512 
513 
514 startspan(n)
515 register int n;
516 {
517 	*op++ = 0;
518 	if (nvlist >= NVLIST) {
519 	    oflush();
520 	}
521 	vlp++;
522 	vlp->p = op;
523 	vlp->v = n;
524 	vlp->d = n;
525 	vlp->u = n;
526 	vlp->h = hpos;
527 	vlp->s = size;
528 	vlp->f = font;
529 	vlp->st = style;
530 	vlp->t = thick;
531 	nvlist++;
532 }
533