1 /*========================================================================*\
2 
3 Copyright (c) 1990-2004  Paul Vojta
4 
5 Permission is hereby granted, free of charge, to any person obtaining a copy
6 of this software and associated documentation files (the "Software"), to
7 deal in the Software without restriction, including without limitation the
8 rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9 sell copies of the Software, and to permit persons to whom the Software is
10 furnished to do so, subject to the following conditions:
11 
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
14 
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 PAUL VOJTA OR ANY OTHER AUTHOR OF THIS SOFTWARE BE LIABLE FOR ANY CLAIM,
19 DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21 OTHER DEALINGS IN THE SOFTWARE.
22 
23 NOTE:
24 This module is based on prior work as noted below.
25 
26 \*========================================================================*/
27 
28 /*
29  * Support drawing routines for TeXsun and TeX
30  *
31  *      Copyright, (C) 1987, 1988 Tim Morgan, UC Irvine
32  *	Adapted for xdvi by Jeffrey Lee, U. of Toronto
33  *
34  * At the time these routines are called, the values of hh and vv should
35  * have been updated to the upper left corner of the graph (the position
36  * the \special appears at in the dvi file).  Then the coordinates in the
37  * graphics commands are in terms of a virtual page with axes oriented the
38  * same as the Imagen and the SUN normally have:
39  *
40  *                      0,0
41  *                       +-----------> +x
42  *                       |
43  *                       |
44  *                       |
45  *                      \ /
46  *                       +y
47  *
48  * Angles are measured in the conventional way, from +x towards +y.
49  * Unfortunately, that reverses the meaning of "counterclockwise"
50  * from what it's normally thought of.
51  *
52  * A lot of floating point arithmetic has been converted to integer
53  * arithmetic for speed.  In some places, this is kind-of kludgy, but
54  * it's worth it.
55  */
56 
57 
58 #include <setjmp.h>
59 
60 #include "xdvi-config.h"
61 #include "xdvi.h"
62 
63 #include <ctype.h>
64 
65 #include "kpathsea/c-fopen.h"
66 #include "kpathsea/c-stat.h"
67 #include "kpathsea/line.h"
68 #include "kpathsea/tex-file.h"
69 
70 #include "special.h"
71 #include "hypertex.h"
72 #include "dvi.h"
73 #include "message-window.h"
74 #include "events.h"
75 #include "dvi-init.h"
76 #include "dvi-draw.h"
77 #include "statusline.h"
78 #include "util.h"
79 #include "image-magick.h"
80 #include "pagesel.h"
81 #include "my-snprintf.h"
82 #include "string-utils.h"
83 
84 #if PS
85 # ifdef	PS_DPS
86 # include "psdps.h"
87 # endif
88 # ifdef	PS_NEWS
89 # include "psnews.h"
90 # endif
91 # ifdef	PS_GS
92 # include "psgs.h"
93 # endif
94 
95 /*
96   Code inside BUG_888087_FIXED improve the appearance of pic specials at magstep 1, but
97   _decreases_ it at other magnifications (bug #888087; see
98   http://sourceforge.net/tracker/index.php?func=detail&aid=888087&group_id=23164&atid=377580)
99   Disabled until this is fixed.
100 */
101 #define BUG_888087_FIXED 0
102 
103 
104 /*
105   Flag whether this page contains raw PS material;
106   used to warn user about this:
107 */
108 Boolean have_raw_postscript = False;
109 
110 # if PS_GS && GS_PIXMAP_CLEARING_HACK
111 /*
112   Flag whether this page contains PS material;
113   used to flush the GS buffer
114 */
115 Boolean had_ps_specials = False;
116 # endif /* PS_GS && GS_PIXMAP_CLEARING_HACK */
117 
118 #endif
119 
120 
121 #if COLOR
122 
123 /*
124  *	Color stack used when scanning.  These records stay around, to ease
125  *	the burden on xmalloc().  The first entry is a dummy entry, giving
126  *	the default foreground color (as modified).
127  */
128 
129 struct colorframe {
130     struct colorframe *next, *prev;
131     struct rgb color;
132 };
133 
134 static Boolean m_have_papersize_special = False;
135 
reset_papersize_special(void)136 void reset_papersize_special(void)
137 {
138     m_have_papersize_special = False;
139 }
140 
have_papersize_special(void)141 Boolean have_papersize_special(void)
142 {
143     return m_have_papersize_special;
144 }
145 
146 static struct colorframe scanstack_head;
147 static int scanstack_len;
148 static struct colorframe *scanstack_current;
149 
150 /*
151  *	Page stack when displaying.  Again, the records stay around once
152  *	allocated.  Bottom entries in the stack (inherited from previous
153  *	pages) are stored in an array instead (see comments in xdvi.h).
154  */
155 
156 static struct colorframe *rcs_head;
157 
158 static Boolean parse_color (const char *cp0, const char *cp, struct rgb *rgbp, Boolean generic_ps_flag);
159 
160 #endif /* COLOR */
161 
162 
163 #ifndef S_IRUSR
164 # define S_IRUSR 0400
165 #endif
166 #ifndef S_IWUSR
167 # define S_IWUSR 0200
168 #endif
169 
170 # if HAVE_SYS_WAIT_H
171 #  include <sys/wait.h>
172 # endif
173 # ifndef WEXITSTATUS
174 #  define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
175 # endif
176 # ifndef WIFEXITED
177 #  define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
178 # endif
179 
180 #define	MAXPOINTS	300	/* Max points in a path */
181 #define	MAX_PEN_SIZE	7	/* Max pixels of pen width */
182 #define	TWOPI		(3.14159265359 * 2.0)
183 
184 extern double floor(double);
185 #define	rint(x)	floor((x) + 0.5)
186 
187 static int xx[MAXPOINTS], yy[MAXPOINTS];	/* Path in milli-inches */
188 static int path_len = 0;	/* # points in current path */
189 static int pen_size = 1;	/* Pixel width of lines drawn */
190 
191 static Boolean whiten = False;
192 static Boolean shade = False;
193 static Boolean blacken = False;
194 
195 Boolean psfig_begun = False;
196 
197 #define	CMD(x, y)	((x) << 8 | (y))
198 
199 
200 /*
201  *	X drawing routines
202  */
203 
204 #define	toint(x)	((int) ((x) + 0.5))
205 #define	xconv(x)	(toint(tpic_conv*(x))/currwin.shrinkfactor + PXL_H)
206 #define	yconv(y)	(toint(tpic_conv*(y))/currwin.shrinkfactor + PXL_V)
207 
208 /*
209  *	Draw a line from (fx,fy) to (tx,ty).
210  */
211 static void
line_btw(int fx,int fy,int tx,int ty)212 line_btw(int fx, int fy, int tx, int ty)
213 {
214     int fcx = xconv(fx);
215     int tcx = xconv(tx);
216     int fcy = yconv(fy);
217     int tcy = yconv(ty);
218 
219     if ((fcx < globals.win_expose.max_x || tcx < globals.win_expose.max_x) && (fcx >= globals.win_expose.min_x || tcx >= globals.win_expose.min_x) &&
220 	(fcy < globals.win_expose.max_y || tcy < globals.win_expose.max_y) && (fcy >= globals.win_expose.min_y || tcy >= globals.win_expose.min_y)) {
221 #if COLOR
222 	if (fg_active != fg_current)
223 	    do_color_change();
224 #endif
225 	XDrawLine(DISP, currwin.win, globals.gc.rule,
226 		  fcx - currwin.base_x, fcy - currwin.base_y,
227 		  tcx - currwin.base_x, tcy - currwin.base_y);
228     }
229 }
230 
231 /*
232  *	Draw a dot at (x,y)
233  */
234 static void
dot_at(int x,int y)235 dot_at(int x, int y)
236 {
237     int cx = xconv(x);
238     int cy = yconv(y);
239 
240     if (cx < globals.win_expose.max_x && cx >= globals.win_expose.min_x && cy < globals.win_expose.max_y && cy >= globals.win_expose.min_y){
241 #if COLOR
242 	if (fg_active != fg_current)
243 	    do_color_change();
244 #endif
245 	XDrawPoint(DISP, currwin.win, globals.gc.rule,
246 		   cx - currwin.base_x, cy - currwin.base_y);
247     }
248 }
249 
250 /*
251  *	Apply the requested attributes to the last path (box) drawn.
252  *	Attributes are reset.
253  *	(Not currently implemented.)
254  */
255 static void
do_attribute_path(int last_min_x,int last_max_x,int last_min_y,int last_max_y)256 do_attribute_path(int last_min_x, int last_max_x, int last_min_y, int last_max_y)
257 {
258     UNUSED(last_min_x);
259     UNUSED(last_max_x);
260     UNUSED(last_min_y);
261     UNUSED(last_max_y);
262 }
263 
264 /*
265  *	Set the size of the virtual pen used to draw in milli-inches
266  */
267 static void
set_pen_size(char * cp)268 set_pen_size(char *cp)
269 {
270     int ps;
271 
272     if (sscanf(cp, " %d ", &ps) != 1) {
273 	XDVI_WARNING((stderr, "invalid .ps command format: %s", cp));
274 	return;
275     }
276     pen_size = (2 * ps * resource.pixels_per_inch / currwin.shrinkfactor + 1000) / 2000;
277     if (pen_size < 1)
278 	pen_size = 1;
279     else if (pen_size > MAX_PEN_SIZE)
280 	pen_size = MAX_PEN_SIZE;
281 
282 #if BUG_888087_FIXED
283     if (globals.gc.rule) {
284 	XGCValues values;
285 	values.line_width = pen_size;
286 	XChangeGC(DISP, globals.gc.rule, GCLineWidth, &values);
287     }
288 #endif
289 }
290 
291 
292 /*
293  *	Print the line defined by previous path commands
294  */
295 
296 static void
flush_path(void)297 flush_path(void)
298 {
299     int i;
300     int last_min_x, last_max_x, last_min_y, last_max_y;
301 
302     last_min_x = 30000;
303     last_min_y = 30000;
304     last_max_x = -30000;
305     last_max_y = -30000;
306     for (i = 1; i < path_len; i++) {
307 	if (xx[i] > last_max_x)
308 	    last_max_x = xx[i];
309 	if (xx[i] < last_min_x)
310 	    last_min_x = xx[i];
311 	if (yy[i] > last_max_y)
312 	    last_max_y = yy[i];
313 	if (yy[i] < last_min_y)
314 	    last_min_y = yy[i];
315 	line_btw(xx[i], yy[i], xx[i + 1], yy[i + 1]);
316     }
317     if (xx[path_len] > last_max_x)
318 	last_max_x = xx[path_len];
319     if (xx[path_len] < last_min_x)
320 	last_min_x = xx[path_len];
321     if (yy[path_len] > last_max_y)
322 	last_max_y = yy[path_len];
323     if (yy[path_len] < last_min_y)
324 	last_min_y = yy[path_len];
325     path_len = 0;
326     do_attribute_path(last_min_x, last_max_x, last_min_y, last_max_y);
327 }
328 
329 
330 /*
331  *	Print a dashed line along the previously defined path, with
332  *	the dashes/inch defined.
333  */
334 
335 static void
flush_dashed(char * cp,Boolean dotted)336 flush_dashed(char *cp, Boolean dotted)
337 {
338     int i;
339     int numdots;
340     int lx0, ly0, lx1, ly1;
341     int cx0, cy0, cx1, cy1;
342     float inchesperdash;
343     double d, spacesize, a, b = 0.0, dx, dy, milliperdash;
344 
345     if (sscanf(cp, " %f ", &inchesperdash) != 1) {
346 	XDVI_WARNING((stderr, "invalid format for dotted/dashed line: %s", cp));
347 	return;
348     }
349     if (path_len <= 1 || inchesperdash <= 0.0) {
350 	XDVI_WARNING((stderr, "invalid conditions for dotted/dashed line"));
351 	return;
352     }
353     milliperdash = inchesperdash * 1000.0;
354     lx0 = xx[1];
355     ly0 = yy[1];
356     lx1 = xx[2];
357     ly1 = yy[2];
358     dx = lx1 - lx0;
359     dy = ly1 - ly0;
360     if (dotted) {
361 	numdots = sqrt(dx * dx + dy * dy) / milliperdash + 0.5;
362 	if (numdots == 0)
363 	    numdots = 1;
364 	for (i = 0; i <= numdots; i++) {
365 	    a = (float)i / (float)numdots;
366 	    cx0 = lx0 + a * dx + 0.5;
367 	    cy0 = ly0 + a * dy + 0.5;
368 	    dot_at(cx0, cy0);
369 	}
370     }
371     else {
372 	d = sqrt(dx * dx + dy * dy);
373 	numdots = d / (2.0 * milliperdash) + 1.0;
374 	if (numdots <= 1)
375 	    line_btw(lx0, ly0, lx1, ly1);
376 	else {
377 	    spacesize = (d - numdots * milliperdash) / (numdots - 1);
378 	    for (i = 0; i < numdots - 1; i++) {
379 		a = i * (milliperdash + spacesize) / d;
380 		b = a + milliperdash / d;
381 		cx0 = lx0 + a * dx + 0.5;
382 		cy0 = ly0 + a * dy + 0.5;
383 		cx1 = lx0 + b * dx + 0.5;
384 		cy1 = ly0 + b * dy + 0.5;
385 		line_btw(cx0, cy0, cx1, cy1);
386 		b += spacesize / d;
387 	    }
388 	    cx0 = lx0 + b * dx + 0.5;
389 	    cy0 = ly0 + b * dy + 0.5;
390 	    line_btw(cx0, cy0, lx1, ly1);
391 	}
392     }
393     path_len = 0;
394 }
395 
396 
397 /*
398  *	Add a point to the current path
399  */
400 
401 static void
add_path(char * cp)402 add_path(char *cp)
403 {
404     int pathx, pathy;
405 
406     if (++path_len >= MAXPOINTS)
407 	XDVI_FATAL((stderr, "Too many points"));
408     if (sscanf(cp, " %d %d ", &pathx, &pathy) != 2)
409 	XDVI_FATAL((stderr, "Malformed path command"));
410     xx[path_len] = pathx;
411     yy[path_len] = pathy;
412 }
413 
414 
415 /*
416  *	Draw to a floating point position
417  */
418 
419 static void
im_fdraw(double x,double y)420 im_fdraw(double x, double y)
421 {
422     if (++path_len >= MAXPOINTS)
423 	XDVI_FATAL((stderr, "Too many arc points"));
424     xx[path_len] = x + 0.5;
425     yy[path_len] = y + 0.5;
426 }
427 
428 
429 /*
430  *	Draw an ellipse with the indicated center and radices.
431  */
432 
433 static void
draw_ellipse(int xc,int yc,int xr,int yr)434 draw_ellipse(int xc, int yc, int xr, int yr)
435 {
436     double angle, theta;
437     int n;
438     int px0, py0, px1, py1;
439 
440     angle = (xr + yr) / 2.0;
441     theta = sqrt(1.0 / angle);
442     n = TWOPI / theta + 0.5;
443     if (n < 12)
444 	n = 12;
445     else if (n > 80)
446 	n = 80;
447     n /= 2;
448     theta = TWOPI / n;
449 
450     angle = 0.0;
451     px0 = xc + xr;	/* cos(0) = 1 */
452     py0 = yc;	/* sin(0) = 0 */
453     while ((angle += theta) <= TWOPI) {
454 	px1 = xc + xr * cos(angle) + 0.5;
455 	py1 = yc + yr * sin(angle) + 0.5;
456 	line_btw(px0, py0, px1, py1);
457 	px0 = px1;
458 	py0 = py1;
459     }
460     line_btw(px0, py0, xc + xr, yc);
461 }
462 
463 /*
464  *	Draw an arc
465  */
466 
467 static void
arc(char * cp,Boolean invis)468 arc(char *cp, Boolean invis)
469 {
470     int xc, yc, xrad, yrad, n;
471     float start_angle, end_angle, angle, theta, r;
472     double xradius, yradius, xcenter, ycenter;
473 
474     n = sscanf(cp, " %d %d %d %d %f %f ", &xc, &yc, &xrad, &yrad,
475 	       &start_angle, &end_angle);
476 
477     if (n != 6) {
478 	XDVI_WARNING((stderr, "invalid arc specification: %s", cp));
479 	return;
480     }
481 
482     if (invis)
483 	return;
484 
485     /* We have a specialized fast way to draw closed circles/ellipses */
486     if (start_angle <= 0.0 && end_angle >= 6.282) {
487 	draw_ellipse(xc, yc, xrad, yrad);
488 	return;
489     }
490     xcenter = xc;
491     ycenter = yc;
492     xradius = xrad;
493     yradius = yrad;
494     r = (xradius + yradius) / 2.0;
495     theta = sqrt(1.0 / r);
496 #if BUG_888087_FIXED
497     n = (pen_size * TWOPI) / (theta * currwin.shrinkfactor) + 0.5;
498 #else
499     n = 0.3 * TWOPI / theta + 0.5;
500 #endif
501     if (n < 12)
502 	n = 12;
503     else if (n > 80)
504 	n = 80;
505     n /= 2;
506     theta = TWOPI / n;
507     flush_path();
508     im_fdraw(xcenter + xradius * cos(start_angle),
509 	     ycenter + yradius * sin(start_angle));
510     angle = start_angle + theta;
511     if (end_angle < start_angle)
512 	end_angle += TWOPI;
513     while (angle < end_angle) {
514 	im_fdraw(xcenter + xradius * cos(angle),
515 		 ycenter + yradius * sin(angle));
516 	angle += theta;
517     }
518     im_fdraw(xcenter + xradius * cos(end_angle),
519 	     ycenter + yradius * sin(end_angle));
520     flush_path();
521 }
522 
523 
524 /*
525  *	APPROXIMATE integer distance between two points
526  */
527 
528 #define	dist(x0, y0, x1, y1)	(abs(x0 - x1) + abs(y0 - y1))
529 
530 
531 /*
532  *	Draw a spline along the previously defined path
533  */
534 
535 static void
flush_spline(void)536 flush_spline(void)
537 {
538     int xp, yp;
539     int N;
540     int lastx = -1, lasty = -1;
541     Boolean lastvalid = False;
542     int t1, t2, t3;
543     int steps;
544     int j;
545     int i, w;
546 
547     N = path_len + 1;
548     xx[0] = xx[1];
549     yy[0] = yy[1];
550     xx[N] = xx[N - 1];
551     yy[N] = yy[N - 1];
552     for (i = 0; i < N - 1; i++) {	/* interval */
553 	steps = (dist(xx[i], yy[i], xx[i + 1], yy[i + 1]) +
554 		 dist(xx[i + 1], yy[i + 1], xx[i + 2], yy[i + 2])) / 80;
555 	for (j = 0; j < steps; j++) {	/* points within */
556 	    w = (j * 1000 + 500) / steps;
557 	    t1 = w * w / 20;
558 	    w -= 500;
559 	    t2 = (750000 - w * w) / 10;
560 	    w -= 500;
561 	    t3 = w * w / 20;
562 	    xp =
563 		(t1 * xx[i + 2] + t2 * xx[i + 1] + t3 * xx[i] + 50000) / 100000;
564 	    yp =
565 		(t1 * yy[i + 2] + t2 * yy[i + 1] + t3 * yy[i] + 50000) / 100000;
566 	    if (lastvalid)
567 		line_btw(lastx, lasty, xp, yp);
568 	    lastx = xp;
569 	    lasty = yp;
570 	    lastvalid = True;
571 	}
572     }
573     path_len = 0;
574 }
575 
576 
577 /*
578  *	Shade the last box, circle, or ellipse
579  */
580 
581 static void
shade_last(void)582 shade_last(void)
583 {
584     blacken = whiten = False;
585     shade = True;
586 }
587 
588 
589 /*
590  *	Make the last box, circle, or ellipse, white inside (shade with white)
591  */
592 
593 static void
whiten_last(void)594 whiten_last(void)
595 {
596     whiten = True;
597     blacken = shade = False;
598 }
599 
600 
601 /*
602  *	Make last box, etc, black inside
603  */
604 
605 static void
blacken_last(void)606 blacken_last(void)
607 {
608     blacken = True;
609     whiten = shade = False;
610 }
611 
612 
613 /*
614  *	Code for PostScript<tm> specials begins here.
615  */
616 
617 #if PS
618 
619 /*
620  *	Information on how to search for PS header and figure files.
621  */
622 
623 static void ps_startup(int, int, const char *);
624 static void ps_startup2(void);
625 
626 /* dummy procedures */
NullProc(void)627 static void NullProc(void) { }
NullProcInt(int flag)628 static void NullProcInt(int flag) { UNUSED(flag); }
NullProcStr(const char * cp)629 static void NullProcStr(const char *cp) { UNUSED(cp); }
630 
631 
632 struct psprocs psp = {	/* used for lazy startup of the ps machinery */
633     /* toggle */ NullProcInt,
634     /* destroy */ NullProc,
635     /* interrupt */ NullProc,
636     /* endpage */ NullProc,
637     /* drawbegin */ ps_startup,
638     /* drawraw */ NullProcStr,
639     /* drawfile */ NULL,
640     /* drawend */ NullProcStr,
641     /* beginheader */ ps_startup2,
642     /* endheader */ NullProc,
643     /* newdoc */ NullProc
644 };
645 
646 struct psprocs no_ps_procs = {	/* used if postscript is unavailable */
647     /* toggle */ NullProcInt,
648     /* destroy */ NullProc,
649     /* interrupt */ NullProc,
650     /* endpage */ NullProc,
651     /* drawbegin */ drawbegin_none,
652     /* drawraw */ NullProcStr,
653     /* drawfile */ NULL,
654     /* drawend */ NullProcStr,
655     /* beginheader */ NullProc,
656     /* endheader */ NullProc,
657     /* newdoc */ NullProc
658 };
659 
660 #endif /* PS */
661 
662 #ifdef MAGICK
663 int bbox_angle;
664 Boolean bbox_valid;
665 unsigned int bbox_width;
666 unsigned int bbox_height;
667 int bbox_voffset;
668 #else
669 static int bbox_angle;
670 static Boolean bbox_valid;
671 static unsigned int bbox_width;
672 static unsigned int bbox_height;
673 static int bbox_voffset;
674 #endif
675 
676 /* info on bboxes on this page */
677 struct bbox_info {
678     int x;
679     int y;
680     int w;
681     int h;
682     int angle;
683 };
684 
685 static struct bbox_info *g_bbox_info = NULL;
686 static size_t g_bbox_info_size = 0;
687 static size_t g_bbox_info_max_size = 0;
688 
689 /*
690   Append the current coordinates to g_bbox_info, unless it already
691   contains these coordinates.
692 */
693 
694 double	bbox_matrix[2][2] = {{1.0, 0.0}, {0.0, 1.0}};
695 Boolean	bbox_scaled = False, bbox_rotated = False;
696 
697 #define	bbox_xscale	bbox_matrix[0][0]
698 #define	bbox_yscale	bbox_matrix[1][1]
699 
700 static void
append_bbox_info(int x,int y,int w,int h,int angle)701 append_bbox_info(int x, int y, int w, int h, int angle)
702 {
703     Boolean found = False;
704     size_t i;
705     const size_t SIZE_STEP = 16;
706 
707     /* is this box already present? */
708     for (i = 0; i < g_bbox_info_size; i++) {
709 	if (g_bbox_info[i].x == x
710 	    && g_bbox_info[i].y == y
711 	    && g_bbox_info[i].w == w
712 	    && g_bbox_info[i].h == h
713 	    && g_bbox_info[i].angle == angle) {
714 	    found = True;
715 	    break;
716 	}
717     }
718 
719     if (!found) {
720 	g_bbox_info_size++;
721 
722 	while (g_bbox_info_size >= g_bbox_info_max_size) {
723 	    g_bbox_info_max_size += SIZE_STEP;
724 	}
725 
726 	g_bbox_info = xrealloc(g_bbox_info, g_bbox_info_max_size * sizeof *g_bbox_info);
727 	g_bbox_info[g_bbox_info_size - 1].x = x;
728 	g_bbox_info[g_bbox_info_size - 1].y = y;
729 	g_bbox_info[g_bbox_info_size - 1].w = w;
730 	g_bbox_info[g_bbox_info_size - 1].h = h;
731 	g_bbox_info[g_bbox_info_size - 1].angle = angle;
732     }
733 }
734 
735 static void
draw_bbox0(int xcorner,int ycorner)736 draw_bbox0(int xcorner, int ycorner)
737 {
738     if (bbox_valid) {
739 #if COLOR
740 	if (fg_active != fg_current)
741 	    do_color_change();
742 #endif
743 
744 	if (! bbox_rotated) {
745 	    ycorner -= bbox_voffset * bbox_yscale;
746 	    XDrawRectangle(DISP, currwin.win, globals.gc.high, xcorner, ycorner,
747 			   bbox_width * bbox_xscale, bbox_height * bbox_yscale);
748 	    if (resource.postscript == 0) {
749 		if (htex_inside_href) {
750 		    htex_set_anchorsize(xcorner, ycorner, xcorner + bbox_width * bbox_xscale,
751 					ycorner + bbox_height * bbox_yscale);
752 		    htex_set_objecttype(HTEX_IMG);
753 		}
754 	    }
755 	}
756 	else {
757 	    float a, b, c, d;
758 
759 	    a = bbox_matrix[0][0] * bbox_width;
760 	    b = bbox_matrix[1][0] * bbox_width;
761 	    c = -bbox_matrix[0][1] * bbox_height;
762 	    d = -bbox_matrix[1][1] * bbox_height;
763 
764 	    XDrawLine(DISP, currwin.win, globals.gc.high,
765 		      xcorner, ycorner,
766 		      xcorner + (int)rint(a), ycorner + (int)rint(b));
767 	    XDrawLine(DISP, currwin.win, globals.gc.high,
768 		      xcorner + (int)rint(a), ycorner + (int)rint(b),
769 		      xcorner + (int)rint(a + c), ycorner + (int)rint(b + d));
770 	    XDrawLine(DISP, currwin.win, globals.gc.high,
771 		      xcorner + (int)rint(a + c), ycorner + (int)rint(b + d),
772 		      xcorner + (int)rint(c), ycorner + (int)rint(d));
773 	    XDrawLine(DISP, currwin.win, globals.gc.high,
774 		      xcorner + (int)rint(c), ycorner + (int)rint(d),
775 		      xcorner, ycorner);
776 	}
777 	bbox_valid = False;
778     }
779 }
780 
781 /*
782   Display the items in bbox_info.
783   Invoked from draw_page() in dvi-draw.c.
784 */
785 void
display_bboxes(void)786 display_bboxes(void)
787 {
788     size_t i;
789 
790     for (i = 0; i < g_bbox_info_size; i++) {
791        if (globals.debug & DBG_PS) {
792            fprintf(stderr, "drawing bbox %lu at %d %d, %d x %d, angle %d\n",
793                    (unsigned long)i,
794                    g_bbox_info[i].x,
795                    g_bbox_info[i].y,
796                    g_bbox_info[i].w,
797                    g_bbox_info[i].h,
798                    g_bbox_info[i].angle);
799        }
800 #if 0
801        XDrawRectangle(DISP, currwin.win, globals.gc.high,
802                       g_bbox_info[i].x,
803                       g_bbox_info[i].y,
804                       g_bbox_info[i].w,
805                       g_bbox_info[i].h);
806 #else
807        bbox_valid = True;
808        bbox_width = g_bbox_info[i].w;
809        bbox_height = bbox_voffset = g_bbox_info[i].h;
810        bbox_angle = g_bbox_info[i].angle;
811        draw_bbox0(g_bbox_info[i].x, g_bbox_info[i].y + bbox_height);
812 #endif
813     }
814     bbox_angle = 0;
815     bbox_valid = False;
816 }
817 
818 void
clear_bboxes(void)819 clear_bboxes(void)
820 {
821     free(g_bbox_info);
822     g_bbox_info = NULL;
823     g_bbox_info_size = 0;
824     g_bbox_info_max_size = 0;
825 }
826 
827 void
save_bbox(void)828 save_bbox(void)
829 {
830     int xcorner, ycorner;
831 
832     if (bbox_valid) {
833 	xcorner = PXL_H - currwin.base_x;
834 	ycorner = PXL_V - currwin.base_y;
835 
836 	ycorner -= bbox_voffset;
837 	append_bbox_info(xcorner, ycorner, bbox_width, bbox_height, bbox_angle);
838 	if (currwin.win == mane.win && resource.postscript != 1) {
839 	    draw_bbox();
840 	}
841 
842 	/* register boundaries of this box as anchor boundaries */
843 	if (htex_inside_href) {
844 	    htex_set_anchorsize(xcorner, ycorner, xcorner + bbox_width, ycorner + bbox_height);
845 	    htex_set_objecttype(HTEX_IMG);
846 	}
847     }
848 }
849 
850 
851 void
draw_bbox(void)852 draw_bbox(void)
853 {
854     draw_bbox0(PXL_H - currwin.base_x, PXL_V - currwin.base_y);
855 }
856 
857 #if PS
858 
859 static XIOErrorHandler oldhandler;
860 
XDviIOErrorHandler(Display * disp)861 static int XDviIOErrorHandler(Display * disp)
862 {
863     ps_destroy();
864     return oldhandler(disp);
865 }
866 
867 static void
actual_startup(int flag)868 actual_startup(int flag)
869 {
870     UNUSED(flag);
871     oldhandler = XSetIOErrorHandler(XDviIOErrorHandler);
872 
873     /*
874      * Figure out what we want to use to display postscript figures
875      * and set at most one of the following to True:
876      * resource.useGS, resource.useDPS, resource.useNeWS
877      *
878      * Choose DPS then NEWS then Ghostscript if they are available
879      */
880     if (!(
881 #ifdef	PS_DPS
882 	  (resource.useDPS && initDPS())
883 #if	defined(PS_NEWS) || defined(PS_GS)
884 	  ||
885 #endif
886 #endif /* PS_DPS */
887 #ifdef	PS_NEWS
888 	  (resource.useNeWS && initNeWS())
889 #ifdef	PS_GS
890 	  ||
891 #endif
892 #endif /* PS_NEWS */
893 #ifdef	PS_GS
894 	  (resource.useGS && initGS())
895 #endif
896 	  ))
897 	psp = no_ps_procs;
898 }
899 
900 static void
ps_startup(int xul,int yul,const char * cp)901 ps_startup(int xul, int yul, const char *cp)
902 {
903     if (resource.postscript == 0) {
904 	draw_bbox();
905 	return;
906     }
907     actual_startup(0);
908     psp.drawbegin(xul, yul, cp);
909 }
910 
911 static void
ps_startup2(void)912 ps_startup2(void)
913 {
914     actual_startup(0);
915     psp.beginheader();
916 }
917 
918 
919 typedef struct {
920   double matrix[2][2];
921   double x, y;
922 } ROTATE_STACK;
923 
924 /*
925  * dumb parsing of PostScript - search for rotation H. Zeller 1/97
926  * Returns true if we find a potentially non-supported command that
927  * we want to warn users about.
928  */
929 static Boolean
ps_parseraw(const char * PostScript_cmd)930 ps_parseraw(const char *PostScript_cmd)
931 {
932     const char *p;
933     double xscale, yscale, angle, sin_a, cos_a, a, b, c, d;
934     int n;
935     static int rotate_stack_len = 0;
936     static int rotate_stack_ptr = 0;
937     static ROTATE_STACK *rotate_stack = NULL;
938 
939     p = strstr(PostScript_cmd, "currentpoint grestore moveto");
940     if (p != NULL) {
941 	if (rotate_stack_ptr > 0) {
942 	    double r;
943 	    rotate_stack_ptr--;
944 	    r = sqrt(((DVI_H - rotate_stack[rotate_stack_ptr].x)
945 		      * (DVI_H - rotate_stack[rotate_stack_ptr].x)
946 		      + (DVI_V - rotate_stack[rotate_stack_ptr].y)
947 		      * (DVI_V - rotate_stack[rotate_stack_ptr].y))
948 		     / (bbox_matrix[0][0] * bbox_matrix[0][0]
949 			+ bbox_matrix[1][0] * bbox_matrix[1][0]))
950 		* (rotate_stack[rotate_stack_ptr].matrix[0][0]
951 		   * bbox_matrix[1][0]
952 		   - rotate_stack[rotate_stack_ptr].matrix[1][0]
953 		   * bbox_matrix[0][0])
954 		/ (rotate_stack[rotate_stack_ptr].matrix[0][0]
955 		   * rotate_stack[rotate_stack_ptr].matrix[0][0]
956 		   + rotate_stack[rotate_stack_ptr].matrix[1][0]
957 		   * rotate_stack[rotate_stack_ptr].matrix[1][0]);
958 	    DVI_H += r * rotate_stack[rotate_stack_ptr].matrix[1][0];
959 	    DVI_V += r * rotate_stack[rotate_stack_ptr].matrix[0][0];
960 	    PXL_V = pixel_conv(DVI_V);
961 	    bcopy(rotate_stack[rotate_stack_ptr].matrix, bbox_matrix,
962 		  sizeof(bbox_matrix));
963 	    bbox_rotated
964 		= (bbox_matrix[0][1] != 0.0 || bbox_matrix[1][0] != 0.0)
965 		? True : False;
966 	    bbox_scaled
967 		= (bbox_rotated
968 		   || bbox_matrix[0][0] != 1.0 || bbox_matrix[1][1] != 1.0)
969 		? True : False;
970 	}
971 	if (globals.debug & DBG_PS)
972 	    fprintf(stderr, "current angle %f: %s\n",
973 		    bbox_matrix[0][0] != 0
974 		    ? -atan(bbox_matrix[1][0] / bbox_matrix[0][0])
975 		    * (360 / TWOPI)
976 		    : bbox_matrix[1][0] >= 0 ? (double)90 : (double)-90,
977 		    PostScript_cmd);
978     }
979 
980     bbox_angle = 0;
981     p = strstr(PostScript_cmd, "rotate");
982     if (p != NULL) {
983 	while (*p != '\0' && !isdigit((int)*p))
984 	    --p;
985 	while (*p != '\0' && isdigit((int)*p))
986 	    --p;
987 	if (*p == '.') --p;
988 	while (*p != '\0' && isdigit(*p)) --p;
989 	if (*p != '+' && *p != '-')
990 	    ++p;
991 	n = sscanf(p, "%lf neg rotate", &angle);
992 	if (n != 1) {
993 	    fprintf(stderr, "%s:  cannot get angle.\n", globals.program_name);
994 	    angle = 0.0;
995 	}
996 	bbox_angle = angle;
997 	if (rotate_stack_ptr >= rotate_stack_len) {
998 	    rotate_stack_len += 0x10;
999 	    rotate_stack = (ROTATE_STACK *)
1000 		xrealloc(rotate_stack,
1001 			 sizeof(ROTATE_STACK) * rotate_stack_len);
1002 	}
1003 	bcopy(bbox_matrix, rotate_stack[rotate_stack_ptr].matrix,
1004 	      sizeof(bbox_matrix));
1005 	rotate_stack[rotate_stack_ptr].x = DVI_H;
1006 	rotate_stack[rotate_stack_ptr].y = DVI_V;
1007 	rotate_stack_ptr++;
1008 
1009 	sin_a = sin(-angle * (TWOPI / 360));
1010 	cos_a = cos(-angle * (TWOPI / 360));
1011 	a =   bbox_matrix[0][0] * cos_a + bbox_matrix[0][1] * sin_a;
1012 	b = - bbox_matrix[0][0] * sin_a + bbox_matrix[0][1] * cos_a;
1013 	c =   bbox_matrix[1][0] * cos_a + bbox_matrix[1][1] * sin_a;
1014 	d = - bbox_matrix[1][0] * sin_a + bbox_matrix[1][1] * cos_a;
1015 	bbox_matrix[0][0] = a;
1016 	bbox_matrix[0][1] = b;
1017 	bbox_matrix[1][0] = c;
1018 	bbox_matrix[1][1] = d;
1019 	bbox_rotated = (b != 0.0 || c != 0.0) ? True : False;
1020 	bbox_scaled
1021 	    = (bbox_rotated
1022 	       || bbox_matrix[0][0] != 1.0 || bbox_matrix[1][1] != 1.0)
1023 	    ? True : False;
1024 	if (globals.debug & DBG_PS)
1025 	    fprintf(stderr, "current angle %f: %s\n",
1026 		    bbox_matrix[0][0] != 0
1027 		    ? -atan(bbox_matrix[1][0] / bbox_matrix[0][0])
1028 		    * (360 / TWOPI)
1029 		    : bbox_matrix[1][0] >= 0 ? (double)90 : (double)-90,
1030 		    PostScript_cmd);
1031     }
1032 
1033     p = strstr(PostScript_cmd, "scale");
1034     if (p != NULL) {
1035 	if (strncmp(p - 4, "div", 3) == 0) {
1036 	    while (*p != '\0' && !isdigit(*p)) --p;
1037 	    while (*p != '\0' && isdigit(*p)) --p;
1038 	    if (*p == '.') --p;
1039 	    while (*p != '\0' && isdigit(*p)) --p;
1040 	    while (*p != '\0' && !isdigit(*p)) --p;
1041 	    while (*p != '\0' && isdigit(*p)) --p;
1042 	    while (*p != '\0' && !isdigit(*p)) --p;
1043 	    while (*p != '\0' && isdigit(*p)) --p;
1044 	    if (*p == '.') --p;
1045 	    while (*p != '\0' && isdigit(*p)) --p;
1046 	    while (*p != '\0' && !isdigit(*p)) --p;
1047 	    while (*p != '\0' && isdigit(*p)) --p;
1048 	    ++p;
1049 	    n = sscanf(p, "1 %lf div 1 %lf div scale", &xscale, &yscale);
1050 	    if (n == 2) {
1051 #ifdef PTEX
1052 		if(TATE) {
1053 		    double temp;
1054 		    temp = xscale;
1055 		    xscale = 1.0/yscale;
1056 		    yscale = 1.0/temp;
1057 		}
1058 		else {
1059 		    xscale = 1.0/xscale;
1060 		    yscale = 1.0/yscale;
1061 		}
1062 #else
1063 		xscale = 1.0/xscale;
1064 		yscale = 1.0/yscale;
1065 #endif
1066 	    } else {
1067 		fprintf(stderr,
1068 			"%s:  cannot get scaling factors.\n",
1069 			globals.program_name);
1070 		xscale = yscale = 1.0;
1071 	    }
1072 	} else {
1073 	    while (*p != '\0' && !isdigit(*p)) --p;
1074 	    while (*p != '\0' && isdigit(*p)) --p;
1075 	    if (*p == '.') --p;
1076 	    while (*p != '\0' && isdigit(*p)) --p;
1077 	    while (*p != '\0' && !isdigit(*p)) --p;
1078 	    while (*p != '\0' && isdigit(*p)) --p;
1079 	    if (*p == '.') --p;
1080 	    while (*p != '\0' && isdigit(*p)) --p;
1081 	    if (*p != '+' && *p != '-') ++p;
1082 	    n = sscanf(p, "%lf %lf scale", &xscale, &yscale);
1083 #ifdef PTEX
1084 	    if(TATE) {
1085 		double temp;
1086 		temp = xscale;
1087 		xscale = yscale;
1088 		yscale = temp;
1089 	    }
1090 #endif
1091 	    if (n != 2) {
1092 		fprintf(stderr,
1093 			"%s:  cannot get scaling factors.\n",
1094 			globals.program_name);
1095 		xscale = yscale = 1.0;
1096 	    }
1097 	}
1098 	bbox_matrix[0][0] *= xscale;
1099 	bbox_matrix[0][1] *= yscale;
1100 	bbox_matrix[1][0] *= xscale;
1101 	bbox_matrix[1][1] *= yscale;
1102 	bbox_scaled
1103 	    = (bbox_rotated
1104 	       || bbox_matrix[0][0] != 1.0 || bbox_matrix[1][1] != 1.0)
1105 	    ? True : False;
1106 	if (globals.debug & DBG_PS)
1107 	    fprintf(stderr, "current scale %f,%f: %s\n",
1108 		    bbox_xscale, bbox_yscale, PostScript_cmd);
1109 	angle = 0.0;
1110 	if (strstr(PostScript_cmd, "neg rotate")) {
1111 	    n = sscanf(p, "%lf neg rotate", &angle);
1112 	    if (n != 1) {
1113 		fprintf(stderr, "%s:  cannot get angle. (%s)\n", globals.program_name,p);
1114 		angle = 0.0;
1115 	    }
1116 	}
1117 	bbox_angle = angle;
1118 	return True;
1119     }
1120     if (strstr(PostScript_cmd, " scale ") != NULL)
1121 	return True;
1122     return False;
1123 }
1124 
1125 
1126 void
drawbegin_none(int xul,int yul,const char * cp)1127 drawbegin_none(int xul, int yul, const char *cp)
1128 {
1129     UNUSED(xul);
1130     UNUSED(yul);
1131     UNUSED(cp);
1132 
1133     draw_bbox();
1134 }
1135 
1136 
1137 struct tickrec {
1138     struct tickrec *next;
1139     int pageno;
1140     char *command;
1141     char *tempname;
1142 };
1143 
1144 static struct tickrec *tickhead = NULL;	/* head of linked list of */
1145 /* cached information */
1146 static int nticks = 0;	/* number of records total */
1147 
1148 #ifndef	TICKCACHESIZE
1149 #define	TICKCACHESIZE	3
1150 #endif
1151 
1152 #ifndef	TICKTMP
1153 #define	TICKTMP		"/tmp"
1154 #endif
1155 
1156 /*
1157  *	cachetick() - returns:
1158  *		NULL		error;
1159  *		fp == NULL	string was not in cache, fd = file
1160  *		fp != NULL	string was in cache, fp = cached file
1161  */
1162 
1163 static struct tickrec *
cachetick(const char * filename,kpse_file_format_type pathinfo,FILE ** fp,int * fdp)1164 cachetick(const char *filename, kpse_file_format_type pathinfo, FILE **fp, int *fdp)
1165 {
1166     struct tickrec **linkp;
1167     struct tickrec *tikp;
1168     struct tickrec **freerecp;
1169 
1170     linkp = &tickhead;
1171     freerecp = NULL;
1172     for (;;) {	/* see if we have it already */
1173 	tikp = *linkp;
1174 	if (tikp == NULL) {	/* if end of list */
1175 	    int fd;
1176 
1177 	    if (nticks >= TICKCACHESIZE && freerecp != NULL) {
1178 		tikp = *freerecp;
1179 		*freerecp = tikp->next;
1180 		free(tikp->command);
1181 		unlink(tikp->tempname);
1182 		/* reuse tikp and tikp->tempname */
1183 	    }
1184 	    else {
1185 		tikp = xmalloc(sizeof(struct tickrec));
1186 		tikp->tempname = NULL;
1187 		++nticks;
1188 	    }
1189 	    fd = xdvi_temp_fd(&tikp->tempname);
1190 	    if (fd == -1) {
1191 		perror("Cannot create temporary file");
1192 		free(tikp->tempname);
1193 		free(tikp);
1194 		return NULL;
1195 	    }
1196 	    tikp->command = xstrdup(filename);
1197 	    *fp = NULL;
1198 	    *fdp = fd;
1199 	    break;
1200 	}
1201 	if (strcmp(filename, tikp->command) == 0) {	/* found it */
1202 	    *linkp = tikp->next;	/* unlink it */
1203 	    *fp = XFOPEN(tikp->tempname, OPEN_MODE);
1204 	    if (*fp == NULL) {
1205 		perror(tikp->tempname);
1206 		free(tikp->tempname);
1207 		free(tikp->command);
1208 		free(tikp);
1209 		return NULL;
1210 	    }
1211 	    break;
1212 	}
1213 	if (tikp->pageno != current_page)
1214 	    freerecp = linkp;
1215 	linkp = &tikp->next;
1216     }
1217     tikp->next = tickhead;	/* link it in */
1218     tickhead = tikp;
1219     tikp->pageno = pathinfo != kpse_tex_ps_header_format ? current_page : -1;
1220     return tikp;
1221 }
1222 
1223 void
ps_clear_cache(void)1224 ps_clear_cache(void)
1225 {
1226     struct tickrec *tikp;
1227 
1228     while (tickhead != NULL) {
1229 	tikp = tickhead;
1230 	tickhead = tickhead->next;
1231 	free(tikp->command);
1232 	unlink(tikp->tempname);
1233 	free((char *)tikp->tempname);
1234 	free(tikp);
1235     }
1236     nticks = 0;
1237 }
1238 
1239 #ifndef	UNCOMPRESS
1240 #define	UNCOMPRESS	"uncompress"
1241 #endif
1242 
1243 #ifndef	GUNZIP
1244 #define	GUNZIP		"gunzip"
1245 #endif
1246 
1247 #ifndef	BUNZIP2
1248 #define	BUNZIP2		"bunzip2"
1249 #endif
1250 
1251 static void send_ps_file(const char *filename, kpse_file_format_type pathinfo);
1252 
1253 static void
enable_specials_send_ps_file(XtPointer data)1254 enable_specials_send_ps_file(XtPointer data)
1255 {
1256     const char *filename = (const char *)data;
1257     resource.allow_shell = True;
1258     redraw_page(); /* to erase the bounding box */
1259     statusline_info(STATUS_MEDIUM,
1260 		     "Shell specials enabled for this session.",
1261 		     filename);
1262     send_ps_file(filename, kpse_pict_format);
1263 }
1264 
1265 static void
try_open_tempname(int status,struct xchild * this)1266 try_open_tempname(int status, struct xchild *this)
1267 {
1268     if (WIFEXITED(status)) { /* child exited normally */
1269 	if (WEXITSTATUS(status) != 0) {
1270 	    /* default error procedure */
1271 	    handle_child_exit(status, this);
1272 	}
1273 	else {
1274 	    struct tickrec *tikp = (struct tickrec *)this->data;
1275 	    FILE *f = XFOPEN(tikp->tempname, OPEN_MODE);
1276 	    fprintf(stderr, "FILE: %s\n", tikp->tempname);
1277 	    if (f == NULL) {
1278 		perror(tikp->tempname);
1279 	    }
1280 	    else {
1281 		fprintf(stderr, "sending file: %s, %p\n", tikp->tempname, (void *)f);
1282 		/* There's no point in invoking
1283 		   psp.drawfile(tikp->tempname, f);
1284 		   here, since usually it will be already too late (draw_part() which
1285 		   had called us via applicationDoSpecial() will already have terminated).
1286 		   So instead, we just close the file and force a redraw of the entire page.
1287 		*/
1288 		fclose(f);
1289 		globals.ev.flags |= EV_NEWPAGE;
1290 	    }
1291 	}
1292     }
1293     else if (WIFSIGNALED(status)) {
1294 	popup_message(globals.widgets.top_level,
1295 		      MSG_WARN,
1296 		      NULL,
1297 		      "Process `%s' terminated abnormally with signal %d.", this->name, WTERMSIG(status));
1298     }
1299     else if (WIFSTOPPED(status)) {
1300 	popup_message(globals.widgets.top_level,
1301 		      MSG_WARN,
1302 		      NULL,
1303 		      "Process `%s' stopped by signal %d.", this->name, WSTOPSIG(status));
1304     }
1305     else {
1306 	popup_message(globals.widgets.top_level,
1307 		      MSG_WARN,
1308 		      NULL,
1309 		      "Process `%s' terminated with unknown status.", this->name);
1310     }
1311 }
1312 
1313 static void
send_ps_file(const char * filename,kpse_file_format_type pathinfo)1314 send_ps_file(const char *filename, kpse_file_format_type pathinfo)
1315 {
1316     FILE *f;
1317     int fd;
1318     static const char *argv[] = { NULL, "-c", NULL, NULL };
1319     char *bufp = NULL;
1320     struct tickrec *volatile tikp;
1321     size_t len;
1322     char magic1, magic2, magic3;
1323     static Boolean warned_about_shellescape = False;
1324     static size_t ffline_len = 0;
1325     static char *ffline = NULL;
1326     const size_t FFLINE_STEP = 128;
1327 
1328     if (psp.drawfile == NULL || resource.postscript == 0) {
1329 	return;
1330     }
1331 
1332     if (filename[0] == '`') {
1333 	if (!resource.allow_shell) {
1334 	    if (!warned_about_shellescape) {
1335 		choice_dialog_sized(globals.widgets.top_level,
1336 				    MSG_INFO,
1337 				    SIZE_MEDIUM,
1338 				    /* helptext */
1339 				    "To enable shell specials, use the \"-allowshell\" command "
1340 				    "line option.\n\n"
1341 				    "WARNING: You should NOT use shell specials like\n"
1342 				    "`gunzip -c file.eps.gz\n"
1343 				    "(e.g. via \\DeclareGraphicsRule{...}) "
1344 				    "to uncompress .eps.gz files, even though some obsolete "
1345 				    "LaTeX documentation might suggest it. Current versions "
1346 				    "of xdvi and dvips will handle .eps.gz files just fine without this trick.\n",
1347 #ifndef MOTIF
1348 				    NULL,
1349 #endif
1350 				    NULL, NULL, /* no pre_callbacks */
1351 				    /* Cancel label/callback */
1352 				    "Cancel", NULL, (XtPointer)NULL,
1353 				    /* Enable label/callback */
1354 				    "Enable", enable_specials_send_ps_file, (XtPointer)filename,
1355 				    /* msg */
1356 				    "This page contains a shell special \"%s\", but execution of shell specials "
1357 				    "is disabled.\n\nYou can now click "
1358 				    "\"Enable\" to enable shell specials for this session only, "
1359 				    "\"Cancel\" to leave the specials disabled, or \"Help\" for more help "
1360 				    "on this topic.\n\n"
1361 				    "Please note that shell specials are a security risk, since they "
1362 				    "allow execution of arbitrary shell commands from the TeX file. "
1363 				    "You should enable them only for DVI files that you created yourself.",
1364 				    filename);
1365 		warned_about_shellescape = True;
1366 	    }
1367 	    else {
1368 		statusline_error(STATUS_MEDIUM,
1369 				 "Info: Shell special \"%s\" disabled.",
1370 				 filename);
1371 	    }
1372 	    draw_bbox();
1373 	    return;
1374 	}
1375 
1376 	tikp = cachetick(filename, pathinfo, &f, &fd);
1377 	if (tikp == NULL)	/* if error */
1378 	    return;
1379 	if (f == NULL) {
1380 	    char *argv[4];
1381 	    /* if not cached, need to create.
1382 	       Fork so that we can collect the process' error messages. */
1383 
1384 	    /* FIXME: Insecure - this is a /tmp race; shouldn't close(fd), just re-use it */
1385 	    close(fd);
1386 
1387 	    len = strlen(filename) + strlen(tikp->tempname) + (4 - 1);
1388 	    if (len > ffline_len) {
1389 		ffline_len += FFLINE_STEP;
1390 		ffline = xrealloc(ffline, ffline_len);
1391 	    }
1392 
1393 	    sprintf(ffline, "%s > %s", filename + 1, tikp->tempname);
1394 	    argv[0] = "/bin/sh";
1395 	    argv[1] = "-c";
1396 	    argv[2] = ffline;
1397 	    argv[3] = NULL;
1398 	    fork_process("/bin/sh", False, globals.dvi_file.dirname, try_open_tempname, tikp, argv);
1399 	}
1400 	else {
1401 	    bufp = tikp->tempname;
1402 	    psp.drawfile(bufp, f);
1403 	}
1404     }
1405     else {
1406 	char *expanded_filename = NULL;
1407 	struct stat statbuf;
1408 	expanded_filename = find_file(filename, &statbuf, pathinfo);
1409 
1410 	if (expanded_filename == NULL &&
1411 	    (pathinfo == kpse_enc_format || pathinfo == kpse_type1_format)) {
1412 	    /* in this case, we also kpathsea-search in the `old' place for
1413 	       backwards compatibility: kpse_tex_ps_header_format (see comment
1414 	       for load_vector(), dvi-draw.c for details) */
1415 	    expanded_filename = kpse_find_file(filename, kpse_tex_ps_header_format, True);
1416 	}
1417 	if (expanded_filename == NULL) {
1418 	    expanded_filename = kpse_find_file(filename, kpse_program_text_format, True);
1419 	}
1420 	if (expanded_filename == NULL) { /* still no success, complain */
1421 	    /* FIXME: this warning may be overwritten by warning about raw PS specials,
1422 	       so additinally dump to stderr. TODO: make statusline printing respect
1423 	       more important messages. */
1424 	    XDVI_WARNING((stderr, "Could not find graphics file \"%s\"", filename));
1425 	    statusline_info(STATUS_MEDIUM, "Warning: Could not find graphics file \"%s\"",
1426 			     filename);
1427 	    draw_bbox();
1428 	    return;
1429 	}
1430 	if (globals.debug & DBG_OPEN)
1431 	    printf("%s:%d: |%s| expanded to |%s|\n", __FILE__, __LINE__, filename, expanded_filename);
1432 
1433 	/* may need to adjust ffline_len, ffline */
1434 	while (strlen(expanded_filename) + 1 > ffline_len) {
1435 	    ffline_len += FFLINE_STEP;
1436 	    ffline = xrealloc(ffline, ffline_len);
1437 	}
1438 	strcpy(ffline, expanded_filename);
1439 	bufp = ffline;
1440 	f = XFOPEN(expanded_filename, OPEN_MODE);
1441 	if (f == NULL) {
1442 	    XDVI_WARNING((stderr, "Could not open graphics file \"%s\": %s", expanded_filename, strerror(errno)));
1443 	    statusline_info(STATUS_MEDIUM, "Warning: Could not open graphics file \"%s\": %s",
1444 			     expanded_filename, strerror(errno));
1445 	    free(expanded_filename);
1446 	    draw_bbox();
1447 	    return;
1448 	}
1449 	free(expanded_filename);
1450 
1451 	/* check for compressed files */
1452 	len = strlen(filename);
1453 	magic1 = '\037';
1454 	magic3 = '\0';
1455 	if ((len > 2 && strcmp(filename + len - 2, ".Z") == 0
1456 	     && (argv[0] = UNCOMPRESS, magic2 = '\235', True))
1457 	    || (len > 3 && strcmp(filename + len - 3, ".gz") == 0
1458 		&& (argv[0] = GUNZIP, magic2 = '\213', True))
1459 	    || (len > 4 && strcmp(filename + len - 4, ".bz2") == 0
1460 		&& (argv[0] = BUNZIP2, magic1 = 'B', magic2 = 'Z',
1461 		    magic3 = 'h', True))) {
1462 	    if (getc(f) != magic1 || (char)getc(f) != magic2
1463 		|| (magic3 != '\0' && getc(f) != magic3)) {
1464 		rewind(f);
1465 	    }
1466 	    else {
1467 		fclose(f);
1468 
1469 		tikp = cachetick(filename, pathinfo, &f, &fd);
1470 		if (tikp == NULL)	/* if error */
1471 		    return;
1472 		if (f == NULL) {	/* if not cached, need to create */
1473 		    pid_t pid;
1474 		    int status;
1475 
1476 		    argv[2] = bufp;
1477 		    fflush(stderr);	/* avoid double flushing */
1478 		    pid = vfork();
1479 		    if (pid == 0) {	/* if child */
1480 			(void)dup2(fd, 1);
1481 			(void)execvp(argv[0], (char **)argv);
1482 			XDVI_ERROR((stderr, "Execvp of %s failed: %s", argv[0], strerror(errno)));
1483 			_exit(EXIT_FAILURE);
1484 		    }
1485 		    (void)close(fd);
1486 
1487 		    for (;;) {
1488 #if HAVE_WAITPID
1489 			if (waitpid(pid, &status, 0) != -1)
1490 			    break;
1491 #else
1492 # if HAVE_WAIT4
1493 			if (wait4(pid, &status, 0, (struct rusage *)NULL)
1494 			    != -1)
1495 			    break;
1496 # else
1497 			int retval;
1498 
1499 			retval = wait(&status);
1500 			if (retval == pid)
1501 			    break;
1502 			if (retval != -1)
1503 			    continue;
1504 # endif	/* HAVE_WAIT4 */
1505 #endif /* HAVE_WAITPID */
1506 			if (errno == EINTR)
1507 			    continue;
1508 			perror("[xdvik] waitpid");
1509 			return;
1510 		    }
1511 		    f = XFOPEN(tikp->tempname, OPEN_MODE);
1512 		    if (f == NULL) {
1513 			perror(tikp->tempname);
1514 			return;
1515 		    }
1516 		}
1517 		bufp = tikp->tempname;
1518 	    }
1519 	}
1520 	/* Success! */
1521 	psp.drawfile(bufp, f); /* this is supposed to close the file */
1522     }
1523 }
1524 
1525 
1526 void
ps_destroy(void)1527 ps_destroy(void)
1528 {
1529     struct tickrec	*tikp;
1530 
1531     /* Note:  old NeXT systems (at least) lack atexit/on_exit.  */
1532     psp.destroy();
1533     for (tikp = tickhead; tikp != NULL; tikp = tikp->next)
1534 	if (unlink(tikp->tempname) < 0)
1535 	    perror(tikp->tempname);
1536 }
1537 
1538 #endif	/* PS */
1539 
1540 
1541 void
init_prescan(void)1542 init_prescan(void)
1543 {
1544 #if PS
1545     struct tickrec	*tikp;
1546 #endif
1547 
1548     scanned_page = scanned_page_reset = resource.prescan ? -1 : total_pages + 1;
1549 #if PS
1550     scanned_page_ps = scanned_page_ps_bak = scanned_page;
1551 #if COLOR
1552     scanned_page_color = scanned_page;
1553 #endif /* COLOR */
1554 #endif /* PS */
1555 
1556     TRACE_FILES((stderr, "init_prescan: scanned_page = %d", scanned_page));
1557 #if PS
1558     if (resource.postscript == 0)
1559 	scanned_page_ps = total_pages + 1;
1560 
1561     for (tikp = tickhead; tikp != NULL; tikp = tikp->next)
1562 	tikp->pageno = -1;
1563     psp.newdoc();
1564 #endif
1565 
1566 #if COLOR
1567     if (!resource.use_color)
1568 	scanned_page_color = total_pages + 1;
1569 #endif
1570 
1571     if (ignore_papersize_specials) {
1572 #if PS && COLOR
1573 	scanned_page = scanned_page_ps < scanned_page_color ? scanned_page_ps : scanned_page_color;
1574 #elif PS
1575 	scanned_page = scanned_page_ps;
1576 #elif COLOR
1577 	scanned_page = scanned_page_color;
1578 #else
1579 	scanned_page = total_pages + 1;
1580 #endif
1581     }
1582 }
1583 
1584 
1585 static void
psfig_special(char * cp)1586 psfig_special(char *cp)
1587 {
1588     char *filename;
1589     int raww, rawh;
1590 
1591     if (strncmp(cp, ":[begin]", 8) == 0) {
1592 	cp += 8;
1593 	bbox_valid = False;
1594 	bbox_angle = 0;
1595 	if (sscanf(cp, "%d %d\n", &raww, &rawh) >= 2) {
1596 	    bbox_valid = True;
1597 	    bbox_width = pixel_conv(spell_conv(raww));
1598 	    bbox_height = pixel_conv(spell_conv(rawh));
1599 	    bbox_voffset = 0;
1600 	}
1601 	if (INSIDE_MANE_WIN) {
1602 #if	PS
1603 	    psp.drawbegin(PXL_H - currwin.base_x, PXL_V - currwin.base_y, cp);
1604 #else
1605 	    draw_bbox();
1606 #endif
1607 	}
1608 	psfig_begun = True;
1609     }
1610     else if (strncmp(cp, " plotfile ", 10) == 0) {
1611 	cp += 10;
1612 	while (isspace((int)*cp))
1613 	    cp++;
1614 	/* handle "`zcat file". Borrowed from dvipsk... */
1615 	if (*cp == '"') {
1616 	    cp++;
1617 	    for (filename = cp; *cp && (*cp != '"'); ++cp);
1618 	}
1619 	else {
1620 	    for (filename = cp; *cp && !isspace((int)*cp); ++cp);
1621 	}
1622 	*cp = '\0';
1623 #if	PS
1624 	if (INSIDE_MANE_WIN)
1625 	    send_ps_file(filename, kpse_pict_format);
1626 #endif
1627     }
1628     else if (strncmp(cp, ":[end]", 6) == 0) {
1629 	cp += 6;
1630 #if	PS
1631 	if (INSIDE_MANE_WIN) {
1632 	    psp.drawend(cp);
1633 	}
1634 #endif
1635 	bbox_valid = False;
1636 	psfig_begun = False;
1637     }
1638     else if (*cp == ':') {
1639 	/* I am going to send some raw postscript stuff */
1640 	++cp;	/* skip the colon */
1641 #if	PS
1642 	if (ps_parseraw(cp))
1643 	    have_raw_postscript = True;
1644 	if (INSIDE_MANE_WIN)
1645 	    psp.drawraw(cp);
1646 #endif
1647     }
1648     else { /* attempt to parse pstricks color specials */
1649 #if COLOR
1650 	struct rgb color;
1651 	if (parse_color(cp, cp, &color, True)) {
1652 	    /* clear stack */
1653 	    if (rcs_head == NULL) {
1654 		rcs_head = xmalloc(sizeof *rcs_head);
1655 		rcs_head->prev = rcs_head->next = NULL;
1656 	    }
1657 	    rcs_top = rcs_head;
1658 	    color_bot_size = 0;
1659 
1660 	    /* Change top of stack */
1661 	    rcs_top->color = color;
1662 
1663 	    set_fg_color(&color);
1664 	}
1665 #endif
1666 	/* also raw PostScript, but no extra colon to skip */
1667 #if PS
1668 	if (ps_parseraw(cp) && INSIDE_MANE_WIN)
1669 		have_raw_postscript = True;
1670 
1671 	if (INSIDE_MANE_WIN) {
1672 	    if (psfig_begun)
1673 		psp.drawraw(cp);
1674 	    else {
1675 		psp.drawbegin(PXL_H - currwin.base_x,
1676 			      PXL_V - currwin.base_y, cp);
1677 		psp.drawend("");
1678 	    }
1679 	}
1680 #endif
1681     }
1682 }
1683 
1684 
1685 /*	Keys for epsf specials */
1686 
1687 static const char *keytab[] = { "clip",
1688 				"llx",
1689 				"lly",
1690 				"urx",
1691 				"ury",
1692 				"rwi",
1693 				"rhi",
1694 				"hsize",
1695 				"vsize",
1696 				"hoffset",
1697 				"voffset",
1698 				"hscale",
1699 				"vscale",
1700 				"angle"
1701 };
1702 
1703 #define	KEY_LLX	keyval[0]
1704 #define	KEY_LLY	keyval[1]
1705 #define	KEY_URX	keyval[2]
1706 #define	KEY_URY	keyval[3]
1707 #define	KEY_RWI	keyval[4]
1708 #define	KEY_RHI	keyval[5]
1709 
1710 #define	NKEYS	(sizeof keytab /sizeof *keytab)
1711 #define	N_ARGLESS_KEYS 1
1712 
1713 static void
epsf_special(char * cp)1714 epsf_special(char *cp)
1715 {
1716     char *filename;
1717     static char *buffer;
1718     static unsigned int buflen = 0;
1719     unsigned int len;
1720     char *q;
1721     int flags = 0;
1722     double keyval[6] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
1723 
1724     filename = cp;
1725     if (*cp == '\'' || *cp == '"') {
1726 	do
1727 	    ++cp;
1728 	while (*cp != '\0' && *cp != *filename);
1729 	++filename;
1730     }
1731     else
1732 	while (*cp != '\0' && !isspace((int)*cp))
1733 	    ++cp;
1734     if (*cp != '\0')
1735 	*cp++ = '\0';
1736     while (isspace((int)*cp))
1737 	++cp;
1738     len = strlen(cp) + NKEYS + 30;
1739     if (buflen < len) {
1740 	if (buflen != 0)
1741 	    free(buffer);
1742 	buflen = len;
1743 	buffer = xmalloc(buflen);
1744     }
1745     strcpy(buffer, "@beginspecial");
1746     q = buffer + strlen(buffer);
1747     while (*cp != '\0') {
1748 	char *p1 = cp;
1749 	size_t keyno;
1750 
1751 	while (*p1 != '=' && !isspace((int)*p1) && *p1 != '\0')
1752 	    ++p1;
1753 	for (keyno = 0;; ++keyno) {
1754 	    if (keyno >= NKEYS) {
1755 		if (globals.warn_spec_now)
1756 		    XDVI_WARNING((stderr, "Ignoring unknown keyword (%.*s) in \\special",
1757 				  (int)(p1 - cp), cp));
1758 		break;
1759 	    }
1760 	    if (memcmp(cp, keytab[keyno], p1 - cp) == 0) {
1761 		if (keyno >= N_ARGLESS_KEYS) {
1762 		    while (isspace((int)*p1))
1763 			++p1;
1764 		    if (*p1 == '=') {
1765 			++p1;
1766 			while (isspace((int)*p1))
1767 			    ++p1;
1768 		    }
1769 		    if (keyno < N_ARGLESS_KEYS + 6) {
1770 			keyval[keyno - N_ARGLESS_KEYS] = atof(p1);
1771 			flags |= (1 << (keyno - N_ARGLESS_KEYS));
1772 		    }
1773 		    *q++ = ' ';
1774 		    while (!isspace((int)*p1) && *p1 != '\0')
1775 			*q++ = *p1++;
1776 		}
1777 		*q++ = ' ';
1778 		*q++ = '@';
1779 		strcpy(q, keytab[keyno]);
1780 		q += strlen(q);
1781 		break;
1782 	    }
1783 	}
1784 	cp = p1;
1785 	while (!isspace((int)*cp) && *cp != '\0')
1786 	    ++cp;
1787 	while (isspace((int)*cp))
1788 	    ++cp;
1789     }
1790     strcpy(q, " @setspecial\n");
1791 
1792     bbox_valid = False;
1793     /* Validate the case where both rwi and rhi are undefined
1794      * (and llx, lly, urx, ury are properly defined) */
1795     if (!(flags & 0x30) && (flags & 0xf) == 0xf) {
1796 	KEY_RWI = (KEY_URX - KEY_LLX) * 10;
1797 	flags |= 0x10;
1798     }
1799     if ((flags & 0x30) == 0x30 || ((flags & 0x30) && (flags & 0xf) == 0xf)) {
1800 	bbox_valid = True;
1801 	bbox_width = 0.1 * ((flags & 0x10) ? KEY_RWI
1802 			    : KEY_RHI * (KEY_URX - KEY_LLX) / (KEY_URY -
1803 							       KEY_LLY)) *
1804 	    dimconv / currwin.shrinkfactor + 0.5;
1805 	bbox_voffset = bbox_height =
1806 	    0.1 * ((flags & 0x20) ? KEY_RHI : KEY_RWI * (KEY_URY - KEY_LLY) /
1807 		   (KEY_URX - KEY_LLX))
1808 	    * dimconv / currwin.shrinkfactor + 0.5;
1809     }
1810 
1811     if (INSIDE_MANE_WIN) {
1812 #if	PS
1813 	psp.drawbegin(PXL_H - currwin.base_x, PXL_V - currwin.base_y, buffer);
1814 	/* talk directly with the DPSHandler here */
1815 	send_ps_file(filename, kpse_pict_format);
1816 	psp.drawend(" @endspecial");
1817 #else
1818 	draw_bbox();
1819 #endif
1820     }
1821     bbox_valid = False;
1822 }
1823 
1824 
1825 static void
quote_special(char * cp)1826 quote_special(char *cp)
1827 {
1828     bbox_valid = False;
1829 
1830 #if	PS
1831     if (currwin.win == mane.win) {
1832 	psp.drawbegin(PXL_H - currwin.base_x, PXL_V - currwin.base_y,
1833 		      "@beginspecial @setspecial ");
1834 	/* talk directly with the DPSHandler here */
1835 	psp.drawraw(cp + 1);
1836 	psp.drawend(" @endspecial");
1837     }
1838 #endif
1839 
1840     /* nothing else to do--there's no bbox here */
1841 }
1842 
1843 #if	PS
1844 
1845 static void
scan_header(char * cp)1846 scan_header(char *cp)
1847 {
1848     char *filename;
1849     /* default for .pro files: */
1850     kpse_file_format_type our_format;
1851 
1852 #if PS_GS
1853     if (gs_postpone_prescan)
1854 	return;
1855 #endif
1856     filename = cp;
1857     if (*cp == '\'' || *cp == '"') {
1858 	do
1859 	    ++cp;
1860 	while (*cp != '\0' && *cp != *filename);
1861 	*cp = '\0';
1862 	++filename;
1863     }
1864 
1865     psp.beginheader();
1866     /* check for suffixes other than `.pro' (default PS headers),
1867        case-insensitive */
1868     if (str_is_suffix(".pfa", filename, False)
1869 	|| str_is_suffix(".pfb", filename, False))
1870 	our_format = kpse_type1_format;
1871     else if (str_is_suffix(".enc", filename, False))
1872 	our_format = kpse_enc_format;
1873     else
1874 	our_format = kpse_tex_ps_header_format;
1875 
1876     send_ps_file(filename, our_format);
1877 }
1878 
1879 static void
scan_bang(char * cp)1880 scan_bang(char *cp)
1881 {
1882     psp.beginheader();
1883     psp.drawraw(cp + 1);
1884 }
1885 
1886 #endif /* PS */
1887 
1888 #if COLOR
1889 
1890 /*
1891  *	Table of dvips color names.  Produced by passing the table in
1892  *	dvipsk/color.lpro through the following perl script, and then
1893  *	through sort.
1894  *
1895  *	#! /usr/bin/perl -w
1896  *
1897  *	sub cvpart {
1898  *	    return $_[0] < 1.0 ? 1.0 - $_[0] : 0.0;
1899  *	}
1900  *
1901  *	$pat = "^\\/(\\w+)\\s*\\{\\s*([0-9.]+)\\s*([0-9.]+)\\s*([0-9.]+)"
1902  *	  . "\\s*([0-9.]+)\\s*setcmykcolor\\s*\\}\\s*DC\\s*\$";
1903  *	while (<STDIN>) {
1904  *	    chop;
1905  *	    if (/^%%/) {next;}
1906  *	    if (/$pat/o) {
1907  *		printf "\t\tDVIPSCOLORDESC(%2d, \"%s\", %g, %g, %g),\n",
1908  *		  length($1), $1,
1909  *		  cvpart($2 + $5), cvpart($3 + $5), cvpart($4 + $5);
1910  *	    }
1911  *	    else {
1912  *		print "Bad line: ", $_, "\n";
1913  *	    }
1914  *	}
1915  */
1916 
1917 struct dvipscolor {
1918     const char *name;
1919     struct rgb color;
1920 };
1921 
1922 static Boolean
parse_color(const char * cp0,const char * cp,struct rgb * rgbp,Boolean generic_ps_flag)1923 parse_color(const char *cp0, const char *cp,
1924 	    struct rgb *rgbp, Boolean generic_ps_flag)
1925 {
1926     double r, g, b;
1927     double k;
1928     double hue, sat, bri;
1929     char dummy[8];
1930 
1931 #define	CVPART(x)	((int)((x) * 65535 + 0.5))
1932 #define	COLOR_DESC(name, r, g, b) \
1933 		{name, {CVPART(r), CVPART(g), CVPART(b)}}
1934 
1935     /* hash table to speed up lookup in following list of color names */
1936     static hashTableT colornames_hash;
1937     /*
1938       Use a prime close to sizof colornames / sizeof colornames[0].
1939       You might want to update this when extending the colornames array,
1940       and to check the filling factor and average chain length by uncommenting the
1941       hash_print(colornames_hash, True);
1942       below. I usually go for filling > 50% and avg. chain len < 2.
1943 
1944       The 68-elem array below results in:
1945       79 buckets, 43 nonempty (54%); 68 entries, average chain 1.6.
1946     */
1947     static const size_t colornames_hash_size = 79;
1948 
1949     static struct dvipscolor colornames[] = {
1950 	COLOR_DESC("Red", 1, 0, 0),
1951 	COLOR_DESC("Tan", 0.86, 0.58, 0.44),
1952 	COLOR_DESC("Blue", 0, 0, 1),
1953 	COLOR_DESC("Cyan", 0, 1, 1),
1954 	COLOR_DESC("Gray", 0.5, 0.5, 0.5),
1955 	COLOR_DESC("Plum", 0.5, 0, 1),
1956 	COLOR_DESC("Black", 0, 0, 0),
1957 	COLOR_DESC("Brown", 0.4, 0, 0),
1958 	COLOR_DESC("Green", 0, 1, 0),
1959 	COLOR_DESC("Melon", 1, 0.54, 0.5),
1960 	COLOR_DESC("Peach", 1, 0.5, 0.3),
1961 	COLOR_DESC("Sepia", 0.3, 0, 0),
1962 	COLOR_DESC("White", 1, 1, 1),
1963 	COLOR_DESC("Maroon", 0.68, 0, 0),
1964 	COLOR_DESC("Orange", 1, 0.39, 0.13),
1965 	COLOR_DESC("Orchid", 0.68, 0.36, 1),
1966 	COLOR_DESC("Purple", 0.55, 0.14, 1),
1967 	COLOR_DESC("Salmon", 1, 0.47, 0.62),
1968 	COLOR_DESC("Violet", 0.21, 0.12, 1),
1969 	COLOR_DESC("Yellow", 1, 1, 0),
1970 	COLOR_DESC("Apricot", 1, 0.68, 0.48),
1971 	COLOR_DESC("Emerald", 0, 1, 0.5),
1972 	COLOR_DESC("Fuchsia", 0.45, 0.01, 0.92),
1973 	COLOR_DESC("Magenta", 1, 0, 1),
1974 	COLOR_DESC("SkyBlue", 0.38, 1, 0.88),
1975 	COLOR_DESC("Thistle", 0.88, 0.41, 1),
1976 	COLOR_DESC("BrickRed", 0.72, 0, 0),
1977 	COLOR_DESC("Cerulean", 0.06, 0.89, 1),
1978 	COLOR_DESC("Lavender", 1, 0.52, 1),
1979 	COLOR_DESC("Mahogany", 0.65, 0, 0),
1980 	COLOR_DESC("Mulberry", 0.64, 0.08, 0.98),
1981 	COLOR_DESC("NavyBlue", 0.06, 0.46, 1),
1982 	COLOR_DESC("SeaGreen", 0.31, 1, 0.5),
1983 	COLOR_DESC("TealBlue", 0.12, 0.98, 0.64),
1984 	COLOR_DESC("BlueGreen", 0.15, 1, 0.67),
1985 	COLOR_DESC("CadetBlue", 0.38, 0.43, 0.77),
1986 	COLOR_DESC("Dandelion", 1, 0.71, 0.16),
1987 	COLOR_DESC("Goldenrod", 1, 0.9, 0.16),
1988 	COLOR_DESC("LimeGreen", 0.5, 1, 0),
1989 	COLOR_DESC("OrangeRed", 1, 0, 0.5),
1990 	COLOR_DESC("PineGreen", 0, 0.75, 0.16),
1991 	COLOR_DESC("RawSienna", 0.55, 0, 0),
1992 	COLOR_DESC("RedOrange", 1, 0.23, 0.13),
1993 	COLOR_DESC("RedViolet", 0.59, 0, 0.66),
1994 	COLOR_DESC("Rhodamine", 1, 0.18, 1),
1995 	COLOR_DESC("RoyalBlue", 0, 0.5, 1),
1996 	COLOR_DESC("RubineRed", 1, 0, 0.87),
1997 	COLOR_DESC("Turquoise", 0.15, 1, 0.8),
1998 	COLOR_DESC("VioletRed", 1, 0.19, 1),
1999 	COLOR_DESC("Aquamarine", 0.18, 1, 0.7),
2000 	COLOR_DESC("BlueViolet", 0.1, 0.05, 0.96),
2001 	COLOR_DESC("DarkOrchid", 0.6, 0.2, 0.8),
2002 	COLOR_DESC("OliveGreen", 0, 0.6, 0),
2003 	COLOR_DESC("Periwinkle", 0.43, 0.45, 1),
2004 	COLOR_DESC("Bittersweet", 0.76, 0.01, 0),
2005 	COLOR_DESC("BurntOrange", 1, 0.49, 0),
2006 	COLOR_DESC("ForestGreen", 0, 0.88, 0),
2007 	COLOR_DESC("GreenYellow", 0.85, 1, 0.31),
2008 	COLOR_DESC("JungleGreen", 0.01, 1, 0.48),
2009 	COLOR_DESC("ProcessBlue", 0.04, 1, 1),
2010 	COLOR_DESC("RoyalPurple", 0.25, 0.1, 1),
2011 	COLOR_DESC("SpringGreen", 0.74, 1, 0.24),
2012 	COLOR_DESC("YellowGreen", 0.56, 1, 0.26),
2013 	COLOR_DESC("MidnightBlue", 0, 0.44, 0.57),
2014 	COLOR_DESC("YellowOrange", 1, 0.58, 0),
2015 	COLOR_DESC("CarnationPink", 1, 0.37, 1),
2016 	COLOR_DESC("CornflowerBlue", 0.35, 0.87, 1),
2017 	COLOR_DESC("WildStrawberry", 1, 0.04, 0.61),
2018     };
2019 #undef CVPART
2020 #undef COLOR_DESC
2021 
2022     UNUSED(cp0);
2023 
2024     while (*cp == ' ') ++cp;
2025 
2026     if (generic_ps_flag) {
2027         /*
2028 	  check for pstricks color specials.
2029 	  The dummy buffer is to ensure that there are no other (general PS)
2030 	  commands following (we wouldn't know how to deal with these).
2031 	*/
2032 	if (sscanf(cp, "%lf %lf %lf setrgbcolor %7s", &r, &g, &b, dummy) == 3
2033 	    && r >= 0 && r <= 1 && g >= 0 && g <= 1 && b >= 0 && b <= 1) {
2034 	    rgbp->r = r * 65535 + 0.5;
2035 	    rgbp->g = g * 65535 + 0.5;
2036 	    rgbp->b = b * 65535 + 0.5;
2037 	    return True;
2038 	}
2039 	else if (sscanf(cp, "%lf %lf %lf %lf setcmykcolor %7s", &r, &g, &b, &k, dummy) == 4
2040 		 && r >= 0 && r <= 1 && g >= 0 && g <= 1 && b >= 0 && b <= 1
2041 		 && k >= 0 && k <= 1) {
2042 	    r = 1.0 - r - k;	/* cyan --> red */
2043 	    rgbp->r = (r < 0 ? 0 : r * 65535 + 0.5);
2044 	    g = 1.0 - g - k;	/* magenta --> green */
2045 	    rgbp->g = (g < 0 ? 0 : g * 65535 + 0.5);
2046 	    b = 1.0 - b - k;	/* yellow --> blue */
2047 	    rgbp->b = (b < 0 ? 0 : b * 65535 + 0.5);
2048 	    return True;
2049 	}
2050 	else if (sscanf(cp, "%lf %lf %lf sethsbcolor %7s", &hue, &sat, &bri, dummy) == 3 && hue >= 0
2051 		 && hue <= 1 && sat >= 0 && sat <= 1 && bri >= 0 && bri <= 1) {
2052 	    int	h	= (int) (6 * hue);
2053 	    double	p	= bri * (1 - sat);
2054 	    double	q	= bri * (1 - sat * (6 * hue - h));
2055 	    double	t	= p - q + bri;
2056 
2057 	    switch (h) {
2058 	    case 0:  r = bri; g = t; b = p; break; /* Red - Yel */
2059 	    case 1:  r = q; g = bri; b = p; break; /* Yel - Grn */
2060 	    case 2:  r = p; g = bri; b = t; break; /* Grn - Cyn */
2061 	    case 3:  r = p; g = q; b = bri; break; /* Cyn - Blu */
2062 	    case 4:  r = t; g = p; b = bri; break; /* Blu - Mag */
2063 	    case 5:  r = bri; g = p; b = q; break; /* Mag - Red */
2064 	    case 6:  r = bri; g = b = p;    break; /* Red */
2065 	    }
2066 	    rgbp->r = r * 65535 + 0.5;
2067 	    rgbp->g = g * 65535 + 0.5;
2068 	    rgbp->b = b * 65535 + 0.5;
2069 	    return True;
2070 	}
2071 	else if (sscanf(cp, "%lf setgray %7s", &r, dummy) == 1 && r >= 0 && r <= 1) {
2072 	    rgbp->r = rgbp->g = rgbp->b = r * 65535 + 0.5;
2073 	    return True;
2074 	}
2075 	else
2076 	    return False;
2077     }
2078 
2079     /* no generic PS command; must be a dvips color special */
2080 
2081     if (memicmp(cp, "rgb ", 4) == 0) {
2082 	if (sscanf(cp + 3, "%lf %lf %lf", &r, &g, &b) == 3
2083 	    && r >= 0 && r <= 1 && g >= 0 && g <= 1 && b >= 0 && b <= 1) {
2084 	    rgbp->r = r * 65535 + 0.5;
2085 	    rgbp->g = g * 65535 + 0.5;
2086 	    rgbp->b = b * 65535 + 0.5;
2087 	    return True;
2088 	}
2089     }
2090     else if (memicmp(cp, "gray ", 5) == 0) {
2091 	if (sscanf(cp + 4, "%lf", &r) == 1 && r >= 0 && r <= 1) {
2092 	    rgbp->r = rgbp->g = rgbp->b = r * 65535 + 0.5;
2093 	    return True;
2094 	}
2095     }
2096     else if (memicmp(cp, "cmyk ", 5) == 0) {
2097 	if (sscanf(cp + 4, "%lf %lf %lf %lf", &r, &g, &b, &k) == 4
2098 	    && r >= 0 && r <= 1 && g >= 0 && g <= 1 && b >= 0 && b <= 1
2099 	    && k >= 0 && k <= 1) {
2100 	    r = 1.0 - r - k;	/* cyan --> red */
2101 	    rgbp->r = (r < 0 ? 0 : r * 65535 + 0.5);
2102 	    g = 1.0 - g - k;	/* magenta --> green */
2103 	    rgbp->g = (g < 0 ? 0 : g * 65535 + 0.5);
2104 	    b = 1.0 - b - k;	/* yellow --> blue */
2105 	    rgbp->b = (b < 0 ? 0 : b * 65535 + 0.5);
2106 	    return True;
2107 	}
2108     }
2109     else if (memicmp(cp, "hsb ", 4) == 0) {
2110 	if (sscanf(cp + 3, "%lf %lf %lf", &hue, &sat, &bri) == 3 && hue >= 0
2111 	    && hue <= 1 && sat >= 0 && sat <= 1 && bri >= 0 && bri <= 1) {
2112 	    int	h	= (int) (6 * hue);
2113 	    double	p	= bri * (1 - sat);
2114 	    double	q	= bri * (1 - sat * (6 * hue - h));
2115 	    double	t	= p - q + bri;
2116 
2117 	    switch (h) {
2118 	    case 0:  r = bri; g = t; b = p; break; /* Red - Yel */
2119 	    case 1:  r = q; g = bri; b = p; break; /* Yel - Grn */
2120 	    case 2:  r = p; g = bri; b = t; break; /* Grn - Cyn */
2121 	    case 3:  r = p; g = q; b = bri; break; /* Cyn - Blu */
2122 	    case 4:  r = t; g = p; b = bri; break; /* Blu - Mag */
2123 	    case 5:  r = bri; g = p; b = q; break; /* Mag - Red */
2124 	    case 6:  r = bri; g = b = p;    break; /* Red */
2125 	    }
2126 	    rgbp->r = r * 65535 + 0.5;
2127 	    rgbp->g = g * 65535 + 0.5;
2128 	    rgbp->b = b * 65535 + 0.5;
2129 	    return True;
2130 	}
2131     }
2132     else { /* is it a dvips color name? */
2133 	size_t idx;
2134 	size_t len = 0;
2135 	static char *testname = NULL;
2136 	static size_t testname_len = 0;
2137 	size_t testname_step = 16;
2138 
2139 	if (colornames_hash.size == 0) { /* initialize hash table */
2140 	    size_t i;
2141 	    colornames_hash = hash_create(colornames_hash_size);
2142 	    /* insert color names and array indexes */
2143 	    for (i = 0; i < (sizeof colornames / sizeof colornames[0]); i++) {
2144 		put_str_int_hash(&colornames_hash, colornames[i].name, i);
2145 	    }
2146 	}
2147 	/* uncomment the following to get statistics on hashing: */
2148 	/* hash_print(colornames_hash, True); */
2149 
2150 	/* need to copy to a null-terminated string for hash lookup ... */
2151 	while (isalpha((int)cp[len])) {
2152 	    ++len;	/* get length of color name */
2153 	}
2154 	while (len >= testname_len) {
2155 	    testname_len += testname_step;
2156 	    testname = xrealloc(testname, testname_len);
2157 	}
2158 	memcpy(testname, cp, len);
2159 	testname[len] = '\0';
2160 
2161 	if (find_str_int_hash(&colornames_hash, testname, &idx)) {
2162 	    *rgbp = colornames[idx].color;
2163 	    return True;
2164 	}
2165     }
2166 
2167     /* not found */
2168     XDVI_WARNING((stderr, "Ignoring invalid color name in special `%s'", cp0 == NULL ? "<NULL>" : cp0));
2169     return False;
2170 }
2171 
2172 void
init_page_colors(struct rgb * foreground,struct rgb * background)2173 init_page_colors(struct rgb *foreground, struct rgb *background)
2174 {
2175     int	i;
2176 
2177     page_colors.size = total_pages;
2178     page_colors.stack = xmalloc(page_colors.size * sizeof *page_colors.stack);
2179 
2180     for (i = 0; i <= scanned_page + 1; ++i) {
2181 	page_colors.stack[i].bg = *background;
2182 	page_colors.stack[i].colorstack = foreground;
2183 	page_colors.stack[i].stacksize = 1;
2184     }
2185     for (; i < total_pages; i++) {
2186 	page_colors.stack[i].colorstack = NULL;
2187     }
2188 
2189     scanstack_head.color = *foreground;
2190     scanstack_len = 1;			/* nothing yet */
2191     scanstack_current = &scanstack_head;	/* stack position */
2192 }
2193 
2194 
2195 static void
scan_bg_color(const char * cp)2196 scan_bg_color(const char *cp)
2197 {
2198     if (!resource.use_color)
2199 	return;
2200 
2201     if (page_colors.stack == NULL)
2202 	init_page_colors(&fg_initial, &bg_initial);
2203 
2204     ASSERT(scanned_page < (int)page_colors.size, "page_colors.size too small");
2205     (void) parse_color(cp, cp + 11, &(page_colors.stack[scanned_page + 1].bg), False);
2206 }
2207 
2208 void
scan_color(const char * cp)2209 scan_color(const char *cp)
2210 {
2211     const char	*cp1 = cp + 6;
2212 
2213     if (!resource.use_color)
2214 	return;
2215 
2216     while (*cp1 == ' ') ++cp1;
2217 
2218     if (page_colors.stack == NULL)
2219 	init_page_colors(&fg_initial, &bg_initial);
2220 
2221     if (memicmp(cp1, "push ", 5) == 0) {
2222 	if (scanstack_current->next == NULL) {	/* if at end */
2223 	    scanstack_current->next = xmalloc(sizeof *scanstack_current);
2224 	    scanstack_current->next->prev = scanstack_current;
2225 	    scanstack_current->next->next = NULL;
2226 	}
2227 	scanstack_current = scanstack_current->next;
2228 	++scanstack_len;
2229 	if (!parse_color(cp, cp1 + 5, &scanstack_current->color, False))
2230 	    scanstack_current->color = scanstack_current->prev->color;
2231     }
2232     else if (memicmp(cp1, "pop", 3) == 0) {
2233 	if (scanstack_len <= 1) {
2234 	    XDVI_WARNING((stderr, "Color pop occurred with empty color stack."));
2235 	}
2236 	else {
2237 	    scanstack_current = scanstack_current->prev;
2238 	    --scanstack_len;
2239 	}
2240     }
2241     else {
2242 	(void) parse_color(cp, cp1, &scanstack_head.color, False);
2243 	if (scanstack_len > 1) {
2244 	    struct colorframe	*cfp;
2245 
2246 	    XDVI_WARNING((stderr, "Global color change occurred with non-empty color stack!\n"
2247 			  "Trying to recover by setting all stack entries to that color."));
2248 	    for (cfp = scanstack_head.next;; cfp = cfp->next) {
2249 		cfp->color = scanstack_head.color;
2250 		if (cfp == scanstack_current) break;
2251 	    }
2252 	}
2253     }
2254 }
2255 
2256 void
scan_color_eop(void)2257 scan_color_eop(void)
2258 {
2259     int i;
2260     struct rgb *prev, *p1, *rgbp;
2261     struct colorframe *cf;
2262 
2263     if (page_colors.stack == NULL)
2264 	return;
2265 
2266     /* set background color for next page */
2267     if (scanned_page + 1 < total_pages) {
2268 	ASSERT(scanned_page + 1 < (int)page_colors.size, "page_colors.size too small");
2269 	page_colors.stack[scanned_page + 1].bg = page_colors.stack[scanned_page].bg;
2270     }
2271     ASSERT(scanned_page < (int)page_colors.size, "page_colors.size too small");
2272     /* save the stack contents */
2273     page_colors.stack[scanned_page].stacksize = scanstack_len;
2274 
2275     prev = &fg_initial;
2276     i = 1;
2277     if (scanned_page > 0) {
2278 	prev = page_colors.stack[scanned_page - 1].colorstack;
2279 	i = page_colors.stack[scanned_page - 1].stacksize;
2280     }
2281     if (scanstack_len <= i) {
2282 	/* try to reuse the previous array */
2283 	p1 = prev;
2284 	cf = &scanstack_head;
2285 	for (i = 0;;) {
2286 	    if (p1->r != cf->color.r || p1->g != cf->color.g || p1->b != cf->color.b)
2287 		break;
2288 	    if (++i >= scanstack_len) {	/* end loop; reuse memory */
2289 		page_colors.stack[scanned_page].colorstack = prev;
2290 		return;	/* done */
2291 	    }
2292 	    ++p1;
2293 	    cf = cf->next;
2294 	}
2295     }
2296     page_colors.stack[scanned_page].colorstack = rgbp = xmalloc(scanstack_len * sizeof *rgbp);
2297     cf = &scanstack_head;
2298     for (i = 0; i < scanstack_len; ++i) {
2299 	*rgbp++ = cf->color;
2300 	cf = cf->next;
2301     }
2302 }
2303 
2304 
2305 /*
2306  *	We don't actually do any X calls yet to change colors; that can wait
2307  *	until a character or rule needs to be drawn.
2308  */
2309 
2310 void
set_fg_color(const struct rgb * color)2311 set_fg_color(const struct rgb *color)
2312 {
2313     struct fgrec	**fgpp;
2314 
2315     if (fg_current != NULL
2316 	&& color->r == fg_current->color.r
2317 	&& color->g == fg_current->color.g
2318 	&& color->b == fg_current->color.b) {
2319 	return;
2320     }
2321 
2322     ASSERT(bg_current != NULL, "Background color not initialized");
2323     for (fgpp = &bg_current->fg_head;;) {
2324 	fg_current = *fgpp;
2325 	if (fg_current == NULL) {	/* if color is not in list */
2326 	    fg_current = *fgpp = xmalloc(sizeof *fg_current);
2327 	    fg_current->next = NULL;
2328 	    fg_current->color = *color;
2329 	    fg_current->pixel_good = False;
2330 #if GREY
2331 	    fg_current->palette_good = False;
2332 #endif
2333 	    break;
2334 	}
2335 	if (fg_current->color.r == color->r
2336 	    && fg_current->color.g == color->g
2337 	    && fg_current->color.b == color->b)
2338 	    break;
2339 	fgpp = &fg_current->next;
2340     }
2341     if (globals.debug & DBG_DVI)
2342 	printf("Changing fg color to %5d %5d %5d\n",
2343 	       color->r, color->g, color->b);
2344 }
2345 
2346 #if 0
2347 static void
2348 show_stack(void) {
2349     struct colorframe *ptr;
2350     int i;
2351     fprintf(stderr, "FG: %d,%d,%d\n", fg_current->color.r, fg_current->color.b, fg_current->color.g);
2352     for (i = 0, ptr = rcs_top; ptr != NULL; ptr = ptr->prev, i++) {
2353 	fprintf(stderr, "stack %d: %d,%d,%d\n", i, ptr->color.r, ptr->color.b, ptr->color.g);
2354     }
2355 }
2356 #endif
2357 
2358 void
color_special(const char * cp)2359 color_special(const char *cp)
2360 {
2361     while (*cp == ' ') ++cp;
2362 
2363     if (memicmp(cp, "push ", 5) == 0) {
2364 	if (rcs_top == NULL) {
2365 	    if (rcs_head == NULL) {
2366 		rcs_head = xmalloc(sizeof *rcs_head);
2367 		rcs_head->prev = rcs_head->next = NULL;
2368 	    }
2369 	    rcs_top = rcs_head;
2370 	}
2371 	else {
2372 	    struct colorframe *rcs_next;
2373 
2374 	    rcs_next = rcs_top->next;
2375 	    if (rcs_next == NULL) {
2376 		rcs_next = rcs_top->next = xmalloc(sizeof *rcs_next);
2377 		rcs_next->prev = rcs_top;
2378 		rcs_next->next = NULL;
2379 	    }
2380 	    rcs_top = rcs_next;
2381 	}
2382 	if (!parse_color(NULL, cp + 5, &rcs_top->color, False)) {
2383 	    if (rcs_top->prev != NULL)
2384 		rcs_top->color = rcs_top->prev->color;
2385 	    else
2386 		rcs_top->color = color_bottom[color_bot_size - 1];
2387 	}
2388 	set_fg_color(&rcs_top->color);
2389     }
2390     else if (memicmp(cp, "pop", 3) == 0) {
2391 	/* Pop stack */
2392 	if (rcs_top != NULL) {
2393 	    if (color_bot_size > 0)
2394 		rcs_top = rcs_top->prev;
2395 	    else
2396 		return;
2397 	}
2398 	else if (color_bot_size > 1)
2399 	    --color_bot_size;
2400 	else {
2401 	    if (scanned_page_reset >= 0) {
2402 		/* turn on scanning and redraw the page */
2403 		scanned_page = scanned_page_color = scanned_page_reset = -1;
2404 #if PS
2405 		scanned_page_ps = scanned_page;
2406 #endif
2407 		/* 		fprintf(stderr, "forcing redraw!!!\n"); */
2408 		globals.ev.flags |= EV_NEWPAGE;		/* force a redraw */
2409 		longjmp(globals.ev.canit, 1);
2410 	    }
2411 	    return;
2412 	}
2413 	/* 	fprintf(stderr, "bot_size: %d\n", color_bot_size); */
2414 	set_fg_color(rcs_top != NULL ? &rcs_top->color
2415 		     : &color_bottom[color_bot_size - 1]);
2416     }
2417     else {
2418 	struct rgb color;
2419 
2420 	if (!parse_color(NULL, cp, &color, False))
2421 	    return;
2422 
2423 	/* clear stack */
2424 	if (rcs_head == NULL) {
2425 	    rcs_head = xmalloc(sizeof *rcs_head);
2426 	    rcs_head->prev = rcs_head->next = NULL;
2427 	}
2428 	rcs_top = rcs_head;
2429 	color_bot_size = 0;
2430 
2431 	/* Change top of stack */
2432 	rcs_top->color = color;
2433 
2434 	set_fg_color(&color);
2435     }
2436 }
2437 
2438 #endif /* COLOR */
2439 
2440 static unsigned int
myatopix(const char ** pp)2441 myatopix(const char **pp)
2442 {
2443 #define SCR_LEN 16
2444     unsigned int value;
2445     const char *cp = *pp;
2446     char scr[16];
2447     const char *p0, *p1;
2448 
2449     p0 = cp;
2450     while ((*cp >= '0' && *cp <= '9') || *cp == '.')
2451 	++cp;
2452     p1 = cp;
2453     while (isspace((int)*cp))
2454 	++cp;
2455     if (*cp >= 'a' && *cp <= 'z' && cp[1] >= 'a' && cp[1] <= 'z') {
2456 	/* if units are present */
2457 	if (p1 - p0 <= SCR_LEN - 3) {
2458 	    sprintf(scr, "%.*s%c%c", (int)(p1 - p0), p0, *cp, cp[1]);
2459 	    value = atopix(scr, False);
2460 	}
2461 	else
2462 	    value = 0;
2463 	cp += 2;
2464     }
2465     else
2466 	value = atopix(p0, False);
2467 
2468     *pp = cp;
2469     return value;
2470 #undef SCR_LEN
2471 }
2472 
2473 static void
scan_papersize(const char * cp0)2474 scan_papersize(const char *cp0)
2475 {
2476     const char *cp = cp0;
2477     unsigned int w, h;
2478     double mag = 1.0;
2479 
2480     if (ignore_papersize_specials)
2481 	return;
2482 
2483     if (*cp == '*') {
2484 	do
2485 	    ++cp;
2486 	while (isspace((int)*cp));
2487 	mag = magnification * .001;
2488     }
2489 
2490     w = myatopix(&cp) * mag + 0.5;
2491 
2492     while (isspace((int)*cp))
2493 	++cp;
2494     if (*cp == ',')
2495 	do
2496 	    ++cp;
2497 	while (isspace((int)*cp));
2498 
2499     h = myatopix(&cp) * mag + 0.5;
2500 
2501     if (w == 0 || h == 0)
2502 	XDVI_WARNING((stderr, "Invalid papersize special `%s'", cp0));
2503     else {
2504 	/* we have a paper size special; disable xdvirc_geometry:
2505 	   fprintf(stderr, "---------- scanned papersize: %dx%d\n", w, h);
2506 	   resource.xdvirc_geometry = NULL;
2507 	*/
2508 	pageinfo_set_page_width(scanned_page + 1, w);
2509 	pageinfo_set_window_width(scanned_page + 1, w);
2510 	pageinfo_set_page_height(scanned_page + 1, h);
2511 	pageinfo_set_window_height(scanned_page + 1, h);
2512     }
2513 }
2514 
2515 /*
2516 ** Modified 22/3/95.
2517 */
2518 
2519 typedef struct psh_node {
2520     char *this_file;
2521     struct psh_node *next;
2522 } t_pshnode;
2523 
2524 static t_pshnode *psh_head = (t_pshnode *) NULL, *psh_tail = (t_pshnode *) NULL;
2525 
2526 /*
2527 ** Include one header file.
2528 */
2529 
psh_includehfile(cp,beginning)2530 static void psh_includehfile(cp, beginning)
2531 char *cp;
2532 int beginning;
2533 
2534 {
2535     static char rawstring[] = " stop\n%%xdvimark\nH userdict begin\0";
2536 
2537     if (!beginning)
2538         psp.drawbegin(PXL_H - currwin.base_x, PXL_V - currwin.base_y, "");
2539     psp.drawraw(rawstring);
2540     send_ps_file(cp, kpse_pict_format);
2541     psp.drawend(" end\n");
2542 }
2543 
2544 /*
2545 ** Include all header files.
2546 */
2547 
psh_includeall()2548 void psh_includeall()
2549 
2550 {
2551     t_pshnode *this_node;
2552 
2553     this_node = psh_head;
2554     while (this_node != (t_pshnode *) NULL) {
2555 	psh_includehfile(this_node->this_file, 1);
2556 	this_node = this_node->next;
2557     }
2558 }
2559 
2560 /*
2561 ** Deal with the "header=<file>" special.
2562 */
2563 
psh_header(cp)2564 static void psh_header(cp)
2565 char *cp;
2566 
2567 {
2568     t_pshnode *this_node;
2569     char *psheader;
2570 
2571     /*
2572     ** Find the file.
2573     */
2574 
2575     psheader = kpse_find_file ( cp+7, kpse_tex_ps_header_format, true );
2576 
2577     if (psheader) {
2578 
2579         /*
2580 	** Include the file directly in /userdict.
2581 	*/
2582 
2583 	/* psh_includehfile(psheader, 0); */
2584 	/* deleted by Kazuaki NAKAYAMA (nakayama@ripms.shinshu-u.ac.jp) */
2585 
2586 	/*
2587 	** Add a new node to the list of header files.
2588 	*/
2589 
2590 	this_node = (t_pshnode *) xmalloc(sizeof(t_pshnode));
2591 	this_node->this_file = strcpy(xmalloc((strlen(psheader) + 1) * sizeof(char)), psheader);
2592 	this_node->next = (t_pshnode *) NULL;
2593 	if (psh_head == (t_pshnode *) NULL)
2594 	    psh_head = psh_tail = this_node;
2595 	else {
2596 	    psh_tail->next = this_node;
2597 	    psh_tail = this_node;
2598 	}
2599 
2600     } else
2601 	XDVI_WARNING((stderr, "Included PostScript header file %s not found", cp + 7));
2602 }
2603 
2604 /*
2605 ** End Modifications
2606 */
2607 
2608 
2609 /*
2610  *	The following copyright message applies to the rest of this file.  --PV
2611  */
2612 
2613 /*
2614  *	This program is Copyright (C) 1987 by the Board of Trustees of the
2615  *	University of Illinois, and by the author Dirk Grunwald.
2616  *
2617  *	This program may be freely copied, as long as this copyright
2618  *	message remaines affixed. It may not be sold, although it may
2619  *	be distributed with other software which is sold. If the
2620  *	software is distributed, the source code must be made available.
2621  *
2622  *	No warranty, expressed or implied, is given with this software.
2623  *	It is presented in the hope that it will prove useful.
2624  *
2625  *	Hacked in ignorance and desperation by jonah@db.toronto.edu
2626  */
2627 
2628 /*
2629  *      The code to handle the \specials generated by tpic was modified
2630  *      by Dirk Grunwald using the code Tim Morgan at Univ. of Calif, Irvine
2631  *      wrote for TeXsun.
2632  */
2633 
2634 static char *
endofcommand(char * cp)2635 endofcommand(char *cp)
2636 {
2637     while (isspace((int)*cp))
2638 	++cp;
2639     if (*cp != '=')
2640 	return NULL;
2641     do
2642 	++cp;
2643     while (isspace((int)*cp));
2644     return cp;
2645 }
2646 
2647 #define	CMD(x, y)	((x) << 8 | (y))
2648 
2649 void
applicationDoSpecial(char * cp,size_t len)2650 applicationDoSpecial(char *cp, size_t len)
2651 {
2652     char *p;
2653 
2654     if (globals.debug & DBG_DVI)
2655 	printf("          `%s'\n", cp);
2656 
2657     while (isspace((int)*cp))
2658 	++cp;
2659 
2660     /* Ignore initial "xdvi:" */
2661     if (memcmp(cp, "xdvi:", 5) == 0) {
2662 	cp += 5;
2663 	while (isspace((int)*cp))
2664 	    ++cp;
2665     }
2666 
2667     /* PostScript specials */
2668 #ifdef MAGICK
2669     if (resource.useMAGICK) {
2670 	if (Magick_parse_special(cp))
2671 	    return;
2672     }
2673 #endif
2674 
2675     if (*cp == '"') {
2676 	quote_special(cp);
2677 	return;
2678     }
2679     if (memicmp(cp, "ps:", 3) == 0) {
2680 	cp += 3;
2681 	psfig_special(cp);
2682 	/* check for hdvips hyperlinks */
2683 	if (memicmp(cp, "sdict begin ", strlen("sdict begin ")) == 0) {
2684 	    static Boolean warned_hypertex_too_old = False;
2685 	    char *match = NULL;
2686 	    if (warned_hypertex_too_old) /* don't continue evaluating links in this case */
2687 		return;
2688 	    cp += strlen("sdict begin ");
2689 	    if (memcmp(cp, "H.S", 3) == 0
2690 		|| memcmp(cp, "H.R", 3) == 0
2691 		|| memcmp(cp, "H.B", 3) == 0
2692 		/* following 2 conditions could be more restrictive: between `begin' and H.A/H.L,
2693 		   there should be a single number (baselineskip in pt) */
2694 		|| (match = strstr(cp, "H.A")) != NULL
2695 		|| (match = strstr(cp, "H.L")) != NULL
2696 		|| (match = strstr(cp, "/Action")) != NULL
2697 		|| (match = strstr(cp, "/Link")) != NULL
2698 		|| (match = strstr(cp, "/View")) != NULL) {
2699 		if (match != NULL)
2700 		    htex_do_special(match, len - 3 - (match - cp) - strlen("sdict begin "));
2701 		else
2702 		    htex_do_special(cp, len - strlen("sdict begin "));
2703 	    }
2704 	    else if (!warned_hypertex_too_old && strstr(cp, "HyperStart") != NULL) {
2705 		popup_message(globals.widgets.top_level,
2706 			      MSG_WARN, NULL,
2707 			      "This DVI was created with a too old version of the `dvips' hyperref driver - "
2708 			      "disabling hyperlinks.\n"
2709 			      "To fix this, you should either upgrade to a current version of hyperref "
2710 			      "(see http://www.tug.org/applications/hyperref/), "
2711 			      "or use the `hypertex' package option, like this:\n\\usepackage[hypertex]{hyperref}\n"
2712 			      "(Be aware though that this option won't work for PS->PDF conversion!)");
2713 		warned_hypertex_too_old = True;
2714 	    }
2715 	}
2716 	else {
2717 	    /* When not ignoring SDict entries, the distiller and pagecolor
2718 	       code in lshort.dvi from CTAN:info/lshort/russian/lshrtdvi.zip
2719 	       causes a failed assertion for 'color_bot_size > 0' in dvi-draw.c;
2720 	       there's something wrong with the parsing order/event handling here
2721 	       (see bug #856547).
2722 	       But we also don't want those entries to trigger erasepage_gs(), so
2723 	       it's correct to ignore them here.
2724 	    */
2725 #if PS_GS && GS_PIXMAP_CLEARING_HACK
2726 	    had_ps_specials = True;
2727 #endif
2728 	}
2729 	return;
2730     }
2731     if (memicmp(cp, "psfile", 6) == 0 && (p = endofcommand(cp + 6)) != NULL) {
2732     	epsf_special(p);
2733 #if PS_GS && GS_PIXMAP_CLEARING_HACK
2734 	had_ps_specials = True;
2735 #endif
2736 	return;
2737     }
2738     if (memicmp(cp, "html:", 5) == 0) {
2739 	htex_do_special(cp + 5, len - 5);
2740 	return;
2741     }
2742 
2743 #if COLOR
2744     if (memicmp(cp, "color ", 6) == 0) {
2745 	/* 	fprintf(stderr, "------------- color special\n"); */
2746 	if (resource.use_color)
2747 	    color_special(cp + 6);
2748 	return;
2749     }
2750 #endif
2751 
2752     /* these should have been scanned */
2753     if (*cp == '!'
2754 	|| (memicmp(cp, "header", 6) == 0 && endofcommand(cp + 6) != NULL)) {
2755 #ifdef PS
2756 	if (resource.postscript != 0 && scanned_page_reset >= 0) {
2757 	    /* turn on scanning and redraw the page */
2758 	    scanned_page = scanned_page_ps = scanned_page_reset = -1;
2759 # if COLOR
2760 	    scanned_page_color = scanned_page;
2761 # endif
2762 	    globals.ev.flags |= EV_NEWPAGE;		/* force a redraw */
2763 	    longjmp(globals.ev.canit, 1);
2764 	}
2765 #endif /* PS */
2766 	return;
2767     }
2768 
2769     if (memicmp(cp, "background ", 11) == 0) {
2770 #if COLOR
2771 	if (resource.use_color && scanned_page_reset >= 0) {
2772 	    /* turn on scanning and redraw the page */
2773 	    scanned_page = scanned_page_color = scanned_page_reset = -1;
2774 # if PS
2775 	    scanned_page_ps = scanned_page;
2776 # endif
2777 	    /* 	    fprintf(stderr, "forcing redraw!\n"); */
2778 	    globals.ev.flags |= EV_NEWPAGE;		/* force a redraw */
2779 	    longjmp(globals.ev.canit, 1);
2780 	}
2781 #endif /* COLOR */
2782 	return;
2783     }
2784 
2785     if (memcmp(cp, "papersize", 9) == 0 && endofcommand(cp + 9) != NULL) {
2786 	m_have_papersize_special = True;
2787 	if (scanned_page_reset >= 0) {
2788 	    /* turn on scanning and redraw the page */
2789 	    scanned_page = scanned_page_reset = -1;
2790 #if PS
2791 	    scanned_page_ps = scanned_page;
2792 #endif
2793 #if COLOR
2794 	    scanned_page_color = scanned_page;
2795 #endif
2796 	    globals.ev.flags |= EV_NEWPAGE; /* force a redraw */
2797 	    longjmp(globals.ev.canit, 1);
2798 	}
2799 	return;
2800     }
2801 
2802     /* tpic specials */
2803 
2804     if (*cp >= 'a' && *cp <= 'z' && cp[1] >= 'a' && cp[1] <= 'z' &&
2805 	(isspace((int)cp[2]) || cp[2] == '\0')) {
2806 	switch (CMD(*cp, cp[1])) {
2807 	case CMD('p', 'n'):
2808 	    set_pen_size(cp + 2);
2809 	    return;
2810 	case CMD('f', 'p'):
2811 	    flush_path();
2812 	    return;
2813 	case CMD('d', 'a'):
2814 	    flush_dashed(cp + 2, False);
2815 	    return;
2816 	case CMD('d', 't'):
2817 	    flush_dashed(cp + 2, True);
2818 	    return;
2819 	case CMD('p', 'a'):
2820 	    add_path(cp + 2);
2821 	    return;
2822 	case CMD('a', 'r'):
2823 	    arc(cp + 2, False);
2824 	    return;
2825 	case CMD('i', 'a'):
2826 	    arc(cp + 2, True);
2827 	    return;
2828 	case CMD('s', 'p'):
2829 	    flush_spline();
2830 	    return;
2831 	case CMD('s', 'h'):
2832 	    shade_last();
2833 	    return;
2834 	case CMD('w', 'h'):
2835 	    whiten_last();
2836 	    return;
2837 	case CMD('b', 'k'):
2838 	    blacken_last();
2839 	    return;
2840 	case CMD('h', 'e'):
2841 	    psh_header(cp); return;
2842 	case CMD('i', 'p'):	/* throw away the path -- jansteen */
2843 	    path_len = 0;
2844 	    return;
2845 	}
2846     }
2847 
2848     if (memcmp(cp, "src:", 4) == 0) {
2849 	have_src_specials = True;
2850     }
2851     else if (globals.warn_spec_now)
2852 	XDVI_WARNING((stderr, "Special \"%s\" not implemented.", cp));
2853 }
2854 
2855 #undef	CMD
2856 
2857 
2858 Boolean
scan_special(char * cp,int cp_len,void * data)2859 scan_special(char *cp, int cp_len, void *data)
2860 {
2861     char *p;
2862     Boolean dummy_ret = True; /* currently unused;
2863 				 FIXME: use return value to avoid redundant calls (instead of longjmp()) */
2864     UNUSED(cp_len); /* TODO: could probably utilize this in call to htex_prescan_special() */
2865     ASSERT(data != NULL, "Must pass a data pointer when using HTEX");
2866 
2867     if (globals.debug & DBG_PS)
2868 	printf("Scanning special `%s'.\n", cp);
2869 
2870     while (isspace((int)*cp))
2871 	++cp;
2872 
2873     /* Ignore initial "xdvi:" */
2874     if (memcmp(cp, "xdvi:", 5) == 0) {
2875 	cp += 5;
2876 	while (isspace((int)*cp))
2877 	    ++cp;
2878     }
2879 
2880     if (memicmp(cp, "ps:", 3) == 0) {
2881 	Boolean found;
2882 	cp += 3;
2883 	/* check for hdvips hyperlinks */
2884 	if (memicmp(cp, "sdict begin ", strlen("sdict begin ")) == 0) {
2885 	    static Boolean hypertex_too_old = False;
2886 	    char *match = NULL;
2887 	    cp += strlen("sdict begin ");
2888 
2889 	    if (strstr(cp, "HyperStart") != NULL)
2890 		hypertex_too_old = True;
2891 	    if (hypertex_too_old)
2892 		return False;
2893 
2894 	    if (memcmp(cp, "H.S", 3) == 0
2895 		|| memcmp(cp, "H.R", 3) == 0
2896 		|| memcmp(cp, "H.B", 3) == 0
2897 		/* following 2 conditions could be more restrictive: between `begin' and H.A/H.L,
2898 		   there should be a single number (baselineskip in pt) */
2899 		|| (match = strstr(cp, "H.A")) != NULL
2900 		|| (match = strstr(cp, "H.L")) != NULL
2901 		|| (match = strstr(cp, "/Action")) != NULL
2902 		|| (match = strstr(cp, "/Link")) != NULL
2903 		|| (match = strstr(cp, "/View")) != NULL) {
2904 		if (match != NULL)
2905 		    found = htex_prescan_special(match, cp_len, data);
2906 		else
2907 		    found = htex_prescan_special(cp, cp_len, data);
2908 	    }
2909 	}
2910 
2911     }
2912     else if (memicmp(cp, "html:", strlen("html:")) == 0) {
2913 	Boolean found;
2914 	size_t offset = strlen("html:");
2915 	found = htex_prescan_special(cp + offset, cp_len - offset, data);
2916 	/* if searching for a specific string, return as soon as it's found - not yet implemented */
2917 #if 0
2918  	if (my_data != NULL && my_data->scan_type == HTEX_ANCHOR_STRING && found) {
2919  	    return True;
2920  	}
2921 #endif
2922     }
2923     /* do the following only if not searching for an anchor string */
2924     if (((struct htex_prescan_data *)data)->scan_type != HTEX_ANCHOR_STRING) {
2925 #if PS
2926 # if COLOR
2927 	if (scanned_page_ps <= scanned_page)
2928 # endif
2929 	{
2930 	    if (*cp == '!') {
2931 		scan_bang(cp);
2932 		return dummy_ret;
2933 	    }
2934 	    else if (memicmp(cp, "header", 6) == 0 && (p = endofcommand(cp + 6)) != NULL) {
2935 		scan_header(p);
2936 		return dummy_ret;
2937 	    }
2938 	}
2939 #endif /* PS */
2940 
2941 #if COLOR
2942 # if PS
2943 	if (scanned_page_color <= scanned_page)
2944 # endif
2945 	{
2946 	    if (memicmp(cp, "background ", 11) == 0) {
2947 		scan_bg_color(cp);
2948 		return dummy_ret;
2949 	    }
2950 	    else if (memicmp(cp, "color ", 6) == 0) {
2951 		scan_color(cp);
2952 		return dummy_ret;
2953 	    }
2954 	}
2955 #endif /* COLOR */
2956 	if (memcmp(cp, "papersize", 9) == 0 && (p = endofcommand(cp + 9)) != NULL) {
2957 	    m_have_papersize_special = True;
2958 	    scan_papersize(p);
2959 	    return dummy_ret;
2960 	}
2961     }
2962     return dummy_ret;
2963 }
2964 
2965 
2966 /* #define	xspell_conv(n)	spell_conv0(n, current_dimconv) */
2967 #define	xpixel_conv(x)	((int) ((x) >> 16))
2968 #define	G_PXL_H		xpixel_conv(currinf.data.dvi_h)
2969 
2970 /* cp is not const, because of endofcommand(). */
2971 void
geom_do_special(struct scan_info * info,char * cp,double current_dimconv)2972 geom_do_special(struct scan_info *info, char *cp, double current_dimconv)
2973 {
2974     const char *p;
2975     struct geom_info *g_info = (struct geom_info *)info->data;
2976 
2977     UNUSED(current_dimconv);
2978 
2979     while (isspace((int)*cp))
2980 	++cp;
2981 
2982     /* Ignore initial "xdvi:" */
2983     if (memcmp(cp, "xdvi:", 5) == 0) {
2984 	cp += 5;
2985 	while (isspace((int)*cp))
2986 	    ++cp;
2987     }
2988 
2989     if (memicmp(cp, "psfile", 6) == 0 && (p = endofcommand(cp + 6)) != NULL) {
2990 	/* compute epsf bounding box */
2991 	char c;
2992 	int flags = 0;
2993 	double keyval[6];
2994 
2995 	c = *p;
2996 	if (c == '\'' || c == '"') {
2997 	    do
2998 		++p;
2999 	    while (*p != '\0' && *p != c);
3000 	}
3001 	else
3002 	    while (*p != '\0' && !isspace((int)*p))
3003 		++p;
3004 	while (isspace((int)*p))
3005 	    ++p;
3006 	while (*p != '\0') {
3007 	    const char *p1 = p;
3008 	    size_t keyno;
3009 
3010 	    while (*p1 != '=' && !isspace((int)*p1) && *p1 != '\0')
3011 		++p1;
3012 	    for (keyno = 0; keyno < NKEYS; ++keyno) {
3013 		if (memcmp(p, keytab[keyno], p1 - p) == 0) {
3014 		    if (keyno >= N_ARGLESS_KEYS) {
3015 			while (isspace((int)*p1))
3016 			    ++p1;
3017 			if (*p1 == '=') {
3018 			    ++p1;
3019 			    while (isspace((int)*p1))
3020 				++p1;
3021 			}
3022 			if (keyno < N_ARGLESS_KEYS + 6) {
3023 			    keyval[keyno - N_ARGLESS_KEYS] = atof(p1);
3024 			    flags |= (1 << (keyno - N_ARGLESS_KEYS));
3025 			}
3026 			while (!isspace((int)*p1) && *p1 != '\0')
3027 			    ++p1;
3028 		    }
3029 		    break;
3030 		}
3031 	    }
3032 	    p = p1;
3033 	    while (!isspace((int)*p) && *p != '\0')
3034 		++p;
3035 	    while (isspace((int)*p))
3036 		++p;
3037 	}
3038 
3039 	if ((flags & 0x30) == 0x30 || ((flags & 0x30) && (flags & 0xf) == 0xf)) {
3040 	    long x = G_PXL_H;
3041 	    long y = PXL_V;
3042 	    long bbox_w;
3043 	    long bbox_h;
3044 
3045 	    bbox_w = 0.1 * ((flags & 0x10) ? KEY_RWI
3046 			    : KEY_RHI * (KEY_URX - KEY_LLX) / (KEY_URY -
3047 							       KEY_LLY)) *
3048 		dimconv + 0.5;
3049 	    bbox_h =
3050 		0.1 * ((flags & 0x20) ? KEY_RHI : KEY_RWI *
3051 		       (KEY_URY - KEY_LLY) / (KEY_URX - KEY_LLX))
3052 		* dimconv + 0.5;
3053 
3054 	    g_info->geom_box(info, x, y - bbox_h, x + bbox_w, y);
3055 	}
3056     }
3057     else if (memicmp(cp, "ps::[begin]", 11) == 0) {
3058 	/* compute psfig bounding box */
3059 	long bbox_w, bbox_h;
3060 
3061 	if (sscanf(cp + 11, "%ld %ld\n", &bbox_w, &bbox_h) >= 2) {
3062 	    long x = G_PXL_H;
3063 	    long y = PXL_V;
3064 
3065 	    bbox_w = xpixel_conv(spell_conv(bbox_w));
3066 	    bbox_h = xpixel_conv(spell_conv(bbox_h));
3067 
3068 	    g_info->geom_box(info, x, y, x + bbox_w, y + bbox_h);
3069 	}
3070     }
3071 }
3072