diff options
Diffstat (limited to 'Build/source/texk/xdvik/dvisel.c')
-rw-r--r-- | Build/source/texk/xdvik/dvisel.c | 759 |
1 files changed, 759 insertions, 0 deletions
diff --git a/Build/source/texk/xdvik/dvisel.c b/Build/source/texk/xdvik/dvisel.c new file mode 100644 index 00000000000..d6b6b7f6793 --- /dev/null +++ b/Build/source/texk/xdvik/dvisel.c @@ -0,0 +1,759 @@ +/* + * select pages from a DVI file. + * + * Copyright (c) 1999 + * WATANABE Takeshi watanabe@komadori.planet.sci.kobe-u.ac.jp + * Copyright (c) 2002-2004 the xdvik development team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL ANY AUTHO OF THIS SOFTWARE BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <string.h> +#include <ctype.h> + +#include "dvi.h" +#include "pagesel.h" +#include "dvisel.h" +#include "util.h" +#include "dvi-init.h" +#include "events.h" +#include "dvi-draw.h" +#include "message-window.h" +#include "statusline.h" +#include "print-dialog.h" + +/* stack for HTEX / Color stack */ +typedef enum { IS_A_HREF, IS_A_COLOR } stackElemT; + +/* + * Length of BOP command, without p[4] (previous BOP pointer): + * bop c_0[4] ... c_9[4] + */ +static const int BOP_PART_LEN = 1 + 10 * 4; +/* + * Like above but with p[4]: + */ +static const int BOP_LEN = 1 + 10 * 4 + 4; +/* + * Lenght of postamble: + * post p[4] num[4] den[4] mag[4] l[4] u[4] s[2] t[2] + */ +static const int POSTAMBLE_LEN = 1 + 4 * 6 + 2 + 2; +/* + * Postamble without p[4] (final bop offset) and t[2] (total page number): + * num[4] den[4] mag[4] l[4] u[4] s[2] + */ +static const int POSTAMBLE_PART_LEN = 4 * 5 + 2; + +static char *dvips_papersize_special = NULL; + +/* + * Struct to save specials that need to be copied verbatim into the output file + * (things like `draftcopy'; see dvips manual, `Literal headers') + */ +struct literal_headers { + size_t size; + char **headers; +}; +static struct literal_headers literal_headers = { 0, NULL }; + +static struct specials_stack color_stack = { 0, NULL}; +static struct specials_stack href_stack = { 0, NULL}; + +/* + stacks saved from the previous page (i.e. active at the + beginning of the page) + */ +static struct specials_stack color_save_stack = { 0, NULL }; +static struct specials_stack href_save_stack = { 0, NULL }; + +static void +push_stack(struct specials_stack *stack, const char *content) +{ + stack->items = xrealloc(stack->items, (stack->stack_len + 1) * sizeof *(stack->items)); + stack->items[stack->stack_len].content = xstrdup(content); + stack->stack_len++; +} + +static Boolean +stack_contains_item(struct specials_stack *stack, const char *str) +{ + size_t i; + for (i = 0; i < stack->stack_len; i++) { + if (strcmp(str, stack->items[i].content) == 0) { + return True; + } + } + return False; +} + +static void +pop_stack(struct specials_stack *stack) +{ + ASSERT(stack->stack_len >= 1, "Attempt to pop empty stack"); + free(stack->items[stack->stack_len - 1].content); + stack->stack_len--; +} + +static void +empty_stack(struct specials_stack *stack) +{ + while(stack->stack_len > 0) { + pop_stack(stack); + } +} + +/* + This is used instead of struct tn, and struct font* from dvi-init.h, + since it appears that we don't need all the information from there. +*/ +static struct fontinfo { + long TeXnumber; + unsigned char info[14]; + char *fontname; + struct fontinfo *next; + Boolean used; +} *fontinfo_head; + +#define PutFour(num, fp) { \ + putc(((num) >> 24) & 0xff, (fp)); \ + putc(((num) >> 16) & 0xff, (fp)); \ + putc(((num) >> 8) & 0xff, (fp)); \ + putc( (num) & 0xff, (fp)); \ +} + +#define CopyNum(fin, fout, num) { \ + int m = num; \ + while (--m >= 0) putc(getc(fin), fout); \ +} + +static void +FontWrite(FILE *fout, struct fontinfo *fontp, long *fout_pos) +{ + if (fontp->TeXnumber > 0xff) { + if (fontp->TeXnumber > 0xffff) { + if (fontp->TeXnumber > 0xffffff) { + putc(FNTDEF4, fout); + putc((fontp->TeXnumber >> 24) & 0xff, fout); + (*fout_pos)++; + } + else { + putc(FNTDEF3, fout); + } + putc((fontp->TeXnumber >> 16) & 0xff, fout); + (*fout_pos)++; + } + else { + putc(FNTDEF2, fout); + } + putc((fontp->TeXnumber >> 8) & 0xff, fout); + (*fout_pos)++; + } + else { + putc(FNTDEF1, fout); + } + putc(fontp->TeXnumber & 0xff, fout); + fwrite(fontp->info, sizeof(char), 14, fout); + fwrite(fontp->fontname, sizeof(char), fontp->info[12] + fontp->info[13], fout); + (*fout_pos) += 2 + 14 + fontp->info[12] + fontp->info[13]; +} + +/* + Invoked by spcl_scan; saves file names referenced by special commands + into the `warn_files' field of `info'. +*/ +static Boolean +scan_for_included_files(char *special, int str_len, void *my_info) +{ + struct select_pages_info *info = (struct select_pages_info *)my_info; + UNUSED(str_len); + if (info->warn_files.items == NULL) /* no info needed, e.g. when printing */ + return True; + + while (*special == ' ' || *special == '\t') + ++special; + + if (memicmp(special, "psfile=", strlen("psfile=")) == 0) { + char *b_ptr = special + strlen("psfile="); + char *e_ptr; + + while (*b_ptr == '"') + b_ptr++; + + if ((e_ptr = strchr(b_ptr, '"')) != NULL) { + size_t len = e_ptr - b_ptr; + char *tmp = xmalloc(len + 1); + memcpy(tmp, b_ptr, len); + tmp[len] = '\0'; + if (!stack_contains_item(&(info->warn_files), tmp)) { + push_stack(&(info->warn_files), tmp); + } + free(tmp); + } + } + + return True; /* dummy */ +} + +/* + Invoked by spcl_scan; saves hyperref and color commands + into the global stacks `color_stack' and `hyperref_stack'. + FIXME: In reality char *special is treated as const, but the declaration + of spcl_scan doesn't allow for this. +*/ +static Boolean +stack_save(char *special, int str_len, void *data) +{ + char *ptr; + size_t len; + + UNUSED(data); + UNUSED(str_len); + + while (isspace((int)*special)) + ++special; + + ptr = special; + + if (memicmp(special, "color ", len = strlen("color ")) == 0) { + special += len; + while (*special == ' ' || *special == '\t') + ++special; + if (memicmp(special, "push ", 5) == 0) { + push_stack(&color_stack, ptr); + } + else if (memicmp(special, "pop", 3) == 0) { + pop_stack(&color_stack); + } + } + else if (memicmp(special, "html:", len = strlen("html:")) == 0) { + /* fprintf(stderr, "++++ special: %s; fd: %ld\n", special + 5, ftell(globals.dvi_file.bak_fp)); */ + special += len; + if (memicmp(special, "<a href=", 8) == 0 + || memicmp(special, "<a name=", 8) == 0) { + push_stack(&href_stack, ptr); + } + else if (memicmp(special, "</a>", 4) == 0) { + pop_stack(&href_stack); + } + } + else if (memicmp(special, "papersize", len = strlen("papersize")) == 0) { + free(dvips_papersize_special); + dvips_papersize_special = xstrdup(special); + } + else if (*special == '!' + || memcmp(special, "header", strlen("header")) == 0) { + size_t idx = literal_headers.size++; + if (globals.debug & DBG_GUI) + fprintf(stderr, "(literal) header %lu: |%s|\n", (unsigned long)idx, special); + literal_headers.headers = xrealloc(literal_headers.headers, + literal_headers.size * sizeof *(literal_headers.headers)); + literal_headers.headers[idx] = xstrdup(special); + } + return True; /* dummy */ +} + +/* write a special to the FILE *fp, updating offset as appropriate. */ +static void +write_special(const char *str, FILE *fp, long *offset) +{ + unsigned int len = strlen(str); + if (len < 256) { + unsigned char c = len; + putc(XXX1, fp); + putc(c, fp); + *offset += 2; + } + else { /* how about xxx2, xxx3? It seems that TeX doesn't use them either? */ + putc(XXX4, fp); + PutFour(len, fp); + *offset += 5; + } + fputs(str, fp); + *offset += len; +} + +/* + * Dump the headers in the global list `literal_headers', + * and free up resources + */ +static void +dump_literal_headers(FILE *fp, long *pos) +{ + size_t i; + for (i = 0; i < literal_headers.size; i++) { + write_special(literal_headers.headers[i], fp, pos); + free(literal_headers.headers[i]); + } + free(literal_headers.headers); + literal_headers.headers = NULL; + literal_headers.size = 0; +} + + +static void +scan_page_for_specials(FILE *fp, int page, Boolean save_stack, + Boolean (*special_proc)(char *str, int str_len, void *data), + struct select_pages_info *info) +{ + off_t pos_save; + static ubyte my_scan_buffer[DVI_BUFFER_LEN]; + + struct drawinf currinf_save; + ubyte maxchar_save; + size_t i; + TRACE_GUI((stderr, "parsing page: %d", page)); + if (save_stack) { + /* + first, copy the current stacks (which reflect the state on the + preceding page) to color_save_stack and href_save_stack: + */ + empty_stack(&color_save_stack); + empty_stack(&href_save_stack); + for (i = 0; i < color_stack.stack_len; i++) { + TRACE_GUI((stderr, "saving stack: |%s|", color_stack.items[i].content)); + push_stack(&color_save_stack, color_stack.items[i].content); + } + for (i = 0; i < href_stack.stack_len; i++) { + TRACE_GUI((stderr, "saving stack: |%s|", href_stack.items[i].content)); + push_stack(&href_save_stack, href_stack.items[i].content); + } + } + + /* (void)fseek(fp, pageinfo_get_offset(page), SEEK_SET); */ + /* + The datastructures in dvi-draw are rather weird in requiring + us to provide a buffer, and point the global currinf.pos and end + pointers to that buffer, for spcl_scan to be able to scan the file. + First save the contents of the exising currinf, pointing to the main file: + */ + pos_save = save_file_status(fp, &currinf_save, &maxchar_save); + + lseek(fileno(fp), pageinfo_get_offset(page), SEEK_SET); + memset((char *)&currinf.data, 0, sizeof currinf.data); + currinf.tn_table_len = TNTABLELEN; + currinf.tn_table = tn_table; + currinf.tn_head = tn_head; + + /* then point currinf to our own buffer: */ + G_dvi_buf_ptr = my_scan_buffer; + currinf.pos = currinf.end = G_dvi_buf_ptr; + lseek(fileno(fp), pageinfo_get_offset(page), SEEK_SET); + /* + finally we may invoke spcl_scan(). I hope we can do without + the + (!setjmp(some_env)) + black magic since there shouldn't be any interruptions ... + */ +#if 1 + spcl_scan(special_proc, info, False, fp); +#else + /* this is an attempt at using setjmp(), but it's just too ugly ... */ + for (;;) { + int page_bak; + if (read_events(EV_NOWAIT) & EV_GE_NEWPAGE) + break; + page_bak = page; + if (!setjmp(globals.ev.canit)) { + fprintf(stderr, "current position: %lu; seeking to: %lu\n", + (unsigned long)pos_save, + (unsigned long)pageinfo_get_offset(page)); + (void) fprintf(stderr, "seeking to offset %ld, page %d of file %d\n", + pageinfo_get_offset(page), + page, fileno(globals.dvi_file.bak_fp)); + fseek(fp, pageinfo_get_offset(page), SEEK_SET); + spcl_scan(special_proc, info, False); + } + else { /* if interrupted */ + fprintf(stderr, "========= interrupted!\n"); + if (page >= page_bak) + page = page_bak; + break; + } + if (page >= current_page) + break; + page++; + } +#endif + /* now we restore those pesky globals, hoping that nobody + has stamped upon them in the meantime. */ + restore_file_status(fp, currinf_save, maxchar_save, pos_save); + + /* reposition file pointer */ + (void)fseek(fp, pageinfo_get_offset(page), SEEK_SET); +} + + +/* dump the `open' commands on the current stack to the FILE *fp */ +static void +stack_dump_open_commands(FILE *fp, long *pos) +{ + size_t i; + TRACE_GUI((stderr, "length of stacks: %lu, %lu", + (unsigned long)color_save_stack.stack_len, + (unsigned long)href_save_stack.stack_len)); + for (i = 0; i < color_save_stack.stack_len; i++) { + TRACE_GUI((stderr, "dumping: |%s|", color_save_stack.items[i].content)); + write_special(color_save_stack.items[i].content, fp, pos); + } + for (i = 0; i < href_save_stack.stack_len; i++) { + TRACE_GUI((stderr, "dumping: |%s|", href_save_stack.items[i].content)); + write_special(href_save_stack.items[i].content, fp, pos); + } + if (dvips_papersize_special != NULL) + write_special(dvips_papersize_special, fp, pos); + TRACE_GUI((stderr, "end of dumping open\n")); +} + +/* dump `close' commands for all the `open' commands on the + current stack (not the saved one, but the one active at the end of the page) + to the FILE *fp (closing tags for hyperref anchors, + `pop' commands for color specials). +*/ +static void +stack_dump_close_commands(FILE *fp, long *pos) +{ + size_t i; + + for (i = color_stack.stack_len; i > 0; i--) { + TRACE_GUI((stderr, "===== color pop")); + write_special("color pop", fp, pos); + } + for (i = href_stack.stack_len; i > 0; i--) { + TRACE_GUI((stderr, "===== html:</a>")); + write_special("html:</a>", fp, pos); + } +} + + +static void +WriteDVI(FILE *fin, FILE *fout, long *fout_pos, int c) +{ + int i, n; + struct fontinfo *fontp; + + if (c >= FNTNUM0 && c <= FNT4) { + if (c >= FNT1) + n = get_bytes(fin, c - FNT1 + 1); + else + n = c - FNTNUM0; + for (fontp = fontinfo_head; fontp; fontp = fontp->next) + if (n == fontp->TeXnumber) + break; + if (fontp && fontp->used == False) { + fontp->used = True; + FontWrite(fout, fontp, fout_pos); + } + putc(c, fout); + (*fout_pos)++; + switch (c) { + case FNT4: + putc((n >> 24) & 0xff, fout); + (*fout_pos)++; + case FNT3: + putc((n >> 16) & 0xff, fout); + (*fout_pos)++; + case FNT2: + putc((n >> 8) & 0xff, fout); + (*fout_pos)++; + case FNT1: + putc(n & 0xff, fout); + (*fout_pos)++; + default: + break; + } + } + else if (c >= FNTDEF1 && c <= FNTDEF4) { + n = get_bytes(fin, c - FNTDEF1 + 1); + for (fontp = fontinfo_head; fontp; fontp = fontp->next) + if (n == fontp->TeXnumber) + break; + if (fontp && fontp->used == False) { + fontp->used = True; + FontWrite(fout, fontp, fout_pos); + } + (void) get_bytes(fin, 12); + (void) get_bytes(fin, (int)(get_byte(fin) + get_byte(fin))); + } + else { + putc(c, fout); + (*fout_pos)++; + n = 0; + if (c <= XXX4) { + for (i = 0; i < c - XXX1 + 1; i++) { + int x = get_byte(fin); + putc(x, fout); + (*fout_pos)++; + n = (n << 8) | x; + } + } + switch (c) { + case SETRULE: + case PUTRULE: + n += 4; + /* fall through */ + + case RIGHT4: + case W4: + case X4: + case DOWN4: + case Y4: + case Z4: + n++; + /* fall through */ + + case RIGHT3: + case W3: + case X3: + case DOWN3: + case Y3: + case Z3: + n++; + /* fall through */ + + case SET2: + case PUT2: + if (!resource.omega) { + dvi_fmt_error("%s:%d: WriteDVI: op-code %d only works with the \"-omega\" option", + __FILE__, __LINE__, c); + } + case RIGHT2: + case W2: + case X2: + case DOWN2: + case Y2: + case Z2: + n++; + /* fall through */ + + case SET1: + case PUT1: + case RIGHT1: + case W1: + case X1: + case DOWN1: + case Y1: + case Z1: +#ifdef PTEX + case TDIR: +#endif + n++; + /* fall through */ + + case XXX1: + case XXX2: + case XXX3: + case XXX4: + CopyNum(fin, fout, n); + (*fout_pos) += n; + /* fall through */ + + default: + break; + } + } +} + +/* public functions */ + + +/* callback functions for selecting pages */ +Boolean +check_pagerange(struct select_pages_info *info, int page) +{ + return (page >= info->from && page <= info->to); +} + +Boolean +check_marked(struct select_pages_info *info, int page) +{ + UNUSED(info); + return pageinfo_is_marked(page); +} + +void +select_pages(struct select_pages_info *pinfo) +{ + int c, n, page, pagecnt; + long fout_pos = 0L; + unsigned long curr_page_offset = 0xffffffff; /* pattern to be overwritten later */ + unsigned long post_cmd_offset = 0xffffffff; /* pattern to be overwritten later */ + struct fontinfo *fontp; + FILE *in_fp = pinfo->finfo->dvi_in.fp; + FILE *out_fp = pinfo->finfo->dvi_tmp.fp; + + Boolean headers_dumped = False; + free(dvips_papersize_special); /* re-initialize */ + dvips_papersize_special = NULL; + + ASSERT(in_fp != NULL, "input file mustn't be NULL in select_pages()!"); + ASSERT(out_fp != NULL, "output file mustn't be NULL in select_pages()!"); + + pinfo->errflag = NO_ERROR; /* reset errflag, in case we're recovering from a previous error */ + + /* get font list from postamble; in_fp already has been positioned + at correct position (start of postamble) in caller. */ + (void)fseek(in_fp, ftell(in_fp), SEEK_SET); /* paranoia */ + (void)get_bytes(in_fp, POSTAMBLE_LEN); + fontinfo_head = NULL; + for (;;) { + if ((c = get_byte(in_fp)) < FNTDEF1 || c > FNTDEF4) /* maybe POSTPOST */ + break; + fontp = (struct fontinfo *) xmalloc(sizeof(struct fontinfo)); + fontp->TeXnumber = get_bytes(in_fp, c - FNTDEF1 + 1); + fread(fontp->info, sizeof(char), 14, in_fp); + n = fontp->info[12] + fontp->info[13]; + fontp->fontname = xmalloc(n); + fread(fontp->fontname, sizeof(char), n, in_fp); + fontp->next = fontinfo_head; + fontinfo_head = fontp; + fontp->used = False; + } + + /* preamble */ + fseek(in_fp, 0L, SEEK_SET); + fout_pos = pageinfo_get_offset(0); + CopyNum(in_fp, out_fp, fout_pos); + + /* each page */ + pagecnt = 0; + for (page = 0; page < total_pages; page++) { + scan_page_for_specials(in_fp, page, True, stack_save, NULL); + + /* should the current page be selected? */ + if (pinfo->callback == NULL || pinfo->callback(pinfo, page)) { + scan_page_for_specials(in_fp, page, False, scan_for_included_files, pinfo); + + /* read BOP except for p[4] */ + CopyNum(in_fp, out_fp, BOP_PART_LEN); + /* read p[4] (previous bop pointer */ + (void)get_bytes(in_fp, 4); + /* write actual value of p[4] */ + PutFour(curr_page_offset, out_fp); + /* remember offset of current page, for postamble */ + curr_page_offset = fout_pos; + /* update fout_pos to current position */ + fout_pos += BOP_LEN; + + if (!headers_dumped) { + headers_dumped = True; + dump_literal_headers(out_fp, &fout_pos); + } + + /* if (!written_pre_cmds) { */ + stack_dump_open_commands(out_fp, &fout_pos); +/* written_pre_cmds = True; */ +/* } */ + + while ((c = getc(in_fp)) != EOF) { + if ((c & 0x80) == 0) { /* ordinary character */ + putc(c, out_fp); + fout_pos++; + } + else if (c == EOP) { /* End Of Page */ +/* fprintf(stderr, "EOP at %ld\n", ftell(fin)); */ + +/* if (page == to) { */ /* print close stack for last page */ + stack_dump_close_commands(out_fp, &fout_pos); +/* } */ + + putc(c, out_fp); + fout_pos++; + break; + } + else { +/* fprintf(stderr, "WRITE_DVI before: %ld, in_fp before: %ld\n", fout_pos, ftell(in_fp)); */ + WriteDVI(in_fp, out_fp, &fout_pos, c); +/* fprintf(stderr, "WRITE_DVI after: %ld; in_fp after: %ld\n", fout_pos, ftell(in_fp)); */ + } + /* HACK alert: force synchronization - why is this neccessary? + Seems to have something do with the fact that two FILE *'s on the same + file are open. POSIX.1, + `Interaction of File Descriptors and Standard I/O Streams' seems to + say the seek()s on one of them also affect the other, but I'm not sure + about this. + */ + fseek(in_fp, 0L, SEEK_CUR); + } + pagecnt++; + } + } + + /* postamble */ + if (!find_postamble(in_fp, &(pinfo->errflag))) { + return; + } + + /* read/write POST command */ + putc(get_byte(in_fp), out_fp); + /* read over last page offset */ + get_bytes(in_fp, 4); + /* write last page offset */ + PutFour(curr_page_offset, out_fp); + /* remember start of POST */ + post_cmd_offset = fout_pos; + /* copy part of postamble */ + CopyNum(in_fp, out_fp, POSTAMBLE_PART_LEN); + /* read t[2], total number of pages */ + get_bytes(in_fp, 2); + /* write t[2] (two calls) */ + putc((pagecnt >> 8) & 0xff, out_fp); + putc(pagecnt & 0xff, out_fp); + /* update fout_pos to current pos */ + fout_pos += POSTAMBLE_LEN; + + /* output font list */ + for (;;) { + if ((c = get_byte(in_fp)) < FNTDEF1 || c > FNTDEF4) /* maybe POSTPOST */ + break; + n = get_bytes(in_fp, c - FNTDEF1 + 1); + for (fontp = fontinfo_head; fontp; fontp = fontp->next) + if (n == fontp->TeXnumber) + break; + if (fontp && fontp->used == True) + FontWrite(out_fp, fontp, &fout_pos); + (void) get_bytes(in_fp, 12); + (void) get_bytes(in_fp, (int)(get_byte(in_fp) + get_byte(in_fp))); + } + + /* free list */ + for (fontp = fontinfo_head; fontp;) { + struct fontinfo *nextp; + free(fontp->fontname); + nextp = fontp->next; + free(fontp); + fontp = nextp; + } + + /* POSTPOST */ + putc(c, out_fp); + get_bytes(in_fp, 4); + PutFour(post_cmd_offset, out_fp); + CopyNum(in_fp, out_fp, 1 + 4); + for (fout_pos += 1 + 4 + 1 + 4; + fout_pos & 3; + fout_pos++) { + putc(TRAILER, out_fp); + } + +/* fclose(in_fp); */ + fflush(out_fp); +} + |