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;