Commit | Line | Data |
---|---|---|
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 | ||
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 | } |