chiark / gitweb /
Import upstream version 5.3.
[mup] / mup / mupdisp / mupdisp.c
CommitLineData
69695f33
MW
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
55struct CONFIG *Conf_info_p; /* the info for the actual $TERM */
56struct Pginfo *Pagehead = (struct Pginfo *) 0; /* all page's bitmap info */
57struct Pginfo *Pagetail = (struct Pginfo *) 0; /* where to add to list */
58struct Pginfo *Currpage_p; /* current page */
59int Pagenum; /* current page number */
60long Beginprolog; /* where in PostScript file the prolog begins */
61long Endprolog; /* where in PostScript file the prolog ends */
62long Begin_offset; /* offset in file where current page begins */
63int Pagenum; /* current page number */
64int Psfile; /* PostScript temp file, file descriptor */
65FILE *PS_file; /* PostScript temp file */
66int Fullbitmaps; /* temp file of full page bitmaps */
67int Partbitmaps; /* temp file for bitmaps for scrollable pages */
68int Nulldev; /* /dev/null */
69#ifdef linux
70char Fullfile[] = "mupdispfXXXXXX"; /* name of gs output tmp file, full page */
71char Partfile[] = "mupdisppXXXXXX"; /* name of gs output tmp file, partial page */
72char Mupfile[] = "mupdispmXXXXXX"; /* Mup output temp file */
73#else
74char Fullfile[L_tmpnam]; /* name of gs output tmp file, full page */
75char Partfile[L_tmpnam]; /* name of gs output tmp file, partial page */
76char Mupfile[L_tmpnam]; /* Mup output temp file */
77#endif
78char **Argv; /* global version of argv */
79int Argc; /* global version of argc */
80int Fullpgmode = DFLT_MODE; /* full page or partial page mode, YES if full */
81char *Exit_errmsg = (char *) 0; /* error message to print upon exit, if any */
82char *Gs_errfile = "mupdispg.err"; /* ghostscript error file */
83int Bits_per_line = 612; /* pixels per line */
84int Bytes_per_line = 77; /* pixels per line divided by 8 rounded up */
85int Lines_per_page = 792; /* vertical pixels */
86char *Version = "5.3";
87
88/* misc function declarations */
89static void parsePS P((FILE *file));
90static void save_endpage P((void));
91
92\f
93
94/* main function. Run Mup, run ghostscript, then do user interface */
95
96int
97main(argc, argv)
98
99int argc;
100char **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
169int
170getpginfo(pgnum)
171
172int 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
201void
202user_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
220int
221scroll(line, distance)
222
223int line;
224int 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
258void
259generalcleanup(status)
260
261int 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
310int
311create_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
355static void
356parsePS(file)
357
358FILE *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
433static void
434save_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}