chiark / gitweb /
Import upstream version 5.3.
[mup] / mup / mupdisp / mupdisp.c
1
2 /* Copyright (c) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 by Arkkra Enterprises */
3 /* All rights reserved */
4
5
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.
13  */
14
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
19  *
20  * For Watcom C under DOS,
21  * Put the following 4 lines in a batch script and execute it
22  * 
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
27  * 
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).
31  *
32  * For Linux,
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
39  *
40  * Other environments may require different options
41  *
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:
44  *      chown root mupdisp
45  *      chmod 4755 mupdisp
46  */
47
48
49 #include "mupdisp.h"
50 #ifdef __WATCOMC__
51 #include <io.h>
52 #endif
53
54
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 */
69 #ifdef linux
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 */
73 #else
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 */
77 #endif
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";
87
88 /* misc function declarations */
89 static void parsePS P((FILE *file));
90 static void save_endpage P((void));
91
92 \f
93
94 /* main function. Run Mup, run ghostscript, then do user interface */
95
96 int
97 main(argc, argv)
98
99 int argc;
100 char **argv;
101
102 {
103         int n;
104
105
106         /* initialize */
107         Argv = argv;
108         Argc = argc;
109         init();
110
111 #ifdef unix
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);
117                 }
118         }
119         signal(SIGWINCH, SIG_IGN);
120 #endif
121
122         if (getenv("MUPQUIET") == 0) {
123                 fprintf(stderr, "Mupdisp - Version %s\n", Version);
124         }
125
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");
130
131         /* make a temp file for PostScript */
132         Psfile = create_tmpfile(Mupfile);
133 #ifdef linux
134         chown(Mupfile, getuid(), getgid());
135 #endif
136
137         /* run Mup with given arguments */
138         run_mup(Argv);
139         if ((PS_file = fopen(Mupfile, "r")) == (FILE *) 0) {
140                 fprintf(stderr, "can't open Mup output file\n");
141                 generalcleanup(1);
142         }
143
144         /* find where pages begin in PostScript file */
145         parsePS(PS_file);
146
147 #ifdef unix
148         if ((Nulldev = open("/dev/null", O_WRONLY, 0)) < 0) {
149                 fprintf(stderr, "can't open /dev/null\n");
150                 generalcleanup(1);
151         }
152 #endif
153
154         /* if environment variable MUPDISPMODE is set, use the small full page
155          * mode as the default */
156         if (getenv("MUPDISPMODE") != (char *) 0) {
157                 Fullpgmode = YES;
158         }
159
160         /* do user interface */
161         user_interf();
162         return(0);
163 }
164 \f
165
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 */
168
169 int
170 getpginfo(pgnum)
171
172 int pgnum;      /* which page */
173
174 {
175         struct Pginfo *pginfo_p;
176
177
178         /* use -1 as special page number to mean first on list */
179         if (pgnum == -1) {
180                 Currpage_p = Pagehead;
181                 return(Currpage_p == (struct Pginfo *) 0 ? NO : YES);
182         }
183
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) {
188                         /* found it */
189                         Currpage_p = pginfo_p;
190                         return(YES);
191                 }
192         }
193
194         /* page not on list */
195         return (NO);
196 }
197 \f
198
199 /* set up and call appropriate user interface routine */
200
201 void
202 user_interf()
203
204 {
205         /* init, draw first page, do user interface */
206         ( *(Conf_info_p->setup) )  ();
207         getpginfo(-1);
208         ( *(Conf_info_p->draw) )  (0, Fullpgmode);
209         ( *(Conf_info_p->user_interf) )  ();
210         ( *(Conf_info_p->cleanup) )  (0);
211 }
212 \f
213
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.
218  */
219
220 int
221 scroll(line, distance)
222
223 int line;
224 int distance;
225
226 {
227         int newlineno;          /* line number after scrolling */
228         int pagebotline;        /* bottom line of page to display */
229
230
231         newlineno = line + (int)(distance * Conf_info_p->adjust);
232         if (newlineno < 0 && line > 0) {
233                 newlineno = 0;
234         }
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;
238         }
239         
240         if ( (newlineno != line) && (newlineno >= 0)
241                         && (newlineno + Conf_info_p->vlines - 1
242                         <= pagebotline) ) {
243                 ( *(Conf_info_p->draw) )  (newlineno, Fullpgmode);
244                 return(newlineno);
245         }
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");
250         }
251         return(line);
252 }
253 \f
254
255 /* general cleanup function to delete temp files. All other cleanup functions
256  * should call this function last, since it exits */
257
258 void
259 generalcleanup(status)
260
261 int status;
262
263 {
264         if (Mupfile[0]) {
265                 close(Psfile);
266                 if (PS_file != 0) {
267                         fclose(PS_file);
268                 }
269                 unlink(Mupfile);
270         }
271         if (Fullfile[0]) {
272                 close(Fullbitmaps);
273                 unlink(Fullfile);
274         }
275         if (Partfile[0]) {
276                 close(Partbitmaps);
277                 unlink(Partfile);
278         }
279         /* if there is an error message to print, do so */
280         if (Exit_errmsg != (char *) 0) {
281                 fprintf(stderr, Exit_errmsg);
282                 
283                 /* if there is a ghostscript error file, print it */
284                 if (status != 0) {
285                         FILE *f;
286                         char buff[BUFSIZ];
287
288                         if ((f = fopen(Gs_errfile, "r")) != NULL) {
289                                 while (fgets(buff, BUFSIZ, f)) {
290                                         fprintf(stderr, "%s", buff);
291                                 }
292                                 fclose(f);
293                                 unlink(Gs_errfile);
294                         }
295                 }
296         }
297         exit(status);
298 }
299 \f
300
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. */
309
310 int
311 create_tmpfile(char *tmpfname)
312
313 {
314         int fd;
315
316
317 #ifdef linux
318         if ((fd = mkstemp(tmpfname)) < 0) {
319                 fprintf(stderr, "can't create temp file\n");
320                 generalcleanup(1);
321         }
322 #else
323         /* create the file name */
324         if (tmpnam(tmpfname) == (char *) 0) {
325                 fprintf(stderr, "can't create temp file\n");
326                 generalcleanup(1);
327         }
328
329         /* open the file */
330 #ifdef O_BINARY
331         if ((fd = open(tmpfname, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0644)) < 0) {
332 #else        
333         if ((fd = open(tmpfname, O_RDWR | O_CREAT | O_TRUNC, 0644)) < 0) {
334 #endif
335                 fprintf(stderr, "can't open temp file\n");
336                 generalcleanup(1);
337         }
338 #endif
339
340         return(fd);
341 }
342 \f
343
344 /* 
345  * Read a PostScript file and save pointer to where the description of
346  * each page begins.
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.
353  */
354
355 static void
356 parsePS(file)
357
358 FILE *file;
359
360 {
361         char buff[BUFSIZ];
362         long linebegin;         /* where in file current line begins */
363
364
365         /* read whole file */
366         linebegin = ftell(file);
367         while (fgets(buff, BUFSIZ, file)) {
368
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;
382                 }
383                 else if (strncmp(buff, "%%BoundingBox:", 14) == 0) {
384                         int x, y;
385
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);
392                                 }
393                                 else {
394                                         fprintf(stderr, "Page is too big to display completely\n");
395                                 }
396                         }
397                 }
398                 else if (strncmp(buff, "%%EndProlog", 11) == 0) {
399                         /* remember where prolog ends */
400                         Endprolog = ftell(PS_file);
401                 }
402
403                 else if (strncmp(buff, "%%Page: ", 8) == 0) {
404                         Pagenum = atoi(buff + 8);
405                 
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;
410                         }
411                 }
412
413                 else if (strncmp(buff, "showpage", 8) == 0) {
414                         /* showpage followed by restore, save info about
415                          * where page ends */
416                         fgets(buff, BUFSIZ, file);
417                         if (strncmp(buff, "restore", 7) == 0) {
418                                 save_endpage();
419                         }
420                 }
421                 linebegin = ftell(file);
422         }
423         
424         /* file was not valid -- something went wrong */
425         if (Endprolog == 0) {
426                 generalcleanup(1);
427         }
428 }
429 \f
430
431 /* at the end of a PostScript page, save info about it */
432
433 static void
434 save_endpage()
435
436 {
437         struct Pginfo *new_p;   /* newly allocated struct for info about page */
438         static int seqnum = 0;      /* sequential count of pages */
439
440
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");
445                 generalcleanup(1);
446         }
447
448         /* fill in info */
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;
455
456         /* link onto list */
457         if (Pagehead == (struct Pginfo *) 0) {
458                 Pagehead = new_p;
459         }
460         if (Pagetail != (struct Pginfo *) 0) {
461                 Pagetail->next = new_p;
462         }
463         Pagetail = new_p;
464 }