2 /* Copyright (c) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 by Arkkra Enterprises */
3 /* All rights reserved */
6 /* Program to display ghostscript bitmap output on screen.
7 * Works either on an AT386 or linux console
8 * or under X-windows from an xterm window or under DOS (with Watcom C).
9 * It could be extended to other
10 * TERM types by writing appropriate functions and adding to the Config
11 * array. Use the existing functions as models.
12 * Passes all arguments on to Mup.
15 /* for compiling under UNIX on x86, try
16 * cc -DSYSV -D_USHORT_H -s -O -o mupdisp *.c -lX11 -lnsl_i
17 * If you compile without XWINDOW, you can get by with just:
18 * cc -s -O -o mupdisp *.c
20 * For Watcom C under DOS,
21 * Put the following 4 lines in a batch script and execute it
23 * for %%f in (*.c) do wcc386 %%f -dDEBUG -on -4r
24 * echo NAME mup > mup.lnk
25 * for %%f in (*.obj) do echo FIL %%f >> mup.lnk
26 * wlink sys dos4g op st=32k @mup.lnk
28 * Note that all the mupdisp *.c and *.h files should be in the
29 * current directory, but there must be no other *.c files there, and no *.obj
30 * files (except that *.obj files from a previous attempt would be okay).
33 * cc -L/usr/X11/lib -o mupdisp *.c -lvga -lX11 -lm
34 * Depending on the versions of libraries you have,
35 * you might not really need the -lm, but it doesn't hurt.
36 * If you don't have libvga on your system, and only intend to use the X11
37 * mode, not the console mode, you can use
38 * cc -L/usr/X11/lib -o mupdisp -DNO_VGA_LIB *.c -lX11
40 * Other environments may require different options
42 * Note that using mupdisp in non-X-window mode on Linux requires that it
43 * can write to the console device. To allow this, make mupdisp setuid to root:
55 struct CONFIG
*Conf_info_p
; /* the info for the actual $TERM */
56 struct Pginfo
*Pagehead
= (struct Pginfo
*) 0; /* all page's bitmap info */
57 struct Pginfo
*Pagetail
= (struct Pginfo
*) 0; /* where to add to list */
58 struct Pginfo
*Currpage_p
; /* current page */
59 int Pagenum
; /* current page number */
60 long Beginprolog
; /* where in PostScript file the prolog begins */
61 long Endprolog
; /* where in PostScript file the prolog ends */
62 long Begin_offset
; /* offset in file where current page begins */
63 int Pagenum
; /* current page number */
64 int Psfile
; /* PostScript temp file, file descriptor */
65 FILE *PS_file
; /* PostScript temp file */
66 int Fullbitmaps
; /* temp file of full page bitmaps */
67 int Partbitmaps
; /* temp file for bitmaps for scrollable pages */
68 int Nulldev
; /* /dev/null */
70 char Fullfile
[] = "mupdispfXXXXXX"; /* name of gs output tmp file, full page */
71 char Partfile
[] = "mupdisppXXXXXX"; /* name of gs output tmp file, partial page */
72 char Mupfile
[] = "mupdispmXXXXXX"; /* Mup output temp file */
74 char Fullfile
[L_tmpnam
]; /* name of gs output tmp file, full page */
75 char Partfile
[L_tmpnam
]; /* name of gs output tmp file, partial page */
76 char Mupfile
[L_tmpnam
]; /* Mup output temp file */
78 char **Argv
; /* global version of argv */
79 int Argc
; /* global version of argc */
80 int Fullpgmode
= DFLT_MODE
; /* full page or partial page mode, YES if full */
81 char *Exit_errmsg
= (char *) 0; /* error message to print upon exit, if any */
82 char *Gs_errfile
= "mupdispg.err"; /* ghostscript error file */
83 int Bits_per_line
= 612; /* pixels per line */
84 int Bytes_per_line
= 77; /* pixels per line divided by 8 rounded up */
85 int Lines_per_page
= 792; /* vertical pixels */
86 char *Version
= "5.3";
88 /* misc function declarations */
89 static void parsePS
P((FILE *file
));
90 static void save_endpage
P((void));
94 /* main function. Run Mup, run ghostscript, then do user interface */
112 /* arrange to clean up temp files. Note that the user interface
113 * will probably have its own cleanup */
114 for (n
= 0; n
< NSIG
; n
++) {
115 if (n
!= SIGKILL
&& n
!= SIGCLD
&& n
!= SIGWINCH
) {
116 signal(n
, generalcleanup
);
119 signal(SIGWINCH
, SIG_IGN
);
122 if (getenv("MUPQUIET") == 0) {
123 fprintf(stderr
, "Mupdisp - Version %s\n", Version
);
126 /* There are several Mup options we want to turn off, because
127 * they don't produce PostScript output. This magic environment
128 * variable tells Mup to make the listed options illegal. */
129 putenv("MUPDELOP=CEfFmMrv");
131 /* make a temp file for PostScript */
132 Psfile
= create_tmpfile(Mupfile
);
134 chown(Mupfile
, getuid(), getgid());
137 /* run Mup with given arguments */
139 if ((PS_file
= fopen(Mupfile
, "r")) == (FILE *) 0) {
140 fprintf(stderr
, "can't open Mup output file\n");
144 /* find where pages begin in PostScript file */
148 if ((Nulldev
= open("/dev/null", O_WRONLY
, 0)) < 0) {
149 fprintf(stderr
, "can't open /dev/null\n");
154 /* if environment variable MUPDISPMODE is set, use the small full page
155 * mode as the default */
156 if (getenv("MUPDISPMODE") != (char *) 0) {
160 /* do user interface */
166 /* given a page number, set Currpage_p to the info for that page
167 * and return YES. Else leave as is and return NO */
172 int pgnum
; /* which page */
175 struct Pginfo
*pginfo_p
;
178 /* use -1 as special page number to mean first on list */
180 Currpage_p
= Pagehead
;
181 return(Currpage_p
== (struct Pginfo
*) 0 ? NO
: YES
);
184 /* search list for requested page */
185 for (pginfo_p
= Pagehead
; pginfo_p
!= (struct Pginfo
*) 0;
186 pginfo_p
= pginfo_p
->next
) {
187 if (pginfo_p
->pagenum
== pgnum
) {
189 Currpage_p
= pginfo_p
;
194 /* page not on list */
199 /* set up and call appropriate user interface routine */
205 /* init, draw first page, do user interface */
206 ( *(Conf_info_p
->setup
) ) ();
208 ( *(Conf_info_p
->draw
) ) (0, Fullpgmode
);
209 ( *(Conf_info_p
->user_interf
) ) ();
210 ( *(Conf_info_p
->cleanup
) ) (0);
214 /* check if scrolling by specified distance from line would leave the
215 * whole screen area within the page. If so, redraw the screen with that
216 * much of a scroll and return the new line number. Otherwise just return
217 * the original line number.
221 scroll(line
, distance
)
227 int newlineno
; /* line number after scrolling */
228 int pagebotline
; /* bottom line of page to display */
231 newlineno
= line
+ (int)(distance
* Conf_info_p
->adjust
);
232 if (newlineno
< 0 && line
> 0) {
235 pagebotline
= Conf_info_p
->adjust
* LINES_PER_PAGE
- 1;
236 if (newlineno
+ Conf_info_p
->vlines
- 1 > pagebotline
) {
237 newlineno
= pagebotline
- Conf_info_p
->vlines
+ 1;
240 if ( (newlineno
!= line
) && (newlineno
>= 0)
241 && (newlineno
+ Conf_info_p
->vlines
- 1
243 ( *(Conf_info_p
->draw
) ) (newlineno
, Fullpgmode
);
246 if (getenv("MUPQUIET") == (char *) 0) {
247 /* some people don't want to be beeped when hitting end of
248 * page, so only exclaim if quiet flag is off */
249 ( *(Conf_info_p
->error
) ) ("can't scroll any farther");
255 /* general cleanup function to delete temp files. All other cleanup functions
256 * should call this function last, since it exits */
259 generalcleanup(status
)
279 /* if there is an error message to print, do so */
280 if (Exit_errmsg
!= (char *) 0) {
281 fprintf(stderr
, Exit_errmsg
);
283 /* if there is a ghostscript error file, print it */
288 if ((f
= fopen(Gs_errfile
, "r")) != NULL
) {
289 while (fgets(buff
, BUFSIZ
, f
)) {
290 fprintf(stderr
, "%s", buff
);
301 /* Create a temporary file. It is passed an array in which the filename is
302 * stored. Newer versions of gcc claim that tmpnam is dangerous and that you
303 * should use mkstemp instead. But some other systems say using mkstemp
304 * is discouraged. Being unable to please everyone, we use mkstemp on linux
305 * and tmpnam elsewhere. So on linux, the argument should be a character
306 * array initialized to end with "XXXXXX" as per what mkstemp wants,
307 * On other systems it should be a character array at least L_tmpnam bytes long.
308 * Returns the file descriptor, opened read/write. */
311 create_tmpfile(char *tmpfname
)
318 if ((fd
= mkstemp(tmpfname
)) < 0) {
319 fprintf(stderr
, "can't create temp file\n");
323 /* create the file name */
324 if (tmpnam(tmpfname
) == (char *) 0) {
325 fprintf(stderr
, "can't create temp file\n");
331 if ((fd
= open(tmpfname
, O_RDWR
| O_CREAT
| O_TRUNC
| O_BINARY
, 0644)) < 0) {
333 if ((fd
= open(tmpfname
, O_RDWR
| O_CREAT
| O_TRUNC
, 0644)) < 0) {
335 fprintf(stderr
, "can't open temp file\n");
345 * Read a PostScript file and save pointer to where the description of
347 * Go through input file. Skip to %%EndProlog. Then for each page,
348 * save file offset where page begins.
349 * This function know about how Mup formats parts of its output. If Mup
350 * ever changes to add extra white space or comments or something in
351 * the specific lines this function cares about, this function will
352 * have to change too.
362 long linebegin
; /* where in file current line begins */
365 /* read whole file */
366 linebegin
= ftell(file
);
367 while (fgets(buff
, BUFSIZ
, file
)) {
369 if (strncmp(buff
, "%!PS-Adobe", 10) == 0) {
370 /* remember where prolog begins. Because of how DOS
371 * deals with cr/nl we can't just back up the length
372 * of the string from the current ftell position and
373 * be assured of being at the %, so that's why we
374 * save the current beginning of each line and then
375 * assign it here. Normally, the %!Adobe line will
376 * be the first line anyway and we wouldn't need this
377 * code at all, but some versions of dos4gw write stuff
378 * to stdout, which ends up in the PostScript file,
379 * which then confuses us, so we want to throw that
380 * away if it is present. */
381 Beginprolog
= linebegin
;
383 else if (strncmp(buff
, "%%BoundingBox:", 14) == 0) {
386 /* adjust for page size */
387 if (sscanf(buff
+ 14, "%*d %*d %d %d", &x
, &y
) == 2) {
388 if ( (x
<= MAX_BYTES_PER_LINE
* 8)
389 && (x
> 0) && (y
> 0) &&
390 (y
<= MAX_LINES_PER_PAGE
) ) {
391 get_paper_size(x
, y
);
394 fprintf(stderr
, "Page is too big to display completely\n");
398 else if (strncmp(buff
, "%%EndProlog", 11) == 0) {
399 /* remember where prolog ends */
400 Endprolog
= ftell(PS_file
);
403 else if (strncmp(buff
, "%%Page: ", 8) == 0) {
404 Pagenum
= atoi(buff
+ 8);
406 /* Page followed by save, save info about page */
407 fgets(buff
, BUFSIZ
, file
);
408 if (strncmp(buff
, "save", 4) == 0) {
409 Begin_offset
= linebegin
;
413 else if (strncmp(buff
, "showpage", 8) == 0) {
414 /* showpage followed by restore, save info about
416 fgets(buff
, BUFSIZ
, file
);
417 if (strncmp(buff
, "restore", 7) == 0) {
421 linebegin
= ftell(file
);
424 /* file was not valid -- something went wrong */
425 if (Endprolog
== 0) {
431 /* at the end of a PostScript page, save info about it */
437 struct Pginfo
*new_p
; /* newly allocated struct for info about page */
438 static int seqnum
= 0; /* sequential count of pages */
441 /* allocate space to save info about page */
442 if ((new_p
= (struct Pginfo
*) malloc (sizeof(struct Pginfo
)))
443 == (struct Pginfo
*) 0) {
444 fprintf(stderr
, "malloc failed\n");
449 new_p
->pagenum
= Pagenum
;
450 new_p
->seqnum
= seqnum
++;
451 new_p
->begin
= Begin_offset
;
452 new_p
->end
= ftell(PS_file
);
453 new_p
->prev
= Pagetail
;
454 new_p
->next
= (struct Pginfo
*) 0;
457 if (Pagehead
== (struct Pginfo
*) 0) {
460 if (Pagetail
!= (struct Pginfo
*) 0) {
461 Pagetail
->next
= new_p
;