summaryrefslogtreecommitdiff
path: root/Build/source/texk/web2c/luatexdir/utils
diff options
context:
space:
mode:
authorTaco Hoekwater <taco@elvenkind.com>2009-03-27 15:30:55 +0000
committerTaco Hoekwater <taco@elvenkind.com>2009-03-27 15:30:55 +0000
commit178de0871d690556af74f3768c11bc812b07f347 (patch)
treea939c31adc90d6207848effaec87dd78ec00e658 /Build/source/texk/web2c/luatexdir/utils
parent4865b23b5199697829e4e6633f2f697b4634c462 (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.c794
-rw-r--r--Build/source/texk/web2c/luatexdir/utils/avl.h115
-rw-r--r--Build/source/texk/web2c/luatexdir/utils/avlstuff.c190
-rw-r--r--Build/source/texk/web2c/luatexdir/utils/avlstuff.h27
-rw-r--r--Build/source/texk/web2c/luatexdir/utils/synctex.c1493
-rw-r--r--Build/source/texk/web2c/luatexdir/utils/synctex.h113
-rw-r--r--Build/source/texk/web2c/luatexdir/utils/utils.c1802
-rw-r--r--Build/source/texk/web2c/luatexdir/utils/writezip.c99
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", &lt);
+ /* 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);
+ }
+}