1 /*========================================================================*\
2
3 Copyright (c) 1990-2013 Paul Vojta
4
5 Permission is hereby granted, free of charge, to any person obtaining a copy
6 of this software and associated documentation files (the "Software"), to
7 deal in the Software without restriction, including without limitation the
8 rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9 sell copies of the Software, and to permit persons to whom the Software is
10 furnished to do so, subject to the following conditions:
11
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
14
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 PAUL VOJTA OR ANY OTHER AUTHOR OF THIS SOFTWARE BE LIABLE FOR ANY CLAIM,
19 DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21 OTHER DEALINGS IN THE SOFTWARE.
22
23 NOTE:
24 xdvi is based on prior work, as noted in the modification history
25 in xdvi.c.
26
27 \*========================================================================*/
28
29 #include "xdvi-config.h"
30 #include "xdvi.h"
31
32 #include "dvi-init.h"
33 #include "dvi-draw.h"
34 #ifdef PTEX
35 #include "ptexvf.h"
36 #include "ptexmap.h"
37 #endif
38 #include "util.h"
39 #include "x_util.h"
40 #include "exit-handlers.h"
41 #include "mime.h"
42 #include "pagesel.h"
43 #include "special.h"
44 #include "hypertex.h"
45 #include "kpathsea/c-fopen.h"
46 #include "kpathsea/c-stat.h"
47 #include "kpathsea/magstep.h"
48 #include "kpathsea/tex-glyph.h"
49 #include "dvi.h"
50 #include "string-utils.h"
51 #include "browser.h"
52 #include "sfSelFile.h"
53 #include "xm_toolbar.h"
54 #include "pagehist.h"
55 #include "message-window.h"
56 #include "search-internal.h"
57 #include "statusline.h"
58 #include "events.h"
59 #include "font-open.h"
60 #ifdef HAVE_LIBPAPER
61 #include <paper.h>
62 #endif
63
64 #if FREETYPE
65 # include FT_SIZES_H
66 #endif
67
68 #define PK_PRE 247
69 #define PK_ID 89
70 #define PK_MAGIC ((PK_PRE << 8) | PK_ID)
71 #define GF_PRE 247
72 #define GF_ID 131
73 #define GF_MAGIC ((GF_PRE << 8) | GF_ID)
74 #define VF_PRE 247
75 #define VF_ID_BYTE 202
76 #define VF_MAGIC ((VF_PRE << 8) | VF_ID_BYTE)
77 #ifdef PTEX
78 #define JFMS_MAGIC 11
79 #define JFMS_TATEMAGIC 9
80 #endif /* PTEX */
81
82 /* font stuff */
83 struct font *tn_table[TNTABLELEN];
84 struct font *font_head = NULL;
85 struct tn *tn_head = NULL;
86 wide_ubyte maxchar;
87 unsigned short current_timestamp = 0;
88
89
90 unsigned long magnification;
91 double dimconv;
92 double tpic_conv;
93
94 /* Curently processed page number (starting at 0). */
95 int current_page = 0;
96 int total_pages = 0;
97
98 /* postamble offset is saved in this global variable */
99 long g_postamble_offset;
100
101
102 static struct stat fstatbuf;
103
104 static FILE *m_dvi_fp = NULL; /* original user's file */
105
106
107 static Boolean open_dvi_file(const char *filename, Boolean open_new_instance);
108
109 /*
110 * DVI preamble and postamble information.
111 */
112 static unsigned long numerator, denominator;
113 static unsigned int dvi_unshrunk_page_w, dvi_unshrunk_page_h;
114 static unsigned int m_paper_unshrunk_w, m_paper_unshrunk_h;
115
116
117 /*
118 * Offset in DVI file of last page, set in read_postamble().
119 */
120 static long m_last_page_offset;
121
122 static const char *dvi_err_list[] = {
123 /* NO_ERROR */ "No Error",
124 /* WRONG_DVI_VERSION */ "Wrong version of DVI output for this program",
125 /* DVI_CORRUPTED */ "DVI file corrupted",
126 /* NOT_A_DVI_FILE */ "Not a DVI file",
127 /* POSTAMBLE_NO_POST */ "Postamble doesn't begin with POST",
128 /* POSTAMBLE_NO_MATCH */ "Postamble doesn't match preamble",
129 /* POSTAMBLE_NON_FNTDEF */ "Non-fntdef command found in postamble",
130 /* NOT_ALL_PIXEL_FILES_FOUND */ "Not all pixel files were found",
131 /* NO_BOP_AT_PAGEDESC */ "Page description doesn't begin with BOP",
132 /* FILE_HAS_ZERO_SIZE */ "File has zero size",
133 /* FILE_DOESNT_EXIST */ "No such file",
134 /* FILE_IS_DIRECTORY */ "Is a directory",
135 /* UNKNOWN_ERROR */ "An unknown error occurred"
136 };
137
138 /*
139 * access method for above list
140 */
141 const char *
get_dvi_error(dviErrFlagT flag)142 get_dvi_error(dviErrFlagT flag) {
143 ASSERT(flag < XtNumber(dvi_err_list), "Flag out of range");
144 return dvi_err_list[flag];
145 }
146
147
148 /*
149 * Extract the unit used in paper size specification. This information is used
150 * to decide the initial grid separation.
151 */
152 static int
atopixunit(const char * arg)153 atopixunit(const char *arg)
154 {
155 int len = strlen(arg);
156
157 /* check if the unit 'mm' or 'cm' occurs in the arg string */
158 return (len > 2 && (arg[len - 2] == 'c' || arg[len - 2] == 'm') && arg[len - 1] == 'm' ?
159 1.0 / 2.54 : 1.0) * resource.pixels_per_inch + 0.5;
160 }
161
162 /*
163 * free_vf_chain frees the vf_chain structure.
164 */
165
166 static void
free_vf_chain(struct tn * tnp)167 free_vf_chain(struct tn *tnp)
168 {
169 while (tnp != NULL) {
170 struct tn *tnp1 = tnp->next;
171 free((char *)tnp);
172 tnp = tnp1;
173 }
174 }
175
176 /*
177 * delete glyph information in a font.
178 */
179
180 static void
delete_glyphs(struct font * fontp)181 delete_glyphs(struct font *fontp)
182 {
183 struct glyph *g;
184 #ifdef PTEX
185 int n, maxchar;
186
187 maxchar = fontp->maxchar + 1;
188 for (n = 0; n < maxchar; ++n) {
189 g = (fontp->flags & FONT_KANJI) ? fontp->kglyph[n] : &fontp->glyph[n];
190 if (g == NULL) continue;
191 #else /* !PTEX */
192 for (g = fontp->glyph; g <= fontp->glyph + fontp->maxchar; ++g) {
193 #endif /* !PTEX */
194 free_bitmap2(g);
195 }
196 }
197
198 void free_bitmap2(struct glyph *g) {
199 {
200 if (g->bitmap2.bits) {
201 free(g->bitmap2.bits);
202 g->bitmap2.bits = NULL;
203 }
204 #ifdef GREY
205 if (g->pixmap2) {
206 XDestroyImage(g->image2);
207 g->pixmap2 = NULL;
208 if (g->pixmap2_gc2 != NULL) {
209 free(g->pixmap2_gc2);
210 g->pixmap2_gc2 = NULL;
211 }
212 }
213 #if COLOR
214 g->fg = NULL;
215 #endif
216 #endif
217 }
218 }
219
220 /*
221 * Release all shrunken bitmaps for all fonts.
222 */
223
224 void
225 reset_fonts(void)
226 {
227 struct font *f;
228
229 for (f = font_head; f != NULL; f = f->next) {
230 if ((f->flags & FONT_LOADED) && !(f->flags & FONT_VIRTUAL)) {
231 delete_glyphs(f);
232 }
233 }
234 }
235
236 /*
237 * free up fonts no longer in use.
238 */
239 static void
240 free_unused_fonts(void)
241 {
242 struct font *fontp;
243 struct font **fontpp;
244
245 fontpp = &font_head;
246 while ((fontp = *fontpp) != NULL) {
247 if (fontp->flags & FONT_IN_USE)
248 fontpp = &fontp->next;
249 else {
250 if (globals.debug & DBG_PK)
251 printf("xdvi: Discarding font \"%s\" at %d dpi\n",
252 fontp->fontname, (int)(fontp->fsize + 0.5));
253 *fontpp = fontp->next; /* remove from list */
254 free(fontp->fontname);
255 if (fontp->flags & FONT_LOADED) {
256 #if FREETYPE
257 if (fontp->ft != NULL) { /* if FreeType font */
258 struct ftfont *ft;
259
260 ft = fontp->ft;
261 if (fontp->size != NULL)
262 FT_Done_Size(fontp->size);
263 if (fontp == ft->first_size) {
264 if (fontp->next_size == NULL) {
265 /* if this is the only size of this font face */
266 FT_Done_Face(ft->face);
267 ft->t1->ft = NULL;
268 free(ft);
269 }
270 else {
271 struct font *fp2;
272
273 ft->first_size = fp2 = fontp->next_size;
274 fp2->file = fontp->file;
275 fontp->file = NULL;
276 fp2->filename = fontp->filename;
277 fontp->filename = NULL;
278 fp2->timestamp = fontp->timestamp;
279 }
280 }
281 else {
282 struct font *fp2;
283
284 fp2 = ft->first_size;
285 while (fp2->next_size != fontp)
286 fp2 = fp2->next_size;
287 fp2->next_size = fontp->next_size;
288 }
289 }
290 #endif
291 if (fontp->file != NULL) {
292 fclose(fontp->file);
293 }
294 #if FREETYPE
295 if (fontp->filename != NULL)
296 #endif
297 free((char *) fontp->filename);
298
299 if (fontp->flags & FONT_VIRTUAL) {
300 struct macro *m;
301
302 for (m = fontp->macro;
303 m <= fontp->macro + fontp->maxchar; ++m)
304 if (m->free_me) free((char *)m->pos);
305 free((char *)fontp->macro);
306 free((char *)fontp->vf_table);
307 free_vf_chain(fontp->vf_chain);
308 }
309 else {
310 delete_glyphs(fontp);
311 #ifdef PTEX
312 if (fontp->flags & FONT_KANJI) {
313 int n;
314 for (n = 0; n < (int)fontp->maxchar + 1; ++n) {
315 if (fontp->kglyph[n] != NULL) {
316 free(fontp->kglyph[n]);
317 }
318 }
319 free(fontp->kglyph);
320 }
321 else {
322 #endif /* PTEX */
323 free((char *)fontp->glyph);
324 fontp->glyph = NULL;
325 #ifdef PTEX
326 }
327 #endif /* PTEX */
328 }
329 free((char *)fontp);
330 }
331 }
332 }
333 }
334
335 #if COLOR
336
337 /*
338 * Release all allocated pixels, and (in greyscale mode) invalidate
339 * all shrunken glyphs.
340 */
341
342 void
343 reset_colors(void)
344 {
345 if (color_list_len != 0) {
346 XFreeColors(DISP, G_colormap, color_list, color_list_len, 0);
347 color_list_len = 0;
348 }
349 while (bg_head != NULL) {
350 struct bgrec *bgp;
351 struct fgrec *fgp;
352
353 for (fgp = bg_head->fg_head; fgp != NULL;) {
354 struct fgrec *fgp1 = fgp->next;
355 free(fgp);
356 fgp = fgp1;
357 }
358 bgp = bg_head->next;
359 free(bg_head);
360 bg_head = bgp;
361 }
362 #if GREY
363 if (resource.use_grey) {
364 struct font *f;
365 struct glyph *g;
366
367 for (f = font_head; f != NULL; f = f->next)
368 if ((f->flags & FONT_LOADED) && !(f->flags & FONT_VIRTUAL)
369 #ifdef PTEX
370 && !(f->flags & FONT_KANJI)
371 #endif /* PTEX */
372 )
373 for (g = f->glyph; g <= f->glyph + f->maxchar; ++g)
374 g->fg = NULL;
375 }
376 #endif /* GREY */
377 bg_current = NULL;
378 fg_active = NULL;
379 color_warned = False;
380 }
381
382 /*
383 * All of the above, plus discard all scanned information.
384 */
385
386 void
387 full_reset_colors(void)
388 {
389 if (page_colors.stack != NULL) {
390 size_t i;
391 struct rgb *last_freed = &fg_initial;
392
393 /* fprintf(stderr, "i: %d; last freed: %p\n", page_colors.size, &fg_initial); */
394 for (i = 0; i < page_colors.size; ++i) {
395 if (page_colors.stack[i].colorstack != last_freed) {
396 last_freed = page_colors.stack[i].colorstack;
397 /* BUG ALERT: don't free &fg_initial, else segfault with changing
398 foreground in xm_colorsel.c! */
399 if (last_freed != NULL && last_freed != &fg_initial) {
400 /* fprintf(stderr, "freeing %d: %p\n", i, last_freed); */
401 free(last_freed);
402 }
403 }
404 }
405 free(page_colors.stack);
406 page_colors.stack = NULL;
407 }
408 reset_colors();
409 }
410
411 #endif /* COLOR */
412
413
414
415 /*
416 * realloc_font allocates the font structure to contain (newsize + 1)
417 * characters.
418 */
419
420 void
421 realloc_font(struct font *fontp, wide_ubyte newsize)
422 {
423 struct glyph *glyph;
424
425 glyph = fontp->glyph = xrealloc(fontp->glyph,
426 (unsigned int)(newsize + 1) * sizeof(struct glyph));
427 if (newsize > fontp->maxchar)
428 memset((char *)(glyph + fontp->maxchar + 1), 0,
429 (int)(newsize - fontp->maxchar) * sizeof(struct glyph));
430 maxchar = fontp->maxchar = newsize;
431 }
432
433
434 /*
435 * realloc_virtual_font does the same thing for virtual fonts.
436 */
437
438 void
439 realloc_virtual_font(struct font *fontp, wide_ubyte newsize)
440 {
441 struct macro *macro;
442
443 macro = fontp->macro = xrealloc(fontp->macro,
444 (unsigned int)(newsize + 1) * sizeof(struct macro));
445 if (newsize > fontp->maxchar)
446 memset((char *)(macro + fontp->maxchar + 1), 0,
447 (int)(newsize - fontp->maxchar) * sizeof(struct macro));
448 maxchar = fontp->maxchar = newsize;
449 }
450
451
452 /*
453 * load_font locates the t1 font or raster file and reads the index of
454 * characters, plus whatever other preprocessing is done (depending on
455 * the format).
456 *
457 * Returns True if sucessful, False if not.
458 */
459
460 Boolean
461 load_font(struct font *fontp
462 #if DELAYED_MKTEXPK
463 , Boolean load_font_now
464 #endif
465 )
466 {
467 double fsize = fontp->fsize;
468 int dpi = fsize + 0.5;
469 char *font_found;
470 int size_found;
471 int magic;
472 Boolean hushcs = resource.hush_chk;
473
474 fontp->file = NULL;
475
476 /* BUG ALERT: This used to be:
477 *
478 * if (--globals.ev.ctr == 0) {
479 * read_events(EV_GE_IDLE);
480 * }
481 * force_statusline_update();
482 * XSync(DISP, False);
483 *
484 * The idea was to update the `loading fonts ...' popup. However,
485 * calling read_events() here may call dvi_file_changed() if the
486 * user clicks on the window, which calls file_exists_p(), and
487 * that changes m_dvi_fp while it's still being used to read the
488 * postamble (where load_font() is called from), which will cause
489 * xdvi to crash! (bug #968127).
490 *
491 * Sadly, without this update, the `loading fonts' popup doesn't
492 * appear before the main window comes up ...
493 */
494
495 #ifdef PTEX
496 fontp->dir = 0;
497 #endif /* PTEX */
498
499 fontp->file = font_open(
500 #if DELAYED_MKTEXPK
501 load_font_now,
502 #endif
503 fontp,
504 (const char **) &font_found,
505 &size_found);
506
507 #if DELAYED_MKTEXPK
508 if (!load_font_now)
509 return True;
510 #endif
511
512 #if FREETYPE
513 if (fontp->ft != NULL) { /* if freetype font */
514 fontp->timestamp = ++current_timestamp;
515 fontp->maxchar = maxchar = 255;
516 fontp->set_char_p = set_ft_char;
517 fontp->glyph = xmalloc (256 * sizeof (struct glyph));
518 memset((char *) fontp->glyph, 0, 256 * sizeof (struct glyph));
519 fontp->flags |= FONT_LOADED;
520 if (font_found != NULL) {
521 statusline_error(STATUS_MEDIUM,
522 "Error: Can't find font %s; using %s instead. Expect ugly output.",
523 fontp->fontname, font_found);
524 force_statusline_update();
525 free(fontp->fontname);
526 fontp->fontname = font_found; /* this has been allocated by font_open */
527 }
528 return True;
529 }
530 #endif /* FREETYPE */
531
532 /* when it's not a freetype font, fontp->file == NULL means total failure */
533 if (fontp->file == NULL) {
534 if (globals.ev.flags & EV_GE_NEWDOC)
535 return False;
536 fontp->flags |= FONT_LOADED; /* as loaded as it'll get */
537 XDVI_ERROR((stderr, "Can't find font %s.%dpk", fontp->fontname, dpi));
538 return False;
539 }
540 fontp->flags |= FONT_LOADED;
541
542 if (font_found != NULL && strcmp(fontp->fontname, font_found) != 0) {
543 /* some other font used - FIXME: is there a more efficient way than strcmp() for checking this? */
544 statusline_error(STATUS_MEDIUM,
545 "Can't find pixel font %s; using %s instead at %d dpi.",
546 fontp->fontname, font_found, dpi);
547 force_statusline_update();
548 free(fontp->fontname);
549 fontp->fontname = font_found; /* this has been allocated by font_open */
550 hushcs = True;
551 }
552 else if (!kpse_bitmap_tolerance((double)size_found, fsize)) { /* a different size used */
553 statusline_error(STATUS_MEDIUM,
554 "Can't find pixel font %s at %d dpi; using %d dpi instead.",
555 fontp->fontname, dpi, size_found);
556 force_statusline_update();
557 }
558
559 /* PK version of some font found */
560 fontp->fsize = size_found;
561 fontp->timestamp = ++current_timestamp;
562 fontp->maxchar = maxchar = 255;
563 #ifdef PTEX
564 if (iskanjifont(fontp->fontname)) {
565 fontp->flags |= FONT_KANJI;
566 fontp->set_char_p = set_char2;
567 } else
568 #endif /* PTEX */
569 fontp->set_char_p = set_char;
570 magic = get_bytes(fontp->file, 2);
571
572 switch(magic) {
573 case PK_MAGIC:
574 read_PK_index(fontp, (wide_bool)hushcs);
575 break;
576 #ifdef USE_GF
577 case GF_MAGIC:
578 read_GF_index(fontp, (wide_bool)hushcs);
579 break;
580 #endif
581 case VF_MAGIC:
582 if (resource.omega)
583 maxchar = read_VF_index(fontp, (wide_bool)hushcs);
584 else
585 (void)read_VF_index(fontp, (wide_bool)hushcs);
586 break;
587 #ifdef PTEX
588 case JFMS_MAGIC:
589 case JFMS_TATEMAGIC:
590 fontp->dir = (magic == JFMS_TATEMAGIC);
591 read_PTEXVF_index(fontp);
592 return True;
593 #endif /* PTEX */
594 default:
595 XDVI_FATAL((stderr, "Cannot recognize format for font file %s",
596 fontp->filename));
597 break;
598 }
599
600 if (fontp->flags & FONT_VIRTUAL) {
601 if (!resource.omega) {
602 while (maxchar > 0 && fontp->macro[maxchar].pos == NULL) {
603 --maxchar;
604 }
605 if (maxchar < 255) {
606 realloc_virtual_font(fontp, (wide_ubyte)maxchar);
607 }
608 }
609 }
610 else {
611 while (maxchar > 0 && fontp->glyph[maxchar].addr == 0)
612 --maxchar;
613 if (maxchar < 255) {
614 realloc_font(fontp, (wide_ubyte)maxchar);
615 }
616 }
617 return True;
618 }
619
620
621 /*
622 * MAGSTEPVALUE - If the given magnification is close to a \magstep
623 * or a \magstephalf, then return twice the number of \magsteps.
624 * Otherwise return NOMAGSTP.
625 */
626
627 #define NOMAGSTP (-29999)
628 #define NOBUILD 29999
629
630 static int
631 magstepvalue(float *mag)
632 {
633 int m = 0;
634 double fmag = *mag;
635 double xmag = resource.pixels_per_inch;
636 float margin = fmag * 0.002;
637
638 if (fmag < resource.pixels_per_inch)
639 for (;;) {
640 if (xmag - fmag < margin && -(xmag - fmag) < margin) {
641 *mag = xmag;
642 return m;
643 }
644 if (xmag < fmag)
645 break;
646 xmag *= 0.9128709292;
647 --m;
648 }
649 else
650 for (;;) {
651 if (xmag - fmag < margin && -(xmag - fmag) < margin) {
652 *mag = xmag;
653 return m;
654 }
655 if (xmag > fmag)
656 break;
657 xmag *= 1.095445115;
658 ++m;
659 }
660 return NOMAGSTP;
661 }
662
663 /*
664 * reuse_font recursively sets the flags for font structures being reused.
665 */
666
667 static void
668 reuse_font(struct font *fontp)
669 {
670 struct font **fp;
671 struct tn *tnp;
672
673 if (fontp->flags & FONT_IN_USE)
674 return;
675
676 fontp->flags |= FONT_IN_USE;
677 if (resource.list_fonts)
678 printf("xdvi: (reusing) %s at %d dpi\n", fontp->fontname,
679 (int)(fontp->fsize + 0.5));
680 if (fontp->flags & FONT_VIRTUAL) {
681 for (fp = fontp->vf_table; fp < fontp->vf_table + VFTABLELEN; ++fp)
682 if (*fp != NULL)
683 reuse_font(*fp);
684 for (tnp = fontp->vf_chain; tnp != NULL; tnp = tnp->next)
685 reuse_font(tnp->fontp);
686 }
687 }
688
689
690 /*
691 * define_font reads the rest of the fntdef command and then reads in
692 * the specified pixel file, adding it to the global linked-list holding
693 * all of the fonts used in the job.
694 */
695 struct font *
696 define_font(
697 #if DELAYED_MKTEXPK
698 Boolean read_fonts, /* reading font definitions */
699 Boolean initialize_fonts, /* also initializing internal data structures for fonts */
700 #else
701 Boolean load_font_now, /* only scanning, or also loading the font? */
702 #endif
703 FILE *file,
704 wide_ubyte cmnd,
705 struct font *vfparent, /* vf parent of this font, or NULL */
706 struct font **tntable, /* table for low TeXnumbers */
707 unsigned int tn_table_len, /* length of table for TeXnumbers */
708 struct tn **tn_headpp, /* addr of head of list of TeXnumbers */
709 Boolean *not_found_flag) /* signal that font hasn't been found */
710 {
711 unsigned int TeXnumber;
712 struct font *fontp;
713 float fsize;
714 double scale_dimconv;
715 long checksum;
716 int scale;
717 int design;
718 int magstepval;
719 int len;
720 char *fontname;
721 int size;
722
723 TeXnumber = get_bytes(file, (int)cmnd - FNTDEF1 + 1);
724 checksum = get_bytes(file, 4);
725 scale = get_bytes(file, 4);
726 design = get_bytes(file, 4);
727 len = get_byte(file);
728 len += get_byte(file); /* sequence point in the middle */
729
730 #if DELAYED_MKTEXPK
731 if (!read_fonts) {
732 get_bytes(file, len);
733 return NULL;
734 }
735 #else
736 if (!load_font_now)
737 return NULL;
738 #endif
739
740 fontname = xmalloc((unsigned)len + 1);
741 (void)fread(fontname, sizeof(char), len, file);
742 fontname[len] = '\0';
743
744 if (globals.debug & DBG_PK)
745 printf("xdvi: Define font \"%s\" scale=%d design=%d number=%d\n",
746 fontname, scale, design, TeXnumber);
747 if (vfparent == NULL) {
748 fsize = 0.001 * scale / design * magnification * resource.pixels_per_inch;
749 scale_dimconv = dimconv;
750 }
751 else {
752 /*
753 * The scaled size is given in units of vfparent->scale * 2 ** -20
754 * SPELL units, so we convert it into SPELL units by multiplying by
755 * vfparent->dimconv.
756 * The design size is given in units of 2 ** -20 pt, so we convert
757 * into SPELL units by multiplying by
758 * (resource.pixels_per_inch * 2**16) / (72.27 * 2**20).
759 */
760 fsize = (72.27 * (1 << 4)) * vfparent->dimconv * scale / design;
761 scale_dimconv = vfparent->dimconv;
762 }
763 magstepval = magstepvalue(&fsize);
764 size = fsize + 0.5;
765 /*
766 * reuse font if possible
767 */
768 for (fontp = font_head;; fontp = fontp->next) {
769 if (fontp == NULL) { /* if font doesn't exist yet */
770 if (resource.list_fonts)
771 printf("xdvi: %s at %d dpi\n", fontname, (int)(fsize + 0.5));
772 fontp = xmalloc((unsigned)sizeof(struct font));
773 fontp->fontname = fontname;
774 fontp->fsize = fsize;
775 fontp->magstepval = magstepval;
776 fontp->file = NULL; /* needed for virtual/freetype fonts */
777 fontp->filename = NULL; /* needed for freetype fonts */
778 fontp->checksum = checksum;
779 fontp->flags = FONT_IN_USE;
780 fontp->dimconv = scale * scale_dimconv / (1 << 20);
781 fontp->set_char_p = load_n_set_char;
782 #if FREETYPE
783 fontp->ft = NULL;
784 /* pixsize = scaled size of the font in pixels,
785 * = scale * [vfparent->]dimconv / (1 << 16).
786 */
787 fontp->pixsize =
788 (vfparent != NULL ? vfparent->dimconv : dimconv) * scale
789 / (1 << 16);
790 #endif
791 if (vfparent == NULL)
792 if (!load_font(fontp
793 #if DELAYED_MKTEXPK
794 , initialize_fonts
795 #endif
796 )) {
797 if (globals.ev.flags & EV_GE_NEWDOC) { /* if aborting */
798 free(fontname);
799 free(fontp);
800 return NULL;
801 }
802 *not_found_flag = True;
803 }
804 fontp->next = font_head;
805 font_head = fontp;
806 break;
807 }
808 if (strcmp(fontname, fontp->fontname) == 0
809 && size == (int)(fontp->fsize + 0.5)) {
810 /* if font already in use */
811 reuse_font(fontp);
812 free(fontname);
813 break;
814 }
815 }
816 if (TeXnumber < tn_table_len)
817 tntable[TeXnumber] = fontp;
818 else {
819 struct tn *tnp;
820 tnp = xmalloc((unsigned)sizeof(struct tn));
821 tnp->next = *tn_headpp;
822 *tn_headpp = tnp;
823 tnp->TeXnumber = TeXnumber;
824 tnp->fontp = fontp;
825 }
826 return fontp;
827 }
828
829
830
831 /*
832 * process_preamble reads the information in the preamble and stores
833 * it into global variables for later use.
834 */
835 Boolean
836 process_preamble(FILE *fp, dviErrFlagT *errflag)
837 {
838 ubyte k;
839 static char job_id[300];
840
841 TRACE_FILES((stderr, "process_preamble: fp = %p, errflag = %d", (void *)fp, *errflag));
842
843 if (get_byte(fp) != PRE) {
844 *errflag = NOT_A_DVI_FILE;
845 TRACE_FILES((stderr, "process_preamble: fp = %p, errflag = %d, returning False", (void *)fp, *errflag));
846 return False;
847 }
848 #ifdef PTEX
849 k = get_byte(fp);
850 if (k != 2 && k != 3)
851 #else /* !PTEX */
852 if (get_byte(fp) != 2)
853 #endif /* !PTEX */
854 {
855 *errflag = WRONG_DVI_VERSION;
856 TRACE_FILES((stderr, "process_preamble: fp = %p, errflag = %d, returning False", (void *)fp, *errflag));
857 return False;
858 }
859 numerator = get_bytes(fp, 4);
860 denominator = get_bytes(fp, 4);
861 magnification = get_bytes(fp, 4);
862 dimconv = (((double)numerator * magnification)
863 / ((double)denominator * 1000.));
864 dimconv = dimconv * (((long)resource.pixels_per_inch) << 16) / 254000;
865 tpic_conv = resource.pixels_per_inch * magnification / 1000000.0;
866 k = get_byte(fp);
867 (void)fread(job_id, sizeof(char), (int)k, fp);
868 job_id[k] = '\0';
869
870 TRACE_FILES((stderr, "process_preamble: fp = %p, errflag = %d, returning True", (void *)fp, *errflag));
871
872 return True;
873 }
874
875 /*
876 * find_postamble locates the beginning of the postamble
877 * and leaves the file ready to start reading at that location.
878 */
879 #define TMPSIZ 516 /* 4 trailer bytes + 512 junk bytes allowed */
880 Boolean
881 find_postamble(FILE *fp, dviErrFlagT *errflag)
882 {
883 long pos;
884 ubyte temp[TMPSIZ];
885 ubyte *p;
886 ubyte *p1;
887 ubyte byte;
888
889 TRACE_FILES((stderr, "find_postamble on fp: %p", (void *)fp));
890
891 fseek(fp, 0L, SEEK_END);
892 pos = ftell(fp) - TMPSIZ;
893 if (pos < 0)
894 pos = 0;
895 fseek(fp, pos, SEEK_SET);
896 p = temp + fread((char *)temp, sizeof(char), TMPSIZ, fp);
897 for (;;) {
898 p1 = p;
899 while (p1 > temp && *(--p1) != TRAILER);
900 p = p1;
901 while (p > temp && *(--p) == TRAILER);
902 if (p <= p1 - 4)
903 break; /* found 4 TRAILER bytes */
904 if (p <= temp) {
905 *errflag = DVI_CORRUPTED;
906 TRACE_FILES((stderr, "find_postamble: returning FALSE"));
907 return False;
908 }
909 }
910 pos += p - temp;
911 byte = *p;
912 while (byte == TRAILER) {
913 fseek(fp, --pos, SEEK_SET);
914 byte = get_byte(fp);
915 }
916 #ifdef PTEX
917 if (byte != 2 && byte != 3)
918 #else /* !PTEX */
919 if (byte != 2)
920 #endif /* !PTEX */
921 {
922 *errflag = WRONG_DVI_VERSION;
923 TRACE_FILES((stderr, "find_postamble: returning FALSE"));
924 return False;
925 }
926 fseek(fp, pos - 4, SEEK_SET);
927 fseek(fp, get_lbytes(fp, 4), SEEK_SET);
928 TRACE_FILES((stderr, "find_postamble: returning TRUE"));
929 return True;
930 }
931
932
933
934 Boolean
935 set_paper_type(const char *arg)
936 {
937 const char *arg1;
938 char temp[21];
939 const char **p;
940 char *q;
941 #ifdef HAVE_LIBPAPER
942 const struct paper *pp;
943 int landscape = 0;
944 #else
945 const char **paper_types = get_paper_types();
946 size_t paper_types_size = get_paper_types_size();
947 #endif
948
949 if (*arg == '+') {
950 ++arg;
951 ignore_papersize_specials = True;
952 }
953 if (strlen(arg) > sizeof(temp) - 1)
954 return False;
955 q = temp;
956 for (;;) { /* convert to lower case */
957 char c = *arg++;
958 if (c >= 'A' && c <= 'Z')
959 c ^= ('a' ^ 'A');
960 *q++ = c;
961 if (c == '\0')
962 break;
963 }
964 arg = temp;
965 #ifdef HAVE_LIBPAPER
966 paperinit();
967 if (strcmp(temp, "libpaper") == 0) {
968 const char *name;
969
970 name = systempapername();
971 if (name == NULL)
972 name = defaultpapername();
973 if (strcmp(name, "libpaper") == 0)
974 name = "a4";
975
976 strncpy(temp, name, sizeof(temp));
977 temp[sizeof(temp) - 1] = '\0';
978 }
979 if (strcmp(temp, "letter") != 0 &&
980 strcmp(temp, "ledger") != 0) {
981 if (temp[strlen(temp) - 1] == 'r') {
982 temp[strlen(temp) - 1] = '\0';
983 landscape = 1;
984 }
985 }
986 for (pp = paperfirst(); pp; pp = papernext(pp)) {
987 if (strcmp(temp, papername(pp)) == 0) {
988 double w, h;
989 char wstr[256];
990 char hstr[256];
991
992 if (landscape == 0) {
993 w = paperpswidth(pp);
994 h = paperpsheight(pp);
995 } else {
996 h = paperpswidth(pp);
997 w = paperpsheight(pp);
998 }
999 w = w / 72.0 * 10 * 2.54;
1000 h = h / 72.0 * 10 * 2.54;
1001 snprintf(wstr, sizeof(wstr), "%f mm", w);
1002 snprintf(hstr, sizeof(hstr), "%f mm", h);
1003 wstr[sizeof(wstr) - 1] = '\0';
1004 hstr[sizeof(hstr) - 1] = '\0';
1005 m_paper_unshrunk_w = atopix(wstr, False);
1006 m_paper_unshrunk_h = atopix(hstr, False);
1007 globals.grid_paper_unit = atopixunit("mm");
1008
1009 break;
1010 }
1011 }
1012 paperdone();
1013 if (pp == NULL)
1014 return (False);
1015 #else
1016 /* perform substitutions */
1017 for (p = paper_types; p < paper_types + paper_types_size; p += 2) {
1018 if (strcmp(temp, *p) == 0) {
1019 arg = p[1];
1020 break;
1021 }
1022 }
1023 arg1 = strchr(arg, 'x');
1024 if (arg1 == NULL)
1025 return False;
1026 m_paper_unshrunk_w = atopix(arg, False);
1027 m_paper_unshrunk_h = atopix(arg1 + 1, False);
1028
1029 globals.grid_paper_unit = atopixunit(arg);
1030 #endif
1031
1032 return (m_paper_unshrunk_w != 0 && m_paper_unshrunk_h != 0);
1033 }
1034
1035 /*
1036 * read_postamble reads the information in the postamble from fp,
1037 * storing it into global variables.
1038 * It also takes care of reading in all of the pixel files for the fonts
1039 * used in the job.
1040 *
1041 * FIXME: Would be better (speed up initialization when needing to generate fonts,
1042 * and allow to open window on first page) if the font loading was done on-demand later!
1043 */
1044 Boolean
1045 read_postamble(FILE *fp, dviErrFlagT *errflag,
1046 #if DELAYED_MKTEXPK
1047 Boolean read_fonts, Boolean initialize_fonts
1048 #else
1049 Boolean load_fonts
1050 #endif
1051 )
1052 {
1053 ubyte cmnd;
1054 Boolean font_not_found = False;
1055 struct font *fontp;
1056
1057 #if DELAYED_MKTEXPK
1058 int tmp_total_pages;
1059 unsigned long tmp_numerator = numerator;
1060 unsigned long tmp_denominator = denominator;
1061 unsigned long tmp_magnification = magnification;
1062 unsigned int tmp_dvi_unshrunk_page_w, tmp_dvi_unshrunk_page_h;
1063 long tmp_last_page_offset;
1064
1065 TRACE_FILES((stderr, "read_postamble: reading %p (%d, %d)", (void *)fp, read_fonts, initialize_fonts));
1066
1067 if (read_fonts && initialize_fonts) {
1068 /* clear existing font table */
1069 memset((char *)tn_table, 0, (int)sizeof tn_table);
1070 free_vf_chain(tn_head);
1071 tn_head = NULL;
1072 for (fontp = font_head; fontp != NULL; fontp = fontp->next)
1073 fontp->flags &= ~FONT_IN_USE;
1074 }
1075 #else /* DELAYED_MKTEXPK */
1076 TRACE_FILES((stderr, "read_postamble: reading %p (%d)", (void *)fp, load_fonts));
1077
1078 /* clear existing font table */
1079 memset((char *)tn_table, 0, (int)sizeof tn_table);
1080 free_vf_chain(tn_head);
1081 tn_head = NULL;
1082 for (fontp = font_head; fontp != NULL; fontp = fontp->next)
1083 fontp->flags &= ~FONT_IN_USE;
1084 #endif /* DELAYED_MKTEXPK */
1085
1086 if (get_byte(fp) != POST) {
1087 *errflag = POSTAMBLE_NO_POST;
1088 TRACE_FILES((stderr, "read_postamble: returning FALSE"));
1089 return False;
1090 }
1091 #if DELAYED_MKTEXPK
1092 tmp_last_page_offset = get_bytes(fp, 4);
1093 if (read_fonts && initialize_fonts)
1094 m_last_page_offset = tmp_last_page_offset;
1095
1096 if (tmp_numerator != get_bytes(fp, 4)
1097 || tmp_denominator != get_bytes(fp, 4)
1098 || tmp_magnification != get_bytes(fp, 4)) {
1099 *errflag = POSTAMBLE_NO_MATCH;
1100 TRACE_FILES((stderr, "read_postamble: returning FALSE"));
1101 return False;
1102 }
1103 else if (read_fonts && initialize_fonts) {
1104 numerator = tmp_numerator;
1105 denominator = tmp_denominator;
1106 magnification = tmp_magnification;
1107 }
1108
1109 /* read largest box height and width */
1110 tmp_dvi_unshrunk_page_h = (spell_conv(get_lbytes(fp, 4)) >> 16) + resource.yoffset_int;
1111 tmp_dvi_unshrunk_page_w = (spell_conv(get_lbytes(fp, 4)) >> 16) + resource.xoffset_int;
1112 (void)get_bytes(fp, 2); /* max stack size */
1113 tmp_total_pages = get_bytes(fp, 2);
1114
1115 if (read_fonts && initialize_fonts) {
1116 dvi_unshrunk_page_h = tmp_dvi_unshrunk_page_h;
1117 if (dvi_unshrunk_page_h < m_paper_unshrunk_h)
1118 dvi_unshrunk_page_h = m_paper_unshrunk_h;
1119 dvi_unshrunk_page_w = tmp_dvi_unshrunk_page_w;
1120 if (dvi_unshrunk_page_w < m_paper_unshrunk_w)
1121 dvi_unshrunk_page_w = m_paper_unshrunk_w;
1122 total_pages = tmp_total_pages;
1123 }
1124 #else /* DELAYED_MKTEXPK */
1125 m_last_page_offset = get_bytes(fp, 4);
1126 if (numerator != get_bytes(fp, 4)
1127 || denominator != get_bytes(fp, 4)
1128 || magnification != get_bytes(fp, 4)) {
1129 *errflag = POSTAMBLE_NO_MATCH;
1130 TRACE_FILES((stderr, "read_postamble: returning FALSE"));
1131 return False;
1132 }
1133
1134 /* read largest box height and width */
1135 dvi_unshrunk_page_h = (spell_conv(get_lbytes(fp, 4)) >> 16) + resource.yoffset_int;
1136 if (dvi_unshrunk_page_h < m_paper_unshrunk_h)
1137 dvi_unshrunk_page_h = m_paper_unshrunk_h;
1138 dvi_unshrunk_page_w = (spell_conv(get_lbytes(fp, 4)) >> 16) + resource.xoffset_int;
1139 if (dvi_unshrunk_page_w < m_paper_unshrunk_w)
1140 dvi_unshrunk_page_w = m_paper_unshrunk_w;
1141 (void)get_bytes(fp, 2); /* max stack size */
1142 total_pages = get_bytes(fp, 2);
1143 #endif /* DELAYED_MKTEXPK */
1144
1145 /* read font definitions */
1146 while ((cmnd = get_byte(fp)) >= FNTDEF1 && cmnd <= FNTDEF4) {
1147 struct font *f = define_font(
1148 #if DELAYED_MKTEXPK
1149 read_fonts, initialize_fonts,
1150 #else
1151 load_fonts,
1152 #endif
1153 fp, cmnd, (struct font *)NULL, tn_table,
1154 TNTABLELEN, &tn_head, &font_not_found);
1155 if (
1156 #if DELAYED_MKTEXPK
1157 read_fonts && initialize_fonts
1158 #else
1159 load_fonts
1160 #endif
1161 && f == NULL) {
1162 TRACE_FILES((stderr, "read_postamble: returning FALSE"));
1163 return False;
1164 }
1165 #if !DELAYED_MKTEXPK
1166 else if (!load_fonts) { /* return early */
1167 TRACE_FILES((stderr, "read_postamble: returning TRUE"));
1168 return True;
1169 }
1170 #endif
1171 }
1172
1173 if (cmnd != POSTPOST) {
1174 *errflag = POSTAMBLE_NON_FNTDEF;
1175 TRACE_FILES((stderr, "read_postamble: returning FALSE"));
1176 return False;
1177 }
1178 if (
1179 #if DELAYED_MKTEXPK
1180 read_fonts && initialize_fonts &&
1181 #endif
1182 font_not_found) {
1183 *errflag = NOT_ALL_PIXEL_FILES_FOUND;
1184 TRACE_FILES((stderr, "read_postamble: returning FALSE"));
1185 return False;
1186 }
1187 #if DELAYED_MKTEXPK
1188 if (read_fonts && initialize_fonts)
1189 free_unused_fonts();
1190 #else
1191 free_unused_fonts();
1192 #endif
1193
1194 TRACE_FILES((stderr, "read_postamble: returning TRUE"));
1195 return True;
1196 }
1197
1198
1199 static Boolean
1200 prepare_pages(dviErrFlagT *errflag)
1201 {
1202 int i;
1203 long offset;
1204
1205 TRACE_FILES((stderr, "calling pageinfo_deallocate"));
1206 pageinfo_deallocate();
1207
1208 TRACE_FILES((stderr, "pageinfo_allocate for %d pages", total_pages + 1));
1209 pageinfo_allocate(total_pages + 1);
1210 pageinfo_set_page_width(total_pages, m_paper_unshrunk_w);
1211 pageinfo_set_page_height(total_pages, m_paper_unshrunk_h);
1212 pageinfo_set_window_width(total_pages, dvi_unshrunk_page_w);
1213 pageinfo_set_window_height(total_pages, dvi_unshrunk_page_h);
1214
1215 pageinfo_set_offset(total_pages - 1, m_last_page_offset);
1216 /*
1217 * Follow back pointers through pages in the DVI file,
1218 * storing the offsets in the pageinfo table.
1219 */
1220 for (offset = m_last_page_offset, i = total_pages; i > 0; i--) {
1221 fseek(globals.dvi_file.bak_fp, offset, SEEK_SET);
1222 if (get_byte(globals.dvi_file.bak_fp) != BOP) {
1223 *errflag = NO_BOP_AT_PAGEDESC;
1224 return False;
1225 }
1226 pageinfo_set_offset(i-1, offset);
1227 /* from c_0, read count0, the TeX page number */
1228 pageinfo_set_number(i-1, get_bytes(globals.dvi_file.bak_fp, 4));
1229
1230 /* skip c_1 to c_9 */
1231 fseek(globals.dvi_file.bak_fp, 9*4L, SEEK_CUR);
1232
1233 /* next 4 byte contain offset to previous bop */
1234 offset = get_bytes(globals.dvi_file.bak_fp, 4);
1235 }
1236 /* If not prescanning, initialize page sizes. */
1237 if (!resource.prescan) {
1238 for (i = 0; i < total_pages; ++i) {
1239 pageinfo_set_page_width(i, m_paper_unshrunk_w);
1240 pageinfo_set_page_height(i, m_paper_unshrunk_h);
1241 pageinfo_set_window_width(i, dvi_unshrunk_page_w);
1242 pageinfo_set_window_height(i, dvi_unshrunk_page_h);
1243 }
1244 }
1245 return True;
1246 }
1247
1248 void
1249 init_page(void)
1250 {
1251 if (globals.dvi_file.bak_fp == NULL)
1252 return;
1253 globals.page.unshrunk_w = pageinfo_get_page_width(current_page);
1254 globals.page.unshrunk_h = pageinfo_get_page_height(current_page);
1255 globals.page.w = ROUNDUP(globals.page.unshrunk_w, mane.shrinkfactor) + 2;
1256 globals.page.h = ROUNDUP(globals.page.unshrunk_h, mane.shrinkfactor) + 2;
1257 TRACE_FILES((stderr, "init_page: setting globals.page.w = %d, globals.page.h = %d", globals.page.w, globals.page.h));
1258 }
1259
1260 #ifndef S_ISDIR
1261 #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
1262 #endif
1263
1264 static char *m_tmp_dvi_name = NULL; /* name of backup file for useTempFp */
1265
1266 /* access function for backup file name */
1267 char *get_tmp_dvi_name(void) {
1268 return m_tmp_dvi_name;
1269 }
1270
1271 static void
1272 remove_tmp_dvi_file(void *dummy)
1273 {
1274 UNUSED(dummy);
1275 if (m_tmp_dvi_name != NULL) {
1276 unlink(m_tmp_dvi_name);
1277 free(m_tmp_dvi_name);
1278 }
1279 m_tmp_dvi_name = NULL;
1280 }
1281
1282
1283 static FILE *
1284 make_backup_fp(FILE *source_fp, FILE *target_fp)
1285 {
1286 static Boolean first_time = True;
1287 static int tmp_fd = 0;
1288
1289 #if !HAVE_FTRUNCATE
1290 /* in this case, we can't use ftruncate() on the existing temp file -
1291 just close the existing one, and set flag to open a new one */
1292 remove_tmp_dvi_file(NULL);
1293 if (target_fp != NULL)
1294 fclose(target_fp);
1295 /* make sure we use a new temp file, else we'd have a race condition
1296 after closing it */
1297 first_time = True;
1298 #endif
1299
1300 if (first_time) { /* doesn't exist yet, create it */
1301 if ((tmp_fd = xdvi_temp_fd(&m_tmp_dvi_name)) == -1) {
1302 XDVI_ERROR((stderr, "error creating temporary file - disabling `useTempFp'."));
1303 resource.use_temp_fp = False;
1304 remove_tmp_dvi_file(NULL);
1305 return NULL;
1306 }
1307 /* fprintf(stderr, "temporary file name: |%s|, %d\n", m_tmp_dvi_name, tmp_fd); */
1308 TRACE_EVENTS((stderr, "Created temp file: |%s|\n", m_tmp_dvi_name));
1309 if ((target_fp = try_fdopen(tmp_fd, "wb+")) == NULL) {
1310 XDVI_ERROR((stderr, "error opening temporary file (%s) - disabling `useTempFp'.", strerror(errno)));
1311 resource.use_temp_fp = False;
1312 remove_tmp_dvi_file(NULL);
1313 return NULL;
1314 }
1315 first_time = False;
1316 }
1317 else { /* if not first time, truncate the existing file,
1318 and position both files at beginning */
1319 ASSERT(target_fp != NULL, "");
1320 ASSERT(source_fp != NULL, "");
1321
1322 #if HAVE_FTRUNCATE
1323 if (ftruncate(tmp_fd, 0) < 0) {
1324
1325 XDVI_ERROR((stderr, "Couldn't truncate file %s: %s - disabling `useTempFp'; target_fp: %p.",
1326 m_tmp_dvi_name, strerror(errno), target_fp));
1327 resource.use_temp_fp = False;
1328 remove_tmp_dvi_file(NULL);
1329 fclose(target_fp);
1330 return NULL;
1331 }
1332 #endif
1333 fseek(target_fp, 0L, SEEK_SET);
1334 fseek(source_fp, 0L, SEEK_SET);
1335 }
1336
1337 /* copy the file */
1338 if (!copy_fp(source_fp, target_fp)) {
1339 XDVI_ERROR((stderr,
1340 "Error creating temporary file: %s\n"
1341 "- disabling `useTempFp'.",
1342 strerror(errno)));
1343 remove_tmp_dvi_file(NULL);
1344 resource.use_temp_fp = False;
1345 fclose(target_fp);
1346 target_fp = NULL;
1347 }
1348
1349 /* rewind both files, else DVI parsing will fail! */
1350 if (target_fp != NULL) {
1351 fflush(target_fp);
1352 }
1353 fseek(source_fp, 0L, SEEK_SET);
1354 if (target_fp != NULL) {
1355 fseek(target_fp, 0L, SEEK_SET);
1356 }
1357
1358 return target_fp;
1359 }
1360
1361 static Boolean
1362 file_exists_p(const char *path, dviErrFlagT *errflag)
1363 {
1364 TRACE_FILES((stderr, "file_exists_p for |%s|", path));
1365 *errflag = UNKNOWN_ERROR;
1366 if ((m_dvi_fp = XFOPEN(path, OPEN_MODE)) == NULL) {
1367 /* fprintf(stderr, "after internal_open_dvi1: xfopen\n"); */
1368 *errflag = FILE_DOESNT_EXIST;
1369 return False;
1370 }
1371 TRACE_FILES((stderr, "m_dvi_fp for |%s| = %p", path, (void *)m_dvi_fp));
1372 /* fprintf(stderr, "after internal_open_dvi2: xfopen\n"); */
1373
1374 /* shouldn't happen */
1375 if (fstat(fileno(m_dvi_fp), &fstatbuf) != 0 || S_ISDIR(fstatbuf.st_mode)) { /* if it's a directory */
1376 *errflag = FILE_IS_DIRECTORY;
1377 fclose(m_dvi_fp);
1378 m_dvi_fp = NULL;
1379 return False;
1380 }
1381 /* If file has zero size, something has gone wrong with downloading
1382 it, and the user should already have been warned about that;
1383 just return in this case.
1384 TODO: can it still happen that we try to load such a file as .dvi
1385 file? (Will exit with `draw_part: unknown op-code xyz' or some such).
1386 In this case, it would be better to look at the preamble before
1387 entering the drawing loop.
1388 */
1389 if (fstatbuf.st_size == 0) {
1390 *errflag = FILE_HAS_ZERO_SIZE;
1391 fclose(m_dvi_fp);
1392 m_dvi_fp = NULL;
1393 return False;
1394 }
1395 return True;
1396 }
1397
1398 /*
1399 * internal_init_dvi is the main subroutine for reading the startup
1400 * information from the dvi file.
1401 */
1402
1403 static Boolean
1404 internal_init_dvi(dviErrFlagT *errflag,
1405 #if DELAYED_MKTEXPK
1406 Boolean read_fonts, Boolean initialize_fonts
1407 #else
1408 Boolean load_fonts
1409 #endif
1410 )
1411 {
1412 char *icon_name = NULL, *title_name = NULL;
1413
1414 have_src_specials = False;
1415
1416 TRACE_FILES((stderr, "internal_init_dvi, globals.dvi_file.bak_fp = %p", (void *)globals.dvi_file.bak_fp));
1417
1418 if (!process_preamble(globals.dvi_file.bak_fp, errflag)
1419 || !find_postamble(globals.dvi_file.bak_fp, errflag)
1420 #if DELAYED_MKTEXPK
1421 || !read_postamble(globals.dvi_file.bak_fp, errflag, read_fonts, initialize_fonts)
1422 #else
1423 || !read_postamble(globals.dvi_file.bak_fp, errflag, load_fonts)
1424 || !prepare_pages(errflag)
1425 #endif
1426 ) {
1427 return False;
1428 }
1429 #if DELAYED_MKTEXPK
1430 if (!read_fonts || !initialize_fonts) /* return early */
1431 return True;
1432
1433 if (!prepare_pages(errflag))
1434 return False;
1435 #endif
1436
1437 if (current_page >= total_pages)
1438 current_page = total_pages - 1;
1439
1440 globals.warn_spec_now = resource.warn_spec;
1441 globals.src.fwd_box_page = -1;
1442 search_reset_info();
1443
1444 if (globals.pausing.num_save != NULL) {
1445 free(globals.pausing.num_save);
1446 globals.pausing.num_save = NULL;
1447 }
1448 if (resource.pause && total_pages > 0) {
1449 globals.pausing.num_save = xmalloc(total_pages * sizeof *globals.pausing.num_save);
1450 memset(globals.pausing.num_save, 0, total_pages * sizeof *globals.pausing.num_save);
1451 }
1452
1453 init_prescan();
1454 /* this allocates icon_name and title_name */
1455 get_icon_and_title(globals.dvi_name, &icon_name, &title_name);
1456 set_icon_and_title(icon_name, title_name);
1457 free(icon_name);
1458 free(title_name);
1459 icon_name = title_name = NULL;
1460
1461 #if defined(MOTIF) && HAVE_XPM
1462 tb_check_navigation_sensitivity(current_page);
1463 #endif
1464 refresh_pagelist(total_pages, current_page);
1465
1466 return True;
1467 }
1468
1469 /*
1470 * internal_open_dvi does the real opening of the dvi file, and sets
1471 * globals.dvi_file.time. It returns True on success, and sets m_dvi_fp and globals.dvi_file.bak_fp.
1472 */
1473
1474 Boolean
1475 internal_open_dvi(const char *path, dviErrFlagT *errflag,
1476 #if DELAYED_MKTEXPK
1477 Boolean read_fonts, Boolean initialize_fonts
1478 #else
1479 Boolean load_fonts
1480 #endif
1481 )
1482 {
1483 /* FILE *tmp_fp = NULL; */
1484 /* static FILE *bak_fp = NULL; /\* re-use the temporary backup fp *\/ */
1485 /* fprintf(stderr, "------------ opening: |%s|\n", path); */
1486
1487 #if DELAYED_MKTEXPK
1488 TRACE_FILES((stderr, "internal_open_dvi for |%s|; loading fonts: %d, %d", path, read_fonts, initialize_fonts));
1489 #else
1490 TRACE_FILES((stderr, "internal_open_dvi for |%s|", path));
1491 #endif
1492 close_old_filep();
1493
1494 if (!file_exists_p(path, errflag)) { /* this should set fstatbuf.st_mtime */
1495 return False;
1496 }
1497
1498 if (!resource.use_temp_fp || (globals.dvi_file.bak_fp = make_backup_fp(m_dvi_fp, globals.dvi_file.bak_fp)) == NULL) {
1499 globals.dvi_file.bak_fp = m_dvi_fp;
1500 }
1501
1502 register_exit_handler(remove_tmp_dvi_file, NULL);
1503
1504 globals.dvi_file.time = fstatbuf.st_mtime;
1505
1506 if (!internal_init_dvi(errflag,
1507 #if DELAYED_MKTEXPK
1508 read_fonts, initialize_fonts
1509 #else
1510 load_fonts
1511 #endif
1512 )) {
1513 return False;
1514 }
1515
1516 #if COLOR
1517 full_reset_colors();
1518 #endif /* COLOR */
1519 reset_papersize_special();
1520
1521 TRACE_FILES((stderr, "internal_open_dvi: SUCCESS!"));
1522
1523 return True;
1524 }
1525
1526 /*
1527 check whether `filename' is a DVI file, by checking if it exists, and if so,
1528 opening it and trying to read a DVI preamble from it:
1529 */
1530 static char *
1531 is_dvi_file(const char *filename)
1532 {
1533 FILE *fp;
1534 struct stat statbuf;
1535 char *full_pathname;
1536 dviErrFlagT unused_error;
1537
1538 TRACE_FILES((stderr, "is_dvi_file %s", filename));
1539 TRACE_HTEX((stderr, "filename: |%s|", filename));
1540
1541 /* used to append `.dvi' if not already present, but that was a
1542 bad idea - if we have both foo.2 and foo.2.dvi, user will want
1543 to open `foo.2' (with some appropriate application) if the link
1544 points to that filename. This means that we need to have
1545 different semantics for filenames on the command-line and
1546 filenames in hyperlinks; the latter *must* specify an
1547 extension, the former may omit the extension and default to DVI
1548 files.
1549 */
1550 /* if ((full_filename = filename_append_dvi(filename)) == NULL) { */
1551 /* free(full_filename); */
1552 /* return NULL; */
1553 /* } */
1554 /* TRACE_HTEX((stderr, "full_filename: |%s|\n", full_filename)); */
1555
1556 if ((full_pathname = find_file(filename, &statbuf, kpse_program_text_format)) == NULL) {
1557 return NULL;
1558 }
1559 else {
1560 char *tmp = canonicalize_path(full_pathname);
1561 free(full_pathname);
1562 full_pathname = tmp;
1563 }
1564
1565 TRACE_HTEX((stderr, "is_dvi_file: full_pathname: |%s|", full_pathname));
1566
1567 if ((fp = XFOPEN(full_pathname, OPEN_MODE)) == NULL) {
1568 free(full_pathname);
1569 return NULL;
1570 }
1571
1572 if (!process_preamble(fp, &unused_error)) {
1573 free(full_pathname);
1574 fclose(fp);
1575 return NULL;
1576 }
1577
1578 fclose(fp);
1579 return full_pathname;
1580 }
1581
1582 static void
1583 report_open_error(const char *msg, const char *filename, dviErrFlagT errflag)
1584 {
1585 const char *errmsg;
1586 switch(errflag) {
1587 case FILE_HAS_ZERO_SIZE:
1588 errmsg = "File has zero size";
1589 break;
1590 case FILE_DOESNT_EXIST:
1591 errmsg = "No such file";
1592 break;
1593 case FILE_IS_DIRECTORY:
1594 errmsg = "Is a directory";
1595 break;
1596 default:
1597 errmsg = "An unknown error occurred";
1598 break;
1599 }
1600 XDVI_ERROR((stderr, "%s: %s: %s", msg, filename, errmsg));
1601 }
1602
1603 /*
1604 Implements the algorithm for opening a DVI file from the command line.
1605 This is similar to `open_dvi_file()' in non-k xdvi.
1606
1607 If the file does not already have `.dvi' extension, append `.dvi'
1608 and set tried_dvi_ext to True. Try to open this file. If the file
1609 doesn't exist, try the original file name. If this file doesn't
1610 exist either, exit with an error message `No such file or directory'.
1611 If tried_dvi_ext == True, also report that file.dvi didn't exist
1612 either.
1613
1614 (A later function will check if the file really is a DVI file, and
1615 will use tried_dvi_ext for the same purpose).
1616 */
1617 char *
1618 find_dvi_file(const char *filename, Boolean *tried_dvi_ext, Boolean from_file_history)
1619 {
1620 char *new_filename;
1621 size_t len;
1622 dviErrFlagT errflag;
1623
1624 ASSERT(filename != NULL, "Filename argument in find_dvi_file() musn't be NULL");
1625 len = strlen(filename);
1626
1627 if (len < sizeof(".dvi") || strcmp(filename + len - sizeof(".dvi") + 1, ".dvi") != 0) {
1628 /* doesn't already have .dvi extension */
1629 TRACE_HTEX((stderr, "|%s| doesn't have .dvi extension, appending ...", filename));
1630 new_filename = xstrdup(filename);
1631 new_filename = xstrcat(new_filename, ".dvi");
1632 *tried_dvi_ext = True;
1633
1634 if (file_exists_p(new_filename, &errflag)) { /* file exists */
1635 char *expanded_filename = expand_filename(new_filename, USE_CWD_PATH);
1636 free(new_filename);
1637 return expanded_filename;
1638 }
1639 else { /* don't report an error; will try verbatim filename next */
1640 free(new_filename);
1641 }
1642 }
1643
1644 /* try verbatim filename (might be strange things like `foo.wdvi') */
1645 if (file_exists_p(filename, &errflag)) {
1646 char *expanded_filename = expand_filename(filename, USE_CWD_PATH);
1647 return expanded_filename;
1648 }
1649 else {
1650 if (*tried_dvi_ext) {
1651 if (!from_file_history) {
1652 XDVI_FATAL((stderr, "%s: %s, and %s.dvi doesn't exist either.",
1653 filename, get_dvi_error(errflag), filename));
1654 }
1655 else {
1656 popup_message(globals.widgets.top_level,
1657 MSG_ERR,
1658 NULL,
1659 "Could not open \"%s\": %s.\n", filename, get_dvi_error(errflag));
1660 }
1661 }
1662 else {
1663 if (!from_file_history) {
1664 XDVI_FATAL((stderr, "%s: %s.", filename, get_dvi_error(errflag)));
1665 }
1666 /* else: file is from history; this is at startup, where we may
1667 loop through the history until we find a usable file. Don't report
1668 an error in this case. */
1669 }
1670 }
1671 return NULL;
1672 }
1673
1674
1675
1676 /*
1677 * A wrapper for open_dvi_file that can also deal with remote files/URLs, and other
1678 * file types. Returns the (newly malloc'ed) new dvi file name if it succeeds, NULL else.
1679 */
1680 char *
1681 open_dvi_file_wrapper(const char *filename,
1682 Boolean from_command_line,
1683 Boolean open_new_instance,
1684 Boolean *tried_dvi_ext,
1685 Boolean from_file_history)
1686 {
1687 char *real_filename = NULL;
1688 char *new_dvi_name = NULL;
1689 char canonical_path[MAXPATHLEN + 1];
1690
1691 if (from_command_line) {
1692 TRACE_HTEX((stderr, "filename IS from commandline"));
1693 /*
1694 if filename is from command-line, we want to treat the file as a DVI file
1695 always (and NOT launch the browser or other programs; that'd just confuse
1696 people, who meant to launch *xdvi*). `find_dvi_file' tries to locate the
1697 file and does all error handling; on success, it returns a fully expanded
1698 filename.
1699 */
1700 real_filename = find_dvi_file(filename, tried_dvi_ext, from_file_history); /* this allocates real_filename */
1701 if (real_filename == NULL)
1702 return False;
1703
1704 TRACE_HTEX((stderr, "filename |%s| %p from commandline;\ndvi_name: |%s|,\nfilename: |%s|%p",
1705 real_filename, real_filename, globals.dvi_name, filename, (void *)filename));
1706 new_dvi_name = xstrdup(REALPATH(real_filename, canonical_path));
1707 free(real_filename);
1708 TRACE_FILES((stderr, "new_dvi_name: |%s|", new_dvi_name));
1709 return new_dvi_name;
1710 }
1711 else {
1712 /*
1713 if it's not from the command line (e.g. result of clicking Mouse-1 on a link);
1714 in this case we might fall back to using the browser.
1715 Check whether it's a local file:
1716 */
1717 const char *filename_no_prefix;
1718 char *expanded_filename;
1719 TRACE_HTEX((stderr, "filename NOT from commandline"));
1720 if ((filename_no_prefix = is_local_file(filename)) != NULL) {
1721 /* if it's a local file, check whether it's a DVI file: */
1722 if ((expanded_filename = is_dvi_file(filename_no_prefix)) != NULL) {
1723 /* yes, open it */
1724 if (open_dvi_file(expanded_filename, open_new_instance)) {
1725 TRACE_FILES((stderr, "success: %p |%s|", expanded_filename, expanded_filename));
1726 if (!open_new_instance) {
1727 new_dvi_name = expand_filename_append_dvi(expanded_filename, USE_DVI_PATH, True);
1728 TRACE_FILES((stderr, "new_dvi_name: %p |%s|", (void*)new_dvi_name, new_dvi_name));
1729 }
1730 }
1731 free(expanded_filename);
1732 return new_dvi_name;
1733 }
1734 else {
1735 /*
1736 local file, but not a DVI file;
1737 try other viewers for this MIME type:
1738 */
1739 TRACE_HTEX((stderr, "%s is NOT a DVI file", filename_no_prefix));
1740 launch_program(filename);
1741 return NULL;
1742 }
1743 }
1744 else {
1745 /* not a local file, retrieve it with the browser: */
1746 launch_browser(filename);
1747 return NULL;
1748 }
1749 }
1750 }
1751
1752
1753 static Boolean
1754 open_dvi_file(const char *filename, Boolean open_new_instance)
1755 {
1756 Boolean retval = True;
1757
1758 char *anchor_name = resource.anchor_pos;
1759
1760 TRACE_HTEX((stderr, "open_dvi_file: |%s| + |%s|",
1761 filename, anchor_name == NULL ? "<no anchor>" : anchor_name));
1762
1763 if (open_new_instance) {
1764 launch_xdvi(filename, anchor_name);
1765 }
1766 else {
1767 dviErrFlagT errflag = NO_ERROR;
1768 TRACE_HTEX((stderr, "internal_open_dvi: |%s|", filename));
1769 if (!internal_open_dvi(filename, &errflag, True
1770 #if DELAYED_MKTEXPK
1771 , TRUE
1772 #endif
1773 )) {
1774 report_open_error("Cannot open DVI file", filename, errflag);
1775 retval = False;
1776 }
1777 }
1778 return retval;
1779 }
1780
1781 /**
1782 ** form_dvi_property forms the property used to exhibit the dvi file name
1783 ** used as a window property (used for source specials).
1784 **/
1785
1786 void
1787 form_dvi_property(void)
1788 {
1789 #if 0
1790 size_t len;
1791 unsigned long ino;
1792 int i;
1793 #endif
1794
1795 if (m_dvi_fp == NULL)
1796 return;
1797
1798 if (dvi_property != NULL)
1799 free(dvi_property);
1800
1801 dvi_property_length = strlen(globals.dvi_name) + 1; /* also copy the terminating 0 */
1802 dvi_property = xmalloc(dvi_property_length);
1803
1804 /* NOTE: we don't use dvi_inode like non-k xdvi, since xdvik keeps closer
1805 track of when the path points to a different inode. */
1806 strcpy(dvi_property, globals.dvi_name);
1807 }
1808
1809
1810 /* access for m_dvi_fp */
1811 void
1812 close_old_filep(void)
1813 {
1814 if (m_dvi_fp != NULL) {
1815 fclose(m_dvi_fp);
1816 m_dvi_fp = NULL;
1817 if (!resource.use_temp_fp)
1818 globals.dvi_file.bak_fp = NULL;
1819 }
1820 }
1821
1822
1823
1824 /**
1825 ** Check for changes in dvi file.
1826 ** Return True if file has changed, False else.
1827 **/
1828
1829 Boolean
1830 dvi_file_changed(void)
1831 {
1832 TRACE_FILES((stderr, "dvi_file_changed: fp = %p?", (void *)m_dvi_fp));
1833
1834 /* fprintf(stderr, "m_dvi_fp3: %p (%s)\n", m_dvi_fp, globals.dvi_name); */
1835 if (m_dvi_fp == NULL) {
1836 TRACE_FILES((stderr, "m_dvi_fp == NULL"));
1837 if (stat(globals.dvi_name, &fstatbuf) == 0 && fstatbuf.st_mtime != globals.dvi_file.time) {
1838 TRACE_FILES((stderr, "file changed"));
1839 if (resource.use_temp_fp) {
1840 dviErrFlagT errflag = NO_ERROR;
1841 #if !DELAYED_MKTEXPK
1842 if (resource.watch_file == 0.0)
1843 statusline_info(STATUS_MEDIUM, "File changed ...");
1844 TRACE_FILES((stderr, "returning FALSE"));
1845 #endif
1846 if (file_exists_p(globals.dvi_name, &errflag)
1847 && process_preamble(m_dvi_fp, &errflag)
1848 && find_postamble(m_dvi_fp, &errflag)
1849 && read_postamble(m_dvi_fp, &errflag, False
1850 #if DELAYED_MKTEXPK
1851 , False
1852 #endif
1853 )) {
1854 TRACE_FILES((stderr, "File OK, reloading ..."));
1855
1856 globals.ev.flags |= EV_RELOAD;
1857 return True;
1858 }
1859 else {
1860 #if DELAYED_MKTEXPK
1861 if (resource.watch_file == 0.0)
1862 statusline_info(STATUS_MEDIUM, "File corrupted (click on window to reload) ...");
1863 TRACE_FILES((stderr, "returning FALSE"));
1864 #endif
1865 return False;
1866 }
1867 }
1868 /* Bug alert: Don't set EV_RELOAD again here, else xdvi
1869 can go into a stretch of time where it again and again
1870 tries to reload a file even if the user doesn't click
1871 on the canvas.
1872 */
1873 /*
1874 else {
1875 globals.ev.flags |= EV_RELOAD;
1876 }
1877 */
1878 TRACE_FILES((stderr, "returning TRUE"));
1879 return True;
1880 }
1881 else {
1882 TRACE_FILES((stderr, "file not changed"));
1883 }
1884 }
1885 /* BUG ALERT: Don't use fstat(fileno(m_dvi_fp) here; if file hase
1886 disappeared, this won't report an error! */
1887 else if (stat(globals.dvi_name, &fstatbuf) != 0 /* stat failed ... */
1888 || fstatbuf.st_mtime != globals.dvi_file.time /* ... or different timestamp, reload: */) {
1889 dviErrFlagT errflag = NO_ERROR;
1890
1891 TRACE_FILES((stderr, "Stat failed, or different timestamp ..."));
1892 globals.dvi_file.time = 0; /* force reload next time also if stat failed */
1893
1894 if (resource.use_temp_fp) { /* in this case, reload only if file has been written completely */
1895 if (resource.watch_file == 0.0) {
1896 statusline_info(STATUS_MEDIUM, "File corrupted (click on window to reload) ...");
1897 globals.cursor.flags |= CURSOR_CORRUPTED;
1898 globals.ev.flags |= EV_CURSOR;
1899 }
1900 else {
1901 statusline_info(STATUS_MEDIUM, "File corrupted (will try to reload) ...");
1902 }
1903 close_old_filep();
1904 if (file_exists_p(globals.dvi_name, &errflag)
1905 && process_preamble(m_dvi_fp, &errflag)
1906 && find_postamble(m_dvi_fp, &errflag)
1907 && read_postamble(m_dvi_fp, &errflag, False
1908 #if DELAYED_MKTEXPK
1909 , False
1910 #endif
1911 )) {
1912 TRACE_FILES((stderr, "File OK, reloading ..."));
1913
1914 globals.ev.flags |= EV_RELOAD;
1915 return True;
1916 }
1917 else {
1918 TRACE_FILES((stderr, "NO successful load: %s", get_dvi_error(errflag)));
1919 /* fprintf(stderr, "=========== NO successful load: %s\n", get_dvi_error(errflag)); */
1920 return False;
1921 }
1922 }
1923 else {
1924 TRACE_FILES((stderr, "Not using temp fp, reloading..."));
1925 globals.ev.flags |= EV_RELOAD;
1926 return True;
1927 }
1928 }
1929 return False;
1930 }
1931
1932 /**
1933 ** Reload the dvi file (unconditionally). Return True on success, False else.
1934 **/
1935
1936 Boolean
1937 load_dvi_file(
1938 #if !DELAYED_MKTEXPK
1939 Boolean load_fonts,
1940 #endif
1941 dviErrFlagT *errflag)
1942 {
1943 unsigned int old_page_w, old_page_h;
1944 static ino_t dvi_inode = 0;
1945
1946 TRACE_FILES((stderr, "load_dvi_file: going to read %p", (void *)m_dvi_fp));
1947
1948 if (resource.use_temp_fp && m_dvi_fp != NULL) {
1949 /* in this case, reload only if file has been written completely */
1950 *errflag = NO_ERROR;
1951 fseek(m_dvi_fp, 0L, SEEK_SET);
1952 if (!process_preamble(m_dvi_fp, errflag)
1953 || !find_postamble(m_dvi_fp, errflag)
1954 || !read_postamble(m_dvi_fp, errflag,
1955 #if DELAYED_MKTEXPK
1956 False, False
1957 #else
1958 True
1959 #endif
1960 )) {
1961 TRACE_FILES((stderr, "reading of %p failed: %s!",
1962 (void *)m_dvi_fp,
1963 get_dvi_error(*errflag)));
1964 return False;
1965 }
1966 }
1967
1968 old_page_w = globals.page.w;
1969 old_page_h = globals.page.h;
1970
1971 *errflag = NO_ERROR;
1972
1973 #if DELAYED_MKTEXPK
1974 /* use same trick with reading postamble twice to first find names of PK fonts
1975 that need to be created. */
1976 reset_missing_font_count();
1977 kpse_set_program_enabled(kpse_any_glyph_format, False, kpse_src_compile);
1978 #endif
1979
1980 if (!internal_open_dvi(globals.dvi_name, errflag,
1981 #if DELAYED_MKTEXPK
1982 True, False
1983 #else
1984 load_fonts
1985 #endif
1986 )) {
1987 XClearWindow(DISP, mane.win);
1988 xdvi_bell();
1989 statusline_info(STATUS_MEDIUM, "%s: %s%s", globals.dvi_name, get_dvi_error(*errflag),
1990 resource.watch_file > 0.0 ? "; will try to reload ..." : " (click on window to reload)");
1991 close_old_filep();
1992
1993 return False;
1994 }
1995 #if DELAYED_MKTEXPK
1996 /* second time */
1997 kpse_set_program_enabled(kpse_any_glyph_format, True, kpse_src_compile);
1998
1999 if (!internal_open_dvi(globals.dvi_name, errflag, True, True)) {
2000 XClearWindow(DISP, mane.win);
2001 xdvi_bell();
2002 statusline_info(STATUS_MEDIUM, "%s: %s%s", globals.dvi_name, get_dvi_error(*errflag),
2003 resource.watch_file > 0.0 ? "; will try to reload ..." : " (click on window to reload)");
2004 close_old_filep();
2005 return False;
2006 }
2007 #endif
2008 else { /* success */
2009 if (fstatbuf.st_ino != dvi_inode) {
2010 dvi_inode = fstatbuf.st_ino;
2011 form_dvi_property();
2012 set_dvi_property();
2013 }
2014 if (globals.page.w != old_page_w || globals.page.h != old_page_h)
2015 reconfig();
2016
2017 htex_reinit();
2018
2019 globals.cursor.flags &= ~CURSOR_CORRUPTED;
2020 return True;
2021 }
2022 }
2023