diff options
author | Taco Hoekwater <taco@elvenkind.com> | 2009-03-27 15:30:55 +0000 |
---|---|---|
committer | Taco Hoekwater <taco@elvenkind.com> | 2009-03-27 15:30:55 +0000 |
commit | 178de0871d690556af74f3768c11bc812b07f347 (patch) | |
tree | a939c31adc90d6207848effaec87dd78ec00e658 /Build/source/texk/web2c/luatexdir/utils | |
parent | 4865b23b5199697829e4e6633f2f697b4634c462 (diff) |
Import of luatex 0.37.0 (autoreconf has not been run yet!)
git-svn-id: svn://tug.org/texlive/trunk@12529 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/texk/web2c/luatexdir/utils')
-rw-r--r-- | Build/source/texk/web2c/luatexdir/utils/avl.c | 794 | ||||
-rw-r--r-- | Build/source/texk/web2c/luatexdir/utils/avl.h | 115 | ||||
-rw-r--r-- | Build/source/texk/web2c/luatexdir/utils/avlstuff.c | 190 | ||||
-rw-r--r-- | Build/source/texk/web2c/luatexdir/utils/avlstuff.h | 27 | ||||
-rw-r--r-- | Build/source/texk/web2c/luatexdir/utils/synctex.c | 1493 | ||||
-rw-r--r-- | Build/source/texk/web2c/luatexdir/utils/synctex.h | 113 | ||||
-rw-r--r-- | Build/source/texk/web2c/luatexdir/utils/utils.c | 1802 | ||||
-rw-r--r-- | Build/source/texk/web2c/luatexdir/utils/writezip.c | 99 |
8 files changed, 4633 insertions, 0 deletions
diff --git a/Build/source/texk/web2c/luatexdir/utils/avl.c b/Build/source/texk/web2c/luatexdir/utils/avl.c new file mode 100644 index 00000000000..79c746cd10d --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/utils/avl.c @@ -0,0 +1,794 @@ +/* Produced by texiweb from libavl.w. */ + +/* libavl - library for manipulation of binary trees. + Copyright (C) 1998-2002, 2004 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. + + The author may be contacted at <blp@gnu.org> on the Internet, or + write to Ben Pfaff, Stanford University, Computer Science Dept., 353 + Serra Mall, Stanford CA 94305, USA. +*/ + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "avl.h" + +/* Creates and returns a new table + with comparison function |compare| using parameter |param| + and memory allocator |allocator|. + Returns |NULL| if memory allocation failed. */ +struct avl_table *avl_create(avl_comparison_func * compare, void *param, + struct libavl_allocator *allocator) +{ + struct avl_table *tree; + + assert(compare != NULL); + + if (allocator == NULL) + allocator = &avl_allocator_default; + + tree = allocator->libavl_malloc(allocator, sizeof *tree); + if (tree == NULL) + return NULL; + + tree->avl_root = NULL; + tree->avl_compare = compare; + tree->avl_param = param; + tree->avl_alloc = allocator; + tree->avl_count = 0; + tree->avl_generation = 0; + + return tree; +} + +/* Search |tree| for an item matching |item|, and return it if found. + Otherwise return |NULL|. */ +void *avl_find(const struct avl_table *tree, const void *item) +{ + const struct avl_node *p; + + assert(tree != NULL && item != NULL); + for (p = tree->avl_root; p != NULL;) { + int cmp = tree->avl_compare(item, p->avl_data, tree->avl_param); + + if (cmp < 0) + p = p->avl_link[0]; + else if (cmp > 0) + p = p->avl_link[1]; + else /* |cmp == 0| */ + return p->avl_data; + } + + return NULL; +} + +/* Inserts |item| into |tree| and returns a pointer to |item|'s address. + If a duplicate item is found in the tree, + returns a pointer to the duplicate without inserting |item|. + Returns |NULL| in case of memory allocation failure. */ +void **avl_probe(struct avl_table *tree, void *item) +{ + struct avl_node *y, *z; /* Top node to update balance factor, and parent. */ + struct avl_node *p, *q; /* Iterator, and parent. */ + struct avl_node *n; /* Newly inserted node. */ + struct avl_node *w; /* New root of rebalanced subtree. */ + int dir; /* Direction to descend. */ + + unsigned char da[AVL_MAX_HEIGHT]; /* Cached comparison results. */ + int k = 0; /* Number of cached results. */ + + assert(tree != NULL && item != NULL); + + z = (struct avl_node *) &tree->avl_root; + y = tree->avl_root; + dir = 0; + for (q = z, p = y; p != NULL; q = p, p = p->avl_link[dir]) { + int cmp = tree->avl_compare(item, p->avl_data, tree->avl_param); + if (cmp == 0) + return &p->avl_data; + + if (p->avl_balance != 0) + z = q, y = p, k = 0; + da[k++] = dir = cmp > 0; + } + + n = q->avl_link[dir] = + tree->avl_alloc->libavl_malloc(tree->avl_alloc, sizeof *n); + if (n == NULL) + return NULL; + + tree->avl_count++; + n->avl_data = item; + n->avl_link[0] = n->avl_link[1] = NULL; + n->avl_balance = 0; + if (y == NULL) + return &n->avl_data; + + for (p = y, k = 0; p != n; p = p->avl_link[da[k]], k++) + if (da[k] == 0) + p->avl_balance--; + else + p->avl_balance++; + + if (y->avl_balance == -2) { + struct avl_node *x = y->avl_link[0]; + if (x->avl_balance == -1) { + w = x; + y->avl_link[0] = x->avl_link[1]; + x->avl_link[1] = y; + x->avl_balance = y->avl_balance = 0; + } else { + assert(x->avl_balance == +1); + w = x->avl_link[1]; + x->avl_link[1] = w->avl_link[0]; + w->avl_link[0] = x; + y->avl_link[0] = w->avl_link[1]; + w->avl_link[1] = y; + if (w->avl_balance == -1) + x->avl_balance = 0, y->avl_balance = +1; + else if (w->avl_balance == 0) + x->avl_balance = y->avl_balance = 0; + else /* |w->avl_balance == +1| */ + x->avl_balance = -1, y->avl_balance = 0; + w->avl_balance = 0; + } + } else if (y->avl_balance == +2) { + struct avl_node *x = y->avl_link[1]; + if (x->avl_balance == +1) { + w = x; + y->avl_link[1] = x->avl_link[0]; + x->avl_link[0] = y; + x->avl_balance = y->avl_balance = 0; + } else { + assert(x->avl_balance == -1); + w = x->avl_link[0]; + x->avl_link[0] = w->avl_link[1]; + w->avl_link[1] = x; + y->avl_link[1] = w->avl_link[0]; + w->avl_link[0] = y; + if (w->avl_balance == +1) + x->avl_balance = 0, y->avl_balance = -1; + else if (w->avl_balance == 0) + x->avl_balance = y->avl_balance = 0; + else /* |w->avl_balance == -1| */ + x->avl_balance = +1, y->avl_balance = 0; + w->avl_balance = 0; + } + } else + return &n->avl_data; + z->avl_link[y != z->avl_link[0]] = w; + + tree->avl_generation++; + return &n->avl_data; +} + +/* Inserts |item| into |table|. + Returns |NULL| if |item| was successfully inserted + or if a memory allocation error occurred. + Otherwise, returns the duplicate item. */ +void *avl_insert(struct avl_table *table, void *item) +{ + void **p = avl_probe(table, item); + return p == NULL || *p == item ? NULL : *p; +} + +/* Inserts |item| into |table|, replacing any duplicate item. + Returns |NULL| if |item| was inserted without replacing a duplicate, + or if a memory allocation error occurred. + Otherwise, returns the item that was replaced. */ +void *avl_replace(struct avl_table *table, void *item) +{ + void **p = avl_probe(table, item); + if (p == NULL || *p == item) + return NULL; + else { + void *r = *p; + *p = item; + return r; + } +} + +/* Deletes from |tree| and returns an item matching |item|. + Returns a null pointer if no matching item found. */ +void *avl_delete(struct avl_table *tree, const void *item) +{ + /* Stack of nodes. */ + struct avl_node *pa[AVL_MAX_HEIGHT]; /* Nodes. */ + unsigned char da[AVL_MAX_HEIGHT]; /* |avl_link[]| indexes. */ + int k; /* Stack pointer. */ + + struct avl_node *p; /* Traverses tree to find node to delete. */ + int cmp; /* Result of comparison between |item| and |p|. */ + + assert(tree != NULL && item != NULL); + + k = 0; + p = (struct avl_node *) &tree->avl_root; + for (cmp = -1; cmp != 0; + cmp = tree->avl_compare(item, p->avl_data, tree->avl_param)) { + int dir = cmp > 0; + + pa[k] = p; + da[k++] = dir; + + p = p->avl_link[dir]; + if (p == NULL) + return NULL; + } + item = p->avl_data; + + if (p->avl_link[1] == NULL) + pa[k - 1]->avl_link[da[k - 1]] = p->avl_link[0]; + else { + struct avl_node *r = p->avl_link[1]; + if (r->avl_link[0] == NULL) { + r->avl_link[0] = p->avl_link[0]; + r->avl_balance = p->avl_balance; + pa[k - 1]->avl_link[da[k - 1]] = r; + da[k] = 1; + pa[k++] = r; + } else { + struct avl_node *s; + int j = k++; + + for (;;) { + da[k] = 0; + pa[k++] = r; + s = r->avl_link[0]; + if (s->avl_link[0] == NULL) + break; + + r = s; + } + + s->avl_link[0] = p->avl_link[0]; + r->avl_link[0] = s->avl_link[1]; + s->avl_link[1] = p->avl_link[1]; + s->avl_balance = p->avl_balance; + + pa[j - 1]->avl_link[da[j - 1]] = s; + da[j] = 1; + pa[j] = s; + } + } + + tree->avl_alloc->libavl_free(tree->avl_alloc, p); + + assert(k > 0); + while (--k > 0) { + struct avl_node *y = pa[k]; + + if (da[k] == 0) { + y->avl_balance++; + if (y->avl_balance == +1) + break; + else if (y->avl_balance == +2) { + struct avl_node *x = y->avl_link[1]; + if (x->avl_balance == -1) { + struct avl_node *w; + assert(x->avl_balance == -1); + w = x->avl_link[0]; + x->avl_link[0] = w->avl_link[1]; + w->avl_link[1] = x; + y->avl_link[1] = w->avl_link[0]; + w->avl_link[0] = y; + if (w->avl_balance == +1) + x->avl_balance = 0, y->avl_balance = -1; + else if (w->avl_balance == 0) + x->avl_balance = y->avl_balance = 0; + else /* |w->avl_balance == -1| */ + x->avl_balance = +1, y->avl_balance = 0; + w->avl_balance = 0; + pa[k - 1]->avl_link[da[k - 1]] = w; + } else { + y->avl_link[1] = x->avl_link[0]; + x->avl_link[0] = y; + pa[k - 1]->avl_link[da[k - 1]] = x; + if (x->avl_balance == 0) { + x->avl_balance = -1; + y->avl_balance = +1; + break; + } else + x->avl_balance = y->avl_balance = 0; + } + } + } else { + y->avl_balance--; + if (y->avl_balance == -1) + break; + else if (y->avl_balance == -2) { + struct avl_node *x = y->avl_link[0]; + if (x->avl_balance == +1) { + struct avl_node *w; + assert(x->avl_balance == +1); + w = x->avl_link[1]; + x->avl_link[1] = w->avl_link[0]; + w->avl_link[0] = x; + y->avl_link[0] = w->avl_link[1]; + w->avl_link[1] = y; + if (w->avl_balance == -1) + x->avl_balance = 0, y->avl_balance = +1; + else if (w->avl_balance == 0) + x->avl_balance = y->avl_balance = 0; + else /* |w->avl_balance == +1| */ + x->avl_balance = -1, y->avl_balance = 0; + w->avl_balance = 0; + pa[k - 1]->avl_link[da[k - 1]] = w; + } else { + y->avl_link[0] = x->avl_link[1]; + x->avl_link[1] = y; + pa[k - 1]->avl_link[da[k - 1]] = x; + if (x->avl_balance == 0) { + x->avl_balance = +1; + y->avl_balance = -1; + break; + } else + x->avl_balance = y->avl_balance = 0; + } + } + } + } + + tree->avl_count--; + tree->avl_generation++; + return (void *) item; +} + +/* Refreshes the stack of parent pointers in |trav| + and updates its generation number. */ +static void trav_refresh(struct avl_traverser *trav) +{ + assert(trav != NULL); + + trav->avl_generation = trav->avl_table->avl_generation; + + if (trav->avl_node != NULL) { + avl_comparison_func *cmp = trav->avl_table->avl_compare; + void *param = trav->avl_table->avl_param; + struct avl_node *node = trav->avl_node; + struct avl_node *i; + + trav->avl_height = 0; + for (i = trav->avl_table->avl_root; i != node;) { + assert(trav->avl_height < AVL_MAX_HEIGHT); + assert(i != NULL); + + trav->avl_stack[trav->avl_height++] = i; + i = i->avl_link[cmp(node->avl_data, i->avl_data, param) > 0]; + } + } +} + +/* Initializes |trav| for use with |tree| + and selects the null node. */ +void avl_t_init(struct avl_traverser *trav, struct avl_table *tree) +{ + trav->avl_table = tree; + trav->avl_node = NULL; + trav->avl_height = 0; + trav->avl_generation = tree->avl_generation; +} + +/* Initializes |trav| for |tree| + and selects and returns a pointer to its least-valued item. + Returns |NULL| if |tree| contains no nodes. */ +void *avl_t_first(struct avl_traverser *trav, struct avl_table *tree) +{ + struct avl_node *x; + + assert(tree != NULL && trav != NULL); + + trav->avl_table = tree; + trav->avl_height = 0; + trav->avl_generation = tree->avl_generation; + + x = tree->avl_root; + if (x != NULL) + while (x->avl_link[0] != NULL) { + assert(trav->avl_height < AVL_MAX_HEIGHT); + trav->avl_stack[trav->avl_height++] = x; + x = x->avl_link[0]; + } + trav->avl_node = x; + + return x != NULL ? x->avl_data : NULL; +} + +/* Initializes |trav| for |tree| + and selects and returns a pointer to its greatest-valued item. + Returns |NULL| if |tree| contains no nodes. */ +void *avl_t_last(struct avl_traverser *trav, struct avl_table *tree) +{ + struct avl_node *x; + + assert(tree != NULL && trav != NULL); + + trav->avl_table = tree; + trav->avl_height = 0; + trav->avl_generation = tree->avl_generation; + + x = tree->avl_root; + if (x != NULL) + while (x->avl_link[1] != NULL) { + assert(trav->avl_height < AVL_MAX_HEIGHT); + trav->avl_stack[trav->avl_height++] = x; + x = x->avl_link[1]; + } + trav->avl_node = x; + + return x != NULL ? x->avl_data : NULL; +} + +/* Searches for |item| in |tree|. + If found, initializes |trav| to the item found and returns the item + as well. + If there is no matching item, initializes |trav| to the null item + and returns |NULL|. */ +void *avl_t_find(struct avl_traverser *trav, struct avl_table *tree, void *item) +{ + struct avl_node *p, *q; + + assert(trav != NULL && tree != NULL && item != NULL); + trav->avl_table = tree; + trav->avl_height = 0; + trav->avl_generation = tree->avl_generation; + for (p = tree->avl_root; p != NULL; p = q) { + int cmp = tree->avl_compare(item, p->avl_data, tree->avl_param); + + if (cmp < 0) + q = p->avl_link[0]; + else if (cmp > 0) + q = p->avl_link[1]; + else { /* |cmp == 0| */ + + trav->avl_node = p; + return p->avl_data; + } + + assert(trav->avl_height < AVL_MAX_HEIGHT); + trav->avl_stack[trav->avl_height++] = p; + } + + trav->avl_height = 0; + trav->avl_node = NULL; + return NULL; +} + +/* Attempts to insert |item| into |tree|. + If |item| is inserted successfully, it is returned and |trav| is + initialized to its location. + If a duplicate is found, it is returned and |trav| is initialized to + its location. No replacement of the item occurs. + If a memory allocation failure occurs, |NULL| is returned and |trav| + is initialized to the null item. */ +void *avl_t_insert(struct avl_traverser *trav, struct avl_table *tree, + void *item) +{ + void **p; + + assert(trav != NULL && tree != NULL && item != NULL); + + p = avl_probe(tree, item); + if (p != NULL) { + trav->avl_table = tree; + trav->avl_node = ((struct avl_node *) + ((char *) p - offsetof(struct avl_node, avl_data))); + trav->avl_generation = tree->avl_generation - 1; + return *p; + } else { + avl_t_init(trav, tree); + return NULL; + } +} + +/* Initializes |trav| to have the same current node as |src|. */ +void *avl_t_copy(struct avl_traverser *trav, const struct avl_traverser *src) +{ + assert(trav != NULL && src != NULL); + + if (trav != src) { + trav->avl_table = src->avl_table; + trav->avl_node = src->avl_node; + trav->avl_generation = src->avl_generation; + if (trav->avl_generation == trav->avl_table->avl_generation) { + trav->avl_height = src->avl_height; + memcpy(trav->avl_stack, (const void *) src->avl_stack, + sizeof *trav->avl_stack * trav->avl_height); + } + } + + return trav->avl_node != NULL ? trav->avl_node->avl_data : NULL; +} + +/* Returns the next data item in inorder + within the tree being traversed with |trav|, + or if there are no more data items returns |NULL|. */ +void *avl_t_next(struct avl_traverser *trav) +{ + struct avl_node *x; + + assert(trav != NULL); + + if (trav->avl_generation != trav->avl_table->avl_generation) + trav_refresh(trav); + + x = trav->avl_node; + if (x == NULL) { + return avl_t_first(trav, trav->avl_table); + } else if (x->avl_link[1] != NULL) { + assert(trav->avl_height < AVL_MAX_HEIGHT); + trav->avl_stack[trav->avl_height++] = x; + x = x->avl_link[1]; + + while (x->avl_link[0] != NULL) { + assert(trav->avl_height < AVL_MAX_HEIGHT); + trav->avl_stack[trav->avl_height++] = x; + x = x->avl_link[0]; + } + } else { + struct avl_node *y; + + do { + if (trav->avl_height == 0) { + trav->avl_node = NULL; + return NULL; + } + + y = x; + x = trav->avl_stack[--trav->avl_height]; + } + while (y == x->avl_link[1]); + } + trav->avl_node = x; + + return x->avl_data; +} + +/* Returns the previous data item in inorder + within the tree being traversed with |trav|, + or if there are no more data items returns |NULL|. */ +void *avl_t_prev(struct avl_traverser *trav) +{ + struct avl_node *x; + + assert(trav != NULL); + + if (trav->avl_generation != trav->avl_table->avl_generation) + trav_refresh(trav); + + x = trav->avl_node; + if (x == NULL) { + return avl_t_last(trav, trav->avl_table); + } else if (x->avl_link[0] != NULL) { + assert(trav->avl_height < AVL_MAX_HEIGHT); + trav->avl_stack[trav->avl_height++] = x; + x = x->avl_link[0]; + + while (x->avl_link[1] != NULL) { + assert(trav->avl_height < AVL_MAX_HEIGHT); + trav->avl_stack[trav->avl_height++] = x; + x = x->avl_link[1]; + } + } else { + struct avl_node *y; + + do { + if (trav->avl_height == 0) { + trav->avl_node = NULL; + return NULL; + } + + y = x; + x = trav->avl_stack[--trav->avl_height]; + } + while (y == x->avl_link[0]); + } + trav->avl_node = x; + + return x->avl_data; +} + +/* Returns |trav|'s current item. */ +void *avl_t_cur(struct avl_traverser *trav) +{ + assert(trav != NULL); + + return trav->avl_node != NULL ? trav->avl_node->avl_data : NULL; +} + +/* Replaces the current item in |trav| by |new| and returns the item replaced. + |trav| must not have the null item selected. + The new item must not upset the ordering of the tree. */ +void *avl_t_replace(struct avl_traverser *trav, void *new) +{ + void *old; + + assert(trav != NULL && trav->avl_node != NULL && new != NULL); + old = trav->avl_node->avl_data; + trav->avl_node->avl_data = new; + return old; +} + +/* Destroys |new| with |avl_destroy (new, destroy)|, + first setting right links of nodes in |stack| within |new| + to null pointers to avoid touching uninitialized data. */ +static void +copy_error_recovery(struct avl_node **stack, int height, + struct avl_table *new, avl_item_func * destroy) +{ + assert(stack != NULL && height >= 0 && new != NULL); + + for (; height > 2; height -= 2) + stack[height - 1]->avl_link[1] = NULL; + avl_destroy(new, destroy); +} + +/* Copies |org| to a newly created tree, which is returned. + If |copy != NULL|, each data item in |org| is first passed to |copy|, + and the return values are inserted into the tree, + with |NULL| return values taken as indications of failure. + On failure, destroys the partially created new tree, + applying |destroy|, if non-null, to each item in the new tree so far, + and returns |NULL|. + If |allocator != NULL|, it is used for allocation in the new tree. + Otherwise, the same allocator used for |org| is used. */ +struct avl_table *avl_copy(const struct avl_table *org, avl_copy_func * copy, + avl_item_func * destroy, + struct libavl_allocator *allocator) +{ + struct avl_node *stack[2 * (AVL_MAX_HEIGHT + 1)]; + int height = 0; + + struct avl_table *new; + const struct avl_node *x; + struct avl_node *y; + + assert(org != NULL); + new = avl_create(org->avl_compare, org->avl_param, + allocator != NULL ? allocator : org->avl_alloc); + if (new == NULL) + return NULL; + new->avl_count = org->avl_count; + if (new->avl_count == 0) + return new; + + x = (const struct avl_node *) &org->avl_root; + y = (struct avl_node *) &new->avl_root; + for (;;) { + while (x->avl_link[0] != NULL) { + assert(height < 2 * (AVL_MAX_HEIGHT + 1)); + + y->avl_link[0] = + new->avl_alloc->libavl_malloc(new->avl_alloc, + sizeof *y->avl_link[0]); + if (y->avl_link[0] == NULL) { + if (y != (struct avl_node *) &new->avl_root) { + y->avl_data = NULL; + y->avl_link[1] = NULL; + } + + copy_error_recovery(stack, height, new, destroy); + return NULL; + } + + stack[height++] = (struct avl_node *) x; + stack[height++] = y; + x = x->avl_link[0]; + y = y->avl_link[0]; + } + y->avl_link[0] = NULL; + + for (;;) { + y->avl_balance = x->avl_balance; + if (copy == NULL) + y->avl_data = x->avl_data; + else { + y->avl_data = copy(x->avl_data, org->avl_param); + if (y->avl_data == NULL) { + y->avl_link[1] = NULL; + copy_error_recovery(stack, height, new, destroy); + return NULL; + } + } + + if (x->avl_link[1] != NULL) { + y->avl_link[1] = + new->avl_alloc->libavl_malloc(new->avl_alloc, + sizeof *y->avl_link[1]); + if (y->avl_link[1] == NULL) { + copy_error_recovery(stack, height, new, destroy); + return NULL; + } + + x = x->avl_link[1]; + y = y->avl_link[1]; + break; + } else + y->avl_link[1] = NULL; + + if (height <= 2) + return new; + + y = stack[--height]; + x = stack[--height]; + } + } +} + +/* Frees storage allocated for |tree|. + If |destroy != NULL|, applies it to each data item in inorder. */ +void avl_destroy(struct avl_table *tree, avl_item_func * destroy) +{ + struct avl_node *p, *q; + + assert(tree != NULL); + + for (p = tree->avl_root; p != NULL; p = q) + if (p->avl_link[0] == NULL) { + q = p->avl_link[1]; + if (destroy != NULL && p->avl_data != NULL) + destroy(p->avl_data, tree->avl_param); + tree->avl_alloc->libavl_free(tree->avl_alloc, p); + } else { + q = p->avl_link[0]; + p->avl_link[0] = q->avl_link[1]; + q->avl_link[1] = p; + } + + tree->avl_alloc->libavl_free(tree->avl_alloc, tree); +} + +/* Allocates |size| bytes of space using |malloc()|. + Returns a null pointer if allocation fails. */ +void *avl_malloc(struct libavl_allocator *allocator, size_t size) +{ + assert(allocator != NULL && size > 0); + return malloc(size); +} + +/* Frees |block|. */ +void avl_free(struct libavl_allocator *allocator, void *block) +{ + assert(allocator != NULL && block != NULL); + free(block); +} + +/* Default memory allocator that uses |malloc()| and |free()|. */ +struct libavl_allocator avl_allocator_default = { + avl_malloc, + avl_free +}; + +#undef NDEBUG +#include <assert.h> + +/* Asserts that |avl_insert()| succeeds at inserting |item| into |table|. */ +void + (avl_assert_insert) (struct avl_table * table, void *item) { + void **p = avl_probe(table, item); + assert(p != NULL && *p == item); +} + +/* Asserts that |avl_delete()| really removes |item| from |table|, + and returns the removed item. */ +void *(avl_assert_delete) (struct avl_table * table, void *item) { + void *p = avl_delete(table, item); + assert(p != NULL); + return p; +} diff --git a/Build/source/texk/web2c/luatexdir/utils/avl.h b/Build/source/texk/web2c/luatexdir/utils/avl.h new file mode 100644 index 00000000000..b9f9e3c01a9 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/utils/avl.h @@ -0,0 +1,115 @@ +/* Produced by texiweb from libavl.w. */ + +/* libavl - library for manipulation of binary trees. + Copyright (C) 1998-2002, 2004 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. + + The author may be contacted at <blp@gnu.org> on the Internet, or + write to Ben Pfaff, Stanford University, Computer Science Dept., 353 + Serra Mall, Stanford CA 94305, USA. +*/ + +#ifndef AVL_H +#define AVL_H 1 + +#include <stddef.h> + +/* Function types. */ +typedef int avl_comparison_func (const void *avl_a, const void *avl_b, + void *avl_param); +typedef void avl_item_func (void *avl_item, void *avl_param); +typedef void *avl_copy_func (void *avl_item, void *avl_param); + +#ifndef LIBAVL_ALLOCATOR +#define LIBAVL_ALLOCATOR +/* Memory allocator. */ +struct libavl_allocator + { + void *(*libavl_malloc) (struct libavl_allocator *, size_t libavl_size); + void (*libavl_free) (struct libavl_allocator *, void *libavl_block); + }; +#endif + +/* Default memory allocator. */ +extern struct libavl_allocator avl_allocator_default; +void *avl_malloc (struct libavl_allocator *, size_t); +void avl_free (struct libavl_allocator *, void *); + +/* Maximum AVL height. */ +#ifndef AVL_MAX_HEIGHT +#define AVL_MAX_HEIGHT 32 +#endif + +/* Tree data structure. */ +struct avl_table + { + struct avl_node *avl_root; /* Tree's root. */ + avl_comparison_func *avl_compare; /* Comparison function. */ + void *avl_param; /* Extra argument to |avl_compare|. */ + struct libavl_allocator *avl_alloc; /* Memory allocator. */ + size_t avl_count; /* Number of items in tree. */ + unsigned long avl_generation; /* Generation number. */ + }; + +/* An AVL tree node. */ +struct avl_node + { + struct avl_node *avl_link[2]; /* Subtrees. */ + void *avl_data; /* Pointer to data. */ + signed char avl_balance; /* Balance factor. */ + }; + +/* AVL traverser structure. */ +struct avl_traverser + { + struct avl_table *avl_table; /* Tree being traversed. */ + struct avl_node *avl_node; /* Current node in tree. */ + struct avl_node *avl_stack[AVL_MAX_HEIGHT]; + /* All the nodes above |avl_node|. */ + size_t avl_height; /* Number of nodes in |avl_parent|. */ + unsigned long avl_generation; /* Generation number. */ + }; + +/* Table functions. */ +struct avl_table *avl_create (avl_comparison_func *, void *, + struct libavl_allocator *); +struct avl_table *avl_copy (const struct avl_table *, avl_copy_func *, + avl_item_func *, struct libavl_allocator *); +void avl_destroy (struct avl_table *, avl_item_func *); +void **avl_probe (struct avl_table *, void *); +void *avl_insert (struct avl_table *, void *); +void *avl_replace (struct avl_table *, void *); +void *avl_delete (struct avl_table *, const void *); +void *avl_find (const struct avl_table *, const void *); +void avl_assert_insert (struct avl_table *, void *); +void *avl_assert_delete (struct avl_table *, void *); + +#define avl_count(table) ((size_t) (table)->avl_count) + +/* Table traverser functions. */ +void avl_t_init (struct avl_traverser *, struct avl_table *); +void *avl_t_first (struct avl_traverser *, struct avl_table *); +void *avl_t_last (struct avl_traverser *, struct avl_table *); +void *avl_t_find (struct avl_traverser *, struct avl_table *, void *); +void *avl_t_insert (struct avl_traverser *, struct avl_table *, void *); +void *avl_t_copy (struct avl_traverser *, const struct avl_traverser *); +void *avl_t_next (struct avl_traverser *); +void *avl_t_prev (struct avl_traverser *); +void *avl_t_cur (struct avl_traverser *); +void *avl_t_replace (struct avl_traverser *, void *); + +#endif /* avl.h */ diff --git a/Build/source/texk/web2c/luatexdir/utils/avlstuff.c b/Build/source/texk/web2c/luatexdir/utils/avlstuff.c new file mode 100644 index 00000000000..aa8522bf9dc --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/utils/avlstuff.c @@ -0,0 +1,190 @@ +/* avlstuff.c + + Copyright 1996-2006 Han The Thanh <thanh@pdftex.org> + Copyright 2006-2008 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + +#include "ptexlib.h" +#include <kpathsea/c-vararg.h> +#include <kpathsea/c-proto.h> +#include "avl.h" + +static const char __svn_version[] = + "$Id: avlstuff.c 2073 2009-03-21 10:06:50Z hhenkel $ $URL: http://scm.foundry.supelec.fr/svn/luatex/trunk/src/texk/web2c/luatexdir/utils/avlstuff.c $"; + +static struct avl_table **PdfObjTree = NULL; + +/**********************************************************************/ +/* memory management functions for AVL */ + +void *avl_xmalloc(struct libavl_allocator *allocator, size_t size) +{ + assert(allocator != NULL && size > 0); + return xmalloc(size); +} + +void avl_xfree(struct libavl_allocator *allocator, void *block) +{ + assert(allocator != NULL && block != NULL); + xfree(block); +} + +struct libavl_allocator avl_xallocator = { + avl_xmalloc, + avl_xfree +}; + +/**********************************************************************/ +/* general AVL comparison functions */ + +int comp_int_entry(const void *pa, const void *pb, void *p) +{ + cmp_return(*(const int *) pa, *(const int *) pb); + return 0; +} + +int comp_string_entry(const void *pa, const void *pb, void *p) +{ + return strcmp((const char *) pa, (const char *) pb); +} + +/**********************************************************************/ +/* One AVL tree for each obj_type 0...pdf_objtype_max */ + +typedef struct oentry_ { + integer int0; + integer objptr; +} oentry; + +/* AVL sort oentry into avl_table[] */ + +int compare_info(const void *pa, const void *pb, void *param) +{ + integer a, b; + int as, ae, bs, be, al, bl; + + a = ((const oentry *) pa)->int0; + b = ((const oentry *) pb)->int0; + if (a < 0 && b < 0) { /* string comparison */ + if (a <= 2097152 && b <= 2097152) { + a += 2097152; + b += 2097152; + as = str_start[-a]; + ae = str_start[-a + 1]; /* start of next string in pool */ + bs = str_start[-b]; + be = str_start[-b + 1]; + al = ae - as; + bl = be - bs; + if (al < bl) /* compare first by string length */ + return -1; + if (al > bl) + return 1; + for (; as < ae; as++, bs++) { + if (str_pool[as] < str_pool[bs]) + return -1; + if (str_pool[as] > str_pool[bs]) + return 1; + } + } else { + pdftex_fail + ("avlstuff.c: compare_items() for single characters: NI"); + } + } else { /* integer comparison */ + if (a < b) + return -1; + if (a > b) + return 1; + } + return 0; +} + +void avl_put_obj(integer objptr, integer t) +{ + static void **pp; + static oentry *oe; + int i; + + if (PdfObjTree == NULL) { + PdfObjTree = xtalloc(pdf_objtype_max + 1, struct avl_table *); + for (i = 0; i <= pdf_objtype_max; i++) + PdfObjTree[i] = NULL; + } + if (PdfObjTree[t] == NULL) { + PdfObjTree[t] = avl_create(compare_info, NULL, &avl_xallocator); + if (PdfObjTree[t] == NULL) + pdftex_fail("avlstuff.c: avl_create() PdfObjTree failed"); + } + oe = xtalloc(1, oentry); + oe->int0 = obj_tab[objptr].int0; + oe->objptr = objptr; /* allows to relocate objtab */ + pp = avl_probe(PdfObjTree[t], oe); + if (pp == NULL) + pdftex_fail("avlstuff.c: avl_probe() out of memory in insertion"); +} + +/* replacement for linear search pascal function "find_obj()" */ + +integer avl_find_obj(integer t, integer i, integer byname) +{ + static oentry *p; + static oentry tmp; + + if (byname > 0) + tmp.int0 = -i; + else + tmp.int0 = i; + if (PdfObjTree == NULL || PdfObjTree[t] == NULL) + return 0; + p = (oentry *) avl_find(PdfObjTree[t], &tmp); + if (p == NULL) + return 0; + return p->objptr; +} + +/**********************************************************************/ + +struct avl_table *mf_tree = NULL; + +typedef struct { + char *tfm_name; + internalfontnumber fontnum; +} mf_entry; + +/**********************************************************************/ +/* cleaning up... */ + +static void destroy_oentry(void *pa, void *pb) +{ + oentry *p = (oentry *) pa; + xfree(p); +} + +void PdfObjTree_free() +{ + int i; + + if (PdfObjTree == NULL) + return; + for (i = 0; i <= pdf_objtype_max; i++) { + if (PdfObjTree[i] != NULL) + avl_destroy(PdfObjTree[i], destroy_oentry); + } + xfree(PdfObjTree); + PdfObjTree = NULL; +} + +/**********************************************************************/ diff --git a/Build/source/texk/web2c/luatexdir/utils/avlstuff.h b/Build/source/texk/web2c/luatexdir/utils/avlstuff.h new file mode 100644 index 00000000000..6690d5036b8 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/utils/avlstuff.h @@ -0,0 +1,27 @@ +/* +Copyright (c) 2004-2007 Han The Thanh, <thanh@pdftex.org> + +This file is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by Free +Software Foundation; either version 2 of the License, or (at your option) +any later version. + +This file is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with pdfTeX; if not, write to the Free Software Foundation, Inc., 51 +Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +$Id: avlstuff.h 2073 2009-03-21 10:06:50Z hhenkel $ +*/ + +#include "avl.h" + +/* memory management functions for avl */ + +extern struct libavl_allocator avl_xallocator; + +/* end of file avlstuff.h */ diff --git a/Build/source/texk/web2c/luatexdir/utils/synctex.c b/Build/source/texk/web2c/luatexdir/utils/synctex.c new file mode 100644 index 00000000000..459ef746d85 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/utils/synctex.c @@ -0,0 +1,1493 @@ +/* +Copyright (c) 2008 jerome DOT laurens AT u-bourgogne DOT fr + +This file is part of the SyncTeX package. + +License: +-------- +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 THE AUTHORS OR COPYRIGHT +HOLDERS 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 + +Except as contained in this notice, the name of the copyright holder +shall not be used in advertising or otherwise to promote the sale, +use or other dealings in this Software without prior written +authorization from the copyright holder. + +Important notice: +----------------- +This file is named "synctex.c", it may or may not have a header counterpart +depending on its use. It aims to provide basic components useful for the +input/output synchronization technology for TeX. +The purpose of the implementation is threefold +- firstly, it defines a new input/output synchronization technology named + "synchronize texnology", "SyncTeX" or "synctex" +- secondly, it defines the naming convention and format of the auxiliary file + used by this technology +- thirdly, it defines the API of a controller and a controller, used in + particular by the pdfTeX and XeTeX programs to prepare synchronization. + +All these are up to a great extent de facto definitions, which means that they +are partly defined by the implementation itself. + +This technology was first designed for pdfTeX, an extension of TeX managing the +pdf output file format, but it can certainly be adapted to other programs built +from TeX as long as the extensions do not break too much the core design. +Moreover, the synchronize texnology only relies on code concept and not +implementation details, so it can be ported to other TeX systems. In order to +support SyncTeX, one can start reading the dedicated section in synctex.ch, +sync-pdftex.ch and sync-xetex.ch. Actually, support is provided for TeX, e-TeX, +pdfTeX and XeTeX. + +Other existing public synchronization technologies are defined by srcltx.sty - +also used by source specials - and pdfsync.sty. Like them, the synchronize +texnology is meant to be shared by various text editors, viewers and TeX +engines. A centralized reference and source of information is available in TeX-Live. + +Versioning: +----------- +As synctex is embedded into different TeX implementation, there is an independent +versionning system. +For TeX implementations, the actual version is: 1 +For .synctex file format, the actual version is SYNCTEX_VERSION below + +Please, do not remove these explanations. + +Acknowledgments: +---------------- +The author received useful remarks from the pdfTeX developers, especially Hahn The Thanh, +and significant help from XeTeX developer Jonathan Kew + +Nota Bene: +---------- +If you include or use a significant part of the synctex package into a software, +I would appreciate to be listed as contributor and see "SyncTeX" highlighted. + +Version 1 +Tue Jul 1 15:23:00 UTC 2008 + +*/ + +# define SYNCTEX_VERSION 1 + +# define SYNCTEX_DEBUG 0 + +/* Debugging: define the next macro to "return;" in order to disable the synctex code + * only suplemental function calls will be used. The compiler may optimize them. */ +# define SYNCTEX_RETURN_IF_DISABLED ; + +# define SYNCTEX_NOERR 0 + +# define EXTERN extern + +# ifdef xfree +# define SYNCTEX_FREE xfree +# else +# define SYNCTEX_FREE(x) free(x) +# endif + +#define ruleht rule_ht +#define ruledp rule_dp +#define rulewd rule_wd +#define zmem varmem +#define jobname job_name +#define totalpages total_pages +#define curinput cur_input +#define synctextagfield synctex_tag_field +#define namefield name_field + +#define gettexstring(a) xstrdup(makecstring(a)) +/*EXTERN char *gettexstring(int n);*/ + +/* the macros defined below do the same job than their almost eponym + * counterparts of *tex.web, the memory access is sometimes more direct + * because *tex.web won't share its own constants the main purpose is to + * maintain very few hook points into *tex.web in order both to ensure + * portability and not modifying to much the original code. see texmfmem.h + * and *tex.web for details, the synctex_ prefix prevents name conflicts, it + * is some kind of namespace +*/ +# warning These structures MUST be kept in synchronization with the main program +/* synctexoption is a global integer variable defined in *tex.web + * it is set to 1 by texmfmp.c if the command line has the '-synctex=1' + * option. */ +# define synctex_options synctexoption +# define SYNCTEX_NO_OPTION INT_MAX +/* if synctex_options is set to SYNCTEX_NO_OPTION, no command line option was provided. */ + +/* glue code: really define the main memory, + * this is exactly the same "mem" as in *tex.web. */ +# define mem zmem +/* glue code: synctexoffset is a global integer variable defined in *tex.web + * it is set to the offset where the primitive \synctex reads and writes its + * value. */ +# define SYNCTEX_VALUE zeqtb[synctexoffset].cint +/* if there were a mean to share the value of synctex_code between *tex.web + * and this file, it would be great. */ + +# define SYNCTEX_UNIT_FACTOR 1 +# define UNIT / synctex_ctxt.unit +/* UNIT is the scale. TeX coordinates are very accurate and client won't need + * that, at leat in a first step. 1.0 <-> 2^16 = 65536. + * The TeX unit is sp (scaled point) or pt/65536 which means that the scale + * factor to retrieve a bp unit (a postscript) is 72/72.27/65536 = + * 1/4096/16.06 = 1/8192/8.03 + * Here we use 1/SYNCTEX_UNIT_FACTOR as scale factor, then we can limit ourselves to + * integers. This default value assumes that TeX magnification factor is 1000. + * The real TeX magnification factor is used to fine tune the synctex context + * scale in the synctex_dot_open function. + * IMPORTANT: We can say that the natural unit of .synctex files is SYNCTEX_UNIT_FACTOR sp. + * To retrieve the proper bp unit, we'll have to divide by 8.03. To reduce + * rounding errors, we'll certainly have to add 0.5 for non negative integers + * and ±0.5 for negative integers. This trick is mainly to gain speed and + * size. A binary file would be more appropriate in that respect, but I guess + * that some clients like auctex would not like it very much. we cannot use + * "<<13" instead of "/SYNCTEX_UNIT_FACTOR" because the integers are signed and we do not + * want the sign bit to be propagated. The origin of the coordinates is at + * the top left corner of the page. For pdf mode, it is straightforward, but + * for dvi mode, we'll have to record the 1in offset in both directions, + * eventually modified by the magnification. +*/ + +/* WARNING: + The 9 definitions below must be in sync with their eponym declarations in + the proper synctex-*.ch* file. +*/ +# define synchronization_field_size 1 +/* The default value is 2, it is suitable for original TeX and alike, + * but it is too big for XeTeX. + * The tag and the line are just the two last words of the node. This is a + * very handy design but this is not strictly required by the concept. If + * really necessary, one can define other storage rules. + * XeTeX redefines synchronization_field_size, + * SYNCTEX_TAG_MODEL and SYNCTEX_LINE_MODEL + * All the default values are targeted to TeX or e-TeX. */ +# define SYNCTEX_TAG_MODEL(NODE,SIZE)\ + vinfo(NODE+SIZE-synchronization_field_size) +# define SYNCTEX_LINE_MODEL(NODE,SIZE)\ + vlink(NODE+SIZE-synchronization_field_size) +/* SYNCTEX_TAG_MODEL and SYNCTEX_LINE_MODEL are used to define + * SYNCTEX_TAG and SYNCTEX_LINE in a model independant way + * Both are tag and line accessors */ +/*# define box_node_size (7+synchronization_field_size)*/ +/* see: @d box_node_size=... + * There should be an automatic process here because these definitions + * are redundant. However, this process would certainly be overcomplicated + * (building then parsing the *tex.web file would be a pain) */ +# define width_offset 2 +/* see: @d width_offset=... */ +# define depth_offset 3 +/* see: @d depth_offset=... */ +# define height_offset 4 +/* see: @d height_offset=... */ + +/* Now define the local version of width(##), height(##) and depth(##) macros + These only depend on the 3 macros above. */ +# define SYNCTEX_TYPE(NODE) type(NODE) +/*# define rule_node 2*/ +/*# define glue_node 10*/ +/*# define kern_node 11*/ +# define SYNCTEX_SUBTYPE(NODE) subtype(NODE) +# define SYNCTEX_WIDTH(NODE) mem[NODE+width_offset].cint +# define SYNCTEX_DEPTH(NODE) mem[NODE+depth_offset].cint +# define SYNCTEX_HEIGHT(NODE) mem[NODE+height_offset].cint + +/* When an hlist ships out, it can contain many different kern/glue nodes with + * exactly the same sync tag and line. To reduce the size of the .synctex + * file, we only display a kern node sync info when either the sync tag or the + * line changes. Also, we try ro reduce the distance between the chosen nodes + * in order to improve accuracy. It means that we display information for + * consecutive nodes, as far as possible. This tricky part uses a "recorder", + * which is the address of the routine that knows how to write the + * synchronization info to the .synctex file. It also uses criteria to detect + * a change in the context, this is the macro SYNCTEX_CONTEXT_DID_CHANGE. The + * SYNCTEX_IGNORE macro is used to detect unproperly initialized nodes. See + * details in the implementation of the functions below. */ +# define SYNCTEX_IGNORE(NODE) SYNCTEX_IS_OFF || !SYNCTEX_VALUE || !SYNCTEX_FILE + +/* Some parts of the code may differ depending on the ouput mode, + * dvi or xdv vs pdf, in particular the management of magnification. + * The default is dvi mode. + * Also, if pdftex is used, the origin of the coordinates is at 0, not at 1in + * Default values are suitable for TeX */ +# define SYNCTEX_OUTPUT "dvi" +# define SYNCTEX_OFFSET_IS_PDF 0 + +# define SYNCTEX_YES (-1) +# define SYNCTEX_NO (0) +# define SYNCTEX_NO_ERROR (0) + +# include "luatexd.h" +# undef SYNCTEX_OFFSET_IS_PDF +# define SYNCTEX_OFFSET_IS_PDF (pdf_output_value>0) +# undef SYNCTEX_OUTPUT +# define SYNCTEX_OUTPUT ((pdf_output_value>0)?"pdf":"dvi") + +#define __SyncTeX__ 1 + +# if defined(__SyncTeX__) + +# include <stdio.h> +# include <stdarg.h> +# include "zlib.h" + +typedef void (*synctex_recorder_t)(halfword); /* recorders know how to record a node */ +typedef int (*synctex_fprintf_t)(void *, const char * , ...); /* print formatted to either FILE * or gzFile */ + +# define SYNCTEX_BITS_PER_BYTE 8 + +/* Here are all the local variables gathered in one "synchronization context" */ +static struct { + void *file; /* the foo.synctex or foo.synctex.gz I/O identifier */ + synctex_fprintf_t fprintf; /* either fprintf or gzprintf */ + char *busy_name; /* the real "foo.synctex(busy)" or "foo.synctex.gz(busy)" name */ + char *root_name; /* in general jobname.tex */ + integer count; /* The number of interesting records in "foo.synctex" */ + /* next concern the last sync record encountered */ + halfword node; /* the last synchronized node, must be set + * before the recorder */ + synctex_recorder_t recorder;/* the recorder of the node above, the + * routine that knows how to record the + * node to the .synctex file */ + integer tag, line; /* current tag and line */ + integer curh, curv; /* current point */ + integer magnification; /* The magnification as given by \mag */ + integer unit; /* The unit, defaults to 1, use 8192 to produce shorter but less accurate info */ + integer total_length; /* The total length of the bytes written since the last check point */ + struct _flags { + unsigned int option_read:1; /* Command line option read (in case of problem or at the end) */ + unsigned int off:1; /* Definitely turn off synctex, corresponds to cli option -synctex=0 */ + unsigned int no_gz:1; /* Whether zlib is used or not */ + unsigned int not_void:1; /* Whether it really contains synchronization material */ + unsigned int warn:1; /* One shot warning flag */ + unsigned int reserved:SYNCTEX_BITS_PER_BYTE*sizeof(int)-5; /* Align */ + } flags; +} synctex_ctxt = { +NULL, NULL, NULL, NULL, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, {0,0,0,0,0,0}}; + +#define SYNCTEX_FILE synctex_ctxt.file +#define SYNCTEX_IS_OFF (synctex_ctxt.flags.off) +#define SYNCTEX_NO_GZ (synctex_ctxt.flags.no_gz) +#define SYNCTEX_NOT_VOID (synctex_ctxt.flags.not_void) +#define SYNCTEX_WARNING_DISABLE (synctex_ctxt.flags.warn) +#define SYNCTEX_fprintf (*synctex_ctxt.fprintf) + +/* Initialize the options, synchronize the variables. + * This is sent by *tex.web before any TeX macro is used. + * */ +void synctex_init_command(void) +{ + /* This is a one shot function, any subsequent call is void */ + if (synctex_ctxt.flags.option_read) { + return; + } + if (SYNCTEX_NO_OPTION == synctex_options) { + /* No option given from the command line */ + SYNCTEX_VALUE = 0; + } else if (synctex_options == 0) { + /* -synctex=0 was given: SyncTeX must be definitely disabled, + * any subsequent \synctex=1 will have no effect at all */ + SYNCTEX_IS_OFF = SYNCTEX_YES; + SYNCTEX_VALUE = 0; + } else { + /* the command line options are not ignored */ + if(synctex_options < 0) { + SYNCTEX_NO_GZ = SYNCTEX_YES; + } + /* Initialize the content of the \synctex primitive */ + SYNCTEX_VALUE = synctex_options; + } + synctex_ctxt.flags.option_read = SYNCTEX_YES; + return; +} + +/* Free all memory used and close the file, + * It is sent locally when there is a problem with synctex output. */ +void synctex_abort(void) +{ + SYNCTEX_RETURN_IF_DISABLED; +#if SYNCTEX_DEBUG + printf("\nSynchronize DEBUG: synctex_abort\n"); +#endif + if (SYNCTEX_FILE) { + if (SYNCTEX_NO_GZ) { + xfclose((FILE *)SYNCTEX_FILE, synctex_ctxt.busy_name); + } else { + gzclose((gzFile)SYNCTEX_FILE); + } + SYNCTEX_FREE(synctex_ctxt.busy_name); + synctex_ctxt.busy_name = NULL; + } + SYNCTEX_FREE(synctex_ctxt.root_name); + synctex_ctxt.root_name = NULL; + SYNCTEX_IS_OFF = SYNCTEX_YES; /* disable synctex */ +} + +static inline int synctex_record_preamble(void); +static inline int synctex_record_input(integer tag, char *name); + +static char *synctex_suffix = ".synctex"; +static char *synctex_suffix_gz = ".gz"; +static char *synctex_suffix_busy = "(busy)"; + +/* synctex_dot_open ensures that the foo.synctex file is open. + * In case of problem, it definitely disables synchronization. + * Now all the output synchronization info is gathered in only one file. + * It is possible to split this info into as many different output files as sheets + * plus 1 for the control but the overall benefits are not so clear. + * For example foo-i.synctex would contain input synchronization + * information for page i alone. +*/ +static void *synctex_dot_open(void) +{ + SYNCTEX_RETURN_IF_DISABLED; +#if SYNCTEX_DEBUG + printf("\nwarning: Synchronize DEBUG: synctex_dot_open\n"); + printf("\nwarning: SYNCTEX_VALUE=%0X\n", SYNCTEX_VALUE); + printf("\nwarning: synctex_options=%0X\n", synctex_options); +# endif + if (SYNCTEX_IS_OFF || !SYNCTEX_VALUE) { + return NULL; /* synchronization is disabled: do nothing */ + } + if (SYNCTEX_FILE) { + return SYNCTEX_FILE; /* synchronization is alerady enabled */ + } +#if SYNCTEX_DEBUG + printf("\nwarning: Synchronize DEBUG: synctex_dot_open 1\n"); +# endif + /* this is the first time we are asked to open the file + this part of code is executed only once: + either SYNCTEX_FILE is nonnegative or synchronization is + definitely disabled. */ + { + char *tmp = gettexstring(jobname); + /* jobname was set by the \jobname command on the *TeX side */ + char * the_busy_name = xmalloc(strlen(tmp) + strlen(synctex_suffix) + strlen(synctex_suffix_gz) + strlen(synctex_suffix_busy) + 1); + if(!the_busy_name) { + SYNCTEX_FREE(tmp); + synctex_abort(); + return NULL; + } + strcpy(the_busy_name, tmp); + SYNCTEX_FREE(tmp); + tmp = NULL; + strcat(the_busy_name, synctex_suffix); + /* Initialize SYNCTEX_NO_GZ with the content of \synctex to let the user choose the format. */ + SYNCTEX_NO_GZ = SYNCTEX_VALUE<0?SYNCTEX_YES:SYNCTEX_NO; + if (!SYNCTEX_NO_GZ) { + strcat(the_busy_name, synctex_suffix_gz); + } + strcat(the_busy_name, synctex_suffix_busy); + if (SYNCTEX_NO_GZ) { + SYNCTEX_FILE = xfopen(the_busy_name, FOPEN_WBIN_MODE); + synctex_ctxt.fprintf = (synctex_fprintf_t)(&fprintf); + } else { + SYNCTEX_FILE = gzopen(the_busy_name, FOPEN_WBIN_MODE); + synctex_ctxt.fprintf = (synctex_fprintf_t)(&gzprintf); + } +#if SYNCTEX_DEBUG + printf("\nwarning: Synchronize DEBUG: synctex_dot_open 2\n"); +#endif + if (SYNCTEX_FILE) { + if(SYNCTEX_NO_ERROR != synctex_record_preamble()) { + synctex_abort(); + return NULL; + } + /* Initialization of the context */ + synctex_ctxt.magnification = 1000; + synctex_ctxt.unit = SYNCTEX_UNIT_FACTOR; + /* synctex_ctxt.busy_name was NULL before, it now owns the_busy_name */ + synctex_ctxt.busy_name = the_busy_name; + the_busy_name = NULL; + /* print the preamble, this is an quite an UTF8 file */ + if (NULL != synctex_ctxt.root_name) { + synctex_record_input(1,synctex_ctxt.root_name); + SYNCTEX_FREE(synctex_ctxt.root_name); + synctex_ctxt.root_name = NULL; + } + synctex_ctxt.count = 0; +#if SYNCTEX_DEBUG + fprintf(stdout, + "\nwarning: Synchronize DEBUG: synctex_dot_open SYNCTEX AVAILABLE\n"); +#endif + } else { + /* no .synctex file available, so disable synchronization */ + SYNCTEX_IS_OFF = SYNCTEX_YES; + SYNCTEX_VALUE = 0; + printf("\nSyncTeX warning: no synchronization, problem with %s\n",the_busy_name); + /* and free the_busy_name */ + SYNCTEX_FREE(the_busy_name); + the_busy_name = NULL; +#if SYNCTEX_DEBUG + fprintf(stdout, + "\nwarning: Synchronize DEBUG: synctex_dot_open SYNCTEX DISABLED\n"); +#endif + } + } + return SYNCTEX_FILE; +} + +/* Each time TeX opens a file, it sends a synctexstartinput message and enters + * this function. Here, a new synchronization tag is created and stored in + * the synctex_tag_field of the TeX current input context. Each synchronized + * TeX node will record this tag instead of the file name. synctexstartinput + * writes the mapping synctag <-> file name to the .synctex (or .synctex.gz) file. A client + * will read the .synctex file and retrieve this mapping, it will be able to + * open the correct file just knowing its tag. If the same file is read + * multiple times, it might be associated to different tags. Synchronization + * controllers, either in viewers, editors or standalone should be prepared to + * handle this situation and take the appropriate action if they want to + * optimize memory. No two different files will have the same positive tag. + * It is not advisable to definitely store the file names here. If the file + * names ever have to be stored, it should definitely be done at the TeX level + * just like src-specials do, such that other components of the program can use + * it. This function does not make any difference between the files, it + * treats the same way .tex, .aux, .sty ... files, even if many of them do not + * contain any material meant to be typeset. +*/ +void synctex_start_input(void) +{ + static unsigned int synctex_tag_counter = 0; + + SYNCTEX_RETURN_IF_DISABLED; +#if SYNCTEX_DEBUG + printf("\nwarning: Synchronize DEBUG: synctexstartinput %i", + synctex_tag_counter); + printf("\nwarning: SYNCTEX_VALUE=%i", SYNCTEX_VALUE); + printf("\nwarning: synctex_options=%0X", synctex_options); +#endif + + if (SYNCTEX_IS_OFF) { + return; + } + /* synctex_tag_counter is a counter uniquely identifying the file actually + * open. Each time tex opens a new file, synctexstartinput will increment this + * counter */ + if (~synctex_tag_counter > 0) { + ++synctex_tag_counter; + } else { + /* we have reached the limit, subsequent files will be softly ignored + * this makes a lot of files... even in 32 bits + * Maybe we will limit this to 16bits and + * use the 16 other bits to store the column number */ + curinput.synctextagfield = 0; + return; + } + curinput.synctextagfield = synctex_tag_counter; /* -> *TeX.web */ + if (synctex_tag_counter == 1) { + /* this is the first file TeX ever opens, in general \jobname.tex we + * do not know yet if synchronization will ever be enabled so we have + * to store the file name, because we will need it later. + * This is necessary because \jobname can be different */ + synctex_ctxt.root_name = gettexstring(curinput.namefield); + /* we could initialize the unit field to 1 to avoid floating point exception + * when accidentaly dividing by the unit. + * This occurs when some SYNCTEX_IGNORE macro is not used. + * But this must not happen unexpectedly, so we leave the unit to 0 */ +#if SYNCTEX_DEBUG + printf("\nwarning: Synchronize DEBUG: synctexstartinput first END\n"); +# endif + return; + } + if (SYNCTEX_FILE || (SYNCTEX_VALUE && (SYNCTEX_NO_ERROR != synctex_dot_open()))) { + char *tmp = gettexstring(curinput.namefield); + synctex_record_input(curinput.synctextagfield,tmp); + SYNCTEX_FREE(tmp); + } +#if SYNCTEX_DEBUG + printf("\nwarning: Synchronize DEBUG: synctexstartinput END\n"); +# endif + return; +} + +/* All the synctex... functions below have the smallest set of parameters. It + * appears to be either the address of a node, or nothing at all. Using zmem, + * which is the place where all the nodes are stored, one can retrieve every + * information about a node. The other information is obtained through the + * global context variable. + */ + +static inline int synctex_record_postamble(void); + + +/* Free all memory used and close the file, + * sent by close_files_and_terminate in tex.web. + * synctexterminate() is called when the TeX run terminates. + * If synchronization was active, the working synctex file is moved to + * the final synctex file name. + * If synchronization was not active of if there is no output, + * the synctex file is removed if any. + * That way we can be sure that any synctex file is in sync with a tex run. + * However, it does not mean that it will be in sync with the pdf, especially + * when the output is dvi or xdv and the dvi (or xdv) to pdf driver has not been applied. + */ +void synctex_terminate(boolean log_opened) +{ + char *tmp = NULL; + char * the_real_syncname = NULL; + SYNCTEX_RETURN_IF_DISABLED; +#if SYNCTEX_DEBUG + printf("\nSynchronize DEBUG: synctexterminate\n"); +#endif + tmp = gettexstring(jobname); + the_real_syncname = xmalloc(strlen(tmp) + strlen(synctex_suffix) + strlen(synctex_suffix_gz) + 1); + if(!the_real_syncname) { + SYNCTEX_FREE(tmp); + synctex_abort(); + return; + } + strcpy(the_real_syncname, tmp); + strcat(the_real_syncname, synctex_suffix); + if (!SYNCTEX_NO_GZ) { + /* Remove any uncompressed synctex file, from a previous build. */ + remove(the_real_syncname); + strcat(the_real_syncname, synctex_suffix_gz); + } + /* allways remove the synctex output file before renaming it, windows requires it. */ + if(0 != remove(the_real_syncname) && errno == EACCES) { + fprintf(stderr,"SyncTeX: Can't remove %s (file is open or read only)\n",the_real_syncname); + } + if (SYNCTEX_FILE) { + if (SYNCTEX_NOT_VOID) { + synctex_record_postamble(); + /* close the synctex file*/ + if (SYNCTEX_NO_GZ) { + xfclose((FILE *)SYNCTEX_FILE, synctex_ctxt.busy_name); + } else { + gzclose((gzFile)SYNCTEX_FILE); + } + /* renaming the working synctex file */ + if(0 == rename(synctex_ctxt.busy_name,the_real_syncname)) { + if(log_opened) { + printf("\nSyncTeX written on %s",the_real_syncname); /* SyncTeX also refers to the contents */ + } + } else { + fprintf(stderr,"SyncTeX: Can't rename %s to %s\n",synctex_ctxt.busy_name,the_real_syncname); + remove(synctex_ctxt.busy_name); + } + } else { + /* close and remove the synctex file because there are no pages of output */ + if (SYNCTEX_NO_GZ) { + xfclose((FILE *)SYNCTEX_FILE, synctex_ctxt.busy_name); + } else { + gzclose((gzFile)SYNCTEX_FILE); + } + remove(synctex_ctxt.busy_name); + } + SYNCTEX_FILE = NULL; + } + if (SYNCTEX_NO_GZ) { + /* Remove any compressed synctex file, from a previous build. */ + strcat(the_real_syncname, synctex_suffix_gz); + remove(the_real_syncname); + } + SYNCTEX_FREE(synctex_ctxt.busy_name); + synctex_ctxt.busy_name = NULL; + SYNCTEX_FREE(the_real_syncname); + SYNCTEX_FREE(tmp); + synctex_abort(); +} + +static inline int synctex_record_content(void); +static inline int synctex_record_settings(void); +static inline int synctex_record_sheet(integer sheet); + +/* Recording the "{..." line. In *tex.web, use synctex_sheet(pdf_output) at + * the very beginning of the ship_out procedure. +*/ +void synctex_sheet(integer mag) +{ + SYNCTEX_RETURN_IF_DISABLED; +#if SYNCTEX_DEBUG + printf("\nSynchronize DEBUG: synctexsheet %i\n",mag); +#endif + if (SYNCTEX_IS_OFF) { + if(SYNCTEX_VALUE && !SYNCTEX_WARNING_DISABLE) { + SYNCTEX_WARNING_DISABLE = SYNCTEX_YES; + printf("\nSyncTeX warning: Synchronization was disabled from\nthe command line with -synctex=0\nChanging the value of \\synctex has no effect."); + } + return; + } + if (SYNCTEX_FILE || (SYNCTEX_VALUE && (SYNCTEX_NO_ERROR != synctex_dot_open()))) { + /* First possibility: the .synctex file is already open because SyncTeX was activated on the CLI + * or it was activated with the \synctex macro and the first page is already shipped out. + * Second possibility: tries to open the .synctex, useful if synchronization was enabled + * from the source file and not from the CLI. + * totalpages is defined in tex.web */ + if (totalpages == 0) { + /* Now it is time to properly set up the scale factor. */ + if(mag>0) { + synctex_ctxt.magnification = mag; + } + if(SYNCTEX_NO_ERROR != synctex_record_settings() + || SYNCTEX_NO_ERROR != synctex_record_content()) { + synctex_abort(); + return; + } + } + synctex_record_sheet(totalpages+1); + } +#if SYNCTEX_DEBUG + printf("\nSynchronize DEBUG: synctexsheet END\n"); +#endif + return; +} + +static inline int synctex_record_teehs(integer sheet); + +/* Recording the "}..." line. In *tex.web, use synctex_teehs at + * the very end of the ship_out procedure. +*/ +void synctex_teehs(void) +{ + SYNCTEX_RETURN_IF_DISABLED; +#if SYNCTEX_DEBUG + printf("\nSynchronize DEBUG: synctexteehs\n"); +#endif + if (SYNCTEX_IS_OFF || !SYNCTEX_FILE ) { + return; + } + synctex_record_teehs(totalpages);/* not totalpages+1*/ +#if SYNCTEX_DEBUG + printf("\nSynchronize DEBUG: synctexteehs END\n"); +#endif + return; +} + +static inline void synctex_record_vlist(halfword p); + +/* This message is sent when a vlist will be shipped out, more precisely at + * the beginning of the vliSYNCTEX_out procedure in *TeX.web. It will be balanced + * by a synctex_tsilv, sent at the end of the vliSYNCTEX_out procedure. p is the + * address of the vlist We assume that p is really a vlist node! */ +void synctex_vlist(halfword this_box) +{ + scaledpos pos; + SYNCTEX_RETURN_IF_DISABLED; +#if SYNCTEX_DEBUG + printf("\nSynchronize DEBUG: synctexhlist\n"); +#endif + if (SYNCTEX_IGNORE(this_box)) { + return; + } + synctex_ctxt.node = this_box; /* 0 to reset */ + synctex_ctxt.recorder = NULL; /* reset */ + synctex_ctxt.tag = SYNCTEX_TAG_MODEL(this_box,box_node_size); + synctex_ctxt.line = SYNCTEX_LINE_MODEL(this_box,box_node_size); + pos = synch_p_with_c(cur); + synctex_ctxt.curh = pos.h; + synctex_ctxt.curv = pos.v; + synctex_record_vlist(this_box); +} + +static inline void synctex_record_tsilv(halfword p); + +/* Recording a "f" line ending a vbox: this message is sent whenever a vlist + * has been shipped out. It is used to close the vlist nesting level. It is + * sent at the end of the vliSYNCTEX_out procedure in *TeX.web to balance a former + * synctex_vlist sent at the beginning of that procedure. */ +void synctex_tsilv(halfword this_box) +{ + scaledpos pos; + SYNCTEX_RETURN_IF_DISABLED; +#if SYNCTEX_DEBUG + printf("\nSynchronize DEBUG: synctextsilv\n"); +#endif + if (SYNCTEX_IGNORE(this_box)) { + return; + } + /* Ignoring any pending info to be recorded */ + synctex_ctxt.node = this_box; /* 0 to reset */ + synctex_ctxt.tag = SYNCTEX_TAG_MODEL(this_box,box_node_size); + synctex_ctxt.line = SYNCTEX_LINE_MODEL(this_box,box_node_size); + pos = synch_p_with_c(cur); + synctex_ctxt.curh = pos.h; + synctex_ctxt.curv = pos.v; + synctex_ctxt.recorder = NULL; + synctex_record_tsilv(this_box); +} + +static inline void synctex_record_void_vlist(halfword p); + +/* This message is sent when a void vlist will be shipped out. + * There is no need to balance a void vlist. */ +void synctex_void_vlist(halfword p, halfword this_box) +{ + scaledpos pos; + SYNCTEX_RETURN_IF_DISABLED; +#if SYNCTEX_DEBUG + printf("\nSynchronize DEBUG: synctexvoidvlist\n"); +#endif + if (SYNCTEX_IGNORE(p)) { + return; + } + synctex_ctxt.node = p; /* reset */ + synctex_ctxt.tag = SYNCTEX_TAG_MODEL(p,box_node_size); + synctex_ctxt.line = SYNCTEX_LINE_MODEL(p,box_node_size); + pos = synch_p_with_c(cur); + synctex_ctxt.curh = pos.h; + synctex_ctxt.curv = pos.v; + synctex_ctxt.recorder = NULL; /* reset */ + synctex_record_void_vlist(p); +} + +static inline void synctex_record_hlist(halfword p); + +/* This message is sent when an hlist will be shipped out, more precisely at + * the beginning of the hliSYNCTEX_out procedure in *TeX.web. It will be balanced + * by a synctex_tsilh, sent at the end of the hliSYNCTEX_out procedure. p is the + * address of the hlist We assume that p is really an hlist node! */ +void synctex_hlist(halfword this_box) +{ + scaledpos pos; + SYNCTEX_RETURN_IF_DISABLED; +#if SYNCTEX_DEBUG + printf("\nSynchronize DEBUG: synctexhlist\n"); +#endif + if (SYNCTEX_IGNORE(this_box)) { + return; + } + synctex_ctxt.node = this_box; /* 0 to reset */ + synctex_ctxt.tag = SYNCTEX_TAG_MODEL(this_box,box_node_size); + synctex_ctxt.line = SYNCTEX_LINE_MODEL(this_box,box_node_size); + pos = synch_p_with_c(cur); + synctex_ctxt.curh = pos.h; + synctex_ctxt.curv = pos.v; + synctex_ctxt.recorder = NULL; /* reset */ + synctex_record_hlist(this_box); +} + +static inline void synctex_record_tsilh(halfword p); + +/* Recording a ")" line ending an hbox this message is sent whenever an hlist + * has been shipped out it is used to close the hlist nesting level. It is + * sent at the end of the hliSYNCTEX_out procedure in *TeX.web to balance a former + * synctex_hlist sent at the beginning of that procedure. */ +void synctex_tsilh(halfword this_box) +{ + scaledpos pos; + SYNCTEX_RETURN_IF_DISABLED; +#if SYNCTEX_DEBUG + printf("\nSynchronize DEBUG: synctextsilh\n"); +#endif + if (SYNCTEX_IGNORE(this_box)) { + return; + } + /* Ignoring any pending info to be recorded */ + synctex_ctxt.node = this_box; /* 0 to force next node to be recorded! */ + synctex_ctxt.tag = SYNCTEX_TAG_MODEL(this_box,box_node_size); + synctex_ctxt.line = SYNCTEX_LINE_MODEL(this_box,box_node_size); + pos = synch_p_with_c(cur); + synctex_ctxt.curh = pos.h; + synctex_ctxt.curv = pos.v; + synctex_ctxt.recorder = NULL; /* reset */ + synctex_record_tsilh(this_box); +} + +static inline void synctex_record_void_hlist(halfword p); + +/* This message is sent when a void hlist will be shipped out. + * There is no need to balance a void hlist. */ +void synctex_void_hlist(halfword p, halfword this_box) +{ + scaledpos pos; + SYNCTEX_RETURN_IF_DISABLED; +#if SYNCTEX_DEBUG + printf("\nSynchronize DEBUG: synctexvoidhlist\n"); +#endif + if (SYNCTEX_IGNORE(p)) { + return; + } + /* the sync context has changed */ + if (synctex_ctxt.recorder != NULL) { + /* but was not yet recorded */ + (*synctex_ctxt.recorder) (synctex_ctxt.node); + } + synctex_ctxt.node = p; /* 0 to reset */ + synctex_ctxt.tag = SYNCTEX_TAG_MODEL(p,box_node_size); + synctex_ctxt.line = SYNCTEX_LINE_MODEL(p,box_node_size); + pos = synch_p_with_c(cur); + synctex_ctxt.curh = pos.h; + synctex_ctxt.curv = pos.v; + synctex_ctxt.recorder = NULL; /* reset */ + synctex_record_void_hlist(p); +} + +/* glue code: these only work with nodes of size medium_node_size */ +# define small_node_size 3 +/* see: @d small_node_size=2 {number of words to allocate for most node types} */ +# define medium_node_size (small_node_size+synchronization_field_size) +/* see: @d rule_node_size=4 */ +# /* define rule_node_size (4+synchronization_field_size)*/ + +/* IN THE SEQUEL, ALL NODE ARE medium_node_size'd, UNTIL THE CONTRARY IS MENTIONNED */ +# undef SYNCTEX_IGNORE +# define SYNCTEX_IGNORE(NODE) SYNCTEX_IS_OFF || !SYNCTEX_VALUE \ + || (0 >= SYNCTEX_TAG_MODEL(NODE,medium_node_size)) \ + || (0 >= SYNCTEX_LINE_MODEL(NODE,medium_node_size)) + +/* This macro will detect a change in the synchronization context. As long as + * the synchronization context remains the same, there is no need to write + * synchronization info: it would not help more. The synchronization context + * has changed when either the line number or the file tag has changed. */ +# define SYNCTEX_CONTEXT_DID_CHANGE(NODE) ((0 == synctex_ctxt.node)\ + || (SYNCTEX_TAG_MODEL(NODE,medium_node_size) != synctex_ctxt.tag)\ + || (SYNCTEX_LINE_MODEL(NODE,medium_node_size) != synctex_ctxt.line)) + +void synctex_math_recorder(halfword p); + +/* glue code this message is sent whenever an inline math node will ship out + See: @ @<Output the non-|char_node| |p| for... */ +void synctex_math(halfword p, halfword this_box) +{ + scaledpos pos; + SYNCTEX_RETURN_IF_DISABLED; +#if SYNCTEX_DEBUG + printf("\nSynchronize DEBUG: synctexmath\n"); +#endif + if (SYNCTEX_IGNORE(p)) { + return; + } + if ((synctex_ctxt.recorder != NULL) && SYNCTEX_CONTEXT_DID_CHANGE(p)) { + /* the sync context did change */ + (*synctex_ctxt.recorder) (synctex_ctxt.node); + } + synctex_ctxt.node = p; + synctex_ctxt.tag = SYNCTEX_TAG_MODEL(p,medium_node_size); + synctex_ctxt.line = SYNCTEX_LINE_MODEL(p,medium_node_size); + pos = synch_p_with_c(cur); + synctex_ctxt.curh = pos.h; + synctex_ctxt.curv = pos.v; + synctex_ctxt.recorder = NULL;/* no need to record once more */ + synctex_math_recorder(p);/* always record synchronously */ +} + +static inline void synctex_record_glue(halfword p); +static inline void synctex_record_kern(halfword p); +static inline void synctex_record_rule(halfword p); + +/* this message is sent whenever an horizontal glue node or rule node ships out + See: move_past:... */ +# define SYNCTEX_IGNORE_RULE(NODE) SYNCTEX_IS_OFF || !SYNCTEX_VALUE \ + || (0 >= SYNCTEX_TAG_MODEL(NODE,rule_node_size)) \ + || (0 >= SYNCTEX_LINE_MODEL(NODE,rule_node_size)) +void synctex_horizontal_rule_or_glue(halfword p, halfword this_box) +{ + scaledpos pos; + SYNCTEX_RETURN_IF_DISABLED; +#if SYNCTEX_DEBUG + printf("\nSynchronize DEBUG: synctexglue\n"); +#endif + if (SYNCTEX_TYPE(p) == rule_node) { /* not medium_node_size so we can't use SYNCTEX_IGNORE */ + if (SYNCTEX_IGNORE_RULE(p)) { + return; + } + } + else { + if (SYNCTEX_IGNORE(p)) { + return; + } + } + synctex_ctxt.node = p; + pos = synch_p_with_c(cur); + synctex_ctxt.curh = pos.h; + synctex_ctxt.curv = pos.v; + synctex_ctxt.recorder = NULL; + switch(SYNCTEX_TYPE(p)) { + case rule_node: + synctex_ctxt.tag = SYNCTEX_TAG_MODEL(p,rule_node_size); + synctex_ctxt.line = SYNCTEX_LINE_MODEL(p,rule_node_size); + synctex_record_rule(p);/* always record synchronously: maybe some text is outside the box */ + break; + case glue_node: + synctex_ctxt.tag = SYNCTEX_TAG_MODEL(p,medium_node_size); + synctex_ctxt.line = SYNCTEX_LINE_MODEL(p,medium_node_size); + synctex_record_glue(p);/* always record synchronously: maybe some text is outside the box */ + break; + case kern_node: + synctex_ctxt.tag = SYNCTEX_TAG_MODEL(p,medium_node_size); + synctex_ctxt.line = SYNCTEX_LINE_MODEL(p,medium_node_size); + synctex_record_kern(p);/* always record synchronously: maybe some text is outside the box */ + break; + default: + printf("\nSynchronize ERROR: unknown node type %i\n",SYNCTEX_TYPE(p)); + } +} + +void synctex_kern_recorder(halfword p); + +/* this message is sent whenever a kern node ships out + See: @ @<Output the non-|char_node| |p| for... */ +void synctex_kern(halfword p, halfword this_box) +{ + SYNCTEX_RETURN_IF_DISABLED; +#if SYNCTEX_DEBUG + printf("\nSynchronize DEBUG: synctexkern\n"); +#endif + if (SYNCTEX_IGNORE(p)) { + return; + } + if (SYNCTEX_CONTEXT_DID_CHANGE(p)) { + /* the sync context has changed */ + if (synctex_ctxt.recorder != NULL) { + /* but was not yet recorded */ + (*synctex_ctxt.recorder) (synctex_ctxt.node); + } + if(synctex_ctxt.node == this_box) { + /* first node in the list */ + synctex_ctxt.node = p; + synctex_ctxt.tag = SYNCTEX_TAG_MODEL(p,medium_node_size); + synctex_ctxt.line = SYNCTEX_LINE_MODEL(p,medium_node_size); + synctex_ctxt.recorder = &synctex_kern_recorder; + } else { + synctex_ctxt.node = p; + synctex_ctxt.tag = SYNCTEX_TAG_MODEL(p,medium_node_size); + synctex_ctxt.line = SYNCTEX_LINE_MODEL(p,medium_node_size); + synctex_ctxt.recorder = NULL; + /* always record when the context has just changed + * and when not the first node */ + synctex_kern_recorder(p); + } + } else { + /* just update the geometry and type (for future improvements) */ + synctex_ctxt.node = p; + synctex_ctxt.tag = SYNCTEX_TAG_MODEL(p,medium_node_size); + synctex_ctxt.line = SYNCTEX_LINE_MODEL(p,medium_node_size); + synctex_ctxt.recorder = &synctex_kern_recorder; + } +} + +/* This last part is used as a tool to infer TeX behaviour, + * but not for direct synchronization. */ +# undef SYNCTEX_IGNORE +# define SYNCTEX_IGNORE(NODE) SYNCTEX_IS_OFF || !SYNCTEX_VALUE || !SYNCTEX_FILE \ + || (synctex_ctxt.count>2000) + +void synctex_char_recorder(halfword p); + +/* this message is sent whenever a char node ships out */ +void synctexchar(halfword p, halfword this_box) +{ + SYNCTEX_RETURN_IF_DISABLED; +#if SYNCTEX_DEBUG + printf("\nSynchronize DEBUG: synctexchar\n"); +#endif + if (SYNCTEX_IGNORE(p)) { + return; + } + if (synctex_ctxt.recorder != NULL) { + /* but was not yet recorded */ + (*synctex_ctxt.recorder) (synctex_ctxt.node); + } + synctex_ctxt.node = p; + synctex_ctxt.tag = 0; + synctex_ctxt.line = 0; + synctex_ctxt.recorder = NULL; + /* always record when the context has just changed */ + synctex_char_recorder(p); +} + +void synctex_node_recorder(halfword p); + +# undef SYNCTEX_IGNORE +# define SYNCTEX_IGNORE(NODE) (SYNCTEX_IS_OFF || !SYNCTEX_VALUE || !SYNCTEX_FILE) + +/* this message should be sent to record information + for a node of an unknown type */ +void synctexnode(halfword p, halfword this_box) +{ + SYNCTEX_RETURN_IF_DISABLED; +#if SYNCTEX_DEBUG + printf("\nSynchronize DEBUG: synctexnode\n"); +#endif + if (SYNCTEX_IGNORE(p)) { + return; + } + /* always record, not very usefull yet */ + synctex_node_recorder(p); +} + +/* this message should be sent to record information + synchronously for the current location */ +void synctex_current(void) +{ + scaledpos pos; + SYNCTEX_RETURN_IF_DISABLED; +#if SYNCTEX_DEBUG + printf("\nSynchronize DEBUG: synctexcurrent\n"); +#endif + if (SYNCTEX_IGNORE(nothing)) { + return; + } else { + pos = synch_p_with_c(cur); + size_t len = SYNCTEX_fprintf(SYNCTEX_FILE,"x%i,%i:%i,%i\n", + synctex_ctxt.tag,synctex_ctxt.line, + pos.h UNIT,pos.v UNIT); + if(len>0) { + synctex_ctxt.total_length += len; + return; + } + } + synctex_abort(); + return; +} + +#pragma mark - +#pragma mark Glue code Recorders + +/* Recording the settings */ +static inline int synctex_record_settings(void) +{ +#if SYNCTEX_DEBUG > 999 + printf("\nSynchronize DEBUG: synctex_record_settings\n"); +#endif + if(NULL == SYNCTEX_FILE) { + return SYNCTEX_NOERR; + } + if(SYNCTEX_FILE) { + size_t len = SYNCTEX_fprintf(SYNCTEX_FILE,"Output:%s\nMagnification:%i\nUnit:%i\nX Offset:%i\nY Offset:%i\n", + SYNCTEX_OUTPUT,synctex_ctxt.magnification,synctex_ctxt.unit, + ((SYNCTEX_OFFSET_IS_PDF != 0) ? 0 : 4736287 UNIT), + ((SYNCTEX_OFFSET_IS_PDF != 0) ? 0 : 4736287 UNIT)); + if(len>0) { + synctex_ctxt.total_length += len; + return SYNCTEX_NOERR; + } + } + synctex_abort(); + return -1; +} + +/* Recording a "SyncTeX..." line */ +static inline int synctex_record_preamble(void) +{ + size_t len = 0; +#if SYNCTEX_DEBUG > 999 + printf("\nSynchronize DEBUG: synctex_record_preamble\n"); +#endif + len = SYNCTEX_fprintf(SYNCTEX_FILE,"SyncTeX Version:%i\n",SYNCTEX_VERSION); + if(len>0) { + synctex_ctxt.total_length = len; + return SYNCTEX_NOERR; + } + synctex_abort(); + return -1; +} + +/* Recording a "Input:..." line */ +static inline int synctex_record_input(integer tag, char *name) +{ + size_t len = 0; +#if SYNCTEX_DEBUG > 999 + printf("\nSynchronize DEBUG: synctex_record_input\n"); +#endif + len = SYNCTEX_fprintf(SYNCTEX_FILE,"Input:%i:%s\n",tag,name); + if(len>0) { + synctex_ctxt.total_length += len; + return SYNCTEX_NOERR; + } + synctex_abort(); + return -1; +} + +/* Recording a "!..." line */ +static inline int synctex_record_anchor(void) +{ + size_t len = 0; +#if SYNCTEX_DEBUG > 999 + printf("\nSynchronize DEBUG: synctex_record_anchor\n"); +#endif + len = SYNCTEX_fprintf(SYNCTEX_FILE,"!%i\n",synctex_ctxt.total_length); + if(len>0) { + synctex_ctxt.total_length = len; + ++synctex_ctxt.count; + return SYNCTEX_NOERR; + } + synctex_abort(); + return -1; +} + +/* Recording a "Content" line */ +static inline int synctex_record_content(void) +{ + size_t len = 0; +#if SYNCTEX_DEBUG > 999 + printf("\nSynchronize DEBUG: synctex_record_content\n"); +#endif + len = SYNCTEX_fprintf(SYNCTEX_FILE,"Content:\n"); + if(len>0) { + synctex_ctxt.total_length += len; + return SYNCTEX_NOERR; + } + synctex_abort(); + return -1; +} + +/* Recording a "{..." line */ +static inline int synctex_record_sheet(integer sheet) +{ +#if SYNCTEX_DEBUG > 999 + printf("\nSynchronize DEBUG: synctex_record_sheet\n"); +#endif + if(SYNCTEX_NOERR == synctex_record_anchor()) { + size_t len = SYNCTEX_fprintf(SYNCTEX_FILE,"{%i\n",sheet); + if(len>0) { + synctex_ctxt.total_length += len; + ++synctex_ctxt.count; + return SYNCTEX_NOERR; + } + } + synctex_abort(); + return -1; +} + +/* Recording a "}..." line */ +static inline int synctex_record_teehs(integer sheet) +{ +#if SYNCTEX_DEBUG > 999 + printf("\nSynchronize DEBUG: synctex_record_teehs\n"); +#endif + if(SYNCTEX_NOERR == synctex_record_anchor()) { + size_t len = SYNCTEX_fprintf(SYNCTEX_FILE,"}%i\n",sheet); + if(len>0) { + synctex_ctxt.total_length += len; + ++synctex_ctxt.count; + return SYNCTEX_NOERR; + } + } + synctex_abort(); + return -1; +} + +/* Recording a "v..." line */ +static inline void synctex_record_void_vlist(halfword p) +{ + size_t len = 0; +#if SYNCTEX_DEBUG > 999 + printf("\nSynchronize DEBUG: synctex_record_void_vlist\n"); +#endif + len = SYNCTEX_fprintf(SYNCTEX_FILE,"v%i,%i:%i,%i:%i,%i,%i\n", + SYNCTEX_TAG_MODEL(p,box_node_size), + SYNCTEX_LINE_MODEL(p,box_node_size), + synctex_ctxt.curh UNIT, synctex_ctxt.curv UNIT, + SYNCTEX_WIDTH(p) UNIT, + SYNCTEX_HEIGHT(p) UNIT, + SYNCTEX_DEPTH(p) UNIT); + if(len>0) { + synctex_ctxt.total_length += len; + ++synctex_ctxt.count; + return; + } + synctex_abort(); + return; +} + +/* Recording a "[..." line */ +static inline void synctex_record_vlist(halfword p) +{ + size_t len = 0; + SYNCTEX_NOT_VOID = SYNCTEX_YES; +#if SYNCTEX_DEBUG > 999 + printf("\nSynchronize DEBUG: synctex_record_vlist\n"); +#endif + len = SYNCTEX_fprintf(SYNCTEX_FILE,"[%i,%i:%i,%i:%i,%i,%i\n", + SYNCTEX_TAG_MODEL(p,box_node_size), + SYNCTEX_LINE_MODEL(p,box_node_size), + synctex_ctxt.curh UNIT, synctex_ctxt.curv UNIT, + SYNCTEX_WIDTH(p) UNIT, + SYNCTEX_HEIGHT(p) UNIT, + SYNCTEX_DEPTH(p) UNIT); + if(len>0) { + synctex_ctxt.total_length += len; + ++synctex_ctxt.count; + return; + } + synctex_abort(); + return; +} + +/* Recording a "]..." line */ +static inline void synctex_record_tsilv(halfword p) +{ + size_t len = 0; +#if SYNCTEX_DEBUG > 999 + printf("\nSynchronize DEBUG: synctex_record_tsilv\n"); +#endif + len = SYNCTEX_fprintf(SYNCTEX_FILE,"]\n"); + if(len>0) { + synctex_ctxt.total_length += len; + return; + } + synctex_abort(); + return; +} + +/* Recording a "h..." line */ +static inline void synctex_record_void_hlist(halfword p) +{ + size_t len = 0; +#if SYNCTEX_DEBUG > 999 + printf("\nSynchronize DEBUG: synctex_record_void_hlist\n"); +#endif + len = SYNCTEX_fprintf(SYNCTEX_FILE,"h%i,%i:%i,%i:%i,%i,%i\n", + SYNCTEX_TAG_MODEL(p,box_node_size), + SYNCTEX_LINE_MODEL(p,box_node_size), + synctex_ctxt.curh UNIT, synctex_ctxt.curv UNIT, + SYNCTEX_WIDTH(p) UNIT, + SYNCTEX_HEIGHT(p) UNIT, + SYNCTEX_DEPTH(p) UNIT); + if(len>0) { + synctex_ctxt.total_length += len; + ++synctex_ctxt.count; + return; + } + synctex_abort(); + return; +} + +/* Recording a "(..." line */ +static inline void synctex_record_hlist(halfword p) +{ + size_t len = 0; + SYNCTEX_NOT_VOID = SYNCTEX_YES; +#if SYNCTEX_DEBUG > 999 + printf("\nSynchronize DEBUG: synctex_record_hlist\n"); +#endif + len = SYNCTEX_fprintf(SYNCTEX_FILE,"(%i,%i:%i,%i:%i,%i,%i\n", + SYNCTEX_TAG_MODEL(p,box_node_size), + SYNCTEX_LINE_MODEL(p,box_node_size), + synctex_ctxt.curh UNIT, synctex_ctxt.curv UNIT, + SYNCTEX_WIDTH(p) UNIT, + SYNCTEX_HEIGHT(p) UNIT, + SYNCTEX_DEPTH(p) UNIT); + if(len>0) { + synctex_ctxt.total_length += len; + ++synctex_ctxt.count; + return; + } + synctex_abort(); + return; +} + +/* Recording a ")..." line */ +static inline void synctex_record_tsilh(halfword p) +{ + size_t len = 0; +#if SYNCTEX_DEBUG > 999 + printf("\nSynchronize DEBUG: synctex_record_tsilh\n"); +#endif + len = SYNCTEX_fprintf(SYNCTEX_FILE,")\n"); + if(len>0) { + synctex_ctxt.total_length += len; + ++synctex_ctxt.count; + return; + } + synctex_abort(); + return; +} + +/* Recording a "Count..." line */ +static inline int synctex_record_count(void) { + size_t len = 0; +#if SYNCTEX_DEBUG > 999 + printf("\nSynchronize DEBUG: synctex_record_count\n"); +#endif + len = SYNCTEX_fprintf(SYNCTEX_FILE,"Count:%i\n",synctex_ctxt.count); + if(len>0) { + synctex_ctxt.total_length += len; + return SYNCTEX_NOERR; + } + synctex_abort(); + return -1; +} + +/* Recording a "Postamble" section */ +static inline int synctex_record_postamble(void) +{ +#if SYNCTEX_DEBUG > 999 + printf("\nSynchronize DEBUG: synctex_record_postamble\n"); +#endif + if(SYNCTEX_NOERR == synctex_record_anchor()) { + size_t len = SYNCTEX_fprintf(SYNCTEX_FILE,"Postamble:\n"); + if(len>0) { + synctex_ctxt.total_length += len; + if(synctex_record_count() || synctex_record_anchor()) { + } else { + len = SYNCTEX_fprintf(SYNCTEX_FILE,"Post scriptum:\n"); + if(len>0) { + synctex_ctxt.total_length += len; + return SYNCTEX_NOERR; + } + } + } + } + synctex_abort(); + return -1; +} + +/* Recording a "g..." line */ +static inline void synctex_record_glue(halfword p) +{ + size_t len = 0; +#if SYNCTEX_DEBUG > 999 + printf("\nSynchronize DEBUG: synctex_glue_recorder\n"); +#endif + len = SYNCTEX_fprintf(SYNCTEX_FILE,"g%i,%i:%i,%i\n", + SYNCTEX_TAG_MODEL(p,medium_node_size), + SYNCTEX_LINE_MODEL(p,medium_node_size), + synctex_ctxt.curh UNIT, synctex_ctxt.curv UNIT); + if(len>0) { + synctex_ctxt.total_length += len; + ++synctex_ctxt.count; + return; + } + synctex_abort(); + return; +} + +/* Recording a "k..." line */ +static inline void synctex_record_kern(halfword p) +{ + size_t len = 0; +#if SYNCTEX_DEBUG > 999 + printf("\nSynchronize DEBUG: synctex_kern_recorder\n"); +#endif + len = SYNCTEX_fprintf(SYNCTEX_FILE,"k%i,%i:%i,%i:%i\n", + SYNCTEX_TAG_MODEL(p,medium_node_size), + SYNCTEX_LINE_MODEL(p,medium_node_size), + synctex_ctxt.curh UNIT, synctex_ctxt.curv UNIT, + SYNCTEX_WIDTH(p) UNIT); + if(len>0) { + synctex_ctxt.total_length += len; + ++synctex_ctxt.count; + return; + } + synctex_abort(); + return; +} + +/* Recording a "r..." line */ +static inline void synctex_record_rule(halfword p) +{ + size_t len = 0; +#if SYNCTEX_DEBUG > 999 + printf("\nSynchronize DEBUG: synctex_record_tsilh\n"); +#endif + len = SYNCTEX_fprintf(SYNCTEX_FILE,"r%i,%i:%i,%i:%i,%i,%i\n", + SYNCTEX_TAG_MODEL(p,rule_node_size), + SYNCTEX_LINE_MODEL(p,rule_node_size), + synctex_ctxt.curh UNIT, synctex_ctxt.curv UNIT, + rulewd UNIT, + ruleht UNIT, + ruledp UNIT); + if(len>0) { + synctex_ctxt.total_length += len; + ++synctex_ctxt.count; + return; + } + synctex_abort(); + return; +} + + +#pragma mark - +#pragma mark Recorders + +/* Recording a "$..." line */ +void synctex_math_recorder(halfword p) +{ + size_t len = 0; +#if SYNCTEX_DEBUG > 999 + printf("\nSynchronize DEBUG: synctex_math_recorder\n"); +#endif + len = SYNCTEX_fprintf(SYNCTEX_FILE,"$%i,%i:%i,%i\n", + SYNCTEX_TAG_MODEL(p,medium_node_size), + SYNCTEX_LINE_MODEL(p,medium_node_size), + synctex_ctxt.curh UNIT, synctex_ctxt.curv UNIT); + if(len>0) { + synctex_ctxt.total_length += len; + ++synctex_ctxt.count; + return; + } + synctex_abort(); + return; +} + +/* Recording a "k..." line */ +void synctex_kern_recorder(halfword p) +{ + size_t len = 0; +#if SYNCTEX_DEBUG > 999 + printf("\nSynchronize DEBUG: synctex_kern_recorder\n"); +#endif + len = SYNCTEX_fprintf(SYNCTEX_FILE,"k%i,%i:%i,%i:%i\n", + SYNCTEX_TAG_MODEL(p,medium_node_size), + SYNCTEX_LINE_MODEL(p,medium_node_size), + synctex_ctxt.curh UNIT, synctex_ctxt.curv UNIT, + SYNCTEX_WIDTH(p) UNIT); + if(len>0) { + synctex_ctxt.total_length += len; + ++synctex_ctxt.count; + return; + } + synctex_abort(); + return; +} + +/* Recording a "c..." line */ +void synctex_char_recorder(halfword p) +{ + size_t len = 0; +#if SYNCTEX_DEBUG > 999 + printf("\nSynchronize DEBUG: synctex_char_recorder\n"); +#endif + len = SYNCTEX_fprintf(SYNCTEX_FILE,"c%i,%i\n", + synctex_ctxt.curh UNIT, synctex_ctxt.curv UNIT); + if(len>0) { + synctex_ctxt.total_length += len; + ++synctex_ctxt.count; + return; + } + synctex_abort(); + return; +} + +/* Recording a "?..." line, type, subtype and position */ +void synctex_node_recorder(halfword p) +{ + size_t len = 0; +#if SYNCTEX_DEBUG > 999 + printf("\nSynchronize DEBUG: synctex_node_recorder(0x%x)\n",p); +#endif + len = SYNCTEX_fprintf(SYNCTEX_FILE,"?%i,%i:%i,%i\n", + synctex_ctxt.curh UNIT, synctex_ctxt.curv UNIT, + mem[p].hh.b0,mem[p].hh.b1); + if(len>0) { + synctex_ctxt.total_length += len; + ++synctex_ctxt.count; + return; + } + synctex_abort(); + return; +} + +# else +# warning "SyncTeX is disabled" +# endif diff --git a/Build/source/texk/web2c/luatexdir/utils/synctex.h b/Build/source/texk/web2c/luatexdir/utils/synctex.h new file mode 100644 index 00000000000..98b11447f20 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/utils/synctex.h @@ -0,0 +1,113 @@ +/* synctex.h + +This file is part of the SyncTeX package. + +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 THE AUTHORS OR COPYRIGHT +HOLDERS 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 + +Acknowledgments: +---------------- +The author received useful remarks from the pdfTeX developers, especially Hahn The Thanh, +and significant help from XeTeX developer Jonathan Kew + +Nota Bene: +---------- +If you include or use a significant part of the synctex package into a software, +I would appreciate to be listed as contributor and see "SyncTeX" highlighted. + +Version 1 +Thu Jun 19 09:39:21 UTC 2008 + +*/ + +# ifndef __SYNCTEX_HEADER__ +# define __SYNCTEX_HEADER__ + +/* Send this message to init the synctex command value to the command line option. + * Sending this message too early will cause a bus error. */ +extern void synctex_init_command(void); + +/* Send this message to clean memory, and close the file. */ +extern void synctex_terminate(int log_opened); + +/* Send this message when starting a new input. */ +extern void synctex_start_input(void); + +/* Recording the "{..." line. In *tex.web, use synctex_sheet(pdf_output) at + * the very beginning of the ship_out procedure. +*/ +extern void synctex_sheet(integer mag); + +/* Recording the "}..." line. In *tex.web, use synctex_teehs at + * the very end of the ship_out procedure. +*/ +extern void synctex_teehs(void); + +/* This message is sent when a vlist will be shipped out, more precisely at + * the beginning of the vlist_out procedure in *TeX.web. It will be balanced + * by a synctex_tsilv, sent at the end of the vlist_out procedure. p is the + * address of the vlist We assume that p is really a vlist node! */ +extern void synctex_vlist(halfword this_box); + +/* Recording a "}" line ending a vbox: this message is sent whenever a vlist + * has been shipped out. It is used to close the vlist nesting level. It is + * sent at the end of each vlist_out procedure in *TeX.web to balance a former + * synctex_vlist sent at the beginning of that procedure. */ +extern void synctex_tsilv(halfword this_box); + +/* This message is sent when a void vlist will be shipped out. + * There is no need to balance a void vlist. */ +extern void synctex_void_vlist(halfword p, halfword this_box); + +/* Send this message when an hlist will be shipped out, more precisely at + * the beginning of the hlist_out procedure in *TeX.web. It must be balanced + * by a synctex_tsilh, sent at the end of the hlist_out procedure. p is the + * address of the hlist. */ +extern void synctex_hlist(halfword this_box); + +/* Send this message at the end of the various hlist_out procedure in *TeX.web + * to balance a former synctex_hlist. */ +extern void synctex_tsilh(halfword this_box); + +/* This message is sent when a void hlist will be shipped out. + * There is no need to balance a void hlist. */ +extern void synctex_void_hlist(halfword p, halfword this_box); + +/* Send this message whenever an inline math node will ship out. */ +extern void synctex_math(halfword p, halfword this_box); + +/* Send this message whenever an horizontal rule or glue node will ship out. */ +extern void synctex_horizontal_rule_or_glue(halfword p, halfword this_box); + +/* Send this message whenever a kern node will ship out. */ +extern void synctex_kern(halfword p, halfword this_box); + +/* this message is sent whenever a char node ships out */ +extern void synctex_char(halfword p, halfword this_box); + +/* this message should be sent to record information + for a node of an unknown type */ +extern void synctex_node(halfword p, halfword this_box); + +/* For debugging purpose only */ +extern void synctex_current(void); + +# endif diff --git a/Build/source/texk/web2c/luatexdir/utils/utils.c b/Build/source/texk/web2c/luatexdir/utils/utils.c new file mode 100644 index 00000000000..1096d9c08e9 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/utils/utils.c @@ -0,0 +1,1802 @@ +/* utils.c + + Copyright 1996-2006 Han The Thanh <thanh@pdftex.org> + Copyright 2006-2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + +#include "openbsd-compat.h" +#ifdef HAVE_ASPRINTF /* asprintf is not defined in openbsd-compat.h, but in stdio.h */ +# include <stdio.h> +#endif + +#include "sys/types.h" +#ifndef __MINGW32__ +# include "sysexits.h" +#else +# define EX_SOFTWARE 70 +#endif +#include "md5.h" +#include <kpathsea/c-proto.h> +#include <kpathsea/c-stat.h> +#include <kpathsea/c-fopen.h> +#include <string.h> +#include <time.h> +#include <float.h> /* for DBL_EPSILON */ +#include "zlib.h" +#include "ptexlib.h" + +#include "png.h" +#include "xpdf/config.h" /* just to get the xpdf version */ + +static const char __svn_version[] = + "$Id: utils.c 2085 2009-03-22 15:21:50Z hhenkel $ $URL: http://scm.foundry.supelec.fr/svn/luatex/trunk/src/texk/web2c/luatexdir/utils/utils.c $"; + +#define check_nprintf(size_get, size_want) \ + if ((unsigned)(size_get) >= (unsigned)(size_want)) \ + pdftex_fail ("snprintf failed: file %s, line %d", __FILE__, __LINE__); + +char *cur_file_name = NULL; +strnumber last_tex_string; +static char print_buf[PRINTF_BUF_SIZE]; +static char *jobname_cstr = NULL; +static char *job_id_string = NULL; +extern string ptexbanner; /* from web2c/lib/texmfmp.c */ +extern string versionstring; /* from web2c/lib/version.c */ +extern KPSEDLL string kpathsea_version_string; /* from kpathsea/version.c */ + +size_t last_ptr_index; /* for use with alloc_array */ + +/* define fb_ptr, fb_array & fb_limit */ +typedef char fb_entry; +define_array(fb); + +/* define char_ptr, char_array & char_limit */ +typedef char char_entry; +define_array(char); + +integer fb_offset(void) +{ + return fb_ptr - fb_array; +} + +void fb_seek(integer offset) +{ + fb_ptr = fb_array + offset; +} + +void fb_putchar(eight_bits b) +{ + alloc_array(fb, 1, SMALL_ARRAY_SIZE); + *fb_ptr++ = b; +} + +void fb_flush(void) +{ + fb_entry *p; + integer n; + for (p = fb_array; p < fb_ptr;) { + n = pdf_buf_size - pdf_ptr; + if (fb_ptr - p < n) + n = fb_ptr - p; + memcpy(pdf_buf + pdf_ptr, p, (unsigned) n); + pdf_ptr += n; + if (pdf_ptr == pdf_buf_size) + pdf_flush(); + p += n; + } + fb_ptr = fb_array; +} + +#define SUBSET_TAG_LENGTH 6 +void make_subset_tag(fd_entry * fd) +{ + int i, j = 0, a[SUBSET_TAG_LENGTH]; + md5_state_t pms; + char *glyph; + glw_entry *glw_glyph; + struct avl_traverser t; + md5_byte_t digest[16]; + void **aa; + static struct avl_table *st_tree = NULL; + if (st_tree == NULL) + st_tree = avl_create(comp_string_entry, NULL, &avl_xallocator); + assert(fd != NULL); + assert(fd->gl_tree != NULL); + assert(fd->fontname != NULL); + assert(fd->subset_tag == NULL); + fd->subset_tag = xtalloc(SUBSET_TAG_LENGTH + 1, char); + do { + md5_init(&pms); + avl_t_init(&t, fd->gl_tree); + if (is_cidkeyed(fd->fm)) { /* glw_entry items */ + for (glw_glyph = (glw_entry *) avl_t_first(&t, fd->gl_tree); + glw_glyph != NULL; glw_glyph = (glw_entry *) avl_t_next(&t)) { + glyph = malloc(24); + sprintf(glyph, "%05u%05u ", glw_glyph->id, glw_glyph->wd); + md5_append(&pms, (md5_byte_t *) glyph, strlen(glyph)); + free(glyph); + } + } else { + for (glyph = (char *) avl_t_first(&t, fd->gl_tree); glyph != NULL; + glyph = (char *) avl_t_next(&t)) { + md5_append(&pms, (md5_byte_t *) glyph, strlen(glyph)); + md5_append(&pms, (md5_byte_t *) " ", 1); + } + } + md5_append(&pms, (md5_byte_t *) fd->fontname, strlen(fd->fontname)); + md5_append(&pms, (md5_byte_t *) & j, sizeof(int)); /* to resolve collision */ + md5_finish(&pms, digest); + for (a[0] = 0, i = 0; i < 13; i++) + a[0] += digest[i]; + for (i = 1; i < SUBSET_TAG_LENGTH; i++) + a[i] = a[i - 1] - digest[i - 1] + digest[(i + 12) % 16]; + for (i = 0; i < SUBSET_TAG_LENGTH; i++) + fd->subset_tag[i] = a[i] % 26 + 'A'; + fd->subset_tag[SUBSET_TAG_LENGTH] = '\0'; + j++; + assert(j < 100); + } + while ((char *) avl_find(st_tree, fd->subset_tag) != NULL); + aa = avl_probe(st_tree, fd->subset_tag); + assert(aa != NULL); + if (j > 2) + pdftex_warn + ("\nmake_subset_tag(): subset-tag collision, resolved in round %d.\n", + j); +} + +void pdf_puts(const char *s) +{ + pdfroom(strlen(s) + 1); + while (*s) + pdf_buf[pdf_ptr++] = *s++; +} + +__attribute__ ((format(printf, 1, 2))) +void pdf_printf(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + vsnprintf(print_buf, PRINTF_BUF_SIZE, fmt, args); + pdf_puts(print_buf); + va_end(args); +} + +strnumber maketexstring(const char *s) +{ + if (s == NULL || *s == 0) + return get_nullstr(); + return maketexlstring(s, strlen(s)); +} + +strnumber maketexlstring(const char *s, size_t l) +{ + if (s == NULL || l == 0) + return get_nullstr(); + check_pool_overflow(pool_ptr + l); + while (l-- > 0) + str_pool[pool_ptr++] = *s++; + last_tex_string = make_string(); + return last_tex_string; +} + +/* print a C string through TeX */ +void print_string(char *j) +{ + while (*j) { + print_char(*j); + j++; + } +} + +/* append a C string to a TeX string */ +void append_string(char *s) +{ + if (s == NULL || *s == 0) + return; + check_buf(pool_ptr + strlen(s), pool_size); + while (*s) + str_pool[pool_ptr++] = *s++; + return; +} + +__attribute__ ((format(printf, 1, 2))) +void tex_printf(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + vsnprintf(print_buf, PRINTF_BUF_SIZE, fmt, args); + print_string(print_buf); + xfflush(stdout); + va_end(args); +} + +/* Helper for pdftex_fail. */ +static void safe_print(const char *str) +{ + const char *c; + for (c = str; *c; ++c) + print(*c); +} + +void remove_pdffile(void) +{ + if (!kpathsea_debug && output_file_name && !fixed_pdf_draftmode) { + xfclose(pdf_file, makecstring(output_file_name)); + remove(makecstring(output_file_name)); + } +} + +/* pdftex_fail may be called when a buffer overflow has happened/is + happening, therefore may not call mktexstring. However, with the + current implementation it appears that error messages are misleading, + possibly because pool overflows are detected too late. + + The output format of this fuction must be the same as pdf_error in + pdftex.web! */ + +__attribute__ ((noreturn, format(printf, 1, 2))) +void pdftex_fail(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + print_ln(); + safe_print("!LuaTeX error"); + if (cur_file_name) { + safe_print(" (file "); + safe_print(cur_file_name); + safe_print(")"); + } + safe_print(": "); + vsnprintf(print_buf, PRINTF_BUF_SIZE, fmt, args); + safe_print(print_buf); + va_end(args); + print_ln(); + remove_pdffile(); + safe_print(" ==> Fatal error occurred, no output PDF file produced!"); + print_ln(); + if (kpathsea_debug) { + abort(); + } else { + exit(EX_SOFTWARE); + } +} + +/* The output format of this fuction must be the same as pdf_warn in + pdftex.web! */ + +__attribute__ ((format(printf, 1, 2))) +void pdftex_warn(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + print_ln(); + tex_printf("LuaTeX warning"); + if (cur_file_name) + tex_printf(" (file %s)", cur_file_name); + tex_printf(": "); + vsnprintf(print_buf, PRINTF_BUF_SIZE, fmt, args); + print_string(print_buf); + va_end(args); + print_ln(); +} + +void tex_error(char *msg, char **hlp) +{ + strnumber aa = 0, bb = 0, cc = 0, dd = 0, ee = 0; + int k = 0; + if (hlp != NULL) { + while (hlp[k] != NULL) + k++; + if (k > 0) + aa = maketexstring(hlp[0]); + if (k > 1) + bb = maketexstring(hlp[1]); + if (k > 2) + cc = maketexstring(hlp[2]); + if (k > 3) + dd = maketexstring(hlp[3]); + if (k > 4) + ee = maketexstring(hlp[4]); + } + do_print_err(maketexstring(msg)); + flush_str(last_tex_string); + +/* *INDENT-OFF* */ + switch (k) { + case 0: dohelp0(); break; + case 1: dohelp1(aa); break; + case 2: dohelp2(aa, bb); break; + case 3: dohelp3(aa, bb, cc); break; + case 4: dohelp4(aa, bb, cc, dd); break; + case 5: dohelp5(aa, bb, cc, dd, ee); break; + } +/* *INDENT-ON* */ + error(); + + if (ee) + flush_str(ee); + if (dd) + flush_str(dd); + if (cc) + flush_str(cc); + if (bb) + flush_str(bb); + if (aa) + flush_str(aa); +} + +void garbage_warning(void) +{ + pdftex_warn("dangling objects discarded, no output file produced."); + remove_pdffile(); +} + +char *makecstring(integer s) +{ + size_t l; + return makeclstring(s, &l); +} + +char *makeclstring(integer s, size_t * len) +{ + static char *cstrbuf = NULL; + char *p; + static int allocsize; + int allocgrow, i, l; + if (s >= 2097152) { + s -= 2097152; + l = str_start[s + 1] - str_start[s]; + *len = l; + check_buf(l + 1, MAX_CSTRING_LEN); + if (cstrbuf == NULL) { + allocsize = l + 1; + cstrbuf = xmallocarray(char, allocsize); + } else if (l + 1 > allocsize) { + allocgrow = allocsize * 0.2; + if (l + 1 - allocgrow > allocsize) + allocsize = l + 1; + else if (allocsize < MAX_CSTRING_LEN - allocgrow) + allocsize += allocgrow; + else + allocsize = MAX_CSTRING_LEN; + cstrbuf = xreallocarray(cstrbuf, char, allocsize); + } + p = cstrbuf; + for (i = 0; i < l; i++) + *p++ = str_pool[i + str_start[s]]; + *p = 0; + } else { + if (cstrbuf == NULL) { + allocsize = 5; + cstrbuf = xmallocarray(char, allocsize); + } + if (s <= 0x7F) { + cstrbuf[0] = s; + cstrbuf[1] = 0; + *len = 1; + } else if (s <= 0x7FF) { + cstrbuf[0] = 0xC0 + (s / 0x40); + cstrbuf[1] = 0x80 + (s % 0x40); + cstrbuf[2] = 0; + *len = 2; + } else if (s <= 0xFFFF) { + cstrbuf[0] = 0xE0 + (s / 0x1000); + cstrbuf[1] = 0x80 + ((s % 0x1000) / 0x40); + cstrbuf[2] = 0x80 + ((s % 0x1000) % 0x40); + cstrbuf[3] = 0; + *len = 3; + } else { + if (s >= 0x10FF00) { + cstrbuf[0] = s - 0x10FF00; + cstrbuf[1] = 0; + *len = 1; + } else { + cstrbuf[0] = 0xF0 + (s / 0x40000); + cstrbuf[1] = 0x80 + ((s % 0x40000) / 0x1000); + cstrbuf[2] = 0x80 + (((s % 0x40000) % 0x1000) / 0x40); + cstrbuf[3] = 0x80 + (((s % 0x40000) % 0x1000) % 0x40); + cstrbuf[4] = 0; + *len = 4; + } + } + } + return cstrbuf; +} + +void set_job_id(int year, int month, int day, int time) +{ + char *name_string, *format_string, *s; + size_t slen; + int i; + + if (job_id_string != NULL) + return; + + name_string = xstrdup(makecstring(job_name)); + format_string = xstrdup(makecstring(format_ident)); + slen = SMALL_BUF_SIZE + + strlen(name_string) + + strlen(format_string) + + strlen(ptexbanner) + + strlen(versionstring) + strlen(kpathsea_version_string); + s = xtalloc(slen, char); + /* The Web2c version string starts with a space. */ + i = snprintf(s, slen, + "%.4d/%.2d/%.2d %.2d:%.2d %s %s %s%s %s", + year, month, day, time / 60, time % 60, + name_string, format_string, ptexbanner, + versionstring, kpathsea_version_string); + check_nprintf(i, slen); + job_id_string = xstrdup(s); + xfree(s); + xfree(name_string); + xfree(format_string); +} + +void make_pdftex_banner(void) +{ + static boolean pdftexbanner_init = false; + char *s; + size_t slen; + int i; + + if (pdftexbanner_init) + return; + + slen = SMALL_BUF_SIZE + + strlen(ptexbanner) + + strlen(versionstring) + strlen(kpathsea_version_string); + s = xtalloc(slen, char); + /* The Web2c version string starts with a space. */ + i = snprintf(s, slen, + "%s%s %s", ptexbanner, versionstring, kpathsea_version_string); + check_nprintf(i, slen); + pdftex_banner = maketexstring(s); + xfree(s); + pdftexbanner_init = true; +} + +strnumber get_resname_prefix(void) +{ +/* static char name_str[] = */ +/* "!\"$&'*+,-.0123456789:;=?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\\" */ +/* "^_`abcdefghijklmnopqrstuvwxyz|~"; */ + static char name_str[] = + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + char prefix[7]; /* make a tag of 6 chars long */ + unsigned long crc; + short i; + size_t base = strlen(name_str); + crc = crc32(0L, Z_NULL, 0); + crc = crc32(crc, (Bytef *) job_id_string, strlen(job_id_string)); + for (i = 0; i < 6; i++) { + prefix[i] = name_str[crc % base]; + crc /= base; + } + prefix[6] = 0; + return maketexstring(prefix); +} + +size_t xfwrite(void *ptr, size_t size, size_t nmemb, FILE * stream) +{ + if (fwrite(ptr, size, nmemb, stream) != nmemb) + pdftex_fail("fwrite() failed"); + return nmemb; +} + +int xfflush(FILE * stream) +{ + if (fflush(stream) != 0) + pdftex_fail("fflush() failed (%s)", strerror(errno)); + return 0; +} + +int xgetc(FILE * stream) +{ + int c = getc(stream); + if (c < 0 && c != EOF) + pdftex_fail("getc() failed (%s)", strerror(errno)); + return c; +} + +int xputc(int c, FILE * stream) +{ + int i = putc(c, stream); + if (i < 0) + pdftex_fail("putc() failed (%s)", strerror(errno)); + return i; +} + +void write_stream_length(integer length, longinteger offset) +{ + if (jobname_cstr == NULL) + jobname_cstr = xstrdup(makecstring(job_name)); + if (fixed_pdf_draftmode == 0) { + xfseeko(pdf_file, (off_t) offset, SEEK_SET, jobname_cstr); + fprintf(pdf_file, "%li", (long int) length); + xfseeko(pdf_file, (off_t) pdfoffset(), SEEK_SET, jobname_cstr); + } +} + +scaled ext_xn_over_d(scaled x, scaled n, scaled d) +{ + double r = (((double) x) * ((double) n)) / ((double) d); + if (r > DBL_EPSILON) + r += 0.5; + else + r -= 0.5; + if (r >= (double) maxinteger || r <= -(double) maxinteger) + pdftex_warn("arithmetic: number too big"); + return (scaled) r; +} + +void libpdffinish() +{ + xfree(fb_array); + xfree(char_array); + xfree(job_id_string); + fm_free(); + t1_free(); + enc_free(); + epdf_free(); + ttf_free(); + sfd_free(); + glyph_unicode_free(); + zip_free(); +} + +/* Converts any string given in in in an allowed PDF string which can be + * handled by printf et.al.: \ is escaped to \\, parenthesis are escaped and + * control characters are octal encoded. + * This assumes that the string does not contain any already escaped + * characters! + */ +char *convertStringToPDFString(const char *in, int len) +{ + static char pstrbuf[MAX_PSTRING_LEN]; + char *out = pstrbuf; + int i, j, k; + char buf[5]; + j = 0; + for (i = 0; i < len; i++) { + check_buf(j + sizeof(buf), MAX_PSTRING_LEN); + if (((unsigned char) in[i] < '!') || ((unsigned char) in[i] > '~')) { + /* convert control characters into oct */ + k = snprintf(buf, sizeof(buf), + "\\%03o", (unsigned int) (unsigned char) in[i]); + check_nprintf(k, sizeof(buf)); + out[j++] = buf[0]; + out[j++] = buf[1]; + out[j++] = buf[2]; + out[j++] = buf[3]; + } else if ((in[i] == '(') || (in[i] == ')')) { + /* escape paranthesis */ + out[j++] = '\\'; + out[j++] = in[i]; + } else if (in[i] == '\\') { + /* escape backslash */ + out[j++] = '\\'; + out[j++] = '\\'; + } else { + /* copy char :-) */ + out[j++] = in[i]; + } + } + out[j] = '\0'; + return pstrbuf; +} + +/* Converts any string given in in in an allowed PDF string which can be + * handled by printf et.al.: \ is escaped to \\, parenthesis are escaped and + * control characters are octal encoded. + * This assumes that the string does not contain any already escaped + * characters! + * + * See escapename for parameter description. + */ +void escapestring(poolpointer in) +{ + const poolpointer out = pool_ptr; + unsigned char ch; + int i; + while (in < out) { + if (pool_ptr + 4 >= pool_size) { + pool_ptr = pool_size; + /* error by str_toks that calls str_room(1) */ + return; + } + + ch = (unsigned char) str_pool[in++]; + + if ((ch < '!') || (ch > '~')) { + /* convert control characters into oct */ + i = snprintf((char *) &str_pool[pool_ptr], 5, + "\\%.3o", (unsigned int) ch); + check_nprintf(i, 5); + pool_ptr += i; + continue; + } + if ((ch == '(') || (ch == ')') || (ch == '\\')) { + /* escape parenthesis and backslash */ + str_pool[pool_ptr++] = '\\'; + } + /* copy char :-) */ + str_pool[pool_ptr++] = ch; + } +} + +/* Convert any given string in a PDF name using escaping mechanism + of PDF 1.2. The result does not include the leading slash. + + PDF specification 1.6, section 3.2.6 "Name Objects" explains: + <blockquote> + Beginning with PDF 1.2, any character except null (character code 0) may + be included in a name by writing its 2-digit hexadecimal code, preceded + by the number sign character (#); see implementation notes 3 and 4 in + Appendix H. This syntax is required to represent any of the delimiter or + white-space characters or the number sign character itself; it is + recommended but not required for characters whose codes are outside the + range 33 (!) to 126 (~). + </blockquote> + The following table shows the conversion that are done by this + function: + code result reason + ----------------------------------- + 0 ignored not allowed + 1..32 escaped must for white-space: + 9 (tab), 10 (lf), 12 (ff), 13 (cr), 32 (space) + recommended for the other control characters + 35 escaped escape char "#" + 37 escaped delimiter "%" + 40..41 escaped delimiters "(" and ")" + 47 escaped delimiter "/" + 60 escaped delimiter "<" + 62 escaped delimiter ">" + 91 escaped delimiter "[" + 93 escaped delimiter "]" + 123 escaped delimiter "{" + 125 escaped delimiter "}" + 127..255 escaped recommended + else copy regular characters + + Parameter "in" is a pointer into the string pool where + the input string is located. The output string is written + as temporary string right after the input string. + Thus at the begin of the procedure the global variable + "pool_ptr" points to the start of the output string and + after the end when the procedure returns. +*/ +void escapename(poolpointer in) +{ + const poolpointer out = pool_ptr; + unsigned char ch; + int i; + + while (in < out) { + if (pool_ptr + 3 >= pool_size) { + pool_ptr = pool_size; + /* error by str_toks that calls str_room(1) */ + return; + } + + ch = (unsigned char) str_pool[in++]; + + if ((ch >= 1 && ch <= 32) || ch >= 127) { + /* escape */ + i = snprintf((char *) &str_pool[pool_ptr], 4, + "#%.2X", (unsigned int) ch); + check_nprintf(i, 4); + pool_ptr += i; + continue; + } + switch (ch) { + case 0: + /* ignore */ + break; + case 35: + case 37: + case 40: + case 41: + case 47: + case 60: + case 62: + case 91: + case 93: + case 123: + case 125: + /* escape */ + i = snprintf((char *) &str_pool[pool_ptr], 4, + "#%.2X", (unsigned int) ch); + check_nprintf(i, 4); + pool_ptr += i; + break; + default: + /* copy */ + str_pool[pool_ptr++] = ch; + } + } +} + +/* Convert any given string in a PDF hexadecimal string. The + result does not include the angle brackets. + + This procedure uses uppercase hexadecimal letters. + + See escapename for description of parameters. +*/ +void escapehex(poolpointer in) +{ + const poolpointer out = pool_ptr; + unsigned char ch; + int i; + + while (in < out) { + if (pool_ptr + 2 >= pool_size) { + pool_ptr = pool_size; + /* error by str_toks that calls str_room(1) */ + return; + } + + ch = (unsigned char) str_pool[in++]; + + i = snprintf((char *) &str_pool[pool_ptr], 3, "%.2X", + (unsigned int) ch); + check_nprintf(i, 3); + pool_ptr += 2; + } +} + +/* Unescape any given hexadecimal string. + + Last hex digit can be omitted, it is replaced by zero, see + PDF specification. + + Invalid digits are silently ignored. + + See escapename for description of parameters. +*/ +void unescapehex(poolpointer in) +{ + const poolpointer out = pool_ptr; + unsigned char ch; + boolean first = true; + unsigned char a = 0; /* to avoid warning about uninitialized use of a */ + while (in < out) { + if (pool_ptr + 1 >= pool_size) { + pool_ptr = pool_size; + /* error by str_toks that calls str_room(1) */ + return; + } + + ch = (unsigned char) str_pool[in++]; + + if ((ch >= '0') && (ch <= '9')) { + ch -= '0'; + } else if ((ch >= 'A') && (ch <= 'F')) { + ch -= 'A' - 10; + } else if ((ch >= 'a') && (ch <= 'f')) { + ch -= 'a' - 10; + } else { + continue; /* ignore wrong character */ + } + + if (first) { + a = ch << 4; + first = false; + continue; + } + + str_pool[pool_ptr++] = a + ch; + first = true; + } + if (!first) { /* last hex digit is omitted */ + str_pool[pool_ptr++] = a; + } +} + +/* Converts any string given in in in an allowed PDF string which is + * hexadecimal encoded; + * sizeof(out) should be at least lin*2+1. + */ +static void convertStringToHexString(const char *in, char *out, int lin) +{ + int i, j, k; + char buf[3]; + j = 0; + for (i = 0; i < lin; i++) { + k = snprintf(buf, sizeof(buf), + "%02X", (unsigned int) (unsigned char) in[i]); + check_nprintf(k, sizeof(buf)); + out[j++] = buf[0]; + out[j++] = buf[1]; + } + out[j] = '\0'; +} + +/* Compute the ID string as per PDF1.4 9.3: + <blockquote> + File identifers are defined by the optional ID entry in a PDF file's + trailer dictionary (see Section 3.4.4, "File Trailer"; see also + implementation note 105 in Appendix H). The value of this entry is an + array of two strings. The first string is a permanent identifier based + on the contents of the file at the time it was originally created, and + does not change when the file is incrementally updated. The second + string is a changing identifier based on the file's contents at the + time it was last updated. When a file is first written, both + identifiers are set to the same value. If both identifiers match when a + file reference is resolved, it is very likely that the correct file has + been found; if only the first identifier matches, then a different + version of the correct file has been found. + To help ensure the uniqueness of file identifiers, it is recommend + that they be computed using a message digest algorithm such as MD5 + (described in Internet RFC 1321, The MD5 Message-Digest Algorithm; see + the Bibliography), using the following information (see implementation + note 106 in Appendix H): + - The current time + - A string representation of the file's location, usually a pathname + - The size of the file in bytes + - The values of all entries in the file's document information + dictionary (see Section 9.2.1, Document Information Dictionary ) + </blockquote> + This stipulates only that the two IDs must be identical when the file is + created and that they should be reasonably unique. Since it's difficult + to get the file size at this point in the execution of pdfTeX and + scanning the info dict is also difficult, we start with a simpler + implementation using just the first two items. + */ +void print_ID(strnumber filename) +{ + time_t t; + size_t size; + char time_str[32]; + md5_state_t state; + md5_byte_t digest[16]; + char id[64]; + char *file_name; + char pwd[4096]; + /* start md5 */ + md5_init(&state); + /* get the time */ + t = time(NULL); + size = strftime(time_str, sizeof(time_str), "%Y%m%dT%H%M%SZ", gmtime(&t)); + md5_append(&state, (const md5_byte_t *) time_str, size); + /* get the file name */ + if (getcwd(pwd, sizeof(pwd)) == NULL) + pdftex_fail("getcwd() failed (%s), (path too long?)", strerror(errno)); + file_name = makecstring(filename); + md5_append(&state, (const md5_byte_t *) pwd, strlen(pwd)); + md5_append(&state, (const md5_byte_t *) "/", 1); + md5_append(&state, (const md5_byte_t *) file_name, strlen(file_name)); + /* finish md5 */ + md5_finish(&state, digest); + /* write the IDs */ + convertStringToHexString((char *) digest, id, 16); + pdf_printf("/ID [<%s> <%s>]", id, id); +} + +/* Print the /CreationDate entry. + + PDF Reference, third edition says about the expected date format: + <blockquote> + 3.8.2 Dates + + PDF defines a standard date format, which closely follows that of + the international standard ASN.1 (Abstract Syntax Notation One), + defined in ISO/IEC 8824 (see the Bibliography). A date is a string + of the form + + (D:YYYYMMDDHHmmSSOHH'mm') + + where + + YYYY is the year + MM is the month + DD is the day (01-31) + HH is the hour (00-23) + mm is the minute (00-59) + SS is the second (00-59) + O is the relationship of local time to Universal Time (UT), + denoted by one of the characters +, -, or Z (see below) + HH followed by ' is the absolute value of the offset from UT + in hours (00-23) + mm followed by ' is the absolute value of the offset from UT + in minutes (00-59) + + The apostrophe character (') after HH and mm is part of the syntax. + All fields after the year are optional. (The prefix D:, although also + optional, is strongly recommended.) The default values for MM and DD + are both 01; all other numerical fields default to zero values. A plus + sign (+) as the value of the O field signifies that local time is + later than UT, a minus sign (-) that local time is earlier than UT, + and the letter Z that local time is equal to UT. If no UT information + is specified, the relationship of the specified time to UT is + considered to be unknown. Whether or not the time zone is known, the + rest of the date should be specified in local time. + + For example, December 23, 1998, at 7:52 PM, U.S. Pacific Standard + Time, is represented by the string + + D:199812231952-08'00' + </blockquote> + + The main difficulty is get the time zone offset. strftime() does this in ISO + C99 (e.g. newer glibc) with %z, but we have to work with other systems (e.g. + Solaris 2.5). +*/ + +static time_t start_time = 0; +#define TIME_STR_SIZE 30 +static char start_time_str[TIME_STR_SIZE]; /* minimum size for time_str is 24: "D:YYYYmmddHHMMSS+HH'MM'" */ + +static void makepdftime(time_t t, char *time_str) +{ + struct tm lt, gmt; + size_t size; + int i, off, off_hours, off_mins; + + /* get the time */ + lt = *localtime(&t); + size = strftime(time_str, TIME_STR_SIZE, "D:%Y%m%d%H%M%S", <); + /* expected format: "YYYYmmddHHMMSS" */ + if (size == 0) { + /* unexpected, contents of time_str is undefined */ + time_str[0] = '\0'; + return; + } + + /* correction for seconds: %S can be in range 00..61, + the PDF reference expects 00..59, + therefore we map "60" and "61" to "59" */ + if (time_str[14] == '6') { + time_str[14] = '5'; + time_str[15] = '9'; + time_str[16] = '\0'; /* for safety */ + } + + /* get the time zone offset */ + gmt = *gmtime(&t); + + /* this calculation method was found in exim's tod.c */ + off = 60 * (lt.tm_hour - gmt.tm_hour) + lt.tm_min - gmt.tm_min; + if (lt.tm_year != gmt.tm_year) { + off += (lt.tm_year > gmt.tm_year) ? 1440 : -1440; + } else if (lt.tm_yday != gmt.tm_yday) { + off += (lt.tm_yday > gmt.tm_yday) ? 1440 : -1440; + } + + if (off == 0) { + time_str[size++] = 'Z'; + time_str[size] = 0; + } else { + off_hours = off / 60; + off_mins = abs(off - off_hours * 60); + i = snprintf(&time_str[size], 9, "%+03d'%02d'", off_hours, off_mins); + check_nprintf(i, 9); + } +} + +void init_start_time() +{ + if (start_time == 0) { + start_time = time((time_t *) NULL); + makepdftime(start_time, start_time_str); + } +} + +void print_creation_date() +{ + init_start_time(); + pdf_printf("/CreationDate (%s)\n", start_time_str); +} + +void print_mod_date() +{ + init_start_time(); + pdf_printf("/ModDate (%s)\n", start_time_str); +} + +void getcreationdate() +{ + /* put creation date on top of string pool and update pool_ptr */ + size_t len = strlen(start_time_str); + + init_start_time(); + + if ((unsigned) (pool_ptr + len) >= (unsigned) pool_size) { + pool_ptr = pool_size; + /* error by str_toks that calls str_room(1) */ + return; + } + + memcpy(&str_pool[pool_ptr], start_time_str, len); + pool_ptr += len; +} + +/* makecfilename + input/ouput same as makecstring: + input: string number + output: C string with quotes removed. + That means, file names that are legal on some operation systems + cannot any more be used since pdfTeX version 1.30.4. +*/ +char *makecfilename(strnumber s) +{ + char *name = makecstring(s); + char *p = name; + char *q = name; + + while (*p) { + if (*p != '"') + *q++ = *p; + p++; + } + *q = '\0'; + return name; +} + +/* function strips trailing zeros in string with numbers; */ +/* leading zeros are not stripped (as in real life) */ +char *stripzeros(char *a) +{ + enum { NONUM, DOTNONUM, INT, DOT, LEADDOT, FRAC } s = NONUM, t = NONUM; + char *p, *q, *r; + for (p = q = r = a; *p != '\0';) { + switch (s) { + case NONUM: + if (*p >= '0' && *p <= '9') + s = INT; + else if (*p == '.') + s = LEADDOT; + break; + case DOTNONUM: + if (*p != '.' && (*p < '0' || *p > '9')) + s = NONUM; + break; + case INT: + if (*p == '.') + s = DOT; + else if (*p < '0' || *p > '9') + s = NONUM; + break; + case DOT: + case LEADDOT: + if (*p >= '0' && *p <= '9') + s = FRAC; + else if (*p == '.') + s = DOTNONUM; + else + s = NONUM; + break; + case FRAC: + if (*p == '.') + s = DOTNONUM; + else if (*p < '0' || *p > '9') + s = NONUM; + break; + default:; + } + switch (s) { + case DOT: + r = q; + break; + case LEADDOT: + r = q + 1; + break; + case FRAC: + if (*p > '0') + r = q + 1; + break; + case NONUM: + if ((t == FRAC || t == DOT) && r != a) { + q = r--; + if (*r == '.') /* was a LEADDOT */ + *r = '0'; + r = a; + } + break; + default:; + } + *q++ = *p++; + t = s; + } + *q = '\0'; + return a; +} + +void initversionstring(char **versions) +{ + (void) asprintf(versions, + "Compiled with libpng %s; using libpng %s\n" + "Compiled with zlib %s; using zlib %s\n" + "Compiled with xpdf version %s\n", + PNG_LIBPNG_VER_STRING, png_libpng_ver, + ZLIB_VERSION, zlib_version, xpdfVersion); +} + +/*************************************************/ +/* Color Stack and Matrix Transformation Support */ +/*************************************************/ + +/* + In the following array and especially stack data structures are used. + They have the following properties: + - They automatically grow dynamically. + - The size never decreases. + - The variable with name ending in "size" contains the number how many + entries the data structure can hold. + - The variable with name ending in "used" contains the number of + actually used entries. + - Memory of strings in stack entries must be allocated and + freed if the stack is cleared. +*/ + +/* Color Stack */ + +#define STACK_INCREMENT 8 +#define MAX_COLORSTACKS 32768 +/* The colorstack number is stored in two bytes (info field of the node) */ +/* Condition (newcolorstack): MAX_COLORSTACKS mod STACK_INCREMENT = 0 */ + +#define COLOR_DEFAULT "0 g 0 G" +/* literal_modes, see pdftex.web */ +#define SET_ORIGIN 0 +#define DIRECT_PAGE 1 +#define DIRECT_ALWAYS 2 + +/* remember shipout mode: page/form */ +static boolean page_mode; + +typedef struct { + char **page_stack; + char **form_stack; + char *page_current; + char *form_current; + char *form_init; + int page_size; + int form_size; + int page_used; + int form_used; + int literal_mode; + boolean page_start; +} colstack_type; + +static colstack_type *colstacks = NULL; +static int colstacks_size = 0; +static int colstacks_used = 0; + +/* Initialization is done, if the color stacks are used, + init_colorstacks() is defined as macro to avoid unnecessary + procedure calls. +*/ +#define init_colorstacks() if (colstacks_size == 0) colstacks_first_init(); +void colstacks_first_init() +{ + colstacks_size = STACK_INCREMENT; + colstacks = xtalloc(colstacks_size, colstack_type); + colstacks_used = 1; + colstacks[0].page_stack = NULL; + colstacks[0].form_stack = NULL; + colstacks[0].page_size = 0; + colstacks[0].form_size = 0; + colstacks[0].page_used = 0; + colstacks[0].form_used = 0; + colstacks[0].page_current = xstrdup(COLOR_DEFAULT); + colstacks[0].form_current = xstrdup(COLOR_DEFAULT); + colstacks[0].form_init = xstrdup(COLOR_DEFAULT); + colstacks[0].literal_mode = DIRECT_ALWAYS; + colstacks[0].page_start = true; +} + +int colorstackused() +{ + init_colorstacks(); + return colstacks_used; +} + +/* newcolorstack() + A new color stack is setup with the given parameters. + The stack number is returned or -1 in case of error (no room). +*/ +int newcolorstack(integer s, integer literal_mode, boolean page_start) +{ + colstack_type *colstack; + int colstack_num; + char *str; + + init_colorstacks(); + + /* make room */ + if (colstacks_used == MAX_COLORSTACKS) { + return -1; + } + if (colstacks_used == colstacks_size) { + colstacks_size += STACK_INCREMENT; + /* If (MAX_COLORSTACKS mod STACK_INCREMENT = 0) then we don't + need to check the case that size overruns MAX_COLORSTACKS. */ + colstacks = xreallocarray(colstacks, colstack_type, colstacks_size); + } + /* claim new color stack */ + colstack_num = colstacks_used++; + colstack = &colstacks[colstack_num]; + /* configure the new color stack */ + colstack->page_stack = NULL; + colstack->form_stack = NULL; + colstack->page_size = 0; + colstack->page_used = 0; + colstack->form_size = 0; + colstack->form_used = 0; + colstack->literal_mode = literal_mode; + colstack->page_start = page_start; + str = makecstring(s); + if (*str == 0) { + colstack->page_current = NULL; + colstack->form_current = NULL; + colstack->form_init = NULL; + } else { + colstack->page_current = xstrdup(str); + colstack->form_current = xstrdup(str); + colstack->form_init = xstrdup(str); + } + return colstack_num; +} + +#define get_colstack(n) (&colstacks[n]) + +/* Puts a string on top of the string pool and updates pool_ptr. */ +void put_cstring_on_str_pool(poolpointer start, char *str) +{ + size_t len; + + if (str == NULL || *str == 0) { + return; + } + + len = strlen(str); + pool_ptr = start + len; + if (pool_ptr >= pool_size) { + pool_ptr = pool_size; + /* error by str_toks that calls str_room(1) */ + return; + } + memcpy(&str_pool[start], str, len); +} + +integer colorstackset(int colstack_no, integer s) +{ + colstack_type *colstack = get_colstack(colstack_no); + + if (page_mode) { + xfree(colstack->page_current); + colstack->page_current = xstrdup(makecstring(s)); + } else { + xfree(colstack->form_current); + colstack->form_current = xstrdup(makecstring(s)); + } + return colstack->literal_mode; +} + +integer colorstackcurrent(int colstack_no) +{ + colstack_type *colstack = get_colstack(colstack_no); + + if (page_mode) { + put_cstring_on_str_pool(pool_ptr, colstack->page_current); + } else { + put_cstring_on_str_pool(pool_ptr, colstack->form_current); + } + return colstack->literal_mode; +} + +integer colorstackpush(int colstack_no, integer s) +{ + colstack_type *colstack = get_colstack(colstack_no); + char *str; + + if (page_mode) { + if (colstack->page_used == colstack->page_size) { + colstack->page_size += STACK_INCREMENT; + colstack->page_stack = xretalloc(colstack->page_stack, + colstack->page_size, char *); + } + colstack->page_stack[colstack->page_used++] = colstack->page_current; + str = makecstring(s); + if (*str == 0) { + colstack->page_current = NULL; + } else { + colstack->page_current = xstrdup(str); + } + } else { + if (colstack->form_used == colstack->form_size) { + colstack->form_size += STACK_INCREMENT; + colstack->form_stack = xretalloc(colstack->form_stack, + colstack->form_size, char *); + } + colstack->form_stack[colstack->form_used++] = colstack->form_current; + str = makecstring(s); + if (*str == 0) { + colstack->form_current = NULL; + } else { + colstack->form_current = xstrdup(str); + } + } + return colstack->literal_mode; +} + +integer colorstackpop(int colstack_no) +{ + colstack_type *colstack = get_colstack(colstack_no); + + if (page_mode) { + if (colstack->page_used == 0) { + pdftex_warn("pop empty color page stack %u", + (unsigned int) colstack_no); + return colstack->literal_mode; + } + xfree(colstack->page_current); + colstack->page_current = colstack->page_stack[--colstack->page_used]; + put_cstring_on_str_pool(pool_ptr, colstack->page_current); + } else { + if (colstack->form_used == 0) { + pdftex_warn("pop empty color form stack %u", + (unsigned int) colstack_no); + return colstack->literal_mode; + } + xfree(colstack->form_current); + colstack->form_current = colstack->form_stack[--colstack->form_used]; + put_cstring_on_str_pool(pool_ptr, colstack->form_current); + } + return colstack->literal_mode; +} + +void colorstackpagestart() +{ + int i, j; + colstack_type *colstack; + + if (page_mode) { + /* see procedure pdf_out_colorstack_startpage */ + return; + } + + for (i = 0; i < colstacks_used; i++) { + colstack = &colstacks[i]; + for (j = 0; j < colstack->form_used; j++) { + xfree(colstack->form_stack[j]); + } + colstack->form_used = 0; + xfree(colstack->form_current); + if (colstack->form_init == NULL) { + colstack->form_current = NULL; + } else { + colstack->form_current = xstrdup(colstack->form_init); + } + } +} + +integer colorstackskippagestart(int colstack_no) +{ + colstack_type *colstack = get_colstack(colstack_no); + + if (!colstack->page_start) { + return 1; + } + if (colstack->page_current == NULL) { + return 0; + } + if (strcmp(COLOR_DEFAULT, colstack->page_current) == 0) { + return 2; + } + return 0; +} + +/* stack for \pdfsetmatrix */ + +typedef struct { + double a; + double b; + double c; + double d; + double e; + double f; +} matrix_entry; +static matrix_entry *matrix_stack = 0; +static int matrix_stack_size = 0; +static int matrix_stack_used = 0; + +boolean matrixused() +{ + return matrix_stack_used > 0; +} + +/* stack for positions of \pdfsave */ +typedef struct { + scaledpos pos; + int matrix_stack; +} pos_entry; +static pos_entry *pos_stack = 0; /* the stack */ +static int pos_stack_size = 0; /* initially empty */ +static int pos_stack_used = 0; /* used entries */ + +void matrix_stack_room() +{ + matrix_entry *new_stack; + + if (matrix_stack_used >= matrix_stack_size) { + matrix_stack_size += STACK_INCREMENT; + new_stack = xtalloc(matrix_stack_size, matrix_entry); + memcpy((void *) new_stack, (void *) matrix_stack, + matrix_stack_used * sizeof(matrix_entry)); + xfree(matrix_stack); + matrix_stack = new_stack; + } +} + +void checkpdfsave(scaledpos pos) +{ + pos_entry *new_stack; + + if (pos_stack_used >= pos_stack_size) { + pos_stack_size += STACK_INCREMENT; + new_stack = xtalloc(pos_stack_size, pos_entry); + memcpy((void *) new_stack, (void *) pos_stack, + pos_stack_used * sizeof(pos_entry)); + xfree(pos_stack); + pos_stack = new_stack; + } + pos_stack[pos_stack_used].pos.h = pos.h; + pos_stack[pos_stack_used].pos.v = pos.v; + if (page_mode) { + pos_stack[pos_stack_used].matrix_stack = matrix_stack_used; + } + pos_stack_used++; +} + +void checkpdfrestore(scaledpos pos) +{ + scaledpos diff; + if (pos_stack_used == 0) { + pdftex_warn("%s", "\\pdfrestore: missing \\pdfsave"); + return; + } + pos_stack_used--; + diff.h = pos.h - pos_stack[pos_stack_used].pos.h; + diff.v = pos.v - pos_stack[pos_stack_used].pos.v; + if (diff.h != 0 || diff.v != 0) { + pdftex_warn("Misplaced \\pdfrestore by (%dsp, %dsp)", diff.h, diff.v); + } + if (page_mode) { + matrix_stack_used = pos_stack[pos_stack_used].matrix_stack; + } +} + +void pdfshipoutbegin(boolean shipping_page) +{ + pos_stack_used = 0; /* start with empty stack */ + + page_mode = shipping_page; + if (shipping_page) { + colorstackpagestart(); + } +} + +void pdfshipoutend(boolean shipping_page) +{ + if (pos_stack_used > 0) { + pdftex_fail("%u unmatched \\pdfsave after %s shipout", + (unsigned int) pos_stack_used, + ((shipping_page) ? "page" : "form")); + } +} + +/* \pdfsetmatrix{a b c d} + e := pos.h + f := pos.v + M_top: current active matrix at the top of + the matrix stack + + The origin of \pdfsetmatrix is the current point. + The annotation coordinate system is the original + page coordinate system. When pdfTeX calculates + annotation rectangles it does not take into + account this transformations, it uses the original + coordinate system. To get the corrected values, + first we go back to the origin, perform the + transformation and go back: + + ( 1 0 0 ) ( a b 0 ) ( 1 0 0 ) + ( 0 1 0 ) x ( c d 0 ) x ( 0 1 0 ) x M_top + ( -e -f 1 ) ( 0 0 1 ) ( e f 1 ) + + ( 1 0 0 ) ( a b 0 ) + = ( 0 1 0 ) x ( c d 0 ) x M_top + ( e f 1 ) ( -e -f 1 ) + + ( a b 0 ) + = ( c d 0 ) x M_top + ( e(1-a)-fc f(1-d)-eb 1 ) + +*/ + +void pdfsetmatrix(poolpointer in, scaledpos pos) +{ + /* Argument of \pdfsetmatrix starts with str_pool[in] and ends + before str_pool[pool_ptr]. */ + + matrix_entry x, *y, *z; + + if (page_mode) { + if (sscanf((const char *) &str_pool[in], " %lf %lf %lf %lf ", + &x.a, &x.b, &x.c, &x.d) != 4) { + pdftex_warn("Unrecognized format of \\pdfsetmatrix{%s}", + &str_pool[pool_ptr]); + return; + } + /* calculate this transformation matrix */ + x.e = (double) pos.h * (1.0 - x.a) - (double) pos.v * x.c; + x.f = (double) pos.v * (1.0 - x.d) - (double) pos.h * x.b; + matrix_stack_room(); + z = &matrix_stack[matrix_stack_used]; + if (matrix_stack_used > 0) { + y = &matrix_stack[matrix_stack_used - 1]; + z->a = x.a * y->a + x.b * y->c; + z->b = x.a * y->b + x.b * y->d; + z->c = x.c * y->a + x.d * y->c; + z->d = x.c * y->b + x.d * y->d; + z->e = x.e * y->a + x.f * y->c + y->e; + z->f = x.e * y->b + x.f * y->d + y->f; + } else { + z->a = x.a; + z->b = x.b; + z->c = x.c; + z->d = x.d; + z->e = x.e; + z->f = x.f; + } + matrix_stack_used++; + } +} + +/* Apply matrix to point (x,y) + + ( a b 0 ) + ( x y 1 ) x ( c d 0 ) = ( xa+yc+e xb+yd+f 1 ) + ( e f 1 ) + + If \pdfsetmatrix wasn't used, then return the value unchanged. +*/ + +/* Return valeus for matrix tranform functions */ +static scaled ret_llx; +static scaled ret_lly; +static scaled ret_urx; +static scaled ret_ury; + +scaled getllx() +{ + return ret_llx; +} + +scaled getlly() +{ + return ret_lly; +} + +scaled geturx() +{ + return ret_urx; +} + +scaled getury() +{ + return ret_ury; +} + +static int last_llx; +static int last_lly; +static int last_urx; +static int last_ury; + +#define DO_ROUND(x) ((x > 0) ? (x + .5) : (x - .5)) +#define DO_MIN(a, b) ((a < b) ? a : b) +#define DO_MAX(a, b) ((a > b) ? a : b) + +void do_matrixtransform(scaled x, scaled y, scaled * retx, scaled * rety) +{ + matrix_entry *m = &matrix_stack[matrix_stack_used - 1]; + double x_old = x; + double y_old = y; + double x_new = x_old * m->a + y_old * m->c + m->e; + double y_new = x_old * m->b + y_old * m->d + m->f; + *retx = (scaled) DO_ROUND(x_new); + *rety = (scaled) DO_ROUND(y_new); +} + +void matrixtransformrect(scaled llx, scaled lly, scaled urx, scaled ury) +{ + scaled x1, x2, x3, x4, y1, y2, y3, y4; + + if (page_mode && matrix_stack_used > 0) { + last_llx = llx; + last_lly = lly; + last_urx = urx; + last_ury = ury; + do_matrixtransform(llx, lly, &x1, &y1); + do_matrixtransform(llx, ury, &x2, &y2); + do_matrixtransform(urx, lly, &x3, &y3); + do_matrixtransform(urx, ury, &x4, &y4); + ret_llx = DO_MIN(DO_MIN(x1, x2), DO_MIN(x3, x4)); + ret_lly = DO_MIN(DO_MIN(y1, y2), DO_MIN(y3, y4)); + ret_urx = DO_MAX(DO_MAX(x1, x2), DO_MAX(x3, x4)); + ret_ury = DO_MAX(DO_MAX(y1, y2), DO_MAX(y3, y4)); + } else { + ret_llx = llx; + ret_lly = lly; + ret_urx = urx; + ret_ury = ury; + } +} + +void matrixtransformpoint(scaled x, scaled y) +{ + if (page_mode && matrix_stack_used > 0) { + do_matrixtransform(x, y, &ret_llx, &ret_lly); + } else { + ret_llx = x; + ret_lly = y; + } +} + +void matrixrecalculate(scaled urx) +{ + matrixtransformrect(last_llx, last_lly, urx, last_ury); +} + +void check_buffer_overflow(int wsize) +{ + int nsize; + if (wsize > buf_size) { + nsize = buf_size + buf_size / 5 + 5; + if (nsize < wsize) { + nsize = wsize + 5; + } + buffer = (unsigned char *) xreallocarray(buffer, char, nsize); + buf_size = nsize; + } +} + +#define EXTRA_STRING 500 + +void check_pool_overflow(int wsize) +{ + int nsize; + if ((wsize - 1) > pool_size) { + nsize = pool_size + pool_size / 5 + EXTRA_STRING; + if (nsize < wsize) { + nsize = wsize + EXTRA_STRING; + } + str_pool = (unsigned char *) xreallocarray(str_pool, char, nsize); + pool_size = nsize; + } +} + +#define max_integer 0x7FFFFFFF + +/* the return value is a decimal number with the point |dd| places from the back, + |scaled_out| is the number of scaled points corresponding to that. +*/ + +scaled divide_scaled(scaled s, scaled m, integer dd) +{ + register scaled q; + register scaled r; + int i; + int sign = 1; + if (s < 0) { + sign = -sign; + s = -s; + } + if (m < 0) { + sign = -sign; + m = -m; + } + if (m == 0) { + pdf_error(maketexstring("arithmetic"), + maketexstring("divided by zero")); + } else if (m >= (max_integer / 10)) { + pdf_error(maketexstring("arithmetic"), maketexstring("number too big")); + } + q = s / m; + r = s % m; + for (i = 1; i <= (int) dd; i++) { + q = 10 * q + (10 * r) / m; + r = (10 * r) % m; + } + /* rounding */ + if (2 * r >= m) { + q++; + r -= m; + } + return sign * q; +} + +/* Same function, but using doubles instead of integers (faster) */ + +scaled divide_scaled_n(double sd, double md, double n) +{ + double dd, di = 0.0; + dd = sd / md * n; + if (dd > 0.0) + di = floor(dd + 0.5); + else if (dd < 0.0) + di = -floor((-dd) + 0.5); + return (scaled) di; +} + + +/* C print interface */ + +void tprint(char *s) +{ + while (*s) + print_char(*s++); +} + +void tprint_nl(char *s) +{ + print_nlp(); + tprint(s); +} + +#define escape_char_code 45 /* escape character for token output */ +#define int_par(a) zeqtb[static_int_base+(a)].cint /* an integer parameter */ +#define escape_char int_par(escape_char_code) + +void tprint_esc(char *s) +{ /* prints escape character, then |s| */ + int c = -1; /* the escape character code */ + if (zeqtb != NULL) { + c = escape_char; + if (c >= 0) + print_char(c); + } + tprint(s); +} + +void tconfusion(char *s) +{ + confusion(maketexstring(s)); +} + +#ifdef MSVC + +# include <math.h> +double rint(double x) +{ + double c, f, d1, d2; + + c = ceil(x); + f = floor(x); + d1 = fabs(c - x); + d2 = fabs(x - f); + if (d1 > d2) + return f; + else + return c; +} + +#endif diff --git a/Build/source/texk/web2c/luatexdir/utils/writezip.c b/Build/source/texk/web2c/luatexdir/utils/writezip.c new file mode 100644 index 00000000000..8fb0a1a651b --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/utils/writezip.c @@ -0,0 +1,99 @@ +/* writezip.c + + Copyright 1996-2006 Han The Thanh <thanh@pdftex.org> + Copyright 2006-2008 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + +#include "ptexlib.h" +#include "zlib.h" +#include <assert.h> + +static const char __svn_version[] = + "$Id: writezip.c 2073 2009-03-21 10:06:50Z hhenkel $ $URL: http://scm.foundry.supelec.fr/svn/luatex/trunk/src/texk/web2c/luatexdir/utils/writezip.c $"; + +#define ZIP_BUF_SIZE 32768 + +#define check_err(f, fn) \ + if (f != Z_OK) \ + pdftex_fail("zlib: %s() failed (error code %d)", fn, f) + +static char *zipbuf = NULL; +static z_stream c_stream; /* compression stream */ + +void write_zip(boolean finish) +{ + int err; + static int level_old = 0; + int level = get_pdf_compress_level(); + assert(level > 0); + cur_file_name = NULL; + if (pdf_stream_length == 0) { + if (zipbuf == NULL) { + zipbuf = xtalloc(ZIP_BUF_SIZE, char); + c_stream.zalloc = (alloc_func) 0; + c_stream.zfree = (free_func) 0; + c_stream.opaque = (voidpf) 0; + check_err(deflateInit(&c_stream, level), "deflateInit"); + } else { + if (level != level_old) { /* \pdfcompresslevel change in mid document */ + check_err(deflateEnd(&c_stream), "deflateEnd"); + c_stream.zalloc = (alloc_func) 0; /* these 3 lines no need, just to be safe */ + c_stream.zfree = (free_func) 0; + c_stream.opaque = (voidpf) 0; + check_err(deflateInit(&c_stream, level), "deflateInit"); + } else + check_err(deflateReset(&c_stream), "deflateReset"); + } + level_old = level; + c_stream.next_out = (Bytef *) zipbuf; + c_stream.avail_out = ZIP_BUF_SIZE; + } + assert(zipbuf != NULL); + c_stream.next_in = pdf_buf; + c_stream.avail_in = pdf_ptr; + for (;;) { + if (c_stream.avail_out == 0) { + pdf_gone += xfwrite(zipbuf, 1, ZIP_BUF_SIZE, pdf_file); + pdf_last_byte = zipbuf[ZIP_BUF_SIZE - 1]; /* not needed */ + c_stream.next_out = (Bytef *) zipbuf; + c_stream.avail_out = ZIP_BUF_SIZE; + } + err = deflate(&c_stream, finish ? Z_FINISH : Z_NO_FLUSH); + if (finish && err == Z_STREAM_END) + break; + check_err(err, "deflate"); + if (!finish && c_stream.avail_in == 0) + break; + } + if (finish) { + if (c_stream.avail_out < ZIP_BUF_SIZE) { /* at least one byte has been output */ + pdf_gone += + xfwrite(zipbuf, 1, ZIP_BUF_SIZE - c_stream.avail_out, pdf_file); + pdf_last_byte = zipbuf[ZIP_BUF_SIZE - c_stream.avail_out - 1]; + } + xfflush(pdf_file); + } + pdf_stream_length = c_stream.total_out; +} + +void zip_free(void) +{ + if (zipbuf != NULL) { + check_err(deflateEnd(&c_stream), "deflateEnd"); + free(zipbuf); + } +} |